Merge pull request #1 from EndrII/sync

Sync
This commit is contained in:
Andrei Yankovich 2017-11-24 20:22:25 +02:00 committed by GitHub
commit 92c7fa39a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1393 additions and 0 deletions

144
sync/ETcpSocket.cpp Executable file
View File

@ -0,0 +1,144 @@
#include "ETcpSocket.h"
#include "exaptions.h"
ETcpSocket::ETcpSocket()
{
source=new QTcpSocket();
init();
}
ETcpSocket::ETcpSocket(QTcpSocket*ptr)
{
source=ptr;
init();
}
ETcpSocket::ETcpSocket(const QString& address, int port){
source = new QTcpSocket();
if(!source->bind(QHostAddress(address),port) || !source->open(QIODevice::ReadWrite)){
throw AddNodeExaption();
}
init();
}
void ETcpSocket::init(){
array=new QByteArray;
connect(source,SIGNAL(connected()),this,SLOT(connected_()));
connect(source,SIGNAL(disconnected()),this,SLOT(disconnected_()));
connect(source,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(error_(QAbstractSocket::SocketError)));
connect(source,SIGNAL(hostFound()),this,SLOT(hostFound_()));
connect(source,SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)),this,SLOT(proxyAuthenticationRequired_(const QNetworkProxy &, QAuthenticator *)));
connect(source,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(stateChanged_(QAbstractSocket::SocketState)));
connect(source,SIGNAL(readyRead()),this,SLOT(readReady_()));
}
void ETcpSocket::error_(QAbstractSocket::SocketError i){
emit Error(this,i);
}
void ETcpSocket::connected_(){
emit Connected(this);
}
void ETcpSocket::disconnected_(){
emit Disconnected(this);
}
void ETcpSocket::hostFound_(){
emit HostFound(this);
}
void ETcpSocket::proxyAuthenticationRequired_(const QNetworkProxy &proxy, QAuthenticator *authenticator){
emit ProxyAuthenticationRequired(this,proxy,authenticator);
}
void ETcpSocket::stateChanged_(QAbstractSocket::SocketState socketState){
emit StateChanged(this,socketState);
}
void ETcpSocket::readReady_(){
bool sizewrite=array->isEmpty();
//while(source->bytesAvailable())
array->append(source->readAll());
QDataStream stream(array,QIODevice::ReadOnly);
if(sizewrite)
stream>>size;
#ifdef QT_DEBUG
qDebug()<<"messae size:"<<size;
qDebug()<<"message package size:"<<array->size();
#endif
if(size==array->size())
{
array->remove(0,sizeof(qint32));
ReadyStack.push_back(array);
array=new QByteArray();
emit Message(this);
}else{
emit donwload(array->size(),size);
}
// emit ReadReady(this);
}
QString ETcpSocket::name() const{
return source->peerAddress().toString();
}
QByteArray* ETcpSocket::topStack(){
if(ReadyStack.size())
return ReadyStack.front();
return NULL;
}
QTcpSocket* ETcpSocket::getSource()const{
return source;
}
void ETcpSocket::nextItem(){
if( ReadyStack.size()){
ReadyStack.pop_front();
}
}
int ETcpSocket::sizeDescriptPackege(){
return sizeof(qint32);
}
QString ETcpSocket::toStringTcp(){
return source->peerAddress().toString();
}
bool ETcpSocket::Write(const QByteArray&data){
if(source->state()==QTcpSocket::ConnectedState){
QByteArray array;
QDataStream stream(&array,QIODevice::ReadWrite);
stream<<qint32(0);
//stream<<data;
array.append(data);
stream.device()->seek(0);
stream<<qint32(array.size());
#ifdef QT_DEBUG
qDebug()<<"size :"<<array.size();
qint64 temp= source->write(array);
qDebug()<<"size write:"<<temp<<" size packege:"<<array.size();
return temp==(array.size());
#else
return source->write(array)==(array.size());
#endif
}
return false;
}
ETcpSocket::~ETcpSocket()
{
for(QByteArray*i:ReadyStack){
i->clear();
delete i;
}
disconnect(source,SIGNAL(connected()),this,SLOT(connected_()));
disconnect(source,SIGNAL(disconnected()),this,SLOT(disconnected_()));
disconnect(source,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(error_(QAbstractSocket::SocketError)));
disconnect(source,SIGNAL(hostFound()),this,SLOT(hostFound_()));
disconnect(source,SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)),this,SLOT(proxyAuthenticationRequired_(const QNetworkProxy &, QAuthenticator *)));
disconnect(source,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(stateChanged_(QAbstractSocket::SocketState)));
disconnect(source,SIGNAL(readyRead()),this,SLOT(readReady_()));
source->deleteLater();
}

