-
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add sharing and friends management
- Loading branch information
1 parent
06f0578
commit 974ecb1
Showing
16 changed files
with
712 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
use plex_api::{sharing::InviteStatus::PendingReceived, MyPlexBuilder}; | ||
use rpassword::prompt_password; | ||
use std::io::{stdin, stdout, BufRead, Write}; | ||
|
||
#[async_std::main] | ||
async fn main() { | ||
let token = prompt_password("Token: ").unwrap(); | ||
print!("Friend's username or friendly_name to accept invitation: "); | ||
stdout().flush().unwrap(); | ||
|
||
let username = stdin().lock().lines().next().unwrap().unwrap(); | ||
|
||
let myplex = MyPlexBuilder::default() | ||
.set_token(token) | ||
.build() | ||
.await | ||
.unwrap(); | ||
|
||
let friends = myplex.sharing().friends(PendingReceived).await.unwrap(); | ||
let friend = friends.into_iter().find(|friend| friend.title == username); | ||
|
||
if let Some(friend) = friend { | ||
friend.accept().await.unwrap(); | ||
println!("The invitation was accepts!"); | ||
} else { | ||
eprintln!("Unable to find a friend with username '{username}'."); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
use plex_api::{sharing::InviteStatus, MyPlexBuilder}; | ||
use rpassword::prompt_password; | ||
use std::io::{stdin, stdout, BufRead, Write}; | ||
|
||
#[async_std::main] | ||
async fn main() { | ||
let token = prompt_password("Token: ").unwrap(); | ||
print!("Friend's username or friendly_name to delete: "); | ||
stdout().flush().unwrap(); | ||
|
||
let username = stdin().lock().lines().next().unwrap().unwrap(); | ||
|
||
let myplex = MyPlexBuilder::default() | ||
.set_token(token) | ||
.build() | ||
.await | ||
.unwrap(); | ||
|
||
let friends = myplex | ||
.sharing() | ||
.friends(InviteStatus::Accepted) | ||
.await | ||
.unwrap(); | ||
let mut friend = friends.into_iter().find(|friend| friend.title == username); | ||
|
||
if friend.is_none() { | ||
let friends = myplex | ||
.sharing() | ||
.friends(InviteStatus::PendingReceived) | ||
.await | ||
.unwrap(); | ||
friend = friends.into_iter().find(|friend| friend.title == username); | ||
} | ||
|
||
if friend.is_none() { | ||
let friends = myplex | ||
.sharing() | ||
.friends(InviteStatus::PendingSent) | ||
.await | ||
.unwrap(); | ||
friend = friends.into_iter().find(|friend| friend.title == username); | ||
} | ||
|
||
if let Some(friend) = friend { | ||
friend.delete().await.unwrap(); | ||
println!("The friend was deleted!"); | ||
} else { | ||
eprintln!("Unable to find a friend with username '{username}'."); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
use plex_api::{sharing::User, MyPlexBuilder}; | ||
use rpassword::prompt_password; | ||
use std::io::{stdin, stdout, BufRead, Write}; | ||
|
||
#[async_std::main] | ||
async fn main() { | ||
let token = prompt_password("Token: ").unwrap(); | ||
print!("Friend's username or email: "); | ||
stdout().flush().unwrap(); | ||
|
||
let identifier = stdin().lock().lines().next().unwrap().unwrap(); | ||
|
||
let myplex = MyPlexBuilder::default() | ||
.set_token(token) | ||
.build() | ||
.await | ||
.unwrap(); | ||
|
||
let friend = myplex | ||
.sharing() | ||
.invite(User::UsernameOrEmail(&identifier)) | ||
.await | ||
.unwrap(); | ||
|
||
println!("Invite sent! Friend id is {}", friend.id); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
use plex_api::{ | ||
sharing::{Filters, Permissions, ShareableLibrary, ShareableServer, User}, | ||
MyPlexBuilder, | ||
}; | ||
use rpassword::prompt_password; | ||
use std::io::{stdin, stdout, BufRead, Write}; | ||
|
||
#[async_std::main] | ||
async fn main() { | ||
let token = prompt_password("Token: ").unwrap(); | ||
let myplex = MyPlexBuilder::default() | ||
.set_token(token) | ||
.build() | ||
.await | ||
.unwrap(); | ||
|
||
print!("Friend's username or email: "); | ||
stdout().flush().unwrap(); | ||
|
||
let friend_identifier = stdin().lock().lines().next().unwrap().unwrap(); | ||
|
||
print!("Machine ID to share: "); | ||
stdout().flush().unwrap(); | ||
|
||
let machine_identifier = stdin().lock().lines().next().unwrap().unwrap(); | ||
|
||
let server_info = myplex.server_info(&machine_identifier).await.unwrap(); | ||
|
||
if server_info.library_sections.is_empty() { | ||
panic!("The server has no libraries"); | ||
} | ||
|
||
println!(); | ||
|
||
println!("Server has the following libraries:"); | ||
|
||
for library in &server_info.library_sections { | ||
println!("{}. {} ({})", library.key, library.title, library.r#type); | ||
} | ||
|
||
println!(); | ||
|
||
print!("Comma-separated library keys to share (or 'all'): "); | ||
stdout().flush().unwrap(); | ||
|
||
let mut libraries = stdin().lock().lines().next().unwrap().unwrap(); | ||
|
||
if libraries == "all" { | ||
libraries = server_info | ||
.library_sections | ||
.iter() | ||
.map(|l| l.id.to_string()) | ||
.collect::<Vec<String>>() | ||
.join(","); | ||
} | ||
|
||
let libraries: Vec<ShareableLibrary> = libraries | ||
.split(',') | ||
.map(|key| ShareableLibrary::LibraryId(key.trim())) | ||
.collect(); | ||
|
||
let friend = myplex | ||
.sharing() | ||
.share( | ||
User::UsernameOrEmail(&friend_identifier), | ||
ShareableServer::MachineIdentifier(&machine_identifier), | ||
&libraries, | ||
Permissions::default(), | ||
Filters::default(), | ||
) | ||
.await | ||
.unwrap(); | ||
|
||
println!("Invite sent! Friend id is {}", friend.id); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
use crate::url::MYPLEX_INVITES_FRIENDS; | ||
use crate::Error; | ||
use crate::HttpClient; | ||
use crate::Result; | ||
use http::StatusCode; | ||
use isahc::AsyncReadResponseExt; | ||
use serde::{Deserialize, Serialize}; | ||
use serde_plain::derive_display_from_serialize; | ||
|
||
#[derive(Serialize, Deserialize, Debug, Clone)] | ||
#[serde(rename_all = "snake_case")] | ||
pub enum InviteStatus { | ||
PendingSent, | ||
Accepted, | ||
PendingReceived, | ||
Pending, | ||
#[cfg(not(feature = "tests_deny_unknown_fields"))] | ||
#[serde(other)] | ||
Unknown, | ||
} | ||
|
||
derive_display_from_serialize!(InviteStatus); | ||
|
||
#[derive(Serialize, Deserialize, Debug, Clone)] | ||
#[serde(rename_all = "camelCase")] | ||
#[cfg_attr(feature = "tests_deny_unknown_fields", serde(deny_unknown_fields))] | ||
pub struct Friend { | ||
/// The global Plex.tv ID of the friend | ||
pub id: u64, | ||
/// Plex.tv user's uuid ?? | ||
pub uuid: String, | ||
/// How the user should be displayed in the interface. | ||
/// Seems to be generated on the backed from either username or friendly_name. | ||
pub title: String, | ||
/// Non-managed user's username | ||
pub username: Option<String>, | ||
/// Non-managed user's email | ||
pub email: Option<String>, | ||
/// Is it a managed user? | ||
pub restricted: bool, | ||
/// Managed user's username | ||
pub friendly_name: Option<String>, | ||
/// User's avatar picture URL | ||
#[serde(with = "http_serde::uri")] | ||
pub thumb: http::Uri, | ||
/// Does the user has access to Plex Home? | ||
pub home: bool, | ||
/// Status of the friendship | ||
pub status: Option<InviteStatus>, | ||
/// Server sharing preferences | ||
pub sharing_settings: Option<super::server::Settings>, | ||
/// List of the shared servers | ||
#[serde(default)] | ||
pub shared_servers: Vec<super::server::SharedServer>, | ||
#[serde(skip)] | ||
/// No idea what this is | ||
pub shared_sources: Vec<String>, | ||
#[serde(skip)] | ||
pub(crate) client: Option<HttpClient>, | ||
} | ||
|
||
impl Friend { | ||
pub async fn accept(&self) -> Result<Friend> { | ||
if !matches!( | ||
self.status, | ||
Some(InviteStatus::PendingReceived) | Some(InviteStatus::Pending) | ||
) { | ||
return Err(Error::InviteAcceptingNotPendingReceived); | ||
} | ||
|
||
let mut friend: Friend = self | ||
.client() | ||
.post(format!("{}/{}/accept", MYPLEX_INVITES_FRIENDS, self.id)) | ||
.json() | ||
.await?; | ||
friend.client = self.client.clone(); | ||
Ok(friend) | ||
} | ||
|
||
/// Shorthand for self.client.as_ref().unwrap(). Unwrap must be safe at this stage | ||
/// since the client must be set for a valid invite. | ||
fn client(&self) -> &HttpClient { | ||
self.client.as_ref().unwrap() | ||
} | ||
|
||
pub async fn delete(self) -> Result<()> { | ||
let mut response = self | ||
.client() | ||
.delete(format!("{}/{}", MYPLEX_INVITES_FRIENDS, self.id)) | ||
.send() | ||
.await?; | ||
|
||
match response.status() { | ||
StatusCode::OK | StatusCode::NO_CONTENT => { | ||
response.consume().await?; | ||
Ok(()) | ||
} | ||
_ => Err(Error::from_response(response).await), | ||
} | ||
} | ||
} |
Oops, something went wrong.