diff --git a/Model/Applications/VideosAPI.swift b/Model/Applications/VideosAPI.swift index ec20479f..f093c346 100644 --- a/Model/Applications/VideosAPI.swift +++ b/Model/Applications/VideosAPI.swift @@ -145,10 +145,10 @@ extension VideosAPI { let startRange = line.range(withName: "start") guard let titleSubstringRange = Range(titleRange, in: description), - let startSubstringRange = Range(startRange, in: description), - let titleCapture = String(description[titleSubstringRange]), - let startCapture = String(description[startSubstringRange]) else { return nil } + let startSubstringRange = Range(startRange, in: description) else { return nil } + let titleCapture = String(description[titleSubstringRange]) + let startCapture = String(description[startSubstringRange]) let startComponents = startCapture.components(separatedBy: ":") guard startComponents.count <= 3 else { return nil } diff --git a/Model/Player/Backends/AVPlayerBackend.swift b/Model/Player/Backends/AVPlayerBackend.swift index 4e45818b..7a218d6b 100644 --- a/Model/Player/Backends/AVPlayerBackend.swift +++ b/Model/Player/Backends/AVPlayerBackend.swift @@ -79,7 +79,7 @@ final class AVPlayerBackend: PlayerBackend { init(model: PlayerModel, controls: PlayerControlsModel?, playerTime: PlayerTimeModel?) { self.model = model self.controls = controls - self.playerTime = playerTime + self.playerTime = playerTime ?? PlayerTimeModel.shared addFrequentTimeObserver() addInfrequentTimeObserver() @@ -582,6 +582,7 @@ final class AVPlayerBackend: PlayerBackend { } if player.timeControlStatus == .playing { + self.model.objectWillChange.send() if player.rate != self.model.currentRate { player.rate = self.model.currentRate } diff --git a/Model/Player/Backends/MPVBackend.swift b/Model/Player/Backends/MPVBackend.swift index c89daa1c..73bf76ce 100644 --- a/Model/Player/Backends/MPVBackend.swift +++ b/Model/Player/Backends/MPVBackend.swift @@ -129,7 +129,7 @@ final class MPVBackend: PlayerBackend { ) { self.model = model self.controls = controls - self.playerTime = playerTime + self.playerTime = playerTime ?? PlayerTimeModel.shared self.networkState = networkState clientTimer = .init(interval: .seconds(Self.timeUpdateInterval), mode: .infinite) { [weak self] _ in diff --git a/Model/Player/Backends/MPVClient.swift b/Model/Player/Backends/MPVClient.swift index b26d7a6f..da260a47 100644 --- a/Model/Player/Backends/MPVClient.swift +++ b/Model/Player/Backends/MPVClient.swift @@ -272,7 +272,11 @@ final class MPVClient: ObservableObject { UIView.animate(withDuration: 0.2, animations: { let aspectRatio = self.aspectRatio > 0 && self.aspectRatio < VideoPlayerView.defaultAspectRatio ? self.aspectRatio : VideoPlayerView.defaultAspectRatio let height = [self.backend.model.playerSize.height, self.backend.model.playerSize.width / aspectRatio].min()! - let offsetY = self.backend.model.playingFullScreen ? ((self.backend.model.playerSize.height / 2.0) - (height / 2)) : 0 + var insets = 0.0 + #if os(iOS) + insets = OrientationTracker.shared.currentInterfaceOrientation.isPortrait ? SafeArea.insets.bottom : 0 + #endif + let offsetY = self.backend.model.playingFullScreen ? ((self.backend.model.playerSize.height / 2.0) - ((height + insets) / 2)) : 0 self.glView?.frame = CGRect(x: 0, y: offsetY, width: roundedWidth, height: height) }) { completion in if completion { diff --git a/Model/Player/Backends/PlayerBackend.swift b/Model/Player/Backends/PlayerBackend.swift index e15d3838..e04eba2f 100644 --- a/Model/Player/Backends/PlayerBackend.swift +++ b/Model/Player/Backends/PlayerBackend.swift @@ -130,8 +130,8 @@ extension PlayerBackend { return } #endif - self.playerTime.currentTime = self.currentTime ?? .zero - self.playerTime.duration = self.playerItemDuration ?? .zero + PlayerTimeModel.shared.currentTime = self.currentTime ?? .zero + PlayerTimeModel.shared.duration = self.playerItemDuration ?? .zero completionHandler?() } } diff --git a/Model/Player/PlayerModel.swift b/Model/Player/PlayerModel.swift index 7e4bbab1..3383cbc0 100644 --- a/Model/Player/PlayerModel.swift +++ b/Model/Player/PlayerModel.swift @@ -53,7 +53,7 @@ final class PlayerModel: ObservableObject { var mpvPlayerView = MPVPlayerView() - @Published var presentingPlayer = false + @Published var presentingPlayer = false { didSet { handlePresentationChange() } } @Published var activeBackend = PlayerBackendType.mpv var avPlayerBackend: AVPlayerBackend! @@ -324,11 +324,7 @@ final class PlayerModel: ObservableObject { } var playerItemDurationWithoutSponsorSegments: CMTime? { - guard let playerItemDuration = playerItemDuration, !playerItemDuration.seconds.isZero else { - return nil - } - - return playerItemDuration - .secondsInDefaultTimescale( + PlayerTimeModel.shared.duration - .secondsInDefaultTimescale( sponsorBlock.segments.reduce(0) { $0 + $1.duration } ) } @@ -496,18 +492,7 @@ final class PlayerModel: ObservableObject { } private func handlePresentationChange() { - var delay = 0.0 - - #if os(iOS) - if presentingPlayer { - delay = 0.2 - } - #endif - - DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in - guard let self = self else { return } - self.backend.setNeedsDrawing(self.presentingPlayer) - } + backend.setNeedsDrawing(presentingPlayer) controls.hide() diff --git a/Model/Player/PlayerTimeModel.swift b/Model/Player/PlayerTimeModel.swift index 6797fb85..62733239 100644 --- a/Model/Player/PlayerTimeModel.swift +++ b/Model/Player/PlayerTimeModel.swift @@ -3,6 +3,7 @@ import Foundation import SwiftUI final class PlayerTimeModel: ObservableObject { + static let shared = PlayerTimeModel() static let timePlaceholder = "--:--" @Published var currentTime = CMTime.zero diff --git a/Model/Search/SearchModel.swift b/Model/Search/SearchModel.swift index 8050db7a..05958d38 100644 --- a/Model/Search/SearchModel.swift +++ b/Model/Search/SearchModel.swift @@ -24,17 +24,12 @@ final class SearchModel: ObservableObject { func changeQuery(_ changeHandler: @escaping (SearchQuery) -> Void = { _ in }) { changeHandler(query) - let newResource = accounts.api.search(query, page: nil) - guard newResource != resource else { - return - } - page = nil - resource = newResource - resource.addObserver(store) - if !query.isEmpty { + resource = accounts.api.search(query, page: nil) + resource.addObserver(store) + loadResource() } } diff --git a/Shared/Defaults.swift b/Shared/Defaults.swift index 180f3b35..eea86c1a 100644 --- a/Shared/Defaults.swift +++ b/Shared/Defaults.swift @@ -278,6 +278,14 @@ enum VisibleSection: String, CaseIterable, Comparable, Defaults.Serializable { enum WatchedVideoStyle: String, Defaults.Serializable { case nothing, badge, decreasedOpacity, both + + var isShowingBadge: Bool { + self == .badge || self == .both + } + + var isDecreasingOpacity: Bool { + self == .decreasedOpacity || self == .both + } } enum WatchedVideoBadgeColor: String, Defaults.Serializable { diff --git a/Shared/Navigation/ContentView.swift b/Shared/Navigation/ContentView.swift index 3d1e39e8..427ed7aa 100644 --- a/Shared/Navigation/ContentView.swift +++ b/Shared/Navigation/ContentView.swift @@ -12,10 +12,8 @@ struct ContentView: View { @EnvironmentObject private var comments @EnvironmentObject private var instances @EnvironmentObject private var navigation - @EnvironmentObject private var networkState @EnvironmentObject private var player @EnvironmentObject private var playerControls - @EnvironmentObject private var playerTime @EnvironmentObject private var playlists @EnvironmentObject private var recents @EnvironmentObject private var search @@ -60,9 +58,7 @@ struct ContentView: View { .environmentObject(comments) .environmentObject(instances) .environmentObject(navigation) - .environmentObject(networkState) .environmentObject(player) - .environmentObject(playerTime) .environmentObject(playlists) .environmentObject(recents) .environmentObject(search) diff --git a/Shared/Player/ChapterView.swift b/Shared/Player/ChapterView.swift index 3cedeee1..cb7b5921 100644 --- a/Shared/Player/ChapterView.swift +++ b/Shared/Player/ChapterView.swift @@ -31,19 +31,28 @@ struct ChapterView: View { } @ViewBuilder func smallImage(_ chapter: Chapter) -> some View { - WebImage(url: chapter.image) - .resizable() - .placeholder { - ProgressView() + if #available(iOS 15, macOS 12, *) { + AsyncImage(url: chapter.image) { image in + image + .resizable() + } placeholder: { + Rectangle().foregroundColor(Color("PlaceholderColor")) } - .indicator(.activity) - #if os(tvOS) - .frame(width: thumbnailWidth, height: 140) - .mask(RoundedRectangle(cornerRadius: 12)) - #else - .frame(width: thumbnailWidth, height: 60) - .mask(RoundedRectangle(cornerRadius: 6)) - #endif + } else { + WebImage(url: chapter.image) + .resizable() + .placeholder { + ProgressView() + } + .indicator(.activity) +#if os(tvOS) + .frame(width: thumbnailWidth, height: 140) + .mask(RoundedRectangle(cornerRadius: 12)) +#else + .frame(width: thumbnailWidth, height: 60) + .mask(RoundedRectangle(cornerRadius: 6)) +#endif + } } private var thumbnailWidth: Double { diff --git a/Shared/Player/Controls/PlayerControls.swift b/Shared/Player/Controls/PlayerControls.swift index 1e216829..16132343 100644 --- a/Shared/Player/Controls/PlayerControls.swift +++ b/Shared/Player/Controls/PlayerControls.swift @@ -72,7 +72,7 @@ struct PlayerControls: View { } .offset(y: playerControlsLayout.osdVerticalOffset + 5) - if model.presentingControls, !model.presentingOverlays { + Section { #if !os(tvOS) HStack { seekBackwardButton @@ -160,7 +160,8 @@ struct PlayerControls: View { .offset(y: -playerControlsLayout.timelineHeight - 5) #endif } - } + }.opacity(model.presentingControls && !model.presentingOverlays ? 1 : 0) + } } .frame(maxWidth: .infinity) @@ -219,14 +220,23 @@ struct PlayerControls: View { let video = item.video, let url = thumbnails.best(video) { - WebImage(url: url) - .resizable() - .placeholder { - Rectangle().fill(Color("PlaceholderColor")) + if #available(iOS 15, macOS 12, *) { + AsyncImage(url: url) { image in + image + .resizable() + } placeholder: { + Rectangle().foregroundColor(Color("PlaceholderColor")) } - .retryOnAppear(true) - .indicator(.activity) - .frame(maxWidth: .infinity, maxHeight: .infinity) + } else { + WebImage(url: url) + .resizable() + .placeholder { + Rectangle().fill(Color("PlaceholderColor")) + } + .retryOnAppear(true) + .indicator(.activity) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } } } diff --git a/Shared/Player/Controls/PlayerControlsLayout.swift b/Shared/Player/Controls/PlayerControlsLayout.swift index 0d8cc079..04acddac 100644 --- a/Shared/Player/Controls/PlayerControlsLayout.swift +++ b/Shared/Player/Controls/PlayerControlsLayout.swift @@ -60,7 +60,7 @@ enum PlayerControlsLayout: String, CaseIterable, Defaults.Serializable { case .veryLarge: return 40 case .large: - return 30 + return 25 case .medium: return 25 case .small: diff --git a/Shared/Player/Controls/TimelineView.swift b/Shared/Player/Controls/TimelineView.swift index 0f8fc1bc..ea4b2245 100644 --- a/Shared/Player/Controls/TimelineView.swift +++ b/Shared/Player/Controls/TimelineView.swift @@ -44,9 +44,10 @@ struct TimelineView: View { @Environment(\.verticalSizeClass) private var verticalSizeClass #endif + @ObservedObject private var playerTime = PlayerTimeModel.shared + @EnvironmentObject private var player @EnvironmentObject private var controls - @EnvironmentObject private var playerTime @Default(.playerControlsLayout) private var regularPlayerControlsLayout @Default(.fullScreenPlayerControlsLayout) private var fullScreenPlayerControlsLayout @@ -392,7 +393,7 @@ struct TimelineView_Previews: PreviewProvider { static var previews: some View { let playerModel = PlayerModel() playerModel.currentItem = .init(Video.fixture) - let playerTimeModel = PlayerTimeModel() + let playerTimeModel = PlayerTimeModel.shared playerTimeModel.player = playerModel playerTimeModel.currentTime = .secondsInDefaultTimescale(33) playerTimeModel.duration = .secondsInDefaultTimescale(100) @@ -400,7 +401,6 @@ struct TimelineView_Previews: PreviewProvider { TimelineView() } .environmentObject(playerModel) - .environmentObject(playerTimeModel) .environmentObject(PlayerControlsModel()) .padding() } diff --git a/Shared/Player/PlayerOrientation.swift b/Shared/Player/PlayerOrientation.swift index 8bc61034..37aedb41 100644 --- a/Shared/Player/PlayerOrientation.swift +++ b/Shared/Player/PlayerOrientation.swift @@ -50,14 +50,20 @@ extension VideoPlayerView { return } - if orientation.isLandscape { - playerControls.presentingControls = false - player.enterFullScreen(showControls: false) - Orientation.lockOrientation(OrientationTracker.shared.currentInterfaceOrientationMask, andRotateTo: orientation) - } else { - player.exitFullScreen(showControls: false) - Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait) + orientationDebouncer.callback = { + DispatchQueue.main.async { + if orientation.isLandscape { + playerControls.presentingControls = false + player.enterFullScreen(showControls: false) + Orientation.lockOrientation(OrientationTracker.shared.currentInterfaceOrientationMask, andRotateTo: orientation) + } else { + player.exitFullScreen(showControls: false) + Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait) + } + } } + + orientationDebouncer.call() } } } diff --git a/Shared/Player/VideoPlayerView.swift b/Shared/Player/VideoPlayerView.swift index 40aa8aea..c40dbd35 100644 --- a/Shared/Player/VideoPlayerView.swift +++ b/Shared/Player/VideoPlayerView.swift @@ -3,6 +3,7 @@ import AVKit import CoreMotion #endif import Defaults +import Repeat import Siesta import SwiftUI @@ -41,6 +42,7 @@ struct VideoPlayerView: View { @State internal var orientation = UIInterfaceOrientation.portrait @State internal var lastOrientation: UIInterfaceOrientation? + @State internal var orientationDebouncer = Debouncer(.milliseconds(300)) #elseif os(macOS) var hoverThrottle = Throttle(interval: 0.5) var mouseLocation: CGPoint { NSEvent.mouseLocation } diff --git a/Shared/Search/SearchView.swift b/Shared/Search/SearchView.swift index d3264afb..65d4b334 100644 --- a/Shared/Search/SearchView.swift +++ b/Shared/Search/SearchView.swift @@ -27,6 +27,7 @@ struct SearchView: View { @EnvironmentObject private var state private var favorites = FavoritesModel.shared + @Default(.recentlyOpened) private var recentlyOpened @Default(.saveRecents) private var saveRecents private var videos = [Video]() @@ -287,11 +288,11 @@ struct SearchView: View { VStack { List { Section(header: Text("Recents")) { - if recentItems.isEmpty { + if recentlyOpened.isEmpty { Text("Search history is empty") .foregroundColor(.secondary) } - ForEach(recentItems) { item in + ForEach(recentlyOpened, id: \.tag) { item in recentItemButton(item) } } @@ -347,7 +348,6 @@ struct SearchView: View { item.type == .channel ? RecentsModel.symbolSystemImage(item.title) : "list.and.film" Label(item.title, systemImage: systemImage) - .lineLimit(1) } .contextMenu { removeButton(item) @@ -391,10 +391,6 @@ struct SearchView: View { searchDate != .any || searchDuration != .any } - private var recentItems: [RecentItem] { - Defaults[.recentlyOpened].reversed() - } - private var searchSortOrderPicker: some View { Picker("Sort", selection: $searchSortOrder) { ForEach(SearchQuery.SortOrder.allCases) { sortOrder in diff --git a/Shared/Videos/VideoBanner.swift b/Shared/Videos/VideoBanner.swift index 6f8eca60..478f0026 100644 --- a/Shared/Videos/VideoBanner.swift +++ b/Shared/Videos/VideoBanner.swift @@ -70,20 +70,37 @@ struct VideoBanner: View { #endif } - private var smallThumbnail: some View { - WebImage(url: video?.thumbnailURL(quality: .medium)) - .resizable() - .placeholder { - ProgressView() + @ViewBuilder private var smallThumbnail: some View { + let url = video?.thumbnailURL(quality: .medium) + if #available(iOS 15, macOS 12, *) { + AsyncImage(url: url) { image in + image + .resizable() + } placeholder: { + Rectangle().foregroundColor(Color("PlaceholderColor")) } - .indicator(.activity) - #if os(tvOS) - .frame(width: thumbnailWidth, height: 140) - .mask(RoundedRectangle(cornerRadius: 12)) - #else - .frame(width: thumbnailWidth, height: 60) - .mask(RoundedRectangle(cornerRadius: 6)) - #endif +#if os(tvOS) + .frame(width: thumbnailWidth, height: 140) + .mask(RoundedRectangle(cornerRadius: 12)) +#else + .frame(width: thumbnailWidth, height: 60) + .mask(RoundedRectangle(cornerRadius: 6)) +#endif + } else { + WebImage(url: url) + .resizable() + .placeholder { + ProgressView() + } + .indicator(.activity) +#if os(tvOS) + .frame(width: thumbnailWidth, height: 140) + .mask(RoundedRectangle(cornerRadius: 12)) +#else + .frame(width: thumbnailWidth, height: 60) + .mask(RoundedRectangle(cornerRadius: 6)) +#endif + } } private var thumbnailWidth: Double { diff --git a/Shared/Videos/VideoCell.swift b/Shared/Videos/VideoCell.swift index 4687f043..95d88b80 100644 --- a/Shared/Videos/VideoCell.swift +++ b/Shared/Videos/VideoCell.swift @@ -382,7 +382,7 @@ struct VideoCell: View { HStack(alignment: .center) { if saveHistory, - watchedVideoStyle == .badge || watchedVideoStyle == .both, + watchedVideoStyle.isShowingBadge, watch?.finished ?? false { Image(systemName: "checkmark.circle.fill") @@ -419,27 +419,32 @@ struct VideoCell: View { private var thumbnailImage: some View { Group { - if let url = thumbnails.best(video) { + let url = thumbnails.best(video) + if #available(iOS 15, macOS 12, *) { + AsyncImage(url: url) { image in + image + .resizable() + } placeholder: { + Rectangle().foregroundColor(Color("PlaceholderColor")) + } +#if os(tvOS) + .frame(minHeight: 320) +#endif + } else { WebImage(url: url) .resizable() .placeholder { - Rectangle().fill(Color("PlaceholderColor")) + Rectangle().foregroundColor(Color("PlaceholderColor")) } .retryOnAppear(true) .onFailure { _ in + guard let url = url else { return } thumbnails.insertUnloadable(url) } - .indicator(.activity) - #if os(tvOS) +#if os(tvOS) .frame(minHeight: 320) - #endif - } else { - ZStack { - Color("PlaceholderColor") - Image(systemName: "exclamationmark.triangle") - } - .font(.system(size: 30)) +#endif } } .mask(RoundedRectangle(cornerRadius: thumbnailRoundingCornerRadius)) diff --git a/Shared/Views/ChannelCell.swift b/Shared/Views/ChannelCell.swift index 371a0e91..e55f4178 100644 --- a/Shared/Views/ChannelCell.swift +++ b/Shared/Views/ChannelCell.swift @@ -37,15 +37,23 @@ struct ChannelCell: View { .opacity(0.6) } .foregroundColor(.secondary) - - WebImage(url: channel.thumbnailURL) - .resizable() - .placeholder { - Rectangle().fill(Color("PlaceholderColor")) + if #available(iOS 15, macOS 12, *) { + AsyncImage(url: channel.thumbnailURL) { image in + image + .resizable() + } placeholder: { + Rectangle().foregroundColor(Color("PlaceholderColor")) } - .indicator(.activity) - .frame(width: 88, height: 88) - .clipShape(Circle()) + } else { + WebImage(url: channel.thumbnailURL) + .resizable() + .placeholder { + Rectangle().fill(Color("PlaceholderColor")) + } + .indicator(.activity) + .frame(width: 88, height: 88) + .clipShape(Circle()) + } DetailBadge(text: channel.name, style: .prominent) diff --git a/Shared/Views/ChannelPlaylistCell.swift b/Shared/Views/ChannelPlaylistCell.swift index 92d7d436..076b9bf6 100644 --- a/Shared/Views/ChannelPlaylistCell.swift +++ b/Shared/Views/ChannelPlaylistCell.swift @@ -37,15 +37,23 @@ struct ChannelPlaylistCell: View { } .foregroundColor(.secondary) - WebImage(url: playlist.thumbnailURL) - .resizable() - .placeholder { - Rectangle().fill(Color("PlaceholderColor")) + if #available(iOS 15, macOS 12, *) { + AsyncImage(url: playlist.thumbnailURL) { image in + image + .resizable() + } placeholder: { + Rectangle().foregroundColor(Color("PlaceholderColor")) } - .indicator(.activity) - .frame(width: 165, height: 88) - .clipShape(RoundedRectangle(cornerRadius: 10)) - + } else { + WebImage(url: playlist.thumbnailURL) + .resizable() + .placeholder { + Rectangle().fill(Color("PlaceholderColor")) + } + .indicator(.activity) + .frame(width: 165, height: 88) + .clipShape(RoundedRectangle(cornerRadius: 10)) + } Group { DetailBadge(text: playlist.title, style: .prominent) .lineLimit(2) diff --git a/Shared/Views/ControlsBar.swift b/Shared/Views/ControlsBar.swift index 40b5147e..d4c94e38 100644 --- a/Shared/Views/ControlsBar.swift +++ b/Shared/Views/ControlsBar.swift @@ -12,7 +12,6 @@ struct ControlsBar: View { @EnvironmentObject private var accounts @EnvironmentObject private var navigation - @EnvironmentObject private var playerControls @EnvironmentObject private var model @EnvironmentObject private var playlists @EnvironmentObject private var recents @@ -63,8 +62,8 @@ struct ControlsBar: View { } } else if detailsToggleFullScreen { Button { - playerControls.presentingControlsOverlay = false - playerControls.presentingControls = false + model.controls.presentingControlsOverlay = false + model.controls.presentingControls = false withAnimation { fullScreen.toggle() } @@ -83,7 +82,7 @@ struct ControlsBar: View { var controls: some View { HStack(spacing: 4) { Group { - if playerControls.isPlaying { + if model.controls.isPlaying { Button(action: { model.pause() }) { @@ -103,7 +102,7 @@ struct ControlsBar: View { } } } - .disabled(playerControls.isLoadingVideo || model.currentItem.isNil) + .disabled(model.controls.isLoadingVideo || model.currentItem.isNil) Button(action: { model.advanceToNextItem() }) { Label("Next", systemImage: "forward.fill") @@ -268,13 +267,22 @@ struct ControlsBar: View { private var authorAvatar: some View { Group { if let video = model.currentItem?.video, let url = video.channel.thumbnailURL { - WebImage(url: url) - .resizable() - .placeholder { - Rectangle().fill(Color("PlaceholderColor")) + if #available(iOS 15, macOS 12, *) { + AsyncImage(url: url) { image in + image + .resizable() + } placeholder: { + Rectangle().foregroundColor(Color("PlaceholderColor")) } - .retryOnAppear(true) - .indicator(.activity) + } else { + WebImage(url: url) + .resizable() + .placeholder { + Rectangle().fill(Color("PlaceholderColor")) + } + .retryOnAppear(true) + .indicator(.activity) + } } else { ZStack { Color(white: 0.6) diff --git a/Shared/YatteeApp.swift b/Shared/YatteeApp.swift index 599d161f..bbe7f6dd 100644 --- a/Shared/YatteeApp.swift +++ b/Shared/YatteeApp.swift @@ -40,7 +40,6 @@ struct YatteeApp: App { @StateObject private var networkState = NetworkStateModel() @StateObject private var player = PlayerModel() @StateObject private var playerControls = PlayerControlsModel() - @StateObject private var playerTime = PlayerTimeModel() @StateObject private var playlists = PlaylistsModel() @StateObject private var recents = RecentsModel() @StateObject private var search = SearchModel() @@ -63,7 +62,6 @@ struct YatteeApp: App { .environmentObject(networkState) .environmentObject(player) .environmentObject(playerControls) - .environmentObject(playerTime) .environmentObject(playlists) .environmentObject(recents) .environmentObject(seek) @@ -137,7 +135,6 @@ struct YatteeApp: App { .environmentObject(networkState) .environmentObject(player) .environmentObject(playerControls) - .environmentObject(playerTime) .environmentObject(playlists) .environmentObject(recents) .environmentObject(search) @@ -205,9 +202,10 @@ struct YatteeApp: App { player.controls = playerControls player.navigation = navigation player.networkState = networkState - player.playerTime = playerTime player.seek = seek + PlayerTimeModel.shared.player = player + if !accounts.current.isNil { player.restoreQueue() }