feat(*): subsonic client with avfoundation playback and apple music ui
This commit is contained in:
commit
e5bb559d7f
93 files changed
+7517
No files matched your search
@@ -0,0 +1,75 @@
|
||||
import Domain
|
||||
import Foundation
|
||||
import Synchronization
|
||||
|
||||
public final class FakeSource: LibrarySource, Starring, LyricsSource {
|
||||
public let id: SourceID
|
||||
public let name: String
|
||||
private let catalog: FakeCatalog
|
||||
private let artworkDirectory: URL
|
||||
private struct Extras {
|
||||
var changes: [Change] = []
|
||||
var artwork: [String: URL] = [:]
|
||||
var lyrics: [String: URL] = [:]
|
||||
}
|
||||
|
||||
private let extras = Mutex(Extras())
|
||||
|
||||
public init(id: SourceID = SourceID(), name: String = "Demo Library") {
|
||||
self.id = id
|
||||
self.name = name
|
||||
catalog = FakeCatalog(source: id)
|
||||
artworkDirectory = FileManager.default.temporaryDirectory.appending(path: "FakeSourceArtwork", directoryHint: .isDirectory)
|
||||
try? FileManager.default.createDirectory(at: artworkDirectory, withIntermediateDirectories: true)
|
||||
}
|
||||
|
||||
public var sampleTracks: [Track] {
|
||||
catalog.changes.compactMap {
|
||||
if case let .upsert(.track(track)) = $0 {
|
||||
return track
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public func add(_ changes: [Change], artworkFiles: [String: URL], lyricsFiles: [String: URL] = [:]) {
|
||||
extras.withLock {
|
||||
$0.changes += changes
|
||||
$0.artwork.merge(artworkFiles) { $1 }
|
||||
$0.lyrics.merge(lyricsFiles) { $1 }
|
||||
}
|
||||
}
|
||||
|
||||
public func lyrics(for track: EntityID) async throws -> Lyrics? {
|
||||
guard let file = extras.withLock({ $0.lyrics[track.remote] }) else { return nil }
|
||||
return try Lyrics(lrc: String(contentsOf: file, encoding: .utf8))
|
||||
}
|
||||
|
||||
public func changes(since cursor: Cursor?) -> AsyncThrowingStream<Change, Error> {
|
||||
let changes = cursor == nil ? catalog.changes + extras.withLock(\.changes) : []
|
||||
return AsyncThrowingStream { continuation in
|
||||
changes.forEach { continuation.yield($0) }
|
||||
continuation.yield(.checkpoint(Cursor("1")))
|
||||
continuation.finish()
|
||||
}
|
||||
}
|
||||
|
||||
public func stream(for _: EntityID) async throws -> StreamRequest {
|
||||
throw SourceError.notFound
|
||||
}
|
||||
|
||||
public func artwork(_ id: EntityID, pixelSize: Int) async throws -> ResourceRequest {
|
||||
if let file = extras.withLock({ $0.artwork[id.remote] }) {
|
||||
return ResourceRequest(url: file)
|
||||
}
|
||||
let side = pixelSize <= 320 ? 320 : 1024
|
||||
let file = artworkDirectory.appending(path: "\(id.remote)-\(side).png")
|
||||
if !FileManager.default.fileExists(atPath: file.path) {
|
||||
guard let data = FakeArtwork.png(seed: id.remote, side: side) else { throw SourceError.notFound }
|
||||
try data.write(to: file, options: .atomic)
|
||||
}
|
||||
return ResourceRequest(url: file)
|
||||
}
|
||||
|
||||
public func setFavorite(_: Bool, for _: EntityID, kind _: EntityKind) async throws {}
|
||||
}
|
||||
Reference in new issue
Block a user