/* 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 */ use anyhow::Result; use jellycommon::user::CreateSessionParams; use log::{debug, info}; use reqwest::{ header::{HeaderMap, HeaderValue}, Client, }; use stream::StreamSpec; use tokio::io::AsyncWriteExt; pub use jellycommon::*; #[derive(Debug, Clone)] pub struct Instance { pub host: String, pub tls: bool, } impl Instance { pub fn new(host: String, tls: bool) -> Self { Self { host, tls } } pub fn base(&self) -> String { format!( "{}://{}", if self.tls { "https" } else { "http" }, self.host ) } pub async fn login(self, data: CreateSessionParams) -> anyhow::Result { let session_token = Client::builder() .build()? .post(format!("{}/api/create_session", self.base())) .json(&data) .send() .await? .json() .await?; let mut headers = HeaderMap::new(); headers.insert( "Cookie", HeaderValue::from_str(&format!("session={session_token}")).unwrap(), ); headers.insert("Accept", HeaderValue::from_static("application/json")); Ok(Session { instance: self, session_token, client: Client::builder().default_headers(headers).build().unwrap(), }) } } pub struct Session { client: Client, instance: Instance, session_token: String, } impl Session { fn session_param(&self) -> String { format!("session={}", self.session_token) } pub async fn node(&self, id: &str) -> Result { debug!("downloading node {id:?}"); Ok(self .client .get(format!("{}/n/{id}", self.instance.base())) .send() .await? .json() .await?) } // TODO use AssetRole instead of str pub async fn node_asset( &self, id: &str, role: AssetRole, width: usize, mut writer: impl tokio::io::AsyncWrite + std::marker::Unpin, ) -> Result<()> { debug!("downloading asset {role:?} for {id:?}"); let mut r = self .client .get(format!( "{}/n/{id}/asset?role={}&width={width}", self.instance.base(), role.as_str() )) .send() .await?; while let Some(chunk) = r.chunk().await? { writer.write_all(&chunk).await?; } Ok(()) } pub async fn reimport(&self) -> Result<()> { // TODO error handling info!("reimport"); self.client .post(format!("{}/admin/import", self.instance.base())) .send() .await?; info!("done"); Ok(()) } pub fn stream(&self, id: &str, stream_spec: &StreamSpec) -> String { format!( "{}/n/{}/stream?{}&{}", self.instance.base(), id, stream_spec.to_query(), self.session_param() ) } }