Skip to content
Permalink
Browse files

Add json playlist import/export facility (#127)

fixes #126
  • Loading branch information
RocketMan committed Jan 17, 2020
1 parent 543ab8c commit 08ad9c44d1895afea2c77cd9a227757a3d6a7ad5
Showing with 139 additions and 9 deletions.
  1. +7 −4 controllers/API.php
  2. +12 −1 controllers/ExportPlaylist.php
  3. +24 −0 engine/PlaylistEntry.php
  4. +96 −4 ui/Playlists.php
@@ -67,7 +67,7 @@ class API extends CommandTarget implements IController {
];

const PLAYLIST_DETAIL_FIELDS = [
"type", "comment", "artist", "track", "album", "label", "tag", "event", "code"
"type", "comment", "artist", "track", "album", "label", "tag", "event", "code", "created"
];

const TRACK_DETAIL_FIELDS = [
@@ -275,13 +275,16 @@ public function getPlaylists() {
Engine::api(IPlaylist::class)->getTracksWithObserver($id?$id:$row["id"],
(new PlaylistObserver())->onComment(function($entry) use(&$events) {
$events[] = ["type" => "comment",
"comment" => $entry->getComment()];
"comment" => $entry->getComment(),
"created" => $entry->getCreated()];
})->onLogEvent(function($entry) use(&$events) {
$events[] = ["type" => "logEvent",
"event" => $entry->getLogEventType(),
"code" => $entry->getLogEventCode()];
"code" => $entry->getLogEventCode(),
"created" => $entry->getCreated()];
})->onSetSeparator(function($entry) use(&$events) {
$events[] = ["type" => "break"];
$events[] = ["type" => "break",
"created" => $entry->getCreated()];
})->onSpin(function($entry) use(&$events) {
$spin = $entry->asArray();
$spin["type"] = "track";
@@ -37,6 +37,7 @@ class ExportPlaylist extends CommandTarget implements IController {
[ "html", "emitHTML" ],
[ "xml", "emitXML" ],
[ "csv", "emitCSV" ],
[ "json", "emitJSON" ],
];

private $dj;
@@ -48,8 +49,12 @@ class ExportPlaylist extends CommandTarget implements IController {
public function processRequest($dispatcher) {
// Ensure user has selected a playlist
$playlist = intval($_REQUEST["playlist"]);
if($playlist == 0)
if($playlist == 0) {
$referer = $_SERVER["HTTP_REFERER"];
if($referer)
header("Location: $referer");
exit;
}

// Get the show and DJ information
$row = Engine::api(IPlaylist::class)->getPlaylist($playlist, 1);
@@ -77,6 +82,12 @@ public function processLocal($action, $subaction) {
$this->dispatchAction($action, self::$actions);
}

public function emitJSON() {
header("Location: ".UI::getBaseUrl().
"zkapi.php?method=getPlaylistsRq&operation=byID&key=".
$_REQUEST["playlist"]."&json=1&includeTracks=1");
}

public function emitXML() {
header("Content-type: application/xml");
header("Content-disposition: attachment; filename=playlist.xml");
@@ -81,6 +81,30 @@ public function __call($name, $args) {
}
}

public static function fromJSON($json) {
$entry = new PlaylistEntry();
switch($json->type) {
case "break":
$entry->setSetSeparator();
break;
case "comment":
$entry->setComment($json->comment);
break;
case "logEvent":
$entry->setLogEvent($json->event, $json->code);
break;
case "track":
$entry->setArtist($json->artist);
$entry->setTrack($json->track);
$entry->setAlbum($json->album);
$entry->setLabel($json->label);
$entry->setTag($json->tag);
break;
}
$entry->setCreated($json->created);
return $entry;
}

/**
* return the type of this playlist entry
*
@@ -1546,7 +1546,8 @@ public function emitEditor() {

public function emitImportExportList() {
$menu[] = [ "u", "", "Export Playlist", "emitExportList" ];
$menu[] = [ "u", "import", "Import Playlist", "emitImportList" ];
$menu[] = [ "u", "importJSON", "Import Playlist (JSON)", "emitImportJSON" ];
$menu[] = [ "u", "importCSV", "Import Playlist (CSV)", "emitImportList" ];
$this->dispatchSubaction($this->action, $this->subaction, $menu);
}

@@ -1565,9 +1566,12 @@ public function emitExportList() {
</SELECT></TD></TR>
<TR><TD>
<B>Export As:</B>
<INPUT TYPE=RADIO NAME=format VALUE=csv CHECKED>CSV
<INPUT TYPE=RADIO NAME=format VALUE=json CHECKED>JSON
<INPUT TYPE=RADIO NAME=format VALUE=csv>CSV
<!--
<INPUT TYPE=RADIO NAME=format VALUE=xml>XML
<INPUT TYPE=RADIO NAME=format VALUE=html>HTML
-->
</TD></TR>
<TR><TD>
<INPUT TYPE=SUBMIT VALUE=" Export Playlist ">
@@ -1658,7 +1662,7 @@ public function emitImportList() {
<INPUT TYPE=HIDDEN NAME=button VALUE=" Setup New Airname... ">
<INPUT TYPE=HIDDEN NAME=session VALUE="<?php echo $this->session->getSessionID();?>">
<INPUT TYPE=HIDDEN NAME=action VALUE="importExport">
<INPUT TYPE=HIDDEN NAME=subaction VALUE="import">
<INPUT TYPE=HIDDEN NAME=subaction VALUE="importCSV">
<INPUT TYPE=HIDDEN NAME=playlist VALUE="<?php echo $playlist;?>">
<INPUT TYPE=HIDDEN NAME=description VALUE="<?php echo htmlentities(stripslashes($description));?>">
<INPUT TYPE=HIDDEN NAME=date VALUE="<?php echo htmlentities(stripslashes($date));?>">
@@ -1685,7 +1689,7 @@ public function emitImportList() {
?>
<FORM ENCTYPE="multipart/form-data" ACTION="?" METHOD=post>
<INPUT TYPE=HIDDEN NAME=action VALUE="importExport">
<INPUT TYPE=HIDDEN NAME=subaction VALUE="import">
<INPUT TYPE=HIDDEN NAME=subaction VALUE="importCSV">
<INPUT TYPE=HIDDEN NAME=session VALUE="<?php echo $this->session->getSessionID();?>">
<INPUT TYPE=HIDDEN NAME=validate VALUE="edit">
<INPUT TYPE=HIDDEN NAME=MAX_FILE_SIZE VALUE=100000>
@@ -1814,6 +1818,94 @@ public function emitImportList() {
}
}

public function emitImportJSON() {
$displayForm = true;
$userfile = $_FILES['userfile']['tmp_name'];
if($userfile) {
// read the JSON file
$file = "";
$fd = fopen($userfile, "r");
while(!self::zkfeof($fd, $tempbuf))
$file .= self::zkfgets($fd, 1024, $tempbuf);
fclose($fd);

// parse the file
$json = json_decode($file);

// validate json root node is type 'show'
if(!$json || $json->type != "show") {
// also allow for 'show' encapsulated within a 'getPlaylistsRs'
if($json && $json->data[0]->type == "show")
$json = $json->data[0];
else
echo "<B><FONT CLASS='error'>File is not in the expected format. Ensure file is a valid JSON playlist.</FONT></B><BR>\n";
}

if($json && $json->type == "show") {
// validate the show's properties
$valid = false;
list($year, $month, $day) = explode("-", $json->date);
if($json->airname && $json->name && $json->time &&
checkdate($month, $day, $year))
$valid = true;

// lookup the airname
if($valid) {
$airname = Engine::api(IDJ::class)->getAirname($json->airname, $this->session->getUser());
if(!$airname)
$valid = false;
}

// create the playlist
if($valid) {
$papi = Engine::api(IPlaylist::class);
$papi->insertPlaylist($this->session->getUser(), $json->date, $json->time, $json->name, $airname);
$playlist = Engine::lastInsertId();

// insert the tracks
foreach($json->data as $pentry) {
$entry = PlaylistEntry::fromJSON($pentry);
$papi->insertTrackEntry($playlist, $entry, false);
// if there is a timestamp, set it via update
if($pentry->created)
$papi->updateTrackEntry($playlist, $entry);
}

// display the editor
$_REQUEST["playlist"] = $playlist;
$this->action = "newListEditor";
$this->emitEditor();
$displayForm = false;
} else
echo "<B><FONT CLASS='error'>Show details are invalid.</FONT></B><BR>\n";
}
}

if($displayForm) {
?>
<FORM ENCTYPE="multipart/form-data" ACTION="?" METHOD=post>
<INPUT TYPE=HIDDEN NAME=action VALUE="importExport">
<INPUT TYPE=HIDDEN NAME=subaction VALUE="importJSON">
<INPUT TYPE=HIDDEN NAME=session VALUE="<?php echo $this->session->getSessionID();?>">
<INPUT TYPE=HIDDEN NAME=MAX_FILE_SIZE VALUE=100000>
<TABLE CELLPADDING=2 CELLSPACING=0>
<TR>
<TD ALIGN=RIGHT>Import from file:</TD><TD><INPUT NAME=userfile TYPE=file></TD>
</TR><TR>
<TD>&nbsp;</TD>
<TD CLASS="sub">NOTE: File must be a UTF-8 encoded JSON playlist,
such as previously exported via Export Playlist.</TD>
</TR><TR>
<TD>&nbsp;</TD>
<TD><INPUT TYPE=submit VALUE=" Import Playlist "></TD>
</TR>
</TABLE>
</FORM>
<?php
UI::setFocus("userfile");
}
}

public function updateDJInfo() {
$validate = $_REQUEST["validate"];
$multi = $_REQUEST["multi"];

0 comments on commit 08ad9c4

Please sign in to comment.
You can’t perform that action at this time.