mirror of
https://github.com/ElvishArtisan/rivendell.git
synced 2025-04-07 17:32:20 +02:00
2018-05-24 Fred Gleason <fredg@paravelsystems.com>
* Added a WebGet web service.
This commit is contained in:
parent
97568f0c0d
commit
9e215e311f
@ -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.
|
||||
|
@ -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 \
|
||||
|
@ -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
BIN
icons/webget_logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
@ -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
|
||||
|
@ -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
52
web/webget/Makefile.am
Normal 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
362
web/webget/webget.cpp
Normal 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
55
web/webget/webget.h
Normal 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
106
web/webget/webget.html
Normal 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"> </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"> </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"> </td></tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td><input type="submit" value="OK"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
187
web/webget/webget.js
Normal file
187
web/webget/webget.js
Normal 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
145
web/webget/webget.py
Executable 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)
|
Loading…
x
Reference in New Issue
Block a user