diff --git a/SoundBand/Header.qml b/SoundBand/Header.qml index 99e1ac9..1014a3e 100644 --- a/SoundBand/Header.qml +++ b/SoundBand/Header.qml @@ -10,7 +10,7 @@ Item { property int currentSongId: 0 property string currentSongName: qsTr("Song is not selected") - property bool playState: false + property bool playState: syncEngine.playState === 1; function changeSong(id, name){ @@ -84,7 +84,6 @@ Item { onClicked: { syncEngine.pause(playState); - playState = !playState; } } @@ -123,7 +122,7 @@ Item { anchors.right: parent.right; anchors.rightMargin: Utils.dp(Screen.pixelDensity, 5) onClicked: { - state = ++state % 4; + state = ++state % 5; syncEngine.setRepeat(state) } } diff --git a/SoundBand/SoundBand.pro b/SoundBand/SoundBand.pro index 0bffc62..c89350c 100644 --- a/SoundBand/SoundBand.pro +++ b/SoundBand/SoundBand.pro @@ -33,7 +33,8 @@ SOURCES += main.cpp \ playlistmodel.cpp \ serverlistmodel.cpp \ playlistsmodel.cpp \ - currentplaylistmodel.cpp + currentplaylistmodel.cpp \ + ../sync/playlist.cpp RESOURCES += qml.qrc @@ -69,5 +70,6 @@ HEADERS += \ playlistmodel.h \ serverlistmodel.h \ playlistsmodel.h \ - currentplaylistmodel.h + currentplaylistmodel.h \ + ../sync/playlist.h diff --git a/SoundBand/currentplaylistmodel.h b/SoundBand/currentplaylistmodel.h index 790ac55..0809d81 100644 --- a/SoundBand/currentplaylistmodel.h +++ b/SoundBand/currentplaylistmodel.h @@ -17,7 +17,7 @@ class CurrentPlayListModel : public QAbstractListModel private: SyncEngine * syncEngine; - const QList *playList; + const QList *playList; int itemCount; private slots: diff --git a/SoundBand/playlistmodel.cpp b/SoundBand/playlistmodel.cpp index 1cf184c..9e03ba8 100644 --- a/SoundBand/playlistmodel.cpp +++ b/SoundBand/playlistmodel.cpp @@ -94,7 +94,7 @@ QVariant PlayListModel::data(const QModelIndex &index, int role) const bool PlayListModel::select(int id){ - for(QList::Iterator i = playList.begin(); i < playList.end(); i++){ + for(QList::Iterator i = playList.begin(); i < playList.end(); i++){ if(i->id == id){ if((i->isSelected = !i->isSelected)){ @@ -114,7 +114,7 @@ bool PlayListModel::select(int id){ QList PlayListModel::getSelected(){ QList result; - for(QList::Iterator i = playList.begin(); i < playList.end(); i++){ + for(QList::Iterator i = playList.begin(); i < playList.end(); i++){ if(i->isSelected){ result.push_back(i->id); } @@ -124,7 +124,7 @@ QList PlayListModel::getSelected(){ bool PlayListModel::isSelected(int id){ - for(QList::Iterator i = playList.begin(); i < playList.end(); i++){ + for(QList::Iterator i = playList.begin(); i < playList.end(); i++){ if(i->id == id){ return i->isSelected; } diff --git a/SoundBand/playlistmodel.h b/SoundBand/playlistmodel.h index 58b7c41..bc8d143 100644 --- a/SoundBand/playlistmodel.h +++ b/SoundBand/playlistmodel.h @@ -16,7 +16,7 @@ class PlayListModel : public QAbstractListModel private: SyncEngine * syncEngine; - QList playList; + QList playList; QString playListName; int itemCount; diff --git a/SoundBand/syncengine.cpp b/SoundBand/syncengine.cpp index 1b4ef08..1eb35da 100644 --- a/SoundBand/syncengine.cpp +++ b/SoundBand/syncengine.cpp @@ -8,9 +8,11 @@ SyncEngine::SyncEngine() sqlApi = sync->getSqlApi(); connect(sync, SIGNAL(networkStateChange()), this, SIGNAL(serversCountChanged())); + connect(sync, SIGNAL(currentPlayListChanged()), this, SIGNAL(currentPlayListNameChanged())); connect(sync, SIGNAL(currentPlayListChanged()), this, SIGNAL(currentPlayListCountChanged())); connect(sync, SIGNAL(seekChanged(qint64)), this, SLOT(seekChanged(qint64))); connect(sync, SIGNAL(currentSongChanged()), this, SIGNAL(currentSongChanged())); + connect(sync, SIGNAL(playStateChanged()), this, SIGNAL(playStateChanged())); } @@ -42,7 +44,7 @@ bool SyncEngine::init(){ return true; } -const QList* SyncEngine::currentPlayList() const{ +const QList* SyncEngine::currentPlayList() const{ return sync->getPlayList(); } @@ -140,7 +142,7 @@ int SyncEngine::repeat()const{ } void SyncEngine::setRepeat(int flag){ - sync->setRepeat((syncLib::Repeat)flag); + sync->setRepeat((QMediaPlaylist::PlaybackMode)flag); } bool SyncEngine::setPlayList(const QString& name){ @@ -157,7 +159,7 @@ bool SyncEngine::setPlayList(const QString& name){ } -bool SyncEngine::getPlayList(QList &playList, const QString &name){ +bool SyncEngine::getPlayList(QList &playList, const QString &name){ 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(){ disconnect(sync, SIGNAL(networkStateChange()), this, SIGNAL(serversCountChanged())); delete sync; diff --git a/SoundBand/syncengine.h b/SoundBand/syncengine.h index a89f69f..6be2ad1 100644 --- a/SoundBand/syncengine.h +++ b/SoundBand/syncengine.h @@ -16,6 +16,7 @@ class SyncEngine : public QObject Q_PROPERTY(double pos READ pos WRITE setPos NOTIFY posChanged) Q_PROPERTY(int repeat READ repeat WRITE setRepeat NOTIFY repeatChanged) Q_PROPERTY(QString currentPlayListName READ currentPlayListName NOTIFY currentPlayListNameChanged) + Q_PROPERTY(int playState READ playState NOTIFY playStateChanged) private: @@ -74,7 +75,7 @@ public slots: * @brief currentPlayList * @return return current Play List */ - const QList *currentPlayList() const; + const QList *currentPlayList() const; /** * @brief currentPlayListName @@ -138,7 +139,7 @@ public slots: * @param name - name of selected playList * @return */ - bool getPlayList(QList &playList, const QString& name); + bool getPlayList(QList &playList, const QString& name); /** * @brief lastError - message of last error @@ -222,6 +223,17 @@ public slots: */ 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: /** @@ -278,6 +290,12 @@ signals: */ void currentPlayListNameChanged(); + /** + * @brief playStateChanged + * emited when state of playing song changed + */ + void playStateChanged(); + }; #endif // SYNCENGINE_H diff --git a/sync/config.h b/sync/config.h index f8f81ec..4988bef 100644 --- a/sync/config.h +++ b/sync/config.h @@ -3,6 +3,9 @@ // general otions #define CURRENT_PLAYLIST_KEY "currentPlayList" +#define MAIN_FOLDER_KEY "songsfolder" +#define MAIN_FOLDER "soundBand" + // LIB VERSION #define MAJOR_VERSION 0 diff --git a/sync/mysql.cpp b/sync/mysql.cpp index f7693ea..faaedc1 100644 --- a/sync/mysql.cpp +++ b/sync/mysql.cpp @@ -2,6 +2,8 @@ #include #include #include "exaptions.h" +#include +#include "playlist.h" namespace syncLib{ @@ -44,6 +46,8 @@ bool MySql::exec(QSqlQuery *sq,const QString& sqlFile){ void MySql::initDB(const QString &database){ if(db) return; dataBaseName = database; + QSettings settings; + songDir = settings.value(MAIN_FOLDER_KEY, QDir::homePath() + "/soundBand").toString(); db = new QSqlDatabase(); *db = QSqlDatabase::addDatabase("QSQLITE", database); QDir d(QString("./%0").arg(dataBaseName)); @@ -51,23 +55,11 @@ void MySql::initDB(const QString &database){ if(db->open()){ 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(" "id INTEGER PRIMARY KEY AUTOINCREMENT, " "name VARCHAR(100), " "size INT NOT NULL, " - "data BLOB NOT NULL " + "data TEXT NOT NULL " ")"); if(!qyery->exec(qyer)){ sqlErrorLog(qyer); @@ -127,15 +119,95 @@ void MySql::initDB(const QString &database){ } } +bool MySql::find(const QMediaContent &song, SongStorage &response){ + QList 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 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 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{ #ifdef QT_DEBUG qDebug()<< qyery << ": fail:\n " <qyery->lastError(); #endif } -int MySql::save(const Song &song){ - QString qyer = QString("SELECT id from songs where name='%0' and size=%1").arg(song.name, - QString::number(song.size)); +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)); + if(!qyery->exec(qyer)){ sqlErrorLog(qyer); return -1; @@ -145,16 +217,12 @@ int MySql::save(const Song &song){ return qyery->value(0).toInt(); } - qyer = QString("INSERT INTO songs (name,size,data) VALUES" - "('%0',%1,:val)").arg(song.name, - QString::number(song.size)); - if(!qyery->prepare(qyer)){ - sqlErrorLog(qyer + " prepare error"); - return -1; - } + qyer = QString("INSERT INTO songs (name,size,data) VALUES " + "('%0',%1,'%2')").arg(song.name, + QString::number(song.size), + song.url.path()); - qyery->bindValue(":val",song.source); - if(!qyery->exec()){ + if(!qyery->exec(qyer)){ sqlErrorLog(qyer); return -1; } @@ -169,18 +237,28 @@ int MySql::save(const Song &song){ return result; } -int MySql::save(const QString &url){ - QFile f(QUrl(url).toLocalFile()); - if(!f.open(QIODevice::ReadOnly)){ +int MySql::save(const Song &song){ + + + QUrl url; + if(!saveToStorage(url, song)){ + return false; + } + + return save(SongStorage(url)); + + +} + +int MySql::save(const QString &url){ + + QUrl qurl = QUrl(url); + + if(!qurl.isValid()){ return -1; } - QByteArray bytes = f.readAll(); - f.close(); - QString name = url.right(url.lastIndexOf(QRegularExpression("[\\/]"))); // meby [[\\\/]] - Song song; - song.name = name; - song.size = bytes.size(); - song.source = bytes; + + SongStorage song(qurl); if(!song.isNameValid()){ return -1; @@ -190,8 +268,7 @@ int MySql::save(const QString &url){ } -bool MySql::load(const SongHeader &song,Song &result){ - result.clear(); +bool MySql::load(const SongHeader &song, SongStorage &result){ if(song.id > -1){ QString qyer = QString("SELECT * from songs where id=%0").arg(song.id); if(!qyery->exec(qyer)){ @@ -213,17 +290,17 @@ bool MySql::load(const SongHeader &song,Song &result){ result.id = qyery->value(0).toInt(); result.name = qyery->value(1).toString(); result.size = qyery->value(2).toInt(); - result.source = qyery->value(3).toByteArray(); - return true; + result.url = QUrl::fromLocalFile(qyery->value(3).toString()); + return result.isValid(); } -bool MySql::updateAvailableSongs(QList& list, const QString& playList, bool forEditing){ +bool MySql::updateAvailableSongs(QList& list, const QString& playList, bool forEditing){ QString qyer; if(playList.isEmpty() || playList == ALL_SONGS_LIST || forEditing){ - qyer = QString("SELECT id,name,size from songs"); + qyer = QString("SELECT * from songs"); }else{ - qyer = QString("SELECT id,name,size from songs where " + qyer = QString("SELECT * from songs where " "id in (select song from playlistsdata where " "playlist='%0')").arg(playList); } @@ -236,11 +313,13 @@ bool MySql::updateAvailableSongs(QList& list, const QString& playLis list.clear(); while(qyery->next()){ - SongHeader song; + SongStorage song; song.isSelected = !forEditing || playList == ALL_SONGS_LIST; song.id = qyery->value(0).toInt(); song.name = qyery->value(1).toString(); song.size = qyery->value(2).toInt(); + song.url = QUrl::fromLocalFile(qyery->value(3).toString()); + list.push_back(song); } @@ -255,7 +334,7 @@ bool MySql::updateAvailableSongs(QList& list, const QString& playLis } while(qyery->next()){ - for(SongHeader& item:list){ + for(SongStorage& item:list){ int id = qyery->value(0).toInt(); if(item.id == id){ item.isSelected = true; @@ -265,19 +344,21 @@ bool MySql::updateAvailableSongs(QList& list, const QString& playLis } } - return true; } -bool MySql::updateAvailableSongs(QStringList& list, const QString& playList){ +bool MySql::updateAvailableSongs(PlayList& list, const QString& playList, bool forEditing){ - QList tempList; + QList tempList; + list.clear(); - if(!updateAvailableSongs(tempList, playList)) + if(!updateAvailableSongs(tempList, playList, forEditing)) return false; - for(SongHeader &header : tempList){ - list.push_back(header.name); + for(SongStorage &header : tempList){ + if(!list.addMedia(header)){ + this->removeSong(header); + } } return true; diff --git a/sync/mysql.h b/sync/mysql.h index 125defa..e5ab00d 100644 --- a/sync/mysql.h +++ b/sync/mysql.h @@ -1,7 +1,7 @@ #ifndef MYSQL_H #define MYSQL_H #include -#include "song.h" +#include "playlist.h" class QSqlDatabase; class QSqlQuery; @@ -14,6 +14,7 @@ private: QSqlDatabase *db; QSqlQuery *qyery; QString dataBaseName; + QString songDir; /** * @brief sqlErrorLog show sql error @@ -21,6 +22,14 @@ private: */ 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: MySql(const QString& databasename); /** @@ -28,13 +37,56 @@ public: */ 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 song - * @brief result - the resulting value; * @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. @@ -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' * @return true if all done */ - bool updateAvailableSongs(QList& list, const QString &playList = "", bool forEditing = false); + bool updateAvailableSongs(QList& list, const QString &playList = "", bool forEditing = false); /** * @brief updateAvelableSongs will update the list of participants of songs. @@ -70,7 +122,7 @@ public: * @param playList - play list of songs (string). * @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. diff --git a/sync/player.cpp b/sync/player.cpp index 1f8494a..05fa849 100644 --- a/sync/player.cpp +++ b/sync/player.cpp @@ -12,24 +12,6 @@ Player::Player(const QString &bufferFile, QObject *parent, Flags flags): 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(){ bufferVolume = volume(); diff --git a/sync/player.h b/sync/player.h index 9af3603..d5077c5 100644 --- a/sync/player.h +++ b/sync/player.h @@ -21,13 +21,6 @@ public: 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. */ diff --git a/sync/playlist.cpp b/sync/playlist.cpp new file mode 100644 index 0000000..a44829d --- /dev/null +++ b/sync/playlist.cpp @@ -0,0 +1,93 @@ +#include "playlist.h" + +PlayList::PlayList() +{ + playListInfo.clear(); + playList = new QMediaPlaylist(); +} + +QList* 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(&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; +} diff --git a/sync/playlist.h b/sync/playlist.h new file mode 100644 index 0000000..256b40b --- /dev/null +++ b/sync/playlist.h @@ -0,0 +1,113 @@ +#ifndef PLAYLIST_H +#define PLAYLIST_H +#include +#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 playListInfo; +public: + PlayList(); + + /** + * @brief getInfo + * @return info from database about songs + */ + QList* 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 diff --git a/sync/song.cpp b/sync/song.cpp index ed62b58..abfc11d 100644 --- a/sync/song.cpp +++ b/sync/song.cpp @@ -1,5 +1,8 @@ #include "song.h" #include +#include +#include + namespace syncLib{ static const QStringList ValidSongs = {".mp3", ".wav", ".ogg"}; @@ -11,6 +14,31 @@ SongHeader::SongHeader() 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){ this->id = right.id; this->name = right.name; @@ -18,15 +46,35 @@ SongHeader& SongHeader::operator =(const SongHeader& right){ 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; } -unsigned int SongHeader::getSize() const{ - QByteArray size; - QDataStream stream(size); - stream << id << name << this->size; - return size.size(); +bool SongHeader::operator ==(const QMediaContent& right)const{ + QString name; + if(!getName(name, right.canonicalUrl())){ + return false; + } + + int size;; + if(!getSize(size, right.canonicalUrl())){ + return false; + } + + return this->name == name && this->size == size; } bool SongHeader::isNameValid() const{ @@ -45,14 +93,6 @@ bool SongHeader::isValid() const{ SongHeader::~SongHeader(){} - - -Song::Song(): - SongHeader() -{ - source.clear(); -} - QDataStream& operator << (QDataStream& stream, const SongHeader& song){ stream << song.id; stream << song.name; @@ -66,6 +106,93 @@ QDataStream& operator >> (QDataStream& stream, SongHeader& song){ 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(song); + stream << song.url; + return stream; +} + +QDataStream& operator >> (QDataStream& stream, SongStorage& song){ + stream >> static_cast(song); + stream >> song.url; + return stream; +} + + +Song::Song(): + SongHeader() +{ + source.clear(); +} + Song::Song(const SongHeader& from) :Song::Song() { @@ -78,10 +205,6 @@ void Song::clear(){ source.clear(); } -unsigned int Song::getSize() const{ - return SongHeader::getSize() + source.size(); -} - const QByteArray& Song::getSource()const{ return source; } diff --git a/sync/song.h b/sync/song.h index eab15b6..54aa297 100644 --- a/sync/song.h +++ b/sync/song.h @@ -4,6 +4,7 @@ #include #include #include "chronotime.h" +#include namespace syncLib { @@ -24,7 +25,9 @@ struct Syncer * (id,size and name) */ class SongHeader{ - +protected: + bool getName(QString &name, const QUrl& url)const; + bool getSize(int &size, const QUrl& url)const; public: bool isSelected; int id; @@ -32,8 +35,10 @@ public: int size; SongHeader(); SongHeader& operator = (const SongHeader& right); - bool operator == (const SongHeader& right); - virtual unsigned int getSize() const; + SongHeader& operator = (const QMediaContent& right); + + bool operator == (const SongHeader& right)const; + bool operator == (const QMediaContent& right)const; bool isNameValid() const; virtual bool isValid() const; virtual ~SongHeader(); @@ -53,7 +58,7 @@ public: * @brief The Song class * into this calss added mediadata of playable media file. */ -class Song : public SongHeader{ +class Song : public SongHeader { private: QByteArray source; public: @@ -61,12 +66,36 @@ public: Song(const SongHeader& from); void clear(); const QByteArray& getSource()const; - unsigned int getSize() const; bool isValid() const; ~Song(); friend QDataStream& operator << (QDataStream& stream, const Song& song); friend QDataStream& operator >> (QDataStream& stream, Song& song); 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 diff --git a/sync/sync.cpp b/sync/sync.cpp index 91df405..2744448 100644 --- a/sync/sync.cpp +++ b/sync/sync.cpp @@ -22,11 +22,14 @@ Sync::Sync(const QString &address, int port, const QString &datadir): if(!player->isAvailable()){ throw MediaException(); } + playList = new PlayList; + + player->setPlaylist(playList->getList()); + fbroadcaster = false; resyncCount = 0; lastSyncTime = 0; - currentSongIndex = 0; ping = 0; sql = new MySql(datadir); @@ -41,7 +44,15 @@ MySql* Sync::getSqlApi(){ return sql; } -bool Sync::updateSongs(QList& 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)){ return false; } @@ -59,28 +70,54 @@ const QString& Sync::getPlayListName() const{ return lastUsedPlayList; } -bool Sync::findHeader(const Song &song){ +bool Sync::play(bool fbroadcast){ + fbroadcaster = fbroadcast; - for(int i = 0; i < playList.size(); i++){ - if(playList[i] == static_cast(song)){ - currentSongIndex = i; - return true; - } + if(fbroadcaster){ + player->play(); + sync(); + }else{ + player->syncBegin(); + } + emit currentSongChanged(); + + 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){ - Song song; + if(!header.isValid()){ + return false; + } + + if(playList->selectSong(header)){ + return play(fbroadcast); + } + + SongStorage song; SongHeader newheader = header; newheader.id = -1; if(!sql->load(newheader, song)){ return false; } - return Sync::play(song, fbroadcast); + return play(song, fbroadcast); } bool Sync::play(const Song &song, bool fbroadcast){ @@ -89,27 +126,27 @@ bool Sync::play(const Song &song, bool fbroadcast){ return false; } - if(!player->setMediaFromBytes(song.getSource())){ - return false; - } - - fbroadcaster = fbroadcast; - - if(!findHeader(song) && sql->save(song) > -1 && - updateSongs(playList) && !findHeader(song)){ + QMediaContent savedSong; + if(!sql->find(static_cast(song), savedSong) && sql->save(song) > -1 && + !sql->find((SongHeader&)song, savedSong)){ return false; } - if(fbroadcaster){ - player->play(); - sync(); - }else{ - player->syncBegin(); + return play(savedSong, fbroadcast); +} + +bool Sync::play(const QMediaContent& media, bool fbroadcast){ + + if(media.isNull()){ + return false; } - emit currentSongChanged(); - return true; + if(!setSingle(media)){ + return false; + } + + return Sync::play(fbroadcast); } bool Sync::play(int id_song, bool fbroadcast){ @@ -119,9 +156,12 @@ bool Sync::play(int id_song, bool fbroadcast){ } SongHeader header; + SongStorage song; header.id = id_song; - Song song; - sql->load(header, song); + + if(!sql->load(header, song)){ + return false; + } return Sync::play(song, fbroadcast); } @@ -135,22 +175,21 @@ bool Sync::play(QString url){ return Sync::play(id); } -Repeat Sync::repeat()const{ - return _repeat; +QMediaPlaylist::PlaybackMode Sync::repeat()const{ + return playList->getList()->playbackMode(); } -void Sync::setRepeat(Repeat flag){ - _repeat = flag; +void Sync::setRepeat(QMediaPlaylist::PlaybackMode flag){ + playList->getList()->setPlaybackMode(flag); } bool Sync::pause(bool state){ - if(!fbroadcaster){ - - if(playList.isEmpty()) + if(player->state() == QMediaPlayer::StoppedState){ + if(playList->isEmpty()) return false; - return play(playList[0]); + return play(); } if(state){ @@ -189,6 +228,9 @@ bool Sync::sync(const Syncer &sync, milliseconds ping){ } +/** + * @todo thi nead send a hedaer +*/ void Sync::sync(){ if(fbroadcaster) @@ -196,7 +238,7 @@ void Sync::sync(){ package pac; if(!createPackage(t_sync, pac)){ - throw CreatePackageExaption(); + CreatePackageExaption(); return; } node->WriteAll(pac.parseTo()); @@ -252,18 +294,17 @@ bool Sync::createPackage(Type type, package &pac){ } if(type & TypePackage::t_song_h && fbroadcaster){ - if(currentSongIndex < 0) + if(playList->getList()->currentIndex() < 0) return false; - pac.header = playList[currentSongIndex]; - + pac.header = *playList->currentHeader(); } if(type & TypePackage::t_song && fbroadcaster){ - if(currentSongIndex < 0) + if(playList->getList()->currentIndex() < 0) return false; - if(!sql->load(playList[currentSongIndex], pac.source)) + if(!playList->currentSong()->toSong(pac.source)) return false; } @@ -387,7 +428,7 @@ void Sync::packageRender(ETcpSocket *socket){ // if requst from client if(pkg.getType() & t_play & t_sync){ - if(currentSongIndex < 0){ + if(playList->getList()->currentIndex() < 0){ throw SyncError(); socket->nextItem(); continue; @@ -443,32 +484,27 @@ void Sync::deepScaned(QList * list){ } void Sync::endPlay(QMediaPlayer::State state){ - if(state == QMediaPlayer::StoppedState){ - switch (_repeat) { - case allPlayListRandom: - next(true); - break; - - case allPlayList: - next(false); - break; - - case oneMusic: - play(playList[currentSongIndex]); - break; - - default: - currentSongIndex = -1; - fbroadcaster = false; - break; - } + switch (state) { + case QMediaPlayer::StoppedState: + fbroadcaster = false; + break; + case QMediaPlayer::PlayingState: + sync(); + break; + case QMediaPlayer::PausedState: + break; + default: + break; } + + emit playStateChanged(); } 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){ @@ -488,19 +524,16 @@ unsigned int Sync::seek() const{ return player->position(); } -const QList* Sync::getPlayList() const{ - return &playList; +const QList* Sync::getPlayList() const{ + return playList->getInfo(); } int Sync::getCurrentSongIndex()const{ - return currentSongIndex; + return playList->getList()->currentIndex(); } -const SongHeader* Sync::getCurrentSong() const{ - if(currentSongIndex < 0 || currentSongIndex >= playList.size()){ - return nullptr; - } - return &playList[currentSongIndex]; +const SongStorage* Sync::getCurrentSong() const{ + return playList->currentSong(); } qint64 Sync::getEndPoint() const { @@ -509,48 +542,53 @@ qint64 Sync::getEndPoint() const { int Sync::addNewSong(const QString &url){ int result = sql->save(url); - updateSongs(playList); + updateSongs(*playList); return result; } bool Sync::updatePlayList(const QString &_playList){ - if(!updateSongs(playList, _playList)){ + if(!updateSongs(*playList, _playList)){ return false; } - if(!playList.size()) + if(!playList->size()) return false; if(fbroadcaster){ - play(playList.first()); + play(fbroadcaster); } return true; } -bool Sync::next(bool random){ - if(playList.isEmpty()) +bool Sync::next(){ + if(playList->isEmpty()) return false; - currentSongIndex = (currentSongIndex + ((random)? rand() % 10000:1)) % playList.size(); - return play(playList[currentSongIndex]); + playList->next(); + emit currentSongChanged(); + return true; } bool Sync::prev(){ - if(playList.isEmpty()) + if(playList->isEmpty()) return false; - --currentSongIndex; - if(currentSongIndex < 0) - currentSongIndex = playList.size() - 1; - return play(playList[currentSongIndex]); + playList->prev(); + emit currentSongChanged(); + return true; +} + +QMediaPlayer::State Sync::playState()const{ + return player->state(); } Sync::~Sync(){ delete node; delete player; delete sql; + delete playList; servers.clear(); } diff --git a/sync/sync.h b/sync/sync.h index 62eb1cd..9c65010 100644 --- a/sync/sync.h +++ b/sync/sync.h @@ -6,7 +6,9 @@ #include #include "config.h" #include "mysql.h" +#include #include "player.h" +#include "playlist.h" namespace syncLib { @@ -14,8 +16,6 @@ typedef std::chrono::time_point Clock; class Node; -enum Repeat{noRepeat, oneMusic, allPlayList, allPlayListRandom}; - /** * @brief The Sync class is main class of this library. @@ -27,9 +27,8 @@ class Sync : public QObject private: Node *node; Player *player; - QList playList; + PlayList *playList; QString lastUsedPlayList; - int currentSongIndex; QList servers; bool fbroadcaster; int resyncCount; @@ -38,13 +37,6 @@ private: LocalScanner deepScaner; MySql *sql; 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 @@ -62,11 +54,17 @@ private: 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 * @return true if all done */ - bool updateSongs(QList &list, const QString &playList = ""); + bool updateSongs(PlayList &list, const QString &playList = ""); /** * @brief packageRender - the handler of all messages received. @@ -96,13 +94,13 @@ public: * @brief repeat * @return flag of repeat */ - Repeat repeat()const; + QMediaPlaylist::PlaybackMode repeat()const; /** * @brief setRepeat * @param flag new flag of repeat */ - void setRepeat(Repeat flag); + void setRepeat(QMediaPlaylist::PlaybackMode flag); /** * @brief getSqlApi @@ -110,6 +108,21 @@ public: */ 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. * @param header of song @@ -117,6 +130,15 @@ public: * @return true if all done else false. */ 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. * @param song playning media data. @@ -228,7 +250,7 @@ public: * @brief getPlayList * @return list of available songs */ - const QList *getPlayList() const; + const QList *getPlayList() const; /** * @brief SongHeader::getCurrentSongIndex @@ -240,7 +262,7 @@ public: * @brief getCurrentSong * @return playing song. */ - const SongHeader *getCurrentSong() const; + const SongStorage *getCurrentSong() const; /** * @brief getEndPoint @@ -266,7 +288,7 @@ public: * @brief next * @return true if all done; */ - bool next(bool random = false); + bool next(); /** * @brief prev @@ -274,6 +296,12 @@ public: */ 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(); @@ -310,6 +338,12 @@ signals: */ void currentSongChanged(); + /** + * @brief playStateChanged + * emited when state of playing song changed + */ + void playStateChanged(); + }; }