Package com.google.ytd.youtube

Source Code of com.google.ytd.youtube.YouTubeApiHelper

/* Copyright (c) 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.ytd.youtube;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.PrivateKey;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.gdata.client.Service.GDataRequest;
import com.google.gdata.client.Service.GDataRequest.RequestType;
import com.google.gdata.client.youtube.YouTubeService;
import com.google.gdata.data.DateTime;
import com.google.gdata.data.Link;
import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.youtube.CaptionTrackEntry;
import com.google.gdata.data.youtube.CaptionTrackFeed;
import com.google.gdata.data.youtube.FormUploadToken;
import com.google.gdata.data.youtube.PlaylistEntry;
import com.google.gdata.data.youtube.PlaylistFeed;
import com.google.gdata.data.youtube.PlaylistLinkEntry;
import com.google.gdata.data.youtube.PlaylistLinkFeed;
import com.google.gdata.data.youtube.UserProfileEntry;
import com.google.gdata.data.youtube.VideoEntry;
import com.google.gdata.data.youtube.VideoFeed;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.ContentType;
import com.google.gdata.util.InvalidEntryException;
import com.google.gdata.util.ServiceException;
import com.google.gdata.util.ServiceForbiddenException;
import com.google.gdata.util.XmlBlob;
import com.google.inject.Inject;
import com.google.ytd.dao.AdminConfigDao;
import com.google.ytd.util.Util;

/**
* Class to handle interfacing with the Google Data Java Client Library's
* YouTube support.
*/
public class YouTubeApiHelper {
  private static final Logger log = Logger.getLogger(YouTubeApiHelper.class.getName());

  // CONSTANTS
  private static final String ENTRY_URL_FORMAT = "http://gdata.youtube.com/feeds/api/videos/%s";
  private static final String UPLOADS_URL_FORMAT = "http://gdata.youtube.com/feeds/api/"
      + "users/%s/uploads/%s";
  private static final String PLAYLIST_ENTRY_URL_FORMAT = "http://gdata.youtube.com/feeds/api/"
      + "playlists/%s";
  private static final String PLAYLIST_FEED_URL = "http://gdata.youtube.com/feeds/api/users/"
      + "default/playlists?max-results=50";
  private static final String USER_ENTRY_URL = "http://gdata.youtube.com/feeds/api/users/default";
  private static final String UPLOAD_TOKEN_URL = "http://gdata.youtube.com/action/GetUploadToken";
  private static final String MODERATION_FEED_ENTRY_URL_FORMAT = "http://gdata.youtube.com/feeds/"
      + "api/products/default/videos/%s";
  private static final String UPDATED_ENTRY_ATOM_FORMAT = "<entry xmlns='http://www.w3.org/2005/"
      + "Atom' xmlns:yt='http://gdata.youtube.com/schemas/2007'><yt:moderationStatus>%s"
      + "</yt:moderationStatus></entry>";
  private static final String MODERATION_ACCEPTED = "accepted";
  private static final String MODERATION_REJECTED = "rejected";
  private static final String CAPTION_FEED_URL_FORMAT = "http://gdata.youtube.com/feeds/api/"
      + "videos/%s/captions";
  private static final String CAPTION_FAILURE_TAG = "invalidFormat";
  private static final String UPLOADS_FEED_URL_FORMAT = "http://gdata.youtube.com/feeds/api/"
      + "users/%s/uploads?max-results=50";
  private static final String CLAIMED = "<yt:claimed"; // closing bracked omitted for robustness

  private Util util;
  private YouTubeService service = null;
  private AdminConfigDao adminConfigDao = null;
 
  /**
   * Create a new instance of the class, initializing a YouTubeService object
   * with parameters specified in appengine-web.xml
   */
  @Inject
  public YouTubeApiHelper(AdminConfigDao adminConfigDao) {
    this.util = Util.get();
    this.adminConfigDao = adminConfigDao;

    String clientId = this.adminConfigDao.getAdminConfig().getClientId();
    String developerKey = this.adminConfigDao.getAdminConfig().getDeveloperKey();

    if (util.isNullOrEmpty(clientId)) {
      clientId = "";
      log.warning("clientId settings property is null or empty.");
    }

    if (util.isNullOrEmpty(developerKey)) {
      log.warning("developerKey settings property is null or empty.");
      service = new YouTubeService(clientId);
    } else {
      service = new YouTubeService(clientId, developerKey);
    }
  }

