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_use_gui)
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_service_requirements & kRequireAV) == kRequireAV;
73  bool require_a = (m_service_requirements & kRequireAudio) != 0;
74  cout << "Desired Services : " << (require_av ? "tv" : require_a ? "tv+radio" : "all") << endl;
75  cout << "Unencrypted Only : " << (m_fta_only ? "yes" : "no") << endl;
76  cout << "Logical Channel Numbers only: " << (m_lcn_only ? "yes" : "no") << endl;
77  cout << "Complete scan data required : " << (m_complete_only ? "yes" : "no") << endl;
78  cout << "Full search for old channels: " << (m_full_channel_search ? "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_do_save)
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_source_id;
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_do_delete)
129  {
130  ScanDTVTransportList trans = transports;
131  for (size_t i = 0; i < db_trans.size(); ++i)
132  trans.push_back(db_trans[i]);
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_do_insert)
155  InsertChannels(transports, info);
156 
157  if (m_do_delete && sourceid)
158  DeleteUnusedTransports(sourceid);
159 
160  if (m_do_delete || m_do_insert)
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_db_mplexid && chan.m_channel_id;
200  if (!was_in_db)
201  continue;
202 
203  if (!chan.m_in_pmt)
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 (size_t k = 0; k < off_air_list.size(); ++k)
232  {
233  int i = off_air_list[k] >> 16, j = off_air_list[k] & 0xFFFF;
235  transports[i].m_channels[j].m_channel_id);
236  deleted[off_air_list[k]] = true;
237  }
238  }
239  else if (kDeleteInvisibleAll == action)
240  {
241  for (size_t k = 0; k < off_air_list.size(); ++k)
242  {
243  int i = off_air_list[k] >> 16, j = off_air_list[k] & 0xFFFF;
244  int chanid = transports[i].m_channels[j].m_channel_id;
245  QString channum = ChannelUtil::GetChanNum(chanid);
246  ChannelUtil::SetVisible(chanid, false);
247  ChannelUtil::SetChannelValue("channum", QString("_%1").arg(channum),
248  chanid);
249  }
250  }
251  else
252  {
253  // TODO manual delete
254  }
255 
256  // TODO delete encrypted channels when m_fta_only set
257 
258  if (deleted.empty())
259  return 0;
260 
261  // Create a new transports list without the deleted channels
262  ScanDTVTransportList newlist;
263  for (size_t i = 0; i < transports.size(); ++i)
264  {
265  newlist.push_back(transports[i]);
266  newlist.back().m_channels.clear();
267  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
268  {
269  if (!deleted.contains(i<<16|j))
270  {
271  newlist.back().m_channels.push_back(
272  transports[i].m_channels[j]);
273  }
274  }
275  }
276 
277  transports = newlist;
278  return deleted.size();
279 }
280 
282 {
283  MSqlQuery query(MSqlQuery::InitCon());
284  query.prepare(
285  "SELECT mplexid FROM dtv_multiplex "
286  "WHERE sourceid = :SOURCEID1 AND "
287  " mplexid NOT IN "
288  " (SELECT mplexid "
289  " FROM channel "
290  " WHERE sourceid = :SOURCEID2)");
291  query.bindValue(":SOURCEID1", sourceid);
292  query.bindValue(":SOURCEID2", sourceid);
293  if (!query.exec())
294  {
295  MythDB::DBError("DeleteUnusedTransports() -- select", query);
296  return 0;
297  }
298 
299  QString msg = tr("Found %n unused transport(s).", "", query.size());
300 
301  LOG(VB_GENERAL, LOG_INFO, LOC + msg);
302 
303  if (query.size() == 0)
304  return 0;
305 
306  DeleteAction action = QueryUserDelete(msg);
307  if (kDeleteIgnoreAll == action)
308  return 0;
309 
310  if (kDeleteAll == action)
311  {
312  query.prepare(
313  "DELETE FROM dtv_multiplex "
314  "WHERE sourceid = :SOURCEID1 AND "
315  " mplexid NOT IN "
316  " (SELECT mplexid "
317  " FROM channel "
318  " WHERE sourceid = :SOURCEID2)");
319  query.bindValue(":SOURCEID1", sourceid);
320  query.bindValue(":SOURCEID2", sourceid);
321  if (!query.exec())
322  {
323  MythDB::DBError("DeleteUnusedTransports() -- delete", query);
324  return 0;
325  }
326  }
327  else
328  {
329  // TODO manual delete
330  LOG(VB_GENERAL, LOG_INFO, LOC + "Manual delete of transport not implemented");
331  }
332  return 0;
333 }
334 
336  const ScanDTVTransportList &transports,
337  const ChannelImporterBasicStats &info)
338 {
339  ScanDTVTransportList list = transports;
340  ScanDTVTransportList inserted;
341  ScanDTVTransportList updated;
342  ScanDTVTransportList skipped_inserts;
343  ScanDTVTransportList skipped_updates;
344 
345  // Insert or update all channels with non-conflicting channum
346  // and complete tuning information.
347  uint chantype = (uint) kChannelTypeNonConflictingFirst;
348  for (; chantype <= (uint) kChannelTypeNonConflictingLast; ++chantype)
349  {
350  auto type = (ChannelType) chantype;
351  uint new_chan = 0;
352  uint old_chan = 0;
353  CountChannels(list, info, type, new_chan, old_chan);
354 
355  if (kNTSCNonConflicting == type)
356  continue;
357 
358  if (old_chan)
359  {
360  //: %n is the number of channels, %1 is the type of channel
361  QString msg = tr("Found %n old %1 channel(s).", "", old_chan)
362  .arg(toString(type));
363 
364  UpdateAction action = QueryUserUpdate(msg);
365  list = UpdateChannels(list, info, action, type, updated, skipped_updates);
366  }
367  if (new_chan)
368  {
369  //: %n is the number of channels, %1 is the type of channel
370  QString msg = tr("Found %n new %1 channel(s).", "", new_chan)
371  .arg(toString(type));
372 
373  InsertAction action = QueryUserInsert(msg);
374  list = InsertChannels(list, info, action, type, inserted, skipped_inserts);
375  }
376  }
377 
378  if (!m_is_interactive)
379  return;
380 
381  // If any of the potential uniques is high and inserting
382  // with those as the channum would result in few conflicts
383  // ask user if it is ok to to proceed using it as the channum
384 
385  // For remaining channels with complete tuning information
386  // insert channels with contiguous list of numbers as the channums
387  chantype = (uint) kChannelTypeConflictingFirst;
388  for (; chantype <= (uint) kChannelTypeConflictingLast; ++chantype)
389  {
390  auto type = (ChannelType) chantype;
391  uint new_chan = 0;
392  uint old_chan = 0;
393  CountChannels(list, info, type, new_chan, old_chan);
394 
395  if (old_chan)
396  {
397  //: %n is the number of channels, %1 is the type of channel
398  QString msg = tr("Found %n conflicting old %1 channel(s).",
399  "", old_chan).arg(toString(type));
400 
401  UpdateAction action = QueryUserUpdate(msg);
402  list = UpdateChannels(list, info, action, type, updated, skipped_updates);
403  }
404  if (new_chan)
405  {
406  //: %n is the number of channels, %1 is the type of channel
407  QString msg = tr("Found %n new conflicting %1 channel(s).",
408  "", new_chan).arg(toString(type));
409 
410  InsertAction action = QueryUserInsert(msg);
411  list = InsertChannels(list, info, action, type, inserted, skipped_inserts);
412  }
413  }
414 
415  // List what has been done with each channel
416  if (!updated.empty())
417  {
418  cout << endl << "Updated old channels (" << SimpleCountChannels(updated) << "):" << endl;
419  cout << FormatChannels(updated).toLatin1().constData() << endl;
420  }
421  if (!skipped_updates.empty())
422  {
423  cout << endl << "Skipped old channels (" << SimpleCountChannels(skipped_updates) << "):" << endl;
424  cout << FormatChannels(skipped_updates).toLatin1().constData() << endl;
425  }
426  if (!inserted.empty())
427  {
428  cout << endl << "Inserted new channels (" << SimpleCountChannels(inserted) << "):" << endl;
429  cout << FormatChannels(inserted).toLatin1().constData() << endl;
430  }
431  if (!skipped_inserts.empty())
432  {
433  cout << endl << "Skipped new channels (" << SimpleCountChannels(skipped_inserts) << "):" << endl;
434  cout << FormatChannels(skipped_inserts).toLatin1().constData() << endl;
435  }
436 
437  // Remaining channels and sum uniques again
438  if (!list.empty())
439  {
440  ChannelImporterBasicStats ninfo = CollectStats(list);
441  ChannelImporterUniquenessStats nstats = CollectUniquenessStats(list, ninfo);
442  cout << "Remaining channels (" << SimpleCountChannels(list) << "):" << endl;
443  cout << FormatChannels(list).toLatin1().constData() << endl;
444  cout << endl;
445  cout << GetSummary(list.size(), ninfo, nstats).toLatin1().constData();
446  cout << endl;
447  }
448 }
449 
450 // ChannelImporter::InsertChannels
451 //
452 // transports List of channels to update
453 // info Channel statistics
454 // action Insert all, Insert manually, Ignore all
455 // type Channel type such as dvb or atsc
456 // inserted_list List of inserted channels
457 // skipped_list List of skipped channels
458 //
459 // return: List of transports/channels that have not been inserted
460 //
462  const ScanDTVTransportList &transports,
463  const ChannelImporterBasicStats &info,
466  ScanDTVTransportList &inserted_list,
467  ScanDTVTransportList &skipped_list)
468 {
469  QString channelFormat = "%1_%2";
470 
471  ScanDTVTransportList next_list;
472 
473  bool cancel_all = false;
474  bool ok_all = false;
475 
476  // Insert all channels with non-conflicting channum
477  // and complete tuning information.
478  for (size_t i = 0; i < transports.size(); ++i)
479  {
480  ScanDTVTransport new_transport;
481  ScanDTVTransport inserted_transport;
482  ScanDTVTransport skipped_transport;
483 
484  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
485  {
486  ChannelInsertInfo chan = transports[i].m_channels[j];
487 
488  bool asked = false;
489  bool filter = false;
490  bool handle = false;
491  if (!chan.m_channel_id && (kInsertIgnoreAll == action) &&
492  IsType(info, chan, type))
493  {
494  filter = true;
495  }
496  else if (!chan.m_channel_id && IsType(info, chan, type))
497  {
498  handle = true;
499  }
500 
501  if (cancel_all)
502  {
503  handle = false;
504  }
505 
506  if (handle && kInsertManual == action)
507  {
508  OkCancelType rc = QueryUserInsert(transports[i], chan);
509  if (kOCTCancelAll == rc)
510  {
511  cancel_all = true;
512  handle = false;
513  }
514  else if (kOCTCancel == rc)
515  {
516  handle = false;
517  }
518  else if (kOCTOk == rc)
519  {
520  asked = true;
521  }
522  }
523 
524  if (handle)
525  {
526  bool conflicting = false;
527 
528  if (chan.m_chan_num.isEmpty() ||
530  {
531  if ((kATSCNonConflicting == type) ||
532  (kATSCConflicting == type))
533  {
534  chan.m_chan_num = channelFormat
535  .arg(chan.m_atsc_major_channel)
536  .arg(chan.m_atsc_minor_channel);
537  }
538  else if (chan.m_si_standard == "dvb")
539  {
540  chan.m_chan_num = QString("%1")
541  .arg(chan.m_service_id);
542  }
543  else if (chan.m_freqid.isEmpty())
544  {
545  chan.m_chan_num = QString("%1-%2")
546  .arg(chan.m_source_id)
547  .arg(chan.m_service_id);
548  }
549  else
550  {
551  chan.m_chan_num = QString("%1-%2")
552  .arg(chan.m_freqid)
553  .arg(chan.m_service_id);
554  }
555 
556  conflicting = ChannelUtil::IsConflicting(
557  chan.m_chan_num, chan.m_source_id);
558  }
559 
560  // Only ask if not already asked before with kInsertManual
561  if (m_is_interactive && !asked &&
562  (conflicting || (kChannelTypeConflictingFirst <= type)))
563  {
564  bool ok_done = false;
565  if (ok_all)
566  {
567  QString val = ComputeSuggestedChannelNum(chan);
568  bool ok = CheckChannelNumber(val, chan);
569  if (ok)
570  {
571  chan.m_chan_num = val;
572  conflicting = false;
573  ok_done = true;
574  }
575  }
576  if (!ok_done)
577  {
578  OkCancelType rc =
579  QueryUserResolve(transports[i], chan);
580 
581  conflicting = true;
582  if (kOCTCancelAll == rc)
583  {
584  cancel_all = true;
585  }
586  else if (kOCTOk == rc)
587  {
588  conflicting = false;
589  }
590  else if (kOCTOkAll == rc)
591  {
592  conflicting = false;
593  ok_all = true;
594  }
595  }
596  }
597 
598  if (conflicting)
599  {
600  handle = false;
601  }
602  }
603 
604  bool inserted = false;
605  if (handle)
606  {
607  int chanid = ChannelUtil::CreateChanID(
608  chan.m_source_id, chan.m_chan_num);
609 
610  chan.m_channel_id = (chanid > 0) ? chanid : chan.m_channel_id;
611 
612  if (chan.m_channel_id)
613  {
614  uint tsid = chan.m_vct_tsid;
615  tsid = (tsid) ? tsid : chan.m_sdt_tsid;
616  tsid = (tsid) ? tsid : chan.m_pat_tsid;
617  tsid = (tsid) ? tsid : chan.m_vct_chan_tsid;
618 
619  if (!chan.m_db_mplexid)
620  {
622  chan.m_source_id, transports[i], tsid, chan.m_orig_netid);
623  }
624  else
625  {
626  // Find the matching multiplex. This updates the
627  // transport and network ID's in case the transport
628  // was created manually
630  tsid, chan.m_orig_netid);
631  if (id >= 0)
632  chan.m_db_mplexid = id;
633  }
634  }
635 
636  if (chan.m_channel_id && chan.m_db_mplexid)
637  {
638  chan.m_channel_id = chanid;
639 
640  inserted = ChannelUtil::CreateChannel(
641  chan.m_db_mplexid,
642  chan.m_source_id,
643  chan.m_channel_id,
644  chan.m_callsign,
645  chan.m_service_name,
646  chan.m_chan_num,
647  chan.m_service_id,
650  chan.m_use_on_air_guide,
651  chan.m_hidden, chan.m_hidden_in_guide,
652  chan.m_freqid,
653  QString(),
654  chan.m_format,
655  QString(),
656  chan.m_default_authority,
657  chan.m_service_type);
658 
659  if (!transports[i].m_iptvTuning.GetDataURL().isEmpty())
661  transports[i].m_iptvTuning);
662  }
663  }
664 
665  if (inserted)
666  {
667  // Update list of inserted channels
668  AddChanToCopy(inserted_transport, transports[i], chan);
669  }
670 
671  if (filter)
672  {
673  // Update list of skipped channels
674  AddChanToCopy(skipped_transport, transports[i], chan);
675  }
676  else if (!inserted)
677  {
678  // Update list of remaining channels
679  AddChanToCopy(new_transport, transports[i], chan);
680  }
681  }
682 
683  if (!new_transport.m_channels.empty())
684  next_list.push_back(new_transport);
685 
686  if (!skipped_transport.m_channels.empty())
687  skipped_list.push_back(skipped_transport);
688 
689  if (!inserted_transport.m_channels.empty())
690  inserted_list.push_back(inserted_transport);
691  }
692 
693  return next_list;
694 }
695 
696 // ChannelImporter::UpdateChannels
697 //
698 // transports list of transports/channels to update
699 // info Channel statistics
700 // action Update All, Ignore All
701 // type Channel type such as dvb or atsc
702 // inserted List of inserted channels
703 // skipped List of skipped channels
704 //
705 // return: List of transports/channels that have not been updated
706 //
708  const ScanDTVTransportList &transports,
709  const ChannelImporterBasicStats &info,
712  ScanDTVTransportList &updated_list,
713  ScanDTVTransportList &skipped_list)
714 {
715  QString channelFormat = "%1_%2";
716  bool renameChannels = false;
717 
718  ScanDTVTransportList next_list;
719 
720  // update all channels with non-conflicting channum
721  // and complete tuning information.
722  for (size_t i = 0; i < transports.size(); ++i)
723  {
724  ScanDTVTransport new_transport;
725  ScanDTVTransport updated_transport;
726  ScanDTVTransport skipped_transport;
727 
728  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
729  {
730  ChannelInsertInfo chan = transports[i].m_channels[j];
731 
732  bool filter = false;
733  bool handle = false;
734  if (chan.m_channel_id && (kUpdateIgnoreAll == action) &&
735  IsType(info, chan, type))
736  {
737  filter = true;
738  }
739  else if (chan.m_channel_id && IsType(info, chan, type))
740  {
741  handle = true;
742  }
743 
744  if (handle)
745  {
746  bool conflicting = false;
747 
748  if (m_keep_channel_numbers)
749  {
751  }
752  if (chan.m_chan_num.isEmpty() || renameChannels ||
754  chan.m_chan_num, chan.m_source_id, chan.m_channel_id))
755  {
756  if (kATSCNonConflicting == type)
757  {
758  chan.m_chan_num = channelFormat
759  .arg(chan.m_atsc_major_channel)
760  .arg(chan.m_atsc_minor_channel);
761  }
762  else if (chan.m_si_standard == "dvb")
763  {
764  chan.m_chan_num = QString("%1")
765  .arg(chan.m_service_id);
766  }
767  else if (chan.m_freqid.isEmpty())
768  {
769  chan.m_chan_num = QString("%1-%2")
770  .arg(chan.m_source_id)
771  .arg(chan.m_service_id);
772  }
773  else
774  {
775  chan.m_chan_num = QString("%1-%2")
776  .arg(chan.m_freqid)
777  .arg(chan.m_service_id);
778  }
779 
780  conflicting = ChannelUtil::IsConflicting(
781  chan.m_chan_num, chan.m_source_id, chan.m_channel_id);
782  }
783 
784  if (conflicting)
785  {
786  handle = false;
787 
788  // Update list of skipped channels
789  AddChanToCopy(skipped_transport, transports[i], 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_vct_tsid;
802  tsid = (tsid) ? tsid : chan.m_sdt_tsid;
803  tsid = (tsid) ? tsid : chan.m_pat_tsid;
804  tsid = (tsid) ? tsid : chan.m_vct_chan_tsid;
806  tsid, chan.m_orig_netid);
807  if (id >= 0)
808  chan.m_db_mplexid = id;
809 
810  updated = ChannelUtil::UpdateChannel(
811  chan.m_db_mplexid,
812  chan.m_source_id,
813  chan.m_channel_id,
814  chan.m_callsign,
815  chan.m_service_name,
816  chan.m_chan_num,
817  chan.m_service_id,
820  chan.m_use_on_air_guide,
821  chan.m_hidden, chan.m_hidden_in_guide,
822  chan.m_freqid,
823  QString(),
824  chan.m_format,
825  QString(),
826  chan.m_default_authority,
827  chan.m_service_type);
828  }
829 
830  if (updated)
831  {
832  // Update list of updated channels
833  AddChanToCopy(updated_transport, transports[i], chan);
834  }
835 
836  if (filter)
837  {
838  // Update list of skipped channels
839  AddChanToCopy(skipped_transport, transports[i], chan);
840  }
841  else if (!updated)
842  {
843  // Update list of remaining channels
844  AddChanToCopy(new_transport, transports[i], chan);
845  }
846  }
847 
848  if (!new_transport.m_channels.empty())
849  next_list.push_back(new_transport);
850 
851  if (!skipped_transport.m_channels.empty())
852  skipped_list.push_back(skipped_transport);
853 
854  if (!updated_transport.m_channels.empty())
855  updated_list.push_back(updated_transport);
856  }
857 
858  return next_list;
859 }
860 
861 // ChannelImporter::AddChanToCopy
862 //
863 // Add channel to copy of transport.
864 // This is used to keep track of what is done with each channel
865 //
866 // transport_copy with zero to all channels of transport
867 // transport transport with channel info as scanned
868 // chan one channel of transport, to be copied
869 //
871  ScanDTVTransport &transport_copy,
872  const ScanDTVTransport &transport,
873  const ChannelInsertInfo &chan
874 )
875 {
876  if (transport_copy.m_channels.empty())
877  {
878  transport_copy = transport;
879  transport_copy.m_channels.clear();
880  }
881  transport_copy.m_channels.push_back(chan);
882 }
883 
885 {
886  ScanDTVTransportList no_dups;
887 
889  if (!transports.empty())
890  tuner_type = transports[0].m_tuner_type;
891 
892  bool is_dvbs = ((DTVTunerType::kTunerTypeDVBS1 == tuner_type) ||
893  (DTVTunerType::kTunerTypeDVBS2 == tuner_type));
894 
895  uint freq_mult = (is_dvbs) ? 1 : 1000;
896 
897  vector<bool> ignore;
898  ignore.resize(transports.size());
899  for (size_t i = 0; i < transports.size(); ++i)
900  {
901  if (ignore[i])
902  continue;
903 
904  for (size_t j = i+1; j < transports.size(); ++j)
905  {
906  if (!transports[i].IsEqual(
907  tuner_type, transports[j], 500 * freq_mult))
908  {
909  continue;
910  }
911 
912  for (size_t k = 0; k < transports[j].m_channels.size(); ++k)
913  {
914  bool found_same = false;
915  for (size_t l = 0; l < transports[i].m_channels.size(); ++l)
916  {
917  if (transports[j].m_channels[k].IsSameChannel(
918  transports[i].m_channels[l]))
919  {
920  found_same = true;
921  transports[i].m_channels[l].ImportExtraInfo(
922  transports[j].m_channels[k]);
923  }
924  }
925  if (!found_same)
926  transports[i].m_channels.push_back(transports[j].m_channels[k]);
927  }
928  ignore[j] = true;
929  }
930  no_dups.push_back(transports[i]);
931  }
932 
933  transports = no_dups;
934 }
935 
937 {
938  bool require_av = (m_service_requirements & kRequireAV) == kRequireAV;
939  bool require_a = (m_service_requirements & kRequireAudio) != 0;
940 
941  for (size_t i = 0; i < transports.size(); ++i)
942  {
943  ChannelInsertInfoList filtered;
944  for (size_t k = 0; k < transports[i].m_channels.size(); ++k)
945  {
946  if (m_fta_only && transports[i].m_channels[k].m_is_encrypted &&
947  transports[i].m_channels[k].m_decryption_status != kEncDecrypted)
948  continue;
949 
950  if (require_a && transports[i].m_channels[k].m_is_data_service)
951  continue;
952 
953  if (require_av && transports[i].m_channels[k].m_is_audio_service)
954  continue;
955 
956  // Filter channels out that do not have a logical channel number
957  if (m_lcn_only && transports[i].m_channels[k].m_chan_num.isEmpty())
958  {
959  QString msg = FormatChannel(transports[i], transports[i].m_channels[k]);
960  LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("No LCN: %1").arg(msg));
961  continue;
962  }
963 
964  // Filter channels out that are not present in PAT and PMT.
965  if (m_complete_only &&
966  !(transports[i].m_channels[k].m_in_pat &&
967  transports[i].m_channels[k].m_in_pmt ))
968  {
969  QString msg = FormatChannel(transports[i], transports[i].m_channels[k]);
970  LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("Not in PAT/PMT: %1").arg(msg));
971  continue;
972  }
973 
974  // Filter channels out that are not present in SDT and that are not ATSC
975  if (m_complete_only &&
976  transports[i].m_channels[k].m_atsc_major_channel == 0 &&
977  transports[i].m_channels[k].m_atsc_minor_channel == 0 &&
978  !(transports[i].m_channels[k].m_in_pat &&
979  transports[i].m_channels[k].m_in_pmt &&
980  transports[i].m_channels[k].m_in_sdt &&
981  (transports[i].m_channels[k].m_pat_tsid ==
982  transports[i].m_channels[k].m_sdt_tsid)))
983  {
984  QString msg = FormatChannel(transports[i], transports[i].m_channels[k]);
985  LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("Not in PAT/PMT/SDT: %1").arg(msg));
986  continue;
987  }
988 
989  // Filter channels out that do not have a name
990  if (m_complete_only && transports[i].m_channels[k].m_service_name.isEmpty())
991  {
992  QString msg = FormatChannel(transports[i], transports[i].m_channels[k]);
993  LOG(VB_CHANSCAN, LOG_INFO, LOC + QString("No name: %1").arg(msg));
994  continue;
995  }
996 
997  // Filter channels out only in channels.conf, i.e. not found
998  if (transports[i].m_channels[k].m_in_channels_conf &&
999  !(transports[i].m_channels[k].m_in_pat ||
1000  transports[i].m_channels[k].m_in_pmt ||
1001  transports[i].m_channels[k].m_in_vct ||
1002  transports[i].m_channels[k].m_in_nit ||
1003  transports[i].m_channels[k].m_in_sdt))
1004  continue;
1005 
1006  filtered.push_back(transports[i].m_channels[k]);
1007  }
1008  transports[i].m_channels = filtered;
1009  }
1010 }
1011 
1020  uint sourceid, ScanDTVTransportList &transports) const
1021 {
1022  ScanDTVTransportList not_in_scan;
1023  int found_in_same_transport = 0;
1024  int found_in_other_transport = 0;
1025  int found_nowhere = 0;
1026 
1028  if (!transports.empty())
1029  tuner_type = transports[0].m_tuner_type;
1030 
1031  bool is_dvbs =
1032  (DTVTunerType::kTunerTypeDVBS1 == tuner_type) ||
1033  (DTVTunerType::kTunerTypeDVBS2 == tuner_type);
1034 
1035  uint freq_mult = (is_dvbs) ? 1 : 1000;
1036 
1037  MSqlQuery query(MSqlQuery::InitCon());
1038  query.prepare(
1039  "SELECT mplexid "
1040  "FROM dtv_multiplex "
1041  "WHERE sourceid = :SOURCEID "
1042  "GROUP BY mplexid "
1043  "ORDER BY mplexid");
1044  query.bindValue(":SOURCEID", sourceid);
1045 
1046  if (!query.exec())
1047  {
1048  MythDB::DBError("GetDBTransports()", query);
1049  return not_in_scan;
1050  }
1051 
1052  while (query.next())
1053  {
1054  ScanDTVTransport db_transport;
1055  uint mplexid = query.value(0).toUInt();
1056  if (db_transport.FillFromDB(tuner_type, mplexid))
1057  {
1058  if (db_transport.m_channels.empty())
1059  {
1060  continue;
1061  }
1062  }
1063 
1064  bool found_transport = false;
1065  QMap<uint,bool> found_chan;
1066 
1067  // Search for old channels in the same transport of the scan.
1068  for (size_t ist = 0; ist < transports.size(); ++ist) // All transports in scan
1069  {
1070  ScanDTVTransport &transport = transports[ist]; // Scanned transport
1071 
1072  if (transport.IsEqual(tuner_type, db_transport, 500 * freq_mult, true)) // Same transport?
1073  {
1074  found_transport = true;
1075  transport.m_mplex = db_transport.m_mplex; // Found multiplex
1076 
1077  for (size_t jdc = 0; jdc < db_transport.m_channels.size(); ++jdc) // All channels in database transport
1078  {
1079  if (!found_chan[jdc]) // Channel not found yet?
1080  {
1081  ChannelInsertInfo &db_chan = db_transport.m_channels[jdc]; // Channel in database transport
1082 
1083  for (size_t ktc = 0; ktc < transport.m_channels.size(); ++ktc) // All channels in scanned transport
1084  {
1085  ChannelInsertInfo &chan = transport.m_channels[ktc]; // 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_db_mplexid = mplexid; // Found multiplex
1091  chan.m_channel_id = db_chan.m_channel_id; // 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_full_channel_search)
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 (size_t ksc = 0; ksc < transport.m_channels.size(); ++ksc) // All channels in scanned transport
1113  {
1114  ChannelInsertInfo &chan = transport.m_channels[ksc]; // 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_channel_id = db_chan.m_channel_id; // 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 (size_t i = 0; i < transports.size(); ++i)
1165  {
1166  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
1167  {
1168  ChannelInsertInfo &chan = transports[i].m_channels[j];
1169  if (((chan.m_could_be_opencable && (chan.m_si_standard == "mpeg")) ||
1170  chan.m_is_opencable) && !chan.m_in_vct)
1171  {
1172  chan.m_si_standard = "opencable";
1173  }
1174  }
1175  }
1176 }
1177 
1179  const ScanDTVTransportList &transports)
1180 {
1182  for (size_t i = 0; i < transports.size(); ++i)
1183  {
1184  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
1185  {
1186  const ChannelInsertInfo &chan = transports[i].m_channels[j];
1187  int enc = (chan.m_is_encrypted) ?
1188  ((chan.m_decryption_status == kEncDecrypted) ? 2 : 1) : 0;
1189  info.m_atsc_channels[enc] += (chan.m_si_standard == "atsc");
1190  info.m_dvb_channels [enc] += (chan.m_si_standard == "dvb");
1191  info.m_mpeg_channels[enc] += (chan.m_si_standard == "mpeg");
1192  info.m_scte_channels[enc] += (chan.m_si_standard == "opencable");
1193  info.m_ntsc_channels[enc] += (chan.m_si_standard == "ntsc");
1194  if (chan.m_si_standard != "ntsc")
1195  {
1196  ++info.m_prognum_cnt[chan.m_service_id];
1197  ++info.m_channum_cnt[map_str(chan.m_chan_num)];
1198  }
1199  if (chan.m_si_standard == "atsc")
1200  {
1201  ++info.m_atscnum_cnt[(chan.m_atsc_major_channel << 16) |
1202  (chan.m_atsc_minor_channel)];
1203  ++info.m_atscmin_cnt[chan.m_atsc_minor_channel];
1204  ++info.m_atscmaj_cnt[chan.m_atsc_major_channel];
1205  }
1206  if (chan.m_si_standard == "ntsc")
1207  {
1208  ++info.m_atscnum_cnt[(chan.m_atsc_major_channel << 16) |
1209  (chan.m_atsc_minor_channel)];
1210  }
1211  }
1212  }
1213 
1214  return info;
1215 }
1216 
1218  const ScanDTVTransportList &transports,
1219  const ChannelImporterBasicStats &info)
1220 {
1222 
1223  for (size_t i = 0; i < transports.size(); ++i)
1224  {
1225  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
1226  {
1227  const ChannelInsertInfo &chan = transports[i].m_channels[j];
1228  stats.m_unique_prognum +=
1229  (info.m_prognum_cnt[chan.m_service_id] == 1) ? 1 : 0;
1230  stats.m_unique_channum +=
1231  (info.m_channum_cnt[map_str(chan.m_chan_num)] == 1) ? 1 : 0;
1232 
1233  if (chan.m_si_standard == "atsc")
1234  {
1235  stats.m_unique_atscnum +=
1236  (info.m_atscnum_cnt[(chan.m_atsc_major_channel << 16) |
1237  (chan.m_atsc_minor_channel)] == 1) ? 1 : 0;
1238  stats.m_unique_atscmin +=
1239  (info.m_atscmin_cnt[(chan.m_atsc_minor_channel)] == 1) ? 1 : 0;
1240  stats.m_max_atscmajcnt = max(
1241  stats.m_max_atscmajcnt,
1242  info.m_atscmaj_cnt[chan.m_atsc_major_channel]);
1243  }
1244  }
1245  }
1246 
1247  stats.m_unique_total = (stats.m_unique_prognum + stats.m_unique_atscnum +
1248  stats.m_unique_atscmin + stats.m_unique_channum);
1249 
1250  return stats;
1251 }
1252 
1253 
1255  const ScanDTVTransport &transport,
1256  const ChannelInsertInfo &chan,
1257  const ChannelImporterBasicStats *info)
1258 {
1259  QString msg;
1260  QTextStream ssMsg(&msg);
1261 
1262  ssMsg << transport.m_modulation.toString().toLatin1().constData()
1263  << ":";
1264  ssMsg << transport.m_frequency << ":";
1265 
1266  QString si_standard = (chan.m_si_standard=="opencable") ?
1267  QString("scte") : chan.m_si_standard;
1268 
1269  if (si_standard == "atsc" || si_standard == "scte")
1270  ssMsg << (QString("%1:%2:%3-%4:%5:%6=%7=%8:%9")
1271  .arg(chan.m_callsign).arg(chan.m_chan_num)
1272  .arg(chan.m_atsc_major_channel)
1273  .arg(chan.m_atsc_minor_channel)
1274  .arg(chan.m_service_id)
1275  .arg(chan.m_vct_tsid)
1276  .arg(chan.m_vct_chan_tsid)
1277  .arg(chan.m_pat_tsid)
1278  .arg(si_standard)).toLatin1().constData();
1279  else if (si_standard == "dvb")
1280  ssMsg << (QString("%1:%2:%3:%4:%5:%6=%7:%8")
1281  .arg(chan.m_service_name).arg(chan.m_chan_num)
1282  .arg(chan.m_netid).arg(chan.m_orig_netid)
1283  .arg(chan.m_service_id)
1284  .arg(chan.m_sdt_tsid)
1285  .arg(chan.m_pat_tsid)
1286  .arg(si_standard)).toLatin1().constData();
1287  else
1288  ssMsg << (QString("%1:%2:%3:%4:%5")
1289  .arg(chan.m_callsign).arg(chan.m_chan_num)
1290  .arg(chan.m_service_id)
1291  .arg(chan.m_pat_tsid)
1292  .arg(si_standard)).toLatin1().constData();
1293 
1294  if (info)
1295  {
1296  ssMsg <<"\t"
1297  << chan.m_channel_id;
1298  }
1299 
1300  if (info)
1301  {
1302  ssMsg << ":"
1303  << (QString("cnt(pnum:%1,channum:%2)")
1304  .arg(info->m_prognum_cnt[chan.m_service_id])
1305  .arg(info->m_channum_cnt[map_str(chan.m_chan_num)])
1306  ).toLatin1().constData();
1307  if (chan.m_si_standard == "atsc")
1308  {
1309  ssMsg <<
1310  (QString(":atsc_cnt(tot:%1,minor:%2)")
1311  .arg(info->m_atscnum_cnt[
1312  (chan.m_atsc_major_channel << 16) |
1313  (chan.m_atsc_minor_channel)])
1314  .arg(info->m_atscmin_cnt[
1315  chan.m_atsc_minor_channel])
1316  ).toLatin1().constData();
1317  }
1318  }
1319 
1320  return msg;
1321 }
1322 
1335  const ScanDTVTransport &/*transport*/,
1336  const ChannelInsertInfo &chan)
1337 {
1338  QString msg;
1339  QTextStream ssMsg(&msg);
1340 
1341  QString si_standard = (chan.m_si_standard=="opencable") ?
1342  QString("scte") : chan.m_si_standard;
1343 
1344  if (si_standard == "atsc" || si_standard == "scte")
1345  {
1346 
1347  if (si_standard == "atsc")
1348  ssMsg << (QString("%1-%2")
1349  .arg(chan.m_atsc_major_channel)
1350  .arg(chan.m_atsc_minor_channel)).toLatin1().constData();
1351  else if (chan.m_freqid.isEmpty())
1352  ssMsg << (QString("%1-%2")
1353  .arg(chan.m_source_id)
1354  .arg(chan.m_service_id)).toLatin1().constData();
1355  else
1356  ssMsg << (QString("%1-%2")
1357  .arg(chan.m_freqid)
1358  .arg(chan.m_service_id)).toLatin1().constData();
1359 
1360  if (!chan.m_callsign.isEmpty())
1361  ssMsg << (QString(" (%1)")
1362  .arg(chan.m_callsign)).toLatin1().constData();
1363  }
1364  else if (si_standard == "dvb")
1365  ssMsg << (QString("%1 (%2 %3)")
1366  .arg(chan.m_service_name).arg(chan.m_service_id)
1367  .arg(chan.m_netid)).toLatin1().constData();
1368  else if (chan.m_freqid.isEmpty())
1369  ssMsg << (QString("%1-%2")
1370  .arg(chan.m_source_id).arg(chan.m_service_id))
1371  .toLatin1().constData();
1372  else
1373  ssMsg << (QString("%1-%2")
1374  .arg(chan.m_freqid).arg(chan.m_service_id))
1375  .toLatin1().constData();
1376 
1377  return msg;
1378 }
1379 
1381  const ScanDTVTransportList &transports_in,
1382  const ChannelImporterBasicStats *info)
1383 {
1384  // Sort transports in order of increasing frequency
1385  struct less_than_key
1386  {
1387  inline bool operator() (const ScanDTVTransport &t1, const ScanDTVTransport &t2)
1388  {
1389  return t1.m_frequency < t2.m_frequency;
1390  }
1391  };
1392  ScanDTVTransportList transports(transports_in);
1393  std::sort(transports.begin(), transports.end(), less_than_key());
1394 
1395  QString msg;
1396 
1397  for (size_t i = 0; i < transports.size(); ++i)
1398  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
1399  msg += FormatChannel(transports[i], transports[i].m_channels[j],
1400  info) + "\n";
1401 
1402  return msg;
1403 }
1404 
1406  const ScanDTVTransport &transport)
1407 {
1408  QString msg;
1409  QTextStream ssMsg(&msg);
1410  ssMsg << transport.toString();
1411  return msg;
1412 }
1413 
1415  const ScanDTVTransportList &transports_in)
1416 {
1417  // Sort transports in order of increasing frequency
1418  struct less_than_key
1419  {
1420  inline bool operator() (const ScanDTVTransport &t1, const ScanDTVTransport &t2)
1421  {
1422  return t1.m_frequency < t2.m_frequency;
1423  }
1424  };
1425  ScanDTVTransportList transports(transports_in);
1426  std::sort(transports.begin(), transports.end(), less_than_key());
1427 
1428  QString msg;
1429 
1430  for (size_t i = 0; i < transports.size(); ++i)
1431  msg += FormatTransport(transports[i]) + "\n";
1432 
1433  return msg;
1434 }
1435 
1437  uint transport_count,
1438  const ChannelImporterBasicStats &info,
1439  const ChannelImporterUniquenessStats &stats)
1440 {
1441  //: %n is the number of transports
1442  QString msg = tr("Found %n transport(s):\n", "", transport_count);
1443  msg += tr("Channels: FTA Enc Dec\n") +
1444  QString("ATSC %1 %2 %3\n")
1445  .arg(info.m_atsc_channels[0],3).arg(info.m_atsc_channels[1],3)
1446  .arg(info.m_atsc_channels[2],3) +
1447  QString("DVB %1 %2 %3\n")
1448  .arg(info.m_dvb_channels [0],3).arg(info.m_dvb_channels [1],3)
1449  .arg(info.m_dvb_channels [2],3) +
1450  QString("SCTE %1 %2 %3\n")
1451  .arg(info.m_scte_channels[0],3).arg(info.m_scte_channels[1],3)
1452  .arg(info.m_scte_channels[2],3) +
1453  QString("MPEG %1 %2 %3\n")
1454  .arg(info.m_mpeg_channels[0],3).arg(info.m_mpeg_channels[1],3)
1455  .arg(info.m_mpeg_channels[2],3) +
1456  QString("NTSC %1\n").arg(info.m_ntsc_channels[0],3) +
1457  tr("Unique: prog %1 atsc %2 atsc minor %3 channum %4\n")
1458  .arg(stats.m_unique_prognum).arg(stats.m_unique_atscnum)
1459  .arg(stats.m_unique_atscmin).arg(stats.m_unique_channum) +
1460  tr("Max atsc major count: %1")
1461  .arg(stats.m_max_atscmajcnt);
1462 
1463  return msg;
1464 }
1465 
1467  const ChannelImporterBasicStats &info,
1468  const ChannelInsertInfo &chan, ChannelType type)
1469 {
1470  switch (type)
1471  {
1472  case kATSCNonConflicting:
1473  return ((chan.m_si_standard == "atsc") /* &&
1474  (info.m_atscnum_cnt[(chan.m_atsc_major_channel << 16) |
1475  (chan.m_atsc_minor_channel)] == 1) */);
1476 
1477  case kDVBNonConflicting:
1478  return ((chan.m_si_standard == "dvb") /* &&
1479  (info.m_prognum_cnt[chan.m_service_id] == 1) */);
1480 
1481  case kMPEGNonConflicting:
1482  return ((chan.m_si_standard == "mpeg") &&
1483  (info.m_channum_cnt[map_str(chan.m_chan_num)] == 1));
1484 
1485  case kSCTENonConflicting:
1486  return (((chan.m_si_standard == "scte") ||
1487  (chan.m_si_standard == "opencable")) &&
1488  (info.m_channum_cnt[map_str(chan.m_chan_num)] == 1));
1489 
1490  case kNTSCNonConflicting:
1491  return ((chan.m_si_standard == "ntsc") &&
1492  (info.m_atscnum_cnt[(chan.m_atsc_major_channel << 16) |
1493  (chan.m_atsc_minor_channel)] == 1));
1494 
1495  case kATSCConflicting:
1496  return ((chan.m_si_standard == "atsc") &&
1497  (info.m_atscnum_cnt[(chan.m_atsc_major_channel << 16) |
1498  (chan.m_atsc_minor_channel)] != 1));
1499 
1500  case kDVBConflicting:
1501  return ((chan.m_si_standard == "dvb") &&
1502  (info.m_prognum_cnt[chan.m_service_id] != 1));
1503 
1504  case kMPEGConflicting:
1505  return ((chan.m_si_standard == "mpeg") &&
1506  (info.m_channum_cnt[map_str(chan.m_chan_num)] != 1));
1507 
1508  case kSCTEConflicting:
1509  return (((chan.m_si_standard == "scte") ||
1510  (chan.m_si_standard == "opencable")) &&
1511  (info.m_channum_cnt[map_str(chan.m_chan_num)] != 1));
1512 
1513  case kNTSCConflicting:
1514  return ((chan.m_si_standard == "ntsc") &&
1515  (info.m_atscnum_cnt[(chan.m_atsc_major_channel << 16) |
1516  (chan.m_atsc_minor_channel)] != 1));
1517  }
1518  return false;
1519 }
1520 
1522  const ScanDTVTransportList &transports,
1523  const ChannelImporterBasicStats &info,
1524  ChannelType type, uint &new_chan, uint &old_chan)
1525 {
1526  new_chan = old_chan = 0;
1527  for (size_t i = 0; i < transports.size(); ++i)
1528  {
1529  for (size_t j = 0; j < transports[i].m_channels.size(); ++j)
1530  {
1531  ChannelInsertInfo chan = transports[i].m_channels[j];
1532  if (IsType(info, chan, type))
1533  {
1534  if (chan.m_channel_id)
1535  ++old_chan;
1536  else
1537  ++new_chan;
1538  }
1539  }
1540  }
1541 }
1542 
1544  const ScanDTVTransportList &transports)
1545 {
1546  int count = 0;
1547  for (size_t i = 0; i < transports.size(); ++i)
1548  {
1549  count += transports[i].m_channels.size();
1550  }
1551  return count;
1552 }
1553 
1566  const ChannelInsertInfo &chan)
1567 {
1568  static QMutex s_lastFreeLock;
1569  static QMap<uint,uint> s_lastFreeChanNumMap;
1570 
1571  // Suggest existing channel number if non-conflicting
1573  return chan.m_chan_num;
1574 
1575  // ATSC major-minor channel number
1576  QString channelFormat = "%1_%2";
1577  QString chan_num = channelFormat
1578  .arg(chan.m_atsc_major_channel)
1579  .arg(chan.m_atsc_minor_channel);
1580  if (chan.m_atsc_minor_channel)
1581  {
1582  if (!ChannelUtil::IsConflicting(chan_num, chan.m_source_id))
1583  return chan_num;
1584  }
1585 
1586  // DVB
1587  if (chan.m_si_standard == "dvb")
1588  {
1589  // Service ID
1590  chan_num = QString("%1").arg(chan.m_service_id);
1591  if (!ChannelUtil::IsConflicting(chan_num, chan.m_source_id))
1592  return chan_num;
1593 
1594  // Frequency ID (channel) - Service ID
1595  if (!chan.m_freqid.isEmpty())
1596  {
1597  chan_num = QString("%1-%2")
1598  .arg(chan.m_freqid)
1599  .arg(chan.m_service_id);
1600  if (!ChannelUtil::IsConflicting(chan_num, chan.m_source_id))
1601  return chan_num;
1602  }
1603 
1604  // Service ID - Network ID
1605  chan_num = QString("%1-%2")
1606  .arg(chan.m_service_id)
1607  .arg(chan.m_netid);
1608  if (!ChannelUtil::IsConflicting(chan_num, chan.m_source_id))
1609  return chan_num;
1610 
1611  // Service ID - Transport ID
1612  chan_num = QString("%1-%2")
1613  .arg(chan.m_service_id)
1614  .arg(chan.m_pat_tsid);
1615  if (!ChannelUtil::IsConflicting(chan_num, chan.m_source_id))
1616  return chan_num;
1617  }
1618 
1619  // Find unused channel number
1620  QMutexLocker locker(&s_lastFreeLock);
1621  uint last_free_chan_num = s_lastFreeChanNumMap[chan.m_source_id];
1622  for (last_free_chan_num++; ; ++last_free_chan_num)
1623  {
1624  chan_num = QString::number(last_free_chan_num);
1625  if (!ChannelUtil::IsConflicting(chan_num, chan.m_source_id))
1626  break;
1627  }
1628  // cppcheck-suppress unreadVariable
1629  s_lastFreeChanNumMap[chan.m_source_id] = last_free_chan_num;
1630 
1631  return chan_num;
1632 }
1633 
1636 {
1637  DeleteAction action = kDeleteAll;
1638  if (m_use_gui)
1639  {
1640  int ret = -1;
1641  do
1642  {
1643  MythScreenStack *popupStack =
1644  GetMythMainWindow()->GetStack("popup stack");
1645  auto *deleteDialog =
1646  new MythDialogBox(msg, popupStack, "deletechannels");
1647 
1648  if (deleteDialog->Create())
1649  {
1650  deleteDialog->AddButton(tr("Delete All"));
1651  deleteDialog->AddButton(tr("Set all invisible"));
1652 // deleteDialog->AddButton(tr("Handle manually"));
1653  deleteDialog->AddButton(tr("Ignore All"));
1654  QObject::connect(deleteDialog, &MythDialogBox::Closed,
1655  [&](const QString & /*resultId*/, int result)
1656  {
1657  ret = result;
1658  m_eventLoop.quit();
1659  });
1660  popupStack->AddScreen(deleteDialog);
1661 
1662  m_eventLoop.exec();
1663  }
1664  } while (ret < 0);
1665 
1666  action = (0 == ret) ? kDeleteAll : action;
1667  action = (1 == ret) ? kDeleteInvisibleAll : action;
1668  action = (2 == ret) ? kDeleteIgnoreAll : action;
1669 // action = (2 == m_deleteChannelResult) ? kDeleteManual : action;
1670 // action = (3 == m_deleteChannelResult) ? kDeleteIgnoreAll : action;
1671  }
1672  else if (m_is_interactive)
1673  {
1674  cout << msg.toLatin1().constData()
1675  << endl
1676  << tr("Do you want to:").toLatin1().constData()
1677  << endl
1678  << tr("1. Delete All").toLatin1().constData()
1679  << endl
1680  << tr("2. Set all invisible").toLatin1().constData()
1681  << endl
1682 // cout << "3. Handle manually" << endl;
1683  << tr("4. Ignore All").toLatin1().constData()
1684  << endl;
1685  while (true)
1686  {
1687  string ret;
1688  cin >> ret;
1689  bool ok;
1690  uint val = QString(ret.c_str()).toUInt(&ok);
1691  if (ok && (val == 1 || val == 2 || val == 4))
1692  {
1693  action = (1 == val) ? kDeleteAll : action;
1694  action = (2 == val) ? kDeleteInvisibleAll : action;
1695  //action = (3 == val) ? kDeleteManual : action;
1696  action = (4 == val) ? kDeleteIgnoreAll : action;
1697  break;
1698  }
1699 
1700  //cout << "Please enter either 1, 2, 3 or 4:" << endl;
1701  cout << tr("Please enter either 1, 2 or 4:")
1702  .toLatin1().constData() << endl;//
1703  }
1704  }
1705 
1706  return action;
1707 }
1708 
1711 {
1712  InsertAction action = kInsertAll;
1713  if (m_use_gui)
1714  {
1715  int ret = -1;
1716  do
1717  {
1718  MythScreenStack *popupStack =
1719  GetMythMainWindow()->GetStack("popup stack");
1720  auto *insertDialog =
1721  new MythDialogBox(msg, popupStack, "insertchannels");
1722 
1723  if (insertDialog->Create())
1724  {
1725  insertDialog->AddButton(tr("Insert All"));
1726  insertDialog->AddButton(tr("Insert Manually"));
1727  insertDialog->AddButton(tr("Ignore All"));
1728  QObject::connect(insertDialog, &MythDialogBox::Closed,
1729  [&](const QString & /*resultId*/, int result)
1730  {
1731  ret = result;
1732  m_eventLoop.quit();
1733  });
1734 
1735  popupStack->AddScreen(insertDialog);
1736  m_eventLoop.exec();
1737  }
1738  } while (ret < 0);
1739 
1740  action = (0 == ret) ? kInsertAll : action;
1741  action = (1 == ret) ? kInsertManual : action;
1742  action = (2 == ret) ? kInsertIgnoreAll : action;
1743  }
1744  else if (m_is_interactive)
1745  {
1746  cout << msg.toLatin1().constData()
1747  << endl
1748  << tr("Do you want to:").toLatin1().constData()
1749  << endl
1750  << tr("1. Insert All").toLatin1().constData()
1751  << endl
1752  << tr("2. Insert Manually").toLatin1().constData()
1753  << endl
1754  << tr("3. Ignore All").toLatin1().constData()
1755  << endl;
1756  while (true)
1757  {
1758  string ret;
1759  cin >> ret;
1760  bool ok;
1761  uint val = QString(ret.c_str()).toUInt(&ok);
1762  if (ok && (1 <= val) && (val <= 3))
1763  {
1764  action = (1 == val) ? kInsertAll : action;
1765  action = (2 == val) ? kInsertManual : action;
1766  action = (3 == val) ? kInsertIgnoreAll : action;
1767  break;
1768  }
1769 
1770  cout << tr("Please enter either 1, 2, or 3:")
1771  .toLatin1().constData() << endl;
1772  }
1773  }
1774 
1775  return action;
1776 }
1777 
1780 {
1781  UpdateAction action = kUpdateAll;
1782 
1783  if (m_use_gui)
1784  {
1785  int ret = -1;
1786  do
1787  {
1788  MythScreenStack *popupStack =
1789  GetMythMainWindow()->GetStack("popup stack");
1790  auto *updateDialog =
1791  new MythDialogBox(msg, popupStack, "updatechannels");
1792 
1793  if (updateDialog->Create())
1794  {
1795  updateDialog->AddButton(tr("Update All"));
1796  updateDialog->AddButton(tr("Ignore All"));
1797  QObject::connect(updateDialog, &MythDialogBox::Closed,
1798  [&](const QString& /*resultId*/, int result)
1799  {
1800  ret = result;
1801  m_eventLoop.quit();
1802  });
1803 
1804  popupStack->AddScreen(updateDialog);
1805  m_eventLoop.exec();
1806  }
1807  } while (ret < 0);
1808 
1809  action = (0 == ret) ? kUpdateAll : action;
1810  action = (1 == ret) ? kUpdateIgnoreAll : action;
1811  }
1812  else if (m_is_interactive)
1813  {
1814  cout << msg.toLatin1().constData()
1815  << endl
1816  << tr("Do you want to:").toLatin1().constData()
1817  << endl
1818  << tr("1. Update All").toLatin1().constData()
1819  << endl
1820  << tr("2. Update Manually").toLatin1().constData()
1821  << endl
1822  << tr("3. Ignore All").toLatin1().constData()
1823  << endl;
1824  while (true)
1825  {
1826  string ret;
1827  cin >> ret;
1828  bool ok;
1829  uint val = QString(ret.c_str()).toUInt(&ok);
1830  if (ok && (1 <= val) && (val <= 3))
1831  {
1832  action = (1 == val) ? kUpdateAll : action;
1833  action = (2 == val) ? kUpdateManual : action;
1834  action = (3 == val) ? kUpdateIgnoreAll : action;
1835  break;
1836  }
1837 
1838  cout << tr("Please enter either 1, 2, or 3:")
1839  .toLatin1().constData() << endl;
1840  }
1841  }
1842 
1843  return action;
1844 }
1845 
1847  MythMainWindow *parent, const QString& title,
1848  const QString& message, QString &text)
1849 {
1850  int dc = -1;
1851  MythScreenStack *popupStack = parent->GetStack("popup stack");
1852  auto *popup = new MythDialogBox(title, message, popupStack,
1853  "manualchannelpopup");
1854 
1855  if (popup->Create())
1856  {
1857  popup->AddButton(QCoreApplication::translate("(Common)", "OK"));
1858  popup->AddButton(tr("Edit"));
1859  popup->AddButton(QCoreApplication::translate("(Common)", "Cancel"));
1860  popup->AddButton(QCoreApplication::translate("(Common)", "Cancel All"));
1861  QObject::connect(popup, &MythDialogBox::Closed,
1862  [&](const QString & /*resultId*/, int result)
1863  {
1864  dc = result;
1865  m_eventLoop.quit();
1866  });
1867  popupStack->AddScreen(popup);
1868  m_eventLoop.exec();
1869  }
1870  else
1871  {
1872  delete popup;
1873  popup = nullptr;
1874  }
1875 
1876  // Choice "Edit"
1877  if (1 == dc)
1878  {
1879  auto *textEdit =
1880  new MythTextInputDialog(popupStack,
1881  tr("Please enter a unique channel number."),
1882  FilterNone, false, text);
1883  if (textEdit->Create())
1884  {
1885  QObject::connect(textEdit, &MythTextInputDialog::haveResult,
1886  [&](QString result)
1887  {
1888  dc = 0;
1889  text = std::move(result);
1890  });
1891  QObject::connect(textEdit, &MythTextInputDialog::Exiting,
1892  [&]()
1893  {
1894  m_eventLoop.quit();
1895  });
1896 
1897  popupStack->AddScreen(textEdit);
1898  m_eventLoop.exec();
1899  }
1900  else
1901  delete textEdit;
1902  }
1903 
1904  OkCancelType rval = kOCTCancel;
1905  switch (dc) {
1906  case 0: rval = kOCTOk; break;
1907  // NOLINTNEXTLINE(bugprone-branch-clone)
1908  case 1: rval = kOCTCancel; break; // "Edit" is done already
1909  case 2: rval = kOCTCancel; break;
1910  case 3: rval = kOCTCancelAll; break;
1911  }
1912  return rval;
1913 }
1914 
1916  MythMainWindow *parent, const QString& title,
1917  const QString& message, QString &text)
1918 {
1919  int dc = -1;
1920  MythScreenStack *popupStack = parent->GetStack("popup stack");
1921  auto *popup = new MythDialogBox(title, message, popupStack,
1922  "resolvechannelpopup");
1923 
1924  if (popup->Create())
1925  {
1926  popup->AddButton(QCoreApplication::translate("(Common)", "OK"));
1927  popup->AddButton(QCoreApplication::translate("(Common)", "OK All"));
1928  popup->AddButton(tr("Edit"));
1929  popup->AddButton(QCoreApplication::translate("(Common)", "Cancel"));
1930  popup->AddButton(QCoreApplication::translate("(Common)", "Cancel All"));
1931  QObject::connect(popup, &MythDialogBox::Closed,
1932  [&](const QString & /*resultId*/, int result)
1933  {
1934  dc = result;
1935  m_eventLoop.quit();
1936  });
1937  popupStack->AddScreen(popup);
1938  m_eventLoop.exec();
1939  }
1940  else
1941  {
1942  delete popup;
1943  popup = nullptr;
1944  }
1945 
1946  // Choice "Edit"
1947  if (2 == dc)
1948  {
1949  auto *textEdit =
1950  new MythTextInputDialog(popupStack,
1951  tr("Please enter a unique channel number."),
1952  FilterNone, false, text);
1953  if (textEdit->Create())
1954  {
1955  QObject::connect(textEdit, &MythTextInputDialog::haveResult,
1956  [&](QString result)
1957  {
1958  dc = 0;
1959  text = std::move(result);
1960  });
1961  QObject::connect(textEdit, &MythTextInputDialog::Exiting,
1962  [&]()
1963  {
1964  m_eventLoop.quit();
1965  });
1966 
1967  popupStack->AddScreen(textEdit);
1968  m_eventLoop.exec();
1969  }
1970  else
1971  delete textEdit;
1972  }
1973 
1974  OkCancelType rval = kOCTCancel;
1975  switch (dc) {
1976  case 0: rval = kOCTOk; break;
1977  case 1: rval = kOCTOkAll; break;
1978  // NOLINTNEXTLINE(bugprone-branch-clone)
1979  case 2: rval = kOCTCancel; break; // "Edit" is done already
1980  case 3: rval = kOCTCancel; break;
1981  case 4: rval = kOCTCancelAll; break;
1982  }
1983  return rval;
1984 }
1985 
1987  const ScanDTVTransport &transport,
1988  ChannelInsertInfo &chan)
1989 {
1990  QString msg = tr("Channel %1 has channel number %2 but that is already in use.")
1991  .arg(SimpleFormatChannel(transport, chan))
1992  .arg(chan.m_chan_num);
1993 
1994  OkCancelType ret = kOCTCancel;
1995 
1996  if (m_use_gui)
1997  {
1998  while (true)
1999  {
2000  QString msg2 = msg;
2001  msg2 += "\n";
2002  msg2 += tr("Please enter a unique channel number.");
2003 
2004  QString val = ComputeSuggestedChannelNum(chan);
2005  msg2 += "\n";
2006  msg2 += tr("Default value is %1.").arg(val);
2007  ret = ShowResolveChannelPopup(
2008  GetMythMainWindow(), tr("Channel Importer"),
2009  msg2, val);
2010 
2011  if (kOCTOk != ret && kOCTOkAll != ret)
2012  break; // user canceled..
2013 
2014  bool ok = CheckChannelNumber(val, chan);
2015  if (ok)
2016  {
2017  chan.m_chan_num = val;
2018  break;
2019  }
2020  }
2021  }
2022  else if (m_is_interactive)
2023  {
2024  cout << msg.toLatin1().constData() << endl;
2025 
2026  QString cancelStr = QCoreApplication::translate("(Common)",
2027  "Cancel").toLower();
2028  QString cancelAllStr = QCoreApplication::translate("(Common)",
2029  "Cancel All").toLower();
2030  QString msg2 = tr("Please enter a non-conflicting channel number "
2031  "(or type '%1' to skip, '%2' to skip all):")
2032  .arg(cancelStr).arg(cancelAllStr);
2033 
2034  while (true)
2035  {
2036  cout << msg2.toLatin1().constData() << endl;
2037  string sret;
2038  cin >> sret;
2039  QString val = QString(sret.c_str());
2040  if (val.toLower() == cancelStr)
2041  {
2042  ret = kOCTCancel;
2043  break; // user canceled..
2044  }
2045  if (val.toLower() == cancelAllStr)
2046  {
2047  ret = kOCTCancelAll;
2048  break; // user canceled..
2049  }
2050 
2051  bool ok = CheckChannelNumber(val, chan);
2052  if (ok)
2053  {
2054  chan.m_chan_num = val;
2055  ret = kOCTOk;
2056  break;
2057  }
2058  }
2059  }
2060 
2061  return ret;
2062 }
2063 
2065  const ScanDTVTransport &transport,
2066  ChannelInsertInfo &chan)
2067 {
2068  QString msg = tr("You chose to manually insert channel %1.")
2069  .arg(SimpleFormatChannel(transport, chan));
2070 
2071  OkCancelType ret = kOCTCancel;
2072 
2073  if (m_use_gui)
2074  {
2075  while (true)
2076  {
2077  QString msg2 = msg;
2078  msg2 += " ";
2079  msg2 += tr("Please enter a unique channel number.");
2080 
2081  QString val = ComputeSuggestedChannelNum(chan);
2082  msg2 += " ";
2083  msg2 += tr("Default value is %1").arg(val);
2084  ret = ShowManualChannelPopup(
2085  GetMythMainWindow(), tr("Channel Importer"),
2086  msg2, val);
2087 
2088  if (kOCTOk != ret)
2089  break; // user canceled..
2090 
2091  bool ok = CheckChannelNumber(val, chan);
2092  if (ok)
2093  {
2094  chan.m_chan_num = val;
2095  ret = kOCTOk;
2096  break;
2097  }
2098  }
2099  }
2100  else if (m_is_interactive)
2101  {
2102  cout << msg.toLatin1().constData() << endl;
2103 
2104  QString cancelStr = QCoreApplication::translate("(Common)", "Cancel").toLower();
2105  QString cancelAllStr = QCoreApplication::translate("(Common)", "Cancel All").toLower();
2106 
2107  //: %1 is the translation of "Cancel", %2 of "Cancel All"
2108  QString msg2 = tr("Please enter a non-conflicting channel number "
2109  "(or type '%1' to skip, '%2' to skip all): ")
2110  .arg(cancelStr).arg(cancelAllStr);
2111 
2112  while (true)
2113  {
2114  cout << msg2.toLatin1().constData() << endl;
2115  string sret;
2116  cin >> sret;
2117  QString val = QString(sret.c_str());
2118  if (val.toLower() == cancelStr)
2119  {
2120  ret = kOCTCancel;
2121  break; // user canceled..
2122  }
2123  if (val.toLower() == cancelAllStr)
2124  {
2125  ret = kOCTCancelAll;
2126  break; // user canceled..
2127  }
2128 
2129  bool ok = CheckChannelNumber(val, chan);
2130  if (ok)
2131  {
2132  chan.m_chan_num = val;
2133  ret = kOCTOk;
2134  break;
2135  }
2136  }
2137  }
2138 
2139  return ret;
2140 }
2141 
2142 // ChannelImporter::CheckChannelNumber
2143 //
2144 // Check validity of a new channel number.
2145 // The channel number is not a number but it is a string that starts with a digit.
2146 // The channel number should not yet exist in this video source.
2147 //
2149  const QString &num,
2150  const ChannelInsertInfo &chan)
2151 {
2152  bool ok = (num.length() >= 1);
2153  ok = ok && ((num[0] >= '0') && (num[0] <= '9'));
2154  ok = ok && !ChannelUtil::IsConflicting(
2155  num, chan.m_source_id, chan.m_channel_id);
2156  return ok;
2157 }
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:781
static bool SetVisible(uint channel_id, bool visible)
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:862
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
QString m_service_name
Definition: channelinfo.h:207
QMap< QString, uint > m_channum_cnt
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
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, bool hidden, bool hidden_in_guide, const QString &freqid, const QString &icon=QString(), QString format="Default", const QString &xmltvid=QString(), const QString &default_authority=QString(), uint service_type=0)
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)
QMap< uint, uint > m_prognum_cnt
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
QMap< uint, uint > m_atscmaj_cnt
QMap< uint, uint > m_atscmin_cnt
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)
void InsertChannels(const ScanDTVTransportList &, const ChannelImporterBasicStats &)
#define LOC
ChannelInsertInfoList m_channels
Definition: dtvmultiplex.h:139
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
OkCancelType ShowManualChannelPopup(MythMainWindow *parent, const QString &title, const QString &message, QString &text)
bool IsSameChannel(const ChannelInsertInfo &, int relaxed=0) const
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:243
bool IsEqual(DTVTunerType type, const DTVMultiplex &other, uint freq_range=0, bool fuzzy=false) const
static void AddChanToCopy(ScanDTVTransport &transport_copy, const ScanDTVTransport &transport, const ChannelInsertInfo &chan)
#define VERBOSE_LEVEL_CHECK(_MASK_, _LEVEL_)
Definition: mythlogging.h:24
static bool IsConflicting(const QString &channum, uint sourceid=0, uint excluded_chanid=0)
Definition: channelutil.h:284
ScanDTVTransportList GetDBTransports(uint sourceid, ScanDTVTransportList &) const
Adds found channel info to transports list, returns channels in DB which were not found in scan in an...
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=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:534
static bool CreateIPTVTuningData(uint channel_id, const IPTVTuningData &tuning)
Definition: channelutil.h:142
static int CreateChanID(uint sourceid, const QString &chan_num)
Creates a unique channel ID for database use.
MythMainWindow * GetMythMainWindow(void)
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:806
static ChannelImporterBasicStats CollectStats(const ScanDTVTransportList &transports)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
QString m_si_standard
Definition: channelinfo.h:229
uint DeleteChannels(ScanDTVTransportList &)
OkCancelType
static ChannelImporterUniquenessStats CollectUniquenessStats(const ScanDTVTransportList &transports, const ChannelImporterBasicStats &info)
bool FillFromDB(DTVTunerType type, uint mplexid) override
static void UpdateChannelNumberFromDB(ChannelInsertInfo &chan)
QString m_default_authority
Definition: channelinfo.h:220
static QString FormatTransports(const ScanDTVTransportList &transports_in)
OkCancelType QueryUserResolve(const ScanDTVTransport &transport, ChannelInsertInfo &chan)
For a single channel.
vector< ScanDTVTransport > ScanDTVTransportList
Definition: dtvmultiplex.h:141
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:602
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
QMap< uint, uint > m_atscnum_cnt
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
void Process(const ScanDTVTransportList &, int sourceid=-1)
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, bool hidden, bool hidden_in_guide, const QString &freqid=QString(), const QString &icon=QString(), QString format=QString(), const QString &xmltvid=QString(), const QString &default_authority=QString(), uint service_type=0)