从URL中获取Youtube视频ID - Swift3

11

我有一个Youtube的URL字符串,希望从中提取视频ID。在Objective-C中,我找到了以下代码:

NSError *error = NULL;
NSRegularExpression *regex = 
[NSRegularExpression regularExpressionWithPattern:@"?.*v=([^&]+)"
                                          options:NSRegularExpressionCaseInsensitive
                                            error:&error];
NSTextCheckingResult *match = [regex firstMatchInString:youtubeURL
                                                options:0
                                                  range:NSMakeRange(0, [youtubeURL length])];
if (match) {
    NSRange videoIDRange = [match rangeAtIndex:1];
    NSString *substringForFirstMatch = [youtubeURL substringWithRange:videoIDRange];
}

当我把这段代码转换成Swift 3时,就是这样:

var error: Error? = nil
var regex = try! NSRegularExpression(pattern: "?.*v=([^&]+)", options: .caseInsensitive)
var match = regex!.firstMatch(in: youtubeURL, options: [], range: NSRange(location: 0, length: youtubeURL.length))!
if match {
    var videoIDRange = match.rangeAt(1)
    var substringForFirstMatch = (youtubeURL as NSString).substring(with: videoIDRange)
}

出现错误:

致命错误:'try!'表达式意外地引发了一个错误:错误域=NSCocoaErrorDomain Code=2048 "The value “?.*v=([^&]+)” is invalid."

有人能帮我解决这个错误,或者有人能够解释如何在Swift 3中从URL获取视频ID吗。

谢谢提前。

9个回答

47

更安全的版本(不使用强制解包!):

extension String {
    var youtubeID: String? {
        let pattern = "((?<=(v|V)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=embed/)|(?<=shorts/))([\\w-]++)"
        
        let regex = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive)
        let range = NSRange(location: 0, length: count)

        guard let result = regex?.firstMatch(in: self, range: range) else {
            return nil
        }
        
        return (self as NSString).substring(with: result.range)
    }
}

示例:

"https://www.youtube.com/watch?v=C0DPdy98e4c".youtubeID // "C0DPdy98e4c"
"https://youtube.com/watch?v=C0DPdy98e4c".youtubeID // "C0DPdy98e4c"
"www.youtube.com/watch?v=C0DPdy98e4c".youtubeID // "C0DPdy98e4c"
"youtube.com/watch?v=C0DPdy98e4c".youtubeID // "C0DPdy98e4c"
"youtube.com/shorts/C0DPdy98e4c".youtubeID // "C0DPdy98e4c"
"youtube.com/embed/C0DPdy98e4c".youtubeID // "C0DPdy98e4c"

"https://youtu.be/C0DPdy98e4c".youtubeID // "C0DPdy98e4c"
"youtu.be/C0DPdy98e4c".youtubeID // "C0DPdy98e4c"

版权归属:Usman Nisar的回答


1
非常好。对我来说运行得很顺畅。 - zumzum
这似乎是可以工作的,但传递https://www.twitch.tv/directory/all/tags/6ea6bca4-4712-4ab9-a906-e3336a9d8039似乎会将directory作为videoID返回。 - sudoExclaimationExclaimation
你能否添加对shorts的支持?它是https://www.youtube.com/shorts/xxxxxx。 - Janneman
@Janneman 应该已经完成了。 - Islam

21

我有一种使用URLComponents的不同方法来完成这个任务。如果存在,您只需从URL中选择“v”参数。

func getYoutubeId(youtubeUrl: String) -> String? {
    return URLComponents(string: youtubeUrl)?.queryItems?.first(where: { $0.name == "v" })?.value
}

然后像这样传入YouTube网址:

print (getYoutubeId(youtubeUrl: "https://www.youtube.com/watch?v=Y7ojcTR78qE&spfreload=9"))

