mirror of
				https://github.com/yt-dlp/yt-dlp.git
				synced 2025-10-31 06:35:12 +00:00 
			
		
		
		
	[ie/youtube] Extract upload timestamp if available (#9856)
Closes #4962, Closes #9829 Authored by: coletdjnz
This commit is contained in:
		| @@ -2333,6 +2333,7 @@ These options may no longer work as intended | |||||||
|     --write-annotations              No supported site has annotations now |     --write-annotations              No supported site has annotations now | ||||||
|     --no-write-annotations           Default |     --no-write-annotations           Default | ||||||
|     --compat-options seperate-video-versions  No longer needed |     --compat-options seperate-video-versions  No longer needed | ||||||
|  |     --compat-options no-youtube-prefer-utc-upload-date  No longer supported | ||||||
| 
 | 
 | ||||||
| #### Removed | #### Removed | ||||||
| These options were deprecated since 2014 and have now been entirely removed | These options were deprecated since 2014 and have now been entirely removed | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ import os | |||||||
| import sys | import sys | ||||||
| import unittest | import unittest | ||||||
| import warnings | import warnings | ||||||
|  | import datetime as dt | ||||||
| 
 | 
 | ||||||
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | ||||||
| 
 | 
 | ||||||
| @@ -27,6 +28,7 @@ from yt_dlp.utils import ( | |||||||
|     ExtractorError, |     ExtractorError, | ||||||
|     InAdvancePagedList, |     InAdvancePagedList, | ||||||
|     LazyList, |     LazyList, | ||||||
|  |     NO_DEFAULT, | ||||||
|     OnDemandPagedList, |     OnDemandPagedList, | ||||||
|     Popen, |     Popen, | ||||||
|     age_restricted, |     age_restricted, | ||||||
| @@ -768,6 +770,11 @@ class TestUtil(unittest.TestCase): | |||||||
| 
 | 
 | ||||||
|     def test_parse_iso8601(self): |     def test_parse_iso8601(self): | ||||||
|         self.assertEqual(parse_iso8601('2014-03-23T23:04:26+0100'), 1395612266) |         self.assertEqual(parse_iso8601('2014-03-23T23:04:26+0100'), 1395612266) | ||||||
|  |         self.assertEqual(parse_iso8601('2014-03-23T23:04:26-07:00'), 1395641066) | ||||||
|  |         self.assertEqual(parse_iso8601('2014-03-23T23:04:26', timezone=dt.timedelta(hours=-7)), 1395641066) | ||||||
|  |         self.assertEqual(parse_iso8601('2014-03-23T23:04:26', timezone=NO_DEFAULT), None) | ||||||
|  |         # default does not override timezone in date_str | ||||||
|  |         self.assertEqual(parse_iso8601('2014-03-23T23:04:26-07:00', timezone=dt.timedelta(hours=-10)), 1395641066) | ||||||
|         self.assertEqual(parse_iso8601('2014-03-23T22:04:26+0000'), 1395612266) |         self.assertEqual(parse_iso8601('2014-03-23T22:04:26+0000'), 1395612266) | ||||||
|         self.assertEqual(parse_iso8601('2014-03-23T22:04:26Z'), 1395612266) |         self.assertEqual(parse_iso8601('2014-03-23T22:04:26Z'), 1395612266) | ||||||
|         self.assertEqual(parse_iso8601('2014-03-23T22:04:26.1234Z'), 1395612266) |         self.assertEqual(parse_iso8601('2014-03-23T22:04:26.1234Z'), 1395612266) | ||||||
|   | |||||||
| @@ -1325,6 +1325,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'uploader_url': 'https://www.youtube.com/@PhilippHagemeister', |                 'uploader_url': 'https://www.youtube.com/@PhilippHagemeister', | ||||||
|                 'uploader_id': '@PhilippHagemeister', |                 'uploader_id': '@PhilippHagemeister', | ||||||
|                 'heatmap': 'count:100', |                 'heatmap': 'count:100', | ||||||
|  |                 'timestamp': 1349198244, | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -1368,6 +1369,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'uploader_url': 'https://www.youtube.com/@PhilippHagemeister', |                 'uploader_url': 'https://www.youtube.com/@PhilippHagemeister', | ||||||
|                 'uploader_id': '@PhilippHagemeister', |                 'uploader_id': '@PhilippHagemeister', | ||||||
|                 'heatmap': 'count:100', |                 'heatmap': 'count:100', | ||||||
|  |                 'timestamp': 1349198244, | ||||||
|             }, |             }, | ||||||
|             'params': { |             'params': { | ||||||
|                 'skip_download': True, |                 'skip_download': True, | ||||||
| @@ -1454,6 +1456,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'comment_count': int, |                 'comment_count': int, | ||||||
|                 'channel_is_verified': True, |                 'channel_is_verified': True, | ||||||
|                 'heatmap': 'count:100', |                 'heatmap': 'count:100', | ||||||
|  |                 'timestamp': 1401991663, | ||||||
|             }, |             }, | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -1513,6 +1516,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'uploader': 'Projekt Melody', |                 'uploader': 'Projekt Melody', | ||||||
|                 'uploader_url': 'https://www.youtube.com/@ProjektMelody', |                 'uploader_url': 'https://www.youtube.com/@ProjektMelody', | ||||||
|                 'uploader_id': '@ProjektMelody', |                 'uploader_id': '@ProjektMelody', | ||||||
|  |                 'timestamp': 1577508724, | ||||||
|             }, |             }, | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -1618,6 +1622,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'uploader_url': 'https://www.youtube.com/@Olympics', |                 'uploader_url': 'https://www.youtube.com/@Olympics', | ||||||
|                 'uploader_id': '@Olympics', |                 'uploader_id': '@Olympics', | ||||||
|                 'channel_is_verified': True, |                 'channel_is_verified': True, | ||||||
|  |                 'timestamp': 1440707674, | ||||||
|             }, |             }, | ||||||
|             'params': { |             'params': { | ||||||
|                 'skip_download': 'requires avconv', |                 'skip_download': 'requires avconv', | ||||||
| @@ -1651,6 +1656,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'uploader': '孫ᄋᄅ', |                 'uploader': '孫ᄋᄅ', | ||||||
|                 'uploader_url': 'https://www.youtube.com/@AllenMeow', |                 'uploader_url': 'https://www.youtube.com/@AllenMeow', | ||||||
|                 'uploader_id': '@AllenMeow', |                 'uploader_id': '@AllenMeow', | ||||||
|  |                 'timestamp': 1299776999, | ||||||
|             }, |             }, | ||||||
|         }, |         }, | ||||||
|         # url_encoded_fmt_stream_map is empty string |         # url_encoded_fmt_stream_map is empty string | ||||||
| @@ -1794,6 +1800,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 }, |                 }, | ||||||
|             }], |             }], | ||||||
|             'params': {'skip_download': True}, |             'params': {'skip_download': True}, | ||||||
|  |             'skip': 'Not multifeed anymore', | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|             # Multifeed video with comma in title (see https://github.com/ytdl-org/youtube-dl/issues/8536) |             # Multifeed video with comma in title (see https://github.com/ytdl-org/youtube-dl/issues/8536) | ||||||
| @@ -1902,6 +1909,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'uploader': 'The Berkman Klein Center for Internet & Society', |                 'uploader': 'The Berkman Klein Center for Internet & Society', | ||||||
|                 'uploader_id': '@BKCHarvard', |                 'uploader_id': '@BKCHarvard', | ||||||
|                 'uploader_url': 'https://www.youtube.com/@BKCHarvard', |                 'uploader_url': 'https://www.youtube.com/@BKCHarvard', | ||||||
|  |                 'timestamp': 1422422076, | ||||||
|             }, |             }, | ||||||
|             'params': { |             'params': { | ||||||
|                 'skip_download': True, |                 'skip_download': True, | ||||||
| @@ -1937,6 +1945,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'uploader_id': '@BernieSanders', |                 'uploader_id': '@BernieSanders', | ||||||
|                 'channel_is_verified': True, |                 'channel_is_verified': True, | ||||||
|                 'heatmap': 'count:100', |                 'heatmap': 'count:100', | ||||||
|  |                 'timestamp': 1447987198, | ||||||
|             }, |             }, | ||||||
|             'params': { |             'params': { | ||||||
|                 'skip_download': True, |                 'skip_download': True, | ||||||
| @@ -2000,6 +2009,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'uploader_id': '@Vsauce', |                 'uploader_id': '@Vsauce', | ||||||
|                 'comment_count': int, |                 'comment_count': int, | ||||||
|                 'channel_is_verified': True, |                 'channel_is_verified': True, | ||||||
|  |                 'timestamp': 1484761047, | ||||||
|             }, |             }, | ||||||
|             'params': { |             'params': { | ||||||
|                 'skip_download': True, |                 'skip_download': True, | ||||||
| @@ -2155,6 +2165,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'uploader': 'l\'Or Vert asbl', |                 'uploader': 'l\'Or Vert asbl', | ||||||
|                 'uploader_url': 'https://www.youtube.com/@ElevageOrVert', |                 'uploader_url': 'https://www.youtube.com/@ElevageOrVert', | ||||||
|                 'uploader_id': '@ElevageOrVert', |                 'uploader_id': '@ElevageOrVert', | ||||||
|  |                 'timestamp': 1497343210, | ||||||
|             }, |             }, | ||||||
|             'params': { |             'params': { | ||||||
|                 'skip_download': True, |                 'skip_download': True, | ||||||
| @@ -2193,6 +2204,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'uploader_id': '@Csharp-video-tutorialsBlogspot', |                 'uploader_id': '@Csharp-video-tutorialsBlogspot', | ||||||
|                 'channel_is_verified': True, |                 'channel_is_verified': True, | ||||||
|                 'heatmap': 'count:100', |                 'heatmap': 'count:100', | ||||||
|  |                 'timestamp': 1377976349, | ||||||
|             }, |             }, | ||||||
|             'params': { |             'params': { | ||||||
|                 'skip_download': True, |                 'skip_download': True, | ||||||
| @@ -2275,6 +2287,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'uploader_id': '@CBSMornings', |                 'uploader_id': '@CBSMornings', | ||||||
|                 'comment_count': int, |                 'comment_count': int, | ||||||
|                 'channel_is_verified': True, |                 'channel_is_verified': True, | ||||||
|  |                 'timestamp': 1405513526, | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -2292,7 +2305,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'view_count': int, |                 'view_count': int, | ||||||
|                 'channel': 'Walk around Japan', |                 'channel': 'Walk around Japan', | ||||||
|                 'tags': ['Ueno Tokyo', 'Okachimachi Tokyo', 'Ameyoko Street', 'Tokyo attraction', 'Travel in Tokyo'], |                 'tags': ['Ueno Tokyo', 'Okachimachi Tokyo', 'Ameyoko Street', 'Tokyo attraction', 'Travel in Tokyo'], | ||||||
|                 'thumbnail': 'https://i.ytimg.com/vi_webp/cBvYw8_A0vQ/hqdefault.webp', |                 'thumbnail': 'https://i.ytimg.com/vi/cBvYw8_A0vQ/hqdefault.jpg', | ||||||
|                 'age_limit': 0, |                 'age_limit': 0, | ||||||
|                 'availability': 'public', |                 'availability': 'public', | ||||||
|                 'channel_url': 'https://www.youtube.com/channel/UC3o_t8PzBmXf5S9b7GLx1Mw', |                 'channel_url': 'https://www.youtube.com/channel/UC3o_t8PzBmXf5S9b7GLx1Mw', | ||||||
| @@ -2302,6 +2315,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'uploader': 'Walk around Japan', |                 'uploader': 'Walk around Japan', | ||||||
|                 'uploader_url': 'https://www.youtube.com/@walkaroundjapan7124', |                 'uploader_url': 'https://www.youtube.com/@walkaroundjapan7124', | ||||||
|                 'uploader_id': '@walkaroundjapan7124', |                 'uploader_id': '@walkaroundjapan7124', | ||||||
|  |                 'timestamp': 1605884416, | ||||||
|             }, |             }, | ||||||
|             'params': { |             'params': { | ||||||
|                 'skip_download': True, |                 'skip_download': True, | ||||||
| @@ -2397,6 +2411,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'comment_count': int, |                 'comment_count': int, | ||||||
|                 'channel_is_verified': True, |                 'channel_is_verified': True, | ||||||
|                 'heatmap': 'count:100', |                 'heatmap': 'count:100', | ||||||
|  |                 'timestamp': 1395685455, | ||||||
|             }, 'params': {'format': 'mhtml', 'skip_download': True} |             }, 'params': {'format': 'mhtml', 'skip_download': True} | ||||||
|         }, { |         }, { | ||||||
|             # Ensure video upload_date is in UTC timezone (video was uploaded 1641170939) |             # Ensure video upload_date is in UTC timezone (video was uploaded 1641170939) | ||||||
| @@ -2426,37 +2441,8 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'uploader_url': 'https://www.youtube.com/@LeonNguyen', |                 'uploader_url': 'https://www.youtube.com/@LeonNguyen', | ||||||
|                 'uploader_id': '@LeonNguyen', |                 'uploader_id': '@LeonNguyen', | ||||||
|                 'heatmap': 'count:100', |                 'heatmap': 'count:100', | ||||||
|  |                 'timestamp': 1641170939, | ||||||
|             } |             } | ||||||
|         }, { |  | ||||||
|             # Same video as above, but with --compat-opt no-youtube-prefer-utc-upload-date |  | ||||||
|             'url': 'https://www.youtube.com/watch?v=2NUZ8W2llS4', |  | ||||||
|             'info_dict': { |  | ||||||
|                 'id': '2NUZ8W2llS4', |  | ||||||
|                 'ext': 'mp4', |  | ||||||
|                 'title': 'The NP that test your phone performance 🙂', |  | ||||||
|                 'description': 'md5:144494b24d4f9dfacb97c1bbef5de84d', |  | ||||||
|                 'channel_id': 'UCRqNBSOHgilHfAczlUmlWHA', |  | ||||||
|                 'channel_url': 'https://www.youtube.com/channel/UCRqNBSOHgilHfAczlUmlWHA', |  | ||||||
|                 'duration': 21, |  | ||||||
|                 'view_count': int, |  | ||||||
|                 'age_limit': 0, |  | ||||||
|                 'categories': ['Gaming'], |  | ||||||
|                 'tags': 'count:23', |  | ||||||
|                 'playable_in_embed': True, |  | ||||||
|                 'live_status': 'not_live', |  | ||||||
|                 'upload_date': '20220102', |  | ||||||
|                 'like_count': int, |  | ||||||
|                 'availability': 'public', |  | ||||||
|                 'channel': 'Leon Nguyen', |  | ||||||
|                 'thumbnail': 'https://i.ytimg.com/vi_webp/2NUZ8W2llS4/maxresdefault.webp', |  | ||||||
|                 'comment_count': int, |  | ||||||
|                 'channel_follower_count': int, |  | ||||||
|                 'uploader': 'Leon Nguyen', |  | ||||||
|                 'uploader_url': 'https://www.youtube.com/@LeonNguyen', |  | ||||||
|                 'uploader_id': '@LeonNguyen', |  | ||||||
|                 'heatmap': 'count:100', |  | ||||||
|             }, |  | ||||||
|             'params': {'compat_opts': ['no-youtube-prefer-utc-upload-date']} |  | ||||||
|         }, { |         }, { | ||||||
|             # date text is premiered video, ensure upload date in UTC (published 1641172509) |             # date text is premiered video, ensure upload date in UTC (published 1641172509) | ||||||
|             'url': 'https://www.youtube.com/watch?v=mzZzzBU6lrM', |             'url': 'https://www.youtube.com/watch?v=mzZzzBU6lrM', | ||||||
| @@ -2488,38 +2474,41 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'comment_count': int, |                 'comment_count': int, | ||||||
|                 'channel_is_verified': True, |                 'channel_is_verified': True, | ||||||
|                 'heatmap': 'count:100', |                 'heatmap': 'count:100', | ||||||
|  |                 'timestamp': 1641172509, | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         {   # continuous livestream. Microformat upload date should be preferred. |         {   # continuous livestream. | ||||||
|             # Upload date was 2021-06-19 (not UTC), while stream start is 2021-11-27 |             # Upload date was 2022-07-12T05:12:29-07:00, while stream start is 2022-07-12T15:59:30+00:00 | ||||||
|             'url': 'https://www.youtube.com/watch?v=kgx4WGK0oNU', |             'url': 'https://www.youtube.com/watch?v=jfKfPfyJRdk', | ||||||
|             'info_dict': { |             'info_dict': { | ||||||
|                 'id': 'kgx4WGK0oNU', |                 'id': 'jfKfPfyJRdk', | ||||||
|                 'title': r're:jazz\/lofi hip hop radio🌱chill beats to relax\/study to \[LIVE 24\/7\] \d{4}-\d{2}-\d{2} \d{2}:\d{2}', |  | ||||||
|                 'ext': 'mp4', |                 'ext': 'mp4', | ||||||
|                 'channel_id': 'UC84whx2xxsiA1gXHXXqKGOA', |                 'channel_id': 'UCSJ4gkVC6NrvII8umztf0Ow', | ||||||
|                 'availability': 'public', |  | ||||||
|                 'age_limit': 0, |  | ||||||
|                 'release_timestamp': 1637975704, |  | ||||||
|                 'upload_date': '20210619', |  | ||||||
|                 'channel_url': 'https://www.youtube.com/channel/UC84whx2xxsiA1gXHXXqKGOA', |  | ||||||
|                 'live_status': 'is_live', |  | ||||||
|                 'thumbnail': 'https://i.ytimg.com/vi/kgx4WGK0oNU/maxresdefault.jpg', |  | ||||||
|                 'channel': 'Abao in Tokyo', |  | ||||||
|                 'channel_follower_count': int, |  | ||||||
|                 'release_date': '20211127', |  | ||||||
|                 'tags': 'count:39', |  | ||||||
|                 'categories': ['People & Blogs'], |  | ||||||
|                 'like_count': int, |                 'like_count': int, | ||||||
|                 'view_count': int, |                 'uploader': 'Lofi Girl', | ||||||
|                 'playable_in_embed': True, |                 'categories': ['Music'], | ||||||
|                 'description': 'md5:2ef1d002cad520f65825346e2084e49d', |  | ||||||
|                 'concurrent_view_count': int, |                 'concurrent_view_count': int, | ||||||
|                 'uploader': 'Abao in Tokyo', |                 'playable_in_embed': True, | ||||||
|                 'uploader_url': 'https://www.youtube.com/@abaointokyo', |                 'timestamp': 1657627949, | ||||||
|                 'uploader_id': '@abaointokyo', |                 'release_date': '20220712', | ||||||
|  |                 'channel_url': 'https://www.youtube.com/channel/UCSJ4gkVC6NrvII8umztf0Ow', | ||||||
|  |                 'description': 'md5:13a6f76df898f5674f9127139f3df6f7', | ||||||
|  |                 'age_limit': 0, | ||||||
|  |                 'thumbnail': 'https://i.ytimg.com/vi/jfKfPfyJRdk/maxresdefault.jpg', | ||||||
|  |                 'release_timestamp': 1657641570, | ||||||
|  |                 'uploader_url': 'https://www.youtube.com/@LofiGirl', | ||||||
|  |                 'channel_follower_count': int, | ||||||
|  |                 'channel_is_verified': True, | ||||||
|  |                 'title': r're:^lofi hip hop radio 📚 - beats to relax/study to', | ||||||
|  |                 'view_count': int, | ||||||
|  |                 'live_status': 'is_live', | ||||||
|  |                 'tags': 'count:32', | ||||||
|  |                 'channel': 'Lofi Girl', | ||||||
|  |                 'availability': 'public', | ||||||
|  |                 'upload_date': '20220712', | ||||||
|  |                 'uploader_id': '@LofiGirl', | ||||||
|             }, |             }, | ||||||
|             'params': {'skip_download': True} |             'params': {'skip_download': True}, | ||||||
|         }, { |         }, { | ||||||
|             'url': 'https://www.youtube.com/watch?v=tjjjtzRLHvA', |             'url': 'https://www.youtube.com/watch?v=tjjjtzRLHvA', | ||||||
|             'info_dict': { |             'info_dict': { | ||||||
| @@ -2545,6 +2534,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'uploader_id': '@lesmiscore', |                 'uploader_id': '@lesmiscore', | ||||||
|                 'uploader': 'Lesmiscore', |                 'uploader': 'Lesmiscore', | ||||||
|                 'uploader_url': 'https://www.youtube.com/@lesmiscore', |                 'uploader_url': 'https://www.youtube.com/@lesmiscore', | ||||||
|  |                 'timestamp': 1648005313, | ||||||
|             } |             } | ||||||
|         }, { |         }, { | ||||||
|             # Prefer primary title+description language metadata by default |             # Prefer primary title+description language metadata by default | ||||||
| @@ -2572,6 +2562,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'uploader_url': 'https://www.youtube.com/@coletdjnz', |                 'uploader_url': 'https://www.youtube.com/@coletdjnz', | ||||||
|                 'uploader_id': '@coletdjnz', |                 'uploader_id': '@coletdjnz', | ||||||
|                 'uploader': 'cole-dlp-test-acc', |                 'uploader': 'cole-dlp-test-acc', | ||||||
|  |                 'timestamp': 1662677394, | ||||||
|             }, |             }, | ||||||
|             'params': {'skip_download': True} |             'params': {'skip_download': True} | ||||||
|         }, { |         }, { | ||||||
| @@ -2585,7 +2576,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'duration': 5, |                 'duration': 5, | ||||||
|                 'live_status': 'not_live', |                 'live_status': 'not_live', | ||||||
|                 'channel_id': 'UCiu-3thuViMebBjw_5nWYrA', |                 'channel_id': 'UCiu-3thuViMebBjw_5nWYrA', | ||||||
|                 'upload_date': '20220728', |                 'upload_date': '20220729', | ||||||
|                 'view_count': int, |                 'view_count': int, | ||||||
|                 'categories': ['People & Blogs'], |                 'categories': ['People & Blogs'], | ||||||
|                 'thumbnail': r're:^https?://.*\.jpg', |                 'thumbnail': r're:^https?://.*\.jpg', | ||||||
| @@ -2598,6 +2589,8 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'uploader_url': 'https://www.youtube.com/@coletdjnz', |                 'uploader_url': 'https://www.youtube.com/@coletdjnz', | ||||||
|                 'uploader_id': '@coletdjnz', |                 'uploader_id': '@coletdjnz', | ||||||
|                 'uploader': 'cole-dlp-test-acc', |                 'uploader': 'cole-dlp-test-acc', | ||||||
|  |                 'timestamp': 1659073275, | ||||||
|  |                 'like_count': int, | ||||||
|             }, |             }, | ||||||
|             'params': {'skip_download': True, 'extractor_args': {'youtube': {'lang': ['fr']}}}, |             'params': {'skip_download': True, 'extractor_args': {'youtube': {'lang': ['fr']}}}, | ||||||
|             'expected_warnings': [r'Preferring "fr" translated fields'], |             'expected_warnings': [r'Preferring "fr" translated fields'], | ||||||
| @@ -2663,6 +2656,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'uploader': 'Projekt Melody', |                 'uploader': 'Projekt Melody', | ||||||
|                 'uploader_id': '@ProjektMelody', |                 'uploader_id': '@ProjektMelody', | ||||||
|                 'uploader_url': 'https://www.youtube.com/@ProjektMelody', |                 'uploader_url': 'https://www.youtube.com/@ProjektMelody', | ||||||
|  |                 'timestamp': 1577508724, | ||||||
|             }, |             }, | ||||||
|             'params': {'extractor_args': {'youtube': {'player_client': ['tv_embedded']}}, 'format': '251-drc'}, |             'params': {'extractor_args': {'youtube': {'player_client': ['tv_embedded']}}, 'format': '251-drc'}, | ||||||
|         }, |         }, | ||||||
| @@ -2697,6 +2691,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'uploader_id': '@sana_natori', |                 'uploader_id': '@sana_natori', | ||||||
|                 'channel_is_verified': True, |                 'channel_is_verified': True, | ||||||
|                 'heatmap': 'count:100', |                 'heatmap': 'count:100', | ||||||
|  |                 'timestamp': 1671798112, | ||||||
|             }, |             }, | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
| @@ -2766,6 +2761,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'uploader_url': 'https://www.youtube.com/@ChristopherSykesDocumentaries', |                 'uploader_url': 'https://www.youtube.com/@ChristopherSykesDocumentaries', | ||||||
|                 'uploader_id': '@ChristopherSykesDocumentaries', |                 'uploader_id': '@ChristopherSykesDocumentaries', | ||||||
|                 'heatmap': 'count:100', |                 'heatmap': 'count:100', | ||||||
|  |                 'timestamp': 1211825920, | ||||||
|             }, |             }, | ||||||
|             'params': { |             'params': { | ||||||
|                 'skip_download': True, |                 'skip_download': True, | ||||||
| @@ -4622,19 +4618,31 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|             'uploader_id': channel_handle, |             'uploader_id': channel_handle, | ||||||
|             'uploader_url': format_field(channel_handle, None, 'https://www.youtube.com/%s', default=None), |             'uploader_url': format_field(channel_handle, None, 'https://www.youtube.com/%s', default=None), | ||||||
|         }) |         }) | ||||||
|  | 
 | ||||||
