Skip to content

Commit

Permalink
feat: Add functionality for transcoding media.
Browse files Browse the repository at this point in the history
  • Loading branch information
Mossop committed Mar 2, 2023
1 parent 0296f43 commit f754142
Show file tree
Hide file tree
Showing 23 changed files with 9,129 additions and 22 deletions.
5 changes: 5 additions & 0 deletions crates/plex-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ sys-info = "^0.9"
monostate = "^0.1.2"
serde-aux = "^4.1.2"
enum_dispatch = "^0.3.8"
url-escape = "^0.1.1"

[build-dependencies]
serde = { version = "^1.0", features = ["derive"] }
Expand All @@ -42,6 +43,10 @@ async-std = { version = "^1.12", features = ["attributes"] }
plex-api-test-helper = { path = "../plex-api-test-helper" }
rstest = "^0.16.0"
rpassword = "^7.2"
dash-mpd = "^0.7.0"
hls_m3u8 = "^0.4.1"
mp4 = "^0.13.0"
mp3-metadata = "^0.3.4"

[dev-dependencies.cargo-husky]
version = "1"
Expand Down
6 changes: 6 additions & 0 deletions crates/plex-api/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ pub enum Error {
PinNotLinked,
#[error("Item requested was not found on the server.")]
ItemNotFound,
#[error("The requested transcode parameters were invalid.")]
InvalidTranscodeSettings,
#[error("The transcode request failed: {0}.")]
TranscodeError(String),
#[error("The server thinks the client should just play the original media.")]
TranscodeRefused,
}

const PLEX_API_ERROR_CODE_AUTH_OTP_REQUIRED: i32 = 1029;
Expand Down
39 changes: 35 additions & 4 deletions crates/plex-api/src/http_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use std::time::Duration;
use uuid::Uuid;

const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);
const DEFAULT_CONNECTIONT_TIMEOUT: Duration = Duration::from_secs(5);
const DEFAULT_CONNECTION_TIMEOUT: Duration = Duration::from_secs(5);

#[derive(Debug, Clone)]
pub struct HttpClient {
Expand Down Expand Up @@ -113,6 +113,7 @@ impl HttpClient {
base_url: self.api_url.clone(),
path_and_query: path,
request_builder: self.prepare_request().method("POST"),
timeout: Some(DEFAULT_TIMEOUT),
}
}

Expand All @@ -128,6 +129,7 @@ impl HttpClient {
base_url: self.api_url.clone(),
path_and_query: path,
request_builder: self.prepare_request_min().method("POST"),
timeout: Some(DEFAULT_TIMEOUT),
}
}

Expand All @@ -142,6 +144,7 @@ impl HttpClient {
base_url: self.api_url.clone(),
path_and_query: path,
request_builder: self.prepare_request().method("GET"),
timeout: Some(DEFAULT_TIMEOUT),
}
}

Expand All @@ -157,6 +160,7 @@ impl HttpClient {
base_url: self.api_url.clone(),
path_and_query: path,
request_builder: self.prepare_request_min().method("GET"),
timeout: Some(DEFAULT_TIMEOUT),
}
}

Expand All @@ -171,6 +175,7 @@ impl HttpClient {
base_url: self.api_url.clone(),
path_and_query: path,
request_builder: self.prepare_request().method("PUT"),
timeout: Some(DEFAULT_TIMEOUT),
}
}

Expand All @@ -186,6 +191,7 @@ impl HttpClient {
base_url: self.api_url.clone(),
path_and_query: path,
request_builder: self.prepare_request_min().method("PUT"),
timeout: Some(DEFAULT_TIMEOUT),
}
}

Expand All @@ -200,6 +206,7 @@ impl HttpClient {
base_url: self.api_url.clone(),
path_and_query: path,
request_builder: self.prepare_request().method("DELETE"),
timeout: Some(DEFAULT_TIMEOUT),
}
}

