if (self.components is None) == (self.swipeableLayoutSlides is None):
300
+
raise ValueError('did not get exactly one of components or swipeableLayoutSlides')
301
+
if self.components and not all(k in self.componentObjects for k in self.components):
302
+
raise ValueError('missing components')
303
+
if self.swipeableLayoutSlides and not all(s.mediumComponentKey in self.componentObjects and s.componentKey in self.componentObjects for s in self.swipeableLayoutSlides):
304
+
raise ValueError('missing components')
305
+
if any(c.destinationKey not in self.destinations for c in self.componentObjects.values() if hasattr(c, 'destinationKey')):
306
+
raise ValueError('missing destinations')
307
+
if any(b.destinationKey not in self.destinations for c in self.componentObjects.values() if isinstance(c, UnifiedCardButtonGroupComponentObject) for b in c.buttons):
308
+
raise ValueError('missing destinations')
309
+
mediaKeys = []
310
+
for c in self.componentObjects.values():
311
+
if isinstance(c, UnifiedCardMediumComponentObject):
if (ext := medium.get('ext')) and (mediaStats := ext['mediaStats']) and isinstance(r := mediaStats['r'], dict) and 'ok' in r and isinstance(r['ok'], dict):
561
-
mKwargs['views'] = int(r['ok']['viewCount'])
562
-
elif (mediaStats := medium.get('mediaStats')):
563
-
mKwargs['views'] = mediaStats['viewCount']
564
-
cls = Video
565
-
elif medium['type'] == 'animated_gif':
566
-
cls = Gif
567
-
media.append(cls(**mKwargs))
818
+
if (mediumO := self._make_medium(medium, kwargs['id'])):
819
+
media.append(mediumO)
568
820
if media:
569
821
kwargs['media'] = media
570
822
if retweetedTweet:
skipped 34 lines
605
857
kwargs['cashtags'] = [o['text'] for o in tweet['entities']['symbols']]
606
858
if card:
607
859
kwargs['card'] = card
608
-
# Try to convert the URL to the non-shortened/t.co one
609
-
try:
610
-
i = kwargs['tcooutlinks'].index(card.url)
611
-
except ValueError:
612
-
_logger.warning('Could not find card URL in tcooutlinks')
613
-
else:
614
-
card.url = kwargs['outlinks'][i]
860
+
if hasattr(card, 'url') and '//t.co/' in card.url and 'tcooutlinks' in kwargs:
861
+
# Try to convert the URL to the non-shortened/t.co one
862
+
try:
863
+
i = kwargs['tcooutlinks'].index(card.url)
864
+
except ValueError:
865
+
_logger.warning('Could not find card URL in tcooutlinks')
866
+
else:
867
+
card.url = kwargs['outlinks'][i]
615
868
return Tweet(**kwargs)
616
869
617
-
def _make_card(self, card, apiType):
618
-
cardKwargs = {}
619
-
for key, kwarg in [('title', 'title'), ('description', 'description'), ('card_url', 'url'), ('thumbnail_image_original', 'thumbnailUrl')]:
620
-
if apiType is _TwitterAPIType.V2:
621
-
value = card['binding_values'].get(key)
622
-
elif apiType is _TwitterAPIType.GRAPHQL:
623
-
value = next((o['value'] for o in card['legacy']['binding_values'] if o['key'] == key), None)
624
-
if not value:
870
+
def _make_medium(self, medium, tweetId):
871
+
if medium['type'] == 'photo':
872
+
if '?format=' in medium['media_url_https'] or '&format=' in medium['media_url_https']:
if (ext := medium.get('ext')) and (mediaStats := ext.get('mediaStats')) and isinstance(r := mediaStats['r'], dict) and 'ok' in r and isinstance(r['ok'], dict):
896
+
mKwargs['views'] = int(r['ok']['viewCount'])
897
+
elif (mediaStats := medium.get('mediaStats')):
898
+
mKwargs['views'] = mediaStats['viewCount']
899
+
cls = Video
900
+
elif medium['type'] == 'animated_gif':
901
+
cls = Gif
902
+
return cls(**mKwargs)
903
+
else:
904
+
_logger.warning(f'Unsupported medium type on tweet {tweetId}: {medium["type"]!r}')
905
+
906
+
def _make_card(self, card, apiType, tweetId):
907
+
bindingValues = {}
908
+
909
+
def _kwargs_from_map(keyKwargMap):
910
+
nonlocal bindingValues
911
+
return {kwarg: bindingValues[key] for key, kwarg in keyKwargMap.items() if key in bindingValues}
912
+
913
+
userRefs = {}
914
+
if apiType is _TwitterAPIType.V2:
915
+
for o in card.get('users', {}).values():
916
+
userId = o['id']
917
+
assert userId not in userRefs
918
+
userRefs[userId] = self._user_to_user(o)
919
+
elif apiType is _TwitterAPIType.GRAPHQL:
920
+
for o in card['legacy'].get('user_refs', {}):
921
+
userId = int(o['rest_id'])
922
+
if userId in userRefs:
923
+
_logger.warning(f'Duplicate user {userId} in card on tweet {tweetId}')
elif any(cardName.startswith(x) for x in ('poll2choice_', 'poll3choice_', 'poll4choice_')) and cardName.split('_', 1)[1] in ('text_only', 'image', 'video'):
_logger.warning(f'Unsupported unified_card type on tweet {tweetId}: {unifiedCardType!r}')
1100
+
return
1101
+
kwargs['type'] = unifiedCardType
1102
+
elif set(c['type'] for c in o['component_objects'].values()) != {'media', 'twitter_list_details'}:
1103
+
_logger.warning(f'Unsupported unified_card type on tweet {tweetId}')
1104
+
return
1105
+
1106
+
kwargs['componentObjects'] = {}
1107
+
for k, v in o['component_objects'].items():
1108
+
if v['type'] == 'details':
1109
+
co = UnifiedCardDetailComponentObject(content = v['data']['title']['content'], destinationKey = v['data']['destination'])
1110
+
elif v['type'] == 'media':
1111
+
co = UnifiedCardMediumComponentObject(mediumKey = v['data']['id'], destinationKey = v['data']['destination'])
1112
+
elif v['type'] == 'button_group':
1113
+
if not all(b['type'] == 'cta' for b in v['data']['buttons']):
1114
+
_logger.warning(f'Unsupported unified_card button_group button type on tweet {tweetId}')
1115
+
return
1116
+
buttons = [UnifiedCardButton(text = b['action'][0].upper() + re.sub('[A-Z]', lambda x: f' {x[0]}', b['action'][1:]), destinationKey = b['destination']) for b in v['data']['buttons']]
1117
+
co = UnifiedCardButtonGroupComponentObject(buttons = buttons)
1118
+
elif v['type'] == 'swipeable_media':
1119
+
media = [UnifiedCardSwipeableMediaMedium(mediumKey = m['id'], destinationKey = m['destination']) for m in v['data']['media_list']]
1120
+
co = UnifiedCardSwipeableMediaComponentObject(media = media)
1121
+
elif v['type'] == 'app_store_details':
1122
+
co = UnifiedCardAppStoreComponentObject(appKey = v['data']['app_id'], destinationKey = v['data']['destination'])
1123
+
elif v['type'] == 'twitter_list_details':
1124
+
co = UnifiedCardTwitterListDetailsComponentObject(
1125
+
name = v['data']['name']['content'],
1126
+
memberCount = v['data']['member_count'],
1127
+
subscriberCount = v['data']['subscriber_count'],
1128
+
user = self._user_to_user(o['users'][v['data']['user_id']]),
1129
+
destinationKey = v['data']['destination'],
1130
+
)
1131
+
else:
1132
+
_logger.warning(f'Unsupported unified_card component type on tweet {tweetId}: {v["type"]!r}')
vKwargs['url'] = f'https://play.google.com/store/apps/details?id={var["id"]}' if var['type'] == 'android_app' else f'https://itunes.apple.com/app/id{var["id"]}'
1178
+
variants.append(UnifiedCardApp(**vKwargs))
1179
+
kwargs['apps'][k] = variants
1180
+
1181
+
if o['components']:
1182
+
kwargs['components'] = o['components']
1183
+
1184
+
if 'layout' in o:
1185
+
if o['layout']['type'] != 'swipeable':
1186
+
_logger.warning(f'Unsupported unified_card layout type on tweet {tweetId}: {o["layout"]["type"]!r}')
1187
+
return
1188
+
kwargs['swipeableLayoutSlides'] = [UnifiedCardSwipeableLayoutSlide(mediumComponentKey = v[0], componentKey = v[1]) for v in o['layout']['data']['slides']]
1189
+
1190
+
return UnifiedCard(**kwargs)
1191
+
1192
+
_logger.warning(f'Unsupported card type on tweet {tweetId}: {cardName!r}')
633
1193
634
1194
def _tweet_to_tweet(self, tweet, obj):
635
1195
user = self._user_to_user(obj['globalObjects']['users'][tweet['user_id_str']])
skipped 3 lines
639
1199
if 'quoted_status_id_str' in tweet and tweet['quoted_status_id_str'] in obj['globalObjects']['tweets']: