mirror of
https://github.com/jellyfin/Swiftfin.git
synced 2024-11-23 14:10:01 +00:00
Support select subtitle font (#498)
This commit is contained in:
parent
38af1cd54a
commit
48a03d8462
@ -11,7 +11,6 @@ import Stinsen
|
||||
import SwiftUI
|
||||
|
||||
final class SettingsCoordinator: NavigationCoordinatable {
|
||||
|
||||
let stack = NavigationStack(initial: \SettingsCoordinator.start)
|
||||
|
||||
@Root
|
||||
@ -32,6 +31,8 @@ final class SettingsCoordinator: NavigationCoordinatable {
|
||||
#if !os(tvOS)
|
||||
@Route(.push)
|
||||
var quickConnect = makeQuickConnectSettings
|
||||
@Route(.push)
|
||||
var fontPicker = makeFontPicker
|
||||
#endif
|
||||
|
||||
@ViewBuilder
|
||||
@ -71,6 +72,12 @@ final class SettingsCoordinator: NavigationCoordinatable {
|
||||
let viewModel = QuickConnectSettingsViewModel()
|
||||
QuickConnectSettingsView(viewModel: viewModel)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func makeFontPicker() -> some View {
|
||||
FontPickerView()
|
||||
.navigationTitle(L10n.subtitleFont)
|
||||
}
|
||||
#endif
|
||||
|
||||
@ViewBuilder
|
||||
|
@ -44,137 +44,137 @@ extension BaseItemDto {
|
||||
var viewModels: [VideoPlayerViewModel] = []
|
||||
|
||||
for currentMediaSource in mediaSources {
|
||||
let videoStream = currentMediaSource.mediaStreams?.filter { $0.type == .video }.first
|
||||
let audioStreams = currentMediaSource.mediaStreams?.filter { $0.type == .audio } ?? []
|
||||
let subtitleStreams = currentMediaSource.mediaStreams?.filter { $0.type == .subtitle } ?? []
|
||||
let videoStream = currentMediaSource.mediaStreams?.filter { $0.type == .video }.first
|
||||
let audioStreams = currentMediaSource.mediaStreams?.filter { $0.type == .audio } ?? []
|
||||
let subtitleStreams = currentMediaSource.mediaStreams?.filter { $0.type == .subtitle } ?? []
|
||||
|
||||
let defaultAudioStream = audioStreams.first(where: { $0.index! == currentMediaSource.defaultAudioStreamIndex! })
|
||||
let defaultAudioStream = audioStreams.first(where: { $0.index! == currentMediaSource.defaultAudioStreamIndex! })
|
||||
|
||||
let defaultSubtitleStream = subtitleStreams
|
||||
.first(where: { $0.index! == currentMediaSource.defaultSubtitleStreamIndex ?? -1 })
|
||||
let defaultSubtitleStream = subtitleStreams
|
||||
.first(where: { $0.index! == currentMediaSource.defaultSubtitleStreamIndex ?? -1 })
|
||||
|
||||
// MARK: Build Streams
|
||||
// MARK: Build Streams
|
||||
|
||||
let directStreamURL: URL
|
||||
let transcodedStreamURL: URLComponents?
|
||||
var hlsStreamURL: URL
|
||||
let mediaSourceID: String
|
||||
let streamType: ServerStreamType
|
||||
let directStreamURL: URL
|
||||
let transcodedStreamURL: URLComponents?
|
||||
var hlsStreamURL: URL
|
||||
let mediaSourceID: String
|
||||
let streamType: ServerStreamType
|
||||
|
||||
if mediaSources.count > 1 {
|
||||
mediaSourceID = currentMediaSource.id!
|
||||
} else {
|
||||
mediaSourceID = self.id!
|
||||
}
|
||||
|
||||
let directStreamBuilder = VideosAPI.getVideoStreamWithRequestBuilder(
|
||||
itemId: self.id!,
|
||||
_static: true,
|
||||
tag: self.etag,
|
||||
playSessionId: response.playSessionId,
|
||||
minSegments: 6,
|
||||
mediaSourceId: mediaSourceID
|
||||
)
|
||||
directStreamURL = URL(string: directStreamBuilder.URLString)!
|
||||
|
||||
if let transcodeURL = currentMediaSource.transcodingUrl {
|
||||
streamType = .transcode
|
||||
transcodedStreamURL = URLComponents(
|
||||
string: SessionManager.main.currentLogin.server.currentURI
|
||||
.appending(transcodeURL)
|
||||
)!
|
||||
} else {
|
||||
streamType = .direct
|
||||
transcodedStreamURL = nil
|
||||
}
|
||||
|
||||
let hlsStreamBuilder = DynamicHlsAPI.getMasterHlsVideoPlaylistWithRequestBuilder(
|
||||
itemId: id ?? "",
|
||||
mediaSourceId: id ?? "",
|
||||
_static: true,
|
||||
tag: currentMediaSource.eTag,
|
||||
deviceProfileId: nil,
|
||||
playSessionId: response.playSessionId,
|
||||
segmentContainer: "ts",
|
||||
segmentLength: nil,
|
||||
minSegments: 2,
|
||||
deviceId: UIDevice.vendorUUIDString,
|
||||
audioCodec: audioStreams
|
||||
.compactMap(\.codec)
|
||||
.joined(separator: ","),
|
||||
breakOnNonKeyFrames: true,
|
||||
requireAvc: true,
|
||||
transcodingMaxAudioChannels: 6,
|
||||
videoCodec: videoStream?.codec,
|
||||
videoStreamIndex: videoStream?.index,
|
||||
enableAdaptiveBitrateStreaming: true
|
||||
)
|
||||
|
||||
var hlsStreamComponents = URLComponents(string: hlsStreamBuilder.URLString)!
|
||||
hlsStreamComponents.addQueryItem(name: "api_key", value: SessionManager.main.currentLogin.user.accessToken)
|
||||
|
||||
hlsStreamURL = hlsStreamComponents.url!
|
||||
|
||||
// MARK: VidoPlayerViewModel Creation
|
||||
|
||||
var subtitle: String?
|
||||
|
||||
// MARK: Attach media content to self
|
||||
|
||||
var modifiedSelfItem = self
|
||||
modifiedSelfItem.mediaStreams = currentMediaSource.mediaStreams
|
||||
|
||||
// TODO: other forms of media subtitle
|
||||
if self.itemType == .episode {
|
||||
if let seriesName = self.seriesName, let episodeLocator = self.getEpisodeLocator() {
|
||||
subtitle = "\(seriesName) - \(episodeLocator)"
|
||||
}
|
||||
}
|
||||
|
||||
let subtitlesEnabled = defaultSubtitleStream != nil
|
||||
|
||||
let shouldShowAutoPlay = Defaults[.shouldShowAutoPlay] && itemType == .episode
|
||||
let autoplayEnabled = Defaults[.autoplayEnabled] && shouldShowAutoPlay
|
||||
|
||||
let overlayType = Defaults[.overlayType]
|
||||
|
||||
let shouldShowPlayPreviousItem = Defaults[.shouldShowPlayPreviousItem] && itemType == .episode
|
||||
let shouldShowPlayNextItem = Defaults[.shouldShowPlayNextItem] && itemType == .episode
|
||||
|
||||
var fileName: String?
|
||||
if let lastInPath = currentMediaSource.path?.split(separator: "/").last {
|
||||
fileName = String(lastInPath)
|
||||
}
|
||||
|
||||
let videoPlayerViewModel = VideoPlayerViewModel(
|
||||
item: modifiedSelfItem,
|
||||
title: modifiedSelfItem.name ?? "",
|
||||
subtitle: subtitle,
|
||||
directStreamURL: directStreamURL,
|
||||
transcodedStreamURL: transcodedStreamURL?.url,
|
||||
hlsStreamURL: hlsStreamURL,
|
||||
streamType: streamType,
|
||||
response: response,
|
||||
audioStreams: audioStreams,
|
||||
subtitleStreams: subtitleStreams,
|
||||
chapters: modifiedSelfItem.chapters ?? [],
|
||||
selectedAudioStreamIndex: defaultAudioStream?.index ?? -1,
|
||||
selectedSubtitleStreamIndex: defaultSubtitleStream?.index ?? -1,
|
||||
subtitlesEnabled: subtitlesEnabled,
|
||||
autoplayEnabled: autoplayEnabled,
|
||||
overlayType: overlayType,
|
||||
shouldShowPlayPreviousItem: shouldShowPlayPreviousItem,
|
||||
shouldShowPlayNextItem: shouldShowPlayNextItem,
|
||||
shouldShowAutoPlay: shouldShowAutoPlay,
|
||||
container: currentMediaSource.container ?? "",
|
||||
filename: fileName,
|
||||
versionName: currentMediaSource.name
|
||||
)
|
||||
|
||||
viewModels.append(videoPlayerViewModel)
|
||||
if mediaSources.count > 1 {
|
||||
mediaSourceID = currentMediaSource.id!
|
||||
} else {
|
||||
mediaSourceID = self.id!
|
||||
}
|
||||
|
||||
return viewModels
|
||||
let directStreamBuilder = VideosAPI.getVideoStreamWithRequestBuilder(
|
||||
itemId: self.id!,
|
||||
_static: true,
|
||||
tag: self.etag,
|
||||
playSessionId: response.playSessionId,
|
||||
minSegments: 6,
|
||||
mediaSourceId: mediaSourceID
|
||||
)
|
||||
directStreamURL = URL(string: directStreamBuilder.URLString)!
|
||||
|
||||
if let transcodeURL = currentMediaSource.transcodingUrl {
|
||||
streamType = .transcode
|
||||
transcodedStreamURL = URLComponents(
|
||||
string: SessionManager.main.currentLogin.server.currentURI
|
||||
.appending(transcodeURL)
|
||||
)!
|
||||
} else {
|
||||
streamType = .direct
|
||||
transcodedStreamURL = nil
|
||||
}
|
||||
|
||||
let hlsStreamBuilder = DynamicHlsAPI.getMasterHlsVideoPlaylistWithRequestBuilder(
|
||||
itemId: id ?? "",
|
||||
mediaSourceId: id ?? "",
|
||||
_static: true,
|
||||
tag: currentMediaSource.eTag,
|
||||
deviceProfileId: nil,
|
||||
playSessionId: response.playSessionId,
|
||||
segmentContainer: "ts",
|
||||
segmentLength: nil,
|
||||
minSegments: 2,
|
||||
deviceId: UIDevice.vendorUUIDString,
|
||||
audioCodec: audioStreams
|
||||
.compactMap(\.codec)
|
||||
.joined(separator: ","),
|
||||
breakOnNonKeyFrames: true,
|
||||
requireAvc: true,
|
||||
transcodingMaxAudioChannels: 6,
|
||||
videoCodec: videoStream?.codec,
|
||||
videoStreamIndex: videoStream?.index,
|
||||
enableAdaptiveBitrateStreaming: true
|
||||
)
|
||||
|
||||
var hlsStreamComponents = URLComponents(string: hlsStreamBuilder.URLString)!
|
||||
hlsStreamComponents.addQueryItem(name: "api_key", value: SessionManager.main.currentLogin.user.accessToken)
|
||||
|
||||
hlsStreamURL = hlsStreamComponents.url!
|
||||
|
||||
// MARK: VidoPlayerViewModel Creation
|
||||
|
||||
var subtitle: String?
|
||||
|
||||
// MARK: Attach media content to self
|
||||
|
||||
var modifiedSelfItem = self
|
||||
modifiedSelfItem.mediaStreams = currentMediaSource.mediaStreams
|
||||
|
||||
// TODO: other forms of media subtitle
|
||||
if self.itemType == .episode {
|
||||
if let seriesName = self.seriesName, let episodeLocator = self.getEpisodeLocator() {
|
||||
subtitle = "\(seriesName) - \(episodeLocator)"
|
||||
}
|
||||
}
|
||||
|
||||
let subtitlesEnabled = defaultSubtitleStream != nil
|
||||
|
||||
let shouldShowAutoPlay = Defaults[.shouldShowAutoPlay] && itemType == .episode
|
||||
let autoplayEnabled = Defaults[.autoplayEnabled] && shouldShowAutoPlay
|
||||
|
||||
let overlayType = Defaults[.overlayType]
|
||||
|
||||
let shouldShowPlayPreviousItem = Defaults[.shouldShowPlayPreviousItem] && itemType == .episode
|
||||
let shouldShowPlayNextItem = Defaults[.shouldShowPlayNextItem] && itemType == .episode
|
||||
|
||||
var fileName: String?
|
||||
if let lastInPath = currentMediaSource.path?.split(separator: "/").last {
|
||||
fileName = String(lastInPath)
|
||||
}
|
||||
|
||||
let videoPlayerViewModel = VideoPlayerViewModel(
|
||||
item: modifiedSelfItem,
|
||||
title: modifiedSelfItem.name ?? "",
|
||||
subtitle: subtitle,
|
||||
directStreamURL: directStreamURL,
|
||||
transcodedStreamURL: transcodedStreamURL?.url,
|
||||
hlsStreamURL: hlsStreamURL,
|
||||
streamType: streamType,
|
||||
response: response,
|
||||
audioStreams: audioStreams,
|
||||
subtitleStreams: subtitleStreams,
|
||||
chapters: modifiedSelfItem.chapters ?? [],
|
||||
selectedAudioStreamIndex: defaultAudioStream?.index ?? -1,
|
||||
selectedSubtitleStreamIndex: defaultSubtitleStream?.index ?? -1,
|
||||
subtitlesEnabled: subtitlesEnabled,
|
||||
autoplayEnabled: autoplayEnabled,
|
||||
overlayType: overlayType,
|
||||
shouldShowPlayPreviousItem: shouldShowPlayPreviousItem,
|
||||
shouldShowPlayNextItem: shouldShowPlayNextItem,
|
||||
shouldShowAutoPlay: shouldShowAutoPlay,
|
||||
container: currentMediaSource.container ?? "",
|
||||
filename: fileName,
|
||||
versionName: currentMediaSource.name
|
||||
)
|
||||
|
||||
viewModels.append(videoPlayerViewModel)
|
||||
}
|
||||
|
||||
return viewModels
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
@ -211,137 +211,137 @@ extension BaseItemDto {
|
||||
var viewModels: [VideoPlayerViewModel] = []
|
||||
|
||||
for currentMediaSource in mediaSources {
|
||||
let videoStream = currentMediaSource.mediaStreams?.filter { $0.type == .video }.first
|
||||
let audioStreams = currentMediaSource.mediaStreams?.filter { $0.type == .audio } ?? []
|
||||
let subtitleStreams = currentMediaSource.mediaStreams?.filter { $0.type == .subtitle } ?? []
|
||||
let videoStream = currentMediaSource.mediaStreams?.filter { $0.type == .video }.first
|
||||
let audioStreams = currentMediaSource.mediaStreams?.filter { $0.type == .audio } ?? []
|
||||
let subtitleStreams = currentMediaSource.mediaStreams?.filter { $0.type == .subtitle } ?? []
|
||||
|
||||
let defaultAudioStream = audioStreams.first(where: { $0.index! == currentMediaSource.defaultAudioStreamIndex! })
|
||||
let defaultAudioStream = audioStreams.first(where: { $0.index! == currentMediaSource.defaultAudioStreamIndex! })
|
||||
|
||||
let defaultSubtitleStream = subtitleStreams
|
||||
.first(where: { $0.index! == currentMediaSource.defaultSubtitleStreamIndex ?? -1 })
|
||||
let defaultSubtitleStream = subtitleStreams
|
||||
.first(where: { $0.index! == currentMediaSource.defaultSubtitleStreamIndex ?? -1 })
|
||||
|
||||
// MARK: Build Streams
|
||||
// MARK: Build Streams
|
||||
|
||||
let directStreamURL: URL
|
||||
let transcodedStreamURL: URLComponents?
|
||||
var hlsStreamURL: URL
|
||||
let mediaSourceID: String
|
||||
let streamType: ServerStreamType
|
||||
let directStreamURL: URL
|
||||
let transcodedStreamURL: URLComponents?
|
||||
var hlsStreamURL: URL
|
||||
let mediaSourceID: String
|
||||
let streamType: ServerStreamType
|
||||
|
||||
if mediaSources.count > 1 {
|
||||
mediaSourceID = currentMediaSource.id!
|
||||
} else {
|
||||
mediaSourceID = self.id!
|
||||
}
|
||||
|
||||
let directStreamBuilder = VideosAPI.getVideoStreamWithRequestBuilder(
|
||||
itemId: self.id!,
|
||||
_static: true,
|
||||
tag: self.etag,
|
||||
playSessionId: response.playSessionId,
|
||||
minSegments: 6,
|
||||
mediaSourceId: mediaSourceID
|
||||
)
|
||||
directStreamURL = URL(string: directStreamBuilder.URLString)!
|
||||
|
||||
if let transcodeURL = currentMediaSource.transcodingUrl, !Defaults[.Experimental.liveTVForceDirectPlay] {
|
||||
streamType = .transcode
|
||||
transcodedStreamURL = URLComponents(
|
||||
string: SessionManager.main.currentLogin.server.currentURI
|
||||
.appending(transcodeURL)
|
||||
)!
|
||||
} else {
|
||||
streamType = .direct
|
||||
transcodedStreamURL = nil
|
||||
}
|
||||
|
||||
let hlsStreamBuilder = DynamicHlsAPI.getMasterHlsVideoPlaylistWithRequestBuilder(
|
||||
itemId: id ?? "",
|
||||
mediaSourceId: id ?? "",
|
||||
_static: true,
|
||||
tag: currentMediaSource.eTag,
|
||||
deviceProfileId: nil,
|
||||
playSessionId: response.playSessionId,
|
||||
segmentContainer: "ts",
|
||||
segmentLength: nil,
|
||||
minSegments: 2,
|
||||
deviceId: UIDevice.vendorUUIDString,
|
||||
audioCodec: audioStreams
|
||||
.compactMap(\.codec)
|
||||
.joined(separator: ","),
|
||||
breakOnNonKeyFrames: true,
|
||||
requireAvc: true,
|
||||
transcodingMaxAudioChannels: 6,
|
||||
videoCodec: videoStream?.codec,
|
||||
videoStreamIndex: videoStream?.index,
|
||||
enableAdaptiveBitrateStreaming: true
|
||||
)
|
||||
|
||||
var hlsStreamComponents = URLComponents(string: hlsStreamBuilder.URLString)!
|
||||
hlsStreamComponents.addQueryItem(name: "api_key", value: SessionManager.main.currentLogin.user.accessToken)
|
||||
|
||||
hlsStreamURL = hlsStreamComponents.url!
|
||||
|
||||
// MARK: VidoPlayerViewModel Creation
|
||||
|
||||
var subtitle: String?
|
||||
|
||||
// MARK: Attach media content to self
|
||||
|
||||
var modifiedSelfItem = self
|
||||
modifiedSelfItem.mediaStreams = currentMediaSource.mediaStreams
|
||||
|
||||
// TODO: other forms of media subtitle
|
||||
if self.itemType == .episode {
|
||||
if let seriesName = self.seriesName, let episodeLocator = self.getEpisodeLocator() {
|
||||
subtitle = "\(seriesName) - \(episodeLocator)"
|
||||
}
|
||||
}
|
||||
|
||||
let subtitlesEnabled = defaultSubtitleStream != nil
|
||||
|
||||
let shouldShowAutoPlay = Defaults[.shouldShowAutoPlay] && itemType == .episode
|
||||
let autoplayEnabled = Defaults[.autoplayEnabled] && shouldShowAutoPlay
|
||||
|
||||
let overlayType = Defaults[.overlayType]
|
||||
|
||||
let shouldShowPlayPreviousItem = Defaults[.shouldShowPlayPreviousItem] && itemType == .episode
|
||||
let shouldShowPlayNextItem = Defaults[.shouldShowPlayNextItem] && itemType == .episode
|
||||
|
||||
var fileName: String?
|
||||
if let lastInPath = currentMediaSource.path?.split(separator: "/").last {
|
||||
fileName = String(lastInPath)
|
||||
}
|
||||
|
||||
let videoPlayerViewModel = VideoPlayerViewModel(
|
||||
item: modifiedSelfItem,
|
||||
title: modifiedSelfItem.name ?? "",
|
||||
subtitle: subtitle,
|
||||
directStreamURL: directStreamURL,
|
||||
transcodedStreamURL: transcodedStreamURL?.url,
|
||||
hlsStreamURL: hlsStreamURL,
|
||||
streamType: streamType,
|
||||
response: response,
|
||||
audioStreams: audioStreams,
|
||||
subtitleStreams: subtitleStreams,
|
||||
chapters: modifiedSelfItem.chapters ?? [],
|
||||
selectedAudioStreamIndex: defaultAudioStream?.index ?? -1,
|
||||
selectedSubtitleStreamIndex: defaultSubtitleStream?.index ?? -1,
|
||||
subtitlesEnabled: subtitlesEnabled,
|
||||
autoplayEnabled: autoplayEnabled,
|
||||
overlayType: overlayType,
|
||||
shouldShowPlayPreviousItem: shouldShowPlayPreviousItem,
|
||||
shouldShowPlayNextItem: shouldShowPlayNextItem,
|
||||
shouldShowAutoPlay: shouldShowAutoPlay,
|
||||
container: currentMediaSource.container ?? "",
|
||||
filename: fileName,
|
||||
versionName: currentMediaSource.name
|
||||
)
|
||||
|
||||
viewModels.append(videoPlayerViewModel)
|
||||
if mediaSources.count > 1 {
|
||||
mediaSourceID = currentMediaSource.id!
|
||||
} else {
|
||||
mediaSourceID = self.id!
|
||||
}
|
||||
|
||||
return viewModels
|
||||
let directStreamBuilder = VideosAPI.getVideoStreamWithRequestBuilder(
|
||||
itemId: self.id!,
|
||||
_static: true,
|
||||
tag: self.etag,
|
||||
playSessionId: response.playSessionId,
|
||||
minSegments: 6,
|
||||
mediaSourceId: mediaSourceID
|
||||
)
|
||||
directStreamURL = URL(string: directStreamBuilder.URLString)!
|
||||
|
||||
if let transcodeURL = currentMediaSource.transcodingUrl, !Defaults[.Experimental.liveTVForceDirectPlay] {
|
||||
streamType = .transcode
|
||||
transcodedStreamURL = URLComponents(
|
||||
string: SessionManager.main.currentLogin.server.currentURI
|
||||
.appending(transcodeURL)
|
||||
)!
|
||||
} else {
|
||||
streamType = .direct
|
||||
transcodedStreamURL = nil
|
||||
}
|
||||
|
||||
let hlsStreamBuilder = DynamicHlsAPI.getMasterHlsVideoPlaylistWithRequestBuilder(
|
||||
itemId: id ?? "",
|
||||
mediaSourceId: id ?? "",
|
||||
_static: true,
|
||||
tag: currentMediaSource.eTag,
|
||||
deviceProfileId: nil,
|
||||
playSessionId: response.playSessionId,
|
||||
segmentContainer: "ts",
|
||||
segmentLength: nil,
|
||||
minSegments: 2,
|
||||
deviceId: UIDevice.vendorUUIDString,
|
||||
audioCodec: audioStreams
|
||||
.compactMap(\.codec)
|
||||
.joined(separator: ","),
|
||||
breakOnNonKeyFrames: true,
|
||||
requireAvc: true,
|
||||
transcodingMaxAudioChannels: 6,
|
||||
videoCodec: videoStream?.codec,
|
||||
videoStreamIndex: videoStream?.index,
|
||||
enableAdaptiveBitrateStreaming: true
|
||||
)
|
||||
|
||||
var hlsStreamComponents = URLComponents(string: hlsStreamBuilder.URLString)!
|
||||
hlsStreamComponents.addQueryItem(name: "api_key", value: SessionManager.main.currentLogin.user.accessToken)
|
||||
|
||||
hlsStreamURL = hlsStreamComponents.url!
|
||||
|
||||
// MARK: VidoPlayerViewModel Creation
|
||||
|
||||
var subtitle: String?
|
||||
|
||||
// MARK: Attach media content to self
|
||||
|
||||
var modifiedSelfItem = self
|
||||
modifiedSelfItem.mediaStreams = currentMediaSource.mediaStreams
|
||||
|
||||
// TODO: other forms of media subtitle
|
||||
if self.itemType == .episode {
|
||||
if let seriesName = self.seriesName, let episodeLocator = self.getEpisodeLocator() {
|
||||
subtitle = "\(seriesName) - \(episodeLocator)"
|
||||
}
|
||||
}
|
||||
|
||||
let subtitlesEnabled = defaultSubtitleStream != nil
|
||||
|
||||
let shouldShowAutoPlay = Defaults[.shouldShowAutoPlay] && itemType == .episode
|
||||
let autoplayEnabled = Defaults[.autoplayEnabled] && shouldShowAutoPlay
|
||||
|
||||
let overlayType = Defaults[.overlayType]
|
||||
|
||||
let shouldShowPlayPreviousItem = Defaults[.shouldShowPlayPreviousItem] && itemType == .episode
|
||||
let shouldShowPlayNextItem = Defaults[.shouldShowPlayNextItem] && itemType == .episode
|
||||
|
||||
var fileName: String?
|
||||
if let lastInPath = currentMediaSource.path?.split(separator: "/").last {
|
||||
fileName = String(lastInPath)
|
||||
}
|
||||
|
||||
let videoPlayerViewModel = VideoPlayerViewModel(
|
||||
item: modifiedSelfItem,
|
||||
title: modifiedSelfItem.name ?? "",
|
||||
subtitle: subtitle,
|
||||
directStreamURL: directStreamURL,
|
||||
transcodedStreamURL: transcodedStreamURL?.url,
|
||||
hlsStreamURL: hlsStreamURL,
|
||||
streamType: streamType,
|
||||
response: response,
|
||||
audioStreams: audioStreams,
|
||||
subtitleStreams: subtitleStreams,
|
||||
chapters: modifiedSelfItem.chapters ?? [],
|
||||
selectedAudioStreamIndex: defaultAudioStream?.index ?? -1,
|
||||
selectedSubtitleStreamIndex: defaultSubtitleStream?.index ?? -1,
|
||||
subtitlesEnabled: subtitlesEnabled,
|
||||
autoplayEnabled: autoplayEnabled,
|
||||
overlayType: overlayType,
|
||||
shouldShowPlayPreviousItem: shouldShowPlayPreviousItem,
|
||||
shouldShowPlayNextItem: shouldShowPlayNextItem,
|
||||
shouldShowAutoPlay: shouldShowAutoPlay,
|
||||
container: currentMediaSource.container ?? "",
|
||||
filename: fileName,
|
||||
versionName: currentMediaSource.name
|
||||
)
|
||||
|
||||
viewModels.append(videoPlayerViewModel)
|
||||
}
|
||||
|
||||
return viewModels
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
// Copyright (c) 2022 Jellyfin & Jellyfin Contributors
|
||||
//
|
||||
|
||||
import UIKit
|
||||
#if os(tvOS)
|
||||
import TVVLCKit
|
||||
#else
|
||||
@ -22,4 +23,14 @@ extension VLCMediaPlayer {
|
||||
with: size.textRendererFontSize
|
||||
)
|
||||
}
|
||||
|
||||
/// Applies font to the player
|
||||
///
|
||||
/// This is pretty hacky until VLCKit 4 has a public API to support this
|
||||
func setSubtitleFont(fontName: String) {
|
||||
perform(
|
||||
Selector(("setTextRendererFont:")),
|
||||
with: fontName
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -396,6 +396,8 @@ internal enum L10n {
|
||||
internal static var studio: String { return L10n.tr("Localizable", "studio") }
|
||||
/// Studios
|
||||
internal static var studios: String { return L10n.tr("Localizable", "studios") }
|
||||
/// Subtitle Font
|
||||
internal static var subtitleFont: String { return L10n.tr("Localizable", "subtitleFont") }
|
||||
/// Subtitles
|
||||
internal static var subtitles: String { return L10n.tr("Localizable", "subtitles") }
|
||||
/// Subtitle Size
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
import Defaults
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
extension SwiftfinStore {
|
||||
enum Defaults {
|
||||
@ -69,6 +70,11 @@ extension Defaults.Keys {
|
||||
)
|
||||
static let autoplayEnabled = Key<Bool>("autoPlayNextItem", default: true, suite: SwiftfinStore.Defaults.generalSuite)
|
||||
static let resumeOffset = Key<Bool>("resumeOffset", default: false, suite: SwiftfinStore.Defaults.generalSuite)
|
||||
static let subtitleFontName = Key<String>(
|
||||
"subtitleFontName",
|
||||
default: UIFont.systemFont(ofSize: 14).fontName,
|
||||
suite: SwiftfinStore.Defaults.generalSuite
|
||||
)
|
||||
static let subtitleSize = Key<SubtitleSize>("subtitleSize", default: .regular, suite: SwiftfinStore.Defaults.generalSuite)
|
||||
|
||||
// Should show video player items
|
||||
|
@ -221,6 +221,7 @@
|
||||
62C29EA326D1030F00C1D2E7 /* ConnectToServerCoodinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C29EA226D1030F00C1D2E7 /* ConnectToServerCoodinator.swift */; };
|
||||
62C29EA626D1036A00C1D2E7 /* HomeCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C29EA526D1036A00C1D2E7 /* HomeCoordinator.swift */; };
|
||||
62C29EA826D103D500C1D2E7 /* LibraryListCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C29EA726D103D500C1D2E7 /* LibraryListCoordinator.swift */; };
|
||||
62C83B08288C6A630004ED0C /* FontPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C83B07288C6A630004ED0C /* FontPicker.swift */; };
|
||||
62E1DCC3273CE19800C9AE76 /* URLExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E1DCC2273CE19800C9AE76 /* URLExtensions.swift */; };
|
||||
62E1DCC4273CE19800C9AE76 /* URLExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E1DCC2273CE19800C9AE76 /* URLExtensions.swift */; };
|
||||
62E1DCC5273CE19800C9AE76 /* URLExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62E1DCC2273CE19800C9AE76 /* URLExtensions.swift */; };
|
||||
@ -732,6 +733,7 @@
|
||||
62C29EA226D1030F00C1D2E7 /* ConnectToServerCoodinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectToServerCoodinator.swift; sourceTree = "<group>"; };
|
||||
62C29EA526D1036A00C1D2E7 /* HomeCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCoordinator.swift; sourceTree = "<group>"; };
|
||||
62C29EA726D103D500C1D2E7 /* LibraryListCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryListCoordinator.swift; sourceTree = "<group>"; };
|
||||
62C83B07288C6A630004ED0C /* FontPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontPicker.swift; sourceTree = "<group>"; };
|
||||
62E1DCC2273CE19800C9AE76 /* URLExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLExtensions.swift; sourceTree = "<group>"; };
|
||||
62E632D9267D2BC40063E547 /* LatestMediaViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LatestMediaViewModel.swift; sourceTree = "<group>"; };
|
||||
62E632DB267D2E130063E547 /* LibrarySearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibrarySearchViewModel.swift; sourceTree = "<group>"; };
|
||||
@ -1658,6 +1660,7 @@
|
||||
E1D4BF7B2719D05000A11E64 /* BasicAppSettingsView.swift */,
|
||||
5338F74D263B61370014BF09 /* ConnectToServerView.swift */,
|
||||
5389276D263C25100035E14B /* ContinueWatchingView.swift */,
|
||||
62C83B07288C6A630004ED0C /* FontPicker.swift */,
|
||||
625CB56E2678C23300530A6E /* HomeView.swift */,
|
||||
E1EBCB45278BD595009FE6E9 /* ItemOverviewView.swift */,
|
||||
E14F7D0A26DB3714007C3AE6 /* ItemView */,
|
||||
@ -2414,6 +2417,7 @@
|
||||
6220D0C026D61C5000B8E046 /* ItemCoordinator.swift in Sources */,
|
||||
E13DD3F227179378009D4DAF /* UserSignInCoordinator.swift in Sources */,
|
||||
621338932660107500A81A2A /* StringExtensions.swift in Sources */,
|
||||
62C83B08288C6A630004ED0C /* FontPicker.swift in Sources */,
|
||||
53FF7F2A263CF3F500585C35 /* LatestMediaView.swift in Sources */,
|
||||
E122A9132788EAAD0060FA63 /* MediaStreamExtension.swift in Sources */,
|
||||
E1C812C5277A90B200918266 /* URLComponentsExtensions.swift in Sources */,
|
||||
|
35
Swiftfin/Views/FontPicker.swift
Normal file
35
Swiftfin/Views/FontPicker.swift
Normal file
@ -0,0 +1,35 @@
|
||||
//
|
||||
// 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 SwiftUI
|
||||
import UIKit
|
||||
|
||||
struct FontPickerView: UIViewControllerRepresentable {
|
||||
func makeUIViewController(context: Context) -> UIFontPickerViewController {
|
||||
let configuration = UIFontPickerViewController.Configuration()
|
||||
configuration.includeFaces = true
|
||||
|
||||
let fontViewController = UIFontPickerViewController(configuration: configuration)
|
||||
fontViewController.delegate = context.coordinator
|
||||
return fontViewController
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIFontPickerViewController, context: Context) {}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator()
|
||||
}
|
||||
|
||||
class Coordinator: NSObject, UIFontPickerViewControllerDelegate {
|
||||
func fontPickerViewControllerDidPickFont(_ viewController: UIFontPickerViewController) {
|
||||
guard let descriptor = viewController.selectedFontDescriptor else { return }
|
||||
Defaults[.subtitleFontName] = descriptor.postscriptName
|
||||
}
|
||||
}
|
||||
}
|
@ -48,6 +48,8 @@ struct SettingsView: View {
|
||||
var resumeOffset
|
||||
@Default(.subtitleSize)
|
||||
var subtitleSize
|
||||
@Default(.subtitleFontName)
|
||||
var subtitleFontName
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
@ -185,6 +187,20 @@ struct SettingsView: View {
|
||||
Text(appearance.localizedName).tag(appearance.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
settingsRouter.route(to: \.fontPicker)
|
||||
} label: {
|
||||
HStack {
|
||||
L10n.subtitleFont.text
|
||||
.foregroundColor(.primary)
|
||||
Spacer()
|
||||
Text(subtitleFontName)
|
||||
.foregroundColor(.gray)
|
||||
Image(systemName: "chevron.right")
|
||||
}
|
||||
}
|
||||
|
||||
Picker(L10n.subtitleSize, selection: $subtitleSize) {
|
||||
ForEach(SubtitleSize.allCases, id: \.self) { size in
|
||||
Text(size.label).tag(size.rawValue)
|
||||
|
@ -568,6 +568,7 @@ extension VLCPlayerViewController {
|
||||
vlcMediaPlayer.delegate = self
|
||||
vlcMediaPlayer.drawable = videoContentView
|
||||
|
||||
vlcMediaPlayer.setSubtitleFont(fontName: Defaults[.subtitleFontName])
|
||||
vlcMediaPlayer.setSubtitleSize(Defaults[.subtitleSize])
|
||||
|
||||
stopOverlayDismissTimer()
|
||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user