- TCL/Thomson: improved file detection (.tar file or directory containing DtvData.db, satellite.db, cloneCRC.bin)

- m3u: #EXTINF tag data is displayed in "Short Name" column and can be edited
- m3u: fixed saving #EXTINF lines containing tag data
- m3u: readded "File / Save as" menu item (but not for other types of lists)
This commit is contained in:
Horst Beham
2023-01-04 13:31:46 +01:00
parent 56d0d0cc2b
commit e27087e6e4
14 changed files with 283 additions and 84 deletions

View File

@@ -30,6 +30,7 @@ namespace ChanSort.Api
public ChannelNameEditMode ChannelNameEdit { get; set; }
public bool CleanUpChannelData { get; set; }
public bool DeviceSettings { get; set; }
public bool CanSaveAs { get; set; }
public bool CanSkipChannels { get; set; } = true;
public bool CanLockChannels { get; set; } = true;
public bool CanHideChannels { get; set; } = true;
@@ -67,12 +68,14 @@ namespace ChanSort.Api
public bool CanEditFavListNames { get; set; }
public bool CanEditAudioPid { get; set; }
public bool AllowShortNameEdit { get; set; }
}
#endregion
private Encoding defaultEncoding;
public string FileName { get; protected set; }
public string SaveAsFileName { get; set; }
public DataRoot DataRoot { get; protected set; }
public SupportedFeatures Features { get; } = new SupportedFeatures();

View File

