Blaupunkt, Dyon, Hisense, Sharp and many others using DVBS_Program.csv, DVBS_CHANNEL_TABLE.csv, MSxxxx_DVBS_CHANNEL_TABLE.csv:

- added support for Hisense .csv files with 6 columns including "channel number" and "LCN"
- using separate lists for DTV, Radio and Data, which all start with 1
This commit is contained in:
Horst Beham
2021-07-23 21:44:02 +02:00
parent e5d7e5a325
commit 22f9d43adc
5 changed files with 834 additions and 134 deletions

View File

@@ -14,21 +14,21 @@ namespace ChanSort.Loader.Sharp
* ===================================
* These formats lack a way to uniquely identify a channel via ONID-TSID-SID and as described in their file header,
* can only be physically reordered to change the zapping order, but don't allow changing the channel number:
* - Hisense LTDN40D50TS: LCN,Channel Name,Service Type,[B]
* - Hisense LTDN40D50TS (LCN is a bogus column name and is actually the physical channel record index in the .dat file): LCN,Channel Name,Service Type,[B]
* - Hisense 40A5100F (DTV,Radio,Data all start at LCN 1): Channel Number,LCN,Channel Name,Service Type,Free or Scramble,Transponder,[S]
* - Sharp LC-43CFE4142E firmware V1.16: Channel Number,Channel Name,Service Type,Free or Scramble,Transponder,[S]
* - Dyon Live 24 Pro, Dyon ENTER 32 Pro X: Channel Number,Channel Name,Service Type,Free or Scramble,Transponder,[S]
* - Blaupunkt B32B133T2CSHD: Channel Number,Channel Name,Service Type,Free or Scramble,Transponder,[S]
*
* MS6486_DVBS_CHANNEL_TABLE + MS6488_HOTELMODE_TABLE.json
* =======================================
* Channel Number,Channel Name,Service Type,Free or Scramble,Frequency(MHz),Polarity,SymbolRate(KS/s),[S]
* - Channel Number,Channel Name,Service Type,Free or Scramble,Frequency(MHz),Polarity,SymbolRate(KS/s),[S]
*
*
* DVBS_CHANNEL_TABLE.csv + dtv_cmdb_*.bin
* =======================================
* This format supposedly supports deleting and changing the program numbers along with reordering (despite the NOTE in the header).
* - Channel number,Channel Name,program count,program index,RF channel number,QAM mode,Band width,PlpID,Frequency,symbol rate,Polarity,SatName,SatID,SatTableID,LowLOF,HighLOF,LNBType,LNBTypeReal,DISEQC level,ToneburstType,Swt10Port,Swt11Port,22KOnOff,LNB power,12VOnOff,Motor position bit8 1:USALS 0:DISEQC1.2,Satellite Angle,Transponder number,Begin transponder,channel Id,unicable frequency,unicable MDU,unicable password pin,LNB index 0:A 1:B,TS id,orig network id,network id,PCR pid,LCN,Free or Scramble,PmtPID,ServiceID,Video_pid,Audio_pid,VideoType,AudioType,NitVer,PatVer,PmtVer,SdtVer,Service Type,[S]
* The file from user "blackbox" was encoded in UTF-8 without BOM
*
*/
internal class SharpSerializer : SerializerBase
@@ -37,11 +37,15 @@ namespace ChanSort.Loader.Sharp
{
Hisense3Columns = 1,
Sharp5Columns = 2,
Sharp7Columns = 3,
Sharp51Columns = 4
Hisense6Columns = 3,
Sharp7Columns = 4,
Sharp51Columns = 5
}
private readonly ChannelList dvbsChannels = new ChannelList(0, "DVB-S");
private readonly ChannelList dtvChannels = new ChannelList(SignalSource.DvbS| SignalSource.Tv, "DTV");
private readonly ChannelList radioChannels = new ChannelList(SignalSource.DvbS|SignalSource.Radio, "Radio");
private readonly ChannelList dataChannels = new ChannelList(SignalSource.DvbS|SignalSource.Data, "Data");
private Encoding encoding;
private FormatVersion formatVersion;
private string[] cols;
@@ -60,7 +64,9 @@ namespace ChanSort.Loader.Sharp
this.Features.CanHaveGaps = false;
this.Features.FavoritesMode = FavoritesMode.None;
this.DataRoot.AddChannelList(this.dvbsChannels);
this.DataRoot.AddChannelList(this.dtvChannels);
this.DataRoot.AddChannelList(this.radioChannels);
this.DataRoot.AddChannelList(this.dataChannels);
}
#endregion
@@ -88,99 +94,129 @@ namespace ChanSort.Loader.Sharp
if (data[0] == "[E]")
break;
var ch = new ChannelInfo(SignalSource.DvbS, i, i-2, "");
for (int j = 0; j < data.Length; j++)
{
var val = data[j];
int.TryParse(val, out var intval);
switch (cols[j])
{
case "lcn":
case "channel number":
ch.RecordOrder = intval;
break;
case "channel name":
ch.Name = val;
break;
case "service type":
if (intval != 0)
{
if (intval == 1)
{
ch.SignalSource |= SignalSource.Tv;
ch.ServiceTypeName = "DTV";
}
else if (intval == 2)
{
ch.SignalSource |= SignalSource.Radio;
ch.ServiceTypeName = "Radio";
}
else if (intval == 3)
{
ch.SignalSource |= SignalSource.Data;
ch.ServiceTypeName = "Data";
}
}
else
ch.ServiceTypeName = val;
break;
case "free or scramble":
ch.Encrypted = val == "Scramble" || val == "1";
break;
case "transponder":
{
var parts = val.Split(' ');
if (int.TryParse(parts[0], out var mhz))
ch.FreqInMhz = mhz;
if (parts.Length > 1 && parts[1].Length > 0)
ch.Polarity = parts[1][0];
if (parts.Length > 2 && int.TryParse(parts[2], out var sr))
ch.SymbolRate = sr;
break;
}
case "frequency(mhz)":
case "frequency":
ch.FreqInMhz = intval;
break;
case "polarity":
if (val.Length > 0)
ch.Polarity = val[0] == '0' || val[0] == 'H' ? 'H' : 'V';
break;
case "symbolrate(ks/s)":
case "symbol rate":
ch.SymbolRate = intval;
break;
case "satname":
ch.Satellite = val;
break;
case "satellite angle":
ch.SatPosition = val;
break;
case "orig network id":
ch.OriginalNetworkId = intval;
break;
case "ts id":
ch.TransportStreamId = intval;
break;
case "serviceid":
ch.ServiceId = intval;
break;
case "pcr pid":
ch.PcrPid = intval;
break;
case "video_pid":
ch.VideoPid = intval;
break;
case "audio_pid":
ch.AudioPid = intval;
break;
}
}
this.DataRoot.AddChannel(this.dvbsChannels, ch);
ReadChannel(i, data);
}
}
#endregion
#region ReadChannel()
private void ReadChannel(int i, string[] data)
{
var ch = new ChannelInfo(SignalSource.DvbS, i, 0, "");
for (int j = 0; j < data.Length; j++)
{
var val = data[j];
int.TryParse(val, out var intval);
switch (cols[j])
{
case "lcn":
if (this.formatVersion == FormatVersion.Hisense3Columns) // this format incorrectly labels the physical channel index column as "LCN"
ch.RecordOrder = intval;
else if (this.formatVersion != FormatVersion.Sharp51Columns) // the Sharp51 format has "channel number", "program index" with valid numbers and "LCN" with all values 65535
ch.OldProgramNr = intval;
break;
case "channel number":
ch.RecordOrder = intval;
if (this.formatVersion == FormatVersion.Sharp51Columns)
ch.OldProgramNr = intval;
break;
case "channel name":
ch.Name = val;
break;
case "service type":
// some files use int values, other use strings
if (intval != 0)
{
if (intval == 1)
{
ch.SignalSource |= SignalSource.Tv;
ch.ServiceTypeName = "DTV";
}
else if (intval == 2)
{
ch.SignalSource |= SignalSource.Radio;
ch.ServiceTypeName = "Radio";
}
else if (intval == 3)
{
ch.SignalSource |= SignalSource.Data;
ch.ServiceTypeName = "Data";
}
}
else
{
ch.ServiceTypeName = val;
var lval = val.ToLowerInvariant();
if (lval == "dtv")
ch.SignalSource |= SignalSource.Tv;
else if (lval == "radio")
ch.SignalSource |= SignalSource.Radio;
else
ch.SignalSource |= SignalSource.Data;
}
break;
case "free or scramble":
ch.Encrypted = val == "Scramble" || val == "1";
break;
case "transponder":
{
var parts = val.Split(' ');
if (int.TryParse(parts[0], out var mhz))
ch.FreqInMhz = mhz;
if (parts.Length > 1 && parts[1].Length > 0)
ch.Polarity = parts[1][0];
if (parts.Length > 2 && int.TryParse(parts[2], out var sr))
ch.SymbolRate = sr;
break;
}
case "frequency(mhz)":
case "frequency":
ch.FreqInMhz = intval;
break;
case "polarity":
if (val.Length > 0)
ch.Polarity = val[0] == '0' || val[0] == 'H' ? 'H' : 'V';
break;
case "symbolrate(ks/s)":
case "symbol rate":
ch.SymbolRate = intval;
break;
case "satname":
ch.Satellite = val;
break;
case "satellite angle":
ch.SatPosition = val;
break;
case "orig network id":
ch.OriginalNetworkId = intval;
break;
case "ts id":
ch.TransportStreamId = intval;
break;
case "serviceid":
ch.ServiceId = intval;
break;
case "pcr pid":
ch.PcrPid = intval;
break;
case "video_pid":
ch.VideoPid = intval;
break;
case "audio_pid":
ch.AudioPid = intval;
break;
}
}
var list = this.GetChannelList(ch);
if (ch.OldProgramNr == 0) // if there was no explicit LCN, channels are automatically numbered sequentially by order in their list
ch.OldProgramNr = list.Count + 1;
this.DataRoot.AddChannel(list, ch);
}
#endregion
#region DetectFormatVersion()
private FormatVersion DetectFormatVersion()
{
@@ -192,6 +228,8 @@ namespace ChanSort.Loader.Sharp
{
if (lines[2] == "Channel Number,Channel Name,Service Type,Free or Scramble,Transponder,[S]")
return FormatVersion.Sharp5Columns;
if (lines[2] == "Channel Number,LCN,Channel Name,Service Type,Free or Scramble,Transponder,[S]")
return FormatVersion.Hisense6Columns;
if (lines[2] == "Channel Number,Channel Name,Service Type,Free or Scramble,Frequency(MHz),Polarity,SymbolRate(KS/s),[S]")
return FormatVersion.Sharp7Columns;
@@ -216,35 +254,49 @@ namespace ChanSort.Loader.Sharp
#region AdjustVisibleColumns()
private void AdjustVisibleColumns()
{
var list = this.dvbsChannels;
list.VisibleColumnFieldNames.Clear();
list.VisibleColumnFieldNames.Add("Position");
list.VisibleColumnFieldNames.Add("OldPosition");
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.RecordOrder));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.Name));
if (this.formatVersion >= FormatVersion.Sharp5Columns)
foreach (var list in this.DataRoot.ChannelLists)
{
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.Encrypted));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.FreqInMhz));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.Polarity));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.SymbolRate));
}
list.VisibleColumnFieldNames.Clear();
list.VisibleColumnFieldNames.Add("Position");
list.VisibleColumnFieldNames.Add("OldPosition");
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.RecordOrder));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.Name));
if (this.formatVersion >= FormatVersion.Sharp5Columns)
{
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.Encrypted));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.FreqInMhz));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.Polarity));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.SymbolRate));
}
if (this.formatVersion >= FormatVersion.Sharp51Columns)
{
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.Satellite));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.OriginalNetworkId));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.TransportStreamId));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.ServiceId));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.PcrPid));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.AudioPid));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.VideoPid));
if (this.formatVersion >= FormatVersion.Sharp51Columns)
{
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.Satellite));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.OriginalNetworkId));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.TransportStreamId));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.ServiceId));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.PcrPid));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.AudioPid));
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.VideoPid));
}
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.ServiceTypeName));
}
list.VisibleColumnFieldNames.Add(nameof(ChannelInfo.ServiceTypeName));
}
#endregion
#region GetChannelList()
private ChannelList GetChannelList(ChannelInfo channel)
{
switch (channel.SignalSource & SignalSource.MaskTvRadioData)
{
case SignalSource.Tv: return dtvChannels;
case SignalSource.Radio: return radioChannels;
default: return dataChannels;
}
}
#endregion
#region Save()
@@ -261,26 +313,38 @@ namespace ChanSort.Loader.Sharp
// index of fields in the extended FormatVersion.Sharp51Columns
var ixChannelNumber = Array.IndexOf(this.cols, "channel number");
var ixProgramIndex = ixChannelNumber < 0 ? -1 : Array.IndexOf(this.cols, "program index");
var ixLcn = ixChannelNumber < 0 ? -1 : Array.IndexOf(this.cols, "lcn");
foreach (var channel in this.dvbsChannels.GetChannelsByNewOrder())
foreach (var channelList in new[] {dtvChannels, radioChannels, dataChannels})
{
// when a reference list was applied, the list may contain proxy entries for deleted channels, which must be ignored
if (channel.IsProxy || channel.IsDeleted)
continue;
foreach (var channel in channelList.GetChannelsByNewOrder())
{
// when a reference list was applied, the list may contain proxy entries for deleted channels, which must be ignored
if (channel.IsProxy || channel.IsDeleted)
continue;
var line = this.lines[channel.RecordIndex];
if (ixProgramIndex >= 0)
{
// this extended format would only change the zapping order unless the "Channel Number" and "Channel Index" fields are updated too
var fields = this.lines[channel.RecordIndex].Split(',');
fields[ixChannelNumber] = fields[ixProgramIndex] = channel.NewProgramNr.ToString();
line = string.Join(",", fields);
var line = this.lines[channel.RecordIndex];
if (ixProgramIndex >= 0)
{
// this extended format would only change the zapping order unless the "Channel Number" and "Channel Index" fields are updated too
var fields = this.lines[channel.RecordIndex].Split(',');
fields[ixChannelNumber] = fields[ixProgramIndex] = channel.NewProgramNr.ToString();
line = string.Join(",", fields);
}
else if (ixLcn >= 0)
{
// Hisense 6 column format with Channel Number, LCN, ...
var fields = this.lines[channel.RecordIndex].Split(',');
fields[ixLcn] = channel.NewProgramNr.ToString();
line = string.Join(",", fields);
}
else
{
// the older formats require the "Channel Number" to be unchanged and update it internally during the import based on the order of the lines
}
file.WriteLine(line);
}
else
{
// the older formats require the "Channel Number" to be unchanged and update it internally during the import based on the order of the lines
}
file.WriteLine(line);
}
file.WriteLine("[E]");