  public YouTubeApiHelper(String clientId) {
    this.util = Util.get();
    if (util.isNullOrEmpty(clientId)) {
      clientId = "";
      log.warning("clientId parameter is null or empty.");
    }
    service = new YouTubeService(clientId);
  }
 
  /**
   * Sets the AuthSub token to use for API requests.
   *
   * @param token
   *          The token to use.
   */
  public void setAuthSubToken(String token) {
    PrivateKey privateKey = adminConfigDao.getPrivateKey();
    if (privateKey == null) {
      service.setAuthSubToken(token);
    } else {
      service.setAuthSubToken(token, privateKey);
    }
  }

  /**
   * Sets the AuthSub token to use for API requests.
   *
   * @param token
   *          The token to use.
   */
  public void setClientLoginToken(String token) {
    service.setUserToken(token);
 
 
  /**
   * Sets an arbitrary header for all outgoing requests using this service
   * instance.
   *
   * @param header
   *          The name of the header.
   * @param value
   *          The header's value.
   */
  public void setHeader(String header, String value) {
    service.getRequestFactory().setHeader(header, value);
  }

  /**
   * Gets the username for the authenticated user, assumes that setToken() has
   * already been called to provide authentication.
   *
   * @return The current username for the authenticated user.
   * @throws ServiceException
   * @throws IOException
   */
  public String getCurrentUsername() throws IOException, ServiceException {
    try {
      UserProfileEntry profile = service.getEntry(new URL(USER_ENTRY_URL), UserProfileEntry.class);
      return profile.getUsername();
    } catch (MalformedURLException e) {
      log.log(Level.WARNING, "", e);
    } catch (AuthenticationException e) {
      if (e.getResponseBody().contains("NoLinkedYouTubeAccount")) {
        throw new IllegalArgumentException("Your account is not linked to a YouTube account. " +
            "Please visit https://www.youtube.com/signin?next=/create_channel to link to a YouTube " +
            "account, and try again.");
      } else {
        throw(e);
      }
    }

    return null;
  }

  /**
   * Sets the value for video moderation, which controls whether partner
   * branding shows up on the video's YouTube.com watch page.
   *
   * This request needs to be made with the authorization of the account that
   * owns the developr token used to originally upload the video. Also, the
   * video must have at least one developer tag set at the time it was uploaded.
   *
   * @param videoId
   *          The YouTube id of the video to moderate.
   * @param isApproved
   *          true if this video is approved, and false if not.
   */
  public void updateModeration(String videoId, boolean isApproved) {
    log.info(String.format("Setting moderation of video id '%s' to '%s'.", videoId, isApproved));

    String entryUrl = String.format(MODERATION_FEED_ENTRY_URL_FORMAT, videoId);
    String updatedEntry;
    if (isApproved) {
      updatedEntry = String.format(UPDATED_ENTRY_ATOM_FORMAT, MODERATION_ACCEPTED);
    } else {
      updatedEntry = String.format(UPDATED_ENTRY_ATOM_FORMAT, MODERATION_REJECTED);
    }

    try {
      GDataRequest request = service.createUpdateRequest(new URL(entryUrl));
      request.getRequestStream().write(updatedEntry.getBytes());
      request.execute();
    } catch (MalformedURLException e) {
      log.log(Level.WARNING, "", e);
    } catch (IOException e) {
      log.log(Level.WARNING, "", e);
    } catch (ServiceException e) {
      log.log(Level.WARNING, "", e);
    }
  }

  /**
   * Submits video metadata to YouTube to get an upload token and URL.
   *
   * @param newEntry
   *          The VideoEntry containing all video metadata for the upload
   * @return A FormUploadToken used when uploading a video to YouTube.
   */
  public FormUploadToken getFormUploadToken(VideoEntry newEntry) {
    try {
      URL uploadUrl = new URL(UPLOAD_TOKEN_URL);
      return service.getFormUploadToken(uploadUrl, newEntry);
    } catch (MalformedURLException e) {
      log.log(Level.WARNING, "", e);
    } catch (ServiceException e) {
      log.log(Level.WARNING, "", e);
    } catch (IOException e) {
      log.log(Level.WARNING, "", e);
    }

    return null;
  }

  public String generateVideoEntryUrl(String videoId) {
    return String.format(ENTRY_URL_FORMAT, videoId);
  }

  public String generateUploadsVideoEntryUrl(String videoId) {
    return generateUploadsVideoEntryUrl("default", videoId);
  }

  public String generateUploadsVideoEntryUrl(String username, String videoId) {
    return String.format(UPLOADS_URL_FORMAT, username, videoId);
  }

  public VideoEntry getUploadsVideoEntry(String videoId) {
    String entryUrl = generateUploadsVideoEntryUrl(videoId);

    return makeVideoEntryRequest(entryUrl);
  }

  public VideoEntry getUploadsVideoEntry(String username, String videoId) {
    String entryUrl = generateUploadsVideoEntryUrl(username, videoId);

    return makeVideoEntryRequest(entryUrl);
  }

  public VideoEntry getVideoEntry(String videoId) {
    String entryUrl = generateVideoEntryUrl(videoId);

    return makeVideoEntryRequest(entryUrl);
  }

  public VideoFeed getUploadsFeed(String username) {
    String url = String.format(UPLOADS_FEED_URL_FORMAT, username);
    try {
      return service.getFeed(new URL(url), VideoFeed.class);
    } catch (MalformedURLException e) {
      log.log(Level.WARNING, "", e);
    } catch (IOException e) {
      log.log(Level.WARNING, "", e);
    } catch (ServiceException e) {
      log.log(Level.WARNING, "", e);
    }

    return null;
  }

  public Map<String, String> getCaptions(String videoId) {
    String feedUrl = String.format(CAPTION_FEED_URL_FORMAT, videoId);
    try {
      CaptionTrackFeed captionTrackFeed = service.getFeed(new URL(feedUrl), CaptionTrackFeed.class);

      HashMap<String, String> languageToUrl = new HashMap<String, String>();
      for (CaptionTrackEntry captionTrackEntry : captionTrackFeed.getEntries()) {
        String languageCode = captionTrackEntry.getLanguageCode();
        Link link = captionTrackEntry.getLink("edit-media", "application/vnd.youtube.timedtext");
        if (link != null) {
          languageToUrl.put(languageCode, link.getHref());
        }
      }

      return languageToUrl;
    } catch (MalformedURLException e) {
      log.log(Level.WARNING, "", e);
    } catch (IOException e) {
      log.log(Level.WARNING, "", e);
    } catch (ServiceException e) {
      log.log(Level.WARNING, "", e);
    }

    return null;
  }

  public String getCaptionTrack(String url) {
    try {
      GDataRequest request = service.createRequest(RequestType.QUERY, new URL(url),
          ContentType.TEXT_PLAIN);
      request.execute();

      BufferedReader reader = new BufferedReader(new InputStreamReader(request.getResponseStream(),
          "UTF-8"));
      StringBuilder builder = new StringBuilder();
      String line;

      while ((line = reader.readLine()) != null) {
        builder.append(line).append("\n");
      }

      return builder.toString();
    } catch (MalformedURLException e) {
      log.log(Level.WARNING, "", e);
    } catch (IOException e) {
      log.log(Level.WARNING, "", e);
    } catch (ServiceException e) {
      log.log(Level.WARNING, "", e);
    }

    return null;
  }

  /**
   * Creates or updates a video caption track. Explicitly throw exceptions so
   * that the calling code knows whether a failure occurred due to a YouTube API
   * issue or due to a bad captions track.
   *
   * @param videoId
   *          The video id of the YouTube video to update.
   * @param captionTrack
   *          The UTF-8 caption track data.
   * @return true if the caption track update was successful; false otherwise.
   * @throws MalformedURLException
   * @throws IOException
   * @throws ServiceException
   */
  public boolean updateCaptionTrack(String videoId, String captionTrack)
      throws MalformedURLException, IOException, ServiceException {
    String captionsUrl = String.format(CAPTION_FEED_URL_FORMAT, videoId);

    GDataRequest request = service.createInsertRequest(new URL(captionsUrl));
    request.getRequestStream().write(captionTrack.getBytes("UTF-8"));
    request.execute();

    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(request
        .getResponseStream()));
    StringBuilder builder = new StringBuilder();
    String line = null;

    while ((line = bufferedReader.readLine()) != null) {
      builder.append(line + "\n");
    }

    bufferedReader.close();

    String responseBody = builder.toString();
    log.info("Response to captions request: " + responseBody);
    if (responseBody.contains(CAPTION_FAILURE_TAG)) {
      return false;
    }

    return true;
  }

