<?php require_once 'formatters/LengthFormatter.php'; /** * * @package Airtime * @copyright 2010 Sourcefabric O.P.S. * @license http://www.gnu.org/licenses/gpl.txt */ class Application_Model_Block { /** * propel connection object. */ private $con; /** * unique id for the block. */ private $id; private $block; /** * info needed to insert a new block element. */ private $blockItem = array( "id" => "", "pos" => "", "cliplength" => "", "cuein" => "00:00:00", "cueout" => "00:00:00", "fadein" => "0.0", "fadeout" => "0.0", ); //using propel's phpNames. private $categories = array( "dc:title" => "Name", "dc:creator" => "Creator", "dc:description" => "Description", "dcterms:extent" => "Length" ); private static $modifier2CriteriaMap = array( "contains" => Criteria::ILIKE, "does not contain" => Criteria::NOT_ILIKE, "is" => Criteria::EQUAL, "is not" => Criteria::NOT_EQUAL, "starts with" => Criteria::ILIKE, "ends with" => Criteria::ILIKE, "is greater than" => Criteria::GREATER_THAN, "is less than" => Criteria::LESS_THAN, "is in the range" => Criteria::CUSTOM); private static $criteria2PeerMap = array( 0 => "Select criteria", "album_title" => "DbAlbumTitle", "artist_name" => "DbArtistName", "bit_rate" => "DbBitRate", "bpm" => "DbBpm", "comments" => "DbComments", "composer" => "DbComposer", "conductor" => "DbConductor", "utime" => "DbUtime", "mtime" => "DbMtime", "lptime" => "DbLPtime", "disc_number" => "DbDiscNumber", "genre" => "DbGenre", "isrc_number" => "DbIsrcNumber", "label" => "DbLabel", "language" => "DbLanguage", "length" => "DbLength", "lyricist" => "DbLyricist", "mood" => "DbMood", "name" => "DbName", "orchestra" => "DbOrchestra", "rating" => "DbRating", "sample_rate" => "DbSampleRate", "track_title" => "DbTrackTitle", "track_number" => "DbTrackNumber", "year" => "DbYear" ); public function __construct($id=null, $con=null) { if (isset($id)) { $this->block = CcBlockQuery::create()->findPk($id); if (is_null($this->block)) { throw new BlockNotFoundException(); } } else { $this->block = new CcBlock(); $this->block->setDbUTime(new DateTime("now", new DateTimeZone("UTC"))); $this->block->save(); } $defaultFade = Application_Model_Preference::GetDefaultFade(); if ($defaultFade !== "") { //fade is in format SS.uuuuuu $this->blockItem["fadein"] = $defaultFade; $this->blockItem["fadeout"] = $defaultFade; } $this->con = isset($con) ? $con : Propel::getConnection(CcBlockPeer::DATABASE_NAME); $this->id = $this->block->getDbId(); } /** * Return local ID of virtual file. * * @return int */ public function getId() { return $this->id; } /** * Rename stored virtual block * * @param string $p_newname */ public function setName($p_newname) { $this->block->setDbName($p_newname); $this->block->setDbMtime(new DateTime("now", new DateTimeZone("UTC"))); $this->block->save($this->con); } /** * Get mnemonic block name * * @return string */ public function getName() { return $this->block->getDbName(); } public function setDescription($p_description) { $this->block->setDbDescription($p_description); $this->block->setDbMtime(new DateTime("now", new DateTimeZone("UTC"))); $this->block->save($this->con); } public function getDescription() { return $this->block->getDbDescription(); } public function getCreator() { return $this->block->getCcSubjs()->getDbLogin(); } public function getCreatorId() { return $this->block->getCcSubjs()->getDbId(); } public function setCreator($p_id) { $this->block->setDbCreatorId($p_id); $this->block->setDbMtime(new DateTime("now", new DateTimeZone("UTC"))); $this->block->save($this->con); } public function getLastModified($format = null) { return $this->block->getDbMtime($format); } public function getSize() { return $this->block->countCcBlockcontentss(); } /** * Get the entire block as a two dimentional array, sorted in order of play. * @param boolean $filterFiles if this is true, it will only return files that has * file_exists flag set to true * @return array */ public function getContents($filterFiles=false) { Logging::log("Getting contents for block {$this->id}"); $files = array(); $sql = <<<"EOT" SELECT pc.id as id, pc.position, pc.cliplength as length, pc.cuein, pc.cueout, pc.fadein, pc.fadeout, bl.type, f.length as orig_length, f.id as item_id, f.track_title, f.artist_name as creator, f.file_exists as exists, f.filepath as path FROM cc_blockcontents AS pc LEFT JOIN cc_files AS f ON pc.file_id=f.id LEFT JOIN cc_block AS bl ON pc.block_id = bl.id WHERE pc.block_id = {$this->id} ORDER BY pc.position; EOT; $con = Propel::getConnection(); $rows = $con->query($sql)->fetchAll(); $offset = 0; foreach ($rows as &$row) { $clipSec = Application_Common_DateHelper::playlistTimeToSeconds($row['length']); $offset += $clipSec; $offset_cliplength = Application_Common_DateHelper::secondsToPlaylistTime($offset); //format the length for UI. $formatter = new LengthFormatter($row['length']); $row['length'] = $formatter->format(); $formatter = new LengthFormatter($offset_cliplength); $row['offset'] = $formatter->format(); //format the fades in format 00(.000000) $fades = $this->getFadeInfo($row['position']); $row['fadein'] = $fades[0]; $row['fadeout'] = $fades[1]; //format original length $formatter = new LengthFormatter($row['orig_length']); $row['orig_length'] = $formatter->format(); } return $rows; } /** * The database stores fades in 00:00:00 Time format with optional millisecond resolution .000000 * but this isn't practical since fades shouldn't be very long usuall 1 second or less. This function * will normalize the fade so that it looks like 00.000000 to the user. **/ public function normalizeFade($fade) { //First get rid of the first six characters 00:00: which will be added back later for db update $fade = substr($fade, 6); //Second add .000000 if the fade does't have milliseconds format already $dbFadeStrPos = strpos( $fade, '.' ); if ( $dbFadeStrPos === False ) $fade .= '.000000'; else while( strlen( $fade ) < 9 ) $fade .= '0'; //done, just need to set back the formated values return $fade; } public function getUnformatedLength() { $this->block->reload(); if ($this->isStatic()){ $length = $this->block->getDbLength(); } else { $length = $this->getDynamicBlockLength(); } return $length; } public function getLength() { $this->block->reload(); $prepend = ""; if ($this->isStatic()){ $length = $this->block->getDbLength(); } else { $length = $this->getDynamicBlockLength(); if (!$this->hasItemLimit()) { $prepend = "~"; } } $formatter = new LengthFormatter($length); $length = $prepend.$formatter->format(); return $length; } public function getDynamicBlockLength() { list($value, $modifier) = $this->getLimitValueAndModifier(); if ($modifier == "items") { $length = $value." ".$modifier; } else { $hour = "00"; if ($modifier == "minutes") { if ($value >59) { $hour = intval($value/60); $value = $value%60; } } else if ($modifier == "hours") { $mins = $value * 60; if ($mins >59) { $hour = intval($mins/60); $hour = str_pad($hour, 2, "0", STR_PAD_LEFT); $value = $mins%60; } } $hour = str_pad($hour, 2, "0", STR_PAD_LEFT); $value = str_pad($value, 2, "0", STR_PAD_LEFT); $length = $hour.":".$value.":00"; } return $length; } public function getLimitValueAndModifier() { $result = CcBlockcriteriaQuery::create()->filterByDbBlockId($this->id) ->filterByDbCriteria('limit')->findOne(); $modifier = $result->getDbModifier(); $value = $result->getDbValue(); return array($value, $modifier); } // this function returns sum of all track length under this block. public function getStaticLength(){ $sql = "SELECT SUM(cliplength) as length FROM cc_blockcontents WHERE block_id={$this->id}"; $r = $this->con->query($sql); $result = $r->fetchAll(PDO::FETCH_NUM); return $result[0][0]; } private function insertBlockElement($info) { $row = new CcBlockcontents(); $row->setDbBlockId($this->id); $row->setDbFileId($info["id"]); $row->setDbPosition($info["pos"]); $row->setDbCliplength($info["cliplength"]); $row->setDbCuein($info["cuein"]); $row->setDbCueout($info["cueout"]); $row->setDbFadein($info["fadein"]); $row->setDbFadeout($info["fadeout"]); $row->save($this->con); // above save result update on cc_block table on length column. // but $this->block doesn't get updated automatically // so we need to manually grab it again from DB so it has updated values // It is something to do FORMAT_ON_DEMAND( Lazy Loading ) $this->block = CcBlockQuery::create()->findPK($this->id); } /* * */ private function buildEntry($p_item, $pos) { $file = CcFilesQuery::create()->findPK($p_item, $this->con); if (isset($file) && $file->getDbFileExists()) { $entry = $this->blockItem; $entry["id"] = $file->getDbId(); $entry["pos"] = $pos; $entry["cliplength"] = $file->getDbLength(); $entry["cueout"] = $file->getDbLength(); return $entry; } else { throw new Exception("trying to add a file that does not exist."); } } public function isStatic(){ if ($this->block->getDbType() == "static") { return true; } else { return false; } } /* * @param array $p_items * an array of audioclips to add to the block * @param int|null $p_afterItem * item which to add the new items after in the block, null if added to the end. * @param string (before|after) $addAfter * whether to add the clips before or after the selected item. */ public function addAudioClips($p_items, $p_afterItem=NULL, $addType = 'after') { $this->con->beginTransaction(); $contentsToUpdate = array(); try { if (is_numeric($p_afterItem)) { Logging::log("Finding block content item {$p_afterItem}"); $afterItem = CcBlockcontentsQuery::create()->findPK($p_afterItem); $index = $afterItem->getDbPosition(); Logging::log("index is {$index}"); $pos = ($addType == 'after') ? $index + 1 : $index; $contentsToUpdate = CcBlockcontentsQuery::create() ->filterByDbBlockId($this->id) ->filterByDbPosition($pos, Criteria::GREATER_EQUAL) ->orderByDbPosition() ->find($this->con); Logging::log("Adding to block"); Logging::log("at position {$pos}"); } else { //add to the end of the block if ($addType == 'after') { $pos = $this->getSize(); } //add to the beginning of the block. else { $pos = 0; $contentsToUpdate = CcBlockcontentsQuery::create() ->filterByDbBlockId($this->id) ->orderByDbPosition() ->find($this->con); } $contentsToUpdate = CcBlockcontentsQuery::create() ->filterByDbBlockId($this->id) ->filterByDbPosition($pos, Criteria::GREATER_EQUAL) ->orderByDbPosition() ->find($this->con); Logging::log("Adding to block"); Logging::log("at position {$pos}"); } foreach ($p_items as $ac) { Logging::log("Adding audio file {$ac}"); if (is_array($ac) && $ac[1] == 'audioclip') { $res = $this->insertBlockElement($this->buildEntry($ac[0], $pos)); $pos = $pos + 1; } elseif (!is_array($ac)) { $res = $this->insertBlockElement($this->buildEntry($ac, $pos)); $pos = $pos + 1; } } //reset the positions of the remaining items. for ($i = 0; $i < count($contentsToUpdate); $i++) { $contentsToUpdate[$i]->setDbPosition($pos); $contentsToUpdate[$i]->save($this->con); $pos = $pos + 1; } $this->block->setDbMtime(new DateTime("now", new DateTimeZone("UTC"))); $this->block->save($this->con); $this->con->commit(); } catch (Exception $e) { $this->con->rollback(); throw $e; } } /** * Move audioClip to the new position in the block * * @param array $p_items * array of unique ids of the selected items * @param int $p_afterItem * unique id of the item to move the clip after */ public function moveAudioClips($p_items, $p_afterItem=NULL) { $this->con->beginTransaction(); try { $contentsToMove = CcBlockcontentsQuery::create() ->filterByDbId($p_items, Criteria::IN) ->orderByDbPosition() ->find($this->con); $otherContent = CcBlockcontentsQuery::create() ->filterByDbId($p_items, Criteria::NOT_IN) ->filterByDbBlockId($this->id) ->orderByDbPosition() ->find($this->con); $pos = 0; //moving items to beginning of the block. if (is_null($p_afterItem)) { Logging::log("moving items to beginning of block"); foreach ($contentsToMove as $item) { Logging::log("item {$item->getDbId()} to pos {$pos}"); $item->setDbPosition($pos); $item->save($this->con); $pos = $pos + 1; } foreach ($otherContent as $item) { Logging::log("item {$item->getDbId()} to pos {$pos}"); $item->setDbPosition($pos); $item->save($this->con); $pos = $pos + 1; } } else { Logging::log("moving items after {$p_afterItem}"); foreach ($otherContent as $item) { Logging::log("item {$item->getDbId()} to pos {$pos}"); $item->setDbPosition($pos); $item->save($this->con); $pos = $pos + 1; if ($item->getDbId() == $p_afterItem) { foreach ($contentsToMove as $move) { Logging::log("item {$move->getDbId()} to pos {$pos}"); $move->setDbPosition($pos); $move->save($this->con); $pos = $pos + 1; } } } } $this->con->commit(); } catch (Exception $e) { $this->con->rollback(); throw $e; } $this->block = CcBlockQuery::create()->findPK($this->id); $this->block->setDbMtime(new DateTime("now", new DateTimeZone("UTC"))); $this->block->save($this->con); } /** * Remove audioClip from block * * @param array $p_items * array of unique item ids to remove from the block.. */ public function delAudioClips($p_items) { $this->con->beginTransaction(); try { CcBlockcontentsQuery::create() ->findPKs($p_items) ->delete($this->con); $contents = CcBlockcontentsQuery::create() ->filterByDbBlockId($this->id) ->orderByDbPosition() ->find($this->con); //reset the positions of the remaining items. for ($i = 0; $i < count($contents); $i++) { $contents[$i]->setDbPosition($i); $contents[$i]->save($this->con); } $this->block->setDbMtime(new DateTime("now", new DateTimeZone("UTC"))); $this->block->save($this->con); $this->con->commit(); } catch (Exception $e) { $this->con->rollback(); throw $e; } } public function getFadeInfo($pos) { Logging::log("Getting fade info for pos {$pos}"); $row = CcBlockcontentsQuery::create() ->joinWith(CcFilesPeer::OM_CLASS) ->filterByDbBlockId($this->id) ->filterByDbPosition($pos) ->findOne(); #Propel returns values in form 00.000000 format which is for only seconds. $fadeIn = $row->getDbFadein(); $fadeOut = $row->getDbFadeout(); return array($fadeIn, $fadeOut); } /** * Change fadeIn and fadeOut values for block Element * * @param int $pos * position of audioclip in block * @param string $fadeIn * new value in ss.ssssss or extent format * @param string $fadeOut * new value in ss.ssssss or extent format * @return boolean */ public function changeFadeInfo($id, $fadeIn, $fadeOut) { //See issue CC-2065, pad the fadeIn and fadeOut so that it is TIME compatable with the DB schema //For the top level PlayList either fadeIn or fadeOut will sometimes be Null so need a gaurd against //setting it to nonNull for checks down below $fadeIn = $fadeIn?'00:00:'.$fadeIn:$fadeIn; $fadeOut = $fadeOut?'00:00:'.$fadeOut:$fadeOut; $this->con->beginTransaction(); $errArray= array(); try { $row = CcBlockcontentsQuery::create()->findPK($id); if (is_null($row)) { throw new Exception("Block item does not exist."); } $clipLength = $row->getDbCliplength(); if (!is_null($fadeIn)) { $sql = "SELECT INTERVAL '{$fadeIn}' > INTERVAL '{$clipLength}'"; $r = $this->con->query($sql); if ($r->fetchColumn(0)) { //"Fade In can't be larger than overall playlength."; $fadeIn = $clipLength; } $row->setDbFadein($fadeIn); } if (!is_null($fadeOut)) { $sql = "SELECT INTERVAL '{$fadeOut}' > INTERVAL '{$clipLength}'"; $r = $this->con->query($sql); if ($r->fetchColumn(0)) { //Fade Out can't be larger than overall playlength."; $fadeOut = $clipLength; } $row->setDbFadeout($fadeOut); } $row->save($this->con); $this->block->setDbMtime(new DateTime("now", new DateTimeZone("UTC"))); $this->block->save($this->con); $this->con->commit(); } catch (Exception $e) { $this->con->rollback(); throw $e; } return array("fadeIn" => $fadeIn, "fadeOut" => $fadeOut); } public function setfades($fadein, $fadeout) { if (isset($fadein)) { Logging::log("Setting block fade in {$fadein}"); $row = CcBlockcontentsQuery::create() ->filterByDbBlockId($this->id) ->filterByDbPosition(0) ->findOne($this->con); $this->changeFadeInfo($row->getDbId(), $fadein, null); } if (isset($fadeout)) { Logging::log("Setting block fade out {$fadeout}"); $row = CcBlockcontentsQuery::create() ->filterByDbBlockId($this->id) ->filterByDbPosition($this->getSize()-1) ->findOne($this->con); $this->changeFadeInfo($row->getDbId(), null, $fadeout); } } /** * Change cueIn/cueOut values for block element * * @param int $pos * position of audioclip in block * @param string $cueIn * new value in ss.ssssss or extent format * @param string $cueOut * new value in ss.ssssss or extent format * @return boolean or pear error object */ public function changeClipLength($id, $cueIn, $cueOut) { $this->con->beginTransaction(); $errArray= array(); try { if (is_null($cueIn) && is_null($cueOut)) { $errArray["error"] = "Cue in and cue out are null."; return $errArray; } $row = CcBlockcontentsQuery::create() ->joinWith(CcFilesPeer::OM_CLASS) ->filterByPrimaryKey($id) ->findOne($this->con); if (is_null($row)) { throw new Exception("Block item does not exist."); } $oldCueIn = $row->getDBCuein(); $oldCueOut = $row->getDbCueout(); $fadeIn = $row->getDbFadein(); $fadeOut = $row->getDbFadeout(); $file = $row->getCcFiles($this->con); $origLength = $file->getDbLength(); if (!is_null($cueIn) && !is_null($cueOut)) { if ($cueOut === "") { $cueOut = $origLength; } $sql = "SELECT INTERVAL '{$cueIn}' > INTERVAL '{$cueOut}'"; $r = $this->con->query($sql); if ($r->fetchColumn(0)) { $errArray["error"] = "Can't set cue in to be larger than cue out."; return $errArray; } $sql = "SELECT INTERVAL '{$cueOut}' > INTERVAL '{$origLength}'"; $r = $this->con->query($sql); if ($r->fetchColumn(0)) { $errArray["error"] = "Can't set cue out to be greater than file length."; return $errArray; } $sql = "SELECT INTERVAL '{$cueOut}' - INTERVAL '{$cueIn}'"; $r = $this->con->query($sql); $cliplength = $r->fetchColumn(0); $row->setDbCuein($cueIn); $row->setDbCueout($cueOut); $row->setDBCliplength($cliplength); } elseif (!is_null($cueIn)) { $sql = "SELECT INTERVAL '{$cueIn}' > INTERVAL '{$oldCueOut}'"; $r = $this->con->query($sql); if ($r->fetchColumn(0)) { $errArray["error"] = "Can't set cue in to be larger than cue out."; return $errArray; } $sql = "SELECT INTERVAL '{$oldCueOut}' - INTERVAL '{$cueIn}'"; $r = $this->con->query($sql); $cliplength = $r->fetchColumn(0); $row->setDbCuein($cueIn); $row->setDBCliplength($cliplength); } elseif (!is_null($cueOut)) { if ($cueOut === "") { $cueOut = $origLength; } $sql = "SELECT INTERVAL '{$cueOut}' < INTERVAL '{$oldCueIn}'"; $r = $this->con->query($sql); if ($r->fetchColumn(0)) { $errArray["error"] = "Can't set cue out to be smaller than cue in."; return $errArray; } $sql = "SELECT INTERVAL '{$cueOut}' > INTERVAL '{$origLength}'"; $r = $this->con->query($sql); if ($r->fetchColumn(0)) { $errArray["error"] = "Can't set cue out to be greater than file length."; return $errArray; } $sql = "SELECT INTERVAL '{$cueOut}' - INTERVAL '{$oldCueIn}'"; $r = $this->con->query($sql); $cliplength = $r->fetchColumn(0); $row->setDbCueout($cueOut); $row->setDBCliplength($cliplength); } $cliplength = $row->getDbCliplength(); $sql = "SELECT INTERVAL '{$fadeIn}' > INTERVAL '{$cliplength}'"; $r = $this->con->query($sql); if ($r->fetchColumn(0)) { $fadeIn = $cliplength; $row->setDbFadein($fadeIn); } $sql = "SELECT INTERVAL '{$fadeOut}' > INTERVAL '{$cliplength}'"; $r = $this->con->query($sql); if ($r->fetchColumn(0)) { $fadeOut = $cliplength; $row->setDbFadein($fadeOut); } $row->save($this->con); $this->block->setDbMtime(new DateTime("now", new DateTimeZone("UTC"))); $this->block->save($this->con); $this->con->commit(); } catch (Exception $e) { $this->con->rollback(); throw $e; } return array("cliplength"=> $cliplength, "cueIn"=> $cueIn, "cueOut"=> $cueOut, "length"=> $this->getUnformatedLength(), "fadeIn"=> $fadeIn, "fadeOut"=> $fadeOut); } public function getAllPLMetaData() { $categories = $this->categories; $md = array(); foreach ($categories as $key => $val) { $method = 'get' . $val; $md[$key] = $this->$method(); } return $md; } public function getMetaData($category) { $cat = $this->categories[$category]; $method = 'get' . $cat; return $this->$method(); } public function setMetaData($category, $value) { $cat = $this->categories[$category]; $method = 'set' . $cat; $this->$method($value); } public static function getBlockCount() { global $CC_CONFIG; $con = Propel::getConnection(); $sql = 'SELECT count(*) as cnt FROM '.$CC_CONFIG["playListTable"]; return $con->query($sql)->fetchColumn(0); } /** * Delete the file from all blocks. * @param string $p_fileId */ public static function DeleteFileFromAllBlocks($p_fileId) { CcBlockcontentsQuery::create()->filterByDbFileId($p_fileId)->delete(); } /** * Delete blocks that match the ids.. * @param array $p_ids */ public static function deleteBlocks($p_ids, $p_userId) { $leftOver = self::blocksNotOwnedByUser($p_ids, $p_userId); if (count($leftOver) == 0) { CcBlockQuery::create()->findPKs($p_ids)->delete(); } else { throw new BlockNoPermissionException; } } // This function returns that are not owen by $p_user_id among $p_ids private static function blocksNotOwnedByUser($p_ids, $p_userId) { $ownedByUser = CcBlockQuery::create()->filterByDbCreatorId($p_userId)->find()->getData(); $selectedPls = $p_ids; $ownedPls = array(); foreach ($ownedByUser as $pl) { if (in_array($pl->getDbId(), $selectedPls)) { $ownedPls[] = $pl->getDbId(); } } $leftOvers = array_diff($selectedPls, $ownedPls); return $leftOvers; } /** * Delete all files from block */ public function deleteAllFilesFromBlock() { CcBlockcontentsQuery::create()->findByDbBlockId($this->id)->delete(); $this->block->reload(); } // smart block functions start public function shuffleSmartBlock(){ // if it here that means it's static pl $this->saveType("static"); $contents = CcBlockcontentsQuery::create() ->filterByDbBlockId($this->id) ->orderByDbPosition() ->find(); $shuffledPos = range(0, count($contents)-1); shuffle($shuffledPos); foreach ($contents as $item) { $item->setDbPosition(array_shift($shuffledPos)); $item->save(); } return array("result"=>0); } public function saveType($p_blockType) { // saving dynamic/static flag CcBlockQuery::create()->findPk($this->id)->setDbType($p_blockType)->save(); } public function setLength($value){ $this->block->setDbLength($value); $this->block->save($this->con); $this->updateBlockLengthInAllPlaylist(); } /** * Saves smart block criteria * @param array $p_criteria */ public function saveSmartBlockCriteria($p_criteria) { $data = $this->organizeSmartPlyalistCriteria($p_criteria); // saving dynamic/static flag $blockType = $data['etc']['sp_type'] == 0 ? 'static':'dynamic'; $this->saveType($blockType); $this->storeCriteriaIntoDb($data); //get number of files that meet the criteria $files = $this->getListofFilesMeetCriteria(); // if the block is dynamic, put null to the length // as it cannot be calculated if ($blockType == 'dynamic') { if ($this->hasItemLimit()) { $this->setLength(null); } else { $this->setLength($this->getDynamicBlockLength()); } } else { $length = $this->getStaticLength(); if (!$length) { $length = "00:00:00"; } $this->setLength($length); } $this->updateBlockLengthInAllPlaylist(); } public function hasItemLimit() { list($value, $modifier) = $this->getLimitValueAndModifier(); if ($modifier == 'items') { return true; } else { return false; } } public function storeCriteriaIntoDb($p_criteriaData){ // delete criteria under $p_blockId CcBlockcriteriaQuery::create()->findByDbBlockId($this->id)->delete(); Logging::log($p_criteriaData); //insert modifier rows if (isset($p_criteriaData['criteria'])) { $critKeys = array_keys($p_criteriaData['criteria']); for ($i = 0; $i < count($critKeys); $i++) { foreach( $p_criteriaData['criteria'][$critKeys[$i]] as $d){ $qry = new CcBlockcriteria(); $qry->setDbCriteria($d['sp_criteria_field']) ->setDbModifier($d['sp_criteria_modifier']) ->setDbValue($d['sp_criteria_value']) ->setDbBlockId($this->id); if (isset($d['sp_criteria_extra'])) { $qry->setDbExtra($d['sp_criteria_extra']); } $qry->save(); } } } // insert limit info $qry = new CcBlockcriteria(); $qry->setDbCriteria("limit") ->setDbModifier($p_criteriaData['etc']['sp_limit_options']) ->setDbValue($p_criteriaData['etc']['sp_limit_value']) ->setDbBlockId($this->id) ->save(); } /** * generate list of tracks. This function saves creiteria and generate * tracks. * @param array $p_criteria */ public function generateSmartBlock($p_criteria, $returnList=false) { $this->saveSmartBlockCriteria($p_criteria); $insertList = $this->getListOfFilesUnderLimit(); $this->deleteAllFilesFromBlock(); $this->addAudioClips(array_keys($insertList)); // update length in playlist contents. $this->updateBlockLengthInAllPlaylist(); return array("result"=>0); } public function updateBlockLengthInAllPlaylist() { $blocks = CcPlaylistcontentsQuery::create()->filterByDbBlockId($this->id)->find(); $blocks->getFirst(); $iterator = $blocks->getIterator(); while ($iterator->valid()) { $length = $this->getUnformatedLength(); if (!preg_match("/^[0-9]{2}:[0-9]{2}:[0-9]{2}/", $length)) { $iterator->current()->setDbClipLength(null); } else { $iterator->current()->setDbClipLength($length); } $iterator->current()->save(); $iterator->next(); } } public function getListOfFilesUnderLimit() { $info = $this->getListofFilesMeetCriteria(); $files = $info['files']; $limit = $info['limit']; $insertList = array(); $totalTime = 0; $totalItems = 0; // this moves the pointer to the first element in the collection $files->getFirst(); $iterator = $files->getIterator(); while ($iterator->valid() && $totalTime < $limit['time']) { $id = $iterator->current()->getDbId(); $length = Application_Common_DateHelper::calculateLengthInSeconds($iterator->current()->getDbLength()); $insertList[$id] = $length; $totalTime += $length; $totalItems++; if ((!is_null($limit['items']) && $limit['items'] == count($insertList)) || $totalItems > 500) { break; } $iterator->next(); } return $insertList; } public function getCriteria() { $criteriaOptions = array( 0 => "Select criteria", "album_title" => "Album", "bit_rate" => "Bit Rate", "bpm" => "Bpm", "comments" => "Comments", "composer" => "Composer", "conductor" => "Conductor", "artist_name" => "Creator", "disc_number" => "Disc Number", "genre" => "Genre", "isrc_number" => "ISRC", "label" => "Label", "language" => "Language", "mtime" => "Last Modified", "lptime" => "Last Played", "length" => "Length", "lyricist" => "Lyricist", "mood" => "Mood", "name" => "Name", "orchestra" => "Orchestra", "rating" => "Rating", "sample_rate" => "Sample Rate", "track_title" => "Title", "track_number" => "Track Number", "utime" => "Uploaded", "year" => "Year" ); // Load criteria from db $out = CcBlockcriteriaQuery::create()->orderByDbCriteria()->findByDbBlockId($this->id); $storedCrit = array(); foreach ($out as $crit) { $criteria = $crit->getDbCriteria(); $modifier = $crit->getDbModifier(); $value = $crit->getDbValue(); $extra = $crit->getDbExtra(); if ($criteria == "limit") { $storedCrit["limit"] = array("value"=>$value, "modifier"=>$modifier); } else { $storedCrit["crit"][$criteria][] = array("criteria"=>$criteria, "value"=>$value, "modifier"=>$modifier, "extra"=>$extra, "display_name"=>$criteriaOptions[$criteria]); } } return $storedCrit; } // this function return list of propel object public function getListofFilesMeetCriteria() { $storedCrit = $this->getCriteria(); $qry = CcFilesQuery::create(); if (isset($storedCrit["crit"])) { foreach ($storedCrit["crit"] as $crit) { $i = 0; foreach ($crit as $criteria) { $spCriteriaPhpName = self::$criteria2PeerMap[$criteria['criteria']]; $spCriteria = $criteria['criteria']; $spCriteriaModifier = $criteria['modifier']; $column = CcFilesPeer::getTableMap()->getColumnByPhpName(self::$criteria2PeerMap[$spCriteria]); // if the column is timestamp, convert it into UTC if ($column->getType() == PropelColumnTypes::TIMESTAMP) { $spCriteriaValue = Application_Common_DateHelper::ConvertToUtcDateTimeString($criteria['value']); /* Check if only a date was supplied and trim * the time after it is converted to UTC time */ if (strlen($criteria['value']) <= 10) { //extract date only from timestamp in db $spCriteria = 'date('.$spCriteria.')'; $spCriteriaValue = substr($spCriteriaValue, 0, 10); } } else if($spCriteria == "bit_rate") { // multiply 1000 because we store only number value // e.g 192kps is stored as 192000 $spCriteriaValue = $criteria['value']*1000; } else { $spCriteriaValue = addslashes($criteria['value']); } if ($spCriteriaModifier == "starts with") { $spCriteriaValue = "$spCriteriaValue%"; } else if ($spCriteriaModifier == "ends with") { $spCriteriaValue = "%$spCriteriaValue"; } else if ($spCriteriaModifier == "contains" || $spCriteriaModifier == "does not contain") { $spCriteriaValue = "%$spCriteriaValue%"; } else if ($spCriteriaModifier == "is in the range") { $spCriteriaValue = "$spCriteria > '$spCriteriaValue' AND $spCriteria <= '$criteria[extra]'"; } $spCriteriaModifier = self::$modifier2CriteriaMap[$spCriteriaModifier]; try{ if ($i > 0) { $qry->addOr($spCriteria, $spCriteriaValue, $spCriteriaModifier); } else { $qry->add($spCriteria, $spCriteriaValue, $spCriteriaModifier); } }catch (Exception $e){ Logging::log($e); } $i++; } } $qry->addAscendingOrderByColumn('random()'); } // construct limit restriction $limits = array(); if (isset($storedCrit['limit'])) { if ($storedCrit['limit']['modifier'] == "items") { $limits['time'] = 1440 * 60; $limits['items'] = $storedCrit['limit']['value']; } else { $limits['time'] = $storedCrit['limit']['modifier'] == "hours" ? intval(floatval($storedCrit['limit']['value']) * 60 * 60) : intval($storedCrit['limit']['value'] * 60); $limits['items'] = null; } } try{ $out = $qry->setFormatter(ModelCriteria::FORMAT_ON_DEMAND)->find(); return array("files"=>$out, "limit"=>$limits, "count"=>$out->count()); }catch(Exception $e){ Logging::log($e); } } public static function organizeSmartPlyalistCriteria($p_criteria) { $fieldNames = array('sp_criteria_field', 'sp_criteria_modifier', 'sp_criteria_value', 'sp_criteria_extra'); $output = array(); foreach ($p_criteria as $ele) { $index = strrpos($ele['name'], '_'); /* Strip field name of modifier index * Ex: sp_criteria_field_0_0 -> sp_criteria_field_0 */ $fieldName = substr($ele['name'], 0, $index); // Get criteria row index. $tempName = $ele['name']; // Get the last digit in the field name preg_match('/^\D*(?=\d)/', $tempName, $r); if (isset($r[0])) { $critIndexPos = strlen($r[0]); $critIndex = $tempName[$critIndexPos]; } $lastChar = substr($ele['name'], -1); // If lastChar is an integer we should strip it off if (!preg_match("/^[a-zA-Z]$/", $lastChar)) { /* Strip field name of criteria index * Ex: sp_criteria_field_0 -> sp_criteria_field * We do this to check if the field name is a criteria * or the block type */ $n = strrpos($fieldName, '_'); $fieldName = substr($fieldName, 0, $n); } if (in_array($fieldName, $fieldNames)) { $rowNum = intval(substr($ele['name'], $index+1)); $output['criteria'][$critIndex][$lastChar][$fieldName] = trim($ele['value']); }else{ $output['etc'][$ele['name']] = $ele['value']; } } return $output; } // smart block functions end } class BlockNotFoundException extends Exception {} class BlockNoPermissionException extends Exception {} class BlockOutDatedException extends Exception {} class BlockDyanmicException extends Exception {}