Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Player] Unified caching (2/X): Unified CacheStorage setup #175

Open
wants to merge 2 commits into
base: alberto/remove-legacy-player-impl
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -39,7 +39,7 @@ final class GRDBCacheStorageTests: XCTestCase {
override func setUpWithError() throws {
// Create an in-memory database for testing
dbQueue = try DatabaseQueue()

try GRDBCacheStorage.initializeDatabase(dbQueue: dbQueue)
cacheStorage = GRDBCacheStorage(dbQueue: dbQueue)
}

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 cachedDbQueue: 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)

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

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
Loading
Loading