convert text/m3u file to playlist, #195

This commit is contained in:
jb-alvarado 2022-09-29 21:33:54 +02:00
parent 1e01757f3b
commit 69a3e59e35
9 changed files with 144 additions and 33 deletions

6
Cargo.lock generated
View File

@ -941,7 +941,7 @@ dependencies = [
[[package]] [[package]]
name = "ffplayout" name = "ffplayout"
version = "0.15.2" version = "0.16.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"clap", "clap",
@ -960,7 +960,7 @@ dependencies = [
[[package]] [[package]]
name = "ffplayout-api" name = "ffplayout-api"
version = "0.6.2" version = "0.6.3"
dependencies = [ dependencies = [
"actix-files", "actix-files",
"actix-multipart", "actix-multipart",
@ -990,7 +990,7 @@ dependencies = [
[[package]] [[package]]
name = "ffplayout-lib" name = "ffplayout-lib"
version = "0.15.2" version = "0.16.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"crossbeam-channel", "crossbeam-channel",

View File

@ -4,7 +4,7 @@ description = "Rest API for ffplayout"
license = "GPL-3.0" license = "GPL-3.0"
authors = ["Jonathan Baecker jonbae77@gmail.com"] authors = ["Jonathan Baecker jonbae77@gmail.com"]
readme = "README.md" readme = "README.md"
version = "0.6.2" version = "0.6.3"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View File

@ -1,31 +1,11 @@
use std::{ use std::{fs, path::PathBuf};
fs::{self, File},
io::Error,
path::PathBuf,
};
use simplelog::*; use simplelog::*;
use crate::utils::{errors::ServiceError, playout_config}; use crate::utils::{errors::ServiceError, playout_config};
use ffplayout_lib::utils::{generate_playlist as playlist_generator, JsonPlaylist}; use ffplayout_lib::utils::{
generate_playlist as playlist_generator, json_reader, json_writer, JsonPlaylist,
fn json_reader(path: &PathBuf) -> Result<JsonPlaylist, Error> { };
let f = File::options().read(true).write(false).open(&path)?;
let p = serde_json::from_reader(f)?;
Ok(p)
}
fn json_writer(path: &PathBuf, data: JsonPlaylist) -> Result<(), Error> {
let f = File::options()
.write(true)
.truncate(true)
.create(true)
.open(&path)?;
serde_json::to_writer_pretty(f, &data)?;
Ok(())
}
pub async fn read_playlist(id: i64, date: String) -> Result<JsonPlaylist, ServiceError> { pub async fn read_playlist(id: i64, date: String) -> Result<JsonPlaylist, ServiceError> {
let (config, _) = playout_config(&id).await?; let (config, _) = playout_config(&id).await?;

View File

@ -4,8 +4,9 @@ description = "24/7 playout based on rust and ffmpeg"
license = "GPL-3.0" license = "GPL-3.0"
authors = ["Jonathan Baecker jonbae77@gmail.com"] authors = ["Jonathan Baecker jonbae77@gmail.com"]
readme = "README.md" readme = "README.md"
version = "0.15.2" version = "0.16.0"
edition = "2021" edition = "2021"
default-run = "ffplayout"
[dependencies] [dependencies]
ffplayout-lib = { path = "../lib" } ffplayout-lib = { path = "../lib" }

View File

@ -35,7 +35,7 @@ use ffplayout_lib::utils::{
use utils::Args; use utils::Args;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
use ffplayout_lib::utils::{mock_time, time_now}; use ffplayout_lib::utils::{import::import_file, mock_time, time_now};
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct StatusData { struct StatusData {
@ -93,7 +93,7 @@ fn main() {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
fake_time(&args); fake_time(&args);
let config = get_config(args); let config = get_config(args.clone());
let config_clone = config.clone(); let config_clone = config.clone();
let play_control = PlayerControl::new(); let play_control = PlayerControl::new();
let playout_stat = PlayoutStatus::new(); let playout_stat = PlayoutStatus::new();
@ -122,6 +122,26 @@ fn main() {
exit(0); exit(0);
} }
if let Some(path) = args.import {
if args.date.is_none() {
error!("Import needs date parameter!");
exit(1);
}
// convert text/m3u file to playlist
match import_file(&config, &args.date.unwrap(), None, &path) {
Ok(m) => {
info!("{m}");
exit(0);
}
Err(e) => {
error!("{e}");
exit(1);
}
}
}
if config.rpc_server.enable { if config.rpc_server.enable {
// If RPC server is enable we also fire up a JSON RPC server. // If RPC server is enable we also fire up a JSON RPC server.
thread::spawn(move || json_rpc_server(config_clone, play_ctl, play_stat, proc_ctl2)); thread::spawn(move || json_rpc_server(config_clone, play_ctl, play_stat, proc_ctl2));

View File

@ -31,6 +31,19 @@ pub struct Args {
#[clap(short, long, help = "Play folder content")] #[clap(short, long, help = "Play folder content")]
pub folder: Option<String>, pub folder: Option<String>,
#[clap(
short,
long,
help = "Target date (YYYY-MM-DD) for text/m3u to playlist import"
)]
pub date: Option<String>,
#[clap(
long,
help = "Import a given text/m3u file and create a playlist from it"
)]
pub import: Option<String>,
#[clap(short, long, help = "Path from playlist")] #[clap(short, long, help = "Path from playlist")]
pub playlist: Option<String>, pub playlist: Option<String>,

View File

@ -4,7 +4,7 @@ description = "Library for ffplayout"
license = "GPL-3.0" license = "GPL-3.0"
authors = ["Jonathan Baecker jonbae77@gmail.com"] authors = ["Jonathan Baecker jonbae77@gmail.com"]
readme = "README.md" readme = "README.md"
version = "0.15.2" version = "0.16.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

78
lib/src/utils/import.rs Normal file
View File

@ -0,0 +1,78 @@
/// Import text/m3u file and create a playlist out of it
use std::{
//error::Error,
fs::{create_dir_all, File},
io::{BufRead, BufReader, Error, ErrorKind},
path::Path,
};
use crate::utils::{json_reader, json_serializer::JsonPlaylist, json_writer, Media, PlayoutConfig};
pub fn import_file(
config: &PlayoutConfig,
date: &str,
channel_name: Option<String>,
path: &str,
) -> Result<String, Error> {
let file = File::open(path)?;
let reader = BufReader::new(file);
let mut playlist = JsonPlaylist {
channel: channel_name.unwrap_or_else(|| "Channel 1".to_string()),
date: date.to_string(),
current_file: None,
start_sec: None,
modified: None,
program: vec![],
};
let playlist_root = Path::new(&config.playlist.path);
if !playlist_root.is_dir() {
return Err(Error::new(
ErrorKind::Other,
format!(
"Playlist folder <b><magenta>{}</></b> not exists!",
&config.playlist.path,
),
));
}
let d: Vec<&str> = date.split('-').collect();
let year = d[0];
let month = d[1];
let playlist_path = playlist_root.join(year).join(month);
let playlist_file = &playlist_path.join(format!("{date}.json"));
create_dir_all(playlist_path)?;
for line in reader.lines() {
let line = line?;
if !line.starts_with('#') {
let item = Media::new(0, line, true);
playlist.program.push(item);
}
}
let mut file_exists = false;
if playlist_file.is_file() {
file_exists = true;
let existing_data = json_reader(playlist_file)?;
if playlist == existing_data {
return Ok(format!("Playlist from {date}, already exists!"));
}
};
let mut msg = format!("Write playlist from {date} success!");
if file_exists {
msg = format!("Update playlist from {date} success!");
}
match json_writer(playlist_file, playlist) {
Ok(_) => Ok(msg),
Err(e) => Err(Error::new(ErrorKind::Other, e)),
}
}

View File

@ -1,6 +1,6 @@
use std::{ use std::{
ffi::OsStr, ffi::OsStr,
fs::{self, metadata}, fs::{self, metadata, File},
io::{BufRead, BufReader, Error}, io::{BufRead, BufReader, Error},
net::TcpListener, net::TcpListener,
path::{Path, PathBuf}, path::{Path, PathBuf},
@ -26,6 +26,7 @@ pub mod config;
pub mod controller; pub mod controller;
pub mod folder; pub mod folder;
mod generator; mod generator;
pub mod import;
pub mod json_serializer; pub mod json_serializer;
mod json_validate; mod json_validate;
mod logging; mod logging;
@ -247,6 +248,24 @@ pub fn fps_calc(r_frame_rate: &str, default: f64) -> f64 {
fps fps
} }
pub fn json_reader(path: &PathBuf) -> Result<JsonPlaylist, Error> {
let f = File::options().read(true).write(false).open(&path)?;
let p = serde_json::from_reader(f)?;
Ok(p)
}
pub fn json_writer(path: &PathBuf, data: JsonPlaylist) -> Result<(), Error> {
let f = File::options()
.write(true)
.truncate(true)
.create(true)
.open(&path)?;
serde_json::to_writer_pretty(f, &data)?;
Ok(())
}
/// Covert JSON string to ffmpeg filter command. /// Covert JSON string to ffmpeg filter command.
pub fn get_filter_from_json(raw_text: String) -> String { pub fn get_filter_from_json(raw_text: String) -> String {
let re1 = Regex::new(r#""|}|\{"#).unwrap(); let re1 = Regex::new(r#""|}|\{"#).unwrap();