74
sync/ETcpSocket.h Executable file
View File

@ -0,0 +1,74 @@
#ifndef CLIENT_H
#define CLIENT_H
#include <QTcpSocket>
#include <QTcpServer>
#include <QList>
#include <QDataStream>
/**
* @brief The ETcpSocket class
* example :
* ETcpSocket *tcp;
* try{
* tcp = new ETcpSocket(addres,port);
* }catch(addNodeExaption e){
* e.what();
* }
* QByteArray *array;
* while(array = tcp.getSource()){
* package pkg(*array);
* package ans = ansver(pkg);
* tcp.Write(ans);
* array->clear();
* delete array;
*
* }
*
*/
class ETcpSocket:public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name)
private:
QTcpSocket *source;
QByteArray *array;
qint32 size;
QList<QByteArray*> ReadyStack;
void init();
private slots:
void connected_();
void disconnected_();
void error_(QAbstractSocket::SocketError socketError);
void hostFound_();
void readReady_();
void proxyAuthenticationRequired_(const QNetworkProxy &proxy, QAuthenticator *authenticator);
void stateChanged_(QAbstractSocket::SocketState socketState);
public:
explicit ETcpSocket();
explicit ETcpSocket(QTcpSocket*);
explicit ETcpSocket(const QString& addres,int port);
QTcpSocket* getSource()const;
QByteArray* topStack();
void nextItem();
int sizeDescriptPackege();
bool Write(const QByteArray&);
~ETcpSocket();
public slots:
QString name()const;
QString toStringTcp();
signals:
void donwload(int val,int max);
void ReadyComplit(ETcpSocket*,QDataStream&);
void Connected(ETcpSocket*);
void Message(ETcpSocket*);
void Disconnected(ETcpSocket*);
void Error(ETcpSocket*,QAbstractSocket::SocketError socketError);
void HostFound(ETcpSocket*);
void ProxyAuthenticationRequired(ETcpSocket*,const QNetworkProxy &proxy, QAuthenticator *authenticator);
void StateChanged(ETcpSocket*,QAbstractSocket::SocketState socketState);
//void Connected(QTcpSocket*);
//void errorConnect(QTcpSocket*,QAbstractSocket::SocketError);
};
#endif // CLIENT_H

68
sync/LocalScanner.cpp Executable file
View File

@ -0,0 +1,68 @@
#include "LocalScanner.h"
#include "config.h"
LocalScanner::LocalScanner():
QObject()
{
wiat.setInterval(1000);
connect(&wiat,SIGNAL(timeout()),SLOT(scaned_()));
}
void LocalScanner::clear(){
results.clear();
}
void LocalScanner::setInterval(int msec){
wiat.setInterval(msec);
}
void LocalScanner::clearSocets(){
for(ETcpSocket* i:socets)
delete i;
socets.clear();
}
QHostAddress LocalScanner::thisAdress(){
QList<QHostAddress> adress= QNetworkInterface::allAddresses();
for(QHostAddress &ip:adress)
if(ip.protocol() == QAbstractSocket::IPv4Protocol && ip != QHostAddress(QHostAddress::LocalHost))
return ip;
return QHostAddress::LocalHost;
}
void LocalScanner::scane(){
if(!socets.empty())
return ;
QList<QHostAddress> adress= QNetworkInterface::allAddresses();
clear();
wiat.start();
for(QHostAddress &ip:adress){
if(ip.protocol() == QAbstractSocket::IPv4Protocol && ip != QHostAddress(QHostAddress::LocalHost)){
for(int i=0;i<256;i++){
QString adr= ip.toString();
adr=adr.left(adr.lastIndexOf("."))+"."+QString::number(i);
ETcpSocket *temp=new ETcpSocket;
connect(temp,SIGNAL(Connected(ETcpSocket*)),SLOT(connected(ETcpSocket*)));
temp->getSource()->connectToHost(adr,DEDAULT_PORT);
socets.push_back(temp);
}
}
}
}
void LocalScanner::scaned_(){
wiat.stop();
emit scaned(&results);
clearSocets();
}
void LocalScanner::connected(ETcpSocket *c){
results.push_back(c);
}
LocalScanner::~LocalScanner(){
for(ETcpSocket* i: socets)
delete i;
socets.clear();
}

