Merge pull request #7 from Trivernis/develop

Develop
main
Julius Riegel 3 years ago committed by GitHub
commit ef09737c17
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,6 +1,6 @@
[package] [package]
name = "hydrus-api" name = "hydrus-api"
version = "0.4.0" version = "0.5.0"
authors = ["trivernis <trivernis@protonmail.com>"] authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018" edition = "2018"
license = "Apache-2.0" license = "Apache-2.0"

@ -23,6 +23,7 @@ use hydrus_api::wrapper::tag::Tag;
use hydrus_api::wrapper::service::ServiceName; use hydrus_api::wrapper::service::ServiceName;
use hydrus_api::wrapper::hydrus_file::FileStatus; use hydrus_api::wrapper::hydrus_file::FileStatus;
use hydrus_api::wrapper::page::PageIdentifier; use hydrus_api::wrapper::page::PageIdentifier;
use hydrus_api::wrapper::builders::search_builder::SortType;
use hydrus_api::wrapper::builders::tag_builder::{ use hydrus_api::wrapper::builders::tag_builder::{
SystemTagBuilder, Comparator SystemTagBuilder, Comparator
}; };
@ -33,11 +34,12 @@ async fn main() {
let access_key = env::var("HYDRUS_ACCESS_KEY").unwrap(); let access_key = env::var("HYDRUS_ACCESS_KEY").unwrap();
let hydrus = Hydrus::new(Client::new(hydrus_url, access_key)); let hydrus = Hydrus::new(Client::new(hydrus_url, access_key));
let files = hydrus.search(vec![ let files = hydrus.search()
Tag::from("character:megumin"), .add_tag(Tag::from("character:megumin"))
SystemTagBuilder::new().archive().build(), .add_tag(SystemTagBuilder::new().archive().build())
SystemTagBuilder::new().number_of_tags(Comparator::Greater, 12).build(), .add_tag(SystemTagBuilder::new().number_of_tags(Comparator::Greater, 12).build())
]).await.unwrap(); .sort(SortType::ModifiedTime)
.run().await.unwrap();
for mut file in files { for mut file in files {
file.add_tags(ServiceName::my_tags(), vec![Tag::from("ark mage")]).await.unwrap(); file.add_tags(ServiceName::my_tags(), vec![Tag::from("ark mage")]).await.unwrap();

@ -21,7 +21,8 @@ use crate::api_core::managing_pages::{
GetPages, GetPagesResponse, GetPages, GetPagesResponse,
}; };
use crate::api_core::searching_and_fetching_files::{ use crate::api_core::searching_and_fetching_files::{
FileMetadata, FileMetadataResponse, GetFile, SearchFiles, SearchFilesResponse, FileMetadata, FileMetadataResponse, FileSearchOptions, GetFile, SearchFiles,
SearchFilesResponse,
}; };
use crate::api_core::Endpoint; use crate::api_core::Endpoint;
use crate::error::{Error, Result}; use crate::error::{Error, Result};
@ -222,13 +223,16 @@ impl Client {
} }
/// Searches for files in the inbox, the archive or both /// Searches for files in the inbox, the archive or both
pub async fn search_files(&self, tags: Vec<String>) -> Result<SearchFilesResponse> { pub async fn search_files(
&self,
tags: Vec<String>,
options: FileSearchOptions,
) -> Result<SearchFilesResponse> {
log::trace!("Searching for files with tags {:?}", tags); log::trace!("Searching for files with tags {:?}", tags);
self.get_and_parse::<SearchFiles, [(&str, String)]>(&[( let mut args = options.into_query_args();
"tags", args.push(("tags", string_list_to_json_array(tags)));
string_list_to_json_array(tags), self.get_and_parse::<SearchFiles, [(&str, String)]>(&args)
)]) .await
.await
} }
/// Returns the metadata for a given list of file_ids or hashes /// Returns the metadata for a given list of file_ids or hashes

@ -10,6 +10,7 @@ pub mod common;
pub mod managing_cookies_and_http_headers; pub mod managing_cookies_and_http_headers;
pub mod managing_pages; pub mod managing_pages;
pub mod searching_and_fetching_files; pub mod searching_and_fetching_files;
pub use searching_and_fetching_files::file_sort_type;
pub(crate) trait Endpoint { pub(crate) trait Endpoint {
type Request: Serialize; type Request: Serialize;

@ -1,6 +1,101 @@
use crate::api_core::common::FileMetadataInfo; use crate::api_core::common::FileMetadataInfo;
use crate::api_core::Endpoint; use crate::api_core::Endpoint;
pub mod file_sort_type {
pub const SORT_FILE_SIZE: u8 = 0;
pub const SORT_FILE_DURATION: u8 = 1;
pub const SORT_FILE_IMPORT_TIME: u8 = 2;
pub const SORT_FILE_TYPE: u8 = 3;
pub const SORT_FILE_RANDOM: u8 = 4;
pub const SORT_FILE_WIDTH: u8 = 5;
pub const SORT_FILE_HEIGHT: u8 = 6;
pub const SORT_FILE_RATIO: u8 = 7;
pub const SORT_FILE_PIXEL_COUNT: u8 = 8;
pub const SORT_FILE_TAG_COUNT: u8 = 9;
pub const SORT_FILE_MEDIA_VIEWS: u8 = 10;
pub const SORT_FILE_MEDIA_VIEWTIME: u8 = 11;
pub const SORT_FILE_BITRATE: u8 = 12;
pub const SORT_FILE_HAS_AUDIO: u8 = 13;
pub const SORT_FILE_MODIFIED_TIME: u8 = 14;
pub const SORT_FILE_FRAMERATE: u8 = 15;
pub const SORT_FILE_FRAME_COUNT: u8 = 16;
}
#[derive(Clone, Debug, Default)]
pub struct FileSearchOptions {
file_service_name: Option<String>,
file_service_key: Option<String>,
tag_service_name: Option<String>,
tag_service_key: Option<String>,
file_sort_type: Option<u8>,
file_sort_asc: Option<bool>,
}
impl FileSearchOptions {
pub fn new() -> Self {
Self::default()
}
pub fn file_service_name<S: ToString>(mut self, name: S) -> Self {
self.file_service_name = Some(name.to_string());
self
}
pub fn file_service_key<S: ToString>(mut self, key: S) -> Self {
self.file_service_key = Some(key.to_string());
self
}
pub fn tag_service_name<S: ToString>(mut self, name: S) -> Self {
self.tag_service_name = Some(name.to_string());
self
}
pub fn tag_service_key<S: ToString>(mut self, key: S) -> Self {
self.tag_service_key = Some(key.to_string());
self
}
pub fn sort_type(mut self, sort_type: u8) -> Self {
self.file_sort_type = Some(sort_type);
self
}
pub fn asc(mut self) -> Self {
self.file_sort_asc = Some(true);
self
}
pub fn desc(mut self) -> Self {
self.file_sort_asc = Some(false);
self
}
pub(crate) fn into_query_args(self) -> Vec<(&'static str, String)> {
let mut args = Vec::new();
if let Some(sort) = self.file_sort_type {
args.push(("file_sort_type", sort.to_string()));
}
if let Some(file_service_name) = self.file_service_name {
args.push(("file_service_name", file_service_name));
}
if let Some(file_service_key) = self.file_service_key {
args.push(("file_service_key", file_service_key));
}
if let Some(tag_service_name) = self.tag_service_name {
args.push(("tag_service_name", tag_service_name))
}
if let Some(tag_service_key) = self.tag_service_key {
args.push(("tag_service_key", tag_service_key));
}
if let Some(sort_asc) = self.file_sort_asc {
args.push(("file_sort_asc", sort_asc.to_string()))
}
args
}
}
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
pub struct SearchFilesResponse { pub struct SearchFilesResponse {
pub file_ids: Vec<u64>, pub file_ids: Vec<u64>,

@ -13,17 +13,20 @@
//! use hydrus_api::wrapper::hydrus_file::FileStatus; //! use hydrus_api::wrapper::hydrus_file::FileStatus;
//! use hydrus_api::wrapper::page::PageIdentifier; //! use hydrus_api::wrapper::page::PageIdentifier;
//! use hydrus_api::wrapper::builders::tag_builder::{SystemTagBuilder, Comparator}; //! use hydrus_api::wrapper::builders::tag_builder::{SystemTagBuilder, Comparator};
//! use hydrus_api::wrapper::builders::search_builder::SortType;
//! //!
//! # #[tokio::test] //! # #[tokio::test]
//! # async fn doctest() { //! # async fn doctest() {
//! let hydrus_url = env::var("HYDRUS_URL").unwrap(); //! let hydrus_url = env::var("HYDRUS_URL").unwrap();
//! let access_key = env::var("HYDRUS_ACCESS_KEY").unwrap(); //! let access_key = env::var("HYDRUS_ACCESS_KEY").unwrap();
//! let hydrus = Hydrus::new(Client::new(hydrus_url, access_key)); //! let hydrus = Hydrus::new(Client::new(hydrus_url, access_key));
//! let files = hydrus.search(vec![ //! let files = hydrus.search()
//! Tag::from("character:megumin"), //! .add_tag(Tag::from("character:megumin"))
//! SystemTagBuilder::new().archive().build(), //! .add_tag(SystemTagBuilder::new().archive().build())
//! SystemTagBuilder::new().tag_namespace_as_number("page", Comparator::Equal, 5).negate().build(), //! .add_tag(SystemTagBuilder::new().tag_namespace_as_number("page", Comparator::Equal, 5).negate().build())
//! ]).await.unwrap(); //! .sort_by(SortType::NumberOfPixels)
//! .sort_descending()
//! .run().await.unwrap();
//! //!
//! for mut file in files { //! for mut file in files {
//! file.add_tags(ServiceName::my_tags(), vec![Tag::from("ark mage")]).await.unwrap(); //! file.add_tags(ServiceName::my_tags(), vec![Tag::from("ark mage")]).await.unwrap();
@ -74,5 +77,5 @@ pub use wrapper::hydrus::Hydrus;
pub mod api_core; pub mod api_core;
pub mod error; pub mod error;
pub(crate) mod utils; pub mod utils;
pub mod wrapper; pub mod wrapper;

@ -1,11 +1,12 @@
use crate::api_core::common::FileIdentifier;
use crate::wrapper::tag::Tag; use crate::wrapper::tag::Tag;
use chrono::{Datelike, Duration}; use chrono::{Datelike, Duration};
pub fn string_list_to_json_array(l: Vec<String>) -> String { pub(crate) fn string_list_to_json_array(l: Vec<String>) -> String {
format!("[\"{}\"]", l.join("\",\"")) format!("[\"{}\"]", l.join("\",\""))
} }
pub fn number_list_to_json_array<T: ToString>(l: Vec<T>) -> String { pub(crate) fn number_list_to_json_array<T: ToString>(l: Vec<T>) -> String {
format!( format!(
"[{}]", "[{}]",
l.into_iter() l.into_iter()
@ -23,7 +24,7 @@ pub fn tag_list_to_string_list(tags: Vec<Tag>) -> Vec<String> {
tags.into_iter().map(|t| t.to_string()).collect() tags.into_iter().map(|t| t.to_string()).collect()
} }
pub fn format_datetime<D: Datelike>(datetime: D) -> String { pub(crate) fn format_datetime<D: Datelike>(datetime: D) -> String {
format!( format!(
"{:04}-{:02}-{:02}", "{:04}-{:02}-{:02}",
datetime.year(), datetime.year(),
@ -32,7 +33,7 @@ pub fn format_datetime<D: Datelike>(datetime: D) -> String {
) )
} }
pub fn format_duration(duration: Duration) -> String { pub(crate) fn format_duration(duration: Duration) -> String {
let mut expression = String::new(); let mut expression = String::new();
let days = duration.num_days(); let days = duration.num_days();
let hours = duration.num_hours() % 24; let hours = duration.num_hours() % 24;
@ -56,3 +57,18 @@ pub fn format_duration(duration: Duration) -> String {
expression expression
} }
pub(crate) fn split_file_identifiers_into_hashes_and_ids(
files: Vec<FileIdentifier>,
) -> (Vec<u64>, Vec<String>) {
let mut ids = Vec::new();
let mut hashes = Vec::new();
for file in files {
match file {
FileIdentifier::ID(id) => ids.push(id),
FileIdentifier::Hash(hash) => hashes.push(hash),
}
}
(ids, hashes)
}

@ -1,3 +1,4 @@
pub mod import_builder; pub mod import_builder;
pub mod tagging_builder; pub mod tagging_builder;
pub mod tag_builder; pub mod tag_builder;
pub mod search_builder;

@ -0,0 +1,115 @@
use crate::api_core::searching_and_fetching_files::FileSearchOptions;
use crate::error::Result;
use crate::utils::tag_list_to_string_list;
use crate::wrapper::hydrus_file::HydrusFile;
use crate::wrapper::service::ServiceName;
use crate::wrapper::tag::Tag;
use crate::Client;
pub enum SortType {
FileSize,
Duration,
ImportTime,
FileType,
Random,
Width,
Height,
Ratio,
NumberOfPixels,
NumberOfTags,
NumberOfMediaViewers,
MediaViewTime,
Bitrate,
HasAudio,
ModifiedTime,
Framerate,
NumberOfFrames,
}
#[derive(Clone, Debug)]
pub struct SearchBuilder {
client: Client,
tags: Vec<Tag>,
options: FileSearchOptions,
}
impl SearchBuilder {
pub(crate) fn new(client: Client) -> Self {
Self {
client,
tags: Vec::new(),
options: FileSearchOptions::new(),
}
}
/// Add multiple tags to filter by
pub fn add_tags(mut self, mut tags: Vec<Tag>) -> Self {
self.tags.append(&mut tags);
self
}
/// Add a tag to filter by
pub fn add_tag(mut self, tag: Tag) -> Self {
self.tags.push(tag);
self
}
/// Sets the sort type
pub fn sort_by(mut self, sort_type: SortType) -> Self {
self.options = self.options.sort_type(sort_type as u8);
self
}
/// Sorts descending
pub fn sort_descending(mut self) -> Self {
self.options = self.options.desc();
self
}
/// Sorts ascending
pub fn sort_ascending(mut self) -> Self {
self.options = self.options.asc();
self
}
/// Sets the file service name to search in
pub fn file_service_name(mut self, service: ServiceName) -> Self {
self.options = self.options.file_service_name(service);
self
}
/// Sets the tag service to search by
pub fn tag_service_name(mut self, service: ServiceName) -> Self {
self.options = self.options.tag_service_name(service);
self
}
/// Sets the file service key. This option is preferred over
/// setting it by name because it's faster
pub fn file_service_key<S: ToString>(mut self, key: S) -> Self {
self.options = self.options.file_service_key(key);
self
}
/// Sets the tag service key. This option is preferred over
/// setting it by name because it's faster
pub fn tag_service_key<S: ToString>(mut self, key: S) -> Self {
self.options = self.options.tag_service_key(key);
self
}
/// Runs the search
pub async fn run(self) -> Result<Vec<HydrusFile>> {
let client = self.client.clone();
let response = client
.search_files(tag_list_to_string_list(self.tags), self.options)
.await?;
let files = response
.file_ids
.into_iter()
.map(|id| HydrusFile::from_id(client.clone(), id))
.collect();
Ok(files)
}
}

@ -1,13 +1,12 @@
use crate::api_core::common::FileIdentifier; use crate::api_core::common::FileIdentifier;
use crate::error::Result; use crate::error::Result;
use crate::utils::tag_list_to_string_list;
use crate::wrapper::address::Address; use crate::wrapper::address::Address;
use crate::wrapper::builders::import_builder::ImportBuilder; use crate::wrapper::builders::import_builder::ImportBuilder;
use crate::wrapper::builders::search_builder::SearchBuilder;
use crate::wrapper::builders::tagging_builder::TaggingBuilder; use crate::wrapper::builders::tagging_builder::TaggingBuilder;
use crate::wrapper::hydrus_file::HydrusFile; use crate::wrapper::hydrus_file::HydrusFile;
use crate::wrapper::page::HydrusPage; use crate::wrapper::page::HydrusPage;
use crate::wrapper::service::Services; use crate::wrapper::service::Services;
use crate::wrapper::tag::Tag;
use crate::wrapper::url::Url; use crate::wrapper::url::Url;
use crate::wrapper::version::Version; use crate::wrapper::version::Version;
use crate::Client; use crate::Client;
@ -82,19 +81,9 @@ impl Hydrus {
TaggingBuilder::new(self.client.clone()) TaggingBuilder::new(self.client.clone())
} }
/// Searches for files that have the given tags and returns a list of hydrus files as a result /// Starts a request to search for files
pub async fn search(&self, tags: Vec<Tag>) -> Result<Vec<HydrusFile>> { pub fn search(&self) -> SearchBuilder {
let search_result = self SearchBuilder::new(self.client.clone())
.client
.search_files(tag_list_to_string_list(tags))
.await?;
let files = search_result
.file_ids
.into_iter()
.map(|id| HydrusFile::from_id(self.client.clone(), id))
.collect();
Ok(files)
} }
/// Returns a hydrus page by page key /// Returns a hydrus page by page key

@ -18,6 +18,16 @@ pub enum FileStatus {
impl Eq for FileStatus {} impl Eq for FileStatus {}
impl From<u8> for FileStatus {
fn from(v: u8) -> FileStatus {
match v {
3 => FileStatus::Deleted,
0 => FileStatus::ReadyForImport,
_ => FileStatus::InDatabase,
}
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct HydrusFile { pub struct HydrusFile {
pub(crate) client: Client, pub(crate) client: Client,
@ -41,17 +51,10 @@ impl HydrusFile {
status: u8, status: u8,
hash: S, hash: S,
) -> Self { ) -> Self {
let status = if status == 3 {
FileStatus::Deleted
} else if status == 0 {
FileStatus::ReadyForImport
} else {
FileStatus::InDatabase
};
Self { Self {
client, client,
id: FileIdentifier::Hash(hash.to_string()), id: FileIdentifier::Hash(hash.to_string()),
status, status: status.into(),
metadata: None, metadata: None,
} }
} }

@ -1,5 +1,6 @@
use crate::api_core::common::{FileIdentifier, PageInformation}; use crate::api_core::common::{FileIdentifier, PageInformation};
use crate::error::Result; use crate::error::Result;
use crate::utils::split_file_identifiers_into_hashes_and_ids;
use crate::Client; use crate::Client;
#[derive(Clone)] #[derive(Clone)]
@ -40,27 +41,26 @@ impl HydrusPage {
/// Adds files to a page /// Adds files to a page
pub async fn add_files(&self, files: Vec<FileIdentifier>) -> Result<()> { pub async fn add_files(&self, files: Vec<FileIdentifier>) -> Result<()> {
let mut hashes = Vec::new(); let (ids, mut hashes) = split_file_identifiers_into_hashes_and_ids(files);
let mut ids = Vec::new();
for file in files {
match file {
FileIdentifier::ID(id) => ids.push(id),
FileIdentifier::Hash(hash) => hashes.push(hash),
}
}
// resolve file ids to hashes // resolve file ids to hashes
if ids.len() > 0 && hashes.len() > 0 { hashes.append(&mut self.resolve_file_ids_to_hashes(ids).await?);
while let Some(id) = ids.pop() {
let metadata = self
.client
.get_file_metadata_by_identifier(FileIdentifier::ID(id))
.await?;
hashes.push(metadata.hash);
}
}
self.client.add_files_to_page(&self.key, ids, hashes).await self.client
.add_files_to_page(&self.key, [].to_vec(), hashes)
.await
}
async fn resolve_file_ids_to_hashes(&self, ids: Vec<u64>) -> Result<Vec<String>> {
let mut hashes = Vec::new();
for id in ids {
let metadata = self
.client
.get_file_metadata_by_identifier(FileIdentifier::ID(id))
.await?;
hashes.push(metadata.hash);
}
Ok(hashes)
} }
} }

@ -4,7 +4,9 @@ use crate::api_core::access_management::{
SERVICE_TYPE_FILE_REPOSITORIES, SERVICE_TYPE_LOCAL_FILES, SERVICE_TYPE_LOCAL_TAGS, SERVICE_TYPE_FILE_REPOSITORIES, SERVICE_TYPE_LOCAL_FILES, SERVICE_TYPE_LOCAL_TAGS,
SERVICE_TYPE_TAG_REPOSITORIES, SERVICE_TYPE_TRASH, SERVICE_TYPE_TAG_REPOSITORIES, SERVICE_TYPE_TRASH,
}; };
use crate::error::Error; use crate::error::Error;
use crate::wrapper::builders::search_builder::SearchBuilder;
use crate::Client; use crate::Client;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::TryFrom; use std::convert::TryFrom;
@ -102,6 +104,22 @@ pub struct Service {
pub service_type: ServiceType, pub service_type: ServiceType,
} }
impl Service {
pub fn search(&self) -> SearchBuilder {
let builder = SearchBuilder::new(self.client.clone());
match self.service_type {
ServiceType::LocalTags | ServiceType::TagRepositories | ServiceType::AllKnownTags => {
builder.tag_service_key(&self.key)
}
ServiceType::LocalFiles
| ServiceType::FileRepositories
| ServiceType::AllLocalFiles
| ServiceType::AllKnownFiles
| ServiceType::Trash => builder.file_service_key(&self.key),
}
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct Services { pub struct Services {
inner: HashMap<ServiceType, Vec<Service>>, inner: HashMap<ServiceType, Vec<Service>>,
@ -140,13 +158,9 @@ impl Services {
/// Returns a list of all services of the given type /// Returns a list of all services of the given type
pub fn get_services(&self, service_type: ServiceType) -> Vec<&Service> { pub fn get_services(&self, service_type: ServiceType) -> Vec<&Service> {
if let Some(services) = self.inner.get(&service_type) { if let Some(services) = self.inner.get(&service_type) {
let mut borrowed_services = Vec::with_capacity(services.len()); services.into_iter().collect()
for service in services {
borrowed_services.push(service)
}
borrowed_services
} else { } else {
Vec::with_capacity(0) Vec::new()
} }
} }
} }

@ -1,11 +1,17 @@
use super::super::common; use super::super::common;
use hydrus_api::api_core::common::FileIdentifier; use hydrus_api::api_core::common::FileIdentifier;
use hydrus_api::api_core::file_sort_type::SORT_FILE_PIXEL_COUNT;
use hydrus_api::api_core::searching_and_fetching_files::FileSearchOptions;
#[tokio::test] #[tokio::test]
async fn is_searches_files() { async fn is_searches_files() {
let client = common::get_client(); let client = common::get_client();
let options = FileSearchOptions::new()
.sort_type(SORT_FILE_PIXEL_COUNT)
.tag_service_name("public tag repository")
.file_service_name("all known files");
client client
.search_files(vec!["beach".to_string()]) .search_files(vec!["beach".to_string()], options)
.await .await
.unwrap(); .unwrap();
} }

@ -1,7 +1,8 @@
mod test_address;
mod test_files; mod test_files;
mod test_hydrus; mod test_hydrus;
mod test_import; mod test_import;
mod test_url;
mod test_page; mod test_page;
mod test_address; mod test_service;
mod test_tags; mod test_tags;
mod test_url;

@ -1,5 +1,6 @@
use super::super::common; use super::super::common;
use hydrus_api::api_core::adding_tags::TagAction; use hydrus_api::api_core::adding_tags::TagAction;
use hydrus_api::wrapper::builders::search_builder::SortType;
use hydrus_api::wrapper::service::{ServiceName, ServiceType}; use hydrus_api::wrapper::service::{ServiceName, ServiceType};
use hydrus_api::wrapper::url::UrlType; use hydrus_api::wrapper::url::UrlType;
@ -36,7 +37,10 @@ async fn it_retrieves_url_information() {
async fn it_searches() { async fn it_searches() {
let hydrus = common::get_hydrus(); let hydrus = common::get_hydrus();
hydrus hydrus
.search(vec!["character:megumin".into()]) .search()
.add_tag("character:megumin".into())
.sort_by(SortType::ModifiedTime)
.run()
.await .await
.unwrap(); .unwrap();
} }

@ -0,0 +1,27 @@
use super::super::common;
use hydrus_api::wrapper::service::{Service, ServiceType, Services};
async fn get_services() -> Services {
let hydrus = common::get_hydrus();
hydrus.services().await.unwrap()
}
async fn get_file_service() -> Service {
let services = get_services().await;
services
.get_services(ServiceType::LocalFiles)
.pop()
.unwrap()
.clone()
}
#[tokio::test]
async fn it_searches_for_files() {
let service = get_file_service().await;
service
.search()
.add_tag("character:rimuru tempest".into())
.run()
.await
.unwrap();
}

@ -10,7 +10,7 @@ use hydrus_api::wrapper::tag::Tag;
async fn retrieve_single_tag(tag: Tag) -> Result<()> { async fn retrieve_single_tag(tag: Tag) -> Result<()> {
let hydrus = common::get_hydrus(); let hydrus = common::get_hydrus();
hydrus.search(vec![tag]).await?; hydrus.search().add_tag(tag).run().await?;
Ok(()) Ok(())
} }

Loading…
Cancel
Save