mirror of
https://github.com/PredatH0r/ChanSort.git
synced 2026-01-16 04:12:03 +01:00
- 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:
@@ -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();
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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:<length>[ key="value" ...],<TrackName>
|
||||
/// </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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
17
source/ChanSort/MainForm.Designer.cs
generated
17
source/ChanSort/MainForm.Designer.cs
generated
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -276,7 +276,7 @@
|
||||
<value>gridLeft</value>
|
||||
</data>
|
||||
<data name=">>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=">>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=">>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=">>colIndex1.Name" xml:space="preserve">
|
||||
<value>colIndex1</value>
|
||||
@@ -1318,13 +1324,13 @@
|
||||
<value>globalImageCollection1</value>
|
||||
</data>
|
||||
<data name=">>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=">>gviewRight.Name" xml:space="preserve">
|
||||
<value>gviewRight</value>
|
||||
</data>
|
||||
<data name=">>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=">>colIndex.Name" xml:space="preserve">
|
||||
<value>colIndex</value>
|
||||
@@ -1578,6 +1584,12 @@
|
||||
<data name=">>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=">>miSaveAs.Name" xml:space="preserve">
|
||||
<value>miSaveAs</value>
|
||||
</data>
|
||||
<data name=">>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=">>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=">>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=">>gridRight.Parent" xml:space="preserve">
|
||||
<value>grpInputList</value>
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
14
source/Test.Loader.M3u/TestFiles/extinftags.m3u
Normal file
14
source/Test.Loader.M3u/TestFiles/extinftags.m3u
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user