using System; using System.Collections.Generic; using System.IO; using System.Text; namespace ChanSort.Api { /// /// Reads a reference list from a .csv file with the format /// [dummy1],ProgramNr,[dummy2],UID,ChannelName[,SignalSource,FavAndFlags] /// public class CsvFileSerializer : SerializerBase { private static readonly List Columns = new List { "OldPosition", "Position", "Name", "OriginalNetworkId", "TransportStreamId", "ServiceId", "Favorites", "Skip", "Lock", "Hidden" }; #region ctor() public CsvFileSerializer(string fileName) : base(fileName) { this.Features.ChannelNameEdit = ChannelNameEditMode.All; this.Features.CanSkipChannels = true; this.Features.CanDeleteChannels = true; this.Features.CanHaveGaps = true; this.Features.EncryptedFlagEdit = false; this.DataRoot.SortedFavorites = false; this.DataRoot.SupportedFavorites = Favorites.A | Favorites.B | Favorites.C | Favorites.D | Favorites.E; } #endregion public override string DisplayName => "ChanSort .csv Reference List Loader"; #region Load() public override void Load() { using (var stream = new StreamReader(this.FileName)) { var lineNr = 0; var line = ""; try { while ((line = stream.ReadLine()) != null) this.ReadChannel(line, ++lineNr); } catch (Exception ex) { throw new FileLoadException($"Error in reference file line #{lineNr}: {line}", ex); } } } #endregion #region ReadChannel() private void ReadChannel(string line, int lineNr) { var parts = CsvFile.Parse(line, ','); if (parts.Count < 5) return; int programNr; if (!int.TryParse(parts[1], out programNr)) return; var uid = parts[3]; if (uid.StartsWith("S")) // remove satellite orbital position from UID ... not all TV models provide this information uid = "S" + uid.Substring(uid.IndexOf('-')); var signalSource = GetSignalSource(ref programNr, uid, parts); if (signalSource == 0) return; var channelList = this.GetChannelList(signalSource); if (channelList == null) return; var name = parts[4]; var channel = new ChannelInfo(signalSource, lineNr, programNr, name); var uidParts = uid.Split('-'); if (uidParts.Length >= 4) { int val; if (int.TryParse(uidParts[1], out val)) channel.OriginalNetworkId = val; if (int.TryParse(uidParts[2], out val)) channel.TransportStreamId = val; if (int.TryParse(uidParts[3], out val)) channel.ServiceId = val; } if (parts.Count >= 7) ApplyFlags(channel, parts[6]); this.DataRoot.AddChannel(channelList, channel); } #endregion #region GetSignalSource() private static SignalSource GetSignalSource(ref int slot, string uid, IList parts) { // new lists store a bitmask which defines the type of channel and list it came from if (parts.Count >= 6 && parts[5].Length >= 4) { SignalSource s = 0; var code = parts[5]; if (code[0] == 'A') s |= SignalSource.Analog; else if (code[0] == 'D') s |= SignalSource.Digital; if (code[1] == 'A') s |= SignalSource.Antenna; else if (code[1] == 'C') s |= SignalSource.Cable; else if (code[1] == 'S') s |= SignalSource.Sat; else if (code[1] == 'I') s |= SignalSource.IP; if (code[2] == 'T') s |= SignalSource.Tv; else if (code[2] == 'R') s |= SignalSource.Radio; s |= (SignalSource) (int.Parse(code.Substring(3)) << 12); return s; } // compatibility for older lists var isTv = slot < 0x4000; slot &= 0x3FFFF; SignalSource signalSource; switch (uid[0]) { case 'S': signalSource = SignalSource.DvbS; break; case 'C': signalSource = SignalSource.DvbCT; break; case 'A': signalSource = SignalSource.AnalogCT; break; case 'H': signalSource = SignalSource.HdPlusD; break; default: return 0; } signalSource |= isTv ? SignalSource.Tv : SignalSource.Radio; return signalSource; } #endregion #region GetChannelList() private ChannelList GetChannelList(SignalSource signalSource) { var channelList = this.DataRoot.GetChannelList(signalSource); if (channelList == null) { channelList = new ChannelList(signalSource, CreateCaption(signalSource)); channelList.VisibleColumnFieldNames = Columns; this.DataRoot.AddChannelList(channelList); } return channelList; } #endregion #region CreateCaption() private string CreateCaption(SignalSource signalSource) { var sb = new StringBuilder(); if ((signalSource & SignalSource.DvbT) == SignalSource.DvbT) sb.Append("DVB-T"); else if ((signalSource & SignalSource.DvbC) == SignalSource.DvbC) sb.Append("DVB-C"); else if ((signalSource & SignalSource.DvbS) == SignalSource.DvbS) sb.Append("DVB-S"); else if ((signalSource & SignalSource.IP) == SignalSource.IP) sb.Append("IP"); else if ((signalSource & SignalSource.Digital) == SignalSource.Digital) sb.Append("DVB"); else if ((signalSource & SignalSource.Analog) == SignalSource.Analog) sb.Append("Analog"); sb.Append(" "); if ((signalSource & SignalSource.Tv) == SignalSource.Tv) sb.Append("TV"); else if ((signalSource & SignalSource.Radio) == SignalSource.Radio) sb.Append("Radio"); else sb.Append("Data"); return sb.ToString(); } #endregion #region ApplyFlags() private void ApplyFlags(ChannelInfo channel, string flags) { channel.Lock = false; channel.Skip = false; channel.Hidden = false; foreach (var c in flags) { switch (c) { case '1': channel.Favorites |= Favorites.A; break; case '2': channel.Favorites |= Favorites.B; break; case '3': channel.Favorites |= Favorites.C; break; case '4': channel.Favorites |= Favorites.D; break; case '5': channel.Favorites |= Favorites.E; break; case 'L': channel.Lock = true; break; case 'S': channel.Skip = true; break; case 'H': channel.Hidden = true; break; case 'D': channel.IsDeleted = true; break; } } } #endregion #region EncodeSignalSource() private static string EncodeSignalSource(SignalSource signalSource) { var sb = new StringBuilder(); if ((signalSource & SignalSource.Analog) != 0) sb.Append('A'); else sb.Append('D'); if ((signalSource & SignalSource.Antenna) != 0) sb.Append('A'); else if ((signalSource & SignalSource.Cable) != 0) sb.Append('C'); else if ((signalSource & SignalSource.Sat) != 0) sb.Append('S'); else sb.Append("I"); if ((signalSource & SignalSource.Radio) != 0) sb.Append('R'); else sb.Append('T'); sb.Append((int) signalSource >> 12); return sb.ToString(); } #endregion #region EncodeFavoritesAndFlags() private static string EncodeFavoritesAndFlags(ChannelInfo channel) { var sb = new StringBuilder(); if ((channel.Favorites & Favorites.A) != 0) sb.Append('1'); if ((channel.Favorites & Favorites.B) != 0) sb.Append('2'); if ((channel.Favorites & Favorites.C) != 0) sb.Append('3'); if ((channel.Favorites & Favorites.D) != 0) sb.Append('4'); if ((channel.Favorites & Favorites.E) != 0) sb.Append('5'); if (channel.Lock) sb.Append('L'); if (channel.Skip) sb.Append('S'); if (channel.Hidden) sb.Append('H'); if (channel.IsDeleted) sb.Append('D'); return sb.ToString(); } #endregion #region Save() public override void Save(string tvDataFile) { Save(tvDataFile, this.DataRoot); this.FileName = tvDataFile; } public static void Save(string tvDataFile, DataRoot dataRoot) { using (var stream = new StreamWriter(tvDataFile)) { Save(stream, dataRoot); } } public static void Save(StreamWriter stream, DataRoot dataRoot) { foreach (var channelList in dataRoot.ChannelLists) { if (channelList.IsMixedSourceFavoritesList) // these pseudo-lists would create dupes for all channels continue; foreach (var channel in channelList.GetChannelsByNewOrder()) { if (channel.NewProgramNr == -1) continue; var line = string.Format("{0},{1},{2},{3},\"{4}\",{5},{6}", "", // past: channel.RecordIndex, channel.NewProgramNr, "", // past: channel.TransportStreamId, channel.Uid, channel.Name, EncodeSignalSource(channel.SignalSource), EncodeFavoritesAndFlags(channel)); stream.WriteLine(line); } } } #endregion } }