Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customizable Device Profiles #1169

Merged
merged 29 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4dac233
Rename ExperimentalSettingsView.swift to PlaybackQualitySettingsView.…
JPKribs Aug 14, 2024
018d6fc
Rename MaximumBitrateSettingsView.swift to PlaybackQualitySettingsVie…
JPKribs Aug 14, 2024
c69a195
Re-implement on Main. Should now have all the Main changed. Added a n…
Aug 14, 2024
383708c
Merge branch 'jellyfin:main' into customDeviceBuilder
JPKribs Aug 22, 2024
a876fd8
Part 1 -> Making VideoPlayerType into a struct (I Hope) correctly
Aug 28, 2024
3e91e0e
Part 1.1 -> Making VideoPlayerType into a struct (I Hope) correctly
Aug 28, 2024
505a8c9
Remove unneeded Files
Aug 28, 2024
a76f63e
Missing file + CustomDeviceProfileSelection -> CustomDeviceProfileAct…
Aug 28, 2024
3eec26f
Change + to Appending
Aug 28, 2024
14c1512
Attempt to add StorageValues+User. Not sure if this is correct?
Aug 28, 2024
887b304
Move the Array unwrapping to funcitons. Not required but this should …
Aug 28, 2024
3b3f4aa
Complete rewrite to allow multiple profiles, compatibility mode, and …
Aug 29, 2024
ee46500
Hardward -> Hardware
Aug 29, 2024
9db7d2f
Update CustomDeviceProfileSettingsView.swift
JPKribs Aug 29, 2024
f61c8bb
It was actually really easy to implement iOS... Trash cans still look…
Aug 29, 2024
b093e74
Merge remote-tracking branch 'refs/remotes/origin/customDeviceBuilder'
Aug 29, 2024
c1e6700
Swipe to Delete instead of the edit button
Aug 29, 2024
a1fd0e1
wip
LePips Aug 29, 2024
e84d4d1
Merge branch 'main' into custom-device-builder
LePips Aug 30, 2024
436513e
wip
LePips Aug 30, 2024
69c2a26
Merge branch 'jellyfin:main' into customDeviceBuilder
JPKribs Aug 30, 2024
0e1eaba
Merge branch 'device-profile-builder' into customDeviceBuilder
JPKribs Aug 31, 2024
363d448
Linting
JPKribs Aug 31, 2024
c21d582
tvOS Implementation
JPKribs Aug 31, 2024
c026ef0
wip
LePips Sep 2, 2024
e091c62
wip
LePips Sep 2, 2024
1e1fbe7
cleanup
LePips Sep 2, 2024
f1eb5a3
Merge branch 'main' into customDeviceBuilder
LePips Sep 2, 2024
3cebdeb
Create Package.resolved
LePips Sep 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions Shared/Coordinators/CustomDeviceProfileCoordinator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// Swiftfin is subject to the terms of the Mozilla Public
// License, v2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2024 Jellyfin & Jellyfin Contributors
//

import Stinsen
import SwiftUI

final class CustomDeviceProfileCoordinator: NavigationCoordinatable {

let stack = NavigationStack(initial: \CustomDeviceProfileCoordinator.start)

@Root
var start = makeStart

@Route(.push)
var customDeviceProfileSettings = makeCustomDeviceProfileSettings
@Route(.push)
var editCustomDeviceProfile = makeEditCustomDeviceProfile
@Route(.push)
var createCustomDeviceProfile = makeCreateCustomDeviceProfile

func makeCustomDeviceProfileSettings() -> NavigationViewCoordinator<PlaybackQualitySettingsCoordinator> {
NavigationViewCoordinator(
PlaybackQualitySettingsCoordinator()
)
}

func makeEditCustomDeviceProfile(profile: Binding<CustomDeviceProfile>)
-> NavigationViewCoordinator<EditCustomDeviceProfileCoordinator> {
NavigationViewCoordinator(EditCustomDeviceProfileCoordinator(profile: profile))
}

func makeCreateCustomDeviceProfile() -> NavigationViewCoordinator<EditCustomDeviceProfileCoordinator> {
NavigationViewCoordinator(EditCustomDeviceProfileCoordinator())
}

@ViewBuilder
func makeStart() -> some View {
CustomDeviceProfileSettingsView()
}
}
57 changes: 57 additions & 0 deletions Shared/Coordinators/EditCustomDeviceProfileCoordinator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// Swiftfin is subject to the terms of the Mozilla Public
// License, v2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2024 Jellyfin & Jellyfin Contributors
//

