mirror of
https://github.com/jellyfin/Swiftfin.git
synced 2024-11-23 14:10:01 +00:00
initial iOS LiveTV coordination
This commit is contained in:
parent
9c41420ecd
commit
4dac5dd0b9
@ -20,6 +20,8 @@ final class LibraryListCoordinator: NavigationCoordinatable {
|
||||
var search = makeSearch
|
||||
@Route(.push)
|
||||
var library = makeLibrary
|
||||
@Route(.push)
|
||||
var liveTV = makeLiveTV
|
||||
|
||||
let viewModel: LibraryListViewModel
|
||||
|
||||
@ -34,6 +36,10 @@ final class LibraryListCoordinator: NavigationCoordinatable {
|
||||
func makeSearch(viewModel: LibrarySearchViewModel) -> SearchCoordinator {
|
||||
SearchCoordinator(viewModel: viewModel)
|
||||
}
|
||||
|
||||
func makeLiveTV() -> LiveTVCoordinator {
|
||||
LiveTVCoordinator()
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func makeStart() -> some View {
|
||||
|
@ -24,7 +24,7 @@ final class LiveTVChannelsCoordinator: NavigationCoordinatable {
|
||||
func makeModalItem(item: BaseItemDto) -> NavigationViewCoordinator<ItemCoordinator> {
|
||||
NavigationViewCoordinator(ItemCoordinator(item: item))
|
||||
}
|
||||
|
||||
|
||||
func makeVideoPlayer(viewModel: VideoPlayerViewModel) -> NavigationViewCoordinator<LiveTVVideoPlayerCoordinator> {
|
||||
NavigationViewCoordinator(LiveTVVideoPlayerCoordinator(viewModel: viewModel))
|
||||
}
|
||||
|
30
Shared/Coordinators/LiveTVCoordinator.swift
Normal file
30
Shared/Coordinators/LiveTVCoordinator.swift
Normal file
@ -0,0 +1,30 @@
|
||||
//
|
||||
// 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) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import JellyfinAPI
|
||||
import Stinsen
|
||||
import SwiftUI
|
||||
|
||||
final class LiveTVCoordinator: NavigationCoordinatable {
|
||||
let stack = NavigationStack(initial: \LiveTVCoordinator.start)
|
||||
|
||||
@Root
|
||||
var start = makeStart
|
||||
// @Route(.push)
|
||||
// var search = makeSearch
|
||||
|
||||
@ViewBuilder
|
||||
func makeStart() -> some View {
|
||||
LiveTVChannelsView()
|
||||
}
|
||||
|
||||
// func makeSearch(viewModel: LibrarySearchViewModel) -> SearchCoordinator {
|
||||
// SearchCoordinator(viewModel: viewModel)
|
||||
// }
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
//
|
||||
// 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) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Defaults
|
||||
import Foundation
|
||||
import JellyfinAPI
|
||||
import Stinsen
|
||||
import SwiftUI
|
||||
|
||||
final class LiveTVVideoPlayerCoordinator: NavigationCoordinatable {
|
||||
|
||||
let stack = NavigationStack(initial: \LiveTVVideoPlayerCoordinator.start)
|
||||
|
||||
@Root
|
||||
var start = makeStart
|
||||
|
||||
let viewModel: VideoPlayerViewModel
|
||||
|
||||
init(viewModel: VideoPlayerViewModel) {
|
||||
self.viewModel = viewModel
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func makeStart() -> some View {
|
||||
// if Defaults[.Experimental.liveTVNativePlayer] {
|
||||
// LiveTVNativeVideoPlayerView(viewModel: viewModel)
|
||||
// .navigationBarHidden(true)
|
||||
// .ignoresSafeArea()
|
||||
// } else {
|
||||
LiveTVPlayerView(viewModel: viewModel)
|
||||
.navigationBarHidden(true)
|
||||
.ignoresSafeArea()
|
||||
// }
|
||||
}
|
||||
}
|
@ -120,7 +120,9 @@ struct LiveTVChannelItemElement: View {
|
||||
.stroke(isFocused ? Color.blue : Color.clear, lineWidth: 4))
|
||||
.cornerRadius(20)
|
||||
.scaleEffect(isFocused ? 1.1 : 1)
|
||||
#if os(tvOS)
|
||||
.focusable(true)
|
||||
#endif
|
||||
.focused($focused)
|
||||
.onChange(of: focused) { foc in
|
||||
withAnimation(.linear(duration: 0.15)) {
|
||||
|
@ -259,6 +259,14 @@
|
||||
C4534981279A3F140045F1E2 /* tvOSLiveTVOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4534980279A3F140045F1E2 /* tvOSLiveTVOverlay.swift */; };
|
||||
C4534983279A40990045F1E2 /* tvOSLiveTVVideoPlayerCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4534982279A40990045F1E2 /* tvOSLiveTVVideoPlayerCoordinator.swift */; };
|
||||
C4534985279A40C60045F1E2 /* LiveTVVideoPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4534984279A40C50045F1E2 /* LiveTVVideoPlayerView.swift */; };
|
||||
C45942C527F67DA400C54FE7 /* LiveTVCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45942C427F67DA400C54FE7 /* LiveTVCoordinator.swift */; };
|
||||
C45942C627F695FB00C54FE7 /* LiveTVProgramsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BE07702725EB06003F4AD1 /* LiveTVProgramsCoordinator.swift */; };
|
||||
C45942C927F697CA00C54FE7 /* iOSLiveTVVideoPlayerCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45942C827F697CA00C54FE7 /* iOSLiveTVVideoPlayerCoordinator.swift */; };
|
||||
C45942CB27F6984100C54FE7 /* LiveTVPlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45942CA27F6984100C54FE7 /* LiveTVPlayerViewController.swift */; };
|
||||
C45942CD27F6994A00C54FE7 /* LiveTVPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C45942CC27F6994A00C54FE7 /* LiveTVPlayerView.swift */; };
|
||||
C45942CE27F69BF300C54FE7 /* LiveTVChannelsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BE078A272844AF003F4AD1 /* LiveTVChannelsView.swift */; };
|
||||
C45942CF27F69BF500C54FE7 /* LiveTVChannelItemElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E52304272CE68800654268 /* LiveTVChannelItemElement.swift */; };
|
||||
C45942D027F69C2400C54FE7 /* LiveTVChannelsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BE07872728448B003F4AD1 /* LiveTVChannelsCoordinator.swift */; };
|
||||
C45B29BB26FAC5B600CEF5E0 /* ColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E173DA5126D04AAF00CC4EB7 /* ColorExtension.swift */; };
|
||||
C4AE2C3027498D2300AE13CF /* LiveTVHomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AE2C2F27498D2300AE13CF /* LiveTVHomeView.swift */; };
|
||||
C4AE2C3227498D6A00AE13CF /* LiveTVProgramsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AE2C3127498D6A00AE13CF /* LiveTVProgramsView.swift */; };
|
||||
@ -739,6 +747,10 @@
|
||||
C4534980279A3F140045F1E2 /* tvOSLiveTVOverlay.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = tvOSLiveTVOverlay.swift; sourceTree = "<group>"; };
|
||||
C4534982279A40990045F1E2 /* tvOSLiveTVVideoPlayerCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = tvOSLiveTVVideoPlayerCoordinator.swift; sourceTree = "<group>"; };
|
||||
C4534984279A40C50045F1E2 /* LiveTVVideoPlayerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LiveTVVideoPlayerView.swift; sourceTree = "<group>"; };
|
||||
C45942C427F67DA400C54FE7 /* LiveTVCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTVCoordinator.swift; sourceTree = "<group>"; };
|
||||
C45942C827F697CA00C54FE7 /* iOSLiveTVVideoPlayerCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSLiveTVVideoPlayerCoordinator.swift; sourceTree = "<group>"; };
|
||||
C45942CA27F6984100C54FE7 /* LiveTVPlayerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTVPlayerViewController.swift; sourceTree = "<group>"; };
|
||||
C45942CC27F6994A00C54FE7 /* LiveTVPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTVPlayerView.swift; sourceTree = "<group>"; };
|
||||
C4AE2C2F27498D2300AE13CF /* LiveTVHomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTVHomeView.swift; sourceTree = "<group>"; };
|
||||
C4AE2C3127498D6A00AE13CF /* LiveTVProgramsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTVProgramsView.swift; sourceTree = "<group>"; };
|
||||
C4B9B91327E1921B0063535C /* LiveTVNativeVideoPlayerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTVNativeVideoPlayerView.swift; sourceTree = "<group>"; };
|
||||
@ -1466,6 +1478,7 @@
|
||||
C4BE07872728448B003F4AD1 /* LiveTVChannelsCoordinator.swift */,
|
||||
C4BE07702725EB06003F4AD1 /* LiveTVProgramsCoordinator.swift */,
|
||||
C4BE07782726EE82003F4AD1 /* LiveTVTabCoordinator.swift */,
|
||||
C45942C427F67DA400C54FE7 /* LiveTVCoordinator.swift */,
|
||||
E193D5412719404B00900D82 /* MainCoordinator */,
|
||||
C40CD921271F8CD8000FB198 /* MoviesLibrariesCoordinator.swift */,
|
||||
6220D0B626D5EE1100B8E046 /* SearchCoordinator.swift */,
|
||||
@ -1724,7 +1737,9 @@
|
||||
E1002B692793E12E00E47059 /* Overlays */,
|
||||
E1C812B5277A8E5D00918266 /* PlayerOverlayDelegate.swift */,
|
||||
E1C812B8277A8E5D00918266 /* VLCPlayerView.swift */,
|
||||
C45942CC27F6994A00C54FE7 /* LiveTVPlayerView.swift */,
|
||||
E1C812B6277A8E5D00918266 /* VLCPlayerViewController.swift */,
|
||||
C45942CA27F6984100C54FE7 /* LiveTVPlayerViewController.swift */,
|
||||
);
|
||||
path = VideoPlayer;
|
||||
sourceTree = "<group>";
|
||||
@ -1801,6 +1816,7 @@
|
||||
E1C812CF277AE4C700918266 /* VideoPlayerCoordinator */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C45942C827F697CA00C54FE7 /* iOSLiveTVVideoPlayerCoordinator.swift */,
|
||||
6220D0C526D62D8700B8E046 /* iOSVideoPlayerCoordinator.swift */,
|
||||
C4534982279A40990045F1E2 /* tvOSLiveTVVideoPlayerCoordinator.swift */,
|
||||
E1C812D0277AE4E300918266 /* tvOSVideoPlayerCoordinator.swift */,
|
||||
@ -2374,6 +2390,7 @@
|
||||
62C29E9F26D1016600C1D2E7 /* iOSMainCoordinator.swift in Sources */,
|
||||
5389276E263C25100035E14B /* ContinueWatchingView.swift in Sources */,
|
||||
53F866442687A45F00DCD1D7 /* PortraitItemButton.swift in Sources */,
|
||||
C45942CD27F6994A00C54FE7 /* LiveTVPlayerView.swift in Sources */,
|
||||
E1AD105626D981CE003E4A08 /* PortraitHStackView.swift in Sources */,
|
||||
62C29EA126D102A500C1D2E7 /* iOSMainTabCoordinator.swift in Sources */,
|
||||
C4BE076E2720FEA8003F4AD1 /* PortraitItemElement.swift in Sources */,
|
||||
@ -2392,9 +2409,11 @@
|
||||
5D160403278A41FD00D22B99 /* VLCPlayer+subtitles.swift in Sources */,
|
||||
536D3D78267BD5C30004248C /* ViewModel.swift in Sources */,
|
||||
E1A2C158279A7D76005EC829 /* BundleExtensions.swift in Sources */,
|
||||
C45942C627F695FB00C54FE7 /* LiveTVProgramsCoordinator.swift in Sources */,
|
||||
E1FCD08826C35A0D007C8DCF /* NetworkError.swift in Sources */,
|
||||
E13AD72E2798BC8D00FDCEE8 /* NativePlayerViewController.swift in Sources */,
|
||||
E13DD3E527177D15009D4DAF /* ServerListView.swift in Sources */,
|
||||
C45942CB27F6984100C54FE7 /* LiveTVPlayerViewController.swift in Sources */,
|
||||
E18845F826DEA9C900B0C5B7 /* ItemViewBody.swift in Sources */,
|
||||
E173DA5426D050F500CC4EB7 /* ServerDetailViewModel.swift in Sources */,
|
||||
E19169CE272514760085832A /* HTTPScheme.swift in Sources */,
|
||||
@ -2412,6 +2431,7 @@
|
||||
E1C812BC277A8E5D00918266 /* PlaybackSpeed.swift in Sources */,
|
||||
E1E5D5492783CDD700692DFE /* OverlaySettingsView.swift in Sources */,
|
||||
E173DA5226D04AAF00CC4EB7 /* ColorExtension.swift in Sources */,
|
||||
C45942D027F69C2400C54FE7 /* LiveTVChannelsCoordinator.swift in Sources */,
|
||||
E11D224227378428003F9CB3 /* ServerDetailCoordinator.swift in Sources */,
|
||||
6264E88C273850380081A12A /* Strings.swift in Sources */,
|
||||
C4AE2C3227498D6A00AE13CF /* LiveTVProgramsView.swift in Sources */,
|
||||
@ -2463,6 +2483,7 @@
|
||||
C4BE07762725EBEA003F4AD1 /* LiveTVProgramsViewModel.swift in Sources */,
|
||||
E13DD3E927177ED6009D4DAF /* ServerListCoordinator.swift in Sources */,
|
||||
E1C812BD277A8E5D00918266 /* PlayerOverlayDelegate.swift in Sources */,
|
||||
C45942C527F67DA400C54FE7 /* LiveTVCoordinator.swift in Sources */,
|
||||
E13DD3C227164941009D4DAF /* SwiftfinStore.swift in Sources */,
|
||||
62E632E0267D30CA0063E547 /* LibraryViewModel.swift in Sources */,
|
||||
E193D4DB27193CCA00900D82 /* PillStackable.swift in Sources */,
|
||||
@ -2480,6 +2501,7 @@
|
||||
091B5A8A2683142E00D78B61 /* ServerDiscovery.swift in Sources */,
|
||||
62E632EF267D43320063E547 /* LibraryFilterViewModel.swift in Sources */,
|
||||
5D64683D277B1649009E09AE /* PreferenceUIHostingSwizzling.swift in Sources */,
|
||||
C45942C927F697CA00C54FE7 /* iOSLiveTVVideoPlayerCoordinator.swift in Sources */,
|
||||
E13DD3C827164B1E009D4DAF /* UIDeviceExtensions.swift in Sources */,
|
||||
E10EAA53277BBD17000269ED /* BaseItemDto+VideoPlayerViewModel.swift in Sources */,
|
||||
E1AD104D26D96CE3003E4A08 /* BaseItemDtoExtensions.swift in Sources */,
|
||||
@ -2496,10 +2518,12 @@
|
||||
62EC353426766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */,
|
||||
5389277C263CC3DB0035E14B /* BlurHashDecode.swift in Sources */,
|
||||
E1D4BF8A2719D3D000A11E64 /* BasicAppSettingsCoordinator.swift in Sources */,
|
||||
C45942CE27F69BF300C54FE7 /* LiveTVChannelsView.swift in Sources */,
|
||||
E13DD3F92717E961009D4DAF /* UserListViewModel.swift in Sources */,
|
||||
E126F741278A656C00A522BF /* ServerStreamType.swift in Sources */,
|
||||
539B2DA5263BA5B8007FF1A4 /* SettingsView.swift in Sources */,
|
||||
5338F74E263B61370014BF09 /* ConnectToServerView.swift in Sources */,
|
||||
C45942CF27F69BF500C54FE7 /* LiveTVChannelItemElement.swift in Sources */,
|
||||
09389CC726819B4600AE350E /* VideoPlayerModel.swift in Sources */,
|
||||
E1D4BF872719D27100A11E64 /* Bitrates.swift in Sources */,
|
||||
6220D0B726D5EE1100B8E046 /* SearchCoordinator.swift in Sources */,
|
||||
|
@ -6,6 +6,7 @@
|
||||
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import Defaults
|
||||
import Foundation
|
||||
import Stinsen
|
||||
import SwiftUI
|
||||
@ -15,8 +16,17 @@ struct LibraryListView: View {
|
||||
var libraryListRouter: LibraryListCoordinator.Router
|
||||
@StateObject
|
||||
var viewModel = LibraryListViewModel()
|
||||
|
||||
let supportedCollectionTypes = ["movies", "tvshows", "boxsets", "other"]
|
||||
|
||||
@Default(.Experimental.liveTVAlphaEnabled)
|
||||
var liveTVAlphaEnabled
|
||||
|
||||
var supportedCollectionTypes: [String] {
|
||||
if liveTVAlphaEnabled {
|
||||
return ["movies", "tvshows", "livetv", "boxsets", "other"]
|
||||
} else {
|
||||
return ["movies", "tvshows", "boxsets", "other"]
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
@ -49,9 +59,13 @@ struct LibraryListView: View {
|
||||
return self.supportedCollectionTypes.contains(collectionType)
|
||||
}, id: \.id) { library in
|
||||
Button {
|
||||
libraryListRouter.route(to: \.library,
|
||||
if library.collectionType == "livetv" {
|
||||
libraryListRouter.route(to: \.liveTV)
|
||||
} else {
|
||||
libraryListRouter.route(to: \.library,
|
||||
(viewModel: LibraryViewModel(parentID: library.id),
|
||||
title: library.name ?? ""))
|
||||
}
|
||||
} label: {
|
||||
ZStack {
|
||||
ImageView(library.getPrimaryImage(maxWidth: 500), blurHash: library.getPrimaryImageBlurHash())
|
||||
|
@ -10,7 +10,201 @@ import Stinsen
|
||||
import SwiftUI
|
||||
|
||||
struct LiveTVProgramsView: View {
|
||||
var body: some View {
|
||||
Text("Coming Soon")
|
||||
}
|
||||
@EnvironmentObject
|
||||
var programsRouter: LiveTVProgramsCoordinator.Router
|
||||
@StateObject
|
||||
var viewModel = LiveTVProgramsViewModel()
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
LazyVStack(alignment: .leading) {
|
||||
if !viewModel.recommendedItems.isEmpty,
|
||||
let items = viewModel.recommendedItems
|
||||
{
|
||||
Text("On Now")
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
.padding(.leading, 90)
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
LazyHStack {
|
||||
Spacer().frame(width: 45)
|
||||
ForEach(items, id: \.id) { item in
|
||||
Button {
|
||||
if let chanId = item.channelId,
|
||||
let chan = viewModel.findChannel(id: chanId)
|
||||
{
|
||||
self.viewModel.fetchVideoPlayerViewModel(item: chan) { playerViewModel in
|
||||
self.programsRouter.route(to: \.videoPlayer, playerViewModel)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
#if os(iOS)
|
||||
#elseif os(tvOS)
|
||||
LandscapeItemElement(item: item)
|
||||
#endif
|
||||
}
|
||||
.buttonStyle(PlainNavigationLinkButtonStyle())
|
||||
}
|
||||
Spacer().frame(width: 45)
|
||||
}
|
||||
}.frame(height: 350)
|
||||
}
|
||||
if !viewModel.seriesItems.isEmpty,
|
||||
let items = viewModel.seriesItems
|
||||
{
|
||||
Text("Shows")
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
.padding(.leading, 90)
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
LazyHStack {
|
||||
Spacer().frame(width: 45)
|
||||
ForEach(items, id: \.id) { item in
|
||||
Button {
|
||||
if let chanId = item.channelId,
|
||||
let chan = viewModel.findChannel(id: chanId)
|
||||
{
|
||||
self.viewModel.fetchVideoPlayerViewModel(item: chan) { playerViewModel in
|
||||
self.programsRouter.route(to: \.videoPlayer, playerViewModel)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
#if os(iOS)
|
||||
#elseif os(tvOS)
|
||||
LandscapeItemElement(item: item)
|
||||
#endif
|
||||
}
|
||||
.buttonStyle(PlainNavigationLinkButtonStyle())
|
||||
}
|
||||
Spacer().frame(width: 45)
|
||||
}
|
||||
}.frame(height: 350)
|
||||
}
|
||||
if !viewModel.movieItems.isEmpty,
|
||||
let items = viewModel.movieItems
|
||||
{
|
||||
Text("Movies")
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
.padding(.leading, 90)
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
LazyHStack {
|
||||
Spacer().frame(width: 45)
|
||||
ForEach(items, id: \.id) { item in
|
||||
Button {
|
||||
if let chanId = item.channelId,
|
||||
let chan = viewModel.findChannel(id: chanId)
|
||||
{
|
||||
self.viewModel.fetchVideoPlayerViewModel(item: chan) { playerViewModel in
|
||||
self.programsRouter.route(to: \.videoPlayer, playerViewModel)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
#if os(iOS)
|
||||
#elseif os(tvOS)
|
||||
LandscapeItemElement(item: item)
|
||||
#endif
|
||||
}
|
||||
.buttonStyle(PlainNavigationLinkButtonStyle())
|
||||
}
|
||||
Spacer().frame(width: 45)
|
||||
}
|
||||
}.frame(height: 350)
|
||||
}
|
||||
if !viewModel.sportsItems.isEmpty,
|
||||
let items = viewModel.sportsItems
|
||||
{
|
||||
Text("Sports")
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
.padding(.leading, 90)
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
LazyHStack {
|
||||
Spacer().frame(width: 45)
|
||||
ForEach(items, id: \.id) { item in
|
||||
Button {
|
||||
if let chanId = item.channelId,
|
||||
let chan = viewModel.findChannel(id: chanId)
|
||||
{
|
||||
self.viewModel.fetchVideoPlayerViewModel(item: chan) { playerViewModel in
|
||||
self.programsRouter.route(to: \.videoPlayer, playerViewModel)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
#if os(iOS)
|
||||
#elseif os(tvOS)
|
||||
LandscapeItemElement(item: item)
|
||||
#endif
|
||||
}
|
||||
.buttonStyle(PlainNavigationLinkButtonStyle())
|
||||
}
|
||||
Spacer().frame(width: 45)
|
||||
}
|
||||
}.frame(height: 350)
|
||||
}
|
||||
if !viewModel.kidsItems.isEmpty,
|
||||
let items = viewModel.kidsItems
|
||||
{
|
||||
Text("Kids")
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
.padding(.leading, 90)
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
LazyHStack {
|
||||
Spacer().frame(width: 45)
|
||||
ForEach(items, id: \.id) { item in
|
||||
Button {
|
||||
if let chanId = item.channelId,
|
||||
let chan = viewModel.findChannel(id: chanId)
|
||||
{
|
||||
self.viewModel.fetchVideoPlayerViewModel(item: chan) { playerViewModel in
|
||||
self.programsRouter.route(to: \.videoPlayer, playerViewModel)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
#if os(iOS)
|
||||
#elseif os(tvOS)
|
||||
LandscapeItemElement(item: item)
|
||||
#endif
|
||||
}
|
||||
.buttonStyle(PlainNavigationLinkButtonStyle())
|
||||
}
|
||||
Spacer().frame(width: 45)
|
||||
}
|
||||
}.frame(height: 350)
|
||||
}
|
||||
if !viewModel.newsItems.isEmpty,
|
||||
let items = viewModel.newsItems
|
||||
{
|
||||
Text("News")
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
.padding(.leading, 90)
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
LazyHStack {
|
||||
Spacer().frame(width: 45)
|
||||
ForEach(items, id: \.id) { item in
|
||||
Button {
|
||||
if let chanId = item.channelId,
|
||||
let chan = viewModel.findChannel(id: chanId)
|
||||
{
|
||||
self.viewModel.fetchVideoPlayerViewModel(item: chan) { playerViewModel in
|
||||
self.programsRouter.route(to: \.videoPlayer, playerViewModel)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
#if os(iOS)
|
||||
#elseif os(tvOS)
|
||||
LandscapeItemElement(item: item)
|
||||
#endif
|
||||
}
|
||||
.buttonStyle(PlainNavigationLinkButtonStyle())
|
||||
}
|
||||
Spacer().frame(width: 45)
|
||||
}
|
||||
}.frame(height: 350)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ struct ExperimentalSettingsView: View {
|
||||
var syncSubtitleStateWithAdjacent
|
||||
@Default(.Experimental.nativePlayer)
|
||||
var nativePlayer
|
||||
@Default(.Experimental.liveTVAlphaEnabled)
|
||||
var liveTVAlphaEnabled
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
@ -31,6 +33,14 @@ struct ExperimentalSettingsView: View {
|
||||
} header: {
|
||||
L10n.experimental.text
|
||||
}
|
||||
|
||||
Section {
|
||||
|
||||
Toggle("Live TV (Alpha)", isOn: $liveTVAlphaEnabled)
|
||||
|
||||
} header: {
|
||||
Text("Live TV")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
38
Swiftfin/Views/VideoPlayer/LiveTVPlayerView.swift
Normal file
38
Swiftfin/Views/VideoPlayer/LiveTVPlayerView.swift
Normal file
@ -0,0 +1,38 @@
|
||||
//
|
||||
// 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) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
//struct NativePlayerView: UIViewControllerRepresentable {
|
||||
//
|
||||
// let viewModel: VideoPlayerViewModel
|
||||
//
|
||||
// typealias UIViewControllerType = NativePlayerViewController
|
||||
//
|
||||
// func makeUIViewController(context: Context) -> NativePlayerViewController {
|
||||
//
|
||||
// NativePlayerViewController(viewModel: viewModel)
|
||||
// }
|
||||
//
|
||||
// func updateUIViewController(_ uiViewController: NativePlayerViewController, context: Context) {}
|
||||
//}
|
||||
|
||||
struct LiveTVPlayerView: UIViewControllerRepresentable {
|
||||
|
||||
let viewModel: VideoPlayerViewModel
|
||||
|
||||
typealias UIViewControllerType = LiveTVPlayerViewController
|
||||
|
||||
func makeUIViewController(context: Context) -> LiveTVPlayerViewController {
|
||||
|
||||
LiveTVPlayerViewController(viewModel: viewModel)
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: LiveTVPlayerViewController, context: Context) {}
|
||||
}
|
1032
Swiftfin/Views/VideoPlayer/LiveTVPlayerViewController.swift
Normal file
1032
Swiftfin/Views/VideoPlayer/LiveTVPlayerViewController.swift
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user