diff --git a/ChangeLog b/ChangeLog index 195071bb..587aac08 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14535,3 +14535,12 @@ 2014-10-08 Fred Gleason * Applied a patch from albanpeignier that fixed a random/null end point with FLAC imports [GitHub pull request #000034]. +2014-10-09 Fred Gleason + * Implemented the 'RDXPORT_COMMAND_AUDIOSTORE' web API call in + 'web/rdxport/audiostore.cpp', 'web/rdxport/rdxport.cpp' and + 'web/rdxport/rdxport.h'. + * Added an 'RDAudioStore' class in 'lib/rdaudiostore.cpp' and + 'lib/rdaudiostore.h'. + * Modified the Desk Gauge widget in 'rdlibrary/disk_gauge.cpp' + and 'rdlibrary/disk_gauge.h' to use the 'RDAudioStore' class + [fixes GitHub issue #000035]. diff --git a/docs/web_api.odt b/docs/web_api.odt index 8ad687f5..23bf8e21 100644 Binary files a/docs/web_api.odt and b/docs/web_api.odt differ diff --git a/lib/Makefile.am b/lib/Makefile.am index 250e8b2b..e751d5b0 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -73,6 +73,7 @@ dist_librd_la_SOURCES = dbversion.h\ rdaudioinfo.cpp rdaudioinfo.h\ rdaudiosettings_dialog.cpp rdaudiosettings_dialog.h\ rdaudiosettings.cpp rdaudiosettings.h\ + rdaudiostore.cpp rdaudiostore.h\ rdbusybar.cpp rdbusybar.h\ rdbusydialog.cpp rdbusydialog.h\ rdbutton_dialog.cpp rdbutton_dialog.h\ @@ -243,6 +244,7 @@ nodist_librd_la_SOURCES = moc_rdadd_cart.cpp\ moc_rdaudioimport.cpp\ moc_rdaudioinfo.cpp\ moc_rdaudiosettings_dialog.cpp\ + moc_rdaudiostore.cpp\ moc_rdbusybar.cpp\ moc_rdbusydialog.cpp\ moc_rdbutton_dialog.cpp\ diff --git a/lib/rdaudiostore.cpp b/lib/rdaudiostore.cpp new file mode 100644 index 00000000..d07fbd54 --- /dev/null +++ b/lib/rdaudiostore.cpp @@ -0,0 +1,203 @@ +// rdaudiostore.cpp +// +// Get information about the audio store. +// +// (C) Copyright 2014 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 + +size_t RDAudioStoreCallback(void *ptr,size_t size,size_t nmemb,void *userdata) +{ + QString *xml=(QString *)userdata; + for(unsigned i=0;i<(size*nmemb);i++) { + *xml+=((const char *)ptr)[i]; + } + return size*nmemb; +} + + +RDAudioStore::RDAudioStore(RDStation *station,RDConfig *config, + QObject *parent,const char *name) + : QObject(parent,name) +{ + conv_station=station; + conv_config=config; + conv_free_bytes=0; + conv_total_bytes=0; +} + + +uint64_t RDAudioStore::freeBytes() const +{ + return conv_free_bytes; +} + + +uint64_t RDAudioStore::totalBytes() const +{ + return conv_total_bytes; +} + + +RDAudioStore::ErrorCode RDAudioStore::runStore(const QString &username, + const QString &password) +{ + long response_code; + CURL *curl=NULL; + char url[1024]; + CURLcode curl_err; + + // + // Generate POST Data + // + QString post=QString().sprintf("COMMAND=%d&LOGIN_NAME=%s&PASSWORD=%s", + RDXPORT_COMMAND_AUDIOSTORE, + (const char *)RDFormPost::urlEncode(username), + (const char *)RDFormPost::urlEncode(password)); + if((curl=curl_easy_init())==NULL) { + return RDAudioStore::ErrorInternal; + } + + // + // Write out URL as a C string before passing to curl_easy_setopt(), + // otherwise some versions of LibCurl will throw a 'bad/illegal format' + // error. + // + strncpy(url,conv_station->webServiceUrl(conv_config),1024); + curl_easy_setopt(curl,CURLOPT_URL,url); + curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,RDAudioStoreCallback); + curl_easy_setopt(curl,CURLOPT_WRITEDATA,&conv_xml); + curl_easy_setopt(curl,CURLOPT_POST,1); + curl_easy_setopt(curl,CURLOPT_POSTFIELDS,(const char *)post); + curl_easy_setopt(curl,CURLOPT_TIMEOUT,RD_CURL_TIMEOUT); + + switch(curl_err=curl_easy_perform(curl)) { + case CURLE_OK: + break; + + case CURLE_UNSUPPORTED_PROTOCOL: + case CURLE_FAILED_INIT: + case CURLE_COULDNT_RESOLVE_PROXY: + case CURLE_PARTIAL_FILE: + case CURLE_HTTP_RETURNED_ERROR: + case CURLE_WRITE_ERROR: + case CURLE_OUT_OF_MEMORY: + case CURLE_OPERATION_TIMEDOUT: + case CURLE_HTTP_POST_ERROR: + curl_easy_cleanup(curl); + fprintf(stderr,"curl error: %d\n",curl_err); + return RDAudioStore::ErrorInternal; + + case CURLE_URL_MALFORMAT: + case CURLE_COULDNT_RESOLVE_HOST: + case CURLE_COULDNT_CONNECT: + case 9: // CURLE_REMOTE_ACCESS_DENIED + curl_easy_cleanup(curl); + return RDAudioStore::ErrorUrlInvalid; + + default: + curl_easy_cleanup(curl); + return RDAudioStore::ErrorService; + } + curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&response_code); + curl_easy_cleanup(curl); + + switch(response_code) { + case 200: + break; + + case 404: + return RDAudioStore::ErrorNoAudio; + + default: + return RDAudioStore::ErrorService; + } + conv_free_bytes=ParseInt("freeBytes",conv_xml); + conv_total_bytes=ParseInt("totalBytes",conv_xml); + + return RDAudioStore::ErrorOk; +} + + +QString RDAudioStore::errorText(RDAudioStore::ErrorCode err) +{ + QString ret=QString().sprintf("Unknown Error [%u]",err); + + switch(err) { + case RDAudioStore::ErrorOk: + ret=tr("OK"); + break; + + case RDAudioStore::ErrorInternal: + ret=tr("Internal Error"); + break; + + case RDAudioStore::ErrorUrlInvalid: + ret=tr("Invalid URL"); + break; + + case RDAudioStore::ErrorService: + ret=tr("RDXport service returned an error"); + break; + + case RDAudioStore::ErrorInvalidUser: + ret=tr("Invalid user or password"); + break; + + case RDAudioStore::ErrorNoAudio: + ret=tr("Audio does not exist"); + break; + } + return ret; +} + + +uint64_t RDAudioStore::ParseInt(const QString &tag,const QString &xml) +{ + // + // FIXME: This is totally ad-hoc, but should work until we settle on + // a proper XML parser. + // + QStringList list=list.split("\n",xml); + for(unsigned i=0;i=2) { + list2=list2.split(">",list2[1]); + if(list2.size()>=2) { + return list2[1].toLongLong(); + } + } + } + } + return -1; +} diff --git a/lib/rdaudiostore.h b/lib/rdaudiostore.h new file mode 100644 index 00000000..a06ed6bb --- /dev/null +++ b/lib/rdaudiostore.h @@ -0,0 +1,55 @@ +// rdaudiostore.h +// +// Get information about the audio store. +// +// (C) Copyright 2014 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 RDAUDIOSTORE_H +#define RDAUDIOSTORE_H + +#include + +#include + +#include +#include + +class RDAudioStore : public QObject +{ + Q_OBJECT; + public: + enum ErrorCode {ErrorOk=0,ErrorInternal=5,ErrorUrlInvalid=7, + ErrorService=8,ErrorInvalidUser=9,ErrorNoAudio=10}; + RDAudioStore(RDStation *station,RDConfig *config,QObject *parent=0, + const char *name=0); + uint64_t freeBytes() const; + uint64_t totalBytes() const; + RDAudioStore::ErrorCode runStore(const QString &username, + const QString &password); + static QString errorText(RDAudioStore::ErrorCode err); + + private: + uint64_t ParseInt(const QString &tag,const QString &xml); + RDStation *conv_station; + RDConfig *conv_config; + QString conv_xml; + uint64_t conv_free_bytes; + uint64_t conv_total_bytes; +}; + + +#endif // RDAUDIOSTORE_H diff --git a/lib/rdxport_interface.h b/lib/rdxport_interface.h index c2b66d31..0b2e75d9 100644 --- a/lib/rdxport_interface.h +++ b/lib/rdxport_interface.h @@ -43,5 +43,7 @@ #define RDXPORT_COMMAND_LISTLOGS 20 #define RDXPORT_COMMAND_LISTSERVICES 21 #define RDXPORT_COMMAND_LISTLOG 22 +#define RDXPORT_COMMAND_AUDIOSTORE 23 + #endif // RDXPORT_INTERFACE_H diff --git a/rdlibrary/disk_gauge.cpp b/rdlibrary/disk_gauge.cpp index 355156b7..43d0fc38 100644 --- a/rdlibrary/disk_gauge.cpp +++ b/rdlibrary/disk_gauge.cpp @@ -22,12 +22,13 @@ #include -#include #include +#include #include #include #include +#include DiskGauge::DiskGauge(int samp_rate,int chans,QWidget *parent,const char *name) : QWidget(parent,name) @@ -41,27 +42,29 @@ DiskGauge::DiskGauge(int samp_rate,int chans,QWidget *parent,const char *name) QFont label_font("Helvetica",12,QFont::Bold); label_font.setPixelSize(12); - disk_label=new QLabel("Free:",this,"free_label"); + disk_label=new QLabel("Free:",this); disk_label->setGeometry(0,0,50,sizeHint().height()); disk_label->setFont(label_font); disk_label->setAlignment(AlignRight|AlignVCenter); + disk_label->setDisabled(true); disk_bar=new QProgressBar(this); disk_bar->setPercentageVisible(false); disk_bar->setGeometry(55,0,sizeHint().width()-55,sizeHint().height()); + disk_bar->setDisabled(true); disk_space_label=new QLabel(this); disk_space_label->setFont(label_font); disk_space_label->setAlignment(AlignCenter); + disk_space_label->setDisabled(true); - struct statfs diskstat; - statfs(RDConfiguration()->audioRoot().ascii(),&diskstat); - disk_bar->setTotalSteps(GetMinutes(diskstat.f_blocks,diskstat.f_bsize)); + /* update(); + */ - QTimer *timer=new QTimer(this,"update_timer"); - connect(timer,SIGNAL(timeout()),this,SLOT(update())); - timer->start(DISK_GAUGE_UPDATE_INTERVAL); + disk_timer=new QTimer(this); + connect(disk_timer,SIGNAL(timeout()),this,SLOT(update())); + disk_timer->start(100); } @@ -79,12 +82,28 @@ QSizePolicy DiskGauge::sizePolicy() const void DiskGauge::update() { - struct statfs diskstat; - statfs(RDConfiguration()->audioRoot().ascii() ,&diskstat); - int mins=GetMinutes(diskstat.f_bavail,diskstat.f_bsize); - disk_bar->setProgress(mins); - disk_space_label-> - setText(QString().sprintf("%dh %02dm",mins/60,mins-60*(mins/60))); + if(lib_user==NULL) { + return; + } + RDAudioStore::ErrorCode conv_err; + RDAudioStore *conv=new RDAudioStore(rdstation_conf,lib_config,this); + if((conv_err=conv->runStore(lib_user->name(),lib_user->password()))== + RDAudioStore::ErrorOk) { + uint64_t free_min=conv->freeBytes()/ + (60*rdlibrary_conf->defaultChannels()*lib_system->sampleRate()); + uint64_t total_min=conv->totalBytes()/ + (60*rdlibrary_conf->defaultChannels()*lib_system->sampleRate()); + disk_bar->setTotalSteps(total_min); + disk_bar->setProgress(free_min); + disk_space_label->setText(QString().sprintf("%luh %02lum",free_min/60, + free_min-60*(free_min/60))); + disk_label->setEnabled(true); + disk_bar->setEnabled(true); + disk_space_label->setEnabled(true); + } + delete conv; + disk_timer->stop(); + disk_timer->start(DISK_GAUGE_UPDATE_INTERVAL,true); } @@ -98,10 +117,3 @@ void DiskGauge::resizeEvent(QResizeEvent *e) setGeometry(0,size().height()/2,size().width(),size().height()/2); delete fm; } - - -int DiskGauge::GetMinutes(long blocks,long block_size) -{ - return (int)(((double)blocks*(double)block_size)/ - (disk_sample_rate*disk_channels*120.0)); -} diff --git a/rdlibrary/disk_gauge.h b/rdlibrary/disk_gauge.h index ee227e77..923d42f6 100644 --- a/rdlibrary/disk_gauge.h +++ b/rdlibrary/disk_gauge.h @@ -25,6 +25,7 @@ #include #include +#include #include #define DISK_GAUGE_UPDATE_INTERVAL 60000 @@ -44,12 +45,12 @@ class DiskGauge : public QWidget void resizeEvent(QResizeEvent *e); private: - int GetMinutes(long blocks,long block_size); QLabel *disk_label; QProgressBar *disk_bar; QLabel *disk_space_label; double disk_sample_rate; double disk_channels; + QTimer *disk_timer; }; diff --git a/web/rdxport/Makefile.am b/web/rdxport/Makefile.am index cda2422c..ae0e9bc7 100644 --- a/web/rdxport/Makefile.am +++ b/web/rdxport/Makefile.am @@ -31,6 +31,7 @@ install-exec-hook: if test -z $(DESTDIR) ; then chown root $(DESTDIR)$(libexecdir)/rdxport.cgi ; chmod 4755 $(DESTDIR)$(libexecdir)/rdxport.cgi ; fi dist_rdxport_cgi_SOURCES = audioinfo.cpp\ + audiostore.cpp\ carts.cpp\ copyaudio.cpp\ deleteaudio.cpp\ diff --git a/web/rdxport/audiostore.cpp b/web/rdxport/audiostore.cpp new file mode 100644 index 00000000..9cd8642d --- /dev/null +++ b/web/rdxport/audiostore.cpp @@ -0,0 +1,53 @@ +// audiostore.cpp +// +// Rivendell web service portal -- AudioStore service +// +// (C) Copyright 2014 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 + +void Xport::AudioStore() +{ + struct statvfs stat; + + memset(&stat,0,sizeof(stat)); + if(statvfs(xport_config->audioRoot(),&stat)<0) { + XmlExit("Internal Error",400); + } + printf("Content-type: application/xml\n"); + printf("Status: 200\n\n"); + printf("\n"); + printf("\n"); + printf(" %lu\n",stat.f_bfree*stat.f_bsize); + printf(" %lu\n",stat.f_blocks*stat.f_bsize); + printf("\n"); + + Exit(0); +} diff --git a/web/rdxport/rdxport.cpp b/web/rdxport/rdxport.cpp index 55ac386c..d8b2db12 100644 --- a/web/rdxport/rdxport.cpp +++ b/web/rdxport/rdxport.cpp @@ -219,6 +219,10 @@ Xport::Xport(QObject *parent,const char *name) AudioInfo(); break; + case RDXPORT_COMMAND_AUDIOSTORE: + AudioStore(); + break; + case RDXPORT_COMMAND_LISTLOGS: ListLogs(); break; diff --git a/web/rdxport/rdxport.h b/web/rdxport/rdxport.h index cf556ebb..72e1252e 100644 --- a/web/rdxport/rdxport.h +++ b/web/rdxport/rdxport.h @@ -54,6 +54,7 @@ class Xport : public QObject void TrimAudio(); void CopyAudio(); void AudioInfo(); + void AudioStore(); void ListLogs(); void ListLog(); void ListServices(); diff --git a/web/tests/Makefile.am b/web/tests/Makefile.am index d1351884..b9219bc3 100644 --- a/web/tests/Makefile.am +++ b/web/tests/Makefile.am @@ -26,6 +26,7 @@ install-exec-am: cp addcart.html $(DESTDIR)@libexecdir@ cp addcut.html $(DESTDIR)@libexecdir@ cp audioinfo.html $(DESTDIR)@libexecdir@ + cp audiostore.html $(DESTDIR)@libexecdir@ cp copyaudio.html $(DESTDIR)@libexecdir@ cp delete_audio.html $(DESTDIR)@libexecdir@ cp editcart.html $(DESTDIR)@libexecdir@ @@ -50,6 +51,7 @@ uninstall: rm -f $(DESTDIR)@libexecdir@/addcart.html rm -f $(DESTDIR)@libexecdir@/addcut.html rm -f $(DESTDIR)@libexecdir@/audioinfo.html + rm -f $(DESTDIR)@libexecdir@/audiostore.html rm -f $(DESTDIR)@libexecdir@/copyaudio.html rm -f $(DESTDIR)@libexecdir@/delete_audio.html rm -f $(DESTDIR)@libexecdir@/editcart.html @@ -72,6 +74,7 @@ uninstall: EXTRA_DIST = addcart.html\ addcut.html\ audioinfo.html\ + audiostore.html\ copyaudio.html\ delete_audio.html\ editcart.html\ diff --git a/web/tests/audiostore.html b/web/tests/audiostore.html new file mode 100644 index 00000000..2a1fac7e --- /dev/null +++ b/web/tests/audiostore.html @@ -0,0 +1,25 @@ + + +Rivendell AUDIOSTORE Service Test Harness + +
+ + + + + + + + + + + + + + + + +
LOGIN NAME:
PASSWORD:
 
+
+ +