Skip to content

Commit

Permalink
CacheStorage setup
Browse files Browse the repository at this point in the history
  • Loading branch information
asendra committed Nov 29, 2024
1 parent c14cd75 commit c236d9e
Show file tree
Hide file tree
Showing 11 changed files with 122 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,28 @@ final class GRDBCacheStorage {

init(dbQueue: DatabaseQueue) {
self.dbQueue = dbQueue
try? initializeDatabase()
}

static func initializeDatabase(dbQueue: DatabaseQueue) throws {
try dbQueue.write { db in
if try !db.tableExists(CacheEntryGRDBEntity.databaseTableName) {
try db.create(table: CacheEntryGRDBEntity.databaseTableName) { t in
t.column("key", .text).primaryKey()
t.column("type", .text).notNull()
t.column("url", .text).notNull()
t.column("lastAccessedAt", .datetime).notNull()
t.column("size", .integer).notNull()
}
}
}
}

static func withDefaultDatabase() throws -> GRDBCacheStorage {
let databaseURL = try GRDBCacheStorage.databaseURL()
let dbQueue = try DatabaseQueue(path: databaseURL.path)
try GRDBCacheStorage.initializeDatabase(dbQueue: dbQueue)

return GRDBCacheStorage(dbQueue: dbQueue)
}
}

Expand Down Expand Up @@ -100,23 +121,14 @@ extension GRDBCacheStorage: CacheStorage {
}

