Implement Factory (#587)
This commit is contained in:
parent
fb38394a43
commit
3ffb67a400
|
@ -8,6 +8,7 @@
|
|||
|
||||
import Combine
|
||||
import Defaults
|
||||
import Factory
|
||||
import Foundation
|
||||
import Nuke
|
||||
import Stinsen
|
||||
|
@ -15,6 +16,10 @@ import SwiftUI
|
|||
import WidgetKit
|
||||
|
||||
final class MainCoordinator: NavigationCoordinatable {
|
||||
|
||||
@Injected(LogManager.service)
|
||||
private var logger
|
||||
|
||||
var stack: NavigationStack<MainCoordinator>
|
||||
|
||||
@Root
|
||||
|
@ -60,13 +65,13 @@ final class MainCoordinator: NavigationCoordinatable {
|
|||
|
||||
@objc
|
||||
func didSignIn() {
|
||||
LogManager.log.info("Received `didSignIn` from SwiftfinNotificationCenter.")
|
||||
logger.info("Received `didSignIn` from SwiftfinNotificationCenter.")
|
||||
root(\.mainTab)
|
||||
}
|
||||
|
||||
@objc
|
||||
func didSignOut() {
|
||||
LogManager.log.info("Received `didSignOut` from SwiftfinNotificationCenter.")
|
||||
logger.info("Received `didSignOut` from SwiftfinNotificationCenter.")
|
||||
root(\.serverList)
|
||||
}
|
||||
|
||||
|
|
|
@ -6,12 +6,17 @@
|
|||
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Factory
|
||||
import Foundation
|
||||
import Nuke
|
||||
import Stinsen
|
||||
import SwiftUI
|
||||
|
||||
final class MainCoordinator: NavigationCoordinatable {
|
||||
|
||||
@Injected(LogManager.service)
|
||||
private var logger
|
||||
|
||||
var stack = NavigationStack<MainCoordinator>(initial: \MainCoordinator.mainTab)
|
||||
|
||||
@Root
|
||||
|
@ -40,13 +45,13 @@ final class MainCoordinator: NavigationCoordinatable {
|
|||
|
||||
@objc
|
||||
func didSignIn() {
|
||||
LogManager.log.info("Received `didSignIn` from NSNotificationCenter.")
|
||||
logger.info("Received `didSignIn` from NSNotificationCenter.")
|
||||
root(\.mainTab)
|
||||
}
|
||||
|
||||
@objc
|
||||
func didSignOut() {
|
||||
LogManager.log.info("Received `didSignOut` from NSNotificationCenter.")
|
||||
logger.info("Received `didSignOut` from NSNotificationCenter.")
|
||||
root(\.serverList)
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import UIKit
|
|||
extension BaseItemDto {
|
||||
func createVideoPlayerViewModel() -> AnyPublisher<[VideoPlayerViewModel], Error> {
|
||||
|
||||
LogManager.log.debug("Creating video player view model for item: \(id ?? "")")
|
||||
LogManager.service().debug("Creating video player view model for item: \(id ?? "")")
|
||||
|
||||
let builder = DeviceProfileBuilder()
|
||||
// TODO: fix bitrate settings
|
||||
|
@ -182,7 +182,7 @@ extension BaseItemDto {
|
|||
|
||||
func createLiveTVVideoPlayerViewModel() -> AnyPublisher<[VideoPlayerViewModel], Error> {
|
||||
|
||||
LogManager.log.debug("Creating liveTV video player view model for item: \(id ?? "")")
|
||||
LogManager.service().debug("Creating liveTV video player view model for item: \(id ?? "")")
|
||||
|
||||
let builder = DeviceProfileBuilder()
|
||||
// TODO: fix bitrate settings
|
||||
|
|
|
@ -22,4 +22,8 @@ public extension URL {
|
|||
|
||||
return items
|
||||
}
|
||||
|
||||
static var documents: URL {
|
||||
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,15 @@
|
|||
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Factory
|
||||
import Foundation
|
||||
import UDPBroadcast
|
||||
|
||||
public class ServerDiscovery {
|
||||
|
||||
@Injected(LogManager.service)
|
||||
private var logger
|
||||
|
||||
public struct ServerLookupResponse: Codable, Hashable, Identifiable {
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
|
@ -56,7 +61,7 @@ public class ServerDiscovery {
|
|||
func receiveHandler(_ ipAddress: String, _ port: Int, _ data: Data) {
|
||||
do {
|
||||
let response = try JSONDecoder().decode(ServerLookupResponse.self, from: data)
|
||||
LogManager.log.debug("Received JellyfinServer from \"\(response.name)\"", tag: "ServerDiscovery")
|
||||
logger.debug("Received JellyfinServer from \"\(response.name)\"", tag: "ServerDiscovery")
|
||||
completion(response)
|
||||
} catch {
|
||||
completion(nil)
|
||||
|
@ -64,15 +69,15 @@ public class ServerDiscovery {
|
|||
}
|
||||
|
||||
func errorHandler(error: UDPBroadcastConnection.ConnectionError) {
|
||||
LogManager.log.error("Error handling response: \(error.localizedDescription)", tag: "ServerDiscovery")
|
||||
logger.error("Error handling response: \(error.localizedDescription)", tag: "ServerDiscovery")
|
||||
}
|
||||
|
||||
do {
|
||||
self.connection = try! UDPBroadcastConnection(port: 7359, handler: receiveHandler, errorHandler: errorHandler)
|
||||
try self.connection?.sendBroadcast("Who is JellyfinServer?")
|
||||
LogManager.log.debug("Discovery broadcast sent", tag: "ServerDiscovery")
|
||||
logger.debug("Discovery broadcast sent", tag: "ServerDiscovery")
|
||||
} catch {
|
||||
LogManager.log.error("Error sending discovery broadcast", tag: "ServerDiscovery")
|
||||
logger.error("Error sending discovery broadcast", tag: "ServerDiscovery")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,16 +6,40 @@
|
|||
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Factory
|
||||
import Foundation
|
||||
import Puppy
|
||||
|
||||
class LogManager {
|
||||
|
||||
static let log = Puppy()
|
||||
static let service = Factory<Puppy>(scope: .singleton) {
|
||||
Puppy.swiftfinInstance()
|
||||
}
|
||||
|
||||
static func setup() {
|
||||
// static let log = Puppy()
|
||||
}
|
||||
|
||||
let logsDirectory = getDocumentsDirectory().appendingPathComponent("logs", isDirectory: true)
|
||||
class LogFormatter: LogFormattable {
|
||||
func formatMessage(
|
||||
_ level: LogLevel,
|
||||
message: String,
|
||||
tag: String,
|
||||
function: String,
|
||||
file: String,
|
||||
line: UInt,
|
||||
swiftLogInfo: [String: String],
|
||||
label: String,
|
||||
date: Date,
|
||||
threadID: UInt64
|
||||
) -> String {
|
||||
let file = shortFileName(file).replacingOccurrences(of: ".swift", with: "")
|
||||
return " [\(level.emoji) \(level)] \(file)#\(line):\(function) \(message)"
|
||||
}
|
||||
}
|
||||
|
||||
private extension Puppy {
|
||||
static func swiftfinInstance() -> Puppy {
|
||||
let logsDirectory = URL.documents.appendingPathComponent("logs", isDirectory: true)
|
||||
|
||||
do {
|
||||
try FileManager.default.createDirectory(
|
||||
|
@ -38,33 +62,9 @@ class LogManager {
|
|||
let consoleLogger = ConsoleLogger("org.jellyfin.swiftfin.logger.console")
|
||||
consoleLogger.format = LogFormatter()
|
||||
|
||||
log.add(fileRotationLogger, withLevel: .debug)
|
||||
log.add(consoleLogger, withLevel: .debug)
|
||||
}
|
||||
|
||||
private static func getDocumentsDirectory() -> URL {
|
||||
// find all possible documents directories for this user
|
||||
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
|
||||
|
||||
// just send back the first one, which ought to be the only one
|
||||
return paths[0]
|
||||
}
|
||||
}
|
||||
|
||||
class LogFormatter: LogFormattable {
|
||||
func formatMessage(
|
||||
_ level: LogLevel,
|
||||
message: String,
|
||||
tag: String,
|
||||
function: String,
|
||||
file: String,
|
||||
line: UInt,
|
||||
swiftLogInfo: [String: String],
|
||||
label: String,
|
||||
date: Date,
|
||||
threadID: UInt64
|
||||
) -> String {
|
||||
let file = shortFileName(file).replacingOccurrences(of: ".swift", with: "")
|
||||
return " [\(level.emoji) \(level)] \(file)#\(line):\(function) \(message)"
|
||||
let logger = Puppy()
|
||||
logger.add(fileRotationLogger, withLevel: .debug)
|
||||
logger.add(consoleLogger, withLevel: .debug)
|
||||
return logger
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,14 @@
|
|||
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Factory
|
||||
import Foundation
|
||||
|
||||
class SwiftfinNotification {
|
||||
|
||||
@Injected(Notifications.service)
|
||||
private var notificationService
|
||||
|
||||
private let notificationName: Notification.Name
|
||||
|
||||
fileprivate init(_ notificationName: Notification.Name) {
|
||||
|
@ -17,23 +21,21 @@ class SwiftfinNotification {
|
|||
}
|
||||
|
||||
func post(object: Any? = nil) {
|
||||
Notifications.main.post(name: notificationName, object: object)
|
||||
notificationService.post(name: notificationName, object: object)
|
||||
}
|
||||
|
||||
func subscribe(_ observer: Any, selector: Selector) {
|
||||
Notifications.main.addObserver(observer, selector: selector, name: notificationName, object: nil)
|
||||
notificationService.addObserver(observer, selector: selector, name: notificationName, object: nil)
|
||||
}
|
||||
|
||||
func unsubscribe(_ observer: Any) {
|
||||
Notifications.main.removeObserver(self, name: notificationName, object: nil)
|
||||
notificationService.removeObserver(self, name: notificationName, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
enum Notifications {
|
||||
|
||||
static let main: NotificationCenter = {
|
||||
NotificationCenter()
|
||||
}()
|
||||
static let service = Factory(scope: .singleton) { NotificationCenter() }
|
||||
|
||||
final class Key {
|
||||
public typealias NotificationKey = Notifications.Key
|
||||
|
@ -50,10 +52,6 @@ enum Notifications {
|
|||
static subscript(key: Key) -> SwiftfinNotification {
|
||||
key.underlyingNotification
|
||||
}
|
||||
|
||||
static func unsubscribe(_ observer: Any) {
|
||||
main.removeObserver(observer)
|
||||
}
|
||||
}
|
||||
|
||||
extension Notifications.Key {
|
||||
|
@ -63,8 +61,5 @@ extension Notifications.Key {
|
|||
static let processDeepLink = NotificationKey("processDeepLink")
|
||||
static let didPurge = NotificationKey("didPurge")
|
||||
static let didChangeServerCurrentURI = NotificationKey("didChangeCurrentLoginURI")
|
||||
static let toggleOfflineMode = NotificationKey("toggleOfflineMode")
|
||||
static let didDeleteOfflineItem = NotificationKey("didDeleteOfflineItem")
|
||||
static let didAddDownload = NotificationKey("didAddDownload")
|
||||
static let didSendStopReport = NotificationKey("didSendStopReport")
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import Combine
|
||||
import Factory
|
||||
import Foundation
|
||||
import JellyfinAPI
|
||||
import Stinsen
|
||||
|
@ -49,7 +50,7 @@ final class ConnectToServerViewModel: ViewModel {
|
|||
let uri = uri.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
.trimmingCharacters(in: .objectReplacement)
|
||||
|
||||
LogManager.log.debug("Attempting to connect to server at \"\(uri)\"", tag: "connectToServer")
|
||||
logger.debug("Attempting to connect to server at \"\(uri)\"", tag: "connectToServer")
|
||||
SessionManager.main.connectToServer(with: uri)
|
||||
.trackActivity(loading)
|
||||
.sink(receiveCompletion: { completion in
|
||||
|
@ -92,7 +93,7 @@ final class ConnectToServerViewModel: ViewModel {
|
|||
}
|
||||
}
|
||||
}, receiveValue: { server in
|
||||
LogManager.log.debug("Connected to server at \"\(uri)\"", tag: "connectToServer")
|
||||
self.logger.debug("Connected to server at \"\(uri)\"", tag: "connectToServer")
|
||||
self.router?.route(to: \.userSignIn, server)
|
||||
})
|
||||
.store(in: &cancellables)
|
||||
|
|
|
@ -59,7 +59,7 @@ final class HomeViewModel: ViewModel {
|
|||
|
||||
@objc
|
||||
func refresh() {
|
||||
LogManager.log.debug("Refresh called.")
|
||||
logger.debug("Refresh called.")
|
||||
|
||||
refreshLibrariesLatest()
|
||||
refreshLatestAddedItems()
|
||||
|
@ -85,7 +85,7 @@ final class HomeViewModel: ViewModel {
|
|||
var newLibraries: [BaseItemDto] = []
|
||||
|
||||
response.items!.forEach { item in
|
||||
LogManager.log
|
||||
self.logger
|
||||
.debug("Retrieved user view: \(item.id!) (\(item.name ?? "nil")) with type \(item.collectionType ?? "nil")")
|
||||
if item.collectionType == "movies" || item.collectionType == "tvshows" {
|
||||
newLibraries.append(item)
|
||||
|
@ -165,7 +165,7 @@ final class HomeViewModel: ViewModel {
|
|||
self.handleAPIRequestError(completion: completion)
|
||||
}
|
||||
}, receiveValue: { response in
|
||||
LogManager.log.debug("Retrieved \(String(response.items!.count)) resume items")
|
||||
self.logger.debug("Retrieved \(String(response.items!.count)) resume items")
|
||||
|
||||
self.resumeItems = response.items ?? []
|
||||
})
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
//
|
||||
|
||||
import Combine
|
||||
import Factory
|
||||
import Foundation
|
||||
import JellyfinAPI
|
||||
import UIKit
|
||||
|
@ -65,7 +66,7 @@ class ItemViewModel: ViewModel {
|
|||
} else {
|
||||
// Remove if necessary. Note that this cannot be in deinit as
|
||||
// holding as an observer won't allow the object to be deinit-ed
|
||||
Notifications.unsubscribe(self)
|
||||
Notifications[.didSendStopReport].unsubscribe(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ final class SeasonItemViewModel: ItemViewModel, EpisodesRowManager {
|
|||
}
|
||||
|
||||
private func requestEpisodes() {
|
||||
LogManager.log
|
||||
logger
|
||||
.debug("Getting episodes in season \(item.id!) (\(item.name!)) of show \(item.seriesId!) (\(item.seriesName!))")
|
||||
TvShowsAPI.getEpisodes(
|
||||
seriesId: item.seriesId ?? "",
|
||||
|
@ -78,7 +78,7 @@ final class SeasonItemViewModel: ItemViewModel, EpisodesRowManager {
|
|||
!episode.unaired && !episode.missing && episode.seasonId ?? "" == self.item.id!
|
||||
}) {
|
||||
self.playButtonItem = nextUpItem
|
||||
LogManager.log.debug("Nextup in season \(self.item.id!) (\(self.item.name!)): \(nextUpItem.id!)")
|
||||
self.logger.debug("Nextup in season \(self.item.id!) (\(self.item.name!)): \(nextUpItem.id!)")
|
||||
}
|
||||
|
||||
// if self.playButtonItem == nil && !self.episodes.isEmpty {
|
||||
|
|
|
@ -49,7 +49,7 @@ final class SeriesItemViewModel: ItemViewModel, EpisodesRowManager {
|
|||
}
|
||||
|
||||
private func getNextUp() {
|
||||
LogManager.log.debug("Getting next up for show \(self.item.id!) (\(self.item.name!))")
|
||||
logger.debug("Getting next up for show \(self.item.id!) (\(self.item.name!))")
|
||||
TvShowsAPI.getNextUp(
|
||||
userId: SessionManager.main.currentLogin.user.id,
|
||||
seriesId: self.item.id!,
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Factory
|
||||
import Foundation
|
||||
import JellyfinAPI
|
||||
import SwiftUICollection
|
||||
|
@ -69,7 +70,7 @@ final class LiveTVChannelsViewModel: ViewModel {
|
|||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.handleAPIRequestError(completion: completion)
|
||||
}, receiveValue: { [weak self] _ in
|
||||
LogManager.log.debug("Received Guide Info")
|
||||
self?.logger.debug("Received Guide Info")
|
||||
guard let self = self else { return }
|
||||
self.getChannels()
|
||||
})
|
||||
|
@ -89,7 +90,7 @@ final class LiveTVChannelsViewModel: ViewModel {
|
|||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.handleAPIRequestError(completion: completion)
|
||||
}, receiveValue: { [weak self] response in
|
||||
LogManager.log.debug("Received \(response.items?.count ?? 0) Channels")
|
||||
self?.logger.debug("Received \(response.items?.count ?? 0) Channels")
|
||||
guard let self = self else { return }
|
||||
self.channels = response.items ?? []
|
||||
self.getPrograms()
|
||||
|
@ -100,7 +101,7 @@ final class LiveTVChannelsViewModel: ViewModel {
|
|||
private func getPrograms() {
|
||||
// http://192.168.1.50:8096/LiveTv/Programs
|
||||
guard !channels.isEmpty else {
|
||||
LogManager.log.debug("Cannot get programs, channels list empty. ")
|
||||
logger.debug("Cannot get programs, channels list empty. ")
|
||||
return
|
||||
}
|
||||
let channelIds = channels.compactMap(\.id)
|
||||
|
@ -126,7 +127,7 @@ final class LiveTVChannelsViewModel: ViewModel {
|
|||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.handleAPIRequestError(completion: completion)
|
||||
}, receiveValue: { [weak self] response in
|
||||
LogManager.log.debug("Received \(response.items?.count ?? 0) Programs")
|
||||
self?.logger.debug("Received \(response.items?.count ?? 0) Programs")
|
||||
guard let self = self else { return }
|
||||
self.programs = response.items ?? []
|
||||
self.channelPrograms = self.processChannelPrograms()
|
||||
|
@ -178,7 +179,7 @@ final class LiveTVChannelsViewModel: ViewModel {
|
|||
}
|
||||
timer = Timer(fire: nextMinute, interval: 60 * 10, repeats: true) { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
LogManager.log.debug("LiveTVChannels schedule check...")
|
||||
self.logger.debug("LiveTVChannels schedule check...")
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
let newChanPrgs = self.processChannelPrograms()
|
||||
DispatchQueue.main.async {
|
||||
|
|
|
@ -49,7 +49,7 @@ final class LiveTVProgramsViewModel: ViewModel {
|
|||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.handleAPIRequestError(completion: completion)
|
||||
}, receiveValue: { [weak self] response in
|
||||
LogManager.log.debug("Received \(response.items?.count ?? 0) Channels")
|
||||
self?.logger.debug("Received \(response.items?.count ?? 0) Channels")
|
||||
guard let self = self else { return }
|
||||
if let chans = response.items {
|
||||
for chan in chans {
|
||||
|
@ -82,7 +82,7 @@ final class LiveTVProgramsViewModel: ViewModel {
|
|||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.handleAPIRequestError(completion: completion)
|
||||
}, receiveValue: { [weak self] response in
|
||||
LogManager.log.debug("Received \(String(response.items?.count ?? 0)) Recommended Programs")
|
||||
self?.logger.debug("Received \(String(response.items?.count ?? 0)) Recommended Programs")
|
||||
guard let self = self else { return }
|
||||
self.recommendedItems = response.items ?? []
|
||||
})
|
||||
|
@ -109,7 +109,7 @@ final class LiveTVProgramsViewModel: ViewModel {
|
|||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.handleAPIRequestError(completion: completion)
|
||||
}, receiveValue: { [weak self] response in
|
||||
LogManager.log.debug("Received \(String(response.items?.count ?? 0)) Series Items")
|
||||
self?.logger.debug("Received \(String(response.items?.count ?? 0)) Series Items")
|
||||
guard let self = self else { return }
|
||||
self.seriesItems = response.items ?? []
|
||||
})
|
||||
|
@ -136,7 +136,7 @@ final class LiveTVProgramsViewModel: ViewModel {
|
|||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.handleAPIRequestError(completion: completion)
|
||||
}, receiveValue: { [weak self] response in
|
||||
LogManager.log.debug("Received \(String(response.items?.count ?? 0)) Movie Items")
|
||||
self?.logger.debug("Received \(String(response.items?.count ?? 0)) Movie Items")
|
||||
guard let self = self else { return }
|
||||
self.movieItems = response.items ?? []
|
||||
})
|
||||
|
@ -159,7 +159,7 @@ final class LiveTVProgramsViewModel: ViewModel {
|
|||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.handleAPIRequestError(completion: completion)
|
||||
}, receiveValue: { [weak self] response in
|
||||
LogManager.log.debug("Received \(String(response.items?.count ?? 0)) Sports Items")
|
||||
self?.logger.debug("Received \(String(response.items?.count ?? 0)) Sports Items")
|
||||
guard let self = self else { return }
|
||||
self.sportsItems = response.items ?? []
|
||||
})
|
||||
|
@ -182,7 +182,7 @@ final class LiveTVProgramsViewModel: ViewModel {
|
|||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.handleAPIRequestError(completion: completion)
|
||||
}, receiveValue: { [weak self] response in
|
||||
LogManager.log.debug("Received \(String(response.items?.count ?? 0)) Kids Items")
|
||||
self?.logger.debug("Received \(String(response.items?.count ?? 0)) Kids Items")
|
||||
guard let self = self else { return }
|
||||
self.kidsItems = response.items ?? []
|
||||
})
|
||||
|
@ -205,7 +205,7 @@ final class LiveTVProgramsViewModel: ViewModel {
|
|||
.sink(receiveCompletion: { [weak self] completion in
|
||||
self?.handleAPIRequestError(completion: completion)
|
||||
}, receiveValue: { [weak self] response in
|
||||
LogManager.log.debug("Received \(String(response.items?.count ?? 0)) News Items")
|
||||
self?.logger.debug("Received \(String(response.items?.count ?? 0)) News Items")
|
||||
guard let self = self else { return }
|
||||
self.newsItems = response.items ?? []
|
||||
})
|
||||
|
|
|
@ -32,13 +32,13 @@ final class QuickConnectSettingsViewModel: ViewModel {
|
|||
self.handleAPIRequestError(displayMessage: L10n.quickConnectInvalidError, completion: completion)
|
||||
switch completion {
|
||||
case .failure:
|
||||
LogManager.log.debug("Invalid Quick Connect code entered")
|
||||
self.logger.debug("Invalid Quick Connect code entered")
|
||||
default:
|
||||
break
|
||||
}
|
||||
}, receiveValue: { _ in
|
||||
// receiving a successful HTTP response indicates a valid code
|
||||
LogManager.log.debug("Valid Quick connect code entered")
|
||||
self.logger.debug("Valid Quick connect code entered")
|
||||
self.showSuccessMessage = true
|
||||
})
|
||||
.store(in: &cancellables)
|
||||
|
|
|
@ -24,6 +24,7 @@ final class SettingsViewModel: ViewModel {
|
|||
|
||||
self.server = server
|
||||
self.user = user
|
||||
super.init()
|
||||
|
||||
// Bitrates
|
||||
let url = Bundle.main.url(forResource: "bitrates", withExtension: "json")!
|
||||
|
@ -33,10 +34,10 @@ final class SettingsViewModel: ViewModel {
|
|||
do {
|
||||
self.bitrates = try JSONDecoder().decode([Bitrates].self, from: jsonData)
|
||||
} catch {
|
||||
LogManager.log.error("Error converting processed JSON into Swift compatible schema.")
|
||||
logger.error("Error converting processed JSON into Swift compatible schema.")
|
||||
}
|
||||
} catch {
|
||||
LogManager.log.error("Error processing JSON file `bitrates.json`")
|
||||
logger.error("Error processing JSON file `bitrates.json`")
|
||||
}
|
||||
|
||||
// Track languages
|
||||
|
|
|
@ -46,7 +46,7 @@ final class UserSignInViewModel: ViewModel {
|
|||
}
|
||||
|
||||
func signIn(username: String, password: String) {
|
||||
LogManager.log.debug("Attempting to login to server at \"\(server.currentURI)\"", tag: "login")
|
||||
logger.debug("Attempting to login to server at \"\(server.currentURI)\"", tag: "login")
|
||||
|
||||
let username = username.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
.trimmingCharacters(in: .objectReplacement)
|
||||
|
@ -99,7 +99,7 @@ final class UserSignInViewModel: ViewModel {
|
|||
|
||||
self.quickConnectSecret = response.secret
|
||||
self.quickConnectCode = response.code
|
||||
LogManager.log.debug("QuickConnect code: \(response.code ?? .emptyDash)")
|
||||
self.logger.debug("QuickConnect code: \(response.code ?? .emptyDash)")
|
||||
|
||||
self.quickConnectTimer = RepeatingTimer(interval: 5) {
|
||||
self.checkAuthStatus(onSuccess)
|
||||
|
@ -120,7 +120,7 @@ final class UserSignInViewModel: ViewModel {
|
|||
// this is a repeated call
|
||||
}, receiveValue: { value in
|
||||
guard let authenticated = value.authenticated, authenticated else {
|
||||
LogManager.log.debug("QuickConnect not authenticated yet")
|
||||
self.logger.debug("QuickConnect not authenticated yet")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -511,7 +511,7 @@ extension VideoPlayerViewModel {
|
|||
.sink { completion in
|
||||
self.handleAPIRequestError(completion: completion)
|
||||
} receiveValue: { _ in
|
||||
LogManager.log.debug("Start report sent for item: \(self.item.id ?? "No ID")")
|
||||
self.logger.debug("Start report sent for item: \(self.item.id ?? "No ID")")
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
@ -547,7 +547,7 @@ extension VideoPlayerViewModel {
|
|||
.sink { completion in
|
||||
self.handleAPIRequestError(completion: completion)
|
||||
} receiveValue: { _ in
|
||||
LogManager.log.debug("Pause report sent for item: \(self.item.id ?? "No ID")")
|
||||
self.logger.debug("Pause report sent for item: \(self.item.id ?? "No ID")")
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
@ -592,7 +592,7 @@ extension VideoPlayerViewModel {
|
|||
.sink { completion in
|
||||
self.handleAPIRequestError(completion: completion)
|
||||
} receiveValue: { _ in
|
||||
LogManager.log.debug("Playback progress sent for item: \(self.item.id ?? "No ID")")
|
||||
self.logger.debug("Playback progress sent for item: \(self.item.id ?? "No ID")")
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
|
@ -619,7 +619,7 @@ extension VideoPlayerViewModel {
|
|||
.sink { completion in
|
||||
self.handleAPIRequestError(completion: completion)
|
||||
} receiveValue: { _ in
|
||||
LogManager.log.debug("Stop report sent for item: \(self.item.id ?? "No ID")")
|
||||
self.logger.debug("Stop report sent for item: \(self.item.id ?? "No ID")")
|
||||
Notifications[.didSendStopReport].post(object: self.item.id)
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
|
|
@ -8,11 +8,14 @@
|
|||
|
||||
import ActivityIndicator
|
||||
import Combine
|
||||
import Factory
|
||||
import Foundation
|
||||
import JellyfinAPI
|
||||
|
||||
class ViewModel: ObservableObject {
|
||||
|
||||
@Injected(LogManager.service)
|
||||
var logger
|
||||
@Published
|
||||
var isLoading = false
|
||||
@Published
|
||||
|
@ -39,18 +42,18 @@ class ViewModel: ObservableObject {
|
|||
case .error(-1, _, _, _):
|
||||
networkError = .URLError(response: errorResponse, displayMessage: displayMessage)
|
||||
// Use the errorResponse description for debugging, rather than the user-facing friendly description which may not be implemented
|
||||
LogManager.log
|
||||
logger
|
||||
.error(
|
||||
"Request failed: URL request failed with error \(networkError.errorMessage.code): \(errorResponse.localizedDescription)"
|
||||
)
|
||||
case .error(-2, _, _, _):
|
||||
networkError = .HTTPURLError(response: errorResponse, displayMessage: displayMessage)
|
||||
LogManager.log
|
||||
logger
|
||||
.error("Request failed: HTTP URL request failed with description: \(errorResponse.localizedDescription)")
|
||||
default:
|
||||
networkError = .JellyfinError(response: errorResponse, displayMessage: displayMessage)
|
||||
// Able to use user-facing friendly description here since just HTTP status codes
|
||||
LogManager.log
|
||||
logger
|
||||
.error(
|
||||
"Request failed: \(networkError.errorMessage.code) - \(networkError.errorMessage.title): \(networkError.errorMessage.message)\n\(error.localizedDescription)"
|
||||
)
|
||||
|
@ -66,7 +69,7 @@ class ViewModel: ObservableObject {
|
|||
message: swiftfinError.errorDescription ?? ""
|
||||
)
|
||||
self.errorMessage = errorMessage
|
||||
LogManager.log.error("Request failed: \(swiftfinError.errorDescription ?? "")")
|
||||
logger.error("Request failed: \(swiftfinError.errorDescription ?? "")")
|
||||
|
||||
default:
|
||||
let genericErrorMessage = ErrorMessage(
|
||||
|
@ -75,7 +78,7 @@ class ViewModel: ObservableObject {
|
|||
message: error.localizedDescription
|
||||
)
|
||||
self.errorMessage = genericErrorMessage
|
||||
LogManager.log.error("Request failed: Generic error - \(error.localizedDescription)")
|
||||
logger.error("Request failed: Generic error - \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,15 @@
|
|||
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Factory
|
||||
import SwiftUI
|
||||
|
||||
extension ItemView {
|
||||
|
||||
struct PlayButton: View {
|
||||
|
||||
@Injected(LogManager.service)
|
||||
private var logger
|
||||
@EnvironmentObject
|
||||
private var itemRouter: ItemCoordinator.Router
|
||||
@ObservedObject
|
||||
|
@ -24,7 +27,7 @@ extension ItemView {
|
|||
if let selectedVideoPlayerViewModel = viewModel.selectedVideoPlayerViewModel {
|
||||
itemRouter.route(to: \.videoPlayer, selectedVideoPlayerViewModel)
|
||||
} else {
|
||||
LogManager.log.error("Attempted to play item but no playback information available")
|
||||
logger.error("Attempted to play item but no playback information available")
|
||||
}
|
||||
} label: {
|
||||
HStack(spacing: 15) {
|
||||
|
@ -55,7 +58,7 @@ extension ItemView {
|
|||
selectedVideoPlayerViewModel.injectCustomValues(startFromBeginning: true)
|
||||
itemRouter.route(to: \.videoPlayer, selectedVideoPlayerViewModel)
|
||||
} else {
|
||||
LogManager.log.error("Attempted to play item but no playback information available")
|
||||
logger.error("Attempted to play item but no playback information available")
|
||||
}
|
||||
} label: {
|
||||
Label(L10n.playFromBeginning, systemImage: "gobackward")
|
||||
|
|
|
@ -10,6 +10,7 @@ import AVFoundation
|
|||
import AVKit
|
||||
import Combine
|
||||
import Defaults
|
||||
import Factory
|
||||
import JellyfinAPI
|
||||
import MediaPlayer
|
||||
import SwiftUI
|
||||
|
@ -20,6 +21,9 @@ import UIKit
|
|||
|
||||
class LiveTVPlayerViewController: UIViewController {
|
||||
|
||||
@Injected(LogManager.service)
|
||||
private var logger
|
||||
|
||||
// MARK: variables
|
||||
|
||||
private var viewModel: VideoPlayerViewModel
|
||||
|
@ -476,11 +480,11 @@ extension LiveTVPlayerViewController {
|
|||
viewModel = newViewModel
|
||||
|
||||
if viewModel.streamType == .direct {
|
||||
LogManager.log.debug("Player set up with direct play stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
logger.debug("Player set up with direct play stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
} else if viewModel.streamType == .transcode && Defaults[.Experimental.forceDirectPlay] {
|
||||
LogManager.log.debug("Player set up with forced direct stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
logger.debug("Player set up with forced direct stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
} else {
|
||||
LogManager.log.debug("Player set up with transcoded stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
logger.debug("Player set up with transcoded stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import AVFoundation
|
|||
import AVKit
|
||||
import Combine
|
||||
import Defaults
|
||||
import Factory
|
||||
import JellyfinAPI
|
||||
import MediaPlayer
|
||||
import SwiftUI
|
||||
|
@ -20,6 +21,9 @@ import UIKit
|
|||
|
||||
class VLCPlayerViewController: UIViewController {
|
||||
|
||||
@Injected(LogManager.service)
|
||||
private var logger
|
||||
|
||||
// MARK: variables
|
||||
|
||||
private var viewModel: VideoPlayerViewModel
|
||||
|
@ -476,11 +480,11 @@ extension VLCPlayerViewController {
|
|||
viewModel = newViewModel
|
||||
|
||||
if viewModel.streamType == .direct {
|
||||
LogManager.log.debug("Player set up with direct play stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
logger.debug("Player set up with direct play stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
} else if viewModel.streamType == .transcode && Defaults[.Experimental.forceDirectPlay] {
|
||||
LogManager.log.debug("Player set up with forced direct stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
logger.debug("Player set up with forced direct stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
} else {
|
||||
LogManager.log.debug("Player set up with transcoded stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
logger.debug("Player set up with transcoded stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -410,6 +410,8 @@
|
|||
E19169CE272514760085832A /* HTTPScheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19169CD272514760085832A /* HTTPScheme.swift */; };
|
||||
E19169CF272514760085832A /* HTTPScheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = E19169CD272514760085832A /* HTTPScheme.swift */; };
|
||||
E192608028D28AAD002314B4 /* UserProfileButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = E192607F28D28AAD002314B4 /* UserProfileButton.swift */; };
|
||||
E192608328D2D0DB002314B4 /* Factory in Frameworks */ = {isa = PBXBuildFile; productRef = E192608228D2D0DB002314B4 /* Factory */; };
|
||||
E192608828D2E5F0002314B4 /* Factory in Frameworks */ = {isa = PBXBuildFile; productRef = E192608728D2E5F0002314B4 /* Factory */; };
|
||||
E1937A3B288E54AD00CB80AA /* BaseItemDto+Images.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1937A3A288E54AD00CB80AA /* BaseItemDto+Images.swift */; };
|
||||
E1937A3C288E54AD00CB80AA /* BaseItemDto+Images.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1937A3A288E54AD00CB80AA /* BaseItemDto+Images.swift */; };
|
||||
E1937A3E288F0D3D00CB80AA /* UIScreenExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1937A3D288F0D3D00CB80AA /* UIScreenExtensions.swift */; };
|
||||
|
@ -1027,6 +1029,7 @@
|
|||
E12186DE2718F1C50010884C /* Defaults in Frameworks */,
|
||||
E1347DB6279E3CA500BC6161 /* Puppy in Frameworks */,
|
||||
53ABFDED26799D7700886593 /* ActivityIndicator in Frameworks */,
|
||||
E192608828D2E5F0002314B4 /* Factory in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -1045,6 +1048,7 @@
|
|||
62666E0327E5017100EC0ECD /* CoreMedia.framework in Frameworks */,
|
||||
62666E0627E5017A00EC0ECD /* CoreVideo.framework in Frameworks */,
|
||||
E10EAA4D277BB716000269ED /* Sliders in Frameworks */,
|
||||
E192608328D2D0DB002314B4 /* Factory in Frameworks */,
|
||||
62C29E9C26D0FE4200C1D2E7 /* Stinsen in Frameworks */,
|
||||
62666E0227E5016D00EC0ECD /* CoreGraphics.framework in Frameworks */,
|
||||
62666E1027E501B400EC0ECD /* VideoToolbox.framework in Frameworks */,
|
||||
|
@ -2269,6 +2273,7 @@
|
|||
E13AF3B928A0C598009093AB /* NukeUI */,
|
||||
E13AF3BB28A0C59E009093AB /* BlurHashKit */,
|
||||
E1734D7D28B9578100C66367 /* CollectionView */,
|
||||
E192608728D2E5F0002314B4 /* Factory */,
|
||||
);
|
||||
productName = "JellyfinPlayer tvOS";
|
||||
productReference = 535870602669D21600D05A09 /* Swiftfin tvOS.app */;
|
||||
|
@ -2308,6 +2313,7 @@
|
|||
E19E6E0628A0B958005C10C8 /* NukeUI */,
|
||||
E19E6E0928A0BEFF005C10C8 /* BlurHashKit */,
|
||||
E1734D7B28B9577700C66367 /* CollectionView */,
|
||||
E192608228D2D0DB002314B4 /* Factory */,
|
||||
);
|
||||
productName = JellyfinPlayer;
|
||||
productReference = 5377CBF1263B596A003A4E83 /* Swiftfin iOS.app */;
|
||||
|
@ -2373,6 +2379,7 @@
|
|||
E19E6E0328A0B958005C10C8 /* XCRemoteSwiftPackageReference "Nuke" */,
|
||||
E19E6E0828A0BEFF005C10C8 /* XCRemoteSwiftPackageReference "BlurHashKit" */,
|
||||
E1734D7A28B9577700C66367 /* XCRemoteSwiftPackageReference "CollectionView" */,
|
||||
E192608128D2D0DB002314B4 /* XCRemoteSwiftPackageReference "Factory" */,
|
||||
);
|
||||
productRefGroup = 5377CBF2263B596A003A4E83 /* Products */;
|
||||
projectDirPath = "";
|
||||
|
@ -3447,6 +3454,14 @@
|
|||
kind = branch;
|
||||
};
|
||||
};
|
||||
E192608128D2D0DB002314B4 /* XCRemoteSwiftPackageReference "Factory" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/hmlongco/Factory";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 1.0.0;
|
||||
};
|
||||
};
|
||||
E19E6E0328A0B958005C10C8 /* XCRemoteSwiftPackageReference "Nuke" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/kean/Nuke";
|
||||
|
@ -3609,6 +3624,16 @@
|
|||
package = E10EAA43277BB646000269ED /* XCRemoteSwiftPackageReference "jellyfin-sdk-swift" */;
|
||||
productName = JellyfinAPI;
|
||||
};
|
||||
E192608228D2D0DB002314B4 /* Factory */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E192608128D2D0DB002314B4 /* XCRemoteSwiftPackageReference "Factory" */;
|
||||
productName = Factory;
|
||||
};
|
||||
E192608728D2E5F0002314B4 /* Factory */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E192608128D2D0DB002314B4 /* XCRemoteSwiftPackageReference "Factory" */;
|
||||
productName = Factory;
|
||||
};
|
||||
E19E6E0428A0B958005C10C8 /* Nuke */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = E19E6E0328A0B958005C10C8 /* XCRemoteSwiftPackageReference "Nuke" */;
|
||||
|
|
|
@ -54,6 +54,15 @@
|
|||
"version" : "6.3.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "factory",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/hmlongco/Factory",
|
||||
"state" : {
|
||||
"revision" : "8557426f3286e20b631ecdac8115242f888656e0",
|
||||
"version" : "1.2.8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "jellyfin-sdk-swift",
|
||||
"kind" : "remoteSourceControl",
|
||||
|
|
|
@ -20,7 +20,6 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
|||
|
||||
// Lazily initialize datastack
|
||||
_ = SwiftfinStore.dataStack
|
||||
LogManager.setup()
|
||||
|
||||
let audioSession = AVAudioSession.sharedInstance()
|
||||
do {
|
||||
|
|
|
@ -6,12 +6,15 @@
|
|||
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Factory
|
||||
import SwiftUI
|
||||
|
||||
extension ItemView {
|
||||
|
||||
struct PlayButton: View {
|
||||
|
||||
@Injected(LogManager.service)
|
||||
private var logger
|
||||
@EnvironmentObject
|
||||
private var itemRouter: ItemCoordinator.Router
|
||||
@ObservedObject
|
||||
|
@ -22,7 +25,7 @@ extension ItemView {
|
|||
if let selectedVideoPlayerViewModel = viewModel.selectedVideoPlayerViewModel {
|
||||
itemRouter.route(to: \.videoPlayer, selectedVideoPlayerViewModel)
|
||||
} else {
|
||||
LogManager.log.error("Attempted to play item but no playback information available")
|
||||
logger.error("Attempted to play item but no playback information available")
|
||||
}
|
||||
} label: {
|
||||
ZStack {
|
||||
|
@ -47,7 +50,7 @@ extension ItemView {
|
|||
selectedVideoPlayerViewModel.injectCustomValues(startFromBeginning: true)
|
||||
itemRouter.route(to: \.videoPlayer, selectedVideoPlayerViewModel)
|
||||
} else {
|
||||
LogManager.log.error("Attempted to play item but no playback information available")
|
||||
logger.error("Attempted to play item but no playback information available")
|
||||
}
|
||||
} label: {
|
||||
Label(L10n.playFromBeginning, systemImage: "gobackward")
|
||||
|
|
|
@ -10,6 +10,7 @@ import AVFoundation
|
|||
import AVKit
|
||||
import Combine
|
||||
import Defaults
|
||||
import Factory
|
||||
import JellyfinAPI
|
||||
import MediaPlayer
|
||||
import MobileVLCKit
|
||||
|
@ -19,6 +20,10 @@ import UIKit
|
|||
// TODO: Look at making the VLC player layer a view
|
||||
|
||||
class LiveTVPlayerViewController: UIViewController {
|
||||
|
||||
@Injected(LogManager.service)
|
||||
private var logger
|
||||
|
||||
// MARK: variables
|
||||
|
||||
private var viewModel: VideoPlayerViewModel
|
||||
|
@ -532,11 +537,11 @@ extension LiveTVPlayerViewController {
|
|||
viewModel = newViewModel
|
||||
|
||||
if viewModel.streamType == .direct {
|
||||
LogManager.log.debug("Player set up with direct play stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
logger.debug("Player set up with direct play stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
} else if viewModel.streamType == .transcode && Defaults[.Experimental.forceDirectPlay] {
|
||||
LogManager.log.debug("Player set up with forced direct stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
logger.debug("Player set up with forced direct stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
} else {
|
||||
LogManager.log.debug("Player set up with transcoded stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
logger.debug("Player set up with transcoded stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import AVFoundation
|
|||
import AVKit
|
||||
import Combine
|
||||
import Defaults
|
||||
import Factory
|
||||
import JellyfinAPI
|
||||
import MediaPlayer
|
||||
import MobileVLCKit
|
||||
|
@ -19,6 +20,10 @@ import UIKit
|
|||
// TODO: Look at making the VLC player layer a view
|
||||
|
||||
class VLCPlayerViewController: UIViewController {
|
||||
|
||||
@Injected(LogManager.service)
|
||||
private var logger
|
||||
|
||||
// MARK: variables
|
||||
|
||||
private var viewModel: VideoPlayerViewModel
|
||||
|
@ -615,11 +620,11 @@ extension VLCPlayerViewController {
|
|||
viewModel = newViewModel
|
||||
|
||||
if viewModel.streamType == .direct {
|
||||
LogManager.log.debug("Player set up with direct play stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
logger.debug("Player set up with direct play stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
} else if viewModel.streamType == .transcode && Defaults[.Experimental.forceDirectPlay] {
|
||||
LogManager.log.debug("Player set up with forced direct stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
logger.debug("Player set up with forced direct stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
} else {
|
||||
LogManager.log.debug("Player set up with transcoded stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
logger.debug("Player set up with transcoded stream for item: \(viewModel.item.id ?? .emptyDash)")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue