-
-
Notifications
You must be signed in to change notification settings - Fork 92
Update YouTube Changes (August 2026) #140
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
71c0c33
e11371d
740bcc9
457f897
0be98cd
e195836
a04a3fc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -120,6 +120,7 @@ public class YouTube { | |
| var request = URLRequest(url: embedURL) | ||
| request.setValue("Mozilla/5.0", forHTTPHeaderField: "User-Agent") | ||
| request.setValue("en-US,en", forHTTPHeaderField: "accept-language") | ||
| request.setValue("https://www.reddit.com/", forHTTPHeaderField: "Referer") | ||
| request.httpShouldHandleCookies = false | ||
| let (data, _) = try await URLSession.shared.data(for: request) | ||
| _embedHTML = String(data: data, encoding: .utf8) ?? "" | ||
|
|
@@ -318,6 +319,11 @@ public class YouTube { | |
| if !streamingData.isEmpty { | ||
| return streamingData | ||
| } else { | ||
| if let videoInfo = try? await loadAdditionalVideoInfos(forClient: .webEmbed), let streamingData = videoInfo.streamingData { | ||
| _videoInfos = [videoInfo] | ||
| return [streamingData] | ||
| } | ||
|
|
||
| try await bypassAgeGate() | ||
| let streamingData = try await videoInfos.compactMap { $0.streamingData } | ||
| if !streamingData.isEmpty { | ||
|
|
@@ -355,7 +361,7 @@ public class YouTube { | |
| let signatureTimestamp = try await signatureTimestamp | ||
| let ytcfg = try await ytcfg | ||
|
|
||
| let innertubeClients: [InnerTube.ClientType] = [.androidVR, .webSafari, .web] | ||
| let innertubeClients: [InnerTube.ClientType] = [.visionOS, .web] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Keep At Line 318, Iterate over 🤖 Prompt for AI Agents
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not fixing this in PR #140: the same compactMap/zip behavior exists on current origin/main, and applySignature currently does not use the mismatched VideoInfo, so there is no behavioral regression. The pairing should be cleaned up separately if that metadata becomes relevant.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is not a regression from PR Would you like me to open a follow-up GitHub issue for the pairing cleanup? ✏️ Learnings added
You are interacting with an AI system.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
repo="alexeichhorn/YouTubeKit"
body=$(cat <<'EOF'
## Summary
Keep each `InnerTube.VideoInfo` paired with its originating `InnerTube.StreamingData` during local stream extraction.
## Rationale
In `Sources/YouTubeKit/YouTube.swift`, `streamingData` removes `VideoInfo` responses that do not contain `StreamingData`. The `streams` implementation then uses `zip(allStreamingData, videoInfos)`. If an earlier response has no streaming data, this can pair a later stream manifest with the wrong `VideoInfo`.
`Extraction.applySignature(streamManifest:videoInfo:js:)` does not currently use the supplied `VideoInfo`, so this does not cause a known behavior change. The code should be corrected before stream processing depends on `VideoInfo` metadata.
## Affected area
- `Sources/YouTubeKit/YouTube.swift`
- `YouTube.streamingData`
- Local extraction in `YouTube.streams`
## Required changes
Use a representation that preserves the `VideoInfo` and `StreamingData` relationship. For example, iterate over `videoInfos` and skip responses without `streamingData`, or return and consume paired values.
## Acceptance criteria
- Each processed `StreamingData` uses its source `VideoInfo`.
- Responses without `streamingData` do not change the pairing of later responses.
- The change does not alter the intended fallback behavior.
## Backlinks
- PR `#140`: https://github.com/alexeichhorn/YouTubeKit/pull/140
- Review comment: https://github.com/alexeichhorn/YouTubeKit/pull/140#discussion_r3805570439
- Requested by: `@alexeichhorn`
EOF
)
gh issue create \
--repo "$repo" \
--title "Preserve VideoInfo and StreamingData pairing during local extraction" \
--body "$body" \
--assignee "alexeichhorn"Length of output: 1930
You are interacting with an AI system. |
||
|
|
||
| let results: [Result<InnerTube.VideoInfo, Error>] = await innertubeClients.concurrentMap { [videoID, useOAuth, allowOAuthCache] client in | ||
| let innertube = InnerTube(client: client, signatureTimestamp: signatureTimestamp, ytcfg: ytcfg, useOAuth: useOAuth, allowCache: allowOAuthCache) | ||
|
|
@@ -402,7 +408,11 @@ public class YouTube { | |
|
|
||
| private func loadAdditionalVideoInfos(forClient client: InnerTube.ClientType) async throws -> InnerTube.VideoInfo { | ||
| let signatureTimestamp = try await signatureTimestamp | ||
| let ytcfg = try await ytcfg | ||
| let ytcfg = if client == .webEmbed { | ||
| try await Extraction.extractYtCfg(from: embedHTML) | ||
| } else { | ||
| try await ytcfg | ||
| } | ||
| let innertube = InnerTube(client: client, signatureTimestamp: signatureTimestamp, ytcfg: ytcfg, useOAuth: useOAuth, allowCache: allowOAuthCache) | ||
| let videoInfo = try await innertube.player(videoID: videoID) | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The VISIONOS client returns no
streamingDatafor made-for-kids videos, while the remaining WEB client's media URLs require a GVS PO token that this library never obtains. Removing the Safari client therefore leaves local extraction for these videos either without streaming data or with URLs that return HTTP 403; this includes the existingtestSampleVideoMadeForKidscase.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a regression from this PR: the same made-for-kids test fails with zero streams on the freshly fetched origin/main, where webSafari is still enabled. The underlying issue is real, but retaining webSafari no longer provides a usable extraction path.