GithubHelp home page GithubHelp logo

alexeichhorn / youtubekit Goto Github PK

View Code? Open in Web Editor NEW
149.0 11.0 27.0 201 KB

YouTube video and audio extractor for iOS, watchOS, visionOS, tvOS and macOS

License: MIT License

Swift 100.00%
youtube downloader video audio extractor youtube-dl swift native

youtubekit's Introduction

YouTubeKit

This package allows to extract the direct video url or audio url for any YouTube video. This therefore allows to play YouTube videos in native UI components.

Disclaimer: YouTubeKit is currently still a work in progress, so it might not work in all regions.

The structurce of the code is strongly aligned with the pytube project (written in Python). This should make future breaking changes (by the YouTube API) easier to fix.

Compatibility

It requires iOS 13, watchOS 6, tvOS 13 or macOS 10.15, since it's relying on the Swift 5.5 Concurrency module. visionOS is also supported.

Usage

  1. Create a YouTube object with the videoURL or videoID of your video:
let video = YouTube(url: videoURL)

or

let video = YouTube(videoID: videoID)
  1. Extract all streams:
let streams = try await video.streams

This will return an array of Stream objects.

  1. Filter for the stream you want by using a normal filter or with provided helper functions like:
let streamsAt1080 = streams.streams(withExactResolution: 1080)
let streamsBelow1080 = streams.filter(byResolution: $0 < 1080)  // all streams with resolution lower than 1080p
let lowestResolution = streams.lowestResolutionStream()
let highestResolution = streams.highestResolutionStream()
let lowestAudioBitrate = streams.lowestAudioBitrateStream()
let highestAudioBitrate = streams.highestAudioBitrateStream()
let audioOnlyStreams = streams.filterAudioOnly()  // all streams without video track
let videoOnlyStreams = streams.filterVideoOnly()  // all streams without audio track
let combinedStreams = streams.filterVideoAndAudio()  // all streams with both video and audio track
  1. Retrieve metadata:
let metadata = try await video.metadata

This will return a YouTubeMetadata object.

Example 1

To play a YouTube video in AVPlayer:

let stream = try await YouTube(videoID: "QdBZY2fkU-0").streams
                          .filterVideoAndAudio()
                          .filter { $0.isNativelyPlayable }
                          .highestResolutionStream()

let player = AVPlayer(url: stream!.url)
// -> Now present the player however you like

The isNativelyPlayable parameter is used to filter only streams that are natively decodable on the current operating system and device.

Example 2

To get the best m4a audio-only url for a given YouTube ID:

let stream = try await YouTube(videoID: "9bZkp7q19f0").streams
                          .filterAudioOnly()
                          .filter { $0.fileExtension == .m4a }
                          .highestAudioBitrateStream()

let streamURL = stream.url

Example 3

To get the video url of type mp4 with the highest available resolution for a given YouTube url:

let stream = try await YouTube(url: youtubeURL).streams
                          .filter { $0.includesVideoAndAudioTrack && $0.fileExtension == .mp4 }
                          .highestResolutionStream()

let streamURL = stream.url                      

The isProgressive parameter is used to filter only streams that contain both video and audio.

Example 4

To get the HLS url for a given YouTube ID of a livestream:

let hlsManifestUrl = try await YouTube(videoID: "21X5lGlDOfg").livestreams
                          .filter { $0.streamType == .hls }
                          .first

Remote Fallback

With local YouTube extractors, there is the problem that YouTube might suddenly change their unofficial API, which can break your existing shipped app. It can take days or weeks for a user to update your app, rendering some features unusable for them in the meantime. To prevent this, YouTubeKit includes a feature that allows you to enable a remote fallback. As soon as local extraction fails, it switches to using a remote server running youtube-dl, that is updated frequently. Simply specify the methods YouTubeKit should use in priority order. The rest of the API remains exactly the same — everything is handled by the library.

let streams = try await YouTube(videoID: "2lAe1cqCOXo", methods: [.local, .remote]).streams

