|
MythTV
0.26-pre
|
00001 // -*- Mode: c++ -*- 00002 /* 00003 * Copyright (C) Daniel Kristjansson 2007 00004 * 00005 * This file is licensed under GPL v2 or (at your option) any later version. 00006 * 00007 */ 00008 00009 #include <QTextStream> 00010 #include <iostream> 00011 00012 using namespace std; 00013 00014 // MythTV headers 00015 #include "channelimporter.h" 00016 #include "mythdialogs.h" 00017 #include "mythwidgets.h" 00018 #include "mythdb.h" 00019 #include "mpegstreamdata.h" // for kEncDecrypted 00020 #include "channelutil.h" 00021 00022 #define LOC QString("ChanImport: ") 00023 00024 static QString map_str(QString str) 00025 { 00026 if (str.isEmpty()) 00027 return ""; 00028 str.detach(); 00029 return str; 00030 } 00031 00032 void ChannelImporter::Process(const ScanDTVTransportList &_transports) 00033 { 00034 if (_transports.empty()) 00035 { 00036 if (use_gui) 00037 { 00038 LOG(VB_GENERAL, LOG_INFO, LOC + (ChannelUtil::GetChannelCount() ? 00039 "No new channels to process" : 00040 "No channels to process..")); 00041 MythPopupBox::showOkPopup( 00042 GetMythMainWindow(), QObject::tr("Channel Importer"), 00043 ChannelUtil::GetChannelCount() 00044 ? QObject::tr("Failed to find any new channels!") 00045 : QObject::tr("Failed to find any channels.")); 00046 } 00047 else 00048 { 00049 cout << (ChannelUtil::GetChannelCount() ? 00050 "No new channels to process" : 00051 "No channels to process.."); 00052 } 00053 00054 return; 00055 } 00056 00057 ScanDTVTransportList transports = _transports; 00058 00059 // Print out each channel 00060 if (VERBOSE_LEVEL_CHECK(VB_CHANSCAN, LOG_ANY)) 00061 { 00062 cout << "Before processing: " << endl; 00063 ChannelImporterBasicStats infoA = CollectStats(transports); 00064 cout << FormatChannels(transports, infoA).toAscii().constData() << endl; 00065 cout << endl << endl; 00066 } 00067 00068 uint saved_scan = 0; 00069 if (do_save) 00070 saved_scan = SaveScan(transports); 00071 00072 CleanupDuplicates(transports); 00073 00074 FilterServices(transports); 00075 00076 // Pull in DB info 00077 uint sourceid = transports[0].channels[0].source_id; 00078 ScanDTVTransportList db_trans = GetDBTransports(sourceid, transports); 00079 00080 // Make sure "Open Cable" channels are marked that way. 00081 FixUpOpenCable(transports); 00082 00083 // if scan was not aborted prematurely.. 00084 uint deleted_count = 0; 00085 if (do_delete) 00086 { 00087 ScanDTVTransportList trans = transports; 00088 for (uint i = 0; i < db_trans.size(); i++) 00089 trans.push_back(db_trans[i]); 00090 deleted_count = DeleteChannels(trans); 00091 if (deleted_count) 00092 transports = trans; 00093 } 00094 00095 // Determine System Info standards.. 00096 ChannelImporterBasicStats info = CollectStats(transports); 00097 00098 // Determine uniqueness of various naming schemes 00099 ChannelImporterUniquenessStats stats = 00100 CollectUniquenessStats(transports, info); 00101 00102 // Print out each channel 00103 cout << FormatChannels(transports, info).toAscii().constData() << endl; 00104 00105 // Create summary 00106 QString msg = GetSummary(transports.size(), info, stats); 00107 cout << msg.toAscii().constData() << endl << endl; 00108 00109 if (do_insert) 00110 InsertChannels(transports, info); 00111 00112 if (do_delete && sourceid) 00113 DeleteUnusedTransports(sourceid); 00114 00115 if (do_delete || do_insert) 00116 ScanInfo::MarkProcessed(saved_scan); 00117 } 00118 00119 QString ChannelImporter::toString(ChannelType type) 00120 { 00121 switch (type) 00122 { 00123 // non-conflicting 00124 case kATSCNonConflicting: return "ATSC"; 00125 case kDVBNonConflicting: return "DVB"; 00126 case kSCTENonConflicting: return "SCTE"; 00127 case kMPEGNonConflicting: return "MPEG"; 00128 case kNTSCNonConflicting: return "NTSC"; 00129 // conflicting 00130 case kATSCConflicting: return "ATSC"; 00131 case kDVBConflicting: return "DVB"; 00132 case kSCTEConflicting: return "SCTE"; 00133 case kMPEGConflicting: return "MPEG"; 00134 case kNTSCConflicting: return "NTSC"; 00135 } 00136 return "Unknown"; 00137 } 00138 00139 uint ChannelImporter::DeleteChannels( 00140 ScanDTVTransportList &transports) 00141 { 00142 vector<uint> off_air_list; 00143 QMap<uint,bool> deleted; 00144 00145 for (uint i = 0; i < transports.size(); i++) 00146 { 00147 for (uint j = 0; j < transports[i].channels.size(); j++) 00148 { 00149 ChannelInsertInfo chan = transports[i].channels[j]; 00150 bool was_in_db = chan.db_mplexid && chan.channel_id; 00151 if (!was_in_db) 00152 continue; 00153 00154 if (!chan.in_pmt) 00155 off_air_list.push_back(i<<16|j); 00156 } 00157 } 00158 00159 ScanDTVTransportList newlist; 00160 if (off_air_list.empty()) 00161 { 00162 return 0; 00163 } 00164 00165 // ask user whether to delete all or some of these stale channels 00166 // if some is selected ask about each individually 00167 QString msg = QObject::tr( 00168 "Found %n off-air channel(s).", "", off_air_list.size()); 00169 DeleteAction action = QueryUserDelete(msg); 00170 if (kDeleteIgnoreAll == action) 00171 return 0; 00172 00173 if (kDeleteAll == action) 00174 { 00175 for (uint k = 0; k < off_air_list.size(); k++) 00176 { 00177 int i = off_air_list[k] >> 16, j = off_air_list[k] & 0xFFFF; 00178 ChannelUtil::DeleteChannel( 00179 transports[i].channels[j].channel_id); 00180 deleted[off_air_list[k]] = true; 00181 } 00182 } 00183 else if (kDeleteInvisibleAll == action) 00184 { 00185 for (uint k = 0; k < off_air_list.size(); k++) 00186 { 00187 int i = off_air_list[k] >> 16, j = off_air_list[k] & 0xFFFF; 00188 int chanid = transports[i].channels[j].channel_id; 00189 QString channum = ChannelUtil::GetChanNum(chanid); 00190 ChannelUtil::SetVisible(chanid, false); 00191 ChannelUtil::SetChannelValue("channum", QString("_%1").arg(channum), 00192 chanid); 00193 } 00194 } 00195 else 00196 { 00197 // TODO manual delete 00198 } 00199 00200 // TODO delete encrypted channels when m_fta_only set 00201 00202 if (deleted.size() == 0) 00203 return 0; 00204 00205 // Create a new transports list without the deleted channels 00206 for (uint i = 0; i < transports.size(); i++) 00207 { 00208 newlist.push_back(transports[i]); 00209 newlist.back().channels.clear(); 00210 for (uint j = 0; j < transports[i].channels.size(); j++) 00211 { 00212 if (!deleted.contains(i<<16|j)) 00213 { 00214 newlist.back().channels.push_back( 00215 transports[i].channels[j]); 00216 } 00217 } 00218 } 00219 00220 // TODO print list of stale channels (as deleted if action approved). 00221 00222 transports = newlist; 00223 return deleted.size(); 00224 } 00225 00226 uint ChannelImporter::DeleteUnusedTransports(uint sourceid) 00227 { 00228 MSqlQuery query(MSqlQuery::InitCon()); 00229 query.prepare( 00230 "SELECT mplexid FROM dtv_multiplex " 00231 "WHERE sourceid = :SOURCEID1 AND " 00232 " mplexid NOT IN " 00233 " (SELECT mplexid " 00234 " FROM channel " 00235 " WHERE sourceid = :SOURCEID2)"); 00236 query.bindValue(":SOURCEID1", sourceid); 00237 query.bindValue(":SOURCEID2", sourceid); 00238 if (!query.exec()) 00239 { 00240 MythDB::DBError("DeleteUnusedTransports() -- select", query); 00241 return 0; 00242 } 00243 00244 QString msg = QObject::tr("Found %n unused transport(s).", "", query.size()); 00245 00246 LOG(VB_GENERAL, LOG_INFO, LOC + msg); 00247 00248 if (query.size() == 0) 00249 return 0; 00250 00251 DeleteAction action = QueryUserDelete(msg); 00252 if (kDeleteIgnoreAll == action) 00253 return 0; 00254 00255 if (kDeleteAll == action) 00256 { 00257 query.prepare( 00258 "DELETE FROM dtv_multiplex " 00259 "WHERE sourceid = :SOURCEID1 AND " 00260 " mplexid NOT IN " 00261 " (SELECT mplexid " 00262 " FROM channel " 00263 " WHERE sourceid = :SOURCEID2)"); 00264 query.bindValue(":SOURCEID1", sourceid); 00265 query.bindValue(":SOURCEID2", sourceid); 00266 if (!query.exec()) 00267 { 00268 MythDB::DBError("DeleteUnusedTransports() -- delete", query); 00269 return 0; 00270 } 00271 } 00272 else 00273 { 00274 // TODO manual delete 00275 } 00276 return 0; 00277 } 00278 00279 void ChannelImporter::InsertChannels( 00280 const ScanDTVTransportList &transports, 00281 const ChannelImporterBasicStats &info) 00282 { 00283 ScanDTVTransportList list = transports; 00284 ScanDTVTransportList filtered; 00285 00286 // insert/update all channels with non-conflicting channum 00287 // and complete tuning information. 00288 00289 uint chantype = (uint) kChannelTypeNonConflictingFirst; 00290 for (; chantype <= (uint) kChannelTypeNonConflictingLast; chantype++) 00291 { 00292 ChannelType type = (ChannelType) chantype; 00293 uint new_chan, old_chan; 00294 CountChannels(list, info, type, new_chan, old_chan); 00295 00296 if (kNTSCNonConflicting == type) 00297 continue; 00298 00299 if (old_chan) 00300 { 00301 QString msg = QObject::tr("Found %n old %1 channel(s).", "", old_chan) 00302 .arg(toString(type)); 00303 00304 UpdateAction action = QueryUserUpdate(msg); 00305 list = UpdateChannels(list, info, action, type, filtered); 00306 } 00307 if (new_chan) 00308 { 00309 QString msg = QObject::tr( 00310 "Found %n new non-conflicting %1 channel(s).", "", new_chan) 00311 .arg(toString(type)); 00312 00313 InsertAction action = QueryUserInsert(msg); 00314 list = InsertChannels(list, info, action, type, filtered); 00315 } 00316 } 00317 00318 if (!is_interactive) 00319 return; 00320 00321 // sum uniques again 00322 ChannelImporterBasicStats ninfo = CollectStats(list); 00323 ChannelImporterUniquenessStats nstats = CollectUniquenessStats(list, ninfo); 00324 cout << endl << endl << "Printing remaining channels" << endl; 00325 cout << FormatChannels(list, ninfo).toAscii().constData() << endl; 00326 cout << GetSummary(list.size(), ninfo, nstats).toAscii().constData() 00327 << endl << endl; 00328 00329 // if any of the potential uniques is high and inserting 00330 // with those as the channum would result in few conflicts 00331 // ask user if it is ok to to proceed using it as the channum 00332 00333 // for remaining channels with complete tuning information 00334 // insert channels with contiguous list of numbers as the channums 00335 chantype = (uint) kChannelTypeConflictingFirst; 00336 for (; chantype <= (uint) kChannelTypeConflictingLast; chantype++) 00337 { 00338 00339 ChannelType type = (ChannelType) chantype; 00340 uint new_chan, old_chan; 00341 CountChannels(list, info, type, new_chan, old_chan); 00342 if (new_chan) 00343 { 00344 QString msg = QObject::tr( 00345 "Found %n new conflicting %1 channel(s).", "", new_chan) 00346 .arg(toString(type)); 00347 00348 InsertAction action = QueryUserInsert(msg); 00349 list = InsertChannels(list, info, action, type, filtered); 00350 } 00351 if (old_chan) 00352 { 00353 QString msg = QObject::tr("Found %n conflicting old %1 channel(s).", 00354 "", old_chan).arg(toString(type)); 00355 00356 UpdateAction action = QueryUserUpdate(msg); 00357 list = UpdateChannels(list, info, action, type, filtered); 00358 } 00359 } 00360 00361 // print list of inserted channels 00362 // print list of ignored channels (by ignored reason category) 00363 // print list of invalid channels 00364 } 00365 00366 ScanDTVTransportList ChannelImporter::InsertChannels( 00367 const ScanDTVTransportList &transports, 00368 const ChannelImporterBasicStats &info, 00369 InsertAction action, ChannelType type, 00370 ScanDTVTransportList &filtered) 00371 { 00372 QString channelFormat = "%1_%2"; 00373 00374 ScanDTVTransportList next_list; 00375 00376 bool ignore_rest = false; 00377 00378 // insert all channels with non-conflicting channum 00379 // and complete tuning information. 00380 for (uint i = 0; i < transports.size(); i++) 00381 { 00382 bool created_new_transport = false; 00383 ScanDTVTransport new_transport; 00384 bool created_filter_transport = false; 00385 ScanDTVTransport filter_transport; 00386 00387 for (uint j = 0; j < transports[i].channels.size(); j++) 00388 { 00389 ChannelInsertInfo chan = transports[i].channels[j]; 00390 00391 bool filter = false, handle = false; 00392 if (!chan.channel_id && (kInsertIgnoreAll == action) && 00393 IsType(info, chan, type)) 00394 { 00395 filter = true; 00396 } 00397 else if (!chan.channel_id && IsType(info, chan, type)) 00398 { 00399 handle = true; 00400 } 00401 00402 if (ignore_rest) 00403 { 00404 cout<<QString("Skipping Insert: %1") 00405 .arg(FormatChannel(transports[i], chan)) 00406 .toAscii().constData()<<endl; 00407 handle = false; 00408 } 00409 00410 if (handle && kInsertManual == action) 00411 { 00412 OkCancelType rc = QueryUserInsert(info, transports[i], chan); 00413 if (kOCTCancelAll == rc) 00414 { 00415 ignore_rest = true; 00416 handle = false; 00417 } 00418 else if (kOCTCancel == rc) 00419 { 00420 handle = false; 00421 } 00422 } 00423 00424 if (handle) 00425 { 00426 bool conflicting = false; 00427 00428 if (chan.chan_num.isEmpty() || 00429 ChannelUtil::IsConflicting(chan.chan_num, chan.source_id)) 00430 { 00431 if ((kATSCNonConflicting == type) || 00432 (kATSCConflicting == type)) 00433 { 00434 chan.chan_num = channelFormat 00435 .arg(chan.atsc_major_channel) 00436 .arg(chan.atsc_minor_channel); 00437 } 00438 else if (chan.si_standard == "dvb") 00439 { 00440 chan.chan_num = QString("%1") 00441 .arg(chan.service_id); 00442 } 00443 else 00444 { 00445 chan.chan_num = QString("%1-%2") 00446 .arg(chan.freqid) 00447 .arg(chan.service_id); 00448 } 00449 00450 conflicting = ChannelUtil::IsConflicting( 00451 chan.chan_num, chan.source_id); 00452 } 00453 00454 if (is_interactive && 00455 (conflicting || (kChannelTypeConflictingFirst <= type))) 00456 { 00457 OkCancelType rc = 00458 QueryUserResolve(info, transports[i], chan); 00459 00460 conflicting = true; 00461 if (kOCTCancelAll == rc) 00462 ignore_rest = true; 00463 else if (kOCTOk == rc) 00464 conflicting = false; 00465 } 00466 00467 if (conflicting) 00468 { 00469 cout<<QString("Skipping Insert: %1") 00470 .arg(FormatChannel(transports[i], chan)) 00471 .toAscii().constData()<<endl; 00472 handle = false; 00473 } 00474 } 00475 00476 bool inserted = false; 00477 if (handle) 00478 { 00479 int chanid = ChannelUtil::CreateChanID( 00480 chan.source_id, chan.chan_num); 00481 00482 chan.channel_id = (chanid > 0) ? chanid : chan.channel_id; 00483 00484 if (chan.channel_id && !chan.db_mplexid) 00485 { 00486 uint tsid = chan.vct_tsid; 00487 tsid = (tsid) ? tsid : chan.sdt_tsid; 00488 tsid = (tsid) ? tsid : chan.pat_tsid; 00489 tsid = (tsid) ? tsid : chan.vct_chan_tsid; 00490 00491 chan.db_mplexid = ChannelUtil::CreateMultiplex( 00492 chan.source_id, transports[i], tsid, chan.orig_netid); 00493 } 00494 00495 if (chan.channel_id && chan.db_mplexid) 00496 { 00497 chan.channel_id = chanid; 00498 00499 cout<<"Insert("<<chan.si_standard.toAscii().constData() 00500 <<"): "<<chan.chan_num.toAscii().constData()<<endl; 00501 00502 inserted = ChannelUtil::CreateChannel( 00503 chan.db_mplexid, 00504 chan.source_id, 00505 chan.channel_id, 00506 chan.callsign, 00507 chan.service_name, 00508 chan.chan_num, 00509 chan.service_id, 00510 chan.atsc_major_channel, 00511 chan.atsc_minor_channel, 00512 chan.use_on_air_guide, 00513 chan.hidden, chan.hidden_in_guide, 00514 chan.freqid, 00515 QString::null, 00516 QString::null, 00517 QString::null, 00518 chan.default_authority); 00519 } 00520 } 00521 00522 if (filter) 00523 { 00524 if (!created_filter_transport) 00525 { 00526 filter_transport = transports[i]; 00527 filter_transport.channels.clear(); 00528 created_filter_transport = true; 00529 } 00530 filter_transport.channels.push_back(transports[i].channels[j]); 00531 } 00532 else if (!inserted) 00533 { 00534 if (!created_new_transport) 00535 { 00536 new_transport = transports[i]; 00537 new_transport.channels.clear(); 00538 created_new_transport = true; 00539 } 00540 new_transport.channels.push_back(transports[i].channels[j]); 00541 } 00542 } 00543 00544 if (created_filter_transport) 00545 filtered.push_back(filter_transport); 00546 00547 if (created_new_transport) 00548 next_list.push_back(new_transport); 00549 } 00550 00551 return next_list; 00552 } 00553 00554 ScanDTVTransportList ChannelImporter::UpdateChannels( 00555 const ScanDTVTransportList &transports, 00556 const ChannelImporterBasicStats &info, 00557 UpdateAction action, ChannelType type, 00558 ScanDTVTransportList &filtered) 00559 { 00560 QString channelFormat = "%1_%2"; 00561 bool renameChannels = false; 00562 00563 ScanDTVTransportList next_list; 00564 00565 // update all channels with non-conflicting channum 00566 // and complete tuning information. 00567 for (uint i = 0; i < transports.size(); i++) 00568 { 00569 bool created_transport = false; 00570 ScanDTVTransport new_transport; 00571 bool created_filter_transport = false; 00572 ScanDTVTransport filter_transport; 00573 00574 for (uint j = 0; j < transports[i].channels.size(); j++) 00575 { 00576 ChannelInsertInfo chan = transports[i].channels[j]; 00577 00578 bool filter = false, handle = false; 00579 if (chan.channel_id && (kUpdateIgnoreAll == action) && 00580 IsType(info, chan, type)) 00581 { 00582 filter = true; 00583 } 00584 else if (chan.channel_id && IsType(info, chan, type)) 00585 { 00586 handle = true; 00587 } 00588 00589 if (handle) 00590 { 00591 bool conflicting = false; 00592 00593 if (chan.chan_num.isEmpty() || renameChannels || 00594 ChannelUtil::IsConflicting( 00595 chan.chan_num, chan.source_id, chan.channel_id)) 00596 { 00597 if (kATSCNonConflicting == type) 00598 { 00599 chan.chan_num = channelFormat 00600 .arg(chan.atsc_major_channel) 00601 .arg(chan.atsc_minor_channel); 00602 } 00603 else if (chan.si_standard == "dvb") 00604 { 00605 chan.chan_num = QString("%1") 00606 .arg(chan.service_id); 00607 } 00608 else 00609 { 00610 chan.chan_num = QString("%1-%2") 00611 .arg(chan.freqid) 00612 .arg(chan.service_id); 00613 } 00614 00615 conflicting = ChannelUtil::IsConflicting( 00616 chan.chan_num, chan.source_id, chan.channel_id); 00617 } 00618 00619 if (conflicting) 00620 { 00621 cout<<"Skipping Update(" 00622 <<chan.si_standard.toAscii().constData()<<"): " 00623 <<chan.chan_num.toAscii().constData()<<endl; 00624 handle = false; 00625 } 00626 } 00627 00628 if (is_interactive && (kUpdateManual == action)) 00629 { 00630 // TODO Ask user how to update this channel.. 00631 } 00632 00633 bool updated = false; 00634 if (handle) 00635 { 00636 cout<<"Update("<<chan.si_standard.toAscii().constData()<<"): " 00637 <<chan.chan_num.toAscii().constData()<<endl; 00638 00639 ChannelUtil::UpdateInsertInfoFromDB(chan); 00640 00641 updated = ChannelUtil::UpdateChannel( 00642 chan.db_mplexid, 00643 chan.source_id, 00644 chan.channel_id, 00645 chan.callsign, 00646 chan.service_name, 00647 chan.chan_num, 00648 chan.service_id, 00649 chan.atsc_major_channel, 00650 chan.atsc_minor_channel, 00651 chan.use_on_air_guide, 00652 chan.hidden, chan.hidden_in_guide, 00653 chan.freqid, 00654 QString::null, 00655 QString::null, 00656 QString::null, 00657 chan.default_authority); 00658 } 00659 00660 if (filter) 00661 { 00662 if (!created_filter_transport) 00663 { 00664 filter_transport = transports[i]; 00665 filter_transport.channels.clear(); 00666 created_filter_transport = true; 00667 } 00668 filter_transport.channels.push_back(transports[i].channels[j]); 00669 } 00670 else if (!updated) 00671 { 00672 if (!created_transport) 00673 { 00674 new_transport = transports[i]; 00675 new_transport.channels.clear(); 00676 created_transport = true; 00677 } 00678 new_transport.channels.push_back(transports[i].channels[j]); 00679 } 00680 } 00681 00682 if (created_filter_transport) 00683 filtered.push_back(filter_transport); 00684 00685 if (created_transport) 00686 next_list.push_back(new_transport); 00687 } 00688 00689 return next_list; 00690 } 00691 00692 void ChannelImporter::CleanupDuplicates(ScanDTVTransportList &transports) const 00693 { 00694 ScanDTVTransportList no_dups; 00695 00696 DTVTunerType tuner_type = DTVTunerType::kTunerTypeATSC; 00697 if (!transports.empty()) 00698 tuner_type = transports[0].tuner_type; 00699 00700 bool is_dvbs = ((DTVTunerType::kTunerTypeDVBS1 == tuner_type) || 00701 (DTVTunerType::kTunerTypeDVBS2 == tuner_type)); 00702 00703 uint freq_mult = (is_dvbs) ? 1 : 1000; 00704 00705 vector<bool> ignore; 00706 ignore.resize(transports.size()); 00707 for (uint i = 0; i < transports.size(); i++) 00708 { 00709 if (ignore[i]) 00710 continue; 00711 00712 for (uint j = i+1; j < transports.size(); j++) 00713 { 00714 if (!transports[i].IsEqual( 00715 tuner_type, transports[j], 500 * freq_mult)) 00716 { 00717 continue; 00718 } 00719 00720 for (uint k = 0; k < transports[j].channels.size(); k++) 00721 { 00722 bool found_same = false; 00723 for (uint l = 0; l < transports[i].channels.size(); l++) 00724 { 00725 if (transports[j].channels[k].IsSameChannel( 00726 transports[i].channels[l])) 00727 { 00728 found_same = true; 00729 transports[i].channels[l].ImportExtraInfo( 00730 transports[j].channels[k]); 00731 } 00732 } 00733 if (!found_same) 00734 transports[i].channels.push_back(transports[j].channels[k]); 00735 } 00736 ignore[j] = true; 00737 } 00738 no_dups.push_back(transports[i]); 00739 } 00740 00741 transports = no_dups; 00742 } 00743 00744 void ChannelImporter::FilterServices(ScanDTVTransportList &transports) const 00745 { 00746 bool require_av = (m_service_requirements & kRequireAV) == kRequireAV; 00747 bool require_a = m_service_requirements & kRequireAudio; 00748 00749 for (uint i = 0; i < transports.size(); i++) 00750 { 00751 ChannelInsertInfoList filtered; 00752 for (uint k = 0; k < transports[i].channels.size(); k++) 00753 { 00754 if (m_fta_only && transports[i].channels[k].is_encrypted && 00755 transports[i].channels[k].decryption_status != kEncDecrypted) 00756 continue; 00757 00758 if (require_a && transports[i].channels[k].is_data_service) 00759 continue; 00760 00761 if (require_av && transports[i].channels[k].is_audio_service) 00762 continue; 00763 00764 // filter channels out only in channels.conf, i.e. not found 00765 if (transports[i].channels[k].in_channels_conf && 00766 !(transports[i].channels[k].in_pat || 00767 transports[i].channels[k].in_pmt || 00768 transports[i].channels[k].in_vct || 00769 transports[i].channels[k].in_nit || 00770 transports[i].channels[k].in_sdt)) 00771 continue; 00772 00773 filtered.push_back(transports[i].channels[k]); 00774 } 00775 transports[i].channels = filtered; 00776 } 00777 } 00778 00783 ScanDTVTransportList ChannelImporter::GetDBTransports( 00784 uint sourceid, ScanDTVTransportList &transports) const 00785 { 00786 ScanDTVTransportList not_in_scan; 00787 00788 DTVTunerType tuner_type = DTVTunerType::kTunerTypeATSC; 00789 if (!transports.empty()) 00790 tuner_type = transports[0].tuner_type; 00791 00792 bool is_dvbs = 00793 (DTVTunerType::kTunerTypeDVBS1 == tuner_type) || 00794 (DTVTunerType::kTunerTypeDVBS2 == tuner_type); 00795 00796 uint freq_mult = (is_dvbs) ? 1 : 1000; 00797 00798 MSqlQuery query(MSqlQuery::InitCon()); 00799 query.prepare( 00800 "SELECT mplexid " 00801 "FROM dtv_multiplex " 00802 "WHERE sourceid = :SOURCEID " 00803 "GROUP BY mplexid " 00804 "ORDER BY mplexid"); 00805 query.bindValue(":SOURCEID", sourceid); 00806 00807 if (!query.exec()) 00808 { 00809 MythDB::DBError("GetDBTransports()", query); 00810 return not_in_scan; 00811 } 00812 00813 while (query.next()) 00814 { 00815 uint mplexid = query.value(0).toUInt(); 00816 00817 ScanDTVTransport newt; 00818 if (!newt.FillFromDB(tuner_type, mplexid)) 00819 continue; 00820 00821 bool newt_found = false; 00822 QMap<uint,bool> found_chan; 00823 00824 for (uint i = 0; i < transports.size(); i++) 00825 { 00826 if (!transports[i].IsEqual(tuner_type, newt, 500 * freq_mult, true)) 00827 continue; 00828 00829 transports[i].mplex = mplexid; 00830 newt_found = true; 00831 for (uint j = 0; j < transports[i].channels.size(); j++) 00832 { 00833 ChannelInsertInfo &chan = transports[i].channels[j]; 00834 for (uint k = 0; k < newt.channels.size(); k++) 00835 { 00836 if (newt.channels[k].IsSameChannel(chan)) 00837 { 00838 found_chan[k] = true; 00839 chan.db_mplexid = mplexid; 00840 chan.channel_id = newt.channels[k].channel_id; 00841 } 00842 } 00843 } 00844 break; 00845 } 00846 00847 if (!newt_found) 00848 { 00849 /* XXX HACK -- begin 00850 * disabling adding transponders not found in the scan list 00851 * to the db list to avoid deleting many channels as off air 00852 * for a single transponder scan 00853 not_in_scan.push_back(newt); 00854 * XXX HACK -- end */ 00855 } 00856 else 00857 { 00858 ScanDTVTransport tmp = newt; 00859 tmp.channels.clear(); 00860 00861 for (uint k = 0; k < newt.channels.size(); k++) 00862 { 00863 if (!found_chan[k]) 00864 tmp.channels.push_back(newt.channels[k]); 00865 } 00866 00867 if (tmp.channels.size()) 00868 not_in_scan.push_back(tmp); 00869 } 00870 } 00871 00872 return not_in_scan; 00873 } 00874 00875 void ChannelImporter::FixUpOpenCable(ScanDTVTransportList &transports) 00876 { 00877 ChannelImporterBasicStats info; 00878 for (uint i = 0; i < transports.size(); i++) 00879 { 00880 for (uint j = 0; j < transports[i].channels.size(); j++) 00881 { 00882 ChannelInsertInfo &chan = transports[i].channels[j]; 00883 if (((chan.could_be_opencable && (chan.si_standard == "mpeg")) || 00884 chan.is_opencable) && !chan.in_vct) 00885 { 00886 chan.si_standard = "opencable"; 00887 } 00888 } 00889 } 00890 } 00891 00892 ChannelImporterBasicStats ChannelImporter::CollectStats( 00893 const ScanDTVTransportList &transports) 00894 { 00895 ChannelImporterBasicStats info; 00896 for (uint i = 0; i < transports.size(); i++) 00897 { 00898 for (uint j = 0; j < transports[i].channels.size(); j++) 00899 { 00900 const ChannelInsertInfo &chan = transports[i].channels[j]; 00901 int enc = (chan.is_encrypted) ? 00902 ((chan.decryption_status == kEncDecrypted) ? 2 : 1) : 0; 00903 info.atsc_channels[enc] += (chan.si_standard == "atsc"); 00904 info.dvb_channels [enc] += (chan.si_standard == "dvb"); 00905 info.mpeg_channels[enc] += (chan.si_standard == "mpeg"); 00906 info.scte_channels[enc] += (chan.si_standard == "opencable"); 00907 info.ntsc_channels[enc] += (chan.si_standard == "ntsc"); 00908 if (chan.si_standard != "ntsc") 00909 { 00910 info.prognum_cnt[chan.service_id]++; 00911 info.channum_cnt[map_str(chan.chan_num)]++; 00912 } 00913 if (chan.si_standard == "atsc") 00914 { 00915 info.atscnum_cnt[(chan.atsc_major_channel << 16) | 00916 (chan.atsc_minor_channel)]++; 00917 info.atscmin_cnt[chan.atsc_minor_channel]++; 00918 info.atscmaj_cnt[chan.atsc_major_channel]++; 00919 } 00920 if (chan.si_standard == "ntsc") 00921 { 00922 info.atscnum_cnt[(chan.atsc_major_channel << 16) | 00923 (chan.atsc_minor_channel)]++; 00924 } 00925 } 00926 } 00927 00928 return info; 00929 } 00930 00931 ChannelImporterUniquenessStats ChannelImporter::CollectUniquenessStats( 00932 const ScanDTVTransportList &transports, 00933 const ChannelImporterBasicStats &info) 00934 { 00935 ChannelImporterUniquenessStats stats; 00936 00937 for (uint i = 0; i < transports.size(); i++) 00938 { 00939 for (uint j = 0; j < transports[i].channels.size(); j++) 00940 { 00941 const ChannelInsertInfo &chan = transports[i].channels[j]; 00942 stats.unique_prognum += 00943 (info.prognum_cnt[chan.service_id] == 1) ? 1 : 0; 00944 stats.unique_channum += 00945 (info.channum_cnt[map_str(chan.chan_num)] == 1) ? 1 : 0; 00946 00947 if (chan.si_standard == "atsc") 00948 { 00949 stats.unique_atscnum += 00950 (info.atscnum_cnt[(chan.atsc_major_channel << 16) | 00951 (chan.atsc_minor_channel)] == 1) ? 1 : 0; 00952 stats.unique_atscmin += 00953 (info.atscmin_cnt[(chan.atsc_minor_channel)] == 1) ? 1 : 0; 00954 stats.max_atscmajcnt = max( 00955 stats.max_atscmajcnt, 00956 info.atscmaj_cnt[chan.atsc_major_channel]); 00957 } 00958 } 00959 } 00960 00961 stats.unique_total = (stats.unique_prognum + stats.unique_atscnum + 00962 stats.unique_atscmin + stats.unique_channum); 00963 00964 return stats; 00965 } 00966 00967 00968 QString ChannelImporter::FormatChannel( 00969 const ScanDTVTransport &transport, 00970 const ChannelInsertInfo &chan, 00971 const ChannelImporterBasicStats *info) 00972 { 00973 QString msg; 00974 QTextStream ssMsg(&msg); 00975 00976 ssMsg << transport.modulation.toString().toAscii().constData() 00977 << ":"; 00978 ssMsg << transport.frequency << ":"; 00979 00980 QString si_standard = (chan.si_standard=="opencable") ? 00981 QString("scte") : chan.si_standard; 00982 00983 if (si_standard == "atsc" || si_standard == "scte") 00984 ssMsg << (QString("%1:%2:%3-%4:%5:%6=%7=%8:%9") 00985 .arg(chan.callsign).arg(chan.chan_num) 00986 .arg(chan.atsc_major_channel) 00987 .arg(chan.atsc_minor_channel) 00988 .arg(chan.service_id) 00989 .arg(chan.vct_tsid) 00990 .arg(chan.vct_chan_tsid) 00991 .arg(chan.pat_tsid) 00992 .arg(si_standard)).toAscii().constData(); 00993 else if (si_standard == "dvb") 00994 ssMsg << (QString("%1:%2:%3:%4:%5:%6=%7:%8") 00995 .arg(chan.service_name).arg(chan.chan_num) 00996 .arg(chan.netid).arg(chan.orig_netid) 00997 .arg(chan.service_id) 00998 .arg(chan.sdt_tsid) 00999 .arg(chan.pat_tsid) 01000 .arg(si_standard)).toAscii().constData(); 01001 else 01002 ssMsg << (QString("%1:%2:%3:%4:%5") 01003 .arg(chan.callsign).arg(chan.chan_num) 01004 .arg(chan.service_id) 01005 .arg(chan.pat_tsid) 01006 .arg(si_standard)).toAscii().constData(); 01007 01008 if (info) 01009 { 01010 ssMsg <<"\t" 01011 << chan.channel_id; 01012 } 01013 01014 if (info) 01015 { 01016 ssMsg << ":" 01017 << (QString("cnt(pnum:%1,channum:%2)") 01018 .arg(info->prognum_cnt[chan.service_id]) 01019 .arg(info->channum_cnt[map_str(chan.chan_num)]) 01020 ).toAscii().constData(); 01021 if (chan.si_standard == "atsc") 01022 { 01023 ssMsg << 01024 (QString(":atsc_cnt(tot:%1,minor:%2)") 01025 .arg(info->atscnum_cnt[ 01026 (chan.atsc_major_channel << 16) | 01027 (chan.atsc_minor_channel)]) 01028 .arg(info->atscmin_cnt[ 01029 chan.atsc_minor_channel]) 01030 ).toAscii().constData(); 01031 } 01032 } 01033 01034 return msg; 01035 } 01036 01037 QString ChannelImporter::SimpleFormatChannel( 01038 const ScanDTVTransport &transport, 01039 const ChannelInsertInfo &chan) 01040 { 01041 QString msg; 01042 QTextStream ssMsg(&msg); 01043 01044 QString si_standard = (chan.si_standard=="opencable") ? 01045 QString("scte") : chan.si_standard; 01046 01047 if (si_standard == "atsc" || si_standard == "scte") 01048 { 01049 01050 if (si_standard == "atsc") 01051 ssMsg << (QString("%1-%2") 01052 .arg(chan.atsc_major_channel) 01053 .arg(chan.atsc_minor_channel)).toAscii().constData(); 01054 else 01055 ssMsg << (QString("%1-%2") 01056 .arg(chan.freqid) 01057 .arg(chan.service_id)).toAscii().constData(); 01058 01059 if (!chan.callsign.isEmpty()) 01060 ssMsg << (QString(" (%1)") 01061 .arg(chan.callsign)).toAscii().constData(); 01062 } 01063 else if (si_standard == "dvb") 01064 ssMsg << (QString("%1 (%2 %3)") 01065 .arg(chan.service_name).arg(chan.service_id) 01066 .arg(chan.netid)).toAscii().constData(); 01067 else 01068 ssMsg << (QString("%1-%2") 01069 .arg(chan.freqid).arg(chan.service_id)) 01070 .toAscii().constData(); 01071 01072 return msg; 01073 } 01074 01075 QString ChannelImporter::FormatChannels( 01076 const ScanDTVTransportList &transports, 01077 const ChannelImporterBasicStats &info) 01078 { 01079 QString msg; 01080 01081 for (uint i = 0; i < transports.size(); i++) 01082 for (uint j = 0; j < transports[i].channels.size(); j++) 01083 msg += FormatChannel(transports[i], transports[i].channels[j], 01084 &info) + "\n"; 01085 01086 return msg; 01087 } 01088 01089 QString ChannelImporter::GetSummary( 01090 uint transport_count, 01091 const ChannelImporterBasicStats &info, 01092 const ChannelImporterUniquenessStats &stats) 01093 { 01094 01095 QString msg = QObject::tr("Found %n transport(s):\n", "", transport_count); 01096 msg += QObject::tr("Channels: FTA Enc Dec\n") + 01097 QString("ATSC %1 %2 %3\n") 01098 .arg(info.atsc_channels[0],3).arg(info.atsc_channels[1],3) 01099 .arg(info.atsc_channels[2],3) + 01100 QString("DVB %1 %2 %3\n") 01101 .arg(info.dvb_channels [0],3).arg(info.dvb_channels [1],3) 01102 .arg(info.dvb_channels [2],3) + 01103 QString("SCTE %1 %2 %3\n") 01104 .arg(info.scte_channels[0],3).arg(info.scte_channels[1],3) 01105 .arg(info.scte_channels[2],3) + 01106 QString("MPEG %1 %2 %3\n") 01107 .arg(info.mpeg_channels[0],3).arg(info.mpeg_channels[1],3) 01108 .arg(info.mpeg_channels[2],3) + 01109 QString("NTSC %1\n").arg(info.ntsc_channels[0],3) + 01110 QObject::tr("Unique: prog %1 atsc %2 atsc minor %3 channum %4\n") 01111 .arg(stats.unique_prognum).arg(stats.unique_atscnum) 01112 .arg(stats.unique_atscmin).arg(stats.unique_channum) + 01113 QObject::tr("Max atsc major count: %1") 01114 .arg(stats.max_atscmajcnt); 01115 01116 return msg; 01117 } 01118 01119 bool ChannelImporter::IsType( 01120 const ChannelImporterBasicStats &info, 01121 const ChannelInsertInfo &chan, ChannelType type) 01122 { 01123 switch (type) 01124 { 01125 case kATSCNonConflicting: 01126 return ((chan.si_standard == "atsc") && 01127 (info.atscnum_cnt[(chan.atsc_major_channel << 16) | 01128 (chan.atsc_minor_channel)] == 1)); 01129 01130 case kDVBNonConflicting: 01131 return ((chan.si_standard == "dvb") && 01132 (info.prognum_cnt[chan.service_id] == 1)); 01133 01134 case kMPEGNonConflicting: 01135 return ((chan.si_standard == "mpeg") && 01136 (info.channum_cnt[map_str(chan.chan_num)] == 1)); 01137 01138 case kSCTENonConflicting: 01139 return (((chan.si_standard == "scte") || 01140 (chan.si_standard == "opencable")) && 01141 (info.channum_cnt[map_str(chan.chan_num)] == 1)); 01142 01143 case kNTSCNonConflicting: 01144 return ((chan.si_standard == "ntsc") && 01145 (info.atscnum_cnt[(chan.atsc_major_channel << 16) | 01146 (chan.atsc_minor_channel)] == 1)); 01147 01148 case kATSCConflicting: 01149 return ((chan.si_standard == "atsc") && 01150 (info.atscnum_cnt[(chan.atsc_major_channel << 16) | 01151 (chan.atsc_minor_channel)] != 1)); 01152 01153 case kDVBConflicting: 01154 return ((chan.si_standard == "dvb") && 01155 (info.prognum_cnt[chan.service_id] != 1)); 01156 01157 case kMPEGConflicting: 01158 return ((chan.si_standard == "mpeg") && 01159 (info.channum_cnt[map_str(chan.chan_num)] != 1)); 01160 01161 case kSCTEConflicting: 01162 return (((chan.si_standard == "scte") || 01163 (chan.si_standard == "opencable")) && 01164 (info.channum_cnt[map_str(chan.chan_num)] != 1)); 01165 01166 case kNTSCConflicting: 01167 return ((chan.si_standard == "ntsc") && 01168 (info.atscnum_cnt[(chan.atsc_major_channel << 16) | 01169 (chan.atsc_minor_channel)] != 1)); 01170 } 01171 return false; 01172 } 01173 01174 void ChannelImporter::CountChannels( 01175 const ScanDTVTransportList &transports, 01176 const ChannelImporterBasicStats &info, 01177 ChannelType type, uint &new_chan, uint &old_chan) 01178 { 01179 new_chan = old_chan = 0; 01180 for (uint i = 0; i < transports.size(); i++) 01181 { 01182 for (uint j = 0; j < transports[i].channels.size(); j++) 01183 { 01184 ChannelInsertInfo chan = transports[i].channels[j]; 01185 if (IsType(info, chan, type)) 01186 { 01187 if (chan.channel_id) 01188 old_chan++; 01189 else 01190 new_chan++; 01191 } 01192 } 01193 } 01194 } 01195 01196 QString ChannelImporter::ComputeSuggestedChannelNum( 01197 const ChannelImporterBasicStats &info, 01198 const ScanDTVTransport &transport, 01199 const ChannelInsertInfo &chan) 01200 { 01201 static QMutex last_free_lock; 01202 static QMap<uint,uint> last_free_chan_num_map; 01203 01204 QString channelFormat = "%1_%2"; 01205 QString chan_num = channelFormat 01206 .arg(chan.atsc_major_channel) 01207 .arg(chan.atsc_minor_channel); 01208 01209 if (!chan.atsc_minor_channel) 01210 { 01211 if (chan.si_standard == "dvb") 01212 { 01213 chan_num = QString("%1") 01214 .arg(chan.service_id); 01215 } 01216 else 01217 chan_num = QString("%1-%2") 01218 .arg(chan.freqid) 01219 .arg(chan.service_id); 01220 } 01221 01222 if (!ChannelUtil::IsConflicting(chan_num, chan.source_id)) 01223 return chan_num; 01224 01225 QMutexLocker locker(&last_free_lock); 01226 uint last_free_chan_num = last_free_chan_num_map[chan.source_id]; 01227 for (last_free_chan_num++; ; last_free_chan_num++) 01228 { 01229 chan_num = QString::number(last_free_chan_num); 01230 if (!ChannelUtil::IsConflicting(chan_num, chan.source_id)) 01231 break; 01232 } 01233 last_free_chan_num_map[chan.source_id] = last_free_chan_num; 01234 01235 return chan_num; 01236 } 01237 01238 ChannelImporter::DeleteAction 01239 ChannelImporter::QueryUserDelete(const QString &msg) 01240 { 01241 DeleteAction action = kDeleteAll; 01242 if (use_gui) 01243 { 01244 QStringList buttons; 01245 buttons.push_back(QObject::tr("Delete all")); 01246 buttons.push_back(QObject::tr("Set all invisible")); 01247 // buttons.push_back(QObject::tr("Handle manually")); 01248 buttons.push_back(QObject::tr("Ignore all")); 01249 01250 DialogCode ret; 01251 do 01252 { 01253 ret = MythPopupBox::ShowButtonPopup( 01254 GetMythMainWindow(), QObject::tr("Channel Importer"), 01255 msg, buttons, kDialogCodeButton0); 01256 01257 ret = (kDialogCodeRejected == ret) ? kDialogCodeButton2 : ret; 01258 01259 } while (!(kDialogCodeButton0 <= ret && ret <= kDialogCodeButton3)); 01260 01261 action = (kDialogCodeButton0 == ret) ? kDeleteAll : action; 01262 action = (kDialogCodeButton1 == ret) ? kDeleteInvisibleAll : action; 01263 action = (kDialogCodeButton2 == ret) ? kDeleteIgnoreAll : action; 01264 // action = (kDialogCodeButton2 == ret) ? kDeleteManual : action; 01265 // action = (kDialogCodeButton3 == ret) ? kDeleteIgnoreAll : action; 01266 } 01267 else if (is_interactive) 01268 { 01269 cout << msg.toAscii().constData() << endl; 01270 cout << "Do you want to:" << endl; 01271 cout << "1. Delete all" << endl; 01272 cout << "2. Set all invisible" << endl; 01273 // cout << "3. Handle manually" << endl; 01274 cout << "4. Ignore all" << endl; 01275 while (true) 01276 { 01277 string ret; 01278 cin >> ret; 01279 bool ok; 01280 uint val = QString(ret.c_str()).toUInt(&ok); 01281 if (ok && (1 <= val) && (val <= 3)) 01282 { 01283 action = (1 == val) ? kDeleteAll : action; 01284 action = (2 == val) ? kDeleteInvisibleAll : action; 01285 //action = (3 == val) ? kDeleteManual : action; 01286 action = (3 == val) ? kDeleteIgnoreAll : action; 01287 action = (4 == val) ? kDeleteIgnoreAll : action; 01288 break; 01289 } 01290 else 01291 { 01292 //cout << "Please enter either 1, 2, 3 or 4:" << endl; 01293 cout << "Please enter either 1, 2 or 4:" << endl;// 01294 } 01295 } 01296 } 01297 01298 return action; 01299 } 01300 01301 ChannelImporter::InsertAction 01302 ChannelImporter::QueryUserInsert(const QString &msg) 01303 { 01304 InsertAction action = kInsertAll; 01305 if (use_gui) 01306 { 01307 QStringList buttons; 01308 buttons.push_back(QObject::tr("Insert all")); 01309 buttons.push_back(QObject::tr("Insert manually")); 01310 buttons.push_back(QObject::tr("Ignore all")); 01311 01312 DialogCode ret; 01313 do 01314 { 01315 ret = MythPopupBox::ShowButtonPopup( 01316 GetMythMainWindow(), QObject::tr("Channel Importer"), 01317 msg, buttons, kDialogCodeButton0); 01318 01319 ret = (kDialogCodeRejected == ret) ? kDialogCodeButton2 : ret; 01320 01321 } while (!(kDialogCodeButton0 <= ret && ret <= kDialogCodeButton2)); 01322 01323 action = (kDialogCodeButton0 == ret) ? kInsertAll : action; 01324 action = (kDialogCodeButton1 == ret) ? kInsertManual : action; 01325 action = (kDialogCodeButton2 == ret) ? kInsertIgnoreAll : action; 01326 } 01327 else if (is_interactive) 01328 { 01329 cout << msg.toAscii().constData() << endl; 01330 cout << "Do you want to:" << endl; 01331 cout << "1. Insert all" << endl; 01332 cout << "2. Insert manually" << endl; 01333 cout << "3. Ignore all" << endl; 01334 while (true) 01335 { 01336 string ret; 01337 cin >> ret; 01338 bool ok; 01339 uint val = QString(ret.c_str()).toUInt(&ok); 01340 if (ok && (1 <= val) && (val <= 3)) 01341 { 01342 action = (1 == val) ? kInsertAll : action; 01343 action = (2 == val) ? kInsertManual : action; 01344 action = (3 == val) ? kInsertIgnoreAll : action; 01345 break; 01346 } 01347 else 01348 { 01349 cout << "Please enter either 1, 2, or 3:" << endl; 01350 } 01351 } 01352 } 01353 01354 return action; 01355 } 01356 01357 ChannelImporter::UpdateAction 01358 ChannelImporter::QueryUserUpdate(const QString &msg) 01359 { 01360 UpdateAction action = kUpdateAll; 01361 01362 if (use_gui) 01363 { 01364 QStringList buttons; 01365 buttons.push_back(QObject::tr("Update all")); 01366 buttons.push_back(QObject::tr("Update manually")); 01367 buttons.push_back(QObject::tr("Ignore all")); 01368 01369 DialogCode ret; 01370 do 01371 { 01372 ret = MythPopupBox::ShowButtonPopup( 01373 GetMythMainWindow(), QObject::tr("Channel Importer"), 01374 msg, buttons, kDialogCodeButton0); 01375 01376 ret = (kDialogCodeRejected == ret) ? kDialogCodeButton2 : ret; 01377 01378 } while (!(kDialogCodeButton0 <= ret && ret <= kDialogCodeButton2)); 01379 01380 action = (kDialogCodeButton0 == ret) ? kUpdateAll : action; 01381 action = (kDialogCodeButton1 == ret) ? kUpdateManual : action; 01382 action = (kDialogCodeButton2 == ret) ? kUpdateIgnoreAll : action; 01383 } 01384 else if (is_interactive) 01385 { 01386 cout << msg.toAscii().constData() 01387 << endl 01388 << QObject::tr("Do you want to:").toAscii().constData() 01389 << endl 01390 << "1. " << QObject::tr("Update all").toAscii().constData() 01391 << endl 01392 << "2. " << QObject::tr("Update manually").toAscii().constData() 01393 << endl 01394 << "3. " << QObject::tr("Ignore all").toAscii().constData() 01395 << endl; 01396 while (true) 01397 { 01398 string ret; 01399 cin >> ret; 01400 bool ok; 01401 uint val = QString(ret.c_str()).toUInt(&ok); 01402 if (ok && (1 <= val) && (val <= 3)) 01403 { 01404 action = (1 == val) ? kUpdateAll : action; 01405 action = (2 == val) ? kUpdateManual : action; 01406 action = (3 == val) ? kUpdateIgnoreAll : action; 01407 break; 01408 } 01409 else 01410 { 01411 cout << QObject::tr( 01412 "Please enter either 1, 2, or 3:") 01413 .toAscii().constData() << endl; 01414 } 01415 } 01416 } 01417 01418 return action; 01419 } 01420 01421 OkCancelType ChannelImporter::ShowManualChannelPopup( 01422 MythMainWindow *parent, QString title, 01423 QString message, QString &text) 01424 { 01425 MythPopupBox *popup = new MythPopupBox(parent, title.toAscii().constData()); 01426 01427 popup->addLabel(message, MythPopupBox::Medium, true); 01428 01429 MythLineEdit *textEdit = new MythLineEdit(popup); 01430 01431 QString orig_text = text; 01432 text = ""; 01433 textEdit->setText(text); 01434 popup->addWidget(textEdit); 01435 01436 popup->addButton(QObject::tr("OK"), popup, SLOT(accept())); 01437 popup->addButton(QObject::tr("Suggest")); 01438 popup->addButton(QObject::tr("Cancel"), popup, SLOT(reject())); 01439 popup->addButton(QObject::tr("Cancel All")); 01440 01441 textEdit->setFocus(); 01442 01443 DialogCode dc = popup->ExecPopup(); 01444 if (kDialogCodeButton1 == dc) 01445 { 01446 popup->hide(); 01447 popup->deleteLater(); 01448 01449 popup = new MythPopupBox(parent, title.toAscii().constData()); 01450 popup->addLabel(message, MythPopupBox::Medium, true); 01451 01452 textEdit = new MythLineEdit(popup); 01453 01454 text = orig_text; 01455 textEdit->setText(text); 01456 popup->addWidget(textEdit); 01457 01458 popup->addButton(QObject::tr("OK"), popup, SLOT(accept()))->setFocus(); 01459 popup->addButton(QObject::tr("Cancel"), popup, SLOT(reject())); 01460 popup->addButton(QObject::tr("Cancel All")); 01461 01462 dc = popup->ExecPopup(); 01463 } 01464 01465 bool ok = (kDialogCodeAccepted == dc); 01466 if (ok) 01467 text = textEdit->text(); 01468 01469 popup->hide(); 01470 popup->deleteLater(); 01471 01472 return (ok) ? kOCTOk : 01473 ((kDialogCodeRejected == dc) ? kOCTCancel : kOCTCancelAll); 01474 } 01475 01476 OkCancelType ChannelImporter::QueryUserResolve( 01477 const ChannelImporterBasicStats &info, 01478 const ScanDTVTransport &transport, 01479 ChannelInsertInfo &chan) 01480 { 01481 QString msg = QObject::tr( 01482 "Channel %1 was found to be in conflict with other channels. ") 01483 .arg(SimpleFormatChannel(transport, chan)); 01484 01485 OkCancelType ret = kOCTCancel; 01486 01487 if (use_gui) 01488 { 01489 while (true) 01490 { 01491 QString msg2 = msg; 01492 msg2 += QObject::tr("Please enter a unique channel number. "); 01493 01494 QString val = ComputeSuggestedChannelNum(info, transport, chan); 01495 ret = ShowManualChannelPopup( 01496 GetMythMainWindow(), QObject::tr("Channel Importer"), 01497 msg2, val); 01498 01499 if (kOCTOk != ret) 01500 break; // user canceled.. 01501 01502 bool ok = (val.length() >= 1); 01503 ok = ok && ((val[0] >= '0') && (val[0] <= '9')); 01504 ok = ok && !ChannelUtil::IsConflicting( 01505 val, chan.source_id, chan.channel_id); 01506 01507 chan.chan_num = (ok) ? val : chan.chan_num; 01508 if (ok) 01509 break; 01510 } 01511 } 01512 else if (is_interactive) 01513 { 01514 cout << msg.toAscii().constData() << endl; 01515 01516 QString cancelStr = QObject::tr("Cancel").toLower(); 01517 QString cancelAllStr = QObject::tr("Cancel All").toLower(); 01518 QString msg2 = QObject::tr( 01519 "Please enter a non-conflicting channel number " 01520 "(or type %1 to skip, %2 to skip all): ") 01521 .arg(cancelStr).arg(cancelAllStr); 01522 01523 while (true) 01524 { 01525 cout << msg2.toAscii().constData() << endl; 01526 string sret; 01527 cin >> sret; 01528 QString val = QString(sret.c_str()); 01529 if (val.toLower() == cancelStr) 01530 { 01531 ret = kOCTCancel; 01532 break; // user canceled.. 01533 } 01534 if (val.toLower() == cancelAllStr) 01535 { 01536 ret = kOCTCancelAll; 01537 break; // user canceled.. 01538 } 01539 01540 bool ok = (val.length() >= 1); 01541 ok = ok && ((val[0] >= '0') && (val[0] <= '9')); 01542 ok = ok && !ChannelUtil::IsConflicting( 01543 val, chan.source_id, chan.channel_id); 01544 01545 chan.chan_num = (ok) ? val : chan.chan_num; 01546 if (ok) 01547 { 01548 ret = kOCTOk; 01549 break; 01550 } 01551 } 01552 } 01553 01554 return ret; 01555 } 01556 01557 OkCancelType ChannelImporter::QueryUserInsert( 01558 const ChannelImporterBasicStats &info, 01559 const ScanDTVTransport &transport, 01560 ChannelInsertInfo &chan) 01561 { 01562 QString msg = QObject::tr( 01563 "You chose to manually insert channel %1.") 01564 .arg(SimpleFormatChannel(transport, chan)); 01565 01566 OkCancelType ret = kOCTCancel; 01567 01568 if (use_gui) 01569 { 01570 while (true) 01571 { 01572 QString msg2 = msg; 01573 msg2 += QObject::tr("Please enter a unique channel number. "); 01574 01575 QString val = ComputeSuggestedChannelNum(info, transport, chan); 01576 ret = ShowManualChannelPopup( 01577 GetMythMainWindow(), QObject::tr("Channel Importer"), 01578 msg2, val); 01579 01580 if (kOCTOk != ret) 01581 break; // user canceled.. 01582 01583 bool ok = (val.length() >= 1); 01584 ok = ok && ((val[0] >= '0') && (val[0] <= '9')); 01585 ok = ok && !ChannelUtil::IsConflicting( 01586 val, chan.source_id, chan.channel_id); 01587 01588 chan.chan_num = (ok) ? val : chan.chan_num; 01589 if (ok) 01590 { 01591 ret = kOCTOk; 01592 break; 01593 } 01594 } 01595 } 01596 else if (is_interactive) 01597 { 01598 cout << msg.toAscii().constData() << endl; 01599 01600 QString cancelStr = QObject::tr("Cancel").toLower(); 01601 QString cancelAllStr = QObject::tr("Cancel All").toLower(); 01602 QString msg2 = QObject::tr( 01603 "Please enter a non-conflicting channel number " 01604 "(or type %1 to skip, %2 to skip all): ") 01605 .arg(cancelStr).arg(cancelAllStr); 01606 01607 while (true) 01608 { 01609 cout << msg2.toAscii().constData() << endl; 01610 string sret; 01611 cin >> sret; 01612 QString val = QString(sret.c_str()); 01613 if (val.toLower() == cancelStr) 01614 { 01615 ret = kOCTCancel; 01616 break; // user canceled.. 01617 } 01618 if (val.toLower() == cancelAllStr) 01619 { 01620 ret = kOCTCancelAll; 01621 break; // user canceled.. 01622 } 01623 01624 bool ok = (val.length() >= 1); 01625 ok = ok && ((val[0] >= '0') && (val[0] <= '9')); 01626 ok = ok && !ChannelUtil::IsConflicting( 01627 val, chan.source_id, chan.channel_id); 01628 01629 chan.chan_num = (ok) ? val : chan.chan_num; 01630 if (ok) 01631 { 01632 ret = kOCTOk; 01633 break; 01634 } 01635 } 01636 } 01637 01638 return ret; 01639 }
1.7.6.1