feat: iOS download groundwork

This commit is contained in:
benonymity 2022-07-06 10:09:17 -04:00
parent e07e7f70d6
commit 2ca9ce797d
6 changed files with 368 additions and 120 deletions

View file

@ -35,6 +35,7 @@
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; }; 504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; };
50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; }; 50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };
A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; }; A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; };
C4B265F5285A5A6600E1B5C3 /* LocalLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B265F4285A5A6600E1B5C3 /* LocalLibrary.swift */; };
C4D0677528106D0C00B8F875 /* DataClasses.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D0677428106D0C00B8F875 /* DataClasses.swift */; }; C4D0677528106D0C00B8F875 /* DataClasses.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D0677428106D0C00B8F875 /* DataClasses.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@ -71,6 +72,7 @@
50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; }; 50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; };
AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; }; AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; }; AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; };
C4B265F4285A5A6600E1B5C3 /* LocalLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalLibrary.swift; sourceTree = "<group>"; };
C4D0677428106D0C00B8F875 /* DataClasses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataClasses.swift; sourceTree = "<group>"; }; C4D0677428106D0C00B8F875 /* DataClasses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataClasses.swift; sourceTree = "<group>"; };
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; }; FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@ -135,6 +137,7 @@
3AD4FCE828043FD7006DB301 /* ServerConnectionConfig.swift */, 3AD4FCE828043FD7006DB301 /* ServerConnectionConfig.swift */,
3ABF580828059BAE005DFBE5 /* PlaybackSession.swift */, 3ABF580828059BAE005DFBE5 /* PlaybackSession.swift */,
C4D0677428106D0C00B8F875 /* DataClasses.swift */, C4D0677428106D0C00B8F875 /* DataClasses.swift */,
C4B265F4285A5A6600E1B5C3 /* LocalLibrary.swift */,
3A90295E280968E700E1D427 /* PlaybackReport.swift */, 3A90295E280968E700E1D427 /* PlaybackReport.swift */,
4DF74911287105C600AC7814 /* DeviceSettings.swift */, 4DF74911287105C600AC7814 /* DeviceSettings.swift */,
); );
@ -315,6 +318,7 @@
3AD4FCE728043E72006DB301 /* AbsDatabase.m in Sources */, 3AD4FCE728043E72006DB301 /* AbsDatabase.m in Sources */,
504EC3081FED79650016851F /* AppDelegate.swift in Sources */, 504EC3081FED79650016851F /* AppDelegate.swift in Sources */,
3A90295F280968E700E1D427 /* PlaybackReport.swift in Sources */, 3A90295F280968E700E1D427 /* PlaybackReport.swift in Sources */,
C4B265F5285A5A6600E1B5C3 /* LocalLibrary.swift in Sources */,
3ABF580928059BAE005DFBE5 /* PlaybackSession.swift in Sources */, 3ABF580928059BAE005DFBE5 /* PlaybackSession.swift in Sources */,
3ABF618F2804325C0070250E /* PlayerHandler.swift in Sources */, 3ABF618F2804325C0070250E /* PlayerHandler.swift in Sources */,
3AD4FCED28044E6C006DB301 /* Store.swift in Sources */, 3AD4FCED28044E6C006DB301 /* Store.swift in Sources */,

View file

