mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-08 21:08:43 +02:00
Compare commits
17
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d626686614 | ||
|
|
60ee33cb72 | ||
|
|
8d2498e96d | ||
|
|
1794484bed | ||
|
|
f2b6331843 | ||
|
|
8d5f33245f | ||
|
|
1ee544b842 | ||
|
|
0fd9463c7c | ||
|
|
ad5edf3aee | ||
|
|
d5fafd8cab | ||
|
|
92256f2fed | ||
|
|
5fa8d4c989 | ||
|
|
6b75f79f00 | ||
|
|
b0c9f29d90 | ||
|
|
7721afc116 | ||
|
|
bc85e9bbfa | ||
|
|
e8b6602fe5 |
@@ -29,8 +29,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 73
|
versionCode 74
|
||||||
versionName "0.9.44-beta"
|
versionName "0.9.45-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.
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ dependencies {
|
|||||||
implementation project(':capacitor-network')
|
implementation project(':capacitor-network')
|
||||||
implementation project(':capacitor-status-bar')
|
implementation project(':capacitor-status-bar')
|
||||||
implementation project(':capacitor-storage')
|
implementation project(':capacitor-storage')
|
||||||
implementation project(':robingenz-capacitor-app-update')
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:dist="http://schemas.android.com/apk/distribution"
|
xmlns:dist="http://schemas.android.com/apk/distribution"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:installLocation="preferExternal"
|
||||||
package="com.audiobookshelf.app">
|
package="com.audiobookshelf.app">
|
||||||
|
|
||||||
<!-- Permissions -->
|
<!-- Permissions -->
|
||||||
|
|||||||
@@ -22,9 +22,5 @@
|
|||||||
{
|
{
|
||||||
"pkg": "@capacitor/storage",
|
"pkg": "@capacitor/storage",
|
||||||
"classpath": "com.capacitorjs.plugins.storage.StoragePlugin"
|
"classpath": "com.capacitorjs.plugins.storage.StoragePlugin"
|
||||||
},
|
|
||||||
{
|
|
||||||
"pkg": "@robingenz/capacitor-app-update",
|
|
||||||
"classpath": "dev.robingenz.capacitor.appupdate.AppUpdatePlugin"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.audiobookshelf.app.data
|
package com.audiobookshelf.app.data
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.support.v4.media.MediaMetadataCompat
|
import android.support.v4.media.MediaMetadataCompat
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@@ -44,7 +45,7 @@ data class LocalLibraryItem(
|
|||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getDuration():Double {
|
fun getDuration():Double {
|
||||||
var total = 0.0
|
var total = 0.0
|
||||||
var audioTracks = media.getAudioTracks()
|
val audioTracks = media.getAudioTracks()
|
||||||
audioTracks.forEach{ total += it.duration }
|
audioTracks.forEach{ total += it.duration }
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
@@ -94,15 +95,17 @@ data class LocalLibraryItem(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getMediaMetadata(): MediaMetadataCompat {
|
fun getMediaMetadata(ctx: Context): MediaMetadataCompat {
|
||||||
|
val coverUri = getCoverUri()
|
||||||
|
|
||||||
return MediaMetadataCompat.Builder().apply {
|
return MediaMetadataCompat.Builder().apply {
|
||||||
putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, id)
|
putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, id)
|
||||||
putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, title)
|
putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, title)
|
||||||
putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
|
putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
|
||||||
putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, authorName)
|
putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, authorName)
|
||||||
putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, getCoverUri().toString())
|
putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, coverUri.toString())
|
||||||
putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, getCoverUri().toString())
|
putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, coverUri.toString())
|
||||||
putString(MediaMetadataCompat.METADATA_KEY_ART_URI, getCoverUri().toString())
|
putString(MediaMetadataCompat.METADATA_KEY_ART_URI, coverUri.toString())
|
||||||
putString(MediaMetadataCompat.METADATA_KEY_AUTHOR, authorName)
|
putString(MediaMetadataCompat.METADATA_KEY_AUTHOR, authorName)
|
||||||
}.build()
|
}.build()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,11 @@ package com.audiobookshelf.app.data
|
|||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.support.v4.media.MediaMetadataCompat
|
import android.support.v4.media.MediaMetadataCompat
|
||||||
import androidx.core.app.NotificationCompat
|
|
||||||
import com.audiobookshelf.app.R
|
import com.audiobookshelf.app.R
|
||||||
import com.audiobookshelf.app.device.DeviceManager
|
import com.audiobookshelf.app.device.DeviceManager
|
||||||
import com.audiobookshelf.app.player.MediaProgressSyncData
|
import com.audiobookshelf.app.player.MediaProgressSyncData
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||||
import com.google.android.exoplayer2.C
|
|
||||||
import com.google.android.exoplayer2.MediaItem
|
import com.google.android.exoplayer2.MediaItem
|
||||||
import com.google.android.exoplayer2.MediaMetadata
|
import com.google.android.exoplayer2.MediaMetadata
|
||||||
import com.google.android.gms.cast.MediaInfo
|
import com.google.android.gms.cast.MediaInfo
|
||||||
@@ -68,7 +66,7 @@ class PlaybackSession(
|
|||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getCurrentTrackIndex():Int {
|
fun getCurrentTrackIndex():Int {
|
||||||
for (i in 0..(audioTracks.size - 1)) {
|
for (i in 0..(audioTracks.size - 1)) {
|
||||||
var track = audioTracks[i]
|
val track = audioTracks[i]
|
||||||
if (currentTimeMs >= track.startOffsetMs && (track.endOffsetMs) > currentTimeMs) {
|
if (currentTimeMs >= track.startOffsetMs && (track.endOffsetMs) > currentTimeMs) {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
@@ -78,14 +76,14 @@ class PlaybackSession(
|
|||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getCurrentTrackTimeMs():Long {
|
fun getCurrentTrackTimeMs():Long {
|
||||||
var currentTrack = audioTracks[this.getCurrentTrackIndex()]
|
val currentTrack = audioTracks[this.getCurrentTrackIndex()]
|
||||||
var time = currentTime - currentTrack.startOffset
|
val time = currentTime - currentTrack.startOffset
|
||||||
return (time * 1000L).toLong()
|
return (time * 1000L).toLong()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getTrackStartOffsetMs(index:Int):Long {
|
fun getTrackStartOffsetMs(index:Int):Long {
|
||||||
var currentTrack = audioTracks[index]
|
val currentTrack = audioTracks[index]
|
||||||
return (currentTrack.startOffset * 1000L).toLong()
|
return (currentTrack.startOffset * 1000L).toLong()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +110,7 @@ class PlaybackSession(
|
|||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getMediaMetadataCompat(): MediaMetadataCompat {
|
fun getMediaMetadataCompat(): MediaMetadataCompat {
|
||||||
var metadataBuilder = MediaMetadataCompat.Builder()
|
val metadataBuilder = MediaMetadataCompat.Builder()
|
||||||
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, displayTitle)
|
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, displayTitle)
|
||||||
.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, displayTitle)
|
.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, displayTitle)
|
||||||
.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, displayAuthor)
|
.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, displayAuthor)
|
||||||
@@ -125,14 +123,14 @@ class PlaybackSession(
|
|||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getExoMediaMetadata(audioTrack:AudioTrack): MediaMetadata {
|
fun getExoMediaMetadata(audioTrack:AudioTrack): MediaMetadata {
|
||||||
var metadataBuilder = MediaMetadata.Builder()
|
val metadataBuilder = MediaMetadata.Builder()
|
||||||
.setTitle(displayTitle)
|
.setTitle(displayTitle)
|
||||||
.setDisplayTitle(displayTitle)
|
.setDisplayTitle(displayTitle)
|
||||||
.setArtist(displayAuthor)
|
.setArtist(displayAuthor)
|
||||||
.setAlbumArtist(displayAuthor)
|
.setAlbumArtist(displayAuthor)
|
||||||
.setSubtitle(displayAuthor)
|
.setSubtitle(displayAuthor)
|
||||||
|
|
||||||
var contentUri = this.getContentUri(audioTrack)
|
val contentUri = this.getContentUri(audioTrack)
|
||||||
metadataBuilder.setMediaUri(contentUri)
|
metadataBuilder.setMediaUri(contentUri)
|
||||||
|
|
||||||
return metadataBuilder.build()
|
return metadataBuilder.build()
|
||||||
@@ -140,15 +138,15 @@ class PlaybackSession(
|
|||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getMediaItems():List<MediaItem> {
|
fun getMediaItems():List<MediaItem> {
|
||||||
var mediaItems:MutableList<MediaItem> = mutableListOf()
|
val mediaItems:MutableList<MediaItem> = mutableListOf()
|
||||||
|
|
||||||
for (audioTrack in audioTracks) {
|
for (audioTrack in audioTracks) {
|
||||||
var mediaMetadata = this.getExoMediaMetadata(audioTrack)
|
val mediaMetadata = this.getExoMediaMetadata(audioTrack)
|
||||||
var mediaUri = this.getContentUri(audioTrack)
|
val mediaUri = this.getContentUri(audioTrack)
|
||||||
var mimeType = audioTrack.mimeType
|
val mimeType = audioTrack.mimeType
|
||||||
|
|
||||||
var queueItem = getQueueItem(audioTrack) // Queue item used in exo player CastManager
|
val queueItem = getQueueItem(audioTrack) // Queue item used in exo player CastManager
|
||||||
var mediaItem = MediaItem.Builder().setUri(mediaUri).setTag(queueItem).setMediaMetadata(mediaMetadata).setMimeType(mimeType).build()
|
val mediaItem = MediaItem.Builder().setUri(mediaUri).setTag(queueItem).setMediaMetadata(mediaMetadata).setMimeType(mimeType).build()
|
||||||
mediaItems.add(mediaItem)
|
mediaItems.add(mediaItem)
|
||||||
}
|
}
|
||||||
return mediaItems
|
return mediaItems
|
||||||
@@ -156,7 +154,7 @@ class PlaybackSession(
|
|||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getCastMediaMetadata(audioTrack:AudioTrack):com.google.android.gms.cast.MediaMetadata {
|
fun getCastMediaMetadata(audioTrack:AudioTrack):com.google.android.gms.cast.MediaMetadata {
|
||||||
var castMetadata = com.google.android.gms.cast.MediaMetadata(com.google.android.gms.cast.MediaMetadata.MEDIA_TYPE_AUDIOBOOK_CHAPTER)
|
val castMetadata = com.google.android.gms.cast.MediaMetadata(com.google.android.gms.cast.MediaMetadata.MEDIA_TYPE_AUDIOBOOK_CHAPTER)
|
||||||
|
|
||||||
coverPath?.let {
|
coverPath?.let {
|
||||||
castMetadata.addImage(WebImage(Uri.parse("$serverAddress/api/items/$libraryItemId/cover?token=${DeviceManager.token}")))
|
castMetadata.addImage(WebImage(Uri.parse("$serverAddress/api/items/$libraryItemId/cover?token=${DeviceManager.token}")))
|
||||||
@@ -171,11 +169,11 @@ class PlaybackSession(
|
|||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun getQueueItem(audioTrack:AudioTrack):MediaQueueItem {
|
fun getQueueItem(audioTrack:AudioTrack):MediaQueueItem {
|
||||||
var castMetadata = getCastMediaMetadata(audioTrack)
|
val castMetadata = getCastMediaMetadata(audioTrack)
|
||||||
|
|
||||||
var mediaUri = getContentUri(audioTrack)
|
val mediaUri = getContentUri(audioTrack)
|
||||||
|
|
||||||
var mediaInfo = MediaInfo.Builder(mediaUri.toString()).apply {
|
val mediaInfo = MediaInfo.Builder(mediaUri.toString()).apply {
|
||||||
setContentUrl(mediaUri.toString())
|
setContentUrl(mediaUri.toString())
|
||||||
setContentType(audioTrack.mimeType)
|
setContentType(audioTrack.mimeType)
|
||||||
setMetadata(castMetadata)
|
setMetadata(castMetadata)
|
||||||
|
|||||||
@@ -57,9 +57,9 @@ class MediaManager(var apiHandler: ApiHandler, var ctx: Context) {
|
|||||||
|
|
||||||
// TODO: Load currently listening category for local items
|
// TODO: Load currently listening category for local items
|
||||||
fun loadLocalCategory():List<LibraryCategory> {
|
fun loadLocalCategory():List<LibraryCategory> {
|
||||||
var localBooks = DeviceManager.dbManager.getLocalLibraryItems("book")
|
val localBooks = DeviceManager.dbManager.getLocalLibraryItems("book")
|
||||||
var localPodcasts = DeviceManager.dbManager.getLocalLibraryItems("podcast")
|
val localPodcasts = DeviceManager.dbManager.getLocalLibraryItems("podcast")
|
||||||
var cats = mutableListOf<LibraryCategory>()
|
val cats = mutableListOf<LibraryCategory>()
|
||||||
if (localBooks.isNotEmpty()) {
|
if (localBooks.isNotEmpty()) {
|
||||||
cats.add(LibraryCategory("local-books", "Local Books", "book", localBooks, true))
|
cats.add(LibraryCategory("local-books", "Local Books", "book", localBooks, true))
|
||||||
}
|
}
|
||||||
@@ -71,9 +71,9 @@ class MediaManager(var apiHandler: ApiHandler, var ctx: Context) {
|
|||||||
|
|
||||||
fun loadAndroidAutoItems(libraryId:String, cb: (List<LibraryCategory>) -> Unit) {
|
fun loadAndroidAutoItems(libraryId:String, cb: (List<LibraryCategory>) -> Unit) {
|
||||||
Log.d(tag, "Load android auto items for library id $libraryId")
|
Log.d(tag, "Load android auto items for library id $libraryId")
|
||||||
var cats = mutableListOf<LibraryCategory>()
|
val cats = mutableListOf<LibraryCategory>()
|
||||||
|
|
||||||
var localCategories = loadLocalCategory()
|
val localCategories = loadLocalCategory()
|
||||||
cats.addAll(localCategories)
|
cats.addAll(localCategories)
|
||||||
|
|
||||||
// Connected to server and has internet - load other cats
|
// Connected to server and has internet - load other cats
|
||||||
@@ -84,7 +84,7 @@ class MediaManager(var apiHandler: ApiHandler, var ctx: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadLibraries { libraries ->
|
loadLibraries { libraries ->
|
||||||
var library = libraries.find { it.id == libraryId } ?: libraries[0]
|
val library = libraries.find { it.id == libraryId } ?: libraries[0]
|
||||||
Log.d(tag, "Loading categories for library ${library.name} - ${library.id} - ${library.mediaType}")
|
Log.d(tag, "Loading categories for library ${library.name} - ${library.id} - ${library.mediaType}")
|
||||||
|
|
||||||
loadLibraryCategories(libraryId) { libraryCategories ->
|
loadLibraryCategories(libraryId) { libraryCategories ->
|
||||||
@@ -99,7 +99,7 @@ class MediaManager(var apiHandler: ApiHandler, var ctx: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadLibraryItems(libraryId) { libraryItems ->
|
loadLibraryItems(libraryId) { libraryItems ->
|
||||||
var mainCat = LibraryCategory("library", "Library", library.mediaType, libraryItems, false)
|
val mainCat = LibraryCategory("library", "Library", library.mediaType, libraryItems, false)
|
||||||
cats.add(mainCat)
|
cats.add(mainCat)
|
||||||
|
|
||||||
cb(cats)
|
cb(cats)
|
||||||
@@ -115,7 +115,7 @@ class MediaManager(var apiHandler: ApiHandler, var ctx: Context) {
|
|||||||
if (serverLibraryItems.isNotEmpty()) {
|
if (serverLibraryItems.isNotEmpty()) {
|
||||||
return serverLibraryItems[0]
|
return serverLibraryItems[0]
|
||||||
} else {
|
} else {
|
||||||
var localBooks = DeviceManager.dbManager.getLocalLibraryItems("book")
|
val localBooks = DeviceManager.dbManager.getLocalLibraryItems("book")
|
||||||
return if (localBooks.isNotEmpty()) return localBooks[0] else null
|
return if (localBooks.isNotEmpty()) return localBooks[0] else null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,10 +137,10 @@ class MediaManager(var apiHandler: ApiHandler, var ctx: Context) {
|
|||||||
|
|
||||||
fun play(libraryItemWrapper:LibraryItemWrapper, mediaPlayer:String, cb: (PlaybackSession) -> Unit) {
|
fun play(libraryItemWrapper:LibraryItemWrapper, mediaPlayer:String, cb: (PlaybackSession) -> Unit) {
|
||||||
if (libraryItemWrapper is LocalLibraryItem) {
|
if (libraryItemWrapper is LocalLibraryItem) {
|
||||||
var localLibraryItem = libraryItemWrapper as LocalLibraryItem
|
val localLibraryItem = libraryItemWrapper as LocalLibraryItem
|
||||||
cb(localLibraryItem.getPlaybackSession(null))
|
cb(localLibraryItem.getPlaybackSession(null))
|
||||||
} else {
|
} else {
|
||||||
var libraryItem = libraryItemWrapper as LibraryItem
|
val libraryItem = libraryItemWrapper as LibraryItem
|
||||||
apiHandler.playLibraryItem(libraryItem.id,"",false, mediaPlayer) {
|
apiHandler.playLibraryItem(libraryItem.id,"",false, mediaPlayer) {
|
||||||
cb(it)
|
cb(it)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-22
@@ -5,9 +5,6 @@ import android.graphics.Bitmap
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.support.v4.media.session.MediaControllerCompat
|
import android.support.v4.media.session.MediaControllerCompat
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.documentfile.provider.DocumentFile
|
|
||||||
import com.anggrayudi.storage.file.getAbsolutePath
|
|
||||||
import com.anggrayudi.storage.file.toRawFile
|
|
||||||
import com.audiobookshelf.app.R
|
import com.audiobookshelf.app.R
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
@@ -62,26 +59,10 @@ class AbMediaDescriptionAdapter constructor(private val controller: MediaControl
|
|||||||
|
|
||||||
private suspend fun resolveUriAsBitmap(uri: Uri): Bitmap? {
|
private suspend fun resolveUriAsBitmap(uri: Uri): Bitmap? {
|
||||||
return withContext(Dispatchers.IO) {
|
return withContext(Dispatchers.IO) {
|
||||||
// Block on downloading artwork.
|
|
||||||
val context = playerNotificationService.getContext()
|
|
||||||
|
|
||||||
// Fix attempt for #35 local cover crashing
|
|
||||||
// Convert content uri to a file and pass to Glide
|
|
||||||
var urival:Any = uri
|
|
||||||
if (uri.toString().startsWith("content:")) {
|
|
||||||
val imageDocFile = DocumentFile.fromSingleUri(context, uri)
|
|
||||||
Log.d(tag, "Converting local content url $uri to file with path ${imageDocFile?.getAbsolutePath(context)}")
|
|
||||||
val file = imageDocFile?.toRawFile(context)
|
|
||||||
file?.let {
|
|
||||||
Log.d(tag, "Using local file image instead of content uri ${it.absolutePath}")
|
|
||||||
urival = it
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Glide.with(context).applyDefaultRequestOptions(glideOptions)
|
Glide.with(playerNotificationService)
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.load(urival)
|
.load(uri)
|
||||||
.placeholder(R.drawable.icon)
|
.placeholder(R.drawable.icon)
|
||||||
.error(R.drawable.icon)
|
.error(R.drawable.icon)
|
||||||
.submit(NOTIFICATION_LARGE_ICON_SIZE, NOTIFICATION_LARGE_ICON_SIZE)
|
.submit(NOTIFICATION_LARGE_ICON_SIZE, NOTIFICATION_LARGE_ICON_SIZE)
|
||||||
@@ -89,7 +70,7 @@ class AbMediaDescriptionAdapter constructor(private val controller: MediaControl
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
|
|
||||||
Glide.with(context).applyDefaultRequestOptions(glideOptions)
|
Glide.with(playerNotificationService)
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.load(Uri.parse("android.resource://com.audiobookshelf.app/" + R.drawable.icon))
|
.load(Uri.parse("android.resource://com.audiobookshelf.app/" + R.drawable.icon))
|
||||||
.submit(NOTIFICATION_LARGE_ICON_SIZE, NOTIFICATION_LARGE_ICON_SIZE)
|
.submit(NOTIFICATION_LARGE_ICON_SIZE, NOTIFICATION_LARGE_ICON_SIZE)
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ class BrowseTree(
|
|||||||
|
|
||||||
// Server continue Listening cat
|
// Server continue Listening cat
|
||||||
libraryCategories.find { it.id == "continue-listening" }?.let { continueListeningCategory ->
|
libraryCategories.find { it.id == "continue-listening" }?.let { continueListeningCategory ->
|
||||||
var continueListeningMediaMetadata = continueListeningCategory.entities.map { liw ->
|
val continueListeningMediaMetadata = continueListeningCategory.entities.map { liw ->
|
||||||
var libraryItem = liw as LibraryItem
|
val libraryItem = liw as LibraryItem
|
||||||
libraryItem.getMediaMetadata()
|
libraryItem.getMediaMetadata()
|
||||||
}
|
}
|
||||||
if (continueListeningMediaMetadata.isNotEmpty()) {
|
if (continueListeningMediaMetadata.isNotEmpty()) {
|
||||||
@@ -74,8 +74,8 @@ class BrowseTree(
|
|||||||
|
|
||||||
// Server library cat
|
// Server library cat
|
||||||
libraryCategories.find { it.id == "library" }?.let { libraryCategory ->
|
libraryCategories.find { it.id == "library" }?.let { libraryCategory ->
|
||||||
var libraryMediaMetadata = libraryCategory.entities.map { libc ->
|
val libraryMediaMetadata = libraryCategory.entities.map { libc ->
|
||||||
var libraryItem = libc as LibraryItem
|
val libraryItem = libc as LibraryItem
|
||||||
libraryItem.getMediaMetadata()
|
libraryItem.getMediaMetadata()
|
||||||
}
|
}
|
||||||
libraryMediaMetadata.forEach {
|
libraryMediaMetadata.forEach {
|
||||||
@@ -86,9 +86,9 @@ class BrowseTree(
|
|||||||
}
|
}
|
||||||
|
|
||||||
libraryCategories.find { it.id == "local-books" }?.let { localBooksCat ->
|
libraryCategories.find { it.id == "local-books" }?.let { localBooksCat ->
|
||||||
var localMediaMetadata = localBooksCat.entities.map { libc ->
|
val localMediaMetadata = localBooksCat.entities.map { libc ->
|
||||||
var libraryItem = libc as LocalLibraryItem
|
val libraryItem = libc as LocalLibraryItem
|
||||||
libraryItem.getMediaMetadata()
|
libraryItem.getMediaMetadata(context)
|
||||||
}
|
}
|
||||||
localMediaMetadata.forEach {
|
localMediaMetadata.forEach {
|
||||||
val children = mediaIdToChildren[DOWNLOADS_ROOT] ?: mutableListOf()
|
val children = mediaIdToChildren[DOWNLOADS_ROOT] ?: mutableListOf()
|
||||||
|
|||||||
@@ -17,10 +17,8 @@ data class MediaProgressSyncData(
|
|||||||
var currentTime:Double // seconds
|
var currentTime:Double // seconds
|
||||||
)
|
)
|
||||||
|
|
||||||
class MediaProgressSyncer(playerNotificationService:PlayerNotificationService, apiHandler: ApiHandler) {
|
class MediaProgressSyncer(val playerNotificationService:PlayerNotificationService, private val apiHandler: ApiHandler) {
|
||||||
private val tag = "MediaProgressSync"
|
private val tag = "MediaProgressSync"
|
||||||
private val playerNotificationService:PlayerNotificationService = playerNotificationService
|
|
||||||
private val apiHandler = apiHandler
|
|
||||||
|
|
||||||
private var listeningTimerTask: TimerTask? = null
|
private var listeningTimerTask: TimerTask? = null
|
||||||
var listeningTimerRunning:Boolean = false
|
var listeningTimerRunning:Boolean = false
|
||||||
@@ -54,7 +52,7 @@ class MediaProgressSyncer(playerNotificationService:PlayerNotificationService, a
|
|||||||
listeningTimerTask = Timer("ListeningTimer", false).schedule(0L, 5000L) {
|
listeningTimerTask = Timer("ListeningTimer", false).schedule(0L, 5000L) {
|
||||||
Handler(Looper.getMainLooper()).post() {
|
Handler(Looper.getMainLooper()).post() {
|
||||||
if (playerNotificationService.currentPlayer.isPlaying) {
|
if (playerNotificationService.currentPlayer.isPlaying) {
|
||||||
var currentTime = playerNotificationService.getCurrentTimeSeconds()
|
val currentTime = playerNotificationService.getCurrentTimeSeconds()
|
||||||
sync(currentTime)
|
sync(currentTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,20 +63,20 @@ class MediaProgressSyncer(playerNotificationService:PlayerNotificationService, a
|
|||||||
if (!listeningTimerRunning) return
|
if (!listeningTimerRunning) return
|
||||||
Log.d(tag, "stop: Stopping listening for $currentDisplayTitle")
|
Log.d(tag, "stop: Stopping listening for $currentDisplayTitle")
|
||||||
|
|
||||||
var currentTime = playerNotificationService.getCurrentTimeSeconds()
|
val currentTime = playerNotificationService.getCurrentTimeSeconds()
|
||||||
sync(currentTime)
|
sync(currentTime)
|
||||||
reset()
|
reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sync(currentTime:Double) {
|
fun sync(currentTime:Double) {
|
||||||
var diffSinceLastSync = System.currentTimeMillis() - lastSyncTime
|
val diffSinceLastSync = System.currentTimeMillis() - lastSyncTime
|
||||||
if (diffSinceLastSync < 1000L) {
|
if (diffSinceLastSync < 1000L) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var listeningTimeToAdd = diffSinceLastSync / 1000L
|
val listeningTimeToAdd = diffSinceLastSync / 1000L
|
||||||
lastSyncTime = System.currentTimeMillis()
|
lastSyncTime = System.currentTimeMillis()
|
||||||
|
|
||||||
var syncData = MediaProgressSyncData(listeningTimeToAdd,currentPlaybackDuration,currentTime)
|
val syncData = MediaProgressSyncData(listeningTimeToAdd,currentPlaybackDuration,currentTime)
|
||||||
|
|
||||||
currentPlaybackSession?.syncData(syncData)
|
currentPlaybackSession?.syncData(syncData)
|
||||||
if (currentIsLocal) {
|
if (currentIsLocal) {
|
||||||
@@ -87,10 +85,16 @@ class MediaProgressSyncer(playerNotificationService:PlayerNotificationService, a
|
|||||||
DeviceManager.dbManager.saveLocalPlaybackSession(it)
|
DeviceManager.dbManager.saveLocalPlaybackSession(it)
|
||||||
saveLocalProgress(it)
|
saveLocalProgress(it)
|
||||||
|
|
||||||
// Send sync to server also if connected to this server and local item belongs to this server
|
// Local library item is linked to a server library item
|
||||||
if (it.serverConnectionConfigId != null && DeviceManager.serverConnectionConfig?.id == it.serverConnectionConfigId) {
|
if (!it.libraryItemId.isNullOrEmpty()) {
|
||||||
apiHandler.sendLocalProgressSync(it) {
|
// Send sync to server also if connected to this server and local item belongs to this server
|
||||||
Log.d(tag, "Local progress sync data sent to server $currentDisplayTitle for time $currentTime")
|
if (it.serverConnectionConfigId != null && DeviceManager.serverConnectionConfig?.id == it.serverConnectionConfigId) {
|
||||||
|
apiHandler.sendLocalProgressSync(it) {
|
||||||
|
Log.d(
|
||||||
|
tag,
|
||||||
|
"Local progress sync data sent to server $currentDisplayTitle for time $currentTime"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,7 +107,7 @@ class MediaProgressSyncer(playerNotificationService:PlayerNotificationService, a
|
|||||||
|
|
||||||
private fun saveLocalProgress(playbackSession:PlaybackSession) {
|
private fun saveLocalProgress(playbackSession:PlaybackSession) {
|
||||||
if (currentLocalMediaProgress == null) {
|
if (currentLocalMediaProgress == null) {
|
||||||
var mediaProgress = DeviceManager.dbManager.getLocalMediaProgress(playbackSession.localMediaProgressId)
|
val mediaProgress = DeviceManager.dbManager.getLocalMediaProgress(playbackSession.localMediaProgressId)
|
||||||
if (mediaProgress == null) {
|
if (mediaProgress == null) {
|
||||||
currentLocalMediaProgress = playbackSession.getNewLocalMediaProgress()
|
currentLocalMediaProgress = playbackSession.getNewLocalMediaProgress()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+15
-5
@@ -66,7 +66,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||||||
private lateinit var transportControls:MediaControllerCompat.TransportControls
|
private lateinit var transportControls:MediaControllerCompat.TransportControls
|
||||||
|
|
||||||
lateinit var mediaManager: MediaManager
|
lateinit var mediaManager: MediaManager
|
||||||
lateinit var apiHandler: ApiHandler
|
private lateinit var apiHandler: ApiHandler
|
||||||
|
|
||||||
lateinit var mPlayer: ExoPlayer
|
lateinit var mPlayer: ExoPlayer
|
||||||
lateinit var currentPlayer:Player
|
lateinit var currentPlayer:Player
|
||||||
@@ -75,7 +75,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||||||
lateinit var sleepTimerManager:SleepTimerManager
|
lateinit var sleepTimerManager:SleepTimerManager
|
||||||
lateinit var mediaProgressSyncer:MediaProgressSyncer
|
lateinit var mediaProgressSyncer:MediaProgressSyncer
|
||||||
|
|
||||||
private var notificationId = 10;
|
private var notificationId = 10
|
||||||
private var channelId = "audiobookshelf_channel"
|
private var channelId = "audiobookshelf_channel"
|
||||||
private var channelName = "Audiobookshelf Channel"
|
private var channelName = "Audiobookshelf Channel"
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||||||
// Android Auto Media Browser Service
|
// Android Auto Media Browser Service
|
||||||
if (SERVICE_INTERFACE == intent.action) {
|
if (SERVICE_INTERFACE == intent.action) {
|
||||||
Log.d(tag, "Is Media Browser Service")
|
Log.d(tag, "Is Media Browser Service")
|
||||||
return super.onBind(intent);
|
return super.onBind(intent)
|
||||||
}
|
}
|
||||||
return binder
|
return binder
|
||||||
}
|
}
|
||||||
@@ -245,11 +245,21 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||||||
mediaSessionConnector = MediaSessionConnector(mediaSession)
|
mediaSessionConnector = MediaSessionConnector(mediaSession)
|
||||||
val queueNavigator: TimelineQueueNavigator = object : TimelineQueueNavigator(mediaSession) {
|
val queueNavigator: TimelineQueueNavigator = object : TimelineQueueNavigator(mediaSession) {
|
||||||
override fun getMediaDescription(player: Player, windowIndex: Int): MediaDescriptionCompat {
|
override fun getMediaDescription(player: Player, windowIndex: Int): MediaDescriptionCompat {
|
||||||
|
val coverUri = currentPlaybackSession!!.getCoverUri()
|
||||||
|
|
||||||
|
// Fix for local images crashing on Android 11 for specific devices
|
||||||
|
// https://stackoverflow.com/questions/64186578/android-11-mediastyle-notification-crash/64232958#64232958
|
||||||
|
ctx.grantUriPermission(
|
||||||
|
"com.android.systemui",
|
||||||
|
coverUri,
|
||||||
|
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
|
)
|
||||||
|
|
||||||
return MediaDescriptionCompat.Builder()
|
return MediaDescriptionCompat.Builder()
|
||||||
.setMediaId(currentPlaybackSession!!.id)
|
.setMediaId(currentPlaybackSession!!.id)
|
||||||
.setTitle(currentPlaybackSession!!.displayTitle)
|
.setTitle(currentPlaybackSession!!.displayTitle)
|
||||||
.setSubtitle(currentPlaybackSession!!.displayAuthor)
|
.setSubtitle(currentPlaybackSession!!.displayAuthor)
|
||||||
.setIconUri(currentPlaybackSession!!.getCoverUri()).build()
|
.setIconUri(coverUri).build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,7 +377,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||||||
val libraryItemId = playbackSession.libraryItemId ?: "" // Must be true since direct play
|
val libraryItemId = playbackSession.libraryItemId ?: "" // Must be true since direct play
|
||||||
val episodeId = playbackSession.episodeId
|
val episodeId = playbackSession.episodeId
|
||||||
apiHandler.playLibraryItem(libraryItemId, episodeId, true, mediaPlayer) {
|
apiHandler.playLibraryItem(libraryItemId, episodeId, true, mediaPlayer) {
|
||||||
Handler(Looper.getMainLooper()).post() {
|
Handler(Looper.getMainLooper()).post {
|
||||||
preparePlayer(it, true, null)
|
preparePlayer(it, true, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,3 @@ project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacit
|
|||||||
|
|
||||||
include ':capacitor-storage'
|
include ':capacitor-storage'
|
||||||
project(':capacitor-storage').projectDir = new File('../node_modules/@capacitor/storage/android')
|
project(':capacitor-storage').projectDir = new File('../node_modules/@capacitor/storage/android')
|
||||||
|
|
||||||
include ':robingenz-capacitor-app-update'
|
|
||||||
project(':robingenz-capacitor-app-update').projectDir = new File('../node_modules/@robingenz/capacitor-app-update/android')
|
|
||||||
|
|||||||
+4
-1
@@ -16,6 +16,7 @@ body {
|
|||||||
min-height: calc(100% - 64px);
|
min-height: calc(100% - 64px);
|
||||||
max-height: calc(100% - 64px);
|
max-height: calc(100% - 64px);
|
||||||
}
|
}
|
||||||
|
|
||||||
#content.playerOpen {
|
#content.playerOpen {
|
||||||
height: calc(100% - 164px);
|
height: calc(100% - 164px);
|
||||||
min-height: calc(100% - 164px);
|
min-height: calc(100% - 164px);
|
||||||
@@ -46,6 +47,7 @@ body {
|
|||||||
.box-shadow-book {
|
.box-shadow-book {
|
||||||
box-shadow: 4px 1px 8px #11111166, -4px 1px 8px #11111166, 1px -4px 8px #11111166;
|
box-shadow: 4px 1px 8px #11111166, -4px 1px 8px #11111166, 1px -4px 8px #11111166;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shadow-height {
|
.shadow-height {
|
||||||
height: calc(100% - 4px);
|
height: calc(100% - 4px);
|
||||||
}
|
}
|
||||||
@@ -53,6 +55,7 @@ body {
|
|||||||
.bookshelfRow {
|
.bookshelfRow {
|
||||||
background-image: url(/wood_panels.jpg);
|
background-image: url(/wood_panels.jpg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.bookshelfDivider {
|
.bookshelfDivider {
|
||||||
background: rgb(149, 119, 90);
|
background: rgb(149, 119, 90);
|
||||||
background: linear-gradient(180deg, rgba(149, 119, 90, 1) 0%, rgba(103, 70, 37, 1) 17%, rgba(103, 70, 37, 1) 88%, rgba(71, 48, 25, 1) 100%);
|
background: linear-gradient(180deg, rgba(149, 119, 90, 1) 0%, rgba(103, 70, 37, 1) 17%, rgba(103, 70, 37, 1) 88%, rgba(71, 48, 25, 1) 100%);
|
||||||
@@ -63,9 +66,9 @@ body {
|
|||||||
Bookshelf Label
|
Bookshelf Label
|
||||||
*/
|
*/
|
||||||
.categoryPlacard {
|
.categoryPlacard {
|
||||||
background-image: url(https://image.freepik.com/free-photo/brown-wooden-textured-flooring-background_53876-128537.jpg);
|
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shinyBlack {
|
.shinyBlack {
|
||||||
background-color: #2d3436;
|
background-color: #2d3436;
|
||||||
background-image: linear-gradient(315deg, #19191a 0%, rgb(15, 15, 15) 74%);
|
background-image: linear-gradient(315deg, #19191a 0%, rgb(15, 15, 15) 74%);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="playbackSession" id="streamContainer" class="fixed top-0 left-0 layout-wrapper right-0 z-50 pointer-events-none" :class="showFullscreen ? 'fullscreen' : ''">
|
<div v-if="playbackSession" id="streamContainer" class="playerContainer fixed top-0 left-0 layout-wrapper right-0 z-50 pointer-events-none" :class="{ fullscreen: showFullscreen, 'ios-player': $platform === 'ios', 'web-player': $platform === 'web' }">
|
||||||
<div v-if="showFullscreen" class="w-full h-full z-10 bg-bg absolute top-0 left-0 pointer-events-auto">
|
<div v-if="showFullscreen" class="w-full h-full z-10 bg-bg absolute top-0 left-0 pointer-events-auto">
|
||||||
<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>
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
<p class="author-text text-white text-opacity-75 truncate">by {{ authorName }}</p>
|
<p class="author-text text-white text-opacity-75 truncate">by {{ authorName }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<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 id="playerContent" class="playerContainer 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 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">
|
<div class="flex items-center justify-between pointer-events-auto">
|
||||||
<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>
|
<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>
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
<div ref="bufferedTrack" class="h-full bg-gray-500 absolute top-0 left-0 pointer-events-none" />
|
<div ref="bufferedTrack" class="h-full bg-gray-500 absolute top-0 left-0 pointer-events-none" />
|
||||||
<div ref="playedTrack" class="h-full bg-gray-200 absolute top-0 left-0 pointer-events-none" />
|
<div ref="playedTrack" class="h-full bg-gray-200 absolute top-0 left-0 pointer-events-none" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex pt-0.5">
|
<div id="timestamp-row" class="flex pt-0.5">
|
||||||
<p class="font-mono text-white text-opacity-90" style="font-size: 0.8rem" ref="currentTimestamp">0:00</p>
|
<p class="font-mono text-white text-opacity-90" style="font-size: 0.8rem" ref="currentTimestamp">0:00</p>
|
||||||
<div class="flex-grow" />
|
<div class="flex-grow" />
|
||||||
<p v-show="showFullscreen" class="text-sm truncate text-white text-opacity-75" style="max-width: 65%">{{ currentChapterTitle }}</p>
|
<p v-show="showFullscreen" class="text-sm truncate text-white text-opacity-75" style="max-width: 65%">{{ currentChapterTitle }}</p>
|
||||||
@@ -659,13 +659,15 @@ export default {
|
|||||||
.bookCoverWrapper {
|
.bookCoverWrapper {
|
||||||
box-shadow: 3px -2px 5px #00000066;
|
box-shadow: 3px -2px 5px #00000066;
|
||||||
}
|
}
|
||||||
#streamContainer {
|
.playerContainer {
|
||||||
box-shadow: 0px -8px 8px #11111155;
|
|
||||||
height: 100px;
|
height: 100px;
|
||||||
}
|
}
|
||||||
.fullscreen #streamContainer {
|
.fullscreen .playerContainer {
|
||||||
height: 200px;
|
height: 200px;
|
||||||
}
|
}
|
||||||
|
#playerContent {
|
||||||
|
box-shadow: 0px -8px 8px #11111155;
|
||||||
|
}
|
||||||
|
|
||||||
#playerTrack {
|
#playerTrack {
|
||||||
transition: all 0.15s cubic-bezier(0.39, 0.575, 0.565, 1);
|
transition: all 0.15s cubic-bezier(0.39, 0.575, 0.565, 1);
|
||||||
@@ -676,6 +678,11 @@ export default {
|
|||||||
margin-bottom: 18px;
|
margin-bottom: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ios-player #timestamp-row {
|
||||||
|
padding-left: 16px;
|
||||||
|
padding-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.cover-wrapper {
|
.cover-wrapper {
|
||||||
bottom: 44px;
|
bottom: 44px;
|
||||||
left: 12px;
|
left: 12px;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<template v-for="shelf in totalShelves">
|
<template v-for="shelf in totalShelves">
|
||||||
<div :key="shelf" class="w-full px-2 relative" :class="showBookshelfListView ? '' : 'bookshelfRow'" :id="`shelf-${shelf - 1}`" :style="{ height: shelfHeight + 'px' }">
|
<div :key="shelf" class="w-full px-2 relative" :class="showBookshelfListView ? '' : 'bookshelfRow'" :id="`shelf-${shelf - 1}`" :style="{ height: shelfHeight + 'px' }">
|
||||||
<div v-if="!showBookshelfListView" class="bookshelfDivider w-full absolute bottom-0 left-0 z-30" style="min-height: 16px" :class="`h-${shelfDividerHeightIndex}`" />
|
<div v-if="!showBookshelfListView" class="bookshelfDivider w-full absolute bottom-0 left-0 z-30" style="min-height: 16px" :class="`h-${shelfDividerHeightIndex}`" />
|
||||||
<div v-else class="flex border-t border-white border-opacity-10 my-3 py-1"/>
|
<div v-else class="flex border-t border-white border-opacity-10" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -25,11 +25,11 @@ export default {
|
|||||||
mixins: [bookshelfCardsHelpers],
|
mixins: [bookshelfCardsHelpers],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
entitiesPerShelf: 2,
|
||||||
bookshelfHeight: 0,
|
bookshelfHeight: 0,
|
||||||
bookshelfWidth: 0,
|
bookshelfWidth: 0,
|
||||||
bookshelfMarginLeft: 0,
|
bookshelfMarginLeft: 0,
|
||||||
shelvesPerPage: 0,
|
shelvesPerPage: 0,
|
||||||
entitiesPerShelf: 2,
|
|
||||||
currentPage: 0,
|
currentPage: 0,
|
||||||
booksPerFetch: 20,
|
booksPerFetch: 20,
|
||||||
initialized: false,
|
initialized: false,
|
||||||
@@ -83,14 +83,11 @@ export default {
|
|||||||
filterBy() {
|
filterBy() {
|
||||||
return this.$store.getters['user/getUserSetting']('mobileFilterBy')
|
return this.$store.getters['user/getUserSetting']('mobileFilterBy')
|
||||||
},
|
},
|
||||||
coverAspectRatio() {
|
|
||||||
return this.$store.getters['getServerSetting']('coverAspectRatio')
|
|
||||||
},
|
|
||||||
isCoverSquareAspectRatio() {
|
isCoverSquareAspectRatio() {
|
||||||
return this.coverAspectRatio === this.$constants.BookCoverAspectRatio.SQUARE
|
return this.bookCoverAspectRatio === 1
|
||||||
},
|
},
|
||||||
bookCoverAspectRatio() {
|
bookCoverAspectRatio() {
|
||||||
return this.isCoverSquareAspectRatio ? 1 : 1.6
|
return this.$store.getters['getBookCoverAspectRatio']
|
||||||
},
|
},
|
||||||
bookWidth() {
|
bookWidth() {
|
||||||
var coverSize = 100
|
var coverSize = 100
|
||||||
@@ -119,7 +116,7 @@ export default {
|
|||||||
return this.$store.getters['libraries/getCurrentLibraryMediaType']
|
return this.$store.getters['libraries/getCurrentLibraryMediaType']
|
||||||
},
|
},
|
||||||
shelfHeight() {
|
shelfHeight() {
|
||||||
if (this.showBookshelfListView) return this.entityHeight
|
if (this.showBookshelfListView) return this.entityHeight + 16
|
||||||
return this.entityHeight + 40
|
return this.entityHeight + 40
|
||||||
},
|
},
|
||||||
totalEntityCardWidth() {
|
totalEntityCardWidth() {
|
||||||
@@ -300,7 +297,6 @@ export default {
|
|||||||
this.bookshelfHeight = clientHeight
|
this.bookshelfHeight = clientHeight
|
||||||
this.bookshelfWidth = clientWidth
|
this.bookshelfWidth = clientWidth
|
||||||
this.entitiesPerShelf = this.showBookshelfListView ? 1 : Math.floor((this.bookshelfWidth - 16) / this.totalEntityCardWidth)
|
this.entitiesPerShelf = this.showBookshelfListView ? 1 : Math.floor((this.bookshelfWidth - 16) / this.totalEntityCardWidth)
|
||||||
|
|
||||||
this.shelvesPerPage = Math.ceil(this.bookshelfHeight / this.shelfHeight) + 2
|
this.shelvesPerPage = Math.ceil(this.bookshelfHeight / this.shelfHeight) + 2
|
||||||
this.bookshelfMarginLeft = (this.bookshelfWidth - this.entitiesPerShelf * this.totalEntityCardWidth) / 2
|
this.bookshelfMarginLeft = (this.bookshelfWidth - this.entitiesPerShelf * this.totalEntityCardWidth) / 2
|
||||||
|
|
||||||
|
|||||||
@@ -353,7 +353,34 @@ export default {
|
|||||||
this.isSelectionMode = val
|
this.isSelectionMode = val
|
||||||
if (!val) this.selected = false
|
if (!val) this.selected = false
|
||||||
},
|
},
|
||||||
setEntity(libraryItem) {
|
setEntity(_libraryItem) {
|
||||||
|
var libraryItem = _libraryItem
|
||||||
|
|
||||||
|
// this code block is only necessary when showing a selected series with sequence #
|
||||||
|
// it will update the selected series so we get realtime updates for series sequence changes
|
||||||
|
if (this.series) {
|
||||||
|
// i know.. but the libraryItem passed to this func cannot be modified so we need to create a copy
|
||||||
|
libraryItem = {
|
||||||
|
..._libraryItem,
|
||||||
|
media: {
|
||||||
|
..._libraryItem.media,
|
||||||
|
metadata: {
|
||||||
|
..._libraryItem.media.metadata
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var mediaMetadata = libraryItem.media.metadata
|
||||||
|
if (mediaMetadata.series) {
|
||||||
|
var newSeries = mediaMetadata.series.find((se) => se.id === this.series.id)
|
||||||
|
if (newSeries) {
|
||||||
|
// update selected series
|
||||||
|
libraryItem.media.metadata.series = newSeries
|
||||||
|
this.libraryItem = libraryItem
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.libraryItem = libraryItem
|
this.libraryItem = libraryItem
|
||||||
},
|
},
|
||||||
setLocalLibraryItem(localLibraryItem) {
|
setLocalLibraryItem(localLibraryItem) {
|
||||||
|
|||||||
@@ -54,16 +54,16 @@ export default {
|
|||||||
updatedAt() {
|
updatedAt() {
|
||||||
return this._author.updatedAt
|
return this._author.updatedAt
|
||||||
},
|
},
|
||||||
serverAddres() {
|
serverAddress() {
|
||||||
return this.$store.getters['user/getServerAddress']
|
return this.$store.getters['user/getServerAddress']
|
||||||
},
|
},
|
||||||
imgSrc() {
|
imgSrc() {
|
||||||
if (!this.imagePath || !this.serverAddres) return null
|
if (!this.imagePath || !this.serverAddress) return null
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production' && this.serverAddress.startsWith('http://192.168')) {
|
||||||
// Testing
|
// Testing
|
||||||
return `http://localhost:3333/api/authors/${this.authorId}/image?token=${this.userToken}&ts=${this.updatedAt}`
|
return `http://localhost:3333/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}`
|
return `${this.serverAddress}/api/authors/${this.authorId}/image?token=${this.userToken}&ts=${this.updatedAt}`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|||||||
@@ -83,11 +83,7 @@ export default {
|
|||||||
this.$emit('input', val)
|
this.$emit('input', val)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isConnected() {
|
|
||||||
return this.$store.state.socketConnected
|
|
||||||
},
|
|
||||||
canCreateBookmark() {
|
canCreateBookmark() {
|
||||||
if (!this.isConnected) return false
|
|
||||||
return !this.bookmarks.find((bm) => bm.time === this.currentTime)
|
return !this.bookmarks.find((bm) => bm.time === this.currentTime)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,6 +23,10 @@
|
|||||||
3AF1970E2806E3CA0096F747 /* AbsAudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF1970D2806E3CA0096F747 /* AbsAudioPlayer.swift */; };
|
3AF1970E2806E3CA0096F747 /* AbsAudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF1970D2806E3CA0096F747 /* AbsAudioPlayer.swift */; };
|
||||||
3AF197102806E3DC0096F747 /* AbsAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AF1970F2806E3DC0096F747 /* AbsAudioPlayer.m */; };
|
3AF197102806E3DC0096F747 /* AbsAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3AF1970F2806E3DC0096F747 /* AbsAudioPlayer.m */; };
|
||||||
3AFCB5E827EA240D00ECCC05 /* NowPlayingInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AFCB5E727EA240D00ECCC05 /* NowPlayingInfo.swift */; };
|
3AFCB5E827EA240D00ECCC05 /* NowPlayingInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AFCB5E727EA240D00ECCC05 /* NowPlayingInfo.swift */; };
|
||||||
|
4D66B952282EE822008272D4 /* AbsDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D66B951282EE822008272D4 /* AbsDownloader.m */; };
|
||||||
|
4D66B954282EE87C008272D4 /* AbsDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D66B953282EE87C008272D4 /* AbsDownloader.swift */; };
|
||||||
|
4D66B956282EE951008272D4 /* AbsFileSystem.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D66B955282EE951008272D4 /* AbsFileSystem.m */; };
|
||||||
|
4D66B958282EEA14008272D4 /* AbsFileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D66B957282EEA14008272D4 /* AbsFileSystem.swift */; };
|
||||||
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; };
|
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; };
|
||||||
504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; };
|
504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; };
|
||||||
504EC30D1FED79650016851F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30B1FED79650016851F /* Main.storyboard */; };
|
504EC30D1FED79650016851F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30B1FED79650016851F /* Main.storyboard */; };
|
||||||
@@ -50,6 +54,10 @@
|
|||||||
3AF1970D2806E3CA0096F747 /* AbsAudioPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AbsAudioPlayer.swift; sourceTree = "<group>"; };
|
3AF1970D2806E3CA0096F747 /* AbsAudioPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AbsAudioPlayer.swift; sourceTree = "<group>"; };
|
||||||
3AF1970F2806E3DC0096F747 /* AbsAudioPlayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AbsAudioPlayer.m; sourceTree = "<group>"; };
|
3AF1970F2806E3DC0096F747 /* AbsAudioPlayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AbsAudioPlayer.m; sourceTree = "<group>"; };
|
||||||
3AFCB5E727EA240D00ECCC05 /* NowPlayingInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NowPlayingInfo.swift; sourceTree = "<group>"; };
|
3AFCB5E727EA240D00ECCC05 /* NowPlayingInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NowPlayingInfo.swift; sourceTree = "<group>"; };
|
||||||
|
4D66B951282EE822008272D4 /* AbsDownloader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AbsDownloader.m; sourceTree = "<group>"; };
|
||||||
|
4D66B953282EE87C008272D4 /* AbsDownloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AbsDownloader.swift; sourceTree = "<group>"; };
|
||||||
|
4D66B955282EE951008272D4 /* AbsFileSystem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AbsFileSystem.m; sourceTree = "<group>"; };
|
||||||
|
4D66B957282EEA14008272D4 /* AbsFileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AbsFileSystem.swift; sourceTree = "<group>"; };
|
||||||
4D8D412C26E187E400BA5F0D /* App-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "App-Bridging-Header.h"; sourceTree = "<group>"; };
|
4D8D412C26E187E400BA5F0D /* App-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "App-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = "<group>"; };
|
50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = "<group>"; };
|
||||||
504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@@ -111,6 +119,10 @@
|
|||||||
3AD4FCE628043E72006DB301 /* AbsDatabase.m */,
|
3AD4FCE628043E72006DB301 /* AbsDatabase.m */,
|
||||||
3AF1970D2806E3CA0096F747 /* AbsAudioPlayer.swift */,
|
3AF1970D2806E3CA0096F747 /* AbsAudioPlayer.swift */,
|
||||||
3AF1970F2806E3DC0096F747 /* AbsAudioPlayer.m */,
|
3AF1970F2806E3DC0096F747 /* AbsAudioPlayer.m */,
|
||||||
|
4D66B951282EE822008272D4 /* AbsDownloader.m */,
|
||||||
|
4D66B953282EE87C008272D4 /* AbsDownloader.swift */,
|
||||||
|
4D66B955282EE951008272D4 /* AbsFileSystem.m */,
|
||||||
|
4D66B957282EEA14008272D4 /* AbsFileSystem.swift */,
|
||||||
);
|
);
|
||||||
path = plugins;
|
path = plugins;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -303,16 +315,20 @@
|
|||||||
3ABF580928059BAE005DFBE5 /* PlaybackSession.swift in Sources */,
|
3ABF580928059BAE005DFBE5 /* PlaybackSession.swift in Sources */,
|
||||||
3ABF618F2804325C0070250E /* PlayerHandler.swift in Sources */,
|
3ABF618F2804325C0070250E /* PlayerHandler.swift in Sources */,
|
||||||
3AD4FCED28044E6C006DB301 /* Store.swift in Sources */,
|
3AD4FCED28044E6C006DB301 /* Store.swift in Sources */,
|
||||||
|
4D66B958282EEA14008272D4 /* AbsFileSystem.swift in Sources */,
|
||||||
3AF1970E2806E3CA0096F747 /* AbsAudioPlayer.swift in Sources */,
|
3AF1970E2806E3CA0096F747 /* AbsAudioPlayer.swift in Sources */,
|
||||||
3AD4FCE928043FD7006DB301 /* ServerConnectionConfig.swift in Sources */,
|
3AD4FCE928043FD7006DB301 /* ServerConnectionConfig.swift in Sources */,
|
||||||
3A200C1527D64D7E00CBF02E /* AudioPlayer.swift in Sources */,
|
3A200C1527D64D7E00CBF02E /* AudioPlayer.swift in Sources */,
|
||||||
|
4D66B956282EE951008272D4 /* AbsFileSystem.m in Sources */,
|
||||||
3AFCB5E827EA240D00ECCC05 /* NowPlayingInfo.swift in Sources */,
|
3AFCB5E827EA240D00ECCC05 /* NowPlayingInfo.swift in Sources */,
|
||||||
3AB34053280829BF0039308B /* Extensions.swift in Sources */,
|
3AB34053280829BF0039308B /* Extensions.swift in Sources */,
|
||||||
3AD4FCEB280443DD006DB301 /* Database.swift in Sources */,
|
3AD4FCEB280443DD006DB301 /* Database.swift in Sources */,
|
||||||
3AD4FCE528043E50006DB301 /* AbsDatabase.swift in Sources */,
|
3AD4FCE528043E50006DB301 /* AbsDatabase.swift in Sources */,
|
||||||
|
4D66B952282EE822008272D4 /* AbsDownloader.m in Sources */,
|
||||||
3AF197102806E3DC0096F747 /* AbsAudioPlayer.m in Sources */,
|
3AF197102806E3DC0096F747 /* AbsAudioPlayer.m in Sources */,
|
||||||
3AF1970C2806E2590096F747 /* ApiClient.swift in Sources */,
|
3AF1970C2806E2590096F747 /* ApiClient.swift in Sources */,
|
||||||
C4D0677528106D0C00B8F875 /* DataClasses.swift in Sources */,
|
C4D0677528106D0C00B8F875 /* DataClasses.swift in Sources */,
|
||||||
|
4D66B954282EE87C008272D4 /* AbsDownloader.swift in Sources */,
|
||||||
3AB34055280832720039308B /* PlayerEvents.swift in Sources */,
|
3AB34055280832720039308B /* PlayerEvents.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -484,7 +500,7 @@
|
|||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 7;
|
CURRENT_PROJECT_VERSION = 7;
|
||||||
DEVELOPMENT_TEAM = "";
|
DEVELOPMENT_TEAM = 7UFJ7D8V6A;
|
||||||
INFOPLIST_FILE = App/Info.plist;
|
INFOPLIST_FILE = App/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// AbsDownloader.m
|
||||||
|
// App
|
||||||
|
//
|
||||||
|
// Created by advplyr on 5/13/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <Capacitor/Capacitor.h>
|
||||||
|
|
||||||
|
CAP_PLUGIN(AbsDownloader, "AbsDownloader",
|
||||||
|
CAP_PLUGIN_METHOD(downloadLibraryItem, CAPPluginReturnPromise);
|
||||||
|
)
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
//
|
||||||
|
// AbsDownloader.swift
|
||||||
|
// App
|
||||||
|
//
|
||||||
|
// Created by advplyr on 5/13/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Capacitor
|
||||||
|
|
||||||
|
@objc(AbsDownloader)
|
||||||
|
public class AbsDownloader: CAPPlugin {
|
||||||
|
@objc func downloadLibraryItem(_ call: CAPPluginCall) {
|
||||||
|
let libraryItemId = call.getString("libraryItemId")
|
||||||
|
let episodeId = call.getString("episodeId")
|
||||||
|
|
||||||
|
NSLog("Download library item \(libraryItemId ?? "N/A") episode \(episodeId ?? "")")
|
||||||
|
|
||||||
|
ApiClient.getLibraryItemWithProgress(libraryItemId: libraryItemId!, episodeId: episodeId) { libraryItem in
|
||||||
|
if (libraryItem == nil) {
|
||||||
|
NSLog("Library item not found")
|
||||||
|
call.resolve()
|
||||||
|
} else {
|
||||||
|
NSLog("Got library item \(libraryItem!)")
|
||||||
|
|
||||||
|
// TODO: break out in seperate functions
|
||||||
|
libraryItem!.media.tracks?.forEach { track in
|
||||||
|
NSLog("TRACK \(track.contentUrl!)")
|
||||||
|
// filename needs to be encoded otherwise would just use contentUrl
|
||||||
|
let filename = track.metadata?.filename ?? ""
|
||||||
|
let filenameEncoded = filename.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
|
||||||
|
let urlstr = "\(Store.serverConfig!.address)/s/item/\(libraryItemId!)/\(filenameEncoded ?? "")?token=\(Store.serverConfig!.token)"
|
||||||
|
let url = URL(string: urlstr)!
|
||||||
|
|
||||||
|
|
||||||
|
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
||||||
|
let itemDirectory = documentsDirectory.appendingPathComponent("\(libraryItemId!)")
|
||||||
|
NSLog("ITEM DIR \(itemDirectory)")
|
||||||
|
|
||||||
|
// Create library item directory
|
||||||
|
do {
|
||||||
|
try FileManager.default.createDirectory(at: itemDirectory, withIntermediateDirectories: false)
|
||||||
|
} catch {
|
||||||
|
NSLog("Failed to CREATE LI DIRECTORY \(error)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output filename
|
||||||
|
let trackFilename = itemDirectory.appendingPathComponent("\(filename)")
|
||||||
|
|
||||||
|
let downloadTask = URLSession.shared.downloadTask(with: url) { urlOrNil, responseOrNil, errorOrNil in
|
||||||
|
|
||||||
|
guard let fileURL = urlOrNil else { return }
|
||||||
|
|
||||||
|
do {
|
||||||
|
NSLog("Download TMP file URL \(fileURL)")
|
||||||
|
let imageData = try Data(contentsOf:fileURL)
|
||||||
|
try imageData.write(to: trackFilename)
|
||||||
|
NSLog("Download written to \(trackFilename)")
|
||||||
|
} catch {
|
||||||
|
NSLog("FILE ERROR: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
downloadTask.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
call.resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
// AbsFileSystem.m
|
||||||
|
// App
|
||||||
|
//
|
||||||
|
// Created by advplyr on 5/13/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <Capacitor/Capacitor.h>
|
||||||
|
|
||||||
|
CAP_PLUGIN(AbsFileSystem, "AbsFileSystem",
|
||||||
|
CAP_PLUGIN_METHOD(selectFolder, CAPPluginReturnPromise);
|
||||||
|
CAP_PLUGIN_METHOD(checkFolderPermission, CAPPluginReturnPromise);
|
||||||
|
CAP_PLUGIN_METHOD(scanFolder, CAPPluginReturnPromise);
|
||||||
|
CAP_PLUGIN_METHOD(removeFolder, CAPPluginReturnPromise);
|
||||||
|
CAP_PLUGIN_METHOD(removeLocalLibraryItem, CAPPluginReturnPromise);
|
||||||
|
CAP_PLUGIN_METHOD(scanLocalLibraryItem, CAPPluginReturnPromise);
|
||||||
|
CAP_PLUGIN_METHOD(deleteItem, CAPPluginReturnPromise);
|
||||||
|
CAP_PLUGIN_METHOD(deleteTrackFromItem, CAPPluginReturnPromise);
|
||||||
|
)
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
//
|
||||||
|
// AbsFileSystem.swift
|
||||||
|
// App
|
||||||
|
//
|
||||||
|
// Created by advplyr on 5/13/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Capacitor
|
||||||
|
|
||||||
|
@objc(AbsFileSystem)
|
||||||
|
public class AbsFileSystem: CAPPlugin {
|
||||||
|
@objc func selectFolder(_ call: CAPPluginCall) {
|
||||||
|
let mediaType = call.getString("mediaType")
|
||||||
|
|
||||||
|
// TODO: Implement
|
||||||
|
NSLog("Select Folder for media type \(mediaType ?? "UNSET")")
|
||||||
|
|
||||||
|
call.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func checkFolderPermission(_ call: CAPPluginCall) {
|
||||||
|
let folderUrl = call.getString("folderUrl")
|
||||||
|
|
||||||
|
// TODO: Is this even necessary on iOS?
|
||||||
|
NSLog("checkFolderPermission for folder \(folderUrl ?? "UNSET")")
|
||||||
|
|
||||||
|
call.resolve([
|
||||||
|
"value": true
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func scanFolder(_ call: CAPPluginCall) {
|
||||||
|
let folderId = call.getString("folderId")
|
||||||
|
let forceAudioProbe = call.getBool("forceAudioProbe", false)
|
||||||
|
|
||||||
|
// TODO: Implement
|
||||||
|
NSLog("scanFolder \(folderId ?? "UNSET") | Force Probe = \(forceAudioProbe)")
|
||||||
|
|
||||||
|
call.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func removeFolder(_ call: CAPPluginCall) {
|
||||||
|
let folderId = call.getString("folderId")
|
||||||
|
|
||||||
|
// TODO: Implement
|
||||||
|
NSLog("removeFolder \(folderId ?? "UNSET")")
|
||||||
|
|
||||||
|
call.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func removeLocalLibraryItem(_ call: CAPPluginCall) {
|
||||||
|
let localLibraryItemId = call.getString("localLibraryItemId")
|
||||||
|
|
||||||
|
// TODO: Implement
|
||||||
|
NSLog("removeLocalLibraryItem \(localLibraryItemId ?? "UNSET")")
|
||||||
|
|
||||||
|
call.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func scanLocalLibraryItem(_ call: CAPPluginCall) {
|
||||||
|
let localLibraryItemId = call.getString("localLibraryItemId")
|
||||||
|
let forceAudioProbe = call.getBool("forceAudioProbe", false)
|
||||||
|
|
||||||
|
// TODO: Implement
|
||||||
|
NSLog("scanLocalLibraryItem \(localLibraryItemId ?? "UNSET") | Force Probe = \(forceAudioProbe)")
|
||||||
|
|
||||||
|
call.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func deleteItem(_ call: CAPPluginCall) {
|
||||||
|
let localLibraryItemId = call.getString("localLibraryItemId")
|
||||||
|
let contentUrl = call.getString("contentUrl")
|
||||||
|
|
||||||
|
// TODO: Implement
|
||||||
|
NSLog("deleteItem \(localLibraryItemId ?? "UNSET") url \(contentUrl ?? "UNSET")")
|
||||||
|
|
||||||
|
call.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func deleteTrackFromItem(_ call: CAPPluginCall) {
|
||||||
|
let localLibraryItemId = call.getString("localLibraryItemId")
|
||||||
|
let trackLocalFileId = call.getString("trackLocalFileId")
|
||||||
|
let contentUrl = call.getString("contentUrl")
|
||||||
|
|
||||||
|
// TODO: Implement
|
||||||
|
NSLog("deleteTrackFromItem \(localLibraryItemId ?? "UNSET") track file \(trackLocalFileId ?? "UNSET") url \(contentUrl ?? "UNSET")")
|
||||||
|
|
||||||
|
call.resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,6 @@ def capacitor_pods
|
|||||||
pod 'CapacitorNetwork', :path => '../../node_modules/@capacitor/network'
|
pod 'CapacitorNetwork', :path => '../../node_modules/@capacitor/network'
|
||||||
pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
|
pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
|
||||||
pod 'CapacitorStorage', :path => '../../node_modules/@capacitor/storage'
|
pod 'CapacitorStorage', :path => '../../node_modules/@capacitor/storage'
|
||||||
pod 'RobingenzCapacitorAppUpdate', :path => '../../node_modules/@robingenz/capacitor-app-update'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
target 'App' do
|
target 'App' do
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ struct LibraryItem: Codable {
|
|||||||
var folderId: String
|
var folderId: String
|
||||||
var path: String
|
var path: String
|
||||||
var relPath: String
|
var relPath: String
|
||||||
|
var isFile: Bool
|
||||||
var mtimeMs: Int64
|
var mtimeMs: Int64
|
||||||
var ctimeMs: Int64
|
var ctimeMs: Int64
|
||||||
var birthtimeMs: Int64
|
var birthtimeMs: Int64
|
||||||
@@ -27,6 +28,7 @@ struct LibraryItem: Codable {
|
|||||||
var mediaType: String
|
var mediaType: String
|
||||||
var media: MediaType
|
var media: MediaType
|
||||||
var libraryFiles: [LibraryFile]
|
var libraryFiles: [LibraryFile]
|
||||||
|
var userMediaProgress:MediaProgress?
|
||||||
}
|
}
|
||||||
struct MediaType: Codable {
|
struct MediaType: Codable {
|
||||||
var libraryItemId: String?
|
var libraryItemId: String?
|
||||||
@@ -125,3 +127,15 @@ struct LibraryFile: Codable {
|
|||||||
var ino: String
|
var ino: String
|
||||||
var metadata: FileMetadata
|
var metadata: FileMetadata
|
||||||
}
|
}
|
||||||
|
struct MediaProgress:Codable {
|
||||||
|
var id:String
|
||||||
|
var libraryItemId:String
|
||||||
|
var episodeId:String?
|
||||||
|
var duration:Double
|
||||||
|
var progress:Double
|
||||||
|
var currentTime:Double
|
||||||
|
var isFinished:Bool
|
||||||
|
var lastUpdate:Int64
|
||||||
|
var startedAt:Int64
|
||||||
|
var finishedAt:Int64?
|
||||||
|
}
|
||||||
|
|||||||
@@ -60,6 +60,27 @@ class ApiClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public static func getResource<T: Decodable>(endpoint: String, decodable: T.Type = T.self, callback: ((_ param: T?) -> Void)?) {
|
||||||
|
if (Store.serverConfig == nil) {
|
||||||
|
NSLog("Server config not set")
|
||||||
|
callback?(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let headers: HTTPHeaders = [
|
||||||
|
"Authorization": "Bearer \(Store.serverConfig!.token)"
|
||||||
|
]
|
||||||
|
|
||||||
|
AF.request("\(Store.serverConfig!.address)/\(endpoint)", method: .get, encoding: JSONEncoding.default, headers: headers).responseDecodable(of: decodable) { response in
|
||||||
|
switch response.result {
|
||||||
|
case .success(let obj):
|
||||||
|
callback?(obj)
|
||||||
|
case .failure(let error):
|
||||||
|
NSLog("api request to \(endpoint) failed")
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static func startPlaybackSession(libraryItemId: String, episodeId: String?, forceTranscode:Bool, callback: @escaping (_ param: PlaybackSession) -> Void) {
|
public static func startPlaybackSession(libraryItemId: String, episodeId: String?, forceTranscode:Bool, callback: @escaping (_ param: PlaybackSession) -> Void) {
|
||||||
var endpoint = "api/items/\(libraryItemId)/play"
|
var endpoint = "api/items/\(libraryItemId)/play"
|
||||||
@@ -83,4 +104,14 @@ class ApiClient {
|
|||||||
public static func reportPlaybackProgress(report: PlaybackReport, sessionId: String) {
|
public static func reportPlaybackProgress(report: PlaybackReport, sessionId: String) {
|
||||||
try? postResource(endpoint: "api/session/\(sessionId)/sync", parameters: report.asDictionary().mapValues({ value in "\(value)" }), callback: nil)
|
try? postResource(endpoint: "api/session/\(sessionId)/sync", parameters: report.asDictionary().mapValues({ value in "\(value)" }), callback: nil)
|
||||||
}
|
}
|
||||||
|
public static func getLibraryItemWithProgress(libraryItemId:String, episodeId:String?, callback: @escaping (_ param: LibraryItem?) -> Void) {
|
||||||
|
var endpoint = "api/items/\(libraryItemId)?expanded=1&include=progress"
|
||||||
|
if episodeId != nil {
|
||||||
|
endpoint += "&episodeId=\(episodeId!)"
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiClient.getResource(endpoint: endpoint, decodable: LibraryItem.self) { obj in
|
||||||
|
callback(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { AppUpdate } from '@robingenz/capacitor-app-update'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -79,36 +77,6 @@ export default {
|
|||||||
this.$refs.streamContainer.streamOpen(stream)
|
this.$refs.streamContainer.streamOpen(stream)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async clickUpdateToast() {
|
|
||||||
var immediateUpdateAllowed = this.$store.state.appUpdateInfo.immediateUpdateAllowed
|
|
||||||
if (immediateUpdateAllowed) {
|
|
||||||
await AppUpdate.performImmediateUpdate()
|
|
||||||
} else {
|
|
||||||
await AppUpdate.openAppStore()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async checkForUpdate() {
|
|
||||||
if (this.$platform == 'web') return
|
|
||||||
console.log('Checking for app update')
|
|
||||||
const result = await AppUpdate.getAppUpdateInfo()
|
|
||||||
if (!result) {
|
|
||||||
console.error('Invalid version check')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
console.log('App Update Info', JSON.stringify(result))
|
|
||||||
this.$store.commit('setAppUpdateInfo', result)
|
|
||||||
if (result.updateAvailability === 2) {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$toast.info(`Update is available! Click to update.`, {
|
|
||||||
draggable: false,
|
|
||||||
hideProgressBar: false,
|
|
||||||
timeout: 20000,
|
|
||||||
closeButton: true,
|
|
||||||
onClick: this.clickUpdateToast
|
|
||||||
})
|
|
||||||
}, 5000)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async loadSavedSettings() {
|
async loadSavedSettings() {
|
||||||
var userSavedServerSettings = await this.$localStore.getServerSettings()
|
var userSavedServerSettings = await this.$localStore.getServerSettings()
|
||||||
if (userSavedServerSettings) {
|
if (userSavedServerSettings) {
|
||||||
@@ -255,7 +223,6 @@ export default {
|
|||||||
console.log(`[default] finished connection attempt or already connected ${!!this.user}`)
|
console.log(`[default] finished connection attempt or already connected ${!!this.user}`)
|
||||||
await this.syncLocalMediaProgress()
|
await this.syncLocalMediaProgress()
|
||||||
this.$store.dispatch('globals/loadLocalMediaProgress')
|
this.$store.dispatch('globals/loadLocalMediaProgress')
|
||||||
this.checkForUpdate()
|
|
||||||
this.loadSavedSettings()
|
this.loadSavedSettings()
|
||||||
this.hasMounted = true
|
this.hasMounted = true
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+18766
-45
File diff suppressed because it is too large
Load Diff
+2
-3
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"name": "audiobookshelf-app",
|
"name": "audiobookshelf-app",
|
||||||
"version": "0.9.44-beta",
|
"version": "0.9.45-beta",
|
||||||
"author": "advplyr",
|
"author": "advplyr",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nuxt --hostname localhost --port 1337",
|
"dev": "nuxt --hostname 0.0.0.0 --port 1337",
|
||||||
"build": "nuxt build",
|
"build": "nuxt build",
|
||||||
"start": "nuxt start",
|
"start": "nuxt start",
|
||||||
"generate": "nuxt generate",
|
"generate": "nuxt generate",
|
||||||
@@ -23,7 +23,6 @@
|
|||||||
"@capacitor/status-bar": "^1.0.8",
|
"@capacitor/status-bar": "^1.0.8",
|
||||||
"@capacitor/storage": "^1.2.5",
|
"@capacitor/storage": "^1.2.5",
|
||||||
"@nuxtjs/axios": "^5.13.6",
|
"@nuxtjs/axios": "^5.13.6",
|
||||||
"@robingenz/capacitor-app-update": "^1.3.1",
|
|
||||||
"core-js": "^3.15.1",
|
"core-js": "^3.15.1",
|
||||||
"date-fns": "^2.25.0",
|
"date-fns": "^2.25.0",
|
||||||
"epubjs": "^0.3.88",
|
"epubjs": "^0.3.88",
|
||||||
|
|||||||
@@ -19,19 +19,10 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="font-mono pt-1 pb-4">{{ $config.version }}</p>
|
<p class="font-mono pt-1 pb-4">{{ $config.version }}</p>
|
||||||
|
|
||||||
<ui-btn v-if="isUpdateAvailable" class="w-full my-4" color="success" @click="clickUpdate">Update is available</ui-btn>
|
|
||||||
|
|
||||||
<ui-btn v-if="!isUpdateAvailable || immediateUpdateAllowed" class="w-full my-4" color="primary" @click="openAppStore">Open app store</ui-btn>
|
|
||||||
|
|
||||||
<p class="text-xs text-gray-400">UA: {{ updateAvailability }} | Avail: {{ availableVersion }} | Curr: {{ currentVersion }} | ImmedAllowed: {{ immediateUpdateAllowed }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { AppUpdate } from '@robingenz/capacitor-app-update'
|
|
||||||
import { AbsAudioPlayer } from '@/plugins/capacitor'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
asyncData({ redirect, store }) {
|
asyncData({ redirect, store }) {
|
||||||
if (!store.state.socketConnected) {
|
if (!store.state.socketConnected) {
|
||||||
@@ -58,24 +49,6 @@ export default {
|
|||||||
},
|
},
|
||||||
serverAddress() {
|
serverAddress() {
|
||||||
return this.serverConnectionConfig.address
|
return this.serverConnectionConfig.address
|
||||||
},
|
|
||||||
appUpdateInfo() {
|
|
||||||
return this.$store.state.appUpdateInfo
|
|
||||||
},
|
|
||||||
availableVersion() {
|
|
||||||
return this.appUpdateInfo ? this.appUpdateInfo.availableVersion : null
|
|
||||||
},
|
|
||||||
currentVersion() {
|
|
||||||
return this.appUpdateInfo ? this.appUpdateInfo.currentVersion : null
|
|
||||||
},
|
|
||||||
immediateUpdateAllowed() {
|
|
||||||
return this.appUpdateInfo ? !!this.appUpdateInfo.immediateUpdateAllowed : false
|
|
||||||
},
|
|
||||||
updateAvailability() {
|
|
||||||
return this.appUpdateInfo ? this.appUpdateInfo.updateAvailability : null
|
|
||||||
},
|
|
||||||
isUpdateAvailable() {
|
|
||||||
return this.updateAvailability === 2
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -85,16 +58,6 @@ export default {
|
|||||||
})
|
})
|
||||||
this.$server.logout()
|
this.$server.logout()
|
||||||
this.$router.push('/connect')
|
this.$router.push('/connect')
|
||||||
},
|
|
||||||
openAppStore() {
|
|
||||||
AppUpdate.openAppStore()
|
|
||||||
},
|
|
||||||
async clickUpdate() {
|
|
||||||
if (this.immediateUpdateAllowed) {
|
|
||||||
AppUpdate.performImmediateUpdate()
|
|
||||||
} else {
|
|
||||||
AppUpdate.openAppStore()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {}
|
mounted() {}
|
||||||
|
|||||||
+18
-8
@@ -49,7 +49,7 @@
|
|||||||
<span class="material-icons">auto_stories</span>
|
<span class="material-icons">auto_stories</span>
|
||||||
<span v-if="!showPlay" class="px-2 text-base">Read {{ ebookFormat }}</span>
|
<span v-if="!showPlay" class="px-2 text-base">Read {{ ebookFormat }}</span>
|
||||||
</ui-btn>
|
</ui-btn>
|
||||||
<ui-btn v-if="user && showPlay && !isIos && !hasLocal" :color="downloadItem ? 'warning' : 'primary'" class="flex items-center justify-center mr-2" :padding-x="2" @click="downloadClick">
|
<ui-btn v-if="user && showPlay && !hasLocal" :color="downloadItem ? 'warning' : 'primary'" class="flex items-center justify-center mr-2" :padding-x="2" @click="downloadClick">
|
||||||
<span class="material-icons" :class="downloadItem ? 'animate-pulse' : ''">{{ downloadItem ? 'downloading' : 'download' }}</span>
|
<span class="material-icons" :class="downloadItem ? 'animate-pulse' : ''">{{ downloadItem ? 'downloading' : 'download' }}</span>
|
||||||
</ui-btn>
|
</ui-btn>
|
||||||
<ui-read-icon-btn v-if="!isPodcast" :disabled="isProcessingReadUpdate" :is-read="userIsFinished" class="flex items-center justify-center" @click="toggleFinished" />
|
<ui-read-icon-btn v-if="!isPodcast" :disabled="isProcessingReadUpdate" :is-read="userIsFinished" class="flex items-center justify-center" @click="toggleFinished" />
|
||||||
@@ -319,13 +319,17 @@ export default {
|
|||||||
if (this.downloadItem) {
|
if (this.downloadItem) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.download()
|
|
||||||
},
|
|
||||||
async download(selectedLocalFolder = null) {
|
|
||||||
if (!this.numTracks) {
|
if (!this.numTracks) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (this.isIos) {
|
||||||
|
// no local folders on iOS
|
||||||
|
this.startDownload()
|
||||||
|
} else {
|
||||||
|
this.download()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async download(selectedLocalFolder = null) {
|
||||||
// Get the local folder to download to
|
// Get the local folder to download to
|
||||||
var localFolder = selectedLocalFolder
|
var localFolder = selectedLocalFolder
|
||||||
if (!localFolder) {
|
if (!localFolder) {
|
||||||
@@ -363,9 +367,15 @@ export default {
|
|||||||
this.startDownload(localFolder)
|
this.startDownload(localFolder)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async startDownload(localFolder) {
|
async startDownload(localFolder = null) {
|
||||||
console.log('Starting download to local folder', localFolder.name)
|
const payload = {
|
||||||
var downloadRes = await AbsDownloader.downloadLibraryItem({ libraryItemId: this.libraryItemId, localFolderId: localFolder.id })
|
libraryItemId: this.libraryItemId
|
||||||
|
}
|
||||||
|
if (localFolder) {
|
||||||
|
console.log('Starting download to local folder', localFolder.name)
|
||||||
|
payload.localFolderId = localFolder.id
|
||||||
|
}
|
||||||
|
var downloadRes = await AbsDownloader.downloadLibraryItem(payload)
|
||||||
if (downloadRes && downloadRes.error) {
|
if (downloadRes && downloadRes.error) {
|
||||||
var errorMsg = downloadRes.error || 'Unknown error'
|
var errorMsg = downloadRes.error || 'Unknown error'
|
||||||
console.error('Download error', errorMsg)
|
console.error('Download error', errorMsg)
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
import { Capacitor } from '@capacitor/core';
|
|
||||||
import { AbsDatabase } from './capacitor/AbsDatabase'
|
import { AbsDatabase } from './capacitor/AbsDatabase'
|
||||||
|
|
||||||
const isWeb = Capacitor.getPlatform() == 'web'
|
|
||||||
|
|
||||||
class DbService {
|
class DbService {
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ export const state = () => ({
|
|||||||
playerIsPlaying: false,
|
playerIsPlaying: false,
|
||||||
isCasting: false,
|
isCasting: false,
|
||||||
isCastAvailable: false,
|
isCastAvailable: false,
|
||||||
appUpdateInfo: null,
|
|
||||||
socketConnected: false,
|
socketConnected: false,
|
||||||
networkConnected: false,
|
networkConnected: false,
|
||||||
networkConnectionType: null,
|
networkConnectionType: null,
|
||||||
@@ -84,9 +83,6 @@ export const mutations = {
|
|||||||
setIsFirstLoad(state, val) {
|
setIsFirstLoad(state, val) {
|
||||||
state.isFirstLoad = val
|
state.isFirstLoad = val
|
||||||
},
|
},
|
||||||
setAppUpdateInfo(state, info) {
|
|
||||||
state.appUpdateInfo = info
|
|
||||||
},
|
|
||||||
setSocketConnected(state, val) {
|
setSocketConnected(state, val) {
|
||||||
state.socketConnected = val
|
state.socketConnected = val
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user