MythTV  master
channelimporter.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
2 /*
3  * Copyright (C) Daniel Kristjansson 2007
4  *
5  * This file is licensed under GPL v2 or (at your option) any later version.
6  *
7  */
8 
9 // C++ includes
10 #include <iostream>
11 #include <utility>
12 #include <algorithm>
13 
14 // Qt includes
15 #include <QTextStream>
16 
17 using namespace std;
18 
19 // MythTV headers
20 #include "channelimporter.h"
21 #include "mythdialogbox.h"
22 #include "mythdb.h"
23 #include "mpegstreamdata.h" // for kEncDecrypted
24 #include "channelutil.h"
25 
26 #define LOC QString("ChanImport: ")
27 
28 static QString map_str(QString str)
29 {
30  if (str.isEmpty())
31  return "";
32  return str;
33 }
34 
36  int sourceid)
37 {
38  if (_transports.empty())
39  {
40  if (m_useGui)
41  {
42  int channels = ChannelUtil::GetChannelCount(sourceid);
43 
44  LOG(VB_GENERAL, LOG_INFO, LOC + (channels ?
45  (m_success ?
46  QString("Found %1 channels")
47  .arg(channels) :
48  "No new channels to process") :
49  "No channels to process.."));
50 
52  channels ?
53  (m_success ? tr("Found %n channel(s)", "", channels) :
54  tr("Failed to find any new channels!"))
55  : tr("Failed to find any channels."));
56  }
57  else
58  {
59  cout << (ChannelUtil::GetChannelCount() ?
60  "No new channels to process" :
61  "No channels to process..");
62  }
63 
64  return;
65  }
66 
67  ScanDTVTransportList transports = _transports;
68 
69  // Print some scan parameters
70  {
71  cout << endl << "Scan parameters:" << endl;
72  bool require_av = (m_serviceRequirements & kRequireAV) == kRequireAV;
73  bool require_a = (m_serviceRequirements & kRequireAudio) != 0;
74  cout << "Desired Services : " << (require_av ? "tv" : require_a ? "tv+radio" : "all") << endl;
75  cout << "Unencrypted Only : " << (m_ftaOnly ? "yes" : "no") << endl;
76  cout << "Logical Channel Numbers only: " << (m_lcnOnly ? "yes" : "no") << endl;
77  cout << "Complete scan data required : " << (m_completeOnly ? "yes" : "no") << endl;
78  cout << "Full search for old channels: " << (m_fullChannelSearch ? "yes" : "no") << endl;
79  }
80 
81  // Print out each channel
82  if (VERBOSE_LEVEL_CHECK(VB_CHANSCAN, LOG_ANY))
83  {
84  cout << endl << "Channel list before processing (";
85  cout << SimpleCountChannels(transports) << "):" << endl;
86  ChannelImporterBasicStats infoA = CollectStats(transports);
87  cout << FormatChannels(transports, &infoA).toLatin1().constData() << endl;
88  cout << endl;
89  }
90 
91  uint saved_scan = 0;
92  if (m_doSave)
93  saved_scan = SaveScan(transports);
94 
95  CleanupDuplicates(transports);
96 
97  FilterServices(transports);
98 
99  // Print out each transport
100  uint transports_scanned_size = transports.size();
101  if (VERBOSE_LEVEL_CHECK(VB_CHANSCAN, LOG_ANY))
102  {
103  cout << endl;
104  cout << "Transport list (" << transports_scanned_size << "):" << endl;
105  cout << FormatTransports(transports).toLatin1().constData() << endl;
106  }
107 
108  // Pull in DB info in transports
109  // Channels not found in scan but only in DB are returned in db_trans
110  sourceid = transports[0].m_channels[0].m_sourceId;
111  ScanDTVTransportList db_trans = GetDBTransports(sourceid, transports);
112 
113  // Make sure "Open Cable" channels are marked that way.
114  FixUpOpenCable(transports);
115 
116  // All channels in the scan after comparing with the database
117  if (VERBOSE_LEVEL_CHECK(VB_CHANSCAN, LOG_ANY))
118  {
119  cout << endl << "Channel list after compare with database (";
120  cout << SimpleCountChannels(transports) << "):" << endl;
121  ChannelImporterBasicStats infoA = CollectStats(transports);
122  cout << FormatChannels(transports, &infoA).toLatin1().constData() << endl;
123  cout << endl;
124  }
125 
126  // Add channels from the DB to the channels from the scan
127  // and possibly delete one or more of the off-air channels
128  if (m_doDelete)
129  {
130  ScanDTVTransportList trans = transports;
131  for (const auto & tran : db_trans)
132  trans.push_back(tran);
133  uint deleted_count = DeleteChannels(trans);
134  if (deleted_count)
135  transports = trans;
136  }
137 
138  // Determine System Info standards..
139  ChannelImporterBasicStats info = CollectStats(transports);
140 
141  // Determine uniqueness of various naming schemes
143  CollectUniquenessStats(transports, info);
144 
145  // Print out each channel
146  cout << endl;
147  cout << "Channel list (" << SimpleCountChannels(transports) << "):" << endl;
148  cout << FormatChannels(transports, &info).toLatin1().constData() << endl;
149 
150  // Create summary
151  QString msg = GetSummary(transports_scanned_size, info, stats);
152  cout << msg.toLatin1().constData() << endl << endl;
153 
154  if (m_doInsert)
155  InsertChannels(transports, info);
156 
157  if (m_doDelete && sourceid)
158  DeleteUnusedTransports(sourceid);
159 
160  if (m_doDelete || m_doInsert)
161  ScanInfo::MarkProcessed(saved_scan);
162 }
163 
165 {
166  switch (type)
167  {
168  // non-conflicting
169  case kATSCNonConflicting: return "ATSC";
170  case kDVBNonConflicting: return "DVB";
171  case kSCTENonConflicting: return "SCTE";
172  case kMPEGNonConflicting: return "MPEG";
173  case kNTSCNonConflicting: return "NTSC";
174  // conflicting
175  case kATSCConflicting: return "ATSC";
176  case kDVBConflicting: return "DVB";
177  case kSCTEConflicting: return "SCTE";
178  case kMPEGConflicting: return "MPEG";
179  case kNTSCConflicting: return "NTSC";
180  }
181  return "Unknown";
182 }
183 
184 // Ask user what to do with the off-air channels
185 //
187  ScanDTVTransportList &transports)
188 {
189  vector<uint> off_air_list;
190  QMap<uint,bool> deleted;
191  ScanDTVTransportList off_air_transports;
192 
193  for (size_t i = 0; i < transports.size(); ++i)
194  {
195  ScanDTVTransport transport_copy;
196  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
197  {
198  ChannelInsertInfo chan = transports[i].m_channels[j];
199  bool was_in_db = chan.m_dbMplexId && chan.m_channelId;
200  if (!was_in_db)
201  continue;
202 
203  if (!chan.m_inPmt)
204  {
205  off_air_list.push_back(i<<16|j);
206  AddChanToCopy(transport_copy, transports[i], chan);
207  }
208  }
209  if (!transport_copy.m_channels.empty())
210  off_air_transports.push_back(transport_copy);
211  }
212 
213  if (off_air_list.empty())
214  return 0;
215 
216  // List of off-air channels (in database but not in the scan)
217  cout << endl << "Off-air channels (" << SimpleCountChannels(off_air_transports) << "):" << endl;
218  ChannelImporterBasicStats infoA = CollectStats(off_air_transports);
219  cout << FormatChannels(off_air_transports, &infoA).toLatin1().constData() << endl;
220 
221  // Ask user whether to delete all or some of these stale channels
222  // if some is selected ask about each individually
223  //: %n is the number of channels
224  QString msg = tr("Found %n off-air channel(s).", "", off_air_list.size());
225  DeleteAction action = QueryUserDelete(msg);
226  if (kDeleteIgnoreAll == action)
227  return 0;
228 
229  if (kDeleteAll == action)
230  {
231  for (uint item : off_air_list)
232  {
233  int i = item >> 16;
234  int j = item & 0xFFFF;
236  transports[i].m_channels[j].m_channelId);
237  deleted[item] = true;
238  }
239  }
240  else if (kDeleteInvisibleAll == action)
241  {
242  for (uint item : off_air_list)
243  {
244  int i = item >> 16;
245  int j = item & 0xFFFF;
246  int chanid = transports[i].m_channels[j].m_channelId;
247  QString channum = ChannelUtil::GetChanNum(chanid);
249  ChannelUtil::SetChannelValue("channum", QString("_%1").arg(channum),
250  chanid);
251  }
252  }
253  else
254  {
255  // TODO manual delete
256  }
257 
258  // TODO delete encrypted channels when m_ftaOnly set
259 
260  if (deleted.empty())
261  return 0;
262 
263  // Create a new transports list without the deleted channels
264  ScanDTVTransportList newlist;
265  for (size_t i = 0; i < transports.size(); ++i)
266  {
267  newlist.push_back(transports[i]);
268  newlist.back().m_channels.clear();
269  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
270  {
271  if (!deleted.contains(i<<16|j))
272  {
273  newlist.back().m_channels.push_back(
274  transports[i].m_channels[j]);
275  }
276  }
277  }
278 
279  transports = newlist;
280  return deleted.size();
281 }
282 
284 {
285  MSqlQuery query(MSqlQuery::InitCon());
286  query.prepare(
287  "SELECT mplexid FROM dtv_multiplex "
288  "WHERE sourceid = :SOURCEID1 AND "
289  " mplexid NOT IN "
290  " (SELECT mplexid "
291  " FROM channel "
292  " WHERE sourceid = :SOURCEID2)");
293  query.bindValue(":SOURCEID1", sourceid);
294  query.bindValue(":SOURCEID2", sourceid);
295  if (!query.exec())
296  {
297  MythDB::DBError("DeleteUnusedTransports() -- select", query);
298  return 0;
299  }
300 
301  QString msg = tr("Found %n unused transport(s).", "", query.size());
302 
303  LOG(VB_GENERAL, LOG_INFO, LOC + msg);
304 
305  if (query.size() == 0)
306  return 0;
307 
308  DeleteAction action = QueryUserDelete(msg);
309  if (kDeleteIgnoreAll == action)
310  return 0;
311 
312  if (kDeleteAll == action)
313  {
314  query.prepare(
315  "DELETE FROM dtv_multiplex "
316  "WHERE sourceid = :SOURCEID1 AND "
317  " mplexid NOT IN "
318  " (SELECT mplexid "
319  " FROM channel "
320  " WHERE sourceid = :SOURCEID2)");
321  query.bindValue(":SOURCEID1", sourceid);
322  query.bindValue(":SOURCEID2", sourceid);
323  if (!query.exec())
324  {
325  MythDB::DBError("DeleteUnusedTransports() -- delete", query);
326  return 0;
327  }
328  }
329  else
330  {
331  // TODO manual delete
332  LOG(VB_GENERAL, LOG_INFO, LOC + "Manual delete of transport not implemented");
333  }
334  return 0;
335 }
336 
338  const ScanDTVTransportList &transports,
339  const ChannelImporterBasicStats &info)
340 {
341  ScanDTVTransportList list = transports;
342  ScanDTVTransportList inserted;
343  ScanDTVTransportList updated;
344  ScanDTVTransportList skipped_inserts;
345  ScanDTVTransportList skipped_updates;
346 
347  // Insert or update all channels with non-conflicting channum
348  // and complete tuning information.
349  uint chantype = (uint) kChannelTypeNonConflictingFirst;
350  for (; chantype <= (uint) kChannelTypeNonConflictingLast; ++chantype)
351  {
352  auto type = (ChannelType) chantype;
353  uint new_chan = 0;
354  uint old_chan = 0;
355  CountChannels(list, info, type, new_chan, old_chan);
356 
357  if (kNTSCNonConflicting == type)
358  continue;
359 
360  if (old_chan)
361  {
362  //: %n is the number of channels, %1 is the type of channel
363  QString msg = tr("Found %n old %1 channel(s).", "", old_chan)
364  .arg(toString(type));
365 
366  UpdateAction action = QueryUserUpdate(msg);
367  list = UpdateChannels(list, info, action, type, updated, skipped_updates);
368  }
369  if (new_chan)
370  {
371  //: %n is the number of channels, %1 is the type of channel
372  QString msg = tr("Found %n new %1 channel(s).", "", new_chan)
373  .arg(toString(type));
374 
375  InsertAction action = QueryUserInsert(msg);
376  list = InsertChannels(list, info, action, type, inserted, skipped_inserts);
377  }
378  }
379 
380  if (!m_isInteractive)
381  return;
382 
383  // If any of the potential uniques is high and inserting
384  // with those as the channum would result in few conflicts
385  // ask user if it is ok to to proceed using it as the channum
386 
387  // For remaining channels with complete tuning information
388  // insert channels with contiguous list of numbers as the channums
389  chantype = (uint) kChannelTypeConflictingFirst;
390  for (; chantype <= (uint) kChannelTypeConflictingLast; ++chantype)
391  {
392  auto type = (ChannelType) chantype;
393  uint new_chan = 0;
394  uint old_chan = 0;
395  CountChannels(list, info, type, new_chan, old_chan);
396 
397  if (old_chan)
398  {
399  //: %n is the number of channels, %1 is the type of channel
400  QString msg = tr("Found %n conflicting old %1 channel(s).",
401  "", old_chan).arg(toString(type));
402 
403  UpdateAction action = QueryUserUpdate(msg);
404  list = UpdateChannels(list, info, action, type, updated, skipped_updates);
405  }
406  if (new_chan)
407  {
408  //: %n is the number of channels, %1 is the type of channel
409  QString msg = tr("Found %n new conflicting %1 channel(s).",
410  "", new_chan).arg(toString(type));
411 
412  InsertAction action = QueryUserInsert(msg);
413  list = InsertChannels(list, info, action, type, inserted, skipped_inserts);
414  }
415  }
416 
417  // List what has been done with each channel
418  if (!updated.empty())
419  {
420  cout << endl << "Updated old channels (" << SimpleCountChannels(updated) << "):" << endl;
421  cout << FormatChannels(updated).toLatin1().constData() << endl;
422  }
423  if (!skipped_updates.empty())
424  {
425  cout << endl << "Skipped old channels (" << SimpleCountChannels(skipped_updates) << "):" << endl;
426  cout << FormatChannels(skipped_updates).toLatin1().constData() << endl;
427  }
428  if (!inserted.empty())
429  {
430  cout << endl << "Inserted new channels (" << SimpleCountChannels(inserted) << "):" << endl;
431  cout << FormatChannels(inserted).toLatin1().constData() << endl;
432  }
433  if (!skipped_inserts.empty())
434  {
435  cout << endl << "Skipped new channels (" << SimpleCountChannels(skipped_inserts) << "):" << endl;
436  cout << FormatChannels(skipped_inserts).toLatin1().constData() << endl;
437  }
438 
439  // Remaining channels and sum uniques again
440  if (!list.empty())
441  {
442  ChannelImporterBasicStats ninfo = CollectStats(list);
443  ChannelImporterUniquenessStats nstats = CollectUniquenessStats(list, ninfo);
444  cout << "Remaining channels (" << SimpleCountChannels(list) << "):" << endl;
445  cout << FormatChannels(list).toLatin1().constData() << endl;
446  cout << endl;
447  cout << GetSummary(list.size(), ninfo, nstats).toLatin1().constData();
448  cout << endl;
449  }
450 }
451 
452 // ChannelImporter::InsertChannels
453 //
454 // transports List of channels to update
455 // info Channel statistics
456 // action Insert all, Insert manually, Ignore all
457 // type Channel type such as dvb or atsc
458 // inserted_list List of inserted channels
459 // skipped_list List of skipped channels
460 //
461 // return: List of transports/channels that have not been inserted
462 //
464  const ScanDTVTransportList &transports,
465  const ChannelImporterBasicStats &info,
468  ScanDTVTransportList &inserted_list,
469  ScanDTVTransportList &skipped_list)
470 {
471  QString channelFormat = "%1_%2";
472 
473  ScanDTVTransportList next_list;
474 
475  bool cancel_all = false;
476  bool ok_all = false;
477 
478  // Insert all channels with non-conflicting channum
479  // and complete tuning information.
480  for (const auto & transport : transports)
481  {
482  ScanDTVTransport new_transport;
483  ScanDTVTransport inserted_transport;
484  ScanDTVTransport skipped_transport;
485 
486  for (size_t j = 0; j < transport.m_channels.size(); ++j)
487  {
488  ChannelInsertInfo chan = transport.m_channels[j];
489 
490  bool asked = false;
491  bool filter = false;
492  bool handle = false;
493  if (!chan.m_channelId && (kInsertIgnoreAll == action) &&
494  IsType(info, chan, type))
495  {
496  filter = true;
497  }
498  else if (!chan.m_channelId && IsType(info, chan, type))
499  {
500  handle = true;
501  }
502 
503  if (cancel_all)
504  {
505  handle = false;
506  }
507 
508  if (handle && kInsertManual == action)
509  {
510  OkCancelType rc = QueryUserInsert(transport, chan);
511  if (kOCTCancelAll == rc)
512  {
513  cancel_all = true;
514  handle = false;
515  }
516  else if (kOCTCancel == rc)
517  {
518  handle = false;
519  }
520  else if (kOCTOk == rc)
521  {
522  asked = true;
523  }
524  }
525 
526  if (handle)
527  {
528  bool conflicting = false;
529 
530  if (chan.m_chanNum.isEmpty() ||
532  {
533  if ((kATSCNonConflicting == type) ||
534  (kATSCConflicting == type))
535  {
536  chan.m_chanNum = channelFormat
537  .arg(chan.m_atscMajorChannel)
538  .arg(chan.m_atscMinorChannel);
539  }
540  else if (chan.m_siStandard == "dvb")
541  {
542  chan.m_chanNum = QString("%1").arg(chan.m_serviceId);
543  }
544  else if (chan.m_freqId.isEmpty())
545  {
546  chan.m_chanNum = QString("%1-%2")
547  .arg(chan.m_sourceId)
548  .arg(chan.m_serviceId);
549  }
550  else
551  {
552  chan.m_chanNum = QString("%1-%2")
553  .arg(chan.m_freqId)
554  .arg(chan.m_serviceId);
555  }
556 
557  conflicting = ChannelUtil::IsConflicting(
558  chan.m_chanNum, chan.m_sourceId);
559  }
560 
561  // Only ask if not already asked before with kInsertManual
562  if (m_isInteractive && !asked &&
563  (conflicting || (kChannelTypeConflictingFirst <= type)))
564  {
565  bool ok_done = false;
566  if (ok_all)
567  {
568  QString val = ComputeSuggestedChannelNum(chan);
569  bool ok = CheckChannelNumber(val, chan);
570  if (ok)
571  {
572  chan.m_chanNum = val;
573  conflicting = false;
574  ok_done = true;
575  }
576  }
577  if (!ok_done)
578  {
579  OkCancelType rc =
580  QueryUserResolve(transport, chan);
581 
582  conflicting = true;
583  if (kOCTCancelAll == rc)
584  {
585  cancel_all = true;
586  }
587  else if (kOCTOk == rc)
588  {
589  conflicting = false;
590  }
591  else if (kOCTOkAll == rc)
592  {
593  conflicting = false;
594  ok_all = true;
595  }
596  }
597  }
598 
599  if (conflicting)
600  {
601  handle = false;
602  }
603  }
604 
605  bool inserted = false;
606  if (handle)
607  {
608  int chanid = ChannelUtil::CreateChanID(
609  chan.m_sourceId, chan.m_chanNum);
610 
611  chan.m_channelId = (chanid > 0) ? chanid : chan.m_channelId;
612 
613  if (chan.m_channelId)
614  {
615  uint tsid = chan.m_vctTsId;
616  tsid = (tsid) ? tsid : chan.m_sdtTsId;
617  tsid = (tsid) ? tsid : chan.m_patTsId;
618  tsid = (tsid) ? tsid : chan.m_vctChanTsId;
619 
620  if (!chan.m_dbMplexId)
621  {
623  chan.m_sourceId, transport, tsid, chan.m_origNetId);
624  }
625  else
626  {
627  // Find the matching multiplex. This updates the
628  // transport and network ID's in case the transport
629  // was created manually
631  tsid, chan.m_origNetId);
632  if (id >= 0)
633  chan.m_dbMplexId = id;
634  }
635  }
636 
637  if (chan.m_channelId && chan.m_dbMplexId)
638  {
639  chan.m_channelId = chanid;
640 
641  inserted = ChannelUtil::CreateChannel(
642  chan.m_dbMplexId,
643  chan.m_sourceId,
644  chan.m_channelId,
645  chan.m_callSign,
646  chan.m_serviceName,
647  chan.m_chanNum,
648  chan.m_serviceId,
649  chan.m_atscMajorChannel,
650  chan.m_atscMinorChannel,
651  chan.m_useOnAirGuide,
653  chan.m_freqId,
654  QString(),
655  chan.m_format,
656  QString(),
657  chan.m_defaultAuthority,
658  chan.m_serviceType);
659 
660  if (!transport.m_iptvTuning.GetDataURL().isEmpty())
662  transport.m_iptvTuning);
663  }
664  }
665 
666  if (inserted)
667  {
668  // Update list of inserted channels
669  AddChanToCopy(inserted_transport, transport, chan);
670  }
671 
672  if (filter)
673  {
674  // Update list of skipped channels
675  AddChanToCopy(skipped_transport, transport, chan);
676  }
677  else if (!inserted)
678  {
679  // Update list of remaining channels
680  AddChanToCopy(new_transport, transport, chan);
681  }
682  }
683 
684  if (!new_transport.m_channels.empty())
685  next_list.push_back(new_transport);
686 
687  if (!skipped_transport.m_channels.empty())
688  skipped_list.push_back(skipped_transport);
689 
690  if (!inserted_transport.m_channels.empty())
691  inserted_list.push_back(inserted_transport);
692  }
693 
694  return next_list;
695 }
696 
697 // ChannelImporter::UpdateChannels
698 //
699 // transports list of transports/channels to update
700 // info Channel statistics
701 // action Update All, Ignore All
702 // type Channel type such as dvb or atsc
703 // inserted List of inserted channels
704 // skipped List of skipped channels
705 //
706 // return: List of transports/channels that have not been updated
707 //
709  const ScanDTVTransportList &transports,
710  const ChannelImporterBasicStats &info,
713  ScanDTVTransportList &updated_list,
714  ScanDTVTransportList &skipped_list)
715 {
716  QString channelFormat = "%1_%2";
717  bool renameChannels = false;
718 
719  ScanDTVTransportList next_list;
720 
721  // update all channels with non-conflicting channum
722  // and complete tuning information.
723  for (const auto & transport : transports)
724  {
725  ScanDTVTransport new_transport;
726  ScanDTVTransport updated_transport;
727  ScanDTVTransport skipped_transport;
728 
729  for (size_t j = 0; j < transport.m_channels.size(); ++j)
730  {
731  ChannelInsertInfo chan = transport.m_channels[j];
732 
733  bool filter = false;
734  bool handle = false;
735  if (chan.m_channelId && (kUpdateIgnoreAll == action) &&
736  IsType(info, chan, type))
737  {
738  filter = true;
739  }
740  else if (chan.m_channelId && IsType(info, chan, type))
741  {
742  handle = true;
743  }
744 
745  if (handle)
746  {
747  bool conflicting = false;
748 
749  if (m_keepChannelNumbers)
750  {
752  }
753  if (chan.m_chanNum.isEmpty() || renameChannels ||
755  chan.m_chanNum, chan.m_sourceId, chan.m_channelId))
756  {
757  if (kATSCNonConflicting == type)
758  {
759  chan.m_chanNum = channelFormat
760  .arg(chan.m_atscMajorChannel)
761  .arg(chan.m_atscMinorChannel);
762  }
763  else if (chan.m_siStandard == "dvb")
764  {
765  chan.m_chanNum = QString("%1").arg(chan.m_serviceId);
766  }
767  else if (chan.m_freqId.isEmpty())
768  {
769  chan.m_chanNum = QString("%1-%2")
770  .arg(chan.m_sourceId)
771  .arg(chan.m_serviceId);
772  }
773  else
774  {
775  chan.m_chanNum = QString("%1-%2")
776  .arg(chan.m_freqId)
777  .arg(chan.m_serviceId);
778  }
779 
780  conflicting = ChannelUtil::IsConflicting(
781  chan.m_chanNum, chan.m_sourceId, chan.m_channelId);
782  }
783 
784  if (conflicting)
785  {
786  handle = false;
787 
788  // Update list of skipped channels
789  AddChanToCopy(skipped_transport, transport, chan);
790  }
791  }
792 
793  bool updated = false;
794  if (handle)
795  {
797 
798  // Find the matching multiplex. This updates the
799  // transport and network ID's in case the transport
800  // was created manually
801  uint tsid = chan.m_vctTsId;
802  tsid = (tsid) ? tsid : chan.m_sdtTsId;
803  tsid = (tsid) ? tsid : chan.m_patTsId;
804  tsid = (tsid) ? tsid : chan.m_vctChanTsId;
806  tsid, chan.m_origNetId);
807  if (id >= 0)
808  chan.m_dbMplexId = id;
809 
810  updated = ChannelUtil::UpdateChannel(
811  chan.m_dbMplexId,
812  chan.m_sourceId,
813  chan.m_channelId,
814  chan.m_callSign,
815  chan.m_serviceName,
816  chan.m_chanNum,
817  chan.m_serviceId,
818  chan.m_atscMajorChannel,
819  chan.m_atscMinorChannel,
820  chan.m_useOnAirGuide,
821  ((chan.m_visible == kChannelAlwaysVisible ||
823  chan.m_visible :
825  chan.m_freqId,
826  QString(),
827  chan.m_format,
828  QString(),
829  chan.m_defaultAuthority,
830  chan.m_serviceType);
831  }
832 
833  if (updated)
834  {
835  // Update list of updated channels
836  AddChanToCopy(updated_transport, transport, chan);
837  }
838 
839  if (filter)
840  {
841  // Update list of skipped channels
842  AddChanToCopy(skipped_transport, transport, chan);
843  }
844  else if (!updated)
845  {
846  // Update list of remaining channels
847  AddChanToCopy(new_transport, transport, chan);
848  }
849  }
850 
851  if (!new_transport.m_channels.empty())
852  next_list.push_back(new_transport);
853 
854  if (!skipped_transport.m_channels.empty())
855  skipped_list.push_back(skipped_transport);
856 
857  if (!updated_transport.m_channels.empty())
858  updated_list.push_back(updated_transport);
859  }
860 
861  return next_list;
862 }
863 
864 // ChannelImporter::AddChanToCopy
865 //
866 // Add channel to copy of transport.
867 // This is used to keep track of what is done with each channel
868 //
869 // transport_copy with zero to all channels of transport
870 // transport transport with channel info as scanned
871 // chan one channel of transport, to be copied
872 //
874  ScanDTVTransport &transport_copy,
875  const ScanDTVTransport &transport,
876  const ChannelInsertInfo &chan
877 )
878 {
879  if (transport_copy.m_channels.empty())
880  {
881  transport_copy = transport;
882  transport_copy.m_channels.clear();
883  }
884  transport_copy.m_channels.push_back(chan);
885 }
886 
888 {
889  ScanDTVTransportList no_dups;
890 
892  if (!transports.empty())
893  tuner_type = transports[0].m_tuner_type;
894 
895  bool is_dvbs = ((DTVTunerType::kTunerTypeDVBS1 == tuner_type) ||
896  (DTVTunerType::kTunerTypeDVBS2 == tuner_type));
897 
898  uint freq_mult = (is_dvbs) ? 1 : 1000;
899 
900  vector<bool> ignore;
901  ignore.resize(transports.size());
902  for (size_t i = 0; i < transports.size(); ++i)
903  {
904  if (ignore[i])
905  continue;
906 
907  for (size_t j = i+1; j < transports.size(); ++j)
908  {
909  if (!transports[i].IsEqual(
910  tuner_type, transports[j], 500 * freq_mult))
911  {
912  continue;
913  }
914 
915  for (size_t k = 0; k < transports[j].m_channels.size(); ++k)
916  {
917  bool found_same = false;
918  for (size_t l = 0; l < transports[i].m_channels.size(); ++l)
919  {
920  if (transports[j].m_channels[k].IsSameChannel(
921  transports[i].m_channels[l]))
922  {
923  found_same = true;
924  transports[i].m_channels[l].ImportExtraInfo(
925  transports[j].m_channels[k]);
926  }
927  }
928  if (!found_same)
929  transports[i].m_channels.push_back(transports[j].m_channels[k]);
930  }
931  ignore[j] = true;
932  }
933  no_dups.push_back(transports[i]);
934  }
935 
936  transports = no_dups;
937 }
938 
940 {
941  bool require_av = (m_serviceRequirements & kRequireAV) == kRequireAV;
942  bool require_a = (m_serviceRequirements & kRequireAudio) != 0;
943 
944  for (auto & transport : transports)
945  {
946  ChannelInsertInfoList filtered;
947  for (size_t k = 0; k < transport.m_channels.size(); ++k)
948  {
949  if (m_ftaOnly && transport.m_channels[k].m_isEncrypted &&
950  transport.m_channels[k].m_decryptionStatus != kEncDecrypted)
951  continue;
952 
953  if (require_a && transport.m_channels[k].m_isDataService)
954  continue;
955 
956  if (require_av && transport.m_channels[k].m_isAudioService)
957  continue;
958 
959  // Filter channels out that do not have a logical channel number
960  if (m_lcnOnly && transport.m_channels[k].m_chanNum.isEmpty())
961  {
962  QString msg = FormatChannel(transport, transport.m_channels[k]);
963  LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("No LCN: %1").arg(msg));
964  continue;
965  }
966 
967  // Filter channels out that are not present in PAT and PMT.
968  if (m_completeOnly &&
969  !(transport.m_channels[k].m_inPat &&
970  transport.m_channels[k].m_inPmt ))
971  {
972  QString msg = FormatChannel(transport, transport.m_channels[k]);
973  LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("Not in PAT/PMT: %1").arg(msg));
974  continue;
975  }
976 
977  // Filter channels out that are not present in SDT and that are not ATSC
978  if (m_completeOnly &&
979  transport.m_channels[k].m_atscMajorChannel == 0 &&
980  transport.m_channels[k].m_atscMinorChannel == 0 &&
981  !(transport.m_channels[k].m_inPat &&
982  transport.m_channels[k].m_inPmt &&
983  transport.m_channels[k].m_inSdt &&
984  (transport.m_channels[k].m_patTsId ==
985  transport.m_channels[k].m_sdtTsId)))
986  {
987  QString msg = FormatChannel(transport, transport.m_channels[k]);
988  LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("Not in PAT/PMT/SDT: %1").arg(msg));
989  continue;
990  }
991 
992  // Filter channels out that do not have a name
993  if (m_completeOnly && transport.m_channels[k].m_serviceName.isEmpty())
994  {
995  QString msg = FormatChannel(transport, transport.m_channels[k]);
996  LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("No name: %1").arg(msg));
997  continue;
998  }
999 
1000  // Filter channels out only in channels.conf, i.e. not found
1001  if (transport.m_channels[k].m_inChannelsConf &&
1002  !(transport.m_channels[k].m_inPat ||
1003  transport.m_channels[k].m_inPmt ||
1004  transport.m_channels[k].m_inVct ||
1005  transport.m_channels[k].m_inNit ||
1006  transport.m_channels[k].m_inSdt))
1007  continue;
1008 
1009  filtered.push_back(transport.m_channels[k]);
1010  }
1011  transport.m_channels = filtered;
1012  }
1013 }
1014 
1023  uint sourceid, ScanDTVTransportList &transports) const
1024 {
1025  ScanDTVTransportList not_in_scan;
1026  int found_in_same_transport = 0;
1027  int found_in_other_transport = 0;
1028  int found_nowhere = 0;
1029 
1031  if (!transports.empty())
1032  tuner_type = transports[0].m_tuner_type;
1033 
1034  bool is_dvbs =
1035  (DTVTunerType::kTunerTypeDVBS1 == tuner_type) ||
1036  (DTVTunerType::kTunerTypeDVBS2 == tuner_type);
1037 
1038  uint freq_mult = (is_dvbs) ? 1 : 1000;
1039 
1040  MSqlQuery query(MSqlQuery::InitCon());
1041  query.prepare(
1042  "SELECT mplexid "
1043  "FROM dtv_multiplex "
1044  "WHERE sourceid = :SOURCEID "
1045  "GROUP BY mplexid "
1046  "ORDER BY mplexid");
1047  query.bindValue(":SOURCEID", sourceid);
1048 
1049  if (!query.exec())
1050  {
1051  MythDB::DBError("GetDBTransports()", query);
1052  return not_in_scan;
1053  }
1054 
1055  while (query.next())
1056  {
1057  ScanDTVTransport db_transport;
1058  uint mplexid = query.value(0).toUInt();
1059  if (db_transport.FillFromDB(tuner_type, mplexid))
1060  {
1061  if (db_transport.m_channels.empty())
1062  {
1063  continue;
1064  }
1065  }
1066 
1067  bool found_transport = false;
1068  QMap<uint,bool> found_chan;
1069 
1070  // Search for old channels in the same transport of the scan.
1071  for (auto & transport : transports) // All transports in scan
1072  { // Scanned transport
1073  if (transport.IsEqual(tuner_type, db_transport, 500 * freq_mult, true)) // Same transport?
1074  {
1075  found_transport = true;
1076  transport.m_mplex = db_transport.m_mplex; // Found multiplex
1077 
1078  for (size_t jdc = 0; jdc < db_transport.m_channels.size(); ++jdc) // All channels in database transport
1079  {
1080  if (!found_chan[jdc]) // Channel not found yet?
1081  {
1082  ChannelInsertInfo &db_chan = db_transport.m_channels[jdc]; // Channel in database transport
1083 
1084  for (auto & chan : transport.m_channels) // All channels in scanned transport
1085  { // Channel in scanned transport
1086  if (db_chan.IsSameChannel(chan, 2)) // Same transport, relaxed check
1087  {
1088  found_in_same_transport++;
1089  found_chan[jdc] = true; // Found channel from database in scan
1090  chan.m_dbMplexId = mplexid; // Found multiplex
1091  chan.m_channelId = db_chan.m_channelId; // This is the crucial field
1092  break; // Ready with scanned transport
1093  }
1094  }
1095  }
1096  }
1097  }
1098  }
1099 
1100  // Search for old channels in all transports of the scan.
1101  // This is done for all channels that have not yet been found.
1102  // This can identify the channels that have moved to another transport.
1103  if (m_fullChannelSearch)
1104  {
1105  for (size_t idc = 0; idc < db_transport.m_channels.size(); ++idc) // All channels in database transport
1106  {
1107  ChannelInsertInfo &db_chan = db_transport.m_channels[idc]; // Channel in database transport
1108 
1109  for (size_t jst = 0; jst < transports.size() && !found_chan[idc]; ++jst) // All transports in scan until found
1110  {
1111  ScanDTVTransport &transport = transports[jst]; // Scanned transport
1112  for (auto & chan : transport.m_channels) // All channels in scanned transport
1113  {
1114  // Channel in scanned transport
1115  if (db_chan.IsSameChannel(chan, 1)) // Different transport, check
1116  { // network id and service id
1117  found_in_other_transport++;
1118  found_chan[idc] = true; // Found channel from database in scan
1119  chan.m_channelId = db_chan.m_channelId; // This is the crucial field
1120  break; // Ready with scanned transport
1121  }
1122  }
1123  }
1124  }
1125  }
1126 
1127  // If the transport in the database is found in the scan
1128  // then all channels in that transport that are not found
1129  // in the scan are copied to the "not_in_scan" list.
1130  if (found_transport)
1131  {
1132  ScanDTVTransport tmp = db_transport;
1133  tmp.m_channels.clear();
1134 
1135  for (size_t idc = 0; idc < db_transport.m_channels.size(); ++idc)
1136  {
1137  if (!found_chan[idc])
1138  {
1139  tmp.m_channels.push_back(db_transport.m_channels[idc]);
1140  found_nowhere++;
1141  }
1142  }
1143 
1144  if (!tmp.m_channels.empty())
1145  not_in_scan.push_back(tmp);
1146  }
1147  }
1148  LOG(VB_GENERAL, LOG_INFO, LOC +
1149  QString("Old channels found in same transport: %1")
1150  .arg(found_in_same_transport));
1151  LOG(VB_GENERAL, LOG_INFO, LOC +
1152  QString("Old channels found in other transport: %1")
1153  .arg(found_in_other_transport));
1154  LOG(VB_GENERAL, LOG_INFO, LOC +
1155  QString("Old channels not found (off-air): %1")
1156  .arg(found_nowhere));
1157 
1158  return not_in_scan;
1159 }
1160 
1162 {
1164  for (auto & transport : transports)
1165  {
1166  for (auto & chan : transport.m_channels)
1167  {
1168  if (((chan.m_couldBeOpencable && (chan.m_siStandard == "mpeg")) ||
1169  chan.m_isOpencable) && !chan.m_inVct)
1170  {
1171  chan.m_siStandard = "opencable";
1172  }
1173  }
1174  }
1175 }
1176 
1178  const ScanDTVTransportList &transports)
1179 {
1181  for (const auto & transport : transports)
1182  {
1183  for (const auto & chan : transport.m_channels)
1184  {
1185  int enc = (chan.m_isEncrypted) ?
1186  ((chan.m_decryptionStatus == kEncDecrypted) ? 2 : 1) : 0;
1187  info.m_atscChannels[enc] += (chan.m_siStandard == "atsc");
1188  info.m_dvbChannels [enc] += (chan.m_siStandard == "dvb");
1189  info.m_mpegChannels[enc] += (chan.m_siStandard == "mpeg");
1190  info.m_scteChannels[enc] += (chan.m_siStandard == "opencable");
1191  info.m_ntscChannels[enc] += (chan.m_siStandard == "ntsc");
1192  if (chan.m_siStandard != "ntsc")
1193  {
1194  ++info.m_progNumCnt[chan.m_serviceId];
1195  ++info.m_chanNumCnt[map_str(chan.m_chanNum)];
1196  }
1197  if (chan.m_siStandard == "atsc")
1198  {
1199  ++info.m_atscNumCnt[(chan.m_atscMajorChannel << 16) |
1200  (chan.m_atscMinorChannel)];
1201  ++info.m_atscMinCnt[chan.m_atscMinorChannel];
1202  ++info.m_atscMajCnt[chan.m_atscMajorChannel];
1203  }
1204  if (chan.m_siStandard == "ntsc")
1205  {
1206  ++info.m_atscNumCnt[(chan.m_atscMajorChannel << 16) |
1207  (chan.m_atscMinorChannel)];
1208  }
1209  }
1210  }
1211 
1212  return info;
1213 }
1214 
1216  const ScanDTVTransportList &transports,
1217  const ChannelImporterBasicStats &info)
1218 {
1220 
1221  for (const auto & transport : transports)
1222  {
1223  for (const auto & chan : transport.m_channels)
1224  {
1225  stats.m_uniqueProgNum +=
1226  (info.m_progNumCnt[chan.m_serviceId] == 1) ? 1 : 0;
1227  stats.m_uniqueChanNum +=
1228  (info.m_chanNumCnt[map_str(chan.m_chanNum)] == 1) ? 1 : 0;
1229 
1230  if (chan.m_siStandard == "atsc")
1231  {
1232  stats.m_uniqueAtscNum +=
1233  (info.m_atscNumCnt[(chan.m_atscMajorChannel << 16) |
1234  (chan.m_atscMinorChannel)] == 1) ? 1 : 0;
1235  stats.m_uniqueAtscMin +=
1236  (info.m_atscMinCnt[(chan.m_atscMinorChannel)] == 1) ? 1 : 0;
1237  stats.m_maxAtscMajCnt = max(
1238  stats.m_maxAtscMajCnt,
1239  info.m_atscMajCnt[chan.m_atscMajorChannel]);
1240  }
1241  }
1242  }
1243 
1244  stats.m_uniqueTotal = (stats.m_uniqueProgNum + stats.m_uniqueAtscNum +
1245  stats.m_uniqueAtscMin + stats.m_uniqueChanNum);
1246 
1247  return stats;
1248 }
1249 
1250 
1252  const ScanDTVTransport &transport,
1253  const ChannelInsertInfo &chan,
1254  const ChannelImporterBasicStats *info)
1255 {
1256  QString msg;
1257  QTextStream ssMsg(&msg);
1258 
1259  ssMsg << transport.m_modulation.toString().toLatin1().constData()
1260  << ":";
1261  ssMsg << transport.m_frequency << ":";
1262 
1263  QString si_standard = (chan.m_siStandard=="opencable") ?
1264  QString("scte") : chan.m_siStandard;
1265 
1266  if (si_standard == "atsc" || si_standard == "scte")
1267  {
1268  ssMsg << (QString("%1:%2:%3-%4:%5:%6=%7=%8:%9")
1269  .arg(chan.m_callSign).arg(chan.m_chanNum)
1270  .arg(chan.m_atscMajorChannel)
1271  .arg(chan.m_atscMinorChannel)
1272  .arg(chan.m_serviceId)
1273  .arg(chan.m_vctTsId)
1274  .arg(chan.m_vctChanTsId)
1275  .arg(chan.m_patTsId)
1276  .arg(si_standard)).toLatin1().constData();
1277  }
1278  else if (si_standard == "dvb")
1279  {
1280  ssMsg << (QString("%1:%2:%3:%4:%5:%6=%7:%8")
1281  .arg(chan.m_serviceName).arg(chan.m_chanNum)
1282  .arg(chan.m_netId).arg(chan.m_origNetId)
1283  .arg(chan.m_serviceId)
1284  .arg(chan.m_sdtTsId)
1285  .arg(chan.m_patTsId)
1286  .arg(si_standard)).toLatin1().constData();
1287  }
1288  else
1289  {
1290  ssMsg << (QString("%1:%2:%3:%4:%5")
1291  .arg(chan.m_callSign).arg(chan.m_chanNum)
1292  .arg(chan.m_serviceId)
1293  .arg(chan.m_patTsId)
1294  .arg(si_standard)).toLatin1().constData();
1295  }
1296 
1297  if (info)
1298  {
1299  ssMsg <<"\t"
1300  << chan.m_channelId;
1301  }
1302 
1303  if (info)
1304  {
1305  ssMsg << ":"
1306  << (QString("cnt(pnum:%1,channum:%2)")
1307  .arg(info->m_progNumCnt[chan.m_serviceId])
1308  .arg(info->m_chanNumCnt[map_str(chan.m_chanNum)])
1309  ).toLatin1().constData();
1310  if (chan.m_siStandard == "atsc")
1311  {
1312  ssMsg <<
1313  (QString(":atsc_cnt(tot:%1,minor:%2)")
1314  .arg(info->m_atscNumCnt[
1315  (chan.m_atscMajorChannel << 16) |
1316  (chan.m_atscMinorChannel)])
1317  .arg(info->m_atscMinCnt[chan.m_atscMinorChannel])
1318  ).toLatin1().constData();
1319  }
1320  }
1321 
1322  return msg;
1323 }
1324 
1337  const ScanDTVTransport &/*transport*/,
1338  const ChannelInsertInfo &chan)
1339 {
1340  QString msg;
1341  QTextStream ssMsg(&msg);
1342 
1343  QString si_standard = (chan.m_siStandard=="opencable") ?
1344  QString("scte") : chan.m_siStandard;
1345 
1346  if (si_standard == "atsc" || si_standard == "scte")
1347  {
1348 
1349  if (si_standard == "atsc")
1350  {
1351  ssMsg << (QString("%1-%2")
1352  .arg(chan.m_atscMajorChannel)
1353  .arg(chan.m_atscMinorChannel)).toLatin1().constData();
1354  }
1355  else if (chan.m_freqId.isEmpty())
1356  {
1357  ssMsg << (QString("%1-%2")
1358  .arg(chan.m_sourceId)
1359  .arg(chan.m_serviceId)).toLatin1().constData();
1360  }
1361  else
1362  {
1363  ssMsg << (QString("%1-%2")
1364  .arg(chan.m_freqId)
1365  .arg(chan.m_serviceId)).toLatin1().constData();
1366  }
1367 
1368  if (!chan.m_callSign.isEmpty())
1369  ssMsg << (QString(" (%1)")
1370  .arg(chan.m_callSign)).toLatin1().constData();
1371  }
1372  else if (si_standard == "dvb")
1373  {
1374  ssMsg << (QString("%1 (%2 %3)")
1375  .arg(chan.m_serviceName).arg(chan.m_serviceId)
1376  .arg(chan.m_netId)).toLatin1().constData();
1377  }
1378  else if (chan.m_freqId.isEmpty())
1379  {
1380  ssMsg << (QString("%1-%2")
1381  .arg(chan.m_sourceId).arg(chan.m_serviceId))
1382  .toLatin1().constData();
1383  }
1384  else
1385  {
1386  ssMsg << (QString("%1-%2")
1387  .arg(chan.m_freqId).arg(chan.m_serviceId))
1388  .toLatin1().constData();
1389  }
1390 
1391  return msg;
1392 }
1393 
1395  const ScanDTVTransportList &transports_in,
1396  const ChannelImporterBasicStats *info)
1397 {
1398  // Sort transports in order of increasing frequency
1399  struct less_than_key
1400  {
1401  inline bool operator() (const ScanDTVTransport &t1, const ScanDTVTransport &t2)
1402  {
1403  return t1.m_frequency < t2.m_frequency;
1404  }
1405  };
1406  ScanDTVTransportList transports(transports_in);
1407  std::sort(transports.begin(), transports.end(), less_than_key());
1408 
1409  QString msg;
1410 
1411  for (auto & transport : transports)
1412  {
1413  for (size_t j = 0; j < transport.m_channels.size(); ++j)
1414  msg += FormatChannel(transport, transport.m_channels[j],
1415  info) + "\n";
1416  }
1417 
1418  return msg;
1419 }
1420 
1422  const ScanDTVTransport &transport)
1423 {
1424  QString msg;
1425  QTextStream ssMsg(&msg);
1426  ssMsg << transport.toString();
1427  return msg;
1428 }
1429 
1431  const ScanDTVTransportList &transports_in)
1432 {
1433  // Sort transports in order of increasing frequency
1434  struct less_than_key
1435  {
1436  inline bool operator() (const ScanDTVTransport &t1, const ScanDTVTransport &t2)
1437  {
1438  return t1.m_frequency < t2.m_frequency;
1439  }
1440  };
1441  ScanDTVTransportList transports(transports_in);
1442  std::sort(transports.begin(), transports.end(), less_than_key());
1443 
1444  QString msg;
1445 
1446  for (const auto & transport : transports)
1447  msg += FormatTransport(transport) + "\n";
1448 
1449  return msg;
1450 }
1451 
1453  uint transport_count,
1454  const ChannelImporterBasicStats &info,
1455  const ChannelImporterUniquenessStats &stats)
1456 {
1457  //: %n is the number of transports
1458  QString msg = tr("Found %n transport(s):\n", "", transport_count);
1459  msg += tr("Channels: FTA Enc Dec\n") +
1460  QString("ATSC %1 %2 %3\n")
1461  .arg(info.m_atscChannels[0],3).arg(info.m_atscChannels[1],3)
1462  .arg(info.m_atscChannels[2],3) +
1463  QString("DVB %1 %2 %3\n")
1464  .arg(info.m_dvbChannels [0],3).arg(info.m_dvbChannels [1],3)
1465  .arg(info.m_dvbChannels [2],3) +
1466  QString("SCTE %1 %2 %3\n")
1467  .arg(info.m_scteChannels[0],3).arg(info.m_scteChannels[1],3)
1468  .arg(info.m_scteChannels[2],3) +
1469  QString("MPEG %1 %2 %3\n")
1470  .arg(info.m_mpegChannels[0],3).arg(info.m_mpegChannels[1],3)
1471  .arg(info.m_mpegChannels[2],3) +
1472  QString("NTSC %1\n").arg(info.m_ntscChannels[0],3) +
1473  tr("Unique: prog %1 atsc %2 atsc minor %3 channum %4\n")
1474  .arg(stats.m_uniqueProgNum).arg(stats.m_uniqueAtscNum)
1475  .arg(stats.m_uniqueAtscMin).arg(stats.m_uniqueChanNum) +
1476  tr("Max atsc major count: %1")
1477  .arg(stats.m_maxAtscMajCnt);
1478 
1479  return msg;
1480 }
1481 
1483  const ChannelImporterBasicStats &info,
1484  const ChannelInsertInfo &chan, ChannelType type)
1485 {
1486  switch (type)
1487  {
1488  case kATSCNonConflicting:
1489  return ((chan.m_siStandard == "atsc") /* &&
1490  (info.m_atscNumCnt[(chan.m_atscMajorChannel << 16) |
1491  (chan.m_atscMinorChannel)] == 1) */);
1492 
1493  case kDVBNonConflicting:
1494  return ((chan.m_siStandard == "dvb") /* &&
1495  (info.m_progNumCnt[chan.m_serviceId] == 1) */);
1496 
1497  case kMPEGNonConflicting:
1498  return ((chan.m_siStandard == "mpeg") &&
1499  (info.m_chanNumCnt[map_str(chan.m_chanNum)] == 1));
1500 
1501  case kSCTENonConflicting:
1502  return (((chan.m_siStandard == "scte") ||
1503  (chan.m_siStandard == "opencable")) &&
1504  (info.m_chanNumCnt[map_str(chan.m_chanNum)] == 1));
1505 
1506  case kNTSCNonConflicting:
1507  return ((chan.m_siStandard == "ntsc") &&
1508  (info.m_atscNumCnt[(chan.m_atscMajorChannel << 16) |
1509  (chan.m_atscMinorChannel)] == 1));
1510 
1511  case kATSCConflicting:
1512  return ((chan.m_siStandard == "atsc") &&
1513  (info.m_atscNumCnt[(chan.m_atscMajorChannel << 16) |
1514  (chan.m_atscMinorChannel)] != 1));
1515 
1516  case kDVBConflicting:
1517  return ((chan.m_siStandard == "dvb") &&
1518  (info.m_progNumCnt[chan.m_serviceId] != 1));
1519 
1520  case kMPEGConflicting:
1521  return ((chan.m_siStandard == "mpeg") &&
1522  (info.m_chanNumCnt[map_str(chan.m_chanNum)] != 1));
1523 
1524  case kSCTEConflicting:
1525  return (((chan.m_siStandard == "scte") ||
1526  (chan.m_siStandard == "opencable")) &&
1527  (info.m_chanNumCnt[map_str(chan.m_chanNum)] != 1));
1528 
1529  case kNTSCConflicting:
1530  return ((chan.m_siStandard == "ntsc") &&
1531  (info.m_atscNumCnt[(chan.m_atscMajorChannel << 16) |
1532  (chan.m_atscMinorChannel)] != 1));
1533  }
1534  return false;
1535 }
1536 
1538  const ScanDTVTransportList &transports,
1539  const ChannelImporterBasicStats &info,
1540  ChannelType type, uint &new_chan, uint &old_chan)
1541 {
1542  new_chan = old_chan = 0;
1543  for (const auto & transport : transports)
1544  {
1545  for (auto chan : transport.m_channels)
1546  {
1547  if (IsType(info, chan, type))
1548  {
1549  if (chan.m_channelId)
1550  ++old_chan;
1551  else
1552  ++new_chan;
1553  }
1554  }
1555  }
1556 }
1557 
1559  const ScanDTVTransportList &transports)
1560 {
1561  int count = 0;
1562  for (const auto & transport : transports)
1563  count += transport.m_channels.size();
1564  return count;
1565 }
1566 
1579  const ChannelInsertInfo &chan)
1580 {
1581  static QMutex s_lastFreeLock;
1582  static QMap<uint,uint> s_lastFreeChanNumMap;
1583 
1584  // Suggest existing channel number if non-conflicting
1586  return chan.m_chanNum;
1587 
1588  // ATSC major-minor channel number
1589  QString channelFormat = "%1_%2";
1590  QString chan_num = channelFormat
1591  .arg(chan.m_atscMajorChannel)
1592  .arg(chan.m_atscMinorChannel);
1593  if (chan.m_atscMinorChannel)
1594  {
1595  if (!ChannelUtil::IsConflicting(chan_num, chan.m_sourceId))
1596  return chan_num;
1597  }
1598 
1599  // DVB
1600  if (chan.m_siStandard == "dvb")
1601  {
1602  // Service ID
1603  chan_num = QString("%1").arg(chan.m_serviceId);
1604  if (!ChannelUtil::IsConflicting(chan_num, chan.m_sourceId))
1605  return chan_num;
1606 
1607  // Frequency ID (channel) - Service ID
1608  if (!chan.m_freqId.isEmpty())
1609  {
1610  chan_num = QString("%1-%2")
1611  .arg(chan.m_freqId)
1612  .arg(chan.m_serviceId);
1613  if (!ChannelUtil::IsConflicting(chan_num, chan.m_sourceId))
1614  return chan_num;
1615  }
1616 
1617  // Service ID - Network ID
1618  chan_num = QString("%1-%2").arg(chan.m_serviceId).arg(chan.m_netId);
1619  if (!ChannelUtil::IsConflicting(chan_num, chan.m_sourceId))
1620  return chan_num;
1621 
1622  // Service ID - Transport ID
1623  chan_num = QString("%1-%2").arg(chan.m_serviceId).arg(chan.m_patTsId);
1624  if (!ChannelUtil::IsConflicting(chan_num, chan.m_sourceId))
1625  return chan_num;
1626  }
1627 
1628  // Find unused channel number
1629  QMutexLocker locker(&s_lastFreeLock);
1630  uint last_free_chan_num = s_lastFreeChanNumMap[chan.m_sourceId];
1631  for (last_free_chan_num++; ; ++last_free_chan_num)
1632  {
1633  chan_num = QString::number(last_free_chan_num);
1634  if (!ChannelUtil::IsConflicting(chan_num, chan.m_sourceId))
1635  break;
1636  }
1637  // cppcheck-suppress unreadVariable
1638  s_lastFreeChanNumMap[chan.m_sourceId] = last_free_chan_num;
1639 
1640  return chan_num;
1641 }
1642 
1645 {
1646  DeleteAction action = kDeleteAll;
1647  if (m_useGui)
1648  {
1649  int ret = -1;
1650  do
1651  {
1652  MythScreenStack *popupStack =
1653  GetMythMainWindow()->GetStack("popup stack");
1654  auto *deleteDialog =
1655  new MythDialogBox(msg, popupStack, "deletechannels");
1656 
1657  if (deleteDialog->Create())
1658  {
1659  deleteDialog->AddButton(tr("Delete All"));
1660  deleteDialog->AddButton(tr("Set all invisible"));
1661 // deleteDialog->AddButton(tr("Handle manually"));
1662  deleteDialog->AddButton(tr("Ignore All"));
1663  QObject::connect(deleteDialog, &MythDialogBox::Closed,
1664  [&](const QString & /*resultId*/, int result)
1665  {
1666  ret = result;
1667  m_eventLoop.quit();
1668  });
1669  popupStack->AddScreen(deleteDialog);
1670 
1671  m_eventLoop.exec();
1672  }
1673  } while (ret < 0);
1674 
1675  action = (0 == ret) ? kDeleteAll : action;
1676  action = (1 == ret) ? kDeleteInvisibleAll : action;
1677  action = (2 == ret) ? kDeleteIgnoreAll : action;
1678 // action = (2 == m_deleteChannelResult) ? kDeleteManual : action;
1679 // action = (3 == m_deleteChannelResult) ? kDeleteIgnoreAll : action;
1680  }
1681  else if (m_isInteractive)
1682  {
1683  cout << msg.toLatin1().constData()
1684  << endl
1685  << tr("Do you want to:").toLatin1().constData()
1686  << endl
1687  << tr("1. Delete All").toLatin1().constData()
1688  << endl
1689  << tr("2. Set all invisible").toLatin1().constData()
1690  << endl
1691 // cout << "3. Handle manually" << endl;
1692  << tr("4. Ignore All").toLatin1().constData()
1693  << endl;
1694  while (true)
1695  {
1696  string ret;
1697  cin >> ret;
1698  bool ok = false;
1699  uint val = QString(ret.c_str()).toUInt(&ok);
1700  if (ok && (val == 1 || val == 2 || val == 4))
1701  {
1702  action = (1 == val) ? kDeleteAll : action;
1703  action = (2 == val) ? kDeleteInvisibleAll : action;
1704  //action = (3 == val) ? kDeleteManual : action;
1705  action = (4 == val) ? kDeleteIgnoreAll : action;
1706  break;
1707  }
1708 
1709  //cout << "Please enter either 1, 2, 3 or 4:" << endl;
1710  cout << tr("Please enter either 1, 2 or 4:")
1711  .toLatin1().constData() << endl;//
1712  }
1713  }
1714 
1715  return action;
1716 }
1717 
1720 {
1721  InsertAction action = kInsertAll;
1722  if (m_useGui)
1723  {
1724  int ret = -1;
1725  do
1726  {
1727  MythScreenStack *popupStack =
1728  GetMythMainWindow()->GetStack("popup stack");
1729  auto *insertDialog =
1730  new MythDialogBox(msg, popupStack, "insertchannels");
1731 
1732  if (insertDialog->Create())
1733  {
1734  insertDialog->AddButton(tr("Insert All"));
1735  insertDialog->AddButton(tr("Insert Manually"));
1736  insertDialog->AddButton(tr("Ignore All"));
1737  QObject::connect(insertDialog, &MythDialogBox::Closed,
1738  [&](const QString & /*resultId*/, int result)
1739  {
1740  ret = result;
1741  m_eventLoop.quit();
1742  });
1743 
1744  popupStack->AddScreen(insertDialog);
1745  m_eventLoop.exec();
1746  }
1747  } while (ret < 0);
1748 
1749  action = (0 == ret) ? kInsertAll : action;
1750  action = (1 == ret) ? kInsertManual : action;
1751  action = (2 == ret) ? kInsertIgnoreAll : action;
1752  }
1753  else if (m_isInteractive)
1754  {
1755  cout << msg.toLatin1().constData()
1756  << endl
1757  << tr("Do you want to:").toLatin1().constData()
1758  << endl
1759  << tr("1. Insert All").toLatin1().constData()
1760  << endl
1761  << tr("2. Insert Manually").toLatin1().constData()
1762  << endl
1763  << tr("3. Ignore All").toLatin1().constData()
1764  << endl;
1765  while (true)
1766  {
1767  string ret;
1768  cin >> ret;
1769  bool ok = false;
1770  uint val = QString(ret.c_str()).toUInt(&ok);
1771  if (ok && (1 <= val) && (val <= 3))
1772  {
1773  action = (1 == val) ? kInsertAll : action;
1774  action = (2 == val) ? kInsertManual : action;
1775  action = (3 == val) ? kInsertIgnoreAll : action;
1776  break;
1777  }
1778 
1779  cout << tr("Please enter either 1, 2, or 3:")
1780  .toLatin1().constData() << endl;
1781  }
1782  }
1783 
1784  return action;
1785 }
1786 
1789 {
1790  UpdateAction action = kUpdateAll;
1791 
1792  if (m_useGui)
1793  {
1794  int ret = -1;
1795  do
1796  {
1797  MythScreenStack *popupStack =
1798  GetMythMainWindow()->GetStack("popup stack");
1799  auto *updateDialog =
1800  new MythDialogBox(msg, popupStack, "updatechannels");
1801 
1802  if (updateDialog->Create())
1803  {
1804  updateDialog->AddButton(tr("Update All"));
1805  updateDialog->AddButton(tr("Ignore All"));
1806  QObject::connect(updateDialog, &MythDialogBox::Closed,
1807  [&](const QString& /*resultId*/, int result)
1808  {
1809  ret = result;
1810  m_eventLoop.quit();
1811  });
1812 
1813  popupStack->AddScreen(updateDialog);
1814  m_eventLoop.exec();
1815  }
1816  } while (ret < 0);
1817 
1818  action = (0 == ret) ? kUpdateAll : action;
1819  action = (1 == ret) ? kUpdateIgnoreAll : action;
1820  }
1821  else if (m_isInteractive)
1822  {
1823  cout << msg.toLatin1().constData()
1824  << endl
1825  << tr("Do you want to:").toLatin1().constData()
1826  << endl
1827  << tr("1. Update All").toLatin1().constData()
1828  << endl
1829  << tr("2. Update Manually").toLatin1().constData()
1830  << endl
1831  << tr("3. Ignore All").toLatin1().constData()
1832  << endl;
1833  while (true)
1834  {
1835  string ret;
1836  cin >> ret;
1837  bool ok = false;
1838  uint val = QString(ret.c_str()).toUInt(&ok);
1839  if (ok && (1 <= val) && (val <= 3))
1840  {
1841  action = (1 == val) ? kUpdateAll : action;
1842  action = (2 == val) ? kUpdateManual : action;
1843  action = (3 == val) ? kUpdateIgnoreAll : action;
1844  break;
1845  }
1846 
1847  cout << tr("Please enter either 1, 2, or 3:")
1848  .toLatin1().constData() << endl;
1849  }
1850  }
1851 
1852  return action;
1853 }
1854 
1856  MythMainWindow *parent, const QString& title,
1857  const QString& message, QString &text)
1858 {
1859  int dc = -1;
1860  MythScreenStack *popupStack = parent->GetStack("popup stack");
1861  auto *popup = new MythDialogBox(title, message, popupStack,
1862  "manualchannelpopup");
1863 
1864  if (popup->Create())
1865  {
1866  popup->AddButton(QCoreApplication::translate("(Common)", "OK"));
1867  popup->AddButton(tr("Edit"));
1868  popup->AddButton(QCoreApplication::translate("(Common)", "Cancel"));
1869  popup->AddButton(QCoreApplication::translate("(Common)", "Cancel All"));
1870  QObject::connect(popup, &MythDialogBox::Closed,
1871  [&](const QString & /*resultId*/, int result)
1872  {
1873  dc = result;
1874  m_eventLoop.quit();
1875  });
1876  popupStack->AddScreen(popup);
1877  m_eventLoop.exec();
1878  }
1879  else
1880  {
1881  delete popup;
1882  popup = nullptr;
1883  }
1884 
1885  // Choice "Edit"
1886  if (1 == dc)
1887  {
1888  auto *textEdit =
1889  new MythTextInputDialog(popupStack,
1890  tr("Please enter a unique channel number."),
1891  FilterNone, false, text);
1892  if (textEdit->Create())
1893  {
1894  QObject::connect(textEdit, &MythTextInputDialog::haveResult,
1895  [&](QString result)
1896  {
1897  dc = 0;
1898  text = std::move(result);
1899  });
1900  QObject::connect(textEdit, &MythTextInputDialog::Exiting,
1901  [&]()
1902  {
1903  m_eventLoop.quit();
1904  });
1905 
1906  popupStack->AddScreen(textEdit);
1907  m_eventLoop.exec();
1908  }
1909  else
1910  delete textEdit;
1911  }
1912 
1913  OkCancelType rval = kOCTCancel;
1914  switch (dc) {
1915  case 0: rval = kOCTOk; break;
1916  // NOLINTNEXTLINE(bugprone-branch-clone)
1917  case 1: rval = kOCTCancel; break; // "Edit" is done already
1918  case 2: rval = kOCTCancel; break;
1919  case 3: rval = kOCTCancelAll; break;
1920  }
1921  return rval;
1922 }
1923 
1925  MythMainWindow *parent, const QString& title,
1926  const QString& message, QString &text)
1927 {
1928  int dc = -1;
1929  MythScreenStack *popupStack = parent->GetStack("popup stack");
1930  auto *popup = new MythDialogBox(title, message, popupStack,
1931  "resolvechannelpopup");
1932 
1933  if (popup->Create())
1934  {
1935  popup->AddButton(QCoreApplication::translate("(Common)", "OK"));
1936  popup->AddButton(QCoreApplication::translate("(Common)", "OK All"));
1937  popup->AddButton(tr("Edit"));
1938  popup->AddButton(QCoreApplication::translate("(Common)", "Cancel"));
1939  popup->AddButton(QCoreApplication::translate("(Common)", "Cancel All"));
1940  QObject::connect(popup, &MythDialogBox::Closed,
1941  [&](const QString & /*resultId*/, int result)
1942  {
1943  dc = result;
1944  m_eventLoop.quit();
1945  });
1946  popupStack->AddScreen(popup);
1947  m_eventLoop.exec();
1948  }
1949  else
1950  {
1951  delete popup;
1952  popup = nullptr;
1953  }
1954 
1955  // Choice "Edit"
1956  if (2 == dc)
1957  {
1958  auto *textEdit =
1959  new MythTextInputDialog(popupStack,
1960  tr("Please enter a unique channel number."),
1961  FilterNone, false, text);
1962  if (textEdit->Create())
1963  {
1964  QObject::connect(textEdit, &MythTextInputDialog::haveResult,
1965  [&](QString result)
1966  {
1967  dc = 0;
1968  text = std::move(result);
1969  });
1970  QObject::connect(textEdit, &MythTextInputDialog::Exiting,
1971  [&]()
1972  {
1973  m_eventLoop.quit();
1974  });
1975 
1976  popupStack->AddScreen(textEdit);
1977  m_eventLoop.exec();
1978  }
1979  else
1980  delete textEdit;
1981  }
1982 
1983  OkCancelType rval = kOCTCancel;
1984  switch (dc) {
1985  case 0: rval = kOCTOk; break;
1986  case 1: rval = kOCTOkAll; break;
1987  // NOLINTNEXTLINE(bugprone-branch-clone)
1988  case 2: rval = kOCTCancel; break; // "Edit" is done already
1989  case 3: rval = kOCTCancel; break;
1990  case 4: rval = kOCTCancelAll; break;
1991  }
1992  return rval;
1993 }
1994 
1996  const ScanDTVTransport &transport,
1997  ChannelInsertInfo &chan)
1998 {
1999  QString msg = tr("Channel %1 has channel number %2 but that is already in use.")
2000  .arg(SimpleFormatChannel(transport, chan))
2001  .arg(chan.m_chanNum);
2002 
2003  OkCancelType ret = kOCTCancel;
2004 
2005  if (m_useGui)
2006  {
2007  while (true)
2008  {
2009  QString msg2 = msg;
2010  msg2 += "\n";
2011  msg2 += tr("Please enter a unique channel number.");
2012 
2013  QString val = ComputeSuggestedChannelNum(chan);
2014  msg2 += "\n";
2015  msg2 += tr("Default value is %1.").arg(val);
2016  ret = ShowResolveChannelPopup(
2017  GetMythMainWindow(), tr("Channel Importer"),
2018  msg2, val);
2019 
2020  if (kOCTOk != ret && kOCTOkAll != ret)
2021  break; // user canceled..
2022 
2023  bool ok = CheckChannelNumber(val, chan);
2024  if (ok)
2025  {
2026  chan.m_chanNum = val;
2027  break;
2028  }
2029  }
2030  }
2031  else if (m_isInteractive)
2032  {
2033  cout << msg.toLatin1().constData() << endl;
2034 
2035  QString cancelStr = QCoreApplication::translate("(Common)",
2036  "Cancel").toLower();
2037  QString cancelAllStr = QCoreApplication::translate("(Common)",
2038  "Cancel All").toLower();
2039  QString msg2 = tr("Please enter a non-conflicting channel number "
2040  "(or type '%1' to skip, '%2' to skip all):")
2041  .arg(cancelStr).arg(cancelAllStr);
2042 
2043  while (true)
2044  {
2045  cout << msg2.toLatin1().constData() << endl;
2046  string sret;
2047  cin >> sret;
2048  QString val = QString(sret.c_str());
2049  if (val.toLower() == cancelStr)
2050  {
2051  ret = kOCTCancel;
2052  break; // user canceled..
2053  }
2054  if (val.toLower() == cancelAllStr)
2055  {
2056  ret = kOCTCancelAll;
2057  break; // user canceled..
2058  }
2059 
2060  bool ok = CheckChannelNumber(val, chan);
2061  if (ok)
2062  {
2063  chan.m_chanNum = val;
2064  ret = kOCTOk;
2065  break;
2066  }
2067  }
2068  }
2069 
2070  return ret;
2071 }
2072 
2074  const ScanDTVTransport &transport,
2075  ChannelInsertInfo &chan)
2076 {
2077  QString msg = tr("You chose to manually insert channel %1.")
2078  .arg(SimpleFormatChannel(transport, chan));
2079 
2080  OkCancelType ret = kOCTCancel;
2081 
2082  if (m_useGui)
2083  {
2084  while (true)
2085  {
2086  QString msg2 = msg;
2087  msg2 += " ";
2088  msg2 += tr("Please enter a unique channel number.");
2089 
2090  QString val = ComputeSuggestedChannelNum(chan);
2091  msg2 += " ";
2092  msg2 += tr("Default value is %1").arg(val);
2093  ret = ShowManualChannelPopup(
2094  GetMythMainWindow(), tr("Channel Importer"),
2095  msg2, val);
2096 
2097  if (kOCTOk != ret)
2098  break; // user canceled..
2099 
2100  bool ok = CheckChannelNumber(val, chan);
2101  if (ok)
2102  {
2103  chan.m_chanNum = val;
2104  ret = kOCTOk;
2105  break;
2106  }
2107  }
2108  }
2109  else if (m_isInteractive)
2110  {
2111  cout << msg.toLatin1().constData() << endl;
2112 
2113  QString cancelStr = QCoreApplication::translate("(Common)", "Cancel").toLower();
2114  QString cancelAllStr = QCoreApplication::translate("(Common)", "Cancel All").toLower();
2115 
2116  //: %1 is the translation of "Cancel", %2 of "Cancel All"
2117  QString msg2 = tr("Please enter a non-conflicting channel number "
2118  "(or type '%1' to skip, '%2' to skip all): ")
2119  .arg(cancelStr).arg(cancelAllStr);
2120 
2121  while (true)
2122  {
2123  cout << msg2.toLatin1().constData() << endl;
2124  string sret;
2125  cin >> sret;
2126  QString val = QString(sret.c_str());
2127  if (val.toLower() == cancelStr)
2128  {
2129  ret = kOCTCancel;
2130  break; // user canceled..
2131  }
2132  if (val.toLower() == cancelAllStr)
2133  {
2134  ret = kOCTCancelAll;
2135  break; // user canceled..
2136  }
2137 
2138  bool ok = CheckChannelNumber(val, chan);
2139  if (ok)
2140  {
2141  chan.m_chanNum = val;
2142  ret = kOCTOk;
2143  break;
2144  }
2145  }
2146  }
2147 
2148  return ret;
2149 }
2150 
2151 // ChannelImporter::CheckChannelNumber
2152 //
2153 // Check validity of a new channel number.
2154 // The channel number is not a number but it is a string that starts with a digit.
2155 // The channel number should not yet exist in this video source.
2156 //
2158  const QString &num,
2159  const ChannelInsertInfo &chan)
2160 {
2161  bool ok = (num.length() >= 1);
2162  ok = ok && ((num[0] >= '0') && (num[0] <= '9'));
2163  ok = ok && !ChannelUtil::IsConflicting(
2164  num, chan.m_sourceId, chan.m_channelId);
2165  return ok;
2166 }
static QString toString(ChannelType type)
static QString FormatTransport(const ScanDTVTransport &transport)
static QString GetSummary(uint transport_count, const ChannelImporterBasicStats &info, const ChannelImporterUniquenessStats &stats)
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:783
QMap< uint, uint > m_atscNumCnt
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:864
QString m_defaultAuthority
Definition: channelinfo.h:233
static QString ComputeSuggestedChannelNum(const ChannelInsertInfo &chan)
Compute a suggested channel number based on various aspects of the channel information.
uint SaveScan(const ScanDTVTransportList &scan)
Definition: scaninfo.cpp:22
QMap< QString, uint > m_chanNumCnt
static void CleanupDuplicates(ScanDTVTransportList &transports)
static QString map_str(QString str)
MythConfirmationDialog * ShowOkPopup(const QString &message, QObject *parent, const char *slot, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
QString toString(MarkTypes type)
static const int kTunerTypeATSC
ChannelVisibleType m_visible
Definition: channelinfo.h:228
static bool IsType(const ChannelImporterBasicStats &info, const ChannelInsertInfo &chan, ChannelType type)
static bool CheckChannelNumber(const QString &num, const ChannelInsertInfo &chan)
OkCancelType ShowResolveChannelPopup(MythMainWindow *parent, const QString &title, const QString &message, QString &text)
DeleteAction QueryUserDelete(const QString &msg)
For multiple channels.
static QString FormatChannels(const ScanDTVTransportList &transports, const ChannelImporterBasicStats *info=nullptr)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
static QString FormatChannel(const ScanDTVTransport &transport, const ChannelInsertInfo &chan, const ChannelImporterBasicStats *info=nullptr)
Basic menu dialog, message and a list of options.
UpdateAction QueryUserUpdate(const QString &msg)
For multiple channels.
int size(void) const
Definition: mythdbcon.h:203
static int GetBetterMplexID(int current_mplexid, int transport_id, int network_id)
Returns best match multiplex ID, creating one if needed.
MythScreenStack * GetStack(const QString &stackname)
void FilterServices(ScanDTVTransportList &transports) const
static bool SetVisible(uint channel_id, ChannelVisibleType visible)
static guint32 * tmp
Definition: goom_core.c:35
static const int kTunerTypeDVBS1
QString toString() const
static void FixUpOpenCable(ScanDTVTransportList &transports)
static bool SetChannelValue(const QString &field_name, const QString &value, uint sourceid, const QString &channum)
#define LOC
ChannelInsertInfoList m_channels
Definition: dtvmultiplex.h:138
static QString SimpleFormatChannel(const ScanDTVTransport &transport, const ChannelInsertInfo &chan)
Format channel information into a simple string.
QVariant value(int i) const
Definition: mythdbcon.h:198
QMap< uint, uint > m_progNumCnt
ScanDTVTransportList GetDBTransports(uint sourceid, ScanDTVTransportList &transports) const
Adds found channel info to transports list, returns channels in DB which were not found in scan in an...
OkCancelType ShowManualChannelPopup(MythMainWindow *parent, const QString &title, const QString &message, QString &text)
static int SimpleCountChannels(const ScanDTVTransportList &transports)
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
static const int kTunerTypeDVBS2
DTVModulation m_modulation
Definition: dtvmultiplex.h:100
void haveResult(QString)
vector< ChannelInsertInfo > ChannelInsertInfoList
Definition: channelinfo.h:256
static bool CreateChannel(uint db_mplexid, uint db_sourceid, uint new_channel_id, const QString &callsign, const QString &service_name, const QString &chan_num, uint service_id, uint atsc_major_channel, uint atsc_minor_channel, bool use_on_air_guide, ChannelVisibleType visible, const QString &freqid, const QString &icon=QString(), QString format="Default", const QString &xmltvid=QString(), const QString &default_authority=QString(), uint service_type=0)
static void AddChanToCopy(ScanDTVTransport &transport_copy, const ScanDTVTransport &transport, const ChannelInsertInfo &chan)
#define VERBOSE_LEVEL_CHECK(_MASK_, _LEVEL_)
Definition: mythlogging.h:24
QMap< uint, uint > m_atscMinCnt
static bool IsConflicting(const QString &channum, uint sourceid=0, uint excluded_chanid=0)
Definition: channelutil.h:286
static bool UpdateChannel(uint db_mplexid, uint source_id, uint channel_id, const QString &callsign, const QString &service_name, const QString &chan_num, uint service_id, uint atsc_major_channel, uint atsc_minor_channel, bool use_on_air_guide, ChannelVisibleType visible, const QString &freqid=QString(), const QString &icon=QString(), QString format=QString(), const QString &xmltvid=QString(), const QString &default_authority=QString(), uint service_type=0)
bool IsSameChannel(const ChannelInsertInfo &other, int relaxed=0) const
QString toString() const
InsertAction QueryUserInsert(const QString &msg)
For multiple channels.
static void CountChannels(const ScanDTVTransportList &transports, const ChannelImporterBasicStats &info, ChannelType type, uint &new_chan, uint &old_chan)
unsigned int uint
Definition: compat.h:140
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:535
static bool CreateIPTVTuningData(uint channel_id, const IPTVTuningData &tuning)
Definition: channelutil.h:144
static int CreateChanID(uint sourceid, const QString &chan_num)
Creates a unique channel ID for database use.
MythMainWindow * GetMythMainWindow(void)
uint DeleteChannels(ScanDTVTransportList &transports)
static bool MarkProcessed(uint scanid)
Definition: scaninfo.cpp:195
ScanDTVTransportList UpdateChannels(const ScanDTVTransportList &transports, const ChannelImporterBasicStats &info, UpdateAction action, ChannelType type, ScanDTVTransportList &updated, ScanDTVTransportList &skipped)
static void UpdateInsertInfoFromDB(ChannelInsertInfo &chan)
Dialog prompting the user to enter a text string.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:808
static ChannelImporterBasicStats CollectStats(const ScanDTVTransportList &transports)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
QString m_serviceName
Definition: channelinfo.h:219
OkCancelType
static ChannelImporterUniquenessStats CollectUniquenessStats(const ScanDTVTransportList &transports, const ChannelImporterBasicStats &info)
void InsertChannels(const ScanDTVTransportList &transports, const ChannelImporterBasicStats &info)
bool FillFromDB(DTVTunerType type, uint mplexid) override
void Process(const ScanDTVTransportList &_transports, int sourceid=-1)
QString m_siStandard
Definition: channelinfo.h:242
static void UpdateChannelNumberFromDB(ChannelInsertInfo &chan)
static QString FormatTransports(const ScanDTVTransportList &transports_in)
QMap< uint, uint > m_atscMajCnt
OkCancelType QueryUserResolve(const ScanDTVTransport &transport, ChannelInsertInfo &chan)
For a single channel.
vector< ScanDTVTransport > ScanDTVTransportList
Definition: dtvmultiplex.h:140
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
static uint CreateMultiplex(int sourceid, const QString &sistandard, uint64_t frequency, const QString &modulation, int transport_id=-1, int network_id=-1)
static uint GetChannelCount(int sourceid=-1)
static QString GetChanNum(int chan_id)
Returns the channel-number string of the given channel.
void Closed(QString, int)
uint DeleteUnusedTransports(uint sourceid)
static bool DeleteChannel(uint channel_id)
uint64_t m_frequency
Definition: dtvmultiplex.h:94