From 887d297a19ad21b9aedc64fc41521626681bce55 Mon Sep 17 00:00:00 2001 From: Fred Gleason Date: Mon, 5 Feb 2024 17:49:26 -0500 Subject: [PATCH] 2024-02-05 Fred Gleason * Added a 'REPLICATORS.PROGRAM_CODE' field to the database. * Incremented the database version to 372. * Added 'RDReplicator::programCode()' and 'RDReplicator::setProgramCode()' methods. * Added a 'Program Code' control to the 'Rivendell Replicators' dialog in rdadmin(1). * Renamed the 'Citadel X-Digital Portal' replicator to 'X-Digital National ISCI Model'. * Added a 'X-Digital Cue Model' replicator. Signed-off-by: Fred Gleason --- ChangeLog | 10 + docs/tables/replicators.txt | 1 + lib/dbversion.h | 2 +- lib/rdreplicator.cpp | 18 +- lib/rdreplicator.h | 4 +- rdadmin/edit_replicator.cpp | 51 ++-- rdadmin/edit_replicator.h | 15 +- rdrepld/Makefile.am | 5 +- rdrepld/rdrepld.cpp | 11 +- rdrepld/replconfig.cpp | 13 + rdrepld/replconfig.h | 3 + rdrepld/xdscue.cpp | 458 +++++++++++++++++++++++++++++++++ rdrepld/xdscue.h | 44 ++++ utils/rddbmgr/revertschema.cpp | 12 +- utils/rddbmgr/schemamap.cpp | 1 + utils/rddbmgr/updateschema.cpp | 17 ++ 16 files changed, 631 insertions(+), 34 deletions(-) create mode 100644 rdrepld/xdscue.cpp create mode 100644 rdrepld/xdscue.h diff --git a/ChangeLog b/ChangeLog index e784a1eb..d8eb5b73 100644 --- a/ChangeLog +++ b/ChangeLog @@ -24616,3 +24616,13 @@ * Incremented the package version to 4.1.2int0. 2024-01-24 Fred Gleason * Incremented the package version to 4.1.3. +2024-02-05 Fred Gleason + * Added a 'REPLICATORS.PROGRAM_CODE' field to the database. + * Incremented the database version to 372. + * Added 'RDReplicator::programCode()' and + 'RDReplicator::setProgramCode()' methods. + * Added a 'Program Code' control to the 'Rivendell Replicators' + dialog in rdadmin(1). + * Renamed the 'Citadel X-Digital Portal' replicator to + 'X-Digital National ISCI Model'. + * Added a 'X-Digital Cue Model' replicator. diff --git a/docs/tables/replicators.txt b/docs/tables/replicators.txt index cd22f9b7..6f31bae9 100644 --- a/docs/tables/replicators.txt +++ b/docs/tables/replicators.txt @@ -9,6 +9,7 @@ NAME varchar(32) not null, primary key DESCRIPTION varchar(64) TYPE_ID int(10) unsigned not null STATION_NAME varchar(64) from STATIONS.NAME +PROGRAM_CODE varchar(191) FORMAT int(10) unsigned CHANNELS int(10) unsigned SAMPRATE int(10) unsigned diff --git a/lib/dbversion.h b/lib/dbversion.h index fff661dd..6610082f 100644 --- a/lib/dbversion.h +++ b/lib/dbversion.h @@ -24,7 +24,7 @@ /* * Current Database Version */ -#define RD_VERSION_DATABASE 371 +#define RD_VERSION_DATABASE 372 #endif // DBVERSION_H diff --git a/lib/rdreplicator.cpp b/lib/rdreplicator.cpp index 41ef3a94..e6af6c49 100644 --- a/lib/rdreplicator.cpp +++ b/lib/rdreplicator.cpp @@ -59,6 +59,18 @@ void RDReplicator::setStationName(const QString &str) } +QString RDReplicator::programCode() const +{ + return GetValue("PROGRAM_CODE").toString(); +} + + +void RDReplicator::setProgramCode(const QString &str) +{ + SetRow("PROGRAM_CODE",str); +} + + QString RDReplicator::description() const { return GetValue("DESCRIPTION").toString(); @@ -202,13 +214,17 @@ QString RDReplicator::typeString(RDReplicator::Type type) QString ret="Unknown type"; switch(type) { case RDReplicator::TypeCitadelXds: - ret="Citadel X-Digital Portal"; + ret="X-Digital National ISCI Model"; break; case RDReplicator::TypeWw1Ipump: ret="Westwood One Wegener Portal"; break; + case RDReplicator::TypeXdsCue: + ret="X-Digital Cue Model"; + break; + case RDReplicator::TypeLast: break; } diff --git a/lib/rdreplicator.h b/lib/rdreplicator.h index 23d80a3f..6554d527 100644 --- a/lib/rdreplicator.h +++ b/lib/rdreplicator.h @@ -28,13 +28,15 @@ class RDReplicator { public: - enum Type {TypeCitadelXds=0,TypeWw1Ipump=1,TypeLast=2}; + enum Type {TypeCitadelXds=0,TypeWw1Ipump=1,TypeXdsCue=2,TypeLast=3}; RDReplicator(const QString &name); QString name() const; RDReplicator::Type type() const; void setType(RDReplicator::Type type) const; QString stationName() const; void setStationName(const QString &str); + QString programCode() const; + void setProgramCode(const QString &str); QString description() const; void setDescription(const QString &str) const; RDSettings::Format format() const; diff --git a/rdadmin/edit_replicator.cpp b/rdadmin/edit_replicator.cpp index d5251929..085f66bd 100644 --- a/rdadmin/edit_replicator.cpp +++ b/rdadmin/edit_replicator.cpp @@ -69,11 +69,22 @@ EditReplicator::EditReplicator(const QString &repl_name,QWidget *parent) repl_description_label->setGeometry(10,33,90,19); repl_description_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + // + // Replicator Program Code + // + repl_program_code_edit=new QLineEdit(this); + repl_program_code_edit->setGeometry(105,55,sizeHint().width()-115,19); + repl_program_code_edit->setMaxLength(191); + QLabel *repl_program_code_label=new QLabel(tr("Program Code")+":",this); + repl_program_code_label->setFont(labelFont()); + repl_program_code_label->setGeometry(0,55,100,19); + repl_program_code_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); + // // Replicator Type // repl_type_box=new QComboBox(this); - repl_type_box->setGeometry(105,55,sizeHint().width()-115,19); + repl_type_box->setGeometry(105,77,sizeHint().width()-115,19); for(unsigned i=0;i<(int)RDReplicator::TypeLast;i++) { repl_type_box->insertItem(repl_type_box->count(), RDReplicator::typeString((RDReplicator::Type)i)); @@ -83,14 +94,14 @@ EditReplicator::EditReplicator(const QString &repl_name,QWidget *parent) } QLabel *repl_type_label=new QLabel(tr("Type:"),this); repl_type_label->setFont(labelFont()); - repl_type_label->setGeometry(10,55,90,19); + repl_type_label->setGeometry(10,77,90,19); repl_type_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Host System // repl_station_box=new QComboBox(this); - repl_station_box->setGeometry(155,77,sizeHint().width()-165,19); + repl_station_box->setGeometry(155,99,sizeHint().width()-165,19); repl_station_model=new RDStationListModel(false,"",this); repl_station_model->setFont(defaultFont()); repl_station_model->setPalette(palette()); @@ -98,55 +109,55 @@ EditReplicator::EditReplicator(const QString &repl_name,QWidget *parent) repl_station_box->setCurrentText(repl_replicator->stationName()); QLabel *repl_station_label=new QLabel(tr("Host System:"),this); repl_station_label->setFont(labelFont()); - repl_station_label->setGeometry(10,77,140,19); + repl_station_label->setGeometry(10,99,140,19); repl_station_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Upload Audio URL // repl_url_edit=new QLineEdit(this); - repl_url_edit->setGeometry(155,99,335,19); + repl_url_edit->setGeometry(155,121,335,19); repl_url_edit->setMaxLength(255); repl_url_label=new QLabel(tr("Audio Upload URL:"),this); repl_url_label->setFont(labelFont()); - repl_url_label->setGeometry(20,99,130,19); + repl_url_label->setGeometry(20,121,130,19); repl_url_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Upload Username // repl_username_edit=new QLineEdit(this); - repl_username_edit->setGeometry(225,121,95,19); + repl_username_edit->setGeometry(225,143,95,19); repl_username_edit->setMaxLength(64); repl_username_label=new QLabel(tr("Username:"),this); repl_username_label->setFont(labelFont()); - repl_username_label->setGeometry(40,121,180,19); + repl_username_label->setGeometry(40,143,180,19); repl_username_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Upload Password // repl_password_edit=new QLineEdit(this); - repl_password_edit->setGeometry(395,121,95,19); + repl_password_edit->setGeometry(395,143,95,19); repl_password_edit->setMaxLength(64); repl_password_edit->setEchoMode(QLineEdit::Password); repl_password_label=new QLabel(tr("Password:"),this); repl_password_label->setFont(labelFont()); - repl_password_label->setGeometry(320,121,70,19); + repl_password_label->setGeometry(320,143,70,19); repl_password_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); // // Audio Format // repl_format_edit=new QLineEdit(this); - repl_format_edit->setGeometry(155,143,285,20); + repl_format_edit->setGeometry(155,165,285,20); repl_format_edit->setReadOnly(true); repl_format_label=new QLabel(tr("Upload Format:"),this); repl_format_label->setFont(labelFont()); - repl_format_label->setGeometry(5,143,145,20); + repl_format_label->setGeometry(5,165,145,20); repl_format_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); repl_format_button=new QPushButton(this); - repl_format_button->setGeometry(450,142,40,24); + repl_format_button->setGeometry(450,164,40,24); repl_format_button->setFont(subButtonFont()); repl_format_button->setText(tr("Set")); connect(repl_format_button,SIGNAL(clicked()),this,SLOT(setFormatData())); @@ -155,11 +166,11 @@ EditReplicator::EditReplicator(const QString &repl_name,QWidget *parent) // Normalize Check Box // repl_normalize_box=new QCheckBox(this); - repl_normalize_box->setGeometry(155,167,15,15); + repl_normalize_box->setGeometry(155,189,15,15); repl_normalize_box->setChecked(true); repl_normalize_check_label=new QLabel(tr("Normalize"),this); repl_normalize_check_label->setFont(labelFont()); - repl_normalize_check_label->setGeometry(175,165,83,20); + repl_normalize_check_label->setGeometry(175,187,83,20); repl_normalize_check_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); connect(repl_normalize_box,SIGNAL(toggled(bool)), this,SLOT(normalizeCheckData(bool))); @@ -168,22 +179,22 @@ EditReplicator::EditReplicator(const QString &repl_name,QWidget *parent) // Normalize Level // repl_normalize_spin=new QSpinBox(this); - repl_normalize_spin->setGeometry(295,165,40,20); + repl_normalize_spin->setGeometry(295,187,40,20); repl_normalize_spin->setRange(-30,-1); repl_normalize_label=new QLabel(tr("Level:"),this); repl_normalize_label->setFont(labelFont()); - repl_normalize_label->setGeometry(245,165,45,20); + repl_normalize_label->setGeometry(245,187,45,20); repl_normalize_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); repl_normalize_unit_label=new QLabel(tr("dBFS"),this); repl_normalize_unit_label->setFont(labelFont()); - repl_normalize_unit_label->setGeometry(340,165,40,20); + repl_normalize_unit_label->setGeometry(340,187,40,20); repl_normalize_unit_label->setAlignment(Qt::AlignLeft|Qt::AlignVCenter); // // Groups Selector // repl_groups_sel=new RDListSelector(this); - repl_groups_sel->setGeometry(60,192,380,130); + repl_groups_sel->setGeometry(60,214,380,130); repl_groups_sel->sourceSetLabel(tr("Available Groups")); repl_groups_sel->destSetLabel(tr("Active Groups")); @@ -212,6 +223,7 @@ EditReplicator::EditReplicator(const QString &repl_name,QWidget *parent) // repl_name_edit->setText(repl_replicator->name()); repl_description_edit->setText(repl_replicator->description()); + repl_program_code_edit->setText(repl_replicator->programCode()); repl_url_edit->setText(repl_replicator->url()); repl_username_edit->setText(repl_replicator->urlUsername()); repl_password_edit->setText(repl_replicator->urlPassword()); @@ -290,6 +302,7 @@ void EditReplicator::okData() RDSqlQuery *q; repl_replicator->setDescription(repl_description_edit->text()); + repl_replicator->setProgramCode(repl_program_code_edit->text()); repl_replicator->setType((RDReplicator::Type)repl_type_box->currentIndex()); repl_replicator->setStationName(repl_station_box->currentText()); repl_replicator->setUrl(repl_url_edit->text()); diff --git a/rdadmin/edit_replicator.h b/rdadmin/edit_replicator.h index 79d0ba89..9822e932 100644 --- a/rdadmin/edit_replicator.h +++ b/rdadmin/edit_replicator.h @@ -2,7 +2,7 @@ // // Edit a Rivendell Replicator // -// (C) Copyright 2002-2019 Fred Gleason +// (C) Copyright 2002-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 @@ -21,12 +21,12 @@ #ifndef EDIT_REPLICATOR_H #define EDIT_REPLICATOR_H -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include #include @@ -54,6 +54,7 @@ class EditReplicator : public RDDialog RDReplicator *repl_replicator; QLineEdit *repl_name_edit; QLineEdit *repl_description_edit; + QLineEdit *repl_program_code_edit; QComboBox *repl_type_box; QComboBox *repl_station_box; RDStationListModel *repl_station_model; diff --git a/rdrepld/Makefile.am b/rdrepld/Makefile.am index b62353ff..88caa61b 100644 --- a/rdrepld/Makefile.am +++ b/rdrepld/Makefile.am @@ -1,6 +1,6 @@ ## Makefile.am ## -## (C) Copyright 2010-2023 Fred Gleason +## (C) Copyright 2010-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 @@ -30,7 +30,8 @@ dist_rdrepld_SOURCES = rdrepld.cpp rdrepld.h \ replconfig.cpp replconfig.h\ replfactory.cpp replfactory.h\ citadelxds.cpp citadelxds.h\ - ww1ipump.cpp ww1ipump.h + ww1ipump.cpp ww1ipump.h\ + xdscue.cpp xdscue.h\ nodist_rdrepld_SOURCES = moc_rdrepld.cpp rdrepld_LDADD = @LIB_RDLIBS@ @LIBVORBIS@ @QT5_LIBS@ @MUSICBRAINZ_LIBS@ @IMAGEMAGICK_LIBS@ diff --git a/rdrepld/rdrepld.cpp b/rdrepld/rdrepld.cpp index 58fffc5b..42baf89f 100644 --- a/rdrepld/rdrepld.cpp +++ b/rdrepld/rdrepld.cpp @@ -2,7 +2,7 @@ // // The Rivendell Replicator Daemon // -// (C) Copyright 2010-2023 Fred Gleason +// (C) Copyright 2010-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 @@ -37,6 +37,7 @@ #include "citadelxds.h" #include "rdrepld.h" #include "ww1ipump.h" +#include "xdscue.h" void SigHandler(int signum) { @@ -216,7 +217,8 @@ void MainObject::LoadReplicators() "`URL_USERNAME`,"+ // 08 "`URL_PASSWORD`,"+ // 09 "`ENABLE_METADATA`,"+ // 10 - "`NORMALIZATION_LEVEL` "+ // 11 + "`NORMALIZATION_LEVEL`,"+ // 11 + "`PROGRAM_CODE` "+ // 12 "from `REPLICATORS` where "+ "`STATION_NAME`='"+RDEscapeString(rda->config()->stationName())+"'"; q=new RDSqlQuery(sql); @@ -235,6 +237,7 @@ void MainObject::LoadReplicators() setUrlPassword(QByteArray::fromBase64(q->value(9).toString().toUtf8())); config->setEnableMetadata(RDBool(q->value(10).toString())); config->setNormalizeLevel(q->value(11).toInt()); + config->setProgramCode(q->value(12).toString()); switch(config->type()) { case RDReplicator::TypeCitadelXds: repl_replicators.push_back(new CitadelXds(config)); @@ -244,6 +247,10 @@ void MainObject::LoadReplicators() repl_replicators.push_back(new Ww1Ipump(config)); break; + case RDReplicator::TypeXdsCue: + repl_replicators.push_back(new XdsCue(config)); + break; + case RDReplicator::TypeLast: break; } diff --git a/rdrepld/replconfig.cpp b/rdrepld/replconfig.cpp index 432a7c95..c8a706d2 100644 --- a/rdrepld/replconfig.cpp +++ b/rdrepld/replconfig.cpp @@ -64,6 +64,18 @@ void ReplConfig::setStationName(const QString &str) } +QString ReplConfig::programCode() const +{ + return repl_program_code; +} + + +void ReplConfig::setProgramCode(const QString &str) +{ + repl_program_code=str; +} + + QString ReplConfig::description() const { return repl_description; @@ -200,6 +212,7 @@ void ReplConfig::clear() { repl_name=""; repl_station_name=""; + repl_program_code=""; repl_description=""; repl_format=RDSettings::Pcm16; repl_channels=2; diff --git a/rdrepld/replconfig.h b/rdrepld/replconfig.h index 09c904f4..95d9b424 100644 --- a/rdrepld/replconfig.h +++ b/rdrepld/replconfig.h @@ -36,6 +36,8 @@ class ReplConfig void setName(const QString &str); QString stationName() const; void setStationName(const QString &str); + QString programCode() const; + void setProgramCode(const QString &str); QString description() const; void setDescription(const QString &str); RDSettings::Format format() const; @@ -64,6 +66,7 @@ class ReplConfig QString repl_name; RDReplicator::Type repl_type; QString repl_station_name; + QString repl_program_code; QString repl_description; RDSettings::Format repl_format; unsigned repl_channels; diff --git a/rdrepld/xdscue.cpp b/rdrepld/xdscue.cpp new file mode 100644 index 00000000..c0a702e5 --- /dev/null +++ b/rdrepld/xdscue.cpp @@ -0,0 +1,458 @@ +// xdscue.cpp +// +// Replicator implementation for X-Digital Cue Model Copy-splitting +// +// (C) Copyright 2010-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 +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xdscue.h" + +#define RD_MAX_CART_NUMBER 999999 + +XdsCue::XdsCue(ReplConfig *repl_config) + : ReplFactory(repl_config) +{ + QString sql; + RDSqlQuery *q; + + sql="select `LAST_ISCI_XREFERENCE` from `VERSION`"; + q=new RDSqlQuery(sql); + if(q->first()) { + xds_isci_datetime=q->value(0).toDateTime(); + } + delete q; +} + + +void XdsCue::startProcess() +{ + CheckIsciXreference(); + CheckCarts(); +} + + +bool XdsCue::processCart(const unsigned cartnum) +{ + QString sql; + RDSqlQuery *q; + bool ret=false; + + sql=QString("select ")+ + "`FILENAME` "+ // 00 + "from `ISCI_XREFERENCE` where "+ + QString::asprintf("(`CART_NUMBER`=%u)&&",cartnum)+ + "(`LATEST_DATE`>=now())&&"+ + "(`FILENAME` like '"+RDEscapeString(config()->programCode()+"_")+"%')"; + q=new RDSqlQuery(sql); + if(q->first()) { + ret=PostCut(RDCut::cutName(cartnum,1),q->value(0).toString()); + } + delete q; + + return ret; +} + + +void XdsCue::CheckIsciXreference() +{ + QString sql; + + QFileInfo *fi=new QFileInfo(rda->system()->isciXreferencePath()); + if(fi->exists()) { + if(fi->lastModified()>xds_isci_datetime) { + if(LoadIsciXreference(rda->system()->isciXreferencePath())) { + sql="update `VERSION` set `LAST_ISCI_XREFERENCE`=now()"; + RDSqlQuery::apply(sql); + xds_isci_datetime=QDateTime(QDate::currentDate(),QTime::currentTime()); + PurgeCuts(); + } + } + } + else { + rda->syslog(LOG_WARNING,"unable to load ISCI cross reference file \"%s\"", + (const char *)rda->system()->isciXreferencePath().toUtf8()); + } + delete fi; +} + + +bool XdsCue::LoadIsciXreference(const QString &filename) +{ + FILE *f=NULL; + char line[1024]; + QString sql; + RDSqlQuery *q; + RDStringList fields; + unsigned cartnum; + QStringList datelist; + QDate date; + bool ok=false; + unsigned linenum=3; + + if((f=fopen(filename.toUtf8(),"r"))==NULL) { + rda->syslog(LOG_WARNING, + "unable to load ISCI cross reference file \"%s\" [%s]", + (const char *)rda->system()->isciXreferencePath().toUtf8(), + strerror(errno)); + return false; + } + + // + // Purge Old Data + // + sql="delete from `ISCI_XREFERENCE`"; + q=new RDSqlQuery(sql); + delete q; + + // + // Skip Header + // + if(fgets(line,1024,f)==NULL) { + rda->syslog(LOG_WARNING,"fgets() error reading ISCI xreference data"); + } + if(fgets(line,1024,f)==NULL) { + rda->syslog(LOG_WARNING,"fgets() error reading ISCI xreference data"); + } + + // + // Load Records + // + while(fgets(line,1024,f)!=NULL) { + fields=fields.split(',',line,"\""); + if(fields.size()==9) { + for(int i=0;isyslog(LOG_WARNING,"invalid date in line %d of \"%s\"", + linenum,(const char *)filename.toUtf8()); + } + } + else { + rda->syslog(LOG_WARNING, + "invalid FILENAME field \"%s\" in line %d of \"%s\"", + (const char *)fields[8].toUtf8(),linenum, + (const char *)filename.toUtf8()); + } + } + else { + rda->syslog(LOG_WARNING,"invalid date in line %d of \"%s\"", + linenum,(const char *)filename.toUtf8()); + } + } + else { + rda->syslog(LOG_DEBUG, + "missing/invalid cart number in line %d of \"%s\"", + linenum,(const char *)filename.toUtf8()); + } + } + else { + rda->syslog(LOG_WARNING,"line %d malformed in \"%s\"", + linenum,(const char *)filename.toUtf8()); + } + linenum++; + } + + // + // Clean Up + // + rda->syslog(LOG_INFO,"loaded ISCI cross reference file \"%s\"", + (const char *)rda->system()->isciXreferencePath().toUtf8()); + fclose(f); + return true; +} + + +bool XdsCue::ValidateFilename(const QString &filename) +{ + bool ret=true; + + // + // List of illegal characters taken from 'Illegal Characters4.doc' + // from Citadel + // + ret=ret&&(filename.indexOf(" ")<0); + ret=ret&&(filename.indexOf("\"")<0); + ret=ret&&(filename.indexOf("%")<0); + ret=ret&&(filename.indexOf("*")<0); + ret=ret&&(filename.indexOf("+")<0); + ret=ret&&(filename.indexOf("/")<0); + ret=ret&&(filename.indexOf(":")<0); + ret=ret&&(filename.indexOf(";")<0); + ret=ret&&(filename.indexOf("<")<0); + ret=ret&&(filename.indexOf("=")<0); + ret=ret&&(filename.indexOf(">")<0); + ret=ret&&(filename.indexOf("?")<0); + ret=ret&&(filename.indexOf("@")<0); + ret=ret&&(filename.indexOf("[")<0); + ret=ret&&(filename.indexOf("\\")<0); + ret=ret&&(filename.indexOf("]")<0); + ret=ret&&(filename.indexOf("^")<0); + ret=ret&&(filename.indexOf("{")<0); + ret=ret&&(filename.indexOf("|")<0); + ret=ret&&(filename.indexOf("}")<0); + + return ret; +} + + +void XdsCue::CheckCarts() +{ + QString sql; + RDSqlQuery *q; + RDSqlQuery *q1; + RDSqlQuery *q2; + QString now=QDateTime(QDate::currentDate(),QTime::currentTime()).addDays(-6). + toString("yyyy-MM-dd hh:mm:ss"); + + // + // Generate Update List + // + sql=QString("select ")+ + "`CART_NUMBER`,"+ // 00 + "`FILENAME` "+ // 01 + "from `ISCI_XREFERENCE` where "+ + "(`LATEST_DATE`>=now())&&"+ + "(`FILENAME` like '"+RDEscapeString(config()->programCode()+"_")+"%')"; + q=new RDSqlQuery(sql); + while(q->next()) { + sql=QString("select `REPL_CART_STATE`.`ID` from ")+ + "`REPL_CART_STATE` left join `CUTS` "+ + "on `REPL_CART_STATE`.`CART_NUMBER`=`CUTS`.`CART_NUMBER` where "+ + "(`CUTS`.`ORIGIN_DATETIME`<`REPL_CART_STATE`.`ITEM_DATETIME`)&&"+ + "(`REPL_CART_STATE`.`REPLICATOR_NAME`='"+ + RDEscapeString(config()->name())+"')&&"+ + QString::asprintf("(`REPL_CART_STATE`.`CART_NUMBER`=%u)&&", + q->value(0).toUInt())+ + "(`REPL_CART_STATE`.`POSTED_FILENAME`='"+ + RDEscapeString(q->value(1).toString())+"')&&"+ + "(`REPL_CART_STATE`.`ITEM_DATETIME`>'"+RDEscapeString(now)+"')&&"+ + "(`REPL_CART_STATE`.`REPOST`='N')"; + q1=new RDSqlQuery(sql); + if(!q1->first()) { + if(PostCut(RDCut::cutName(q->value(0).toUInt(),1), + q->value(1).toString())) { + sql=QString("select `ID` from `REPL_CART_STATE` where ")+ + "(`REPLICATOR_NAME`='"+RDEscapeString(config()->name())+"')&&"+ + QString::asprintf("(`CART_NUMBER`=%u)&&",q->value(0).toUInt())+ + "(`POSTED_FILENAME`='"+RDEscapeString(q->value(1).toString())+"')"; + q2=new RDSqlQuery(sql); + if(q2->first()) { + sql=QString("update `REPL_CART_STATE` set ")+ + "`ITEM_DATETIME`=now(),"+ + "`REPOST`='N' where "+ + "(`REPLICATOR_NAME`='"+RDEscapeString(config()->name())+"')&&"+ + QString::asprintf("(`CART_NUMBER`=%u)&&",q->value(0).toUInt())+ + "(`POSTED_FILENAME`='"+RDEscapeString(q->value(1).toString())+"')"; + } + else { + sql=QString("insert into `REPL_CART_STATE` set ")+ + "`ITEM_DATETIME`=now(),"+ + "`REPOST`='N',"+ + "`REPLICATOR_NAME`='"+RDEscapeString(config()->name())+"',"+ + QString::asprintf("`CART_NUMBER`=%u,",q->value(0).toUInt())+ + "`POSTED_FILENAME`='"+RDEscapeString(q->value(1).toString())+"'"; + } + delete q2; + RDSqlQuery::apply(sql); + } + } + delete q1; + } + delete q; +} + + +bool XdsCue::PostCut(const QString &cutname,const QString &filename) +{ + // + // Export File + // + RDAudioConvert::ErrorCode conv_err; + RDUpload::ErrorCode upload_err; + float speed_ratio=1.0; + RDCut *cut=new RDCut(cutname); + if(!cut->exists()) { + delete cut; + return false; + } + if(cut->length()==0) { + delete cut; + return true; + } + RDCart *cart=new RDCart(cut->cartNumber()); + if(cart->enforceLength()) { + speed_ratio=(float)cut->length()/(float)cart->forcedLength(); + } + RDSettings *settings=new RDSettings(); + QString tempfile=RDTempDirectory::basePath()+"/"+filename; + RDAudioConvert *conv=new RDAudioConvert(); + conv->setSourceFile(RDCut::pathName(cutname)); + conv->setDestinationFile(tempfile); + conv->setRange(cut->startPoint(),cut->endPoint()); + conv->setSpeedRatio(speed_ratio); + settings->setFormat(config()->format()); + settings->setChannels(config()->channels()); + settings->setSampleRate(config()->sampleRate()); + settings->setBitRate(config()->bitRate()); + settings->setQuality(config()->quality()); + settings->setNormalizationLevel(config()->normalizeLevel()/1000); + conv->setDestinationSettings(settings); + delete cart; + delete cut; + switch(conv_err=conv->convert()) { + case RDAudioConvert::ErrorOk: + break; + + default: + rda->syslog(LOG_WARNING, + "XdsCue: audio conversion failed: %s, cutname: %s", + (const char *)RDAudioConvert::errorText(conv_err).toUtf8(), + (const char *)cutname.toUtf8()); + delete conv; + delete settings; + return false; + } + delete conv; + delete settings; + + // + // Upload File + // + QString err_msg; + RDUpload *upload=new RDUpload(rda->config()); + upload->setSourceFile(tempfile); + upload->setDestinationUrl(config()->url()+"/"+filename); + // + // FIXME: Finish implementing ssh(1) id keys! + // + switch(upload_err=upload->runUpload(config()->urlUsername(), + config()->urlPassword(),"",false,&err_msg, + rda->config()->logXloadDebugData())) { + case RDUpload::ErrorOk: + break; + + default: + rda->syslog(LOG_WARNING,"XdsCue: audio upload failed: %s", + err_msg.toUtf8().constData()); + unlink(tempfile.toUtf8()); + delete upload; + return false; + } + unlink(tempfile.toUtf8()); + delete upload; + rda->syslog(LOG_INFO,"XdsCue: uploaded cut %s to %s/%s", + cutname.toUtf8().constData(), + config()->url().toUtf8().constData(), + filename.toUtf8().constData()); + + return true; +} + + +void XdsCue::PurgeCuts() +{ + QString sql; + RDSqlQuery *q; + RDSqlQuery *q1; + RDSqlQuery *q2; + RDDelete *conv; + RDDelete::ErrorCode conv_err; + + sql=QString("select ")+ + "`ID`,"+ // 00 + "`POSTED_FILENAME` "+ // 01 + "from `REPL_CART_STATE` where "+ + "`REPLICATOR_NAME`='"+RDEscapeString(config()->name())+"'"; + q=new RDSqlQuery(sql); + while(q->next()) { + sql=QString("select `ID` from `ISCI_XREFERENCE` where ")+ + "`FILENAME`='"+RDEscapeString(q->value(1).toString())+"'"; + q1=new RDSqlQuery(sql); + if(!q1->first()) { + QString path=config()->url(); + if(path.right(1)!="/") { + path+="/"; + } + QUrl url(path+q->value(1).toString()); + conv=new RDDelete(rda->config()); + conv->setTargetUrl(url.toString()); + // + // FIXME: Finish implementing ssh(1) key support! + // + if((conv_err=conv->runDelete(config()->urlUsername(), + config()->urlPassword(),"",false, + rda->config()->logXloadDebugData()))== + RDDelete::ErrorOk) { + sql=QString::asprintf("delete from `REPL_CART_STATE` where `ID`=%d", + q->value(0).toInt()); + q2=new RDSqlQuery(sql); + delete q2; + rda->syslog(LOG_INFO,"purged \"%s\" for replicator \"%s\"", + (const char *)url.toString().toUtf8(), + (const char *)config()->name().toUtf8()); + } + else { + rda->syslog(LOG_WARNING, + "unable to delete \"%s\" for replicator \"%s\" [%s]", + (const char *)url.toString().toUtf8(), + (const char *)config()->name().toUtf8(), + (const char *)RDDelete::errorText(conv_err).toUtf8()); + } + delete conv; + } + delete q1; + } + delete q; +} diff --git a/rdrepld/xdscue.h b/rdrepld/xdscue.h new file mode 100644 index 00000000..50f37a09 --- /dev/null +++ b/rdrepld/xdscue.h @@ -0,0 +1,44 @@ +// xdscue.h +// +// Replicator implementation for X-Digital Cue Model Copy-splitting +// +// (C) Copyright 2010-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 +// published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#ifndef XDSCUE_H +#define XDSCUE_H + +#include "replfactory.h" + +class XdsCue : public ReplFactory +{ + public: + XdsCue(ReplConfig *repl_config); + void startProcess(); + bool processCart(const unsigned cartnum); + + private: + void CheckIsciXreference(); + bool LoadIsciXreference(const QString &filename); + bool ValidateFilename(const QString &filename); + void CheckCarts(); + bool PostCut(const QString &cutname,const QString &filename); + void PurgeCuts(); + QDateTime xds_isci_datetime; +}; + + +#endif // XDSCUE_H diff --git a/utils/rddbmgr/revertschema.cpp b/utils/rddbmgr/revertschema.cpp index e51645da..cf69d492 100644 --- a/utils/rddbmgr/revertschema.cpp +++ b/utils/rddbmgr/revertschema.cpp @@ -40,6 +40,17 @@ bool MainObject::RevertSchema(int cur_schema,int set_schema,QString *err_msg) // NEW SCHEMA REVERSIONS GO HERE... + // + // Revert 372 + // + if((cur_schema == 372) && (set_schema < cur_schema)) + { + DropIndex("ISCI_XREFERENCE","FILENAME_IDX"); + DropColumn("REPLICATORS", "PROGRAM_CODE"); + + WriteSchemaVersion(--cur_schema); + } + // // Revert 371 // @@ -50,7 +61,6 @@ bool MainObject::RevertSchema(int cur_schema,int set_schema,QString *err_msg) WriteSchemaVersion(--cur_schema); } - // // Revert 370 // diff --git a/utils/rddbmgr/schemamap.cpp b/utils/rddbmgr/schemamap.cpp index fb993cc1..9119553f 100644 --- a/utils/rddbmgr/schemamap.cpp +++ b/utils/rddbmgr/schemamap.cpp @@ -162,6 +162,7 @@ void MainObject::InitializeSchemaMap() { global_version_map["3.6"]=347; global_version_map["4.0"]=370; global_version_map["4.1"]=371; + global_version_map["4.2"]=372; } diff --git a/utils/rddbmgr/updateschema.cpp b/utils/rddbmgr/updateschema.cpp index 740e9f32..e6fe22f3 100644 --- a/utils/rddbmgr/updateschema.cpp +++ b/utils/rddbmgr/updateschema.cpp @@ -11423,6 +11423,23 @@ bool MainObject::UpdateSchema(int cur_schema,int set_schema,QString *err_msg) WriteSchemaVersion(++cur_schema); } + if((cur_schema<372)&&(set_schema>cur_schema)) { + sql=QString("alter table `REPLICATORS` ")+ + "add column `PROGRAM_CODE` varchar(191) "+ + "after `STATION_NAME`"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + sql=QString("create index `FILENAME_IDX` on ")+ + "`ISCI_XREFERENCE` (`FILENAME`)"; + if(!RDSqlQuery::apply(sql,err_msg)) { + return false; + } + + WriteSchemaVersion(++cur_schema); + } + // NEW SCHEMA UPDATES GO HERE...