basic movie item view
This commit is contained in:
parent
2919b25a5d
commit
897d158707
|
@ -24,6 +24,38 @@ struct PortraitItemElement: View {
|
||||||
.cornerRadius(10)
|
.cornerRadius(10)
|
||||||
.shadow(radius: focused ? 10.0 : 0)
|
.shadow(radius: focused ? 10.0 : 0)
|
||||||
.shadow(radius: focused ? 10.0 : 0)
|
.shadow(radius: focused ? 10.0 : 0)
|
||||||
|
.overlay(
|
||||||
|
ZStack {
|
||||||
|
if item.userData?.isFavorite ?? false {
|
||||||
|
Image(systemName: "circle.fill")
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.opacity(0.6)
|
||||||
|
Image(systemName: "heart.fill")
|
||||||
|
.foregroundColor(Color(.systemRed))
|
||||||
|
.font(.system(size: 10))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(2)
|
||||||
|
.opacity(1)
|
||||||
|
, alignment: .bottomLeading)
|
||||||
|
.overlay(
|
||||||
|
ZStack {
|
||||||
|
if item.userData?.played ?? false {
|
||||||
|
Image(systemName: "circle.fill")
|
||||||
|
.foregroundColor(.white)
|
||||||
|
Image(systemName: "checkmark.circle.fill")
|
||||||
|
.foregroundColor(Color(.systemBlue))
|
||||||
|
} else {
|
||||||
|
if(item.userData?.unplayedItemCount != nil) {
|
||||||
|
Image(systemName: "circle.fill")
|
||||||
|
.foregroundColor(Color(.systemBlue))
|
||||||
|
Text(String(item.userData!.unplayedItemCount ?? 0))
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.font(.caption2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.padding(2)
|
||||||
|
.opacity(1), alignment: .topTrailing).opacity(1)
|
||||||
}
|
}
|
||||||
.onChange(of: envFocused) { envFocus in
|
.onChange(of: envFocused) { envFocus in
|
||||||
withAnimation(.linear(duration: 0.15)) {
|
withAnimation(.linear(duration: 0.15)) {
|
||||||
|
|
|
@ -25,7 +25,7 @@ struct ContinueWatchingView: View {
|
||||||
LazyHStack {
|
LazyHStack {
|
||||||
Spacer().frame(width: 45)
|
Spacer().frame(width: 45)
|
||||||
ForEach(items, id: \.id) { item in
|
ForEach(items, id: \.id) { item in
|
||||||
NavigationLink(destination: VideoPlayerView(item: item)) {
|
NavigationLink(destination: LazyView { ItemView(item: item) }) {
|
||||||
LandscapeItemElement(item: item)
|
LandscapeItemElement(item: item)
|
||||||
}
|
}
|
||||||
.buttonStyle(PlainNavigationLinkButtonStyle())
|
.buttonStyle(PlainNavigationLinkButtonStyle())
|
||||||
|
|
|
@ -33,7 +33,9 @@ struct HomeView: View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
let library = viewModel.libraries.first(where: { $0.id == libraryID })
|
let library = viewModel.libraries.first(where: { $0.id == libraryID })
|
||||||
|
|
||||||
NavigationLink(destination: Text("library_latest")) {
|
NavigationLink(destination: LazyView {
|
||||||
|
LibraryView(viewModel: .init(parentID: libraryID, filters: viewModel.recentFilterSet), title: library?.name ?? "")
|
||||||
|
}) {
|
||||||
HStack {
|
HStack {
|
||||||
Text("Latest \(library?.name ?? "")")
|
Text("Latest \(library?.name ?? "")")
|
||||||
.font(.headline)
|
.font(.headline)
|
||||||
|
@ -45,6 +47,7 @@ struct HomeView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Spacer().frame(height: 30)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/* JellyfinPlayer/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 2021 Aiden Vigue & Jellyfin Contributors
|
||||||
|
*/
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import Introspect
|
||||||
|
import JellyfinAPI
|
||||||
|
|
||||||
|
class VideoPlayerItem: ObservableObject {
|
||||||
|
@Published var shouldShowPlayer: Bool = false
|
||||||
|
@Published var itemToPlay: BaseItemDto = BaseItemDto()
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ItemView: View {
|
||||||
|
private var item: BaseItemDto
|
||||||
|
|
||||||
|
@StateObject private var videoPlayerItem: VideoPlayerItem = VideoPlayerItem()
|
||||||
|
@State private var videoIsLoading: Bool = false; // This variable is only changed by the underlying VLC view.
|
||||||
|
@State private var isLoading: Bool = false
|
||||||
|
@State private var viewDidLoad: Bool = false
|
||||||
|
|
||||||
|
init(item: BaseItemDto) {
|
||||||
|
self.item = item
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
NavigationLink(destination: VideoPlayerView(item: videoPlayerItem.itemToPlay), isActive: $videoPlayerItem.shouldShowPlayer) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
.buttonStyle(PlainNavigationLinkButtonStyle())
|
||||||
|
.focusable(false)
|
||||||
|
|
||||||
|
Group {
|
||||||
|
if item.type == "Movie" {
|
||||||
|
MovieItemView(item: item)
|
||||||
|
} else {
|
||||||
|
Text("Type: \(item.type ?? "") not implemented yet :(")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.environmentObject(videoPlayerItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct JellyfinPlayer_tvOSApp: App {
|
struct JellyfinPlayer_tvOSApp: App {
|
||||||
let persistenceController = PersistenceController.shared
|
let persistenceController = PersistenceController.shared
|
||||||
|
|
|
@ -42,7 +42,7 @@ struct LatestMediaView: View {
|
||||||
LazyHStack {
|
LazyHStack {
|
||||||
Spacer().frame(width: 45)
|
Spacer().frame(width: 45)
|
||||||
ForEach(items, id: \.id) { item in
|
ForEach(items, id: \.id) { item in
|
||||||
NavigationLink(destination: Text("itemv")) {
|
NavigationLink(destination: LazyView { ItemView(item: item) }) {
|
||||||
PortraitItemElement(item: item)
|
PortraitItemElement(item: item)
|
||||||
}.buttonStyle(PlainNavigationLinkButtonStyle())
|
}.buttonStyle(PlainNavigationLinkButtonStyle())
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* JellyfinPlayer/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 2021 Aiden Vigue & Jellyfin Contributors
|
||||||
|
*/
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct LibraryView: View {
|
||||||
|
@StateObject var viewModel: LibraryViewModel
|
||||||
|
var title: String
|
||||||
|
|
||||||
|
// MARK: tracks for grid
|
||||||
|
var defaultFilters = LibraryFilters(filters: [], sortOrder: [.ascending], withGenres: [], tags: [], sortBy: [.name])
|
||||||
|
|
||||||
|
@State var isShowingSearchView = false
|
||||||
|
@State var isShowingFilterView = false
|
||||||
|
|
||||||
|
@State private var tracks: [GridItem] = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 250)
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Group {
|
||||||
|
if viewModel.isLoading == true {
|
||||||
|
ProgressView()
|
||||||
|
} else if !viewModel.items.isEmpty {
|
||||||
|
ScrollView(.vertical) {
|
||||||
|
Spacer().frame(height: 16)
|
||||||
|
LazyVGrid(columns: tracks) {
|
||||||
|
ForEach(viewModel.items, id: \.id) { item in
|
||||||
|
if(item.type != "Folder") {
|
||||||
|
NavigationLink(destination: LazyView { ItemView(item: item) }) {
|
||||||
|
PortraitItemElement(item: item)
|
||||||
|
}.buttonStyle(PlainNavigationLinkButtonStyle())
|
||||||
|
.onAppear() {
|
||||||
|
if item == viewModel.items.last && viewModel.hasNextPage {
|
||||||
|
print("Last item visible, load more items.")
|
||||||
|
viewModel.requestNextPageAsync()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer().frame(height: 16)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text("No results.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationBarTitle(title)
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
||||||
|
if viewModel.hasPreviousPage {
|
||||||
|
Button {
|
||||||
|
viewModel.requestPreviousPage()
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "chevron.left")
|
||||||
|
}.disabled(viewModel.isLoading)
|
||||||
|
}
|
||||||
|
if viewModel.hasNextPage {
|
||||||
|
Button {
|
||||||
|
viewModel.requestNextPage()
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "chevron.right")
|
||||||
|
}.disabled(viewModel.isLoading)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}/*
|
||||||
|
.sheet(isPresented: $isShowingFilterView) {
|
||||||
|
LibraryFilterView(filters: $viewModel.filters, enabledFilterType: viewModel.enabledFilterType, parentId: viewModel.parentID ?? "")
|
||||||
|
}
|
||||||
|
.background(
|
||||||
|
NavigationLink(destination: LibrarySearchView(viewModel: .init(parentID: viewModel.parentID)),
|
||||||
|
isActive: $isShowingSearchView) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// stream BM^S by nicki!
|
||||||
|
//
|
|
@ -0,0 +1,180 @@
|
||||||
|
//
|
||||||
|
/*
|
||||||
|
* 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 2021 Aiden Vigue & Jellyfin Contributors
|
||||||
|
*/
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import JellyfinAPI
|
||||||
|
|
||||||
|
struct MovieItemView: View {
|
||||||
|
let item: BaseItemDto
|
||||||
|
@EnvironmentObject private var playbackInfo: VideoPlayerItem
|
||||||
|
|
||||||
|
@State var actors: [BaseItemPerson] = [];
|
||||||
|
@State var studio: String? = nil;
|
||||||
|
@State var director: String? = nil;
|
||||||
|
|
||||||
|
@Namespace private var namespace
|
||||||
|
|
||||||
|
func onAppear() {
|
||||||
|
actors = []
|
||||||
|
director = nil
|
||||||
|
studio = nil
|
||||||
|
var actor_index = 0;
|
||||||
|
item.people?.forEach { person in
|
||||||
|
if(person.type == "Actor") {
|
||||||
|
if(actor_index < 8) {
|
||||||
|
actors.append(person)
|
||||||
|
}
|
||||||
|
actor_index = actor_index + 1;
|
||||||
|
}
|
||||||
|
if(person.type == "Director") {
|
||||||
|
director = person.name ?? ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
ImageView(src: item.getBackdropImage(maxWidth: 1920), bh: item.getBackdropImageBlurHash())
|
||||||
|
.opacity(0.4)
|
||||||
|
ScrollView {
|
||||||
|
LazyVStack {
|
||||||
|
HStack {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(item.name ?? "")
|
||||||
|
.font(.title)
|
||||||
|
.fontWeight(.bold)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
HStack {
|
||||||
|
if item.productionYear != nil {
|
||||||
|
Text(String(item.productionYear!)).font(.subheadline)
|
||||||
|
.fontWeight(.medium)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.lineLimit(1)
|
||||||
|
}
|
||||||
|
Text(item.getItemRuntime()).font(.subheadline)
|
||||||
|
.fontWeight(.medium)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.lineLimit(1)
|
||||||
|
if item.officialRating != nil {
|
||||||
|
Text(item.officialRating!).font(.subheadline)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.lineLimit(1)
|
||||||
|
.padding(EdgeInsets(top: 1, leading: 4, bottom: 1, trailing: 4))
|
||||||
|
.overlay(RoundedRectangle(cornerRadius: 2)
|
||||||
|
.stroke(Color.secondary, lineWidth: 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
VStack(alignment: .trailing) {
|
||||||
|
if(studio != nil) {
|
||||||
|
Text("STUDIO")
|
||||||
|
.font(.body)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
Text(studio!)
|
||||||
|
.font(.body)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.padding(.bottom, 40)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(director != nil) {
|
||||||
|
Text("DIRECTOR")
|
||||||
|
.font(.body)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
Text(director!)
|
||||||
|
.font(.body)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
.padding(.bottom, 40)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!actors.isEmpty) {
|
||||||
|
Text("CAST")
|
||||||
|
.font(.body)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
ForEach(actors, id: \.id) { person in
|
||||||
|
Text(person.name!)
|
||||||
|
.font(.body)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(item.taglines?.first ?? "")
|
||||||
|
.font(.body)
|
||||||
|
.italic()
|
||||||
|
.fontWeight(.medium)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
|
||||||
|
Text(item.overview ?? "")
|
||||||
|
.font(.body)
|
||||||
|
.fontWeight(.medium)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
VStack {
|
||||||
|
Button {
|
||||||
|
playbackInfo.shouldShowPlayer = true
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "heart.fill")
|
||||||
|
.font(.system(size: 40))
|
||||||
|
.padding(.vertical, 12).padding(.horizontal, 20)
|
||||||
|
}
|
||||||
|
Text("Favorite")
|
||||||
|
.font(.caption)
|
||||||
|
}
|
||||||
|
VStack {
|
||||||
|
Button {
|
||||||
|
playbackInfo.itemToPlay = item
|
||||||
|
playbackInfo.shouldShowPlayer = true
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "play.fill")
|
||||||
|
.font(.system(size: 40))
|
||||||
|
.padding(.vertical, 12).padding(.horizontal, 20)
|
||||||
|
}.prefersDefaultFocus(in: namespace)
|
||||||
|
Text("Play")
|
||||||
|
.font(.caption)
|
||||||
|
}
|
||||||
|
VStack {
|
||||||
|
Button {
|
||||||
|
playbackInfo.shouldShowPlayer = true
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "eye.fill")
|
||||||
|
.font(.system(size: 40))
|
||||||
|
.padding(.vertical, 12).padding(.horizontal, 20)
|
||||||
|
}
|
||||||
|
Text("Mark Watched")
|
||||||
|
.font(.caption)
|
||||||
|
}
|
||||||
|
}.padding(.top, 15)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
}.padding(.top, 50)
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack {
|
||||||
|
ImageView(src: item.getPrimaryImage(maxWidth: 450), bh: item.getPrimaryImageBlurHash())
|
||||||
|
.frame(width: 450, height: 675)
|
||||||
|
.cornerRadius(10)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.padding(EdgeInsets(top: 90, leading: 90, bottom: 0, trailing: 90))
|
||||||
|
}
|
||||||
|
}.onAppear(perform: onAppear)
|
||||||
|
.focusScope(namespace)
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,7 +24,7 @@ struct NextUpView: View {
|
||||||
LazyHStack {
|
LazyHStack {
|
||||||
Spacer().frame(width: 45)
|
Spacer().frame(width: 45)
|
||||||
ForEach(items, id: \.id) { item in
|
ForEach(items, id: \.id) { item in
|
||||||
NavigationLink(destination: VideoPlayerView(item: item)) {
|
NavigationLink(destination: LazyView { ItemView(item: item) }) {
|
||||||
LandscapeItemElement(item: item)
|
LandscapeItemElement(item: item)
|
||||||
}.buttonStyle(PlainNavigationLinkButtonStyle())
|
}.buttonStyle(PlainNavigationLinkButtonStyle())
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ struct VideoPlayerView: UIViewControllerRepresentable {
|
||||||
|
|
||||||
func makeUIViewController(context: Context) -> some UIViewController {
|
func makeUIViewController(context: Context) -> some UIViewController {
|
||||||
|
|
||||||
let storyboard = UIStoryboard(name: "VideoPlayerStoryboard", bundle: nil)
|
let storyboard = UIStoryboard(name: "VideoPlayer", bundle: nil)
|
||||||
let viewController = storyboard.instantiateViewController(withIdentifier: "VideoPlayer") as! VideoPlayerViewController
|
let viewController = storyboard.instantiateViewController(withIdentifier: "VideoPlayer") as! VideoPlayerViewController
|
||||||
viewController.manifest = item
|
viewController.manifest = item
|
||||||
|
|
||||||
|
|
|
@ -244,17 +244,12 @@ class VideoPlayerViewController: UIViewController, VideoPlayerSettingsDelegate,
|
||||||
// Pause and load captions into memory.
|
// Pause and load captions into memory.
|
||||||
mediaPlayer.pause()
|
mediaPlayer.pause()
|
||||||
|
|
||||||
var shouldHaveSubtitleTracks = 0
|
|
||||||
subtitleTrackArray.forEach { sub in
|
subtitleTrackArray.forEach { sub in
|
||||||
if sub.id != -1 && sub.delivery == .external && sub.codec != "subrip" {
|
if sub.id != -1 && sub.delivery == .external {
|
||||||
shouldHaveSubtitleTracks = shouldHaveSubtitleTracks + 1
|
|
||||||
mediaPlayer.addPlaybackSlave(sub.url!, type: .subtitle, enforce: false)
|
mediaPlayer.addPlaybackSlave(sub.url!, type: .subtitle, enforce: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for captions to load
|
|
||||||
while mediaPlayer.numberOfSubtitlesTracks != shouldHaveSubtitleTracks {}
|
|
||||||
|
|
||||||
// Select default track & resume playback
|
// Select default track & resume playback
|
||||||
mediaPlayer.currentVideoSubTitleIndex = selectedCaptionTrack
|
mediaPlayer.currentVideoSubTitleIndex = selectedCaptionTrack
|
||||||
mediaPlayer.pause()
|
mediaPlayer.pause()
|
||||||
|
@ -714,18 +709,8 @@ class VideoPlayerViewController: UIViewController, VideoPlayerSettingsDelegate,
|
||||||
|
|
||||||
// Move time along transport bar
|
// Move time along transport bar
|
||||||
func mediaPlayerTimeChanged(_ aNotification: Notification!) {
|
func mediaPlayerTimeChanged(_ aNotification: Notification!) {
|
||||||
|
|
||||||
if loading {
|
|
||||||
loading = false
|
|
||||||
DispatchQueue.main.async { [self] in
|
|
||||||
activityIndicator.isHidden = true
|
|
||||||
activityIndicator.stopAnimating()
|
|
||||||
}
|
|
||||||
updateNowPlayingCenter(time: nil, playing: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
let time = mediaPlayer.position
|
let time = mediaPlayer.position
|
||||||
if time != lastTime {
|
if abs(time-lastTime) > 0.00005 {
|
||||||
self.currentTimeLabel.text = formatSecondsToHMS(Double(mediaPlayer.time.intValue/1000))
|
self.currentTimeLabel.text = formatSecondsToHMS(Double(mediaPlayer.time.intValue/1000))
|
||||||
self.remainingTimeLabel.text = "-" + formatSecondsToHMS(Double(abs(mediaPlayer.remainingTime.intValue/1000)))
|
self.remainingTimeLabel.text = "-" + formatSecondsToHMS(Double(abs(mediaPlayer.remainingTime.intValue/1000)))
|
||||||
|
|
||||||
|
@ -749,15 +734,23 @@ class VideoPlayerViewController: UIViewController, VideoPlayerSettingsDelegate,
|
||||||
controlsAppearTime = 999_999_999_999_999
|
controlsAppearTime = 999_999_999_999_999
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
lastTime = time
|
||||||
}
|
}
|
||||||
|
|
||||||
lastTime = time
|
|
||||||
|
|
||||||
if CACurrentMediaTime() - lastProgressReportTime > 5 {
|
if CACurrentMediaTime() - lastProgressReportTime > 5 {
|
||||||
|
mediaPlayer.currentVideoSubTitleIndex = selectedCaptionTrack
|
||||||
sendProgressReport(eventName: "timeupdate")
|
sendProgressReport(eventName: "timeupdate")
|
||||||
lastProgressReportTime = CACurrentMediaTime()
|
lastProgressReportTime = CACurrentMediaTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if loading {
|
||||||
|
loading = false
|
||||||
|
DispatchQueue.main.async { [self] in
|
||||||
|
activityIndicator.isHidden = true
|
||||||
|
activityIndicator.stopAnimating()
|
||||||
|
}
|
||||||
|
updateNowPlayingCenter(time: nil, playing: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Settings Delegate
|
// MARK: Settings Delegate
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
5310695A2684E7EE00CFFDBA /* VideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531069532684E7EE00CFFDBA /* VideoPlayer.swift */; };
|
5310695A2684E7EE00CFFDBA /* VideoPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531069532684E7EE00CFFDBA /* VideoPlayer.swift */; };
|
||||||
5310695B2684E7EE00CFFDBA /* AudioView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531069542684E7EE00CFFDBA /* AudioView.swift */; };
|
5310695B2684E7EE00CFFDBA /* AudioView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531069542684E7EE00CFFDBA /* AudioView.swift */; };
|
||||||
5310695C2684E7EE00CFFDBA /* VideoPlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531069552684E7EE00CFFDBA /* VideoPlayerViewController.swift */; };
|
5310695C2684E7EE00CFFDBA /* VideoPlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531069552684E7EE00CFFDBA /* VideoPlayerViewController.swift */; };
|
||||||
5310695D2684E7EE00CFFDBA /* VideoPlayerStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 531069562684E7EE00CFFDBA /* VideoPlayerStoryboard.storyboard */; };
|
5310695D2684E7EE00CFFDBA /* VideoPlayer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 531069562684E7EE00CFFDBA /* VideoPlayer.storyboard */; };
|
||||||
531690E5267ABD5C005D8AB9 /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690E4267ABD5C005D8AB9 /* MainTabView.swift */; };
|
531690E5267ABD5C005D8AB9 /* MainTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690E4267ABD5C005D8AB9 /* MainTabView.swift */; };
|
||||||
531690E7267ABD79005D8AB9 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690E6267ABD79005D8AB9 /* HomeView.swift */; };
|
531690E7267ABD79005D8AB9 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690E6267ABD79005D8AB9 /* HomeView.swift */; };
|
||||||
531690ED267ABF46005D8AB9 /* ContinueWatchingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690EB267ABF46005D8AB9 /* ContinueWatchingView.swift */; };
|
531690ED267ABF46005D8AB9 /* ContinueWatchingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531690EB267ABF46005D8AB9 /* ContinueWatchingView.swift */; };
|
||||||
|
@ -80,6 +80,7 @@
|
||||||
53A089D0264DA9DA00D57806 /* MovieItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A089CF264DA9DA00D57806 /* MovieItemView.swift */; };
|
53A089D0264DA9DA00D57806 /* MovieItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A089CF264DA9DA00D57806 /* MovieItemView.swift */; };
|
||||||
53A431BD266B0FF20016769F /* JellyfinAPI in Frameworks */ = {isa = PBXBuildFile; productRef = 53A431BC266B0FF20016769F /* JellyfinAPI */; };
|
53A431BD266B0FF20016769F /* JellyfinAPI in Frameworks */ = {isa = PBXBuildFile; productRef = 53A431BC266B0FF20016769F /* JellyfinAPI */; };
|
||||||
53A431BF266B0FFE0016769F /* JellyfinAPI in Frameworks */ = {isa = PBXBuildFile; productRef = 53A431BE266B0FFE0016769F /* JellyfinAPI */; };
|
53A431BF266B0FFE0016769F /* JellyfinAPI in Frameworks */ = {isa = PBXBuildFile; productRef = 53A431BE266B0FFE0016769F /* JellyfinAPI */; };
|
||||||
|
53A83C33268A309300DF3D92 /* LibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A83C32268A309300DF3D92 /* LibraryView.swift */; };
|
||||||
53ABFDDC267972BF00886593 /* TVServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53ABFDDB267972BF00886593 /* TVServices.framework */; };
|
53ABFDDC267972BF00886593 /* TVServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53ABFDDB267972BF00886593 /* TVServices.framework */; };
|
||||||
53ABFDDE267974E300886593 /* SplashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53ABFDDD267974E300886593 /* SplashView.swift */; };
|
53ABFDDE267974E300886593 /* SplashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53ABFDDD267974E300886593 /* SplashView.swift */; };
|
||||||
53ABFDE4267974EF00886593 /* LibraryListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5742678C33500530A6E /* LibraryListViewModel.swift */; };
|
53ABFDE4267974EF00886593 /* LibraryListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 625CB5742678C33500530A6E /* LibraryListViewModel.swift */; };
|
||||||
|
@ -93,6 +94,8 @@
|
||||||
53ABFDEE26799DCD00886593 /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531AC8BE26750DE20091C7EB /* ImageView.swift */; };
|
53ABFDEE26799DCD00886593 /* ImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 531AC8BE26750DE20091C7EB /* ImageView.swift */; };
|
||||||
53AD124D267029D60094A276 /* SeriesItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53987CA526572F0700E7EA70 /* SeriesItemView.swift */; };
|
53AD124D267029D60094A276 /* SeriesItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53987CA526572F0700E7EA70 /* SeriesItemView.swift */; };
|
||||||
53AD124E26702B8A0094A276 /* SeasonItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53987CA326572C1300E7EA70 /* SeasonItemView.swift */; };
|
53AD124E26702B8A0094A276 /* SeasonItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53987CA326572C1300E7EA70 /* SeasonItemView.swift */; };
|
||||||
|
53CD2A40268A49C2002ABD4E /* ItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53CD2A3F268A49C2002ABD4E /* ItemView.swift */; };
|
||||||
|
53CD2A42268A4B38002ABD4E /* MovieItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53CD2A41268A4B38002ABD4E /* MovieItemView.swift */; };
|
||||||
53DE4BD02670961400739748 /* EpisodeItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53987CA72657424A00E7EA70 /* EpisodeItemView.swift */; };
|
53DE4BD02670961400739748 /* EpisodeItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53987CA72657424A00E7EA70 /* EpisodeItemView.swift */; };
|
||||||
53DE4BD2267098F300739748 /* SearchBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53DE4BD1267098F300739748 /* SearchBarView.swift */; };
|
53DE4BD2267098F300739748 /* SearchBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53DE4BD1267098F300739748 /* SearchBarView.swift */; };
|
||||||
53DF641E263D9C0600A7CD1A /* LibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53DF641D263D9C0600A7CD1A /* LibraryView.swift */; };
|
53DF641E263D9C0600A7CD1A /* LibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53DF641D263D9C0600A7CD1A /* LibraryView.swift */; };
|
||||||
|
@ -214,7 +217,7 @@
|
||||||
531069532684E7EE00CFFDBA /* VideoPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoPlayer.swift; sourceTree = "<group>"; };
|
531069532684E7EE00CFFDBA /* VideoPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoPlayer.swift; sourceTree = "<group>"; };
|
||||||
531069542684E7EE00CFFDBA /* AudioView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioView.swift; sourceTree = "<group>"; };
|
531069542684E7EE00CFFDBA /* AudioView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AudioView.swift; sourceTree = "<group>"; };
|
||||||
531069552684E7EE00CFFDBA /* VideoPlayerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoPlayerViewController.swift; sourceTree = "<group>"; };
|
531069552684E7EE00CFFDBA /* VideoPlayerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoPlayerViewController.swift; sourceTree = "<group>"; };
|
||||||
531069562684E7EE00CFFDBA /* VideoPlayerStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = VideoPlayerStoryboard.storyboard; sourceTree = "<group>"; };
|
531069562684E7EE00CFFDBA /* VideoPlayer.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = VideoPlayer.storyboard; sourceTree = "<group>"; };
|
||||||
531690E4267ABD5C005D8AB9 /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = "<group>"; };
|
531690E4267ABD5C005D8AB9 /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = "<group>"; };
|
||||||
531690E6267ABD79005D8AB9 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
|
531690E6267ABD79005D8AB9 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
|
||||||
531690EB267ABF46005D8AB9 /* ContinueWatchingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContinueWatchingView.swift; sourceTree = "<group>"; };
|
531690EB267ABF46005D8AB9 /* ContinueWatchingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContinueWatchingView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -276,11 +279,14 @@
|
||||||
53987CA72657424A00E7EA70 /* EpisodeItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EpisodeItemView.swift; sourceTree = "<group>"; };
|
53987CA72657424A00E7EA70 /* EpisodeItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EpisodeItemView.swift; sourceTree = "<group>"; };
|
||||||
539B2DA4263BA5B8007FF1A4 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
539B2DA4263BA5B8007FF1A4 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||||
53A089CF264DA9DA00D57806 /* MovieItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieItemView.swift; sourceTree = "<group>"; };
|
53A089CF264DA9DA00D57806 /* MovieItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieItemView.swift; sourceTree = "<group>"; };
|
||||||
|
53A83C32268A309300DF3D92 /* LibraryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryView.swift; sourceTree = "<group>"; };
|
||||||
53ABFDDA267972BF00886593 /* JellyfinPlayer tvOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "JellyfinPlayer tvOS.entitlements"; sourceTree = "<group>"; };
|
53ABFDDA267972BF00886593 /* JellyfinPlayer tvOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "JellyfinPlayer tvOS.entitlements"; sourceTree = "<group>"; };
|
||||||
53ABFDDB267972BF00886593 /* TVServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TVServices.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS15.0.sdk/System/Library/Frameworks/TVServices.framework; sourceTree = DEVELOPER_DIR; };
|
53ABFDDB267972BF00886593 /* TVServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TVServices.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS15.0.sdk/System/Library/Frameworks/TVServices.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
53ABFDDD267974E300886593 /* SplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashView.swift; sourceTree = "<group>"; };
|
53ABFDDD267974E300886593 /* SplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashView.swift; sourceTree = "<group>"; };
|
||||||
53ABFDEA2679753200886593 /* ConnectToServerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectToServerView.swift; sourceTree = "<group>"; };
|
53ABFDEA2679753200886593 /* ConnectToServerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectToServerView.swift; sourceTree = "<group>"; };
|
||||||
53AD124C2670278D0094A276 /* JellyfinPlayer.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = JellyfinPlayer.entitlements; sourceTree = "<group>"; };
|
53AD124C2670278D0094A276 /* JellyfinPlayer.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = JellyfinPlayer.entitlements; sourceTree = "<group>"; };
|
||||||
|
53CD2A3F268A49C2002ABD4E /* ItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemView.swift; sourceTree = "<group>"; };
|
||||||
|
53CD2A41268A4B38002ABD4E /* MovieItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieItemView.swift; sourceTree = "<group>"; };
|
||||||
53D5E3DC264B47EE00BADDC8 /* MobileVLCKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MobileVLCKit.xcframework; path = Carthage/Build/MobileVLCKit.xcframework; sourceTree = "<group>"; };
|
53D5E3DC264B47EE00BADDC8 /* MobileVLCKit.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MobileVLCKit.xcframework; path = Carthage/Build/MobileVLCKit.xcframework; sourceTree = "<group>"; };
|
||||||
53DE4BD1267098F300739748 /* SearchBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBarView.swift; sourceTree = "<group>"; };
|
53DE4BD1267098F300739748 /* SearchBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBarView.swift; sourceTree = "<group>"; };
|
||||||
53DF641D263D9C0600A7CD1A /* LibraryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryView.swift; sourceTree = "<group>"; };
|
53DF641D263D9C0600A7CD1A /* LibraryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -400,7 +406,7 @@
|
||||||
531069502684E7EE00CFFDBA /* InfoTabBarViewController.swift */,
|
531069502684E7EE00CFFDBA /* InfoTabBarViewController.swift */,
|
||||||
531069532684E7EE00CFFDBA /* VideoPlayer.swift */,
|
531069532684E7EE00CFFDBA /* VideoPlayer.swift */,
|
||||||
531069552684E7EE00CFFDBA /* VideoPlayerViewController.swift */,
|
531069552684E7EE00CFFDBA /* VideoPlayerViewController.swift */,
|
||||||
531069562684E7EE00CFFDBA /* VideoPlayerStoryboard.storyboard */,
|
531069562684E7EE00CFFDBA /* VideoPlayer.storyboard */,
|
||||||
);
|
);
|
||||||
path = VideoPlayer;
|
path = VideoPlayer;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -449,6 +455,9 @@
|
||||||
531690E6267ABD79005D8AB9 /* HomeView.swift */,
|
531690E6267ABD79005D8AB9 /* HomeView.swift */,
|
||||||
531690F8267AD135005D8AB9 /* README.md */,
|
531690F8267AD135005D8AB9 /* README.md */,
|
||||||
536D3D7E267BDF100004248C /* LatestMediaView.swift */,
|
536D3D7E267BDF100004248C /* LatestMediaView.swift */,
|
||||||
|
53A83C32268A309300DF3D92 /* LibraryView.swift */,
|
||||||
|
53CD2A3F268A49C2002ABD4E /* ItemView.swift */,
|
||||||
|
53CD2A41268A4B38002ABD4E /* MovieItemView.swift */,
|
||||||
);
|
);
|
||||||
path = "JellyfinPlayer tvOS";
|
path = "JellyfinPlayer tvOS";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -806,7 +815,7 @@
|
||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
5310695D2684E7EE00CFFDBA /* VideoPlayerStoryboard.storyboard in Resources */,
|
5310695D2684E7EE00CFFDBA /* VideoPlayer.storyboard in Resources */,
|
||||||
5358706A2669D21700D05A09 /* Preview Assets.xcassets in Resources */,
|
5358706A2669D21700D05A09 /* Preview Assets.xcassets in Resources */,
|
||||||
535870672669D21700D05A09 /* Assets.xcassets in Resources */,
|
535870672669D21700D05A09 /* Assets.xcassets in Resources */,
|
||||||
5358707E2669D64F00D05A09 /* bitrates.json in Resources */,
|
5358707E2669D64F00D05A09 /* bitrates.json in Resources */,
|
||||||
|
@ -948,6 +957,8 @@
|
||||||
62E632DE267D2E170063E547 /* LatestMediaViewModel.swift in Sources */,
|
62E632DE267D2E170063E547 /* LatestMediaViewModel.swift in Sources */,
|
||||||
536D3D88267C17350004248C /* PublicUserButton.swift in Sources */,
|
536D3D88267C17350004248C /* PublicUserButton.swift in Sources */,
|
||||||
62E632EA267D3FF50063E547 /* SeasonItemViewModel.swift in Sources */,
|
62E632EA267D3FF50063E547 /* SeasonItemViewModel.swift in Sources */,
|
||||||
|
53CD2A40268A49C2002ABD4E /* ItemView.swift in Sources */,
|
||||||
|
53CD2A42268A4B38002ABD4E /* MovieItemView.swift in Sources */,
|
||||||
536D3D7F267BDF100004248C /* LatestMediaView.swift in Sources */,
|
536D3D7F267BDF100004248C /* LatestMediaView.swift in Sources */,
|
||||||
091B5A8E268315D400D78B61 /* UDPBroadCastConnection.swift in Sources */,
|
091B5A8E268315D400D78B61 /* UDPBroadCastConnection.swift in Sources */,
|
||||||
531690ED267ABF46005D8AB9 /* ContinueWatchingView.swift in Sources */,
|
531690ED267ABF46005D8AB9 /* ContinueWatchingView.swift in Sources */,
|
||||||
|
@ -955,6 +966,7 @@
|
||||||
531690F7267ACC00005D8AB9 /* LandscapeItemElement.swift in Sources */,
|
531690F7267ACC00005D8AB9 /* LandscapeItemElement.swift in Sources */,
|
||||||
62E632E1267D30CA0063E547 /* LibraryViewModel.swift in Sources */,
|
62E632E1267D30CA0063E547 /* LibraryViewModel.swift in Sources */,
|
||||||
535870A82669D8AE00D05A09 /* StringExtensions.swift in Sources */,
|
535870A82669D8AE00D05A09 /* StringExtensions.swift in Sources */,
|
||||||
|
53A83C33268A309300DF3D92 /* LibraryView.swift in Sources */,
|
||||||
62E632ED267D410B0063E547 /* SeriesItemViewModel.swift in Sources */,
|
62E632ED267D410B0063E547 /* SeriesItemViewModel.swift in Sources */,
|
||||||
62E632F0267D43320063E547 /* LibraryFilterViewModel.swift in Sources */,
|
62E632F0267D43320063E547 /* LibraryFilterViewModel.swift in Sources */,
|
||||||
5310695A2684E7EE00CFFDBA /* VideoPlayer.swift in Sources */,
|
5310695A2684E7EE00CFFDBA /* VideoPlayer.swift in Sources */,
|
||||||
|
@ -1471,8 +1483,8 @@
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/kean/NukeUI";
|
repositoryURL = "https://github.com/kean/NukeUI";
|
||||||
requirement = {
|
requirement = {
|
||||||
kind = upToNextMajorVersion;
|
kind = exactVersion;
|
||||||
minimumVersion = 0.3.0;
|
version = 0.3.0;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
625CB5782678C4A400530A6E /* XCRemoteSwiftPackageReference "ActivityIndicator" */ = {
|
625CB5782678C4A400530A6E /* XCRemoteSwiftPackageReference "ActivityIndicator" */ = {
|
||||||
|
|
|
@ -69,8 +69,8 @@
|
||||||
"repositoryURL": "https://github.com/kean/NukeUI",
|
"repositoryURL": "https://github.com/kean/NukeUI",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "4516371912149ac024dec361827931b46a69c217",
|
"revision": "d2580b8d22b29c6244418d8e4b568f3162191460",
|
||||||
"version": "0.6.2"
|
"version": "0.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -67,7 +67,7 @@ struct ConnectToServerView: View {
|
||||||
Text(publicUser.name ?? "").font(.subheadline).fontWeight(.semibold)
|
Text(publicUser.name ?? "").font(.subheadline).fontWeight(.semibold)
|
||||||
Spacer()
|
Spacer()
|
||||||
if publicUser.primaryImageTag != nil {
|
if publicUser.primaryImageTag != nil {
|
||||||
ImageView(src: URL(string: "\(ServerEnvironment.current.server.baseURI ?? "")/Users/\(publicUser.id ?? "")/Images/Primary?width=120&quality=80&tag=\(publicUser.primaryImageTag!)")!)
|
ImageView(src: URL(string: "\(ServerEnvironment.current.server.baseURI ?? "")/Users/\(publicUser.id ?? "")/Images/Primary?width=60&quality=80&tag=\(publicUser.primaryImageTag!)")!)
|
||||||
.frame(width: 60, height: 60)
|
.frame(width: 60, height: 60)
|
||||||
.cornerRadius(30.0)
|
.cornerRadius(30.0)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -70,7 +70,7 @@ extension BaseItemDto {
|
||||||
}
|
}
|
||||||
|
|
||||||
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
||||||
let urlString = "\(ServerEnvironment.current.server.baseURI!)/Items/\(imageItemId)/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=60&tag=\(imageTag)"
|
let urlString = "\(ServerEnvironment.current.server.baseURI!)/Items/\(imageItemId)/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=96&tag=\(imageTag)"
|
||||||
return URL(string: urlString)!
|
return URL(string: urlString)!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ extension BaseItemDto {
|
||||||
let imageTag = (self.parentBackdropImageTags ?? [""])[0]
|
let imageTag = (self.parentBackdropImageTags ?? [""])[0]
|
||||||
|
|
||||||
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
||||||
let urlString = "\(ServerEnvironment.current.server.baseURI!)/Items/\(self.parentBackdropItemId ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=60&tag=\(imageTag)"
|
let urlString = "\(ServerEnvironment.current.server.baseURI!)/Items/\(self.parentBackdropItemId ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=96&tag=\(imageTag)"
|
||||||
return URL(string: urlString)!
|
return URL(string: urlString)!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ extension BaseItemDto {
|
||||||
let imageType = "Primary"
|
let imageType = "Primary"
|
||||||
let imageTag = self.seriesPrimaryImageTag ?? ""
|
let imageTag = self.seriesPrimaryImageTag ?? ""
|
||||||
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
||||||
let urlString = "\(ServerEnvironment.current.server.baseURI!)/Items/\(self.seriesId ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=60&tag=\(imageTag)"
|
let urlString = "\(ServerEnvironment.current.server.baseURI!)/Items/\(self.seriesId ?? "")/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=96&tag=\(imageTag)"
|
||||||
return URL(string: urlString)!
|
return URL(string: urlString)!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ extension BaseItemDto {
|
||||||
|
|
||||||
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
let x = UIScreen.main.nativeScale * CGFloat(maxWidth)
|
||||||
|
|
||||||
let urlString = "\(ServerEnvironment.current.server.baseURI!)/Items/\(imageItemId)/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=60&tag=\(imageTag)"
|
let urlString = "\(ServerEnvironment.current.server.baseURI!)/Items/\(imageItemId)/Images/\(imageType)?maxWidth=\(String(Int(x)))&quality=96&tag=\(imageTag)"
|
||||||
return URL(string: urlString)!
|
return URL(string: urlString)!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,16 +24,14 @@ struct ImageView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
LazyImage(source: source) { state in
|
LazyImage(source: source)
|
||||||
if let image = state.image {
|
.placeholder {
|
||||||
image
|
Image(uiImage: UIImage(blurHash: blurhash, size: CGSize(width: 8, height: 8))!)
|
||||||
} else if state.error != nil {
|
.resizable()
|
||||||
Rectangle()
|
}
|
||||||
.fill(Color.gray)
|
.failure {
|
||||||
} else {
|
Rectangle()
|
||||||
Image(uiImage: UIImage(blurHash: blurhash, size: CGSize(width: 16, height: 16))!)
|
.background(Color.gray)
|
||||||
.resizable()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -210,17 +210,9 @@ open class UDPBroadcastConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
guard sent > 0 else {
|
guard sent > 0 else {
|
||||||
if let errorString = String(validatingUTF8: strerror(errno)) {
|
|
||||||
// debugPrint("UDP connection failed to send data: \(errorString)")
|
|
||||||
}
|
|
||||||
closeConnection()
|
closeConnection()
|
||||||
throw ConnectionError.sendingMessageFailed(code: errno)
|
throw ConnectionError.sendingMessageFailed(code: errno)
|
||||||
}
|
}
|
||||||
|
|
||||||
if sent == broadcastMessageLength {
|
|
||||||
// Success
|
|
||||||
// debugPrint("UDP connection sent \(broadcastMessageLength) bytes")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,11 +88,45 @@ final class LibraryViewModel: ViewModel {
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func requestItemsAsync(with filters: LibraryFilters) {
|
||||||
|
let personIDs: [String] = [person].compactMap(\.?.id)
|
||||||
|
let studioIDs: [String] = [studio].compactMap(\.?.id)
|
||||||
|
let genreIDs: [String]
|
||||||
|
if filters.withGenres.isEmpty {
|
||||||
|
genreIDs = [genre].compactMap(\.?.id)
|
||||||
|
} else {
|
||||||
|
genreIDs = filters.withGenres.compactMap(\.id)
|
||||||
|
}
|
||||||
|
let sortBy = filters.sortBy.map(\.rawValue)
|
||||||
|
|
||||||
|
ItemsAPI.getItemsByUserId(userId: SessionManager.current.user.user_id!, startIndex: currentPage * 100, limit: 100, recursive: filters.filters.contains(.isFavorite) ? true : false,
|
||||||
|
searchTerm: nil, sortOrder: filters.sortOrder, parentId: parentID,
|
||||||
|
fields: [.primaryImageAspectRatio, .seriesPrimaryImage, .seasonUserData, .overview, .genres, .people],
|
||||||
|
filters: filters.filters, sortBy: sortBy, tags: filters.tags,
|
||||||
|
enableUserData: true, personIds: personIDs, studioIds: studioIDs, genreIds: genreIDs, enableImages: true)
|
||||||
|
.sink(receiveCompletion: { [weak self] completion in
|
||||||
|
self?.handleAPIRequestCompletion(completion: completion)
|
||||||
|
}, receiveValue: { [weak self] response in
|
||||||
|
guard let self = self else { return }
|
||||||
|
let totalPages = ceil(Double(response.totalRecordCount ?? 0) / 100.0)
|
||||||
|
self.totalPages = Int(totalPages)
|
||||||
|
self.hasPreviousPage = self.currentPage > 0
|
||||||
|
self.hasNextPage = self.currentPage < self.totalPages - 1
|
||||||
|
self.items.append(contentsOf: response.items ?? [])
|
||||||
|
})
|
||||||
|
.store(in: &cancellables)
|
||||||
|
}
|
||||||
|
|
||||||
func requestNextPage() {
|
func requestNextPage() {
|
||||||
currentPage += 1
|
currentPage += 1
|
||||||
requestItems(with: filters)
|
requestItems(with: filters)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func requestNextPageAsync() {
|
||||||
|
currentPage += 1
|
||||||
|
requestItemsAsync(with: filters)
|
||||||
|
}
|
||||||
|
|
||||||
func requestPreviousPage() {
|
func requestPreviousPage() {
|
||||||
currentPage -= 1
|
currentPage -= 1
|
||||||
requestItems(with: filters)
|
requestItems(with: filters)
|
||||||
|
|
Loading…
Reference in New Issue