Warning, /multimedia/audiotube/src/contents/ui/main.qml is written in an unsupported language. File is not indexed.
0001 // SPDX-FileCopyrightText: 2021 Jonah BrĂ¼chert <jbb@kaidan.im> 0002 // 0003 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0004 0005 import QtQuick 2.1 0006 import QtQuick.Controls 2.12 as Controls 0007 import QtQuick.Layouts 1.3 0008 import org.kde.kirigami as Kirigami 0009 import QtMultimedia 0010 0011 import org.kde.ytmusic 1.0 0012 0013 import org.nemomobile.qtmpris 1.0 0014 0015 Kirigami.ApplicationWindow { 0016 id: root 0017 minimumWidth: 300 0018 minimumHeight: 300 0019 pageStack.globalToolBar.style: wideScreen? Kirigami.ApplicationHeaderStyle.None: Kirigami.ApplicationHeaderStyle.Breadcrumb 0020 0021 color: "transparent" 0022 Blur{id:blur} 0023 Component.onCompleted: { 0024 blur.setBlur(sidebar, true); 0025 } 0026 0027 pageStack.columnView.columnResizeMode: Kirigami.ColumnView.SingleColumn 0028 0029 property alias searchField: searchLoader // TODO 0030 property bool wideScreen: width >= 600 0031 property bool showSearch: false // only applicable if not widescreen 0032 0033 // so that there is still a separator, since the header style is none 0034 Kirigami.Separator { 0035 anchors.top: parent.top 0036 anchors.left: parent.left 0037 anchors.right: parent.right 0038 visible: wideScreen 0039 } 0040 0041 Kirigami.PagePool { 0042 id: pool 0043 } 0044 0045 Loader { 0046 id: shareMenu 0047 active: false 0048 sourceComponent: ShareMenu {} 0049 } 0050 0051 function openShareMenu(inputTitle: string, url: string) { 0052 shareMenu.active = true 0053 shareMenu.item.inputTitle = inputTitle 0054 shareMenu.item.url = url 0055 shareMenu.item.open() 0056 } 0057 0058 Sidebar { 0059 id:sidebar 0060 visible: wideScreen 0061 height: pageStack.height - playerFooter.implicitHeight 0062 0063 } 0064 NavigationBar{ 0065 id: bottombar 0066 visible: !wideScreen 0067 } 0068 0069 header: Controls.Control { 0070 visible: wideScreen || root.showSearch 0071 padding: Kirigami.Units.largeSpacing 0072 0073 background: Rectangle { 0074 width: parent.width 0075 height: parent.height 0076 anchors.fill: parent 0077 Kirigami.Theme.inherit: false 0078 Kirigami.Theme.colorSet: Kirigami.Theme.Header 0079 color: Kirigami.Theme.backgroundColor 0080 } 0081 0082 contentItem: RowLayout { 0083 spacing: 0 0084 Row { 0085 Layout.alignment: Qt.AlignLeft 0086 0087 Controls.ToolButton { 0088 id: back 0089 enabled: applicationWindow().pageStack.layers.depth > 1 || (applicationWindow().pageStack.depth > 1 && (applicationWindow().pageStack.currentIndex > 0 || applicationWindow().pageStack.contentItem.contentX > 0)) // Copied from https://invent.kde.org/frameworks/kirigami/-/blob/master/src/controls/templates/private/BackButton.qml#L16 0090 icon.name: "draw-arrow-back" 0091 onClicked: pageStack.goBack() 0092 visible: wideScreen 0093 } 0094 Controls.ToolButton { 0095 icon.name: "draw-arrow-forward" 0096 enabled: applicationWindow().pageStack.currentIndex < applicationWindow().pageStack.depth-1 0097 onClicked: pageStack.goForward() 0098 visible: wideScreen 0099 } 0100 } 0101 0102 // spacer 0103 Item { 0104 visible: wideScreen 0105 Layout.fillWidth: !root.wideScreen 0106 } 0107 SearchWithDropdown { 0108 id: searchLoader 0109 visible: root.wideScreen || root.showSearch 0110 Layout.alignment: Qt.AlignHCenter 0111 Layout.fillWidth: true 0112 Layout.fillHeight: true 0113 height: back.height 0114 width: wideScreen ? null : root.width 0115 Layout.maximumWidth: wideScreen ? 400 : root.width 0116 } 0117 0118 Item { 0119 width: 2*back.width 0120 visible: wideScreen 0121 0122 } 0123 } 0124 } 0125 0126 title: i18n("AudioTube") 0127 0128 contextDrawer: Kirigami.ContextDrawer { 0129 id: contextDrawer 0130 } 0131 0132 pageStack.initialPage: pool.loadPage(Qt.resolvedUrl("qrc:/LibraryPage.qml")) 0133 pageStack.clip: true 0134 0135 function play(videoId) { 0136 UserPlaylistModel.initialVideoId = videoId 0137 } 0138 0139 function playPlaylist(playlistId) { 0140 UserPlaylistModel.playlistId = playlistId 0141 } 0142 0143 function playShufflePlaylist(playlistId) { 0144 UserPlaylistModel.shuffle = true 0145 UserPlaylistModel.playlistId = playlistId 0146 } 0147 0148 function playFavourites(shuffle) { 0149 UserPlaylistModel.playFavourites(Library.favourites, shuffle) 0150 } 0151 0152 function playPlaybackHistory(model, shuffle) { 0153 UserPlaylistModel.playPlaybackHistory(model, shuffle) 0154 } 0155 0156 function focusSearch(){searchLoader.forceFocus()} 0157 0158 Connections { 0159 target: ErrorHandler 0160 0161 function onErrorOccurred(error) { 0162 showPassiveNotification(error) 0163 } 0164 } 0165 0166 Component { 0167 id: searchAlbum 0168 0169 ColumnLayout { 0170 id: mpdelegateItem 0171 0172 required property string videoId 0173 required property string title 0174 0175 width: 90 0176 Layout.maximumWidth: 70 0177 0178 Kirigami.ShadowedRectangle { 0179 id: recCover 0180 MouseArea { 0181 id: recArea 0182 anchors.fill: parent 0183 onClicked: play(mpdelegateItem.videoId) 0184 hoverEnabled: !Kirigami.Settings.hasTransientTouchInput 0185 onEntered: { 0186 if (!Kirigami.Settings.hasTransientTouchInput) { 0187 recSelected.visible = true 0188 searchTitle.color = Kirigami.Theme.hoverColor 0189 searchTitle.font.bold = true 0190 } 0191 } 0192 onExited: { 0193 recSelected.visible = false 0194 searchTitle.color = Kirigami.Theme.textColor 0195 searchTitle.font.bold = false 0196 } 0197 } 0198 Layout.margins: 5 0199 0200 width: 70 0201 height: 70 0202 radius: 10 0203 shadow.size: 15 0204 shadow.xOffset: 5 0205 shadow.yOffset: 5 0206 shadow.color: Qt.rgba(0, 0, 0, 0.2) 0207 Rectangle { 0208 width: 70 0209 height: 70 0210 0211 color: "transparent" 0212 0213 ThumbnailSource { 0214 id: thumbnailSource 0215 videoId: mpdelegateItem.videoId 0216 } 0217 RoundedImage { 0218 source: thumbnailSource.cachedPath 0219 height: parent.height 0220 width: height 0221 radius: 10 0222 } 0223 Rectangle { 0224 id: recSelected 0225 0226 Rectangle { 0227 anchors.fill: parent 0228 color: Kirigami.Theme.hoverColor 0229 radius: 10 0230 opacity: 0.2 0231 } 0232 0233 visible: false 0234 anchors.fill: parent 0235 0236 radius: 9 0237 0238 border.color: Kirigami.Theme.hoverColor 0239 border.width: 2 0240 color: "transparent" 0241 } 0242 } 0243 } 0244 Controls.Label { 0245 id: searchTitle 0246 leftPadding:5 0247 Layout.maximumWidth: 70 0248 text: mpdelegateItem.title 0249 elide: Qt.ElideRight 0250 wrapMode: Text.WordWrap 0251 Layout.maximumHeight: 40 0252 } 0253 Item { 0254 height: 5 0255 } 0256 } 0257 } 0258 0259 0260 pageStack.anchors.bottomMargin: wideScreen ? playerFooter.minimizedPlayerHeight: playerFooter.minimizedPlayerHeight+bottombar.height 0261 pageStack.anchors.leftMargin: wideScreen ? sidebar.width:0 0262 0263 // media player 0264 PlayerFooter { 0265 id: playerFooter 0266 anchors.topMargin: -root.header.height 0267 anchors.fill: parent 0268 footerSpacing: wideScreen ? 0 : bottombar.height 0269 0270 // only expand flicking area to full screen when it is open 0271 z: (contentY === 0) ? -1 : 999 0272 } 0273 0274 MprisPlayer { 0275 id: mprisPlayer 0276 serviceName: "AudioTube" 0277 0278 property string artist: playerFooter.videoInfoExtractor.artist 0279 property string songTitle: playerFooter.videoInfoExtractor.title ? playerFooter.videoInfoExtractor.title : i18n("No song playing") 0280 property int songLength: playerFooter.audioPlayer.duration * 1000 0281 property alias thumbnail: playerFooter.thumbnail 0282 0283 function next() { 0284 if(UserPlaylistModel.canSkip) { 0285 UserPlaylistModel.next() 0286 } 0287 else{ 0288 playerFooter.audioPlayer.stop() 0289 } 0290 } 0291 0292 //Mpris2 Root interface 0293 identity: root.title 0294 supportedUriSchemes: [] 0295 supportedMimeTypes: [] 0296 desktopEntry: "org.kde.audiotube" 0297 0298 //Mpris2 Player Interface 0299 canControl: true 0300 canGoNext: UserPlaylistModel.canSkip 0301 canGoPrevious: UserPlaylistModel.canSkipBack 0302 canPause: playerFooter.audioPlayer.status !== MediaPlayer.NoMedia 0303 canPlay: playerFooter.audioPlayer.status !== MediaPlayer.NoMedia 0304 canSeek: false 0305 0306 playbackStatus: playerFooter.audioPlayer.playbackState === MediaPlayer.PlayingState ? Mpris.Playing : (playerFooter.audioPlayer.playbackState == MediaPlayer.PausedState ? Mpris.Paused : Mpris.Stopped) 0307 shuffle: false 0308 volume: playerFooter.audioOutput.muted ? 0.0 : playerFooter.audioOutput.volume 0309 position: playerFooter.audioPlayer.position * 1000 0310 0311 onPauseRequested: playerFooter.audioPlayer.pause() 0312 onPlayRequested: playerFooter.audioPlayer.play() 0313 onPlayPauseRequested: { 0314 if(playerFooter.audioPlayer.playbackStatus === MediaPlayer.PlayingState) { 0315 playerFooter.audioPlayer.pause() 0316 } 0317 else if(playerFooter.PlayingState === MediaPlayer.PausedState) { 0318 PlayerFooter.audioPlayer.play() 0319 } 0320 } 0321 onStopRequested: playerFooter.audioPlayer.stop() 0322 onNextRequested: { 0323 next() 0324 } 0325 onPreviousRequested: { 0326 if(UserPlaylistModel.canSkipBack) { 0327 UserPlaylistModel.previous() 0328 } 0329 else{ 0330 playerFooter.audioPlayer.stop() 0331 } 0332 } 0333 onSeekRequested: { 0334 if(canSeek) { 0335 if(playerFooter.audioPlayer.position + offset/1000 < 0) { 0336 playerFooter.audioPlayer.seek(0) 0337 } 0338 else if(playerFooter.audioPlayer.position + offset/1000 > playerFooter.audioPlayer.duration) { 0339 next() 0340 } 0341 else { 0342 playerFooter.audioPlayer.seek(Math.floor(playerFooter.audioPlayer.position + offset/1000)); 0343 } 0344 emitSeeked() 0345 } 0346 } 0347 onSetPositionRequested: { 0348 if(canSeek) { 0349 if(position >= 0 && position/1000 <= playerFooter.audioPlayer.duration) { 0350 playerFooter.audioPlayer.seek(Math.floor(position/1000)) 0351 emitSeeked() 0352 } 0353 } 0354 } 0355 onShuffleRequested: { 0356 UserPlaylistModel.shufflePlaylist() 0357 } 0358 0359 onArtistChanged: { 0360 var metadata = mprisPlayer.metadata 0361 metadata[Mpris.metadataToString(Mpris.Artist)] = [artist] 0362 mprisPlayer.metadata = metadata 0363 } 0364 onSongTitleChanged: { 0365 var metadata = mprisPlayer.metadata 0366 metadata[Mpris.metadataToString(Mpris.Title)] = songTitle 0367 mprisPlayer.metadata = metadata 0368 } 0369 onSongLengthChanged: { 0370 var metadata = mprisPlayer.metadata 0371 metadata[Mpris.metadataToString(Mpris.Length)] = songLength 0372 mprisPlayer.metadata = metadata 0373 } 0374 onThumbnailChanged: { 0375 var metadata = mprisPlayer.metadata 0376 metadata[Mpris.metadataToString(Mpris.ArtUrl)] = thumbnail 0377 mprisPlayer.metadata = metadata 0378 } 0379 } 0380 }