2018-05-24 Fred Gleason <fredg@paravelsystems.com>

* Added a WebGet web service.
This commit is contained in:
Fred Gleason 2018-05-25 01:00:43 +00:00
parent 97568f0c0d
commit 9e215e311f
12 changed files with 926 additions and 2 deletions

View File

@ -16896,3 +16896,5 @@
* Modified caed(8) to start JACK clients after a 5 second delay.
2018-05-22 Fred Gleason <fredg@paravelsystems.com>
* Incremented the package version to 2.19.2vlog01.
2018-05-24 Fred Gleason <fredg@paravelsystems.com>
* Added a WebGet web service.

View File

@ -532,6 +532,7 @@ AC_CONFIG_FILES([rivendell.spec \
web/rdcastmanager/Makefile \
web/rdxport/Makefile \
web/tests/Makefile \
web/webget/Makefile \
conf/Makefile \
docs/Makefile \
docs/apis/Makefile \

View File

@ -28,6 +28,8 @@ install-exec-am:
cp redball.png $(DESTDIR)@libexecdir@
cp whiteball.png $(DESTDIR)@libexecdir@
cp progressbar.gif $(DESTDIR)@libexecdir@
mkdir -p $(DESTDIR)@libexecdir@/logos
cp webget_logo.png $(DESTDIR)@libexecdir@/logos/
mkdir -p $(DESTDIR)@prefix@/share/icons/hicolor/16x16/apps
cp rivendell-16x16.png $(DESTDIR)@prefix@/share/icons/hicolor/16x16/apps/rivendell.png
mkdir -p $(DESTDIR)@prefix@/share/icons/hicolor/22x22/apps
@ -190,6 +192,7 @@ uninstall-local:
rm -f $(DESTDIR)@libexecdir@/redball.png
rm -f $(DESTDIR)@libexecdir@/whiteball.png
rm -f $(DESTDIR)@libexecdir@/progressbar.gif
rm -f $(DESTDIR)@libexecdir@/logos/webget_logo.png
rm -f $(DESTDIR)@prefix@/share/icons/hicolor/16x16/apps/rivendell.png
rm -f $(DESTDIR)@prefix@/share/icons/hicolor/22x22/apps/rivendell.png
rm -f $(DESTDIR)@prefix@/share/icons/hicolor/32x32/apps/rivendell.png
@ -470,6 +473,7 @@ EXTRA_DIST = admin.xpm\
trashcan-32x32.xpm\
upload.xpm\
user.xpm\
webget_logo.png\
whiteball.png\
whiteball.xpm

BIN
icons/webget_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -112,11 +112,13 @@ mkdir $RPM_BUILD_ROOT/.qt
touch $RPM_BUILD_ROOT/.qt/qt
rm -f $RPM_BUILD_ROOT/lib/security/pam_rd.la
rm -f $RPM_BUILD_ROOT/lib64/security/pam_rd.la
rm -rf $RPM_BUILD_ROOT/@libexecdir@/logos
if test "@WIN32_SOURCE@" ; then
mkdir -p $RPM_BUILD_ROOT/var/win32
cp @WIN32_SOURCE@ $RPM_BUILD_ROOT/var/win32/
fi
mkdir -p $RPM_BUILD_ROOT/@DOC_PATH@
mkdir -p $RPM_BUILD_ROOT/@DOC_PATH@/logos
cp icons/webget_logo.png $RPM_BUILD_ROOT/@DOC_PATH@/logos/
cp AUTHORS $RPM_BUILD_ROOT/@DOC_PATH@/
cp ChangeLog $RPM_BUILD_ROOT/@DOC_PATH@/
cp COPYING $RPM_BUILD_ROOT/@DOC_PATH@/
@ -220,6 +222,12 @@ fi
if [ -x %{_bindir}/gtk-update-icon-cache ] ; then
%{_bindir}/gtk-update-icon-cache -f --quiet %{_datadir}/icons/hicolor || :
fi
if test ! -e @libexecdir@/logos ; then
mkdir -p @libexecdir@/logos
fi
if test ! -f @libexecdir@/logos/webget_logo.png ; then
cp @DOC_PATH@/logos/webget_logo.png @libexecdir@/logos/webget_logo.png
fi
exit 0
@ -389,6 +397,7 @@ rm -rf $RPM_BUILD_ROOT
/.qt/qt
@libexecdir@/*
%attr(6755,root,root) @libexecdir@/rdxport.cgi
%attr(6755,root,root) @libexecdir@/webget.cgi
@sysconfdir@/rd-bin.conf
@WIN32_PATH@
/etc/pam.d/rivendell

View File

@ -23,7 +23,8 @@
SUBDIRS = rdcastmanager\
rdfeed\
rdxport\
tests
tests\
webget
AM_CPPFLAGS = -Wall -DPREFIX=\"$(prefix)\" -DQTDIR=\"@QT_DIR@\" @QT_CXXFLAGS@ -I$(top_srcdir)/lib
LIBS = @QT_LIBS@ -L$(top_srcdir)/lib

52
web/webget/Makefile.am Normal file
View File

@ -0,0 +1,52 @@
## Makefile.am
##
## (C) Copyright 2017-2018 Fred Gleason <fredg@paravelsystems.com>
##
## 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.
##
## Use automake to process this into a Makefile.in
AM_CPPFLAGS = -Wall -DPREFIX=\"$(prefix)\" -DQTDIR=\"@QT_DIR@\" @QT_CXXFLAGS@ -I$(top_srcdir)/lib
LIBS = @QT_LIBS@ -L$(top_srcdir)/lib
MOC = @QT_MOC@
# The dependency for qt's Meta Object Compiler (moc)
moc_%.cpp: %.h
$(MOC) $< -o $@
install-exec-local:
mkdir -p $(DESTDIR)@libexecdir@
cp webget.html $(DESTDIR)@libexecdir@/
cp webget.js $(DESTDIR)@libexecdir@/
install-exec-hook:
if test -z $(DESTDIR) ; then chown root $(DESTDIR)$(libexecdir)/webget.cgi ; chmod 4755 $(DESTDIR)$(libexecdir)/webget.cgi ; fi
uninstall-local:
rm -f $(DESTDIR)@libexecdir@/webget.html
rm -f $(DESTDIR)@libexecdir@/webget.js
libexec_PROGRAMS = webget.cgi
dist_webget_cgi_SOURCES = webget.cpp webget.h
nodist_webget_cgi_SOURCES = moc_webget.cpp
webget_cgi_LDADD = @LIB_RDLIBS@ -lsndfile @LIBVORBIS@
EXTRA_DIST = webget.html\
webget.js
CLEANFILES = *~
MAINTAINERCLEANFILES = *~\
Makefile.in

362
web/webget/webget.cpp Normal file
View File

@ -0,0 +1,362 @@
// webget.cpp
//
// Rivendell download utility
//
// (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
//
// 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 <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <openssl/sha.h>
#include <qapplication.h>
#include <rdapplication.h>
#include <rdescape_string.h>
#include <rdweb.h>
#include "webget.h"
MainObject::MainObject(QObject *parent)
:QObject(parent)
{
QString err_msg;
//
// Open the Database
//
rda=new RDApplication("webget.cgi","webget.cgi",WEBGET_CGI_USAGE,this);
if(!rda->open(&err_msg)) {
XmlExit(err_msg,500,"webget.cpp",LINE_NUMBER);
}
//
// Read Command Options
//
for(unsigned i=0;i<rda->cmdSwitch()->keys();i++) {
if(!rda->cmdSwitch()->processed(i)) {
XmlExit("unknown command option",500,"webget.cpp",LINE_NUMBER);
}
}
//
// Drop root permissions
//
if(setgid(rda->config()->gid())<0) {
XmlExit("Unable to set Rivendell group",500,"webget.cpp",LINE_NUMBER);
}
if(setuid(rda->config()->uid())<0) {
XmlExit("Unable to set Rivendell user",500,"webget.cpp",LINE_NUMBER);
}
if(getuid()==0) {
XmlExit("Rivendell user should never be \"root\"!",500,"webget.cpp",
LINE_NUMBER);
}
//
// Determine Connection Type
//
if(getenv("REQUEST_METHOD")==NULL) {
XmlExit("missing REQUEST_METHOD",500,"webget.cpp",LINE_NUMBER);
}
if(QString(getenv("REQUEST_METHOD")).lower()!="post") {
XmlExit("invalid web method",500,"webget.cpp",LINE_NUMBER);
}
if(getenv("REMOTE_ADDR")!=NULL) {
webget_remote_address.setAddress(getenv("REMOTE_ADDR"));
}
if(getenv("REMOTE_HOST")!=NULL) {
webget_remote_hostname=getenv("REMOTE_HOST");
}
if(webget_remote_hostname.isEmpty()) {
webget_remote_hostname=webget_remote_address.toString();
}
//
// Generate Post
//
webget_post=new RDFormPost(RDFormPost::AutoEncoded,false);
if(webget_post->error()!=RDFormPost::ErrorOk) {
XmlExit(webget_post->errorString(webget_post->error()),400,"webget.cpp",
LINE_NUMBER);
Exit(0);
}
//
// Authenticate Connection
//
if(!Authenticate()) {
printf("Content-type: text/html\n");
printf("Status: 400\n");
printf("\n");
printf("Invalid User name or Password!\n");
Exit(0);
}
//
// Connect to ripcd(8)
//
connect(rda->ripc(),SIGNAL(connected(bool)),
this,SLOT(ripcConnectedData(bool)));
rda->ripc()->
connectHost("localhost",RIPCD_TCP_PORT,rda->config()->password());
}
void MainObject::ripcConnectedData(bool state)
{
if(!state) {
XmlExit("unable to connect to ripc service",500,"webget.cpp",LINE_NUMBER);
Exit(0);
}
QString title;
if(!webget_post->getValue("title",&title)) {
XmlExit("missing \"title\"",400,"webget.cpp",LINE_NUMBER);
}
int samprate;
if(!webget_post->getValue("samprate",&samprate)) {
XmlExit("missing \"samprate\"",400,"webget.cpp",LINE_NUMBER);
}
int format;
if(!webget_post->getValue("format",&format)) {
XmlExit("missing \"format\"",400,"webget.cpp",LINE_NUMBER);
}
int bitrate=0;
if((format==RDSettings::MpegL2)||(format==RDSettings::MpegL3)) {
if(!webget_post->getValue("bitrate",&bitrate)) {
XmlExit("missing \"bitrate\"",400,"webget.cpp",LINE_NUMBER);
}
}
int quality=0;
if(format==RDSettings::OggVorbis) {
if(!webget_post->getValue("quality",&quality)) {
XmlExit("missing \"quality\"",400,"webget.cpp",LINE_NUMBER);
}
}
unsigned cartnum=0;
int cutnum=0;
QString sql=QString("select ")+
"CUTS.CUT_NAME from "+
"CART left join CUTS on CART.NUMBER=CUTS.CART_NUMBER where "+
"CART.TITLE=\""+RDEscapeString(title)+"\" && "+
QString().sprintf("CART.TYPE=%d",RDCart::Audio);
RDSqlQuery *q=new RDSqlQuery(sql);
if(q->first()) {
cartnum=RDCut::cartNumber(q->value(0).toString());
cutnum=RDCut::cutNumber(q->value(0).toString());
}
delete q;
if(cartnum==0) {
printf("Content-type: text/html\n");
printf("Status: 400\n");
printf("\n");
printf("No such cart!\n");
Exit(0);
}
//
// Audio Settings
//
RDSettings *settings=new RDSettings();
settings->setFormat((RDSettings::Format)format);
settings->setChannels(1);
settings->setSampleRate(samprate);
settings->setBitRate(bitrate);
settings->setQuality(quality);
settings->setNormalizationLevel(-100);
//
// Generate Metadata
//
RDCart *cart=new RDCart(cartnum);
RDCut *cut=new RDCut(cartnum,cutnum);
RDWaveData *wavedata=NULL;
QString rdxl;
float speed_ratio=1.0;
wavedata=new RDWaveData();
if(wavedata!=NULL) {
cart->getMetadata(wavedata);
cut->getMetadata(wavedata);
if(cart->enforceLength()) {
speed_ratio=(float)cut->length()/(float)cart->forcedLength();
}
rdxl=cart->xml(true,false,settings,cutnum);
}
//
// Export Cut
//
int fd;
ssize_t n;
uint8_t data[2048];
QString err_msg;
RDTempDirectory *tempdir=new RDTempDirectory("rdxport-export");
if(!tempdir->create(&err_msg)) {
XmlExit("unable to create temporary directory ["+err_msg+"]",500);
}
QString tmpfile=tempdir->path()+"/exported_audio";
RDAudioConvert *conv=new RDAudioConvert(this);
conv->setSourceFile(RDCut::pathName(cartnum,cutnum));
conv->setDestinationFile(tmpfile);
conv->setDestinationSettings(settings);
conv->setDestinationWaveData(wavedata);
conv->setDestinationRdxl(rdxl);
conv->setRange(cut->startPoint(),cut->endPoint());
conv->setSpeedRatio(speed_ratio);
delete cut;
delete cart;
RDAudioConvert::ErrorCode conv_err;
int resp_code=0;
switch(conv_err=conv->convert()) {
case RDAudioConvert::ErrorOk:
switch(settings->format()) {
case RDSettings::Pcm16:
case RDSettings::Pcm24:
printf("Content-type: audio/x-wav\n\n");
break;
case RDSettings::MpegL1:
case RDSettings::MpegL2:
case RDSettings::MpegL2Wav:
case RDSettings::MpegL3:
printf("Content-type: audio/x-mpeg\n\n");
break;
case RDSettings::OggVorbis:
printf("Content-type: audio/ogg\n\n");
break;
case RDSettings::Flac:
printf("Content-type: audio/flac\n\n");
break;
}
fflush(NULL);
if((fd=open(tmpfile,O_RDONLY))>=0) {
while((n=read(fd,data,2048))>0) {
write(1,data,n);
}
}
close(fd);
unlink(tmpfile);
delete tempdir;
Exit(0);
break;
case RDAudioConvert::ErrorFormatNotSupported:
case RDAudioConvert::ErrorInvalidSettings:
resp_code=415;
break;
case RDAudioConvert::ErrorNoSource:
resp_code=404;
break;
case RDAudioConvert::ErrorNoDestination:
case RDAudioConvert::ErrorInvalidSource:
case RDAudioConvert::ErrorNoSpace:
case RDAudioConvert::ErrorInternal:
case RDAudioConvert::ErrorNoDisc:
case RDAudioConvert::ErrorNoTrack:
case RDAudioConvert::ErrorInvalidSpeed:
case RDAudioConvert::ErrorFormatError:
resp_code=500;
break;
}
delete conv;
delete settings;
if(wavedata!=NULL) {
delete wavedata;
}
delete tempdir;
if(resp_code==200) {
Exit(200);
}
else {
XmlExit(RDAudioConvert::errorText(conv_err),resp_code,"export.cpp",
LINE_NUMBER,conv_err);
}
}
bool MainObject::Authenticate()
{
QString name;
QString passwd;
if(!webget_post->getValue("LOGIN_NAME",&name)) {
return false;
}
if(!webget_post->getValue("PASSWORD",&passwd)) {
return false;
}
RDUser *user=new RDUser(name);
if((!user->exists())||
(!user->checkPassword(passwd,false))||
(!user->webgetLogin())) {
return false;
}
return true;
}
void MainObject::Exit(int code)
{
if(webget_post!=NULL) {
delete webget_post;
}
exit(code);
}
void MainObject::XmlExit(const QString &str,int code,const QString &srcfile,
int srcline,RDAudioConvert::ErrorCode err)
{
if(webget_post!=NULL) {
delete webget_post;
}
#ifdef RDXPORT_DEBUG
if(srcline>0) {
RDXMLResult(str+" \""+srcfile+"\" "+QString().sprintf("line %d",srcline),
code,err);
}
else {
RDXMLResult(str,code,err);
}
#else
RDXMLResult(str,code,err);
#endif // RDXPORT_DEBUG
exit(0);
}
int main(int argc,char *argv[])
{
QApplication a(argc,argv,false);
new MainObject();
return a.exec();
}

55
web/webget/webget.h Normal file
View File

@ -0,0 +1,55 @@
// webget.h
//
// Rivendell audio download utility
//
// (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
//
// 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 WEBGET_H
#define WEBGET_H
#include <qobject.h>
#include <rdaudioconvert.h>
#include <rdformpost.h>
#define STRINGIZE(x) STRINGIZE2(x)
#define STRINGIZE2(x) #x
#define LINE_NUMBER QString(STRINGIZE(__LINE__)).toInt()
#define WEBGET_CGI_USAGE "\n"
class MainObject : public QObject
{
Q_OBJECT;
public:
MainObject(QObject *parent=0);
private slots:
void ripcConnectedData(bool state);
private:
bool Authenticate();
void Exit(int code);
void XmlExit(const QString &msg,int code,
const QString &srcfile="",int line=-1,
RDAudioConvert::ErrorCode err=RDAudioConvert::ErrorOk);
RDFormPost *webget_post;
QString webget_remote_hostname;
QHostAddress webget_remote_address;
};
#endif // WEBGET_H

106
web/webget/webget.html Normal file
View File

@ -0,0 +1,106 @@
<html>
<head>
<title>Rivendell Webget</title>
<script src="webget.js" type="application/javascript"></script>
</head>
<body>
<form method="post" action="webget.cgi" enctype="application/x-www-form-urlencoded">
<table style="margin: auto;padding: 10px 0" cellpadding="0" cellspacing="5" border="0">
<tr>
<td colspan="2"><img src="logos/webget_logo.png" border="0"></td>
</tr>
<tr>
<td colspan="2"><strong>Get audio from Rivendell</strong></td>
</tr>
<tr><td colspan="2"><hr></td></tr>
<tr>
<td style="text-align: right">Cart Title:</td>
<td><input type="text" name="title" size="40" maxlength="255"></td>
</tr>
<tr><td cellspan="2">&nbsp</td></tr>
<tr>
<td style="text-align: right">Channels:</td>
<td>
<select name="channels" id="channels" value="1" onclick="formatChanged()">
<option value="1">Monaural</option>
<option value="2">Stereo</option>
</select>
</td>
</tr>
<tr>
<td style="text-align: right">Sample Rate:</td>
<td>
<select name="samprate" id="samprate" onclick="samplerateChanged()">
<option value="8000">8000 samples/sec</option>
<option value="11025">11025 samples/sec</option>
<option value="12000">12000 samples/sec</option>
<option value="16000" selected>16000 samples/sec</option>
<option value="22050">22050 samples/sec</option>
<option value="24000">24000 samples/sec</option>
<option value="32000">32000 samples/sec</option>
<option value="44100">44100 samples/sec</option>
<option value="48000">48000 samples/sec</option>
</select>
</td>
</tr>
<tr>
<td style="text-align: right">Format:</td>
<td>
<select name="format" id="format" onclick="formatChanged()">
<option value="4">FLAC</option>
<option value="5">OggVorbis</option>
<option value="2">MP2</option>
<option value="3" selected>MP3</option>
<option value="0">PCM16 [wav]</option>
<option value="7">PCM24 [wav]</option>
</select>
</td>
</tr>
<tr>
<td style="text-align: right">Bit Rate:</td>
<td>
<select name="bitrate" id="bitrate">
<option value="16000" selected>16 kbit/sec</option>
<option value="32000">32 kbit/sec</option>
<option value="64000">64 kbit/sec</option>
<option value="128000">128 kbit/sec</option>
<option value="256000">256 kbit/sec</option>
</select>
</td>
</tr>
<tr>
<td style="text-align: right">Quality:</td>
<td>
<select name="quality" id="quality" disabled>
<option value="0">0 (lowest)</option>
<option value="1">1</option>
<option value="2" selected>2 (default)</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">0 (highest)</option>
</select>
</td>
</tr>
<tr><td cellspan="2">&nbsp</td></tr>
<td style="text-align: right">User Name:</td>
<td><input type="text" size="32" maxsize="255" name="LOGIN_NAME"></td>
</tr>
<tr>
<td style="text-align: right">Password:</td>
<td><input type="password" size="32" maxsize="32" name="PASSWORD"></td>
<tr><td cellspan="2">&nbsp</td></tr>
<tr>
<td>&nbsp;</td>
<td><input type="submit" value="OK"></td>
</tr>
</table>
</form>
</body>
</html>

187
web/webget/webget.js Normal file
View File

@ -0,0 +1,187 @@
// webget.js
//
// (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
//
// 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.
//
function Id(id)
{
return document.getElementById(id);
}
function GetMpegVersion(samprate)
{
mpeg_ver=1.0;
switch(samprate) {
case 32000:
case 44100:
case 48000:
mpeg_ver=1.0;
break;
case 16000:
case 22050:
case 24000:
mpeg_ver=2.0;
break;
case 8000:
case 11025:
case 12000:
mpeg_ver=2.5;
break;
}
return mpeg_ver;
}
function SetLayerII()
{
mpeg_ver=GetMpegVersion(parseInt(Id('samprate').value));
if(mpeg_ver==1.0) {
if(Id('channels').value=='1') {
Id('bitrate').innerHTML=
'<option value="32000">32 kbit/sec</option>\n'+
'<option value="48000" selected>48 kbit/sec</option>\n'+
'<option value="56000">56 kbit/sec</option>\n'+
'<option value="64000">64 kbit/sec</option>\n'+
'<option value="80000">80 kbit/sec</option>\n'+
'<option value="96000">96 kbit/sec</option>\n'+
'<option value="112000">112 kbit/sec</option>\n'+
'<option value="128000">128 kbit/sec</option>\n'+
'<option value="160000">160 kbit/sec</option>\n'+
'<option value="192000">192 kbit/sec</option>\n';
}
else {
Id('bitrate').innerHTML=
'<option value="32000">32 kbit/sec</option>\n'+
'<option value="48000" selected>48 kbit/sec</option>\n'+
'<option value="56000">56 kbit/sec</option>\n'+
'<option value="64000">64 kbit/sec</option>\n'+
'<option value="80000">80 kbit/sec</option>\n'+
'<option value="96000">96 kbit/sec</option>\n'+
'<option value="112000">112 kbit/sec</option>\n'+
'<option value="128000">128 kbit/sec</option>\n'+
'<option value="160000">160 kbit/sec</option>\n'+
'<option value="192000">192 kbit/sec</option>\n'+
'<option value="224000">224 kbit/sec</option>\n'+
'<option value="256000">256 kbit/sec</option>\n'+
'<option value="320000">320 kbit/sec</option>\n'+
'<option value="384000">384 kbit/sec</option>\n';
}
}
if(mpeg_ver==2.0) {
Id('bitrate').innerHTML=
'<option value="8000">8 kbit/sec</option>\n'+
'<option value="16000" selected>16 kbit/sec</option>\n'+
'<option value="24000">24 kbit/sec</option>\n'+
'<option value="32000">32 kbit/sec</option>\n'+
'<option value="40000">40 kbit/sec</option>\n'+
'<option value="48000">48 kbit/sec</option>\n'+
'<option value="56000">56 kbit/sec</option>\n'+
'<option value="64000">64 kbit/sec</option>\n'+
'<option value="80000">80 kbit/sec</option>\n'+
'<option value="96000">96 kbit/sec</option>\n'+
'<option value="112000">112 kbit/sec</option>\n'+
'<option value="128000">128 kbit/sec</option>\n'+
'<option value="144000">144 kbit/sec</option>\n'+
'<option value="160000">160 kbit/sec</option>\n';
}
if(mpeg_ver==2.5) {
Id('bitrate').innerHTML=
'<option value="8000">8 kbit/sec</option>\n'+
'<option value="16000" selected>16 kbit/sec</option>\n'+
'<option value="24000">24 kbit/sec</option>\n'+
'<option value="32000">32 kbit/sec</option>\n'+
'<option value="40000">40 kbit/sec</option>\n'+
'<option value="48000">48 kbit/sec</option>\n'+
'<option value="56000">56 kbit/sec</option>\n'+
'<option value="64000">64 kbit/sec</option>\n';
}
}
function SetLayerIII()
{
mpeg_ver=GetMpegVersion(parseInt(Id('samprate').value));
if(mpeg_ver==1.0) {
Id('bitrate').innerHTML=
'<option value="32000">32 kbit/sec</option>\n'+
'<option value="40000" selected>40 kbit/sec</option>\n'+
'<option value="48000">48 kbit/sec</option>\n'+
'<option value="56000">56 kbit/sec</option>\n'+
'<option value="64000">64 kbit/sec</option>\n'+
'<option value="80000">80 kbit/sec</option>\n'+
'<option value="96000">96 kbit/sec</option>\n'+
'<option value="112000">112 kbit/sec</option>\n'+
'<option value="128000">128 kbit/sec</option>\n'+
'<option value="160000">160 kbit/sec</option>\n'+
'<option value="192000">192 kbit/sec</option>\n'+
'<option value="224000">224 kbit/sec</option>\n'+
'<option value="256000">256 kbit/sec</option>\n'+
'<option value="320000">320 kbit/sec</option>\n';
}
if(mpeg_ver==2.0) {
Id('bitrate').innerHTML=
'<option value="8000">8 kbit/sec</option>\n'+
'<option value="16000" selected>16 kbit/sec</option>\n'+
'<option value="24000">24 kbit/sec</option>\n'+
'<option value="32000">32 kbit/sec</option>\n'+
'<option value="40000">40 kbit/sec</option>\n'+
'<option value="48000">48 kbit/sec</option>\n'+
'<option value="56000">56 kbit/sec</option>\n'+
'<option value="64000">64 kbit/sec</option>\n'+
'<option value="80000">80 kbit/sec</option>\n'+
'<option value="96000">96 kbit/sec</option>\n'+
'<option value="112000">112 kbit/sec</option>\n'+
'<option value="128000">128 kbit/sec</option>\n'+
'<option value="144000">144 kbit/sec</option>\n'+
'<option value="160000">160 kbit/sec</option>\n';
}
if(mpeg_ver==2.5) {
Id('bitrate').innerHTML=
'<option value="8000">8 kbit/sec</option>\n'+
'<option value="16000" selected>16 kbit/sec</option>\n'+
'<option value="24000">24 kbit/sec</option>\n'+
'<option value="32000">32 kbit/sec</option>\n'+
'<option value="40000">40 kbit/sec</option>\n'+
'<option value="48000">48 kbit/sec</option>\n'+
'<option value="56000">56 kbit/sec</option>\n'+
'<option value="64000">64 kbit/sec</option>\n';
}
}
function samplerateChanged()
{
if(Id('format').value=='2') {
SetLayerII();
}
if(Id('format').value=='3') {
SetLayerIII();
}
}
function formatChanged()
{
Id('bitrate').disabled=
!((Id('format').value=="2")||
(Id('format').value=="3"));
Id('quality').disabled=Id('format').value!="5";
samplerateChanged();
}

145
web/webget/webget.py Executable file
View File

@ -0,0 +1,145 @@
#!/usr/bin/python
from __future__ import print_function
import os
import sys
import subprocess
import tempfile
import urllib
import ConfigParser
import mysql.connector
def eprint(*args,**kwargs):
print(*args,file=sys.stderr,**kwargs)
def GetDbCredentials():
config=ConfigParser.ConfigParser()
config.readfp(open('/etc/rd.conf'))
return (config.get('mySQL','Loginname'),config.get('mySQL','Password'),
config.get('mySQL','Hostname'),config.get('mySQL','Database'))
def OpenDb():
creds=GetDbCredentials()
return mysql.connector.connect(user=creds[0],password=creds[1],
host=creds[2],database=creds[3],
buffered=True)
def CgiError(msg,resp_code):
print('Content-type: text/html')
print('Status: '+str(resp_code))
print()
print(msg)
sys.exit(0)
def AuthenticateUser(name,passwd,db):
sql='select WEBGET_LOGIN_PRIV from USERS where ';
sql+='LOGIN_NAME="'+name+'" && PASSWORD="'+passwd+'"'
q=db.cursor()
q.execute(sql)
if(len(q.fetchall())==0):
CgiError("Invalid User name or Password!",400)
#
# Main Function
#
title=''
samprate=48000
format='MP3'
bitrate=32000
quality=2
login_name=''
password=''
#
# Get call fields
#
f0=os.environ['QUERY_STRING'].split('&')
for field in f0:
f1=field.split('=')
if(f1[0]=='title'):
title=urllib.unquote(f1[1])
if(f1[0]=='samprate'):
samprate=int(urllib.unquote(f1[1]))
if(f1[0]=='format'):
format=urllib.unquote(f1[1])
if(f1[0]=='bitrate'):
bitrate=int(urllib.unquote(f1[1]))
if(f1[0]=='quality'):
quality=int(urllib.unquote(f1[1]))
if(f1[0]=='login_name'):
login_name=urllib.unquote(f1[1])
if(f1[0]=='password'):
password=urllib.unquote(f1[1])
login_name=login_name.replace('+',' ')
password=password.replace('+',' ')
db=OpenDb()
AuthenticateUser(login_name,password,db)
if(len(title)==0):
CgiError('No title specified!',400)
title=title.replace('+',' ')
#
# Generate file export
#
tempdir=tempfile.mkdtemp()
try:
filename=subprocess.check_output(('rdexport','--title='+title,
'--metadata-pattern=%t',
'--samplerate='+str(samprate),
'--format='+format,
'--bitrate='+str(bitrate),
'--quality='+str(quality),
tempdir))
except subprocess.CalledProcessError:
os.rmdir(tempdir)
print('Content-type: text/html')
print('Status: 500')
print()
print('500 - unable to execute rdexport(1)')
sys.exit(0)
if(len(filename)==0):
os.rmdir(tempdir)
print('Content-type: text/html')
print('Status: 404')
print()
print('404 - no cart with that title');
sys.exit(0)
filename=filename[0:len(filename)-1]
filepath=tempdir+'/'+filename
try:
mimetype=subprocess.check_output(('file','--brief','--mime-type',filepath))
except subprocess.CalledProcessError:
os.remove(filepath)
os.rmdir(tempdir)
print('Content-type: text/html')
print('Status: 500')
print()
print('500 - unable to determine output mime-type')
sys.exit(0)
#
# Render the output
#
try:
f=open(filepath,'r')
except IOError, reason:
print('Content-type: text/html')
print()
print('500 - unable to open exported file "'+filepath+'" ['+str(reason)+']')
sys.exit(0)
print('Content-Disposition: attachment; filename=\"'+filename+'\"')
print('Content-type: '+mimetype)
bytes=f.read(1024)
while(len(bytes)>0):
sys.stdout.write(bytes)
bytes=f.read(1024)
f.close()
os.remove(filepath)
os.rmdir(tempdir)