diff --git a/3rdparty/taglib.pri b/3rdparty/taglib.pri new file mode 100644 index 0000000..392e7b2 --- /dev/null +++ b/3rdparty/taglib.pri @@ -0,0 +1,256 @@ + DEPENDPATH += $$PWD/taglib + DEPENDPATH += $$PWD/taglib/ape + DEPENDPATH += $$PWD/taglib/asf + DEPENDPATH += $$PWD/taglib/flac + DEPENDPATH += $$PWD/taglib/it + DEPENDPATH += $$PWD/taglib/mod + DEPENDPATH += $$PWD/taglib/mp4 + DEPENDPATH += $$PWD/taglib/mpc + DEPENDPATH += $$PWD/taglib/mpeg + DEPENDPATH += $$PWD/taglib/mpeg/id3v1 + DEPENDPATH += $$PWD/taglib/mpeg/id3v2 + DEPENDPATH += $$PWD/taglib/mpeg/id3v2/frames + DEPENDPATH += $$PWD/taglib/ogg + DEPENDPATH += $$PWD/taglib/ogg/flac + DEPENDPATH += $$PWD/taglib/ogg/opus + DEPENDPATH += $$PWD/taglib/ogg/speex + DEPENDPATH += $$PWD/taglib/ogg/vorbis + DEPENDPATH += $$PWD/taglib/riff + DEPENDPATH += $$PWD/taglib/riff/aiff + DEPENDPATH += $$PWD/taglib/riff/wav + DEPENDPATH += $$PWD/taglib/s3m + DEPENDPATH += $$PWD/taglib/toolkit + DEPENDPATH += $$PWD/taglib/trueaudio + DEPENDPATH += $$PWD/taglib/wavpack + DEPENDPATH += $$PWD/taglib/xm + + + INCLUDEPATH += $$PWD/taglib + INCLUDEPATH += $$PWD/taglib/ape + INCLUDEPATH += $$PWD/taglib/asf + INCLUDEPATH += $$PWD/taglib/flac + INCLUDEPATH += $$PWD/taglib/it + INCLUDEPATH += $$PWD/taglib/mod + INCLUDEPATH += $$PWD/taglib/mp4 + INCLUDEPATH += $$PWD/taglib/mpc + INCLUDEPATH += $$PWD/taglib/mpeg + INCLUDEPATH += $$PWD/taglib/mpeg/id3v1 + INCLUDEPATH += $$PWD/taglib/mpeg/id3v2 + INCLUDEPATH += $$PWD/taglib/mpeg/id3v2/frames + INCLUDEPATH += $$PWD/taglib/ogg + INCLUDEPATH += $$PWD/taglib/ogg/flac + INCLUDEPATH += $$PWD/taglib/ogg/opus + INCLUDEPATH += $$PWD/taglib/ogg/speex + INCLUDEPATH += $$PWD/taglib/ogg/vorbis + INCLUDEPATH += $$PWD/taglib/riff + INCLUDEPATH += $$PWD/taglib/riff/aiff + INCLUDEPATH += $$PWD/taglib/riff/wav + INCLUDEPATH += $$PWD/taglib/s3m + INCLUDEPATH += $$PWD/taglib/toolkit + INCLUDEPATH += $$PWD/taglib/trueaudio + INCLUDEPATH += $$PWD/taglib/wavpack + INCLUDEPATH += $$PWD/taglib/xm + +SOURCES += \ + $$PWD/taglib/ape/apefile.cpp \ + $$PWD/taglib/ape/apefooter.cpp \ + $$PWD/taglib/ape/apeitem.cpp \ + $$PWD/taglib/ape/apeproperties.cpp \ + $$PWD/taglib/ape/apetag.cpp \ + $$PWD/taglib/asf/asfattribute.cpp \ + $$PWD/taglib/asf/asffile.cpp \ + $$PWD/taglib/asf/asfpicture.cpp \ + $$PWD/taglib/asf/asfproperties.cpp \ + $$PWD/taglib/asf/asftag.cpp \ + $$PWD/taglib/flac/flacfile.cpp \ + $$PWD/taglib/flac/flacmetadatablock.cpp \ + $$PWD/taglib/flac/flacpicture.cpp \ + $$PWD/taglib/flac/flacproperties.cpp \ + $$PWD/taglib/flac/flacunknownmetadatablock.cpp \ + $$PWD/taglib/it/itfile.cpp \ + $$PWD/taglib/it/itproperties.cpp \ + $$PWD/taglib/mod/modfile.cpp \ + $$PWD/taglib/mod/modfilebase.cpp \ + $$PWD/taglib/mod/modproperties.cpp \ + $$PWD/taglib/mod/modtag.cpp \ + $$PWD/taglib/mp4/mp4atom.cpp \ + $$PWD/taglib/mp4/mp4coverart.cpp \ + $$PWD/taglib/mp4/mp4file.cpp \ + $$PWD/taglib/mp4/mp4item.cpp \ + $$PWD/taglib/mp4/mp4properties.cpp \ + $$PWD/taglib/mp4/mp4tag.cpp \ + $$PWD/taglib/mpc/mpcfile.cpp \ + $$PWD/taglib/mpc/mpcproperties.cpp \ + $$PWD/taglib/mpeg/id3v1/id3v1genres.cpp \ + $$PWD/taglib/mpeg/id3v1/id3v1tag.cpp \ + $$PWD/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp \ + $$PWD/taglib/mpeg/id3v2/frames/commentsframe.cpp \ + $$PWD/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp \ + $$PWD/taglib/mpeg/id3v2/frames/ownershipframe.cpp \ + $$PWD/taglib/mpeg/id3v2/frames/popularimeterframe.cpp \ + $$PWD/taglib/mpeg/id3v2/frames/privateframe.cpp \ + $$PWD/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp \ + $$PWD/taglib/mpeg/id3v2/frames/textidentificationframe.cpp \ + $$PWD/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp \ + $$PWD/taglib/mpeg/id3v2/frames/unknownframe.cpp \ + $$PWD/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp \ + $$PWD/taglib/mpeg/id3v2/frames/urllinkframe.cpp \ + $$PWD/taglib/mpeg/id3v2/id3v2extendedheader.cpp \ + $$PWD/taglib/mpeg/id3v2/id3v2footer.cpp \ + $$PWD/taglib/mpeg/id3v2/id3v2frame.cpp \ + $$PWD/taglib/mpeg/id3v2/id3v2framefactory.cpp \ + $$PWD/taglib/mpeg/id3v2/id3v2header.cpp \ + $$PWD/taglib/mpeg/id3v2/id3v2synchdata.cpp \ + $$PWD/taglib/mpeg/id3v2/id3v2tag.cpp \ + $$PWD/taglib/mpeg/mpegfile.cpp \ + $$PWD/taglib/mpeg/mpegheader.cpp \ + $$PWD/taglib/mpeg/mpegproperties.cpp \ + $$PWD/taglib/mpeg/xingheader.cpp \ + $$PWD/taglib/ogg/flac/oggflacfile.cpp \ + $$PWD/taglib/ogg/opus/opusfile.cpp \ + $$PWD/taglib/ogg/opus/opusproperties.cpp \ + $$PWD/taglib/ogg/speex/speexfile.cpp \ + $$PWD/taglib/ogg/speex/speexproperties.cpp \ + $$PWD/taglib/ogg/vorbis/vorbisfile.cpp \ + $$PWD/taglib/ogg/vorbis/vorbisproperties.cpp \ + $$PWD/taglib/ogg/oggfile.cpp \ + $$PWD/taglib/ogg/oggpage.cpp \ + $$PWD/taglib/ogg/oggpageheader.cpp \ + $$PWD/taglib/ogg/xiphcomment.cpp \ + $$PWD/taglib/riff/aiff/aifffile.cpp \ + $$PWD/taglib/riff/aiff/aiffproperties.cpp \ + $$PWD/taglib/riff/wav/infotag.cpp \ + $$PWD/taglib/riff/wav/wavfile.cpp \ + $$PWD/taglib/riff/wav/wavproperties.cpp \ + $$PWD/taglib/riff/rifffile.cpp \ + $$PWD/taglib/s3m/s3mfile.cpp \ + $$PWD/taglib/s3m/s3mproperties.cpp \ + $$PWD/taglib/toolkit/tbytevector.cpp \ + $$PWD/taglib/toolkit/tbytevectorlist.cpp \ + $$PWD/taglib/toolkit/tbytevectorstream.cpp \ + $$PWD/taglib/toolkit/tdebug.cpp \ + $$PWD/taglib/toolkit/tdebuglistener.cpp \ + $$PWD/taglib/toolkit/tfile.cpp \ + $$PWD/taglib/toolkit/tfilestream.cpp \ + $$PWD/taglib/toolkit/tiostream.cpp \ + $$PWD/taglib/toolkit/tpropertymap.cpp \ + $$PWD/taglib/toolkit/trefcounter.cpp \ + $$PWD/taglib/toolkit/tstring.cpp \ + $$PWD/taglib/toolkit/tstringlist.cpp \ + $$PWD/taglib/toolkit/unicode.cpp \ + $$PWD/taglib/trueaudio/trueaudiofile.cpp \ + $$PWD/taglib/trueaudio/trueaudioproperties.cpp \ + $$PWD/taglib/wavpack/wavpackfile.cpp \ + $$PWD/taglib/wavpack/wavpackproperties.cpp \ + $$PWD/taglib/xm/xmfile.cpp \ + $$PWD/taglib/xm/xmproperties.cpp \ + $$PWD/taglib/audioproperties.cpp \ + $$PWD/taglib/fileref.cpp \ + $$PWD/taglib/tag.cpp \ + $$PWD/taglib/tagunion.cpp \ + +HEADERS += \ + $$PWD/taglib/ape/apefile.h \ + $$PWD/taglib/ape/apefooter.h \ + $$PWD/taglib/ape/apeitem.h \ + $$PWD/taglib/ape/apeproperties.h \ + $$PWD/taglib/ape/apetag.h \ + $$PWD/taglib/asf/asfattribute.h \ + $$PWD/taglib/asf/asffile.h \ + $$PWD/taglib/asf/asfpicture.h \ + $$PWD/taglib/asf/asfproperties.h \ + $$PWD/taglib/asf/asftag.h \ + $$PWD/taglib/flac/flacfile.h \ + $$PWD/taglib/flac/flacmetadatablock.h \ + $$PWD/taglib/flac/flacpicture.h \ + $$PWD/taglib/flac/flacproperties.h \ + $$PWD/taglib/flac/flacunknownmetadatablock.h \ + $$PWD/taglib/it/itfile.h \ + $$PWD/taglib/it/itproperties.h \ + $$PWD/taglib/mod/modfile.h \ + $$PWD/taglib/mod/modfilebase.h \ + $$PWD/taglib/mod/modfileprivate.h \ + $$PWD/taglib/mod/modproperties.h \ + $$PWD/taglib/mod/modtag.h \ + $$PWD/taglib/mp4/mp4atom.h \ + $$PWD/taglib/mp4/mp4coverart.h \ + $$PWD/taglib/mp4/mp4file.h \ + $$PWD/taglib/mp4/mp4item.h \ + $$PWD/taglib/mp4/mp4properties.h \ + $$PWD/taglib/mp4/mp4tag.h \ + $$PWD/taglib/mpc/mpcfile.h \ + $$PWD/taglib/mpc/mpcproperties.h \ + $$PWD/taglib/mpeg/id3v1/id3v1genres.h \ + $$PWD/taglib/mpeg/id3v1/id3v1tag.h \ + $$PWD/taglib/mpeg/id3v2/frames/attachedpictureframe.h \ + $$PWD/taglib/mpeg/id3v2/frames/commentsframe.h \ + $$PWD/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.h \ + $$PWD/taglib/mpeg/id3v2/frames/ownershipframe.h \ + $$PWD/taglib/mpeg/id3v2/frames/popularimeterframe.h \ + $$PWD/taglib/mpeg/id3v2/frames/privateframe.h \ + $$PWD/taglib/mpeg/id3v2/frames/relativevolumeframe.h \ + $$PWD/taglib/mpeg/id3v2/frames/textidentificationframe.h \ + $$PWD/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h \ + $$PWD/taglib/mpeg/id3v2/frames/unknownframe.h \ + $$PWD/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h \ + $$PWD/taglib/mpeg/id3v2/frames/urllinkframe.h \ + $$PWD/taglib/mpeg/id3v2/id3v2extendedheader.h \ + $$PWD/taglib/mpeg/id3v2/id3v2footer.h \ + $$PWD/taglib/mpeg/id3v2/id3v2frame.h \ + $$PWD/taglib/mpeg/id3v2/id3v2framefactory.h \ + $$PWD/taglib/mpeg/id3v2/id3v2header.h \ + $$PWD/taglib/mpeg/id3v2/id3v2synchdata.h \ + $$PWD/taglib/mpeg/id3v2/id3v2tag.h \ + $$PWD/taglib/mpeg/mpegfile.h \ + $$PWD/taglib/mpeg/mpegheader.h \ + $$PWD/taglib/mpeg/mpegproperties.h \ + $$PWD/taglib/mpeg/xingheader.h \ + $$PWD/taglib/ogg/flac/oggflacfile.h \ + $$PWD/taglib/ogg/opus/opusfile.h \ + $$PWD/taglib/ogg/opus/opusproperties.h \ + $$PWD/taglib/ogg/speex/speexfile.h \ + $$PWD/taglib/ogg/speex/speexproperties.h \ + $$PWD/taglib/ogg/vorbis/vorbisfile.h \ + $$PWD/taglib/ogg/vorbis/vorbisproperties.h \ + $$PWD/taglib/ogg/oggfile.h \ + $$PWD/taglib/ogg/oggpage.h \ + $$PWD/taglib/ogg/oggpageheader.h \ + $$PWD/taglib/ogg/xiphcomment.h \ + $$PWD/taglib/riff/aiff/aifffile.h \ + $$PWD/taglib/riff/aiff/aiffproperties.h \ + $$PWD/taglib/riff/wav/infotag.h \ + $$PWD/taglib/riff/wav/wavfile.h \ + $$PWD/taglib/riff/wav/wavproperties.h \ + $$PWD/taglib/riff/rifffile.h \ + $$PWD/taglib/s3m/s3mfile.h \ + $$PWD/taglib/s3m/s3mproperties.h \ + $$PWD/taglib/toolkit/taglib.h \ + $$PWD/taglib/toolkit/tbytevector.h \ + $$PWD/taglib/toolkit/tbytevectorlist.h \ + $$PWD/taglib/toolkit/tbytevectorstream.h \ + $$PWD/taglib/toolkit/tdebug.h \ + $$PWD/taglib/toolkit/tdebuglistener.h \ + $$PWD/taglib/toolkit/tfile.h \ + $$PWD/taglib/toolkit/tfilestream.h \ + $$PWD/taglib/toolkit/tiostream.h \ + $$PWD/taglib/toolkit/tlist.h \ + $$PWD/taglib/toolkit/tmap.h \ + $$PWD/taglib/toolkit/tpropertymap.h \ + $$PWD/taglib/toolkit/trefcounter.h \ + $$PWD/taglib/toolkit/tstring.h \ + $$PWD/taglib/toolkit/tstringlist.h \ + $$PWD/taglib/toolkit/tutils.h \ + $$PWD/taglib/toolkit/unicode.h \ + $$PWD/taglib/trueaudio/trueaudiofile.h \ + $$PWD/taglib/trueaudio/trueaudioproperties.h \ + $$PWD/taglib/wavpack/wavpackfile.h \ + $$PWD/taglib/wavpack/wavpackproperties.h \ + $$PWD/taglib/xm/xmfile.h \ + $$PWD/taglib/xm/xmproperties.h \ + $$PWD/taglib/audioproperties.h \ + $$PWD/taglib/fileref.h \ + $$PWD/taglib/tag.h \ + $$PWD/taglib/taglib_export.h \ + $$PWD/taglib/tagunion.h \ + $$PWD/taglib/config.h \ + $$PWD/taglib/taglib_config.h \ diff --git a/db/script.sql b/db/script.sql index 92b771a..913c806 100644 --- a/db/script.sql +++ b/db/script.sql @@ -1,158 +1,151 @@ CREATE TABLE ARTISTS ( artist TEXT , artwork TEXT , wiki TEXT, PRIMARY KEY(artist) -) ; +); CREATE TABLE ALBUMS ( album TEXT , artist TEXT, artwork TEXT, wiki TEXT, PRIMARY KEY(album, artist), FOREIGN KEY(artist) REFERENCES artists(artist) -) ; +); CREATE TABLE TAGS ( tag TEXT NOT NULL, context TEXT NOT NULL, PRIMARY KEY(tag, context) -) ; +); CREATE TABLE MOODS ( mood TEXT PRIMARY KEY -) ; - +); CREATE TABLE PLAYLISTS ( -playlist TEXT PRIMARY KEY , +playlist TEXT PRIMARY KEY, adddate DATE NOT NULL -) ; +); CREATE TABLE SOURCES_TYPES ( -id INTEGER PRIMARY KEY , +id INTEGER PRIMARY KEY, name TEXT NOT NULL -) ; +); CREATE TABLE FOLDERS ( -url TEXT PRIMARY KEY, +url TEXT PRIMARY KEY, adddate DATE NOT NULL -) ; +); CREATE TABLE SOURCES ( url TEXT PRIMARY KEY , sourcetype INTEGER NOT NULL, FOREIGN KEY(sourcetype) REFERENCES SOURCES_TYPES(id) -) ; +); CREATE TABLE TRACKS ( url TEXT , source TEXT , track INTEGER , title TEXT NOT NULL, artist TEXT NOT NULL, album TEXT NOT NULL, duration INTEGER , comment TEXT, count INTEGER , fav INTEGER NOT NULL, rate INTEGER NOT NULL, releasedate DATE , adddate DATE NOT NULL, lyrics TEXT NOT NULL, genre TEXT, color TEXT, wiki TEXT NOT NULL, PRIMARY KEY (url), FOREIGN KEY(source) REFERENCES SOURCES(url), FOREIGN KEY(album, artist) REFERENCES albums(album, artist) -) ; - +); CREATE TABLE TRACKS_MOODS ( mood TEXT NOT NULL , url TEXT NOT NULL , FOREIGN KEY(mood) REFERENCES MOODS(mood), FOREIGN KEY(url) REFERENCES TRACKS(url) - -) ; +); CREATE TABLE TRACKS_TAGS ( tag TEXT NOT NULL , context TEXT NOT NULL , url TEXT NOT NULL , PRIMARY KEY (tag, context, url), FOREIGN KEY(tag, context) REFERENCES TAGS(tag, context), FOREIGN KEY(url) REFERENCES TRACKS(url) - -) ; +); CREATE TABLE ARTISTS_TAGS ( tag TEXT NOT NULL , context TEXT NOT NULL, artist TEXT NOT NULL , PRIMARY KEY (tag, context, artist), FOREIGN KEY(tag, context) REFERENCES TAGS(tag, context), FOREIGN KEY(artist) REFERENCES ARTISTS(artist) - -) ; +); CREATE TABLE ALBUMS_TAGS ( tag TEXT NOT NULL , context TEXT NOT NULL, album TEXT NOT NULL , artist TEXT NOT NULL, PRIMARY KEY (tag, context, album, artist), FOREIGN KEY(tag, context) REFERENCES TAGS(tag, context), FOREIGN KEY(album, artist) REFERENCES ALBUMS(album, artist) -) ; +); CREATE TABLE PLAYLISTS_MOODS ( playlist TEXT NOT NULL , mood TEXT NOT NULL , PRIMARY KEY (playlist, mood), FOREIGN KEY(playlist) REFERENCES PLAYLISTS(playlist), FOREIGN KEY(mood) REFERENCES MOODS(mood) - -) ; +); CREATE TABLE TRACKS_PLAYLISTS ( playlist TEXT NOT NULL , url TEXT NOT NULL , adddate DATE NOT NULL, PRIMARY KEY (playlist, url), FOREIGN KEY(playlist) REFERENCES PLAYLISTS(playlist), FOREIGN KEY(url) REFERENCES TRACKS(url) -) ; +); CREATE TABLE LOG ( id INTEGER NOT NULL, adddate DATE NOT NULL, - PRIMARY KEY(id) ); --First insertions INSERT INTO SOURCES_TYPES VALUES (1,"LOCAL"); INSERT INTO SOURCES_TYPES VALUES (2,"ONLINE"); INSERT INTO SOURCES_TYPES VALUES (3,"DEVICE"); diff --git a/main.qml b/main.qml index 7e0ccea..024e003 100644 --- a/main.qml +++ b/main.qml @@ -1,886 +1,884 @@ import QtQuick 2.10 import QtQuick.Controls 2.10 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 import QtQuick.Controls.Material 2.1 import "utils" import "widgets" import "widgets/PlaylistsView" import "widgets/MainPlaylist" import "widgets/SettingsView" import "widgets/SearchView" import "widgets/CloudView" import "view_models" import "view_models/BabeTable" import "services/local" import "services/web" //import "services/web/Spotify" import "view_models/BabeGrid" import "widgets/InfoView" import "db/Queries.js" as Q import "utils/Help.js" as H import "utils/Player.js" as Player import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import Player 1.0 import AlbumsList 1.0 import TracksList 1.0 import PlaylistsList 1.0 Maui.ApplicationWindow { id: root title: qsTr("vvave") /***************************************************/ /******************** ALIASES ********************/ /*************************************************/ property alias mainPlaylist: mainPlaylist property alias selectionBar: _selectionBar property alias progressBar: progressBar property alias dialog : _dialogLoader.item Maui.App.iconName: "qrc:/assets/vvave.svg" Maui.App.description: qsTr("VVAVE will handle your whole music collection by retreaving semantic information from the web. Just relax, enjoy and discover your new music ") /***************************************************/ /******************** PLAYBACK ********************/ /*************************************************/ property bool isShuffle: Maui.FM.loadSettings("SHUFFLE","PLAYBACK", false) - readonly property var currentTrack: mainlistEmpty? ({url: "", artwork: "", fav: "0", stars: "0"}) : mainPlaylist.table.listModel.get(currentTrackIndex) + property var currentTrack: mainlistEmpty? ({url: "", artwork: "", fav: "0", stars: "0"}) : mainPlaylist.table.listModel.get(currentTrackIndex) property int currentTrackIndex: -1 property int prevTrackIndex: 0 readonly property string currentArtwork: currentTrack.artwork - readonly property bool currentBabe: currentTrack.fav == "0" ? false : true property alias durationTimeLabel: player.duration property string progressTimeLabel: player.transformTime((player.duration/1000) *(player.pos/ 1000)) property alias isPlaying: player.playing property int onQueue: 0 property bool mainlistEmpty: !mainPlaylist.table.count > 0 /***************************************************/ /******************** HANDLERS ********************/ /*************************************************/ readonly property var viewsIndex: ({ tracks: 0, albums: 1, artists: 2, playlists: 3, cloud: 4, folders: 5, youtube: 6, search: 7}) property string syncPlaylist: "" property bool sync: false property bool focusView : false property bool selectionMode : false /***************************************************/ /******************** UI COLORS *******************/ /*************************************************/ readonly property color babeColor: "#f84172" /*SIGNALS*/ signal missingAlert(var track) /*HANDLE EVENTS*/ onClosing: Player.savePlaylist() onMissingAlert: { var message = qsTr("Missing file...") var messageBody = track.title + " by " + track.artist + " is missing.\nDo you want to remove it from your collection?" notify("dialog-question", message, messageBody, function () { mainPlaylist.list.remove(mainPlaylist.table.currentIndex) }) } /*COMPONENTS*/ Player { id: player volume: 100 onFinishedChanged: if (!mainlistEmpty) { if (currentTrack.url) mainPlaylist.list.countUp(currentTrackIndex) Player.nextTrack() } } FloatingDisk { id: _floatingDisk opacity: 1 - _drawer.position } headBar.middleContent : Maui.ActionGroup { id: _actionGroup // Layout.fillWidth: true Layout.fillHeight: true Layout.minimumWidth: implicitWidth currentIndex : swipeView.currentIndex onCurrentIndexChanged: swipeView.currentIndex = currentIndex hiddenActions: [ Action { text: qsTr("Cloud") icon.name: "folder-cloud" }, Action { text: qsTr("Folders") icon.name: "folder" }, Action { text: qsTr("YouTube") icon.name: "internet-services" } ] Action { icon.name: "view-media-track" text: qsTr("Tracks") } Action { text: qsTr("Albums") icon.name: /*"album"*/ "view-media-album-cover" } Action { text: qsTr("Artists") icon.name: "view-media-artist" } Action { text: qsTr("Playlists") icon.name: "view-media-playlist" } } onSearchButtonClicked: { _actionGroup.currentIndex = viewsIndex.search // searchView.searchInput.forceActiveFocus() } Loader { id: _dialogLoader } InfoView { id: infoView maxWidth: parent.width * 0.8 maxHeight: parent.height * 0.9 } Loader { id: _focusViewLoader active: focusView source: "widgets/FocusView.qml" } Component { id: _shareDialogComponent Maui.ShareDialog {} } Component { id: _fmDialogComponent Maui.FileDialog { } } SourcesDialog { id: sourcesDialog } mainMenu: [ // Maui.MenuItem // { // text: "Vvave Stream" // icon.name: "headphones" // onTriggered: // { // pageStack.currentIndex = 1 // currentView = viewsIndex.vvave // } // }, // Maui.MenuItem // { // text: qsTr("Linking") // icon.name: "view-links" // onTriggered: // { // pageStack.currentIndex = 1 // currentView = viewsIndex.linking // if(!isLinked) linkingView.linkingConf.open() // } // }, // Maui.MenuItem // { // text: qsTr("Cloud") // icon.name: "folder-cloud" // onTriggered: // { // pageStack.currentIndex = 1 // currentView = viewsIndex.cloud // } // }, // Maui.MenuItem // { // text: qsTr("Spotify") // icon.name: "internet-services" // onTriggered: // { // pageStack.currentIndex = 1 // currentView = viewsIndex.spotify // } // }, MenuSeparator{}, MenuItem { text: qsTr("Sources") icon.name: "folder-add" onTriggered: sourcesDialog.open() }, MenuItem { text: qsTr("Open") icon.name: "folder-add" onTriggered: { _dialogLoader.sourceComponent = _fmDialogComponent root.dialog.settings.onlyDirs = false root.dialog.currentPath = "file:///home/camilo/Music" root.dialog.settings.filterType = Maui.FMList.AUDIO console.log("SETTIGN FILTER TYPE FISR", root.dialog.settings.filterType, Maui.FMList.AUDIO) root.dialog.show(function(paths) { vvave.openUrls(paths) root.dialog.close() }) } }/*, Menu { title: qsTr("Collection") // icon.name: "settings-configure" MenuItem { text: qsTr("Re-Scan") onTriggered: bae.refreshCollection(); } MenuItem { text: qsTr("Refresh...") onTriggered: H.refreshCollection(); } MenuItem { text: qsTr("Clean") onTriggered: bae.removeMissingTracks(); } }*/ // Maui.Menu // { // title: qsTr("Settings...") // // Kirigami.Action // // { // // text: "Brainz" // // Kirigami.Action // // { // // id: brainzToggle // // text: checked ? "Turn OFF" : "Turn ON" // // checked: bae.brainzState() // // checkable: true // // onToggled: // // { // // checked = !checked // // bae.saveSetting("AUTO", checked, "BRAINZ") // //// bae.brainz(checked) // // } // // } // // } // Maui.MenuItem // { // text: "Info label" + checked ? "ON" : "OFF" // checked: infoLabels // checkable: true // onToggled: // { // infoLabels = checked // bae.saveSetting("LABELS", infoLabels ? true : false, "PLAYBACK") // } // } // Maui.MenuItem // { // text: "Autoplay" // checked: autoplay // checkable: true // onToggled: // { // autoplay = checked // bae.saveSetting("AUTOPLAY", autoplay ? true : false, "BABE") // } // } // } ] Playlists { id: playlistsList } PlaylistDialog { id: playlistDialog } sideBar: Maui.AbstractSideBar { id: _drawer focus: true width: visible ? Math.min(Kirigami.Units.gridUnit * (Kirigami.Settings.isMobile? 18 : 20), root.width) : 0 modal: !isWide closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent height: _drawer.modal ? implicitHeight - _mainPage.footer.height : implicitHeight MainPlaylist { id: mainPlaylist anchors.fill: parent Connections { target: mainPlaylist onCoverPressed: Player.appendAll(tracks) onCoverDoubleClicked: Player.playAll(tracks) } } } Maui.Page { id: _mainPage anchors.fill: parent footer: ColumnLayout { id: _footerLayout visible: !mainlistEmpty || isPlaying height: visible ? Maui.Style.toolBarHeight * 1.2 : 0 width: parent.width spacing: 0 Kirigami.Separator { Layout.fillWidth: true } Slider { id: progressBar Layout.preferredHeight: Maui.Style.unit * (Kirigami.Settings.isMobile ? 6 : 8) Layout.fillWidth: true padding: 0 from: 0 to: 1000 value: player.pos spacing: 0 focus: true onMoved: { player.pos = value } background: Rectangle { implicitWidth: progressBar.width implicitHeight: progressBar.height width: progressBar.availableWidth height: implicitHeight color: "transparent" Rectangle { width: progressBar.visualPosition * parent.width height: progressBar.height color: Kirigami.Theme.highlightColor } } handle: Rectangle { x: progressBar.leftPadding + progressBar.visualPosition * (progressBar.availableWidth - width) y: -(progressBar.height * 0.8) implicitWidth: progressBar.pressed ? Maui.Style.iconSizes.medium : 0 implicitHeight: progressBar.pressed ? Maui.Style.iconSizes.medium : 0 radius: progressBar.pressed ? Maui.Style.iconSizes.medium : 0 color: Kirigami.Theme.highlightColor } } Maui.ToolBar { Layout.fillHeight: true Layout.fillWidth: true position: ToolBar.Footer rightContent: ToolButton { icon.name: "media-speaker" onClicked: _sliderPopup.open() Popup { id: _sliderPopup height: 150 width: parent.width y: -150 x: 0 // closePolicy: Popup.CloseOnEscape | Popup.CloseOnPress Slider { id: _volumeSlider visible: true height: parent.height width: 20 anchors.horizontalCenter: parent.horizontalCenter from: 0 to: 100 value: player.volume orientation: Qt.Vertical onMoved: { player.volume = value } } } } middleContent: [ ToolButton { id: babeBtnIcon icon.name: "love" enabled: currentTrackIndex >= 0 - icon.color: currentBabe ? babeColor : Kirigami.Theme.textColor + icon.color: currentTrack.fav == "0" ? babeColor : Kirigami.Theme.textColor onClicked: if (!mainlistEmpty) { mainPlaylist.list.fav(currentTrackIndex, !(mainPlaylist.listModel.get(currentTrackIndex).fav == "1")) - currentBabe = mainPlaylist.listModel.get(currentTrackIndex).fav == "1" } }, ToolButton { icon.name: "media-skip-backward" icon.color: Kirigami.Theme.textColor onClicked: Player.previousTrack() onPressAndHold: Player.playAt(prevTrackIndex) }, ToolButton { id: playIcon enabled: currentTrackIndex >= 0 icon.color: Kirigami.Theme.textColor icon.name: isPlaying ? "media-playback-pause" : "media-playback-start" onClicked: player.playing = !player.playing }, ToolButton { id: nextBtn icon.color: Kirigami.Theme.textColor icon.name: "media-skip-forward" onClicked: Player.nextTrack() onPressAndHold: Player.playAt(Player.shuffle()) }, ToolButton { id: shuffleBtn icon.color: babeColor icon.name: isShuffle ? "media-playlist-shuffle" : "media-playlist-normal" onClicked: { isShuffle = !isShuffle Maui.FM.saveSettings("SHUFFLE", isShuffle, "PLAYBACK") } } ] } } ColumnLayout { anchors.fill: parent SwipeView { id: swipeView Layout.fillHeight: true Layout.fillWidth: true // interactive: Kirigami.Settings.isMobile currentIndex: _actionGroup.currentIndex onCurrentIndexChanged: _actionGroup.currentIndex = currentIndex clip: true onCurrentItemChanged: currentItem.forceActiveFocus() TracksView { id: tracksView Connections { target: vvave onRefreshTables: tracksView.list.refresh() } Connections { target: tracksView onRowClicked: Player.quickPlay(tracksView.listModel.get(index)) onQuickPlayTrack: Player.quickPlay(tracksView.listModel.get(index)) onAppendTrack: Player.addTrack(tracksView.listModel.get(index)) onPlayAll: Player.playAll( tracksView.listModel.getAll()) onAppendAll: Player.appendAll( tracksView.listModel.getAll()) onQueueTrack: Player.queueTracks([tracksView.listModel.get(index)], index) } } Loader { active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem || item sourceComponent: AlbumsView { id: albumsView holder.emoji: "qrc:/assets/dialog-information.svg" holder.isMask: false holder.title : "No Albums!" holder.body: "Add new music sources" holder.emojiSize: Maui.Style.iconSizes.huge title: count + qsTr(" albums") list.query: Albums.ALBUMS list.sortBy: Albums.ALBUM Connections { target: vvave onRefreshTables: albumsView.list.refresh() } Connections { target: albumsView onRowClicked: Player.quickPlay(track) onAppendTrack: Player.addTrack(track) onPlayTrack: Player.quickPlay(track) onAlbumCoverClicked: albumsView.populateTable(album, artist) onAlbumCoverPressedAndHold: { var query = Q.GET.albumTracks_.arg(album) query = query.arg(artist) mainPlaylist.list.clear() mainPlaylist.list.sortBy = Tracks.NONE mainPlaylist.list.query = query Player.playAt(0) } onPlayAll: Player.playAll(albumsView.listModel.getAll()) onAppendAll: Player.appendAll(albumsView.listModel.getAll()) } } } Loader { active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem || item sourceComponent: AlbumsView { id: artistsView holder.emoji: "qrc:/assets/dialog-information.svg" holder.isMask: false holder.title : qsTr("No Artists!") holder.body: qsTr("Add new music sources") holder.emojiSize: Maui.Style.iconSizes.huge title: count + qsTr(" artists") list.query: Albums.ARTISTS list.sortBy: Albums.ARTIST table.list.sortBy: Tracks.NONE Connections { target: vvave onRefreshTables: artistsView.list.refresh() } Connections { target: artistsView onRowClicked: Player.quickPlay(track) onAppendTrack: Player.addTrack(track) onPlayTrack: Player.quickPlay(track) onAlbumCoverClicked: artistsView.populateTable(undefined, artist) onAlbumCoverPressedAndHold: { var query = Q.GET.artistTracks_.arg(artist) mainPlaylist.list.clear() mainPlaylist.list.sortBy = Tracks.NONE mainPlaylist.list.query = query Player.playAt(0) } onPlayAll: Player.artistsView(albumsView.listModel.getAll()) onAppendAll: Player.artistsView(albumsView.listModel.getAll()) } } } Loader { active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem || item sourceComponent: PlaylistsView { id: playlistsView Connections { target: playlistsView onRowClicked: Player.quickPlay(track) onAppendTrack: Player.addTrack(track) onPlayTrack: Player.quickPlay(track) onAppendAll: Player.appendAll(playlistsView.listModel.getAll()) onSyncAndPlay: { Player.playAll(playlistsView.listModel.getAll()) root.sync = true root.syncPlaylist = playlist } onPlayAll: Player.playAll(playlistsView.listModel.getAll()) } } } Loader { active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem || item sourceComponent: CloudView { id: cloudView } } Loader { active: SwipeView.isCurrentItem || item sourceComponent: FoldersView { id: foldersView Connections { target: vvave onRefreshTables: foldersView.populate() } Connections { target: foldersView.list onRowClicked: Player.quickPlay(foldersView.list.model.get(index)) onQuickPlayTrack: Player.quickPlay(foldersView.list.model.get(index)) onAppendTrack: Player.addTrack(foldersView.listModel.get(index)) onPlayAll: Player.playAll(foldersView.listModel.getAll()) onAppendAll: Player.appendAll(foldersView.listModel.getAll()) onQueueTrack: Player.queueTracks([foldersView.list.model.get(index)], index) } } } Loader { active: SwipeView.isCurrentItem || item sourceComponent: YouTube { id: youtubeView } } Loader { active: SwipeView.isCurrentItem || (item && item.listView.count > 0) sourceComponent: SearchTable { id: searchView Connections { target: searchView onRowClicked: Player.quickPlay(searchView.listModel.get(index)) onQuickPlayTrack: Player.quickPlay(searchView.listModel.get(index)) onAppendTrack: Player.addTrack(searchView.listModel.get(index)) onPlayAll: Player.playAll(searchView.listModel.getAll()) onAppendAll: Player.appendAll(searchView.listModel.getAll()) onArtworkDoubleClicked: { var query = Q.GET.albumTracks_.arg( searchView.listModel.get( index).album) query = query.arg(searchView.listModel.get(index).artist) mainPlaylist.list.clear() mainPlaylist.list.sortBy = Tracks.NONE mainPlaylist.list.query = query Player.playAt(0) } } } } } SelectionBar { id: _selectionBar property alias listView: _selectionBar.selectionList Layout.maximumWidth: 500 Layout.minimumWidth: 100 Layout.fillWidth: true Layout.alignment: Qt.AlignCenter Layout.margins: Maui.Style.space.big Layout.topMargin: Maui.Style.space.small Layout.bottomMargin: Maui.Style.space.big onExitClicked: { root.selectionMode = false clear() } } } } /*CONNECTIONS*/ Connections { target: vvave onRefreshTables: { if(size>0) root.notify("emblem-info", "Collection updated", size+" new tracks added...") } // onRefreshTracks: H.refreshTracks() // onRefreshAlbums: H.refreshAlbums() // onRefreshArtists: H.refreshArtists() // onCoverReady: // { // root.currentArtwork = path // currentTrack.artwork = currentArtwork // mainPlaylist.list.update(currentTrack, currentTrackIndex); // } // onTrackLyricsReady: // { // console.log(lyrics) // if (url === currentTrack.url) // Player.setLyrics(lyrics) // } // onSkipTrack: Player.nextTrack() // onBabeIt: if (!mainlistEmpty) // { // mainPlaylist.list.fav(currentTrackIndex, !(mainPlaylist.list.get(currentTrackIndex).fav == "1")) // currentBabe = mainPlaylist.list.get(currentTrackIndex).fav == "1" // } onOpenFiles: { Player.appendTracksAt(tracks, 0) Player.playAt(0) } } Component.onCompleted: { if(isAndroid) { Maui.Android.statusbarColor(Kirigami.Theme.backgroundColor, true) Maui.Android.navBarColor(Kirigami.Theme.backgroundColor, true) } } } diff --git a/models/cloud/cloud.cpp b/models/cloud/cloud.cpp index 49819e1..367233d 100644 --- a/models/cloud/cloud.cpp +++ b/models/cloud/cloud.cpp @@ -1,179 +1,180 @@ #include "cloud.h" #include "abstractmusicprovider.h" #include "NextCloud/nextmusic.h" #ifdef STATIC_MAUIKIT #include "mauiaccounts.h" #else #include #endif Cloud::Cloud(QObject *parent) : MauiList (parent), provider(new NextMusic(this)) { connect(MauiAccounts::instance(), &MauiAccounts::currentAccountChanged, [this](QVariantMap account) { this->provider->setCredentials(FMH::toModel(account)); this->setList(); }); connect(provider, &AbstractMusicProvider::collectionReady, [=](FMH::MODEL_LIST data) { emit this->albumsChanged(); emit this->artistsChanged(); emit this->preListChanged(); this->list = data; this->sortList(); emit this->postListChanged(); }); connect(static_cast (provider), &NextMusic::trackPathReady, [=](QString id, QString path) { auto track = static_cast (provider)->getTrackItem(id); track[FMH::MODEL_KEY::URL] = path; emit this->fileReady(FMH::toMap(track)); }); } void Cloud::componentComplete() { - + this->provider->setCredentials(FMH::toModel(MauiAccounts::instance()->getCurrentAccount())); + this->setList(); } void Cloud::setSortBy(const Cloud::SORTBY &sort) { if(this->sort == sort) return; this->sort = sort; emit this->preListChanged(); this->sortList(); emit this->postListChanged(); emit this->sortByChanged(); } Cloud::SORTBY Cloud::getSortBy() const { return this->sort; } QVariantList Cloud::getAlbums() const { return this->provider->getAlbumsList(); } QVariantList Cloud::getArtists() const { return this->provider->getArtistsList(); } FMH::MODEL_LIST Cloud::items() const { return this->list; } void Cloud::setList() { this->provider->getCollection(); } void Cloud::sortList() { if(this->sort == Cloud::SORTBY::NONE) return; const auto key = static_cast(this->sort); std::sort(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; }); } QVariantMap Cloud::get(const int &index) const { if(index >= this->list.size() || index < 0) return QVariantMap(); QVariantMap res; const auto item = this->list.at(index); for(auto key : item.keys()) res.insert(FMH::MODEL_NAME[key], item[key]); return res; } QVariantList Cloud::getAll() { return QVariantList(); } void Cloud::upload(const QUrl &url) { } void Cloud::getFileUrl(const QString &id) { static_cast(this->provider)->getTrackPath(id); } void Cloud::getFileUrl(const int &index) { if(index >= this->list.size() || index < 0) return; this->getFileUrl(this->list.at(index)[FMH::MODEL_KEY::ID]); } diff --git a/services/local/fileloader.h b/services/local/fileloader.h index f4eebd2..2497360 100644 --- a/services/local/fileloader.h +++ b/services/local/fileloader.h @@ -1,89 +1,90 @@ #ifndef FILELOADER_H #define FILELOADER_H #include #include #include #include "services/local/taginfo.h" #include "db/collectionDB.h" #include "utils/bae.h" namespace FLoader { static inline QList getPathContents(QList &urls, const QUrl &url) { if(!FMH::fileExists(url) && !url.isLocalFile()) return urls; if (QFileInfo(url.toLocalFile()).isDir()) { QDirIterator it(url.toLocalFile(), QStringList() << FMH::FILTER_LIST[FMH::FILTER_TYPE::AUDIO] << "*.m4a", QDir::Files, QDirIterator::Subdirectories); while (it.hasNext()) urls << QUrl::fromLocalFile(it.next()); }else if (QFileInfo(url.toLocalFile()).isFile()) urls << url.toString(); return urls; } // returns the number of new items added to the collection db static inline uint getTracks(const QList& paths) { - auto db = CollectionDB::getInstance(); + const auto db = CollectionDB::getInstance(); const auto urls = std::accumulate(paths.begin(), paths.end(), QList(), getPathContents); for(const auto &path : paths) if(path.isLocalFile() && FMH::fileExists(path)) db->addFolder(path.toString()); uint newTracks = 0; if(urls.isEmpty()) return newTracks; for(const auto &url : urls) { if(db->check_existance(BAE::TABLEMAP[BAE::TABLE::TRACKS], FMH::MODEL_NAME[FMH::MODEL_KEY::URL], url.toString())) continue; TagInfo info(url.toLocalFile()); if(info.isNull()) continue; + qDebug()<< url << "HHH"; + 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 = FMH::parentDir(url).toString(); const auto duration = info.getDuration(); const auto year = info.getYear(); - FMH::MODEL trackMap = { {FMH::MODEL_KEY::URL, url.toString()}, {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, "0"}, {FMH::MODEL_KEY::RELEASEDATE, QString::number(year)} }; BAE::artworkCache(trackMap, FMH::MODEL_KEY::ALBUM); if(db->addTrack(trackMap)) newTracks++; } return newTracks; } } #endif // FILELOADER_H diff --git a/services/local/player.cpp b/services/local/player.cpp index 8331df5..6097895 100644 --- a/services/local/player.cpp +++ b/services/local/player.cpp @@ -1,211 +1,211 @@ #include "player.h" #include "../../utils/bae.h" -//#ifdef STATIC_MAUIKIT -//#include "mauiaccounts.h" -//#else -//#include -//#endif +#ifdef STATIC_MAUIKIT +#include "mauiaccounts.h" +#else +#include +#endif Player::Player(QObject *parent) : QObject(parent), player(new QMediaPlayer(this)), updater(new QTimer(this)) { this->player->setVolume(this->volume); connect(this->updater, &QTimer::timeout, this, &Player::update); } inline QNetworkRequest getOcsRequest(const QNetworkRequest& request) { qDebug() << Q_FUNC_INFO; qDebug()<< "FORMING THE REQUEST" << request.url(); // Read raw headers out of the provided request QMap rawHeaders; for (const QByteArray& headerKey : request.rawHeaderList()) { rawHeaders.insert(headerKey, request.rawHeader(headerKey)); } -// const auto account = FMH::toModel(MauiAccounts::instance()->getCurrentAccount()); - const auto account = FMH::MODEL(); + const auto account = FMH::toModel(MauiAccounts::instance()->getCurrentAccount()); +// const auto account = FMH::MODEL(); const QString concatenated = QString("%1:%2").arg(account[FMH::MODEL_KEY::USER], account[FMH::MODEL_KEY::PASSWORD]); const QByteArray data = concatenated.toLocal8Bit().toBase64(); const QString headerData = "Basic " + data; // Construct new QNetworkRequest with prepared header values QNetworkRequest newRequest(request); newRequest.setRawHeader(QString("Authorization").toLocal8Bit(), headerData.toLocal8Bit()); newRequest.setRawHeader(QByteArrayLiteral("OCS-APIREQUEST"), QByteArrayLiteral("true")); newRequest.setRawHeader(QByteArrayLiteral("Cache-Control"), QByteArrayLiteral("public")); newRequest.setRawHeader(QByteArrayLiteral("Content-Description"), QByteArrayLiteral("File Transfer")); newRequest.setHeader(QNetworkRequest::ContentTypeHeader, "audio/mpeg"); newRequest.setAttribute(QNetworkRequest::CacheSaveControlAttribute, true); newRequest.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache); qDebug() << "headers" << newRequest.rawHeaderList() << newRequest.url(); return newRequest; } bool Player::play() const { if(this->url.isEmpty()) return false; if(!updater->isActive()) this->updater->start(500); this->player->play(); return true; } void Player::pause() const { if(this->player->isAvailable()) this->player->pause(); } void Player::stop() { if(this->player->isAvailable()) { this->player->stop(); this->url = QString(); this->player->setMedia(QMediaContent()); } this->playing = false; emit this->playingChanged(); this->updater->stop(); this->emitState(); } void Player::emitState() { switch(this->player->state()) { case QMediaPlayer::PlayingState: this->state = Player::STATE::PLAYING; break; case QMediaPlayer::PausedState: this->state = Player::STATE::PAUSED; break; case QMediaPlayer::StoppedState: this->state = Player::STATE::STOPED; break; } emit this->stateChanged(); } QString Player::transformTime(const int &pos) { return BAE::transformTime(pos); } void Player::setUrl(const QUrl &value) { // if(value == this->url) // return; this->url = value; emit this->urlChanged(); this->pos = 0; emit this->posChanged(); const auto media = this->url.isLocalFile() ? QMediaContent(this->url) : QMediaContent(getOcsRequest(QNetworkRequest(this->url))); this->player->setMedia(media); this->emitState(); } QUrl Player::getUrl() const { return this->url; } void Player::setVolume(const int &value) { if(value == this->volume) return; this->volume = value; this->player->setVolume(volume); emit this->volumeChanged(); } int Player::getVolume() const { return this->volume; } int Player::getDuration() const { return static_cast(this->player->duration()); } Player::STATE Player::getState() const { return this->state; } void Player::setPlaying(const bool &value) { this->playing = value; if(this->playing) this->play(); else this->pause(); emit this->playingChanged(); this->emitState(); } bool Player::getPlaying() const { return this->playing; } bool Player::getFinished() { return this->finished; } void Player::setPos(const int &value) { this->pos = value; this->player->setPosition(this->player->duration() / 1000 * this->pos); this->emitState(); this->posChanged(); } int Player::getPos() const { return this->pos; } void Player::update() { if(this->player->isAvailable()) { this->pos = static_cast(static_cast(this->player->position())/this->player->duration()*1000);; emit this->durationChanged(); emit this->posChanged(); } if(this->player->state() == QMediaPlayer::StoppedState && this->updater->isActive() && this->player->position() == this->player->duration()) { this->finished = true; emit this->finishedChanged(); } this->emitState(); } diff --git a/services/local/taginfo.cpp b/services/local/taginfo.cpp index 0c88781..2c607be 100644 --- a/services/local/taginfo.cpp +++ b/services/local/taginfo.cpp @@ -1,151 +1,152 @@ /* Babe - tiny music player Copyright (C) 2017 Camilo Higuita This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "taginfo.h" #include "../../utils/bae.h" using namespace BAE; TagInfo::TagInfo(const QString &url, QObject *parent) : QObject(parent) { this->path = url; QFileInfo _file(this->path); if(_file.isReadable()) { - this->file = new TagLib::FileRef(TagLib::FileName(this->path.toStdWString().c_str())); + qDebug()<< "is readble"; + this->file = new TagLib::FileRef(path.toUtf8()); } else this->file = new TagLib::FileRef(); } TagInfo::~TagInfo() { qDebug()<< "DELETING TAGINFO"; - // delete this->file; + delete this->file; } bool TagInfo::isNull() { - return this->file->isNull() && this->file->tag() == nullptr ; + return this->file->isNull(); } QString TagInfo::getAlbum() const { const auto value = QString::fromStdWString(file->tag()->album().toWString()); return !value.isEmpty() ? value : SLANG[W::UNKNOWN]; } QString TagInfo::getTitle() const { const auto value = QString::fromStdWString(file->tag()->title().toWString()); return !value.isEmpty() ? value : fileName(); } QString TagInfo::getArtist() const { const auto value = QString::fromStdWString(file->tag()->artist().toWString()); return !value.isEmpty() ? value : SLANG[W::UNKNOWN]; } int TagInfo::getTrack() const { return static_cast(file->tag()->track()); } QString TagInfo::getGenre() const { const auto value = QString::fromStdWString(file->tag()->genre().toWString()); return !value.isEmpty() ? value : SLANG[W::UNKNOWN]; } QString TagInfo::fileName() const { return BAE::getNameFromLocation(path); } uint TagInfo::getYear() const { return file->tag()->year(); } int TagInfo::getDuration() const { return file->audioProperties()->length(); } QString TagInfo::getComment() const { const auto value = QString::fromStdWString(file->tag()->comment().toWString()); return !value.isEmpty() ?value : SLANG[W::UNKNOWN]; } QByteArray TagInfo::getCover() const { QByteArray array; return array; } void TagInfo::setCover(const QByteArray &array) { Q_UNUSED(array); } void TagInfo::setComment(const QString &comment) { this->file->tag()->setComment(comment.toStdString()); this->file->save(); } void TagInfo::setAlbum(const QString &album) { this->file->tag()->setAlbum(album.toStdString()); this->file->save(); } void TagInfo::setTitle(const QString &title) { this->file->tag()->setTitle(title.toStdString()); this->file->save(); } void TagInfo::setTrack(const int &track) { this->file->tag()->setTrack(static_cast(track)); this->file->save(); } void TagInfo::setArtist(const QString &artist) { this->file->tag()->setArtist(artist.toStdString()); this->file->save(); } void TagInfo::setGenre(const QString &genre) { this->file->tag()->setGenre(genre.toStdString()); this->file->save(); } diff --git a/view_models/BabeList.qml b/view_models/BabeList.qml index fe590ff..3b7c0f0 100644 --- a/view_models/BabeList.qml +++ b/view_models/BabeList.qml @@ -1,36 +1,36 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.6 as Kirigami import org.kde.mauikit 1.0 as Maui Maui.Page { id: control property alias listView : babeList.listView property alias model : babeList.model property alias delegate : babeList.delegate property alias count : babeList.count property alias currentIndex : babeList.currentIndex property alias currentItem : babeList.currentItem property alias holder : babeList.holder property alias section : babeList.section property bool wasPulled : false signal pulled() Maui.ListBrowser { id: babeList anchors.fill: parent holder.visible: count === 0 topMargin: Maui.Style.space.medium - listView.headerPositioning: ListView.PullBackHeader + listView.headerPositioning: Kirigami.Settings.isMobile ? ListView.PullBackHeader : ListView.OverlayHeader listView.footerPositioning: ListView.OverlayFooter Kirigami.Theme.colorSet: Kirigami.Theme.Window Kirigami.Theme.inherit: false } } diff --git a/view_models/BabeTable/PlaylistDialog.qml b/view_models/BabeTable/PlaylistDialog.qml index 934d05c..90817b4 100644 --- a/view_models/BabeTable/PlaylistDialog.qml +++ b/view_models/BabeTable/PlaylistDialog.qml @@ -1,108 +1,108 @@ import QtQuick 2.0 import QtQuick.Controls 2.10 import org.kde.kirigami 2.2 as Kirigami import org.kde.mauikit 1.0 as Maui import QtQuick.Layouts 1.3 import PlaylistsList 1.0 import "../../view_models" import "../../utils/Player.js" as Player import "../../db/Queries.js" as Q Maui.Dialog { - title: "Add "+ tracks.length +" tracks to..." property var tracks : [] maxHeight: 400 * Maui.Style.unit page.padding: Maui.Style.space.medium - + acceptButton.text: qsTr("Save") + rejectButton.text: qsTr("Cancel") ColumnLayout { anchors.fill: parent BabeList { id: dialogList Layout.fillHeight: true Layout.fillWidth: true headBar.visible: false holder.title: qsTr("There's not playlists") holder.body: qsTr("Create a new one and start adding tracks to it") model: Maui.BaseModel { list: playlistsList } delegate: Maui.ListDelegate { id: delegate label: model.playlist Connections { target: delegate onClicked: dialogList.currentIndex = index onPressAndHold: { dialogList.currentIndex = index insert() } } } } Maui.TextField { Layout.fillWidth: true id: newPlaylistField color: Kirigami.Theme.textColor placeholderText: qsTr("New playlist") onAccepted: { addPlaylist() playlistsList.addTrack(dialogList.listView.currentIndex, tracks) clear() } actions.data: ToolButton { icon.name: "checkbox" icon.color: Kirigami.Theme.textColor onClicked: addPlaylist() } } } onAccepted: { if(newPlaylistField.text.length) addPlaylist() insert() } function insert() { playlistsList.addTrack(dialogList.listView.currentIndex, tracks) close() } function addPlaylist() { if (newPlaylistField.text) { var title = newPlaylistField.text.trim() if(playlistsList.insert(title)) { dialogList.currentIndex = 2 dialogList.listView.positionViewAtEnd() } newPlaylistField.clear() } } } diff --git a/widgets/CloudView/CloudView.qml b/widgets/CloudView/CloudView.qml index eb7622a..0f9b543 100644 --- a/widgets/CloudView/CloudView.qml +++ b/widgets/CloudView/CloudView.qml @@ -1,285 +1,293 @@ import QtQuick 2.10 import QtQuick.Controls 2.10 import QtQuick.Layouts 1.3 import org.kde.mauikit 1.0 as Maui import org.kde.kirigami 2.7 as Kirigami import "../../view_models/BabeTable" import "../../view_models/BabeGrid" import "../../utils/Player.js" as Player import CloudList 1.0 Maui.Page { id: control property alias list : _cloudList Maui.BaseModel { id: _cloudModel list: _cloudList } Cloud { id: _cloudList onFileReady: Player.addTrack(track) } headBar.visible: !_listView.holder.visible headBar.leftContent: [ ToolButton { id : playAllBtn // text: qsTr("Play all") icon.name : "media-playlist-play" // onClicked: playAll() }, ToolButton { id: appendBtn // text: qsTr("Append") icon.name : "media-playlist-append"//"media-repeat-track-amarok" // onClicked: appendAll() }] headBar.rightContent: [ ToolButton { icon.name: "item-select" onClicked: selectionMode = !selectionMode checkable: false checked: selectionMode }, Maui.ToolButtonMenu { id: sortBtn icon.name: "view-sort" MenuItem { text: qsTr("Title") checkable: true checked: list.sortBy === Cloud.TITLE onTriggered: list.sortBy = Cloud.TITLE autoExclusive: true } MenuItem { text: qsTr("Track") checkable: true checked: list.sortBy === Cloud.TRACK onTriggered: list.sortBy = Cloud.TRACK autoExclusive: true } MenuItem { text: qsTr("Artist") checkable: true checked: list.sortBy === Cloud.ARTIST onTriggered: list.sortBy = Cloud.ARTIST autoExclusive: true } MenuItem { text: qsTr("Album") checkable: true checked: list.sortBy === Cloud.ALBUM onTriggered: list.sortBy = Cloud.ALBUM autoExclusive: true } MenuItem { text: qsTr("Most played") checkable: true checked: list.sortBy === Cloud.COUNT onTriggered: list.sortBy = Cloud.COUNT autoExclusive: true } MenuItem { text: qsTr("Rate") checkable: true checked: list.sortBy === Cloud.RATE onTriggered: list.sortBy = Cloud.RATE autoExclusive: true } MenuItem { text: qsTr("Favorite") checkable: true checked: list.sortBy === Cloud.FAV onTriggered: list.sortBy = Cloud.FAV autoExclusive: true } MenuItem { text: qsTr("Release date") checkable: true checked: list.sortBy === Cloud.RELEASEDATE onTriggered: list.sortBy = Cloud.RELEASEDATE autoExclusive: true } MenuItem { text: qsTr("Add date") checkable: true checked: list.sortBy === Cloud.ADDDATE onTriggered: list.sortBy = Cloud.ADDDATE autoExclusive: true } MenuSeparator{} MenuItem { text: qsTr("Group") checkable: true // checked: group onTriggered: group = !group } } ] Maui.ListBrowser { id: _listView anchors.fill: parent clip: true holder.visible: count === 0 holder.emoji: "qrc:/assets/dialog-information.svg" holder.title : qsTr("Opps!") holder.body: qsTr("You don't have an account set up.\nYou can set up your account now by clicking here or under the Accounts options in the main menu") + listView.spacing: Maui.Style.space.small * (Kirigami.Settings.isMobile ? 1.4 : 1.2) Connections { target: _listView.holder onActionTriggered: { if(root.accounts) root.accounts.open() } } topMargin: Maui.Style.space.medium model: _cloudModel section.property: "artist" section.criteria: ViewSection.FullString section.delegate: Maui.LabelDelegate { id: _sectionDelegate label: section isSection: true width: parent.width Kirigami.Theme.backgroundColor: "#333" Kirigami.Theme.textColor: "#fafafa" background: Rectangle { color: Kirigami.Theme.backgroundColor } } listView.header: Rectangle { Kirigami.Theme.inherit: false width: parent.width height: 150 z: _listView.listView.z+999 color: Kirigami.Theme.backgroundColor visible: _headList.count > 0 ListView { id: _headList anchors.fill: parent anchors.margins: Maui.Style.space.medium spacing: Maui.Style.space.medium orientation: ListView.Horizontal model: list.artists delegate: BabeAlbum { height: 120 width: height albumRadius: Maui.Style.radiusV isCurrentItem: ListView.isCurrentItem anchors.verticalCenter: parent.verticalCenter showLabels: true label1.text: modelData.album ? modelData.album : modelData.artist label2.text: modelData.artist && modelData.album ? modelData.artist : "" image.source: modelData.artwork ? modelData.artwork : "qrc:/assets/cover.png" } } } listView.headerPositioning: ListView.PullBackHeader delegate: TableDelegate { id: delegate width: parent.width number : false coverArt : false + + ToolButton + { + icon.name: "document-download" + Layout.fillHeight: true + } + onClicked: { _listView.currentIndex = index // if(selectionMode) // { // H.addToSelection(control.list.get(_listView.currentIndex)) // return // } list.getFileUrl(index); // if(isMobile) // rowClicked(index) } // onDoubleClicked: // { // currentIndex = index // if(!isMobile) // rowClicked(index) // } // onPlay: // { // currentIndex = index // if(Maui.FM.fileExists("file://" + _cloudList.get(index).thumbnail)) // { // quickPlayTrack(index) // }else // { // _cloudList.requestFile(index) // } // } // onArtworkCoverClicked: // { // currentIndex = index // goToAlbum() // } } } } diff --git a/widgets/FocusView.qml b/widgets/FocusView.qml index d419cc7..677ffb6 100644 --- a/widgets/FocusView.qml +++ b/widgets/FocusView.qml @@ -1,440 +1,439 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import "../utils/Player.js" as Player import QtGraphicalEffects 1.0 Maui.Page { id: control visible: focusView parent: ApplicationWindow.overlay anchors.fill: parent z: parent.z + 99999 Kirigami.Theme.inherit: false Kirigami.Theme.colorSet: Kirigami.Theme.View focus: true Component.onCompleted: { _drawer.visible = false forceActiveFocus() } Component.onDestruction: { _drawer.visible = true } headBar.background: null headBar.height: Maui.Style.toolBarHeight headBar.leftContent: ToolButton { icon.name: "go-previous" onClicked: focusView = false } Keys.onBackPressed: { focusView = false event.accepted = true } Shortcut { sequence: StandardKey.Back onActivated: focusView = false } ColumnLayout { anchors.fill: parent anchors.margins: Maui.Style.space.big RowLayout { Layout.fillWidth: true Layout.preferredHeight: width Item { Layout.fillHeight: true Layout.preferredWidth: Maui.Style.iconSizes.big Rectangle { visible: (_listView.currentIndex > 0) && (_listView.count > 1) height: Maui.Style.iconSizes.small width : height radius: height color: Kirigami.Theme.textColor opacity: 0.4 anchors.centerIn: parent } } ListView { id: _listView Layout.fillWidth: true Layout.fillHeight: true orientation: ListView.Horizontal clip: true focus: true interactive: true currentIndex: currentTrackIndex spacing: Maui.Style.space.medium cacheBuffer: control.width * 1 onCurrentIndexChanged: positionViewAtIndex(currentIndex, ListView.Center) highlightFollowsCurrentItem: true highlightMoveDuration: 0 snapMode: ListView.SnapToOneItem model: mainPlaylist.listModel highlightRangeMode: ListView.StrictlyEnforceRange keyNavigationEnabled: true keyNavigationWraps : true onCurrentItemChanged: { var index = indexAt(contentX, contentY) if(index !== currentTrackIndex) Player.playAt(index) } delegate: Item { id: _delegate height: _listView.height width: _listView.width property bool isCurrentItem : ListView.isCurrentItem Rectangle { id: _bg width: _image.width + Maui.Style.space.medium height: width anchors.centerIn: parent radius: height color: Kirigami.Theme.backgroundColor } DropShadow { anchors.fill: _bg horizontalOffset: 0 verticalOffset: 0 radius: 8.0 samples: 17 color: "#80000000" source: _bg } RotationAnimator on rotation { from: 0 to: 360 duration: 5000 loops: Animation.Infinite running: root.isPlaying && isCurrentItem } Image { id: _image width: Math.min(parent.width, parent.height) * 0.9 height: width anchors.centerIn: parent sourceSize.width: height sourceSize.height: height fillMode: Image.PreserveAspectFit antialiasing: false smooth: true asynchronous: true source: model.artwork ? model.artwork : "qrc:/assets/cover.png" onStatusChanged: { if (status == Image.Error) source = "qrc:/assets/cover.png"; } Rectangle { color: control.Kirigami.Theme.backgroundColor height: parent.height * 0.25 width: height anchors.centerIn: parent radius: height } Rectangle { id: _roundRec color: control.Kirigami.Theme.backgroundColor height: parent.height * 0.20 width: height anchors.centerIn: parent radius: height } InnerShadow { anchors.fill: _roundRec radius: 8.0 samples: 16 horizontalOffset: 0 verticalOffset: 0 color: "#b0000000" source: _roundRec } layer.enabled: true layer.effect: OpacityMask { maskSource: Item { width: _image.width height: _image.height Rectangle { anchors.centerIn: parent width: _image.width height: _image.height radius: height } } } } } } Item { Layout.fillHeight: true Layout.preferredWidth: Maui.Style.iconSizes.big Rectangle { visible: (_listView.currentIndex < _listView.count - 1) && (_listView.count > 1) height: Maui.Style.iconSizes.small width : height radius: height color: Kirigami.Theme.textColor opacity: 0.4 anchors.centerIn: parent } } } RowLayout { Layout.fillWidth: true Layout.preferredHeight: Maui.Style.toolBarHeight ToolButton { icon.name: "view-list-details" onClicked: focusView = false Layout.alignment: Qt.AlignCenter } ColumnLayout { Layout.fillWidth: true Layout.fillHeight: true Layout.alignment: Qt.AlignCenter spacing: 0 Label { id: _label1 visible: text.length Layout.fillWidth: true Layout.fillHeight: false verticalAlignment: Qt.AlignVCenter horizontalAlignment: Qt.AlignHCenter text: currentTrack.title elide: Text.ElideMiddle wrapMode: Text.NoWrap color: control.Kirigami.Theme.textColor font.weight: Font.Normal font.pointSize: Maui.Style.fontSizes.huge } Label { id: _label2 visible: text.length Layout.fillWidth: true Layout.fillHeight: false verticalAlignment: Qt.AlignVCenter horizontalAlignment: Qt.AlignHCenter text: currentTrack.artist elide: Text.ElideMiddle wrapMode: Text.NoWrap color: control.Kirigami.Theme.textColor font.weight: Font.Normal font.pointSize: Maui.Style.fontSizes.big opacity: 0.7 } } ToolButton { icon.name: "documentinfo" onClicked: focusView = false Layout.alignment: Qt.AlignCenter } } RowLayout { Layout.fillWidth: true Label { visible: text.length Layout.fillWidth: true Layout.fillHeight: false verticalAlignment: Qt.AlignVCenter horizontalAlignment: Qt.AlignHCenter text: progressTimeLabel elide: Text.ElideMiddle wrapMode: Text.NoWrap color: control.Kirigami.Theme.textColor font.weight: Font.Normal font.pointSize: Maui.Style.fontSizes.medium opacity: 0.7 } Slider { id: progressBar Layout.fillWidth: true padding: 0 from: 0 to: 1000 value: player.pos spacing: 0 focus: true onMoved: { player.pos = value } } Label { visible: text.length Layout.fillWidth: true Layout.fillHeight: false verticalAlignment: Qt.AlignVCenter horizontalAlignment: Qt.AlignHCenter text: player.transformTime(player.duration/1000) elide: Text.ElideMiddle wrapMode: Text.NoWrap color: control.Kirigami.Theme.textColor font.weight: Font.Normal font.pointSize: Maui.Style.fontSizes.medium opacity: 0.7 } } Maui.ToolBar { Layout.preferredHeight: Maui.Style.toolBarHeight * 2 Layout.fillWidth: true position: ToolBar.Footer background: null middleContent: [ ToolButton { id: babeBtnIcon icon.width: Maui.Style.iconSizes.big icon.height: Maui.Style.iconSizes.big icon.name: "love" enabled: currentTrackIndex >= 0 - icon.color: currentBabe ? babeColor : Kirigami.Theme.textColor + icon.color: currentTrack.fav == "0" ? babeColor : Kirigami.Theme.textColor onClicked: if (!mainlistEmpty) { mainPlaylist.list.fav(currentTrackIndex, !(mainPlaylist.list.get(currentTrackIndex).fav == "1")) - currentBabe = mainPlaylist.list.get(currentTrackIndex).fav == "1" } }, ToolButton { icon.name: "media-skip-backward" icon.color: Kirigami.Theme.textColor icon.width: Maui.Style.iconSizes.big icon.height: Maui.Style.iconSizes.big onClicked: Player.previousTrack() onPressAndHold: Player.playAt(prevTrackIndex) }, ToolButton { id: playIcon icon.width: Maui.Style.iconSizes.huge icon.height: Maui.Style.iconSizes.huge enabled: currentTrackIndex >= 0 icon.color: Kirigami.Theme.textColor icon.name: isPlaying ? "media-playback-pause" : "media-playback-start" onClicked: player.playing = !player.playing }, ToolButton { id: nextBtn icon.color: Kirigami.Theme.textColor icon.width: Maui.Style.iconSizes.big icon.height: Maui.Style.iconSizes.big icon.name: "media-skip-forward" onClicked: Player.nextTrack() onPressAndHold: Player.playAt(Player.shuffle()) }, ToolButton { id: shuffleBtn icon.width: Maui.Style.iconSizes.big icon.height: Maui.Style.iconSizes.big icon.color: babeColor icon.name: isShuffle ? "media-playlist-shuffle" : "media-playlist-normal" onClicked: { isShuffle = !isShuffle Maui.FM.saveSettings("SHUFFLE", isShuffle, "PLAYBACK") } } ] } } }