@@ -7,6 +7,7 @@ namespace ChanSort.Loader.M3u
{
public List<string> Lines { get; }
public int ExtInfTrackNameIndex { get; set; }
public int ExtInfParamIndex { get; set; }
public Channel(int index, int progNr, string name, List<string> lines) : base(SignalSource.IP, index, progNr, name)
{

View File

@@ -30,16 +30,18 @@ namespace ChanSort.Loader.M3u
this.Features.ChannelNameEdit = ChannelNameEditMode.All;
this.Features.DeleteMode = DeleteMode.Physically;
this.Features.FavoritesMode = FavoritesMode.None;
this.Features.CanSaveAs = true;
this.Features.CanLockChannels = false;
this.Features.CanSkipChannels = false;
this.Features.CanHideChannels = false;
this.Features.AllowShortNameEdit = true;
this.DataRoot.AddChannelList(this.allChannels);
base.DefaultEncoding = new UTF8Encoding(false);
this.allChannels.VisibleColumnFieldNames = new List<string>()
{
"+OldPosition", "+Position", "+Name", "+SatPosition", "+Source", "+FreqInMhz", "+Polarity", "+SymbolRate", "+Satellite", "+Provider", "+Debug"
"+OldPosition", "+Position", "+Name", "+SatPosition", "+Source", "+FreqInMhz", "+Polarity", "+SymbolRate", "+Satellite", "+Provider", "+Debug", "+ShortName"
};
}
#endregion
@@ -122,12 +124,14 @@ namespace ChanSort.Loader.M3u
{
int progNr = 0;
string name = "";
string paramStr = null;
int extInfParamIndex = -1;
int extInfTrackNameIndex = -1;
if (extInfLine != null)
{
bool extInfContainsProgNr = false;
ParseExtInf(extInfLine, out name, out extInfTrackNameIndex, out var param);
ParseExtInf(extInfLine, out name, out extInfTrackNameIndex, out paramStr, out extInfParamIndex, out var param);
if (name != "")
{
var match = ExtInfTrackName.Match(name);
@@ -153,6 +157,8 @@ namespace ChanSort.Loader.M3u
chan.Uid = uriLine;
chan.ExtInfTrackNameIndex = extInfTrackNameIndex;
chan.Provider = group;
chan.ShortName = paramStr;
chan.ExtInfParamIndex = extInfParamIndex;
try
{
@@ -212,10 +218,12 @@ namespace ChanSort.Loader.M3u
/// parse track name from lines that may look like:
/// #EXTINF:&lt;length&gt;[ key="value" ...],&lt;TrackName&gt;
/// </summary>
private void ParseExtInf(string extInfLine, out string name, out int nameIndex, out Dictionary<string,string> param)
private void ParseExtInf(string extInfLine, out string name, out int nameIndex, out string paramString, out int paramIndex, out Dictionary<string,string> param)
{
name = "";
nameIndex = -1;
paramString = "";
paramIndex = -1;
param = new Dictionary<string, string>();
bool inQuote = false;
var key = "";
@@ -232,7 +240,10 @@ namespace ChanSort.Loader.M3u
break;
case ExtInfParsePhase.Length:
if (ch == ' ')
{
phase = ExtInfParsePhase.Key;
paramIndex = i;
}
else if (ch == ',')
{
phase = ExtInfParsePhase.Name;
@@ -253,12 +264,12 @@ namespace ChanSort.Loader.M3u
param[key] = value;
key = "";
value = "";
}
else if (ch == ',' && !inQuote)
{
phase = ExtInfParsePhase.Name;
param[key] = value;
phase = ExtInfParsePhase.Name;
nameIndex = i + 1;
}
else
value += ch;
@@ -269,6 +280,9 @@ namespace ChanSort.Loader.M3u
break;
}
}
if (paramIndex >= 0 && nameIndex >= 0)
paramString = extInfLine.Substring(paramIndex + 1, nameIndex - paramIndex - 2);
}
#endregion
@@ -276,6 +290,9 @@ namespace ChanSort.Loader.M3u
#region Save()
public override void Save()
{
if (!string.IsNullOrEmpty(this.SaveAsFileName))
this.FileName = this.SaveAsFileName;
using var file = new StreamWriter(new FileStream(this.FileName, FileMode.Create), this.overrideEncoding ?? this.DefaultEncoding);
file.NewLine = this.newLine;
@@ -285,19 +302,25 @@ namespace ChanSort.Loader.M3u
foreach (ChannelInfo channel in this.allChannels.GetChannelsByNewOrder())
{
// when a reference list was applied, the list may contain proxy entries for deleted channels, which must be ignored
if (channel is Channel chan && !channel.IsDeleted)
if (channel is not Channel chan || channel.IsDeleted)
continue;
foreach (var line in chan.Lines)
{
foreach (var line in chan.Lines)
if (line.StartsWith("#EXTINF:"))
{
if (line.StartsWith("#EXTINF:"))
{
var progNrPrefix = this.allChannelsPrefixedWithProgNr ? chan.NewProgramNr + ". " : "";
var linePrefix = chan.ExtInfTrackNameIndex >= 0 ? line.Substring(0, chan.ExtInfTrackNameIndex) : "";
file.WriteLine($"{linePrefix}{progNrPrefix}{chan.Name}");
}
else
file.WriteLine(line);
var progNrPrefix = this.allChannelsPrefixedWithProgNr ? chan.NewProgramNr + ". " : "";
string linePrefix;
if (chan.ExtInfParamIndex >= 0)
linePrefix = line.Substring(0, chan.ExtInfParamIndex) + " " + chan.ShortName + ",";
else if (chan.ExtInfTrackNameIndex >= 0)
linePrefix = line.Substring(0, chan.ExtInfTrackNameIndex);
else
linePrefix = "#EXTINF:-1,";
file.WriteLine($"{linePrefix}{progNrPrefix}{chan.Name}");
}
else
file.WriteLine(line);
}
}

View File

@@ -10,14 +10,16 @@ using SharpCompress.Writers.Tar;
namespace ChanSort.Loader.TCL
{
/*
* This class loads TCL / Thomson .tar files containing DtvData.db and satellite.db SQLite databases.
* This class loads TCL / Thomson channel lists from a directory or a .tar file containing cloneCRC.bin, DtvData.db and satellite.db.
*
* None of the sample files contained more than a single input source (DVB-C/T/S), so for the time being this loader puts everything into a single list
*/
class DtvDataSerializer : SerializerBase
{
private readonly ChannelList channels = new (SignalSource.All, "All");
private string dbDir;
private string dtvFile;
private string satFile;
private string crcFile;
private readonly HashSet<string> tableNames = new();
@@ -67,58 +69,66 @@ namespace ChanSort.Loader.TCL
#region Load()
public override void Load()
{
PrepareWorkingDirectory();
ValidateCrc();
ReadSattelliteDb();
ReadDtvDataDb();
}
#endregion
#region PrepareWorkingDirectory()
/// <summary>
/// this.FileName might be
/// - a .tar file containing database/cloneCRC.bin, database/userdata/DtvData.db, database/userdata/satellite.db
/// - a .db file in a folder with DtvData.db and satellite.db and a cloneCRC.bin in either the same dir or the parent dir
/// Other situations have already been handled in the <see cref="TclPlugin"/>
/// </summary>
private void PrepareWorkingDirectory()
{
var ext = Path.GetExtension(this.FileName).ToLowerInvariant();
if (ext == ".tar")
{
UntarToTempDir();
this.crcFile = Path.Combine(this.TempPath, "database", "cloneCRC.bin");
this.dbDir = Path.Combine(this.TempPath, "database", "userdata");
}
else if (ext == ".db")
{
this.dbDir = Path.GetDirectoryName(this.FileName);
this.crcFile = Path.Combine(this.dbDir, "cloneCRC.bin");
if (!File.Exists(crcFile))
this.crcFile = Path.Combine(Path.GetDirectoryName(this.dbDir), "cloneCRC.bin");
}
else
throw LoaderException.TryNext("unrecognized TCL/Thomson directory structure");
this.dtvFile = Path.Combine(dbDir, "DtvData.db");
if (!File.Exists(dtvFile))
throw LoaderException.TryNext("Missing DtvData.db file");
this.satFile = Path.Combine(dbDir, "satellite.db");
if (!File.Exists(satFile))
satFile = null;
if (!File.Exists(crcFile))
crcFile = null;
}
#endregion
#region UntarToTempDir()
private void UntarToTempDir()
{
using var tar = TarArchive.Open(this.FileName);
var rdr = tar.ExtractAllEntries();
this.TempPath = Path.Combine(Path.GetTempPath(), "ChanSort_" + DateTime.Now.ToString("yyyyMMdd-HHmmss"));
Directory.CreateDirectory(this.TempPath);
rdr.WriteAllToDirectory(this.TempPath, new ExtractionOptions { ExtractFullPath=true });
this.crcFile = Path.Combine(this.TempPath, "database", "cloneCRC.bin");
var dbDir = Path.Combine(this.TempPath, "database", "userdata");
this.dtvFile = Path.Combine(dbDir, "DtvData.db");
var satFile = Path.Combine(dbDir, "satellite.db");
if (!File.Exists(dtvFile) || !File.Exists(satFile))
throw LoaderException.TryNext("DtvData.db or satellite.db missing");
ValidateCrc(satFile);
string satConnString = $"Data Source={satFile};Pooling=False";
using (var conn = new SqliteConnection(satConnString))
{
conn.Open();
using var cmd = conn.CreateCommand();
this.RepairCorruptedDatabaseImage(cmd);
cmd.CommandText = "SELECT name FROM sqlite_master WHERE type = 'table'";
using (var r = cmd.ExecuteReader())
{
while (r.Read())
this.tableNames.Add(r.GetString(0).ToLowerInvariant());
}
if (!this.tableNames.Contains("sateliteinfotbl") || !this.tableNames.Contains("transponderinfotbl"))
throw LoaderException.TryNext("File doesn't contain the expected tables");
this.ReadSatellites(cmd);
}
string dtvConnString = $"Data Source={dtvFile};Pooling=False";
using (var conn = new SqliteConnection(dtvConnString))
{
conn.Open();
using var cmd = conn.CreateCommand();
this.RepairCorruptedDatabaseImage(cmd);
this.ReadTransponders(cmd);
this.ReadChannels(cmd);
}
rdr.WriteAllToDirectory(this.TempPath, new ExtractionOptions { ExtractFullPath = true });
}
#endregion
#region ValidateCrc()
private void ValidateCrc(string satFile)
private void ValidateCrc()
{
if (!File.Exists(crcFile))
return;
@@ -136,19 +146,62 @@ namespace ChanSort.Loader.TCL
//throw LoaderException.Fail(msg);
}
data = File.ReadAllBytes(satFile);
actual = crc.Calc(data);
expected = BitConverter.ToUInt16(crcData, 4);
if (actual != expected)
if (satFile != null)
{
var msg = $"Invalid CRC16-CCITT check sum for {satFile}. Expected {expected:X4} but calculated {actual:X4}";
protocol.AppendLine(msg);
//throw LoaderException.Fail(msg);
data = File.ReadAllBytes(satFile);
actual = crc.Calc(data);
expected = BitConverter.ToUInt16(crcData, 4);
if (actual != expected)
{
var msg = $"Invalid CRC16-CCITT check sum for {satFile}. Expected {expected:X4} but calculated {actual:X4}";
protocol.AppendLine(msg);
//throw LoaderException.Fail(msg);
}
}
}
#endregion
#region ReadSattelliteDb()
private void ReadSattelliteDb()
{
if (this.satFile == null)
return;
string satConnString = $"Data Source={satFile};Pooling=False";
using var conn = new SqliteConnection(satConnString);
conn.Open();
using var cmd = conn.CreateCommand();
this.RepairCorruptedDatabaseImage(cmd);
cmd.CommandText = "SELECT name FROM sqlite_master WHERE type = 'table'";
using (var r = cmd.ExecuteReader())
{
while (r.Read())
this.tableNames.Add(r.GetString(0).ToLowerInvariant());
}
if (!this.tableNames.Contains("sateliteinfotbl") || !this.tableNames.Contains("transponderinfotbl"))
throw LoaderException.TryNext("File doesn't contain the expected tables");
this.ReadSatellites(cmd);
}
#endregion
#region ReadDtvDataDb()
private void ReadDtvDataDb()
{
string dtvConnString = $"Data Source={dtvFile};Pooling=False";
using var conn = new SqliteConnection(dtvConnString);
conn.Open();
using var cmd = conn.CreateCommand();
this.RepairCorruptedDatabaseImage(cmd);
this.ReadTransponders(cmd);
this.ReadChannels(cmd);
}
#endregion
#region RepairCorruptedDatabaseImage()
private void RepairCorruptedDatabaseImage(SqliteCommand cmd)
{
@@ -285,7 +338,8 @@ left outer join CurCIOPSerType c on c.u8DtvRoute=p.u8DtvRoute
UpdateCrc();
WriteToTar();
if (Path.GetExtension(this.FileName).ToLowerInvariant() == ".tar")
WriteToTar();
}
#endregion
@@ -329,9 +383,14 @@ left outer join CurCIOPSerType c on c.u8DtvRoute=p.u8DtvRoute
#endregion
#region UpdateCrc
/// <summary>
/// update CRC in cloneCRC.bin
/// </summary>
private void UpdateCrc()
{
// update cloneCRC.bin in temp folder
if (this.crcFile == null)
return;
var dtvData = File.ReadAllBytes(dtvFile);
var crc = Crc16.CCITT.Calc(dtvData);
var crcData = File.ReadAllBytes(this.crcFile);

View File

@@ -6,11 +6,33 @@ namespace ChanSort.Loader.TCL
{
public string DllName { get; set; }
public string PluginName => "TCL";
public string FileFilter => "*.tar";
public string FileFilter => "*.tar;*.bin;*.db";
public SerializerBase CreateSerializer(string inputFile)
{
return new DtvDataSerializer(inputFile);
var ext = Path.GetExtension(inputFile).ToLowerInvariant();
if (ext == ".tar")
return new DtvDataSerializer(inputFile);
var name = Path.GetFileName(inputFile).ToLowerInvariant();
var dir = Path.GetDirectoryName(inputFile);
if (name == "dtvdata.db" || name == "satellite.db")
return new DtvDataSerializer(Path.Combine(dir, "DtvData.db"));
if (name == "clonecrc.bin")
{
// cloneCRC.bin normally is in the parent folder of userdata/DtvData.db, but might also be in the same folder
var file1 = Path.Combine(dir, "userdata", "DtvData.db");
var file2 = Path.Combine(dir, "DtvData.db");
foreach (var file in new[] { file1, file2 })
{
if (File.Exists(file))
return new DtvDataSerializer(file);
}
}
throw LoaderException.TryNext("No DtvData.db file found");
}
}
}

View File

@@ -117,6 +117,7 @@
this.miRestoreOriginal = new DevExpress.XtraBars.BarButtonItem();
this.miDeleteBackup = new DevExpress.XtraBars.BarButtonItem();
this.miSave = new DevExpress.XtraBars.BarButtonItem();
this.miSaveAs = new DevExpress.XtraBars.BarButtonItem();
this.miSaveReferenceFile = new DevExpress.XtraBars.BarButtonItem();
this.miConvert = new DevExpress.XtraBars.BarButtonItem();
this.miExcelExport = new DevExpress.XtraBars.BarButtonItem();
@@ -1133,9 +1134,10 @@
this.miDeleteBackup,
this.miMarkForSwapping,
this.miSwapWithMarked,
this.miConvert});
this.miConvert,
this.miSaveAs});
this.barManager1.MainMenu = this.bar1;
this.barManager1.MaxItemId = 121;
this.barManager1.MaxItemId = 122;
this.barManager1.ShowFullMenus = true;
this.barManager1.ShortcutItemClick += new DevExpress.XtraBars.ShortcutItemClickEventHandler(this.barManager1_ShortcutItemClick);
//
@@ -1188,6 +1190,7 @@
new DevExpress.XtraBars.LinkPersistInfo(this.miRestoreOriginal),
new DevExpress.XtraBars.LinkPersistInfo(this.miDeleteBackup),
new DevExpress.XtraBars.LinkPersistInfo(this.miSave, true),
new DevExpress.XtraBars.LinkPersistInfo(this.miSaveAs),
new DevExpress.XtraBars.LinkPersistInfo(this.miSaveReferenceFile),
new DevExpress.XtraBars.LinkPersistInfo(this.miConvert),
new DevExpress.XtraBars.LinkPersistInfo(this.miExcelExport),
@@ -1249,6 +1252,13 @@
this.miSave.Name = "miSave";
this.miSave.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.miSave_ItemClick);
//
// miSaveAs
//
resources.ApplyResources(this.miSaveAs, "miSaveAs");
this.miSaveAs.Id = 121;
this.miSaveAs.Name = "miSaveAs";
this.miSaveAs.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.miSaveAs_ItemClick);
//
// miSaveReferenceFile
//
resources.ApplyResources(this.miSaveReferenceFile, "miSaveReferenceFile");
@@ -2516,6 +2526,7 @@
private DevExpress.XtraBars.BarButtonItem miMarkForSwapping;
private DevExpress.XtraBars.BarButtonItem miSwapWithMarked;
private DevExpress.XtraBars.BarButtonItem miConvert;
}
private DevExpress.XtraBars.BarButtonItem miSaveAs;
}
}

