- Changes icon

- Improved state sync
- Moved player to shared
This commit is contained in:
Rasmus Krämer 2022-03-28 22:09:43 +02:00
parent 4f1f0e4db7
commit f98aea71cb
35 changed files with 388 additions and 60 deletions

View file

@ -658,6 +658,10 @@ export default {
this.setFromObj()
}
console.log("received metadata update", data);
if(data.currentRate && data.currentRate > 0) this.playbackSpeed = data.currentRate
this.timeupdate()
},
async init() {

View file

@ -63,10 +63,19 @@
name = Frameworks;
sourceTree = "<group>";
};
3AC8248B27F2316900529205 /* Shared */ = {
isa = PBXGroup;
children = (
3AFCB5E527EA232A00ECCC05 /* models */,
3AFCB5E627EA23F700ECCC05 /* util */,
3A200C1427D64D7E00CBF02E /* AudioPlayer.swift */,
);
path = Shared;
sourceTree = "<group>";
};
3AFCB5E427EA231D00ECCC05 /* audio */ = {
isa = PBXGroup;
children = (
3A200C1427D64D7E00CBF02E /* AudioPlayer.swift */,
4D8D410B26E17C3A00BA5F0D /* MyNativeAudio.swift */,
4D8D412D26E187E500BA5F0D /* MyNativeAudio.m */,
);
@ -92,6 +101,7 @@
504EC2FB1FED79650016851F = {
isa = PBXGroup;
children = (
3AC8248B27F2316900529205 /* Shared */,
504EC3061FED79650016851F /* App */,
504EC3051FED79650016851F /* Products */,
7F8756D8B27F46E3366F6CEA /* Pods */,
@ -110,8 +120,6 @@
504EC3061FED79650016851F /* App */ = {
isa = PBXGroup;
children = (
3AFCB5E627EA23F700ECCC05 /* util */,
3AFCB5E527EA232A00ECCC05 /* models */,
3AFCB5E427EA231D00ECCC05 /* audio */,
50379B222058CBB4000EE86E /* capacitor.config.json */,
504EC3071FED79650016851F /* AppDelegate.swift */,
@ -163,8 +171,8 @@
504EC2FC1FED79650016851F /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 0920;
LastSwiftUpdateCheck = 920;
LastUpgradeCheck = 920;
TargetAttributes = {
504EC3031FED79650016851F = {
CreatedOnToolsVersion = 9.2;
@ -390,7 +398,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_APPICON_NAME = Icons;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
@ -414,7 +422,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_APPICON_NAME = Icons;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;

View file

@ -1,6 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,300 @@
{
"images" : [
{
"filename" : "icon-40.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"filename" : "icon-60.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"filename" : "icon-58.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "icon-87.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"filename" : "icon-80.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"filename" : "icon-120.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"filename" : "icon-120.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"filename" : "icon-180.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"filename" : "icon-20.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"filename" : "icon-40.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"filename" : "icon-29.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"filename" : "icon-58.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "icon-40.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"filename" : "icon-80.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"filename" : "icon-76.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"filename" : "icon-152.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"filename" : "icon-167.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"filename" : "icon-1024.png",
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
},
{
"filename" : "icon-120.png",
"idiom" : "car",
"scale" : "2x",
"size" : "60x60"
},
{
"filename" : "icon-180.png",
"idiom" : "car",
"scale" : "3x",
"size" : "60x60"
},
{
"filename" : "icon-48.png",
"idiom" : "watch",
"role" : "notificationCenter",
"scale" : "2x",
"size" : "24x24",
"subtype" : "38mm"
},
{
"filename" : "icon-55.png",
"idiom" : "watch",
"role" : "notificationCenter",
"scale" : "2x",
"size" : "27.5x27.5",
"subtype" : "42mm"
},
{
"filename" : "icon-58.png",
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "icon-87.png",
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "watch",
"role" : "notificationCenter",
"scale" : "2x",
"size" : "33x33",
"subtype" : "45mm"
},
{
"filename" : "icon-80.png",
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "40x40",
"subtype" : "38mm"
},
{
"filename" : "icon-88.png",
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "44x44",
"subtype" : "40mm"
},
{
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "46x46",
"subtype" : "41mm"
},
{
"filename" : "icon-100.png",
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "50x50",
"subtype" : "44mm"
},
{
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "51x51",
"subtype" : "45mm"
},
{
"filename" : "icon-172.png",
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "86x86",
"subtype" : "38mm"
},
{
"filename" : "icon-196.png",
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "98x98",
"subtype" : "42mm"
},
{
"filename" : "icon-216.png",
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "108x108",
"subtype" : "44mm"
},
{
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "117x117",
"subtype" : "45mm"
},
{
"filename" : "icon-1024.png",
"idiom" : "watch-marketing",
"scale" : "1x",
"size" : "1024x1024"
},
{
"filename" : "icon-16.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"filename" : "icon-32.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"filename" : "icon-32.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"filename" : "icon-64.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"filename" : "icon-128.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"filename" : "icon-256.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"filename" : "icon-256.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"filename" : "icon-512.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"filename" : "icon-512.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"filename" : "icon-1024.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 605 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 927 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View file

@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>audiobookshelf-app</string>
<string>Audiobookshelf</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@ -27,6 +27,10 @@
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSUserActivityTypes</key>
<array>
<string>INPlayMediaIntent</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>

View file

@ -9,8 +9,6 @@ func parseSleepTime(millis: String?) -> Double {
@objc(MyNativeAudio)
public class MyNativeAudio: CAPPlugin {
var currentPlayer: AudioPlayer?
var playerContext = 0
var currentSleepTimer: Timer? = nil
@ -35,22 +33,27 @@ public class MyNativeAudio: CAPPlugin {
)
let playWhenReady = call.getBool("playWhenReady", false)
if currentPlayer != nil && currentPlayer?.audiobook.streamId == audiobook.streamId {
if AudioPlayer.instance != nil && AudioPlayer.instance?.audiobook.streamId == audiobook.streamId {
if playWhenReady {
self.currentPlayer?.play()
AudioPlayer.instance?.play()
}
call.resolve(["success": true])
return
} else if currentPlayer != nil && currentPlayer?.audiobook.streamId != audiobook.streamId {
} else if AudioPlayer.instance != nil && AudioPlayer.instance?.audiobook.streamId != audiobook.streamId {
stop()
}
currentPlayer = AudioPlayer(audiobook: audiobook, playWhenReady: playWhenReady)
currentPlayer!.addObserver(self, forKeyPath: #keyPath(AudioPlayer.status), options: .new, context: &playerContext)
AudioPlayer.instance = AudioPlayer(audiobook: audiobook, playWhenReady: playWhenReady)
AudioPlayer.instance!.addObserver(self, forKeyPath: #keyPath(AudioPlayer.status), options: .new, context: &playerContext)
call.resolve(["success": true])
}
override public func load() {
NSLog("Load MyNativeAudio")
NotificationCenter.default.addObserver(self, selector: #selector(sendMetadata), name: UIApplication.willEnterForegroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(sendMetadata), name: UIApplication.didBecomeActiveNotification, object: nil)
}
public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard context == &playerContext else {
@ -58,37 +61,39 @@ public class MyNativeAudio: CAPPlugin {
return
}
if keyPath == #keyPath(AudioPlayer.status) {
NSLog("AudioPlayer state change: \(String(describing: keyPath))")
if keyPath == #keyPath(AudioPlayer.status) || keyPath == #keyPath(AudioPlayer.rate) {
sendMetadata()
}
}
@objc func seekForward(_ call: CAPPluginCall) {
if self.currentPlayer == nil {
if AudioPlayer.instance == nil {
call.resolve()
return
}
let amount = (Double(call.getString("amount", "0")) ?? 0) / 1000
let destinationTime = self.currentPlayer!.getCurrentTime() + amount
let destinationTime = AudioPlayer.instance!.getCurrentTime() + amount
self.currentPlayer!.seek(destinationTime)
AudioPlayer.instance!.seek(destinationTime)
call.resolve()
}
@objc func seekBackward(_ call: CAPPluginCall) {
if self.currentPlayer == nil {
if AudioPlayer.instance == nil {
call.resolve()
return
}
let amount = (Double(call.getString("amount", "0")) ?? 0) / 1000
let destinationTime = self.currentPlayer!.getCurrentTime() - amount
let destinationTime = AudioPlayer.instance!.getCurrentTime() - amount
self.currentPlayer!.seek(destinationTime)
AudioPlayer.instance!.seek(destinationTime)
call.resolve()
}
@objc func seekPlayer(_ call: CAPPluginCall) {
if self.currentPlayer == nil {
if AudioPlayer.instance == nil {
call.resolve()
return
}
@ -96,28 +101,28 @@ public class MyNativeAudio: CAPPlugin {
let seekTime = (Double(call.getString("timeMs", "0")) ?? 0) / 1000
NSLog("Seek Player \(seekTime)")
self.currentPlayer!.seek(seekTime)
AudioPlayer.instance!.seek(seekTime)
call.resolve()
}
@objc func pausePlayer(_ call: CAPPluginCall) {
if self.currentPlayer == nil {
if AudioPlayer.instance == nil {
call.resolve()
return
}
self.currentPlayer!.pause()
AudioPlayer.instance!.pause()
sendPlaybackStatusUpdate(false)
call.resolve()
}
@objc func playPlayer(_ call: CAPPluginCall) {
if self.currentPlayer == nil {
if AudioPlayer.instance == nil {
call.resolve()
return
}
self.currentPlayer!.play(allowSeekBack: true)
AudioPlayer.instance!.play(allowSeekBack: true)
sendPlaybackStatusUpdate(true)
call.resolve()
@ -128,10 +133,10 @@ public class MyNativeAudio: CAPPlugin {
call.resolve()
}
@objc func stop(_ call: CAPPluginCall? = nil) {
if self.currentPlayer != nil {
self.currentPlayer!.destroy()
if AudioPlayer.instance != nil {
AudioPlayer.instance!.destroy()
}
self.currentPlayer = nil
AudioPlayer.instance = nil
if call != nil {
call!.resolve([ "result": true ])
@ -139,37 +144,37 @@ public class MyNativeAudio: CAPPlugin {
}
@objc func getCurrentTime(_ call: CAPPluginCall) {
if self.currentPlayer == nil {
if AudioPlayer.instance == nil {
call.resolve()
return
}
let currentTime = self.currentPlayer?.getCurrentTime() ?? 0
let currentTime = AudioPlayer.instance?.getCurrentTime() ?? 0
call.resolve([ "value": currentTime * 1000, "bufferedTime": currentTime * 1000 ])
}
@objc func getStreamSyncData(_ call: CAPPluginCall) {
if self.currentPlayer == nil {
if AudioPlayer.instance == nil {
call.resolve([ "isPlaying": false, "lastPauseTime": 0, "id": nil ])
return
}
call.resolve([ "isPlaying": self.currentPlayer!.rate > 0.0, "lastPauseTime": 0, "id": self.currentPlayer?.audiobook.streamId as Any ])
call.resolve([ "isPlaying": AudioPlayer.instance!.rate > 0.0, "lastPauseTime": 0, "id": AudioPlayer.instance?.audiobook.streamId as Any ])
}
@objc func setPlaybackSpeed(_ call: CAPPluginCall) {
if self.currentPlayer == nil {
if AudioPlayer.instance == nil {
call.resolve()
return
}
let speed = call.getFloat("speed") ?? 0
self.currentPlayer!.setPlaybackRate(speed)
AudioPlayer.instance!.setPlaybackRate(speed)
call.resolve()
}
@objc func setSleepTimer(_ call: CAPPluginCall) {
if self.currentPlayer == nil {
if AudioPlayer.instance == nil {
call.resolve()
return
}
@ -180,14 +185,14 @@ public class MyNativeAudio: CAPPlugin {
call.resolve([ "success": true ])
}
@objc func increaseSleepTime(_ call: CAPPluginCall) {
if self.currentPlayer == nil {
if AudioPlayer.instance == nil {
call.resolve()
return
}
var time = self.remainingSleepDuration + parseSleepTime(millis: call.getString("time"))
if time > self.currentPlayer!.getDuration() {
time = self.currentPlayer!.getDuration()
if time > AudioPlayer.instance!.getDuration() {
time = AudioPlayer.instance!.getDuration()
}
setSleepTimer(seconds: time)
@ -217,7 +222,7 @@ public class MyNativeAudio: CAPPlugin {
}
func setSleepTimer(seconds: Double) {
if currentPlayer == nil {
if AudioPlayer.instance == nil {
return
}
@ -239,7 +244,7 @@ public class MyNativeAudio: CAPPlugin {
}
}
func updateSleepTime() {
if currentPlayer == nil {
if AudioPlayer.instance == nil {
return
}
@ -248,10 +253,10 @@ public class MyNativeAudio: CAPPlugin {
currentSleepTimer!.invalidate()
}
self.notifyListeners("onSleepTimerEnded", data: [
"value": currentPlayer!.getCurrentTime(),
"value": AudioPlayer.instance!.getCurrentTime(),
])
currentPlayer!.pause()
AudioPlayer.instance!.pause()
return
}
@ -261,16 +266,21 @@ public class MyNativeAudio: CAPPlugin {
])
}
func sendMetadata() {
if self.currentPlayer == nil {
@objc func sendMetadata() {
if AudioPlayer.instance == nil {
return
}
NSLog("fired metadata update")
self.notifyListeners("onMetadata", data: [
"duration": self.currentPlayer!.getDuration() * 1000,
"currentTime": self.currentPlayer!.getCurrentTime() * 1000,
"stateName": "unknown"
"duration": AudioPlayer.instance!.getDuration() * 1000,
"currentTime": AudioPlayer.instance!.getCurrentTime() * 1000,
"stateName": "unknown",
"currentRate": AudioPlayer.instance!.rate
])
sendPlaybackStatusUpdate(AudioPlayer.instance!.rate != 0.0)
}
func sendPlaybackStatusUpdate(_ playing: Bool) {
self.notifyListeners("onPlayingUpdate", data: [

View file

@ -26,6 +26,7 @@ class AudioPlayer: NSObject {
private var audioPlayer: AVPlayer
public var audiobook: Audiobook
// MARK: - Constructor
init(audiobook: Audiobook, playWhenReady: Bool = false) {
self.playWhenReady = playWhenReady
self.audiobook = audiobook
@ -54,7 +55,7 @@ class AudioPlayer: NSObject {
deinit {
destroy()
}
func destroy() {
public func destroy() {
pause()
audioPlayer.replaceCurrentItem(with: nil)
@ -167,9 +168,8 @@ class AudioPlayer: NSObject {
}
}
// MARK: - Now playing
func setupRemoteTransportControls() {
private func setupRemoteTransportControls() {
DispatchQueue.main.sync {
UIApplication.shared.beginReceivingRemoteControlEvents()
}
@ -228,8 +228,7 @@ class AudioPlayer: NSObject {
return .success
}
}
func updateNowPlaying() {
private func updateNowPlaying() {
NowPlayingInfo.update(duration: getDuration(), currentTime: getCurrentTime(), rate: rate)
}
@ -240,7 +239,7 @@ class AudioPlayer: NSObject {
guard let playerStatus = AVPlayerItem.Status(rawValue: (change?[.newKey] as? Int ?? -1)) else { return }
if playerStatus == .readyToPlay {
updateNowPlaying()
self.updateNowPlaying()
self.status = 0
if self.playWhenReady {
@ -251,7 +250,7 @@ class AudioPlayer: NSObject {
}
} else if context == &playerContext {
if keyPath == #keyPath(AVPlayer.rate) {
setPlaybackRate(change?[.newKey] as? Float ?? 1.0, observed: true)
self.setPlaybackRate(change?[.newKey] as? Float ?? 1.0, observed: true)
} else if keyPath == #keyPath(AVPlayer.currentItem) {
NSLog("WARNING: Item ended")
}
@ -260,4 +259,7 @@ class AudioPlayer: NSObject {
return
}
}
// MARK: - Factory
public static var instance: AudioPlayer?
}