mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-07-28 07:28:35 +02:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48313b60c1 | ||
|
|
3be13025a8 | ||
|
|
505717e580 | ||
|
|
b4bb6bf81b | ||
|
|
6a8d901ce1 | ||
|
|
68f3f0e276 | ||
|
|
ddf8e66a08 | ||
|
|
5b7a492a48 |
@@ -29,8 +29,8 @@ android {
|
||||
applicationId "com.audiobookshelf.app"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 81
|
||||
versionName "0.9.50-beta"
|
||||
versionCode 82
|
||||
versionName "0.9.51-beta"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
||||
@@ -13,6 +13,7 @@ import androidx.core.app.ActivityCompat
|
||||
import com.anggrayudi.storage.SimpleStorage
|
||||
import com.anggrayudi.storage.SimpleStorageHelper
|
||||
import com.audiobookshelf.app.data.AbsDatabase
|
||||
import com.audiobookshelf.app.data.DbManager
|
||||
import com.audiobookshelf.app.player.PlayerNotificationService
|
||||
import com.audiobookshelf.app.plugins.AbsAudioPlayer
|
||||
import com.audiobookshelf.app.plugins.AbsDownloader
|
||||
@@ -53,14 +54,15 @@ class MainActivity : BridgeActivity() {
|
||||
// .build())
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
Log.d(tag, "onCreate")
|
||||
|
||||
DbManager.initialize(applicationContext)
|
||||
|
||||
// Grant full storage access for testing
|
||||
// var ss = SimpleStorage(this)
|
||||
// ss.requestFullStorageAccess()
|
||||
|
||||
var permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
val permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
if (permission != PackageManager.PERMISSION_GRANTED) {
|
||||
ActivityCompat.requestPermissions(this,
|
||||
PERMISSIONS_ALL,
|
||||
@@ -71,8 +73,6 @@ class MainActivity : BridgeActivity() {
|
||||
registerPlugin(AbsDownloader::class.java)
|
||||
registerPlugin(AbsFileSystem::class.java)
|
||||
registerPlugin(AbsDatabase::class.java)
|
||||
|
||||
Paper.init(applicationContext)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
package com.audiobookshelf.app.data
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.audiobookshelf.app.plugins.AbsDownloader
|
||||
import io.paperdb.Paper
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
|
||||
class DbManager {
|
||||
val tag = "DbManager"
|
||||
|
||||
companion object {
|
||||
var isDbInitialized = false
|
||||
|
||||
fun initialize(ctx: Context) {
|
||||
if (isDbInitialized) return
|
||||
Paper.init(ctx)
|
||||
isDbInitialized = true
|
||||
Log.i("DbManager", "Initialized Paper db")
|
||||
}
|
||||
}
|
||||
|
||||
fun getDeviceData(): DeviceData {
|
||||
return Paper.book("device").read("data") ?: DeviceData(mutableListOf(), null, null, DeviceSettings.default())
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import com.audiobookshelf.app.data.*
|
||||
import com.audiobookshelf.app.device.DeviceManager
|
||||
import com.audiobookshelf.app.server.ApiHandler
|
||||
import java.util.*
|
||||
import io.paperdb.Paper
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlin.coroutines.resume
|
||||
@@ -16,8 +15,6 @@ import kotlin.coroutines.suspendCoroutine
|
||||
class MediaManager(var apiHandler: ApiHandler, var ctx: Context) {
|
||||
val tag = "MediaManager"
|
||||
|
||||
var isPaperInitialized = false
|
||||
|
||||
var serverLibraryItems = listOf<LibraryItem>()
|
||||
var selectedLibraryId = ""
|
||||
|
||||
@@ -29,14 +26,6 @@ class MediaManager(var apiHandler: ApiHandler, var ctx: Context) {
|
||||
var serverLibraries = listOf<Library>()
|
||||
var serverConfigIdUsed:String? = null
|
||||
|
||||
fun initializeAndroidAuto() {
|
||||
if (!isPaperInitialized) {
|
||||
Log.d(tag, "Android Auto started when MainActivity was never started - initializing Paper")
|
||||
Paper.init(ctx)
|
||||
isPaperInitialized = true
|
||||
}
|
||||
}
|
||||
|
||||
fun getIsLibrary(id:String) : Boolean {
|
||||
return serverLibraries.find { it.id == id } != null
|
||||
}
|
||||
|
||||
+14
-3
@@ -160,6 +160,8 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||
super.onCreate()
|
||||
ctx = this
|
||||
|
||||
DbManager.initialize(ctx)
|
||||
|
||||
// Initialize API
|
||||
apiHandler = ApiHandler(ctx)
|
||||
|
||||
@@ -224,6 +226,11 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||
mediaSessionConnector = MediaSessionConnector(mediaSession)
|
||||
val queueNavigator: TimelineQueueNavigator = object : TimelineQueueNavigator(mediaSession) {
|
||||
override fun getMediaDescription(player: Player, windowIndex: Int): MediaDescriptionCompat {
|
||||
if (currentPlaybackSession == null) {
|
||||
Log.e(tag,"Playback session is not set - returning blank MediaDescriptionCompat")
|
||||
return MediaDescriptionCompat.Builder().build()
|
||||
}
|
||||
|
||||
val coverUri = currentPlaybackSession!!.getCoverUri()
|
||||
|
||||
// Fix for local images crashing on Android 11 for specific devices
|
||||
@@ -641,8 +648,13 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||
|
||||
fun closePlayback() {
|
||||
Log.d(tag, "closePlayback")
|
||||
currentPlayer.clearMediaItems()
|
||||
currentPlayer.stop()
|
||||
try {
|
||||
currentPlayer.stop()
|
||||
currentPlayer.clearMediaItems()
|
||||
} catch(e:Exception) {
|
||||
Log.e(tag, "Exception clearing exoplayer $e")
|
||||
}
|
||||
|
||||
currentPlaybackSession = null
|
||||
clientEventEmitter?.onPlaybackClosed()
|
||||
PlayerListener.lastPauseTime = 0
|
||||
@@ -716,7 +728,6 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||
null
|
||||
} else {
|
||||
isStarted = true
|
||||
mediaManager.initializeAndroidAuto()
|
||||
mediaManager.checkResetServerItems() // Reset any server items if no longer connected to server
|
||||
|
||||
isAndroidAuto = true
|
||||
|
||||
@@ -401,9 +401,13 @@ class AbsDownloader : Plugin() {
|
||||
|
||||
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)
|
||||
if (localFolderFile == null) {
|
||||
Log.e(tag, "Local Folder File from uri is null")
|
||||
} else {
|
||||
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..
|
||||
}
|
||||
|
||||
@@ -139,8 +139,9 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
showFullscreen() {
|
||||
showFullscreen(val) {
|
||||
this.updateScreenSize()
|
||||
this.$store.commit('setPlayerFullscreen', !!val)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -712,6 +713,10 @@ export default {
|
||||
var coverHeight = this.fullscreenBookCoverWidth * this.bookCoverAspectRatio
|
||||
document.documentElement.style.setProperty('--cover-image-width', this.fullscreenBookCoverWidth + 'px')
|
||||
document.documentElement.style.setProperty('--cover-image-height', coverHeight + 'px')
|
||||
},
|
||||
minimizePlayerEvt() {
|
||||
console.log('Minimize Player Evt')
|
||||
this.showFullscreen = false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -719,6 +724,8 @@ export default {
|
||||
if (screen.orientation) {
|
||||
screen.orientation.addEventListener('change', this.screenOrientationChange)
|
||||
}
|
||||
|
||||
this.$eventBus.$on('minimize-player', this.minimizePlayerEvt)
|
||||
document.body.addEventListener('touchstart', this.touchstart)
|
||||
document.body.addEventListener('touchend', this.touchend)
|
||||
document.body.addEventListener('touchmove', this.touchmove)
|
||||
@@ -735,6 +742,7 @@ export default {
|
||||
}
|
||||
|
||||
this.forceCloseDropdownMenu()
|
||||
this.$eventBus.$off('minimize-player', this.minimizePlayerEvt)
|
||||
document.body.removeEventListener('touchstart', this.touchstart)
|
||||
document.body.removeEventListener('touchend', this.touchend)
|
||||
document.body.removeEventListener('touchmove', this.touchmove)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<div v-else class="w-full">
|
||||
<form v-show="!showAuth" @submit.prevent="submit" novalidate class="w-full">
|
||||
<h2 class="text-lg leading-7 mb-2">Server address</h2>
|
||||
<ui-text-input v-model="serverConfig.address" :disabled="processing || !networkConnected || serverConfig.id" placeholder="http://55.55.55.55:13378" type="url" class="w-full h-10" />
|
||||
<ui-text-input v-model="serverConfig.address" :disabled="processing || !networkConnected || !!serverConfig.id" placeholder="http://55.55.55.55:13378" type="url" class="w-full h-10" />
|
||||
<div class="flex justify-end items-center mt-6">
|
||||
<!-- <div class="relative flex">
|
||||
<button class="outline-none uppercase tracking-wide font-semibold text-xs text-gray-300" type="button" @click="addCustomHeaders">Add Custom Headers</button>
|
||||
@@ -76,12 +76,6 @@
|
||||
import { Dialog } from '@capacitor/dialog'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
deviceData: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loggedIn: false,
|
||||
@@ -99,6 +93,9 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
deviceData() {
|
||||
return this.$store.state.deviceData || {}
|
||||
},
|
||||
networkConnected() {
|
||||
return this.$store.state.networkConnected
|
||||
},
|
||||
@@ -167,7 +164,10 @@ export default {
|
||||
if (value) {
|
||||
this.processing = true
|
||||
await this.$db.removeServerConnectionConfig(this.serverConfig.id)
|
||||
this.deviceData.serverConnectionConfigs = this.deviceData.serverConnectionConfigs.filter((scc) => scc.id != this.serverConfig.id)
|
||||
const updatedDeviceData = { ...this.deviceData }
|
||||
updatedDeviceData.serverConnectionConfigs = this.deviceData.serverConnectionConfigs.filter((scc) => scc.id != this.serverConfig.id)
|
||||
this.$store.commit('setDeviceData', updatedDeviceData)
|
||||
|
||||
this.serverConfig = {
|
||||
address: null,
|
||||
userId: null,
|
||||
@@ -185,7 +185,6 @@ export default {
|
||||
}
|
||||
this.showForm = true
|
||||
this.showAuth = true
|
||||
console.log('Edit server config', serverConfig)
|
||||
},
|
||||
newServerConfigClick() {
|
||||
this.serverConfig = {
|
||||
@@ -274,6 +273,12 @@ export default {
|
||||
this.error = 'Invalid username'
|
||||
return
|
||||
}
|
||||
const duplicateConfig = this.serverConnectionConfigs.find((scc) => scc.address === this.serverConfig.address && scc.username === this.serverConfig.username)
|
||||
if (duplicateConfig) {
|
||||
this.error = 'Config already exists for this address and username'
|
||||
return
|
||||
}
|
||||
|
||||
this.error = null
|
||||
this.processing = true
|
||||
|
||||
|
||||
@@ -77,6 +77,8 @@ export default {
|
||||
}
|
||||
},
|
||||
setShow() {
|
||||
this.$store.commit('globals/setIsModalOpen', true)
|
||||
|
||||
document.body.appendChild(this.el)
|
||||
setTimeout(() => {
|
||||
this.content.style.transform = 'scale(1)'
|
||||
@@ -84,18 +86,28 @@ export default {
|
||||
document.documentElement.classList.add('modal-open')
|
||||
},
|
||||
setHide() {
|
||||
this.$store.commit('globals/setIsModalOpen', false)
|
||||
|
||||
this.content.style.transform = 'scale(0)'
|
||||
this.el.remove()
|
||||
document.documentElement.classList.remove('modal-open')
|
||||
},
|
||||
closeModalEvt() {
|
||||
console.log('Close modal event')
|
||||
this.show = false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$eventBus.$on('close-modal', this.closeModalEvt)
|
||||
this.el = this.$refs.wrapper
|
||||
this.content = this.$refs.content
|
||||
this.content.style.transform = 'scale(0)'
|
||||
this.content.style.transition = 'transform 0.25s cubic-bezier(0.16, 1, 0.3, 1)'
|
||||
this.el.style.opacity = 1
|
||||
this.el.remove()
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$eventBus.$off('close-modal', this.closeModalEvt)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,13 +1,14 @@
|
||||
<template>
|
||||
<div class="w-full px-2 py-2 overflow-hidden relative">
|
||||
<div v-if="book" class="flex h-20">
|
||||
<div v-if="book" class="flex w-full">
|
||||
<div class="h-full relative" :style="{ width: bookWidth + 'px' }">
|
||||
<covers-book-cover :library-item="book" :width="bookWidth" :book-cover-aspect-ratio="bookCoverAspectRatio" />
|
||||
</div>
|
||||
<div class="w-80 h-full px-2 flex items-center">
|
||||
<div>
|
||||
<nuxt-link :to="`/item/${book.id}`" class="truncate text-sm">{{ bookTitle }}</nuxt-link>
|
||||
<div class="flex-grow book-table-content h-full px-2 flex items-center">
|
||||
<div class="max-w-full">
|
||||
<nuxt-link :to="`/item/${book.id}`" class="truncate block text-sm">{{ bookTitle }}</nuxt-link>
|
||||
<p class="truncate block text-gray-400 text-xs">{{ bookAuthor }}</p>
|
||||
<p class="text-xxs text-gray-500">{{ bookDuration }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -46,13 +47,13 @@ export default {
|
||||
return this.mediaMetadata.authorName || ''
|
||||
},
|
||||
bookDuration() {
|
||||
return this.$secondsToTimestamp(this.media.duration)
|
||||
return this.$elapsedPretty(this.media.duration)
|
||||
},
|
||||
bookCoverAspectRatio() {
|
||||
return this.$store.getters['getBookCoverAspectRatio']
|
||||
},
|
||||
bookWidth() {
|
||||
if (this.bookCoverAspectRatio === 1) return 80
|
||||
if (this.bookCoverAspectRatio === 1) return 50
|
||||
return 50
|
||||
},
|
||||
isMissing() {
|
||||
@@ -78,4 +79,11 @@ export default {
|
||||
},
|
||||
mounted() {}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.book-table-content {
|
||||
width: calc(100% - 50px);
|
||||
max-width: calc(100% - 50px);
|
||||
}
|
||||
</style>
|
||||
@@ -479,12 +479,12 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = Icons;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 10;
|
||||
CURRENT_PROJECT_VERSION = 11;
|
||||
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.50;
|
||||
MARKETING_VERSION = 0.9.51;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.audiobookshelf.app.dev;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -503,12 +503,12 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = Icons;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 10;
|
||||
CURRENT_PROJECT_VERSION = 11;
|
||||
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.50;
|
||||
MARKETING_VERSION = 0.9.51;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.audiobookshelf.app;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
|
||||
|
||||
@@ -23,8 +23,9 @@ public class AbsAudioPlayer: CAPPlugin {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(onPlaybackFailed), name: NSNotification.Name(PlayerEvents.failed.rawValue), object: nil)
|
||||
|
||||
self.bridge?.webView?.allowsBackForwardNavigationGestures = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@objc func prepareLibraryItem(_ call: CAPPluginCall) {
|
||||
let libraryItemId = call.getString("libraryItemId")
|
||||
let episodeId = call.getString("episodeId")
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "audiobookshelf-app",
|
||||
"version": "0.9.50-beta",
|
||||
"version": "0.9.51-beta",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "audiobookshelf-app",
|
||||
"version": "0.9.50-beta",
|
||||
"version": "0.9.51-beta",
|
||||
"author": "advplyr",
|
||||
"scripts": {
|
||||
"dev": "nuxt --hostname 0.0.0.0 --port 1337",
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<covers-collection-cover :book-items="bookItems" :width="240" :height="120 * bookCoverAspectRatio" :book-cover-aspect-ratio="bookCoverAspectRatio" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-grow px-2 py-6 md:py-0 md:px-10">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-grow py-6">
|
||||
<div class="flex items-center px-2">
|
||||
<h1 class="text-xl font-sans">
|
||||
{{ collectionName }}
|
||||
</h1>
|
||||
@@ -18,7 +18,7 @@
|
||||
</ui-btn>
|
||||
</div>
|
||||
|
||||
<div class="my-8 max-w-2xl">
|
||||
<div class="my-8 max-w-2xl px-2">
|
||||
<p class="text-base text-gray-100">{{ description }}</p>
|
||||
</div>
|
||||
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@
|
||||
|
||||
<!-- <p class="absolute bottom-16 left-0 right-0 px-2 text-center text-error"><strong>Important!</strong> This app requires that you are running <u>your own server</u> and does not provide any content.</p> -->
|
||||
|
||||
<connection-server-connect-form v-if="deviceData" :device-data="deviceData" />
|
||||
<connection-server-connect-form v-if="deviceData" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-center pt-4 fixed bottom-4 left-0 right-0">
|
||||
|
||||
@@ -131,6 +131,11 @@ class AbsAudioPlayerWeb extends WebPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
// PluginMethod
|
||||
async getIsCastAvailable() {
|
||||
return false
|
||||
}
|
||||
|
||||
initializePlayer() {
|
||||
if (document.getElementById('audio-player')) {
|
||||
document.getElementById('audio-player').remove()
|
||||
|
||||
@@ -52,7 +52,7 @@ class AbsDatabaseWeb extends WebPlugin {
|
||||
async removeServerConnectionConfig(serverConnectionConfigCallObject) {
|
||||
var serverConnectionConfigId = serverConnectionConfigCallObject.serverConnectionConfigId
|
||||
var deviceData = await this.getDeviceData()
|
||||
deviceData.serverConnectionConfigs = deviceData.serverConnectionConfigs.filter(ssc => ssc.id == serverConnectionConfigId)
|
||||
deviceData.serverConnectionConfigs = deviceData.serverConnectionConfigs.filter(ssc => ssc.id != serverConnectionConfigId)
|
||||
localStorage.setItem('device', JSON.stringify(deviceData))
|
||||
}
|
||||
|
||||
|
||||
+46
-19
@@ -14,21 +14,6 @@ if (Capacitor.getPlatform() != 'web') {
|
||||
setStatusBarStyleDark()
|
||||
}
|
||||
|
||||
App.addListener('backButton', async ({ canGoBack }) => {
|
||||
if (!canGoBack) {
|
||||
const { value } = await Dialog.confirm({
|
||||
title: 'Confirm',
|
||||
message: `Did you want to exit the app?`,
|
||||
})
|
||||
if (value) {
|
||||
App.exitApp()
|
||||
}
|
||||
|
||||
} else {
|
||||
window.history.back()
|
||||
}
|
||||
})
|
||||
|
||||
Vue.prototype.$isDev = process.env.NODE_ENV !== 'production'
|
||||
|
||||
Vue.prototype.$dateDistanceFromNow = (unixms) => {
|
||||
@@ -52,17 +37,20 @@ Vue.prototype.$bytesPretty = (bytes, decimals = 2) => {
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]
|
||||
}
|
||||
|
||||
Vue.prototype.$elapsedPretty = (seconds) => {
|
||||
Vue.prototype.$elapsedPretty = (seconds, useFullNames = false) => {
|
||||
if (seconds < 60) {
|
||||
return `${Math.floor(seconds)} sec${useFullNames ? 'onds' : ''}`
|
||||
}
|
||||
var minutes = Math.floor(seconds / 60)
|
||||
if (minutes < 70) {
|
||||
return `${minutes} min`
|
||||
return `${minutes} min${useFullNames ? `ute${minutes === 1 ? '' : 's'}` : ''}`
|
||||
}
|
||||
var hours = Math.floor(minutes / 60)
|
||||
minutes -= hours * 60
|
||||
if (!minutes) {
|
||||
return `${hours} hr`
|
||||
return `${hours} ${useFullNames ? 'hours' : 'hr'}`
|
||||
}
|
||||
return `${hours} hr ${minutes} min`
|
||||
return `${hours} ${useFullNames ? `hour${hours === 1 ? '' : 's'}` : 'hr'} ${minutes} ${useFullNames ? `minute${minutes === 1 ? '' : 's'}` : 'min'}`
|
||||
}
|
||||
|
||||
Vue.prototype.$secondsToTimestamp = (seconds) => {
|
||||
@@ -132,6 +120,45 @@ Vue.prototype.$encode = encode
|
||||
const decode = (text) => Buffer.from(decodeURIComponent(text), 'base64').toString()
|
||||
Vue.prototype.$decode = decode
|
||||
|
||||
export default ({ store, app }) => {
|
||||
// iOS Only
|
||||
// backButton event does not work with iOS swipe navigation so use this workaround
|
||||
if (app.router && Capacitor.getPlatform() === 'ios') {
|
||||
app.router.beforeEach((to, from, next) => {
|
||||
if (store.state.globals.isModalOpen) {
|
||||
Vue.prototype.$eventBus.$emit('close-modal')
|
||||
}
|
||||
if (store.state.playerIsFullscreen) {
|
||||
Vue.prototype.$eventBus.$emit('minimize-player')
|
||||
}
|
||||
next()
|
||||
})
|
||||
}
|
||||
|
||||
// Android only
|
||||
App.addListener('backButton', async ({ canGoBack }) => {
|
||||
if (store.state.globals.isModalOpen) {
|
||||
Vue.prototype.$eventBus.$emit('close-modal')
|
||||
return
|
||||
}
|
||||
if (store.state.playerIsFullscreen) {
|
||||
Vue.prototype.$eventBus.$emit('minimize-player')
|
||||
return
|
||||
}
|
||||
if (!canGoBack) {
|
||||
const { value } = await Dialog.confirm({
|
||||
title: 'Confirm',
|
||||
message: `Did you want to exit the app?`,
|
||||
})
|
||||
if (value) {
|
||||
App.exitApp()
|
||||
}
|
||||
} else {
|
||||
window.history.back()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export {
|
||||
encode,
|
||||
decode
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const state = () => ({
|
||||
isModalOpen: false,
|
||||
itemDownloads: [],
|
||||
bookshelfListView: false,
|
||||
series: null,
|
||||
@@ -93,6 +94,9 @@ export const actions = {
|
||||
}
|
||||
|
||||
export const mutations = {
|
||||
setIsModalOpen(state, val) {
|
||||
state.isModalOpen = val
|
||||
},
|
||||
addUpdateItemDownload(state, downloadItem) {
|
||||
var index = state.itemDownloads.findIndex(i => i.id == downloadItem.id)
|
||||
if (index >= 0) {
|
||||
|
||||
@@ -6,6 +6,7 @@ export const state = () => ({
|
||||
playerEpisodeId: null,
|
||||
playerIsLocal: false,
|
||||
playerIsPlaying: false,
|
||||
playerIsFullscreen: false,
|
||||
isCasting: false,
|
||||
isCastAvailable: false,
|
||||
socketConnected: false,
|
||||
@@ -93,6 +94,9 @@ export const mutations = {
|
||||
setPlayerPlaying(state, val) {
|
||||
state.playerIsPlaying = val
|
||||
},
|
||||
setPlayerFullscreen(state, val) {
|
||||
state.playerIsFullscreen = val
|
||||
},
|
||||
setHasStoragePermission(state, val) {
|
||||
state.hasStoragePermission = val
|
||||
},
|
||||
|
||||
@@ -33,6 +33,9 @@ module.exports = {
|
||||
sans: ['Source Sans Pro', ...defaultTheme.fontFamily.sans],
|
||||
mono: ['Ubuntu Mono', ...defaultTheme.fontFamily.mono],
|
||||
book: ['Gentium Book Basic', 'serif']
|
||||
},
|
||||
fontSize: {
|
||||
xxs: '0.625rem'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user