mirror of
https://github.com/yattee/yattee.git
synced 2024-11-10 00:08:21 +00:00
Preliminary support for Piped playlist (listing playlists and videos)
This commit is contained in:
parent
836057578f
commit
8d36f57271
@ -4,7 +4,7 @@ import Siesta
|
||||
import SwiftyJSON
|
||||
|
||||
final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
static var authorizedEndpoints = ["subscriptions", "subscribe", "unsubscribe"]
|
||||
static var authorizedEndpoints = ["subscriptions", "subscribe", "unsubscribe", "user/playlists"]
|
||||
|
||||
@Published var account: Account!
|
||||
|
||||
@ -81,6 +81,10 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
return CommentsPage(comments: comments, nextPage: nextPage, disabled: disabled)
|
||||
}
|
||||
|
||||
configureTransformer(pathPattern("user/playlists")) { (content: Entity<JSON>) -> [Playlist] in
|
||||
content.json.arrayValue.map { self.extractUserPlaylist(from: $0)! }
|
||||
}
|
||||
|
||||
if account.token.isNil {
|
||||
updateToken()
|
||||
}
|
||||
@ -166,7 +170,9 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
|
||||
var home: Resource? { nil }
|
||||
var popular: Resource? { nil }
|
||||
var playlists: Resource? { nil }
|
||||
var playlists: Resource? {
|
||||
resource(baseURL: account.instance.apiURL, path: "user/playlists")
|
||||
}
|
||||
|
||||
func subscribe(_ channelID: String, onCompletion: @escaping () -> Void = {}) {
|
||||
resource(baseURL: account.instance.apiURL, path: "subscribe")
|
||||
@ -180,7 +186,10 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
.onCompletion { _ in onCompletion() }
|
||||
}
|
||||
|
||||
func playlist(_: String) -> Resource? { nil }
|
||||
func playlist(_ id: String) -> Resource? {
|
||||
channelPlaylist(id)
|
||||
}
|
||||
|
||||
func playlistVideo(_: String, _: String) -> Resource? { nil }
|
||||
func playlistVideos(_: String) -> Resource? { nil }
|
||||
|
||||
@ -359,6 +368,14 @@ final class PipedAPI: Service, ObservableObject, VideosAPI {
|
||||
)!
|
||||
}
|
||||
|
||||
private func extractUserPlaylist(from json: JSON) -> Playlist? {
|
||||
let id = json["id"].stringValue
|
||||
let title = json["name"].stringValue
|
||||
let visibility = Playlist.Visibility.private
|
||||
|
||||
return Playlist(id: id, title: title, visibility: visibility)
|
||||
}
|
||||
|
||||
private func extractDescription(from content: JSON) -> String? {
|
||||
guard var description = content.dictionaryValue["description"]?.string else {
|
||||
return nil
|
||||
|
@ -32,6 +32,10 @@ enum VideosApp: String, CaseIterable {
|
||||
}
|
||||
|
||||
var supportsUserPlaylists: Bool {
|
||||
true
|
||||
}
|
||||
|
||||
var userPlaylistsEndpointIncludesVideos: Bool {
|
||||
self == .invidious
|
||||
}
|
||||
|
||||
|
@ -18,11 +18,11 @@ struct Playlist: Identifiable, Equatable, Hashable {
|
||||
var title: String
|
||||
var visibility: Visibility
|
||||
|
||||
var updated: TimeInterval
|
||||
var updated: TimeInterval?
|
||||
|
||||
var videos = [Video]()
|
||||
|
||||
init(id: String, title: String, visibility: Visibility, updated: TimeInterval, videos: [Video] = []) {
|
||||
init(id: String, title: String, visibility: Visibility, updated: TimeInterval? = nil, videos: [Video] = []) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
self.visibility = visibility
|
||||
|
@ -11,9 +11,7 @@ struct AppSidebarPlaylists: View {
|
||||
NavigationLink(tag: TabSelection.playlist(playlist.id), selection: $navigation.tabSelection) {
|
||||
LazyView(PlaylistVideosView(playlist))
|
||||
} label: {
|
||||
Label(playlist.title, systemImage: RecentsModel.symbolSystemImage(playlist.title))
|
||||
.backport
|
||||
.badge(Text("\(playlist.videos.count)"))
|
||||
playlistLabel(playlist)
|
||||
}
|
||||
.id(playlist.id)
|
||||
.contextMenu {
|
||||
@ -34,6 +32,18 @@ struct AppSidebarPlaylists: View {
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder func playlistLabel(_ playlist: Playlist) -> some View {
|
||||
let label = Label(playlist.title, systemImage: RecentsModel.symbolSystemImage(playlist.title))
|
||||
|
||||
if player.accounts.app.userPlaylistsEndpointIncludesVideos {
|
||||
label
|
||||
.backport
|
||||
.badge(Text("\(playlist.videos.count)"))
|
||||
} else {
|
||||
label
|
||||
}
|
||||
}
|
||||
|
||||
var newPlaylistButton: some View {
|
||||
Button(action: { navigation.presentNewPlaylistForm() }) {
|
||||
Label("New Playlist", systemImage: "plus.circle")
|
||||
|
@ -104,7 +104,8 @@ struct PlaybackBar: View {
|
||||
}
|
||||
|
||||
guard let video = player.currentVideo,
|
||||
let time = player.time else {
|
||||
let time = player.time
|
||||
else {
|
||||
return ""
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,17 @@ struct PlaylistVideosView: View {
|
||||
@Environment(\.inNavigationView) private var inNavigationView
|
||||
@EnvironmentObject<PlayerModel> private var player
|
||||
|
||||
@StateObject private var store = Store<ChannelPlaylist>()
|
||||
|
||||
var contentItems: [ContentItem] {
|
||||
ContentItem.array(of: playlist.videos)
|
||||
ContentItem.array(of: playlist.videos.isEmpty ? (store.item?.videos ?? []) : playlist.videos)
|
||||
}
|
||||
|
||||
private var resource: Resource? {
|
||||
let resource = player.accounts.api.playlist(playlist.id)
|
||||
resource?.addObserver(store)
|
||||
|
||||
return resource
|
||||
}
|
||||
|
||||
var videos: [Video] {
|
||||
@ -22,6 +31,11 @@ struct PlaylistVideosView: View {
|
||||
var body: some View {
|
||||
PlayerControlsView {
|
||||
VerticalCells(items: contentItems)
|
||||
.onAppear {
|
||||
if !player.accounts.app.userPlaylistsEndpointIncludesVideos {
|
||||
resource?.loadIfNeeded()
|
||||
}
|
||||
}
|
||||
#if !os(tvOS)
|
||||
.navigationTitle("\(playlist.title) Playlist")
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user