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
//! Main representation of a imageboard post
//!
//! # Post
//! A [`Post` struct](Post) is a generic representation of an imageboard post.
//!
//! Most imageboard APIs have a common set of info from the files we want to download.
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::{cmp::Ordering, ops::Not};
use crate::ImageBoards;
use self::rating::Rating;
pub mod error;
pub mod rating;
/// Special enum to simplify the selection of the output file name when downloading a [`Post`]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum NameType {
ID,
MD5,
}
impl Not for NameType {
type Output = Self;
fn not(self) -> Self::Output {
match self {
NameType::ID => NameType::MD5,
NameType::MD5 => NameType::ID,
}
}
}
/// Queue that combines all posts collected, with which tags and with a user-defined blacklist in case an Extractor implements [Auth](ibdl-extractors::websites::Auth).
#[derive(Debug)]
pub struct PostQueue {
/// The imageboard where the posts come from.
pub imageboard: ImageBoards,
/// The internal `Client` used by the extractor.
pub client: Client,
/// A list containing all `Post`s collected.
pub posts: Vec<Post>,
/// The tags used to search the collected posts.
pub tags: Vec<String>,
}
impl PostQueue {
pub fn prepare(&mut self, limit: Option<u16>) {
if let Some(max) = limit {
self.posts.truncate(max as usize);
} else {
self.posts.shrink_to_fit()
}
}
}
/// Catchall model for the necessary parts of the imageboard post to properly identify, download and save it.
#[derive(Debug, Clone, Serialize, Deserialize, Eq)]
pub struct Post {
/// ID number of the post given by the imageboard
pub id: u64,
/// Direct URL of the original image file located inside the imageboard's server
pub url: String,
/// Instead of calculating the downloaded file's MD5 hash on the fly, it uses the one provided by the API.
pub md5: String,
/// The original file extension provided by the imageboard.
///
/// ```https://konachan.com``` (Moebooru) and some other imageboards don't provide this field. So, additional work is required to get the file extension from the url
pub extension: String,
/// Rating of the post. Can be:
///
/// * `Rating::Safe` for SFW posts
/// * `Rating::Questionable` for a not necessarily SFW post
/// * `Rating::Explicit` for NSFW posts
/// * `Rating::Unknown` in case none of the above are correctly parsed
pub rating: Rating,
/// Set of tags associated with the post.
///
/// Used to exclude posts according to a blacklist
pub tags: Vec<String>,
}
impl Ord for Post {
fn cmp(&self, other: &Self) -> Ordering {
self.id.cmp(&other.id)
}
}
impl PartialOrd for Post {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for Post {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Post {
/// Get the final file name of the post for saving.
#[inline]
pub fn file_name(&self, name_type: NameType) -> String {
let name = match name_type {
NameType::ID => self.id.to_string(),
NameType::MD5 => self.md5.to_string(),
};
format!("{}.{}", name, self.extension)
}
#[inline]
pub fn name(&self, name_type: NameType) -> String {
match name_type {
NameType::ID => self.id.to_string(),
NameType::MD5 => self.md5.to_string(),
}
}
}