//------------------------------------------------------------------------------------------------------------------- void Xcddb::parseData(const char *buffer) { //writeLog("parseData Start"); std::map<CStdString, CStdString> keywords; std::list<CStdString> keywordsOrder; // remember order of keywords as it appears in data received from CDDB // Collect all the keywords and put them in map. // Multiple occurrences of the same keyword indicate that // the data contained on those lines should be concatenated char *line; const char trenner[3] = {'\n', '\r', '\0'}; line = strtok((char*)buffer, trenner); // skip first line while ((line = strtok(0, trenner))) { // Lines that begin with # are comments, should be ignored if (line[0] != '#') { char *s = strstr(line, "="); if (s != NULL) { CStdString strKeyword(line, s - line); strKeyword.TrimRight(" "); CStdString strValue(s+1); strValue.Replace("\\n", "\n"); strValue.Replace("\\t", "\t"); strValue.Replace("\\\\", "\\"); g_charsetConverter.unknownToUTF8(strValue); std::map<CStdString, CStdString>::const_iterator it = keywords.find(strKeyword); if (it != keywords.end()) strValue = it->second + strValue; // keyword occured before, concatenate else keywordsOrder.push_back(strKeyword); keywords[strKeyword] = strValue; } } } // parse keywords for (std::list<CStdString>::const_iterator it = keywordsOrder.begin(); it != keywordsOrder.end(); ++it) { CStdString strKeyword = *it; CStdString strValue = keywords[strKeyword]; if (strKeyword == "DTITLE") { // DTITLE may contain artist and disc title, separated with " / ", // for example: DTITLE=Modern Talking / Album: Victory (The 11th Album) bool found = false; for (int i = 0; i < strValue.GetLength() - 2; i++) { if (strValue[i] == ' ' && strValue[i + 1] == '/' && strValue[i + 2] == ' ') { m_strDisk_artist = TrimToUTF8(strValue.Left(i)); m_strDisk_title = TrimToUTF8(strValue.Mid(i+3)); found = true; break; } } if (!found) m_strDisk_title = TrimToUTF8(strValue); } else if (strKeyword == "DYEAR") m_strYear = TrimToUTF8(strValue); else if (strKeyword== "DGENRE") m_strGenre = TrimToUTF8(strValue); else if (strKeyword.Left(6) == "TTITLE") addTitle(strKeyword + "=" + strValue); else if (strKeyword == "EXTD") { CStdString strExtd(strValue); if (m_strYear.IsEmpty()) { // Extract Year from extended info // as a fallback int iPos = strExtd.Find("YEAR:"); if (iPos > -1) // You never know if you really get UTF-8 strings from cddb g_charsetConverter.unknownToUTF8(strExtd.Mid(iPos + 6, 4), m_strYear); } if (m_strGenre.IsEmpty()) { // Extract ID3 Genre // as a fallback int iPos = strExtd.Find("ID3G:"); if (iPos > -1) { CStdString strGenre = strExtd.Mid(iPos + 5, 4); strGenre.TrimLeft(' '); if (StringUtils::IsNaturalNumber(strGenre)) { CID3Tag tag; m_strGenre=tag.ParseMP3Genre(strGenre); } } } } else if (strKeyword.Left(4) == "EXTT") addExtended(strKeyword + "=" + strValue); } //writeLog("parseData Ende"); }
bool URIUtils::IsMultiPath(const CStdString& strPath) { return strPath.Left(10).Equals("multipath:"); }
bool URIUtils::IsCDDA(const CStdString& strFile) { return strFile.Left(5).Equals("cdda:"); }
CFileItem* CHDDirectory::BuildResolvedFileItem(const CStdString& strRoot, WIN32_FIND_DATA& wfd) { CFileItem* pItem = 0; #ifdef __APPLE__ // Attempt to resolve aliases. FSRef fsRef; Boolean isDir; Boolean isAlias; char resolvedAliasPath[MAX_PATH]; bool useAlias = false; // Convert to FSRef. CStdString strPath = strRoot; CStdString strFile = strPath + wfd.cFileName; if (FSPathMakeRef((const UInt8* )strFile.c_str(), &fsRef, &isDir) == noErr) { if (FSResolveAliasFileWithMountFlags(&fsRef, TRUE, &isDir, &isAlias, kResolveAliasFileNoUI) == noErr) { // If it was an alias, use the reference instead. if (isAlias) { if (FSRefMakePath(&fsRef, (UInt8* )resolvedAliasPath, MAX_PATH) == noErr) useAlias = true; } } else { // Broken item. return 0; } } // Compute the *final* name/path of the file. if (useAlias) { strPath = resolvedAliasPath; strFile = CUtil::GetFileName(strPath); strPath = strPath.Left(strPath.length()-strFile.length()); } else { strFile = wfd.cFileName; } // Check for smart folders. if (CUtil::IsSmartFolder(strFile)) { // Use the original name, without extension. CStdString smartFolder = strFile; int iPos = smartFolder.ReverseFind("."); if (iPos > 0) smartFolder = smartFolder.Left(iPos); strFile.Replace(':', '/'); pItem = new CFileItem(smartFolder); //pItem->m_strPath = "smartfolder:/" + strPath + strFile; pItem->m_strPath = strPath + strFile; pItem->m_bIsFolder = true; } else if (useAlias) { // Add the alias. strFile.Replace(':', '/'); pItem = new CFileItem(strFile); pItem->m_strPath = resolvedAliasPath; pItem->m_bIsFolder = isDir ? true : false; if (isDir == false) { // Get the size of the file. struct stat64 statInfo; stat64(resolvedAliasPath, &statInfo); pItem->m_dwSize = statInfo.st_size; } } else #endif { // Go the default route. pItem = BuildFileItem(strRoot, wfd); } // Save file time. FILETIME localTime; FileTimeToLocalFileTime(&wfd.ftLastWriteTime, &localTime); pItem->m_dateTime = localTime; return pItem; }
void CMythSession::SetSeasonAndEpisode(const cmyth_proginfo_t &program, int *season, int *episode) { /* * A valid programid generated from an XMLTV source should look like: * [EP|MV|SH|SP][seriesid][episode][season]([partnumber][parttotal]) * mythtv/trunk/programs/mytfilldatabaseline/xmltvparser.cpp - Line 522 onwards. * * Season changed to a base36 character for XMLTV in Myth 0.24. http://svn.mythtv.org/trac/changeset/24724 * * A valid SchedulesDirect programid appears to have a similar format to the XMLTV programid but * doesn't have any obvious way to parse out the season and episode information. The number at the * end of the programid could possibly be the completely sequential number for the episode, but * even that doesn't seem to match up with TVDB. SchedulesDirect data does seem to have a valid * original air date though, so if we identify a SchedulesDirect programid, leave the season and * episode as 0. */ CStdString programid = GetValue(m_dll->proginfo_programid(program)); CStdString seriesid = GetValue(m_dll->proginfo_seriesid(program)); /* * Default the season and episode to 0 so XBMC treats the content as an episode and displays tag * information. If the season and episode can be parsed from the programid these will be * overwritten. */ *season = 0; *episode = 0; if (programid.IsEmpty() // Can't do anything if the program ID is empty || seriesid.IsEmpty()) // Can't figure out the end parsing if the series ID is empty { return; CStdString category = programid.Left(2); // Valid for both XMLTV and SchedulesDirect sources if (category != "MV" // Movie && category != "EP" // Series && category != "SH" // TV Show && category != "SP") // Sports return; if (programid.Mid(category.length(), seriesid.length()) != seriesid) // Series ID does not follow the category return; CStdString remainder = programid.Mid(category.length() + seriesid.length()); // Whatever is after series ID /* * All SchedulesDirect remainders appear to be 4 characters and start with a 0. If the assumption * is correct that the number somehow relates to the sequential episode number across all seasons * then we can ignore remainders that start with 0. It will be very unlikely for a sequential * episode number for a series to be > 999. */ if (remainder.length() == 4 // All SchedulesDirect codes seem to be 4 characters && remainder.Left(0) == "0") // Padded with 0's for low number. No valid XMLTV remainder will start with 0. return; /* * If the remainder is more than 5 characters, it must include the optional part number and total * number of parts. Strip off the last 2 characters assuming that there are ridiculously few * cases where the number of parts for a single episode is > 9. */ if (remainder.length() >= 5) // Must include optional part number and total number of parts remainder = remainder.Left(remainder.length() - 2); // Assumes part number and total are both < 10 /* * Now for some heuristic black magic. */ if (remainder.length() == 2) // Single character season and episode. { *season = atoi(remainder.Right(1)); // TODO: Fix for base 36 in Myth 0.24. Assume season < 10 *episode = atoi(remainder.Left(1)); } else if (remainder.length() == 3) // Ambiguous in Myth 0.23. Single character season in Myth 0.24 { /* * Following heuristics are intended to work with largest possible number of cases. It won't be * perfect, but way better than just assuming the season is < 10. */ if (remainder.Right(1) == "0") // e.g. 610. Unlikely to have a season of 0 (specials) with more than 9 special episodes. { *season = atoi(remainder.Right(2)); *episode = atoi(remainder.Left(1)); } else if (remainder.Mid(2, 1) == "0") // e.g. 203. Can't have a season start with 0. Must be end of episode. { *season = atoi(remainder.Right(1)); // TODO: Fix for base 36 in Myth 0.24. Assume season < 10 *episode = atoi(remainder.Left(2)); } else if (atoi(remainder.Left(1)) > 3) // e.g. 412. Very unlikely to have more than 39 episodes per season if season > 9. { /* * TODO: See if a check for > 2 is better, e.g. is it still unlike to have more than 29 episodes * per season if season > 9? */ *season = atoi(remainder.Right(2)); *episode = atoi(remainder.Left(1)); } else // e.g. 129. Assume season is < 10 or Myth 0.24 Base 36 season. { *season = atoi(remainder.Right(1)); // TODO: Fix for base 36 in Myth 0.24. Assume season < 10 *episode = atoi(remainder.Left(2)); } } else if (remainder.length() == 4) // Double digit season and episode in Myth 0.23 OR TODO: has part number and total number of parts { *season = atoi(remainder.Right(2)); *episode = atoi(remainder.Left(2)); } return; }
bool URIUtils::IsVideoDb(const CStdString& strFile) { return strFile.Left(8).Equals("videodb:"); }
bool URIUtils::IsBluray(const CStdString& strFile) { return strFile.Left(7).Equals("bluray:"); }
void FileRecursiveMkdir(CStdString& path, int permissions, CStdString owner, CStdString group, CStdString rootDirectory) { int position = 0, newPermissions = permissions; bool done = false; /* * Create the directories first. We have separated this because * we do not want the introduction of rootDirectory to break * any old functionality. */ while (!done) { position = path.Find('/', position+1); if (position == -1) { done = true; } else { CStdString level = path.Left(position); ACE_OS::mkdir((PCSTR)level); } } done = false; position = 0; if(rootDirectory.size()) { if(path.Find(rootDirectory) >= 0) { position = 1 + rootDirectory.size(); } } if(newPermissions & S_IRUSR) { newPermissions |= S_IXUSR; } if(newPermissions & S_IRGRP) { newPermissions |= S_IXGRP; } if(newPermissions & S_IROTH) { newPermissions |= S_IXOTH; } while (!done) { position = path.Find('/', position+1); if (position == -1) { done = true; } else { CStdString level = path.Left(position); if(owner.size() && group.size()) { FileSetOwnership(level, owner, group); } if(newPermissions) { FileSetPermissions(level, newPermissions); } } } }
//********************************************************************************************* bool CRTVDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items) { CURL url(strPath); CStdString strRoot = strPath; URIUtils::AddSlashAtEnd(strRoot); // Host name is "*" so we try to discover all ReplayTVs. This requires some trickery but works. if (url.GetHostName() == "*") { // Check to see whether the URL's path is blank or "Video" if (url.GetFileName() == "" || url.GetFileName() == "Video") { int iOldSize=items.Size(); struct RTV * rtv = NULL; int numRTV; // Request that all ReplayTVs on the LAN identify themselves. Each ReplayTV // is given 3000ms to respond to the request. rtv_discovery returns an array // of structs containing the IP and friendly name of all ReplayTVs found on the LAN. // For some reason, DVArchive doesn't respond to this request (probably only responds // to requests from an IP address of a real ReplayTV). numRTV = rtv_discovery(&rtv, 3000); // Run through the array and add the ReplayTVs found as folders in XBMC. // We must add the IP of each ReplayTV as if it is a file name at the end of a the // auto-discover URL--e.g. rtv://*/192.168.1.100--because XBMC does not permit // dyamically added shares and will not play from them. This little trickery is // the best workaround I could come up with. for (int i = 0; i < numRTV; i++) { CFileItemPtr pItem(new CFileItem(rtv[i].friendlyName)); // This will keep the /Video or / and allow one to set up an auto ReplayTV // share of either type--simple file listing or ReplayGuide listing. pItem->SetPath(strRoot + rtv[i].hostname); pItem->m_bIsFolder = true; pItem->SetLabelPreformated(true); items.Add(pItem); } free(rtv); return (items.Size()>iOldSize); // Else the URL's path should be an IP address of the ReplayTV } else { CStdString strURL, strRTV; int pos; // Isolate the IP from the URL and replace the "*" with the real IP // of the ReplayTV. E.g., rtv://*/Video/192.168.1.100/ becomes // rtv://192.168.1.100/Video/ . This trickery makes things work. strURL = strRoot.TrimRight('/'); pos = strURL.ReverseFind('/'); strRTV = strURL.Left(pos + 1); strRTV.Replace("*", strURL.Mid(pos + 1)); CURL tmpURL(strRTV); // Force the newly constructed share into the right variables to // be further processed by the remainder of GetDirectory. url = tmpURL; strRoot = strRTV; } } // Allow for ReplayTVs on ports other than 80 CStdString strHostAndPort; strHostAndPort = url.GetHostName(); if (url.HasPort()) { char buffer[10]; sprintf(buffer,"%i",url.GetPort()); strHostAndPort += ':'; strHostAndPort += buffer; } // No path given, list shows from ReplayGuide if (url.GetFileName() == "") { unsigned char * data = NULL; // Get the RTV guide data in XML format rtv_get_guide_xml(&data, strHostAndPort.c_str()); // Begin parsing the XML data CXBMCTinyXML xmlDoc; xmlDoc.Parse( (const char *) data ); if ( xmlDoc.Error() ) { free(data); return false; } TiXmlElement* pRootElement = xmlDoc.RootElement(); if (!pRootElement) { free(data); return false; } const TiXmlNode *pChild = pRootElement->FirstChild(); while (pChild > 0) { CStdString strTagName = pChild->Value(); if ( !strcmpi(strTagName.c_str(), "ITEM") ) { const TiXmlNode *nameNode = pChild->FirstChild("DISPLAYNAME"); // const TiXmlNode *qualityNode = pChild->FirstChild("QUALITY"); const TiXmlNode *recordedNode = pChild->FirstChild("RECORDED"); const TiXmlNode *pathNode = pChild->FirstChild("PATH"); // const TiXmlNode *durationNode = pChild->FirstChild("DURATION"); const TiXmlNode *sizeNode = pChild->FirstChild("SIZE"); const TiXmlNode *atrbNode = pChild->FirstChild("ATTRIB"); SYSTEMTIME dtDateTime; DWORD dwFileSize = 0; memset(&dtDateTime, 0, sizeof(dtDateTime)); // DISPLAYNAME const char* szName = NULL; if (nameNode) { szName = nameNode->FirstChild()->Value() ; } else { // Something went wrong, the recording has no name free(data); return false; } // QUALITY // const char* szQuality = NULL; // if (qualityNode) // { // szQuality = qualityNode->FirstChild()->Value() ; // } // RECORDED if (recordedNode) { CStdString strRecorded = recordedNode->FirstChild()->Value(); int iYear, iMonth, iDay; iYear = atoi(strRecorded.Left(4).c_str()); iMonth = atoi(strRecorded.Mid(5, 2).c_str()); iDay = atoi(strRecorded.Mid(8, 2).c_str()); dtDateTime.wYear = iYear; dtDateTime.wMonth = iMonth; dtDateTime.wDay = iDay; int iHour, iMin, iSec; iHour = atoi(strRecorded.Mid(11, 2).c_str()); iMin = atoi(strRecorded.Mid(14, 2).c_str()); iSec = atoi(strRecorded.Mid(17, 2).c_str()); dtDateTime.wHour = iHour; dtDateTime.wMinute = iMin; dtDateTime.wSecond = iSec; } // PATH const char* szPath = NULL; if (pathNode) { szPath = pathNode->FirstChild()->Value() ; } else { // Something went wrong, the recording has no filename free(data); return false; } // DURATION // const char* szDuration = NULL; // if (durationNode) // { // szDuration = durationNode->FirstChild()->Value() ; // } // SIZE // NOTE: Size here is actually just duration in minutes because // filesize is not reported by the stripped down GuideParser I use if (sizeNode) { dwFileSize = atol( sizeNode->FirstChild()->Value() ); } // ATTRIB // NOTE: Not currently reported in the XML guide data, nor is it particularly // needed unless someone wants to add the ability to sub-divide the recordings // into categories, as on a real RTV. int attrib = 0; if (atrbNode) { attrib = atoi( atrbNode->FirstChild()->Value() ); } bool bIsFolder(false); if (attrib & FILE_ATTRIBUTE_DIRECTORY) bIsFolder = true; CFileItemPtr pItem(new CFileItem(szName)); pItem->m_dateTime=dtDateTime; pItem->SetPath(strRoot + szPath); // Hack to show duration of show in minutes as KB in XMBC because // it doesn't currently permit showing duration in minutes. // E.g., a 30 minute show will show as 29.3 KB in XBMC. pItem->m_dwSize = dwFileSize * 1000; pItem->m_bIsFolder = bIsFolder; pItem->SetLabelPreformated(true); items.Add(pItem); } pChild = pChild->NextSibling(); } free(data); // Path given (usually Video), list filenames only } else { unsigned char * data; char * p, * q; unsigned long status; // Return a listing of all files in the given path status = rtv_list_files(&data, strHostAndPort.c_str(), url.GetFileName().c_str()); if (status == 0) { return false; } // Loop through the file list using pointers p and q, where p will point to the current // filename and q will point to the next filename p = (char *) data; while (p) { // Look for the end of the current line of the file listing q = strchr(p, '\n'); // If found, replace the newline character with the NULL terminator if (q) { *q = '\0'; // Increment q so that it points to the next filename q++; // *p should be the current null-terminated filename in the list if (*p) { // Only display MPEG files in XBMC (but not circular.mpg, as that is the RTV // video buffer and XBMC may cause problems if it tries to play it) if (strstr(p, ".mpg") && !strstr(p, "circular")) { CFileItemPtr pItem(new CFileItem(p)); pItem->SetPath(strRoot + p); pItem->m_bIsFolder = false; // The list returned by the RTV doesn't include file sizes, unfortunately //pItem->m_dwSize = atol(szSize); pItem->SetLabelPreformated(true); items.Add(pItem); } } } // Point p to the next filename in the list and loop p = q; } free(data); } return true; }
// TODO: Currently no support for "embedded thumb" as there is no easy way to grab it // without sending a file that has this as it's album to this class void CGUIDialogMusicInfo::OnGetThumb() { CFileItemList items; // Current thumb if (CFile::Exists(m_albumItem->GetThumbnailImage())) { CFileItemPtr item(new CFileItem("thumb://Current", false)); item->SetThumbnailImage(m_albumItem->GetThumbnailImage()); item->SetLabel(g_localizeStrings.Get(20016)); items.Add(item); } // Grab the thumbnail(s) from the web vector<CStdString> thumbs; if (m_bArtistInfo) m_artist.thumbURL.GetThumbURLs(thumbs); else m_album.thumbURL.GetThumbURLs(thumbs); for (unsigned int i = 0; i < thumbs.size(); ++i) { CStdString strItemPath; strItemPath.Format("thumb://Remote%i", i); CFileItemPtr item(new CFileItem(strItemPath, false)); item->SetThumbnailImage(thumbs[i]); item->SetIconImage("DefaultPicture.png"); item->SetLabel(g_localizeStrings.Get(20015)); // TODO: Do we need to clear the cached image? // CTextureCache::Get().ClearCachedImage(thumb); items.Add(item); } // local thumb CStdString localThumb; if (m_bArtistInfo) { CMusicDatabase database; database.Open(); CStdString strArtistPath; if (database.GetArtistPath(m_artist.idArtist,strArtistPath)) URIUtils::AddFileToFolder(strArtistPath,"folder.jpg",localThumb); } else localThumb = m_albumItem->GetUserMusicThumb(); if (CFile::Exists(localThumb)) { CFileItemPtr item(new CFileItem("thumb://Local", false)); item->SetThumbnailImage(localThumb); item->SetLabel(g_localizeStrings.Get(20017)); items.Add(item); } else { CFileItemPtr item(new CFileItem("thumb://None", false)); if (m_bArtistInfo) item->SetIconImage("DefaultArtist.png"); else item->SetIconImage("DefaultAlbumCover.png"); item->SetLabel(g_localizeStrings.Get(20018)); items.Add(item); } CStdString result; bool flip=false; VECSOURCES sources(g_settings.m_musicSources); g_mediaManager.GetLocalDrives(sources); if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(1030), result, &flip)) return; // user cancelled if (result == "thumb://Current") return; // user chose the one they have // delete the thumbnail if that's what the user wants, else overwrite with the // new thumbnail CStdString cachedThumb; if (m_bArtistInfo) cachedThumb = m_albumItem->GetCachedArtistThumb(); else cachedThumb = CThumbnailCache::GetAlbumThumb(m_album); CTextureCache::Get().ClearCachedImage(cachedThumb, true); if (result.Left(14) == "thumb://Remote") { int number = atoi(result.Mid(14)); CFile::Cache(thumbs[number], cachedThumb); } else if (result == "thumb://Local") CFile::Cache(localThumb, cachedThumb); else if (CFile::Exists(result)) CPicture::CreateThumbnail(result, cachedThumb); else result = "thumb://None"; if (result == "thumb://None") { // clear this thumb (note - it'll likely be recached, nothing we can do about that at this point) CTextureCache::Get().ClearCachedImage(cachedThumb, true); cachedThumb = ""; } m_albumItem->SetThumbnailImage(cachedThumb); m_hasUpdatedThumb = true; // tell our GUI to completely reload all controls (as some of them // are likely to have had this image in use so will need refreshing) CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS); g_windowManager.SendMessage(msg); // Update our screen Update(); }
// Allow user to select a Fanart void CGUIDialogMusicInfo::OnGetFanart() { CFileItemList items; CStdString cachedThumb(CThumbnailCache::GetThumb(m_artist.strArtist,g_settings.GetMusicFanartFolder())); if (CFile::Exists(cachedThumb)) { CFileItemPtr itemCurrent(new CFileItem("fanart://Current",false)); itemCurrent->SetThumbnailImage(cachedThumb); itemCurrent->SetLabel(g_localizeStrings.Get(20440)); items.Add(itemCurrent); } // Grab the thumbnails from the web for (unsigned int i = 0; i < m_artist.fanart.GetNumFanarts(); i++) { CStdString strItemPath; strItemPath.Format("fanart://Remote%i",i); CFileItemPtr item(new CFileItem(strItemPath, false)); CStdString thumb = m_artist.fanart.GetPreviewURL(i); item->SetThumbnailImage(CTextureCache::GetWrappedThumbURL(thumb)); item->SetIconImage("DefaultPicture.png"); item->SetLabel(g_localizeStrings.Get(20441)); // TODO: Do we need to clear the cached image? // CTextureCache::Get().ClearCachedImage(thumb); items.Add(item); } // Grab a local thumb CMusicDatabase database; database.Open(); CStdString strArtistPath; database.GetArtistPath(m_artist.idArtist,strArtistPath); CFileItem item(strArtistPath,true); CStdString strLocal = item.GetLocalFanart(); if (!strLocal.IsEmpty()) { CFileItemPtr itemLocal(new CFileItem("fanart://Local",false)); itemLocal->SetThumbnailImage(strLocal); itemLocal->SetLabel(g_localizeStrings.Get(20438)); // TODO: Do we need to clear the cached image? CTextureCache::Get().ClearCachedImage(strLocal); items.Add(itemLocal); } else { CFileItemPtr itemNone(new CFileItem("fanart://None", false)); itemNone->SetIconImage("DefaultArtist.png"); itemNone->SetLabel(g_localizeStrings.Get(20439)); items.Add(itemNone); } CStdString result; VECSOURCES sources(g_settings.m_musicSources); g_mediaManager.GetLocalDrives(sources); bool flip=false; if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445)) return; // user cancelled // delete the thumbnail if that's what the user wants, else overwrite with the // new thumbnail if (result.Equals("fanart://Current")) return; if (result.Equals("fanart://Local")) result = strLocal; CTextureCache::Get().ClearCachedImage(cachedThumb, true); if (!result.Equals("fanart://None")) { // local file if (result.Left(15) == "fanart://Remote") { int iFanart = atoi(result.Mid(15).c_str()); m_artist.fanart.SetPrimaryFanart(iFanart); // download the fullres fanart image CStdString tempFile = "special://temp/fanart_download.jpg"; CAsyncFileCopy downloader; if (!downloader.Copy(m_artist.fanart.GetImageURL(), tempFile, g_localizeStrings.Get(13413))) return; result = tempFile; } if (flip) CPicture::ConvertFile(result, cachedThumb,0,1920,-1,100,true); else CPicture::CacheFanart(result, cachedThumb); m_albumItem->SetProperty("fanart_image",cachedThumb); m_hasUpdatedThumb = true; } // tell our GUI to completely reload all controls (as some of them // are likely to have had this image in use so will need refreshing) CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS); g_windowManager.SendMessage(msg); // Update our screen Update(); }
bool CAVPlayerOperations::IsCorrectPlayer(const CStdString &method) { return (method.Left(5).Equals("audio") && g_application.IsPlayingAudio()) || (method.Left(5).Equals("video") && g_application.IsPlayingVideo()); }
bool IsRoutableAddress(const CStdString& address) { if (address.Find(_T(":")) != -1) { CStdString long_address = GetIPV6LongForm(address); if (long_address.IsEmpty()) return false; if (long_address[0] == '0') { // ::/128 if (long_address == _T("0000:0000:0000:0000:0000:0000:0000:0000")) return false; // ::1/128 if (long_address == _T("0000:0000:0000:0000:0000:0000:0000:0001")) return false; if (long_address.Left(30) == _T("0000:0000:0000:0000:0000:ffff:")) { // IPv4 mapped CStdString ipv4; ipv4.Format(_T("%d.%d.%d.%d"), DigitHexToDecNum(long_address[30]) * 16 + DigitHexToDecNum(long_address[31]), DigitHexToDecNum(long_address[32]) * 16 + DigitHexToDecNum(long_address[33]), DigitHexToDecNum(long_address[35]) * 16 + DigitHexToDecNum(long_address[36]), DigitHexToDecNum(long_address[37]) * 16 + DigitHexToDecNum(long_address[38])); return IsRoutableAddress(ipv4); } return true; } if (long_address[0] == 'f') { if (long_address[1] == 'e') { // fe80::/10 (link local) const TCHAR& c = long_address[2]; int v; if (c >= 'a') v = c - 'a' + 10; else v = c - '0'; if ((v & 0xc) == 0x8) return false; return true; } else if (long_address[1] == 'c' || long_address[1] == 'd') { // fc00::/7 (site local) return false; } } return true; } else { // Assumes address is already a valid IP address if (address.Left(3) == _T("127") || address.Left(3) == _T("10.") || address.Left(7) == _T("192.168") || address.Left(7) == _T("169.254")) return false; if (address.Left(3) == _T("172")) { CStdString middle = address.Mid(4); int pos = address.Find(_T(".")); if (pos == -1) return false; int part = _ttoi(middle.Left(pos)); if (part >= 16 && part <= 31) return false; } return true; } }
bool MatchesFilter(CStdString filter, CStdString ip) { // A single asterix matches all IPs. if (filter == _T("*")) return true; // Check for IP range syntax. int pos = filter.Find('/'); if (pos != -1) { // CIDR filter int prefixLength = _ttoi(filter.Mid(pos+1)); if (ip.Find(':') != -1) { // IPv6 address CStdString left = GetIPV6LongForm(filter.Left(pos)); if (left.Find(':') == -1) return false; ip = GetIPV6LongForm(ip); LPCTSTR i = ip; LPCTSTR f = left; while (prefixLength >= 4) { if (*i != *f) return false; if (!*i) return true; if (*i == ':') { ++i; ++f; } ++i; ++f; prefixLength -= 4; } if (!prefixLength) return true; int mask; if (prefixLength == 1) mask = 0x8; else if (prefixLength == 2) mask = 0xc; else mask = 0xe; return (DigitHexToDecNum(*i) & mask) == (DigitHexToDecNum(*f) & mask); } else { if (prefixLength < 0) prefixLength = 0; else if (prefixLength > 32) prefixLength = 32; // IPv4 address CStdString left = filter.Left(pos); if (left.Find(':') != -1) return false; unsigned long i = ntohl(inet_addr(ConvToLocal(ip))); unsigned long f = ntohl(inet_addr(ConvToLocal(left))); i &= prefixMasksV4[prefixLength]; f &= prefixMasksV4[prefixLength]; return i == f; } } else { // Literal filter if (filter.Find(':') != -1) return filter == GetIPV6ShortForm(ip); else return filter == ip; } }
bool URIUtils::IsHTSP(const CStdString& strFile) { return strFile.Left(5).Equals("htsp:"); }
bool URIUtils::IsDAAP(const CStdString& strFile) { return strFile.Left(5).Equals("daap:"); }
bool URIUtils::IsMusicDb(const CStdString& strFile) { return strFile.Left(8).Equals("musicdb:"); }
bool URIUtils::IsUPnP(const CStdString& strFile) { return strFile.Left(5).Equals("upnp:"); }
bool URIUtils::IsLastFM(const CStdString& strFile) { return strFile.Left(7).Equals("lastfm:"); }
bool URIUtils::IsTuxBox(const CStdString& strFile) { return strFile.Left(7).Equals("tuxbox:"); }
bool URIUtils::IsAndroidApp(const CStdString &path) { return path.Left(11).Equals("androidapp:"); }
bool URIUtils::IsMythTV(const CStdString& strFile) { return strFile.Left(5).Equals("myth:"); }
void CURL::Parse(const CStdString& strURL1) { Reset(); // start by validating the path CStdString strURL = CUtil::ValidatePath(strURL1); // strURL can be one of the following: // format 1: protocol://[username:password]@hostname[:port]/directoryandfile // format 2: protocol://file // format 3: drive:directoryandfile // // first need 2 check if this is a protocol or just a normal drive & path if (!strURL.size()) return ; if (strURL.Equals("?", true)) return; if (strURL.size() > 1 && strURL[1] == ':') { // form is drive:directoryandfile // set filename and update extension SetFileName(strURL); return ; } // form is format 1 or 2 // format 1: protocol://[domain;][username:password]@hostname[:port]/directoryandfile // format 2: protocol://file // decode protocol int iPos = strURL.Find("://"); if (iPos < 0) { // This is an ugly hack that needs some work. // example: filename /foo/bar.zip/alice.rar/bob.avi // This should turn into zip://rar:///foo/bar.zip/alice.rar/bob.avi iPos = 0; while (1) { iPos = strURL.Find(".zip/", iPos); int extLen = 3; if (iPos < 0) { #ifndef _LINUX // check for misconstructed protocols iPos = strURL.Find(":"); if (iPos == strURL.GetLength() - 1) { SetProtocol(strURL.Left(iPos)); iPos += 1; break; } else #endif { /* set filename and update extension*/ SetFileName(strURL); return ; } } iPos += extLen + 1; CStdString archiveName = strURL.Left(iPos); struct __stat64 s; if (XFILE::CFile::Stat(archiveName, &s) == 0) { #ifdef _LINUX if (!S_ISDIR(s.st_mode)) #else if (!(s.st_mode & S_IFDIR)) #endif { CUtil::URLEncode(archiveName); CURL c((CStdString)"zip" + "://" + archiveName + '/' + strURL.Right(strURL.size() - iPos - 1)); *this = c; return; } } } } else { SetProtocol(strURL.Left(iPos)); iPos += 3; } // virtual protocols // why not handle all format 2 (protocol://file) style urls here? // ones that come to mind are iso9660, cdda, musicdb, etc. // they are all local protocols and have no server part, port number, special options, etc. // this removes the need for special handling below. if ( m_strProtocol.Equals("stack") || m_strProtocol.Equals("virtualpath") || m_strProtocol.Equals("multipath") || m_strProtocol.Equals("filereader") || m_strProtocol.Equals("special") ) { SetFileName(strURL.Mid(iPos)); return; } // check for username/password - should occur before first / if (iPos == -1) iPos = 0; // for protocols supporting options, chop that part off here // maybe we should invert this list instead? int iEnd = strURL.length(); const char* sep = NULL; CStdString strProtocol2 = GetTranslatedProtocol(); if(m_strProtocol.Equals("rss") || m_strProtocol.Equals("addons")) sep = "?"; else if(strProtocol2.Equals("http") || strProtocol2.Equals("https") || strProtocol2.Equals("plugin") || strProtocol2.Equals("hdhomerun") || strProtocol2.Equals("rtsp") || strProtocol2.Equals("zip")) sep = "?;#|"; else if(strProtocol2.Equals("ftp") || strProtocol2.Equals("ftps")) sep = "?;"; if(sep) { int iOptions = strURL.find_first_of(sep, iPos); if (iOptions >= 0 ) { // we keep the initial char as it can be any of the above int iProto = strURL.find_first_of("|",iOptions); if (iProto >= 0) { m_strProtocolOptions = strURL.substr(iProto+1); m_strOptions = strURL.substr(iOptions,iProto-iOptions); } else m_strOptions = strURL.substr(iOptions); iEnd = iOptions; } } int iSlash = strURL.Find("/", iPos); if(iSlash >= iEnd) iSlash = -1; // was an invalid slash as it was contained in options if( !m_strProtocol.Equals("iso9660") ) { int iAlphaSign = strURL.Find("@", iPos); if (iAlphaSign >= 0 && iAlphaSign < iEnd && (iAlphaSign < iSlash || iSlash < 0)) { // username/password found CStdString strUserNamePassword = strURL.Mid(iPos, iAlphaSign - iPos); // first extract domain, if protocol is smb if (m_strProtocol.Equals("smb")) { int iSemiColon = strUserNamePassword.Find(";"); if (iSemiColon >= 0) { m_strDomain = strUserNamePassword.Left(iSemiColon); strUserNamePassword.Delete(0, iSemiColon + 1); } } // username:password int iColon = strUserNamePassword.Find(":"); if (iColon >= 0) { m_strUserName = strUserNamePassword.Left(iColon); iColon++; m_strPassword = strUserNamePassword.Right(strUserNamePassword.size() - iColon); } // username else { m_strUserName = strUserNamePassword; } iPos = iAlphaSign + 1; iSlash = strURL.Find("/", iAlphaSign); if(iSlash >= iEnd) iSlash = -1; } } // detect hostname:port/ if (iSlash < 0) { CStdString strHostNameAndPort = strURL.Mid(iPos, iEnd - iPos); int iColon = strHostNameAndPort.Find(":"); if (iColon >= 0) { m_strHostName = strHostNameAndPort.Left(iColon); iColon++; CStdString strPort = strHostNameAndPort.Right(strHostNameAndPort.size() - iColon); m_iPort = atoi(strPort.c_str()); } else { m_strHostName = strHostNameAndPort; } } else { CStdString strHostNameAndPort = strURL.Mid(iPos, iSlash - iPos); int iColon = strHostNameAndPort.Find(":"); if (iColon >= 0) { m_strHostName = strHostNameAndPort.Left(iColon); iColon++; CStdString strPort = strHostNameAndPort.Right(strHostNameAndPort.size() - iColon); m_iPort = atoi(strPort.c_str()); } else { m_strHostName = strHostNameAndPort; } iPos = iSlash + 1; if (iEnd > iPos) { m_strFileName = strURL.Mid(iPos, iEnd - iPos); iSlash = m_strFileName.Find("/"); if(iSlash < 0) m_strShareName = m_strFileName; else m_strShareName = m_strFileName.Left(iSlash); } } // iso9960 doesnt have an hostname;-) if (m_strProtocol.CompareNoCase("iso9660") == 0 || m_strProtocol.CompareNoCase("musicdb") == 0 || m_strProtocol.CompareNoCase("videodb") == 0 || m_strProtocol.CompareNoCase("lastfm") == 0 || m_strProtocol.Left(3).CompareNoCase("mem") == 0) { if (m_strHostName != "" && m_strFileName != "") { CStdString strFileName = m_strFileName; m_strFileName.Format("%s/%s", m_strHostName.c_str(), strFileName.c_str()); m_strHostName = ""; } else { if (!m_strHostName.IsEmpty() && strURL[iEnd-1]=='/') m_strFileName = m_strHostName + "/"; else m_strFileName = m_strHostName; m_strHostName = ""; } } m_strFileName.Replace("\\", "/"); /* update extension */ SetFileName(m_strFileName); /* decode urlencoding on this stuff */ if( m_strProtocol.Equals("rar") || m_strProtocol.Equals("zip") || m_strProtocol.Equals("musicsearch")) { CUtil::URLDecode(m_strHostName); // Validate it as it is likely to contain a filename SetHostName(CUtil::ValidatePath(m_strHostName)); } CUtil::URLDecode(m_strUserName); CUtil::URLDecode(m_strPassword); }
bool URIUtils::IsHDHomeRun(const CStdString& strFile) { return strFile.Left(10).Equals("hdhomerun:"); }
bool URIUtils::GetParentPath(const CStdString& strPath, CStdString& strParent) { strParent = ""; CURL url(strPath); CStdString strFile = url.GetFileName(); if ( URIUtils::ProtocolHasParentInHostname(url.GetProtocol()) && strFile.IsEmpty()) { strFile = url.GetHostName(); return GetParentPath(strFile, strParent); } else if (url.GetProtocol() == "stack") { CStackDirectory dir; CFileItemList items; dir.GetDirectory(strPath,items); GetDirectory(items[0]->GetPath(),items[0]->m_strDVDLabel); if (items[0]->m_strDVDLabel.Mid(0,6).Equals("rar://") || items[0]->m_strDVDLabel.Mid(0,6).Equals("zip://")) GetParentPath(items[0]->m_strDVDLabel, strParent); else strParent = items[0]->m_strDVDLabel; for( int i=1;i<items.Size();++i) { GetDirectory(items[i]->GetPath(),items[i]->m_strDVDLabel); if (items[0]->m_strDVDLabel.Mid(0,6).Equals("rar://") || items[0]->m_strDVDLabel.Mid(0,6).Equals("zip://")) items[i]->SetPath(GetParentPath(items[i]->m_strDVDLabel)); else items[i]->SetPath(items[i]->m_strDVDLabel); GetCommonPath(strParent,items[i]->GetPath()); } return true; } else if (url.GetProtocol() == "multipath") { // get the parent path of the first item return GetParentPath(CMultiPathDirectory::GetFirstPath(strPath), strParent); } else if (url.GetProtocol() == "plugin") { if (!url.GetOptions().IsEmpty()) { url.SetOptions(""); strParent = url.Get(); return true; } if (!url.GetFileName().IsEmpty()) { url.SetFileName(""); strParent = url.Get(); return true; } if (!url.GetHostName().IsEmpty()) { url.SetHostName(""); strParent = url.Get(); return true; } return true; // already at root } else if (url.GetProtocol() == "special") { if (HasSlashAtEnd(strFile) ) strFile = strFile.Left(strFile.size() - 1); if(strFile.ReverseFind('/') < 0) return false; } else if (strFile.size() == 0) { if (url.GetHostName().size() > 0) { // we have an share with only server or workgroup name // set hostname to "" and return true to get back to root url.SetHostName(""); strParent = url.Get(); return true; } return false; } if (HasSlashAtEnd(strFile) ) { strFile = strFile.Left(strFile.size() - 1); } int iPos = strFile.ReverseFind('/'); #ifndef _LINUX if (iPos < 0) { iPos = strFile.ReverseFind('\\'); } #endif if (iPos < 0) { url.SetFileName(""); strParent = url.Get(); return true; } strFile = strFile.Left(iPos); AddSlashAtEnd(strFile); url.SetFileName(strFile); strParent = url.Get(); return true; }
bool URIUtils::IsSlingbox(const CStdString& strFile) { return strFile.Left(6).Equals("sling:"); }
bool URIUtils::IsStack(const CStdString& strFile) { return strFile.Left(6).Equals("stack:"); }
bool URIUtils::IsVTP(const CStdString& strFile) { return strFile.Left(4).Equals("vtp:"); }
bool URIUtils::IsISO9660(const CStdString& strFile) { return strFile.Left(8).Equals("iso9660:"); }
bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button) { CFileItemPtr item; if (itemNumber >= 0 && itemNumber < m_vecItems->Size()) item = m_vecItems->Get(itemNumber); switch (button) { case CONTEXT_BUTTON_SET_DEFAULT: g_settings.m_defaultVideoLibSource = GetQuickpathName(item->m_strPath); g_settings.Save(); return true; case CONTEXT_BUTTON_CLEAR_DEFAULT: g_settings.m_defaultVideoLibSource.Empty(); g_settings.Save(); return true; case CONTEXT_BUTTON_EDIT: UpdateVideoTitle(item.get()); CUtil::DeleteVideoDatabaseDirectoryCache(); Update(m_vecItems->m_strPath); return true; case CONTEXT_BUTTON_SET_SEASON_THUMB: case CONTEXT_BUTTON_SET_ACTOR_THUMB: case CONTEXT_BUTTON_SET_ARTIST_THUMB: case CONTEXT_BUTTON_SET_MOVIESET_THUMB: { // Grab the thumbnails from the web CStdString strPath; CFileItemList items; CUtil::AddFileToFolder(g_advancedSettings.m_cachePath,"imdbthumbs",strPath); CUtil::WipeDir(strPath); XFILE::CDirectory::Create(strPath); CFileItemPtr noneitem(new CFileItem("thumb://None", false)); CStdString cachedThumb = m_vecItems->Get(itemNumber)->GetCachedSeasonThumb(); if (button == CONTEXT_BUTTON_SET_ACTOR_THUMB) cachedThumb = m_vecItems->Get(itemNumber)->GetCachedActorThumb(); if (button == CONTEXT_BUTTON_SET_ARTIST_THUMB) cachedThumb = m_vecItems->Get(itemNumber)->GetCachedArtistThumb(); if (button == CONTEXT_BUTTON_SET_MOVIESET_THUMB) cachedThumb = m_vecItems->Get(itemNumber)->GetCachedVideoThumb(); if (CFile::Exists(cachedThumb)) { CFileItemPtr item(new CFileItem("thumb://Current", false)); item->SetThumbnailImage(cachedThumb); item->SetLabel(g_localizeStrings.Get(20016)); items.Add(item); } noneitem->SetIconImage("DefaultFolder.png"); noneitem->SetLabel(g_localizeStrings.Get(20018)); vector<CStdString> thumbs; if (button != CONTEXT_BUTTON_SET_ARTIST_THUMB) { CVideoInfoTag tag; if (button == CONTEXT_BUTTON_SET_SEASON_THUMB) m_database.GetTvShowInfo("",tag,m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId); else tag = *m_vecItems->Get(itemNumber)->GetVideoInfoTag(); if (button == CONTEXT_BUTTON_SET_SEASON_THUMB) tag.m_strPictureURL.GetThumbURLs(thumbs, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iSeason); else tag.m_strPictureURL.GetThumbURLs(thumbs); for (unsigned int i = 0; i < thumbs.size(); i++) { CStdString strItemPath; strItemPath.Format("thumb://Remote%i",i); CFileItemPtr item(new CFileItem(strItemPath, false)); item->SetThumbnailImage(thumbs[i]); item->SetIconImage("DefaultPicture.png"); item->SetLabel(g_localizeStrings.Get(20015)); items.Add(item); // TODO: Do we need to clear the cached image? // CTextureCache::Get().ClearCachedImage(thumbs[i]); } } bool local=false; if (button == CONTEXT_BUTTON_SET_ARTIST_THUMB) { CStdString picturePath; CStdString strPath = m_vecItems->Get(itemNumber)->m_strPath; CUtil::RemoveSlashAtEnd(strPath); int nPos=strPath.ReverseFind("/"); if (nPos>-1) { // try to guess where the user should start // browsing for the artist thumb CMusicDatabase database; database.Open(); long idArtist=database.GetArtistByName(m_vecItems->Get(itemNumber)->GetLabel()); database.GetArtistPath(idArtist, picturePath); } CStdString strThumb; CUtil::AddFileToFolder(picturePath,"folder.jpg",strThumb); if (XFILE::CFile::Exists(strThumb)) { CFileItemPtr pItem(new CFileItem(strThumb,false)); pItem->SetLabel(g_localizeStrings.Get(20017)); pItem->SetThumbnailImage(strThumb); items.Add(pItem); local = true; } else noneitem->SetIconImage("DefaultArtist.png"); } if (button == CONTEXT_BUTTON_SET_ACTOR_THUMB) { CStdString picturePath; CStdString strThumb; CUtil::AddFileToFolder(picturePath,"folder.jpg",strThumb); if (XFILE::CFile::Exists(strThumb)) { CFileItemPtr pItem(new CFileItem(strThumb,false)); pItem->SetLabel(g_localizeStrings.Get(20017)); pItem->SetThumbnailImage(strThumb); items.Add(pItem); local = true; } else noneitem->SetIconImage("DefaultActor.png"); } if (button == CONTEXT_BUTTON_SET_MOVIESET_THUMB) noneitem->SetIconImage("DefaultVideo.png"); if (!local) items.Add(noneitem); VECSOURCES sources=g_settings.m_videoSources; g_mediaManager.GetLocalDrives(sources); CStdString result; if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20019), result)) { return false; // user cancelled } if (result == "thumb://Current") return false; // user chose the one they have // delete the thumbnail if that's what the user wants, else overwrite with the // new thumbnail CTextureCache::Get().ClearCachedImage(cachedThumb, true); if (result.Left(14) == "thumb://Remote") { int number = atoi(result.Mid(14)); CFile::Cache(thumbs[number], cachedThumb); } if (result == "thumb://None") CTextureCache::Get().ClearCachedImage(cachedThumb, true); else CFile::Cache(result,cachedThumb); CUtil::DeleteVideoDatabaseDirectoryCache(); CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS); g_windowManager.SendMessage(msg); Update(m_vecItems->m_strPath); return true; } case CONTEXT_BUTTON_UPDATE_LIBRARY: { OnScan(""); return true; } case CONTEXT_BUTTON_UNLINK_MOVIE: { OnLinkMovieToTvShow(itemNumber, true); Update(m_vecItems->m_strPath); return true; } case CONTEXT_BUTTON_LINK_MOVIE: { OnLinkMovieToTvShow(itemNumber, false); return true; } case CONTEXT_BUTTON_GO_TO_ARTIST: { CStdString strPath; CMusicDatabase database; database.Open(); strPath.Format("musicdb://2/%ld/",database.GetArtistByName(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_strArtist)); g_windowManager.ActivateWindow(WINDOW_MUSIC_NAV,strPath); return true; } case CONTEXT_BUTTON_GO_TO_ALBUM: { CStdString strPath; CMusicDatabase database; database.Open(); strPath.Format("musicdb://3/%ld/",database.GetAlbumByName(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_strAlbum)); g_windowManager.ActivateWindow(WINDOW_MUSIC_NAV,strPath); return true; } case CONTEXT_BUTTON_PLAY_OTHER: { CMusicDatabase database; database.Open(); CSong song; if (database.GetSongById(database.GetSongByArtistAndAlbumAndTitle(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_strArtist,m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_strAlbum, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_strTitle), song)) { g_application.getApplicationMessenger().PlayFile(song); } return true; } case CONTEXT_BUTTON_UNLINK_BOOKMARK: { m_database.Open(); m_database.DeleteBookMarkForEpisode(*m_vecItems->Get(itemNumber)->GetVideoInfoTag()); m_database.Close(); CUtil::DeleteVideoDatabaseDirectoryCache(); Update(m_vecItems->m_strPath); return true; } default: break; } return CGUIWindowVideoBase::OnContextButton(itemNumber, button); }