martes, 11 de marzo de 2014

YouTube API V3 + Spring Roo

En este post voy a explicar cómo obtener los videos subidos a un canal youtube desde una aplicación web desarrollada con spring mvc. También explicare como obtener información relacionada al video: duración, cantidad de reproducciones… y como hacer un paginador del listado de videos.

Para todo esto vamos a utilizar la api que nos provee google: YouTube Data API (v3). Esta api nos permite incorporar funcionalidades de youtube en nuestra propia aplicación. Se puede utilizar la api para buscar videos, insertar, actualizar y borrar recursos tales como videos o listas de reproducciones de video.

Contenido:

  1. Registrar nuestra aplicación para obtener una clave publica de google.
  2. Definición del modelo para convertir la data json a objetos java.
  3. Servicio spring que contendrá la api necesaria para la búsqueda de videos.
  4. Definición del controlador que recibirá las peticiones realizadas por el usuario.
  5. Diseño de la vista para mostrar la lista de videos de manera paginada.
  6. Vista de la aplicación.
1. Registrar nuestra aplicación para obtener una clave publica de google.

2. Definición del modelo para convertir la data json a objetos java: Vamos a crear dos clases java, una para manejar la información relacionada a la lista de videos retornada por google y otra clase para manejar la información del video.
public class VideoYoutube {
 private String videoId;

 private Date publischedAt;

 private String channelId;

 private String title;

 private String description;

 private String thumbnail;

 private String channelTitle;

 private String duration;

 private Long viewCount;

 public VideoYoutube() {
  this.videoId = "";
  this.publischedAt = null;
  this.channelId = "";
  this.title = "";
  this.description = "";
  this.thumbnail = "";
  this.channelTitle = "";
  this.duration = "";
  this.viewCount = null;
 }

 public VideoYoutube(String videoId, Date publischedAt, String channelId,
   String title, String description, String thumbnail,
   String channelTitle, String duration, Long viewCount) {
  super();
  this.videoId = videoId;
  this.publischedAt = publischedAt;
  this.channelId = channelId;
  this.title = title;
  this.description = description;
  this.thumbnail = thumbnail;
  this.channelTitle = channelTitle;
  this.duration = duration;
  this.viewCount = viewCount;
 }

 public String getVideoId() {
  return videoId;
 }

 public void setVideoId(String videoId) {
  this.videoId = videoId;
 }

 public Date getPublischedAt() {
  return publischedAt;
 }

 public void setPublischedAt(Date publischedAt) {
  this.publischedAt = publischedAt;
 }

 public String getChannelId() {
  return channelId;
 }

 public void setChannelId(String channelId) {
  this.channelId = channelId;
 }

 public String getTitle() {
  return title;
 }

 public void setTitle(String title) {
  this.title = title;
 }

 public String getDescription() {
  return description;
 }

 public void setDescription(String description) {
  this.description = description;
 }

 public String getThumbnail() {
  return thumbnail;
 }

 public void setThumbnail(String thumbnail) {
  this.thumbnail = thumbnail;
 }

 public String getChannelTitle() {
  return channelTitle;
 }

 public void setChannelTitle(String channelTitle) {
  this.channelTitle = channelTitle;
 }

 public String getDuration() {
  if (duration != null && !duration.equals("")) {
   StringBuilder strPeriod = new StringBuilder();
   PeriodFormatter formatter = ISOPeriodFormat.standard();
   Period period = formatter.parsePeriod(duration);
   if (period.getHours() > 0) {
    strPeriod.append(period.getHours()).append(":");
   }
   if (period.getMinutes() < 10) {
    strPeriod.append("0").append(period.getMinutes()).append(":");
   } else {
    strPeriod.append(period.getMinutes()).append(":");
   }
   if (period.getSeconds() < 10) {
    strPeriod.append("0").append(period.getSeconds());
   } else {
    strPeriod.append(period.getSeconds());
   }
   return strPeriod.toString();
  }
  return duration;
 }

 public void setDuration(String duration) {
  this.duration = duration;
 }

