mirror of
https://github.com/yattee/yattee.git
synced 2025-04-27 15:30:33 +05:30
Improve sharing, add YouTube links
This commit is contained in:
parent
0091af683f
commit
f49453e871
@ -1,3 +1,4 @@
|
|||||||
|
import AVFoundation
|
||||||
import Foundation
|
import Foundation
|
||||||
import Siesta
|
import Siesta
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ protocol VideosAPI {
|
|||||||
func channelPlaylist(_ id: String) -> Resource?
|
func channelPlaylist(_ id: String) -> Resource?
|
||||||
|
|
||||||
func loadDetails(_ item: PlayerQueueItem, completionHandler: @escaping (PlayerQueueItem) -> Void)
|
func loadDetails(_ item: PlayerQueueItem, completionHandler: @escaping (PlayerQueueItem) -> Void)
|
||||||
func shareURL(_ item: ContentItem) -> URL?
|
func shareURL(_ item: ContentItem, frontendHost: String?, time: CMTime?) -> URL?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension VideosAPI {
|
extension VideosAPI {
|
||||||
@ -50,23 +51,33 @@ extension VideosAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func shareURL(_ item: ContentItem) -> URL? {
|
func shareURL(_ item: ContentItem, frontendHost: String? = nil, time: CMTime? = nil) -> URL? {
|
||||||
guard let frontendHost = account.instance.frontendHost else {
|
guard let frontendHost = frontendHost ?? account.instance.frontendHost else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var urlComponents = account.instance.urlComponents
|
var urlComponents = account.instance.urlComponents
|
||||||
urlComponents.host = frontendHost
|
urlComponents.host = frontendHost
|
||||||
|
|
||||||
|
var queryItems = [URLQueryItem]()
|
||||||
|
|
||||||
switch item.contentType {
|
switch item.contentType {
|
||||||
case .video:
|
case .video:
|
||||||
urlComponents.path = "/watch"
|
urlComponents.path = "/watch"
|
||||||
urlComponents.query = "v=\(item.video.videoID)"
|
queryItems.append(.init(name: "v", value: item.video.videoID))
|
||||||
case .channel:
|
case .channel:
|
||||||
urlComponents.path = "/channel/\(item.channel.id)"
|
urlComponents.path = "/channel/\(item.channel.id)"
|
||||||
case .playlist:
|
case .playlist:
|
||||||
urlComponents.path = "/playlist"
|
urlComponents.path = "/playlist"
|
||||||
urlComponents.query = "list=\(item.playlist.id)"
|
queryItems.append(.init(name: "list", value: item.playlist.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !time.isNil, time!.seconds.isFinite {
|
||||||
|
queryItems.append(.init(name: "t", value: "\(Int(time!.seconds))s"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !queryItems.isEmpty {
|
||||||
|
urlComponents.queryItems = queryItems
|
||||||
}
|
}
|
||||||
|
|
||||||
return urlComponents.url!
|
return urlComponents.url!
|
||||||
|
@ -381,7 +381,10 @@ final class PlayerModel: ObservableObject {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.updateNowPlayingInfo()
|
#if !os(tvOS)
|
||||||
|
self.updateNowPlayingInfo()
|
||||||
|
#endif
|
||||||
|
|
||||||
self.handleSegments(at: self.player.currentTime())
|
self.handleSegments(at: self.player.currentTime())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ struct FavoriteItemView: View {
|
|||||||
|
|
||||||
var label: String {
|
var label: String {
|
||||||
if case let .playlist(id) = item.section {
|
if case let .playlist(id) = item.section {
|
||||||
return playlistsModel.find(id: id)?.title ?? "Unknown Playlist"
|
return playlistsModel.find(id: id)?.title ?? "Playlist"
|
||||||
}
|
}
|
||||||
|
|
||||||
return item.section.label
|
return item.section.label
|
||||||
|
@ -13,6 +13,7 @@ struct VideoDetails: View {
|
|||||||
@State private var confirmationShown = false
|
@State private var confirmationShown = false
|
||||||
@State private var presentingAddToPlaylist = false
|
@State private var presentingAddToPlaylist = false
|
||||||
@State private var presentingShareSheet = false
|
@State private var presentingShareSheet = false
|
||||||
|
@State private var shareURL = ""
|
||||||
|
|
||||||
@State private var currentPage = Page.details
|
@State private var currentPage = Page.details
|
||||||
|
|
||||||
@ -254,7 +255,8 @@ struct VideoDetails: View {
|
|||||||
HStack {
|
HStack {
|
||||||
ShareButton(
|
ShareButton(
|
||||||
contentItem: ContentItem(video: video),
|
contentItem: ContentItem(video: video),
|
||||||
presentingShareSheet: $presentingShareSheet
|
presentingShareSheet: $presentingShareSheet,
|
||||||
|
shareURL: $shareURL
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
@ -286,7 +288,6 @@ struct VideoDetails: View {
|
|||||||
.help("Add to Playlist...")
|
.help("Add to Playlist...")
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
.foregroundColor(.blue)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(maxHeight: 35)
|
.frame(maxHeight: 35)
|
||||||
@ -300,9 +301,7 @@ struct VideoDetails: View {
|
|||||||
}
|
}
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.sheet(isPresented: $presentingShareSheet) {
|
.sheet(isPresented: $presentingShareSheet) {
|
||||||
if let url = accounts.api.shareURL(contentItem) {
|
ShareSheet(activityItems: [shareURL])
|
||||||
ShareSheet(activityItems: [url])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ struct ChannelPlaylistView: View {
|
|||||||
}
|
}
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: shareButtonPlacement) {
|
ToolbarItem(placement: .navigation) {
|
||||||
ShareButton(
|
ShareButton(
|
||||||
contentItem: contentItem,
|
contentItem: contentItem,
|
||||||
presentingShareSheet: $presentingShareSheet
|
presentingShareSheet: $presentingShareSheet
|
||||||
@ -84,14 +84,6 @@ struct ChannelPlaylistView: View {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private var shareButtonPlacement: ToolbarItemPlacement {
|
|
||||||
#if os(iOS)
|
|
||||||
.navigation
|
|
||||||
#else
|
|
||||||
.automatic
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
private var contentItem: ContentItem {
|
private var contentItem: ContentItem {
|
||||||
ContentItem(playlist: playlist)
|
ContentItem(playlist: playlist)
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ struct ChannelVideosView: View {
|
|||||||
#endif
|
#endif
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: shareButtonPlacement) {
|
ToolbarItem(placement: .navigation) {
|
||||||
ShareButton(
|
ShareButton(
|
||||||
contentItem: contentItem,
|
contentItem: contentItem,
|
||||||
presentingShareSheet: $presentingShareSheet
|
presentingShareSheet: $presentingShareSheet
|
||||||
@ -140,14 +140,6 @@ struct ChannelVideosView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var shareButtonPlacement: ToolbarItemPlacement {
|
|
||||||
#if os(iOS)
|
|
||||||
.navigation
|
|
||||||
#else
|
|
||||||
.automatic
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
private var contentItem: ContentItem {
|
private var contentItem: ContentItem {
|
||||||
ContentItem(channel: channel)
|
ContentItem(channel: channel)
|
||||||
}
|
}
|
||||||
|
@ -3,43 +3,105 @@ import SwiftUI
|
|||||||
struct ShareButton: View {
|
struct ShareButton: View {
|
||||||
let contentItem: ContentItem
|
let contentItem: ContentItem
|
||||||
@Binding var presentingShareSheet: Bool
|
@Binding var presentingShareSheet: Bool
|
||||||
|
@Binding var shareURL: String
|
||||||
|
|
||||||
@EnvironmentObject<AccountsModel> private var accounts
|
@EnvironmentObject<AccountsModel> private var accounts
|
||||||
|
@EnvironmentObject<PlayerModel> private var player
|
||||||
|
|
||||||
|
init(
|
||||||
|
contentItem: ContentItem,
|
||||||
|
presentingShareSheet: Binding<Bool>,
|
||||||
|
shareURL: Binding<String>? = nil
|
||||||
|
) {
|
||||||
|
self.contentItem = contentItem
|
||||||
|
_presentingShareSheet = presentingShareSheet
|
||||||
|
_shareURL = shareURL ?? .constant("")
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Group {
|
Menu {
|
||||||
if let url = shareURL {
|
instanceActions
|
||||||
Button {
|
youtubeActions
|
||||||
#if os(iOS)
|
} label: {
|
||||||
presentingShareSheet = true
|
Label("Share", systemImage: "square.and.arrow.up")
|
||||||
#else
|
|
||||||
NSPasteboard.general.clearContents()
|
|
||||||
NSPasteboard.general.setString(url, forType: .string)
|
|
||||||
#endif
|
|
||||||
} label: {
|
|
||||||
#if os(iOS)
|
|
||||||
Label("Share", systemImage: "square.and.arrow.up")
|
|
||||||
#else
|
|
||||||
EmptyView()
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
.keyboardShortcut("c")
|
|
||||||
.foregroundColor(.blue)
|
|
||||||
.buttonStyle(.plain)
|
|
||||||
.labelStyle(.iconOnly)
|
.labelStyle(.iconOnly)
|
||||||
} else {
|
}
|
||||||
EmptyView()
|
.menuStyle(.borderlessButton)
|
||||||
|
#if os(macOS)
|
||||||
|
.frame(maxWidth: 35)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private var instanceActions: some View {
|
||||||
|
Group {
|
||||||
|
if let url = accounts.api.shareURL(contentItem) {
|
||||||
|
Button(labelForShareURL(accounts.app.name)) {
|
||||||
|
shareAction(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
if contentItem.contentType == .video {
|
||||||
|
Button(labelForShareURL(accounts.app.name, withTime: true)) {
|
||||||
|
shareAction(
|
||||||
|
accounts.api.shareURL(
|
||||||
|
contentItem,
|
||||||
|
time: player.player.currentTime()
|
||||||
|
)!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var shareURL: String? {
|
private var youtubeActions: some View {
|
||||||
accounts.api.shareURL(contentItem)?.absoluteString
|
Group {
|
||||||
|
if let url = accounts.api.shareURL(contentItem, frontendHost: "www.youtube.com") {
|
||||||
|
Button(labelForShareURL("YouTube")) {
|
||||||
|
shareAction(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
if contentItem.contentType == .video {
|
||||||
|
Button(labelForShareURL("YouTube", withTime: true)) {
|
||||||
|
shareAction(
|
||||||
|
accounts.api.shareURL(
|
||||||
|
contentItem,
|
||||||
|
frontendHost: "www.youtube.com",
|
||||||
|
time: player.player.currentTime()
|
||||||
|
)!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func shareAction(_ url: URL) {
|
||||||
|
#if os(macOS)
|
||||||
|
NSPasteboard.general.clearContents()
|
||||||
|
NSPasteboard.general.setString(url.absoluteString, forType: .string)
|
||||||
|
#else
|
||||||
|
shareURL = url.absoluteString
|
||||||
|
presentingShareSheet = true
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private func labelForShareURL(_ app: String, withTime: Bool = false) -> String {
|
||||||
|
let time = withTime ? "with time" : ""
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
return "Copy \(app) link \(time)"
|
||||||
|
#else
|
||||||
|
return "Share \(app) link \(time)"
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ShareButton_Previews: PreviewProvider {
|
struct ShareButton_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
ShareButton(contentItem: ContentItem(video: Video.fixture), presentingShareSheet: .constant(false))
|
ShareButton(
|
||||||
|
contentItem: ContentItem(video: Video.fixture),
|
||||||
|
presentingShareSheet: .constant(false)
|
||||||
|
)
|
||||||
|
.injectFixtureEnvironmentObjects()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ struct EditFavorites: View {
|
|||||||
|
|
||||||
func label(_ item: FavoriteItem) -> String {
|
func label(_ item: FavoriteItem) -> String {
|
||||||
if case let .playlist(id) = item.section {
|
if case let .playlist(id) = item.section {
|
||||||
return playlistsModel.find(id: id)?.title ?? "Unknown Playlist"
|
return playlistsModel.find(id: id)?.title ?? "Playlist"
|
||||||
}
|
}
|
||||||
|
|
||||||
return item.section.label
|
return item.section.label
|
||||||
|
Loading…
x
Reference in New Issue
Block a user