File indexing completed on 2024-04-21 15:38:07
0001 /** 0002 * Copyright (C) 2005-2006 by Koos Vriezen <koos.vriezen@gmail.com> 0003 * 0004 * This library is free software; you can redistribute it and/or 0005 * modify it under the terms of the GNU Library General Public 0006 * License version 2 as published by the Free Software Foundation. 0007 * 0008 * This library is distributed in the hope that it will be useful, 0009 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0011 * Library General Public License for more details. 0012 * 0013 * You should have received a copy of the GNU Library General Public License 0014 * along with this library; see the file COPYING.LIB. If not, write to 0015 * the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, 0016 * Boston, MA 02110-1301, USA. 0017 **/ 0018 0019 #include "config-kmplayer.h" 0020 #include <kdebug.h> 0021 #include "kmplayer_atom.h" 0022 #include "kmplayer_smil.h" 0023 0024 #include <QTextStream> 0025 0026 using namespace KMPlayer; 0027 0028 Node *ATOM::Feed::childFromTag (const QString & tag) { 0029 QByteArray ba = tag.toLatin1 (); 0030 const char *name = ba.constData (); 0031 if (!strcmp (name, "entry")) 0032 return new ATOM::Entry (m_doc); 0033 else if (!strcmp (name, "link")) 0034 return new ATOM::Link (m_doc); 0035 else if (!strcmp (name, "title")) 0036 return new DarkNode (m_doc, tag.toUtf8 (), id_node_title); 0037 return NULL; 0038 } 0039 0040 void ATOM::Feed::closed () { 0041 for (Node *c = firstChild (); c; c = c->nextSibling ()) 0042 if (c->id == id_node_title) { 0043 title = c->innerText ().simplified (); 0044 break; 0045 } 0046 Element::closed (); 0047 } 0048 0049 void *ATOM::Feed::role (RoleType msg, void *content) 0050 { 0051 if (RolePlaylist == msg) 0052 return !title.isEmpty () ? (PlaylistRole *) this : NULL; 0053 return Element::role (msg, content); 0054 } 0055 0056 Node *ATOM::Entry::childFromTag (const QString &tag) { 0057 QByteArray ba = tag.toLatin1 (); 0058 const char *cstr = ba.constData (); 0059 if (!strcmp (cstr, "link")) 0060 return new ATOM::Link (m_doc); 0061 else if (!strcmp (cstr, "content")) 0062 return new ATOM::Content (m_doc); 0063 else if (!strcmp (cstr, "title")) 0064 return new DarkNode (m_doc, tag.toUtf8 (), id_node_title); 0065 else if (!strcmp (cstr, "summary")) 0066 return new DarkNode (m_doc, tag.toUtf8 (), id_node_summary); 0067 else if (!strcmp (cstr, "media:group")) 0068 return new MediaGroup (m_doc); 0069 else if (!strcmp (cstr, "gd:rating")) 0070 return new DarkNode (m_doc, tag.toUtf8 (), id_node_gd_rating); 0071 else if (!strcmp (cstr, "category") || 0072 !strcmp (cstr, "author:") || 0073 !strcmp (cstr, "id") || 0074 !strcmp (cstr, "updated") || 0075 !strncmp (cstr, "yt:", 3) || 0076 !strncmp (cstr, "gd:", 3)) 0077 return new DarkNode (m_doc, tag.toUtf8 (), id_node_ignored); 0078 return NULL; 0079 } 0080 0081 void ATOM::Entry::closed () { 0082 MediaGroup *group = NULL; 0083 Node *rating = NULL; 0084 for (Node *c = firstChild (); c; c = c->nextSibling ()) 0085 if (c->id == id_node_title) { 0086 title = c->innerText ().simplified (); 0087 } else if (c->id == id_node_gd_rating) { 0088 rating = c; 0089 } else if (c->id == id_node_media_group) { 0090 group = static_cast <MediaGroup *> (c); 0091 } 0092 if (group) 0093 group->addSummary (this, rating, QString(), QString(), QString(), 0, 0); 0094 Element::closed (); 0095 } 0096 0097 void *ATOM::Entry::role (RoleType msg, void *content) 0098 { 0099 if (RolePlaylist == msg) 0100 return !title.isEmpty () ? (PlaylistRole *) this : NULL; 0101 return Element::role (msg, content); 0102 } 0103 0104 Node::PlayType ATOM::Link::playType () { 0105 return src.isEmpty () ? play_type_none : play_type_unknown; 0106 } 0107 0108 void ATOM::Link::closed () { 0109 QString href; 0110 QString rel; 0111 for (Attribute *a = attributes ().first (); a; a = a->nextSibling ()) { 0112 if (a->name () == Ids::attr_href) 0113 href = a->value (); 0114 else if (a->name () == Ids::attr_title) 0115 title = a->value (); 0116 else if (a->name () == "rel") 0117 rel = a->value (); 0118 } 0119 if (!href.isEmpty () && rel == QString::fromLatin1 ("enclosure")) 0120 src = href; 0121 else if (title.isEmpty ()) 0122 title = href; 0123 Mrl::closed (); 0124 } 0125 0126 void ATOM::Content::closed () { 0127 for (Attribute *a = attributes ().first (); a; a = a->nextSibling ()) { 0128 if (a->name () == Ids::attr_src) 0129 src = a->value (); 0130 else if (a->name () == Ids::attr_type) { 0131 QString v = a->value ().toLower (); 0132 if (v == QString::fromLatin1 ("text")) 0133 mimetype = QString::fromLatin1 ("text/plain"); 0134 else if (v == QString::fromLatin1 ("html")) 0135 mimetype = QString::fromLatin1 ("text/html"); 0136 else if (v == QString::fromLatin1 ("xhtml")) 0137 mimetype = QString::fromLatin1 ("application/xhtml+xml"); 0138 else 0139 mimetype = v; 0140 } 0141 } 0142 Mrl::closed (); 0143 } 0144 0145 Node::PlayType ATOM::Content::playType () { 0146 if (!hasChildNodes () && !src.isEmpty ()) 0147 return play_type_unknown; 0148 return play_type_none; 0149 } 0150 0151 Node *ATOM::MediaGroup::childFromTag (const QString &tag) { 0152 QByteArray ba = tag.toLatin1 (); 0153 const char *cstr = ba.constData (); 0154 if (!strcmp (cstr, "media:content")) 0155 return new ATOM::MediaContent (m_doc); 0156 else if (!strcmp (cstr, "media:title")) 0157 return new DarkNode (m_doc, tag.toUtf8 (), id_node_media_title); 0158 else if (!strcmp (cstr, "media:description")) 0159 return new DarkNode (m_doc, tag.toUtf8 (), id_node_media_description); 0160 else if (!strcmp (cstr, "media:thumbnail")) 0161 return new DarkNode (m_doc, tag.toUtf8 (), id_node_media_thumbnail); 0162 else if (!strcmp (cstr, "media:player")) 0163 return new DarkNode (m_doc, tag.toUtf8 (), id_node_media_player); 0164 else if (!strcmp (cstr, "media:category") || 0165 !strcmp (cstr, "media:keywords") || 0166 !strcmp (cstr, "media:credit")) 0167 return new DarkNode (m_doc, tag.toUtf8 (), id_node_ignored); 0168 else if (!strcmp (cstr, "smil")) 0169 return new SMIL::Smil (m_doc); 0170 return NULL; 0171 } 0172 0173 void ATOM::MediaGroup::message (MessageType msg, void *content) { 0174 if (MsgChildFinished == msg && 0175 id_node_media_content == ((Posting *) content)->source->id) 0176 finish (); // only play one 0177 Element::message (msg, content); 0178 } 0179 0180 static QString makeStar (int x, bool fill) { 0181 QString path = "<path style=\"stroke:#A0A0A0;stroke-width:2px;stroke-opacity:1;"; 0182 if (fill) 0183 path += "fill:#ff0000"; 0184 else 0185 path += "fill:#C0C0C0"; 0186 path += "\" d=\"M 21.428572,23.571429 " 0187 "L 10.84984,18.213257 L 0.43866021,23.890134 L 2.2655767,12.173396 " 0188 "L -6.3506861,4.0260275 L 5.3571425,2.142857 L 10.443179,-8.5693712 " 0189 "L 15.852098,1.9835038 L 27.611704,3.5103513 L 19.246772,11.915557 " 0190 "L 21.428572,23.571429 z\"" 0191 " transform=\"translate("; 0192 path += QString::number (x); 0193 path += ",11)\"/>"; 0194 return path; 0195 } 0196 0197 static QString makeImage(const QString& url, int width, int height) { 0198 QString str = QString ("<img region=\"image\" src=\"") + url + QChar ('"'); 0199 if (width && height) { 0200 str += QString(" width=\"%1\" height=\"%2\"").arg(width).arg(height); 0201 } 0202 str += QString (" dur=\"20\" transIn=\"fade\" fill=\"transition\" fit=\"meet\"/>"); 0203 return str; 0204 } 0205 0206 //http://code.google.com/apis/youtube/2.0/developers_guide_protocol.html 0207 void ATOM::MediaGroup::addSummary(Node *p, Node *rating_node, 0208 const QString& alt_title, const QString& alt_desc, const QString& alt_img, int width, int height) { 0209 QString images; 0210 QString desc; 0211 QString title; 0212 QString player; 0213 QString ratings; 0214 int img_count = 0; 0215 if (rating_node) { 0216 Element *e = static_cast <Element *> (rating_node); 0217 QString nr = e->getAttribute ("average"); 0218 if (!nr.isEmpty ()) { 0219 int rating = ((int) nr.toDouble ()) % 6; 0220 ratings = "<img region=\"rating\">" 0221 "<svg width=\"200\" height=\"40\">"; 0222 for (int i = 0; i < 5; ++i) 0223 ratings += makeStar (10 + i * 40, rating > i); 0224 ratings += "</svg></img>"; 0225 } 0226 } 0227 for (Node *c = firstChild (); c; c = c->nextSibling ()) { 0228 switch (c->id) { 0229 case id_node_media_title: 0230 title = c->innerText (); 0231 break; 0232 case id_node_media_description: 0233 desc = c->innerText (); 0234 break; 0235 case id_node_media_player: 0236 player = static_cast <Element *> (c)->getAttribute (Ids::attr_url); 0237 break; 0238 case id_node_media_thumbnail: 0239 { 0240 Element *e = static_cast <Element *> (c); 0241 QString url = e->getAttribute (Ids::attr_url); 0242 if (!url.isEmpty ()) { 0243 images += makeImage(url, e->getAttribute (Ids::attr_width).toInt(), 0244 e->getAttribute (Ids::attr_height).toInt()); 0245 img_count++; 0246 } 0247 break; 0248 } 0249 } 0250 } 0251 if (title.isEmpty()) 0252 title = alt_title; 0253 if (desc.isEmpty()) 0254 desc = alt_desc; 0255 if (!img_count && !alt_img.isEmpty()) { 0256 images = makeImage(alt_img, width, height); 0257 ++img_count; 0258 } 0259 if (img_count) { 0260 QString buf; 0261 QTextStream out (&buf, QIODevice::WriteOnly); 0262 out << "<smil><head>"; 0263 if (!title.isEmpty ()) 0264 out << "<title>" << title << "</title>"; 0265 out << "<layout><root-layout width=\"400\" height=\"300\" background-color=\"#F5F5DC\"/>"; 0266 if (!title.isEmpty ()) 0267 out << "<region id=\"title\" left=\"20\" top=\"10\" height=\"18\" right=\"10\"/>"; 0268 out << "<region id=\"image\" left=\"5\" top=\"40\" width=\"130\" bottom=\"30\"/>"; 0269 if (!ratings.isEmpty ()) 0270 out << "<region id=\"rating\" left=\"15\" width=\"100\" height=\"20\" bottom=\"5\"/>"; 0271 out << "<region id=\"text\" left=\"140\" top=\"40\" bottom=\"10\" right=\"10\" fit=\"scroll\"/>" 0272 "</layout>" 0273 "<transition id=\"fade\" dur=\"0.3\" type=\"fade\"/>" 0274 "</head><body>" 0275 "<par><seq repeatCount=\"indefinite\">"; 0276 out << images; 0277 out << "</seq>"; 0278 if (!title.isEmpty ()) { 0279 if (!player.isEmpty ()) 0280 out << "<a href=\"" << XMLStringlet(player) << "\" target=\"top\">"; 0281 out << "<smilText region=\"title\" textFontWeight=\"bold\" textFontSize=\"11\""; 0282 if (!player.isEmpty ()) 0283 out << " textColor=\"blue\""; 0284 out << ">" << XMLStringlet (title) << "</smilText>"; 0285 if (!player.isEmpty ()) 0286 out << "</a>"; 0287 } 0288 if (!ratings.isEmpty ()) 0289 out << ratings; 0290 out << "<smilText region=\"text\" textFontFamily=\"serif\" textFontSize=\"11\">"; 0291 out << XMLStringlet (desc); 0292 out << QString ("</smilText></par></body></smil>"); 0293 QTextStream inxml (&buf, QIODevice::ReadOnly); 0294 KMPlayer::readXML (this, inxml, QString (), false); 0295 NodePtr n = lastChild(); 0296 n->normalize (); 0297 n->auxiliary_node = true; 0298 removeChild (n); 0299 p->insertBefore (n, p->firstChild ()); 0300 } 0301 } 0302 0303 void ATOM::MediaContent::closed () { 0304 unsigned fsize = 0; 0305 unsigned bitrate = 0; 0306 TrieString fs ("fileSize"); 0307 TrieString rate ("bitrate"); 0308 for (Attribute *a = attributes ().first (); a; a = a->nextSibling ()) { 0309 if (a->name () == Ids::attr_url) 0310 src = a->value(); 0311 else if (a->name () == Ids::attr_type) 0312 mimetype = a->value (); 0313 else if (a->name () == Ids::attr_height) 0314 size.height = a->value ().toInt (); 0315 else if (a->name () == Ids::attr_width) 0316 size.width = a->value ().toInt (); 0317 else if (a->name () == Ids::attr_width) 0318 size.width = a->value ().toInt (); 0319 else if (a->name () == fs) 0320 fsize = a->value ().toInt (); 0321 else if (a->name () == rate) 0322 bitrate = a->value ().toInt (); 0323 } 0324 if (!mimetype.isEmpty ()) { 0325 title = mimetype; 0326 if (fsize > 0) { 0327 if (fsize > 1024 * 1024) 0328 title += QString (" (%1 Mb)").arg (fsize / (1024 * 1024)); 0329 else 0330 title += QString (" (%1 kb)").arg (fsize / 1024); 0331 } else if (bitrate > 0) { 0332 if (bitrate > 10 * 1024) 0333 title += QString(" (%1 Mbit/s)").arg(bitrate / 1024); 0334 else 0335 title += QString(" (%1 kbit/s)").arg(bitrate); 0336 } 0337 } 0338 Mrl::closed (); 0339 }