diff --git a/main.qml b/main.qml index 3162d48..5dcead7 100644 --- a/main.qml +++ b/main.qml @@ -1,1040 +1,1040 @@ 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 FMList 1.0 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 : [ Maui.ToolButton { iconName: "view-media-track" active: currentView === viewsIndex.tracks showIndicator: true iconColor: active ? babeColor : altColorText onClicked: currentView = viewsIndex.tracks text: qsTr("Tracks") colorScheme.highlightColor: babeColor spacing: 0 }, Maui.ToolButton { text: qsTr("Albums") active: currentView === viewsIndex.albums iconName: /*"album"*/ "view-media-album-cover" iconColor: currentView === viewsIndex.albums ? babeColor : altColorText onClicked: currentView = viewsIndex.albums colorScheme.highlightColor: babeColor showIndicator: true }, Maui.ToolButton { text: qsTr("Artists") active: currentView === viewsIndex.artists iconName: "view-media-artist" iconColor: currentView === viewsIndex.artists ? babeColor : altColorText onClicked: currentView = viewsIndex.artists colorScheme.highlightColor: babeColor showIndicator: true }, Maui.ToolButton { text: qsTr("Playlists") active: currentView === viewsIndex.playlists iconName: "view-media-playlist" iconColor: currentView === viewsIndex.playlists ? babeColor : altColorText onClicked: 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 { iconName: "headphones" visible: _drawer.modal iconColor: _drawer.visible ? babeColor : textColor onClicked: _drawer.visible = !_drawer.visible colorScheme.highlightColor: babeColor // text: qsTr("Now") } middleContent: [ Maui.ToolButton { id: babeBtnIcon iconName: "love" iconColor: 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 { iconName: "media-skip-backward" iconColor: textColor onClicked: Player.previousTrack() onPressAndHold: Player.playAt(prevTrackIndex) }, Maui.ToolButton { id: playIcon iconColor: textColor iconName: isPlaying ? "media-playback-pause" : "media-playback-start" onClicked: player.playing = !player.playing }, Maui.ToolButton { id: nextBtn iconColor: textColor iconName: "media-skip-forward" onClicked: Player.nextTrack() onPressAndHold: Player.playAt(Player.shuffle()) }, Maui.ToolButton { id: shuffleBtn iconColor: babeColor iconName: 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("Folders") icon.name: "folder" onTriggered: currentView = viewsIndex.folders }, // 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("YouTube") icon.name: "internet-services" onTriggered: currentView = viewsIndex.youtube }, // 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{}, Maui.MenuItem { text: qsTr("Sources...") icon.name: "folder-add" onTriggered: sourcesDialog.open() }, Maui.MenuItem { text: qsTr("Open...") icon.name: "folder-add" onTriggered: { fmDialog.onlyDirs = false fmDialog.filterType = FMList.AUDIO fmDialog.show(function(paths) { vvave.openUrls(paths) }) } } // Maui.Menu // { // title: qsTr("Collection") // // icon.name: "settings-configure" // Maui.MenuItem // { // text: qsTr("Re-Scan") // onTriggered: bae.refreshCollection(); // } // Maui.MenuItem // { // text: qsTr("Refresh...") // onTriggered: H.refreshCollection(); // } // Maui.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 * 15 height: root.height - root.headBar.implicitHeight - root.footBar.implicitHeight - modal: root.width < _drawer.width*1.5 + 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 0c4aebf..db9c907 100644 --- a/models/albums/albumsmodel.cpp +++ b/models/albums/albumsmodel.cpp @@ -1,270 +1,267 @@ #include "albumsmodel.h" #include "db/collectionDB.h" #include "utils/brain.h" #include AlbumsModel::AlbumsModel(QObject *parent) : BaseList(parent) { 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(!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->deleteLater(); - }); + QObject::connect(watcher, &QFutureWatcher::finished, + watcher, &QFutureWatcher::deleteLater); auto func = [=]() { QList requests; int index = -1; - for(auto &album : this->list) + for(auto album : this->list) { index++; if(!album[FMH::MODEL_KEY::ARTWORK].isEmpty()) continue; if(BAE::artworkCache(album, FMH::MODEL_KEY::ALBUM)) { - qDebug()<< "cache artwork done" << album; 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 = [=](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, [=](QString path) { qDebug()<< "Saving artwork file to" << path; FMH::MODEL newTrack = request.track; newTrack[FMH::MODEL_KEY::ARTWORK] = path; 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); bool stop = false; QObject::connect(this, &AlbumsModel::destroyed, [&]() { qDebug()<< stop << &stop; stop = true; qDebug()<< stop << &stop; }); for(const auto &req : requests) { if(stop) { loop.quit(); return; } pulpo.request(req); 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/tracks/tracksmodel.cpp b/models/tracks/tracksmodel.cpp index faa9ab3..e5ee9b4 100644 --- a/models/tracks/tracksmodel.cpp +++ b/models/tracks/tracksmodel.cpp @@ -1,379 +1,341 @@ #include "tracksmodel.h" #include "db/collectionDB.h" TracksModel::TracksModel(QObject *parent) : BaseList(parent) { 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; - qDebug()<< "setting query"<< this->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; - this->preListChanged(); + emit this->preListChanged(); this->sortList(); - this->postListChanged(); + 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); - qDebug()<< "SORTING LIST BY"<< this->sort; qSort(this->list.begin(), this->list.end(), [key](const FMH::MODEL &e1, const FMH::MODEL &e2) -> bool { - auto role = key; - - switch(role) + switch(key) { case FMH::MODEL_KEY::RELEASEDATE: case FMH::MODEL_KEY::RATE: case FMH::MODEL_KEY::FAV: case FMH::MODEL_KEY::COUNT: { - if(e1[role].toInt() > e2[role].toInt()) + if(e1[key].toInt() > e2[key].toInt()) return true; break; } case FMH::MODEL_KEY::TRACK: { - if(e1[role].toInt() < e2[role].toInt()) + if(e1[key].toInt() < e2[key].toInt()) return true; break; } 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); + 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[role]).toLower(); - const auto str2 = QString(e2[role]).toLower(); + const auto str1 = QString(e1[key]).toLower(); + const auto str2 = QString(e2[key]).toLower(); if(str1 < str2) return true; break; } default: - if(e1[role] < e2[role]) + if(e1[key] < e2[key]) return true; } return false; }); } void TracksModel::setList() { emit this->preListChanged(); - this->list = this->db->getDBData(this->query); - - qDebug()<< "my LIST" ; 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(auto item : this->list) - { - QVariantMap map; - for(auto key : item.keys()) - map.insert(FMH::MODEL_NAME[key], item[key]); - - res << map; - } + 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(); - - FMH::MODEL model; - for(auto key : item.keys()) - model.insert(FMH::MODEL_NAME_KEY[key], item[key].toString()); - - qDebug() << "Appending item to list" << item; - this->list << model; - - qDebug()<< this->list; - + 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; - 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); - + this->list.insert(at, FM::toModel(item)); emit this->postItemAppended(); } void TracksModel::appendQuery(const QString &query) { - if(query.isEmpty() || query == this->query) - return; - - this->query = 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/settings/fileloader.h b/settings/fileloader.h index bf2b1dc..fb3f363 100644 --- a/settings/fileloader.h +++ b/settings/fileloader.h @@ -1,88 +1,88 @@ #ifndef FILELOADER_H #define FILELOADER_H #include #include #include "../services/local/taginfo.h" #include "../db/collectionDB.h" #include "utils/bae.h" namespace FLoader { -inline QStringList getPathContents(QStringList urls, QString path) +inline QStringList getPathContents(QStringList &urls, const QString &path) { if(!FMH::fileExists(path)) return urls; if (QFileInfo(path).isDir()) { QDirIterator it(path, QStringList() << FMH::FILTER_LIST[FMH::FILTER_TYPE::AUDIO] << "*.m4a", QDir::Files, QDirIterator::Subdirectories); while (it.hasNext()) urls << it.next(); }else if (QFileInfo(path).isFile()) urls << path; return urls; } // returns the number of new items added to the collection db inline uint getTracks(const QStringList& paths) { auto db = CollectionDB::getInstance(); const auto urls = std::accumulate(paths.begin(), paths.end(), QStringList(), getPathContents); for(const auto &path : paths) if(FMH::fileExists(path)) db->addFolder(path); uint newTracks = 0; if(urls.isEmpty()) return newTracks; TagInfo info; for(const auto &url : urls) { if(db->check_existance(BAE::TABLEMAP[BAE::TABLE::TRACKS], FMH::MODEL_NAME[FMH::MODEL_KEY::URL], url)) continue; if(!info.feed(url)) continue; const auto track = info.getTrack(); const auto genre = info.getGenre(); const auto album = BAE::fixString(info.getAlbum()); const auto title = BAE::fixString(info.getTitle()); /* to fix*/ const auto artist = BAE::fixString(info.getArtist()); const auto sourceUrl = QFileInfo(url).dir().path(); const auto duration = info.getDuration(); const auto year = info.getYear(); FMH::MODEL trackMap = { {FMH::MODEL_KEY::URL, url}, {FMH::MODEL_KEY::TRACK, QString::number(track)}, {FMH::MODEL_KEY::TITLE, title}, {FMH::MODEL_KEY::ARTIST, artist}, {FMH::MODEL_KEY::ALBUM, album}, {FMH::MODEL_KEY::DURATION,QString::number(duration)}, {FMH::MODEL_KEY::GENRE, genre}, {FMH::MODEL_KEY::SOURCE, sourceUrl}, {FMH::MODEL_KEY::FAV, url.startsWith(BAE::YoutubeCachePath) ? "1": "0"}, {FMH::MODEL_KEY::RELEASEDATE, QString::number(year)} }; qDebug() << url; BAE::artworkCache(trackMap, FMH::MODEL_KEY::ALBUM); if(db->addTrack(trackMap)) newTracks++; } return newTracks; } } #endif // FILELOADER_H diff --git a/utils/Help.js b/utils/Help.js index 41c1d77..fdebf4b 100644 --- a/utils/Help.js +++ b/utils/Help.js @@ -1,112 +1,111 @@ .import "../db/Queries.js" as Q .import "../utils/Player.js" as Player function rootWidth() { return root.width; } function rootHeight() { return root.height; } function setStars(stars) { switch (stars) { case "0": case 0: return " "; case "1": case 1: return "\uf4CE"; case "2": case 2: return "\uf4CE \uf4CE"; case "3": case 3: return "\uf4CE \uf4CE \uf4CE"; case "4": case 4: return "\uf4CE \uf4CE \uf4CE \uf4CE"; case "5": case 5: return "\uf4CE \uf4CE \uf4CE \uf4CE \uf4CE"; default: return "error"; } } function refreshCollection(size) { if(size>0) root.notify("emblem-info", "Collection updated", size+" new tracks added...") refreshTracks() refreshAlbums() refreshArtists() refreshFolders() } function refreshFolders() { foldersView.populate() } function refreshTracks() { tracksView.list.refresh() } function refreshAlbums() { albumsView.list.refresh() albumsView.list.fetchInformation(); } function refreshArtists() { artistsView.list.refresh() } function notify(title, body) { if(isMobile) root.notify(title+"\n"+body) else bae.notify(title, body) } - function addPlaylist(playlist) { playlistsView.playlistViewModel.model.insert(0, playlist) } function searchFor(query) { if(currentView !== viewsIndex.search) currentView = viewsIndex.search searchView.runSearch(query) } function addSource() { sourcesDialog.open() } function addToSelection(item) { item.thumbnail= item.artwork item.label= item.title item.mime= "image" item.tooltip= item.url item.path= item.url selectionBar.append(item) } diff --git a/utils/Player.js b/utils/Player.js index 0e98359..d08e693 100644 --- a/utils/Player.js +++ b/utils/Player.js @@ -1,238 +1,234 @@ .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)) { player.url = currentTrack.url; player.playing = true if(currentTrack.artwork && currentTrack.artwork.length && currentTrack.artwork !== "NONE") currentArtwork = currentTrack.artwork -// else bae.loadCover(currentTrack.url) + // else bae.loadCover(currentTrack.url) progressBar.enabled = true if(!isMobile) { root.title = currentTrack.title + " - " +currentTrack.artist -// if(!root.active) -// bae.notifySong(currentTrack.url) + // 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) }else missingAlert(currentTrack) } } function queueTracks(tracks) -{ - if(tracks) +{ + if(tracks && tracks.length > 0) { - if(tracks.length > 0) - { - onQueue++ - console.log(onQueue) - appendTracksAt(tracks, currentTrackIndex+1) - root.notify("", "Queue", tracks.length + " tracks added put on queue") - } + 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 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 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 mainPlaylist.albumsRoll.positionAlbum(currentTrackIndex) playTrack(currentTrackIndex) } } function quickPlay(track) { // root.pageStack.currentIndex = 0 appendTrack(track) playAt(mainPlaylist.listView.count-1) mainPlaylist.listView.positionViewAtEnd() mainPlaylist.albumsRoll.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) { infoMsgAnim() // addToPlaylist([track.url], syncPlaylist) } } // if(track) // { // var empty = root.mainPlaylist.list.count // if((empty > 0 && track.url !== root.mainPlaylist.list.model.get(root.mainPlaylist.list.count-1).url) || empty === 0) // { // root.mainPlaylist.list.model.append(track) // if(empty === 0 && root.mainPlaylist.list.count>0) // playAt(0) // } // } } 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() { sync = false syncPlaylist = "" infoMsg = "" if(_drawer.modal && !_drawer.visible) _drawer.visible = true mainPlaylist.listView.positionViewAtBeginning() playAt(0) } diff --git a/utils/bae.h b/utils/bae.h index 0700c0a..3875a45 100644 --- a/utils/bae.h +++ b/utils/bae.h @@ -1,494 +1,494 @@ #ifndef BAE_H #define BAE_H #include "string" #include #include #include #include #include #include #include #include #include #include #include #ifdef STATIC_MAUIKIT #include "../mauikit/src/fm/fmh.h" #else #include #endif #include #define BABE_MAJOR_VERSION 0 #define BABE_MINOR_VERSION 1 #define BABE_PATCH_VERSION 0 #define BABE_VERSION_STR "0.1.0" #define BABE_VERSION KDE_MAKE_VERSION(0,1,0) using namespace std; namespace BAE { Q_NAMESPACE inline bool isMobile() { #if defined(Q_OS_ANDROID) return true; #elif defined(Q_OS_LINUX) return false; #elif defined(Q_OS_WIN32) return false; #elif defined(Q_OS_WIN64) return false; #elif defined(Q_OS_MACOS) return false; #elif defined(Q_OS_IOS) return true; #elif defined(Q_OS_HAIKU) return false; #endif } inline bool isAndroid() { #if defined(Q_OS_ANDROID) return true; #elif defined(Q_OS_LINUX) return false; #elif defined(Q_OS_WIN32) return false; #elif defined(Q_OS_WIN64) return false; #elif defined(Q_OS_MACOS) return false; #elif defined(Q_OS_IOS) return false; #elif defined(Q_OS_HAIKU) return false; #endif } enum SEG { HALF = 500, ONE = 1000, ONEHALF = 1500, TWO = 2000, THREE = 3000 }; enum SearchT { LIKE, SIMILAR }; typedef QMap SEARCH; static const SEARCH SearchTMap { { BAE::SearchT::LIKE, "like" }, { BAE::SearchT::SIMILAR, "similar" } }; enum class W : uint_fast8_t { ALL, NONE, LIKE, TAG, SIMILAR, UNKNOWN, DONE, DESC, ASC, CODE, MSG }; static const QMap SLANG = { {W::ALL, "ALL"}, {W::NONE, "NONE"}, {W::LIKE, "LIKE"}, {W::SIMILAR, "SIMILAR"}, {W::UNKNOWN, "UNKNOWN"}, {W::DONE, "DONE"}, {W::DESC, "DESC"}, {W::ASC, "ASC"}, {W::TAG, "TAG"}, {W::MSG, "MSG"}, {W::CODE, "CODE"} }; enum class TABLE : uint8_t { ALBUMS, ARTISTS, MOODS, PLAYLISTS, SOURCES, SOURCES_TYPES, TRACKS, TRACKS_MOODS, TRACKS_PLAYLISTS, TAGS, ALBUMS_TAGS, ARTISTS_TAGS, TRACKS_TAGS, LOGS, FOLDERS, ALL, NONE }; static const QMap TABLEMAP = { {TABLE::ALBUMS,"albums"}, {TABLE::ARTISTS,"artists"}, {TABLE::MOODS,"moods"}, {TABLE::PLAYLISTS,"playlists"}, {TABLE::SOURCES,"sources"}, {TABLE::SOURCES_TYPES,"sources_types"}, {TABLE::TRACKS,"tracks"}, {TABLE::TRACKS_MOODS,"tracks_moods"}, {TABLE::TRACKS_PLAYLISTS,"tracks_playlists"}, {TABLE::TAGS,"tags"}, {TABLE::ALBUMS_TAGS,"albums_tags"}, {TABLE::ARTISTS_TAGS,"artists_tags"}, {TABLE::TRACKS_TAGS,"tracks_tags"}, {TABLE::LOGS,"logs"}, {TABLE::FOLDERS,"folders"} }; enum class KEY :uint8_t { URL = 0, SOURCES_URL = 1, TRACK = 2, TITLE = 3, ARTIST = 4, ALBUM = 5, DURATION = 6, PLAYED = 7, BABE = 8, STARS = 9, RELEASE_DATE = 10, ADD_DATE = 11, LYRICS = 12, GENRE = 13, ART = 14, TAG = 15, MOOD = 16, PLAYLIST = 17, ARTWORK = 18, WIKI = 19, SOURCE_TYPE = 20, CONTEXT = 21, RETRIEVAL_DATE = 22, COMMENT = 23, ID = 24, SQL = 25, NONE = 26 }; typedef QMap DB; typedef QList DB_LIST; static const DB KEYMAP = { {KEY::URL, "url"}, {KEY::SOURCES_URL, "sources_url"}, {KEY::TRACK, "track"}, {KEY::TITLE, "title"}, {KEY::ARTIST, "artist"}, {KEY::ALBUM, "album"}, {KEY::DURATION, "duration"}, {KEY::PLAYED, "played"}, {KEY::BABE, "babe"}, {KEY::STARS, "stars"}, {KEY::RELEASE_DATE, "releaseDate"}, {KEY::ADD_DATE, "addDate"}, {KEY::LYRICS, "lyrics"}, {KEY::GENRE, "genre"}, {KEY::ART, "art"}, {KEY::TAG, "tag"}, {KEY::MOOD, "mood"}, {KEY::PLAYLIST, "playlist"}, {KEY::ARTWORK, "artwork"}, {KEY::WIKI, "wiki"}, {KEY::SOURCE_TYPE, "source_types_id"}, {KEY::CONTEXT, "context"}, {KEY::RETRIEVAL_DATE, "retrieval_date"}, {KEY::ID, "id"}, {KEY::COMMENT, "comment"}, {KEY::SQL, "sql"} }; static const DB TracksColsMap = { {KEY::URL, KEYMAP[KEY::URL]}, {KEY::SOURCES_URL, KEYMAP[KEY::SOURCES_URL]}, {KEY::TRACK, KEYMAP[KEY::TRACK]}, {KEY::TITLE, KEYMAP[KEY::TITLE]}, {KEY::ARTIST, KEYMAP[KEY::ARTIST]}, {KEY::ALBUM, KEYMAP[KEY::ALBUM]}, {KEY::DURATION, KEYMAP[KEY::DURATION]}, {KEY::PLAYED, KEYMAP[KEY::PLAYED]}, {KEY::BABE, KEYMAP[KEY::BABE]}, {KEY::STARS, KEYMAP[KEY::STARS]}, {KEY::RELEASE_DATE, KEYMAP[KEY::RELEASE_DATE]}, {KEY::ADD_DATE, KEYMAP[KEY::ADD_DATE]}, {KEY::LYRICS, KEYMAP[KEY::LYRICS]}, {KEY::GENRE, KEYMAP[KEY::GENRE]}, {KEY::ART, KEYMAP[KEY::ART]} }; inline QString transformTime(const qint64 &value) { QString tStr; if (value) { QTime time((value/3600)%60, (value/60)%60, value%60, (value*1000)%1000); QString format = "mm:ss"; if (value > 3600) format = "hh:mm:ss"; tStr = time.toString(format); } return tStr.isEmpty() ? "00:00" : tStr; } inline QString getNameFromLocation(const QString &str) { QString ret; int index = 0; for(int i = str.size() - 1; i >= 0; i--) if(str[i] == '/') { index = i + 1; i = -1; } for(; index < str.size(); index++) ret.push_back(str[index]); return ret; } const QString SettingPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)+"/vvave/"; const QString ArtworkPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)+"/vvave/artwork/"; const QString CollectionDBPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)+"/vvave/"; const QString CachePath = BAE::isMobile() ? BAE::ArtworkPath : QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation)+"/vvave/"; const QString YoutubeCachePath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation)+"/vvave/youtube/"; const QString NotifyDir = SettingPath; const QStringList MusicPaths = QStandardPaths::standardLocations(QStandardPaths::MusicLocation); const QStringList DownloadsPaths = QStandardPaths::standardLocations(QStandardPaths::DownloadLocation); const QString MusicPath = FMH::MusicPath; const QString HomePath = FMH::HomePath; const QString DownloadsPath = FMH::DownloadsPath; const QString BabePort = "8483"; const QString LinkPort = "3333"; const QString App = "vvave"; const QString Description = "vvave music player"; const QString Version = BABE_VERSION_STR; const QString DBName = "collection.db"; const QStringList MoodColors = {"#F0FF01","#01FF5B","#3DAEFD","#B401FF","#E91E63"}; const QStringList defaultSources = QStringList() << BAE::MusicPath << BAE::DownloadsPath << BAE::MusicPaths << BAE::DownloadsPaths << BAE::YoutubeCachePath; inline QString fixTitle(const QString &title,const QString &s,const QString &e) { QString newTitle; for(int i=0; i«»,.?/{}\'\"\\\[\\\]\\\\]"))); QString title = str; title = title.remove(QChar::Null); title = title.contains(QChar('\u0000')) ? title.replace(QChar('\u0000'),"") : title; title = title.contains("(") && title.contains(")") ? fixTitle(title, "(",")") : title; title = title.contains("[") && title.contains("]") ? fixTitle(title, "[","]") : title; title = title.contains("{") && title.contains("}") ? fixTitle(title, "{","}") : title; title = title.contains("ft", Qt::CaseInsensitive) ? removeSubstring(title, "ft") : title; title = title.contains("ft.", Qt::CaseInsensitive) ? removeSubstring(title, "ft.") : title; title = title.contains("featuring", Qt::CaseInsensitive) ? removeSubstring(title, "featuring"):title; title = title.contains("feat", Qt::CaseInsensitive) ? removeSubstring(title, "feat") : title; title = title.contains("official video", Qt::CaseInsensitive) ? removeSubstring(title, "official video"):title; title = title.contains("live", Qt::CaseInsensitive) ? removeSubstring(title, "live") : title; title = title.contains("...") ? title.replace("..." ,"") : title; title = title.contains("|") ? title.replace("|", "") : title; title = title.contains("|") ? removeSubstring(title, "|") : title; title = title.contains('"') ? title.replace('"', "") : title; title = title.contains(":") ? title.replace(":", "") : title; // title=title.contains("&")? title.replace("&", "and"):title; //qDebug()<<"fixed string:"<bytesAvailable()) return true; else return false; } } #endif // BAE_H diff --git a/vvave.cpp b/vvave.cpp index 026ebef..5a4b413 100644 --- a/vvave.cpp +++ b/vvave.cpp @@ -1,165 +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) { 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(QString url : urls) + 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 + }else { if(info.feed(url)) { - auto album = BAE::fixString(info.getAlbum()); - auto track= info.getTrack(); - auto title = BAE::fixString(info.getTitle()); /* to fix*/ - auto artist = BAE::fixString(info.getArtist()); - auto genre = info.getGenre(); - auto sourceUrl = QFileInfo(url).dir().path(); - auto duration = info.getDuration(); - auto year = info.getYear(); + 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)} }); } } - qDebug()<<"opening urls " << urls << data; - emit this->openFiles(data); }