@ -14,27 +14,33 @@ public class AbsDownloader: CAPPlugin {
let libraryItemId = call.getString("libraryItemId") let libraryItemId = call.getString("libraryItemId")
let episodeId = call.getString("episodeId") let episodeId = call.getString("episodeId")
NSLog("Download library item \(libraryItemId ?? "N/A") episode \(episodeId ?? "")") NSLog("Download library item \(libraryItemId ?? "N/A") / episode \(episodeId ?? "")")
ApiClient.getLibraryItemWithProgress(libraryItemId: libraryItemId!, episodeId: episodeId) { libraryItem in ApiClient.getLibraryItemWithProgress(libraryItemId: libraryItemId!, episodeId: episodeId) { libraryItem in
if (libraryItem == nil) { if (libraryItem == nil) {
NSLog("Library item not found") NSLog("Library item not found")
call.resolve() call.resolve()
} else { } else {
NSLog("Got library item \(libraryItem!)") NSLog("Got library item from server \(libraryItem!.id)")
// TODO: break out in seperate functions self.startLibraryItemDownload(libraryItem: libraryItem!)
libraryItem!.media.tracks?.forEach { track in call.resolve()
}
}
}
func startLibraryItemDownload(libraryItem: LibraryItem) {
let length = libraryItem.media.tracks.count
if length > 0 {
libraryItem.media.tracks.enumerated().forEach { position, track in
NSLog("TRACK \(track.contentUrl!)") NSLog("TRACK \(track.contentUrl!)")
// filename needs to be encoded otherwise would just use contentUrl // filename needs to be encoded otherwise would just use contentUrl
let filename = track.metadata?.filename ?? "" let filename = track.metadata?.filename ?? ""
let filenameEncoded = filename.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed) let filenameEncoded = filename.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
let urlstr = "\(Store.serverConfig!.address)/s/item/\(libraryItemId!)/\(filenameEncoded ?? "")?token=\(Store.serverConfig!.token)" let urlstr = "\(Store.serverConfig!.address)/s/item/\(libraryItem.id)/\(filenameEncoded ?? "")?token=\(Store.serverConfig!.token)"
let url = URL(string: urlstr)! let url = URL(string: urlstr)!
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let itemDirectory = documentsDirectory.appendingPathComponent("\(libraryItemId!)") let itemDirectory = documentsDirectory.appendingPathComponent("\(libraryItem.id)")
NSLog("ITEM DIR \(itemDirectory)") NSLog("ITEM DIR \(itemDirectory)")
// Create library item directory // Create library item directory
@ -53,8 +59,8 @@ public class AbsDownloader: CAPPlugin {
do { do {
NSLog("Download TMP file URL \(fileURL)") NSLog("Download TMP file URL \(fileURL)")
let imageData = try Data(contentsOf:fileURL) let audioData = try Data(contentsOf:fileURL)
try imageData.write(to: trackFilename) try audioData.write(to: trackFilename)
NSLog("Download written to \(trackFilename)") NSLog("Download written to \(trackFilename)")
} catch { } catch {
NSLog("FILE ERROR: \(error)") NSLog("FILE ERROR: \(error)")
@ -62,10 +68,17 @@ public class AbsDownloader: CAPPlugin {
} }
downloadTask.resume() downloadTask.resume()
} }
} else {
NSLog("No audio tracks for the supplied library item")
call.resolve() }
} // let encoder = JSONEncoder()
// let jsobj = try encoder.encode(Download)
// notifyListeners("onItemDownloadComplete", data: jsobj)
} }
} }
struct DownloadItem: Codable {
var isDownloading = false
var progress: Float = 0
var resumeData: Data?
// var task: URLSessionDownloadTask?
} }

View file

