convert text/m3u file to playlist, #195
This commit is contained in:
parent
1e01757f3b
commit
69a3e59e35
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -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",
|
||||||
|
@ -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]
|
||||||
|
@ -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?;
|
||||||
|
@ -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" }
|
||||||
|
@ -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));
|
||||||
|
@ -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>,
|
||||||
|
|
||||||
|
@ -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
78
lib/src/utils/import.rs
Normal 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)),
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user