mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-08-28 13:58:23 +02:00
Add support for fading out on sleep timer end
Playback will start to fadeout during last 60 seconds of the sleep timer. Once faded out, playback will be paused, volume reset, and playback seeked to start of fadeout.
This commit is contained in:
parent
769ce0ade9
commit
33c738873f
6 changed files with 75 additions and 5 deletions
|
@ -14,7 +14,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
// Override point for customization after application launch.
|
// Override point for customization after application launch.
|
||||||
|
|
||||||
let configuration = Realm.Configuration(
|
let configuration = Realm.Configuration(
|
||||||
schemaVersion: 18,
|
schemaVersion: 19,
|
||||||
migrationBlock: { [weak self] migration, oldSchemaVersion in
|
migrationBlock: { [weak self] migration, oldSchemaVersion in
|
||||||
if (oldSchemaVersion < 1) {
|
if (oldSchemaVersion < 1) {
|
||||||
self?.logger.log("Realm schema version was \(oldSchemaVersion)")
|
self?.logger.log("Realm schema version was \(oldSchemaVersion)")
|
||||||
|
@ -61,6 +61,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
newObject?["streamingUsingCellular"] = "ALWAYS"
|
newObject?["streamingUsingCellular"] = "ALWAYS"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (oldSchemaVersion < 18) {
|
||||||
|
self?.logger.log("Realm schema version was \(oldSchemaVersion)... Adding disableSleepTimerFadeOut settings")
|
||||||
|
migration.enumerateObjects(ofType: PlayerSettings.className()) { oldObject, newObject in
|
||||||
|
newObject?["disableSleepTimerFadeOut"] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -246,6 +246,7 @@ public class AbsDatabase: CAPPlugin {
|
||||||
let languageCode = call.getString("languageCode") ?? "en-us"
|
let languageCode = call.getString("languageCode") ?? "en-us"
|
||||||
let downloadUsingCellular = call.getString("downloadUsingCellular") ?? "ALWAYS"
|
let downloadUsingCellular = call.getString("downloadUsingCellular") ?? "ALWAYS"
|
||||||
let streamingUsingCellular = call.getString("streamingUsingCellular") ?? "ALWAYS"
|
let streamingUsingCellular = call.getString("streamingUsingCellular") ?? "ALWAYS"
|
||||||
|
let disableSleepTimerFadeOut = call.getBool("disableSleepTimerFadeOut") ?? false
|
||||||
let settings = DeviceSettings()
|
let settings = DeviceSettings()
|
||||||
settings.disableAutoRewind = disableAutoRewind
|
settings.disableAutoRewind = disableAutoRewind
|
||||||
settings.enableAltView = enableAltView
|
settings.enableAltView = enableAltView
|
||||||
|
@ -257,6 +258,7 @@ public class AbsDatabase: CAPPlugin {
|
||||||
settings.languageCode = languageCode
|
settings.languageCode = languageCode
|
||||||
settings.downloadUsingCellular = downloadUsingCellular
|
settings.downloadUsingCellular = downloadUsingCellular
|
||||||
settings.streamingUsingCellular = streamingUsingCellular
|
settings.streamingUsingCellular = streamingUsingCellular
|
||||||
|
settings.disableSleepTimerFadeOut = disableSleepTimerFadeOut
|
||||||
|
|
||||||
Database.shared.setDeviceSettings(deviceSettings: settings)
|
Database.shared.setDeviceSettings(deviceSettings: settings)
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ class DeviceSettings: Object {
|
||||||
@Persisted var languageCode: String = "en-us"
|
@Persisted var languageCode: String = "en-us"
|
||||||
@Persisted var downloadUsingCellular: String = "ALWAYS"
|
@Persisted var downloadUsingCellular: String = "ALWAYS"
|
||||||
@Persisted var streamingUsingCellular: String = "ALWAYS"
|
@Persisted var streamingUsingCellular: String = "ALWAYS"
|
||||||
|
@Persisted var disableSleepTimerFadeOut: Bool = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDefaultDeviceSettings() -> DeviceSettings {
|
func getDefaultDeviceSettings() -> DeviceSettings {
|
||||||
|
@ -36,6 +37,7 @@ func deviceSettingsToJSON(settings: DeviceSettings) -> Dictionary<String, Any> {
|
||||||
"hapticFeedback": settings.hapticFeedback,
|
"hapticFeedback": settings.hapticFeedback,
|
||||||
"languageCode": settings.languageCode,
|
"languageCode": settings.languageCode,
|
||||||
"downloadUsingCellular": settings.downloadUsingCellular,
|
"downloadUsingCellular": settings.downloadUsingCellular,
|
||||||
"streamingUsingCellular": settings.streamingUsingCellular
|
"streamingUsingCellular": settings.streamingUsingCellular,
|
||||||
|
"disableSleepTimerFadeOut": settings.disableSleepTimerFadeOut
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -366,6 +366,56 @@ class AudioPlayer: NSObject {
|
||||||
updateNowPlaying()
|
updateNowPlaying()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func startFadeOut() {
|
||||||
|
guard self.isInitialized() else { return }
|
||||||
|
guard let currentTime = self.getCurrentTime() else { return }
|
||||||
|
logger.log("fadeOut: Fading out playback")
|
||||||
|
|
||||||
|
// Define fade parameters.
|
||||||
|
let fadeDuration: Float = 60.0 // total fade duration in seconds
|
||||||
|
let interval: Float = 1.0 // timer interval in seconds
|
||||||
|
|
||||||
|
// Get the current volume.
|
||||||
|
let initialVolume = self.audioPlayer.volume
|
||||||
|
let targetVolume: Float = 0.0
|
||||||
|
|
||||||
|
// If the current volume is already at or below zero, just pause.
|
||||||
|
if initialVolume <= targetVolume {
|
||||||
|
self.pause()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the volume change per timer tick.
|
||||||
|
// (targetVolume - initialVolume) is negative since target < initial.
|
||||||
|
let step = (targetVolume - initialVolume) * interval / fadeDuration
|
||||||
|
|
||||||
|
// Schedule a timer on the main queue to adjust the volume.
|
||||||
|
DispatchQueue.runOnMainQueue { [weak self] in
|
||||||
|
var timer = Timer.scheduledTimer(withTimeInterval: TimeInterval(interval), repeats: true) { t in
|
||||||
|
guard let self = self else {
|
||||||
|
t.invalidate()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the new volume.
|
||||||
|
let newVolume = self.audioPlayer.volume + step
|
||||||
|
|
||||||
|
// Check if the next step would go below zero.
|
||||||
|
if newVolume > targetVolume {
|
||||||
|
self.audioPlayer.volume = newVolume
|
||||||
|
} else {
|
||||||
|
// Ensure volume is exactly zero and end fade.
|
||||||
|
self.audioPlayer.volume = targetVolume
|
||||||
|
t.invalidate()
|
||||||
|
self.logger.log("Fadeout: Fade complete, pausing playback")
|
||||||
|
self.pause()
|
||||||
|
self.audioPlayer.volume = initialVolume
|
||||||
|
self.seek(currentTime, from: "fadeOut")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func seek(_ to: Double, from: String) {
|
public func seek(_ to: Double, from: String) {
|
||||||
logger.log("SEEK: Seek to \(to) from \(from)")
|
logger.log("SEEK: Seek to \(to) from \(from)")
|
||||||
|
|
||||||
|
|
|
@ -126,6 +126,10 @@ extension AudioPlayer {
|
||||||
sleepTimeRemaining -= 1
|
sleepTimeRemaining -= 1
|
||||||
self.sleepTimeRemaining = sleepTimeRemaining
|
self.sleepTimeRemaining = sleepTimeRemaining
|
||||||
|
|
||||||
|
if sleepTimeRemaining == 60 && self.isSleepTimerFadeOutEnabled() {
|
||||||
|
self.startFadeOut()
|
||||||
|
}
|
||||||
|
|
||||||
// Handle the sleep if the timer has expired
|
// Handle the sleep if the timer has expired
|
||||||
if sleepTimeRemaining <= 0 {
|
if sleepTimeRemaining <= 0 {
|
||||||
self.handleSleepEnd()
|
self.handleSleepEnd()
|
||||||
|
@ -155,4 +159,8 @@ extension AudioPlayer {
|
||||||
return self.sleepTimeChapterStopAt != nil
|
return self.sleepTimeChapterStopAt != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func isSleepTimerFadeOutEnabled() -> Bool {
|
||||||
|
let deviceSettings = Database.shared.getDeviceSettings()
|
||||||
|
return !deviceSettings.disableSleepTimerFadeOut
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,7 @@
|
||||||
<ui-text-input :value="shakeSensitivityOption" readonly append-icon="expand_more" style="width: 145px; max-width: 145px" />
|
<ui-text-input :value="shakeSensitivityOption" readonly append-icon="expand_more" style="width: 145px; max-width: 145px" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
<div class="flex items-center py-3">
|
<div class="flex items-center py-3">
|
||||||
<div class="w-10 flex justify-center" @click="toggleDisableSleepTimerFadeOut">
|
<div class="w-10 flex justify-center" @click="toggleDisableSleepTimerFadeOut">
|
||||||
<ui-toggle-switch v-model="settings.disableSleepTimerFadeOut" @input="saveSettings" />
|
<ui-toggle-switch v-model="settings.disableSleepTimerFadeOut" @input="saveSettings" />
|
||||||
|
@ -91,6 +92,7 @@
|
||||||
<p class="pl-4">{{ $strings.LabelDisableAudioFadeOut }}</p>
|
<p class="pl-4">{{ $strings.LabelDisableAudioFadeOut }}</p>
|
||||||
<span class="material-icons-outlined ml-2" @click.stop="showInfo('disableSleepTimerFadeOut')">info</span>
|
<span class="material-icons-outlined ml-2" @click.stop="showInfo('disableSleepTimerFadeOut')">info</span>
|
||||||
</div>
|
</div>
|
||||||
|
<template v-if="!isiOS">
|
||||||
<div class="flex items-center py-3">
|
<div class="flex items-center py-3">
|
||||||
<div class="w-10 flex justify-center" @click="toggleDisableSleepTimerResetFeedback">
|
<div class="w-10 flex justify-center" @click="toggleDisableSleepTimerResetFeedback">
|
||||||
<ui-toggle-switch v-model="settings.disableSleepTimerResetFeedback" @input="saveSettings" />
|
<ui-toggle-switch v-model="settings.disableSleepTimerResetFeedback" @input="saveSettings" />
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue