Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add functionality for transcoding media. #516

Merged
merged 1 commit into from
Mar 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/plex-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,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 @@ -13,7 +13,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 @@ -114,6 +114,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 @@ -129,6 +130,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 @@ -143,6 +145,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 @@ -158,6 +161,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 @@ -172,6 +176,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 @@ -187,6 +192,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 @@ -201,6 +207,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 @@ -216,6 +223,7 @@ impl HttpClient {
base_url: self.api_url.clone(),
path_and_query: path,
request_builder: self.prepare_request_min().method("DELETE"),
timeout: Some(DEFAULT_TIMEOUT),
}
}

Expand Down Expand Up @@ -245,13 +253,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 @@ -262,9 +282,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 @@ -289,6 +314,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 @@ -350,8 +376,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 @@ -376,6 +401,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