mirror of
https://github.com/ivabus/hlssimple
synced 2024-11-21 15:45:08 +03:00
Initial commit
This commit is contained in:
commit
acdf0d39f2
9 changed files with 176 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
.DS_Store
|
9
.rustfmt.toml
Normal file
9
.rustfmt.toml
Normal file
|
@ -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
|
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
|
@ -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"
|
21
README.md
Normal file
21
README.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# HLSsimple
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cargo install --git https://github.com/ivabus/hlssimple
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```shell
|
||||||
|
hlssimple --stream-dir <STREAMS_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) |
|
9
Rocket.toml
Normal file
9
Rocket.toml
Normal file
|
@ -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
|
BIN
img/obs_all_in_one.webp
Normal file
BIN
img/obs_all_in_one.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 102 KiB |
BIN
img/obs_split.webp
Normal file
BIN
img/obs_split.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 103 KiB |
11
index.html
Normal file
11
index.html
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<h1>Stream index</h1>
|
||||||
|
|
||||||
|
<h2>How to play?</h2>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>If you're using Safari-based browsers (on iOS, iPadOS or macOS) <a href=\"https://caniuse.com/http-live-streaming\">(browser support)</a> you can just click "Play"</li>
|
||||||
|
<li>Also you can play this streams using VLCs "Open URL" and pasting link to .m3u8 file</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
111
src/main.rs
Normal file
111
src/main.rs
Normal file
|
@ -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/<page..>")]
|
||||||
|
fn play(page: PathBuf) -> RawHtml<String> {
|
||||||
|
RawHtml(format!(
|
||||||
|
"<video controls>
|
||||||
|
<source src=\"/{}\" type=\"application/x-mpegURL\">
|
||||||
|
</video>",
|
||||||
|
page.to_str().unwrap()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StreamURL {
|
||||||
|
alias: String,
|
||||||
|
url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
fn index() -> RawHtml<String> {
|
||||||
|
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<StreamURL> = 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<String> = 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!(
|
||||||
|
"<a href=\"{}\">{}</a> <a href=\"/play/{}\">Play</a> (copy link into your player)<br>",
|
||||||
|
&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(())
|
||||||
|
}
|
Loading…
Reference in a new issue