QVariant PodcastParser::Load(QIODevice* device, const QUrl& url) const {
  QXmlStreamReader reader(device);

  while (!reader.atEnd()) {
    switch (reader.readNext()) {
    case QXmlStreamReader::StartElement: {
      const QStringRef name = reader.name();
      if (name == "rss") {
        Podcast podcast;
        if (!ParseRss(&reader, &podcast)) {
          return QVariant();
        } else {
          podcast.set_url(url);
          return QVariant::fromValue(podcast);
        }
      } else if (name == "opml") {
        OpmlContainer container;
        if (!ParseOpml(&reader, &container)) {
          return QVariant();
        } else {
          container.url = url;
          return QVariant::fromValue(container);
        }
      }

      return QVariant();
    }

    default:
      break;
    }
  }

  return QVariant();
}
void PodcastParser::ParseOutline(QXmlStreamReader* reader, OpmlContainer* ret) const {
  while (!reader->atEnd()) {
    QXmlStreamReader::TokenType type = reader->readNext();
    switch (type) {
    case QXmlStreamReader::StartElement: {
      const QStringRef name = reader->name();
      if (name != "outline") {
        Utilities::ConsumeCurrentElement(reader);
        continue;
      }

      QXmlStreamAttributes attributes = reader->attributes();

      if (attributes.value("type").toString() == "rss") {
        // Parse the feed and add it to this container
        Podcast podcast;
        podcast.set_description(attributes.value("description").toString());
        podcast.set_title(attributes.value("text").toString());
        podcast.set_image_url_large(QUrl::fromEncoded(attributes.value("imageHref").toString().toAscii()));
        podcast.set_url(QUrl::fromEncoded(attributes.value("xmlUrl").toString().toAscii()));
        ret->feeds.append(podcast);

        // Consume any children and the EndElement.
        Utilities::ConsumeCurrentElement(reader);
      } else {
        // Create a new child container
        OpmlContainer child;

        // Take the name from the fullname attribute first if it exists.
        child.name = attributes.value("fullname").toString();
        if (child.name.isEmpty()) {
          child.name = attributes.value("text").toString();
        }

        // Parse its contents and add it to this container
        ParseOutline(reader, &child);
        ret->containers.append(child);
      }

      break;
    }

    case QXmlStreamReader::EndElement:
      return;

    default:
      break;
    }
  }
}
void ITunesSearchPage::SearchFinished(QNetworkReply* reply) {
  reply->deleteLater();
  emit Busy(false);

  model()->clear();

  // Was there a network error?
  if (reply->error() != QNetworkReply::NoError) {
    QMessageBox::warning(this, tr("Failed to fetch podcasts"),
                         reply->errorString());
    return;
  }

  QJson::Parser parser;
  QVariant data = parser.parse(reply);

  // Was it valid JSON?
  if (data.isNull()) {
    QMessageBox::warning(
        this, tr("Failed to fetch podcasts"),
        tr("There was a problem parsing the response from the iTunes Store"));
    return;
  }

  // Was there an error message in the JSON?
  if (data.toMap().contains("errorMessage")) {
    QMessageBox::warning(this, tr("Failed to fetch podcasts"),
                         data.toMap()["errorMessage"].toString());
    return;
  }

  for (const QVariant& result_variant : data.toMap()["results"].toList()) {
    QVariantMap result(result_variant.toMap());
    if (result["kind"].toString() != "podcast") {
      continue;
    }

    Podcast podcast;
    podcast.set_author(result["artistName"].toString());
    podcast.set_title(result["trackName"].toString());
    podcast.set_url(result["feedUrl"].toUrl());
    podcast.set_link(result["trackViewUrl"].toUrl());
    podcast.set_image_url_small(QUrl(result["artworkUrl30"].toString()));
    podcast.set_image_url_large(QUrl(result["artworkUrl100"].toString()));

    model()->appendRow(model()->CreatePodcastItem(podcast));
  }
}