Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
| 1 | <?php |
||
| 10 | class VideoLibrary |
||
| 11 | { |
||
| 12 | |||
| 13 | const GENRE_TYPE_MOVIE = 'movie'; |
||
| 14 | const GENRE_TYPE_TVSHOW = 'tvshow'; |
||
| 15 | const SORT_ORDER_ASCENDING = 'ascending'; |
||
| 16 | |||
| 17 | /** |
||
| 18 | * @var string[] default properties for movies |
||
| 19 | */ |
||
| 20 | private static $_defaultMovieProperties = array( |
||
| 21 | 'year', 'genre', 'thumbnail', 'art', 'rating', 'runtime', 'playcount', 'dateadded', 'premiered' |
||
| 22 | ); |
||
| 23 | |||
| 24 | /** |
||
| 25 | * @var string[] default properties for TV shows |
||
| 26 | */ |
||
| 27 | private static $_defaultTVShowProperties = array( |
||
| 28 | 'year', 'genre', 'thumbnail', 'art', 'playcount', 'premiered', |
||
| 29 | ); |
||
| 30 | |||
| 31 | /** |
||
| 32 | * @var string[] default properties for episodes |
||
| 33 | */ |
||
| 34 | private static $_defaultEpisodeProperties = array( |
||
| 35 | 'plot', 'runtime', 'season', 'episode', 'streamdetails', 'thumbnail', |
||
| 36 | 'file', 'title', 'tvshowid', 'showtitle', 'playcount', |
||
| 37 | ); |
||
| 38 | |||
| 39 | /** |
||
| 40 | * Returns all genres available for the specified media type (default to |
||
| 41 | * movie genres). |
||
| 42 | * @param string $type the media type (see class constants) |
||
| 43 | * @return array the list of genres |
||
| 44 | */ |
||
| 45 | public static function getGenres($type = self::GENRE_TYPE_MOVIE) |
||
| 46 | { |
||
| 47 | $response = Yii::app()->xbmc->performRequest('VideoLibrary.GetGenres', array( |
||
| 48 | 'type'=>$type, 'sort'=>array( |
||
| 49 | 'order'=>self::SORT_ORDER_ASCENDING, 'method'=>'label'))); |
||
| 50 | |||
| 51 | return self::normalizeResponse($response, 'genres', array()); |
||
| 52 | } |
||
| 53 | |||
| 54 | /** |
||
| 55 | * Returns a list of all actors. |
||
| 56 | * @param string $mediaType the media type to fetch actors for (movies or |
||
| 57 | * TV shows) |
||
| 58 | * @return Actor[] the actors |
||
| 59 | */ |
||
| 60 | public static function getActors($mediaType) |
||
| 61 | { |
||
| 62 | // Fetch the list of all works |
||
| 63 | $works = array(); |
||
| 64 | |||
| 65 | if ($mediaType === Actor::MEDIA_TYPE_MOVIE) |
||
| 66 | $works = VideoLibrary::getMovies(array('properties'=>array('cast'))); |
||
| 67 | elseif ($mediaType === Actor::MEDIA_TYPE_TVSHOW) |
||
| 68 | $works = VideoLibrary::getTVShows(array('properties'=>array('cast'))); |
||
| 69 | |||
| 70 | // Build a list of all unique actors |
||
| 71 | $actors = array(); |
||
| 72 | |||
| 73 | foreach ($works as $work) |
||
| 74 | $actors = array_merge($actors, $work->cast); |
||
| 75 | |||
| 76 | // array_unique compares by string |
||
| 77 | return array_unique($actors); |
||
| 78 | } |
||
| 79 | |||
| 80 | /** |
||
| 81 | * @return array list of all movie directors |
||
| 82 | */ |
||
| 83 | public static function getDirectors() |
||
| 84 | { |
||
| 85 | // Fetch the list of all movies |
||
| 86 | $movies = VideoLibrary::getMovies(array('properties'=>array('director'))); |
||
| 87 | $directors = array(); |
||
| 88 | |||
| 89 | foreach ($movies as $movie) |
||
| 90 | $directors = array_merge($directors, $movie->director); |
||
| 91 | |||
| 92 | // We want this to be an array with just values, the keys don't matter |
||
| 93 | return array_values(array_unique($directors)); |
||
| 94 | } |
||
| 95 | |||
| 96 | /** |
||
| 97 | * Returns a list of movies |
||
| 98 | * @param array $params request parameters |
||
| 99 | * @return Movie[] the movies |
||
| 100 | */ |
||
| 101 | public static function getMovies($params = array()) |
||
| 102 | { |
||
| 103 | self::addDefaultSort($params, 'sorttitle'); |
||
| 104 | self::defaultProperties($params, self::$_defaultMovieProperties); |
||
| 105 | |||
| 106 | $response = Yii::app()->xbmc->performRequest('VideoLibrary.GetMovies', $params); |
||
| 107 | |||
| 108 | return self::normalizeResponse($response, 'movies', array(), new Movie()); |
||
|
|
|||
| 109 | } |
||
| 110 | |||
| 111 | /** |
||
| 112 | * Returns the recently added movies |
||
| 113 | * @return Movie[] the movies |
||
| 114 | */ |
||
| 115 | public static function getRecentlyAddedMovies($params = array()) |
||
| 116 | { |
||
| 117 | self::defaultProperties($params, self::$_defaultMovieProperties); |
||
| 118 | |||
| 119 | // The grid shows six items per row, we don't want the 25th item to be |
||
| 120 | // lonely |
||
| 121 | $params['limits'] = new stdClass(); |
||
| 122 | $params['limits']->end = 24; |
||
| 123 | |||
| 124 | $response = Yii::app()->xbmc->performRequest('VideoLibrary.GetRecentlyAddedMovies', $params); |
||
| 125 | |||
| 126 | return self::normalizeResponse($response, 'movies', array(), new Movie()); |
||
| 127 | } |
||
| 128 | |||
| 129 | /** |
||
| 130 | * Returns details about the specified movie. The properties array |
||
| 131 | * specifies which movie attributes to return. |
||
| 132 | * @param int $movieId the movie ID |
||
| 133 | * @param string[] $properties the properties to include in the result |
||
| 134 | * @return Movie the movie details |
||
| 135 | */ |
||
| 136 | public static function getMovieDetails($movieId, $properties) |
||
| 137 | { |
||
| 138 | $response = Yii::app()->xbmc->performRequest('VideoLibrary.GetMovieDetails', array( |
||
| 139 | 'movieid'=>(int)$movieId, |
||
| 140 | 'properties'=>$properties)); |
||
| 141 | |||
| 142 | return self::normalizeResponse($response, 'moviedetails', null, new Movie()); |
||
| 143 | } |
||
| 144 | |||
| 145 | /** |
||
| 146 | * Returns a list of TV shows. If no sort mechanism is specified in |
||
| 147 | * @params the result will be sorted alphabetically by title. |
||
| 148 | * @param array $params request parameters |
||
| 149 | * @return TVShow[] the TV shows |
||
| 150 | */ |
||
| 151 | public static function getTVShows($params = array()) |
||
| 152 | { |
||
| 153 | self::addDefaultSort($params); |
||
| 154 | self::defaultProperties($params, self::$_defaultTVShowProperties); |
||
| 155 | $response = Yii::app()->xbmc->performRequest('VideoLibrary.GetTVShows', $params); |
||
| 156 | |||
| 157 | return self::normalizeResponse($response, 'tvshows', array(), new TVShow()); |
||
| 158 | } |
||
| 159 | |||
| 160 | /** |
||
| 161 | * Returns the recently added episodes |
||
| 162 | * @param array $params (optional) request parameters |
||
| 163 | * @return Episode[] the recently added episodes |
||
| 164 | */ |
||
| 165 | public static function getRecentlyAddedEpisodes($params = array()) |
||
| 166 | { |
||
| 167 | self::defaultProperties($params, self::$_defaultEpisodeProperties); |
||
| 168 | |||
| 169 | $response = Yii::app()->xbmc->performRequest( |
||
| 170 | 'VideoLibrary.GetRecentlyAddedEpisodes', $params); |
||
| 171 | |||
| 172 | return self::normalizeResponse($response, 'episodes', array(), new Episode()); |
||
| 173 | } |
||
| 174 | |||
| 175 | /** |
||
| 176 | * Returns details about the specified TV show. The properties array |
||
| 177 | * specifies which attributes to return. |
||
| 178 | * @param int $tvshowId the show ID |
||
| 179 | * @param string[] $properties the properties to include in the result |
||
| 180 | * @return TVShow the show details |
||
| 181 | * @throws CHttpException if the show was not found |
||
| 182 | */ |
||
| 183 | public static function getTVShowDetails($tvshowId, $properties) |
||
| 184 | { |
||
| 185 | $response = Yii::app()->xbmc->performRequest('VideoLibrary.GetTVShowDetails', array( |
||
| 186 | 'tvshowid'=>(int)$tvshowId, |
||
| 187 | 'properties'=>$properties)); |
||
| 188 | |||
| 189 | return self::normalizeResponse($response, 'tvshowdetails', null, new TVShow()); |
||
| 190 | } |
||
| 191 | |||
| 192 | /** |
||
| 193 | * Returns the season for the specified TV show, sorted alphabetically |
||
| 194 | * @param int $tvshowId the TV show ID |
||
| 195 | * @return Season[] the seasons |
||
| 196 | */ |
||
| 197 | public static function getSeasons($tvshowId) |
||
| 198 | { |
||
| 199 | $params = array('tvshowid'=>(int)$tvshowId, |
||
| 200 | 'properties'=>array('season', 'art', 'episode', 'showtitle', 'tvshowid', 'playcount')); |
||
| 201 | |||
| 202 | self::addDefaultSort($params); |
||
| 203 | |||
| 204 | $response = Yii::app()->xbmc->performRequest('VideoLibrary.GetSeasons', |
||
| 205 | $params); |
||
| 206 | |||
| 207 | return self::normalizeResponse($response, 'seasons', array(), new Season()); |
||
| 208 | } |
||
| 209 | |||
| 210 | /** |
||
| 211 | * Returns information about a single season |
||
| 212 | * @param int $tvshowId the tv show ID |
||
| 213 | * @param int $season the season number |
||
| 214 | * @return Season the season details |
||
| 215 | * @throws PageNotFoundException if the season is not found |
||
| 216 | */ |
||
| 217 | public static function getSeasonDetails($tvshowId, $season) |
||
| 218 | { |
||
| 219 | $seasons = self::getSeasons($tvshowId); |
||
| 220 | |||
| 221 | foreach ($seasons as $seasonObj) |
||
| 222 | if ($seasonObj->season == $season) |
||
| 223 | return $seasonObj; |
||
| 224 | |||
| 225 | throw new PageNotFoundException(); |
||
| 226 | } |
||
| 227 | |||
| 228 | /** |
||
| 229 | * Returns the episodes for the specified show and season |
||
| 230 | * @param int $tvshowId the TV show ID |
||
| 231 | * @param int $season the season number |
||
| 232 | * @param array $properties properties to include in the results. Defaults |
||
| 233 | * to null, meaning the default properties for episodes will be included |
||
| 234 | * @return Episode[] the episodes |
||
| 235 | */ |
||
| 236 | public static function getEpisodes($tvshowId, $season, $properties = null) |
||
| 237 | { |
||
| 238 | $params = array( |
||
| 239 | 'tvshowid'=>(int)$tvshowId, |
||
| 240 | 'season'=>(int)$season); |
||
| 241 | |||
| 242 | self::addDefaultSort($params); |
||
| 243 | |||
| 244 | if ($properties === null) |
||
| 245 | self::defaultProperties($params, self::$_defaultEpisodeProperties); |
||
| 246 | else |
||
| 247 | $params['properties'] = $properties; |
||
| 248 | |||
| 249 | $response = Yii::app()->xbmc->performRequest( |
||
| 250 | 'VideoLibrary.GetEpisodes', $params); |
||
| 251 | |||
| 252 | return self::normalizeResponse($response, 'episodes', array(), new Episode()); |
||
| 253 | } |
||
| 254 | |||
| 255 | /** |
||
| 256 | * Returns details about the specified TV show episode |
||
| 257 | * @param int $episodeId the episode ID |
||
| 258 | * @param string[] $properties the properties to include in the result |
||
| 259 | * @return Episode the episode details |
||
| 260 | */ |
||
| 261 | public static function getEpisodeDetails($episodeId, $properties) |
||
| 262 | { |
||
| 263 | $response = Yii::app()->xbmc->performRequest('VideoLibrary.GetEpisodeDetails', array( |
||
| 264 | 'episodeid'=>(int)$episodeId, |
||
| 265 | 'properties'=>$properties)); |
||
| 266 | |||
| 267 | return self::normalizeResponse($response, 'episodedetails', null, new Episode()); |
||
| 268 | } |
||
| 269 | |||
| 270 | /** |
||
| 271 | * Returns an array with the download links for a video. It takes a file |
||
| 272 | * string from e.g. VideoLibrary.GetVideoDetails as parameter. |
||
| 273 | * @param string $file a file string returned from the API |
||
| 274 | * @param boolean $omitCredentials whether URL credentials should be |
||
| 275 | * omitted |
||
| 276 | * @return array the download links |
||
| 277 | * @throws CHttpException if the files have been deleted from disk while |
||
| 278 | * the item is still in the library |
||
| 279 | */ |
||
| 280 | public static function getVideoLinks($file, $omitCredentials = false) |
||
| 281 | { |
||
| 282 | $files = array(); |
||
| 283 | |||
| 284 | foreach (self::parseStackedFile($file) as $rawFile) |
||
| 285 | { |
||
| 286 | // Create the URL to the file. If the file has been deleted from |
||
| 287 | // disc but the movie still exists in the library the API call |
||
| 288 | // throws an exception. We just skip this file if that's the case. |
||
| 289 | try |
||
| 290 | { |
||
| 291 | $response = Yii::app()->xbmc->performRequest( |
||
| 292 | 'Files.PrepareDownload', array('path'=>$rawFile)); |
||
| 293 | |||
| 294 | $files[] = Yii::app()->xbmc->getVFSHelper()->getUrl( |
||
| 295 | $response->result->details->path, $omitCredentials); |
||
| 296 | } |
||
| 297 | catch(CHttpException $e) |
||
| 298 | { |
||
| 299 | $files[] = false; |
||
| 300 | } |
||
| 301 | } |
||
| 302 | |||
| 303 | return $files; |
||
| 304 | } |
||
| 305 | |||
| 306 | /** |
||
| 307 | * Parses potential stack:// files into an array of "normal" VFS URLs |
||
| 308 | * @param string $file a VFS URL (potentially stack://) |
||
| 309 | * @return array list of files |
||
| 310 | */ |
||
| 311 | private static function parseStackedFile($file) |
||
| 322 | } |
||
| 323 | |||
| 324 | /** |
||
| 325 | * Adds the specified sort method to the request parameters. The sort |
||
| 326 | * method defaults to "label": |
||
| 327 | * @param array $params the parameters |
||
| 328 | * @param string $sortMethod the sort method to use |
||
| 329 | */ |
||
| 330 | private static function addDefaultSort(&$params, $sortMethod = 'label') |
||
| 331 | { |
||
| 332 | if (!isset($params['sort'])) |
||
| 333 | { |
||
| 334 | $params['sort'] = array( |
||
| 335 | 'order'=>self::SORT_ORDER_ASCENDING, |
||
| 336 | 'ignorearticle'=>Setting::getBoolean('ignoreArticle'), |
||
| 337 | 'method'=>$sortMethod); |
||
| 338 | } |
||
| 339 | } |
||
| 340 | |||
| 341 | /** |
||
| 342 | * Ensures that the specified properties are specified in |
||
| 343 | * params['properties']. If properties have already been defined they |
||
| 344 | * remain untouched |
||
| 345 | * @param array $params the parameters |
||
| 346 | * @param string[] $properties the properties |
||
| 347 | */ |
||
| 348 | private static function defaultProperties(&$params, $properties) |
||
| 352 | } |
||
| 353 | |||
| 354 | /** |
||
| 355 | * Returns the $resultSet from the $response object, or $defaultValue if |
||
| 356 | * the result set is not available |
||
| 357 | * @param stdClass $response an API response object |
||
| 358 | * @param string $resultSet the name of the result set |
||
| 359 | * @param mixed $defaultValue the value to return if the result set is not |
||
| 360 | * available |
||
| 361 | * @param mixed an instance of the class the results should be mapped to, or |
||
| 362 | * null to use stdClass |
||
| 363 | * @return mixed the normalized response |
||
| 364 | */ |
||
| 365 | private static function normalizeResponse($response, $resultSet, $defaultValue, $targetObject = null) |
||
| 385 | } |
||
| 386 | |||
| 387 | } |
||
| 388 |