Skip to content

Instantly share code, notes, and snippets.

@AKB428
Last active August 29, 2015 14:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AKB428/4a9fdd1e52bcce6da3e7 to your computer and use it in GitHub Desktop.
Save AKB428/4a9fdd1e52bcce6da3e7 to your computer and use it in GitHub Desktop.
Twitterの新機能である動画投稿のツイートからの動画URLをJSONから抽出し、動画をじゃぶじゃぶダウンロードする ref: http://qiita.com/AKB428/items/f61f2e87e652eb1690d9
Twitter twitter = new TwitterFactory(cb.build()).getInstance();
twitter.setOAuthConsumer(twitterModel.getConsumerKey(), twitterModel.getConsumerSecret());
twitter.setOAuthAccessToken(new AccessToken(twitterModel.getAccessToken(), twitterModel.getAccessToken_secret()));
List<Status> statuses = twitter.getUserTimeline(args[0]);
System.out.println("Showing home timeline.");
for (Status status : statuses) {
System.out.println(status.getUser().getName() + ":" + status.getText());
String rawJSON = DataObjectFactory.getRawJSON(status);
System.out.println(rawJSON);
// Twitter4Jを使用し画像URL(1枚のみ)を取得
MediaEntity[] arrMedia = status.getMediaEntities();
for (MediaEntity media : arrMedia) {
System.out.println("MediaEntity= " + media.getMediaURL());
}
// Twitter4Jを使用し画像URL(複数)を取得
MediaEntity[] arrMediaExt = status.getExtendedMediaEntities();
for (MediaEntity media : arrMediaExt) {
System.out.println("ExtendedMediaEntities= " + media.getMediaURL());
}
URLEntity[] entity = status.getURLEntities();
for (URLEntity urlEntity : entity) {
System.out.println("URLEntity= " + urlEntity.getExpandedURL());
}
// VideoInfoクラスを使用し動画URLを取得
List<VideoInfo> videoInfoList = VideoInfo.fromRawJson(rawJSON);
if (videoInfoList != null) {
for (VideoInfo videoInfo : videoInfoList) {
videoInfo.printFormatVideoInfo();
}
}
}
video_info
|-- duration_millis : 11965
|-- aspect_ratio : [1, 1]
|-- variants : [
{
|-- bitrate : 832000
|-- content_type : video/webm
|-- url : https://video.twimg.com/ext_tw_video/561814337585569793/pu/vid/480x480/QKhWzHdEK0QDPtg8.webm
}
{
|-- bitrate : 1280000
|-- content_type : video/mp4
|-- url : https://video.twimg.com/ext_tw_video/561814337585569793/pu/vid/720x720/IdBKWbELa8D8cqLD.mp4
}
{
|-- bitrate : 320000
|-- content_type : video/mp4
|-- url : https://video.twimg.com/ext_tw_video/561814337585569793/pu/vid/240x240/7EK_gSEEPFBATzXI.mp4
}
{
|-- bitrate : 832000
|-- content_type : video/mp4
|-- url : https://video.twimg.com/ext_tw_video/561814337585569793/pu/vid/480x480/QKhWzHdEK0QDPtg8.mp4
}
{
|-- bitrate : null
|-- content_type : application/x-mpegURL
|-- url : https://video.twimg.com/ext_tw_video/561814337585569793/pu/pl/6G6kgS5lRXAOiCcg.m3u8
}
]
package akb428.tkws;
import java.io.IOException;
import java.util.List;
import org.codehaus.jackson.JsonProcessingException;
import akb428.twitter.model.VideoInfo;
//arg[0] = ツイッターの生JSON
public class TwitterVideoInfoParserSample {
public static void main(String[] args) throws JsonProcessingException, IOException {
String rawJsonString = args[0];
List<VideoInfo> videoInfoList = VideoInfo.fromRawJson(rawJsonString);
for (VideoInfo videoInfo : videoInfoList) {
videoInfo.printFormatVideoInfo();
}
}
}
package akb428.twitter.model.videoinfo;
public class Variant {
private Long bitrate = null;
private String contentType = null;
private String url = null;
//TODO publicなgetter setter メソッドは記載省略
}
{
"contributors": null,
"text": "ツイッター動画投稿テスト http://t.co/y9hjEfABw1",
"geo": null,
"retweeted": false,
"in_reply_to_screen_name": null,
"possibly_sensitive": false,
"truncated": false,
"lang": "ja",
"entities": {
"symbols": [],
"urls": [],
"hashtags": [],
"media": [
{
"sizes": {
"thumb": {
"w": 150,
"resize": "crop",
"h": 150
},
"small": {
"w": 340,
"resize": "fit",
"h": 340
},
"large": {
"w": 720,
"resize": "fit",
"h": 720
},
"medium": {
"w": 600,
"resize": "fit",
"h": 600
}
},
"id": 561814337585569800,
"media_url_https": "https://pbs.twimg.com/ext_tw_video_thumb/561814337585569793/pu/img/ZyW1taT-3_AkDB-4.jpg",
"media_url": "http://pbs.twimg.com/ext_tw_video_thumb/561814337585569793/pu/img/ZyW1taT-3_AkDB-4.jpg",
"expanded_url": "http://twitter.com/n428dev/status/561814427150741505/video/1",
"indices": [
13,
35
],
"id_str": "561814337585569793",
"type": "photo",
"display_url": "pic.twitter.com/y9hjEfABw1",
"url": "http://t.co/y9hjEfABw1"
}
],
"user_mentions": []
},
"in_reply_to_status_id_str": null,
"id": 561814427150741500,
"extended_entities": {
"media": [
{
"sizes": {
"thumb": {
"w": 150,
"resize": "crop",
"h": 150
},
"small": {
"w": 340,
"resize": "fit",
"h": 340
},
"large": {
"w": 720,
"resize": "fit",
"h": 720
},
"medium": {
"w": 600,
"resize": "fit",
"h": 600
}
},
"id": 561814337585569800,
"media_url_https": "https://pbs.twimg.com/ext_tw_video_thumb/561814337585569793/pu/img/ZyW1taT-3_AkDB-4.jpg",
"video_info": {
"duration_millis": 11965,
"variants": [
{
"bitrate": 832000,
"content_type": "video/webm",
"url": "https://video.twimg.com/ext_tw_video/561814337585569793/pu/vid/480x480/QKhWzHdEK0QDPtg8.webm"
},
{
"bitrate": 1280000,
"content_type": "video/mp4",
"url": "https://video.twimg.com/ext_tw_video/561814337585569793/pu/vid/720x720/IdBKWbELa8D8cqLD.mp4"
},
{
"bitrate": 320000,
"content_type": "video/mp4",
"url": "https://video.twimg.com/ext_tw_video/561814337585569793/pu/vid/240x240/7EK_gSEEPFBATzXI.mp4"
},
{
"bitrate": 832000,
"content_type": "video/mp4",
"url": "https://video.twimg.com/ext_tw_video/561814337585569793/pu/vid/480x480/QKhWzHdEK0QDPtg8.mp4"
},
{
"content_type": "application/x-mpegURL",
"url": "https://video.twimg.com/ext_tw_video/561814337585569793/pu/pl/6G6kgS5lRXAOiCcg.m3u8"
}
],
"aspect_ratio": [
1,
1
]
},
"media_url": "http://pbs.twimg.com/ext_tw_video_thumb/561814337585569793/pu/img/ZyW1taT-3_AkDB-4.jpg",
"expanded_url": "http://twitter.com/n428dev/status/561814427150741505/video/1",
"indices": [
13,
35
],
"id_str": "561814337585569793",
"type": "video",
"display_url": "pic.twitter.com/y9hjEfABw1",
"url": "http://t.co/y9hjEfABw1"
}
]
},
"source": "<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>",
"in_reply_to_user_id_str": null,
"favorited": false,
"in_reply_to_status_id": null,
"retweet_count": 0,
"created_at": "Sun Feb 01 09:12:52 +0000 2015",
"in_reply_to_user_id": null,
"favorite_count": 0,
"id_str": "561814427150741505",
"place": null,
"user": {
"location": "",
"default_profile": true,
"profile_background_tile": false,
"statuses_count": 66,
"lang": "ja",
"profile_link_color": "0084B4",
"id": 826661216,
"following": false,
"protected": false,
"profile_location": null,
"favourites_count": 5,
"profile_text_color": "333333",
"description": "428で働くプログラマー",
"verified": false,
"contributors_enabled": false,
"profile_sidebar_border_color": "C0DEED",
"name": "AKB428",
"profile_background_color": "C0DEED",
"created_at": "Sun Sep 16 07:06:52 +0000 2012",
"is_translation_enabled": false,
"default_profile_image": false,
"followers_count": 9,
"profile_image_url_https": "https://pbs.twimg.com/profile_images/507934202185011200/HcQGt2_r_normal.jpeg",
"geo_enabled": false,
"profile_background_image_url": "http://abs.twimg.com/images/themes/theme1/bg.png",
"profile_background_image_url_https": "https://abs.twimg.com/images/themes/theme1/bg.png",
"follow_request_sent": false,
"entities": {
"description": {
"urls": []
}
},
"url": null,
"utc_offset": 32400,
"time_zone": "Irkutsk",
"notifications": false,
"profile_use_background_image": true,
"friends_count": 40,
"profile_sidebar_fill_color": "DDEEF6",
"screen_name": "n428dev",
"id_str": "826661216",
"profile_image_url": "http://pbs.twimg.com/profile_images/507934202185011200/HcQGt2_r_normal.jpeg",
"listed_count": 0,
"is_translator": false
},
"coordinates": null
}
package akb428.twitter.model;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.ObjectMapper;
import akb428.twitter.model.videoinfo.Variant;
public class VideoInfo {
private Long durationMillis = 0L;
private List<Variant> variants = null;
private List<Long> aspectRatio = null;
/**
* ツイートの生JSON文字列からVideoInfo情報をListにして返却します VideoInfoが見つからない場合はnullを返却します
* ツイートの生JSONはTwitter4Jであれば
*
* ----------------------------------------------------------------
* ConfigurationBuilder cb = new ConfigurationBuilder();
* cb.setJSONStoreEnabled(true); String rawJsonString =
* DataObjectFactory.getRawJSON(Status status);
* ----------------------------------------------------------------
*
* で取得できます。
*
* @param rawJsonString
* JSON文字列
* @return VideoInfoのリスト。ビデオ情報がない場合はnull
* @throws JsonProcessingException
* @throws IOException
*/
public static List<VideoInfo> fromRawJson(String rawJsonString) throws JsonProcessingException, IOException {
List<VideoInfo> videoInfoList = new ArrayList<>();
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(rawJsonString);
JsonNode extentdedEntitiesNode = rootNode.get("extended_entities");
if (extentdedEntitiesNode == null) {
return null;
}
JsonNode mediaNodo = extentdedEntitiesNode.get("media");
if (mediaNodo == null) {
return null;
}
Iterator<JsonNode> mediaNodeList = mediaNodo.getElements();
// mediaノードにはビデオ以外もあるので、動画情報のみ取得するようにする
// video_infoがあるかどうか
// media.type="video" かどうか
// のいずれかで判定する
// media: [ {type: video, video_info:{}}, {type: video, video_info:{}},
// {}, ]
while (mediaNodeList.hasNext()) {
JsonNode videoNode = mediaNodeList.next().get("video_info");
if (videoNode != null) {
VideoInfo videoInfo = fromVideoJsonNode(videoNode);
videoInfoList.add(videoInfo);
}
}
return videoInfoList;
}
/**
* videoInfo(JsonNodeクラス)をVideoInfoクラスに変換します
* VideoInfoクラスの各フィールドに対応するJSONプロパティが存在しないときはフィールドには何も設定しません。(nullのまま)
*
* @param videoNode
* @return
*/
public static VideoInfo fromVideoJsonNode(JsonNode videoNode) {
VideoInfo videoInfo = new VideoInfo();
JsonNode durationMillisNode = videoNode.get("duration_millis");
if (durationMillisNode != null) {
videoInfo.setDurationMillis(durationMillisNode.getLongValue());
}
JsonNode aspectRatioNode = videoNode.get("aspect_ratio");
if (aspectRatioNode != null) {
List<Long> aspectRatioList = new ArrayList<>();
Iterator<JsonNode> aspectRatioChildNodes = aspectRatioNode.getElements();
while (aspectRatioChildNodes.hasNext()) {
aspectRatioList.add(aspectRatioChildNodes.next().getLongValue());
}
videoInfo.setAspectRatio(aspectRatioList);
}
JsonNode variantsNode = videoNode.get("variants");
if (variantsNode != null) {
List<Variant> variantList = new ArrayList<>();
Iterator<JsonNode> variantChildNodes = variantsNode.getElements();
while (variantChildNodes.hasNext()) {
Variant variant = new Variant();
JsonNode child = variantChildNodes.next();
if (child.get("bitrate") != null) {
variant.setBitrate(child.get("bitrate").getLongValue());
}
variant.setContentType(child.get("content_type").getTextValue());
variant.setUrl(child.get("url").getTextValue());
variantList.add(variant);
}
videoInfo.setVariants(variantList);
}
return videoInfo;
}
/**
* VideoInfoリストを整形してprintします
*/
public void printFormatVideoInfo() {
System.out.println("video_info");
System.out.println("|-- duration_millis : " + this.getDurationMillis());
System.out.println("|-- aspect_ratio : " + this.getAspectRatio());
System.out.println("|-- variants : [ ");
for (Variant variant : this.getVariants()) {
System.out.println(" {");
System.out.println(" |-- bitrate : " + variant.getBitrate());
System.out.println(" |-- content_type : " + variant.getContentType());
System.out.println(" |-- url : " + variant.getUrl());
System.out.println(" }");
}
System.out.println(" ]");
}
//TODO publicなgetter setter メソッドは記載省略
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment