diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f50a0b71..a25327eeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ # Changelog +# 2.03(2024-07-x) +* added `source` - `input` - `name` attribute to README +* added `chno` to Playlist attributes. +* `epg_channel_id` mapping fixed + # v2.0.2(2024-05-28) * Added Encoding handling: gzip,deflate * Fixed panic when `tvg-id` is not set. diff --git a/README.md b/README.md index 1b1150d83..e745c2ab1 100644 --- a/README.md +++ b/README.md @@ -248,6 +248,7 @@ This will replace all occurrences of `!delimiter!` and `!quality!` in the regexp Each input has the following attributes: +- `name` is optional, if set it must be unique, should be set for the webui - `type` is optional, default is `m3u`. Valid values are `m3u` and `xtream` - `enabled` is optional, default is true, if you disable the processing is skipped - `persist` is optional, you can skip or leave it blank to avoid persisting the input file. The `{}` in the filename is filled with the current timestamp. @@ -278,6 +279,7 @@ Example input config for `m3u` sources: - inputs: - url: 'test-input.m3u' + name: test_m3u epg_url: 'test-epg.xml' enabled: false persist: 'playlist_1_{}.m3u' @@ -598,7 +600,7 @@ Attributes is a map of key value pairs. Valid keys are: - `audio_track` - `time_shift` - `rec` -- `source` +- `url` If the regexps matches, the given fields will be set to the new value You can use `captures` in attributes. diff --git a/src/model/config.rs b/src/model/config.rs index ee7bc4f22..73a114903 100644 --- a/src/model/config.rs +++ b/src/model/config.rs @@ -25,10 +25,12 @@ use crate::utils::default_utils::{default_as_default, default_as_false, default_ default_as_zero_u8, default_as_two_u16}; pub(crate) const MAPPER_ATTRIBUTE_FIELDS: &[&str] = &[ - "name", "title", "group", "id", "logo", + "name", "title", "group", "id", "chno", "logo", "logo_small", "parent_code", "audio_track", - "time_shift", "rec", "url", + "time_shift", "rec", "url", "epg_channel_id", "epg_id" ]; + + pub(crate) const AFFIX_FIELDS: &[&str] = &["name", "title", "group"]; #[macro_export] diff --git a/src/model/playlist.rs b/src/model/playlist.rs index 4983280b4..9490cda95 100644 --- a/src/model/playlist.rs +++ b/src/model/playlist.rs @@ -67,6 +67,7 @@ pub(crate) struct PlaylistItemHeader { pub stream_id: Rc, pub id: Rc, pub name: Rc, + pub chno: Rc, pub logo: Rc, pub logo_small: Rc, pub group: Rc, @@ -90,32 +91,6 @@ pub(crate) struct PlaylistItemHeader { pub input_id: u16, } -macro_rules! update_fields { - ($self:expr, $field:expr, $($prop:ident),*; $val:expr) => { - match $field { - $( - stringify!($prop) => { - $self.$prop = Rc::new($val); - true - } - )* - _ => false, - } - }; -} - -macro_rules! get_fields { - ($self:expr, $field:expr, $($prop:ident),*;) => { - match $field { - $( - stringify!($prop) => Some($self.$prop.clone()), - )* - _ => None, - } - }; -} - - macro_rules! to_m3u_non_empty_fields { ($header:expr, $line:expr, $(($prop:ident, $field:expr)),*;) => { $( @@ -126,39 +101,49 @@ macro_rules! to_m3u_non_empty_fields { }; } -impl FieldAccessor for PlaylistItemHeader { - fn get_field(&self, field: &str) -> Option> { - let val = get_fields!(self, field, id, stream_id, name, logo, logo_small, group, title, parent_code, audio_track, time_shift, rec, url;); - if val.is_some() { - return val; - } - match field { - "epg_channel_id" | "epg_id" => self.epg_channel_id.clone(), - _ => None - } - } - fn set_field(&mut self, field: &str, value: &str) -> bool { - let val = String::from(value); - let updated = update_fields!(self, field, id, stream_id, name, logo, logo_small, group, title, parent_code, audio_track, time_shift, rec, url; val); - if updated { - return updated; - } - match field { - "epg_channel_id" | "epg_id" => { - self.epg_channel_id = Some(Rc::new(value.to_owned())); - true +macro_rules! generate_field_accessor_impl_for_playlist_item_header { + ($($prop:ident),*;) => { + impl FieldAccessor for PlaylistItemHeader { + fn get_field(&self, field: &str) -> Option> { + match field { + $( + stringify!($prop) => Some(self.$prop.clone()), + )* + "epg_channel_id" | "epg_id" => self.epg_channel_id.clone(), + _ => None, + } + } + + fn set_field(&mut self, field: &str, value: &str) -> bool { + let val = String::from(value); + match field { + $( + stringify!($prop) => { + self.$prop = Rc::new(val); + true + } + )* + "epg_channel_id" | "epg_id" => { + self.epg_channel_id = Some(Rc::new(value.to_owned())); + true + } + _ => false, + } } - _ => false, } } } +// !! should be in sync with MAPPER_ATTRIBUTES except stream_id !! +generate_field_accessor_impl_for_playlist_item_header!(id, stream_id, name, chno, logo, logo_small, group, title, parent_code, audio_track, time_shift, rec, url;); + #[derive(Debug, Clone, Serialize, Deserialize)] pub(crate) struct M3uPlaylistItem { pub stream_id: Rc, pub provider_id: Rc, pub name: Rc, + pub chno: Rc, pub logo: Rc, pub logo_small: Rc, pub group: Rc, @@ -180,13 +165,12 @@ impl M3uPlaylistItem { self.epg_channel_id.as_ref().map_or("", |o| o.as_ref()), self.name, self.group); - // line = format!("{} tvg-chno=\"{}\"", line, header.chno); - if !ignore_logo { to_m3u_non_empty_fields!(self, line, (logo, "tvg-logo"), (logo_small, "tvg-logo-small");); } to_m3u_non_empty_fields!(self, line, + (chno, "tvg-chno"), (parent_code, "parent-code"), (audio_track, "audio-track"), (time_shift, "timeshift"), @@ -235,6 +219,7 @@ impl PlaylistItem { stream_id: Rc::clone(&header.stream_id), provider_id: Rc::clone(&header.id), name: Rc::clone(&header.name), + chno: Rc::clone(&header.chno), logo: Rc::clone(&header.logo), logo_small: Rc::clone(&header.logo_small), group: Rc::clone(&header.group), diff --git a/src/processing/m3u_parser.rs b/src/processing/m3u_parser.rs index b2e25e880..d95c4732e 100644 --- a/src/processing/m3u_parser.rs +++ b/src/processing/m3u_parser.rs @@ -58,6 +58,7 @@ fn create_empty_playlistitem_header(input_id: u16, url: &str) -> PlaylistItemHea id: default_as_empty_rc_str(), stream_id: default_as_empty_rc_str(), name: default_as_empty_rc_str(), + chno: default_as_empty_rc_str(), logo: default_as_empty_rc_str(), logo_small: default_as_empty_rc_str(), group: default_as_empty_rc_str(), @@ -108,6 +109,7 @@ fn process_header(input: &ConfigInput, video_suffixes: &Vec<&str>, content: &str (id, "tvg-id"), (group, "group-title"), (name, "tvg-name"), + (chno, "tvg-chno"), (parent_code, "parent-code"), (audio_track, "audio-track"), (logo, "tvg-logo"), diff --git a/src/processing/xtream_parser.rs b/src/processing/xtream_parser.rs index 27c569c31..e14f11b82 100644 --- a/src/processing/xtream_parser.rs +++ b/src/processing/xtream_parser.rs @@ -42,6 +42,7 @@ pub(crate) fn parse_xtream_series_info(info: &Value, group_title: &str, input: & id: Rc::new(episode.id.clone()), stream_id: Rc::new(episode.id.clone()), name: Rc::new(episode.title.clone()), + chno: default_as_empty_rc_str(), logo: Rc::new(episode.info.movie_image.clone()), logo_small: default_as_empty_rc_str(), group: Rc::new(group_title.to_string()), @@ -101,6 +102,7 @@ pub(crate) fn parse_xtream(input: &ConfigInput, id: Rc::new(stream.get_stream_id()), stream_id: Rc::new(stream.get_stream_id()), name: Rc::clone(&stream.name), + chno: default_as_empty_rc_str(), logo: Rc::clone(&stream.stream_icon), logo_small: default_as_empty_rc_str(), group: Rc::clone(title),