From a181cd0c600409a11f3f5e8448eaef4c63fdf1ed Mon Sep 17 00:00:00 2001
From: pukkandan <pukkandan.ytdlp@gmail.com>
Date: Mon, 23 Aug 2021 21:51:42 +0530
Subject: [PATCH] [facebook] Fix metadata extraction Original PR:
 https://github.com/ytdl-org/youtube-dl/pull/29796 Closes #453,
 https://github.com/ytdl-org/youtube-dl/issues/29421,
 https://github.com/ytdl-org/youtube-dl/issues/23627,
 https://github.com/ytdl-org/youtube-dl/issues/23180,
 https://github.com/ytdl-org/youtube-dl/issues/14156

Authored by: kikuyan
---
 yt_dlp/extractor/facebook.py | 122 ++++++++++++++++++++++++-----------
 1 file changed, 84 insertions(+), 38 deletions(-)

diff --git a/yt_dlp/extractor/facebook.py b/yt_dlp/extractor/facebook.py
index d0295c881..e5bdb335a 100644
--- a/yt_dlp/extractor/facebook.py
+++ b/yt_dlp/extractor/facebook.py
@@ -20,6 +20,7 @@
     int_or_none,
     js_to_json,
     limit_length,
+    merge_dicts,
     network_exceptions,
     parse_count,
     qualities,
@@ -80,7 +81,8 @@ class FacebookIE(InfoExtractor):
         'info_dict': {
             'id': '274175099429670',
             'ext': 'mp4',
-            'title': 're:^Asif Nawab Butt posted a video',
+            'title': 'Asif Nawab Butt',
+            'description': 'Asif Nawab Butt',
             'uploader': 'Asif Nawab Butt',
             'upload_date': '20140506',
             'timestamp': 1399398998,
@@ -135,15 +137,17 @@ class FacebookIE(InfoExtractor):
             'upload_date': '20160223',
             'uploader': 'Barack Obama',
         },
