Compare commits

...
Author SHA1 Message Date
advplyr 67f514524f Remove unused imports and release log 2025-04-24 17:03:14 -05:00
advplyr d97c6a0872 Add:Android sleep timer setting to play a chime when almost finished #600 2025-04-24 16:42:55 -05:00
advplyr e7ad62760f Fix android cover image not showing in notification for downloaded media #1493, upgrade glide 2025-04-23 17:01:52 -05:00
advplyr fd34ea8124 Show change library dropdown in app bar when socket is not connected #1494 2025-04-23 15:46:19 -05:00
advplyr 5db94bf5b8 Fix series sequence text color on book cards in light mode #1549 2025-04-22 16:50:21 -05:00
advplyr 796d6d79d4 Add logs for android auto 2025-04-21 17:34:30 -05:00
advplyr 03aafafe1c Fix bug with AbsLogger not initialized 2025-04-21 17:11:23 -05:00
advplyr 3f303abc19 iOS version bump 0.9.80 2025-04-20 17:17:18 -05:00
advplyr 85d6958025 Version bump v0.9.80-beta 2025-04-20 16:54:49 -05:00
advplyrandGitHub 669bd7827b Merge pull request #1545 from weblate/weblate-audiobookshelf-abs-mobile-app
Translations update from Hosted Weblate
2025-04-20 16:17:36 -05:00
petr-prikrylandHosted Weblate 2b48f0c6a9 Translated using Weblate (Czech)
Currently translated at 100.0% (345 of 345 strings)

Translation: Audiobookshelf/Abs Mobile App
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-mobile-app/cs/
2025-04-20 21:11:10 +00:00
ABS translatorandHosted Weblate 2c44d38906 Translated using Weblate (Arabic)
Currently translated at 52.1% (180 of 345 strings)

Translation: Audiobookshelf/Abs Mobile App
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-mobile-app/ar/
2025-04-20 21:11:10 +00:00
Максим ГорпинічandHosted Weblate 750726ff6a Translated using Weblate (Ukrainian)
Currently translated at 100.0% (345 of 345 strings)

Translation: Audiobookshelf/Abs Mobile App
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-mobile-app/uk/
2025-04-20 21:11:09 +00:00
Jakob ZollandHosted Weblate dafab492fe Translated using Weblate (German)
Currently translated at 100.0% (345 of 345 strings)

