Merge branch 'devel' of dev.sourcefabric.org:airtime into cc-2528-use-virtualenv-for-installing
Conflicts: python_apps/media-monitor/airtime-media-monitor
This commit is contained in:
commit
107c100cc4
2
VERSION
2
VERSION
|
@ -1,2 +1,2 @@
|
||||||
PRODUCT_ID=Airtime
|
PRODUCT_ID=Airtime
|
||||||
PRODUCT_RELEASE=1.9.0-beta4
|
PRODUCT_RELEASE=1.9.0-RC1
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
define('AIRTIME_VERSION', '1.9.0-beta4');
|
define('AIRTIME_VERSION', '1.9.0-RC1');
|
||||||
define('AIRTIME_COPYRIGHT_DATE', '2010-2011');
|
define('AIRTIME_COPYRIGHT_DATE', '2010-2011');
|
||||||
define('AIRTIME_REST_VERSION', '1.1');
|
define('AIRTIME_REST_VERSION', '1.1');
|
||||||
|
|
||||||
|
|
|
@ -74,8 +74,8 @@ class ApiController extends Zend_Controller_Action
|
||||||
$download = ("true" == $this->_getParam('download'));
|
$download = ("true" == $this->_getParam('download'));
|
||||||
|
|
||||||
$logger = Logging::getLogger();
|
$logger = Logging::getLogger();
|
||||||
|
|
||||||
if(!in_array($api_key, $CC_CONFIG["apiKey"]) &&
|
if(!in_array($api_key, $CC_CONFIG["apiKey"]) &&
|
||||||
is_null(Zend_Auth::getInstance()->getStorage()->read()))
|
is_null(Zend_Auth::getInstance()->getStorage()->read()))
|
||||||
{
|
{
|
||||||
header('HTTP/1.0 401 Unauthorized');
|
header('HTTP/1.0 401 Unauthorized');
|
||||||
|
@ -120,7 +120,7 @@ class ApiController extends Zend_Controller_Action
|
||||||
fclose($fp);
|
fclose($fp);
|
||||||
|
|
||||||
//make sure to exit here so that no other output is sent.
|
//make sure to exit here so that no other output is sent.
|
||||||
exit;
|
exit;
|
||||||
} else {
|
} else {
|
||||||
$logger->err('Resource in database, but not in storage: "'.$filepath.'"');
|
$logger->err('Resource in database, but not in storage: "'.$filepath.'"');
|
||||||
}
|
}
|
||||||
|
@ -336,12 +336,12 @@ class ApiController extends Zend_Controller_Action
|
||||||
$this->view->fileid = $file_id;
|
$this->view->fileid = $file_id;
|
||||||
$this->view->showinstanceid = $show_instance_id;
|
$this->view->showinstanceid = $show_instance_id;
|
||||||
|
|
||||||
|
|
||||||
$showCanceled = false;
|
$showCanceled = false;
|
||||||
$file = StoredFile::Recall($file_id);
|
$file = StoredFile::Recall($file_id);
|
||||||
//$show_instance = $this->_getParam('show_instance');
|
//$show_instance = $this->_getParam('show_instance');
|
||||||
|
|
||||||
$show_name = "";
|
$show_name = null;
|
||||||
try {
|
try {
|
||||||
$show_inst = new ShowInstance($show_instance_id);
|
$show_inst = new ShowInstance($show_instance_id);
|
||||||
|
|
||||||
|
@ -359,10 +359,17 @@ class ApiController extends Zend_Controller_Action
|
||||||
$showCanceled = true;
|
$showCanceled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$tmpTitle = !(empty($show_name))?$show_name."-":"";
|
if (isset($show_name)) {
|
||||||
$tmpTitle .= $file->getName();
|
$tmpTitle = "$show_name-$show_start_time";
|
||||||
|
$tmpTitle = str_replace(" ", "-", $tmpTitle);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$tmpTitle = $file->getName();
|
||||||
|
}
|
||||||
|
|
||||||
$file->setMetadataValue('MDATA_KEY_TITLE', $tmpTitle);
|
$file->setMetadataValue('MDATA_KEY_TITLE', $tmpTitle);
|
||||||
|
$file->setMetadataValue('MDATA_KEY_CREATOR', "Airtime Show Recorder");
|
||||||
|
$file->setMetadataValue('MDATA_KEY_TRACKNUMBER', null);
|
||||||
|
|
||||||
if (!$showCanceled && Application_Model_Preference::GetDoSoundCloudUpload())
|
if (!$showCanceled && Application_Model_Preference::GetDoSoundCloudUpload())
|
||||||
{
|
{
|
||||||
|
@ -392,7 +399,7 @@ class ApiController extends Zend_Controller_Action
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->view->id = $file_id;
|
$this->view->id = $file_id;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mediaMonitorSetupAction() {
|
public function mediaMonitorSetupAction() {
|
||||||
|
@ -409,7 +416,7 @@ class ApiController extends Zend_Controller_Action
|
||||||
print 'You are not allowed to access this resource.';
|
print 'You are not allowed to access this resource.';
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->view->stor = MusicDir::getStorDir()->getDirectory();
|
$this->view->stor = MusicDir::getStorDir()->getDirectory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,7 +446,7 @@ class ApiController extends Zend_Controller_Action
|
||||||
if ($mode == "create") {
|
if ($mode == "create") {
|
||||||
$filepath = $md['MDATA_KEY_FILEPATH'];
|
$filepath = $md['MDATA_KEY_FILEPATH'];
|
||||||
$file = StoredFile::RecallByFilepath($filepath);
|
$file = StoredFile::RecallByFilepath($filepath);
|
||||||
|
|
||||||
if (is_null($file)) {
|
if (is_null($file)) {
|
||||||
$file = StoredFile::Insert($md);
|
$file = StoredFile::Insert($md);
|
||||||
} else {
|
} else {
|
||||||
|
@ -447,7 +454,7 @@ class ApiController extends Zend_Controller_Action
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//Martin Konecny July 14th, 2011: The following commented out code is the way
|
//Martin Konecny July 14th, 2011: The following commented out code is the way
|
||||||
//we used to check for duplicates (by md5). Why are we checking by md5 and
|
//we used to check for duplicates (by md5). Why are we checking by md5 and
|
||||||
//not by filepath?
|
//not by filepath?
|
||||||
|
@ -509,7 +516,7 @@ class ApiController extends Zend_Controller_Action
|
||||||
|
|
||||||
$this->view->id = $file->getId();
|
$this->view->id = $file->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function listAllFilesAction() {
|
public function listAllFilesAction() {
|
||||||
global $CC_CONFIG;
|
global $CC_CONFIG;
|
||||||
|
|
||||||
|
@ -522,10 +529,10 @@ class ApiController extends Zend_Controller_Action
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
$dir_id = $request->getParam('dir_id');
|
$dir_id = $request->getParam('dir_id');
|
||||||
|
|
||||||
$this->view->files = StoredFile::listAllFiles($dir_id);
|
$this->view->files = StoredFile::listAllFiles($dir_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function listAllWatchedDirsAction() {
|
public function listAllWatchedDirsAction() {
|
||||||
global $CC_CONFIG;
|
global $CC_CONFIG;
|
||||||
|
|
||||||
|
@ -537,69 +544,69 @@ class ApiController extends Zend_Controller_Action
|
||||||
print 'You are not allowed to access this resource.';
|
print 'You are not allowed to access this resource.';
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = array();
|
$result = array();
|
||||||
|
|
||||||
$arrWatchedDirs = MusicDir::getWatchedDirs();
|
$arrWatchedDirs = MusicDir::getWatchedDirs();
|
||||||
$storDir = MusicDir::getStorDir();
|
$storDir = MusicDir::getStorDir();
|
||||||
|
|
||||||
$result[$storDir->getId()] = $storDir->getDirectory();
|
$result[$storDir->getId()] = $storDir->getDirectory();
|
||||||
|
|
||||||
foreach ($arrWatchedDirs as $watchedDir){
|
foreach ($arrWatchedDirs as $watchedDir){
|
||||||
$result[$watchedDir->getId()] = $watchedDir->getDirectory();
|
$result[$watchedDir->getId()] = $watchedDir->getDirectory();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->view->dirs = $result;
|
$this->view->dirs = $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addWatchedDirAction() {
|
public function addWatchedDirAction() {
|
||||||
global $CC_CONFIG;
|
global $CC_CONFIG;
|
||||||
|
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
$api_key = $request->getParam('api_key');
|
$api_key = $request->getParam('api_key');
|
||||||
$path = base64_decode($request->getParam('path'));
|
$path = base64_decode($request->getParam('path'));
|
||||||
|
|
||||||
if (!in_array($api_key, $CC_CONFIG["apiKey"]))
|
if (!in_array($api_key, $CC_CONFIG["apiKey"]))
|
||||||
{
|
{
|
||||||
header('HTTP/1.0 401 Unauthorized');
|
header('HTTP/1.0 401 Unauthorized');
|
||||||
print 'You are not allowed to access this resource.';
|
print 'You are not allowed to access this resource.';
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->view->msg = MusicDir::addWatchedDir($path);
|
$this->view->msg = MusicDir::addWatchedDir($path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function removeWatchedDirAction() {
|
public function removeWatchedDirAction() {
|
||||||
global $CC_CONFIG;
|
global $CC_CONFIG;
|
||||||
|
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
$api_key = $request->getParam('api_key');
|
$api_key = $request->getParam('api_key');
|
||||||
$path = base64_decode($request->getParam('path'));
|
$path = base64_decode($request->getParam('path'));
|
||||||
|
|
||||||
if (!in_array($api_key, $CC_CONFIG["apiKey"]))
|
if (!in_array($api_key, $CC_CONFIG["apiKey"]))
|
||||||
{
|
{
|
||||||
header('HTTP/1.0 401 Unauthorized');
|
header('HTTP/1.0 401 Unauthorized');
|
||||||
print 'You are not allowed to access this resource.';
|
print 'You are not allowed to access this resource.';
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->view->msg = MusicDir::removeWatchedDir($path);
|
$this->view->msg = MusicDir::removeWatchedDir($path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setStorageDirAction() {
|
public function setStorageDirAction() {
|
||||||
global $CC_CONFIG;
|
global $CC_CONFIG;
|
||||||
|
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
$api_key = $request->getParam('api_key');
|
$api_key = $request->getParam('api_key');
|
||||||
$path = base64_decode($request->getParam('path'));
|
$path = base64_decode($request->getParam('path'));
|
||||||
|
|
||||||
if (!in_array($api_key, $CC_CONFIG["apiKey"]))
|
if (!in_array($api_key, $CC_CONFIG["apiKey"]))
|
||||||
{
|
{
|
||||||
header('HTTP/1.0 401 Unauthorized');
|
header('HTTP/1.0 401 Unauthorized');
|
||||||
print 'You are not allowed to access this resource.';
|
print 'You are not allowed to access this resource.';
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->view->msg = MusicDir::setStorDir($path);
|
$this->view->msg = MusicDir::setStorDir($path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,12 +63,15 @@ class LibraryController extends Zend_Controller_Action
|
||||||
$paramsPop = str_replace('#id#', $id, $params);
|
$paramsPop = str_replace('#id#', $id, $params);
|
||||||
$paramsPop = str_replace('#type#', $type, $paramsPop);
|
$paramsPop = str_replace('#type#', $type, $paramsPop);
|
||||||
|
|
||||||
|
$userInfo = Zend_Auth::getInstance()->getStorage()->read();
|
||||||
|
$user = new User($userInfo->id);
|
||||||
|
|
||||||
$pl_sess = $this->pl_sess;
|
$pl_sess = $this->pl_sess;
|
||||||
|
|
||||||
if($type === "au") {
|
if($type === "au") {
|
||||||
|
|
||||||
if(isset($pl_sess->id)) {
|
if(isset($pl_sess->id)) {
|
||||||
$menu[] = array('action' => array('type' => 'ajax', 'url' => '/Playlist/add-item'.$params, 'callback' => 'window["setSPLContent"]'),
|
$menu[] = array('action' => array('type' => 'ajax', 'url' => '/Playlist/add-item'.$params, 'callback' => 'window["setSPLContent"]'),
|
||||||
'title' => 'Add to Playlist');
|
'title' => 'Add to Playlist');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,9 +88,12 @@ class LibraryController extends Zend_Controller_Action
|
||||||
$menu[] = array('action' => array('type' => 'gourl', 'url' => $url),
|
$menu[] = array('action' => array('type' => 'gourl', 'url' => $url),
|
||||||
'title' => 'Download');
|
'title' => 'Download');
|
||||||
|
|
||||||
$menu[] = array('action' => array('type' => 'fn',
|
|
||||||
'callback' => "window['confirmDeleteAudioClip']('$paramsPop')"),
|
if ($user->isAdmin()) {
|
||||||
'title' => 'Delete');
|
$menu[] = array('action' => array('type' => 'fn',
|
||||||
|
'callback' => "window['confirmDeleteAudioClip']('$paramsPop')"),
|
||||||
|
'title' => 'Delete');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if($type === "pl") {
|
else if($type === "pl") {
|
||||||
|
|
||||||
|
@ -121,32 +127,38 @@ class LibraryController extends Zend_Controller_Action
|
||||||
public function deleteAction()
|
public function deleteAction()
|
||||||
{
|
{
|
||||||
$id = $this->_getParam('id');
|
$id = $this->_getParam('id');
|
||||||
|
$userInfo = Zend_Auth::getInstance()->getStorage()->read();
|
||||||
|
$user = new User($userInfo->id);
|
||||||
|
|
||||||
if (!is_null($id)) {
|
if ($user->isAdmin()) {
|
||||||
$file = StoredFile::Recall($id);
|
|
||||||
|
|
||||||
if (PEAR::isError($file)) {
|
if (!is_null($id)) {
|
||||||
$this->view->message = $file->getMessage();
|
$file = StoredFile::Recall($id);
|
||||||
return;
|
|
||||||
}
|
if (PEAR::isError($file)) {
|
||||||
else if(is_null($file)) {
|
$this->view->message = $file->getMessage();
|
||||||
$this->view->message = "file doesn't exist";
|
return;
|
||||||
return;
|
}
|
||||||
|
else if(is_null($file)) {
|
||||||
|
$this->view->message = "file doesn't exist";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$res = $file->delete();
|
||||||
|
|
||||||
|
if (PEAR::isError($res)) {
|
||||||
|
$this->view->message = $res->getMessage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$res = settype($res, "integer");
|
||||||
|
$data = array("filepath" => $file->getFilePath(), "delete" => $res);
|
||||||
|
RabbitMq::SendMessageToMediaMonitor("file_delete", $data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$res = $file->delete();
|
$this->view->id = $id;
|
||||||
|
|
||||||
if (PEAR::isError($res)) {
|
|
||||||
$this->view->message = $res->getMessage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$data = array("filepath" => $file->getFilePath(), "delete" => $res);
|
|
||||||
RabbitMq::SendMessageToMediaMonitor("file_delete", $data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->view->id = $id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function contentsAction()
|
public function contentsAction()
|
||||||
|
|
|
@ -24,10 +24,10 @@ class NowplayingController extends Zend_Controller_Action
|
||||||
$refer_sses = new Zend_Session_Namespace('referrer');
|
$refer_sses = new Zend_Session_Namespace('referrer');
|
||||||
$userInfo = Zend_Auth::getInstance()->getStorage()->read();
|
$userInfo = Zend_Auth::getInstance()->getStorage()->read();
|
||||||
$user = new User($userInfo->id);
|
$user = new User($userInfo->id);
|
||||||
|
|
||||||
$form = new Application_Form_RegisterAirtime();
|
|
||||||
|
|
||||||
if ($request->isPost()) {
|
if ($request->isPost()) {
|
||||||
|
$form = new Application_Form_RegisterAirtime();
|
||||||
|
|
||||||
$values = $request->getPost();
|
$values = $request->getPost();
|
||||||
if ($values["Publicise"] != 1){
|
if ($values["Publicise"] != 1){
|
||||||
Application_Model_Preference::SetSupportFeedback($values["SupportFeedback"]);
|
Application_Model_Preference::SetSupportFeedback($values["SupportFeedback"]);
|
||||||
|
@ -64,6 +64,10 @@ class NowplayingController extends Zend_Controller_Action
|
||||||
//popup if previous page was login
|
//popup if previous page was login
|
||||||
if($refer_sses->referrer == 'login' && Application_Model_Nowplaying::ShouldShowPopUp()
|
if($refer_sses->referrer == 'login' && Application_Model_Nowplaying::ShouldShowPopUp()
|
||||||
&& !Application_Model_Preference::GetSupportFeedback() && $user->isAdmin()){
|
&& !Application_Model_Preference::GetSupportFeedback() && $user->isAdmin()){
|
||||||
|
|
||||||
|
$form = new Application_Form_RegisterAirtime();
|
||||||
|
|
||||||
|
|
||||||
$logo = Application_Model_Preference::GetStationLogo();
|
$logo = Application_Model_Preference::GetStationLogo();
|
||||||
if($logo){
|
if($logo){
|
||||||
$this->view->logoImg = $logo;
|
$this->view->logoImg = $logo;
|
||||||
|
|
|
@ -123,9 +123,10 @@ class PreferenceController extends Zend_Controller_Action
|
||||||
$chosen = $this->getRequest()->getParam("dir");
|
$chosen = $this->getRequest()->getParam("dir");
|
||||||
$element = $this->getRequest()->getParam("element");
|
$element = $this->getRequest()->getParam("element");
|
||||||
$watched_dirs_form = new Application_Form_WatchedDirPreferences();
|
$watched_dirs_form = new Application_Form_WatchedDirPreferences();
|
||||||
$watched_dirs_form->populate(array('storageFolder' => $chosen));
|
|
||||||
$res = MusicDir::setStorDir($chosen);
|
$res = MusicDir::setStorDir($chosen);
|
||||||
if($res['code'] != 0){
|
if($res['code'] != 0){
|
||||||
|
$watched_dirs_form->populate(array('storageFolder' => $chosen));
|
||||||
$watched_dirs_form->getElement($element)->setErrors(array($res['error']));
|
$watched_dirs_form->getElement($element)->setErrors(array($res['error']));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,9 +138,10 @@ class PreferenceController extends Zend_Controller_Action
|
||||||
$chosen = $this->getRequest()->getParam("dir");
|
$chosen = $this->getRequest()->getParam("dir");
|
||||||
$element = $this->getRequest()->getParam("element");
|
$element = $this->getRequest()->getParam("element");
|
||||||
$watched_dirs_form = new Application_Form_WatchedDirPreferences();
|
$watched_dirs_form = new Application_Form_WatchedDirPreferences();
|
||||||
$watched_dirs_form->populate(array('watchedFolder' => $chosen));
|
|
||||||
$res = MusicDir::addWatchedDir($chosen);
|
$res = MusicDir::addWatchedDir($chosen);
|
||||||
if($res['code'] != 0){
|
if($res['code'] != 0){
|
||||||
|
$watched_dirs_form->populate(array('watchedFolder' => $chosen));
|
||||||
$watched_dirs_form->getElement($element)->setErrors(array($res['error']));
|
$watched_dirs_form->getElement($element)->setErrors(array($res['error']));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
class NestedDirectoryException extends Exception { }
|
||||||
|
|
||||||
class MusicDir {
|
class MusicDir {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,7 +42,75 @@ class MusicDir {
|
||||||
|
|
||||||
public function remove()
|
public function remove()
|
||||||
{
|
{
|
||||||
|
global $CC_DBC;
|
||||||
|
|
||||||
|
$music_dir_id = $this->getId();
|
||||||
|
|
||||||
|
$sql = "SELECT DISTINCT s.instance_id from cc_music_dirs as md LEFT JOIN cc_files as f on f.directory = md.id
|
||||||
|
RIGHT JOIN cc_schedule as s on s.file_id = f.id WHERE md.id = $music_dir_id";
|
||||||
|
|
||||||
|
$show_instances = $CC_DBC->GetAll($sql);
|
||||||
|
|
||||||
$this->_dir->delete();
|
$this->_dir->delete();
|
||||||
|
|
||||||
|
foreach ($show_instances as $show_instance_row) {
|
||||||
|
$temp_show = new ShowInstance($show_instance_row["instance_id"]);
|
||||||
|
$temp_show->updateScheduledTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
RabbitMq::PushSchedule();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if p_dir1 is the ancestor of p_dir2. Returns
|
||||||
|
* true if it is the ancestor, false otherwise. Note that
|
||||||
|
* /home/user is considered the ancestor of /home/user
|
||||||
|
*
|
||||||
|
* @param string $p_dir1
|
||||||
|
* The potential ancestor directory.
|
||||||
|
* @param string $p_dir2
|
||||||
|
* The potential descendent directory.
|
||||||
|
* @return boolean
|
||||||
|
* Returns true if it is the ancestor, false otherwise.
|
||||||
|
*/
|
||||||
|
private static function isAncestorDir($p_dir1, $p_dir2){
|
||||||
|
if (strlen($p_dir1) > strlen($p_dir2)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return substr($p_dir2, 0, strlen($p_dir1)) == $p_dir1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the path provided is a valid path. A valid path
|
||||||
|
* is defined as not being nested within an existing watched directory,
|
||||||
|
* or vice-versa. Throws a NestedDirectoryException if invalid.
|
||||||
|
*
|
||||||
|
* @param string $p_path
|
||||||
|
* The path we want to validate
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function isPathValid($p_path){
|
||||||
|
$dirs = self::getWatchedDirs();
|
||||||
|
$dirs[] = self::getStorDir();
|
||||||
|
|
||||||
|
foreach ($dirs as $dirObj){
|
||||||
|
$dir = $dirObj->getDirectory();
|
||||||
|
$diff = strlen($dir) - strlen($p_path);
|
||||||
|
if ($diff == 0){
|
||||||
|
if ($dir == $p_path){
|
||||||
|
throw new NestedDirectoryException("'$p_path' is already watched.");
|
||||||
|
}
|
||||||
|
} else if ($diff > 0){
|
||||||
|
if (self::isAncestorDir($p_path, $dir)){
|
||||||
|
throw new NestedDirectoryException("'$p_path' contains nested watched directory: '$dir'");
|
||||||
|
}
|
||||||
|
} else { /* diff < 0*/
|
||||||
|
if (self::isAncestorDir($dir, $p_path)){
|
||||||
|
throw new NestedDirectoryException("'$p_path' is nested within existing watched directory: '$dir'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function addDir($p_path, $p_type)
|
public static function addDir($p_path, $p_type)
|
||||||
|
@ -51,16 +121,23 @@ class MusicDir {
|
||||||
$dir = new CcMusicDirs();
|
$dir = new CcMusicDirs();
|
||||||
$dir->setType($p_type);
|
$dir->setType($p_type);
|
||||||
$p_path = realpath($p_path)."/";
|
$p_path = realpath($p_path)."/";
|
||||||
$temp = $dir->setDirectory($p_path);
|
|
||||||
try{
|
|
||||||
|
try {
|
||||||
|
/* isPathValid() checks if path is a substring or a superstring of an
|
||||||
|
* existing dir and if not, throws NestedDirectoryException */
|
||||||
|
self::isPathValid($p_path);
|
||||||
|
$dir->setDirectory($p_path);
|
||||||
|
|
||||||
$dir->save();
|
$dir->save();
|
||||||
return array("code"=>0);
|
return array("code"=>0);
|
||||||
}
|
} catch (NestedDirectoryException $nde){
|
||||||
catch(Exception $e){
|
$msg = $nde->getMessage();
|
||||||
//echo $e->getMessage();
|
return array("code"=>1, "error"=>"$msg");
|
||||||
|
} catch(Exception $e){
|
||||||
return array("code"=>1, "error"=>"'$p_path' is already set as the current storage dir or in the watched folders list");
|
return array("code"=>1, "error"=>"'$p_path' is already set as the current storage dir or in the watched folders list");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function addWatchedDir($p_path)
|
public static function addWatchedDir($p_path)
|
||||||
|
@ -123,8 +200,8 @@ class MusicDir {
|
||||||
$mus_dir = new MusicDir($dir);
|
$mus_dir = new MusicDir($dir);
|
||||||
|
|
||||||
return $mus_dir;
|
return $mus_dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function setStorDir($p_dir)
|
public static function setStorDir($p_dir)
|
||||||
{
|
{
|
||||||
if(!is_dir($p_dir)){
|
if(!is_dir($p_dir)){
|
||||||
|
@ -162,7 +239,7 @@ class MusicDir {
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function removeWatchedDir($p_dir){
|
public static function removeWatchedDir($p_dir){
|
||||||
$p_dir = realpath($p_dir)."/";
|
$p_dir = realpath($p_dir)."/";
|
||||||
$dir = MusicDir::getDirByPath($p_dir);
|
$dir = MusicDir::getDirByPath($p_dir);
|
||||||
|
@ -180,7 +257,7 @@ class MusicDir {
|
||||||
public static function splitFilePath($p_filepath)
|
public static function splitFilePath($p_filepath)
|
||||||
{
|
{
|
||||||
$mus_dir = self::getWatchedDirFromFilepath($p_filepath);
|
$mus_dir = self::getWatchedDirFromFilepath($p_filepath);
|
||||||
|
|
||||||
if(is_null($mus_dir)) {
|
if(is_null($mus_dir)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ class ScheduleGroup {
|
||||||
|
|
||||||
// Check if there are any conflicts with existing entries
|
// Check if there are any conflicts with existing entries
|
||||||
$metadata = $track->getMetadata();
|
$metadata = $track->getMetadata();
|
||||||
$length = trim($metadata["length"]);
|
$length = $metadata['MDATA_KEY_DURATION'];
|
||||||
if (empty($length)) {
|
if (empty($length)) {
|
||||||
return new PEAR_Error("Length is empty.");
|
return new PEAR_Error("Length is empty.");
|
||||||
}
|
}
|
||||||
|
@ -540,7 +540,7 @@ class Schedule {
|
||||||
$retVal = $CC_DBC->query($sql);
|
$retVal = $CC_DBC->query($sql);
|
||||||
return $retVal;
|
return $retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getSchduledPlaylistCount(){
|
public static function getSchduledPlaylistCount(){
|
||||||
global $CC_CONFIG, $CC_DBC;
|
global $CC_CONFIG, $CC_DBC;
|
||||||
$sql = "SELECT count(*) as cnt FROM ".$CC_CONFIG['scheduleTable'];
|
$sql = "SELECT count(*) as cnt FROM ".$CC_CONFIG['scheduleTable'];
|
||||||
|
@ -750,7 +750,7 @@ class Schedule {
|
||||||
global $CC_CONFIG, $CC_DBC;
|
global $CC_CONFIG, $CC_DBC;
|
||||||
$CC_DBC->query("TRUNCATE TABLE ".$CC_CONFIG["scheduleTable"]);
|
$CC_DBC->query("TRUNCATE TABLE ".$CC_CONFIG["scheduleTable"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function createNewFormSections($p_view){
|
public static function createNewFormSections($p_view){
|
||||||
$formWhat = new Application_Form_AddShowWhat();
|
$formWhat = new Application_Form_AddShowWhat();
|
||||||
$formWho = new Application_Form_AddShowWho();
|
$formWho = new Application_Form_AddShowWho();
|
||||||
|
@ -769,7 +769,7 @@ class Schedule {
|
||||||
$formRecord->removeDecorator('DtDdWrapper');
|
$formRecord->removeDecorator('DtDdWrapper');
|
||||||
$formAbsoluteRebroadcast->removeDecorator('DtDdWrapper');
|
$formAbsoluteRebroadcast->removeDecorator('DtDdWrapper');
|
||||||
$formRebroadcast->removeDecorator('DtDdWrapper');
|
$formRebroadcast->removeDecorator('DtDdWrapper');
|
||||||
|
|
||||||
$p_view->what = $formWhat;
|
$p_view->what = $formWhat;
|
||||||
$p_view->when = $formWhen;
|
$p_view->when = $formWhen;
|
||||||
$p_view->repeats = $formRepeats;
|
$p_view->repeats = $formRepeats;
|
||||||
|
@ -779,7 +779,7 @@ class Schedule {
|
||||||
$p_view->absoluteRebroadcast = $formAbsoluteRebroadcast;
|
$p_view->absoluteRebroadcast = $formAbsoluteRebroadcast;
|
||||||
$p_view->rebroadcast = $formRebroadcast;
|
$p_view->rebroadcast = $formRebroadcast;
|
||||||
$p_view->addNewShow = true;
|
$p_view->addNewShow = true;
|
||||||
|
|
||||||
$formWhat->populate(array('add_show_id' => '-1'));
|
$formWhat->populate(array('add_show_id' => '-1'));
|
||||||
$formWhen->populate(array('add_show_start_date' => date("Y-m-d"),
|
$formWhen->populate(array('add_show_start_date' => date("Y-m-d"),
|
||||||
'add_show_start_time' => '00:00',
|
'add_show_start_time' => '00:00',
|
||||||
|
|
|
@ -318,6 +318,13 @@ class StoredFile {
|
||||||
|
|
||||||
Playlist::DeleteFileFromAllPlaylists($this->getId());
|
Playlist::DeleteFileFromAllPlaylists($this->getId());
|
||||||
$this->_file->delete();
|
$this->_file->delete();
|
||||||
|
|
||||||
|
if (isset($res)) {
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -341,18 +348,7 @@ class StoredFile {
|
||||||
return PEAR::raiseError('Cannot delete a file that is scheduled in the future.');
|
return PEAR::raiseError('Cannot delete a file that is scheduled in the future.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$storageDir = MusicDir::getStorDir()->getDirectory();
|
return true;
|
||||||
$dirCompare = substr($this->getFilePath(), 0, strlen($storageDir));
|
|
||||||
|
|
||||||
//return PEAR::raiseError("({$storageDir} , {$dirCompare})");
|
|
||||||
|
|
||||||
// Only delete the file from filesystem if it has been copied to the storage directory
|
|
||||||
if ($dirCompare === $storageDir) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
//used by jjmenu
|
//used by jjmenu
|
||||||
function getId() {
|
function getId() {
|
||||||
var tr_id = $(this.triggerElement).attr("id");
|
var tr_id = $(this.triggerElement).attr("id");
|
||||||
tr_id = tr_id.split("_");
|
tr_id = tr_id.split("_");
|
||||||
|
|
||||||
return tr_id[1];
|
return tr_id[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getType() {
|
function getType() {
|
||||||
var tr_id = $(this.triggerElement).attr("id");
|
var tr_id = $(this.triggerElement).attr("id");
|
||||||
tr_id = tr_id.split("_");
|
tr_id = tr_id.split("_");
|
||||||
|
|
||||||
|
@ -25,8 +25,8 @@ function deleteItem(type, id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteAudioClip(json) {
|
function deleteAudioClip(json) {
|
||||||
if(json.message) {
|
if(json.message) {
|
||||||
alert(json.message);
|
alert(json.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,13 +35,13 @@ function deleteAudioClip(json) {
|
||||||
|
|
||||||
//callbacks called by jjmenu
|
//callbacks called by jjmenu
|
||||||
function confirmDeleteAudioClip(params){
|
function confirmDeleteAudioClip(params){
|
||||||
if(confirm('Are you sure you want to delete?')){
|
if(confirm('The file will be deleted from disk, are you sure you want to delete?')){
|
||||||
var url = '/Library/delete' + params;
|
var url = '/Library/delete' + params;
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: url,
|
url: url,
|
||||||
success: deleteAudioClip
|
success: deleteAudioClip
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//callbacks called by jjmenu
|
//callbacks called by jjmenu
|
||||||
|
@ -52,12 +52,12 @@ function confirmDeletePlaylist(params){
|
||||||
url: url,
|
url: url,
|
||||||
success: deletePlaylist
|
success: deletePlaylist
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function deletePlaylist(json) {
|
function deletePlaylist(json) {
|
||||||
if(json.message) {
|
if(json.message) {
|
||||||
alert(json.message);
|
alert(json.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,15 +69,15 @@ function deletePlaylist(json) {
|
||||||
function addLibraryItemEvents() {
|
function addLibraryItemEvents() {
|
||||||
|
|
||||||
$('#library_display tr[id ^= "au"]')
|
$('#library_display tr[id ^= "au"]')
|
||||||
.draggable({
|
.draggable({
|
||||||
helper: 'clone',
|
helper: 'clone',
|
||||||
cursor: 'pointer'
|
cursor: 'pointer'
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#library_display tbody tr')
|
$('#library_display tbody tr')
|
||||||
.jjmenu("click",
|
.jjmenu("click",
|
||||||
[{get:"/Library/context-menu/format/json/id/#id#/type/#type#"}],
|
[{get:"/Library/context-menu/format/json/id/#id#/type/#type#"}],
|
||||||
{id: getId, type: getType},
|
{id: getId, type: getType},
|
||||||
{xposition: "mouse", yposition: "mouse"});
|
{xposition: "mouse", yposition: "mouse"});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ function dtRowCallback( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$(nRow).attr("id", type+'_'+id);
|
$(nRow).attr("id", type+'_'+id);
|
||||||
|
|
||||||
// insert id on lenth field
|
// insert id on lenth field
|
||||||
$('td:eq(4)', nRow).attr("id", "length");
|
$('td:eq(4)', nRow).attr("id", "length");
|
||||||
|
|
||||||
|
@ -111,14 +111,14 @@ function dtRowCallback( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
|
||||||
button: 'Close' // Show a close link in the title
|
button: 'Close' // Show a close link in the title
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
position: {
|
position: {
|
||||||
|
|
||||||
adjust: {
|
adjust: {
|
||||||
screen: true // Keep the tooltip on-screen at all times
|
screen: true // Keep the tooltip on-screen at all times
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
style: {
|
style: {
|
||||||
border: {
|
border: {
|
||||||
width: 0,
|
width: 0,
|
||||||
|
@ -146,16 +146,16 @@ $(document).ready(function() {
|
||||||
"sAjaxSource": "/Library/contents/format/json",
|
"sAjaxSource": "/Library/contents/format/json",
|
||||||
"fnServerData": function ( sSource, aoData, fnCallback ) {
|
"fnServerData": function ( sSource, aoData, fnCallback ) {
|
||||||
$.ajax( {
|
$.ajax( {
|
||||||
"dataType": 'json',
|
"dataType": 'json',
|
||||||
"type": "POST",
|
"type": "POST",
|
||||||
"url": sSource,
|
"url": sSource,
|
||||||
"data": aoData,
|
"data": aoData,
|
||||||
"success": fnCallback
|
"success": fnCallback
|
||||||
} );
|
} );
|
||||||
},
|
},
|
||||||
"fnRowCallback": dtRowCallback,
|
"fnRowCallback": dtRowCallback,
|
||||||
"fnDrawCallback": dtDrawCallback,
|
"fnDrawCallback": dtDrawCallback,
|
||||||
"aoColumns": [
|
"aoColumns": [
|
||||||
/* Id */ { "sName": "id", "bSearchable": false, "bVisible": false },
|
/* Id */ { "sName": "id", "bSearchable": false, "bVisible": false },
|
||||||
/* Title */ { "sName": "track_title" },
|
/* Title */ { "sName": "track_title" },
|
||||||
/* Creator */ { "sName": "artist_name" },
|
/* Creator */ { "sName": "artist_name" },
|
||||||
|
|
|
@ -33,7 +33,7 @@ function setWatchedDirEvents() {
|
||||||
//knownPaths: [{text:'Desktop', image:'desktop.png', path:'/home'}],
|
//knownPaths: [{text:'Desktop', image:'desktop.png', path:'/home'}],
|
||||||
knownPaths: [],
|
knownPaths: [],
|
||||||
imageUrl: 'img/icons/',
|
imageUrl: 'img/icons/',
|
||||||
systemImageUrl: 'img/browser/',
|
systemImageUrl: '/css/img/',
|
||||||
handlerUrl: '/Preference/server-browse/format/json',
|
handlerUrl: '/Preference/server-browse/format/json',
|
||||||
title: 'Choose Folder to Watch',
|
title: 'Choose Folder to Watch',
|
||||||
basePath: '',
|
basePath: '',
|
||||||
|
|
|
@ -3,8 +3,9 @@
|
||||||
namespace DoctrineMigrations;
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
update cc_files table to include to "directory" column as well as add foreign key relation to
|
1) update cc_files table to include to "directory" column
|
||||||
cc_music_dirs table.
|
2) create a foreign key relationship from cc_files to cc_music_dirs
|
||||||
|
3) create a foreign key relationship from cc_schedule to cc_files
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use Doctrine\DBAL\Migrations\AbstractMigration,
|
use Doctrine\DBAL\Migrations\AbstractMigration,
|
||||||
|
@ -14,21 +15,22 @@ class Version20110711161043 extends AbstractMigration
|
||||||
{
|
{
|
||||||
public function up(Schema $schema)
|
public function up(Schema $schema)
|
||||||
{
|
{
|
||||||
|
/* 1) update cc_files table to include to "directory" column */
|
||||||
//CREATE the default value of "/srv/airtime/stor", this can be updated later in the upgrade script.
|
|
||||||
$this->_addSql("INSERT INTO cc_music_dirs (type, directory) VALUES ('stor', '/srv/airtime/stor');");
|
$this->_addSql("INSERT INTO cc_music_dirs (type, directory) VALUES ('stor', '/srv/airtime/stor');");
|
||||||
|
|
||||||
$this->_addSql("INSERT INTO cc_music_dirs (type, directory) VALUES ('upgrade', '');");
|
$this->_addSql("INSERT INTO cc_music_dirs (type, directory) VALUES ('upgrade', '');");
|
||||||
|
|
||||||
$cc_music_dirs = $schema->getTable('cc_music_dirs');
|
$cc_music_dirs = $schema->getTable('cc_music_dirs');
|
||||||
|
|
||||||
//start cc_files modifications
|
/* 2) create a foreign key relationship from cc_files to cc_music_dirs */
|
||||||
$cc_files = $schema->getTable('cc_files');
|
$cc_files = $schema->getTable('cc_files');
|
||||||
$cc_files->addColumn('directory', 'integer', array('default'=> 2));
|
$cc_files->addColumn('directory', 'integer', array('default'=> 2));
|
||||||
|
|
||||||
$cc_files->addNamedForeignKeyConstraint('cc_music_dirs_folder_fkey', $cc_music_dirs, array('directory'), array('id'), array('onDelete' => 'CASCADE'));
|
$cc_files->addNamedForeignKeyConstraint('cc_music_dirs_folder_fkey', $cc_music_dirs, array('directory'), array('id'), array('onDelete' => 'CASCADE'));
|
||||||
//end cc_files modifications
|
|
||||||
|
/* 3) create a foreign key relationship from cc_schedule to cc_files */
|
||||||
|
$cc_schedule = $schema->getTable('cc_schedule');
|
||||||
|
$cc_schedule->addNamedForeignKeyConstraint('cc_files_folder_fkey', $cc_files, array('file_id'), array('id'), array('onDelete' => 'CASCADE'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function down(Schema $schema)
|
public function down(Schema $schema)
|
||||||
|
|
|
@ -19,6 +19,9 @@ python ${SCRIPTPATH}/../python_apps/create-pypo-user.py
|
||||||
|
|
||||||
php ${SCRIPTPATH}/include/airtime-install.php $@
|
php ${SCRIPTPATH}/include/airtime-install.php $@
|
||||||
|
|
||||||
|
echo -e "\n*** API Client Installation ***"
|
||||||
|
python ${SCRIPTPATH}/../python_apps/api_clients/install/api_client_install.py
|
||||||
|
|
||||||
echo -e "\n*** Pypo Installation ***"
|
echo -e "\n*** Pypo Installation ***"
|
||||||
python ${SCRIPTPATH}/../python_apps/pypo/install/pypo-install.py
|
python ${SCRIPTPATH}/../python_apps/pypo/install/pypo-install.py
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,10 @@ python ${SCRIPTPATH}/../python_apps/show-recorder/install/recorder-uninstall.py
|
||||||
echo -e "\n*** Uninstalling Media Monitor ***"
|
echo -e "\n*** Uninstalling Media Monitor ***"
|
||||||
python ${SCRIPTPATH}/../python_apps/media-monitor/install/media-monitor-uninstall.py
|
python ${SCRIPTPATH}/../python_apps/media-monitor/install/media-monitor-uninstall.py
|
||||||
|
|
||||||
|
echo -e "\n*** Uninstalling API Client ***"
|
||||||
|
python ${SCRIPTPATH}/../python_apps/api_clients/install/api_client_uninstall.py
|
||||||
|
|
||||||
|
|
||||||
echo -e "\n*** Removing Pypo User ***"
|
echo -e "\n*** Removing Pypo User ***"
|
||||||
python ${SCRIPTPATH}/../python_apps/remove-pypo-user.py
|
python ${SCRIPTPATH}/../python_apps/remove-pypo-user.py
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ class AirtimeIni
|
||||||
const CONF_FILE_AIRTIME = "/etc/airtime/airtime.conf";
|
const CONF_FILE_AIRTIME = "/etc/airtime/airtime.conf";
|
||||||
const CONF_FILE_PYPO = "/etc/airtime/pypo.cfg";
|
const CONF_FILE_PYPO = "/etc/airtime/pypo.cfg";
|
||||||
const CONF_FILE_RECORDER = "/etc/airtime/recorder.cfg";
|
const CONF_FILE_RECORDER = "/etc/airtime/recorder.cfg";
|
||||||
|
const CONF_FILE_API_CLIENT = "/etc/airtime/api_client.cfg";
|
||||||
const CONF_FILE_LIQUIDSOAP = "/etc/airtime/liquidsoap.cfg";
|
const CONF_FILE_LIQUIDSOAP = "/etc/airtime/liquidsoap.cfg";
|
||||||
const CONF_FILE_MEDIAMONITOR = "/etc/airtime/media-monitor.cfg";
|
const CONF_FILE_MEDIAMONITOR = "/etc/airtime/media-monitor.cfg";
|
||||||
const CONF_FILE_MONIT = "/etc/monit/conf.d/airtime-monit.cfg";
|
const CONF_FILE_MONIT = "/etc/monit/conf.d/airtime-monit.cfg";
|
||||||
|
@ -65,6 +66,10 @@ class AirtimeIni
|
||||||
echo "Could not copy airtime.conf to /etc/airtime/. Exiting.";
|
echo "Could not copy airtime.conf to /etc/airtime/. Exiting.";
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
if (!copy(__DIR__."/../../python_apps/api_clients/api_client.cfg", AirtimeIni::CONF_FILE_API_CLIENT)){
|
||||||
|
echo "Could not copy api_client.cfg to /etc/airtime/. Exiting.";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
if (!copy(__DIR__."/../../python_apps/pypo/pypo.cfg", AirtimeIni::CONF_FILE_PYPO)){
|
if (!copy(__DIR__."/../../python_apps/pypo/pypo.cfg", AirtimeIni::CONF_FILE_PYPO)){
|
||||||
echo "Could not copy pypo.cfg to /etc/airtime/. Exiting.";
|
echo "Could not copy pypo.cfg to /etc/airtime/. Exiting.";
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
|
|
||||||
//Pear classes.
|
//Pear classes.
|
||||||
set_include_path(__DIR__.'/../../airtime_mvc/library/pear' . PATH_SEPARATOR . get_include_path());
|
set_include_path(__DIR__.'/../../airtime_mvc/library/pear' . PATH_SEPARATOR . get_include_path());
|
||||||
|
|
||||||
require_once('DB.php');
|
require_once('DB.php');
|
||||||
|
require_once(__DIR__.'/../../airtime_mvc/application/configs/constants.php');
|
||||||
require_once(dirname(__FILE__).'/AirtimeIni.php');
|
require_once(dirname(__FILE__).'/AirtimeIni.php');
|
||||||
|
|
||||||
if(exec("whoami") != "root"){
|
if(exec("whoami") != "root"){
|
||||||
|
@ -87,9 +89,13 @@ if (strcmp($version, "1.9.0") < 0){
|
||||||
//set the new version in the database.
|
//set the new version in the database.
|
||||||
$sql = "DELETE FROM cc_pref WHERE keystr = 'system_version'";
|
$sql = "DELETE FROM cc_pref WHERE keystr = 'system_version'";
|
||||||
$CC_DBC->query($sql);
|
$CC_DBC->query($sql);
|
||||||
$sql = "INSERT INTO cc_pref (keystr, valstr) VALUES ('system_version', '1.9.0-devel')";
|
|
||||||
|
$newVersion = AIRTIME_VERSION;
|
||||||
|
$sql = "INSERT INTO cc_pref (keystr, valstr) VALUES ('system_version', '$newVersion')";
|
||||||
$CC_DBC->query($sql);
|
$CC_DBC->query($sql);
|
||||||
|
|
||||||
|
echo PHP_EOL."*** Updating Api Client ***".PHP_EOL;
|
||||||
|
passthru("python ".__DIR__."/../../python_apps/api_clients/install/api_client_install.py");
|
||||||
|
|
||||||
echo PHP_EOL."*** Updating Pypo ***".PHP_EOL;
|
echo PHP_EOL."*** Updating Pypo ***".PHP_EOL;
|
||||||
passthru("python ".__DIR__."/../../python_apps/pypo/install/pypo-install.py");
|
passthru("python ".__DIR__."/../../python_apps/pypo/install/pypo-install.py");
|
||||||
|
|
|
@ -321,7 +321,7 @@ class AirtimeInstall{
|
||||||
INSERT INTO cc_country (isocode, name) VALUES ('ZWE', 'Zimbabwe ');";
|
INSERT INTO cc_country (isocode, name) VALUES ('ZWE', 'Zimbabwe ');";
|
||||||
|
|
||||||
echo "* Inserting data into country table".PHP_EOL;
|
echo "* Inserting data into country table".PHP_EOL;
|
||||||
execSqlQuery($sql);
|
Airtime190Upgrade::execSqlQuery($sql);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,7 +332,7 @@ class AirtimeIni{
|
||||||
const CONF_FILE_RECORDER = "/etc/airtime/recorder.cfg";
|
const CONF_FILE_RECORDER = "/etc/airtime/recorder.cfg";
|
||||||
const CONF_FILE_LIQUIDSOAP = "/etc/airtime/liquidsoap.cfg";
|
const CONF_FILE_LIQUIDSOAP = "/etc/airtime/liquidsoap.cfg";
|
||||||
const CONF_FILE_MEDIAMONITOR = "/etc/airtime/media-monitor.cfg";
|
const CONF_FILE_MEDIAMONITOR = "/etc/airtime/media-monitor.cfg";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function updates an INI style config file.
|
* This function updates an INI style config file.
|
||||||
*
|
*
|
||||||
|
@ -420,7 +420,7 @@ class AirtimeIni{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function upgradeConfigFiles(){
|
public static function upgradeConfigFiles(){
|
||||||
|
|
||||||
$configFiles = array(AirtimeIni::CONF_FILE_AIRTIME,
|
$configFiles = array(AirtimeIni::CONF_FILE_AIRTIME,
|
||||||
AirtimeIni::CONF_FILE_PYPO,
|
AirtimeIni::CONF_FILE_PYPO,
|
||||||
|
@ -443,7 +443,7 @@ class AirtimeIni{
|
||||||
* This function creates the /etc/airtime configuration folder
|
* This function creates the /etc/airtime configuration folder
|
||||||
* and copies the default config files to it.
|
* and copies the default config files to it.
|
||||||
*/
|
*/
|
||||||
function CreateIniFiles()
|
public static function CreateIniFiles()
|
||||||
{
|
{
|
||||||
if (!file_exists("/etc/airtime/")){
|
if (!file_exists("/etc/airtime/")){
|
||||||
if (!mkdir("/etc/airtime/", 0755, true)){
|
if (!mkdir("/etc/airtime/", 0755, true)){
|
||||||
|
@ -474,115 +474,122 @@ class AirtimeIni{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function InstallAirtimePhpServerCode($phpDir)
|
class Airtime190Upgrade{
|
||||||
{
|
|
||||||
|
|
||||||
$AIRTIME_SRC = realpath(__DIR__.'/../../../airtime_mvc');
|
public static function InstallAirtimePhpServerCode($phpDir)
|
||||||
|
{
|
||||||
|
|
||||||
echo "* Installing PHP code to ".$phpDir.PHP_EOL;
|
$AIRTIME_SRC = realpath(__DIR__.'/../../../airtime_mvc');
|
||||||
exec("mkdir -p ".$phpDir);
|
|
||||||
exec("cp -R ".$AIRTIME_SRC."/* ".$phpDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
function CopyUtils()
|
echo "* Installing PHP code to ".$phpDir.PHP_EOL;
|
||||||
{
|
exec("mkdir -p ".$phpDir);
|
||||||
$utilsSrc = __DIR__."/../../../utils";
|
exec("cp -R ".$AIRTIME_SRC."/* ".$phpDir);
|
||||||
|
|
||||||
echo "* Installing binaries to ".CONF_DIR_BINARIES.PHP_EOL;
|
|
||||||
exec("mkdir -p ".CONF_DIR_BINARIES);
|
|
||||||
exec("cp -R ".$utilsSrc." ".CONF_DIR_BINARIES);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Removes pypo, media-monitor, show-recorder and utils from system. These will
|
|
||||||
* be reinstalled by the main airtime-upgrade script.
|
|
||||||
*/
|
|
||||||
function UninstallBinaries()
|
|
||||||
{
|
|
||||||
echo "* Removing Airtime binaries from ".CONF_DIR_BINARIES.PHP_EOL;
|
|
||||||
exec('rm -rf "'.CONF_DIR_BINARIES.'"');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function removeOldAirtimeImport(){
|
|
||||||
exec('rm -f "/usr/bin/airtime-import"');
|
|
||||||
exec('rm -f "/usr/lib/airtime/utils/airtime-import.php"');
|
|
||||||
exec('rm -rf "/usr/lib/airtime/utils/airtime-import"');
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateAirtimeImportSymLink(){
|
|
||||||
$dir = "/usr/lib/airtime/utils/airtime-import/airtime-import";
|
|
||||||
exec("ln -s $dir /usr/bin/airtime-import");
|
|
||||||
}
|
|
||||||
|
|
||||||
function execSqlQuery($sql){
|
|
||||||
global $CC_DBC;
|
|
||||||
|
|
||||||
$result = $CC_DBC->query($sql);
|
|
||||||
if (PEAR::isError($result)) {
|
|
||||||
echo "* Failed sql query: $sql".PHP_EOL;
|
|
||||||
echo "* Message {$result->getMessage()}".PHP_EOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function connectToDatabase(){
|
|
||||||
global $CC_DBC, $CC_CONFIG;
|
|
||||||
|
|
||||||
$values = parse_ini_file('/etc/airtime/airtime.conf', true);
|
|
||||||
|
|
||||||
// Database config
|
|
||||||
$CC_CONFIG['dsn']['username'] = $values['database']['dbuser'];
|
|
||||||
$CC_CONFIG['dsn']['password'] = $values['database']['dbpass'];
|
|
||||||
$CC_CONFIG['dsn']['hostspec'] = $values['database']['host'];
|
|
||||||
$CC_CONFIG['dsn']['phptype'] = 'pgsql';
|
|
||||||
$CC_CONFIG['dsn']['database'] = $values['database']['dbname'];
|
|
||||||
|
|
||||||
$CC_DBC = DB::connect($CC_CONFIG['dsn'], FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Old database had a "fullpath" column that stored the absolute path of each track. We have to
|
|
||||||
* change it so that the "fullpath" column has path relative to the "directory" column.
|
|
||||||
*/
|
|
||||||
function installMediaMonitor($values){
|
|
||||||
|
|
||||||
/* Handle Database Changes. */
|
|
||||||
$stor_dir = realpath($values['general']['base_files_dir']."/stor")."/";
|
|
||||||
echo "* Inserting stor directory location $stor_dir into music_dirs table".PHP_EOL;
|
|
||||||
$sql = "UPDATE cc_music_dirs SET directory='$stor_dir' WHERE type='stor'";
|
|
||||||
echo $sql.PHP_EOL;
|
|
||||||
execSqlQuery($sql);
|
|
||||||
|
|
||||||
$sql = "SELECT id FROM cc_music_dirs WHERE type='stor'";
|
|
||||||
echo $sql.PHP_EOL;
|
|
||||||
$rows = execSqlQuery($sql);
|
|
||||||
|
|
||||||
echo "Creating media-monitor log file";
|
|
||||||
mkdir("/var/log/airtime/media-monitor/", 755, true);
|
|
||||||
touch("/var/log/airtime/media-monitor/media-monitor.log");
|
|
||||||
|
|
||||||
/* create media monitor config: */
|
|
||||||
if (!copy(__DIR__."/../../../python_apps/media-monitor/media-monitor.cfg", AirtimeIni::CONF_FILE_MEDIAMONITOR)){
|
|
||||||
echo "Could not copy media-monitor.cfg to /etc/airtime/. Exiting.";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
echo "Reorganizing files in stor directory";
|
public static function CopyUtils()
|
||||||
$mediaMonitorUpgradePath = realpath(__DIR__."/../../../python_apps/media-monitor/media-monitor-upgrade.py");
|
{
|
||||||
exec("su -c \"python $mediaMonitorUpgradePath\"", $output);
|
$utilsSrc = __DIR__."/../../../utils";
|
||||||
print_r($output);
|
|
||||||
|
|
||||||
$oldAndNewFileNames = json_decode($output[0]);
|
echo "* Installing binaries to ".CONF_DIR_BINARIES.PHP_EOL;
|
||||||
|
exec("mkdir -p ".CONF_DIR_BINARIES);
|
||||||
|
exec("cp -R ".$utilsSrc." ".CONF_DIR_BINARIES);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($oldAndNewFileNames as $pair){
|
/* Removes pypo, media-monitor, show-recorder and utils from system. These will
|
||||||
$relPathNew = pg_escape_string(substr($pair[1], strlen($stor_dir)));
|
* be reinstalled by the main airtime-upgrade script.
|
||||||
$absPathOld = pg_escape_string($pair[0]);
|
*/
|
||||||
$sql = "UPDATE cc_files SET filepath = '$relPathNew', directory=1 WHERE filepath = '$absPathOld'";
|
public static function UninstallBinaries()
|
||||||
|
{
|
||||||
|
echo "* Removing Airtime binaries from ".CONF_DIR_BINARIES.PHP_EOL;
|
||||||
|
exec('rm -rf "'.CONF_DIR_BINARIES.'"');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static function removeOldAirtimeImport(){
|
||||||
|
exec('rm -f "/usr/bin/airtime-import"');
|
||||||
|
exec('rm -f "/usr/lib/airtime/utils/airtime-import.php"');
|
||||||
|
exec('rm -rf "/usr/lib/airtime/utils/airtime-import"');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function updateAirtimeImportSymLink(){
|
||||||
|
$dir = "/usr/lib/airtime/utils/airtime-import/airtime-import";
|
||||||
|
exec("ln -s $dir /usr/bin/airtime-import");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function execSqlQuery($sql){
|
||||||
|
global $CC_DBC;
|
||||||
|
|
||||||
|
$result = $CC_DBC->query($sql);
|
||||||
|
if (PEAR::isError($result)) {
|
||||||
|
echo "* Failed sql query: $sql".PHP_EOL;
|
||||||
|
echo "* Message {$result->getMessage()}".PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function connectToDatabase(){
|
||||||
|
global $CC_DBC, $CC_CONFIG;
|
||||||
|
|
||||||
|
$values = parse_ini_file('/etc/airtime/airtime.conf', true);
|
||||||
|
|
||||||
|
// Database config
|
||||||
|
$CC_CONFIG['dsn']['username'] = $values['database']['dbuser'];
|
||||||
|
$CC_CONFIG['dsn']['password'] = $values['database']['dbpass'];
|
||||||
|
$CC_CONFIG['dsn']['hostspec'] = $values['database']['host'];
|
||||||
|
$CC_CONFIG['dsn']['phptype'] = 'pgsql';
|
||||||
|
$CC_CONFIG['dsn']['database'] = $values['database']['dbname'];
|
||||||
|
|
||||||
|
$CC_DBC = DB::connect($CC_CONFIG['dsn'], FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Old database had a "fullpath" column that stored the absolute path of each track. We have to
|
||||||
|
* change it so that the "fullpath" column has path relative to the "directory" column.
|
||||||
|
*/
|
||||||
|
public static function installMediaMonitor($values){
|
||||||
|
|
||||||
|
/* Handle Database Changes. */
|
||||||
|
$stor_dir = realpath($values['general']['base_files_dir']."/stor")."/";
|
||||||
|
echo "* Inserting stor directory location $stor_dir into music_dirs table".PHP_EOL;
|
||||||
|
$sql = "UPDATE cc_music_dirs SET directory='$stor_dir' WHERE type='stor'";
|
||||||
echo $sql.PHP_EOL;
|
echo $sql.PHP_EOL;
|
||||||
execSqlQuery($sql);
|
Airtime190Upgrade::execSqlQuery($sql);
|
||||||
|
|
||||||
|
$sql = "SELECT id FROM cc_music_dirs WHERE type='stor'";
|
||||||
|
echo $sql.PHP_EOL;
|
||||||
|
$rows = Airtime190Upgrade::execSqlQuery($sql);
|
||||||
|
|
||||||
|
echo "Creating media-monitor log file";
|
||||||
|
mkdir("/var/log/airtime/media-monitor/", 755, true);
|
||||||
|
touch("/var/log/airtime/media-monitor/media-monitor.log");
|
||||||
|
|
||||||
|
/* create media monitor config: */
|
||||||
|
if (!copy(__DIR__."/../../../python_apps/media-monitor/media-monitor.cfg", AirtimeIni::CONF_FILE_MEDIAMONITOR)){
|
||||||
|
echo "Could not copy media-monitor.cfg to /etc/airtime/. Exiting.";
|
||||||
|
}
|
||||||
|
|
||||||
|
AirtimeIni::UpdateIniValue(AirtimeIni::CONF_FILE_MEDIAMONITOR, "api_key", $values["general"]["api_key"]);
|
||||||
|
|
||||||
|
echo "Reorganizing files in stor directory";
|
||||||
|
$mediaMonitorUpgradePath = realpath(__DIR__."/../../../python_apps/media-monitor/media-monitor-upgrade.py");
|
||||||
|
exec("su -c \"python $mediaMonitorUpgradePath\"", $output);
|
||||||
|
print_r($output);
|
||||||
|
|
||||||
|
$oldAndNewFileNames = json_decode($output[0]);
|
||||||
|
|
||||||
|
foreach ($oldAndNewFileNames as $pair){
|
||||||
|
$relPathNew = pg_escape_string(substr($pair[1], strlen($stor_dir)));
|
||||||
|
$absPathOld = pg_escape_string($pair[0]);
|
||||||
|
$sql = "UPDATE cc_files SET filepath = '$relPathNew', directory=1 WHERE filepath = '$absPathOld'";
|
||||||
|
echo $sql.PHP_EOL;
|
||||||
|
Airtime190Upgrade::execSqlQuery($sql);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AirtimeInstall::CreateZendPhpLogFile();
|
AirtimeInstall::CreateZendPhpLogFile();
|
||||||
|
|
||||||
/* In version 1.9.0 we have have switched from daemontools to more traditional
|
/* In version 1.9.0 we have have switched from daemontools to more traditional
|
||||||
|
@ -622,19 +629,19 @@ foreach ($pathnames as $pn){
|
||||||
/* update Airtime Server PHP files */
|
/* update Airtime Server PHP files */
|
||||||
$values = parse_ini_file(AirtimeIni::CONF_FILE_AIRTIME, true);
|
$values = parse_ini_file(AirtimeIni::CONF_FILE_AIRTIME, true);
|
||||||
$phpDir = $values['general']['airtime_dir'];
|
$phpDir = $values['general']['airtime_dir'];
|
||||||
InstallAirtimePhpServerCode($phpDir);
|
Airtime190Upgrade::InstallAirtimePhpServerCode($phpDir);
|
||||||
|
|
||||||
/* update utils (/usr/lib/airtime) folder */
|
/* update utils (/usr/lib/airtime) folder */
|
||||||
UninstallBinaries();
|
Airtime190Upgrade::UninstallBinaries();
|
||||||
CopyUtils();
|
Airtime190Upgrade::CopyUtils();
|
||||||
|
|
||||||
/* James made a new airtime-import script, lets remove the old airtime-import php script,
|
/* James made a new airtime-import script, lets remove the old airtime-import php script,
|
||||||
*install the new airtime-import.py script and update the /usr/bin/symlink.
|
*install the new airtime-import.py script and update the /usr/bin/symlink.
|
||||||
*/
|
*/
|
||||||
removeOldAirtimeImport();
|
Airtime190Upgrade::removeOldAirtimeImport();
|
||||||
updateAirtimeImportSymLink();
|
Airtime190Upgrade::updateAirtimeImportSymLink();
|
||||||
|
|
||||||
connectToDatabase();
|
Airtime190Upgrade::connectToDatabase();
|
||||||
|
|
||||||
if(AirtimeInstall::DbTableExists('doctrine_migration_versions') === false) {
|
if(AirtimeInstall::DbTableExists('doctrine_migration_versions') === false) {
|
||||||
$migrations = array('20110312121200', '20110331111708', '20110402164819', '20110406182005');
|
$migrations = array('20110312121200', '20110331111708', '20110402164819', '20110406182005');
|
||||||
|
@ -652,7 +659,7 @@ AirtimeInstall::InsertCountryDataIntoDatabase();
|
||||||
/* create cron file for phone home stat */
|
/* create cron file for phone home stat */
|
||||||
AirtimeInstall::CreateCronFile();
|
AirtimeInstall::CreateCronFile();
|
||||||
|
|
||||||
installMediaMonitor($values);
|
Airtime190Upgrade::installMediaMonitor($values);
|
||||||
|
|
||||||
AirtimeIni::upgradeConfigFiles();
|
AirtimeIni::upgradeConfigFiles();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from configobj import ConfigObj
|
||||||
|
|
||||||
|
class AirtimeMediaConfig:
|
||||||
|
|
||||||
|
MODE_CREATE = "create"
|
||||||
|
MODE_MODIFY = "modify"
|
||||||
|
MODE_MOVED = "moved"
|
||||||
|
MODE_DELETE = "delete"
|
||||||
|
|
||||||
|
def __init__(self, logger):
|
||||||
|
|
||||||
|
# loading config file
|
||||||
|
try:
|
||||||
|
config = ConfigObj('/etc/airtime/media-monitor.cfg')
|
||||||
|
self.cfg = config
|
||||||
|
except Exception, e:
|
||||||
|
logger.info('Error loading config: ', e)
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
self.storage_directory = None
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,243 @@
|
||||||
|
import os
|
||||||
|
import grp
|
||||||
|
import pwd
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from subprocess import Popen, PIPE
|
||||||
|
from airtimemetadata import AirtimeMetadata
|
||||||
|
|
||||||
|
class MediaMonitorCommon:
|
||||||
|
|
||||||
|
timestamp_file = "/var/tmp/airtime/last_index"
|
||||||
|
|
||||||
|
def __init__(self, airtime_config):
|
||||||
|
self.supported_file_formats = ['mp3', 'ogg']
|
||||||
|
self.logger = logging.getLogger()
|
||||||
|
self.config = airtime_config
|
||||||
|
self.md_manager = AirtimeMetadata()
|
||||||
|
|
||||||
|
def is_parent_directory(self, filepath, directory):
|
||||||
|
filepath = os.path.normpath(filepath)
|
||||||
|
directory = os.path.normpath(directory)
|
||||||
|
return (directory == filepath[0:len(directory)])
|
||||||
|
|
||||||
|
def is_temp_file(self, filename):
|
||||||
|
info = filename.split(".")
|
||||||
|
|
||||||
|
if(info[-2] in self.supported_file_formats):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_audio_file(self, filename):
|
||||||
|
info = filename.split(".")
|
||||||
|
|
||||||
|
if(info[-1] in self.supported_file_formats):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
#check if file is readable by "nobody"
|
||||||
|
def has_correct_permissions(self, filepath):
|
||||||
|
#drop root permissions and become "nobody"
|
||||||
|
os.seteuid(65534)
|
||||||
|
|
||||||
|
try:
|
||||||
|
open(filepath)
|
||||||
|
readable = True
|
||||||
|
except IOError:
|
||||||
|
self.logger.warn("File does not have correct permissions: '%s'", filepath)
|
||||||
|
readable = False
|
||||||
|
except Exception, e:
|
||||||
|
self.logger.error("Unexpected exception thrown: %s", e)
|
||||||
|
readable = False
|
||||||
|
finally:
|
||||||
|
#reset effective user to root
|
||||||
|
os.seteuid(0)
|
||||||
|
|
||||||
|
return readable
|
||||||
|
|
||||||
|
def set_needed_file_permissions(self, item, is_dir):
|
||||||
|
try:
|
||||||
|
omask = os.umask(0)
|
||||||
|
|
||||||
|
uid = pwd.getpwnam('www-data')[2]
|
||||||
|
gid = grp.getgrnam('www-data')[2]
|
||||||
|
|
||||||
|
os.chown(item, uid, gid)
|
||||||
|
|
||||||
|
if is_dir is True:
|
||||||
|
os.chmod(item, 02777)
|
||||||
|
else:
|
||||||
|
os.chmod(item, 0666)
|
||||||
|
|
||||||
|
except Exception, e:
|
||||||
|
self.logger.error("Failed to change file's owner/group/permissions. %s", e)
|
||||||
|
finally:
|
||||||
|
os.umask(omask)
|
||||||
|
|
||||||
|
|
||||||
|
#checks if path is a directory, and if it doesnt exist, then creates it.
|
||||||
|
#Otherwise prints error to log file.
|
||||||
|
def ensure_is_dir(self, directory):
|
||||||
|
try:
|
||||||
|
omask = os.umask(0)
|
||||||
|
if not os.path.exists(directory):
|
||||||
|
os.makedirs(directory, 02777)
|
||||||
|
elif not os.path.isdir(directory):
|
||||||
|
#path exists but it is a file not a directory!
|
||||||
|
self.logger.error("path %s exists, but it is not a directory!!!")
|
||||||
|
finally:
|
||||||
|
os.umask(omask)
|
||||||
|
|
||||||
|
#moves file from source to dest but also recursively removes the
|
||||||
|
#the source file's parent directories if they are now empty.
|
||||||
|
def move_file(self, source, dest):
|
||||||
|
|
||||||
|
try:
|
||||||
|
omask = os.umask(0)
|
||||||
|
os.rename(source, dest)
|
||||||
|
except Exception, e:
|
||||||
|
self.logger.error("failed to move file. %s", e)
|
||||||
|
finally:
|
||||||
|
os.umask(omask)
|
||||||
|
|
||||||
|
dir = os.path.dirname(source)
|
||||||
|
self.cleanup_empty_dirs(dir)
|
||||||
|
|
||||||
|
#keep moving up the file hierarchy and deleting parent
|
||||||
|
#directories until we hit a non-empty directory, or we
|
||||||
|
#hit the organize dir.
|
||||||
|
def cleanup_empty_dirs(self, dir):
|
||||||
|
if os.path.normpath(dir) != self.config.organize_directory:
|
||||||
|
if len(os.listdir(dir)) == 0:
|
||||||
|
os.rmdir(dir)
|
||||||
|
|
||||||
|
pdir = os.path.dirname(dir)
|
||||||
|
self.cleanup_empty_dirs(pdir)
|
||||||
|
|
||||||
|
|
||||||
|
#checks if path exists already in stor. If the path exists and the md5s are the
|
||||||
|
#same just overwrite.
|
||||||
|
def create_unique_filename(self, filepath, old_filepath):
|
||||||
|
|
||||||
|
try:
|
||||||
|
if(os.path.exists(filepath)):
|
||||||
|
self.logger.info("Path %s exists", filepath)
|
||||||
|
|
||||||
|
self.logger.info("Checking if md5s are the same.")
|
||||||
|
md5_fp = self.md_manager.get_md5(filepath)
|
||||||
|
md5_ofp = self.md_manager.get_md5(old_filepath)
|
||||||
|
|
||||||
|
if(md5_fp == md5_ofp):
|
||||||
|
self.logger.info("Md5s are the same, moving to same filepath.")
|
||||||
|
return filepath
|
||||||
|
|
||||||
|
self.logger.info("Md5s aren't the same, appending to filepath.")
|
||||||
|
file_dir = os.path.dirname(filepath)
|
||||||
|
filename = os.path.basename(filepath).split(".")[0]
|
||||||
|
#will be in the format .ext
|
||||||
|
file_ext = os.path.splitext(filepath)[1]
|
||||||
|
i = 1;
|
||||||
|
while(True):
|
||||||
|
new_filepath = '%s/%s(%s)%s' % (file_dir, filename, i, file_ext)
|
||||||
|
self.logger.error("Trying %s", new_filepath)
|
||||||
|
|
||||||
|
if(os.path.exists(new_filepath)):
|
||||||
|
i = i+1;
|
||||||
|
else:
|
||||||
|
filepath = new_filepath
|
||||||
|
break
|
||||||
|
|
||||||
|
except Exception, e:
|
||||||
|
self.logger.error("Exception %s", e)
|
||||||
|
|
||||||
|
return filepath
|
||||||
|
|
||||||
|
#create path in /srv/airtime/stor/imported/[song-metadata]
|
||||||
|
def create_file_path(self, original_path, orig_md):
|
||||||
|
|
||||||
|
storage_directory = self.config.storage_directory
|
||||||
|
|
||||||
|
is_recorded_show = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
#will be in the format .ext
|
||||||
|
file_ext = os.path.splitext(original_path)[1]
|
||||||
|
file_ext = file_ext.encode('utf-8')
|
||||||
|
|
||||||
|
path_md = ['MDATA_KEY_TITLE', 'MDATA_KEY_CREATOR', 'MDATA_KEY_SOURCE', 'MDATA_KEY_TRACKNUMBER', 'MDATA_KEY_BITRATE']
|
||||||
|
|
||||||
|
md = {}
|
||||||
|
for m in path_md:
|
||||||
|
if m not in orig_md:
|
||||||
|
md[m] = u'unknown'.encode('utf-8')
|
||||||
|
else:
|
||||||
|
#get rid of any "/" which will interfere with the filepath.
|
||||||
|
if isinstance(orig_md[m], basestring):
|
||||||
|
md[m] = orig_md[m].replace("/", "-")
|
||||||
|
else:
|
||||||
|
md[m] = orig_md[m]
|
||||||
|
|
||||||
|
if 'MDATA_KEY_TRACKNUMBER' in orig_md:
|
||||||
|
#make sure all track numbers are at least 2 digits long in the filepath.
|
||||||
|
md['MDATA_KEY_TRACKNUMBER'] = "%02d" % (int(md['MDATA_KEY_TRACKNUMBER']))
|
||||||
|
|
||||||
|
#format bitrate as 128kbps
|
||||||
|
md['MDATA_KEY_BITRATE'] = str(md['MDATA_KEY_BITRATE']/1000)+"kbps"
|
||||||
|
|
||||||
|
filepath = None
|
||||||
|
#file is recorded by Airtime
|
||||||
|
#/srv/airtime/stor/recorded/year/month/year-month-day-time-showname-bitrate.ext
|
||||||
|
if(md['MDATA_KEY_CREATOR'] == "AIRTIMERECORDERSOURCEFABRIC".encode('utf-8')):
|
||||||
|
#yyyy-mm-dd-hh-MM-ss
|
||||||
|
y = orig_md['MDATA_KEY_YEAR'].split("-")
|
||||||
|
filepath = '%s/%s/%s/%s/%s-%s-%s%s' % (storage_directory, "recorded".encode('utf-8'), y[0], y[1], orig_md['MDATA_KEY_YEAR'], md['MDATA_KEY_TITLE'], md['MDATA_KEY_BITRATE'], file_ext)
|
||||||
|
elif(md['MDATA_KEY_TRACKNUMBER'] == u'unknown'.encode('utf-8')):
|
||||||
|
filepath = '%s/%s/%s/%s/%s-%s%s' % (storage_directory, "imported".encode('utf-8'), md['MDATA_KEY_CREATOR'], md['MDATA_KEY_SOURCE'], md['MDATA_KEY_TITLE'], md['MDATA_KEY_BITRATE'], file_ext)
|
||||||
|
else:
|
||||||
|
filepath = '%s/%s/%s/%s/%s-%s-%s%s' % (storage_directory, "imported".encode('utf-8'), md['MDATA_KEY_CREATOR'], md['MDATA_KEY_SOURCE'], md['MDATA_KEY_TRACKNUMBER'], md['MDATA_KEY_TITLE'], md['MDATA_KEY_BITRATE'], file_ext)
|
||||||
|
|
||||||
|
filepath = self.create_unique_filename(filepath, original_path)
|
||||||
|
self.logger.info('Unique filepath: %s', filepath)
|
||||||
|
self.ensure_is_dir(os.path.dirname(filepath))
|
||||||
|
|
||||||
|
except Exception, e:
|
||||||
|
self.logger.error('Exception: %s', e)
|
||||||
|
|
||||||
|
return filepath
|
||||||
|
|
||||||
|
def execCommandAndReturnStdOut(self, command):
|
||||||
|
p = Popen(command, shell=True, stdout=PIPE)
|
||||||
|
stdout = p.communicate()[0]
|
||||||
|
if p.returncode != 0:
|
||||||
|
self.logger.warn("command \n%s\n return with a non-zero return value", command)
|
||||||
|
return stdout
|
||||||
|
|
||||||
|
def scan_dir_for_new_files(self, dir):
|
||||||
|
command = 'find "%s" -type f -iname "*.ogg" -o -iname "*.mp3" -readable' % dir.replace('"', '\\"')
|
||||||
|
self.logger.debug(command)
|
||||||
|
stdout = self.execCommandAndReturnStdOut(command)
|
||||||
|
stdout = unicode(stdout, "utf_8")
|
||||||
|
|
||||||
|
return stdout.splitlines()
|
||||||
|
|
||||||
|
def touch_index_file(self):
|
||||||
|
open(self.timestamp_file, "w")
|
||||||
|
|
||||||
|
def organize_new_file(self, pathname):
|
||||||
|
self.logger.info("Organizing new file: %s", pathname)
|
||||||
|
file_md = self.md_manager.get_md_from_file(pathname)
|
||||||
|
|
||||||
|
if file_md is not None:
|
||||||
|
#is_recorded_show = 'MDATA_KEY_CREATOR' in file_md and \
|
||||||
|
# file_md['MDATA_KEY_CREATOR'] == "AIRTIMERECORDERSOURCEFABRIC".encode('utf-8')
|
||||||
|
filepath = self.create_file_path(pathname, file_md)
|
||||||
|
|
||||||
|
self.logger.debug("Moving from %s to %s", pathname, filepath)
|
||||||
|
self.move_file(pathname, filepath)
|
||||||
|
else:
|
||||||
|
filepath = None
|
||||||
|
self.logger.warn("File %s, has invalid metadata", pathname)
|
||||||
|
|
||||||
|
return filepath
|
|
@ -0,0 +1,83 @@
|
||||||
|
<?php
|
||||||
|
/* THIS FILE IS NOT MEANT FOR CUSTOMIZING.
|
||||||
|
* PLEASE EDIT THE FOLLOWING TO CHANGE YOUR CONFIG:
|
||||||
|
* /etc/airtime/airtime.conf
|
||||||
|
* /etc/airtime/pypo.cfg
|
||||||
|
* /etc/airtime/recorder.cfg
|
||||||
|
*/
|
||||||
|
|
||||||
|
global $CC_CONFIG;
|
||||||
|
|
||||||
|
$CC_CONFIG = array(
|
||||||
|
// prefix for table names in the database
|
||||||
|
'tblNamePrefix' => 'cc_',
|
||||||
|
|
||||||
|
/* ================================================ storage configuration */
|
||||||
|
|
||||||
|
'soundcloud-client-id' => '2CLCxcSXYzx7QhhPVHN4A',
|
||||||
|
'soundcloud-client-secret' => 'pZ7beWmF06epXLHVUP1ufOg2oEnIt9XhE8l8xt0bBs',
|
||||||
|
|
||||||
|
"rootDir" => __DIR__."/../..",
|
||||||
|
'pearPath' => dirname(__FILE__).'/../../../airtime_mvc/library/pear',
|
||||||
|
'zendPath' => dirname(__FILE__).'/../../../airtime_mvc/library/Zend',
|
||||||
|
'phingPath' => dirname(__FILE__).'/../../../airtime_mvc/library/phing'
|
||||||
|
);
|
||||||
|
|
||||||
|
$CC_CONFIG = Config::loadConfig($CC_CONFIG);
|
||||||
|
|
||||||
|
// Add database table names
|
||||||
|
$CC_CONFIG['playListTable'] = $CC_CONFIG['tblNamePrefix'].'playlist';
|
||||||
|
$CC_CONFIG['playListContentsTable'] = $CC_CONFIG['tblNamePrefix'].'playlistcontents';
|
||||||
|
$CC_CONFIG['filesTable'] = $CC_CONFIG['tblNamePrefix'].'files';
|
||||||
|
$CC_CONFIG['accessTable'] = $CC_CONFIG['tblNamePrefix'].'access';
|
||||||
|
$CC_CONFIG['permTable'] = $CC_CONFIG['tblNamePrefix'].'perms';
|
||||||
|
$CC_CONFIG['sessTable'] = $CC_CONFIG['tblNamePrefix'].'sess';
|
||||||
|
$CC_CONFIG['subjTable'] = $CC_CONFIG['tblNamePrefix'].'subjs';
|
||||||
|
$CC_CONFIG['smembTable'] = $CC_CONFIG['tblNamePrefix'].'smemb';
|
||||||
|
$CC_CONFIG['prefTable'] = $CC_CONFIG['tblNamePrefix'].'pref';
|
||||||
|
$CC_CONFIG['scheduleTable'] = $CC_CONFIG['tblNamePrefix'].'schedule';
|
||||||
|
$CC_CONFIG['playListTimeView'] = $CC_CONFIG['tblNamePrefix'].'playlisttimes';
|
||||||
|
$CC_CONFIG['showSchedule'] = $CC_CONFIG['tblNamePrefix'].'show_schedule';
|
||||||
|
$CC_CONFIG['showDays'] = $CC_CONFIG['tblNamePrefix'].'show_days';
|
||||||
|
$CC_CONFIG['showTable'] = $CC_CONFIG['tblNamePrefix'].'show';
|
||||||
|
$CC_CONFIG['showInstances'] = $CC_CONFIG['tblNamePrefix'].'show_instances';
|
||||||
|
|
||||||
|
$CC_CONFIG['playListSequence'] = $CC_CONFIG['playListTable'].'_id';
|
||||||
|
$CC_CONFIG['filesSequence'] = $CC_CONFIG['filesTable'].'_id';
|
||||||
|
$CC_CONFIG['prefSequence'] = $CC_CONFIG['prefTable'].'_id';
|
||||||
|
$CC_CONFIG['permSequence'] = $CC_CONFIG['permTable'].'_id';
|
||||||
|
$CC_CONFIG['subjSequence'] = $CC_CONFIG['subjTable'].'_id';
|
||||||
|
$CC_CONFIG['smembSequence'] = $CC_CONFIG['smembTable'].'_id';
|
||||||
|
|
||||||
|
// Add libs to the PHP path
|
||||||
|
$old_include_path = get_include_path();
|
||||||
|
set_include_path('.'.PATH_SEPARATOR.$CC_CONFIG['pearPath']
|
||||||
|
.PATH_SEPARATOR.$CC_CONFIG['zendPath']
|
||||||
|
.PATH_SEPARATOR.$old_include_path);
|
||||||
|
|
||||||
|
class Config {
|
||||||
|
public static function loadConfig($CC_CONFIG) {
|
||||||
|
$values = parse_ini_file('/etc/airtime/airtime.conf', true);
|
||||||
|
|
||||||
|
// Name of the web server user
|
||||||
|
$CC_CONFIG['webServerUser'] = $values['general']['web_server_user'];
|
||||||
|
$CC_CONFIG['rabbitmq'] = $values['rabbitmq'];
|
||||||
|
|
||||||
|
$CC_CONFIG['baseUrl'] = $values['general']['base_url'];
|
||||||
|
$CC_CONFIG['basePort'] = $values['general']['base_port'];
|
||||||
|
|
||||||
|
// Database config
|
||||||
|
$CC_CONFIG['dsn']['username'] = $values['database']['dbuser'];
|
||||||
|
$CC_CONFIG['dsn']['password'] = $values['database']['dbpass'];
|
||||||
|
$CC_CONFIG['dsn']['hostspec'] = $values['database']['host'];
|
||||||
|
$CC_CONFIG['dsn']['phptype'] = 'pgsql';
|
||||||
|
$CC_CONFIG['dsn']['database'] = $values['database']['dbname'];
|
||||||
|
|
||||||
|
$CC_CONFIG['apiKey'] = array($values['general']['api_key']);
|
||||||
|
|
||||||
|
$CC_CONFIG['soundcloud-connection-retries'] = $values['soundcloud']['connection_retries'];
|
||||||
|
$CC_CONFIG['soundcloud-connection-wait'] = $values['soundcloud']['time_between_retries'];
|
||||||
|
|
||||||
|
return $CC_CONFIG;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
[loggers]
|
||||||
|
keys=root
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys=fileOutHandler
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys=simpleFormatter
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level=DEBUG
|
||||||
|
handlers=fileOutHandler
|
||||||
|
|
||||||
|
[handler_fileOutHandler]
|
||||||
|
class=logging.handlers.RotatingFileHandler
|
||||||
|
level=DEBUG
|
||||||
|
formatter=simpleFormatter
|
||||||
|
args=("/var/log/airtime/media-monitor/media-monitor.log", 'a', 1000000, 5,)
|
||||||
|
|
||||||
|
[formatter_simpleFormatter]
|
||||||
|
format=%(asctime)s %(levelname)s - [%(filename)s : %(funcName)s() : line %(lineno)d] - %(message)s
|
||||||
|
datefmt=
|
|
@ -0,0 +1 @@
|
||||||
|
bin_dir = "/usr/lib/airtime/api_clients"
|
|
@ -0,0 +1,25 @@
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
from configobj import ConfigObj
|
||||||
|
|
||||||
|
def get_current_script_dir():
|
||||||
|
return os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
|
def copy_dir(src_dir, dest_dir):
|
||||||
|
if (os.path.exists(dest_dir)) and (dest_dir != "/"):
|
||||||
|
shutil.rmtree(dest_dir)
|
||||||
|
if not (os.path.exists(dest_dir)):
|
||||||
|
print "Copying directory "+os.path.realpath(src_dir)+" to "+os.path.realpath(dest_dir)
|
||||||
|
shutil.copytree(src_dir, dest_dir)
|
||||||
|
|
||||||
|
current_script_dir = get_current_script_dir()
|
||||||
|
|
||||||
|
"""load config file"""
|
||||||
|
try:
|
||||||
|
config = ConfigObj("%s/../api_client.cfg" % current_script_dir)
|
||||||
|
except Exception, e:
|
||||||
|
print 'Error loading config file: ', e
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
copy_dir("%s/../../api_clients"%current_script_dir, config["bin_dir"])
|
|
@ -0,0 +1,21 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from configobj import ConfigObj
|
||||||
|
|
||||||
|
def remove_path(path):
|
||||||
|
os.system('rm -rf "%s"' % path)
|
||||||
|
|
||||||
|
def get_current_script_dir():
|
||||||
|
return os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
|
current_script_dir = get_current_script_dir()
|
||||||
|
|
||||||
|
"""load config file"""
|
||||||
|
try:
|
||||||
|
config = ConfigObj("%s/../api_client.cfg" % current_script_dir)
|
||||||
|
except Exception, e:
|
||||||
|
print 'Error loading config file: ', e
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print "Removing API Client files"
|
||||||
|
remove_path(config["bin_dir"])
|
|
@ -30,6 +30,8 @@ except Exception, e:
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
logger.info("\n\n*** Media Monitor bootup ***\n\n")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
config = AirtimeMediaConfig(logger)
|
config = AirtimeMediaConfig(logger)
|
||||||
api_client = api_client.api_client_factory(config.cfg)
|
api_client = api_client.api_client_factory(config.cfg)
|
||||||
|
|
|
@ -8,7 +8,7 @@ virtualenv_bin="/usr/lib/airtime/airtime_virtualenv/bin/"
|
||||||
media_monitor_path="/usr/lib/airtime/media-monitor/"
|
media_monitor_path="/usr/lib/airtime/media-monitor/"
|
||||||
media_monitor_script="MediaMonitor.py"
|
media_monitor_script="MediaMonitor.py"
|
||||||
|
|
||||||
api_client_path="/usr/lib/airtime/pypo/"
|
api_client_path="/usr/lib/airtime/"
|
||||||
|
|
||||||
cd ${media_monitor_path}
|
cd ${media_monitor_path}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ from subprocess import Popen, PIPE
|
||||||
class AirtimeMediaMonitorBootstrap():
|
class AirtimeMediaMonitorBootstrap():
|
||||||
|
|
||||||
"""AirtimeMediaMonitorBootstrap constructor
|
"""AirtimeMediaMonitorBootstrap constructor
|
||||||
|
|
||||||
Keyword Arguments:
|
Keyword Arguments:
|
||||||
logger -- reference to the media-monitor logging facility
|
logger -- reference to the media-monitor logging facility
|
||||||
pe -- reference to an instance of ProcessEvent
|
pe -- reference to an instance of ProcessEvent
|
||||||
|
@ -17,36 +17,36 @@ class AirtimeMediaMonitorBootstrap():
|
||||||
self.pe = pe
|
self.pe = pe
|
||||||
self.api_client = api_client
|
self.api_client = api_client
|
||||||
self.mmc = mmc
|
self.mmc = mmc
|
||||||
|
|
||||||
"""On bootup we want to scan all directories and look for files that
|
"""On bootup we want to scan all directories and look for files that
|
||||||
weren't there or files that changed before media-monitor process
|
weren't there or files that changed before media-monitor process
|
||||||
went offline.
|
went offline.
|
||||||
"""
|
"""
|
||||||
def scan(self):
|
def scan(self):
|
||||||
directories = self.get_list_of_watched_dirs();
|
directories = self.get_list_of_watched_dirs();
|
||||||
|
|
||||||
self.logger.info("watched directories found: %s", directories)
|
self.logger.info("watched directories found: %s", directories)
|
||||||
|
|
||||||
for id, dir in directories.iteritems():
|
for id, dir in directories.iteritems():
|
||||||
self.logger.debug("%s, %s", id, dir)
|
self.logger.debug("%s, %s", id, dir)
|
||||||
self.sync_database_to_filesystem(id, dir)
|
self.sync_database_to_filesystem(id, dir)
|
||||||
|
|
||||||
"""Gets a list of files that the Airtime database knows for a specific directory.
|
"""Gets a list of files that the Airtime database knows for a specific directory.
|
||||||
You need to provide the directory's row ID, which is obtained when calling
|
You need to provide the directory's row ID, which is obtained when calling
|
||||||
get_list_of_watched_dirs function.
|
get_list_of_watched_dirs function.
|
||||||
dir_id -- row id of the directory in the cc_watched_dirs database table
|
dir_id -- row id of the directory in the cc_watched_dirs database table
|
||||||
"""
|
"""
|
||||||
def list_db_files(self, dir_id):
|
def list_db_files(self, dir_id):
|
||||||
return self.api_client.list_all_db_files(dir_id)
|
return self.api_client.list_all_db_files(dir_id)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
returns the path and the database row id for this path for all watched directories. Also
|
returns the path and the database row id for this path for all watched directories. Also
|
||||||
returns the Stor directory, which can be identified by its row id (always has value of "1")
|
returns the Stor directory, which can be identified by its row id (always has value of "1")
|
||||||
"""
|
"""
|
||||||
def get_list_of_watched_dirs(self):
|
def get_list_of_watched_dirs(self):
|
||||||
json = self.api_client.list_all_watched_dirs()
|
json = self.api_client.list_all_watched_dirs()
|
||||||
return json["dirs"]
|
return json["dirs"]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This function takes in a path name provided by the database (and its corresponding row id)
|
This function takes in a path name provided by the database (and its corresponding row id)
|
||||||
and reads the list of files in the local file system. Its purpose is to discover which files
|
and reads the list of files in the local file system. Its purpose is to discover which files
|
||||||
|
@ -57,42 +57,42 @@ class AirtimeMediaMonitorBootstrap():
|
||||||
dir -- pathname of the directory
|
dir -- pathname of the directory
|
||||||
"""
|
"""
|
||||||
def sync_database_to_filesystem(self, dir_id, dir):
|
def sync_database_to_filesystem(self, dir_id, dir):
|
||||||
"""
|
"""
|
||||||
set to hold new and/or modified files. We use a set to make it ok if files are added
|
set to hold new and/or modified files. We use a set to make it ok if files are added
|
||||||
twice. This is because some of the tests for new files return result sets that are not
|
twice. This is because some of the tests for new files return result sets that are not
|
||||||
mutually exclusive from each other.
|
mutually exclusive from each other.
|
||||||
"""
|
"""
|
||||||
new_and_modified_files = set()
|
new_and_modified_files = set()
|
||||||
removed_files = set()
|
removed_files = set()
|
||||||
|
|
||||||
|
|
||||||
db_known_files_set = set()
|
db_known_files_set = set()
|
||||||
files = self.list_db_files(dir_id)
|
files = self.list_db_files(dir_id)
|
||||||
for file in files['files']:
|
for file in files['files']:
|
||||||
db_known_files_set.add(file)
|
db_known_files_set.add(file)
|
||||||
|
|
||||||
new_files = self.mmc.scan_dir_for_new_files(dir)
|
new_files = self.mmc.scan_dir_for_new_files(dir)
|
||||||
all_files_set = set()
|
all_files_set = set()
|
||||||
for file_path in new_files:
|
for file_path in new_files:
|
||||||
if len(file_path.strip(" \n")) > 0:
|
if len(file_path.strip(" \n")) > 0:
|
||||||
all_files_set.add(file_path[len(dir):])
|
all_files_set.add(file_path[len(dir):])
|
||||||
|
|
||||||
if os.path.exists(self.mmc.timestamp_file):
|
if os.path.exists(self.mmc.timestamp_file):
|
||||||
"""find files that have been modified since the last time media-monitor process started."""
|
"""find files that have been modified since the last time media-monitor process started."""
|
||||||
time_diff_sec = time.time() - os.path.getmtime(self.mmc.timestamp_file)
|
time_diff_sec = time.time() - os.path.getmtime(self.mmc.timestamp_file)
|
||||||
command = "find %s -type f -iname '*.ogg' -o -iname '*.mp3' -readable -mmin -%d" % (dir, time_diff_sec/60+1)
|
command = "find %s -type f -iname '*.ogg' -o -iname '*.mp3' -readable -mmin -%d" % (dir, time_diff_sec/60+1)
|
||||||
else:
|
else:
|
||||||
command = "find %s -type f -iname '*.ogg' -o -iname '*.mp3' -readable" % dir
|
command = "find %s -type f -iname '*.ogg' -o -iname '*.mp3' -readable" % dir
|
||||||
|
|
||||||
stdout = self.mmc.execCommandAndReturnStdOut(command)
|
stdout = self.mmc.execCommandAndReturnStdOut(command)
|
||||||
stdout = unicode(stdout, "utf_8")
|
stdout = unicode(stdout, "utf_8")
|
||||||
|
|
||||||
new_files = stdout.splitlines()
|
new_files = stdout.splitlines()
|
||||||
|
|
||||||
for file_path in new_files:
|
for file_path in new_files:
|
||||||
if len(file_path.strip(" \n")) > 0:
|
if len(file_path.strip(" \n")) > 0:
|
||||||
new_and_modified_files.add(file_path[len(dir)+1:])
|
new_and_modified_files.add(file_path[len(dir):])
|
||||||
|
|
||||||
"""
|
"""
|
||||||
new_and_modified_files gives us a set of files that were either copied or modified
|
new_and_modified_files gives us a set of files that were either copied or modified
|
||||||
since the last time media-monitor was running. These files were collected based on
|
since the last time media-monitor was running. These files were collected based on
|
||||||
|
@ -101,28 +101,32 @@ class AirtimeMediaMonitorBootstrap():
|
||||||
not affect last modified timestamp). Lets get a list of files that are on the file-system
|
not affect last modified timestamp). Lets get a list of files that are on the file-system
|
||||||
that the db has no record of, and vice-versa.
|
that the db has no record of, and vice-versa.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
deleted_files_set = db_known_files_set - all_files_set
|
deleted_files_set = db_known_files_set - all_files_set
|
||||||
new_files_set = all_files_set - db_known_files_set
|
new_files_set = all_files_set - db_known_files_set
|
||||||
modified_files_set = new_and_modified_files - new_files_set
|
modified_files_set = new_and_modified_files - new_files_set
|
||||||
|
|
||||||
self.logger.info("Deleted files: \n%s\n\n"%deleted_files_set)
|
self.logger.info("Deleted files: \n%s\n\n"%deleted_files_set)
|
||||||
self.logger.info("New files: \n%s\n\n"%new_files_set)
|
self.logger.info("New files: \n%s\n\n"%new_files_set)
|
||||||
self.logger.info("Modified files: \n%s\n\n"%modified_files_set)
|
self.logger.info("Modified files: \n%s\n\n"%modified_files_set)
|
||||||
|
|
||||||
#"touch" file timestamp
|
#"touch" file timestamp
|
||||||
self.mmc.touch_index_file()
|
self.mmc.touch_index_file()
|
||||||
|
|
||||||
for file_path in deleted_files_set:
|
for file_path in deleted_files_set:
|
||||||
self.pe.handle_removed_file(False, "%s/%s" % (dir, file_path))
|
self.pe.handle_removed_file(False, "%s%s" % (dir, file_path))
|
||||||
|
deleted_files_set.clear()
|
||||||
|
|
||||||
for file_path in new_files_set:
|
for file_path in new_files_set:
|
||||||
file_path = "%s/%s" % (dir, file_path)
|
file_path = "%s%s" % (dir, file_path)
|
||||||
if os.path.exists(file_path):
|
if os.path.exists(file_path):
|
||||||
self.pe.handle_created_file(False, os.path.basename(file_path), file_path)
|
self.pe.handle_created_file(False, os.path.basename(file_path), file_path)
|
||||||
|
|
||||||
|
new_files_set.clear()
|
||||||
|
|
||||||
for file_path in modified_files_set:
|
for file_path in modified_files_set:
|
||||||
file_path = "%s/%s" % (dir, file_path)
|
file_path = "%s%s" % (dir, file_path)
|
||||||
if os.path.exists(file_path):
|
if os.path.exists(file_path):
|
||||||
self.pe.handle_modified_file(False, os.path.basename(file_path), file_path)
|
self.pe.handle_modified_file(False, os.path.basename(file_path), file_path)
|
||||||
|
modified_files_set.clear()
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ class AirtimeNotifier(Notifier):
|
||||||
self.logger.info("Adding file to ignore: %s ", filepath)
|
self.logger.info("Adding file to ignore: %s ", filepath)
|
||||||
mm.add_filepath_to_ignore(filepath)
|
mm.add_filepath_to_ignore(filepath)
|
||||||
|
|
||||||
if m['delete'] == "true":
|
if m['delete']:
|
||||||
self.logger.info("Deleting file: %s ", filepath)
|
self.logger.info("Deleting file: %s ", filepath)
|
||||||
os.unlink(filepath)
|
os.unlink(filepath)
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,9 @@ class AirtimeProcessEvent(ProcessEvent):
|
||||||
#file created is a tmp file which will be modified and then moved back to the original filename.
|
#file created is a tmp file which will be modified and then moved back to the original filename.
|
||||||
#Easy Tag creates this when changing metadata of ogg files.
|
#Easy Tag creates this when changing metadata of ogg files.
|
||||||
self.temp_files[pathname] = None
|
self.temp_files[pathname] = None
|
||||||
|
#file is being overwritten/replaced in GUI.
|
||||||
|
elif "goutputstream" in pathname:
|
||||||
|
self.temp_files[pathname] = None
|
||||||
elif self.mmc.is_audio_file(pathname):
|
elif self.mmc.is_audio_file(pathname):
|
||||||
if self.mmc.is_parent_directory(pathname, self.config.organize_directory):
|
if self.mmc.is_parent_directory(pathname, self.config.organize_directory):
|
||||||
#file was created in /srv/airtime/stor/organize. Need to process and move
|
#file was created in /srv/airtime/stor/organize. Need to process and move
|
||||||
|
|
|
@ -5,7 +5,7 @@ virtualenv_bin="/usr/lib/airtime/airtime_virtualenv/bin/"
|
||||||
|
|
||||||
ls_user="pypo"
|
ls_user="pypo"
|
||||||
export HOME="/var/tmp/airtime/pypo/"
|
export HOME="/var/tmp/airtime/pypo/"
|
||||||
api_client_path="/usr/lib/airtime/pypo/"
|
api_client_path="/usr/lib/airtime/"
|
||||||
ls_path="/usr/lib/airtime/pypo/bin/liquidsoap_bin/liquidsoap"
|
ls_path="/usr/lib/airtime/pypo/bin/liquidsoap_bin/liquidsoap"
|
||||||
ls_param="/usr/lib/airtime/pypo/bin/liquidsoap_scripts/ls_script.liq"
|
ls_param="/usr/lib/airtime/pypo/bin/liquidsoap_scripts/ls_script.liq"
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ pypo_user="pypo"
|
||||||
|
|
||||||
# Location of pypo_cli.py Python script
|
# Location of pypo_cli.py Python script
|
||||||
pypo_path="/usr/lib/airtime/pypo/bin/"
|
pypo_path="/usr/lib/airtime/pypo/bin/"
|
||||||
api_client_path="/usr/lib/airtime/pypo/"
|
api_client_path="/usr/lib/airtime/"
|
||||||
pypo_script="pypo-cli.py"
|
pypo_script="pypo-cli.py"
|
||||||
cd ${pypo_path}
|
cd ${pypo_path}
|
||||||
exec 2>&1
|
exec 2>&1
|
||||||
|
|
|
@ -96,7 +96,6 @@ try:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
copy_dir("%s/.."%current_script_dir, config["bin_dir"]+"/bin/")
|
copy_dir("%s/.."%current_script_dir, config["bin_dir"]+"/bin/")
|
||||||
copy_dir("%s/../../api_clients"%current_script_dir, config["bin_dir"]+"/api_clients/")
|
|
||||||
|
|
||||||
print "Setting permissions"
|
print "Setting permissions"
|
||||||
os.system("chmod -R 755 "+config["bin_dir"])
|
os.system("chmod -R 755 "+config["bin_dir"])
|
||||||
|
|
|
@ -10,7 +10,7 @@ recorder_user="pypo"
|
||||||
recorder_path="/usr/lib/airtime/show-recorder/"
|
recorder_path="/usr/lib/airtime/show-recorder/"
|
||||||
recorder_script="recorder.py"
|
recorder_script="recorder.py"
|
||||||
|
|
||||||
api_client_path="/usr/lib/airtime/pypo/"
|
api_client_path="/usr/lib/airtime/"
|
||||||
cd ${recorder_path}
|
cd ${recorder_path}
|
||||||
|
|
||||||
exec 2>&1
|
exec 2>&1
|
||||||
|
|
|
@ -47,10 +47,10 @@ def copy_or_move_files_to(paths, dest, flag):
|
||||||
if( 'mp3' in ext or 'ogg' in ext ):
|
if( 'mp3' in ext or 'ogg' in ext ):
|
||||||
destfile = dest+os.path.basename(path)
|
destfile = dest+os.path.basename(path)
|
||||||
if(flag == 'copy'):
|
if(flag == 'copy'):
|
||||||
print "Copying %(src)s to %(dest)s....." % {'src':path, 'dest':destfile}
|
print "Copying %(src)s to %(dest)s..." % {'src':path, 'dest':destfile}
|
||||||
shutil.copy2(path, destfile)
|
shutil.copy2(path, destfile)
|
||||||
elif(flag == 'move'):
|
elif(flag == 'move'):
|
||||||
print "Moving %(src)s to %(dest)s....." % {'src':path, 'dest':destfile}
|
print "Moving %(src)s to %(dest)s..." % {'src':path, 'dest':destfile}
|
||||||
shutil.move(path, destfile)
|
shutil.move(path, destfile)
|
||||||
else:
|
else:
|
||||||
print "Cannot find file or path: %s" % path
|
print "Cannot find file or path: %s" % path
|
||||||
|
@ -95,7 +95,7 @@ def printHelp():
|
||||||
========================
|
========================
|
||||||
There are two ways to import audio files into Airtime:
|
There are two ways to import audio files into Airtime:
|
||||||
|
|
||||||
1) Copy or move files into the storage folder
|
1) Use airtime-import to copy or move files into the storage folder.
|
||||||
|
|
||||||
Copied or moved files will be placed into the folder:
|
Copied or moved files will be placed into the folder:
|
||||||
%s
|
%s
|
||||||
|
@ -103,12 +103,12 @@ There are two ways to import audio files into Airtime:
|
||||||
Files will be automatically organized into the structure
|
Files will be automatically organized into the structure
|
||||||
"Artist/Album/TrackNumber-TrackName-Bitrate.file_extension".
|
"Artist/Album/TrackNumber-TrackName-Bitrate.file_extension".
|
||||||
|
|
||||||
2) Add a folder to the Airtime library("watch" a folder)
|
2) Use airtime-import to add a folder to the Airtime library ("watch" a folder).
|
||||||
|
|
||||||
All the files in the watched folder will be imported to Airtime and the
|
All the files in the watched folder will be imported to Airtime and the
|
||||||
folder will be monitored to automatically detect any changes. Hence any
|
folder will be monitored to automatically detect any changes. Hence any
|
||||||
changes done in the folder(add, delete, edit a file) will trigger
|
changes done in the folder(add, delete, edit a file) will trigger
|
||||||
updates in Airtime libarary.
|
updates in Airtime library.
|
||||||
""" % storage_dir
|
""" % storage_dir
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
print ""
|
print ""
|
||||||
|
@ -117,7 +117,7 @@ def CopyAction(option, opt, value, parser):
|
||||||
errorIfMultipleOption(parser.rargs)
|
errorIfMultipleOption(parser.rargs)
|
||||||
stor = helper_get_stor_dir()
|
stor = helper_get_stor_dir()
|
||||||
if(stor is None):
|
if(stor is None):
|
||||||
exit("Unable to connect to the server.")
|
exit("Unable to connect to the Airtime server.")
|
||||||
dest = stor+"organize/"
|
dest = stor+"organize/"
|
||||||
copy_or_move_files_to(parser.rargs, dest, 'copy')
|
copy_or_move_files_to(parser.rargs, dest, 'copy')
|
||||||
|
|
||||||
|
@ -125,16 +125,16 @@ def MoveAction(option, opt, value, parser):
|
||||||
errorIfMultipleOption(parser.rargs)
|
errorIfMultipleOption(parser.rargs)
|
||||||
stor = helper_get_stor_dir()
|
stor = helper_get_stor_dir()
|
||||||
if(stor is None):
|
if(stor is None):
|
||||||
exit("Unable to connect to the server.")
|
exit("Unable to connect to the Airtime server.")
|
||||||
dest = stor+"organize/"
|
dest = stor+"organize/"
|
||||||
copy_or_move_files_to(parser.rargs, dest, 'move')
|
copy_or_move_files_to(parser.rargs, dest, 'move')
|
||||||
|
|
||||||
def WatchAddAction(option, opt, value, parser):
|
def WatchAddAction(option, opt, value, parser):
|
||||||
errorIfMultipleOption(parser.rargs)
|
errorIfMultipleOption(parser.rargs)
|
||||||
if(len(parser.rargs) > 1):
|
if(len(parser.rargs) > 1):
|
||||||
raise OptionValueError("Too many arguments. This option need exactly one argument.")
|
raise OptionValueError("Too many arguments. This option requires exactly one argument.")
|
||||||
elif(len(parser.rargs) == 0 ):
|
elif(len(parser.rargs) == 0 ):
|
||||||
raise OptionValueError("No argument found. This option need exactly one argument.")
|
raise OptionValueError("No argument found. This option requires exactly one argument.")
|
||||||
path = parser.rargs[0]
|
path = parser.rargs[0]
|
||||||
if(os.path.isdir(path)):
|
if(os.path.isdir(path)):
|
||||||
res = api_client.add_watched_dir(path)
|
res = api_client.add_watched_dir(path)
|
||||||
|
@ -144,17 +144,17 @@ def WatchAddAction(option, opt, value, parser):
|
||||||
if(res['msg']['code'] == 0):
|
if(res['msg']['code'] == 0):
|
||||||
print "%s added to watched folder list successfully" % path
|
print "%s added to watched folder list successfully" % path
|
||||||
else:
|
else:
|
||||||
print "Adding a watched folder failed. : %s" % res['msg']['error']
|
print "Adding a watched folder failed: %s" % res['msg']['error']
|
||||||
else:
|
else:
|
||||||
print "Given path is not a directory: %s" % path
|
print "Given path is not a directory: %s" % path
|
||||||
|
|
||||||
def WatchListAction(option, opt, value, parser):
|
def WatchListAction(option, opt, value, parser):
|
||||||
errorIfMultipleOption(parser.rargs)
|
errorIfMultipleOption(parser.rargs)
|
||||||
if(len(parser.rargs) > 0):
|
if(len(parser.rargs) > 0):
|
||||||
raise OptionValueError("This option doesn't take any argument.")
|
raise OptionValueError("This option doesn't take any arguments.")
|
||||||
res = api_client.list_all_watched_dirs()
|
res = api_client.list_all_watched_dirs()
|
||||||
if(res is None):
|
if(res is None):
|
||||||
exit("Unable to connect to the server.")
|
exit("Unable to connect to the Airtime server.")
|
||||||
dirs = res["dirs"].items()
|
dirs = res["dirs"].items()
|
||||||
# there will be always 1 which is storage folder
|
# there will be always 1 which is storage folder
|
||||||
if(len(dirs) == 1):
|
if(len(dirs) == 1):
|
||||||
|
@ -167,21 +167,21 @@ def WatchListAction(option, opt, value, parser):
|
||||||
def WatchRemoveAction(option, opt, value, parser):
|
def WatchRemoveAction(option, opt, value, parser):
|
||||||
errorIfMultipleOption(parser.rargs)
|
errorIfMultipleOption(parser.rargs)
|
||||||
if(len(parser.rargs) > 1):
|
if(len(parser.rargs) > 1):
|
||||||
raise OptionValueError("Too many arguments. This option need exactly one argument.")
|
raise OptionValueError("Too many arguments. This option requires exactly one argument.")
|
||||||
elif(len(parser.rargs) == 0 ):
|
elif(len(parser.rargs) == 0 ):
|
||||||
raise OptionValueError("No argument found. This option need exactly one argument.")
|
raise OptionValueError("No argument found. This option requires exactly one argument.")
|
||||||
path = parser.rargs[0]
|
path = parser.rargs[0]
|
||||||
if(os.path.isdir(path)):
|
if(os.path.isdir(path)):
|
||||||
res = api_client.remove_watched_dir(path)
|
res = api_client.remove_watched_dir(path)
|
||||||
if(res is None):
|
if(res is None):
|
||||||
exit("Unable to connect to the server.")
|
exit("Unable to connect to the Airtime server.")
|
||||||
# sucess
|
# sucess
|
||||||
if(res['msg']['code'] == 0):
|
if(res['msg']['code'] == 0):
|
||||||
print "%s removed from watched folder list successfully" % path
|
print "%s removed from watch folder list successfully." % path
|
||||||
else:
|
else:
|
||||||
print "Removing a watched folder failed. : %s" % res['msg']['error']
|
print "Removing the watch folder failed: %s" % res['msg']['error']
|
||||||
else:
|
else:
|
||||||
print "Given path is not a directory: %s" % path
|
print "The given path is not a directory: %s" % path
|
||||||
|
|
||||||
def StorageSetAction(option, opt, value, parser):
|
def StorageSetAction(option, opt, value, parser):
|
||||||
bypass = False
|
bypass = False
|
||||||
|
@ -199,38 +199,38 @@ def StorageSetAction(option, opt, value, parser):
|
||||||
confirm = raw_input("Are you sure you want to change the storage direcory? (y/N)")
|
confirm = raw_input("Are you sure you want to change the storage direcory? (y/N)")
|
||||||
confirm = confirm or 'N'
|
confirm = confirm or 'N'
|
||||||
while(confirm not in possibleInput):
|
while(confirm not in possibleInput):
|
||||||
print "Not an acceptable input: %s" % confirm
|
print "Not an acceptable input: %s\n" % confirm
|
||||||
confirm = raw_input("Are you sure you want to change the storage direcory? (y/N)")
|
confirm = raw_input("Are you sure you want to change the storage direcory? (y/N) ")
|
||||||
confirm = confirm or 'N'
|
confirm = confirm or 'N'
|
||||||
if(confirm == 'n' or confirm =='N'):
|
if(confirm == 'n' or confirm =='N'):
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if(len(parser.rargs) > 1):
|
if(len(parser.rargs) > 1):
|
||||||
raise OptionValueError("Too many arguments. This option need exactly one argument.")
|
raise OptionValueError("Too many arguments. This option requires exactly one argument.")
|
||||||
elif(len(parser.rargs) == 0 ):
|
elif(len(parser.rargs) == 0 ):
|
||||||
raise OptionValueError("No argument found. This option need exactly one argument.")
|
raise OptionValueError("No argument found. This option requires exactly one argument.")
|
||||||
|
|
||||||
path = parser.rargs[0]
|
path = parser.rargs[0]
|
||||||
if(os.path.isdir(path)):
|
if(os.path.isdir(path)):
|
||||||
res = api_client.set_storage_dir(path)
|
res = api_client.set_storage_dir(path)
|
||||||
if(res is None):
|
if(res is None):
|
||||||
exit("Unable to connect to the server.")
|
exit("Unable to connect to the Airtime server.")
|
||||||
# sucess
|
# sucess
|
||||||
if(res['msg']['code'] == 0):
|
if(res['msg']['code'] == 0):
|
||||||
print "Successfully set storage folder to %s" % path
|
print "Successfully set storage folder to %s" % path
|
||||||
else:
|
else:
|
||||||
print "Setting storage folder to failed.: %s" % res['msg']['error']
|
print "Setting storage folder failed: %s" % res['msg']['error']
|
||||||
else:
|
else:
|
||||||
print "Given path is not a directory: %s" % path
|
print "The given path is not a directory: %s" % path
|
||||||
|
|
||||||
def StorageGetAction(option, opt, value, parser):
|
def StorageGetAction(option, opt, value, parser):
|
||||||
errorIfMultipleOption(parser.rargs)
|
errorIfMultipleOption(parser.rargs)
|
||||||
if(len(parser.rargs) > 0):
|
if(len(parser.rargs) > 0):
|
||||||
raise OptionValueError("This option doesn't take any argument.")
|
raise OptionValueError("This option does not take any arguments.")
|
||||||
print helper_get_stor_dir()
|
print helper_get_stor_dir()
|
||||||
|
|
||||||
usage = """[-c|--copy FILE/DIR [FILE/DIR...]] [-m|--move FILE/DIR [FILE/DIR...]]
|
usage = """[-c|--copy FILE/DIR [FILE/DIR...]] [-m|--move FILE/DIR [FILE/DIR...]]
|
||||||
[--watch-add DIR] [--watch-list] [--watch-remve DIR]
|
[--watch-add DIR] [--watch-list] [--watch-remove DIR]
|
||||||
[--storage-dir-set DIR] [--storage-dir-get]"""
|
[--storage-dir-set DIR] [--storage-dir-get]"""
|
||||||
|
|
||||||
parser = OptionParser(usage=usage, add_help_option=False)
|
parser = OptionParser(usage=usage, add_help_option=False)
|
||||||
|
|
Loading…
Reference in New Issue