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 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)
}
}

View File

@ -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

View File

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

View File

@ -94,7 +94,7 @@ QVariant PlayListModel::data(const QModelIndex &index, int role) const
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->isSelected = !i->isSelected)){
@ -114,7 +114,7 @@ bool PlayListModel::select(int id){
QList<int> PlayListModel::getSelected(){
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){
result.push_back(i->id);
}
@ -124,7 +124,7 @@ QList<int> PlayListModel::getSelected(){
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){
return i->isSelected;
}

View File

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

View File

@ -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<syncLib::SongHeader>* SyncEngine::currentPlayList() const{
const QList<syncLib::SongStorage>* 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<syncLib::SongHeader> &playList, const QString &name){
bool SyncEngine::getPlayList(QList<SongStorage> &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;

View File

@ -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<syncLib::SongHeader> *currentPlayList() const;
const QList<SongStorage> *currentPlayList() const;
/**
* @brief currentPlayListName
@ -138,7 +139,7 @@ public slots:
* @param name - name of selected playList
* @return
*/
bool getPlayList(QList<syncLib::SongHeader> &playList, const QString& name);
bool getPlayList(QList<SongStorage> &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

View File

@ -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

View File

@ -2,6 +2,8 @@
#include <QSqlQuery>
#include <QtSql>
#include "exaptions.h"
#include <QSettings>
#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<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{
#ifdef QT_DEBUG
qDebug()<< qyery << ": fail:\n " <<this->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;
@ -146,15 +218,11 @@ int MySql::save(const Song &song){
}
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;
}
"('%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 Song &song){
QUrl url;
if(!saveToStorage(url, song)){
return false;
}
return save(SongStorage(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;
}
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<SongHeader>& list, const QString& playList, bool forEditing){
bool MySql::updateAvailableSongs(QList<SongStorage>& 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<SongHeader>& 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<SongHeader>& 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<SongHeader>& 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<SongHeader> tempList;
QList<SongStorage> 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;

View File

@ -1,7 +1,7 @@
#ifndef MYSQL_H
#define MYSQL_H
#include <QString>
#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<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.
@ -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.

View File

@ -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();

View File

@ -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.
*/

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 <QStringList>
#include <QRegularExpression>
#include <QFile>
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<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()
{
@ -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;
}

View File

@ -4,6 +4,7 @@
#include <QByteArray>
#include <QDataStream>
#include "chronotime.h"
#include <QMediaContent>
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();
@ -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

View File

@ -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<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)){
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;
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;
}
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<const SongHeader&>(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);
}
emit currentSongChanged();
return true;
bool Sync::play(const QMediaContent& media, bool fbroadcast){
if(media.isNull()){
return false;
}
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<ETcpSocket *> * 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;
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<SongHeader>* Sync::getPlayList() const{
return &playList;
const QList<SongStorage>* 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();
}

View File

@ -6,7 +6,9 @@
#include <chrono>
#include "config.h"
#include "mysql.h"
#include <QMediaPlaylist>
#include "player.h"
#include "playlist.h"
namespace syncLib {
@ -14,8 +16,6 @@ typedef std::chrono::time_point<std::chrono::high_resolution_clock> 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<SongHeader> playList;
PlayList *playList;
QString lastUsedPlayList;
int currentSongIndex;
QList<ETcpSocket*> 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<SongHeader> &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<SongHeader> *getPlayList() const;
const QList<SongStorage> *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();
};
}