0xced / XCDYouTubeKit

YouTube video player for iOS, tvOS and macOS
MIT License
2.92k stars 626 forks source link

Error XCDYouTubeVideoErrorDomain -2 when i try open video #544

Open Dem909 opened 3 years ago

Dem909 commented 3 years ago

when i try to open any video it returns an error XCDYouTubeVideoErrorDomain, Code: -2

Before that, other tips from other discussions helped, but now something new :(

Dem909 commented 3 years ago

This brunch helped me

pod 'XCDYouTubeKit', :git => 'https://github.com/dpwilhelmsen/XCDYouTubeKit', :branch => 'hotfix/fix-get-video-info-error', submodules: true

But i just replace 2 files https://github.com/dpwilhelmsen/XCDYouTubeKit/blob/hotfix/fix-get-video-info-error/XCDYouTubeKit/XCDYouTubeVideoOperation.m

https://github.com/dpwilhelmsen/XCDYouTubeKit/blob/hotfix/fix-get-video-info-error/XCDYouTubeKit/XCDYouTubeVideoWebpage.m

It's working one time, now not work

toyinswift commented 3 years ago

yes, it does not work anymore

Dem909 commented 3 years ago

And now what i can do? :)

cbg-dev-k commented 3 years ago

I'm seeing the same issue here.

When using a VPN to the US, videos still play. Maybe it's related to the consent cookie tweak we've had to implement before? That only affected non-US clients I believe.

ardavydov commented 3 years ago

Ah shit, here we go again

someziggyman commented 3 years ago

Maybe this can help: https://stackoverflow.com/questions/67615278/get-video-info-youtube-endpoint-suddenly-returning-404-not-found/67629882#67629882

There's a new answer named UPDATE (July 2021)

POST: https://youtubei.googleapis.com/youtubei/v1/player?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8

with the body :

{ "context": { "client": { "hl": "en", "clientName": "WEB", "clientVersion": "2.20210721.00.00", "mainAppWebInfo": { "graftUrl": "/watch?v={VIDEO_ID}" } } }, "videoId": "{VIDEO_ID}" }

armendh commented 3 years ago

the above mentioned fix seems to work checkout #545

the innertube api key is hardcoded and we don't know how long it will last so it is recommended to scrap the api key from youtube and set it via XCDYouTubeClient.setInnertubeApiKey

func scrapInnertubeApiKey(){
    let link = "https://www.youtube.com"
    Alamofire.request(link).responseString { (response) in  // network lib https://github.com/Alamofire/Alamofire
        if let html = response.value {
            if let doc = try? HTML(html: html, encoding: .utf8) {   // HTML parser https://github.com/tid-kijyun/Kanna
                if let text = doc.xpath("//script[contains(., 'INNERTUBE_API_KEY')]/text()").first?.text {
                    if let results = text.match("ytcfg.set\\((\\{.*?\\})\\)").last?.last {
                        if let data = results.data(using: .utf8), let model = try? JSONDecoder().decode(InnertubeScrap.self, from: data) {
                            let key = model.INNERTUBE_API_KEY
                            XCDYouTubeClient.setInnertubeApiKey(key)
                        }
                    }
                }
            }
        }
    }
}

struct InnertubeScrap : Codable {
    let INNERTUBE_API_KEY : String
}

extension String {
    func match(_ regex: String) -> [[String]] {
        let nsString = self as NSString
        return (try? NSRegularExpression(pattern: regex, options: []))?.matches(in: self, options: [], range: NSMakeRange(0, nsString.length)).map { match in
            (0..<match.numberOfRanges).map { match.range(at: $0).location == NSNotFound ? "" : nsString.substring(with: match.range(at: $0)) }
        } ?? []
    }
}
SirCochese commented 3 years ago

I can confirm the post method works, but the response schema is different than the current get_video_info. So, I think there are some meaningful changes required to get this working from within XCDYouTubeKit. In case it helps anyone in the short-term, here's snippet from my swift app. But I started needing to re-implement some of the signature stuff inside XCDYouTubeKit, and decided to slow my roll.

Note that postToUrl is a shared helper which is pretty simple, so left out here for brevity.

func getVideoUrl(videoId: String, completion: @escaping ([UInt:String]) -> Void) {
    let infoParams: [String: Any] = [
        "graftUrl": "/watch?v=\(videoId)"
    ]
    let clientParams: [String: Any] = [
        "hl": "en",
        "clientName": "WEB",
        "clientVersion": "2.20210721.00.00",
        "mainAppWebInfo": infoParams
    ]
    let contextParams: [String: Any] = [
        "client": clientParams
    ]
    let parameters: [String: Any] = [
        "context": contextParams,
        "videoId": videoId
    ]

    postToUrl(urlStr: "https://youtubei.googleapis.com/youtubei/v1/player?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", parameters: parameters, commandCompletion: { (data, response, error) in
        var streamURLs: [UInt:String] = [:]

        //let dataString = String(decoding: data!, as: UTF8.self)
        //STLog(logStr: dataString)

        if  let data = data,
            let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
            let streamingData = json["streamingData"] as? [String: Any],
            let formats = streamingData["formats"] as? [[String: Any]] {
                for format in formats
                {
                    if let itag = format["itag"] as? UInt
                    {
                        if let url = format["url"] as? String {
                            streamURLs[itag] = url
                        }
                        else if let signatureCipher = format["signatureCipher"] as? String,
                                let queryDict = signatureCipher.queryDictionary,
                                let url = queryDict["url"],
                                let sig = queryDict["s"] {
                            // TODO: twiddle the signature, should likely do this in XCDYouTubeKit
                            streamURLs[itag] = url
                        }
                    }
                }
            }

        completion(streamURLs)
    })
}
Dem909 commented 3 years ago