|  |         # We only want timestamp IF it has time precision AND a timezone | ||||||
|  |         # Currently the uploadDate in microformats appears to be in US/Pacific timezone. | ||||||
|  |         timestamp = ( | ||||||
|  |             parse_iso8601(get_first(microformats, 'uploadDate'), timezone=NO_DEFAULT) | ||||||
|  |             or parse_iso8601(search_meta('uploadDate'), timezone=NO_DEFAULT) | ||||||
|  |         ) | ||||||
|  |         upload_date = ( | ||||||
|  |             dt.datetime.fromtimestamp(timestamp, dt.timezone.utc).strftime('%Y%m%d') if timestamp else | ||||||
|  |             ( | ||||||
|  |                 unified_strdate(get_first(microformats, 'uploadDate')) | ||||||
|  |                 or unified_strdate(search_meta('uploadDate')) | ||||||
|  |             )) | ||||||
|  | 
 | ||||||
|  |         # In the case we cannot get the timestamp: | ||||||
|         # The upload date for scheduled, live and past live streams / premieres in microformats |         # The upload date for scheduled, live and past live streams / premieres in microformats | ||||||
|         # may be different from the stream date. Although not in UTC, we will prefer it in this case. |         # may be different from the stream date. Although not in UTC, we will prefer it in this case. | ||||||
|         # See: https://github.com/yt-dlp/yt-dlp/pull/2223#issuecomment-1008485139 |         # See: https://github.com/yt-dlp/yt-dlp/pull/2223#issuecomment-1008485139 | ||||||
|         upload_date = ( |         if not upload_date or (not timestamp and live_status in ('not_live', None)): | ||||||
|             unified_strdate(get_first(microformats, 'uploadDate')) |             # this should be in UTC, as configured in the cookie/client context | ||||||
|             or unified_strdate(search_meta('uploadDate'))) |  | ||||||
|         if not upload_date or ( |  | ||||||
|             live_status in ('not_live', None) |  | ||||||
|             and 'no-youtube-prefer-utc-upload-date' not in self.get_param('compat_opts', []) |  | ||||||
|         ): |  | ||||||
|             upload_date = strftime_or_none( |             upload_date = strftime_or_none( | ||||||
|                 self._parse_time_text(self._get_text(vpir, 'dateText'))) or upload_date |                 self._parse_time_text(self._get_text(vpir, 'dateText'))) or upload_date | ||||||
|  | 
 | ||||||
