Публикация видео на канале пользователя YouTube

от автора

Разрабатывая приложение под Android для создания слайд-шоу из фотографий, столкнулся с задачей публиковать готовые ролики на личном канале пользователя YouTube.
Пришлось повозиться, т.к. во-первых, применяем облачные вычисления и обработка данных может занимать дольше времени, чем действует token. А во-вторых, чтобы реализовать функцию, пришлось столкнуться с некоторыми неточностями и вопросами в мануале Google. Подробности под катом.


Суть задачи:
Когда пользователь выбрал все фотографии на своем Android и определился с музыкальным сопровождением, данные для обработки отправляются на сервер, а пользователю предлагается указать, куда нужно разместить готовое видео: в on-line коллекцию на нашем сервере, на общем канале приложения в YouTube или своем личном канале YouTube. (Также на электронный адрес приходит ссылка для скачивания видео).
В случае выбора публикации на личном канале, мы спрашиваем разрешение и при подтверждении получаем token, необходимый для доступа к каналу, который сохраняется на сервере вместе с данными пользователя и остается валидным 60 минут.
Но если у пользователя медленный интернет (длительная закачка фотографий) или накопилась очередь на сервере, token становится невалидным, требуется повторный запрос. Для этой ситуации мы сразу стали запрашивать token и refresh token, срок действия второго неограничен и мы можем публиковать видео даже по истечению 60 минут.
Исправленные ошибки:
Подробнее что такое token и refresh token и как их использовать есть в мануале , но здесь есть вещи, которые работали не совсем так, как было описано.
1. Самая первая сложность: полученный на девайсе token был невалидным при использовании на сервере.

В итоге решили вот так
На Android используем:

Intent intent = new Intent(...); intent.setData(Uri.parse("https://accounts.google.com/o/oauth2/auth?"+ "scope= " + scope + "&" + "redirect_uri=" + redirect_uri + "&" + "response_type=" + response_type + "&" + "client_id=" + client_id)); startActivityForResult(intent);  

В результате получаем код авторизации, затем получаем сами token и refresh token

HttpPosthttppost = newHttpPost("https://accounts.google.com/o/oauth2/token"); httppost.setHeader("Content-Type", "application/x-www-form-urlencoded"); List<NameValuePair>nameValuePairs = newArrayList<NameValuePair>(); nameValuePairs.add(newBasicNameValuePair("code", code)); nameValuePairs.add(newBasicNameValuePair("client_id", client_id)); nameValuePairs.add(newBasicNameValuePair("redirect_uri", redirect_uri)); nameValuePairs.add(newBasicNameValuePair("grant_type", "authorization_code")); httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs)); HttpResponse response = httpclient.execute(httppost); HttpEntity entity = response.getEntity(); BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent())); String responseLines; while ((line = reader.readLine()) != null) { 	responseLines += line; } JSONObject jsonObject = new JSONObject(responseLines); 

В результате получаем ответ вида:

{   "access_token":"1/fFAGRNJru1FTz70BzhT3Zg",   "expires_in":3920,   "token_type":"Bearer",   "refresh_token":"1/xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI" } 

Получаем token и refresh token и отсылаем их на сервер
На стороне сервера так:
Для проверки валидности токена:

var verificationUri = "https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=" + youTubeToken;                 var hc = new HttpClient(); var response = hc.GetAsync(verificationUri).Result; string tokenInfo = response.Content.ReadAsStringAsync().Result; JsonTextParser parse = new JsonTextParser(); JsonObject jsonObj = parse.Parse(tokenInfo); 

В результате получаем ответ вида:

{   "access_token":"1/fFAGRNJru1FTz70BzhT3Zg",   "expires_in":320,   "token_type":"Bearer" } 

Если времени жизни token-а достаточно, то используем его, если нет, то получаем новый token, использую refresh token:

WebClient client = new WebClient(); NameValueCollectionloginFormValues = newNameValueCollection(); loginFormValues.Add("client_id", client_id); loginFormValues.Add("refresh_token", refreshYouTubeToken); loginFormValues.Add("grant_type", "refresh_token"); Byte[] response = client.UploadValues("https://accounts.google.com/o/oauth2/token", loginFormValues); string result = Encoding.UTF8.GetString(response); JsonTextParser parse = newJsonTextParser(); JsonObjectjsonObj = parse.Parse(result); 

В результате получаем ответ вида:

{   "access_token":"1/fFBGRNJru1FQd44AzqT3Zg",   "expires_in":3600,   "token_type":"Bearer", } 

Используем полученный token по назначению.

2. Далее параметры «scope», «state» и «client_secret»: в некоторых случаях, где по документации эти параметры должны быть указаны обязательно, программа выдавала ошибку, если мы ставили их, и прекрасно работала, если не ставили. И наоборот – иногда их приходилось использовать не только там, где указано.

Например, параметр «state» часто возвращает значение в виде ближайшего штата США, где расположен сервер Google – это повышает скорость обмена данными между сервером и девайсом. В некоторых случаях этот параметр указан как обязательный. Но в России он бесполезен.

«scope» отвечает за то, какой сервис мы хотим использовать, обращаясь к Google. Непонятно, где получить полный список значений этого параметра. Поскольку нам нужен был YouTube, мы, указав его, просто угадали. При этом непонятно, какое значение параметра должно быть, если мы хотим обратиться, например, к Гугл-календарю?

В одном из запросов Google по документации нужен параметр client_secret. У нас нет его. А если мы получаем и указываем – ничего не работает.

3. Большая часть нашего приложения сделана на webserver. Но при запросе token-а webserver был невалидный, поэтому мы применили installedapps и запросы к нему подошли. Если честно, не поняли, почему так.

4. В документации developers.google.com/accounts/docs/OAuth2InstalledApp#formingtheurl указаны два возможных варианта параметра redirect_uri: “urn:ietf:wg:oauth:2.0:oob” и localhost. Как использовать localhost, мне было непонятно, с “urn:ietf:wg:oauth:2.0:oob” все работало.

5. Также открытым остается вопрос, как использовать token, полученный с Android — аккаунта на самом девайсе. Т.е. к Android привязан аккаунт, и именно с этого аккаунта можно получить token, но неясно, как его можно использовать. Мы пользуемся тем, что запрашиваем с аккаунта пользователя.

6. Непонятно еще, какой scope использовать именно для «входа через аккаунт GOOGLE»? У нас используется «для управления YouTube».

7. А еще интересно, почему библиотека Google для OAUTH2 Android так много весит? Используя ее, наше приложение становится в несколько раз тяжелее, поэтому от ее использования мы отказались.

Спасибо за внимание! Буду рад, если кому-то мои комментарии помогут, а также, если кто-то сможет ответить на мои вопросы.

ссылка на оригинал статьи http://habrahabr.ru/post/171035/