mirror of
https://github.com/jellyfin/Swiftfin.git
synced 2024-11-28 00:30:35 +00:00
270 lines
13 KiB
Swift
270 lines
13 KiB
Swift
//
|
|
// LibraryView.swift
|
|
// JellyfinPlayer
|
|
//
|
|
// Created by Aiden Vigue on 5/1/21.
|
|
//
|
|
|
|
import SwiftUI
|
|
import SwiftyRequest
|
|
import SwiftyJSON
|
|
import ExyteGrid
|
|
import SDWebImageSwiftUI
|
|
|
|
struct LibraryView: View {
|
|
@Environment(\.managedObjectContext) private var viewContext
|
|
@EnvironmentObject var globalData: GlobalData
|
|
@State private var prefill_id: String = "";
|
|
@State private var library_names: [String: String] = [:]
|
|
@State private var library_ids: [String] = []
|
|
@State private var selected_library_id: String = "";
|
|
@State private var isLoading: Bool = true;
|
|
|
|
@State private var startIndex: Int = 0;
|
|
@State private var endIndex: Int = 60;
|
|
@State private var totalItems: Int = 0;
|
|
|
|
@State private var viewDidLoad: Bool = false;
|
|
@State private var filterString: String = "&SortBy=SortName&SortOrder=Descending";
|
|
@State private var showFiltersPopover: Bool = false
|
|
|
|
|
|
var gridItems: [GridItem] = [GridItem(.adaptive(minimum: 150, maximum: 400))]
|
|
|
|
init(prefill: String?, names: [String: String], libraries: [String]) {
|
|
_prefill_id = State(wrappedValue: prefill ?? "")
|
|
_library_names = State(wrappedValue: names)
|
|
_library_ids = State(wrappedValue: libraries)
|
|
//print("prefilling w/ \(prefill ?? "") aka \(names[prefill ?? ""] ?? "nil")")
|
|
}
|
|
|
|
init(prefill: String?, names: [String: String], libraries: [String], filter: String) {
|
|
_prefill_id = State(wrappedValue: prefill ?? "")
|
|
_library_names = State(wrappedValue: names)
|
|
_library_ids = State(wrappedValue: libraries)
|
|
_filterString = State(wrappedValue: filter);
|
|
//print("prefilling w/ \(prefill ?? "") aka \(names[prefill ?? ""] ?? "nil")")
|
|
}
|
|
|
|
@State var items: [ResumeItem] = []
|
|
|
|
func listOnAppear() {
|
|
if(_viewDidLoad.wrappedValue == false) {
|
|
//print("running VDL")
|
|
_viewDidLoad.wrappedValue = true;
|
|
_library_ids.wrappedValue.append("favorites")
|
|
_library_names.wrappedValue["favorites"] = "Favorites"
|
|
|
|
_library_ids.wrappedValue.append("genres")
|
|
_library_names.wrappedValue["genres"] = "Genres"
|
|
}
|
|
}
|
|
|
|
func loadItems() {
|
|
_isLoading.wrappedValue = true;
|
|
let url = "/Users/\(globalData.user?.user_id ?? "")/Items?Limit=\(endIndex)&StartIndex=\(startIndex)&Recursive=true&Fields=PrimaryImageAspectRatio%2CBasicSyncInfo&ImageTypeLimit=1&EnableImageTypes=Primary%2CBackdrop%2CThumb%2CBanner&IncludeItemTypes=Movie,Series\(selected_library_id == "favorites" ? "&Filters=IsFavorite" : "&ParentId=" + selected_library_id)\(filterString)"
|
|
|
|
let request = RestRequest(method: .get, url: (globalData.server?.baseURI ?? "") + url)
|
|
request.headerParameters["X-Emby-Authorization"] = globalData.authHeader
|
|
request.contentType = "application/json"
|
|
request.acceptType = "application/json"
|
|
|
|
request.responseData() { (result: Result<RestResponse<Data>, RestError>) in
|
|
switch result {
|
|
case .success(let response):
|
|
let body = response.body
|
|
do {
|
|
let json = try JSON(data: body)
|
|
_totalItems.wrappedValue = json["TotalRecordCount"].int ?? 0;
|
|
for (_,item):(String, JSON) in json["Items"] {
|
|
// Do something you want
|
|
let itemObj = ResumeItem()
|
|
itemObj.Type = item["Type"].string ?? ""
|
|
if(itemObj.Type == "Series") {
|
|
itemObj.ItemBadge = item["UserData"]["UnplayedItemCount"].int ?? 0
|
|
itemObj.Image = item["ImageTags"]["Primary"].string ?? ""
|
|
itemObj.ImageType = "Primary"
|
|
itemObj.BlurHash = item["ImageBlurHashes"]["Primary"][itemObj.Image].string ?? ""
|
|
itemObj.Name = item["Name"].string ?? ""
|
|
itemObj.Type = item["Type"].string ?? ""
|
|
itemObj.IndexNumber = nil
|
|
itemObj.Id = item["Id"].string ?? ""
|
|
itemObj.ParentIndexNumber = nil
|
|
itemObj.SeasonId = nil
|
|
itemObj.SeriesId = nil
|
|
itemObj.SeriesName = nil
|
|
itemObj.ProductionYear = item["ProductionYear"].int ?? 0
|
|
} else {
|
|
itemObj.ProductionYear = item["ProductionYear"].int ?? 0
|
|
itemObj.Image = item["ImageTags"]["Primary"].string ?? ""
|
|
itemObj.ImageType = "Primary"
|
|
itemObj.BlurHash = item["ImageBlurHashes"]["Primary"][itemObj.Image].string ?? ""
|
|
itemObj.Name = item["Name"].string ?? ""
|
|
itemObj.Type = item["Type"].string ?? ""
|
|
itemObj.IndexNumber = item["IndexNumber"].int ?? nil
|
|
itemObj.Id = item["Id"].string ?? ""
|
|
itemObj.ParentIndexNumber = item["ParentIndexNumber"].int ?? nil
|
|
itemObj.SeasonId = item["SeasonId"].string ?? nil
|
|
itemObj.SeriesId = item["SeriesId"].string ?? nil
|
|
itemObj.SeriesName = item["SeriesName"].string ?? nil
|
|
}
|
|
itemObj.Watched = item["UserData"]["Played"].bool ?? false
|
|
|
|
_items.wrappedValue.append(itemObj)
|
|
}
|
|
} catch {
|
|
|
|
}
|
|
break
|
|
case .failure(let error):
|
|
debugPrint(error)
|
|
break
|
|
}
|
|
_isLoading.wrappedValue = false;
|
|
}
|
|
}
|
|
|
|
func onAppear() {
|
|
if(_prefill_id.wrappedValue != "") {
|
|
_selected_library_id.wrappedValue = _prefill_id.wrappedValue;
|
|
}
|
|
if(_items.wrappedValue.count == 0) {
|
|
loadItems()
|
|
}
|
|
}
|
|
|
|
@Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass?
|
|
@Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass?
|
|
|
|
var isPortrait: Bool {
|
|
let result = verticalSizeClass == .regular && horizontalSizeClass == .compact
|
|
return result
|
|
}
|
|
|
|
var tracks: [GridTrack] {
|
|
self.isPortrait ? 3 : 6
|
|
}
|
|
|
|
var body: some View {
|
|
if(prefill_id != "") {
|
|
LoadingView(isShowing: $isLoading) {
|
|
GeometryReader { geometry in
|
|
Grid(tracks: self.tracks, spacing: GridSpacing(horizontal: 0, vertical: 20)) {
|
|
ForEach(items, id: \.Id) { item in
|
|
NavigationLink(destination: ItemView(item: item )) {
|
|
VStack(alignment: .leading) {
|
|
if(item.Type == "Movie") {
|
|
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?fillWidth=300&fillHeight=450&quality=90&tag=\(item.Image)"))
|
|
.resizable()
|
|
.placeholder {
|
|
Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 32, height: 32))!)
|
|
.resizable()
|
|
.frame(width: 100, height: 150)
|
|
.cornerRadius(10)
|
|
}
|
|
.frame(width:100, height: 150)
|
|
.cornerRadius(10)
|
|
.shadow(radius: 5)
|
|
} else {
|
|
WebImage(url: URL(string: "\(globalData.server?.baseURI ?? "")/Items/\(item.Id)/Images/\(item.ImageType)?fillWidth=300&fillHeight=450&quality=90&tag=\(item.Image)"))
|
|
.resizable()
|
|
.placeholder {
|
|
Image(uiImage: UIImage(blurHash: (item.BlurHash == "" ? "W$H.4}D%bdo#a#xbtpxVW?W?jXWsXVt7Rjf5axWqxbWXnhada{s-" : item.BlurHash), size: CGSize(width: 32, height: 32))!)
|
|
.resizable()
|
|
.frame(width: 100, height: 150)
|
|
.cornerRadius(10)
|
|
}
|
|
.frame(width:100, height: 150)
|
|
.cornerRadius(10).overlay(
|
|
ZStack {
|
|
Text("\(String(item.ItemBadge ?? 0))")
|
|
.font(.caption)
|
|
.padding(3)
|
|
.foregroundColor(.white)
|
|
}.background(Color.black)
|
|
.opacity(0.8)
|
|
.cornerRadius(10.0)
|
|
.padding(3), alignment: .topTrailing
|
|
)
|
|
.shadow(radius: 5)
|
|
}
|
|
Text(item.Name)
|
|
.font(.caption)
|
|
.fontWeight(.semibold)
|
|
.foregroundColor(.primary)
|
|
.lineLimit(1)
|
|
Text(String(item.ProductionYear))
|
|
.foregroundColor(.secondary)
|
|
.font(.caption)
|
|
.fontWeight(.medium)
|
|
}.frame(width: 100)
|
|
}
|
|
}
|
|
if(startIndex + endIndex < totalItems) {
|
|
HStack() {
|
|
Spacer()
|
|
Button() {
|
|
startIndex += endIndex;
|
|
loadItems()
|
|
} label: {
|
|
HStack() {
|
|
Text("Load more").font(.callout)
|
|
Image(systemName: "arrow.clockwise")
|
|
}
|
|
}
|
|
Spacer()
|
|
}.gridSpan(column: self.isPortrait ? 3 : 6)
|
|
}
|
|
Spacer().frame(height: 2).gridSpan(column: self.isPortrait ? 3 : 6)
|
|
}.gridContentMode(.scroll)
|
|
}
|
|
}
|
|
.overrideViewPreference(.unspecified)
|
|
.onAppear(perform: onAppear)
|
|
.onChange(of: filterString) { tag in
|
|
isLoading = true;
|
|
startIndex = 0;
|
|
totalItems = 0;
|
|
items = [];
|
|
loadItems();
|
|
}
|
|
.navigationTitle(library_names[prefill_id] ?? "Library")
|
|
.toolbar {
|
|
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
|
Button {
|
|
} label: {
|
|
Image(systemName: "magnifyingglass")
|
|
}
|
|
Button {
|
|
showFiltersPopover = true
|
|
} label: {
|
|
Image(systemName: "line.horizontal.3.decrease")
|
|
}
|
|
}
|
|
}.popover( isPresented: self.$showFiltersPopover, arrowEdge: .bottom) { LibraryFilterView(library: selected_library_id, output: $filterString, close: $showFiltersPopover) }
|
|
} else {
|
|
List(library_ids, id:\.self) { id in
|
|
if(id != "genres") {
|
|
NavigationLink(destination: LibraryView(prefill: id, names: library_names, libraries: library_ids)) {
|
|
Text(library_names[id] ?? "").foregroundColor(Color.primary)
|
|
}
|
|
} else {
|
|
NavigationLink(destination: LibraryView(prefill: id, names: library_names, libraries: library_ids)) {
|
|
Text(library_names[id] ?? "").foregroundColor(Color.primary)
|
|
}
|
|
}
|
|
}.onAppear(perform: listOnAppear).overrideViewPreference(.unspecified).navigationTitle("All Media")
|
|
.toolbar {
|
|
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
|
Button {
|
|
print("Search tapped!")
|
|
} label: {
|
|
Image(systemName: "magnifyingglass")
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|