diff --git a/airtime_mvc/application/configs/constants.php b/airtime_mvc/application/configs/constants.php
index 95c8b3263..df5ef7741 100644
--- a/airtime_mvc/application/configs/constants.php
+++ b/airtime_mvc/application/configs/constants.php
@@ -4,19 +4,31 @@ define('AIRTIME_VERSION', '1.9.0-devel');
define('AIRTIME_COPYRIGHT_DATE', '2010-2011');
define('AIRTIME_REST_VERSION', '1.1');
-// Metadata Keys
-define('UI_MDATA_KEY_TITLE', 'dc:title');
-define('UI_MDATA_KEY_CREATOR', 'dc:creator');
-define('UI_MDATA_KEY_SOURCE', 'dc:source');
-define('UI_MDATA_KEY_DURATION', 'dcterms:extent');
-define('UI_MDATA_KEY_URL', 'ls:url');
-define('UI_MDATA_KEY_FORMAT', 'dc:format');
-define('UI_MDATA_KEY_DESCRIPTION', 'dc:description');
-define('UI_MDATA_KEY_CHANNELS', 'ls:channels');
-define('UI_MDATA_KEY_SAMPLERATE', 'ls:samplerate');
-define('UI_MDATA_KEY_BITRATE', 'ls:bitrate');
-define('UI_MDATA_KEY_ENCODER', 'ls:encoder');
-define('UI_MDATA_KEY_FILENAME', 'ls:filename');
+// Metadata Keys for files
+define('MDATA_KEY_FILEPATH', 'filepath');
+define('MDATA_KEY_MD5', 'md5');
+define('MDATA_KEY_TITLE', 'track_title');
+define('MDATA_KEY_CREATOR', 'artist_name');
+define('MDATA_KEY_SOURCE', 'album_title');
+define('MDATA_KEY_DURATION', 'length');
+define('MDATA_KEY_MIME', 'mime');
+define('MDATA_KEY_FTYPE', 'ftype');
+define('MDATA_KEY_URL', 'url');
+define('MDATA_KEY_GENRE', 'genre');
+define('MDATA_KEY_MOOD', 'mood');
+define('MDATA_KEY_LABEL', 'label');
+define('MDATA_KEY_COMPOSER', 'composer');
+define('MDATA_KEY_FORMAT', 'format');
+define('MDATA_KEY_DESCRIPTION', 'description');
+define('MDATA_KEY_SAMPLERATE', 'sample_rate');
+define('MDATA_KEY_BITRATE', 'bit_rate');
+define('MDATA_KEY_ENCODER', 'encoded_by');
+define('MDATA_KEY_ISRC', 'isrc_number');
+define('MDATA_KEY_COPYRIGHT', 'copyright');
+define('MDATA_KEY_YEAR', 'year');
+define('MDATA_KEY_BPM', 'bpm');
+define('MDATA_KEY_TRACKNUMBER', 'track_number');
+define('MDATA_KEY_CONDUCTOR', 'conductor');
define('UI_MDATA_VALUE_FORMAT_FILE', 'File');
define('UI_MDATA_VALUE_FORMAT_STREAM', 'live stream');
diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php
index 422fa608c..c6d7dfc13 100644
--- a/airtime_mvc/application/controllers/ApiController.php
+++ b/airtime_mvc/application/controllers/ApiController.php
@@ -10,6 +10,8 @@ class ApiController extends Zend_Controller_Action
$context->addActionContext('version', 'json')
->addActionContext('recorded-shows', 'json')
->addActionContext('upload-recorded', 'json')
+ ->addActionContext('media-monitor-setup', 'json')
+ ->addActionContext('media-item-status', 'json')
->addActionContext('reload-metadata', 'json')
->initContext();
}
@@ -77,7 +79,7 @@ class ApiController extends Zend_Controller_Action
if (ctype_alnum($file_id) && strlen($file_id) == 32) {
$media = StoredFile::RecallByGunid($file_id);
if ($media != null && !PEAR::isError($media)) {
- $filepath = $media->getRealFilePath();
+ $filepath = $media->getFilePath();
if(!is_file($filepath))
{
header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
@@ -293,17 +295,17 @@ class ApiController extends Zend_Controller_Action
print 'You are not allowed to access this resource.';
exit;
}
-
+
$showCanceled = false;
$show_instance = $this->_getParam('show_instance');
-
+
$upload_dir = ini_get("upload_tmp_dir");
$file = StoredFile::uploadFile($upload_dir);
-
+
$show_name = "";
try {
$show_inst = new ShowInstance($show_instance);
-
+
$show_inst->setRecordedFile($file->getId());
$show_name = $show_inst->getName();
$show_genre = $show_inst->getGenre();
@@ -317,12 +319,12 @@ class ApiController extends Zend_Controller_Action
//the library), now lets just return.
$showCanceled = true;
}
-
+
$tmpTitle = !(empty($show_name))?$show_name."-":"";
$tmpTitle .= $file->getName();
-
+
$file->setMetadataValue(UI_MDATA_KEY_TITLE, $tmpTitle);
-
+
if (!$showCanceled && Application_Model_Preference::GetDoSoundCloudUpload())
{
for ($i=0; $i<$CC_CONFIG['soundcloud-connection-retries']; $i++) {
@@ -353,8 +355,57 @@ class ApiController extends Zend_Controller_Action
$this->view->id = $file->getId();
}
- public function reloadMetadataAction() {
+ public function mediaMonitorSetupAction() {
+ global $CC_CONFIG;
+ // disable the view and the layout
+ $this->view->layout()->disableLayout();
+ $this->_helper->viewRenderer->setNoRender(true);
+
+ $api_key = $this->_getParam('api_key');
+ if (!in_array($api_key, $CC_CONFIG["apiKey"]))
+ {
+ header('HTTP/1.0 401 Unauthorized');
+ print 'You are not allowed to access this resource.';
+ exit;
+ }
+
+ $plupload_dir = ini_get("upload_tmp_dir") . DIRECTORY_SEPARATOR . "plupload";
+
+ //need to make sure plupload dir exists so we can watch it.
+ if(!file_exists($plupload_dir)) {
+ @mkdir($plupload_dir, 0755);
+ }
+
+ $this->view->stor = $CC_CONFIG['storageDir'];
+ $this->view->plupload = $plupload_dir;
+ }
+
+ public function mediaItemStatusAction() {
+ global $CC_CONFIG;
+
+ $api_key = $this->_getParam('api_key');
+ if (!in_array($api_key, $CC_CONFIG["apiKey"]))
+ {
+ header('HTTP/1.0 401 Unauthorized');
+ print 'You are not allowed to access this resource.';
+ exit;
+ }
+
+ $md5 = $this->_getParam('md5');
+ $file = StoredFile::RecallByMd5($md5);
+
+ //New file added to Airtime
+ if (is_null($file)) {
+ $this->view->airtime_status = 0;
+ }
+ else {
+ $this->view->airtime_status = 1;
+ }
+
+ }
+
+ public function reloadMetadataAction() {
global $CC_CONFIG;
$api_key = $this->_getParam('api_key');
@@ -366,22 +417,62 @@ class ApiController extends Zend_Controller_Action
}
$md = $this->_getParam('md');
- $filepath = $md['filepath'];
- $filepath = str_replace("\\", "", $filepath);
- $file = StoredFile::Recall(null, null, null, $filepath);
- if (PEAR::isError($file) || is_null($file)) {
- $this->view->response = "File not in Airtime's Database";
- return;
+ $mode = $this->_getParam('mode');
+
+ if ($mode == "create") {
+ $md5 = $md['MDATA_KEY_MD5'];
+ $file = StoredFile::RecallByMd5($md5);
+
+ if (is_null($file)) {
+ $file = StoredFile::Insert($md);
+ }
+ else {
+ $this->view->error = "File already exists in Airtime.";
+ return;
+ }
+ }
+ else if ($mode == "modify") {
+ $filepath = $md['MDATA_KEY_FILEPATH'];
+ $filepath = str_replace("\\", "", $filepath);
+ $file = StoredFile::RecallByFilepath($filepath);
+
+ //File is not in database anymore.
+ if (is_null($file)) {
+ $this->view->error = "File does not exist in Airtime.";
+ return;
+ }
+ //Updating a metadata change.
+ else {
+ $file->setMetadata($md);
+ }
+ }
+ else if ($mode == "moved") {
+ $md5 = $md['MDATA_KEY_MD5'];
+ $file = StoredFile::RecallByMd5($md5);
+
+ if (is_null($file)) {
+ $this->view->error = "File doesn't exist in Airtime.";
+ return;
+ }
+ else {
+ $file->setMetadata($md);
+ }
+ }
+ else if ($mode == "delete") {
+ $filepath = $md['MDATA_KEY_FILEPATH'];
+ $filepath = str_replace("\\", "", $filepath);
+ $file = StoredFile::RecallByFilepath($filepath);
+
+ if (is_null($file)) {
+ $this->view->error = "File doesn't exist in Airtime.";
+ return;
+ }
+ else {
+ $file->delete();
+ }
}
- $res = $file->replaceDbMetadata($md);
-
- if (PEAR::isError($res)) {
- $this->view->response = "Metadata Change Failed";
- }
- else {
- $this->view->response = "Success!";
- }
+ $this->view->id = $file->getId();
}
}
diff --git a/airtime_mvc/application/controllers/LibraryController.php b/airtime_mvc/application/controllers/LibraryController.php
index 180402d7c..cc45408bf 100644
--- a/airtime_mvc/application/controllers/LibraryController.php
+++ b/airtime_mvc/application/controllers/LibraryController.php
@@ -78,7 +78,7 @@ class LibraryController extends Zend_Controller_Action
$file_id = $this->_getParam('id', null);
$file = StoredFile::Recall($file_id);
- $url = $file->getFileURL().'/api_key/'.$CC_CONFIG["apiKey"][0].'/download/true';
+ $url = $file->getFileUrl().'/api_key/'.$CC_CONFIG["apiKey"][0].'/download/true';
$menu[] = array('action' => array('type' => 'gourl', 'url' => $url),
'title' => 'Download');
@@ -162,18 +162,18 @@ class LibraryController extends Zend_Controller_Action
if ($form->isValid($request->getPost())) {
$formdata = $form->getValues();
- $file->replaceDbMetadata($formdata);
+ $file->setDbColMetadata($formdata);
$data = $formdata;
- $data['filepath'] = $file->getRealFilePath();
- //wait for 1.9.0 release
- //RabbitMq::SendFileMetaData($data);
+ $data['filepath'] = $file->getFilePath();
+
+ RabbitMq::SendFileMetaData($data);
$this->_helper->redirector('index');
}
}
- $form->populate($file->md);
+ $form->populate($file->getDbColMetadata());
$this->view->form = $form;
}
diff --git a/airtime_mvc/application/controllers/PluploadController.php b/airtime_mvc/application/controllers/PluploadController.php
index 56c07bc23..d8fc10c08 100644
--- a/airtime_mvc/application/controllers/PluploadController.php
+++ b/airtime_mvc/application/controllers/PluploadController.php
@@ -25,9 +25,13 @@ class PluploadController extends Zend_Controller_Action
public function uploadAction()
{
$upload_dir = ini_get("upload_tmp_dir") . DIRECTORY_SEPARATOR . "plupload";
- $file = StoredFile::uploadFile($upload_dir);
+ $res = StoredFile::uploadFile($upload_dir);
- die('{"jsonrpc" : "2.0", "id" : '.$file->getId().' }');
+ if (isset($res)) {
+ die('{"jsonrpc" : "2.0", "id" : '.$file->getMessage().' }');
+ }
+
+ die('{"jsonrpc" : "2.0"}');
}
}
diff --git a/airtime_mvc/application/models/RabbitMq.php b/airtime_mvc/application/models/RabbitMq.php
index 170395f76..ee70ae6c5 100644
--- a/airtime_mvc/application/models/RabbitMq.php
+++ b/airtime_mvc/application/models/RabbitMq.php
@@ -40,9 +40,6 @@ class RabbitMq
}
}
-/*
-* wait for 1.9.0 release
-
public static function SendFileMetaData($md)
{
global $CC_CONFIG;
@@ -64,7 +61,5 @@ class RabbitMq
$channel->close();
$conn->close();
}
-*/
-
}
diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php
index 84556ce78..3e27f4393 100644
--- a/airtime_mvc/application/models/StoredFile.php
+++ b/airtime_mvc/application/models/StoredFile.php
@@ -1,329 +1,8 @@
"ftype",
- "dc:format" => "format",
- "ls:bitrate" => "bit_rate",
- "ls:samplerate" => "sample_rate",
- "dcterms:extent" => "length",
- "dc:title" => "track_title",
- "dc:description" => "comments",
- "dc:type" => "genre",
- "dc:creator" => "artist_name",
- "dc:source" => "album_title",
- "ls:channels" => "channels",
- "ls:filename" => "name",
- "ls:year" => "year",
- "ls:url" => "url",
- "ls:track_num" => "track_number",
- "ls:mood" => "mood",
- "ls:bpm" => "bpm",
- "ls:disc_num" => "disc_number",
- "ls:rating" => "rating",
- "ls:encoded_by" => "encoded_by",
- "dc:publisher" => "label",
- "ls:composer" => "composer",
- "ls:encoder" => "encoder",
- "ls:crc" => "checksum",
- "ls:lyrics" => "lyrics",
- "ls:orchestra" => "orchestra",
- "ls:conductor" => "conductor",
- "ls:lyricist" => "lyricist",
- "ls:originallyricist" => "original_lyricist",
- "ls:radiostationname" => "radio_station_name",
- "ls:audiofileinfourl" => "info_url",
- "ls:artisturl" => "artist_url",
- "ls:audiosourceurl" => "audio_source_url",
- "ls:radiostationurl" => "radio_station_url",
- "ls:buycdurl" => "buy_this_url",
- "ls:isrcnumber" => "isrc_number",
- "ls:catalognumber" => "catalog_number",
- "ls:originalartist" => "original_artist",
- "dc:rights" => "copyright",
- "dcterms:temporal" => "report_datetime",
- "dcterms:spatial" => "report_location",
- "dcterms:entity" => "report_organization",
- "dc:subject" => "subject",
- "dc:contributor" => "contributor",
- "dc:language" => "language");
-
- public static function GetMapMetadataXmlToDb() {
- return Metadata::$MAP_METADATA_XML_TO_DB;
- }
-
- /**
- * Track numbers in metadata tags can come in many formats:
- * "1 of 20", "1/20", "20/1". This function parses the track
- * number and gets the real number so that we can sort by it
- * in the database.
- *
- * @param string $p_trackNumber
- * @return int
- */
- public static function ParseTrackNumber($p_trackNumber)
- {
- $num = trim($p_trackNumber);
- if (!is_numeric($num)) {
- $matches = preg_match("/\s*([0-9]+)([^0-9]*)([0-9]*)\s*/", $num, $results);
- $trackNum = 0;
- foreach ($results as $result) {
- if (is_numeric($result)) {
- if ($trackNum == 0) {
- $trackNum = $result;
- } elseif ($result < $trackNum) {
- $trackNum = $result;
- }
- }
- }
- } else {
- $trackNum = $num;
- }
- return $trackNum;
- }
-
-
- /**
- * Add data to the array $p_mdata.
- *
- * Converts the given string ($val) into UTF-8.
- *
- * @param array $p_mdata
- * The array to add the metadata to.
- * @param string $p_key
- * Metadata key.
- * @param string $p_val
- * Metadata value.
- * @param string $p_inputEncoding
- * Encoding type of the input value.
- */
- public static function AddToArray(&$p_mdata, $p_key, $p_val, $p_inputEncoding='iso-8859-1')
- {
- if (!is_null($p_val)) {
- $data = $p_val;
- $outputEncoding = 'UTF-8';
- //if (function_exists('iconv') && ($p_inputEncoding != $outputEncoding) ) {
- if (function_exists('iconv') && is_string($p_val)) {
- $newData = @iconv($p_inputEncoding, $outputEncoding, $data);
- if ($newData === FALSE) {
- echo "Warning: convert $key data to unicode failed\n";
- } elseif ($newData != $data) {
- echo "Converted string: '$data' (".gettype($data).") -> '$newData' (".gettype($newData).").\n";
- $data = $newData;
- }
- }
- $p_mdata[$p_key] = trim($data);
- }
- }
-
-
- /**
- * Return an array with the given audio file's ID3 tags. The keys in the
- * array can be:
- *
- * dc:format ("mime type")
- * dcterms:extent ("duration")
- * dc:title
- * dc:creator ("artist")
- * dc:source ("album")
- * dc:type ("genre")
- * ls:bitrate
- * ls:encoded_by
- * ls:track_num
- * ls:channels
- * ls:year
- * ls:filename
- *
- *
- * @param string $p_filename
- * @param boolean $p_testonly
- * For diagnostic and debugging purposes - setting this to TRUE
- * will print out the values found in the file and the ones assigned
- * to the return array.
- * @return array|PEAR_Error
- */
- public static function LoadFromFile($p_filename, $p_testonly = false)
- {
- $getID3 = new getID3();
- $infoFromFile = $getID3->analyze($p_filename);
- if (PEAR::isError($infoFromFile)) {
- return $infoFromFile;
- }
- if (isset($infoFromFile['error'])) {
- return new PEAR_Error(array_pop($infoFromFile['error']));
- }
- if (!$infoFromFile['bitrate']) {
- return new PEAR_Error("File given is not an audio file.");
- }
-
- if ($p_testonly) {
- print_r($infoFromFile);
- }
- $titleKey = 'dc:title';
- $flds = array(
- 'dc:format' => array(
- array('path'=>"['mime_type']", 'ignoreEnc'=>TRUE),
- ),
- 'ls:bitrate' => array(
- array('path'=>"['bitrate']", 'ignoreEnc'=>TRUE),
- array('path'=>"['audio']['bitrate']", 'ignoreEnc'=>TRUE),
- ),
- 'ls:samplerate' => array(
- array('path'=>"['audio']['sample_rate']", 'ignoreEnc'=>TRUE),
- ),
- 'ls:encoder' => array(
- array('path'=>"['audio']['codec']", 'ignoreEnc'=>TRUE),
- ),
- 'dcterms:extent'=> array(
- array('path'=>"['playtime_seconds']", 'ignoreEnc'=>TRUE),
- ),
- 'ls:composer'=> array(
- array('path'=>"['id3v2']['comments']['composer']", 'dataPath'=>"[0]", 'ignoreEnc'=>TRUE),
- array('path'=>"['id3v2']['TCOM'][0]", 'dataPath'=>"['data']", 'encPath'=>"['encoding']"),
- array('path'=>"['tags']['id3v2']['composer']", 'dataPath'=>"[0]", 'ignoreEnc'=>TRUE),
- array('path'=>"['ogg']['comments']['composer']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- array('path'=>"['tags']['vorbiscomment']['composer']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- ),
- 'dc:description'=> array(
- array('path'=>"['id3v1']['comments']['comment']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- array('path'=>"['id3v2']['comments']['comments']", 'dataPath'=>"[0]", 'ignoreEnc'=>TRUE),
- array('path'=>"['id3v2']['COMM'][0]", 'dataPath'=>"['data']", 'encPath'=>"['encoding']"),
- array('path'=>"['tags']['id3v2']['comments']", 'dataPath'=>"[0]", 'ignoreEnc'=>TRUE),
- array('path'=>"['ogg']['comments']['comment']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- array('path'=>"['tags']['vorbiscomment']['comment']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- ),
- 'dc:type'=> array(
- array('path'=>"['id3v1']", 'dataPath'=>"['genre']", 'encPath'=>"['encoding']"),
- array('path'=>"['id3v2']['comments']['content_type']", 'dataPath'=>"[0]", 'ignoreEnc'=>TRUE),
- array('path'=>"['id3v2']['TCON'][0]", 'dataPath'=>"['data']", 'encPath'=>"['encoding']"),
- array('path'=>"['ogg']['comments']['genre']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- array('path'=>"['tags']['vorbiscomment']['genre']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- ),
- 'dc:title' => array(
- array('path'=>"['id3v2']['comments']['title']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- array('path'=>"['id3v2']['TIT2'][0]", 'dataPath'=>"['data']", 'encPath'=>"['encoding']"),
- array('path'=>"['id3v2']['TT2'][0]", 'dataPath'=>"['data']", 'encPath'=>"['encoding']"),
- array('path'=>"['id3v1']", 'dataPath'=>"['title']", 'encPath'=>"['encoding']"),
- array('path'=>"['ogg']['comments']['title']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- array('path'=>"['tags']['vorbiscomment']['title']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- ),
- 'dc:creator' => array(
- array('path'=>"['id3v2']['comments']['artist']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- array('path'=>"['id3v2']['TPE1'][0]", 'dataPath'=>"['data']", 'encPath'=>"['encoding']"),
- array('path'=>"['id3v2']['TP1'][0]", 'dataPath'=>"['data']", 'encPath'=>"['encoding']"),
- array('path'=>"['id3v1']", 'dataPath'=>"['artist']", 'encPath'=>"['encoding']"),
- array('path'=>"['ogg']['comments']['artist']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- array('path'=>"['tags']['vorbiscomment']['artist']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- ),
- 'dc:source' => array(
- array('path'=>"['id3v2']['comments']['album']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- array('path'=>"['id3v2']['TALB'][0]", 'dataPath'=>"['data']", 'encPath'=>"['encoding']"),
- array('path'=>"['id3v2']['TAL'][0]", 'dataPath'=>"['data']", 'encPath'=>"['encoding']"),
- array('path'=>"['ogg']['comments']['album']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- array('path'=>"['tags']['vorbiscomment']['album']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- ),
- 'ls:encoded_by' => array(
- array('path'=>"['id3v2']['TENC'][0]", 'dataPath'=>"['data']", 'encPath'=>"['encoding']"),
- array('path'=>"['id3v2']['TEN'][0]", 'dataPath'=>"['data']", 'encPath'=>"['encoding']"),
- array('path'=>"['ogg']['comments']['encoded-by']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- array('path'=>"['tags']['vorbiscomment']['encoded-by']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- ),
- 'ls:track_num' => array(
- array('path'=>"['id3v2']['TRCK'][0]", 'dataPath'=>"['data']", 'encPath'=>"['encoding']"),
- array('path'=>"['id3v2']['TRK'][0]", 'dataPath'=>"['data']", 'encPath'=>"['encoding']"),
- array('path'=>"['ogg']['comments']['tracknumber']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- array('path'=>"['tags']['vorbiscomment']['tracknumber']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- ),
- // 'ls:genre' => array(
- // array('path'=>"['id3v1']", 'dataPath'=>"['genre']", 'encPath'=>"['encoding']"),
- // array('path'=>"['id3v2']['TCON'][0]", 'dataPath'=>"['data']", 'encPath'=>"['encoding']"),
- // array('path'=>"['id3v2']['comments']['content_type']", 'dataPath'=>"[0]", 'ignoreEnc'=>TRUE),
- // array('path'=>"['ogg']['comments']['genre']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- // array('path'=>"['tags']['vorbiscomment']['genre']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- // ),
- 'ls:channels' => array(
- array('path'=>"['audio']['channels']", 'ignoreEnc'=>TRUE),
- ),
- 'ls:year' => array(
- array('path'=>"['comments']['date']"),
- array('path'=>"['ogg']['comments']['date']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- array('path'=>"['tags']['vorbiscomment']['date']", 'dataPath'=>"[0]", 'encPath'=>"['encoding']"),
- ),
- 'ls:filename' => array(
- array('path'=>"['filename']"),
- ),
- );
- $mdata = array();
- if (isset($infoFromFile['audio'])) {
- $mdata['audio'] = $infoFromFile['audio'];
- }
- if (isset($infoFromFile['playtime_seconds'])) {
- $mdata['playtime_seconds'] = $infoFromFile['playtime_seconds'];
- }
-
- $titleHaveSet = FALSE;
- foreach ($flds as $key => $getid3keys) {
- foreach ($getid3keys as $getid3key) {
- $path = $getid3key["path"];
- $ignoreEnc = isset($getid3key["ignoreEnc"])?
- $getid3key["ignoreEnc"]:FALSE;
- $dataPath = isset($getid3key["dataPath"])?$getid3key["dataPath"]:"";
- $encPath = isset($getid3key["encPath"])?$getid3key["encPath"]:"";
- $enc = "UTF-8";
-
- $tagElement = "\$infoFromFile$path$dataPath";
- eval("\$tagExists = isset($tagElement);");
- if ($tagExists) {
- //echo "ignore encoding: ".($ignoreEnc?"yes":"no")."\n";
- //echo "tag exists\n";
- //echo "encode path: $encPath\n";
- eval("\$data = $tagElement;");
- if (!$ignoreEnc && $encPath != "") {
- $encodedElement = "\$infoFromFile$path$encPath";
- eval("\$encodedElementExists = isset($encodedElement);");
- if ($encodedElementExists) {
- eval("\$enc = $encodedElement;");
- }
- }
-
- // Special case handling for track number
- if ($key == "ls:track_num") {
- $data = Metadata::ParseTrackNumber($data);
- }
- Metadata::AddToArray($mdata, $key, $data, $enc);
- if ($key == $titleKey) {
- $titleHaveSet = TRUE;
- }
- break;
- }
- }
- }
- if ($p_testonly) {
- var_dump($mdata);
- }
-
- if (!$titleHaveSet || trim($mdata[$titleKey]) == '') {
- Metadata::AddToArray($mdata, $titleKey, basename($p_filename));
- }
- return $mdata;
- }
-}
-
/**
* StoredFile class
*
- * Airtime file storage support class.
- * Represents one virtual file in storage. Virtual file has up to two parts:
- *
- * - metadata in database
- * - binary media data in real file
- *
- *
* @package Airtime
* @subpackage StorageServer
* @copyright 2010 Sourcefabric O.P.S.
@@ -332,604 +11,185 @@ class Metadata {
*/
class StoredFile {
- // *** Variables stored in the database ***
+ /**
+ * @holds propel database object
+ */
+ private $_file;
/**
- * @var int
+ * array of db metadata -> propel
*/
- private $id;
+ private $_dbMD = array (
+ "track_title" => "DbTrackTitle",
+ "artist_name" => "DbArtistName",
+ "album_title" => "DbAlbumTitle",
+ "genre" => "DbGenre",
+ "mood" => "DbMood",
+ "track_number" => "DbTrackNumber",
+ "bpm" => "DbBpm",
+ "label" => "DbLabel",
+ "composer" => "DbComposer",
+ "encoded_by" => "DbEncodedBy",
+ "conductor" => "DbConductor",
+ "year" => "DbYear",
+ "info_url" => "DbInfoUrl",
+ "isrc_number" => "DbIsrcNumber",
+ "copyright" => "DbCopyright",
+ "length" => "DbLength",
+ "bit_rate" => "DbBitRate",
+ "sample_rate" => "DbSampleRate",
+ "mime" => "DbMime",
+ "filepath" => "DbFilepath",
+ "md5" => "DbMd5",
+ "ftype" => "DbFtype"
+ );
- /**
- * Unique ID for the file. This is stored in HEX format. It is
- * converted to a bigint whenever it is used in a database call.
- *
- * @var string
- */
- public $gunid;
-
- /**
- * The unique ID of the file as it is stored in the database.
- * This is for debugging purposes and may not always exist in this
- * class.
- *
- * @var string
- */
- //private $gunidBigint;
-
- /**
- * @var string
- */
- private $name;
-
- /**
- * @var string
- */
- private $mime;
-
- /**
- * Can be 'audioclip'...others might be coming, like webstream.
- *
- * @var string
- */
- private $ftype;
-
- /**
- * Can be 'ready', 'edited', 'incomplete'.
- *
- * @var string
- */
- private $state;
-
- /**
- * @var int
- */
- private $currentlyaccessing;
-
- /**
- * @var int
- */
- private $editedby;
-
- /**
- * @var timestamp
- */
- private $mtime;
-
- /**
- * @var string
- */
- private $md5;
-
- /**
- * @var string
- */
- private $filepath;
-
-
- // *** Variables NOT stored in the database ***
-
- /**
- * Directory where the file is located.
- *
- * @var string
- */
- private $resDir;
-
- /**
- * @var boolean
- */
- private $exists;
-
- /**
- * @var MetaData
- */
- public $md;
-
- /* ========================================================== constructor */
- /**
- * Constructor, but shouldn't be externally called
- *
- * @param string $p_gunid
- * globally unique id of file
- */
- public function __construct($p_gunid=NULL)
+ public function __construct()
{
- $this->gunid = $p_gunid;
- if (empty($this->gunid)) {
- $this->gunid = StoredFile::generateGunid();
+
+ }
+
+ public function getId()
+ {
+ return $this->_file->getDbId();
+ }
+
+ public function getGunId() {
+ return $this->_file->getDbGunid();
+ }
+
+ public function getFormat()
+ {
+ return $this->_file->getDbFtype();
+ }
+
+ public function setFormat($p_format)
+ {
+ $this->_file->setDbFtype($p_format);
+ }
+
+ /**
+ * Set multiple metadata values using defined metadata constants.
+ *
+ * @param array $p_md
+ * example: $p_md['MDATA_KEY_URL'] = 'http://www.fake.com'
+ */
+ public function setMetadata($p_md=null)
+ {
+ if (is_null($p_md)) {
+ $this->setDbColMetadata();
}
else {
- $this->loadMetadata();
- $this->exists = is_file($this->filepath) && is_readable($this->filepath);
+ $dbMd = array();
+ foreach ($p_md as $mdConst => $mdValue) {
+ $dbMd[constant($mdConst)] = $mdValue;
+ }
+ $this->setDbColMetadata($dbMd);
}
}
/**
- * For testing only, do not use.
- */
- public function __setGunid($p_guid) {
- $this->gunid = $p_guid;
- }
-
- /**
- * Convert XML name to database column name. Used for backwards compatibility
- * with old code.
+ * Set multiple metadata values using database columns as indexes.
*
- * @param string $p_category
- * @return string|null
+ * @param array $p_md
+ * example: $p_md['url'] = 'http://www.fake.com'
*/
- public static function xmlCategoryToDbColumn($p_category)
+ public function setDbColMetadata($p_md=null)
{
- $map = Metadata::GetMapMetadataXmlToDb();
- if (array_key_exists($p_category, $map)) {
- return $map[$p_category];
+ if (is_null($p_md)) {
+ foreach ($this->_dbMD as $dbColumn => $propelColumn) {
+ $method = "set$propelColumn";
+ $this->_file->$method(null);
+ }
}
- return null;
+ else {
+ foreach ($p_md as $dbColumn => $mdValue) {
+ //don't blank out name, defaults to original filename on first insertion to database.
+ if($p_category == "track_title" && (is_null($p_value) || $p_value == "")) {
+ continue;
+ }
+ $propelColumn = $this->_dbMD[$dbColumn];
+ $method = "set$propelColumn";
+ $this->_file->$method($mdValue);
+ }
+ }
+
+ $this->_file->save();
}
-
/**
- * Convert database column name to XML name.
+ * Set metadata element value
*
- * @param string $p_dbColumn
- * @return string|null
+ * @param string $category
+ * Metadata element by metadata constant
+ * @param string $value
+ * value to store, if NULL then delete record
*/
- public static function dbColumnToXmlCatagory($p_dbColumn)
+ public function setMetadataValue($p_category, $p_value)
{
- $str = array_search($p_dbColumn, Metadata::GetMapMetadataXmlToDb());
- // make return value consistent with xmlCategoryToDbColumn()
- if ($str === FALSE) {
- $str = null;
- }
- return $str;
+ $this->setDbColMetadataValue(constant($p_category), $p_value);
}
-
/**
- * GUNID needs to be set before you call this function.
+ * Set metadata element value
*
+ * @param string $category
+ * Metadata element by db column
+ * @param string $value
+ * value to store, if NULL then delete record
*/
- public function loadMetadata()
+ public function setDbColMetadataValue($p_category, $p_value)
{
- global $CC_CONFIG, $CC_DBC;
- $escapedValue = pg_escape_string($this->gunid);
- $sql = "SELECT * FROM ".$CC_CONFIG["filesTable"]
- ." WHERE gunid='$escapedValue'";
-
- $this->md = $CC_DBC->getRow($sql);
-
- if (PEAR::isError($this->md)) {
- $error = $this->md;
- $this->md = null;
- return $error;
- }
- $this->filepath = $this->md["filepath"];
- if (is_null($this->md)) {
- $this->md = array();
+ //don't blank out name, defaults to original filename on first insertion to database.
+ if($p_category == "track_title" && (is_null($p_value) || $p_value == "")) {
return;
}
- $compatibilityData = array();
- foreach ($this->md as $key => $value) {
- if ($xmlName = StoredFile::dbColumnToXmlCatagory($key)) {
- $compatibilityData[$xmlName] = $value;
- }
- }
-
- $this->md = array_merge($this->md, $compatibilityData);
- //$this->md = $compatibilityData;
- }
-
- public function setFormat($p_value)
- {
- $this->md["format"] = $p_value;
- }
-
- public function replaceMetadata($p_values)
- {
- global $CC_CONFIG, $CC_DBC;
- foreach ($p_values as $category => $value) {
- $escapedValue = pg_escape_string($value);
- $columnName = StoredFile::xmlCategoryToDbColumn($category);
- if (!is_null($columnName)) {
- $sql = "UPDATE ".$CC_CONFIG["filesTable"]
- ." SET $columnName='$escapedValue'"
- ." WHERE gunid = '".$this->gunid."'";
- $CC_DBC->query($sql);
- }
- }
- $this->loadMetadata();
- }
-
- public function replaceDbMetadata($p_values)
- {
- global $CC_CONFIG, $CC_DBC;
-
- $data = array();
- foreach ($p_values as $category => $value) {
- if (isset($value) && ($value != '')) {
- $escapedValue = pg_escape_string($value);
- $columnName = $category;
- if (!is_null($columnName)) {
- $data[] = "$columnName='$escapedValue'";
- }
- }
- }
-
- $data = join(",", $data);
- $sql = "UPDATE ".$CC_CONFIG["filesTable"]
- ." SET $data"
- ." WHERE gunid = '".$this->gunid."'";
- $res = $CC_DBC->query($sql);
- if (PEAR::isError($res)) {
- $CC_DBC->query("ROLLBACK");
- return $res;
- }
- }
-
- public function clearMetadata()
- {
- $metadataColumns = array("format", "bit_rate", "sample_rate", "length",
- "track_title", "comments", "genre", "artist_name", "channels", "name",
- "year", "url", "track_number");
- foreach ($metadataColumns as $columnName) {
- if (!is_null($columnName)) {
- $sql = "UPDATE ".$CC_CONFIG["filesTable"]
- ." SET $columnName=''"
- ." WHERE gunid = '".$this->gunid."'";
- $CC_DBC->query($sql);
- }
- }
- }
-
-
- /**
- * Create instance of StoredFile object and insert new file
- *
- * @param array $p_values
- * "filepath" - required, local path to media file (where it is before import)
- * "id" - optional, local object id, will be generated if not given
- * "gunid" - optional, unique id, for insert file with gunid, will be generated if not given
- * "filename" - optional, will use "filepath" if not given
- * "metadata" - optional, array of extra metadata, will be automatically calculated if not given.
- * "mime" - optional, MIME type, highly recommended to pass in, will be automatically calculated if not given.
- * "md5" - optional, MD5 sum, highly recommended to pass in, will be automatically calculated if not given.
- *
- * @param boolean $p_copyMedia
- * copy the media file if true, make symlink if false
- *
- * @return StoredFile|NULL|PEAR_Error
- */
- public static function Insert($p_values, $p_copyMedia=TRUE)
- {
- global $CC_CONFIG, $CC_DBC;
-
- if (!isset($p_values["filepath"])) {
- return new PEAR_Error("StoredFile::Insert: filepath not set.");
- }
- if (!file_exists($p_values['filepath'])) {
- return PEAR::raiseError("StoredFile::Insert: ".
- "media file not found ({$p_values['filepath']})");
- }
-
- $gunid = isset($p_values['gunid'])?$p_values['gunid']:NULL;
-
- // Create the StoredFile object
- $storedFile = new StoredFile($gunid);
-
- // Get metadata
- if (isset($p_values["metadata"])) {
- $metadata = $p_values['metadata'];
- } else {
- $metadata = Metadata::LoadFromFile($p_values["filepath"]);
- }
-
- $storedFile->name = isset($p_values['filename']) ? $p_values['filename'] : $p_values["filepath"];
- $storedFile->id = isset($p_values['id']) && is_integer($p_values['id'])?(int)$p_values['id']:null;
- // NOTE: POSTGRES-SPECIFIC KEYWORD "DEFAULT" BEING USED, WOULD BE "NULL" IN MYSQL
- $sqlId = !is_null($storedFile->id)?"'".$storedFile->id."'":'DEFAULT';
- $storedFile->ftype = isset($p_values['filetype']) ? strtolower($p_values['filetype']) : "audioclip";
- $storedFile->mime = (isset($p_values["mime"]) ? $p_values["mime"] : NULL );
- // $storedFile->filepath = $p_values['filepath'];
- if (isset($p_values['md5'])) {
- $storedFile->md5 = $p_values['md5'];
- } elseif (file_exists($p_values['filepath'])) {
- //echo "StoredFile::Insert: WARNING: Having to recalculate MD5 value\n";
- $storedFile->md5 = md5_file($p_values['filepath']);
- }
-
- // Check for duplicates -- return duplicate
- $duplicate = StoredFile::RecallByMd5($storedFile->md5);
- if ($duplicate) {
- return $duplicate;
- }
-
- $storedFile->exists = FALSE;
-
- // Insert record into the database
- $escapedName = pg_escape_string($storedFile->name);
- $escapedFtype = pg_escape_string($storedFile->ftype);
- $sql = "INSERT INTO ".$CC_CONFIG['filesTable']
- ."(id, name, gunid, mime, state, ftype, mtime, md5)"
- ."VALUES ({$sqlId}, '{$escapedName}', "
- ." '{$storedFile->gunid}',"
- ." '{$storedFile->mime}', 'incomplete', '$escapedFtype',"
- ." now(), '{$storedFile->md5}')";
- //$_SESSION["debug"] .= "sql: ".$sql."
";
- //echo $sql."\n";
- $res = $CC_DBC->query($sql);
- if (PEAR::isError($res)) {
- $CC_DBC->query("ROLLBACK");
- return $res;
- }
-
- if (!is_integer($storedFile->id)) {
- // NOTE: POSTGRES-SPECIFIC
- $sql = "SELECT currval('".$CC_CONFIG["filesSequence"]."_seq')";
- $storedFile->id = $CC_DBC->getOne($sql);
- }
- $storedFile->setMetadataBatch($metadata);
-
- // Save media file
- $res = $storedFile->addFile($p_values['filepath'], $p_copyMedia);
- if (PEAR::isError($res)) {
- echo "StoredFile::Insert -- addFile(): '".$res->getMessage()."'\n";
- return $res;
- }
-
- if (empty($storedFile->mime)) {
- //echo "StoredFile::Insert: WARNING: Having to recalculate MIME value\n";
- $storedFile->setMime($storedFile->getMime());
- }
-
- // Save state
- $storedFile->setState('ready');
-
- // Recall the object to get all the proper values
- $storedFile = StoredFile::RecallByGunid($storedFile->gunid);
- return $storedFile;
+ $propelColumn = $this->_dbMD[$p_category];
+ $method = "set$propelColumn";
+ $this->_file->$method($p_value);
+ $this->_file->save();
}
/**
- * Fetch instance of StoreFile object.
- * Should be supplied with only ONE parameter, all the rest should
- * be NULL.
+ * Get one metadata value.
*
- * @param int $p_id
- * local id
- * @param string $p_gunid
- * global unique id of file
- * @param string $p_md5sum
- * MD5 sum of the file
- * @return StoredFile|Playlist|NULL
- * Return NULL if the object doesnt exist in the DB.
+ * @param string $p_category (MDATA_KEY_URL)
+ * @return string
*/
- public static function Recall($p_id=null, $p_gunid=null, $p_md5sum=null, $p_filepath=null)
+ public function getMetadataValue($p_category)
{
- global $CC_DBC;
- global $CC_CONFIG;
- if (!is_null($p_id)) {
- $cond = "id='".intval($p_id)."'";
- } elseif (!is_null($p_gunid)) {
- $cond = "gunid='$p_gunid'";
- } elseif (!is_null($p_md5sum)) {
- $cond = "md5='$p_md5sum'";
- } elseif (!is_null($p_filepath)) {
- $cond = "filepath='$p_filepath'";
- } else {
- return null;
- }
- $sql = "SELECT *"
- ." FROM ".$CC_CONFIG['filesTable']
- ." WHERE $cond";
-
- $row = $CC_DBC->getRow($sql);
- if (PEAR::isError($row) || is_null($row)) {
- return $row;
- }
- $gunid = $row['gunid'];
- $storedFile = new StoredFile($gunid);
- $storedFile->id = $row['id'];
- $storedFile->name = $row['name'];
- $storedFile->mime = $row['mime'];
- $storedFile->ftype = $row['ftype'];
- $storedFile->state = $row['state'];
- $storedFile->currentlyaccessing = $row['currentlyaccessing'];
- $storedFile->editedby = $row['editedby'];
- $storedFile->mtime = $row['mtime'];
- $storedFile->md5 = $row['md5'];
- $storedFile->filepath = $row['filepath'];
-
- if(file_exists($row['filepath'])) {
- $storedFile->exists = true;
- }
- else {
- $storedFile->exists = false;
- }
-
- $storedFile->setFormat($row['ftype']);
- return $storedFile;
+ return $this->getDbColMetadataValue(constant($p_category));
}
- /**
- * Create instance of StoreFile object and recall existing file
- * by gunid.
+ /**
+ * Get one metadata value.
*
- * @param string $p_gunid
- * global unique id of file
- * @return StoredFile
+ * @param string $p_category (url)
+ * @return string
*/
- public static function RecallByGunid($p_gunid='')
+ public function getDbColMetadataValue($p_category)
{
- return StoredFile::Recall(null, $p_gunid);
+ $propelColumn = $this->_dbMD[$p_category];
+ $method = "get$propelColumn";
+ return $this->_file->$method();
}
-
/**
- * Fetch the StoredFile by looking up the MD5 value.
+ * Get metadata as array, indexed by the column names in the database.
*
- * @param string $p_md5sum
- * @return StoredFile|NULL|PEAR_Error
+ * @return array
*/
- public static function RecallByMd5($p_md5sum)
+ public function getDbColMetadata()
{
- return StoredFile::Recall(null, null, $p_md5sum);
+ $md = array();
+ foreach ($this->_dbMD as $dbColumn => $propelColumn) {
+ $method = "get$propelColumn";
+ $md[$dbColumn] = $this->_file->$method();
+ }
+
+ return $md;
}
-
- public static function GetAll()
- {
- global $CC_CONFIG, $CC_DBC;
- $sql = "SELECT * FROM ".$CC_CONFIG["filesTable"];
- $rows = $CC_DBC->GetAll($sql);
- return $rows;
- }
-
- private function ensureDir($dir)
- {
- if (!is_dir($dir)) {
- mkdir($dir, 02775);
- chmod($dir, 02775);
- }
- }
-
- private function createUniqueFilename($base, $ext)
- {
- if(file_exists("$base.$ext")) {
- $i = 1;
- while(true) {
- if(file_exists("$base($i).$ext")) {
- $i = $i+1;
- }
- else {
- return "$base($i).$ext";
- }
- }
- }
-
- return "$base.$ext";
- }
-
- /**
- * Generate the location to store the file.
- * It creates the subdirectory if needed.
- */
- private function generateFilePath()
- {
- global $CC_CONFIG, $CC_DBC;
-
- $storageDir = $CC_CONFIG['storageDir'];
- $info = pathinfo($this->name);
- $origName = $info['filename'];
- $fileExt = strtolower($info["extension"]);
-
- $this->loadMetadata();
-
- $artist = $this->md["dc:creator"];
- $album = $this->md["dc:source"];
- $title = $this->md["dc:title"];
- $track_num = $this->md["ls:track_num"];
-
- if(isset($artist) && $artist != "") {
- $base = "$storageDir/$artist";
- $this->ensureDir($base);
-
- if(isset($album) && $album != "") {
- $base = "$base/$album";
- $this->ensureDir($base);
- }
-
- if(isset($title) && $title != "") {
- if(isset($track_num) && $track_num != "") {
- if($track_num < 10 && strlen($track_num) == 1) {
- $track_num = "0$track_num";
- }
- $base = "$base/$track_num - $title";
- }
- else {
- $base = "$base/$title";
- }
- }
- else {
- $base = "$base/$origName";
- }
- }
- else {
- $base = "$storageDir/$origName";
- }
-
- return $this->createUniqueFilename($base, $fileExt);
- }
-
- /**
- * Insert media file to filesystem
- *
- * @param string $p_localFilePath
- * local path
- * @param boolean $p_copyMedia
- * copy the media file if true, make symlink if false
- * @return TRUE|PEAR_Error
- */
- public function addFile($p_localFilePath, $p_copyMedia=TRUE)
- {
- global $CC_CONFIG, $CC_DBC;
- if ($this->exists) {
- return FALSE;
- }
- // for files downloaded from remote instance:
- if ($p_localFilePath == $this->filepath) {
- $this->exists = TRUE;
- return TRUE;
- }
- umask(0002);
- $dstFile = '';
- if ($p_copyMedia) {
- $dstFile = $this->generateFilePath();
- $r = @copy($p_localFilePath, $dstFile);
- if (!$r) {
- $this->exists = FALSE;
- return PEAR::raiseError(
- "StoredFile::addFile: file save failed".
- " ($p_localFilePath, {$this->filepath})",GBERR_FILEIO
- );
- }
- } else {
- $dstFile = $p_localFilePath;
- $r = TRUE;
- }
- $this->filepath = $dstFile;
- $sqlPath = pg_escape_string($this->filepath);
- $sql = "UPDATE ".$CC_CONFIG["filesTable"]
- ." SET filepath='{$sqlPath}'"
- ." WHERE id={$this->id}";
- //echo $sql."\n";
- $res = $CC_DBC->query($sql);
- if (PEAR::isError($res)) {
- return $res;
- }
- $this->exists = TRUE;
- return TRUE;
- }
-
-
- /**
- * Find and return the first exact match for the original file name
- * that was used on import.
- * @param string $p_name
- */
- public static function findByOriginalName($p_name)
- {
- global $CC_CONFIG, $CC_DBC;
- $sql = "SELECT id FROM ".$CC_CONFIG["filesTable"]
- ." WHERE name='".pg_escape_string($p_name)."'";
- $id = $CC_DBC->getOne($sql);
- if (is_numeric($id)) {
- return StoredFile::Recall($id);
- } else {
- return NULL;
- }
- }
-
-
/**
* Delete and insert media file
*
@@ -954,192 +214,6 @@ class StoredFile {
return $this->addFile($p_localFilePath, $p_copyMedia);
}
-
- /**
- * Return true if file corresponding to the object exists
- *
- * @return boolean
- */
- public function existsFile()
- {
- return $this->exists;
- }
-
-
- /**
- * Create instance of StoredFile object and make copy of existing file
- *
- * @param StoredFile $p_src
- * source object
- * @param int $p_nid
- * new local id
- * @return StoredFile
- */
- public static function CopyOf(&$p_src, $p_nid)
- {
- $values = array(
- "id" => $p_nid,
- "filename" => $p_src->name,
- "filepath" => $p_src->getRealFilePath(),
- "filetype" => $p_src->getType()
- );
- $storedFile = StoredFile::Insert($values);
- if (PEAR::isError($storedFile)) {
- return $storedFile;
- }
- $storedFile->replaceMetadata($p_src->getAllMetadata(), 'string');
- return $storedFile;
- }
-
-
- private static function NormalizeExtent($v)
- {
- if (!preg_match("|^\d{2}:\d{2}:\d{2}.\d{6}$|", $v)) {
- $s = Playlist::playlistTimeToSeconds($v);
- $t = Playlist::secondsToPlaylistTime($s);
- return $t;
- }
- return $v;
- }
-
- /**
- * Set metadata element value
- *
- * @param string $category
- * Metadata element identification (e.g. dc:title)
- * @param string $value
- * value to store, if NULL then delete record
- * @return boolean
- */
- public function setMetadataValue($p_category, $p_value)
- {
- global $CC_CONFIG, $CC_DBC;
- if (!is_string($p_category) || is_array($p_value)) {
- return FALSE;
- }
- if ($p_category == 'dcterms:extent') {
- $p_value = StoredFile::NormalizeExtent($p_value);
- }
- $columnName = StoredFile::xmlCategoryToDbColumn($p_category); // Get column name
-
- if (!is_null($columnName)) {
- $escapedValue = pg_escape_string($p_value);
- $sql = "UPDATE ".$CC_CONFIG["filesTable"]
- ." SET $columnName='$escapedValue'"
- ." WHERE id={$this->id}";
- //var_dump($sql);
- $res = $CC_DBC->query($sql);
- if (PEAR::isError($res)) {
- return $res;
- }
- }
- return TRUE;
- }
-
-
- /**
- * Set metadata values in 'batch' mode
- *
- * @param array $values
- * array of key/value pairs
- * (e.g. 'dc:title'=>'New title')
- * @return boolean
- */
- public function setMetadataBatch($values)
- {
- global $CC_CONFIG, $CC_DBC;
- if (!is_array($values)) {
- $values = array($values);
- }
- if (count($values) == 0) {
- return true;
- }
- foreach ($values as $category => $oneValue) {
- $columnName = StoredFile::xmlCategoryToDbColumn($category);
- if (!is_null($columnName)) {
- if ($category == 'dcterms:extent') {
- $oneValue = StoredFile::NormalizeExtent($oneValue);
- }
- // Since track_number is an integer, you cannot set
- // it to be the empty string, so we NULL it instead.
- if ($columnName == 'track_number' && empty($oneValue)) {
- $sqlPart = "$columnName = NULL";
- } elseif (($columnName == 'length') && (strlen($oneValue) > 8)) {
- // Postgres doesnt like it if you try to store really large hour
- // values. TODO: We need to fix the underlying problem of getting the
- // right values.
- $parts = explode(':', $oneValue);
- $hour = intval($parts[0]);
- if ($hour > 24) {
- continue;
- } else {
- $sqlPart = "$columnName = '$oneValue'";
- }
- } else {
- $escapedValue = pg_escape_string($oneValue);
- $sqlPart = "$columnName = '$escapedValue'";
- }
- $sqlValues[] = $sqlPart;
- }
- }
- if (count($sqlValues)==0) {
- return TRUE;
- }
- $sql = "UPDATE ".$CC_CONFIG["filesTable"]
- ." SET ".join(",", $sqlValues)
- ." WHERE id={$this->id}";
- $CC_DBC->query($sql);
- return TRUE;
- }
-
-
- /**
- * Get metadata as array, indexed by the column names in the database.
- *
- * @return array
- */
- public function getMetadata()
- {
- return $this->md;
- }
-
- /**
- * Get one metadata value.
- *
- * @param string $p_name
- * @return string
- */
- public function getMetadataValue($p_name)
- {
- if (isset($this->md[$p_name])){
- return $this->md[$p_name];
- } else {
- return "";
- }
- }
-
- /**
- * Rename stored virtual file
- *
- * @param string $p_newname
- * @return TRUE|PEAR_Error
- */
- public function setName($p_newname)
- {
- global $CC_CONFIG, $CC_DBC;
- $escapedName = pg_escape_string($p_newname);
- $sql = "UPDATE ".$CC_CONFIG['filesTable']
- ." SET name='$escapedName', mtime=now()"
- ." WHERE gunid='{$this->gunid}'";
- $res = $CC_DBC->query($sql);
- if (PEAR::isError($res)) {
- return $res;
- }
- $this->name = $p_newname;
- return TRUE;
- }
-
-
/**
* Set state of virtual file
*
@@ -1166,156 +240,6 @@ class StoredFile {
return TRUE;
}
- /**
- * Set mime-type of virtual file
- *
- * @param string $p_mime
- * mime-type
- * @return boolean|PEAR_Error
- */
- public function setMime($p_mime)
- {
- global $CC_CONFIG, $CC_DBC;
- if (!is_string($p_mime)) {
- $p_mime = 'application/octet-stream';
- }
- $escapedMime = pg_escape_string($p_mime);
- $sql = "UPDATE ".$CC_CONFIG['filesTable']
- ." SET mime='$escapedMime', mtime=now()"
- ." WHERE gunid='{$this->gunid}'";
- $res = $CC_DBC->query($sql);
- if (PEAR::isError($res)) {
- return $res;
- }
- $this->mime = $p_mime;
- return TRUE;
- }
-
-
- /**
- * Set md5 of virtual file
- *
- * @param string $p_md5sum
- * @return boolean|PEAR_Error
- */
- public function setMd5($p_md5sum)
- {
- global $CC_CONFIG, $CC_DBC;
- $escapedMd5 = pg_escape_string($p_md5sum);
- $sql = "UPDATE ".$CC_CONFIG['filesTable']
- ." SET md5='$escapedMd5', mtime=now()"
- ." WHERE gunid='{$this->gunid}'";
- $res = $CC_DBC->query($sql);
- if (PEAR::isError($res)) {
- return $res;
- }
- $this->md5 = $p_md5sum;
- return TRUE;
- }
-
- /**
- * Delete media file from filesystem.
- * You cant delete a file if it is being accessed.
- * You cant delete a file if it is scheduled to be played in the future.
- * The file will be removed from all playlists it is a part of.
- *
- * @return boolean|PEAR_Error
- */
- public function deleteFile()
- {
- global $CC_CONFIG;
- if (!$this->exists) {
- return FALSE;
- }
- if ($this->isAccessed()) {
- return PEAR::raiseError(
- 'Cannot delete a file that is currently accessed.'
- );
- }
-
- // Check if the file is scheduled to be played in the future
- if (Schedule::IsFileScheduledInTheFuture($this->id)) {
- return PEAR::raiseError(
- 'Cannot delete a file that is scheduled in the future.'
- );
- }
-
- // Only delete the file from filesystem if it has been copied to the
- // storage directory. (i.e. dont delete linked files)
- if (substr($this->filepath, 0, strlen($CC_CONFIG["storageDir"])) == $CC_CONFIG["storageDir"]) {
- // Delete the file
- if (!file_exists($this->filepath) || @unlink($this->filepath)) {
- $this->exists = FALSE;
- return TRUE;
- }
- else {
- return PEAR::raiseError("StoredFile::deleteFile: unlink failed ({$this->filepath})");
- }
- }
- else {
- $this->exists = FALSE;
- return TRUE;
- }
- }
-
-
- /**
- * Delete stored virtual file
- *
- * @param boolean $p_deleteFile
- *
- * @return TRUE|PEAR_Error
- */
- public function delete($p_deleteFile = true)
- {
- global $CC_CONFIG, $CC_DBC;
- if ($p_deleteFile) {
- $res = $this->deleteFile();
- if (PEAR::isError($res)) {
- return $res;
- }
-
- Playlist::DeleteFileFromAllPlaylists($this->id);
- }
-
- $sql = "DELETE FROM ".$CC_CONFIG['filesTable']
- ." WHERE gunid='{$this->gunid}'";
- $res = $CC_DBC->query($sql);
- if (PEAR::isError($res)) {
- return $res;
- }
- return TRUE;
- }
-
-
- public static function deleteById($p_id)
- {
- global $CC_CONFIG, $CC_DBC;
- if (!is_numeric($p_id)) {
- return FALSE;
- }
- $sql = "DELETE FROM ".$CC_CONFIG["filesTable"]." WHERE id=$p_id";
-
- $res = $CC_DBC->query($sql);
- if (PEAR::isError($res)) {
- return $res;
- }
- return TRUE;
- }
-
- public static function deleteAll()
- {
- global $CC_CONFIG, $CC_DBC;
- $files = StoredFile::getAll();
- foreach ($files as $file) {
- $media = StoredFile::Recall($file["id"]);
- $result = $media->delete();
- if (PEAR::isError($result)) {
- return $result;
- }
- }
- }
-
/**
* Returns an array of playlist objects that this file is a part of.
* @return array
@@ -1335,255 +259,125 @@ class StoredFile {
return $playlists;
}
-
/**
- * Returns true if virtual file is currently in use.
- * Static or dynamic call is possible.
+ * Delete stored virtual file
*
- * @param string $p_gunid
- * optional (for static call), global unique id
- * @return boolean|PEAR_Error
+ * @param boolean $p_deleteFile
+ *
+ * @return void|PEAR_Error
*/
- public function isAccessed($p_gunid=NULL)
+ public function delete()
{
- global $CC_CONFIG, $CC_DBC;
- if (is_null($p_gunid)) {
- return ($this->currentlyaccessing > 0);
+ if ($this->exists()) {
+ if ($this->getFormat() == 'audioclip') {
+ $res = $this->deleteFile();
+ if (PEAR::isError($res)) {
+ return $res;
+ }
+ }
}
- $sql = "SELECT currentlyAccessing FROM ".$CC_CONFIG['filesTable']
- ." WHERE gunid='$p_gunid'";
- $ca = $CC_DBC->getOne($sql);
- if (is_null($ca)) {
- return PEAR::raiseError(
- "StoredFile::isAccessed: invalid gunid ($p_gunid)",
- GBERR_FOBJNEX
- );
- }
- return ($ca > 0);
+
+ Playlist::DeleteFileFromAllPlaylists($this->getId());
+ $this->_file->delete();
}
+ /**
+ * Delete media file from filesystem.
+ * You cant delete a file if it is being accessed.
+ * You cant delete a file if it is scheduled to be played in the future.
+ * The file will be removed from all playlists it is a part of.
+ *
+ * @return void|PEAR_Error
+ */
+ public function deleteFile()
+ {
+ global $CC_CONFIG;
+
+ if ($this->isAccessed()) {
+ return PEAR::raiseError('Cannot delete a file that is currently accessed.');
+ }
+
+ // Check if the file is scheduled to be played in the future
+ if (Schedule::IsFileScheduledInTheFuture($this->getId())) {
+ return PEAR::raiseError('Cannot delete a file that is scheduled in the future.');
+ }
+
+ // Only delete the file from filesystem if it has been copied to the storage directory
+ if (substr($this->getFilePath(), 0, strlen($CC_CONFIG["storageDir"])) == $CC_CONFIG["storageDir"]) {
+ // Delete the file
+ $res = unlink($this->getFilePath());
+ if (!$res) {
+ return PEAR::raiseError("StoredFile::deleteFile: unlink failed ({$this->getFilePath()})");
+ }
+ }
+ }
/**
- * Returns true if virtual file is edited
- *
- * @param string $p_playlistId
- * playlist global unique ID
+ * Returns true if media file exists
* @return boolean
*/
- public function isEdited($p_playlistId=NULL)
- {
- if (is_null($p_playlistId)) {
- return ($this->state == 'edited');
- }
- $state = $this->getState($p_playlistId);
- if ($state != 'edited') {
- return FALSE;
- }
- return TRUE;
- }
-
-
- /**
- * Returns id of user editing playlist
- *
- * @param string $p_playlistId
- * playlist global unique ID
- * @return int|null|PEAR_Error
- * id of user editing it
- */
- public function isEditedBy($p_playlistId=NULL)
- {
- global $CC_CONFIG, $CC_DBC;
- if (is_null($p_playlistId)) {
- $p_playlistId = $this->gunid;
- }
- $sql = "SELECT editedBy FROM ".$CC_CONFIG['filesTable']
- ." WHERE gunid='$p_playlistId'";
- $ca = $CC_DBC->getOne($sql);
- if (PEAR::isError($ca)) {
- return $ca;
- }
- if (is_null($ca)) {
- return $ca;
- }
- return intval($ca);
- }
-
-
- /**
- * Return local ID of virtual file.
- *
- * @return int
- */
- public function getId()
- {
- return $this->id;
- }
-
-
- /**
- * Return global ID of virtual file.
- *
- * @return string
- */
- public function getGunid()
- {
- return $this->gunid;
- }
-
-
- /**
- * Returns true if raw media file exists
- * @return boolean|PEAR_Error
- */
public function exists()
{
- global $CC_CONFIG, $CC_DBC;
- $sql = "SELECT gunid "
- ." FROM ".$CC_CONFIG['filesTable']
- ." WHERE gunid='{$this->gunid}'";
- $indb = $CC_DBC->getRow($sql);
- if (PEAR::isError($indb)) {
- return $indb;
+ if ($this->_file->isDeleted()) {
+ return false;
}
- if (is_null($indb)) {
- return FALSE;
- }
- if ($this->ftype == 'audioclip') {
+ if ($this->getFormat() == 'audioclip') {
return $this->existsFile();
}
- return TRUE;
}
-
/**
- * Create new global unique id
- * @return string
+ * Returns true if raw media file exists
+ * @return boolean
*/
- public static function generateGunid()
- {
- return md5(uniqid("", true));
+ public function existsFile() {
+
+ $filepath = $this->_file->getDbFilepath();
+
+ if (!isset($filepath) || !file_exists($filepath) || !is_readable($filepath)) {
+ return false;
+ }
+ else {
+ return true;
+ }
}
+ /**
+ * Returns true if virtual file is currently in use.
+ *
+ * @return boolean
+ */
+ public function isAccessed()
+ {
+ return ($this->_file->getDbCurrentlyaccessing() > 0);
+ }
/**
* Return suitable extension.
*
- * @todo make it general - is any tool for it?
- *
* @return string
* file extension without a dot
*/
public function getFileExtension()
{
- $fname = $this->getName();
- $pos = strrpos($fname, '.');
- if ($pos !== FALSE) {
- $ext = substr($fname, $pos+1);
- if ($ext !== FALSE) {
- return $ext;
- }
+ $mime = $this->_file->getDbMime();
+
+ if ($mime == "audio/vorbis") {
+ return "ogg";
}
- switch (strtolower($this->mime)) {
- case "audio/mpeg":
- $ext = "mp3";
- break;
- case "audio/x-wav":
- case "audio/x-wave":
- $ext = "wav";
- break;
- case "audio/x-ogg":
- case "application/x-ogg":
- $ext = "ogg";
- break;
- default:
- $ext = "bin";
- break;
+ else if ($mime == "audio/mp3") {
+ return "mp3";
}
- return $ext;
}
-
- /**
- * Get mime-type stored in the file.
- * Warning: this function is slow!
- *
- * @return string
- */
- public function getMime()
- {
- $a = Metadata::LoadFromFile($this->filepath);
- if (PEAR::isError($a)) {
- return $a;
- }
- if (isset($a['dc:format'])) {
- return $a['dc:format'];
- }
- return '';
- }
-
-
- /**
- * Convenience function.
- * @return string
- */
- public function getTitle()
- {
- return $this->md["title"];
- }
-
- public function getType()
- {
- return $this->ftype;
- }
-
- /**
- * Get storage-internal file state
- *
- * @param string $p_gunid
- * global unique id of file
- * @return string
- * see install()
- */
- public function getState($p_gunid=NULL)
- {
- global $CC_CONFIG, $CC_DBC;
- if (is_null($p_gunid)) {
- return $this->state;
- }
- $sql = "SELECT state FROM ".$CC_CONFIG['filesTable']
- ." WHERE gunid='$p_gunid'";
- return $CC_DBC->getOne($sql);
- }
-
-
- /**
- * Get mnemonic file name
- *
- * @param string $p_gunid
- * global unique id of file
- * @return string
- */
- public function getName($p_gunid=NULL)
- {
- global $CC_CONFIG, $CC_DBC;
- if (is_null($p_gunid)) {
- return $this->name;
- }
- $sql = "SELECT name FROM ".$CC_CONFIG['filesTable']
- ." WHERE gunid='$p_gunid'";
- return $CC_DBC->getOne($sql);
- }
-
-
/**
* Get real filename of raw media data
*
* @return string
*/
- public function getRealFilePath()
+ public function getFilePath()
{
- return $this->filepath;
+ return $this->_file->getDbFilepath();
}
/**
@@ -1592,65 +386,138 @@ class StoredFile {
public function getFileUrl()
{
global $CC_CONFIG;
- return "http://$CC_CONFIG[baseUrl]:$CC_CONFIG[basePort]/api/get-media/file/".$this->gunid.".".$this->getFileExtension();
+ return "http://$CC_CONFIG[baseUrl]:$CC_CONFIG[basePort]/api/get-media/file/".$this->getGunId().".".$this->getFileExtension();
+ }
+
+ public static function Insert($md=null)
+ {
+ $file = new CcFiles();
+ $file->setDbGunid(md5(uniqid("", true)));
+ $file->save();
+
+ $storedFile = new StoredFile();
+ $storedFile->_file = $file;
+
+ if(isset($md)) {
+ $storedFile->setMetadata($md);
+ }
+
+ return $storedFile;
}
/**
- * Get real filename of metadata file
+ * Fetch instance of StoreFile object.
+ * Should be supplied with only ONE parameter, all the rest should
+ * be NULL.
*
- * @return string
- * @see MetaData
+ * @param int $p_id
+ * local id
+ * @param string $p_gunid
+ * global unique id of file
+ * @param string $p_md5sum
+ * MD5 sum of the file
+ * @return StoredFile|NULL
+ * Return NULL if the object doesnt exist in the DB.
*/
- public function getRealMetadataFileName()
+ public static function Recall($p_id=null, $p_gunid=null, $p_md5sum=null, $p_filepath=null)
{
- //return $this->md->getFileName();
- return $this->md["name"];
+ if (isset($p_id)) {
+ $file = CcFilesQuery::create()->findPK(intval($p_id));
+ }
+ else if (isset($p_gunid)) {
+ $file = CcFilesQuery::create()
+ ->filterByDbGunid($p_gunid)
+ ->findOne();
+ }
+ else if (isset($p_md5sum)) {
+ $file = CcFilesQuery::create()
+ ->filterByDbMd5($p_md5sum)
+ ->findOne();
+ }
+ else if (isset($p_filepath)) {
+ $file = CcFilesQuery::create()
+ ->filterByDbFilepath($p_filepath)
+ ->findOne();
+ }
+ else {
+ return null;
+ }
+
+ if (isset($file)) {
+ $storedFile = new StoredFile();
+ $storedFile->_file = $file;
+
+ return $storedFile;
+ }
+ else {
+ return null;
+ }
+ }
+
+ /**
+ * Create instance of StoreFile object and recall existing file
+ * by gunid.
+ *
+ * @param string $p_gunid
+ * global unique id of file
+ * @return StoredFile|NULL
+ */
+ public static function RecallByGunid($p_gunid)
+ {
+ return StoredFile::Recall(null, $p_gunid);
}
/**
- * Create and return name for temporary symlink.
+ * Fetch the StoredFile by looking up the MD5 value.
*
- * @todo Should be more unique
- * @return string
+ * @param string $p_md5sum
+ * @return StoredFile|NULL
*/
- private function _getAccessFileName($p_token, $p_ext='EXT')
+ public static function RecallByMd5($p_md5sum)
{
- global $CC_CONFIG;
- return $CC_CONFIG['accessDir']."/$p_token.$p_ext";
+ return StoredFile::Recall(null, null, $p_md5sum);
}
+ /**
+ * Fetch the StoredFile by looking up its filepath.
+ *
+ * @param string $p_filepath path of file stored in Airtime.
+ * @return StoredFile|NULL
+ */
+ public static function RecallByFilepath($p_filepath)
+ {
+ return StoredFile::Recall(null, null, null, $p_filepath);
+ }
public static function searchFilesForPlaylistBuilder($datatables) {
global $CC_CONFIG;
+ $displayData = array("track_title", "artist_name", "album_title", "track_number", "length", "ftype");
+
$plSelect = "SELECT ";
$fileSelect = "SELECT ";
- foreach (Metadata::GetMapMetadataXmlToDb() as $key => $val){
+ foreach ($displayData as $key){
- if($key === "dc:title"){
- $plSelect .= "name AS ".$val.", ";
- $fileSelect .= $val.", ";
+ if($key === "track_title"){
+ $plSelect .= "name AS ".$key.", ";
+ $fileSelect .= $key.", ";
}
- else if ($key === "ls:type"){
- $plSelect .= "'playlist' AS ".$val.", ";
- $fileSelect .= $val.", ";
+ else if ($key === "ftype"){
+ $plSelect .= "'playlist' AS ".$key.", ";
+ $fileSelect .= $key.", ";
}
- else if ($key === "dc:creator"){
- $plSelect .= "creator AS ".$val.", ";
- $fileSelect .= $val.", ";
+ else if ($key === "artist_name"){
+ $plSelect .= "creator AS ".$key.", ";
+ $fileSelect .= $key.", ";
}
- else if ($key === "dcterms:extent"){
- $plSelect .= "length, ";
- $fileSelect .= "length, ";
- }
- else if ($key === "dc:description"){
- $plSelect .= "text(description) AS ".$val.", ";
- $fileSelect .= $val.", ";
+ else if ($key === "length"){
+ $plSelect .= $key.", ";
+ $fileSelect .= $key.", ";
}
else {
- $plSelect .= "NULL AS ".$val.", ";
- $fileSelect .= $val.", ";
+ $plSelect .= "NULL AS ".$key.", ";
+ $fileSelect .= $key.", ";
}
}
@@ -1754,7 +621,7 @@ class StoredFile {
return array("sEcho" => intval($data["sEcho"]), "iTotalDisplayRecords" => $totalDisplayRows, "iTotalRecords" => $totalRows, "aaData" => $results);
}
- public static function uploadFile($p_targetDir)
+ public static function uploadFile($p_targetDir)
{
// HTTP headers for no cache etc
header('Content-type: text/plain; charset=UTF-8');
@@ -1765,7 +632,6 @@ class StoredFile {
header("Pragma: no-cache");
// Settings
- //$p_targetDir = ini_get("upload_tmp_dir"); //. DIRECTORY_SEPARATOR . "plupload";
$cleanupTargetDir = false; // Remove old files
$maxFileAge = 60 * 60; // Temp file age in seconds
@@ -1866,35 +732,22 @@ class StoredFile {
}
}
}
+ else {
- $metadata = Metadata::LoadFromFile($audio_file);
+ global $CC_CONFIG;
+ $stor = $CC_CONFIG["storageDir"];
+ $audio_stor = $stor . DIRECTORY_SEPARATOR . $fileName;
- if (PEAR::isError($metadata)) {
- die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": ' + $metadata->getMessage() + '}}');
+ $md = array();
+ $md['MDATA_KEY_MD5'] = $md5;
+ $md['MDATA_KEY_FILEPATH'] = $audio_stor;
+ $md['MDATA_KEY_TITLE'] = $fileName;
+
+ StoredFile::Insert($md);
+ $r = @chmod($audio_file, 0666);
+ $r = @rename($audio_file, $audio_stor);
}
- // no id3 title tag -> use the original filename for title
- if (empty($metadata[UI_MDATA_KEY_TITLE])) {
- $metadata[UI_MDATA_KEY_TITLE] = basename($audio_file);
- $metadata[UI_MDATA_KEY_FILENAME] = basename($audio_file);
- }
-
- $values = array(
- "filename" => basename($audio_file),
- "filepath" => $audio_file,
- "filetype" => "audioclip",
- "mime" => $metadata[UI_MDATA_KEY_FORMAT],
- "md5" => $md5
- );
- $storedFile = StoredFile::Insert($values);
-
- if (PEAR::isError($storedFile)) {
- die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": ' + $storedFile->getMessage() + '}}');
- }
-
- $storedFile->setMetadataBatch($metadata);
-
- return $storedFile;
}
}
diff --git a/install/airtime-install b/install/airtime-install
index b86f03ad8..bc2187d68 100755
--- a/install/airtime-install
+++ b/install/airtime-install
@@ -11,11 +11,11 @@ SCRIPTPATH=`dirname $SCRIPT`
echo -e "\n******************************** Install Begin *********************************"
-php ${SCRIPTPATH}/airtime-install.php $@
-
echo -e "\n*** Creating Pypo User ***"
python ${SCRIPTPATH}/../python_apps/create-pypo-user.py
+php ${SCRIPTPATH}/airtime-install.php $@
+
echo -e "\n*** Pypo Installation ***"
python ${SCRIPTPATH}/../python_apps/pypo/install/pypo-install.py
diff --git a/install/airtime-install.php b/install/airtime-install.php
index 8a0a6f741..cb4a0c90f 100644
--- a/install/airtime-install.php
+++ b/install/airtime-install.php
@@ -121,8 +121,6 @@ if ($db_install) {
AirtimeInstall::InstallStorageDirectory();
-AirtimeInstall::ChangeDirOwnerToWebserver($CC_CONFIG["storageDir"]);
-
AirtimeInstall::CreateSymlinksToUtils();
AirtimeInstall::CreateZendPhpLogFile();
diff --git a/install/include/AirtimeInstall.php b/install/include/AirtimeInstall.php
index c1d4a9e7a..dbbff57b6 100644
--- a/install/include/AirtimeInstall.php
+++ b/install/include/AirtimeInstall.php
@@ -116,42 +116,39 @@ class AirtimeInstall
}
}
- public static function ChangeDirOwnerToWebserver($filePath)
- {
- global $CC_CONFIG;
- echo "* Giving Apache permission to access $filePath".PHP_EOL;
-
- $success = chgrp($filePath, $CC_CONFIG["webServerUser"]);
- $fileperms=@fileperms($filePath);
- $fileperms = $fileperms | 0x0010; // group write bit
- $fileperms = $fileperms | 0x0400; // group sticky bit
- chmod($filePath, $fileperms);
- }
-
public static function InstallStorageDirectory()
{
global $CC_CONFIG, $CC_DBC;
echo "* Storage directory setup".PHP_EOL;
- foreach (array('baseFilesDir', 'storageDir') as $d) {
- if ( !file_exists($CC_CONFIG[$d]) ) {
- @mkdir($CC_CONFIG[$d], 02775, true);
- if (file_exists($CC_CONFIG[$d])) {
- $rp = realpath($CC_CONFIG[$d]);
- echo "* Directory $rp created".PHP_EOL;
- } else {
- echo "* Failed creating {$CC_CONFIG[$d]}".PHP_EOL;
- exit(1);
- }
- } elseif (is_writable($CC_CONFIG[$d])) {
- $rp = realpath($CC_CONFIG[$d]);
- echo "* Skipping directory already exists: $rp".PHP_EOL;
+ $stor_dir = $CC_CONFIG['storageDir'];
+
+ if (!file_exists($stor_dir)) {
+ @mkdir($stor_dir, 02777, true);
+ if (file_exists($stor_dir)) {
+ $rp = realpath($stor_dir);
+ echo "* Directory $rp created".PHP_EOL;
} else {
- $rp = realpath($CC_CONFIG[$d]);
- echo "* WARNING: Directory already exists, but is not writable: $rp".PHP_EOL;
+ echo "* Failed creating {$stor_dir}".PHP_EOL;
+ exit(1);
}
- $CC_CONFIG[$d] = $rp;
}
+ else if (is_writable($stor_dir)) {
+ $rp = realpath($stor_dir);
+ echo "* Skipping directory already exists: $rp".PHP_EOL;
+ }
+ else {
+ $rp = realpath($stor_dir);
+ echo "* WARNING: Directory already exists, but is not writable: $rp".PHP_EOL;
+ return;
+ }
+
+ echo "* Giving Apache permission to access $rp".PHP_EOL;
+ $success = chgrp($rp, $CC_CONFIG["webServerUser"]);
+ $success = chown($rp, "pypo");
+ $success = chmod($rp, 02777);
+ $CC_CONFIG['storageDir'] = $rp;
+
}
public static function CreateDatabaseUser()
@@ -300,7 +297,7 @@ class AirtimeInstall
echo "* Installing airtime-update-db-settings".PHP_EOL;
$dir = AirtimeInstall::CONF_DIR_BINARIES."/utils/airtime-update-db-settings";
exec("ln -s $dir /usr/bin/airtime-update-db-settings");
-
+
echo "* Installing airtime-check-system".PHP_EOL;
$dir = AirtimeInstall::CONF_DIR_BINARIES."/utils/airtime-check-system";
exec("ln -s $dir /usr/bin/airtime-check-system");
diff --git a/python_apps/api_clients/api_client.py b/python_apps/api_clients/api_client.py
index 84e1a4d43..016041255 100644
--- a/python_apps/api_clients/api_client.py
+++ b/python_apps/api_clients/api_client.py
@@ -117,6 +117,9 @@ class ApiClientInterface:
def upload_recorded_show(self):
pass
+ def check_media_status(self, md5):
+ pass
+
def update_media_metadata(self, md):
pass
@@ -356,13 +359,52 @@ class AirTimeApiClient(ApiClientInterface):
return response
- def update_media_metadata(self, md):
+ def setup_media_monitor(self):
+ logger = logging.getLogger()
+
+ response = None
+ try:
+ url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["media_setup_url"])
+ url = url.replace("%%api_key%%", self.config["api_key"])
+ logger.debug(url)
+
+ response = urllib.urlopen(url)
+ response = json.loads(response.read())
+ logger.debug("Json Media Setup %s", response)
+
+ except Exception, e:
+ response = None
+ logger.error("Exception: %s", e)
+
+ return response
+
+ def check_media_status(self, md5):
+ logger = logging.getLogger()
+
+ response = None
+ try:
+ url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["media_status_url"])
+ url = url.replace("%%api_key%%", self.config["api_key"])
+ url = url.replace("%%md5%%", md5)
+ logger.debug(url)
+
+ response = urllib.urlopen(url)
+ response = json.loads(response.read())
+ logger.info("Json Media Status %s", response)
+
+ except Exception, e:
+ logger.error("Exception: %s", e)
+
+ return response
+
+ def update_media_metadata(self, md, mode):
logger = logging.getLogger()
response = None
try:
url = "http://%s:%s/%s/%s" % (self.config["base_url"], str(self.config["base_port"]), self.config["api_base"], self.config["update_media_url"])
logger.debug(url)
url = url.replace("%%api_key%%", self.config["api_key"])
+ url = url.replace("%%mode%%", mode)
data = recursive_urlencode(md)
req = urllib2.Request(url, data)
@@ -372,6 +414,7 @@ class AirTimeApiClient(ApiClientInterface):
response = json.loads(response)
except Exception, e:
+ response = None
logger.error("Exception: %s", e)
return response
diff --git a/python_apps/media-monitor/MediaMonitor.py b/python_apps/media-monitor/MediaMonitor.py
index 3143307c2..a909e2874 100644
--- a/python_apps/media-monitor/MediaMonitor.py
+++ b/python_apps/media-monitor/MediaMonitor.py
@@ -9,7 +9,10 @@ import sys
import hashlib
import json
import shutil
+import math
+from collections import deque
+from pwd import getpwnam
from subprocess import Popen, PIPE, STDOUT
from configobj import ConfigObj
@@ -23,8 +26,13 @@ from kombu.connection import BrokerConnection
from kombu.messaging import Exchange, Queue, Consumer, Producer
from api_clients import api_client
+MODE_CREATE = "create"
+MODE_MODIFY = "modify"
+MODE_MOVED = "moved"
+MODE_DELETE = "delete"
+
global storage_directory
-storage_directory = "/srv/airtime/stor"
+global plupload_directory
# configure logging
try:
@@ -46,35 +54,27 @@ list of supported easy tags in mutagen version 1.20
['albumartistsort', 'musicbrainz_albumstatus', 'lyricist', 'releasecountry', 'date', 'performer', 'musicbrainz_albumartistid', 'composer', 'encodedby', 'tracknumber', 'musicbrainz_albumid', 'album', 'asin', 'musicbrainz_artistid', 'mood', 'copyright', 'author', 'media', 'length', 'version', 'artistsort', 'titlesort', 'discsubtitle', 'website', 'musicip_fingerprint', 'conductor', 'compilation', 'barcode', 'performer:*', 'composersort', 'musicbrainz_discid', 'musicbrainz_albumtype', 'genre', 'isrc', 'discnumber', 'musicbrainz_trmid', 'replaygain_*_gain', 'musicip_puid', 'artist', 'title', 'bpm', 'musicbrainz_trackid', 'arranger', 'albumsort', 'replaygain_*_peak', 'organization']
"""
-
-def checkRabbitMQ(notifier):
- try:
- notifier.connection.drain_events(timeout=int(config["check_airtime_events"]))
- except Exception, e:
- logger = logging.getLogger('root')
- logger.info("%s", e)
-
class AirtimeNotifier(Notifier):
def __init__(self, watch_manager, default_proc_fun=None, read_freq=0, threshold=0, timeout=None):
Notifier.__init__(self, watch_manager, default_proc_fun, read_freq, threshold, timeout)
self.airtime2mutagen = {\
- "track_title": "title",\
- "artist_name": "artist",\
- "album_title": "album",\
- "genre": "genre",\
- "mood": "mood",\
- "track_number": "tracknumber",\
- "bpm": "bpm",\
- "label": "organization",\
- "composer": "composer",\
- "encoded_by": "encodedby",\
- "conductor": "conductor",\
- "year": "date",\
- "info_url": "website",\
- "isrc_number": "isrc",\
- "copyright": "copyright",\
+ "MDATA_KEY_TITLE": "title",\
+ "MDATA_KEY_CREATOR": "artist",\
+ "MDATA_KEY_SOURCE": "album",\
+ "MDATA_KEY_GENRE": "genre",\
+ "MDATA_KEY_MOOD": "mood",\
+ "MDATA_KEY_TRACKNUMBER": "tracknumber",\
+ "MDATA_KEY_BPM": "bpm",\
+ "MDATA_KEY_LABEL": "organization",\
+ "MDATA_KEY_COMPOSER": "composer",\
+ "MDATA_KEY_ENCODER": "encodedby",\
+ "MDATA_KEY_CONDUCTOR": "conductor",\
+ "MDATA_KEY_YEAR": "date",\
+ "MDATA_KEY_URL": "website",\
+ "MDATA_KEY_ISRC": "isrc",\
+ "MDATA_KEY_COPYRIGHT": "copyright",\
}
schedule_exchange = Exchange("airtime-media-monitor", "direct", durable=True, auto_delete=True)
@@ -112,27 +112,48 @@ class MediaMonitor(ProcessEvent):
self.api_client = api_client.api_client_factory(config)
self.mutagen2airtime = {\
- "title": "track_title",\
- "artist": "artist_name",\
- "album": "album_title",\
- "genre": "genre",\
- "mood": "mood",\
- "tracknumber": "track_number",\
- "bpm": "bpm",\
- "organization": "label",\
- "composer": "composer",\
- "encodedby": "encoded_by",\
- "conductor": "conductor",\
- "date": "year",\
- "website": "info_url",\
- "isrc": "isrc_number",\
- "copyright": "copyright",\
+ "title": "MDATA_KEY_TITLE",\
+ "artist": "MDATA_KEY_CREATOR",\
+ "album": "MDATA_KEY_SOURCE",\
+ "genre": "MDATA_KEY_GENRE",\
+ "mood": "MDATA_KEY_MOOD",\
+ "tracknumber": "MDATA_KEY_TRACKNUMBER",\
+ "bpm": "MDATA_KEY_BPM",\
+ "organization": "MDATA_KEY_LABEL",\
+ "composer": "MDATA_KEY_COMPOSER",\
+ "encodedby": "MDATA_KEY_ENCODER",\
+ "conductor": "MDATA_KEY_CONDUCTOR",\
+ "date": "MDATA_KEY_YEAR",\
+ "website": "MDATA_KEY_URL",\
+ "isrc": "MDATA_KEY_ISRC",\
+ "copyright": "MDATA_KEY_COPYRIGHT",\
}
self.supported_file_formats = ['mp3', 'ogg']
self.logger = logging.getLogger('root')
self.temp_files = {}
- self.imported_renamed_files = {}
+ self.moved_files = {}
+ self.file_events = deque()
+
+ self.mask = pyinotify.IN_CREATE | \
+ pyinotify.IN_MODIFY | \
+ pyinotify.IN_MOVED_FROM | \
+ pyinotify.IN_MOVED_TO | \
+ pyinotify.IN_DELETE | \
+ pyinotify.IN_DELETE_SELF
+
+ self.wm = WatchManager()
+
+ schedule_exchange = Exchange("airtime-media-monitor", "direct", durable=True, auto_delete=True)
+ schedule_queue = Queue("media-monitor", exchange=schedule_exchange, key="filesystem")
+ connection = BrokerConnection(config["rabbitmq_host"], config["rabbitmq_user"], config["rabbitmq_password"], "/")
+ channel = connection.channel()
+
+ def watch_directory(self, directory):
+ return self.wm.add_watch(directory, self.mask, rec=True, auto_add=True)
+
+ def is_parent_directory(self, filepath, directory):
+ return (directory == filepath[0:len(directory)])
def get_md5(self, filepath):
f = open(filepath, 'rb')
@@ -142,20 +163,40 @@ class MediaMonitor(ProcessEvent):
return md5
- def ensure_dir(self, filepath):
+ ## mutagen_length is in seconds with the format (d+).dd
+ ## return format hh:mm:ss.uuu
+ def format_length(self, mutagen_length):
+ t = float(mutagen_length)
+ h = int(math.floor(t/3600))
+ t = t % 3600
+ m = int(math.floor(t/60))
+ s = t % 60
+ # will be ss.uuu
+ s = str(s)
+ s = s[:6]
+
+ length = "%s:%s:%s" % (h, m, s)
+
+ return length
+
+ def ensure_dir(self, filepath):
directory = os.path.dirname(filepath)
- if ((not os.path.exists(directory)) or ((os.path.exists(directory) and not os.path.isdir(directory)))):
- os.makedirs(directory, 02775)
+ try:
+ omask = os.umask(0)
+ if ((not os.path.exists(directory)) or ((os.path.exists(directory) and not os.path.isdir(directory)))):
+ os.makedirs(directory, 02777)
+ self.watch_directory(directory)
+ finally:
+ os.umask(omask)
def create_unique_filename(self, filepath):
- file_dir = os.path.dirname(filepath)
- filename = os.path.basename(filepath).split(".")[0]
- file_ext = os.path.splitext(filepath)[1]
-
if(os.path.exists(filepath)):
+ file_dir = os.path.dirname(filepath)
+ filename = os.path.basename(filepath).split(".")[0]
+ file_ext = os.path.splitext(filepath)[1]
i = 1;
while(True):
new_filepath = "%s/%s(%s).%s" % (file_dir, filename, i, file_ext)
@@ -165,79 +206,119 @@ class MediaMonitor(ProcessEvent):
else:
filepath = new_filepath
- self.imported_renamed_files[filepath] = 0
-
return filepath
def create_file_path(self, imported_filepath):
global storage_directory
- original_name = os.path.basename(imported_filepath)
- file_ext = os.path.splitext(imported_filepath)[1]
- file_info = mutagen.File(imported_filepath, easy=True)
-
- metadata = {'artist':None,
- 'album':None,
- 'title':None,
- 'tracknumber':None}
-
- for key in metadata.keys():
- if key in file_info:
- metadata[key] = file_info[key][0]
-
- if metadata['artist'] is not None:
- base = "%s/%s" % (storage_directory, metadata['artist'])
- if metadata['album'] is not None:
- base = "%s/%s" % (base, metadata['album'])
- if metadata['title'] is not None:
- if metadata['tracknumber'] is not None:
- metadata['tracknumber'] = "%02d" % (int(metadata['tracknumber']))
- base = "%s/%s - %s" % (base, metadata['tracknumber'], metadata['title'])
- else:
- base = "%s/%s" % (base, metadata['title'])
- else:
- base = "%s/%s" % (base, original_name)
- else:
- base = "%s/%s" % (storage_directory, original_name)
-
- base = "%s%s" % (base, file_ext)
-
- filepath = self.create_unique_filename(base)
- self.ensure_dir(filepath)
- shutil.move(imported_filepath, filepath)
-
- def update_airtime(self, event):
- self.logger.info("Updating Change to Airtime")
try:
- md5 = self.get_md5(event.pathname)
- md = {'filepath':event.pathname, 'md5':md5}
+ #get rid of file extention from original name, name might have more than 1 '.' in it.
+ original_name = os.path.basename(imported_filepath)
+ #self.logger.info('original name: %s', original_name)
+ original_name = original_name.split(".")[0:-1]
+ #self.logger.info('original name: %s', original_name)
+ original_name = ''.join(original_name)
+ #self.logger.info('original name: %s', original_name)
- file_info = mutagen.File(event.pathname, easy=True)
- attrs = self.mutagen2airtime
- for key in file_info.keys() :
- if key in attrs :
- md[attrs[key]] = file_info[key][0]
+ file_ext = os.path.splitext(imported_filepath)[1]
+ file_info = mutagen.File(imported_filepath, easy=True)
- data = {'md': md}
- response = self.api_client.update_media_metadata(data)
+ metadata = {'artist':None,
+ 'album':None,
+ 'title':None,
+ 'tracknumber':None}
+
+ for key in metadata.keys():
+ if key in file_info:
+ metadata[key] = file_info[key][0]
+
+ if metadata['artist'] is not None:
+ base = "%s/%s" % (storage_directory, metadata['artist'])
+ if metadata['album'] is not None:
+ base = "%s/%s" % (base, metadata['album'])
+ if metadata['title'] is not None:
+ if metadata['tracknumber'] is not None:
+ metadata['tracknumber'] = "%02d" % (int(metadata['tracknumber']))
+ base = "%s/%s - %s" % (base, metadata['tracknumber'], metadata['title'])
+ else:
+ base = "%s/%s" % (base, metadata['title'])
+ else:
+ base = "%s/%s" % (base, original_name)
+ else:
+ base = "%s/%s" % (storage_directory, original_name)
+
+ base = "%s%s" % (base, file_ext)
+
+ filepath = self.create_unique_filename(base)
+ self.ensure_dir(filepath)
except Exception, e:
- self.logger.info("%s", e)
+ self.logger.error('Exception: %s', e)
- def is_renamed_file(self, filename):
- if filename in self.imported_renamed_files:
- del self.imported_renamed_files[filename]
- return True
+ return filepath
- return False
+ def get_mutagen_info(self, filepath):
+ md = {}
+ md5 = self.get_md5(filepath)
+ md['MDATA_KEY_MD5'] = md5
+
+ file_info = mutagen.File(filepath, easy=True)
+ attrs = self.mutagen2airtime
+ for key in file_info.keys() :
+ if key in attrs :
+ md[attrs[key]] = file_info[key][0]
+
+ md['MDATA_KEY_BITRATE'] = file_info.info.bitrate
+ md['MDATA_KEY_SAMPLERATE'] = file_info.info.sample_rate
+ md['MDATA_KEY_DURATION'] = self.format_length(file_info.info.length)
+ md['MDATA_KEY_MIME'] = file_info.mime[0]
+
+ if "mp3" in md['MDATA_KEY_MIME']:
+ md['MDATA_KEY_FTYPE'] = "audioclip"
+ elif "vorbis" in md['MDATA_KEY_MIME']:
+ md['MDATA_KEY_FTYPE'] = "audioclip"
+
+ return md
+
+
+ def update_airtime(self, d):
+
+ filepath = d['filepath']
+ mode = d['mode']
+
+ data = None
+ md = {}
+ md['MDATA_KEY_FILEPATH'] = filepath
+
+ if (os.path.exists(filepath) and (mode == MODE_CREATE)):
+ mutagen = self.get_mutagen_info(filepath)
+ md.update(mutagen)
+ data = {'md': md}
+ elif (os.path.exists(filepath) and (mode == MODE_MODIFY)):
+ mutagen = self.get_mutagen_info(filepath)
+ md.update(mutagen)
+ data = {'md': md}
+ elif (mode == MODE_MOVED):
+ mutagen = self.get_mutagen_info(filepath)
+ md.update(mutagen)
+ data = {'md': md}
+ elif (mode == MODE_DELETE):
+ data = {'md': md}
+
+ if data is not None:
+ self.logger.info("Updating Change to Airtime")
+ response = None
+ while response is None:
+ response = self.api_client.update_media_metadata(data, mode)
+ time.sleep(5)
def is_temp_file(self, filename):
info = filename.split(".")
if(info[-2] in self.supported_file_formats):
return True
- else :
+ else:
return False
def is_audio_file(self, filename):
@@ -245,63 +326,124 @@ class MediaMonitor(ProcessEvent):
if(info[-1] in self.supported_file_formats):
return True
- else :
+ else:
return False
-
def process_IN_CREATE(self, event):
if not event.dir:
-
+ self.logger.info("%s: %s", event.maskname, event.pathname)
#file created is a tmp file which will be modified and then moved back to the original filename.
if self.is_temp_file(event.name) :
self.temp_files[event.pathname] = None
#This is a newly imported file.
else :
- #if not is_renamed_file(event.pathname):
- self.create_file_path(event.pathname)
+ global plupload_directory
+ #files that have been added through plupload have a placeholder already put in Airtime's database.
+ if not self.is_parent_directory(event.pathname, plupload_directory):
+ md5 = self.get_md5(event.pathname)
+ response = self.api_client.check_media_status(md5)
- self.logger.info("%s: %s", event.maskname, event.pathname)
+ #this file is new, md5 does not exist in Airtime.
+ if(response['airtime_status'] == 0):
+ filepath = self.create_file_path(event.pathname)
+ os.rename(event.pathname, filepath)
+ self.file_events.append({'mode': MODE_CREATE, 'filepath': filepath})
def process_IN_MODIFY(self, event):
- if not event.dir :
-
- if self.is_audio_file(event.name) :
- self.update_airtime(event)
-
- self.logger.info("%s: %s", event.maskname, event.pathname)
+ if not event.dir:
+ self.logger.info("%s: %s", event.maskname, event.pathname)
+ global plupload_directory
+ #files that have been added through plupload have a placeholder already put in Airtime's database.
+ if not self.is_parent_directory(event.pathname, plupload_directory):
+ if self.is_audio_file(event.name) :
+ self.file_events.append({'filepath': event.pathname, 'mode': MODE_MODIFY})
def process_IN_MOVED_FROM(self, event):
- if event.pathname in self.temp_files :
+ self.logger.info("%s: %s", event.maskname, event.pathname)
+ if event.pathname in self.temp_files:
del self.temp_files[event.pathname]
self.temp_files[event.cookie] = event.pathname
-
- self.logger.info("%s: %s", event.maskname, event.pathname)
+ else:
+ self.moved_files[event.cookie] = event.pathname
def process_IN_MOVED_TO(self, event):
- if event.cookie in self.temp_files :
- del self.temp_files[event.cookie]
- self.update_airtime(event)
-
self.logger.info("%s: %s", event.maskname, event.pathname)
+ if event.cookie in self.temp_files:
+ del self.temp_files[event.cookie]
+ self.file_events.append({'filepath': event.pathname, 'mode': MODE_MODIFY})
+ elif event.cookie in self.moved_files:
+ old_filepath = self.moved_files[event.cookie]
+ del self.moved_files[event.cookie]
+
+ global plupload_directory
+ if self.is_parent_directory(old_filepath, plupload_directory):
+ #file renamed from /tmp/plupload does not have a path in our naming scheme yet.
+ md_filepath = self.create_file_path(event.pathname)
+ #move the file a second time to its correct Airtime naming schema.
+ os.rename(event.pathname, md_filepath)
+ self.file_events.append({'filepath': md_filepath, 'mode': MODE_MOVED})
+ else:
+ self.file_events.append({'filepath': event.pathname, 'mode': MODE_MOVED})
+
+ else:
+ #TODO need to pass in if md5 exists to this file creation function, identical files will just replace current files not have a (1) etc.
+ #file has been most likely dropped into stor folder from an unwatched location. (from gui, mv command not cp)
+ md_filepath = self.create_file_path(event.pathname)
+ os.rename(event.pathname, md_filepath)
+ self.file_events.append({'mode': MODE_CREATE, 'filepath': md_filepath})
+
+ def process_IN_DELETE(self, event):
+ if not event.dir:
+ self.logger.info("%s: %s", event.maskname, event.pathname)
+ self.file_events.append({'filepath': event.pathname, 'mode': MODE_DELETE})
def process_default(self, event):
self.logger.info("%s: %s", event.maskname, event.pathname)
+ def notifier_loop_callback(self, notifier):
+
+ while len(self.file_events) > 0:
+ file_info = self.file_events.popleft()
+ self.update_airtime(file_info)
+
+ try:
+ notifier.connection.drain_events(timeout=1)
+ except Exception, e:
+ self.logger.info("%s", e)
+
if __name__ == '__main__':
try:
- # watched events
- mask = pyinotify.IN_CREATE | pyinotify.IN_MODIFY | pyinotify.IN_MOVED_FROM | pyinotify.IN_MOVED_TO
- #mask = pyinotify.ALL_EVENTS
-
- wm = WatchManager()
- wdd = wm.add_watch(storage_directory, mask, rec=True, auto_add=True)
-
logger = logging.getLogger('root')
- logger.info("Added watch to %s", storage_directory)
+ mm = MediaMonitor()
- notifier = AirtimeNotifier(wm, MediaMonitor(), read_freq=int(config["check_filesystem_events"]), timeout=1)
+ response = None
+ while response is None:
+ response = mm.api_client.setup_media_monitor()
+ time.sleep(5)
+
+ storage_directory = response["stor"]
+ plupload_directory = response["plupload"]
+
+ wdd = mm.watch_directory(storage_directory)
+ logger.info("Added watch to %s", storage_directory)
+ logger.info("wdd result %s", wdd[storage_directory])
+ wdd = mm.watch_directory(plupload_directory)
+ logger.info("Added watch to %s", plupload_directory)
+ logger.info("wdd result %s", wdd[plupload_directory])
+
+ notifier = AirtimeNotifier(mm.wm, mm, read_freq=int(config["check_filesystem_events"]), timeout=1)
notifier.coalesce_events()
- notifier.loop(callback=checkRabbitMQ)
+
+ #notifier.loop(callback=mm.notifier_loop_callback)
+
+ while True:
+ if(notifier.check_events(1)):
+ notifier.read_events()
+ notifier.process_events()
+ mm.notifier_loop_callback(notifier)
+
except KeyboardInterrupt:
notifier.stop()
+ except Exception, e:
+ logger.error('Exception: %s', e)
diff --git a/python_apps/media-monitor/media-monitor.cfg b/python_apps/media-monitor/media-monitor.cfg
index 38b44cea7..e1f36619f 100644
--- a/python_apps/media-monitor/media-monitor.cfg
+++ b/python_apps/media-monitor/media-monitor.cfg
@@ -19,8 +19,14 @@ api_base = 'api'
# URL to get the version number of the server API
version_url = 'version/api_key/%%api_key%%'
+# URL to setup the media monitor
+media_setup_url = 'media-monitor-setup/format/json/api_key/%%api_key%%'
+
+# URL to check Airtime's status of a file
+media_status_url = 'media-item-status/format/json/api_key/%%api_key%%/md5/%%md5%%'
+
# URL to tell Airtime to update file's meta data
-update_media_url = 'reload-metadata/format/json/api_key/%%api_key%%'
+update_media_url = 'reload-metadata/format/json/api_key/%%api_key%%/mode/%%mode%%'
############################################
# RabbitMQ settings #