@ -2,11 +2,12 @@
// DataClasses.swift // DataClasses.swift
// App // App
// //
// Created by Benonymity on 4/20/22. // Created by benonymity on 4/20/22.
// //
import Foundation import Foundation
import CoreMedia import CoreMedia
import RealmSwift
struct LibraryItem: Codable { struct LibraryItem: Codable {
var id: String var id: String
@ -30,87 +31,88 @@ struct LibraryItem: Codable {
var libraryFiles: [LibraryFile] var libraryFiles: [LibraryFile]
var userMediaProgress:MediaProgress? var userMediaProgress:MediaProgress?
} }
struct MediaType: Codable { class MediaType: Object, Codable {
var libraryItemId: String? var libraryItemId: String? = ""
var metadata: Metadata var metadata: Metadata?
var coverPath: String? var coverPath: String? = ""
var tags: [String]? var tags: List<String?>
var audioFiles: [AudioTrack]? var audioFiles: List<AudioFile>
var chapters: [Chapter]? var chapters: List<Chapter>
var tracks: [AudioTrack]? var tracks: List<AudioTrack>
var size: Int64? var size: Int64? = nil
var duration: Double? var duration: Double? = nil
var episodes: [PodcastEpisode]? var episodes: List<PodcastEpisode>
var autoDownloadEpisodes: Bool? var autoDownloadEpisodes: Bool? = nil
} }
struct Metadata: Codable { class Metadata: Object, Codable {
var title: String var title: String
var subtitle: String? var subtitle: String? = ""
var authors: [Author]? var authors: List<Author>
var narrators: [String]? var narrators: List<String?>
var genres: [String] var genres: List<String?>
var publishedYear: String? var publishedYear: String? = ""
var publishedDate: String? var publishedDate: String? = ""
var publisher: String? var publisher: String? = ""
var description: String? // I think calling the below variable description conflicts with some public variables declared in some of the Pods we use, so it's desc. ¯\_()_/¯
var isbn: String? final var description: String
var asin: String? var isbn: String? = ""
var language: String? var asin: String? = ""
var language: String? = ""
var explicit: Bool var explicit: Bool
var authorName: String? var authorName: String? = ""
var authorNameLF: String? var authorNameLF: String? = ""
var narratorName: String? var narratorName: String? = ""
var seriesName: String? var seriesName: String? = ""
var feedUrl: String? var feedUrl: String? = ""
} }
struct PodcastEpisode: Codable { class PodcastEpisode: Object, Codable {
var id: String var id: String
var index: Int var index: Int
var episode: String? var episode: String? = ""
var episodeType: String? var episodeType: String? = ""
var title: String var title: String
var subtitle: String? var subtitle: String? = ""
var description: String? var escription: String? = ""
var audioFile: AudioFile? var audioFile: AudioFile? = nil
var audioTrack: AudioTrack? var audioTrack: AudioTrack? = nil
var duration: Double var duration: Double
var size: Int64 var size: Int64
// var serverEpisodeId: String? // var serverEpisodeId: String?
} }
struct AudioFile: Codable { class AudioFile: Object, Codable {
var index: Int @Persisted var index: Int
var ino: String @Persisted var ino: String
var metadata: FileMetadata @Persisted var metadata: FileMetadata?
} }
struct Author: Codable { class Author: Object, Codable {
var id: String @Persisted var id: String
var name: String @Persisted var name: String
var coverPath: String? @Persisted var coverPath: String? = ""
} }
struct Chapter: Codable { class Chapter: Object, Codable {
var id: Int @Persisted var id: Int
var start: Double @Persisted var start: Double
var end: Double @Persisted var end: Double
var title: String? @Persisted var title: String? = nil
} }
struct AudioTrack: Codable { struct AudioTrack: Codable {
var index: Int? var index: Int? = nil
var startOffset: Double? var startOffset: Double? = nil
var duration: Double var duration: Double
var title: String? var title: String? = ""
var contentUrl: String? var contentUrl: String? = ""
var mimeType: String var mimeType: String
var metadata: FileMetadata? var metadata: FileMetadata? = nil
// var isLocal: Bool var isLocal: Bool
// var localFileId: String? var localFileId: String? = ""
// var audioProbeResult: AudioProbeResult? Needed for local playback // var audioProbeResult: AudioProbeResult? // Needed for local playback. Requires local FFMPEG? Not sure how doable this is on iOS
var serverIndex: Int? var serverIndex: Int? = nil
} }
struct FileMetadata: Codable { class FileMetadata: Object, Codable {
var filename: String @Persisted var filename: String
var ext: String @Persisted var ext: String
var path: String @Persisted var path: String
var relPath: String @Persisted var relPath: String
} }
struct Library: Codable { struct Library: Codable {
var id: String var id: String
@ -125,7 +127,7 @@ struct Folder: Codable {
} }
struct LibraryFile: Codable { struct LibraryFile: Codable {
var ino: String var ino: String
var metadata: FileMetadata var metadata: FileMetadata?
} }
struct MediaProgress: Codable { struct MediaProgress: Codable {
var id: String var id: String
@ -139,3 +141,14 @@ struct MediaProgress:Codable {
var startedAt: Int64 var startedAt: Int64
var finishedAt: Int64? var finishedAt: Int64?
} }
struct PlaybackMetadata: Codable {
var duration: Double
var currentTime: Double
var playerState: PlayerState
}
enum PlayerState: Codable {
case IDLE
case BUFFERING
case READY
case ENDED
}

View file

@ -0,0 +1,153 @@
//
// LocalLibrary.swift
// App
//
// Created by benonymity on 6/15/22.
//
import Foundation
import RealmSwift
class LocalLibraryItem: Object, Codable {
@Persisted(primaryKey: true) var id: String
@Persisted var basePath: String
@Persisted var absolutePath: String
@Persisted var contentUrl: String
@Persisted var isInvalid: Bool
@Persisted var mediaType: String
@Persisted var media: MediaType?
@Persisted var localFiles: List<LocalFile>
@Persisted var coverContentUrl: String? = nil
@Persisted var coverAbsolutePath: String? = nil
@Persisted var isLocal: Bool
@Persisted var serverConnectionConfigId: String? = nil
@Persisted var serverAddress: String? = nil
@Persisted var serverUserId: String? = nil
@Persisted var libraryItemId: String? = nil
}
class LocalMediaItem: Object, Codable {
@Persisted var id: String
@Persisted var name: String
@Persisted var mediaType: String
@Persisted var folderId: String
@Persisted var contentUrl: String
@Persisted var simplePath: String
@Persisted var basePath: String
@Persisted var absolutePath: String
@Persisted var audioTracks: List<AudioTrack>
@Persisted var localFiles: List<LocalFile>
@Persisted var coverContentUrl: String? = ""
@Persisted var coverAbsolutePath: String? = ""
}
class MediaType: Object, Codable {
@Persisted var libraryItemId: String? = ""
@Persisted var metadata: Metadata?
@Persisted var coverPath: String? = ""
@Persisted var tags: List<String?>
@Persisted var audioFiles: List<AudioFile>
@Persisted var chapters: List<Chapter>
@Persisted var tracks: List<AudioTrack>
@Persisted var size: Int64? = nil
@Persisted var duration: Double? = nil
@Persisted var episodes: List<PodcastEpisode>
@Persisted var autoDownloadEpisodes: Bool? = nil
}
class Metadata: Object, Codable {
@Persisted var title: String
@Persisted var subtitle: String? = ""
@Persisted var authors: List<Author>
@Persisted var narrators: List<String?>
@Persisted var genres: List<String?>
@Persisted var publishedYear: String? = ""
@Persisted var publishedDate: String? = ""
@Persisted var publisher: String? = ""
// I think calling the below variable description conflicts with some public variables declared in some of the Pods we use, so it's desc. ¯\_()_/¯
@Persisted final var description: 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? = ""
}
class PodcastEpisode: Object, Codable {
@Persisted var id: String
@Persisted var index: Int
@Persisted var episode: String? = ""
@Persisted var episodeType: String? = ""
@Persisted var title: String
@Persisted var subtitle: String? = ""
@Persisted var escription: String? = ""
@Persisted var audioFile: AudioFile? = nil
@Persisted var audioTrack: AudioTrack? = nil
@Persisted var duration: Double
@Persisted var size: Int64
// @Persisted var serverEpisodeId: String?
}
class AudioFile: Object, Codable {
@Persisted var index: Int
@Persisted var ino: String
@Persisted var metadata: FileMetadata?
}
class Author: Object, Codable {
@Persisted var id: String
@Persisted var name: String
@Persisted var coverPath: String? = ""
}
class Chapter: Object, Codable {
@Persisted var id: Int
@Persisted var start: Double
@Persisted var end: Double
@Persisted var title: String? = nil
}
class AudioTrack: Object, Codable {
@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: FileMetadata? = nil
@Persisted var isLocal: Bool
@Persisted var localFileId: String? = ""
// var audioProbeResult: AudioProbeResult? // Needed for local playback. Requires local FFMPEG? Not sure how doable this is on iOS
@Persisted var serverIndex: Int? = nil
}
class FileMetadata: Object, Codable {
@Persisted var filename: String
@Persisted var ext: String
@Persisted var path: String
@Persisted var relPath: String
}
class LocalFile: Object, Codable {
@Persisted var id: String
@Persisted var filename: String? = ""
@Persisted var contentUrl: String
@Persisted var basePath: String
@Persisted var absolutePath: String
@Persisted var simplePath: String
@Persisted var mimeType: String? = ""
@Persisted var size: Int64
}
class LocalMediaProgress: Object, Codable {
@Persisted var id: String
@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
// 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? = ""
}

View file

@ -64,6 +64,7 @@ class Database {
setLastActiveConfigIndex(index: config.index) setLastActiveConfigIndex(index: config.index)
} }
} }
public func deleteServerConnectionConfig(id: String) { public func deleteServerConnectionConfig(id: String) {
Database.realmQueue.sync { Database.realmQueue.sync {
let config = instance.object(ofType: ServerConnectionConfig.self, forPrimaryKey: id) let config = instance.object(ofType: ServerConnectionConfig.self, forPrimaryKey: id)
@ -80,6 +81,7 @@ class Database {
} }
} }
} }
public func getServerConnectionConfigs() -> [ServerConnectionConfig] { public func getServerConnectionConfigs() -> [ServerConnectionConfig] {
var refrences: [ThreadSafeReference<ServerConnectionConfig>] = [] var refrences: [ThreadSafeReference<ServerConnectionConfig>] = []
@ -108,6 +110,7 @@ class Database {
setLastActiveConfigIndex(index: nil) setLastActiveConfigIndex(index: nil)
} }
} }
public func setLastActiveConfigIndex(index: Int?) { public func setLastActiveConfigIndex(index: Int?) {
let existing = instance.objects(ServerConnectionConfigActiveIndex.self) let existing = instance.objects(ServerConnectionConfigActiveIndex.self)
let obj = ServerConnectionConfigActiveIndex() let obj = ServerConnectionConfigActiveIndex()
@ -123,6 +126,7 @@ class Database {
debugPrint(exception) debugPrint(exception)
} }
} }
public func getLastActiveConfigIndex() -> Int? { public func getLastActiveConfigIndex() -> Int? {
return Database.realmQueue.sync { return Database.realmQueue.sync {
return instance.objects(ServerConnectionConfigActiveIndex.self).first?.index ?? nil return instance.objects(ServerConnectionConfigActiveIndex.self).first?.index ?? nil
@ -139,6 +143,49 @@ class Database {
} }
} catch(let exception) { } catch(let exception) {
NSLog("failed to save device settings") NSLog("failed to save device settings")
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 []
}
}
public func getLocalLibraryItemByLLId(libraryItem: String) -> LocalLibraryItem? {
let items = getLocalLibraryItems()
for item in items {
if (item.id == libraryItem) {
return item
}
}
NSLog("Local library item with id \(libraryItem) 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) debugPrint(exception)
} }
} }
@ -148,4 +195,22 @@ class Database {
return instance.objects(DeviceSettings.self).first ?? getDefaultDeviceSettings() return instance.objects(DeviceSettings.self).first ?? getDefaultDeviceSettings()
} }
} }
public func removeLocalLibraryItem(localLibraryItemId: String) {
let item = getLocalLibraryItemByLLId(libraryItem: localLibraryItemId)
Database.realmQueue.sync {
do {
try instance.write {
if item != nil {
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)
}
}
}
} }

View file

@ -307,7 +307,7 @@ export default {
return this.ebookFile && this.ebookFormat !== 'pdf' return this.ebookFile && this.ebookFormat !== 'pdf'
}, },
showDownload() { showDownload() {
if (this.isIos) return false // if (this.isIos) return false
return this.user && this.userCanDownload && this.showPlay && !this.hasLocal return this.user && this.userCanDownload && this.showPlay && !this.hasLocal
}, },
ebookFile() { ebookFile() {