Compare commits

...

10 Commits

Author SHA1 Message Date
68ac9a2005 customization: dark theme, auto-refresh back to index 5 s after submit 2022-08-11 13:36:18 -07:00
8512b62cb4 local settings 2022-08-09 22:20:50 -07:00
T. Isaac Lightburn
f928d788f9 Update README.md 2018-12-21 03:49:35 -06:00
T. Isaac Lightburn
b445ae0920 Fix changing accepting in OpenKJ not affecting accepting status 2018-02-08 11:12:37 -06:00
T. Isaac Lightburn
ff7b0e0502 Merge branch 'master' of https://github.com/OpenKJ/StandaloneRequestServer 2018-02-08 11:05:49 -06:00
T. Isaac Lightburn
162a0f1ec1 Make git ignore db file in source dir 2018-02-08 11:04:11 -06:00
T. Isaac Lightburn
561d8855ee Delete okjweb.db 2018-02-08 10:59:10 -06:00
T. Isaac Lightburn
1a245160d8 Update README.md 2018-02-07 20:53:43 -06:00
T. Isaac Lightburn
4ee4e3cc0a Update README.md 2018-02-07 20:51:44 -06:00
T. Isaac Lightburn
2b9c644016 Initial import 2018-02-07 20:48:28 -06:00
10 changed files with 646 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*~

View File

@@ -1,9 +1,14 @@
# OpenKJ Standalone Request Server
Standalone basic single-venue request server implementation for use with OpenKJ.
Note: This is intended for people who already know how to configure and manage their own webservers and have a general familiarity with php. The easier and more feature rich option is to use the hosted service available at https://okjsongbook.com
Requires php
Can be run under either php's built in web server or under any web server with php support like apache or nginx.
Ignores any API key specified in the OpenKJ.
If you were serving this from a web server as http://10.0.0.1/requestserver, you would configure the server URL in OpenKJ to point to http://10.0.0.1/requestserver/api.php
settings.inc should be edited with an appropriate database path that the webserver has write access to. If the database file doesn't exist, it will be created automatically.

223
api.php Normal file
View File