View File

@@ -308,6 +308,10 @@ namespace ChanSort.Ui
this.currentRefFile = Path.Combine(Path.GetDirectoryName(this.currentTvFile) ?? "",
Path.GetFileNameWithoutExtension(this.currentTvFile) + ".txt");
}
if (this.currentTvSerializer != null)
this.currentTvSerializer.SaveAsFileName = tvDataFile;
this.Text = this.title + " - " + this.currentTvFile;
}
@@ -358,6 +362,7 @@ namespace ChanSort.Ui
this.UpdateFavoritesEditor(this.DataRoot.SupportedFavorites);
this.colEncrypted.OptionsColumn.AllowEdit = this.currentTvSerializer.Features.EncryptedFlagEdit;
this.colAudioPid.OptionsColumn.AllowEdit = this.currentTvSerializer.Features.CanEditAudioPid;
this.colShortName.OptionsColumn.AllowEdit = this.currentTvSerializer.Features.AllowShortNameEdit;
this.UpdateMenu(true);
if (this.DataRoot.Warnings.Length > 0 && this.miShowWarningsAfterLoad.Checked)
@@ -1796,6 +1801,8 @@ namespace ChanSort.Ui
this.mnuGotoChannelList.Enabled = fileLoaded;
this.mnuGotoFavList.Enabled = fileLoaded;
this.miGotoLeftList.Enabled = this.miGotoRightList.Enabled = fileLoaded;
this.miSaveAs.Enabled = fileLoaded && this.currentTvSerializer.Features.CanSaveAs;
this.miSaveAs.Visibility = miSaveAs.Enabled ? BarItemVisibility.Always : BarItemVisibility.Never;
}
this.miAddChannel.Enabled = mayEdit; // && isRight;
@@ -3331,6 +3338,11 @@ namespace ChanSort.Ui
TryExecute(this.SaveFiles);
}
private void miSaveAs_ItemClick(object sender, ItemClickEventArgs e)
{
TryExecute(this.ShowSaveFileDialog);
}
private void miConvert_ItemClick(object sender, ItemClickEventArgs e)
{
XtraMessageBox.Show(this, Resources.MainForm_miConvert_MessageBody, Resources.MainForm_miConvert_MessageHeader);
@@ -3936,6 +3948,6 @@ namespace ChanSort.Ui
}
}
#endregion
#endregion
}
}

