1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
/*
This file is part of jellything (https://codeberg.org/metamuffin/jellything)
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2023 metamuffin <metamuffin.org>
*/
use anyhow::Context;
use jellycommon::chrono::{format::Parsed, DateTime, Utc};
use log::info;
use serde::Deserialize;
use std::io::Write;
#[derive(Debug, Clone, Deserialize)]
pub struct TmdbQuery {
pub page: usize,
pub results: Vec<TmdbQueryResult>,
pub total_pages: usize,
pub total_results: usize,
}
#[derive(Debug, Clone, Deserialize)]
pub struct TmdbQueryResult {
pub adult: bool,
pub backdrop_path: Option<String>,
pub genre_ids: Vec<u64>,
pub id: u64,
pub original_language: Option<String>,
pub original_title: Option<String>,
pub overview: String,
pub popularity: f64,
pub poster_path: Option<String>,
pub release_date: Option<String>,
pub title: Option<String>,
pub name: Option<String>,
pub vote_average: f64,
pub vote_count: usize,
}
#[derive(Debug, Clone, Deserialize)]
pub struct TmdbDetails {
pub adult: bool,
pub backdrop_path: Option<String>,
pub genres: Vec<TmdbGenre>,
pub id: u64,
pub original_language: Option<String>,
pub original_title: Option<String>,
pub overview: String,
pub popularity: f64,
pub poster_path: Option<String>,
pub release_date: Option<String>,
pub title: Option<String>,
pub name: Option<String>,
pub vote_average: f64,
pub vote_count: usize,
pub budget: Option<usize>,
pub homepage: Option<String>,
pub imdb_id: Option<String>,
pub production_companies: Vec<TmdbProductionCompany>,
pub revenue: Option<usize>,
pub tagline: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct TmdbGenre {
pub id: u64,
pub name: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct TmdbProductionCompany {
pub id: u64,
pub name: String,
pub logo_path: Option<String>,
}
#[derive(Debug, Clone, Copy)]
pub enum TmdbKind {
Tv,
Movie,
}
impl TmdbKind {
pub fn as_str(&self) -> &'static str {
match self {
TmdbKind::Tv => "tv",
TmdbKind::Movie => "movie",
}
}
}
pub async fn tmdb_search(kind: TmdbKind, query: &str, key: &str) -> anyhow::Result<TmdbQuery> {
info!("searching tmdb: {query:?}");
Ok(reqwest::get(&format!(
"https://api.themoviedb.org/3/search/{}?query={}&api_key={key}",
kind.as_str(),
query.replace(" ", "+")
))
.await?
.json::<TmdbQuery>()
.await?)
}
pub async fn tmdb_details(kind: TmdbKind, id: u64, key: &str) -> anyhow::Result<TmdbDetails> {
info!("fetching details: {id:?}");
Ok(reqwest::get(&format!(
"https://api.themoviedb.org/3/{}/{id}?api_key={key}",
kind.as_str()
))
.await?
.json()
.await?)
}
pub async fn tmdb_image(path: &str) -> anyhow::Result<Vec<u8>> {
info!("downloading image {path:?}");
let res = reqwest::get(&format!("https://image.tmdb.org/t/p/original{path}")).await?;
Ok(res.bytes().await?.to_vec())
}
pub fn parse_release_date(d: &str) -> anyhow::Result<DateTime<Utc>> {
let (year, month, day) = (&d[0..4], &d[5..7], &d[8..10]);
let (year, month, day) = (
year.parse().context("parsing year")?,
month.parse().context("parsing month")?,
day.parse().context("parsing day")?,
);
let mut p = Parsed::new();
p.year = Some(year);
p.month = Some(month);
p.day = Some(day);
p.hour_div_12 = Some(0);
p.hour_mod_12 = Some(0);
p.minute = Some(0);
p.second = Some(0);
Ok(p.to_datetime_with_timezone(&Utc)?)
}
|