mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-31 23:57:15 +02:00
@@ -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";
|
||||||
|
|||||||
@@ -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 ])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+19892
-30
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user