jellyflood/Shared/ViewModels/LibraryViewModel/ItemLibraryViewModel.swift

139 lines
4.6 KiB
Swift

//
// Swiftfin is subject to the terms of the Mozilla Public
// License, v2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2024 Jellyfin & Jellyfin Contributors
//
import Combine
import Defaults
import Get
import JellyfinAPI
import OrderedCollections
import SwiftUI
final class ItemLibraryViewModel: PagingLibraryViewModel<BaseItemDto> {
// MARK: get
override func get(page: Int) async throws -> [BaseItemDto] {
let parameters = itemParameters(for: page)
let request = Paths.getItemsByUserID(userID: userSession.user.id, parameters: parameters)
let response = try await userSession.client.send(request)
// 1 - only care to keep collections that hold valid items
// 2 - if parent is type `folder`, then we are in a folder-view
// context so change `collectionFolder` types to `folder`
// for better view handling
let items = (response.value.items ?? [])
.filter { item in
if let collectionType = item.collectionType {
return ["movies", "tvshows", "mixed", "boxsets"].contains(collectionType)
}
return true
}
.map { item in
if parent?.libraryType == .folder, item.type == .collectionFolder {
return item.mutating(\.type, with: .folder)
}
return item
}
return items
}
// MARK: item parameters
func itemParameters(for page: Int?) -> Paths.GetItemsByUserIDParameters {
var libraryID: String?
var personIDs: [String]?
var studioIDs: [String]?
var includeItemTypes: [BaseItemKind] = [.movie, .series, .boxSet]
var isRecursive: Bool? = true
// TODO: determine `includeItemTypes` better
// - look at parent collection type if necessary
// - condense supported values
if let libraryType = parent?.libraryType, let id = parent?.id {
switch libraryType {
case .collectionFolder, .userView:
libraryID = id
case .folder:
libraryID = id
isRecursive = nil
includeItemTypes = [.movie, .series, .boxSet, .folder, .collectionFolder]
case .person:
personIDs = [id]
case .studio:
studioIDs = [id]
default: ()
}
}
var parameters = Paths.GetItemsByUserIDParameters()
parameters.enableUserData = true
parameters.fields = .MinimumFields
parameters.includeItemTypes = includeItemTypes
parameters.isRecursive = isRecursive
parameters.parentID = libraryID
parameters.personIDs = personIDs
parameters.studioIDs = studioIDs
// Page size
if let page {
parameters.limit = pageSize
parameters.startIndex = page * pageSize
}
// Filters
if let filterViewModel {
let filters = filterViewModel.currentFilters
parameters.filters = filters.traits
parameters.genres = filters.genres.map(\.value)
parameters.sortBy = filters.sortBy.map(\.rawValue)
parameters.sortOrder = filters.sortOrder
parameters.tags = filters.tags.map(\.value)
parameters.years = filters.years.compactMap { Int($0.value) }
if filters.letter.first?.value == "#" {
parameters.nameLessThan = "A"
} else {
parameters.nameStartsWith = filters.letter
.map(\.value)
.filter { $0 != "#" }
.first
}
// Random sort won't take into account previous items, so
// manual exclusion is necessary. This could possibly be
// a performance issue for loading pages after already loading
// many items, but there's nothing we can do about that.
if filters.sortBy.first == ItemSortBy.random {
parameters.excludeItemIDs = elements.compactMap(\.id)
}
}
return parameters
}
// MARK: getRandomItem
override func getRandomItem() async -> BaseItemDto? {
var parameters = itemParameters(for: nil)
parameters.limit = 1
parameters.sortBy = [ItemSortBy.random.rawValue]
let request = Paths.getItemsByUserID(userID: userSession.user.id, parameters: parameters)
let response = try? await userSession.client.send(request)
return response?.value.items?.first
}
}