import Stinsen
import SwiftUI

final class EditCustomDeviceProfileCoordinator: NavigationCoordinatable {

let stack = NavigationStack(initial: \EditCustomDeviceProfileCoordinator.start)

@Root
var start = makeStart

// TODO: fix for tvOS

@Route(.push)
var customDeviceAudioEditor = makeCustomDeviceAudioEditor
@Route(.push)
var customDeviceVideoEditor = makeCustomDeviceVideoEditor
@Route(.push)
var customDeviceContainerEditor = makeCustomDeviceContainerEditor

private let profile: Binding<CustomDeviceProfile>?

init(profile: Binding<CustomDeviceProfile>? = nil) {
self.profile = profile
}

@ViewBuilder
func makeCustomDeviceAudioEditor(selection: Binding<[AudioCodec]>) -> some View {
OrderedSectionSelectorView(selection: selection, sources: AudioCodec.allCases)
.navigationTitle(L10n.audio)
}

@ViewBuilder
func makeCustomDeviceVideoEditor(selection: Binding<[VideoCodec]>) -> some View {
OrderedSectionSelectorView(selection: selection, sources: VideoCodec.allCases)
.navigationTitle(L10n.video)
}

@ViewBuilder
func makeCustomDeviceContainerEditor(selection: Binding<[MediaContainer]>) -> some View {
OrderedSectionSelectorView(selection: selection, sources: MediaContainer.allCases)
.navigationTitle(L10n.containers)
}

@ViewBuilder
func makeStart() -> some View {
CustomDeviceProfileSettingsView.EditCustomDeviceProfileView(profile: profile)
.navigationTitle(L10n.customProfile)
}
}
41 changes: 41 additions & 0 deletions Shared/Coordinators/PlaybackQualitySettingsCoordinator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// Swiftfin is subject to the terms of the Mozilla Public
// License, v2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2024 Jellyfin & Jellyfin Contributors
//

import Stinsen
import SwiftUI

