CC-84: Smart Playlists

- introducing smart-block
This commit is contained in:
James 2012-07-25 12:44:37 -04:00
parent 722e470f6f
commit 1f3cbd8aba
56 changed files with 12502 additions and 734 deletions

View file

@ -45,48 +45,6 @@ class Application_Model_Playlist
"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",
"radio_station_name" => "DbRadioStation",
"rating" => "DbRating",
"sample_rate" => "DbSampleRate",
"track_title" => "DbTrackTitle",
"track_num" => "DbTrackNum",
"year" => "DbYear"
);
public function __construct($id=null, $con=null)
{
@ -214,9 +172,9 @@ class Application_Model_Playlist
$files[$i] = $row->toArray(BasePeer::TYPE_FIELDNAME, true, true);
$clipSec = Application_Model_Playlist::playlistTimeToSeconds($files[$i]['cliplength']);
$clipSec = Application_Common_DateHelper::playlistTimeToSeconds($files[$i]['cliplength']);
$offset += $clipSec;
$offset_cliplength = Application_Model_Playlist::secondsToPlaylistTime($offset);
$offset_cliplength = Application_Common_DateHelper::secondsToPlaylistTime($offset);
//format the length for UI.
$formatter = new LengthFormatter($files[$i]['cliplength']);
@ -297,14 +255,6 @@ class Application_Model_Playlist
throw new Exception("trying to add a file that does not exist.");
}
}
public function isStatic(){
if ($this->pl->getDbType() == "static") {
return true;
} else {
return false;
}
}
/*
* @param array $p_items
@ -781,58 +731,6 @@ class Application_Model_Playlist
$this->$method($value);
}
/**
* This function is used for calculations! Don't modify for display purposes!
*
* Convert playlist time value to float seconds
*
* @param string $plt
* playlist interval value (HH:mm:ss.dddddd)
* @return int
* seconds
*/
public static function playlistTimeToSeconds($plt)
{
$arr = preg_split('/:/', $plt);
if (isset($arr[2])) {
return (intval($arr[0])*60 + intval($arr[1]))*60 + floatval($arr[2]);
}
if (isset($arr[1])) {
return intval($arr[0])*60 + floatval($arr[1]);
}
return floatval($arr[0]);
}
/**
* This function is used for calculations! Don't modify for display purposes!
*
* Convert float seconds value to playlist time format
*
* @param float $seconds
* @return string
* interval in playlist time format (HH:mm:ss.d)
*/
public static function secondsToPlaylistTime($p_seconds)
{
$info = explode('.', $p_seconds);
$seconds = $info[0];
if (!isset($info[1])) {
$milliStr = 0;
} else {
$milliStr = $info[1];
}
$hours = floor($seconds / 3600);
$seconds -= $hours * 3600;
$minutes = floor($seconds / 60);
$seconds -= $minutes * 60;
$res = sprintf("%02d:%02d:%02d.%s", $hours, $minutes, $seconds, $milliStr);
return $res;
}
public static function getPlaylistCount()
{
global $CC_CONFIG;
@ -888,291 +786,6 @@ class Application_Model_Playlist
{
CcPlaylistcontentsQuery::create()->findByDbPlaylistId($this->id)->delete();
}
// smart playlist functions start
public function shuffleSmartPlaylist(){
// if it here that means it's static pl
$this->saveType("static");
$contents = CcPlaylistcontentsQuery::create()
->filterByDbPlaylistId($this->id)
->orderByDbPosition()
->find();
$shuffledPos = range(0, count($contents)-1);
shuffle($shuffledPos);
$temp = new CcPlaylist();
foreach ($contents as $item) {
$item->setDbPosition(array_shift($shuffledPos));
$item->save();
}
return array("result"=>0);
}
public function saveType($p_playlistType)
{
// saving dynamic/static flag
CcPlaylistQuery::create()->findPk($this->id)->setDbType($p_playlistType)->save();
}
/**
* Saves smart playlist criteria
* @param array $p_criteria
*/
public function saveSmartPlaylistCriteria($p_criteria)
{
$data = $this->organizeSmartPlyalistCriteria($p_criteria);
// things we need to check
// 1. limit value shouldn't be empty and has upperbound of 24 hrs
// 2. sp_criteria or sp_criteria_modifier shouldn't be 0
// 3. validate formate according to DB column type
$multiplier = 1;
$result = 0;
$errors = array();
$error = array();
// saving dynamic/static flag
$playlistType = $data['etc']['sp_type'] == 0 ? 'static':'dynamic';
$this->saveType($playlistType);
// validation start
if ($data['etc']['sp_limit_options'] == 'hours') {
$multiplier = 60;
}
if ($data['etc']['sp_limit_options'] == 'hours' || $data['etc']['sp_limit_options'] == 'mins') {
if ($data['etc']['sp_limit_value'] == "" || floatval($data['etc']['sp_limit_value']) <= 0) {
$error[] = "Limit cannot be empty or smaller than 0";
} else {
$mins = $data['etc']['sp_limit_value'] * $multiplier;
if ($mins > 14400) {
$error[] = "Limit cannot be more than 24 hrs";
}
}
} else {
if ($data['etc']['sp_limit_value'] == "" || floatval($data['etc']['sp_limit_value']) <= 0) {
$error[] = "Limit cannot be empty or smaller than 0";
} else if (floatval($data['etc']['sp_limit_value']) < 1) {
$error[] = "The value should be an integer";
}
}
if (count($error) > 0){
$errors[] = array("element"=>"sp_limit_value", "msg"=>$error);
}
foreach ($data['criteria'] as $key=>$d){
$error = array();
$column = CcFilesPeer::getTableMap()->getColumnByPhpName(self::$criteria2PeerMap[$d['sp_criteria_field']]);
// check for not selected select box
if ($d['sp_criteria_field'] == "0" || $d['sp_criteria_modifier'] == "0"){
$error[] = "You must select Criteria and Modifier";
} else {
// validation on type of column
if ($d['sp_criteria_field'] == 'length') {
if (!preg_match("/(\d{2}):(\d{2}):(\d{2})/", $d['sp_criteria_value'])) {
$error[] = "'Length' should be in '00:00:00' format";
}
} else if ($column->getType() == PropelColumnTypes::TIMESTAMP) {
if (!preg_match("/(\d{4})-(\d{2})-(\d{2})/", $d['sp_criteria_value'])) {
$error[] = "The value should be in timestamp format(eg. 0000-00-00 or 00-00-00 00:00:00";
} else if (!Application_Common_DateHelper::checkDateTimeRangeForSQL($d['sp_criteria_value'])) {
// check for if it is in valid range( 1753-01-01 ~ 12/31/9999 )
$error[] = "$d[sp_criteria_value] is not a valid date/time string";
}
if (isset($d['sp_criteria_extra'])) {
if (!preg_match("/(\d{4})-(\d{2})-(\d{2})/", $d['sp_criteria_extra'])) {
$error[] = "The value should be in timestamp format(eg. 0000-00-00 or 00-00-00 00:00:00";
} else if (!Application_Common_DateHelper::checkDateTimeRangeForSQL($d['sp_criteria_extra'])) {
// check for if it is in valid range( 1753-01-01 ~ 12/31/9999 )
$error[] = "$d[sp_criteria_extra] is not a valid date/time string";
}
}
} else if ($column->getType() == PropelColumnTypes::INTEGER) {
if (!is_numeric($d['sp_criteria_value'])) {
$error[] = "The value has to be numeric";
}
// length check
if (intval($d['sp_criteria_value']) >= pow(2,31)) {
$error[] = "The value should be less then 2147483648";
}
} else if ($column->getType() == PropelColumnTypes::VARCHAR) {
if (strlen($d['sp_criteria_value']) > $column->getSize()) {
$error[] = "The value should be less ".$column->getSize()." characters";
}
}
}
if ($d['sp_criteria_value'] == "") {
$error[] = "Value cannot be empty";
}
if(count($error) > 0){
$errors[] = array("element"=>"sp_criteria_field_".$key, "msg"=>$error);
}
}
$result = count($errors) > 0 ? 1 :0;
if ($result == 0) {
$this->storeCriteriaIntoDb($data);
}
//get number of files that meet the criteria
$files = $this->getListofFilesMeetCriteria();
return array("result"=>$result, "errors"=>$errors, "poolCount"=>$files["count"]);
}
public function storeCriteriaIntoDb($p_criteriaData){
// delete criteria under $p_playlistId
CcPlaylistcriteriaQuery::create()->findByDbPlaylistId($this->id)->delete();
foreach( $p_criteriaData['criteria'] as $d){
$qry = new CcPlaylistcriteria();
$qry->setDbCriteria($d['sp_criteria_field'])
->setDbModifier($d['sp_criteria_modifier'])
->setDbValue($d['sp_criteria_value'])
->setDbPlaylistId($this->id);
if (isset($d['sp_criteria_extra'])) {
$qry->setDbExtra($d['sp_criteria_extra']);
}
$qry->save();
}
// insert limit info
$qry = new CcPlaylistcriteria();
$qry->setDbCriteria("limit")
->setDbModifier($p_criteriaData['etc']['sp_limit_options'])
->setDbValue($p_criteriaData['etc']['sp_limit_value'])
->setDbPlaylistId($this->id)
->save();
}
/**
* generate list of tracks. This function saves creiteria and generate
* tracks.
* @param array $p_criteria
*/
public function generateSmartPlaylist($p_criteria, $returnList=false)
{
$result = $this->saveSmartPlaylistCriteria($p_criteria);
if ($result['result'] != 0) {
return $result;
} else {
$insertList = $this->getListOfFilesUnderLimit();
$this->deleteAllFilesFromPlaylist();
$this->addAudioClips(array_keys($insertList));
return array("result"=>0);
}
}
public function getListOfFilesUnderLimit()
{
$info = $this->getListofFilesMeetCriteria();
$files = $info['files'];
$limit = $info['limit'];
$insertList = array();
$totalTime = 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;
if ( !is_null($limit['items']) && $limit['items'] == count($insertList)) {
break;
}
$iterator->next();
}
return $insertList;
}
// this function return list of propel object
public function getListofFilesMeetCriteria()
{
$out = CcPlaylistcriteriaQuery::create()->findByDbPlaylistId($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"][] = array("criteria"=>$criteria, "value"=>$value, "modifier"=>$modifier, "extra"=>$extra);
}
}
$qry = CcFilesQuery::create();
if (isset($storedCrit["crit"])) {
foreach ($storedCrit["crit"] as $criteria) {
$spCriteriaPhpName = self::$criteria2PeerMap[$criteria['criteria']];
$spCriteria = $criteria['criteria'];
$spCriteriaModifier = $criteria['modifier'];
$spCriteriaValue = $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{
$qry->filterBy($spCriteriaPhpName, $spCriteriaValue, $spCriteriaModifier);
$qry->addAscendingOrderByColumn('random()');
}catch (Exception $e){
Logging::log($e);
}
}
}
// 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($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);
}
}
private 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'], '_');
$fieldName = substr($ele['name'], 0, $index);
if (in_array($fieldName, $fieldNames)) {
$rowNum = intval(substr($ele['name'], $index+1));
$output['criteria'][$rowNum][$fieldName] = trim($ele['value']);
}else{
$output['etc'][$ele['name']] = $ele['value'];
}
}
return $output;
}
// smart playlist functions end
} // class Playlist