+        'skip': 'Gif on giphy.com gone',
     }, {
         # have 1080P, but only up to 720p in swf params
         # data.video.story.attachments[].media
         'url': 'https://www.facebook.com/cnn/videos/10155529876156509/',
-        'md5': '9571fae53d4165bbbadb17a94651dcdc',
+        'md5': '3f3798adb2b73423263e59376f1f5eb7',
         'info_dict': {
             'id': '10155529876156509',
             'ext': 'mp4',
-            'title': 'She survived the holocaust — and years later, she’s getting her citizenship s...',
+            'title': 'Holocaust survivor becomes US citizen',
+            'description': 'She survived the holocaust — and years later, she’s getting her citizenship so she can vote for Hillary Clinton http://cnn.it/2eERh5f',
             'timestamp': 1477818095,
             'upload_date': '20161030',
             'uploader': 'CNN',
@@ -157,15 +161,18 @@ class FacebookIE(InfoExtractor):
         'info_dict': {
             'id': '1417995061575415',
             'ext': 'mp4',
-            'title': 'md5:1db063d6a8c13faa8da727817339c857',
-            'timestamp': 1486648217,
+            'title': 'Yaroslav Korpan - Довгоочікуване відео',
+            'description': 'Довгоочікуване відео',
+            'timestamp': 1486648771,
             'upload_date': '20170209',
             'uploader': 'Yaroslav Korpan',
+            'uploader_id': '100000948048708',
         },
         'params': {
             'skip_download': True,
         },
     }, {
+        # FIXME
         'url': 'https://www.facebook.com/LaGuiaDelVaron/posts/1072691702860471',
         'info_dict': {
             'id': '1072691702860471',
@@ -183,12 +190,14 @@ class FacebookIE(InfoExtractor):
         # data.node.comet_sections.content.story.attachments[].style_type_renderer.attachment.media
         'url': 'https://www.facebook.com/groups/1024490957622648/permalink/1396382447100162/',
         'info_dict': {
-            'id': '1396382447100162',
+            'id': '202882990186699',
             'ext': 'mp4',
-            'title': 'md5:19a428bbde91364e3de815383b54a235',
-            'timestamp': 1486035494,
+            'title': 'Elisabeth Ahtn - Hello? Yes your uber ride is here\n* Jukin...',
+            'description': 'Hello? Yes your uber ride is here\n* Jukin Media Verified *\nFind this video and others like it by visiting...',
+            'timestamp': 1486035513,
             'upload_date': '20170202',
             'uploader': 'Elisabeth Ahtn',
+            'uploader_id': '100013949973717',
         },
         'params': {
             'skip_download': True,
@@ -229,8 +238,12 @@ class FacebookIE(InfoExtractor):
         'info_dict': {
             'id': '359649331226507',
             'ext': 'mp4',
-            'title': '#ESLOne VoD - Birmingham Finals Day#1 Fnatic vs. @Evil Geniuses',
+            'title': 'Fnatic vs. EG - Group A - Opening Match - ESL One Birmingham Day 1',
+            'description': '#ESLOne VoD - Birmingham Finals Day#1 Fnatic vs. @Evil Geniuses',
+            'timestamp': 1527084179,
+            'upload_date': '20180523',
             'uploader': 'ESL One Dota 2',
+            'uploader_id': '234218833769558',
         },
         'params': {
             'skip_download': True,
@@ -247,6 +260,7 @@ class FacebookIE(InfoExtractor):
         'url': 'https://www.facebook.com/watch/?v=647537299265662',
         'only_matching': True,
     }, {
+        # FIXME: https://github.com/yt-dlp/yt-dlp/issues/542
         # data.node.comet_sections.content.story.attachments[].style_type_renderer.attachment.all_subattachments.nodes[].media
         'url': 'https://www.facebook.com/PankajShahLondon/posts/10157667649866271',
         'info_dict': {
@@ -277,6 +291,7 @@ class FacebookIE(InfoExtractor):
             'upload_date': '20161122',
             'timestamp': 1479793574,
         },
+        'skip': 'No video',
     }, {
         # data.video.creation_story.attachments[].media
         'url': 'https://www.facebook.com/watch/live/?v=1823658634322275',
@@ -379,6 +394,56 @@ def _extract_from_url(self, url, video_id):
         webpage = self._download_webpage(
             url.replace('://m.facebook.com/', '://www.facebook.com/'), video_id)
 
+        def extract_metadata(webpage):
+            video_title = self._html_search_regex(
+                r'<h2\s+[^>]*class="uiHeaderTitle"[^>]*>([^<]*)</h2>', webpage,
+                'title', default=None)
+            if not video_title:
+                video_title = self._html_search_regex(
+                    r'(?s)<span class="fbPhotosPhotoCaption".*?id="fbPhotoPageCaption"><span class="hasCaption">(.*?)</span>',
+                    webpage, 'alternative title', default=None)
+            if not video_title:
+                video_title = self._html_search_meta(
+                    ['og:title', 'twitter:title', 'description'],
+                    webpage, 'title', default=None)
+            if video_title:
+                video_title = limit_length(video_title, 80)
+            else:
+                video_title = 'Facebook video #%s' % video_id
+            description = self._html_search_meta(
+                ['description', 'og:description', 'twitter:description'],
+                webpage, 'description', default=None)
+            uploader = clean_html(get_element_by_id(
+                'fbPhotoPageAuthorName', webpage)) or self._search_regex(
+                r'ownerName\s*:\s*"([^"]+)"', webpage, 'uploader',
+                default=None) or self._og_search_title(webpage, fatal=False)
+            timestamp = int_or_none(self._search_regex(
+                r'<abbr[^>]+data-utime=["\'](\d+)', webpage,
+                'timestamp', default=None))
+            thumbnail = self._html_search_meta(
+                ['og:image', 'twitter:image'], webpage, 'thumbnail', default=None)
+            # some webpages contain unretrievable thumbnail urls
+            # like https://lookaside.fbsbx.com/lookaside/crawler/media/?media_id=10155168902769113&get_thumbnail=1
+            # in https://www.facebook.com/yaroslav.korpan/videos/1417995061575415/
+            if thumbnail and not re.search(r'\.(?:jpg|png)', thumbnail):
+                thumbnail = None
+            view_count = parse_count(self._search_regex(
+                r'\bviewCount\s*:\s*["\']([\d,.]+)', webpage, 'view count',
+                default=None))
+            info_dict = {
+                'title': video_title,
+                'description': description,
+                'uploader': uploader,
+                'timestamp': timestamp,
+                'thumbnail': thumbnail,
+                'view_count': view_count,
+            }
+            info_json_ld = self._search_json_ld(webpage, video_id, default={})
+            if info_json_ld.get('title'):
+                info_json_ld['title'] = limit_length(
+                    re.sub(r'\s*\|\s*Facebook$', '', info_json_ld['title']), 80)
+            return merge_dicts(info_json_ld, info_dict)
+
         video_data = None
 
         def extract_video_data(instances):
@@ -511,7 +576,15 @@ def parse_attachment(attachment, key='media'):
                     if not entries:
                         parse_graphql_video(video)
 
-                return self.playlist_result(entries, video_id)
+                if len(entries) > 1:
+                    return self.playlist_result(entries, video_id)
+
+                video_info = entries[0]
+                webpage_info = extract_metadata(webpage)
+                # honor precise duration in video info
+                if video_info.get('duration'):
+                    webpage_info['duration'] = video_info['duration']
+                return merge_dicts(webpage_info, video_info)
 
         if not video_data:
             m_msg = re.search(r'class="[^"]*uiInterstitialContent[^"]*"><div>(.*?)</div>', webpage)
@@ -629,39 +702,12 @@ def parse_attachment(attachment, key='media'):
 
         process_formats(formats)
 
-        description = self._html_search_meta('description', webpage, default=None)
-        video_title = self._html_search_regex(
-            (r'<h2\s+[^>]*class="uiHeaderTitle"[^>]*>([^<]*)</h2>',
-             r'(?s)<span class="fbPhotosPhotoCaption".*?id="fbPhotoPageCaption"><span class="hasCaption">(.*?)</span>'),
-            webpage, 'title', default=None) or self._og_search_title(webpage, default=None) or description
-        if video_title:
-            video_title = limit_length(video_title, 80)
-        else:
-            video_title = 'Facebook video #%s' % video_id
-        uploader = clean_html(get_element_by_id(
-            'fbPhotoPageAuthorName', webpage)) or self._search_regex(
-            r'ownerName\s*:\s*"([^"]+)"', webpage, 'uploader',
-            default=None) or self._og_search_title(webpage, fatal=False)
-        timestamp = int_or_none(self._search_regex(
-            r'<abbr[^>]+data-utime=["\'](\d+)', webpage,
-            'timestamp', default=None))
-        thumbnail = self._html_search_meta(['og:image', 'twitter:image'], webpage)
-
-        view_count = parse_count(self._search_regex(
-            r'\bviewCount\s*:\s*["\']([\d,.]+)', webpage, 'view count',
-            default=None))
-
         info_dict = {
             'id': video_id,
-            'title': video_title,
             'formats': formats,
-            'uploader': uploader,
-            'timestamp': timestamp,
-            'description': description,
-            'thumbnail': thumbnail,
-            'view_count': view_count,
             'subtitles': subtitles,
         }
+        info_dict.update(extract_metadata(webpage))
 
         return info_dict