get program infos
This commit is contained in:
parent
d41f35bedd
commit
f576dedcb9
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -962,7 +962,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ffplayout"
|
||||
version = "0.16.3"
|
||||
version = "0.16.4"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
@ -982,7 +982,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ffplayout-api"
|
||||
version = "0.7.1"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"actix-files",
|
||||
"actix-multipart",
|
||||
@ -999,6 +999,7 @@ dependencies = [
|
||||
"jsonwebtoken",
|
||||
"once_cell",
|
||||
"rand",
|
||||
"regex",
|
||||
"relative-path",
|
||||
"reqwest",
|
||||
"rpassword",
|
||||
@ -1012,7 +1013,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ffplayout-lib"
|
||||
version = "0.16.3"
|
||||
version = "0.16.4"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"crossbeam-channel",
|
||||
|
@ -20,7 +20,7 @@ Check the [releases](https://github.com/ffplayout/ffplayout/releases/latest) for
|
||||
- send emails with error message
|
||||
- overlay a logo
|
||||
- overlay text, controllable through [ffplayout-frontend](https://github.com/ffplayout/ffplayout-frontend) (needs ffmpeg with libzmq and enabled JSON RPC server)
|
||||
- EBU R128 loudness normalization (single pass)
|
||||
- EBU R128 loudness normalization (single pass) (experimental *)
|
||||
- loop playlist infinitely
|
||||
- [remote source](/docs/remote_source.md)
|
||||
- trim and fade the last clip, to get full 24 hours
|
||||
|
@ -4,7 +4,7 @@ description = "Rest API for ffplayout"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Jonathan Baecker jonbae77@gmail.com"]
|
||||
readme = "README.md"
|
||||
version = "0.7.1"
|
||||
version = "0.8.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@ -23,6 +23,7 @@ futures-util = { version = "0.3", default-features = false, features = ["std"] }
|
||||
jsonwebtoken = "8"
|
||||
once_cell = "1.10"
|
||||
rand = "0.8"
|
||||
regex = "1"
|
||||
relative-path = "1.6"
|
||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||
rpassword = "6.0"
|
||||
|
@ -17,6 +17,8 @@ use argon2::{
|
||||
password_hash::{rand_core::OsRng, PasswordHash, SaltString},
|
||||
Argon2, PasswordHasher, PasswordVerifier,
|
||||
};
|
||||
use chrono::{DateTime, Duration, Local, NaiveDateTime, TimeZone, Utc};
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use simplelog::*;
|
||||
|
||||
@ -33,10 +35,16 @@ use crate::utils::{
|
||||
browser, create_directory, remove_file_or_folder, rename_file, upload, MoveObject,
|
||||
PathObject,
|
||||
},
|
||||
naive_date_time_from_str,
|
||||
playlist::{delete_playlist, generate_playlist, read_playlist, write_playlist},
|
||||
playout_config, read_log_file, read_playout_config, Role,
|
||||
};
|
||||
use ffplayout_lib::utils::{import::import_file, JsonPlaylist, PlayoutConfig};
|
||||
use ffplayout_lib::{
|
||||
utils::{
|
||||
get_date_range, import::import_file, sec_to_time, time_to_sec, JsonPlaylist, PlayoutConfig,
|
||||
},
|
||||
vec_strings,
|
||||
};
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ResponseObj<T> {
|
||||
@ -71,6 +79,24 @@ pub struct ImportObj {
|
||||
date: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct ProgramObj {
|
||||
#[serde(deserialize_with = "naive_date_time_from_str")]
|
||||
start_after: Option<NaiveDateTime>,
|
||||
#[serde(deserialize_with = "naive_date_time_from_str")]
|
||||
start_before: Option<NaiveDateTime>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct ProgramTtem {
|
||||
source: String,
|
||||
start: String,
|
||||
r#in: f64,
|
||||
out: f64,
|
||||
duration: f64,
|
||||
category: String,
|
||||
}
|
||||
|
||||
/// #### User Handling
|
||||
///
|
||||
/// **Login**
|
||||
@ -676,7 +702,7 @@ pub async fn del_playlist(
|
||||
/// **Read Log Life**
|
||||
///
|
||||
/// ```BASH
|
||||
/// curl -X Get http://127.0.0.1:8787/api/log/1
|
||||
/// curl -X GET http://127.0.0.1:8787/api/log/1
|
||||
/// -H 'Content-Type: application/json' -H 'Authorization: Bearer <TOKEN>'
|
||||
/// ```
|
||||
#[get("/log/{id}")]
|
||||
@ -762,7 +788,7 @@ pub async fn remove(
|
||||
/// **Upload File**
|
||||
///
|
||||
/// ```BASH
|
||||
/// curl -X POST http://127.0.0.1:8787/api/file/1/upload/ -H 'Authorization: Bearer <TOKEN>'
|
||||
/// curl -X PUT http://127.0.0.1:8787/api/file/1/upload/ -H 'Authorization: Bearer <TOKEN>'
|
||||
/// -F "file=@file.mp4"
|
||||
/// ```
|
||||
#[put("/file/{id}/upload/")]
|
||||
@ -781,7 +807,7 @@ async fn save_file(
|
||||
/// lines with leading "#" will be ignore
|
||||
///
|
||||
/// ```BASH
|
||||
/// curl -X POST http://127.0.0.1:8787/api/file/1/import/ -H 'Authorization: Bearer <TOKEN>'
|
||||
/// curl -X PUT http://127.0.0.1:8787/api/file/1/import/ -H 'Authorization: Bearer <TOKEN>'
|
||||
/// -F "file=@list.m3u"
|
||||
/// ```
|
||||
#[put("/file/{id}/import/")]
|
||||
@ -803,3 +829,88 @@ async fn import_playlist(
|
||||
|
||||
Ok(HttpResponse::Ok().into())
|
||||
}
|
||||
|
||||
/// **Program info**
|
||||
///
|
||||
/// Get infos about give program
|
||||
///
|
||||
/// ```BASH
|
||||
/// curl -X GET http://127.0.0.1:8787/program/1/?start_after=2022-11-13T12:00:00&start_before=2022-11-20T11:59:59 \
|
||||
/// -H 'Authorization: Bearer <TOKEN>'
|
||||
/// ```
|
||||
#[get("/program/{id}/")]
|
||||
#[has_any_role("Role::Admin", "Role::User", type = "Role")]
|
||||
async fn get_program(
|
||||
id: web::Path<i32>,
|
||||
obj: web::Query<ProgramObj>,
|
||||
) -> Result<impl Responder, ServiceError> {
|
||||
let (config, _) = playout_config(&*id).await?;
|
||||
let start_sec = config.playlist.start_sec.unwrap();
|
||||
let mut days = 0;
|
||||
let mut program = vec![];
|
||||
|
||||
let after = match obj.start_after {
|
||||
Some(d) => d,
|
||||
None => Utc::now().naive_utc(),
|
||||
};
|
||||
|
||||
let before = match obj.start_before {
|
||||
Some(d) => d,
|
||||
None => Utc::now().naive_utc(),
|
||||
};
|
||||
|
||||
if start_sec > time_to_sec(&after.format("%H:%M:%S").to_string()) {
|
||||
days = 1;
|
||||
}
|
||||
|
||||
let date_range = get_date_range(&vec_strings![
|
||||
(after - Duration::days(days)).format("%Y-%m-%d"),
|
||||
"-",
|
||||
before.format("%Y-%m-%d")
|
||||
]);
|
||||
|
||||
for date in date_range {
|
||||
let mut naive = NaiveDateTime::parse_from_str(
|
||||
&format!("{date} {}", sec_to_time(start_sec)),
|
||||
"%Y-%m-%d %H:%M:%S%.3f",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let playlist = match read_playlist(*id, date.clone()).await {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
error!("Error in PLaylist from {date}: {e}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
for item in playlist.program {
|
||||
let start: DateTime<Local> = Local.from_local_datetime(&naive).unwrap();
|
||||
|
||||
let source = match Regex::new(&config.text.regex)
|
||||
.ok()
|
||||
.and_then(|r| r.captures(&item.source))
|
||||
{
|
||||
Some(t) => t[1].to_string(),
|
||||
None => item.source,
|
||||
};
|
||||
|
||||
let p_item = ProgramTtem {
|
||||
source,
|
||||
start: start.format("%Y-%m-%d %H:%M:%S%.3f%:z").to_string(),
|
||||
r#in: item.seek,
|
||||
out: item.out,
|
||||
duration: item.duration,
|
||||
category: item.category,
|
||||
};
|
||||
|
||||
if naive >= after && naive <= before {
|
||||
program.push(p_item);
|
||||
}
|
||||
|
||||
naive += Duration::milliseconds(((item.out - item.seek) * 1000.0) as i64);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(web::Json(program))
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ use api::{
|
||||
routes::{
|
||||
add_channel, add_dir, add_preset, add_user, control_playout, del_playlist, delete_preset,
|
||||
file_browser, gen_playlist, get_all_channels, get_channel, get_log, get_playlist,
|
||||
get_playout_config, get_presets, get_user, import_playlist, login, media_current,
|
||||
media_last, media_next, move_rename, patch_channel, process_control, remove,
|
||||
get_playout_config, get_presets, get_program, get_user, import_playlist, login,
|
||||
media_current, media_last, media_next, move_rename, patch_channel, process_control, remove,
|
||||
remove_channel, save_file, save_playlist, send_text_message, update_playout_config,
|
||||
update_preset, update_user,
|
||||
},
|
||||
@ -121,7 +121,8 @@ async fn main() -> std::io::Result<()> {
|
||||
.service(move_rename)
|
||||
.service(remove)
|
||||
.service(save_file)
|
||||
.service(import_playlist),
|
||||
.service(import_playlist)
|
||||
.service(get_program),
|
||||
)
|
||||
.service(Files::new("/", public_path()).index_file("index.html"))
|
||||
})
|
||||
|
@ -5,10 +5,11 @@ use std::{
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use chrono::prelude::*;
|
||||
use chrono::{format::ParseErrorKind, prelude::*};
|
||||
use faccess::PathExt;
|
||||
use once_cell::sync::OnceCell;
|
||||
use rpassword::read_password;
|
||||
use serde::{de, Deserialize, Deserializer};
|
||||
use simplelog::*;
|
||||
|
||||
pub mod args_parse;
|
||||
@ -23,7 +24,7 @@ use crate::db::{
|
||||
models::{Channel, User},
|
||||
};
|
||||
use crate::utils::{args_parse::Args, errors::ServiceError};
|
||||
use ffplayout_lib::utils::PlayoutConfig;
|
||||
use ffplayout_lib::utils::{time_to_sec, PlayoutConfig};
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub enum Role {
|
||||
@ -175,7 +176,9 @@ pub async fn run_args(mut args: Args) -> Result<(), i32> {
|
||||
|
||||
pub fn read_playout_config(path: &str) -> Result<PlayoutConfig, Box<dyn Error>> {
|
||||
let file = File::open(path)?;
|
||||
let config: PlayoutConfig = serde_yaml::from_reader(file)?;
|
||||
let mut config: PlayoutConfig = serde_yaml::from_reader(file)?;
|
||||
|
||||
config.playlist.start_sec = Some(time_to_sec(&config.playlist.day_start));
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
@ -234,3 +237,26 @@ pub fn local_utc_offset() -> i32 {
|
||||
|
||||
utc_offset
|
||||
}
|
||||
|
||||
pub fn naive_date_time_from_str<'de, D>(deserializer: D) -> Result<Option<NaiveDateTime>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s: String = Deserialize::deserialize(deserializer)?;
|
||||
match NaiveDateTime::parse_from_str(&s, "%Y-%m-%dT%H:%M:%S") {
|
||||
Ok(date_time) => Ok(Some(date_time)),
|
||||
Err(e) => {
|
||||
if e.kind() == ParseErrorKind::TooShort {
|
||||
match NaiveDateTime::parse_from_str(&format!("{s}T00:00:00"), "%Y-%m-%dT%H:%M:%S") {
|
||||
Ok(date_time) => Ok(Some(date_time)),
|
||||
Err(e) => Err(de::Error::custom(e)),
|
||||
}
|
||||
} else {
|
||||
match NaiveDateTime::parse_from_str(&s, "%Y-%m-%dT%H:%M:%S%#z") {
|
||||
Ok(date_time) => Ok(Some(date_time)),
|
||||
Err(_) => Err(de::Error::custom(e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ description = "24/7 playout based on rust and ffmpeg"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Jonathan Baecker jonbae77@gmail.com"]
|
||||
readme = "README.md"
|
||||
version = "0.16.3"
|
||||
version = "0.16.4"
|
||||
edition = "2021"
|
||||
default-run = "ffplayout"
|
||||
|
||||
|
@ -4,7 +4,7 @@ description = "Library for ffplayout"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Jonathan Baecker jonbae77@gmail.com"]
|
||||
readme = "README.md"
|
||||
version = "0.16.3"
|
||||
version = "0.16.4"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
@ -14,41 +14,12 @@ use std::{
|
||||
sync::{atomic::AtomicUsize, Arc, Mutex},
|
||||
};
|
||||
|
||||
use chrono::{Duration, NaiveDate};
|
||||
use simplelog::*;
|
||||
|
||||
use super::folder::FolderSource;
|
||||
use crate::utils::{json_serializer::JsonPlaylist, time_to_sec, Media, PlayoutConfig};
|
||||
|
||||
/// Generate a vector with dates, from given range.
|
||||
fn get_date_range(date_range: &[String]) -> Vec<String> {
|
||||
let mut range = vec![];
|
||||
|
||||
let start = match NaiveDate::parse_from_str(&date_range[0], "%Y-%m-%d") {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
error!("date format error in: <yellow>{:?}</>", date_range[0]);
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let end = match NaiveDate::parse_from_str(&date_range[2], "%Y-%m-%d") {
|
||||
Ok(e) => e,
|
||||
Err(_) => {
|
||||
error!("date format error in: <yellow>{:?}</>", date_range[2]);
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let duration = end.signed_duration_since(start);
|
||||
let days = duration.num_days() + 1;
|
||||
|
||||
for day in 0..days {
|
||||
range.push((start + Duration::days(day)).format("%Y-%m-%d").to_string());
|
||||
}
|
||||
|
||||
range
|
||||
}
|
||||
use crate::utils::{
|
||||
get_date_range, json_serializer::JsonPlaylist, time_to_sec, Media, PlayoutConfig,
|
||||
};
|
||||
|
||||
/// Generate playlists
|
||||
pub fn generate_playlist(
|
||||
|
@ -797,6 +797,36 @@ pub fn test_tcp_port(url: &str) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Generate a vector with dates, from given range.
|
||||
pub fn get_date_range(date_range: &[String]) -> Vec<String> {
|
||||
let mut range = vec![];
|
||||
|
||||
let start = match NaiveDate::parse_from_str(&date_range[0], "%Y-%m-%d") {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
error!("date format error in: <yellow>{:?}</>", date_range[0]);
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let end = match NaiveDate::parse_from_str(&date_range[2], "%Y-%m-%d") {
|
||||
Ok(e) => e,
|
||||
Err(_) => {
|
||||
error!("date format error in: <yellow>{:?}</>", date_range[2]);
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let duration = end.signed_duration_since(start);
|
||||
let days = duration.num_days() + 1;
|
||||
|
||||
for day in 0..days {
|
||||
range.push((start + Duration::days(day)).format("%Y-%m-%d").to_string());
|
||||
}
|
||||
|
||||
range
|
||||
}
|
||||
|
||||
pub fn home_dir() -> Option<PathBuf> {
|
||||
home_dir_inner()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user