 public Long getViewCount() {
  return viewCount;
 }

 public void setViewCount(Long viewCount) {
  this.viewCount = viewCount;
 }

}

public class PageResultYoutube {
 private String nextPageToken;

 private String prevPageToken;

 private int totalResults;

 private int resultsPerPage;

 private Map videosResults;

 public PageResultYoutube(String nextPageToken, String prevPageToken,
   int totalResults, int resultsPerPage,
   Map videosResults) {
  super();
  this.nextPageToken = nextPageToken;
  this.prevPageToken = prevPageToken;
  this.totalResults = totalResults;
  this.resultsPerPage = resultsPerPage;
  this.videosResults = videosResults;
 }

 public PageResultYoutube(String dataJson) {
  JSONObject jo = new JSONObject(dataJson);
  if (jo.has("nextPageToken")) {
   nextPageToken = jo.getString("nextPageToken");
  }
  if (jo.has("prevPageToken")) {
   prevPageToken = jo.getString("prevPageToken");
  }
  if (jo.has("pageInfo")) {
   JSONObject pageInfo = jo.getJSONObject("pageInfo");
   totalResults = pageInfo.getInt("totalResults");
   resultsPerPage = pageInfo.getInt("resultsPerPage");
  }
  videosResults = new HashMap();
  if (jo.has("items")) {
   JSONArray items = jo.getJSONArray("items");
   VideoYoutube video;
   for (int i = 0; i < items.length(); i++) {
    video = new VideoYoutube();
    JSONObject item = items.getJSONObject(i);
    JSONObject id = item.getJSONObject("id");
    JSONObject snippet = item.getJSONObject("snippet");   
    video.setVideoId(id.getString("videoId"));
    video.setChannelId(snippet.getString("channelId"));
    video.setChannelTitle(snippet.getString("channelTitle"));
    video.setDescription(snippet.getString("description"));
    video.setPublischedAt(new DateTime(snippet
      .getString("publishedAt")).toDate());
    video.setThumbnail(snippet.getJSONObject("thumbnails")
      .getJSONObject("default").getString("url"));
    video.setTitle(snippet.getString("title"));
    videosResults.put(video.getVideoId(), video);
   }

  }

 }

 public String getVideoIdsSeparetedByComma() {
  StringBuilder str = new StringBuilder();
  if (videosResults != null && videosResults.size() > 0) {
   for (Entry entry : videosResults.entrySet()) {
    str.append(entry.getKey()).append(",");
   }
  }// XXX fix validation
  String finalStr = str.toString();
  finalStr = finalStr.substring(0, finalStr.toString().length() - 1);
  return finalStr;
 }

 public void addVideo(VideoYoutube video) {
  videosResults.put(video.getVideoId(), video);
 }

 public String getNextPageToken() {
  return nextPageToken;
 }

 public void setNextPageToken(String nextPageToken) {
  this.nextPageToken = nextPageToken;
 }

 public String getPrevPageToken() {
  return prevPageToken;
 }

 public void setPrevPageToken(String prevPageToken) {
  this.prevPageToken = prevPageToken;
 }

 public int getTotalResults() {
  return totalResults;
 }

 public void setTotalResults(int totalResults) {
  this.totalResults = totalResults;
 }

 public int getResultsPerPage() {
  return resultsPerPage;
 }

 public void setResultsPerPage(int resultsPerPage) {
  this.resultsPerPage = resultsPerPage;
 }

 public Map getVideosResults() {
  return videosResults;
 }

 public void setVideosResults(Map videosResults) {
  this.videosResults = videosResults;
 }

 public void setContentDetailsVideos(String contentDetailsJson) {
  JSONObject jo = new JSONObject(contentDetailsJson);
  if (jo.has("items")) {
   JSONArray items = jo.getJSONArray("items");
   for (int i = 0; i < items.length(); i++) {
    JSONObject item = items.getJSONObject(i);
    VideoYoutube video = videosResults.get(item.get("id"));
    JSONObject cd = item.getJSONObject("contentDetails");
    video.setDuration(cd.getString("duration"));
    JSONObject stats = item.getJSONObject("statistics");
    video.setViewCount(stats.getLong("viewCount"));
    videosResults.put(video.getVideoId(), video);
   }
  }
 }

}


