mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 21:43:41 +00:00
Comments UI fixes
This commit is contained in:
parent
8d49934fe8
commit
008cd1553d
@ -27,10 +27,15 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
||||
self.account = account
|
||||
signedIn = false
|
||||
|
||||
if account.anonymous {
|
||||
validInstance = true
|
||||
return
|
||||
}
|
||||
|
||||
validInstance = false
|
||||
signedIn = !account.anonymous
|
||||
|
||||
configure()
|
||||
validate()
|
||||
}
|
||||
|
||||
func validate() {
|
||||
@ -81,11 +86,11 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("popular"), requestMethods: [.get]) { (content: Entity<JSON>) -> [Video] in
|
||||
content.json.arrayValue.map(InvidiousAPI.extractVideo)
|
||||
content.json.arrayValue.map(self.extractVideo)
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("trending"), requestMethods: [.get]) { (content: Entity<JSON>) -> [Video] in
|
||||
content.json.arrayValue.map(InvidiousAPI.extractVideo)
|
||||
content.json.arrayValue.map(self.extractVideo)
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("search"), requestMethods: [.get]) { (content: Entity<JSON>) -> [ContentItem] in
|
||||
@ -93,11 +98,11 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
||||
let type = $0.dictionaryValue["type"]?.stringValue
|
||||
|
||||
if type == "channel" {
|
||||
return ContentItem(channel: InvidiousAPI.extractChannel(from: $0))
|
||||
return ContentItem(channel: self.extractChannel(from: $0))
|
||||
} else if type == "playlist" {
|
||||
return ContentItem(playlist: InvidiousAPI.extractChannelPlaylist(from: $0))
|
||||
return ContentItem(playlist: self.extractChannelPlaylist(from: $0))
|
||||
}
|
||||
return ContentItem(video: InvidiousAPI.extractVideo(from: $0))
|
||||
return ContentItem(video: self.extractVideo(from: $0))
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,11 +115,11 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("auth/playlists"), requestMethods: [.get]) { (content: Entity<JSON>) -> [Playlist] in
|
||||
content.json.arrayValue.map(Playlist.init)
|
||||
content.json.arrayValue.map(self.extractPlaylist)
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("auth/playlists/*"), requestMethods: [.get]) { (content: Entity<JSON>) -> Playlist in
|
||||
Playlist(content.json)
|
||||
self.extractPlaylist(from: content.json)
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("auth/playlists"), requestMethods: [.post, .patch]) { (content: Entity<Data>) -> Playlist in
|
||||
@ -124,30 +129,30 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
||||
|
||||
configureTransformer(pathPattern("auth/feed"), requestMethods: [.get]) { (content: Entity<JSON>) -> [Video] in
|
||||
if let feedVideos = content.json.dictionaryValue["videos"] {
|
||||
return feedVideos.arrayValue.map(InvidiousAPI.extractVideo)
|
||||
return feedVideos.arrayValue.map(self.extractVideo)
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("auth/subscriptions"), requestMethods: [.get]) { (content: Entity<JSON>) -> [Channel] in
|
||||
content.json.arrayValue.map(InvidiousAPI.extractChannel)
|
||||
content.json.arrayValue.map(self.extractChannel)
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("channels/*"), requestMethods: [.get]) { (content: Entity<JSON>) -> Channel in
|
||||
InvidiousAPI.extractChannel(from: content.json)
|
||||
self.extractChannel(from: content.json)
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("channels/*/latest"), requestMethods: [.get]) { (content: Entity<JSON>) -> [Video] in
|
||||
content.json.arrayValue.map(InvidiousAPI.extractVideo)
|
||||
content.json.arrayValue.map(self.extractVideo)
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("playlists/*"), requestMethods: [.get]) { (content: Entity<JSON>) -> ChannelPlaylist in
|
||||
InvidiousAPI.extractChannelPlaylist(from: content.json)
|
||||
self.extractChannelPlaylist(from: content.json)
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("videos/*"), requestMethods: [.get]) { (content: Entity<JSON>) -> Video in
|
||||
InvidiousAPI.extractVideo(from: content.json)
|
||||
self.extractVideo(from: content.json)
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,7 +297,7 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
||||
return AVURLAsset(url: url)
|
||||
}
|
||||
|
||||
static func extractVideo(from json: JSON) -> Video {
|
||||
func extractVideo(from json: JSON) -> Video {
|
||||
let indexID: String?
|
||||
var id: Video.ID
|
||||
var publishedAt: Date?
|
||||
@ -335,8 +340,15 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
||||
)
|
||||
}
|
||||
|
||||
static func extractChannel(from json: JSON) -> Channel {
|
||||
let thumbnailURL = "https:\(json["authorThumbnails"].arrayValue.first?.dictionaryValue["url"]?.stringValue ?? "")"
|
||||
func extractChannel(from json: JSON) -> Channel {
|
||||
var thumbnailURL = json["authorThumbnails"].arrayValue.last?.dictionaryValue["url"]?.stringValue ?? ""
|
||||
|
||||
// append https protocol to unproxied thumbnail URL if it's missing
|
||||
if thumbnailURL.count > 2,
|
||||
String(thumbnailURL[..<thumbnailURL.index(thumbnailURL.startIndex, offsetBy: 2)]) == "//"
|
||||
{
|
||||
thumbnailURL = "https:\(thumbnailURL)"
|
||||
}
|
||||
|
||||
return Channel(
|
||||
id: json["authorId"].stringValue,
|
||||
@ -344,33 +356,33 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
||||
thumbnailURL: URL(string: thumbnailURL),
|
||||
subscriptionsCount: json["subCount"].int,
|
||||
subscriptionsText: json["subCountText"].string,
|
||||
videos: json.dictionaryValue["latestVideos"]?.arrayValue.map(InvidiousAPI.extractVideo) ?? []
|
||||
videos: json.dictionaryValue["latestVideos"]?.arrayValue.map(extractVideo) ?? []
|
||||
)
|
||||
}
|
||||
|
||||
static func extractChannelPlaylist(from json: JSON) -> ChannelPlaylist {
|
||||
func extractChannelPlaylist(from json: JSON) -> ChannelPlaylist {
|
||||
let details = json.dictionaryValue
|
||||
return ChannelPlaylist(
|
||||
id: details["playlistId"]!.stringValue,
|
||||
title: details["title"]!.stringValue,
|
||||
thumbnailURL: details["playlistThumbnail"]?.url,
|
||||
channel: extractChannel(from: json),
|
||||
videos: details["videos"]?.arrayValue.compactMap(InvidiousAPI.extractVideo) ?? []
|
||||
videos: details["videos"]?.arrayValue.compactMap(extractVideo) ?? []
|
||||
)
|
||||
}
|
||||
|
||||
private static func extractThumbnails(from details: JSON) -> [Thumbnail] {
|
||||
private func extractThumbnails(from details: JSON) -> [Thumbnail] {
|
||||
details["videoThumbnails"].arrayValue.map { json in
|
||||
Thumbnail(url: json["url"].url!, quality: .init(rawValue: json["quality"].string!)!)
|
||||
}
|
||||
}
|
||||
|
||||
private static func extractStreams(from json: JSON) -> [Stream] {
|
||||
private func extractStreams(from json: JSON) -> [Stream] {
|
||||
extractFormatStreams(from: json["formatStreams"].arrayValue) +
|
||||
extractAdaptiveFormats(from: json["adaptiveFormats"].arrayValue)
|
||||
}
|
||||
|
||||
private static func extractFormatStreams(from streams: [JSON]) -> [Stream] {
|
||||
private func extractFormatStreams(from streams: [JSON]) -> [Stream] {
|
||||
streams.map {
|
||||
SingleAssetStream(
|
||||
avAsset: AVURLAsset(url: $0["url"].url!),
|
||||
@ -381,7 +393,7 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
||||
}
|
||||
}
|
||||
|
||||
private static func extractAdaptiveFormats(from streams: [JSON]) -> [Stream] {
|
||||
private func extractAdaptiveFormats(from streams: [JSON]) -> [Stream] {
|
||||
let audioAssetURL = streams.first { $0["type"].stringValue.starts(with: "audio/mp4") }
|
||||
guard audioAssetURL != nil else {
|
||||
return []
|
||||
@ -400,10 +412,20 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI {
|
||||
}
|
||||
}
|
||||
|
||||
private static func extractRelated(from content: JSON) -> [Video] {
|
||||
private func extractRelated(from content: JSON) -> [Video] {
|
||||
content
|
||||
.dictionaryValue["recommendedVideos"]?
|
||||
.arrayValue
|
||||
.compactMap(extractVideo(from:)) ?? []
|
||||
}
|
||||
|
||||
private func extractPlaylist(from content: JSON) -> Playlist {
|
||||
.init(
|
||||
id: content["playlistId"].stringValue,
|
||||
title: content["title"].stringValue,
|
||||
visibility: content["isListed"].boolValue ? .public : .private,
|
||||
updated: content["updated"].doubleValue,
|
||||
videos: content["videos"].arrayValue.map { extractVideo(from: $0) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -40,23 +40,23 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("channel/*")) { (content: Entity<JSON>) -> Channel? in
|
||||
PipedAPI.extractChannel(from: content.json)
|
||||
self.extractChannel(from: content.json)
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("playlists/*")) { (content: Entity<JSON>) -> ChannelPlaylist? in
|
||||
PipedAPI.extractChannelPlaylist(from: content.json)
|
||||
self.extractChannelPlaylist(from: content.json)
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("streams/*")) { (content: Entity<JSON>) -> Video? in
|
||||
PipedAPI.extractVideo(from: content.json)
|
||||
self.extractVideo(from: content.json)
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("trending")) { (content: Entity<JSON>) -> [Video] in
|
||||
PipedAPI.extractVideos(from: content.json)
|
||||
self.extractVideos(from: content.json)
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("search")) { (content: Entity<JSON>) -> [ContentItem] in
|
||||
PipedAPI.extractContentItems(from: content.json.dictionaryValue["items"]!)
|
||||
self.extractContentItems(from: content.json.dictionaryValue["items"]!)
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("suggestions")) { (content: Entity<JSON>) -> [String] in
|
||||
@ -64,16 +64,16 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("subscriptions")) { (content: Entity<JSON>) -> [Channel] in
|
||||
content.json.arrayValue.map { PipedAPI.extractChannel(from: $0)! }
|
||||
content.json.arrayValue.map { self.extractChannel(from: $0)! }
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("feed")) { (content: Entity<JSON>) -> [Video] in
|
||||
content.json.arrayValue.map { PipedAPI.extractVideo(from: $0)! }
|
||||
content.json.arrayValue.map { self.extractVideo(from: $0)! }
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("comments/*")) { (content: Entity<JSON>) -> CommentsPage in
|
||||
let details = content.json.dictionaryValue
|
||||
let comments = details["comments"]?.arrayValue.map { PipedAPI.extractComment(from: $0)! } ?? []
|
||||
let comments = details["comments"]?.arrayValue.map { self.extractComment(from: $0)! } ?? []
|
||||
let nextPage = details["nextpage"]?.stringValue
|
||||
let disabled = details["disabled"]?.boolValue ?? false
|
||||
|
||||
@ -86,7 +86,7 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
}
|
||||
|
||||
func needsAuthorization(_ url: URL) -> Bool {
|
||||
PipedAPI.authorizedEndpoints.contains { url.absoluteString.contains($0) }
|
||||
Self.authorizedEndpoints.contains { url.absoluteString.contains($0) }
|
||||
}
|
||||
|
||||
func updateToken() {
|
||||
@ -190,7 +190,7 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
"**\(path)"
|
||||
}
|
||||
|
||||
private static func extractContentItem(from content: JSON) -> ContentItem? {
|
||||
private func extractContentItem(from content: JSON) -> ContentItem? {
|
||||
let details = content.dictionaryValue
|
||||
let url: String! = details["url"]?.string
|
||||
|
||||
@ -210,17 +210,17 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
|
||||
switch contentType {
|
||||
case .video:
|
||||
if let video = PipedAPI.extractVideo(from: content) {
|
||||
if let video = extractVideo(from: content) {
|
||||
return ContentItem(video: video)
|
||||
}
|
||||
|
||||
case .playlist:
|
||||
if let playlist = PipedAPI.extractChannelPlaylist(from: content) {
|
||||
if let playlist = extractChannelPlaylist(from: content) {
|
||||
return ContentItem(playlist: playlist)
|
||||
}
|
||||
|
||||
case .channel:
|
||||
if let channel = PipedAPI.extractChannel(from: content) {
|
||||
if let channel = extractChannel(from: content) {
|
||||
return ContentItem(channel: channel)
|
||||
}
|
||||
}
|
||||
@ -228,11 +228,11 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
return nil
|
||||
}
|
||||
|
||||
private static func extractContentItems(from content: JSON) -> [ContentItem] {
|
||||
content.arrayValue.compactMap { PipedAPI.extractContentItem(from: $0) }
|
||||
private func extractContentItems(from content: JSON) -> [ContentItem] {
|
||||
content.arrayValue.compactMap { extractContentItem(from: $0) }
|
||||
}
|
||||
|
||||
private static func extractChannel(from content: JSON) -> Channel? {
|
||||
private func extractChannel(from content: JSON) -> Channel? {
|
||||
let attributes = content.dictionaryValue
|
||||
guard let id = attributes["id"]?.stringValue ??
|
||||
(attributes["url"] ?? attributes["uploaderUrl"])?.stringValue.components(separatedBy: "/").last
|
||||
@ -244,25 +244,28 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
|
||||
var videos = [Video]()
|
||||
if let relatedStreams = attributes["relatedStreams"] {
|
||||
videos = PipedAPI.extractVideos(from: relatedStreams)
|
||||
videos = extractVideos(from: relatedStreams)
|
||||
}
|
||||
|
||||
let name = attributes["name"]?.stringValue ?? attributes["uploaderName"]?.stringValue ?? attributes["uploader"]?.stringValue ?? ""
|
||||
let thumbnailURL = attributes["avatarUrl"]?.url ?? attributes["uploaderAvatar"]?.url ?? attributes["avatar"]?.url ?? attributes["thumbnail"]?.url
|
||||
|
||||
return Channel(
|
||||
id: id,
|
||||
name: attributes["name"]!.stringValue,
|
||||
thumbnailURL: attributes["thumbnail"]?.url,
|
||||
name: name,
|
||||
thumbnailURL: thumbnailURL,
|
||||
subscriptionsCount: subscriptionsCount,
|
||||
videos: videos
|
||||
)
|
||||
}
|
||||
|
||||
static func extractChannelPlaylist(from json: JSON) -> ChannelPlaylist? {
|
||||
func extractChannelPlaylist(from json: JSON) -> ChannelPlaylist? {
|
||||
let details = json.dictionaryValue
|
||||
let id = details["url"]?.stringValue.components(separatedBy: "?list=").last ?? UUID().uuidString
|
||||
let thumbnailURL = details["thumbnail"]?.url ?? details["thumbnailUrl"]?.url
|
||||
var videos = [Video]()
|
||||
if let relatedStreams = details["relatedStreams"] {
|
||||
videos = PipedAPI.extractVideos(from: relatedStreams)
|
||||
videos = extractVideos(from: relatedStreams)
|
||||
}
|
||||
return ChannelPlaylist(
|
||||
id: id,
|
||||
@ -274,7 +277,7 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
)
|
||||
}
|
||||
|
||||
private static func extractVideo(from content: JSON) -> Video? {
|
||||
private func extractVideo(from content: JSON) -> Video? {
|
||||
let details = content.dictionaryValue
|
||||
let url = details["url"]?.string
|
||||
|
||||
@ -287,7 +290,7 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
let channelId = details["uploaderUrl"]!.stringValue.components(separatedBy: "/").last!
|
||||
|
||||
let thumbnails: [Thumbnail] = Thumbnail.Quality.allCases.compactMap {
|
||||
if let url = PipedAPI.buildThumbnailURL(from: content, quality: $0) {
|
||||
if let url = buildThumbnailURL(from: content, quality: $0) {
|
||||
return Thumbnail(url: url, quality: $0)
|
||||
}
|
||||
|
||||
@ -295,18 +298,20 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
}
|
||||
|
||||
let author = details["uploaderName"]?.stringValue ?? details["uploader"]!.stringValue
|
||||
let authorThumbnailURL = details["avatarUrl"]?.url ?? details["uploaderAvatar"]?.url ?? details["avatar"]?.url
|
||||
|
||||
let published = (details["uploadedDate"] ?? details["uploadDate"])?.stringValue ??
|
||||
(details["uploaded"]!.double! / 1000).formattedAsRelativeTime()!
|
||||
|
||||
return Video(
|
||||
videoID: PipedAPI.extractID(from: content),
|
||||
videoID: extractID(from: content),
|
||||
title: details["title"]!.stringValue,
|
||||
author: author,
|
||||
length: details["duration"]!.doubleValue,
|
||||
published: published,
|
||||
views: details["views"]!.intValue,
|
||||
description: PipedAPI.extractDescription(from: content),
|
||||
channel: Channel(id: channelId, name: author),
|
||||
description: extractDescription(from: content),
|
||||
channel: Channel(id: channelId, name: author, thumbnailURL: authorThumbnailURL),
|
||||
thumbnails: thumbnails,
|
||||
likes: details["likes"]?.int,
|
||||
dislikes: details["dislikes"]?.int,
|
||||
@ -315,16 +320,16 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
)
|
||||
}
|
||||
|
||||
private static func extractID(from content: JSON) -> Video.ID {
|
||||
private func extractID(from content: JSON) -> Video.ID {
|
||||
content.dictionaryValue["url"]?.stringValue.components(separatedBy: "?v=").last ??
|
||||
extractThumbnailURL(from: content)!.relativeString.components(separatedBy: "/")[4]
|
||||
}
|
||||
|
||||
private static func extractThumbnailURL(from content: JSON) -> URL? {
|
||||
private func extractThumbnailURL(from content: JSON) -> URL? {
|
||||
content.dictionaryValue["thumbnail"]?.url! ?? content.dictionaryValue["thumbnailUrl"]!.url!
|
||||
}
|
||||
|
||||
private static func buildThumbnailURL(from content: JSON, quality: Thumbnail.Quality) -> URL? {
|
||||
private func buildThumbnailURL(from content: JSON, quality: Thumbnail.Quality) -> URL? {
|
||||
let thumbnailURL = extractThumbnailURL(from: content)
|
||||
guard !thumbnailURL.isNil else {
|
||||
return nil
|
||||
@ -337,7 +342,7 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
)!
|
||||
}
|
||||
|
||||
private static func extractDescription(from content: JSON) -> String? {
|
||||
private func extractDescription(from content: JSON) -> String? {
|
||||
guard var description = content.dictionaryValue["description"]?.string else {
|
||||
return nil
|
||||
}
|
||||
@ -359,22 +364,22 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
return description
|
||||
}
|
||||
|
||||
private static func extractVideos(from content: JSON) -> [Video] {
|
||||
private func extractVideos(from content: JSON) -> [Video] {
|
||||
content.arrayValue.compactMap(extractVideo(from:))
|
||||
}
|
||||
|
||||
private static func extractStreams(from content: JSON) -> [Stream] {
|
||||
private func extractStreams(from content: JSON) -> [Stream] {
|
||||
var streams = [Stream]()
|
||||
|
||||
if let hlsURL = content.dictionaryValue["hls"]?.url {
|
||||
streams.append(Stream(hlsURL: hlsURL))
|
||||
}
|
||||
|
||||
guard let audioStream = PipedAPI.compatibleAudioStreams(from: content).first else {
|
||||
guard let audioStream = compatibleAudioStreams(from: content).first else {
|
||||
return streams
|
||||
}
|
||||
|
||||
let videoStreams = PipedAPI.compatibleVideoStream(from: content)
|
||||
let videoStreams = compatibleVideoStream(from: content)
|
||||
|
||||
videoStreams.forEach { videoStream in
|
||||
let audioAsset = AVURLAsset(url: audioStream.dictionaryValue["url"]!.url!)
|
||||
@ -397,14 +402,14 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
return streams
|
||||
}
|
||||
|
||||
private static func extractRelated(from content: JSON) -> [Video] {
|
||||
private func extractRelated(from content: JSON) -> [Video] {
|
||||
content
|
||||
.dictionaryValue["relatedStreams"]?
|
||||
.arrayValue
|
||||
.compactMap(extractVideo(from:)) ?? []
|
||||
}
|
||||
|
||||
private static func compatibleAudioStreams(from content: JSON) -> [JSON] {
|
||||
private func compatibleAudioStreams(from content: JSON) -> [JSON] {
|
||||
content
|
||||
.dictionaryValue["audioStreams"]?
|
||||
.arrayValue
|
||||
@ -414,14 +419,14 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
} ?? []
|
||||
}
|
||||
|
||||
private static func compatibleVideoStream(from content: JSON) -> [JSON] {
|
||||
private func compatibleVideoStream(from content: JSON) -> [JSON] {
|
||||
content
|
||||
.dictionaryValue["videoStreams"]?
|
||||
.arrayValue
|
||||
.filter { $0.dictionaryValue["format"] == "MPEG_4" } ?? []
|
||||
}
|
||||
|
||||
private static func extractComment(from content: JSON) -> Comment? {
|
||||
private func extractComment(from content: JSON) -> Comment? {
|
||||
let details = content.dictionaryValue
|
||||
let author = details["author"]?.stringValue ?? ""
|
||||
let commentorUrl = details["commentorUrl"]?.stringValue
|
||||
|
@ -22,11 +22,12 @@ struct Playlist: Identifiable, Equatable, Hashable {
|
||||
|
||||
var videos = [Video]()
|
||||
|
||||
init(id: String, title: String, visibility: Visibility, updated: TimeInterval) {
|
||||
init(id: String, title: String, visibility: Visibility, updated: TimeInterval, videos: [Video] = []) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.visibility = visibility
|
||||
self.updated = updated
|
||||
self.videos = videos
|
||||
}
|
||||
|
||||
init(_ json: JSON) {
|
||||
@ -34,7 +35,6 @@ struct Playlist: Identifiable, Equatable, Hashable {
|
||||
title = json["title"].stringValue
|
||||
visibility = json["isListed"].boolValue ? .public : .private
|
||||
updated = json["updated"].doubleValue
|
||||
videos = json["videos"].arrayValue.map { InvidiousAPI.extractVideo(from: $0) }
|
||||
}
|
||||
|
||||
static func == (lhs: Playlist, rhs: Playlist) -> Bool {
|
||||
|
Loading…
Reference in New Issue
Block a user