29
sync/LocalScanner.h Executable file
View File

@ -0,0 +1,29 @@
#ifndef LocalServers_H
#define LocalServers_H
#include <QNetworkInterface>
#include <QHostInfo>
#include <QList>
#include <QTimer>
#include "ETcpSocket.h"
class LocalScanner:public QObject{
Q_OBJECT
private:
QList<ETcpSocket*> socets;
QList<ETcpSocket*> results;
QTimer wiat;
void clear();
void clearSocets();
private slots:
void scaned_();
void connected(ETcpSocket *);
public:
void scane();
void setInterval(int msec);
static QHostAddress thisAdress();
LocalScanner();
~LocalScanner();
signals:
void scaned(QList<ETcpSocket*>*);
};
#endif // LocalServers_H

19
sync/config.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef CONFIG_H
#define CONFIG_H
// LIB VERSION
#define MAJOR_VERSION 0
#define MINOR_VERSION 0
#define REVISION_VERSION 0
// sqlite config
#define DATABASE_NAME "songdata.dat"
#define DATATABLE_NAME "songs"
// network config
#define DEDAULT_PORT 1239
#define MAX_SYNC_TIME 20 * 1000 // 10 sec on microsec
#define SYNC_TIME 5 * 1000 // 5 sec on microsec
#define DEEP_SCANER_INTERVAL 10000 // 10 sec
#endif // CONFIG_H

58
sync/exaptions.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef EXAPTIONS_H
#define EXAPTIONS_H
#include <exception>
#include <QString>
#include <QTranslator>
/**
* @brief The MediaException class
*/
class MediaException:public std::exception
{
public:
QString what(){
return QObject::tr("Your operating system or platform has not supported media files.");
}
};
class AddNodeExaption:public std::exception
{
public:
QString what(){
return QObject::tr("Address not available");
}
};
class CreatePackageExaption:public std::exception
{
public:
QString what(){
return QObject::tr("Сould not generate network packet");
}
};
class BadAnswerExaption:public std::exception
{
public:
QString what(){
return QObject::tr("could not parse message nodes.");
}
};
class BrodcastConflict:public std::exception
{
public:
QString what(){
return QObject::tr("The server received the packet from the server.");
}
};
class SyncError:public std::exception
{
public:
QString what(){
return QObject::tr("The playlist is empty, the player has nothing to play.");
}
};
#endif // EXAPTIONS_H

210
sync/node.cpp Normal file
View File

