Merge branch 'main' into PangMo5/ci-macos-12
This commit is contained in:
commit
957b35b870
|
@ -27,8 +27,14 @@ final class LiveTVVideoPlayerCoordinator: NavigationCoordinatable {
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
func makeStart() -> some View {
|
func makeStart() -> some View {
|
||||||
LiveTVVideoPlayerView(viewModel: viewModel)
|
if Defaults[.Experimental.liveTVNativePlayer] {
|
||||||
.navigationBarHidden(true)
|
LiveTVNativeVideoPlayerView(viewModel: viewModel)
|
||||||
.ignoresSafeArea()
|
.navigationBarHidden(true)
|
||||||
|
.ignoresSafeArea()
|
||||||
|
} else {
|
||||||
|
LiveTVVideoPlayerView(viewModel: viewModel)
|
||||||
|
.navigationBarHidden(true)
|
||||||
|
.ignoresSafeArea()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -226,7 +226,7 @@ extension BaseItemDto {
|
||||||
mediaSourceId: mediaSourceID)
|
mediaSourceId: mediaSourceID)
|
||||||
directStreamURL = URL(string: directStreamBuilder.URLString)!
|
directStreamURL = URL(string: directStreamBuilder.URLString)!
|
||||||
|
|
||||||
if let transcodeURL = currentMediaSource.transcodingUrl {
|
if let transcodeURL = currentMediaSource.transcodingUrl, !Defaults[.Experimental.liveTVForceDirectPlay] {
|
||||||
streamType = .transcode
|
streamType = .transcode
|
||||||
transcodedStreamURL = URLComponents(string: SessionManager.main.currentLogin.server.currentURI
|
transcodedStreamURL = URLComponents(string: SessionManager.main.currentLogin.server.currentURI
|
||||||
.appending(transcodeURL))!
|
.appending(transcodeURL))!
|
||||||
|
|
|
@ -74,8 +74,10 @@ extension Defaults.Keys {
|
||||||
static let syncSubtitleStateWithAdjacent = Key<Bool>("experimental.syncSubtitleState", default: false,
|
static let syncSubtitleStateWithAdjacent = Key<Bool>("experimental.syncSubtitleState", default: false,
|
||||||
suite: SwiftfinStore.Defaults.generalSuite)
|
suite: SwiftfinStore.Defaults.generalSuite)
|
||||||
static let forceDirectPlay = Key<Bool>("forceDirectPlay", default: false, suite: SwiftfinStore.Defaults.generalSuite)
|
static let forceDirectPlay = Key<Bool>("forceDirectPlay", default: false, suite: SwiftfinStore.Defaults.generalSuite)
|
||||||
static let liveTVAlphaEnabled = Key<Bool>("liveTVAlphaEnabled", default: false, suite: SwiftfinStore.Defaults.generalSuite)
|
|
||||||
static let nativePlayer = Key<Bool>("nativePlayer", default: false, suite: SwiftfinStore.Defaults.generalSuite)
|
static let nativePlayer = Key<Bool>("nativePlayer", default: false, suite: SwiftfinStore.Defaults.generalSuite)
|
||||||
|
static let liveTVAlphaEnabled = Key<Bool>("liveTVAlphaEnabled", default: false, suite: SwiftfinStore.Defaults.generalSuite)
|
||||||
|
static let liveTVForceDirectPlay = Key<Bool>("liveTVForceDirectPlay", default: false, suite: SwiftfinStore.Defaults.generalSuite)
|
||||||
|
static let liveTVNativePlayer = Key<Bool>("liveTVNativePlayer", default: false, suite: SwiftfinStore.Defaults.generalSuite)
|
||||||
}
|
}
|
||||||
|
|
||||||
// tvos specific
|
// tvos specific
|
||||||
|
|
|
@ -21,6 +21,10 @@ protocol EpisodesRowManager: ViewModel {
|
||||||
|
|
||||||
extension EpisodesRowManager {
|
extension EpisodesRowManager {
|
||||||
|
|
||||||
|
var sortedSeasons: [BaseItemDto] {
|
||||||
|
Array(seasonsEpisodes.keys).sorted(by: { $0.indexNumber ?? 0 < $1.indexNumber ?? 0 })
|
||||||
|
}
|
||||||
|
|
||||||
// Also retrieves the current season episodes if available
|
// Also retrieves the current season episodes if available
|
||||||
func retrieveSeasons() {
|
func retrieveSeasons() {
|
||||||
TvShowsAPI.getSeasons(seriesId: item.seriesId ?? "",
|
TvShowsAPI.getSeasons(seriesId: item.seriesId ?? "",
|
||||||
|
|
|
@ -151,7 +151,8 @@ final class VideoPlayerViewModel: ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setSeconds(_ seconds: Int64) {
|
func setSeconds(_ seconds: Int64) {
|
||||||
let videoDuration = item.runTimeTicks!
|
guard let runTimeTicks = item.runTimeTicks else { return }
|
||||||
|
let videoDuration = runTimeTicks
|
||||||
let percentage = Double(seconds * 10_000_000) / Double(videoDuration)
|
let percentage = Double(seconds * 10_000_000) / Double(videoDuration)
|
||||||
|
|
||||||
sliderPercentage = percentage
|
sliderPercentage = percentage
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
//
|
|
||||||
// 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 JellyfinAPI
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct LiveTVChannelItemElement: View {
|
|
||||||
@Environment(\.isFocused)
|
|
||||||
var envFocused: Bool
|
|
||||||
@State
|
|
||||||
var focused: Bool = false
|
|
||||||
|
|
||||||
var channel: BaseItemDto
|
|
||||||
var program: BaseItemDto?
|
|
||||||
var startString = " "
|
|
||||||
var endString = " "
|
|
||||||
var progressPercent = Double(0)
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
VStack {
|
|
||||||
HStack {
|
|
||||||
Spacer()
|
|
||||||
Text(channel.number ?? "")
|
|
||||||
.font(.footnote)
|
|
||||||
.frame(alignment: .trailing)
|
|
||||||
}.frame(alignment: .top)
|
|
||||||
ImageView(channel.getPrimaryImage(maxWidth: 125))
|
|
||||||
.frame(width: 125, alignment: .center)
|
|
||||||
.offset(x: 0, y: -32)
|
|
||||||
Text(channel.name ?? "?")
|
|
||||||
.font(.footnote)
|
|
||||||
.lineLimit(1)
|
|
||||||
.frame(alignment: .center)
|
|
||||||
Text(program?.name ?? L10n.notAvailableSlash)
|
|
||||||
.font(.body)
|
|
||||||
.lineLimit(1)
|
|
||||||
.foregroundColor(.green)
|
|
||||||
VStack {
|
|
||||||
HStack {
|
|
||||||
Text(startString)
|
|
||||||
.font(.footnote)
|
|
||||||
.lineLimit(1)
|
|
||||||
.frame(alignment: .leading)
|
|
||||||
|
|
||||||
Spacer()
|
|
||||||
|
|
||||||
Text(endString)
|
|
||||||
.font(.footnote)
|
|
||||||
.lineLimit(1)
|
|
||||||
.frame(alignment: .trailing)
|
|
||||||
}
|
|
||||||
GeometryReader { gp in
|
|
||||||
ZStack(alignment: .leading) {
|
|
||||||
RoundedRectangle(cornerRadius: 6)
|
|
||||||
.fill(Color.gray)
|
|
||||||
.opacity(0.4)
|
|
||||||
.frame(minWidth: 100, maxWidth: .infinity, minHeight: 12, maxHeight: 12)
|
|
||||||
RoundedRectangle(cornerRadius: 6)
|
|
||||||
.fill(Color.jellyfinPurple)
|
|
||||||
.frame(width: CGFloat(progressPercent * gp.size.width), height: 12)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
.background(Color.clear)
|
|
||||||
.border(focused ? Color.blue : Color.clear, width: 4)
|
|
||||||
.onChange(of: envFocused) { envFocus in
|
|
||||||
withAnimation(.linear(duration: 0.15)) {
|
|
||||||
self.focused = envFocus
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.scaleEffect(focused ? 1.1 : 1)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
//
|
||||||
|
// 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 JellyfinAPI
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct LiveTVChannelItemElement: View {
|
||||||
|
@FocusState
|
||||||
|
private var focused: Bool
|
||||||
|
@State
|
||||||
|
private var loading: Bool = false
|
||||||
|
@State
|
||||||
|
private var isFocused: Bool = false
|
||||||
|
|
||||||
|
var channel: BaseItemDto
|
||||||
|
var program: BaseItemDto?
|
||||||
|
var startString = " "
|
||||||
|
var endString = " "
|
||||||
|
var progressPercent = Double(0)
|
||||||
|
var onSelect: (@escaping (Bool) -> Void) -> Void
|
||||||
|
|
||||||
|
private var detailText: String {
|
||||||
|
guard let program = program else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var text = ""
|
||||||
|
if let season = program.parentIndexNumber,
|
||||||
|
let episode = program.indexNumber
|
||||||
|
{
|
||||||
|
text.append("\(season)x\(episode) ")
|
||||||
|
} else if let episode = program.indexNumber {
|
||||||
|
text.append("\(episode) ")
|
||||||
|
}
|
||||||
|
if let title = program.episodeTitle {
|
||||||
|
text.append("\(title) ")
|
||||||
|
}
|
||||||
|
if let year = program.productionYear {
|
||||||
|
text.append("\(year) ")
|
||||||
|
}
|
||||||
|
if let rating = program.officialRating {
|
||||||
|
text.append("\(rating)")
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
VStack {
|
||||||
|
HStack {
|
||||||
|
Text(channel.number ?? "")
|
||||||
|
.font(.footnote)
|
||||||
|
.frame(alignment: .leading)
|
||||||
|
.padding()
|
||||||
|
Spacer()
|
||||||
|
}.frame(alignment: .top)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
VStack {
|
||||||
|
ImageView(channel.getPrimaryImage(maxWidth: 128))
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.frame(width: 128, alignment: .center)
|
||||||
|
.padding(.init(top: 8, leading: 0, bottom: 0, trailing: 0))
|
||||||
|
Text(channel.name ?? "?")
|
||||||
|
.font(.footnote)
|
||||||
|
.lineLimit(1)
|
||||||
|
.frame(alignment: .center)
|
||||||
|
Text(program?.name ?? L10n.notAvailableSlash)
|
||||||
|
.font(.body)
|
||||||
|
.lineLimit(1)
|
||||||
|
.foregroundColor(.green)
|
||||||
|
Text(detailText)
|
||||||
|
.font(.body)
|
||||||
|
.lineLimit(1)
|
||||||
|
.foregroundColor(.green)
|
||||||
|
Spacer()
|
||||||
|
HStack(alignment: .bottom) {
|
||||||
|
VStack {
|
||||||
|
Spacer()
|
||||||
|
HStack {
|
||||||
|
Text(startString)
|
||||||
|
.font(.footnote)
|
||||||
|
.lineLimit(1)
|
||||||
|
.frame(alignment: .leading)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Text(endString)
|
||||||
|
.font(.footnote)
|
||||||
|
.lineLimit(1)
|
||||||
|
.frame(alignment: .trailing)
|
||||||
|
}
|
||||||
|
GeometryReader { gp in
|
||||||
|
ZStack(alignment: .leading) {
|
||||||
|
RoundedRectangle(cornerRadius: 6)
|
||||||
|
.fill(Color.gray)
|
||||||
|
.opacity(0.4)
|
||||||
|
.frame(minWidth: 100, maxWidth: .infinity, minHeight: 12, maxHeight: 12)
|
||||||
|
RoundedRectangle(cornerRadius: 6)
|
||||||
|
.fill(Color.jellyfinPurple)
|
||||||
|
.frame(width: CGFloat(progressPercent * gp.size.width), height: 12)
|
||||||
|
}
|
||||||
|
.frame(alignment: .bottom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
.opacity(loading ? 0.5 : 1.0)
|
||||||
|
|
||||||
|
if loading {
|
||||||
|
ProgressView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.overlay(RoundedRectangle(cornerRadius: 20)
|
||||||
|
.stroke(isFocused ? Color.blue : Color.clear, lineWidth: 4))
|
||||||
|
.cornerRadius(20)
|
||||||
|
.scaleEffect(isFocused ? 1.1 : 1)
|
||||||
|
.focusable(true)
|
||||||
|
.focused($focused)
|
||||||
|
.onChange(of: focused) { foc in
|
||||||
|
withAnimation(.linear(duration: 0.15)) {
|
||||||
|
self.isFocused = foc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onLongPressGesture(minimumDuration: 0.01, pressing: { _ in }) {
|
||||||
|
onSelect { loadingState in
|
||||||
|
loading = loadingState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,18 +54,21 @@ struct LiveTVChannelsView: View {
|
||||||
let item = cell.item
|
let item = cell.item
|
||||||
let channel = item.channel
|
let channel = item.channel
|
||||||
if channel.type != "Folder" {
|
if channel.type != "Folder" {
|
||||||
Button {
|
let progressPercent = item.program?.getLiveProgressPercentage() ?? 0
|
||||||
self.viewModel.fetchVideoPlayerViewModel(item: channel) { playerViewModel in
|
LiveTVChannelItemElement(channel: channel,
|
||||||
self.router.route(to: \.videoPlayer, playerViewModel)
|
program: item.program,
|
||||||
}
|
startString: item.program?.getLiveStartTimeString(formatter: viewModel.timeFormatter) ?? " ",
|
||||||
} label: {
|
endString: item.program?.getLiveEndTimeString(formatter: viewModel.timeFormatter) ?? " ",
|
||||||
LiveTVChannelItemElement(channel: channel,
|
progressPercent: progressPercent > 1.0 ? 1.0 : progressPercent,
|
||||||
program: item.program,
|
onSelect: { loadingAction in
|
||||||
startString: item.program?.getLiveStartTimeString(formatter: viewModel.timeFormatter) ?? " ",
|
loadingAction(true)
|
||||||
endString: item.program?.getLiveEndTimeString(formatter: viewModel.timeFormatter) ?? " ",
|
self.viewModel.fetchVideoPlayerViewModel(item: channel) { playerViewModel in
|
||||||
progressPercent: item.program?.getLiveProgressPercentage() ?? 0)
|
self.router.route(to: \.videoPlayer, playerViewModel)
|
||||||
}
|
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
|
||||||
.buttonStyle(PlainNavigationLinkButtonStyle())
|
loadingAction(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,11 +15,16 @@ struct ExperimentalSettingsView: View {
|
||||||
var forceDirectPlay
|
var forceDirectPlay
|
||||||
@Default(.Experimental.syncSubtitleStateWithAdjacent)
|
@Default(.Experimental.syncSubtitleStateWithAdjacent)
|
||||||
var syncSubtitleStateWithAdjacent
|
var syncSubtitleStateWithAdjacent
|
||||||
@Default(.Experimental.liveTVAlphaEnabled)
|
|
||||||
var liveTVAlphaEnabled
|
|
||||||
@Default(.Experimental.nativePlayer)
|
@Default(.Experimental.nativePlayer)
|
||||||
var nativePlayer
|
var nativePlayer
|
||||||
|
|
||||||
|
@Default(.Experimental.liveTVAlphaEnabled)
|
||||||
|
var liveTVAlphaEnabled
|
||||||
|
@Default(.Experimental.liveTVForceDirectPlay)
|
||||||
|
var liveTVForceDirectPlay
|
||||||
|
@Default(.Experimental.liveTVNativePlayer)
|
||||||
|
var liveTVNativePlayer
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
Section {
|
Section {
|
||||||
|
@ -28,13 +33,23 @@ struct ExperimentalSettingsView: View {
|
||||||
|
|
||||||
Toggle("Sync Subtitles with Adjacent Episodes", isOn: $syncSubtitleStateWithAdjacent)
|
Toggle("Sync Subtitles with Adjacent Episodes", isOn: $syncSubtitleStateWithAdjacent)
|
||||||
|
|
||||||
Toggle("Live TV (Alpha)", isOn: $liveTVAlphaEnabled)
|
|
||||||
|
|
||||||
Toggle("Native Player", isOn: $nativePlayer)
|
Toggle("Native Player", isOn: $nativePlayer)
|
||||||
|
|
||||||
} header: {
|
} header: {
|
||||||
L10n.experimental.text
|
L10n.experimental.text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
|
||||||
|
Toggle("Live TV (Alpha)", isOn: $liveTVAlphaEnabled)
|
||||||
|
|
||||||
|
Toggle("Live TV Force Direct Play", isOn: $liveTVForceDirectPlay)
|
||||||
|
|
||||||
|
Toggle("Live TV Native Player", isOn: $liveTVNativePlayer)
|
||||||
|
|
||||||
|
} header: {
|
||||||
|
Text("Live TV")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
//
|
||||||
|
// 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 LiveTVNativeVideoPlayerView: UIViewControllerRepresentable {
|
||||||
|
|
||||||
|
let viewModel: VideoPlayerViewModel
|
||||||
|
|
||||||
|
typealias UIViewControllerType = NativePlayerViewController
|
||||||
|
|
||||||
|
func makeUIViewController(context: Context) -> NativePlayerViewController {
|
||||||
|
NativePlayerViewController(viewModel: viewModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIViewController(_ uiViewController: NativePlayerViewController, context: Context) {}
|
||||||
|
}
|
|
@ -262,7 +262,7 @@
|
||||||
C45B29BB26FAC5B600CEF5E0 /* ColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E173DA5126D04AAF00CC4EB7 /* ColorExtension.swift */; };
|
C45B29BB26FAC5B600CEF5E0 /* ColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E173DA5126D04AAF00CC4EB7 /* ColorExtension.swift */; };
|
||||||
C4AE2C3027498D2300AE13CF /* LiveTVHomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AE2C2F27498D2300AE13CF /* LiveTVHomeView.swift */; };
|
C4AE2C3027498D2300AE13CF /* LiveTVHomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AE2C2F27498D2300AE13CF /* LiveTVHomeView.swift */; };
|
||||||
C4AE2C3227498D6A00AE13CF /* LiveTVProgramsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AE2C3127498D6A00AE13CF /* LiveTVProgramsView.swift */; };
|
C4AE2C3227498D6A00AE13CF /* LiveTVProgramsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AE2C3127498D6A00AE13CF /* LiveTVProgramsView.swift */; };
|
||||||
C4AE2C3327498DBE00AE13CF /* LiveTVChannelItemElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E52304272CE68800654268 /* LiveTVChannelItemElement.swift */; };
|
C4B9B91427E1921B0063535C /* LiveTVNativeVideoPlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B9B91327E1921B0063535C /* LiveTVNativeVideoPlayerView.swift */; };
|
||||||
C4BE0764271FC0BB003F4AD1 /* TVLibrariesCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BE0762271FC0BB003F4AD1 /* TVLibrariesCoordinator.swift */; };
|
C4BE0764271FC0BB003F4AD1 /* TVLibrariesCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BE0762271FC0BB003F4AD1 /* TVLibrariesCoordinator.swift */; };
|
||||||
C4BE0767271FC109003F4AD1 /* TVLibrariesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BE0765271FC109003F4AD1 /* TVLibrariesViewModel.swift */; };
|
C4BE0767271FC109003F4AD1 /* TVLibrariesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BE0765271FC109003F4AD1 /* TVLibrariesViewModel.swift */; };
|
||||||
C4BE076A271FC164003F4AD1 /* TVLibrariesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BE0768271FC164003F4AD1 /* TVLibrariesView.swift */; };
|
C4BE076A271FC164003F4AD1 /* TVLibrariesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BE0768271FC164003F4AD1 /* TVLibrariesView.swift */; };
|
||||||
|
@ -741,6 +741,7 @@
|
||||||
C4534984279A40C50045F1E2 /* LiveTVVideoPlayerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LiveTVVideoPlayerView.swift; sourceTree = "<group>"; };
|
C4534984279A40C50045F1E2 /* LiveTVVideoPlayerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LiveTVVideoPlayerView.swift; sourceTree = "<group>"; };
|
||||||
C4AE2C2F27498D2300AE13CF /* LiveTVHomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveTVHomeView.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>"; };
|
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>"; };
|
||||||
C4BE0762271FC0BB003F4AD1 /* TVLibrariesCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVLibrariesCoordinator.swift; sourceTree = "<group>"; };
|
C4BE0762271FC0BB003F4AD1 /* TVLibrariesCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVLibrariesCoordinator.swift; sourceTree = "<group>"; };
|
||||||
C4BE0765271FC109003F4AD1 /* TVLibrariesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVLibrariesViewModel.swift; sourceTree = "<group>"; };
|
C4BE0765271FC109003F4AD1 /* TVLibrariesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVLibrariesViewModel.swift; sourceTree = "<group>"; };
|
||||||
C4BE0768271FC164003F4AD1 /* TVLibrariesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVLibrariesView.swift; sourceTree = "<group>"; };
|
C4BE0768271FC164003F4AD1 /* TVLibrariesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVLibrariesView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -999,6 +1000,7 @@
|
||||||
E17885A7278130690094FBCF /* Overlays */,
|
E17885A7278130690094FBCF /* Overlays */,
|
||||||
E1C812C8277AE40900918266 /* VideoPlayerView.swift */,
|
E1C812C8277AE40900918266 /* VideoPlayerView.swift */,
|
||||||
C4534984279A40C50045F1E2 /* LiveTVVideoPlayerView.swift */,
|
C4534984279A40C50045F1E2 /* LiveTVVideoPlayerView.swift */,
|
||||||
|
C4B9B91327E1921B0063535C /* LiveTVNativeVideoPlayerView.swift */,
|
||||||
C453497E279A2DA50045F1E2 /* LiveTVPlayerViewController.swift */,
|
C453497E279A2DA50045F1E2 /* LiveTVPlayerViewController.swift */,
|
||||||
E1384943278036C70024FB48 /* VLCPlayerViewController.swift */,
|
E1384943278036C70024FB48 /* VLCPlayerViewController.swift */,
|
||||||
E13AD72F2798C60F00FDCEE8 /* NativePlayerViewController.swift */,
|
E13AD72F2798C60F00FDCEE8 /* NativePlayerViewController.swift */,
|
||||||
|
@ -1563,6 +1565,7 @@
|
||||||
C4E5081C2703F8370045C9AB /* LibrarySearchView.swift */,
|
C4E5081C2703F8370045C9AB /* LibrarySearchView.swift */,
|
||||||
53A83C32268A309300DF3D92 /* LibraryView.swift */,
|
53A83C32268A309300DF3D92 /* LibraryView.swift */,
|
||||||
C4BE078A272844AF003F4AD1 /* LiveTVChannelsView.swift */,
|
C4BE078A272844AF003F4AD1 /* LiveTVChannelsView.swift */,
|
||||||
|
C4E52304272CE68800654268 /* LiveTVChannelItemElement.swift */,
|
||||||
C4BE078D27298817003F4AD1 /* LiveTVHomeView.swift */,
|
C4BE078D27298817003F4AD1 /* LiveTVHomeView.swift */,
|
||||||
C4BE07732725EB66003F4AD1 /* LiveTVProgramsView.swift */,
|
C4BE07732725EB66003F4AD1 /* LiveTVProgramsView.swift */,
|
||||||
C40CD927271F8DAB000FB198 /* MovieLibrariesView.swift */,
|
C40CD927271F8DAB000FB198 /* MovieLibrariesView.swift */,
|
||||||
|
@ -1758,7 +1761,6 @@
|
||||||
531AC8BE26750DE20091C7EB /* ImageView.swift */,
|
531AC8BE26750DE20091C7EB /* ImageView.swift */,
|
||||||
E1047E2227E5880000CB0D4A /* InitialFailureView.swift */,
|
E1047E2227E5880000CB0D4A /* InitialFailureView.swift */,
|
||||||
621338B22660A07800A81A2A /* LazyView.swift */,
|
621338B22660A07800A81A2A /* LazyView.swift */,
|
||||||
C4E52304272CE68800654268 /* LiveTVChannelItemElement.swift */,
|
|
||||||
53E4E648263F725B00F67C6B /* MultiSelectorView.swift */,
|
53E4E648263F725B00F67C6B /* MultiSelectorView.swift */,
|
||||||
6225FCCA2663841E00E067F6 /* ParallaxHeader.swift */,
|
6225FCCA2663841E00E067F6 /* ParallaxHeader.swift */,
|
||||||
531690F9267AD6EC005D8AB9 /* PlainNavigationLinkButton.swift */,
|
531690F9267AD6EC005D8AB9 /* PlainNavigationLinkButton.swift */,
|
||||||
|
@ -2171,6 +2173,7 @@
|
||||||
E193D53327193F7D00900D82 /* FilterCoordinator.swift in Sources */,
|
E193D53327193F7D00900D82 /* FilterCoordinator.swift in Sources */,
|
||||||
6267B3DC2671139500A7371D /* ImageExtensions.swift in Sources */,
|
6267B3DC2671139500A7371D /* ImageExtensions.swift in Sources */,
|
||||||
E103A6A9278AB6FF00820EC7 /* CinematicNextUpCardView.swift in Sources */,
|
E103A6A9278AB6FF00820EC7 /* CinematicNextUpCardView.swift in Sources */,
|
||||||
|
C4B9B91427E1921B0063535C /* LiveTVNativeVideoPlayerView.swift in Sources */,
|
||||||
E107BB9427880A8F00354E07 /* CollectionItemViewModel.swift in Sources */,
|
E107BB9427880A8F00354E07 /* CollectionItemViewModel.swift in Sources */,
|
||||||
C4534985279A40C60045F1E2 /* LiveTVVideoPlayerView.swift in Sources */,
|
C4534985279A40C60045F1E2 /* LiveTVVideoPlayerView.swift in Sources */,
|
||||||
E1A2C15A279A7D76005EC829 /* BundleExtensions.swift in Sources */,
|
E1A2C15A279A7D76005EC829 /* BundleExtensions.swift in Sources */,
|
||||||
|
@ -2437,7 +2440,6 @@
|
||||||
E13DD3FC2717EAE8009D4DAF /* UserListView.swift in Sources */,
|
E13DD3FC2717EAE8009D4DAF /* UserListView.swift in Sources */,
|
||||||
6220D0CC26D640C400B8E046 /* AppURLHandler.swift in Sources */,
|
6220D0CC26D640C400B8E046 /* AppURLHandler.swift in Sources */,
|
||||||
53DE4BD2267098F300739748 /* SearchBarView.swift in Sources */,
|
53DE4BD2267098F300739748 /* SearchBarView.swift in Sources */,
|
||||||
C4AE2C3327498DBE00AE13CF /* LiveTVChannelItemElement.swift in Sources */,
|
|
||||||
E1E48CC9271E6D410021A2F9 /* RefreshHelper.swift in Sources */,
|
E1E48CC9271E6D410021A2F9 /* RefreshHelper.swift in Sources */,
|
||||||
E1AA33222782648000F6439C /* OverlaySliderColor.swift in Sources */,
|
E1AA33222782648000F6439C /* OverlaySliderColor.swift in Sources */,
|
||||||
E1D4BF842719D25A00A11E64 /* TrackLanguage.swift in Sources */,
|
E1D4BF842719D25A00A11E64 /* TrackLanguage.swift in Sources */,
|
||||||
|
|
|
@ -29,7 +29,8 @@ struct EpisodesRowView<RowManager>: View where RowManager: EpisodesRowManager {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Menu {
|
Menu {
|
||||||
ForEach(Array(viewModel.seasonsEpisodes.keys).sorted(by: { $0.name ?? "" < $1.name ?? "" }), id: \.self) { season in
|
ForEach(viewModel.sortedSeasons,
|
||||||
|
id: \.self) { season in
|
||||||
Button {
|
Button {
|
||||||
viewModel.select(season: season)
|
viewModel.select(season: season)
|
||||||
} label: {
|
} label: {
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue