mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-07-15 16:34:43 +02:00
Rewrite of object model to use Unrealm
This addresses issues with JSON serialization
This commit is contained in:
parent
0b46a9c9b1
commit
f6c43e479d
12 changed files with 297 additions and 662 deletions
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -15,7 +15,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
migrationBlock: { migration, oldSchemaVersion in
|
||||
if (oldSchemaVersion < 1) {
|
||||
NSLog("Realm schema version was \(oldSchemaVersion)")
|
||||
migration.enumerateObjects(ofType: DeviceSettings.className()) { oldObject, newObject in
|
||||
migration.enumerateObjects(ofType: DeviceSettings.rlmClassName()) { oldObject, newObject in
|
||||
newObject?["enableAltView"] = false
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,32 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
)
|
||||
Realm.Configuration.defaultConfiguration = configuration
|
||||
|
||||
Realm.registerRealmables(DeviceSettings.self)
|
||||
Realm.registerRealmables(ServerConnectionConfig.self)
|
||||
Realm.registerRealmables(ServerConnectionConfigActiveIndex.self)
|
||||
|
||||
// Data classes
|
||||
Realm.registerRealmables(LibraryItem.self)
|
||||
Realm.registerRealmables(MediaType.self)
|
||||
Realm.registerRealmables(Metadata.self)
|
||||
Realm.registerRealmables(PodcastEpisode.self)
|
||||
Realm.registerRealmables(AudioFile.self)
|
||||
Realm.registerRealmables(Author.self)
|
||||
Realm.registerRealmables(Chapter.self)
|
||||
Realm.registerRealmables(AudioTrack.self)
|
||||
Realm.registerRealmables(FileMetadata.self)
|
||||
Realm.registerRealmables(Library.self)
|
||||
Realm.registerRealmables(Folder.self)
|
||||
Realm.registerRealmables(LibraryFile.self)
|
||||
Realm.registerRealmables(MediaProgress.self)
|
||||
Realm.registerRealmables(PlaybackMetadata.self)
|
||||
|
||||
// Local library
|
||||
Realm.registerRealmables(LocalLibraryItem.self)
|
||||
Realm.registerRealmables(LocalPodcastEpisode.self)
|
||||
Realm.registerRealmables(LocalFile.self)
|
||||
Realm.registerRealmables(LocalMediaProgress.self)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
@ -35,18 +35,12 @@ public class AbsDatabase: CAPPlugin {
|
|||
let token = call.getString("token", "")
|
||||
|
||||
let name = "\(address) (\(username))"
|
||||
let config = ServerConnectionConfig()
|
||||
|
||||
if id == nil {
|
||||
id = "\(address)@\(username)".toBase64()
|
||||
}
|
||||
|
||||
config.id = id!
|
||||
config.name = name
|
||||
config.address = address
|
||||
config.userId = userId
|
||||
config.username = username
|
||||
config.token = token
|
||||
let config = ServerConnectionConfig(id: id!, index: 0, name: name, address: address, userId: userId, username: username, token: token)
|
||||
|
||||
Store.serverConfig = config
|
||||
call.resolve(convertServerConnectionConfigToJSON(config: config))
|
||||
|
@ -128,11 +122,7 @@ public class AbsDatabase: CAPPlugin {
|
|||
let enableAltView = call.getBool("enableAltView") ?? false
|
||||
let jumpBackwardsTime = call.getInt("jumpBackwardsTime") ?? 10
|
||||
let jumpForwardTime = call.getInt("jumpForwardTime") ?? 10
|
||||
let settings = DeviceSettings()
|
||||
settings.disableAutoRewind = disableAutoRewind
|
||||
settings.enableAltView = enableAltView
|
||||
settings.jumpBackwardsTime = jumpBackwardsTime
|
||||
settings.jumpForwardTime = jumpForwardTime
|
||||
let settings = DeviceSettings(disableAutoRewind: disableAutoRewind, enableAltView: enableAltView, jumpBackwardsTime: jumpBackwardsTime, jumpForwardTime: jumpForwardTime)
|
||||
|
||||
Database.shared.setDeviceSettings(deviceSettings: settings)
|
||||
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
|
||||
import Foundation
|
||||
import CoreMedia
|
||||
import RealmSwift
|
||||
import Unrealm
|
||||
|
||||
struct LibraryItem: Codable {
|
||||
struct LibraryItem: Realmable, Codable {
|
||||
var id: String
|
||||
var ino: String
|
||||
var libraryId: String
|
||||
|
@ -17,12 +17,12 @@ struct LibraryItem: Codable {
|
|||
var path: String
|
||||
var relPath: String
|
||||
var isFile: Bool
|
||||
var mtimeMs: Int64
|
||||
var ctimeMs: Int64
|
||||
var birthtimeMs: Int64
|
||||
var addedAt: Int64
|
||||
var updatedAt: Int64
|
||||
var lastScan: Int64?
|
||||
var mtimeMs: Int
|
||||
var ctimeMs: Int
|
||||
var birthtimeMs: Int
|
||||
var addedAt: Int
|
||||
var updatedAt: Int
|
||||
var lastScan: Int?
|
||||
var scanVersion: String?
|
||||
var isMissing: Bool
|
||||
var isInvalid: Bool
|
||||
|
@ -30,9 +30,29 @@ struct LibraryItem: Codable {
|
|||
var media: MediaType
|
||||
var libraryFiles: [LibraryFile]
|
||||
var userMediaProgress: MediaProgress?
|
||||
|
||||
init() {
|
||||
id = ""
|
||||
ino = ""
|
||||
libraryId = ""
|
||||
folderId = ""
|
||||
path = ""
|
||||
relPath = ""
|
||||
isFile = true
|
||||
mtimeMs = 0
|
||||
ctimeMs = 0
|
||||
birthtimeMs = 0
|
||||
addedAt = 0
|
||||
updatedAt = 0
|
||||
isMissing = false
|
||||
isInvalid = false
|
||||
mediaType = ""
|
||||
media = MediaType()
|
||||
libraryFiles = []
|
||||
}
|
||||
}
|
||||
|
||||
struct MediaType: Codable {
|
||||
struct MediaType: Realmable, Codable {
|
||||
var libraryItemId: String?
|
||||
var metadata: Metadata
|
||||
var coverPath: String?
|
||||
|
@ -40,13 +60,17 @@ struct MediaType: Codable {
|
|||
var audioFiles: [AudioFile]?
|
||||
var chapters: [Chapter]?
|
||||
var tracks: [AudioTrack]?
|
||||
var size: Int64?
|
||||
var size: Int?
|
||||
var duration: Double?
|
||||
var episodes: [PodcastEpisode]?
|
||||
var autoDownloadEpisodes: Bool?
|
||||
|
||||
init() {
|
||||
metadata = Metadata()
|
||||
}
|
||||
}
|
||||
|
||||
struct Metadata: Codable {
|
||||
struct Metadata: Realmable, Codable {
|
||||
var title: String
|
||||
var subtitle: String?
|
||||
var authors: [Author]?
|
||||
|
@ -65,9 +89,15 @@ struct Metadata: Codable {
|
|||
var narratorName: String?
|
||||
var seriesName: String?
|
||||
var feedUrl: String?
|
||||
|
||||
init() {
|
||||
title = "Unknown"
|
||||
genres = []
|
||||
explicit = false
|
||||
}
|
||||
}
|
||||
|
||||
struct PodcastEpisode: Codable {
|
||||
struct PodcastEpisode: Realmable, Codable {
|
||||
var id: String
|
||||
var index: Int
|
||||
var episode: String?
|
||||
|
@ -78,30 +108,55 @@ struct PodcastEpisode: Codable {
|
|||
var audioFile: AudioFile?
|
||||
var audioTrack: AudioTrack?
|
||||
var duration: Double
|
||||
var size: Int64
|
||||
var size: Int
|
||||
// var serverEpisodeId: String?
|
||||
|
||||
init() {
|
||||
id = ""
|
||||
index = 0
|
||||
title = "Unknown"
|
||||
duration = 0
|
||||
size = 0
|
||||
}
|
||||
}
|
||||
|
||||
struct AudioFile: Codable {
|
||||
struct AudioFile: Realmable, Codable {
|
||||
var index: Int
|
||||
var ino: String
|
||||
var metadata: FileMetadata
|
||||
|
||||
init() {
|
||||
index = 0
|
||||
ino = ""
|
||||
metadata = FileMetadata()
|
||||
}
|
||||
}
|
||||
|
||||
struct Author: Codable {
|
||||
struct Author: Realmable, Codable {
|
||||
var id: String
|
||||
var name: String
|
||||
var coverPath: String?
|
||||
|
||||
init() {
|
||||
id = ""
|
||||
name = "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
struct Chapter: Codable {
|
||||
struct Chapter: Realmable, Codable {
|
||||
var id: Int
|
||||
var start: Double
|
||||
var end: Double
|
||||
var title: String?
|
||||
|
||||
init() {
|
||||
id = 0
|
||||
start = 0
|
||||
end = 0
|
||||
}
|
||||
}
|
||||
|
||||
struct AudioTrack: Codable {
|
||||
struct AudioTrack: Realmable, Codable {
|
||||
var index: Int?
|
||||
var startOffset: Double?
|
||||
var duration: Double
|
||||
|
@ -113,34 +168,64 @@ struct AudioTrack: Codable {
|
|||
// var localFileId: String?
|
||||
// var audioProbeResult: AudioProbeResult? Needed for local playback
|
||||
var serverIndex: Int?
|
||||
|
||||
init() {
|
||||
duration = 0
|
||||
mimeType = ""
|
||||
}
|
||||
}
|
||||
|
||||
struct FileMetadata: Codable {
|
||||
struct FileMetadata: Realmable, Codable {
|
||||
var filename: String
|
||||
var ext: String
|
||||
var path: String
|
||||
var relPath: String
|
||||
|
||||
init() {
|
||||
filename = ""
|
||||
ext = ""
|
||||
path = ""
|
||||
relPath = ""
|
||||
}
|
||||
}
|
||||
|
||||
struct Library: Codable {
|
||||
struct Library: Realmable, Codable {
|
||||
var id: String
|
||||
var name: String
|
||||
var folders: [Folder]
|
||||
var icon: String
|
||||
var mediaType: String
|
||||
|
||||
init() {
|
||||
id = ""
|
||||
name = "Unknown"
|
||||
folders = []
|
||||
icon = ""
|
||||
mediaType = ""
|
||||
}
|
||||
}
|
||||
|
||||
struct Folder: Codable {
|
||||
struct Folder: Realmable, Codable {
|
||||
var id: String
|
||||
var fullPath: String
|
||||
|
||||
init() {
|
||||
id = ""
|
||||
fullPath = ""
|
||||
}
|
||||
}
|
||||
|
||||
struct LibraryFile: Codable {
|
||||
struct LibraryFile: Realmable, Codable {
|
||||
var ino: String
|
||||
var metadata: FileMetadata
|
||||
|
||||
init() {
|
||||
ino = ""
|
||||
metadata = FileMetadata()
|
||||
}
|
||||
}
|
||||
|
||||
struct MediaProgress:Codable {
|
||||
struct MediaProgress: Realmable, Codable {
|
||||
var id: String
|
||||
var libraryItemId: String
|
||||
var episodeId: String?
|
||||
|
@ -148,15 +233,36 @@ struct MediaProgress:Codable {
|
|||
var progress: Double
|
||||
var currentTime: Double
|
||||
var isFinished: Bool
|
||||
var lastUpdate:Int64
|
||||
var startedAt:Int64
|
||||
var finishedAt:Int64?
|
||||
var lastUpdate: Int
|
||||
var startedAt: Int
|
||||
var finishedAt: Int?
|
||||
|
||||
init() {
|
||||
id = ""
|
||||
libraryItemId = ""
|
||||
duration = 0
|
||||
progress = 0
|
||||
currentTime = 0
|
||||
isFinished = false
|
||||
lastUpdate = 0
|
||||
startedAt = 0
|
||||
}
|
||||
}
|
||||
|
||||
struct PlaybackMetadata: Codable {
|
||||
struct PlaybackMetadata: Realmable, Codable {
|
||||
var duration: Double
|
||||
var currentTime: Double
|
||||
var playerState: PlayerState
|
||||
|
||||
init() {
|
||||
duration = 0
|
||||
currentTime = 0
|
||||
playerState = PlayerState.IDLE
|
||||
}
|
||||
|
||||
static func ignoredProperties() -> [String] {
|
||||
return ["playerState"]
|
||||
}
|
||||
}
|
||||
|
||||
enum PlayerState: Codable {
|
||||
|
|
|
@ -7,21 +7,17 @@
|
|||
|
||||
import Foundation
|
||||
import RealmSwift
|
||||
import Unrealm
|
||||
|
||||
class DeviceSettings: Object {
|
||||
@Persisted var disableAutoRewind: Bool
|
||||
@Persisted var enableAltView: Bool
|
||||
@Persisted var jumpBackwardsTime: Int
|
||||
@Persisted var jumpForwardTime: Int
|
||||
struct DeviceSettings: Realmable {
|
||||
var disableAutoRewind: Bool = false
|
||||
var enableAltView: Bool = false
|
||||
var jumpBackwardsTime: Int = 10
|
||||
var jumpForwardTime: Int = 10
|
||||
}
|
||||
|
||||
func getDefaultDeviceSettings() -> DeviceSettings {
|
||||
let settings = DeviceSettings()
|
||||
settings.disableAutoRewind = false
|
||||
settings.enableAltView = false
|
||||
settings.jumpForwardTime = 10
|
||||
settings.jumpBackwardsTime = 10
|
||||
return settings
|
||||
return DeviceSettings()
|
||||
}
|
||||
|
||||
func deviceSettingsToJSON(settings: DeviceSettings) -> Dictionary<String, Any> {
|
||||
|
|
|
@ -6,139 +6,81 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import RealmSwift
|
||||
import Unrealm
|
||||
|
||||
class LocalLibraryItem: Object, Encodable {
|
||||
@Persisted(primaryKey: true) var id: String = "local_\(UUID().uuidString)"
|
||||
@Persisted var basePath: String = ""
|
||||
@Persisted var absolutePath: String = ""
|
||||
@Persisted var contentUrl: String
|
||||
@Persisted var isInvalid: Bool = false
|
||||
@Persisted var mediaType: String
|
||||
@Persisted var media: LocalMediaType?
|
||||
@Persisted var localFiles: List<LocalFile>
|
||||
@Persisted var coverContentUrl: String? = nil
|
||||
@Persisted var coverAbsolutePath: String? = nil
|
||||
@Persisted var isLocal: Bool = true
|
||||
@Persisted var serverConnectionConfigId: String? = nil
|
||||
@Persisted var serverAddress: String? = nil
|
||||
@Persisted var serverUserId: String? = nil
|
||||
@Persisted var libraryItemId: String? = nil
|
||||
struct LocalLibraryItem: Realmable, Codable {
|
||||
var id: String = "local_\(UUID().uuidString)"
|
||||
var basePath: String = ""
|
||||
var absolutePath: String = ""
|
||||
var contentUrl: String = ""
|
||||
var isInvalid: Bool = false
|
||||
var mediaType: String = ""
|
||||
var media: MediaType?
|
||||
var localFiles: [LocalFile] = []
|
||||
var coverContentUrl: String?
|
||||
var coverAbsolutePath: String?
|
||||
var isLocal: Bool = true
|
||||
var serverConnectionConfigId: String?
|
||||
var serverAddress: String?
|
||||
var serverUserId: String?
|
||||
var libraryItemId: String?
|
||||
|
||||
static func primaryKey() -> String? {
|
||||
return "id"
|
||||
}
|
||||
}
|
||||
|
||||
class LocalMediaType: Object, Encodable {
|
||||
@Persisted var libraryItemId: String? = ""
|
||||
@Persisted var metadata: LocalMetadata?
|
||||
@Persisted var coverPath: String? = ""
|
||||
@Persisted var tags: List<String?>
|
||||
@Persisted var audioFiles: List<LocalAudioFile>
|
||||
@Persisted var chapters: List<LocalChapter>
|
||||
@Persisted var tracks: List<LocalAudioTrack>
|
||||
@Persisted var size: Int64? = nil
|
||||
@Persisted var duration: Double? = nil
|
||||
@Persisted var episodes: List<LocalPodcastEpisode>
|
||||
@Persisted var autoDownloadEpisodes: Bool? = nil
|
||||
struct LocalPodcastEpisode: Realmable, Codable {
|
||||
var id: String = UUID().uuidString
|
||||
var index: Int = 0
|
||||
var episode: String?
|
||||
var episodeType: String?
|
||||
var title: String = "Unknown"
|
||||
var subtitle: String?
|
||||
var desc: String?
|
||||
var audioFile: AudioFile?
|
||||
var audioTrack: AudioTrack?
|
||||
var duration: Double = 0
|
||||
var size: Int = 0
|
||||
var serverEpisodeId: String?
|
||||
|
||||
static func primaryKey() -> String? {
|
||||
return "id"
|
||||
}
|
||||
}
|
||||
|
||||
class LocalMetadata: Object, Encodable {
|
||||
@Persisted var title: String
|
||||
@Persisted var subtitle: String? = ""
|
||||
@Persisted var authors: List<LocalAuthor>
|
||||
@Persisted var narrators: List<String?>
|
||||
@Persisted var genres: List<String?>
|
||||
@Persisted var publishedYear: String? = ""
|
||||
@Persisted var publishedDate: String? = ""
|
||||
@Persisted var publisher: String? = ""
|
||||
@Persisted var desc: String? = ""
|
||||
@Persisted var isbn: String? = ""
|
||||
@Persisted var asin: String? = ""
|
||||
@Persisted var language: String? = ""
|
||||
@Persisted var explicit: Bool
|
||||
@Persisted var authorName: String? = ""
|
||||
@Persisted var authorNameLF: String? = ""
|
||||
@Persisted var narratorName: String? = ""
|
||||
@Persisted var seriesName: String? = ""
|
||||
@Persisted var feedUrl: String? = ""
|
||||
struct LocalFile: Realmable, Codable {
|
||||
var id: String = UUID().uuidString
|
||||
var filename: String?
|
||||
var contentUrl: String = ""
|
||||
var absolutePath: String = ""
|
||||
var mimeType: String?
|
||||
var size: Int = 0
|
||||
|
||||
static func primaryKey() -> String? {
|
||||
return "id"
|
||||
}
|
||||
}
|
||||
|
||||
class LocalPodcastEpisode: Object, Encodable {
|
||||
@Persisted var id: String = UUID().uuidString
|
||||
@Persisted var index: Int
|
||||
@Persisted var episode: String? = ""
|
||||
@Persisted var episodeType: String? = ""
|
||||
@Persisted var title: String
|
||||
@Persisted var subtitle: String? = ""
|
||||
@Persisted var desc: String? = ""
|
||||
@Persisted var audioFile: LocalAudioFile? = nil
|
||||
@Persisted var audioTrack: LocalAudioTrack? = nil
|
||||
@Persisted var duration: Double
|
||||
@Persisted var size: Int64
|
||||
@Persisted var serverEpisodeId: String?
|
||||
}
|
||||
|
||||
class LocalAudioFile: Object, Encodable {
|
||||
@Persisted var index: Int
|
||||
@Persisted var ino: String
|
||||
@Persisted var metadata: LocalFileMetadata?
|
||||
}
|
||||
|
||||
class LocalAuthor: Object, Encodable {
|
||||
@Persisted var id: String = UUID().uuidString
|
||||
@Persisted var name: String
|
||||
@Persisted var coverPath: String? = ""
|
||||
}
|
||||
|
||||
class LocalChapter: Object, Encodable {
|
||||
@Persisted var id: Int
|
||||
@Persisted var start: Double
|
||||
@Persisted var end: Double
|
||||
@Persisted var title: String? = nil
|
||||
}
|
||||
|
||||
class LocalAudioTrack: Object, Encodable {
|
||||
@Persisted var index: Int? = nil
|
||||
@Persisted var startOffset: Double? = nil
|
||||
@Persisted var duration: Double
|
||||
@Persisted var title: String? = ""
|
||||
@Persisted var contentUrl: String? = ""
|
||||
@Persisted var mimeType: String
|
||||
@Persisted var metadata: LocalFileMetadata? = nil
|
||||
@Persisted var isLocal: Bool = true
|
||||
@Persisted var localFileId: String? = ""
|
||||
@Persisted var serverIndex: Int? = nil
|
||||
}
|
||||
|
||||
class LocalFileMetadata: Object, Encodable {
|
||||
@Persisted var filename: String
|
||||
@Persisted var ext: String
|
||||
@Persisted var path: String
|
||||
@Persisted var relPath: String
|
||||
}
|
||||
|
||||
class LocalFile: Object, Encodable {
|
||||
@Persisted var id: String = UUID().uuidString
|
||||
@Persisted var filename: String? = ""
|
||||
@Persisted var contentUrl: String
|
||||
@Persisted var absolutePath: String
|
||||
@Persisted var mimeType: String? = ""
|
||||
@Persisted var size: Int64
|
||||
}
|
||||
|
||||
class LocalMediaProgress: Object, Encodable {
|
||||
@Persisted(primaryKey: true) var id: String = UUID().uuidString
|
||||
@Persisted var localLibraryItemId: String
|
||||
@Persisted var localEpisodeId: String? = ""
|
||||
@Persisted var duration: Double
|
||||
@Persisted var progress: Double // 0 to 1
|
||||
@Persisted var currentTime: Double
|
||||
@Persisted var isFinished: Bool
|
||||
@Persisted var lastUpdate: Int64
|
||||
@Persisted var startedAt: Int64
|
||||
@Persisted var finishedAt: Int64? = nil
|
||||
struct LocalMediaProgress: Realmable, Codable {
|
||||
var id: String = UUID().uuidString
|
||||
var localLibraryItemId: String = ""
|
||||
var localEpisodeId: String?
|
||||
var duration: Double = 0
|
||||
var progress: Double = 0
|
||||
var currentTime: Double = 0
|
||||
var isFinished: Bool = false
|
||||
var lastUpdate: Int = 0
|
||||
var startedAt: Int = 0
|
||||
var finishedAt: Int?
|
||||
// For local lib items from server to support server sync
|
||||
@Persisted var serverConnectionConfigId: String? = ""
|
||||
@Persisted var serverAddress: String? = ""
|
||||
@Persisted var serverUserId: String? = ""
|
||||
@Persisted var libraryItemId: String? = ""
|
||||
@Persisted var episodeId: String? = ""
|
||||
var serverConnectionConfigId: String?
|
||||
var serverAddress: String?
|
||||
var serverUserId: String?
|
||||
var libraryItemId: String?
|
||||
var episodeId: String?
|
||||
|
||||
static func primaryKey() -> String? {
|
||||
return "id"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,49 +8,12 @@
|
|||
import Foundation
|
||||
|
||||
extension LocalLibraryItem {
|
||||
enum CodingKeys: CodingKey {
|
||||
case id
|
||||
case basePath
|
||||
case absolutePath
|
||||
case contentUrl
|
||||
case isInvalid
|
||||
case mediaType
|
||||
case media
|
||||
case localFiles
|
||||
case coverContentUrl
|
||||
case coverAbsolutePath
|
||||
case isLocal
|
||||
case serverConnectionConfigId
|
||||
case serverAddress
|
||||
case serverUserId
|
||||
case libraryItemId
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(id, forKey: .id)
|
||||
try container.encode(basePath, forKey: .basePath)
|
||||
try container.encode(absolutePath, forKey: .absolutePath)
|
||||
try container.encode(contentUrl, forKey: .contentUrl)
|
||||
try container.encode(isInvalid, forKey: .isInvalid)
|
||||
try container.encode(mediaType, forKey: .mediaType)
|
||||
try container.encode(media, forKey: .media)
|
||||
try container.encode(localFiles, forKey: .localFiles)
|
||||
try container.encode(coverContentUrl, forKey: .coverContentUrl)
|
||||
try container.encode(coverAbsolutePath, forKey: .coverAbsolutePath)
|
||||
try container.encode(isLocal, forKey: .isLocal)
|
||||
try container.encode(serverConnectionConfigId, forKey: .serverConnectionConfigId)
|
||||
try container.encode(serverAddress, forKey: .serverAddress)
|
||||
try container.encode(serverUserId, forKey: .serverUserId)
|
||||
try container.encode(libraryItemId, forKey: .libraryItemId)
|
||||
}
|
||||
|
||||
convenience init(_ item: LibraryItem, localUrl: URL, server: ServerConnectionConfig, files: [LocalFile]) {
|
||||
init(_ item: LibraryItem, localUrl: URL, server: ServerConnectionConfig, files: [LocalFile]) {
|
||||
self.init()
|
||||
self.contentUrl = localUrl.absoluteString
|
||||
self.mediaType = item.mediaType
|
||||
self.media = LocalMediaType(item.media, coverPath: "", files: files)
|
||||
self.localFiles.append(objectsIn: files)
|
||||
self.media = item.media
|
||||
self.localFiles = files
|
||||
// TODO: self.coverContentURL
|
||||
// TODO: self.converAbsolutePath
|
||||
self.libraryItemId = item.id
|
||||
|
@ -61,7 +24,7 @@ extension LocalLibraryItem {
|
|||
|
||||
func getDuration() -> Double {
|
||||
var total = 0.0
|
||||
self.media?.tracks.forEach { track in total += track.duration }
|
||||
self.media?.tracks?.forEach { track in total += track.duration }
|
||||
return total
|
||||
}
|
||||
|
||||
|
@ -107,315 +70,14 @@ extension LocalLibraryItem {
|
|||
}
|
||||
}
|
||||
|
||||
extension LocalMediaType {
|
||||
enum CodingKeys: CodingKey {
|
||||
case libraryItemId
|
||||
case metadata
|
||||
case coverPath
|
||||
case tags
|
||||
case audioFiles
|
||||
case chapters
|
||||
case tracks
|
||||
case size
|
||||
case duration
|
||||
case episodes
|
||||
case autoDownloadEpisodes
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(libraryItemId, forKey: .libraryItemId)
|
||||
try container.encode(metadata, forKey: .metadata)
|
||||
try container.encode(coverPath, forKey: .coverPath)
|
||||
try container.encode(tags, forKey: .tags)
|
||||
try container.encode(audioFiles, forKey: .audioFiles)
|
||||
try container.encode(chapters, forKey: .chapters)
|
||||
try container.encode(tracks, forKey: .tracks)
|
||||
try container.encode(size, forKey: .size)
|
||||
try container.encode(duration, forKey: .duration)
|
||||
try container.encode(episodes, forKey: .episodes)
|
||||
try container.encode(autoDownloadEpisodes, forKey: .autoDownloadEpisodes)
|
||||
}
|
||||
|
||||
convenience init(_ mediaType: MediaType, coverPath: String, files: [LocalFile]) {
|
||||
self.init()
|
||||
self.libraryItemId = mediaType.libraryItemId
|
||||
self.metadata = LocalMetadata(mediaType.metadata)
|
||||
self.coverPath = coverPath
|
||||
self.tags.append(objectsIn: mediaType.tags ?? [])
|
||||
self.audioFiles.append(objectsIn: mediaType.audioFiles!.enumerated().map() {
|
||||
i, audioFile -> LocalAudioFile in LocalAudioFile(audioFile)
|
||||
})
|
||||
self.chapters.append(objectsIn: mediaType.chapters!.enumerated().map() {
|
||||
i, chapter -> LocalChapter in LocalChapter(chapter)
|
||||
})
|
||||
self.tracks.append(objectsIn: mediaType.tracks!.enumerated().map() {
|
||||
i, track in LocalAudioTrack(track, libraryItemId: self.libraryItemId ?? "", filename: files[i].filename ?? "")
|
||||
})
|
||||
self.size = mediaType.size
|
||||
self.duration = mediaType.duration
|
||||
// TODO: self.episodes
|
||||
// TODO: Handle podcast auto downloads
|
||||
self.autoDownloadEpisodes = mediaType.autoDownloadEpisodes
|
||||
}
|
||||
}
|
||||
|
||||
extension LocalMetadata {
|
||||
enum CodingKeys: CodingKey {
|
||||
case title
|
||||
case subtitle
|
||||
case authors
|
||||
case narrators
|
||||
case genres
|
||||
case publishedYear
|
||||
case publishedDate
|
||||
case publisher
|
||||
case desc
|
||||
case isbn
|
||||
case asin
|
||||
case language
|
||||
case explicit
|
||||
case authorName
|
||||
case authorNameLF
|
||||
case narratorName
|
||||
case seriesName
|
||||
case feedUrl
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(title, forKey: .title)
|
||||
try container.encode(subtitle, forKey: .subtitle)
|
||||
try container.encode(authors, forKey: .authors)
|
||||
try container.encode(narrators, forKey: .narrators)
|
||||
try container.encode(genres, forKey: .genres)
|
||||
try container.encode(publishedYear, forKey: .publishedYear)
|
||||
try container.encode(publishedDate, forKey: .publishedDate)
|
||||
try container.encode(publisher, forKey: .publisher)
|
||||
try container.encode(desc, forKey: .desc)
|
||||
try container.encode(isbn, forKey: .isbn)
|
||||
try container.encode(asin, forKey: .asin)
|
||||
try container.encode(language, forKey: .language)
|
||||
try container.encode(explicit, forKey: .explicit)
|
||||
try container.encode(authorName, forKey: .authorName)
|
||||
try container.encode(authorNameLF, forKey: .authorNameLF)
|
||||
try container.encode(narratorName, forKey: .narratorName)
|
||||
try container.encode(seriesName, forKey: .seriesName)
|
||||
try container.encode(feedUrl, forKey: .feedUrl)
|
||||
}
|
||||
|
||||
convenience init(_ metadata: Metadata) {
|
||||
self.init()
|
||||
self.title = metadata.title
|
||||
self.subtitle = metadata.subtitle
|
||||
self.narrators.append(objectsIn: metadata.narrators ?? [])
|
||||
self.genres.append(objectsIn: metadata.genres)
|
||||
self.publishedYear = metadata.publishedYear
|
||||
self.publishedDate = metadata.publishedDate
|
||||
self.publisher = metadata.publisher
|
||||
self.desc = metadata.description
|
||||
self.isbn = metadata.isbn
|
||||
self.asin = metadata.asin
|
||||
self.language = metadata.language
|
||||
self.explicit = metadata.explicit
|
||||
self.authorName = metadata.authorName
|
||||
self.authorNameLF = metadata.authorNameLF
|
||||
self.narratorName = metadata.narratorName
|
||||
self.seriesName = metadata.seriesName
|
||||
self.feedUrl = metadata.feedUrl
|
||||
}
|
||||
}
|
||||
|
||||
extension LocalPodcastEpisode {
|
||||
enum CodingKeys: CodingKey {
|
||||
case id
|
||||
case index
|
||||
case episode
|
||||
case episodeType
|
||||
case title
|
||||
case subtitle
|
||||
case desc
|
||||
case audioFile
|
||||
case audioTrack
|
||||
case duration
|
||||
case size
|
||||
case serverEpisodeId
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(id, forKey: .id)
|
||||
try container.encode(index, forKey: .index)
|
||||
try container.encode(episode, forKey: .episode)
|
||||
try container.encode(episodeType, forKey: .episodeType)
|
||||
try container.encode(title, forKey: .title)
|
||||
try container.encode(subtitle, forKey: .subtitle)
|
||||
try container.encode(desc, forKey: .desc)
|
||||
try container.encode(audioFile, forKey: .audioFile)
|
||||
try container.encode(audioTrack, forKey: .audioTrack)
|
||||
try container.encode(duration, forKey: .duration)
|
||||
try container.encode(size, forKey: .size)
|
||||
try container.encode(serverEpisodeId, forKey: .serverEpisodeId)
|
||||
}
|
||||
}
|
||||
|
||||
extension LocalAudioFile {
|
||||
enum CodingKeys: CodingKey {
|
||||
case index
|
||||
case ino
|
||||
case metadata
|
||||
}
|
||||
|
||||
convenience init(_ audioFile: AudioFile) {
|
||||
self.init()
|
||||
self.index = audioFile.index
|
||||
self.ino = audioFile.ino
|
||||
// self.metadata
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(index, forKey: .index)
|
||||
try container.encode(ino, forKey: .ino)
|
||||
try container.encode(metadata, forKey: .metadata)
|
||||
}
|
||||
}
|
||||
|
||||
extension LocalAuthor {
|
||||
enum CodingKeys: CodingKey {
|
||||
case id
|
||||
case name
|
||||
case coverPath
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(id, forKey: .id)
|
||||
try container.encode(name, forKey: .name)
|
||||
try container.encode(coverPath, forKey: .coverPath)
|
||||
}
|
||||
|
||||
convenience init(_ author: Author) {
|
||||
self.init()
|
||||
self.id = author.id
|
||||
self.name = author.name
|
||||
// self.coverPath
|
||||
}
|
||||
}
|
||||
|
||||
extension LocalChapter {
|
||||
enum CodingKeys: CodingKey {
|
||||
case id
|
||||
case start
|
||||
case end
|
||||
case title
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(id, forKey: .id)
|
||||
try container.encode(start, forKey: .start)
|
||||
try container.encode(end, forKey: .end)
|
||||
try container.encode(title, forKey: .title)
|
||||
}
|
||||
|
||||
convenience init(_ chapter: Chapter) {
|
||||
self.init()
|
||||
self.id = chapter.id
|
||||
self.start = chapter.start
|
||||
self.end = chapter.end
|
||||
self.title = chapter.title
|
||||
}
|
||||
}
|
||||
|
||||
extension LocalAudioTrack {
|
||||
enum CodingKeys: CodingKey {
|
||||
case index
|
||||
case startOffset
|
||||
case duration
|
||||
case title
|
||||
case contentUrl
|
||||
case mimeType
|
||||
case metadata
|
||||
case isLocal
|
||||
case localFileId
|
||||
case serverIndex
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(index, forKey: .index)
|
||||
try container.encode(startOffset, forKey: .startOffset)
|
||||
try container.encode(duration, forKey: .duration)
|
||||
try container.encode(title, forKey: .title)
|
||||
try container.encode(contentUrl, forKey: .contentUrl)
|
||||
try container.encode(mimeType, forKey: .mimeType)
|
||||
try container.encode(metadata, forKey: .metadata)
|
||||
try container.encode(isLocal, forKey: .isLocal)
|
||||
try container.encode(localFileId, forKey: .localFileId)
|
||||
try container.encode(serverIndex, forKey: .serverIndex)
|
||||
}
|
||||
|
||||
convenience init(_ track: AudioTrack, libraryItemId: String, filename: String) {
|
||||
self.init()
|
||||
self.index = track.index
|
||||
self.startOffset = track.startOffset
|
||||
self.duration = track.duration
|
||||
self.title = track.title
|
||||
self.contentUrl = "" // TODO: Different URL
|
||||
self.mimeType = track.mimeType
|
||||
// TODO: self.metadata
|
||||
self.localFileId = "\(libraryItemId)_\(filename.toBase64())"
|
||||
self.serverIndex = track.serverIndex
|
||||
}
|
||||
}
|
||||
|
||||
extension LocalFileMetadata {
|
||||
enum CodingKeys: CodingKey {
|
||||
case filename
|
||||
case ext
|
||||
case path
|
||||
case relPath
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(filename, forKey: .filename)
|
||||
try container.encode(ext, forKey: .ext)
|
||||
try container.encode(path, forKey: .path)
|
||||
try container.encode(relPath, forKey: .relPath)
|
||||
}
|
||||
|
||||
/* TODO: Can we skip this object? */
|
||||
}
|
||||
|
||||
extension LocalFile {
|
||||
enum CodingKeys: CodingKey {
|
||||
case id
|
||||
case filename
|
||||
case contentUrl
|
||||
case absolutePath
|
||||
case mimeType
|
||||
case size
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(id, forKey: .id)
|
||||
try container.encode(filename, forKey: .filename)
|
||||
try container.encode(contentUrl, forKey: .contentUrl)
|
||||
try container.encode(absolutePath, forKey: .absolutePath)
|
||||
try container.encode(mimeType, forKey: .mimeType)
|
||||
try container.encode(size, forKey: .size)
|
||||
}
|
||||
|
||||
convenience init(_ libraryItemId: String, _ filename: String, _ mimeType: String, _ localUrl: URL) {
|
||||
init(_ libraryItemId: String, _ filename: String, _ mimeType: String, _ localUrl: URL) {
|
||||
self.init()
|
||||
self.id = "\(libraryItemId)_\(filename.toBase64())"
|
||||
self.filename = filename
|
||||
self.contentUrl = localUrl.absoluteString
|
||||
self.absolutePath = localUrl.path
|
||||
self.size = localUrl.fileSize
|
||||
self.size = Int(localUrl.fileSize)
|
||||
}
|
||||
|
||||
func isAudioFile() -> Bool {
|
||||
|
@ -428,42 +90,3 @@ extension LocalFile {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension LocalMediaProgress {
|
||||
enum CodingKeys: CodingKey {
|
||||
case id
|
||||
case localLibraryItemId
|
||||
case localEpisodeId
|
||||
case duration
|
||||
case progress
|
||||
case currentTime
|
||||
case isFinished
|
||||
case lastUpdate
|
||||
case startedAt
|
||||
case finishedAt
|
||||
case serverConnectionConfigId
|
||||
case serverAddress
|
||||
case serverUserId
|
||||
case libraryItemId
|
||||
case episodeId
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(id, forKey: .id)
|
||||
try container.encode(localLibraryItemId, forKey: .localLibraryItemId)
|
||||
try container.encode(localEpisodeId, forKey: .localEpisodeId)
|
||||
try container.encode(duration, forKey: .duration)
|
||||
try container.encode(progress, forKey: .progress)
|
||||
try container.encode(currentTime, forKey: .currentTime)
|
||||
try container.encode(isFinished, forKey: .isFinished)
|
||||
try container.encode(lastUpdate, forKey: .lastUpdate)
|
||||
try container.encode(startedAt, forKey: .startedAt)
|
||||
try container.encode(finishedAt, forKey: .finishedAt)
|
||||
try container.encode(serverConnectionConfigId, forKey: .serverConnectionConfigId)
|
||||
try container.encode(serverAddress, forKey: .serverAddress)
|
||||
try container.encode(serverUserId, forKey: .serverUserId)
|
||||
try container.encode(libraryItemId, forKey: .libraryItemId)
|
||||
try container.encode(episodeId, forKey: .episodeId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,20 +7,29 @@
|
|||
|
||||
import Foundation
|
||||
import RealmSwift
|
||||
import Unrealm
|
||||
|
||||
class ServerConnectionConfig: Object {
|
||||
@Persisted(primaryKey: true) var id: String
|
||||
@Persisted(indexed: true) var index: Int
|
||||
@Persisted var name: String
|
||||
@Persisted var address: String
|
||||
@Persisted var userId: String
|
||||
@Persisted var username: String
|
||||
@Persisted var token: String
|
||||
struct ServerConnectionConfig: Realmable {
|
||||
var id: String = UUID().uuidString
|
||||
var index: Int = 0
|
||||
var name: String = ""
|
||||
var address: String = ""
|
||||
var userId: String = ""
|
||||
var username: String = ""
|
||||
var token: String = ""
|
||||
|
||||
static func primaryKey() -> String? {
|
||||
return "id"
|
||||
}
|
||||
class ServerConnectionConfigActiveIndex: Object {
|
||||
// This could overflow, but you really would have to try
|
||||
@Persisted var index: Int?
|
||||
|
||||
static func indexedProperties() -> [String] {
|
||||
return ["index"]
|
||||
}
|
||||
}
|
||||
|
||||
struct ServerConnectionConfigActiveIndex: Realmable {
|
||||
// This could overflow, but you really would have to try
|
||||
var index: Int?
|
||||
}
|
||||
|
||||
func convertServerConnectionConfigToJSON(config: ServerConnectionConfig) -> Dictionary<String, Any> {
|
||||
|
|
|
@ -114,9 +114,11 @@ class ApiClient {
|
|||
callback(session)
|
||||
}
|
||||
}
|
||||
|
||||
public static func reportPlaybackProgress(report: PlaybackReport, sessionId: String) {
|
||||
try? postResource(endpoint: "api/session/\(sessionId)/sync", parameters: report.asDictionary().mapValues({ value in "\(value)" }), callback: nil)
|
||||
}
|
||||
|
||||
public static func getLibraryItemWithProgress(libraryItemId:String, episodeId:String?, callback: @escaping (_ param: LibraryItem?) -> Void) {
|
||||
var endpoint = "api/items/\(libraryItemId)?expanded=1&include=progress"
|
||||
if episodeId != nil {
|
||||
|
|
|
@ -23,16 +23,12 @@ class Database {
|
|||
}
|
||||
|
||||
public func setServerConnectionConfig(config: ServerConnectionConfig) {
|
||||
var refrence: ThreadSafeReference<ServerConnectionConfig>?
|
||||
if config.realm != nil {
|
||||
refrence = ThreadSafeReference(to: config)
|
||||
}
|
||||
|
||||
var config = config
|
||||
Database.realmQueue.sync {
|
||||
let existing: ServerConnectionConfig? = instance.object(ofType: ServerConnectionConfig.self, forPrimaryKey: config.id)
|
||||
var existing: ServerConnectionConfig? = instance.object(ofType: ServerConnectionConfig.self, forPrimaryKey: config.id)
|
||||
|
||||
if config.index == 0 {
|
||||
let lastConfig: ServerConnectionConfig? = instance.objects(ServerConnectionConfig.self).last
|
||||
var lastConfig: ServerConnectionConfig? = instance.objects(ServerConnectionConfig.self).last
|
||||
|
||||
if lastConfig != nil {
|
||||
config.index = lastConfig!.index + 1
|
||||
|
@ -46,15 +42,7 @@ class Database {
|
|||
if existing != nil {
|
||||
instance.delete(existing!)
|
||||
}
|
||||
if refrence == nil {
|
||||
instance.add(config)
|
||||
} else {
|
||||
guard let resolved = instance.resolve(refrence!) else {
|
||||
throw "unable to resolve refrence"
|
||||
}
|
||||
|
||||
instance.add(resolved);
|
||||
}
|
||||
}
|
||||
} catch(let exception) {
|
||||
NSLog("failed to save server config")
|
||||
|
@ -83,25 +71,8 @@ class Database {
|
|||
}
|
||||
|
||||
public func getServerConnectionConfigs() -> [ServerConnectionConfig] {
|
||||
var refrences: [ThreadSafeReference<ServerConnectionConfig>] = []
|
||||
|
||||
Database.realmQueue.sync {
|
||||
let configs = instance.objects(ServerConnectionConfig.self)
|
||||
refrences = configs.map { config in
|
||||
return ThreadSafeReference(to: config)
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
let realm = try Realm()
|
||||
|
||||
return refrences.map { refrence in
|
||||
return realm.resolve(refrence)!
|
||||
}
|
||||
} catch(let exception) {
|
||||
NSLog("error while readling configs")
|
||||
debugPrint(exception)
|
||||
return []
|
||||
return Array(instance.objects(ServerConnectionConfig.self))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,7 +84,7 @@ class Database {
|
|||
|
||||
public func setLastActiveConfigIndex(index: Int?) {
|
||||
let existing = instance.objects(ServerConnectionConfigActiveIndex.self)
|
||||
let obj = ServerConnectionConfigActiveIndex()
|
||||
var obj = ServerConnectionConfigActiveIndex()
|
||||
obj.index = index
|
||||
|
||||
do {
|
||||
|
@ -149,25 +120,8 @@ class Database {
|
|||
}
|
||||
|
||||
public func getLocalLibraryItems(mediaType: MediaType? = nil) -> [LocalLibraryItem] {
|
||||
var localLibraryItems: [ThreadSafeReference<LocalLibraryItem>] = []
|
||||
|
||||
Database.realmQueue.sync {
|
||||
let items = instance.objects(LocalLibraryItem.self)
|
||||
localLibraryItems = items.map { item in
|
||||
return ThreadSafeReference(to: item)
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
let realm = try Realm()
|
||||
|
||||
return localLibraryItems.map { item in
|
||||
return realm.resolve(item)!
|
||||
}
|
||||
} catch(let exception) {
|
||||
NSLog("error while readling local library items")
|
||||
debugPrint(exception)
|
||||
return []
|
||||
Array(instance.objects(LocalLibraryItem.self))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,26 +137,14 @@ class Database {
|
|||
}
|
||||
|
||||
public func getLocalLibraryItem(localLibraryItem: String) -> LocalLibraryItem? {
|
||||
let items = getLocalLibraryItems()
|
||||
for item in items {
|
||||
if (item.id == localLibraryItem) {
|
||||
return item
|
||||
Database.realmQueue.sync {
|
||||
instance.object(ofType: LocalLibraryItem.self, forPrimaryKey: localLibraryItem)
|
||||
}
|
||||
}
|
||||
NSLog("Local library item with id \(localLibraryItem) not found")
|
||||
return nil
|
||||
}
|
||||
|
||||
public func saveLocalLibraryItem(localLibraryItem: LocalLibraryItem) {
|
||||
Database.realmQueue.sync {
|
||||
do {
|
||||
try instance.write {
|
||||
instance.add(localLibraryItem);
|
||||
}
|
||||
} catch(let exception) {
|
||||
NSLog("Unable to save local library item")
|
||||
debugPrint(exception)
|
||||
}
|
||||
try! instance.write { instance.add(localLibraryItem) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,19 +155,10 @@ class Database {
|
|||
}
|
||||
|
||||
public func removeLocalLibraryItem(localLibraryItemId: String) {
|
||||
let item = getLocalLibraryItemByLLId(libraryItem: localLibraryItemId)
|
||||
Database.realmQueue.sync {
|
||||
do {
|
||||
try instance.write {
|
||||
if item != nil {
|
||||
try! instance.write {
|
||||
let item = getLocalLibraryItemByLLId(libraryItem: localLibraryItemId)
|
||||
instance.delete(item!)
|
||||
} else {
|
||||
NSLog("Unable to find local library item to delete")
|
||||
}
|
||||
}
|
||||
} catch (let exception) {
|
||||
NSLog("Unable to delete local library item")
|
||||
debugPrint(exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import Foundation
|
|||
import RealmSwift
|
||||
|
||||
class Store {
|
||||
@ThreadSafe private static var _serverConfig: ServerConnectionConfig?
|
||||
private static var _serverConfig: ServerConnectionConfig?
|
||||
public static var serverConfig: ServerConnectionConfig? {
|
||||
get {
|
||||
return _serverConfig
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue