From 68bd802bd0a80df684539b03a29f95b786504748 Mon Sep 17 00:00:00 2001 From: Fred Gleason Date: Tue, 2 Apr 2024 17:42:04 -0400 Subject: [PATCH] 2024-04-02 Fred Gleason * Fixed a bug in the PAD subsystem that caused meta-events to be included in the update events. * Refactored the PAD subsystem to use native Qt JSON methods. Signed-off-by: Fred Gleason --- ChangeLog | 4 + apis/pypad/api/pypad.py | 2 +- lib/rdlogplay.cpp | 235 ++++++++++++++++++++-------------------- lib/rdlogplay.h | 8 +- rdpadd/repeater.cpp | 23 +++- rdpadd/repeater.h | 4 +- 6 files changed, 150 insertions(+), 126 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3cf8b75f..dcf2e845 100644 --- a/ChangeLog +++ b/ChangeLog @@ -24684,3 +24684,7 @@ to be emitted when processing a Chain event. 2024-04-02 Fred Gleason * Added an 'eventType' field to the PAD JSON schema. +2024-04-02 Fred Gleason + * Fixed a bug in the PAD subsystem that caused meta-events to be + included in the update events. + * Refactored the PAD subsystem to use native Qt JSON methods. diff --git a/apis/pypad/api/pypad.py b/apis/pypad/api/pypad.py index ea4ccfd6..e942ee58 100644 --- a/apis/pypad/api/pypad.py +++ b/apis/pypad/api/pypad.py @@ -957,7 +957,7 @@ class Receiver(object): if c[0]==10: linebytes=line.decode('utf-8','replace') msg+=linebytes - if linebytes=='\r\n': + if linebytes=='\n': jdata=json.loads(msg) if (not self.__active_now_groups and not self.__active_next_groups) or (jdata['padUpdate'] is not None and jdata['padUpdate']['now'] is not None and jdata['padUpdate']['now']['groupName'] in self.__active_now_groups) or (jdata['padUpdate'] is not None and jdata['padUpdate']['next'] is not None and jdata['padUpdate']['next']['groupName'] in self.__active_next_groups): self.__pypad_Process(Update(jdata,self.__config_parser,rd_config)) diff --git a/lib/rdlogplay.cpp b/lib/rdlogplay.cpp index d3d0c708..1b0cee92 100644 --- a/lib/rdlogplay.cpp +++ b/lib/rdlogplay.cpp @@ -3114,9 +3114,12 @@ void RDLogPlay::SendNowNext() if(nextLine()>=0) { for(int i=nextLine();istatus()==RDLogLine::Scheduled)&&(!logLine(i)->asyncronous())) { - logline[1]=logLine(i); - i=lineCount(); + if((ll->type()==RDLogLine::Cart)||(ll->type()==RDLogLine::Macro)) { + if((ll->status()==RDLogLine::Scheduled)&& + (!logLine(i)->asyncronous())) { + logline[1]=logLine(i); + i=lineCount(); + } } } } @@ -3170,50 +3173,55 @@ void RDLogPlay::SendNowNext() int next_num=1; for(int i=0;i<2;i++) { - play_pad_socket[i]->write(QString("{\r\n").toUtf8()); - play_pad_socket[i]->write(QString(" \"padUpdate\": {\r\n").toUtf8()); - play_pad_socket[i]-> - write(RDJsonField("dateTime",QDateTime::currentDateTime(),8).toUtf8()); - play_pad_socket[i]-> - write(RDJsonField("hostName",rda->station()->name(),8).toUtf8()); - play_pad_socket[i]-> - write(RDJsonField("shortHostName",rda->station()->shortName(),8).toUtf8()); - play_pad_socket[i]->write(RDJsonField("machine",play_id+1,8).toUtf8()); - play_pad_socket[i]->write(RDJsonField("onairFlag",play_onair_flag,8).toUtf8()); - play_pad_socket[i]-> - write(RDJsonField("mode",RDAirPlayConf::logModeText(play_op_mode),8).toUtf8()); + + // + // Header Fields + // + QJsonObject jo0; + jo0.insert("dateTime",QDateTime::currentDateTime().toString(Qt::ISODate)); + jo0.insert("hostName",rda->station()->name()); + jo0.insert("shortHostName",rda->station()->shortName()); + jo0.insert("machine",1+play_id); + jo0.insert("onairFlag",play_onair_flag); + jo0.insert("mode",RDAirPlayConf::logModeText(play_op_mode)); // // Service // + QJsonObject jo1; if(svcname.isEmpty()) { - play_pad_socket[i]->write(RDJsonNullField("service",8).toUtf8()); + jo1.insert("service",QJsonValue()); } else { - RDSvc *svc=new RDSvc(svcname,rda->station(),rda->config(),this); - play_pad_socket[i]->write(QString(" \"service\": {\r\n").toUtf8()); - play_pad_socket[i]->write(RDJsonField("name",svcname,12).toUtf8()); - play_pad_socket[i]-> - write(RDJsonField("description",svc->description(),12).toUtf8()); - play_pad_socket[i]-> - write(RDJsonField("programCode",svc->programCode(),12,true).toUtf8()); - play_pad_socket[i]->write(QString(" },\r\n").toUtf8()); - delete svc; + QString sql=QString("select ")+ + "`DESCRIPTION`,"+ // 00 + "`PROGRAM_CODE` "+ // 01 + "from `SERVICES` where "+ + "`NAME`='"+RDEscapeString(svcname)+"'"; + RDSqlQuery *q=new RDSqlQuery(sql); + if(q->first()) { + jo1.insert("description",q->value(0).toString()); + jo1.insert("programCode",q->value(1).toString()); + } + else { + jo1.insert("description",QJsonValue()); + jo1.insert("programCode",QJsonValue()); + } + delete q; } + jo0.insert("service",jo1); // // Log // - play_pad_socket[i]->write(QString(" \"log\": {\r\n").toUtf8()); - play_pad_socket[i]->write(RDJsonField("name",logName(),12,true).toUtf8()); - play_pad_socket[i]->write(QString(" },\r\n").toUtf8()); + QJsonObject jo2; + jo2.insert("name",logName()); + jo0.insert("log",jo2); // // Now // - play_pad_socket[i]-> - write(GetPadJson("now",logline[0],next_datetime,now_line,8,false). - toUtf8()); + jo0.insert("now",GetPadJson("now",logline[0],next_datetime,now_line)); // // Next @@ -3221,25 +3229,24 @@ void RDLogPlay::SendNowNext() if((mode()==RDAirPlayConf::Auto)&&(logline[0]!=NULL)) { next_datetime=next_datetime.addSecs(logline[0]->forcedLength()/1000); } - play_pad_socket[i]-> - write(GetPadJson("next",logline[1], - next_datetime,nextLine(),8,(i==0)|| - ((1+nextLine())>=lineCount())||(nextLine()<0)).toUtf8()); + jo0.insert("next",GetPadJson("next",logline[1],next_datetime,nextLine())); + // + // Extended Events + // if(nextLine()>=0) { - // - // Extended Events - // if((i>0)&&(nextLine()>=0)) { for(int j=nextLine()+1;jstatus()==RDLogLine::Scheduled)&& - (!logLine(i)->asyncronous())) { - next_datetime=next_datetime.addSecs(ll->forcedLength()/1000); - play_pad_socket[i]-> - write(GetPadJson(QString::asprintf("next%d",next_num++), - ll,next_datetime,j,8,(1+j)==lineCount()). - toUtf8()); + if((ll->type()==RDLogLine::Cart)||(ll->type()==RDLogLine::Macro)) { + if((ll->status()==RDLogLine::Scheduled)&& + (!logLine(i)->asyncronous())) { + next_datetime=next_datetime.addSecs(ll->forcedLength()/1000); + jo0.insert(QString::asprintf("next%d",next_num), + GetPadJson(QString::asprintf("next%d",next_num),ll, + next_datetime,j)); + next_num++; + } } } } @@ -3249,8 +3256,11 @@ void RDLogPlay::SendNowNext() // // Commit the update // - play_pad_socket[i]->write(QString(" }\r\n").toUtf8()); - play_pad_socket[i]->write(QString("}\r\n\r\n").toUtf8()); + QJsonObject jo3; + jo3.insert("padUpdate",jo0); + QJsonDocument jdoc; + jdoc.setObject(jo3); + play_pad_socket[i]->write(jdoc.toJson().trimmed()); } // @@ -3298,83 +3308,70 @@ void RDLogPlay::UpdateRestartData() } -QString RDLogPlay::GetPadJson(const QString &name,RDLogLine *ll, - const QDateTime &start_datetime,int line, - int padding,bool final) const +QJsonValue RDLogPlay::GetPadJson(const QString &name,RDLogLine *ll, + const QDateTime &start_datetime,int line) + const { - QString ret; + QJsonObject jo0; if(ll==NULL) { - ret=RDJsonNullField(name,padding,final); - } - else { - ret+=RDJsonPadding(padding)+"\""+name+"\": {\r\n"; - if(start_datetime.isValid()) { - ret+=RDJsonField("startDateTime",start_datetime,4+padding); - } - else { - ret+=RDJsonNullField("startDateTime",4+padding); - } - ret+=RDJsonField("lineNumber",line,4+padding); - ret+=RDJsonField("lineId",ll->id(),4+padding); - ret+=RDJsonField("eventType",RDLogLine::typeText(ll->type()),4+padding); - if(ll->type()==RDLogLine::Cart) { - ret+=RDJsonField("cartNumber",ll->cartNumber(),4+padding); - ret+=RDJsonField("cartType",RDCart::typeText(ll->cartType()),4+padding); - if(ll->cartType()==RDCart::Audio) { - ret+=RDJsonField("cutNumber",ll->cutNumber(),4+padding); - } - else { - ret+=RDJsonNullField("cutNumber",4+padding); - } - } - else { - ret+=RDJsonNullField("cartNumber",4+padding); - ret+=RDJsonNullField("cartType",4+padding); - ret+=RDJsonNullField("cutNumber",4+padding); - } - if(ll->useEventLength()) { - ret+=RDJsonField("length",ll->eventLength(),4+padding); - } - else { - ret+=RDJsonField("length",ll->forcedLength(),4+padding); - } - if(ll->year().isValid()) { - ret+=RDJsonField("year",ll->year().year(),4+padding); - } - else { - ret+=RDJsonNullField("year",4+padding); - } - ret+=RDJsonField("groupName",ll->groupName(),4+padding); - ret+=RDJsonField("title",ll->title(),4+padding); - ret+=RDJsonField("artist",ll->artist(),4+padding); - ret+=RDJsonField("publisher",ll->publisher(),4+padding); - ret+=RDJsonField("composer",ll->composer(),4+padding); - ret+=RDJsonField("album",ll->album(),4+padding); - ret+=RDJsonField("label",ll->label(),4+padding); - ret+=RDJsonField("client",ll->client(),4+padding); - ret+=RDJsonField("agency",ll->agency(),4+padding); - ret+=RDJsonField("conductor",ll->conductor(),4+padding); - ret+=RDJsonField("userDefined",ll->userDefined(),4+padding); - ret+=RDJsonField("songId",ll->songId(),4+padding); - ret+=RDJsonField("outcue",ll->outcue(),4+padding); - ret+=RDJsonField("description",ll->description(),4+padding); - ret+=RDJsonField("isrc",ll->isrc(),4+padding); - ret+=RDJsonField("isci",ll->isci(),4+padding); - ret+=RDJsonField("recordingMbId",ll->recordingMbId(),4+padding); - ret+=RDJsonField("releaseMbId",ll->releaseMbId(),4+padding); - ret+=RDJsonField("externalEventId",ll->extEventId(),4+padding); - ret+=RDJsonField("externalData",ll->extData(),4+padding); - ret+=RDJsonField("externalAnncType",ll->extAnncType(),4+padding,true); - if(final) { - ret+=RDJsonPadding(padding)+"}\r\n"; - } - else { - ret+=RDJsonPadding(padding)+"},\r\n"; - } + return QJsonValue(); } - return ret; + jo0.insert("startDateTime",start_datetime.toString(Qt::ISODate)); + jo0.insert("lineNumber",line); + jo0.insert("lineId",ll->id()); + jo0.insert("eventType",RDLogLine::typeText(ll->type())); + if((ll->type()==RDLogLine::Cart)||(ll->type()==RDLogLine::Macro)) { + jo0.insert("cartNumber",(int)ll->cartNumber()); + jo0.insert("cartType",RDCart::typeText(ll->cartType())); + if(ll->cartType()==RDCart::Audio) { + jo0.insert("cutNumber",ll->cutNumber()); + } + else { + jo0.insert("cutNumber",QJsonValue()); + } + } + else { + jo0.insert("cartNumber",QJsonValue()); + jo0.insert("cartType",QJsonValue()); + jo0.insert("cutNumber",QJsonValue()); + } + if(ll->useEventLength()) { + jo0.insert("length",ll->eventLength()); + } + else { + jo0.insert("length",(qint64)ll->forcedLength()); + } + if(ll->year().isValid()) { + jo0.insert("year",ll->year().year()); + } + else { + jo0.insert("year",QJsonValue()); + } + jo0.insert("groupName",ll->groupName()); + jo0.insert("title",ll->title()); + jo0.insert("artist",ll->artist()); + jo0.insert("publisher",ll->publisher()); + jo0.insert("composer",ll->composer()); + jo0.insert("album",ll->album()); + jo0.insert("label",ll->label()); + jo0.insert("client",ll->client()); + jo0.insert("agency",ll->agency()); + jo0.insert("conductor",ll->conductor()); + jo0.insert("userDefined",ll->userDefined()); + jo0.insert("songId",ll->songId()); + jo0.insert("outcue",ll->outcue()); + jo0.insert("description",ll->description()); + jo0.insert("isrc",ll->isrc()); + jo0.insert("isci",ll->isci()); + jo0.insert("recordingMbId",ll->recordingMbId()); + jo0.insert("releaseMbId",ll->releaseMbId()); + jo0.insert("externalEventId",ll->extEventId()); + jo0.insert("externalData",ll->extData()); + jo0.insert("externalAnncType",ll->extAnncType()); + + return jo0; } diff --git a/lib/rdlogplay.h b/lib/rdlogplay.h index 73ec04ae..2419e74c 100644 --- a/lib/rdlogplay.h +++ b/lib/rdlogplay.h @@ -22,6 +22,9 @@ #define RDLOGPLAY_H #include +#include +#include +#include #include #include #include @@ -198,9 +201,8 @@ class RDLogPlay : public RDLogModel bool ClearBlock(int start_line); void SendNowNext(); void UpdateRestartData(); - QString GetPadJson(const QString &name,RDLogLine *ll, - const QDateTime &start_datetime,int line,int padding, - bool final=false) const; + QJsonValue GetPadJson(const QString &name,RDLogLine *ll, + const QDateTime &start_datetime,int line) const; void LogTraffic(RDLogLine *logline,RDLogLine::PlaySource src, RDAirPlayConf::TrafficAction action,bool onair_flag) const; void DumpToSyslog(int prio_lvl,const QString &hdr) const; diff --git a/rdpadd/repeater.cpp b/rdpadd/repeater.cpp index b9450aff..e43705dc 100644 --- a/rdpadd/repeater.cpp +++ b/rdpadd/repeater.cpp @@ -30,6 +30,8 @@ MetadataSource::MetadataSource(QTcpSocket *sock) { meta_socket=sock; + meta_curly_count=0; + meta_quoted=false; meta_committed=true; } @@ -46,7 +48,24 @@ bool MetadataSource::appendBuffer(const QByteArray &data) meta_buffer.clear(); } meta_buffer+=data; - meta_committed=meta_buffer.endsWith("\r\n\r\n"); + + for(int i=0;i::const_iterator it=pad_sources.begin(); it!=pad_sources.end();it++) { - if(it.value()->isCommitted()) { + if(it.value()->isCommitted()&&(!it.value()->buffer().trimmed().isEmpty())) { pad_client_sockets.value(id)->write(it.value()->buffer()); } } diff --git a/rdpadd/repeater.h b/rdpadd/repeater.h index 71040cc3..daf2a899 100644 --- a/rdpadd/repeater.h +++ b/rdpadd/repeater.h @@ -2,7 +2,7 @@ // // Rivendell PAD Data Repeater // -// (C) Copyright 2018 Fred Gleason +// (C) Copyright 2018-2024 Fred Gleason // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as @@ -42,6 +42,8 @@ class MetadataSource private: QByteArray meta_buffer; + int meta_curly_count; + bool meta_quoted; bool meta_committed; QTcpSocket *meta_socket; };