bool Content::DownloadFile( const QString &sURL, const QString &sStorageGroup ) { QFileInfo finfo(sURL); QString filename = finfo.fileName(); StorageGroup sgroup(sStorageGroup, gCoreContext->GetHostName(), false); QString outDir = sgroup.FindNextDirMostFree(); QString outFile; if (outDir.isEmpty()) { LOG(VB_GENERAL, LOG_ERR, QString("Unable to determine directory " "to write to in %1 write command").arg(sURL)); return false; } if ((filename.contains("/../")) || (filename.startsWith("../"))) { LOG(VB_GENERAL, LOG_ERR, QString("ERROR: %1 write filename '%2' does not " "pass sanity checks.") .arg(sURL).arg(filename)); return false; } outFile = outDir + "/" + filename; if (GetMythDownloadManager()->download(sURL, outFile)) return true; return false; }
QString Content::GetHash( const QString &sStorageGroup, const QString &sFileName ) { if ((sFileName.isEmpty()) || (sFileName.contains("/../")) || (sFileName.startsWith("../"))) { LOG(VB_GENERAL, LOG_ERR, QString("ERROR checking for file, filename '%1' " "fails sanity checks").arg(sFileName)); return QString(); } QString storageGroup = "Default"; if (!sStorageGroup.isEmpty()) storageGroup = sStorageGroup; StorageGroup sgroup(storageGroup, gCoreContext->GetHostName()); QString fullname = sgroup.FindFile(sFileName); QString hash = FileHash(fullname); if (hash == "NULL") return QString(); return hash; }
/** \fn DBUtil::GetBackupDirectory(void) * \brief Determines the appropriate path for the database backup. * * The function requests the special "DB Backups" storage group. In the * event the group is not defined, the StorageGroup will fall back to using * the "Default" group. For users upgrading from version 0.20 or before * (which do not support Storage Groups), the StorageGroup will fall back to * using the old RecordFilePrefix. */ QString DBUtil::GetBackupDirectory() { QString directory; StorageGroup sgroup("DB Backups", gCoreContext->GetHostName()); QStringList dirList = sgroup.GetDirList(); if (dirList.size()) { directory = sgroup.FindNextDirMostFree(); if (!QDir(directory).exists()) { LOG(VB_FILE, LOG_INFO, "GetBackupDirectory() - ignoring " + directory + ", using /tmp"); directory = QString::null; } } if (directory.isNull()) // Rather than use kDefaultStorageDir, the default for // FindNextDirMostFree() when no dirs are defined for the StorageGroup, // use /tmp as it's possible that kDefaultStorageDir doesn't exist // and (at least on *nix) less possible that /tmp doesn't exist directory = "/tmp"; return directory; }
HTTPLiveStream::HTTPLiveStream(QString srcFile, uint16_t width, uint16_t height, uint32_t bitrate, uint32_t abitrate, uint16_t maxSegments, uint16_t segmentSize, uint32_t aobitrate, int32_t srate) : m_writing(false), m_streamid(-1), m_sourceFile(srcFile), m_sourceWidth(0), m_sourceHeight(0), m_segmentSize(segmentSize), m_maxSegments(maxSegments), m_segmentCount(0), m_startSegment(0), m_curSegment(0), m_height(height), m_width(width), m_bitrate(bitrate), m_audioBitrate(abitrate), m_audioOnlyBitrate(aobitrate), m_sampleRate(srate), m_created(MythDate::current()), m_lastModified(MythDate::current()), m_percentComplete(0), m_status(kHLSStatusUndefined) { if ((m_width == 0) && (m_height == 0)) m_width = 640; if (m_bitrate == 0) m_bitrate = 800000; if (m_audioBitrate == 0) m_audioBitrate = 64000; if (m_segmentSize == 0) m_segmentSize = 10; if (m_audioOnlyBitrate == 0) m_audioOnlyBitrate = 32000; m_sourceHost = gCoreContext->GetHostName(); QFileInfo finfo(m_sourceFile); m_outBase = finfo.fileName() + QString(".%1x%2_%3kV_%4kA").arg(m_width).arg(m_height) .arg(m_bitrate/1000).arg(m_audioBitrate/1000); SetOutputVars(); m_fullURL = m_httpPrefix + m_outBase + ".m3u8"; m_relativeURL = m_httpPrefixRel + m_outBase + ".m3u8"; StorageGroup sgroup("Streaming", gCoreContext->GetHostName()); m_outDir = sgroup.GetFirstDir(); QDir outDir(m_outDir); if (!outDir.exists() && !outDir.mkdir(m_outDir)) { LOG(VB_RECORD, LOG_ERR, "Unable to create HTTP Live Stream output " "directory, Live Stream will not be created"); return; } AddStream(); }
void MythUIHelperPrivate::Init(void) { screensaver = ScreenSaverControl::get(); GetScreenBounds(); StoreGUIsettings(); screenSetup = true; StorageGroup sgroup("Themes", gCoreContext->GetHostName()); m_userThemeDir = sgroup.GetFirstDir(true); }
bool Video::AddVideo( const QString &sFilename, const QString &sHost ) { if ( sHost.isEmpty() ) throw( QString( "Host not provided! Local storage is deprecated and " "is not supported by the API." )); if ( sFilename.isEmpty() || (sFilename.contains("/../")) || (sFilename.startsWith("../")) ) { throw( QString( "Filename not provided, or fails sanity checks!" )); } StorageGroup sgroup("Videos", sHost); QString fullname = sgroup.FindFile(sFilename); if ( !QFile::exists(fullname) ) throw( QString( "Provided filename does not exist!" )); QString hash = FileHash(fullname); if (hash == "NULL") { VERBOSE(VB_GENERAL, QString("Video Hash Failed. Unless this is a DVD " "or Blu-ray, something has probably gone " "wrong.")); hash = ""; } VideoMetadata newFile(sFilename, hash, VIDEO_TRAILER_DEFAULT, VIDEO_COVERFILE_DEFAULT, VIDEO_SCREENSHOT_DEFAULT, VIDEO_BANNER_DEFAULT, VIDEO_FANART_DEFAULT, VideoMetadata::FilenameToMeta(sFilename, 1), VideoMetadata::FilenameToMeta(sFilename, 4), QString(), VIDEO_YEAR_DEFAULT, QDate::fromString("0000-00-00","YYYY-MM-DD"), VIDEO_INETREF_DEFAULT, QString(), VIDEO_DIRECTOR_DEFAULT, QString(), VIDEO_PLOT_DEFAULT, 0.0, VIDEO_RATING_DEFAULT, 0, VideoMetadata::FilenameToMeta(sFilename, 2).toInt(), VideoMetadata::FilenameToMeta(sFilename, 3).toInt(), QDate::currentDate(), 0, ParentalLevel::plLowest); newFile.SetHost(sHost); newFile.SaveToDatabase(); return true; }
Symbol * ShadingContext::symbol (ustring name) { ShaderGroup &sgroup (*attribs()); int nlayers = sgroup.nlayers (); if (sgroup.llvm_compiled_version()) { for (int layer = nlayers-1; layer >= 0; --layer) { int symidx = sgroup[layer]->findsymbol (name); if (symidx >= 0) return sgroup[layer]->symbol (symidx); } } return NULL; }
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; }
QStringList Content::GetFileList( const QString &sStorageGroup ) { if (sStorageGroup.isEmpty()) { QString sMsg( "GetFileList - StorageGroup missing."); LOG(VB_UPNP, LOG_ERR, sMsg); throw sMsg; } StorageGroup sgroup(sStorageGroup); return sgroup.GetFileList("", true); }
QString VideoMetadata::VideoFileHash(const QString &file_name, const QString &host) { if (!host.isEmpty() && !isHostMaster(host)) { QString url = generate_file_url("Videos", host, file_name); return RemoteFile::GetFileHash(url); } else if (!host.isEmpty()) { StorageGroup sgroup("Videos", host); QString fullname = sgroup.FindFile(file_name); return FileHash(fullname); } else return FileHash(file_name); }
void * ShadingContext::symbol_data (Symbol &sym) { ShaderGroup &sgroup (*attribs()); if (! sgroup.llvm_compiled_version()) return NULL; // can't retrieve symbol if we didn't JIT and runit if (sym.dataoffset() >= 0 && (int)m_heap.size() > sym.dataoffset()) { // lives on the heap return &m_heap[sym.dataoffset()]; } // doesn't live on the heap if ((sym.symtype() == SymTypeParam || sym.symtype() == SymTypeOutputParam) && (sym.valuesource() == Symbol::DefaultVal || sym.valuesource() == Symbol::InstanceVal)) { ASSERT (sym.data()); return sym.data() ? sym.data() : NULL; } return NULL; // not something we can retrieve }
bool FileServerHandler::HandleDeleteFile(SocketHandler *socket, QString filename, QString storagegroup) { StorageGroup sgroup(storagegroup, "", false); QStringList res; if ((filename.isEmpty()) || (filename.contains("/../")) || (filename.startsWith("../"))) { LOG(VB_GENERAL, LOG_ERR, QString("ERROR deleting file, filename '%1' fails sanity checks") .arg(filename)); if (socket) { res << "0"; socket->SendStringList(res); return true; } return false; } QString fullfile = sgroup.FindFile(filename); if (fullfile.isEmpty()) { LOG(VB_GENERAL, LOG_ERR, QString("Unable to find %1 in HandleDeleteFile()") .arg(filename)); if (socket) { res << "0"; socket->SendStringList(res); return true; } return false; } QFile checkFile(fullfile); if (checkFile.exists()) { if (socket) { res << "1"; socket->SendStringList(res); } RunDeleteThread(); deletethread->AddFile(fullfile); } else { LOG(VB_GENERAL, LOG_ERR, QString("Error deleting file: '%1'") .arg(fullfile)); if (socket) { res << "0"; socket->SendStringList(res); } } return true; }
/** * \addtogroup myth_network_protocol * \par QUERY_FILE_HASH \e storagegroup \e filename */ bool FileServerHandler::HandleQueryFileHash(SocketHandler *socket, QStringList &slist) { QString storageGroup = "Default"; QString hostname = gCoreContext->GetHostName(); QString filename = ""; QStringList res; switch (slist.size()) { case 4: if (!slist[3].isEmpty()) hostname = slist[3]; case 3: if (!slist[2].isEmpty()) storageGroup = slist[2]; case 2: filename = slist[1]; if (filename.isEmpty() || filename.contains("/../") || filename.startsWith("../")) { LOG(VB_GENERAL, LOG_ERR, QString("ERROR checking for file, filename '%1' " "fails sanity checks").arg(filename)); res << ""; socket->SendStringList(res); return true; } break; default: return false; } QString hash = ""; if (hostname == gCoreContext->GetHostName()) { // looking for file on me, return directly StorageGroup sgroup(storageGroup, gCoreContext->GetHostName()); QString fullname = sgroup.FindFile(filename); hash = FileHash(fullname); } else { QReadLocker rlock(&m_fsLock); if (m_fsMap.contains(hostname)) { // looking for file on connected host, query from it if (m_fsMap[hostname]->SendReceiveStringList(slist)) hash = slist[0]; } else { // looking for file on unknown host // assume host is an IP address, and look for matching // entry in database MSqlQuery query(MSqlQuery::InitCon()); query.prepare("SELECT hostname FROM settings " "WHERE value='BackendServerIP' " " OR value='BackendServerIP6' " "AND data=:HOSTNAME;"); query.bindValue(":HOSTNAME", hostname); if (query.exec() && query.next()) { // address matches an entry hostname = query.value(0).toString(); if (m_fsMap.contains(hostname)) { // entry matches a connection slist.clear(); slist << "QUERY_FILE_HASH" << filename << storageGroup; if (m_fsMap[hostname]->SendReceiveStringList(slist)) hash = slist[0]; } } } } res << hash; socket->SendStringList(res); return true; }
/** * \addtogroup myth_network_protocol * \par QUERY_FILE_EXISTS \e filename \e storagegroup */ bool FileServerHandler::HandleQueryFileExists(SocketHandler *socket, QStringList &slist) { QString storageGroup = "Default"; QStringList res; if (slist.size() == 3) { if (!slist[2].isEmpty()) storageGroup = slist[2]; } else if (slist.size() != 2) return false; QString filename = slist[1]; if ((filename.isEmpty()) || (filename.contains("/../")) || (filename.startsWith("../"))) { LOG(VB_GENERAL, LOG_ERR, QString("ERROR checking for file, filename '%1' " "fails sanity checks").arg(filename)); res << ""; socket->SendStringList(res); return true; } StorageGroup sgroup(storageGroup, gCoreContext->GetHostName()); QString fullname = sgroup.FindFile(filename); if (!fullname.isEmpty()) { res << "1" << fullname; // TODO: convert me to QFile struct stat fileinfo; if (stat(fullname.toLocal8Bit().constData(), &fileinfo) >= 0) { res << QString::number(fileinfo.st_dev) << QString::number(fileinfo.st_ino) << QString::number(fileinfo.st_mode) << QString::number(fileinfo.st_nlink) << QString::number(fileinfo.st_uid) << QString::number(fileinfo.st_gid) << QString::number(fileinfo.st_rdev) << QString::number(fileinfo.st_size) #ifdef USING_MINGW << "0" << "0" #else << QString::number(fileinfo.st_blksize) << QString::number(fileinfo.st_blocks) #endif << QString::number(fileinfo.st_atime) << QString::number(fileinfo.st_mtime) << QString::number(fileinfo.st_ctime); } } else res << "0"; socket->SendStringList(res); return true; }
bool FileServerHandler::HandleAnnounce(MythSocket *socket, QStringList &commands, QStringList &slist) { if (commands[1] == "FileServer") { if (slist.size() >= 3) { SocketHandler *handler = new SocketHandler(socket, m_parent, commands[2]); handler->BlockShutdown(true); handler->AllowStandardEvents(true); handler->AllowSystemEvents(true); QWriteLocker wlock(&m_fsLock); m_fsMap.insert(commands[2], handler); m_parent->AddSocketHandler(handler); slist.clear(); slist << "OK"; handler->SendStringList(slist); return true; } return false; } if (commands[1] != "FileTransfer") return false; if (slist.size() < 3) return false; if ((commands.size() < 3) || (commands.size() > 6)) return false; FileTransfer *ft = NULL; QString hostname = ""; QString filename = ""; bool writemode = false; bool usereadahead = true; int timeout_ms = 2000; switch (commands.size()) { case 6: timeout_ms = commands[5].toInt(); case 5: usereadahead = commands[4].toInt(); case 4: writemode = commands[3].toInt(); default: hostname = commands[2]; } QStringList::const_iterator it = slist.begin(); QUrl qurl = *(++it); QString wantgroup = *(++it); QStringList checkfiles; while (++it != slist.end()) checkfiles += *(it); slist.clear(); LOG(VB_GENERAL, LOG_DEBUG, "FileServerHandler::HandleAnnounce"); LOG(VB_GENERAL, LOG_INFO, QString("adding: %1 as remote file transfer") .arg(hostname)); if (writemode) { if (wantgroup.isEmpty()) wantgroup = "Default"; StorageGroup sgroup(wantgroup, gCoreContext->GetHostName(), false); QString dir = sgroup.FindNextDirMostFree(); if (dir.isEmpty()) { LOG(VB_GENERAL, LOG_ERR, "Unable to determine directory " "to write to in FileTransfer write command"); slist << "ERROR" << "filetransfer_directory_not_found"; socket->writeStringList(slist); return true; } QString basename = qurl.path(); if (basename.isEmpty()) { LOG(VB_GENERAL, LOG_ERR, QString("FileTransfer write " "filename is empty in url '%1'.") .arg(qurl.toString())); slist << "ERROR" << "filetransfer_filename_empty"; socket->writeStringList(slist); return true; } if ((basename.contains("/../")) || (basename.startsWith("../"))) { LOG(VB_GENERAL, LOG_ERR, QString("FileTransfer write " "filename '%1' does not pass sanity checks.") .arg(basename)); slist << "ERROR" << "filetransfer_filename_dangerous"; socket->writeStringList(slist); return true; } filename = dir + "/" + basename; } else filename = LocalFilePath(qurl, wantgroup); QFileInfo finfo(filename); if (finfo.isDir()) { LOG(VB_GENERAL, LOG_ERR, QString("FileTransfer filename " "'%1' is actually a directory, cannot transfer.") .arg(filename)); slist << "ERROR" << "filetransfer_filename_is_a_directory"; socket->writeStringList(slist); return true; } if (writemode) { QString dirPath = finfo.absolutePath(); QDir qdir(dirPath); if (!qdir.exists()) { if (!qdir.mkpath(dirPath)) { LOG(VB_GENERAL, LOG_ERR, QString("FileTransfer " "filename '%1' is in a subdirectory which does " "not exist, but can not be created.") .arg(filename)); slist << "ERROR" << "filetransfer_unable_to_create_subdirectory"; socket->writeStringList(slist); return true; } } ft = new FileTransfer(filename, socket, m_parent, writemode); } else ft = new FileTransfer(filename, socket, m_parent, usereadahead, timeout_ms); ft->BlockShutdown(true); { QWriteLocker wlock(&m_ftLock); m_ftMap.insert(socket->socket(), ft); } slist << "OK" << QString::number(socket->socket()) << QString::number(ft->GetFileSize()); if (checkfiles.size()) { QFileInfo fi(filename); QDir dir = fi.absoluteDir(); for (it = checkfiles.begin(); it != checkfiles.end(); ++it) { if (dir.exists(*it) && QFileInfo(dir, *it).size() >= kReadTestSize) slist << *it; } } socket->writeStringList(slist); m_parent->AddSocketHandler(ft); return true; }
bool FileServerHandler::HandleDownloadFile(SocketHandler *socket, QStringList &slist) { QStringList res; if (slist.size() != 4) { res << "ERROR" << QString("Bad %1 command").arg(slist[0]); socket->SendStringList(res); return true; } bool synchronous = (slist[0] == "DOWNLOAD_FILE_NOW"); QString srcURL = slist[1]; QString storageGroup = slist[2]; QString filename = slist[3]; StorageGroup sgroup(storageGroup, gCoreContext->GetHostName(), false); QString outDir = sgroup.FindNextDirMostFree(); QString outFile; QStringList retlist; if (filename.isEmpty()) { QFileInfo finfo(srcURL); filename = finfo.fileName(); } if (outDir.isEmpty()) { LOG(VB_GENERAL, LOG_ERR, QString("Unable to determine directory " "to write to in %1 write command").arg(slist[0])); res << "ERROR" << "downloadfile_directory_not_found"; socket->SendStringList(res); return true; } if ((filename.contains("/../")) || (filename.startsWith("../"))) { LOG(VB_GENERAL, LOG_ERR, QString("ERROR: %1 write " "filename '%2' does not pass sanity checks.") .arg(slist[0]).arg(filename)); res << "ERROR" << "downloadfile_filename_dangerous"; socket->SendStringList(res); return true; } outFile = outDir + "/" + filename; if (synchronous) { if (GetMythDownloadManager()->download(srcURL, outFile)) { res << "OK" << gCoreContext->GetMasterHostPrefix(storageGroup) + filename; } else res << "ERROR"; } else { QMutexLocker locker(&m_downloadURLsLock); m_downloadURLs[outFile] = gCoreContext->GetMasterHostPrefix(storageGroup) + StorageGroup::GetRelativePathname(outFile); GetMythDownloadManager()->queueDownload(srcURL, outFile, this); res << "OK" << gCoreContext->GetMasterHostPrefix(storageGroup) + filename; } socket->SendStringList(res); return true; }
bool UPnpCDSVideo::LoadVideos(const UPnpCDSRequest* pRequest, UPnpCDSExtensionResults* pResults, IDTokenMap tokens) { QString sRequestId = pRequest->m_sObjectId; uint16_t nCount = pRequest->m_nRequestedCount; uint16_t nOffset = pRequest->m_nStartingIndex; // We must use a dedicated connection to get an acccurate value from // FOUND_ROWS() MSqlQuery query(MSqlQuery::InitCon(MSqlQuery::kDedicatedConnection)); QString sql = "SELECT SQL_CALC_FOUND_ROWS " "v.intid, title, subtitle, filename, director, plot, " "rating, year, userrating, length, " "season, episode, coverfile, insertdate, host, " "g.genre, studio, collectionref, contenttype " "FROM videometadata v " "LEFT JOIN videogenre g ON g.intid=v.category " "%1 " // "ORDER BY title, season, episode " "LIMIT :OFFSET,:COUNT "; QStringList clauses; QString whereString = BuildWhereClause(clauses, tokens); query.prepare(sql.arg(whereString)); BindValues(query, tokens); query.bindValue(":OFFSET", nOffset); query.bindValue(":COUNT", nCount); if (!query.exec()) return false; while (query.next()) { int nVidID = query.value( 0).toInt(); QString sTitle = query.value( 1).toString(); QString sSubtitle = query.value( 2).toString(); QString sFilePath = query.value( 3).toString(); QString sDirector = query.value( 4).toString(); QString sPlot = query.value( 5).toString(); // QString sRating = query.value( 6).toString(); int nYear = query.value( 7).toInt(); // int nUserRating = query.value( 8).toInt(); uint32_t nLength = query.value( 9).toUInt(); // Convert from minutes to milliseconds nLength = (nLength * 60 *1000); int nSeason = query.value(10).toInt(); int nEpisode = query.value(11).toInt(); QString sCoverArt = query.value(12).toString(); QDateTime dtInsertDate = MythDate::as_utc(query.value(13).toDateTime()); QString sHostName = query.value(14).toString(); QString sGenre = query.value(15).toString(); // QString sStudio = query.value(16).toString(); // QString sCollectionRef = query.value(17).toString(); QString sContentType = query.value(18).toString(); // ---------------------------------------------------------------------- // Cache Host ip Address & Port // ---------------------------------------------------------------------- // If the host-name is empty then we assume it is our local host // otherwise, we look up the host's IP address and port. When the // client then trys to play the video it will be directed to the // host which actually has the content. if (!m_mapBackendIp.contains( sHostName )) { if (sHostName.isEmpty()) { m_mapBackendIp[sHostName] = gCoreContext->GetBackendServerIP4(); } else { m_mapBackendIp[sHostName] = gCoreContext->GetBackendServerIP4(sHostName); } } if (!m_mapBackendPort.contains( sHostName )) { if (sHostName.isEmpty()) { m_mapBackendPort[sHostName] = gCoreContext->GetBackendStatusPort(); } else { m_mapBackendPort[sHostName] = gCoreContext->GetBackendStatusPort(sHostName); } } // ---------------------------------------------------------------------- // Build Support Strings // ---------------------------------------------------------------------- QString sName = sTitle; if( !sSubtitle.isEmpty() ) { sName += " - " + sSubtitle; } QUrl URIBase; URIBase.setScheme("http"); URIBase.setHost(m_mapBackendIp[sHostName]); URIBase.setPort(m_mapBackendPort[sHostName]); CDSObject *pItem; if (sContentType == "MOVIE") { pItem = CDSObject::CreateMovie( CreateIDString(sRequestId, "Video", nVidID), sTitle, pRequest->m_sParentId ); } else { pItem = CDSObject::CreateVideoItem( CreateIDString(sRequestId, "Video", nVidID), sName, pRequest->m_sParentId ); } if (!sSubtitle.isEmpty()) pItem->SetPropValue( "description", sSubtitle ); else pItem->SetPropValue( "description", sPlot.left(128).append(" ...")); pItem->SetPropValue( "longDescription", sPlot ); pItem->SetPropValue( "director" , sDirector ); if (nEpisode > 0 || nSeason > 0) // There has got to be a better way { pItem->SetPropValue( "seriesTitle" , sTitle ); pItem->SetPropValue( "programTitle" , sSubtitle ); pItem->SetPropValue( "episodeNumber" , QString::number(nEpisode)); //pItem->SetPropValue( "episodeCount" , nEpisodeCount); } pItem->SetPropValue( "genre" , sGenre ); if (nYear > 1830 && nYear < 9999) pItem->SetPropValue( "date", QDate(nYear,1,1).toString(Qt::ISODate)); else pItem->SetPropValue( "date", UPnPDateTime::DateTimeFormat(dtInsertDate) ); // HACK: Windows Media Centre Compat (Not a UPnP or DLNA requirement, should only be done for WMC) // pItem->SetPropValue( "genre" , "[Unknown Genre]" ); // pItem->SetPropValue( "actor" , "[Unknown Author]" ); // pItem->SetPropValue( "creator" , "[Unknown Creator]" ); // pItem->SetPropValue( "album" , "[Unknown Album]" ); //// //pItem->SetPropValue( "producer" , ); //pItem->SetPropValue( "rating" , ); //pItem->SetPropValue( "actor" , ); //pItem->SetPropValue( "publisher" , ); //pItem->SetPropValue( "language" , ); //pItem->SetPropValue( "relation" , ); //pItem->SetPropValue( "region" , ); // Only add the reference ID for items which are not in the // 'All Videos' container QString sRefIDBase = QString("%1/Video").arg(m_sExtensionId); if ( pRequest->m_sParentId != sRefIDBase ) { QString sRefId = QString( "%1=%2") .arg( sRefIDBase ) .arg( nVidID ); pItem->SetPropValue( "refID", sRefId ); } // FIXME - If the slave or storage hosting this video is offline we // won't find it. We probably shouldn't list it, but better // still would be storing the filesize in the database so we // don't waste time re-checking it constantly QString sFullFileName = sFilePath; if (!QFile::exists( sFullFileName )) { StorageGroup sgroup("Videos"); sFullFileName = sgroup.FindFile( sFullFileName ); } QFileInfo fInfo( sFullFileName ); // ---------------------------------------------------------------------- // Add Video Resource Element based on File extension (HTTP) // ---------------------------------------------------------------------- QString sMimeType = HTTPRequest::GetMimeType( QFileInfo(sFilePath).suffix() ); // HACK: If we are dealing with a Sony Blu-ray player then we fake the // MIME type to force the video to appear // if ( pRequest->m_eClient == CDS_ClientSonyDB ) // { // sMimeType = "video/avi"; // } QUrl resURI = URIBase; QUrlQuery resQuery; resURI.setPath("/Content/GetVideo"); resQuery.addQueryItem("Id", QString::number(nVidID)); resURI.setQuery(resQuery); // DLNA requires a mimetype of video/mp2p for TS files, it's not the // correct mimetype, but then DLNA doesn't seem to care about such // things if (sMimeType == "video/mp2t" || sMimeType == "video/mp2p") sMimeType = "video/mpeg"; QString sProtocol = DLNA::ProtocolInfoString(UPNPProtocol::kHTTP, sMimeType); Resource *pRes = pItem->AddResource( sProtocol, resURI.toEncoded() ); pRes->AddAttribute( "size" , QString("%1").arg(fInfo.size()) ); pRes->AddAttribute( "duration", UPnPDateTime::resDurationFormat(nLength) ); // ---------------------------------------------------------------------- // Add Artwork // ---------------------------------------------------------------------- if (!sCoverArt.isEmpty() && (sCoverArt != "No Cover")) { PopulateArtworkURIS(pItem, nVidID, URIBase); } pResults->Add( pItem ); pItem->DecrRef(); } // Just in case FOUND_ROWS() should fail, ensure m_nTotalMatches contains // at least the size of this result set if (query.size() >= 0) pResults->m_nTotalMatches = query.size(); // Fetch the total number of matches ignoring any LIMITs query.prepare("SELECT FOUND_ROWS()"); if (query.exec() && query.next()) pResults->m_nTotalMatches = query.value(0).toUInt(); return true; }