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
|
/*
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 <metamuffin.org>
*/
use anyhow::Result;
use log::debug;
use reqwest::{
header::{HeaderMap, HeaderValue},
Client,
};
use serde_json::json;
use std::time::Duration;
use stream::StreamSpec;
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,
username: String,
password: String,
expire: Duration,
) -> anyhow::Result<Session> {
let session_token = Client::builder()
.build()?
.post(format!("{}/api/create_session", self.base()))
.json(&json!({
"expire": expire.as_secs(),
"password": password,
"username": username,
}))
.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<NodePublic> {
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 std::io::Write,
) -> 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)?;
}
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()
)
}
}
|