Skip to content

Commit

Permalink
feat: 7zip support + fix some doc tests
Browse files Browse the repository at this point in the history
  • Loading branch information
elpiel committed Feb 3, 2025
1 parent 34a399c commit 7a3df69
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 79 deletions.
129 changes: 79 additions & 50 deletions src/models/streaming_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1074,52 +1074,77 @@ pub async fn update_stream_source_streaming_url<E: Env + 'static>(
stream_url.query_pairs_mut().extend_pairs(query_pairs);

Ok(stream.to_converted(ConvertedStreamSource::Url { url: stream_url }))
// let mut stream_url = streaming_server_url
// .join("rar/stream")
// .map_err(|err| EnvError::Other(err.to_string()))?;

// let query_pairs = ArchiveStreamRequest {
// key: response_key,
// options: ArchiveStreamOptions {
// file_idx,
// file_must_include,
// },
// }
// .to_query_pairs();
// stream_url.query_pairs_mut().extend_pairs(query_pairs);

// Ok(stream.to_converted(ConvertedStreamSource::Url { url: stream_url }))
// error on Url::join should never happen.
// let create_stream_url = streaming_server_url
// .join("rar/stream")
// .map_err(|err| EnvError::Other(err.to_string()))?;

// let request = Request::post(create_stream_url.as_str())
// .header(http::header::CONTENT_TYPE, "application/json")
// .body(rar_urls)
// .expect("request builder failed");

// let response = E::fetch::<_, ArchiveCreateResponse>(request).await?;
// let response_key = response
// .key
// .filter(|key| !key.is_empty())
// .ok_or_else(|| EnvError::Other("Could not create RAR key".into()))?;

// let mut stream_url = streaming_server_url
// .join("rar/stream")
// .map_err(|err| EnvError::Other(err.to_string()))?;

// let query_pairs = ArchiveStreamRequest {
// key: response_key,
// options: ArchiveStreamOptions {
// file_idx,
// file_must_include,
// },
// }
// .to_query_pairs();
// stream_url.query_pairs_mut().extend_pairs(query_pairs);
}
(
Some(_),
StreamSource::Zip7 {
urls,
file_idx,
file_must_include,
},
) => {
if urls.is_empty() {
return Err(EnvError::Other("No 7zip URLs provided".into()));
}

let streaming_server_url_request = streaming_server_url.clone();
let key = uuid::Uuid::new_v4().to_string();
let request_key = key.clone();
// run concurrently the request for making the zip/create request to the server
E::exec_concurrent(async move {
let create_stream_url = streaming_server_url_request
.join(&format!("7zip/create/{request_key}"))
.expect("Url should always be valid");

let request = Request::post(create_stream_url.as_str())
.header(http::header::CONTENT_TYPE, "application/json")
.header(http::header::ACCEPT, "*/*")
.body(urls.clone())
.expect("request builder failed");

tracing::trace!(
"Server Request for 7zip: {:?}\nJSON Body:{}",
request,
serde_json::to_string(&urls).unwrap()
);

let response_key = E::fetch::<_, ArchiveCreateResponse>(request)
.await
.and_then(|response| {
response
.key
.filter(|key| !key.is_empty())
.ok_or_else(|| EnvError::Other("Could not create 7zip key".into()))
});

match response_key {
Ok(key) => {
tracing::info!("Successful request to the Server for creating a 7zip stream with key: {key}")
}
Err(err) => {
tracing::error!(
"Error creating 7zip stream, no confirmation returned by the server: {}",
err
)
}
}
});

let mut stream_url = streaming_server_url
.join("7zip/stream")
.map_err(|err| EnvError::Other(err.to_string()))?;

let query_pairs = ArchiveStreamRequest {
key,
options: ArchiveStreamOptions {
file_idx,
file_must_include,
},
}
.to_query_pairs();
stream_url.query_pairs_mut().extend_pairs(query_pairs);

// Ok(stream.to_converted(ConvertedStreamSource::Url { url: stream_url }))
Ok(stream.to_converted(ConvertedStreamSource::Url { url: stream_url }))
}
(
Some(_),
Expand Down Expand Up @@ -1264,11 +1289,15 @@ pub async fn update_stream_source_streaming_url<E: Env + 'static>(

Ok(stream.to_converted(ConvertedStreamSource::Url { url: stream_url }))
}
(None, StreamSource::Rar { .. } | StreamSource::Zip { .. } | StreamSource::Nzb { .. }) => {
Err(EnvError::Other(
"Can't play Rar/Zip/Uzb because streaming server is not running".into(),
))
}
(
None,
StreamSource::Rar { .. }
| StreamSource::Zip7 { .. }
| StreamSource::Zip { .. }
| StreamSource::Nzb { .. },
) => Err(EnvError::Other(
"Can't play Rar/Zip/Uzb because streaming server is not running".into(),
)),
// no further changes are needed for now
// we still need to create torrents, etc. in stremio-video
// as it's not part of the current scope
Expand Down
57 changes: 39 additions & 18 deletions src/types/resource/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ impl Stream {
file_idx: _,
file_must_include: _,
} => None,
StreamSource::Zip7 {
urls: _,
file_idx: _,
file_must_include: _,
} => None,
StreamSource::Zip {
zip_urls: _,
file_idx: _,
Expand Down Expand Up @@ -351,49 +356,51 @@ impl Stream {
/// [`StreamSource::Rar`] with `rarUrls` field:
///
/// ```
/// use stremio_core::types::resource::StreamSource;
/// use stremio_core::types::resource::{ArchiveUrl, StreamSource};
///
/// let streams_json = serde_json::json!([
/// {
/// "rarUrls": ["https://example-source.com/file.rar", "https://example-source2.com/file2.rar"],
/// "rarUrls": [{"url": "https://example-source.com/file.rar", "bytes": 10000 }, {"url": "https://example-source2.com/file2.rar", "bytes": null }],
/// // ...Stream
/// },
/// {
/// "rarUrls": ["https://example-source3.com/file.rar", "https://example-source4.com/file2.rar"],
/// "rarUrls": [{"url": "https://example-source3.com/file.rar"}, {"url": "https://example-source4.com/file2.rar"}],
/// "fileIdx": 1,
/// "fileMustInclude": ["includeFile1"],
/// // ...Stream
/// },
/// {
/// "rarUrls": ["https://example-source5.com/file.rar", "https://example-source6.com/file2.rar"],
/// "rarUrls": [{"url": "https://example-source5.com/file.rar"}, {"url": "https://example-source6.com/file2.rar"}],
/// "fileMustInclude": ["includeFile2"],
/// // ...Stream
/// },
/// {
/// "rarUrls": ["https://example-source7.com/file.rar", "https://example-source8.com/file2.rar"],
/// "rarUrls": [{"url": "https://example-source7.com/file.rar"}, {"url": "https://example-source8.com/file2.rar"}],
/// "fileIdx": 2,
/// // ...Stream
/// }
/// ]);
///
/// let expected = vec![
/// StreamSource::Rar {
/// rar_urls: vec!["https://example-source.com/file.rar".parse().unwrap(), "https://example-source2.com/file2.rar".parse().unwrap()],
/// rar_urls: vec![ArchiveUrl { url: "https://example-source.com/file.rar".parse().unwrap(), bytes: Some(10_000) }, ArchiveUrl {url: "https://example-source2.com/file2.rar".parse().unwrap(), bytes: None }],
/// file_idx: None,
/// file_must_include: vec![],
/// },
/// StreamSource::Rar {
/// rar_urls: vec!["https://example-source3.com/file.rar".parse().unwrap(), "https://example-source4.com/file2.rar".parse().unwrap()],
/// rar_urls: vec![ArchiveUrl { url: "https://example-source3.com/file.rar".parse().unwrap(), bytes: None }, ArchiveUrl {url: "https://example-source4.com/file2.rar".parse().unwrap(), bytes: None }],
/// file_idx: Some(1),
/// file_must_include: vec!["includeFile1".into()]
/// },
/// StreamSource::Rar {
/// rar_urls: vec!["https://example-source5.com/file.rar".parse().unwrap(), "https://example-source6.com/file2.rar".parse().unwrap()],
/// rar_urls: vec![ArchiveUrl { url: "https://example-source5.com/file.rar".parse().unwrap(), bytes: None }, ArchiveUrl {url: "https://example-source6.com/file2.rar".parse().unwrap(), bytes: None }],
/// file_idx: None,
/// file_must_include: vec!["includeFile2".into()]
/// },
/// StreamSource::Rar {
/// rar_urls: vec!["https://example-source7.com/file.rar".parse().unwrap(), "https://example-source8.com/file2.rar".parse().unwrap()],
/// rar_urls: vec![
/// ArchiveUrl { url: "https://example-source7.com/file.rar".parse().unwrap(), bytes: None }, ArchiveUrl {url: "https://example-source8.com/file2.rar".parse().unwrap(), bytes: None }
/// ],
/// file_idx: Some(2),
/// file_must_include: vec![],
/// },
Expand All @@ -407,49 +414,49 @@ impl Stream {
/// [`StreamSource::Zip`] with `zipUrls` field:
///
/// ```
/// use stremio_core::types::resource::StreamSource;
/// use stremio_core::types::resource::{ArchiveUrl, StreamSource};
///
/// let streams_json = serde_json::json!([
/// {
/// "zipUrls": ["https://example-source.com/file.rar", "https://example-source2.com/file2.rar"],
/// "zipUrls": [{"url": "https://example-source.com/file.rar", "bytes": 20000}, {"url": "https://example-source2.com/file2.rar"}],
/// // ...Stream
/// },
/// {
/// "zipUrls": ["https://example-source3.com/file.rar", "https://example-source4.com/file2.rar"],
/// "zipUrls": [{"url": "https://example-source3.com/file.rar"}, {"url": "https://example-source4.com/file2.rar"}],
/// "fileIdx": 1,
/// "fileMustInclude": ["includeFile1"],
/// // ...Stream
/// },
/// {
/// "zipUrls": ["https://example-source5.com/file.rar", "https://example-source6.com/file2.rar"],
/// "zipUrls": [{"url": "https://example-source5.com/file.rar"}, {"url": "https://example-source6.com/file2.rar"}],
/// "fileMustInclude": ["includeFile2"],
/// // ...Stream
/// },
/// {
/// "zipUrls": ["https://example-source7.com/file.rar", "https://example-source8.com/file2.rar"],
/// "zipUrls": [{"url": "https://example-source7.com/file.rar"}, {"url": "https://example-source8.com/file2.rar"}],
/// "fileIdx": 2,
/// // ...Stream
/// }
/// ]);
///
/// let expected = vec![
/// StreamSource::Zip {
/// zip_urls: vec!["https://example-source.com/file.rar".parse().unwrap(), "https://example-source2.com/file2.rar".parse().unwrap()],
/// zip_urls: vec![ArchiveUrl {url: "https://example-source.com/file.rar".parse().unwrap(), bytes: Some(20_000) }, ArchiveUrl {url: "https://example-source2.com/file2.rar".parse().unwrap(), bytes: None}],
/// file_idx: None,
/// file_must_include: vec![],
/// },
/// StreamSource::Zip {
/// zip_urls: vec!["https://example-source3.com/file.rar".parse().unwrap(), "https://example-source4.com/file2.rar".parse().unwrap()],
/// zip_urls: vec![ArchiveUrl {url: "https://example-source3.com/file.rar".parse().unwrap(), bytes: None}, ArchiveUrl {url: "https://example-source4.com/file2.rar".parse().unwrap(), bytes: None}],
/// file_idx: Some(1),
/// file_must_include: vec!["includeFile1".into()],
/// },
/// StreamSource::Zip {
/// zip_urls: vec!["https://example-source5.com/file.rar".parse().unwrap(), "https://example-source6.com/file2.rar".parse().unwrap()],
/// zip_urls: vec![ArchiveUrl {url: "https://example-source5.com/file.rar".parse().unwrap(), bytes: None}, ArchiveUrl {url: "https://example-source6.com/file2.rar".parse().unwrap(), bytes: None}],
/// file_idx: None,
/// file_must_include: vec!["includeFile2".into()],
/// },
/// StreamSource::Zip {
/// zip_urls: vec!["https://example-source7.com/file.rar".parse().unwrap(), "https://example-source8.com/file2.rar".parse().unwrap()],
/// zip_urls: vec![ArchiveUrl {url: "https://example-source7.com/file.rar".parse().unwrap(), bytes: None}, ArchiveUrl {url: "https://example-source8.com/file2.rar".parse().unwrap(), bytes: None}],
/// file_idx: Some(2),
/// file_must_include: vec![],
/// },
Expand All @@ -473,6 +480,7 @@ pub enum StreamSource {
YouTube {
yt_id: String,
},
/// Rar archive source
#[serde(rename_all = "camelCase")]
Rar {
rar_urls: Vec<ArchiveUrl>,
Expand All @@ -482,6 +490,7 @@ pub enum StreamSource {
#[serde_as(deserialize_as = "DefaultOnNull")]
file_must_include: Vec<String>,
},
/// Zip archive source
#[serde(rename_all = "camelCase")]
Zip {
zip_urls: Vec<ArchiveUrl>,
Expand All @@ -491,6 +500,18 @@ pub enum StreamSource {
#[serde_as(deserialize_as = "DefaultOnNull")]
file_must_include: Vec<String>,
},
/// 7zip archive source
#[serde(rename_all = "camelCase")]
Zip7 {
#[serde(rename = "7zUrls")]
urls: Vec<ArchiveUrl>,
#[serde(default)]
file_idx: Option<u16>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
#[serde_as(deserialize_as = "DefaultOnNull")]
file_must_include: Vec<String>,
},
/// Nzb source
#[serde(rename_all = "camelCase")]
Nzb {
nzb_url: Url,
Expand Down
21 changes: 10 additions & 11 deletions src/types/streaming_server/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use url::Url;
use crate::types::{streaming_server::PeerSearch, torrent::InfoHash};

pub struct ArchiveStreamRequest<Key = String> {
/// The `rar/create` or `zip/create` key returned in the response
/// The `rar/create`, `zip/create` or `7zip/create` key returned in the response or created by us
pub key: Key,
pub options: ArchiveStreamOptions,
}
Expand Down Expand Up @@ -44,17 +44,17 @@ pub struct ArchiveStreamOptions {
/// `http://127.0.0.1:11470/opensubHash?videoUrl=https%3A%2F%2Fexample.com%2Fmy-awesome-video.mp4`
///
/// ```
/// use core::types::streaming_server::CreateTorrentRequest;
/// use stremio_core::types::streaming_server::CreateTorrentRequest;
///
/// let request: http::Request = CreateTorrentRequest {
/// let request: http::Request<()> = CreateTorrentRequest {
/// server_url: "http://127.0.0.1:11470/".parse().unwrap(),
/// sources: vec!["https://example.com/my-awesome-video.mp4".parse().unwrap()]
/// info_hash: "".parse().unwrap()
/// sources: vec!["https://example.com/my-awesome-video.mp4".parse().unwrap()],
/// info_hash: "017d177431e71199c96fbf2d4dee312471560af1".parse().unwrap(),
/// file_idx: 1,
///
/// }.into();
///
/// assert_eq!("http://127.0.0.1:11470/opensubHash?videoUrl=https%3A%2F%2Fexample.com%2Fmy-awesome-video.mp4", request.uri().to_string());
/// assert_eq!("http://127.0.0.1:11470/017d177431e71199c96fbf2d4dee312471560af1/1?tr=https%3A%2F%2Fexample.com%2Fmy-awesome-video.mp4", request.uri().to_string());
/// assert_eq!(&(), request.body());
/// ```
pub struct CreateTorrentRequest {
Expand Down Expand Up @@ -93,7 +93,6 @@ impl From<CreateTorrentRequest> for Request<()> {
}
}

// x.finish();
uri
};

Expand All @@ -112,11 +111,11 @@ impl From<CreateTorrentRequest> for Request<()> {
/// `http://127.0.0.1:11470/opensubHash?videoUrl=https%3A%2F%2Fexample.com%2Fmy-awesome-video.mp4`
///
/// ```
/// use core::types::streaming_server::OpensubtitlesParapRequest;
/// use stremio_core::types::streaming_server::OpensubtitlesParamsRequest;
///
/// let request: http::Request = OpensubtitlesParapRequest {
/// server_url: "http://127.0.0.1:11470/".parse().unwrap()
/// media_url: "https://example.com/my-awesome-video.mp4".parse().unwrap()
/// let request: http::Request<()> = OpensubtitlesParamsRequest {
/// server_url: "http://127.0.0.1:11470/".parse().unwrap(),
/// media_url: "https://example.com/my-awesome-video.mp4".parse().unwrap(),
///
/// }.into();
///
Expand Down

0 comments on commit 7a3df69

Please sign in to comment.