use crate::api::client::{endpoints, GdeltClient};
use crate::error::Result;
use serde::{Deserialize, Serialize};
use tracing::instrument;
#[derive(Debug, Clone)]
pub struct TvApi {
client: GdeltClient,
}
impl TvApi {
pub fn new(client: GdeltClient) -> Self {
Self { client }
}
#[instrument(skip(self))]
pub async fn search(&self, params: TvSearchParams) -> Result<TvSearchResponse> {
if params.station.is_none() {
return Err(crate::error::GdeltError::Validation(
"TV search requires a station parameter. Use --station to specify one \
(e.g., --station CNN, --station FOXNEWS, --station MSNBC). \
Run 'gdelt tv stations' to see available stations.".to_string()
));
}
let url = self.build_search_url(¶ms);
self.client.get_json(&url).await
}
#[instrument(skip(self))]
pub async fn clips(&self, params: TvClipsParams) -> Result<TvClipsResponse> {
let url = self.build_clips_url(¶ms);
self.client.get_json(&url).await
}
#[instrument(skip(self))]
pub async fn timeline(&self, params: TvTimelineParams) -> Result<TvTimelineResponse> {
let url = self.build_timeline_url(¶ms);
self.client.get_json(&url).await
}
#[instrument(skip(self))]
pub async fn stations(&self, params: TvStationsParams) -> Result<TvStationsResponse> {
let url = self.build_stations_url(¶ms);
self.client.get_json(&url).await
}
fn build_search_url(&self, params: &TvSearchParams) -> String {
let max_records = params.max_records.unwrap_or(250).to_string();
let mut query_params = vec![
("query", params.query.as_str()),
("mode", "ClipGallery"),
("format", "json"),
("maxrecords", &max_records),
];
if let Some(ref timespan) = params.timespan {
query_params.push(("timespan", timespan));
}
if let Some(ref station) = params.station {
query_params.push(("station", station));
}
GdeltClient::build_url(endpoints::TV_API, &query_params)
}
fn build_clips_url(&self, params: &TvClipsParams) -> String {
let mut query_params = vec![
("query", params.query.as_str()),
("mode", "ClipGallery"),
("format", "json"),
];
if let Some(ref timespan) = params.timespan {
query_params.push(("timespan", timespan));
}
GdeltClient::build_url(endpoints::TV_API, &query_params)
}
fn build_timeline_url(&self, params: &TvTimelineParams) -> String {
let mode = if params.by_station {
"TimelineVolStation"
} else {
"TimelineVol"
};
let mut query_params = vec![
("query", params.query.as_str()),
("mode", mode),
("format", "json"),
];
if let Some(ref timespan) = params.timespan {
query_params.push(("timespan", timespan));
}
GdeltClient::build_url(endpoints::TV_API, &query_params)
}
fn build_stations_url(&self, params: &TvStationsParams) -> String {
let mut query_params = vec![
("mode", "StationList"),
("format", "json"),
];
if let Some(ref country) = params.country {
query_params.push(("country", country));
}
GdeltClient::build_url(endpoints::TV_API, &query_params)
}
}
#[derive(Debug, Clone)]
pub struct TvAiApi {
client: GdeltClient,
}
impl TvAiApi {
pub fn new(client: GdeltClient) -> Self {
Self { client }
}
#[instrument(skip(self))]
pub async fn search(&self, params: TvAiSearchParams) -> Result<TvAiSearchResponse> {
let url = self.build_search_url(¶ms);
self.client.get_json(&url).await
}
#[instrument(skip(self))]
pub async fn concepts(&self, params: TvAiConceptsParams) -> Result<TvAiConceptsResponse> {
let url = self.build_concepts_url(¶ms);
self.client.get_json(&url).await
}
#[instrument(skip(self))]
pub async fn visual(&self, params: TvAiVisualParams) -> Result<TvAiVisualResponse> {
let url = self.build_visual_url(¶ms);
self.client.get_json(&url).await
}
fn build_search_url(&self, params: &TvAiSearchParams) -> String {
let mut query_params = vec![
("query", params.query.as_str()),
("mode", "ClipGallery"),
("format", "json"),
];
if let Some(ref timespan) = params.timespan {
query_params.push(("timespan", timespan));
}
if params.captions {
query_params.push(("searchcaptions", "1"));
}
if params.ocr {
query_params.push(("searchocr", "1"));
}
if params.asr {
query_params.push(("searchasr", "1"));
}
GdeltClient::build_url(endpoints::TV_API, &query_params)
}
fn build_concepts_url(&self, params: &TvAiConceptsParams) -> String {
let mut query_params = vec![
("concept", params.concept.as_str()),
("mode", "ClipGallery"),
("format", "json"),
];
if let Some(ref timespan) = params.timespan {
query_params.push(("timespan", timespan));
}
GdeltClient::build_url(endpoints::TV_API, &query_params)
}
fn build_visual_url(&self, params: &TvAiVisualParams) -> String {
let mut query_params = vec![
("entity", params.entity.as_str()),
("mode", "ClipGallery"),
("format", "json"),
];
if let Some(ref timespan) = params.timespan {
query_params.push(("timespan", timespan));
}
GdeltClient::build_url(endpoints::TV_API, &query_params)
}
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct TvSearchParams {
pub query: String,
pub timespan: Option<String>,
pub station: Option<String>,
pub max_records: Option<u32>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct TvClipsParams {
pub query: String,
pub timespan: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct TvTimelineParams {
pub query: String,
pub timespan: Option<String>,
pub by_station: bool,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct TvStationsParams {
pub country: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TvSearchResponse {
#[serde(default)]
pub clips: Vec<TvClip>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TvClip {
#[serde(default)]
pub url: String,
#[serde(default)]
pub preview: Option<String>,
#[serde(default)]
pub station: String,
#[serde(default)]
pub show: Option<String>,
#[serde(default)]
pub date: String,
#[serde(default)]
pub snippet: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TvClipsResponse {
#[serde(default)]
pub clips: Vec<TvClip>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TvTimelineResponse {
#[serde(default)]
pub timeline: Vec<TvTimelineEntry>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TvTimelineEntry {
pub date: String,
#[serde(default)]
pub value: f64,
#[serde(default)]
pub station: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TvStationsResponse {
#[serde(default)]
pub stations: Vec<TvStation>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TvStation {
pub id: String,
pub name: String,
#[serde(default)]
pub country: Option<String>,
#[serde(default)]
pub market: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct TvAiSearchParams {
pub query: String,
pub timespan: Option<String>,
pub captions: bool,
pub ocr: bool,
pub asr: bool,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct TvAiConceptsParams {
pub concept: String,
pub timespan: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct TvAiVisualParams {
pub entity: String,
pub timespan: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TvAiSearchResponse {
#[serde(default)]
pub clips: Vec<TvAiClip>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TvAiClip {
#[serde(default)]
pub url: String,
#[serde(default)]
pub preview: Option<String>,
#[serde(default)]
pub station: String,
#[serde(default)]
pub date: String,
#[serde(default)]
pub snippet: Option<String>,
#[serde(default)]
pub concepts: Vec<String>,
#[serde(default)]
pub entities: Vec<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TvAiConceptsResponse {
#[serde(default)]
pub clips: Vec<TvAiClip>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TvAiVisualResponse {
#[serde(default)]
pub clips: Vec<TvAiClip>,
}