Expand All @@ -215,6 +222,7 @@ impl HttpClient {
base_url: self.api_url.clone(),
path_and_query: path,
request_builder: self.prepare_request().method("DELETE"),
timeout: Some(DEFAULT_TIMEOUT),
}
}

Expand All @@ -241,13 +249,25 @@ where
base_url: Uri,
path_and_query: P,
request_builder: Builder,
timeout: Option<Duration>,
}

impl<'a, P> RequestBuilder<'a, P>
where
PathAndQuery: TryFrom<P>,
<PathAndQuery as TryFrom<P>>::Error: Into<http::Error>,
{
// Sets the maximum timeout for this request or disables timeouts.
pub fn timeout(self, timeout: Option<Duration>) -> RequestBuilder<'a, P> {
Self {
http_client: self.http_client,
base_url: self.base_url,
path_and_query: self.path_and_query,
request_builder: self.request_builder,
timeout,
}
}

/// Adds a body to the request.
pub fn body<B>(self, body: B) -> Result<Request<'a, B>>
where
Expand All @@ -258,9 +278,14 @@ where
uri_parts.path_and_query = Some(path_and_query);
let uri = Uri::from_parts(uri_parts).map_err(Into::<http::Error>::into)?;

let mut builder = self.request_builder.uri(uri);
if let Some(timeout) = self.timeout {
builder = builder.timeout(timeout);
}

Ok(Request {
http_client: self.http_client,
request: self.request_builder.uri(uri).body(body)?,
request: builder.body(body)?,
})
}

Expand All @@ -285,6 +310,7 @@ where
base_url: self.base_url,
path_and_query: self.path_and_query,
request_builder: self.request_builder.header(key, value),
timeout: self.timeout,
}
}

Expand Down Expand Up @@ -346,8 +372,7 @@ impl Default for HttpClientBuilder {
let client = HttpClient {
api_url: Uri::from_static(MYPLEX_DEFAULT_API_URL),
http_client: IsahcHttpClient::builder()
.timeout(DEFAULT_TIMEOUT)
.connect_timeout(DEFAULT_CONNECTIONT_TIMEOUT)
.connect_timeout(DEFAULT_CONNECTION_TIMEOUT)
.redirect_policy(RedirectPolicy::None)
.build()
.expect("failed to create default http client"),
Expand All @@ -372,6 +397,12 @@ impl Default for HttpClientBuilder {
}

impl HttpClientBuilder {
/// Creates a client that maps to Plex's Generic profile which has no
/// particular settings defined for transcoding.
pub fn generic() -> Self {
Self::default().set_x_plex_platform("Generic".to_string())
}

pub fn build(self) -> Result<HttpClient> {
self.client
}
Expand Down
9 changes: 8 additions & 1 deletion crates/plex-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,20 @@ pub use http_client::HttpClient;
pub use http_client::HttpClientBuilder;
pub use media_container::devices::Feature;
pub use media_container::preferences::Value as SettingValue;
pub use media_container::server::library::ContainerFormat;
pub use media_container::server::library::{
AudioCodec, ContainerFormat, Decision, Protocol, SubtitleCodec, VideoCodec,
};
pub use media_container::server::Feature as ServerFeature;
pub use myplex::{device, pin::PinManager, MyPlex, MyPlexBuilder};
pub use player::Player;
pub use server::library::{
Artist, Collection, Episode, Item, Library, MediaItem, MetadataItem, Movie, MusicAlbum, Photo,
PhotoAlbum, PhotoAlbumItem, Playlist, Season, Show, Track, Video,
};
pub use server::transcode::{
AudioSetting, Constraint, Limitation, MusicTranscodeOptions, TranscodeSession, TranscodeStatus,
VideoSetting, VideoTranscodeOptions,
};
pub use server::Server;

pub type Result<T = (), E = error::Error> = std::result::Result<T, E>;
Loading

0 comments on commit f754142

Please sign in to comment.