diff --git a/core/3rdparty/qtpromise b/core/3rdparty/qtpromise new file mode 160000 index 0000000..f7639e9 --- /dev/null +++ b/core/3rdparty/qtpromise @@ -0,0 +1 @@ +Subproject commit f7639e921ee7b0d20de4ac0da67c0f69e0692101 diff --git a/core/include/JellyfinQt/apimodel.h b/core/include/JellyfinQt/apimodel.h index 59a8f4e..a0bff30 100644 --- a/core/include/JellyfinQt/apimodel.h +++ b/core/include/JellyfinQt/apimodel.h @@ -239,23 +239,33 @@ bool setRequestStartIndex(P ¶meters, int startIndex) { #ifndef JELLYFIN_APIMODEL_CPP extern template bool setRequestStartIndex(Loader::GetUserViewsParams ¶ms, int startIndex); extern template void setRequestLimit(Loader::GetUserViewsParams ¶ms, int limit); + extern template QList extractRecords(const DTO::BaseItemDtoQueryResult &result); extern template int extractTotalRecordCount(const DTO::BaseItemDtoQueryResult &result); extern template QList extractRecords(const QList &result); extern template int extractTotalRecordCount(const QList &result); + extern template void setRequestLimit(Loader::GetLatestMediaParams ¶ms, int limit); extern template bool setRequestStartIndex(Loader::GetLatestMediaParams ¶ms, int offset); + extern template void setRequestLimit(Loader::GetItemsByUserIdParams ¶ms, int limit); extern template bool setRequestStartIndex(Loader::GetItemsByUserIdParams ¶ms, int offset); + extern template void setRequestLimit(Loader::GetResumeItemsParams ¶ms, int limit); extern template bool setRequestStartIndex(Loader::GetResumeItemsParams ¶ms, int offset); + extern template void setRequestLimit(Loader::GetPublicUsersParams ¶ms, int limit); extern template bool setRequestStartIndex(Loader::GetPublicUsersParams ¶ms, int offset); + extern template void setRequestLimit(Loader::GetNextUpParams ¶ms, int limit); extern template bool setRequestStartIndex(Loader::GetNextUpParams ¶ms, int offset); + extern template void setRequestLimit(Loader::GetAlbumArtistsParams ¶ms, int limit); extern template bool setRequestStartIndex(Loader::GetAlbumArtistsParams ¶ms, int offset); +extern template void setRequestLimit(Loader::GetLiveTvChannelsParams ¶ms, int limit); +extern template bool setRequestStartIndex(Loader::GetLiveTvChannelsParams ¶ms, int offset); + extern template QList extractRecords(const QList &result); extern template int extractTotalRecordCount(const QList &result); #endif @@ -519,6 +529,7 @@ public: BaseApiModel::setLoader(newLoader); BaseApiModel::disconnectOldLoader(m_loader); m_loader = castedLoader; + reload(); } else { qWarning() << "Somehow set a BaseModelLoader on ApiModel instead of a ModelLoader"; } diff --git a/core/include/JellyfinQt/viewmodel/item.h b/core/include/JellyfinQt/viewmodel/item.h index 06426c8..2cff478 100644 --- a/core/include/JellyfinQt/viewmodel/item.h +++ b/core/include/JellyfinQt/viewmodel/item.h @@ -178,9 +178,12 @@ public: Q_PROPERTY(int albumCount READ albumCount NOTIFY albumCountChanged) Q_PROPERTY(int artistCount READ artistCount NOTIFY artistCountChanged) Q_PROPERTY(int musicVideoCount READ musicVideoCount NOTIFY musicVideoCountChanged) - Q_PROPERTY(QString mediaType READ mediaType READ mediaType NOTIFY mediaTypeChanged) + Q_PROPERTY(QString mediaType READ mediaType NOTIFY mediaTypeChanged) + Q_PROPERTY(QDateTime endDate READ endDate NOTIFY endDateChanged) + Q_PROPERTY(QDateTime startDate READ startDate NOTIFY startDateChanged) Q_PROPERTY(int width READ width NOTIFY widthChanged) Q_PROPERTY(int height READ height NOTIFY heightChanged) + Q_PROPERTY(Jellyfin::ViewModel::Item *currentProgram READ currentProgram NOTIFY currentProgramChanged) QString jellyfinId() const { return m_data->jellyfinId(); } QString name() const { return m_data->name(); } @@ -225,6 +228,9 @@ public: QStringList backdropImageTags() const { return m_data->backdropImageTags(); } QJsonObject imageBlurHashes() const { return m_data->imageBlurHashes(); } QString mediaType() const { return m_data->mediaType(); } + QDateTime endDate() const { return m_data->endDate(); } + QDateTime startDate() const { return m_data->startDate(); } + Item *currentProgram() const { return m_currentProgram; } int trailerCount() const { return m_data->trailerCount().value_or(0); } int movieCount() const { return m_data->movieCount().value_or(0); } @@ -308,8 +314,11 @@ signals: void artistCountChanged(int newArtistCount); void musicVideoCountChanged(int newMusicVideoCount); void mediaTypeChanged(const QString &newMediaType); + void endDateChanged(); + void startDateChanged(); void widthChanged(int newWidth); void heightChanged(int newHeight); + void currentProgramChanged(); protected: void setUserData(DTO::UserItemDataDto &newData); void setUserData(QSharedPointer newData); @@ -322,6 +331,7 @@ protected: QObjectList m_videoStreams; QObjectList m_subtitleStreams; QObjectList m_artistItems; + Item *m_currentProgram = nullptr; private slots: void onUserDataChanged(const DTO::UserItemDataDto &userData); }; diff --git a/core/include/JellyfinQt/viewmodel/itemmodel.h b/core/include/JellyfinQt/viewmodel/itemmodel.h index 26a6f40..5c9e9c3 100644 --- a/core/include/JellyfinQt/viewmodel/itemmodel.h +++ b/core/include/JellyfinQt/viewmodel/itemmodel.h @@ -327,6 +327,32 @@ public: FWDLISTPROP(int, years, Years); }; +using LiveTvChannelsLoaderBase = AbstractUserParameterLoader; +class LiveTvChannelsLoader : public LiveTvChannelsLoaderBase { + Q_OBJECT +public: + explicit LiveTvChannelsLoader(QObject *parent = nullptr); + + FWDPROP(Jellyfin::DTO::ChannelTypeClass::Value, type, Type) + FWDPROP(bool, isMovie, IsMovie) + FWDPROP(bool, isSeries, IsSeries) + FWDPROP(bool, isNews, IsNews) + FWDPROP(bool, isKids, IsKids) + FWDPROP(bool, isSports, IsSports) + FWDPROP(bool, isFavorite, IsFavorite) + FWDPROP(bool, isLiked, IsLiked) + FWDPROP(bool, isDisliked, IsDisliked) + FWDPROP(bool, enableImages, EnableImages) + FWDPROP(int, imageTypeLimit, ImageTypeLimit) + FWDLISTPROP(Jellyfin::DTO::ImageTypeClass::Value, enableImageTypes, EnableImageTypes) + FWDLISTPROP(Jellyfin::DTO::ItemFieldsClass::Value, fields, Fields) + FWDPROP(bool, enableUserData, EnableUserData) + FWDPROP(QStringList, sortBy, SortBy) + FWDPROP(Jellyfin::DTO::SortOrderClass::Value, sortOrder, SortOrder) + FWDPROP(bool, enableFavoriteSorting, EnableFavoriteSorting) + FWDPROP(bool, addCurrentProgram, AddCurrentProgram) +}; + /** * @brief Base class for each model that works with items. */ @@ -369,6 +395,10 @@ public: userDataLastPlayedDate, userDataPlayed, userDataKey, + currentProgramName, + currentProgramOverview, + currentProgramStartDate, + currentProgramEndDate, jellyfinExtendModelAfterHere = Qt::UserRole + 300 // Should be enough for now }; @@ -410,6 +440,10 @@ public: JFRN(userDataLastPlayedDate), JFRN(userDataPlayed), JFRN(userDataKey), + JFRN(currentProgramName), + JFRN(currentProgramOverview), + JFRN(currentProgramStartDate), + JFRN(currentProgramEndDate), }; } QVariant data(const QModelIndex &index, int role) const override; diff --git a/core/src/apimodel.cpp b/core/src/apimodel.cpp index ed6d90e..20113d9 100644 --- a/core/src/apimodel.cpp +++ b/core/src/apimodel.cpp @@ -194,6 +194,18 @@ bool setRequestStartIndex(Loader::GetAlbumArtistsParams ¶ms, int offset) { return true; } +template<> +void setRequestLimit(Loader::GetLiveTvChannelsParams ¶ms, int limit) { + params.setLimit(limit); +} + +template<> +bool setRequestStartIndex(Loader::GetLiveTvChannelsParams ¶ms, int offset) { + params.setStartIndex(offset); + return true; +} + + template<> QList extractRecords(const QList &result) { return result; diff --git a/core/src/jellyfin.cpp b/core/src/jellyfin.cpp index f637bce..ddba2c4 100644 --- a/core/src/jellyfin.cpp +++ b/core/src/jellyfin.cpp @@ -83,6 +83,7 @@ void JellyfinPlugin::registerTypes(const char *uri) { qmlRegisterType(uri, 1, 0, "NextUpLoader"); qmlRegisterType(uri, 1, 0, "PublicUsersLoader"); qmlRegisterType(uri, 1, 0, "AlbumArtistLoader"); + qmlRegisterType(uri, 1, 0, "LiveTvChannelsLoader"); // Enumerations qmlRegisterUncreatableType(uri, 1, 0, "GeneralCommandType", "Is an enum"); diff --git a/core/src/viewmodel/item.cpp b/core/src/viewmodel/item.cpp index 0a6ee4a..85a452f 100644 --- a/core/src/viewmodel/item.cpp +++ b/core/src/viewmodel/item.cpp @@ -34,9 +34,7 @@ Item::Item(QObject *parent, QSharedPointer data) : QObject(parent), m_data(data), m_userData(new UserData(this)){ - connect(m_data.data(), &Model::Item::userDataChanged, this, &Item::onUserDataChanged); - m_userData->setData(data->userData()); - updateMediaStreams(); + this->setData(data); } void Item::setData(QSharedPointer newData) { @@ -50,8 +48,18 @@ void Item::setData(QSharedPointer newData) { connect(m_data.data(), &Model::Item::userDataChanged, this, &Item::onUserDataChanged); updateMediaStreams(); setUserData(m_data->userData()); + + if (m_data->currentProgram().isNull()) { + m_currentProgram = nullptr; + } else { + QSharedPointer dataDto = m_data->currentProgram(); + QSharedPointer data = QSharedPointer::create(*dataDto.data()); + m_currentProgram = new Item(this, data); + } + emit currentProgramChanged(); } + emit userDataChanged(m_userData); } diff --git a/core/src/viewmodel/itemmodel.cpp b/core/src/viewmodel/itemmodel.cpp index f99d05e..10c648b 100644 --- a/core/src/viewmodel/itemmodel.cpp +++ b/core/src/viewmodel/itemmodel.cpp @@ -16,18 +16,19 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "JellyfinQt/viewmodel/itemmodel.h" +#include -#include "JellyfinQt/viewmodel/item.h" +#include -#include "JellyfinQt/loader/http/artists.h" -#include "JellyfinQt/loader/http/items.h" -#include "JellyfinQt/loader/http/userlibrary.h" -#include "JellyfinQt/loader/http/userviews.h" -#include "JellyfinQt/loader/http/tvshows.h" +#include +#include +#include +#include +#include +#include -#include "JellyfinQt/viewmodel/userdata.h" -#include "JellyfinQt/viewmodel/utils.h" +#include +#include #define JF_CASE(roleName) case roleName: \ try { \ @@ -64,6 +65,9 @@ NextUpLoader::NextUpLoader(QObject *parent) AlbumArtistLoader::AlbumArtistLoader(QObject *parent) : AlbumArtistLoaderBase(new Jellyfin::Loader::HTTP::GetAlbumArtistsLoader(), parent) {} +LiveTvChannelsLoader::LiveTvChannelsLoader(QObject *parent) + : LiveTvChannelsLoaderBase(new Jellyfin::Loader::HTTP::GetLiveTvChannelsLoader(), parent) {} + ItemModel::ItemModel(QObject *parent) : ApiModel(parent) { connect(this, &QAbstractItemModel::rowsInserted, this, &ItemModel::onInsertItems); @@ -126,6 +130,30 @@ QVariant ItemModel::data(const QModelIndex &index, int role) const { return QVariant(item->userData()->played()); case RoleNames::userDataKey: return QVariant(item->userData()->key()); + case RoleNames::currentProgramName: + if (item->currentProgram()) { + return QVariant(item->currentProgram()->name()); + } else { + return QVariant(); + } + case RoleNames::currentProgramOverview: + if (item->currentProgram()) { + return QVariant(item->currentProgram()->overview()); + } else { + return QVariant(); + } + case RoleNames::currentProgramStartDate: + if (item->currentProgram()) { + return QVariant(item->currentProgram()->startDate()); + } else { + return QVariant(); + } + case RoleNames::currentProgramEndDate: + if (item->currentProgram()) { + return QVariant(item->currentProgram()->endDate()); + } else { + return QVariant(); + } default: return QVariant(); } diff --git a/rpm/harbour-sailfin.changes b/rpm/harbour-sailfin.changes index b440338..1ebc622 100644 --- a/rpm/harbour-sailfin.changes +++ b/rpm/harbour-sailfin.changes @@ -12,6 +12,12 @@ # * date Author's Name version-release # - Summary of changes # +* ??? ??? ? ???? Chris Josten ?.?.?-? +- New features: + - Added a page that shows a list of Live TV channels with details about currently running + programs. +- Changes: + - TV CHannels on the main page are now sorted by channel number, instead of recently added * Tue Jan 2 2024 Chris Josten 0.5.0-1 - New features - Allow remote controlling other Jellyfin clients. See the pulley item on the main screen named diff --git a/sailfish/CMakeLists.txt b/sailfish/CMakeLists.txt index b10a161..cb24550 100644 --- a/sailfish/CMakeLists.txt +++ b/sailfish/CMakeLists.txt @@ -56,10 +56,12 @@ set(sailfin_QML_SOURCES qml/pages/itemdetails/CollectionPage.qml qml/pages/itemdetails/EpisodePage.qml qml/pages/itemdetails/FilmPage.qml - qml/pages/itemdetails/MusicAlbumPage.qml + qml/pages/itemdetails/LiveTvChannelPage.qml + qml/pages/itemdetails/LiveTvChannelsPage.qml + qml/pages/itemdetails/MusicAlbumPage.qml qml/pages/itemdetails/MusicArtistPage.qml qml/pages/itemdetails/MusicLibraryPage.qml - qml/pages/itemdetails/PhotoPage.qml + qml/pages/itemdetails/PhotoPage.qml qml/pages/itemdetails/SeasonPage.qml qml/pages/itemdetails/SeriesPage.qml qml/pages/itemdetails/UnsupportedPage.qml diff --git a/sailfish/qml/Utils.js b/sailfish/qml/Utils.js index 5716697..fbf72bd 100644 --- a/sailfish/qml/Utils.js +++ b/sailfish/qml/Utils.js @@ -115,6 +115,8 @@ function getPageUrl(mediaType, itemType, isFolder) { return Qt.resolvedUrl("pages/itemdetails/MusicAlbumPage.qml") case "photo": return Qt.resolvedUrl("pages/itemdetails/PhotoPage.qml") + case "tvchannel": + return Qt.resolvedUrl("pages/itemdetails/LiveTvChannelPage.qml") case "collectionfolder": // TODO: support for other collection folders switch(mediaType.toLowerCase()) { @@ -124,6 +126,8 @@ function getPageUrl(mediaType, itemType, isFolder) { // FALLTRHOUGH default: switch (mediaType ? mediaType.toLowerCase() : isFolder ? "folder" : "") { + case "livetv": + return Qt.resolvedUrl("pages/itemdetails/LiveTvChannelsPage.qml") case "folder": return Qt.resolvedUrl("pages/itemdetails/CollectionPage.qml") case "video": diff --git a/sailfish/qml/components/PlaybackBar.qml b/sailfish/qml/components/PlaybackBar.qml index beb1b80..0688bab 100644 --- a/sailfish/qml/components/PlaybackBar.qml +++ b/sailfish/qml/components/PlaybackBar.qml @@ -191,7 +191,7 @@ PanelBackground { left: parent.left verticalCenter: parent.verticalCenter } - height: parent + height: parent.height source: "image://theme/icon-s-device-upload" visible: controllingRemote } diff --git a/sailfish/qml/components/RemoteImage.qml b/sailfish/qml/components/RemoteImage.qml index 9db5f0a..ac2dd6d 100644 --- a/sailfish/qml/components/RemoteImage.qml +++ b/sailfish/qml/components/RemoteImage.qml @@ -106,6 +106,9 @@ SilicaItem { BusyIndicator { anchors.centerIn: parent running: realImage.status === Image.Loading + size: root.height <= Theme.fontSizeLarge + ? BusyIndicatorSize.Small + : BusyIndicatorSize.Medium } HighlightImage { diff --git a/sailfish/qml/pages/MainPage.qml b/sailfish/qml/pages/MainPage.qml index d8019e0..34f38a6 100644 --- a/sailfish/qml/pages/MainPage.qml +++ b/sailfish/qml/pages/MainPage.qml @@ -114,6 +114,12 @@ Page { apiClient: appWindow.apiClient parentId: jellyfinId } + Binding on loader { + when: model.collectionType == "livetv" + value: J.LiveTvChannelsLoader{ + apiClient: appWindow.apiClient + } + } Connections { target: mediaLibraryLoader onReady: loader.reload() diff --git a/sailfish/qml/pages/itemdetails/LiveTvChannelPage.qml b/sailfish/qml/pages/itemdetails/LiveTvChannelPage.qml new file mode 100644 index 0000000..09652a0 --- /dev/null +++ b/sailfish/qml/pages/itemdetails/LiveTvChannelPage.qml @@ -0,0 +1,23 @@ +import QtQuick 2.0 +import Sailfish.Silica 1.0 + +import "../../components" + +VideoPage { + title: itemData.currentProgram.name + subtitle: qsTr("%1 | %2 - %3") + .arg(itemData.name) + .arg(Qt.formatTime(itemData.currentProgram.startDate)) + .arg(Qt.formatTime(itemData.currentProgram.endDate)) + + SectionHeader { + text: qsTr("Program info") + } + + PlainLabel { + id: overviewText + text: itemData.currentProgram.overview || qsTr("No program info available") + font.pixelSize: Theme.fontSizeSmall + color: Theme.secondaryHighlightColor + } +} diff --git a/sailfish/qml/pages/itemdetails/LiveTvChannelsPage.qml b/sailfish/qml/pages/itemdetails/LiveTvChannelsPage.qml new file mode 100644 index 0000000..d6dfe39 --- /dev/null +++ b/sailfish/qml/pages/itemdetails/LiveTvChannelsPage.qml @@ -0,0 +1,109 @@ +import QtQuick 2.6 +import Sailfish.Silica 1.0 +import nl.netsoj.chris.Jellyfin 1.0 as J + +import "../../components" +import "../../" + +BaseDetailPage { + J.ItemModel { + id: episodeModel + loader: J.LiveTvChannelsLoader{ + apiClient: appWindow.apiClient + fields: [J.ItemFields.Overview] + autoReload: itemData.jellyfinId.length > 0 + } + } + + SilicaListView { + anchors.fill: parent + model: episodeModel + header: PageHeader { + title: itemData.name + description: itemData.seriesName + } + delegate: BackgroundItem { + height: content.height + onClicked: appWindow.navigateToItem(model.jellyfinId, model.mediaType, model.type, model.isFolder); + + Column { + id: content + anchors { + left: parent.left + leftMargin: Theme.horizontalPageMargin + right: parent.right + rightMargin: Theme.horizontalPageMargin + } + spacing: Theme.paddingSmall + topPadding: Theme.paddingLarge + bottomPadding: Theme.paddingLarge + width: parent.width + + + Row { + anchors { + left: parent.left + right: parent.right + } + spacing: Theme.paddingMedium + + RemoteImage { + id: channelLogo + width: Theme.fontSizeLarge + height: width + source: Utils.itemModelImageUrl(apiClient.baseUrl, model.jellyfinId, model.imageTags.Primary, "Primary", {"maxHeight": height}) + blurhash: model.imageBlurHashes.Primary[model.imageTags.Primary] + fillMode: Image.PreserveAspectFit + fallbackColor: "transparent" + } + + Label { + anchors.verticalCenter: channelLogo.verticalCenter + id: channelName + text: model.name + font.pixelSize: Theme.fontSizeLarge + } + } + Item { + anchors { left: parent.left; right: parent.right } + height: programName.height + Label { + id: programName + anchors { + left: parent.left + right: programTime.left + rightMargin: Theme.paddingLarge + } + text: currentProgramName + ? currentProgramName + //: Shown in the channel list when the name of the current program is unknown + : qsTr("No program information available") + truncationMode: TruncationMode.Fade + } + Label { + id: programTime + anchors.right: parent.right + text: "%1 - %2" + .arg(Qt.formatTime(currentProgramStartDate)) + .arg(Qt.formatTime(currentProgramEndDate)) + color: highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor + } + } + Label { + anchors { + left: parent.left + right: parent.right + } + visible: text + + text: currentProgramOverview + color: highlighted ? Theme.secondaryHighlightColor : Theme.secondaryColor + maximumLineCount: 2 + wrapMode: Text.WordWrap + elide: "ElideRight" + } + } + } + VerticalScrollDecorator {} + } +} diff --git a/sailfish/qml/pages/itemdetails/MusicAlbumPage.qml b/sailfish/qml/pages/itemdetails/MusicAlbumPage.qml index 5c72d1f..9b0ae2a 100644 --- a/sailfish/qml/pages/itemdetails/MusicAlbumPage.qml +++ b/sailfish/qml/pages/itemdetails/MusicAlbumPage.qml @@ -57,7 +57,6 @@ BaseDetailPage { fields: [J.ItemFields.ItemCounts, J.ItemFields.PrimaryImageAspectRatio] parentId: itemData.jellyfinId autoReload: itemData.jellyfinId.length > 0 - //onParentIdChanged: if (parentId.length > 0) reload() } } RowLayout { diff --git a/sailfish/qml/pages/itemdetails/SeasonPage.qml b/sailfish/qml/pages/itemdetails/SeasonPage.qml index cf1e604..c26d341 100644 --- a/sailfish/qml/pages/itemdetails/SeasonPage.qml +++ b/sailfish/qml/pages/itemdetails/SeasonPage.qml @@ -37,11 +37,6 @@ BaseDetailPage { } } - Connections { - target: itemData - onReady: episodeModel.reload() - } - SilicaListView { anchors.fill: parent contentHeight: content.height @@ -149,19 +144,4 @@ BaseDetailPage { VerticalScrollDecorator {} } - Connections { - target: itemData - onStatusChanged: { - if (itemData.status == JellyfinItem.Ready) { - episodeModel.reload() - } - } - } - onStatusChanged: { - if (status == PageStatus.Active) { - //console.log(JSON.stringify(itemData)) - //episodeModel.show = itemData.seriesId - //episodeModel.seasonId = itemData.jellyfinId - } - } } diff --git a/sailfish/qml/pages/itemdetails/SeriesPage.qml b/sailfish/qml/pages/itemdetails/SeriesPage.qml index b6dc7ec..c2137c2 100644 --- a/sailfish/qml/pages/itemdetails/SeriesPage.qml +++ b/sailfish/qml/pages/itemdetails/SeriesPage.qml @@ -72,10 +72,6 @@ BaseDetailPage { autoReload: itemData.jellyfinId.length > 0 } } - Connections { - target: itemData - onReady: showSeasonsModel.reload() - } SilicaListView { model: showSeasonsModel @@ -96,17 +92,4 @@ BaseDetailPage { } } - - /*onStatusChanged: { - if (status == PageStatus.Active) { - showSeasonsModel.reload() - } - }*/ - Connections { - target: itemData - onJellyfinIdChanged: { - console.log("Item id changed") - //showSeasonsModel.show = itemData.jellyfinId - } - } } diff --git a/sailfish/qml/pages/itemdetails/VideoPage.qml b/sailfish/qml/pages/itemdetails/VideoPage.qml index ae5bf66..48229f0 100644 --- a/sailfish/qml/pages/itemdetails/VideoPage.qml +++ b/sailfish/qml/pages/itemdetails/VideoPage.qml @@ -31,6 +31,7 @@ import "../.." */ BaseDetailPage { id: detailPage + property alias title: pageHeader.title property alias subtitle: pageHeader.description default property alias _data: content.data property real _playbackProsition: itemData.userData.playbackPositionTicks diff --git a/sailfish/translations/harbour-sailfin-de.ts b/sailfish/translations/harbour-sailfin-de.ts index ff4ad46..9a6a661 100644 --- a/sailfish/translations/harbour-sailfin-de.ts +++ b/sailfish/translations/harbour-sailfin-de.ts @@ -241,6 +241,29 @@ + + LiveTvChannelPage + + %1 | %2 - %3 + + + + Program info + + + + No program info available + + + + + LiveTvChannelsPage + + No program information available + Shown in the channel list when the name of the current program is unknown + + + LoginDialog diff --git a/sailfish/translations/harbour-sailfin-ru.ts b/sailfish/translations/harbour-sailfin-ru.ts index fecb903..d20b6e2 100644 --- a/sailfish/translations/harbour-sailfin-ru.ts +++ b/sailfish/translations/harbour-sailfin-ru.ts @@ -223,6 +223,29 @@ + + LiveTvChannelPage + + %1 | %2 - %3 + + + + Program info + + + + No program info available + + + + + LiveTvChannelsPage + + No program information available + Shown in the channel list when the name of the current program is unknown + + + LoginDialog diff --git a/sailfish/translations/harbour-sailfin.ts b/sailfish/translations/harbour-sailfin.ts index be39f8f..298618a 100644 --- a/sailfish/translations/harbour-sailfin.ts +++ b/sailfish/translations/harbour-sailfin.ts @@ -219,6 +219,29 @@ + + LiveTvChannelPage + + %1 | %2 - %3 + + + + Program info + + + + No program info available + + + + + LiveTvChannelsPage + + No program information available + Shown in the channel list when the name of the current program is unknown + + + LoginDialog