@ -0,0 +1,210 @@
#include "node.h"
#include <QTcpSocket>
#include "song.h"
#include <QDataStream>
#include "exaptions.h"
namespace syncLib{
package::package(){
clear();
}
package::package(const QByteArray &array):
package::package(){
parseFrom(array);
}
const SongHeader& package::getHeader() const{
return header;
}
const Song& package::getSong() const{
return source;
}
const Syncer& package::getPlayData() const{
return playdata;
}
const Type& package::getType() const{
return type;
}
bool package::isValid() const{
bool ret = true;
if(type == TypePackage::t_void){
return false;
}
if(type & TypePackage::t_play){
ret = ret && true;
}
if(type & TypePackage::t_sync){
ret = ret && (playdata.run > 0 && playdata.seek > 0);
}
if(type & TypePackage::t_song_h){
ret = ret && header.size > 0;
}
if(type & TypePackage::t_song){
ret = ret && source.size > 0;
}
if(type & TypePackage::t_close){
ret = ret && true;
}
if(type & TypePackage::t_stop){
ret = ret && true;
}
return ret;
}
void package::clear(){
type = TypePackage::t_void;
source.clear();
playdata.run = 0;
playdata.seek = 0;
}
QByteArray package::parseTo(){
QByteArray temp;
QDataStream stream(temp);
temp.clear();
if(isValid()){
stream << static_cast<unsigned char>(type);
if(type & TypePackage::t_sync){
stream << playdata.run;
stream << playdata.seek;
}
if(type & TypePackage::t_song_h){
stream << header;
}
if(type & TypePackage::t_song){
stream << source;
}
}
return temp;
}
bool package::parseFrom(const QByteArray &array){
type = TypePackage::t_void;
QDataStream stream(array);
unsigned char temp_type;
stream >> temp_type;
type = static_cast<TypePackage> (temp_type);
if(type & TypePackage::t_sync){
stream >> playdata.run;
stream >> playdata.seek;
}
if(type & TypePackage::t_song_h){
stream >> header;
}
if(type & TypePackage::t_song){
stream >> source;
}
return isValid();
}
Node::Node():QTcpServer(){
connect(this,SIGNAL(acceptError(QAbstractSocket::SocketError)),SLOT(acceptError_(QAbstractSocket::SocketError)));
connect(this,SIGNAL(newConnection()),SLOT(newConnection_()));
}
void Node::acceptError_(ETcpSocket*c){
c->getSource()->close();
clients.removeOne(c);
emit ClientDisconnected(c);
delete c;
}
QList<ETcpSocket*>* Node::getClients(){
return &clients;
}
void Node::newConnection_(){
ETcpSocket *newClient=new ETcpSocket(nextPendingConnection());
clients.push_back(newClient);
connect(newClient,SIGNAL(Disconnected(ETcpSocket*)),this,SLOT(acceptError_(ETcpSocket*)));
connect(newClient,SIGNAL(Message(ETcpSocket*)),this,SLOT(readData(ETcpSocket*)));
emit ClientConnected(newClient);
}
void Node::readData(ETcpSocket *c){
emit Message(c);
}
void Node::WriteAll(const QByteArray &data){
for(ETcpSocket*i:clients){
i->getSource()->write(data);
}
}
void Node::disconnectClient(ETcpSocket *c){
c->getSource()->close();
clients.removeOne(c);
delete c;
}
bool Node::addNode(const QString &node,int port){
ETcpSocket *temp;
try{
temp = new ETcpSocket(node,port);
}catch(AddNodeExaption &e){
#ifdef QT_DEBUG
qDebug() << e.what();
#endif
return false;
}
clients.push_back(temp);
return true;
}
bool Node::addNode(ETcpSocket *node){
if(node->getSource()->isOpen()){
clients.append(node);
return true;
}
return false;
}
Node::~Node(){
for(ETcpSocket *i:clients){
i->getSource()->close();
delete i;
}
this->close();
}
}

107
sync/node.h Normal file
View File

@ -0,0 +1,107 @@
#ifndef NODE_H
#define NODE_H
#include <QTcpServer>
#include "ETcpSocket.h"
#include <song.h>
class Syncer;
namespace syncLib {
typedef unsigned char Type;
/**
* @brief The TypePackage enum
* t_void - this package empty and not valid.
* t_close - the information about close channel.
* t_sync - the infomation about sync playning media file on network.
* t_song - the package with this type is necessary for translite media data on network.
* t_stop - the package with type 'stop' necessary for stoping playning media files.
*/
enum TypePackage{
t_void = 0x00,
t_play = 0x01,
t_song_h = 0x02,
t_song = 0x04,
t_sync = 0x08,
t_close = 0x10,
t_stop = 0x20,
t_what = 0x40,
t_brodcaster = 0x80
};
/**
* @brief The package class. Package for translite media data on network
*
* parse map:
* 1 byle - type
* 4 byte - size of data of package (it avelable if type is t_sync or t_song)
* data
*/
class package
{
/*parse map */
/*
* 1 byle - type
* 4 byte - size of data of package (it avelable if type is t_sync or t_song)
* data
*/
private:
Type type;
Song source;
SongHeader header;
Syncer playdata;
public:
package();
package(const QByteArray& array);
~package();
/**
* @brief getHeader
* @return Header of the song
*/
const SongHeader& getHeader() const;
/**
* @brief getSong
* @return Song
*/
const Song& getSong() const;
/**
* @brief getPlayTime
* @return time of playning media data
*/
const Syncer &getPlayData() const;
const Type& getType() const;
bool isValid() const;
void clear();
QByteArray parseTo();
bool parseFrom(const QByteArray& array);
friend class Sync;
};
class Node:public QTcpServer{
Q_OBJECT
protected:
QList<ETcpSocket*> clients;
private slots:
void acceptError_(ETcpSocket*);
void newConnection_();
void readData(ETcpSocket*_client);
public:
Node();
void WriteAll(const QByteArray&);
void disconnectClient(ETcpSocket*);
QList<ETcpSocket*>* getClients();
bool addNode(const QString &node, int port = DEDAULT_PORT);
bool addNode(ETcpSocket* node);
~Node();
signals:
void Error(QString);
void Message(ETcpSocket*);
void ClientDisconnected(ETcpSocket*);
void ClientConnected(ETcpSocket*);
};
}
#endif // NODE_H

81
sync/song.cpp Normal file
View File

@ -0,0 +1,81 @@
#include "song.h"
namespace syncLib{
SongHeader::SongHeader()
{
this->id = -1;
this->name = "";
this->size = 0;
}
SongHeader& SongHeader::operator =(const SongHeader& right){
this->id = right.id;
this->name = right.name;
this->size = right.size;
return *this;
}
bool SongHeader::operator ==(const SongHeader& right){
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();
}
Song::Song():
SongHeader()
{
source.clear();
}
QDataStream& operator << (QDataStream& stream, const SongHeader& song){
stream << song.id;
stream << song.name;
stream << song.size;
return stream;
}
QDataStream& operator >> (QDataStream& stream, SongHeader& song){
stream >> song.id;
stream >> song.name;
stream >> song.size;
return stream;
}
Song::Song(const SongHeader& from)
:Song::Song()
{
this->id = from.id;
this->name = from.name;
this->size = from.size;
}
void Song::clear(){
source.clear();
}
Song::~Song(){
source.clear();
}
unsigned int Song::getSize() const{
return SongHeader::getSize() + source.size();
}
QDataStream& operator << (QDataStream& stream,const Song& song){
stream << static_cast<const SongHeader&>(song);
stream << song.source;
return stream;
}
QDataStream& operator >> (QDataStream& stream, Song& song){
stream >> static_cast<SongHeader&>(song);
stream >> song.source;
return stream;
}
}

66
sync/song.h Normal file
View File

@ -0,0 +1,66 @@
#ifndef SONG_H
#define SONG_H
#include <QString>
#include <QByteArray>
#include <QDataStream>
#include <config.h>
/**
* @brief Time_point on nanosecunds (uint64_t)
*/
typedef quint64 milliseconds;
namespace syncLib {
/**
* @brief The Syncer struct
*
*/
struct Syncer
{
/**
* @brief seek - wher is play media file
*/
milliseconds seek;
/**
* @brief run when is play media file (int)
*/
milliseconds run;
};
/**
* @brief The SongHeader class sound header with media information
* (id,size and name)
*/
class SongHeader
{
public:
int id;
QString name;
int size;
SongHeader();
SongHeader& operator = (const SongHeader& right);
bool operator == (const SongHeader& right);
virtual unsigned int getSize()const;
virtual ~SongHeader();
friend QDataStream& operator << (QDataStream& stream, const SongHeader& song);
friend QDataStream& operator >> (QDataStream& stream, SongHeader& song);
};
/**
* @brief The Song class
* into this calss added mediadata of playable media file.
*/
class Song : public SongHeader{
private:
QByteArray source;
public:
Song();
Song(const SongHeader& from);
void clear();
unsigned int getSize() const;
~Song();
friend QDataStream& operator << (QDataStream& stream, const Song& song);
friend QDataStream& operator >> (QDataStream& stream, Song& song);
friend class Sync;
};
}
#endif // SONG_H

350
sync/sync.cpp Normal file
View File

