Block.php 56 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634
  1. <?php
  2. require_once 'formatters/LengthFormatter.php';
  3. /**
  4. *
  5. * @package Airtime
  6. * @copyright 2010 Sourcefabric O.P.S.
  7. * @license http://www.gnu.org/licenses/gpl.txt
  8. */
  9. class Application_Model_Block implements Application_Model_LibraryEditable
  10. {
  11. /**
  12. * propel connection object.
  13. */
  14. private $con;
  15. /**
  16. * unique id for the block.
  17. */
  18. private $id;
  19. private $block;
  20. /**
  21. * info needed to insert a new block element.
  22. */
  23. private $blockItem = array(
  24. "id" => "",
  25. "pos" => "",
  26. "cliplength" => "",
  27. "cuein" => "00:00:00",
  28. "cueout" => "00:00:00",
  29. "fadein" => "0.0",
  30. "fadeout" => "0.0",
  31. "crossfadeDuration" => 0
  32. );
  33. //using propel's phpNames.
  34. private $categories = array(
  35. "dc:title" => "Name",
  36. "dc:creator" => "Creator",
  37. "dc:description" => "Description",
  38. "dcterms:extent" => "Length"
  39. );
  40. private static $modifier2CriteriaMap = array(
  41. "contains" => Criteria::ILIKE,
  42. "does not contain" => Criteria::NOT_ILIKE,
  43. "is" => Criteria::EQUAL,
  44. "is not" => Criteria::NOT_EQUAL,
  45. "starts with" => Criteria::ILIKE,
  46. "ends with" => Criteria::ILIKE,
  47. "is greater than" => Criteria::GREATER_THAN,
  48. "is less than" => Criteria::LESS_THAN,
  49. "is in the range" => Criteria::CUSTOM);
  50. private static $criteria2PeerMap = array(
  51. 0 => "Select criteria",
  52. "album_title" => "DbAlbumTitle",
  53. "artist_name" => "DbArtistName",
  54. "bit_rate" => "DbBitRate",
  55. "bpm" => "DbBpm",
  56. "composer" => "DbComposer",
  57. "conductor" => "DbConductor",
  58. "copyright" => "DbCopyright",
  59. "cuein" => "DbCuein",
  60. "cueout" => "DbCueout",
  61. "encoded_by" => "DbEncodedBy",
  62. "utime" => "DbUtime",
  63. "mtime" => "DbMtime",
  64. "lptime" => "DbLPtime",
  65. "genre" => "DbGenre",
  66. "info_url" => "DbInfoUrl",
  67. "isrc_number" => "DbIsrcNumber",
  68. "label" => "DbLabel",
  69. "language" => "DbLanguage",
  70. "length" => "DbLength",
  71. "mime" => "DbMime",
  72. "mood" => "DbMood",
  73. "owner_id" => "DbOwnerId",
  74. "replay_gain" => "DbReplayGain",
  75. "sample_rate" => "DbSampleRate",
  76. "track_title" => "DbTrackTitle",
  77. "track_number" => "DbTrackNumber",
  78. "year" => "DbYear"
  79. );
  80. public function __construct($id=null, $con=null)
  81. {
  82. if (isset($id)) {
  83. $this->block = CcBlockQuery::create()->findPk($id);
  84. if (is_null($this->block)) {
  85. throw new BlockNotFoundException();
  86. }
  87. } else {
  88. $this->block = new CcBlock();
  89. $this->block->setDbUTime(new DateTime("now", new DateTimeZone("UTC")));
  90. $this->block->save();
  91. }
  92. $this->blockItem["fadein"] = Application_Model_Preference::GetDefaultFadeIn();
  93. $this->blockItem["fadeout"] = Application_Model_Preference::GetDefaultFadeOut();
  94. $this->blockItem["crossfadeDuration"] = Application_Model_Preference::GetDefaultCrossfadeDuration();
  95. $this->con = isset($con) ? $con : Propel::getConnection(CcBlockPeer::DATABASE_NAME);
  96. $this->id = $this->block->getDbId();
  97. }
  98. /**
  99. * Return local ID of virtual file.
  100. *
  101. * @return int
  102. */
  103. public function getId()
  104. {
  105. return $this->id;
  106. }
  107. /**
  108. * Rename stored virtual block
  109. *
  110. * @param string $p_newname
  111. */
  112. public function setName($p_newname)
  113. {
  114. $this->block->setDbName($p_newname);
  115. $this->block->setDbMtime(new DateTime("now", new DateTimeZone("UTC")));
  116. $this->block->save($this->con);
  117. }
  118. /**
  119. * Get mnemonic block name
  120. *
  121. * @return string
  122. */
  123. public function getName()
  124. {
  125. return $this->block->getDbName();
  126. }
  127. public function setDescription($p_description)
  128. {
  129. $this->block->setDbDescription($p_description);
  130. $this->block->setDbMtime(new DateTime("now", new DateTimeZone("UTC")));
  131. $this->block->save($this->con);
  132. }
  133. public function getDescription()
  134. {
  135. return $this->block->getDbDescription();
  136. }
  137. public function getCreator()
  138. {
  139. return $this->block->getCcSubjs()->getDbLogin();
  140. }
  141. public function getCreatorId()
  142. {
  143. return $this->block->getCcSubjs()->getDbId();
  144. }
  145. public function setCreator($p_id)
  146. {
  147. $this->block->setDbCreatorId($p_id);
  148. $this->block->setDbMtime(new DateTime("now", new DateTimeZone("UTC")));
  149. $this->block->save($this->con);
  150. }
  151. public function getLastModified($format = null)
  152. {
  153. return $this->block->getDbMtime($format);
  154. }
  155. public function getSize()
  156. {
  157. return $this->block->countCcBlockcontentss();
  158. }
  159. /**
  160. * Get the entire block as a two dimentional array, sorted in order of play.
  161. * @param boolean $filterFiles if this is true, it will only return files that has
  162. * file_exists flag set to true
  163. * @return array
  164. */
  165. public function getContents($filterFiles=false)
  166. {
  167. Logging::info("Getting contents for block {$this->id}");
  168. $sql = <<<SQL
  169. SELECT pc.id AS id,
  170. pc.position,
  171. pc.cliplength AS LENGTH,
  172. pc.cuein,
  173. pc.cueout,
  174. pc.fadein,
  175. pc.fadeout,
  176. pc.trackoffset,
  177. bl.type,
  178. f.LENGTH AS orig_length,
  179. f.id AS item_id,
  180. f.track_title,
  181. f.artist_name AS creator,
  182. f.file_exists AS EXISTS,
  183. f.filepath AS path,
  184. f.mime as mime
  185. FROM cc_blockcontents AS pc
  186. LEFT JOIN cc_files AS f ON pc.file_id=f.id
  187. LEFT JOIN cc_block AS bl ON pc.block_id = bl.id
  188. WHERE pc.block_id = :block_id
  189. SQL;
  190. if ($filterFiles) {
  191. $sql .= <<<SQL
  192. AND f.file_exists = :file_exists
  193. SQL;
  194. }
  195. $sql .= <<<SQL
  196. ORDER BY pc.position
  197. SQL;
  198. $params = array(':block_id'=>$this->id);
  199. if ($filterFiles) {
  200. $params[':file_exists'] = $filterFiles;
  201. }
  202. $rows = Application_Common_Database::prepareAndExecute($sql, $params);
  203. $offset = 0;
  204. foreach ($rows as &$row) {
  205. $clipSec = Application_Common_DateHelper::playlistTimeToSeconds($row['length']);
  206. $row['trackSec'] = $clipSec;
  207. $row['cueInSec'] = Application_Common_DateHelper::playlistTimeToSeconds($row['cuein']);
  208. $row['cueOutSec'] = Application_Common_DateHelper::playlistTimeToSeconds($row['cueout']);
  209. $trackoffset = $row['trackoffset'];
  210. $offset += $clipSec;
  211. $offset -= $trackoffset;
  212. $offset_cliplength = Application_Common_DateHelper::secondsToPlaylistTime($offset);
  213. //format the length for UI.
  214. $formatter = new LengthFormatter($row['length']);
  215. $row['length'] = $formatter->format();
  216. $formatter = new LengthFormatter($offset_cliplength);
  217. $row['offset'] = $formatter->format();
  218. //format the fades in format 00(.0)
  219. $fades = $this->getFadeInfo($row['position']);
  220. $row['fadein'] = $fades[0];
  221. $row['fadeout'] = $fades[1];
  222. // format the cues in format 00:00:00(.0)
  223. // we need to add the '.0' for cues and not fades
  224. // because propel takes care of this for us
  225. // (we use propel to fetch the fades)
  226. $row['cuein'] = str_pad(substr($row['cuein'], 0, 10), 10, '.0');
  227. $row['cueout'] = str_pad(substr($row['cueout'], 0, 10), 10, '.0');
  228. //format original length
  229. $formatter = new LengthFormatter($row['orig_length']);
  230. $row['orig_length'] = $formatter->format();
  231. // XSS exploit prevention
  232. $row["track_title"] = htmlspecialchars($row["track_title"]);
  233. $row["creator"] = htmlspecialchars($row["creator"]);
  234. }
  235. return $rows;
  236. }
  237. /**
  238. * The database stores fades in 00:00:00 Time format with optional millisecond resolution .000000
  239. * but this isn't practical since fades shouldn't be very long usuall 1 second or less. This function
  240. * will normalize the fade so that it looks like 00.000000 to the user.
  241. **/
  242. public function normalizeFade($fade)
  243. {
  244. //First get rid of the first six characters 00:00: which will be added back later for db update
  245. $fade = substr($fade, 6);
  246. //Second add .000000 if the fade does't have milliseconds format already
  247. $dbFadeStrPos = strpos( $fade, '.' );
  248. if ( $dbFadeStrPos === False )
  249. $fade .= '.000000';
  250. else
  251. while( strlen( $fade ) < 9 )
  252. $fade .= '0';
  253. //done, just need to set back the formated values
  254. return $fade;
  255. }
  256. public function getUnformatedLength()
  257. {
  258. $this->block->reload();
  259. if ($this->isStatic()) {
  260. $length = $this->block->getDbLength();
  261. } else {
  262. $length = $this->getDynamicBlockLength();
  263. }
  264. return $length;
  265. }
  266. public function getLength()
  267. {
  268. $this->block->reload();
  269. $prepend = "";
  270. if ($this->isStatic()) {
  271. $length = $this->block->getDbLength();
  272. } else {
  273. $length = $this->getDynamicBlockLength();
  274. if (!$this->hasItemLimit()) {
  275. $prepend = "~";
  276. }
  277. }
  278. $formatter = new LengthFormatter($length);
  279. $length = $prepend.$formatter->format();
  280. return $length;
  281. }
  282. public function getDynamicBlockLength()
  283. {
  284. list($value, $modifier) = $this->getLimitValueAndModifier();
  285. if ($modifier == "items") {
  286. $length = $value." "._("items");
  287. } else {
  288. $hour = "00";
  289. $mins = "00";
  290. if ($modifier == "minutes") {
  291. $mins = $value;
  292. if ($value >59) {
  293. $hour = intval($value/60);
  294. $mins = $value%60;
  295. }
  296. } elseif ($modifier == "hours") {
  297. $mins = $value * 60;
  298. if ($mins >59) {
  299. $hour = intval($mins/60);
  300. $hour = str_pad($hour, 2, "0", STR_PAD_LEFT);
  301. $mins = $mins%60;
  302. }
  303. }
  304. $hour = str_pad($hour, 2, "0", STR_PAD_LEFT);
  305. $mins = str_pad($mins, 2, "0", STR_PAD_LEFT);
  306. $length = $hour.":".$mins.":00";
  307. }
  308. return $length;
  309. }
  310. public function getLimitValueAndModifier()
  311. {
  312. $result = CcBlockcriteriaQuery::create()->filterByDbBlockId($this->id)
  313. ->filterByDbCriteria('limit')->findOne();
  314. $modifier = $result->getDbModifier();
  315. $value = $result->getDbValue();
  316. return array($value, $modifier);
  317. }
  318. // this function returns sum of all track length under this block.
  319. public function getStaticLength()
  320. {
  321. $sql = <<<SQL
  322. SELECT SUM(cliplength) AS LENGTH
  323. FROM cc_blockcontents as bc
  324. JOIN cc_files as f ON bc.file_id = f.id
  325. WHERE block_id = :block_id
  326. AND f.file_exists = true
  327. SQL;
  328. $result = Application_Common_Database::prepareAndExecute($sql, array(':block_id'=>$this->id), 'all', PDO::FETCH_NUM);
  329. return $result[0][0];
  330. }
  331. private function insertBlockElement($info)
  332. {
  333. $row = new CcBlockcontents();
  334. $row->setDbBlockId($this->id);
  335. $row->setDbFileId($info["id"]);
  336. $row->setDbPosition($info["pos"]);
  337. $row->setDbCliplength($info["cliplength"]);
  338. $row->setDbCuein($info["cuein"]);
  339. $row->setDbCueout($info["cueout"]);
  340. $row->setDbFadein(Application_Common_DateHelper::secondsToPlaylistTime($info["fadein"]));
  341. $row->setDbFadeout(Application_Common_DateHelper::secondsToPlaylistTime($info["fadeout"]));
  342. $row->setDbTrackOffset($info["crossfadeDuration"]);
  343. $row->save($this->con);
  344. // above save result update on cc_block table on length column.
  345. // but $this->block doesn't get updated automatically
  346. // so we need to manually grab it again from DB so it has updated values
  347. // It is something to do FORMAT_ON_DEMAND( Lazy Loading )
  348. $this->block = CcBlockQuery::create()->findPK($this->id);
  349. }
  350. /*
  351. *
  352. */
  353. private function buildEntry($p_item, $pos)
  354. {
  355. $file = CcFilesQuery::create()->findPK($p_item, $this->con);
  356. if (isset($file) && $file->visible()) {
  357. $entry = $this->blockItem;
  358. $entry["id"] = $file->getDbId();
  359. $entry["pos"] = $pos;
  360. $entry["cueout"] = $file->getDbCueout();
  361. $entry["cuein"] = $file->getDbCuein();
  362. $cue_out = Application_Common_DateHelper::calculateLengthInSeconds($entry['cueout']);
  363. $cue_in = Application_Common_DateHelper::calculateLengthInSeconds($entry['cuein']);
  364. $entry["cliplength"] = Application_Common_DateHelper::secondsToPlaylistTime($cue_out-$cue_in);
  365. return $entry;
  366. } else {
  367. throw new Exception("trying to add a file that does not exist.");
  368. }
  369. }
  370. public function isStatic()
  371. {
  372. return $this->block->getDbType() == "static";
  373. }
  374. /*
  375. * @param array $p_items
  376. * an array of audioclips to add to the block
  377. * @param int|null $p_afterItem
  378. * item which to add the new items after in the block, null if added to the end.
  379. * @param string (before|after) $addAfter
  380. * whether to add the clips before or after the selected item.
  381. */
  382. public function addAudioClips($p_items, $p_afterItem=NULL, $addType = 'after')
  383. {
  384. $this->con->beginTransaction();
  385. $contentsToUpdate = array();
  386. try {
  387. if (is_numeric($p_afterItem)) {
  388. Logging::info("Finding block content item {$p_afterItem}");
  389. $afterItem = CcBlockcontentsQuery::create()->findPK($p_afterItem);
  390. $index = $afterItem->getDbPosition();
  391. Logging::info("index is {$index}");
  392. $pos = ($addType == 'after') ? $index + 1 : $index;
  393. $contentsToUpdate = CcBlockcontentsQuery::create()
  394. ->filterByDbBlockId($this->id)
  395. ->filterByDbPosition($pos, Criteria::GREATER_EQUAL)
  396. ->orderByDbPosition()
  397. ->find($this->con);
  398. Logging::info("Adding to block");
  399. Logging::info("at position {$pos}");
  400. } else {
  401. //add to the end of the block
  402. if ($addType == 'after') {
  403. $pos = $this->getSize();
  404. }
  405. //add to the beginning of the block.
  406. else {
  407. $pos = 0;
  408. $contentsToUpdate = CcBlockcontentsQuery::create()
  409. ->filterByDbBlockId($this->id)
  410. ->orderByDbPosition()
  411. ->find($this->con);
  412. }
  413. $contentsToUpdate = CcBlockcontentsQuery::create()
  414. ->filterByDbBlockId($this->id)
  415. ->filterByDbPosition($pos, Criteria::GREATER_EQUAL)
  416. ->orderByDbPosition()
  417. ->find($this->con);
  418. Logging::info("Adding to block");
  419. Logging::info("at position {$pos}");
  420. }
  421. foreach ($p_items as $ac) {
  422. //Logging::info("Adding audio file {$ac[0]}");
  423. try {
  424. if (is_array($ac) && $ac[1] == 'audioclip') {
  425. $res = $this->insertBlockElement($this->buildEntry($ac[0], $pos));
  426. // update is_playlist flag in cc_files to indicate the
  427. // file belongs to a playlist or block (in this case a block)
  428. $db_file = CcFilesQuery::create()->findPk($ac[0], $this->con);
  429. $db_file->setDbIsPlaylist(true)->save($this->con);
  430. $pos = $pos + 1;
  431. } elseif (!is_array($ac)) {
  432. $res = $this->insertBlockElement($this->buildEntry($ac, $pos));
  433. $pos = $pos + 1;
  434. $db_file = CcFilesQuery::create()->findPk($ac, $this->con);
  435. $db_file->setDbIsPlaylist(true)->save($this->con);
  436. }
  437. } catch (Exception $e) {
  438. Logging::info($e->getMessage());
  439. }
  440. }
  441. //reset the positions of the remaining items.
  442. for ($i = 0; $i < count($contentsToUpdate); $i++) {
  443. $contentsToUpdate[$i]->setDbPosition($pos);
  444. $contentsToUpdate[$i]->save($this->con);
  445. $pos = $pos + 1;
  446. }
  447. $this->block->setDbMtime(new DateTime("now", new DateTimeZone("UTC")));
  448. $this->block->save($this->con);
  449. $this->con->commit();
  450. $this->updateBlockLengthInAllPlaylist();
  451. } catch (Exception $e) {
  452. $this->con->rollback();
  453. throw $e;
  454. }
  455. }
  456. /**
  457. * Move audioClip to the new position in the block
  458. *
  459. * @param array $p_items
  460. * array of unique ids of the selected items
  461. * @param int $p_afterItem
  462. * unique id of the item to move the clip after
  463. */
  464. public function moveAudioClips($p_items, $p_afterItem=NULL)
  465. {
  466. $this->con->beginTransaction();
  467. try {
  468. $contentsToMove = CcBlockcontentsQuery::create()
  469. ->filterByDbId($p_items, Criteria::IN)
  470. ->orderByDbPosition()
  471. ->find($this->con);
  472. $otherContent = CcBlockcontentsQuery::create()
  473. ->filterByDbId($p_items, Criteria::NOT_IN)
  474. ->filterByDbBlockId($this->id)
  475. ->orderByDbPosition()
  476. ->find($this->con);
  477. $pos = 0;
  478. //moving items to beginning of the block.
  479. if (is_null($p_afterItem)) {
  480. Logging::info("moving items to beginning of block");
  481. foreach ($contentsToMove as $item) {
  482. Logging::info("item {$item->getDbId()} to pos {$pos}");
  483. $item->setDbPosition($pos);
  484. $item->save($this->con);
  485. $pos = $pos + 1;
  486. }
  487. foreach ($otherContent as $item) {
  488. Logging::info("item {$item->getDbId()} to pos {$pos}");
  489. $item->setDbPosition($pos);
  490. $item->save($this->con);
  491. $pos = $pos + 1;
  492. }
  493. } else {
  494. Logging::info("moving items after {$p_afterItem}");
  495. foreach ($otherContent as $item) {
  496. Logging::info("item {$item->getDbId()} to pos {$pos}");
  497. $item->setDbPosition($pos);
  498. $item->save($this->con);
  499. $pos = $pos + 1;
  500. if ($item->getDbId() == $p_afterItem) {
  501. foreach ($contentsToMove as $move) {
  502. Logging::info("item {$move->getDbId()} to pos {$pos}");
  503. $move->setDbPosition($pos);
  504. $move->save($this->con);
  505. $pos = $pos + 1;
  506. }
  507. }
  508. }
  509. }
  510. $this->con->commit();
  511. } catch (Exception $e) {
  512. $this->con->rollback();
  513. throw $e;
  514. }
  515. $this->block = CcBlockQuery::create()->findPK($this->id);
  516. $this->block->setDbMtime(new DateTime("now", new DateTimeZone("UTC")));
  517. $this->block->save($this->con);
  518. }
  519. /**
  520. * Remove audioClip from block
  521. *
  522. * @param array $p_items
  523. * array of unique item ids to remove from the block..
  524. */
  525. public function delAudioClips($p_items)
  526. {
  527. $this->con->beginTransaction();
  528. try {
  529. // we need to get the file id of the item we are deleting
  530. // before the item gets deleted from the block
  531. $itemsToDelete = CcBlockcontentsQuery::create()
  532. ->filterByPrimaryKeys($p_items)
  533. ->filterByDbFileId(null, Criteria::NOT_EQUAL)
  534. ->find($this->con);
  535. CcBlockcontentsQuery::create()
  536. ->findPKs($p_items)
  537. ->delete($this->con);
  538. // now that the items have been deleted we can update the
  539. // is_playlist flag in cc_files
  540. Application_Model_StoredFile::setIsPlaylist($itemsToDelete, 'block', false);
  541. $contents = CcBlockcontentsQuery::create()
  542. ->filterByDbBlockId($this->id)
  543. ->orderByDbPosition()
  544. ->find($this->con);
  545. //reset the positions of the remaining items.
  546. for ($i = 0; $i < count($contents); $i++) {
  547. $contents[$i]->setDbPosition($i);
  548. $contents[$i]->save($this->con);
  549. }
  550. $this->block->setDbMtime(new DateTime("now", new DateTimeZone("UTC")));
  551. $this->block->save($this->con);
  552. $this->con->commit();
  553. $this->updateBlockLengthInAllPlaylist();
  554. } catch (Exception $e) {
  555. $this->con->rollback();
  556. throw $e;
  557. }
  558. }
  559. public function getFadeInfo($pos)
  560. {
  561. //Logging::info("Getting fade info for pos {$pos}");
  562. $row = CcBlockcontentsQuery::create()
  563. ->joinWith(CcFilesPeer::OM_CLASS)
  564. ->filterByDbBlockId($this->id)
  565. ->filterByDbPosition($pos)
  566. ->findOne();
  567. //Propel returns values in form 00.000000 format which is for only seconds.
  568. //We only want to display 1 decimal
  569. $fadeIn = substr($row->getDbFadein(), 0, 4);
  570. $fadeOut = substr($row->getDbFadeout(), 0, 4);
  571. return array($fadeIn, $fadeOut);
  572. }
  573. /*
  574. * create a crossfade from item in cc_playlist_contents with $id1 to item $id2.
  575. *
  576. * $fadeOut length of fade out in seconds if $id1
  577. * $fadeIn length of fade in in seconds of $id2
  578. * $offset time in seconds from end of $id1 that $id2 will begin to play.
  579. */
  580. public function createCrossfade($id1, $fadeOut, $id2, $fadeIn, $offset)
  581. {
  582. $this->con->beginTransaction();
  583. if (!isset($offset)) {
  584. $offset = Application_Model_Preference::GetDefaultCrossfadeDuration();
  585. }
  586. try {
  587. if (isset($id1)) {
  588. $this->changeFadeInfo($id1, null, $fadeOut);
  589. }
  590. if (isset($id2)) {
  591. $this->changeFadeInfo($id2, $fadeIn, null, $offset);
  592. }
  593. $this->con->commit();
  594. } catch (Exception $e) {
  595. $this->con->rollback();
  596. throw $e;
  597. }
  598. }
  599. /**
  600. * Change fadeIn and fadeOut values for block Element
  601. *
  602. * @param int $pos
  603. * position of audioclip in block
  604. * @param string $fadeIn
  605. * new value in ss.ssssss or extent format
  606. * @param string $fadeOut
  607. * new value in ss.ssssss or extent format
  608. * @return boolean
  609. */
  610. public function changeFadeInfo($id, $fadeIn, $fadeOut, $offset=null)
  611. {
  612. //See issue CC-2065, pad the fadeIn and fadeOut so that it is TIME compatable with the DB schema
  613. //For the top level PlayList either fadeIn or fadeOut will sometimes be Null so need a gaurd against
  614. //setting it to nonNull for checks down below
  615. $fadeIn = $fadeIn?'00:00:'.$fadeIn:$fadeIn;
  616. $fadeOut = $fadeOut?'00:00:'.$fadeOut:$fadeOut;
  617. $this->con->beginTransaction();
  618. try {
  619. $row = CcBlockcontentsQuery::create()->findPK($id);
  620. if (is_null($row)) {
  621. throw new Exception("Block item does not exist.");
  622. }
  623. $clipLength = $row->getDbCliplength();
  624. if (!is_null($fadeIn)) {
  625. $sql = "SELECT :fade_in::INTERVAL > :clip_length::INTERVAL";
  626. $params = array(
  627. ':fade_in' => $fadeIn,
  628. ':clip_length' => $clipLength
  629. );
  630. $result = Application_Common_Database::prepareAndExecute($sql, $params, 'column');
  631. if ($result) {
  632. //"Fade In can't be larger than overall playlength.";
  633. $fadeIn = $clipLength;
  634. }
  635. $row->setDbFadein($fadeIn);
  636. if (!is_null($offset)) {
  637. $row->setDbTrackOffset($offset);
  638. Logging::info("Setting offset {$offset} on item {$id}");
  639. $row->save($this->con);
  640. }
  641. }
  642. if (!is_null($fadeOut)) {
  643. $sql = "SELECT :fade_out::INTERVAL > :clip_length::INTERVAL";
  644. $params = array(
  645. ':fade_out' => $fadeOut,
  646. ':clip_length' => $clipLength
  647. );
  648. $result = Application_Common_Database::prepareAndExecute($sql, $params, 'column');
  649. if ($result) {
  650. //"Fade Out can't be larger than overall playlength.";
  651. $fadeOut = $clipLength;
  652. }
  653. $row->setDbFadeout($fadeOut);
  654. }
  655. $row->save($this->con);
  656. $this->block->setDbMtime(new DateTime("now", new DateTimeZone("UTC")));
  657. $this->block->save($this->con);
  658. $this->con->commit();
  659. } catch (Exception $e) {
  660. $this->con->rollback();
  661. throw $e;
  662. }
  663. return array("fadeIn" => $fadeIn, "fadeOut" => $fadeOut);
  664. }
  665. public function setfades($fadein, $fadeout)
  666. {
  667. if (isset($fadein)) {
  668. Logging::info("Setting block fade in {$fadein}");
  669. $row = CcBlockcontentsQuery::create()
  670. ->filterByDbBlockId($this->id)
  671. ->filterByDbPosition(0)
  672. ->findOne($this->con);
  673. $this->changeFadeInfo($row->getDbId(), $fadein, null);
  674. }
  675. if (isset($fadeout)) {
  676. Logging::info("Setting block fade out {$fadeout}");
  677. $row = CcBlockcontentsQuery::create()
  678. ->filterByDbBlockId($this->id)
  679. ->filterByDbPosition($this->getSize()-1)
  680. ->findOne($this->con);
  681. $this->changeFadeInfo($row->getDbId(), null, $fadeout);
  682. }
  683. }
  684. /**
  685. * Change cueIn/cueOut values for block element
  686. *
  687. * @param int $pos
  688. * position of audioclip in block
  689. * @param string $cueIn
  690. * new value in ss.ssssss or extent format
  691. * @param string $cueOut
  692. * new value in ss.ssssss or extent format
  693. * @return boolean or pear error object
  694. */
  695. public function changeClipLength($id, $cueIn, $cueOut)
  696. {
  697. $this->con->beginTransaction();
  698. $errArray= array();
  699. try {
  700. if (is_null($cueIn) && is_null($cueOut)) {
  701. $errArray["error"] = _("Cue in and cue out are null.");
  702. return $errArray;
  703. }
  704. $row = CcBlockcontentsQuery::create()
  705. ->joinWith(CcFilesPeer::OM_CLASS)
  706. ->filterByPrimaryKey($id)
  707. ->findOne($this->con);
  708. if (is_null($row)) {
  709. throw new Exception("Block item does not exist.");
  710. }
  711. $oldCueIn = $row->getDBCuein();
  712. $oldCueOut = $row->getDbCueout();
  713. $fadeIn = $row->getDbFadein();
  714. $fadeOut = $row->getDbFadeout();
  715. $file = $row->getCcFiles($this->con);
  716. $origLength = $file->getDbLength();
  717. if (!is_null($cueIn) && !is_null($cueOut)) {
  718. if ($cueOut === "") {
  719. $cueOut = $origLength;
  720. }
  721. $sql = "SELECT :cue_out::INTERVAL > :orig_length::INTERVAL";
  722. $params = array(
  723. ':cue_out' => $cueOut,
  724. ':orig_length' => $origLength
  725. );
  726. $result = Application_Common_Database::prepareAndExecute($sql, $params, 'column');
  727. if ($result) {
  728. $errArray["error"] = _("Can't set cue out to be greater than file length.");
  729. return $errArray;
  730. }
  731. $sql = "SELECT :cue_in::INTERVAL > :cue_out::INTERVAL";
  732. $params = array(
  733. ':cue_in' => $cueIn,
  734. ':cue_out' => $cueOut
  735. );
  736. $result = Application_Common_Database::prepareAndExecute($sql, $params, 'column');
  737. if ($result) {
  738. $errArray["error"] = _("Can't set cue in to be larger than cue out.");
  739. return $errArray;
  740. }
  741. $sql = "SELECT :cue_out::INTERVAL - :cue_in::INTERVAL";
  742. $result = Application_Common_Database::prepareAndExecute($sql, $params, 'column');
  743. $cliplength = $result;
  744. $row->setDbCuein($cueIn);
  745. $row->setDbCueout($cueOut);
  746. $row->setDBCliplength($cliplength);
  747. } elseif (!is_null($cueIn)) {
  748. $sql = "SELECT :cue_in::INTERVAL > :old_cue_out::INTERVAL";
  749. $params = array(
  750. ':cue_in' => $cueIn,
  751. ':old_cue_out' => $oldCueOut
  752. );
  753. $result = Application_Common_Database::prepareAndExecute($sql, $params, 'column');
  754. if ($result) {
  755. $errArray["error"] = _("Can't set cue in to be larger than cue out.");
  756. return $errArray;
  757. }
  758. $sql = "SELECT :old_cue_out::INTERVAL - :cue_in::INTERVAL";
  759. $result = Application_Common_Database::prepareAndExecute($sql, $params, 'column');
  760. $cliplength = $result;
  761. $row->setDbCuein($cueIn);
  762. $row->setDBCliplength($cliplength);
  763. } elseif (!is_null($cueOut)) {
  764. if ($cueOut === "") {
  765. $cueOut = $origLength;
  766. }
  767. $sql = "SELECT :cue_out::INTERVAL > :orig_length::INTERVAL";
  768. $params = array(
  769. ':cue_out' => $cueOut,
  770. ':orig_length' => $origLength
  771. );
  772. $result = Application_Common_Database::prepareAndExecute($sql, $params, 'column');
  773. if ($result) {
  774. $errArray["error"] = _("Can't set cue out to be greater than file length.");
  775. return $errArray;
  776. }
  777. $sql = "SELECT :cue_out::INTERVAL < :old_cue_in::INTERVAL";
  778. $params = array(
  779. ':cue_out' => $cueOut,
  780. ':old_cue_in' => $oldCueIn
  781. );
  782. $result = Application_Common_Database::prepareAndExecute($sql, $params, 'column');
  783. if ($result) {
  784. $errArray["error"] = _("Can't set cue out to be smaller than cue in.");
  785. return $errArray;
  786. }
  787. $sql = "SELECT :cue_out::INTERVAL - :old_cue_in::INTERVAL";
  788. $result = Application_Common_Database::prepareAndExecute($sql, $params, 'column');
  789. $cliplength = $result;
  790. $row->setDbCueout($cueOut);
  791. $row->setDBCliplength($cliplength);
  792. }
  793. $cliplength = $row->getDbCliplength();
  794. $sql = "SELECT :fade_in::INTERVAL > :clip_length::INTERVAL";
  795. $params = array(
  796. ':fade_in' => $fadeIn,
  797. ':clip_length' => $cliplength
  798. );
  799. $result = Application_Common_Database::prepareAndExecute($sql, $params, 'column');
  800. if ($result) {
  801. $fadeIn = $cliplength;
  802. $row->setDbFadein($fadeIn);
  803. }
  804. $sql = "SELECT :fade_out::INTERVAL > :clip_length::INTERVAL";
  805. $params = array(
  806. ':fade_out' => $fadeOut,
  807. ':clip_length' => $cliplength
  808. );
  809. $result = Application_Common_Database::prepareAndExecute($sql, $params, 'column');
  810. if ($result) {
  811. $fadeOut = $cliplength;
  812. $row->setDbFadein($fadeOut);
  813. }
  814. $row->save($this->con);
  815. $this->block->setDbMtime(new DateTime("now", new DateTimeZone("UTC")));
  816. $this->block->save($this->con);
  817. $this->con->commit();
  818. } catch (Exception $e) {
  819. $this->con->rollback();
  820. throw $e;
  821. }
  822. return array("cliplength"=> $cliplength, "cueIn"=> $cueIn, "cueOut"=> $cueOut, "length"=> $this->getUnformatedLength(),
  823. "fadeIn"=> $fadeIn, "fadeOut"=> $fadeOut);
  824. }
  825. public function getAllPLMetaData()
  826. {
  827. $categories = $this->categories;
  828. $md = array();
  829. foreach ($categories as $key => $val) {
  830. $method = 'get' . $val;
  831. $md[$key] = $this->$method();
  832. }
  833. return $md;
  834. }
  835. public function getMetaData($category)
  836. {
  837. $cat = $this->categories[$category];
  838. $method = 'get' . $cat;
  839. return $this->$method();
  840. }
  841. public function setMetadata($category, $value)
  842. {
  843. $cat = $this->categories[$category];
  844. $method = 'set' . $cat;
  845. $this->$method($value);
  846. }
  847. public static function getBlockCount()
  848. {
  849. $sql = 'SELECT count(*) as cnt FROM cc_playlist';
  850. $res = Application_Common_Database::prepareAndExecute($sql, array(),
  851. Application_Common_Database::COLUMN);
  852. return $res;
  853. }
  854. /**
  855. * Delete the file from all blocks.
  856. * @param string $p_fileId
  857. */
  858. public static function DeleteFileFromAllBlocks($p_fileId)
  859. {
  860. CcBlockcontentsQuery::create()->filterByDbFileId($p_fileId)->delete();
  861. }
  862. /**
  863. * Delete blocks that match the ids..
  864. * @param array $p_ids
  865. */
  866. public static function deleteBlocks($p_ids, $p_userId)
  867. {
  868. $userInfo = Zend_Auth::getInstance()->getStorage()->read();
  869. $user = new Application_Model_User($userInfo->id);
  870. $isAdminOrPM = $user->isUserType(array(UTYPE_ADMIN, UTYPE_PROGRAM_MANAGER));
  871. // get only the files from the blocks
  872. // we are about to delete
  873. $itemsToDelete = CcBlockcontentsQuery::create()
  874. ->filterByDbBlockId($p_ids)
  875. ->filterByDbFileId(null, Criteria::NOT_EQUAL)
  876. ->find();
  877. $updateIsPlaylistFlag = false;
  878. if (!$isAdminOrPM) {
  879. $leftOver = self::blocksNotOwnedByUser($p_ids, $p_userId);
  880. if (count($leftOver) == 0) {
  881. CcBlockQuery::create()->findPKs($p_ids)->delete();
  882. $updateIsPlaylistFlag = true;
  883. } else {
  884. throw new BlockNoPermissionException;
  885. }
  886. } else {
  887. CcBlockQuery::create()->findPKs($p_ids)->delete();
  888. $updateIsPlaylistFlag = true;
  889. }
  890. if ($updateIsPlaylistFlag) {
  891. // update is_playlist flag in cc_files
  892. Application_Model_StoredFile::setIsPlaylist(
  893. $itemsToDelete,
  894. 'block',
  895. false
  896. );
  897. }
  898. }
  899. // This function returns that are not owen by $p_user_id among $p_ids
  900. private static function blocksNotOwnedByUser($p_ids, $p_userId)
  901. {
  902. $ownedByUser = CcBlockQuery::create()->filterByDbCreatorId($p_userId)->find()->getData();
  903. $selectedPls = $p_ids;
  904. $ownedPls = array();
  905. foreach ($ownedByUser as $pl) {
  906. if (in_array($pl->getDbId(), $selectedPls)) {
  907. $ownedPls[] = $pl->getDbId();
  908. }
  909. }
  910. $leftOvers = array_diff($selectedPls, $ownedPls);
  911. return $leftOvers;
  912. }
  913. /**
  914. * Delete all files from block
  915. */
  916. public function deleteAllFilesFromBlock()
  917. {
  918. // get only the files from the playlist
  919. // we are about to clear out
  920. $itemsToDelete = CcBlockcontentsQuery::create()
  921. ->filterByDbBlockId($this->id)
  922. ->filterByDbFileId(null, Criteria::NOT_EQUAL)
  923. ->find();
  924. CcBlockcontentsQuery::create()->findByDbBlockId($this->id)->delete();
  925. // update is_playlist flag in cc_files
  926. Application_Model_StoredFile::setIsPlaylist(
  927. $itemsToDelete,
  928. 'block',
  929. false
  930. );
  931. //$this->block->reload();
  932. $this->block->setDbMtime(new DateTime("now", new DateTimeZone("UTC")));
  933. $this->block->save($this->con);
  934. $this->con->commit();
  935. }
  936. // smart block functions start
  937. public function shuffleSmartBlock()
  938. {
  939. // if it here that means it's static pl
  940. $this->saveType("static");
  941. $contents = CcBlockcontentsQuery::create()
  942. ->filterByDbBlockId($this->id)
  943. ->orderByDbPosition()
  944. ->find();
  945. $shuffledPos = range(0, count($contents)-1);
  946. shuffle($shuffledPos);
  947. foreach ($contents as $item) {
  948. $item->setDbPosition(array_shift($shuffledPos));
  949. $item->save();
  950. }
  951. return array("result"=>0);
  952. }
  953. public function saveType($p_blockType)
  954. {
  955. // saving dynamic/static flag
  956. CcBlockQuery::create()->findPk($this->id)->setDbType($p_blockType)->save();
  957. }
  958. public function setLength($value)
  959. {
  960. $this->block->setDbLength($value);
  961. $this->block->save($this->con);
  962. $this->updateBlockLengthInAllPlaylist();
  963. }
  964. /**
  965. * Saves smart block criteria
  966. * @param array $p_criteria
  967. */
  968. public function saveSmartBlockCriteria($p_criteria)
  969. {
  970. $data = $this->organizeSmartPlaylistCriteria($p_criteria);
  971. // saving dynamic/static flag
  972. $blockType = $data['etc']['sp_type'] == 0 ? 'static':'dynamic';
  973. $this->saveType($blockType);
  974. $this->storeCriteriaIntoDb($data);
  975. // if the block is dynamic, put null to the length
  976. // as it cannot be calculated
  977. if ($blockType == 'dynamic') {
  978. if ($this->hasItemLimit()) {
  979. $this->setLength(null);
  980. } else {
  981. $this->setLength($this->getDynamicBlockLength());
  982. }
  983. } else {
  984. $length = $this->getStaticLength();
  985. if (!$length) {
  986. $length = "00:00:00";
  987. }
  988. $this->setLength($length);
  989. }
  990. $this->updateBlockLengthInAllPlaylist();
  991. }
  992. public function hasItemLimit()
  993. {
  994. list($value, $modifier) = $this->getLimitValueAndModifier();
  995. return ($modifier == 'items');
  996. }
  997. public function storeCriteriaIntoDb($p_criteriaData)
  998. {
  999. // delete criteria under $p_blockId
  1000. CcBlockcriteriaQuery::create()->findByDbBlockId($this->id)->delete();
  1001. //Logging::info($p_criteriaData);
  1002. //insert modifier rows
  1003. if (isset($p_criteriaData['criteria'])) {
  1004. $critKeys = array_keys($p_criteriaData['criteria']);
  1005. for ($i = 0; $i < count($critKeys); $i++) {
  1006. foreach ($p_criteriaData['criteria'][$critKeys[$i]] as $d) {
  1007. $field = $d['sp_criteria_field'];
  1008. $value = $d['sp_criteria_value'];
  1009. if ($field == 'utime' || $field == 'mtime' || $field == 'lptime') {
  1010. $value = Application_Common_DateHelper::UserTimezoneStringToUTCString($value);
  1011. }
  1012. $qry = new CcBlockcriteria();
  1013. $qry->setDbCriteria($field)
  1014. ->setDbModifier($d['sp_criteria_modifier'])
  1015. ->setDbValue($value)
  1016. ->setDbBlockId($this->id);
  1017. if (isset($d['sp_criteria_extra'])) {
  1018. if ($field == 'utime' || $field == 'mtime' || $field == 'lptime') {
  1019. $d['sp_criteria_extra'] = Application_Common_DateHelper::UserTimezoneStringToUTCString($d['sp_criteria_extra']);
  1020. }
  1021. $qry->setDbExtra($d['sp_criteria_extra']);
  1022. }
  1023. $qry->save();
  1024. }
  1025. }
  1026. }
  1027. // insert sort info
  1028. $qry = new CcBlockcriteria();
  1029. $qry->setDbCriteria("sort")
  1030. ->setDbModifier("N/A")
  1031. ->setDbValue($p_criteriaData['etc']['sp_sort_options'])
  1032. ->setDbBlockId($this->id)
  1033. ->save();
  1034. // insert limit info
  1035. $qry = new CcBlockcriteria();
  1036. $qry->setDbCriteria("limit")
  1037. ->setDbModifier($p_criteriaData['etc']['sp_limit_options'])
  1038. ->setDbValue($p_criteriaData['etc']['sp_limit_value'])
  1039. ->setDbBlockId($this->id)
  1040. ->save();
  1041. // insert repeate track option
  1042. $qry = new CcBlockcriteria();
  1043. $qry->setDbCriteria("repeat_tracks")
  1044. ->setDbModifier("N/A")
  1045. ->setDbValue($p_criteriaData['etc']['sp_repeat_tracks'])
  1046. ->setDbBlockId($this->id)
  1047. ->save();
  1048. }
  1049. /**
  1050. * generate list of tracks. This function saves creiteria and generate
  1051. * tracks.
  1052. * @param array $p_criteria
  1053. */
  1054. public function generateSmartBlock($p_criteria, $returnList=false)
  1055. {
  1056. $this->saveSmartBlockCriteria($p_criteria);
  1057. $insertList = $this->getListOfFilesUnderLimit();
  1058. $this->deleteAllFilesFromBlock();
  1059. // constrcut id array
  1060. $ids = array();
  1061. foreach ($insertList as $ele) {
  1062. $ids[] = $ele['id'];
  1063. }
  1064. $this->addAudioClips(array_values($ids));
  1065. // update length in playlist contents.
  1066. $this->updateBlockLengthInAllPlaylist();
  1067. return array("result"=>0);
  1068. }
  1069. public function updateBlockLengthInAllPlaylist()
  1070. {
  1071. $blocks = CcPlaylistcontentsQuery::create()->filterByDbBlockId($this->id)->find();
  1072. $blocks->getFirst();
  1073. $iterator = $blocks->getIterator();
  1074. while ($iterator->valid()) {
  1075. $length = $this->getUnformatedLength();
  1076. if (!preg_match("/^[0-9]{2}:[0-9]{2}:[0-9]{2}/", $length)) {
  1077. $iterator->current()->setDbClipLength(null);
  1078. } else {
  1079. $iterator->current()->setDbClipLength($length);
  1080. }
  1081. $iterator->current()->save();
  1082. $iterator->next();
  1083. }
  1084. }
  1085. public function getListOfFilesUnderLimit()
  1086. {
  1087. $info = $this->getListofFilesMeetCriteria();
  1088. $files = $info['files'];
  1089. $limit = $info['limit'];
  1090. $repeat = $info['repeat_tracks'];
  1091. $insertList = array();
  1092. $totalTime = 0;
  1093. $totalItems = 0;
  1094. // this moves the pointer to the first element in the collection
  1095. $files->getFirst();
  1096. $iterator = $files->getIterator();
  1097. $isBlockFull = false;
  1098. while ($iterator->valid()) {
  1099. $id = $iterator->current()->getDbId();
  1100. $fileLength = $iterator->current()->getCueLength();
  1101. $length = Application_Common_DateHelper::calculateLengthInSeconds($fileLength);
  1102. $insertList[] = array('id'=>$id, 'length'=>$length);
  1103. $totalTime += $length;
  1104. $totalItems++;
  1105. if ((!is_null($limit['items']) && $limit['items'] == count($insertList)) || $totalItems > 500 || $totalTime > $limit['time']) {
  1106. $isBlockFull = true;
  1107. break;
  1108. }
  1109. $iterator->next();
  1110. }
  1111. $sizeOfInsert = count($insertList);
  1112. // if block is not full and repeat_track is check, fill up more
  1113. while (!$isBlockFull && $repeat == 1 && $sizeOfInsert > 0) {
  1114. Logging::debug("adding repeated tracks.");
  1115. Logging::debug("total time = " . $totalTime);
  1116. $randomEleKey = array_rand(array_slice($insertList, 0, $sizeOfInsert));
  1117. $insertList[] = $insertList[$randomEleKey];
  1118. $totalTime += $insertList[$randomEleKey]['length'];
  1119. $totalItems++;
  1120. if ((!is_null($limit['items']) && $limit['items'] == count($insertList)) || $totalItems > 500 || $totalTime > $limit['time']) {
  1121. break;
  1122. }
  1123. }
  1124. return $insertList;
  1125. }
  1126. public function getCriteria()
  1127. {
  1128. $criteriaOptions = array(
  1129. 0 => _("Select criteria"),
  1130. "album_title" => _("Album"),
  1131. "bit_rate" => _("Bit Rate (Kbps)"),
  1132. "bpm" => _("BPM"),
  1133. "composer" => _("Composer"),
  1134. "conductor" => _("Conductor"),
  1135. "copyright" => _("Copyright"),
  1136. "cuein" => _("Cue In"),
  1137. "cueout" => _("Cue Out"),
  1138. "artist_name" => _("Creator"),
  1139. "encoded_by" => _("Encoded By"),
  1140. "genre" => _("Genre"),
  1141. "isrc_number" => _("ISRC"),
  1142. "label" => _("Label"),
  1143. "language" => _("Language"),
  1144. "utime" => _("Upload Time"),
  1145. "mtime" => _("Last Modified"),
  1146. "lptime" => _("Last Played"),
  1147. "length" => _("Length"),
  1148. "mime" => _("Mime"),
  1149. "mood" => _("Mood"),
  1150. "owner_id" => _("Owner"),
  1151. "replay_gain" => _("Replay Gain"),
  1152. "sample_rate" => _("Sample Rate (kHz)"),
  1153. "track_title" => _("Title"),
  1154. "track_number" => _("Track Number"),
  1155. "utime" => _("Uploaded"),
  1156. "info_url" => _("Website"),
  1157. "year" => _("Year")
  1158. );
  1159. $modifierOptions = array(
  1160. "0" => _("Select modifier"),
  1161. "contains" => _("contains"),
  1162. "does not contain" => _("does not contain"),
  1163. "is" => _("is"),
  1164. "is not" => _("is not"),
  1165. "starts with" => _("starts with"),
  1166. "ends with" => _("ends with"),
  1167. "is" => _("is"),
  1168. "is not" => _("is not"),
  1169. "is greater than" => _("is greater than"),
  1170. "is less than" => _("is less than"),
  1171. "is in the range" => _("is in the range")
  1172. );
  1173. // Load criteria from db
  1174. $out = CcBlockcriteriaQuery::create()->orderByDbCriteria()->findByDbBlockId($this->id);
  1175. $storedCrit = array();
  1176. foreach ($out as $crit) {
  1177. $criteria = $crit->getDbCriteria();
  1178. $modifier = $crit->getDbModifier();
  1179. $value = htmlspecialchars($crit->getDbValue());
  1180. $extra = $crit->getDbExtra();
  1181. if ($criteria == "limit") {
  1182. $storedCrit["limit"] = array(
  1183. "value"=>$value,
  1184. "modifier"=>$modifier,
  1185. "display_modifier"=>_($modifier));
  1186. } else if($criteria == "repeat_tracks") {
  1187. $storedCrit["repeat_tracks"] = array("value"=>$value);
  1188. } else if($criteria == "sort") {
  1189. $storedCrit["sort"] = array("value"=>$value);
  1190. } else {
  1191. $storedCrit["crit"][$criteria][] = array(
  1192. "criteria"=>$criteria,
  1193. "value"=>$value,
  1194. "modifier"=>$modifier,
  1195. "extra"=>$extra,
  1196. "display_name"=>$criteriaOptions[$criteria],
  1197. "display_modifier"=>$modifierOptions[$modifier]);
  1198. }
  1199. }
  1200. return $storedCrit;
  1201. }
  1202. // this function return list of propel object
  1203. public function getListofFilesMeetCriteria()
  1204. {
  1205. $storedCrit = $this->getCriteria();
  1206. $qry = CcFilesQuery::create();
  1207. $qry->useFkOwnerQuery("subj", "left join");
  1208. if (isset($storedCrit["crit"])) {
  1209. foreach ($storedCrit["crit"] as $crit) {
  1210. $i = 0;
  1211. foreach ($crit as $criteria) {
  1212. $spCriteria = $criteria['criteria'];
  1213. $spCriteriaModifier = $criteria['modifier'];
  1214. $column = CcFilesPeer::getTableMap()->getColumnByPhpName(self::$criteria2PeerMap[$spCriteria]);
  1215. //data should already be in UTC, do we have to do anything special here anymore?
  1216. if ($column->getType() == PropelColumnTypes::TIMESTAMP) {
  1217. $spCriteriaValue = $criteria['value'];
  1218. if (isset($criteria['extra'])) {
  1219. $spCriteriaExtra = $criteria['extra'];
  1220. }
  1221. } elseif ($spCriteria == "bit_rate" || $spCriteria == 'sample_rate') {
  1222. // multiply 1000 because we store only number value
  1223. // e.g 192kps is stored as 192000
  1224. $spCriteriaValue = $criteria['value']*1000;
  1225. if (isset($criteria['extra'])) {
  1226. $spCriteriaExtra = $criteria['extra']*1000;
  1227. }
  1228. /*
  1229. * If user is searching for an exact match of length we need to
  1230. * search as if it starts with the specified length because the
  1231. * user only sees the rounded version (i.e. 4:02.7 is 4:02.761625
  1232. * in the database)
  1233. */
  1234. } elseif (in_array($spCriteria, array('length', 'cuein', 'cueout')) && $spCriteriaModifier == "is") {
  1235. $spCriteriaModifier = "starts with";
  1236. $spCriteria = $spCriteria.'::text';
  1237. $spCriteriaValue = $criteria['value'];
  1238. } else {
  1239. /* Propel does not escape special characters properly when using LIKE/ILIKE
  1240. * We have to add extra slashes in these cases
  1241. */
  1242. $tempModifier = trim(self::$modifier2CriteriaMap[$spCriteriaModifier]);
  1243. if ($tempModifier == 'ILIKE') {
  1244. $spCriteriaValue = addslashes($criteria['value']);
  1245. // addslashes() does not esapce '%' so we have to do it manually
  1246. $spCriteriaValue = str_replace('%', '\%', $spCriteriaValue);
  1247. } else {
  1248. $spCriteriaValue = ($criteria['value']);
  1249. }
  1250. $spCriteriaExtra = $criteria['extra'];
  1251. }
  1252. if ($spCriteriaModifier == "starts with") {
  1253. $spCriteriaValue = "$spCriteriaValue%";
  1254. } elseif ($spCriteriaModifier == "ends with") {
  1255. $spCriteriaValue = "%$spCriteriaValue";
  1256. } elseif ($spCriteriaModifier == "contains" || $spCriteriaModifier == "does not contain") {
  1257. $spCriteriaValue = "%$spCriteriaValue%";
  1258. } elseif ($spCriteriaModifier == "is in the range") {
  1259. $spCriteriaValue = "$spCriteria >= '$spCriteriaValue' AND $spCriteria <= '$spCriteriaExtra'";
  1260. }
  1261. $spCriteriaModifier = self::$modifier2CriteriaMap[$spCriteriaModifier];
  1262. try {
  1263. if ($spCriteria == "owner_id") {
  1264. $spCriteria = "subj.login";
  1265. }
  1266. if ($i > 0) {
  1267. $qry->addOr($spCriteria, $spCriteriaValue, $spCriteriaModifier);
  1268. } else {
  1269. $qry->add($spCriteria, $spCriteriaValue, $spCriteriaModifier);
  1270. }
  1271. if ($spCriteriaModifier == Criteria::NOT_ILIKE || $spCriteriaModifier == Criteria::NOT_EQUAL) {
  1272. $qry->addOr($spCriteria, null, Criteria::ISNULL);
  1273. }
  1274. } catch (Exception $e) {
  1275. Logging::info($e);
  1276. }
  1277. $i++;
  1278. }
  1279. }
  1280. // check if file exists
  1281. $qry->add("file_exists", "true", Criteria::EQUAL);
  1282. $qry->add("hidden", "false", Criteria::EQUAL);
  1283. $sortTracks = 'random';
  1284. if (isset($storedCrit['sort'])) {
  1285. $sortTracks = $storedCrit['sort']['value'];
  1286. }
  1287. if ($sortTracks == 'newest') {
  1288. $qry->addDescendingOrderByColumn('utime');
  1289. }
  1290. else if ($sortTracks == 'oldest') {
  1291. $qry->addAscendingOrderByColumn('utime');
  1292. }
  1293. else if ($sortTracks == 'random') {
  1294. $qry->addAscendingOrderByColumn('random()');
  1295. } else {
  1296. Logging::warning("Unimplemented sortTracks type in ".__FILE__);
  1297. }
  1298. }
  1299. // construct limit restriction
  1300. $limits = array();
  1301. if (isset($storedCrit['limit'])) {
  1302. if ($storedCrit['limit']['modifier'] == "items") {
  1303. $limits['time'] = 1440 * 60;
  1304. $limits['items'] = $storedCrit['limit']['value'];
  1305. } else {
  1306. $limits['time'] = $storedCrit['limit']['modifier'] == "hours" ?
  1307. intval(floatval($storedCrit['limit']['value']) * 60 * 60) :
  1308. intval($storedCrit['limit']['value'] * 60);
  1309. $limits['items'] = null;
  1310. }
  1311. }
  1312. $repeatTracks = 0;
  1313. if (isset($storedCrit['repeat_tracks'])) {
  1314. $repeatTracks = $storedCrit['repeat_tracks']['value'];
  1315. }
  1316. try {
  1317. $out = $qry->setFormatter(ModelCriteria::FORMAT_ON_DEMAND)->find();
  1318. return array("files"=>$out, "limit"=>$limits, "repeat_tracks"=> $repeatTracks, "count"=>$out->count());
  1319. } catch (Exception $e) {
  1320. Logging::info($e);
  1321. }
  1322. }
  1323. public static function organizeSmartPlaylistCriteria($p_criteria)
  1324. {
  1325. $fieldNames = array('sp_criteria_field', 'sp_criteria_modifier', 'sp_criteria_value', 'sp_criteria_extra');
  1326. $output = array();
  1327. foreach ($p_criteria as $ele) {
  1328. $index = strrpos($ele['name'], '_');
  1329. /* Strip field name of modifier index
  1330. * Ex: sp_criteria_field_0_0 -> sp_criteria_field_0
  1331. */
  1332. $fieldName = substr($ele['name'], 0, $index);
  1333. // Get criteria row index.
  1334. $tempName = $ele['name'];
  1335. // Get the last digit in the field name
  1336. preg_match('/^\D*(?=\d)/', $tempName, $r);
  1337. if (isset($r[0])) {
  1338. $critIndexPos = strlen($r[0]);
  1339. $critIndex = $tempName[$critIndexPos];
  1340. }
  1341. $lastChar = substr($ele['name'], -1);
  1342. // If lastChar is an integer we should strip it off
  1343. if (!preg_match("/^[a-zA-Z]$/", $lastChar)) {
  1344. /* Strip field name of criteria index
  1345. * Ex: sp_criteria_field_0 -> sp_criteria_field
  1346. * We do this to check if the field name is a criteria
  1347. * or the block type
  1348. */
  1349. $n = strrpos($fieldName, '_');
  1350. $fieldName = substr($fieldName, 0, $n);
  1351. }
  1352. if (in_array($fieldName, $fieldNames)) {
  1353. $output['criteria'][$critIndex][$lastChar][$fieldName] = trim($ele['value']);
  1354. } else {
  1355. $output['etc'][$ele['name']] = $ele['value'];
  1356. }
  1357. }
  1358. return $output;
  1359. }
  1360. public static function getAllBlockFiles()
  1361. {
  1362. $sql = <<<SQL
  1363. SELECT distinct(file_id)
  1364. FROM cc_blockcontents
  1365. SQL;
  1366. $files = Application_Common_Database::prepareAndExecute($sql, array());
  1367. $real_files = array();
  1368. foreach ($files as $f) {
  1369. $real_files[] = $f['file_id'];
  1370. }
  1371. return $real_files;
  1372. }
  1373. // smart block functions end
  1374. }
  1375. class BlockNotFoundException extends Exception {}
  1376. class BlockNoPermissionException extends Exception {}
  1377. class BlockOutDatedException extends Exception {}
  1378. class BlockDyanmicException extends Exception {}