![JAR search and dependency download from the Maven repository](/logo.png)
twitter4jads.impl.TwitterAdsMediaUploadApiImpl Maven / Gradle / Ivy
The newest version!
package twitter4jads.impl;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.reflect.TypeToken;
import twitter4jads.TwitterAdsClient;
import twitter4jads.TwitterAdsConstants;
import twitter4jads.api.TwitterAdsMediaUploadApi;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import twitter4jads.internal.http.HttpParameter;
import twitter4jads.internal.http.HttpResponse;
import twitter4jads.internal.models4j.TwitterException;
import twitter4jads.internal.models4j.TwitterInvalidParameterException;
import twitter4jads.internal.models4j.TwitterRuntimeException;
import twitter4jads.models.ads.HttpVerb;
import twitter4jads.models.media.TwitterMediaType;
import twitter4jads.models.video.UploadMediaObjectResponse;
import twitter4jads.util.TwitterAdUtil;
import javax.xml.bind.DatatypeConverter;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
/**
*
* Date: 4/5/16
* Time: 10:41 AM
*/
public class TwitterAdsMediaUploadApiImpl implements TwitterAdsMediaUploadApi {
private static final Map VIDEO_SIZE_PROCESSING_WAIT_TIME_MAP;
static {
final Map videoSizeProcessingWaitTimeMap = Maps.newHashMap();
videoSizeProcessingWaitTimeMap.put(TwitterAdsConstants.FIFTY_MIB, TwitterAdsConstants.MAX_WAIT_INTERVAL_FIFTY_MIB);
videoSizeProcessingWaitTimeMap.put(TwitterAdsConstants.ONE_HUNDRED_FIFTY_MIB, TwitterAdsConstants.MAX_WAIT_INTERVAL_ONE_HUNDRED_FIFTY_MIB);
videoSizeProcessingWaitTimeMap.put(TwitterAdsConstants.FIVE_HUNDRED_MIB, TwitterAdsConstants.MAX_WAIT_INTERVAL_FIVE_HUNDRED_MIB);
VIDEO_SIZE_PROCESSING_WAIT_TIME_MAP = Collections.unmodifiableMap(videoSizeProcessingWaitTimeMap);
}
private final TwitterAdsClient twitterAdsClient;
public TwitterAdsMediaUploadApiImpl(TwitterAdsClient twitterAdsClient) {
this.twitterAdsClient = twitterAdsClient;
}
@Override
public String uploadMediaAndGetMediaKey(String mediaUrl, Set accountUserIds, TwitterMediaType twitterMediaType, String name)
throws TwitterException {
final UploadMediaObjectResponse responseFromFinalize = uploadAndGetMediaId(mediaUrl, accountUserIds, twitterMediaType, name);
final String mediaIdString = responseFromFinalize.getMediaIdString();
final String mediaKey = responseFromFinalize.getMediaKey();
final Long videoSize = responseFromFinalize.getSize();
//as per documentation if media process info is null then the video is ready
if (responseFromFinalize.getUploadMediaProcessingInfo() == null) {
return mediaKey;
}
if (responseFromFinalize.getUploadMediaProcessingInfo().getUploadErrorInfo() != null) {
throw new TwitterException(responseFromFinalize.getUploadMediaProcessingInfo().getUploadErrorInfo().getMessage());
}
final String state = responseFromFinalize.getUploadMediaProcessingInfo().getState();
final Integer progressPercentage = responseFromFinalize.getUploadMediaProcessingInfo().getProgressPercentage();
if ((TwitterAdUtil.isNotNullOrEmpty(state) && state.equalsIgnoreCase("succeeded")) ||
(progressPercentage != null && progressPercentage == 100)) {
return mediaKey;
}
return waitForVideoProcessingAndReturnMediaKey(mediaKey, mediaIdString, responseFromFinalize, videoSize);
}
// ------------------------------------------------------------------- PRIVATE METHODS ----------------------------------------------------------
private UploadMediaObjectResponse uploadAndGetMediaId(String mediaUrl, Set accountUserIds, TwitterMediaType twitterMediaType, String name)
throws TwitterException {
try {
String mediaSizeInBytes = getMediaSizeInBytes(mediaUrl);
String mediaId = initiateMediaUpload(mediaSizeInBytes, accountUserIds, twitterMediaType, name);
uploadMedia(mediaUrl, mediaId, mediaSizeInBytes);
return finalizeMediaUpload(mediaId);
} catch (Exception e) {
if (e instanceof TwitterException) {
throw (TwitterException) e;
}
throw new TwitterException("Error Occurred while uploading Media", e);
}
}
private String getMediaSizeInBytes(String mediaUrl) throws TwitterException, IOException {
try {
URLConnection urlConnection = new URL(mediaUrl).openConnection();
return urlConnection.getHeaderField("Content-Length");
} catch (MalformedURLException e) {
throw new IllegalArgumentException("Invalid media url");
}
}
private String initiateMediaUpload(String mediaSizeInBytes, Set accountUserIds, TwitterMediaType twitterMediaType, String name)
throws TwitterException {
if (StringUtils.isBlank(mediaSizeInBytes)) {
throw new TwitterInvalidParameterException("Media could not be uploaded as connection could not be established");
}
Long mediaSizeInBytesLong;
try {
mediaSizeInBytesLong = Long.valueOf(mediaSizeInBytes);
} catch (NumberFormatException eX) {
throw new TwitterException("Media could not be uploaded as connection could not be established");
}
if (twitterMediaType == TwitterMediaType.IMAGE && mediaSizeInBytesLong > TwitterAdsConstants.MAX_IMAGE_SIZE_FOR_TWITTER_IN_BYTES) {
throw new TwitterInvalidParameterException("Image should be less than 5 MB in size");
}
if (twitterMediaType == TwitterMediaType.VIDEO && mediaSizeInBytesLong > TwitterAdsConstants.MAX_VIDEO_SIZE_IN_BYTES) {
throw new TwitterInvalidParameterException("Video should be less than 500 MB in size");
}
final String url = twitterAdsClient.getMediaUploadBaseUrl() + TwitterAdsConstants.UPLOAD_MEDIA_URL + TwitterAdsConstants.UPLOAD_JSON;
final HttpParameter[] parameters = createInitiateMediaUploadParams(mediaSizeInBytes, accountUserIds, twitterMediaType);
return twitterAdsClient.mediaUploadInitOrFinalize(url, parameters).getMediaIdString();
}
private UploadMediaObjectResponse finalizeMediaUpload(String mediaId) throws TwitterException {
final String url = twitterAdsClient.getMediaUploadBaseUrl() + TwitterAdsConstants.UPLOAD_MEDIA_URL + TwitterAdsConstants.UPLOAD_JSON;
final HttpParameter[] parameters = createFinalizeMediaUploadParams(mediaId);
final Type type = new TypeToken() {
}.getType();
return twitterAdsClient.executeRequest(url, parameters, type, HttpVerb.POST);
}
private HttpParameter[] createInitiateMediaUploadParams(String mediaSizeInBytes, Set accountUserIds, TwitterMediaType twitterMediaType) {
if (StringUtils.isBlank(mediaSizeInBytes)) {
throw new TwitterRuntimeException(null, new TwitterException("mediaSizeInBytes cannot be blank or null."));
}
final List params = Lists.newArrayList();
params.add(new HttpParameter(TwitterAdsConstants.PARAM_COMMAND, "INIT"));
params.add(new HttpParameter(TwitterAdsConstants.PARAM_TOTAL_BYTES, mediaSizeInBytes));
switch (twitterMediaType) {
case VIDEO:
/*
Only one attributable user can be specified per video.
Only the attributable user will be able to use the video in a Tweet.
If additional owners are also listed, they will be ignored.
*/
params.add(new HttpParameter(TwitterAdsConstants.PARAM_MEDIA_TYPE, "video/mp4"));
params.add(new HttpParameter(TwitterAdsConstants.PARAM_MEDIA_CATEGORY, "amplify_video"));
if (TwitterAdUtil.isNotEmpty(accountUserIds) && accountUserIds.size() == 1) {
final String accountUserId = accountUserIds.iterator().next();
params.add(new HttpParameter(TwitterAdsConstants.PARAM_ATTRIBUTABLE_USER_ID, accountUserId));
}
break;
case IMAGE:
params.add(new HttpParameter(TwitterAdsConstants.PARAM_MEDIA_TYPE, "image/jpeg"));
params.add(new HttpParameter(TwitterAdsConstants.PARAM_MEDIA_CATEGORY, "tweet_image"));
break;
case DM_IMAGE:
params.add(new HttpParameter(TwitterAdsConstants.PARAM_MEDIA_TYPE, "image/jpeg"));
params.add(new HttpParameter(TwitterAdsConstants.PARAM_MEDIA_CATEGORY, "dm_image"));
params.add(new HttpParameter(TwitterAdsConstants.PARAM_SHARED, true));
break;
case DM_VIDEO:
params.add(new HttpParameter(TwitterAdsConstants.PARAM_MEDIA_TYPE, "video/mp4"));
params.add(new HttpParameter(TwitterAdsConstants.PARAM_MEDIA_CATEGORY, "dm_video"));
params.add(new HttpParameter(TwitterAdsConstants.PARAM_SHARED, true));
break;
case DM_GIF:
params.add(new HttpParameter(TwitterAdsConstants.PARAM_MEDIA_TYPE, "video/gif")); //check this
params.add(new HttpParameter(TwitterAdsConstants.PARAM_MEDIA_CATEGORY, "dm_image"));
params.add(new HttpParameter(TwitterAdsConstants.PARAM_SHARED, true));
break;
default:
break;
}
if (TwitterAdUtil.isNotEmpty(accountUserIds)) {
params.add(new HttpParameter(TwitterAdsConstants.PARAM_ADDITIONAL_OWNERS, TwitterAdUtil.getCsv(accountUserIds)));
}
return params.toArray(new HttpParameter[params.size()]);
}
private void uploadMedia(String mediaUrl, String mediaId, String mediaSizeInBytes) throws TwitterException, IOException {
int segmentIndex = 0;
Long bytesLeftToUpload = Long.valueOf(mediaSizeInBytes);
InputStream inputStream = null;
BufferedInputStream bis = null;
try {
inputStream = new URL(mediaUrl).openStream();
bis = new BufferedInputStream(inputStream);
while (bytesLeftToUpload > 0) {
int totalBytesRead = 0;
byte[] chunkToUpload = new byte[2 * TwitterAdsConstants.TWO_MIB];
while (totalBytesRead < TwitterAdsConstants.TWO_MIB) {
byte[] chunk = new byte[TwitterAdsConstants.TWO_MIB];
int bytesRead = bis.read(chunk);
if (bytesRead == -1) {
break;
} else {
chunk = Arrays.copyOf(chunk, bytesRead);
for (int i = 0; i < bytesRead; i++) {
chunkToUpload[totalBytesRead++] = chunk[i];
}
}
}
if (totalBytesRead > 0) {
chunkToUpload = Arrays.copyOf(chunkToUpload, totalBytesRead);
String base64Encoding = DatatypeConverter.printBase64Binary(chunkToUpload);
appendChunk(mediaId, base64Encoding, segmentIndex);
bytesLeftToUpload -= totalBytesRead;
segmentIndex += 1;
} else {
break;
}
}
} finally {
if (inputStream != null) {
IOUtils.closeQuietly(bis);
}
}
}
private void appendChunk(String mediaId, String chunk, int segmentIndex) throws TwitterException {
String url = twitterAdsClient.getMediaUploadBaseUrl() + "media/upload.json";
List params = createAppendChunkParams(mediaId, chunk, segmentIndex);
HttpParameter[] parameters = params.toArray(new HttpParameter[params.size()]);
HttpResponse response = twitterAdsClient.postRequest(url, parameters);
int responseCode = response.getStatusCode();
if (responseCode < TwitterAdsConstants.SUCCESSFULL_CALL_BEGIN_CODE || responseCode > TwitterAdsConstants.SUCCESSFULL_CALL_END_CODE) {
throw new TwitterException(response.asString());
}
}
private List createAppendChunkParams(String mediaId, String chunk, int segment_index) {
List params = Lists.newArrayList();
params.add(new HttpParameter(TwitterAdsConstants.PARAM_COMMAND, "APPEND"));
params.add(new HttpParameter(TwitterAdsConstants.PARAM_MEDIA_ID, mediaId));
params.add(new HttpParameter(TwitterAdsConstants.PARAM_MEDIA_DATA, chunk));
params.add(new HttpParameter(TwitterAdsConstants.PARAM_SEGMENT_INDEX, segment_index));
return params;
}
private HttpParameter[] createFinalizeMediaUploadParams(String mediaId) {
final List params = Lists.newArrayList();
params.add(new HttpParameter(TwitterAdsConstants.PARAM_COMMAND, "FINALIZE"));
params.add(new HttpParameter(TwitterAdsConstants.PARAM_MEDIA_ID, mediaId));
return params.toArray(new HttpParameter[params.size()]);
}
private String waitForVideoProcessingAndReturnMediaKey(String mediaKey, String mediaIdString, UploadMediaObjectResponse statusResponse, Long videoSize)
throws TwitterException {
if (statusResponse == null) {
statusResponse = getUploadStatus(mediaIdString);
}
Long timeToWait = 0L;
Long checkAfterSeconds = statusResponse.getUploadMediaProcessingInfo().getCheckAfterSeconds();
Long maxWaitTime = decideMaxWaitTime(videoSize);
while (timeToWait < maxWaitTime) {
TwitterAdUtil.reallySleep(checkAfterSeconds * 1000);
timeToWait = timeToWait + checkAfterSeconds;
statusResponse = getUploadStatus(mediaIdString);
if (statusResponse == null) {
throw new TwitterException("Could not upload Video successfully, please select a different video");
}
//as per documentation if media process info is null then the video is ready
if (statusResponse.getUploadMediaProcessingInfo() == null) {
return mediaKey;
}
if (statusResponse.getUploadMediaProcessingInfo().getUploadErrorInfo() != null) {
throw new TwitterException(statusResponse.getUploadMediaProcessingInfo().getUploadErrorInfo().getMessage());
}
String state = statusResponse.getUploadMediaProcessingInfo().getState();
Integer progressPercentage = statusResponse.getUploadMediaProcessingInfo().getProgressPercentage();
if ((TwitterAdUtil.isNotNullOrEmpty(state) && state.equalsIgnoreCase("succeeded")) ||
(progressPercentage != null && progressPercentage == 100)) {
return mediaKey;
}
}
if (statusResponse.getUploadMediaProcessingInfo().getProgressPercentage() != null &&
statusResponse.getUploadMediaProcessingInfo().getProgressPercentage() < 100 &&
statusResponse.getUploadMediaProcessingInfo().getState() != null &&
statusResponse.getUploadMediaProcessingInfo().getState().equalsIgnoreCase("in_progress")) {
throw new TwitterException(
"Please retry playing the ad, or upload a new video, there is problem at Twitter's end in processing the " + "video");
}
throw new TwitterException(statusResponse.getUploadMediaProcessingInfo().getUploadErrorInfo().getMessage());
}
private Long decideMaxWaitTime(Long videoSize) {
Long maxWaitTime = VIDEO_SIZE_PROCESSING_WAIT_TIME_MAP.get(getVideoSizeGroup(videoSize));
if (maxWaitTime == null) {
maxWaitTime = TwitterAdsConstants.MAX_WAIT_INTERVAL_ONE_HUNDRED_FIFTY_MIB;
}
return maxWaitTime;
}
private Long getVideoSizeGroup(Long videoSize) {
if (videoSize == null) {
return TwitterAdsConstants.ONE_HUNDRED_FIFTY_MIB;
}
if (videoSize <= TwitterAdsConstants.FIFTY_MIB) {
return TwitterAdsConstants.FIFTY_MIB;
} else if (videoSize <= TwitterAdsConstants.ONE_HUNDRED_FIFTY_MIB) {
return TwitterAdsConstants.ONE_HUNDRED_FIFTY_MIB;
} else {
return TwitterAdsConstants.FIVE_HUNDRED_MIB;
}
}
private UploadMediaObjectResponse getUploadStatus(String mediaId) throws TwitterException {
final String url = twitterAdsClient.getMediaUploadBaseUrl() + "media/upload.json";
final List params = Lists.newArrayList();
params.add(new HttpParameter(TwitterAdsConstants.PARAM_COMMAND, "STATUS"));
params.add(new HttpParameter(TwitterAdsConstants.PARAM_MEDIA_ID, mediaId));
Type type = new TypeToken() {
}.getType();
return twitterAdsClient.executeRequest(url, params.toArray(new HttpParameter[params.size()]), type, HttpVerb.GET);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy