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