|
|
||
|---|---|---|
| .forgejo/workflows | ||
| cli | ||
| codegen | ||
| downloader | ||
| notes | ||
| src | ||
| testfiles | ||
| tests | ||
| .editorconfig | ||
| .gitignore | ||
| .gitleaks.toml | ||
| .pre-commit-config.yaml | ||
| Cargo.toml | ||
| CHANGELOG.md | ||
| cliff.toml | ||
| DEVELOPMENT.md | ||
| Justfile | ||
| LICENSE | ||
| README.md | ||
| renovate.json | ||
RustyPipe is a fully featured Rust client for the public YouTube / YouTube Music API (Innertube), inspired by NewPipe. It lets you fetch videos, streams, playlists, channels, search results, music metadata and more, without an API key and without the official YouTube SDK.
About this fork
This is a maintained fork of the upstream RustyPipe by ThetaDev (GPL-3.0). All credit for the original library goes to the upstream author and contributors.
The fork exists to keep the player pipeline working against YouTube's frequently rotating
player.js. The notable changes (see thesulkta-sig-portbranch and the*-sulktatags) are:
- Soft-fail signature deobfuscation — when YouTube ships a
player.jsshape the built-in regexes don't recognise, the player path no longer aborts. Only the load-bearing signature timestamp is treated as required; the actual sig/nsig functions are best-effort and a warning is logged instead of failing the whole call.- iOS-first default client order — the iOS Innertube path returns pre-signed stream URLs (no cipher/throttling params) and needs neither device attestation nor signature deobfuscation, so it is the most reliable "just works" default. Android is kept in rotation only when BotGuard / PO-token signing is wired up.
If you just want the canonical library, use upstream. If you want the resilience patches above, this fork is a drop-in replacement at the same API.
Features
YouTube
- Player (video/audio streams, subtitles)
- VideoDetails (metadata, comments, recommended videos)
- Playlist
- Channel (videos, shorts, livestreams, playlists, info, search)
- ChannelRSS
- Search (with filters)
- Search suggestions
- Trending
- URL resolver
- Subscriptions
- Playback history
YouTube Music
- Playlist
- Album
- Artist
- Search
- Search suggestions
- Radio
- Track details (lyrics, recommendations)
- Moods/Genres
- Charts
- New (albums, music videos)
- Saved items
- Playback history
Getting started
The RustyPipe library works as follows: first you instantiate a RustyPipe client. You can
either create it with default options or use RustyPipe::builder() to customize it.
For fetching data you start with a new RustyPipe query object (rp.query()). The query
object holds options for an individual query (e.g. content language or country). You can
adjust these options with setter methods. Finally call your query method to fetch the data
you need.
All query methods are async; you need the tokio runtime to execute them.
let rp = RustyPipe::new();
let rp = RustyPipe::builder().storage_dir("/app/data").build().unwrap();
let channel = rp.query().lang(Language::De).channel_videos("UCl2mFZoRqjw_ELax4Yisf6w").await.unwrap();
Here are a few examples to get you started:
Cargo.toml
[dependencies]
rustypipe = "0.11"
tokio = { version = "1.20.0", features = ["macros", "rt-multi-thread"] }
Watch a video
use std::process::Command;
use rustypipe::{client::RustyPipe, param::StreamFilter};
#[tokio::main]
async fn main() {
// Create a client
let rp = RustyPipe::new();
// Fetch the player
let player = rp.query().player("pPvd8UxmSbQ").await.unwrap();
// Select the best streams
let (video, audio) = player.select_video_audio_stream(&StreamFilter::default());
// Open mpv player
let mut args = vec![video.expect("no video stream").url.to_owned()];
if let Some(audio) = audio {
args.push(format!("--audio-file={}", audio.url));
}
Command::new("mpv").args(args).output().unwrap();
}
Get a playlist
use rustypipe::client::RustyPipe
#[tokio::main]
async fn main() {
// Create a client
let rp = RustyPipe::new();
// Get the playlist
let playlist = rp
.query()
.playlist("PL2_OBreMn7FrsiSW0VDZjdq0xqUKkZYHT")
.await
.unwrap();
// Get all items (maximum: 1000)
playlist.videos.extend_limit(rp.query(), 1000).await.unwrap();
println!("Name: {}", playlist.name);
println!("Author: {}", playlist.channel.unwrap().name);
println!("Last update: {}", playlist.last_update.unwrap());
playlist
.videos
.items
.iter()
.for_each(|v| println!("[{}] {} ({}s)", v.id, v.name, v.length));
}
Get a channel
use rustypipe::client::RustyPipe
#[tokio::main]
async fn main() {
// Create a client
let rp = RustyPipe::new();
// Get the channel
let channel = rp
.query()
.channel_videos("UCl2mFZoRqjw_ELax4Yisf6w")
.await
.unwrap();
println!("Name: {}", channel.name);
println!("Description: {}", channel.description);
println!("Subscribers: {}", channel.subscriber_count.unwrap());
channel
.content
.items
.iter()
.for_each(|v| println!("[{}] {} ({}s)", v.id, v.name, v.length.unwrap()));
}
Building and testing
RustyPipe is a standard Cargo workspace (the library plus the rustypipe-cli and
rustypipe-downloader companion crates).
# Build everything
cargo build --workspace
# Run the offline unit + snapshot tests (no network access required)
cargo test --workspace
# Run the fork's end-to-end smoke tests against live YouTube
# (network access required; some clients may be rate-limited from datacenter IPs)
cargo test --test sulkta_smoke -- --nocapture
Crate features
Some features of RustyPipe are gated behind Cargo features to avoid compiling unneeded dependencies.
rssFetch a channel's RSS feed, which is faster than fetching the channel pageuserdataAdd functions to fetch YouTube user data (watch history, subscriptions, music library)
You can also choose the TLS library used for making web requests using the same features
as the reqwest crate (default-tls, native-tls, native-tls-alpn,
native-tls-vendored, rustls-tls-webpki-roots, rustls-tls-native-roots).
Cache storage
The RustyPipe cache holds the current version numbers for all clients, the JavaScript code used to deobfuscate video URLs and the authentication token/cookies. Never share the contents of the cache if you are using authentication.
By default the cache is written to a JSON file named rustypipe_cache.json in the
current working directory. This path can be changed with the storage_dir option of the
RustyPipeBuilder. The RustyPipe CLI stores its cache in the userdata folder. The full
path on Linux is ~/.local/share/rustypipe/rustypipe_cache.json.
You can integrate your own cache storage backend (e.g. database storage) by implementing
the CacheStorage trait.
Reports
RustyPipe has a builtin error reporting system. If a YouTube response cannot be
deserialized or parsed, the original response data along with some request metadata is
written to a JSON file in the folder rustypipe_reports, located in RustyPipe's storage
directory (current folder by default, ~/.local/share/rustypipe for the CLI).
When submitting a bug report, you can share this report to help resolve the issue.
RustyPipe reports come in 3 severity levels:
- DBG (no error occurred, report creation was enabled by the
RustyPipeQuery::reportquery option) - WRN (parts of the response could not be deserialized/parsed, response data may be incomplete)
- ERR (entire response could not be deserialized/parsed, RustyPipe returned an error)
PO tokens
Since August 2024 YouTube requires PO tokens to access streams from web-based clients (Desktop, Mobile). Otherwise streams will return a 403 error.
Generating PO tokens requires a simulated browser environment, which would be too large to include in RustyPipe directly.
Therefore, PO token generation is handled by a separate CLI application
(rustypipe-botguard) which is called
by the RustyPipe crate. RustyPipe automatically detects the rustypipe-botguard binary if
it is located in PATH or the current working directory. If your rustypipe-botguard binary
is located at a different path, you can specify it with the .botguard_bin(path) option.
Authentication
RustyPipe supports authenticating with your YouTube account to access age-restricted/private videos and user information. There are 2 supported authentication methods: OAuth and cookies.
To execute a query with authentication, use the .authenticated() query option. This
option is enabled by default for queries that always require authentication like fetching
user data. RustyPipe may automatically use authentication in case a video is
age-restricted or your IP address is banned by YouTube. If you never want to use
authentication, set the .unauthenticated() query option.
OAuth
OAuth is the authentication method used by the YouTube TV client. It is more user-friendly than extracting cookies, however it only works with the TV client. This means that you can only fetch videos and not access any user data.
To login using OAuth, you first have to get a new device code using the
rp.user_auth_get_code() function. You can then enter the code on
https://google.com/device and log in with your Google account. After generating the
code, you can call the rp.user_auth_wait_for_login() function which waits until the
user has logged in and stores the authentication token in the cache.
Cookies
Authenticating with cookies allows you to use the functionality of the YouTube/YouTube Music Desktop client. You can fetch your subscribed channels, playlists and your music collection. You can also fetch videos using the Desktop client, including private videos, as long as you have access to them.
To authenticate with cookies you have to log into YouTube in a fresh browser session (open Incognito/Private mode). Then extract the cookies from the developer tools or by using a browser extension. Close the browser window after extracting the cookies to prevent YouTube from rotating the cookies.
You can then add the cookies to your RustyPipe client using the user_auth_set_cookie or
user_auth_set_cookie_txt function. The cookies are stored in the cache file. To log out,
use the function user_auth_remove_cookie.
Contributing
Issues and pull requests are welcome. Please keep changes focused, run
cargo fmt, cargo clippy --workspace and cargo test --workspace before submitting,
and include a snapshot/unit test for any new parsing behaviour. If your change concerns
the player or signature pipeline, the live smoke tests (cargo test --test sulkta_smoke)
are a useful sanity check.
License
RustyPipe is licensed under the GNU General Public License v3.0 or later (GPL-3.0-or-later). See LICENSE for the full text. As a fork of the upstream RustyPipe project, this repository preserves the original license and attribution.
This project is not affiliated with, sponsored by, or endorsed by YouTube or Google.
