user gets notified interatively or by email in case of a CIS error
[living-lab-site.git] / application / models / videos_model.php
1 <?php
2
3 /**
4  * Class Videos_model models videos information from the DB
5  *
6  * @category    Model
7  * @author              Călin-Andrei Burloiu
8  */
9 class Videos_model extends CI_Model {
10         public $db = NULL;
11         
12         public function __construct()
13         {
14                 parent::__construct();
15                 
16                 if ($this->db === NULL)
17                 {
18                         $this->load->library('singleton_db');
19                         $this->db = $this->singleton_db->connect();
20                 }
21         }
22         
23         /**
24          * Retrieves a set of videos information which can be used for displaying
25          * that videos as a list with few details.
26          *
27          * @param               int $category_id        DB category ID; pass NULL for all
28          * categories
29          * @param mixed $user an user_id (as int) or an username 
30          * (as string); pass NULL for all users
31          * @param int $offset
32          * @param int $count number of videos to retrieve; if set to TRUE this
33          * method will retrieve the number of videos that satisfy condition
34          * @param string $ordering      control videos ording by these
35          * possibilities:
36          * <ul>
37          *   <li><strong>'hottest':</strong> newest most appreciated first. An
38          *   appreciated video is one which has a bigger
39          *   score = views + likes - dislikes.</li>
40          *   <li><strong>'newest':</strong> newest first.</li>
41          *   <li><strong>'alphabetically':</strong> sort alphabetically.</li>
42          * </ul>
43          * @param bool $unactivated whether to retrieve or not ingested unactivated
44          * videos; typically only administrators should see this kind of assets
45          * @return array a list of videos, each one being an assoc array with:
46          * <ul>
47          *   <li>id, name, title, duration, thumbs_count, default_thumb, views => from DB</li>
48          *   <li>shorted_title => ellipsized title</li>
49          *   <li>video_url => P2P-Tube video URl</li>
50          *   <li>user_id, user_name</li>
51          *   <li>thumbs => thumbnail images' URLs</li>
52          * </ul>
53          */
54         public function get_videos_summary($category_id, $user, $offset, $count,
55                 $ordering = 'hottest', $unactivated = FALSE)
56         {
57                 $this->load->helper('text');
58                 
59                 $order_statement = "";
60                 if ($count !== TRUE)
61                 {
62                         // Ordering
63                         switch ($ordering)
64                         {
65                         case 'hottest':
66                                 $order_statement = "ORDER BY date DESC, score DESC, RAND()";
67                                 break;
68                         case 'newest':
69                                 $order_statement = "ORDER BY date DESC";
70                                 break;
71                         case 'alphabetically':
72                                 $order_statement = "ORDER BY title";
73                                 break;
74
75                         default:
76                                 $order_statement = "";
77                         }
78                 }
79                 
80                 // Show unactivated videos.
81                 $cond_unactivated = ($unactivated
82                                 ? '(a.activation_code IS NULL OR a.activation_code IS NOT NULL
83                                         AND a.cis_response = '. CIS_RESP_COMPLETION. ')'
84                                 : 'a.activation_code IS NULL');
85                 
86                 // Category filtering
87                 if ($category_id === NULL)
88                         $cond_category = "1";
89                 else
90                 {
91                         $category_id = intval($category_id);
92                         $cond_category = "category_id = $category_id";
93                 }
94                 
95                 // User filtering
96                 if ($user === NULL)
97                         $cond_user = "1";
98                 else
99                 {
100                         if (is_int($user))
101                                 $cond_user = "v.user_id = $user";
102                         else if (is_string($user))
103                                 $cond_user = "u.username = '$user'";
104                 }
105                 
106                 if ($count === TRUE)
107                         $fields = "COUNT(*) count";
108                 else
109                         $fields = "v.id, name, title, duration, user_id, u.username, views,
110                                 thumbs_count, default_thumb,
111                                 (views + likes - dislikes) AS score,
112                                 a.activation_code, a.cis_response";
113                 
114                 $query = $this->db->query(
115                         "SELECT $fields
116                         FROM `videos` v 
117                                 LEFT JOIN `videos_unactivated` a ON (id = a.video_id),
118                                 `users` u
119                         WHERE v.user_id = u.id AND $cond_category AND $cond_user
120                                 AND $cond_unactivated
121                         $order_statement
122                         LIMIT $offset, $count"); 
123                 
124                 if ($query->num_rows() > 0)
125                 {
126                         if ($count === TRUE)
127                                 return $query->row()->count;
128                         
129                         $videos = $query->result_array();
130                 }
131                 else
132                         return array();
133                 
134                 foreach ($videos as & $video)
135                 {
136                         // P2P-Tube Video URL
137                         $video['video_url'] = site_url(sprintf("watch/%d/%s",
138                                 $video['id'], $video['name']));
139                         
140                         // Thumbnails
141                         $video['thumbs'] = $this->get_thumbs($video['name'], 
142                                 $video['thumbs_count']);
143                         $video['json_thumbs'] = json_encode($video['thumbs']);
144                                 
145                         // Ellipsized title
146                         //$video['shorted_title'] = ellipsize($video['title'], 45, 0.75);
147                         $video['shorted_title'] = character_limiter($video['title'], 50);
148                 }
149                 
150                 return $videos;
151         }
152         
153         /**
154          * Returns the number of videos from database from a specific category or
155          * user.
156          * NULL parameters count videos from all categories and / or all users.
157          * 
158          * @param int $category_id
159          * @param mixed $user an user_id (as int) or an username (as string)
160          * @return int number of videos or FALSE if an error occured
161          */
162         public function get_videos_count($category_id = NULL, $user = NULL,
163                         $unactivated = FALSE)
164         {
165                 return $this->get_videos_summary($category_id, $user, 0, TRUE, NULL,
166                                 $unactivated);
167         }
168         
169         /**
170          * Retrieves information about a video.
171          *
172          * If $name does not match with the video's `name` from the DB an error is
173          * marked in the key 'err'. If it's NULL it is ignored.
174          *
175          * @access public
176          * @param string $id    video's `id` column from `videos` DB table
177          * @param string $name  video's `name` column from `videos` DB
178          * table. NULL means there is no name provided.
179          * @return array        an associative list with information about a video
180          * with the following keys:
181          * <ul>
182          *   <li>all columns form DB with some exceptions that are overwritten or new</li>
183          *   <li>content is moved in assets</li>
184          *   <li>assets => list of associative lists where each one represents a</li>
185          * video asset having keys: "src", "res", "par" and "ext". Value of key
186          * "src" is the video torrent formated as
187          * {name}_{format}.{video_ext}.{default_torrent_ext}</li>
188          *   <li>username => user name from `users` table</li>
189          *   <li>category_title => a human-friendly category name</li>
190          *   <li>tags => associative list of "tag => score"</li>
191          *   <li>date => date and time when the video was created</li>
192          *   <li>thumbs => thumbnail images' URLs</li>
193          * </ul>
194          */
195         public function get_video($id, $name = NULL)
196         {
197                 $this->load->helper('video');
198                 $this->load->helper('text');
199                 
200                 $query = $this->db->query("SELECT v.*, u.username,
201                                         a.activation_code, a.cis_response
202                                 FROM `videos` v 
203                                         LEFT JOIN `videos_unactivated` a ON (v.id = a.video_id),
204                                         `users` u
205                                 WHERE v.user_id = u.id AND v.id = $id");
206                 $video = array();
207                 
208                 if ($query->num_rows() > 0)
209                 {
210                         $video = $query->row_array();
211                         if ($name !== NULL && $video['name'] != $name)
212                                 $video['err'] = 'INVALID_NAME';
213                 }
214                 else
215                 {
216                         return FALSE;
217                 }
218                 
219                 // Convert JSON encoded string to arrays.
220                 $video['assets'] = json_decode($video['formats'], TRUE);
221                 unset($video['formats']);
222                 $video['tags'] = json_decode($video['tags'], TRUE);
223                 asort($video['tags']);
224                 $video['tags'] = array_reverse($video['tags'], TRUE);
225                 
226                 // Sort assets by their megapixels number.
227                 function access_function($a) { return $a['res']; }
228                 function assets_cmp($a, $b) 
229                         { return megapixels_cmp($a, $b, "access_function"); }
230                 usort($video['assets'], "assets_cmp");
231                 
232                 // Torrents
233                 $video['url'] = array();
234                 foreach ($video['assets'] as & $asset)
235                 {
236                         $def = substr($asset['res'], strpos($asset['res'], 'x') + 1) . 'p';
237                         $asset['def'] = $def;
238                         $asset['src'] = site_url('data/torrents/'. $video['name'] . '_'
239                                 . $def . '.'. $asset['ext']
240                                 . '.'. $this->config->item('default_torrent_ext'));
241                 }
242                 
243                 // Category title
244                 $categories = $this->config->item('categories');
245                 $category_name = $categories[ intval($video['category_id']) ];
246                 $video['category_title'] = $category_name ?
247                         $this->lang->line("ui_categ_$category_name") : $category_name;
248                 
249                 // Thumbnails
250                 $video['thumbs'] = $this->get_thumbs($video['name'], $video['thumbs_count']);
251                 
252                 // Shorted description
253                 $video['shorted_description'] = character_limiter(
254                                 $video['description'], 128);
255                 
256                 return $video;
257         }
258         
259         /**
260          * Adds a new uploaded video to the DB.
261          * 
262          * @param string $name
263          * @param string $title
264          * @param string $description
265          * @param string $tags comma separated tags
266          * @param string $duration video duration formatted [HH:]mm:ss
267          * @param array $formats a dictionary corresponding to `formats` JSON
268          * column from `videos` table
269          * @param int $category_id
270          * @param int $user_id
271          * @param string $uploaded_file the raw video file uploaded by the user
272          * @return mixed returns an activation code on success or FALSE otherwise
273          */
274         public function add_video($name, $title, $description, $tags, $duration,
275                         $formats, $category_id, $user_id, $uploaded_file)
276         {
277                 $this->load->config('content_ingestion');
278                 
279                 // Tags.
280                 $json_tags = array();
281                 $tok = strtok($tags, ',');
282                 while ($tok != FALSE)
283                 {
284                         $json_tags[trim($tok)] = 0;
285                         
286                         $tok = strtok(',');
287                 }
288                 $json_tags = json_encode($json_tags);
289                 
290                 $json_formats = json_encode($formats);
291                 
292                 // Thumbnail images
293                 $thumbs_count = $this->config->item('thumbs_count');
294                 $default_thumb = rand(0, $thumbs_count - 1);
295                 
296                 $query = $this->db->query("INSERT INTO `videos`
297                                 (name, title, description, duration, formats, category_id,
298                                                 user_id, tags, date, thumbs_count, default_thumb)
299                                 VALUES ('$name', '$title', '$description', '$duration',
300                                                 '$json_formats', $category_id,
301                                                 $user_id, '$json_tags', utc_timestamp(),
302                                                 $thumbs_count, $default_thumb)");
303                 if ($query === FALSE)
304                         return FALSE;
305                 
306                 // Find out the id of the new video added.
307                 $query = $this->db->query("SELECT id from `videos`
308                                 WHERE name = '$name'");
309                 if ($query->num_rows() === 0)
310                         return FALSE;
311                 $video_id = $query->row()->id;
312                 
313                 // Activation code.
314                 $activation_code = Videos_model::gen_activation_code();
315                 
316                 $query = $this->db->query("INSERT INTO `videos_unactivated`
317                                 (video_id, activation_code, uploaded_file)
318                                 VALUES ($video_id, '$activation_code', '$uploaded_file')");
319                 
320                 return $activation_code;
321         }
322         
323         /**
324          * Request content_ingest to the CIS in order to start the content
325          * ingestion process.
326          * 
327          * @param string $activation_code
328          * @param string $raw_video_fn uploaded video file name
329          * @param string $name
330          * @param int $raw_video_size uploaded video file size in bytes
331          * @param array $transcode_configs dictionary which must be included in
332          * the JSON data that needs to be sent to CIS
333          * @return mixed return the HTTP content (body) on success and FALSE
334          * otherwise
335          */
336         public function send_content_ingestion($activation_code, $raw_video_fn,
337                         $name, $raw_video_size, $transcode_configs)
338         {
339                 $this->config->load('content_ingestion');
340                 
341                 $url = $this->config->item('cis_url') . 'ingest_content';
342                 $data = array(
343                         'code'=>$activation_code,
344                         'raw_video'=>$raw_video_fn,
345                         'name'=>$name,
346                         'weight'=>$raw_video_size,
347                         'transcode_configs'=>$transcode_configs,
348                         'thumbs'=>$this->config->item('thumbs_count')
349                 );
350                 $json_data = json_encode($data);
351                 
352                 // Send request to CIS.
353                 $r = new HttpRequest($url, HttpRequest::METH_POST);
354                 $r->setBody($json_data);
355                 try
356                 {
357                         $response = $r->send()->getBody();
358                 }
359                 catch (HttpException $ex) 
360                 {
361                         return FALSE;
362                 }
363                 
364                 return $response;
365         }
366         
367         public function set_cis_response($activation_code,
368                         $response = CIS_RESP_COMPLETION)
369         {
370                 return $this->db->query("UPDATE `videos_unactivated`
371                         SET cis_response = $response
372                         WHERE activation_code = '$activation_code'");
373         }
374         
375         public function send_upload_error_email($activation_code,
376                         $cis_response = CIS_RESP_INTERNAL_ERROR)
377         {
378                 $query = $this->db->query("SELECT v.title, u.email
379                         FROM `videos_unactivated` a, `videos` v, `users` u
380                         WHERE a.activation_code = '$activation_code'
381                                 AND a.video_id = v.id AND v.user_id = u.id");
382                 
383                 if ($query->num_rows() > 0)
384                 {
385                         $title = $query->row()->title;
386                         $email = $query->row()->email;
387                 }
388                 else
389                         return FALSE;
390                 
391                 $subject = '['. $this->config->item('site_name')
392                                 . '] Upload Error';
393                 if ($cis_response == CIS_RESP_INTERNAL_ERROR)
394                 {
395                         $msg = sprintf($this->lang->line(
396                                         'video_internal_cis_error_email_content'), $title);
397                 }
398                 else if ($cis_response == CIS_RESP_UNREACHABLE)
399                 {
400                         $msg = sprintf($this->lang->line(
401                                         'video_unreachable_cis_error_email_content'), $title);
402                 }
403                 $headers = "From: ". $this->config->item('noreply_email');
404                 
405                 return mail($email, $subject, $msg, $headers);
406         }
407         
408         /**
409          * Activates a video by deleting its entry from `videos_unactivated`.
410          * 
411          * @param mixed $code_or_id use type string for activation_code or type
412          * int for video_id
413          * @return boolean TRUE on success, FALSE otherwise
414          */
415         public function activate_video($code_or_id)
416         {
417                 if (is_string($code_or_id))
418                         $query = $this->db->query("SELECT uploaded_file from `videos_unactivated`
419                                 WHERE activation_code = '$code_or_id'");
420                 else if (is_int($code_or_id))
421                         $query = $this->db->query("SELECT uploaded_file from `videos_unactivated`
422                                 WHERE video_id = '$code_or_id'");
423                 else
424                         return FALSE;
425                 
426                 if ($query->num_rows() > 0)
427                         $uploaded_file = $query->row()->uploaded_file;
428                 else
429                         return FALSE;
430                 
431                 if (is_string($code_or_id))
432                         $query = $this->db->query("DELETE FROM `videos_unactivated`
433                                 WHERE activation_code = '$code_or_id'");
434                 else if (is_int($code_or_id))
435                         $query = $this->db->query("DELETE FROM `videos_unactivated`
436                                 WHERE video_id = '$code_or_id'");
437                 else
438                         return FALSE;
439                 
440                 if (!$query)
441                         return $query;
442                 
443                 return unlink("data/upload/$uploaded_file");            
444         }
445         
446         public function get_unactivated_videos()
447         {
448                 $query = $this->db->query("SELECT a.video_id, v.name, a.cis_response
449                         FROM `videos_unactivated` a, `videos` v
450                         WHERE a.video_id = v.id AND a.cis_response = "
451                                 . CIS_RESP_COMPLETION);
452                 
453                 if ($query->num_rows() > 0)
454                         return $query->result_array();
455                 else
456                         return FALSE;
457         }
458         
459         /**
460          * Retrieves comments for a video.
461          * 
462          * @param int $video_id
463          * @param int $offset
464          * @param int $count
465          * @param string $ordering      control comments ording by these possibilities:
466          * <ul>
467          *   <li><strong>'hottest':</strong> newest most appreciated first. An
468          *   appreciated comment is one which has a bigger
469          *   score = likes - dislikes.</li>
470          *   <li><strong>'newest':</strong> newest first.</li>
471          * </ul>
472          * @return array        an array with comments
473          */
474         public function get_video_comments($video_id, $offset, $count,
475                         $ordering = 'newest')
476         {
477                 $this->load->helper('date');
478                 $cond_hottest = '';
479                 
480                 // Ordering
481                 switch ($ordering)
482                 {
483                 case 'newest':
484                         $order_statement = "ORDER BY time DESC";
485                         break;
486                 case 'hottest':
487                         $order_statement = "ORDER BY score DESC, time DESC";
488                         $cond_hottest = "AND c.likes + c.dislikes > 0";
489                         break;
490                                 
491                 default:
492                         $order_statement = "";
493                 }
494                 
495                 $query = $this->db->query(
496                         "SELECT c.*, u.username, u.time_zone, (c.likes + c.dislikes) AS score
497                                 FROM `videos_comments` c, `users` u
498                                 WHERE c.user_id = u.id AND video_id = $video_id $cond_hottest
499                                 $order_statement
500                                 LIMIT $offset, $count");
501                 
502                 if ($query->num_rows() == 0)
503                         return array();
504                 
505                 $comments = $query->result_array();
506                 
507                 foreach ($comments as & $comment)
508                 {
509                         $comment['local_time'] = human_gmt_to_human_local($comment['time'],
510                                 $comment['time_zone']);
511                 }
512                 
513                 return $comments;
514         }
515         
516         public function get_video_comments_count($video_id)
517         {
518                 $query = $this->db->query(
519                                         "SELECT COUNT(*) count
520                                                 FROM `videos_comments`
521                                                 WHERE video_id = $video_id");
522                                 
523                 if ($query->num_rows() == 0)
524                         return FALSE;
525                 
526                 return $query->row()->count;
527         }
528         
529         /**
530          * Insert in DB a comment for a video.
531          * 
532          * @param int $video_id
533          * @param int $user_id
534          * @param string $content
535          */
536         public function comment_video($video_id, $user_id, $content)
537         {
538                 // Prepping content.
539                 $content = substr($content, 0, 512);
540                 $content = htmlspecialchars($content);
541                 $content = nl2br($content);
542                 
543                 return $query = $this->db->query(
544                         "INSERT INTO `videos_comments` (video_id, user_id, content, time)
545                         VALUES ($video_id, $user_id, '$content', UTC_TIMESTAMP())");
546         }
547         
548         /**
549          * Increments views count for a video.
550          * 
551          * @param int $id       DB video id
552          * @return void
553          */
554         public function inc_views($id)
555         {
556                 return $this->db->query('UPDATE `videos` '
557                                                 . 'SET `views`=`views`+1 '
558                                                 . 'WHERE id='. $id); 
559         }
560         
561         public function vote($video_id, $user_id, $like = TRUE)
562         {
563                 if ($like)
564                 {
565                         $col = 'likes';
566                         $action = 'like';
567                 }
568                 else
569                 {
570                         $col = 'dislikes';
571                         $action = 'dislike';
572                 }
573                 
574                 $query = $this->db->query("SELECT * FROM `users_actions`
575                         WHERE user_id = $user_id
576                                 AND target_id = $video_id
577                                 AND target_type = 'video'
578                                 AND action = '$action'
579                                 AND date = CURDATE()");
580                 // User already voted today
581                 if ($query->num_rows() > 0)
582                         return -1;
583                 
584                 $this->db->query("UPDATE `videos`
585                         SET $col = $col + 1
586                         WHERE id = $video_id");
587                 
588                 // Mark this action so that the user cannot repeat it today.
589                 $this->db->query("INSERT INTO `users_actions`
590                                 (user_id, action, target_type, target_id, date)
591                         VALUES ( $user_id, '$action', 'video', $video_id, CURDATE() )");
592                 
593                 $query = $this->db->query("SELECT $col FROM `videos`
594                         WHERE id = $video_id");
595                 
596                 if ($query->num_rows() === 1)
597                 {
598                         $row = $query->row_array();
599                         return $row[ $col ];
600                 }
601                 
602                 // Error
603                 return FALSE;
604         }
605         
606         public function vote_comment($comment_id, $user_id, $like = TRUE)
607         {
608                 if ($like)
609                 {
610                         $col = 'likes';
611                         $action = 'like';
612                 }
613                 else
614                 {
615                         $col = 'dislikes';
616                         $action = 'dislike';
617                 }
618         
619                 $query = $this->db->query("SELECT * FROM `users_actions`
620                                 WHERE user_id = $user_id
621                                         AND target_id = $comment_id
622                                         AND target_type = 'vcomment'
623                                         AND action = '$action'
624                                         AND date = CURDATE()");
625                 // User already voted today
626                 if ($query->num_rows() > 0)
627                         return -1;
628         
629                 $this->db->query("UPDATE `videos_comments`
630                                 SET $col = $col + 1
631                                 WHERE id = $comment_id");
632         
633                 // Mark this action so that the user cannot repeat it today.
634                 $this->db->query("INSERT INTO `users_actions`
635                                         (user_id, action, target_type, target_id, date)
636                                 VALUES ( $user_id, '$action', 'vcomment', $comment_id, CURDATE() )");
637         
638                 $query = $this->db->query("SELECT $col FROM `videos_comments`
639                                 WHERE id = $comment_id");
640         
641                 if ($query->num_rows() === 1)
642                 {
643                         $row = $query->row_array();
644                         return $row[ $col ];
645                 }
646         
647                 // Error
648                 return FALSE;
649         }
650         
651         public function get_thumbs($name, $count)
652         {
653                 $thumbs = array();
654                 
655                 for ($i=0; $i < $count; $i++)
656                         $thumbs[] = site_url(sprintf("data/thumbs/%s_t%02d.jpg", $name, $i));
657                 
658                 return $thumbs;
659         }
660
661         /**
662          * Searches videos in DB based on a search query string and returns an
663          * associative array of results.
664          * If count is zero the function only return the number of results.
665          * @param string $search_query
666          * @param int $offset
667          * @param int $count
668          * @param int $category_id      if NULL, all categories are searched
669          * @return array        an associative array with the same keys as that from
670          * get_videos_summary's result, but with two additional keys: 
671          * description and date.
672          */
673         public function search_videos($search_query, $offset = 0, $count = 0, 
674                                                                         $category_id = NULL)
675         {
676                 $search_query = trim($search_query);
677                 $search_query = str_replace("'", " ", $search_query);
678                 
679                 // Search word fragments.
680                 // sfc = search fragment condition
681                 $sfc = "( ";
682                 // sfr = search fragment relevance
683                 $sfr = "( ";
684                 $sep = ' +-*<>()~"';
685                 $fragm = strtok($search_query, $sep);
686                 while ($fragm !== FALSE)
687                 {
688                         $sfc .= "(title LIKE '%$fragm%'
689                                         OR description LIKE '%$fragm%'
690                                         OR tags LIKE '%$fragm%') OR ";
691                         
692                         // Frament relevances are half of boolean relevances such
693                         // that they will appear at the end of the results.
694                         $sfr .= "0.25 * (title LIKE '%$fragm%')
695                                         + 0.1 * (description LIKE '%$fragm%')
696                                         + 0.15 * (tags LIKE '%$fragm%') + ";
697                         
698                         $fragm = strtok($sep);
699                 }
700                 $sfc = substr($sfc, 0, -4) . " )";
701                 $sfr = substr($sfr, 0, -3) . " )";
702                 
703                 if (! $this->is_advanced_search_query($search_query))
704                 {
705                         $search_cond = "MATCH (title, description, tags)
706                                         AGAINST ('$search_query') OR $sfc";
707                         $relevance = "( MATCH (title, description, tags)
708                                         AGAINST ('$search_query') + $sfr ) AS relevance";
709                 }
710                 // boolean mode
711                 else
712                 {
713                         $against = "AGAINST ('$search_query' IN BOOLEAN MODE)";
714                         $search_cond = "( MATCH (title, description, tags)
715                                         $against) OR $sfc";
716                         $relevance = "( 0.5 * (MATCH(title) $against)
717                                         + 0.3 * (MATCH(tags) $against)
718                                         + 0.2 * (MATCH(description) $against)
719                                         + $sfr) AS relevance";
720                 }
721                 
722                 if ($count === 0)
723                 {
724                         $selected_columns = "COUNT(*) count";
725                         $order = "";
726                         $limit = "";
727                 }
728                 else
729                 {
730                         // TODO select data, description if details are needed
731                         $selected_columns = "v.id, name, title, duration, user_id, views,
732                                         thumbs_count, default_thumb, u.username,
733                                         (views + likes - dislikes) AS score, 
734                                         $relevance";
735                         $order = "ORDER BY relevance DESC, score DESC";
736                         $limit = "LIMIT $offset, $count";
737                 }
738                 
739                 if ($category_id !== NULL)
740                         $category_cond = "category_id = '$category_id' AND ";
741                 else
742                         $category_cond = "";
743
744                 $str_query = "SELECT $selected_columns
745                         FROM `videos` v, `users` u
746                         WHERE  v.user_id = u.id AND $category_cond ( $search_cond )
747                         $order
748                         $limit";
749 //              echo "<p>$str_query</p>";
750                 $query = $this->db->query($str_query);
751                 
752                 if ($query->num_rows() > 0)
753                 {
754                         if ($count === 0)
755                                 return $query->row()->count;
756                         else
757                                 $videos = $query->result_array();
758                 }
759                 else
760                         return NULL;
761                 
762                 $this->load->helper('text');
763                 
764                 foreach ($videos as & $video)
765                 {
766                         // P2P-Tube Video URL
767                         $video['video_url'] = site_url(sprintf("watch/%d/%s",
768                                 $video['id'], $video['name']));
769                         
770                         // Thumbnails
771                         $video['thumbs'] = $this->get_thumbs($video['name'], 
772                                 $video['thumbs_count']);
773                         $video['json_thumbs'] = json_encode($video['thumbs']);
774                                 
775                         // Ellipsized title
776                         //$video['shorted_title'] = ellipsize($video['title'], 45, 0.75);
777                         $video['shorted_title'] = character_limiter($video['title'], 50);
778                         
779                         // TODO: user information
780                         $video['user_name'] = 'TODO';
781                 }
782                 
783                 return $videos;
784         }
785         
786         public function decode_search_query($search_query)
787         {
788                 $search_query = urldecode($search_query);
789                 
790                 $search_query = str_replace('_AST_', '*', $search_query);
791                 $search_query = str_replace('_AND_', '+', $search_query);
792                 $search_query = str_replace('_GT_', '>', $search_query);
793                 $search_query = str_replace('_LT_', '<', $search_query);
794                 $search_query = str_replace('_PO_', '(', $search_query);
795                 $search_query = str_replace('_PC_', ')', $search_query);
796                 $search_query = str_replace('_LOW_', '~', $search_query);
797                 $search_query = str_replace('_QUO_', '"', $search_query);
798                 
799                 return $search_query;
800         }
801         
802         public function encode_search_query($search_query)
803         {
804                 $search_query = str_replace('*', '_AST_', $search_query);
805                 $search_query = str_replace('+', '_AND_', $search_query);
806                 $search_query = str_replace('>', '_GT_', $search_query);
807                 $search_query = str_replace('<', '_LT_', $search_query);
808                 $search_query = str_replace('(', '_PO_', $search_query);
809                 $search_query = str_replace(')', '_PC_', $search_query);
810                 $search_query = str_replace('~', '_LOW_', $search_query);
811                 $search_query = str_replace('"', '_QUO_', $search_query);
812                 
813                 $search_query = urlencode($search_query);
814         
815                 return $search_query;
816         }
817         
818         /**
819          * Return TRUE if it contains any special caracter from an advanced search
820          * query.
821          * @param string $search_query
822          * @return boolean
823          */
824         public function is_advanced_search_query($search_query)
825         {
826                 return (preg_match('/\*|\+|\-|>|\<|\(|\)|~|"/', $search_query) == 0
827                         ? FALSE : TRUE);
828         }
829         
830         public static function gen_activation_code()
831         {
832                 $ci =& get_instance();
833                 
834                 $activation_code = substr(
835                         sha1($ci->config->item('encryption_key')
836                                 . mt_rand()),
837                         0,
838                         16);
839                 
840                 return $activation_code;
841         }
842 }
843
844 /* End of file videos_model.php */
845 /* Location: ./application/models/videos_model.php */