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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
mod filter;
mod friend;
mod server;
mod shareables;

use crate::{
    media_container::users::AllowTuners,
    url::{MYPLEX_INVITES_FRIENDS, MYPLEX_INVITES_INVITE, MYPLEX_INVITES_SHARED_SERVERS},
    MyPlex, Result,
};
use serde::{Deserialize, Serialize};

pub use filter::SharingFilter;
pub use friend::{Friend, InviteStatus};
pub use server::SharedServer;
pub use shareables::*;

#[derive(Debug)]
pub struct Sharing<'a> {
    myplex: &'a MyPlex,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Permissions {
    pub allow_sync: bool,
    pub allow_channels: bool,
    pub allow_tuners: AllowTuners,
    pub allow_subtitle_admin: bool,
    pub allow_camera_upload: bool,
}

impl Default for Permissions {
    fn default() -> Self {
        Self {
            allow_sync: true,
            allow_channels: true,
            allow_tuners: AllowTuners::None,
            allow_subtitle_admin: true,
            allow_camera_upload: false,
        }
    }
}

#[derive(Default, Debug, Deserialize, Serialize, Clone)]
pub struct Filters {
    #[serde(rename = "filterMovies")]
    pub movies: SharingFilter,
    #[serde(rename = "filterMusic")]
    pub music: SharingFilter,
    #[serde(rename = "filterTelevision")]
    pub television: SharingFilter,
    #[serde(rename = "filterPhotos")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub photos: Option<SharingFilter>,
    #[serde(rename = "filterAll", skip)]
    _all: Option<String>,
}

#[derive(Serialize, Debug)]
#[serde(rename_all = "camelCase")]
struct ShareServerRequest {
    invited_email: String,
    settings: server::Settings,
    library_section_ids: Vec<u32>,
    machine_identifier: String,
}

impl<'a> Sharing<'a> {
    pub fn new(myplex: &'a MyPlex) -> Self {
        Sharing { myplex }
    }

    /// Invite a friend.
    #[tracing::instrument(level = "debug", skip_all, fields(identifier = user.id()))]
    pub async fn invite<'b>(&self, user: User<'b>) -> Result<friend::Friend> {
        self.myplex
            .client()
            .post(format!(
                "{}?{}",
                MYPLEX_INVITES_INVITE,
                serde_urlencoded::to_string([("identifier", user.id())])?
            ))
            .json()
            .await
    }

    /// Share the server with somebody. The user doesn't have to be an accepted friend.
    #[tracing::instrument(level = "debug", skip_all, fields(user_identifier = user.id(), machine_identifier = server.id()))]
    pub async fn share<'b, 'c, 'd>(
        &self,
        user: User<'b>,
        server: ShareableServer<'c>,
        sections: &[ShareableLibrary<'d>],
        permissions: Permissions,
        filters: Filters,
    ) -> Result<SharedServer> {
        let server_info = self.myplex.server_info(server.id()).await?;
        let sections: Vec<u32> = sections
            .iter()
            .filter_map(|s| s.id().parse().ok())
            .collect();
        let request = ShareServerRequest {
            invited_email: user.id().to_owned(),
            settings: server::Settings {
                allow_channels: permissions.allow_channels,
                allow_subtitle_admin: permissions.allow_subtitle_admin,
                allow_sync: permissions.allow_sync,
                allow_tuners: permissions.allow_tuners,
                allow_camera_upload: permissions.allow_camera_upload,
                filter_movies: Some(filters.movies),
                filter_television: Some(filters.television),
                filter_music: Some(filters.music),
                filter_photos: filters.photos,
                ..Default::default()
            },
            library_section_ids: server_info
                .library_sections
                .iter()
                .filter_map(|s| {
                    if sections.contains(&s.key) {
                        Some(s.id)
                    } else {
                        None
                    }
                })
                .collect(),
            machine_identifier: server.id().to_owned(),
        };

        self.myplex
            .client()
            .post(MYPLEX_INVITES_SHARED_SERVERS)
            .json_body(&request)?
            .json()
            .await
    }

    /// Returns a list of friends with the requested status, including managed users.
    #[tracing::instrument(level = "debug", skip(self))]
    pub async fn friends(&self, status: InviteStatus) -> Result<Vec<friend::Friend>> {
        let mut friends: Vec<friend::Friend> = self
            .myplex
            .client()
            .get(format!("{}?includeSharedServers=true&includeSharedSources=true&includeSharingSettings=true&status={}", MYPLEX_INVITES_FRIENDS, status))
            .json()
            .await?;

        for friend in &mut friends {
            friend.client = Some(self.myplex.client().clone())
        }

        Ok(friends)
    }
}