|         info['upload_date'] = upload_date |         info['upload_date'] = upload_date | ||||||
|  |         info['timestamp'] = timestamp | ||||||
| 
 | 
 | ||||||
|         if upload_date and live_status not in ('is_live', 'post_live', 'is_upcoming'): |         if upload_date and live_status not in ('is_live', 'post_live', 'is_upcoming'): | ||||||
|             # Newly uploaded videos' HLS formats are potentially problematic and need to be checked |             # Newly uploaded videos' HLS formats are potentially problematic and need to be checked | ||||||
|   | |||||||
| @@ -478,7 +478,7 @@ def create_parser(): | |||||||
|             }, 'aliases': { |             }, 'aliases': { | ||||||
|                 'youtube-dl': ['all', '-multistreams', '-playlist-match-filter', '-manifest-filesize-approx'], |                 'youtube-dl': ['all', '-multistreams', '-playlist-match-filter', '-manifest-filesize-approx'], | ||||||
|                 'youtube-dlc': ['all', '-no-youtube-channel-redirect', '-no-live-chat', '-playlist-match-filter', '-manifest-filesize-approx'], |                 'youtube-dlc': ['all', '-no-youtube-channel-redirect', '-no-live-chat', '-playlist-match-filter', '-manifest-filesize-approx'], | ||||||
|                 '2021': ['2022', 'no-certifi', 'filename-sanitization', 'no-youtube-prefer-utc-upload-date'], |                 '2021': ['2022', 'no-certifi', 'filename-sanitization'], | ||||||
|                 '2022': ['2023', 'no-external-downloader-progress', 'playlist-match-filter', 'prefer-legacy-http-handler', 'manifest-filesize-approx'], |                 '2022': ['2023', 'no-external-downloader-progress', 'playlist-match-filter', 'prefer-legacy-http-handler', 'manifest-filesize-approx'], | ||||||
|                 '2023': [], |                 '2023': [], | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -1134,7 +1134,7 @@ def is_path_like(f): | |||||||
|     return isinstance(f, (str, bytes, os.PathLike)) |     return isinstance(f, (str, bytes, os.PathLike)) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def extract_timezone(date_str): | def extract_timezone(date_str, default=None): | ||||||
|     m = re.search( |     m = re.search( | ||||||
|         r'''(?x) |         r'''(?x) | ||||||
|             ^.{8,}?                                              # >=8 char non-TZ prefix, if present |             ^.{8,}?                                              # >=8 char non-TZ prefix, if present | ||||||
| @@ -1146,21 +1146,25 @@ def extract_timezone(date_str): | |||||||
|                 (?P<hours>[0-9]{2}):?(?P<minutes>[0-9]{2})       # hh[:]mm |                 (?P<hours>[0-9]{2}):?(?P<minutes>[0-9]{2})       # hh[:]mm | ||||||
|             $) |             $) | ||||||
|         ''', date_str) |         ''', date_str) | ||||||
|  |     timezone = None | ||||||
|  | 
 | ||||||
