108 lines
4.1 KiB
Swift
108 lines
4.1 KiB
Swift
import UIKit
|
||
import UniformTypeIdentifiers
|
||
|
||
final class ShareViewController: UIViewController {
|
||
private let statusLabel = UILabel()
|
||
|
||
override func viewDidLoad() {
|
||
super.viewDidLoad()
|
||
setupUI()
|
||
Task { await handleIncomingShare() }
|
||
}
|
||
|
||
private func setupUI() {
|
||
view.backgroundColor = .systemBackground
|
||
statusLabel.translatesAutoresizingMaskIntoConstraints = false
|
||
statusLabel.textAlignment = .center
|
||
statusLabel.numberOfLines = 0
|
||
statusLabel.text = "Paylaşılan bağlantı alınıyor..."
|
||
|
||
view.addSubview(statusLabel)
|
||
NSLayoutConstraint.activate([
|
||
statusLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
|
||
statusLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
|
||
statusLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor)
|
||
])
|
||
}
|
||
|
||
@MainActor
|
||
private func updateStatus(_ text: String) {
|
||
statusLabel.text = text
|
||
}
|
||
|
||
private func handleIncomingShare() async {
|
||
guard let item = extensionContext?.inputItems.first as? NSExtensionItem,
|
||
let providers = item.attachments else {
|
||
updateStatus("Paylaşılan içerik okunamadı.")
|
||
return
|
||
}
|
||
|
||
for provider in providers {
|
||
if let extracted = await extractURL(from: provider), isSupportedStreamingURL(extracted) {
|
||
SharedPayloadStore.saveIncomingURL(extracted.absoluteString)
|
||
updateStatus("Bağlantı alındı, uygulama açılıyor...")
|
||
openHostApp()
|
||
return
|
||
}
|
||
}
|
||
|
||
updateStatus("Geçerli bir Netflix/Prime Video linki bulunamadı.")
|
||
}
|
||
|
||
private func extractURL(from provider: NSItemProvider) async -> URL? {
|
||
if provider.hasItemConformingToTypeIdentifier(UTType.url.identifier) {
|
||
return await withCheckedContinuation { continuation in
|
||
provider.loadItem(forTypeIdentifier: UTType.url.identifier, options: nil) { item, _ in
|
||
continuation.resume(returning: item as? URL)
|
||
}
|
||
}
|
||
}
|
||
|
||
if provider.hasItemConformingToTypeIdentifier(UTType.text.identifier) {
|
||
return await withCheckedContinuation { continuation in
|
||
provider.loadItem(forTypeIdentifier: UTType.text.identifier, options: nil) { item, _ in
|
||
if let raw = item as? String, let url = URL(string: raw.trimmingCharacters(in: .whitespacesAndNewlines)) {
|
||
continuation.resume(returning: url)
|
||
return
|
||
}
|
||
continuation.resume(returning: nil)
|
||
}
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
private func isSupportedStreamingURL(_ url: URL) -> Bool {
|
||
let host = url.host?.lowercased() ?? ""
|
||
let netflixHosts = ["www.netflix.com", "netflix.com", "www.netflix.com.tr", "netflix.com.tr"]
|
||
let primeHosts = ["www.primevideo.com", "primevideo.com", "www.amazon.com", "amazon.com"]
|
||
|
||
let isNetflix = netflixHosts.contains(host)
|
||
let isPrime = primeHosts.contains(host)
|
||
guard isNetflix || isPrime else { return false }
|
||
|
||
let path = url.path.lowercased()
|
||
if path.contains("/title/") || path.contains("/watch/") || path.contains("/detail/") {
|
||
return true
|
||
}
|
||
|
||
// Some share links can be shortened/redirect style without a canonical path.
|
||
return !path.isEmpty && path != "/"
|
||
}
|
||
|
||
private func openHostApp() {
|
||
guard let url = URL(string: "\(SharedConfig.appURLScheme)://ingest") else {
|
||
extensionContext?.completeRequest(returningItems: nil)
|
||
return
|
||
}
|
||
|
||
extensionContext?.open(url) { success in
|
||
// If opening succeeded, the system should transition to the host app.
|
||
// Completing the extension request immediately can bounce back to the source app.
|
||
guard !success else { return }
|
||
self.extensionContext?.completeRequest(returningItems: nil)
|
||
}
|
||
}
|
||
}
|