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