commit acdf0d39f2d4995fd36efd35e6e13d1b52bfa8d2 Author: Ivan Bushchik Date: Sun Jun 18 10:50:58 2023 +0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..212de44 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +.DS_Store \ No newline at end of file diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..32e806d --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,9 @@ +edition = "2021" +hard_tabs = true +merge_derives = true +reorder_imports = true +reorder_modules = true +use_field_init_shorthand = true +use_small_heuristics = "Off" +wrap_comments = true +comment_width = 80 diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..28ee952 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "hlssimple" +version = "0.1.0" +edition = "2021" +license = "MIT" +repository = "https://github.com/ivabus/hlssimple" +description = "HTTP server for hls streaming and nothing more" + + +[dependencies] +rocket = "0.5.0-rc.3" +clap = { version = "4.3.4", features = [ "derive" ] } +smurf = "0.2.0" \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..55f4b6f --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# HLSsimple + +## Installation + +```shell +cargo install --git https://github.com/ivabus/hlssimple +``` + +## Usage + +```shell +hlssimple --stream-dir +``` + +Where `STREAMS_DIR` contains raw (>= 1) streams (.m3u8 file name will be used for index), or streams contained in subdirs (subdir name will be used to index). + +## OBS Configuration + +| All streams in one dir | Streams split by subdirs | +|------------------------------------------|-------------------------------------------------------| +| ![All in one](./img/obs_all_in_one.webp) | ![OBS Split streams by subdirs](./img/obs_split.webp) | diff --git a/Rocket.toml b/Rocket.toml new file mode 100644 index 0000000..5e1688a --- /dev/null +++ b/Rocket.toml @@ -0,0 +1,9 @@ +[default] +address = "0.0.0.0" +port = 50428 +keep_alive = 0 +ident = "hlssimple via Rocket" +ip_header = false +log_level = "normal" +temp_dir = "/tmp" +cli_colors = true diff --git a/img/obs_all_in_one.webp b/img/obs_all_in_one.webp new file mode 100644 index 0000000..1916952 Binary files /dev/null and b/img/obs_all_in_one.webp differ diff --git a/img/obs_split.webp b/img/obs_split.webp new file mode 100644 index 0000000..3333948 Binary files /dev/null and b/img/obs_split.webp differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..2fd0617 --- /dev/null +++ b/index.html @@ -0,0 +1,11 @@ +

Stream index

+ +

How to play?

+ + + +
+ diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..dd38de9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,111 @@ +/* + * MIT License + * + * Copyright (c) 2023 Ivan Bushchik + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#[macro_use] +extern crate rocket; + +use clap::Parser; +use rocket::fs::FileServer; +use rocket::response::content::RawHtml; +use std::fs::read_dir; +use std::path::PathBuf; + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + #[arg(short, long)] + streams_dir: String, +} + +#[get("/play/")] +fn play(page: PathBuf) -> RawHtml { + RawHtml(format!( + "", + page.to_str().unwrap() + )) +} + +struct StreamURL { + alias: String, + url: String, +} + +#[get("/")] +fn index() -> RawHtml { + let args = Args::parse(); + let dir = read_dir(args.streams_dir).unwrap(); + let mut res = match smurf::io::read_file_to_str(&PathBuf::from("./index.html")) { + Some(s) => s, + None => String::new(), + }; + let mut m3u8: Vec = vec![]; + for i in dir { + let path = i.unwrap().path(); + if path.is_file() && path.file_name().unwrap().to_str().unwrap() != ".DS_Store" { + if path.extension().unwrap().to_str().unwrap() == "m3u8" { + let filename = path.file_name().unwrap().to_str().unwrap(); + m3u8.push(StreamURL { + url: filename.to_string(), + alias: filename.to_string(), + }); + } + } + if path.is_dir() { + let mut dir: Vec = vec![]; + for i in read_dir(&path).unwrap() { + dir.push(i.unwrap().file_name().to_str().unwrap().to_string()) + } + for i in &dir { + if i.contains(".m3u8") { + m3u8.push(StreamURL { + alias: path.file_name().unwrap().to_str().unwrap().to_string(), + url: format!("{}/{}", path.file_name().unwrap().to_str().unwrap(), i), + }); + } + } + } + } + m3u8.sort_by_key(|x| x.alias.clone()); + for i in m3u8 { + res += &*format!( + "{} Play (copy link into your player)
", + &i.url, &i.alias, &i.url + ); + } + RawHtml(res) +} + +#[rocket::main] +async fn main() -> Result<(), rocket::Error> { + let args = Args::parse(); + let _rocket = rocket::build() + .mount("/", routes![play, index]) + .mount("/", FileServer::from(args.streams_dir)) + .launch() + .await?; + + Ok(()) +}