3. Servicio spring que contendrá la api necesaria para la búsqueda de videos.
/**
 * 
 * @author Williams Rivas Created 20/02/2014 12:59:49
 * 
 */
@Service
public class YoutubeServiceImpl implements YoutubeService {

 public static final String PARAM_MAX_RESULTS = "maxResults";

 private static int DEFAULT_VALUE_MAX_RESULTS = 16;

 private static String DEFAULT_VALUE_CHANNEL_ID = “XXXXXXXXXXXXXXXXXXXXXX";

 public static final String PARAM_PART = "part";

 public static final String DEFAULT_VALUE_PART = "snippet";

 public static final String DEFAULT_VALUE_PARTS_VIDEOS = "contentDetails%2Cstatistics";

 public static final String PARAM_PAGE_TOKEN = "pageToken";

 public static final String PARAM_CHANNEL_ID = "channelId";

 public static final String PARAM_VIDEO_IDS = "id";

 public static final String PARAM_KEY = "key";

 private static final String DEFAULT_VALUE_KEY = "API_KEY_GENERADA_EN_EL_PASO_UNO_DEL_CONTENIDO_DE_ESTE_POST";

 public static final String URL_SEARCH_YOUTUBE = "https://www.googleapis.com/youtube/v3/search?type=video&";

 public static final String URL_VIDEOS_LIST = "https://www.googleapis.com/youtube/v3/videos?";

 public static final String PARAM_VALUE_SEPARATOR = "=";

 public static final String PARAM_VALUE_CONCAT = "&";

 public static final String PARAM_VALUE_COMMA = "%2C";

 @Autowired
 protected YoutubeCanalRepository youtubeCanalRepository;

 @Override
 public PageResultYoutube searchYoutubeVideos(String part, String channelId,
   int maxResults, String pageToken) throws SearchYoutubeException {
  setDefaultValues();
  String query = buildQueryUrl(part, channelId, maxResults, pageToken,
    null);
  String dataJson = Util.httpGet(query);

  PageResultYoutube page = new PageResultYoutube(dataJson);

  String videoIds = page.getVideoIdsSeparetedByComma();

  String contentDetailsJson = "";
  try {
   contentDetailsJson = Util.httpGet(buildQueryUrl(
     DEFAULT_VALUE_PARTS_VIDEOS, null, DEFAULT_VALUE_MAX_RESULTS,
     null, videoIds));   
  } catch (HttpGetException e) {
   throw new SearchYoutubeException(e.getMessage());
  }

  page.setContentDetailsVideos(contentDetailsJson);

  return page;
 }

 @Override
 public PageResultYoutube searchYoutubeVideos(String part, String channelId,
   int maxResults) throws SearchYoutubeException {
  return searchYoutubeVideos(part, channelId, maxResults, null);
 }

 @Override
 public PageResultYoutube searchYoutubeVideos()
   throws SearchYoutubeException {
  return searchYoutubeVideos(DEFAULT_VALUE_PART,
    DEFAULT_VALUE_CHANNEL_ID, DEFAULT_VALUE_MAX_RESULTS);
 }

