From 1eea96eefc0e5e9416ef8e60108aecd53b230f0c Mon Sep 17 00:00:00 2001 From: Naomi Date: Fri, 13 May 2011 13:27:25 -0400 Subject: [PATCH 1/2] CC-1799 Put Airtime Storage into a Human Readable File Naming Convention naming convention in fallback order stor/artist/album/track - title.ext stor/artist/album/title.ext stor/artist/album/originalfilename.ext stor/artist/track - title.ext stor/artist/title.ext stor/artist/originalfilename.ext stor/originalfilename.ext --- airtime_mvc/application/models/StoredFile.php | 71 +++++++++++++++++-- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php index d5d4faf80..32ced891d 100644 --- a/airtime_mvc/application/models/StoredFile.php +++ b/airtime_mvc/application/models/StoredFile.php @@ -773,6 +773,31 @@ class StoredFile { return $rows; } + private function ensureDir($dir) + { + if (!is_dir($dir)) { + mkdir($dir, 02775); + chmod($dir, 02775); + } + } + + private function createUniqueFilename($base, $ext) + { + if(file_exists("$base.$ext")) { + $i = 1; + while(true) { + if(file_exists("$base($i).$ext")) { + $i = $i+1; + } + else { + return "$base($i).$ext"; + } + } + } + + return "$base.$ext"; + } + /** * Generate the location to store the file. * It creates the subdirectory if needed. @@ -780,14 +805,48 @@ class StoredFile { private function generateFilePath() { global $CC_CONFIG, $CC_DBC; - $resDir = $CC_CONFIG['storageDir']."/".substr($this->gunid, 0, 3); - if (!is_dir($resDir)) { - mkdir($resDir, 02775); - chmod($resDir, 02775); - } + + $storageDir = $CC_CONFIG['storageDir']; $info = pathinfo($this->name); + $origName = $info['filename']; $fileExt = strtolower($info["extension"]); - return "{$resDir}/{$this->gunid}.{$fileExt}"; + + $this->loadMetadata(); + + $artist = $this->md["dc:creator"]; + $album = $this->md["dc:source"]; + $title = $this->md["dc:title"]; + $track_num = $this->md["ls:track_num"]; + + if(isset($artist) && $artist != "") { + $base = "$storageDir/$artist"; + $this->ensureDir($base); + + if(isset($album) && $album != "") { + $base = "$base/$album"; + $this->ensureDir($base); + } + + if(isset($title) && $title != "") { + if(isset($track_num) && $track_num != "") { + if($track_num < 10 && strlen($track_num) == 1) { + $track_num = "0$track_num"; + } + $base = "$base/$track_num - $title"; + } + else { + $base = "$base/$title"; + } + } + else { + $base = "$base/$origName"; + } + } + else { + $base = "$storageDir/$origName"; + } + + return $this->createUniqueFilename($base, $fileExt); } /** From ea1b254ebd4db8743c6feff49298124a461858d3 Mon Sep 17 00:00:00 2001 From: Naomi Date: Fri, 13 May 2011 18:03:34 -0400 Subject: [PATCH 2/2] CC-1799 Put Airtime Storage into a Human Readable File Naming Convention searching database by filename to retrieve file. Checking if is supported audio file/temp audio file. --- .../application/controllers/ApiController.php | 7 +-- airtime_mvc/application/models/StoredFile.php | 4 +- python_apps/pytag-fs/MediaMonitor.py | 45 +++++++++++-------- 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index a6f0825b7..23073c351 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -64,7 +64,7 @@ class ApiController extends Zend_Controller_Action $api_key = $this->_getParam('api_key'); $downlaod = $this->_getParam('download'); - + if(!in_array($api_key, $CC_CONFIG["apiKey"])) { header('HTTP/1.0 401 Unauthorized'); @@ -331,8 +331,9 @@ class ApiController extends Zend_Controller_Action } $md = $this->_getParam('md'); - - $file = StoredFile::Recall(null, $md['gunid']); + $filepath = $md['filepath']; + $filepath = str_replace("\\", "", $filepath); + $file = StoredFile::Recall(null, null, null, $filepath); if (PEAR::isError($file) || is_null($file)) { $this->view->response = "File not in Airtime's Database"; return; diff --git a/airtime_mvc/application/models/StoredFile.php b/airtime_mvc/application/models/StoredFile.php index 32ced891d..1657f2318 100644 --- a/airtime_mvc/application/models/StoredFile.php +++ b/airtime_mvc/application/models/StoredFile.php @@ -701,7 +701,7 @@ class StoredFile { * @return StoredFile|Playlist|NULL * Return NULL if the object doesnt exist in the DB. */ - public static function Recall($p_id=null, $p_gunid=null, $p_md5sum=null) + public static function Recall($p_id=null, $p_gunid=null, $p_md5sum=null, $p_filepath=null) { global $CC_DBC; global $CC_CONFIG; @@ -711,6 +711,8 @@ class StoredFile { $cond = "gunid='$p_gunid'"; } elseif (!is_null($p_md5sum)) { $cond = "md5='$p_md5sum'"; + } elseif (!is_null($p_filepath)) { + $cond = "filepath='$p_filepath'"; } else { return null; } diff --git a/python_apps/pytag-fs/MediaMonitor.py b/python_apps/pytag-fs/MediaMonitor.py index 38423fcc9..d63e7c3b5 100644 --- a/python_apps/pytag-fs/MediaMonitor.py +++ b/python_apps/pytag-fs/MediaMonitor.py @@ -70,7 +70,7 @@ class AirtimeNotifier(Notifier): "isrc_number": "isrc",\ "copyright": "copyright",\ } - + schedule_exchange = Exchange("airtime-media-monitor", "direct", durable=True, auto_delete=True) schedule_queue = Queue("media-monitor", exchange=schedule_exchange, key="filesystem") self.connection = BrokerConnection(config["rabbitmq_host"], config["rabbitmq_user"], config["rabbitmq_password"], "/") @@ -86,7 +86,7 @@ class AirtimeNotifier(Notifier): logger = logging.getLogger('root') logger.info("Received md from RabbitMQ: " + body) - m = json.loads(message.body) + m = json.loads(message.body) airtime_file = mutagen.File(m['filepath'], easy=True) del m['filepath'] for key in m.keys() : @@ -123,21 +123,19 @@ class MediaMonitor(ProcessEvent): "copyright": "copyright",\ } + self.supported_file_formats = ['mp3', 'ogg'] self.logger = logging.getLogger('root') - self.temp_files = {} def update_airtime(self, event): self.logger.info("Updating Change to Airtime") - try: + try: f = open(event.pathname, 'rb') m = hashlib.md5() m.update(f.read()) - md5 = m.hexdigest() - gunid = event.name.split('.')[0] - md = {'gunid':gunid, 'md5':md5} + md = {'filepath':event.pathname, 'md5':md5} file_info = mutagen.File(event.pathname, easy=True) attrs = self.mutagen2airtime @@ -152,12 +150,28 @@ class MediaMonitor(ProcessEvent): except Exception, e: self.logger.info("%s", e) + 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 + + def process_IN_CREATE(self, event): if not event.dir : - filename_info = event.name.split(".") #file created is a tmp file which will be modified and then moved back to the original filename. - if len(filename_info) > 2 : + if self.is_temp_file(event.name) : self.temp_files[event.pathname] = None #This is a newly imported file. else : @@ -165,18 +179,13 @@ class MediaMonitor(ProcessEvent): self.logger.info("%s: %s", event.maskname, event.pathname) - #event.path : /srv/airtime/stor/bd2 - #event.name : bd2aa73b58d9c8abcced989621846e99.mp3 - #event.pathname : /srv/airtime/stor/bd2/bd2aa73b58d9c8abcced989621846e99.mp3 def process_IN_MODIFY(self, event): if not event.dir : - filename_info = event.name.split(".") - #file modified is not a tmp file. - if len(filename_info) == 2 : - self.update_airtime(event) + if self.is_audio_file(event.name) : + self.update_airtime(event) - self.logger.info("%s: path: %s name: %s", event.maskname, event.path, event.name) + self.logger.info("%s: %s", event.maskname, event.pathname) def process_IN_MOVED_FROM(self, event): if event.pathname in self.temp_files : @@ -189,7 +198,7 @@ class MediaMonitor(ProcessEvent): if event.cookie in self.temp_files : del self.temp_files[event.cookie] self.update_airtime(event) - + self.logger.info("%s: %s", event.maskname, event.pathname) def process_default(self, event):