/*
* Copyright (c) 2013 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.ytdl;
import android.app.IntentService;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
import android.util.Log;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.client.util.ExponentialBackOff;
import com.google.api.services.youtube.YouTube;
import com.google.common.collect.Lists;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
/**
* @author Ibrahim Ulukaya <ulukaya@google.com>
* <p/>
* Intent service to handle uploads.
*/
public class UploadService extends IntentService {
/**
* defines how long we'll wait for a video to finish processing
*/
private static final int PROCESSING_TIMEOUT_SEC = 60 * 20; // 20 minutes
/**
* controls how often to poll for video processing status
*/
private static final int PROCESSING_POLL_INTERVAL_SEC = 60;
/**
* how long to wait before re-trying the upload
*/
private static final int UPLOAD_REATTEMPT_DELAY_SEC = 60;
/**
* max number of retry attempts
*/
private static final int MAX_RETRY = 3;
private static final String TAG = "UploadService";
/**
* processing start time
*/
private static long mStartTime;
final HttpTransport transport = AndroidHttp.newCompatibleTransport();
final JsonFactory jsonFactory = new GsonFactory();
GoogleAccountCredential credential;
/**
* tracks the number of upload attempts
*/
private int mUploadAttemptCount;
public UploadService() {
super("YTUploadService");
}
private static void zzz(int duration) throws InterruptedException {
Log.d(TAG, String.format("Sleeping for [%d] ms ...", duration));
Thread.sleep(duration);
Log.d(TAG, String.format("Sleeping for [%d] ms ... done", duration));
}
private static boolean timeoutExpired(long startTime, int timeoutSeconds) {
long currTime = System.currentTimeMillis();
long elapsed = currTime - startTime;
if (elapsed >= timeoutSeconds * 1000) {
return true;
} else {
return false;
}
}
@Override
protected void onHandleIntent(Intent intent) {
Uri fileUri = intent.getData();
String chosenAccountName = intent.getStringExtra(MainActivity.ACCOUNT_KEY);
credential =
GoogleAccountCredential.usingOAuth2(getApplicationContext(), Lists.newArrayList(Auth.SCOPES));
credential.setSelectedAccountName(chosenAccountName);
credential.setBackOff(new ExponentialBackOff());
String appName = getResources().getString(R.string.app_name);
final YouTube youtube =
new YouTube.Builder(transport, jsonFactory, credential).setApplicationName(
appName).build();
try {
tryUploadAndShowSelectableNotification(fileUri, youtube);
} catch (InterruptedException e) {
// ignore
}
}
private void tryUploadAndShowSelectableNotification(final Uri fileUri, final YouTube youtube) throws InterruptedException {
while (true) {
Log.i(TAG, String.format("Uploading [%s] to YouTube", fileUri.toString()));
String videoId = tryUpload(fileUri, youtube);
if (videoId != null) {
Log.i(TAG, String.format("Uploaded video with ID: %s", videoId));
tryShowSelectableNotification(videoId, youtube);
return;
} else {
Log.e(TAG, String.format("Failed to upload %s", fileUri.toString()));
if (mUploadAttemptCount++ < MAX_RETRY) {
Log.i(TAG, String.format("Will retry to upload the video ([%d] out of [%d] reattempts)",
mUploadAttemptCount, MAX_RETRY));
zzz(UPLOAD_REATTEMPT_DELAY_SEC * 1000);
} else {
Log.e(TAG, String.format("Giving up on trying to upload %s after %d attempts",
fileUri.toString(), mUploadAttemptCount));
return;
}
}
}
}
private void tryShowSelectableNotification(final String videoId, final YouTube youtube)
throws InterruptedException {
mStartTime = System.currentTimeMillis();
boolean processed = false;
while (!processed) {
processed = ResumableUpload.checkIfProcessed(videoId, youtube);
if (!processed) {
// wait a while
Log.d(TAG, String.format("Video [%s] is not processed yet, will retry after [%d] seconds",
videoId, PROCESSING_POLL_INTERVAL_SEC));
if (!timeoutExpired(mStartTime, PROCESSING_TIMEOUT_SEC)) {
zzz(PROCESSING_POLL_INTERVAL_SEC * 1000);
} else {
Log.d(TAG, String.format("Bailing out polling for processing status after [%d] seconds",
PROCESSING_TIMEOUT_SEC));
return;
}
} else {
ResumableUpload.showSelectableNotification(videoId, getApplicationContext());
return;
}
}
}
private String tryUpload(Uri mFileUri, YouTube youtube) {
long fileSize;
InputStream fileInputStream = null;
String videoId = null;
try {
fileSize = getContentResolver().openFileDescriptor(mFileUri, "r").getStatSize();
fileInputStream = getContentResolver().openInputStream(mFileUri);
String[] proj = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(mFileUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
videoId = ResumableUpload.upload(youtube, fileInputStream, fileSize, mFileUri, cursor.getString(column_index), getApplicationContext());
} catch (FileNotFoundException e) {
Log.e(getApplicationContext().toString(), e.getMessage());
} finally {
try {
fileInputStream.close();
} catch (IOException e) {
// ignore
}
}
return videoId;
}
}