/* 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::UserPermission; use log::debug; use reqwest::{ header::{HeaderMap, HeaderValue}, Client, }; use serde::Serialize; use std::collections::HashSet; use stream::StreamSpec; use tokio::io::AsyncWriteExt; pub use jellycommon::*; #[derive(Debug, Clone)] pub struct Instance { pub host: String, pub tls: bool, } #[derive(Serialize)] pub struct LoginDetails { pub username: String, pub password: String, pub expire: Option, pub drop_permissions: Option>, } 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: LoginDetails) -> 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: &str, 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={role}", self.instance.base())) .send() .await?; while let Some(chunk) = r.chunk().await? { writer.write_all(&chunk).await?; } 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() ) } }