Merge pull request #117 from rasmuslos/master

Small improvements
This commit is contained in:
advplyr
2022-03-23 18:01:24 -05:00
committed by GitHub
8 changed files with 20081 additions and 106 deletions
+40 -4
View File
@@ -8,6 +8,9 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; }; 2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; };
3A200C1527D64D7E00CBF02E /* AudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A200C1427D64D7E00CBF02E /* AudioPlayer.swift */; };
3A200C1727D64E0D00CBF02E /* Audiobook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A200C1627D64E0D00CBF02E /* Audiobook.swift */; };
3AFCB5E827EA240D00ECCC05 /* NowPlayingInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AFCB5E727EA240D00ECCC05 /* NowPlayingInfo.swift */; };
4D8D410C26E17C3A00BA5F0D /* MyNativeAudio.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D8D410B26E17C3A00BA5F0D /* MyNativeAudio.swift */; }; 4D8D410C26E17C3A00BA5F0D /* MyNativeAudio.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D8D410B26E17C3A00BA5F0D /* MyNativeAudio.swift */; };
4D8D412E26E187E500BA5F0D /* MyNativeAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D8D412D26E187E500BA5F0D /* MyNativeAudio.m */; }; 4D8D412E26E187E500BA5F0D /* MyNativeAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D8D412D26E187E500BA5F0D /* MyNativeAudio.m */; };
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 */; };
@@ -21,6 +24,9 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = "<group>"; }; 2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = "<group>"; };
3A200C1427D64D7E00CBF02E /* AudioPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayer.swift; sourceTree = "<group>"; };
3A200C1627D64E0D00CBF02E /* Audiobook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Audiobook.swift; sourceTree = "<group>"; };
3AFCB5E727EA240D00ECCC05 /* NowPlayingInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NowPlayingInfo.swift; sourceTree = "<group>"; };
4D8D410B26E17C3A00BA5F0D /* MyNativeAudio.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyNativeAudio.swift; sourceTree = "<group>"; }; 4D8D410B26E17C3A00BA5F0D /* MyNativeAudio.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyNativeAudio.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>"; };
4D8D412D26E187E500BA5F0D /* MyNativeAudio.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MyNativeAudio.m; sourceTree = "<group>"; }; 4D8D412D26E187E500BA5F0D /* MyNativeAudio.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MyNativeAudio.m; sourceTree = "<group>"; };
@@ -57,6 +63,32 @@
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
3AFCB5E427EA231D00ECCC05 /* audio */ = {
isa = PBXGroup;
children = (
3A200C1427D64D7E00CBF02E /* AudioPlayer.swift */,
4D8D410B26E17C3A00BA5F0D /* MyNativeAudio.swift */,
4D8D412D26E187E500BA5F0D /* MyNativeAudio.m */,
);
path = audio;
sourceTree = "<group>";
};
3AFCB5E527EA232A00ECCC05 /* models */ = {
isa = PBXGroup;
children = (
3A200C1627D64E0D00CBF02E /* Audiobook.swift */,
);
path = models;
sourceTree = "<group>";
};
3AFCB5E627EA23F700ECCC05 /* util */ = {
isa = PBXGroup;
children = (
3AFCB5E727EA240D00ECCC05 /* NowPlayingInfo.swift */,
);
path = util;
sourceTree = "<group>";
};
504EC2FB1FED79650016851F = { 504EC2FB1FED79650016851F = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -78,6 +110,9 @@
504EC3061FED79650016851F /* App */ = { 504EC3061FED79650016851F /* App */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
3AFCB5E627EA23F700ECCC05 /* util */,
3AFCB5E527EA232A00ECCC05 /* models */,
3AFCB5E427EA231D00ECCC05 /* audio */,
50379B222058CBB4000EE86E /* capacitor.config.json */, 50379B222058CBB4000EE86E /* capacitor.config.json */,
504EC3071FED79650016851F /* AppDelegate.swift */, 504EC3071FED79650016851F /* AppDelegate.swift */,
504EC30B1FED79650016851F /* Main.storyboard */, 504EC30B1FED79650016851F /* Main.storyboard */,
@@ -86,8 +121,6 @@
504EC3131FED79650016851F /* Info.plist */, 504EC3131FED79650016851F /* Info.plist */,
2FAD9762203C412B000D30F8 /* config.xml */, 2FAD9762203C412B000D30F8 /* config.xml */,
50B271D01FEDC1A000F3C39B /* public */, 50B271D01FEDC1A000F3C39B /* public */,
4D8D410B26E17C3A00BA5F0D /* MyNativeAudio.swift */,
4D8D412D26E187E500BA5F0D /* MyNativeAudio.m */,
4D8D412C26E187E400BA5F0D /* App-Bridging-Header.h */, 4D8D412C26E187E400BA5F0D /* App-Bridging-Header.h */,
); );
path = App; path = App;
@@ -218,6 +251,9 @@
4D8D412E26E187E500BA5F0D /* MyNativeAudio.m in Sources */, 4D8D412E26E187E500BA5F0D /* MyNativeAudio.m in Sources */,
504EC3081FED79650016851F /* AppDelegate.swift in Sources */, 504EC3081FED79650016851F /* AppDelegate.swift in Sources */,
4D8D410C26E17C3A00BA5F0D /* MyNativeAudio.swift in Sources */, 4D8D410C26E17C3A00BA5F0D /* MyNativeAudio.swift in Sources */,
3A200C1727D64E0D00CBF02E /* Audiobook.swift in Sources */,
3A200C1527D64D7E00CBF02E /* AudioPlayer.swift in Sources */,
3AFCB5E827EA240D00ECCC05 /* NowPlayingInfo.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -364,7 +400,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 0.9.40; MARKETING_VERSION = 0.9.40;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = com.audiobookshelf.app; PRODUCT_BUNDLE_IDENTIFIER = com.audiobookshelf.app.dev;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OBJC_BRIDGING_HEADER = "App/App-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "App/App-Bridging-Header.h";
@@ -387,7 +423,7 @@
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";
MARKETING_VERSION = 0.9.40; MARKETING_VERSION = 0.9.40;
PRODUCT_BUNDLE_IDENTIFIER = com.audiobookshelf.app; PRODUCT_BUNDLE_IDENTIFIER = com.audiobookshelf.app.dev;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
SWIFT_OBJC_BRIDGING_HEADER = "App/App-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "App/App-Bridging-Header.h";
-1
View File
@@ -7,7 +7,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow? var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UIApplication.shared.beginReceivingRemoteControlEvents()
// Override point for customization after application launch. // Override point for customization after application launch.
return true return true
} }
@@ -10,23 +10,16 @@ import AVFoundation
import UIKit import UIKit
import MediaPlayer import MediaPlayer
func getData(from url: URL, completion: @escaping (UIImage?) -> Void) {
URLSession.shared.dataTask(with: url, completionHandler: {(data, response, error) in
if let data = data {
completion(UIImage(data:data))
}
}).resume()
}
class AudioPlayer: NSObject { class AudioPlayer: NSObject {
// enums and @objc are not compatible // enums and @objc are not compatible
@objc dynamic var status: Int @objc dynamic var status: Int
@objc dynamic var rate: Float @objc dynamic var rate: Float
private var tmpRate: Float = 1.0 private var tmpRate: Float = 1.0
private var lastPlayTime: Double = 0.0
private var playerContext = 0 private var playerContext = 0
private var playerItemContext = 0 private var playerItemContext = 0
private var nowPlayingInfo: [String: Any] = [:]
private var playWhenReady: Bool private var playWhenReady: Bool
@@ -44,7 +37,7 @@ class AudioPlayer: NSObject {
initAudioSession() initAudioSession()
setupRemoteTransportControls() setupRemoteTransportControls()
invokeMetadataUpdate() NowPlayingInfo.setAudiobook(audiobook: audiobook)
// Listen to player events // Listen to player events
self.audioPlayer.addObserver(self, forKeyPath: #keyPath(AVPlayer.rate), options: .new, context: &playerContext) self.audioPlayer.addObserver(self, forKeyPath: #keyPath(AVPlayer.rate), options: .new, context: &playerContext)
@@ -63,13 +56,48 @@ class AudioPlayer: NSObject {
} }
func destroy() { func destroy() {
pause() pause()
audioPlayer.replaceCurrentItem(with: nil)
nowPlayingInfo = [:] do {
updateNowPlaying() try AVAudioSession.sharedInstance().setActive(false)
} catch {
NSLog("Failed to set AVAudioSession inactive")
print(error)
}
DispatchQueue.main.sync {
UIApplication.shared.endReceivingRemoteControlEvents()
}
} }
// MARK: - Methods // MARK: - Methods
public func play() { public func play(allowSeekBack: Bool = false) {
if allowSeekBack {
let diffrence = Date.timeIntervalSinceReferenceDate - lastPlayTime
var time: Int?
if lastPlayTime == 0 {
time = 5
} else if diffrence < 6 {
time = 2
} else if diffrence < 12 {
time = 10
} else if diffrence < 30 {
time = 15
} else if diffrence < 180 {
time = 20
} else if diffrence < 3600 {
time = 25
} else {
time = 29
}
if time != nil {
seek(getCurrentTime() - Double(time!))
}
}
lastPlayTime = Date.timeIntervalSinceReferenceDate
self.audioPlayer.play() self.audioPlayer.play()
self.status = 1 self.status = 1
self.rate = self.tmpRate self.rate = self.tmpRate
@@ -83,6 +111,7 @@ class AudioPlayer: NSObject {
self.rate = 0.0 self.rate = 0.0
updateNowPlaying() updateNowPlaying()
lastPlayTime = Date.timeIntervalSinceReferenceDate
} }
public func seek(_ to: Double) { public func seek(_ to: Double) {
let continuePlaing = rate > 0.0 let continuePlaing = rate > 0.0
@@ -137,18 +166,18 @@ class AudioPlayer: NSObject {
print(error) print(error)
} }
} }
private func shouldFetchCover() -> Bool {
nowPlayingInfo[MPNowPlayingInfoPropertyExternalContentIdentifier] as? String != audiobook.streamId || nowPlayingInfo[MPMediaItemPropertyArtwork] == nil
}
// MARK: - Now playing // MARK: - Now playing
func setupRemoteTransportControls() { func setupRemoteTransportControls() {
DispatchQueue.main.sync {
UIApplication.shared.beginReceivingRemoteControlEvents()
}
let commandCenter = MPRemoteCommandCenter.shared() let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.playCommand.isEnabled = true commandCenter.playCommand.isEnabled = true
commandCenter.playCommand.addTarget { [unowned self] event in commandCenter.playCommand.addTarget { [unowned self] event in
play() play(allowSeekBack: true)
return .success return .success
} }
commandCenter.pauseCommand.isEnabled = true commandCenter.pauseCommand.isEnabled = true
@@ -187,51 +216,21 @@ class AudioPlayer: NSObject {
self.seek(event.positionTime) self.seek(event.positionTime)
return .success return .success
} }
}
func invokeMetadataUpdate() {
if !shouldFetchCover() || audiobook.artworkUrl == nil {
setMetadata(nil)
return
}
guard let url = URL(string: audiobook.artworkUrl!) else { return } commandCenter.changePlaybackRateCommand.isEnabled = true
getData(from: url) { [weak self] image in commandCenter.changePlaybackRateCommand.supportedPlaybackRates = [0.5, 0.75, 1.0, 1.25, 1.5, 2]
guard let self = self, commandCenter.changePlaybackRateCommand.addTarget { event in
let downloadedImage = image else { guard let event = event as? MPChangePlaybackRateCommandEvent else {
return return .noSuchContent
} }
let artwork = MPMediaItemArtwork.init(boundsSize: downloadedImage.size, requestHandler: { _ -> UIImage in
return downloadedImage
})
self.setMetadata(artwork) self.setPlaybackRate(event.playbackRate)
return .success
} }
} }
func setMetadata(_ artwork: MPMediaItemArtwork?) {
if artwork != nil {
nowPlayingInfo[MPMediaItemPropertyArtwork] = artwork
} else if shouldFetchCover() {
nowPlayingInfo[MPMediaItemPropertyArtwork] = nil
}
nowPlayingInfo[MPNowPlayingInfoPropertyExternalContentIdentifier] = audiobook.streamId
nowPlayingInfo[MPNowPlayingInfoPropertyAssetURL] = URL(string: audiobook.playlistUrl)
nowPlayingInfo[MPNowPlayingInfoPropertyIsLiveStream] = false
nowPlayingInfo[MPNowPlayingInfoPropertyMediaType] = "hls"
nowPlayingInfo[MPMediaItemPropertyTitle] = audiobook.title
nowPlayingInfo[MPMediaItemPropertyArtist] = audiobook.author ?? "unknown"
nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = audiobook.series
}
func updateNowPlaying() { func updateNowPlaying() {
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = getDuration() NowPlayingInfo.update(duration: getDuration(), currentTime: getCurrentTime(), rate: rate)
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = getCurrentTime()
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = rate
nowPlayingInfo[MPNowPlayingInfoPropertyDefaultPlaybackRate] = 1.0
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
} }
// MARK: - Observer // MARK: - Observer
@@ -9,7 +9,6 @@ func parseSleepTime(millis: String?) -> Double {
@objc(MyNativeAudio) @objc(MyNativeAudio)
public class MyNativeAudio: CAPPlugin { public class MyNativeAudio: CAPPlugin {
var currentCall: CAPPluginCall?
var currentPlayer: AudioPlayer? var currentPlayer: AudioPlayer?
var playerContext = 0 var playerContext = 0
@@ -36,17 +35,19 @@ public class MyNativeAudio: CAPPlugin {
) )
let playWhenReady = call.getBool("playWhenReady", false) let playWhenReady = call.getBool("playWhenReady", false)
if self.currentPlayer != nil && self.currentPlayer?.audiobook.streamId == audiobook.streamId { if currentPlayer != nil && currentPlayer?.audiobook.streamId == audiobook.streamId {
if playWhenReady { if playWhenReady {
self.currentPlayer?.play() self.currentPlayer?.play()
} }
call.resolve(["success": true]) call.resolve(["success": true])
return return
} else if currentPlayer != nil && currentPlayer?.audiobook.streamId != audiobook.streamId {
stop()
} }
self.currentPlayer = AudioPlayer(audiobook: audiobook, playWhenReady: playWhenReady) currentPlayer = AudioPlayer(audiobook: audiobook, playWhenReady: playWhenReady)
self.currentPlayer!.addObserver(self, forKeyPath: #keyPath(AudioPlayer.status), options: .new, context: &playerContext) currentPlayer!.addObserver(self, forKeyPath: #keyPath(AudioPlayer.status), options: .new, context: &playerContext)
call.resolve(["success": true]) call.resolve(["success": true])
} }
@@ -116,7 +117,7 @@ public class MyNativeAudio: CAPPlugin {
return return
} }
self.currentPlayer!.play() self.currentPlayer!.play(allowSeekBack: true)
sendPlaybackStatusUpdate(true) sendPlaybackStatusUpdate(true)
call.resolve() call.resolve()
@@ -126,15 +127,14 @@ public class MyNativeAudio: CAPPlugin {
stop() stop()
call.resolve() call.resolve()
} }
@objc func stop() { @objc func stop(_ call: CAPPluginCall? = nil) {
if let call = currentCall { if self.currentPlayer != nil {
if self.currentPlayer != nil { self.currentPlayer!.destroy()
self.currentPlayer!.destroy() }
} self.currentPlayer = nil
self.currentPlayer = nil if call != nil {
currentCall = nil; call!.resolve([ "result": true ])
call.resolve([ "result": true ])
} }
} }
+79
View File
@@ -0,0 +1,79 @@
//
// NowPlaying.swift
// App
//
// Created by Rasmus Krämer on 22.03.22.
//
import Foundation
import MediaPlayer
func getData(from url: URL, completion: @escaping (UIImage?) -> Void) {
URLSession.shared.dataTask(with: url, completionHandler: {(data, response, error) in
if let data = data {
completion(UIImage(data:data))
}
}).resume()
}
class NowPlayingInfo {
private static var nowPlayingInfo: [String: Any] = [:]
private static var audiobook: Audiobook?
public static func setAudiobook(audiobook: Audiobook) {
self.audiobook = audiobook
setMetadata(nil)
if !shouldFetchCover() || audiobook.artworkUrl == nil {
return
}
guard let url = URL(string: audiobook.artworkUrl!) else { return }
getData(from: url) { [self] image in
guard let downloadedImage = image else {
return
}
let artwork = MPMediaItemArtwork.init(boundsSize: downloadedImage.size, requestHandler: { _ -> UIImage in
return downloadedImage
})
self.setMetadata(artwork)
}
}
public static func update(duration: Double, currentTime: Double, rate: Float) {
nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = duration
nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = currentTime
nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = rate
nowPlayingInfo[MPNowPlayingInfoPropertyDefaultPlaybackRate] = 1.0
MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}
public static func reset() {
audiobook = nil
MPNowPlayingInfoCenter.default().nowPlayingInfo = nil
}
private static func setMetadata(_ artwork: MPMediaItemArtwork?) {
if self.audiobook == nil {
return
}
if artwork != nil {
nowPlayingInfo[MPMediaItemPropertyArtwork] = artwork
} else if shouldFetchCover() {
nowPlayingInfo[MPMediaItemPropertyArtwork] = nil
}
nowPlayingInfo[MPNowPlayingInfoPropertyExternalContentIdentifier] = audiobook!.streamId
nowPlayingInfo[MPNowPlayingInfoPropertyAssetURL] = URL(string: audiobook!.playlistUrl)
nowPlayingInfo[MPNowPlayingInfoPropertyIsLiveStream] = false
nowPlayingInfo[MPNowPlayingInfoPropertyMediaType] = "hls"
nowPlayingInfo[MPMediaItemPropertyTitle] = audiobook!.title
nowPlayingInfo[MPMediaItemPropertyArtist] = audiobook!.author ?? "unknown"
nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = audiobook!.series
}
private static func shouldFetchCover() -> Bool {
audiobook != nil && (nowPlayingInfo[MPNowPlayingInfoPropertyExternalContentIdentifier] as? String != audiobook!.streamId || nowPlayingInfo[MPMediaItemPropertyArtwork] == nil)
}
}
+19892 -30
View File
File diff suppressed because it is too large Load Diff