Note that postToUrl is a shared helper which is pretty simple, so left out here for brevity.

Return error "Precondition check failed" :(

Dem909 commented 3 years ago

the above mentioned fix seems to work checkout #545

the innertube api key is hardcoded and we don't know how long it will last so it is recommended to scrap the api key from youtube and set it via XCDYouTubeClient.setInnertubeApiKey

func scrapInnertubeApiKey(){
    let link = "https://www.youtube.com"
    Alamofire.request(link).responseString { (response) in  // network lib https://github.com/Alamofire/Alamofire
        if let html = response.value {
            if let doc = try? HTML(html: html, encoding: .utf8) {   // HTML parser https://github.com/tid-kijyun/Kanna
                if let text = doc.xpath("//script[contains(., 'INNERTUBE_API_KEY')]/text()").first?.text {
                    if let results = text.match("ytcfg.set\\((\\{.*?\\})\\)").last?.last {
                        if let data = results.data(using: .utf8), let model = try? JSONDecoder().decode(InnertubeScrap.self, from: data) {
                            let key = model.INNERTUBE_API_KEY
                            XCDYouTubeClient.setInnertubeApiKey(key)
                        }
                    }
                }
            }
        }
    }
}

struct InnertubeScrap : Codable {
    let INNERTUBE_API_KEY : String
}

extension String {
    func match(_ regex: String) -> [[String]] {
        let nsString = self as NSString
        return (try? NSRegularExpression(pattern: regex, options: []))?.matches(in: self, options: [], range: NSMakeRange(0, nsString.length)).map { match in
            (0..<match.numberOfRanges).map { match.range(at: $0).location == NSNotFound ? "" : nsString.substring(with: match.range(at: $0)) }
        } ?? []
    }
}

Looks good, but the problem is that at my stage if let text = doc.xpath("//script[contains(., 'INNERTUBE_API_KEY')]/text()").first?.text { does not return anything, and a screen with the YouTube privacy policy is displayed in the doc. content

But i tested key "AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8" - he is work, but Privacy Policy blocked all and video not returned(

Dem909 commented 3 years ago

When i try get video info, google return this:

<NSHTTPURLResponse: 0x283dae420> { URL: https://youtubei.googleapis.com/youtubei/v1/player?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8 } { Status Code: 404, Headers { "Alt-Svc" = ( "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"" ); "Content-Encoding" = ( br ); "Content-Length" = ( 138 ); "Content-Type" = ( "application/json; charset=UTF-8" ); Date = ( "Sun, 25 Jul 2021 21:16:49 GMT" ); Server = ( ESF ); Vary = ( Origin, "X-Origin", Referer ); "x-content-type-options" = ( nosniff ); "x-frame-options" = ( SAMEORIGIN ); "x-xss-protection" = ( 0 ); } }

SirCochese commented 3 years ago

Note that postToUrl is a shared helper which is pretty simple, so left out here for brevity.

Return error "Precondition check failed" :(

I saw that, you need to make sure that the content type is application/json and you don't percent encode it.

Dem909 commented 3 years ago

Note that postToUrl is a shared helper which is pretty simple, so left out here for brevity.

Return error "Precondition check failed" :(

I saw that, you need to make sure that the content type is application/json and you don't percent encode it.

It's... work...

I hope this doesn't sound weird, but I love you, thank you very much! :) You helped a lot, because the application in the store began to pour out, users complained and real hell began for me ... You really saved me, thanks!

Kiu212 commented 3 years ago

I followed the code in #545, but still getting XCDYouTubeVideoErrorDomain -2 for upcoming live stream video. This is no doubt that if the problem solved, this will be the perfect fix. Any idea for this? Thanks.

444ten commented 3 years ago

@Kiu212 +1 Addition. This problem is specific to the European Region

cbg-dev-k commented 3 years ago

After combining #545 with the changes I already had locally (consent cookie etc), it started playing videos again in europe.

When I try a livestream however, the POST works but youtubekit fails to play the stream. The log eventually notifies that a request went timeout. Anyone else seeing this?

The timeout error: [XCDYouTubeKit] URL GET operation finished with error: The request timed out. Domain: NSURLErrorDomain Code: -1001 User Info: { NSErrorFailingURLKey = "https://r9---sn-uxaxoxu-cg0r.googlevideo.com/videoplayback?expire=1627321020&ei=XJ7-YIKUIYKx1wK-2pCADw&ip=2a02%3A1810%3Aa418%3Af700%3A38fc%3Aa27c%3Aa8bc%3A63e6&id=DWcJFNfaw9c.3&itag=243&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C248%2C278&source=yt_live_broadcast&requiressl=yes&mh=Ms&mm=44%2C29&mn=sn-uxaxoxu-cg0r%2Csn-5hnekn7d&ms=lva%2Crdu&mv=u&mvi=9&pl=45&pcm2=yes&vprv=1&live=1&hang=1&noclen=1&mime=video%2Fwebm&ns=EtxyS696fTkFieyGVFLQZrAG&gir=yes&mt=1627298950&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466588&c=WEB&n=FSF8yu_YZ5HouZ&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cpcm2%2Cvprv%2Clive%2Chang%2Cnoclen%2Cmime%2Cns%2Cgir&sig=AOq0QJ8wRQIhAKPUVg1BS0dny9J4cVDhB57s1VBGBrZw2NppdaL-APesAiAU8b2kXbeM9iV-Yt_s4qMftNiJhNKDitq8M0FD6ESGMg%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl&lsig=AG3C_xAwRgIhAI8vIV-EqXsXq0Y5zwdqvYYlrTKy3TqHoGRy0l4rXNJ5AiEAyspZ6SOPFDdQ-CnjDvqHl55kOAVMenro0IxH-jFdcoA%3D&ratebypass=yes"; NSErrorFailingURLStringKey = "https://r9---sn-uxaxoxu-cg0r.googlevideo.com/videoplayback?expire=1627321020&ei=XJ7-YIKUIYKx1wK-2pCADw&ip=2a02%3A1810%3Aa418%3Af700%3A38fc%3Aa27c%3Aa8bc%3A63e6&id=DWcJFNfaw9c.3&itag=243&aitags=133%2C134%2C135%2C136%2C137%2C160%2C242%2C243%2C244%2C247%2C248%2C278&source=yt_live_broadcast&requiressl=yes&mh=Ms&mm=44%2C29&mn=sn-uxaxoxu-cg0r%2Csn-5hnekn7d&ms=lva%2Crdu&mv=u&mvi=9&pl=45&pcm2=yes&vprv=1&live=1&hang=1&noclen=1&mime=video%2Fwebm&ns=EtxyS696fTkFieyGVFLQZrAG&gir=yes&mt=1627298950&fvip=4&keepalive=yes&fexp=24001373%2C24007246&beids=9466588&c=WEB&n=FSF8yu_YZ5HouZ&sparams=expire%2Cei%2Cip%2Cid%2Caitags%2Csource%2Crequiressl%2Cpcm2%2Cvprv%2Clive%2Chang%2Cnoclen%2Cmime%2Cns%2Cgir&sig=AOq0QJ8wRQIhAKPUVg1BS0dny9J4cVDhB57s1VBGBrZw2NppdaL-APesAiAU8b2kXbeM9iV-Yt_s4qMftNiJhNKDitq8M0FD6ESGMg%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl&lsig=AG3C_xAwRgIhAI8vIV-EqXsXq0Y5zwdqvYYlrTKy3TqHoGRy0l4rXNJ5AiEAyspZ6SOPFDdQ-CnjDvqHl55kOAVMenro0IxH-jFdcoA%3D&ratebypass=yes"; NSLocalizedDescription = "The request timed out."; NSUnderlyingError = "Error Domain=kCFErrorDomainCFNetwork Code=-1001 \"(null)\" UserInfo={_kCFStreamErrorCodeKey=-2102, _kCFStreamErrorDomainKey=4}"; "_NSURLErrorFailingURLSessionTaskErrorKey" = "LocalDataTask <2272A8E5-2351-4FC3-942F-980D3DCBAEC5>.<1>"; "_NSURLErrorRelatedURLSessionTaskErrorKey" = ( "LocalDataTask <2272A8E5-2351-4FC3-942F-980D3DCBAEC5>.<1>" ); "_kCFStreamErrorCodeKey" = "-2102"; "_kCFStreamErrorDomainKey" = 4; }

tkachV commented 3 years ago

I have same error... If someone have solution - pls share... Thanks.

HaKKeBeilHaRRy commented 3 years ago

need a fix :'(

cbg-dev-k commented 3 years ago

Well damn, it seems I didn't apply one of the previous fixes and it came back to bite me.

The only missing code I needed was near line 173 in XCDYoutubeVideo.m: if (httpLiveStream.length == 0) { httpLiveStream = info[@"streamingData"][@"hlsManifestUrl"]; }

Now both livestreams and past broadcasts are working for me.

HaKKeBeilHaRRy commented 3 years ago

no of the fixes works anymore (for me). it looks like it will only play videos randomly.

FazilMuhammed commented 3 years ago

anyone got the solution ??

MaurixFx commented 3 years ago

Hello guys, someone got the solution?

dredscabral commented 2 years ago

I was getting this error when I tried to pass the video url instead of the video id.