|     if not m: |     if not m: | ||||||
|         m = re.search(r'\d{1,2}:\d{1,2}(?:\.\d+)?(?P<tz>\s*[A-Z]+)$', date_str) |         m = re.search(r'\d{1,2}:\d{1,2}(?:\.\d+)?(?P<tz>\s*[A-Z]+)$', date_str) | ||||||
|         timezone = TIMEZONE_NAMES.get(m and m.group('tz').strip()) |         timezone = TIMEZONE_NAMES.get(m and m.group('tz').strip()) | ||||||
|         if timezone is not None: |         if timezone is not None: | ||||||
|             date_str = date_str[:-len(m.group('tz'))] |             date_str = date_str[:-len(m.group('tz'))] | ||||||
|         timezone = dt.timedelta(hours=timezone or 0) |             timezone = dt.timedelta(hours=timezone) | ||||||
|     else: |     else: | ||||||
|         date_str = date_str[:-len(m.group('tz'))] |         date_str = date_str[:-len(m.group('tz'))] | ||||||
|         if not m.group('sign'): |         if m.group('sign'): | ||||||
|             timezone = dt.timedelta() |  | ||||||
|         else: |  | ||||||
|             sign = 1 if m.group('sign') == '+' else -1 |             sign = 1 if m.group('sign') == '+' else -1 | ||||||
|             timezone = dt.timedelta( |             timezone = dt.timedelta( | ||||||
|                 hours=sign * int(m.group('hours')), |                 hours=sign * int(m.group('hours')), | ||||||
|                 minutes=sign * int(m.group('minutes'))) |                 minutes=sign * int(m.group('minutes'))) | ||||||
|  | 
 | ||||||
