- unified handling for deleting channels (action based on file format support either mark-as-deleted, remove-from-file, append-at-end)

- added unit tests
- internal code cleanup
This commit is contained in:
hbeham
2019-11-08 02:31:44 +01:00
parent caca802c0a
commit 34b281f5fc
105 changed files with 2024 additions and 1442 deletions

View File

@@ -10,21 +10,31 @@ namespace ChanSort.Api
private string uid;
private string serviceTypeName;
/// <summary>
/// List of channels that have the same UID as this channel and were not added to the channel list directly
/// </summary>
public readonly List<ChannelInfo> Duplicates = new List<ChannelInfo>();
public virtual bool IsDeleted { get; set; }
public SignalSource SignalSource { get; set; }
public string Source { get; set; }
/// <summary>
/// Index or ID of the data record
/// </summary>
public long RecordIndex { get; set; }
/// <summary>
/// logical record order (might be different from the index, like old LG TLL files with a linked list of record indices)
/// </summary>
public int RecordOrder { get; set; }
/// <summary>
/// original program number from the file, except for channels with IsDeleted==true, which will have the value -1
/// </summary>
public int OldProgramNr { get; set; }
/// <summary>
/// new program number or -1, if the channel isn't assigned a number or has IsDeleted==true
/// </summary>
public int NewProgramNr { get; set; }
public string Name { get; set; }
public string ShortName { get; set; }
public Favorites Favorites { get; set; }
public bool Skip { get; set; }
public bool Lock { get; set; }
public bool Hidden { get; set; }
@@ -39,23 +49,50 @@ namespace ChanSort.Api
public int AudioPid { get; set; }
public int OriginalNetworkId { get; set; }
public int TransportStreamId { get; set; }
public int Bouquet { get; set; }
public string Provider { get; set; }
public int SymbolRate { get; set; }
public int ServiceType { get; set; }
public string Debug { get; private set; }
public string SatPosition { get; set; }
public Transponder Transponder { get; set; }
/// <summary>
/// Bitmask in which favorite lists the channel is included
/// </summary>
public Favorites Favorites { get; set; }
/// <summary>
/// current number of the channel in the various favorite lists (if individual sorting is supported)
/// </summary>
public List<int> FavIndex { get; }
/// <summary>
/// original number of the channel in the various favorite lists (if individual sorting is supported)
/// </summary>
public List<int> OldFavIndex { get; }
/// <summary>
/// predefined LCN (logical channel number) assigned by TV firmware or cable/sat operator
/// </summary>
public int ProgramNrPreset { get; set; }
public bool IsNameModified { get; set; }
/// <summary>
/// A proxy channel is inserted into the current channel list when there was no match for a reference list channel
/// </summary>
public bool IsProxy => this.RecordIndex < 0;
/// <summary>
/// arbitrary information that can be shown in a UI column to assist in analyzing a file format while coding a plugin
/// </summary>
public string Debug { get; private set; }
#region ctor()
protected ChannelInfo()
{
this.OldProgramNr = -1;
this.NewProgramNr = -1;
this.FavIndex = new List<int>(MAX_FAV_LISTS);
this.OldFavIndex = new List<int>(MAX_FAV_LISTS);
@@ -76,7 +113,6 @@ namespace ChanSort.Api
this.SignalSource = source;
this.RecordIndex = index;
this.RecordOrder = (int)index;
this.NewProgramNr = -1;
this.OldProgramNr = oldProgNr;
this.Name = name;
this.Encrypted = null;
@@ -99,6 +135,11 @@ namespace ChanSort.Api
#endregion
#region Uid
/// <summary>
/// The Uid is the preferred way of matching channels between the current channel list and a reference list.
/// The basic format of this string was taken from a command line tool "TllSort" for LG TVs but then expanded beyond that
/// in order to support the various file formats and the data provided in those.
/// </summary>
public string Uid
{
get
@@ -113,7 +154,7 @@ namespace ChanSort.Api
this.uid = "S" + /*this.SatPosition + */ "-" + this.OriginalNetworkId + "-" + this.TransportStreamId + "-" + this.ServiceId;
else if ((this.SignalSource & SignalSource.MaskAntennaCableSat) == SignalSource.Antenna || (this.SignalSource & SignalSource.MaskAntennaCableSat) == SignalSource.Cable)
{
// ChannelOrTransponder is needed for DVB-T where the same ONID+TSID+SID can be received from 2 different towers (on different frequencies)
// ChannelOrTransponder is needed for DVB-T where the same ONID+TSID+SID can be received from 2 different radio transmitters, but on different frequencies/channels
this.uid = "C-" + this.OriginalNetworkId + "-" + this.TransportStreamId + "-" + this.ServiceId + "-" + this.ChannelOrTransponder;
}
else
@@ -177,23 +218,6 @@ namespace ChanSort.Api
}
#endregion
#region GetFavString()
public static string GetFavString(Favorites favorites)
{
string sep = "";
string text = "";
foreach (Favorites favMask in Enum.GetValues(typeof(Favorites)))
{
if ((favorites & favMask) != 0)
{
text += sep + favMask.ToString();
sep = ",";
}
}
return text;
}
#endregion
#region ParseFavString()
public static Favorites ParseFavString(string value)
{
@@ -246,12 +270,19 @@ namespace ChanSort.Api
#endregion
#region UpdateRawData()
/// <summary>
/// called during the saving procedure to update the external channel list data with the changes made by the user
/// </summary>
public virtual void UpdateRawData()
{
}
#endregion
#region ChangeEncoding()
/// <summary>
/// for file formats that allow characters in local code pages, this method should re-parse the raw data bytes for the given encoding
/// </summary>
/// <param name="encoding"></param>
public virtual void ChangeEncoding(System.Text.Encoding encoding)
{
}
@@ -259,16 +290,25 @@ namespace ChanSort.Api
#region GetPosition(), SetPosition(), ChangePosition()
/// <summary>
/// Gets the new channel number in the main channel list (index=0) or the various favorite lists (1-x)
/// </summary>
public int GetPosition(int subListIndex)
{
return subListIndex == 0 ? this.NewProgramNr : this.FavIndex[subListIndex - 1];
}
/// <summary>
/// Gets the original channel number in the main channel list (index=0) or the various favorite lists (1-x)
/// </summary>
public int GetOldPosition(int subListIndex)
{
return subListIndex == 0 ? this.OldProgramNr : this.OldFavIndex[subListIndex - 1];
}
/// <summary>
/// Sets the new channel number in the main channel list (index=0) or the various favorite lists (1-x)
/// </summary>
public void SetPosition(int subListIndex, int newPos)
{
if (subListIndex == 0)
@@ -284,6 +324,9 @@ namespace ChanSort.Api
}
}
/// <summary>
/// Sets the original channel number in the main channel list (index=0) or the various favorite lists (1-x)
/// </summary>
public void SetOldPosition(int subListIndex, int oldPos)
{
if (subListIndex == 0)
@@ -292,6 +335,9 @@ namespace ChanSort.Api
this.OldFavIndex[subListIndex - 1] = oldPos;
}
/// <summary>
/// Internal helper method to adjust the main or favorite program number by a delta value
/// </summary>
internal void ChangePosition(int subListIndex, int delta)
{
if (subListIndex == 0)

View File

@@ -1,41 +1,37 @@
using System.Collections.Generic;
using System.Text;
using System.Linq;
namespace ChanSort.Api
{
public class DataRoot
{
private readonly IDictionary<int, Satellite> satellites = new Dictionary<int, Satellite>();
private readonly IDictionary<int, Transponder> transponder = new Dictionary<int, Transponder>();
private readonly IDictionary<int, LnbConfig> lnbConfig = new Dictionary<int, LnbConfig>();
private readonly IList<ChannelList> channelLists = new List<ChannelList>();
private readonly StringBuilder warnings = new StringBuilder();
private readonly SerializerBase loader;
public StringBuilder Warnings { get { return this.warnings; } }
public IDictionary<int, Satellite> Satellites { get { return this.satellites; } }
public IDictionary<int, Transponder> Transponder { get { return this.transponder; } }
public IDictionary<int, LnbConfig> LnbConfig { get { return this.lnbConfig; } }
public IEnumerable<ChannelList> ChannelLists { get { return this.channelLists; } }
public bool IsEmpty { get { return this.channelLists.Count == 0; } }
public StringBuilder Warnings { get; } = new StringBuilder();
public IDictionary<int, Satellite> Satellites { get; } = new Dictionary<int, Satellite>();
public IDictionary<int, Transponder> Transponder { get; } = new Dictionary<int, Transponder>();
public IDictionary<int, LnbConfig> LnbConfig { get; } = new Dictionary<int, LnbConfig>();
public IEnumerable<ChannelList> ChannelLists => this.channelLists;
public bool IsEmpty => this.channelLists.Count == 0;
public bool NeedsSaving { get; set; }
public Favorites SupportedFavorites { get; set; }
public bool SortedFavorites { get; set; }
public bool MixedSourceFavorites { get; set; }
public bool AllowGapsInFavNumbers { get; set; }
public bool ShowDeletedChannels { get; set; }
public bool DeletedChannelsNeedNumbers { get; set; }
public DataRoot()
public Favorites SupportedFavorites => this.loader.Features.SupportedFavorites;
public bool SortedFavorites => this.loader.Features.SortedFavorites;
public bool MixedSourceFavorites => this.loader.Features.MixedSourceFavorites;
public bool AllowGapsInFavNumbers => this.loader.Features.AllowGapsInFavNumbers;
public bool DeletedChannelsNeedNumbers => this.loader.Features.DeletedChannelsNeedNumbers;
public DataRoot(SerializerBase loader)
{
this.SupportedFavorites = Favorites.A | Favorites.B | Favorites.C | Favorites.D;
this.loader = loader;
}
#region AddSatellite()
public virtual void AddSatellite(Satellite satellite)
{
this.satellites.Add(satellite.Id, satellite);
this.Satellites.Add(satellite.Id, satellite);
}
#endregion
@@ -43,21 +39,21 @@ namespace ChanSort.Api
public virtual void AddTransponder(Satellite sat, Transponder trans)
{
trans.Satellite = sat;
if (this.transponder.ContainsKey(trans.Id))
if (this.Transponder.ContainsKey(trans.Id))
{
this.warnings.AppendFormat("Duplicate transponder data record for satellite #{0} with id {1}\r\n", sat?.Id, trans.Id);
this.Warnings.AppendFormat("Duplicate transponder data record for satellite #{0} with id {1}\r\n", sat?.Id, trans.Id);
return;
}
if (sat != null)
sat.Transponder.Add(trans.Id, trans);
this.transponder.Add(trans.Id, trans);
this.Transponder.Add(trans.Id, trans);
}
#endregion
#region AddLnbConfig()
public void AddLnbConfig(LnbConfig lnb)
{
this.lnbConfig.Add(lnb.Id, lnb);
this.LnbConfig.Add(lnb.Id, lnb);
}
#endregion
@@ -65,7 +61,7 @@ namespace ChanSort.Api
public virtual void AddChannelList(ChannelList list)
{
this.channelLists.Add(list);
this.MixedSourceFavorites |= list.IsMixedSourceFavoritesList;
this.loader.Features.MixedSourceFavorites |= list.IsMixedSourceFavoritesList;
}
#endregion
@@ -74,7 +70,7 @@ namespace ChanSort.Api
{
if (list == null)
{
warnings.AppendFormat("No list found to add channel '{0}'\r\n", channel);
this.Warnings.AppendFormat("No list found to add channel '{0}'\r\n", channel);
return;
}
string warning = list.AddChannel(channel);
@@ -105,6 +101,26 @@ namespace ChanSort.Api
}
#endregion
#region ValidateAfterLoad()
public virtual void ValidateAfterLoad()
{
foreach (var list in this.ChannelLists)
{
if (list.IsMixedSourceFavoritesList)
continue;
// make sure that deleted channels have OldProgramNr = -1
foreach (var chan in list.Channels)
{
if (chan.IsDeleted)
chan.OldProgramNr = -1;
else if (chan.OldProgramNr < 0) // old versions of ChanSort saved -1 and without setting IsDeleted
chan.IsDeleted = true;
}
}
}
#endregion
#region ApplyCurrentProgramNumbers()
public void ApplyCurrentProgramNumbers()
{
@@ -120,35 +136,37 @@ namespace ChanSort.Api
foreach (var channel in list.Channels)
{
for (int i = 0; i <= c; i++)
channel.SetPosition(i, channel.IsDeleted && !this.DeletedChannelsNeedNumbers ? -1 : channel.GetOldPosition(i));
channel.SetPosition(i, channel.GetOldPosition(i));
}
}
}
#endregion
#region SetPrNrForDeletedChannels()
public void SetPrNrForDeletedChannels()
#region ValidateAfterSave()
public virtual void ValidateAfterSave()
{
if (this.DeletedChannelsNeedNumbers)
return;
// make sure that deleted channels have OldProgramNr = -1
// set old numbers to match the new numbers
// also make sure that deleted channels are either removed from the list or assigned the -1 prNr, depending on the loader's DeleteMode
foreach (var list in this.ChannelLists)
{
if (list.IsMixedSourceFavoritesList)
continue;
foreach (var chan in list.Channels)
for (int i = 0; i < list.Channels.Count; i++)
{
var chan = list.Channels[i];
if (chan.IsDeleted)
{
chan.NewProgramNr = -1;
chan.OldProgramNr = -1;
if (this.loader.Features.DeleteMode == SerializerBase.DeleteMode.Physically)
list.Channels.RemoveAt(i--);
else
chan.NewProgramNr = -1;
}
else if (chan.OldProgramNr == -1) // old versions of ChanSort saved -1 and without setting IsDeleted
chan.IsDeleted = true;
chan.OldProgramNr = chan.NewProgramNr;
chan.OldFavIndex.Clear();
chan.OldFavIndex.AddRange(chan.FavIndex);
}
}
}
#endregion
}
}

View File

@@ -82,7 +82,7 @@ namespace ChanSort.Api
{
AppendInOrder=0,
AppendAlphabetically=1,
MarkDeleted=2
Delete=2
}
[Flags]