Initial commit

This commit is contained in:
2025-05-03 15:19:56 -06:00
commit 90c7b9d654
48 changed files with 3192 additions and 0 deletions
+40
View File
@@ -0,0 +1,40 @@
use std::rc::Rc;
use crate::Config;
use crate::model::{Collection, CollectionId};
use super::{Error, make_request};
/// TMDB Collections API client
pub struct Client {
config: Rc<Config>,
}
impl Client {
/// Create a new client with the given configuration
pub fn new(config: Rc<Config>) -> Self {
Self { config }
}
}
impl Client {
/// Fetch the details of the collection refered to by ID
pub async fn get_details(
&self,
id: impl Into<CollectionId>,
language: Option<&str>,
) -> Result<Collection, Error> {
Ok(self
.config
.client
.execute(make_request(
&self.config,
&format!("/3/collection/{}", id.into()),
language,
)?)
.await?
.error_for_status()?
.json()
.await?)
}
}
+47
View File
@@ -0,0 +1,47 @@
use std::rc::Rc;
use crate::Config;
use crate::model::{Episode, ShowId};
use super::{Error, make_request};
/// TMDB Episodes API client
pub struct Client {
config: Rc<Config>,
}
impl Client {
/// Create a new client with the given configuration
pub fn new(config: Rc<Config>) -> Self {
Self { config }
}
}
impl Client {
/// Fetch the details of the episode refered to by ID, season number and episode number
pub async fn get_details(
&self,
id: impl Into<ShowId>,
season: impl Into<i32>,
episode: impl Into<i32>,
language: Option<&str>,
) -> Result<Episode, Error> {
Ok(self
.config
.client
.execute(make_request(
&self.config,
&format!(
"/3/tv/{}/season/{}/episode/{}",
id.into(),
season.into(),
episode.into()
),
language,
)?)
.await?
.error_for_status()?
.json()
.await?)
}
}
+58
View File
@@ -0,0 +1,58 @@
use std::rc::Rc;
use crate::Config;
use crate::model::{MovieGenre, ShowGenre};
use super::{Error, make_request};
/// TMDB Genre API client
pub struct Client {
config: Rc<Config>,
}
impl Client {
/// Create a new client with the given configuration
pub fn new(config: Rc<Config>) -> Self {
Self { config }
}
}
impl Client {
/// Fetch the list of all valid movie genres
pub async fn get_movie_genres(&self, language: Option<&str>) -> Result<Vec<MovieGenre>, Error> {
#[derive(Debug, serde::Deserialize)]
struct Genres {
genres: Vec<MovieGenre>,
}
let genres: Genres = self
.config
.client
.execute(make_request(&self.config, "/3/genre/movie/list", language)?)
.await?
.error_for_status()?
.json()
.await?;
Ok(genres.genres)
}
/// Fetch the list of all valid show genres
pub async fn get_tv_genres(&self, language: Option<&str>) -> Result<Vec<ShowGenre>, Error> {
#[derive(Debug, serde::Deserialize)]
struct Genres {
genres: Vec<ShowGenre>,
}
let genres: Genres = self
.config
.client
.execute(make_request(&self.config, "/3/genre/tv/list", language)?)
.await?
.error_for_status()?
.json()
.await?;
Ok(genres.genres)
}
}
+45
View File
@@ -0,0 +1,45 @@
use reqwest::Request;
use reqwest::header;
use crate::Config;
/// Collections API
pub mod collections;
/// Episodes API
pub mod episodes;
/// Genres API
pub mod genres;
/// Movies API
pub mod movies;
/// Seasons API
pub mod seasons;
/// Shows API
pub mod shows;
/// A generic error wrapping Url and Reqwest errors
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// Url error wrapper
#[error("url parse error: {0}")]
Url(#[from] url::ParseError),
/// Reqwest error wrapper
#[error("reqwest error: {0}")]
Reqwest(#[from] reqwest::Error),
}
fn make_request(config: &Config, path: &str, language: Option<&str>) -> Result<Request, Error> {
let url = config.base_url.join(path)?;
let mut builder = config.client.get(url).header(
header::AUTHORIZATION,
format!("Bearer {}", config.bearer_token),
);
if let Some(ref user_agent) = config.user_agent {
builder = builder.header(header::USER_AGENT, user_agent);
}
if let Some(language) = language {
builder = builder.query(&[("language", language)]);
}
Ok(builder.build()?)
}
+40
View File
@@ -0,0 +1,40 @@
use std::rc::Rc;
use crate::Config;
use crate::model::{Movie, MovieId};
use super::{Error, make_request};
/// TMDB Movies API client
pub struct Client {
config: Rc<Config>,
}
impl Client {
/// Create a new client with the given configuration
pub fn new(config: Rc<Config>) -> Self {
Self { config }
}
}
impl Client {
/// Fetch the details of the movie refered to by ID
pub async fn get_details(
&self,
id: impl Into<MovieId>,
language: Option<&str>,
) -> Result<Movie, Error> {
Ok(self
.config
.client
.execute(make_request(
&self.config,
&format!("/3/movie/{}", id.into()),
language,
)?)
.await?
.error_for_status()?
.json()
.await?)
}
}
+41
View File
@@ -0,0 +1,41 @@
use std::rc::Rc;
use crate::Config;
use crate::model::{Season, ShowId};
use super::{Error, make_request};
/// TMDB Seasons API client
pub struct Client {
config: Rc<Config>,
}
impl Client {
/// Create a new client with the given configuration
pub fn new(config: Rc<Config>) -> Self {
Self { config }
}
}
impl Client {
/// Fetch the details of the season refered to by ID and season number
pub async fn get_details(
&self,
id: impl Into<ShowId>,
season: impl Into<i32>,
language: Option<&str>,
) -> Result<Season, Error> {
Ok(self
.config
.client
.execute(make_request(
&self.config,
&format!("/3/tv/{}/season/{}", id.into(), season.into()),
language,
)?)
.await?
.error_for_status()?
.json()
.await?)
}
}
+40
View File
@@ -0,0 +1,40 @@
use std::rc::Rc;
use crate::Config;
use crate::model::{Show, ShowId};
use super::{Error, make_request};
/// TMDB Shows API client
pub struct Client {
config: Rc<Config>,
}
impl Client {
/// Create a new client with the given configuration
pub fn new(config: Rc<Config>) -> Self {
Self { config }
}
}
impl Client {
/// Fetch the details of the show refered to by ID
pub async fn get_details(
&self,
id: impl Into<ShowId>,
language: Option<&str>,
) -> Result<Show, Error> {
Ok(self
.config
.client
.execute(make_request(
&self.config,
&format!("/3/tv/{}", id.into()),
language,
)?)
.await?
.error_for_status()?
.json()
.await?)
}
}