Open cbg-dev-k opened 3 years ago
@kbex-dev I have just tried your repo, and everything works well, except i can't get the thumbnail in the iOS demo. it says unsupported URL, please help.
Hi @zhengyanWork, I'm using a different API to get video_info but didn't change the mapping provided by YoutubeKit. It's possible the thumbnail url was moved to another field and a change is needed on the Video model in this repo?
I'm sorry to say that this isn't a priority for my use case, I won't have time to look into it.
@kbex-dev Thanks for this!
To fix thumbnails, insert the following after Line 228 in XCDYouTubeVideo.m
file :-
if (!thumbnails) {
NSDictionary *thumbnailDictionary = videoDetails[@"thumbnail"];
if (thumbnailDictionary && [thumbnailDictionary isKindOfClass:[NSDictionary class]]) {
NSArray *thumbnailArray = thumbnailDictionary[@"thumbnails"];
if (thumbnailArray && [thumbnailArray isKindOfClass:[NSArray class]]) {
thumbnails = thumbnailArray;
}
}
}
Hey @kunalsood, thanks for sharing.
I've added your code to the fork, though I haven't confirmed the fix. Hopefully it helps others needing those thumbnails.
No worries @kbex-dev.
One question (since I haven't get been able to go through all the issues open about this, on this repo here): Where is the _innertubeApiKey
here from? Is it an API key I generate for my own use? or is it safe to use the one already there?
The default key is scraped from their website. I think you could create your own API key from a YT account, but I never bothered since we're not exactly following their terms of service :|
In my own codebase I occasionally scrape the YT website to see if the key was updated, but I think it's still the same.
Another gotcha is that their API expects a consent/gdpr cookie (at least when in Europe). This can also be mocked.
I'll add my own (Swift 5) implementation for both issues below as a reference:
import Alamofire
import Foundation
import Kanna
import XCDYouTubeKit
enum Hacks {
static func provideYoutubeCookie() {
guard let cookieStorage = Alamofire.SessionManager.default.session.configuration.httpCookieStorage else { return }
let cookieName = "CONSENT"
let cookieValue = "YES+cb.\(youtubeDateString)-17-p0.en+FX+\(Int.random(in: 101...998))"
if let existingCookie = cookieStorage.cookies?.first(where: { $0.name == cookieName }),
existingCookie.value.contains("YES") {
// existing cookie should be good
return
}
let hackyYoutubeCookie = HTTPCookie(properties: [
.domain: ".youtube.com",
.path: "/",
.name: cookieName,
.value: cookieValue,
.secure: "TRUE"
])
hackyYoutubeCookie.map { cookieStorage.setCookie($0) }
}
/// Using a recent date for consent cookie, in case youtube verifies this
private static var youtubeDateString: String {
let defaultDateString = "20210519"
guard let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: Date()) else { return defaultDateString }
return youtubeDateFormatter.string(from: yesterday)
}
private static let youtubeDateFormatter = DateFormatter().then {
$0.locale = Locale(identifier: "en_US_POSIX")
$0.dateFormat = "yyyyMMdd"
}
}
extension Hacks {
static func scrapeYoutubeApiKey() {
Hacks.provideYoutubeCookie()
let link = "https://www.youtube.com"
Alamofire.request(link).responseString { response in
if let html = response.value {
if let doc = try? HTML(html: html, encoding: .utf8) {
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
// swiftlint:disable:next legacy_constructor
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)) }
} ?? []
}
}
Awesome! Thanks.
The slow download issue seems to be popping up more and more for our users.
yt-dlp seems to handle it fine, but I'm having a hard time finding out what they do to fix it.
If anyone is feeling up for it, I'd happily accept PRs on my fork :)
I'm having trouble with Iphone 8 without sound, anyone has seen this behavior?
Hi @kbex-dev,
Thanks for the work you've done. Instead of deleting my original comments, I thought I'd just pass on what I'd done. Brilliant work! https://stephenmonro.wordpress.com/2022/04/13/getting-youtube-videos-to-play-again-in-ionic5/
Thanks, Steve
Hi Steve
I'm glad you got it to work!
As a word of warning, these fixes aren't a complete solution. It worked for a while, but I've since received new reports about slow/stalled videos.
I haven't been able to fix those issues on this repo without a complete refactor to mimick youtubedl :/
Well, it's much better than having them not working for the moment. :) Apparently it hasn't been working since last year... so I'll take the gamble and push an upload to the Apple App store. I'll keep you posted if I notice anything weird though.
This PR contains all the fixes I had applied locally (from other comments on the repo) + a fix for the slow downloads