@ -0,0 +1,350 @@
#include "sync.h"
#include <QtSql>
#include <QMultimedia>
#include <QMediaPlayer>
#include <QSqlQuery>
#include "exaptions.h"
#include "time.h"
#include "thread"
#include "config.h"
#ifdef QT_DEBUG
#include <QDebug>
#endif
namespace syncLib{
Sync::Sync(){
node = new Node();
player = new QMediaPlayer(nullptr,QMediaPlayer::LowLatency);
if(!player->isAvailable()){
throw MediaException();
}
initDB();
connect(node,SIGNAL(Message(ETcpSocket*)),SLOT(packageRender(ETcpSocket*)));
connect(&deepScaner,SIGNAL(scaned(QList<ETcpSocket*>*)),SLOT(deepScaned(QList<ETcpSocket*>*)));
}
void Sync::initDB(){
if(db) return;
*db = QSqlDatabase::addDatabase("QSQLITE");
QDir d(QString("./%0").arg(DATABASE_NAME));
db->setDatabaseName(d.absolutePath());
if(db->open()){
qyery = new QSqlQuery(*db);
QString qyer = QString("CREATE TABLE IF NOT EXISTS %0 "
"id int NOT NULL AUTO_INCREMENT,"
"name VARCHAR(100),"
"size INT NOT NULL,"
"data BLOB NOT NULL").arg(DATATABLE_NAME);
qyery->exec(qyer);
}
}
int Sync::save(const Song &song){
QString qyer = QString("INSERT INTO %0 (name, size, data) VALUES"
"(%1,%2, :data)").arg(DATATABLE_NAME,
song.name,
QString::number(song.size));
qyery->prepare(qyer);
qyery->bindValue(":data",song.source);
if(!qyery->exec())
return -1;
if(qyery->exec(QString("SELECT MAAX(id) form %0").arg(DATATABLE_NAME)))
return -1;
return qyery->value(0).toInt();
}
bool Sync::load(const SongHeader &song,Song &result){
result.clear();
if(song.id > -1){
QString qyer = QString("SELECT * from %0 where id=%1").arg(DATATABLE_NAME).arg(song.id);
if(!qyery->exec(qyer)){
return false;
}
}else if(!song.name.isEmpty() && song.size > 0){
QString qyer = QString("SELECT * from %0 where name=%1 and size=%2").arg(DATATABLE_NAME).arg(song.name).arg(song.size);
if(!qyery->exec(qyer)){
return false;
}
}else {
return false;
}
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;
}
/*
* information about chrono
* https://stackoverflow.com/questions/31255486/c-how-do-i-convert-a-stdchronotime-point-to-long-and-back
*/
milliseconds Sync::now(){
auto tim = std::chrono::system_clock::now();
auto mc = std::chrono::time_point_cast<std::chrono::milliseconds>(tim);
auto epoh = mc.time_since_epoch();
#ifdef QT_DEBUG
qDebug() << epoh.count();
#endif
return epoh.count();
}
Clock Sync::from(const milliseconds& mc){
std::chrono::milliseconds dur(mc);
return Clock(dur);
}
bool Sync::play(const SongHeader &header, const Syncer *syncdata){
QString qyer = QString("SELECT * from %0 where name=%1 and size=%2").arg(DATATABLE_NAME).arg(header.name).arg(header.size);
if(!qyery->exec(qyer)){
return false;
}
Song song;
song.id = qyery->value(0).toInt();
song.name = qyery->value(1).toString();
song.size = qyery->value(2).toInt();
song.source = qyery->value(3).toByteArray();
return Sync::play(song,syncdata);
}
bool Sync::play(Song &song, Syncer *syncdata){
QBuffer buffer(&song.source);
player->setMedia(QMediaContent(), &buffer);
if(syncdata && !sync(*syncdata)){
return false;
}
fbroadcaster = !bool(syncdata);
if(fbroadcaster){
package pac;
if(!createPackage(t_song_h | t_sync, pac)){
throw CreatePackageExaption();
}
node->WriteAll(pac.parseTo());
}
player->play();
playList->push_front(static_cast<SongHeader>(song));
return true;
}
bool Sync::play(int id_song, Syncer *syncdata){
QString qyer = QString("SELECT * from %0 where id=%1").arg(DATATABLE_NAME).arg(id_song);
if(!qyery->exec(qyer)){
return false;
}
Song song;
song.id = qyery->value(0).toInt();
song.name = qyery->value(1).toString();
song.size = qyery->value(2).toInt();
song.source = qyery->value(3).toByteArray();
return Sync::play(song,syncdata);
}
bool Sync::play(QString url){
QFile f(url);
if(!f.open(QIODevice::ReadOnly)){
return false;
}
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;
song.id = Sync::save(song);
if(song.id < 0)
return false;
return Sync::play(song);
}
void Sync::pause(){
player->pause();
}
void Sync::stop(){
player->stop();
}
void Sync::jump(const int seek){
player->setPosition(seek);
}
bool Sync::sync(const Syncer &sync){
milliseconds sync_time = sync.run - now();
if(sync_time > MAX_SYNC_TIME && sync_time <= 0)
return false;
Clock run_time = from(sync.run);
do {
std::this_thread::yield();
} while (std::chrono::high_resolution_clock::now() < run_time);
player->setPosition(sync.seek);
return true;
}
bool Sync::createPackage(Type type, package &pac){
pac.clear();
pac.type = type;
if(type & TypePackage::t_sync && fbroadcaster){
pac.playdata.run = now() + SYNC_TIME;
pac.playdata.seek = player->position() + SYNC_TIME;
}
if(type & TypePackage::t_song_h && fbroadcaster){
if(playList->isEmpty())
return false;
pac.header = playList->front();
}
if(type & TypePackage::t_song && fbroadcaster){
if(playList->isEmpty())
return false;
if(!load(playList->front(), pac.source))
return false;
}
if(fbroadcaster)
pac.type = TypePackage(pac.type | t_brodcaster);
return pac.isValid();
}
void Sync::packageRender(ETcpSocket *socket){
QByteArray *array;
while((array = socket->topStack())){
package pkg;
if(!pkg.parseFrom((*array))){
throw BadAnswerExaption();
continue;
}
// package answer;
// scaning servers
if(pkg.getType() & t_brodcaster && servers.indexOf(socket) == -1){
servers.append(socket);
}
if(!(pkg.getType() & t_brodcaster) && servers.indexOf(socket) != -1){
servers.removeOne(socket);
}
if(fbroadcaster == (pkg.getType() & t_brodcaster)){
throw BrodcastConflict();
return;
}
if(pkg.getType() & t_brodcaster){
// if requst from server
if(pkg.getType() & t_play){
player->play();
}
if((pkg.getType() & t_song_h) && !play(pkg.getHeader(), &pkg.getPlayData())){
if((pkg.getType() & t_song) && !play(pkg.getSong(), &pkg.getPlayData())){
package answer;
if(!createPackage(t_song | t_sync, answer)){
throw CreatePackageExaption();
}
socket->Write(answer.parseTo());
}
}
if(pkg.getType() & t_close){
socket->getSource()->close();
node->getClients()->removeOne(socket);
delete socket;
}
if(pkg.getType() & t_what){
package answer;
if(!createPackage(t_void, answer)){
throw CreatePackageExaption();
}
socket->Write(answer.parseTo());
}
if(pkg.getType() & t_sync){
if(playList->empty()){
throw SyncError();
}
}
}else{
package answer;
if(!createPackage(pkg.getType() | ~t_what | ~t_play | ~t_stop | ~t_brodcaster, answer)){
throw CreatePackageExaption();
}
socket->Write(answer.parseTo());
if(pkg.getType() & t_close){
socket->getSource()->close();
node->getClients()->removeOne(socket);
delete socket;
}
}
array->clear();
delete array;
}
}
void Sync::rescan(bool deep){
package pac;
if(!createPackage(t_what,pac)){
throw CreatePackageExaption();
return;
}
node->WriteAll(pac.parseTo());
if(deep){
deepScaner.setInterval(DEEP_SCANER_INTERVAL);
deepScaner.scane();
}
}
void Sync::deepScaned(QList<ETcpSocket *> * list){
package pac;
if(!createPackage(t_what,pac)){
throw CreatePackageExaption();
return;
}
QByteArray array = pac.parseTo();
for(ETcpSocket * i: *list){
i->Write(array);
}
}
Sync::~Sync(){
delete node;
delete db;
delete player;
servers.clear();
}
}

