Skip to content

Commit

Permalink
Change references of video to media
Browse files Browse the repository at this point in the history
  • Loading branch information
dormant-user committed Feb 25, 2024
1 parent f9f7fd4 commit 3b44e75
Show file tree
Hide file tree
Showing 17 changed files with 86 additions and 81 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "RuStream"
version = "1.0.1"
description = "Self-hosted Streaming Engine, that can render videos via authenticated sessions."
description = "Self-hosted Streaming Engine, that can render media files via authenticated sessions."
license = "MIT"
documentation = "https://docs.rs/RuStream"
homepage = "https://github.com/thevickypedia/RuStream"
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
[![none-shall-pass][nsp-logo]][nsp]

#### Summary
[`RuStream`][repo] is a self-hosted streaming engine, that can render videos via authenticated sessions.
[`RuStream`][repo] is a self-hosted streaming engine, that can render media files via authenticated sessions.

### Installation

Expand Down Expand Up @@ -61,14 +61,14 @@ curl -o RuStream-Windows-x86_64.zip -LH "Accept: application/octet-stream" "http

**Mandatory**
- **authorization**: Dictionary of key-value pairs with `username` as key and `password` as value.
- **video_source**: Source path for video files.
- **media_source**: Source path for the files to be streamed.
> Files starting with `_` _(underscore)_ and `.` _(dot)_ will be ignored
**Optional**
- **video_host**: IP address to host the video. Defaults to `127.0.0.1` / `localhost`
- **video_port**: Port number to host the application. Defaults to `8000`
- **media_host**: IP address to host the server. Defaults to `127.0.0.1` / `localhost`
- **media_port**: Port number to host the application. Defaults to `8000`
- **session_duration**: Time _(in seconds)_ each authenticated session should last. Defaults to `3600`
- **file_formats**: Vector of supported video file formats. Defaults to `[mp4, mov]`
- **file_formats**: Vector of supported file formats. Defaults to `[mp4, mov, jpg, jpeg]`
- **workers**: Number of workers to spin up for the server. Defaults to the number of physical cores.
- **max_connections**: Maximum number of concurrent connections per worker. Defaults to `3`
- **websites**: Vector of websites (_supports regex_) to add to CORS configuration. _Required only if tunneled via CDN_
Expand Down
8 changes: 4 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub async fn start() -> io::Result<()> {
// Create a dedicated clone, since it will be used within closure
let config_clone = config.clone();
let jinja_clone = jinja_env.clone();
let host = format!("{}:{}", config.video_host, config.video_port);
let host = format!("{}:{}", config.media_host, config.media_port);
log::info!("{} [workers:{}] running on http://{} (Press CTRL+C to quit)",
&cargo.pkg_name, &config.workers, &host);
/*
Expand All @@ -72,9 +72,9 @@ pub async fn start() -> io::Result<()> {
.service(routes::auth::logout)
.service(routes::auth::home)
.service(routes::auth::error)
.service(routes::video::track)
.service(routes::video::stream)
.service(routes::video::streaming_endpoint)
.service(routes::media::track)
.service(routes::media::stream)
.service(routes::media::streaming_endpoint)
};
let server = HttpServer::new(application)
.workers(config.workers as usize)
Expand Down
2 changes: 1 addition & 1 deletion src/routes/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ pub async fn logout(config: web::Data<Arc<squire::settings::Config>>,
if tracker.get(&host).is_some() {
tracker.remove(&host);
} else {
log::warn!("Session information for {} was not stored or no video was played", host);
log::warn!("Session information for {} was not stored or no file was rendered", host);
}
rendered = logout_template.render(minijinja::context!(detail => "You have been logged out successfully.")).unwrap();

Expand Down
45 changes: 23 additions & 22 deletions src/routes/video.rs → src/routes/media.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ fn url_encode(path: &String) -> String {
///
/// # Arguments
///
/// * `true_path` - True path of the requested video file.
/// * `relative_path` - The string representation of the relative video path.
/// * `true_path` - True path of the requested file.
/// * `relative_path` - The string representation of the relative filepath.
///
/// # Returns
///
Expand Down Expand Up @@ -88,7 +88,7 @@ pub async fn track(config: web::Data<Arc<squire::settings::Config>>,
squire::logger::log_connection(&request);
log::debug!("{}", auth_response.detail);
log::debug!("Track requested: {}", &info.file);
let filepath = Path::new(&config.video_source).join(&info.file);
let filepath = Path::new(&config.media_source).join(&info.file);
log::debug!("Track file lookup: {}", &filepath.to_string_lossy());
match std::fs::read_to_string(&filepath) {
Ok(content) => HttpResponse::Ok()
Expand Down Expand Up @@ -119,48 +119,48 @@ fn render_content(landing: Template, serializable: HashMap<&str, &String>) -> Ht
}
}

/// Handles requests for the '/stream/{video_path:.*}' endpoint, serving video files and directories.
/// Handles requests for the '/stream/{media_path:.*}' endpoint, serving media files and directories.
///
/// # Arguments
///
/// * `config` - Configuration data for the application.
/// * `environment` - Configuration container for the loaded templates.
/// * `request` - A reference to the Actix web `HttpRequest` object.
/// * `video_path` - The path parameter representing the video file or directory.
/// * `media_path` - The path parameter representing the media file or directory.
///
/// # Returns
///
/// Returns an `HttpResponse` containing the video content or directory listing, or an error response.
#[get("/stream/{video_path:.*}")]
/// Returns an `HttpResponse` containing the media content or directory listing, or an error response.
#[get("/stream/{media_path:.*}")]
pub async fn stream(config: web::Data<Arc<squire::settings::Config>>,
environment: web::Data<Arc<Mutex<Environment<'static>>>>,
request: HttpRequest, video_path: web::Path<String>) -> HttpResponse {
request: HttpRequest, media_path: web::Path<String>) -> HttpResponse {
let auth_response = squire::authenticator::verify_token(&request, &config);
if !auth_response.ok {
return routes::auth::failed_auth(auth_response, &config);
}
squire::logger::log_connection(&request);
log::debug!("{}", auth_response.detail);
let filepath = video_path.to_string();
// True path of the video file
let __target = config.video_source.join(&filepath);
let filepath = media_path.to_string();
// True path of the media file
let __target = config.media_source.join(&filepath);
if !__target.exists() {
return HttpResponse::NotFound().json(routes::auth::DetailError {
detail: format!("'{}' was not found", filepath)
});
}
// True path of the video file as a String
// True path of the media file as a String
let __target_str = __target.to_string_lossy().to_string();
let __filename = __target.file_name().unwrap().to_string_lossy().to_string();
let template = environment.lock().unwrap();
if __target.is_file() {
let landing = template.get_template("landing").unwrap();
let rust_iter = squire::content::get_iter(&__target, &config.file_formats);
let render_path = format!("/video?file={}", url_encode(&filepath));
let render_path = format!("/media?file={}", url_encode(&filepath));
let prev = rust_iter.previous.unwrap_or_default();
let next = rust_iter.next.unwrap_or_default();
let mut context_builder = vec![
("video_title", &__filename),
("media_title", &__filename),
("path", &render_path),
("previous", &prev),
("next", &next)
Expand Down Expand Up @@ -200,18 +200,19 @@ pub async fn stream(config: web::Data<Arc<squire::settings::Config>>,
return HttpResponse::build(StatusCode::OK)
.content_type("text/html; charset=utf-8")
.body(listing.render(context!(
custom_title => child_dir,
files => listing_page.files, directories => listing_page.directories)
).unwrap());
}
log::error!("Something went really wrong");
log::error!("Video Path: {}", filepath);
log::error!("Media Path: {}", filepath);
log::error!("Target: {}", __target_str);
HttpResponse::ExpectationFailed().json(routes::auth::DetailError {
detail: format!("'{}' was neither a file nor a folder", filepath)
})
}

/// Handles requests for the '/video' endpoint, serving video content for streaming.
/// Handles requests for the `/media` endpoint, serving media content for streaming.
///
/// # Arguments
///
Expand All @@ -221,8 +222,8 @@ pub async fn stream(config: web::Data<Arc<squire::settings::Config>>,
///
/// # Returns
///
/// Returns an `HttpResponse` containing the video content or an error response.
#[get("/video")]
/// Returns an `HttpResponse` containing the media content or an error response.
#[get("/media")]
pub async fn streaming_endpoint(config: web::Data<Arc<squire::settings::Config>>,
request: HttpRequest, info: web::Query<Payload>) -> HttpResponse {
let auth_response = squire::authenticator::verify_token(&request, &config);
Expand All @@ -231,9 +232,9 @@ pub async fn streaming_endpoint(config: web::Data<Arc<squire::settings::Config>>
}
squire::logger::log_connection(&request);
let host = request.connection_info().host().to_owned();
let video_path = config.video_source.join(&info.file);
if video_path.exists() {
let file = actix_files::NamedFile::open_async(video_path).await.unwrap();
let media_path = config.media_source.join(&info.file);
if media_path.exists() {
let file = actix_files::NamedFile::open_async(media_path).await.unwrap();
// Check if the host is making a continued connection streaming the same file
let mut tracker = constant::HOST_SERVE.lock().unwrap();
if tracker.get(&host).unwrap() != &info.file {
Expand All @@ -242,7 +243,7 @@ pub async fn streaming_endpoint(config: web::Data<Arc<squire::settings::Config>>
}
return file.into_response(&request);
}
let error = format!("File {:?} not found", video_path);
let error = format!("File {:?} not found", media_path);
log::error!("{}", error);
HttpResponse::NotFound().body(error)
}
6 changes: 3 additions & 3 deletions src/routes/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// Module for the primary and health check entrypoints.
/// Module for the primary and health check entry points.
pub mod basics;
/// Module for all the video based entrypoints.
pub mod video;
/// Module for all the rendering based entry points.
pub mod media;
/// Module for `/home`, `/login`, `/logout` and `/error` entrypoints.
pub mod auth;
4 changes: 2 additions & 2 deletions src/squire/content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ fn natural_sort_key(filename: &str) -> Vec<Result<i32, String>> {
pub fn get_all_stream_content(config: &Config) -> ContentPayload {
let mut payload = ContentPayload::default();

for entry in WalkDir::new(&config.video_source).into_iter().filter_map(|e| e.ok()) {
for entry in WalkDir::new(&config.media_source).into_iter().filter_map(|e| e.ok()) {
if entry.path().ends_with("__") {
continue;
}
Expand All @@ -80,7 +80,7 @@ pub fn get_all_stream_content(config: &Config) -> ContentPayload {

if let Some(extension) = PathBuf::from(file_name).extension().and_then(|ext| ext.to_str()) {
if config.file_formats.iter().any(|format| extension == format) {
let path = entry.path().strip_prefix(&config.video_source)
let path = entry.path().strip_prefix(&config.media_source)
.unwrap_or_else(|_| Path::new(""));
let components: &Vec<_> = &path.components().collect();
if components.len() == 1 {
Expand Down
2 changes: 1 addition & 1 deletion src/squire/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub mod ascii_art;
pub mod middleware;
/// Module for the function that converts the subtitles from `srt` to `vtt` file format.
pub mod subtitles;
/// Module for the functions that scan the video source and render the filenames as struct.
/// Module for the functions that scans the source and renders the filenames as a struct.
pub mod content;
/// Module that handles the authentication and
pub mod authenticator;
20 changes: 10 additions & 10 deletions src/squire/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ use std::net::ToSocketAddrs;
pub struct Config {
/// Dictionary of key-value pairs for authorization (username and password).
pub authorization: HashMap<String, String>,
/// Source path for video files.
pub video_source: path::PathBuf,
/// Source path for media files.
pub media_source: path::PathBuf,

/// Debug flag to enable debug level logging.
pub debug: bool,
/// Host IP address for video hosting.
pub video_host: String,
/// Host IP address for media streaming.
pub media_host: String,
/// Port number for hosting the application.
pub video_port: i32,
pub media_port: i32,
/// Duration of a session in seconds.
pub session_duration: i32,
/// List of supported video file formats.
/// List of supported file formats.
pub file_formats: Vec<String>,

/// Number of worker threads to spin up the server.
Expand All @@ -43,8 +43,8 @@ pub fn default_debug() -> bool { false }
/// Returns the default value for ssl files
pub fn default_ssl() -> path::PathBuf { path::PathBuf::new() }

/// Returns the default video host based on the local machine's IP address.
pub fn default_video_host() -> String {
/// Returns the default media host based on the local machine's IP address.
pub fn default_media_host() -> String {
let hostname = "localhost";
match (hostname, 0).to_socket_addrs() {
Ok(mut addrs) => {
Expand All @@ -59,8 +59,8 @@ pub fn default_video_host() -> String {
"localhost".to_string()
}

/// Returns the default video port (8000).
pub fn default_video_port() -> i32 { 8000 }
/// Returns the default media port (8000).
pub fn default_media_port() -> i32 { 8000 }

/// Returns the default session duration (3600 seconds).
pub fn default_session_duration() -> i32 { 3600 }
Expand Down
26 changes: 13 additions & 13 deletions src/squire/startup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub fn init_logger(debug: bool, crate_name: &String) {
));
std::env::set_var("RUST_BACKTRACE", "1");
} else {
// Set Actix logging to warning mode since it becomes too noisy when streaming a giant video file.
// Set Actix logging to warning mode since it becomes too noisy when streaming large files
std::env::set_var("RUST_LOG", format!(
"actix_web=warn,actix_server=warn,{}=info", crate_name
));
Expand Down Expand Up @@ -51,15 +51,15 @@ fn mandatory_vars() -> (std::collections::HashMap<String, String>, std::path::Pa
);
}
};
let video_source_str = match std::env::var("video_source") {
let media_source_str = match std::env::var("media_source") {
Ok(val) => val,
Err(_) => {
panic!(
"\nvideo_source\n\texpected a directory path, received null [value=missing]\n",
"\nmedia_source\n\texpected a directory path, received null [value=missing]\n",
);
}
};
(authorization, std::path::PathBuf::from(video_source_str))
(authorization, std::path::PathBuf::from(media_source_str))
}

/// Extracts the env var by key and parses it as a `bool`
Expand Down Expand Up @@ -163,10 +163,10 @@ fn parse_path(key: &str) -> Option<std::path::PathBuf> {
///
/// Instantiates the `Config` struct with the required parameters.
fn load_env_vars() -> settings::Config {
let (authorization, video_source) = mandatory_vars();
let (authorization, media_source) = mandatory_vars();
let debug = parse_bool("debug").unwrap_or(settings::default_debug());
let video_host = std::env::var("video_host").unwrap_or(settings::default_video_host());
let video_port = parse_i32("video_port").unwrap_or(settings::default_video_port());
let media_host = std::env::var("media_host").unwrap_or(settings::default_media_host());
let media_port = parse_i32("media_port").unwrap_or(settings::default_media_port());
let session_duration = parse_i32("session_duration").unwrap_or(settings::default_session_duration());
let file_formats = parse_vec("file_formats").unwrap_or(settings::default_file_formats());
let workers = parse_i32("workers").unwrap_or(settings::default_workers());
Expand All @@ -177,10 +177,10 @@ fn load_env_vars() -> settings::Config {
let cert_file = parse_path("cert_file").unwrap_or(settings::default_ssl());
settings::Config {
authorization,
video_source,
media_source,
debug,
video_host,
video_port,
media_host,
media_port,
session_duration,
file_formats,
workers,
Expand All @@ -200,10 +200,10 @@ fn load_env_vars() -> settings::Config {
fn validate_vars() -> settings::Config {
let config = load_env_vars();
let mut errors = "".to_owned();
if !config.video_source.exists() || !config.video_source.is_dir() {
if !config.media_source.exists() || !config.media_source.is_dir() {
let err1 = format!(
"\nvideo_source\n\tInput [{}] is not a valid directory [value=invalid]\n",
config.video_source.to_string_lossy()
"\nmedia_source\n\tInput [{}] is not a valid directory [value=invalid]\n",
config.media_source.to_string_lossy()
);
errors.push_str(&err1);
}
Expand Down
4 changes: 2 additions & 2 deletions src/templates/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ pub fn get_content() -> String {
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Rustic video streaming</title>
<meta property="og:type" content="VideoStreaming">
<title>RuStream - Self-hosted Streaming Engine</title>
<meta property="og:type" content="MediaStreaming">
<meta name="keywords" content="Rust, streaming, actix, JavaScript, HTML, CSS">
<meta name="author" content="Vignesh Rao">
<meta content="width=device-width, initial-scale=1" name="viewport">
Expand Down
Loading

0 comments on commit 3b44e75

Please sign in to comment.