diff --git a/source/ChanSort.Api/Controller/Editor.cs b/source/ChanSort.Api/Controller/Editor.cs index 47f6ad4..399561a 100644 --- a/source/ChanSort.Api/Controller/Editor.cs +++ b/source/ChanSort.Api/Controller/Editor.cs @@ -415,8 +415,8 @@ namespace ChanSort.Api /// private void ApplyPrNrToFavLists(ChannelInfo tvChannel) { - var supMask = (int)this.DataRoot.SupportedFavorites; - var refMask = (int)tvChannel.Favorites; + var supMask = (ulong)this.DataRoot.SupportedFavorites; + var refMask = (ulong)tvChannel.Favorites; for (int i = 0; supMask != 0; i++) { tvChannel.FavIndex[i] = (refMask & 0x01) == 0 ? -1 : tvChannel.NewProgramNr; diff --git a/source/ChanSort.Api/Model/DataRoot.cs b/source/ChanSort.Api/Model/DataRoot.cs index bab56d7..bf1a027 100644 --- a/source/ChanSort.Api/Model/DataRoot.cs +++ b/source/ChanSort.Api/Model/DataRoot.cs @@ -267,6 +267,8 @@ namespace ChanSort.Api public string GetFavListCaption(int favIndex, bool asTabCaption = false) { + if (favIndex < 0) + return ""; var hasCaption = favListCaptions.TryGetValue(favIndex, out var caption); if (!asTabCaption) return caption; diff --git a/source/ChanSort.Loader.Enigma2/Enigma2Plugin.cs b/source/ChanSort.Loader.Enigma2/Enigma2Plugin.cs index 3472fa0..6a87622 100644 --- a/source/ChanSort.Loader.Enigma2/Enigma2Plugin.cs +++ b/source/ChanSort.Loader.Enigma2/Enigma2Plugin.cs @@ -6,7 +6,7 @@ namespace ChanSort.Loader.Enigma2 { public string DllName { get; set; } public string PluginName => "Enigma2 (Linux Receiver)"; - public string FileFilter => "bouquets.*"; + public string FileFilter => "lamedb"; public SerializerBase CreateSerializer(string inputFile) { diff --git a/source/ChanSort.Loader.Enigma2/Serializer.cs b/source/ChanSort.Loader.Enigma2/Serializer.cs index 2d1d504..c6abbc0 100644 --- a/source/ChanSort.Loader.Enigma2/Serializer.cs +++ b/source/ChanSort.Loader.Enigma2/Serializer.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using ChanSort.Api; namespace ChanSort.Loader.Enigma2 @@ -17,8 +18,7 @@ namespace ChanSort.Loader.Enigma2 { private static readonly Encoding utf8WithoutBom = new UTF8Encoding(false); - private ChannelList tv = new ChannelList(SignalSource.Digital | SignalSource.Tv, "TV"); - private ChannelList radio = new ChannelList(SignalSource.Digital | SignalSource.Radio, "Radio"); + private ChannelList channels = new ChannelList(SignalSource.Digital, "All Channels"); private readonly List favListFileNames = new(); private readonly Dictionary transponderByLamedbId = new(); @@ -43,10 +43,8 @@ namespace ChanSort.Loader.Enigma2 this.Features.SupportedFavorites = 0; // dynamically added this.Features.CanSaveAs = false; - this.tv.IsMixedSourceFavoritesList = true; - this.DataRoot.AddChannelList(this.tv); - this.radio.IsMixedSourceFavoritesList = true; - this.DataRoot.AddChannelList(this.radio); + this.channels.IsMixedSourceFavoritesList = true; + this.DataRoot.AddChannelList(this.channels); // hide columns for fields that don't exist in Silva-Schneider channel list foreach (var list in this.DataRoot.ChannelLists) @@ -73,11 +71,28 @@ namespace ChanSort.Loader.Enigma2 this.decoder = new DvbStringDecoder(this.DefaultEncoding); this.LoadLamedb(); - int favId = 0; - foreach(var file in Directory.GetFiles(Path.GetDirectoryName(this.FileName), "userbouquet.*")) - this.LoadBouquet(file, ref favId); - } + int favIndex = 0; + // load all userbouquet files listed in bouquets.tv + var path = Path.Combine(Path.GetDirectoryName(this.FileName), "bouquets.tv"); + if (File.Exists(path)) + LoadBouquets(path, ref favIndex); + + // load all userbouquet files listed in bouquets.radio + path = Path.Combine(Path.GetDirectoryName(this.FileName), "bouquets.radio"); + if (File.Exists(path)) + LoadBouquets(path, ref favIndex); + + // load all unlisted userbouquet files + foreach (var file in Directory.GetFiles(Path.GetDirectoryName(this.FileName), "userbouquet.*")) + { + var ext = Path.GetExtension(file); + if (ext != ".tv" && ext != ".radio") // ignore .del, .bak and other irrelevant files + continue; + if (!this.favListFileNames.Contains(file)) + this.LoadUserBouquet(file, ref favIndex); + } + } #endregion #region LoadLamedb() @@ -213,20 +228,33 @@ namespace ChanSort.Loader.Enigma2 } } - this.DataRoot.AddChannel(this.tv, ch); + //var list = (ch.SignalSource & SignalSource.Radio) != 0 ? this.radio : this.tv; + var list = this.channels; + this.DataRoot.AddChannel(list, ch); this.channelsByBouquetId[$"{ch.DvbNamespace}:{ch.OriginalNetworkId}:{ch.TransportStreamId}:{ch.ServiceId}"] = ch; } #endregion - #region LoadBoquet - private void LoadBouquet(string file, ref int favIndex) + #region LoadBouquets + private void LoadBouquets(string file, ref int favIndex) { - ChannelList list; - if (file.EndsWith(".tv")) - list = this.tv; - else if (file.EndsWith(".radio")) - list = this.radio; - else + var regex = new Regex(".*FROM BOUQUET \"(.*?)\".*"); + foreach (var line in File.ReadAllLines(file)) + { + var match = regex.Match(line); + if (match.Success) + { + var path = Path.Combine(Path.GetDirectoryName(file), match.Groups[1].Value); + LoadUserBouquet(path, ref favIndex); + } + } + } + #endregion + + #region LoadUserBoquet + private void LoadUserBouquet(string file, ref int favIndex) + { + if (!File.Exists(file)) return; using var r = new StreamReader(File.OpenRead(file), utf8WithoutBom); @@ -238,7 +266,6 @@ namespace ChanSort.Loader.Enigma2 } this.DataRoot.SetFavListCaption(favIndex, line.Substring(6)); - this.Features.SupportedFavorites = (Favorites)((int)this.Features.SupportedFavorites<<1)|Favorites.A; int lineNr = 0; int progNr = 0; @@ -289,7 +316,9 @@ namespace ChanSort.Loader.Enigma2 ch.SetOldPosition(1+favIndex, ++progNr); } + this.favListFileNames.Add(file); + this.Features.SupportedFavorites = (Favorites)((ulong)this.Features.SupportedFavorites << 1) | Favorites.A; ++favIndex; } #endregion @@ -338,7 +367,7 @@ namespace ChanSort.Loader.Enigma2 var file = this.favListFileNames[favIndex]; using var w = new StreamWriter(File.OpenWrite(file), utf8WithoutBom); w.WriteLine($"#NAME {this.DataRoot.GetFavListCaption(favIndex)}"); - foreach (var ch in this.tv.Channels.OrderBy(c => c.GetPosition(favIndex+1))) + foreach (var ch in this.channels.Channels.OrderBy(c => c.GetPosition(favIndex+1))) { if (!(ch is Channel c) || c.GetPosition(favIndex + 1) < 0) continue; diff --git a/source/ChanSort/MainForm.cs b/source/ChanSort/MainForm.cs index 2144fbc..be0f5d3 100644 --- a/source/ChanSort/MainForm.cs +++ b/source/ChanSort/MainForm.cs @@ -443,10 +443,12 @@ namespace ChanSort.Ui this.repositoryItemCheckedComboBoxEdit2.Items.Clear(); var regex = "["; var favCount = 0; - for (var favMask = (uint)favorites; (favMask & 1) != 0; favMask >>= 1) + for (var favMask = (ulong)favorites; (favMask & 1) != 0; favMask >>= 1) { var c = (char) ('A' + favCount); ++favCount; + if (favCount >= 26) + continue; this.repositoryItemCheckedComboBoxEdit1.Items.Add(c); this.repositoryItemCheckedComboBoxEdit2.Items.Add(c); @@ -467,13 +469,15 @@ namespace ChanSort.Ui regex += "]*"; this.repositoryItemCheckedComboBoxEdit1.Mask.EditMask = regex; this.repositoryItemCheckedComboBoxEdit2.Mask.EditMask = regex; - + + this.tabSubList.BeginUpdate(); while (this.tabSubList.TabPages.Count > favCount + 1) this.tabSubList.TabPages.RemoveAt(this.tabSubList.TabPages.Count - 1); while (this.tabSubList.TabPages.Count < favCount + 1) this.tabSubList.TabPages.Add(); for (int i = 1; i < this.tabSubList.TabPages.Count; i++) this.tabSubList.TabPages[i].Text = this.DataRoot.GetFavListCaption(i - 1, true); + this.tabSubList.EndUpdate(); if (!this.DataRoot.SortedFavorites || this.subListIndex >= favCount) { @@ -1926,8 +1930,14 @@ namespace ChanSort.Ui var sb = new StringBuilder(); int i = 0; - for (var mask = (int)fav; mask != 0; mask >>= 1) + for (var mask = (ulong)fav; mask != 0; mask >>= 1) { + if (i >= 26) + { + sb.Append("+"); + break; + } + if ((mask & 1) != 0) sb.Append((char)('A' + i)); ++i; diff --git a/source/ChanSort/Properties/licenses.licx b/source/ChanSort/Properties/licenses.licx index 0f52b01..d1aab4d 100644 --- a/source/ChanSort/Properties/licenses.licx +++ b/source/ChanSort/Properties/licenses.licx @@ -1,5 +1,6 @@ -DevExpress.XtraEditors.PictureEdit, DevExpress.XtraEditors.v20.1, Version=20.1.8.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a +DevExpress.XtraEditors.ComboBoxEdit, DevExpress.XtraEditors.v20.1, Version=20.1.8.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a +DevExpress.XtraBars.BarManager, DevExpress.XtraBars.v20.1, Version=20.1.8.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a DevExpress.XtraEditors.CheckEdit, DevExpress.XtraEditors.v20.1, Version=20.1.8.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a DevExpress.XtraEditors.Repository.RepositoryItemTextEdit, DevExpress.XtraEditors.v20.1, Version=20.1.8.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a +DevExpress.XtraEditors.PictureEdit, DevExpress.XtraEditors.v20.1, Version=20.1.8.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a DevExpress.XtraEditors.ButtonEdit, DevExpress.XtraEditors.v20.1, Version=20.1.8.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a -DevExpress.XtraBars.BarManager, DevExpress.XtraBars.v20.1, Version=20.1.8.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a diff --git a/source/ChanSort/ReferenceListForm.cs b/source/ChanSort/ReferenceListForm.cs index 7158ec2..62dd364 100644 --- a/source/ChanSort/ReferenceListForm.cs +++ b/source/ChanSort/ReferenceListForm.cs @@ -18,16 +18,21 @@ namespace ChanSort.Ui private SerializerBase serializer; private readonly string[] closeButtonText; - class MixedSourceList + class ListOption { + private DataRoot root; public ChannelList ChannelList { get; } - public int FavIndex { get; } + public int PosIndex { get; } - public MixedSourceList(ChannelList list, int favIndex) + public string Caption { get; } + public ListOption(ChannelList list, int posIndex, string caption) { ChannelList = list; - FavIndex = favIndex; + PosIndex = posIndex; + Caption = caption; } + + public override string ToString() => Caption; } public ReferenceListForm(MainForm main) @@ -129,21 +134,29 @@ namespace ChanSort.Ui continue; if (list.IsMixedSourceFavoritesList) { - this.comboSource.Properties.Items.Add(new MixedSourceList(list, i)) + for (int i = 0; i <= list.FavListCount; i++) + this.comboSource.Properties.Items.Add(new ListOption(list, i, list + (i==0 ? "" : " - " + serializer.DataRoot.GetFavListCaption(i-1)))); } else - this.comboSource.Properties.Items.Add(list); + this.comboSource.Properties.Items.Add(new ListOption(list, 0, list.Caption)); } this.comboTarget.EditValue = null; this.comboTarget.Properties.Items.Clear(); foreach (var list in main.DataRoot.ChannelLists) { - if (!list.IsMixedSourceFavoritesList && list.Channels.Count > 0) + if (list.Channels.Count == 0) + continue; + if (list.IsMixedSourceFavoritesList) { - this.comboTarget.Properties.Items.Add(list); + for (int i = 0; i <= list.FavListCount; i++) + this.comboTarget.Properties.Items.Add(new ListOption(list, i, list + (i==0 ? "" : " - " + main.DataRoot.GetFavListCaption(i-1)))); + } + else + { + this.comboTarget.Properties.Items.Add(new ListOption(list, 0, list.Caption)); if (main.CurrentChannelList == list) - this.comboTarget.SelectedItem = list; + this.comboTarget.SelectedIndex = this.comboTarget.Properties.Items.Count-1; } } @@ -178,9 +191,9 @@ namespace ChanSort.Ui checkEdit.Checked = checkEdit.Enabled = true; } - var list = (ChannelList) this.comboSource.EditValue; + var list = (ListOption) this.comboSource.EditValue; this.lblSourceInfo.Text = GetInfoText(list, true); - list = (ChannelList) this.comboTarget.EditValue; + list = (ListOption) this.comboTarget.EditValue; this.lblTargetInfo.Text = GetInfoText(list, false); var canApply = (cbAnalog.Checked || cbDigital.Checked) && (cbTv.Checked || cbRadio.Checked || cbData.Checked); @@ -191,8 +204,9 @@ namespace ChanSort.Ui #region GetInfoText() - private string GetInfoText(ChannelList list, bool source) + private string GetInfoText(ListOption option, bool source) { + var list = option?.ChannelList; var src = list?.SignalSource ?? 0; var sb = new StringBuilder(); @@ -277,14 +291,14 @@ namespace ChanSort.Ui UpdateInfoTextAndOptions(); // auto-select a compatible source list - var list = (ChannelList)this.comboTarget.EditValue; + var list = ((ListOption)this.comboTarget.EditValue)?.ChannelList; if (list != null) { this.comboSource.SelectedIndex = -1; var src = list.SignalSource; - foreach (ChannelList sourceList in this.comboSource.Properties.Items) + foreach (ListOption sourceList in this.comboSource.Properties.Items) { - if ((sourceList.SignalSource & src) == src) + if ((sourceList.ChannelList.SignalSource & src) == src) { this.comboSource.SelectedItem = sourceList; break; @@ -299,7 +313,7 @@ namespace ChanSort.Ui private void comboSource_EditValueChanged(object sender, EventArgs e) { UpdateInfoTextAndOptions(); - var list = (ChannelList)this.comboSource.EditValue; + var list = ((ListOption)this.comboSource.EditValue)?.ChannelList; this.comboPrNr.Text = list == null || list.Count == 0 ? "1" : list.Channels.Min(ch => Math.Max(ch.OldProgramNr, 1)).ToString(); } @@ -309,14 +323,14 @@ namespace ChanSort.Ui private void btnApply_Click(object sender, EventArgs e) { - var src = (ChannelList) this.comboSource.EditValue; - var target = (ChannelList) this.comboTarget.EditValue; + var src = (ListOption) this.comboSource.EditValue; + var target = (ListOption) this.comboTarget.EditValue; int offset; if (int.TryParse(this.comboPrNr.Text, out offset)) - offset -= src.Channels.Min(ch => Math.Max(ch.OldProgramNr, 1)); + offset -= src.ChannelList.Channels.Min(ch => Math.Max(ch.OldProgramNr, 1)); bool overwrite = true; - if (target.GetChannelsByNewOrder().Any(ch => ch.NewProgramNr != -1)) + if (target.ChannelList.GetChannelsByNewOrder().Any(ch => ch.NewProgramNr != -1)) { using (var dlg = new ActionBoxDialog(Resources.ReferenceListForm_btnApply_ConflictHandling)) { @@ -327,7 +341,7 @@ namespace ChanSort.Ui switch (dlg.ShowDialog(this)) { case DialogResult.OK: - target.Channels.ForEach(ch => ch.NewProgramNr = -1); + target.ChannelList.Channels.ForEach(ch => ch.NewProgramNr = -1); break; case DialogResult.Yes: //overwrite = true; @@ -341,7 +355,7 @@ namespace ChanSort.Ui } } - main.Editor.ApplyReferenceList(this.serializer.DataRoot, src, target, false, offset, FilterChannel, overwrite, this.cbConsecutive.Checked); + main.Editor.ApplyReferenceList(this.serializer.DataRoot, src.ChannelList, target.ChannelList, false, offset, FilterChannel, overwrite, this.cbConsecutive.Checked); main.RefreshGrids(); }