143
sync/sync.h Normal file
View File

@ -0,0 +1,143 @@
#ifndef SYNC_H
#define SYNC_H
#include "song.h"
#include "node.h"
#include "LocalScanner.h"
#include <chrono>
class QSqlDatabase;
class QMediaPlayer;
class QSqlQuery;
namespace syncLib {
typedef std::chrono::time_point<std::chrono::high_resolution_clock> Clock;
class Node;
/**
* @brief The Sync class is main class of this library.
* the 'sync' has supported synced playning media files on network and saving media data into local database.
*/
class Sync : public QObject
{
Q_OBJECT
private:
Node *node;
QSqlDatabase *db;
QMediaPlayer *player;
QList<SongHeader>* playList;
QSqlQuery *qyery;
QList<ETcpSocket*> servers;
bool fbroadcaster;
LocalScanner deepScaner;
/**
* @brief rescan - search for existing servers
* result saved in servers
*/
void rescan(bool deep = false);
/**
* @brief initDB initialize local database of song
*/
void initDB();
/**
* @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);
/**
* @brief save media data into local database.
* @param song savining media data.
* @return id of song saved on local database.
*/
int save(const Song &song);
/**
* @brief fromDataBase return a song from local database by id.
* @param id of song saved in local database.
* @return song drom local database.
*/
Song fromDataBase(const int id);
/**
* @brief now - get now time on microsecunds
* @return - count of microsecunds
*/
milliseconds now();
/**
* @brief from cast to chrono secunds
* @param mcrs microseconds of uint_64
* @return microseconds of chrono
*/
Clock from(const milliseconds &mcrs);
/**
* @brief createPackage - Create a package that shows current state of the node
* @param type - Type of an answer
* @param pac - the resulting value
* @return true if everything's done
*/
bool createPackage(Type type ,package& pac);
private slots:
/**
* @brief packageRender - the handler of all messages received.
* @param socket
*/
void packageRender(ETcpSocket* socket);
/**
* @brief deepScaned scaning in local network
*/
void deepScaned(QList<ETcpSocket *> *);
public:
/**
* @brief Play song in this device, if device has not supported playning media data this method throw MediaExcrption.
* @param header of song
* @param syncdata data of synbced playning of media data.
* @return true if all done else false.
*/
bool play(const SongHeader &header, const Syncer *syncdata = nullptr);
/**
* @brief Play song in this device, if device has not supported playning media data this method throw MediaExcrption.
* @param song playning media data.
* @param syncdata data of synbced playning of media data.
* @return true if all done else false.
*/
bool play(Song &song, Syncer *syncdata = nullptr);
/**
* @brief Play song from local media file.
* @param url of local media file.
* @return true if all done else false.
*/
bool play(QString url);
/**
* @brief Play song from local database by id.
* @param id_song of song.
* @return true if all done else false.
*/
bool play(int id_song, Syncer* syncdata = nullptr);
/**
* @brief Pause playning song.
*/
void pause();
/**
* @brief stop playning song.
*/
void stop();
/**
* @brief jump - jump to new position of playning media data.
* @param seek - a new position of media data.
*/
void jump(const int seek);
/**
* @brief sync with server
* @param sync - data of sync
*/
bool sync(const Syncer& sync);
Sync();
~Sync();
};
}
#endif // SYNC_H

44
sync/sync.pro Normal file
View File

@ -0,0 +1,44 @@
#-------------------------------------------------
#
# Project created by QtCreator 2017-10-28T16:29:39
#
#-------------------------------------------------
QT += network multimedia sql
QT -= gui
TARGET = sync
TEMPLATE = lib
CONFIG += staticlib
# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
sync.cpp \
song.cpp \
node.cpp \
ETcpSocket.cpp \
LocalScanner.cpp
HEADERS += \
sync.h \
song.h \
node.h \
config.h \
exaptions.h \
ETcpSocket.h \
LocalScanner.h
unix {
target.path = /usr/lib
INSTALLS += target
}