 private String buildQueryUrl(String part, String channelId, int maxResults,
   String pageToken, String videoIds) {
  StringBuilder url;
  if (channelId != null && !channelId.equals("")) {
   url = new StringBuilder(URL_SEARCH_YOUTUBE);
   // parametro part
   url.append(PARAM_PART).append(PARAM_VALUE_SEPARATOR);
   if (part != null && !part.equals("")) {
    if (!part.equals(DEFAULT_VALUE_PART)) {
     url.append(DEFAULT_VALUE_PART);
    } else {
     url.append(part);
    }
   } else {
    url.append(DEFAULT_VALUE_PART);
   }

   // parametro channel id
   url.append(PARAM_VALUE_CONCAT).append(PARAM_CHANNEL_ID)
     .append(PARAM_VALUE_SEPARATOR);
   if (channelId != null && !channelId.equals("")) {
    url.append(channelId);
   } else {
    url.append(DEFAULT_VALUE_CHANNEL_ID);
   }
   // pagina
   if (pageToken != null && !pageToken.equals("")) {
    url.append(PARAM_VALUE_CONCAT).append(PARAM_PAGE_TOKEN)
      .append(PARAM_VALUE_SEPARATOR).append(pageToken);
   }
  } else { // busqueda no por canal sino por ids de videos
     // https://www.googleapis.com/youtube/v3/videos?part=snippet%2CcontentDetails%2Cstatistics&id=uWKM4F2RAtI%2CJ2NIttHwZBA%2C6UliPT1LIc4%2CSaLWwDyHMvo&maxResults=3&key=AIzaSyBB8x12DXzrzXKhkum5f_Nv3Yl7-0GSwCg
   url = new StringBuilder(URL_VIDEOS_LIST);

   // parametro part
   url.append(PARAM_PART).append(PARAM_VALUE_SEPARATOR);
   if (part != null && !part.equals("")) {
    if (!part.equals(DEFAULT_VALUE_PARTS_VIDEOS)) {
     url.append(DEFAULT_VALUE_PARTS_VIDEOS);
    } else {
     url.append(part);
    }
   } else {
    url.append(DEFAULT_VALUE_PARTS_VIDEOS);
   }
   // videos ids separados por comma
   if (videoIds != null && !videoIds.equals("")) {
    url.append(PARAM_VALUE_CONCAT).append(PARAM_VIDEO_IDS)
      .append(PARAM_VALUE_SEPARATOR);
    url.append(videoIds);
   }
  }

  // max results
  url.append(PARAM_VALUE_CONCAT).append(PARAM_MAX_RESULTS)
    .append(PARAM_VALUE_SEPARATOR);
  if (maxResults <= 0) {
   url.append(DEFAULT_VALUE_MAX_RESULTS);
  } else {
   url.append(maxResults);
  }

  // key developer
  url.append(PARAM_VALUE_CONCAT).append(PARAM_KEY)
    .append(PARAM_VALUE_SEPARATOR).append(DEFAULT_VALUE_KEY);

  return url.toString();
 }

 @Override
 public PageResultYoutube searchYoutubeVideos(String pageToken)
   throws SearchYoutubeException {
  return searchYoutubeVideos(DEFAULT_VALUE_PART,
    DEFAULT_VALUE_CHANNEL_ID, DEFAULT_VALUE_MAX_RESULTS, pageToken);
 }

 private void setDefaultValues() {
  List canals = youtubeCanalRepository.findAll();
  if (canals != null && canals.size() > 0) {
   DEFAULT_VALUE_CHANNEL_ID = canals.get(0).getIdChannel();
   DEFAULT_VALUE_MAX_RESULTS = canals.get(0).getMaxResults();
  }
 }
}


4. Definición del controlador que recibirá las peticiones realizadas por el usuario.
@RequestMapping("/youtube/**")
@Controller
public class YoutubeController extends GlobalModelAttributes {

 @Autowired
 private YoutubeService youtube;

 @RequestMapping(method = RequestMethod.POST, value = "{id}")
 public void post(@PathVariable Long id, ModelMap modelMap,
   HttpServletRequest request, HttpServletResponse response) {
 }

 @RequestMapping
 public String index(
   @RequestParam(value = "page", required = false) String page,
   ModelMap uiModel) {
  PageResultYoutube result;
  if (page != null) {
   result = youtube.searchYoutubeVideos(page);
  } else {
   result = youtube.searchYoutubeVideos();
  }
  uiModel.addAttribute("youtube", result);
  return "youtube/index";
 }
}


5. Diseño de la vista para mostrar la lista de videos de manera paginada.

Videos Youtube



6. Vista de la aplicación

No hay comentarios:

Publicar un comentario