  /**
   * Gets a YouTube video entry given a specific video id. Constructs the entry
   * URL based on a hardcoded URL prefix, which might need to be changed in the
   * future.
   *
   * @param entryUrl
   *          A URL string representing a GData video entity.
   * @return A VideoEntry representing the video in question, or null.
   */
  public VideoEntry makeVideoEntryRequest(String entryUrl) {
    try {
      return service.getEntry(new URL(entryUrl), VideoEntry.class);
    } catch (MalformedURLException e) {
      log.log(Level.WARNING, "", e);
    } catch (IOException e) {
      log.log(Level.WARNING, "", e);
    } catch (ServiceException e) {
      // This may be thrown if the video is not found, i.e. because it is not done processing.
      // We don't need to log it at WARNING level.
      // TODO: Propogate AuthenticationExceptions so the calling code can invalidate the token.
      log.log(Level.INFO, "", e);
    }

    log.info(String.format("Couldn't get video entry from %s.", entryUrl));
    return null;
  }

  public PlaylistEntry getVideoInPlaylist(String playlistId, String videoId) {
    String playlistUrl = getPlaylistFeedUrl(playlistId);

    try {
      while (playlistUrl != null) {
        PlaylistFeed playlistFeed = service.getFeed(new URL(playlistUrl), PlaylistFeed.class);
       
        Link nextLink = playlistFeed.getNextLink();
        if (nextLink == null) {
          playlistUrl = null;
        } else {
          playlistUrl = nextLink.getHref();
        }
       
        for (PlaylistEntry playlistEntry : playlistFeed.getEntries()) {
          if (playlistEntry.getMediaGroup().getVideoId().equals(videoId)) {
            return playlistEntry;
          }
        }
      }
    } catch (MalformedURLException e) {
      log.log(Level.WARNING, "", e);
    } catch (IOException e) {
      log.log(Level.WARNING, "", e);
    } catch (ServiceException e) {
      // TODO: Propogate AuthenticationExceptions so the calling code can
      // invalidate the token.
      log.log(Level.WARNING, "", e);
    }

    return null;
  }
 
