search category dropdown adde to the interface
[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                 if ($this->db === NULL)
15                 {
16                         $this->load->library('singleton_db');
17                         $this->db = $this->singleton_db->connect();
18                 }
19         }
20         
21         /**
22          * Retrieves information about a set of videos which are going to be
23          * displayed in the catalog.
24          *
25          * @param               int $category_id        DB category ID
26          * @param               int $offset
27          * @param               int $count
28          * @param               string $ordering        control videos ording by these
29          * possibilities:
30          * <ul>
31          *   <li><strong>'hottest':</strong> newest most appreciated first. An
32          *   appreciated video is one which has a bigger
33          *   score = views + likes - dislikes.</li>
34          *   <li><strong>'newest':</strong> newest first.</li>
35          *   <li><strong>'alphabetically':</strong> sort alphabetically.</li>
36          * </ul>
37          * @return              array   a list of videos, each one being an assoc array with:
38          * <ul>
39          *   <li>id, name, title, duration, thumbs_count, default_thumb, views => from DB</li>
40          *   <li>shorted_title => ellipsized title</li>
41          *   <li>video_url => P2P-Tube video URl</li>
42          *   <li>TODO: user_id, user_name</li>
43          *   <li>thumbs => thumbnail images' URLs</li>
44          * </ul>
45          */
46         public function get_videos_summary($category_id, $offset, $count, $ordering = 'hottest')
47         {
48                 $this->load->helper('text');
49                 
50                 // Ordering
51                 switch ($ordering)
52                 {
53                 case 'hottest':
54                         $order_statement = "ORDER BY date DESC, score DESC, RAND()";
55                         break;
56                 case 'newest':
57                         $order_statement = "ORDER BY date DESC";
58                         break;
59                 case 'alphabetically':
60                         $order_statement = "ORDER BY title";
61                         break;
62                         
63                 default:
64                         $order_statement = "";
65                 }
66                 
67                 $query = $this->db->query(
68                         "SELECT id, name, title, duration, user_id, views, thumbs_count,
69                                 default_thumb, (views + likes - dislikes) AS score
70                         FROM `videos`
71                         WHERE category_id = ?
72                         $order_statement
73                         LIMIT ?, ?", 
74                         array(intval($category_id), $offset, $count)); 
75                 
76                 if ($query->num_rows() > 0)
77                         $videos = $query->result_array();
78                 else
79                         return NULL;
80                 
81                 foreach ($videos as & $video)
82                 {
83                         // P2P-Tube Video URL
84                         $video['video_url'] = site_url(sprintf("watch/%d/%s",
85                                 $video['id'], $video['name']));
86                         
87                         // Thumbnails
88                         $video['thumbs'] = $this->get_thumbs($video['name'], 
89                                 $video['thumbs_count']);
90                                 
91                         // Ellipsized title
92                         //$video['shorted_title'] = ellipsize($video['title'], 45, 0.75);
93                         $video['shorted_title'] = character_limiter($video['title'], 50);
94                         
95                         // TODO: user information
96                         $video['user_name'] = 'TODO';
97                 }
98                 
99                 return $videos;
100         }
101         
102         public function get_videos_count($category_id)
103         {
104                 $query = $this->db->query(
105                         'SELECT COUNT(*) count
106                         FROM `videos`
107                         WHERE category_id = ?',
108                         $category_id);
109                 
110                 if ($query->num_rows() > 0)
111                         return $query->row()->count;
112                 
113                 // Error
114                 return NULL;
115         }
116         
117         /**
118          * Retrieves information about a video.
119          *
120          * If $name does not match with the video's `name` from the DB an error is
121          * marked in the key 'err'. If it's NULL it is ignored.
122          *
123          * @access              public
124          * @param               string $id      video's `id` column from `videos` DB table
125          * @param               string $name    video's `name` column from `videos` DB
126          * table. NULL means there is no name provided.
127          * @return              array   an associative list with information about a video
128          * with the following keys:
129          * <ul>
130          *   <li>all columns form DB with some exceptions that are overwritten or new</li>
131          *   <li>content is moved in assets</li>
132          *   <li>assets => list of associative lists where each one represents a</li>
133          * video asset having keys: "src", "res", "par" and "ext". Value of key
134          * "src" is the video torrent formated as
135          * {name}_{format}.{video_ext}.{default_torrent_ext}</li>
136          *   <li>user_name => TODO: user name from `users` table</li>
137          *   <li>category_title => a human-friendly category name</li>
138          *   <li>tags => associative list of "tag => score"</li>
139          *   <li>date => date and time when the video was created</li>
140          *   <li>thumbs => thumbnail images' URLs</li>
141          * </ul>
142          */
143         public function get_video($id, $name = NULL)
144         {
145                 $this->load->helper('video');
146                 
147                 $query = $this->db->query('SELECT * 
148                                                                 FROM `videos` 
149                                                                 WHERE id = ?', $id);
150                 $video = array();
151                 
152                 if ($query->num_rows() > 0)
153                 {
154                         $video = $query->row_array();
155                         if ($name !== NULL && $video['name'] != $name)
156                                 $video['err'] = 'INVALID_NAME';
157                 }
158                 else
159                 {
160                         $video['err'] = 'INVALID_ID';
161                         return $video;
162                 }
163                 
164                 // Convert JSON encoded string to arrays.
165                 $video['assets'] = json_decode($video['formats'], TRUE);
166                 unset($video['formats']);
167                 $video['tags'] = json_decode($video['tags'], TRUE);
168                 asort($video['tags']);
169                 $video['tags'] = array_reverse($video['tags'], TRUE);
170                 
171                 // Sort assets by their megapixels number.
172                 function access_function($a) { return $a['res']; }
173                 function assets_cmp($a, $b) 
174                         { return megapixels_cmp($a, $b, "access_function"); }
175                 usort($video['assets'], "assets_cmp");
176                 
177                 // Torrents
178                 $video['url'] = array();
179                 foreach ($video['assets'] as & $asset)
180                 {
181                         $def = substr($asset['res'], strpos($asset['res'], 'x') + 1) . 'p';
182                         $asset['src'] = site_url('data/torrents/'. $video['name'] . '_'
183                                 . $def . '.'. $asset['ext']
184                                 . '.'. $this->config->item('default_torrent_ext'));
185                 }
186                 
187                 // Category title
188                 $categories = $this->config->item('categories');
189                 $category_name = $categories[ intval($video['category_id']) ];
190                 $video['category_title'] = $category_name ?
191                         $this->lang->line("ui_categ_$category_name") : $category_name;
192                 
193                 // Thumbnails
194                 $video['thumbs'] = $this->get_thumbs($video['name'], $video['thumbs_count']);
195                 
196                 // TODO: user information
197                 $video['user_name'] = 'TODO';
198                 
199                 return $video;
200         }
201         
202         /**
203          * Increment a video parameter from DB: `views`, `likes` or `dislikes`.
204          * 
205          * @param int $id       DB video id
206          * @param string $param DB parameter column name.
207          * @return void
208          */
209         public function inc_video_var($id, $var)
210         {
211                 // TODO error report if query returns FALSE
212                 $this->db->query('UPDATE `videos` '
213                                                 . 'SET `'. $var. '`=`'. $var. '`+1 '
214                                                 . 'WHERE id='. $id); 
215         }
216         
217         public function get_thumbs($name, $count)
218         {
219                 $thumbs = array();
220                 
221                 for ($i=0; $i < $count; $i++)
222                         $thumbs[] = site_url(sprintf("data/thumbs/%s_t%02d.jpg", $name, $i));
223                 
224                 return $thumbs;
225         }
226
227         /**
228          * Searches videos in DB based on a search query string and returns an
229          * associative array of results.
230          * If count is zero the function only return the number of results.
231          * @param string $search_query
232          * @param int $offset
233          * @param int $count
234          * @param int $category_id      if NULL, all categories are searched
235          * @return array        an associative array with the same keys as that from
236          * get_videos_summary's result, but with two additional keys: 
237          * description and date.
238          */
239         public function search_videos($search_query, $offset = 0, $count = 0, 
240                                                                         $category_id = NULL)
241         {
242                 $search_query = trim($search_query);
243                 $search_query = str_replace("'", " ", $search_query);
244                 
245                 // Search word fragments.
246                 // sfc = search fragment condition
247                 $sfc = "( ";
248                 // sfr = serach fragment relevation
249                 $sfr = "( ";
250                 $sep = ' +-*<>()~"';
251                 $fragm = strtok($search_query, $sep);
252                 while ($fragm !== FALSE)
253                 {
254                         $sfc .= "(title LIKE '%$fragm%'
255                                         OR description LIKE '%$fragm%'
256                                         OR tags LIKE '%$fragm%') OR ";
257                         
258                         // Frament relevations are half of boolean relevations such
259                         // that they will appear at the end of the results.
260                         $sfr .= "0.25 * (title LIKE '%$fragm%')
261                                         + 0.1 * (description LIKE '%$fragm%')
262                                         + 0.15 * (tags LIKE '%$fragm%') + ";
263                         
264                         $fragm = strtok($sep);
265                 }
266                 $sfc = substr($sfc, 0, -4) . " )";
267                 $sfr = substr($sfr, 0, -3) . " )";
268                 
269                 if (! $this->is_advanced_search_query($search_query))
270                 {
271                         $search_cond = "MATCH (title, description, tags)
272                                         AGAINST ('$search_query') OR $sfc";
273                         $relevance = "( MATCH (title, description, tags)
274                                         AGAINST ('$search_query') + $sfr ) AS relevance";
275                 }
276                 // boolean mode
277                 else
278                 {
279                         $against = "AGAINST ('$search_query' IN BOOLEAN MODE)";
280                         $search_cond = "( MATCH (title, description, tags)
281                                         $against) OR $sfc";
282                         $relevance = "( 0.5 * (MATCH(title) $against)
283                                         + 0.3 * (MATCH(tags) $against)
284                                         + 0.2 * (MATCH(description) $against)
285                                         + $sfr) AS relevance";
286                 }
287                 
288                 if ($count === 0)
289                 {
290                         $selected_columns = "COUNT(*) count";
291                         $order = "";
292                         $limit = "";
293                 }
294                 else
295                 {
296                         // TODO select data, description if details are needed
297                         $selected_columns = "id, name, title, duration, user_id, views,
298                                         thumbs_count, default_thumb,
299                                         (views + likes - dislikes) AS score, 
300                                         $relevance";
301                         $order = "ORDER BY relevance DESC, score DESC";
302                         $limit = "LIMIT $offset, $count";
303                 }
304                 
305                 if ($category_id !== NULL)
306                         $category_cond = "category_id = '$category_id' AND ";
307                 else
308                         $category_cond = "";
309
310                 $str_query = "SELECT $selected_columns
311                         FROM `videos`
312                         WHERE  $category_cond ( $search_cond )
313                         $order
314                         $limit";
315 //              echo "<p>$str_query</p>";
316                 $query = $this->db->query($str_query);
317                 
318                 if ($query->num_rows() > 0)
319                 {
320                         if ($count === 0)
321                                 return $query->row()->count;
322                         else
323                                 $videos = $query->result_array();
324                 }
325                 else
326                         return NULL;
327                 
328                 $this->load->helper('text');
329                 
330                 foreach ($videos as & $video)
331                 {
332                         // P2P-Tube Video URL
333                         $video['video_url'] = site_url(sprintf("watch/%d/%s",
334                                 $video['id'], $video['name']));
335                         
336                         // Thumbnails
337                         $video['thumbs'] = $this->get_thumbs($video['name'], 
338                                 $video['thumbs_count']);
339                                 
340                         // Ellipsized title
341                         //$video['shorted_title'] = ellipsize($video['title'], 45, 0.75);
342                         $video['shorted_title'] = character_limiter($video['title'], 50);
343                         
344                         // TODO: user information
345                         $video['user_name'] = 'TODO';
346                 }
347                 
348                 return $videos;
349         }
350         
351         public function decode_search_query($search_query)
352         {
353                 $search_query = urldecode($search_query);
354                 
355                 $search_query = str_replace('_AST_', '*', $search_query);
356                 $search_query = str_replace('_AND_', '+', $search_query);
357                 $search_query = str_replace('_GT_', '>', $search_query);
358                 $search_query = str_replace('_LT_', '<', $search_query);
359                 $search_query = str_replace('_PO_', '(', $search_query);
360                 $search_query = str_replace('_PC_', ')', $search_query);
361                 $search_query = str_replace('_LOW_', '~', $search_query);
362                 $search_query = str_replace('_QUO_', '"', $search_query);
363                 
364                 return $search_query;
365         }
366         
367         public function encode_search_query($search_query)
368         {
369                 $search_query = str_replace('*', '_AST_', $search_query);
370                 $search_query = str_replace('+', '_AND_', $search_query);
371                 $search_query = str_replace('>', '_GT_', $search_query);
372                 $search_query = str_replace('<', '_LT_', $search_query);
373                 $search_query = str_replace('(', '_PO_', $search_query);
374                 $search_query = str_replace(')', '_PC_', $search_query);
375                 $search_query = str_replace('~', '_LOW_', $search_query);
376                 $search_query = str_replace('"', '_QUO_', $search_query);
377                 
378                 $search_query = urlencode($search_query);
379         
380                 return $search_query;
381         }
382         
383         /**
384          * Return TRUE if it contains any special caracter from an advanced search
385          * query.
386          * @param string $search_query
387          * @return boolean
388          */
389         public function is_advanced_search_query($search_query)
390         {
391                 return (preg_match('/\*|\+|\-|>|\<|\(|\)|~|"/', $search_query) == 0
392                         ? FALSE : TRUE);
393         }
394 }
395
396 /* End of file videos_model.php */
397 /* Location: ./application/models/videos_model.php */