diff --git a/Model/Applications/InvidiousAPI.swift b/Model/Applications/InvidiousAPI.swift index af1d3092..72865cae 100644 --- a/Model/Applications/InvidiousAPI.swift +++ b/Model/Applications/InvidiousAPI.swift @@ -157,6 +157,15 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI { configureTransformer(pathPattern("videos/*"), requestMethods: [.get]) { (content: Entity) -> Video in self.extractVideo(from: content.json) } + + configureTransformer(pathPattern("comments/*")) { (content: Entity) -> CommentsPage in + let details = content.json.dictionaryValue + let comments = details["comments"]?.arrayValue.compactMap { self.extractComment(from: $0) } ?? [] + let nextPage = details["continuation"]?.string + let disabled = !details["error"].isNil + + return CommentsPage(comments: comments, nextPage: nextPage, disabled: disabled) + } } private func pathPattern(_ path: String) -> String { @@ -337,7 +346,12 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI { .withParam("q", query.lowercased()) } - func comments(_: Video.ID, page _: String?) -> Resource? { nil } + func comments(_ id: Video.ID, page: String?) -> Resource? { + let resource = resource(baseURL: account.url, path: basePathAppending("comments/\(id)")) + guard let page = page else { return resource } + + return resource.withParam("continuation", page) + } private func searchQuery(_ query: String) -> String { var searchQuery = query @@ -533,4 +547,23 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI { videos: content["videos"].arrayValue.map { extractVideo(from: $0) } ) } + + private func extractComment(from content: JSON) -> Comment? { + let details = content.dictionaryValue + let author = details["author"]?.string ?? "" + let channelId = details["authorId"]?.string ?? UUID().uuidString + let authorAvatarURL = details["authorThumbnails"]?.arrayValue.last?.dictionaryValue["url"]?.string ?? "" + return Comment( + id: UUID().uuidString, + author: author, + authorAvatarURL: authorAvatarURL, + time: details["publishedText"]?.string ?? "", + pinned: false, + hearted: false, + likeCount: details["likeCount"]?.int ?? 0, + text: details["content"]?.string ?? "", + repliesPage: details["replies"]?.dictionaryValue["continuation"]?.string, + channel: Channel(id: channelId, name: author) + ) + } } diff --git a/Model/Applications/VideosApp.swift b/Model/Applications/VideosApp.swift index c3721768..ba5caab8 100644 --- a/Model/Applications/VideosApp.swift +++ b/Model/Applications/VideosApp.swift @@ -51,10 +51,6 @@ enum VideosApp: String, CaseIterable { self == .piped } - var supportsComments: Bool { - self == .piped - } - var searchUsesIndexedPages: Bool { self == .invidious } diff --git a/Model/CommentsModel.swift b/Model/CommentsModel.swift index 4849aeb4..74045af7 100644 --- a/Model/CommentsModel.swift +++ b/Model/CommentsModel.swift @@ -17,38 +17,16 @@ final class CommentsModel: ObservableObject { var player: PlayerModel! - static var instance: Instance? { - InstancesModel.find(Defaults[.commentsInstanceID]) + var instance: Instance? { + player.accounts.current?.instance } - var api: VideosAPI? { - Self.instance.isNil ? nil : PipedAPI(account: Self.instance!.anonymousAccount) - } - - static var enabled: Bool { - !instance.isNil - } - - #if !os(tvOS) - static var placement: CommentsPlacement { - Defaults[.commentsPlacement] - } - #endif - var nextPageAvailable: Bool { !(nextPage?.isEmpty ?? true) } func load(page: String? = nil) { - guard Self.enabled, !loaded else { - return - } - - guard !Self.instance.isNil, - let video = player.currentVideo - else { - return - } + guard let video = player.currentVideo else { return } if !firstPage && !nextPageAvailable { return @@ -56,7 +34,7 @@ final class CommentsModel: ObservableObject { firstPage = page.isNil || page!.isEmpty - api?.comments(video.videoID, page: page)? + player.accounts.api.comments(video.videoID, page: page)? .load() .onSuccess { [weak self] response in if let page: CommentsPage = response.typedContent() { @@ -65,6 +43,9 @@ final class CommentsModel: ObservableObject { self?.disabled = page.disabled } } + .onFailure { [weak self] requestError in + self?.disabled = !requestError.json.dictionaryValue["error"].isNil + } .onCompletion { [weak self] _ in self?.loaded = true } @@ -94,7 +75,7 @@ final class CommentsModel: ObservableObject { repliesPageID = page repliesLoaded = false - api?.comments(player.currentVideo!.videoID, page: page)? + player.accounts.api.comments(player.currentVideo!.videoID, page: page)? .load() .onSuccess { [weak self] response in if let page: CommentsPage = response.typedContent() { diff --git a/Shared/Defaults.swift b/Shared/Defaults.swift index 1bdf77ce..e35857e3 100644 --- a/Shared/Defaults.swift +++ b/Shared/Defaults.swift @@ -53,7 +53,6 @@ extension Defaults.Keys { static let playerInstanceID = Key("playerInstance") static let showKeywords = Key("showKeywords", default: false) static let showHistoryInPlayer = Key("showHistoryInPlayer", default: false) - static let commentsInstanceID = Key("commentsInstance", default: nil) #if !os(tvOS) static let commentsPlacement = Key("commentsPlacement", default: .separate) #endif diff --git a/Shared/Player/VideoDetails.swift b/Shared/Player/VideoDetails.swift index a59b3e2b..8f629701 100644 --- a/Shared/Player/VideoDetails.swift +++ b/Shared/Player/VideoDetails.swift @@ -255,23 +255,8 @@ struct VideoDetails: View { } } } - - if !video.isNil, CommentsModel.placement == .info { - Divider() - #if os(macOS) - .padding(.bottom, 20) - #else - .padding(.vertical, 10) - #endif - } } .padding(.horizontal) - - LazyVStack { - if !video.isNil, CommentsModel.placement == .info { - CommentsView() - } - } } } diff --git a/Shared/Settings/PlayerSettings.swift b/Shared/Settings/PlayerSettings.swift index ea4601b7..c3b3cc08 100644 --- a/Shared/Settings/PlayerSettings.swift +++ b/Shared/Settings/PlayerSettings.swift @@ -5,7 +5,6 @@ struct PlayerSettings: View { @Default(.instances) private var instances @Default(.playerInstanceID) private var playerInstanceID @Default(.quality) private var quality - @Default(.commentsInstanceID) private var commentsInstanceID @Default(.playerSidebar) private var playerSidebar @Default(.showHistoryInPlayer) private var showHistory @@ -64,10 +63,6 @@ struct PlayerSettings: View { closeLastItemOnPlaybackEndToggle } - Section(header: SettingsHeader(text: "Comments")) { - commentsInstancePicker - } - Section(header: SettingsHeader(text: "Interface")) { #if os(iOS) if idiom == .pad { @@ -135,22 +130,6 @@ struct PlayerSettings: View { #endif } - private var commentsInstancePicker: some View { - Picker("Source", selection: $commentsInstanceID) { - Text("Disabled").tag(Optional("")) - - ForEach(InstancesModel.all.filter { $0.app.supportsComments }) { instance in - Text(instance.description).tag(Optional(instance.id)) - } - } - .labelsHidden() - #if os(iOS) - .pickerStyle(.automatic) - #elseif os(tvOS) - .pickerStyle(.inline) - #endif - } - private var sidebarPicker: some View { Picker("Sidebar", selection: $playerSidebar) { #if os(macOS) diff --git a/Shared/Settings/SettingsView.swift b/Shared/Settings/SettingsView.swift index 3eba63d2..b483b1d1 100644 --- a/Shared/Settings/SettingsView.swift +++ b/Shared/Settings/SettingsView.swift @@ -190,7 +190,7 @@ struct SettingsView: View { case .browsing: return 390 case .player: - return 500 + return 390 case .history: return 480 case .sponsorBlock: