mod defaults;
mod types;
use std::fmt;
use serde::Serialize;
use crate::catalog::{ApiMethodSpec, Platform, PlatformSpec, method_specs, platform_spec};
use crate::events::EventBus;
use defaults::{platform_default_headers, platform_default_method};
pub use types::{ClientOptions, CookieConfig, RequestConfig, RequestProfile};
#[derive(Clone)]
pub struct AmagiClient {
options: ClientOptions,
events: EventBus,
}
impl fmt::Debug for AmagiClient {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AmagiClient")
.field("options", &self.options)
.finish_non_exhaustive()
}
}
impl Default for AmagiClient {
fn default() -> Self {
Self::new(ClientOptions::default())
}
}
impl AmagiClient {
pub fn new(options: ClientOptions) -> Self {
Self {
options,
events: EventBus::default(),
}
}
pub fn from_env() -> Result<Self, crate::error::AppError> {
Ok(Self::new(ClientOptions::from_env()?))
}
pub fn events(&self) -> &EventBus {
&self.events
}
pub fn options(&self) -> &ClientOptions {
&self.options
}
pub fn platform(&self, platform: Platform) -> PlatformClient {
PlatformClient {
platform,
cookie: self
.options
.cookies
.for_platform(platform)
.map(str::to_owned),
request: self.options.request.clone(),
}
}
pub fn bilibili(&self) -> PlatformClient {
self.platform(Platform::Bilibili)
}
pub fn bilibili_fetcher(&self) -> crate::platforms::bilibili::BilibiliFetcher {
crate::platforms::bilibili::BilibiliFetcher::new(self.bilibili())
}
pub fn douyin(&self) -> PlatformClient {
self.platform(Platform::Douyin)
}
pub fn kuaishou(&self) -> PlatformClient {
self.platform(Platform::Kuaishou)
}
pub fn xiaohongshu(&self) -> PlatformClient {
self.platform(Platform::Xiaohongshu)
}
pub fn twitter(&self) -> PlatformClient {
self.platform(Platform::Twitter)
}
pub fn xiaohongshu_fetcher(&self) -> crate::platforms::xiaohongshu::XiaohongshuFetcher {
crate::platforms::xiaohongshu::XiaohongshuFetcher::new(self.xiaohongshu())
}
pub fn douyin_fetcher(&self) -> crate::platforms::douyin::DouyinFetcher {
crate::platforms::douyin::DouyinFetcher::new(self.douyin())
}
pub fn kuaishou_fetcher(&self) -> crate::platforms::kuaishou::KuaishouFetcher {
crate::platforms::kuaishou::KuaishouFetcher::new(self.kuaishou())
}
pub fn twitter_fetcher(&self) -> crate::platforms::twitter::TwitterFetcher {
crate::platforms::twitter::TwitterFetcher::new(self.twitter())
}
pub fn catalog(&self) -> [PlatformSpec; 5] {
Platform::ALL.map(|platform| self.platform(platform).spec())
}
}
pub fn create_amagi_client(options: ClientOptions) -> AmagiClient {
AmagiClient::new(options)
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct PlatformClient {
pub platform: Platform,
pub cookie: Option<String>,
pub request: RequestConfig,
}
impl PlatformClient {
pub fn has_cookie(&self) -> bool {
self.cookie
.as_deref()
.is_some_and(|value| !value.is_empty())
}
pub fn api_base_path(&self) -> &'static str {
self.platform.api_base_path()
}
pub fn spec(&self) -> PlatformSpec {
platform_spec(self.platform)
}
pub fn methods(&self) -> &'static [ApiMethodSpec] {
method_specs(self.platform)
}
pub fn request_profile(&self) -> RequestProfile {
let mut headers = platform_default_headers(self.platform);
let cookie_header = match self.platform {
Platform::Xiaohongshu => "cookie",
_ => "Cookie",
};
headers.insert(
cookie_header.to_owned(),
self.cookie.clone().unwrap_or_default(),
);
headers.extend(self.request.headers.clone());
RequestProfile {
platform: self.platform,
method: platform_default_method(self.platform),
timeout_ms: self.request.timeout_ms,
max_retries: self.request.max_retries,
headers,
}
}
}