mirror of
https://github.com/QuasarApp/SoundBand.git
synced 2025-04-26 15:24:31 +00:00
commit
92c7fa39a2
144
sync/ETcpSocket.cpp
Executable file
144
sync/ETcpSocket.cpp
Executable 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
74
sync/ETcpSocket.h
Executable 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
68
sync/LocalScanner.cpp
Executable 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
29
sync/LocalScanner.h
Executable 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
19
sync/config.h
Normal 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
58
sync/exaptions.h
Normal 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
210
sync/node.cpp
Normal 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
107
sync/node.h
Normal 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
81
sync/song.cpp
Normal 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
66
sync/song.h
Normal 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
350
sync/sync.cpp
Normal 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
143
sync/sync.h
Normal 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
44
sync/sync.pro
Normal 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
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user