View File

@@ -276,7 +276,7 @@
<value>gridLeft</value>
</data>
<data name="&gt;&gt;gridLeft.Type" xml:space="preserve">
<value>ChanSort.XGridControl, ChanSort, Version=1.0.8403.33212, Culture=neutral, PublicKeyToken=null</value>
<value>ChanSort.XGridControl, ChanSort, Version=1.0.8404.23947, Culture=neutral, PublicKeyToken=null</value>
</data>
<data name="&gt;&gt;gridLeft.Parent" xml:space="preserve">
<value>grpOutputList</value>
@@ -419,6 +419,12 @@
<data name="miSave.ImageOptions.ImageIndex" type="System.Int32, mscorlib">
<value>4</value>
</data>
<data name="miSaveAs.Caption" xml:space="preserve">
<value>Save as...</value>
</data>
<data name="miSaveAs.Hint" xml:space="preserve">
<value>Save channel list under a different file name</value>
</data>
<data name="miSaveReferenceFile.Caption" xml:space="preserve">
<value>Save reference list...</value>
</data>
@@ -1234,7 +1240,7 @@
<value>gviewLeft</value>
</data>
<data name="&gt;&gt;gviewLeft.Type" xml:space="preserve">
<value>ChanSort.XGridView, ChanSort, Version=1.0.8403.33212, Culture=neutral, PublicKeyToken=null</value>
<value>ChanSort.XGridView, ChanSort, Version=1.0.8404.23947, Culture=neutral, PublicKeyToken=null</value>
</data>
<data name="&gt;&gt;colIndex1.Name" xml:space="preserve">
<value>colIndex1</value>
@@ -1318,13 +1324,13 @@
<value>globalImageCollection1</value>
</data>
<data name="&gt;&gt;globalImageCollection1.Type" xml:space="preserve">
<value>ChanSort.Ui.GlobalImageCollection, ChanSort, Version=1.0.8403.33212, Culture=neutral, PublicKeyToken=null</value>
<value>ChanSort.Ui.GlobalImageCollection, ChanSort, Version=1.0.8404.23947, Culture=neutral, PublicKeyToken=null</value>
</data>
<data name="&gt;&gt;gviewRight.Name" xml:space="preserve">
<value>gviewRight</value>
</data>
<data name="&gt;&gt;gviewRight.Type" xml:space="preserve">
<value>ChanSort.XGridView, ChanSort, Version=1.0.8403.33212, Culture=neutral, PublicKeyToken=null</value>
<value>ChanSort.XGridView, ChanSort, Version=1.0.8404.23947, Culture=neutral, PublicKeyToken=null</value>
</data>
<data name="&gt;&gt;colIndex.Name" xml:space="preserve">
<value>colIndex</value>
@@ -1578,6 +1584,12 @@
<data name="&gt;&gt;miSave.Type" xml:space="preserve">
<value>DevExpress.XtraBars.BarButtonItem, DevExpress.XtraBars.v22.1, Version=22.1.6.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a</value>
</data>
<data name="&gt;&gt;miSaveAs.Name" xml:space="preserve">
<value>miSaveAs</value>
</data>
<data name="&gt;&gt;miSaveAs.Type" xml:space="preserve">
<value>DevExpress.XtraBars.BarButtonItem, DevExpress.XtraBars.v22.1, Version=22.1.6.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a</value>
</data>
<data name="&gt;&gt;miSaveReferenceFile.Name" xml:space="preserve">
<value>miSaveReferenceFile</value>
</data>
@@ -2113,7 +2125,7 @@
<value>DevExpress.XtraEditors.XtraForm, DevExpress.Utils.v22.1, Version=22.1.6.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a</value>
</data>
<data name="SharedImageCollection.Timestamp" type="System.DateTime, mscorlib">
<value>01/03/2023 18:28:18</value>
<value>01/04/2023 13:23:37</value>
</data>
<data name="SharedImageCollection.ImageSize" type="System.Drawing.Size, System.Drawing">
<value>16, 16</value>
@@ -2996,7 +3008,7 @@
<value>gridRight</value>
</data>
<data name="&gt;&gt;gridRight.Type" xml:space="preserve">
<value>ChanSort.XGridControl, ChanSort, Version=1.0.8403.33212, Culture=neutral, PublicKeyToken=null</value>
<value>ChanSort.XGridControl, ChanSort, Version=1.0.8404.23947, Culture=neutral, PublicKeyToken=null</value>
</data>
<data name="&gt;&gt;gridRight.Parent" xml:space="preserve">
<value>grpInputList</value>

