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 }