Add podcast detail page
This commit is contained in:
parent
3ec5d94c30
commit
172dabc6be
7 changed files with 187 additions and 11 deletions
15
main.py
15
main.py
|
@ -368,6 +368,20 @@ class gPotherSide:
|
||||||
if episode.total_time > 0:
|
if episode.total_time > 0:
|
||||||
yield '%02d:%02d:%02d' % (episode.total_time / (60 * 60), (episode.total_time / 60) % 60, episode.total_time % 60)
|
yield '%02d:%02d:%02d' % (episode.total_time / (60 * 60), (episode.total_time / 60) % 60, episode.total_time % 60)
|
||||||
|
|
||||||
|
def show_podcast(self, podcast_id):
|
||||||
|
podcast = self._get_podcast_by_id(podcast_id)
|
||||||
|
if podcast is None:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'title': podcast.title,
|
||||||
|
'description': util.remove_html_tags(podcast.description),
|
||||||
|
'link': podcast.link,
|
||||||
|
'url': podcast.url,
|
||||||
|
'section': podcast.section,
|
||||||
|
'coverart': self._get_cover(podcast),
|
||||||
|
}
|
||||||
|
|
||||||
def set_config_value(self, option, value):
|
def set_config_value(self, option, value):
|
||||||
self.core.config.update_field(option, value)
|
self.core.config.update_field(option, value)
|
||||||
|
|
||||||
|
@ -430,3 +444,4 @@ set_config_value = gpotherside.set_config_value
|
||||||
get_config_value = gpotherside.get_config_value
|
get_config_value = gpotherside.get_config_value
|
||||||
get_directory_providers = gpotherside.get_directory_providers
|
get_directory_providers = gpotherside.get_directory_providers
|
||||||
get_directory_entries = gpotherside.get_directory_entries
|
get_directory_entries = gpotherside.get_directory_entries
|
||||||
|
show_podcast = gpotherside.show_podcast
|
||||||
|
|
|
@ -45,6 +45,7 @@ SlidePage {
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
py.call('main.show_episode', [episode_id], function (episode) {
|
py.call('main.show_episode', [episode_id], function (episode) {
|
||||||
|
detailPage.title = episode.title;
|
||||||
descriptionLabel.text = episode.description;
|
descriptionLabel.text = episode.description;
|
||||||
metadataLabel.text = episode.metadata;
|
metadataLabel.text = episode.metadata;
|
||||||
detailPage.link = episode.link;
|
detailPage.link = episode.link;
|
||||||
|
|
|
@ -47,6 +47,12 @@ SlidePage {
|
||||||
py.call('main.mark_episodes_as_old', [episodesPage.podcast_id]);
|
py.call('main.mark_episodes_as_old', [episodesPage.podcast_id]);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'Podcast details',
|
||||||
|
callback: function () {
|
||||||
|
pgst.loadPage('PodcastDetail.qml', {podcast_id: podcast_id, title: title});
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Unsubscribe',
|
label: 'Unsubscribe',
|
||||||
callback: function () {
|
callback: function () {
|
||||||
|
|
|
@ -27,7 +27,7 @@ import 'common/util.js' as Util
|
||||||
Item {
|
Item {
|
||||||
id: expander
|
id: expander
|
||||||
|
|
||||||
property bool expanded: false
|
property bool expanded: !canExpand
|
||||||
property bool canExpand: expandedHeight > contractedHeight
|
property bool canExpand: expandedHeight > contractedHeight
|
||||||
|
|
||||||
property real contractedHeight: 100 * pgst.scalef
|
property real contractedHeight: 100 * pgst.scalef
|
||||||
|
|
161
touch/PodcastDetail.qml
Normal file
161
touch/PodcastDetail.qml
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* gPodder QML UI Reference Implementation
|
||||||
|
* Copyright (c) 2014, Thomas Perl <m@thp.io>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.0
|
||||||
|
|
||||||
|
import 'common/constants.js' as Constants
|
||||||
|
import 'common/util.js' as Util
|
||||||
|
import 'icons/icons.js' as Icons
|
||||||
|
|
||||||
|
SlidePage {
|
||||||
|
id: podcastDetail
|
||||||
|
|
||||||
|
property int podcast_id
|
||||||
|
property string title
|
||||||
|
property string description
|
||||||
|
property string link
|
||||||
|
property string section
|
||||||
|
property string coverart
|
||||||
|
property string url
|
||||||
|
|
||||||
|
property bool ready: false
|
||||||
|
|
||||||
|
hasMenuButton: true
|
||||||
|
onMenuButtonClicked: {
|
||||||
|
pgst.showSelection([
|
||||||
|
{
|
||||||
|
label: 'Visit website',
|
||||||
|
callback: function () {
|
||||||
|
Qt.openUrlExternally(podcastDetail.link);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Copy feed URL',
|
||||||
|
callback: function () {
|
||||||
|
pgst.loadPage('TextInputDialog.qml', {
|
||||||
|
placeholderText: 'Feed URL',
|
||||||
|
text: podcastDetail.url,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Change section',
|
||||||
|
callback: function () {
|
||||||
|
var ctx = { py: py, id: podcastDetail.podcast_id };
|
||||||
|
pgst.loadPage('TextInputDialog.qml', {
|
||||||
|
buttonText: 'Change section',
|
||||||
|
placeholderText: 'New section',
|
||||||
|
text: section,
|
||||||
|
callback: function (new_section) {
|
||||||
|
ctx.py.call('main.change_section', [ctx.id, new_section]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
PBusyIndicator {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: !podcastDetail.ready
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
py.call('main.show_podcast', [podcast_id], function (podcast) {
|
||||||
|
podcastDetail.title = podcast.title;
|
||||||
|
podcastDetail.description = podcast.description;
|
||||||
|
podcastDetail.link = podcast.link;
|
||||||
|
podcastDetail.section = podcast.section;
|
||||||
|
podcastDetail.coverart = podcast.coverart;
|
||||||
|
podcastDetail.url = podcast.url;
|
||||||
|
podcastDetail.ready = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Flickable {
|
||||||
|
id: flickable
|
||||||
|
anchors.fill: parent
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
|
||||||
|
contentWidth: detailColumn.width
|
||||||
|
contentHeight: detailColumn.height + detailColumn.spacing
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: detailColumn
|
||||||
|
|
||||||
|
width: podcastDetail.width
|
||||||
|
spacing: Constants.layout.padding * pgst.scalef
|
||||||
|
|
||||||
|
Item { height: Constants.layout.padding * pgst.scalef; width: parent.width }
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width - 2 * Constants.layout.padding * pgst.scalef
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
spacing: Constants.layout.padding * pgst.scalef
|
||||||
|
|
||||||
|
PExpander {
|
||||||
|
width: parent.width
|
||||||
|
expandedHeight: coverImage.height
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: coverImage
|
||||||
|
source: podcastDetail.coverart
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
width: parent.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PLabel {
|
||||||
|
text: podcastDetail.title
|
||||||
|
width: parent.width
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
font.pixelSize: 35 * pgst.scalef
|
||||||
|
color: Constants.colors.highlight
|
||||||
|
}
|
||||||
|
|
||||||
|
PLabel {
|
||||||
|
visible: text !== ''
|
||||||
|
text: podcastDetail.link
|
||||||
|
width: parent.width
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
font.pixelSize: 20 * pgst.scalef
|
||||||
|
color: Constants.colors.placeholder
|
||||||
|
}
|
||||||
|
|
||||||
|
PLabel {
|
||||||
|
text: 'Section: ' + podcastDetail.section
|
||||||
|
width: parent.width
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
font.pixelSize: 20 * pgst.scalef
|
||||||
|
color: Constants.colors.placeholder
|
||||||
|
}
|
||||||
|
|
||||||
|
PLabel {
|
||||||
|
text: podcastDetail.description
|
||||||
|
width: parent.width
|
||||||
|
font.pixelSize: 30 * pgst.scalef
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PScrollDecorator { flickable: flickable }
|
||||||
|
}
|
|
@ -137,17 +137,9 @@ SlidePage {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Change section',
|
label: 'Podcast details',
|
||||||
callback: function () {
|
callback: function () {
|
||||||
var ctx = { py: py, id: id };
|
pgst.loadPage('PodcastDetail.qml', {podcast_id: id, title: title});
|
||||||
pgst.loadPage('TextInputDialog.qml', {
|
|
||||||
buttonText: 'Change section',
|
|
||||||
placeholderText: 'New section',
|
|
||||||
text: section,
|
|
||||||
callback: function (new_section) {
|
|
||||||
ctx.py.call('main.change_section', [ctx.id, new_section]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
], title);
|
], title);
|
||||||
|
|
|
@ -62,6 +62,7 @@ Dialog {
|
||||||
id: button
|
id: button
|
||||||
width: input.width
|
width: input.width
|
||||||
height: input.height
|
height: input.height
|
||||||
|
visible: textInputDialog.buttonText !== ''
|
||||||
|
|
||||||
PLabel {
|
PLabel {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue