From 5a1a5a9d9530d837649f16edaf98ad22041720c0 Mon Sep 17 00:00:00 2001 From: Jim Broadus Date: Sat, 17 Nov 2018 05:29:16 -0800 Subject: [PATCH] Display podcast episode information. (#6203) * Display podcast episode information. Add an EpisodeInfoWidget with title, author, duration, date, and description fields. Include this in the PodcastInfoDialog. If exactly one episode is selected, then show both the podcast and episode widgets and display the episode's URL. Otherwise, hide the episode widget and follow the existing behavior. Note that the desription field for the EpisodeInfoWidget uses the QLabel and does not currently download embedded images. Add an always_show_hours option to the PrettyTime methods to include hours in formatting even if the field is zero. This is less ambiguious in some cases where duration is displayed. * Apply patch from automated formatter test. --- src/CMakeLists.txt | 3 + src/core/utilities.cpp | 8 +- src/core/utilities.h | 4 +- src/internet/podcasts/episodeinfowidget.cpp | 40 ++++++ src/internet/podcasts/episodeinfowidget.h | 46 +++++++ src/internet/podcasts/episodeinfowidget.ui | 137 ++++++++++++++++++++ src/internet/podcasts/podcastinfodialog.cpp | 18 ++- src/internet/podcasts/podcastinfodialog.h | 2 + src/internet/podcasts/podcastinfodialog.ui | 43 +++++- src/internet/podcasts/podcastservice.cpp | 26 +++- 10 files changed, 309 insertions(+), 18 deletions(-) create mode 100644 src/internet/podcasts/episodeinfowidget.cpp create mode 100644 src/internet/podcasts/episodeinfowidget.h create mode 100644 src/internet/podcasts/episodeinfowidget.ui diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 185ac995c..e4cc15cc8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -263,6 +263,7 @@ set(SOURCES internet/podcasts/addpodcastbyurl.cpp internet/podcasts/addpodcastdialog.cpp internet/podcasts/addpodcastpage.cpp + internet/podcasts/episodeinfowidget.cpp internet/podcasts/fixedopmlpage.cpp internet/podcasts/gpoddersearchpage.cpp internet/podcasts/gpoddersync.cpp @@ -560,6 +561,7 @@ set(HEADERS internet/podcasts/addpodcastbyurl.h internet/podcasts/addpodcastdialog.h internet/podcasts/addpodcastpage.h + internet/podcasts/episodeinfowidget.h internet/podcasts/fixedopmlpage.h internet/podcasts/gpoddersearchpage.h internet/podcasts/gpoddersync.h @@ -715,6 +717,7 @@ set(UI internet/podcasts/addpodcastbyurl.ui internet/podcasts/addpodcastdialog.ui + internet/podcasts/episodeinfowidget.ui internet/podcasts/gpoddersearchpage.ui internet/podcasts/itunessearchpage.ui internet/podcasts/podcastinfodialog.ui diff --git a/src/core/utilities.cpp b/src/core/utilities.cpp index 35e15c1aa..fc300a005 100644 --- a/src/core/utilities.cpp +++ b/src/core/utilities.cpp @@ -89,7 +89,7 @@ QString PrettyTimeDelta(int seconds) { return (seconds >= 0 ? "+" : "-") + PrettyTime(seconds); } -QString PrettyTime(int seconds) { +QString PrettyTime(int seconds, bool always_show_hours) { // last.fm sometimes gets the track length wrong, so you end up with // negative times. seconds = qAbs(seconds); @@ -99,7 +99,7 @@ QString PrettyTime(int seconds) { seconds %= 60; QString ret; - if (hours) + if (hours || always_show_hours) ret.sprintf("%d:%02d:%02d", hours, minutes, seconds); // NOLINT(runtime/printf) else @@ -108,8 +108,8 @@ QString PrettyTime(int seconds) { return ret; } -QString PrettyTimeNanosec(qint64 nanoseconds) { - return PrettyTime(nanoseconds / kNsecPerSec); +QString PrettyTimeNanosec(qint64 nanoseconds, bool always_show_hours) { + return PrettyTime(nanoseconds / kNsecPerSec, always_show_hours); } QString WordyTime(quint64 seconds) { diff --git a/src/core/utilities.h b/src/core/utilities.h index 6d1a8972d..7345a1140 100644 --- a/src/core/utilities.h +++ b/src/core/utilities.h @@ -39,9 +39,9 @@ class QXmlStreamReader; struct QMetaObject; namespace Utilities { -QString PrettyTime(int seconds); +QString PrettyTime(int seconds, bool always_show_hours = false); QString PrettyTimeDelta(int seconds); -QString PrettyTimeNanosec(qint64 nanoseconds); +QString PrettyTimeNanosec(qint64 nanoseconds, bool always_show_hours = false); QString PrettySize(quint64 bytes); QString PrettySize(const QSize& size); QString WordyTime(quint64 seconds); diff --git a/src/internet/podcasts/episodeinfowidget.cpp b/src/internet/podcasts/episodeinfowidget.cpp new file mode 100644 index 000000000..7cf8bde12 --- /dev/null +++ b/src/internet/podcasts/episodeinfowidget.cpp @@ -0,0 +1,40 @@ +/* This file is part of Clementine. + Copyright 2018, Jim Broadus + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#include "core/utilities.h" +#include "episodeinfowidget.h" +#include "ui_episodeinfowidget.h" + +#include + +EpisodeInfoWidget::EpisodeInfoWidget(QWidget* parent) + : QWidget(parent), ui_(new Ui_EpisodeInfoWidget), app_(nullptr) { + ui_->setupUi(this); +} + +EpisodeInfoWidget::~EpisodeInfoWidget() { delete ui_; } + +void EpisodeInfoWidget::SetApplication(Application* app) { app_ = app; } + +void EpisodeInfoWidget::SetEpisode(const PodcastEpisode& episode) { + episode_ = episode; + ui_->title->setText(episode.title()); + ui_->description->setText(episode.description()); + ui_->author->setText(episode.author()); + ui_->date->setText(episode.publication_date().toString("d MMMM yyyy")); + ui_->duration->setText(Utilities::PrettyTime(episode.duration_secs(), true)); +} diff --git a/src/internet/podcasts/episodeinfowidget.h b/src/internet/podcasts/episodeinfowidget.h new file mode 100644 index 000000000..b228ad4ec --- /dev/null +++ b/src/internet/podcasts/episodeinfowidget.h @@ -0,0 +1,46 @@ +/* This file is part of Clementine. + Copyright 2018, Jim Broadus + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#ifndef INTERNET_PODCASTS_EPISODEINFOWIDGET_H_ +#define INTERNET_PODCASTS_EPISODEINFOWIDGET_H_ + +#include "podcastepisode.h" + +#include + +class Application; +class Ui_EpisodeInfoWidget; + +class EpisodeInfoWidget : public QWidget { + Q_OBJECT + + public: + explicit EpisodeInfoWidget(QWidget* parent = nullptr); + ~EpisodeInfoWidget(); + + void SetApplication(Application* app); + + void SetEpisode(const PodcastEpisode& episode); + + private: + Ui_EpisodeInfoWidget* ui_; + + Application* app_; + PodcastEpisode episode_; +}; + +#endif // INTERNET_PODCASTS_EPISODEINFOWIDGET_H_ diff --git a/src/internet/podcasts/episodeinfowidget.ui b/src/internet/podcasts/episodeinfowidget.ui new file mode 100644 index 000000000..e4945a45a --- /dev/null +++ b/src/internet/podcasts/episodeinfowidget.ui @@ -0,0 +1,137 @@ + + + EpisodeInfoWidget + + + + 0 + 0 + 398 + 551 + + + + Form + + + #title { + font-weight: bold; +} + +#description { + font-size: smaller; +} + +QLineEdit { + background: transparent; +} + + + + QLayout::SetMinAndMaxSize + + + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + QLayout::SetMinAndMaxSize + + + + + Author + + + true + + + + + + + false + + + true + + + + + + + false + + + true + + + + + + + Date + + + true + + + + + + + Duration + + + true + + + + + + + false + + + true + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + diff --git a/src/internet/podcasts/podcastinfodialog.cpp b/src/internet/podcasts/podcastinfodialog.cpp index 33db8cde9..ae398a6b6 100644 --- a/src/internet/podcasts/podcastinfodialog.cpp +++ b/src/internet/podcasts/podcastinfodialog.cpp @@ -24,7 +24,8 @@ PodcastInfoDialog::PodcastInfoDialog(Application* app, QWidget* parent) app_(app), ui_(new Ui_PodcastInfoDialog) { ui_->setupUi(this); - ui_->details->SetApplication(app); + ui_->podcast_details->SetApplication(app); + ui_->episode_details->SetApplication(app); } PodcastInfoDialog::~PodcastInfoDialog() { @@ -32,8 +33,19 @@ PodcastInfoDialog::~PodcastInfoDialog() { } void PodcastInfoDialog::ShowPodcast(const Podcast& podcast) { - show(); + ui_->episode_info_scroll_area->hide(); ui_->podcast_url->setText(podcast.url().toString()); ui_->podcast_url->setReadOnly(true); - ui_->details->SetPodcast(podcast); + ui_->podcast_details->SetPodcast(podcast); + show(); +} + +void PodcastInfoDialog::ShowEpisode(const PodcastEpisode& episode, + const Podcast& podcast) { + ui_->episode_info_scroll_area->show(); + ui_->podcast_url->setText(episode.url().toString()); + ui_->podcast_url->setReadOnly(true); + ui_->podcast_details->SetPodcast(podcast); + ui_->episode_details->SetEpisode(episode); + show(); } diff --git a/src/internet/podcasts/podcastinfodialog.h b/src/internet/podcasts/podcastinfodialog.h index 3643d8007..93540175a 100644 --- a/src/internet/podcasts/podcastinfodialog.h +++ b/src/internet/podcasts/podcastinfodialog.h @@ -22,6 +22,7 @@ class Application; class Podcast; +class PodcastEpisode; class Ui_PodcastInfoDialog; class PodcastInfoDialog : public QDialog { @@ -32,6 +33,7 @@ class PodcastInfoDialog : public QDialog { ~PodcastInfoDialog(); void ShowPodcast(const Podcast& podcast); + void ShowEpisode(const PodcastEpisode& episode, const Podcast& podcast); private: Application* app_; diff --git a/src/internet/podcasts/podcastinfodialog.ui b/src/internet/podcasts/podcastinfodialog.ui index e0cbb0970..573dddff6 100644 --- a/src/internet/podcasts/podcastinfodialog.ui +++ b/src/internet/podcasts/podcastinfodialog.ui @@ -7,7 +7,7 @@ 0 0 493 - 395 + 415 @@ -18,7 +18,36 @@ - + + + true + + + + 250 + 100 + + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 473 + 163 + + + + + + + 250 @@ -37,13 +66,13 @@ true - + 0 0 473 - 313 + 162 @@ -68,6 +97,12 @@
internet/podcasts/podcastinfowidget.h
1 + + EpisodeInfoWidget + QWidget +
internet/podcasts/episodeinfowidget.h
+ 1 +
diff --git a/src/internet/podcasts/podcastservice.cpp b/src/internet/podcasts/podcastservice.cpp index 6b196aaab..2258038a8 100644 --- a/src/internet/podcasts/podcastservice.cpp +++ b/src/internet/podcasts/podcastservice.cpp @@ -528,8 +528,15 @@ void PodcastService::ShowContextMenu(const QPoint& global_pos) { } if (selected_podcasts_.count() == 1) { - info_selected_action_->setEnabled(true); + if (selected_episodes_.count() == 1) { + info_selected_action_->setText(tr("Episode information")); + info_selected_action_->setEnabled(true); + } else { + info_selected_action_->setText(tr("Podcast information")); + info_selected_action_->setEnabled(true); + } } else { + info_selected_action_->setText(tr("Podcast information")); info_selected_action_->setEnabled(false); } @@ -699,10 +706,19 @@ void PodcastService::DownloadSelectedEpisode() { } void PodcastService::PodcastInfo() { - if (selected_podcasts_.count() > 0) { - const Podcast podcast = - selected_podcasts_[0].data(Role_Podcast).value(); - podcast_info_dialog_.reset(new PodcastInfoDialog(app_)); + if (selected_podcasts_.isEmpty()) { + // Should never happen. + return; + } + const Podcast podcast = + selected_podcasts_[0].data(Role_Podcast).value(); + podcast_info_dialog_.reset(new PodcastInfoDialog(app_)); + + if (selected_episodes_.count() == 1) { + const PodcastEpisode episode = + selected_episodes_[0].data(Role_Episode).value(); + podcast_info_dialog_->ShowEpisode(episode, podcast); + } else { podcast_info_dialog_->ShowPodcast(podcast); } }