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.
This commit is contained in:
parent
d8bab5a49f
commit
5a1a5a9d95
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/* This file is part of Clementine.
|
||||
Copyright 2018, Jim Broadus <jbroadus@gmail.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "core/utilities.h"
|
||||
#include "episodeinfowidget.h"
|
||||
#include "ui_episodeinfowidget.h"
|
||||
|
||||
#include <QTime>
|
||||
|
||||
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));
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/* This file is part of Clementine.
|
||||
Copyright 2018, Jim Broadus <jbroadus@gmail.com>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef INTERNET_PODCASTS_EPISODEINFOWIDGET_H_
|
||||
#define INTERNET_PODCASTS_EPISODEINFOWIDGET_H_
|
||||
|
||||
#include "podcastepisode.h"
|
||||
|
||||
#include <QFrame>
|
||||
|
||||
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_
|
|
@ -0,0 +1,137 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>EpisodeInfoWidget</class>
|
||||
<widget class="QWidget" name="EpisodeInfoWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>398</width>
|
||||
<height>551</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">#title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#description {
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
QLineEdit {
|
||||
background: transparent;
|
||||
}</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMinAndMaxSize</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="title">
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="description">
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMinAndMaxSize</enum>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="author_label">
|
||||
<property name="text">
|
||||
<string>Author</string>
|
||||
</property>
|
||||
<property name="field_label" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="date">
|
||||
<property name="frame">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="author">
|
||||
<property name="frame">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="date_label">
|
||||
<property name="text">
|
||||
<string>Date</string>
|
||||
</property>
|
||||
<property name="field_label" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="duration_label">
|
||||
<property name="text">
|
||||
<string>Duration</string>
|
||||
</property>
|
||||
<property name="field_label" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="duration">
|
||||
<property name="frame">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>493</width>
|
||||
<height>395</height>
|
||||
<height>415</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -18,7 +18,36 @@
|
|||
<widget class="QLineEdit" name="podcast_url"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="details_scroll_area">
|
||||
<widget class="QScrollArea" name="episode_info_scroll_area">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>250</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="EpisodeInfoWidget" name="episode_details">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>473</width>
|
||||
<height>163</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="podcast_info_scroll_area">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>250</width>
|
||||
|
@ -37,13 +66,13 @@
|
|||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="PodcastInfoWidget" name="details">
|
||||
<widget class="PodcastInfoWidget" name="podcast_details">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>473</width>
|
||||
<height>313</height>
|
||||
<height>162</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
|
@ -68,6 +97,12 @@
|
|||
<header>internet/podcasts/podcastinfowidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>EpisodeInfoWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header location="global">internet/podcasts/episodeinfowidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
|
|
|
@ -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>();
|
||||
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>();
|
||||
podcast_info_dialog_.reset(new PodcastInfoDialog(app_));
|
||||
|
||||
if (selected_episodes_.count() == 1) {
|
||||
const PodcastEpisode episode =
|
||||
selected_episodes_[0].data(Role_Episode).value<PodcastEpisode>();
|
||||
podcast_info_dialog_->ShowEpisode(episode, podcast);
|
||||
} else {
|
||||
podcast_info_dialog_->ShowPodcast(podcast);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue