diff --git a/src/processing/m3u_parser.rs b/src/processing/m3u_parser.rs index 70e8b1334..51e0d6ba3 100644 --- a/src/processing/m3u_parser.rs +++ b/src/processing/m3u_parser.rs @@ -195,33 +195,30 @@ pub(crate) fn consume_m3u(cfg: &Config, input: &ConfigIn } pub(crate) fn parse_m3u(cfg: &Config, input: &ConfigInput, lines: &[String]) -> Vec { - let mut groups: std::collections::HashMap, Vec> = std::collections::HashMap::new(); - let mut sort_order: Vec> = vec![]; - let mut playlist = Vec::new(); - consume_m3u(cfg, input, lines.iter().cloned(), |item| playlist.push(item)); - playlist.drain(..).for_each(|item| { + let mut sort_order: Vec> = vec![]; + let mut idx: usize = 0; + let mut group_map: std::collections::HashMap, usize> = std::collections::HashMap::new(); + consume_m3u(cfg, input, lines.iter().cloned(), |item| { let key = Rc::clone(&item.header.borrow().group); - // let key2 = String::from(&item.header.group); - match groups.entry(Rc::clone(&key)) { - std::collections::hash_map::Entry::Vacant(e) => { - e.insert(vec![item]); - sort_order.push(Rc::clone(&key)); + match group_map.entry(key) { + std::collections::hash_map::Entry::Vacant(v) => { + v.insert(idx); + idx += 1; + sort_order.push(vec![item]); + } + std::collections::hash_map::Entry::Occupied(o) => { + sort_order.get_mut(*o.get()).unwrap().push(item); } - std::collections::hash_map::Entry::Occupied(mut e) => { e.get_mut().push(item); } } }); - let mut result: Vec = vec![]; - for (grp_id, (key, channels)) in (1_u32..).zip(groups.into_iter()) { - let cluster = channels.first().map(|pli| pli.header.borrow().xtream_cluster); - result.push(PlaylistGroup {id: grp_id, xtream_cluster: cluster.unwrap(), title: Rc::clone(&key), channels }); - } - // apply the sort order from the provider - let mut sort_iterator = sort_order.iter(); - result.sort_by(|f, s| { - let i1 = sort_iterator.position(|r| **r == *f.title).unwrap(); - let i2 = sort_iterator.position(|r| **r == *s.title).unwrap(); - i1.cmp(&i2) - }); + let mut grp_id = 0; + let result: Vec = sort_order.drain(..).map(|channels| { + let channel = channels.first(); + let cluster = channel.map(|pli| pli.header.borrow().xtream_cluster).unwrap(); + let group_title = channel.map(|pli| Rc::clone(&pli.header.borrow().group)).unwrap(); + grp_id += 1; + PlaylistGroup { id: grp_id, xtream_cluster: cluster, title: Rc::clone(&group_title), channels } + }).collect(); result } diff --git a/src/processing/playlist_processor.rs b/src/processing/playlist_processor.rs index c180f0c69..d3bd70ee3 100644 --- a/src/processing/playlist_processor.rs +++ b/src/processing/playlist_processor.rs @@ -376,31 +376,23 @@ fn execute_pipe<'a>(target: &ConfigTarget, pipe: &ProcessingPipe, fpl: &mut Fetc // This method is needed, because of duplicate group names in different inputs. // We merge the same group names considering cluster together. fn flatten_groups(mut playlistgroups: Vec) -> Vec { - let mut group_map: HashMap<(Rc, XtreamCluster), PlaylistGroup> = HashMap::new(); - let mut sort_order = vec![]; + let mut sort_order: Vec = vec![]; + let mut idx: usize = 0; + let mut group_map: HashMap<(Rc, XtreamCluster), usize> = HashMap::new(); playlistgroups.drain(..).for_each(|group| { let key = (Rc::clone(&group.title), group.xtream_cluster); match group_map.entry(key) { - std::collections::hash_map::Entry::Occupied(o) => { - // we loose the group id (category id) at this point, which should be available in - // the playlist item header. - o.into_mut().channels.extend(group.channels); - } std::collections::hash_map::Entry::Vacant(v) => { - sort_order.push(Rc::clone(&group.title)); - v.insert(group); - }, + v.insert(idx); + idx += 1; + sort_order.push(group); + } + std::collections::hash_map::Entry::Occupied(o) => { + sort_order.get_mut(*o.get()).unwrap().channels.extend(group.channels); + } }; }); - let mut flat_groups: Vec = group_map.into_values().collect(); - // apply the initial sort order - let mut sort_iterator = sort_order.iter(); - flat_groups.sort_by(|f, s| { - let i1 = sort_iterator.position(|r| **r == *f.title).unwrap(); - let i2 = sort_iterator.position(|r| **r == *s.title).unwrap(); - i1.cmp(&i2) - }); - flat_groups + sort_order } async fn process_playlist<'a>(playlists: &mut [FetchedPlaylist<'a>], @@ -455,9 +447,9 @@ async fn process_playlist<'a>(playlists: &mut [FetchedPlaylist<'a>], info!("Playlist is empty: {}", &target.name); Ok(()) } else { - process_watch(target, cfg, &new_playlist); let mut flat_new_playlist = flatten_groups(new_playlist); sort_playlist(target, &mut flat_new_playlist); + process_watch(target, cfg, &flat_new_playlist); persist_playlist(&mut flat_new_playlist, flatten_tvguide(&new_epg).as_ref(), target, cfg) } } diff --git a/src/repository/bplustree.rs b/src/repository/bplustree.rs index c5c2c853e..03127ae81 100644 --- a/src/repository/bplustree.rs +++ b/src/repository/bplustree.rs @@ -123,13 +123,13 @@ where match bincode::serialize_into(encoder, &self) { Ok(()) => Ok(()), Err(e) => { - println!("Failed to write bplusstree to disk {e}"); + println!("Failed to write bplustree to disk {e}"); Ok(()) } } } - // If file exists the file is deserialized, otherweise an empty tree is returned + // If file exists the file is deserialized, otherwise an empty tree is returned pub(crate) fn deserialize_from_file(filename: &str) -> Self { match File::open(filename) { Ok(file) => { diff --git a/test/rest-api.http b/test/rest-api.http index 2c98c8399..77b067bc9 100644 --- a/test/rest-api.http +++ b/test/rest-api.http @@ -6,17 +6,19 @@ Content-Type: application/json "url": "iptv.m3u" } -### - ### config request GET http://localhost:8901/api/v1/config Content-Type: application/json -### - ### auth POST http://localhost:8901/auth/token Content-Type: application/json {"username": "test", "password": "test"} +### xtream live_categories +GET http://localhost:8901/m3u?username=me&password=me + +### xtream live_categories +GET http://localhost:8901/player_api.php?action=get_live_categories&username=xt&password=xt +