You can also set methods: [.remote] if you only want remote extraction.

How It Works

Since streams are often bound to the device's location or IP address, we can't simply use youtube-dl on a remote server and send back the stream urls. Instead, the server makes all HTTP requests through the requesting device. When starting remote extraction, the device opens a WebSocket connection to the remote server. The server then sends multiple HTTP request packets to the device. The device executes these on behalf of the server and returns the full response. The server then processes and extracts the stream urls and sends them back to the device. This ensures the retrieved stream urls are playable on your device.

Currently, the default remote server is hosted by me. A way for you to host it yourself is coming soon.

youtubekit's People

Contributors

alexeichhorn avatar waliid avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

youtubekit's Issues

Video stream url is so slow for downloading or streaming?

Hi

I'm developing app that use your repo. It's amazing repo written in swift. Thank you for that. But as title above. URL is so slow for loading/downloading/watching. Technically, it's from Youtube Server or from you repo. Hope to get your response soon.

Best Regard

The flag isNativelyPlayable is returning a wrong value

On the Stream model, the isNativelyPlayable flag is returning true even if the videoCodec or the audioCodec are nil.

public var isNativelyPlayable: Bool {
    (videoCodec?.isNativelyPlayable ?? true) && (audioCodec?.isNativelyPlayable ?? true)
}

A better default value would be false.

public var isNativelyPlayable: Bool {
    (videoCodec?.isNativelyPlayable ?? false) && (audioCodec?.isNativelyPlayable ?? false)
}

Additionally, the video codec AVC1: 42001E is not compatible with the Apple TV platform, only the AVC1: 64001F.


AVC1: 42001E Stream:

Stream(
    url: https://rr3---sn-2vgu0b5auxaxjvh-apnd.googlevideo.com/videoplayback?expire=171218………, 
    itag: YouTubeKit.ITag(
        itag: 18, 
        videoResolution: Optional(360), 
        audioBitrate: Optional(96)), 
    mimeType: "video/mp4", 
    videoCodec: Optional(YouTubeKit.VideoCodec.avc1(version: "42001E")), 
    audioCodec: Optional(YouTubeKit.AudioCodec.mp4a(version: "40.2")), 
    fileExtension: YouTubeKit.FileExtension.mp4, bitrate: Optional(137681), 
    averageBitrate: nil, 
    type: "video", 
    subtype: "mp4", 
    filesize: nil
)

AVC1: 64001F Stream:

Stream(
    url: https://rr1---sn-2vgu0b5auxaxjvh-apnd.googlevideo.com/videoplayback?expire=171218………,
    itag: YouTubeKit.ITag(
        itag: 22, 
        videoResolution: Optional(720), 
        audioBitrate: Optional(192)), 
    mimeType: "video/mp4", 
    videoCodec: Optional(YouTubeKit.VideoCodec.avc1(version: "64001F")), 
    audioCodec: Optional(YouTubeKit.AudioCodec.mp4a(version: "40.2")), 
    fileExtension: YouTubeKit.FileExtension.mp4, 
    bitrate: Optional(981047), 
    averageBitrate: nil, 
    type: "video", 
    subtype: "mp4", 
    filesize: nil
)

Right now my implementation to filter the streams for playback on Apple TV using AVPlayer, looks like this:

videoStream = streams.filter { ($0.audioCodec?.isNativelyPlayable ?? false) && ($0.videoCodec?.isNativelyPlayable ?? false) && $0.videoCodec.debugDescription.contains("64001F") }.first?.url

Audio stream duration doubled?

I'm using YouTubeKit to get the audio url. and feed it to AVPlayer. It plays fine, except audio's total length seems doubled.

For a 1-minute video, we can get an audio url, but the total duration of the stream is 2 minutes. After playing 1 minute, the player keeps playing for another 1 minute, but there is no sound. I try copying the audio url and paste it in browser to play, also showing double audio length.

Has anyone seen this issue before?