  public boolean insertVideoIntoPlaylist(String playlistId, String videoId) {
    return insertVideoIntoPlaylist(playlistId, videoId, true);
  }

  public boolean insertVideoIntoPlaylist(String playlistId, String videoId, boolean retry) {
    log.info(String.format("Attempting to insert video id '%s' into playlist id '%s'...",
        videoId, playlistId));
    PlaylistEntry playlistEntry = new PlaylistEntry();
    playlistEntry.setId(videoId);

    if (getVideoInPlaylist(playlistId, videoId) != null) {
      log.warning(String.format("Video id '%s' is already in playlist id '%s'.", videoId,
          playlistId));
      // Return true here, so that the video is flagged as being in the playlist.
      return true;
    }
   
    // As of Aug 2011, it's now possible to set the playlist position at insertion time.
    playlistEntry.setPosition(0);

    try {
      playlistEntry = service.insert(new URL(getPlaylistFeedUrl(playlistId)), playlistEntry);
      log.info(String.format("Inserted video id '%s' into playlist id '%s' at position 1.",
        videoId, playlistId));
      return true;
    } catch (MalformedURLException e) {
      log.log(Level.WARNING, "", e);
    } catch (IOException e) {
      log.log(Level.WARNING, "", e);
    } catch (ServiceForbiddenException e) {
      log.log(Level.INFO, "Maximum size of playlist reached.", e);

      if (retry) {
        log.info("Removing oldest entry from playlist and retrying...");

        PlaylistEntry lastVideo = getLastVideoInPlaylist(playlistId);
        if (lastVideo != null) {
          try {
            lastVideo.delete();
            log.info("Last entry removed.");

            return insertVideoIntoPlaylist(playlistId, videoId, false);
          } catch (IOException innerEx) {
            log.log(Level.WARNING, "", innerEx);
          } catch (ServiceException innerEx) {
            log.log(Level.WARNING, "", innerEx);
          } catch (UnsupportedOperationException innerEx) {
            log.log(Level.WARNING, "", innerEx);
          }
        }
      }
    } catch (ServiceException e) {
      log.log(Level.WARNING, "", e);
    }

    return false;
  }
 
  public PlaylistEntry getLastVideoInPlaylist(String playlistId) {
    String playlistUrl = getPlaylistFeedUrl(playlistId);

    try {
      PlaylistFeed playlistFeed = null;
     
      while (playlistUrl != null) {
        playlistFeed = service.getFeed(new URL(playlistUrl), PlaylistFeed.class);
        Link nextLink = playlistFeed.getNextLink();
       
        if (nextLink == null) {
          playlistUrl = null;
        } else {
          playlistUrl = nextLink.getHref();
        }
      }
     
      if (playlistFeed != null) {
        List<PlaylistEntry> entries = playlistFeed.getEntries();
        return entries.get(entries.size() - 1);
      }
    } catch (MalformedURLException e) {
      log.log(Level.WARNING, "", e);
    } catch (IOException e) {
      log.log(Level.WARNING, "", e);
    } catch (ServiceException e) {
      log.log(Level.WARNING, "", e);
    }

    return null;
  }