单行代码 return URLComponents(string: youtubeUrl)?.queryItems?.first(where: { $0.name == "v" })?.value - Leo Dabus
1
你会如何处理短链接?(例如:"https://youtu.be/sWx8TtRBOfk") - Islam
@IslamQ。这将起作用:URLComponents(string:“http://youtu.be/sWx8TtRBOfk”)?.path.replacingOccurrences(of:“/”,with:“”) - totiDev
1
@IslamQ.,请查看这个答案以解决您的问题。 - Hemang

6

Swift 5

var youtubeURLs = [
    "http://www.youtube.com/watch?v=-wtIMTCHWuI",
    "http://www.youtube.com/v/-wtIMTCHWuI?version=3&autohide=1",
    "http://youtu.be/-wtIMTCHWuI",
    "http://www.youtube.com/oembed?url=http%3A//www.youtube.com/watch?v%3D-wtIMTCHWuI&format=json",
    "https://youtu.be/uJ2PZaO1N5E",
    "https://www.youtube.com/embed/M7lc1UVf-VE",
    "http://www.youtube.com/attribution_link?a=JdfC0C9V6ZI&u=%2Fwatch%3Fv%3DEhxJLojIE_o%26feature%3Dshare",
    "https://www.youtube.com/attribution_link?a=8g8kPrPIi-ecwIsS&u=/watch%3Fv%3DyZv2daTWRZU%26feature%3Dem-uploademail"
]

func getVideoID(from urlString: String) -> String? {
    guard let url = urlString.removingPercentEncoding else { return nil }
    do {
        let regex = try NSRegularExpression.init(pattern: "((?<=(v|V)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=embed/))([\\w-]++)", options: .caseInsensitive)
        let range = NSRange(location: 0, length: url.count)
        if let matchRange = regex.firstMatch(in: url, options: .reportCompletion, range: range)?.range {
            let matchLength = (matchRange.lowerBound + matchRange.length) - 1
            if range.contains(matchRange.lowerBound) &&
                range.contains(matchLength) {
                let start = url.index(url.startIndex, offsetBy: matchRange.lowerBound)
                let end = url.index(url.startIndex, offsetBy: matchLength)
                return String(url[start...end])
            }
        }
    } catch {
        print(error.localizedDescription)
    }
    return nil
}

for url in youtubeURLs {
    print("Video id: \(getVideoID(from: url) ?? "NA") for url: \(url)")
}

结果:

Video id: -wtIMTCHWuI for url: http://www.youtube.com/watch?v=-wtIMTCHWuI
Video id: -wtIMTCHWuI for url: http://www.youtube.com/v/-wtIMTCHWuI?version=3&autohide=1
Video id: -wtIMTCHWuI for url: http://youtu.be/-wtIMTCHWuI
Video id: -wtIMTCHWuI for url: http://www.youtube.com/oembed?url=http%3A//www.youtube.com/watch?v%3D-wtIMTCHWuI&format=json
Video id: uJ2PZaO1N5E for url: https://youtu.be/uJ2PZaO1N5E
Video id: M7lc1UVf-VE for url: https://www.youtube.com/embed/M7lc1UVf-VE
Video id: EhxJLojIE_o for url: http://www.youtube.com/attribution_link?a=JdfC0C9V6ZI&u=%2Fwatch%3Fv%3DEhxJLojIE_o%26feature%3Dshare
Video id: yZv2daTWRZU for url: https://www.youtube.com/attribution_link?a=8g8kPrPIi-ecwIsS&u=/watch%3Fv%3DyZv2daTWRZU%26feature%3Dem-uploademail

5
这里是提取任何YouTube链接中视频ID的代码: (Swift)
func extractYoutubeId(fromLink link: String) -> String {
        let regexString: String = "((?<=(v|V)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=embed/))([\\w-]++)"
        let regExp = try? NSRegularExpression(pattern: regexString, options: .caseInsensitive)
        let array: [Any] = (regExp?.matches(in: link, options: [], range: NSRange(location: 0, length: (link.characters.count ))))!
        if array.count > 0 {
            let result: NSTextCheckingResult? = array.first as? NSTextCheckingResult
            return (link as NSString).substring(with: (result?.range)!)
        }

        return ""
    }

这似乎是有效的,但传递 https://www.twitch.tv/directory/all/tags/6ea6bca4-4712-4ab9-a906-e3336a9d8039 似乎会将 directory 作为视频ID返回。 - sudoExclaimationExclaimation
extractYoutubeId仅适用于YouTube视频链接。 - Usman Nisar

4

使用优雅的flatMap实现 Swift 4版本:

func extractYouTubeId(from url: String) -> String? {
    let typePattern = "(?:(?:\\.be\\/|embed\\/|v\\/|\\?v=|\\&v=|\\/videos\\/)|(?:[\\w+]+#\\w\\/\\w(?:\\/[\\w]+)?\\/\\w\\/))([\\w-_]+)"
    let regex = try? NSRegularExpression(pattern: typePattern, options: .caseInsensitive)
    return regex
        .flatMap { $0.firstMatch(in: url, range: NSMakeRange(0, url.count)) }
        .flatMap { Range($0.range(at: 1), in: url) }
        .map { String(url[$0]) }
}

这种方法使用正则表达式来检测大多数可能的YouTube URL格式(.be/*/embed//v/ - 您可以在此处找到完整列表)。


1
你的第一个问题是在表达式中没有转义??是保留字符,如果你想在表达式中使用它,你必须用\进行转义,并且由于\也用于转义"字符,所以你必须使用双反斜杠来转义?,例如\\?。因此,根据上述信息,以下代码可以正确提取videoId。
let youtubeURL = "https://www.youtube.com/watch?v=uH8o-JTHJdM"
let regex = try! NSRegularExpression(pattern: "\\?.*v=([^&]+)", options: .caseInsensitive)
let match = regex.firstMatch(in: youtubeURL, options: [], range: NSRange(location: 0, length: youtubeURL.characters.count))
if let videoIDRange = match?.rangeAt(1) {
    let substringForFirstMatch = (youtubeURL as NSString).substring(with: videoIDRange)
} else {
    //NO video URL
}

1

Youtube的URL示例:

let urls: [String] = [
    "www.youtube-nocookie.com/embed/up_lNV-yoK4?rel=0",
    "http://www.youtube.com/watch?v=peFZbP64dsU",
    "http://www.youtube.com/watch?v=cKZDdG9FTKY&feature=channel",
    "http://youtube.com/v/dQw4w9WgXcQ?feature=youtube_gdata_player",
    "http://youtube.com/?v=dQw4w9WgXcQ&feature=youtube_gdata_player",
    "http://youtu.be/6dwqZw0j_jY",
    "http://youtu.be/dQw4w9WgXcQ?feature=youtube_gdata_playe",
    "http://youtube.com/vi/dQw4w9WgXcQ?feature=youtube_gdata_player",
    "http://youtube.com/?vi=dQw4w9WgXcQ&feature=youtube_gdata_player",
    "http://youtube.com/watch?vi=dQw4w9WgXcQ&feature=youtube_gdata_player",
    "http://www.youtube.com/user/Scobleizer#p/u/1/1p3vcRhsYGo?rel=0",
    "http://www.youtube.com/user/SilkRoadTheatre#p/a/u/2/6dwqZw0j_jY",
    "1p3vcRhsY02"
]

我的扩展基于Islam Q.的解决方案:

private extension String {
    var youtubeID: String? {
        let pattern = "((?<=(v|V|vi)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=vi=)|(?<=/u/[0-9_]/)|(?<=embed/))([\\w-]++)"
        let regex = try? NSRegularExpression(pattern: pattern, options: .caseInsensitive)
        let range = NSRange(location: 0, length: count)

        guard let result = regex?.firstMatch(in: self, range: range) else {
            return count == 11 ? self : nil
        }

        let id = (self as NSString).substring(with: result.range)
        return id.count == 11 ? id : nil
    }
}

1

最近Youtube在直播视频前面添加了一个前缀。

我使用了这个帖子中的解决方案https://dev59.com/n1gR5IYBdhLWcg3wk9-d#62651531

然后我只是添加了(?<=live/)

let regex = try NSRegularExpression.init(pattern: "((?<=(v|V)/)|(?<=be/)|(?<=(\\?|\\&)v=)|(?<=embed/)|(?<=live/))([\\w-]++)", options: .caseInsensitive)

0

要从Youtube Url获取视频Id,请使用代码# Swift4

var videoId = ""

    if youtubeLink.lowercased().contains("youtu.be"){
            linkString = youtubeLink
            if let range = linkString.range(of: "be/"){
                videoId = youtubeLink[range.upperBound...].trimmingCharacters(in: .whitespaces)
            }
        }
        else if youtubeLink.lowercased().contains("youtube.com"){
            linkString = youtubeLink
            if let range = linkString.range(of: "?v="){
                videoId = youtubeLink[range.upperBound...].trimmingCharacters(in: .whitespaces)
            }
        }

希望能有所帮助!:)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接