private extension GRDBCacheStorage {
func initializeDatabase() throws {
do {
try dbQueue.write { db in
if try !db.tableExists(CacheEntryGRDBEntity.databaseTableName) {
try db.create(table: CacheEntryGRDBEntity.databaseTableName) { t in
t.column("key", .text).primaryKey()
t.column("type", .text).notNull()
t.column("url", .text).notNull()
t.column("lastAccessedAt", .datetime).notNull()
t.column("size", .integer).notNull()
}
}
}
} catch {
// TODO: Log error
print("Failed to initialize table \(CacheEntryGRDBEntity.databaseTableName): \(error)")
throw error
}
static func databaseURL() throws -> URL {
let appSupportURL = PlayerWorld.fileManagerClient.applicationSupportDirectory()
let directoryURL = appSupportURL.appendingPathComponent("PlayerCacheDatabase", isDirectory: true)
try PlayerWorld.fileManagerClient.createDirectory(
at: directoryURL,
withIntermediateDirectories: true,
attributes: [FileAttributeKey.protectionKey: URLFileProtection.none]
)
return directoryURL.appendingPathComponent("db.sqlite")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ typealias MainPlayerType = GenericMediaPlayer & LiveMediaPlayer & UCMediaPlayer
final class InternalPlayerLoader: PlayerLoader {
private let configuration: Configuration
private let fairPlayLicenseFetcher: FairPlayLicenseFetcher

private let credentialsProvider: CredentialsProvider

private let featureFlagProvider: FeatureFlagProvider
private let cacheStorage: CacheStorage?

let mainPlayer: MainPlayerType
var players: [GenericMediaPlayer] = []
Expand All @@ -29,20 +28,22 @@ final class InternalPlayerLoader: PlayerLoader {

required init(
with configuration: Configuration,
and fairplayLicenseFetcher: FairPlayLicenseFetcher,
and fairPlayLicenseFetcher: FairPlayLicenseFetcher,
featureFlagProvider: FeatureFlagProvider,
credentialsProvider: CredentialsProvider,
mainPlayer: MainPlayerType.Type,
externalPlayers: [GenericMediaPlayer.Type]
externalPlayers: [GenericMediaPlayer.Type],
cacheStorage: CacheStorage?
) {
self.configuration = configuration
fairPlayLicenseFetcher = fairplayLicenseFetcher
self.fairPlayLicenseFetcher = fairPlayLicenseFetcher
self.credentialsProvider = credentialsProvider
self.featureFlagProvider = featureFlagProvider
self.cacheStorage = cacheStorage

let fileManager = PlayerWorld.fileManagerClient
let cachePath = PlayerWorld.fileManagerClient.cachesDirectory()
self.mainPlayer = mainPlayer.init(
cachePath: fileManager.cachesDirectory(),
cachePath: cachePath,
featureFlagProvider: featureFlagProvider
)

Expand All @@ -51,7 +52,7 @@ final class InternalPlayerLoader: PlayerLoader {
externalPlayers.forEach { externalPlayerType in
registerPlayer(
externalPlayerType.init(
cachePath: fileManager.cachesDirectory(),
cachePath: cachePath,
featureFlagProvider: featureFlagProvider
)
)
Expand Down
3 changes: 2 additions & 1 deletion Sources/Player/PlaybackEngine/Internal/PlayerLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ protocol PlayerLoader: AnyObject {
featureFlagProvider: FeatureFlagProvider,
credentialsProvider: CredentialsProvider,
mainPlayer: MainPlayerType.Type,
externalPlayers: [GenericMediaPlayer.Type]
externalPlayers: [GenericMediaPlayer.Type],
cacheStorage: CacheStorage?
)

func load(_ playableStorageMediaProduct: PlayableOfflinedMediaProduct) async throws -> Asset
Expand Down
3 changes: 2 additions & 1 deletion Sources/Player/PlaybackEngine/PlayerEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ final class PlayerEngine {
_ configuration: Configuration,
_ playerEventSender: PlayerEventSender,
_ networkMonitor: NetworkMonitor,
_ offlineStorage: OfflineStorage?,
_ offlineStorage: OfflineStorage,
_ cacheStorage: CacheStorage?,
_ offlinePlaybackPrivilegeCheck: (() -> Bool)?,
_ playerLoader: PlayerLoader,
_ featureFlagProvider: FeatureFlagProvider,
Expand Down
27 changes: 25 additions & 2 deletions Sources/Player/Player.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ public final class Player {
@Atomic
private(set) var offlineStorage: OfflineStorage

@Atomic
private(set) var cacheStorage: CacheStorage?

@Atomic
private(set) var offlineEngine: OfflineEngine

Expand Down Expand Up @@ -64,6 +67,7 @@ public final class Player {
urlSession: URLSession,
configuration: Configuration,
offlineStorage: OfflineStorage,
cacheStorage: CacheStorage?,
djProducer: DJProducer,
playerEventSender: PlayerEventSender,
fairplayLicenseFetcher: FairPlayLicenseFetcher,
Expand All @@ -81,6 +85,7 @@ public final class Player {
playerURLSession = urlSession
self.configuration = configuration
self.offlineStorage = offlineStorage
self.cacheStorage = cacheStorage
self.djProducer = djProducer
self.fairplayLicenseFetcher = fairplayLicenseFetcher
self.streamingPrivilegesHandler = streamingPrivilegesHandler
Expand Down Expand Up @@ -141,6 +146,14 @@ public extension Player {
return nil
}

let cacheStorage: CacheStorage
do {
cacheStorage = try Player.initializedCacheStorage()
} catch {
PlayerWorld.logger?.log(loggable: PlayerLoggable.withDefaultDatabase(error: error))
return nil
}

Time.initialise()

let timeoutPolicy = TimeoutPolicy.standard
Expand Down Expand Up @@ -204,6 +217,7 @@ public extension Player {
sharedPlayerURLSession,
configuration,
offlineStorage,
cacheStorage,
djProducer,
fairplayLicenseFetcher,
networkMonitor,
Expand All @@ -220,6 +234,7 @@ public extension Player {
urlSession: sharedPlayerURLSession,
configuration: configuration,
offlineStorage: offlineStorage,
cacheStorage: cacheStorage,
djProducer: djProducer,
playerEventSender: playerEventSender,
fairplayLicenseFetcher: fairplayLicenseFetcher,
Expand Down Expand Up @@ -413,6 +428,7 @@ private extension Player {
playerURLSession,
configuration,
offlineStorage,
cacheStorage,
djProducer,
fairplayLicenseFetcher,
networkMonitor,
Expand All @@ -428,7 +444,8 @@ private extension Player {
static func newPlayerEngine(
_ urlSession: URLSession,
_ configuration: Configuration,
_ offlineStorage: OfflineStorage?,
_ offlineStorage: OfflineStorage,
_ cacheStorage: CacheStorage?,
_ djProducer: DJProducer,
_ fairplayLicenseFetcher: FairPlayLicenseFetcher,
_ networkMonitor: NetworkMonitor,
Expand All @@ -445,7 +462,8 @@ private extension Player {
featureFlagProvider: featureFlagProvider,
credentialsProvider: credentialsProvider,
mainPlayer: Player.mainPlayerType(),
externalPlayers: externalPlayersSupplier?() ?? []
externalPlayers: externalPlayersSupplier?() ?? [],
cacheStorage: cacheStorage
)

let playerInstance = PlayerEngine(
Expand All @@ -458,6 +476,7 @@ private extension Player {
playerEventSender,
networkMonitor,
offlineStorage,
cacheStorage,
offlinePlaybackPrivilegeCheck,
internalPlayerLoader,
featureFlagProvider,
Expand All @@ -471,6 +490,10 @@ private extension Player {
try GRDBOfflineStorage.withDefaultDatabase()
}

static func initializedCacheStorage() throws -> CacheStorage {
try GRDBCacheStorage.withDefaultDatabase()
}

static func newOfflineEngine(
_ storage: OfflineStorage,
_ configuration: Configuration,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Foundation
@testable import Player

final class CacheStorageMock: CacheStorage {
func save(_ entry: CacheEntry) throws {}

func get(key: String) throws -> CacheEntry? {
nil
}

func delete(key: String) throws {}

func update(_ entry: CacheEntry) throws {}

func getAll() throws -> [CacheEntry] {
[]
}

func totalSize() throws -> Int {
0
}

func pruneToSize(_ maxSize: Int) throws {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,14 @@ final class PlayerLoaderMock: PlayerLoader {
featureFlagProvider: FeatureFlagProvider = .mock,
credentialsProvider: CredentialsProvider = CredentialsProviderMock(),
mainPlayer: MainPlayerType.Type = PlayerMock.self,
externalPlayers: [GenericMediaPlayer.Type] = []
externalPlayers: [GenericMediaPlayer.Type] = [],
cacheStorage: CacheStorage? = nil
) {}

convenience init() {
self.init(
with: Configuration.mock(),
and: FairPlayLicenseFetcher.mock(),
featureFlagProvider: .mock,
externalPlayers: []
and: FairPlayLicenseFetcher.mock()
)
}

Expand Down
6 changes: 4 additions & 2 deletions Tests/PlayerTests/Mocks/Player+Mock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ extension PlayerEngine {
configuration: Configuration = Configuration.mock(),
playerEventSender: PlayerEventSender = PlayerEventSenderMock(),
networkMonitor: NetworkMonitor = NetworkMonitorMock(),
storage: OfflineStorage = OfflineStorageMock(),
offlineStorage: OfflineStorage = OfflineStorageMock(),
cacheStorage: CacheStorage? = CacheStorageMock(),
playerLoader: PlayerLoader = PlayerLoaderMock(),
featureFlagProvider: FeatureFlagProvider = .mock,
notificationsHandler: NotificationsHandler? = .mock(queue: DispatchQueue(label: "com.tidal.queue.for.testing"))
Expand All @@ -30,7 +31,8 @@ extension PlayerEngine {
configuration,
playerEventSender,
networkMonitor,
storage,
offlineStorage,
cacheStorage,
nil,
playerLoader,
featureFlagProvider,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ final class InternalPlayerLoaderTests: XCTestCase {
featureFlagProvider: FeatureFlagProvider.mock,
credentialsProvider: CredentialsProviderMock(),
mainPlayer: PlayerMock.self,
externalPlayers: []
externalPlayers: [],
cacheStorage: CacheStorageMock()
)
mockPlayer = internalLoader.getMainPlayerInstance() as? PlayerMock
}
Expand Down
16 changes: 11 additions & 5 deletions Tests/PlayerTests/Player/PlayerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import XCTest
final class PlayerTests: XCTestCase {
private var player: Player!
private var playerEventSender: PlayerEventSenderMock!
private var dbQueue: DatabaseQueue!
private var offlineDbQueue: DatabaseQueue!
private var cachedDQueue: DatabaseQueue!

override func setUpWithError() throws {
PlayerWorld = PlayerWorldClient.mock(developmentFeatureFlagProvider: DevelopmentFeatureFlagProvider.mock)
Expand All @@ -33,8 +34,12 @@ final class PlayerTests: XCTestCase {
dataWriter: dataWriter
)

dbQueue = try DatabaseQueue()
let storage = GRDBOfflineStorage(dbQueue: dbQueue)
offlineDbQueue = try DatabaseQueue()
let offlineStorage = GRDBOfflineStorage(dbQueue: offlineDbQueue)

cachedDQueue = try DatabaseQueue()
let cacheStorage = GRDBCacheStorage(dbQueue: cachedDQueue)

let fairplayLicenseFetcher = FairPlayLicenseFetcher.mock()
let networkMonitor = NetworkMonitorMock()

Expand All @@ -51,7 +56,7 @@ final class PlayerTests: XCTestCase {

let notificationsHandler = NotificationsHandler.mock()
let offlineEngine = OfflineEngine.mock(
storage: storage,
storage: offlineStorage,
playerEventSender: playerEventSender,
notificationsHandler: notificationsHandler
)
Expand All @@ -61,7 +66,8 @@ final class PlayerTests: XCTestCase {
queue: OperationQueueMock(),
urlSession: urlSession,
configuration: configuration,
offlineStorage: storage,
offlineStorage: offlineStorage,
cacheStorage: cacheStorage,
djProducer: djProducer,
playerEventSender: playerEventSender,
fairplayLicenseFetcher: fairplayLicenseFetcher,
Expand Down
13 changes: 9 additions & 4 deletions Tests/PlayerTests/Playlog/PlayLogTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ final class PlayLogTests: XCTestCase {
private var fairplayLicenseFetcher: FairPlayLicenseFetcher!
private var djProducer: DJProducer!
private var playerLoader: InternalPlayerLoader!
private var storage: OfflineStorage!
private var offlineStorage: OfflineStorage!
private var cacheStorage: CacheStorage!
private var networkMonitor: NetworkMonitorMock!
private var configuration: Configuration!
private var notificationsHandler: NotificationsHandler!
Expand Down Expand Up @@ -118,7 +119,9 @@ final class PlayLogTests: XCTestCase {
featureFlagProvider: featureFlagProvider
)

storage = OfflineStorageMock()
offlineStorage = OfflineStorageMock()
cacheStorage = CacheStorageMock()

fairplayLicenseFetcher = FairPlayLicenseFetcher.mock(
httpClient: HttpClient(using: urlSession),
credentialsProvider: credentialsProvider,
Expand Down Expand Up @@ -1508,7 +1511,8 @@ private extension PlayLogTests {
featureFlagProvider: featureFlagProvider,
credentialsProvider: credentialsProvider,
mainPlayer: Player.mainPlayerType(),
externalPlayers: []
externalPlayers: [],
cacheStorage: cacheStorage
)

let operationQueue = OperationQueue()
Expand All @@ -1525,7 +1529,8 @@ private extension PlayLogTests {
configuration: configuration,
playerEventSender: playerEventSender,
networkMonitor: networkMonitor,
storage: storage,
offlineStorage: offlineStorage,
cacheStorage: cacheStorage,
playerLoader: playerLoader,
featureFlagProvider: featureFlagProvider,
notificationsHandler: notificationsHandler
Expand Down

0 comments on commit c236d9e

Please sign in to comment.