Merge remote-tracking branch 'origin/main' into main

This commit is contained in:
Aiden Vigue 2021-08-06 15:59:06 -06:00
commit c5517a2e59
No known key found for this signature in database
GPG Key ID: B9A09843AB079D5B
13 changed files with 242 additions and 38 deletions

View File

@ -42,7 +42,7 @@ jobs:
path: .build
key: ${{ runner.os }}-${{ matrix.scheme }}-spm-${{ hashFiles('**/Package.resolved') }}
restore-keys: |
${{ runner.os }}-${{ matrix.scheme }}-spm-
${{ runner.os }}-${{ matrix.scheme }}-spm2-
- name: Cache DerivedData folder
uses: actions/cache@v2

View File

@ -53,8 +53,8 @@ struct MainTabView: View {
Text("All Media")
Image(systemName: "folder")
}
.tag(Tab.home)
.tag(Tab.allMedia)
SettingsView(viewModel: SettingsViewModel())
.offset(y: -1) // don't remove this. it breaks tabview on 4K displays.
.tabItem {

View File

@ -76,7 +76,7 @@ struct SettingsView: View {
}
Button {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
SessionManager.current.logout();
SessionManager.current.logout()
let nc = NotificationCenter.default
nc.post(name: Notification.Name("didSignOut"), object: nil)
}

View File

@ -582,11 +582,11 @@ class VideoPlayerViewController: UIViewController, VideoPlayerSettingsDelegate,
updateNowPlayingCenter(time: nil, playing: mediaPlayer.state == .playing)
if (eventName == "timeupdate" && mediaPlayer.state == .playing) || eventName != "timeupdate" {
var ticks: Int64 = Int64(mediaPlayer.position * Float(manifest.runTimeTicks!));
if(ticks == 0) {
var ticks: Int64 = Int64(mediaPlayer.position * Float(manifest.runTimeTicks!))
if ticks == 0 {
ticks = manifest.userData?.playbackPositionTicks ?? 0
}
let progressInfo = PlaybackProgressInfo(canSeek: true, item: manifest, itemId: manifest.id, sessionId: playSessionId, mediaSourceId: manifest.id, audioStreamIndex: Int(selectedAudioTrack), subtitleStreamIndex: Int(selectedCaptionTrack), isPaused: (!playing), isMuted: false, positionTicks: ticks, playbackStartTimeTicks: Int64(startTime), volumeLevel: 100, brightness: 100, aspectRatio: nil, playMethod: playbackItem.videoType, liveStreamId: nil, playSessionId: playSessionId, repeatMode: .repeatNone, nowPlayingQueue: [], playlistItemId: "playlistItem0")
PlaystateAPI.reportPlaybackProgress(playbackProgressInfo: progressInfo)

View File

@ -47,7 +47,7 @@ struct PortraitItemView: View {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.accentColor)
.background(Color(.white))
.cornerRadius(.infinity)
.clipShape(Circle().scale(0.8))
} else {
if item.userData?.unplayedItemCount != nil {
Capsule()

View File

@ -192,7 +192,7 @@ class EmailHelper: NSObject, MFMailComposeViewControllerDelegate {
let data = fileManager.contents(atPath: logURL.path)
picker.setSubject("[DEV-BUG] SwiftFin")
picker.setMessageBody("Please don't edit this email.\n Please don't change the subject. \nUDID: \(UIDevice.current.identifierForVendor?.uuidString ?? "NIL")\n", isHTML: false);
picker.setMessageBody("Please don't edit this email.\n Please don't change the subject. \nUDID: \(UIDevice.current.identifierForVendor?.uuidString ?? "NIL")\n", isHTML: false)
picker.setToRecipients(["SwiftFin Bug Reports <swiftfin-bugs@jellyfin.org>"])
picker.addAttachmentData(data!, mimeType: "text/plain", fileName: logURL.lastPathComponent)
picker.mailComposeDelegate = self

View File

@ -9,28 +9,232 @@ import SwiftUI
struct SeriesItemView: View {
@StateObject var viewModel: SeriesItemViewModel
@State private var orientation = UIDeviceOrientation.unknown
@Environment(\.horizontalSizeClass) var hSizeClass
@Environment(\.verticalSizeClass) var vSizeClass
@State private var tracks: [GridItem] = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 125)
@ViewBuilder
var portraitHeaderView: some View {
ImageView(src: viewModel.item
.getBackdropImage(maxWidth: UIDevice.current.userInterfaceIdiom == .pad ? 622 : Int(UIScreen.main.bounds.width)),
bh: viewModel.item.getBackdropImageBlurHash())
.opacity(0.4)
.blur(radius: 2.0)
}
var portraitHeaderOverlayView: some View {
HStack(alignment: .bottom, spacing: 12) {
ImageView(src: viewModel.item.getPrimaryImage(maxWidth: 120), bh: viewModel.item.getPrimaryImageBlurHash())
.frame(width: 120, height: 180)
.cornerRadius(10)
VStack(alignment: .leading) {
Text(viewModel.item.name ?? "").font(.headline)
.fontWeight(.semibold)
.foregroundColor(.primary)
.fixedSize(horizontal: false, vertical: true)
.offset(y: -4)
HStack {
Text(String(viewModel.item.productionYear ?? 0)).font(.subheadline)
.fontWeight(.medium)
.foregroundColor(.secondary)
.lineLimit(1)
if let officialRating = viewModel.item.officialRating {
Text(officialRating).font(.subheadline)
.fontWeight(.semibold)
.foregroundColor(.secondary)
.lineLimit(1)
.padding(EdgeInsets(top: 1, leading: 4, bottom: 1, trailing: 4))
.overlay(RoundedRectangle(cornerRadius: 2)
.stroke(Color.secondary, lineWidth: 1))
}
}
}.offset(y: -32)
}.padding(.horizontal, 16)
.offset(y: 22)
}
func recalcTracks() {
tracks = Array(repeating: .init(.flexible()), count: Int(UIScreen.main.bounds.size.width) / 125)
}
var innerBody: some View {
ScrollView {
LazyVStack(alignment: .leading, spacing: 0) {
if let firstTagline = viewModel.item.taglines?.first {
Text(firstTagline).font(.body).italic()
.fixedSize(horizontal: false, vertical: true)
.padding(.bottom, 8)
.padding(.horizontal, 16)
}
if let genreItems = viewModel.item.genreItems,
!genreItems.isEmpty {
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(spacing: 8) {
Text("Genres:").font(.callout).fontWeight(.semibold)
ForEach(genreItems, id: \.id) { genre in
NavigationLink(destination: LazyView {
LibraryView(viewModel: .init(genre: genre), title: genre.name ?? "")
}) {
Text(genre.name ?? "").font(.footnote)
}
}
}
.padding(.horizontal, 16)
}
.padding(.bottom, 8)
}
Text(viewModel.item.overview ?? "")
.font(.footnote)
.fixedSize(horizontal: false, vertical: true)
.padding(.bottom, 16)
.padding(.horizontal, 16)
Text("Seasons")
.font(.callout).fontWeight(.semibold)
.padding(.horizontal, 16)
}
.padding(.top, 24)
LazyVGrid(columns: tracks) {
ForEach(viewModel.seasons, id: \.id) { season in
PortraitItemView(item: season)
}
}
.padding(.bottom, 16)
.padding(.horizontal, 8)
LazyVStack(alignment: .leading, spacing: 0) {
if let people = viewModel.item.people,
!people.isEmpty {
Text("CAST")
.font(.callout).fontWeight(.semibold)
.padding(.bottom, 8)
.padding(.horizontal, 16)
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(spacing: 16) {
ForEach(people, id: \.self) { person in
if person.type == "Actor" {
NavigationLink(destination: LazyView {
LibraryView(viewModel: .init(person: person), title: person.name ?? "")
}) {
VStack {
ImageView(src: person
.getImage(baseURL: ServerEnvironment.current.server.baseURI!, maxWidth: 100),
bh: person.getBlurHash())
.frame(width: 100, height: 100)
.cornerRadius(10)
Text(person.name ?? "").font(.footnote).fontWeight(.regular).lineLimit(1)
.frame(width: 100).foregroundColor(Color.primary)
if let role = person.role,
!role.isEmpty {
Text(role).font(.caption).fontWeight(.medium).lineLimit(1)
.foregroundColor(Color.secondary).frame(width: 100)
}
}
}
}
}
}
.padding(.horizontal, 16)
}
.padding(.bottom, 16)
}
if let studios = viewModel.item.studios,
!studios.isEmpty {
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(spacing: 16) {
Text("Studios:").font(.callout).fontWeight(.semibold)
ForEach(studios, id: \.id) { studio in
NavigationLink(destination: LazyView {
LibraryView(viewModel: .init(studio: studio), title: studio.name ?? "")
}) {
Text(studio.name ?? "").font(.footnote)
}
}
}
.padding(.horizontal, 16)
}
.padding(.bottom, 16)
}
if !viewModel.similarItems.isEmpty {
Text("More Like This")
.font(.callout).fontWeight(.semibold)
.padding(.bottom, 8)
.padding(.horizontal, 16)
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(spacing: 16) {
ForEach(viewModel.similarItems, id: \.self) { similarItem in
NavigationLink(destination: LazyView { ItemView(item: similarItem) }) {
PortraitItemView(item: similarItem)
}
}
}
.padding(.horizontal, 16)
}
.padding(.bottom, 16)
}
}
}
}
var landscapeView: some View {
GeometryReader { geometry in
ZStack {
ImageView(src: viewModel.item.getBackdropImage(maxWidth: 200),
bh: viewModel.item.getBackdropImageBlurHash())
.opacity(0.4)
.frame(width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing,
height: geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom)
.edgesIgnoringSafeArea(.all)
.blur(radius: 4)
HStack(alignment: .top, spacing: 16) {
VStack(alignment: .leading) {
ImageView(src: viewModel.item.getPrimaryImage(maxWidth: 120),
bh: viewModel.item.getPrimaryImageBlurHash())
.frame(width: 120, height: 180)
.cornerRadius(10)
HStack {
Text(String(viewModel.item.productionYear ?? 0)).font(.subheadline)
.fontWeight(.medium)
.foregroundColor(.secondary)
.lineLimit(1)
if let officialRating = viewModel.item.officialRating {
Text(officialRating).font(.subheadline)
.fontWeight(.semibold)
.foregroundColor(.secondary)
.lineLimit(1)
.padding(EdgeInsets(top: 1, leading: 4, bottom: 1, trailing: 4))
.overlay(RoundedRectangle(cornerRadius: 2)
.stroke(Color.secondary, lineWidth: 1))
}
}
}
.padding(.top, 16)
innerBody
}
}
}
}
var body: some View {
if viewModel.isLoading {
ProgressView()
} else {
ScrollView(.vertical) {
Spacer().frame(height: 16)
LazyVGrid(columns: tracks) {
ForEach(viewModel.seasons, id: \.id) { season in
PortraitItemView(item: season)
Group {
if hSizeClass == .compact && vSizeClass == .regular {
ParallaxHeaderScrollView(header: portraitHeaderView,
staticOverlayView: portraitHeaderOverlayView,
overlayAlignment: .bottomLeading,
headerHeight: UIScreen.main.bounds.width * 0.5625) {
innerBody
}
Spacer().frame(height: 2)
}.onRotate { _ in
recalcTracks()
} else {
landscapeView
}
}
.onRotate {
orientation = $0
recalcTracks()
}
.overrideViewPreference(.unspecified)
.navigationTitle(viewModel.item.name ?? "")
.navigationBarTitleDisplayMode(.inline)

View File

@ -77,8 +77,8 @@ struct SettingsView: View {
Text("Signed in as \(username)").foregroundColor(.primary)
Spacer()
Button {
print("logging out");
close = false;
print("logging out")
close = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
let nc = NotificationCenter.default
nc.post(name: Notification.Name("didSignOut"), object: nil)
@ -88,9 +88,9 @@ struct SettingsView: View {
}
}
Button {
close = false;
close = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
SessionManager.current.logout();
SessionManager.current.logout()
let nc = NotificationCenter.default
nc.post(name: Notification.Name("didSignOut"), object: nil)
}

View File

@ -653,8 +653,8 @@ class PlayerViewController: UIViewController, GCKDiscoveryManagerListener, GCKRe
if fetchCaptions {
mediaPlayer.pause()
subtitleTrackArray.forEach { sub in
//stupid fxcking jeff decides to re-encode these when added.
//only add playback streams when codec not supported by VLC.
// stupid fxcking jeff decides to re-encode these when added.
// only add playback streams when codec not supported by VLC.
if sub.id != -1 && sub.delivery == .external && sub.codec != "subrip" {
mediaPlayer.addPlaybackSlave(sub.url!, type: .subtitle, enforce: false)
}
@ -1014,7 +1014,7 @@ struct VLCPlayerWithControls: UIViewControllerRepresentable {
func showLoadingView(_ viewController: PlayerViewController) {
self.loadBinding.wrappedValue = true
}
func exitPlayer(_ viewController: PlayerViewController) {
self.pBinding.wrappedValue = false
}
@ -1041,11 +1041,11 @@ struct VLCPlayerWithControls: UIViewControllerRepresentable {
extension PlayerViewController {
func sendProgressReport(eventName: String) {
if (eventName == "timeupdate" && mediaPlayer.state == .playing) || eventName != "timeupdate" {
var ticks: Int64 = Int64(mediaPlayer.position * Float(manifest.runTimeTicks!));
if(ticks == 0) {
var ticks: Int64 = Int64(mediaPlayer.position * Float(manifest.runTimeTicks!))
if ticks == 0 {
ticks = manifest.userData?.playbackPositionTicks ?? 0
}
let progressInfo = PlaybackProgressInfo(canSeek: true, item: manifest, itemId: manifest.id, sessionId: playSessionId, mediaSourceId: manifest.id, audioStreamIndex: Int(selectedAudioTrack), subtitleStreamIndex: Int(selectedCaptionTrack), isPaused: (mediaPlayer.state == .paused), isMuted: false, positionTicks: ticks, playbackStartTimeTicks: Int64(startTime), volumeLevel: 100, brightness: 100, aspectRatio: nil, playMethod: playbackItem.videoType, liveStreamId: nil, playSessionId: playSessionId, repeatMode: .repeatNone, nowPlayingQueue: [], playlistItemId: "playlistItem0")
PlaystateAPI.reportPlaybackProgress(playbackProgressInfo: progressInfo)

View File

@ -112,8 +112,8 @@ class DeviceProfileBuilder {
subtitleProfiles.append(SubtitleProfile(format: "subrip", method: .embed))
subtitleProfiles.append(SubtitleProfile(format: "sub", method: .embed))
subtitleProfiles.append(SubtitleProfile(format: "pgssub", method: .embed))
//These need to be filtered. Most subrips are embedded. I hate subtitles.
// These need to be filtered. Most subrips are embedded. I hate subtitles.
subtitleProfiles.append(SubtitleProfile(format: "subrip", method: .external))
subtitleProfiles.append(SubtitleProfile(format: "sub", method: .external))
subtitleProfiles.append(SubtitleProfile(format: "ass", method: .external))

View File

@ -26,7 +26,7 @@ struct ImageView: View {
var body: some View {
LazyImage(source: source)
.placeholder {
Image(uiImage: UIImage(blurHash: blurhash, size: CGSize(width: 8, height: 8)) ?? UIImage(blurHash: "001fC^", size: CGSize(width:8,height:8))!)
Image(uiImage: UIImage(blurHash: blurhash, size: CGSize(width: 8, height: 8)) ?? UIImage(blurHash: "001fC^", size: CGSize(width: 8, height: 8))!)
.resizable()
}
.failure {

View File

@ -23,7 +23,7 @@ final class SessionManager {
fileprivate(set) var user: SignedInUser!
fileprivate(set) var deviceID: String = ""
fileprivate(set) var accessToken: String = ""
#if os(tvOS)
let tvUserManager = TVUserManager()
#endif
@ -41,10 +41,10 @@ final class SessionManager {
}
}
#else
if(lastUsedUserID != nil) {
if lastUsedUserID != nil {
savedUsers?.forEach { savedUser in
if(savedUser.user_id ?? "" == lastUsedUserID!) {
user = savedUser;
if savedUser.user_id ?? "" == lastUsedUserID! {
user = savedUser
}
}
} else {
@ -146,7 +146,7 @@ final class SessionManager {
#if os(tvOS)
let descriptor: TVAppProfileDescriptor = TVAppProfileDescriptor(name: user.username!)
self.tvUserManager.shouldStorePreferenceForCurrentUser(to: descriptor) { should in
if(should) {
if should {
user.appletv_id = self.tvUserManager.currentUserIdentifier ?? ""
}
}
@ -178,7 +178,7 @@ final class SessionManager {
keychain.accessGroup = "9R8RREG67J.me.vigue.jellyfin.sharedKeychain"
keychain.delete("AccessToken_\(user?.user_id ?? "")")
generateAuthHeader(with: nil, deviceID: nil)
if(user != nil) {
if user != nil {
let deleteRequest = NSBatchDeleteRequest(objectIDs: [user.objectID])
user = nil
_ = try? PersistenceController.shared.container.viewContext.execute(deleteRequest)

View File

@ -54,8 +54,8 @@ final class LibraryFilterViewModel: ViewModel {
init(filters: LibraryFilters? = nil,
enabledFilterType: [FilterType] = [.tag, .genre, .sortBy, .sortOrder, .filter], parentId: String) {
self.enabledFilterType = enabledFilterType
self.selectedSortBy = filters?.sortBy.first ?? .name;
self.selectedSortOrder = filters?.sortOrder.first ?? .descending;
self.selectedSortBy = filters?.sortBy.first ?? .name
self.selectedSortOrder = filters?.sortOrder.first ?? .descending
self.parentId = parentId
super.init()