@@ -0,0 +1,223 @@
<?php
include_once("global.inc");
$json = file_get_contents("php://input");
$data = json_decode($json,true);
$command = $data['command'];
if ($command == '')
{
exit();
}
// API stuff for songbook mobile apps
if ($command == "venueExists")
{
$venueUrlName = $data['venueUrlName'];
$exists = venueExists($venueUrlName);
$output = array('command'=>$command,'error'=>'false', 'exists'=>$exists);
print(json_encode($output,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
exit();
}
if ($command == "venueAccepting")
{
if (getAccepting())
$output = array('command'=>$command,'accepting'=>true);
else
$output = array('command'=>$command,'accepting'=>false);
header('Content-type: application/json');
print(json_encode($output,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
exit();
}
if ($command == "submitRequest")
{
$songId = $data['songId'];
$singerName = $data['singerName'];
$sql = "SELECT artist,title FROM songdb WHERE song_id = $songId";
foreach ($db->query($sql) as $row) {
$artist = $row['artist'];
$title = $row['title'];
}
$stmt = $db->prepare("INSERT INTO requests (singer,artist,title) VALUES(:singerName, :artist, :title)");
$stmt->execute(array(":singerName" => $singerName, ":artist" => $artist, ":title" => $title));
newSerial();
$output = array('command'=>$command,'error'=>'false', 'success'=>true);
print(json_encode($output,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
exit();
}
if ($command == "search")
{
$terms = explode(' ',$data['searchString']);
$no = count($terms);
$wherestring = '';
if ($no == 1) {
$wherestring = "WHERE (combined LIKE \"%" . $terms[0] . "%\")";
} elseif ($no >= 2) {
foreach ($terms as $i => $term) {
if ($i == 0) {
$wherestring .= "WHERE ((combined LIKE \"%" . $term . "%\")";
}
if (($i > 0) && ($i < $no - 1)) {
$wherestring .= " AND (combined LIKE \"%" . $term . "%\")";
}
if ($i == $no - 1) {
$wherestring .= " AND (combined LIKE \"%" . $term . "%\") AND(artist<>'DELETED'))";
}
}
} else {
$wherestring = "";
}
$entries = null;
$res = array();
$sql = "SELECT song_id,artist,title,combined FROM songdb $wherestring ORDER BY UPPER(artist), UPPER(title)";
foreach ($db->query($sql) as $row)
{
if ((stripos($row['combined'],'wvocal') === false) && (stripos($row['combined'],'w-vocal') === false) && (stripos($row['combined'],'vocals') === false)) {
$res[] = array('song_id'=>$row['song_id'],'artist'=>$row['artist'],'title'=>$row['title']);
}
}
$output = array("command" => "search", "songs" => $res);
header('Content-type: application/json');
print(json_encode($output,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
exit();
}
// API stuff for OpenKJ application
if ($command == "clearDatabase")
{
$db->exec("DELETE FROM songdb");
$db->exec("DELETE FROM requests");
$newSerial = newSerial();
$output = array('command'=>$command,'error'=>'false', 'serial'=>newSerial());
print(json_encode($output,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
exit();
}
function error($error_string) {
header('Content-type: application/json');
print(json_encode(array('command'=>$command,'error'=>'true','errorString'=>$error_string),JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
exit();
}
if ($command == "clearRequests")
{
$db->exec("DELETE FROM requests");
$output = array('command'=>$command,'error'=>'false', 'serial'=>newSerial());
print(json_encode($output,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
exit();
}
if ($command == "deleteRequest")
{
$request_id = $data['request_id'];
$stmt = $db->prepare("DELETE FROM requests WHERE request_id = :requestId");
$stmt->execute(array(":requestId" => $request_id));
$output = array('command'=>$command,'error'=>'false', 'serial'=>newSerial());
print(json_encode($output,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
exit();
}
if ($command == "connectionTest")
{
header('Content-type: application/json');
$output = array('command'=>$command,'connection'=>'ok');
print(json_encode($output,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
exit();
}
if ($command == "addSongs")
{
$stmt = $db->prepare("INSERT OR IGNORE INTO songdb (artist, title, combined) VALUES (:artist, :title, :combined)");
$db->beginTransaction();
$errors = array();
$count = 0;
$artist = "";
$title = "";
$combined = "";
$error = "false";
foreach ($data['songs'] as $song)
{
$artist = $song['artist'];
$title = $song['title'];
$combined = $artist . " " . $title;
$inarray = array(":artist" => $artist, ":title" => $title, ":combined" => $combined);
$result = $stmt->execute($inarray);
if ($result === false)
{
$errors[] = $db->errorInfo();
$error = "true";
}
$count++;
}
$result = $db->commit();
if ($result == false)
$errors[] = $db->errorInfo();
$output['command'] = $command;
$output['error'] = $error;
$output['errors'] = $errors;
$output['entries processed'] = $count;
$output['last_artist'] = $artist;
$output['last_title'] = $title;
header('Content-type: application/json');
print(json_encode($output,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
exit();
}
if ($command == "getSerial")
{
$output = array('command'=>$command,'serial'=>getSerial(),'error'=>'false');
header('Content-type: application/json');
print(json_encode($output,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
exit();
}
if ($command == "getAccepting")
{
$accepting = getAccepting();
$output = array('command'=>$command,'accepting'=>$venue['accepting'],'venue_id'=>0);
header('Content-type: application/json');
print(json_encode($output,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
exit();
}
if ($command == "setAccepting")
{
$accepting = (bool)$data['accepting'];
setAccepting($accepting);
$newSerial = newSerial();
$output = array('command'=>$command,'error'=>'false','venue_id'=>0,'accepting'=>$accepting,'serial'=>$newSerial);
print(json_encode($output,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
exit();
}
if ($command == "getVenues")
{
$output = getVenues();
$output['command'] = $command;
$output['error'] = 'false';
header('Content-type: application/json');
print(json_encode($output,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
exit();
}
if ($command == "getRequests")
{
$serial = getSerial();
$output = getRequests();
$output['command'] = $command;
$output['error'] = 'false';
$output['serial'] = $serial;
header('Content-type: application/json');
print(json_encode($output,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));
exit();
}
?>

166
global.inc Normal file
View File

@@ -0,0 +1,166 @@
<?php
include("settings.inc");
$db = new PDO("sqlite:$dbFilePath");
$db->exec("CREATE TABLE IF NOT EXISTS songdb (song_id integer PRIMARY KEY AUTOINCREMENT, artist text, title TEXT, combined TEXT UNIQUE)");
$db->exec("CREATE TABLE IF NOT EXISTS state (accepting bool, serial integer NOT NULL)");
$db->exec("INSERT OR IGNORE INTO state (rowid,accepting,serial) VALUES(0,0,1)");
$db->exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_songstrings ON songdb(combined)");
$db->exec("CREATE TABLE IF NOT EXISTS requests (request_id integer PRIMARY KEY AUTOINCREMENT, artist TEXT, title TEXT, singer TEXT, request_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP)");
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$songdbtable = "songdb";
$requeststable = "requests";
if (isset($_SERVER['REFERER'])) $referer = $_SERVER['REFERER'];
function siteheader($title)
{
global $venueName;
global $screensize;
echo "<html><head>
<link href='https://fonts.googleapis.com/css?family=Audiowide' rel='stylesheet'>
<link href='https://fonts.googleapis.com/css?family=Roboto' rel='stylesheet'>
<title>$venueName Karaoke Songbook</title>
<link rel=stylesheet type=\"text/css\" href=venuestyle.css />
<script type=\"text/javascript\">
function submitreq(varid){
window.location = \"./submitreq.php?id=\" + varid;
}
</script>
</head><body>";
}
function sitefooter() {
echo "</div></body></html>";
}
function navbar($backurl)
{
if ($backurl == "")
$backurl = index.php;
global $screensize;
echo "<div class=navbar>
<span class=title>City Karaoke Songbook</span>
</div><div class=mainbody><span class=backbtn><a class=button href=\"$backurl\" class=navbar id=backlink>Back</a></span>";
}
function setAccepting($accepting)
{
global $db;
if ($accepting == 1)
{
echo("setting accepting to 1");
$db->exec("UPDATE state SET accepting=1");
}
else
{
echo("setting accepting to 0");
$db->exec("UPDATE state SET accepting=0");
}
}
function getAccepting()
{
global $db;
$accepting = false;
foreach ($db->query("SELECT accepting FROM state LIMIT 1") as $row)
{
$accepting = $row['accepting'];
}
return $accepting;
}
function searchform()
{
global $db;
global $venue_id;
if (!getAccepting())
{
echo "<br><br><h2>Sorry, requests are not being accepted at this time</h2>";
}
else
{
global $url_name;
global $screensize;
echo "<br><p><form method=get action=search.php>Song search: <input type=text name=q autofocus autocomplete=off><input type=submit value=Search></form></p>";
echo '<p class=info>You may enter any part of the artist and/or title of the song. Partial words are allowed.</p>
<p class=info>For example "pai bla stone" would match "Rolling Stones, The - Paint it black".</p>';
}
}
function getSerial()
{
global $db;
$serial = -1;
foreach ($db->query("SELECT serial FROM state LIMIT 1") as $row)
{
$serial = (int)$row['serial'];
}
return $serial;
}
function newSerial()
{
global $db;
$serial = getSerial();
$newSerial = mt_rand(0,99999);
while ($newSerial == $serial)
{
$newSerial = mt_rand(0,99999);
}
$db->exec("UPDATE state SET serial=$newSerial");
return $newSerial;
}
function getVenue()
{
// We don't really do multiple venues in standalone, just fake it
global $db;
global $venueName;
$serial = -1;
$venue = array();
$venue['venue_id'] = $venue_id;
$venue['accepting'] = getAccepting();
$venue['name'] = $venueName;
$venue['url_name'] = "none";
return $venue;
}
function getVenues()
{
// We don't really do multiple venues in standalone, just fake it
global $db;
global $venueName;
$venues = array();
$venue['venue_id'] = 0;
$venue['accepting'] = getAccepting();
$venue['name'] = $venueName;
$venue['url_name'] = "none";
$venues['venues'][] = $venue;
return $venues;
}
function getRequests()
{
global $db;
$requests = array();
$result = $db->query("SELECT request_id,artist,title,singer,strftime('%s', request_time) AS unixtime FROM requests");
if ($result)
{
foreach ($result as $row)
{
$request['request_id'] = (int)$row['request_id'];
$request['artist'] = $row['artist'];
$request['title'] = $row['title'];
$request['singer'] = $row['singer'];
$request['request_time'] = (int)$row['unixtime'];
$requests['requests'][] = $request;
}
}
return $requests;
}
?>

16
index.php Normal file
View File

@@ -0,0 +1,16 @@
<?php
include("global.inc");
siteheader("Home");
navbar("index.php");
searchform();
/*
if ($screensize == 'xlarge')
{
echo "<br><br><p class=info>Want to search using your smartphone, tablet, or laptop?<br><br>Browse to songbook.openkj.org/venue/$url_name in the web browser on your device.<br><br>
";
}
*/
sitefooter();
?>

69
search.php Normal file
View File

@@ -0,0 +1,69 @@
<?php
include('global.inc');
siteheader('Search Results');
navbar("index.php");
if ($_GET['q'] == '') {
echo "<p>You must enter at least one search term</p>";
die();
}
if (strlen($_GET['q']) < 3)
{
echo '<p>Your search string was too short, please try again</p>';
die();
}
echo '<br><p>Search Results<br>Click a song to submit it</p>';
$terms = explode(' ',$_GET['q']);
$no = count($terms);
$wherestring = '';
if ($no == 1) {
$wherestring = "WHERE (combined LIKE \"%" . $terms[0] . "%\")";
} elseif ($no >= 2) {
foreach ($terms as $i => $term) {
if ($i == 0) {
$wherestring .= "WHERE ((combined LIKE \"%" . $term . "%\")";
}
if (($i > 0) && ($i < $no - 1)) {
$wherestring .= " AND (combined LIKE \"%" . $term . "%\")";
}
if ($i == $no - 1) {
$wherestring .= " AND (combined LIKE \"%" . $term . "%\") AND(artist != 'DELETED'))";
}
}
} else {
echo "<li>You must enter at least one search term</li>";
die();
}
$entries = null;
$res = array();
$sql = "SELECT song_id,artist,title,combined FROM songdb $wherestring ORDER BY UPPER(artist), UPPER(title)";
foreach ($db->query($sql) as $row)
{
if ((stripos($row['combined'],'wvocal') === false) && (stripos($row['combined'],'w-vocal') === false) && (stripos($row['combined'],'vocals') === false)) {
$res[$row['song_id']] = $row['artist'] . " - " . $row['title'];
}
}
$db = null;
$unique = array_unique($res);
foreach ($unique as $key => $val) {
$entries[] = "<tr><td class=result onclick=\"submitreq(${key})\">" . $val . "</td></tr>";
}
if (count($unique) > 0) {
echo '<table border=1>';
foreach ($entries as $song) {
echo $song;
}
echo '</table>';
} else {
echo "<p>Sorry, no match found.</p>";
}
sitefooter();
?>

5
settings.inc Normal file
View File

@@ -0,0 +1,5 @@
<?php
$venueName = 'City Karaoke';
$dbFilePath = '/home/salfter/StandaloneRequestServer/okjweb.db';
?>

35
submitreq-run.php Normal file
View File

@@ -0,0 +1,35 @@
<?php
include('global.inc');
header("Refresh: 5; url=index.php");
siteheader("Song Submitted");
$songid = $_GET['songid'];
$singer = $_GET['singer'];
if ($singer == '') {
navbar($_SERVER['HTTP_REFERER']);
echo "<p>Sorry, you must input a singer name. Please go back and try again.</p>";
die();
}
navbar("index.php");
$entries = null;
$wherestring = null;
$artist = '';
$title = '';
$sql = "SELECT artist,title FROM songdb WHERE song_id = $songid";
foreach ($db->query($sql) as $row) {
$artist = $row['artist'];
$title = $row['title'];
}
$stmt = $db->prepare("INSERT INTO requests (singer,artist,title) VALUES(:singer, :artist, :title)");
$stmt->execute(array(":singer" => $singer, ":artist" => $artist, ":title" => $title));
newSerial();
echo "<p>Song: $artist - $title</p>
<p>Submitted for singer: $singer</p>";
// <br><p>Please press back to return to the main screen</p>
//";
sitefooter();
?>

25
submitreq.php Normal file
View File

@@ -0,0 +1,25 @@
<?php
include('global.inc');
siteheader('Submit Request');
$referer = $_SERVER['HTTP_REFERER'];
if (strpos($referer,'submitreq-run.php?screensize=$screensize') !== false)
{
navbar("index.php");
} else {
navbar($referer);
}
$songid = $_GET['id'];
$artist = '';
$title = '';
$sql = "SELECT artist,title FROM songdb WHERE song_id = $songid";
foreach ($db->query($sql) as $row) {
$artist = $row['artist'];
$title = $row['title'];
}
$db = null;
echo "<br><p>Submitting Song:<br>";
echo "<p>$artist - $title</p>";
echo "<form method=get action=submitreq-run.php><input type=hidden name=screensize value=$screensize><input type=hidden name=songid value=$songid>Please enter your name or nickname:<br><input type=text name=singer autocomplete=off autofocus><input type=submit></form>";
echo "<p class=info>If you have a common first name, please also enter your last initial or last name.<br>Doing so will help eliminate confusion and reduce the risk of your turn getting skipped.";
?>

101
venuestyle.css Normal file
View File

@@ -0,0 +1,101 @@
@import url(http://fonts.googleapis.com/css?family=Scada);
@import url(http://fonts.googleapis.com/css?family=Audiowide);
body
{
text-align: center;
font-family: 'Scada', sans-serif;
font-size: 1.2em;
font-weight: bold;
background-color: #202020;
color: white;
padding-top: 60px;
}
input[type=text] {
border: 2px solid #202020;
padding-top: 5px;
padding-bottom: 5px;
margin-right: 5px;
font-size: 1.2em;
font-weight: bold;
color: white;
background-color: #404040;
}
p.info
{
font-size: 1.0em;
}
div.navbar
{
# height: 50px;
width: 100%;
position: fixed;
left: 0;
top: 0;
padding-left: 5px;
padding-top: 5px;
padding-bottom: 5px;
padding-right: 5px
width: 100%;
background-color: #202020;
color: white;
text-align: left;
border-style: solid;
border-width: 0px 0px 2px 0px;
}
a.navbar
{
color: #202020;;
text-decoration: none;
}
div.spacer
{
position: relative;
width: 90%;
top: 0;
height: 50px;
}
table
{
margin: auto;
min-width: 60%;
border: 1px;
}
td.result
{
color: white;
background-color: #202020;
font-size: 1.6em;
font-weight: bold;
text-align: left;
padding: 20px;
border-spacing: 10px;
border-style: solid;
border-size: 2px;
}
input[type=button], input[type=submit], input[type=reset], a.button {
background-color: #202020;
border: 2px;
border-style: solid;
border-color: white;
font-weight: bold;
color: white;
padding: 8px 20px;
margin-top: 12px;
text-decoration: none;
font-size: 1.2em;
cursor: pointer;
}
.backbtn {
position: fixed;
top: 75px;
left: 5px;
}
.title {
font-size: 2em;
font-family: Audiowide, cursive;
float: right;
margin-right: 10px;
text-shadow: 4px 4px 4px #aaa;
}