QString MetadataDownload::getMXMLPath(QString filename) { QString ret; QString xmlname; QUrl qurl(filename); QString ext = QFileInfo(qurl.path()).suffix(); xmlname = filename.left(filename.size() - ext.size()) + "mxml"; QUrl xurl(xmlname); if (xmlname.startsWith("myth://")) { if (qurl.host().toLower() != gCoreContext->GetHostName().toLower() && (qurl.host() != gCoreContext->GetSettingOnHost("BackendServerIP", gCoreContext->GetHostName()))) { if (RemoteFile::Exists(xmlname)) ret = xmlname; } else { StorageGroup sg; QString fn = sg.FindFile(xurl.path()); if (!fn.isEmpty() && QFile::exists(fn)) ret = xmlname; } } else { if (QFile::exists(xmlname)) ret = xmlname; } return ret; }
QString MetadataDownload::getNFOPath(QString filename) { QString ret; QString nfoname; QUrl qurl(filename); QString ext = QFileInfo(qurl.path()).suffix(); nfoname = filename.left(filename.size() - ext.size()) + "nfo"; QUrl nurl(nfoname); if (nfoname.startsWith("myth://")) { if (qurl.host().toLower() != gCoreContext->GetHostName().toLower() && (!gCoreContext->IsThisHost(qurl.host()))) { if (RemoteFile::Exists(nfoname)) ret = nfoname; } else { StorageGroup sg; QString fn = sg.FindFile(nurl.path()); if (!fn.isEmpty() && QFile::exists(fn)) ret = nfoname; } } else { if (QFile::exists(nfoname)) ret = nfoname; } return ret; }
QString StorageGroup::FindFileDir(QString filename) { QString result = ""; QFileInfo checkFile(""); int curDir = 0; while (curDir < m_dirlist.size()) { QString testFile = m_dirlist[curDir] + "/" + filename; LOG(VB_FILE, LOG_DEBUG, LOC + QString("FindFileDir: Checking '%1' for '%2'") .arg(m_dirlist[curDir]).arg(testFile)); checkFile.setFile(testFile); if (checkFile.exists() || checkFile.isSymLink()) { QString tmp = m_dirlist[curDir]; tmp.detach(); return tmp; } curDir++; } if (m_groupname.isEmpty() || (m_allowFallback == false)) { // Not found in any dir, so try RecordFilePrefix if it exists QString tmpFile = gCoreContext->GetSetting("RecordFilePrefix") + "/" + filename; checkFile.setFile(tmpFile); if (checkFile.exists() || checkFile.isSymLink()) result = tmpFile; } else if (m_groupname != "Default") { // Not found in current group so try Default StorageGroup sgroup("Default"); QString tmpFile = sgroup.FindFileDir(filename); result = (tmpFile.isEmpty()) ? result : tmpFile; } else { // Not found in Default so try any dir StorageGroup sgroup; QString tmpFile = sgroup.FindFileDir(filename); result = (tmpFile.isEmpty()) ? result : tmpFile; } result.detach(); return result; }
/** \fn ImageUtils::GetStorageDirs() * \brief Gets the available storage groups * \return List of all available storage groups */ QStringList ImageUtils::GetStorageDirs() { QStringList sgDirList; // The name that shall be used for the images storage group. It must be // specified because its not part of the default storage group names QString sgName = gCoreContext->GetSetting("GalleryStorageGroupName"); if (!sgName.isEmpty()) { QString host = gCoreContext->GetHostName(); // Search for the specified dirs in the defined storage group. // If there is no such storage group then don't use the fallback // and don't get the default storage group name of "/mnt/store". // The list will be empty. The user has to check the settings. StorageGroup sg; sg.Init(sgName, host, false); sgDirList = sg.GetDirList(); } return sgDirList; }
QString getLocalStorageGroupPath(VideoArtworkType type, QString host) { QString path; StorageGroup sg; if (type == kArtworkCoverart) sg.Init("Coverart", host); else if (type == kArtworkFanart) sg.Init("Fanart", host); else if (type == kArtworkBanner) sg.Init("Banners", host); else if (type == kArtworkScreenshot) sg.Init("Screenshots", host); else sg.Init("Default", host); path = sg.FindNextDirMostFree(); return path; }
QString FileServerHandler::LocalFilePath(const QUrl &url, const QString &wantgroup) { QString lpath = url.path(); if (lpath.section('/', -2, -2) == "channels") { // This must be an icon request. Check channel.icon to be safe. QString querytext; QString file = lpath.section('/', -1); lpath = ""; MSqlQuery query(MSqlQuery::InitCon()); query.prepare("SELECT icon FROM channel WHERE icon LIKE :FILENAME ;"); query.bindValue(":FILENAME", QString("%/") + file); if (query.exec() && query.next()) { lpath = query.value(0).toString(); } else { MythDB::DBError("Icon path", query); } } else { lpath = lpath.section('/', -1); QString fpath = lpath; if (fpath.right(4) == ".png") fpath = fpath.left(fpath.length() - 4); ProgramInfo pginfo(fpath); if (pginfo.GetChanID()) { QString pburl = GetPlaybackURL(&pginfo); if (pburl.left(1) == "/") { lpath = pburl.section('/', 0, -2) + "/" + lpath; LOG(VB_FILE, LOG_INFO, QString("Local file path: %1").arg(lpath)); } else { LOG(VB_GENERAL, LOG_ERR, QString("LocalFilePath unable to find local " "path for '%1', found '%2' instead.") .arg(lpath).arg(pburl)); lpath = ""; } } else if (!lpath.isEmpty()) { // For securities sake, make sure filename is really the pathless. QString opath = lpath; StorageGroup sgroup; if (!wantgroup.isEmpty()) { sgroup.Init(wantgroup); lpath = url.toString(); } else { lpath = QFileInfo(lpath).fileName(); } QString tmpFile = sgroup.FindFile(lpath); if (!tmpFile.isEmpty()) { lpath = tmpFile; LOG(VB_FILE, LOG_INFO, QString("LocalFilePath(%1 '%2'), found through " "exhaustive search at '%3'") .arg(url.toString()).arg(opath).arg(lpath)); } else { LOG(VB_GENERAL, LOG_ERR, QString("LocalFilePath unable to " "find local path for '%1'.") .arg(opath)); lpath = ""; } } else { lpath = ""; } } return lpath; }
void MetadataImageDownload::run() { RunProlog(); // Always handle thumbnails first, they're higher priority. ThumbnailData *thumb; while ((thumb = moreThumbs()) != NULL) { QString sFilename = getDownloadFilename(thumb->title, thumb->url); bool exists = QFile::exists(sFilename); if (!exists && !thumb->url.isEmpty()) { if (!GetMythDownloadManager()->download(thumb->url, sFilename)) { LOG(VB_GENERAL, LOG_ERR, QString("MetadataImageDownload: failed to download thumbnail from: %1") .arg(thumb->url)); delete thumb; continue; } } // inform parent we have thumbnail ready for it if (QFile::exists(sFilename) && m_parent) { LOG(VB_GENERAL, LOG_DEBUG, QString("Threaded Image Thumbnail Download: %1") .arg(sFilename)); thumb->url = sFilename; QCoreApplication::postEvent(m_parent, new ThumbnailDLEvent(thumb)); } else delete thumb; } MetadataLookup *lookup; while ((lookup = moreDownloads()) != NULL) { DownloadMap downloads = lookup->GetDownloads(); DownloadMap downloaded; for (DownloadMap::iterator i = downloads.begin(); i != downloads.end(); ++i) { VideoArtworkType type = i.key(); ArtworkInfo info = i.value(); QString filename = getDownloadFilename( type, lookup, info.url ); if (lookup->GetHost().isEmpty()) { QString path = getLocalWritePath(lookup->GetType(), type); QDir dirPath(path); if (!dirPath.exists()) if (!dirPath.mkpath(path)) { LOG(VB_GENERAL, LOG_ERR, QString("Metadata Image Download: Unable to create " "path %1, aborting download.").arg(path)); QCoreApplication::postEvent(m_parent, new ImageDLFailureEvent(lookup)); continue; } QString finalfile = path + "/" + filename; QString oldurl = info.url; info.url = finalfile; if (!QFile::exists(finalfile) || lookup->GetAllowOverwrites()) { QFile dest_file(finalfile); if (dest_file.exists()) { QFileInfo fi(finalfile); GetMythUI()->RemoveFromCacheByFile(fi.fileName()); dest_file.remove(); } LOG(VB_GENERAL, LOG_INFO, QString("Metadata Image Download: %1 ->%2") .arg(oldurl).arg(finalfile)); QByteArray *download = new QByteArray(); GetMythDownloadManager()->download(oldurl, download); QImage testImage; bool didLoad = testImage.loadFromData(*download); if (!didLoad) { LOG(VB_GENERAL, LOG_ERR, QString("Tried to write %1, but it appears to be " "an HTML redirect (filesize %2).") .arg(oldurl).arg(download->size())); delete download; download = NULL; QCoreApplication::postEvent(m_parent, new ImageDLFailureEvent(lookup)); continue; } if (dest_file.open(QIODevice::WriteOnly)) { off_t size = dest_file.write(*download, download->size()); if (size != download->size()) { LOG(VB_GENERAL, LOG_ERR, QString("Image Download: Error Writing Image " "to file: %1").arg(finalfile)); QCoreApplication::postEvent(m_parent, new ImageDLFailureEvent(lookup)); } else downloaded.insert(type, info); } delete download; } else downloaded.insert(type, info); } else { QString path = getStorageGroupURL(type, lookup->GetHost()); QString finalfile = path + filename; QString oldurl = info.url; info.url = finalfile; bool exists = false; bool onMaster = false; QString resolvedFN; if ((lookup->GetHost().toLower() == gCoreContext->GetHostName().toLower()) || (gCoreContext->IsThisHost(lookup->GetHost()))) { StorageGroup sg; resolvedFN = sg.FindFile(filename); exists = QFile::exists(resolvedFN); if (!exists) { resolvedFN = getLocalStorageGroupPath(type, lookup->GetHost()) + "/" + filename; } onMaster = true; } else exists = RemoteFile::Exists(finalfile); if (!exists || lookup->GetAllowOverwrites()) { if (exists && !onMaster) { QFileInfo fi(finalfile); GetMythUI()->RemoveFromCacheByFile(fi.fileName()); RemoteFile::DeleteFile(finalfile); } else if (exists) QFile::remove(resolvedFN); LOG(VB_GENERAL, LOG_INFO, QString("Metadata Image Download: %1 -> %2") .arg(oldurl).arg(finalfile)); QByteArray *download = new QByteArray(); GetMythDownloadManager()->download(oldurl, download); QImage testImage; bool didLoad = testImage.loadFromData(*download); if (!didLoad) { LOG(VB_GENERAL, LOG_ERR, QString("Tried to write %1, but it appears to be " "an HTML redirect or corrupt file " "(filesize %2).") .arg(oldurl).arg(download->size())); delete download; download = NULL; QCoreApplication::postEvent(m_parent, new ImageDLFailureEvent(lookup)); continue; } if (!onMaster) { RemoteFile *outFile = new RemoteFile(finalfile, true); if (!outFile->isOpen()) { LOG(VB_GENERAL, LOG_ERR, QString("Image Download: Failed to open " "remote file (%1) for write. Does " "Storage Group Exist?") .arg(finalfile)); delete outFile; outFile = NULL; QCoreApplication::postEvent(m_parent, new ImageDLFailureEvent(lookup)); } else { off_t written = outFile->Write(*download, download->size()); if (written != download->size()) { LOG(VB_GENERAL, LOG_ERR, QString("Image Download: Error Writing Image " "to file: %1").arg(finalfile)); QCoreApplication::postEvent(m_parent, new ImageDLFailureEvent(lookup)); } else downloaded.insert(type, info); delete outFile; outFile = NULL; } } else { QFile dest_file(resolvedFN); if (dest_file.open(QIODevice::WriteOnly)) { off_t size = dest_file.write(*download, download->size()); if (size != download->size()) { LOG(VB_GENERAL, LOG_ERR, QString("Image Download: Error Writing Image " "to file: %1").arg(finalfile)); QCoreApplication::postEvent(m_parent, new ImageDLFailureEvent(lookup)); } else downloaded.insert(type, info); } } delete download; } else downloaded.insert(type, info); } } lookup->SetDownloads(downloaded); QCoreApplication::postEvent(m_parent, new ImageDLEvent(lookup)); } RunEpilog(); }
bool PreviewGenerator::Run(void) { bool ok = false; if (!IsLocal()) { if (!localOnly) { ok = RemotePreviewRun(); } else { VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Run() file not local: '%1'") .arg(pathname)); } } else { // This is where we fork and run mythbackend to actually make preview QString command = gContext->GetInstallPrefix() + "/bin/mythbackend --generate-preview "; command += QString("%1x%2") .arg(outSize.width()).arg(outSize.height()); if (captureTime >= 0) command += QString("@%1%2") .arg(captureTime).arg(timeInSeconds ? "s" : "f"); command += " "; command += QString("--chanid %1 ").arg(programInfo.chanid); command += QString("--starttime %1 ") .arg(programInfo.recstartts.toString("yyyyMMddhhmmss")); if (!outFileName.isEmpty()) command += QString("--outfile \"%1\" ").arg(outFileName); int ret = myth_system(command); if (ret) { VERBOSE(VB_IMPORTANT, LOC_ERR + "Encountered problems running " + QString("'%1'").arg(command)); } else { VERBOSE(VB_PLAYBACK, LOC + "Preview process returned 0."); QString outname = (!outFileName.isEmpty()) ? outFileName : (pathname + ".png"); QString lpath = QFileInfo(outname).fileName(); if (lpath == outname) { StorageGroup sgroup; QString tmpFile = sgroup.FindRecordingFile(lpath); outname = (tmpFile.isEmpty()) ? outname : tmpFile; } QFileInfo fi(outname); ok = (fi.exists() && fi.isReadable() && fi.size()); if (ok) VERBOSE(VB_PLAYBACK, LOC + "Preview process ran ok."); else { VERBOSE(VB_IMPORTANT, LOC_ERR + "Preview process not ok." + QString("\n\t\t\tfileinfo(%1)").arg(outname) <<" exists: "<<fi.exists() <<" readable: "<<fi.isReadable() <<" size: "<<fi.size()); } } } if (ok) { QMutexLocker locker(&previewLock); emit previewReady(&programInfo); } return ok; }
bool PreviewGenerator::Run(void) { QString msg; QDateTime dtm = QDateTime::currentDateTime(); QTime tm = QTime::currentTime(); bool ok = false; QString command = GetInstallPrefix() + "/bin/mythpreviewgen"; bool local_ok = ((IsLocal() || !!(mode & kForceLocal)) && (!!(mode & kLocal)) && QFileInfo(command).isExecutable()); if (!local_ok) { if (!!(mode & kRemote)) { ok = RemotePreviewRun(); if (ok) { msg = QString("Generated remotely in %1 seconds, starting at %2") .arg(tm.elapsed()*0.001) .arg(tm.toString(Qt::ISODate)); } } else { LOG(VB_GENERAL, LOG_ERR, LOC + QString("Run() cannot generate preview locally for: '%1'") .arg(pathname)); msg = "Failed, local preview requested for remote file."; } } else { // This is where we fork and run mythpreviewgen to actually make preview command += QString(" --size %1x%2") .arg(outSize.width()).arg(outSize.height()); if (captureTime >= 0) { if (timeInSeconds) command += QString(" --seconds %1").arg(captureTime); else command += QString(" --frame %1").arg(captureTime); } command += QString(" --chanid %1").arg(programInfo.GetChanID()); command += QString(" --starttime %1") .arg(programInfo.GetRecordingStartTime(MythDate)); if (!outFileName.isEmpty()) command += QString(" --outfile \"%1\"").arg(outFileName); command += logPropagateArgs; if (!logPropagateQuiet()) command += " --quiet"; // Timeout in 30s uint ret = myth_system(command, kMSDontBlockInputDevs | kMSDontDisableDrawing | kMSProcessEvents, 30); if (ret != GENERIC_EXIT_OK) { LOG(VB_GENERAL, LOG_ERR, LOC + QString("Encountered problems running '%1' (%2)") .arg(command) .arg(ret)); } else { LOG(VB_PLAYBACK, LOG_INFO, LOC + "Preview process returned 0."); QString outname = (!outFileName.isEmpty()) ? outFileName : (pathname + ".png"); QString lpath = QFileInfo(outname).fileName(); if (lpath == outname) { StorageGroup sgroup; QString tmpFile = sgroup.FindFile(lpath); outname = (tmpFile.isEmpty()) ? outname : tmpFile; } QFileInfo fi(outname); ok = (fi.exists() && fi.isReadable() && fi.size()); if (ok) { LOG(VB_PLAYBACK, LOG_INFO, LOC + "Preview process ran ok."); msg = QString("Generated on %1 in %2 seconds, starting at %3") .arg(gCoreContext->GetHostName()) .arg(tm.elapsed()*0.001) .arg(tm.toString(Qt::ISODate)); } else { LOG(VB_GENERAL, LOG_ERR, LOC + "Preview process not ok." + QString("\n\t\t\tfileinfo(%1)").arg(outname) + QString(" exists: %1").arg(fi.exists()) + QString(" readable: %1").arg(fi.isReadable()) + QString(" size: %1").arg(fi.size())); LOG(VB_GENERAL, LOG_ERR, LOC + QString("Despite command '%1' returning success") .arg(command)); msg = QString("Failed to read preview image despite " "preview process returning success."); } } } QMutexLocker locker(&previewLock); // Backdate file to start of preview time in case a bookmark was made // while we were generating the preview. QString output_fn = outFileName.isEmpty() ? (programInfo.GetPathname()+".png") : outFileName; QDateTime dt; if (ok) { QFileInfo fi(output_fn); if (fi.exists()) dt = fi.lastModified(); } QString message = (ok) ? "PREVIEW_SUCCESS" : "PREVIEW_FAILED"; if (listener) { QStringList list; list.push_back(programInfo.MakeUniqueKey()); list.push_back(outFileName.isEmpty() ? (programInfo.GetPathname()+".png") : outFileName); list.push_back(msg); list.push_back(dt.isValid()?dt.toString(Qt::ISODate):""); list.push_back(token); QCoreApplication::postEvent(listener, new MythEvent(message, list)); } return ok; }
bool PreviewGenerator::Run(void) { QString msg; QDateTime dtm = MythDate::current(); QTime tm = QTime::currentTime(); bool ok = false; QString command = GetAppBinDir() + "mythpreviewgen"; bool local_ok = ((IsLocal() || !!(m_mode & kForceLocal)) && (!!(m_mode & kLocal)) && QFileInfo(command).isExecutable()); if (!local_ok) { if (!!(m_mode & kRemote)) { ok = RemotePreviewRun(); if (ok) { msg = QString("Generated remotely in %1 seconds, starting at %2") .arg(tm.elapsed()*0.001) .arg(tm.toString(Qt::ISODate)); } } else { LOG(VB_GENERAL, LOG_ERR, LOC + QString("Run() cannot generate preview locally for: '%1'") .arg(m_pathname)); msg = "Failed, local preview requested for remote file."; } } else { // This is where we fork and run mythpreviewgen to actually make preview QStringList cmdargs; cmdargs << "--size" << QString("%1x%2").arg(m_outSize.width()).arg(m_outSize.height()); if (m_captureTime >= 0) { if (m_timeInSeconds) cmdargs << "--seconds"; else cmdargs << "--frame"; cmdargs << QString::number(m_captureTime); } cmdargs << "--chanid" << QString::number(m_programInfo.GetChanID()) << "--starttime" << m_programInfo.GetRecordingStartTime(MythDate::kFilename); if (!m_outFileName.isEmpty()) cmdargs << "--outfile" << m_outFileName; // Timeout in 30s MythSystemLegacy *ms = new MythSystemLegacy(command, cmdargs, kMSDontBlockInputDevs | kMSDontDisableDrawing | kMSProcessEvents | kMSAutoCleanup | kMSPropagateLogs); ms->SetNice(10); ms->SetIOPrio(7); ms->Run(30); uint ret = ms->Wait(); delete ms; if (ret != GENERIC_EXIT_OK) { LOG(VB_GENERAL, LOG_ERR, LOC + QString("Encountered problems running '%1 %2' - (%3)") .arg(command).arg(cmdargs.join(" ")).arg(ret)); } else { LOG(VB_PLAYBACK, LOG_INFO, LOC + "Preview process returned 0."); QString outname = (!m_outFileName.isEmpty()) ? m_outFileName : (m_pathname + ".png"); QString lpath = QFileInfo(outname).fileName(); if (lpath == outname) { StorageGroup sgroup; QString tmpFile = sgroup.FindFile(lpath); outname = (tmpFile.isEmpty()) ? outname : tmpFile; } QFileInfo fi(outname); ok = (fi.exists() && fi.isReadable() && fi.size()); if (ok) { LOG(VB_PLAYBACK, LOG_INFO, LOC + "Preview process ran ok."); msg = QString("Generated on %1 in %2 seconds, starting at %3") .arg(gCoreContext->GetHostName()) .arg(tm.elapsed()*0.001) .arg(tm.toString(Qt::ISODate)); } else { LOG(VB_GENERAL, LOG_ERR, LOC + "Preview process not ok." + QString("\n\t\t\tfileinfo(%1)").arg(outname) + QString(" exists: %1").arg(fi.exists()) + QString(" readable: %1").arg(fi.isReadable()) + QString(" size: %1").arg(fi.size())); LOG(VB_GENERAL, LOG_ERR, LOC + QString("Despite command '%1' returning success") .arg(command)); msg = QString("Failed to read preview image despite " "preview process returning success."); } } } QMutexLocker locker(&m_previewLock); // Backdate file to start of preview time in case a bookmark was made // while we were generating the preview. QString output_fn = m_outFileName.isEmpty() ? (m_programInfo.GetPathname()+".png") : m_outFileName; QDateTime dt; if (ok) { QFileInfo fi(output_fn); if (fi.exists()) dt = fi.lastModified(); } QString message = (ok) ? "PREVIEW_SUCCESS" : "PREVIEW_FAILED"; if (m_listener) { QStringList list; list.push_back(QString::number(m_programInfo.GetRecordingID())); list.push_back(output_fn); list.push_back(msg); list.push_back(dt.isValid()?dt.toUTC().toString(Qt::ISODate):""); list.push_back(m_token); QCoreApplication::postEvent(m_listener, new MythEvent(message, list)); } return ok; }