Teach send subcommand to recognize --skip and --take options

This commit is contained in:
Tangent Wantwight 2020-09-11 22:57:23 -04:00
parent 18fb8390a0
commit 3bc46210e4
4 changed files with 48 additions and 9 deletions

View File

@ -2,6 +2,7 @@
- forget a channel's initialization segment when no transmitter is active. This improves behavior when a channel is occasionally used for streams with different codecs. - forget a channel's initialization segment when no transmitter is active. This improves behavior when a channel is occasionally used for streams with different codecs.
- Add INFO logging for channel creation/garbage-collection - Add INFO logging for channel creation/garbage-collection
- Start throttle timing on first data instead of throttle creation (improves cases where the source is slow to start) - Start throttle timing on first data instead of throttle creation (improves cases where the source is slow to start)
- Teach send subcommand to recognize --skip and --take options
## v0.3.0 ## v0.3.0
- update internals to v0.2 of `warp` and `tokio`; no remaining code relies on `futures` 0.1 - update internals to v0.2 of `warp` and `tokio`; no remaining code relies on `futures` 0.1

View File

@ -67,6 +67,15 @@ pub enum Chunk {
Empty Empty
} }
impl Chunk {
pub fn overlaps(&self, start: u128, stop: u128) -> bool {
match self {
Chunk::Cluster(head, _) => head.start as u128 <= stop && head.end as u128 >= start,
_ => true,
}
}
}
// TODO: make an external iterator type so we can remove Chunk::RemainingBody & Chunk::Empty // TODO: make an external iterator type so we can remove Chunk::RemainingBody & Chunk::Empty
impl Iterator for Chunk { impl Iterator for Chunk {
type Item = Bytes; type Item = Bytes;

View File

@ -1,6 +1,9 @@
use std::time::Duration;
use bytes::Bytes; use bytes::Bytes;
use futures::{Stream, TryStreamExt}; use futures::{Stream, TryStreamExt};
use tokio_util::codec::{BytesCodec, FramedRead}; use tokio_util::codec::{BytesCodec, FramedRead};
use webmetro::error::WebmetroError;
pub mod dump; pub mod dump;
pub mod filter; pub mod filter;
@ -11,6 +14,17 @@ pub mod send;
/// is NOT actually async, and just uses blocking read. Don't use more than /// is NOT actually async, and just uses blocking read. Don't use more than
/// one at once, who knows who gets which bytes. /// one at once, who knows who gets which bytes.
pub fn stdin_stream() -> impl Stream<Item = Result<Bytes, std::io::Error>> + Sized + Unpin { pub fn stdin_stream() -> impl Stream<Item = Result<Bytes, std::io::Error>> + Sized + Unpin {
FramedRead::new(tokio::io::stdin(), BytesCodec::new()) FramedRead::new(tokio::io::stdin(), BytesCodec::new()).map_ok(|bytes| bytes.freeze())
.map_ok(|bytes| bytes.freeze()) }
pub fn parse_time(arg: Option<&str>) -> Result<Option<Duration>, WebmetroError> {
match arg {
Some(string) => match string.parse() {
Ok(secs) => Ok(Some(Duration::from_secs(secs))),
Err(err) => Err(WebmetroError::ApplicationError {
message: err.to_string(),
}),
},
None => Ok(None),
}
} }

View File

@ -5,7 +5,7 @@ use hyper::{client::HttpConnector, Body, Client, Request};
use std::io::{stdout, Write}; use std::io::{stdout, Write};
use stream::iter; use stream::iter;
use super::stdin_stream; use super::{parse_time, stdin_stream};
use webmetro::{ use webmetro::{
chunk::{Chunk, WebmStream}, chunk::{Chunk, WebmStream},
error::WebmetroError, error::WebmetroError,
@ -22,26 +22,41 @@ pub fn options() -> App<'static, 'static> {
.arg(Arg::with_name("throttle") .arg(Arg::with_name("throttle")
.long("throttle") .long("throttle")
.help("Slow down upload to \"real time\" speed as determined by the timestamps (useful for streaming static files)")) .help("Slow down upload to \"real time\" speed as determined by the timestamps (useful for streaming static files)"))
.arg(Arg::with_name("skip")
.takes_value(true)
.short("s")
.long("skip")
.help("Skip approximately n seconds of content before uploading or throttling"))
.arg(Arg::with_name("take")
.takes_value(true)
.short("t")
.long("take")
.help("Stop uploading after approximately n seconds of content"))
} }
type BoxedChunkStream = Box<dyn Stream<Item = Result<Chunk, WebmetroError>> + Send + Sync + Unpin>; type BoxedChunkStream = Box<dyn Stream<Item = Result<Chunk, WebmetroError>> + Send + Sync + Unpin>;
#[tokio::main] #[tokio::main]
pub async fn run(args: &ArgMatches) -> Result<(), WebmetroError> { pub async fn run(args: &ArgMatches) -> Result<(), WebmetroError> {
// parse args
let url_str = match args.value_of("url") {
Some(url) => String::from(url),
_ => return Err("Listen address wasn't provided".into()),
};
let start_time = parse_time(args.value_of("skip"))?.map_or(0, |s| s.as_millis());
let stop_time = parse_time(args.value_of("take"))?.map_or(std::u128::MAX, |t| t.as_millis() + start_time);
// build pipeline // build pipeline
let mut timecode_fixer = ChunkTimecodeFixer::new(); let mut timecode_fixer = ChunkTimecodeFixer::new();
let mut chunk_stream: BoxedChunkStream = Box::new( let mut chunk_stream: BoxedChunkStream = Box::new(
stdin_stream() stdin_stream()
.parse_ebml() .parse_ebml()
.chunk_webm() .chunk_webm()
.map_ok(move |chunk| timecode_fixer.process(chunk)), .map_ok(move |chunk| timecode_fixer.process(chunk))
.try_filter(move |chunk| future::ready(chunk.overlaps(start_time, stop_time))),
); );
let url_str = match args.value_of("url") {
Some(url) => String::from(url),
_ => return Err("Listen address wasn't provided".into()),
};
if args.is_present("throttle") { if args.is_present("throttle") {
chunk_stream = Box::new(Throttle::new(chunk_stream)); chunk_stream = Box::new(Throttle::new(chunk_stream));
} }