### Example usage of media_urls_from_tweet_url Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/fn.media_urls_from_tweet_url.html Demonstrates calling the function with a sample tweet URL. The `?` operator propagates any `TwitterError`. ```rust let urls = tweet2media::media_urls_from_tweet_url( "https://x.com/example/status/1234567890123456789", )?; ``` -------------------------------- ### Fetch media URLs from a tweet URL asynchronously Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/fn.async_media_urls_from_tweet_url.html This example demonstrates how to call the async_media_urls_from_tweet_url function to retrieve media URLs from a specific tweet URL. ```rust let urls = tweet2media::async_media_urls_from_tweet_url( "https://x.com/example/status/1234567890123456789", ) .await?; ``` -------------------------------- ### Quick start: fetch media URLs from a public tweet URL Source: https://docs.rs/tweet2media Uses the blocking convenience function media_urls_from_tweet_url to extract media URLs from a public tweet. Returns a Result, so the caller must handle errors. Prints each URL to stdout. ```rust let urls = tweet2media::media_urls_from_tweet_url( "https://x.com/example/status/1234567890123456789", )?; for url in urls { println!("{url}"); } ``` -------------------------------- ### tweet_id_from_url doctest example Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/shared.rs.html Shows how to extract a numeric tweet ID from a Twitter/X status URL using tweet_id_from_url. The example uses a URL with an extra /photo/1 segment and asserts the result is "1234567890". ```rust ``` # fn main() -> Result<(), tweet2media::TwitterError> { let id = tweet2media::twitter::tweet_id_from_url( "https://x.com/example/status/1234567890/photo/1", )?; assert_eq!(id, "1234567890"); # Ok(()) # } ``` ``` -------------------------------- ### Async client usage Source: https://docs.rs/tweet2media/latest/src/tweet2media/lib.rs.html Uses the async `AsyncTwitterClient` with the builder to fetch media URLs from a tweet URL. Mirrors the blocking API and uses `reqwest::Client` internally. ```rust async fn run() -> Result<(), tweet2media::TwitterError> { let mut client = tweet2media::AsyncTwitterClient::builder().build()?; let urls = client .media_urls_from_tweet_url_mut("https://x.com/example/status/1234567890123456789") .await?; let _ = urls; Ok(()) } ``` -------------------------------- ### Retrieve guest token and store in cookies Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/async_client.rs.html Sends a POST request to obtain a guest token, parses the response, and stores the token in cookies and as a field. Requires a valid response with a 'guest_token' field, otherwise returns a TwitterError::UnexpectedResponse. ```rust .header(CONTENT_TYPE, HeaderValue::from_static("application/json")) .send() .await?; let data = self.handle_response(response).await?; let token = data .get("guest_token") .and_then(Value::as_str) .ok_or_else(|| TwitterError::UnexpectedResponse("missing guest_token".into()))? .to_string(); self.cookies.insert("gt".to_string(), token.clone()); self.guest_token = Some(token); Ok(()) ``` -------------------------------- ### AsyncTwitterClient::builder Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.AsyncTwitterClient.html Creates a builder for configuring an async client. ```APIDOC ## AsyncTwitterClient::builder ### Description Creates a builder for configuring an async client. ### Method builder ### Signature pub fn builder() -> AsyncTwitterClientBuilder ### Returns AsyncTwitterClientBuilder - A builder for configuring an async client. ``` -------------------------------- ### Build TwitterClient Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/blocking.rs.html Constructs a new TwitterClient. If no CSRF cookie is provided, a random one is generated for guest-session requests. ```rust pub fn build(mut self) -> Result { ensure_csrf_cookie(&mut self.cookies); let client = match self.client { Some(client) => client, None => Client::builder() .timeout(self.timeout) .redirect(reqwest::redirect::Policy::limited(10)) .build()?, }; Ok(TwitterClient { client, cookies: self.cookies, user_agent: self.user_agent, guest_token: None, client_transaction_id: self.client_transaction_id, }) } } ``` -------------------------------- ### Reuse a TwitterClient for multiple tweets Source: https://docs.rs/tweet2media Builds a TwitterClient and reuses it to fetch media URLs from a tweet URL and a tweet ID. Keeping the client in memory preserves guest tokens and cookies between requests. ```rust let mut client = tweet2media::TwitterClient::builder().build()?; let first = client.media_urls_from_tweet_url_mut( "https://x.com/example/status/1234567890123456789", )?; let second = client.media_urls_from_tweet_id("9876543210987654321")?; ``` -------------------------------- ### Construct Twitter API Headers Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/shared.rs.html Builds a HeaderMap with necessary authentication and request metadata for Twitter API calls. ```rust pub(super) fn twitter_headers( user_agent: &str, csrf_token: &str, auth: bool, logged_in: bool, guest_token: Option<&str>, client_transaction_id: Option<&str>, cookies: &BTreeMap, ) -> Result { let mut headers = HeaderMap::new(); headers.insert(ACCEPT, HeaderValue::from_static("*/*")); headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); headers.insert(REFERER, HeaderValue::from_static("https://x.com/")); headers.insert(USER_AGENT, HeaderValue::from_str(user_agent)?); headers.insert(AUTHORIZATION, HeaderValue::from_static(BEARER_TOKEN)); headers.insert("x-csrf-token", HeaderValue::from_str(csrf_token)?); headers.insert("x-twitter-client-language", HeaderValue::from_static("en")); headers.insert("x-twitter-active-user", HeaderValue::from_static("yes")); headers.insert("Sec-Fetch-Dest", HeaderValue::from_static("empty")); headers.insert("Sec-Fetch-Mode", HeaderValue::from_static("cors")); headers.insert("Sec-Fetch-Site", HeaderValue::from_static("same-origin")); if auth { if logged_in { headers.insert( "x-twitter-auth-type", HeaderValue::from_static("OAuth2Session"), ); if let Some(transaction_id) = client_transaction_id { headers.insert( "x-client-transaction-id", HeaderValue::from_str(transaction_id)?, ); } } else if let Some(guest_token) = guest_token { headers.insert("x-guest-token", HeaderValue::from_str(guest_token)?); } } if !cookies.is_empty() { headers.insert(COOKIE, HeaderValue::from_str(&cookie_header(cookies))?); } Ok(headers) } ``` -------------------------------- ### async_media_urls_from_tweet_url Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/fn.async_media_urls_from_tweet_url.html Fetches a tweet URL asynchronously through an anonymous guest session. This is the async counterpart to media_urls_from_tweet_url. It creates a new AsyncTwitterClient for one request. Build and reuse a client when fetching multiple tweets. For authenticated-only tweets, use AsyncTwitterClient::builder and provide cookies from a logged-in browser session (auth_token and ct0 are the important ones). ```APIDOC ## async_media_urls_from_tweet_url ### Description Fetches a tweet URL asynchronously through an anonymous guest session. This is the async counterpart to `crate::media_urls_from_tweet_url`. It creates a new `AsyncTwitterClient` for one request. Build and reuse a client when fetching multiple tweets. For authenticated-only tweets, use `AsyncTwitterClient::builder` and provide cookies from a logged-in browser session (`auth_token` and `ct0` are the important ones). ### Function Signature ```rust pub async fn async_media_urls_from_tweet_url( tweet_url: &str, ) -> Result, TwitterError> ``` ### Parameters - **tweet_url** (`&str`) - Required - The URL of the tweet to fetch media URLs from. ### Returns - `Result, TwitterError>` - A vector of media URLs on success, or a `TwitterError` on failure. ### Example ```rust let urls = tweet2media::async_media_urls_from_tweet_url( "https://x.com/example/status/1234567890123456789", ) .await?; ``` ### Notes - This function creates a new `AsyncTwitterClient` for each request. For multiple tweets, build and reuse a client. - For authenticated-only tweets, use `AsyncTwitterClient::builder` and provide cookies from a logged-in browser session (`auth_token` and `ct0` are the important ones). ``` -------------------------------- ### Media Extraction Methods Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/blocking.rs.html Methods for fetching media URLs from tweet URLs or IDs. Use the _mut variants to preserve the client for reuse. ```rust impl TwitterClient { /// Creates a builder for configuring a blocking client. pub fn builder() -> TwitterClientBuilder { TwitterClientBuilder::default() } /// Fetches media URLs from a tweet URL, consuming the client. /// /// This is convenient when you only need one request from an already-built client. Use /// [`Self::media_urls_from_tweet_url_mut`] if you want to keep using the same client after the /// request. pub fn media_urls_from_tweet_url( mut self, tweet_url: &str, ) -> Result, TwitterError> { self.media_urls_from_tweet_url_mut(tweet_url) } /// Fetches media URLs from a tweet URL while preserving the client for reuse. /// /// The URL must contain a numeric `/status/` path segment. Both `x.com` and `twitter.com` /// URLs are accepted. pub fn media_urls_from_tweet_url_mut( &mut self, tweet_url: &str, ) -> Result, TwitterError> { let tweet_id = tweet_id_from_url(tweet_url)?; self.media_urls_from_tweet_id(&tweet_id) } /// Fetches media URLs from a numeric tweet ID. /// /// Returns an empty vector when the tweet is accessible but contains no supported image, GIF, or /// video media. pub fn media_urls_from_tweet_id( &mut self, tweet_id: &str, ) -> Result, TwitterError> { let tweet = self.tweet_result_by_rest_id(tweet_id)?; Ok(extract_media_urls(&tweet)) } ``` -------------------------------- ### Apply headers to request builder Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/async_client.rs.html Builds headers using twitter_headers and adds Origin and X-Requested-With headers. Returns a Result with the modified RequestBuilder. ```rust let headers = twitter_headers( &self.user_agent, self.csrf_token(), auth, self.logged_in(), self.guest_token.as_deref(), self.client_transaction_id.as_deref(), &self.cookies, )?; Ok(request .headers(headers) .header("Origin", WEB_ROOT) .header("X-Requested-With", "XMLHttpRequest")) ``` -------------------------------- ### Define Features Pagination Configuration Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/shared.rs.html Returns a JSON object containing the feature flags required for Twitter pagination requests. ```rust fn features_pagination() -> Value { json!({ "payments_enabled": false, "rweb_video_screen_enabled": false, "rweb_xchat_enabled": false, "profile_label_improvements_pcf_label_in_post_enabled": true, "rweb_tipjar_consumption_enabled": true, "verified_phone_label_enabled": false, "creator_subscriptions_tweet_preview_api_enabled": true, "responsive_web_graphql_timeline_navigation_enabled": true, "responsive_web_graphql_skip_user_profile_image_extensions_enabled": false, "premium_content_api_read_enabled": false, "communities_web_enable_tweet_community_results_fetch": true, "c9s_tweet_anatomy_moderator_badge_enabled": true, "responsive_web_grok_analyze_button_fetch_trends_enabled": false, "responsive_web_grok_analyze_post_followups_enabled": true, "responsive_web_jetfuel_frame": true, "responsive_web_grok_share_attachment_enabled": true, "articles_preview_enabled": true, "responsive_web_edit_tweet_api_enabled": true, "graphql_is_translatable_rweb_tweet_is_translatable_enabled": true, "view_counts_everywhere_api_enabled": true, "longform_notetweets_consumption_enabled": true, "responsive_web_twitter_article_tweet_consumption_enabled": true, "tweet_awards_web_tipping_enabled": false, "responsive_web_grok_show_grok_translated_post": false, "responsive_web_grok_analysis_button_from_backend": true, "creator_subscriptions_quote_tweet_preview_enabled": false, "freedom_of_speech_not_reach_fetch_enabled": true, "standardized_nudges_misinfo": true, "tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled": true, "longform_notetweets_rich_text_read_enabled": true, "longform_notetweets_inline_media_enabled": true, "responsive_web_grok_image_annotation_enabled": true, "responsive_web_grok_imagine_annotation_enabled": true, "responsive_web_grok_community_note_auto_translation_is_enabled": false, "responsive_web_enhance_cards_enabled": false }) } ``` -------------------------------- ### TwitterClientBuilder::build Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/blocking.rs.html Builds a blocking TwitterClient from the builder configuration. ```APIDOC ## TwitterClientBuilder::build ### Description Builds a blocking `TwitterClient`. ### Method Builder method ### Endpoint `TwitterClientBuilder::build() -> Result` ### Parameters None. ### Response Returns a `Result` containing the built `TwitterClient` or a `TwitterError`. ``` -------------------------------- ### Unit tests for media extraction functions Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/media.rs.html Contains two unit tests: rewrites_image_to_original verifies original_image_url behavior, and extracts_images_and_best_video checks extract_media_urls with a sample tweet JSON, expecting the original image URL and the highest-bitrate MP4 video URL. ```Rust #[cfg(test)] mod tests { use super::*; use serde_json::json; #[test] fn rewrites_image_to_original() { assert_eq!( original_image_url("https://pbs.twimg.com/media/abc.jpg"), a "https://pbs.twimg.com/media/abc?format=jpg&name=orig" a ); a assert_eq!( a original_image_url("https://pbs.twimg.com/media/abc?format=png&name=small"), a "https://pbs.twimg.com/media/abc?format=png&name=orig" a ); a } a #[test] a fn extracts_images_and_best_video() { a let tweet = json!({ a "legacy": { a "extended_entities": { a "media": [ a {"media_url_https": "https://pbs.twimg.com/media/a.jpg"}, a { "video_info": { "variants": [ {"content_type": "application/x-mpegURL", "url": "https://video.twimg.com/a.m3u8"}, a {"content_type": "video/mp4", "bitrate": 256000, "url": "https://video.twimg.com/a-low.mp4"}, a {"content_type": "video/mp4", "bitrate": 832000, "url": "https://video.twimg.com/a-high.mp4"} a ] a }, a "media_url_https": "https://pbs.twimg.com/media/video-thumb.jpg" a } a ] a } a } a }); a assert_eq!( a extract_media_urls(&tweet), a vec![ a "https://pbs.twimg.com/media/a?format=jpg&name=orig".to_string(), a "https://video.twimg.com/a-high.mp4".to_string() a ] a ); a } } ``` -------------------------------- ### AsyncTwitterClientBuilder::build Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.AsyncTwitterClientBuilder.html Builds an asynchronous AsyncTwitterClient, generating a random ct0 cookie if none was provided. ```APIDOC ## AsyncTwitterClientBuilder::build ### Description Builds an asynchronous `AsyncTwitterClient`. If no `ct0` CSRF cookie was provided, a random one is generated for guest-session requests. Construction can fail if the underlying reqwest client cannot be built. ### Method pub fn build(self) -> Result ### Returns Result - The built client or an error. ``` -------------------------------- ### Internal API Request Handling Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/blocking.rs.html Internal methods for managing authentication, CSRF tokens, and executing JSON requests. ```rust fn logged_in(&self) -> bool { self.cookies.contains_key("auth_token") } fn csrf_token(&self) -> &str { self.cookies.get("ct0").map(String::as_str).unwrap_or("") } fn tweet_result_by_rest_id(&mut self, tweet_id: &str) -> Result { let endpoint = "/graphql/qxWQxcMLiTPcavz9Qy5hwQ/TweetResultByRestId"; let url = format!("{API_ROOT}{endpoint}"); let params = tweet_result_params(tweet_id); let data = self.call_json(Method::GET, &url, Some(¶ms), true)?; tweet_from_result_response(data) } fn call_json( &mut self, method: Method, url: &str, params: Option<&[(&str, String)]>, auth: bool, ) -> Result { if auth && !self.logged_in() && self.guest_token.is_none() { self.authenticate_guest()?; } let mut request = self.client.request(method, url); request = self.apply_headers(request, auth)?; if let Some(params) = params { request = request.query(params); } let response = request.send()?; self.handle_response(response) } fn authenticate_guest(&mut self) -> Result<(), TwitterError> { let url = format!("{GUEST_API_ROOT}/1.1/guest/activate.json"); let response = self .client .post(&url) .header(AUTHORIZATION, HeaderValue::from_static(BEARER_TOKEN)) .header(USER_AGENT, HeaderValue::from_str(&self.user_agent)?) .header(ACCEPT, HeaderValue::from_static("*/*")) .header(CONTENT_TYPE, HeaderValue::from_static("application/json")) .send()?; let data = self.handle_response(response)?; let token = data ``` -------------------------------- ### TwitterClient::builder Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.TwitterClient.html Creates a builder for configuring a blocking TwitterClient. ```APIDOC ## TwitterClient::builder ### Description Creates a builder for configuring a blocking TwitterClient. ### Method builder ### Signature pub fn builder() -> TwitterClientBuilder ### Parameters None ### Returns - **TwitterClientBuilder** - A builder for configuring a blocking client. ``` -------------------------------- ### AsyncTwitterClientBuilder::user_agent Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.AsyncTwitterClientBuilder.html Sets the User-Agent header used for Twitter/X requests, defaulting to a desktop browser user agent. ```APIDOC ## AsyncTwitterClientBuilder::user_agent ### Description Sets the `User-Agent` header used for Twitter/X requests. The default is a desktop browser user agent. Supplying the same user agent as the browser session that provided your cookies can help avoid authentication mismatches. ### Method pub fn user_agent(self, user_agent: impl Into) -> Self ### Parameters - **user_agent** (impl Into) - Required - The user agent string to set. ### Returns Self - The builder instance for chaining. ``` -------------------------------- ### TwitterClientBuilder::build Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/blocking.rs.html Builds the TwitterClient. If no `ct0` CSRF cookie was provided, a random one is generated for guest-session requests. Construction can fail if the underlying reqwest client cannot be built. ```APIDOC ## TwitterClientBuilder::build ### Description Builds the TwitterClient. If no `ct0` CSRF cookie was provided, a random one is generated for guest-session requests. Construction can fail if the underlying reqwest client cannot be built. ### Method build ### Signature pub fn build(mut self) -> Result ### Returns - **Result** - The configured client, or an error. ``` -------------------------------- ### Async usage with AsyncTwitterClient Source: https://docs.rs/tweet2media Uses the async API, which mirrors the blocking API and uses reqwest::Client internally. The method is awaited and returns a Result. ```rust let mut client = tweet2media::AsyncTwitterClient::builder().build()?; let urls = client .media_urls_from_tweet_url_mut("https://x.com/example/status/1234567890123456789") .await?; ``` -------------------------------- ### AsyncTwitterClient::media_urls_from_tweet_url Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.AsyncTwitterClient.html Fetches media URLs from a tweet URL, consuming the client. This is convenient when you only need one request from an already-built client. ```APIDOC ## AsyncTwitterClient::media_urls_from_tweet_url ### Description Fetches media URLs from a tweet URL, consuming the client. This is convenient when you only need one request from an already-built client. Use `media_urls_from_tweet_url_mut` if you want to keep using the same client after the request. ### Method media_urls_from_tweet_url ### Signature pub async fn media_urls_from_tweet_url(self, tweet_url: &str) -> Result, TwitterError> ### Parameters - **tweet_url** (&str) - Required - The URL of the tweet to fetch media URLs from. ### Returns Result, TwitterError> - A vector of media URLs on success, or a TwitterError on failure. ``` -------------------------------- ### TwitterClient::media_urls_from_tweet_url_mut Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.TwitterClient.html Fetches media URLs from a tweet URL while preserving the client for reuse. Accepts both x.com and twitter.com URLs. ```APIDOC ## TwitterClient::media_urls_from_tweet_url_mut ### Description Fetches media URLs from a tweet URL while preserving the client for reuse. The URL must contain a numeric /status/ path segment. Both x.com and twitter.com URLs are accepted. ### Method media_urls_from_tweet_url_mut ### Signature pub fn media_urls_from_tweet_url_mut(&mut self, tweet_url: &str) -> Result, TwitterError> ### Parameters - **tweet_url** (&str) - Required - The URL of the tweet to fetch media from. ### Returns - **Result, TwitterError>** - A vector of media URLs on success, or a TwitterError on failure. ``` -------------------------------- ### AsyncTwitterClient::media_urls_from_tweet_url_mut Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.AsyncTwitterClient.html Fetches media URLs from a tweet URL while preserving the client for reuse. The URL must contain a numeric `/status/` path segment. ```APIDOC ## AsyncTwitterClient::media_urls_from_tweet_url_mut ### Description Fetches media URLs from a tweet URL while preserving the client for reuse. The URL must contain a numeric `/status/` path segment. Both `x.com` and `twitter.com` URLs are accepted. ### Method media_urls_from_tweet_url_mut ### Signature pub async fn media_urls_from_tweet_url_mut(&mut self, tweet_url: &str) -> Result, TwitterError> ### Parameters - **tweet_url** (&str) - Required - The URL of the tweet to fetch media URLs from. Must contain a numeric `/status/` path segment. ### Returns Result, TwitterError> - A vector of media URLs on success, or a TwitterError on failure. ``` -------------------------------- ### TwitterClientBuilder::build Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.TwitterClientBuilder.html Builds a blocking TwitterClient. If no ct0 CSRF cookie was provided, a random one is generated for guest-session requests. Construction can fail if the underlying reqwest client cannot be built. ```APIDOC ## build ### Description Builds a blocking `TwitterClient`. If no `ct0` CSRF cookie was provided, a random one is generated for guest-session requests. Construction can fail if the underlying reqwest client cannot be built. ### Method pub fn build(self) -> Result ### Returns Result - The built TwitterClient or an error. ``` -------------------------------- ### Twitter/X media extraction module definition Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/mod.rs.html The module structure for Twitter/X media extraction, re-exporting async and blocking clients along with shared utilities. ```rust 1//! Twitter/X media extraction clients and helpers. 2//! 3//! This module contains both blocking and async clients. The crate root re-exports 4//! the main types and functions, so most users can import from `tweet2media` 5//! directly instead of this module. 6 7mod async_client; 8mod blocking; 9mod media; 10mod shared; 11 12pub use async_client::{ 13 async_media_urls_from_tweet_url, AsyncTwitterClient, AsyncTwitterClientBuilder, 14}; 15pub use blocking::{media_urls_from_tweet_url, TwitterClient, TwitterClientBuilder}; 16pub use shared::{tweet_id_from_url, TwitterError}; 17 18#[cfg(test)] 19mod tests { 20 use super::tweet_id_from_url; 21 22 #[test] 23 fn parses_status_url() { 24 assert_eq!( 25 tweet_id_from_url("https://x.com/user/status/1234567890/photo/1").unwrap(), 26 "1234567890" 27 ); 28 assert_eq!( 29 tweet_id_from_url("https://twitter.com/i/web/status/42").unwrap(), 30 "42" 31 ); 32 } 33} ``` -------------------------------- ### AsyncTwitterClientBuilder::cookie_header Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.AsyncTwitterClientBuilder.html Adds cookies from a standard Cookie header string, such as "auth_token=...; ct0=...". ```APIDOC ## AsyncTwitterClientBuilder::cookie_header ### Description Adds cookies from a normal `Cookie` header string, e.g. `"auth_token=...; ct0=..."`. ### Method pub fn cookie_header(self, cookie_header: &str) -> Self ### Parameters - **cookie_header** (&str) - Required - The Cookie header string containing cookies. ### Returns Self - The builder instance for chaining. ``` -------------------------------- ### AsyncTwitterClientBuilder::client Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.AsyncTwitterClientBuilder.html Uses a caller-provided non-blocking reqwest::Client for advanced configuration like proxies or custom TLS. ```APIDOC ## AsyncTwitterClientBuilder::client ### Description Uses a caller-provided non-blocking `reqwest::Client`. This is useful for async applications that need proxy configuration, proxy authentication, custom TLS settings, custom DNS, or shared connection pools. The library still adds request-specific Twitter/X headers and its own in-memory `Cookie` header on each request. ### Method pub fn client(self, client: Client) -> Self ### Parameters - **client** (Client) - Required - The preconfigured reqwest client. ### Returns Self - The builder instance for chaining. ``` -------------------------------- ### Handle response and parse JSON Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/async_client.rs.html Updates cookies from response headers, reads the response text, and parses it as JSON. On parse failure, returns a JSON object with an error message. Then calls parse_response_json. ```rust self.update_cookies(response.headers()); let status = response.status(); let text = response.text().await?; let data: Value = serde_json::from_str(&text).unwrap_or_else(|_| { json!({ "errors": [{ "message": text }] }) }); parse_response_json(status, data, self.logged_in()) ``` -------------------------------- ### media_urls_from_tweet_url Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/fn.media_urls_from_tweet_url.html Fetches a tweet URL through an anonymous guest session and returns all image/video media URLs. This convenience function creates a new TwitterClient for one request. For authenticated-only tweets, use TwitterClient::builder with cookies. ```APIDOC ## Function media_urls_from_tweet_url ### Description Fetches a tweet URL through an anonymous guest session and returns all image/video media URLs. This convenience function creates a new TwitterClient for one request. If you are fetching several tweets, build and reuse a client instead so the guest token can be reused. For authenticated-only tweets, use TwitterClient::builder and provide cookies from a logged-in browser session (auth_token and ct0 are the important ones). ### Signature pub fn media_urls_from_tweet_url( tweet_url: &str, ) -> Result, TwitterError> ### Parameters - **tweet_url** (&str) - Required - The URL of the tweet to fetch media URLs from. ### Return Value - Result, TwitterError> - On success, returns a vector of media URLs (strings). On failure, returns a TwitterError. ### Example ```rust let urls = tweet2media::media_urls_from_tweet_url( "https://x.com/example/status/1234567890123456789", )?; ``` ``` -------------------------------- ### Function signature of media_urls_from_tweet_url Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/fn.media_urls_from_tweet_url.html Shows the public signature of the function. It accepts a tweet URL as a string slice and returns a `Result` containing a vector of media URL strings or a `TwitterError`. ```rust pub fn media_urls_from_tweet_url( tweet_url: &str, ) -> Result, TwitterError> ``` -------------------------------- ### TwitterClient Struct Definition Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/blocking.rs.html The core structure for the blocking Twitter/X media extraction client, which stores cookies and a guest token in memory. ```rust #[derive(Debug, Clone)] pub struct TwitterClient { client: Client, cookies: BTreeMap, user_agent: String, guest_token: Option, client_transaction_id: Option, } ``` -------------------------------- ### Generate Tweet Result Parameters Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/shared.rs.html Creates the query parameters required for fetching a specific tweet by ID. ```rust pub(super) fn tweet_result_params(tweet_id: &str) -> Vec<(&'static str, String)> { let mut features = features_pagination(); if let Value::Object(map) = &mut features { map.remove("rweb_video_screen_enabled"); } vec![ ( "variables", json!({ "tweetId": tweet_id, "withCommunity": false, "includePromotedContent": false, "withVoice": false }) .to_string(), ), ("features", features.to_string()), ( "fieldToggles", json!({ "withArticleRichContentState": true, "withArticlePlainText": false, "withGrokAnalyze": false, "withDisallowedReplyControls": false }) .to_string(), ), ] } ``` -------------------------------- ### TwitterClientBuilder::user_agent Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.TwitterClientBuilder.html Sets the User-Agent header used for Twitter/X requests. The default is a desktop browser user agent. ```APIDOC ## user_agent ### Description Sets the `User-Agent` header used for Twitter/X requests. The default is a desktop browser user agent. Supplying the same user agent as the browser session that provided your cookies can help avoid authentication mismatches. ### Method pub fn user_agent(self, user_agent: impl Into) -> Self ### Parameters - **user_agent** (impl Into) - Required - The user agent string. ### Returns Self - The builder for chaining. ``` -------------------------------- ### TwitterClient::media_urls_from_tweet_url Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.TwitterClient.html Fetches media URLs from a tweet URL, consuming the client. Convenient for one-off requests. ```APIDOC ## TwitterClient::media_urls_from_tweet_url ### Description Fetches media URLs from a tweet URL, consuming the client. This is convenient when you only need one request from an already-built client. ### Method media_urls_from_tweet_url ### Signature pub fn media_urls_from_tweet_url(self, tweet_url: &str) -> Result, TwitterError> ### Parameters - **tweet_url** (&str) - Required - The URL of the tweet to fetch media from. ### Returns - **Result, TwitterError>** - A vector of media URLs on success, or a TwitterError on failure. ``` -------------------------------- ### AsyncTwitterClientBuilder::cookie Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.AsyncTwitterClientBuilder.html Adds a single cookie, such as auth_token or ct0, from a logged-in browser session to the client builder. ```APIDOC ## AsyncTwitterClientBuilder::cookie ### Description Adds one cookie, e.g. `auth_token` or `ct0`, from a logged-in browser session. ### Method pub fn cookie(self, name: impl Into, value: impl Into) -> Self ### Parameters - **name** (impl Into) - Required - The cookie name. - **value** (impl Into) - Required - The cookie value. ### Returns Self - The builder instance for chaining. ``` -------------------------------- ### Apply Twitter Request Headers Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/blocking.rs.html The apply_headers function constructs and attaches necessary authentication and session headers to a RequestBuilder, including CSRF tokens and guest tokens. ```rust fn apply_headers( &self, request: RequestBuilder, auth: bool, ) -> Result { let headers = twitter_headers( &self.user_agent, self.csrf_token(), auth, self.logged_in(), self.guest_token.as_deref(), self.client_transaction_id.as_deref(), &self.cookies, )?; Ok(request .headers(headers) .header("Origin", WEB_ROOT) .header("X-Requested-With", "XMLHttpRequest")) } ``` -------------------------------- ### Authenticated tweets: provide browser cookies Source: https://docs.rs/tweet2media Builds a TwitterClient with a cookie header copied from the browser's Cookie request header. The most important cookies are auth_token and ct0. Required for protected, age-restricted, or otherwise authenticated-only tweets. ```rust let mut client = tweet2media::TwitterClient::builder() .cookie_header("auth_token=...; ct0=...") .build()?; let urls = client.media_urls_from_tweet_url_mut( "https://x.com/example/status/1234567890123456789", )?; ``` -------------------------------- ### Update Cookies from Headers Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/blocking.rs.html The update_cookies function provides a wrapper to synchronize internal cookie storage with headers received from a response. ```rust fn update_cookies(&mut self, headers: &HeaderMap) { update_cookies_from_headers(&mut self.cookies, headers); } ``` -------------------------------- ### Update cookies from headers Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/async_client.rs.html Calls update_cookies_from_headers to update the internal cookie store from the response headers. ```rust update_cookies_from_headers(&mut self.cookies, headers); ``` -------------------------------- ### TwitterClientBuilder::client Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.TwitterClientBuilder.html Uses a caller-provided reqwest::blocking::Client. Useful for proxy configuration, proxy authentication, custom TLS settings, custom DNS, or shared connection pools. ```APIDOC ## client ### Description Uses a caller-provided `reqwest::blocking::Client`. This is useful for applications that need proxy configuration, proxy authentication, custom TLS settings, custom DNS, or shared connection pools. The library still adds request-specific Twitter/X headers and its own in-memory `Cookie` header on each request. ### Method pub fn client(self, client: Client) -> Self ### Parameters - **client** (Client) - Required - The preconfigured reqwest blocking client. ### Returns Self - The builder for chaining. ``` -------------------------------- ### extract_media_urls and helper functions for media extraction Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/media.rs.html Defines the public function extract_media_urls that extracts unique media URLs from a tweet JSON value, skipping unavailable media and preferring the best video URL or original image URL. Includes helper functions media_unavailable, best_video_url, image_url, and original_image_url. ```Rust use serde_json::Value; use std::collections::HashSet; pub(super) fn extract_media_urls(tweet: &Value) -> Vec { let mut out = Vec::new(); let mut seen = HashSet::new(); let media_arrays = [ tweet.pointer("/legacy/extended_entities/media"), tweet.pointer("/legacy/entities/media"), tweet.pointer("/extended_entities/media"), tweet.pointer("/entities/media"), ]; for media_array in media_arrays.into_iter().flatten() { let Some(items) = media_array.as_array() else { continue; }; for media in items { if media_unavailable(media) { continue; } if let Some(url) = best_video_url(media).or_else(|| image_url(media)) && seen.insert(url.clone()) { out.push(url); } } } out } fn media_unavailable(media: &Value) -> bool { media .pointer("/ext_media_availability/status") .and_then(Value::as_str) == Some("Unavailable") } fn best_video_url(media: &Value) -> Option { let variants = media.pointer("/video_info/variants")?.as_array()?; variants .iter() .filter(|variant| { let url = variant .get("url") .and_then(Value::as_str) .unwrap_or_default(); let content_type = variant .get("content_type") .and_then(Value::as_str) .unwrap_or_default(); content_type == "video/mp4" || url.contains(".mp4") }) .max_by_key(|variant| variant.get("bitrate").and_then(Value::as_u64).unwrap_or(0)) .and_then(|variant| variant.get("url").and_then(Value::as_str)) .or_else(|| { variants .iter() .max_by_key(|variant| variant.get("bitrate").and_then(Value::as_u64).unwrap_or(0)) .and_then(|variant| variant.get("url").and_then(Value::as_str)) }) .map(str::to_string) } fn image_url(media: &Value) -> Option { let url = media .get("media_url_https") .or_else(|| media.get("media_url")) .and_then(Value::as_str)?; Some(original_image_url(url)) } fn original_image_url(url: &str) -> String { if url.contains("?format=") { if let Some((prefix, _)) = url.split_once("name=") { return format!("{prefix}name=orig"); } return format!("{url}&name=orig"); } let clean = url.split('?').next().unwrap_or(url); if let Some((base, ext)) = clean.rsplit_once('.') { return format!("{base}?format={ext}&name=orig"); } url.to_string() } ``` -------------------------------- ### TwitterClientBuilder::cookie Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.TwitterClientBuilder.html Adds one cookie, e.g. auth_token or ct0, from a logged-in browser session. ```APIDOC ## cookie ### Description Adds one cookie, e.g. `auth_token` or `ct0`, from a logged-in browser session. ### Method pub fn cookie(self, name: impl Into, value: impl Into) -> Self ### Parameters - **name** (impl Into) - Required - The cookie name. - **value** (impl Into) - Required - The cookie value. ### Returns Self - The builder for chaining. ``` -------------------------------- ### Parse Response JSON Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/shared.rs.html Validates the HTTP status and checks for API-level errors in the response JSON. ```rust pub(super) fn parse_response_json( status: reqwest::StatusCode, data: Value, logged_in: bool, ) -> Result { if status.as_u16() == 429 { return Err(TwitterError::RateLimited); } if let Some(errors) = data.get("errors").and_then(Value::as_array) && !errors.is_empty() { let messages = errors .iter() .filter_map(|error| error.get("message").and_then(Value::as_str)) .collect::>(); let message = if messages.is_empty() { ``` -------------------------------- ### TwitterClientBuilder::cookie_header Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.TwitterClientBuilder.html Adds cookies from a normal Cookie header string, e.g. "auth_token=...; ct0=...". ```APIDOC ## cookie_header ### Description Adds cookies from a normal `Cookie` header string, e.g. `"auth_token=...; ct0=..."`. ### Method pub fn cookie_header(self, cookie_header: &str) -> Self ### Parameters - **cookie_header** (&str) - Required - The Cookie header string. ### Returns Self - The builder for chaining. ``` -------------------------------- ### AsyncTwitterClient::media_urls_from_tweet_id Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.AsyncTwitterClient.html Fetches media URLs from a numeric tweet ID. Returns an empty vector when the tweet is accessible but contains no supported media. ```APIDOC ## AsyncTwitterClient::media_urls_from_tweet_id ### Description Fetches media URLs from a numeric tweet ID. Returns an empty vector when the tweet is accessible but contains no supported image, GIF, or video media. ### Method media_urls_from_tweet_id ### Signature pub async fn media_urls_from_tweet_id(&mut self, tweet_id: &str) -> Result, TwitterError> ### Parameters - **tweet_id** (&str) - Required - The numeric ID of the tweet to fetch media URLs from. ### Returns Result, TwitterError> - A vector of media URLs on success, or a TwitterError on failure. Returns an empty vector when the tweet is accessible but contains no supported media. ``` -------------------------------- ### TwitterClientBuilder::timeout Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.TwitterClientBuilder.html Sets the timeout used when this builder constructs its own reqwest client. This is ignored if Self::client is used with a preconfigured client. ```APIDOC ## timeout ### Description Sets the timeout used when this builder constructs its own reqwest client. This is ignored if `Self::client` is used with a preconfigured client. ### Method pub fn timeout(self, timeout: Duration) -> Self ### Parameters - **timeout** (Duration) - Required - The timeout duration. ### Returns Self - The builder for chaining. ``` -------------------------------- ### Generate Random Alphanumeric Token Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/shared.rs.html Creates a random string of a specified length using alphanumeric characters. ```rust fn random_token(len: usize) -> String { rand::rng() .sample_iter(&Alphanumeric) .take(len) .map(char::from) .collect() } ``` -------------------------------- ### AsyncTwitterClientBuilder::timeout Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.AsyncTwitterClientBuilder.html Sets the timeout used when the builder constructs its own async reqwest client. ```APIDOC ## AsyncTwitterClientBuilder::timeout ### Description Sets the timeout used when this builder constructs its own async reqwest client. This is ignored if `Self::client` is used with a preconfigured client. ### Method pub fn timeout(self, timeout: Duration) -> Self ### Parameters - **timeout** (Duration) - Required - The timeout duration. ### Returns Self - The builder instance for chaining. ``` -------------------------------- ### TwitterClient::media_urls_from_tweet_id Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.TwitterClient.html Fetches media URLs from a numeric tweet ID. Returns an empty vector when the tweet is accessible but contains no supported media. ```APIDOC ## TwitterClient::media_urls_from_tweet_id ### Description Fetches media URLs from a numeric tweet ID. Returns an empty vector when the tweet is accessible but contains no supported image, GIF, or video media. ### Method media_urls_from_tweet_id ### Signature pub fn media_urls_from_tweet_id(&mut self, tweet_id: &str) -> Result, TwitterError> ### Parameters - **tweet_id** (&str) - Required - The numeric ID of the tweet. ### Returns - **Result, TwitterError>** - A vector of media URLs on success, or a TwitterError on failure. ``` -------------------------------- ### Extracting a tweet ID from a URL Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/fn.tweet_id_from_url.html Demonstrates how to use tweet_id_from_url to parse a status URL containing extra path segments and verify the extracted ID. ```rust let id = tweet2media::twitter::tweet_id_from_url( "https://x.com/example/status/1234567890/photo/1", )?; assert_eq!(id, "1234567890"); ``` -------------------------------- ### AsyncTwitterClientBuilder::client_transaction_id Source: https://docs.rs/tweet2media/latest/tweet2media/twitter/struct.AsyncTwitterClientBuilder.html Sets an optional x-client-transaction-id header for logged-in sessions that require it. ```APIDOC ## AsyncTwitterClientBuilder::client_transaction_id ### Description Optional support for logged-in sessions that require X's web transaction header. gallery-dl computes this dynamically from X's web scripts. For the single-tweet endpoint, anonymous guest sessions usually do not need it; if your logged-in cookies are rejected, pass a fresh `x-client-transaction-id` captured from your browser's network requests. ### Method pub fn client_transaction_id(self, value: impl Into) -> Self ### Parameters - **value** (impl Into) - Required - The client transaction ID string. ### Returns Self - The builder instance for chaining. ``` -------------------------------- ### Handle Twitter API Responses Source: https://docs.rs/tweet2media/latest/src/tweet2media/twitter/blocking.rs.html The handle_response function updates internal cookies from response headers and parses the response body into a JSON Value, handling potential parsing errors by wrapping them in an error object. ```rust fn handle_response(&mut self, response: Response) -> Result { self.update_cookies(response.headers()); let status = response.status(); let text = response.text()?; let data: Value = serde_json::from_str(&text).unwrap_or_else(|_| { json!({ "errors": [{ "message": text }] }) }); parse_response_json(status, data, self.logged_in()) } ```