agent-p 0.3.0

A fast podcast downloader with customizable ID3 tagging
//! Shared `reqwest` client carrying a descriptive `User-Agent`.
//!
//! Some podcast hosts (e.g. Buzzsprout's `audio.buzzsprout.com` CDN) reject
//! requests with no `User-Agent` header, returning a `403` HTML page instead of
//! the audio file. `reqwest::get` sends no `User-Agent` by default, so all HTTP
//! access goes through this client to avoid being mistaken for a blocked bot.
//!
//! The client carries the built-in `AgentP/<version>` default; a podcast can
//! override it per audio download via its `user_agent` field (applied as a
//! per-request header in `download`).

use std::sync::LazyLock;

use reqwest::Client;

const DEFAULT_USER_AGENT: &str = concat!(
    "AgentP/",
    env!("CARGO_PKG_VERSION"),
    " (+https://github.com/ByteMagicInc/AgentP)"
);

static CLIENT: LazyLock<Client> = LazyLock::new(|| {
    Client::builder()
        .user_agent(DEFAULT_USER_AGENT)
        .build()
        .expect("failed to build reqwest client")
});

/// Error text for a host that answered with a web page instead of the requested
/// content, the signature of an internet filter or proxy intercepting the request.
/// The URL is shown without its query string or fragment, which can carry access tokens.
pub fn filter_block_message(blocking_domain: &str, url: &str) -> String {
    let url = url.split(['?', '#']).next().unwrap_or(url);
    format!(
        "{blocking_domain} (internet-filter/proxy) blocked URL {url} - check the filter/proxy settings"
    )
}

/// Shared `reqwest` client with the built-in `User-Agent` set as its default.
pub fn client() -> &'static Client {
    &CLIENT
}