View File

@@ -1,4 +1,5 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;
using ChanSort.Api;
using ChanSort.Loader.M3u;
@@ -8,8 +9,9 @@ namespace Test.Loader.M3u
[TestClass]
public class M3uTest
{
#region TestReading()
[TestMethod]
public void TestMethod1()
public void TestReading()
{
var m3uFile = TestUtils.DeploymentItem("Test.Loader.M3u\\TestFiles\\example.m3u");
var refFile = TestUtils.DeploymentItem("Test.Loader.M3u\\TestFiles\\example-ref.txt");
@@ -49,6 +51,28 @@ namespace Test.Loader.M3u
Assert.AreEqual(1, chans[5].NewProgramNr);
Assert.AreEqual(2, chans[4].NewProgramNr);
}
#endregion
#region TestSavingKeepsExtinfTags()
[TestMethod]
public void TestSavingKeepsExtinfTags()
{
var m3uFile = TestUtils.DeploymentItem("Test.Loader.M3u\\TestFiles\\extinftags.m3u");
var orig = File.ReadAllText(m3uFile);
var loader = new M3uPlugin();
var ser = loader.CreateSerializer(m3uFile);
ser.Load();
ser.Save();
var text = File.ReadAllText(m3uFile);
orig = orig.Replace("\r", "").TrimEnd();
text = text.Replace("\r", "").TrimEnd();
NUnit.Framework.Assert.AreEqual(orig, text);
}
#endregion
#region TestChannelAndFavListEditing
[TestMethod]
@@ -58,6 +82,5 @@ namespace Test.Loader.M3u
RoundtripTest.TestChannelAndFavListEditing(tempFile, new M3uPlugin());
}
#endregion
}
}