  public boolean removeVideoFromPlaylist(String playlistId, String videoId) {
    try {
      PlaylistEntry playlistEntry = getVideoInPlaylist(playlistId, videoId);

      if (playlistEntry == null) {
        log.warning(String.format("Could not find video id '%s' in playlist id '%s'.", videoId,
            playlistId));
        return false;
      } else {
        playlistEntry.delete();

        log.info(String.format("Removed video '%s' from playlist id '%s'.", videoId, playlistId));

        return true;
      }
    } catch (MalformedURLException e) {
      log.log(Level.WARNING, "", e);
    } catch (IOException e) {
      log.log(Level.WARNING, "", e);
    } catch (ServiceException e) {
      // TODO: Propogate AuthenticationExceptions so the calling code can
      // invalidate the token.
      log.log(Level.WARNING, "", e);
    }

    return false;
  }

  public String getPlaylistFeedUrl(String playlistId) {
    return String.format(PLAYLIST_ENTRY_URL_FORMAT, playlistId);
  }
 
  public List<PlaylistLinkEntry> getDefaulUsersPlaylists() {
    ArrayList<PlaylistLinkEntry> playlistEntries = new ArrayList<PlaylistLinkEntry>();

    try {
      URL feedUrl = new URL(PLAYLIST_FEED_URL);

      while (feedUrl != null) {
        PlaylistLinkFeed playlistFeed = service.getFeed(feedUrl, PlaylistLinkFeed.class);
        playlistEntries.addAll(playlistFeed.getEntries());
       
        Link nextLink = playlistFeed.getNextLink();
        if (nextLink == null) {
          feedUrl = null;
        } else {
          feedUrl = new URL(nextLink.getHref());
        }
      }
     
      return playlistEntries;
    } catch (MalformedURLException e) {
      log.log(Level.WARNING, "", e);
    } catch (IOException e) {
      log.log(Level.WARNING, "", e);
    } catch (ServiceException e) {
      log.log(Level.WARNING, "", e);
    }

    return null;
  }

  public String createPlaylist(String title, String description) throws ServiceException {
    PlaylistLinkEntry newEntry = new PlaylistLinkEntry();
    newEntry.setTitle(new PlainTextConstruct(title));
    newEntry.setSummary(new PlainTextConstruct(description));

    try {
      PlaylistLinkEntry createdEntry;
     
      try {
        createdEntry = service.insert(new URL(PLAYLIST_FEED_URL), newEntry);
      } catch(InvalidEntryException e) {
        // If the first attempt to create the playlist fails with this exception,
        // it's most likely due to a duplicate playlist title.
        // So let's make the title unique and try again.
        String newTitle = title + " - " + DateTime.now().toUiString();
        log.info(String.format("Playlist with title '%s' already exists. Attempting to create " +
            "playlist with title '%s'.", title, newTitle));

        newEntry.setTitle(new PlainTextConstruct(newTitle));
       
        createdEntry = service.insert(new URL(PLAYLIST_FEED_URL), newEntry);
      }
     
      String id = createdEntry.getPlaylistId();

      log.info(String.format("Created new playlist with id '%s'.", id));
      return id;
    } catch (MalformedURLException e) {
      log.log(Level.WARNING, "", e);
    } catch (IOException e) {
      log.log(Level.WARNING, "", e);
    }

    return null;
  }

  /**
   * Check to see if yt:claimed element is present. Since the entry
   * class does not include an accessor method yet, we keep the code here.
   * @param videoEntry the entry to inspect
   * @return <code>true</code> if claimed can be found
   */
  public boolean isClaimed(VideoEntry videoEntry) {
    // currently the element is only available in the extensions blob as an unrecognized extension
    // TODO - jarekw@ , once the client library supports the extension, this code needs to change
    XmlBlob xmlBlob = videoEntry.getXmlBlob();
    String text = xmlBlob.getFullText();
    return text != null && text.contains(CLAIMED);
  }
}
TOP

Related Classes of com.google.ytd.youtube.YouTubeApiHelper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.