From 19499d0d570820e828c6327b99e3b21374b628b3 Mon Sep 17 00:00:00 2001 From: Horst Beham Date: Thu, 2 Jan 2020 11:48:00 +0100 Subject: [PATCH] initial support for .m3u Lists (for SAT>IP) --- .../ChanSort.Loader.M3u.csproj | 79 +++++++++ source/ChanSort.Loader.M3u/Channel.cs | 14 ++ .../Properties/AssemblyInfo.cs | 36 ++++ source/ChanSort.Loader.M3u/Serializer.cs | 161 ++++++++++++++++++ .../ChanSort.Loader.M3u/SerializerPlugin.cs | 16 ++ source/ChanSort.sln | 14 ++ source/ChanSort/ChanSort.csproj | 4 + source/ChanSort/MainForm.cs | 4 +- source/ChanSort/MainForm.resx | 60 +++---- source/changelog.md | 4 + 10 files changed, 360 insertions(+), 32 deletions(-) create mode 100644 source/ChanSort.Loader.M3u/ChanSort.Loader.M3u.csproj create mode 100644 source/ChanSort.Loader.M3u/Channel.cs create mode 100644 source/ChanSort.Loader.M3u/Properties/AssemblyInfo.cs create mode 100644 source/ChanSort.Loader.M3u/Serializer.cs create mode 100644 source/ChanSort.Loader.M3u/SerializerPlugin.cs diff --git a/source/ChanSort.Loader.M3u/ChanSort.Loader.M3u.csproj b/source/ChanSort.Loader.M3u/ChanSort.Loader.M3u.csproj new file mode 100644 index 0000000..1b0b711 --- /dev/null +++ b/source/ChanSort.Loader.M3u/ChanSort.Loader.M3u.csproj @@ -0,0 +1,79 @@ + + + + + Debug + AnyCPU + {484028B6-3AAE-4F7E-A88A-76BEEB70203B} + Library + Properties + ChanSort.Loader.M3u + ChanSort.Loader.M3u + v4.6 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + latest + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + latest + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + latest + prompt + MinimumRecommendedRules.ruleset + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + latest + prompt + MinimumRecommendedRules.ruleset + + + + + + + + + + + + + + + + + + + + + {dccffa08-472b-4d17-bb90-8f513fc01392} + ChanSort.Api + + + + \ No newline at end of file diff --git a/source/ChanSort.Loader.M3u/Channel.cs b/source/ChanSort.Loader.M3u/Channel.cs new file mode 100644 index 0000000..a309f69 --- /dev/null +++ b/source/ChanSort.Loader.M3u/Channel.cs @@ -0,0 +1,14 @@ +using ChanSort.Api; + +namespace ChanSort.Loader.M3u +{ + internal class Channel : ChannelInfo + { + public string Uri { get; } + + public Channel(int index, int progNr, string name, string uri) : base(SignalSource.IP, index, progNr, name) + { + this.Uri = uri; + } + } +} diff --git a/source/ChanSort.Loader.M3u/Properties/AssemblyInfo.cs b/source/ChanSort.Loader.M3u/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d800aaa --- /dev/null +++ b/source/ChanSort.Loader.M3u/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ChanSort.Loader.M3u")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ChanSort.Loader.M3u")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("484028b6-3aae-4f7e-a88a-76beeb70203b")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/source/ChanSort.Loader.M3u/Serializer.cs b/source/ChanSort.Loader.M3u/Serializer.cs new file mode 100644 index 0000000..002946f --- /dev/null +++ b/source/ChanSort.Loader.M3u/Serializer.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Web; +using ChanSort.Api; + +namespace ChanSort.Loader.M3u +{ + class Serializer : SerializerBase + { + private readonly ChannelList allChannels = new ChannelList(SignalSource.IP, "All"); + + private List lines = new List(); + + #region ctor() + public Serializer(string inputFile) : base(inputFile) + { + this.Features.ChannelNameEdit = ChannelNameEditMode.All; + this.Features.DeleteMode = DeleteMode.Physically; + this.Features.SortedFavorites = false; + this.Features.SupportedFavorites = 0; + this.Features.CanLockChannels = false; + this.Features.CanSkipChannels = false; + this.Features.CanHideChannels = false; + + this.DataRoot.AddChannelList(this.allChannels); + + base.DefaultEncoding = new UTF8Encoding(false); + this.allChannels.VisibleColumnFieldNames = new List() + { + "OldPosition", "Position", "Name", "FreqInMhz", "Polarity", "SymbolRate", "VideoPid", "AudioPid", "Satellite" + }; + } + #endregion + + #region Load() + public override void Load() + { + var rdr = new StreamReader(this.FileName); + var header = rdr.ReadLine()?.TrimEnd(); + if (header != "#EXTM3U") + throw new FileLoadException("Unsupported .m3u file: " + this.FileName); + + int lineNr=0; + string line1, line2; + while ((line1 = rdr.ReadLine()) != null) + { + ++lineNr; + if (line1.Trim() == "") + continue; + + var lineNr1 = lineNr; + while ((line2 = rdr.ReadLine()) != null) + { + ++lineNr; + if (line2.Trim() != "") + { + ReadChannel(lineNr1, line1, line2); + break; + } + } + } + } + #endregion + + #region ReadChannel() + private static readonly Regex ExtInfRegex = new Regex(@"^#EXTINF:\d+,(?:(\d+)\. )?(.*)$"); + private void ReadChannel(int lineNr, string line1, string line2) + { + var match = ExtInfRegex.Match(line1); + if (!match.Success) + throw new FileLoadException($"Unsupported #EXTINF line #{lineNr}: {line1}"); + + int progNr = string.IsNullOrEmpty(match.Groups[2].Value) + ? this.allChannels.Count + 1 + : this.ParseInt(match.Groups[1].Value); + + Uri uri; + try + { + uri = new Uri(line2); + } + catch + { + throw new FileLoadException($"Unsupported URI in line #{lineNr}: {line2}"); + } + + var chan = new Channel(lineNr, progNr, match.Groups[2].Value, line2); + chan.Satellite = uri.GetLeftPart(UriPartial.Path); + var parms = HttpUtility.ParseQueryString(uri.Query); + foreach (var key in parms.AllKeys) + { + var val = parms.Get(key); + switch (key) + { + case "freq": + chan.FreqInMhz = this.ParseInt(val); + break; + case "pol": + if (val.Length == 1) + chan.Polarity = Char.ToUpper(val[0]); + break; + case "sr": + chan.SymbolRate = this.ParseInt(val); + break; + case "pids": + var pids = val.Split(','); + //if (pids.Length > 3) + // chan.PcrPid = this.ParseInt(pids[3]); + if (pids.Length > 4) + chan.VideoPid = this.ParseInt(pids[4]); + if (pids.Length > 5) + chan.AudioPid = this.ParseInt(pids[5]); + break; + } + } + + this.DataRoot.AddChannel(this.allChannels, chan); + } + #endregion + + #region DefaultEncoding + public override Encoding DefaultEncoding + { + get => base.DefaultEncoding; // set to UTF-8 without BOM in constructor + set { } // can't be changed + } + #endregion + + + #region Save() + public override void Save(string tvOutputFile) + { + using var file = new StreamWriter(new FileStream(tvOutputFile, FileMode.Create), this.DefaultEncoding); + file.WriteLine("#EXTM3U"); + 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) + { + file.WriteLine($"#EXTINF:0,{chan.NewProgramNr}. {chan.Name}"); + file.WriteLine(chan.Uri); + } + } + + this.FileName = tvOutputFile; + } + #endregion + + #region GetFileInformation() + public override string GetFileInformation() + { + StringBuilder sb = new StringBuilder(); + sb.Append(base.GetFileInformation()); + return sb.ToString(); + } + #endregion + } +} diff --git a/source/ChanSort.Loader.M3u/SerializerPlugin.cs b/source/ChanSort.Loader.M3u/SerializerPlugin.cs new file mode 100644 index 0000000..1ba18ea --- /dev/null +++ b/source/ChanSort.Loader.M3u/SerializerPlugin.cs @@ -0,0 +1,16 @@ +using ChanSort.Api; + +namespace ChanSort.Loader.M3u +{ + public class SerializerPlugin : ISerializerPlugin + { + public string DllName { get; set; } + public string PluginName => "m3u (WinAmp, VLC, SAT>IP, ...)"; + public string FileFilter => "*.m3u"; + + public SerializerBase CreateSerializer(string inputFile) + { + return new Serializer(inputFile); + } + } +} diff --git a/source/ChanSort.sln b/source/ChanSort.sln index 046f91e..005d6ee 100644 --- a/source/ChanSort.sln +++ b/source/ChanSort.sln @@ -74,6 +74,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test.Loader.Toshiba", "Test EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test.Loader.VDR", "Test.Loader.VDR\Test.Loader.VDR.csproj", "{AED060F0-495C-494C-89C2-7A96A0FA3762}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChanSort.Loader.M3u", "ChanSort.Loader.M3u\ChanSort.Loader.M3u.csproj", "{484028B6-3AAE-4F7E-A88A-76BEEB70203B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -408,6 +410,18 @@ Global {AED060F0-495C-494C-89C2-7A96A0FA3762}.Release|Mixed Platforms.Build.0 = Release|Any CPU {AED060F0-495C-494C-89C2-7A96A0FA3762}.Release|x86.ActiveCfg = Release|x86 {AED060F0-495C-494C-89C2-7A96A0FA3762}.Release|x86.Build.0 = Release|x86 + {484028B6-3AAE-4F7E-A88A-76BEEB70203B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {484028B6-3AAE-4F7E-A88A-76BEEB70203B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {484028B6-3AAE-4F7E-A88A-76BEEB70203B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {484028B6-3AAE-4F7E-A88A-76BEEB70203B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {484028B6-3AAE-4F7E-A88A-76BEEB70203B}.Debug|x86.ActiveCfg = Debug|x86 + {484028B6-3AAE-4F7E-A88A-76BEEB70203B}.Debug|x86.Build.0 = Debug|x86 + {484028B6-3AAE-4F7E-A88A-76BEEB70203B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {484028B6-3AAE-4F7E-A88A-76BEEB70203B}.Release|Any CPU.Build.0 = Release|Any CPU + {484028B6-3AAE-4F7E-A88A-76BEEB70203B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {484028B6-3AAE-4F7E-A88A-76BEEB70203B}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {484028B6-3AAE-4F7E-A88A-76BEEB70203B}.Release|x86.ActiveCfg = Release|x86 + {484028B6-3AAE-4F7E-A88A-76BEEB70203B}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/source/ChanSort/ChanSort.csproj b/source/ChanSort/ChanSort.csproj index 1446333..45bf3ce 100644 --- a/source/ChanSort/ChanSort.csproj +++ b/source/ChanSort/ChanSort.csproj @@ -404,6 +404,10 @@ {e972d8a1-2f5f-421c-ac91-cff45e5191be} ChanSort.Loader.LG + + {484028b6-3aae-4f7e-a88a-76beeb70203b} + ChanSort.Loader.M3u + {68da8072-3a29-4076-9f64-d66f38349585} ChanSort.Loader.Panasonic diff --git a/source/ChanSort/MainForm.cs b/source/ChanSort/MainForm.cs index 93ca1f3..198bfce 100644 --- a/source/ChanSort/MainForm.cs +++ b/source/ChanSort/MainForm.cs @@ -1512,7 +1512,7 @@ namespace ChanSort.Ui if (col == this.colDebug) return col.Visible; if (col == this.colSignalSource) return col.Visible; if (col == this.colLogicalIndex) return col.Visible; - if (col == this.colPolarity) return (source & SignalSource.Sat) != 0; + if (col == this.colPolarity) return (source & SignalSource.Sat) != 0 || (source & SignalSource.IP) != 0; return true; } @@ -1618,7 +1618,7 @@ namespace ChanSort.Ui this.btnToggleFavF.Enabled = mayEdit && (this.DataRoot.SupportedFavorites & Favorites.F) != 0 && this.subListIndex != 6; this.btnToggleFavG.Enabled = mayEdit && (this.DataRoot.SupportedFavorites & Favorites.G) != 0 && this.subListIndex != 7; this.btnToggleFavH.Enabled = mayEdit && (this.DataRoot.SupportedFavorites & Favorites.H) != 0 && this.subListIndex != 8; - this.btnToggleLock.Enabled = mayEdit; + this.btnToggleLock.Enabled = mayEdit && this.DataRoot.CanLock; if (afterFileLoad) { diff --git a/source/ChanSort/MainForm.resx b/source/ChanSort/MainForm.resx index 0f7a8ae..c76ca5c 100644 --- a/source/ChanSort/MainForm.resx +++ b/source/ChanSort/MainForm.resx @@ -270,7 +270,7 @@ Numeric - 499, 374 + 499, 368 1 @@ -291,7 +291,7 @@ Bottom - 2, 428 + 2, 422 2, 2, 2, 2 @@ -330,7 +330,7 @@ 6, 13 - 1416, 557 + 1402, 551 Bottom, Left, Right @@ -339,7 +339,7 @@ 0, 5 - 1405, 0 + 1391, 0 Pr# @@ -357,7 +357,7 @@ 0 - 1411, 22 + 1397, 22 6 @@ -381,7 +381,7 @@ 0, 83 - 1416, 27 + 1402, 27 10 @@ -795,7 +795,7 @@ 0, 0 - 1416, 26 + 1402, 26 barDockControlTop @@ -813,10 +813,10 @@ Bottom - 0, 557 + 0, 551 - 1416, 0 + 1402, 0 barDockControlBottom @@ -837,7 +837,7 @@ 0, 26 - 0, 531 + 0, 525 barDockControlLeft @@ -855,10 +855,10 @@ Right - 1416, 26 + 1402, 26 - 0, 531 + 0, 525 barDockControlRight @@ -1018,7 +1018,7 @@ Top, Right - 1238, 4 + 1224, 4 166, 54 @@ -1045,7 +1045,7 @@ 0, 33 - 1226, 0 + 1212, 0 No channel lists @@ -1063,7 +1063,7 @@ 0 - 1232, 22 + 1218, 22 5 @@ -1147,7 +1147,7 @@ 0, 26 - 1416, 57 + 1402, 57 0 @@ -1264,7 +1264,7 @@ globalImageCollection1 - ChanSort.Ui.GlobalImageCollection, ChanSort, Version=1.0.7305.24811, Culture=neutral, PublicKeyToken=null + ChanSort.Ui.GlobalImageCollection, ChanSort, Version=1.0.7305.33710, Culture=neutral, PublicKeyToken=null gviewRight @@ -1374,6 +1374,12 @@ DevExpress.XtraGrid.Columns.GridColumn, DevExpress.XtraGrid.v19.2, Version=19.2.5.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a + + colPolarity + + + DevExpress.XtraGrid.Columns.GridColumn, DevExpress.XtraGrid.v19.2, Version=19.2.5.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a + colChannelOrTransponder @@ -1428,12 +1434,6 @@ DevExpress.XtraGrid.Columns.GridColumn, DevExpress.XtraGrid.v19.2, Version=19.2.5.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a - - colPolarity - - - DevExpress.XtraGrid.Columns.GridColumn, DevExpress.XtraGrid.v19.2, Version=19.2.5.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a - colNetworkName @@ -1957,7 +1957,7 @@ DevExpress.XtraEditors.XtraForm, DevExpress.Utils.v19.2, Version=19.2.5.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a - 01/01/2020 13:48:29 + 01/01/2020 18:44:40 16, 16 @@ -2392,7 +2392,7 @@ 0, 0 - 503, 447 + 503, 441 0 @@ -2773,7 +2773,7 @@ Signal source - 903, 374 + 889, 368 1 @@ -2794,7 +2794,7 @@ Bottom - 2, 428 + 2, 422 2, 2, 2, 2 @@ -2986,7 +2986,7 @@ 2, 21 - 903, 33 + 889, 33 0 @@ -3010,7 +3010,7 @@ 0, 0 - 907, 447 + 893, 441 0 @@ -3034,7 +3034,7 @@ Panel2 - 1416, 447 + 1402, 441 5 diff --git a/source/changelog.md b/source/changelog.md index ad70342..9560f72 100644 --- a/source/changelog.md +++ b/source/changelog.md @@ -1,6 +1,10 @@ ChanSort Change Log =================== +2020-01-02 +- added support for m3u lists (SAT>IP, VLC, WinAmp, ...) +- disabled "Lock" toggle button when the list does not support parental locks + 2020-01-01 - fixed loading of Samsung .scm files (Samsung.ini file was missing in the release package) - added "polarity" information for Samsung .scm and .zip files