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

в 4:42, , рубрики: android, YouTube, видео, Облачные вычисления, метки: , , ,

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

Суть задачи:
Когда пользователь выбрал все фотографии на своем 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 так много весит? Используя ее, наше приложение становится в несколько раз тяжелее, поэтому от ее использования мы отказались.

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

Автор: Santori

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js