diff --git a/main.qml b/main.qml index 822832a..be5e548 100644 --- a/main.qml +++ b/main.qml @@ -1,855 +1,885 @@ import QtQuick 2.10 import QtQuick.Controls 2.10 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 import QtQuick.Controls.Material 2.1 import "utils" import "widgets" import "widgets/PlaylistsView" import "widgets/MainPlaylist" import "widgets/SettingsView" import "widgets/SearchView" //import "widgets/CloudView" import "view_models" import "view_models/BabeTable" import "services/local" import "services/web" //import "services/web/Spotify" import "view_models/BabeGrid" import "widgets/InfoView" import "db/Queries.js" as Q import "utils/Help.js" as H import "utils/Player.js" as Player import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import Player 1.0 import AlbumsList 1.0 import TracksList 1.0 import PlaylistsList 1.0 Maui.ApplicationWindow { id: root title: qsTr("vvave") /***************************************************/ /******************** ALIASES ********************/ /*************************************************/ property alias mainPlaylist: mainPlaylist property alias selectionBar: _selectionBar property alias progressBar: progressBar property alias dialog : _dialogLoader.item Maui.App.iconName: "qrc:/assets/vvave.svg" Maui.App.description: qsTr("VVAVE will handle your whole music collection by retreaving semantic information from the web. Just relax, enjoy and discover your new music ") /***************************************************/ /******************** PLAYBACK ********************/ /*************************************************/ property bool isShuffle: Maui.FM.loadSettings("SHUFFLE","PLAYBACK", false) - property var currentTrack: ({ - fav: "0", - stars: "0" - }) + readonly property var currentTrack: mainlistEmpty? ({url: "", artwork: "", fav: "0", stars: "0"}) : mainPlaylist.table.listModel.get(currentTrackIndex) property int currentTrackIndex: -1 property int prevTrackIndex: 0 - property string currentArtwork: !mainlistEmpty ? mainPlaylist.listModel.get(0).artwork : "" - property bool currentBabe: currentTrack.fav == "0" ? false : true + readonly property string currentArtwork: currentTrack.artwork + readonly property bool currentBabe: currentTrack.fav == "0" ? false : true property alias durationTimeLabel: player.duration property string progressTimeLabel: player.transformTime((player.duration/1000) *(player.pos/ 1000)) property alias isPlaying: player.playing property int onQueue: 0 property bool mainlistEmpty: !mainPlaylist.table.count > 0 /***************************************************/ /******************** HANDLERS ********************/ /*************************************************/ readonly property var viewsIndex: ({ tracks: 0, albums: 1, artists: 2, playlists: 3, folders: 4, - cloud : 5, - youtube: 6, - search: 7}) + youtube: 5, + search: 6}) property string syncPlaylist: "" property bool sync: false property bool focusView : false property bool selectionMode : false /***************************************************/ /******************** UI COLORS *******************/ /*************************************************/ readonly property color babeColor: "#f84172" /*SIGNALS*/ signal missingAlert(var track) /*HANDLE EVENTS*/ onClosing: Player.savePlaylist() onMissingAlert: { var message = qsTr("Missing file...") var messageBody = track.title + " by " + track.artist + " is missing.\nDo you want to remove it from your collection?" notify("dialog-question", message, messageBody, function () { mainPlaylist.list.remove(mainPlaylist.table.currentIndex) }) } /*COMPONENTS*/ Player { id: player volume: 100 onFinishedChanged: if (!mainlistEmpty) { if (currentTrack.url) mainPlaylist.list.countUp(currentTrackIndex) Player.nextTrack() } } FloatingDisk { id: _floatingDisk opacity: 1 - _drawer.position } headBar.middleContent : Maui.ActionGroup { id: _actionGroup -// Layout.fillWidth: true + // Layout.fillWidth: true Layout.fillHeight: true Layout.minimumWidth: implicitWidth currentIndex : swipeView.currentIndex onCurrentIndexChanged: swipeView.currentIndex = currentIndex hiddenActions: [ Action { text: qsTr("Folders") icon.name: "folder" }, -// Action -// { -// text: qsTr("Cloud") -// icon.name: "folder-cloud" -// }, + // Action + // { + // text: qsTr("Cloud") + // icon.name: "folder-cloud" + // }, Action { text: qsTr("YouTube") icon.name: "internet-services" } ] Action { icon.name: "view-media-track" text: qsTr("Tracks") } Action { text: qsTr("Albums") icon.name: /*"album"*/ "view-media-album-cover" } Action { text: qsTr("Artists") icon.name: "view-media-artist" } Action { text: qsTr("Playlists") icon.name: "view-media-playlist" } } onSearchButtonClicked: { _actionGroup.currentIndex = viewsIndex.search // searchView.searchInput.forceActiveFocus() } Loader { id: _dialogLoader } InfoView { id: infoView maxWidth: parent.width * 0.8 maxHeight: parent.height * 0.9 } Loader { id: _focusViewLoader active: focusView source: "widgets/FocusView.qml" } Component { id: _shareDialogComponent Maui.ShareDialog {} } Component { id: _fmDialogComponent Maui.FileDialog { } } SourcesDialog { id: sourcesDialog } mainMenu: [ // Maui.MenuItem // { // text: "Vvave Stream" // icon.name: "headphones" // onTriggered: // { // pageStack.currentIndex = 1 // currentView = viewsIndex.vvave // } // }, // Maui.MenuItem // { // text: qsTr("Linking") // icon.name: "view-links" // onTriggered: // { // pageStack.currentIndex = 1 // currentView = viewsIndex.linking // if(!isLinked) linkingView.linkingConf.open() // } // }, // Maui.MenuItem // { // text: qsTr("Cloud") // icon.name: "folder-cloud" // onTriggered: // { // pageStack.currentIndex = 1 // currentView = viewsIndex.cloud // } // }, // Maui.MenuItem // { // text: qsTr("Spotify") // icon.name: "internet-services" // onTriggered: // { // pageStack.currentIndex = 1 // currentView = viewsIndex.spotify // } // }, MenuSeparator{}, MenuItem { text: qsTr("Sources") icon.name: "folder-add" onTriggered: sourcesDialog.open() }, MenuItem { text: qsTr("Open") icon.name: "folder-add" onTriggered: { _dialogLoader.sourceComponent = _fmDialogComponent root.dialog.settings.onlyDirs = false root.dialog.currentPath = "file:///home/camilo/Music" root.dialog.settings.filterType = Maui.FMList.AUDIO console.log("SETTIGN FILTER TYPE FISR", root.dialog.settings.filterType, Maui.FMList.AUDIO) root.dialog.show(function(paths) { vvave.openUrls(paths) root.dialog.close() }) } }/*, Menu { title: qsTr("Collection") // icon.name: "settings-configure" MenuItem { text: qsTr("Re-Scan") onTriggered: bae.refreshCollection(); } MenuItem { text: qsTr("Refresh...") onTriggered: H.refreshCollection(); } MenuItem { text: qsTr("Clean") onTriggered: bae.removeMissingTracks(); } }*/ // Maui.Menu // { // title: qsTr("Settings...") // // Kirigami.Action // // { // // text: "Brainz" // // Kirigami.Action // // { // // id: brainzToggle // // text: checked ? "Turn OFF" : "Turn ON" // // checked: bae.brainzState() // // checkable: true // // onToggled: // // { // // checked = !checked // // bae.saveSetting("AUTO", checked, "BRAINZ") // //// bae.brainz(checked) // // } // // } // // } // Maui.MenuItem // { // text: "Info label" + checked ? "ON" : "OFF" // checked: infoLabels // checkable: true // onToggled: // { // infoLabels = checked // bae.saveSetting("LABELS", infoLabels ? true : false, "PLAYBACK") // } // } // Maui.MenuItem // { // text: "Autoplay" // checked: autoplay // checkable: true // onToggled: // { // autoplay = checked // bae.saveSetting("AUTOPLAY", autoplay ? true : false, "BABE") // } // } // } ] Playlists { id: playlistsList } PlaylistDialog { id: playlistDialog } sideBar: Maui.AbstractSideBar { id: _drawer focus: true width: visible ? Math.min(Kirigami.Units.gridUnit * (Kirigami.Settings.isMobile? 18 : 15), root.width) : 0 modal: !isWide closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent height: _drawer.modal ? implicitHeight - _mainPage.footer.height : implicitHeight MainPlaylist { id: mainPlaylist anchors.fill: parent Connections { target: mainPlaylist onCoverPressed: Player.appendAll(tracks) onCoverDoubleClicked: Player.playAll(tracks) } } } Maui.Page { id: _mainPage anchors.fill: parent footer: ColumnLayout { id: _footerLayout visible: !mainlistEmpty || isPlaying height: visible ? Maui.Style.toolBarHeight * 1.2 : 0 width: parent.width spacing: 0 Kirigami.Separator { Layout.fillWidth: true } Slider { id: progressBar Layout.preferredHeight: Maui.Style.unit * (Kirigami.Settings.isMobile ? 6 : 8) Layout.fillWidth: true padding: 0 from: 0 to: 1000 value: player.pos spacing: 0 focus: true onMoved: { player.pos = value } background: Rectangle { implicitWidth: progressBar.width implicitHeight: progressBar.height width: progressBar.availableWidth height: implicitHeight color: "transparent" Rectangle { width: progressBar.visualPosition * parent.width height: progressBar.height color: Kirigami.Theme.highlightColor } } handle: Rectangle { x: progressBar.leftPadding + progressBar.visualPosition * (progressBar.availableWidth - width) y: -(progressBar.height * 0.8) implicitWidth: progressBar.pressed ? Maui.Style.iconSizes.medium : 0 implicitHeight: progressBar.pressed ? Maui.Style.iconSizes.medium : 0 radius: progressBar.pressed ? Maui.Style.iconSizes.medium : 0 color: Kirigami.Theme.highlightColor } } Maui.ToolBar { Layout.fillHeight: true Layout.fillWidth: true position: ToolBar.Footer + rightContent: ToolButton + { + icon.name: "media-speaker" + onClicked: _sliderPopup.open() + + Popup + { + id: _sliderPopup + height: 150 + width: parent.width + y: -150 + x: 0 + // closePolicy: Popup.CloseOnEscape | Popup.CloseOnPress + Slider + { + id: _volumeSlider + visible: true + height: parent.height + width: 20 + anchors.horizontalCenter: parent.horizontalCenter + from: 0 + to: 100 + value: player.volume + orientation: Qt.Vertical + + onMoved: + { + player.volume = value + } + } + } + + } + middleContent: [ ToolButton { id: babeBtnIcon icon.name: "love" enabled: currentTrackIndex >= 0 icon.color: currentBabe ? babeColor : Kirigami.Theme.textColor onClicked: if (!mainlistEmpty) { mainPlaylist.list.fav(currentTrackIndex, !(mainPlaylist.listModel.get(currentTrackIndex).fav == "1")) currentBabe = mainPlaylist.listModel.get(currentTrackIndex).fav == "1" } }, ToolButton { icon.name: "media-skip-backward" icon.color: Kirigami.Theme.textColor onClicked: Player.previousTrack() onPressAndHold: Player.playAt(prevTrackIndex) }, ToolButton { id: playIcon enabled: currentTrackIndex >= 0 icon.color: Kirigami.Theme.textColor icon.name: isPlaying ? "media-playback-pause" : "media-playback-start" onClicked: player.playing = !player.playing }, ToolButton { id: nextBtn icon.color: Kirigami.Theme.textColor icon.name: "media-skip-forward" onClicked: Player.nextTrack() onPressAndHold: Player.playAt(Player.shuffle()) }, ToolButton { id: shuffleBtn icon.color: babeColor icon.name: isShuffle ? "media-playlist-shuffle" : "media-playlist-normal" onClicked: { isShuffle = !isShuffle Maui.FM.saveSettings("SHUFFLE", isShuffle, "PLAYBACK") } } ] } } ColumnLayout { anchors.fill: parent SwipeView { id: swipeView Layout.fillHeight: true Layout.fillWidth: true // interactive: Kirigami.Settings.isMobile currentIndex: _actionGroup.currentIndex onCurrentIndexChanged: _actionGroup.currentIndex = currentIndex clip: true onCurrentItemChanged: currentItem.forceActiveFocus() TracksView { id: tracksView Connections { target: vvave onRefreshTables: tracksView.list.refresh() } Connections { target: tracksView onRowClicked: Player.quickPlay(tracksView.listModel.get(index)) onQuickPlayTrack: Player.quickPlay(tracksView.listModel.get(index)) onAppendTrack: Player.addTrack(tracksView.listModel.get(index)) onPlayAll: Player.playAll( tracksView.listModel.getAll()) onAppendAll: Player.appendAll( tracksView.listModel.getAll()) onQueueTrack: Player.queueTracks([tracksView.listModel.get(index)], index) } } Loader { active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem || item sourceComponent: AlbumsView { id: albumsView holder.emoji: "qrc:/assets/MusicBox.png" holder.isMask: false holder.title : "No Albums!" holder.body: "Add new music sources" holder.emojiSize: Maui.Style.iconSizes.huge title: count + qsTr(" albums") list.query: Albums.ALBUMS list.sortBy: Albums.ALBUM Connections { target: vvave onRefreshTables: albumsView.list.refresh() } Connections { target: albumsView onRowClicked: Player.quickPlay(track) onAppendTrack: Player.addTrack(track) onPlayTrack: Player.quickPlay(track) onAlbumCoverClicked: albumsView.populateTable(album, artist) onAlbumCoverPressedAndHold: { var query = Q.GET.albumTracks_.arg(album) query = query.arg(artist) mainPlaylist.list.clear() mainPlaylist.list.sortBy = Tracks.NONE mainPlaylist.list.query = query Player.playAt(0) } onPlayAll: Player.playAll(albumsView.listModel.getAll()) onAppendAll: Player.appendAll(albumsView.listModel.getAll()) } } } Loader { active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem || item sourceComponent: AlbumsView { id: artistsView holder.emoji: "qrc:/assets/MusicBox.png" holder.isMask: false holder.title : qsTr("No Artists!") holder.body: qsTr("Add new music sources") holder.emojiSize: Maui.Style.iconSizes.huge title: count + qsTr(" artists") list.query: Albums.ARTISTS list.sortBy: Albums.ARTIST table.list.sortBy: Tracks.NONE Connections { target: vvave onRefreshTables: artistsView.list.refresh() } Connections { target: artistsView onRowClicked: Player.quickPlay(track) onAppendTrack: Player.addTrack(track) onPlayTrack: Player.quickPlay(track) onAlbumCoverClicked: artistsView.populateTable(undefined, artist) onAlbumCoverPressedAndHold: { var query = Q.GET.artistTracks_.arg(artist) mainPlaylist.list.clear() mainPlaylist.list.sortBy = Tracks.NONE mainPlaylist.list.query = query Player.playAt(0) } onPlayAll: Player.artistsView(albumsView.listModel.getAll()) onAppendAll: Player.artistsView(albumsView.listModel.getAll()) } } } Loader { active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem || item sourceComponent: PlaylistsView { id: playlistsView Connections { target: playlistsView onRowClicked: Player.quickPlay(track) onAppendTrack: Player.addTrack(track) onPlayTrack: Player.quickPlay(track) onAppendAll: Player.appendAll(playlistsView.listModel.getAll()) onSyncAndPlay: { Player.playAll(playlistsView.listModel.getAll()) root.sync = true root.syncPlaylist = playlist } onPlayAll: Player.playAll(playlistsView.listModel.getAll()) } } } Loader { active: SwipeView.isCurrentItem || item sourceComponent: FoldersView { id: foldersView Connections { target: vvave onRefreshTables: foldersView.populate() } Connections { target: foldersView.list onRowClicked: Player.quickPlay(foldersView.list.model.get(index)) onQuickPlayTrack: Player.quickPlay(foldersView.list.model.get(index)) onAppendTrack: Player.addTrack(foldersView.listModel.get(index)) onPlayAll: Player.playAll(foldersView.listModel.getAll()) onAppendAll: Player.appendAll(foldersView.listModel.getAll()) onQueueTrack: Player.queueTracks([foldersView.list.model.get(index)], index) } } } -// Loader -// { -// active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem || item -// sourceComponent: CloudView -// { -// id: cloudView -// } -// } + // Loader + // { + // active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem || item + // sourceComponent: CloudView + // { + // id: cloudView + // } + // } Loader { active: SwipeView.isCurrentItem || item sourceComponent: YouTube { id: youtubeView } } Loader { active: SwipeView.isCurrentItem || (item && item.listView.count > 0) sourceComponent: SearchTable { id: searchView Connections { target: searchView onRowClicked: Player.quickPlay(searchView.listModel.get(index)) onQuickPlayTrack: Player.quickPlay(searchView.listModel.get(index)) onAppendTrack: Player.addTrack(searchView.listModel.get(index)) onPlayAll: Player.playAll(searchView.listModel.getAll()) onAppendAll: Player.appendAll(searchView.listModel.getAll()) onArtworkDoubleClicked: { var query = Q.GET.albumTracks_.arg( searchView.listModel.get( index).album) query = query.arg(searchView.listModel.get(index).artist) mainPlaylist.list.clear() mainPlaylist.list.sortBy = Tracks.NONE mainPlaylist.list.query = query Player.playAt(0) } } } } } SelectionBar { id: _selectionBar property alias listView: _selectionBar.selectionList Layout.maximumWidth: 500 Layout.minimumWidth: 100 Layout.fillWidth: true Layout.alignment: Qt.AlignCenter Layout.margins: Maui.Style.space.big Layout.topMargin: Maui.Style.space.small Layout.bottomMargin: Maui.Style.space.big onExitClicked: { root.selectionMode = false clear() } } } } /*CONNECTIONS*/ Connections { target: vvave onRefreshTables: { if(size>0) root.notify("emblem-info", "Collection updated", size+" new tracks added...") } // onRefreshTracks: H.refreshTracks() // onRefreshAlbums: H.refreshAlbums() // onRefreshArtists: H.refreshArtists() // onCoverReady: // { // root.currentArtwork = path // currentTrack.artwork = currentArtwork // mainPlaylist.list.update(currentTrack, currentTrackIndex); // } // onTrackLyricsReady: // { // console.log(lyrics) // if (url === currentTrack.url) // Player.setLyrics(lyrics) // } // onSkipTrack: Player.nextTrack() // onBabeIt: if (!mainlistEmpty) // { // mainPlaylist.list.fav(currentTrackIndex, !(mainPlaylist.list.get(currentTrackIndex).fav == "1")) // currentBabe = mainPlaylist.list.get(currentTrackIndex).fav == "1" // } onOpenFiles: { Player.appendTracksAt(tracks, 0) Player.playAt(0) } } Component.onCompleted: { if(isAndroid) { Maui.Android.statusbarColor(Kirigami.Theme.backgroundColor, true) Maui.Android.navBarColor(Kirigami.Theme.backgroundColor, true) } } } diff --git a/services/local/player.cpp b/services/local/player.cpp index bdc565e..8331df5 100644 --- a/services/local/player.cpp +++ b/services/local/player.cpp @@ -1,210 +1,211 @@ #include "player.h" #include "../../utils/bae.h" -#ifdef STATIC_MAUIKIT -#include "mauiaccounts.h" -#else -#include -#endif +//#ifdef STATIC_MAUIKIT +//#include "mauiaccounts.h" +//#else +//#include +//#endif Player::Player(QObject *parent) : QObject(parent), player(new QMediaPlayer(this)), updater(new QTimer(this)) { this->player->setVolume(this->volume); connect(this->updater, &QTimer::timeout, this, &Player::update); } inline QNetworkRequest getOcsRequest(const QNetworkRequest& request) { qDebug() << Q_FUNC_INFO; qDebug()<< "FORMING THE REQUEST" << request.url(); // Read raw headers out of the provided request QMap rawHeaders; for (const QByteArray& headerKey : request.rawHeaderList()) { rawHeaders.insert(headerKey, request.rawHeader(headerKey)); } - const auto account = FMH::toModel(MauiAccounts::instance()->getCurrentAccount()); +// const auto account = FMH::toModel(MauiAccounts::instance()->getCurrentAccount()); + const auto account = FMH::MODEL(); const QString concatenated = QString("%1:%2").arg(account[FMH::MODEL_KEY::USER], account[FMH::MODEL_KEY::PASSWORD]); const QByteArray data = concatenated.toLocal8Bit().toBase64(); const QString headerData = "Basic " + data; // Construct new QNetworkRequest with prepared header values QNetworkRequest newRequest(request); newRequest.setRawHeader(QString("Authorization").toLocal8Bit(), headerData.toLocal8Bit()); newRequest.setRawHeader(QByteArrayLiteral("OCS-APIREQUEST"), QByteArrayLiteral("true")); newRequest.setRawHeader(QByteArrayLiteral("Cache-Control"), QByteArrayLiteral("public")); newRequest.setRawHeader(QByteArrayLiteral("Content-Description"), QByteArrayLiteral("File Transfer")); newRequest.setHeader(QNetworkRequest::ContentTypeHeader, "audio/mpeg"); newRequest.setAttribute(QNetworkRequest::CacheSaveControlAttribute, true); newRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); qDebug() << "headers" << newRequest.rawHeaderList() << newRequest.url(); return newRequest; } bool Player::play() const { if(this->url.isEmpty()) return false; if(!updater->isActive()) this->updater->start(500); this->player->play(); return true; } void Player::pause() const { if(this->player->isAvailable()) this->player->pause(); } void Player::stop() { if(this->player->isAvailable()) { this->player->stop(); this->url = QString(); this->player->setMedia(QMediaContent()); } this->playing = false; emit this->playingChanged(); this->updater->stop(); this->emitState(); } void Player::emitState() { switch(this->player->state()) { case QMediaPlayer::PlayingState: this->state = Player::STATE::PLAYING; break; case QMediaPlayer::PausedState: this->state = Player::STATE::PAUSED; break; case QMediaPlayer::StoppedState: this->state = Player::STATE::STOPED; break; } emit this->stateChanged(); } QString Player::transformTime(const int &pos) { return BAE::transformTime(pos); } void Player::setUrl(const QUrl &value) { - if(value == this->url) - return; +// if(value == this->url) +// return; this->url = value; emit this->urlChanged(); this->pos = 0; emit this->posChanged(); const auto media = this->url.isLocalFile() ? QMediaContent(this->url) : QMediaContent(getOcsRequest(QNetworkRequest(this->url))); this->player->setMedia(media); this->emitState(); } QUrl Player::getUrl() const { return this->url; } void Player::setVolume(const int &value) { if(value == this->volume) return; this->volume = value; this->player->setVolume(volume); emit this->volumeChanged(); } int Player::getVolume() const { return this->volume; } int Player::getDuration() const { return static_cast(this->player->duration()); } Player::STATE Player::getState() const { return this->state; } void Player::setPlaying(const bool &value) { this->playing = value; if(this->playing) this->play(); else this->pause(); emit this->playingChanged(); this->emitState(); } bool Player::getPlaying() const { return this->playing; } bool Player::getFinished() { return this->finished; } void Player::setPos(const int &value) { this->pos = value; this->player->setPosition(this->player->duration() / 1000 * this->pos); this->emitState(); this->posChanged(); } int Player::getPos() const { return this->pos; } void Player::update() { if(this->player->isAvailable()) { this->pos = static_cast(static_cast(this->player->position())/this->player->duration()*1000);; emit this->durationChanged(); emit this->posChanged(); } if(this->player->state() == QMediaPlayer::StoppedState && this->updater->isActive() && this->player->position() == this->player->duration()) { this->finished = true; emit this->finishedChanged(); } this->emitState(); } diff --git a/utils/Player.js b/utils/Player.js index d569c9d..7dedba5 100644 --- a/utils/Player.js +++ b/utils/Player.js @@ -1,216 +1,212 @@ .import org.kde.mauikit 1.0 as Maui function playTrack(index) { if((index < mainPlaylist.listView.count) && (mainPlaylist.listView.count > 0) && (index > -1)) { - currentTrack = mainPlaylist.list.get(index) - if(typeof(currentTrack) === "undefined") return if(!Maui.FM.fileExists(currentTrack.url) && currentTrack.url.startsWith("file://")) { missingAlert(currentTrack) return } player.url = currentTrack.url; player.playing = true - currentArtwork = currentTrack.artwork progressBar.enabled = true root.title = currentTrack.title + " - " +currentTrack.artist // if(!root.active) // bae.notifySong(currentTrack.url) // if(currentTrack.lyrics.length < 1) // bae.trackLyrics(currentTrack.url) // root.mainPlaylist.infoView.wikiAlbum = bae.albumWiki(root.mainPlaylist.currentTrack.album,root.mainPlaylist.currentTrack.artist) // root.mainPlaylist.infoView.wikiArtist = bae.artistWiki(root.mainPlaylist.currentTrack.artist) // // root.mainPlaylist.infoView.artistHead = bae.artistArt(root.mainPlaylist.currentTrack.artist) } } function queueTracks(tracks) { if(tracks && tracks.length > 0) { appendTracksAt(tracks, currentTrackIndex+onQueue+1) onQueue++ root.notify("", "Queue", tracks.length + " tracks added put on queue") } } function setLyrics(lyrics) { currentTrack.lyrics = lyrics infoView.lyricsText.text = lyrics } function stop() { player.stop() progressBar.value = 0 progressBar.enabled = false root.title = "Babe..." } function pauseTrack() { player.playing = false } function resumeTrack() { if(!player.play() && !mainlistEmpty) playAt(0) } function nextTrack() { if(!mainlistEmpty) { var next = 0 if(isShuffle && onQueue === 0) next = shuffle() else next = currentTrackIndex+1 >= mainPlaylist.listView.count? 0 : currentTrackIndex+1 - prevTrackIndex = mainPlaylist.listView.currentIndex + prevTrackIndex = currentTrackIndex playAt(next) if(onQueue > 0) { onQueue-- - console.log(onQueue) } } } function previousTrack() { if(!mainlistEmpty) { - var previous = previous = currentTrackIndex-1 >= 0 ? mainPlaylist.listView.currentIndex-1 : currentTrackIndex-1 - prevTrackIndex = mainPlaylist.listView.currentIndex + const previous = currentTrackIndex-1 >= 0 ? mainPlaylist.listView.currentIndex-1 : mainPlaylist.listView.count-1 + prevTrackIndex = currentTrackIndex playAt(previous) } } function shuffle() { var pos = Math.floor(Math.random() * mainPlaylist.listView.count) return pos } function playAt(index) { if((index < mainPlaylist.listView.count) && (index > -1)) { currentTrackIndex = index mainPlaylist.listView.currentIndex = currentTrackIndex playTrack(currentTrackIndex) } } function quickPlay(track) { // root.pageStack.currentIndex = 0 appendTrack(track) playAt(mainPlaylist.listView.count-1) mainPlaylist.listView.positionViewAtEnd() } function appendTracksAt(tracks, at) { if(tracks) for(var i in tracks) mainPlaylist.list.append(tracks[i], parseInt(at)+parseInt(i)) } function appendTrack(track) { if(track) { mainPlaylist.list.append(track) if(sync === true) { playlistsList.addTrack(syncPlaylist, [track.url]) } } } function addTrack(track) { if(track) { appendTrack(track) mainPlaylist.listView.positionViewAtEnd() } } function appendAll(tracks) { if(tracks) { for(var i in tracks) appendTrack(tracks[i]) mainPlaylist.listView.positionViewAtEnd() } } function savePlaylist() { var list = [] var n = mainPlaylist.listView.count n = n > 15 ? 15 : n for(var i=0 ; i < n; i++) { var url = mainPlaylist.list.get(i).url list.push(url) } Maui.FM.saveSettings("LASTPLAYLIST", list, "PLAYLIST"); Maui.FM.saveSettings("PLAYLIST_POS", mainPlaylist.listView.currentIndex, "MAINWINDOW") } function clearOutPlaylist() { mainPlaylist.list.clear() stop() } function cleanPlaylist() { var urls = [] for(var i = 0; i < mainPlaylist.listView.count; i++) { var url = mainPlaylist.list.get(i).url if(urls.indexOf(url)<0) urls.push(url) else mainPlaylist.list.remove(i) } } function playAll(tracks) { sync = false syncPlaylist = "" mainPlaylist.list.clear() appendAll(tracks) if(_drawer.modal && !_drawer.visible) _drawer.visible = true mainPlaylist.listView.positionViewAtBeginning() playAt(0) } diff --git a/view_models/BabeTable/BabeTable.qml b/view_models/BabeTable/BabeTable.qml index 519d73d..3802998 100644 --- a/view_models/BabeTable/BabeTable.qml +++ b/view_models/BabeTable/BabeTable.qml @@ -1,508 +1,517 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import TracksList 1.0 import "../../utils/Player.js" as Player import "../../utils/Help.js" as H import "../../db/Queries.js" as Q import ".." BabeList { id: control // cacheBuffer : 300 property alias list : _tracksList property alias listModel : _tracksModel property alias removeDialog : _removeDialog property bool trackNumberVisible property bool coverArtVisible : false property bool allowMenu: true property bool showQuickActions : true property bool group : false property alias contextMenu : contextMenu property alias contextMenuItems : contextMenu.contentData property alias playAllBtn : playAllBtn property alias appendBtn : appendBtn signal rowClicked(int index) signal rowDoubleClicked(int index) signal rowPressed(int index) signal quickPlayTrack(int index) signal queueTrack(int index) signal appendTrack(int index) signal artworkDoubleClicked(int index) signal playAll() signal appendAll() focus: true holder.visible: list.count === 0 listView.spacing: Maui.Style.space.small * (Kirigami.Settings.isMobile ? 1.4 : 1.2) headBar.leftContent: [ ToolButton { id : playAllBtn icon.name : "media-playlist-play" onClicked: playAll() }, ToolButton { id: appendBtn icon.name : "media-playlist-append" onClicked: appendAll() }] headBar.rightContent: [ ToolButton { icon.name: "item-select" onClicked: selectionMode = !selectionMode checkable: false checked: selectionMode }, Maui.ToolButtonMenu { id: sortBtn icon.name: "view-sort" visible: list.count > 2 MenuItem { text: qsTr("Title") checkable: true checked: list.sortBy === Tracks.TITLE onTriggered: list.sortBy = Tracks.TITLE autoExclusive: true } MenuItem { text: qsTr("Track") checkable: true checked: list.sortBy === Tracks.TRACK onTriggered: list.sortBy = Tracks.TRACK autoExclusive: true } MenuItem { text: qsTr("Artist") checkable: true checked: list.sortBy === Tracks.ARTIST onTriggered: list.sortBy = Tracks.ARTIST autoExclusive: true } MenuItem { text: qsTr("Album") checkable: true checked: list.sortBy === Tracks.ALBUM onTriggered: list.sortBy = Tracks.ALBUM autoExclusive: true } MenuItem { text: qsTr("Most played") checkable: true checked: list.sortBy === Tracks.COUNT onTriggered: list.sortBy = Tracks.COUNT autoExclusive: true } MenuItem { text: qsTr("Rate") checkable: true checked: list.sortBy === Tracks.RATE onTriggered: list.sortBy = Tracks.RATE autoExclusive: true } MenuItem { text: qsTr("Favorite") checkable: true checked: list.sortBy === Tracks.FAV onTriggered: list.sortBy = Tracks.FAV autoExclusive: true } MenuItem { text: qsTr("Release date") checkable: true checked: list.sortBy === Tracks.RELEASEDATE onTriggered: list.sortBy = Tracks.RELEASEDATE autoExclusive: true } MenuItem { text: qsTr("Add date") checkable: true checked: list.sortBy === Tracks.ADDDATE onTriggered: list.sortBy = Tracks.ADDDATE autoExclusive: true } MenuSeparator{} MenuItem { text: qsTr("Group") checkable: true checked: group onTriggered: { group = !group groupBy() } } }, ToolButton { id: _filterButton icon.name: "view-filter" checkable: true visible: list.count > 10 } ] listView.header: Maui.ToolBar { Kirigami.Theme.inherit: false // Kirigami.Theme.backgroundColor: control.Kirigami.Theme.backgroundColor visible: _filterButton.checked && _filterButton.visible width: control.width position: ToolBar.Header z: listView.z + 9 middleContent: Maui.TextField { Layout.fillWidth: true onAccepted: listModel.setFilterString(text) onTextChanged: listModel.setFilterString(text) } } Maui.Dialog { id: _removeDialog property int index title: qsTr("Remove track") message: qsTr("You can delete the file from your computer or remove it from your collection") rejectButton.text: qsTr("Delete") acceptButton.text: qsTr("Remove") page.padding: Maui.Style.space.huge onAccepted: { list.remove(listView.currentIndex) close() } onRejected: { if(Maui.FM.removeFile(list.get(index).url)) list.remove(listView.currentIndex) close() } } TableMenu { id: contextMenu MenuSeparator {} MenuItem { text: qsTr("Go to Artist") onTriggered: goToArtist() } MenuItem { text: qsTr("Go to Album") onTriggered: goToAlbum() } onFavClicked: { list.fav(listView.currentIndex, !(list.get(listView.currentIndex).fav == "1")) } onQueueClicked: Player.queueTracks([list.get(listView.currentIndex)]) onPlayClicked: quickPlayTrack(listView.currentIndex) onAppendClicked: appendTrack(listView.currentIndex) onSaveToClicked: { playlistDialog.tracks = [list.get(listView.currentIndex).url] playlistDialog.open() } onOpenWithClicked: Maui.FM.openLocation([list.get(listView.currentIndex).url]) onDeleteClicked: { _removeDialog.index= listView.currentIndex _removeDialog.open() } onRateClicked: { list.rate(listView.currentIndex, rate); } onColorClicked: { list.color(listView.currentIndex, color); } onInfoClicked: { infoView.show(list.get(listView.currentIndex)) } onCopyToClicked: { cloudView.list.upload(listView.currentIndex) } onShareClicked: { const url = list.get(listView.currentIndex).url if(isAndroid) { Maui.Android.shareDialog(url) return } _dialogLoader.sourceComponent = _shareDialogComponent root.dialog.show([url]) } } section.criteria: ViewSection.FullString section.delegate: Maui.LabelDelegate { id: _sectionDelegate label: section isSection: true width: control.width Kirigami.Theme.backgroundColor: "#333" Kirigami.Theme.textColor: "#fafafa" background: Rectangle { color: Kirigami.Theme.backgroundColor } } Maui.BaseModel { id: _tracksModel list: _tracksList recursiveFilteringEnabled: true sortCaseSensitivity: Qt.CaseInsensitive filterCaseSensitivity: Qt.CaseInsensitive } Tracks { id: _tracksList onSortByChanged: if(control.group) control.groupBy() } model: _tracksModel // property alias animBabe: delegate.animBabe delegate: TableDelegate { id: delegate width: listView.width number : trackNumberVisible ? true : false coverArt : coverArtVisible ? (control.width > 200) : coverArtVisible - showQuickActions: control.showQuickActions onPressAndHold: if(Kirigami.Settings.isMobile && allowMenu) openItemMenu(index) onRightClicked: if(allowMenu) openItemMenu(index) onLeftEmblemClicked: H.addToSelection(list.get(index)) isSelected: selectionBar.contains(model.url) sameAlbum: { if(coverArt) { if(list.get(index-1)) { if(list.get(index-1).album === album && list.get(index-1).artist === artist) true else false }else false }else false } + ToolButton + { + Layout.fillHeight: true + Layout.preferredWidth: implicitWidth + visible: control.showQuickActions && (Kirigami.Settings.isMobile ? true : delegate.hovered) + icon.name: "media-playlist-append" + onClicked: delegate.append() + opacity: delegate.hovered ? 0.8 : 0.6 + } + onClicked: { currentIndex = index if(selectionMode) { H.addToSelection(list.get(listView.currentIndex)) return } if(Kirigami.Settings.isMobile) rowClicked(index) } onDoubleClicked: { currentIndex = index if(!Kirigami.Settings.isMobile) rowClicked(index) } onPlay: { currentIndex = index quickPlayTrack(index) } onAppend: { currentIndex = index appendTrack(index) } onArtworkCoverClicked: { currentIndex = index goToAlbum() } Connections { target: selectionBar onUriRemoved: { if(uri === model.url) delegate.isSelected = false } onUriAdded: { if(uri === model.url) delegate.isSelected = true } onCleared: delegate.isSelected = false } } function openItemMenu(index) { currentIndex = index contextMenu.rate = list.get(currentIndex).rate contextMenu.fav = list.get(currentIndex).fav == "1" contextMenu.popup() rowPressed(index) } function saveList() { var trackList = [] if(list.count > 0) { for(var i = 0; i < list.count; ++i) trackList.push(listModel.get(i).url) playlistDialog.tracks = trackList playlistDialog.open() } } function queueList() { var trackList = [] if(list.count > 0) { for(var i = 0; i < list.count; ++i) trackList.push(list.get(i)) Player.queueTracks(trackList) } } function goToAlbum() { swipeView.currentIndex = viewsIndex.albums const item = list.get(listView.currentIndex) swipeView.currentItem.item.populateTable(item.album, item.artist) contextMenu.close() } function goToArtist() { swipeView.currentIndex = viewsIndex.artists const item = list.get(listView.currentIndex) swipeView.currentItem.item.populateTable(undefined, item.artist) contextMenu.close() } function groupBy() { var prop = "undefined" if(group) switch(list.sortBy) { case Tracks.TITLE: prop = "title" break case Tracks.ARTIST: prop = "artist" break case Tracks.ALBUM: prop = "album" break case Tracks.RATE: prop = "rate" break case Tracks.FAV: prop = "fav" break case Tracks.ADDDATE: prop = "adddate" break case Tracks.RELEASEDATE: prop = "releasedate" break; case Tracks.COUNT: prop = "count" break } section.property = prop } } diff --git a/view_models/BabeTable/TableDelegate.qml b/view_models/BabeTable/TableDelegate.qml index 26eac49..f10a6f8 100644 --- a/view_models/BabeTable/TableDelegate.qml +++ b/view_models/BabeTable/TableDelegate.qml @@ -1,104 +1,94 @@ import QtQuick 2.10 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.10 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import "../../view_models" import "../../utils/Help.js" as H Maui.ItemDelegate { id: control isCurrentItem: ListView.isCurrentItem || isSelected - + default property alias content : _template.content property bool showQuickActions: true property bool number : false property bool coverArt : false property bool showEmblem: true property bool keepEmblemOverlay: selectionMode property bool isSelected : false property color trackMood : model.color readonly property color bgColor : Kirigami.Theme.backgroundColor readonly property int altHeight : Maui.Style.rowHeight * 1.4 property bool sameAlbum : false width: parent.width height: sameAlbum ? Maui.Style.rowHeight : altHeight padding: 0 rightPadding: leftPadding leftPadding: Maui.Style.space.small signal play() signal append() signal leftClicked() signal leftEmblemClicked(int index) signal artworkCoverClicked() signal artworkCoverDoubleClicked() Kirigami.Theme.backgroundColor: model.color.length > 0 ? Qt.rgba(trackMood.r, trackMood.g, trackMood.b, 0.2): bgColor function rate(stars) { trackRating.text = stars } RowLayout { anchors.fill: parent Item { Layout.fillHeight: true Layout.preferredWidth: _leftEmblemIcon.height + Maui.Style.space.small visible: (control.keepEmblemOverlay || control.isSelected) && control.showEmblem Maui.Badge { id: _leftEmblemIcon anchors.centerIn: parent iconName: control.isSelected ? "list-remove" : "list-add" onClicked: control.leftEmblemClicked(index) size: Maui.Style.iconSizes.small } } Maui.ListItemTemplate { id: _template Layout.fillWidth: true Layout.fillHeight: true isCurrentItem: control.isCurrentItem iconSizeHint: height - Maui.Style.space.small label1.text: control.number ? model.track + ". " + model.title : model.title label2.text: model.artist + " | " + model.album label2.visible: control.coverArt ? !control.sameAlbum : true label3.text: model.fav ? (model.fav == "1" ? "\uf2D1" : "") : "" label3.font.family: "Material Design Icons" label4.font.family: "Material Design Icons" label4.text: model.rate ? H.setStars(model.rate) : "" iconVisible: !control.sameAlbum && control.coverArt imageSource: model.artwork ? model.artwork : "qrc:/assets/cover.png" - - ToolButton - { - Layout.fillHeight: true - Layout.preferredWidth: implicitWidth - visible: showQuickActions && (Kirigami.Settings.isMobile ? true : control.hovered) - icon.name: "media-playlist-append" - onClicked: control.append() - opacity: control.hovered ? 0.8 : 0.6 - } } } } diff --git a/widgets/FoldersView.qml b/widgets/FoldersView.qml index 2a77d65..997d6d1 100644 --- a/widgets/FoldersView.qml +++ b/widgets/FoldersView.qml @@ -1,82 +1,82 @@ import QtQuick 2.0 import QtQuick.Controls 2.10 import org.kde.mauikit 1.0 as Maui import "../view_models/BabeTable" import "../db/Queries.js" as Q Item { id: control property alias list : _filterList property alias listModel : _filterList.model property var tracks : [] property string currentFolder : "" Maui.GridBrowser { id: browser anchors.fill: parent showEmblem: false model: ListModel {} itemWidth: itemSize * 1.2 itemHeight: itemSize * 1.2 onItemClicked: { var item = browser.model.get(index) _filterList.title= item.label currentFolder = item.path filter() _listDialog.open() } } Maui.Holder { anchors.fill: parent visible: !browser.count - emoji: "qrc:/assets/MusicCloud.png" + emoji: "qrc:/assets/dialog-information.svg" isMask: false title : "No Folders!" body: "Add new music to your sources to browse by folders" emojiSize: Maui.Style.iconSizes.huge } Maui.Dialog { id: _listDialog parent: parent maxHeight: maxWidth maxWidth: Maui.Style.unit * 600 defaultButtons: false page.padding: 0 BabeTable { id: _filterList anchors.fill: parent coverArtVisible: true holder.emoji: "qrc:/assets/MusicCloud.png" holder.isMask: false holder.title : "No Tracks!" holder.body: "This source folder seems to be empty!" holder.emojiSize: Maui.Style.iconSizes.huge } } Component.onCompleted: populate() function populate() { browser.model.clear() var folders = vvave.sourceFolders(); if(folders.length > 0) for(var i in folders) browser.model.append(folders[i]) } function filter() { var where = "source = \""+currentFolder+"\"" _filterList.list.query = (Q.GET.tracksWhere_.arg(where)) } } diff --git a/widgets/MainPlaylist/AlbumsRoll.qml b/widgets/MainPlaylist/AlbumsRoll.qml index 3326b9e..ec1821a 100644 --- a/widgets/MainPlaylist/AlbumsRoll.qml +++ b/widgets/MainPlaylist/AlbumsRoll.qml @@ -1,117 +1,138 @@ import QtQuick.Controls 2.10 import QtQuick 2.10 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import QtGraphicalEffects 1.0 import "../../view_models/BabeGrid" import "../../utils/Player.js" as Player Maui.ToolBar { id: control visible: !mainlistEmpty padding: 0 height: Maui.Style.toolBarHeight * 1.2 background: Item { Image { id: artworkBg height: parent.height width: parent.width sourceSize.width: parent.width sourceSize.height: parent.height fillMode: Image.PreserveAspectCrop antialiasing: true smooth: true asynchronous: true cache: true source: currentArtwork } FastBlur { id: fastBlur anchors.fill: parent source: artworkBg radius: 100 transparentBorder: false cached: true Rectangle { anchors.fill: parent color: Kirigami.Theme.backgroundColor opacity: 0.8 } } Kirigami.Separator { anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right } } + rightContent: [ + ToolButton + { + icon.name: "edit-delete" + onClicked: + { + player.stop() + mainPlaylist.table.list.clear() + root.sync = false + root.syncPlaylist = "" + } + } + ] + + leftContent: ToolButton + { + icon.name: "document-save" + onClicked: mainPlaylist.table.saveList() + } + middleContent: ListView { id: _listView Layout.fillWidth: true Layout.preferredHeight: Maui.Style.toolBarHeight orientation: ListView.Horizontal clip: true focus: true interactive: true currentIndex: currentTrackIndex spacing: Maui.Style.space.medium -// cacheBuffer: control.width * 1 + // cacheBuffer: control.width * 1 onCurrentIndexChanged: positionViewAtIndex(currentIndex, ListView.Center) highlightFollowsCurrentItem: true highlightMoveDuration: 0 snapMode: ListView.SnapToOneItem model: mainPlaylist.listModel highlightRangeMode: ListView.StrictlyEnforceRange keyNavigationEnabled: true keyNavigationWraps : true onCurrentItemChanged: { var index = indexAt(contentX, contentY) if(index !== currentTrackIndex) Player.playAt(index) } delegate: Maui.ItemDelegate { id: _delegate height: _listView.height width: _listView.width padding: 0 Kirigami.Theme.inherit: true Maui.ListItemTemplate { anchors.fill: parent iconSizeHint: height - Maui.Style.space.small + iconVisible: false imageSource: model.artwork ? model.artwork : "qrc:/assets/cover.png" label1.text: model.title label2.text: model.artist + " | " + model.album } onClicked: focusView = true background: null } } } diff --git a/widgets/MainPlaylist/MainPlaylist.qml b/widgets/MainPlaylist/MainPlaylist.qml index ce4dba8..4280c22 100644 --- a/widgets/MainPlaylist/MainPlaylist.qml +++ b/widgets/MainPlaylist/MainPlaylist.qml @@ -1,208 +1,229 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.2 as Kirigami import org.kde.mauikit 1.0 as Maui import "../../utils/Player.js" as Player import "../../db/Queries.js" as Q import "../../utils" import "../../widgets" import "../../view_models" import "../../view_models/BabeTable" Maui.Page { - id: mainPlaylistRoot + id: control property alias list : table.list property alias listModel: table.listModel property alias listView : table.listView property alias table: table property alias menu : playlistMenu property alias contextMenu: table.contextMenu signal coverDoubleClicked(var tracks) signal coverPressed(var tracks) focus: true headBar.visible: false PlaylistMenu { id: playlistMenu onClearOut: Player.clearOutPlaylist() onClean: Player.cleanPlaylist() onSaveToClicked: table.saveList() } footer: AlbumsRoll { id: _albumsRoll width: table.width position: ToolBar.Footer } BabeTable { id: table anchors.fill: parent focus: true headBar.visible: false footBar.visible: false coverArtVisible: true holder.emoji: "qrc:/assets/dialog-information.svg" holder.isMask: false holder.title : "Meh!" holder.body: "Start putting together your playlist!" holder.emojiSize: Maui.Style.iconSizes.huge - onRowClicked: play(index) - showQuickActions: false - listView.header: Maui.ToolBar - { - Kirigami.Theme.inherit: false - z: table.z +999 - width: table.width - visible: table.list.count > 0 - rightContent: ToolButton - { - icon.name: "edit-clear" - onClicked: - { - player.stop() - mainPlaylist.table.list.clear() - root.sync = false - root.syncPlaylist = "" - } - } - - middleContent: Label - { - text: qsTr("Now playing") - } - - leftContent: ToolButton - { - icon.name: "document-save" - onClicked: mainPlaylist.table.saveList() - } - } - - listView.footer: Rectangle + listView.header: Rectangle { visible: root.sync Kirigami.Theme.inherit: false Kirigami.Theme.colorSet:Kirigami.Theme.Complementary z: table.z + 999 width: table.width - height: Maui.Style.rowHeightAlt + height: visible ? Maui.Style.rowHeightAlt : 0 color: Kirigami.Theme.backgroundColor RowLayout { anchors.fill: parent anchors.leftMargin: Maui.Style.space.small Label { Layout.fillWidth: true Layout.fillHeight: true anchors.margins: Maui.Style.space.small text: qsTr("Syncing to ") + root.syncPlaylist } ToolButton { Layout.fillHeight: true icon.name: "dialog-close" onClicked: { root.sync = false root.syncPlaylist = "" } } } } + delegate: TableDelegate + { + id: delegate + width: listView.width + number : false + coverArt : true + showEmblem: false + onPressAndHold: if(Kirigami.Settings.isMobile && table.allowMenu) table.openItemMenu(index) + onRightClicked: if(allowMenu) openItemMenu(index) + sameAlbum: + { + if(coverArt) + { + if(list.get(index-1)) + { + if(list.get(index-1).album === album && list.get(index-1).artist === artist) true + else false + }else false + }else false + } + + ToolButton + { + Layout.fillHeight: true + Layout.preferredWidth: implicitWidth + visible: (Kirigami.Settings.isMobile ? true : delegate.hovered) + icon.name: "edit-clear" + onClicked: + { + if(index === currentTrackIndex) + player.stop() + + list.remove(index) + } + + opacity: delegate.hovered ? 0.8 : 0.6 + } + + onClicked: + { + table.currentIndex = index + if(Kirigami.Settings.isMobile) + control.play(index) + } + + onDoubleClicked: + { + table.currentIndex = index + if(!Kirigami.Settings.isMobile) + control.play(index) + } + } + onArtworkDoubleClicked: contextMenu.babeIt(index) property int startContentY Component.onCompleted: { var lastplaylist = Maui.FM.loadSettings("LASTPLAYLIST", "PLAYLIST", []) var n = lastplaylist.length if(n>0) { for(var i = 0; i < n; i++) { var where = "url = \""+lastplaylist[i]+"\"" var query = Q.GET.tracksWhere_.arg(where) table.list.appendQuery(query); } }else { where = "fav = 1" query = Q.GET.tracksWhere_.arg(where) table.list.appendQuery(query); } // if(autoplay) // Player.playAt(0) } } // function goFocusMode() // { // if(focusMode) // { // if(isMobile) // { // root.width = screenWidth // root.height= screenHeight // }else // { // cover.y = 0 // root.maximumWidth = screenWidth // root.minimumWidth = columnWidth // root.maximumHeight = screenHeight // root.minimumHeight = columnWidth // root.width = columnWidth // root.height = 700 // } // }else // { // if(isMobile) // { // }else // { // root.maximumWidth = columnWidth // root.minimumWidth = columnWidth // root.maximumHeight = columnWidth // root.minimumHeight = columnWidth // // root.footer.visible = false // // mainlistContext.visible = false // } // } // focusMode = !focusMode // } function play(index) { prevTrackIndex = currentTrackIndex Player.playAt(index) } } diff --git a/widgets/PlaylistsView/PlaylistsView.qml b/widgets/PlaylistsView/PlaylistsView.qml index cc6ed56..8ed9c28 100644 --- a/widgets/PlaylistsView/PlaylistsView.qml +++ b/widgets/PlaylistsView/PlaylistsView.qml @@ -1,201 +1,201 @@ import QtQuick 2.10 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.10 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import TracksList 1.0 import QtGraphicalEffects 1.0 import "../../view_models/BabeTable" import "../../view_models" import "../../db/Queries.js" as Q import "../../utils/Help.js" as H Maui.Page { id: control spacing: Maui.Style.space.medium property string currentPlaylist property string playlistQuery property alias playlistModel : playlistViewModel.model property alias playlistViewList : playlistViewModel property alias listModel : filterList.listModel signal rowClicked(var track) signal quickPlayTrack(var track) signal appendTrack(var track) signal playAll() signal syncAndPlay(string playlist) signal appendAll() Maui.FloatingButton { id: _overlayButton z: 999 anchors.right: parent.right anchors.bottom: parent.bottom anchors.margins: Maui.Style.toolBarHeight anchors.bottomMargin: Maui.Style.toolBarHeight icon.name : "list-add" onClicked: newPlaylistDialog.open() } PlaylistsViewModel { id: playlistViewModel anchors.fill: parent } Maui.NewDialog { id: newPlaylistDialog - title: qsTr("New Playlist...") + title: qsTr("Add new playlist") onFinished: addPlaylist(text) acceptText: qsTr("Create") rejectButton.visible: false } Maui.Dialog { id: _filterDialog property bool isPublic: true parent: parent maxHeight: maxWidth maxWidth: Maui.Style.unit * 600 defaultButtons: false page.padding: 0 BabeTable { id: filterList anchors.fill: parent clip: true coverArtVisible: true headBar.visible: !holder.visible title: control.currentPlaylist holder.emoji: "qrc:/assets/dialog-information.svg" holder.isMask: false holder.title : title holder.body: "Your playlist is empty,
start adding new music to it" holder.emojiSize: Maui.Style.iconSizes.huge contextMenuItems: MenuItem { text: qsTr("Remove from playlist") } // headerMenu.menuItem: [ // Maui.MenuItem // { // enabled: !playlistViewModel.model.get(playlistViewModel.currentIndex).playlistIcon // text: "Sync tags" // onTriggered: {} // }, // Maui.MenuItem // { // enabled: !playlistViewModel.model.get(playlistViewModel.currentIndex).playlistIcon // text: "Play-n-Sync" // onTriggered: // { // filterList.headerMenu.close() // syncAndPlay(playlistViewModel.currentIndex) // } // }, // Maui.MenuItem // { // enabled: !playlistViewModel.model.get(playlistViewModel.currentIndex).playlistIcon // text: "Remove playlist" // onTriggered: removePlaylist() // } // ] // contextMenu.menuItem: [ // MenuItem // { // text: qsTr("Remove from playlist") // onTriggered: // { // bae.removePlaylistTrack(filterList.model.get(filterList.currentIndex).url, playlistViewModel.model.get(playlistViewModel.currentIndex).playlist) // populate(playlistQuery) // } // } // ] section.criteria: ViewSection.FullString section.delegate: Maui.LabelDelegate { label: filterList.section.property === qsTr("stars") ? H.setStars(section) : section isSection: true labelTxt.font.family: "Material Design Icons" width: filterList.width } Connections { target: filterList onRowClicked: control.rowClicked(filterList.listModel.get(index)) onQuickPlayTrack: control.quickPlayTrack(filterList.listModel.get(filterList.currentIndex)) onAppendTrack: control.appendTrack(filterList.listModel.get(filterList.currentIndex)) onPlayAll: { if(_filterDialog.isPublic) control.syncAndPlay(control.currentPlaylist) else control.playAll() _filterDialog.close() } onAppendAll: appendAll() onPulled: populate(playlistQuery) } Connections { target: filterList.contextMenu onRemoveClicked: { playlistsList.removeTrack(playlistViewList.currentIndex, filterList.listModel.get(filterList.currentIndex).url) populate(playlistQuery) } } } } function appendToExtraList(res) { if(res.length>0) for(var i in res) playlistViewModelFilter.model.append(res[i]) } function populate(query, isPublic) { playlistQuery = query _filterDialog.isPublic = isPublic filterList.list.query = playlistQuery _filterDialog.open() } function removePlaylist() { playlistsList.removePlaylist(playlistViewList.currentIndex) } function addPlaylist(text) { var title = text.trim() if(playlistsList.insert(title)) control.listView.positionViewAtEnd() } }