Merge pull request #62 from QuasarApp/default_qtPlayList

create song storage class
This commit is contained in:
Andrei Yankovich 2018-04-05 21:55:09 +03:00 committed by GitHub
commit 03bbae91fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 784 additions and 218 deletions

View File

@ -10,7 +10,7 @@ Item {
property int currentSongId: 0 property int currentSongId: 0
property string currentSongName: qsTr("Song is not selected") property string currentSongName: qsTr("Song is not selected")
property bool playState: false property bool playState: syncEngine.playState === 1;
function changeSong(id, name){ function changeSong(id, name){
@ -84,7 +84,6 @@ Item {
onClicked: { onClicked: {
syncEngine.pause(playState); syncEngine.pause(playState);
playState = !playState;
} }
} }
@ -123,7 +122,7 @@ Item {
anchors.right: parent.right; anchors.right: parent.right;
anchors.rightMargin: Utils.dp(Screen.pixelDensity, 5) anchors.rightMargin: Utils.dp(Screen.pixelDensity, 5)
onClicked: { onClicked: {
state = ++state % 4; state = ++state % 5;
syncEngine.setRepeat(state) syncEngine.setRepeat(state)
} }
} }

View File

@ -33,7 +33,8 @@ SOURCES += main.cpp \
playlistmodel.cpp \ playlistmodel.cpp \
serverlistmodel.cpp \ serverlistmodel.cpp \
playlistsmodel.cpp \ playlistsmodel.cpp \
currentplaylistmodel.cpp currentplaylistmodel.cpp \
../sync/playlist.cpp
RESOURCES += qml.qrc RESOURCES += qml.qrc
@ -69,5 +70,6 @@ HEADERS += \
playlistmodel.h \ playlistmodel.h \
serverlistmodel.h \ serverlistmodel.h \
playlistsmodel.h \ playlistsmodel.h \
currentplaylistmodel.h currentplaylistmodel.h \
../sync/playlist.h

View File

@ -17,7 +17,7 @@ class CurrentPlayListModel : public QAbstractListModel
private: private:
SyncEngine * syncEngine; SyncEngine * syncEngine;
const QList<syncLib::SongHeader> *playList; const QList<syncLib::SongStorage> *playList;
int itemCount; int itemCount;
private slots: private slots:

View File

@ -94,7 +94,7 @@ QVariant PlayListModel::data(const QModelIndex &index, int role) const
bool PlayListModel::select(int id){ bool PlayListModel::select(int id){
for(QList<syncLib::SongHeader>::Iterator i = playList.begin(); i < playList.end(); i++){ for(QList<syncLib::SongStorage>::Iterator i = playList.begin(); i < playList.end(); i++){
if(i->id == id){ if(i->id == id){
if((i->isSelected = !i->isSelected)){ if((i->isSelected = !i->isSelected)){
@ -114,7 +114,7 @@ bool PlayListModel::select(int id){
QList<int> PlayListModel::getSelected(){ QList<int> PlayListModel::getSelected(){
QList<int> result; QList<int> result;
for(QList<syncLib::SongHeader>::Iterator i = playList.begin(); i < playList.end(); i++){ for(QList<syncLib::SongStorage>::Iterator i = playList.begin(); i < playList.end(); i++){
if(i->isSelected){ if(i->isSelected){
result.push_back(i->id); result.push_back(i->id);
} }
@ -124,7 +124,7 @@ QList<int> PlayListModel::getSelected(){
bool PlayListModel::isSelected(int id){ bool PlayListModel::isSelected(int id){
for(QList<syncLib::SongHeader>::Iterator i = playList.begin(); i < playList.end(); i++){ for(QList<syncLib::SongStorage>::Iterator i = playList.begin(); i < playList.end(); i++){
if(i->id == id){ if(i->id == id){
return i->isSelected; return i->isSelected;
} }

View File

@ -16,7 +16,7 @@ class PlayListModel : public QAbstractListModel
private: private:
SyncEngine * syncEngine; SyncEngine * syncEngine;
QList<syncLib::SongHeader> playList; QList<syncLib::SongStorage> playList;
QString playListName; QString playListName;
int itemCount; int itemCount;

View File

@ -8,9 +8,11 @@ SyncEngine::SyncEngine()
sqlApi = sync->getSqlApi(); sqlApi = sync->getSqlApi();
connect(sync, SIGNAL(networkStateChange()), this, SIGNAL(serversCountChanged())); connect(sync, SIGNAL(networkStateChange()), this, SIGNAL(serversCountChanged()));
connect(sync, SIGNAL(currentPlayListChanged()), this, SIGNAL(currentPlayListNameChanged()));
connect(sync, SIGNAL(currentPlayListChanged()), this, SIGNAL(currentPlayListCountChanged())); connect(sync, SIGNAL(currentPlayListChanged()), this, SIGNAL(currentPlayListCountChanged()));
connect(sync, SIGNAL(seekChanged(qint64)), this, SLOT(seekChanged(qint64))); connect(sync, SIGNAL(seekChanged(qint64)), this, SLOT(seekChanged(qint64)));
connect(sync, SIGNAL(currentSongChanged()), this, SIGNAL(currentSongChanged())); connect(sync, SIGNAL(currentSongChanged()), this, SIGNAL(currentSongChanged()));
connect(sync, SIGNAL(playStateChanged()), this, SIGNAL(playStateChanged()));
} }
@ -42,7 +44,7 @@ bool SyncEngine::init(){
return true; return true;
} }
const QList<syncLib::SongHeader>* SyncEngine::currentPlayList() const{ const QList<syncLib::SongStorage>* SyncEngine::currentPlayList() const{
return sync->getPlayList(); return sync->getPlayList();
} }
@ -140,7 +142,7 @@ int SyncEngine::repeat()const{
} }
void SyncEngine::setRepeat(int flag){ void SyncEngine::setRepeat(int flag){
sync->setRepeat((syncLib::Repeat)flag); sync->setRepeat((QMediaPlaylist::PlaybackMode)flag);
} }
bool SyncEngine::setPlayList(const QString& name){ bool SyncEngine::setPlayList(const QString& name){
@ -157,7 +159,7 @@ bool SyncEngine::setPlayList(const QString& name){
} }
bool SyncEngine::getPlayList(QList<syncLib::SongHeader> &playList, const QString &name){ bool SyncEngine::getPlayList(QList<SongStorage> &playList, const QString &name){
return sqlApi->updateAvailableSongs(playList, name, true); return sqlApi->updateAvailableSongs(playList, name, true);
} }
@ -251,6 +253,10 @@ bool SyncEngine::removeFromPlayList(int id, const QString &playList){
} }
int SyncEngine::playState()const{
return sync->playState();
}
SyncEngine::~SyncEngine(){ SyncEngine::~SyncEngine(){
disconnect(sync, SIGNAL(networkStateChange()), this, SIGNAL(serversCountChanged())); disconnect(sync, SIGNAL(networkStateChange()), this, SIGNAL(serversCountChanged()));
delete sync; delete sync;

View File

@ -16,6 +16,7 @@ class SyncEngine : public QObject
Q_PROPERTY(double pos READ pos WRITE setPos NOTIFY posChanged) Q_PROPERTY(double pos READ pos WRITE setPos NOTIFY posChanged)
Q_PROPERTY(int repeat READ repeat WRITE setRepeat NOTIFY repeatChanged) Q_PROPERTY(int repeat READ repeat WRITE setRepeat NOTIFY repeatChanged)
Q_PROPERTY(QString currentPlayListName READ currentPlayListName NOTIFY currentPlayListNameChanged) Q_PROPERTY(QString currentPlayListName READ currentPlayListName NOTIFY currentPlayListNameChanged)
Q_PROPERTY(int playState READ playState NOTIFY playStateChanged)
private: private:
@ -74,7 +75,7 @@ public slots:
* @brief currentPlayList * @brief currentPlayList
* @return return current Play List * @return return current Play List
*/ */
const QList<syncLib::SongHeader> *currentPlayList() const; const QList<SongStorage> *currentPlayList() const;
/** /**
* @brief currentPlayListName * @brief currentPlayListName
@ -138,7 +139,7 @@ public slots:
* @param name - name of selected playList * @param name - name of selected playList
* @return * @return
*/ */
bool getPlayList(QList<syncLib::SongHeader> &playList, const QString& name); bool getPlayList(QList<SongStorage> &playList, const QString& name);
/** /**
* @brief lastError - message of last error * @brief lastError - message of last error
@ -222,6 +223,17 @@ public slots:
*/ */
bool removeFromPlayList(int id, const QString& playList); bool removeFromPlayList(int id, const QString& playList);
/**
* @brief playState
* @return playState current song ( )
* @value 0 is no repeat
* @value 1 is single repeat
* @value 2 is single playlist
* @value 3 is repeat playlist
* @value 4 is random play
*/
int playState()const;
signals: signals:
/** /**
@ -278,6 +290,12 @@ signals:
*/ */
void currentPlayListNameChanged(); void currentPlayListNameChanged();
/**
* @brief playStateChanged
* emited when state of playing song changed
*/
void playStateChanged();
}; };
#endif // SYNCENGINE_H #endif // SYNCENGINE_H

View File

@ -3,6 +3,9 @@
// general otions // general otions
#define CURRENT_PLAYLIST_KEY "currentPlayList" #define CURRENT_PLAYLIST_KEY "currentPlayList"
#define MAIN_FOLDER_KEY "songsfolder"
#define MAIN_FOLDER "soundBand"
// LIB VERSION // LIB VERSION
#define MAJOR_VERSION 0 #define MAJOR_VERSION 0

View File

@ -2,6 +2,8 @@
#include <QSqlQuery> #include <QSqlQuery>
#include <QtSql> #include <QtSql>
#include "exaptions.h" #include "exaptions.h"
#include <QSettings>
#include "playlist.h"
namespace syncLib{ namespace syncLib{
@ -44,6 +46,8 @@ bool MySql::exec(QSqlQuery *sq,const QString& sqlFile){
void MySql::initDB(const QString &database){ void MySql::initDB(const QString &database){
if(db) return; if(db) return;
dataBaseName = database; dataBaseName = database;
QSettings settings;
songDir = settings.value(MAIN_FOLDER_KEY, QDir::homePath() + "/soundBand").toString();
db = new QSqlDatabase(); db = new QSqlDatabase();
*db = QSqlDatabase::addDatabase("QSQLITE", database); *db = QSqlDatabase::addDatabase("QSQLITE", database);
QDir d(QString("./%0").arg(dataBaseName)); QDir d(QString("./%0").arg(dataBaseName));
@ -51,23 +55,11 @@ void MySql::initDB(const QString &database){
if(db->open()){ if(db->open()){
qyery = new QSqlQuery(*db); qyery = new QSqlQuery(*db);
// /*
// *https://stackoverflow.com/questions/40863216/sqlite-why-is-foreign-key-constraint-not-working-here
// */
// QString qyer = QString("PRAGMA foreign_keys = ON");
// if(!qyery->exec(qyer)){
// sqlErrorLog(qyer);
// throw InitDBError();
// delete db;
// return;
// }
QString qyer = QString("CREATE TABLE IF NOT EXISTS songs(" QString qyer = QString("CREATE TABLE IF NOT EXISTS songs("
"id INTEGER PRIMARY KEY AUTOINCREMENT, " "id INTEGER PRIMARY KEY AUTOINCREMENT, "
"name VARCHAR(100), " "name VARCHAR(100), "
"size INT NOT NULL, " "size INT NOT NULL, "
"data BLOB NOT NULL " "data TEXT NOT NULL "
")"); ")");
if(!qyery->exec(qyer)){ if(!qyery->exec(qyer)){
sqlErrorLog(qyer); sqlErrorLog(qyer);
@ -127,15 +119,95 @@ void MySql::initDB(const QString &database){
} }
} }
bool MySql::find(const QMediaContent &song, SongStorage &response){
QList<SongStorage> songs;
if(!updateAvailableSongs(songs)){
return false;
}
for(SongStorage &i: songs){
if(i == song){
response = i;
return true;
}
}
return false;
}
bool MySql::find(const QMediaContent &song, SongHeader &response){
QList<SongStorage> songs;
if(!updateAvailableSongs(songs)){
return false;
}
for(SongStorage &i: songs){
if(i == song){
response = (SongHeader&)i;
return true;
}
}
return false;
}
bool MySql::find(const SongHeader &song, QMediaContent &response){
QList<SongStorage> songs;
if(!updateAvailableSongs(songs)){
return false;
}
for(SongStorage &i: songs){
if((SongHeader&)i == song){
response = i.toMedia();
return true;
}
}
return false;
}
void MySql::setSoundDir(const QString &str){
songDir = str;
QSettings().setValue(MAIN_FOLDER_KEY, songDir);
}
bool MySql::saveToStorage(QUrl &url, const Song &song) const{
if(!song.isValid()){
return false;
}
QFile file(songDir + "/" + song.name);
if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate)){
return false;
}
file.write(song.source.data(), song.source.length());
file.close();
url = QUrl::fromLocalFile(songDir + "/" + song.name);
return url.isValid();
}
void MySql::sqlErrorLog(const QString &qyery)const{ void MySql::sqlErrorLog(const QString &qyery)const{
#ifdef QT_DEBUG #ifdef QT_DEBUG
qDebug()<< qyery << ": fail:\n " <<this->qyery->lastError(); qDebug()<< qyery << ": fail:\n " <<this->qyery->lastError();
#endif #endif
} }
int MySql::save(const Song &song){ int MySql::save(const SongStorage &song){
QString qyer = QString("SELECT id from songs where name='%0' and size=%1").arg(song.name,
QString::number(song.size)); QString qyer = QString("SELECT id from songs where name='%0' and size=%1").
arg(song.name, QString::number(song.size));
if(!qyery->exec(qyer)){ if(!qyery->exec(qyer)){
sqlErrorLog(qyer); sqlErrorLog(qyer);
return -1; return -1;
@ -146,15 +218,11 @@ int MySql::save(const Song &song){
} }
qyer = QString("INSERT INTO songs (name,size,data) VALUES " qyer = QString("INSERT INTO songs (name,size,data) VALUES "
"('%0',%1,:val)").arg(song.name, "('%0',%1,'%2')").arg(song.name,
QString::number(song.size)); QString::number(song.size),
if(!qyery->prepare(qyer)){ song.url.path());
sqlErrorLog(qyer + " prepare error");
return -1;
}
qyery->bindValue(":val",song.source); if(!qyery->exec(qyer)){
if(!qyery->exec()){
sqlErrorLog(qyer); sqlErrorLog(qyer);
return -1; return -1;
} }
@ -169,18 +237,28 @@ int MySql::save(const Song &song){
return result; return result;
} }
int MySql::save(const Song &song){
QUrl url;
if(!saveToStorage(url, song)){
return false;
}
return save(SongStorage(url));
}
int MySql::save(const QString &url){ int MySql::save(const QString &url){
QFile f(QUrl(url).toLocalFile());
if(!f.open(QIODevice::ReadOnly)){ QUrl qurl = QUrl(url);
if(!qurl.isValid()){
return -1; return -1;
} }
QByteArray bytes = f.readAll();
f.close(); SongStorage song(qurl);
QString name = url.right(url.lastIndexOf(QRegularExpression("[\\/]"))); // meby [[\\\/]]
Song song;
song.name = name;
song.size = bytes.size();
song.source = bytes;
if(!song.isNameValid()){ if(!song.isNameValid()){
return -1; return -1;
@ -190,8 +268,7 @@ int MySql::save(const QString &url){
} }
bool MySql::load(const SongHeader &song,Song &result){ bool MySql::load(const SongHeader &song, SongStorage &result){
result.clear();
if(song.id > -1){ if(song.id > -1){
QString qyer = QString("SELECT * from songs where id=%0").arg(song.id); QString qyer = QString("SELECT * from songs where id=%0").arg(song.id);
if(!qyery->exec(qyer)){ if(!qyery->exec(qyer)){
@ -213,17 +290,17 @@ bool MySql::load(const SongHeader &song,Song &result){
result.id = qyery->value(0).toInt(); result.id = qyery->value(0).toInt();
result.name = qyery->value(1).toString(); result.name = qyery->value(1).toString();
result.size = qyery->value(2).toInt(); result.size = qyery->value(2).toInt();
result.source = qyery->value(3).toByteArray(); result.url = QUrl::fromLocalFile(qyery->value(3).toString());
return true; return result.isValid();
} }
bool MySql::updateAvailableSongs(QList<SongHeader>& list, const QString& playList, bool forEditing){ bool MySql::updateAvailableSongs(QList<SongStorage>& list, const QString& playList, bool forEditing){
QString qyer; QString qyer;
if(playList.isEmpty() || playList == ALL_SONGS_LIST || forEditing){ if(playList.isEmpty() || playList == ALL_SONGS_LIST || forEditing){
qyer = QString("SELECT id,name,size from songs"); qyer = QString("SELECT * from songs");
}else{ }else{
qyer = QString("SELECT id,name,size from songs where " qyer = QString("SELECT * from songs where "
"id in (select song from playlistsdata where " "id in (select song from playlistsdata where "
"playlist='%0')").arg(playList); "playlist='%0')").arg(playList);
} }
@ -236,11 +313,13 @@ bool MySql::updateAvailableSongs(QList<SongHeader>& list, const QString& playLis
list.clear(); list.clear();
while(qyery->next()){ while(qyery->next()){
SongHeader song; SongStorage song;
song.isSelected = !forEditing || playList == ALL_SONGS_LIST; song.isSelected = !forEditing || playList == ALL_SONGS_LIST;
song.id = qyery->value(0).toInt(); song.id = qyery->value(0).toInt();
song.name = qyery->value(1).toString(); song.name = qyery->value(1).toString();
song.size = qyery->value(2).toInt(); song.size = qyery->value(2).toInt();
song.url = QUrl::fromLocalFile(qyery->value(3).toString());
list.push_back(song); list.push_back(song);
} }
@ -255,7 +334,7 @@ bool MySql::updateAvailableSongs(QList<SongHeader>& list, const QString& playLis
} }
while(qyery->next()){ while(qyery->next()){
for(SongHeader& item:list){ for(SongStorage& item:list){
int id = qyery->value(0).toInt(); int id = qyery->value(0).toInt();
if(item.id == id){ if(item.id == id){
item.isSelected = true; item.isSelected = true;
@ -265,19 +344,21 @@ bool MySql::updateAvailableSongs(QList<SongHeader>& list, const QString& playLis
} }
} }
return true; return true;
} }
bool MySql::updateAvailableSongs(QStringList& list, const QString& playList){ bool MySql::updateAvailableSongs(PlayList& list, const QString& playList, bool forEditing){
QList<SongHeader> tempList; QList<SongStorage> tempList;
list.clear();
if(!updateAvailableSongs(tempList, playList)) if(!updateAvailableSongs(tempList, playList, forEditing))
return false; return false;
for(SongHeader &header : tempList){ for(SongStorage &header : tempList){
list.push_back(header.name); if(!list.addMedia(header)){
this->removeSong(header);
}
} }
return true; return true;

View File

@ -1,7 +1,7 @@
#ifndef MYSQL_H #ifndef MYSQL_H
#define MYSQL_H #define MYSQL_H
#include <QString> #include <QString>
#include "song.h" #include "playlist.h"
class QSqlDatabase; class QSqlDatabase;
class QSqlQuery; class QSqlQuery;
@ -14,6 +14,7 @@ private:
QSqlDatabase *db; QSqlDatabase *db;
QSqlQuery *qyery; QSqlQuery *qyery;
QString dataBaseName; QString dataBaseName;
QString songDir;
/** /**
* @brief sqlErrorLog show sql error * @brief sqlErrorLog show sql error
@ -21,6 +22,14 @@ private:
*/ */
void sqlErrorLog(const QString& qyery) const; void sqlErrorLog(const QString& qyery) const;
/**
* @brief saveToStorage save song as file into hdd
* @param url - url of song after save
* @param song - saved song
* @return true if all done
*/
bool saveToStorage(QUrl& url, const Song& song)const;
public: public:
MySql(const QString& databasename); MySql(const QString& databasename);
/** /**
@ -28,13 +37,56 @@ public:
*/ */
void initDB(const QString& database = DATABASE_NAME ); void initDB(const QString& database = DATABASE_NAME );
/**
* @brief find - find song
* @param song - song header
* @param response Media Content of finded song
* @return true if song finded
*/
bool find(const SongHeader& song, QMediaContent& response);
/**
* @brief find - find song
* @param song - song header
* @param response Media Content of finded song
* @return true if song finded
*/
bool find(const QMediaContent& song, SongHeader& response);
/**
* @brief find - find song
* @param song - media Content
* @param response header of finded song
* @return true if song finded
*/
bool find(const QMediaContent& song, SongStorage &response);
/**
* @brief setSoundDir
* @param str
*/
void setSoundDir(const QString& str);
/**
* @brief findSong
* @param song
*/
bool findSong(const SongHeader &song);
/** /**
* @brief load song of database; * @brief load song of database;
* @brief song - * @brief song -
* @brief result - the resulting value; * @brief result - the resulting value;
* @return true if everything's done * @return true if everything's done
*/ */
bool load(const SongHeader &song, Song &result); bool load(const SongHeader &song, SongStorage &result);
/**
* @brief save media data into local database.
* @param song savining media data.
* @return id of song saved on local database.
*/
int save(const SongStorage &song);
/** /**
* @brief save media data into local database. * @brief save media data into local database.
@ -62,7 +114,7 @@ public:
* @param forEdit - flag for editing play list. If this flag = true then return all available songs with corect flag 'isSelect' * @param forEdit - flag for editing play list. If this flag = true then return all available songs with corect flag 'isSelect'
* @return true if all done * @return true if all done
*/ */
bool updateAvailableSongs(QList<SongHeader>& list, const QString &playList = "", bool forEditing = false); bool updateAvailableSongs(QList<SongStorage>& list, const QString &playList = "", bool forEditing = false);
/** /**
* @brief updateAvelableSongs will update the list of participants of songs. * @brief updateAvelableSongs will update the list of participants of songs.
@ -70,7 +122,7 @@ public:
* @param playList - play list of songs (string). * @param playList - play list of songs (string).
* @return true if all done * @return true if all done
*/ */
bool updateAvailableSongs(QStringList& list, const QString &playList = ""); bool updateAvailableSongs(PlayList& list, const QString &playList = "", bool forEditing = false);
/** /**
* @brief removeSong - remove song from local database. * @brief removeSong - remove song from local database.

View File

@ -12,24 +12,6 @@ Player::Player(const QString &bufferFile, QObject *parent, Flags flags):
bufferVolume = 0; bufferVolume = 0;
} }
bool Player::setMediaFromBytes(const QByteArray &array){
QFile f(buffer);
if(!f.open(QIODevice::WriteOnly | QIODevice::Truncate)){
return false;
}
if(array.length() != f.write(array.data(),array.length())){
f.close();
return false;
}
f.close();
setMedia(QUrl::fromLocalFile(QDir("./").absoluteFilePath(buffer)));
return true;
}
void Player::syncBegin(){ void Player::syncBegin(){
bufferVolume = volume(); bufferVolume = volume();

View File

@ -21,13 +21,6 @@ public:
Player(const QString& bufferFile, QObject *parent = Q_NULLPTR, Flags flags = Flags()); Player(const QString& bufferFile, QObject *parent = Q_NULLPTR, Flags flags = Flags());
/**
* @brief setMediaFromBytes
* @param array of song data
* @return true if all done
*/
bool setMediaFromBytes(const QByteArray& array);
/** /**
* @brief syncBegin - palyer waiting for sunced. * @brief syncBegin - palyer waiting for sunced.
*/ */

93
sync/playlist.cpp Normal file
View File

@ -0,0 +1,93 @@
#include "playlist.h"
PlayList::PlayList()
{
playListInfo.clear();
playList = new QMediaPlaylist();
}
QList<SongStorage>* PlayList::getInfo(){
return &playListInfo;
}
QMediaPlaylist* PlayList::getList(){
return playList;
}
void PlayList::clear(){
playList->clear();
playListInfo.clear();
}
bool PlayList::addMedia(const SongStorage &song){
if(!song.isValid())
return false;
if(!playList->addMedia(song.toMedia()))
return false;
playListInfo.push_back(song);
return true;
}
void PlayList::next()const{
playList->next();
}
void PlayList::prev()const{
playList->previous();
}
bool PlayList::isValid()const{
return playListInfo.size() == playList->mediaCount();
}
bool PlayList::isEmpty()const{
return playList->isEmpty() && playListInfo.isEmpty();
}
const SongHeader *PlayList::currentHeader()const{
if(playList->isEmpty() || playList->currentIndex() < 0)
return nullptr;
return static_cast<const SongHeader*>(&playListInfo[playList->currentIndex()]);
}
const SongStorage *PlayList::currentSong()const{
if(playList->isEmpty() || playList->currentIndex() < 0)
return nullptr;
return &playListInfo[playList->currentIndex()];
}
int PlayList::size()const{
return playList->mediaCount();
}
int PlayList::find(const SongHeader &header)const{
for(int i = 0; i < playList->mediaCount(); ++i) {
if(header == playList->media(i)) {
return i;
}
}
return -1;
}
bool PlayList::selectSong(int index){
if(playList->mediaCount() <= index || index < 0){
return false;
}
playList->setCurrentIndex(index);
return true;
}
bool PlayList::selectSong(const SongHeader &header){
return selectSong(find(header));
}
PlayList::~PlayList(){
delete playList;
}

113
sync/playlist.h Normal file
View File

@ -0,0 +1,113 @@
#ifndef PLAYLIST_H
#define PLAYLIST_H
#include <QMediaPlaylist>
#include "song.h"
using namespace syncLib;
/**
* @brief The PlayList class
* palyList with songs info
*/
class PlayList
{
private:
/**
* @brief playList media play list
*/
QMediaPlaylist *playList;
/**
* @brief playListInfo - info from databas
*/
QList<SongStorage> playListInfo;
public:
PlayList();
/**
* @brief getInfo
* @return info from database about songs
*/
QList<SongStorage>* getInfo();
/**
* @brief clear this play list
*/
void clear();
/**
* @brief addMedia media content into playList
* @param song - media content
* @return true if all done
*/
bool addMedia(const SongStorage& song);
/**
* @brief next
*/
void next()const;
/**
* @brief prev
*/
void prev()const;
/**
* @brief isValid
* @return true if this object valid
*/
bool isValid()const;
/**
* @brief isEmpty
* @return true if playlist empty
*/
bool isEmpty()const;
/**
* @brief getlist
* @return return pointer to media content
*/
QMediaPlaylist* getList();
/**
* @brief currentHeader
* @return header of curent song
*/
const SongHeader* currentHeader()const;
/**
* @brief currentStorage
* @return header of curent song
*/
const SongStorage* currentSong()const;
/**
* @brief size
* @return count of media items of playlist
*/
int size()const;
/**
* @brief find
* @return index of finded media
*/
int find(const SongHeader& header)const;
/**
* @brief selectSong
* @return true if song selected
*/
bool selectSong(int index);
/**
* @brief selectSong
* @param header of selecting song
* @return if song selected return true;
*/
bool selectSong(const SongHeader& header);
~PlayList();
};
#endif // PLAYLIST_H

View File

@ -1,5 +1,8 @@
#include "song.h" #include "song.h"
#include <QStringList> #include <QStringList>
#include <QRegularExpression>
#include <QFile>
namespace syncLib{ namespace syncLib{
static const QStringList ValidSongs = {".mp3", ".wav", ".ogg"}; static const QStringList ValidSongs = {".mp3", ".wav", ".ogg"};
@ -11,6 +14,31 @@ SongHeader::SongHeader()
this->size = 0; this->size = 0;
} }
bool SongHeader::getName(QString & name, const QUrl &url) const {
if(url.isLocalFile() && url.isValid()){
name = url.fileName();
name = name.right(name.lastIndexOf(QRegularExpression("[\\\/]")));
return true;
}
return false;
}
bool SongHeader::getSize(int & size, const QUrl &url) const {
if(url.isLocalFile() && url.isValid()){
QFile f(url.toLocalFile());
if(!f.exists()){
return false;
}
size = f.size();
return true;
}
return false;
}
SongHeader& SongHeader::operator =(const SongHeader& right){ SongHeader& SongHeader::operator =(const SongHeader& right){
this->id = right.id; this->id = right.id;
this->name = right.name; this->name = right.name;
@ -18,15 +46,35 @@ SongHeader& SongHeader::operator =(const SongHeader& right){
return *this; return *this;
} }
bool SongHeader::operator ==(const SongHeader& right){ SongHeader& SongHeader::operator =(const QMediaContent& right){
this->id = -1;
if(!getName(name, right.canonicalUrl())){
name.clear();
}
if(!getSize(this->size, right.canonicalUrl())){
this->size = 0;
}
return *this;
}
bool SongHeader::operator ==(const SongHeader& right)const{
return this->name == right.name && this->size == right.size; return this->name == right.name && this->size == right.size;
} }
unsigned int SongHeader::getSize() const{ bool SongHeader::operator ==(const QMediaContent& right)const{
QByteArray size; QString name;
QDataStream stream(size); if(!getName(name, right.canonicalUrl())){
stream << id << name << this->size; return false;
return size.size(); }
int size;;
if(!getSize(size, right.canonicalUrl())){
return false;
}
return this->name == name && this->size == size;
} }
bool SongHeader::isNameValid() const{ bool SongHeader::isNameValid() const{
@ -45,14 +93,6 @@ bool SongHeader::isValid() const{
SongHeader::~SongHeader(){} SongHeader::~SongHeader(){}
Song::Song():
SongHeader()
{
source.clear();
}
QDataStream& operator << (QDataStream& stream, const SongHeader& song){ QDataStream& operator << (QDataStream& stream, const SongHeader& song){
stream << song.id; stream << song.id;
stream << song.name; stream << song.name;
@ -66,6 +106,93 @@ QDataStream& operator >> (QDataStream& stream, SongHeader& song){
return stream; return stream;
} }
SongStorage::SongStorage():
SongHeader()
{
url.clear();
}
SongStorage::SongStorage(const SongHeader& from)
:SongStorage::SongStorage()
{
this->id = from.id;
this->name = from.name;
this->size = from.size;
}
SongStorage::SongStorage(const QUrl& from)
:SongStorage::SongStorage()
{
if(!from.isValid() || !from.isLocalFile()){
return;
}
QFile f(from.toLocalFile());
this->size = f.size();
f.close();
this->id = -1;
url = from;
if(!getName(name, from)){
name.clear();
}
}
SongStorage::SongStorage(const QMediaContent& from)
:SongStorage::SongStorage(from.canonicalUrl())
{
}
const QUrl& SongStorage::getSource()const{
return url;
}
bool SongStorage::isValid() const{
return SongHeader::isValid() && url.isValid() && QFile(url.toLocalFile()).exists();
}
SongStorage::~SongStorage(){
url.clear();
}
QMediaContent SongStorage::toMedia()const{
return QMediaContent(url);
}
bool SongStorage::toSong(Song&)const{
Song song(*((SongHeader*)this));
QFile f(url.toLocalFile());
if(!f.open(QIODevice::ReadOnly))
return false;
song.source = f.readAll();
f.close();
return song.isValid();
}
QDataStream& operator << (QDataStream& stream,const SongStorage& song){
stream << static_cast<const SongHeader&>(song);
stream << song.url;
return stream;
}
QDataStream& operator >> (QDataStream& stream, SongStorage& song){
stream >> static_cast<SongHeader&>(song);
stream >> song.url;
return stream;
}
Song::Song():
SongHeader()
{
source.clear();
}
Song::Song(const SongHeader& from) Song::Song(const SongHeader& from)
:Song::Song() :Song::Song()
{ {
@ -78,10 +205,6 @@ void Song::clear(){
source.clear(); source.clear();
} }
unsigned int Song::getSize() const{
return SongHeader::getSize() + source.size();
}
const QByteArray& Song::getSource()const{ const QByteArray& Song::getSource()const{
return source; return source;
} }

View File

@ -4,6 +4,7 @@
#include <QByteArray> #include <QByteArray>
#include <QDataStream> #include <QDataStream>
#include "chronotime.h" #include "chronotime.h"
#include <QMediaContent>
namespace syncLib { namespace syncLib {
@ -24,7 +25,9 @@ struct Syncer
* (id,size and name) * (id,size and name)
*/ */
class SongHeader{ class SongHeader{
protected:
bool getName(QString &name, const QUrl& url)const;
bool getSize(int &size, const QUrl& url)const;
public: public:
bool isSelected; bool isSelected;
int id; int id;
@ -32,8 +35,10 @@ public:
int size; int size;
SongHeader(); SongHeader();
SongHeader& operator = (const SongHeader& right); SongHeader& operator = (const SongHeader& right);
bool operator == (const SongHeader& right); SongHeader& operator = (const QMediaContent& right);
virtual unsigned int getSize() const;
bool operator == (const SongHeader& right)const;
bool operator == (const QMediaContent& right)const;
bool isNameValid() const; bool isNameValid() const;
virtual bool isValid() const; virtual bool isValid() const;
virtual ~SongHeader(); virtual ~SongHeader();
@ -61,12 +66,36 @@ public:
Song(const SongHeader& from); Song(const SongHeader& from);
void clear(); void clear();
const QByteArray& getSource()const; const QByteArray& getSource()const;
unsigned int getSize() const;
bool isValid() const; bool isValid() const;
~Song(); ~Song();
friend QDataStream& operator << (QDataStream& stream, const Song& song); friend QDataStream& operator << (QDataStream& stream, const Song& song);
friend QDataStream& operator >> (QDataStream& stream, Song& song); friend QDataStream& operator >> (QDataStream& stream, Song& song);
friend class MySql; friend class MySql;
friend class SongStorage;
}; };
/**
* @brief The SongStorage class
* header with url to song source
*/
class SongStorage : public SongHeader {
private:
QUrl url;
public:
SongStorage();
SongStorage(const SongHeader& from);
SongStorage(const QUrl& url);
SongStorage(const QMediaContent& media);
const QUrl& getSource()const;
bool toSong(Song &)const;
QMediaContent toMedia()const;
bool isValid() const;
~SongStorage();
friend QDataStream& operator << (QDataStream& stream, const SongStorage& song);
friend QDataStream& operator >> (QDataStream& stream, SongStorage& song);
friend class MySql;
};
} }
#endif // SONG_H #endif // SONG_H

View File

@ -22,11 +22,14 @@ Sync::Sync(const QString &address, int port, const QString &datadir):
if(!player->isAvailable()){ if(!player->isAvailable()){
throw MediaException(); throw MediaException();
} }
playList = new PlayList;
player->setPlaylist(playList->getList());
fbroadcaster = false; fbroadcaster = false;
resyncCount = 0; resyncCount = 0;
lastSyncTime = 0; lastSyncTime = 0;
currentSongIndex = 0;
ping = 0; ping = 0;
sql = new MySql(datadir); sql = new MySql(datadir);
@ -41,7 +44,15 @@ MySql* Sync::getSqlApi(){
return sql; return sql;
} }
bool Sync::updateSongs(QList<SongHeader>& list, const QString& playList){ bool Sync::setSingle(const QMediaContent& media){
playList->clear();
playList->addMedia(media);
emit currentPlayListChanged();
return true;
}
bool Sync::updateSongs(PlayList& list, const QString& playList){
if(!sql->updateAvailableSongs(list, playList)){ if(!sql->updateAvailableSongs(list, playList)){
return false; return false;
} }
@ -59,28 +70,54 @@ const QString& Sync::getPlayListName() const{
return lastUsedPlayList; return lastUsedPlayList;
} }
bool Sync::findHeader(const Song &song){ bool Sync::play(bool fbroadcast){
fbroadcaster = fbroadcast;
if(fbroadcaster){
player->play();
sync();
}else{
player->syncBegin();
}
emit currentSongChanged();
for(int i = 0; i < playList.size(); i++){
if(playList[i] == static_cast<SongHeader>(song)){
currentSongIndex = i;
return true; return true;
} }
bool Sync::play(const SongStorage &song, bool fbroadcast){
if(!song.isValid()){
return false;
} }
return false; if(playList->selectSong(song)){
return play(fbroadcast);
}
playList->clear();
playList->addMedia(song);
return play(fbroadcast);
} }
bool Sync::play(const SongHeader &header, bool fbroadcast){ bool Sync::play(const SongHeader &header, bool fbroadcast){
Song song; if(!header.isValid()){
return false;
}
if(playList->selectSong(header)){
return play(fbroadcast);
}
SongStorage song;
SongHeader newheader = header; SongHeader newheader = header;
newheader.id = -1; newheader.id = -1;
if(!sql->load(newheader, song)){ if(!sql->load(newheader, song)){
return false; return false;
} }
return Sync::play(song, fbroadcast); return play(song, fbroadcast);
} }
bool Sync::play(const Song &song, bool fbroadcast){ bool Sync::play(const Song &song, bool fbroadcast){
@ -89,27 +126,27 @@ bool Sync::play(const Song &song, bool fbroadcast){
return false; return false;
} }
if(!player->setMediaFromBytes(song.getSource())){ QMediaContent savedSong;
return false; if(!sql->find(static_cast<const SongHeader&>(song), savedSong) && sql->save(song) > -1 &&
} !sql->find((SongHeader&)song, savedSong)){
fbroadcaster = fbroadcast;
if(!findHeader(song) && sql->save(song) > -1 &&
updateSongs(playList) && !findHeader(song)){
return false; return false;
} }
if(fbroadcaster){ return play(savedSong, fbroadcast);
player->play();
sync();
}else{
player->syncBegin();
} }
emit currentSongChanged(); bool Sync::play(const QMediaContent& media, bool fbroadcast){
return true;
if(media.isNull()){
return false;
}
if(!setSingle(media)){
return false;
}
return Sync::play(fbroadcast);
} }
bool Sync::play(int id_song, bool fbroadcast){ bool Sync::play(int id_song, bool fbroadcast){
@ -119,9 +156,12 @@ bool Sync::play(int id_song, bool fbroadcast){
} }
SongHeader header; SongHeader header;
SongStorage song;
header.id = id_song; header.id = id_song;
Song song;
sql->load(header, song); if(!sql->load(header, song)){
return false;
}
return Sync::play(song, fbroadcast); return Sync::play(song, fbroadcast);
} }
@ -135,22 +175,21 @@ bool Sync::play(QString url){
return Sync::play(id); return Sync::play(id);
} }
Repeat Sync::repeat()const{ QMediaPlaylist::PlaybackMode Sync::repeat()const{
return _repeat; return playList->getList()->playbackMode();
} }
void Sync::setRepeat(Repeat flag){ void Sync::setRepeat(QMediaPlaylist::PlaybackMode flag){
_repeat = flag; playList->getList()->setPlaybackMode(flag);
} }
bool Sync::pause(bool state){ bool Sync::pause(bool state){
if(!fbroadcaster){ if(player->state() == QMediaPlayer::StoppedState){
if(playList->isEmpty())
if(playList.isEmpty())
return false; return false;
return play(playList[0]); return play();
} }
if(state){ if(state){
@ -189,6 +228,9 @@ bool Sync::sync(const Syncer &sync, milliseconds ping){
} }
/**
* @todo thi nead send a hedaer
*/
void Sync::sync(){ void Sync::sync(){
if(fbroadcaster) if(fbroadcaster)
@ -196,7 +238,7 @@ void Sync::sync(){
package pac; package pac;
if(!createPackage(t_sync, pac)){ if(!createPackage(t_sync, pac)){
throw CreatePackageExaption(); CreatePackageExaption();
return; return;
} }
node->WriteAll(pac.parseTo()); node->WriteAll(pac.parseTo());
@ -252,18 +294,17 @@ bool Sync::createPackage(Type type, package &pac){
} }
if(type & TypePackage::t_song_h && fbroadcaster){ if(type & TypePackage::t_song_h && fbroadcaster){
if(currentSongIndex < 0) if(playList->getList()->currentIndex() < 0)
return false; return false;
pac.header = playList[currentSongIndex]; pac.header = *playList->currentHeader();
} }
if(type & TypePackage::t_song && fbroadcaster){ if(type & TypePackage::t_song && fbroadcaster){
if(currentSongIndex < 0) if(playList->getList()->currentIndex() < 0)
return false; return false;
if(!sql->load(playList[currentSongIndex], pac.source)) if(!playList->currentSong()->toSong(pac.source))
return false; return false;
} }
@ -387,7 +428,7 @@ void Sync::packageRender(ETcpSocket *socket){
// if requst from client // if requst from client
if(pkg.getType() & t_play & t_sync){ if(pkg.getType() & t_play & t_sync){
if(currentSongIndex < 0){ if(playList->getList()->currentIndex() < 0){
throw SyncError(); throw SyncError();
socket->nextItem(); socket->nextItem();
continue; continue;
@ -443,32 +484,27 @@ void Sync::deepScaned(QList<ETcpSocket *> * list){
} }
void Sync::endPlay(QMediaPlayer::State state){ void Sync::endPlay(QMediaPlayer::State state){
if(state == QMediaPlayer::StoppedState){
switch (_repeat) { switch (state) {
case allPlayListRandom: case QMediaPlayer::StoppedState:
next(true);
break;
case allPlayList:
next(false);
break;
case oneMusic:
play(playList[currentSongIndex]);
break;
default:
currentSongIndex = -1;
fbroadcaster = false; fbroadcaster = false;
break; break;
case QMediaPlayer::PlayingState:
sync();
break;
case QMediaPlayer::PausedState:
break;
default:
break;
} }
} emit playStateChanged();
} }
QString Sync::getVersion(){ QString Sync::getVersion(){
return QString(tr("Version") + "%0.%1.%2").arg(MAJOR_VERSION).arg(MINOR_VERSION).arg(REVISION_VERSION); return QString(tr("Version") + "%0.%1.%2").
arg(MAJOR_VERSION).arg(MINOR_VERSION).arg(REVISION_VERSION);
} }
bool Sync::setValume(unsigned int valume){ bool Sync::setValume(unsigned int valume){
@ -488,19 +524,16 @@ unsigned int Sync::seek() const{
return player->position(); return player->position();
} }
const QList<SongHeader>* Sync::getPlayList() const{ const QList<SongStorage>* Sync::getPlayList() const{
return &playList; return playList->getInfo();
} }
int Sync::getCurrentSongIndex()const{ int Sync::getCurrentSongIndex()const{
return currentSongIndex; return playList->getList()->currentIndex();
} }
const SongHeader* Sync::getCurrentSong() const{ const SongStorage* Sync::getCurrentSong() const{
if(currentSongIndex < 0 || currentSongIndex >= playList.size()){ return playList->currentSong();
return nullptr;
}
return &playList[currentSongIndex];
} }
qint64 Sync::getEndPoint() const { qint64 Sync::getEndPoint() const {
@ -509,48 +542,53 @@ qint64 Sync::getEndPoint() const {
int Sync::addNewSong(const QString &url){ int Sync::addNewSong(const QString &url){
int result = sql->save(url); int result = sql->save(url);
updateSongs(playList); updateSongs(*playList);
return result; return result;
} }
bool Sync::updatePlayList(const QString &_playList){ bool Sync::updatePlayList(const QString &_playList){
if(!updateSongs(playList, _playList)){ if(!updateSongs(*playList, _playList)){
return false; return false;
} }
if(!playList.size()) if(!playList->size())
return false; return false;
if(fbroadcaster){ if(fbroadcaster){
play(playList.first()); play(fbroadcaster);
} }
return true; return true;
} }
bool Sync::next(bool random){ bool Sync::next(){
if(playList.isEmpty()) if(playList->isEmpty())
return false; return false;
currentSongIndex = (currentSongIndex + ((random)? rand() % 10000:1)) % playList.size(); playList->next();
return play(playList[currentSongIndex]); emit currentSongChanged();
return true;
} }
bool Sync::prev(){ bool Sync::prev(){
if(playList.isEmpty()) if(playList->isEmpty())
return false; return false;
--currentSongIndex; playList->prev();
if(currentSongIndex < 0) emit currentSongChanged();
currentSongIndex = playList.size() - 1; return true;
return play(playList[currentSongIndex]); }
QMediaPlayer::State Sync::playState()const{
return player->state();
} }
Sync::~Sync(){ Sync::~Sync(){
delete node; delete node;
delete player; delete player;
delete sql; delete sql;
delete playList;
servers.clear(); servers.clear();
} }

View File

@ -6,7 +6,9 @@
#include <chrono> #include <chrono>
#include "config.h" #include "config.h"
#include "mysql.h" #include "mysql.h"
#include <QMediaPlaylist>
#include "player.h" #include "player.h"
#include "playlist.h"
namespace syncLib { namespace syncLib {
@ -14,8 +16,6 @@ typedef std::chrono::time_point<std::chrono::high_resolution_clock> Clock;
class Node; class Node;
enum Repeat{noRepeat, oneMusic, allPlayList, allPlayListRandom};
/** /**
* @brief The Sync class is main class of this library. * @brief The Sync class is main class of this library.
@ -27,9 +27,8 @@ class Sync : public QObject
private: private:
Node *node; Node *node;
Player *player; Player *player;
QList<SongHeader> playList; PlayList *playList;
QString lastUsedPlayList; QString lastUsedPlayList;
int currentSongIndex;
QList<ETcpSocket*> servers; QList<ETcpSocket*> servers;
bool fbroadcaster; bool fbroadcaster;
int resyncCount; int resyncCount;
@ -38,13 +37,6 @@ private:
LocalScanner deepScaner; LocalScanner deepScaner;
MySql *sql; MySql *sql;
int port; int port;
Repeat _repeat;
/**
* @brief findHeader set current song if playList have playng song
* @return true if all done
*/
bool findHeader(const Song& song);
/** /**
* @brief rescan - search for existing servers * @brief rescan - search for existing servers
@ -62,11 +54,17 @@ private:
private slots: private slots:
/**
* @brief setSingle set singl or temp playlist
* @return true if all done
*/
bool setSingle(const QMediaContent& media);
/** /**
* @brief updateSongs use method update avelable songs from sql database * @brief updateSongs use method update avelable songs from sql database
* @return true if all done * @return true if all done
*/ */
bool updateSongs(QList<SongHeader> &list, const QString &playList = ""); bool updateSongs(PlayList &list, const QString &playList = "");
/** /**
* @brief packageRender - the handler of all messages received. * @brief packageRender - the handler of all messages received.
@ -96,13 +94,13 @@ public:
* @brief repeat * @brief repeat
* @return flag of repeat * @return flag of repeat
*/ */
Repeat repeat()const; QMediaPlaylist::PlaybackMode repeat()const;
/** /**
* @brief setRepeat * @brief setRepeat
* @param flag new flag of repeat * @param flag new flag of repeat
*/ */
void setRepeat(Repeat flag); void setRepeat(QMediaPlaylist::PlaybackMode flag);
/** /**
* @brief getSqlApi * @brief getSqlApi
@ -110,6 +108,21 @@ public:
*/ */
MySql* getSqlApi(); MySql* getSqlApi();
/**
* @brief Play song in this device, if device has not supported playning media data this method throw MediaExcrption.
* @param fbroadcast - server broadcasting sound.
* @return true if all done else false.
*/
bool play(bool fbroadcast = true);
/**
* @brief Play song in this device, if device has not supported playning media data this method throw MediaExcrption.
* @param header of song
* @param fbroadcast - server broadcasting sound.
* @return true if all done else false.
*/
bool play(const QMediaContent &media, bool fbroadcast = true);
/** /**
* @brief Play song in this device, if device has not supported playning media data this method throw MediaExcrption. * @brief Play song in this device, if device has not supported playning media data this method throw MediaExcrption.
* @param header of song * @param header of song
@ -117,6 +130,15 @@ public:
* @return true if all done else false. * @return true if all done else false.
*/ */
bool play(const SongHeader &header, bool fbroadcast = true); bool play(const SongHeader &header, bool fbroadcast = true);
/**
* @brief Play song in this device, if device has not supported playning media data this method throw MediaExcrption.
* @param header of song
* @param fbroadcast - server broadcasting sound.
* @return true if all done else false.
*/
bool play(const SongStorage &song, bool fbroadcast = true);
/** /**
* @brief Play song in this device, if device has not supported playning media data this method throw MediaExcrption. * @brief Play song in this device, if device has not supported playning media data this method throw MediaExcrption.
* @param song playning media data. * @param song playning media data.
@ -228,7 +250,7 @@ public:
* @brief getPlayList * @brief getPlayList
* @return list of available songs * @return list of available songs
*/ */
const QList<SongHeader> *getPlayList() const; const QList<SongStorage> *getPlayList() const;
/** /**
* @brief SongHeader::getCurrentSongIndex * @brief SongHeader::getCurrentSongIndex
@ -240,7 +262,7 @@ public:
* @brief getCurrentSong * @brief getCurrentSong
* @return playing song. * @return playing song.
*/ */
const SongHeader *getCurrentSong() const; const SongStorage *getCurrentSong() const;
/** /**
* @brief getEndPoint * @brief getEndPoint
@ -266,7 +288,7 @@ public:
* @brief next * @brief next
* @return true if all done; * @return true if all done;
*/ */
bool next(bool random = false); bool next();
/** /**
* @brief prev * @brief prev
@ -274,6 +296,12 @@ public:
*/ */
bool prev(); bool prev();
/**
* @brief playState
* @return state of media data
*/
QMediaPlayer::State playState()const;
Sync(const QString &address = DEFAULT_ADRESS, int port = DEFAULT_PORT, const QString& datadir = DATABASE_NAME); Sync(const QString &address = DEFAULT_ADRESS, int port = DEFAULT_PORT, const QString& datadir = DATABASE_NAME);
~Sync(); ~Sync();
@ -310,6 +338,12 @@ signals:
*/ */
void currentSongChanged(); void currentSongChanged();
/**
* @brief playStateChanged
* emited when state of playing song changed
*/
void playStateChanged();
}; };
} }