File indexing completed on 2024-05-12 04:52:15

0001 /*
0002  * dvbmanager.cpp
0003  *
0004  * Copyright (C) 2008-2011 Christoph Pfister <christophpfister@gmail.com>
0005  *
0006  * This program is free software; you can redistribute it and/or modify
0007  * it under the terms of the GNU General Public License as published by
0008  * the Free Software Foundation; either version 2 of the License, or
0009  * (at your option) any later version.
0010  *
0011  * This program is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU General Public License along
0017  * with this program; if not, write to the Free Software Foundation, Inc.,
0018  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
0019  */
0020 
0021 #include "../log.h"
0022 
0023 #include <config-kaffeine.h>
0024 #include <KConfigGroup>
0025 #include <KSharedConfig>
0026 #include <QDir>
0027 #include <QPluginLoader>
0028 #include <QRegularExpressionMatch>
0029 #include <QStandardPaths>
0030 
0031 #include "dvbconfig.h"
0032 #include "dvbdevice.h"
0033 #include "dvbdevice_linux.h"
0034 #include "dvbepg.h"
0035 #include "dvbliveview.h"
0036 #include "dvbmanager.h"
0037 #include "dvbmanager_p.h"
0038 #include "dvbsi.h"
0039 #include "xmltv.h"
0040 
0041 DvbManager::DvbManager(MediaWidget *mediaWidget_, QWidget *parent_) : QObject(parent_),
0042     parent(parent_), mediaWidget(mediaWidget_), channelView(NULL), dvbDumpEnabled(false)
0043 {
0044     channelModel = DvbChannelModel::createSqlModel(this);
0045     recordingModel = new DvbRecordingModel(this, this);
0046     epgModel = new DvbEpgModel(this, this);
0047     liveView = new DvbLiveView(this, this);
0048     xmlTv = new XmlTv(this);
0049 
0050     readDeviceConfigs();
0051     updateSourceMapping();
0052 
0053     loadDeviceManager();
0054 
0055     DvbSiText::setOverride6937(override6937Charset());
0056 
0057     QString xmlFile = getXmltvFileName();
0058 
0059     if (!xmlFile.isEmpty())
0060         xmlTv->addFile(xmlFile);
0061 }
0062 
0063 DvbManager::~DvbManager()
0064 {
0065     writeDeviceConfigs();
0066 
0067     // we need an explicit deletion order (device users ; devices ; device manager)
0068 
0069     delete xmlTv;
0070     delete epgModel;
0071     epgModel = NULL;
0072     delete recordingModel;
0073 
0074     foreach (const DvbDeviceConfig &deviceConfig, deviceConfigs) {
0075         delete deviceConfig.device;
0076     }
0077 }
0078 
0079 DvbDevice *DvbManager::requestDevice(const QString &source, const DvbTransponder &transponder,
0080     DvbManager::RequestType requestType)
0081 {
0082     Q_ASSERT(requestType != Exclusive);
0083     // FIXME call DvbEpgModel::startEventFilter / DvbEpgModel::stopEventFilter here?
0084 
0085     reacquireDevice = false;
0086     for (int i = 0; i < deviceConfigs.size(); ++i) {
0087         const DvbDeviceConfig &it = deviceConfigs.at(i);
0088 
0089         if ((it.device == NULL) || (it.useCount < 1)) {
0090             continue;
0091         }
0092 
0093         if ((it.source == source) && it.transponder.corresponds(transponder)) {
0094             ++deviceConfigs[i].useCount;
0095 
0096             if (requestType == Prioritized) {
0097                 ++deviceConfigs[i].prioritizedUseCount;
0098             }
0099 
0100             return it.device;
0101         }
0102     }
0103 
0104     for (int i = 0; i < deviceConfigs.size(); ++i) {
0105         const DvbDeviceConfig &it = deviceConfigs.at(i);
0106 
0107         if ((it.device == NULL) || (it.useCount != 0)) {
0108             continue;
0109         }
0110 
0111         foreach (const DvbConfig &config, it.configs) {
0112             if (config->name == source) {
0113                 DvbDevice *device = it.device;
0114 
0115                 if (!device->acquire(config.constData())) {
0116                     continue;
0117                 }
0118 
0119                 deviceConfigs[i].useCount = 1;
0120 
0121                 if (requestType == Prioritized) {
0122                     deviceConfigs[i].prioritizedUseCount = 1;
0123                 }
0124 
0125                 deviceConfigs[i].source = source;
0126                 deviceConfigs[i].transponder = transponder;
0127                 device->tune(transponder);
0128                 return device;
0129             }
0130         }
0131     }
0132 
0133     if (requestType != Prioritized) {
0134         return NULL;
0135     }
0136 
0137     for (int i = 0; i < deviceConfigs.size(); ++i) {
0138         const DvbDeviceConfig &it = deviceConfigs.at(i);
0139 
0140         if ((it.device == NULL) || (it.useCount == 0) || (it.prioritizedUseCount != 0)) {
0141             continue;
0142         }
0143 
0144         foreach (const DvbConfig &config, it.configs) {
0145             if (config->name == source) {
0146                 deviceConfigs[i].useCount = 1;
0147                 deviceConfigs[i].prioritizedUseCount = 1;
0148                 deviceConfigs[i].source = source;
0149                 deviceConfigs[i].transponder = transponder;
0150 
0151                 DvbDevice *device = it.device;
0152                 device->reacquire(config.constData());
0153                 device->tune(transponder);
0154                 reacquireDevice = true;
0155                 return device;
0156             }
0157         }
0158     }
0159 
0160     return NULL;
0161 }
0162 
0163 DvbDevice *DvbManager::requestExclusiveDevice(const QString &source)
0164 {
0165     for (int i = 0; i < deviceConfigs.size(); ++i) {
0166         const DvbDeviceConfig &it = deviceConfigs.at(i);
0167 
0168         if ((it.device == NULL) || (it.useCount != 0)) {
0169             continue;
0170         }
0171 
0172         foreach (const DvbConfig &config, it.configs) {
0173             if (config->name == source) {
0174                 DvbDevice *device = it.device;
0175 
0176                 if (!device->acquire(config.constData())) {
0177                     continue;
0178                 }
0179 
0180                 deviceConfigs[i].useCount = -1;
0181                 deviceConfigs[i].source.clear();
0182                 return device;
0183             }
0184         }
0185     }
0186 
0187     return NULL;
0188 }
0189 
0190 void DvbManager::releaseDevice(DvbDevice *device, RequestType requestType)
0191 {
0192     for (int i = 0; i < deviceConfigs.size(); ++i) {
0193         const DvbDeviceConfig &it = deviceConfigs.at(i);
0194 
0195         if (it.device == device) {
0196             switch (requestType) {
0197             case Prioritized:
0198                 --deviceConfigs[i].prioritizedUseCount;
0199                 Q_ASSERT(it.prioritizedUseCount >= 0);
0200             // fall through
0201             case Shared:
0202                 --deviceConfigs[i].useCount;
0203                 Q_ASSERT(it.useCount >= 0);
0204                 Q_ASSERT(it.useCount >= it.prioritizedUseCount);
0205 
0206                 if (it.useCount == 0) {
0207                     it.device->release();
0208                 }
0209 
0210                 break;
0211             case Exclusive:
0212                 Q_ASSERT(it.useCount == -1);
0213                 Q_ASSERT(it.prioritizedUseCount == 0);
0214                 deviceConfigs[i].useCount = 0;
0215                 it.device->release();
0216                 break;
0217             }
0218 
0219             break;
0220         }
0221     }
0222 }
0223 
0224 QList<DvbDeviceConfig> DvbManager::getDeviceConfigs() const
0225 {
0226     return deviceConfigs;
0227 }
0228 
0229 void DvbManager::updateDeviceConfigs(const QList<DvbDeviceConfigUpdate> &configUpdates)
0230 {
0231     for (int i = 0; i < configUpdates.size(); ++i) {
0232         const DvbDeviceConfigUpdate &configUpdate = configUpdates.at(i);
0233 
0234         for (int j = i;; ++j) {
0235             Q_ASSERT(j < deviceConfigs.size());
0236 
0237             if (&deviceConfigs.at(j) == configUpdate.deviceConfig) {
0238                 if (i != j) {
0239                     deviceConfigs.move(j, i);
0240                 }
0241 
0242                 deviceConfigs[i].configs = configUpdate.configs;
0243                 break;
0244             }
0245         }
0246     }
0247 
0248     for (int i = configUpdates.size(); i < deviceConfigs.size(); ++i) {
0249         if (deviceConfigs.at(i).device != NULL) {
0250             deviceConfigs[i].configs.clear();
0251         } else {
0252             deviceConfigs.removeAt(i);
0253             --i;
0254         }
0255     }
0256 
0257     updateSourceMapping();
0258 }
0259 
0260 QDate DvbManager::getScanDataDate()
0261 {
0262     if (!scanDataDate.isValid()) {
0263         readScanData();
0264     }
0265 
0266     return scanDataDate;
0267 }
0268 
0269 QStringList DvbManager::getScanSources(TransmissionType type)
0270 {
0271     if (scanData.isEmpty()) {
0272         readScanData();
0273     }
0274 
0275     return scanSources.value(type);
0276 }
0277 
0278 QString DvbManager::getAutoScanSource(const QString &source) const
0279 {
0280     QPair<TransmissionType, QString> scanSource = sourceMapping.value(source);
0281 
0282     if (scanSource.second.isEmpty()) {
0283         qCWarning(logDvb, "Invalid source for autoscan");
0284         return QString();
0285     }
0286 
0287     if (((scanSource.first == DvbT) || (scanSource.first == IsdbT)) && (scanSource.second.startsWith(QLatin1String("AUTO")))) {
0288         return scanSource.second;
0289     }
0290 
0291     return QString();
0292 }
0293 
0294 QList<DvbTransponder> DvbManager::getTransponders(DvbDevice *device, const QString &source)
0295 {
0296     if (scanData.isEmpty()) {
0297         readScanData();
0298     }
0299 
0300     QPair<TransmissionType, QString> scanSource = sourceMapping.value(source);
0301 
0302     if (scanSource.second.isEmpty()) {
0303         qCWarning(logDvb, "Invalid source. Can't get transponder");
0304         return QList<DvbTransponder>();
0305     }
0306 
0307     if ((scanSource.first == DvbS) &&
0308         ((device->getTransmissionTypes() & DvbDevice::DvbS2) != 0)) {
0309         scanSource.first = DvbS2;
0310     }
0311 
0312     if ((scanSource.first == DvbT) &&
0313         ((device->getTransmissionTypes() & DvbDevice::DvbT2) != 0)) {
0314         scanSource.first = DvbT2;
0315     }
0316 
0317     return scanData.value(scanSource);
0318 }
0319 
0320 bool DvbManager::updateScanData(const QByteArray &data)
0321 {
0322     QByteArray uncompressed = qUncompress(data);
0323 
0324     if (uncompressed.isEmpty()) {
0325         qCWarning(logDvb, "Failed to uncompress the scan data file");
0326         return false;
0327     }
0328 
0329     if (!DvbScanData(uncompressed).readDate().isValid()) {
0330         qCWarning(logDvb, "Invalid format at the scan data file");
0331         return false;
0332     }
0333 
0334     QFile file(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1String("/scanfile.dvb"));
0335 
0336     if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
0337         qCWarning(logDvb, "Cannot open %s", qPrintable(file.fileName()));
0338         return false;
0339     }
0340 
0341     file.write(uncompressed);
0342     file.close();
0343 
0344     readScanData();
0345     return true;
0346 }
0347 
0348 QString DvbManager::getRecordingFolder() const
0349 {
0350     return KSharedConfig::openConfig()->group("DVB").readEntry("RecordingFolder", QDir::homePath());
0351 }
0352 
0353 QString DvbManager::getTimeShiftFolder() const
0354 {
0355     return KSharedConfig::openConfig()->group("DVB").readEntry("TimeShiftFolder", QDir::homePath());
0356 }
0357 
0358 QString DvbManager::getXmltvFileName() const
0359 {
0360     return KSharedConfig::openConfig()->group("DVB").readEntry("XmltvFileName", "");
0361 }
0362 
0363 int DvbManager::getBeginMargin() const
0364 {
0365     return KSharedConfig::openConfig()->group("DVB").readEntry("BeginMargin", 300);
0366 }
0367 
0368 int DvbManager::getEndMargin() const
0369 {
0370     return KSharedConfig::openConfig()->group("DVB").readEntry("EndMargin", 600);
0371 }
0372 
0373 QString DvbManager::getNamingFormat() const
0374 {
0375     return KSharedConfig::openConfig()->group("DVB").readEntry("NamingFormat", "%title");
0376 }
0377 
0378 QString DvbManager::getRecordingRegex() const
0379 {
0380     return KSharedConfig::openConfig()->group("DVB").readEntry("RecordingRegex", "");
0381 }
0382 
0383 QStringList DvbManager::getRecordingRegexList() const
0384 {
0385     return KSharedConfig::openConfig()->group("DVB").readEntry("RecordingRegexList", QStringList());
0386 }
0387 
0388 QList<int> DvbManager::getRecordingRegexPriorityList() const
0389 {
0390     return KSharedConfig::openConfig()->group("DVB").readEntry("RecordingRegexPriorityList", QList<int>());
0391 }
0392 
0393 QString DvbManager::getActionAfterRecording() const
0394 {
0395     return KSharedConfig::openConfig()->group("DVB").readEntry("ActionAfterRecording", "");
0396 }
0397 
0398 bool DvbManager::override6937Charset() const
0399 {
0400     return KSharedConfig::openConfig()->group("DVB").readEntry("Override6937", false);
0401 }
0402 
0403 bool DvbManager::isScanWhenIdle() const
0404 {
0405     return KSharedConfig::openConfig()->group("DVB").readEntry("ScanWhenIdle", false);
0406 }
0407 
0408 bool DvbManager::createInfoFile() const
0409 {
0410     return KSharedConfig::openConfig()->group("DVB").readEntry("CreateInfoFile", false);
0411 }
0412 
0413 bool DvbManager::disableEpg() const
0414 {
0415     return KSharedConfig::openConfig()->group("DVB").readEntry("DisableEpg", false);
0416 }
0417 
0418 void DvbManager::setRecordingFolder(const QString &path)
0419 {
0420     KSharedConfig::openConfig()->group("DVB").writeEntry("RecordingFolder", path);
0421 }
0422 
0423 void DvbManager::setTimeShiftFolder(const QString &path)
0424 {
0425     KSharedConfig::openConfig()->group("DVB").writeEntry("TimeShiftFolder", path);
0426 }
0427 
0428 void DvbManager::setXmltvFileName(const QString &path)
0429 {
0430     KSharedConfig::openConfig()->group("DVB").writeEntry("XmltvFileName", path);
0431     xmlTv->clear();
0432     xmlTv->addFile(path);
0433 }
0434 
0435 void DvbManager::setBeginMargin(int beginMargin)
0436 {
0437     KSharedConfig::openConfig()->group("DVB").writeEntry("BeginMargin", beginMargin);
0438 }
0439 
0440 void DvbManager::setEndMargin(int endMargin)
0441 {
0442     KSharedConfig::openConfig()->group("DVB").writeEntry("EndMargin", endMargin);
0443 }
0444 
0445 void DvbManager::setNamingFormat(QString namingFormat)
0446 {
0447     KSharedConfig::openConfig()->group("DVB").writeEntry("NamingFormat", namingFormat);
0448 }
0449 
0450 void DvbManager::setRecordingRegex(QString regex)
0451 {
0452     KSharedConfig::openConfig()->group("DVB").writeEntry("RecordingRegex", regex);
0453 }
0454 
0455 void DvbManager::setRecordingRegexList(const QStringList regexList)
0456 {
0457     KSharedConfig::openConfig()->group("DVB").writeEntry("RecordingRegexList", regexList);
0458 }
0459 
0460 void DvbManager::setRecordingRegexPriorityList(const QList<int> regexList)
0461 {
0462     KSharedConfig::openConfig()->group("DVB").writeEntry("RecordingRegexPriorityList", regexList);
0463 }
0464 
0465 bool DvbManager::addRecordingRegex(QString regex)
0466 {
0467     QStringList regexList = getRecordingRegexList();
0468     regexList.append(regex);
0469     setRecordingRegexList(regexList);
0470     return true;
0471 }
0472 
0473 bool DvbManager::addRecordingRegexPriority(int regexPriority)
0474 {
0475     QList<int> regexPriorityList = getRecordingRegexPriorityList();
0476     regexPriorityList.append(regexPriority);
0477     setRecordingRegexPriorityList(regexPriorityList);
0478     return true;
0479 }
0480 
0481 bool DvbManager::removeRecordingRegex(QString regex)
0482 {
0483     QStringList regexList = getRecordingRegexList();
0484     if (regexList.contains(regex)) {
0485         regexList.removeOne(regex);
0486         setRecordingRegexList(regexList);
0487         return true;
0488     }
0489     setRecordingRegexList(regexList);
0490     return false;
0491 }
0492 
0493 bool DvbManager::removeRecordingRegexPriority(int priority)
0494 {
0495     QList<int> regexPriorityList = getRecordingRegexPriorityList();
0496     if (regexPriorityList.contains(priority)) {
0497         regexPriorityList.removeOne(priority);
0498         setRecordingRegexPriorityList(regexPriorityList);
0499         return true;
0500     }
0501     setRecordingRegexPriorityList(regexPriorityList);
0502     return false;
0503 }
0504 
0505 void DvbManager::setActionAfterRecording(QString actionAfterRecording)
0506 {
0507     KSharedConfig::openConfig()->group("DVB").writeEntry("ActionAfterRecording", actionAfterRecording);
0508 }
0509 
0510 void DvbManager::setOverride6937Charset(bool override)
0511 {
0512     KSharedConfig::openConfig()->group("DVB").writeEntry("Override6937", override);
0513     DvbSiText::setOverride6937(override);
0514 }
0515 
0516 void DvbManager::setScanWhenIdle(bool scanWhenIdle)
0517 {
0518     KSharedConfig::openConfig()->group("DVB").writeEntry("ScanWhenIdle", scanWhenIdle);
0519 }
0520 
0521 void DvbManager::setCreateInfoFile(bool createInfoFile)
0522 {
0523     KSharedConfig::openConfig()->group("DVB").writeEntry("CreateInfoFile", createInfoFile);
0524 }
0525 
0526 void DvbManager::setDisableEpg(bool disableEpg)
0527 {
0528     KSharedConfig::openConfig()->group("DVB").writeEntry("DisableEpg", disableEpg);
0529 }
0530 
0531 void DvbManager::enableDvbDump()
0532 {
0533     if (dvbDumpEnabled) {
0534         return;
0535     }
0536 
0537     dvbDumpEnabled = true;
0538 
0539     foreach (const DvbDeviceConfig &deviceConfig, deviceConfigs) {
0540         if (deviceConfig.device != NULL) {
0541             deviceConfig.device->enableDvbDump();
0542         }
0543     }
0544 }
0545 
0546 void DvbManager::requestBuiltinDeviceManager(QObject *&builtinDeviceManager)
0547 {
0548     builtinDeviceManager = new DvbLinuxDeviceManager(this);
0549 }
0550 
0551 void DvbManager::deviceAdded(DvbBackendDevice *backendDevice)
0552 {
0553     DvbDevice *device = new DvbDevice(backendDevice, this);
0554     QString deviceId = device->getDeviceId();
0555     QString frontendName = device->getFrontendName();
0556 
0557     if (dvbDumpEnabled) {
0558         device->enableDvbDump();
0559     }
0560 
0561     for (int i = 0;; ++i) {
0562         if (i == deviceConfigs.size()) {
0563             deviceConfigs.append(DvbDeviceConfig(deviceId, frontendName, device));
0564             break;
0565         }
0566 
0567         const DvbDeviceConfig &it = deviceConfigs.at(i);
0568 
0569         if ((it.deviceId.isEmpty() || deviceId.isEmpty() || (it.deviceId == deviceId)) &&
0570             (it.frontendName == frontendName) && (it.device == NULL)) {
0571             deviceConfigs[i].device = device;
0572             break;
0573         }
0574     }
0575 }
0576 
0577 void DvbManager::deviceRemoved(DvbBackendDevice *backendDevice)
0578 {
0579     for (int i = 0; i < deviceConfigs.size(); ++i) {
0580         DvbDeviceConfig &it = deviceConfigs[i];
0581 
0582         if (it.device && it.device->getBackendDevice() == backendDevice) {
0583             if (it.useCount != 0) {
0584                 it.useCount = 0;
0585                 it.prioritizedUseCount = 0;
0586                 it.device->release();
0587             }
0588 
0589             delete it.device;
0590             it.device = NULL;
0591             break;
0592         }
0593     }
0594 }
0595 
0596 void DvbManager::loadDeviceManager()
0597 {
0598     QDir dir(QString::fromUtf8(KAFFEINE_LIB_INSTALL_DIR "/"));
0599     QStringList entries = dir.entryList(QStringList(QLatin1String("*kaffeinedvb*")), QDir::NoFilter,
0600         QDir::Name | QDir::Reversed);
0601 
0602     foreach (const QString &entry, entries) {
0603         QString path = dir.filePath(entry);
0604 
0605         if (!QLibrary::isLibrary(path)) {
0606             continue;
0607         }
0608 
0609         QObject *deviceManager = QPluginLoader(path).instance();
0610 
0611         if (deviceManager == NULL) {
0612             qCWarning(logDvb, "Cannot load dvb device manager %s", qPrintable(path));
0613             break;
0614         }
0615 
0616         qCInfo(logDvb, "Using dvb device manager %s", qPrintable(path));
0617         deviceManager->setParent(this);
0618         connect(deviceManager, SIGNAL(requestBuiltinDeviceManager(QObject*&)),
0619             this, SLOT(requestBuiltinDeviceManager(QObject*&)));
0620         connect(deviceManager, SIGNAL(deviceAdded(DvbBackendDevice*)),
0621             this, SLOT(deviceAdded(DvbBackendDevice*)));
0622         connect(deviceManager, SIGNAL(deviceRemoved(DvbBackendDevice*)),
0623             this, SLOT(deviceRemoved(DvbBackendDevice*)));
0624         QMetaObject::invokeMethod(deviceManager, "doColdPlug");
0625         return;
0626     }
0627 
0628     qCInfo(logDvb, "Using built-in dvb device manager");
0629     DvbLinuxDeviceManager *deviceManager = new DvbLinuxDeviceManager(this);
0630     connect(deviceManager, SIGNAL(deviceAdded(DvbBackendDevice*)),
0631         this, SLOT(deviceAdded(DvbBackendDevice*)));
0632     connect(deviceManager, SIGNAL(deviceRemoved(DvbBackendDevice*)),
0633         this, SLOT(deviceRemoved(DvbBackendDevice*)));
0634     deviceManager->doColdPlug();
0635 }
0636 
0637 void DvbManager::readDeviceConfigs()
0638 {
0639     QFile file(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1String("/config.dvb"));
0640     const char *errMsg;
0641 
0642     if (!file.open(QIODevice::ReadOnly)) {
0643         qCWarning(logDvb, "Cannot open %s", qPrintable(file.fileName()));
0644         return;
0645     }
0646 
0647     DvbDeviceConfigReader reader(&file);
0648 
0649     while (!reader.atEnd()) {
0650         if (reader.readLine() != QLatin1String("[device]")) {
0651             continue;
0652         }
0653 
0654         QString deviceId = reader.readString(QLatin1String("deviceId"));
0655         QString frontendName = reader.readString(QLatin1String("frontendName"));
0656         int configCount = reader.readInt(QLatin1String("configCount"));
0657 
0658         if (!reader.isValid()) {
0659             errMsg = "device section invalid";
0660             break;
0661         }
0662 
0663         DvbDeviceConfig deviceConfig(deviceId, frontendName, NULL);
0664 
0665         for (int i = 0; i < configCount; ++i) {
0666             while (!reader.atEnd()) {
0667                 if (reader.readLine() == QLatin1String("[config]")) {
0668                     break;
0669                 }
0670             }
0671 
0672             DvbConfigBase::TransmissionType type =
0673                 reader.readEnum(QLatin1String("type"), DvbConfigBase::TransmissionTypeMax);
0674 
0675             if (!reader.isValid()) {
0676                 errMsg = "transmission type invalid";
0677                 break;
0678             }
0679 
0680             DvbConfigBase *config = new DvbConfigBase(type);
0681 
0682             config->numberOfTuners = 1;
0683             config->name = reader.readString(QLatin1String("name"));
0684             config->scanSource = reader.readString(QLatin1String("scanSource"));
0685             config->timeout = reader.readInt(QLatin1String("timeout"));
0686 
0687             if (type == DvbConfigBase::DvbS) {
0688                 config->latitude = 0;
0689                 config->longitude = 0;
0690                 config->higherVoltage = Qt::PartiallyChecked;
0691                 config->configuration = reader.readEnum(QLatin1String("configuration"),
0692                     DvbConfigBase::ConfigurationMax);
0693                 config->lnbNumber = reader.readInt(QLatin1String("lnbNumber"));
0694                 config->currentLnb.alias = reader.readString(QLatin1String("lnb"));
0695                 config->higherVoltage = reader.readInt(QLatin1String("higherVoltage"));
0696                 if (config->configuration == DvbConfigBase::UsalsRotor) {
0697                     config->latitude = reader.readDouble(QLatin1String("latitude"));
0698                     config->longitude = reader.readInt(QLatin1String("longitude"));
0699                 }
0700             }
0701 
0702             if (!reader.isValid()) {
0703                 errMsg = "DVB device data invalid";
0704                 delete config;
0705                 break;
0706             }
0707 
0708             deviceConfig.configs.append(DvbConfig(config));
0709         }
0710 
0711         deviceConfigs.append(deviceConfig);
0712     }
0713 
0714     if (!reader.isValid())
0715         qCWarning(logDvb, "Found some problems at %s: %s", qPrintable(file.fileName()), errMsg);
0716 }
0717 
0718 void DvbManager::writeDeviceConfigs()
0719 {
0720     QFile file(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1String("/config.dvb"));
0721 
0722     if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
0723         qCWarning(logDvb, "Cannot open %s", qPrintable(file.fileName()));
0724         return;
0725     }
0726 
0727     DvbDeviceConfigWriter writer(&file);
0728 
0729     foreach (const DvbDeviceConfig &deviceConfig, deviceConfigs) {
0730         writer.write(QLatin1String("[device]"));
0731         writer.write(QLatin1String("deviceId"), deviceConfig.deviceId);
0732         writer.write(QLatin1String("frontendName"), deviceConfig.frontendName);
0733         writer.write(QLatin1String("configCount"), deviceConfig.configs.size());
0734 
0735         for (int i = 0; i < deviceConfig.configs.size(); ++i) {
0736             const DvbConfig &config = deviceConfig.configs.at(i);
0737             writer.write(QLatin1String("[config]"));
0738             writer.write(QLatin1String("type"), config->getTransmissionType());
0739             writer.write(QLatin1String("name"), config->name);
0740             writer.write(QLatin1String("scanSource"), config->scanSource);
0741             writer.write(QLatin1String("timeout"), config->timeout);
0742 
0743             if (config->getTransmissionType() == DvbConfigBase::DvbS) {
0744                 writer.write(QLatin1String("configuration"), config->configuration);
0745                 writer.write(QLatin1String("lnbNumber"), config->lnbNumber);
0746                 writer.write(QLatin1String("lnb"), config->currentLnb.alias);
0747                 writer.write(QLatin1String("higherVoltage"), config->higherVoltage);
0748                 if (config->configuration == DvbConfigBase::UsalsRotor) {
0749                     writer.write(QLatin1String("latitude"), config->latitude);
0750                     writer.write(QLatin1String("longitude"), config->longitude);
0751                 }
0752             }
0753         }
0754     }
0755 }
0756 
0757 void DvbManager::updateSourceMapping()
0758 {
0759     sourceMapping.clear();
0760 
0761     foreach (const DvbDeviceConfig &deviceConfig, deviceConfigs) {
0762         for (int i = 0; i < deviceConfig.configs.size(); ++i) {
0763             const DvbConfig &config = deviceConfig.configs.at(i);
0764             TransmissionType type;
0765 
0766             switch (config->getTransmissionType()) {
0767             case DvbConfigBase::DvbC:
0768                 type = DvbC;
0769                 break;
0770             case DvbConfigBase::DvbS:
0771                 type = DvbS;
0772                 break;
0773             case DvbConfigBase::DvbT:
0774                 type = DvbT;
0775                 break;
0776             case DvbConfigBase::Atsc:
0777                 type = Atsc;
0778                 break;
0779             case DvbConfigBase::IsdbT:
0780                 type = IsdbT;
0781                 break;
0782             default:
0783                 Q_ASSERT(false);
0784                 continue;
0785             }
0786 
0787             sourceMapping.insert(config->name, qMakePair(type, config->scanSource));
0788         }
0789     }
0790 
0791     sources = sourceMapping.keys();
0792 }
0793 
0794 void DvbManager::readScanData()
0795 {
0796     scanSources.clear();
0797     scanData.clear();
0798 
0799     QFile globalFile(QString::fromUtf8(KAFFEINE_DATA_INSTALL_DIR "/kaffeine/scanfile.dvb"));
0800     QDate globalDate;
0801 
0802     if (globalFile.open(QIODevice::ReadOnly)) {
0803         globalDate = DvbScanData(globalFile.read(1024)).readDate();
0804 
0805         if (globalDate.isNull()) {
0806             qCWarning(logDvb, "Cannot parse %s", qPrintable(globalFile.fileName()));
0807         }
0808 
0809         globalFile.close();
0810     } else {
0811         qCWarning(logDvb, "Cannot open global scanfile %s", qPrintable(globalFile.fileName()));
0812     }
0813 
0814     QFile localFile(QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1String("/scanfile.dvb"));
0815     QByteArray localData;
0816     QDate localDate;
0817 
0818     if (localFile.open(QIODevice::ReadOnly)) {
0819         localData = localFile.readAll();
0820         localDate = DvbScanData(localData).readDate();
0821 
0822         if (localDate.isNull()) {
0823             qCWarning(logDvb, "Cannot parse %s", qPrintable(localFile.fileName()));
0824         }
0825 
0826         localFile.close();
0827     }
0828 
0829     if (localDate < globalDate) {
0830         localData.clear();
0831 
0832         if (localFile.exists() && !localFile.remove()) {
0833             qCWarning(logDvb, "Cannot remove %s", qPrintable(localFile.fileName()));
0834         }
0835 
0836         if (!globalFile.copy(localFile.fileName())) {
0837             qCWarning(logDvb, "Cannot copy %s to %s", qPrintable(globalFile.fileName()), qPrintable(localFile.fileName()));
0838         }
0839 
0840         if (localFile.open(QIODevice::ReadOnly)) {
0841             localData = localFile.readAll();
0842             localFile.close();
0843         } else {
0844             qCWarning(logDvb, "Cannot open %s", qPrintable(localFile.fileName()));
0845             scanDataDate = QDate(1900, 1, 1);
0846             return;
0847         }
0848     }
0849 
0850     DvbScanData data(localData);
0851     scanDataDate = data.readDate();
0852 
0853     if (!scanDataDate.isValid()) {
0854         qCWarning(logDvb, "Cannot parse %s", qPrintable(localFile.fileName()));
0855         scanDataDate = QDate(1900, 1, 1);
0856         return;
0857     }
0858 
0859     // Parse scan file
0860 
0861     QRegularExpression rejex = QRegularExpression("\\[(\\S+)/(\\S+)\\]");
0862     QRegularExpressionMatch match;
0863     TransmissionType type;
0864 
0865     while (!data.checkEnd()) {
0866         const char *line = data.readLine();
0867 
0868         // Discard empty lines
0869         if (*line == 0)
0870             continue;
0871 
0872         QString qLine(line);
0873 
0874         if (!qLine.contains(rejex, &match)) {
0875             qCWarning(logDvb, "Unrecognized line: '%s'", line);
0876             continue;
0877 
0878         }
0879 
0880         QString typeStr = match.captured(1);
0881         QString name = match.captured(2);
0882 
0883         if (!typeStr.compare("dvb-c", Qt::CaseInsensitive))
0884             type = DvbC;
0885         else if (!typeStr.compare("dvb-s", Qt::CaseInsensitive))
0886             type = DvbS;
0887         else if (!typeStr.compare("dvb-t", Qt::CaseInsensitive))
0888             type = DvbT;
0889         else if (!typeStr.compare("atsc", Qt::CaseInsensitive))
0890             type = Atsc;
0891         else if (!typeStr.compare("isdb-t", Qt::CaseInsensitive))
0892             type = IsdbT;
0893         else {
0894             qCWarning(logDvb, "Transmission type '%s' unknown", qPrintable(typeStr));
0895             continue;
0896         }
0897 
0898         QList<DvbTransponder> transponders;
0899         bool containsDvbS1 = false;
0900         bool containsDvbT1 = false;
0901 
0902         while (!data.checkEnd()) {
0903             line = data.getLine();
0904 
0905             if ((*line == '[') || (*line == 0)) {
0906                 break;
0907             }
0908 
0909             line = data.readLine();
0910 
0911             // Ignore lines with empty strings
0912             if (*line == 0)
0913                 continue;
0914 
0915             DvbTransponder transponder =
0916                 DvbTransponder::fromString(QString::fromLatin1(line));
0917 
0918             if (!transponder.isValid()) {
0919                 qCWarning(logDvb, "Error parsing line : '%s'", qPrintable(line));
0920             } else {
0921                 transponders.append(transponder);
0922 
0923                 if (transponder.getTransmissionType() ==
0924                     DvbTransponderBase::DvbS) {
0925                     containsDvbS1 = true;
0926                 }
0927                 if (transponder.getTransmissionType() ==
0928                     DvbTransponderBase::DvbT) {
0929                     containsDvbT1 = true;
0930                 }
0931             }
0932         }
0933 
0934         if (type == DvbS || type == DvbS2) {
0935             scanSources[DvbS2].append(name);
0936             scanData.insert(qMakePair(DvbS2, name), transponders);
0937 
0938             if (containsDvbS1) {
0939                 for (int i = 0; i < transponders.size(); ++i) {
0940                     if (transponders.at(i).getTransmissionType() ==
0941                         DvbTransponderBase::DvbS2) {
0942                         transponders.removeAt(i);
0943                         --i;
0944                     }
0945                 }
0946 
0947                 scanSources[DvbS].append(name);
0948                 scanData.insert(qMakePair(DvbS, name), transponders);
0949             }
0950         } else if (type == DvbT || type == DvbT2) {
0951             scanSources[DvbT2].append(name);
0952             scanData.insert(qMakePair(DvbT2, name), transponders);
0953 
0954             if (containsDvbT1) {
0955                 for (int i = 0; i < transponders.size(); ++i) {
0956                     if (transponders.at(i).getTransmissionType() ==
0957                         DvbTransponderBase::DvbT2) {
0958                         transponders.removeAt(i);
0959                         --i;
0960                     }
0961                 }
0962 
0963                 scanSources[DvbT].append(name);
0964                 scanData.insert(qMakePair(DvbT, name), transponders);
0965             }
0966         } else {
0967             scanSources[type].append(name);
0968             scanData.insert(qMakePair(type, name), transponders);
0969         }
0970     }
0971 
0972     if (!data.checkEnd())
0973         qCWarning(logDvb, "Some data at the scan file were not parsed");
0974 }
0975 
0976 DvbDeviceConfig::DvbDeviceConfig(const QString &deviceId_, const QString &frontendName_,
0977     DvbDevice *device_) : deviceId(deviceId_), frontendName(frontendName_), device(device_),
0978     useCount(0), prioritizedUseCount(0)
0979 {
0980 }
0981 
0982 DvbDeviceConfig::~DvbDeviceConfig()
0983 {
0984 }
0985 
0986 DvbDeviceConfigUpdate::DvbDeviceConfigUpdate(const DvbDeviceConfig *deviceConfig_) :
0987     deviceConfig(deviceConfig_)
0988 {
0989 }
0990 
0991 DvbDeviceConfigUpdate::~DvbDeviceConfigUpdate()
0992 {
0993 }
0994 
0995 DvbScanData::DvbScanData(const QByteArray &data_) : data(data_)
0996 {
0997     begin = data.begin();
0998     pos = data.begin();
0999     end = data.constEnd();
1000 }
1001 
1002 DvbScanData::~DvbScanData()
1003 {
1004 }
1005 
1006 bool DvbScanData::checkEnd() const
1007 {
1008     return (pos == end);
1009 }
1010 
1011 const char *DvbScanData::getLine() const
1012 {
1013     return pos;
1014 }
1015 
1016 const char *DvbScanData::readLine()
1017 {
1018     // ignore comments
1019 
1020     while (*pos == '#') {
1021         do {
1022             ++pos;
1023 
1024             if (pos == end) {
1025                 return end;
1026             }
1027         } while (*pos != '\n');
1028 
1029         ++pos;
1030     }
1031 
1032     char *line = pos;
1033 
1034     while (pos < end) {
1035         if (*pos == ' ')
1036             ++pos;
1037         if (*pos == '\n') {
1038             *pos = 0;
1039             ++pos;
1040             break;
1041         }
1042 
1043         ++pos;
1044     }
1045 
1046     return line;
1047 }
1048 
1049 QDate DvbScanData::readDate()
1050 {
1051     if (strcmp(readLine(), "[date]") != 0) {
1052         return QDate();
1053     }
1054 
1055     return QDate::fromString(QString::fromLatin1(readLine()), Qt::ISODate);
1056 }
1057 
1058 #include "moc_dvbmanager.cpp"