如何检测非.mp4结尾的视频URL并从间接链接提取下载视频
Got it, let's break down how to solve your two key challenges: handling indirect video URLs (like YouTube page links) and detecting video URLs that don’t end with .mp4. Here’s a practical, step-by-step approach tailored to your Swift codebase:
Indirect URLs point to webpages, not direct video files. To extract the underlying video source, you’ll need to fetch and parse the webpage’s content. Keep in mind: platforms like YouTube have anti-scraping measures and strict terms of service—always prioritize official APIs if available.
Core Workflow:
- Fetch the webpage’s HTML content
- Parse the HTML to locate embedded video sources
- Validate and select a usable video URL
Example Implementation (with SwiftSoup for HTML parsing):
First, add SwiftSoup to your project (via CocoaPods or Swift Package Manager) for easier HTML parsing. Then update your code:
import UIKit import SwiftSoup class VideoDownloader: UIViewController { @IBAction func downloadButtonAction(_ sender: Any) { // Test with a YouTube page link or direct video URL let inputURLString = "https://www.youtube.com/watch?v=example" processInputURL(inputURLString) } // Route input URL to direct video handling or webpage extraction func processInputURL(_ urlString: String) { guard let inputURL = URL(string: urlString) else { print("Invalid URL provided") return } checkIfDirectVideo(inputURL) { isDirect, videoURL in if isDirect, let validVideoURL = videoURL { self.downloadVideoLinkAndCreateAsset(validVideoURL.absoluteString) } else { self.extractVideoFromWebpage(inputURL) { extractedURL in guard let videoURL = extractedURL else { print("Failed to pull video from webpage") return } self.downloadVideoLinkAndCreateAsset(videoURL.absoluteString) } } } } // Check if URL points directly to a video (via HTTP HEAD request) func checkIfDirectVideo(_ url: URL, completion: @escaping (Bool, URL?) -> Void) { var request = URLRequest(url: url) request.httpMethod = "HEAD" URLSession.shared.dataTask(with: request) { _, response, error in guard let httpResponse = response as? HTTPURLResponse, error == nil else { completion(false, nil) return } if let contentType = httpResponse.allHeaderFields["Content-Type"] as? String { let isVideo = contentType.starts(with: "video/") completion(isVideo, isVideo ? url : nil) } else { completion(false, nil) } }.resume() } // Extract video source from webpage HTML func extractVideoFromWebpage(_ webpageURL: URL, completion: @escaping (URL?) -> Void) { URLSession.shared.dataTask(with: webpageURL) { data, _, error in guard let data = data, error == nil else { completion(nil) return } do { let htmlString = String(data: data, encoding: .utf8)! let doc = try SwiftSoup.parse(htmlString) // Look for direct video sources in <video> or <source> tags if let videoSrc = try doc.select("video").attr("src").nonEmpty { completion(URL(string: videoSrc)) } else if let sourceSrc = try doc.select("source[type^=video/]").attr("src").nonEmpty { completion(URL(string: sourceSrc)) } else { // For dynamic sites (like YouTube), static parsing won't work—use official APIs instead print("No static video source found. For platforms like YouTube, use their official Data API.") completion(nil) } } catch { print("HTML parsing error: \(error.localizedDescription)") completion(nil) } }.resume() } // Your existing download function, updated to handle non-.mp4 formats func downloadVideoLinkAndCreateAsset(_ videoLink: String) { guard let videoURL = URL(string: videoLink) else { return } guard let docsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return } URLSession.shared.downloadTask(with: videoURL) { tempURL, response, error in guard let tempURL = tempURL, error == nil else { print("Download failed: \(error?.localizedDescription ?? "Unknown error")") return } do { // Set correct file extension based on content type let fileExt = self.getFileExtension(from: response) let finalURL = docsDir.appendingPathComponent("\(UUID().uuidString).\(fileExt)") try FileManager.default.moveItem(at: tempURL, to: finalURL) print("Video saved to: \(finalURL)") } catch { print("Failed to save video: \(error.localizedDescription)") } }.resume() } // Helper to get file extension from HTTP response content type func getFileExtension(from response: URLResponse?) -> String { guard let httpResponse = response as? HTTPURLResponse, let contentType = httpResponse.allHeaderFields["Content-Type"] as? String else { return "mp4" // Fallback } switch contentType { case "video/mp4": return "mp4" case "video/webm": return "webm" case "video/quicktime": return "mov" case "video/x-matroska": return "mkv" default: return "mp4" } } } // Small extension to check for non-empty strings extension String { var nonEmpty: String? { let trimmed = self.trimmingCharacters(in: .whitespacesAndNewlines) return trimmed.isEmpty ? nil : trimmed } }
Many video URLs don’t end with .mp4 (e.g., https://example.com/video?id=123 or streaming formats like .m3u8). Use these methods to validate them:
- Check HTTP Content-Type: Send a HEAD request to verify the response’s
Content-Typestarts withvideo/ - Parse URL patterns: Use regex to match common video extensions (even if hidden in query parameters)
- Use response metadata: After fetching the resource, confirm it’s a video via its file signature or metadata
Key Notes:
- For dynamic sites (like YouTube) that load videos via JavaScript, static HTML parsing won’t work. Use platform-specific APIs (e.g., YouTube Data API) to get legitimate video stream URLs.
- Always comply with a website’s terms of service—unauthorized scraping can lead to legal issues or blocked access.
内容的提问来源于stack exchange,提问作者ThatMan

