public function __construct($arg) { $config = \Slim\Slim::getInstance()->config['mpd']; $arg = join(DS, $arg); if (is_numeric($arg) === TRUE) { $t = \Slimpd\Track::getInstanceByAttributes(array('id' => (int) $arg)); if (is_object($t) === TRUE) { $this->absolutePath = $config['musicdir'] . $t->getRelativePath(); $this->fingerprint = $t->getFingerprint(); $this->ext = $t->getAudioDataFormat(); } } else { if (is_file($config['alternative_musicdir'] . $arg) === TRUE) { $arg = $config['alternative_musicdir'] . $arg; } if (is_file($config['musicdir'] . $arg) === TRUE) { $arg = $config['musicdir'] . $arg; } if (is_file($arg) === TRUE) { $this->absolutePath = $arg; $this->ext = pathinfo($arg, PATHINFO_EXTENSION); } } if (is_file($this->absolutePath) === FALSE) { // TODO: should we serve a default waveform svg? return NULL; } if (!preg_match("/^([a-f0-9]){32}\$/", $this->fingerprint)) { // extract the fingerprint if ($fp = \Slimpd\Importer::extractAudioFingerprint($this->absolutePath)) { $this->fingerprint = $fp; } else { # TODO: handle missing fingerprint die('invalid fingerprint: ' . $this->absolutePath); } } $this->setPeakFilePath(); if (is_file($this->peakValuesFilePath) === FALSE) { session_write_close(); // do not block other requests during processing $tmpFileName = APP_ROOT . 'cache' . DS . $this->ext . '.' . $this->fingerprint . '.'; if (is_file($tmpFileName . 'mp3') === TRUE || is_file($tmpFileName . 'wav') === TRUE) { # make sure same file isnt processed twice simultaneously by different client-requests... # TODO: send a message to client for requesting waveform again after a few seconds? # or sleep here until tmp files had been deleted? # or redirect to same route with increasing counter until a maximum is reached return NULL; } $this->generatePeakFile(); } }
public function cmd($cmd, $item = NULL) { // TODO: check access // @see: http://www.musicpd.org/doc/protocol/playback_commands.html // validate commands switch ($cmd) { case 'update': $config = \Slim\Slim::getInstance()->config['mpd']; # TODO: move 'disallow_full_database_update' from config.ini to user-previleges if (!$item && $config['disallow_full_database_update'] == '0') { return $this->mpd($cmd); } if (is_string($item) === TRUE) { $item = $item; } if (is_array($item) === TRUE) { $item = join(DS, $item); } if (is_file($config['musicdir'] . $item) === FALSE && is_dir($config['musicdir'] . $item) === FALSE) { // error - invalid $item return FALSE; } \Slimpd\importer::queDirectoryUpdate($item); return $this->mpd('update "' . str_replace("\"", "\\\"", $item) . '"'); // tracks that hasnt been importet in mpd database have to get inserted befor playing // TODO: should this also trigger a mysql-db-insert of this track? // TODO: should we allow this also for directories or limit this function to single music files? // tracks that hasnt been importet in mpd database have to get inserted befor playing // TODO: should this also trigger a mysql-db-insert of this track? // TODO: should we allow this also for directories or limit this function to single music files? case 'updateMpdAndPlay': $config = \Slim\Slim::getInstance()->config['mpd']; # TODO: move 'disallow_full_database_update' from config.ini to user-previleges if (!$item && $config['disallow_full_database_update'] == '0') { return $this->mpd($cmd); } if (is_string($item) === TRUE) { $item = $item; } if (is_array($item) === TRUE) { $item = join(DS, $item); } if (is_file($config['musicdir'] . $item) === FALSE) { // error - invalid $item or $item is a directory # TODO: send warning to client? return FALSE; } // now we have to find the nearest parent directory that already exists in mpd-database $closestExistingItemInMpdDatabase = $this->findClosestExistingItem($item); // special case when we try to play a single new file (without parent-dir) out of mpd root if ($closestExistingItemInMpdDatabase === NULL && $config['disallow_full_database_update'] == '1') { # TODO: send warning to client? return FALSE; } if ($closestExistingItemInMpdDatabase !== $item) { $this->cmd('update', $closestExistingItemInMpdDatabase); // TODO: replace dirty sleep with mpd-status-poll and continue as soon as the item is imported sleep(1); } return $this->cmd('addSelect', $item); case 'seekPercent': $currentSong = $this->mpd('currentsong'); $cmd = 'seek ' . $currentSong['Pos'] . ' ' . round($item * ($currentSong['Time'] / 100)) . ''; $this->mpd($cmd); case 'status': case 'stats': case 'currentsong': return $this->mpd($cmd); case 'play': case 'pause': case 'stop': case 'previous': case 'next': $this->mpd($cmd); break; case 'toggleRepeat': $status = $this->mpd('status'); $this->mpd('repeat ' . (int) ($status['repeat'] xor 1)); break; case 'toggleRandom': $status = $this->mpd('status'); $this->mpd('random ' . (int) ($status['random'] xor 1)); break; case 'toggleConsume': $status = $this->mpd('status'); $this->mpd('consume ' . (int) ($status['consume'] xor 1)); break; case 'playlistStatus': $this->playlistStatus(); break; case 'addSelect': # TODO: general handling of position to add # TODO: general handling of playing immediately or simply appending to playlist $path = ''; if (is_string($item) === TRUE) { $path = $item; } if (is_numeric($item) === TRUE) { $path = \Slimpd\Track::getInstanceByAttributes(array('id' => $item))->getRelativePath(); } if (is_array($item) === TRUE) { $path = join(DS, $item); } if (is_file(\Slim\Slim::getInstance()->config['mpd']['musicdir'] . $path) === TRUE) { $this->mpd('addid "' . str_replace("\"", "\\\"", $path) . '" 0'); $this->mpd('play 0'); } else { // trailing slash on directories will not work - lets remove it if (substr($path, -1) === DS) { $path = substr($path, 0, -1); } $this->mpd('add "' . str_replace("\"", "\\\"", $path) . '"'); } break; case 'playIndex': $this->mpd('play ' . $item); break; case 'deleteIndex': $this->mpd('delete ' . $item); break; case 'clearPlaylist': $this->mpd('clear'); break; case 'clearPlaylistNotCurrent': $status = $this->mpd('status'); $songId = isset($status['songid']) ? $status['songid'] : 0; if ($songId > 0) { // move current song to first position $this->mpd('moveid ' . $songId . ' 0'); $playlistLength = isset($status['playlistlength']) ? $status['playlistlength'] : 0; if ($playlistLength > 1) { $this->mpd('delete 1:' . $playlistLength); } } break; case 'playSelect': // playSelect(); // playSelect(); case 'addSelect': // addSelect(); // addSelect(); case 'deleteIndexAjax': // deleteIndexAjax(); // deleteIndexAjax(); case 'deletePlayed': // deletePlayed(); // deletePlayed(); case 'volumeImageMap': // volumeImageMap(); // volumeImageMap(); case 'toggleMute': // toggleMute(); // toggleMute(); case 'loopGain': // loopGain(); // loopGain(); case 'playlistTrack': // playlistTrack(); die('sorry, not implemented yet'); break; default: die('unsupported'); break; } }
foreach ($rows as $row) { $excerped = $cl->BuildExcerpts([$row['phrase']], 'slimpdautocomplete', $term); $filterType = $filterTypeMapping[$row['type']]; $entry = ['label' => $excerped[0], 'url' => $filterType === 'track' ? '/searchall/page/1/sort/relevance/desc?q=' . $row['phrase'] : '/library/' . $filterType . '/' . $row['itemid'], 'type' => $filterType, 'typelabel' => $app->ll->str($filterType), 'itemid' => $row['itemid']]; switch ($filterType) { case 'artist': case 'label': $entry['img'] = '/skin/default/img/icon-' . $filterType . '.png'; break; case 'album': case 'track': $entry['img'] = '/image-50/' . $filterType . '/' . $row['itemid']; break; } $result[] = $entry; } } #echo "<pre>" . print_r($result,1); die(); #echo "<pre>" . print_r($rows,1); die(); echo json_encode($result); exit; })->name('autocomplete'); $app->get('/deliver/:item+', function ($item) use($app, $config) { $path = join(DS, $item); if (is_numeric($path)) { $track = \Slimpd\Track::getInstanceByAttributes(array('id' => (int) $path)); $path = $track === NULL ? '' : $track->getRelativePath(); } deliver($app->config['mpd']['alternative_musicdir'] . $path, $app); $app->stop(); });