mirror of
https://framagit.org/veretcle/scootaloo.git
synced 2025-07-20 17:11:19 +02:00
188 lines
6.0 KiB
Rust
188 lines
6.0 KiB
Rust
use crate::config::MastodonConfig;
|
||
use crate::config::TwitterConfig;
|
||
use crate::util::cache_media;
|
||
use crate::ScootalooError;
|
||
|
||
use egg_mode::{
|
||
entities::{MediaEntity, MediaType},
|
||
tweet::{user_timeline, Tweet},
|
||
user::UserID,
|
||
KeyPair, Token,
|
||
};
|
||
use std::error::Error;
|
||
|
||
/// Gets Twitter oauth2 token
|
||
pub fn get_oauth2_token(config: &TwitterConfig) -> Token {
|
||
let con_token = KeyPair::new(
|
||
config.consumer_key.to_owned(),
|
||
config.consumer_secret.to_owned(),
|
||
);
|
||
let access_token = KeyPair::new(
|
||
config.access_key.to_owned(),
|
||
config.access_secret.to_owned(),
|
||
);
|
||
|
||
Token::Access {
|
||
consumer: con_token,
|
||
access: access_token,
|
||
}
|
||
}
|
||
|
||
/// Gets Twitter user timeline
|
||
pub async fn get_user_timeline(
|
||
config: &MastodonConfig,
|
||
token: &Token,
|
||
lid: Option<u64>,
|
||
) -> Result<Vec<Tweet>, Box<dyn Error>> {
|
||
// fix the page size to 200 as it is the maximum Twitter authorizes
|
||
let (_, feed) = user_timeline(
|
||
UserID::from(config.twitter_screen_name.to_owned()),
|
||
true,
|
||
false,
|
||
token,
|
||
)
|
||
.with_page_size(200)
|
||
.older(lid)
|
||
.await?;
|
||
|
||
Ok(feed.to_vec())
|
||
}
|
||
|
||
/// Retrieves a single media from a tweet and store it in a temporary file
|
||
pub async fn get_tweet_media(m: &MediaEntity, t: &str) -> Result<String, Box<dyn Error>> {
|
||
match m.media_type {
|
||
MediaType::Photo => cache_media(&m.media_url_https, t).await,
|
||
_ => match &m.video_info {
|
||
Some(v) => match &v.variants.iter().find(|&x| x.content_type == "video/mp4") {
|
||
Some(u) => cache_media(&u.url, t).await,
|
||
None => Err(ScootalooError::new(&format!(
|
||
"Media Type for {} is video but no mp4 file URL is available",
|
||
&m.url
|
||
))
|
||
.into()),
|
||
},
|
||
None => Err(ScootalooError::new(&format!(
|
||
"Media Type for {} is video but does not contain any video_info",
|
||
&m.url
|
||
))
|
||
.into()),
|
||
},
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
use egg_mode::entities::{
|
||
MediaSize, MediaSizes,
|
||
MediaType::{Gif, Photo},
|
||
ResizeMode::Crop,
|
||
ResizeMode::Fit,
|
||
VideoInfo, VideoVariant,
|
||
};
|
||
use std::fs::remove_dir_all;
|
||
|
||
const TMP_DIR: &'static str = "/tmp/scootaloo_get_tweet_media_test";
|
||
|
||
#[tokio::test]
|
||
async fn test_get_tweet_media() {
|
||
let m_photo = MediaEntity {
|
||
display_url: "pic.twitter.com/sHrwmP69Yv".to_string(),
|
||
expanded_url: "https://twitter.com/NintendojoFR/status/1555473821121056771/photo/1"
|
||
.to_string(),
|
||
id: 1555473771280080896,
|
||
range: (91, 114),
|
||
media_url: "http://pbs.twimg.com/media/FZYnJ1qWIAAReHt.jpg".to_string(),
|
||
media_url_https: "https://pbs.twimg.com/media/FZYnJ1qWIAAReHt.jpg"
|
||
.to_string(),
|
||
sizes: MediaSizes {
|
||
thumb: MediaSize {
|
||
w: 150,
|
||
h: 150,
|
||
resize: Crop
|
||
},
|
||
small: MediaSize {
|
||
w: 680,
|
||
h: 510,
|
||
resize: Fit
|
||
},
|
||
medium: MediaSize {
|
||
w: 1200,
|
||
h: 900,
|
||
resize: Fit
|
||
},
|
||
large: MediaSize {
|
||
w: 1280,
|
||
h: 960,
|
||
resize: Fit
|
||
}
|
||
},
|
||
source_status_id: None,
|
||
media_type: Photo,
|
||
url: "https://t.co/sHrwmP69Yv".to_string(),
|
||
video_info: None,
|
||
ext_alt_text: Some("Le menu «\u{a0}Classes » du jeu vidéo Xenoblade Chronicles 3 (Switch). L’affinité du personnage pour la classe est notée par quatre lettres : C, A, C, A (caca)."
|
||
.to_string())
|
||
};
|
||
let m_video = MediaEntity {
|
||
display_url: "pic.twitter.com/xDln0RrkjU".to_string(),
|
||
expanded_url: "https://twitter.com/NintendojoFR/status/1551822196833673218/photo/1"
|
||
.to_string(),
|
||
id: 1551822189711790081,
|
||
range: (275, 298),
|
||
media_url: "http://pbs.twimg.com/tweet_video_thumb/FYkuD0RXEAE-iDx.jpg".to_string(),
|
||
media_url_https: "https://pbs.twimg.com/tweet_video_thumb/FYkuD0RXEAE-iDx.jpg"
|
||
.to_string(),
|
||
sizes: MediaSizes {
|
||
thumb: MediaSize {
|
||
w: 150,
|
||
h: 150,
|
||
resize: Crop,
|
||
},
|
||
small: MediaSize {
|
||
w: 320,
|
||
h: 240,
|
||
resize: Fit,
|
||
},
|
||
medium: MediaSize {
|
||
w: 320,
|
||
h: 240,
|
||
resize: Fit,
|
||
},
|
||
large: MediaSize {
|
||
w: 320,
|
||
h: 240,
|
||
resize: Fit,
|
||
},
|
||
},
|
||
source_status_id: None,
|
||
media_type: Gif,
|
||
url: "https://t.co/xDln0RrkjU".to_string(),
|
||
video_info: Some(VideoInfo {
|
||
aspect_ratio: (4, 3),
|
||
duration_millis: None,
|
||
variants: vec![VideoVariant {
|
||
bitrate: Some(0),
|
||
content_type: "video/mp4".parse::<mime::Mime>().unwrap(),
|
||
url: "https://video.twimg.com/tweet_video/FYkuD0RXEAE-iDx.mp4".to_string(),
|
||
}],
|
||
}),
|
||
ext_alt_text: Some("Scared Nintendo GIF".to_string()),
|
||
};
|
||
|
||
let tweet_media_photo = get_tweet_media(&m_photo, TMP_DIR).await.unwrap();
|
||
let tweet_media_video = get_tweet_media(&m_video, TMP_DIR).await.unwrap();
|
||
|
||
assert_eq!(
|
||
tweet_media_photo,
|
||
format!("{}/FZYnJ1qWIAAReHt.jpg", TMP_DIR)
|
||
);
|
||
assert_eq!(
|
||
tweet_media_video,
|
||
format!("{}/FYkuD0RXEAE-iDx.mp4", TMP_DIR)
|
||
);
|
||
|
||
remove_dir_all(TMP_DIR).unwrap();
|
||
}
|
||
}
|