Java: [YouTube] Alle Videos eines Kanals herunterladen
Um meinen "Downloader" zu verbessern, habe ich mir überlegt was für Funktionen (mir persönlich) noch fehlen.
Da wir nun eine Methode haben um von einem Benutzernamen auf die Channel-ID schließen zu können - ist es nun möglich mit der Channel-ID vorhandene Videos zu beziehen mit Hilfe der API.
Da wir nun alle Videos eines Kanals hinzufügen können, werde ich mich an eine Lösung für das Problem der Playlists setzen.
Hierzu möchte ich die Auswahl haben, welche Videos hinzugefügt werden. Das Tutorial dazu folgt in kürze.
Verwendeter YouTube-Account:
http://youtube.com/sempervideo
JSON from URL:
http://stackoverflow.com/questions/4308554/simplest-way-to-read-json-from-a-url-in-java
- R3DST0RM aka. Dominik
Da ich ziemlich faul bin was Copy&Paste etc. angeht, wollte ich mir das Ganze etwas leichter machen, d.h. ich wollte wenn ich so etwas wie user:{YouTubeUsername} eingebe, mein "Downloader" mir alle Videos automatisch zur Download-Liste (von dem gewünschten Kanal) hinzufügt.
So weit zur Theorie kommen wir nun zur Praxis. Im Endeffekt arbeite ich hier mit JSON Objekten, welche ich von der YouTube API beziehe um damit dann automatisch rekursiv alle Video-IDs des Kanals zu beziehen.
Zunächst ist es wichtig zu wissen was wir brauchen:
- Einen YouTube Kanal - im folgenden benutze ich: sempervideo
- Die YouTube Kanal ID - werden wir mit Hilfe der API beziehen [benötigt YT-Namen]
- Unsere Java Entwicklungsumgebung
- Eine Java Klasse
Sobald wir einen API-Key besitzen können wir auch schon loslegen.
1 - Auflösen des Benutzernamen in eine Channel ID
Um mit Hilfe der API den Benutzernamen in eine Channel ID zu überführen benutzen wir folgende Anfrage:
https://www.googleapis.com/youtube/v3/channels?part=id&forUsername={USERNAME}&key={YOUR_API_KEY}
Für {USERNAME} setzen wir: sempervideo
und für {YOUR_API_KEY} setzen wir unseren API-Key ein.
Was als Antwort zurück kommt sieht wie folgt aus:
Die Channel-ID habe ich im Bild "gelb" markiert. Wenn wir in unserem Browser nun folgendes eingeben youtube.com/channel/UCCI6C8hD-hTZi2JEmS7zvQw (also youtube.com/channel/{CHANNEL_ID} ), dann landen wir auf dem gesuchten Channel.
Um das ganze nun automatisiert zu realiseren habe ich folgenden Code Snippet:
private String GetChannelIDFromUsername(String username) { try{ JSONObject obj = readJsonFromUrl("https://www.googleapis.com/youtube/v3/channels?part=id&forUsername=" + username + "&key={YOUR_API_KEY}"); JSONArray arr = obj.getJSONArray("items"); return arr.getJSONObject(0).getString("id"); }catch (Exception ex) { // Wenn kein User gefunden wird ist Channel ID "" return ""; } } // Kleine Hilfe um JSON Objekte von einer URL zu beziehen private JSONObject readJsonFromUrl(String url) throws IOException, JSONException { InputStream is = new URL(url).openStream(); try { BufferedReader rd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8"))); String jsonText = readAll(rd); JSONObject json = new JSONObject(jsonText); return json; } finally { is.close(); } } private String readAll(Reader rd) throws IOException { StringBuilder sb = new StringBuilder(); int cp; while ((cp = rd.read()) != -1) { sb.append((char) cp); } return sb.toString(); }
Da wir nun eine Methode haben um von einem Benutzernamen auf die Channel-ID schließen zu können - ist es nun möglich mit der Channel-ID vorhandene Videos zu beziehen mit Hilfe der API.
Die Theorie dafür ist wie folgt. Im JSON Objekt, bekommen wir alle Video-IDs gelistet (bis max. 50 pro Seite). Um natürlich alle zu bekommen müssen wir rekursiv mit dem nextPageToken arbeiten. Dieser Token erlaubt es uns auf die "nächste Seite" zu navigieren. Dies passiert in dem wir am Ende der URL folgendes anhängen:
&pageToken={nextPageToken_Inhalt}
Da wir am Anfang gar nicht wissen was unser "pageToken" ist, lassen wir beim ersten "Request" das "pageToken" leer ( &pageToken={LEER} ).
https://www.googleapis.com/youtube/v3/search?key={YOUR_API_KEY}&channelId=UCCI6C8hD-hTZi2JEmS7zvQw&part=id&order=date&maxResults=50&nextPageToken&pageToken=
Das Ergebnis ist dann folgendes:
Der Rekursive Code dafür ist folgender:
private String[] GetAllVideoIdsFromChannelID(String channelID, String nextPageToken){
try {
JSONObject obj = readJsonFromUrl("https://www.googleapis.com/youtube/v3/search?" +
"key={YOUR_API_KEY}&channelId=" + channelID +
"&part=id&order=date&maxResults=50&nextPageToken&pageToken=" + nextPageToken);
String token;
try {
token = obj.getString("nextPageToken");
}catch (Exception ex){
token = ""; //falls es kein Page Token mehr gibt sprich die letzte Seite erreicht wurde
}
JSONArray arr = obj.getJSONArray("items");
List itemlist = new ArrayList(); // Da ich nicht weiß wie lang mein Array wird nehme ich eine Liste
for (int i = 0; i < arr.length(); i++)
{
try {
itemlist.add(arr.getJSONObject(i).getJSONObject("id").getString("videoId"));
}
catch (Exception ex){
// Ignoriere da keine Video ID gefunden wurde in diesem Block aber es vielleicht später noch eine gibt
}
}
// Konvertiere eine Liste in ein String Array
String[] items = new String[itemlist.size()];
for (int i = 0; i < itemlist.size(); i++) {
items[i] = itemlist.get(i);
}
if(token.equals("")) //rekursive Abbruchbedingung
return items;
else //Stream.concat ist ab JAVA 8 verfügbar deshalb benutze ich dies!
return Stream.concat(Arrays.stream(items), Arrays.stream(GetAllVideoIdsFromChannelID(channelID, token)))
.toArray(String[]::new);
}catch (IOException ex){
System.err.println("IO Exception");
return null;
}
}
Nachdem ich das ganze nun in meinen Downloader eingebunden habe sehen wir folgendes Resultat:Da wir nun alle Videos eines Kanals hinzufügen können, werde ich mich an eine Lösung für das Problem der Playlists setzen.
Hierzu möchte ich die Auswahl haben, welche Videos hinzugefügt werden. Das Tutorial dazu folgt in kürze.
Verwendeter YouTube-Account:
http://youtube.com/sempervideo
JSON from URL:
http://stackoverflow.com/questions/4308554/simplest-way-to-read-json-from-a-url-in-java
- R3DST0RM aka. Dominik
Kommentare
Kommentar veröffentlichen