Translation: Audiobookshelf/Abs Mobile App
Translate-URL: https://hosted.weblate.org/projects/audiobookshelf/abs-mobile-app/de/
2025-04-20 21:11:08 +00:00
advplyr 6419c8dc3a Update deploy-apk workflow for java 21 2025-04-20 16:10:54 -05:00
advplyrandGitHub 3bb5ce5924 Merge pull request #1546 from advplyr/abslogger
Logs page with AbsLogger plugin
2025-04-20 16:09:14 -05:00
24 changed files with 237 additions and 68 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
uses: actions/setup-java@v2 uses: actions/setup-java@v2
with: with:
distribution: 'temurin' distribution: 'temurin'
java-version: 17 java-version: 21
- name: install dependencies - name: install dependencies
run: npm ci run: npm ci
+2 -2
View File
@@ -36,8 +36,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 110 versionCode 111
versionName "0.9.79-beta" versionName "0.9.80-beta"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
manifestPlaceholders = [ manifestPlaceholders = [
"appAuthRedirectScheme": "com.audiobookshelf.app" "appAuthRedirectScheme": "com.audiobookshelf.app"
@@ -135,6 +135,7 @@ data class DeviceSettings(
var sleepTimerLength: Long, // Time in milliseconds var sleepTimerLength: Long, // Time in milliseconds
var disableSleepTimerFadeOut: Boolean, var disableSleepTimerFadeOut: Boolean,
var disableSleepTimerResetFeedback: Boolean, var disableSleepTimerResetFeedback: Boolean,
var enableSleepTimerAlmostDoneChime: Boolean,
var languageCode: String, var languageCode: String,
var downloadUsingCellular: DownloadUsingCellularSetting, var downloadUsingCellular: DownloadUsingCellularSetting,
var streamingUsingCellular: StreamingUsingCellularSetting, var streamingUsingCellular: StreamingUsingCellularSetting,
@@ -163,6 +164,7 @@ data class DeviceSettings(
autoSleepTimerAutoRewindTime = 300000L, // 5 minutes autoSleepTimerAutoRewindTime = 300000L, // 5 minutes
disableSleepTimerFadeOut = false, disableSleepTimerFadeOut = false,
disableSleepTimerResetFeedback = false, disableSleepTimerResetFeedback = false,
enableSleepTimerAlmostDoneChime = false,
languageCode = "en-us", languageCode = "en-us",
downloadUsingCellular = DownloadUsingCellularSetting.ALWAYS, downloadUsingCellular = DownloadUsingCellularSetting.ALWAYS,
streamingUsingCellular = StreamingUsingCellularSetting.ALWAYS, streamingUsingCellular = StreamingUsingCellularSetting.ALWAYS,
@@ -188,9 +190,9 @@ data class DeviceSettings(
@JsonIgnore @JsonIgnore
fun getShakeThresholdGravity() : Float { // Used in ShakeDetector fun getShakeThresholdGravity() : Float { // Used in ShakeDetector
return if (shakeSensitivity == ShakeSensitivitySetting.VERY_HIGH) 1.2f return if (shakeSensitivity == ShakeSensitivitySetting.VERY_HIGH) 1.1f
else if (shakeSensitivity == ShakeSensitivitySetting.HIGH) 1.4f else if (shakeSensitivity == ShakeSensitivitySetting.HIGH) 1.3f
else if (shakeSensitivity == ShakeSensitivitySetting.MEDIUM) 1.6f else if (shakeSensitivity == ShakeSensitivitySetting.MEDIUM) 1.5f
else if (shakeSensitivity == ShakeSensitivitySetting.LOW) 2f else if (shakeSensitivity == ShakeSensitivitySetting.LOW) 2f
else if (shakeSensitivity == ShakeSensitivitySetting.VERY_LOW) 2.7f else if (shakeSensitivity == ShakeSensitivitySetting.VERY_LOW) 2.7f
else { else {
@@ -34,6 +34,7 @@ object DeviceManager {
val serverConnectionConfigId get() = serverConnectionConfig?.id ?: "" val serverConnectionConfigId get() = serverConnectionConfig?.id ?: ""
val serverConnectionConfigName get() = serverConnectionConfig?.name ?: "" val serverConnectionConfigName get() = serverConnectionConfig?.name ?: ""
val serverConnectionConfigString get() = serverConnectionConfig?.name ?: "No server connection"
val serverAddress val serverAddress
get() = serverConnectionConfig?.address ?: "" get() = serverConnectionConfig?.address ?: ""
val serverUserId val serverUserId
@@ -63,6 +64,10 @@ object DeviceManager {
if (deviceData.deviceSettings?.autoSleepTimerAutoRewindTime == null) { if (deviceData.deviceSettings?.autoSleepTimerAutoRewindTime == null) {
deviceData.deviceSettings?.autoSleepTimerAutoRewindTime = 300000L // 5 minutes deviceData.deviceSettings?.autoSleepTimerAutoRewindTime = 300000L // 5 minutes
} }
// Initialize sleep timer almost done chime added in v0.9.81
if (deviceData.deviceSettings?.enableSleepTimerAlmostDoneChime == null) {
deviceData.deviceSettings?.enableSleepTimerAlmostDoneChime = false
}
// Language added in v0.9.69 // Language added in v0.9.69
if (deviceData.deviceSettings?.languageCode == null) { if (deviceData.deviceSettings?.languageCode == null) {
@@ -1,15 +1,20 @@
package com.audiobookshelf.app.managers package com.audiobookshelf.app.managers
import android.content.Context import android.content.Context
import android.media.MediaPlayer
import android.os.* import android.os.*
import android.util.Log import android.util.Log
import com.audiobookshelf.app.R
import com.audiobookshelf.app.device.DeviceManager import com.audiobookshelf.app.device.DeviceManager
import com.audiobookshelf.app.player.PlayerNotificationService import com.audiobookshelf.app.player.PlayerNotificationService
import com.audiobookshelf.app.player.SLEEP_TIMER_WAKE_UP_EXPIRATION import com.audiobookshelf.app.player.SLEEP_TIMER_WAKE_UP_EXPIRATION
import com.audiobookshelf.app.plugins.AbsLogger
import java.util.* import java.util.*
import kotlin.concurrent.schedule import kotlin.concurrent.schedule
import kotlin.math.roundToInt import kotlin.math.roundToInt
const val SLEEP_TIMER_CHIME_SOUND_VOLUME = 0.7f
class SleepTimerManager class SleepTimerManager
constructor(private val playerNotificationService: PlayerNotificationService) { constructor(private val playerNotificationService: PlayerNotificationService) {
private val tag = "SleepTimerManager" private val tag = "SleepTimerManager"
@@ -156,6 +161,10 @@ constructor(private val playerNotificationService: PlayerNotificationService) {
) )
} }
if (sleepTimeSecondsRemaining == 30 && sleepTimerElapsed > 1 && DeviceManager.deviceData.deviceSettings?.enableSleepTimerAlmostDoneChime == true) {
playChimeSound()
}
if (sleepTimeSecondsRemaining <= 0) { if (sleepTimeSecondsRemaining <= 0) {
Log.d(tag, "Sleep Timer Pausing Player on Chapter") Log.d(tag, "Sleep Timer Pausing Player on Chapter")
pause() pause()
@@ -263,6 +272,18 @@ constructor(private val playerNotificationService: PlayerNotificationService) {
} }
} }
/** Plays chime sound */
private fun playChimeSound() {
AbsLogger.info(tag, "playChimeSound: Playing sleep timer chime sound")
val ctx = playerNotificationService.getContext()
val mediaPlayer = MediaPlayer.create(ctx, R.raw.bell)
mediaPlayer.setVolume(SLEEP_TIMER_CHIME_SOUND_VOLUME, SLEEP_TIMER_CHIME_SOUND_VOLUME)
mediaPlayer.start()
mediaPlayer.setOnCompletionListener {
mediaPlayer.release()
}
}
/** /**
* Gets the chapter end time for use in End of Chapter timers. If less than 10 seconds remain in * Gets the chapter end time for use in End of Chapter timers. If less than 10 seconds remain in
* the chapter, then use the next chapter. * the chapter, then use the next chapter.
@@ -136,7 +136,6 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
val serverConnConfig = if (DeviceManager.isConnectedToServer) DeviceManager.serverConnectionConfig else DeviceManager.deviceData.getLastServerConnectionConfig() val serverConnConfig = if (DeviceManager.isConnectedToServer) DeviceManager.serverConnectionConfig else DeviceManager.deviceData.getLastServerConnectionConfig()
if (!DeviceManager.isConnectedToServer || !DeviceManager.checkConnectivity(ctx) || serverConnConfig == null || serverConnConfig.id !== serverConfigIdUsed) { if (!DeviceManager.isConnectedToServer || !DeviceManager.checkConnectivity(ctx) || serverConnConfig == null || serverConnConfig.id !== serverConfigIdUsed) {
Log.d(tag, "Reset caches")
podcastEpisodeLibraryItemMap = mutableMapOf() podcastEpisodeLibraryItemMap = mutableMapOf()
serverLibraries = listOf() serverLibraries = listOf()
serverLibraryItems = mutableListOf() serverLibraryItems = mutableListOf()
@@ -7,7 +7,6 @@ import android.net.Uri
import android.os.Build import android.os.Build
import android.provider.MediaStore import android.provider.MediaStore
import android.support.v4.media.session.MediaControllerCompat import android.support.v4.media.session.MediaControllerCompat
import android.util.Log
import com.audiobookshelf.app.BuildConfig import com.audiobookshelf.app.BuildConfig
import com.audiobookshelf.app.R import com.audiobookshelf.app.R
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
@@ -41,7 +40,6 @@ class AbMediaDescriptionAdapter constructor(private val controller: MediaControl
// Cache the bitmap for the current audiobook so that successive calls to // Cache the bitmap for the current audiobook so that successive calls to
// `getCurrentLargeIcon` don't cause the bitmap to be recreated. // `getCurrentLargeIcon` don't cause the bitmap to be recreated.
currentIconUri = albumArtUri currentIconUri = albumArtUri
Log.d(tag, "ART $currentIconUri")
if (currentIconUri.toString().startsWith("content://")) { if (currentIconUri.toString().startsWith("content://")) {
currentBitmap = if (Build.VERSION.SDK_INT < 28) { currentBitmap = if (Build.VERSION.SDK_INT < 28) {
@@ -8,7 +8,6 @@ import android.util.Log
import android.view.KeyEvent import android.view.KeyEvent
import com.audiobookshelf.app.data.LibraryItemWrapper import com.audiobookshelf.app.data.LibraryItemWrapper
import com.audiobookshelf.app.data.PodcastEpisode import com.audiobookshelf.app.data.PodcastEpisode
import com.audiobookshelf.app.device.DeviceManager
import java.util.* import java.util.*
import kotlin.concurrent.schedule import kotlin.concurrent.schedule
@@ -4,14 +4,11 @@ import android.annotation.SuppressLint
import android.app.* import android.app.*
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Color import android.graphics.Color
import android.graphics.ImageDecoder
import android.hardware.Sensor import android.hardware.Sensor
import android.hardware.SensorManager import android.hardware.SensorManager
import android.net.* import android.net.*
import android.os.* import android.os.*
import android.provider.MediaStore
import android.provider.Settings import android.provider.Settings
import android.support.v4.media.MediaBrowserCompat import android.support.v4.media.MediaBrowserCompat
import android.support.v4.media.MediaDescriptionCompat import android.support.v4.media.MediaDescriptionCompat
@@ -311,19 +308,6 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
val coverUri = currentPlaybackSession!!.getCoverUri(ctx) val coverUri = currentPlaybackSession!!.getCoverUri(ctx)
var bitmap: Bitmap? = null
// Local covers get bitmap
if (currentPlaybackSession!!.localLibraryItem?.coverContentUrl != null) {
bitmap =
if (Build.VERSION.SDK_INT < 28) {
MediaStore.Images.Media.getBitmap(ctx.contentResolver, coverUri)
} else {
val source: ImageDecoder.Source =
ImageDecoder.createSource(ctx.contentResolver, coverUri)
ImageDecoder.decodeBitmap(source)
}
}
// Fix for local images crashing on Android 11 for specific devices // Fix for local images crashing on Android 11 for specific devices
// https://stackoverflow.com/questions/64186578/android-11-mediastyle-notification-crash/64232958#64232958 // https://stackoverflow.com/questions/64186578/android-11-mediastyle-notification-crash/64232958#64232958
try { try {
@@ -347,8 +331,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
.setExtras(extra) .setExtras(extra)
.setTitle(currentPlaybackSession!!.displayTitle) .setTitle(currentPlaybackSession!!.displayTitle)
bitmap?.let { mediaDescriptionBuilder.setIconBitmap(it) } mediaDescriptionBuilder.setIconUri(coverUri)
?: mediaDescriptionBuilder.setIconUri(coverUri)
return mediaDescriptionBuilder.build() return mediaDescriptionBuilder.build()
} }
@@ -1106,11 +1089,12 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
// No further calls will be made to other media browsing methods. // No further calls will be made to other media browsing methods.
null null
} else { } else {
Log.d(tag, "Android Auto starting $clientPackageName $clientUid") AbsLogger.info(tag, "onGetRoot: clientPackageName: $clientPackageName, clientUid: $clientUid")
isStarted = true isStarted = true
// Reset cache if no longer connected to server or server changed // Reset cache if no longer connected to server or server changed
if (mediaManager.checkResetServerItems()) { if (mediaManager.checkResetServerItems()) {
AbsLogger.info(tag, "onGetRoot: Reset Android Auto server items cache (${DeviceManager.serverConnectionConfigString})")
forceReloadingAndroidAuto = true forceReloadingAndroidAuto = true
} }
@@ -1135,7 +1119,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
parentMediaId: String, parentMediaId: String,
result: Result<MutableList<MediaBrowserCompat.MediaItem>> result: Result<MutableList<MediaBrowserCompat.MediaItem>>
) { ) {
Log.d(tag, "ON LOAD CHILDREN $parentMediaId") AbsLogger.info(tag, "onLoadChildren: parentMediaId: $parentMediaId (${DeviceManager.serverConnectionConfigString})")
result.detach() result.detach()
@@ -1146,7 +1130,6 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
} }
if (parentMediaId == DOWNLOADS_ROOT) { // Load downloads if (parentMediaId == DOWNLOADS_ROOT) { // Load downloads
val localBooks = DeviceManager.dbManager.getLocalLibraryItems("book") val localBooks = DeviceManager.dbManager.getLocalLibraryItems("book")
val localPodcasts = DeviceManager.dbManager.getLocalLibraryItems("podcast") val localPodcasts = DeviceManager.dbManager.getLocalLibraryItems("podcast")
val localBrowseItems: MutableList<MediaBrowserCompat.MediaItem> = mutableListOf() val localBrowseItems: MutableList<MediaBrowserCompat.MediaItem> = mutableListOf()
@@ -1243,8 +1226,10 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
Log.d(tag, "Trying to initialize browseTree.") Log.d(tag, "Trying to initialize browseTree.")
if (!this::browseTree.isInitialized || forceReloadingAndroidAuto) { if (!this::browseTree.isInitialized || forceReloadingAndroidAuto) {
forceReloadingAndroidAuto = false forceReloadingAndroidAuto = false
AbsLogger.info(tag, "onLoadChildren: Loading Android Auto items")
mediaManager.loadAndroidAutoItems { mediaManager.loadAndroidAutoItems {
Log.d(tag, "android auto loaded. Starting browseTree initialize") AbsLogger.info(tag, "onLoadChildren: Loaded Android Auto data, initializing browseTree")
browseTree = browseTree =
BrowseTree( BrowseTree(
this, this,
@@ -1260,15 +1245,21 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
MediaBrowserCompat.MediaItem.FLAG_BROWSABLE MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
) )
} }
Log.d(tag, "browseTree initialize and android auto loaded")
result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?) result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?)
firstLoadDone = true firstLoadDone = true
if (mediaManager.serverLibraries.isNotEmpty()) { if (mediaManager.serverLibraries.isNotEmpty()) {
Log.d(tag, "Starting personalization fetch") AbsLogger.info(tag, "onLoadChildren: Android Auto fetching personalized data for all libraries")
mediaManager.populatePersonalizedDataForAllLibraries { notifyChildrenChanged("/") } mediaManager.populatePersonalizedDataForAllLibraries {
AbsLogger.info(tag, "onLoadChildren: Android Auto loaded personalized data for all libraries")
notifyChildrenChanged("/")
}
Log.d(tag, "Initialize inprogress items") AbsLogger.info(tag, "onLoadChildren: Android Auto fetching in progress items")
mediaManager.initializeInProgressItems { notifyChildrenChanged("/") } mediaManager.initializeInProgressItems {
AbsLogger.info(tag, "onLoadChildren: Android Auto loaded in progress items")
notifyChildrenChanged("/")
}
} }
} }
} else { } else {
@@ -1288,7 +1279,8 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
MediaBrowserCompat.MediaItem.FLAG_BROWSABLE MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
) )
} }
Log.d(tag, "browseTree initialize and android auto loaded")
AbsLogger.info(tag, "onLoadChildren: Android auto data loaded")
result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?) result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?)
} }
} else if (parentMediaId == LIBRARIES_ROOT || parentMediaId == RECENTLY_ROOT) { } else if (parentMediaId == LIBRARIES_ROOT || parentMediaId == RECENTLY_ROOT) {
@@ -5,6 +5,7 @@ import android.hardware.SensorEvent
import android.hardware.SensorEventListener import android.hardware.SensorEventListener
import android.hardware.SensorManager import android.hardware.SensorManager
import com.audiobookshelf.app.device.DeviceManager import com.audiobookshelf.app.device.DeviceManager
import com.audiobookshelf.app.plugins.AbsLogger
import kotlin.math.sqrt import kotlin.math.sqrt
class ShakeDetector : SensorEventListener { class ShakeDetector : SensorEventListener {
@@ -46,6 +47,7 @@ class ShakeDetector : SensorEventListener {
if (mShakeTimestamp + SHAKE_COUNT_RESET_TIME_MS < now) { if (mShakeTimestamp + SHAKE_COUNT_RESET_TIME_MS < now) {
mShakeCount = 0 mShakeCount = 0
} }
AbsLogger.info("ShakeDetector", "Device shake above threshold ($gForce > $shakeThreshold)")
mShakeTimestamp = now mShakeTimestamp = now
mShakeCount++ mShakeCount++
mListener!!.onShake(mShakeCount) mListener!!.onShake(mShakeCount)
@@ -29,16 +29,16 @@ class AbsLogger : Plugin() {
onLogEmitter = { log:AbsLog -> onLogEmitter = { log:AbsLog ->
notifyListeners("onLog", JSObject(jacksonMapper.writeValueAsString(log))) notifyListeners("onLog", JSObject(jacksonMapper.writeValueAsString(log)))
} }
Log.i("AbsLogger", "Initialize AbsLogger plugin") info("AbsLogger", "load: AbsLogger plugin initialized")
} }
companion object { companion object {
lateinit var onLogEmitter:(log:AbsLog) -> Unit var onLogEmitter:((log:AbsLog) -> Unit)? = null
fun log(level:String, tag:String, message:String) { fun log(level:String, tag:String, message:String) {
val absLog = AbsLog(id = UUID.randomUUID().toString(), tag, level, message, timestamp = System.currentTimeMillis()) val absLog = AbsLog(id = UUID.randomUUID().toString(), tag, level, message, timestamp = System.currentTimeMillis())
DeviceManager.dbManager.saveLog(absLog) DeviceManager.dbManager.saveLog(absLog)
onLogEmitter(absLog) onLogEmitter?.let { it(absLog) }
} }
fun info(tag:String, message:String) { fun info(tag:String, message:String) {
Log.i("AbsLogger", message) Log.i("AbsLogger", message)
Binary file not shown.
+1 -1
View File
@@ -14,7 +14,7 @@ ext {
androidx_core_ktx_version = '1.16.0' androidx_core_ktx_version = '1.16.0'
androidx_media_version = '1.7.0' androidx_media_version = '1.7.0'
exoplayer_version = '2.18.7' exoplayer_version = '2.18.7'
glide_version = '4.11.0' glide_version = '4.16.0'
junit_version = '4.13.2' junit_version = '4.13.2'
kotlin_version = '2.1.0' kotlin_version = '2.1.0'
kotlin_coroutines_version = '1.10.1' kotlin_coroutines_version = '1.10.1'
+1 -4
View File
@@ -7,7 +7,7 @@
<a v-if="showBack" @click="back" class="rounded-full h-10 w-10 flex items-center justify-center mr-2 cursor-pointer"> <a v-if="showBack" @click="back" class="rounded-full h-10 w-10 flex items-center justify-center mr-2 cursor-pointer">
<span class="material-symbols text-3xl text-fg">arrow_back</span> <span class="material-symbols text-3xl text-fg">arrow_back</span>
</a> </a>
<div v-if="user && currentLibrary && socketConnected"> <div v-if="user && currentLibrary">
<div class="pl-1.5 pr-2.5 py-2 bg-bg bg-opacity-30 rounded-md flex items-center" @click="clickShowLibraryModal"> <div class="pl-1.5 pr-2.5 py-2 bg-bg bg-opacity-30 rounded-md flex items-center" @click="clickShowLibraryModal">
<ui-library-icon :icon="currentLibraryIcon" :size="4" font-size="base" /> <ui-library-icon :icon="currentLibraryIcon" :size="4" font-size="base" />
<p class="text-sm leading-4 ml-2 mt-0.5 max-w-24 truncate">{{ currentLibraryName }}</p> <p class="text-sm leading-4 ml-2 mt-0.5 max-w-24 truncate">{{ currentLibraryName }}</p>
@@ -56,9 +56,6 @@ export default {
this.$store.commit('setCastAvailable', val) this.$store.commit('setCastAvailable', val)
} }
}, },
socketConnected() {
return this.$store.state.socketConnected
},
currentLibrary() { currentLibrary() {
return this.$store.getters['libraries/getCurrentLibrary'] return this.$store.getters['libraries/getCurrentLibrary']
}, },
+2 -2
View File
@@ -77,12 +77,12 @@
</div> </div>
<!-- Series sequence --> <!-- Series sequence -->
<div v-if="seriesSequence && showSequence && !isSelectionMode" class="absolute rounded-lg bg-black bg-opacity-90 box-shadow-md z-10" :style="{ top: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem', padding: `${0.1 * sizeMultiplier}rem ${0.25 * sizeMultiplier}rem` }"> <div v-if="seriesSequence && showSequence && !isSelectionMode" class="absolute rounded-lg bg-black/90 text-white box-shadow-md z-10" :style="{ top: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem', padding: `${0.1 * sizeMultiplier}rem ${0.25 * sizeMultiplier}rem` }">
<p :style="{ fontSize: sizeMultiplier * 0.8 + 'rem' }">#{{ seriesSequence }}</p> <p :style="{ fontSize: sizeMultiplier * 0.8 + 'rem' }">#{{ seriesSequence }}</p>
</div> </div>
<!-- Podcast Episode # --> <!-- Podcast Episode # -->
<div v-if="recentEpisodeNumber !== null && !isSelectionMode" class="absolute rounded-lg bg-black bg-opacity-90 box-shadow-md z-10" :style="{ top: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem', padding: `${0.1 * sizeMultiplier}rem ${0.25 * sizeMultiplier}rem` }"> <div v-if="recentEpisodeNumber !== null && !isSelectionMode" class="absolute rounded-lg bg-black/90 box-shadow-md z-10" :style="{ top: 0.375 * sizeMultiplier + 'rem', right: 0.375 * sizeMultiplier + 'rem', padding: `${0.1 * sizeMultiplier}rem ${0.25 * sizeMultiplier}rem` }">
<p class="text-white" :style="{ fontSize: sizeMultiplier * 0.8 + 'rem' }"> <p class="text-white" :style="{ fontSize: sizeMultiplier * 0.8 + 'rem' }">
Episode<span v-if="recentEpisodeNumber"> #{{ recentEpisodeNumber }}</span> Episode<span v-if="recentEpisodeNumber"> #{{ recentEpisodeNumber }}</span>
</p> </p>
+4 -4
View File
@@ -732,12 +732,12 @@
ASSETCATALOG_COMPILER_APPICON_NAME = Icons; ASSETCATALOG_COMPILER_APPICON_NAME = Icons;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 36; CURRENT_PROJECT_VERSION = 37;
DEVELOPMENT_TEAM = 7UFJ7D8V6A; DEVELOPMENT_TEAM = 7UFJ7D8V6A;
INFOPLIST_FILE = App/Info.plist; INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0; IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 0.9.79; MARKETING_VERSION = 0.9.80;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = com.audiobookshelf.app.dev; PRODUCT_BUNDLE_IDENTIFIER = com.audiobookshelf.app.dev;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@@ -756,12 +756,12 @@
ASSETCATALOG_COMPILER_APPICON_NAME = Icons; ASSETCATALOG_COMPILER_APPICON_NAME = Icons;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 36; CURRENT_PROJECT_VERSION = 37;
DEVELOPMENT_TEAM = 7UFJ7D8V6A; DEVELOPMENT_TEAM = 7UFJ7D8V6A;
INFOPLIST_FILE = App/Info.plist; INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0; IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 0.9.79; MARKETING_VERSION = 0.9.80;
PRODUCT_BUNDLE_IDENTIFIER = com.audiobookshelf.app; PRODUCT_BUNDLE_IDENTIFIER = com.audiobookshelf.app;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "audiobookshelf-app", "name": "audiobookshelf-app",
"version": "0.9.79-beta", "version": "0.9.80-beta",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "audiobookshelf-app", "name": "audiobookshelf-app",
"version": "0.9.79-beta", "version": "0.9.80-beta",
"dependencies": { "dependencies": {
"@capacitor-community/keep-awake": "^7.0.0", "@capacitor-community/keep-awake": "^7.0.0",
"@capacitor-community/volume-buttons": "^7.0.0", "@capacitor-community/volume-buttons": "^7.0.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "audiobookshelf-app", "name": "audiobookshelf-app",
"version": "0.9.79-beta", "version": "0.9.80-beta",
"author": "advplyr", "author": "advplyr",
"scripts": { "scripts": {
"dev": "nuxt --hostname 0.0.0.0 --port 1337", "dev": "nuxt --hostname 0.0.0.0 --port 1337",
+17
View File
@@ -100,6 +100,13 @@
<p class="pl-4">{{ $strings.LabelDisableVibrateOnReset }}</p> <p class="pl-4">{{ $strings.LabelDisableVibrateOnReset }}</p>
<span class="material-symbols text-xl ml-2" @click.stop="showInfo('disableSleepTimerResetFeedback')">info</span> <span class="material-symbols text-xl ml-2" @click.stop="showInfo('disableSleepTimerResetFeedback')">info</span>
</div> </div>
<div class="flex items-center py-3">
<div class="w-10 flex justify-center" @click="toggleSleepTimerAlmostDoneChime">
<ui-toggle-switch v-model="settings.enableSleepTimerAlmostDoneChime" @input="saveSettings" />
</div>
<p class="pl-4">{{ $strings.LabelSleepTimerAlmostDoneChime }}</p>
<span class="material-symbols text-xl ml-2" @click.stop="showInfo('enableSleepTimerAlmostDoneChime')">info</span>
</div>
<div class="flex items-center py-3"> <div class="flex items-center py-3">
<div class="w-10 flex justify-center" @click="toggleAutoSleepTimer"> <div class="w-10 flex justify-center" @click="toggleAutoSleepTimer">
<ui-toggle-switch v-model="settings.autoSleepTimer" @input="saveSettings" /> <ui-toggle-switch v-model="settings.autoSleepTimer" @input="saveSettings" />
@@ -207,6 +214,7 @@ export default {
sleepTimerLength: 900000, // 15 minutes sleepTimerLength: 900000, // 15 minutes
disableSleepTimerFadeOut: false, disableSleepTimerFadeOut: false,
disableSleepTimerResetFeedback: false, disableSleepTimerResetFeedback: false,
enableSleepTimerAlmostDoneChime: false,
autoSleepTimerAutoRewind: false, autoSleepTimerAutoRewind: false,
autoSleepTimerAutoRewindTime: 300000, // 5 minutes autoSleepTimerAutoRewindTime: 300000, // 5 minutes
languageCode: 'en-us', languageCode: 'en-us',
@@ -234,6 +242,10 @@ export default {
name: this.$strings.LabelDisableVibrateOnReset, name: this.$strings.LabelDisableVibrateOnReset,
message: this.$strings.LabelDisableVibrateOnResetHelp message: this.$strings.LabelDisableVibrateOnResetHelp
}, },
enableSleepTimerAlmostDoneChime: {
name: this.$strings.LabelSleepTimerAlmostDoneChime,
message: this.$strings.LabelSleepTimerAlmostDoneChimeHelp
},
autoSleepTimerAutoRewind: { autoSleepTimerAutoRewind: {
name: this.$strings.LabelAutoSleepTimerAutoRewind, name: this.$strings.LabelAutoSleepTimerAutoRewind,
message: this.$strings.LabelAutoSleepTimerAutoRewindHelp message: this.$strings.LabelAutoSleepTimerAutoRewindHelp
@@ -549,6 +561,10 @@ export default {
this.settings.disableSleepTimerResetFeedback = !this.settings.disableSleepTimerResetFeedback this.settings.disableSleepTimerResetFeedback = !this.settings.disableSleepTimerResetFeedback
this.saveSettings() this.saveSettings()
}, },
toggleSleepTimerAlmostDoneChime() {
this.settings.enableSleepTimerAlmostDoneChime = !this.settings.enableSleepTimerAlmostDoneChime
this.saveSettings()
},
toggleDisableAutoRewind() { toggleDisableAutoRewind() {
this.settings.disableAutoRewind = !this.settings.disableAutoRewind this.settings.disableAutoRewind = !this.settings.disableAutoRewind
this.saveSettings() this.saveSettings()
@@ -620,6 +636,7 @@ export default {
this.settings.sleepTimerLength = !isNaN(deviceSettings.sleepTimerLength) ? deviceSettings.sleepTimerLength : 900000 // 15 minutes this.settings.sleepTimerLength = !isNaN(deviceSettings.sleepTimerLength) ? deviceSettings.sleepTimerLength : 900000 // 15 minutes
this.settings.disableSleepTimerFadeOut = !!deviceSettings.disableSleepTimerFadeOut this.settings.disableSleepTimerFadeOut = !!deviceSettings.disableSleepTimerFadeOut
this.settings.disableSleepTimerResetFeedback = !!deviceSettings.disableSleepTimerResetFeedback this.settings.disableSleepTimerResetFeedback = !!deviceSettings.disableSleepTimerResetFeedback
this.settings.enableSleepTimerAlmostDoneChime = !!deviceSettings.enableSleepTimerAlmostDoneChime
this.settings.autoSleepTimerAutoRewind = !!deviceSettings.autoSleepTimerAutoRewind this.settings.autoSleepTimerAutoRewind = !!deviceSettings.autoSleepTimerAutoRewind
this.settings.autoSleepTimerAutoRewindTime = !isNaN(deviceSettings.autoSleepTimerAutoRewindTime) ? deviceSettings.autoSleepTimerAutoRewindTime : 300000 // 5 minutes this.settings.autoSleepTimerAutoRewindTime = !isNaN(deviceSettings.autoSleepTimerAutoRewindTime) ? deviceSettings.autoSleepTimerAutoRewindTime : 300000 // 5 minutes
+126 -10
View File
@@ -20,24 +20,26 @@
"ButtonDisableAutoTimer": "تعطيل المؤقت التلقائي", "ButtonDisableAutoTimer": "تعطيل المؤقت التلقائي",
"ButtonDisconnect": "فصل", "ButtonDisconnect": "فصل",
"ButtonGoToWebClient": "اذهب إلى عميل الويب", "ButtonGoToWebClient": "اذهب إلى عميل الويب",
"ButtonHistory": "تاريخ", "ButtonHistory": "السجل التاريخي",
"ButtonHome": "الرئيسية", "ButtonHome": "الرئيسية",
"ButtonIssues": "مشاكل", "ButtonIssues": "مشاكل",
"ButtonLatest": "أحدث", "ButtonLatest": "الأحدث",
"ButtonLibrary": "المكتبة", "ButtonLibrary": "المكتبة",
"ButtonLocalMedia": "ملفات الوسائط المحلية", "ButtonLocalMedia": "الوسائط المحلية",
"ButtonLogs": "سجلات",
"ButtonManageLocalFiles": "إدارة الملفات المحلية", "ButtonManageLocalFiles": "إدارة الملفات المحلية",
"ButtonNewFolder": "مجلد جديد", "ButtonNewFolder": "مجلد جديد",
"ButtonNextEpisode": "الحلقة التالية", "ButtonNextEpisode": "الحلقة التالية",
"ButtonOk": "موافق",
"ButtonOpenFeed": "فتح التغذية", "ButtonOpenFeed": "فتح التغذية",
"ButtonOverride": "تجاوز", "ButtonOverride": "تجاوز",
"ButtonPause": "تَوَقَّف", "ButtonPause": "إيقاف مؤقت",
"ButtonPlay": "تشغيل", "ButtonPlay": "تشغيل",
"ButtonPlayEpisode": "شغل الحلقة", "ButtonPlayEpisode": "تشغيل الحلقة",
"ButtonPlaylists": "قوائم التشغيل", "ButtonPlaylists": "قوائم التشغيل",
"ButtonRead": "اقرأ", "ButtonRead": "اقرأ",
"ButtonReadLess": "قلص", "ButtonReadLess": "اقرأ أقل",
"ButtonReadMore": "المزيد", "ButtonReadMore": "اقرأ أكثر",
"ButtonRemove": "إزالة", "ButtonRemove": "إزالة",
"ButtonRemoveFromServer": "إزالة من الخادم", "ButtonRemoveFromServer": "إزالة من الخادم",
"ButtonSave": "حفظ", "ButtonSave": "حفظ",
@@ -47,21 +49,135 @@
"ButtonSeries": "سلسلة", "ButtonSeries": "سلسلة",
"ButtonSetTimer": "اضبط مؤقت", "ButtonSetTimer": "اضبط مؤقت",
"ButtonStream": "بث", "ButtonStream": "بث",
"ButtonSubmit": "تقديم", "ButtonSubmit": "إرسال",
"ButtonSwitchServerUser": "تبديل الخادم/المستخدم", "ButtonSwitchServerUser": "تبديل الخادم/المستخدم",
"ButtonUserStats": "إحصائيات المستخدم", "ButtonUserStats": "إحصائيات المستخدم",
"ButtonYes": "نعم", "ButtonYes": "نعم",
"HeaderAccount": "الحساب", "HeaderAccount": "الحساب",
"HeaderAdvanced": "متقدم", "HeaderAdvanced": "متقدم",
"HeaderAudioTracks": "المسارات الصوتية", "HeaderAndroidAutoSettings": "إعدادات أندرويد للسيارة",
"HeaderAudioTracks": "المقاطع الصوتية",
"HeaderChapters": "الفصول", "HeaderChapters": "الفصول",
"HeaderCollection": "مجموعة", "HeaderCollection": "مجموعة",
"HeaderCollectionItems": "عناصر المجموعة", "HeaderCollectionItems": "عناصر المجموعة",
"HeaderConnectionStatus": "حالة الاتصال",
"HeaderDataSettings": "إعدادات البيانات", "HeaderDataSettings": "إعدادات البيانات",
"HeaderDetails": "التفاصيل", "HeaderDetails": "التفاصيل",
"HeaderDownloads": "التنزيلات",
"HeaderEbookFiles": "ملفات الكتب الإلكترونية", "HeaderEbookFiles": "ملفات الكتب الإلكترونية",
"HeaderEpisodes": "الحلقات", "HeaderEpisodes": "الحلقات",
"HeaderEreaderSettings": "إعدادات القارئ الإلكتروني", "HeaderEreaderSettings": "إعدادات القارئ الإلكتروني",
"HeaderLatestEpisodes": "أحدث الحلقات", "HeaderLatestEpisodes": "أحدث الحلقات",
"HeaderLibraries": "المكتبات" "HeaderLibraries": "المكتبات",
"HeaderLocalFolders": "المجلدات المحلية",
"HeaderLocalLibraryItems": "عناصر المكتبة المحلية",
"HeaderNewPlaylist": "قائمة تشغيل جديدة",
"HeaderOpenRSSFeed": "فتح تغذية RSS",
"HeaderPlaybackSettings": "إعدادات التشغيل",
"HeaderPlaylist": "قائمة تشغيل",
"HeaderPlaylistItems": "عناصر قائمة التشغيل",
"HeaderProgressSyncFailed": "فشلت المزامنة",
"HeaderRSSFeed": "تغذية RSS",
"HeaderRSSFeedGeneral": "تفاصيل RSS",
"HeaderRSSFeedIsOpen": "مغذي RSS مفتوح",
"HeaderSelectDownloadLocation": "حدد موقع التنزيل",
"HeaderSettings": "إعدادات",
"HeaderSleepTimer": "مؤقت النوم",
"HeaderSleepTimerSettings": "إعدادات مؤقت النوم",
"HeaderStatsMinutesListeningChart": "الدقائق المسموعة (آخر 7 أيام)",
"HeaderStatsRecentSessions": "الجلسات الأخيرة",
"HeaderTableOfContents": "جدول المحتويات",
"HeaderUserInterfaceSettings": "إعدادات واجهة المستخدم",
"HeaderYourStats": "إحصائياتك",
"LabelAddToPlaylist": "أضف إلى قائمة التشغيل",
"LabelAddedAt": "أضيفت على",
"LabelAddedDate": "تمت الإضافة",
"LabelAll": "الكل",
"LabelAllowSeekingOnMediaControls": "السماح بالبحث عن الموضع في التحكم بإشعارات الوسائط",
"LabelAlways": "دائماً",
"LabelAndroidAutoBrowseLimitForGrouping": "حد السحب الأبجدي",
"LabelAndroidAutoBrowseLimitForGroupingHelp": "لا تستخدم السحب الأبجدي عندما يكون عدد العناصر المراد عرضها أقل من هذا العدد",
"LabelAndroidAutoBrowseSeriesSequenceOrder": "ترتيب كتب السلسلة",
"LabelAskConfirmation": "اطلب التأكيد",
"LabelAuthor": "المؤلف",
"LabelAuthorFirstLast": "المؤلف (الاسم الأول الأخير)",
"LabelAuthorLastFirst": "المؤلف (الاسم الأخير، الأول)",
"LabelAuthors": "المؤلفون",
"LabelAutoDownloadEpisodes": "تنزيل الحلقات تلقائيًا",
"LabelAutoRewindTime": "إعادة الوقت تلقائياَ",
"LabelAutoSleepTimer": "مؤقت النوم التلقائي",
"LabelAutoSleepTimerAutoRewind": "مؤقتا النوم والإرجاع التلقائيين",
"LabelAutoSleepTimerAutoRewindHelp": "عندما ينتهي مؤقت النوم التلقائي، فإن تشغيل العنصر مرة أخرى سيؤدي إلى الإرجاع التلقائي.",
"LabelAutoSleepTimerHelp": "عند تشغيل الوسائط بين أوقات البداية والنهاية المحددة، سيبدأ مؤقت النوم تلقائيًا.",
"LabelBooks": "الكتب",
"LabelChapterTrack": "مسار الفصل",
"LabelChapters": "الفصول",
"LabelClosePlayer": "إغلاق المشغل",
"LabelCollapseSeries": "إخفاء المسلسلات",
"LabelComplete": "مكتمل",
"LabelContinueBooks": "استمرار الكتب",
"LabelContinueEpisodes": "استمرار الحلقات",
"LabelContinueListening": "استمرار الاستماع",
"LabelContinueReading": "استمرار القراءة",
"LabelContinueSeries": "استمرار المسلسلات",
"LabelCustomTime": "الوقت المخصص",
"LabelDescription": "الوصف",
"LabelDisableAudioFadeOut": "تعطيل التلاشي الصوتي",
"LabelDisableAudioFadeOutHelp": "سيبدأ مستوى الصوت بالانخفاض عندما يتبقى أقل من دقيقة واحدة على مؤقت النوم. فعّل هذا الإعداد لعدم التلاشي.",
"LabelDisableAutoRewind": "تعطيل الإعادة التلقائية",
"LabelDisableShakeToReset": "تعطيل الرج لإعادة الضبط",
"LabelDisableShakeToResetHelp": "سيؤدي هزّ جهازك أثناء تشغيل المؤقت أو خلال دقيقتين من انتهاءه إلى إعادة ضبط مؤقت النوم. فعّل هذا الإعداد لتعطيل الرج لإعادة الضبط.",
"LabelDisableVibrateOnReset": "تعطيل الاهتزاز عند إعادة الضبط",
"LabelDisableVibrateOnResetHelp": "عند إعادة ضبط مؤقت النوم، سيهتز جهازك. فعّل هذا الإعداد لعدم الاهتزاز عند إعادة ضبط مؤقت النوم.",
"LabelDiscover": "استكشف",
"LabelDownload": "تنزيل",
"LabelDownloadUsingCellular": "التنزيل عبر بيانات الهاتف",
"LabelDownloaded": "تم تنزيلها",
"LabelDuration": "المدة",
"LabelEbook": "الكتاب الإلكتروني",
"LabelEbooks": "الكتب الإلكترونية",
"LabelEnable": "تمكين",
"LabelEnableMp3IndexSeeking": "تمكين البحث عن فهرس mp3",
"LabelEnableMp3IndexSeekingHelp": "يجب تفعيل هذا الإعداد فقط إذا كانت ملفات MP3 لديك لا تعمل بشكل صحيح. غالبًا ما يكون البحث غير الدقيق ناتجًا عن ملفات MP3 ذات معدل البت المتغير (VBR). سيفرض هذا الإعداد البحث عن الفهرس، حيث يتم إنشاء تعيين زمني للبايت أثناء قراءة الملف. في بعض الحالات، مع ملفات MP3 كبيرة الحجم، قد يكون هناك تأخير عند البحث قرب نهاية الملف.",
"LabelEnd": "انهاء",
"LabelEndOfChapter": "نهاية الفصل",
"LabelEndTime": "وقت النهاية",
"LabelEpisode": "الحلقة",
"LabelFeedURL": "عنوان التغذية",
"LabelFile": "الملف",
"LabelFileBirthtime": "وقت انشاء الملف",
"LabelFileModified": "تم تعديل الملف",
"LabelFilename": "اسم الملف",
"LabelFinished": "المنجزة",
"LabelFolder": "المجلد",
"LabelFontBoldness": "تعريض الخط",
"LabelFontScale": "نطاق الخط",
"LabelGenre": "التصنيف",
"LabelGenres": "التصانيف",
"LabelHapticFeedback": "ردود الفعل اللمسية",
"LabelHasEbook": "يحتوي كتاب إلكتروني",
"LabelHasSupplementaryEbook": "يحتوي كتاب إلكتروني تكميلي",
"LabelHeavy": "ثقيل",
"LabelHigh": "مرتفع",
"LabelHost": "المضيف",
"LabelInProgress": "تحت التنفيذ",
"LabelIncomplete": "غير مكتمل",
"LabelInternalAppStorage": "وحدة تخزين التطبيق الداخلي",
"LabelJumpBackwardsTime": "وقت القفز للخلف",
"LabelJumpForwardsTime": "وقت القفز للأمام",
"LabelKeepScreenAwake": "إبقاء الشاشة يقظة",
"LabelLanguage": "اللغة",
"LabelLayout": "التنسيق",
"LabelLayoutAuto": "تلقائي",
"LabelLayoutSinglePage": "صفحة واحدة",
"LabelLight": "خفيف/فاتح",
"LabelLineSpacing": "تباعد الأسطر",
"LabelListenAgain": "الاستماع مجدداً",
"LabelLocalBooks": "الكتب المحلية",
"LabelLocalPodcasts": "المدونات الصوتية المحلية",
"LabelLockOrientation": "قفل الاتجاه",
"LabelLockPlayer": "قفل المشغل",
"LabelLow": "منخفض",
"LabelMediaType": "نوع الوسائط",
"LabelMedium": "متوسط"
} }
+20 -3
View File
@@ -26,6 +26,7 @@
"ButtonLatest": "Nejnovější", "ButtonLatest": "Nejnovější",
"ButtonLibrary": "Knihovna", "ButtonLibrary": "Knihovna",
"ButtonLocalMedia": "Místní média", "ButtonLocalMedia": "Místní média",
"ButtonLogs": "Záznamy",
"ButtonManageLocalFiles": "Spravovat místní soubory", "ButtonManageLocalFiles": "Spravovat místní soubory",
"ButtonNewFolder": "Nová složka", "ButtonNewFolder": "Nová složka",
"ButtonNextEpisode": "Další epizoda", "ButtonNextEpisode": "Další epizoda",
@@ -66,7 +67,7 @@
"HeaderEbookFiles": "Soubory e-knih", "HeaderEbookFiles": "Soubory e-knih",
"HeaderEpisodes": "Epizody", "HeaderEpisodes": "Epizody",
"HeaderEreaderSettings": "Nastavení čtečky e-knih", "HeaderEreaderSettings": "Nastavení čtečky e-knih",
"HeaderLatestEpisodes": "Nejnovější epizody", "HeaderLatestEpisodes": "Nové epizody",
"HeaderLibraries": "Knihovny", "HeaderLibraries": "Knihovny",
"HeaderLocalFolders": "Místní složky", "HeaderLocalFolders": "Místní složky",
"HeaderLocalLibraryItems": "Místní položky knihovny", "HeaderLocalLibraryItems": "Místní položky knihovny",
@@ -75,7 +76,7 @@
"HeaderPlaybackSettings": "Nastavení přehrávání", "HeaderPlaybackSettings": "Nastavení přehrávání",
"HeaderPlaylist": "Seznam skladeb", "HeaderPlaylist": "Seznam skladeb",
"HeaderPlaylistItems": "Položky seznamu přehrávání", "HeaderPlaylistItems": "Položky seznamu přehrávání",
"HeaderProgressSyncFailed": "Chyba při synchronizaci", "HeaderProgressSyncFailed": "Chyba při synchronizaci pokroku",
"HeaderRSSFeed": "RSS kanál", "HeaderRSSFeed": "RSS kanál",
"HeaderRSSFeedGeneral": "Detaily RSS", "HeaderRSSFeedGeneral": "Detaily RSS",
"HeaderRSSFeedIsOpen": "Kanál RSS je otevřen", "HeaderRSSFeedIsOpen": "Kanál RSS je otevřen",
@@ -94,6 +95,9 @@
"LabelAll": "Vše", "LabelAll": "Vše",
"LabelAllowSeekingOnMediaControls": "Povolit vyhledávání polohy v ovládacích prvcích oznámení médií", "LabelAllowSeekingOnMediaControls": "Povolit vyhledávání polohy v ovládacích prvcích oznámení médií",
"LabelAlways": "Vždy", "LabelAlways": "Vždy",
"LabelAndroidAutoBrowseLimitForGrouping": "Omezení abecedního rozbalovacího seznamu",
"LabelAndroidAutoBrowseLimitForGroupingHelp": "Nepoužívejte abecední řazení, pokud je k zobrazení méně než tento počet položek",
"LabelAndroidAutoBrowseSeriesSequenceOrder": "Řazení sérií knih",
"LabelAskConfirmation": "Požádat o potvrzení", "LabelAskConfirmation": "Požádat o potvrzení",
"LabelAuthor": "Autor", "LabelAuthor": "Autor",
"LabelAuthorFirstLast": "Autor (jméno a příjmení)", "LabelAuthorFirstLast": "Autor (jméno a příjmení)",
@@ -134,7 +138,7 @@
"LabelEbooks": "E-knihy", "LabelEbooks": "E-knihy",
"LabelEnable": "Povolit", "LabelEnable": "Povolit",
"LabelEnableMp3IndexSeeking": "Povolit indexové vyhledávání mp3", "LabelEnableMp3IndexSeeking": "Povolit indexové vyhledávání mp3",
"LabelEnableMp3IndexSeekingHelp": "Toto nastavení by mělo být povoleno pouze v případě, že soubory MP3 nejsou správně vyhledávány. Nepřesné vyhledávání je s největší pravděpodobností způsobeno soubory MP3 s proměnlivým datovým tokem (VBR). Toto nastavení vynutí indexové vyhledávání, při kterém se při čtení souboru vytváří mapování času na bajty. V některých případech u velkých souborů MP3 dochází ke zpoždění při vyhledávání ke konci souboru.", "LabelEnableMp3IndexSeekingHelp": "Toto nastavení by mělo být povoleno pouze v případě, že v souborech MP3 nelze přeskakovat. Nepřesné přeskakování je s největší pravděpodobností způsobeno soubory MP3 s proměnlivým datovým tokem (VBR). Toto nastavení vynutí indexové vyhledávání, při kterém se při čtení souboru vytváří mapování času na bajty. V některých případech u velkých souborů MP3 dochází ke zpoždění při vyhledávání ke konci souboru.",
"LabelEnd": "Konec", "LabelEnd": "Konec",
"LabelEndOfChapter": "Konec kapitoly", "LabelEndOfChapter": "Konec kapitoly",
"LabelEndTime": "Do", "LabelEndTime": "Do",
@@ -161,6 +165,7 @@
"LabelInternalAppStorage": "Interní úložiště aplikace", "LabelInternalAppStorage": "Interní úložiště aplikace",
"LabelJumpBackwardsTime": "Délka skoku zpět v čase", "LabelJumpBackwardsTime": "Délka skoku zpět v čase",
"LabelJumpForwardsTime": "Délka skoku vpřed v čase", "LabelJumpForwardsTime": "Délka skoku vpřed v čase",
"LabelKeepScreenAwake": "Nezhasínat obrazovku",
"LabelLanguage": "Jazyk", "LabelLanguage": "Jazyk",
"LabelLayout": "Rozvržení", "LabelLayout": "Rozvržení",
"LabelLayoutAuto": "Automatické", "LabelLayoutAuto": "Automatické",
@@ -175,6 +180,7 @@
"LabelLow": "Nízké", "LabelLow": "Nízké",
"LabelMediaType": "Typ média", "LabelMediaType": "Typ média",
"LabelMedium": "Střední", "LabelMedium": "Střední",
"LabelMissing": "Chybějící",
"LabelMore": "Více", "LabelMore": "Více",
"LabelMoreInfo": "Více informací", "LabelMoreInfo": "Více informací",
"LabelName": "Jméno", "LabelName": "Jméno",
@@ -192,6 +198,8 @@
"LabelNotFinished": "Nedokončeno", "LabelNotFinished": "Nedokončeno",
"LabelNotStarted": "Nezahájeno", "LabelNotStarted": "Nezahájeno",
"LabelNumEpisodes": "{0} epizod", "LabelNumEpisodes": "{0} epizod",
"LabelNumEpisodesIncomplete": "{0} epizod, {1} nekompletní",
"LabelNumberOfEpisodes": "Počet epizod",
"LabelOff": "Vypnout", "LabelOff": "Vypnout",
"LabelOn": "Zapnuto", "LabelOn": "Zapnuto",
"LabelPassword": "Heslo", "LabelPassword": "Heslo",
@@ -220,6 +228,8 @@
"LabelScaleElapsedTimeBySpeed": "Škálovat uplynulý čas podle rychlosti", "LabelScaleElapsedTimeBySpeed": "Škálovat uplynulý čas podle rychlosti",
"LabelSeason": "Sezóna", "LabelSeason": "Sezóna",
"LabelSelectADevice": "Vyberte zařízení", "LabelSelectADevice": "Vyberte zařízení",
"LabelSequenceAscending": "Řadit vzestupně",
"LabelSequenceDescending": "Řadit sestupně",
"LabelSeries": "Série", "LabelSeries": "Série",
"LabelServerAddress": "Adresa serveru", "LabelServerAddress": "Adresa serveru",
"LabelSetEbookAsPrimary": "Nastavit jako primární", "LabelSetEbookAsPrimary": "Nastavit jako primární",
@@ -243,6 +253,7 @@
"LabelTag": "Štítek", "LabelTag": "Štítek",
"LabelTags": "Štítky", "LabelTags": "Štítky",
"LabelTheme": "Téma", "LabelTheme": "Téma",
"LabelThemeBlack": "Černé",
"LabelThemeDark": "Tmavé", "LabelThemeDark": "Tmavé",
"LabelThemeLight": "Světlé", "LabelThemeLight": "Světlé",
"LabelTimeRemaining": "{0} zbývá", "LabelTimeRemaining": "{0} zbývá",
@@ -269,6 +280,7 @@
"MessageBookshelfEmpty": "Knihovna je prázdná", "MessageBookshelfEmpty": "Knihovna je prázdná",
"MessageConfirmDeleteLocalEpisode": "Odebrat místní epizodu „{0}“ ze zařízení? Soubor na serveru zůstane nezměněný.", "MessageConfirmDeleteLocalEpisode": "Odebrat místní epizodu „{0}“ ze zařízení? Soubor na serveru zůstane nezměněný.",
"MessageConfirmDeleteLocalFiles": "Odebrat místní soubory této položky ze zařízení? Soubory na serveru a váš pokrok nebudou ovlivněny.", "MessageConfirmDeleteLocalFiles": "Odebrat místní soubory této položky ze zařízení? Soubory na serveru a váš pokrok nebudou ovlivněny.",
"MessageConfirmDisableAutoTimer": "Určitě chcete vypnout automatický časovač pro zbytek dnešního dne? Časovač bude opět aktivován po uběhnutí doby automatického spánku nebo po restartování aplikace.",
"MessageConfirmDiscardProgress": "Opravdu chcete zahodit svůj pokrok?", "MessageConfirmDiscardProgress": "Opravdu chcete zahodit svůj pokrok?",
"MessageConfirmDownloadUsingCellular": "Chystáte se stahovat přes mobilní data. Toto může zahrnovat poplatky za mobilní data. Chcete pokračovat?", "MessageConfirmDownloadUsingCellular": "Chystáte se stahovat přes mobilní data. Toto může zahrnovat poplatky za mobilní data. Chcete pokračovat?",
"MessageConfirmMarkAsFinished": "Opravdu chcete tuto položku označit jako dokončenou?", "MessageConfirmMarkAsFinished": "Opravdu chcete tuto položku označit jako dokončenou?",
@@ -283,8 +295,10 @@
"MessageFetching": "Načítání...", "MessageFetching": "Načítání...",
"MessageFollowTheProjectOnGithub": "Sledujte projekt na GitHubu", "MessageFollowTheProjectOnGithub": "Sledujte projekt na GitHubu",
"MessageItemDownloadCompleteFailedToCreate": "Stahování položky bylo dokončeno, ale nepodařilo se vytvořit položku v knihovně", "MessageItemDownloadCompleteFailedToCreate": "Stahování položky bylo dokončeno, ale nepodařilo se vytvořit položku v knihovně",
"MessageItemMissing": "Položka chybí a musí být opravena na serveru. Typicky je položka označena jako chybějící, protože cesty k souborům nejsou přístupné.",
"MessageLoading": "Načítá se...", "MessageLoading": "Načítá se...",
"MessageLoadingServerData": "Načítání dat ze serveru...", "MessageLoadingServerData": "Načítání dat ze serveru...",
"MessageLocalFolderDescription": "„Interní úložiště aplikace“ je přístupné pouze této aplikaci. Tato aplikace podporuje pouze média stažená přímo prostřednictvím aplikace. Složky sdíleného úložiště lze použít k tomu, aby ostatní aplikace měly přístup k médiím staženým touto aplikací.",
"MessageMarkAsFinished": "Označit jako dokončené", "MessageMarkAsFinished": "Označit jako dokončené",
"MessageMediaLinkedToADifferentServer": "Média jsou propojena se serverem Audiobookshelf na jiné adrese ({0}). Pokrok bude synchronizován, když budete připojeni k této adrese serveru.", "MessageMediaLinkedToADifferentServer": "Média jsou propojena se serverem Audiobookshelf na jiné adrese ({0}). Pokrok bude synchronizován, když budete připojeni k této adrese serveru.",
"MessageMediaLinkedToADifferentUser": "Média jsou propojena se serverem, ale byla stažena jiným uživatelem. Pokrok bude synchronizován pouze s uživatelem, který je stáhl.", "MessageMediaLinkedToADifferentUser": "Média jsou propojena se serverem, ale byla stažena jiným uživatelem. Pokrok bude synchronizován pouze s uživatelem, který je stáhl.",
@@ -303,7 +317,10 @@
"MessageNoSeries": "Žádné série", "MessageNoSeries": "Žádné série",
"MessageNoUpdatesWereNecessary": "Nebyly nutné žádné aktualizace", "MessageNoUpdatesWereNecessary": "Nebyly nutné žádné aktualizace",
"MessageNoUserPlaylists": "Nemáte žádné seznamy skladeb", "MessageNoUserPlaylists": "Nemáte žádné seznamy skladeb",
"MessageOldServerConnectionWarning": "Konfigurace připojení k serveru používá staré ID uživatele. Odstraňte a znovu přidejte toto připojení k serveru.",
"MessageOldServerConnectionWarningHelp": "Připojení k tomuto serveru jste původně nastavili před migrací databáze ve verzi 2.3.0 vydané v červnu 2023. Budoucí aktualizace serveru odstraní možnost přihlášení pomocí tohoto starého připojení. Odstraňte prosím stávající připojení k serveru a připojte se znovu (pomocí stejné adresy serveru a přihlašovacích údajů). Pokud máte v tomto zařízení stažená média, bude nutné je pro synchronizaci se serverem stáhnout znovu.",
"MessagePodcastSearchField": "Zadejte hledaný pojem pro RSS feed URL", "MessagePodcastSearchField": "Zadejte hledaný pojem pro RSS feed URL",
"MessageProgressSyncFailed": "Poslední pokus o nahlášení průběhu poslechu na server se nezdařil. Během přehrávání médií se budou požadavky na synchronizaci pokroku nadále pokoušet každých 15 až 60 sekund.",
"MessageReportBugsAndContribute": "Nahlašte chyby, vyžádejte si funkce a přispěte na", "MessageReportBugsAndContribute": "Nahlašte chyby, vyžádejte si funkce a přispěte na",
"MessageSeriesAlreadyDownloaded": "Všechny knihy v této sérii jste již stáhli.", "MessageSeriesAlreadyDownloaded": "Všechny knihy v této sérii jste již stáhli.",
"MessageSeriesDownloadConfirm": "Stáhnout chybějící {0} knihu(y) s {1} souborem(y), celkem {2} do složky {3}?", "MessageSeriesDownloadConfirm": "Stáhnout chybějící {0} knihu(y) s {1} souborem(y), celkem {2} do složky {3}?",
+1
View File
@@ -26,6 +26,7 @@
"ButtonLatest": "Neueste", "ButtonLatest": "Neueste",
"ButtonLibrary": "Bibliothek", "ButtonLibrary": "Bibliothek",
"ButtonLocalMedia": "Lokale Medien", "ButtonLocalMedia": "Lokale Medien",
"ButtonLogs": "Protokolle",
"ButtonManageLocalFiles": "Verwalte lokale Dateien", "ButtonManageLocalFiles": "Verwalte lokale Dateien",
"ButtonNewFolder": "Neuer Ordner", "ButtonNewFolder": "Neuer Ordner",
"ButtonNextEpisode": "Nächste Episode", "ButtonNextEpisode": "Nächste Episode",
+2
View File
@@ -241,6 +241,8 @@
"LabelShowAll": "Show All", "LabelShowAll": "Show All",
"LabelSize": "Size", "LabelSize": "Size",
"LabelSleepTimer": "Sleep timer", "LabelSleepTimer": "Sleep timer",
"LabelSleepTimerAlmostDoneChime": "Play a chime when almost finished",
"LabelSleepTimerAlmostDoneChimeHelp": "Play a chime when the sleep timer has 30 seconds remaining",
"LabelStart": "Start", "LabelStart": "Start",
"LabelStartTime": "Start time", "LabelStartTime": "Start time",
"LabelStatsBestDay": "Best Day", "LabelStatsBestDay": "Best Day",
+1
View File
@@ -26,6 +26,7 @@
"ButtonLatest": "Останні", "ButtonLatest": "Останні",
"ButtonLibrary": "Бібліотека", "ButtonLibrary": "Бібліотека",
"ButtonLocalMedia": "Локальні файли", "ButtonLocalMedia": "Локальні файли",
"ButtonLogs": "Журнали",
"ButtonManageLocalFiles": "Керування локальними файлами", "ButtonManageLocalFiles": "Керування локальними файлами",
"ButtonNewFolder": "Нова тека", "ButtonNewFolder": "Нова тека",
"ButtonNextEpisode": "Наступний епізод", "ButtonNextEpisode": "Наступний епізод",