Commit 61208aed authored by Camilo higuita's avatar Camilo higuita

cloud integration intial work

parent a64320e6
......@@ -223,12 +223,12 @@ QString Babe::albumArt(const QString &album, const QString &artist)
void Babe::fetchTrackLyrics(FMH::MODEL &song)
{
pulpo->registerServices({SERVICES::LyricWikia, SERVICES::Genius});
pulpo->setOntology(PULPO::ONTOLOGY::TRACK);
pulpo->setInfo(PULPO::INFO::LYRICS);
this->pulpo->registerServices({SERVICES::LyricWikia, SERVICES::Genius});
this->pulpo->setOntology(PULPO::ONTOLOGY::TRACK);
this->pulpo->setInfo(PULPO::INFO::LYRICS);
qDebug()<<"STARTED FETCHING LYRICS";
pulpo->feed(song, PULPO::RECURSIVE::OFF);
this->pulpo->feed(song, PULPO::RECURSIVE::OFF);
qDebug()<<"DONE FETCHING LYRICS";
}
......
......@@ -36,6 +36,7 @@
#include "models/tracks/tracksmodel.h"
#include "models/albums/albumsmodel.h"
#include "models/playlists/playlistsmodel.h"
#include "models/cloud/cloud.h"
#include "models/baselist.h"
#include "models/basemodel.h"
......@@ -110,6 +111,7 @@ int main(int argc, char *argv[])
qmlRegisterType<TracksModel>("TracksList", 1, 0, "Tracks");
qmlRegisterType<PlaylistsModel>("PlaylistsList", 1, 0, "Playlists");
qmlRegisterType<AlbumsModel>("AlbumsList", 1, 0, "Albums");
qmlRegisterType<Cloud>("CloudList", 1, 0, "CloudList");
qmlRegisterType<Player>("Player", 1, 0, "Player");
......
......@@ -12,6 +12,7 @@ import "widgets/PlaylistsView"
import "widgets/MainPlaylist"
import "widgets/SettingsView"
import "widgets/SearchView"
import "widgets/CloudView"
import "view_models"
import "view_models/BabeTable"
......@@ -22,6 +23,8 @@ 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
......@@ -58,7 +61,7 @@ Maui.ApplicationWindow
stars: "0"
})
property int currentTrackIndex: 0
property int currentTrackIndex: -1
property int prevTrackIndex: 0
property string currentArtwork: !mainlistEmpty ? mainPlaylist.list.get(0).artwork : ""
......@@ -303,6 +306,13 @@ Maui.ApplicationWindow
searchView.searchInput.forceActiveFocus()
}
InfoView
{
id: infoView
maxWidth: parent.width * 0.8
maxHeight: parent.height * 0.9
}
Maui.ShareDialog
{
id: shareDialog
......@@ -311,10 +321,6 @@ Maui.ApplicationWindow
Maui.FileDialog
{
id: fmDialog
onlyDirs: false
filterType: FMList.AUDIO
sortBy: FMList.MODIFIED
mode: modes.OPEN
}
SourcesDialog
......@@ -499,7 +505,7 @@ Maui.ApplicationWindow
anchors.fill: parent
z: -999
color: altColor
opacity: opacityLevel
opacity: 0.8
SequentialAnimation
{
......@@ -636,11 +642,18 @@ Maui.ApplicationWindow
{
target: tracksView
onRowClicked: Player.addTrack(tracksView.list.get(index))
onQuickPlayTrack: Player.quickPlay(tracksView.list.get(index))
onPlayAll: Player.playAll(bae.get(Q.GET.allTracks))
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.append(Q.GET.allTracks)
mainPlaylist.list.appendQuery(Q.GET.allTracks)
mainPlaylist.listView.positionViewAtEnd()
}
......@@ -743,6 +756,7 @@ Maui.ApplicationWindow
onAppendAll:
{
var query = Q.GET.artistTracks_.arg(artist)
mainPlaylist.list.appendQuery(query)
mainPlaylist.listView.positionViewAtEnd()
}
......@@ -752,7 +766,9 @@ Maui.ApplicationWindow
PlaylistsView
{
id: playlistsView
Connections {
Connections
{
target: playlistsView
onRowClicked: Player.addTrack(track)
onQuickPlayTrack: Player.quickPlay(track)
......@@ -804,12 +820,32 @@ Maui.ApplicationWindow
onRowClicked: Player.addTrack(foldersView.list.model.get(index))
onQuickPlayTrack: Player.quickPlay(foldersView.list.model.get(index))
onPlayAll: Player.playAll(foldersView.getTracks())
onAppendAll: Player.appendAll(foldersView.getTracks())
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
......@@ -915,6 +951,7 @@ Maui.ApplicationWindow
onTrackLyricsReady:
{
console.log(lyrics)
if (url === currentTrack.url)
Player.setLyrics(lyrics)
}
......
#include "cloud.h"
#include "services/local/taginfo.h"
Cloud::Cloud(QObject *parent) : BaseList (parent)
{
this->fm = FM::getInstance();
this->setList();
connect(this->fm, &FM::cloudServerContentReady, [this](const FMH::MODEL_LIST &list, const QString &url)
{
Q_UNUSED(url);
emit this->preListChanged();
this->list = list;
this->formatList();
emit this->postListChanged();
});
connect(this->fm, &FM::warningMessage, [this](const QString &message)
{
emit this->warning(message);
});
connect(this->fm, &FM::newItem, [this](const FMH::MODEL &item, const QString &path)
{
auto newItem = item;
auto url = item[FMH::MODEL_KEY::URL];
auto thumbnail = item[FMH::MODEL_KEY::THUMBNAIL];
newItem[FMH::MODEL_KEY::FAV] = QString("0");
newItem[FMH::MODEL_KEY::RATE] = QString("0");
newItem[FMH::MODEL_KEY::URL] = FMH::fileExists(thumbnail)? thumbnail : item[FMH::MODEL_KEY::URL];
newItem[FMH::MODEL_KEY::SOURCE] = FMH::fileExists(thumbnail)? thumbnail : item[FMH::MODEL_KEY::PATH];
if(FMH::fileExists(thumbnail))
{
qDebug()<< "file exists:" << thumbnail;
TagInfo info;
info.feed(thumbnail);
newItem[FMH::MODEL_KEY::ARTIST] = info.getArtist();
newItem[FMH::MODEL_KEY::ALBUM] = info.getAlbum();
newItem[FMH::MODEL_KEY::TITLE] = info.getTitle();
newItem[FMH::MODEL_KEY::RELEASEDATE] = QString::number(info.getYear());
newItem[FMH::MODEL_KEY::TRACK] = QString::number(info.getTrack());
}else
newItem[FMH::MODEL_KEY::TITLE] = item[FMH::MODEL_KEY::LABEL];
emit this->preItemAppended();
this->list << newItem;
emit this->postItemAppended();
});
connect(this->fm, &FM::cloudItemReady, [this](const FMH::MODEL &item, const QString &path)
{
qDebug()<< "REQUESTED CLOUD IMAGE READY << " << item;
Q_UNUSED(path);
auto newItem = item;
auto url = item[FMH::MODEL_KEY::URL];
auto thumbnail = item[FMH::MODEL_KEY::THUMBNAIL];
newItem[FMH::MODEL_KEY::FAV] = QString("0");
newItem[FMH::MODEL_KEY::RATE] = QString("0");
newItem[FMH::MODEL_KEY::URL] = FMH::fileExists(thumbnail)? thumbnail : item[FMH::MODEL_KEY::URL];
newItem[FMH::MODEL_KEY::SOURCE] = FMH::fileExists(thumbnail)? thumbnail : item[FMH::MODEL_KEY::PATH];
if(FMH::fileExists(thumbnail))
{
qDebug()<< "file exists:" << thumbnail;
TagInfo info;
info.feed(thumbnail);
newItem[FMH::MODEL_KEY::ARTIST] = info.getArtist();
newItem[FMH::MODEL_KEY::ALBUM] = info.getAlbum();
newItem[FMH::MODEL_KEY::TITLE] = info.getTitle();
newItem[FMH::MODEL_KEY::RELEASEDATE] = QString::number(info.getYear());
newItem[FMH::MODEL_KEY::TRACK] = QString::number(info.getTrack());
}else
newItem[FMH::MODEL_KEY::TITLE] = item[FMH::MODEL_KEY::LABEL];
this->update(FM::toMap(newItem), this->pending.take(QString(item[FMH::MODEL_KEY::PATH]).replace(FMH::CloudCachePath+"opendesktop", FMH::PATHTYPE_NAME[FMH::PATHTYPE_KEY::CLOUD_PATH])));
emit this->cloudItemReady(FM::toMap(newItem));
});
}
FMH::MODEL_LIST Cloud::items() const
{
return this->list;
}
void Cloud::setAccount(const QString value)
{
if(this->account == value)
return;
this->account = value;
emit this->accountChanged();
this->setList();
}
QString Cloud::getAccount() const
{
return this->account;
}
void Cloud::setList()
{
emit this->preListChanged();
this->list.clear();
this->fm->getCloudServerContent(FMH::PATHTYPE_NAME[FMH::PATHTYPE_KEY::CLOUD_PATH]+"/"+this->account, FMH::FILTER_LIST[FMH::FILTER_TYPE::AUDIO], 3);
emit this->postListChanged();
}
void Cloud::formatList()
{
for(auto &item : this->list)
{
auto url = item[FMH::MODEL_KEY::URL];
auto thumbnail = item[FMH::MODEL_KEY::THUMBNAIL];
item[FMH::MODEL_KEY::FAV] = QString("0");
item[FMH::MODEL_KEY::RATE] = QString("0");
item[FMH::MODEL_KEY::URL] = FMH::fileExists(thumbnail)? thumbnail : item[FMH::MODEL_KEY::URL];
item[FMH::MODEL_KEY::SOURCE] = FMH::fileExists(thumbnail)? thumbnail : item[FMH::MODEL_KEY::PATH];
qDebug()<< "CLOUD FILE" << thumbnail;
if(FMH::fileExists(thumbnail))
{
qDebug()<< "file exists:" << thumbnail;
TagInfo info;
info.feed(thumbnail);
item[FMH::MODEL_KEY::ARTIST] = info.getArtist();
item[FMH::MODEL_KEY::ALBUM] = info.getAlbum();
item[FMH::MODEL_KEY::TITLE] = info.getTitle();
item[FMH::MODEL_KEY::RELEASEDATE] = QString::number(info.getYear());
item[FMH::MODEL_KEY::TRACK] = QString::number(info.getTrack());
}else
{
item[FMH::MODEL_KEY::TITLE] = item[FMH::MODEL_KEY::LABEL];
}
}
}
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;
}
void Cloud::requestFile(const int &index)
{
if(index < 0 || index >= this->list.size())
return;
this->pending.insert(this->list[index][FMH::MODEL_KEY::PATH], index);
qDebug()<< "1-PEDNIGN CLOUD"<< this->pending;
this->fm->getCloudItem(FM::toMap(this->list[index]));
}
bool Cloud::update(const QVariantMap &data, const int &index)
{
if(index < 0 || index >= this->list.size())
return false;
auto newData = this->list[index];
QVector<int> 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;
}
void Cloud::upload(const QString &url)
{
this->fm->copy({FMH::getFileInfo(url)}, FMH::PATHTYPE_NAME[FMH::PATHTYPE_KEY::CLOUD_PATH]+"/"+this->account);
}
#ifndef CLOUD_H
#define CLOUD_H
#include <QObject>
#include "../baselist.h"
class FM;
class Cloud : public BaseList
{
Q_OBJECT
Q_PROPERTY(QString account READ getAccount WRITE setAccount NOTIFY accountChanged)
public:
enum SORTBY : uint_fast8_t
{
SIZE = FMH::MODEL_KEY::SIZE,
MODIFIED = FMH::MODEL_KEY::MODIFIED,
DATE = FMH::MODEL_KEY::DATE,
LABEL = FMH::MODEL_KEY::LABEL,
MIME = FMH::MODEL_KEY::MIME
}; Q_ENUM(SORTBY)
explicit Cloud(QObject *parent = nullptr);
FMH::MODEL_LIST items() const override;
void setAccount(const QString value);
QString getAccount() const;
private:
FMH::MODEL_LIST list;
void setList();
void formatList();
QHash<QString, int> pending;
QString account;
FM *fm;
public slots:
QVariantMap get(const int &index) const override;
void requestFile(const int &index);
bool update(const QVariantMap &data, const int &index) override;
void upload(const QString &url);
signals:
void accountChanged();
void cloudItemReady(QVariantMap item);
void warning(QString error);
};
#endif // CLOUD_H
......@@ -179,11 +179,12 @@ void TracksModel::append(const QVariantMap &item, const int &at)
void TracksModel::appendQuery(const QString &query)
{
if(this->query.isEmpty())
if(query.isEmpty() || query == this->query)
return;
emit this->preListChanged();
this->query = query;
emit this->preListChanged();
this->list << this->db->getDBData(query);
emit this->postListChanged();
......
......@@ -75,22 +75,22 @@ QStringList Pulpo::queryHtml(const QByteArray &array, const QString &className)
{
QStringList res;
// auto doc = QGumboDocument::parse(array);
// auto root = doc.rootNode();
// auto doc = QGumboDocument::parse(array);
// auto root = doc.rootNode();
// auto nodes = root.getElementsByTagName(HtmlTag::TITLE);
// Q_ASSERT(nodes.size() == 1);
// auto nodes = root.getElementsByTagName(HtmlTag::TITLE);
// Q_ASSERT(nodes.size() == 1);
// auto title = nodes.front();
// qDebug() << "title is: " << title.innerText();
// auto title = nodes.front();
// qDebug() << "title is: " << title.innerText();
// auto container = root.getElementsByClassName(className);
// // if(container.size() == 1)
// // return res;
// auto container = root.getElementsByClassName(className);
// // if(container.size() == 1)
// // return res;
// auto children = container.front().children();
// for(const auto &i : children)
// res << i.innerText();
// auto children = container.front().children();
// for(const auto &i : children)
// res << i.innerText();
return res;
......@@ -231,8 +231,6 @@ bool Pulpo::parseArray()
case PULPO::ONTOLOGY::TRACK: return this->parseTrack();
default: return false;
}
return false;
}
QByteArray Pulpo::startConnection(const QString &url, const QMap<QString,QString> &headers)
......@@ -251,8 +249,7 @@ QByteArray Pulpo::startConnection(const QString &url, const QMap<QString,QString
QEventLoop loop;
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), &loop,
SLOT(quit()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), &loop, SLOT(quit()));
loop.exec();
......@@ -274,5 +271,23 @@ QByteArray Pulpo::startConnection(const QString &url, const QMap<QString,QString
return QByteArray();
}
void Pulpo::startConnectionAsync(const QString &url, const QMap<QString,QString> &headers)
{
if(!url.isEmpty())
{
auto downloader = new FMH::Downloader;
connect(downloader, &FMH::Downloader::dataReady, [=](QByteArray array)
{
qDebug()<< "DATA READY << " << array;
emit this->arrayReady(array);
// downloader->deleteLater();
});
qDebug()<< "trying to get lyrics for "<< url;
downloader->getArray(url);
}
}
......@@ -23,51 +23,55 @@ using namespace PULPO;
class Pulpo : public QObject
{
Q_OBJECT
Q_OBJECT
public:
explicit Pulpo(const FMH::MODEL &song, QObject *parent = nullptr);
explicit Pulpo(QObject *parent = nullptr);
~Pulpo();
public:
explicit Pulpo(const FMH::MODEL &song, QObject *parent = nullptr);
explicit Pulpo(QObject *parent = nullptr);
~Pulpo();
bool feed(const FMH::MODEL &song, const PULPO::RECURSIVE &recursive = PULPO::RECURSIVE::ON );
void registerServices(const QList<PULPO::SERVICES> &services);
void setInfo(const PULPO::INFO &info);
void setOntology(const PULPO::ONTOLOGY &ontology);
PULPO::ONTOLOGY getOntology();
void setRecursive(const PULPO::RECURSIVE &state);
bool feed(const FMH::MODEL &song, const PULPO::RECURSIVE &recursive = PULPO::RECURSIVE::ON );
void registerServices(const QList<PULPO::SERVICES> &services);
void setInfo(const PULPO::INFO &info);
void setOntology(const PULPO::ONTOLOGY &ontology);
PULPO::ONTOLOGY getOntology();
void setRecursive(const PULPO::RECURSIVE &state);
QStringList queryHtml(const QByteArray &array, const QString &className = QString());
QStringList queryHtml(const QByteArray &array, const QString &className = QString());
private:
void initServices();
PULPO::RECURSIVE recursive = PULPO::RECURSIVE::ON;
QList<SERVICES> registeredServices = {};
private:
void initServices();
PULPO::RECURSIVE recursive = PULPO::RECURSIVE::ON;
QList<SERVICES> registeredServices = {};
void passSignal(const FMH::MODEL &track, const PULPO::RESPONSE &response);
void passSignal(const FMH::MODEL &track, const PULPO::RESPONSE &response);
QNetworkAccessManager *manager;
QNetworkReply *reply;
protected:
QByteArray array;
FMH::MODEL track;
PULPO::INFO info = INFO::NONE;
PULPO::ONTOLOGY ontology = ONTOLOGY::NONE;
PULPO::AVAILABLE availableInfo;
protected:
QByteArray array;
FMH::MODEL track;
PULPO::INFO info = INFO::NONE;
PULPO::ONTOLOGY ontology = ONTOLOGY::NONE;
PULPO::AVAILABLE availableInfo;
PULPO::RESPONSE packResponse(const PULPO::ONTOLOGY ontology, const PULPO::INFO &infoKey, const PULPO::CONTEXT &contextName, const QVariant &value);
PULPO::RESPONSE packResponse(const PULPO::ONTOLOGY ontology, const PULPO::INFO &infoKey, const PULPO::VALUE &map);
PULPO::RESPONSE packResponse(const PULPO::ONTOLOGY ontology, const PULPO::INFO &infoKey, const PULPO::CONTEXT &contextName, const QVariant &value);
PULPO::RESPONSE packResponse(const PULPO::ONTOLOGY ontology, const PULPO::INFO &infoKey, const PULPO::VALUE &map);
QByteArray startConnection(const QString &url, const QMap<QString, QString> &headers = {});
bool parseArray();
QByteArray startConnection(const QString &url, const QMap<QString, QString> &headers = {});
void startConnectionAsync(const QString &url, const QMap<QString, QString> &headers = {});
bool parseArray();
/* expected methods to be overrided by services */
bool setUpService(const PULPO::ONTOLOGY &ontology, const PULPO::INFO &info);
virtual bool parseArtist() {return false;}
virtual bool parseAlbum() {return false;}
virtual bool parseTrack() {return false;}
/* expected methods to be overrided by services */
bool setUpService(const PULPO::ONTOLOGY &ontology, const PULPO::INFO &info);
virtual bool parseArtist() {return false;}
virtual bool parseAlbum() {return false;}
virtual bool parseTrack() {return false;}
signals:
void infoReady(FMH::MODEL track, PULPO::RESPONSE response);
void serviceFail(const QString &message);
signals:
void infoReady(FMH::MODEL track, PULPO::RESPONSE response);
void serviceFail(const QString &message);
void arrayReady(QByteArray array);
};
#endif // ARTWORK_H
......@@ -44,10 +44,8 @@ bool lyricWikia::setUpService(const PULPO::ONTOLOGY &ontology, const PULPO::INFO
return this->parseArray();
}
bool lyricWikia::parseTrack()
{
{
QString xmlData(this->array);
QDomDocument doc;
......
......@@ -71,5 +71,6 @@
<file>widgets/FoldersView.qml</file>
<file>assets/vvave.svg</file>
<file>assets/materialdesignicons-webfont.ttf</file>
<file>widgets/CloudView/CloudView.qml</file>
</qresource>
</RCC>
......@@ -58,7 +58,7 @@ function queueTracks(tracks)
function setLyrics(lyrics)
{
currentTrack.lyrics = lyrics
mainPlaylist.infoView.lyricsText.text = lyrics
infoView.lyricsText.text = lyrics
}
function stop()
......
......@@ -205,13 +205,16 @@ BabeList
contextMenu.close()
}
},
MenuSeparator {},
Maui.MenuItem
{
text: qsTr("Go to Artist")
onTriggered: goToArtist()
},
Maui.MenuItem
{
text: qsTr("Go to Album")
......@@ -247,6 +250,16 @@ BabeList
{
list.color(listView.currentIndex, color);
}
onInfoClicked:
{
infoView.show(list.get(listView.currentIndex))
}
onCopyToClicked:
{
cloudView.list.upload(listView.currentIndex)
}
}
listView.highlightFollowsCurrentItem: false
......
......@@ -27,8 +27,8 @@ Maui.Dialog
Layout.fillWidth: true
headBar.visible: false
holder.title: "There's not playlists"
holder.body: "Create a new one and start adding tracks to it"
holder.title: qsTr("There's not playlists")
holder.body: qsTr("Create a new one and start adding tracks to it")
model: playlistsView.playlistModel
......
......@@ -28,6 +28,8 @@ Maui.Menu
signal selectClicked(var paths)
signal rateClicked(var paths, int rate)
signal colorClicked(var paths, string color)
signal infoClicked()
signal copyToClicked()
property alias menuItem : control.content
......@@ -53,7 +55,7 @@ Maui.Menu
Maui.MenuItem
{
text: qsTr("Save to...")
text: qsTr("Add to...")
onTriggered:
{
saveToClicked(paths)
......@@ -61,6 +63,16 @@ Maui.Menu
}
}
Maui.MenuItem
{
text: qsTr("Copy to cloud")
onTriggered:
{
copyToClicked(paths)
close()
}
}
Maui.MenuItem
{
text: isAndroid ? qsTr("Open with...") : qsTr("Show in folder...")
......@@ -82,6 +94,16 @@ Maui.Menu
}
}
// Maui.MenuItem
// {
// text: qsTr("Info...")
// onTriggered:
// {
// infoClicked()
// close()
// }
// }