mirror of
https://github.com/yattee/yattee.git
synced 2026-10-28 15:37:17 +00:00
Add an optional frontend URL for Piped instances used by browser actions
This commit is contained in:
@@ -16794,6 +16794,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"sources.field.frontendURL" : {
|
||||||
|
"comment" : "Field label for optional Piped web frontend URL",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Frontend URL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"sources.field.name" : {
|
"sources.field.name" : {
|
||||||
"comment" : "Field label for source name",
|
"comment" : "Field label for source name",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -17025,6 +17036,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"sources.footer.frontendURL" : {
|
||||||
|
"comment" : "Footer text explaining the optional Piped web frontend URL",
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Optional. Piped often serves its web frontend from a different address than the API. When set, it is used for opening this instance in the browser and copying its link. Leave empty to use the API URL."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"sources.footer.proxiesVideos" : {
|
"sources.footer.proxiesVideos" : {
|
||||||
"comment" : "Footer text explaining video proxy option",
|
"comment" : "Footer text explaining video proxy option",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|||||||
@@ -58,6 +58,11 @@ struct Instance: Identifiable, Codable, Hashable, Sendable {
|
|||||||
/// The base URL of the instance.
|
/// The base URL of the instance.
|
||||||
let url: URL
|
let url: URL
|
||||||
|
|
||||||
|
/// Optional URL of the web frontend, when it differs from the API URL
|
||||||
|
/// (Piped serves its API and frontend from separate hosts). Used for
|
||||||
|
/// browser-facing actions (open in browser, copy link) — never for API calls.
|
||||||
|
var frontendURL: URL?
|
||||||
|
|
||||||
/// Optional user-defined name for this instance.
|
/// Optional user-defined name for this instance.
|
||||||
var name: String?
|
var name: String?
|
||||||
|
|
||||||
@@ -102,6 +107,7 @@ struct Instance: Identifiable, Codable, Hashable, Sendable {
|
|||||||
id: UUID = UUID(),
|
id: UUID = UUID(),
|
||||||
type: InstanceType,
|
type: InstanceType,
|
||||||
url: URL,
|
url: URL,
|
||||||
|
frontendURL: URL? = nil,
|
||||||
name: String? = nil,
|
name: String? = nil,
|
||||||
isEnabled: Bool = true,
|
isEnabled: Bool = true,
|
||||||
dateAdded: Date = Date(),
|
dateAdded: Date = Date(),
|
||||||
@@ -113,6 +119,7 @@ struct Instance: Identifiable, Codable, Hashable, Sendable {
|
|||||||
self.id = id
|
self.id = id
|
||||||
self.type = type
|
self.type = type
|
||||||
self.url = url
|
self.url = url
|
||||||
|
self.frontendURL = frontendURL
|
||||||
self.name = name
|
self.name = name
|
||||||
self.isEnabled = isEnabled
|
self.isEnabled = isEnabled
|
||||||
self.dateAdded = dateAdded
|
self.dateAdded = dateAdded
|
||||||
@@ -127,6 +134,7 @@ struct Instance: Identifiable, Codable, Hashable, Sendable {
|
|||||||
id = try container.decode(UUID.self, forKey: .id)
|
id = try container.decode(UUID.self, forKey: .id)
|
||||||
type = try container.decode(InstanceType.self, forKey: .type)
|
type = try container.decode(InstanceType.self, forKey: .type)
|
||||||
url = try container.decode(URL.self, forKey: .url)
|
url = try container.decode(URL.self, forKey: .url)
|
||||||
|
frontendURL = try container.decodeIfPresent(URL.self, forKey: .frontendURL)
|
||||||
name = try container.decodeIfPresent(String.self, forKey: .name)
|
name = try container.decodeIfPresent(String.self, forKey: .name)
|
||||||
isEnabled = try container.decode(Bool.self, forKey: .isEnabled)
|
isEnabled = try container.decode(Bool.self, forKey: .isEnabled)
|
||||||
dateAdded = try container.decode(Date.self, forKey: .dateAdded)
|
dateAdded = try container.decode(Date.self, forKey: .dateAdded)
|
||||||
@@ -160,6 +168,12 @@ struct Instance: Identifiable, Codable, Hashable, Sendable {
|
|||||||
return components.url?.absoluteString ?? url.absoluteString
|
return components.url?.absoluteString ?? url.absoluteString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The URL browser-facing actions should use: the web frontend when one is
|
||||||
|
/// configured, else the API URL.
|
||||||
|
var browseURL: URL {
|
||||||
|
frontendURL ?? url
|
||||||
|
}
|
||||||
|
|
||||||
var contentSource: ContentSource {
|
var contentSource: ContentSource {
|
||||||
type.contentSource(for: url)
|
type.contentSource(for: url)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ struct InstanceBrowseView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
openURL(instance.url)
|
openURL(instance.browseURL)
|
||||||
} label: {
|
} label: {
|
||||||
Label(String(localized: "instance.browse.openInBrowser"), systemImage: "safari")
|
Label(String(localized: "instance.browse.openInBrowser"), systemImage: "safari")
|
||||||
}
|
}
|
||||||
@@ -257,10 +257,10 @@ struct InstanceBrowseView: View {
|
|||||||
|
|
||||||
private func copyInstanceURL() {
|
private func copyInstanceURL() {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
UIPasteboard.general.string = instance.url.absoluteString
|
UIPasteboard.general.string = instance.browseURL.absoluteString
|
||||||
#elseif os(macOS)
|
#elseif os(macOS)
|
||||||
NSPasteboard.general.clearContents()
|
NSPasteboard.general.clearContents()
|
||||||
NSPasteboard.general.setString(instance.url.absoluteString, forType: .string)
|
NSPasteboard.general.setString(instance.browseURL.absoluteString, forType: .string)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ private struct EditRemoteServerContent: View {
|
|||||||
@State private var allowInvalidCertificates: Bool
|
@State private var allowInvalidCertificates: Bool
|
||||||
@State private var proxiesVideos: Bool
|
@State private var proxiesVideos: Bool
|
||||||
|
|
||||||
|
// Piped serves its API and web frontend from separate hosts; browser-facing
|
||||||
|
// actions (open in browser, copy link) use this URL when set.
|
||||||
|
@State private var frontendURLString: String
|
||||||
|
|
||||||
// HTTP Basic Auth credentials (for any instance type behind a reverse proxy;
|
// HTTP Basic Auth credentials (for any instance type behind a reverse proxy;
|
||||||
// required for Yattee Server, optional for Invidious/Piped/PeerTube).
|
// required for Yattee Server, optional for Invidious/Piped/PeerTube).
|
||||||
@State private var basicAuthUsername: String = ""
|
@State private var basicAuthUsername: String = ""
|
||||||
@@ -66,6 +70,7 @@ private struct EditRemoteServerContent: View {
|
|||||||
_isEnabled = State(initialValue: instance.isEnabled)
|
_isEnabled = State(initialValue: instance.isEnabled)
|
||||||
_allowInvalidCertificates = State(initialValue: instance.allowInvalidCertificates)
|
_allowInvalidCertificates = State(initialValue: instance.allowInvalidCertificates)
|
||||||
_proxiesVideos = State(initialValue: instance.proxiesVideos)
|
_proxiesVideos = State(initialValue: instance.proxiesVideos)
|
||||||
|
_frontendURLString = State(initialValue: instance.frontendURL?.absoluteString ?? "")
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
@@ -121,6 +126,27 @@ private struct EditRemoteServerContent: View {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if instance.type == .piped {
|
||||||
|
Section {
|
||||||
|
#if os(tvOS)
|
||||||
|
TVSettingsTextField(title: String(localized: "sources.field.frontendURL"), text: $frontendURLString)
|
||||||
|
#elseif os(macOS)
|
||||||
|
LabeledContent(String(localized: "sources.field.frontendURL")) {
|
||||||
|
TextField("", text: $frontendURLString)
|
||||||
|
.autocorrectionDisabled()
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
TextField(String(localized: "sources.field.frontendURL"), text: $frontendURLString)
|
||||||
|
.textContentType(.URL)
|
||||||
|
.keyboardType(.URL)
|
||||||
|
.textInputAutocapitalization(.never)
|
||||||
|
.autocorrectionDisabled()
|
||||||
|
#endif
|
||||||
|
} footer: {
|
||||||
|
Text(String(localized: "sources.footer.frontendURL"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if instance.type == .yatteeServer {
|
if instance.type == .yatteeServer {
|
||||||
// Server Info Section
|
// Server Info Section
|
||||||
Section {
|
Section {
|
||||||
@@ -465,6 +491,11 @@ private struct EditRemoteServerContent: View {
|
|||||||
updated.allowInvalidCertificates = allowInvalidCertificates
|
updated.allowInvalidCertificates = allowInvalidCertificates
|
||||||
updated.proxiesVideos = proxiesVideos
|
updated.proxiesVideos = proxiesVideos
|
||||||
|
|
||||||
|
if instance.type == .piped {
|
||||||
|
let trimmed = frontendURLString.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
updated.frontendURL = trimmed.isEmpty ? nil : Instance.normalizeSourceURL(trimmed)
|
||||||
|
}
|
||||||
|
|
||||||
// Save / clear HTTP Basic Auth credentials. Yattee Server never auto-clears
|
// Save / clear HTTP Basic Auth credentials. Yattee Server never auto-clears
|
||||||
// (credentials are required and the user re-enters to overwrite). Piped never
|
// (credentials are required and the user re-enters to overwrite). Piped never
|
||||||
// persists them — its session token reuses the same Authorization header.
|
// persists them — its session token reuses the same Authorization header.
|
||||||
|
|||||||
Reference in New Issue
Block a user