YouTube propone una vasta serie di interfacce per la gestione automatizzata dei video; in quest'articolo descriveremo l'operazione più interessante dal punto di vista dell'automazione, cioè l'attività di pubblicazione ("upload"), effettuata tramite Java, le API (Application Programming Interface) del servizio di Mountain View e le sue librerie ufficiali.
Account ed autorizzazioni
La prima cosa che bisogna fare per poter utilizzare le API di YouTube è quella di procurarsi un account Google, grazie ad esso si potrà accedere all'interfaccia Developer Console di Google dove registrare la propria applicazione. La creazione del progetto è piuttosto semplice e si limita a definire il nome dello stesso. Passo successivo sarà quello di dare l'autorizzazione alle API che vorrete utilizzare. Nel nostro caso abilitiamo "YouTube Data API v3".
A questo punto si dovrà creare una nuova chiave (public API access) per definire gli IP da cui l'applicazione effettuerà la chiamata verso i server di YouTube. Verrà consegnata una API key da utilizzare in seguito per l'integrazione.
Una volta ottenuta la chiave si dovrà creare un identificativo del client, che potrà variare in funzione del tipo di applicazione progettata, Web, service o mobile:
Nell'esempio che descriveremo nei prossimi paragrafi utilizzeremo un servizio di tipo "service account", per il quale non sarà necessario aggiungere nessun tipo di informazione. Appena creato il Client ID potremo recuperare il codice attraverso un file JSON che la stessa interfaccia ci permetterà di scaricare. A questo punto avremo le chiavi di accesso al sistema di YouTube attraverso il nostro account.
Librerie e progetti di riferimento
Prima di passare all'esempio concreto è bene chiarire che la relativa semplicità che mostreremo è nascosta da una grande quantità di codice giá scritto e librerie che riutilizzeremo in maniera più o meno consapevole. Il progetto di riferimento per questa trattazione è basato sugli esempi di sviluppo delle stesse YouTube Data API che si fonda su google-api-java-client che a sua volta si basa su Google HTTP Client Library for Java e Google OAuth Client Library for Java. Si avrà quindi bisogno di una buona quantità di altri progetti come Guava, Jetty e le relative dipendenze. Qui le riassumo in modo che possiate linkarle in maniera diretta. Se doveste usare Maven si potrà fare riferimento ai repository ufficiali dei suddetti progetti.
- commons-logging-1.1.1.jar
- google-api-client-1.17.0-rc.jar
- google-api-client-java6-1.17.0-rc.jar
- google-api-services-oauth2-v2-rev63-1.17.0-rc.jar
- google-api-services-YouTube-v3-rev94-1.17.0-rc.jar
- google-http-client-1.17.0-rc.jar
- google-http-client-jackson2-1.17.0-rc.jar
- google-oauth-client-1.17.0-rc.jar
- google-oauth-client-java6-1.17.0-rc.jar
- google-oauth-client-jetty-1.17.0-rc.jar
- gson-2.1.jar
- guava-16.0.jar
- httpclient-4.0.1.jar
- httpcore-4.0.1.jar
- jackson-core-2.1.3.jar
- jackson-core-asl-1.9.11.jar
- jetty-6.1.26.jar
- jetty-util-6.1.26.jar
- jsr305-1.3.9.jar
- log4j-1.2.14.jar
- protobuf-java-2.4.1.jar
- servlet-api.jar
- xpp3-1.1.4c.jar
Dal punto di vista architetturale utilizzeremo le API in stile REST in una maniera simile a quanto abbiamo scritto relativamente alla tecnologia CXF, con lo scambio di informazioni attraverso protocollo HTTP. Attraverso le librerie di Jetty verrà messo su un piccolo Web server per poter comunicare con i server YouTube e, nel nostro caso, essere capaci di pubblicare i video.
Codice di esempio Upload
L'obiettivo del nostro esempio è quello di descrivere come sia possibile far uso delle API di YouTube per pubblicare un video nel nostro account. La prima cosa da fare, seguendo le regole della programmazione Object Oriented, sarà quella di creare la nostrá entità, nel nostro caso una classe che chiameremo YTVideo
, che conterrà tutte le informazioni utili (o almeno il minimo set necessario) per poter pubblicare un video.
package html.it.YouTube.model;
…
public class YTVideo {
String code;
String title;
String description;
String localFile;
String privacy;
List<String> tags;
String ytCode;
String ytStatus;
private long size;
… // costruttore e relativi getter e setter
}
Un semplicissimo bean ci permetterà di gestire le informazioni base per la pubblicazione, come il titolo, la descrizione del video e attributi opzionali molto utili come i tag e la risorsa vera e propria, rappresentata dal file (potreste decidere di rendere la cosa più strutturata con ulteriori entità, come YouTubeFile
o YouTubeTag
). Infine, molto utili per le integrazioni, attributi come lo stato di pubblicazione o il codice che YouTube assegnerà alla risorsa (rispettivamente ytStatus
e ytCode
). Anche qui, per semplicità ci limiteremo a utilizzare degli String
.
Sempre per mantenere un minimo di astrazione, creiamo un'interfaccia, che estenderemo con una classe che sarà il nostro vero controller applicativo.
package html.it.YouTube;
import html.it.YouTube.model.YTVideo;
import java.io.IOException;
public interface YTUploader {
String uploadYTVideo(YTVideo video) throws IOException;
}
Nella prossima parte di questa trattazione verranno analizzate tutte le operazioni e il codice necessari per i passaggi che vanno dall'autenticazione fino all'upload dei contenuti.
Inizializzazione della classe
Prima di analizzare il codice, cerchiamo di descriverlo in pseudo linguaggio e di capire la sua logica applicativa. Il nostro metodo ha l'obiettivo di pubblicare su YouTube il video ricevuto e restituire il codice della piattaforma; si avrà quindi:
- Autenticazione alle API di YouTube (solo all'inizializzazione della classe).
- Definizione del video da pubblicare (semplice conversione dal nostro oggetto all'oggetto utilizzato dalle API).
- Definizione del flusso dei dati (cioè il file che contiene il video).
- Configurazione dei parametri utili all'upload.
- Upload del video ed attesa (metodo sincrono) del codice YouTube di ritorno.
package html.it.youtube;
…
public class YTSimpleUploader implements YTUploader {
private static Logger log = Logger.getLogger(YTSimpleUploader.class);
private YouTube youtube;
private static final String VIDEO_FILE_FORMAT = "video/*";
private static final int UPLOAD_CHUNK_SIZE = MediaHttpUploader.MINIMUM_CHUNK_SIZE*5;
public YTSimpleUploader() throws IOException{
List<String> scopes = Lists.newArrayList("https://www.googleapis.com/auth/youtube.upload");
Credential credential = Auth.authorize(scopes, "uploadvideo");
youtube = new YouTube.Builder(Auth.HTTP_TRANSPORT, Auth.JSON_FACTORY, credential).setApplicationName(
"html-sample").build();
}
L'inizializzazione della nostra classe permette di effettuare l'autenticazione durante la creazione dell'oggetto. In pratica si definisce l'endpoint del servizio REST da usare (https://www.googleapis.com/auth/youtube.upload
), il tipo di operazione (uploadvideo
) e si crea l'oggetto Youtube
, che sará il punto di accesso al servizio. Si potrà creare la classe Auth
, a partire dall'esempio delle API, editandola opportunamente come segue:
package com.google.api.services.samples.youtube.cmdline;
…
public class Auth {
…
public static Credential authorize(List<String> scopes, String credentialDatastore) throws IOException {
// Caricamento delle chiavi
Reader clientSecretReader = new InputStreamReader(Auth.class.getResourceAsStream("/client_secrets.json"));
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, clientSecretReader);
// Creazione del datastore
FileDataStoreFactory fileDataStoreFactory = new FileDataStoreFactory(new File(System.getProperty("user.home") + "/" + CREDENTIALS_DIRECTORY));
DataStore<StoredCredential> datastore = fileDataStoreFactory.getDataStore(credentialDatastore);
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, scopes).setCredentialDataStore(datastore)
.build();
LocalServerReceiver localReceiver = new LocalServerReceiver.Builder().setPort(8899).build();
return new AuthorizationCodeInstalledApp(flow, localReceiver).authorize("your user name");
}
}
Si dovrà quindi modificare il file in cui è stata salvata la chiave segreta ed editare il datastore (o mantenete quello di default) dove verranno salvate le informazioni relative ai privilegi. Si faccia poi attenzione alla porta utilizzata per il servizio (queste API creeranno socket a basso livello per la connessione da e verso il Web service HTTP REST) e, infine, si cambi l'utente per l'autorizzazione (l'utente Google).
Upload del video su YouTube
Si torni alla classe YTSimpleUploader
per osservare l'intero flusso per la pubblicazione.
public String uploadYTVideo(YTVideo video) throws IOException{
log.info("Uploading "+video);
//Gestione metadati, uso di varie classi di supporto, conversione dal nostro oggetto
Video videoObjectDefiningMetadata = new Video();
VideoStatus status = new VideoStatus();
status.setPrivacyStatus(video.getPrivacy());
videoObjectDefiningMetadata.setStatus(status);
VideoSnippet snippet = new VideoSnippet();
snippet.setTitle(video.getTitle());
snippet.setDescription(video.getDescription());
List<String> tags = video.getTags();
snippet.setTags(tags);
videoObjectDefiningMetadata.setSnippet(snippet);
//Stream, importante è il set della dimensione del file per poi calcolare la percentuale di upload
File media=new File(video.getLocalFile());
FileInputStream fis=new FileInputStream(media);
InputStreamContent mediaContent = new InputStreamContent(VIDEO_FILE_FORMAT,fis);
mediaContent.setLength(media.length());
/*
* Recuperiamo l'oggetto che si occuperà dell'upload. Passiamo i metadati, il video sotto forma
* di stream e alcune informazioni di ritorno (come lo status, o il codice del video,
* ottenuto dallo snippet)
* */
YouTube.Videos.Insert videoInsert = youtube.videos().insert("snippet,statistics,status",
videoObjectDefiningMetadata, mediaContent);
//Qui definiamo i parametri per effettuare l'upload
MediaHttpUploader uploader = videoInsert.getMediaHttpUploader();
//E' possibile il metodo di upload diretto, non consigliato per grandi file di upload
uploader.setDirectUploadEnabled(false);
//Il valore deve essere multiplo di MediaHttpUploader.MINIMUM_CHUNK_SIZE (circa 220KB)
uploader.setChunkSize(UPLOAD_CHUNK_SIZE);
//Creiamo una anonymous class listener, utile per controllare lo stato dell'upload
MediaHttpUploaderProgressListener progressListener = new MediaHttpUploaderProgressListener() {
public void progressChanged(MediaHttpUploader uploader) throws IOException {
switch (uploader.getUploadState()) {
case INITIATION_STARTED:
log.info("Initiation Started");
break;
case INITIATION_COMPLETE:
log.info("Initiation Completed");
break;
case MEDIA_IN_PROGRESS:
log.info("Upload in progress " + Math.round(uploader.getProgress()*100)+"%");
break;
case MEDIA_COMPLETE:
log.info("Upload Completed!");
break;
case NOT_STARTED:
log.info("Upload Not Started!");
break;
}
}
};
uploader.setProgressListener(progressListener);
//Esecuzione dell'upload
Video returnedVideo = videoInsert.execute();
return returnedVideo.getId();
}
Il flusso esecutivo è piuttosto semplice ed immediato, ovviamente, avendo a che fare con scambio di file (di grosse dimensioni) su rete è bene ottimizzare i parametri in funzione delle risorse a disposizione (come spiegato nei commenti).
Test per il funzionamento della classe
Ora non resta che recuperare un video (praticamente qualsiasi formato è valido per YouTube) e provare con una classe di esempio.
package it.html.test;
…
public class SimpleMain {
public static void main(String[] args) throws IOException {
List<String> tags=new ArrayList<String>();
tags.add("test");
tags.add("html.it");
//Assicuratevi di avere un file nella giusta posizione e settare private in modo che il video sia visibile solo a voi
YTVideo video=new YTVideo("Simple test", "Simple test description", "./resources/test.mp4", tags, "private");
YTUploader uploader=new YTSimpleUploader();
String code = uploader.uploadYTVideo(video);
System.out.println("Hai appena pubblicato il video "+code+" - http://www.youtube.com/watch?v="+code);
}
}
La prima volta che si eseguirà un upload da un nuovo IP autorizzato, verrà chiesto di autenticare l'applicazione. Da browser si aprirà una finestra Web, altrimenti, apparirá su linea di comando, un link per effettuare l'operazione di upload.