mirror of
https://github.com/yattee/yattee.git
synced 2024-12-23 05:53:41 +00:00
Add Invidious comments support
This commit is contained in:
parent
4fcf57d755
commit
7c4ee9bf35
@ -157,6 +157,15 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
|||||||
configureTransformer(pathPattern("videos/*"), requestMethods: [.get]) { (content: Entity<JSON>) -> Video in
|
configureTransformer(pathPattern("videos/*"), requestMethods: [.get]) { (content: Entity<JSON>) -> Video in
|
||||||
self.extractVideo(from: content.json)
|
self.extractVideo(from: content.json)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configureTransformer(pathPattern("comments/*")) { (content: Entity<JSON>) -> 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 {
|
private func pathPattern(_ path: String) -> String {
|
||||||
@ -337,7 +346,12 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
|||||||
.withParam("q", query.lowercased())
|
.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 {
|
private func searchQuery(_ query: String) -> String {
|
||||||
var searchQuery = query
|
var searchQuery = query
|
||||||
@ -533,4 +547,23 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
|||||||
videos: content["videos"].arrayValue.map { extractVideo(from: $0) }
|
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)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,10 +51,6 @@ enum VideosApp: String, CaseIterable {
|
|||||||
self == .piped
|
self == .piped
|
||||||
}
|
}
|
||||||
|
|
||||||
var supportsComments: Bool {
|
|
||||||
self == .piped
|
|
||||||
}
|
|
||||||
|
|
||||||
var searchUsesIndexedPages: Bool {
|
var searchUsesIndexedPages: Bool {
|
||||||
self == .invidious
|
self == .invidious
|
||||||
}
|
}
|
||||||
|
@ -17,38 +17,16 @@ final class CommentsModel: ObservableObject {
|
|||||||
|
|
||||||
var player: PlayerModel!
|
var player: PlayerModel!
|
||||||
|
|
||||||
static var instance: Instance? {
|
var instance: Instance? {
|
||||||
InstancesModel.find(Defaults[.commentsInstanceID])
|
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 {
|
var nextPageAvailable: Bool {
|
||||||
!(nextPage?.isEmpty ?? true)
|
!(nextPage?.isEmpty ?? true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func load(page: String? = nil) {
|
func load(page: String? = nil) {
|
||||||
guard Self.enabled, !loaded else {
|
guard let video = player.currentVideo else { return }
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard !Self.instance.isNil,
|
|
||||||
let video = player.currentVideo
|
|
||||||
else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !firstPage && !nextPageAvailable {
|
if !firstPage && !nextPageAvailable {
|
||||||
return
|
return
|
||||||
@ -56,7 +34,7 @@ final class CommentsModel: ObservableObject {
|
|||||||
|
|
||||||
firstPage = page.isNil || page!.isEmpty
|
firstPage = page.isNil || page!.isEmpty
|
||||||
|
|
||||||
api?.comments(video.videoID, page: page)?
|
player.accounts.api.comments(video.videoID, page: page)?
|
||||||
.load()
|
.load()
|
||||||
.onSuccess { [weak self] response in
|
.onSuccess { [weak self] response in
|
||||||
if let page: CommentsPage = response.typedContent() {
|
if let page: CommentsPage = response.typedContent() {
|
||||||
@ -65,6 +43,9 @@ final class CommentsModel: ObservableObject {
|
|||||||
self?.disabled = page.disabled
|
self?.disabled = page.disabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onFailure { [weak self] requestError in
|
||||||
|
self?.disabled = !requestError.json.dictionaryValue["error"].isNil
|
||||||
|
}
|
||||||
.onCompletion { [weak self] _ in
|
.onCompletion { [weak self] _ in
|
||||||
self?.loaded = true
|
self?.loaded = true
|
||||||
}
|
}
|
||||||
@ -94,7 +75,7 @@ final class CommentsModel: ObservableObject {
|
|||||||
repliesPageID = page
|
repliesPageID = page
|
||||||
repliesLoaded = false
|
repliesLoaded = false
|
||||||
|
|
||||||
api?.comments(player.currentVideo!.videoID, page: page)?
|
player.accounts.api.comments(player.currentVideo!.videoID, page: page)?
|
||||||
.load()
|
.load()
|
||||||
.onSuccess { [weak self] response in
|
.onSuccess { [weak self] response in
|
||||||
if let page: CommentsPage = response.typedContent() {
|
if let page: CommentsPage = response.typedContent() {
|
||||||
|
@ -53,7 +53,6 @@ extension Defaults.Keys {
|
|||||||
static let playerInstanceID = Key<Instance.ID?>("playerInstance")
|
static let playerInstanceID = Key<Instance.ID?>("playerInstance")
|
||||||
static let showKeywords = Key<Bool>("showKeywords", default: false)
|
static let showKeywords = Key<Bool>("showKeywords", default: false)
|
||||||
static let showHistoryInPlayer = Key<Bool>("showHistoryInPlayer", default: false)
|
static let showHistoryInPlayer = Key<Bool>("showHistoryInPlayer", default: false)
|
||||||
static let commentsInstanceID = Key<Instance.ID?>("commentsInstance", default: nil)
|
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
static let commentsPlacement = Key<CommentsPlacement>("commentsPlacement", default: .separate)
|
static let commentsPlacement = Key<CommentsPlacement>("commentsPlacement", default: .separate)
|
||||||
#endif
|
#endif
|
||||||
|
@ -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)
|
.padding(.horizontal)
|
||||||
|
|
||||||
LazyVStack {
|
|
||||||
if !video.isNil, CommentsModel.placement == .info {
|
|
||||||
CommentsView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ struct PlayerSettings: View {
|
|||||||
@Default(.instances) private var instances
|
@Default(.instances) private var instances
|
||||||
@Default(.playerInstanceID) private var playerInstanceID
|
@Default(.playerInstanceID) private var playerInstanceID
|
||||||
@Default(.quality) private var quality
|
@Default(.quality) private var quality
|
||||||
@Default(.commentsInstanceID) private var commentsInstanceID
|
|
||||||
|
|
||||||
@Default(.playerSidebar) private var playerSidebar
|
@Default(.playerSidebar) private var playerSidebar
|
||||||
@Default(.showHistoryInPlayer) private var showHistory
|
@Default(.showHistoryInPlayer) private var showHistory
|
||||||
@ -64,10 +63,6 @@ struct PlayerSettings: View {
|
|||||||
closeLastItemOnPlaybackEndToggle
|
closeLastItemOnPlaybackEndToggle
|
||||||
}
|
}
|
||||||
|
|
||||||
Section(header: SettingsHeader(text: "Comments")) {
|
|
||||||
commentsInstancePicker
|
|
||||||
}
|
|
||||||
|
|
||||||
Section(header: SettingsHeader(text: "Interface")) {
|
Section(header: SettingsHeader(text: "Interface")) {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
if idiom == .pad {
|
if idiom == .pad {
|
||||||
@ -135,22 +130,6 @@ struct PlayerSettings: View {
|
|||||||
#endif
|
#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 {
|
private var sidebarPicker: some View {
|
||||||
Picker("Sidebar", selection: $playerSidebar) {
|
Picker("Sidebar", selection: $playerSidebar) {
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
|
@ -190,7 +190,7 @@ struct SettingsView: View {
|
|||||||
case .browsing:
|
case .browsing:
|
||||||
return 390
|
return 390
|
||||||
case .player:
|
case .player:
|
||||||
return 500
|
return 390
|
||||||
case .history:
|
case .history:
|
||||||
return 480
|
return 480
|
||||||
case .sponsorBlock:
|
case .sponsorBlock:
|
||||||
|
Loading…
Reference in New Issue
Block a user