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_RELEASE=1.9.0-beta4
|
||||
PRODUCT_RELEASE=1.9.0-RC1
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
define('AIRTIME_VERSION', '1.9.0-beta4');
|
||||
define('AIRTIME_VERSION', '1.9.0-RC1');
|
||||
define('AIRTIME_COPYRIGHT_DATE', '2010-2011');
|
||||
define('AIRTIME_REST_VERSION', '1.1');
|
||||
|
||||
|
|
|
@ -74,8 +74,8 @@ class ApiController extends Zend_Controller_Action
|
|||
$download = ("true" == $this->_getParam('download'));
|
||||
|
||||
$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()))
|
||||
{
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
|
@ -120,7 +120,7 @@ class ApiController extends Zend_Controller_Action
|
|||
fclose($fp);
|
||||
|
||||
//make sure to exit here so that no other output is sent.
|
||||
exit;
|
||||
exit;
|
||||
} else {
|
||||
$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->showinstanceid = $show_instance_id;
|
||||
|
||||
|
||||
|
||||
$showCanceled = false;
|
||||
$file = StoredFile::Recall($file_id);
|
||||
//$show_instance = $this->_getParam('show_instance');
|
||||
|
||||
$show_name = "";
|
||||
$show_name = null;
|
||||
try {
|
||||
$show_inst = new ShowInstance($show_instance_id);
|
||||
|
||||
|
@ -359,10 +359,17 @@ class ApiController extends Zend_Controller_Action
|
|||
$showCanceled = true;
|
||||
}
|
||||
|
||||
$tmpTitle = !(empty($show_name))?$show_name."-":"";
|
||||
$tmpTitle .= $file->getName();
|
||||
if (isset($show_name)) {
|
||||
$tmpTitle = "$show_name-$show_start_time";
|
||||
$tmpTitle = str_replace(" ", "-", $tmpTitle);
|
||||
}
|
||||
else {
|
||||
$tmpTitle = $file->getName();
|
||||
}
|
||||
|
||||
$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())
|
||||
{
|
||||
|
@ -392,7 +399,7 @@ class ApiController extends Zend_Controller_Action
|
|||
}
|
||||
|
||||
$this->view->id = $file_id;
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function mediaMonitorSetupAction() {
|
||||
|
@ -409,7 +416,7 @@ class ApiController extends Zend_Controller_Action
|
|||
print 'You are not allowed to access this resource.';
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
$this->view->stor = MusicDir::getStorDir()->getDirectory();
|
||||
}
|
||||
|
||||
|
@ -439,7 +446,7 @@ class ApiController extends Zend_Controller_Action
|
|||
if ($mode == "create") {
|
||||
$filepath = $md['MDATA_KEY_FILEPATH'];
|
||||
$file = StoredFile::RecallByFilepath($filepath);
|
||||
|
||||
|
||||
if (is_null($file)) {
|
||||
$file = StoredFile::Insert($md);
|
||||
} else {
|
||||
|
@ -447,7 +454,7 @@ class ApiController extends Zend_Controller_Action
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//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
|
||||
//not by filepath?
|
||||
|
@ -509,7 +516,7 @@ class ApiController extends Zend_Controller_Action
|
|||
|
||||
$this->view->id = $file->getId();
|
||||
}
|
||||
|
||||
|
||||
public function listAllFilesAction() {
|
||||
global $CC_CONFIG;
|
||||
|
||||
|
@ -522,10 +529,10 @@ class ApiController extends Zend_Controller_Action
|
|||
exit;
|
||||
}
|
||||
$dir_id = $request->getParam('dir_id');
|
||||
|
||||
|
||||
$this->view->files = StoredFile::listAllFiles($dir_id);
|
||||
}
|
||||
|
||||
|
||||
public function listAllWatchedDirsAction() {
|
||||
global $CC_CONFIG;
|
||||
|
||||
|
@ -537,69 +544,69 @@ class ApiController extends Zend_Controller_Action
|
|||
print 'You are not allowed to access this resource.';
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
$result = array();
|
||||
|
||||
|
||||
$arrWatchedDirs = MusicDir::getWatchedDirs();
|
||||
$storDir = MusicDir::getStorDir();
|
||||
|
||||
|
||||
$result[$storDir->getId()] = $storDir->getDirectory();
|
||||
|
||||
|
||||
foreach ($arrWatchedDirs as $watchedDir){
|
||||
$result[$watchedDir->getId()] = $watchedDir->getDirectory();
|
||||
}
|
||||
|
||||
|
||||
$this->view->dirs = $result;
|
||||
}
|
||||
|
||||
|
||||
public function addWatchedDirAction() {
|
||||
global $CC_CONFIG;
|
||||
|
||||
$request = $this->getRequest();
|
||||
$api_key = $request->getParam('api_key');
|
||||
$path = base64_decode($request->getParam('path'));
|
||||
|
||||
|
||||
if (!in_array($api_key, $CC_CONFIG["apiKey"]))
|
||||
{
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
print 'You are not allowed to access this resource.';
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
$this->view->msg = MusicDir::addWatchedDir($path);
|
||||
}
|
||||
|
||||
|
||||
public function removeWatchedDirAction() {
|
||||
global $CC_CONFIG;
|
||||
|
||||
$request = $this->getRequest();
|
||||
$api_key = $request->getParam('api_key');
|
||||
$path = base64_decode($request->getParam('path'));
|
||||
|
||||
|
||||
if (!in_array($api_key, $CC_CONFIG["apiKey"]))
|
||||
{
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
print 'You are not allowed to access this resource.';
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
$this->view->msg = MusicDir::removeWatchedDir($path);
|
||||
}
|
||||
|
||||
|
||||
public function setStorageDirAction() {
|
||||
global $CC_CONFIG;
|
||||
|
||||
$request = $this->getRequest();
|
||||
$api_key = $request->getParam('api_key');
|
||||
$path = base64_decode($request->getParam('path'));
|
||||
|
||||
|
||||
if (!in_array($api_key, $CC_CONFIG["apiKey"]))
|
||||
{
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
print 'You are not allowed to access this resource.';
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
$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('#type#', $type, $paramsPop);
|
||||
|
||||
$userInfo = Zend_Auth::getInstance()->getStorage()->read();
|
||||
$user = new User($userInfo->id);
|
||||
|
||||
$pl_sess = $this->pl_sess;
|
||||
|
||||
if($type === "au") {
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
|
@ -85,9 +88,12 @@ class LibraryController extends Zend_Controller_Action
|
|||
$menu[] = array('action' => array('type' => 'gourl', 'url' => $url),
|
||||
'title' => 'Download');
|
||||
|
||||
$menu[] = array('action' => array('type' => 'fn',
|
||||
'callback' => "window['confirmDeleteAudioClip']('$paramsPop')"),
|
||||
'title' => 'Delete');
|
||||
|
||||
if ($user->isAdmin()) {
|
||||
$menu[] = array('action' => array('type' => 'fn',
|
||||
'callback' => "window['confirmDeleteAudioClip']('$paramsPop')"),
|
||||
'title' => 'Delete');
|
||||
}
|
||||
}
|
||||
else if($type === "pl") {
|
||||
|
||||
|
@ -121,32 +127,38 @@ class LibraryController extends Zend_Controller_Action
|
|||
public function deleteAction()
|
||||
{
|
||||
$id = $this->_getParam('id');
|
||||
$userInfo = Zend_Auth::getInstance()->getStorage()->read();
|
||||
$user = new User($userInfo->id);
|
||||
|
||||
if (!is_null($id)) {
|
||||
$file = StoredFile::Recall($id);
|
||||
if ($user->isAdmin()) {
|
||||
|
||||
if (PEAR::isError($file)) {
|
||||
$this->view->message = $file->getMessage();
|
||||
return;
|
||||
}
|
||||
else if(is_null($file)) {
|
||||
$this->view->message = "file doesn't exist";
|
||||
return;
|
||||
if (!is_null($id)) {
|
||||
$file = StoredFile::Recall($id);
|
||||
|
||||
if (PEAR::isError($file)) {
|
||||
$this->view->message = $file->getMessage();
|
||||
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();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
$this->view->id = $id;
|
||||
}
|
||||
|
||||
public function contentsAction()
|
||||
|
|
|
@ -24,10 +24,10 @@ class NowplayingController extends Zend_Controller_Action
|
|||
$refer_sses = new Zend_Session_Namespace('referrer');
|
||||
$userInfo = Zend_Auth::getInstance()->getStorage()->read();
|
||||
$user = new User($userInfo->id);
|
||||
|
||||
$form = new Application_Form_RegisterAirtime();
|
||||
|
||||
|
||||
if ($request->isPost()) {
|
||||
$form = new Application_Form_RegisterAirtime();
|
||||
|
||||
$values = $request->getPost();
|
||||
if ($values["Publicise"] != 1){
|
||||
Application_Model_Preference::SetSupportFeedback($values["SupportFeedback"]);
|
||||
|
@ -64,6 +64,10 @@ class NowplayingController extends Zend_Controller_Action
|
|||
//popup if previous page was login
|
||||
if($refer_sses->referrer == 'login' && Application_Model_Nowplaying::ShouldShowPopUp()
|
||||
&& !Application_Model_Preference::GetSupportFeedback() && $user->isAdmin()){
|
||||
|
||||
$form = new Application_Form_RegisterAirtime();
|
||||
|
||||
|
||||
$logo = Application_Model_Preference::GetStationLogo();
|
||||
if($logo){
|
||||
$this->view->logoImg = $logo;
|
||||
|
|
|
@ -123,9 +123,10 @@ class PreferenceController extends Zend_Controller_Action
|
|||
$chosen = $this->getRequest()->getParam("dir");
|
||||
$element = $this->getRequest()->getParam("element");
|
||||
$watched_dirs_form = new Application_Form_WatchedDirPreferences();
|
||||
$watched_dirs_form->populate(array('storageFolder' => $chosen));
|
||||
|
||||
$res = MusicDir::setStorDir($chosen);
|
||||
if($res['code'] != 0){
|
||||
$watched_dirs_form->populate(array('storageFolder' => $chosen));
|
||||
$watched_dirs_form->getElement($element)->setErrors(array($res['error']));
|
||||
}
|
||||
|
||||
|
@ -137,9 +138,10 @@ class PreferenceController extends Zend_Controller_Action
|
|||
$chosen = $this->getRequest()->getParam("dir");
|
||||
$element = $this->getRequest()->getParam("element");
|
||||
$watched_dirs_form = new Application_Form_WatchedDirPreferences();
|
||||
$watched_dirs_form->populate(array('watchedFolder' => $chosen));
|
||||
|
||||
$res = MusicDir::addWatchedDir($chosen);
|
||||
if($res['code'] != 0){
|
||||
$watched_dirs_form->populate(array('watchedFolder' => $chosen));
|
||||
$watched_dirs_form->getElement($element)->setErrors(array($res['error']));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
class NestedDirectoryException extends Exception { }
|
||||
|
||||
class MusicDir {
|
||||
|
||||
/**
|
||||
|
@ -40,7 +42,75 @@ class MusicDir {
|
|||
|
||||
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();
|
||||
|
||||
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)
|
||||
|
@ -51,16 +121,23 @@ class MusicDir {
|
|||
$dir = new CcMusicDirs();
|
||||
$dir->setType($p_type);
|
||||
$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();
|
||||
return array("code"=>0);
|
||||
}
|
||||
catch(Exception $e){
|
||||
//echo $e->getMessage();
|
||||
} catch (NestedDirectoryException $nde){
|
||||
$msg = $nde->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");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static function addWatchedDir($p_path)
|
||||
|
@ -123,8 +200,8 @@ class MusicDir {
|
|||
$mus_dir = new MusicDir($dir);
|
||||
|
||||
return $mus_dir;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static function setStorDir($p_dir)
|
||||
{
|
||||
if(!is_dir($p_dir)){
|
||||
|
@ -162,7 +239,7 @@ class MusicDir {
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static function removeWatchedDir($p_dir){
|
||||
$p_dir = realpath($p_dir)."/";
|
||||
$dir = MusicDir::getDirByPath($p_dir);
|
||||
|
@ -180,7 +257,7 @@ class MusicDir {
|
|||
public static function splitFilePath($p_filepath)
|
||||
{
|
||||
$mus_dir = self::getWatchedDirFromFilepath($p_filepath);
|
||||
|
||||
|
||||
if(is_null($mus_dir)) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ class ScheduleGroup {
|
|||
|
||||
// Check if there are any conflicts with existing entries
|
||||
$metadata = $track->getMetadata();
|
||||
$length = trim($metadata["length"]);
|
||||
$length = $metadata['MDATA_KEY_DURATION'];
|
||||
if (empty($length)) {
|
||||
return new PEAR_Error("Length is empty.");
|
||||
}
|
||||
|
@ -540,7 +540,7 @@ class Schedule {
|
|||
$retVal = $CC_DBC->query($sql);
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
|
||||
public static function getSchduledPlaylistCount(){
|
||||
global $CC_CONFIG, $CC_DBC;
|
||||
$sql = "SELECT count(*) as cnt FROM ".$CC_CONFIG['scheduleTable'];
|
||||
|
@ -750,7 +750,7 @@ class Schedule {
|
|||
global $CC_CONFIG, $CC_DBC;
|
||||
$CC_DBC->query("TRUNCATE TABLE ".$CC_CONFIG["scheduleTable"]);
|
||||
}
|
||||
|
||||
|
||||
public static function createNewFormSections($p_view){
|
||||
$formWhat = new Application_Form_AddShowWhat();
|
||||
$formWho = new Application_Form_AddShowWho();
|
||||
|
@ -769,7 +769,7 @@ class Schedule {
|
|||
$formRecord->removeDecorator('DtDdWrapper');
|
||||
$formAbsoluteRebroadcast->removeDecorator('DtDdWrapper');
|
||||
$formRebroadcast->removeDecorator('DtDdWrapper');
|
||||
|
||||
|
||||
$p_view->what = $formWhat;
|
||||
$p_view->when = $formWhen;
|
||||
$p_view->repeats = $formRepeats;
|
||||
|
@ -779,7 +779,7 @@ class Schedule {
|
|||
$p_view->absoluteRebroadcast = $formAbsoluteRebroadcast;
|
||||
$p_view->rebroadcast = $formRebroadcast;
|
||||
$p_view->addNewShow = true;
|
||||
|
||||
|
||||
$formWhat->populate(array('add_show_id' => '-1'));
|
||||
$formWhen->populate(array('add_show_start_date' => date("Y-m-d"),
|
||||
'add_show_start_time' => '00:00',
|
||||
|
|
|
@ -318,6 +318,13 @@ class StoredFile {
|
|||
|
||||
Playlist::DeleteFileFromAllPlaylists($this->getId());
|
||||
$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.');
|
||||
}
|
||||
|
||||
$storageDir = MusicDir::getStorDir()->getDirectory();
|
||||
$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;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
//used by jjmenu
|
||||
function getId() {
|
||||
function getId() {
|
||||
var tr_id = $(this.triggerElement).attr("id");
|
||||
tr_id = tr_id.split("_");
|
||||
|
||||
return tr_id[1];
|
||||
}
|
||||
|
||||
function getType() {
|
||||
function getType() {
|
||||
var tr_id = $(this.triggerElement).attr("id");
|
||||
tr_id = tr_id.split("_");
|
||||
|
||||
|
@ -25,8 +25,8 @@ function deleteItem(type, id) {
|
|||
}
|
||||
|
||||
function deleteAudioClip(json) {
|
||||
if(json.message) {
|
||||
alert(json.message);
|
||||
if(json.message) {
|
||||
alert(json.message);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -35,13 +35,13 @@ function deleteAudioClip(json) {
|
|||
|
||||
//callbacks called by jjmenu
|
||||
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;
|
||||
$.ajax({
|
||||
url: url,
|
||||
success: deleteAudioClip
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//callbacks called by jjmenu
|
||||
|
@ -52,12 +52,12 @@ function confirmDeletePlaylist(params){
|
|||
url: url,
|
||||
success: deletePlaylist
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function deletePlaylist(json) {
|
||||
if(json.message) {
|
||||
alert(json.message);
|
||||
if(json.message) {
|
||||
alert(json.message);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -69,15 +69,15 @@ function deletePlaylist(json) {
|
|||
function addLibraryItemEvents() {
|
||||
|
||||
$('#library_display tr[id ^= "au"]')
|
||||
.draggable({
|
||||
.draggable({
|
||||
helper: 'clone',
|
||||
cursor: 'pointer'
|
||||
});
|
||||
|
||||
$('#library_display tbody tr')
|
||||
.jjmenu("click",
|
||||
[{get:"/Library/context-menu/format/json/id/#id#/type/#type#"}],
|
||||
{id: getId, type: getType},
|
||||
.jjmenu("click",
|
||||
[{get:"/Library/context-menu/format/json/id/#id#/type/#type#"}],
|
||||
{id: getId, type: getType},
|
||||
{xposition: "mouse", yposition: "mouse"});
|
||||
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ function dtRowCallback( nRow, aData, iDisplayIndex, iDisplayIndexFull ) {
|
|||
}
|
||||
|
||||
$(nRow).attr("id", type+'_'+id);
|
||||
|
||||
|
||||
// insert id on lenth field
|
||||
$('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
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
position: {
|
||||
|
||||
|
||||
adjust: {
|
||||
screen: true // Keep the tooltip on-screen at all times
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
style: {
|
||||
border: {
|
||||
width: 0,
|
||||
|
@ -146,16 +146,16 @@ $(document).ready(function() {
|
|||
"sAjaxSource": "/Library/contents/format/json",
|
||||
"fnServerData": function ( sSource, aoData, fnCallback ) {
|
||||
$.ajax( {
|
||||
"dataType": 'json',
|
||||
"type": "POST",
|
||||
"url": sSource,
|
||||
"data": aoData,
|
||||
"dataType": 'json',
|
||||
"type": "POST",
|
||||
"url": sSource,
|
||||
"data": aoData,
|
||||
"success": fnCallback
|
||||
} );
|
||||
},
|
||||
"fnRowCallback": dtRowCallback,
|
||||
"fnDrawCallback": dtDrawCallback,
|
||||
"aoColumns": [
|
||||
"aoColumns": [
|
||||
/* Id */ { "sName": "id", "bSearchable": false, "bVisible": false },
|
||||
/* Title */ { "sName": "track_title" },
|
||||
/* Creator */ { "sName": "artist_name" },
|
||||
|
|
|
@ -33,7 +33,7 @@ function setWatchedDirEvents() {
|
|||
//knownPaths: [{text:'Desktop', image:'desktop.png', path:'/home'}],
|
||||
knownPaths: [],
|
||||
imageUrl: 'img/icons/',
|
||||
systemImageUrl: 'img/browser/',
|
||||
systemImageUrl: '/css/img/',
|
||||
handlerUrl: '/Preference/server-browse/format/json',
|
||||
title: 'Choose Folder to Watch',
|
||||
basePath: '',
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
namespace DoctrineMigrations;
|
||||
|
||||
/*
|
||||
update cc_files table to include to "directory" column as well as add foreign key relation to
|
||||
cc_music_dirs table.
|
||||
1) update cc_files table to include to "directory" column
|
||||
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,
|
||||
|
@ -14,21 +15,22 @@ class Version20110711161043 extends AbstractMigration
|
|||
{
|
||||
public function up(Schema $schema)
|
||||
{
|
||||
|
||||
//CREATE the default value of "/srv/airtime/stor", this can be updated later in the upgrade script.
|
||||
/* 1) update cc_files table to include to "directory" column */
|
||||
$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', '');");
|
||||
|
||||
$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->addColumn('directory', 'integer', array('default'=> 2));
|
||||
|
||||
$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)
|
||||
|
|
|
@ -19,6 +19,9 @@ python ${SCRIPTPATH}/../python_apps/create-pypo-user.py
|
|||
|
||||
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 ***"
|
||||
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 ***"
|
||||
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 ***"
|
||||
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_PYPO = "/etc/airtime/pypo.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_MEDIAMONITOR = "/etc/airtime/media-monitor.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.";
|
||||
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)){
|
||||
echo "Could not copy pypo.cfg to /etc/airtime/. Exiting.";
|
||||
exit(1);
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
|
||||
//Pear classes.
|
||||
set_include_path(__DIR__.'/../../airtime_mvc/library/pear' . PATH_SEPARATOR . get_include_path());
|
||||
|
||||
require_once('DB.php');
|
||||
require_once(__DIR__.'/../../airtime_mvc/application/configs/constants.php');
|
||||
require_once(dirname(__FILE__).'/AirtimeIni.php');
|
||||
|
||||
if(exec("whoami") != "root"){
|
||||
|
@ -87,9 +89,13 @@ if (strcmp($version, "1.9.0") < 0){
|
|||
//set the new version in the database.
|
||||
$sql = "DELETE FROM cc_pref WHERE keystr = 'system_version'";
|
||||
$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);
|
||||
|
||||
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;
|
||||
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 ');";
|
||||
|
||||
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_LIQUIDSOAP = "/etc/airtime/liquidsoap.cfg";
|
||||
const CONF_FILE_MEDIAMONITOR = "/etc/airtime/media-monitor.cfg";
|
||||
|
||||
|
||||
/**
|
||||
* 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,
|
||||
AirtimeIni::CONF_FILE_PYPO,
|
||||
|
@ -443,7 +443,7 @@ class AirtimeIni{
|
|||
* This function creates the /etc/airtime configuration folder
|
||||
* and copies the default config files to it.
|
||||
*/
|
||||
function CreateIniFiles()
|
||||
public static function CreateIniFiles()
|
||||
{
|
||||
if (!file_exists("/etc/airtime/")){
|
||||
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;
|
||||
exec("mkdir -p ".$phpDir);
|
||||
exec("cp -R ".$AIRTIME_SRC."/* ".$phpDir);
|
||||
}
|
||||
$AIRTIME_SRC = realpath(__DIR__.'/../../../airtime_mvc');
|
||||
|
||||
function CopyUtils()
|
||||
{
|
||||
$utilsSrc = __DIR__."/../../../utils";
|
||||
|
||||
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 "* Installing PHP code to ".$phpDir.PHP_EOL;
|
||||
exec("mkdir -p ".$phpDir);
|
||||
exec("cp -R ".$AIRTIME_SRC."/* ".$phpDir);
|
||||
}
|
||||
|
||||
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);
|
||||
public static function CopyUtils()
|
||||
{
|
||||
$utilsSrc = __DIR__."/../../../utils";
|
||||
|
||||
$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){
|
||||
$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'";
|
||||
/* Removes pypo, media-monitor, show-recorder and utils from system. These will
|
||||
* be reinstalled by the main airtime-upgrade script.
|
||||
*/
|
||||
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;
|
||||
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();
|
||||
|
||||
/* 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 */
|
||||
$values = parse_ini_file(AirtimeIni::CONF_FILE_AIRTIME, true);
|
||||
$phpDir = $values['general']['airtime_dir'];
|
||||
InstallAirtimePhpServerCode($phpDir);
|
||||
Airtime190Upgrade::InstallAirtimePhpServerCode($phpDir);
|
||||
|
||||
/* update utils (/usr/lib/airtime) folder */
|
||||
UninstallBinaries();
|
||||
CopyUtils();
|
||||
Airtime190Upgrade::UninstallBinaries();
|
||||
Airtime190Upgrade::CopyUtils();
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
removeOldAirtimeImport();
|
||||
updateAirtimeImportSymLink();
|
||||
Airtime190Upgrade::removeOldAirtimeImport();
|
||||
Airtime190Upgrade::updateAirtimeImportSymLink();
|
||||
|
||||
connectToDatabase();
|
||||
Airtime190Upgrade::connectToDatabase();
|
||||
|
||||
if(AirtimeInstall::DbTableExists('doctrine_migration_versions') === false) {
|
||||
$migrations = array('20110312121200', '20110331111708', '20110402164819', '20110406182005');
|
||||
|
@ -652,7 +659,7 @@ AirtimeInstall::InsertCountryDataIntoDatabase();
|
|||
/* create cron file for phone home stat */
|
||||
AirtimeInstall::CreateCronFile();
|
||||
|
||||
installMediaMonitor($values);
|
||||
Airtime190Upgrade::installMediaMonitor($values);
|
||||
|
||||
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.info("\n\n*** Media Monitor bootup ***\n\n")
|
||||
|
||||
try:
|
||||
config = AirtimeMediaConfig(logger)
|
||||
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_script="MediaMonitor.py"
|
||||
|
||||
api_client_path="/usr/lib/airtime/pypo/"
|
||||
api_client_path="/usr/lib/airtime/"
|
||||
|
||||
cd ${media_monitor_path}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ from subprocess import Popen, PIPE
|
|||
class AirtimeMediaMonitorBootstrap():
|
||||
|
||||
"""AirtimeMediaMonitorBootstrap constructor
|
||||
|
||||
|
||||
Keyword Arguments:
|
||||
logger -- reference to the media-monitor logging facility
|
||||
pe -- reference to an instance of ProcessEvent
|
||||
|
@ -17,36 +17,36 @@ class AirtimeMediaMonitorBootstrap():
|
|||
self.pe = pe
|
||||
self.api_client = api_client
|
||||
self.mmc = mmc
|
||||
|
||||
|
||||
"""On bootup we want to scan all directories and look for files that
|
||||
weren't there or files that changed before media-monitor process
|
||||
went offline.
|
||||
"""
|
||||
def scan(self):
|
||||
directories = self.get_list_of_watched_dirs();
|
||||
|
||||
|
||||
self.logger.info("watched directories found: %s", directories)
|
||||
|
||||
|
||||
for id, dir in directories.iteritems():
|
||||
self.logger.debug("%s, %s", id, dir)
|
||||
self.sync_database_to_filesystem(id, dir)
|
||||
|
||||
|
||||
"""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.
|
||||
dir_id -- row id of the directory in the cc_watched_dirs database table
|
||||
"""
|
||||
def list_db_files(self, 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")
|
||||
"""
|
||||
def get_list_of_watched_dirs(self):
|
||||
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)
|
||||
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
|
||||
"""
|
||||
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
|
||||
twice. This is because some of the tests for new files return result sets that are not
|
||||
mutually exclusive from each other.
|
||||
"""
|
||||
new_and_modified_files = set()
|
||||
removed_files = set()
|
||||
|
||||
|
||||
|
||||
|
||||
db_known_files_set = set()
|
||||
files = self.list_db_files(dir_id)
|
||||
for file in files['files']:
|
||||
db_known_files_set.add(file)
|
||||
|
||||
|
||||
new_files = self.mmc.scan_dir_for_new_files(dir)
|
||||
all_files_set = set()
|
||||
for file_path in new_files:
|
||||
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):
|
||||
"""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)
|
||||
command = "find %s -type f -iname '*.ogg' -o -iname '*.mp3' -readable -mmin -%d" % (dir, time_diff_sec/60+1)
|
||||
else:
|
||||
command = "find %s -type f -iname '*.ogg' -o -iname '*.mp3' -readable" % dir
|
||||
|
||||
|
||||
stdout = self.mmc.execCommandAndReturnStdOut(command)
|
||||
stdout = unicode(stdout, "utf_8")
|
||||
|
||||
new_files = stdout.splitlines()
|
||||
|
||||
|
||||
for file_path in new_files:
|
||||
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
|
||||
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
|
||||
that the db has no record of, and vice-versa.
|
||||
"""
|
||||
|
||||
|
||||
deleted_files_set = db_known_files_set - all_files_set
|
||||
new_files_set = all_files_set - db_known_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("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
|
||||
self.mmc.touch_index_file()
|
||||
|
||||
|
||||
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:
|
||||
file_path = "%s/%s" % (dir, file_path)
|
||||
file_path = "%s%s" % (dir, file_path)
|
||||
if os.path.exists(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:
|
||||
file_path = "%s/%s" % (dir, file_path)
|
||||
file_path = "%s%s" % (dir, file_path)
|
||||
if os.path.exists(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)
|
||||
mm.add_filepath_to_ignore(filepath)
|
||||
|
||||
if m['delete'] == "true":
|
||||
if m['delete']:
|
||||
self.logger.info("Deleting file: %s ", 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.
|
||||
#Easy Tag creates this when changing metadata of ogg files.
|
||||
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):
|
||||
if self.mmc.is_parent_directory(pathname, self.config.organize_directory):
|
||||
#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"
|
||||
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_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
|
||||
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"
|
||||
cd ${pypo_path}
|
||||
exec 2>&1
|
||||
|
|
|
@ -96,7 +96,6 @@ try:
|
|||
sys.exit(1)
|
||||
|
||||
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"
|
||||
os.system("chmod -R 755 "+config["bin_dir"])
|
||||
|
|
|
@ -10,7 +10,7 @@ recorder_user="pypo"
|
|||
recorder_path="/usr/lib/airtime/show-recorder/"
|
||||
recorder_script="recorder.py"
|
||||
|
||||
api_client_path="/usr/lib/airtime/pypo/"
|
||||
api_client_path="/usr/lib/airtime/"
|
||||
cd ${recorder_path}
|
||||
|
||||
exec 2>&1
|
||||
|
|
|
@ -47,10 +47,10 @@ def copy_or_move_files_to(paths, dest, flag):
|
|||
if( 'mp3' in ext or 'ogg' in ext ):
|
||||
destfile = dest+os.path.basename(path)
|
||||
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)
|
||||
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)
|
||||
else:
|
||||
print "Cannot find file or path: %s" % path
|
||||
|
@ -95,7 +95,7 @@ def printHelp():
|
|||
========================
|
||||
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:
|
||||
%s
|
||||
|
@ -103,12 +103,12 @@ There are two ways to import audio files into Airtime:
|
|||
Files will be automatically organized into the structure
|
||||
"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
|
||||
folder will be monitored to automatically detect any changes. Hence any
|
||||
changes done in the folder(add, delete, edit a file) will trigger
|
||||
updates in Airtime libarary.
|
||||
updates in Airtime library.
|
||||
""" % storage_dir
|
||||
parser.print_help()
|
||||
print ""
|
||||
|
@ -117,7 +117,7 @@ def CopyAction(option, opt, value, parser):
|
|||
errorIfMultipleOption(parser.rargs)
|
||||
stor = helper_get_stor_dir()
|
||||
if(stor is None):
|
||||
exit("Unable to connect to the server.")
|
||||
exit("Unable to connect to the Airtime server.")
|
||||
dest = stor+"organize/"
|
||||
copy_or_move_files_to(parser.rargs, dest, 'copy')
|
||||
|
||||
|
@ -125,16 +125,16 @@ def MoveAction(option, opt, value, parser):
|
|||
errorIfMultipleOption(parser.rargs)
|
||||
stor = helper_get_stor_dir()
|
||||
if(stor is None):
|
||||
exit("Unable to connect to the server.")
|
||||
exit("Unable to connect to the Airtime server.")
|
||||
dest = stor+"organize/"
|
||||
copy_or_move_files_to(parser.rargs, dest, 'move')
|
||||
|
||||
def WatchAddAction(option, opt, value, parser):
|
||||
errorIfMultipleOption(parser.rargs)
|
||||
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 ):
|
||||
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]
|
||||
if(os.path.isdir(path)):
|
||||
res = api_client.add_watched_dir(path)
|
||||
|
@ -144,17 +144,17 @@ def WatchAddAction(option, opt, value, parser):
|
|||
if(res['msg']['code'] == 0):
|
||||
print "%s added to watched folder list successfully" % path
|
||||
else:
|
||||
print "Adding a watched folder failed. : %s" % res['msg']['error']
|
||||
print "Adding a watched folder failed: %s" % res['msg']['error']
|
||||
else:
|
||||
print "Given path is not a directory: %s" % path
|
||||
|
||||
def WatchListAction(option, opt, value, parser):
|
||||
errorIfMultipleOption(parser.rargs)
|
||||
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()
|
||||
if(res is None):
|
||||
exit("Unable to connect to the server.")
|
||||
exit("Unable to connect to the Airtime server.")
|
||||
dirs = res["dirs"].items()
|
||||
# there will be always 1 which is storage folder
|
||||
if(len(dirs) == 1):
|
||||
|
@ -167,21 +167,21 @@ def WatchListAction(option, opt, value, parser):
|
|||
def WatchRemoveAction(option, opt, value, parser):
|
||||
errorIfMultipleOption(parser.rargs)
|
||||
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 ):
|
||||
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]
|
||||
if(os.path.isdir(path)):
|
||||
res = api_client.remove_watched_dir(path)
|
||||
if(res is None):
|
||||
exit("Unable to connect to the server.")
|
||||
exit("Unable to connect to the Airtime server.")
|
||||
# sucess
|
||||
if(res['msg']['code'] == 0):
|
||||
print "%s removed from watched folder list successfully" % path
|
||||
print "%s removed from watch folder list successfully." % path
|
||||
else:
|
||||
print "Removing a watched folder failed. : %s" % res['msg']['error']
|
||||
print "Removing the watch folder failed: %s" % res['msg']['error']
|
||||
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):
|
||||
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 = confirm or 'N'
|
||||
while(confirm not in possibleInput):
|
||||
print "Not an acceptable input: %s" % confirm
|
||||
confirm = raw_input("Are you sure you want to change the storage direcory? (y/N)")
|
||||
print "Not an acceptable input: %s\n" % confirm
|
||||
confirm = raw_input("Are you sure you want to change the storage direcory? (y/N) ")
|
||||
confirm = confirm or 'N'
|
||||
if(confirm == 'n' or confirm =='N'):
|
||||
sys.exit(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 ):
|
||||
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]
|
||||
if(os.path.isdir(path)):
|
||||
res = api_client.set_storage_dir(path)
|
||||
if(res is None):
|
||||
exit("Unable to connect to the server.")
|
||||
exit("Unable to connect to the Airtime server.")
|
||||
# sucess
|
||||
if(res['msg']['code'] == 0):
|
||||
print "Successfully set storage folder to %s" % path
|
||||
else:
|
||||
print "Setting storage folder to failed.: %s" % res['msg']['error']
|
||||
print "Setting storage folder failed: %s" % res['msg']['error']
|
||||
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):
|
||||
errorIfMultipleOption(parser.rargs)
|
||||
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()
|
||||
|
||||
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]"""
|
||||
|
||||
parser = OptionParser(usage=usage, add_help_option=False)
|
||||
|
|
Loading…
Reference in New Issue