diff --git a/Sora/Utils/DownloadUtils/DownloadModels.swift b/Sora/Utils/DownloadUtils/DownloadModels.swift index bb10633..1278542 100644 --- a/Sora/Utils/DownloadUtils/DownloadModels.swift +++ b/Sora/Utils/DownloadUtils/DownloadModels.swift @@ -357,18 +357,29 @@ struct ActiveDownload: Identifiable, Equatable { // Add the same grouping properties as DownloadedAsset for consistency var groupTitle: String { - if type == .episode, let showTitle = metadata?.showTitle, !showTitle.isEmpty { - return showTitle - } - // For movies or episodes without show title, use the title from metadata or fallback to URL - return metadata?.title ?? originalURL.lastPathComponent - } + if type == .episode, + let showTitle = metadata?.showTitle, + !showTitle.isEmpty { + return showTitle + } + return metadata?.title ?? originalURL.lastPathComponent + } var episodeDisplayName: String { - guard type == .episode else { return metadata?.title ?? originalURL.lastPathComponent } + guard type == .episode else { + return metadata?.title ?? originalURL.lastPathComponent + } - // Return the title directly since titles typically already contain episode information - return metadata?.title ?? originalURL.lastPathComponent + // Extract base episode number from metadata or default to 1 + let episodeNumber = metadata?.episode ?? 1 + let base = "Episode \(episodeNumber)" + + // Check if we have a valid title that's different from the base + if let title = metadata?.title, !title.isEmpty, title != base { + return "\(base): \(title)" + } else { + return base + } } init( diff --git a/Sora/Views/DownloadView.swift b/Sora/Views/DownloadView.swift index 3b5e60a..126be66 100644 --- a/Sora/Views/DownloadView.swift +++ b/Sora/Views/DownloadView.swift @@ -186,7 +186,8 @@ struct DownloadView: View { SimpleDownloadGroup( title: title, assets: assets, - posterURL: assets.first?.metadata?.posterURL + posterURL: assets.first?.metadata?.showPosterURL + ?? assets.first?.metadata?.posterURL ) }.sorted { $0.title < $1.title } } @@ -238,7 +239,7 @@ struct DownloadView: View { module: dummyModule, urlString: asset.localURL.absoluteString, fullUrl: asset.originalURL.absoluteString, - title: asset.name, + title: asset.metadata?.showTitle ?? asset.name, episodeNumber: asset.metadata?.episode ?? 0, onWatchNext: {}, subtitlesURL: asset.localSubtitleURL?.absoluteString, @@ -484,9 +485,17 @@ struct EpisodeRow: View { } VStack(alignment: .leading, spacing: 2) { - Text(asset.episodeDisplayName) + Text("Episode \(asset.metadata?.episode ?? 1)") .font(.subheadline) .lineLimit(1) + + let base = "Episode \(asset.metadata?.episode ?? 1)" + if asset.episodeDisplayName != base { + Text(asset.episodeDisplayName) + .font(.caption) + .foregroundColor(.secondary) + .lineLimit(1) + } HStack(spacing: 4) { Text(asset.downloadDate.formatted(date: .abbreviated, time: .omitted)) @@ -498,7 +507,6 @@ struct EpisodeRow: View { .foregroundColor(.blue) .font(.caption) } - if !asset.fileExists { Image(systemName: "exclamationmark.triangle") .foregroundColor(.orange) diff --git a/Sora/Views/LibraryView/LibraryView.swift b/Sora/Views/LibraryView/LibraryView.swift index 32cae84..3d24c3d 100644 --- a/Sora/Views/LibraryView/LibraryView.swift +++ b/Sora/Views/LibraryView/LibraryView.swift @@ -286,7 +286,9 @@ struct ContinueWatchingCell: View { }) { VStack(alignment: .leading) { ZStack { - KFImage(URL(string: item.imageUrl.isEmpty ? "https://raw.githubusercontent.com/cranci1/Sora/refs/heads/main/assets/banner2.png" : item.imageUrl)) + KFImage(URL(string: item.imageUrl.isEmpty + ? "https://raw.githubusercontent.com/cranci1/Sora/refs/heads/main/assets/banner2.png" + : item.imageUrl)) .placeholder { RoundedRectangle(cornerRadius: 10) .fill(Color.gray.opacity(0.3)) @@ -300,11 +302,23 @@ struct ContinueWatchingCell: View { .cornerRadius(10) .clipped() .overlay( - KFImage(URL(string: item.module.metadata.iconUrl)) - .resizable() - .frame(width: 24, height: 24) - .cornerRadius(4) - .padding(4), + Group { + if item.streamUrl.hasPrefix("file://") { + Image(systemName: "arrow.down.app.fill") + .resizable() + .scaledToFit() + .frame(width: 24, height: 24) + .foregroundColor(.white) + .background(Color.black.cornerRadius(6)) // black exactly 24×24 + .padding(4) // add spacing outside the black + } else { + KFImage(URL(string: item.module.metadata.iconUrl)) + .resizable() + .frame(width: 24, height: 24) + .cornerRadius(4) + .padding(4) + } + }, alignment: .topLeading ) } diff --git a/Sora/Views/MediaInfoView/EpisodeCell/EpisodeCell.swift b/Sora/Views/MediaInfoView/EpisodeCell/EpisodeCell.swift index 2448bc3..53207c4 100644 --- a/Sora/Views/MediaInfoView/EpisodeCell/EpisodeCell.swift +++ b/Sora/Views/MediaInfoView/EpisodeCell/EpisodeCell.swift @@ -461,8 +461,10 @@ struct EpisodeCell: View { let episodeThumbnailURL = URL(string: episodeImageUrl.isEmpty ? defaultBannerImage : episodeImageUrl) let showPosterImageURL = URL(string: showPosterURL ?? defaultBannerImage) - let episodeName = episodeTitle.isEmpty ? "Episode \(episodeID + 1)" : episodeTitle - let fullEpisodeTitle = "Episode \(episodeID + 1): \(episodeName)" + let baseTitle = "Episode \(episodeID + 1)" + let fullEpisodeTitle = episodeTitle.isEmpty + ? baseTitle + : "\(baseTitle): \(episodeTitle)" let animeTitle = parentTitle.isEmpty ? "Unknown Anime" : parentTitle diff --git a/Sora/Views/MediaInfoView/MediaInfoView.swift b/Sora/Views/MediaInfoView/MediaInfoView.swift index fc168ef..a77a1c7 100644 --- a/Sora/Views/MediaInfoView/MediaInfoView.swift +++ b/Sora/Views/MediaInfoView/MediaInfoView.swift @@ -1315,8 +1315,8 @@ struct MediaInfoView: View { let episodeTitle = metadata?.title["en"] ?? metadata?.title.values.first ?? "" let episodeImageUrl = metadata?.imageUrl ?? "" - let episodeName = episodeTitle.isEmpty ? "Episode \(episode.number)" : episodeTitle - let fullEpisodeTitle = "Episode \(episode.number): \(episodeName)" + let episodeName = metadata?.title["en"] ?? "Episode \(episode.number)" + let fullEpisodeTitle = episodeName let episodeThumbnailURL: URL? if !episodeImageUrl.isEmpty {