mirror of
https://github.com/cranci1/Sora.git
synced 2026-03-11 17:45:37 +00:00
Revert "yeah i dont even know what is this"
This reverts commit 3478a3f11e.
This commit is contained in:
parent
3478a3f11e
commit
d0a622c2b7
1 changed files with 60 additions and 214 deletions
|
|
@ -18,12 +18,6 @@ struct MediaItem: Identifiable {
|
|||
let airdate: String
|
||||
}
|
||||
|
||||
extension EpisodeLink: Equatable {
|
||||
static func == (lhs: EpisodeLink, rhs: EpisodeLink) -> Bool {
|
||||
return lhs.href == rhs.href && lhs.number == rhs.number
|
||||
}
|
||||
}
|
||||
|
||||
struct MediaInfoView: View {
|
||||
let title: String
|
||||
@State var imageUrl: String
|
||||
|
|
@ -92,24 +86,8 @@ struct MediaInfoView: View {
|
|||
@State private var bulkDownloadProgress: String = ""
|
||||
@State private var tmdbType: TMDBFetcher.MediaType? = nil
|
||||
|
||||
@State private var cachedGroupedEpisodes: [[EpisodeLink]] = []
|
||||
@State private var cachedRanges: [Range<Int>] = []
|
||||
@State private var cachedBannerImage: String = ""
|
||||
@State private var lastEpisodeCount: Int = 0
|
||||
@State private var lastChunkSize: Int = 0
|
||||
|
||||
@State private var viewState: ViewState = .loading
|
||||
|
||||
enum ViewState {
|
||||
case loading
|
||||
case loaded
|
||||
case error
|
||||
}
|
||||
|
||||
@State private var currentTasks: Set<Task<Void, Never>> = []
|
||||
|
||||
private var isGroupedBySeasons: Bool {
|
||||
return cachedGroupedEpisodes.count > 1
|
||||
return groupedEpisodes().count > 1
|
||||
}
|
||||
|
||||
private var isCompactLayout: Bool {
|
||||
|
|
@ -134,14 +112,11 @@ struct MediaInfoView: View {
|
|||
var body: some View {
|
||||
ZStack {
|
||||
Group {
|
||||
switch viewState {
|
||||
case .loading:
|
||||
if isLoading {
|
||||
ProgressView()
|
||||
.padding()
|
||||
case .loaded:
|
||||
} else {
|
||||
mainScrollView
|
||||
case .error:
|
||||
errorView
|
||||
}
|
||||
}
|
||||
.navigationBarHidden(true)
|
||||
|
|
@ -149,7 +124,6 @@ struct MediaInfoView: View {
|
|||
.onAppear {
|
||||
buttonRefreshTrigger.toggle()
|
||||
tabBarController.hideTabBar()
|
||||
updateCachedBannerImage()
|
||||
}
|
||||
.onChange(of: selectedRange) { newValue in
|
||||
UserDefaults.standard.set(newValue.lowerBound, forKey: selectedRangeKey)
|
||||
|
|
@ -157,15 +131,8 @@ struct MediaInfoView: View {
|
|||
.onChange(of: selectedSeason) { newValue in
|
||||
UserDefaults.standard.set(newValue, forKey: selectedSeasonKey)
|
||||
}
|
||||
.onChange(of: episodeLinks.count) { _ in
|
||||
updateCachedComputations()
|
||||
}
|
||||
.onChange(of: episodeChunkSize) { _ in
|
||||
updateCachedComputations()
|
||||
}
|
||||
.onDisappear(){
|
||||
tabBarController.showTabBar()
|
||||
cancelAllTasks()
|
||||
}
|
||||
.task {
|
||||
guard !hasFetched else { return }
|
||||
|
|
@ -177,19 +144,13 @@ struct MediaInfoView: View {
|
|||
}
|
||||
|
||||
DropManager.shared.showDrop(title: "Fetching Data", subtitle: "Please wait while fetching.", duration: 0.5, icon: UIImage(systemName: "arrow.triangle.2.circlepath"))
|
||||
|
||||
let fetchTask = Task {
|
||||
await fetchDetailsAsync()
|
||||
|
||||
if !Task.isCancelled {
|
||||
if savedCustomID != 0 {
|
||||
itemID = savedCustomID
|
||||
} else {
|
||||
await fetchMetadataIDIfNeededAsync()
|
||||
}
|
||||
}
|
||||
fetchDetails()
|
||||
|
||||
if savedCustomID != 0 {
|
||||
itemID = savedCustomID
|
||||
} else {
|
||||
fetchMetadataIDIfNeeded()
|
||||
}
|
||||
currentTasks.insert(fetchTask)
|
||||
|
||||
hasFetched = true
|
||||
AnalyticsManager.shared.sendEvent(
|
||||
|
|
@ -214,7 +175,6 @@ struct MediaInfoView: View {
|
|||
activeFetchID = nil
|
||||
isFetchingEpisode = false
|
||||
showStreamLoadingView = false
|
||||
cancelAllTasks()
|
||||
}
|
||||
|
||||
VStack {
|
||||
|
|
@ -240,29 +200,6 @@ struct MediaInfoView: View {
|
|||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var errorView: some View {
|
||||
VStack(spacing: 16) {
|
||||
Image(systemName: "exclamationmark.triangle")
|
||||
.font(.system(size: 48))
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
Text("Failed to Load")
|
||||
.font(.title2)
|
||||
.fontWeight(.semibold)
|
||||
|
||||
Button("Retry") {
|
||||
viewState = .loading
|
||||
let retryTask = Task {
|
||||
await fetchDetailsAsync()
|
||||
}
|
||||
currentTasks.insert(retryTask)
|
||||
}
|
||||
.buttonStyle(.borderedProminent)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var mainScrollView: some View {
|
||||
ScrollView {
|
||||
|
|
@ -282,7 +219,6 @@ struct MediaInfoView: View {
|
|||
.clipped()
|
||||
}
|
||||
}
|
||||
.id("hero-image-\(imageUrl)")
|
||||
|
||||
VStack(spacing: 0) {
|
||||
Rectangle()
|
||||
|
|
@ -701,7 +637,7 @@ struct MediaInfoView: View {
|
|||
.foregroundColor(.accentColor)
|
||||
}
|
||||
} else if isGroupedBySeasons {
|
||||
let seasons = cachedGroupedEpisodes
|
||||
let seasons = groupedEpisodes()
|
||||
if seasons.count > 1 {
|
||||
Menu {
|
||||
ForEach(0..<seasons.count, id: \.self) { index in
|
||||
|
|
@ -739,6 +675,8 @@ struct MediaInfoView: View {
|
|||
let totalTime = UserDefaults.standard.double(forKey: "totalTime_\(ep.href)")
|
||||
let progress = totalTime > 0 ? lastPlayedTime / totalTime : 0
|
||||
|
||||
let defaultBannerImageValue = getBannerImageBasedOnAppearance()
|
||||
|
||||
EpisodeCell(
|
||||
episodeIndex: i,
|
||||
episode: ep.href,
|
||||
|
|
@ -746,7 +684,7 @@ struct MediaInfoView: View {
|
|||
progress: progress,
|
||||
itemID: itemID ?? 0,
|
||||
totalEpisodes: episodeLinks.count,
|
||||
defaultBannerImage: cachedBannerImage,
|
||||
defaultBannerImage: defaultBannerImageValue,
|
||||
module: module,
|
||||
parentTitle: title,
|
||||
showPosterURL: imageUrl,
|
||||
|
|
@ -768,21 +706,23 @@ struct MediaInfoView: View {
|
|||
tmdbID: tmdbID,
|
||||
seasonNumber: 1
|
||||
)
|
||||
.id("episode-\(ep.href)-\(isMultiSelectMode)")
|
||||
.disabled(isFetchingEpisode)
|
||||
.disabled(isFetchingEpisode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var seasonsEpisodeList: some View {
|
||||
if !cachedGroupedEpisodes.isEmpty, selectedSeason < cachedGroupedEpisodes.count {
|
||||
let seasons = groupedEpisodes()
|
||||
if !seasons.isEmpty, selectedSeason < seasons.count {
|
||||
LazyVStack(spacing: 15) {
|
||||
ForEach(cachedGroupedEpisodes[selectedSeason], id: \.href) { ep in
|
||||
ForEach(seasons[selectedSeason]) { ep in
|
||||
let lastPlayedTime = UserDefaults.standard.double(forKey: "lastPlayedTime_\(ep.href)")
|
||||
let totalTime = UserDefaults.standard.double(forKey: "totalTime_\(ep.href)")
|
||||
let progress = totalTime > 0 ? lastPlayedTime / totalTime : 0
|
||||
|
||||
let defaultBannerImageValue = getBannerImageBasedOnAppearance()
|
||||
|
||||
EpisodeCell(
|
||||
episodeIndex: selectedSeason,
|
||||
episode: ep.href,
|
||||
|
|
@ -790,7 +730,7 @@ struct MediaInfoView: View {
|
|||
progress: progress,
|
||||
itemID: itemID ?? 0,
|
||||
totalEpisodes: episodeLinks.count,
|
||||
defaultBannerImage: cachedBannerImage,
|
||||
defaultBannerImage: defaultBannerImageValue,
|
||||
module: module,
|
||||
parentTitle: title,
|
||||
showPosterURL: imageUrl,
|
||||
|
|
@ -812,8 +752,7 @@ struct MediaInfoView: View {
|
|||
tmdbID: tmdbID,
|
||||
seasonNumber: selectedSeason + 1
|
||||
)
|
||||
.id("season-episode-\(ep.href)-\(isMultiSelectMode)")
|
||||
.disabled(isFetchingEpisode)
|
||||
.disabled(isFetchingEpisode)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -821,132 +760,6 @@ struct MediaInfoView: View {
|
|||
}
|
||||
}
|
||||
|
||||
private func updateCachedComputations() {
|
||||
let currentEpisodeCount = episodeLinks.count
|
||||
let currentChunkSize = episodeChunkSize
|
||||
|
||||
guard currentEpisodeCount != lastEpisodeCount || currentChunkSize != lastChunkSize else {
|
||||
return
|
||||
}
|
||||
|
||||
lastEpisodeCount = currentEpisodeCount
|
||||
lastChunkSize = currentChunkSize
|
||||
|
||||
cachedGroupedEpisodes = groupedEpisodes()
|
||||
cachedRanges = generateRanges()
|
||||
updateCachedBannerImage()
|
||||
}
|
||||
|
||||
private func updateCachedBannerImage() {
|
||||
cachedBannerImage = getBannerImageBasedOnAppearance()
|
||||
}
|
||||
|
||||
private func fetchDetailsAsync() async {
|
||||
await MainActor.run {
|
||||
viewState = .loading
|
||||
}
|
||||
|
||||
do {
|
||||
try await Task.sleep(nanoseconds: 500_000_000)
|
||||
|
||||
guard !Task.isCancelled else { return }
|
||||
|
||||
let jsContent = try moduleManager.getModuleContent(module)
|
||||
|
||||
await MainActor.run {
|
||||
jsController.loadScript(jsContent)
|
||||
}
|
||||
|
||||
await withCheckedContinuation { continuation in
|
||||
if module.metadata.asyncJS == true {
|
||||
jsController.fetchDetailsJS(url: href) { items, episodes in
|
||||
if let item = items.first {
|
||||
self.synopsis = item.description
|
||||
self.aliases = item.aliases
|
||||
self.airdate = item.airdate
|
||||
}
|
||||
self.episodeLinks = episodes
|
||||
self.updateCachedComputations()
|
||||
self.restoreSelectionState()
|
||||
self.viewState = .loaded
|
||||
self.isRefetching = false
|
||||
continuation.resume()
|
||||
}
|
||||
} else {
|
||||
jsController.fetchDetails(url: href) { items, episodes in
|
||||
if let item = items.first {
|
||||
self.synopsis = item.description
|
||||
self.aliases = item.aliases
|
||||
self.airdate = item.airdate
|
||||
}
|
||||
self.episodeLinks = episodes
|
||||
self.updateCachedComputations()
|
||||
self.restoreSelectionState()
|
||||
self.viewState = .loaded
|
||||
self.isRefetching = false
|
||||
continuation.resume()
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
Logger.shared.log("Error loading module: \(error)", type: "Error")
|
||||
viewState = .error
|
||||
isRefetching = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchMetadataIDIfNeededAsync() async {
|
||||
let provider = UserDefaults.standard.string(forKey: "metadataProviders") ?? "TMDB"
|
||||
let cleaned = cleanTitle(title)
|
||||
|
||||
if provider == "TMDB" {
|
||||
await MainActor.run { tmdbID = nil }
|
||||
await withCheckedContinuation { continuation in
|
||||
tmdbFetcher.fetchBestMatchID(for: cleaned) { id, type in
|
||||
Task { @MainActor in
|
||||
guard !Task.isCancelled else {
|
||||
continuation.resume()
|
||||
return
|
||||
}
|
||||
self.tmdbID = id
|
||||
self.tmdbType = type
|
||||
Logger.shared.log("Fetched TMDB ID: \(id ?? -1) (\(type?.rawValue ?? "unknown")) for title: \(cleaned)", type: "Debug")
|
||||
continuation.resume()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if provider == "Anilist" {
|
||||
await MainActor.run { itemID = nil }
|
||||
await withCheckedContinuation { continuation in
|
||||
fetchItemID(byTitle: cleaned) { result in
|
||||
Task { @MainActor in
|
||||
guard !Task.isCancelled else {
|
||||
continuation.resume()
|
||||
return
|
||||
}
|
||||
switch result {
|
||||
case .success(let id):
|
||||
self.itemID = id
|
||||
Logger.shared.log("Fetched AniList ID: \(id) for title: \(cleaned)", type: "Debug")
|
||||
case .failure(let error):
|
||||
Logger.shared.log("Failed to fetch AniList ID: \(error)", type: "Error")
|
||||
}
|
||||
continuation.resume()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func cancelAllTasks() {
|
||||
for task in currentTasks {
|
||||
task.cancel()
|
||||
}
|
||||
currentTasks.removeAll()
|
||||
}
|
||||
|
||||
private func restoreSelectionState() {
|
||||
if let savedStart = UserDefaults.standard.object(forKey: selectedRangeKey) as? Int,
|
||||
let savedRange = generateRanges().first(where: { $0.lowerBound == savedStart }) {
|
||||
|
|
@ -956,7 +769,7 @@ struct MediaInfoView: View {
|
|||
}
|
||||
|
||||
if let savedSeason = UserDefaults.standard.object(forKey: selectedSeasonKey) as? Int {
|
||||
let maxIndex = max(0, cachedGroupedEpisodes.count - 1)
|
||||
let maxIndex = max(0, groupedEpisodes().count - 1)
|
||||
selectedSeason = min(savedSeason, maxIndex)
|
||||
}
|
||||
}
|
||||
|
|
@ -1044,7 +857,7 @@ struct MediaInfoView: View {
|
|||
var updates = [String: Double]()
|
||||
|
||||
if inSeason {
|
||||
let seasons = cachedGroupedEpisodes
|
||||
let seasons = groupedEpisodes()
|
||||
for ep2 in seasons[selectedSeason] where ep2.number < ep.number {
|
||||
let href = ep2.href
|
||||
updates["lastPlayedTime_\(href)"] = 99999999.0
|
||||
|
|
@ -1229,10 +1042,43 @@ struct MediaInfoView: View {
|
|||
}
|
||||
|
||||
func fetchDetails() {
|
||||
let fetchTask = Task {
|
||||
await fetchDetailsAsync()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
Task {
|
||||
do {
|
||||
let jsContent = try moduleManager.getModuleContent(module)
|
||||
jsController.loadScript(jsContent)
|
||||
if module.metadata.asyncJS == true {
|
||||
jsController.fetchDetailsJS(url: href) { items, episodes in
|
||||
if let item = items.first {
|
||||
self.synopsis = item.description
|
||||
self.aliases = item.aliases
|
||||
self.airdate = item.airdate
|
||||
}
|
||||
self.episodeLinks = episodes
|
||||
self.restoreSelectionState()
|
||||
self.isLoading = false
|
||||
self.isRefetching = false
|
||||
}
|
||||
} else {
|
||||
jsController.fetchDetails(url: href) { items, episodes in
|
||||
if let item = items.first {
|
||||
self.synopsis = item.description
|
||||
self.aliases = item.aliases
|
||||
self.airdate = item.airdate
|
||||
}
|
||||
self.episodeLinks = episodes
|
||||
self.restoreSelectionState()
|
||||
self.isLoading = false
|
||||
self.isRefetching = false
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
Logger.shared.log("Error loading module: \(error)", type: "Error")
|
||||
self.isLoading = false
|
||||
self.isRefetching = false
|
||||
}
|
||||
}
|
||||
}
|
||||
currentTasks.insert(fetchTask)
|
||||
}
|
||||
|
||||
func fetchStream(href: String) {
|
||||
|
|
@ -1625,7 +1471,7 @@ struct MediaInfoView: View {
|
|||
|
||||
private func selectAllVisibleEpisodes() {
|
||||
if isGroupedBySeasons {
|
||||
let seasons = cachedGroupedEpisodes
|
||||
let seasons = groupedEpisodes()
|
||||
if !seasons.isEmpty, selectedSeason < seasons.count {
|
||||
for episode in seasons[selectedSeason] {
|
||||
selectedEpisodes.insert(episode.number)
|
||||
|
|
|
|||
Loading…
Reference in a new issue