final class PlaybackQualitySettingsCoordinator: NavigationCoordinatable {

let stack = NavigationStack(initial: \PlaybackQualitySettingsCoordinator.start)

@Root
var start = makeStart

@Route(.push)
var customDeviceProfileSettings = makeCustomDeviceProfileSettings

func makeCustomDeviceProfileSettings() -> NavigationViewCoordinator<CustomDeviceProfileCoordinator> {
NavigationViewCoordinator(
CustomDeviceProfileCoordinator()
)
}

func makeEditCustomDeviceProfile(profile: Binding<CustomDeviceProfile>)
-> NavigationViewCoordinator<EditCustomDeviceProfileCoordinator> {
NavigationViewCoordinator(EditCustomDeviceProfileCoordinator(profile: profile))
}

func makeCreateCustomDeviceProfile() -> NavigationViewCoordinator<EditCustomDeviceProfileCoordinator> {
NavigationViewCoordinator(EditCustomDeviceProfileCoordinator())
}

@ViewBuilder
func makeStart() -> some View {
PlaybackQualitySettingsView()
}
}
73 changes: 54 additions & 19 deletions Shared/Coordinators/SettingsCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ final class SettingsCoordinator: NavigationCoordinatable {
@Route(.push)
var nativePlayerSettings = makeNativePlayerSettings
@Route(.push)
var maximumBitrateSettings = makeMaximumBitrateSettings
var playbackQualitySettings = makePlaybackQualitySettings
@Route(.push)
var quickConnect = makeQuickConnectAuthorize
@Route(.push)
Expand All @@ -46,6 +46,13 @@ final class SettingsCoordinator: NavigationCoordinatable {
var serverDetail = makeServerDetail
@Route(.push)
var videoPlayerSettings = makeVideoPlayerSettings
@Route(.push)
var customDeviceProfileSettings = makeCustomDeviceProfileSettings

@Route(.modal)
var editCustomDeviceProfile = makeEditCustomDeviceProfile
@Route(.modal)
var createCustomDeviceProfile = makeCreateCustomDeviceProfile

#if DEBUG
@Route(.push)
Expand All @@ -59,13 +66,15 @@ final class SettingsCoordinator: NavigationCoordinatable {
@Route(.modal)
var experimentalSettings = makeExperimentalSettings
@Route(.modal)
var indicatorSettings = makeIndicatorSettings
@Route(.modal)
var log = makeLog
@Route(.modal)
var serverDetail = makeServerDetail
@Route(.modal)
var videoPlayerSettings = makeVideoPlayerSettings
@Route(.modal)
var maximumBitrateSettings = makeMaximumBitrateSettings
var playbackQualitySettings = makePlaybackQualitySettings
#endif

#if os(iOS)
Expand All @@ -75,8 +84,22 @@ final class SettingsCoordinator: NavigationCoordinatable {
}

@ViewBuilder
func makeMaximumBitrateSettings() -> some View {
MaximumBitrateSettingsView()
func makePlaybackQualitySettings() -> some View {
PlaybackQualitySettingsView()
}

@ViewBuilder
func makeCustomDeviceProfileSettings() -> some View {
CustomDeviceProfileSettingsView()
}

func makeEditCustomDeviceProfile(profile: Binding<CustomDeviceProfile>)
-> NavigationViewCoordinator<EditCustomDeviceProfileCoordinator> {
NavigationViewCoordinator(EditCustomDeviceProfileCoordinator(profile: profile))
}

func makeCreateCustomDeviceProfile() -> NavigationViewCoordinator<EditCustomDeviceProfileCoordinator> {
NavigationViewCoordinator(EditCustomDeviceProfileCoordinator())
}

@ViewBuilder
Expand Down Expand Up @@ -123,27 +146,31 @@ final class SettingsCoordinator: NavigationCoordinatable {
EditServerView(server: server)
}

#if DEBUG
@ViewBuilder
func makeDebugSettings() -> some View {
DebugSettingsView()
}
#endif

func makeItemFilterDrawerSelector(selection: Binding<[ItemFilterType]>) -> some View {
OrderedSectionSelectorView(selection: selection, sources: ItemFilterType.allCases)
.navigationTitle(L10n.filters)
}

func makeVideoPlayerSettings() -> VideoPlayerSettingsCoordinator {
VideoPlayerSettingsCoordinator()
}

#if DEBUG
@ViewBuilder
func makeDebugSettings() -> some View {
DebugSettingsView()
}
#endif

#endif

#if os(tvOS)

func makeCustomizeViewsSettings() -> NavigationViewCoordinator<CustomizeSettingsCoordinator> {
NavigationViewCoordinator(CustomizeSettingsCoordinator())
func makeCustomizeViewsSettings() -> NavigationViewCoordinator<BasicNavigationViewCoordinator> {
NavigationViewCoordinator(
BasicNavigationViewCoordinator {
CustomizeViewsSettings()
}
)
}

func makeExperimentalSettings() -> NavigationViewCoordinator<BasicNavigationViewCoordinator> {
Expand All @@ -154,20 +181,28 @@ final class SettingsCoordinator: NavigationCoordinatable {
)
}

func makeIndicatorSettings() -> NavigationViewCoordinator<BasicNavigationViewCoordinator> {
NavigationViewCoordinator {
IndicatorSettingsView()
}
}

func makeServerDetail(server: ServerState) -> NavigationViewCoordinator<BasicNavigationViewCoordinator> {
NavigationViewCoordinator {
EditServerView(server: server)
}
}

func makeVideoPlayerSettings() -> NavigationViewCoordinator<VideoPlayerSettingsCoordinator> {
NavigationViewCoordinator(VideoPlayerSettingsCoordinator())
NavigationViewCoordinator(
VideoPlayerSettingsCoordinator()
)
}

func makeMaximumBitrateSettings() -> NavigationViewCoordinator<BasicNavigationViewCoordinator> {
NavigationViewCoordinator {
MaximumBitrateSettingsView()
}
func makePlaybackQualitySettings() -> NavigationViewCoordinator<PlaybackQualitySettingsCoordinator> {
NavigationViewCoordinator(
PlaybackQualitySettingsCoordinator()
)
}
#endif

Expand Down
5 changes: 5 additions & 0 deletions Shared/Extensions/Array.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,8 @@ extension Array {
return removeFirst()
}
}

// extension Array where Element: RawRepresentable<String> {
//
// var asCommaString: String {}
// }
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,19 @@ import JellyfinAPI
import Logging

extension BaseItemDto {

func videoPlayerViewModel(with mediaSource: MediaSourceInfo) async throws -> VideoPlayerViewModel {

let currentVideoPlayerType = Defaults[.VideoPlayer.videoPlayerType]
let currentVideoBitrate = Defaults[.VideoPlayer.appMaximumBitrate]
let currentVideoBitrate = Defaults[.VideoPlayer.Playback.appMaximumBitrate]
let compatibilityMode = Defaults[.VideoPlayer.Playback.compatibilityMode]

let maxBitrate = try await getMaxBitrate(for: currentVideoBitrate)
let profile = DeviceProfile.build(for: currentVideoPlayerType, maxBitrate: maxBitrate)
let profile = DeviceProfile.build(
for: currentVideoPlayerType,
compatibilityMode: compatibilityMode,
maxBitrate: maxBitrate
)

let userSession = Container.shared.currentUserSession()!

Expand Down Expand Up @@ -46,14 +53,17 @@ extension BaseItemDto {
}

func liveVideoPlayerViewModel(with mediaSource: MediaSourceInfo, logger: Logger) async throws -> VideoPlayerViewModel {

let currentVideoPlayerType = Defaults[.VideoPlayer.videoPlayerType]
let currentVideoBitrate = Defaults[.VideoPlayer.appMaximumBitrate]
let currentVideoBitrate = Defaults[.VideoPlayer.Playback.appMaximumBitrate]
let compatibilityMode = Defaults[.VideoPlayer.Playback.compatibilityMode]

let maxBitrate = try await getMaxBitrate(for: currentVideoBitrate)
var profile = DeviceProfile.build(for: currentVideoPlayerType, maxBitrate: maxBitrate)
if Defaults[.Experimental.liveTVForceDirectPlay] {
profile.directPlayProfiles = [DirectPlayProfile(type: .video)]
}
let profile = DeviceProfile.build(
for: currentVideoPlayerType,
compatibilityMode: compatibilityMode,
maxBitrate: maxBitrate
)

let userSession = Container.shared.currentUserSession()!

Expand Down Expand Up @@ -101,7 +111,7 @@ extension BaseItemDto {
}

private func getMaxBitrate(for bitrate: PlaybackBitrate) async throws -> Int {
let settingBitrate = Defaults[.VideoPlayer.appMaximumBitrateTest]
let settingBitrate = Defaults[.VideoPlayer.Playback.appMaximumBitrateTest]

guard bitrate != .auto else {
return try await testBitrate(with: settingBitrate.rawValue)
Expand Down
32 changes: 32 additions & 0 deletions Shared/Extensions/JellyfinAPI/CodecProfile.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// Swiftfin is subject to the terms of the Mozilla Public
// License, v2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2024 Jellyfin & Jellyfin Contributors
//

import Foundation
import JellyfinAPI

extension CodecProfile {

init(
codec: String? = nil,
container: String? = nil,
type: CodecType? = nil,
@ArrayBuilder<ProfileCondition> applyConditions: () -> [ProfileCondition] = { [] },
@ArrayBuilder<ProfileCondition> conditions: () -> [ProfileCondition] = { [] }
) {
let applyConditions = applyConditions()
let conditions = conditions()

self.init(
applyConditions: applyConditions.isEmpty ? nil : applyConditions,
codec: codec,
conditions: conditions.isEmpty ? nil : conditions,
container: container,
type: type
)
}
}
Loading
Loading