Not sure if this is YouTubeKit specific or YouTube making it that way.

can't get video with audio

https://www.youtube.com/watch?v=NbXSj2WT6nw
https://m.youtube.com/watch?v=NbXSj2WT6nw

example address
all addresses have this problem.

I get an array of links and print

includesVideoTrack = true ==== includesAudioTrack false format mp4
includesVideoTrack = true ==== includesAudioTrack false format webm
includesVideoTrack = true ==== includesAudioTrack false format mp4
includesVideoTrack = true ==== includesAudioTrack false format webm
includesVideoTrack = true ==== includesAudioTrack false format mp4
includesVideoTrack = true ==== includesAudioTrack false format webm
includesVideoTrack = true ==== includesAudioTrack false format mp4
includesVideoTrack = true ==== includesAudioTrack false format webm
includesVideoTrack = true ==== includesAudioTrack false format mp4
includesVideoTrack = true ==== includesAudioTrack false format webm
includesVideoTrack = false ==== includesAudioTrack true format mp4
includesVideoTrack = false ==== includesAudioTrack true format mp4

there are links only with video or only with audio
missing together
requests used different, no filters applied
let video = YouTube(url: videoURL)
let video = YouTube(videoID: videoID)

the problem was like this:
class InnerTube
init(client: ClientType = .ios, useOAuth: Bool = false, allowCache: Bool = true)

