diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bfcffe..3bda283 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,96 +1,96 @@ project(vvave) cmake_minimum_required(VERSION 3.0) find_package(ECM 1.7.0 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${ECM_MODULE_PATH}) set(CMAKE_CXX_STANDARD 17) find_package(MauiKit REQUIRED) find_package(Qt5 REQUIRED NO_MODULE COMPONENTS Qml Quick Network WebSockets Sql QuickControls2 Xml Multimedia Widgets DBus Svg) include(KDEInstallDirs) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(ECMInstallIcons) include(FeatureSummary) include(ECMAddAppIcon) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTORCC ON) add_executable(vvave qml.qrc main.cpp # babe.cpp vvave.cpp # pulpo/services/geniusService.cpp # pulpo/services/deezerService.cpp pulpo/services/lastfmService.cpp # pulpo/services/lyricwikiaService.cpp # pulpo/services/spotifyService.cpp # pulpo/services/musicbrainzService.cpp pulpo/pulpo.cpp pulpo/htmlparser.cpp pulpo/service.cpp services/local/taginfo.cpp services/local/player.cpp services/local/youtubedl.cpp - services/local/linking.cpp +# services/local/linking.cpp services/local/socket.cpp - services/web/babeit.cpp +# services/web/babeit.cpp services/web/youtube.cpp - services/web/Spotify/spotify.cpp +# services/web/Spotify/spotify.cpp db/collectionDB.cpp # settings/BabeSettings.cpp # settings/fileloader.cpp # utils/brain.cpp - utils/babeconsole.cpp +# utils/babeconsole.cpp models/tracks/tracksmodel.cpp models/basemodel.cpp models/baselist.cpp models/playlists/playlistsmodel.cpp models/albums/albumsmodel.cpp - models/cloud/cloud.cpp +# models/cloud/cloud.cpp ) if (ANDROID) find_package(Qt5 REQUIRED COMPONENTS AndroidExtras WebView Xml) find_package(OpenSSL REQUIRED) include(ExternalProject) externalproject_add(taglib URL http://taglib.org/releases/taglib-1.11.1.tar.gz CMAKE_ARGS -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} ) set(TAGLIB_INCLUDE_DIRS ${CMAKE_INSTALL_PREFIX}/include ${CMAKE_INSTALL_PREFIX}/include/taglib) set(TAGLIB_LIBRARIES ${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/libtag.a) target_link_libraries(vvave Qt5::AndroidExtras Qt5::WebView Qt5::Xml OpenSSL::SSL) add_dependencies(vvave taglib) kde_source_files_enable_exceptions(vvave babe.cpp) else() find_package(Taglib REQUIRED) find_package(Qt5 REQUIRED COMPONENTS WebEngine) find_package(KF5 ${KF5_VERSION} REQUIRED COMPONENTS I18n Notifications Config KIO Attica) target_sources(vvave PRIVATE kde/mpris2.cpp kde/notify.cpp ) target_link_libraries(vvave Qt5::WebEngine KF5::ConfigCore KF5::Notifications KF5::KIOCore KF5::I18n Qt5::DBus KF5::Attica) endif() if (TARGET create-apk-vvave) set_target_properties(create-apk-vvave PROPERTIES ANDROID_APK_DIR "${MAUIKIT_ANDROID_DIR}") endif() target_include_directories(vvave PRIVATE ${TAGLIB_INCLUDE_DIRS}) target_link_libraries(vvave MauiKit Qt5::Network Qt5::Sql Qt5::WebSockets Qt5::Qml Qt5::Xml Qt5::Multimedia Qt5::Widgets Qt5::QuickControls2 ${TAGLIB_LIBRARIES}) install(TARGETS vvave ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES org.kde.vvave.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) #TODO: port to ecm_install_icons() install(FILES assets/vvave.svg DESTINATION ${KDE_INSTALL_ICONDIR}/hicolor/scalable/apps) install(FILES org.kde.vvave.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/main.qml b/main.qml index 15fa87d..ccd79a1 100644 --- a/main.qml +++ b/main.qml @@ -1,1065 +1,1066 @@ import QtQuick 2.9 import QtQuick.Controls 2.3 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.6 as Kirigami import org.kde.mauikit 1.0 as Maui import Player 1.0 import AlbumsList 1.0 import TracksList 1.0 import BaseModel 1.0 import TracksList 1.0 Maui.ApplicationWindow { id: root title: qsTr("vvave") /***************************************************/ /******************** ALIASES ********************/ /*************************************************/ property alias mainPlaylist: mainPlaylist property alias selectionBar: _selectionBar property alias progressBar: progressBar about.appIcon: "qrc:/assets/vvave.svg" about.appDescription: qsTr("VVAVE will handle your whole music collection by retreaving semantic information from the web. Just relax, enjoy and discover your new music ") showAccounts: false /***************************************************/ /******************** PLAYBACK ********************/ /*************************************************/ property bool isShuffle: Maui.FM.loadSettings("SHUFFLE","PLAYBACK", false) property var currentTrack: ({ fav: "0", stars: "0" }) property int currentTrackIndex: -1 property int prevTrackIndex: 0 property string currentArtwork: !mainlistEmpty ? mainPlaylist.list.get(0).artwork : "" property bool currentBabe: currentTrack.fav == "0" ? false : true property alias durationTimeLabel: player.duration property string progressTimeLabel: player.transformTime(player.position/1000) property alias isPlaying: player.playing property int onQueue: 0 property bool mainlistEmpty: !mainPlaylist.table.count > 0 /***************************************************/ /******************** HANDLERS ********************/ /*************************************************/ property int currentView: viewsIndex.tracks readonly property var viewsIndex: ({ tracks: 0, albums: 1, artists: 2, playlists: 3, search: 4, folders: 5, // cloud: 6, // vvave: 7, // linking: 8, youtube: 6, // spotify: 10 }) property string syncPlaylist: "" property bool sync: false property string infoMsg: "" property bool infoLabels: Maui.FM.loadSettings("LABELS", "PLAYBACK", false) == "true" ? true : false // property bool isLinked: false // property bool isServing: false // property bool focusMode : 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 = track.title + " by " + track.artist + " is missing" var messageBody = "Do you want to remove it from your collection?" notify("alert", message, messageBody, function () { // bae.removeTrack(currentTrack.url) //todo mainPlaylist.table.model.remove(mainPlaylist.table.currentIndex) }) } /*COMPONENTS*/ Player { id: player volume: 100 onFinishedChanged: if (!mainlistEmpty) { if (currentTrack.url) mainPlaylist.list.countUp(currentTrackIndex) Player.nextTrack() } } // BabeNotify // { // id: babeNotify //todo // } /* UI */ altToolBars: false accentColor: babeColor // headBarFGColor: altColorText // headBarBGColor: "#212121" // altColorText: darkTextColor floatingBar: false headBar.spacing: space.big headBar.middleContent : Kirigami.ActionToolBar { display: isWide ? ToolButton.TextBesideIcon : ToolButton.TextUnderIcon Layout.fillWidth: true hiddenActions: [ Kirigami.Action { text: qsTr("Folders") icon.name: "folder" checked: currentView === viewsIndex.folders checkable: false onTriggered: currentView = viewsIndex.folders }, Kirigami.Action { text: qsTr("YouTube") checkable: false icon.name: "internet-services" checked: currentView === viewsIndex.youtube onTriggered: currentView = viewsIndex.youtube } ] actions: [ Kirigami.Action { icon.name: "view-media-track" checked: currentView === viewsIndex.tracks // showIndicator: true // icon.color: active ? babeColor : altColorText onTriggered: currentView = viewsIndex.tracks text: qsTr("Tracks") checkable: false // colorScheme.highlightColor: babeColor // spacing: 0 }, Kirigami.Action { text: qsTr("Albums") checkable: false checked: currentView === viewsIndex.albums icon.name: /*"album"*/ "view-media-album-cover" // icon.color: currentView === viewsIndex.albums ? babeColor : altColorText onTriggered: currentView = viewsIndex.albums // colorScheme.highlightColor: babeColor // showIndicator: true }, Kirigami.Action { Layout.fillWidth: true text: qsTr("Artists") checkable: false checked: currentView === viewsIndex.artists icon.name: "view-media-artist" Kirigami.Theme.textColor: currentView === viewsIndex.artists ? babeColor : altColorText onTriggered: currentView = viewsIndex.artists // colorScheme.highlightColor: babeColor // showIndicator: true }, Kirigami.Action { Layout.fillWidth: true text: qsTr("Playlists") checkable: false checked: currentView === viewsIndex.playlists icon.name: "view-media-playlist" // icon.color: currentView === viewsIndex.playlists ? babeColor : altColorText onTriggered: currentView = viewsIndex.playlists // colorScheme.highlightColor: babeColor // showIndicator: true } ] } footBar.visible: !mainlistEmpty footBar.implicitHeight: footBar.visible ? toolBarHeight * 1.2 : 0 page.footBarItem: ColumnLayout { id: _footerLayout height: footBar.height width: root.width spacing: 0 Slider { id: progressBar Layout.preferredHeight: unit * (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: babeColor } } handle: Rectangle { x: progressBar.leftPadding + progressBar.visualPosition * (progressBar.availableWidth - width) y: -(progressBar.height * 0.8) implicitWidth: progressBar.pressed ? iconSizes.medium : 0 implicitHeight: progressBar.pressed ? iconSizes.medium : 0 radius: progressBar.pressed ? iconSizes.medium : 0 color: babeColor } } Kirigami.Separator { Layout.fillWidth: true color: borderColor } Maui.ToolBar { Layout.fillHeight: true Layout.fillWidth: true - leftContent: Maui.ToolButton + leftContent: ToolButton { - iconName: "headphones" + icon.name: "headphones" visible: _drawer.modal - iconColor: _drawer.visible ? babeColor : textColor + icon.color: _drawer.visible ? babeColor : textColor onClicked: _drawer.visible = !_drawer.visible - colorScheme.highlightColor: babeColor + Kirigami.Theme.highlightColor: babeColor // text: qsTr("Now") } middleContent: [ - Maui.ToolButton + ToolButton { id: babeBtnIcon - iconName: "love" - - iconColor: currentBabe ? babeColor : textColor + icon.name: "love" + enabled: currentTrackIndex >= 0 + icon.color: currentBabe ? babeColor : textColor onClicked: if (!mainlistEmpty) { mainPlaylist.list.fav(currentTrackIndex, !(mainPlaylist.list.get(currentTrackIndex).fav == "1")) currentBabe = mainPlaylist.list.get(currentTrackIndex).fav == "1" } }, - Maui.ToolButton + ToolButton { - iconName: "media-skip-backward" - iconColor: textColor + icon.name: "media-skip-backward" + icon.color: textColor onClicked: Player.previousTrack() onPressAndHold: Player.playAt(prevTrackIndex) }, - Maui.ToolButton + ToolButton { id: playIcon - iconColor: textColor - iconName: isPlaying ? "media-playback-pause" : "media-playback-start" + enabled: currentTrackIndex >= 0 + icon.color: textColor + icon.name: isPlaying ? "media-playback-pause" : "media-playback-start" onClicked: player.playing = !player.playing }, - Maui.ToolButton + ToolButton { id: nextBtn - iconColor: textColor - iconName: "media-skip-forward" + icon.color: textColor + icon.name: "media-skip-forward" onClicked: Player.nextTrack() onPressAndHold: Player.playAt(Player.shuffle()) }, - Maui.ToolButton + ToolButton { id: shuffleBtn - iconColor: babeColor - iconName: isShuffle ? "media-playlist-shuffle" : "media-playlist-normal" + icon.color: babeColor + icon.name: isShuffle ? "media-playlist-shuffle" : "media-playlist-normal" onClicked: { isShuffle = !isShuffle Maui.FM.saveSettings("SHUFFLE", isShuffle, "PLAYBACK") } } ] } } // leftIcon.iconColor: currentView === viewsIndex.search ? babeColor : altColorText onSearchButtonClicked: { currentView = viewsIndex.search searchView.searchInput.forceActiveFocus() } InfoView { id: infoView maxWidth: parent.width * 0.8 maxHeight: parent.height * 0.9 } Maui.ShareDialog { id: shareDialog } Maui.FileDialog { id: fmDialog } 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: { fmDialog.onlyDirs = false fmDialog.filterType = Maui.FMList.AUDIO fmDialog.show(function(paths) { vvave.openUrls(paths) }) } }/*, 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") // } // } // } ] Item { id: message visible: infoMsg.length && sync anchors.bottom: parent.bottom width: parent.width height: iconSize z: 999 Rectangle { id: infoBg anchors.fill: parent z: -999 color: altColor opacity: 0.8 SequentialAnimation { id: animBg PropertyAnimation { target: infoBg property: "color" easing.type: Easing.InOutQuad to: babeColor duration: 250 } PropertyAnimation { target: infoBg property: "color" easing.type: Easing.InOutQuad to: altColor duration: 500 } } } Label { id: infoTxt anchors.centerIn: parent anchors.fill: parent height: parent.height width: parent.width font.pointSize: fontSizes.medium text: infoMsg horizontalAlignment: Qt.AlignHCenter verticalAlignment: Qt.AlignVCenter color: textColor SequentialAnimation { id: animTxt PropertyAnimation { target: infoTxt property: "color" easing.type: Easing.InOutQuad to: "white" duration: 250 } PropertyAnimation { target: infoTxt property: "color" easing.type: Easing.InOutQuad to: textColor duration: 500 } } } } PlaylistDialog { id: playlistDialog } globalDrawer: Maui.GlobalDrawer { id: _drawer width: Kirigami.Units.gridUnit * 18 height: root.height - root.headBar.implicitHeight - root.footBar.implicitHeight modal: !isWide handleVisible: false closePolicy: Popup.NoAutoClose contentItem: MainPlaylist { id: mainPlaylist Connections { target: mainPlaylist onCoverPressed: Player.appendAll(tracks) onCoverDoubleClicked: Player.playAll(tracks) } } } ColumnLayout { anchors.fill: parent SwipeView { id: swipeView Layout.fillHeight: true Layout.fillWidth: true interactive: isMobile currentIndex: currentView onCurrentItemChanged: currentItem.forceActiveFocus() onCurrentIndexChanged: { currentView = currentIndex // if (!babeitView.isConnected && currentIndex === viewsIndex.vvave) // babeitView.logginDialog.open() } TracksView { id: tracksView Connections { target: tracksView onRowClicked: Player.addTrack(tracksView.list.get(index)) onQuickPlayTrack: Player.quickPlay(tracksView.list.get(index)) onPlayAll: { var query = Q.GET.allTracks mainPlaylist.list.clear() mainPlaylist.list.query = query Player.playAll() } onAppendAll: { mainPlaylist.list.appendQuery(Q.GET.allTracks) mainPlaylist.listView.positionViewAtEnd() } onQueueTrack: Player.queueTracks([tracksView.list.get(index)], index) } } AlbumsView { id: albumsView holder.emoji: "qrc:/assets/MusicBox.png" holder.isMask: false holder.title : "No Albums!" holder.body: "Add new music sources" holder.emojiSize: iconSizes.huge headBarTitle: count + qsTr(" albums") list.query: Albums.ALBUMS list.sortBy: Albums.ALBUM Connections { target: albumsView onRowClicked: 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.playAll() } onPlayAll: { var query = Q.GET.albumTracks_.arg(album) query = query.arg(artist) query = query.arg(data.artist) mainPlaylist.list.clear() mainPlaylist.list.query = query Player.playAll() } onAppendAll: { var query = Q.GET.albumTracks_.arg(album) query = query.arg(artist) mainPlaylist.list.appendQuery(query) mainPlaylist.listView.positionViewAtEnd() } } } 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: iconSizes.huge headBarTitle: count + qsTr(" artists") list.query: Albums.ARTISTS list.sortBy: Albums.ARTIST table.list.sortBy: Tracks.NONE Connections { target: artistsView onRowClicked: 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.playAll() } onPlayAll: { var query = Q.GET.artistTracks_.arg(artist) query = query.arg(data.artist) mainPlaylist.list.clear() mainPlaylist.list.sortBy = Tracks.NONE mainPlaylist.list.query = query Player.playAll() } onAppendAll: { var query = Q.GET.artistTracks_.arg(artist) mainPlaylist.list.appendQuery(query) mainPlaylist.listView.positionViewAtEnd() } } } PlaylistsView { id: playlistsView Connections { target: playlistsView onRowClicked: Player.addTrack(track) onQuickPlayTrack: Player.quickPlay(track) onPlayAll: { var query = playlistsView.playlistQuery mainPlaylist.list.clear() mainPlaylist.list.sortBy = Tracks.NONE mainPlaylist.list.query = query Player.playAll() } onAppendAll: { var query = playlistsView.playlistQuery mainPlaylist.list.appendQuery(query) mainPlaylist.listView.positionViewAtEnd() } onPlaySync: { var query = playlistsView.playlistQuery mainPlaylist.list.appendQuery(query) Player.playAll() root.sync = true root.syncPlaylist = playlist root.infoMsg = qsTr("Syncing to ") + playlist } } } SearchTable { id: searchView Connections { target: searchView onRowClicked: Player.addTrack(searchView.list.get(index)) onQuickPlayTrack: Player.quickPlay(searchView.list.get(index)) onPlayAll: { mainPlaylist.list.clear() var tracks = searchView.list.getAll() for(var i in tracks) Player.appendTrack(tracks[i]) Player.playAll() } onAppendAll: Player.appendAll(searchView.list.getAll()) onArtworkDoubleClicked: { var query = Q.GET.albumTracks_.arg( searchView.searchTable.model.get( index).album) query = query.arg(searchView.searchTable.model.get( index).artist) mainPlaylist.list.clear() mainPlaylist.list.sortBy = Tracks.NONE mainPlaylist.list.query = query Player.playAll() } } } FoldersView { id: foldersView Connections { target: foldersView.list onRowClicked: Player.addTrack(foldersView.list.model.get(index)) onQuickPlayTrack: Player.quickPlay(foldersView.list.model.get(index)) onPlayAll: { mainPlaylist.list.clear() // mainPlaylist.list.sortBy = Tracks.NONE mainPlaylist.list.query = foldersView.list.list.query Player.playAll() } onAppendAll: { var query = foldersView.list.list.query mainPlaylist.list.appendQuery(query) mainPlaylist.listView.positionViewAtEnd() } onQueueTrack: Player.queueTracks([foldersView.list.model.get(index)], index) } } // CloudView // { // id: cloudView // onQuickPlayTrack: Player.quickPlay(cloudView.list.get(index)) // } // BabeitView // { // id: babeitView // } // LinkingView // { // id: linkingView // } YouTube { id: youtubeView } // Spotify // { // id: spotifyView // } } Maui.SelectionBar { id: _selectionBar property alias listView: _selectionBar.selectionList Layout.fillWidth: true Layout.margins: space.big Layout.topMargin: space.small Layout.bottomMargin: space.big onIconClicked: _contextMenu.popup() onExitClicked: clear() colorScheme.backgroundColor: "#212121" model: BaseModel { list: _selectionBarModelList } Tracks { id: _selectionBarModelList } SelectionBarMenu { id: _contextMenu } function append(item) { if(selectedPaths.indexOf(item.path) < 0) { selectedItems.push(item) selectedPaths.push(item.path) // for(var i = 0; i < selectionList.count ; i++ ) // if(selectionList.model.get(i).path === item.path) // { // selectionList.model.remove(i) // return // } selectionList.model.list.append(item) selectionList.positionViewAtEnd() if(position === Qt.Vertical) return if(typeof(riseContent) === "undefined") return riseContent() } } } } /*animations*/ /*FUNCTIONS*/ function infoMsgAnim() { animBg.running = true animTxt.running = true } function toggleMaximized() { if (root.visibility === Window.Maximized) { root.showNormal(); } else { root.showMaximized(); } } /*CONNECTIONS*/ Connections { target: vvave onRefreshTables: H.refreshCollection(size) 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) } } } diff --git a/models/albums/albumsmodel.cpp b/models/albums/albumsmodel.cpp index aaa0250..a187936 100644 --- a/models/albums/albumsmodel.cpp +++ b/models/albums/albumsmodel.cpp @@ -1,276 +1,276 @@ #include "albumsmodel.h" #include "db/collectionDB.h" #include "utils/brain.h" #include -AlbumsModel::AlbumsModel(QObject *parent) : BaseList(parent) +AlbumsModel::AlbumsModel(QObject *parent) : BaseList(parent), + db(CollectionDB::getInstance()) { - this->db = CollectionDB::getInstance(); connect(this, &AlbumsModel::queryChanged, this, &AlbumsModel::setList); } AlbumsModel::~AlbumsModel() {} FMH::MODEL_LIST AlbumsModel::items() const { return this->list; } void AlbumsModel::setQuery(const QUERY &query) { if(this->query == query) return; this->query = query; emit this->queryChanged(); } AlbumsModel::QUERY AlbumsModel::getQuery() const { return this->query; } void AlbumsModel::setSortBy(const SORTBY &sort) { if(this->sort == sort) return; this->sort = sort; emit this->preListChanged(); this->sortList(); emit this->postListChanged(); emit this->sortByChanged(); } AlbumsModel::SORTBY AlbumsModel::getSortBy() const { return this->sort; } void AlbumsModel::sortList() { const auto key = static_cast(this->sort); qDebug()<< "SORTING LIST BY"<< this->sort; std::sort(this->list.begin(), this->list.end(), [key](const FMH::MODEL &e1, const FMH::MODEL &e2) -> bool { const auto role = key; switch(role) { case FMH::MODEL_KEY::RELEASEDATE: { if(e1[role].toDouble() > e2[role].toDouble()) return true; break; } case FMH::MODEL_KEY::ADDDATE: { const auto date1 = QDateTime::fromString(e1[role], Qt::TextDate); const auto date2 = QDateTime::fromString(e2[role], Qt::TextDate); if(date1.secsTo(QDateTime::currentDateTime()) < date2.secsTo(QDateTime::currentDateTime())) return true; break; } case FMH::MODEL_KEY::ARTIST: case FMH::MODEL_KEY::ALBUM: { const auto str1 = QString(e1[role]).toLower(); const auto str2 = QString(e2[role]).toLower(); if(str1 < str2) return true; break; } default: if(e1[role] < e2[role]) return true; } return false; }); } void AlbumsModel::setList() { emit this->preListChanged(); QString m_Query; if(this->query == AlbumsModel::QUERY::ALBUMS) m_Query = "select * from albums order by album asc"; else if(this->query == AlbumsModel::QUERY::ARTISTS) m_Query = "select * from artists order by artist asc"; //get albums data with modifier for missing images for artworks this->list = this->db->getDBData(m_Query, [&](FMH::MODEL &item) { if(!item[FMH::MODEL_KEY::ARTWORK].isEmpty() && !FMH::fileExists(item[FMH::MODEL_KEY::ARTWORK])) { this->db->removeArtwork(FMH::MODEL_NAME[static_cast(this->query)], FM::toMap(item)); item[FMH::MODEL_KEY::ARTWORK] = ""; } }); this->sortList(); emit this->postListChanged(); } void AlbumsModel::fetchInformation() { qDebug() << "RNUNGING BRAIN EFFORRTS"; QFutureWatcher *watcher = new QFutureWatcher; QObject::connect(watcher, &QFutureWatcher::finished, watcher, &QFutureWatcher::deleteLater); // QObject::connect(this, &AlbumsModel::destroyed, watcher, &QFutureWatcher::waitForFinished); auto func = [&, stop = bool()]() mutable { stop = false; QList requests; int index = -1; for(auto album : this->list) { index++; if(!album[FMH::MODEL_KEY::ARTWORK].isEmpty()) continue; if(BAE::artworkCache(album, FMH::MODEL_KEY::ALBUM)) { this->db->insertArtwork(album); emit this->updateModel(index, {FMH::MODEL_KEY::ARTWORK}); continue; } PULPO::REQUEST request; request.track = album; request.ontology = this->query == AlbumsModel::QUERY::ALBUMS ? PULPO::ONTOLOGY::ALBUM : PULPO::ONTOLOGY::ARTIST; request.services = {PULPO::SERVICES::LastFm, PULPO::SERVICES::Spotify, PULPO::SERVICES::MusicBrainz}; request.info = {PULPO::INFO::ARTWORK}; request.callback = [&, index](PULPO::REQUEST request, PULPO::RESPONSES responses) { qDebug() << "DONE WITH " << request.track ; for(const auto &res : responses) { if(res.context == PULPO::CONTEXT::IMAGE && !res.value.toString().isEmpty()) { qDebug()<<"SAVING ARTWORK FOR: " << request.track[FMH::MODEL_KEY::ALBUM]; auto downloader = new FMH::Downloader; QObject::connect(downloader, &FMH::Downloader::fileSaved, [&, index, request, downloader = std::move(downloader)](QString path) { qDebug()<< "Saving artwork file to" << path; FMH::MODEL newTrack = request.track; newTrack[FMH::MODEL_KEY::ARTWORK] = path; this->db->insertArtwork(newTrack); this->updateArtwork(index, path); downloader->deleteLater(); }); QStringList filePathList = res.value.toString().split('/'); const auto format = "." + filePathList.at(filePathList.count() - 1).split(".").last(); QString name = !request.track[FMH::MODEL_KEY::ALBUM].isEmpty() ? request.track[FMH::MODEL_KEY::ARTIST] + "_" + request.track[FMH::MODEL_KEY::ALBUM] : request.track[FMH::MODEL_KEY::ARTIST]; name.replace("/", "-"); name.replace("&", "-"); downloader->setFile(res.value.toString(), BAE::CachePath + name + format); } } }; requests << request; } Pulpo pulpo; QEventLoop loop; QObject::connect(&pulpo, &Pulpo::finished, &loop, &QEventLoop::quit); QObject::connect(this, &AlbumsModel::destroyed, [&stop]() { qDebug()<< stop << &stop; stop = true; qDebug()<< stop << &stop; }); for(const auto &req : requests) { pulpo.request(req); if(stop) { qDebug()<< "TRYING EXITING THREAD LOADINFO" << loop.isRunning(); if(loop.isRunning()) loop.quit(); qDebug()<< "EXITING THREAD LOADINFO" << loop.isRunning(); break; }else { loop.exec(); } } }; QFuture t1 = QtConcurrent::run(func); watcher->setFuture(t1); } void AlbumsModel::updateArtwork(const int index, const QString &artwork) { if(index >= this->list.size() || index < 0) return; this->list[index][FMH::MODEL_KEY::ARTWORK] = artwork; emit this->updateModel(index, {FMH::MODEL_KEY::ARTWORK}); } QVariantMap AlbumsModel::get(const int &index) const { if(index >= this->list.size() || index < 0) return QVariantMap(); return FM::toMap(this->list.at(index)); } void AlbumsModel::append(const QVariantMap &item) { if(item.isEmpty()) return; emit this->preItemAppended(); FMH::MODEL model; for(auto key : item.keys()) model.insert(FMH::MODEL_NAME_KEY[key], item[key].toString()); this->list << model; emit this->postItemAppended(); } void AlbumsModel::append(const QVariantMap &item, const int &at) { if(item.isEmpty()) return; if(at > this->list.size() || at < 0) return; qDebug()<< "trying to append at" << at << item["title"]; emit this->preItemAppendedAt(at); FMH::MODEL model; for(auto key : item.keys()) model.insert(FMH::MODEL_NAME_KEY[key], item[key].toString()); this->list.insert(at, model); emit this->postItemAppended(); } void AlbumsModel::refresh() { this->setList(); } diff --git a/models/playlists/playlistsmodel.cpp b/models/playlists/playlistsmodel.cpp index cc10cf2..0ec5776 100644 --- a/models/playlists/playlistsmodel.cpp +++ b/models/playlists/playlistsmodel.cpp @@ -1,257 +1,257 @@ #include "playlistsmodel.h" #include "db/collectionDB.h" -PlaylistsModel::PlaylistsModel(QObject *parent) : BaseList(parent) +PlaylistsModel::PlaylistsModel(QObject *parent) : BaseList(parent), + db(CollectionDB::getInstance()) { - this->db = CollectionDB::getInstance(); this->setList(); } FMH::MODEL_LIST PlaylistsModel::items() const { return this->list; } void PlaylistsModel::setSortBy(const SORTBY &sort) { if(this->sort == sort) return; this->sort = sort; this->preListChanged(); this->sortList(); this->postListChanged(); emit this->sortByChanged(); } PlaylistsModel::SORTBY PlaylistsModel::getSortBy() const { return this->sort; } void PlaylistsModel::sortList() { const auto key = static_cast(this->sort); qDebug()<< "SORTING LIST BY"<< this->sort; qSort(this->list.begin() + this->defaultPlaylists().size(), this->list.end(), [key](const FMH::MODEL &e1, const FMH::MODEL &e2) -> bool { auto role = key; switch(role) { case FMH::MODEL_KEY::ADDDATE: { auto currentTime = QDateTime::currentDateTime(); auto date1 = QDateTime::fromString(e1[role], Qt::TextDate); auto date2 = QDateTime::fromString(e2[role], Qt::TextDate); if(date1.secsTo(currentTime) < date2.secsTo(currentTime)) return true; break; } case FMH::MODEL_KEY::TITLE: { const auto str1 = QString(e1[role]).toLower(); const auto str2 = QString(e2[role]).toLower(); if(str1 < str2) return true; break; } default: if(e1[role] < e2[role]) return true; } return false; }); } void PlaylistsModel::setList() { qDebug()<< "trying to set playlists list"; emit this->preListChanged(); this->list << this->db->getPlaylists(); this->list << this->defaultPlaylists(); // this->sortList(); emit this->postListChanged(); } FMH::MODEL PlaylistsModel::packPlaylist(const QString &playlist) { return FMH::MODEL { {FMH::MODEL_KEY::PLAYLIST, playlist}, {FMH::MODEL_KEY::ADDDATE, QDateTime::currentDateTime().toString(Qt::DateFormat::TextDate)} // {FMH::MODEL_KEY::ICON, "view-media-playlist"} }; } FMH::MODEL_LIST PlaylistsModel::defaultPlaylists() { return FMH::MODEL_LIST { { {FMH::MODEL_KEY::PLAYLIST, "Most Played"}, {FMH::MODEL_KEY::ICON, "view-media-playcount"}, {FMH::MODEL_KEY::ADDDATE,QDateTime::currentDateTime().toString(Qt::DateFormat::TextDate)} }, { {FMH::MODEL_KEY::PLAYLIST, "Rating"}, {FMH::MODEL_KEY::ICON, "view-media-favorite"}, {FMH::MODEL_KEY::ADDDATE,QDateTime::currentDateTime().toString(Qt::DateFormat::TextDate)} }, { {FMH::MODEL_KEY::PLAYLIST, "Recent"}, {FMH::MODEL_KEY::ICON, "view-media-recent"}, {FMH::MODEL_KEY::ADDDATE,QDateTime::currentDateTime().toString(Qt::DateFormat::TextDate)} }, { {FMH::MODEL_KEY::PLAYLIST, "Favs"}, {FMH::MODEL_KEY::ICON, "love"}, {FMH::MODEL_KEY::ADDDATE,QDateTime::currentDateTime().toString(Qt::DateFormat::TextDate)} }, { {FMH::MODEL_KEY::PLAYLIST, "Online"}, {FMH::MODEL_KEY::ICON, "internet-services"}, {FMH::MODEL_KEY::ADDDATE,QDateTime::currentDateTime().toString(Qt::DateFormat::TextDate)} }, // { // {FMH::MODEL_KEY::PLAYLIST, "Tags"}, // {FMH::MODEL_KEY::ICON, "tag"}, // {FMH::MODEL_KEY::ADDDATE,QDateTime::currentDateTime().toString(Qt::DateFormat::TextDate)} // }, // { // {FMH::MODEL_KEY::PLAYLIST, "Relationships"}, // {FMH::MODEL_KEY::ICON, "view-media-similarartists"}, // {FMH::MODEL_KEY::ADDDATE,QDateTime::currentDateTime().toString(Qt::DateFormat::TextDate)} // }, // { // {FMH::MODEL_KEY::PLAYLIST, "Popular"}, // {FMH::MODEL_KEY::ICON, "view-media-chart"}, // {FMH::MODEL_KEY::ADDDATE,QDateTime::currentDateTime().toString(Qt::DateFormat::TextDate)} // }, // { // {FMH::MODEL_KEY::PLAYLIST, "Genres"}, // {FMH::MODEL_KEY::ICON, "view-media-genre"}, // {FMH::MODEL_KEY::ADDDATE,QDateTime::currentDateTime().toString(Qt::DateFormat::TextDate)} // } }; } QVariantMap PlaylistsModel::get(const int &index) const { if(index >= this->list.size() || index < 0) return QVariantMap(); return FM::toMap(this->list.at(index)); } void PlaylistsModel::append(const QVariantMap &item) { if(item.isEmpty()) return; emit this->preItemAppended(); FMH::MODEL model; for(auto key : item.keys()) model.insert(FMH::MODEL_NAME_KEY[key], item[key].toString()); this->list << model; emit this->postItemAppended(); } void PlaylistsModel::append(const QVariantMap &item, const int &at) { if(item.isEmpty()) return; if(at > this->list.size() || at < 0) return; qDebug()<< "trying to append at" << at << item["title"]; emit this->preItemAppendedAt(at); FMH::MODEL model; for(auto key : item.keys()) model.insert(FMH::MODEL_NAME_KEY[key], item[key].toString()); this->list.insert(at, model); emit this->postItemAppended(); } void PlaylistsModel::insert(const QString &playlist) { if(playlist.isEmpty()) return; emit this->preItemAppended(); this->list << this->packPlaylist(playlist); emit this->postItemAppended(); } void PlaylistsModel::insertAt(const QString &playlist, const int &at) { if(playlist.isEmpty()) return; if(at > this->list.size() || at < 0) return; emit this->preItemAppendedAt(at); if(this->db->addPlaylist(playlist)) this->list.insert(at, this->packPlaylist(playlist)); emit this->postItemAppended(); } void PlaylistsModel::addTrack(const int &index, const QStringList &urls) { if(index >= this->list.size() || index < 0) return; for(auto url : urls) this->db->trackPlaylist(url, this->list[index][FMH::MODEL_KEY::PLAYLIST]); } void PlaylistsModel::removeTrack(const int &index, const QString &url) { if(index >= this->list.size() || index < 0) return; this->db->removePlaylistTrack(url, this->list.at(index)[FMH::MODEL_KEY::PLAYLIST]); } void PlaylistsModel::removePlaylist(const int &index) { if(index >= this->list.size() || index < 0) return; if(this->db->removePlaylist(this->list.at(index)[FMH::MODEL_KEY::PLAYLIST])) { emit this->preItemRemoved(index); this->list.removeAt(index); emit this->postItemRemoved(); } } diff --git a/models/tracks/tracksmodel.cpp b/models/tracks/tracksmodel.cpp index e5ee9b4..67abf80 100644 --- a/models/tracks/tracksmodel.cpp +++ b/models/tracks/tracksmodel.cpp @@ -1,341 +1,341 @@ #include "tracksmodel.h" #include "db/collectionDB.h" -TracksModel::TracksModel(QObject *parent) : BaseList(parent) +TracksModel::TracksModel(QObject *parent) : BaseList(parent), + db(CollectionDB::getInstance()) { - this->db = CollectionDB::getInstance(); connect(this, &TracksModel::queryChanged, this, &TracksModel::setList); } FMH::MODEL_LIST TracksModel::items() const { return this->list; } void TracksModel::setQuery(const QString &query) { if(this->query == query) return; this->query = query; emit this->queryChanged(); } QString TracksModel::getQuery() const { return this->query; } void TracksModel::setSortBy(const SORTBY &sort) { if(this->sort == sort) return; this->sort = sort; emit this->preListChanged(); this->sortList(); emit this->postListChanged(); emit this->sortByChanged(); } TracksModel::SORTBY TracksModel::getSortBy() const { return this->sort; } void TracksModel::sortList() { if(this->sort == TracksModel::SORTBY::NONE) return; const auto key = static_cast(this->sort); qSort(this->list.begin(), this->list.end(), [key](const FMH::MODEL &e1, const FMH::MODEL &e2) -> bool { switch(key) { case FMH::MODEL_KEY::RELEASEDATE: case FMH::MODEL_KEY::RATE: case FMH::MODEL_KEY::FAV: case FMH::MODEL_KEY::COUNT: { if(e1[key].toInt() > e2[key].toInt()) return true; break; } case FMH::MODEL_KEY::TRACK: { if(e1[key].toInt() < e2[key].toInt()) return true; break; } case FMH::MODEL_KEY::ADDDATE: { auto currentTime = QDateTime::currentDateTime(); auto date1 = QDateTime::fromString(e1[key], Qt::TextDate); auto date2 = QDateTime::fromString(e2[key], Qt::TextDate); if(date1.secsTo(currentTime) < date2.secsTo(currentTime)) return true; break; } case FMH::MODEL_KEY::TITLE: case FMH::MODEL_KEY::ARTIST: case FMH::MODEL_KEY::ALBUM: case FMH::MODEL_KEY::FORMAT: { const auto str1 = QString(e1[key]).toLower(); const auto str2 = QString(e2[key]).toLower(); if(str1 < str2) return true; break; } default: if(e1[key] < e2[key]) return true; } return false; }); } void TracksModel::setList() { emit this->preListChanged(); this->list = this->db->getDBData(this->query); this->sortList(); emit this->postListChanged(); } QVariantMap TracksModel::get(const int &index) const { if(index >= this->list.size() || index < 0) return QVariantMap(); return FM::toMap(this->list.at(index)); } QVariantList TracksModel::getAll() { QVariantList res; for(const auto &item : this->list) res << FM::toMap(item); return res; } void TracksModel::append(const QVariantMap &item) { if(item.isEmpty()) return; emit this->preItemAppended(); this->list << FM::toModel(item); emit this->postItemAppended(); } void TracksModel::append(const QVariantMap &item, const int &at) { if(item.isEmpty()) return; if(at > this->list.size() || at < 0) return; emit this->preItemAppendedAt(at); this->list.insert(at, FM::toModel(item)); emit this->postItemAppended(); } void TracksModel::appendQuery(const QString &query) { emit this->preListChanged(); this->list << this->db->getDBData(query); emit this->postListChanged(); } void TracksModel::searchQueries(const QStringList &queries) { emit this->preListChanged(); this->list.clear(); bool hasKey = false; for(auto searchQuery : queries) { if(searchQuery.contains(BAE::SearchTMap[BAE::SearchT::LIKE]+":") || searchQuery.startsWith("#")) { if(searchQuery.startsWith("#")) searchQuery = searchQuery.replace("#","").trimmed(); else searchQuery = searchQuery.replace(BAE::SearchTMap[BAE::SearchT::LIKE]+":","").trimmed(); searchQuery = searchQuery.trimmed(); if(!searchQuery.isEmpty()) { this->list << this->db->getSearchedTracks(FMH::MODEL_KEY::WIKI, searchQuery); this->list << this->db->getSearchedTracks(FMH::MODEL_KEY::TAG, searchQuery); this->list << this->db->getSearchedTracks(FMH::MODEL_KEY::LYRICS, searchQuery); } }else if(searchQuery.contains((BAE::SearchTMap[BAE::SearchT::SIMILAR]+":"))) { searchQuery=searchQuery.replace(BAE::SearchTMap[BAE::SearchT::SIMILAR]+":","").trimmed(); searchQuery=searchQuery.trimmed(); if(!searchQuery.isEmpty()) this->list << this->db->getSearchedTracks(FMH::MODEL_KEY::TAG, searchQuery); }else { FMH::MODEL_KEY key; QHashIterator k(FMH::MODEL_NAME); while (k.hasNext()) { k.next(); if(searchQuery.contains(QString(k.value()+":"))) { hasKey=true; key=k.key(); searchQuery = searchQuery.replace(k.value()+":","").trimmed(); } } searchQuery = searchQuery.trimmed(); if(!searchQuery.isEmpty()) { if(hasKey) this->list << this->db->getSearchedTracks(key, searchQuery); else { auto queryTxt = QString("SELECT t.*, al.artwork FROM tracks t INNER JOIN albums al ON t.album = al.album AND t.artist = al.artist WHERE t.title LIKE \"%"+searchQuery+"%\" OR t.artist LIKE \"%"+searchQuery+"%\" OR t.album LIKE \"%"+searchQuery+"%\"OR t.genre LIKE \"%"+searchQuery+"%\"OR t.url LIKE \"%"+searchQuery+"%\" ORDER BY strftime(\"%s\", t.addDate) desc LIMIT 1000"); this->list << this->db->getDBData(queryTxt); } } } } emit this->postListChanged(); } void TracksModel::clear() { emit this->preListChanged(); this->list.clear(); emit this->postListChanged(); } bool TracksModel::color(const int &index, const QString &color) { if(index >= this->list.size() || index < 0) return false; auto item = this->list[index]; if(this->db->colorTagTrack(item[FMH::MODEL_KEY::URL], color)) { this->list[index][FMH::MODEL_KEY::COLOR] = color; emit this->updateModel(index, {FMH::MODEL_KEY::COLOR}); return true; } return false; } bool TracksModel::fav(const int &index, const bool &value) { if(index >= this->list.size() || index < 0) return false; auto item = this->list[index]; if(this->db->favTrack(item[FMH::MODEL_KEY::URL], value)) { this->list[index][FMH::MODEL_KEY::FAV] = value ? "1" : "0"; emit this->updateModel(index, {FMH::MODEL_KEY::FAV}); qDebug()<< "FAVVING TRACKS"<< item; return true; } return false; } bool TracksModel::rate(const int &index, const int &value) { if(index >= this->list.size() || index < 0) return false; auto item = this->list[index]; if(this->db->rateTrack(item[FMH::MODEL_KEY::URL], value)) { this->list[index][FMH::MODEL_KEY::RATE] = QString::number(value); emit this->updateModel(index, {FMH::MODEL_KEY::RATE}); return true; } return false; } bool TracksModel::countUp(const int &index) { if(index >= this->list.size() || index < 0) return false; auto item = this->list[index]; if(this->db->playedTrack(item[FMH::MODEL_KEY::URL])) { this->list[index][FMH::MODEL_KEY::COUNT] = QString::number(item[FMH::MODEL_KEY::COUNT].toInt() + 1); emit this->updateModel(index, {FMH::MODEL_KEY::COUNT}); return true; } return false; } bool TracksModel::remove(const int &index) { if(index >= this->list.size() || index < 0) return false; emit this->preItemRemoved(index); this->list.removeAt(index); emit this->postItemRemoved(); return true; } void TracksModel::refresh() { this->setList(); } bool TracksModel::update(const QVariantMap &data, const int &index) { if(index >= this->list.size() || index < 0) return false; auto newData = this->list[index]; QVector roles; for(auto key : data.keys()) if(newData[FMH::MODEL_NAME_KEY[key]] != data[key].toString()) { newData.insert(FMH::MODEL_NAME_KEY[key], data[key].toString()); roles << FMH::MODEL_NAME_KEY[key]; } this->list[index] = newData; emit this->updateModel(index, roles); return true; } diff --git a/services/local/player.cpp b/services/local/player.cpp index a19288b..eb663e1 100644 --- a/services/local/player.cpp +++ b/services/local/player.cpp @@ -1,205 +1,204 @@ #include "player.h" #include "../../utils/bae.h" -Player::Player(QObject *parent) : QObject(parent) +Player::Player(QObject *parent) : QObject(parent), + player(new QMediaPlayer(this)), + updater(new QTimer(this)) { - this->player = new QMediaPlayer(this); this->buffer = new QBuffer(this->player); - connect(this->player, &QMediaPlayer::durationChanged, this, [&](qint64 dur) - { - emit this->durationChanged(/*BAE::transformTime(dur/1000)*/); - }); +// connect(this->player, &QMediaPlayer::durationChanged, this, [&](qint64 dur) +// { +// emit this->durationChanged(/*BAE::transformTime(dur/1000)*/); +// }); this->player->setVolume(this->volume); - - this->updater = new QTimer(this); connect(this->updater, &QTimer::timeout, this, &Player::update); } -bool Player::play() +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() +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) { - auto time = BAE::transformTime(pos); - return time; + return BAE::transformTime(pos); } + void Player::appendBuffe(QByteArray &array) { qDebug()<<"APENDING TO BUFFER"<< array << this->array; this->array.append(array, array.length()); amountBuffers++; if(amountBuffers == 1) playBuffer(); } void Player::playRemote(const QString &url) { qDebug()<<"Trying to play remote"<url = url; this->player->setMedia(QUrl::fromUserInput(url)); this->play(); } void Player::setUrl(const QString &value) { if(value == this->url) return; this->url = value; emit this->urlChanged(); this->pos = 0; emit this->posChanged(); - auto media = QMediaContent(QUrl::fromLocalFile(this->url)); + const auto media = QMediaContent(QUrl::fromLocalFile(this->url)); this->player->setMedia(media); this->emitState(); } QString 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->player->setPosition(this->player->duration() / 1000 * this->pos); this->emitState(); this->posChanged(); } int Player::getPos() const { return this->pos; } void Player::playBuffer() { buffer->setData(array); buffer->open(QIODevice::ReadOnly); if(!buffer->isReadable()) qDebug()<<"Cannot read buffer"; player->setMedia(QMediaContent(),buffer); this->url = "buffer"; this->play(); this->emitState(); } void Player::update() { if(this->player->isAvailable()) { this->pos = static_cast(static_cast(this->player->position())/this->player->duration()*1000);; 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/services/local/player.h b/services/local/player.h index df48d15..3b1ba61 100644 --- a/services/local/player.h +++ b/services/local/player.h @@ -1,91 +1,91 @@ #ifndef PLAYER_H #define PLAYER_H #include #include #include #include class Player : public QObject { Q_OBJECT Q_PROPERTY(QString url READ getUrl WRITE setUrl NOTIFY urlChanged) Q_PROPERTY(int volume READ getVolume WRITE setVolume NOTIFY volumeChanged) Q_PROPERTY(Player::STATE state READ getState NOTIFY stateChanged) Q_PROPERTY(int duration READ getDuration NOTIFY durationChanged) Q_PROPERTY(bool playing READ getPlaying WRITE setPlaying NOTIFY playingChanged) Q_PROPERTY(bool finished READ getFinished NOTIFY finishedChanged) Q_PROPERTY(int pos READ getPos WRITE setPos NOTIFY posChanged) public: enum STATE : uint_fast8_t { PLAYING, PAUSED, STOPED, ERROR };Q_ENUM(STATE) explicit Player(QObject *parent = nullptr); void playBuffer(); Q_INVOKABLE void appendBuffe(QByteArray &array); void setUrl(const QString &value); QString getUrl() const; void setVolume(const int &value); int getVolume() const; int getDuration() const; Player::STATE getState() const; void setPlaying(const bool &value); bool getPlaying() const; bool getFinished(); int getPos() const; void setPos(const int &value); private: QMediaPlayer *player; QTimer *updater; int amountBuffers =0; void update(); QBuffer *buffer; QByteArray array; QString url; int volume = 100; Player::STATE state = STATE::STOPED; bool playing = false; bool finished = false; - bool play(); - void pause(); + bool play() const; + void pause() const; void emitState(); int pos = 0; signals: void durationChanged(); void urlChanged(); void volumeChanged(); void stateChanged(); void playingChanged(); void finishedChanged(); void posChanged(); public slots: - QString transformTime(const int &pos); + static QString transformTime(const int &pos); void playRemote(const QString &url); void stop(); }; #endif // PLAYER_H diff --git a/view_models/BabeTable/BabeTable.qml b/view_models/BabeTable/BabeTable.qml index a8be554..d9d3b03 100644 --- a/view_models/BabeTable/BabeTable.qml +++ b/view_models/BabeTable/BabeTable.qml @@ -1,445 +1,445 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.6 as Kirigami import org.kde.mauikit 1.0 as Maui import BaseModel 1.0 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: babeTableRoot // cacheBuffer : 300 property alias list : _tracksList property alias listModel : _tracksModel property alias removeDialog : _removeDialog property bool trackNumberVisible property bool quickPlayVisible : true property bool coverArtVisible : false property bool menuItemVisible : isMobile property bool trackDuration property bool trackRating property bool allowMenu: true property bool isArtworkRemote : false property bool showIndicator : false property bool group : false property alias contextMenu : contextMenu - property alias contextMenuItems : contextMenu.content + property alias contextMenuItems : contextMenu.contentData property alias playAllBtn : playAllBtn property alias appendBtn : appendBtn signal rowClicked(int index) signal rowPressed(int index) signal quickPlayTrack(int index) signal queueTrack(int index) signal artworkDoubleClicked(int index) signal playAll() signal appendAll() // altToolBars: true onGroupChanged: groupBy() focus: true //headBar.middleStrech: false headBar.leftSretch: false headBar.rightContent: Kirigami.ActionToolBar { actions: [ Kirigami.Action { id : playAllBtn text: qsTr("Play all") icon.name : "media-playlist-play" onTriggered: playAll() }, Kirigami.Action { id: appendBtn text: qsTr("Append") icon.name : "media-playlist-append"//"media-repeat-track-amarok" onTriggered: appendAll() }, Kirigami.Action { id: sortBtn text: qsTr("Sort") icon.name: "view-sort" Kirigami.Action { text: qsTr("Title") checkable: true checked: list.sortBy === Tracks.TITLE onTriggered: list.sortBy = Tracks.TITLE } // Kirigami.Action // { // text: qsTr("Track") // checkable: true // checked: list.sortBy === Tracks.TRACK // onTriggered: list.sortBy = Tracks.TRACK // } // Kirigami.Action // { // text: qsTr("Artist") // checkable: true // checked: list.sortBy === Tracks.ARTIST // onTriggered: list.sortBy = Tracks.ARTIST // } // Kirigami.Action // { // text: qsTr("Album") // checkable: true // checked: list.sortBy === Tracks.ALBUM // onTriggered: list.sortBy = Tracks.ALBUM // } // Kirigami.Action // { // text: qsTr("Most played") // checkable: true // checked: list.sortBy === Tracks.COUNT // onTriggered: list.sortBy = Tracks.COUNT // } // Kirigami.Action // { // text: qsTr("Rate") // checkable: true // checked: list.sortBy === Tracks.RATE // onTriggered: list.sortBy = Tracks.RATE // } // Kirigami.Action // { // text: qsTr("Fav") // checkable: true // checked: list.sortBy === Tracks.FAV // onTriggered: list.sortBy = Tracks.FAV // } // Kirigami.Action // { // text: qsTr("Release date") // checkable: true // checked: list.sortBy === Tracks.RELEASEDATE // onTriggered: list.sortBy = Tracks.RELEASEDATE // } // Kirigami.Action // { // text: qsTr("Add date") // checkable: true // checked: list.sortBy === Tracks.ADDDATE // onTriggered: list.sortBy = Tracks.ADDDATE // } // Kirigami.Action // { // text: qsTr("Group") // checkable: true // checked: group // onTriggered: group = !group // } }, Kirigami.Action { text: qsTr("Select") icon.name: "item-select" onTriggered: selectionMode = !selectionMode checkable: false checked: selectionMode } ] } 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") // rejectButton.icon.name: "archive-remove" acceptButton.text: qsTr("Remove") onAccepted: { list.remove(listView.currentIndex) close() } onRejected: { if(Maui.FM.removeFile(list.get(index).url)) list.remove(listView.currentIndex) close() } } TableMenu { id: contextMenu MenuSeparator {} - Maui.MenuItem + MenuItem { text: qsTr("Go to Artist") onTriggered: goToArtist() } - Maui.MenuItem + 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)]) onSaveToClicked: { playlistDialog.tracks = [list.get(listView.currentIndex).url] playlistDialog.open() } onOpenWithClicked: Maui.FM.openLocation([list.get(listView.currentIndex).url]) onRemoveClicked: { _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: { isAndroid ? Maui.Android.shareDialog(list.get(listView.currentIndex)) : shareDialog.show([list.get(listView.currentIndex).url]) } } section.criteria: ViewSection.FullString section.delegate: Maui.LabelDelegate { id: _sectionDelegate label: section isSection: true boldLabel: true colorScheme.backgroundColor: "#333" colorScheme.textColor: "#fafafa" background: Rectangle { color: colorScheme.backgroundColor } } BaseModel { id: _tracksModel list: _tracksList } Tracks { id: _tracksList onSortByChanged: if(babeTableRoot.group) babeTableRoot.groupBy() } model: _tracksModel // property alias animBabe: delegate.animBabe delegate: TableDelegate { id: delegate width: listView.width number : trackNumberVisible ? true : false quickPlay: quickPlayVisible coverArt : coverArtVisible ? (babeTableRoot.width > 300) : coverArtVisible trackDurationVisible : trackDuration trackRatingVisible : trackRating menuItem: menuItemVisible remoteArtwork: isArtworkRemote playingIndicator: showIndicator onPressAndHold: if(isMobile && allowMenu) openItemMenu(index) onRightClicked: if(allowMenu) openItemMenu(index) onClicked: { currentIndex = index if(selectionMode) { H.addToSelection(listView.model.get(listView.currentIndex)) return } if(isMobile) rowClicked(index) } onDoubleClicked: { currentIndex = index if(!isMobile) rowClicked(index) } onPlay: { currentIndex = index quickPlayTrack(index) } onArtworkCoverClicked: { currentIndex = index goToAlbum() } } function openItemMenu(index) { currentIndex = index contextMenu.rate = list.get(currentIndex).rate contextMenu.fav = list.get(currentIndex).fav == "1" contextMenu.popup() rowPressed(index) console.log(list.get(currentIndex).fav) } function saveList() { var trackList = [] if(model.count > 0) { for(var i = 0; i < model.count; ++i) trackList.push(model.get(i).url) playlistDialog.tracks = trackList playlistDialog.open() } } function queueList() { var trackList = [] if(model.count > 0) { for(var i = 0; i < model.count; ++i) trackList.push(model.get(i)) Player.queueTracks(trackList) } } function goToAlbum() { root.currentView = viewsIndex.albums var item = listView.model.get(listView.currentIndex) albumsView.populateTable(item.album, item.artist) contextMenu.close() } function goToArtist() { root.currentView = viewsIndex.artists var item = listView.model.get(listView.currentIndex) artistsView.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/TableMenu.qml b/view_models/BabeTable/TableMenu.qml index 9f4de43..e7d91aa 100644 --- a/view_models/BabeTable/TableMenu.qml +++ b/view_models/BabeTable/TableMenu.qml @@ -1,256 +1,257 @@ import QtQuick 2.0 import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import "../../utils" import ".." import "../../utils/Help.js" as H import org.kde.mauikit 1.0 as Maui +import org.kde.kirigami 2.6 as Kirigami -Maui.Menu +Menu { id: control width: unit * 200 property int rate : 0 property bool fav : false property string starColor : "#FFC107" property string starReg : textColor property string starIcon: "draw-star" signal removeClicked() signal favClicked() signal queueClicked() signal saveToClicked() signal openWithClicked() signal editClicked() signal shareClicked() signal selectClicked() signal rateClicked(int rate) signal colorClicked(color color) signal infoClicked() signal copyToClicked() - property alias menuItem : control.content + property alias menuItem : control.contentData - Maui.MenuItem + MenuItem { text: qsTr("Select...") onTriggered: { H.addToSelection(listView.model.get(listView.currentIndex)) contextMenu.close() } } MenuSeparator {} - Maui.MenuItem + MenuItem { text: !fav ? qsTr("Fav it"): qsTr("UnFav it") onTriggered: { favClicked() close() } } - Maui.MenuItem + MenuItem { text: qsTr("Queue") onTriggered: { queueClicked() close() } } - Maui.MenuItem + MenuItem { text: qsTr("Add to...") onTriggered: { saveToClicked() close() } } MenuSeparator {} - Maui.MenuItem + MenuItem { text: qsTr("Share...") onTriggered: { shareClicked() close() } } - Maui.MenuItem + MenuItem { visible: root.showAccounts text: qsTr("Copy to cloud") onTriggered: { copyToClicked() close() } } - Maui.MenuItem + MenuItem { text: isAndroid ? qsTr("Open with...") : qsTr("Show in folder...") onTriggered: { openWithClicked() close() } } MenuSeparator {} - Maui.MenuItem + MenuItem { visible: false text: qsTr("Edit...") onTriggered: { editClicked() close() } } // Maui.MenuItem // { // text: qsTr("Info...") // onTriggered: // { // infoClicked() // close() // } // } - Maui.MenuItem + MenuItem { text: qsTr("Remove") - colorScheme.textColor: dangerColor + Kirigami.Theme.textColor: dangerColor onTriggered: { removeClicked() // listModel.remove(list.currentIndex) close() } } MenuSeparator {} - Maui.MenuItem + MenuItem { id: starsRow width: parent.width height: iconSizes.medium + space.small RowLayout { anchors.fill: parent Maui.ToolButton { Layout.fillWidth: true Layout.fillHeight: true iconName: starIcon size: iconSizes.medium iconColor: rate >= 1 ? starColor :starReg onClicked: { rate = 1 rateClicked(rate) close() } } Maui.ToolButton { Layout.fillWidth: true Layout.fillHeight: true size: iconSizes.medium iconName: starIcon iconColor: rate >= 2 ? starColor :starReg onClicked: { rate = 2 rateClicked(rate) close() } } Maui.ToolButton { Layout.fillWidth: true Layout.fillHeight: true size: iconSizes.medium iconName: starIcon iconColor: rate >= 3 ? starColor :starReg onClicked: { rate = 3 rateClicked(rate) close() } } Maui.ToolButton { Layout.fillWidth: true Layout.fillHeight: true size: iconSizes.medium iconName: starIcon iconColor: rate >= 4 ? starColor :starReg onClicked: { rate = 4 rateClicked(rate) close() } } Maui.ToolButton { Layout.fillWidth: true Layout.fillHeight: true size: iconSizes.medium iconName: starIcon iconColor: rate >= 5 ? starColor :starReg onClicked: { rate = 5 rateClicked(rate) close() } } } } - Maui.MenuItem + MenuItem { id: colorsRow width: parent.width height: iconSizes.medium + space.small ColorTagsBar { anchors.fill: parent onColorClicked: { control.colorClicked(color) control.close() } } } } diff --git a/vvave.cpp b/vvave.cpp index 5a4b413..779856d 100644 --- a/vvave.cpp +++ b/vvave.cpp @@ -1,162 +1,162 @@ #include "vvave.h" #include "db/collectionDB.h" #include "settings/fileloader.h" #include "utils/brain.h" #if (defined (Q_OS_LINUX) && !defined (Q_OS_ANDROID)) #include "kde/notify.h" #endif #include #include #ifdef STATIC_MAUIKIT #include "fm.h" #else #include #endif /* * Sets upthe app default config paths * BrainDeamon to get collection information * YoutubeFetcher ? * * */ -vvave::vvave(QObject *parent) : QObject(parent) +vvave::vvave(QObject *parent) : QObject(parent), + notify(new Notify(this)), + db(CollectionDB::getInstance()) { - this->db = CollectionDB::getInstance(); for(const auto &path : {BAE::CachePath, BAE::YoutubeCachePath}) { QDir dirPath(path); if (!dirPath.exists()) dirPath.mkpath("."); } #if (defined (Q_OS_LINUX) && !defined (Q_OS_ANDROID)) if(!FMH::fileExists(BAE::NotifyDir+"/vvave.notifyrc")) QFile::copy(":/assets/vvave.notifyrc", BAE::NotifyDir+"/vvave.notifyrc"); - this->notify = new Notify(this); connect(this->notify, &Notify::babeSong, [this]() { // emit this->babeIt(); }); connect(this->notify, &Notify::skipSong, [this]() { // emit this->skipTrack(); }); #endif } vvave::~vvave() {} void vvave::checkCollection(const QStringList &paths, std::function cb) { QFutureWatcher *watcher = new QFutureWatcher; connect(watcher, &QFutureWatcher::finished, [=]() { const uint newTracks = watcher->future().result(); if(cb) cb(newTracks); watcher->deleteLater(); }); const auto func = [=]() -> uint { auto newPaths = paths; for(auto path : newPaths) if(path.startsWith("file://")) path.replace("file://", ""); return FLoader::getTracks(newPaths); }; QFuture t1 = QtConcurrent::run(func); watcher->setFuture(t1); } //// PUBLIC SLOTS QVariantList vvave::sourceFolders() { const auto sources = this->db->getDBData("select * from sources"); QVariantList res; for(const auto &item : sources) res << FMH::getDirInfo(item[FMH::MODEL_KEY::URL]); return res; } bool vvave::removeSource(const QString &source) { if(!this->getSourceFolders().contains(source)) return false; return this->db->removeSource(source); } QString vvave::moodColor(const int &index) { if(index < BAE::MoodColors.size() && index > -1) return BAE::MoodColors.at(index); else return ""; } void vvave::scanDir(const QStringList &paths) { this->checkCollection(paths, [=](uint size) {emit this->refreshTables(size);}); } QStringList vvave::getSourceFolders() { return this->db->getSourcesFolders(); } void vvave::openUrls(const QStringList &urls) { if(urls.isEmpty()) return; QVariantList data; TagInfo info; for(const auto &url : urls) if(db->check_existance(BAE::TABLEMAP[BAE::TABLE::TRACKS], FMH::MODEL_NAME[FMH::MODEL_KEY::URL], url)) { data << FM::toMap(this->db->getDBData(QStringList() << url).first()); }else { if(info.feed(url)) { const auto album = BAE::fixString(info.getAlbum()); const auto track= info.getTrack(); const auto title = BAE::fixString(info.getTitle()); /* to fix*/ const auto artist = BAE::fixString(info.getArtist()); const auto genre = info.getGenre(); const auto sourceUrl = QFileInfo(url).dir().path(); const auto duration = info.getDuration(); const auto year = info.getYear(); data << QVariantMap({ {FMH::MODEL_NAME[FMH::MODEL_KEY::URL], url}, {FMH::MODEL_NAME[FMH::MODEL_KEY::TRACK], QString::number(track)}, {FMH::MODEL_NAME[FMH::MODEL_KEY::TITLE], title}, {FMH::MODEL_NAME[FMH::MODEL_KEY::ARTIST], artist}, {FMH::MODEL_NAME[FMH::MODEL_KEY::ALBUM], album}, {FMH::MODEL_NAME[FMH::MODEL_KEY::DURATION],QString::number(duration)}, {FMH::MODEL_NAME[FMH::MODEL_KEY::GENRE], genre}, {FMH::MODEL_NAME[FMH::MODEL_KEY::SOURCE], sourceUrl}, {FMH::MODEL_NAME[FMH::MODEL_KEY::FAV],"0"}, {FMH::MODEL_NAME[FMH::MODEL_KEY::RELEASEDATE], QString::number(year)} }); } } emit this->openFiles(data); } diff --git a/widgets/MainPlaylist/PlaylistMenu.qml b/widgets/MainPlaylist/PlaylistMenu.qml index 7c15e45..8a2474b 100644 --- a/widgets/MainPlaylist/PlaylistMenu.qml +++ b/widgets/MainPlaylist/PlaylistMenu.qml @@ -1,45 +1,45 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import org.kde.mauikit 1.0 as Maui -Maui.Menu +Menu { signal clearOut() signal clean() signal callibrate() signal hideCover() signal saveToClicked() - Maui.MenuItem + MenuItem { text: qsTr("Clear out...") onTriggered: clearOut() } - Maui.MenuItem + MenuItem { text: qsTr("Clean...") onTriggered: clean() } - Maui.MenuItem + MenuItem { text: qsTr("Save list to...") onTriggered: saveToClicked() } - Maui.MenuItem + MenuItem { enabled: syncPlaylist.length > 0 text: syncPlaylist.length > 0 && sync ? qsTr("Pause syncing") : qsTr("Continue syncing") onTriggered: sync = !sync } // Maui.MenuItem // { // text: qsTr("Playlist list...") // checkable: true // checked: mainPlaylistItem.visible // onTriggered: mainPlaylistItem.visible = !mainPlaylistItem.visible // } } diff --git a/widgets/PlaylistsView/PlaylistsView.qml b/widgets/PlaylistsView/PlaylistsView.qml index a1cc742..f3f068b 100644 --- a/widgets/PlaylistsView/PlaylistsView.qml +++ b/widgets/PlaylistsView/PlaylistsView.qml @@ -1,229 +1,229 @@ import QtQuick 2.9 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.2 import org.kde.kirigami 2.6 as Kirigami import org.kde.mauikit 1.0 as Maui import "../../view_models/BabeTable" import "../../view_models" import "../../db/Queries.js" as Q import "../../utils/Help.js" as H ColumnLayout { id: control clip: true spacing: 0 property string playlistQuery property alias playlistModel : playlistViewModel.model property alias playlistList : playlistViewModel.list property alias playlistViewList : playlistViewModel signal rowClicked(var track) signal quickPlayTrack(var track) signal playAll() signal playSync(var playlist) signal appendAll() SwipeView { id: playlistSwipe Layout.fillHeight: true Layout.fillWidth: true interactive: false clip: true PlaylistsViewModel { id: playlistViewModel onPlaySync: syncAndPlay(index) } BabeList { id: playlistViewModelFilter headBarExitIcon: "go-previous" model : ListModel {} delegate: Maui.LabelDelegate { id: delegate label : tag Connections { target: delegate onClicked: {} } } onExit: playlistSwipe.currentIndex = 0 } } ColorTagsBar { Layout.fillWidth: true height: rowHeightAlt recSize: isMobile ? iconSize : 16 onColorClicked: populate(Q.GET.colorTracks_.arg(color.toLowerCase())) } Maui.Dialog { id: _filterDialog parent: parent maxHeight: maxWidth maxWidth: unit * 600 defaultButtons: false page.margins: 0 BabeTable { id: filterList anchors.fill: parent clip: true quickPlayVisible: true coverArtVisible: true trackRating: true trackDuration: false headBar.visible: !holder.visible headBarExit: false headBarTitle: playlistViewModel.list.get(playlistViewModel.currentIndex).playlist holder.emoji: "qrc:/assets/Electricity.png" holder.isMask: false holder.title : playlistViewModel.model.get(playlistViewModel.currentIndex).playlist holder.body: "Your playlist is empty,
start adding new music to it" holder.emojiSize: iconSizes.huge contextMenuItems: - Maui.MenuItem + 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 boldLabel: true labelTxt.font.family: "Material Design Icons" } Connections { target: filterList onRowClicked: control.rowClicked(filterList.model.get(index)) onQuickPlayTrack: control.quickPlayTrack(filterList.model.get(filterList.currentIndex)) onPlayAll: playAll() onAppendAll: appendAll() onPulled: populate(playlistQuery) } Connections { target: filterList.contextMenu onRemoveClicked: { playlistList.removeTrack(playlistViewList.currentIndex, filterList.list.get(filterList.currentIndex).url) populate(playlistQuery) } } } } function populateExtra(query, title) { // playlistSwipe.currentIndex = 1 // var res = bae.get(query) // playlistViewModelFilter.clearTable() // playlistViewModelFilter.headBarTitle = title // appendToExtraList(res) } function appendToExtraList(res) { if(res.length>0) for(var i in res) playlistViewModelFilter.model.append(res[i]) } function populate(query) { playlistQuery = query filterList.list.query = playlistQuery _filterDialog.open() } function syncAndPlay(index) { if(!playlistList.get(index).playlistIcon) playSync(playlistList.get(index).playlist) } function removePlaylist() { playlistList.removePlaylist(playlistViewList.currentIndex) filterList.clearTable() } } diff --git a/widgets/SelectionBarMenu.qml b/widgets/SelectionBarMenu.qml index ab323b0..e6f40ef 100644 --- a/widgets/SelectionBarMenu.qml +++ b/widgets/SelectionBarMenu.qml @@ -1,214 +1,214 @@ import QtQuick 2.0 import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import "../utils" import ".." import "../utils/Help.js" as H import "../utils/Player.js" as Player import "../view_models" - +import org.kde.kirigami 2.6 as Kirigami import org.kde.mauikit 1.0 as Maui -Maui.Menu +Menu { id: control width: unit * 200 property int rate : 0 property string starColor : "#FFC107" property string starReg : textColor property string starIcon: "draw-star" signal rateClicked(int rate) - Maui.MenuItem + MenuItem { text: qsTr("Play...") onTriggered: { mainPlaylist.list.clear() var tracks = _selectionBarModelList.getAll() for(var i in tracks) Player.appendTrack(tracks[i]) Player.playAll() } } - Maui.MenuItem + MenuItem { text: qsTr("Append...") onTriggered: Player.appendAll(_selectionBarModelList.getAll()) } - Maui.MenuItem + MenuItem { text: qsTr("Queue") onTriggered: { Player.queueTracks(_selectionBarModelList.getAll()) close() } } MenuSeparator {} - Maui.MenuItem + MenuItem { text: qsTr("Fav/UnFav them") onTriggered: { for(var i= 0; i < _selectionBar.count; i++) _selectionBarModelList.fav(i, !(_selectionBarModelList.get(i).fav == "1")) } } - Maui.MenuItem + MenuItem { text: qsTr("Add to...") onTriggered: { playlistDialog.tracks = _selectionBar.selectedPaths playlistDialog.open() close() } } MenuSeparator {} - Maui.MenuItem + MenuItem { text: qsTr("Share...") onTriggered: { isAndroid ? Maui.Android.shareDialog(_selectionBar.selectedPaths) : shareDialog.show(_selectionBar.selectedPaths) close() } } MenuSeparator {} - Maui.MenuItem + MenuItem { text: qsTr("Remove") - colorScheme.textColor: dangerColor + Kirigami.Theme.textColor: dangerColor onTriggered: { close() } } MenuSeparator {} - Maui.MenuItem + MenuItem { id: starsRow width: parent.width height: iconSizes.medium + space.small RowLayout { anchors.fill: parent Maui.ToolButton { Layout.fillWidth: true Layout.fillHeight: true iconName: starIcon size: iconSizes.medium iconColor: rate >= 1 ? starColor :starReg onClicked: { rate = 1 } } Maui.ToolButton { Layout.fillWidth: true Layout.fillHeight: true size: iconSizes.medium iconName: starIcon iconColor: rate >= 2 ? starColor :starReg onClicked: { rate = 2 } } Maui.ToolButton { Layout.fillWidth: true Layout.fillHeight: true size: iconSizes.medium iconName: starIcon iconColor: rate >= 3 ? starColor :starReg onClicked: { rate = 3 } } Maui.ToolButton { Layout.fillWidth: true Layout.fillHeight: true size: iconSizes.medium iconName: starIcon iconColor: rate >= 4 ? starColor :starReg onClicked: { rate = 4 } } Maui.ToolButton { Layout.fillWidth: true Layout.fillHeight: true size: iconSizes.medium iconName: starIcon iconColor: rate >= 5 ? starColor :starReg onClicked: { rate = 5 } } } } onRateChanged: { close() for(var i= 0; i < _selectionBar.count; i++) _selectionBarModelList.rate(i, control.rate) } - Maui.MenuItem + MenuItem { id: colorsRow width: parent.width height: iconSizes.medium + space.small ColorTagsBar { anchors.fill: parent onColorClicked: { for(var i= 0; i < _selectionBar.count; i++) _selectionBarModelList.color(i, color) control.close() } } } }