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 PodcastService::CopyToDevice(const QModelIndexList& episode_indexes, const QModelIndexList& podcast_indexes) { PodcastEpisode episode_tmp; SongList songs; PodcastEpisodeList episodes; Podcast podcast; for (const QModelIndex& index : episode_indexes) { episode_tmp = index.data(Role_Episode).value<PodcastEpisode>(); if (episode_tmp.downloaded()) episodes << episode_tmp; } for (const QModelIndex& podcast : podcast_indexes) { for (int i = 0; i < podcast.model()->rowCount(podcast); ++i) { const QModelIndex& index = podcast.child(i, 0); episode_tmp = index.data(Role_Episode).value<PodcastEpisode>(); if (episode_tmp.downloaded() && !episode_tmp.listened()) episodes << episode_tmp; } } for (const PodcastEpisode& episode : episodes) { podcast = backend_->GetSubscriptionById(episode.podcast_database_id()); songs.append(episode.ToSong(podcast)); } organise_dialog_->SetDestinationModel( app_->device_manager()->connected_devices_model(), true); organise_dialog_->SetCopy(true); if (organise_dialog_->SetSongs(songs)) organise_dialog_->show(); }
void PodcastBackend::Unsubscribe(const Podcast& podcast) { // If this podcast is not already in the database, do nothing if (!podcast.is_valid()) { return; } QMutexLocker l(db_->Mutex()); QSqlDatabase db(db_->Connect()); ScopedTransaction t(&db); // Remove the podcast. QSqlQuery q("DELETE FROM podcasts WHERE ROWID = :id", db); q.bindValue(":id", podcast.database_id()); q.exec(); if (db_->CheckErrors(q)) return; // Remove all episodes in the podcast q = QSqlQuery("DELETE FROM podcast_episodes WHERE podcast_id = :id", db); q.bindValue(":id", podcast.database_id()); q.exec(); if (db_->CheckErrors(q)) return; t.Commit(); emit SubscriptionRemoved(podcast); }
Song PodcastEpisode::ToSong(const Podcast& podcast) const { Song ret; ret.set_valid(true); ret.set_title(title().simplified()); ret.set_artist(author().simplified()); ret.set_length_nanosec(kNsecPerSec * duration_secs()); ret.set_year(publication_date().date().year()); ret.set_comment(description()); if (downloaded() && QFile::exists(local_url().toLocalFile())) { ret.set_url(local_url()); } else { ret.set_url(url()); } ret.set_basefilename(QFileInfo(ret.url().path()).fileName()); // Use information from the podcast if it's set if (podcast.is_valid()) { ret.set_album(podcast.title().simplified()); ret.set_art_automatic(podcast.ImageUrlLarge().toString()); } return ret; }
void PodcastInfoWidget::SetPodcast(const Podcast& podcast) { if (image_id_) { app_->album_cover_loader()->CancelTask(image_id_); image_id_ = 0; } podcast_ = podcast; if (podcast.ImageUrlLarge().isValid()) { // Start loading an image for this item. image_id_ = app_->album_cover_loader()->LoadImageAsync( cover_options_, podcast.ImageUrlLarge().toString(), QString()); } ui_->image->hide(); SetText(podcast.title(), ui_->title); SetText(podcast.description(), ui_->description); SetText(podcast.copyright(), ui_->copyright, ui_->copyright_label); SetText(podcast.author(), ui_->author, ui_->author_label); SetText(podcast.owner_name(), ui_->owner, ui_->owner_label); SetText(podcast.link().toString(), ui_->website, ui_->website_label); SetText(podcast.extra("gpodder:subscribers").toString(), ui_->subscribers, ui_->subscribers_label); if (!image_id_) { emit LoadingFinished(); } }
void GPodderSearchPage::SearchFinished(mygpo::PodcastListPtr list) { emit Busy(false); model()->clear(); for (mygpo::PodcastPtr gpo_podcast : list->list()) { Podcast podcast; podcast.InitFromGpo(gpo_podcast.data()); model()->appendRow(model()->CreatePodcastItem(podcast)); } }
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 PodcastUpdater::PodcastLoaded(PodcastUrlLoaderReply* reply, const Podcast& podcast, bool one_of_many) { reply->deleteLater(); if (one_of_many) { if (--pending_replies_ == 0) { // This was the last reply we were waiting for. Save this time as being // the last sucessful update and restart the timer. last_full_update_ = QDateTime::currentDateTime(); SaveSettings(); RestartTimer(); } } if (!reply->is_success()) { qLog(Warning) << "Error fetching podcast at" << podcast.url() << ":" << reply->error_text(); return; } if (reply->result_type() != PodcastUrlLoaderReply::Type_Podcast) { qLog(Warning) << "The URL" << podcast.url() << "no longer contains a podcast"; return; } // Get the episode URLs we had for this podcast already. QSet<QUrl> existing_urls; foreach (const PodcastEpisode& episode, app_->podcast_backend()->GetEpisodes(podcast.database_id())) { existing_urls.insert(episode.url()); } // Add any new episodes PodcastEpisodeList new_episodes; foreach (const Podcast& reply_podcast, reply->podcast_results()) { foreach (const PodcastEpisode& episode, reply_podcast.episodes()) { if (!existing_urls.contains(episode.url())) { PodcastEpisode episode_copy(episode); episode_copy.set_podcast_database_id(podcast.database_id()); new_episodes.append(episode_copy); } } } app_->podcast_backend()->AddEpisodes(&new_episodes); qLog(Info) << "Added" << new_episodes.count() << "new episodes for" << podcast.url(); }
void PodcastUpdater::SubscriptionAdded(const Podcast& podcast) { // Only update a new podcast immediately if it doesn't have an episode list. // We assume that the episode list has already been fetched recently // otherwise. if (podcast.episodes().isEmpty()) { UpdatePodcastNow(podcast); } }
Podcast PodcastBackend::GetSubscriptionByUrl(const QUrl& url) { Podcast ret; QMutexLocker l(db_->Mutex()); QSqlDatabase db(db_->Connect()); QSqlQuery q("SELECT ROWID, " + Podcast::kColumnSpec + " FROM podcasts" " WHERE url = :url", db); q.bindValue(":url", url.toEncoded()); q.exec(); if (!db_->CheckErrors(q) && q.next()) { ret.InitFromQuery(q); } return ret; }
Podcast PodcastBackend::GetSubscriptionById(int id) { Podcast ret; QMutexLocker l(db_->Mutex()); QSqlDatabase db(db_->Connect()); QSqlQuery q("SELECT ROWID, " + Podcast::kColumnSpec + " FROM podcasts" " WHERE ROWID = :id", db); q.bindValue(":id", id); q.exec(); if (!db_->CheckErrors(q) && q.next()) { ret.InitFromQuery(q); } return ret; }
PodcastList PodcastBackend::GetAllSubscriptions() { PodcastList ret; QMutexLocker l(db_->Mutex()); QSqlDatabase db(db_->Connect()); QSqlQuery q("SELECT ROWID, " + Podcast::kColumnSpec + " FROM podcasts", db); q.exec(); if (db_->CheckErrors(q)) return ret; while (q.next()) { Podcast podcast; podcast.InitFromQuery(q); ret << podcast; } return ret; }
void PodcastService::CancelDownload(const QModelIndexList& episode_indexes, const QModelIndexList& podcast_indexes) { PodcastEpisode episode_tmp; SongList songs; PodcastEpisodeList episodes; Podcast podcast; for (const QModelIndex& index : episode_indexes) { episode_tmp = index.data(Role_Episode).value<PodcastEpisode>(); episodes << episode_tmp; } for (const QModelIndex& podcast : podcast_indexes) { for (int i = 0; i < podcast.model()->rowCount(podcast); ++i) { const QModelIndex& index = podcast.child(i, 0); episode_tmp = index.data(Role_Episode).value<PodcastEpisode>(); episodes << episode_tmp; } } episodes = app_->podcast_downloader()->EpisodesDownloading(episodes); app_->podcast_downloader()->cancelDownload(episodes); }
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)); } }
void PodcastUpdater::UpdatePodcastNow(const Podcast& podcast) { PodcastUrlLoaderReply* reply = loader_->Load(podcast.url()); NewClosure(reply, SIGNAL(Finished(bool)), this, SLOT(PodcastLoaded(PodcastUrlLoaderReply*,Podcast,bool)), reply, podcast, false); }