mirror of
https://github.com/yattee/yattee.git
synced 2025-08-09 04:04:07 +00:00
Open videos via URL scheme
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
//
|
||||
// AppDelegate.swift
|
||||
// Pearvidious
|
||||
//
|
||||
// Created by Arkadiusz Fal on 20/10/2021.
|
||||
//
|
||||
|
||||
import Foundation
|
@@ -4,16 +4,20 @@ import Foundation
|
||||
extension Defaults.Keys {
|
||||
static let invidiousInstanceID = "default-invidious-instance"
|
||||
static let pipedInstanceID = "default-piped-instance"
|
||||
static let privateAccountID = "default-private-invidious-account"
|
||||
|
||||
static let instances = Key<[Instance]>("instances", default: [
|
||||
.init(app: .piped, id: pipedInstanceID, name: "Public", url: "https://pipedapi.kavin.rocks"),
|
||||
.init(app: .invidious, id: invidiousInstanceID, name: "Private", url: "https://invidious.home.arekf.net")
|
||||
])
|
||||
static let accounts = Key<[Account]>("accounts", default: [
|
||||
.init(instanceID: invidiousInstanceID,
|
||||
name: "arekf",
|
||||
url: "https://invidious.home.arekf.net",
|
||||
sid: "ki55SJbaQmm0bOxUWctGAQLYPQRgk-CXDPw5Dp4oBmI=")
|
||||
.init(
|
||||
id: privateAccountID,
|
||||
instanceID: invidiousInstanceID,
|
||||
name: "arekf",
|
||||
url: "https://invidious.home.arekf.net",
|
||||
sid: "ki55SJbaQmm0bOxUWctGAQLYPQRgk-CXDPw5Dp4oBmI="
|
||||
)
|
||||
])
|
||||
static let lastAccountID = Key<Account.ID?>("lastAccountID")
|
||||
static let lastInstanceID = Key<Instance.ID?>("lastInstanceID")
|
||||
|
@@ -34,6 +34,10 @@ struct ContentView: View {
|
||||
#endif
|
||||
}
|
||||
.onAppear(perform: configure)
|
||||
|
||||
.handlesExternalEvents(preferring: Set(["*"]), allowing: Set(["*"]))
|
||||
.onOpenURL(perform: handleOpenedURL)
|
||||
|
||||
.environmentObject(accounts)
|
||||
.environmentObject(instances)
|
||||
.environmentObject(navigation)
|
||||
@@ -113,6 +117,25 @@ struct ContentView: View {
|
||||
|
||||
navigation.presentingWelcomeScreen = true
|
||||
}
|
||||
|
||||
func handleOpenedURL(_ url: URL) {
|
||||
guard !accounts.current.isNil else {
|
||||
return
|
||||
}
|
||||
|
||||
let parser = VideoURLParser(url: url)
|
||||
|
||||
guard let id = parser.id else {
|
||||
return
|
||||
}
|
||||
|
||||
accounts.api.video(id).load().onSuccess { response in
|
||||
if let video: Video = response.typedContent() {
|
||||
self.player.playNow(video, at: parser.time)
|
||||
self.player.presentPlayer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
|
@@ -3,10 +3,15 @@ import SwiftUI
|
||||
|
||||
@main
|
||||
struct PearvidiousApp: App {
|
||||
#if os(macOS)
|
||||
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
||||
#endif
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
}
|
||||
.handlesExternalEvents(matching: Set(["*"]))
|
||||
#if !os(tvOS)
|
||||
.commands {
|
||||
SidebarCommands()
|
||||
|
78
Shared/VideoURLParser.swift
Normal file
78
Shared/VideoURLParser.swift
Normal file
@@ -0,0 +1,78 @@
|
||||
import Foundation
|
||||
|
||||
struct VideoURLParser {
|
||||
let url: URL
|
||||
|
||||
var id: String? {
|
||||
if urlComponents?.host == "youtu.be", let path = urlComponents?.path {
|
||||
return String(path.suffix(from: path.index(path.startIndex, offsetBy: 1)))
|
||||
}
|
||||
|
||||
return queryItemValue("v")
|
||||
}
|
||||
|
||||
var time: TimeInterval? {
|
||||
guard let time = queryItemValue("t") else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let timeComponents = parseTime(time)
|
||||
|
||||
guard !timeComponents.isEmpty,
|
||||
let hours = TimeInterval(timeComponents["hours"] ?? "0"),
|
||||
let minutes = TimeInterval(timeComponents["minutes"] ?? "0"),
|
||||
let seconds = TimeInterval(timeComponents["seconds"] ?? "0")
|
||||
else {
|
||||
if let time = TimeInterval(time) {
|
||||
return time
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return seconds + (minutes * 60) + (hours * 60 * 60)
|
||||
}
|
||||
|
||||
func queryItemValue(_ name: String) -> String? {
|
||||
queryItems.first { $0.name == name }?.value
|
||||
}
|
||||
|
||||
private var queryItems: [URLQueryItem] {
|
||||
urlComponents?.queryItems ?? []
|
||||
}
|
||||
|
||||
private var urlComponents: URLComponents? {
|
||||
URLComponents(url: url, resolvingAgainstBaseURL: false)
|
||||
}
|
||||
|
||||
private func parseTime(_ time: String) -> [String: String] {
|
||||
let results = timeRegularExpression.matches(
|
||||
in: time,
|
||||
range: NSRange(time.startIndex..., in: time)
|
||||
)
|
||||
|
||||
guard let match = results.first else {
|
||||
return [:]
|
||||
}
|
||||
|
||||
var components: [String: String] = [:]
|
||||
|
||||
for name in ["hours", "minutes", "seconds"] {
|
||||
let matchRange = match.range(withName: name)
|
||||
|
||||
if let substringRange = Range(matchRange, in: time) {
|
||||
let capture = String(time[substringRange])
|
||||
components[name] = capture
|
||||
}
|
||||
}
|
||||
|
||||
return components
|
||||
}
|
||||
|
||||
private var timeRegularExpression: NSRegularExpression {
|
||||
try! NSRegularExpression(
|
||||
pattern: "(?:(?<hours>[0-9+])+h)?(?:(?<minutes>[0-9]+)m)?(?:(?<seconds>[0-9]*)s)?",
|
||||
options: .caseInsensitive
|
||||
)
|
||||
}
|
||||
}
|
@@ -31,7 +31,6 @@ struct HorizontalCells: View {
|
||||
.padding(.vertical, 10)
|
||||
#endif
|
||||
}
|
||||
.id(items.map(\.id).joined())
|
||||
#if os(tvOS)
|
||||
.frame(height: 560)
|
||||
#else
|
||||
|
@@ -17,7 +17,6 @@ struct VerticalCells: View {
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.id(items.map(\.id).joined())
|
||||
.edgesIgnoringSafeArea(.horizontal)
|
||||
#if os(macOS)
|
||||
.background()
|
||||
|
Reference in New Issue
Block a user