mirror of
https://github.com/jellyfin/Swiftfin.git
synced 2024-11-27 16:20:25 +00:00
add aspect fill and update packages
This commit is contained in:
parent
44229beee0
commit
45bafed1e9
@ -13,4 +13,19 @@ extension CGSize {
|
||||
static func Circle(radius: CGFloat) -> CGSize {
|
||||
CGSize(width: radius, height: radius)
|
||||
}
|
||||
|
||||
// From https://gist.github.com/jkosoy/c835fea2c03e76720c77
|
||||
static func aspectFill(aspectRatio: CGSize, minimumSize: CGSize) -> CGSize {
|
||||
var minimumSize = minimumSize
|
||||
let mW = minimumSize.width / aspectRatio.width
|
||||
let mH = minimumSize.height / aspectRatio.height
|
||||
|
||||
if mH > mW {
|
||||
minimumSize.width = minimumSize.height / aspectRatio.height * aspectRatio.width
|
||||
} else if mW > mH {
|
||||
minimumSize.height = minimumSize.width / aspectRatio.width * aspectRatio.height
|
||||
}
|
||||
|
||||
return minimumSize
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,8 @@
|
||||
"repositoryURL": "https://github.com/CombineCommunity/CombineExt",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "8ca006df5e3cc6bb176b70238e2b0014bbc3a235",
|
||||
"version": "1.0.0"
|
||||
"revision": "0880829102152185190064fd17847a7c681d2127",
|
||||
"version": "1.5.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -42,8 +42,8 @@
|
||||
"repositoryURL": "https://github.com/sindresorhus/Defaults",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "8a6e4a96fd38504a05903d136c85634b65fd7c4d",
|
||||
"version": "6.0.0"
|
||||
"revision": "55f3302c3ab30a8760f10042d0ebc0a6907f865a",
|
||||
"version": "6.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -96,8 +96,8 @@
|
||||
"repositoryURL": "https://github.com/sushichop/Puppy",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "dc82e65c749cee431ffbb8c0913680b61ccd7e08",
|
||||
"version": "0.2.0"
|
||||
"revision": "95ce04b0e778b8d7c351876bc98bbf68328dfc9b",
|
||||
"version": "0.3.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -105,8 +105,8 @@
|
||||
"repositoryURL": "https://github.com/rundfunk47/stinsen",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "5e6c714f6f308877c8a988523915f9eb592d7d82",
|
||||
"version": "2.0.3"
|
||||
"revision": "36d97964075dc770046ddef9346a29bfa8982d6d",
|
||||
"version": "2.0.7"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -87,6 +87,8 @@ struct VLCPlayerOverlayView: View {
|
||||
|
||||
HStack(spacing: 20) {
|
||||
|
||||
// MARK: Previous Item
|
||||
|
||||
if viewModel.shouldShowPlayPreviousItem {
|
||||
Button {
|
||||
viewModel.playerOverlayDelegate?.didSelectPlayPreviousItem()
|
||||
@ -97,6 +99,8 @@ struct VLCPlayerOverlayView: View {
|
||||
.foregroundColor(viewModel.nextItemVideoPlayerViewModel == nil ? .gray : .white)
|
||||
}
|
||||
|
||||
// MARK: Next Item
|
||||
|
||||
if viewModel.shouldShowPlayNextItem {
|
||||
Button {
|
||||
viewModel.playerOverlayDelegate?.didSelectPlayNextItem()
|
||||
@ -107,6 +111,8 @@ struct VLCPlayerOverlayView: View {
|
||||
.foregroundColor(viewModel.nextItemVideoPlayerViewModel == nil ? .gray : .white)
|
||||
}
|
||||
|
||||
// MARK: Autoplay
|
||||
|
||||
if viewModel.shouldShowAutoPlay {
|
||||
Button {
|
||||
viewModel.autoplayEnabled.toggle()
|
||||
@ -119,6 +125,8 @@ struct VLCPlayerOverlayView: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Subtitle Toggle
|
||||
|
||||
if !viewModel.subtitleStreams.isEmpty {
|
||||
Button {
|
||||
viewModel.subtitlesEnabled.toggle()
|
||||
@ -133,10 +141,32 @@ struct VLCPlayerOverlayView: View {
|
||||
.foregroundColor(viewModel.selectedSubtitleStreamIndex == -1 ? .gray : .white)
|
||||
}
|
||||
|
||||
// MARK: Screen Fill
|
||||
|
||||
Button {
|
||||
viewModel.playerOverlayDelegate?.didSelectScreenFill()
|
||||
} label: {
|
||||
if viewModel.playerOverlayDelegate?.getScreenFilled() ?? true {
|
||||
if viewModel.playerOverlayDelegate?.isVideoAspectRatioGreater() ?? true {
|
||||
Image(systemName: "rectangle.arrowtriangle.2.inward")
|
||||
} else {
|
||||
Image(systemName: "rectangle.portrait.arrowtriangle.2.inward")
|
||||
}
|
||||
} else {
|
||||
if viewModel.playerOverlayDelegate?.isVideoAspectRatioGreater() ?? true {
|
||||
Image(systemName: "rectangle.arrowtriangle.2.outward")
|
||||
} else {
|
||||
Image(systemName: "rectangle.portrait.arrowtriangle.2.outward")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Settings Menu
|
||||
|
||||
Menu {
|
||||
|
||||
// MARK: Audio Streams
|
||||
|
||||
Menu {
|
||||
ForEach(viewModel.audioStreams, id: \.self) { audioStream in
|
||||
Button {
|
||||
@ -156,6 +186,8 @@ struct VLCPlayerOverlayView: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Subtitle Streams
|
||||
|
||||
Menu {
|
||||
ForEach(viewModel.subtitleStreams, id: \.self) { subtitleStream in
|
||||
Button {
|
||||
@ -175,6 +207,8 @@ struct VLCPlayerOverlayView: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Playback Speed
|
||||
|
||||
Menu {
|
||||
ForEach(PlaybackSpeed.allCases, id: \.self) { speed in
|
||||
Button {
|
||||
@ -194,6 +228,8 @@ struct VLCPlayerOverlayView: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Chapters
|
||||
|
||||
if !viewModel.chapters.isEmpty {
|
||||
Button {
|
||||
viewModel.playerOverlayDelegate?.didSelectChapters()
|
||||
@ -205,6 +241,8 @@ struct VLCPlayerOverlayView: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Jump Button Lengths
|
||||
|
||||
if viewModel.shouldShowJumpButtonsInOverlayMenu {
|
||||
Menu {
|
||||
ForEach(VideoPlayerJumpLength.allCases, id: \.self) { forwardLength in
|
||||
|
@ -32,4 +32,10 @@ protocol PlayerOverlayDelegate {
|
||||
|
||||
func didSelectChapters()
|
||||
func didSelectChapter(_ chapter: ChapterInfo)
|
||||
|
||||
func didSelectScreenFill()
|
||||
func getScreenFilled() -> Bool
|
||||
// Returns whether the aspect ratio of the video
|
||||
// is greater than the aspect ratio of the screen
|
||||
func isVideoAspectRatioGreater() -> Bool
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ class VLCPlayerViewController: UIViewController {
|
||||
private var lastProgressReportTicks: Int64 = 0
|
||||
private var viewModelListeners = Set<AnyCancellable>()
|
||||
private var overlayDismissTimer: Timer?
|
||||
private var isScreenFilled: Bool = false
|
||||
|
||||
private var currentPlayerTicks: Int64 {
|
||||
Int64(vlcMediaPlayer.time.intValue) * 100_000
|
||||
@ -42,7 +43,7 @@ class VLCPlayerViewController: UIViewController {
|
||||
}
|
||||
|
||||
private lazy var videoContentView = makeVideoContentView()
|
||||
private lazy var mainGestureView = makeTapGestureView()
|
||||
private lazy var mainGestureView = makeMainGestureView()
|
||||
private var currentOverlayHostingController: UIHostingController<VLCPlayerOverlayView>?
|
||||
private var currentChapterOverlayHostingController: UIHostingController<VLCPlayerChapterOverlayView>?
|
||||
private var currentJumpBackwardOverlayView: UIImageView?
|
||||
@ -152,7 +153,7 @@ class VLCPlayerViewController: UIViewController {
|
||||
return view
|
||||
}
|
||||
|
||||
private func makeTapGestureView() -> UIView {
|
||||
private func makeMainGestureView() -> UIView {
|
||||
let view = UIView()
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
@ -164,7 +165,10 @@ class VLCPlayerViewController: UIViewController {
|
||||
let leftSwipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(didLeftSwipe))
|
||||
leftSwipeGesture.direction = .left
|
||||
|
||||
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(didPinch(_:)))
|
||||
|
||||
view.addGestureRecognizer(singleTapGesture)
|
||||
view.addGestureRecognizer(pinchGesture)
|
||||
|
||||
if viewModel.jumpGesturesEnabled {
|
||||
view.addGestureRecognizer(rightSwipeGesture)
|
||||
@ -189,6 +193,23 @@ class VLCPlayerViewController: UIViewController {
|
||||
self.didSelectBackward()
|
||||
}
|
||||
|
||||
private var pinchScale: CGFloat = 1
|
||||
|
||||
@objc
|
||||
private func didPinch(_ gestureRecognizer: UIPinchGestureRecognizer) {
|
||||
if gestureRecognizer.state == .began || gestureRecognizer.state == .changed {
|
||||
pinchScale = gestureRecognizer.scale
|
||||
} else {
|
||||
isScreenFilled.toggle()
|
||||
|
||||
if pinchScale > 1 {
|
||||
fillScreen()
|
||||
} else {
|
||||
shrinkScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: setupOverlayHostingController
|
||||
|
||||
private func setupOverlayHostingController(viewModel: VideoPlayerViewModel) {
|
||||
@ -814,4 +835,53 @@ extension VLCPlayerViewController: PlayerOverlayDelegate {
|
||||
|
||||
viewModel.sendProgressReport()
|
||||
}
|
||||
|
||||
func didSelectScreenFill() {
|
||||
|
||||
isScreenFilled.toggle()
|
||||
|
||||
if isScreenFilled {
|
||||
fillScreen()
|
||||
} else {
|
||||
shrinkScreen()
|
||||
}
|
||||
}
|
||||
|
||||
private func fillScreen() {
|
||||
let screenSize = UIScreen.main.bounds.size
|
||||
let videoSize = vlcMediaPlayer.videoSize
|
||||
let fillSize = CGSize.aspectFill(aspectRatio: videoSize, minimumSize: screenSize)
|
||||
|
||||
let scale: CGFloat
|
||||
|
||||
if fillSize.height > screenSize.height {
|
||||
scale = fillSize.height / screenSize.height
|
||||
} else {
|
||||
scale = fillSize.width / screenSize.width
|
||||
}
|
||||
|
||||
UIView.animate(withDuration: 0.2) {
|
||||
self.videoContentView.transform = CGAffineTransform(scaleX: scale, y: scale)
|
||||
}
|
||||
}
|
||||
|
||||
private func shrinkScreen() {
|
||||
UIView.animate(withDuration: 0.2) {
|
||||
self.videoContentView.transform = .identity
|
||||
}
|
||||
}
|
||||
|
||||
func getScreenFilled() -> Bool {
|
||||
isScreenFilled
|
||||
}
|
||||
|
||||
func isVideoAspectRatioGreater() -> Bool {
|
||||
let screenSize = UIScreen.main.bounds.size
|
||||
let videoSize = vlcMediaPlayer.videoSize
|
||||
|
||||
let screenAspectRatio = screenSize.width / screenSize.height
|
||||
let videoAspectRatio = videoSize.width / videoSize.height
|
||||
|
||||
return videoAspectRatio > screenAspectRatio
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user