links with video and audio began to come
init(client: ClientType = .web, useOAuth: Bool = false, allowCache: Bool = true) {
but very few links, only two

an error in work, links are not parsed

an error in work, links are not parsed for audio

🟣 ID = 3F4Ls0UlBt4 findetStreams count 2
⭕️ ADD URL 1
2023-04-27 20:15:23.628611+0500 uPlayer[2107:497390] [Cipher] finished regex search, matched ([a-zA-Z0-9$]+)\s*=\sfunction(\sa\s*)\s*{\sa\s=\sa.split(\s""\s*)
2023-04-27 20:15:23.629348+0500 uPlayer[2107:497390] [Cipher] getting transform plan
2023-04-27 20:15:23.637503+0500 uPlayer[2107:497390] [Cipher] getting transform object
2023-04-27 20:15:23.637695+0500 uPlayer[2107:497262] [Cipher] finished regex search, matched ([a-zA-Z0-9$]+)\s*=\sfunction(\sa\s*)\s*{\sa\s=\sa.split(\s""\s*)
2023-04-27 20:15:23.638158+0500 uPlayer[2107:497262] [Cipher] getting transform plan
2023-04-27 20:15:23.646991+0500 uPlayer[2107:497262] [Cipher] getting transform object
🟣 ID = uZg5hQxqhFQ
🟣 ID = 3taPdov5Sps
🟣 ID = U5k8d4oK45E
🟣 ID = vi8a88mBXok
🟣 ID = Ho3xcroioSM
🟣 ID = vs61OHs2g-w
🟣 ID = Sfqt2KtSj-Q
🟣 ID = cqJwrvEodvU

Crash

Hey!
often crash here
can i fix it somehow?
Снимок экрана 2023-01-13 в 13 15 17

QUESTION: Downloading is really slow

I noticed that loading the URL retrieved in a WKWebView's AUDIO element seems to load the audio instantly.
We can also skip to different times.

However, performing the download in Swift using for example the Alamofire library is super slow, as if the server was throttling our request.

Any idea on how we could improve the situation?

Over the last weeks, more and more videos can't be downloaded

First of all, I love this SPM package. I use it in www.macwhisper.com and it makes a lot of people happy! Over the last week, I've been receiving messages from users that they're unable to download more and more YouTube videos. Here's an example I was able to reproduce.

Would love any ideas, thanks!

I'm using these settings:

let stream = try await YouTube(url: URL(string: urlString)!)
                    .streams
                    .filter {
                        $0.isProgressive && $0.subtype == "mp4"
                    }
                    .lowestResolutionStream()

This Youtube video:
https://youtu.be/dkpDjd2nHgo

Gives this stream url:
https://rr4---sn-5hnednsz.googlevideo.com/videoplayback?expire=1692295932&ei=mw7eZJTuPNC7x_APpfSyuAo&ip=109.38.145.80&id=o-AHFLv1AmaIAbbJNXCgwtS9kUlSALTqbC_TQ62NJm5nhB&itag=18&source=youtube&requiressl=yes&mh=_c&mm=31%2C29&mn=sn-5hnednsz%2Csn-5hne6ns6&ms=au%2Crdu&mv=m&mvi=4&pl=24&initcwndbps=1270000&spc=UWF9f5Q0OSv3De2zhUTox19rvDl877swgPrAjymrIQ&vprv=1&svpuc=1&mime=video%2Fmp4&gir=yes&clen=361938904&ratebypass=yes&dur=5046.926&lmt=1692258791620214&mt=1692273917&fvip=3&fexp=24007246%2C24362685&beids=24350017&c=ANDROID&txp=4438434&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Csvpuc%2Cmime%2Cgir%2Cclen%2Cratebypass%2Cdur%2Clmt&sig=AOq0QJ8wRQIgDcCmFqsM46xJxUYRLEzb7kk9KkTgebmh20BlMPX_EtYCIQCYJbUggvCvlY99fZbXFMLS7VNqgJr6ZdeRECbNWlf97w%3D%3D&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRQIhAJVWcwK31jeNHq5HYDZjzjvTve_r6d_1xJ-FezBcAiH0AiAmPZXaCruRM65mpADZtZnY4iiTMSz0Ud4L5Vs4Emd2MQ%3D%3D

When I open that link in Brave I get this error:
image

GetYTPlayerConfig - error

It stopped working today 😢

[Extraction] pattern (ytplayer\.config\s*=\s*) failed: The operation couldn’t be completed.

Failed to decode object from given start point:

Hi, The descrambling mechanism does not seem to work anymore. I receive below error on Xcode log, and the stream URL too refuses to play...

applying descrambler
finding initial function name
Failed to decode object from given start point: The data couldn’t be read because it isn’t in the correct format.
[Extraction] pattern (ytplayer.config\s*=\s*) failed: The operation couldn’t be completed.

Unable to download certain videos

Hi.
The following error occurs with certain videos.
Relatively recent uploads are not possible.

finished with error [57] Error Domain=NSPOSIXErrorDomain Code=57 "Socket is not connected" UserInfo={NSErrorFailingURLStringKey=https://youtubekit-remote.losjet.com/v1?videoID=VGKrQu3ERgY, NSErrorFailingURLKey=https://youtubekit-remote.losjet.com/v1?videoID=VGKrQu3ERgY, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalWebSocketTask <5A6E716A-8ADA-40C6-A620-4C8AE9294E65>.<13>"
), _NSURLErrorFailingURLSessionTaskErrorKey=LocalWebSocketTask <5A6E716A-8ADA-40C6-A620-4C8AE9294E65>.<13>}

`YouTubeKitError.htmlParseError` & slow download speed

Hi! :)

I'm getting a YouTubeKitError.htmlParseError being thrown in Extraction.getYTPlayerConfig() for the pattern ytplayer\.config\s*=\s*

This leads to pattern (ytplayer\.config\s*=\s*) failed: The operation couldn’t be completed. being logged.

(example URL: https://www.youtube.com/watch?v=8j6pkKwpOFk)

After that, the download proceeds but is really slow. (this may or may not be related to the above).

I guess YouTube is throttling the download, because (it's really slow, and) if I also just wget the URL that YouTubeKit is downloading, the download speed is get is like 11KB/s (on a gigabit/ethernet line)

CleanShot 2024-04-05 at 10 58 03@2x

My guess is that the HTML parse for config is failing, and the fallback only allows slow downloads? Just a guess though.

Thanks for reading!

Age Restriction?

Firstly, this is great!
I'm trying to download an audio stream and I'm getting the error "YouTube selection error ERROR: videoAgeRestricted"
Any idea how I might get around this?

alert "content is not available on this app."

Hello, it doesn't seem to be working properly now with this message
“The following content is not available on this app. Watch this content on the latest version of YouTube”

IMG_5218

What can I do to solve this problem?

Can't get video with audio

Hi,

Using the sample code

let stream = try await YouTube(videoID: "<id>").streams
                          .filter { $0.isProgressive && $0.subtype == "mp4" }
                          .highestResolutionStream()

i don't get any streams

Removing $0.isProgressive i can get streams but all videos doesn't have audio.

thumbnail access ?

This library works as expected, but it seems not possible to get thumbnail(s), which is needed for UI

RegexMatchError after breaking change from YT

Looks like yesterday there was a breaking change from YT.
Pytube team have already fixed the issue but sadly YouTubeKit breaks on getTransformObject regex.
I've attempted to escape the variable as hinted by the // TODO:, but it did not help.
P.S. what an amazing job you've done with this so far!

Unable to stream video, audio working ok.

Hi,
I'm unable to stream video, the audio is working fine. I've listed the errors below.
[Extraction] applying descrambler
[Extraction] finding initial function name
[Extraction] Failed to decode object from given start point: The data couldn’t be read because it isn’t in the correct format.
pattern (ytplayer.config\s*=\s*) failed: The operation couldn’t be completed. (YouTubeKit.YouTubeKitError error 1.)
signature found, skip decipher

Screenshot 2023-07-20 at 12 09 21 PM

The code fails on line 67 and the error is being thrown on line 69.
I'm using the 0.1.8 release. The audio plays but no video. I'm using SwiftUI Xcode 14.3.1, iOS 16.5.1 (c). Mac Mini M1.

Any help would be appreciated.
Thanks.

Can't play video (iOS17.1.1)

Hello.
An issue occurred where the video URL could not be obtained on iOS17.1.1.

https://www.youtube.com/watch?v=XzOvgu3GPwY

let stream = try await YouTube(videoID: "XzOvgu3GPwY").streams
    .filter { $0.isProgressive && $0.subtype == "mp4" && $0.includesVideoTrack }
    .highestResolutionStream()

=> return nil

I edited the code as below, but only the video plays, the audio does not work.

let stream = try await YouTube(videoID: "XzOvgu3GPwY").streams
    .filter { $0.subtype == "mp4" }
    .highestResolutionStream()

Could you tell me if there is a way to improve it?

remote mode

Hello!
When using remote mode, the following error often occurs:

Task <88DC4801-6F6D-42C9-A04B-B1F9A0098C1D>.<14> finished with error [57] Error Domain=NSPOSIXErrorDomain Code=57 "Socket is not connected" UserInfo={NSErrorFailingURLStringKey=https://youtubekit-remote.losjet.com/v1?videoID=gir8BEqAutk, NSErrorFailingURLKey=https://youtubekit-remote.losjet.com/v1?videoID=gir8BEqAutk, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalWebSocketTask <88DC4801-6F6D-42C9-A04B-B1F9A0098C1D>.<14>"
), _NSURLErrorFailingURLSessionTaskErrorKey=LocalWebSocketTask <88DC4801-6F6D-42C9-A04B-B1F9A0098C1D>.<14>}
Task .<15> finished with error [57] Error Domain=NSPOSIXErrorDomain Code=57 "Socket is not connected" UserInfo={NSErrorFailingURLStringKey=https://youtubekit-remote.losjet.com/v1?videoID=nBZlrbrBO1I, NSErrorFailingURLKey=https://youtubekit-remote.losjet.com/v1?videoID=nBZlrbrBO1I, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalWebSocketTask .<15>"
), _NSURLErrorFailingURLSessionTaskErrorKey=LocalWebSocketTask .<15>}

Add m3u8 support?

YouTube's iOS client has added a new hls manifest option with qualities to 1080p. Could this be added to YouTubeKit?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.