View File

@@ -15,6 +15,9 @@
<ProjectReference Include="..\Test.Loader\Test.Loader.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="TestFiles\extinftags.m3u">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestFiles\example-ref.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
@@ -25,6 +28,7 @@
<ItemGroup>
<PackageReference Include="MSTest.TestAdapter" Version="3.0.2" />
<PackageReference Include="MSTest.TestFramework" Version="3.0.2" />
<PackageReference Include="NUnit" Version="3.13.3" />
</ItemGroup>
<Import Project="..\packages\NETStandard.Library.2.0.3\build\net48\NETStandard.Library.targets" Condition="Exists('..\packages\NETStandard.Library.2.0.3\build\net48\NETStandard.Library.targets')" />
</Project>

View File

@@ -0,0 +1,14 @@
#EXTM3U url-tvg="http://satip-server.local/epg/myepg.xml"
#
#EXTINF:-1 tvg-id="3sat" tvg-logo="https://raw.githubusercontent.com/jnk22/kodinerds-iptv/master/logos/tv/3sat.png",3sat HD
#EXTALBUMARTURL:https://raw.githubusercontent.com/jnk22/kodinerds-iptv/master/logos/tv/3sat.png
http://satip-server.local/?src=1&freq=11347&pol=v&msys=dvbs2&mtype=8psk&sr=22000&pids=0,16,17,18,20,21,6500,6510,6520,6521,6522,6523,6530,6531,6570
#
#EXTINF:-1 tvg-id="kika" tvg-logo="https://raw.githubusercontent.com/jnk22/kodinerds-iptv/master/logos/tv/kika.png",KiKA HD
#EXTALBUMARTURL:https://raw.githubusercontent.com/jnk22/kodinerds-iptv/master/logos/tv/kika.png
http://satip-server.local/?src=1&freq=11347&pol=v&msys=dvbs2&mtype=8psk&sr=22000&pids=0,16,17,18,20,21,6600,6610,6620,6621,6622,6630,6631,6670
#EXTINF:0 group-title="Streams",Russia Today
https://rt-news-gd.secure2.footprint.net/1103.m3u8
#EXTINF:0,RBK
http://e3.online.video.rbc.ru/online2/rbctv_576p/index.m3u8