|  |     if timezone is None and default is not NO_DEFAULT: | ||||||
|  |         timezone = default or dt.timedelta() | ||||||
|  | 
 | ||||||
|     return timezone, date_str |     return timezone, date_str | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @@ -1172,10 +1176,9 @@ def parse_iso8601(date_str, delimiter='T', timezone=None): | |||||||
| 
 | 
 | ||||||
|     date_str = re.sub(r'\.[0-9]+', '', date_str) |     date_str = re.sub(r'\.[0-9]+', '', date_str) | ||||||
| 
 | 
 | ||||||
|     if timezone is None: |     timezone, date_str = extract_timezone(date_str, timezone) | ||||||
|         timezone, date_str = extract_timezone(date_str) |  | ||||||
| 
 | 
 | ||||||
|     with contextlib.suppress(ValueError): |     with contextlib.suppress(ValueError, TypeError): | ||||||
|         date_format = f'%Y-%m-%d{delimiter}%H:%M:%S' |         date_format = f'%Y-%m-%d{delimiter}%H:%M:%S' | ||||||
|         dt_ = dt.datetime.strptime(date_str, date_format) - timezone |         dt_ = dt.datetime.strptime(date_str, date_format) - timezone | ||||||
|         return calendar.timegm(dt_.timetuple()) |         return calendar.timegm(dt_.timetuple()) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 coletdjnz
					coletdjnz