From bf4bc3a66869a7575c2f957cd848b379794bf94e Mon Sep 17 00:00:00 2001 From: Aaron Parecki Date: Mon, 19 Feb 2018 07:31:14 -0800 Subject: [PATCH] extract photos and videos from streaming tweets when truncated --- lib/XRay/Formats/Twitter.php | 60 ++- tests/TwitterTest.php | 22 +- .../streaming-tweet-truncated-with-photo.json | 434 ++++++++++++++++++ .../streaming-tweet-truncated-with-video.json | 290 ++++++++++++ 4 files changed, 784 insertions(+), 22 deletions(-) create mode 100644 tests/data/api.twitter.com/streaming-tweet-truncated-with-photo.json create mode 100644 tests/data/api.twitter.com/streaming-tweet-truncated-with-video.json diff --git a/lib/XRay/Formats/Twitter.php b/lib/XRay/Formats/Twitter.php index c2e8e44..1e10a29 100644 --- a/lib/XRay/Formats/Twitter.php +++ b/lib/XRay/Formats/Twitter.php @@ -114,27 +114,15 @@ class Twitter extends Format { // Photos and Videos if(property_exists($tweet, 'extended_entities') && property_exists($tweet->extended_entities, 'media')) { foreach($tweet->extended_entities->media as $media) { - if($media->type == 'photo') { - if(!array_key_exists('photo', $entry)) - $entry['photo'] = []; - - $entry['photo'][] = $media->media_url_https; - - } elseif($media->type == 'video') { - if(!array_key_exists('video', $entry)) - $entry['video'] = []; - - // Find the highest bitrate video that is mp4 - $videos = $media->video_info->variants; - $videos = array_filter($videos, function($v) { - return property_exists($v, 'bitrate') && $v->content_type == 'video/mp4'; - }); - if(count($videos)) { - usort($videos, function($a,$b) { - return $a->bitrate < $b->bitrate; - }); - $entry['video'][] = $videos[0]->url; - } + self::extractMedia($media, $entry); + } + } + + // Photos from Streaming API Tweets + if(property_exists($tweet, 'extended_tweet')) { + if(property_exists($tweet->extended_tweet, 'entities') && property_exists($tweet->extended_tweet->entities, 'media')) { + foreach($tweet->extended_tweet->entities->media as $media) { + self::extractMedia($media, $entry); } } } @@ -181,6 +169,31 @@ class Twitter extends Format { ]; } + private static function extractMedia($media, &$entry) { + if($media->type == 'photo') { + if(!array_key_exists('photo', $entry)) + $entry['photo'] = []; + + $entry['photo'][] = $media->media_url_https; + + } elseif($media->type == 'video') { + if(!array_key_exists('video', $entry)) + $entry['video'] = []; + + // Find the highest bitrate video that is mp4 + $videos = $media->video_info->variants; + $videos = array_filter($videos, function($v) { + return property_exists($v, 'bitrate') && $v->content_type == 'video/mp4'; + }); + if(count($videos)) { + usort($videos, function($a,$b) { + return $a->bitrate < $b->bitrate; + }); + $entry['video'][] = $videos[0]->url; + } + } + } + private static function _buildHCardFromTwitterProfile($profile) { if(!$profile) return false; @@ -223,6 +236,11 @@ class Twitter extends Format { if(property_exists($tweet, 'extended_tweet')) { $text = $tweet->extended_tweet->full_text; + $text = mb_substr($text, + $tweet->extended_tweet->display_text_range[0], + $tweet->extended_tweet->display_text_range[1]-$tweet->extended_tweet->display_text_range[0], + 'UTF-8'); + if(property_exists($tweet->extended_tweet, 'entities')) { $entities = $tweet->extended_tweet->entities; } diff --git a/tests/TwitterTest.php b/tests/TwitterTest.php index ba855df..0a4b6c6 100644 --- a/tests/TwitterTest.php +++ b/tests/TwitterTest.php @@ -208,7 +208,27 @@ class TwitterTest extends PHPUnit_Framework_TestCase { $data = $this->parse(['url' => $url, 'body' => $json]); $this->assertEquals("#indieweb community. Really would like to see a Micropub client for Gratitude logging and also a Mastodon poster similar to the twitter one.\nFeel like I could (maybe) rewrite previous open code to do some of this :)", $data['data']['content']['text']); - $this->assertArrayNotHasKey('html', $data['data']['content']); + $this->assertEquals('#indieweb community. Really would like to see a Micropub client for Gratitude logging and also a Mastodon poster similar to the twitter one.
+Feel like I could (maybe) rewrite previous open code to do some of this :)', $data['data']['content']['html']); + } + + public function testStreamingTweetTruncatedWithPhoto() { + list($url, $json) = $this->loadTweet('streaming-tweet-truncated-with-photo'); + $data = $this->parse(['url' => $url, 'body' => $json]); + + $this->assertEquals("#MicrosoftFlow ninja-tip.\nI'm getting better at custom-connector and auth. Thanks @skillriver \nThis is OAuth2 with MSA/Live (not AzureAD) which I need to do MVP timesheets.\nStill dislike Swagger so I don't know why I bother with this. I'm just that lazy doing this manually", $data['data']['content']['text']); + $this->assertEquals(4, count($data['data']['photo'])); + $this->assertEquals('https://pbs.twimg.com/media/DWZ-5UPVAAAQOWY.jpg', $data['data']['photo'][0]); + $this->assertEquals('https://pbs.twimg.com/media/DWaAhZ2UQAAIEoS.jpg', $data['data']['photo'][3]); + } + + public function testStreamingTweetTruncatedWithVidoe() { + list($url, $json) = $this->loadTweet('streaming-tweet-truncated-with-video'); + $data = $this->parse(['url' => $url, 'body' => $json]); + + $this->assertEquals("hi @aaronpk Ends was a great job I was just talking to her about the house I think she is just talking to you about that stuff like that you don't have any idea of how to make to your job so you don't want me going back on your own to make it happen", $data['data']['content']['text']); + $this->assertEquals(1, count($data['data']['video'])); + $this->assertEquals('https://video.twimg.com/ext_tw_video/965608338917548032/pu/vid/720x720/kreAfCMf-B1dLqBH.mp4', $data['data']['video'][0]); } } diff --git a/tests/data/api.twitter.com/streaming-tweet-truncated-with-photo.json b/tests/data/api.twitter.com/streaming-tweet-truncated-with-photo.json new file mode 100644 index 0000000..aed80c4 --- /dev/null +++ b/tests/data/api.twitter.com/streaming-tweet-truncated-with-photo.json @@ -0,0 +1,434 @@ +{ + "created_at": "Mon Feb 19 15:03:23 +0000 2018", + "id": 965602723251945473, + "id_str": "965602723251945473", + "text": "#MicrosoftFlow ninja-tip.\nI'm getting better at custom-connector and auth. Thanks @skillriver \nThis is OAuth2 with\u2026 https://t.co/pL951ynEfd", + "display_text_range": [ + 0, + 140 + ], + "source": "Twitter Web Client", + "truncated": true, + "in_reply_to_status_id": null, + "in_reply_to_status_id_str": null, + "in_reply_to_user_id": null, + "in_reply_to_user_id_str": null, + "in_reply_to_screen_name": null, + "user": { + "id": 17532000, + "id_str": "17532000", + "name": "John LIU", + "screen_name": "johnnliu", + "location": "Sydney", + "url": "http://johnliu.net", + "description": "coder \u2022 blogger \u2022 MVP Office365 & SharePoint \u2022 #MSFlow \n[work sharepointgurus..net]", + "translator_type": "none", + "protected": false, + "verified": false, + "followers_count": 1361, + "friends_count": 421, + "listed_count": 142, + "favourites_count": 8661, + "statuses_count": 13745, + "created_at": "Fri Nov 21 05:46:26 +0000 2008", + "utc_offset": 39600, + "time_zone": "Sydney", + "geo_enabled": true, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "profile_background_color": "C0DEED", + "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", + "profile_background_tile": false, + "profile_link_color": "1DA1F2", + "profile_sidebar_border_color": "C0DEED", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "profile_image_url": "http://pbs.twimg.com/profile_images/961904825716715520/NhPxyw2N_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/961904825716715520/NhPxyw2N_normal.jpg", + "profile_banner_url": "https://pbs.twimg.com/profile_banners/17532000/1432696432", + "default_profile": true, + "default_profile_image": false, + "following": null, + "follow_request_sent": null, + "notifications": null + }, + "geo": null, + "coordinates": null, + "place": null, + "contributors": null, + "is_quote_status": false, + "extended_tweet": { + "full_text": "#MicrosoftFlow ninja-tip.\nI'm getting better at custom-connector and auth. Thanks @skillriver \nThis is OAuth2 with MSA/Live (not AzureAD) which I need to do MVP timesheets.\nStill dislike Swagger so I don't know why I bother with this. I'm just that lazy doing this manually https://t.co/dUDaqcQssO", + "display_text_range": [ + 0, + 274 + ], + "entities": { + "hashtags": [ + { + "text": "MicrosoftFlow", + "indices": [ + 0, + 14 + ] + } + ], + "urls": [], + "user_mentions": [ + { + "screen_name": "skillriver", + "name": "Jan Vidar Elven", + "id": 550859390, + "id_str": "550859390", + "indices": [ + 83, + 94 + ] + } + ], + "symbols": [], + "media": [ + { + "id": 965598693268193280, + "id_str": "965598693268193280", + "indices": [ + 275, + 298 + ], + "media_url": "http://pbs.twimg.com/media/DWZ-5UPVAAAQOWY.jpg", + "media_url_https": "https://pbs.twimg.com/media/DWZ-5UPVAAAQOWY.jpg", + "url": "https://t.co/dUDaqcQssO", + "display_url": "pic.twitter.com/dUDaqcQssO", + "expanded_url": "https://twitter.com/johnnliu/status/965602723251945473/photo/1", + "type": "photo", + "sizes": { + "small": { + "w": 638, + "h": 680, + "resize": "fit" + }, + "thumb": { + "w": 150, + "h": 150, + "resize": "crop" + }, + "medium": { + "w": 754, + "h": 804, + "resize": "fit" + }, + "large": { + "w": 754, + "h": 804, + "resize": "fit" + } + } + }, + { + "id": 965600129775411204, + "id_str": "965600129775411204", + "indices": [ + 275, + 298 + ], + "media_url": "http://pbs.twimg.com/media/DWaAM7pVoAQuGhL.jpg", + "media_url_https": "https://pbs.twimg.com/media/DWaAM7pVoAQuGhL.jpg", + "url": "https://t.co/dUDaqcQssO", + "display_url": "pic.twitter.com/dUDaqcQssO", + "expanded_url": "https://twitter.com/johnnliu/status/965602723251945473/photo/1", + "type": "photo", + "sizes": { + "thumb": { + "w": 150, + "h": 150, + "resize": "crop" + }, + "medium": { + "w": 562, + "h": 904, + "resize": "fit" + }, + "large": { + "w": 562, + "h": 904, + "resize": "fit" + }, + "small": { + "w": 423, + "h": 680, + "resize": "fit" + } + } + }, + { + "id": 965600383589523456, + "id_str": "965600383589523456", + "indices": [ + 275, + 298 + ], + "media_url": "http://pbs.twimg.com/media/DWaAbtLVoAA2O68.jpg", + "media_url_https": "https://pbs.twimg.com/media/DWaAbtLVoAA2O68.jpg", + "url": "https://t.co/dUDaqcQssO", + "display_url": "pic.twitter.com/dUDaqcQssO", + "expanded_url": "https://twitter.com/johnnliu/status/965602723251945473/photo/1", + "type": "photo", + "sizes": { + "large": { + "w": 908, + "h": 1017, + "resize": "fit" + }, + "thumb": { + "w": 150, + "h": 150, + "resize": "crop" + }, + "medium": { + "w": 908, + "h": 1017, + "resize": "fit" + }, + "small": { + "w": 607, + "h": 680, + "resize": "fit" + } + } + }, + { + "id": 965600481480294400, + "id_str": "965600481480294400", + "indices": [ + 275, + 298 + ], + "media_url": "http://pbs.twimg.com/media/DWaAhZ2UQAAIEoS.jpg", + "media_url_https": "https://pbs.twimg.com/media/DWaAhZ2UQAAIEoS.jpg", + "url": "https://t.co/dUDaqcQssO", + "display_url": "pic.twitter.com/dUDaqcQssO", + "expanded_url": "https://twitter.com/johnnliu/status/965602723251945473/photo/1", + "type": "photo", + "sizes": { + "large": { + "w": 813, + "h": 1031, + "resize": "fit" + }, + "thumb": { + "w": 150, + "h": 150, + "resize": "crop" + }, + "small": { + "w": 536, + "h": 680, + "resize": "fit" + }, + "medium": { + "w": 813, + "h": 1031, + "resize": "fit" + } + } + } + ] +}, +"extended_entities": { + "media": [ + { + "id": 965598693268193280, + "id_str": "965598693268193280", + "indices": [ + 275, + 298 + ], + "media_url": "http://pbs.twimg.com/media/DWZ-5UPVAAAQOWY.jpg", + "media_url_https": "https://pbs.twimg.com/media/DWZ-5UPVAAAQOWY.jpg", + "url": "https://t.co/dUDaqcQssO", + "display_url": "pic.twitter.com/dUDaqcQssO", + "expanded_url": "https://twitter.com/johnnliu/status/965602723251945473/photo/1", + "type": "photo", + "sizes": { + "small": { + "w": 638, + "h": 680, + "resize": "fit" + }, + "thumb": { + "w": 150, + "h": 150, + "resize": "crop" + }, + "medium": { + "w": 754, + "h": 804, + "resize": "fit" + }, + "large": { + "w": 754, + "h": 804, + "resize": "fit" + } + } + }, + { + "id": 965600129775411204, + "id_str": "965600129775411204", + "indices": [ + 275, + 298 + ], + "media_url": "http://pbs.twimg.com/media/DWaAM7pVoAQuGhL.jpg", + "media_url_https": "https://pbs.twimg.com/media/DWaAM7pVoAQuGhL.jpg", + "url": "https://t.co/dUDaqcQssO", + "display_url": "pic.twitter.com/dUDaqcQssO", + "expanded_url": "https://twitter.com/johnnliu/status/965602723251945473/photo/1", + "type": "photo", + "sizes": { + "thumb": { + "w": 150, + "h": 150, + "resize": "crop" + }, + "medium": { + "w": 562, + "h": 904, + "resize": "fit" + }, + "large": { + "w": 562, + "h": 904, + "resize": "fit" + }, + "small": { + "w": 423, + "h": 680, + "resize": "fit" + } + } + }, + { + "id": 965600383589523456, + "id_str": "965600383589523456", + "indices": [ + 275, + 298 + ], + "media_url": "http://pbs.twimg.com/media/DWaAbtLVoAA2O68.jpg", + "media_url_https": "https://pbs.twimg.com/media/DWaAbtLVoAA2O68.jpg", + "url": "https://t.co/dUDaqcQssO", + "display_url": "pic.twitter.com/dUDaqcQssO", + "expanded_url": "https://twitter.com/johnnliu/status/965602723251945473/photo/1", + "type": "photo", + "sizes": { + "large": { + "w": 908, + "h": 1017, + "resize": "fit" + }, + "thumb": { + "w": 150, + "h": 150, + "resize": "crop" + }, + "medium": { + "w": 908, + "h": 1017, + "resize": "fit" + }, + "small": { + "w": 607, + "h": 680, + "resize": "fit" + } + } + }, + { + "id": 965600481480294400, + "id_str": "965600481480294400", + "indices": [ + 275, + 298 + ], + "media_url": "http://pbs.twimg.com/media/DWaAhZ2UQAAIEoS.jpg", + "media_url_https": "https://pbs.twimg.com/media/DWaAhZ2UQAAIEoS.jpg", + "url": "https://t.co/dUDaqcQssO", + "display_url": "pic.twitter.com/dUDaqcQssO", + "expanded_url": "https://twitter.com/johnnliu/status/965602723251945473/photo/1", + "type": "photo", + "sizes": { + "large": { + "w": 813, + "h": 1031, + "resize": "fit" + }, + "thumb": { + "w": 150, + "h": 150, + "resize": "crop" + }, + "small": { + "w": 536, + "h": 680, + "resize": "fit" + }, + "medium": { + "w": 813, + "h": 1031, + "resize": "fit" + } + } + } + ] +} +}, +"quote_count": 0, +"reply_count": 0, +"retweet_count": 0, +"favorite_count": 0, +"entities": { + "hashtags": [ + { + "text": "MicrosoftFlow", + "indices": [ + 0, + 14 + ] + } + ], + "urls": [ + { + "url": "https://t.co/pL951ynEfd", + "expanded_url": "https://twitter.com/i/web/status/965602723251945473", + "display_url": "twitter.com/i/web/status/9\u2026", + "indices": [ + 117, + 140 + ] + } + ], + "user_mentions": [ + { + "screen_name": "skillriver", + "name": "Jan Vidar Elven", + "id": 550859390, + "id_str": "550859390", + "indices": [ + 83, + 94 + ] + } + ], + "symbols": [] +}, +"favorited": false, +"retweeted": false, +"possibly_sensitive": false, +"filter_level": "low", +"lang": "en", +"timestamp_ms": "1519052603911" +} diff --git a/tests/data/api.twitter.com/streaming-tweet-truncated-with-video.json b/tests/data/api.twitter.com/streaming-tweet-truncated-with-video.json new file mode 100644 index 0000000..7a03248 --- /dev/null +++ b/tests/data/api.twitter.com/streaming-tweet-truncated-with-video.json @@ -0,0 +1,290 @@ + +{ + "created_at": "Mon Feb 19 15:25:48 +0000 2018", + "id": 965608361797419010, + "id_str": "965608361797419010", + "text": "hi @aaronpk Ends was a great job I was just talking to her about the house I think she is just talking to you about\u2026 https://t.co/ggjjk7ZZZY", + "display_text_range": [ + 0, + 140 + ], + "source": "Twitter for iPhone", + "truncated": true, + "in_reply_to_status_id": null, + "in_reply_to_status_id_str": null, + "in_reply_to_user_id": null, + "in_reply_to_user_id_str": null, + "in_reply_to_screen_name": null, + "user": { + "id": 143883456, + "id_str": "143883456", + "name": "aaronpk dev", + "screen_name": "pkdev", + "location": "Portland, OR", + "url": "http://aaronpk.micro.blog/", + "description": "Dev account for testing Twitter things. Follow me here: https://twitter.com/aaronpk", + "translator_type": "none", + "protected": false, + "verified": false, + "followers_count": 1, + "friends_count": 1, + "listed_count": 0, + "favourites_count": 2, + "statuses_count": 58, + "created_at": "Fri May 14 17:47:15 +0000 2010", + "utc_offset": -28800, + "time_zone": "Pacific Time (US & Canada)", + "geo_enabled": true, + "lang": "en", + "contributors_enabled": false, + "is_translator": false, + "profile_background_color": "C0DEED", + "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", + "profile_background_tile": false, + "profile_link_color": "1DA1F2", + "profile_sidebar_border_color": "C0DEED", + "profile_sidebar_fill_color": "DDEEF6", + "profile_text_color": "333333", + "profile_use_background_image": true, + "profile_image_url": "http://pbs.twimg.com/profile_images/638125135904436224/qd_d94Qn_normal.jpg", + "profile_image_url_https": "https://pbs.twimg.com/profile_images/638125135904436224/qd_d94Qn_normal.jpg", + "default_profile": true, + "default_profile_image": false, + "following": null, + "follow_request_sent": null, + "notifications": null + }, + "geo": null, + "coordinates": null, + "place": { + "id": "ac88a4f17a51c7fc", + "url": "https://api.twitter.com/1.1/geo/id/ac88a4f17a51c7fc.json", + "place_type": "city", + "name": "Portland", + "full_name": "Portland, OR", + "country_code": "US", + "country": "United States", + "bounding_box": { + "type": "Polygon", + "coordinates": [ + [ + [ + -122.790065, + 45.421863 + ], + [ + -122.790065, + 45.650941 + ], + [ + -122.471751, + 45.650941 + ], + [ + -122.471751, + 45.421863 + ] + ] + ] + }, + "attributes": {} + }, + "contributors": null, + "is_quote_status": false, + "extended_tweet": { + "full_text": "hi @aaronpk Ends was a great job I was just talking to her about the house I think she is just talking to you about that stuff like that you don't have any idea of how to make to your job so you don't want me going back on your own to make it happen https://t.co/9ko0nnAG7K", + "display_text_range": [ + 0, + 249 + ], + "entities": { + "hashtags": [], + "urls": [], + "user_mentions": [ + { + "screen_name": "aaronpk", + "name": "Aaron Parecki", + "id": 14447132, + "id_str": "14447132", + "indices": [ + 3, + 11 + ] + } + ], + "symbols": [], + "media": [ + { + "id": 965608338917548032, + "id_str": "965608338917548032", + "indices": [ + 250, + 273 + ], + "media_url": "http://pbs.twimg.com/ext_tw_video_thumb/965608338917548032/pu/img/TXwZ-AA8tSYTaZYS.jpg", + "media_url_https": "https://pbs.twimg.com/ext_tw_video_thumb/965608338917548032/pu/img/TXwZ-AA8tSYTaZYS.jpg", + "url": "https://t.co/9ko0nnAG7K", + "display_url": "pic.twitter.com/9ko0nnAG7K", + "expanded_url": "https://twitter.com/pkdev/status/965608361797419010/video/1", + "type": "video", + "sizes": { + "thumb": { + "w": 150, + "h": 150, + "resize": "crop" + }, + "small": { + "w": 680, + "h": 680, + "resize": "fit" + }, + "medium": { + "w": 720, + "h": 720, + "resize": "fit" + }, + "large": { + "w": 720, + "h": 720, + "resize": "fit" + } + }, + "video_info": { + "aspect_ratio": [ + 1, + 1 + ], + "duration_millis": 3995, + "variants": [ + { + "content_type": "application/x-mpegURL", + "url": "https://video.twimg.com/ext_tw_video/965608338917548032/pu/pl/KAdZKmD5lymHTt7A.m3u8" + }, + { + "bitrate": 1280000, + "content_type": "video/mp4", + "url": "https://video.twimg.com/ext_tw_video/965608338917548032/pu/vid/720x720/kreAfCMf-B1dLqBH.mp4" + }, + { + "bitrate": 256000, + "content_type": "video/mp4", + "url": "https://video.twimg.com/ext_tw_video/965608338917548032/pu/vid/240x240/5ZfV0xsJ4NmRKkcM.mp4" + }, + { + "bitrate": 832000, + "content_type": "video/mp4", + "url": "https://video.twimg.com/ext_tw_video/965608338917548032/pu/vid/480x480/EYfnabEG0Uno0Azc.mp4" + } + ] + } + } + ] + }, + "extended_entities": { + "media": [ + { + "id": 965608338917548032, + "id_str": "965608338917548032", + "indices": [ + 250, + 273 + ], + "media_url": "http://pbs.twimg.com/ext_tw_video_thumb/965608338917548032/pu/img/TXwZ-AA8tSYTaZYS.jpg", + "media_url_https": "https://pbs.twimg.com/ext_tw_video_thumb/965608338917548032/pu/img/TXwZ-AA8tSYTaZYS.jpg", + "url": "https://t.co/9ko0nnAG7K", + "display_url": "pic.twitter.com/9ko0nnAG7K", + "expanded_url": "https://twitter.com/pkdev/status/965608361797419010/video/1", + "type": "video", + "sizes": { + "thumb": { + "w": 150, + "h": 150, + "resize": "crop" + }, + "small": { + "w": 680, + "h": 680, + "resize": "fit" + }, + "medium": { + "w": 720, + "h": 720, + "resize": "fit" + }, + "large": { + "w": 720, + "h": 720, + "resize": "fit" + } + }, + "video_info": { + "aspect_ratio": [ + 1, + 1 + ], + "duration_millis": 3995, + "variants": [ + { + "content_type": "application/x-mpegURL", + "url": "https://video.twimg.com/ext_tw_video/965608338917548032/pu/pl/KAdZKmD5lymHTt7A.m3u8" + }, + { + "bitrate": 1280000, + "content_type": "video/mp4", + "url": "https://video.twimg.com/ext_tw_video/965608338917548032/pu/vid/720x720/kreAfCMf-B1dLqBH.mp4" + }, + { + "bitrate": 256000, + "content_type": "video/mp4", + "url": "https://video.twimg.com/ext_tw_video/965608338917548032/pu/vid/240x240/5ZfV0xsJ4NmRKkcM.mp4" + }, + { + "bitrate": 832000, + "content_type": "video/mp4", + "url": "https://video.twimg.com/ext_tw_video/965608338917548032/pu/vid/480x480/EYfnabEG0Uno0Azc.mp4" + } + ] + } + } + ] + } + }, + "quote_count": 0, + "reply_count": 0, + "retweet_count": 0, + "favorite_count": 0, + "entities": { + "hashtags": [], + "urls": [ + { + "url": "https://t.co/ggjjk7ZZZY", + "expanded_url": "https://twitter.com/i/web/status/965608361797419010", + "display_url": "twitter.com/i/web/status/9\u2026", + "indices": [ + 117, + 140 + ] + } + ], + "user_mentions": [ + { + "screen_name": "aaronpk", + "name": "Aaron Parecki", + "id": 14447132, + "id_str": "14447132", + "indices": [ + 3, + 11 + ] + } + ], + "symbols": [] + }, + "favorited": false, + "retweeted": false, + "possibly_sensitive": false, + "filter_level": "low", + "lang": "en", + "timestamp_ms": "1519053948245" +}