View File

@@ -1,6 +1,13 @@
ChanSort Change Log
===================
2023-01-04
- TCL/Thomson: improved file detection (.tar file or directory containing DtvData.db,
satellite.db, cloneCRC.bin)
- m3u: #EXTINF tag data is displayed in "Short Name" column and can be edited
- m3u: fixed saving #EXTINF lines containing tag data
- m3u: readded "File / Save as" menu item (but not for other types of lists)
2023-01-03
- added support for TCL / Thomson \*.tar channel lists (containing DtvData.db and satellite.db)
- updated hotbird reference list for Italy

View File

@@ -94,6 +94,10 @@ Do not make any changes in the service menu, as this could damage your TV. Only
- Models that export files named dvb\*_config.xml.
- Models that export a cvt_database.dat file, e.g. 24 GHB 5944: see [Sharp](#Sharp)
<a name="tcl"/>TCL, Thomson
---
- Models that export a .tar file containing DtvData.db and satellite.db
<a name="satcodx"/>SatcoDX (supplier for ITT, Medion, Nabo, ok., PEAQ, Schaub-Lorenz, Silva-Schneider, Telefunken)
---
Various brands use the same hardware for DVB-S, which exports .sdx files

View File

@@ -94,6 +94,10 @@ Servicemenu: MENU 1147 / MENU 11471147 / SOURCE 2580
- Modelle die Dateien mit Namen dvb\*_config.xml exportieren.
- Modelle die eine cvt_database.dat Datei exportieren, z.B. 24 GHB 5944: siehe "Sharp"
<a name="tcl"/>TCL, Thomson
---
- Modelle die eine .tar Datei exportieren, in der DtvData.db und satellite.db enthalten sind
<a name="satcodx"/>SatcoDX (Lieferant f<>r ITT, Medion, Nabo, ok., PEAQ, Schaub-Lorenz, Silva-Schneider, Telefunken)
---
Mehrere Marken nutzen die gleiche Hardware f<>r DVB-S und exportieren .sdx Dateien