mirror of
https://github.com/HenkKalkwater/harbour-sailfin.git
synced 2024-11-22 01:05:17 +00:00
Add navigation to artists from tracks
I'm not to happy about the C++ sides. If anyone from the future finds this commit with "git blame" while debugging this code: I apologise
This commit is contained in:
parent
3f9661ccb5
commit
0fafb19c7d
|
@ -28,6 +28,7 @@
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QQmlListProperty>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
|
||||||
|
@ -55,6 +56,51 @@ namespace ViewModel {
|
||||||
|
|
||||||
class UserData;
|
class UserData;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
template<typename T>
|
||||||
|
void qqmlistproperty_qlist_append(QQmlListProperty<T> *prop, T *data) {
|
||||||
|
static_cast<QList<T> *>(prop->data())->append(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void qqmlistproperty_qlist_clear(QQmlListProperty<T> *prop) {
|
||||||
|
static_cast<QList<T> *>(prop->data())->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T *qqmlistproperty_qlist_at(QQmlListProperty<T> *prop, qint32 idx) {
|
||||||
|
return &static_cast<QList<T> *>(prop->data())->at(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void qqmlistproperty_qlist_count(QQmlListProperty<T> *prop) {
|
||||||
|
static_cast<QList<T> *>(prop->data())->count();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
QQmlListProperty<T> qQmlListPropertyFromQList(QObject *object, QList<T> *list) {
|
||||||
|
return QQmlListProperty<T>(object, list, &qqmlistproperty_qlist_append<T>, &qqmlistproperty_qlist_count<T>, &qqmlistproperty_qlist_at<T>, &qqmlistproperty_qlist_clear<T>);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class NameGuidPair : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
|
||||||
|
Q_PROPERTY(QString jellyfinId READ jellyfinId NOTIFY jellyfinIdChanged)
|
||||||
|
public:
|
||||||
|
explicit NameGuidPair(QSharedPointer<DTO::NameGuidPair> data = QSharedPointer<DTO::NameGuidPair>::create(QStringLiteral("00000000000000000000000000000000")), QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QString name() const { return m_data->name(); }
|
||||||
|
QString jellyfinId() const { return m_data->jellyfinId(); }
|
||||||
|
signals:
|
||||||
|
void nameChanged(const QString &newName);
|
||||||
|
void jellyfinIdChanged(const QString &newJellyfinId);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QSharedPointer<DTO::NameGuidPair> m_data;
|
||||||
|
};
|
||||||
|
|
||||||
class Item : public QObject {
|
class Item : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
@ -116,6 +162,7 @@ public:
|
||||||
Q_PROPERTY(QList<QObject *> subtitleStreams READ subtitleStreams NOTIFY subtitleStreamsChanged)
|
Q_PROPERTY(QList<QObject *> subtitleStreams READ subtitleStreams NOTIFY subtitleStreamsChanged)
|
||||||
Q_PROPERTY(double primaryImageAspectRatio READ primaryImageAspectRatio NOTIFY primaryImageAspectRatioChanged)
|
Q_PROPERTY(double primaryImageAspectRatio READ primaryImageAspectRatio NOTIFY primaryImageAspectRatioChanged)
|
||||||
Q_PROPERTY(QStringList artists READ artists NOTIFY artistsChanged)
|
Q_PROPERTY(QStringList artists READ artists NOTIFY artistsChanged)
|
||||||
|
Q_PROPERTY(QList<QObject *> artistItems READ artistItems NOTIFY artistItemsChanged);
|
||||||
// Why is this a QJsonObject? Well, because I couldn't be bothered to implement the deserialisations of
|
// Why is this a QJsonObject? Well, because I couldn't be bothered to implement the deserialisations of
|
||||||
// a QHash at the moment.
|
// a QHash at the moment.
|
||||||
Q_PROPERTY(QJsonObject imageTags READ imageTags NOTIFY imageTagsChanged)
|
Q_PROPERTY(QJsonObject imageTags READ imageTags NOTIFY imageTagsChanged)
|
||||||
|
@ -171,6 +218,7 @@ public:
|
||||||
QObjectList subtitleStreams() const { return m_subtitleStreams; }
|
QObjectList subtitleStreams() const { return m_subtitleStreams; }
|
||||||
double primaryImageAspectRatio() const { return m_data->primaryImageAspectRatio().value_or(1.0); }
|
double primaryImageAspectRatio() const { return m_data->primaryImageAspectRatio().value_or(1.0); }
|
||||||
QStringList artists() const { return m_data->artists(); }
|
QStringList artists() const { return m_data->artists(); }
|
||||||
|
QList<QObject *> artistItems() const{ return this->m_artistItems; }
|
||||||
QJsonObject imageTags() const { return m_data->imageTags(); }
|
QJsonObject imageTags() const { return m_data->imageTags(); }
|
||||||
QStringList backdropImageTags() const { return m_data->backdropImageTags(); }
|
QStringList backdropImageTags() const { return m_data->backdropImageTags(); }
|
||||||
QJsonObject imageBlurHashes() const { return m_data->imageBlurHashes(); }
|
QJsonObject imageBlurHashes() const { return m_data->imageBlurHashes(); }
|
||||||
|
@ -243,6 +291,7 @@ signals:
|
||||||
void subtitleStreamsChanged(QVariantList &newSubtitleStreams);
|
void subtitleStreamsChanged(QVariantList &newSubtitleStreams);
|
||||||
void primaryImageAspectRatioChanged(double newPrimaryImageAspectRatio);
|
void primaryImageAspectRatioChanged(double newPrimaryImageAspectRatio);
|
||||||
void artistsChanged(const QStringList &newArtists);
|
void artistsChanged(const QStringList &newArtists);
|
||||||
|
void artistItemsChanged();
|
||||||
void imageTagsChanged();
|
void imageTagsChanged();
|
||||||
void backdropImageTagsChanged();
|
void backdropImageTagsChanged();
|
||||||
void imageBlurHashesChanged();
|
void imageBlurHashesChanged();
|
||||||
|
@ -269,6 +318,7 @@ protected:
|
||||||
QObjectList m_audioStreams;
|
QObjectList m_audioStreams;
|
||||||
QObjectList m_videoStreams;
|
QObjectList m_videoStreams;
|
||||||
QObjectList m_subtitleStreams;
|
QObjectList m_subtitleStreams;
|
||||||
|
QObjectList m_artistItems;
|
||||||
private slots:
|
private slots:
|
||||||
void onUserDataChanged(const DTO::UserItemDataDto &userData);
|
void onUserDataChanged(const DTO::UserItemDataDto &userData);
|
||||||
};
|
};
|
||||||
|
|
|
@ -355,6 +355,7 @@ public:
|
||||||
indexNumber,
|
indexNumber,
|
||||||
runTimeTicks,
|
runTimeTicks,
|
||||||
artists,
|
artists,
|
||||||
|
artistItems,
|
||||||
isFolder,
|
isFolder,
|
||||||
overview,
|
overview,
|
||||||
parentIndexNumber,
|
parentIndexNumber,
|
||||||
|
@ -395,6 +396,7 @@ public:
|
||||||
JFRN(indexNumber),
|
JFRN(indexNumber),
|
||||||
JFRN(runTimeTicks),
|
JFRN(runTimeTicks),
|
||||||
JFRN(artists),
|
JFRN(artists),
|
||||||
|
JFRN(artistItems),
|
||||||
JFRN(isFolder),
|
JFRN(isFolder),
|
||||||
JFRN(overview),
|
JFRN(overview),
|
||||||
JFRN(parentIndexNumber),
|
JFRN(parentIndexNumber),
|
||||||
|
|
|
@ -59,6 +59,7 @@ public:
|
||||||
// Item properties
|
// Item properties
|
||||||
name = Qt::UserRole + 1,
|
name = Qt::UserRole + 1,
|
||||||
artists,
|
artists,
|
||||||
|
artistItems,
|
||||||
runTimeTicks,
|
runTimeTicks,
|
||||||
|
|
||||||
// Non-item properties
|
// Non-item properties
|
||||||
|
|
|
@ -54,6 +54,7 @@ void JellyfinPlugin::registerTypes(const char *uri) {
|
||||||
qmlRegisterUncreatableType<ViewModel::User>(uri, 1, 0, "User", "Acquire one via UserLoader or exposed properties");
|
qmlRegisterUncreatableType<ViewModel::User>(uri, 1, 0, "User", "Acquire one via UserLoader or exposed properties");
|
||||||
qmlRegisterUncreatableType<EventBus>(uri, 1, 0, "EventBus", "Obtain one via your ApiClient");
|
qmlRegisterUncreatableType<EventBus>(uri, 1, 0, "EventBus", "Obtain one via your ApiClient");
|
||||||
qmlRegisterUncreatableType<WebSocket>(uri, 1, 0, "WebSocket", "Obtain one via your ApiClient");
|
qmlRegisterUncreatableType<WebSocket>(uri, 1, 0, "WebSocket", "Obtain one via your ApiClient");
|
||||||
|
qmlRegisterUncreatableType<ViewModel::NameGuidPair>(uri, 1, 0, "NameGuidPair", "Obbtain one via an Item");
|
||||||
qmlRegisterUncreatableType<ViewModel::MediaStream>(uri, 1, 0, "MediaStream", "Obtain one via an Item");
|
qmlRegisterUncreatableType<ViewModel::MediaStream>(uri, 1, 0, "MediaStream", "Obtain one via an Item");
|
||||||
qmlRegisterUncreatableType<ViewModel::Settings>(uri, 1, 0, "Settings", "Obtain one via your ApiClient");
|
qmlRegisterUncreatableType<ViewModel::Settings>(uri, 1, 0, "Settings", "Obtain one via your ApiClient");
|
||||||
qmlRegisterUncreatableType<ViewModel::UserData>(uri, 1, 0, "UserData", "Obtain one via an Item");
|
qmlRegisterUncreatableType<ViewModel::UserData>(uri, 1, 0, "UserData", "Obtain one via an Item");
|
||||||
|
|
|
@ -26,6 +26,9 @@
|
||||||
namespace Jellyfin {
|
namespace Jellyfin {
|
||||||
namespace ViewModel {
|
namespace ViewModel {
|
||||||
|
|
||||||
|
NameGuidPair::NameGuidPair(QSharedPointer<DTO::NameGuidPair> data, QObject *parent)
|
||||||
|
: QObject(parent), m_data(data) {}
|
||||||
|
|
||||||
Item::Item(QObject *parent, QSharedPointer<Model::Item> data)
|
Item::Item(QObject *parent, QSharedPointer<Model::Item> data)
|
||||||
: QObject(parent),
|
: QObject(parent),
|
||||||
m_data(data),
|
m_data(data),
|
||||||
|
@ -44,6 +47,7 @@ void Item::setData(QSharedPointer<Model::Item> newData) {
|
||||||
|
|
||||||
if (!m_data.isNull()) {
|
if (!m_data.isNull()) {
|
||||||
connect(m_data.data(), &Model::Item::userDataChanged, this, &Item::onUserDataChanged);
|
connect(m_data.data(), &Model::Item::userDataChanged, this, &Item::onUserDataChanged);
|
||||||
|
updateMediaStreams();
|
||||||
setUserData(m_data->userData());
|
setUserData(m_data->userData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,6 +81,11 @@ void Item::updateMediaStreams() {
|
||||||
qDebug() << m_audioStreams.size() << " audio streams, " << m_videoStreams.size() << " video streams, "
|
qDebug() << m_audioStreams.size() << " audio streams, " << m_videoStreams.size() << " video streams, "
|
||||||
<< m_subtitleStreams.size() << " subtitle streams, " << m_allMediaStreams.size() << " streams total";
|
<< m_subtitleStreams.size() << " subtitle streams, " << m_allMediaStreams.size() << " streams total";
|
||||||
|
|
||||||
|
m_artistItems.clear();
|
||||||
|
const QList<DTO::NameGuidPair> artists = m_data->artistItems();
|
||||||
|
for (auto it = artists.cbegin(); it != artists.cend(); it++) {
|
||||||
|
m_artistItems.append(new NameGuidPair(QSharedPointer<DTO::NameGuidPair>::create(*it), this));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Item::setUserData(DTO::UserItemDataDto &newData) {
|
void Item::setUserData(DTO::UserItemDataDto &newData) {
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
*/
|
*/
|
||||||
#include "JellyfinQt/viewmodel/itemmodel.h"
|
#include "JellyfinQt/viewmodel/itemmodel.h"
|
||||||
|
|
||||||
|
#include "JellyfinQt/viewmodel/item.h"
|
||||||
|
|
||||||
#include "JellyfinQt/loader/http/artists.h"
|
#include "JellyfinQt/loader/http/artists.h"
|
||||||
#include "JellyfinQt/loader/http/items.h"
|
#include "JellyfinQt/loader/http/items.h"
|
||||||
#include "JellyfinQt/loader/http/userlibrary.h"
|
#include "JellyfinQt/loader/http/userlibrary.h"
|
||||||
|
@ -93,6 +95,14 @@ QVariant ItemModel::data(const QModelIndex &index, int role) const {
|
||||||
case RoleNames::runTimeTicks:
|
case RoleNames::runTimeTicks:
|
||||||
return QVariant(item->runTimeTicks().value_or(0));
|
return QVariant(item->runTimeTicks().value_or(0));
|
||||||
JF_CASE(artists)
|
JF_CASE(artists)
|
||||||
|
case RoleNames::artistItems: {
|
||||||
|
QVariantList data;
|
||||||
|
auto artists = item->artistItems();
|
||||||
|
for (auto it = artists.cbegin(); it != artists.cend(); it++) {
|
||||||
|
data.append(QVariant::fromValue(new NameGuidPair(QSharedPointer<DTO::NameGuidPair>::create(*it), const_cast<ItemModel *>(this))));
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
case RoleNames::isFolder:
|
case RoleNames::isFolder:
|
||||||
return QVariant(item->isFolder().value_or(false));
|
return QVariant(item->isFolder().value_or(false));
|
||||||
JF_CASE(overview)
|
JF_CASE(overview)
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
*/
|
*/
|
||||||
#include "JellyfinQt/viewmodel/playlist.h"
|
#include "JellyfinQt/viewmodel/playlist.h"
|
||||||
|
|
||||||
|
#include "JellyfinQt/viewmodel/item.h"
|
||||||
|
|
||||||
namespace Jellyfin {
|
namespace Jellyfin {
|
||||||
namespace ViewModel {
|
namespace ViewModel {
|
||||||
|
|
||||||
|
@ -47,6 +49,7 @@ QHash<int, QByteArray> Playlist::roleNames() const {
|
||||||
return {
|
return {
|
||||||
{RoleNames::name, "name"},
|
{RoleNames::name, "name"},
|
||||||
{RoleNames::artists, "artists"},
|
{RoleNames::artists, "artists"},
|
||||||
|
{RoleNames::artistItems, "artistItems"},
|
||||||
{RoleNames::runTimeTicks, "runTimeTicks"},
|
{RoleNames::runTimeTicks, "runTimeTicks"},
|
||||||
{RoleNames::section, "section"},
|
{RoleNames::section, "section"},
|
||||||
{RoleNames::playing, "playing"},
|
{RoleNames::playing, "playing"},
|
||||||
|
@ -83,6 +86,16 @@ QVariant Playlist::data(const QModelIndex &index, int role) const {
|
||||||
return QVariant(rowData->name());
|
return QVariant(rowData->name());
|
||||||
case RoleNames::artists:
|
case RoleNames::artists:
|
||||||
return QVariant(rowData->artists());
|
return QVariant(rowData->artists());
|
||||||
|
case RoleNames::artistItems: {
|
||||||
|
QVariantList result;
|
||||||
|
|
||||||
|
auto items = rowData->artistItems();
|
||||||
|
for (auto it = items.cbegin(); it != items.cend(); it++) {
|
||||||
|
result.append(QVariant::fromValue(new NameGuidPair(QSharedPointer<DTO::NameGuidPair>::create(*it), const_cast<Playlist *>(this))));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
case RoleNames::runTimeTicks:
|
case RoleNames::runTimeTicks:
|
||||||
return QVariant(rowData->runTimeTicks().value_or(-1));
|
return QVariant(rowData->runTimeTicks().value_or(-1));
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
- New layout for artist pages
|
- New layout for artist pages
|
||||||
- New layout for the music library
|
- New layout for the music library
|
||||||
- New layout for playlist pages
|
- New layout for playlist pages
|
||||||
|
- Navigation to artists of a song added when long-pressing a song or pressing the name
|
||||||
|
on the now playing screen.
|
||||||
- Bug fixes
|
- Bug fixes
|
||||||
- The album overview page should now behave correclty with an image with a non-square image
|
- The album overview page should now behave correclty with an image with a non-square image
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ SilicaListView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delegate: SongDelegate {
|
delegate: SongDelegate {
|
||||||
artists: model.artists
|
artists: model.artistItems
|
||||||
name: model.name
|
name: model.name
|
||||||
width: parent.width
|
width: parent.width
|
||||||
indexNumber: index + 1
|
indexNumber: index + 1
|
||||||
|
|
|
@ -147,6 +147,11 @@ PanelBackground {
|
||||||
maximumLineCount: 1
|
maximumLineCount: 1
|
||||||
truncationMode: TruncationMode.Fade
|
truncationMode: TruncationMode.Fade
|
||||||
color: highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
|
color: highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
|
||||||
|
linkColor: Theme.secondaryColor
|
||||||
|
onLinkActivated: {
|
||||||
|
appWindow.navigateToItem(link, "Audio", "MusicArtist", true)
|
||||||
|
}
|
||||||
|
textFormat: Text.RichText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,6 +354,21 @@ PanelBackground {
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: artists
|
target: artists
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
text: {
|
||||||
|
var links = [];
|
||||||
|
var items = manager.item.artistItems;
|
||||||
|
console.log(items)
|
||||||
|
for (var i = 0; i < items.length; i++) {
|
||||||
|
links.push("<a href=\"%1\" style=\"text-decoration:none;color:%3\">%2</a>"
|
||||||
|
.arg(items[i].jellyfinId)
|
||||||
|
.arg(items[i].name)
|
||||||
|
.arg(Theme.secondaryColor)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return links.join(", ")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
AnchorChanges {
|
AnchorChanges {
|
||||||
target: artists
|
target: artists
|
||||||
|
@ -419,6 +439,7 @@ PanelBackground {
|
||||||
id: fullPage
|
id: fullPage
|
||||||
Page {
|
Page {
|
||||||
property bool __hidePlaybackBar: true
|
property bool __hidePlaybackBar: true
|
||||||
|
property bool __isPlaybackBar: true
|
||||||
showNavigationIndicator: true
|
showNavigationIndicator: true
|
||||||
allowedOrientations: appWindow.allowedOrientations
|
allowedOrientations: appWindow.allowedOrientations
|
||||||
SilicaFlickable {
|
SilicaFlickable {
|
||||||
|
|
|
@ -31,6 +31,7 @@ ListItem {
|
||||||
|
|
||||||
contentHeight: songName.height + songArtists.height + 2 * Theme.paddingMedium
|
contentHeight: songName.height + songArtists.height + 2 * Theme.paddingMedium
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
menu: contextMenu
|
||||||
|
|
||||||
TextMetrics {
|
TextMetrics {
|
||||||
id: indexMetrics
|
id: indexMetrics
|
||||||
|
@ -77,7 +78,13 @@ ListItem {
|
||||||
right: parent.right
|
right: parent.right
|
||||||
rightMargin: Theme.horizontalPageMargin
|
rightMargin: Theme.horizontalPageMargin
|
||||||
}
|
}
|
||||||
text: artists.join(", ")
|
text: {
|
||||||
|
var names = []
|
||||||
|
for (var i = 0; i < artists.length; i++) {
|
||||||
|
names.push(artists[i].name)
|
||||||
|
}
|
||||||
|
return names.join(", ")
|
||||||
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
truncationMode: TruncationMode.Fade
|
truncationMode: TruncationMode.Fade
|
||||||
color: highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
|
color: highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
|
||||||
|
@ -97,4 +104,48 @@ ListItem {
|
||||||
color: highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
|
color: highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor
|
||||||
highlighted: down || playing
|
highlighted: down || playing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function goToArtist(id) {
|
||||||
|
appWindow.navigateToItem(id, "audio", "MusicArtist", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: contextMenu
|
||||||
|
ContextMenu {
|
||||||
|
MenuItem {
|
||||||
|
text: {
|
||||||
|
if(artists.length === 1) {
|
||||||
|
//: Context menu item for navigating to the artist of the selected track
|
||||||
|
return qsTr("Go to %1").arg(artists[0].name)
|
||||||
|
} else {
|
||||||
|
//: Context menu item for navigating to one of the artists of the selected track (opens submenu)
|
||||||
|
return qsTr("Go to artists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onDelayedClick: {
|
||||||
|
if (artists.length > 1) {
|
||||||
|
songDelegateRoot.menu = artistMenu
|
||||||
|
songDelegateRoot.openMenu()
|
||||||
|
} else {
|
||||||
|
goToArtist(artists[0].jellyfinId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: artistMenu
|
||||||
|
ContextMenu {
|
||||||
|
Repeater {
|
||||||
|
model: artists
|
||||||
|
MenuItem {
|
||||||
|
text: modelData.name
|
||||||
|
onDelayedClick: goToArtist(modelData.jellyfinId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onClosed: songDelegateRoot.menu = contextMenu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,7 +154,13 @@ ApplicationWindow {
|
||||||
if (mediaType === "Audio" && !isFolder) {
|
if (mediaType === "Audio" && !isFolder) {
|
||||||
playbackManager.playItemId(jellyfinId)
|
playbackManager.playItemId(jellyfinId)
|
||||||
} else {
|
} else {
|
||||||
pageStack.push(Utils.getPageUrl(mediaType, type, isFolder), {"itemId": jellyfinId});
|
var url = Utils.getPageUrl(mediaType, type, isFolder)
|
||||||
|
var properties = {"itemId": jellyfinId}
|
||||||
|
if ("__isPlaybackBar" in pageStack.currentPage) {
|
||||||
|
pageStack.replace(url, properties);
|
||||||
|
} else {
|
||||||
|
pageStack.push(url, properties);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ BaseDetailPage {
|
||||||
delegate: SongDelegate {
|
delegate: SongDelegate {
|
||||||
id: songDelegate
|
id: songDelegate
|
||||||
name: model.name
|
name: model.name
|
||||||
artists: model.artists
|
artists: model.artistItems
|
||||||
duration: model.runTimeTicks
|
duration: model.runTimeTicks
|
||||||
indexNumber: itemData.type === "MusicAlbum" ? model.indexNumber : index + 1
|
indexNumber: itemData.type === "MusicAlbum" ? model.indexNumber : index + 1
|
||||||
onClicked: window.playbackManager.playItemInList(collectionModel, model.index)
|
onClicked: window.playbackManager.playItemInList(collectionModel, model.index)
|
||||||
|
|
|
@ -19,6 +19,15 @@ BaseDetailPage {
|
||||||
SilicaFlickable {
|
SilicaFlickable {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
contentHeight: content.height
|
contentHeight: content.height
|
||||||
|
Component {
|
||||||
|
id: latestMediaLoaderComponent
|
||||||
|
J.LatestMediaLoader {
|
||||||
|
apiClient: appWindow.apiClient
|
||||||
|
parentId: itemData.jellyfinId
|
||||||
|
includeItemTypes: "Audio"
|
||||||
|
autoReload: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: albumArtistLoaderComponent
|
id: albumArtistLoaderComponent
|
||||||
|
@ -64,7 +73,6 @@ BaseDetailPage {
|
||||||
text: qsTr("Recently added")
|
text: qsTr("Recently added")
|
||||||
//collapseWhenEmpty: false
|
//collapseWhenEmpty: false
|
||||||
extraBusy: !_firstTimeLoaded
|
extraBusy: !_firstTimeLoaded
|
||||||
clickable: false
|
|
||||||
loader: J.LatestMediaLoader {
|
loader: J.LatestMediaLoader {
|
||||||
apiClient: appWindow.apiClient
|
apiClient: appWindow.apiClient
|
||||||
parentId: itemData.jellyfinId
|
parentId: itemData.jellyfinId
|
||||||
|
@ -72,6 +80,12 @@ BaseDetailPage {
|
||||||
includeItemTypes: "Audio"
|
includeItemTypes: "Audio"
|
||||||
limit: 12
|
limit: 12
|
||||||
}
|
}
|
||||||
|
onHeaderClicked: pageStack.push(Qt.resolvedUrl("CollectionPage.qml"), {
|
||||||
|
"loader": latestMediaLoaderComponent.createObject(musicLibraryPage),
|
||||||
|
//: Page title for the list of all albums within the music library
|
||||||
|
"pageTitle": qsTr("Latest media"),
|
||||||
|
"allowSort": false
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue