|
|
|
@ -2,7 +2,7 @@ use std::{io::ErrorKind, path::Path, time::Duration}; |
|
|
|
|
|
|
|
use anyhow::anyhow;
|
|
|
|
use futures_util::StreamExt as _;
|
|
|
|
use http::{header::{CONTENT_TYPE, RANGE}, request::Builder as RequestBuilder, uri::{Authority, Scheme}, Request, Response, StatusCode, Uri};
|
|
|
|
use http::{header::{CONTENT_LENGTH, CONTENT_TYPE, RANGE}, request::Builder as RequestBuilder, uri::{Authority, Scheme}, HeaderValue, Request, Response, StatusCode, Uri};
|
|
|
|
use http_body_util::BodyDataStream;
|
|
|
|
use m3u8_rs::{MediaPlaylist, MediaSegment, Playlist};
|
|
|
|
use reqwest::{redirect::Policy, Body};
|
|
|
|
@ -114,10 +114,20 @@ impl State { |
|
|
|
. await
|
|
|
|
. map_err(|e| DownloadError::new(uri.clone(), Some(e)))?;
|
|
|
|
|
|
|
|
// We always need the content-type to be able to decide
|
|
|
|
let content_type = response.headers()[CONTENT_TYPE].to_str()
|
|
|
|
. expect("No content-type header found in response");
|
|
|
|
match response.headers().get("x-finished") {
|
|
|
|
None => (),
|
|
|
|
Some(v) => if let Ok(v) = v.to_str() {
|
|
|
|
if v == "true" {
|
|
|
|
return Ok(())
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// We always need the content-type to be able to decide
|
|
|
|
let content_type = response.headers().get(CONTENT_TYPE)
|
|
|
|
. expect("No content-type header found in response")
|
|
|
|
. to_str()
|
|
|
|
. expect("Can't create &str from content-type header") ;
|
|
|
|
if content_type != "video/MP2T" {
|
|
|
|
let message = format!("unexpected content-type: {}", content_type);
|
|
|
|
log!(Level::Debug, "{}", message);
|
|
|
|
@ -208,6 +218,43 @@ impl State { |
|
|
|
|
|
|
|
async fn request(&mut self, uri: &Uri, from: u64) -> anyhow::Result<Response<Body>>
|
|
|
|
{
|
|
|
|
let request = RequestBuilder::new()
|
|
|
|
. method("HEAD")
|
|
|
|
. uri(uri)
|
|
|
|
. body(Body::default())?;
|
|
|
|
|
|
|
|
log!(Level::Debug, "{:?}", request);
|
|
|
|
|
|
|
|
// Send get request with timeout.
|
|
|
|
// let send_fut = self.client.get(uri).send()?;
|
|
|
|
let send_fut = self.client.execute(request);
|
|
|
|
let response = timeout(self.timeout, send_fut).await??;
|
|
|
|
|
|
|
|
anyhow::ensure!( response.status().is_success()
|
|
|
|
, "resonse status failed: {}"
|
|
|
|
, response.status() );
|
|
|
|
|
|
|
|
log!(Level::Debug, "{:?}", response);
|
|
|
|
|
|
|
|
let content_length: u64 = response.headers().get(CONTENT_LENGTH)
|
|
|
|
. or(Some(&HeaderValue::from(0)))
|
|
|
|
. expect("No CONTENT-LENGTH in response")
|
|
|
|
. to_str()
|
|
|
|
. expect("unable to get CONTENT-LENGTH value")
|
|
|
|
. parse()
|
|
|
|
. expect("unable to parse CONTENT-LENGTH value");
|
|
|
|
|
|
|
|
if from != 0 && content_length - 1 <= from {
|
|
|
|
let response = Response::builder()
|
|
|
|
. header("x-finished", "true")
|
|
|
|
. body(Body::default())
|
|
|
|
. expect("Unable to create default response");
|
|
|
|
|
|
|
|
log!(Level::Debug, "content_length: {}, from: {}", content_length, from);
|
|
|
|
|
|
|
|
return Ok(response);
|
|
|
|
}
|
|
|
|
|
|
|
|
let request = RequestBuilder::new()
|
|
|
|
. uri(uri)
|
|
|
|
. header(RANGE, format!("bytes={}-", from))
|
|
|
|
|