aboutsummaryrefslogtreecommitdiff
path: root/client/src/lib.rs
blob: f88ecc5aa392bb585ddac70353250460fc39cd71 (plain)
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
124
125
126
/*
    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 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<Session> {
        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<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: 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()
        )
    }
}