From bb4f82c026a7a266c5ae7962e871b306e5a8b8a3 Mon Sep 17 00:00:00 2001 From: elupus Date: Sun, 29 Aug 2010 16:26:50 +0000 Subject: [PATCH] Backport: r33281 changed: parse media:group entries in rss (will select the first available item) r33282 changed: improve rss directory by always selecting the resource with highest bitrate or resolution r33283 changed: always select the longest rss title r33284 changed: if mimetype is set that is a more sure type than any video/audio/picture tags r33285 added: method to set a listitems mimetype from python. allows plugins to avoid the http HEAD request done to look this up on playback r33286 changed: make sure a rss resource selects content based in prio list video/audio/rss/image changed: if content is application/rss+xml and protocol is http:// replace protocol with rss:// since server may be silly and not return correct content type r33287 added: parse SVT Play rss extensions rss continuation links git-svn-id: https://xbmc.svn.sourceforge.net/svnroot/xbmc/branches/Dharma@33313 568bbfeb-2a22-0410-94d2-cc84cf5bfa90 --- xbmc/FileItem.cpp | 22 +- xbmc/FileSystem/RSSDirectory.cpp | 252 +++++++++++++++------ xbmc/lib/libPython/xbmcmodule/listitem.cpp | 40 ++++ 3 files changed, 231 insertions(+), 83 deletions(-) diff --git a/xbmc/FileItem.cpp b/xbmc/FileItem.cpp index 00b6af6a7e24d..57c3baed65ac6 100644 --- a/xbmc/FileItem.cpp +++ b/xbmc/FileItem.cpp @@ -456,14 +456,14 @@ bool CFileItem::Exists(bool bUseCache /* = true */) const bool CFileItem::IsVideo() const { - if (HasVideoInfoTag()) return true; - if (HasMusicInfoTag()) return false; - if (HasPictureInfoTag()) return false; - /* check preset mime type */ if( m_mimetype.Left(6).Equals("video/") ) return true; + if (HasVideoInfoTag()) return true; + if (HasMusicInfoTag()) return false; + if (HasPictureInfoTag()) return false; + if (IsHDHomeRun() || IsTuxBox() || CUtil::IsDVD(m_strPath)) return true; @@ -492,16 +492,16 @@ bool CFileItem::IsVideo() const bool CFileItem::IsAudio() const { + /* check preset mime type */ + if( m_mimetype.Left(6).Equals("audio/") ) + return true; + if (HasMusicInfoTag()) return true; if (HasVideoInfoTag()) return false; if (HasPictureInfoTag()) return false; if (IsCDDA()) return true; if (!m_bIsFolder && IsLastFM()) return true; - /* check preset mime type */ - if( m_mimetype.Left(6).Equals("audio/") ) - return true; - CStdString extension; if( m_mimetype.Left(12).Equals("application/") ) { /* check for some standard types */ @@ -534,13 +534,13 @@ bool CFileItem::IsKaraoke() const bool CFileItem::IsPicture() const { + if( m_mimetype.Left(6).Equals("image/") ) + return true; + if (HasPictureInfoTag()) return true; if (HasMusicInfoTag()) return false; if (HasVideoInfoTag()) return false; - if( m_mimetype.Left(6).Equals("image/") ) - return true; - return CUtil::IsPicture(m_strPath); } diff --git a/xbmc/FileSystem/RSSDirectory.cpp b/xbmc/FileSystem/RSSDirectory.cpp index b7978a4db65bc..5288c09cea5d0 100644 --- a/xbmc/FileSystem/RSSDirectory.cpp +++ b/xbmc/FileSystem/RSSDirectory.cpp @@ -35,6 +35,33 @@ using namespace XFILE; using namespace std; using namespace MUSIC_INFO; +namespace { + + struct SResource + { + SResource() + : width(0) + , height(0) + , bitrate(0) + , duration(0) + , size(0) + {} + + CStdString tag; + CStdString path; + CStdString mime; + CStdString lang; + int width; + int height; + int bitrate; + int duration; + int64_t size; + }; + + typedef std::vector SResources; + +} + CRSSDirectory::CRSSDirectory() { SetCacheDirectory(DIR_CACHE_ONCE); @@ -101,37 +128,36 @@ static time_t ParseDate(const CStdString & strDate) // Check the difference between the time of last check and time of the item return mktime(&pubDate); } -static void ParseItem(CFileItem* item, TiXmlElement* root); +static void ParseItem(CFileItem* item, SResources& resources, TiXmlElement* root); -static void ParseItemMRSS(CFileItem* item, TiXmlElement* item_child, const CStdString& name, const CStdString& xmlns) +static void ParseItemMRSS(CFileItem* item, SResources& resources, TiXmlElement* item_child, const CStdString& name, const CStdString& xmlns) { CVideoInfoTag* vtag = item->GetVideoInfoTag(); CStdString text = item_child->GetText(); if(name == "content") { - const char* url = item_child->Attribute("url"); - const char* type = item_child->Attribute("type"); - const char* dur = item_child->Attribute("duration"); - - if (url && item->m_strPath == "" && IsPathToMedia(url)) - item->m_strPath = url; - - if (type) - { - CStdString strType(type); - item->SetMimeType(strType); - if (url && item->m_strPath.IsEmpty() && - (strType.Left(6).Equals("video/") || - strType.Left(6).Equals("audio/") - )) - item->m_strPath = url; - } - - if(dur) - vtag->m_strRuntime = StringUtils::SecondsToTimeString(atoi(dur)); - - ParseItem(item, item_child); + SResource res; + res.tag = "media:content"; + res.mime = item_child->Attribute("type"); + res.path = item_child->Attribute("url"); + if(item_child->Attribute("width")) + res.width = atoi(item_child->Attribute("width")); + if(item_child->Attribute("height")) + res.height = atoi(item_child->Attribute("height")); + if(item_child->Attribute("bitrate")) + res.bitrate = atoi(item_child->Attribute("bitrate")); + if(item_child->Attribute("duration")) + res.duration = atoi(item_child->Attribute("duration")); + if(item_child->Attribute("fileSize")) + res.size = _atoi64(item_child->Attribute("fileSize")); + + resources.push_back(res); + ParseItem(item, resources, item_child); + } + else if(name == "group") + { + ParseItem(item, resources, item_child); } else if(name == "thumbnail") { @@ -149,7 +175,8 @@ static void ParseItemMRSS(CFileItem* item, TiXmlElement* item_child, const CStdS if(text.IsEmpty()) return; - vtag->m_strTitle = text; + if(text.length() > item->m_strTitle.length()) + item->m_strTitle = text; } else if(name == "description") { @@ -221,7 +248,7 @@ static void ParseItemMRSS(CFileItem* item, TiXmlElement* item_child, const CStdS } -static void ParseItemItunes(CFileItem* item, TiXmlElement* item_child, const CStdString& name, const CStdString& xmlns) +static void ParseItemItunes(CFileItem* item, SResources& resources, TiXmlElement* item_child, const CStdString& name, const CStdString& xmlns) { CVideoInfoTag* vtag = item->GetVideoInfoTag(); CStdString text = item_child->GetText(); @@ -246,11 +273,14 @@ static void ParseItemItunes(CFileItem* item, TiXmlElement* item_child, const CSt item->SetProperty("keywords", text); } -static void ParseItemRSS(CFileItem* item, TiXmlElement* item_child, const CStdString& name, const CStdString& xmlns) +static void ParseItemRSS(CFileItem* item, SResources& resources, TiXmlElement* item_child, const CStdString& name, const CStdString& xmlns) { CStdString text = item_child->GetText(); if (name == "title") - item->m_strTitle = text; + { + if(text.length() > item->m_strTitle.length()) + item->m_strTitle = text; + } else if (name == "pubDate") { CDateTime pubDate(ParseDate(text)); @@ -258,35 +288,27 @@ static void ParseItemRSS(CFileItem* item, TiXmlElement* item_child, const CStdSt } else if (name == "link") { - string strPrefix = text.substr(0, text.find_first_of(":")); - if (strPrefix == "rss") - { - // If this is an rss item, we treat it as another level in the directory - item->m_bIsFolder = true; - item->m_strPath = text; - } - else if (item->m_strPath == "" && IsPathToMedia(text)) - item->m_strPath = text; + SResource res; + res.tag = "rss:link"; + res.path = text; + resources.push_back(res); } else if(name == "enclosure") { - const char * url = item_child->Attribute("url"); - if (url && item->m_strPath.IsEmpty() && IsPathToMedia(url)) - item->m_strPath = url; - const char * content_type = item_child->Attribute("type"); - if (content_type) - { - item->SetMimeType(content_type); - CStdString strContentType(content_type); - if (url && item->m_strPath.IsEmpty() && - (strContentType.Left(6).Equals("video/") || - strContentType.Left(6).Equals("audio/") - )) - item->m_strPath = url; - } - const char * len = item_child->Attribute("length"); - if (len) - item->m_dwSize = _atoi64(len); + const char * url = item_child->Attribute("url"); + const char * type = item_child->Attribute("type"); + const char * len = item_child->Attribute("length"); + + SResource res; + res.tag = "rss:enclosure"; + if(url) + res.path = url; + if(type) + res.mime = type; + if(len) + res.size = _atoi64(len); + + resources.push_back(res); } else if(name == "description") { @@ -296,12 +318,17 @@ static void ParseItemRSS(CFileItem* item, TiXmlElement* item_child, const CStdSt } else if(name == "guid") { - if (item->m_strPath.IsEmpty() && IsPathToMedia(text)) - item->m_strPath = text; + if(IsPathToMedia(text)) + { + SResource res; + res.tag = "rss:guid"; + res.path = text; + resources.push_back(res); + } } } -static void ParseItemVoddler(CFileItem* item, TiXmlElement* element, const CStdString& name, const CStdString& xmlns) +static void ParseItemVoddler(CFileItem* item, SResources& resources, TiXmlElement* element, const CStdString& name, const CStdString& xmlns) { CVideoInfoTag* vtag = item->GetVideoInfoTag(); CStdString text = element->GetText(); @@ -310,12 +337,11 @@ static void ParseItemVoddler(CFileItem* item, TiXmlElement* element, const CStdS { vtag->m_strTrailer = text; - CStdString type = element->Attribute("type"); - if(item->m_strPath.IsEmpty()) - { - item->m_strPath = text; - item->SetMimeType(type); - } + SResource res; + res.tag = "voddler:trailer"; + res.mime = element->Attribute("type"); + res.path = text; + resources.push_back(res); } else if(name == "year") vtag->m_iYear = atoi(text); @@ -333,7 +359,7 @@ static void ParseItemVoddler(CFileItem* item, TiXmlElement* element, const CStdS } } -static void ParseItemBoxee(CFileItem* item, TiXmlElement* element, const CStdString& name, const CStdString& xmlns) +static void ParseItemBoxee(CFileItem* item, SResources& resources, TiXmlElement* element, const CStdString& name, const CStdString& xmlns) { CVideoInfoTag* vtag = item->GetVideoInfoTag(); CStdString text = element->GetText(); @@ -358,7 +384,7 @@ static void ParseItemBoxee(CFileItem* item, TiXmlElement* element, const CStdStr item->SetProperty("boxee:releasedate", text); } -static void ParseItemZink(CFileItem* item, TiXmlElement* element, const CStdString& name, const CStdString& xmlns) +static void ParseItemZink(CFileItem* item, SResources& resources, TiXmlElement* element, const CStdString& name, const CStdString& xmlns) { CVideoInfoTag* vtag = item->GetVideoInfoTag(); CStdString text = element->GetText(); @@ -378,7 +404,20 @@ static void ParseItemZink(CFileItem* item, TiXmlElement* element, const CStdStri vtag->m_strRuntime = text; } -static void ParseItem(CFileItem* item, TiXmlElement* root) +static void ParseItemSVT(CFileItem* item, SResources& resources, TiXmlElement* element, const CStdString& name, const CStdString& xmlns) +{ + CStdString text = element->GetText(); + if (name == "xmllink") + { + SResource res; + res.tag = "svtplay:xmllink"; + res.path = text; + res.mime = "application/rss+xml"; + resources.push_back(res); + } +} + +static void ParseItem(CFileItem* item, SResources& resources, TiXmlElement* root) { for (TiXmlElement* child = root->FirstChildElement(); child; child = child->NextSiblingElement()) { @@ -392,17 +431,86 @@ static void ParseItem(CFileItem* item, TiXmlElement* root) } if (xmlns == "media") - ParseItemMRSS (item, child, name, xmlns); + ParseItemMRSS (item, resources, child, name, xmlns); else if (xmlns == "itunes") - ParseItemItunes (item, child, name, xmlns); + ParseItemItunes (item, resources, child, name, xmlns); else if (xmlns == "voddler") - ParseItemVoddler(item, child, name, xmlns); + ParseItemVoddler(item, resources, child, name, xmlns); else if (xmlns == "boxee") - ParseItemBoxee (item, child, name, xmlns); + ParseItemBoxee (item, resources, child, name, xmlns); else if (xmlns == "zn") - ParseItemZink (item, child, name, xmlns); + ParseItemZink (item, resources, child, name, xmlns); + else if (xmlns == "svtplay") + ParseItemSVT (item, resources, child, name, xmlns); + else + ParseItemRSS (item, resources, child, name, xmlns); + } +} + +static bool FindMime(SResources resources, CStdString mime) +{ + for(SResources::iterator it = resources.begin(); it != resources.end(); it++) + { + if(it->mime.Left(mime.length()).Equals(mime)) + return true; + } + return false; +} + +static void ParseItem(CFileItem* item, TiXmlElement* root) +{ + SResources resources; + ParseItem(item, resources, root); + + const char* prio[] = { "media:content", "voddler:trailer", "rss:enclosure", "svtplay:xmllink", "rss:link", "rss:guid", NULL }; + + CStdString mime; + if (FindMime(resources, "video/")) + mime = "video/"; + else if(FindMime(resources, "audio/")) + mime = "audio/"; + else if(FindMime(resources, "application/rss")) + mime = "application/rss"; + else if(FindMime(resources, "image/")) + mime = "image/"; + + SResources::iterator best = resources.end(); + for(const char** type = prio; *type && best == resources.end(); type++) + { + for(SResources::iterator it = resources.begin(); it != resources.end(); it++) + { + if(it->mime.Left(mime.length()) != mime) + continue; + + if(it->tag == *type) + { + if(best == resources.end()) + best = it; + else if(it->width && it->height || best->width && best->height) + { + if(it->width*it->height > best->width*best->height) + best = it; + } + else if(it->bitrate > best->bitrate) + best = it; + } + } + } + + if(best != resources.end()) + { + item->SetMimeType(best->mime); + item->m_strPath = best->path; + item->m_dwSize = best->size; + + /* handling of mimetypes fo directories are sub optimal at best */ + if(best->mime == "application/rss+xml" && item->m_strPath.Left(7).Equals("http://")) + item->m_strPath.replace(0, 7, "rss://"); + + if(item->m_strPath.Left(6).Equals("rss://")) + item->m_bIsFolder = true; else - ParseItemRSS (item, child, name, xmlns); + item->m_bIsFolder = false; } if(!item->m_strTitle.IsEmpty()) diff --git a/xbmc/lib/libPython/xbmcmodule/listitem.cpp b/xbmc/lib/libPython/xbmcmodule/listitem.cpp index 7f2e76d2a08a0..137436e6d3dbb 100644 --- a/xbmc/lib/libPython/xbmcmodule/listitem.cpp +++ b/xbmc/lib/libPython/xbmcmodule/listitem.cpp @@ -824,6 +824,45 @@ namespace PYXBMC return Py_None; } + PyDoc_STRVAR(setMimeType__doc__, + "setPath(path) -- Sets the listitem's mimetype if known.\n" + "\n" + "path : string or unicode - mimetype.\n" + "\n" + "*If known prehand, this can avoid xbmc doing HEAD requests to http servers to figure out file type.\n" + "\n"); + + PyObject* ListItem_SetMimeType(ListItem *self, PyObject *args, PyObject *kwds) + { + if (!self->item) return NULL; + PyObject* pType = NULL; + + if (!PyArg_ParseTuple(args, (char*)"O", &pType)) return NULL; + static const char *keywords[] = { "mimetype", NULL }; + + if (!PyArg_ParseTupleAndKeywords( + args, + kwds, + (char*)"O", + (char**)keywords, + &pType + )) + { + return NULL; + } + + string type; + if (pType && !PyXBMCGetUnicodeString(type, pType, 1)) + return NULL; + // set path + PyXBMCGUILock(); + self->item->SetMimeType(type); + PyXBMCGUIUnlock(); + + Py_INCREF(Py_None); + return Py_None; + } + PyMethodDef ListItem_methods[] = { {(char*)"getLabel" , (PyCFunction)ListItem_GetLabel, METH_VARARGS, getLabel__doc__}, {(char*)"setLabel" , (PyCFunction)ListItem_SetLabel, METH_VARARGS, setLabel__doc__}, @@ -838,6 +877,7 @@ namespace PYXBMC {(char*)"getProperty", (PyCFunction)ListItem_GetProperty, METH_VARARGS|METH_KEYWORDS, getProperty__doc__}, {(char*)"addContextMenuItems", (PyCFunction)ListItem_AddContextMenuItems, METH_VARARGS|METH_KEYWORDS, addContextMenuItems__doc__}, {(char*)"setPath" , (PyCFunction)ListItem_SetPath, METH_VARARGS|METH_KEYWORDS, setPath__doc__}, + {(char*)"setMimeType" , (PyCFunction)ListItem_SetMimeType, METH_VARARGS|METH_KEYWORDS, setMimeType__doc__}, {NULL, NULL, 0, NULL} };