From fdf58d9460693eca360985ddf9148609539516e0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 27 May 2024 17:01:51 +0000 Subject: [PATCH] [CI] auto update yt_dlp to upstream commit c53c2e40fde8f2e15c7c62f8ca1a5d9e90ddc079 --- lib/yt_dlp/extractor/_extractors.py | 1050 ++++++++++--------- lib/yt_dlp/extractor/abc.py | 4 +- lib/yt_dlp/extractor/abematv.py | 5 +- lib/yt_dlp/extractor/acfun.py | 4 +- lib/yt_dlp/extractor/adn.py | 4 +- lib/yt_dlp/extractor/adobetv.py | 4 +- lib/yt_dlp/extractor/airtv.py | 2 +- lib/yt_dlp/extractor/allstar.py | 1 - lib/yt_dlp/extractor/alphaporno.py | 4 +- lib/yt_dlp/extractor/alura.py | 12 +- lib/yt_dlp/extractor/amara.py | 2 +- lib/yt_dlp/extractor/amp.py | 2 +- lib/yt_dlp/extractor/anchorfm.py | 2 +- lib/yt_dlp/extractor/angel.py | 2 +- lib/yt_dlp/extractor/appleconnect.py | 5 +- lib/yt_dlp/extractor/appletrailers.py | 2 +- lib/yt_dlp/extractor/arnes.py | 2 +- lib/yt_dlp/extractor/atvat.py | 2 +- lib/yt_dlp/extractor/awaan.py | 2 +- lib/yt_dlp/extractor/banbye.py | 4 +- lib/yt_dlp/extractor/bannedvideo.py | 6 +- lib/yt_dlp/extractor/beeg.py | 1 - lib/yt_dlp/extractor/bleacherreport.py | 2 +- lib/yt_dlp/extractor/blogger.py | 2 +- lib/yt_dlp/extractor/bostonglobe.py | 1 - lib/yt_dlp/extractor/boxcast.py | 6 +- lib/yt_dlp/extractor/brainpop.py | 2 +- lib/yt_dlp/extractor/brightcove.py | 4 +- lib/yt_dlp/extractor/caffeinetv.py | 74 ++ lib/yt_dlp/extractor/cbs.py | 6 +- lib/yt_dlp/extractor/ceskatelevize.py | 2 +- lib/yt_dlp/extractor/cinetecamilano.py | 1 + lib/yt_dlp/extractor/clippit.py | 4 +- lib/yt_dlp/extractor/common.py | 53 +- lib/yt_dlp/extractor/corus.py | 2 +- lib/yt_dlp/extractor/crackle.py | 2 +- lib/yt_dlp/extractor/cspan.py | 6 +- lib/yt_dlp/extractor/ctsnews.py | 2 +- lib/yt_dlp/extractor/dailymail.py | 2 +- lib/yt_dlp/extractor/damtomo.py | 2 +- lib/yt_dlp/extractor/democracynow.py | 4 +- lib/yt_dlp/extractor/digitalconcerthall.py | 1 - lib/yt_dlp/extractor/discoverygo.py | 2 +- lib/yt_dlp/extractor/disney.py | 4 +- lib/yt_dlp/extractor/douyutv.py | 2 +- lib/yt_dlp/extractor/dplay.py | 2 +- lib/yt_dlp/extractor/drtuber.py | 2 +- lib/yt_dlp/extractor/duboku.py | 2 +- lib/yt_dlp/extractor/dvtv.py | 4 +- lib/yt_dlp/extractor/dw.py | 2 +- lib/yt_dlp/extractor/ertgr.py | 4 +- lib/yt_dlp/extractor/europa.py | 2 +- lib/yt_dlp/extractor/euscreen.py | 3 +- lib/yt_dlp/extractor/eyedotv.py | 4 +- lib/yt_dlp/extractor/fancode.py | 8 +- lib/yt_dlp/extractor/faz.py | 2 +- lib/yt_dlp/extractor/fczenit.py | 2 +- lib/yt_dlp/extractor/fifa.py | 1 - lib/yt_dlp/extractor/filmon.py | 4 +- lib/yt_dlp/extractor/gab.py | 2 +- lib/yt_dlp/extractor/gamejolt.py | 2 +- lib/yt_dlp/extractor/gaskrank.py | 1 + lib/yt_dlp/extractor/gbnews.py | 107 ++ lib/yt_dlp/extractor/generic.py | 2 +- lib/yt_dlp/extractor/gettr.py | 2 +- lib/yt_dlp/extractor/gigya.py | 1 - lib/yt_dlp/extractor/glomex.py | 2 +- lib/yt_dlp/extractor/go.py | 10 +- lib/yt_dlp/extractor/godresource.py | 2 +- lib/yt_dlp/extractor/gofile.py | 5 +- lib/yt_dlp/extractor/gotostage.py | 9 +- lib/yt_dlp/extractor/hbo.py | 4 +- lib/yt_dlp/extractor/hearthisat.py | 2 +- lib/yt_dlp/extractor/hketv.py | 2 +- lib/yt_dlp/extractor/hrti.py | 2 +- lib/yt_dlp/extractor/huya.py | 6 +- lib/yt_dlp/extractor/ichinanalive.py | 2 +- lib/yt_dlp/extractor/infoq.py | 4 +- lib/yt_dlp/extractor/iprima.py | 6 +- lib/yt_dlp/extractor/iqiyi.py | 10 +- lib/yt_dlp/extractor/itprotv.py | 3 +- lib/yt_dlp/extractor/itv.py | 9 +- lib/yt_dlp/extractor/iwara.py | 4 +- lib/yt_dlp/extractor/jamendo.py | 2 +- lib/yt_dlp/extractor/japandiet.py | 4 +- lib/yt_dlp/extractor/jove.py | 5 +- lib/yt_dlp/extractor/jstream.py | 2 +- lib/yt_dlp/extractor/kakao.py | 2 +- lib/yt_dlp/extractor/kaltura.py | 8 +- lib/yt_dlp/extractor/kankanews.py | 4 +- lib/yt_dlp/extractor/kuwo.py | 4 +- lib/yt_dlp/extractor/lci.py | 27 +- lib/yt_dlp/extractor/lcp.py | 2 +- lib/yt_dlp/extractor/lecture2go.py | 2 +- lib/yt_dlp/extractor/lecturio.py | 2 +- lib/yt_dlp/extractor/leeco.py | 2 +- lib/yt_dlp/extractor/libraryofcongress.py | 1 - lib/yt_dlp/extractor/lifenews.py | 2 +- lib/yt_dlp/extractor/limelight.py | 2 +- lib/yt_dlp/extractor/linkedin.py | 2 +- lib/yt_dlp/extractor/mainstreaming.py | 3 +- lib/yt_dlp/extractor/manoto.py | 7 +- lib/yt_dlp/extractor/medaltv.py | 2 +- lib/yt_dlp/extractor/mediaklikk.py | 7 +- lib/yt_dlp/extractor/mediaset.py | 4 +- lib/yt_dlp/extractor/mediasite.py | 5 +- lib/yt_dlp/extractor/microsoftstream.py | 2 +- lib/yt_dlp/extractor/mildom.py | 4 +- lib/yt_dlp/extractor/mit.py | 4 +- lib/yt_dlp/extractor/monstercat.py | 2 +- lib/yt_dlp/extractor/moviepilot.py | 2 +- lib/yt_dlp/extractor/movingimage.py | 2 +- lib/yt_dlp/extractor/msn.py | 2 +- lib/yt_dlp/extractor/n1.py | 2 +- lib/yt_dlp/extractor/naver.py | 2 +- lib/yt_dlp/extractor/nba.py | 2 +- lib/yt_dlp/extractor/nbc.py | 2 +- lib/yt_dlp/extractor/ndr.py | 2 +- lib/yt_dlp/extractor/nfhsnetwork.py | 8 +- lib/yt_dlp/extractor/nhl.py | 2 +- lib/yt_dlp/extractor/ninenews.py | 2 +- lib/yt_dlp/extractor/ninenow.py | 2 +- lib/yt_dlp/extractor/nitter.py | 9 +- lib/yt_dlp/extractor/nobelprize.py | 6 +- lib/yt_dlp/extractor/noz.py | 6 +- lib/yt_dlp/extractor/nuevo.py | 6 +- lib/yt_dlp/extractor/nuvid.py | 2 +- lib/yt_dlp/extractor/nzherald.py | 5 +- lib/yt_dlp/extractor/odkmedia.py | 2 +- lib/yt_dlp/extractor/olympics.py | 5 +- lib/yt_dlp/extractor/onenewsnz.py | 6 +- lib/yt_dlp/extractor/onet.py | 4 +- lib/yt_dlp/extractor/opencast.py | 2 +- lib/yt_dlp/extractor/openrec.py | 2 +- lib/yt_dlp/extractor/ora.py | 1 + lib/yt_dlp/extractor/packtpub.py | 3 +- lib/yt_dlp/extractor/panopto.py | 10 +- lib/yt_dlp/extractor/paramountplus.py | 2 +- lib/yt_dlp/extractor/pbs.py | 4 +- lib/yt_dlp/extractor/pearvideo.py | 2 +- lib/yt_dlp/extractor/peertube.py | 2 +- lib/yt_dlp/extractor/piksel.py | 21 +- lib/yt_dlp/extractor/pladform.py | 4 +- lib/yt_dlp/extractor/platzi.py | 2 +- lib/yt_dlp/extractor/playtvak.py | 2 +- lib/yt_dlp/extractor/pluralsight.py | 2 +- lib/yt_dlp/extractor/polsatgo.py | 2 +- lib/yt_dlp/extractor/pornflip.py | 6 +- lib/yt_dlp/extractor/pornovoisines.py | 2 +- lib/yt_dlp/extractor/prx.py | 11 +- lib/yt_dlp/extractor/puhutv.py | 2 +- lib/yt_dlp/extractor/qingting.py | 1 - lib/yt_dlp/extractor/qqmusic.py | 2 +- lib/yt_dlp/extractor/radiocanada.py | 2 +- lib/yt_dlp/extractor/radiocomercial.py | 2 +- lib/yt_dlp/extractor/radiozet.py | 2 +- lib/yt_dlp/extractor/radlive.py | 4 +- lib/yt_dlp/extractor/rai.py | 4 +- lib/yt_dlp/extractor/rbgtum.py | 2 +- lib/yt_dlp/extractor/rcti.py | 4 +- lib/yt_dlp/extractor/rds.py | 4 +- lib/yt_dlp/extractor/redbulltv.py | 2 +- lib/yt_dlp/extractor/reddit.py | 2 +- lib/yt_dlp/extractor/redgifs.py | 2 +- lib/yt_dlp/extractor/redtube.py | 2 +- lib/yt_dlp/extractor/reuters.py | 2 +- lib/yt_dlp/extractor/rmcdecouverte.py | 2 +- lib/yt_dlp/extractor/rte.py | 2 +- lib/yt_dlp/extractor/rtp.py | 9 +- lib/yt_dlp/extractor/rtvcplay.py | 7 +- lib/yt_dlp/extractor/rtvs.py | 1 - lib/yt_dlp/extractor/rutube.py | 2 +- lib/yt_dlp/extractor/rutv.py | 6 +- lib/yt_dlp/extractor/ruutu.py | 2 +- lib/yt_dlp/extractor/safari.py | 1 - lib/yt_dlp/extractor/scrippsnetworks.py | 4 +- lib/yt_dlp/extractor/scte.py | 2 +- lib/yt_dlp/extractor/sendtonews.py | 6 +- lib/yt_dlp/extractor/seznamzpravy.py | 2 +- lib/yt_dlp/extractor/shahid.py | 2 +- lib/yt_dlp/extractor/shemaroome.py | 2 +- lib/yt_dlp/extractor/sixplay.py | 2 +- lib/yt_dlp/extractor/skynewsarabia.py | 2 +- lib/yt_dlp/extractor/sohu.py | 8 +- lib/yt_dlp/extractor/sovietscloset.py | 5 +- lib/yt_dlp/extractor/spankbang.py | 2 +- lib/yt_dlp/extractor/springboardplatform.py | 6 +- lib/yt_dlp/extractor/startv.py | 4 +- lib/yt_dlp/extractor/stitcher.py | 2 +- lib/yt_dlp/extractor/storyfire.py | 2 +- lib/yt_dlp/extractor/streamable.py | 2 +- lib/yt_dlp/extractor/stripchat.py | 2 +- lib/yt_dlp/extractor/sunporno.py | 4 +- lib/yt_dlp/extractor/syfy.py | 2 +- lib/yt_dlp/extractor/tbs.py | 2 +- lib/yt_dlp/extractor/teachable.py | 4 +- lib/yt_dlp/extractor/teachertube.py | 2 +- lib/yt_dlp/extractor/teamcoco.py | 2 +- lib/yt_dlp/extractor/teamtreehouse.py | 2 +- lib/yt_dlp/extractor/ted.py | 5 +- lib/yt_dlp/extractor/tele13.py | 2 +- lib/yt_dlp/extractor/telewebion.py | 1 + lib/yt_dlp/extractor/tempo.py | 2 +- lib/yt_dlp/extractor/tencent.py | 2 +- lib/yt_dlp/extractor/theguardian.py | 2 +- lib/yt_dlp/extractor/theintercept.py | 4 +- lib/yt_dlp/extractor/theplatform.py | 24 +- lib/yt_dlp/extractor/thisvid.py | 2 +- lib/yt_dlp/extractor/threeqsdn.py | 2 +- lib/yt_dlp/extractor/tiktok.py | 410 +++++--- lib/yt_dlp/extractor/toypics.py | 3 +- lib/yt_dlp/extractor/triller.py | 2 +- lib/yt_dlp/extractor/trueid.py | 4 +- lib/yt_dlp/extractor/tumblr.py | 2 +- lib/yt_dlp/extractor/turner.py | 12 +- lib/yt_dlp/extractor/tv2.py | 4 +- lib/yt_dlp/extractor/tv2hu.py | 2 +- lib/yt_dlp/extractor/tvanouvelles.py | 2 +- lib/yt_dlp/extractor/tvn24.py | 2 +- lib/yt_dlp/extractor/tvp.py | 2 +- lib/yt_dlp/extractor/tvplay.py | 2 +- lib/yt_dlp/extractor/tvplayer.py | 2 +- lib/yt_dlp/extractor/tweakers.py | 2 +- lib/yt_dlp/extractor/twitter.py | 2 +- lib/yt_dlp/extractor/udn.py | 2 +- lib/yt_dlp/extractor/ukcolumn.py | 8 +- lib/yt_dlp/extractor/urplay.py | 4 +- lib/yt_dlp/extractor/usatoday.py | 2 +- lib/yt_dlp/extractor/ustream.py | 4 +- lib/yt_dlp/extractor/ustudio.py | 2 +- lib/yt_dlp/extractor/veo.py | 1 - lib/yt_dlp/extractor/vesti.py | 2 +- lib/yt_dlp/extractor/vevo.py | 2 +- lib/yt_dlp/extractor/vice.py | 4 +- lib/yt_dlp/extractor/vidio.py | 2 +- lib/yt_dlp/extractor/vidlii.py | 2 +- lib/yt_dlp/extractor/vimeo.py | 10 +- lib/yt_dlp/extractor/viu.py | 6 +- lib/yt_dlp/extractor/vk.py | 6 +- lib/yt_dlp/extractor/walla.py | 2 +- lib/yt_dlp/extractor/washingtonpost.py | 1 - lib/yt_dlp/extractor/wdr.py | 4 +- lib/yt_dlp/extractor/weibo.py | 2 +- lib/yt_dlp/extractor/whowatch.py | 4 +- lib/yt_dlp/extractor/wimtv.py | 2 +- lib/yt_dlp/extractor/wppilot.py | 10 +- lib/yt_dlp/extractor/wsj.py | 2 +- lib/yt_dlp/extractor/xhamster.py | 2 +- lib/yt_dlp/extractor/xnxx.py | 2 +- lib/yt_dlp/extractor/xstream.py | 4 +- lib/yt_dlp/extractor/xvideos.py | 2 +- lib/yt_dlp/extractor/xxxymovies.py | 2 +- lib/yt_dlp/extractor/yandexmusic.py | 2 +- lib/yt_dlp/extractor/youporn.py | 391 ++++++- lib/yt_dlp/extractor/youtube.py | 134 +-- lib/yt_dlp/extractor/zapiks.py | 4 +- lib/yt_dlp/extractor/zhihu.py | 2 +- lib/yt_dlp/extractor/zingmp3.py | 2 +- lib/yt_dlp/extractor/zype.py | 2 +- lib/yt_dlp/options.py | 2 +- lib/yt_dlp/utils/_utils.py | 21 +- lib/yt_dlp/version.py | 6 +- lib/yt_dlp_version | 2 +- 263 files changed, 1892 insertions(+), 1238 deletions(-) create mode 100644 lib/yt_dlp/extractor/caffeinetv.py create mode 100644 lib/yt_dlp/extractor/gbnews.py diff --git a/lib/yt_dlp/extractor/_extractors.py b/lib/yt_dlp/extractor/_extractors.py index fc18ead3a..e9cd38a65 100644 --- a/lib/yt_dlp/extractor/_extractors.py +++ b/lib/yt_dlp/extractor/_extractors.py @@ -1,4 +1,5 @@ # flake8: noqa: F401 +# isort: off from .youtube import ( # Youtube is moved to the top to improve performance YoutubeIE, @@ -24,6 +25,8 @@ YoutubeConsentRedirectIE, ) +# isort: on + from .abc import ( ABCIE, ABCIViewIE, @@ -43,27 +46,33 @@ ) from .academicearth import AcademicEarthCourseIE from .acast import ( - ACastIE, ACastChannelIE, + ACastIE, +) +from .acfun import ( + AcFunBangumiIE, + AcFunVideoIE, +) +from .adn import ( + ADNIE, + ADNSeasonIE, ) -from .acfun import AcFunVideoIE, AcFunBangumiIE -from .adn import ADNIE, ADNSeasonIE from .adobeconnect import AdobeConnectIE from .adobetv import ( + AdobeTVChannelIE, AdobeTVEmbedIE, AdobeTVIE, AdobeTVShowIE, - AdobeTVChannelIE, AdobeTVVideoIE, ) from .adultswim import AdultSwimIE from .aenetworks import ( - AENetworksIE, AENetworksCollectionIE, + AENetworksIE, AENetworksShowIE, - HistoryTopicIE, - HistoryPlayerIE, BiographyIE, + HistoryPlayerIE, + HistoryTopicIE, ) from .aeonco import AeonCoIE from .afreecatv import ( @@ -79,77 +88,85 @@ ) from .airtv import AirTVIE from .aitube import AitubeKZVideoIE +from .aliexpress import AliExpressLiveIE from .aljazeera import AlJazeeraIE +from .allocine import AllocineIE from .allstar import ( AllstarIE, AllstarProfileIE, ) from .alphaporno import AlphaPornoIE +from .alsace20tv import ( + Alsace20TVEmbedIE, + Alsace20TVIE, +) from .altcensored import ( - AltCensoredIE, AltCensoredChannelIE, + AltCensoredIE, ) from .alura import ( + AluraCourseIE, AluraIE, - AluraCourseIE ) from .amadeustv import AmadeusTVIE from .amara import AmaraIE -from .amcnetworks import AMCNetworksIE from .amazon import ( - AmazonStoreIE, AmazonReviewsIE, + AmazonStoreIE, ) from .amazonminitv import ( AmazonMiniTVIE, AmazonMiniTVSeasonIE, AmazonMiniTVSeriesIE, ) +from .amcnetworks import AMCNetworksIE from .americastestkitchen import ( AmericasTestKitchenIE, AmericasTestKitchenSeasonIE, ) from .anchorfm import AnchorFMEpisodeIE from .angel import AngelIE +from .antenna import ( + Ant1NewsGrArticleIE, + Ant1NewsGrEmbedIE, + AntennaGrWatchIE, +) from .anvato import AnvatoIE from .aol import AolIE -from .allocine import AllocineIE -from .aliexpress import AliExpressLiveIE -from .alsace20tv import ( - Alsace20TVIE, - Alsace20TVEmbedIE, -) from .apa import APAIE from .aparat import AparatIE from .appleconnect import AppleConnectIE +from .applepodcasts import ApplePodcastsIE from .appletrailers import ( AppleTrailersIE, AppleTrailersSectionIE, ) -from .applepodcasts import ApplePodcastsIE from .archiveorg import ( ArchiveOrgIE, YoutubeWebArchiveIE, ) from .arcpublishing import ArcPublishingIE -from .arkena import ArkenaIE from .ard import ( + ARDIE, ARDBetaMediathekIE, ARDMediathekCollectionIE, - ARDIE, ) +from .arkena import ArkenaIE +from .arnes import ArnesIE from .art19 import ( Art19IE, Art19ShowIE, ) from .arte import ( - ArteTVIE, + ArteTVCategoryIE, ArteTVEmbedIE, + ArteTVIE, ArteTVPlaylistIE, - ArteTVCategoryIE, ) -from .arnes import ArnesIE -from .asobichannel import AsobiChannelIE, AsobiChannelTagURLIE +from .asobichannel import ( + AsobiChannelIE, + AsobiChannelTagURLIE, +) from .asobistage import AsobiStageIE from .atresplayer import AtresPlayerIE from .atscaleconf import AtScaleConfEventIE @@ -160,57 +177,60 @@ AudiodraftCustomIE, AudiodraftGenericIE, ) -from .audiomack import AudiomackIE, AudiomackAlbumIE +from .audiomack import ( + AudiomackAlbumIE, + AudiomackIE, +) from .audius import ( AudiusIE, - AudiusTrackIE, AudiusPlaylistIE, AudiusProfileIE, + AudiusTrackIE, ) from .awaan import ( AWAANIE, - AWAANVideoIE, AWAANLiveIE, AWAANSeasonIE, + AWAANVideoIE, ) from .axs import AxsIE from .azmedien import AZMedienIE from .baidu import BaiduVideoIE from .banbye import ( - BanByeIE, BanByeChannelIE, + BanByeIE, ) from .bandaichannel import BandaiChannelIE from .bandcamp import ( - BandcampIE, BandcampAlbumIE, - BandcampWeeklyIE, + BandcampIE, BandcampUserIE, + BandcampWeeklyIE, ) from .bannedvideo import BannedVideoIE from .bbc import ( - BBCCoUkIE, + BBCIE, BBCCoUkArticleIE, + BBCCoUkIE, BBCCoUkIPlayerEpisodesIE, BBCCoUkIPlayerGroupIE, BBCCoUkPlaylistIE, - BBCIE, ) -from .beeg import BeegIE -from .behindkink import BehindKinkIE -from .bellmedia import BellMediaIE from .beatbump import ( - BeatBumpVideoIE, BeatBumpPlaylistIE, + BeatBumpVideoIE, ) from .beatport import BeatportIE +from .beeg import BeegIE +from .behindkink import BehindKinkIE +from .bellmedia import BellMediaIE from .berufetv import BerufeTVIE from .bet import BetIE from .bfi import BFIPlayerIE from .bfmtv import ( BFMTVIE, - BFMTVLiveIE, BFMTVArticleIE, + BFMTVLiveIE, ) from .bibeltv import ( BibelTVLiveIE, @@ -221,37 +241,37 @@ from .bigo import BigoIE from .bild import BildIE from .bilibili import ( - BiliBiliIE, + BilibiliAudioAlbumIE, + BilibiliAudioIE, BiliBiliBangumiIE, - BiliBiliBangumiSeasonIE, BiliBiliBangumiMediaIE, + BiliBiliBangumiSeasonIE, + BilibiliCategoryIE, BilibiliCheeseIE, BilibiliCheeseSeasonIE, - BiliBiliSearchIE, - BilibiliCategoryIE, - BilibiliAudioIE, - BilibiliAudioAlbumIE, - BiliBiliPlayerIE, - BilibiliSpaceVideoIE, - BilibiliSpaceAudioIE, BilibiliCollectionListIE, - BilibiliSeriesListIE, BilibiliFavoritesListIE, - BilibiliWatchlaterIE, + BiliBiliIE, + BiliBiliPlayerIE, BilibiliPlaylistIE, + BiliBiliSearchIE, + BilibiliSeriesListIE, + BilibiliSpaceAudioIE, + BilibiliSpaceVideoIE, + BilibiliWatchlaterIE, BiliIntlIE, BiliIntlSeriesIE, BiliLiveIE, ) from .biobiochiletv import BioBioChileTVIE from .bitchute import ( - BitChuteIE, BitChuteChannelIE, + BitChuteIE, ) from .blackboardcollaborate import BlackboardCollaborateIE from .bleacherreport import ( - BleacherReportIE, BleacherReportCMSIE, + BleacherReportIE, ) from .blerp import BlerpIE from .blogger import BloggerIE @@ -264,68 +284,69 @@ from .boxcast import BoxCastVideoIE from .bpb import BpbIE from .br import BRIE -from .bravotv import BravoTVIE from .brainpop import ( - BrainPOPIE, - BrainPOPJrIE, BrainPOPELLIE, BrainPOPEspIE, BrainPOPFrIE, + BrainPOPIE, BrainPOPIlIE, + BrainPOPJrIE, ) +from .bravotv import BravoTVIE from .breitbart import BreitBartIE from .brightcove import ( BrightcoveLegacyIE, BrightcoveNewIE, ) from .brilliantpala import ( - BrilliantpalaElearnIE, BrilliantpalaClassesIE, + BrilliantpalaElearnIE, ) -from .businessinsider import BusinessInsiderIE from .bundesliga import BundesligaIE from .bundestag import BundestagIE +from .businessinsider import BusinessInsiderIE from .buzzfeed import BuzzFeedIE from .byutv import BYUtvIE from .c56 import C56IE +from .caffeinetv import CaffeineTVIE from .callin import CallinIE from .caltrans import CaltransIE from .cam4 import CAM4IE from .camdemy import ( + CamdemyFolderIE, CamdemyIE, - CamdemyFolderIE ) from .camfm import ( CamFMEpisodeIE, - CamFMShowIE + CamFMShowIE, ) from .cammodels import CamModelsIE from .camsoda import CamsodaIE from .camtasia import CamtasiaEmbedIE from .canal1 import Canal1IE from .canalalpha import CanalAlphaIE -from .canalplus import CanalplusIE from .canalc2 import Canalc2IE +from .canalplus import CanalplusIE from .caracoltv import CaracolTvPlayIE from .cartoonnetwork import CartoonNetworkIE from .cbc import ( CBCIE, - CBCPlayerIE, - CBCPlayerPlaylistIE, CBCGemIE, - CBCGemPlaylistIE, CBCGemLiveIE, + CBCGemPlaylistIE, + CBCPlayerIE, + CBCPlayerPlaylistIE, ) from .cbs import ( CBSIE, ParamountPressExpressIE, ) from .cbsnews import ( - CBSNewsEmbedIE, - CBSNewsIE, - CBSLocalIE, CBSLocalArticleIE, + CBSLocalIE, CBSLocalLiveIE, + CBSNewsEmbedIE, + CBSNewsIE, CBSNewsLiveIE, CBSNewsLiveVideoIE, ) @@ -354,12 +375,12 @@ from .cinemax import CinemaxIE from .cinetecamilano import CinetecaMilanoIE from .cineverse import ( - CineverseIE, CineverseDetailsIE, + CineverseIE, ) from .ciscolive import ( - CiscoLiveSessionIE, CiscoLiveSearchIE, + CiscoLiveSessionIE, ) from .ciscowebex import CiscoWebexIE from .cjsw import CJSWIE @@ -372,16 +393,13 @@ from .clubic import ClubicIE from .clyp import ClypIE from .cmt import CMTIE -from .cnbc import ( - CNBCVideoIE, -) +from .cnbc import CNBCVideoIE from .cnn import ( CNNIE, - CNNBlogsIE, CNNArticleIE, + CNNBlogsIE, CNNIndonesiaIE, ) -from .coub import CoubIE from .comedycentral import ( ComedyCentralIE, ComedyCentralTVIE, @@ -399,44 +417,48 @@ from .condenast import CondeNastIE from .contv import CONtvIE from .corus import CorusIE +from .coub import CoubIE +from .cozytv import CozyTVIE from .cpac import ( CPACIE, CPACPlaylistIE, ) -from .cozytv import CozyTVIE from .cracked import CrackedIE from .crackle import CrackleIE from .craftsy import CraftsyIE from .crooksandliars import CrooksAndLiarsIE from .crowdbunker import ( - CrowdBunkerIE, CrowdBunkerChannelIE, + CrowdBunkerIE, ) from .crtvg import CrtvgIE from .crunchyroll import ( + CrunchyrollArtistIE, CrunchyrollBetaIE, CrunchyrollBetaShowIE, CrunchyrollMusicIE, - CrunchyrollArtistIE, ) -from .cspan import CSpanIE, CSpanCongressIE +from .cspan import ( + CSpanCongressIE, + CSpanIE, +) from .ctsnews import CtsNewsIE from .ctv import CTVIE from .ctvnews import CTVNewsIE from .cultureunplugged import CultureUnpluggedIE from .curiositystream import ( - CuriosityStreamIE, CuriosityStreamCollectionsIE, + CuriosityStreamIE, CuriosityStreamSeriesIE, ) from .cwtv import CWTVIE from .cybrary import ( + CybraryCourseIE, CybraryIE, - CybraryCourseIE ) from .dacast import ( - DacastVODIE, DacastPlaylistIE, + DacastVODIE, ) from .dailymail import DailyMailIE from .dailymotion import ( @@ -458,8 +480,8 @@ DangalPlaySeasonIE, ) from .daum import ( - DaumIE, DaumClipIE, + DaumIE, DaumPlaylistIE, DaumUserIE, ) @@ -467,49 +489,69 @@ from .dbtv import DBTVIE from .dctp import DctpTvIE from .deezer import ( - DeezerPlaylistIE, DeezerAlbumIE, + DeezerPlaylistIE, ) from .democracynow import DemocracynowIE from .detik import DetikEmbedIE +from .deuxm import ( + DeuxMIE, + DeuxMNewsIE, +) +from .dfb import DFBIE +from .dhm import DHMIE +from .digitalconcerthall import DigitalConcertHallIE +from .digiteka import DigitekaIE +from .discogs import DiscogsReleasePlaylistIE +from .discovery import DiscoveryIE +from .disney import DisneyIE +from .dispeak import DigitallySpeakingIE from .dlf import ( DLFIE, DLFCorpusIE, ) -from .dfb import DFBIE -from .dhm import DHMIE +from .dlive import ( + DLiveStreamIE, + DLiveVODIE, +) from .douyutv import ( DouyuShowIE, DouyuTVIE, ) from .dplay import ( - DPlayIE, - DiscoveryPlusIE, - HGTVDeIE, - GoDiscoveryIE, - TravelChannelIE, + TLCIE, + AmHistoryChannelIE, + AnimalPlanetIE, CookingChannelIE, - HGTVUsaIE, - FoodNetworkIE, - InvestigationDiscoveryIE, DestinationAmericaIE, - AmHistoryChannelIE, - ScienceChannelIE, - DIYNetworkIE, DiscoveryLifeIE, - AnimalPlanetIE, - TLCIE, - MotorTrendIE, - MotorTrendOnDemandIE, - DiscoveryPlusIndiaIE, DiscoveryNetworksDeIE, + DiscoveryPlusIE, + DiscoveryPlusIndiaIE, + DiscoveryPlusIndiaShowIE, DiscoveryPlusItalyIE, DiscoveryPlusItalyShowIE, - DiscoveryPlusIndiaShowIE, + DIYNetworkIE, + DPlayIE, + FoodNetworkIE, GlobalCyclingNetworkPlusIE, + GoDiscoveryIE, + HGTVDeIE, + HGTVUsaIE, + InvestigationDiscoveryIE, + MotorTrendIE, + MotorTrendOnDemandIE, + ScienceChannelIE, + TravelChannelIE, ) -from .dreisat import DreiSatIE from .drbonanza import DRBonanzaIE +from .dreisat import DreiSatIE +from .drooble import DroobleIE +from .dropbox import DropboxIE +from .dropout import ( + DropoutIE, + DropoutSeasonIE, +) from .drtuber import DrTuberIE from .drtv import ( DRTVIE, @@ -518,32 +560,21 @@ DRTVSeriesIE, ) from .dtube import DTubeIE -from .dvtv import DVTVIE from .duboku import ( DubokuIE, - DubokuPlaylistIE + DubokuPlaylistIE, ) from .dumpert import DumpertIE -from .deuxm import ( - DeuxMIE, - DeuxMNewsIE -) -from .digitalconcerthall import DigitalConcertHallIE -from .discogs import DiscogsReleasePlaylistIE -from .discovery import DiscoveryIE -from .disney import DisneyIE -from .dispeak import DigitallySpeakingIE -from .dropbox import DropboxIE -from .dropout import ( - DropoutSeasonIE, - DropoutIE -) from .duoplay import DuoplayIE +from .dvtv import DVTVIE from .dw import ( DWIE, DWArticleIE, ) -from .eagleplatform import EaglePlatformIE, ClipYouEmbedIE +from .eagleplatform import ( + ClipYouEmbedIE, + EaglePlatformIE, +) from .ebaumsworld import EbaumsWorldIE from .ebay import EbayIE from .egghead import ( @@ -567,8 +598,8 @@ from .eporner import EpornerIE from .erocast import ErocastIE from .eroprofile import ( - EroProfileIE, EroProfileAlbumIE, + EroProfileIE, ) from .err import ERRJupiterIE from .ertgr import ( @@ -578,31 +609,33 @@ ) from .espn import ( ESPNIE, - WatchESPNIE, ESPNArticleIE, - FiveThirtyEightIE, ESPNCricInfoIE, + FiveThirtyEightIE, + WatchESPNIE, ) from .ettutv import EttuTvIE -from .europa import EuropaIE, EuroParlWebstreamIE +from .europa import ( + EuropaIE, + EuroParlWebstreamIE, +) from .europeantour import EuropeanTourIE from .eurosport import EurosportIE from .euscreen import EUScreenIE from .expressen import ExpressenIE from .eyedotv import EyedoTVIE from .facebook import ( + FacebookAdsIE, FacebookIE, FacebookPluginsVideoIE, FacebookRedirectURLIE, FacebookReelIE, - FacebookAdsIE, ) -from .fathom import FathomIE from .fancode import ( + FancodeLiveIE, FancodeVodIE, - FancodeLiveIE ) - +from .fathom import FathomIE from .faz import FazIE from .fc2 import ( FC2IE, @@ -612,8 +645,8 @@ from .fczenit import FczenitIE from .fifa import FifaIE from .filmon import ( - FilmOnIE, FilmOnChannelIE, + FilmOnIE, ) from .filmweb import FilmwebIE from .firsttv import FirstTVIE @@ -621,17 +654,17 @@ from .flextv import FlexTVIE from .flickr import FlickrIE from .floatplane import ( - FloatplaneIE, FloatplaneChannelIE, + FloatplaneIE, ) from .folketinget import FolketingetIE from .footyroom import FootyRoomIE from .formula1 import Formula1IE from .fourtube import ( FourTubeIE, - PornTubeIE, - PornerBrosIE, FuxIE, + PornerBrosIE, + PornTubeIE, ) from .fox import FOXIE from .fox9 import ( @@ -639,8 +672,8 @@ FOX9NewsIE, ) from .foxnews import ( - FoxNewsIE, FoxNewsArticleIE, + FoxNewsIE, FoxNewsVideoIE, ) from .foxsports import FoxSportsIE @@ -648,20 +681,20 @@ from .franceinter import FranceInterIE from .francetv import ( FranceTVIE, - FranceTVSiteIE, FranceTVInfoIE, + FranceTVSiteIE, ) from .freesound import FreesoundIE from .freespeech import FreespeechIE -from .frontendmasters import ( - FrontendMastersIE, - FrontendMastersLessonIE, - FrontendMastersCourseIE -) from .freetv import ( FreeTvIE, FreeTvMoviesIE, ) +from .frontendmasters import ( + FrontendMastersCourseIE, + FrontendMastersIE, + FrontendMastersLessonIE, +) from .fujitv import FujiTVFODPlus7IE from .funimation import ( FunimationIE, @@ -672,32 +705,37 @@ from .funker530 import Funker530IE from .fuyintv import FuyinTVIE from .gab import ( - GabTVIE, GabIE, + GabTVIE, ) from .gaia import GaiaIE from .gamejolt import ( - GameJoltIE, - GameJoltUserIE, + GameJoltCommunityIE, GameJoltGameIE, GameJoltGameSoundtrackIE, - GameJoltCommunityIE, + GameJoltIE, GameJoltSearchIE, + GameJoltUserIE, ) from .gamespot import GameSpotIE from .gamestar import GameStarIE from .gaskrank import GaskrankIE from .gazeta import GazetaIE +from .gbnews import GBNewsIE from .gdcvault import GDCVaultIE from .gedidigital import GediDigitalIE from .generic import GenericIE +from .genericembeds import ( + HTML5MediaEmbedIE, + QuotedHTMLIE, +) from .genius import ( GeniusIE, GeniusLyricsIE, ) from .getcourseru import ( + GetCourseRuIE, GetCourseRuPlayerIE, - GetCourseRuIE ) from .gettr import ( GettrIE, @@ -706,41 +744,45 @@ from .giantbomb import GiantBombIE from .glide import GlideIE from .globalplayer import ( + GlobalPlayerAudioEpisodeIE, + GlobalPlayerAudioIE, GlobalPlayerLiveIE, GlobalPlayerLivePlaylistIE, - GlobalPlayerAudioIE, - GlobalPlayerAudioEpisodeIE, - GlobalPlayerVideoIE + GlobalPlayerVideoIE, ) from .globo import ( - GloboIE, GloboArticleIE, + GloboIE, +) +from .glomex import ( + GlomexEmbedIE, + GlomexIE, ) from .gmanetwork import GMANetworkVideoIE from .go import GoIE -from .godtube import GodTubeIE from .godresource import GodResourceIE +from .godtube import GodTubeIE from .gofile import GofileIE from .golem import GolemIE from .goodgame import GoodGameIE from .googledrive import ( - GoogleDriveIE, GoogleDriveFolderIE, + GoogleDriveIE, ) from .googlepodcasts import ( - GooglePodcastsIE, GooglePodcastsFeedIE, + GooglePodcastsIE, ) from .googlesearch import GoogleSearchIE -from .gopro import GoProIE from .goplay import GoPlayIE +from .gopro import GoProIE from .goshgay import GoshgayIE from .gotostage import GoToStageIE from .gputechconf import GPUTechConfIE from .gronkh import ( - GronkhIE, GronkhFeedIE, - GronkhVodsIE + GronkhIE, + GronkhVodsIE, ) from .groupon import GrouponIE from .harpodeon import HarpodeonIE @@ -749,10 +791,10 @@ from .heise import HeiseIE from .hellporno import HellPornoIE from .hgtv import HGTVComShowIE -from .hketv import HKETVIE from .hidive import HiDiveIE from .historicfilms import HistoricFilmsIE from .hitrecord import HitRecordIE +from .hketv import HKETVIE from .hollywoodreporter import ( HollywoodReporterIE, HollywoodReporterPlaylistIE, @@ -761,8 +803,8 @@ from .hotnewhiphop import HotNewHipHopIE from .hotstar import ( HotStarIE, - HotStarPrefixIE, HotStarPlaylistIE, + HotStarPrefixIE, HotStarSeasonIE, HotStarSeriesIE, ) @@ -773,34 +815,30 @@ HRTiPlaylistIE, ) from .hse import ( - HSEShowIE, HSEProductIE, -) -from .genericembeds import ( - HTML5MediaEmbedIE, - QuotedHTMLIE, + HSEShowIE, ) from .huajiao import HuajiaoIE -from .huya import HuyaLiveIE from .huffpost import HuffPostIE from .hungama import ( + HungamaAlbumPlaylistIE, HungamaIE, HungamaSongIE, - HungamaAlbumPlaylistIE, ) +from .huya import HuyaLiveIE from .hypem import HypemIE from .hypergryph import MonsterSirenHypergryphMusicIE from .hytale import HytaleIE from .icareus import IcareusIE from .ichinanalive import ( - IchinanaLiveIE, IchinanaLiveClipIE, + IchinanaLiveIE, ) from .idolplus import IdolPlusIE from .ign import ( IGNIE, - IGNVideoIE, IGNArticleIE, + IGNVideoIE, ) from .iheart import ( IHeartRadioIE, @@ -810,12 +848,12 @@ from .iltalehti import IltalehtiIE from .imdb import ( ImdbIE, - ImdbListIE + ImdbListIE, ) from .imgur import ( - ImgurIE, ImgurAlbumIE, ImgurGalleryIE, + ImgurIE, ) from .ina import InaIE from .inc import IncIE @@ -824,20 +862,20 @@ from .instagram import ( InstagramIE, InstagramIOSIE, - InstagramUserIE, - InstagramTagIE, InstagramStoryIE, + InstagramTagIE, + InstagramUserIE, ) from .internazionale import InternazionaleIE from .internetvideoarchive import InternetVideoArchiveIE from .iprima import ( + IPrimaCNNIE, IPrimaIE, - IPrimaCNNIE ) from .iqiyi import ( - IqiyiIE, + IqAlbumIE, IqIE, - IqAlbumIE + IqiyiIE, ) from .islamchannel import ( IslamChannelIE, @@ -845,16 +883,16 @@ ) from .israelnationalnews import IsraelNationalNewsIE from .itprotv import ( + ITProTVCourseIE, ITProTVIE, - ITProTVCourseIE ) from .itv import ( - ITVIE, ITVBTCCIE, + ITVIE, ) from .ivi import ( + IviCompilationIE, IviIE, - IviCompilationIE ) from .ivideon import IvideonIE from .iwara import ( @@ -865,15 +903,15 @@ from .ixigua import IxiguaIE from .izlesene import IzleseneIE from .jamendo import ( - JamendoIE, JamendoAlbumIE, + JamendoIE, ) from .japandiet import ( + SangiinIE, + SangiinInstructionIE, ShugiinItvLiveIE, ShugiinItvLiveRoomIE, ShugiinItvVodIE, - SangiinInstructionIE, - SangiinIE, ) from .jeuxvideo import JeuxVideoIE from .jiocinema import ( @@ -881,13 +919,13 @@ JioCinemaSeriesIE, ) from .jiosaavn import ( - JioSaavnSongIE, JioSaavnAlbumIE, JioSaavnPlaylistIE, + JioSaavnSongIE, ) -from .jove import JoveIE from .joj import JojIE from .joqrag import JoqrAgIE +from .jove import JoveIE from .jstream import JStreamIE from .jtbc import ( JTBCIE, @@ -914,17 +952,17 @@ from .kommunetv import KommunetvIE from .kompas import KompasVideoIE from .koo import KooIE -from .kth import KTHIE from .krasview import KrasViewIE +from .kth import KTHIE from .ku6 import Ku6IE from .kukululive import KukuluLiveIE from .kuwo import ( - KuwoIE, KuwoAlbumIE, - KuwoChartIE, - KuwoSingerIE, KuwoCategoryIE, + KuwoChartIE, + KuwoIE, KuwoMvIE, + KuwoSingerIE, ) from .la7 import ( LA7IE, @@ -944,14 +982,14 @@ ) from .lci import LCIIE from .lcp import ( - LcpPlayIE, LcpIE, + LcpPlayIE, ) from .lecture2go import Lecture2GoIE from .lecturio import ( - LecturioIE, LecturioCourseIE, LecturioDeCourseIE, + LecturioIE, ) from .leeco import ( LeIE, @@ -968,22 +1006,22 @@ from .libraryofcongress import LibraryOfCongressIE from .libsyn import LibsynIE from .lifenews import ( - LifeNewsIE, LifeEmbedIE, + LifeNewsIE, ) from .likee import ( LikeeIE, - LikeeUserIE + LikeeUserIE, ) from .limelight import ( - LimelightMediaIE, LimelightChannelIE, LimelightChannelListIE, + LimelightMediaIE, ) from .linkedin import ( LinkedInIE, - LinkedInLearningIE, LinkedInLearningCourseIE, + LinkedInLearningIE, ) from .liputan6 import Liputan6IE from .listennotes import ListenNotesIE @@ -1000,25 +1038,23 @@ LnkIE, ) from .loom import ( - LoomIE, LoomFolderIE, + LoomIE, ) from .lovehomeporn import LoveHomePornIE from .lrt import ( LRTVODIE, - LRTStreamIE + LRTStreamIE, ) from .lsm import ( LSMLREmbedIE, LSMLTVEmbedIE, - LSMReplayIE -) -from .lumni import ( - LumniIE + LSMReplayIE, ) +from .lumni import LumniIE from .lynda import ( + LyndaCourseIE, LyndaIE, - LyndaCourseIE ) from .maariv import MaarivIE from .magellantv import MagellanTVIE @@ -1030,13 +1066,13 @@ ) from .mainstreaming import MainStreamingIE from .mangomolo import ( - MangomoloVideoIE, MangomoloLiveIE, + MangomoloVideoIE, ) from .manoto import ( ManotoTVIE, - ManotoTVShowIE, ManotoTVLiveIE, + ManotoTVShowIE, ) from .manyvids import ManyVidsIE from .maoritv import MaoriTVIE @@ -1052,13 +1088,14 @@ from .medaltv import MedalTVIE from .mediaite import MediaiteIE from .mediaklikk import MediaKlikkIE +from .medialaan import MedialaanIE from .mediaset import ( MediasetIE, MediasetShowIE, ) from .mediasite import ( - MediasiteIE, MediasiteCatalogIE, + MediasiteIE, MediasiteNamedCatalogIE, ) from .mediastream import ( @@ -1068,26 +1105,30 @@ from .mediaworksnz import MediaWorksNZVODIE from .medici import MediciIE from .megaphone import MegaphoneIE +from .megatvcom import ( + MegaTVComEmbedIE, + MegaTVComIE, +) from .meipai import MeipaiIE from .melonvod import MelonVODIE from .metacritic import MetacriticIE from .mgtv import MGTVIE +from .microsoftembed import MicrosoftEmbedIE from .microsoftstream import MicrosoftStreamIE from .microsoftvirtualacademy import ( - MicrosoftVirtualAcademyIE, MicrosoftVirtualAcademyCourseIE, + MicrosoftVirtualAcademyIE, ) -from .microsoftembed import MicrosoftEmbedIE from .mildom import ( - MildomIE, - MildomVodIE, MildomClipIE, + MildomIE, MildomUserVodIE, + MildomVodIE, ) from .minds import ( - MindsIE, MindsChannelIE, MindsGroupIE, + MindsIE, ) from .minoto import MinotoIE from .mirrativ import ( @@ -1095,31 +1136,34 @@ MirrativUserIE, ) from .mirrorcouk import MirrorCoUKIE -from .mit import TechTVMITIE, OCWMITIE +from .mit import ( + OCWMITIE, + TechTVMITIE, +) from .mitele import MiTeleIE from .mixch import ( - MixchIE, MixchArchiveIE, + MixchIE, ) from .mixcloud import ( MixcloudIE, - MixcloudUserIE, MixcloudPlaylistIE, + MixcloudUserIE, ) from .mlb import ( MLBIE, - MLBVideoIE, MLBTVIE, MLBArticleIE, + MLBVideoIE, ) from .mlssoccer import MLSSoccerIE from .mocha import MochaVideoIE from .mojvideo import MojvideoIE from .monstercat import MonstercatIE from .motherless import ( - MotherlessIE, - MotherlessGroupIE, MotherlessGalleryIE, + MotherlessGroupIE, + MotherlessIE, MotherlessUploaderIE, ) from .motorsport import MotorsportIE @@ -1129,23 +1173,26 @@ from .movingimage import MovingImageIE from .msn import MSNIE from .mtv import ( - MTVIE, - MTVVideoIE, - MTVServicesEmbeddedIE, MTVDEIE, - MTVJapanIE, + MTVIE, MTVItaliaIE, MTVItaliaProgrammaIE, + MTVJapanIE, + MTVServicesEmbeddedIE, + MTVVideoIE, ) from .muenchentv import MuenchenTVIE -from .murrtube import MurrtubeIE, MurrtubeUserIE +from .murrtube import ( + MurrtubeIE, + MurrtubeUserIE, +) from .museai import MuseAIIE from .musescore import MuseScoreIE from .musicdex import ( - MusicdexSongIE, MusicdexAlbumIE, MusicdexArtistIE, MusicdexPlaylistIE, + MusicdexSongIE, ) from .mx3 import ( Mx3IE, @@ -1156,7 +1203,10 @@ MxplayerIE, MxplayerShowIE, ) -from .myspace import MySpaceIE, MySpaceAlbumIE +from .myspace import ( + MySpaceAlbumIE, + MySpaceIE, +) from .myspass import MySpassIE from .myvideoge import MyVideoGeIE from .myvidster import MyVidsterIE @@ -1170,8 +1220,8 @@ NateProgramIE, ) from .nationalgeographic import ( - NationalGeographicVideoIE, NationalGeographicTVIE, + NationalGeographicVideoIE, ) from .naver import ( NaverIE, @@ -1179,12 +1229,12 @@ NaverNowIE, ) from .nba import ( - NBAWatchEmbedIE, - NBAWatchIE, - NBAWatchCollectionIE, - NBAEmbedIE, NBAIE, NBAChannelIE, + NBAEmbedIE, + NBAWatchCollectionIE, + NBAWatchEmbedIE, + NBAWatchIE, ) from .nbc import ( NBCIE, @@ -1198,35 +1248,35 @@ ) from .ndr import ( NDRIE, - NJoyIE, NDREmbedBaseIE, NDREmbedIE, NJoyEmbedIE, + NJoyIE, ) from .ndtv import NDTVIE from .nebula import ( - NebulaIE, + NebulaChannelIE, NebulaClassIE, + NebulaIE, NebulaSubscriptionsIE, - NebulaChannelIE, ) from .nekohacker import NekoHackerIE from .nerdcubed import NerdCubedFeedIE -from .netzkino import NetzkinoIE from .neteasemusic import ( - NetEaseMusicIE, NetEaseMusicAlbumIE, - NetEaseMusicSingerIE, + NetEaseMusicDjRadioIE, + NetEaseMusicIE, NetEaseMusicListIE, NetEaseMusicMvIE, NetEaseMusicProgramIE, - NetEaseMusicDjRadioIE, + NetEaseMusicSingerIE, ) from .netverse import ( NetverseIE, NetversePlaylistIE, NetverseSearchIE, ) +from .netzkino import NetzkinoIE from .newgrounds import ( NewgroundsIE, NewgroundsPlaylistIE, @@ -1235,14 +1285,14 @@ from .newspicks import NewsPicksIE from .newsy import NewsyIE from .nextmedia import ( - NextMediaIE, - NextMediaActionNewsIE, AppleDailyIE, + NextMediaActionNewsIE, + NextMediaIE, NextTVIE, ) from .nexx import ( - NexxIE, NexxEmbedIE, + NexxIE, ) from .nfb import ( NFBIE, @@ -1256,43 +1306,43 @@ NFLPlusReplayIE, ) from .nhk import ( - NhkVodIE, - NhkVodProgramIE, NhkForSchoolBangumiIE, - NhkForSchoolSubjectIE, NhkForSchoolProgramListIE, + NhkForSchoolSubjectIE, NhkRadioNewsPageIE, NhkRadiruIE, NhkRadiruLiveIE, + NhkVodIE, + NhkVodProgramIE, ) from .nhl import NHLIE from .nick import ( - NickIE, NickBrIE, NickDeIE, + NickIE, NickRuIE, ) from .niconico import ( + NiconicoHistoryIE, NiconicoIE, + NiconicoLiveIE, NiconicoPlaylistIE, - NiconicoUserIE, NiconicoSeriesIE, - NiconicoHistoryIE, + NiconicoUserIE, NicovideoSearchDateIE, NicovideoSearchIE, NicovideoSearchURLIE, NicovideoTagURLIE, - NiconicoLiveIE, +) +from .niconicochannelplus import ( + NiconicoChannelPlusChannelLivesIE, + NiconicoChannelPlusChannelVideosIE, + NiconicoChannelPlusIE, ) from .ninaprotocol import NinaProtocolIE from .ninecninemedia import ( - NineCNineMediaIE, CPTwentyFourIE, -) -from .niconicochannelplus import ( - NiconicoChannelPlusIE, - NiconicoChannelPlusChannelVideosIE, - NiconicoChannelPlusChannelLivesIE, + NineCNineMediaIE, ) from .ninegag import NineGagIE from .ninenews import NineNewsIE @@ -1317,24 +1367,24 @@ ) from .noz import NozIE from .npo import ( - AndereTijdenIE, NPOIE, + VPROIE, + WNLIE, + AndereTijdenIE, + HetKlokhuisIE, NPOLiveIE, - NPORadioIE, NPORadioFragmentIE, + NPORadioIE, SchoolTVIE, - HetKlokhuisIE, - VPROIE, - WNLIE, ) from .npr import NprIE from .nrk import ( NRKIE, + NRKTVIE, NRKPlaylistIE, + NRKRadioPodkastIE, NRKSkoleIE, - NRKTVIE, NRKTVDirekteIE, - NRKRadioPodkastIE, NRKTVEpisodeIE, NRKTVEpisodesIE, NRKTVSeasonIE, @@ -1346,18 +1396,18 @@ from .ntvde import NTVDeIE from .ntvru import NTVRuIE from .nubilesporn import NubilesPornIE -from .nytimes import ( - NYTimesIE, - NYTimesArticleIE, - NYTimesCookingIE, - NYTimesCookingRecipeIE, -) from .nuum import ( NuumLiveIE, - NuumTabIE, NuumMediaIE, + NuumTabIE, ) from .nuvid import NuvidIE +from .nytimes import ( + NYTimesArticleIE, + NYTimesCookingIE, + NYTimesCookingRecipeIE, + NYTimesIE, +) from .nzherald import NZHeraldIE from .nzonscreen import NZOnScreenIE from .nzz import NZZIE @@ -1365,7 +1415,7 @@ from .odnoklassniki import OdnoklassnikiIE from .oftv import ( OfTVIE, - OfTVPlaylistIE + OfTVPlaylistIE, ) from .oktoberfesttv import OktoberfestTVIE from .olympics import OlympicsReplayIE @@ -1378,8 +1428,8 @@ from .onenewsnz import OneNewsNZIE from .oneplace import OnePlacePodcastIE from .onet import ( - OnetIE, OnetChannelIE, + OnetIE, OnetMVPIE, OnetPlIE, ) @@ -1389,33 +1439,33 @@ OpencastPlaylistIE, ) from .openrec import ( - OpenRecIE, OpenRecCaptureIE, + OpenRecIE, OpenRecMovieIE, ) from .ora import OraTVIE from .orf import ( - ORFFM4StoryIE, + ORFIPTVIE, ORFONIE, - ORFRadioIE, + ORFFM4StoryIE, ORFPodcastIE, - ORFIPTVIE, + ORFRadioIE, ) from .outsidetv import OutsideTVIE from .owncloud import OwnCloudIE from .packtpub import ( - PacktPubIE, PacktPubCourseIE, + PacktPubIE, ) from .palcomp3 import ( - PalcoMP3IE, PalcoMP3ArtistIE, + PalcoMP3IE, PalcoMP3VideoIE, ) from .panopto import ( PanoptoIE, PanoptoListIE, - PanoptoPlaylistIE + PanoptoPlaylistIE, ) from .paramountplus import ( ParamountPlusIE, @@ -1424,12 +1474,18 @@ from .parler import ParlerIE from .parlview import ParlviewIE from .patreon import ( + PatreonCampaignIE, PatreonIE, - PatreonCampaignIE ) -from .pbs import PBSIE, PBSKidsIE +from .pbs import ( + PBSIE, + PBSKidsIE, +) from .pearvideo import PearVideoIE -from .peekvids import PeekVidsIE, PlayVidsIE +from .peekvids import ( + PeekVidsIE, + PlayVidsIE, +) from .peertube import ( PeerTubeIE, PeerTubePlaylistIE, @@ -1437,7 +1493,7 @@ from .peertv import PeerTVIE from .peloton import ( PelotonIE, - PelotonLiveIE + PelotonLiveIE, ) from .performgroup import PerformGroupIE from .periscope import ( @@ -1457,8 +1513,8 @@ from .piksel import PikselIE from .pinkbike import PinkbikeIE from .pinterest import ( - PinterestIE, PinterestCollectionIE, + PinterestIE, ) from .pixivsketch import ( PixivSketchIE, @@ -1467,19 +1523,22 @@ from .pladform import PladformIE from .planetmarathi import PlanetMarathiIE from .platzi import ( - PlatziIE, PlatziCourseIE, + PlatziIE, ) from .playplustv import PlayPlusTVIE from .playsuisse import PlaySuisseIE from .playtvak import PlaytvakIE from .playwire import PlaywireIE -from .plutotv import PlutoTVIE from .pluralsight import ( - PluralsightIE, PluralsightCourseIE, + PluralsightIE, +) +from .plutotv import PlutoTVIE +from .podbayfm import ( + PodbayFMChannelIE, + PodbayFMIE, ) -from .podbayfm import PodbayFMIE, PodbayFMChannelIE from .podchaser import PodchaserIE from .podomatic import PodomaticIE from .pokemon import ( @@ -1487,15 +1546,15 @@ PokemonWatchIE, ) from .pokergo import ( - PokerGoIE, PokerGoCollectionIE, + PokerGoIE, ) from .polsatgo import PolsatGoIE from .polskieradio import ( - PolskieRadioIE, - PolskieRadioLegacyIE, PolskieRadioAuditionIE, PolskieRadioCategoryIE, + PolskieRadioIE, + PolskieRadioLegacyIE, PolskieRadioPlayerIE, PolskieRadioPodcastIE, PolskieRadioPodcastListIE, @@ -1506,57 +1565,62 @@ from .pornflip import PornFlipIE from .pornhub import ( PornHubIE, - PornHubUserIE, - PornHubPlaylistIE, PornHubPagedVideoListIE, + PornHubPlaylistIE, + PornHubUserIE, PornHubUserVideosUploadIE, ) from .pornotube import PornotubeIE from .pornovoisines import PornoVoisinesIE from .pornoxo import PornoXOIE -from .puhutv import ( - PuhuTVIE, - PuhuTVSerieIE, -) from .pr0gramm import Pr0grammIE -from .prankcast import PrankCastIE, PrankCastPostIE +from .prankcast import ( + PrankCastIE, + PrankCastPostIE, +) from .premiershiprugby import PremiershipRugbyIE from .presstv import PressTVIE from .projectveritas import ProjectVeritasIE from .prosiebensat1 import ProSiebenSat1IE from .prx import ( - PRXStoryIE, - PRXSeriesIE, PRXAccountIE, + PRXSeriesIE, + PRXSeriesSearchIE, PRXStoriesSearchIE, - PRXSeriesSearchIE + PRXStoryIE, +) +from .puhutv import ( + PuhuTVIE, + PuhuTVSerieIE, ) from .puls4 import Puls4IE from .pyvideo import PyvideoIE from .qdance import QDanceIE from .qingting import QingTingIE from .qqmusic import ( + QQMusicAlbumIE, QQMusicIE, + QQMusicPlaylistIE, QQMusicSingerIE, - QQMusicAlbumIE, QQMusicToplistIE, - QQMusicPlaylistIE, ) from .r7 import ( R7IE, R7ArticleIE, ) -from .radiko import RadikoIE, RadikoRadioIE +from .radiko import ( + RadikoIE, + RadikoRadioIE, +) from .radiocanada import ( - RadioCanadaIE, RadioCanadaAudioVideoIE, + RadioCanadaIE, ) from .radiocomercial import ( RadioComercialIE, RadioComercialPlaylistIE, ) from .radiode import RadioDeIE -from .radiojavan import RadioJavanIE from .radiofrance import ( FranceCultureIE, RadioFranceIE, @@ -1565,35 +1629,36 @@ RadioFranceProfileIE, RadioFranceProgramScheduleIE, ) -from .radiozet import RadioZetPodcastIE +from .radiojavan import RadioJavanIE from .radiokapital import ( RadioKapitalIE, RadioKapitalShowIE, ) +from .radiozet import RadioZetPodcastIE from .radlive import ( - RadLiveIE, RadLiveChannelIE, + RadLiveIE, RadLiveSeasonIE, ) from .rai import ( - RaiIE, RaiCulturaIE, + RaiIE, + RaiNewsIE, RaiPlayIE, RaiPlayLiveIE, RaiPlayPlaylistIE, RaiPlaySoundIE, RaiPlaySoundLiveIE, RaiPlaySoundPlaylistIE, - RaiNewsIE, RaiSudtirolIE, ) from .raywenderlich import ( - RayWenderlichIE, RayWenderlichCourseIE, + RayWenderlichIE, ) from .rbgtum import ( - RbgTumIE, RbgTumCourseIE, + RbgTumIE, RbgTumNewCourseIE, ) from .rcs import ( @@ -1607,12 +1672,15 @@ RCTIPlusTVIE, ) from .rds import RDSIE -from .redbee import ParliamentLiveUKIE, RTBFIE +from .redbee import ( + RTBFIE, + ParliamentLiveUKIE, +) from .redbulltv import ( - RedBullTVIE, RedBullEmbedIE, - RedBullTVRrnContentIE, RedBullIE, + RedBullTVIE, + RedBullTVRrnContentIE, ) from .reddit import RedditIE from .redge import RedCDNLivxIE @@ -1632,107 +1700,100 @@ from .rheinmaintv import RheinMainTVIE from .ridehome import RideHomeIE from .rinsefm import ( - RinseFMIE, RinseFMArtistPlaylistIE, + RinseFMIE, ) from .rmcdecouverte import RMCDecouverteIE from .rockstargames import RockstarGamesIE from .rokfin import ( - RokfinIE, - RokfinStackIE, RokfinChannelIE, + RokfinIE, RokfinSearchIE, + RokfinStackIE, +) +from .roosterteeth import ( + RoosterTeethIE, + RoosterTeethSeriesIE, ) -from .roosterteeth import RoosterTeethIE, RoosterTeethSeriesIE from .rottentomatoes import RottenTomatoesIE from .rozhlas import ( + MujRozhlasIE, RozhlasIE, RozhlasVltavaIE, - MujRozhlasIE, ) -from .rte import RteIE, RteRadioIE +from .rte import ( + RteIE, + RteRadioIE, +) +from .rtl2 import RTL2IE from .rtlnl import ( - RtlNlIE, - RTLLuTeleVODIE, RTLLuArticleIE, RTLLuLiveIE, RTLLuRadioIE, + RTLLuTeleVODIE, + RtlNlIE, ) -from .rtl2 import RTL2IE from .rtnews import ( - RTNewsIE, RTDocumentryIE, RTDocumentryPlaylistIE, + RTNewsIE, RuptlyIE, ) from .rtp import RTPIE from .rtrfm import RTRFMIE from .rts import RTSIE from .rtvcplay import ( - RTVCPlayIE, - RTVCPlayEmbedIE, RTVCKalturaIE, + RTVCPlayEmbedIE, + RTVCPlayIE, ) from .rtve import ( RTVEALaCartaIE, RTVEAudioIE, - RTVELiveIE, RTVEInfantilIE, + RTVELiveIE, RTVETelevisionIE, ) from .rtvs import RTVSIE from .rtvslo import RTVSLOIE +from .rudovideo import RudoVideoIE from .rule34video import Rule34VideoIE from .rumble import ( + RumbleChannelIE, RumbleEmbedIE, RumbleIE, - RumbleChannelIE, -) -from .rudovideo import RudoVideoIE -from .rutube import ( - RutubeIE, - RutubeChannelIE, - RutubeEmbedIE, - RutubeMovieIE, - RutubePersonIE, - RutubePlaylistIE, - RutubeTagsIE, -) -from .glomex import ( - GlomexIE, - GlomexEmbedIE, -) -from .megatvcom import ( - MegaTVComIE, - MegaTVComEmbedIE, -) -from .antenna import ( - AntennaGrWatchIE, - Ant1NewsGrArticleIE, - Ant1NewsGrEmbedIE, +) +from .rutube import ( + RutubeChannelIE, + RutubeEmbedIE, + RutubeIE, + RutubeMovieIE, + RutubePersonIE, + RutubePlaylistIE, + RutubeTagsIE, ) from .rutv import RUTVIE from .ruutu import RuutuIE from .ruv import ( RuvIE, - RuvSpilaIE + RuvSpilaIE, ) from .s4c import ( S4CIE, - S4CSeriesIE + S4CSeriesIE, ) from .safari import ( - SafariIE, SafariApiIE, SafariCourseIE, + SafariIE, ) from .saitosan import SaitosanIE from .samplefocus import SampleFocusIE from .sapo import SapoIE from .sbs import SBSIE from .sbscokr import ( - SBSCoKrIE, SBSCoKrAllvodProgramIE, + SBSCoKrIE, SBSCoKrProgramsVodIE, ) from .screen9 import Screen9IE @@ -1740,24 +1801,27 @@ from .screencastify import ScreencastifyIE from .screencastomatic import ScreencastOMaticIE from .scrippsnetworks import ( - ScrippsNetworksWatchIE, ScrippsNetworksIE, + ScrippsNetworksWatchIE, ) +from .scrolller import ScrolllerIE from .scte import ( SCTEIE, SCTECourseIE, ) -from .scrolller import ScrolllerIE from .sejmpl import SejmIE from .senalcolombia import SenalColombiaLiveIE -from .senategov import SenateISVPIE, SenateGovIE +from .senategov import ( + SenateGovIE, + SenateISVPIE, +) from .sendtonews import SendtoNewsIE from .servus import ServusIE from .sevenplus import SevenPlusIE from .sexu import SexuIE from .seznamzpravy import ( - SeznamZpravyIE, SeznamZpravyArticleIE, + SeznamZpravyIE, ) from .shahid import ( ShahidIE, @@ -1765,38 +1829,38 @@ ) from .sharepoint import SharePointIE from .sharevideos import ShareVideosEmbedIE -from .sibnet import SibnetEmbedIE from .shemaroome import ShemarooMeIE from .showroomlive import ShowRoomLiveIE +from .sibnet import SibnetEmbedIE from .simplecast import ( - SimplecastIE, SimplecastEpisodeIE, + SimplecastIE, SimplecastPodcastIE, ) from .sina import SinaIE from .sixplay import SixPlayIE from .skeb import SkebIE +from .sky import ( + SkyNewsIE, + SkyNewsStoryIE, + SkySportsIE, + SkySportsNewsIE, +) from .skyit import ( + CieloTVItIE, + SkyItArteIE, + SkyItIE, SkyItPlayerIE, SkyItVideoIE, SkyItVideoLiveIE, - SkyItIE, - SkyItArteIE, - CieloTVItIE, TV8ItIE, ) from .skylinewebcams import SkylineWebcamsIE from .skynewsarabia import ( - SkyNewsArabiaIE, SkyNewsArabiaArticleIE, + SkyNewsArabiaIE, ) from .skynewsau import SkyNewsAUIE -from .sky import ( - SkyNewsIE, - SkyNewsStoryIE, - SkySportsIE, - SkySportsNewsIE, -) from .slideshare import SlideshareIE from .slideslive import SlidesLiveIE from .slutload import SlutloadIE @@ -1813,29 +1877,29 @@ from .soundcloud import ( SoundcloudEmbedIE, SoundcloudIE, - SoundcloudSetIE, + SoundcloudPlaylistIE, SoundcloudRelatedIE, + SoundcloudSearchIE, + SoundcloudSetIE, + SoundcloudTrackStationIE, SoundcloudUserIE, SoundcloudUserPermalinkIE, - SoundcloudTrackStationIE, - SoundcloudPlaylistIE, - SoundcloudSearchIE, ) from .soundgasm import ( SoundgasmIE, - SoundgasmProfileIE + SoundgasmProfileIE, ) from .southpark import ( - SouthParkIE, SouthParkDeIE, SouthParkDkIE, SouthParkEsIE, + SouthParkIE, SouthParkLatIE, - SouthParkNlIE + SouthParkNlIE, ) from .sovietscloset import ( SovietsClosetIE, - SovietsClosetPlaylistIE + SovietsClosetPlaylistIE, ) from .spankbang import ( SpankBangIE, @@ -1846,12 +1910,6 @@ BellatorIE, ParamountNetworkIE, ) -from .stageplus import StagePlusVODConcertIE -from .startrek import StarTrekIE -from .stitcher import ( - StitcherIE, - StitcherShowIE, -) from .sport5 import Sport5IE from .sportbox import SportBoxIE from .sportdeutschland import SportDeutschlandIE @@ -1875,19 +1933,25 @@ from .stacommu import ( StacommuLiveIE, StacommuVODIE, - TheaterComplexTownVODIE, TheaterComplexTownPPVIE, + TheaterComplexTownVODIE, ) +from .stageplus import StagePlusVODConcertIE from .stanfordoc import StanfordOpenClassroomIE +from .startrek import StarTrekIE from .startv import StarTVIE from .steam import ( - SteamIE, SteamCommunityBroadcastIE, + SteamIE, +) +from .stitcher import ( + StitcherIE, + StitcherShowIE, ) from .storyfire import ( StoryFireIE, - StoryFireUserIE, StoryFireSeriesIE, + StoryFireUserIE, ) from .streamable import StreamableIE from .streamcz import StreamCZIE @@ -1908,26 +1972,26 @@ SVTSeriesIE, ) from .swearnet import SwearnetEpisodeIE -from .syvdk import SYVDKIE from .syfy import SyfyIE +from .syvdk import SYVDKIE from .sztvhu import SztvHuIE from .tagesschau import TagesschauIE from .taptap import ( - TapTapMomentIE, TapTapAppIE, TapTapAppIntlIE, + TapTapMomentIE, TapTapPostIntlIE, ) from .tass import TassIE from .tbs import TBSIE from .tbsjp import ( TBSJPEpisodeIE, - TBSJPProgramIE, TBSJPPlaylistIE, + TBSJPProgramIE, ) from .teachable import ( - TeachableIE, TeachableCourseIE, + TeachableIE, ) from .teachertube import ( TeacherTubeIE, @@ -1935,8 +1999,8 @@ ) from .teachingchannel import TeachingChannelIE from .teamcoco import ( - TeamcocoIE, ConanClassicIE, + TeamcocoIE, ) from .teamtreehouse import TeamTreeHouseIE from .ted import ( @@ -1955,15 +2019,18 @@ from .telemb import TeleMBIE from .telemundo import TelemundoIE from .telequebec import ( - TeleQuebecIE, - TeleQuebecSquatIE, TeleQuebecEmissionIE, + TeleQuebecIE, TeleQuebecLiveIE, + TeleQuebecSquatIE, TeleQuebecVideoIE, ) from .teletask import TeleTaskIE from .telewebion import TelewebionIE -from .tempo import TempoIE, IVXPlayerIE +from .tempo import ( + IVXPlayerIE, + TempoIE, +) from .tencent import ( IflixEpisodeIE, IflixSeriesIE, @@ -1987,8 +2054,8 @@ from .theholetv import TheHoleTvIE from .theintercept import TheInterceptIE from .theplatform import ( - ThePlatformIE, ThePlatformFeedIE, + ThePlatformIE, ) from .thestar import TheStarIE from .thesun import TheSunIE @@ -2000,50 +2067,52 @@ ThisVidMemberIE, ThisVidPlaylistIE, ) +from .threeqsdn import ThreeQSDNIE from .threespeak import ( ThreeSpeakIE, ThreeSpeakUserIE, ) -from .threeqsdn import ThreeQSDNIE from .tiktok import ( + DouyinIE, + TikTokCollectionIE, + TikTokEffectIE, TikTokIE, - TikTokUserIE, + TikTokLiveIE, TikTokSoundIE, - TikTokEffectIE, TikTokTagIE, + TikTokUserIE, TikTokVMIE, - TikTokLiveIE, - DouyinIE, ) from .tmz import TMZIE from .tnaflix import ( - TNAFlixNetworkEmbedIE, - TNAFlixIE, EMPFlixIE, MovieFapIE, + TNAFlixIE, + TNAFlixNetworkEmbedIE, ) from .toggle import ( - ToggleIE, MeWatchIE, + ToggleIE, ) -from .toggo import ( - ToggoIE, -) +from .toggo import ToggoIE from .tonline import TOnlineIE from .toongoggles import ToonGogglesIE from .toutv import TouTvIE -from .toypics import ToypicsUserIE, ToypicsIE +from .toypics import ( + ToypicsIE, + ToypicsUserIE, +) from .traileraddict import TrailerAddictIE from .triller import ( TrillerIE, - TrillerUserIE, TrillerShortIE, + TrillerUserIE, ) from .trovo import ( + TrovoChannelClipIE, + TrovoChannelVodIE, TrovoIE, TrovoVodIE, - TrovoChannelVodIE, - TrovoChannelClipIE, ) from .trtcocuk import TrtCocukVideoIE from .trtworld import TrtWorldIE @@ -2052,26 +2121,26 @@ from .truth import TruthIE from .trutv import TruTVIE from .tube8 import Tube8IE -from .tubetugraz import TubeTuGrazIE, TubeTuGrazSeriesIE +from .tubetugraz import ( + TubeTuGrazIE, + TubeTuGrazSeriesIE, +) from .tubitv import ( TubiTvIE, TubiTvShowIE, ) from .tumblr import TumblrIE from .tunein import ( - TuneInStationIE, - TuneInPodcastIE, TuneInPodcastEpisodeIE, + TuneInPodcastIE, TuneInShortenerIE, + TuneInStationIE, ) from .tv2 import ( TV2IE, - TV2ArticleIE, KatsomoIE, MTVUutisetArticleIE, -) -from .tv24ua import ( - TV24UAVideoIE, + TV2ArticleIE, ) from .tv2dk import ( TV2DKIE, @@ -2084,16 +2153,17 @@ from .tv4 import TV4IE from .tv5mondeplus import TV5MondePlusIE from .tv5unis import ( - TV5UnisVideoIE, TV5UnisIE, + TV5UnisVideoIE, ) +from .tv24ua import TV24UAVideoIE from .tva import ( TVAIE, QubIE, ) from .tvanouvelles import ( - TVANouvellesIE, TVANouvellesArticleIE, + TVANouvellesIE, ) from .tvc import ( TVCIE, @@ -2106,19 +2176,19 @@ from .tvn24 import TVN24IE from .tvnoe import TVNoeIE from .tvopengr import ( - TVOpenGrWatchIE, TVOpenGrEmbedIE, + TVOpenGrWatchIE, ) from .tvp import ( - TVPEmbedIE, TVPIE, + TVPEmbedIE, TVPStreamIE, TVPVODSeriesIE, TVPVODVideoIE, ) from .tvplay import ( - TVPlayIE, TVPlayHomeIE, + TVPlayIE, ) from .tvplayer import TVPlayerIE from .tweakers import TweakersIE @@ -2130,29 +2200,29 @@ TwitCastingUserIE, ) from .twitch import ( - TwitchVodIE, + TwitchClipsIE, TwitchCollectionIE, - TwitchVideosIE, + TwitchStreamIE, TwitchVideosClipsIE, TwitchVideosCollectionsIE, - TwitchStreamIE, - TwitchClipsIE, + TwitchVideosIE, + TwitchVodIE, ) from .twitter import ( - TwitterCardIE, - TwitterIE, TwitterAmplifyIE, TwitterBroadcastIE, - TwitterSpacesIE, + TwitterCardIE, + TwitterIE, TwitterShortenerIE, + TwitterSpacesIE, ) from .txxx import ( - TxxxIE, PornTopIE, + TxxxIE, ) from .udemy import ( + UdemyCourseIE, UdemyIE, - UdemyCourseIE ) from .udn import UDNEmbedIE from .ufctv import ( @@ -2161,16 +2231,13 @@ ) from .ukcolumn import UkColumnIE from .uktvplay import UKTVPlayIE -from .digiteka import DigitekaIE -from .dlive import ( - DLiveVODIE, - DLiveStreamIE, -) -from .drooble import DroobleIE from .umg import UMGDeIE from .unistra import UnistraIE from .unity import UnityIE -from .unsupported import KnownDRMIE, KnownPiracyIE +from .unsupported import ( + KnownDRMIE, + KnownPiracyIE, +) from .uol import UOLIE from .uplynk import ( UplynkIE, @@ -2180,10 +2247,13 @@ from .urplay import URPlayIE from .usanetwork import USANetworkIE from .usatoday import USATodayIE -from .ustream import UstreamIE, UstreamChannelIE +from .ustream import ( + UstreamChannelIE, + UstreamIE, +) from .ustudio import ( - UstudioIE, UstudioEmbedIE, + UstudioIE, ) from .utreon import UtreonIE from .varzesh3 import Varzesh3IE @@ -2191,7 +2261,7 @@ from .veo import VeoIE from .veoh import ( VeohIE, - VeohUserIE + VeohUserIE, ) from .vesti import VestiIE from .vevo import ( @@ -2199,14 +2269,14 @@ VevoPlaylistIE, ) from .vgtv import ( + VGTVIE, BTArticleIE, BTVestlendingenIE, - VGTVIE, ) from .vh1 import VH1IE from .vice import ( - ViceIE, ViceArticleIE, + ViceIE, ViceShowIE, ) from .viddler import ViddlerIE @@ -2218,42 +2288,46 @@ from .videodetective import VideoDetectiveIE from .videofyme import VideofyMeIE from .videoken import ( + VideoKenCategoryIE, VideoKenIE, VideoKenPlayerIE, VideoKenPlaylistIE, - VideoKenCategoryIE, VideoKenTopicIE, ) from .videomore import ( VideomoreIE, - VideomoreVideoIE, VideomoreSeasonIE, + VideomoreVideoIE, ) from .videopress import VideoPressIE from .vidio import ( VidioIE, + VidioLiveIE, VidioPremierIE, - VidioLiveIE ) from .vidlii import VidLiiIE from .vidly import VidlyIE from .viewlift import ( - ViewLiftIE, ViewLiftEmbedIE, + ViewLiftIE, ) from .viidea import ViideaIE +from .viki import ( + VikiChannelIE, + VikiIE, +) from .vimeo import ( - VimeoIE, + VHXEmbedIE, VimeoAlbumIE, VimeoChannelIE, VimeoGroupsIE, + VimeoIE, VimeoLikesIE, VimeoOndemandIE, VimeoProIE, VimeoReviewIE, VimeoUserIE, VimeoWatchLaterIE, - VHXEmbedIE, ) from .vimm import ( VimmIE, @@ -2263,46 +2337,41 @@ VineIE, VineUserIE, ) -from .viki import ( - VikiIE, - VikiChannelIE, -) from .viously import ViouslyIE from .viqeo import ViqeoIE from .viu import ( ViuIE, - ViuPlaylistIE, ViuOTTIE, ViuOTTIndonesiaIE, + ViuPlaylistIE, ) from .vk import ( VKIE, - VKUserVideosIE, - VKWallPostIE, VKPlayIE, VKPlayLiveIE, + VKUserVideosIE, + VKWallPostIE, ) from .vocaroo import VocarooIE from .vodpl import VODPlIE from .vodplatform import VODPlatformIE from .voicy import ( - VoicyIE, VoicyChannelIE, + VoicyIE, ) from .volejtv import VolejTVIE from .voxmedia import ( - VoxMediaVolumeIE, VoxMediaIE, + VoxMediaVolumeIE, ) from .vrt import ( VRTIE, - VrtNUIE, - KetnetIE, DagelijkseKostIE, + KetnetIE, Radio1BeIE, + VrtNUIE, ) from .vtm import VTMIE -from .medialaan import MedialaanIE from .vuclip import VuClipIE from .vvvvid import ( VVVVIDIE, @@ -2310,20 +2379,20 @@ ) from .walla import WallaIE from .washingtonpost import ( - WashingtonPostIE, WashingtonPostArticleIE, + WashingtonPostIE, ) from .wat import WatIE from .wdr import ( WDRIE, - WDRPageIE, WDRElefantIE, WDRMobileIE, + WDRPageIE, ) from .webcamerapl import WebcameraplIE from .webcaster import ( - WebcasterIE, WebcasterFeedIE, + WebcasterIE, ) from .webofstories import ( WebOfStoriesIE, @@ -2331,42 +2400,42 @@ ) from .weibo import ( WeiboIE, - WeiboVideoIE, WeiboUserIE, + WeiboVideoIE, ) from .weiqitv import WeiqiTVIE from .weverse import ( WeverseIE, - WeverseMediaIE, - WeverseMomentIE, + WeverseLiveIE, WeverseLiveTabIE, + WeverseMediaIE, WeverseMediaTabIE, - WeverseLiveIE, + WeverseMomentIE, ) from .wevidi import WeVidiIE from .weyyak import WeyyakIE +from .whowatch import WhoWatchIE from .whyp import WhypIE from .wikimedia import WikimediaIE from .wimbledon import WimbledonIE from .wimtv import WimTVIE -from .whowatch import WhoWatchIE from .wistia import ( + WistiaChannelIE, WistiaIE, WistiaPlaylistIE, - WistiaChannelIE, ) from .wordpress import ( - WordpressPlaylistEmbedIE, WordpressMiniAudioPlayerEmbedIE, + WordpressPlaylistEmbedIE, ) from .worldstarhiphop import WorldStarHipHopIE from .wppilot import ( - WPPilotIE, WPPilotChannelsIE, + WPPilotIE, ) from .wrestleuniverse import ( - WrestleUniverseVODIE, WrestleUniversePPVIE, + WrestleUniverseVODIE, ) from .wsj import ( WSJIE, @@ -2374,22 +2443,22 @@ ) from .wwe import WWEIE from .wykop import ( - WykopDigIE, WykopDigCommentIE, - WykopPostIE, + WykopDigIE, WykopPostCommentIE, + WykopPostIE, ) from .xanimu import XanimuIE from .xboxclips import XboxClipsIE from .xhamster import ( - XHamsterIE, XHamsterEmbedIE, + XHamsterIE, XHamsterUserIE, ) from .xiaohongshu import XiaoHongShuIE from .ximalaya import ( + XimalayaAlbumIE, XimalayaIE, - XimalayaAlbumIE ) from .xinpianchang import XinpianchangIE from .xminus import XMinusIE @@ -2397,27 +2466,27 @@ from .xstream import XstreamIE from .xvideos import ( XVideosIE, - XVideosQuickiesIE + XVideosQuickiesIE, ) from .xxxymovies import XXXYMoviesIE from .yahoo import ( YahooIE, - YahooSearchIE, YahooJapanNewsIE, + YahooSearchIE, ) from .yandexdisk import YandexDiskIE from .yandexmusic import ( - YandexMusicTrackIE, YandexMusicAlbumIE, - YandexMusicPlaylistIE, - YandexMusicArtistTracksIE, YandexMusicArtistAlbumsIE, + YandexMusicArtistTracksIE, + YandexMusicPlaylistIE, + YandexMusicTrackIE, ) from .yandexvideo import ( YandexVideoIE, YandexVideoPreviewIE, - ZenYandexIE, ZenYandexChannelIE, + ZenYandexIE, ) from .yapfiles import YapFilesIE from .yappy import ( @@ -2431,24 +2500,34 @@ YoukuShowIE, ) from .younow import ( - YouNowLiveIE, YouNowChannelIE, + YouNowLiveIE, YouNowMomentIE, ) -from .youporn import YouPornIE +from .youporn import ( + YouPornCategoryIE, + YouPornChannelIE, + YouPornCollectionIE, + YouPornIE, + YouPornStarIE, + YouPornTagIE, + YouPornVideosIE, +) from .zaiko import ( - ZaikoIE, ZaikoETicketIE, + ZaikoIE, ) from .zapiks import ZapiksIE from .zattoo import ( BBVTVIE, + EWETVIE, + SAKTVIE, + VTXTVIE, BBVTVLiveIE, BBVTVRecordingsIE, EinsUndEinsTVIE, EinsUndEinsTVLiveIE, EinsUndEinsTVRecordingsIE, - EWETVIE, EWETVLiveIE, EWETVRecordingsIE, GlattvisionTVIE, @@ -2466,13 +2545,11 @@ QuantumTVIE, QuantumTVLiveIE, QuantumTVRecordingsIE, + SAKTVLiveIE, + SAKTVRecordingsIE, SaltTVIE, SaltTVLiveIE, SaltTVRecordingsIE, - SAKTVIE, - SAKTVLiveIE, - SAKTVRecordingsIE, - VTXTVIE, VTXTVLiveIE, VTXTVRecordingsIE, WalyTVIE, @@ -2483,7 +2560,10 @@ ZattooMoviesIE, ZattooRecordingsIE, ) -from .zdf import ZDFIE, ZDFChannelIE +from .zdf import ( + ZDFIE, + ZDFChannelIE, +) from .zee5 import ( Zee5IE, Zee5SeriesIE, @@ -2493,16 +2573,16 @@ from .zetland import ZetlandDKArticleIE from .zhihu import ZhihuIE from .zingmp3 import ( - ZingMp3IE, ZingMp3AlbumIE, ZingMp3ChartHomeIE, - ZingMp3WeekChartIE, ZingMp3ChartMusicVideoIE, - ZingMp3UserIE, ZingMp3HubIE, + ZingMp3IE, ZingMp3LiveRadioIE, ZingMp3PodcastEpisodeIE, ZingMp3PodcastIE, + ZingMp3UserIE, + ZingMp3WeekChartIE, ) from .zoom import ZoomIE from .zype import ZypeIE diff --git a/lib/yt_dlp/extractor/abc.py b/lib/yt_dlp/extractor/abc.py index b21742281..2c0d296fd 100644 --- a/lib/yt_dlp/extractor/abc.py +++ b/lib/yt_dlp/extractor/abc.py @@ -6,10 +6,10 @@ from .common import InfoExtractor from ..compat import compat_str from ..utils import ( - dict_get, ExtractorError, - js_to_json, + dict_get, int_or_none, + js_to_json, parse_iso8601, str_or_none, traverse_obj, diff --git a/lib/yt_dlp/extractor/abematv.py b/lib/yt_dlp/extractor/abematv.py index fee7375ea..b8c79b912 100644 --- a/lib/yt_dlp/extractor/abematv.py +++ b/lib/yt_dlp/extractor/abematv.py @@ -12,20 +12,21 @@ import urllib.request import urllib.response import uuid -from ..utils.networking import clean_proxies + from .common import InfoExtractor from ..aes import aes_ecb_decrypt from ..utils import ( ExtractorError, + OnDemandPagedList, bytes_to_intlist, decode_base_n, int_or_none, intlist_to_bytes, - OnDemandPagedList, time_seconds, traverse_obj, update_url_query, ) +from ..utils.networking import clean_proxies def add_opener(ydl, handler): # FIXME: Create proper API in .networking diff --git a/lib/yt_dlp/extractor/acfun.py b/lib/yt_dlp/extractor/acfun.py index c3b4f432e..07933192f 100644 --- a/lib/yt_dlp/extractor/acfun.py +++ b/lib/yt_dlp/extractor/acfun.py @@ -3,10 +3,10 @@ float_or_none, format_field, int_or_none, - str_or_none, - traverse_obj, parse_codecs, parse_qs, + str_or_none, + traverse_obj, ) diff --git a/lib/yt_dlp/extractor/adn.py b/lib/yt_dlp/extractor/adn.py index 898d37298..2f3b67dad 100644 --- a/lib/yt_dlp/extractor/adn.py +++ b/lib/yt_dlp/extractor/adn.py @@ -10,18 +10,18 @@ from ..compat import compat_b64decode from ..networking.exceptions import HTTPError from ..utils import ( + ExtractorError, ass_subtitles_timecode, bytes_to_intlist, bytes_to_long, - ExtractorError, float_or_none, int_or_none, intlist_to_bytes, long_to_bytes, parse_iso8601, pkcs1pad, - strip_or_none, str_or_none, + strip_or_none, try_get, unified_strdate, urlencode_postdata, diff --git a/lib/yt_dlp/extractor/adobetv.py b/lib/yt_dlp/extractor/adobetv.py index d1525a1af..08e9e5182 100644 --- a/lib/yt_dlp/extractor/adobetv.py +++ b/lib/yt_dlp/extractor/adobetv.py @@ -4,11 +4,11 @@ from .common import InfoExtractor from ..compat import compat_str from ..utils import ( + ISO639Utils, + OnDemandPagedList, float_or_none, int_or_none, - ISO639Utils, join_nonempty, - OnDemandPagedList, parse_duration, str_or_none, str_to_int, diff --git a/lib/yt_dlp/extractor/airtv.py b/lib/yt_dlp/extractor/airtv.py index 0b73a966e..6cc63cd7f 100644 --- a/lib/yt_dlp/extractor/airtv.py +++ b/lib/yt_dlp/extractor/airtv.py @@ -5,7 +5,7 @@ int_or_none, mimetype2ext, parse_iso8601, - traverse_obj + traverse_obj, ) diff --git a/lib/yt_dlp/extractor/allstar.py b/lib/yt_dlp/extractor/allstar.py index 87219f2f8..49df4bf3a 100644 --- a/lib/yt_dlp/extractor/allstar.py +++ b/lib/yt_dlp/extractor/allstar.py @@ -12,7 +12,6 @@ ) from ..utils.traversal import traverse_obj - _FIELDS = ''' _id clipImageSource diff --git a/lib/yt_dlp/extractor/alphaporno.py b/lib/yt_dlp/extractor/alphaporno.py index 8d5b472d3..f927965de 100644 --- a/lib/yt_dlp/extractor/alphaporno.py +++ b/lib/yt_dlp/extractor/alphaporno.py @@ -1,9 +1,9 @@ from .common import InfoExtractor from ..utils import ( - parse_iso8601, + int_or_none, parse_duration, parse_filesize, - int_or_none, + parse_iso8601, ) diff --git a/lib/yt_dlp/extractor/alura.py b/lib/yt_dlp/extractor/alura.py index b785c62c3..cb2b9891e 100644 --- a/lib/yt_dlp/extractor/alura.py +++ b/lib/yt_dlp/extractor/alura.py @@ -1,17 +1,13 @@ import re from .common import InfoExtractor - -from ..compat import ( - compat_urlparse, -) - +from ..compat import compat_urlparse from ..utils import ( + ExtractorError, + clean_html, + int_or_none, urlencode_postdata, urljoin, - int_or_none, - clean_html, - ExtractorError ) diff --git a/lib/yt_dlp/extractor/amara.py b/lib/yt_dlp/extractor/amara.py index 5018710e0..509b21a53 100644 --- a/lib/yt_dlp/extractor/amara.py +++ b/lib/yt_dlp/extractor/amara.py @@ -1,6 +1,6 @@ from .common import InfoExtractor -from .youtube import YoutubeIE from .vimeo import VimeoIE +from .youtube import YoutubeIE from ..utils import ( int_or_none, parse_iso8601, diff --git a/lib/yt_dlp/extractor/amp.py b/lib/yt_dlp/extractor/amp.py index 0d259c549..6b2bf2db2 100644 --- a/lib/yt_dlp/extractor/amp.py +++ b/lib/yt_dlp/extractor/amp.py @@ -1,7 +1,7 @@ from .common import InfoExtractor from ..utils import ( - determine_ext, ExtractorError, + determine_ext, int_or_none, mimetype2ext, parse_iso8601, diff --git a/lib/yt_dlp/extractor/anchorfm.py b/lib/yt_dlp/extractor/anchorfm.py index 52f2ad057..5e78f372e 100644 --- a/lib/yt_dlp/extractor/anchorfm.py +++ b/lib/yt_dlp/extractor/anchorfm.py @@ -5,7 +5,7 @@ int_or_none, str_or_none, traverse_obj, - unified_timestamp + unified_timestamp, ) diff --git a/lib/yt_dlp/extractor/angel.py b/lib/yt_dlp/extractor/angel.py index 306b3651e..9f5b9b523 100644 --- a/lib/yt_dlp/extractor/angel.py +++ b/lib/yt_dlp/extractor/angel.py @@ -1,7 +1,7 @@ import re from .common import InfoExtractor -from ..utils import url_or_none, merge_dicts +from ..utils import merge_dicts, url_or_none class AngelIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/appleconnect.py b/lib/yt_dlp/extractor/appleconnect.py index d00b0f906..433eb4ed8 100644 --- a/lib/yt_dlp/extractor/appleconnect.py +++ b/lib/yt_dlp/extractor/appleconnect.py @@ -1,8 +1,5 @@ from .common import InfoExtractor -from ..utils import ( - str_to_int, - ExtractorError -) +from ..utils import ExtractorError, str_to_int class AppleConnectIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/appletrailers.py b/lib/yt_dlp/extractor/appletrailers.py index 2e0b0a8c9..21103aee5 100644 --- a/lib/yt_dlp/extractor/appletrailers.py +++ b/lib/yt_dlp/extractor/appletrailers.py @@ -1,5 +1,5 @@ -import re import json +import re from .common import InfoExtractor from ..compat import compat_urlparse diff --git a/lib/yt_dlp/extractor/arnes.py b/lib/yt_dlp/extractor/arnes.py index a493714d1..9a5524aab 100644 --- a/lib/yt_dlp/extractor/arnes.py +++ b/lib/yt_dlp/extractor/arnes.py @@ -4,8 +4,8 @@ compat_urllib_parse_urlparse, ) from ..utils import ( - format_field, float_or_none, + format_field, int_or_none, parse_iso8601, remove_start, diff --git a/lib/yt_dlp/extractor/atvat.py b/lib/yt_dlp/extractor/atvat.py index d60feba31..20ee34cca 100644 --- a/lib/yt_dlp/extractor/atvat.py +++ b/lib/yt_dlp/extractor/atvat.py @@ -2,10 +2,10 @@ from .common import InfoExtractor from ..utils import ( + ExtractorError, float_or_none, jwt_encode_hs256, try_get, - ExtractorError, ) diff --git a/lib/yt_dlp/extractor/awaan.py b/lib/yt_dlp/extractor/awaan.py index 6fc938de9..a8dfb3efc 100644 --- a/lib/yt_dlp/extractor/awaan.py +++ b/lib/yt_dlp/extractor/awaan.py @@ -2,8 +2,8 @@ from .common import InfoExtractor from ..compat import ( - compat_urllib_parse_urlencode, compat_str, + compat_urllib_parse_urlencode, ) from ..utils import ( format_field, diff --git a/lib/yt_dlp/extractor/banbye.py b/lib/yt_dlp/extractor/banbye.py index 67af29a96..c4e07a79a 100644 --- a/lib/yt_dlp/extractor/banbye.py +++ b/lib/yt_dlp/extractor/banbye.py @@ -2,12 +2,12 @@ from .common import InfoExtractor from ..compat import ( - compat_urllib_parse_urlparse, compat_parse_qs, + compat_urllib_parse_urlparse, ) from ..utils import ( - format_field, InAdvancePagedList, + format_field, traverse_obj, unified_timestamp, ) diff --git a/lib/yt_dlp/extractor/bannedvideo.py b/lib/yt_dlp/extractor/bannedvideo.py index 51e722057..82dc9ab02 100644 --- a/lib/yt_dlp/extractor/bannedvideo.py +++ b/lib/yt_dlp/extractor/bannedvideo.py @@ -2,11 +2,11 @@ from .common import InfoExtractor from ..utils import ( - try_get, - int_or_none, - url_or_none, float_or_none, + int_or_none, + try_get, unified_timestamp, + url_or_none, ) diff --git a/lib/yt_dlp/extractor/beeg.py b/lib/yt_dlp/extractor/beeg.py index 042b3220b..da98ac314 100644 --- a/lib/yt_dlp/extractor/beeg.py +++ b/lib/yt_dlp/extractor/beeg.py @@ -1,5 +1,4 @@ from .common import InfoExtractor - from ..utils import ( int_or_none, str_or_none, diff --git a/lib/yt_dlp/extractor/bleacherreport.py b/lib/yt_dlp/extractor/bleacherreport.py index e875957cf..aa3d63ee7 100644 --- a/lib/yt_dlp/extractor/bleacherreport.py +++ b/lib/yt_dlp/extractor/bleacherreport.py @@ -1,5 +1,5 @@ -from .common import InfoExtractor from .amp import AMPIE +from .common import InfoExtractor from ..utils import ( ExtractorError, int_or_none, diff --git a/lib/yt_dlp/extractor/blogger.py b/lib/yt_dlp/extractor/blogger.py index 3d6e03304..ef0151de6 100644 --- a/lib/yt_dlp/extractor/blogger.py +++ b/lib/yt_dlp/extractor/blogger.py @@ -1,3 +1,4 @@ +from .common import InfoExtractor from ..utils import ( mimetype2ext, parse_duration, @@ -5,7 +6,6 @@ str_or_none, traverse_obj, ) -from .common import InfoExtractor class BloggerIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/bostonglobe.py b/lib/yt_dlp/extractor/bostonglobe.py index 92f8ea2cb..267586687 100644 --- a/lib/yt_dlp/extractor/bostonglobe.py +++ b/lib/yt_dlp/extractor/bostonglobe.py @@ -1,7 +1,6 @@ import re from .common import InfoExtractor - from ..utils import ( extract_attributes, ) diff --git a/lib/yt_dlp/extractor/boxcast.py b/lib/yt_dlp/extractor/boxcast.py index 51f9eb787..da06cc3f8 100644 --- a/lib/yt_dlp/extractor/boxcast.py +++ b/lib/yt_dlp/extractor/boxcast.py @@ -1,9 +1,5 @@ from .common import InfoExtractor -from ..utils import ( - js_to_json, - traverse_obj, - unified_timestamp -) +from ..utils import js_to_json, traverse_obj, unified_timestamp class BoxCastVideoIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/brainpop.py b/lib/yt_dlp/extractor/brainpop.py index 1200437e6..04b1dd80c 100644 --- a/lib/yt_dlp/extractor/brainpop.py +++ b/lib/yt_dlp/extractor/brainpop.py @@ -6,7 +6,7 @@ classproperty, int_or_none, traverse_obj, - urljoin + urljoin, ) diff --git a/lib/yt_dlp/extractor/brightcove.py b/lib/yt_dlp/extractor/brightcove.py index 61b18412d..4190e1a09 100644 --- a/lib/yt_dlp/extractor/brightcove.py +++ b/lib/yt_dlp/extractor/brightcove.py @@ -12,10 +12,11 @@ ) from ..networking.exceptions import HTTPError from ..utils import ( + ExtractorError, + UnsupportedError, clean_html, dict_get, extract_attributes, - ExtractorError, find_xpath_attr, fix_xml_ampersands, float_or_none, @@ -29,7 +30,6 @@ try_get, unescapeHTML, unsmuggle_url, - UnsupportedError, update_url_query, url_or_none, ) diff --git a/lib/yt_dlp/extractor/caffeinetv.py b/lib/yt_dlp/extractor/caffeinetv.py new file mode 100644 index 000000000..aa107f858 --- /dev/null +++ b/lib/yt_dlp/extractor/caffeinetv.py @@ -0,0 +1,74 @@ +from .common import InfoExtractor +from ..utils import ( + determine_ext, + int_or_none, + parse_iso8601, + traverse_obj, + urljoin, +) + + +class CaffeineTVIE(InfoExtractor): + _VALID_URL = r'https?://(?:www\.)?caffeine\.tv/[^/?#]+/video/(?P[\da-f-]+)' + _TESTS = [{ + 'url': 'https://www.caffeine.tv/TsuSurf/video/cffc0a00-e73f-11ec-8080-80017d29f26e', + 'info_dict': { + 'id': 'cffc0a00-e73f-11ec-8080-80017d29f26e', + 'ext': 'mp4', + 'title': 'GOOOOD MORNINNNNN #highlights', + 'timestamp': 1654702180, + 'upload_date': '20220608', + 'uploader': 'RahJON Wicc', + 'uploader_id': 'TsuSurf', + 'duration': 3145, + 'age_limit': 17, + 'thumbnail': 'https://www.caffeine.tv/broadcasts/776b6f84-9cd5-42e3-af1d-4a776eeed697/replay/lobby.jpg', + 'comment_count': int, + 'view_count': int, + 'like_count': int, + 'tags': ['highlights', 'battlerap'], + }, + 'params': { + 'skip_download': 'm3u8', + }, + }] + + def _real_extract(self, url): + video_id = self._match_id(url) + json_data = self._download_json( + f'https://api.caffeine.tv/social/public/activity/{video_id}', video_id) + broadcast_info = traverse_obj(json_data, ('broadcast_info', {dict})) or {} + + video_url = broadcast_info['video_url'] + ext = determine_ext(video_url) + if ext == 'm3u8': + formats = self._extract_m3u8_formats(video_url, video_id, 'mp4') + else: + formats = [{'url': video_url}] + + return { + 'id': video_id, + 'formats': formats, + **traverse_obj(json_data, { + 'like_count': ('like_count', {int_or_none}), + 'view_count': ('view_count', {int_or_none}), + 'comment_count': ('comment_count', {int_or_none}), + 'tags': ('tags', ..., {str}, {lambda x: x or None}), + 'uploader': ('user', 'name', {str}), + 'uploader_id': (((None, 'user'), 'username'), {str}, any), + 'is_live': ('is_live', {bool}), + }), + **traverse_obj(broadcast_info, { + 'title': ('broadcast_title', {str}), + 'duration': ('content_duration', {int_or_none}), + 'timestamp': ('broadcast_start_time', {parse_iso8601}), + 'thumbnail': ('preview_image_path', {lambda x: urljoin(url, x)}), + }), + 'age_limit': { + # assume Apple Store ratings: https://en.wikipedia.org/wiki/Mobile_software_content_rating_system + 'FOUR_PLUS': 0, + 'NINE_PLUS': 9, + 'TWELVE_PLUS': 12, + 'SEVENTEEN_PLUS': 17, + }.get(broadcast_info.get('content_rating'), 17), + } diff --git a/lib/yt_dlp/extractor/cbs.py b/lib/yt_dlp/extractor/cbs.py index cf830210f..aca9782c7 100644 --- a/lib/yt_dlp/extractor/cbs.py +++ b/lib/yt_dlp/extractor/cbs.py @@ -5,14 +5,14 @@ from ..utils import ( ExtractorError, extract_attributes, + find_xpath_attr, get_element_html_by_id, int_or_none, - find_xpath_attr, smuggle_url, - xpath_element, - xpath_text, update_url_query, url_or_none, + xpath_element, + xpath_text, ) diff --git a/lib/yt_dlp/extractor/ceskatelevize.py b/lib/yt_dlp/extractor/ceskatelevize.py index 156b6a324..5d6335729 100644 --- a/lib/yt_dlp/extractor/ceskatelevize.py +++ b/lib/yt_dlp/extractor/ceskatelevize.py @@ -101,7 +101,7 @@ def _real_extract(self, url): site_name = self._og_search_property('site_name', webpage, fatal=False, default='Česká televize') playlist_title = self._og_search_title(webpage, default=None) if site_name and playlist_title: - playlist_title = re.split(r'\s*[—|]\s*%s' % (site_name, ), playlist_title, 1)[0] + playlist_title = re.split(r'\s*[—|]\s*%s' % (site_name, ), playlist_title, maxsplit=1)[0] playlist_description = self._og_search_description(webpage, default=None) if playlist_description: playlist_description = playlist_description.replace('\xa0', ' ') diff --git a/lib/yt_dlp/extractor/cinetecamilano.py b/lib/yt_dlp/extractor/cinetecamilano.py index 9cffa11e8..745b71f24 100644 --- a/lib/yt_dlp/extractor/cinetecamilano.py +++ b/lib/yt_dlp/extractor/cinetecamilano.py @@ -1,4 +1,5 @@ import json + from .common import InfoExtractor from ..networking.exceptions import HTTPError from ..utils import ( diff --git a/lib/yt_dlp/extractor/clippit.py b/lib/yt_dlp/extractor/clippit.py index 006a713b2..67b56e00d 100644 --- a/lib/yt_dlp/extractor/clippit.py +++ b/lib/yt_dlp/extractor/clippit.py @@ -1,11 +1,11 @@ +import re + from .common import InfoExtractor from ..utils import ( parse_iso8601, qualities, ) -import re - class ClippitIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/common.py b/lib/yt_dlp/extractor/common.py index a952828fb..1d2c443c0 100644 --- a/lib/yt_dlp/extractor/common.py +++ b/lib/yt_dlp/extractor/common.py @@ -1,5 +1,6 @@ import base64 import collections +import functools import getpass import hashlib import http.client @@ -21,7 +22,6 @@ import urllib.request import xml.etree.ElementTree -from ..compat import functools # isort: split from ..compat import ( compat_etree_fromstring, compat_expanduser, @@ -2451,7 +2451,7 @@ def _parse_smil_formats_and_subtitles( }) continue - src_url = src if src.startswith('http') else urllib.parse.urljoin(base, src) + src_url = src if src.startswith('http') else urllib.parse.urljoin(f'{base}/', src) src_url = src_url.strip() if proto == 'm3u8' or src_ext == 'm3u8': @@ -3384,23 +3384,16 @@ def manifest_url(manifest): return formats def _find_jwplayer_data(self, webpage, video_id=None, transform_source=js_to_json): - mobj = re.search( - r'''(?s)jwplayer\s*\(\s*(?P'|")(?!(?P=q)).+(?P=q)\s*\)(?!).*?\.\s*setup\s*\(\s*(?P(?:\([^)]*\)|[^)])+)\s*\)''', - webpage) - if mobj: - try: - jwplayer_data = self._parse_json(mobj.group('options'), - video_id=video_id, - transform_source=transform_source) - except ExtractorError: - pass - else: - if isinstance(jwplayer_data, dict): - return jwplayer_data + return self._search_json( + r'''(?'|")(?!(?P=q)).+(?P=q)\s*\)(?:(?!).)*?\.\s*(?:setup\s*\(|(?Pload)\s*\(\s*\[)''', + webpage, 'JWPlayer data', video_id, + # must be a {...} or sequence, ending + contains_pattern=r'\{(?s:.*)}(?(load)(?:\s*,\s*\{(?s:.*)})*)', end_pattern=r'(?(load)\]|\))', + transform_source=transform_source, default=None) - def _extract_jwplayer_data(self, webpage, video_id, *args, **kwargs): + def _extract_jwplayer_data(self, webpage, video_id, *args, transform_source=js_to_json, **kwargs): jwplayer_data = self._find_jwplayer_data( - webpage, video_id, transform_source=js_to_json) + webpage, video_id, transform_source=transform_source) return self._parse_jwplayer_data( jwplayer_data, video_id, *args, **kwargs) @@ -3432,22 +3425,14 @@ def _parse_jwplayer_data(self, jwplayer_data, video_id=None, require_title=True, mpd_id=mpd_id, rtmp_params=rtmp_params, base_url=base_url) subtitles = {} - tracks = video_data.get('tracks') - if tracks and isinstance(tracks, list): - for track in tracks: - if not isinstance(track, dict): - continue - track_kind = track.get('kind') - if not track_kind or not isinstance(track_kind, str): - continue - if track_kind.lower() not in ('captions', 'subtitles'): - continue - track_url = urljoin(base_url, track.get('file')) - if not track_url: - continue - subtitles.setdefault(track.get('label') or 'en', []).append({ - 'url': self._proto_relative_url(track_url) - }) + for track in traverse_obj(video_data, ( + 'tracks', lambda _, v: v['kind'].lower() in ('captions', 'subtitles'))): + track_url = urljoin(base_url, track.get('file')) + if not track_url: + continue + subtitles.setdefault(track.get('label') or 'en', []).append({ + 'url': self._proto_relative_url(track_url) + }) entry = { 'id': this_video_id, @@ -3532,7 +3517,7 @@ def _parse_jwplayer_formats(self, jwplayer_sources_data, video_id=None, # See com/longtailvideo/jwplayer/media/RTMPMediaProvider.as # of jwplayer.flash.swf rtmp_url_parts = re.split( - r'((?:mp4|mp3|flv):)', source_url, 1) + r'((?:mp4|mp3|flv):)', source_url, maxsplit=1) if len(rtmp_url_parts) == 3: rtmp_url, prefix, play_path = rtmp_url_parts a_format.update({ diff --git a/lib/yt_dlp/extractor/corus.py b/lib/yt_dlp/extractor/corus.py index bcc34ddd8..0a98c980f 100644 --- a/lib/yt_dlp/extractor/corus.py +++ b/lib/yt_dlp/extractor/corus.py @@ -1,7 +1,7 @@ from .theplatform import ThePlatformFeedIE from ..utils import ( - dict_get, ExtractorError, + dict_get, float_or_none, int_or_none, ) diff --git a/lib/yt_dlp/extractor/crackle.py b/lib/yt_dlp/extractor/crackle.py index 1ef90b5a0..0cb7d940c 100644 --- a/lib/yt_dlp/extractor/crackle.py +++ b/lib/yt_dlp/extractor/crackle.py @@ -6,6 +6,7 @@ from .common import InfoExtractor from ..networking.exceptions import HTTPError from ..utils import ( + ExtractorError, determine_ext, float_or_none, int_or_none, @@ -13,7 +14,6 @@ parse_age_limit, parse_duration, url_or_none, - ExtractorError ) diff --git a/lib/yt_dlp/extractor/cspan.py b/lib/yt_dlp/extractor/cspan.py index 0075680e8..e56584e4e 100644 --- a/lib/yt_dlp/extractor/cspan.py +++ b/lib/yt_dlp/extractor/cspan.py @@ -1,10 +1,12 @@ import re from .common import InfoExtractor +from .senategov import SenateISVPIE +from .ustream import UstreamIE from ..compat import compat_HTMLParseError from ..utils import ( - determine_ext, ExtractorError, + determine_ext, extract_attributes, find_xpath_attr, get_element_by_attribute, @@ -19,8 +21,6 @@ str_to_int, unescapeHTML, ) -from .senategov import SenateISVPIE -from .ustream import UstreamIE class CSpanIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/ctsnews.py b/lib/yt_dlp/extractor/ctsnews.py index cec178f03..1817bd2ff 100644 --- a/lib/yt_dlp/extractor/ctsnews.py +++ b/lib/yt_dlp/extractor/ctsnews.py @@ -1,6 +1,6 @@ from .common import InfoExtractor -from ..utils import unified_timestamp from .youtube import YoutubeIE +from ..utils import unified_timestamp class CtsNewsIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/dailymail.py b/lib/yt_dlp/extractor/dailymail.py index 43401e111..4c25bea11 100644 --- a/lib/yt_dlp/extractor/dailymail.py +++ b/lib/yt_dlp/extractor/dailymail.py @@ -1,8 +1,8 @@ from .common import InfoExtractor from ..compat import compat_str from ..utils import ( - int_or_none, determine_protocol, + int_or_none, try_get, unescapeHTML, ) diff --git a/lib/yt_dlp/extractor/damtomo.py b/lib/yt_dlp/extractor/damtomo.py index 5e14d6aff..2e0f6f0d3 100644 --- a/lib/yt_dlp/extractor/damtomo.py +++ b/lib/yt_dlp/extractor/damtomo.py @@ -1,8 +1,8 @@ import re from .common import InfoExtractor -from ..utils import ExtractorError, clean_html, int_or_none, try_get, unified_strdate from ..compat import compat_str +from ..utils import ExtractorError, clean_html, int_or_none, try_get, unified_strdate class DamtomoBaseIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/democracynow.py b/lib/yt_dlp/extractor/democracynow.py index 1624d085c..177424937 100644 --- a/lib/yt_dlp/extractor/democracynow.py +++ b/lib/yt_dlp/extractor/democracynow.py @@ -1,11 +1,11 @@ -import re import os.path +import re from .common import InfoExtractor from ..compat import compat_urlparse from ..utils import ( - url_basename, remove_start, + url_basename, ) diff --git a/lib/yt_dlp/extractor/digitalconcerthall.py b/lib/yt_dlp/extractor/digitalconcerthall.py index c11cd790b..4380c414e 100644 --- a/lib/yt_dlp/extractor/digitalconcerthall.py +++ b/lib/yt_dlp/extractor/digitalconcerthall.py @@ -1,5 +1,4 @@ from .common import InfoExtractor - from ..utils import ( ExtractorError, parse_resolution, diff --git a/lib/yt_dlp/extractor/discoverygo.py b/lib/yt_dlp/extractor/discoverygo.py index 1f3d8e31c..b2663a63d 100644 --- a/lib/yt_dlp/extractor/discoverygo.py +++ b/lib/yt_dlp/extractor/discoverygo.py @@ -2,9 +2,9 @@ from .common import InfoExtractor from ..utils import ( + ExtractorError, determine_ext, extract_attributes, - ExtractorError, int_or_none, parse_age_limit, remove_end, diff --git a/lib/yt_dlp/extractor/disney.py b/lib/yt_dlp/extractor/disney.py index 430de326f..d8dde0ca7 100644 --- a/lib/yt_dlp/extractor/disney.py +++ b/lib/yt_dlp/extractor/disney.py @@ -2,10 +2,10 @@ from .common import InfoExtractor from ..utils import ( - int_or_none, - unified_strdate, determine_ext, + int_or_none, join_nonempty, + unified_strdate, update_url_query, ) diff --git a/lib/yt_dlp/extractor/douyutv.py b/lib/yt_dlp/extractor/douyutv.py index ee8893d5a..244ffdf1c 100644 --- a/lib/yt_dlp/extractor/douyutv.py +++ b/lib/yt_dlp/extractor/douyutv.py @@ -1,5 +1,5 @@ -import time import hashlib +import time import urllib import uuid diff --git a/lib/yt_dlp/extractor/dplay.py b/lib/yt_dlp/extractor/dplay.py index 1ecc4baf6..ddf2128b0 100644 --- a/lib/yt_dlp/extractor/dplay.py +++ b/lib/yt_dlp/extractor/dplay.py @@ -4,8 +4,8 @@ from .common import InfoExtractor from ..networking.exceptions import HTTPError from ..utils import ( - determine_ext, ExtractorError, + determine_ext, float_or_none, int_or_none, remove_start, diff --git a/lib/yt_dlp/extractor/drtuber.py b/lib/yt_dlp/extractor/drtuber.py index e5dab6ac0..a9247edc0 100644 --- a/lib/yt_dlp/extractor/drtuber.py +++ b/lib/yt_dlp/extractor/drtuber.py @@ -2,8 +2,8 @@ from .common import InfoExtractor from ..utils import ( - int_or_none, NO_DEFAULT, + int_or_none, parse_duration, str_to_int, ) diff --git a/lib/yt_dlp/extractor/duboku.py b/lib/yt_dlp/extractor/duboku.py index 626e577e7..adc7705bc 100644 --- a/lib/yt_dlp/extractor/duboku.py +++ b/lib/yt_dlp/extractor/duboku.py @@ -5,9 +5,9 @@ from .common import InfoExtractor from ..compat import compat_urlparse from ..utils import ( + ExtractorError, clean_html, extract_attributes, - ExtractorError, get_elements_by_class, int_or_none, js_to_json, diff --git a/lib/yt_dlp/extractor/dvtv.py b/lib/yt_dlp/extractor/dvtv.py index e67143370..e6660dcd9 100644 --- a/lib/yt_dlp/extractor/dvtv.py +++ b/lib/yt_dlp/extractor/dvtv.py @@ -2,15 +2,15 @@ from .common import InfoExtractor from ..utils import ( - determine_ext, ExtractorError, + determine_ext, int_or_none, join_nonempty, js_to_json, mimetype2ext, + parse_iso8601, try_get, unescapeHTML, - parse_iso8601, ) diff --git a/lib/yt_dlp/extractor/dw.py b/lib/yt_dlp/extractor/dw.py index f7b852076..feab804af 100644 --- a/lib/yt_dlp/extractor/dw.py +++ b/lib/yt_dlp/extractor/dw.py @@ -1,10 +1,10 @@ from .common import InfoExtractor +from ..compat import compat_urlparse from ..utils import ( int_or_none, unified_strdate, url_or_none, ) -from ..compat import compat_urlparse class DWIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/ertgr.py b/lib/yt_dlp/extractor/ertgr.py index 9ecdf5d3b..19c6933e7 100644 --- a/lib/yt_dlp/extractor/ertgr.py +++ b/lib/yt_dlp/extractor/ertgr.py @@ -4,15 +4,15 @@ from .common import InfoExtractor from ..compat import compat_str from ..utils import ( + ExtractorError, clean_html, determine_ext, - ExtractorError, dict_get, int_or_none, merge_dicts, - parse_qs, parse_age_limit, parse_iso8601, + parse_qs, str_or_none, try_get, url_or_none, diff --git a/lib/yt_dlp/extractor/europa.py b/lib/yt_dlp/extractor/europa.py index 29dfc8ae9..0cf889a1e 100644 --- a/lib/yt_dlp/extractor/europa.py +++ b/lib/yt_dlp/extractor/europa.py @@ -8,7 +8,7 @@ qualities, traverse_obj, unified_strdate, - xpath_text + xpath_text, ) diff --git a/lib/yt_dlp/extractor/euscreen.py b/lib/yt_dlp/extractor/euscreen.py index 65a1dc7c5..66fa42fa1 100644 --- a/lib/yt_dlp/extractor/euscreen.py +++ b/lib/yt_dlp/extractor/euscreen.py @@ -1,8 +1,7 @@ from .common import InfoExtractor - from ..utils import ( - parse_duration, js_to_json, + parse_duration, ) diff --git a/lib/yt_dlp/extractor/eyedotv.py b/lib/yt_dlp/extractor/eyedotv.py index d8b068e9c..4a13ab08d 100644 --- a/lib/yt_dlp/extractor/eyedotv.py +++ b/lib/yt_dlp/extractor/eyedotv.py @@ -1,8 +1,8 @@ from .common import InfoExtractor from ..utils import ( - xpath_text, - parse_duration, ExtractorError, + parse_duration, + xpath_text, ) diff --git a/lib/yt_dlp/extractor/fancode.py b/lib/yt_dlp/extractor/fancode.py index cddf25497..1e80f9a37 100644 --- a/lib/yt_dlp/extractor/fancode.py +++ b/lib/yt_dlp/extractor/fancode.py @@ -1,12 +1,6 @@ from .common import InfoExtractor - from ..compat import compat_str -from ..utils import ( - parse_iso8601, - ExtractorError, - try_get, - mimetype2ext -) +from ..utils import ExtractorError, mimetype2ext, parse_iso8601, try_get class FancodeVodIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/faz.py b/lib/yt_dlp/extractor/faz.py index bca62add9..796bac3c3 100644 --- a/lib/yt_dlp/extractor/faz.py +++ b/lib/yt_dlp/extractor/faz.py @@ -3,9 +3,9 @@ from .common import InfoExtractor from ..compat import compat_etree_fromstring from ..utils import ( + int_or_none, xpath_element, xpath_text, - int_or_none, ) diff --git a/lib/yt_dlp/extractor/fczenit.py b/lib/yt_dlp/extractor/fczenit.py index 8175b6b0f..b2dbb92d5 100644 --- a/lib/yt_dlp/extractor/fczenit.py +++ b/lib/yt_dlp/extractor/fczenit.py @@ -1,7 +1,7 @@ from .common import InfoExtractor from ..utils import ( - int_or_none, float_or_none, + int_or_none, ) diff --git a/lib/yt_dlp/extractor/fifa.py b/lib/yt_dlp/extractor/fifa.py index f604cbd40..ae837f6a0 100644 --- a/lib/yt_dlp/extractor/fifa.py +++ b/lib/yt_dlp/extractor/fifa.py @@ -1,5 +1,4 @@ from .common import InfoExtractor - from ..utils import ( int_or_none, traverse_obj, diff --git a/lib/yt_dlp/extractor/filmon.py b/lib/yt_dlp/extractor/filmon.py index 0cd18f494..69ca87c84 100644 --- a/lib/yt_dlp/extractor/filmon.py +++ b/lib/yt_dlp/extractor/filmon.py @@ -2,10 +2,10 @@ from ..compat import compat_str from ..networking.exceptions import HTTPError from ..utils import ( + ExtractorError, + int_or_none, qualities, strip_or_none, - int_or_none, - ExtractorError, ) diff --git a/lib/yt_dlp/extractor/gab.py b/lib/yt_dlp/extractor/gab.py index f9d22fd33..c10d290dc 100644 --- a/lib/yt_dlp/extractor/gab.py +++ b/lib/yt_dlp/extractor/gab.py @@ -7,7 +7,7 @@ parse_codecs, parse_duration, str_to_int, - unified_timestamp + unified_timestamp, ) diff --git a/lib/yt_dlp/extractor/gamejolt.py b/lib/yt_dlp/extractor/gamejolt.py index 1d3c0b110..b284e1e28 100644 --- a/lib/yt_dlp/extractor/gamejolt.py +++ b/lib/yt_dlp/extractor/gamejolt.py @@ -10,7 +10,7 @@ int_or_none, str_or_none, traverse_obj, - try_get + try_get, ) diff --git a/lib/yt_dlp/extractor/gaskrank.py b/lib/yt_dlp/extractor/gaskrank.py index bc56b03e3..6403be8cf 100644 --- a/lib/yt_dlp/extractor/gaskrank.py +++ b/lib/yt_dlp/extractor/gaskrank.py @@ -1,4 +1,5 @@ import re + from .common import InfoExtractor from ..utils import ( float_or_none, diff --git a/lib/yt_dlp/extractor/gbnews.py b/lib/yt_dlp/extractor/gbnews.py new file mode 100644 index 000000000..bb1554eea --- /dev/null +++ b/lib/yt_dlp/extractor/gbnews.py @@ -0,0 +1,107 @@ +import functools + +from .common import InfoExtractor +from ..utils import ( + ExtractorError, + extract_attributes, + get_elements_html_by_class, + url_or_none, +) +from ..utils.traversal import traverse_obj + + +class GBNewsIE(InfoExtractor): + IE_DESC = 'GB News clips, features and live streams' + _VALID_URL = r'https?://(?:www\.)?gbnews\.(?:uk|com)/(?:\w+/)?(?P[^#?]+)' + + _PLATFORM = 'safari' + _SSMP_URL = 'https://mm-v2.simplestream.com/ssmp/api.php' + _TESTS = [{ + 'url': 'https://www.gbnews.com/news/bbc-claudine-gay-harvard-university-antisemitism-row', + 'info_dict': { + 'id': '52264136', + 'ext': 'mp4', + 'thumbnail': r're:https?://www\.gbnews\.\w+/.+\.(?:jpe?g|png|webp)', + 'display_id': 'bbc-claudine-gay-harvard-university-antisemitism-row', + 'description': 'The post was criticised by former employers of the broadcaster', + 'title': 'BBC deletes post after furious backlash over headline downplaying antisemitism', + }, + }, { + 'url': 'https://www.gbnews.com/royal/prince-harry-in-love-with-kate-meghan-markle-jealous-royal', + 'info_dict': { + 'id': '52328390', + 'ext': 'mp4', + 'thumbnail': r're:https?://www\.gbnews\.\w+/.+\.(?:jpe?g|png|webp)', + 'display_id': 'prince-harry-in-love-with-kate-meghan-markle-jealous-royal', + 'description': 'Ingrid Seward has published 17 books documenting the highs and lows of the Royal Family', + 'title': 'Royal author claims Prince Harry was \'in love\' with Kate - Meghan was \'jealous\'', + } + }, { + 'url': 'https://www.gbnews.uk/watchlive', + 'info_dict': { + 'id': '1069', + 'ext': 'mp4', + 'thumbnail': r're:https?://www\.gbnews\.\w+/.+\.(?:jpe?g|png|webp)', + 'display_id': 'watchlive', + 'live_status': 'is_live', + 'title': r're:^GB News Live', + }, + 'params': {'skip_download': 'm3u8'}, + }] + + @functools.lru_cache + def _get_ss_endpoint(self, data_id, data_env): + if not data_id: + data_id = 'GB003' + if not data_env: + data_env = 'production' + + json_data = self._download_json( + self._SSMP_URL, None, 'Downloading Simplestream JSON metadata', query={ + 'id': data_id, + 'env': data_env, + }) + meta_url = traverse_obj(json_data, ('response', 'api_hostname', {url_or_none})) + if not meta_url: + raise ExtractorError('No API host found') + + return meta_url + + def _real_extract(self, url): + display_id = self._match_id(url).rpartition('/')[2] + webpage = self._download_webpage(url, display_id) + + video_data = None + elements = get_elements_html_by_class('simplestream', webpage) + for html_tag in elements: + attributes = extract_attributes(html_tag) + if 'sidebar' not in (attributes.get('class') or ''): + video_data = attributes + if not video_data: + raise ExtractorError('Could not find video element', expected=True) + + endpoint_url = self._get_ss_endpoint(video_data.get('data-id'), video_data.get('data-env')) + + uvid = video_data['data-uvid'] + video_type = video_data.get('data-type') + if not video_type or video_type == 'vod': + video_type = 'show' + stream_data = self._download_json( + f'{endpoint_url}/api/{video_type}/stream/{uvid}', + uvid, 'Downloading stream JSON', query={ + 'key': video_data.get('data-key'), + 'platform': self._PLATFORM, + }) + if traverse_obj(stream_data, 'drm'): + self.report_drm(uvid) + + return { + 'id': uvid, + 'display_id': display_id, + 'title': self._og_search_title(webpage, default=None), + 'description': self._og_search_description(webpage, default=None), + 'formats': self._extract_m3u8_formats(traverse_obj(stream_data, ( + 'response', 'stream', {url_or_none})), uvid, 'mp4'), + 'thumbnail': self._og_search_thumbnail(webpage, default=None), + 'is_live': video_type == 'live', + } diff --git a/lib/yt_dlp/extractor/generic.py b/lib/yt_dlp/extractor/generic.py index 2cfed0fd0..2818c718d 100644 --- a/lib/yt_dlp/extractor/generic.py +++ b/lib/yt_dlp/extractor/generic.py @@ -4,7 +4,7 @@ import urllib.parse import xml.etree.ElementTree -from .common import InfoExtractor # isort: split +from .common import InfoExtractor from .commonprotocols import RtmpIE from .youtube import YoutubeIE from ..compat import compat_etree_fromstring diff --git a/lib/yt_dlp/extractor/gettr.py b/lib/yt_dlp/extractor/gettr.py index 7795dc56f..b9dc7c63c 100644 --- a/lib/yt_dlp/extractor/gettr.py +++ b/lib/yt_dlp/extractor/gettr.py @@ -1,7 +1,7 @@ from .common import InfoExtractor from ..utils import ( - bool_or_none, ExtractorError, + bool_or_none, dict_get, float_or_none, int_or_none, diff --git a/lib/yt_dlp/extractor/gigya.py b/lib/yt_dlp/extractor/gigya.py index c5bc86bb4..7baf8de8d 100644 --- a/lib/yt_dlp/extractor/gigya.py +++ b/lib/yt_dlp/extractor/gigya.py @@ -1,5 +1,4 @@ from .common import InfoExtractor - from ..utils import ( ExtractorError, urlencode_postdata, diff --git a/lib/yt_dlp/extractor/glomex.py b/lib/yt_dlp/extractor/glomex.py index 22aac0db9..515f3c567 100644 --- a/lib/yt_dlp/extractor/glomex.py +++ b/lib/yt_dlp/extractor/glomex.py @@ -3,9 +3,9 @@ from .common import InfoExtractor from ..utils import ( + ExtractorError, determine_ext, extract_attributes, - ExtractorError, int_or_none, parse_qs, smuggle_url, diff --git a/lib/yt_dlp/extractor/go.py b/lib/yt_dlp/extractor/go.py index b075a02e0..fba98d79f 100644 --- a/lib/yt_dlp/extractor/go.py +++ b/lib/yt_dlp/extractor/go.py @@ -3,16 +3,16 @@ from .adobepass import AdobePassIE from ..compat import compat_str from ..utils import ( - int_or_none, + ExtractorError, determine_ext, + int_or_none, parse_age_limit, - remove_start, remove_end, + remove_start, + traverse_obj, try_get, - urlencode_postdata, - ExtractorError, unified_timestamp, - traverse_obj, + urlencode_postdata, ) diff --git a/lib/yt_dlp/extractor/godresource.py b/lib/yt_dlp/extractor/godresource.py index f010fff36..276a6c7fe 100644 --- a/lib/yt_dlp/extractor/godresource.py +++ b/lib/yt_dlp/extractor/godresource.py @@ -4,7 +4,7 @@ determine_ext, str_or_none, unified_timestamp, - url_or_none + url_or_none, ) from ..utils.traversal import traverse_obj diff --git a/lib/yt_dlp/extractor/gofile.py b/lib/yt_dlp/extractor/gofile.py index c6eca0c4d..fac088462 100644 --- a/lib/yt_dlp/extractor/gofile.py +++ b/lib/yt_dlp/extractor/gofile.py @@ -1,10 +1,7 @@ import hashlib from .common import InfoExtractor -from ..utils import ( - ExtractorError, - try_get -) +from ..utils import ExtractorError, try_get class GofileIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/gotostage.py b/lib/yt_dlp/extractor/gotostage.py index 112293bef..9c1a6cb91 100644 --- a/lib/yt_dlp/extractor/gotostage.py +++ b/lib/yt_dlp/extractor/gotostage.py @@ -1,11 +1,8 @@ +import json + from .common import InfoExtractor from ..compat import compat_str -from ..utils import ( - try_get, - url_or_none -) - -import json +from ..utils import try_get, url_or_none class GoToStageIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/hbo.py b/lib/yt_dlp/extractor/hbo.py index 530bdb727..2551cfffd 100644 --- a/lib/yt_dlp/extractor/hbo.py +++ b/lib/yt_dlp/extractor/hbo.py @@ -2,11 +2,11 @@ from .common import InfoExtractor from ..utils import ( - xpath_text, - xpath_element, int_or_none, parse_duration, urljoin, + xpath_element, + xpath_text, ) diff --git a/lib/yt_dlp/extractor/hearthisat.py b/lib/yt_dlp/extractor/hearthisat.py index c7da8f97d..eb0a77952 100644 --- a/lib/yt_dlp/extractor/hearthisat.py +++ b/lib/yt_dlp/extractor/hearthisat.py @@ -1,7 +1,7 @@ from .common import InfoExtractor from ..utils import ( - determine_ext, KNOWN_EXTENSIONS, + determine_ext, str_to_int, ) diff --git a/lib/yt_dlp/extractor/hketv.py b/lib/yt_dlp/extractor/hketv.py index e026996da..099c2a175 100644 --- a/lib/yt_dlp/extractor/hketv.py +++ b/lib/yt_dlp/extractor/hketv.py @@ -1,8 +1,8 @@ from .common import InfoExtractor from ..compat import compat_str from ..utils import ( - clean_html, ExtractorError, + clean_html, int_or_none, merge_dicts, parse_count, diff --git a/lib/yt_dlp/extractor/hrti.py b/lib/yt_dlp/extractor/hrti.py index 57b76e46b..41d50d000 100644 --- a/lib/yt_dlp/extractor/hrti.py +++ b/lib/yt_dlp/extractor/hrti.py @@ -4,8 +4,8 @@ from ..networking import Request from ..networking.exceptions import HTTPError from ..utils import ( - clean_html, ExtractorError, + clean_html, int_or_none, parse_age_limit, try_get, diff --git a/lib/yt_dlp/extractor/huya.py b/lib/yt_dlp/extractor/huya.py index c4965f9bc..5379b5410 100644 --- a/lib/yt_dlp/extractor/huya.py +++ b/lib/yt_dlp/extractor/huya.py @@ -2,8 +2,8 @@ import random import re -from ..compat import compat_urlparse, compat_b64decode - +from .common import InfoExtractor +from ..compat import compat_b64decode, compat_urlparse from ..utils import ( ExtractorError, int_or_none, @@ -13,8 +13,6 @@ update_url_query, ) -from .common import InfoExtractor - class HuyaLiveIE(InfoExtractor): _VALID_URL = r'https?://(?:www\.|m\.)?huya\.com/(?P[^/#?&]+)(?:\D|$)' diff --git a/lib/yt_dlp/extractor/ichinanalive.py b/lib/yt_dlp/extractor/ichinanalive.py index 9d55ddc02..c28d09f34 100644 --- a/lib/yt_dlp/extractor/ichinanalive.py +++ b/lib/yt_dlp/extractor/ichinanalive.py @@ -1,6 +1,6 @@ from .common import InfoExtractor -from ..utils import ExtractorError, str_or_none, traverse_obj, unified_strdate from ..compat import compat_str +from ..utils import ExtractorError, str_or_none, traverse_obj, unified_strdate class IchinanaLiveIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/infoq.py b/lib/yt_dlp/extractor/infoq.py index 192bcfe35..2bb48508c 100644 --- a/lib/yt_dlp/extractor/infoq.py +++ b/lib/yt_dlp/extractor/infoq.py @@ -1,3 +1,4 @@ +from .bokecc import BokeCCBaseIE from ..compat import ( compat_b64decode, compat_urllib_parse_unquote, @@ -6,10 +7,9 @@ from ..utils import ( ExtractorError, determine_ext, - update_url_query, traverse_obj, + update_url_query, ) -from .bokecc import BokeCCBaseIE class InfoQIE(BokeCCBaseIE): diff --git a/lib/yt_dlp/extractor/iprima.py b/lib/yt_dlp/extractor/iprima.py index f7aa579b3..d5a3d8095 100644 --- a/lib/yt_dlp/extractor/iprima.py +++ b/lib/yt_dlp/extractor/iprima.py @@ -3,12 +3,12 @@ from .common import InfoExtractor from ..utils import ( + ExtractorError, determine_ext, js_to_json, - urlencode_postdata, - ExtractorError, parse_qs, - traverse_obj + traverse_obj, + urlencode_postdata, ) diff --git a/lib/yt_dlp/extractor/iqiyi.py b/lib/yt_dlp/extractor/iqiyi.py index 3368ab1d9..85ed549de 100644 --- a/lib/yt_dlp/extractor/iqiyi.py +++ b/lib/yt_dlp/extractor/iqiyi.py @@ -4,20 +4,16 @@ import time from .common import InfoExtractor -from ..compat import ( - compat_str, - compat_urllib_parse_urlencode, - compat_urllib_parse_unquote -) from .openload import PhantomJSwrapper +from ..compat import compat_str, compat_urllib_parse_unquote, compat_urllib_parse_urlencode from ..utils import ( + ExtractorError, clean_html, decode_packed_codes, - ExtractorError, float_or_none, format_field, - get_element_by_id, get_element_by_attribute, + get_element_by_id, int_or_none, js_to_json, ohdave_rsa_encrypt, diff --git a/lib/yt_dlp/extractor/itprotv.py b/lib/yt_dlp/extractor/itprotv.py index 713fd4ec5..5d6fbaa01 100644 --- a/lib/yt_dlp/extractor/itprotv.py +++ b/lib/yt_dlp/extractor/itprotv.py @@ -1,12 +1,11 @@ import re from .common import InfoExtractor - from ..utils import ( int_or_none, str_or_none, traverse_obj, - urljoin + urljoin, ) diff --git a/lib/yt_dlp/extractor/itv.py b/lib/yt_dlp/extractor/itv.py index 9ac7be307..55c416521 100644 --- a/lib/yt_dlp/extractor/itv.py +++ b/lib/yt_dlp/extractor/itv.py @@ -1,23 +1,22 @@ import json -from .common import InfoExtractor from .brightcove import BrightcoveNewIE - +from .common import InfoExtractor from ..compat import compat_str from ..utils import ( + JSON_LD_RE, + ExtractorError, base_url, clean_html, determine_ext, extract_attributes, - ExtractorError, get_element_by_class, - JSON_LD_RE, merge_dicts, parse_duration, smuggle_url, try_get, - url_or_none, url_basename, + url_or_none, urljoin, ) diff --git a/lib/yt_dlp/extractor/iwara.py b/lib/yt_dlp/extractor/iwara.py index e23fdfd6a..a11f3f11d 100644 --- a/lib/yt_dlp/extractor/iwara.py +++ b/lib/yt_dlp/extractor/iwara.py @@ -1,9 +1,9 @@ import functools -import urllib.parse -import urllib.error import hashlib import json import time +import urllib.error +import urllib.parse from .common import InfoExtractor from ..utils import ( diff --git a/lib/yt_dlp/extractor/jamendo.py b/lib/yt_dlp/extractor/jamendo.py index a2bbba397..8557a81ad 100644 --- a/lib/yt_dlp/extractor/jamendo.py +++ b/lib/yt_dlp/extractor/jamendo.py @@ -1,8 +1,8 @@ import hashlib import random -from ..compat import compat_str from .common import InfoExtractor +from ..compat import compat_str from ..utils import ( clean_html, int_or_none, diff --git a/lib/yt_dlp/extractor/japandiet.py b/lib/yt_dlp/extractor/japandiet.py index 6c650568a..19d2b923b 100644 --- a/lib/yt_dlp/extractor/japandiet.py +++ b/lib/yt_dlp/extractor/japandiet.py @@ -1,5 +1,6 @@ import re +from .common import InfoExtractor from ..utils import ( ExtractorError, clean_html, @@ -9,9 +10,8 @@ smuggle_url, traverse_obj, try_call, - unsmuggle_url + unsmuggle_url, ) -from .common import InfoExtractor def _parse_japanese_date(text): diff --git a/lib/yt_dlp/extractor/jove.py b/lib/yt_dlp/extractor/jove.py index 245fe73d4..8069fea4c 100644 --- a/lib/yt_dlp/extractor/jove.py +++ b/lib/yt_dlp/extractor/jove.py @@ -1,8 +1,5 @@ from .common import InfoExtractor -from ..utils import ( - ExtractorError, - unified_strdate -) +from ..utils import ExtractorError, unified_strdate class JoveIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/jstream.py b/lib/yt_dlp/extractor/jstream.py index 3e2e62712..00ac7ccca 100644 --- a/lib/yt_dlp/extractor/jstream.py +++ b/lib/yt_dlp/extractor/jstream.py @@ -1,6 +1,6 @@ import base64 -import re import json +import re from .common import InfoExtractor from ..utils import ( diff --git a/lib/yt_dlp/extractor/kakao.py b/lib/yt_dlp/extractor/kakao.py index 43055e89d..563aa2d72 100644 --- a/lib/yt_dlp/extractor/kakao.py +++ b/lib/yt_dlp/extractor/kakao.py @@ -3,8 +3,8 @@ from ..utils import ( ExtractorError, int_or_none, - strip_or_none, str_or_none, + strip_or_none, traverse_obj, unified_timestamp, ) diff --git a/lib/yt_dlp/extractor/kaltura.py b/lib/yt_dlp/extractor/kaltura.py index 95e2deea5..4752d5a55 100644 --- a/lib/yt_dlp/extractor/kaltura.py +++ b/lib/yt_dlp/extractor/kaltura.py @@ -4,18 +4,18 @@ from .common import InfoExtractor from ..compat import ( - compat_urlparse, compat_parse_qs, + compat_urlparse, ) from ..utils import ( - clean_html, ExtractorError, + clean_html, format_field, int_or_none, - unsmuggle_url, + remove_start, smuggle_url, traverse_obj, - remove_start + unsmuggle_url, ) diff --git a/lib/yt_dlp/extractor/kankanews.py b/lib/yt_dlp/extractor/kankanews.py index 8f247b305..3d74c745c 100644 --- a/lib/yt_dlp/extractor/kankanews.py +++ b/lib/yt_dlp/extractor/kankanews.py @@ -1,7 +1,7 @@ -import time +import hashlib import random import string -import hashlib +import time import urllib.parse from .common import InfoExtractor diff --git a/lib/yt_dlp/extractor/kuwo.py b/lib/yt_dlp/extractor/kuwo.py index 3c93dedac..b77667160 100644 --- a/lib/yt_dlp/extractor/kuwo.py +++ b/lib/yt_dlp/extractor/kuwo.py @@ -3,10 +3,10 @@ from .common import InfoExtractor from ..compat import compat_urlparse from ..utils import ( - get_element_by_id, - clean_html, ExtractorError, InAdvancePagedList, + clean_html, + get_element_by_id, remove_start, ) diff --git a/lib/yt_dlp/extractor/lci.py b/lib/yt_dlp/extractor/lci.py index e7d2f8a24..708cb548d 100644 --- a/lib/yt_dlp/extractor/lci.py +++ b/lib/yt_dlp/extractor/lci.py @@ -1,9 +1,25 @@ from .common import InfoExtractor +from .wat import WatIE +from ..utils import ExtractorError, int_or_none +from ..utils.traversal import traverse_obj class LCIIE(InfoExtractor): - _VALID_URL = r'https?://(?:www\.)?(?:lci|tf1info)\.fr/[^/]+/[\w-]+-(?P\d+)\.html' + _VALID_URL = r'https?://(?:www\.)?(?:lci|tf1info)\.fr/(?:[^/?#]+/)+[\w-]+-(?P\d+)\.html' _TESTS = [{ + 'url': 'https://www.tf1info.fr/replay-lci/videos/video-24h-pujadas-du-vendredi-24-mai-6708-2300831.html', + 'info_dict': { + 'id': '14113788', + 'ext': 'mp4', + 'title': '24H Pujadas du vendredi 24 mai 2024', + 'thumbnail': 'https://photos.tf1.fr/1280/720/24h-pujadas-du-24-mai-2024-55bf2d-0@1x.jpg', + 'upload_date': '20240524', + 'duration': 6158, + }, + 'params': { + 'skip_download': True, + }, + }, { 'url': 'https://www.tf1info.fr/politique/election-presidentielle-2022-second-tour-j-2-marine-le-pen-et-emmanuel-macron-en-interview-de-lci-vendredi-soir-2217486.html', 'info_dict': { 'id': '13875948', @@ -24,5 +40,10 @@ class LCIIE(InfoExtractor): def _real_extract(self, url): video_id = self._match_id(url) webpage = self._download_webpage(url, video_id) - wat_id = self._search_regex(r'watId["\']?\s*:\s*["\']?(\d+)', webpage, 'wat id') - return self.url_result('wat:' + wat_id, 'Wat', wat_id) + next_data = self._search_nextjs_data(webpage, video_id) + wat_id = traverse_obj(next_data, ( + 'props', 'pageProps', 'page', 'tms', 'videos', {dict.keys}, ..., {int_or_none}, any)) + if wat_id is None: + raise ExtractorError('Could not find wat_id') + + return self.url_result(f'wat:{wat_id}', WatIE, str(wat_id)) diff --git a/lib/yt_dlp/extractor/lcp.py b/lib/yt_dlp/extractor/lcp.py index 9846319e0..62874195f 100644 --- a/lib/yt_dlp/extractor/lcp.py +++ b/lib/yt_dlp/extractor/lcp.py @@ -1,5 +1,5 @@ -from .common import InfoExtractor from .arkena import ArkenaIE +from .common import InfoExtractor class LcpPlayIE(ArkenaIE): # XXX: Do not subclass from concrete IE diff --git a/lib/yt_dlp/extractor/lecture2go.py b/lib/yt_dlp/extractor/lecture2go.py index 10fb5d479..1a3ada1e5 100644 --- a/lib/yt_dlp/extractor/lecture2go.py +++ b/lib/yt_dlp/extractor/lecture2go.py @@ -4,8 +4,8 @@ from ..utils import ( determine_ext, determine_protocol, - parse_duration, int_or_none, + parse_duration, ) diff --git a/lib/yt_dlp/extractor/lecturio.py b/lib/yt_dlp/extractor/lecturio.py index 629d208fc..90f0268d7 100644 --- a/lib/yt_dlp/extractor/lecturio.py +++ b/lib/yt_dlp/extractor/lecturio.py @@ -2,9 +2,9 @@ from .common import InfoExtractor from ..utils import ( + ExtractorError, clean_html, determine_ext, - ExtractorError, float_or_none, int_or_none, str_or_none, diff --git a/lib/yt_dlp/extractor/leeco.py b/lib/yt_dlp/extractor/leeco.py index 5d61a607f..a113b3d0d 100644 --- a/lib/yt_dlp/extractor/leeco.py +++ b/lib/yt_dlp/extractor/leeco.py @@ -11,9 +11,9 @@ compat_urllib_parse_urlencode, ) from ..utils import ( + ExtractorError, determine_ext, encode_data_uri, - ExtractorError, int_or_none, orderedSet, parse_iso8601, diff --git a/lib/yt_dlp/extractor/libraryofcongress.py b/lib/yt_dlp/extractor/libraryofcongress.py index b76ca0908..297993939 100644 --- a/lib/yt_dlp/extractor/libraryofcongress.py +++ b/lib/yt_dlp/extractor/libraryofcongress.py @@ -1,7 +1,6 @@ import re from .common import InfoExtractor - from ..utils import ( determine_ext, float_or_none, diff --git a/lib/yt_dlp/extractor/lifenews.py b/lib/yt_dlp/extractor/lifenews.py index 919cfcb37..ea150a58b 100644 --- a/lib/yt_dlp/extractor/lifenews.py +++ b/lib/yt_dlp/extractor/lifenews.py @@ -6,8 +6,8 @@ compat_urlparse, ) from ..utils import ( - determine_ext, ExtractorError, + determine_ext, int_or_none, parse_iso8601, remove_end, diff --git a/lib/yt_dlp/extractor/limelight.py b/lib/yt_dlp/extractor/limelight.py index 4e50f106f..1ff091ddb 100644 --- a/lib/yt_dlp/extractor/limelight.py +++ b/lib/yt_dlp/extractor/limelight.py @@ -3,13 +3,13 @@ from .common import InfoExtractor from ..networking.exceptions import HTTPError from ..utils import ( + ExtractorError, determine_ext, float_or_none, int_or_none, smuggle_url, try_get, unsmuggle_url, - ExtractorError, ) diff --git a/lib/yt_dlp/extractor/linkedin.py b/lib/yt_dlp/extractor/linkedin.py index e12f467ef..2a7c6f0e0 100644 --- a/lib/yt_dlp/extractor/linkedin.py +++ b/lib/yt_dlp/extractor/linkedin.py @@ -7,8 +7,8 @@ extract_attributes, float_or_none, int_or_none, - srt_subtitles_timecode, mimetype2ext, + srt_subtitles_timecode, traverse_obj, try_get, url_or_none, diff --git a/lib/yt_dlp/extractor/mainstreaming.py b/lib/yt_dlp/extractor/mainstreaming.py index fd9bba8bc..fa12a6a8d 100644 --- a/lib/yt_dlp/extractor/mainstreaming.py +++ b/lib/yt_dlp/extractor/mainstreaming.py @@ -1,14 +1,13 @@ import re from .common import InfoExtractor - from ..utils import ( int_or_none, js_to_json, parse_duration, traverse_obj, try_get, - urljoin + urljoin, ) diff --git a/lib/yt_dlp/extractor/manoto.py b/lib/yt_dlp/extractor/manoto.py index 2792e6e70..44c321c26 100644 --- a/lib/yt_dlp/extractor/manoto.py +++ b/lib/yt_dlp/extractor/manoto.py @@ -1,10 +1,5 @@ from .common import InfoExtractor -from ..utils import ( - clean_html, - int_or_none, - traverse_obj -) - +from ..utils import clean_html, int_or_none, traverse_obj _API_URL = 'https://dak1vd5vmi7x6.cloudfront.net/api/v1/publicrole/{}/{}?id={}' diff --git a/lib/yt_dlp/extractor/medaltv.py b/lib/yt_dlp/extractor/medaltv.py index 675ad8ccc..d040fb48f 100644 --- a/lib/yt_dlp/extractor/medaltv.py +++ b/lib/yt_dlp/extractor/medaltv.py @@ -4,8 +4,8 @@ from ..compat import compat_str from ..utils import ( ExtractorError, - format_field, float_or_none, + format_field, int_or_none, str_or_none, traverse_obj, diff --git a/lib/yt_dlp/extractor/mediaklikk.py b/lib/yt_dlp/extractor/mediaklikk.py index fcc4827b5..c01597762 100644 --- a/lib/yt_dlp/extractor/mediaklikk.py +++ b/lib/yt_dlp/extractor/mediaklikk.py @@ -1,14 +1,11 @@ +from .common import InfoExtractor +from ..compat import compat_str, compat_urllib_parse_unquote from ..utils import ( ExtractorError, traverse_obj, unified_strdate, url_or_none, ) -from .common import InfoExtractor -from ..compat import ( - compat_urllib_parse_unquote, - compat_str -) class MediaKlikkIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/mediaset.py b/lib/yt_dlp/extractor/mediaset.py index e04a1ce90..b7df5c75a 100644 --- a/lib/yt_dlp/extractor/mediaset.py +++ b/lib/yt_dlp/extractor/mediaset.py @@ -5,11 +5,11 @@ from ..utils import ( ExtractorError, GeoRestrictedError, - int_or_none, OnDemandPagedList, + int_or_none, try_get, - urljoin, update_url_query, + urljoin, ) diff --git a/lib/yt_dlp/extractor/mediasite.py b/lib/yt_dlp/extractor/mediasite.py index 7ea78ab69..d3fec4ec2 100644 --- a/lib/yt_dlp/extractor/mediasite.py +++ b/lib/yt_dlp/extractor/mediasite.py @@ -1,5 +1,5 @@ -import re import json +import re from .common import InfoExtractor from ..compat import ( @@ -10,16 +10,15 @@ ExtractorError, float_or_none, mimetype2ext, + smuggle_url, str_or_none, try_call, try_get, - smuggle_url, unsmuggle_url, url_or_none, urljoin, ) - _ID_RE = r'(?:[0-9a-f]{32,34}|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12,14})' diff --git a/lib/yt_dlp/extractor/microsoftstream.py b/lib/yt_dlp/extractor/microsoftstream.py index 5f5f16087..f6a0b416d 100644 --- a/lib/yt_dlp/extractor/microsoftstream.py +++ b/lib/yt_dlp/extractor/microsoftstream.py @@ -3,8 +3,8 @@ from .common import InfoExtractor from ..utils import ( merge_dicts, - parse_iso8601, parse_duration, + parse_iso8601, parse_resolution, try_get, url_basename, diff --git a/lib/yt_dlp/extractor/mildom.py b/lib/yt_dlp/extractor/mildom.py index f64d575dc..caf60c805 100644 --- a/lib/yt_dlp/extractor/mildom.py +++ b/lib/yt_dlp/extractor/mildom.py @@ -4,11 +4,11 @@ from .common import InfoExtractor from ..utils import ( + ExtractorError, + OnDemandPagedList, determine_ext, dict_get, - ExtractorError, float_or_none, - OnDemandPagedList, traverse_obj, ) diff --git a/lib/yt_dlp/extractor/mit.py b/lib/yt_dlp/extractor/mit.py index 38cc0c274..979584ed6 100644 --- a/lib/yt_dlp/extractor/mit.py +++ b/lib/yt_dlp/extractor/mit.py @@ -1,11 +1,11 @@ -import re import json +import re from .common import InfoExtractor from .youtube import YoutubeIE from ..utils import ( - clean_html, ExtractorError, + clean_html, get_element_by_id, ) diff --git a/lib/yt_dlp/extractor/monstercat.py b/lib/yt_dlp/extractor/monstercat.py index a69a12e18..411d41cb0 100644 --- a/lib/yt_dlp/extractor/monstercat.py +++ b/lib/yt_dlp/extractor/monstercat.py @@ -8,10 +8,10 @@ get_element_html_by_class, get_element_text_and_html_by_tag, int_or_none, - unified_strdate, strip_or_none, traverse_obj, try_call, + unified_strdate, ) diff --git a/lib/yt_dlp/extractor/moviepilot.py b/lib/yt_dlp/extractor/moviepilot.py index 35c57bc70..ed5be4fa6 100644 --- a/lib/yt_dlp/extractor/moviepilot.py +++ b/lib/yt_dlp/extractor/moviepilot.py @@ -1,5 +1,5 @@ -from .dailymotion import DailymotionIE from .common import InfoExtractor +from .dailymotion import DailymotionIE class MoviepilotIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/movingimage.py b/lib/yt_dlp/extractor/movingimage.py index cdd8ba4dc..6e0ea2652 100644 --- a/lib/yt_dlp/extractor/movingimage.py +++ b/lib/yt_dlp/extractor/movingimage.py @@ -1,7 +1,7 @@ from .common import InfoExtractor from ..utils import ( - unescapeHTML, parse_duration, + unescapeHTML, ) diff --git a/lib/yt_dlp/extractor/msn.py b/lib/yt_dlp/extractor/msn.py index 77d1806a3..79728e106 100644 --- a/lib/yt_dlp/extractor/msn.py +++ b/lib/yt_dlp/extractor/msn.py @@ -3,8 +3,8 @@ from .common import InfoExtractor from ..compat import compat_str from ..utils import ( - determine_ext, ExtractorError, + determine_ext, int_or_none, unescapeHTML, ) diff --git a/lib/yt_dlp/extractor/n1.py b/lib/yt_dlp/extractor/n1.py index edc41443a..8a8a5fec7 100644 --- a/lib/yt_dlp/extractor/n1.py +++ b/lib/yt_dlp/extractor/n1.py @@ -2,8 +2,8 @@ from .common import InfoExtractor from ..utils import ( - unified_timestamp, extract_attributes, + unified_timestamp, ) diff --git a/lib/yt_dlp/extractor/naver.py b/lib/yt_dlp/extractor/naver.py index 885557e91..26400e383 100644 --- a/lib/yt_dlp/extractor/naver.py +++ b/lib/yt_dlp/extractor/naver.py @@ -4,8 +4,8 @@ import itertools import json import re -import urllib.parse import time +import urllib.parse from .common import InfoExtractor from ..utils import ( diff --git a/lib/yt_dlp/extractor/nba.py b/lib/yt_dlp/extractor/nba.py index 81d11e3a5..ec4d6368e 100644 --- a/lib/yt_dlp/extractor/nba.py +++ b/lib/yt_dlp/extractor/nba.py @@ -7,9 +7,9 @@ compat_urllib_parse_unquote, ) from ..utils import ( + OnDemandPagedList, int_or_none, merge_dicts, - OnDemandPagedList, parse_duration, parse_iso8601, parse_qs, diff --git a/lib/yt_dlp/extractor/nbc.py b/lib/yt_dlp/extractor/nbc.py index 267fa8353..e88f98abf 100644 --- a/lib/yt_dlp/extractor/nbc.py +++ b/lib/yt_dlp/extractor/nbc.py @@ -3,9 +3,9 @@ import re import xml.etree.ElementTree +from .adobepass import AdobePassIE from .common import InfoExtractor from .theplatform import ThePlatformIE, default_ns -from .adobepass import AdobePassIE from ..compat import compat_urllib_parse_unquote from ..networking import HEADRequest from ..utils import ( diff --git a/lib/yt_dlp/extractor/ndr.py b/lib/yt_dlp/extractor/ndr.py index 41ea3629a..243221d46 100644 --- a/lib/yt_dlp/extractor/ndr.py +++ b/lib/yt_dlp/extractor/ndr.py @@ -3,8 +3,8 @@ from .common import InfoExtractor from ..compat import compat_urllib_parse_urlparse from ..utils import ( - determine_ext, ExtractorError, + determine_ext, int_or_none, merge_dicts, parse_iso8601, diff --git a/lib/yt_dlp/extractor/nfhsnetwork.py b/lib/yt_dlp/extractor/nfhsnetwork.py index febad8fdf..be732a32f 100644 --- a/lib/yt_dlp/extractor/nfhsnetwork.py +++ b/lib/yt_dlp/extractor/nfhsnetwork.py @@ -1,11 +1,5 @@ from .common import InfoExtractor - - -from ..utils import ( - try_get, - unified_strdate, - unified_timestamp -) +from ..utils import try_get, unified_strdate, unified_timestamp class NFHSNetworkIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/nhl.py b/lib/yt_dlp/extractor/nhl.py index 2521c40e0..64cddb408 100644 --- a/lib/yt_dlp/extractor/nhl.py +++ b/lib/yt_dlp/extractor/nhl.py @@ -3,8 +3,8 @@ from ..utils import ( determine_ext, int_or_none, - parse_iso8601, parse_duration, + parse_iso8601, ) diff --git a/lib/yt_dlp/extractor/ninenews.py b/lib/yt_dlp/extractor/ninenews.py index 900d9ba60..0b4f47b48 100644 --- a/lib/yt_dlp/extractor/ninenews.py +++ b/lib/yt_dlp/extractor/ninenews.py @@ -1,5 +1,5 @@ -from .common import InfoExtractor from .brightcove import BrightcoveNewIE +from .common import InfoExtractor from ..utils import ExtractorError from ..utils.traversal import traverse_obj diff --git a/lib/yt_dlp/extractor/ninenow.py b/lib/yt_dlp/extractor/ninenow.py index c655b75f4..b7170b0e7 100644 --- a/lib/yt_dlp/extractor/ninenow.py +++ b/lib/yt_dlp/extractor/ninenow.py @@ -2,8 +2,8 @@ from ..compat import compat_str from ..utils import ( ExtractorError, - int_or_none, float_or_none, + int_or_none, smuggle_url, str_or_none, try_get, diff --git a/lib/yt_dlp/extractor/nitter.py b/lib/yt_dlp/extractor/nitter.py index 35d1311dc..249e7cd33 100644 --- a/lib/yt_dlp/extractor/nitter.py +++ b/lib/yt_dlp/extractor/nitter.py @@ -1,13 +1,14 @@ +import random +import re + from .common import InfoExtractor from ..compat import compat_urlparse from ..utils import ( + determine_ext, parse_count, - unified_timestamp, remove_end, - determine_ext, + unified_timestamp, ) -import re -import random class NitterIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/nobelprize.py b/lib/yt_dlp/extractor/nobelprize.py index cddc72f71..513529bea 100644 --- a/lib/yt_dlp/extractor/nobelprize.py +++ b/lib/yt_dlp/extractor/nobelprize.py @@ -1,11 +1,11 @@ from .common import InfoExtractor from ..utils import ( - js_to_json, - mimetype2ext, determine_ext, - update_url_query, get_element_by_attribute, int_or_none, + js_to_json, + mimetype2ext, + update_url_query, ) diff --git a/lib/yt_dlp/extractor/noz.py b/lib/yt_dlp/extractor/noz.py index c7b803803..19cb972c0 100644 --- a/lib/yt_dlp/extractor/noz.py +++ b/lib/yt_dlp/extractor/noz.py @@ -1,11 +1,11 @@ from .common import InfoExtractor +from ..compat import compat_urllib_parse_unquote from ..utils import ( - int_or_none, find_xpath_attr, - xpath_text, + int_or_none, update_url_query, + xpath_text, ) -from ..compat import compat_urllib_parse_unquote class NozIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/nuevo.py b/lib/yt_dlp/extractor/nuevo.py index ec54041f1..5670445aa 100644 --- a/lib/yt_dlp/extractor/nuevo.py +++ b/lib/yt_dlp/extractor/nuevo.py @@ -1,9 +1,5 @@ from .common import InfoExtractor - -from ..utils import ( - float_or_none, - xpath_text -) +from ..utils import float_or_none, xpath_text class NuevoBaseIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/nuvid.py b/lib/yt_dlp/extractor/nuvid.py index 6ac351cb0..0ef0ec70b 100644 --- a/lib/yt_dlp/extractor/nuvid.py +++ b/lib/yt_dlp/extractor/nuvid.py @@ -2,8 +2,8 @@ from .common import InfoExtractor from ..utils import ( - parse_duration, int_or_none, + parse_duration, strip_or_none, traverse_obj, url_or_none, diff --git a/lib/yt_dlp/extractor/nzherald.py b/lib/yt_dlp/extractor/nzherald.py index 062f9a875..0a12aea71 100644 --- a/lib/yt_dlp/extractor/nzherald.py +++ b/lib/yt_dlp/extractor/nzherald.py @@ -3,10 +3,7 @@ from .brightcove import BrightcoveNewIE from .common import InfoExtractor from ..compat import compat_str -from ..utils import ( - ExtractorError, - traverse_obj -) +from ..utils import ExtractorError, traverse_obj class NZHeraldIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/odkmedia.py b/lib/yt_dlp/extractor/odkmedia.py index b852160b9..8321b0741 100644 --- a/lib/yt_dlp/extractor/odkmedia.py +++ b/lib/yt_dlp/extractor/odkmedia.py @@ -7,7 +7,7 @@ GeoRestrictedError, float_or_none, traverse_obj, - try_call + try_call, ) diff --git a/lib/yt_dlp/extractor/olympics.py b/lib/yt_dlp/extractor/olympics.py index 61d1f4048..5507d2fda 100644 --- a/lib/yt_dlp/extractor/olympics.py +++ b/lib/yt_dlp/extractor/olympics.py @@ -1,8 +1,5 @@ from .common import InfoExtractor -from ..utils import ( - int_or_none, - try_get -) +from ..utils import int_or_none, try_get class OlympicsReplayIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/onenewsnz.py b/lib/yt_dlp/extractor/onenewsnz.py index a46211e77..351b397de 100644 --- a/lib/yt_dlp/extractor/onenewsnz.py +++ b/lib/yt_dlp/extractor/onenewsnz.py @@ -1,10 +1,6 @@ from .brightcove import BrightcoveNewIE from .common import InfoExtractor - -from ..utils import ( - ExtractorError, - traverse_obj -) +from ..utils import ExtractorError, traverse_obj class OneNewsNZIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/onet.py b/lib/yt_dlp/extractor/onet.py index 0d59e8cb4..da10f3779 100644 --- a/lib/yt_dlp/extractor/onet.py +++ b/lib/yt_dlp/extractor/onet.py @@ -2,13 +2,13 @@ from .common import InfoExtractor from ..utils import ( - determine_ext, + NO_DEFAULT, ExtractorError, + determine_ext, float_or_none, get_element_by_class, int_or_none, js_to_json, - NO_DEFAULT, parse_iso8601, remove_start, strip_or_none, diff --git a/lib/yt_dlp/extractor/opencast.py b/lib/yt_dlp/extractor/opencast.py index 1fafd9afb..12bf55704 100644 --- a/lib/yt_dlp/extractor/opencast.py +++ b/lib/yt_dlp/extractor/opencast.py @@ -2,8 +2,8 @@ from .common import InfoExtractor from ..utils import ( - determine_ext, ExtractorError, + determine_ext, int_or_none, parse_iso8601, traverse_obj, diff --git a/lib/yt_dlp/extractor/openrec.py b/lib/yt_dlp/extractor/openrec.py index 82a81c6c2..c9a96aeb4 100644 --- a/lib/yt_dlp/extractor/openrec.py +++ b/lib/yt_dlp/extractor/openrec.py @@ -1,4 +1,5 @@ from .common import InfoExtractor +from ..compat import compat_str from ..utils import ( ExtractorError, get_first, @@ -8,7 +9,6 @@ unified_strdate, unified_timestamp, ) -from ..compat import compat_str class OpenRecBaseIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/ora.py b/lib/yt_dlp/extractor/ora.py index d49909d52..0e7a8484e 100644 --- a/lib/yt_dlp/extractor/ora.py +++ b/lib/yt_dlp/extractor/ora.py @@ -1,4 +1,5 @@ import re + from .common import InfoExtractor from ..compat import compat_urlparse from ..utils import ( diff --git a/lib/yt_dlp/extractor/packtpub.py b/lib/yt_dlp/extractor/packtpub.py index 56203306f..3e969c846 100644 --- a/lib/yt_dlp/extractor/packtpub.py +++ b/lib/yt_dlp/extractor/packtpub.py @@ -3,13 +3,12 @@ from .common import InfoExtractor from ..networking.exceptions import HTTPError from ..utils import ( - clean_html, ExtractorError, + clean_html, # remove_end, str_or_none, strip_or_none, unified_timestamp, - # urljoin, ) diff --git a/lib/yt_dlp/extractor/panopto.py b/lib/yt_dlp/extractor/panopto.py index 63c5fd68f..6b2596236 100644 --- a/lib/yt_dlp/extractor/panopto.py +++ b/lib/yt_dlp/extractor/panopto.py @@ -5,17 +5,13 @@ import random from .common import InfoExtractor -from ..compat import ( - compat_urllib_parse_urlparse, - compat_urlparse -) - +from ..compat import compat_urllib_parse_urlparse, compat_urlparse from ..utils import ( - bug_reports_message, ExtractorError, + OnDemandPagedList, + bug_reports_message, get_first, int_or_none, - OnDemandPagedList, parse_qs, srt_subtitles_timecode, traverse_obj, diff --git a/lib/yt_dlp/extractor/paramountplus.py b/lib/yt_dlp/extractor/paramountplus.py index 7e472a63e..3f19803c0 100644 --- a/lib/yt_dlp/extractor/paramountplus.py +++ b/lib/yt_dlp/extractor/paramountplus.py @@ -1,7 +1,7 @@ import itertools -from .common import InfoExtractor from .cbs import CBSBaseIE +from .common import InfoExtractor from ..utils import ( ExtractorError, int_or_none, diff --git a/lib/yt_dlp/extractor/pbs.py b/lib/yt_dlp/extractor/pbs.py index 2bb2ea9f1..f6f5a5c3e 100644 --- a/lib/yt_dlp/extractor/pbs.py +++ b/lib/yt_dlp/extractor/pbs.py @@ -3,10 +3,11 @@ from .common import InfoExtractor from ..compat import compat_str from ..utils import ( + US_RATINGS, ExtractorError, determine_ext, - int_or_none, float_or_none, + int_or_none, js_to_json, orderedSet, strip_jsonp, @@ -14,7 +15,6 @@ traverse_obj, unified_strdate, url_or_none, - US_RATINGS, ) diff --git a/lib/yt_dlp/extractor/pearvideo.py b/lib/yt_dlp/extractor/pearvideo.py index e27e5a7ba..086eaaf00 100644 --- a/lib/yt_dlp/extractor/pearvideo.py +++ b/lib/yt_dlp/extractor/pearvideo.py @@ -3,8 +3,8 @@ from .common import InfoExtractor from ..utils import ( qualities, - unified_timestamp, traverse_obj, + unified_timestamp, ) diff --git a/lib/yt_dlp/extractor/peertube.py b/lib/yt_dlp/extractor/peertube.py index 730b2393e..b7919c073 100644 --- a/lib/yt_dlp/extractor/peertube.py +++ b/lib/yt_dlp/extractor/peertube.py @@ -4,6 +4,7 @@ from .common import InfoExtractor from ..compat import compat_str from ..utils import ( + OnDemandPagedList, format_field, int_or_none, parse_resolution, @@ -12,7 +13,6 @@ unified_timestamp, url_or_none, urljoin, - OnDemandPagedList, ) diff --git a/lib/yt_dlp/extractor/piksel.py b/lib/yt_dlp/extractor/piksel.py index 97a9bf574..02ae2fe1a 100644 --- a/lib/yt_dlp/extractor/piksel.py +++ b/lib/yt_dlp/extractor/piksel.py @@ -2,8 +2,8 @@ from .common import InfoExtractor from ..utils import ( - dict_get, ExtractorError, + dict_get, int_or_none, join_nonempty, parse_iso8601, @@ -25,29 +25,31 @@ class PikselIE(InfoExtractor): )| (?:api|player)\.multicastmedia| (?:api-ovp|player)\.piksel - )\.com| + )\.(?:com|tech)| (?: mz-edge\.stream\.co| movie-s\.nhk\.or )\.jp| vidego\.baltimorecity\.gov )/v/(?:refid/(?P[^/]+)/prefid/)?(?P[\w-]+)''' - _EMBED_REGEX = [r']+src=["\'](?P(?:https?:)?//player\.piksel\.com/v/[a-z0-9]+)'] + _EMBED_REGEX = [r']+src=["\'](?P(?:https?:)?//player\.piksel\.(?:com|tech)/v/[a-z0-9]+)'] _TESTS = [ { - 'url': 'http://player.piksel.com/v/ums2867l', + 'url': 'http://player.piksel.tech/v/ums2867l', 'md5': '34e34c8d89dc2559976a6079db531e85', 'info_dict': { 'id': 'ums2867l', 'ext': 'mp4', 'title': 'GX-005 with Caption', 'timestamp': 1481335659, - 'upload_date': '20161210' + 'upload_date': '20161210', + 'description': '', + 'thumbnail': 'https://thumbs.piksel.tech/thumbs/aid/t1488331553/3238987.jpg?w=640&h=480', } }, { # Original source: http://www.uscourts.gov/cameras-courts/state-washington-vs-donald-j-trump-et-al - 'url': 'https://player.piksel.com/v/v80kqp41', + 'url': 'https://player.piksel.tech/v/v80kqp41', 'md5': '753ddcd8cc8e4fa2dda4b7be0e77744d', 'info_dict': { 'id': 'v80kqp41', @@ -55,7 +57,8 @@ class PikselIE(InfoExtractor): 'title': 'WAW- State of Washington vs. Donald J. Trump, et al', 'description': 'State of Washington vs. Donald J. Trump, et al, Case Number 17-CV-00141-JLR, TRO Hearing, Civil Rights Case, 02/3/2017, 1:00 PM (PST), Seattle Federal Courthouse, Seattle, WA, Judge James L. Robart presiding.', 'timestamp': 1486171129, - 'upload_date': '20170204' + 'upload_date': '20170204', + 'thumbnail': 'https://thumbs.piksel.tech/thumbs/aid/t1495569155/3279887.jpg?w=640&h=360', } }, { @@ -65,7 +68,7 @@ class PikselIE(InfoExtractor): } ] - def _call_api(self, app_token, resource, display_id, query, host='https://player.piksel.com', fatal=True): + def _call_api(self, app_token, resource, display_id, query, host='https://player.piksel.tech', fatal=True): url = urljoin(host, f'/ws/ws_{resource}/api/{app_token}/mode/json/apiv/5') response = traverse_obj( self._download_json(url, display_id, query=query, fatal=fatal), ('response', {dict})) or {} @@ -146,7 +149,7 @@ def process_asset_files(asset_files): smil_url = dict_get(video_data, ['httpSmil', 'hdSmil', 'rtmpSmil']) if smil_url: - transform_source = None + transform_source = lambda x: x.replace('src="/', 'src="') if ref_id == 'nhkworld': # TODO: figure out if this is something to be fixed in urljoin, # _parse_smil_formats or keep it here diff --git a/lib/yt_dlp/extractor/pladform.py b/lib/yt_dlp/extractor/pladform.py index d67f6005c..c72a3876c 100644 --- a/lib/yt_dlp/extractor/pladform.py +++ b/lib/yt_dlp/extractor/pladform.py @@ -1,11 +1,11 @@ from .common import InfoExtractor from ..utils import ( - determine_ext, ExtractorError, + determine_ext, int_or_none, parse_qs, - xpath_text, qualities, + xpath_text, ) diff --git a/lib/yt_dlp/extractor/platzi.py b/lib/yt_dlp/extractor/platzi.py index 166b98c4a..d978c080b 100644 --- a/lib/yt_dlp/extractor/platzi.py +++ b/lib/yt_dlp/extractor/platzi.py @@ -4,8 +4,8 @@ compat_str, ) from ..utils import ( - clean_html, ExtractorError, + clean_html, int_or_none, str_or_none, try_get, diff --git a/lib/yt_dlp/extractor/playtvak.py b/lib/yt_dlp/extractor/playtvak.py index c418f88cb..a01b42290 100644 --- a/lib/yt_dlp/extractor/playtvak.py +++ b/lib/yt_dlp/extractor/playtvak.py @@ -1,7 +1,7 @@ from .common import InfoExtractor from ..compat import ( - compat_urlparse, compat_urllib_parse_urlencode, + compat_urlparse, ) from ..utils import ( ExtractorError, diff --git a/lib/yt_dlp/extractor/pluralsight.py b/lib/yt_dlp/extractor/pluralsight.py index 809b65608..60c9efffe 100644 --- a/lib/yt_dlp/extractor/pluralsight.py +++ b/lib/yt_dlp/extractor/pluralsight.py @@ -10,8 +10,8 @@ compat_urlparse, ) from ..utils import ( - dict_get, ExtractorError, + dict_get, float_or_none, int_or_none, parse_duration, diff --git a/lib/yt_dlp/extractor/polsatgo.py b/lib/yt_dlp/extractor/polsatgo.py index 1cebb365e..ecf2132b4 100644 --- a/lib/yt_dlp/extractor/polsatgo.py +++ b/lib/yt_dlp/extractor/polsatgo.py @@ -3,10 +3,10 @@ from .common import InfoExtractor from ..utils import ( + ExtractorError, int_or_none, try_get, url_or_none, - ExtractorError, ) diff --git a/lib/yt_dlp/extractor/pornflip.py b/lib/yt_dlp/extractor/pornflip.py index 51a9cf38f..d711d3e67 100644 --- a/lib/yt_dlp/extractor/pornflip.py +++ b/lib/yt_dlp/extractor/pornflip.py @@ -1,9 +1,5 @@ from .common import InfoExtractor -from ..utils import ( - int_or_none, - parse_duration, - parse_iso8601 -) +from ..utils import int_or_none, parse_duration, parse_iso8601 class PornFlipIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/pornovoisines.py b/lib/yt_dlp/extractor/pornovoisines.py index 2e51b4f6b..b8e8701a8 100644 --- a/lib/yt_dlp/extractor/pornovoisines.py +++ b/lib/yt_dlp/extractor/pornovoisines.py @@ -1,7 +1,7 @@ from .common import InfoExtractor from ..utils import ( - int_or_none, float_or_none, + int_or_none, unified_strdate, ) diff --git a/lib/yt_dlp/extractor/prx.py b/lib/yt_dlp/extractor/prx.py index 5bb183270..338794ed5 100644 --- a/lib/yt_dlp/extractor/prx.py +++ b/lib/yt_dlp/extractor/prx.py @@ -1,14 +1,15 @@ import itertools + from .common import InfoExtractor, SearchInfoExtractor from ..utils import ( - urljoin, - traverse_obj, + clean_html, int_or_none, mimetype2ext, - clean_html, - url_or_none, - unified_timestamp, str_or_none, + traverse_obj, + unified_timestamp, + url_or_none, + urljoin, ) diff --git a/lib/yt_dlp/extractor/puhutv.py b/lib/yt_dlp/extractor/puhutv.py index 4b8e5e90d..fc4c29e95 100644 --- a/lib/yt_dlp/extractor/puhutv.py +++ b/lib/yt_dlp/extractor/puhutv.py @@ -3,8 +3,8 @@ from ..networking.exceptions import HTTPError from ..utils import ( ExtractorError, - int_or_none, float_or_none, + int_or_none, parse_resolution, str_or_none, try_get, diff --git a/lib/yt_dlp/extractor/qingting.py b/lib/yt_dlp/extractor/qingting.py index aa690d492..cb00de2d5 100644 --- a/lib/yt_dlp/extractor/qingting.py +++ b/lib/yt_dlp/extractor/qingting.py @@ -1,5 +1,4 @@ from .common import InfoExtractor - from ..utils import traverse_obj diff --git a/lib/yt_dlp/extractor/qqmusic.py b/lib/yt_dlp/extractor/qqmusic.py index 92858259a..90141e63b 100644 --- a/lib/yt_dlp/extractor/qqmusic.py +++ b/lib/yt_dlp/extractor/qqmusic.py @@ -4,8 +4,8 @@ from .common import InfoExtractor from ..utils import ( - clean_html, ExtractorError, + clean_html, strip_jsonp, unescapeHTML, ) diff --git a/lib/yt_dlp/extractor/radiocanada.py b/lib/yt_dlp/extractor/radiocanada.py index 1a5a6355a..4a09dcdfc 100644 --- a/lib/yt_dlp/extractor/radiocanada.py +++ b/lib/yt_dlp/extractor/radiocanada.py @@ -1,8 +1,8 @@ from .common import InfoExtractor from ..networking.exceptions import HTTPError from ..utils import ( - determine_ext, ExtractorError, + determine_ext, int_or_none, unified_strdate, ) diff --git a/lib/yt_dlp/extractor/radiocomercial.py b/lib/yt_dlp/extractor/radiocomercial.py index 38f8cf786..0c219778f 100644 --- a/lib/yt_dlp/extractor/radiocomercial.py +++ b/lib/yt_dlp/extractor/radiocomercial.py @@ -14,7 +14,7 @@ try_call, unified_strdate, update_url, - urljoin + urljoin, ) from ..utils.traversal import traverse_obj diff --git a/lib/yt_dlp/extractor/radiozet.py b/lib/yt_dlp/extractor/radiozet.py index 67520172e..632c8c281 100644 --- a/lib/yt_dlp/extractor/radiozet.py +++ b/lib/yt_dlp/extractor/radiozet.py @@ -1,7 +1,7 @@ from .common import InfoExtractor from ..utils import ( - traverse_obj, strip_or_none, + traverse_obj, ) diff --git a/lib/yt_dlp/extractor/radlive.py b/lib/yt_dlp/extractor/radlive.py index 3c00183be..325e278fc 100644 --- a/lib/yt_dlp/extractor/radlive.py +++ b/lib/yt_dlp/extractor/radlive.py @@ -1,13 +1,13 @@ import json +from .common import InfoExtractor from ..utils import ( ExtractorError, format_field, traverse_obj, try_get, - unified_timestamp + unified_timestamp, ) -from .common import InfoExtractor class RadLiveIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/rai.py b/lib/yt_dlp/extractor/rai.py index c1fc65c81..c2e7a6fb8 100644 --- a/lib/yt_dlp/extractor/rai.py +++ b/lib/yt_dlp/extractor/rai.py @@ -3,11 +3,11 @@ from .common import InfoExtractor from ..networking import HEADRequest from ..utils import ( + ExtractorError, + GeoRestrictedError, clean_html, determine_ext, - ExtractorError, filter_dict, - GeoRestrictedError, int_or_none, join_nonempty, parse_duration, diff --git a/lib/yt_dlp/extractor/rbgtum.py b/lib/yt_dlp/extractor/rbgtum.py index 54f194cbd..5f2d0c103 100644 --- a/lib/yt_dlp/extractor/rbgtum.py +++ b/lib/yt_dlp/extractor/rbgtum.py @@ -1,7 +1,7 @@ import re from .common import InfoExtractor -from ..utils import parse_qs, remove_start, traverse_obj, ExtractorError +from ..utils import ExtractorError, parse_qs, remove_start, traverse_obj class RbgTumIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/rcti.py b/lib/yt_dlp/extractor/rcti.py index 6a7c7f399..9c382e257 100644 --- a/lib/yt_dlp/extractor/rcti.py +++ b/lib/yt_dlp/extractor/rcti.py @@ -5,11 +5,11 @@ from .common import InfoExtractor from ..networking.exceptions import HTTPError from ..utils import ( - dict_get, ExtractorError, + dict_get, strip_or_none, traverse_obj, - try_get + try_get, ) diff --git a/lib/yt_dlp/extractor/rds.py b/lib/yt_dlp/extractor/rds.py index 1a1c6634e..cc76b898a 100644 --- a/lib/yt_dlp/extractor/rds.py +++ b/lib/yt_dlp/extractor/rds.py @@ -1,10 +1,10 @@ from .common import InfoExtractor +from ..compat import compat_str from ..utils import ( + js_to_json, parse_duration, parse_iso8601, - js_to_json, ) -from ..compat import compat_str class RDSIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/redbulltv.py b/lib/yt_dlp/extractor/redbulltv.py index d1de2490f..fac51b9ef 100644 --- a/lib/yt_dlp/extractor/redbulltv.py +++ b/lib/yt_dlp/extractor/redbulltv.py @@ -1,8 +1,8 @@ from .common import InfoExtractor from ..networking.exceptions import HTTPError from ..utils import ( - float_or_none, ExtractorError, + float_or_none, ) diff --git a/lib/yt_dlp/extractor/reddit.py b/lib/yt_dlp/extractor/reddit.py index 44c0353da..bc3e5f7ee 100644 --- a/lib/yt_dlp/extractor/reddit.py +++ b/lib/yt_dlp/extractor/reddit.py @@ -10,8 +10,8 @@ try_get, unescapeHTML, update_url_query, - urlencode_postdata, url_or_none, + urlencode_postdata, ) diff --git a/lib/yt_dlp/extractor/redgifs.py b/lib/yt_dlp/extractor/redgifs.py index f9453202b..d0546bbfa 100644 --- a/lib/yt_dlp/extractor/redgifs.py +++ b/lib/yt_dlp/extractor/redgifs.py @@ -5,10 +5,10 @@ from ..networking.exceptions import HTTPError from ..utils import ( ExtractorError, + OnDemandPagedList, int_or_none, qualities, try_get, - OnDemandPagedList, ) diff --git a/lib/yt_dlp/extractor/redtube.py b/lib/yt_dlp/extractor/redtube.py index 965abbee8..14ed0edab 100644 --- a/lib/yt_dlp/extractor/redtube.py +++ b/lib/yt_dlp/extractor/redtube.py @@ -1,7 +1,7 @@ from .common import InfoExtractor from ..utils import ( - determine_ext, ExtractorError, + determine_ext, int_or_none, merge_dicts, str_to_int, diff --git a/lib/yt_dlp/extractor/reuters.py b/lib/yt_dlp/extractor/reuters.py index 0a8f13b9f..9c9bac6af 100644 --- a/lib/yt_dlp/extractor/reuters.py +++ b/lib/yt_dlp/extractor/reuters.py @@ -2,8 +2,8 @@ from .common import InfoExtractor from ..utils import ( - js_to_json, int_or_none, + js_to_json, unescapeHTML, ) diff --git a/lib/yt_dlp/extractor/rmcdecouverte.py b/lib/yt_dlp/extractor/rmcdecouverte.py index 8d29b302b..bc59ed07e 100644 --- a/lib/yt_dlp/extractor/rmcdecouverte.py +++ b/lib/yt_dlp/extractor/rmcdecouverte.py @@ -1,5 +1,5 @@ -from .common import InfoExtractor from .brightcove import BrightcoveLegacyIE +from .common import InfoExtractor from ..compat import ( compat_parse_qs, compat_urlparse, diff --git a/lib/yt_dlp/extractor/rte.py b/lib/yt_dlp/extractor/rte.py index 7ba80d4ba..729804d23 100644 --- a/lib/yt_dlp/extractor/rte.py +++ b/lib/yt_dlp/extractor/rte.py @@ -3,13 +3,13 @@ from .common import InfoExtractor from ..networking.exceptions import HTTPError from ..utils import ( + ExtractorError, float_or_none, parse_iso8601, str_or_none, try_get, unescapeHTML, url_or_none, - ExtractorError, ) diff --git a/lib/yt_dlp/extractor/rtp.py b/lib/yt_dlp/extractor/rtp.py index 5928a207a..ec78d0a66 100644 --- a/lib/yt_dlp/extractor/rtp.py +++ b/lib/yt_dlp/extractor/rtp.py @@ -1,9 +1,10 @@ -from .common import InfoExtractor -from ..utils import js_to_json -import re +import base64 import json +import re import urllib.parse -import base64 + +from .common import InfoExtractor +from ..utils import js_to_json class RTPIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/rtvcplay.py b/lib/yt_dlp/extractor/rtvcplay.py index 741c47262..e7dcd5fd6 100644 --- a/lib/yt_dlp/extractor/rtvcplay.py +++ b/lib/yt_dlp/extractor/rtvcplay.py @@ -1,16 +1,17 @@ import re -from .common import InfoExtractor, ExtractorError +from .common import InfoExtractor from ..utils import ( + ExtractorError, clean_html, determine_ext, - int_or_none, float_or_none, + int_or_none, js_to_json, mimetype2ext, traverse_obj, - urljoin, url_or_none, + urljoin, ) diff --git a/lib/yt_dlp/extractor/rtvs.py b/lib/yt_dlp/extractor/rtvs.py index a84a78da8..defb8d741 100644 --- a/lib/yt_dlp/extractor/rtvs.py +++ b/lib/yt_dlp/extractor/rtvs.py @@ -1,7 +1,6 @@ import re from .common import InfoExtractor - from ..utils import ( parse_duration, traverse_obj, diff --git a/lib/yt_dlp/extractor/rutube.py b/lib/yt_dlp/extractor/rutube.py index 287824d08..eb12f32fa 100644 --- a/lib/yt_dlp/extractor/rutube.py +++ b/lib/yt_dlp/extractor/rutube.py @@ -5,8 +5,8 @@ compat_str, ) from ..utils import ( - determine_ext, bool_or_none, + determine_ext, int_or_none, parse_qs, try_get, diff --git a/lib/yt_dlp/extractor/rutv.py b/lib/yt_dlp/extractor/rutv.py index d7f9a7337..726d49111 100644 --- a/lib/yt_dlp/extractor/rutv.py +++ b/lib/yt_dlp/extractor/rutv.py @@ -1,11 +1,7 @@ import re from .common import InfoExtractor -from ..utils import ( - ExtractorError, - int_or_none, - str_to_int -) +from ..utils import ExtractorError, int_or_none, str_to_int class RUTVIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/ruutu.py b/lib/yt_dlp/extractor/ruutu.py index 33f6652df..dc61387be 100644 --- a/lib/yt_dlp/extractor/ruutu.py +++ b/lib/yt_dlp/extractor/ruutu.py @@ -4,8 +4,8 @@ from .common import InfoExtractor from ..compat import compat_urllib_parse_urlparse from ..utils import ( - determine_ext, ExtractorError, + determine_ext, find_xpath_attr, int_or_none, traverse_obj, diff --git a/lib/yt_dlp/extractor/safari.py b/lib/yt_dlp/extractor/safari.py index 8d322d710..17dff0afa 100644 --- a/lib/yt_dlp/extractor/safari.py +++ b/lib/yt_dlp/extractor/safari.py @@ -2,7 +2,6 @@ import re from .common import InfoExtractor - from ..compat import ( compat_parse_qs, compat_urlparse, diff --git a/lib/yt_dlp/extractor/scrippsnetworks.py b/lib/yt_dlp/extractor/scrippsnetworks.py index 3912f7786..85d51cd59 100644 --- a/lib/yt_dlp/extractor/scrippsnetworks.py +++ b/lib/yt_dlp/extractor/scrippsnetworks.py @@ -1,8 +1,8 @@ -import json import hashlib +import json -from .aws import AWSIE from .anvato import AnvatoIE +from .aws import AWSIE from .common import InfoExtractor from ..utils import ( smuggle_url, diff --git a/lib/yt_dlp/extractor/scte.py b/lib/yt_dlp/extractor/scte.py index 9c2ca8c51..fc91d60e1 100644 --- a/lib/yt_dlp/extractor/scte.py +++ b/lib/yt_dlp/extractor/scte.py @@ -2,8 +2,8 @@ from .common import InfoExtractor from ..utils import ( - decode_packed_codes, ExtractorError, + decode_packed_codes, urlencode_postdata, ) diff --git a/lib/yt_dlp/extractor/sendtonews.py b/lib/yt_dlp/extractor/sendtonews.py index 1ecea71fc..99fcf51f1 100644 --- a/lib/yt_dlp/extractor/sendtonews.py +++ b/lib/yt_dlp/extractor/sendtonews.py @@ -2,12 +2,12 @@ from .common import InfoExtractor from ..utils import ( + determine_protocol, float_or_none, - parse_iso8601, - update_url_query, int_or_none, - determine_protocol, + parse_iso8601, unescapeHTML, + update_url_query, ) diff --git a/lib/yt_dlp/extractor/seznamzpravy.py b/lib/yt_dlp/extractor/seznamzpravy.py index 79e888583..b31d566df 100644 --- a/lib/yt_dlp/extractor/seznamzpravy.py +++ b/lib/yt_dlp/extractor/seznamzpravy.py @@ -4,11 +4,11 @@ compat_urllib_parse_urlparse, ) from ..utils import ( - urljoin, int_or_none, parse_codecs, parse_qs, try_get, + urljoin, ) diff --git a/lib/yt_dlp/extractor/shahid.py b/lib/yt_dlp/extractor/shahid.py index d509e8879..89aee2728 100644 --- a/lib/yt_dlp/extractor/shahid.py +++ b/lib/yt_dlp/extractor/shahid.py @@ -5,9 +5,9 @@ from .aws import AWSIE from ..networking.exceptions import HTTPError from ..utils import ( - clean_html, ExtractorError, InAdvancePagedList, + clean_html, int_or_none, parse_iso8601, str_or_none, diff --git a/lib/yt_dlp/extractor/shemaroome.py b/lib/yt_dlp/extractor/shemaroome.py index ec9938b8c..cca86ed6c 100644 --- a/lib/yt_dlp/extractor/shemaroome.py +++ b/lib/yt_dlp/extractor/shemaroome.py @@ -4,8 +4,8 @@ compat_b64decode, ) from ..utils import ( - bytes_to_intlist, ExtractorError, + bytes_to_intlist, intlist_to_bytes, unified_strdate, ) diff --git a/lib/yt_dlp/extractor/sixplay.py b/lib/yt_dlp/extractor/sixplay.py index ef93b9276..44619a16c 100644 --- a/lib/yt_dlp/extractor/sixplay.py +++ b/lib/yt_dlp/extractor/sixplay.py @@ -6,8 +6,8 @@ determine_ext, int_or_none, parse_qs, - try_get, qualities, + try_get, ) diff --git a/lib/yt_dlp/extractor/skynewsarabia.py b/lib/yt_dlp/extractor/skynewsarabia.py index 867782778..234703cf7 100644 --- a/lib/yt_dlp/extractor/skynewsarabia.py +++ b/lib/yt_dlp/extractor/skynewsarabia.py @@ -1,8 +1,8 @@ from .common import InfoExtractor from ..compat import compat_str from ..utils import ( - parse_iso8601, parse_duration, + parse_iso8601, ) diff --git a/lib/yt_dlp/extractor/sohu.py b/lib/yt_dlp/extractor/sohu.py index c0ff4f9aa..a41ad303a 100644 --- a/lib/yt_dlp/extractor/sohu.py +++ b/lib/yt_dlp/extractor/sohu.py @@ -8,13 +8,13 @@ ) from ..utils import ( ExtractorError, - int_or_none, float_or_none, - url_or_none, - unified_timestamp, + int_or_none, + traverse_obj, try_get, + unified_timestamp, + url_or_none, urljoin, - traverse_obj, ) diff --git a/lib/yt_dlp/extractor/sovietscloset.py b/lib/yt_dlp/extractor/sovietscloset.py index 493eea2a6..773ddd344 100644 --- a/lib/yt_dlp/extractor/sovietscloset.py +++ b/lib/yt_dlp/extractor/sovietscloset.py @@ -1,8 +1,5 @@ from .common import InfoExtractor -from ..utils import ( - try_get, - unified_timestamp -) +from ..utils import try_get, unified_timestamp class SovietsClosetBaseIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/spankbang.py b/lib/yt_dlp/extractor/spankbang.py index 43da34a32..c73f7971d 100644 --- a/lib/yt_dlp/extractor/spankbang.py +++ b/lib/yt_dlp/extractor/spankbang.py @@ -2,8 +2,8 @@ from .common import InfoExtractor from ..utils import ( - determine_ext, ExtractorError, + determine_ext, merge_dicts, parse_duration, parse_resolution, diff --git a/lib/yt_dlp/extractor/springboardplatform.py b/lib/yt_dlp/extractor/springboardplatform.py index a98584a27..bdb8ef496 100644 --- a/lib/yt_dlp/extractor/springboardplatform.py +++ b/lib/yt_dlp/extractor/springboardplatform.py @@ -4,11 +4,11 @@ from ..utils import ( ExtractorError, int_or_none, - xpath_attr, - xpath_text, - xpath_element, unescapeHTML, unified_timestamp, + xpath_attr, + xpath_element, + xpath_text, ) diff --git a/lib/yt_dlp/extractor/startv.py b/lib/yt_dlp/extractor/startv.py index bb6e8f1ea..312a4fde0 100644 --- a/lib/yt_dlp/extractor/startv.py +++ b/lib/yt_dlp/extractor/startv.py @@ -3,10 +3,10 @@ compat_str, ) from ..utils import ( - clean_html, ExtractorError, - traverse_obj, + clean_html, int_or_none, + traverse_obj, ) diff --git a/lib/yt_dlp/extractor/stitcher.py b/lib/yt_dlp/extractor/stitcher.py index 2fd200f87..46a15e6a1 100644 --- a/lib/yt_dlp/extractor/stitcher.py +++ b/lib/yt_dlp/extractor/stitcher.py @@ -1,9 +1,9 @@ from .common import InfoExtractor from ..compat import compat_str from ..utils import ( + ExtractorError, clean_html, clean_podcast_url, - ExtractorError, int_or_none, str_or_none, try_get, diff --git a/lib/yt_dlp/extractor/storyfire.py b/lib/yt_dlp/extractor/storyfire.py index 566f77782..20a70a7bc 100644 --- a/lib/yt_dlp/extractor/storyfire.py +++ b/lib/yt_dlp/extractor/storyfire.py @@ -2,9 +2,9 @@ from .common import InfoExtractor from ..utils import ( + OnDemandPagedList, format_field, int_or_none, - OnDemandPagedList, smuggle_url, ) diff --git a/lib/yt_dlp/extractor/streamable.py b/lib/yt_dlp/extractor/streamable.py index 462861e0e..c303ac53a 100644 --- a/lib/yt_dlp/extractor/streamable.py +++ b/lib/yt_dlp/extractor/streamable.py @@ -3,8 +3,8 @@ ExtractorError, float_or_none, int_or_none, - try_get, parse_codecs, + try_get, ) diff --git a/lib/yt_dlp/extractor/stripchat.py b/lib/yt_dlp/extractor/stripchat.py index b9523c865..a847925e4 100644 --- a/lib/yt_dlp/extractor/stripchat.py +++ b/lib/yt_dlp/extractor/stripchat.py @@ -3,7 +3,7 @@ ExtractorError, UserNotLive, lowercase_escape, - traverse_obj + traverse_obj, ) diff --git a/lib/yt_dlp/extractor/sunporno.py b/lib/yt_dlp/extractor/sunporno.py index 708873a95..501156e51 100644 --- a/lib/yt_dlp/extractor/sunporno.py +++ b/lib/yt_dlp/extractor/sunporno.py @@ -2,10 +2,10 @@ from .common import InfoExtractor from ..utils import ( - parse_duration, + determine_ext, int_or_none, + parse_duration, qualities, - determine_ext, ) diff --git a/lib/yt_dlp/extractor/syfy.py b/lib/yt_dlp/extractor/syfy.py index bd2d73842..29e5e573f 100644 --- a/lib/yt_dlp/extractor/syfy.py +++ b/lib/yt_dlp/extractor/syfy.py @@ -1,7 +1,7 @@ from .adobepass import AdobePassIE from ..utils import ( - update_url_query, smuggle_url, + update_url_query, ) diff --git a/lib/yt_dlp/extractor/tbs.py b/lib/yt_dlp/extractor/tbs.py index 808c6c73d..4e178593f 100644 --- a/lib/yt_dlp/extractor/tbs.py +++ b/lib/yt_dlp/extractor/tbs.py @@ -2,8 +2,8 @@ from .turner import TurnerBaseIE from ..compat import ( - compat_urllib_parse_urlparse, compat_parse_qs, + compat_urllib_parse_urlparse, ) from ..utils import ( float_or_none, diff --git a/lib/yt_dlp/extractor/teachable.py b/lib/yt_dlp/extractor/teachable.py index 5eac9aa3f..778fa1263 100644 --- a/lib/yt_dlp/extractor/teachable.py +++ b/lib/yt_dlp/extractor/teachable.py @@ -3,10 +3,10 @@ from .common import InfoExtractor from .wistia import WistiaIE from ..utils import ( - clean_html, ExtractorError, - int_or_none, + clean_html, get_element_by_class, + int_or_none, strip_or_none, urlencode_postdata, urljoin, diff --git a/lib/yt_dlp/extractor/teachertube.py b/lib/yt_dlp/extractor/teachertube.py index 90a976297..740240993 100644 --- a/lib/yt_dlp/extractor/teachertube.py +++ b/lib/yt_dlp/extractor/teachertube.py @@ -2,8 +2,8 @@ from .common import InfoExtractor from ..utils import ( - determine_ext, ExtractorError, + determine_ext, qualities, ) diff --git a/lib/yt_dlp/extractor/teamcoco.py b/lib/yt_dlp/extractor/teamcoco.py index d32f81262..3fb899cac 100644 --- a/lib/yt_dlp/extractor/teamcoco.py +++ b/lib/yt_dlp/extractor/teamcoco.py @@ -13,8 +13,8 @@ parse_qs, traverse_obj, unified_timestamp, - urljoin, url_or_none, + urljoin, ) diff --git a/lib/yt_dlp/extractor/teamtreehouse.py b/lib/yt_dlp/extractor/teamtreehouse.py index dd802db5b..ba25cdcf6 100644 --- a/lib/yt_dlp/extractor/teamtreehouse.py +++ b/lib/yt_dlp/extractor/teamtreehouse.py @@ -2,9 +2,9 @@ from .common import InfoExtractor from ..utils import ( + ExtractorError, clean_html, determine_ext, - ExtractorError, float_or_none, get_element_by_class, get_element_by_id, diff --git a/lib/yt_dlp/extractor/ted.py b/lib/yt_dlp/extractor/ted.py index c28a15498..0969bbb03 100644 --- a/lib/yt_dlp/extractor/ted.py +++ b/lib/yt_dlp/extractor/ted.py @@ -2,14 +2,13 @@ import re from .common import InfoExtractor - from ..utils import ( int_or_none, + parse_duration, str_to_int, try_get, - url_or_none, unified_strdate, - parse_duration, + url_or_none, ) diff --git a/lib/yt_dlp/extractor/tele13.py b/lib/yt_dlp/extractor/tele13.py index 212af3785..1705c2d55 100644 --- a/lib/yt_dlp/extractor/tele13.py +++ b/lib/yt_dlp/extractor/tele13.py @@ -1,9 +1,9 @@ from .common import InfoExtractor from .youtube import YoutubeIE from ..utils import ( + determine_ext, js_to_json, qualities, - determine_ext, ) diff --git a/lib/yt_dlp/extractor/telewebion.py b/lib/yt_dlp/extractor/telewebion.py index 5fdcddd8b..380c84d98 100644 --- a/lib/yt_dlp/extractor/telewebion.py +++ b/lib/yt_dlp/extractor/telewebion.py @@ -1,4 +1,5 @@ from __future__ import annotations + import functools import json import textwrap diff --git a/lib/yt_dlp/extractor/tempo.py b/lib/yt_dlp/extractor/tempo.py index 9318d6f9a..71e54eb0c 100644 --- a/lib/yt_dlp/extractor/tempo.py +++ b/lib/yt_dlp/extractor/tempo.py @@ -5,7 +5,7 @@ int_or_none, parse_iso8601, traverse_obj, - try_call + try_call, ) diff --git a/lib/yt_dlp/extractor/tencent.py b/lib/yt_dlp/extractor/tencent.py index 6618ea4e6..ae2cb483f 100644 --- a/lib/yt_dlp/extractor/tencent.py +++ b/lib/yt_dlp/extractor/tencent.py @@ -8,8 +8,8 @@ from ..aes import aes_cbc_encrypt_bytes from ..utils import ( ExtractorError, - float_or_none, determine_ext, + float_or_none, int_or_none, js_to_json, traverse_obj, diff --git a/lib/yt_dlp/extractor/theguardian.py b/lib/yt_dlp/extractor/theguardian.py index a231eccf4..fb6407715 100644 --- a/lib/yt_dlp/extractor/theguardian.py +++ b/lib/yt_dlp/extractor/theguardian.py @@ -10,7 +10,7 @@ parse_qs, traverse_obj, unified_strdate, - urljoin + urljoin, ) diff --git a/lib/yt_dlp/extractor/theintercept.py b/lib/yt_dlp/extractor/theintercept.py index a991a4dfd..99f0d42ef 100644 --- a/lib/yt_dlp/extractor/theintercept.py +++ b/lib/yt_dlp/extractor/theintercept.py @@ -1,9 +1,9 @@ from .common import InfoExtractor from ..compat import compat_str from ..utils import ( - parse_iso8601, - int_or_none, ExtractorError, + int_or_none, + parse_iso8601, ) diff --git a/lib/yt_dlp/extractor/theplatform.py b/lib/yt_dlp/extractor/theplatform.py index 9160f5ec6..eeb33a660 100644 --- a/lib/yt_dlp/extractor/theplatform.py +++ b/lib/yt_dlp/extractor/theplatform.py @@ -1,29 +1,27 @@ -import re -import time -import hmac import binascii import hashlib +import hmac +import re +import time - -from .once import OnceIE from .adobepass import AdobePassIE -from ..networking import Request +from .once import OnceIE +from ..networking import HEADRequest, Request from ..utils import ( - determine_ext, ExtractorError, + determine_ext, + find_xpath_attr, float_or_none, int_or_none, - parse_qs, - unsmuggle_url, - update_url_query, - xpath_with_ns, mimetype2ext, - find_xpath_attr, + parse_qs, traverse_obj, + unsmuggle_url, update_url, + update_url_query, urlhandle_detect_ext, + xpath_with_ns, ) -from ..networking import HEADRequest default_ns = 'http://www.w3.org/2005/SMIL21/Language' _x = lambda p: xpath_with_ns(p, {'smil': default_ns}) diff --git a/lib/yt_dlp/extractor/thisvid.py b/lib/yt_dlp/extractor/thisvid.py index 9d3368ed7..04b083811 100644 --- a/lib/yt_dlp/extractor/thisvid.py +++ b/lib/yt_dlp/extractor/thisvid.py @@ -134,7 +134,7 @@ def _make_playlist_result(self, url): title = re.split( r'(?i)\s*\|\s*ThisVid\.com\s*$', self._og_search_title(webpage, default=None) - or self._html_search_regex(r'(?s)]*>(.+?)]*>(.+?)[\w\.-]+)/?(?:$|[#?])' - _WORKING = False + _VALID_URL = r'(?:tiktokuser:|https?://(?:www\.)?tiktok\.com/@)(?P[\w.-]+)/?(?:$|[#?])' _TESTS = [{ 'url': 'https://tiktok.com/@corgibobaa?lang=en', 'playlist_mincount': 45, 'info_dict': { - 'id': '6935371178089399301', + 'id': 'MS4wLjABAAAAepiJKgwWhulvCpSuUVsp7sgVVsFJbbNaLeQ6OQ0oAJERGDUIXhb2yxxHZedsItgT', 'title': 'corgibobaa', - 'thumbnail': r're:https://.+_1080x1080\.webp' }, - 'expected_warnings': ['Retrying'] }, { 'url': 'https://www.tiktok.com/@6820838815978423302', 'playlist_mincount': 5, 'info_dict': { - 'id': '6820838815978423302', + 'id': 'MS4wLjABAAAA0tF1nBwQVVMyrGu3CqttkNgM68Do1OXUFuCY0CRQk8fEtSVDj89HqoqvbSTmUP2W', 'title': '6820838815978423302', - 'thumbnail': r're:https://.+_1080x1080\.webp' }, - 'expected_warnings': ['Retrying'] }, { 'url': 'https://www.tiktok.com/@meme', 'playlist_mincount': 593, 'info_dict': { - 'id': '79005827461758976', + 'id': 'MS4wLjABAAAAiKfaDWeCsT3IHwY77zqWGtVRIy9v4ws1HbVi7auP1Vx7dJysU_hc5yRiGywojRD6', 'title': 'meme', - 'thumbnail': r're:https://.+_1080x1080\.webp' }, - 'expected_warnings': ['Retrying'] + }, { + 'url': 'tiktokuser:MS4wLjABAAAAM3R2BtjzVT-uAtstkl2iugMzC6AtnpkojJbjiOdDDrdsTiTR75-8lyWJCY5VvDrZ', + 'playlist_mincount': 31, + 'info_dict': { + 'id': 'MS4wLjABAAAAM3R2BtjzVT-uAtstkl2iugMzC6AtnpkojJbjiOdDDrdsTiTR75-8lyWJCY5VvDrZ', + }, }] + _USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:115.0) Gecko/20100101 Firefox/115.0' + _API_BASE_URL = 'https://www.tiktok.com/api/creator/item_list/' - r''' # TODO: Fix by adding _signature to api_url - def _entries(self, webpage, user_id, username): - secuid = self._search_regex(r'\"secUid\":\"(?P[^\"]+)', webpage, username) - verifyfp_cookie = self._get_cookies('https://www.tiktok.com').get('s_v_web_id') - if not verifyfp_cookie: - raise ExtractorError('Improper cookies (missing s_v_web_id).', expected=True) - api_url = f'https://m.tiktok.com/api/post/item_list/?aid=1988&cookie_enabled=true&count=30&verifyFp={verifyfp_cookie.value}&secUid={secuid}&cursor=' - cursor = '0' - for page in itertools.count(): - data_json = self._download_json(api_url + cursor, username, note='Downloading Page %d' % page) - for video in data_json.get('itemList', []): - video_id = video['id'] - video_url = f'https://www.tiktok.com/@{user_id}/video/{video_id}' - yield self._url_result(video_url, 'TikTok', video_id, str_or_none(video.get('desc'))) - if not data_json.get('hasMore'): - break - cursor = data_json['cursor'] - ''' - - def _video_entries_api(self, webpage, user_id, username): - query = { - 'user_id': user_id, - 'count': 21, - 'max_cursor': 0, - 'min_cursor': 0, - 'retry_type': 'no_retry', - 'device_id': self._DEVICE_ID, # Some endpoints don't like randomized device_id, so it isn't directly set in _call_api. + def _build_web_query(self, sec_uid, cursor): + return { + 'aid': '1988', + 'app_language': 'en', + 'app_name': 'tiktok_web', + 'browser_language': 'en-US', + 'browser_name': 'Mozilla', + 'browser_online': 'true', + 'browser_platform': 'Win32', + 'browser_version': '5.0 (Windows)', + 'channel': 'tiktok_web', + 'cookie_enabled': 'true', + 'count': '15', + 'cursor': cursor, + 'device_id': self._DEVICE_ID, + 'device_platform': 'web_pc', + 'focus_state': 'true', + 'from_page': 'user', + 'history_len': '2', + 'is_fullscreen': 'false', + 'is_page_visible': 'true', + 'language': 'en', + 'os': 'windows', + 'priority_region': '', + 'referer': '', + 'region': 'US', + 'screen_height': '1080', + 'screen_width': '1920', + 'secUid': sec_uid, + 'type': '1', # pagination type: 0 == oldest-to-newest, 1 == newest-to-oldest + 'tz_name': 'UTC', + 'verifyFp': f'verify_{"".join(random.choices(string.hexdigits, k=7))}', + 'webcast_language': 'en', } + def _entries(self, sec_uid, user_name): + display_id = user_name or sec_uid + seen_ids = set() + + cursor = int(time.time() * 1E3) for page in itertools.count(1): - for retry in self.RetryManager(): - try: - post_list = self._call_api( - 'aweme/post', query, username, note=f'Downloading user video list page {page}', - errnote='Unable to download user video list') - except ExtractorError as e: - if isinstance(e.cause, json.JSONDecodeError) and e.cause.pos == 0: - retry.error = e - continue - raise - yield from post_list.get('aweme_list', []) - if not post_list.get('has_more'): + response = self._download_json( + self._API_BASE_URL, display_id, f'Downloading page {page}', + query=self._build_web_query(sec_uid, cursor), headers={'User-Agent': self._USER_AGENT}) + + for video in traverse_obj(response, ('itemList', lambda _, v: v['id'])): + video_id = video['id'] + if video_id in seen_ids: + continue + seen_ids.add(video_id) + webpage_url = self._create_url(display_id, video_id) + yield self.url_result( + webpage_url, TikTokIE, + **self._parse_aweme_video_web(video, webpage_url, video_id, extract_flat=True)) + + old_cursor = cursor + cursor = traverse_obj( + response, ('itemList', -1, 'createTime', {lambda x: int(x * 1E3)})) + if not cursor or old_cursor == cursor: + # User may not have posted within this ~1 week lookback, so manually adjust cursor + cursor = old_cursor - 7 * 86_400_000 + # In case 'hasMorePrevious' is wrong, break if we have gone back before TikTok existed + if cursor < 1472706000000 or not traverse_obj(response, 'hasMorePrevious'): break - query['max_cursor'] = post_list['max_cursor'] - - def _entries_api(self, user_id, videos): - for video in videos: - yield { - **self._parse_aweme_video_app(video), - 'extractor_key': TikTokIE.ie_key(), - 'extractor': 'TikTok', - 'webpage_url': f'https://tiktok.com/@{user_id}/video/{video["aweme_id"]}', - } - def _real_extract(self, url): - user_name = self._match_id(url) - webpage = self._download_webpage(url, user_name, headers={ - 'User-Agent': 'facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)' - }) - user_id = self._html_search_regex(r'snssdk\d*://user/profile/(\d+)', webpage, 'user ID', default=None) or user_name + def _get_sec_uid(self, user_url, user_name, msg): + webpage = self._download_webpage( + user_url, user_name, fatal=False, headers={'User-Agent': 'Mozilla/5.0'}, + note=f'Downloading {msg} webpage', errnote=f'Unable to download {msg} webpage') or '' + return (traverse_obj(self._get_universal_data(webpage, user_name), + ('webapp.user-detail', 'userInfo', 'user', 'secUid', {str})) + or traverse_obj(self._get_sigi_state(webpage, user_name), + ('LiveRoom', 'liveRoomUserInfo', 'user', 'secUid', {str}), + ('UserModule', 'users', ..., 'secUid', {str}, any))) - videos = LazyList(self._video_entries_api(webpage, user_id, user_name)) - thumbnail = traverse_obj(videos, (0, 'author', 'avatar_larger', 'url_list', 0)) + def _real_extract(self, url): + user_name, sec_uid = self._match_id(url), None + if mobj := re.fullmatch(r'MS4wLjABAAAA[\w-]{64}', user_name): + user_name, sec_uid = None, mobj.group(0) + else: + sec_uid = (self._get_sec_uid(self._UPLOADER_URL_FORMAT % user_name, user_name, 'user') + or self._get_sec_uid(self._UPLOADER_URL_FORMAT % f'{user_name}/live', user_name, 'live')) + + if not sec_uid: + webpage = self._download_webpage( + f'https://www.tiktok.com/embed/@{user_name}', user_name, + note='Downloading user embed page', fatal=False) or '' + data = traverse_obj(self._search_json( + r']+\bid=[\'"]__FRONTITY_CONNECT_STATE__[\'"][^>]*>', + webpage, 'data', user_name, default={}), + ('source', 'data', f'/embed/@{user_name}', {dict})) + + for aweme_id in traverse_obj(data, ('videoList', ..., 'id', {str})): + webpage_url = self._create_url(user_name, aweme_id) + video_data, _ = self._extract_web_data_and_status(webpage_url, aweme_id, fatal=False) + sec_uid = self._parse_aweme_video_web( + video_data, webpage_url, aweme_id, extract_flat=True).get('channel_id') + if sec_uid: + break + + if not sec_uid: + raise ExtractorError( + 'Unable to extract secondary user ID. If you are able to get the channel_id ' + 'from a video posted by this user, try using "tiktokuser:channel_id" as the ' + 'input URL (replacing `channel_id` with its actual value)', expected=True) - return self.playlist_result(self._entries_api(user_id, videos), user_id, user_name, thumbnail=thumbnail) + return self.playlist_result(self._entries(sec_uid, user_name), sec_uid, user_name) class TikTokBaseListIE(TikTokBaseIE): # XXX: Conventionally, base classes should end with BaseIE/InfoExtractor @@ -1083,6 +1121,64 @@ def _real_extract(self, url): return self.playlist_result(self._entries(tag_id, display_id), tag_id, display_id) +class TikTokCollectionIE(TikTokBaseIE): + IE_NAME = 'tiktok:collection' + _VALID_URL = r'https?://www\.tiktok\.com/@(?P[\w.-]+)/collection/(?P[^/?#]+)-(?P<id>\d+)/?(?:[?#]|$)' + _TESTS = [{ + # playlist should have exactly 9 videos + 'url': 'https://www.tiktok.com/@imanoreotwe/collection/count-test-7371330159376370462', + 'info_dict': { + 'id': '7371330159376370462', + 'title': 'imanoreotwe-count-test' + }, + 'playlist_count': 9 + }, { + # tests returning multiple pages of a large collection + 'url': 'https://www.tiktok.com/@imanoreotwe/collection/%F0%9F%98%82-7111887189571160875', + 'info_dict': { + 'id': '7111887189571160875', + 'title': 'imanoreotwe-%F0%9F%98%82' + }, + 'playlist_mincount': 100 + }] + _API_BASE_URL = 'https://www.tiktok.com/api/collection/item_list/' + _PAGE_COUNT = 30 + + def _build_web_query(self, collection_id, cursor): + return { + 'aid': '1988', + 'collectionId': collection_id, + 'count': self._PAGE_COUNT, + 'cursor': cursor, + 'sourceType': '113', + } + + def _entries(self, collection_id): + cursor = 0 + for page in itertools.count(1): + response = self._download_json( + self._API_BASE_URL, collection_id, f'Downloading page {page}', + query=self._build_web_query(collection_id, cursor)) + + for video in traverse_obj(response, ('itemList', lambda _, v: v['id'])): + video_id = video['id'] + author = traverse_obj(video, ('author', ('uniqueId', 'secUid', 'id'), {str}, any)) or '_' + webpage_url = self._create_url(author, video_id) + yield self.url_result( + webpage_url, TikTokIE, + **self._parse_aweme_video_web(video, webpage_url, video_id, extract_flat=True)) + + if not traverse_obj(response, 'hasMore'): + break + cursor += self._PAGE_COUNT + + def _real_extract(self, url): + collection_id, title, user_name = self._match_valid_url(url).group('id', 'title', 'user_id') + + return self.playlist_result( + self._entries(collection_id), collection_id, '-'.join((user_name, title))) + + class DouyinIE(TikTokBaseIE): _VALID_URL = r'https?://(?:www\.)?douyin\.com/video/(?P<id>[0-9]+)' _TESTS = [{ @@ -1098,7 +1194,6 @@ class DouyinIE(TikTokBaseIE): 'uploader_url': 'https://www.douyin.com/user/MS4wLjABAAAAEKnfa654JAJ_N5lgZDQluwsxmY0lhfmEYNQBBkwGG98', 'channel_id': 'MS4wLjABAAAAEKnfa654JAJ_N5lgZDQluwsxmY0lhfmEYNQBBkwGG98', 'channel': '杨超越', - 'creators': ['杨超越'], 'duration': 19, 'timestamp': 1620905839, 'upload_date': '20210513', @@ -1123,7 +1218,6 @@ class DouyinIE(TikTokBaseIE): 'uploader_url': 'https://www.douyin.com/user/MS4wLjABAAAAZJpnglcjW2f_CMVcnqA_6oVBXKWMpH0F8LIHuUu8-lA', 'channel_id': 'MS4wLjABAAAAZJpnglcjW2f_CMVcnqA_6oVBXKWMpH0F8LIHuUu8-lA', 'channel': '杨超越工作室', - 'creators': ['杨超越工作室'], 'duration': 42, 'timestamp': 1625739481, 'upload_date': '20210708', @@ -1148,7 +1242,6 @@ class DouyinIE(TikTokBaseIE): 'uploader_url': 'https://www.douyin.com/user/MS4wLjABAAAAEKnfa654JAJ_N5lgZDQluwsxmY0lhfmEYNQBBkwGG98', 'channel_id': 'MS4wLjABAAAAEKnfa654JAJ_N5lgZDQluwsxmY0lhfmEYNQBBkwGG98', 'channel': '杨超越', - 'creators': ['杨超越'], 'duration': 17, 'timestamp': 1619098692, 'upload_date': '20210422', @@ -1190,7 +1283,6 @@ class DouyinIE(TikTokBaseIE): 'uploader_url': 'https://www.douyin.com/user/MS4wLjABAAAAEKnfa654JAJ_N5lgZDQluwsxmY0lhfmEYNQBBkwGG98', 'channel_id': 'MS4wLjABAAAAEKnfa654JAJ_N5lgZDQluwsxmY0lhfmEYNQBBkwGG98', 'channel': '杨超越', - 'creators': ['杨超越'], 'duration': 15, 'timestamp': 1621261163, 'upload_date': '20210517', diff --git a/lib/yt_dlp/extractor/toypics.py b/lib/yt_dlp/extractor/toypics.py index aa7ee6c48..ccb2ef816 100644 --- a/lib/yt_dlp/extractor/toypics.py +++ b/lib/yt_dlp/extractor/toypics.py @@ -1,6 +1,7 @@ -from .common import InfoExtractor import re +from .common import InfoExtractor + class ToypicsIE(InfoExtractor): _WORKING = False diff --git a/lib/yt_dlp/extractor/triller.py b/lib/yt_dlp/extractor/triller.py index 56e51fea8..3bdeedd43 100644 --- a/lib/yt_dlp/extractor/triller.py +++ b/lib/yt_dlp/extractor/triller.py @@ -14,8 +14,8 @@ traverse_obj, unified_timestamp, url_basename, - urljoin, url_or_none, + urljoin, ) diff --git a/lib/yt_dlp/extractor/trueid.py b/lib/yt_dlp/extractor/trueid.py index 86f0990e8..efedac180 100644 --- a/lib/yt_dlp/extractor/trueid.py +++ b/lib/yt_dlp/extractor/trueid.py @@ -1,13 +1,13 @@ from .common import InfoExtractor from ..networking.exceptions import HTTPError from ..utils import ( - determine_ext, ExtractorError, + determine_ext, int_or_none, parse_age_limit, traverse_obj, unified_timestamp, - url_or_none + url_or_none, ) diff --git a/lib/yt_dlp/extractor/tumblr.py b/lib/yt_dlp/extractor/tumblr.py index a26bdcaae..f2d0c5901 100644 --- a/lib/yt_dlp/extractor/tumblr.py +++ b/lib/yt_dlp/extractor/tumblr.py @@ -3,7 +3,7 @@ ExtractorError, int_or_none, traverse_obj, - urlencode_postdata + urlencode_postdata, ) diff --git a/lib/yt_dlp/extractor/turner.py b/lib/yt_dlp/extractor/turner.py index 630d84bdc..b27db87bf 100644 --- a/lib/yt_dlp/extractor/turner.py +++ b/lib/yt_dlp/extractor/turner.py @@ -3,17 +3,17 @@ from .adobepass import AdobePassIE from ..compat import compat_str from ..utils import ( - fix_xml_ampersands, - xpath_text, - int_or_none, + ExtractorError, determine_ext, + fix_xml_ampersands, float_or_none, + int_or_none, parse_duration, - xpath_attr, - update_url_query, - ExtractorError, strip_or_none, + update_url_query, url_or_none, + xpath_attr, + xpath_text, ) diff --git a/lib/yt_dlp/extractor/tv2.py b/lib/yt_dlp/extractor/tv2.py index 7756aa3f5..9b19e7995 100644 --- a/lib/yt_dlp/extractor/tv2.py +++ b/lib/yt_dlp/extractor/tv2.py @@ -3,10 +3,10 @@ from .common import InfoExtractor from ..networking.exceptions import HTTPError from ..utils import ( - determine_ext, ExtractorError, - int_or_none, + determine_ext, float_or_none, + int_or_none, js_to_json, parse_iso8601, remove_end, diff --git a/lib/yt_dlp/extractor/tv2hu.py b/lib/yt_dlp/extractor/tv2hu.py index 9c0a111c0..cd35ff5fb 100644 --- a/lib/yt_dlp/extractor/tv2hu.py +++ b/lib/yt_dlp/extractor/tv2hu.py @@ -1,8 +1,8 @@ # encoding: utf-8 from .common import InfoExtractor from ..utils import ( - traverse_obj, UnsupportedError, + traverse_obj, ) diff --git a/lib/yt_dlp/extractor/tvanouvelles.py b/lib/yt_dlp/extractor/tvanouvelles.py index b9f5e110e..dbebda4f4 100644 --- a/lib/yt_dlp/extractor/tvanouvelles.py +++ b/lib/yt_dlp/extractor/tvanouvelles.py @@ -1,7 +1,7 @@ import re -from .common import InfoExtractor from .brightcove import BrightcoveNewIE +from .common import InfoExtractor class TVANouvellesIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/tvn24.py b/lib/yt_dlp/extractor/tvn24.py index 527681315..ac480580a 100644 --- a/lib/yt_dlp/extractor/tvn24.py +++ b/lib/yt_dlp/extractor/tvn24.py @@ -1,7 +1,7 @@ from .common import InfoExtractor from ..utils import ( - int_or_none, NO_DEFAULT, + int_or_none, unescapeHTML, ) diff --git a/lib/yt_dlp/extractor/tvp.py b/lib/yt_dlp/extractor/tvp.py index a8d00e243..f1ebf027a 100644 --- a/lib/yt_dlp/extractor/tvp.py +++ b/lib/yt_dlp/extractor/tvp.py @@ -4,10 +4,10 @@ from .common import InfoExtractor from ..utils import ( + ExtractorError, clean_html, determine_ext, dict_get, - ExtractorError, int_or_none, js_to_json, str_or_none, diff --git a/lib/yt_dlp/extractor/tvplay.py b/lib/yt_dlp/extractor/tvplay.py index 48a6efe1c..29185d34b 100644 --- a/lib/yt_dlp/extractor/tvplay.py +++ b/lib/yt_dlp/extractor/tvplay.py @@ -4,8 +4,8 @@ from ..compat import compat_urlparse from ..networking.exceptions import HTTPError from ..utils import ( - determine_ext, ExtractorError, + determine_ext, int_or_none, parse_iso8601, qualities, diff --git a/lib/yt_dlp/extractor/tvplayer.py b/lib/yt_dlp/extractor/tvplayer.py index 228c2366e..d43bdc2ff 100644 --- a/lib/yt_dlp/extractor/tvplayer.py +++ b/lib/yt_dlp/extractor/tvplayer.py @@ -2,10 +2,10 @@ from ..compat import compat_str from ..networking.exceptions import HTTPError from ..utils import ( + ExtractorError, extract_attributes, try_get, urlencode_postdata, - ExtractorError, ) diff --git a/lib/yt_dlp/extractor/tweakers.py b/lib/yt_dlp/extractor/tweakers.py index e8e1fc666..9249550c9 100644 --- a/lib/yt_dlp/extractor/tweakers.py +++ b/lib/yt_dlp/extractor/tweakers.py @@ -1,7 +1,7 @@ from .common import InfoExtractor from ..utils import ( - int_or_none, determine_ext, + int_or_none, mimetype2ext, ) diff --git a/lib/yt_dlp/extractor/twitter.py b/lib/yt_dlp/extractor/twitter.py index fc80dade8..1a11162a0 100644 --- a/lib/yt_dlp/extractor/twitter.py +++ b/lib/yt_dlp/extractor/twitter.py @@ -1,10 +1,10 @@ +import functools import json import random import re from .common import InfoExtractor from .periscope import PeriscopeBaseIE, PeriscopeIE -from ..compat import functools # isort: split from ..compat import ( compat_parse_qs, compat_urllib_parse_unquote, diff --git a/lib/yt_dlp/extractor/udn.py b/lib/yt_dlp/extractor/udn.py index 10668ac4b..d5849d29b 100644 --- a/lib/yt_dlp/extractor/udn.py +++ b/lib/yt_dlp/extractor/udn.py @@ -1,12 +1,12 @@ import re from .common import InfoExtractor +from ..compat import compat_urlparse from ..utils import ( determine_ext, int_or_none, js_to_json, ) -from ..compat import compat_urlparse class UDNEmbedIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/ukcolumn.py b/lib/yt_dlp/extractor/ukcolumn.py index f914613c0..f141804c8 100644 --- a/lib/yt_dlp/extractor/ukcolumn.py +++ b/lib/yt_dlp/extractor/ukcolumn.py @@ -1,11 +1,11 @@ +from .common import InfoExtractor +from .vimeo import VimeoIE +from .youtube import YoutubeIE from ..utils import ( + ExtractorError, unescapeHTML, urljoin, - ExtractorError, ) -from .common import InfoExtractor -from .vimeo import VimeoIE -from .youtube import YoutubeIE class UkColumnIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/urplay.py b/lib/yt_dlp/extractor/urplay.py index 7f97fc95f..928e6e1c2 100644 --- a/lib/yt_dlp/extractor/urplay.py +++ b/lib/yt_dlp/extractor/urplay.py @@ -1,9 +1,9 @@ from .common import InfoExtractor from ..utils import ( - dict_get, ExtractorError, - int_or_none, ISO639Utils, + dict_get, + int_or_none, parse_age_limit, try_get, unified_timestamp, diff --git a/lib/yt_dlp/extractor/usatoday.py b/lib/yt_dlp/extractor/usatoday.py index 3243f3e3b..42a28c509 100644 --- a/lib/yt_dlp/extractor/usatoday.py +++ b/lib/yt_dlp/extractor/usatoday.py @@ -1,4 +1,5 @@ from .common import InfoExtractor +from ..compat import compat_str from ..utils import ( ExtractorError, get_element_by_attribute, @@ -6,7 +7,6 @@ try_get, update_url_query, ) -from ..compat import compat_str class USATodayIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/ustream.py b/lib/yt_dlp/extractor/ustream.py index 5df241653..046e3d768 100644 --- a/lib/yt_dlp/extractor/ustream.py +++ b/lib/yt_dlp/extractor/ustream.py @@ -7,10 +7,10 @@ compat_urlparse, ) from ..utils import ( - encode_data_uri, ExtractorError, - int_or_none, + encode_data_uri, float_or_none, + int_or_none, join_nonempty, mimetype2ext, str_or_none, diff --git a/lib/yt_dlp/extractor/ustudio.py b/lib/yt_dlp/extractor/ustudio.py index c3aeeb961..f6ce5b357 100644 --- a/lib/yt_dlp/extractor/ustudio.py +++ b/lib/yt_dlp/extractor/ustudio.py @@ -1,8 +1,8 @@ from .common import InfoExtractor from ..utils import ( int_or_none, - unified_strdate, unescapeHTML, + unified_strdate, ) diff --git a/lib/yt_dlp/extractor/veo.py b/lib/yt_dlp/extractor/veo.py index ef44d421e..205f8ea63 100644 --- a/lib/yt_dlp/extractor/veo.py +++ b/lib/yt_dlp/extractor/veo.py @@ -1,5 +1,4 @@ from .common import InfoExtractor - from ..utils import ( int_or_none, mimetype2ext, diff --git a/lib/yt_dlp/extractor/vesti.py b/lib/yt_dlp/extractor/vesti.py index 3f2dddbe9..a2e90226a 100644 --- a/lib/yt_dlp/extractor/vesti.py +++ b/lib/yt_dlp/extractor/vesti.py @@ -1,8 +1,8 @@ import re from .common import InfoExtractor -from ..utils import ExtractorError from .rutv import RUTVIE +from ..utils import ExtractorError class VestiIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/vevo.py b/lib/yt_dlp/extractor/vevo.py index aa40227a7..7715d6839 100644 --- a/lib/yt_dlp/extractor/vevo.py +++ b/lib/yt_dlp/extractor/vevo.py @@ -1,5 +1,5 @@ -import re import json +import re from .common import InfoExtractor from ..compat import compat_str diff --git a/lib/yt_dlp/extractor/vice.py b/lib/yt_dlp/extractor/vice.py index d31908fb1..b072d9d73 100644 --- a/lib/yt_dlp/extractor/vice.py +++ b/lib/yt_dlp/extractor/vice.py @@ -10,10 +10,10 @@ from ..compat import compat_str from ..networking.exceptions import HTTPError from ..utils import ( - clean_html, ExtractorError, - int_or_none, OnDemandPagedList, + clean_html, + int_or_none, parse_age_limit, str_or_none, try_get, diff --git a/lib/yt_dlp/extractor/vidio.py b/lib/yt_dlp/extractor/vidio.py index 770aa284d..6322bb04b 100644 --- a/lib/yt_dlp/extractor/vidio.py +++ b/lib/yt_dlp/extractor/vidio.py @@ -1,7 +1,7 @@ from .common import InfoExtractor from ..utils import ( - clean_html, ExtractorError, + clean_html, format_field, get_element_by_class, int_or_none, diff --git a/lib/yt_dlp/extractor/vidlii.py b/lib/yt_dlp/extractor/vidlii.py index 44353b7fc..e1219a8a0 100644 --- a/lib/yt_dlp/extractor/vidlii.py +++ b/lib/yt_dlp/extractor/vidlii.py @@ -3,8 +3,8 @@ from .common import InfoExtractor from ..networking import HEADRequest from ..utils import ( - format_field, float_or_none, + format_field, get_element_by_id, int_or_none, str_to_int, diff --git a/lib/yt_dlp/extractor/vimeo.py b/lib/yt_dlp/extractor/vimeo.py index 91b976403..ac96ade18 100644 --- a/lib/yt_dlp/extractor/vimeo.py +++ b/lib/yt_dlp/extractor/vimeo.py @@ -1,21 +1,21 @@ import base64 import functools -import re import itertools +import re from .common import InfoExtractor from ..compat import compat_str, compat_urlparse from ..networking import HEADRequest, Request from ..networking.exceptions import HTTPError from ..utils import ( + ExtractorError, + OnDemandPagedList, clean_html, determine_ext, - ExtractorError, get_element_by_class, - js_to_json, int_or_none, + js_to_json, merge_dicts, - OnDemandPagedList, parse_filesize, parse_iso8601, parse_qs, @@ -26,8 +26,8 @@ unified_timestamp, unsmuggle_url, urlencode_postdata, - urljoin, urlhandle_detect_ext, + urljoin, ) diff --git a/lib/yt_dlp/extractor/viu.py b/lib/yt_dlp/extractor/viu.py index 6f9af9f64..480f49b7b 100644 --- a/lib/yt_dlp/extractor/viu.py +++ b/lib/yt_dlp/extractor/viu.py @@ -1,8 +1,8 @@ -import re import json -import uuid import random +import re import urllib.parse +import uuid from .common import InfoExtractor from ..compat import compat_str @@ -10,10 +10,10 @@ ExtractorError, int_or_none, remove_end, + smuggle_url, strip_or_none, traverse_obj, try_get, - smuggle_url, unified_timestamp, unsmuggle_url, url_or_none, diff --git a/lib/yt_dlp/extractor/vk.py b/lib/yt_dlp/extractor/vk.py index 28d502685..9a3c75b62 100644 --- a/lib/yt_dlp/extractor/vk.py +++ b/lib/yt_dlp/extractor/vk.py @@ -20,6 +20,7 @@ parse_resolution, str_or_none, str_to_int, + traverse_obj, try_call, unescapeHTML, unified_timestamp, @@ -27,7 +28,6 @@ url_or_none, urlencode_postdata, urljoin, - traverse_obj, ) @@ -467,13 +467,13 @@ def _real_extract(self, url): 'source_preference': 1, 'height': height, }) - elif format_id == 'hls': + elif format_id.startswith('hls') and format_id != 'hls_live_playback': fmts, subs = self._extract_m3u8_formats_and_subtitles( format_url, video_id, 'mp4', 'm3u8_native', m3u8_id=format_id, fatal=False, live=is_live) formats.extend(fmts) self._merge_subtitles(subs, target=subtitles) - elif format_id.startswith('dash_'): + elif format_id.startswith('dash') and format_id not in ('dash_live_playback', 'dash_uni'): fmts, subs = self._extract_mpd_formats_and_subtitles( format_url, video_id, mpd_id=format_id, fatal=False) formats.extend(fmts) diff --git a/lib/yt_dlp/extractor/walla.py b/lib/yt_dlp/extractor/walla.py index a1a9c1708..3ac0f8387 100644 --- a/lib/yt_dlp/extractor/walla.py +++ b/lib/yt_dlp/extractor/walla.py @@ -2,8 +2,8 @@ from .common import InfoExtractor from ..utils import ( - xpath_text, int_or_none, + xpath_text, ) diff --git a/lib/yt_dlp/extractor/washingtonpost.py b/lib/yt_dlp/extractor/washingtonpost.py index 74501b1d2..1cfed2da5 100644 --- a/lib/yt_dlp/extractor/washingtonpost.py +++ b/lib/yt_dlp/extractor/washingtonpost.py @@ -1,7 +1,6 @@ import re from .common import InfoExtractor - from ..utils import traverse_obj diff --git a/lib/yt_dlp/extractor/wdr.py b/lib/yt_dlp/extractor/wdr.py index f80f140ed..0b7ddd239 100644 --- a/lib/yt_dlp/extractor/wdr.py +++ b/lib/yt_dlp/extractor/wdr.py @@ -6,16 +6,16 @@ compat_urlparse, ) from ..utils import ( + ExtractorError, determine_ext, dict_get, - ExtractorError, js_to_json, strip_jsonp, try_get, unified_strdate, update_url_query, - urlhandle_detect_ext, url_or_none, + urlhandle_detect_ext, ) diff --git a/lib/yt_dlp/extractor/weibo.py b/lib/yt_dlp/extractor/weibo.py index 2fca745aa..b6a659385 100644 --- a/lib/yt_dlp/extractor/weibo.py +++ b/lib/yt_dlp/extractor/weibo.py @@ -1,6 +1,6 @@ +import itertools import json import random -import itertools import urllib.parse from .common import InfoExtractor diff --git a/lib/yt_dlp/extractor/whowatch.py b/lib/yt_dlp/extractor/whowatch.py index f2808cd9f..492891d78 100644 --- a/lib/yt_dlp/extractor/whowatch.py +++ b/lib/yt_dlp/extractor/whowatch.py @@ -1,12 +1,12 @@ from .common import InfoExtractor +from ..compat import compat_str from ..utils import ( + ExtractorError, int_or_none, qualities, try_call, try_get, - ExtractorError, ) -from ..compat import compat_str class WhoWatchIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/wimtv.py b/lib/yt_dlp/extractor/wimtv.py index f9bf092df..d7d77c0db 100644 --- a/lib/yt_dlp/extractor/wimtv.py +++ b/lib/yt_dlp/extractor/wimtv.py @@ -1,9 +1,9 @@ from .common import InfoExtractor from ..utils import ( + ExtractorError, determine_ext, parse_duration, urlencode_postdata, - ExtractorError, ) diff --git a/lib/yt_dlp/extractor/wppilot.py b/lib/yt_dlp/extractor/wppilot.py index 5e590e2f4..0ef4e8e53 100644 --- a/lib/yt_dlp/extractor/wppilot.py +++ b/lib/yt_dlp/extractor/wppilot.py @@ -1,13 +1,13 @@ +import json +import random +import re + from .common import InfoExtractor from ..utils import ( - try_get, ExtractorError, + try_get, ) -import json -import random -import re - class WPPilotBaseIE(InfoExtractor): _VIDEO_URL = 'https://pilot.wp.pl/api/v1/channel/%s' diff --git a/lib/yt_dlp/extractor/wsj.py b/lib/yt_dlp/extractor/wsj.py index 86e264679..35fe30362 100644 --- a/lib/yt_dlp/extractor/wsj.py +++ b/lib/yt_dlp/extractor/wsj.py @@ -1,7 +1,7 @@ from .common import InfoExtractor from ..utils import ( - int_or_none, float_or_none, + int_or_none, unified_strdate, ) diff --git a/lib/yt_dlp/extractor/xhamster.py b/lib/yt_dlp/extractor/xhamster.py index 01ac5ddb6..0b3a620ec 100644 --- a/lib/yt_dlp/extractor/xhamster.py +++ b/lib/yt_dlp/extractor/xhamster.py @@ -4,11 +4,11 @@ from .common import InfoExtractor from ..compat import compat_str from ..utils import ( + ExtractorError, clean_html, determine_ext, dict_get, extract_attributes, - ExtractorError, float_or_none, int_or_none, parse_duration, diff --git a/lib/yt_dlp/extractor/xnxx.py b/lib/yt_dlp/extractor/xnxx.py index 1452aaec3..74d4f0419 100644 --- a/lib/yt_dlp/extractor/xnxx.py +++ b/lib/yt_dlp/extractor/xnxx.py @@ -2,9 +2,9 @@ from .common import InfoExtractor from ..utils import ( + NO_DEFAULT, determine_ext, int_or_none, - NO_DEFAULT, str_to_int, ) diff --git a/lib/yt_dlp/extractor/xstream.py b/lib/yt_dlp/extractor/xstream.py index 8dd1cd9ef..322e86570 100644 --- a/lib/yt_dlp/extractor/xstream.py +++ b/lib/yt_dlp/extractor/xstream.py @@ -2,11 +2,11 @@ from .common import InfoExtractor from ..utils import ( + find_xpath_attr, int_or_none, parse_iso8601, - xpath_with_ns, xpath_text, - find_xpath_attr, + xpath_with_ns, ) diff --git a/lib/yt_dlp/extractor/xvideos.py b/lib/yt_dlp/extractor/xvideos.py index a489033ab..6b16ac291 100644 --- a/lib/yt_dlp/extractor/xvideos.py +++ b/lib/yt_dlp/extractor/xvideos.py @@ -3,9 +3,9 @@ from .common import InfoExtractor from ..compat import compat_urllib_parse_unquote from ..utils import ( + ExtractorError, clean_html, determine_ext, - ExtractorError, int_or_none, parse_duration, ) diff --git a/lib/yt_dlp/extractor/xxxymovies.py b/lib/yt_dlp/extractor/xxxymovies.py index e3e3a9fe6..aa6c84d09 100644 --- a/lib/yt_dlp/extractor/xxxymovies.py +++ b/lib/yt_dlp/extractor/xxxymovies.py @@ -1,7 +1,7 @@ from .common import InfoExtractor from ..utils import ( - parse_duration, int_or_none, + parse_duration, ) diff --git a/lib/yt_dlp/extractor/yandexmusic.py b/lib/yt_dlp/extractor/yandexmusic.py index 794dc3eae..acfe69bf4 100644 --- a/lib/yt_dlp/extractor/yandexmusic.py +++ b/lib/yt_dlp/extractor/yandexmusic.py @@ -5,8 +5,8 @@ from ..compat import compat_str from ..utils import ( ExtractorError, - int_or_none, float_or_none, + int_or_none, try_get, ) diff --git a/lib/yt_dlp/extractor/youporn.py b/lib/yt_dlp/extractor/youporn.py index 6d4e31bf3..0e047aa16 100644 --- a/lib/yt_dlp/extractor/youporn.py +++ b/lib/yt_dlp/extractor/youporn.py @@ -1,19 +1,27 @@ +import itertools import re from .common import InfoExtractor from ..utils import ( + ExtractorError, + clean_html, extract_attributes, + get_element_by_class, + get_element_by_id, + get_elements_html_by_class, int_or_none, merge_dicts, - str_to_int, + parse_count, + parse_qs, traverse_obj, unified_strdate, url_or_none, + urljoin, ) class YouPornIE(InfoExtractor): - _VALID_URL = r'https?://(?:www\.)?youporn\.com/(?:watch|embed)/(?P<id>\d+)(?:/(?P<display_id>[^/?#&]+))?' + _VALID_URL = r'https?://(?:www\.)?youporn\.com/(?:watch|embed)/(?P<id>\d+)(?:/(?P<display_id>[^/?#&]+))?/?(?:[#?]|$)' _EMBED_REGEX = [r'<iframe[^>]+\bsrc=["\'](?P<url>(?:https?:)?//(?:www\.)?youporn\.com/embed/\d+)'] _TESTS = [{ 'url': 'http://www.youporn.com/watch/505835/sex-ed-is-it-safe-to-masturbate-daily/', @@ -34,7 +42,7 @@ class YouPornIE(InfoExtractor): 'tags': list, 'age_limit': 18, }, - 'skip': 'This video has been disabled', + 'skip': 'This video has been deactivated', }, { # Unknown uploader 'url': 'http://www.youporn.com/watch/561726/big-tits-awesome-brunette-on-amazing-webcam-show/?from=related3&al=2&from_id=561726&pos=4', @@ -72,7 +80,6 @@ class YouPornIE(InfoExtractor): 'id': '16290308', 'age_limit': 18, 'categories': [], - 'description': str, # TODO: detect/remove SEO spam description in ytdl backport 'display_id': 'tinderspecial-trailer1', 'duration': 298.0, 'ext': 'mp4', @@ -90,7 +97,17 @@ def _real_extract(self, url): video_id, display_id = self._match_valid_url(url).group('id', 'display_id') self._set_cookie('.youporn.com', 'age_verified', '1') webpage = self._download_webpage(f'https://www.youporn.com/watch/{video_id}', video_id) - definitions = self._search_json(r'\bplayervars\s*:', webpage, 'player vars', video_id)['mediaDefinitions'] + + watchable = self._search_regex( + r'''(<div\s[^>]*\bid\s*=\s*('|")?watch-container(?(2)\2|(?!-)\b)[^>]*>)''', + webpage, 'watchability', default=None) + if not watchable: + msg = re.split(r'\s{2}', clean_html(get_element_by_id('mainContent', webpage)) or '')[0] + raise ExtractorError( + f'{self.IE_NAME} says: {msg}' if msg else 'Video unavailable', expected=True) + + player_vars = self._search_json(r'\bplayervars\s*:', webpage, 'player vars', video_id) + definitions = player_vars['mediaDefinitions'] def get_format_data(data, stream_type): info_url = traverse_obj(data, (lambda _, v: v['format'] == stream_type, 'videoUrl', {url_or_none}, any)) @@ -143,8 +160,10 @@ def get_format_data(data, stream_type): thumbnail = self._search_regex( r'(?:imageurl\s*=|poster\s*:)\s*(["\'])(?P<thumbnail>.+?)\1', webpage, 'thumbnail', fatal=False, group='thumbnail') - duration = int_or_none(self._html_search_meta( - 'video:duration', webpage, 'duration', fatal=False)) + duration = traverse_obj(player_vars, ('duration', {int_or_none})) + if duration is None: + duration = int_or_none(self._html_search_meta( + 'video:duration', webpage, 'duration', fatal=False)) uploader = self._html_search_regex( r'(?s)<div[^>]+class=["\']submitByLink["\'][^>]*>(.+?)</div>', @@ -160,11 +179,11 @@ def get_format_data(data, stream_type): view_count = None views = self._search_regex( - r'(<div[^>]+\bclass=["\']js_videoInfoViews["\']>)', webpage, - 'views', default=None) + r'(<div [^>]*\bdata-value\s*=[^>]+>)\s*<label>Views:</label>', + webpage, 'views', default=None) if views: - view_count = str_to_int(extract_attributes(views).get('data-value')) - comment_count = str_to_int(self._search_regex( + view_count = parse_count(extract_attributes(views).get('data-value')) + comment_count = parse_count(self._search_regex( r'>All [Cc]omments? \(([\d,.]+)\)', webpage, 'comment count', default=None)) @@ -182,7 +201,8 @@ def extract_tag_box(regex, title): data = self._search_json_ld(webpage, video_id, expected_type='VideoObject', fatal=False) data.pop('url', None) - return merge_dicts(data, { + + result = merge_dicts(data, { 'id': video_id, 'display_id': display_id, 'title': title, @@ -198,3 +218,350 @@ def extract_tag_box(regex, title): 'age_limit': age_limit, 'formats': formats, }) + + # Remove SEO spam "description" + description = result.get('description') + if description and description.startswith(f'Watch {result.get("title")} online'): + del result['description'] + + return result + + +class YouPornListBase(InfoExtractor): + def _get_next_url(self, url, pl_id, html): + return urljoin(url, self._search_regex( + r'''<a [^>]*?\bhref\s*=\s*("|')(?P<url>(?:(?!\1)[^>])+)\1''', + get_element_by_id('next', html) or '', 'next page', + group='url', default=None)) + + @classmethod + def _get_title_from_slug(cls, title_slug): + return re.sub(r'[_-]', ' ', title_slug) + + def _entries(self, url, pl_id, html=None, page_num=None): + start = page_num or 1 + for page in itertools.count(start): + if not html: + html = self._download_webpage( + url, pl_id, note=f'Downloading page {page}', fatal=page == start) + if not html: + return + for element in get_elements_html_by_class('video-title', html): + if video_url := traverse_obj(element, ({extract_attributes}, 'href', {lambda x: urljoin(url, x)})): + yield self.url_result(video_url) + + if page_num is not None: + return + next_url = self._get_next_url(url, pl_id, html) + if not next_url or next_url == url: + return + url = next_url + html = None + + def _real_extract(self, url, html=None): + m_dict = self._match_valid_url(url).groupdict() + pl_id, page_type, sort = (m_dict.get(k) for k in ('id', 'type', 'sort')) + qs = {k: v[-1] for k, v in parse_qs(url).items() if v} + + base_id = pl_id or 'YouPorn' + title = self._get_title_from_slug(base_id) + if page_type: + title = f'{page_type.capitalize()} {title}' + base_id = [base_id.lower()] + if sort is None: + title += ' videos' + else: + title = f'{title} videos by {re.sub(r"[_-]", " ", sort)}' + base_id.append(sort) + if qs: + filters = list(map('='.join, sorted(qs.items()))) + title += f' ({",".join(filters)})' + base_id.extend(filters) + pl_id = '/'.join(base_id) + + return self.playlist_result( + self._entries(url, pl_id, html=html, page_num=int_or_none(qs.get('page'))), + playlist_id=pl_id, playlist_title=title) + + +class YouPornCategoryIE(YouPornListBase): + IE_DESC = 'YouPorn category, with sorting, filtering and pagination' + _VALID_URL = r'''(?x) + https?://(?:www\.)?youporn\.com/ + (?P<type>category)/(?P<id>[^/?#&]+) + (?:/(?P<sort>popular|views|rating|time|duration))?/?(?:[#?]|$) + ''' + _TESTS = [{ + 'note': 'Full list with pagination', + 'url': 'https://www.youporn.com/category/popular-with-women/popular/', + 'info_dict': { + 'id': 'popular-with-women/popular', + 'title': 'Category popular with women videos by popular', + }, + 'playlist_mincount': 39, + }, { + 'note': 'Filtered paginated list with single page result', + 'url': 'https://www.youporn.com/category/popular-with-women/duration/?min_minutes=10', + 'info_dict': { + 'id': 'popular-with-women/duration/min_minutes=10', + 'title': 'Category popular with women videos by duration (min_minutes=10)', + }, + 'playlist_mincount': 2, + # 'playlist_maxcount': 30, + }, { + 'note': 'Single page of full list', + 'url': 'https://www.youporn.com/category/popular-with-women/popular?page=1', + 'info_dict': { + 'id': 'popular-with-women/popular/page=1', + 'title': 'Category popular with women videos by popular (page=1)', + }, + 'playlist_count': 36, + }] + + +class YouPornChannelIE(YouPornListBase): + IE_DESC = 'YouPorn channel, with sorting and pagination' + _VALID_URL = r'''(?x) + https?://(?:www\.)?youporn\.com/ + (?P<type>channel)/(?P<id>[^/?#&]+) + (?:/(?P<sort>rating|views|duration))?/?(?:[#?]|$) + ''' + _TESTS = [{ + 'note': 'Full list with pagination', + 'url': 'https://www.youporn.com/channel/x-feeds/', + 'info_dict': { + 'id': 'x-feeds', + 'title': 'Channel X-Feeds videos', + }, + 'playlist_mincount': 37, + }, { + 'note': 'Single page of full list (no filters here)', + 'url': 'https://www.youporn.com/channel/x-feeds/duration?page=1', + 'info_dict': { + 'id': 'x-feeds/duration/page=1', + 'title': 'Channel X-Feeds videos by duration (page=1)', + }, + 'playlist_count': 24, + }] + + @staticmethod + def _get_title_from_slug(title_slug): + return re.sub(r'_', ' ', title_slug).title() + + +class YouPornCollectionIE(YouPornListBase): + IE_DESC = 'YouPorn collection (user playlist), with sorting and pagination' + _VALID_URL = r'''(?x) + https?://(?:www\.)?youporn\.com/ + (?P<type>collection)s/videos/(?P<id>\d+) + (?:/(?P<sort>rating|views|time|duration))?/?(?:[#?]|$) + ''' + _TESTS = [{ + 'note': 'Full list with pagination', + 'url': 'https://www.youporn.com/collections/videos/33044251/', + 'info_dict': { + 'id': '33044251', + 'title': 'Collection Sexy Lips videos', + 'uploader': 'ph-littlewillyb', + }, + 'playlist_mincount': 50, + }, { + 'note': 'Single page of full list (no filters here)', + 'url': 'https://www.youporn.com/collections/videos/33044251/time?page=1', + 'info_dict': { + 'id': '33044251/time/page=1', + 'title': 'Collection Sexy Lips videos by time (page=1)', + 'uploader': 'ph-littlewillyb', + }, + 'playlist_count': 20, + }] + + def _real_extract(self, url): + pl_id = self._match_id(url) + html = self._download_webpage(url, pl_id) + playlist = super()._real_extract(url, html=html) + infos = re.sub(r'\s+', ' ', clean_html(get_element_by_class( + 'collection-infos', html)) or '') + title, uploader = self._search_regex( + r'^\s*Collection: (?P<title>.+?) \d+ VIDEOS \d+ VIEWS \d+ days LAST UPDATED From: (?P<uploader>[\w_-]+)', + infos, 'title/uploader', group=('title', 'uploader'), default=(None, None)) + if title: + playlist.update({ + 'title': playlist['title'].replace(playlist['id'].split('/')[0], title), + 'uploader': uploader, + }) + + return playlist + + +class YouPornTagIE(YouPornListBase): + IE_DESC = 'YouPorn tag (porntags), with sorting, filtering and pagination' + _VALID_URL = r'''(?x) + https?://(?:www\.)?youporn\.com/ + porn(?P<type>tag)s/(?P<id>[^/?#&]+) + (?:/(?P<sort>views|rating|time|duration))?/?(?:[#?]|$) + ''' + _TESTS = [{ + 'note': 'Full list with pagination', + 'url': 'https://www.youporn.com/porntags/austrian', + 'info_dict': { + 'id': 'austrian', + 'title': 'Tag austrian videos', + }, + 'playlist_mincount': 33, + 'expected_warnings': ['YouPorn tag pages are not correctly cached'], + }, { + 'note': 'Filtered paginated list with single page result', + 'url': 'https://www.youporn.com/porntags/austrian/duration/?min_minutes=10', + 'info_dict': { + 'id': 'austrian/duration/min_minutes=10', + 'title': 'Tag austrian videos by duration (min_minutes=10)', + }, + 'playlist_mincount': 10, + # number of videos per page is (row x col) 2x3 + 6x4 + 2, or + 3, + # or more, varying with number of ads; let's set max as 9x4 + # NB col 1 may not be shown in non-JS page with site CSS and zoom 100% + # 'playlist_maxcount': 32, + 'expected_warnings': ['YouPorn tag pages are not correctly cached'], + }, { + 'note': 'Single page of full list', + 'url': 'https://www.youporn.com/porntags/austrian/?page=1', + 'info_dict': { + 'id': 'austrian/page=1', + 'title': 'Tag austrian videos (page=1)', + }, + 'playlist_mincount': 32, + # 'playlist_maxcount': 34, + 'expected_warnings': ['YouPorn tag pages are not correctly cached'], + }] + + def _real_extract(self, url): + self.report_warning( + 'YouPorn tag pages are not correctly cached and ' + 'often return incorrect results', only_once=True) + return super()._real_extract(url) + + +class YouPornStarIE(YouPornListBase): + IE_DESC = 'YouPorn Pornstar, with description, sorting and pagination' + _VALID_URL = r'''(?x) + https?://(?:www\.)?youporn\.com/ + (?P<type>pornstar)/(?P<id>[^/?#&]+) + (?:/(?P<sort>rating|views|duration))?/?(?:[#?]|$) + ''' + _TESTS = [{ + 'note': 'Full list with pagination', + 'url': 'https://www.youporn.com/pornstar/daynia/', + 'info_dict': { + 'id': 'daynia', + 'title': 'Pornstar Daynia videos', + 'description': r're:Daynia Rank \d+ Videos \d+ Views [\d,.]+ .+ Subscribers \d+', + }, + 'playlist_mincount': 40, + }, { + 'note': 'Single page of full list (no filters here)', + 'url': 'https://www.youporn.com/pornstar/daynia/?page=1', + 'info_dict': { + 'id': 'daynia/page=1', + 'title': 'Pornstar Daynia videos (page=1)', + 'description': 're:.{180,}', + }, + 'playlist_count': 26, + }] + + @staticmethod + def _get_title_from_slug(title_slug): + return re.sub(r'_', ' ', title_slug).title() + + def _real_extract(self, url): + pl_id = self._match_id(url) + html = self._download_webpage(url, pl_id) + playlist = super()._real_extract(url, html=html) + INFO_ELEMENT_RE = r'''(?x) + <div [^>]*\bclass\s*=\s*('|")(?:[\w$-]+\s+|\s)*?pornstar-info-wrapper(?:\s+[\w$-]+|\s)*\1[^>]*> + (?P<info>[\s\S]+?)(?:</div>\s*){6,} + ''' + + if infos := self._search_regex(INFO_ELEMENT_RE, html, 'infos', group='info', default=''): + infos = re.sub( + r'(?:\s*nl=nl)+\s*', ' ', + re.sub(r'(?u)\s+', ' ', clean_html(re.sub('\n', 'nl=nl', infos)))).replace('ribe Subsc', '') + + return { + **playlist, + 'description': infos.strip() or None, + } + + +class YouPornVideosIE(YouPornListBase): + IE_DESC = 'YouPorn video (browse) playlists, with sorting, filtering and pagination' + _VALID_URL = r'''(?x) + https?://(?:www\.)?youporn\.com/ + (?:(?P<id>browse)/)? + (?P<sort>(?(id) + (?:duration|rating|time|views)| + (?:most_(?:favou?rit|view)ed|recommended|top_rated)?)) + (?:[/#?]|$) + ''' + _TESTS = [{ + 'note': 'Full list with pagination (too long for test)', + 'url': 'https://www.youporn.com/', + 'info_dict': { + 'id': 'youporn', + 'title': 'YouPorn videos', + }, + 'only_matching': True, + }, { + 'note': 'Full list with pagination (too long for test)', + 'url': 'https://www.youporn.com/recommended', + 'info_dict': { + 'id': 'youporn/recommended', + 'title': 'YouPorn videos by recommended', + }, + 'only_matching': True, + }, { + 'note': 'Full list with pagination (too long for test)', + 'url': 'https://www.youporn.com/top_rated', + 'info_dict': { + 'id': 'youporn/top_rated', + 'title': 'YouPorn videos by top rated', + }, + 'only_matching': True, + }, { + 'note': 'Full list with pagination (too long for test)', + 'url': 'https://www.youporn.com/browse/time', + 'info_dict': { + 'id': 'browse/time', + 'title': 'YouPorn videos by time', + }, + 'only_matching': True, + }, { + 'note': 'Filtered paginated list with single page result', + 'url': 'https://www.youporn.com/most_favorited/?res=VR&max_minutes=2', + 'info_dict': { + 'id': 'youporn/most_favorited/max_minutes=2/res=VR', + 'title': 'YouPorn videos by most favorited (max_minutes=2,res=VR)', + }, + 'playlist_mincount': 10, + # 'playlist_maxcount': 28, + }, { + 'note': 'Filtered paginated list with several pages', + 'url': 'https://www.youporn.com/most_favorited/?res=VR&max_minutes=5', + 'info_dict': { + 'id': 'youporn/most_favorited/max_minutes=5/res=VR', + 'title': 'YouPorn videos by most favorited (max_minutes=5,res=VR)', + }, + 'playlist_mincount': 45, + }, { + 'note': 'Single page of full list', + 'url': 'https://www.youporn.com/browse/time?page=1', + 'info_dict': { + 'id': 'browse/time/page=1', + 'title': 'YouPorn videos by time (page=1)', + }, + 'playlist_count': 36, + }] + + @staticmethod + def _get_title_from_slug(title_slug): + return 'YouPorn' if title_slug == 'browse' else title_slug diff --git a/lib/yt_dlp/extractor/youtube.py b/lib/yt_dlp/extractor/youtube.py index e676c5cde..54da4e362 100644 --- a/lib/yt_dlp/extractor/youtube.py +++ b/lib/yt_dlp/extractor/youtube.py @@ -1325,6 +1325,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'uploader_url': 'https://www.youtube.com/@PhilippHagemeister', 'uploader_id': '@PhilippHagemeister', 'heatmap': 'count:100', + 'timestamp': 1349198244, } }, { @@ -1368,6 +1369,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'uploader_url': 'https://www.youtube.com/@PhilippHagemeister', 'uploader_id': '@PhilippHagemeister', 'heatmap': 'count:100', + 'timestamp': 1349198244, }, 'params': { 'skip_download': True, @@ -1454,6 +1456,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'comment_count': int, 'channel_is_verified': True, 'heatmap': 'count:100', + 'timestamp': 1401991663, }, }, { @@ -1513,6 +1516,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'uploader': 'Projekt Melody', 'uploader_url': 'https://www.youtube.com/@ProjektMelody', 'uploader_id': '@ProjektMelody', + 'timestamp': 1577508724, }, }, { @@ -1618,6 +1622,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'uploader_url': 'https://www.youtube.com/@Olympics', 'uploader_id': '@Olympics', 'channel_is_verified': True, + 'timestamp': 1440707674, }, 'params': { 'skip_download': 'requires avconv', @@ -1651,6 +1656,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'uploader': '孫ᄋᄅ', 'uploader_url': 'https://www.youtube.com/@AllenMeow', 'uploader_id': '@AllenMeow', + 'timestamp': 1299776999, }, }, # url_encoded_fmt_stream_map is empty string @@ -1794,6 +1800,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): }, }], 'params': {'skip_download': True}, + 'skip': 'Not multifeed anymore', }, { # 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_id': '@BKCHarvard', 'uploader_url': 'https://www.youtube.com/@BKCHarvard', + 'timestamp': 1422422076, }, 'params': { 'skip_download': True, @@ -1937,6 +1945,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'uploader_id': '@BernieSanders', 'channel_is_verified': True, 'heatmap': 'count:100', + 'timestamp': 1447987198, }, 'params': { 'skip_download': True, @@ -2000,6 +2009,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'uploader_id': '@Vsauce', 'comment_count': int, 'channel_is_verified': True, + 'timestamp': 1484761047, }, 'params': { 'skip_download': True, @@ -2155,6 +2165,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'uploader': 'l\'Or Vert asbl', 'uploader_url': 'https://www.youtube.com/@ElevageOrVert', 'uploader_id': '@ElevageOrVert', + 'timestamp': 1497343210, }, 'params': { 'skip_download': True, @@ -2193,6 +2204,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'uploader_id': '@Csharp-video-tutorialsBlogspot', 'channel_is_verified': True, 'heatmap': 'count:100', + 'timestamp': 1377976349, }, 'params': { 'skip_download': True, @@ -2275,6 +2287,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'uploader_id': '@CBSMornings', 'comment_count': int, 'channel_is_verified': True, + 'timestamp': 1405513526, } }, { @@ -2292,7 +2305,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'view_count': int, 'channel': 'Walk around Japan', '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, 'availability': 'public', 'channel_url': 'https://www.youtube.com/channel/UC3o_t8PzBmXf5S9b7GLx1Mw', @@ -2302,6 +2315,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'uploader': 'Walk around Japan', 'uploader_url': 'https://www.youtube.com/@walkaroundjapan7124', 'uploader_id': '@walkaroundjapan7124', + 'timestamp': 1605884416, }, 'params': { 'skip_download': True, @@ -2397,6 +2411,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'comment_count': int, 'channel_is_verified': True, 'heatmap': 'count:100', + 'timestamp': 1395685455, }, 'params': {'format': 'mhtml', 'skip_download': True} }, { # 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_id': '@LeonNguyen', '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) 'url': 'https://www.youtube.com/watch?v=mzZzzBU6lrM', @@ -2488,38 +2474,41 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'comment_count': int, 'channel_is_verified': True, 'heatmap': 'count:100', + 'timestamp': 1641172509, } }, - { # continuous livestream. Microformat upload date should be preferred. - # Upload date was 2021-06-19 (not UTC), while stream start is 2021-11-27 - 'url': 'https://www.youtube.com/watch?v=kgx4WGK0oNU', + { # continuous livestream. + # 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=jfKfPfyJRdk', 'info_dict': { - 'id': 'kgx4WGK0oNU', - '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}', + 'id': 'jfKfPfyJRdk', 'ext': 'mp4', - 'channel_id': 'UC84whx2xxsiA1gXHXXqKGOA', - 'availability': 'public', + 'channel_id': 'UCSJ4gkVC6NrvII8umztf0Ow', + 'like_count': int, + 'uploader': 'Lofi Girl', + 'categories': ['Music'], + 'concurrent_view_count': int, + 'playable_in_embed': True, + 'timestamp': 1657627949, + 'release_date': '20220712', + 'channel_url': 'https://www.youtube.com/channel/UCSJ4gkVC6NrvII8umztf0Ow', + 'description': 'md5:13a6f76df898f5674f9127139f3df6f7', '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', + 'thumbnail': 'https://i.ytimg.com/vi/jfKfPfyJRdk/maxresdefault.jpg', + 'release_timestamp': 1657641570, + 'uploader_url': 'https://www.youtube.com/@LofiGirl', 'channel_follower_count': int, - 'release_date': '20211127', - 'tags': 'count:39', - 'categories': ['People & Blogs'], - 'like_count': int, + 'channel_is_verified': True, + 'title': r're:^lofi hip hop radio 📚 - beats to relax/study to', 'view_count': int, - 'playable_in_embed': True, - 'description': 'md5:2ef1d002cad520f65825346e2084e49d', - 'concurrent_view_count': int, - 'uploader': 'Abao in Tokyo', - 'uploader_url': 'https://www.youtube.com/@abaointokyo', - 'uploader_id': '@abaointokyo', + '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', 'info_dict': { @@ -2545,6 +2534,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'uploader_id': '@lesmiscore', 'uploader': 'Lesmiscore', 'uploader_url': 'https://www.youtube.com/@lesmiscore', + 'timestamp': 1648005313, } }, { # Prefer primary title+description language metadata by default @@ -2572,6 +2562,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'uploader_url': 'https://www.youtube.com/@coletdjnz', 'uploader_id': '@coletdjnz', 'uploader': 'cole-dlp-test-acc', + 'timestamp': 1662677394, }, 'params': {'skip_download': True} }, { @@ -2585,7 +2576,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'duration': 5, 'live_status': 'not_live', 'channel_id': 'UCiu-3thuViMebBjw_5nWYrA', - 'upload_date': '20220728', + 'upload_date': '20220729', 'view_count': int, 'categories': ['People & Blogs'], 'thumbnail': r're:^https?://.*\.jpg', @@ -2598,6 +2589,8 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'uploader_url': 'https://www.youtube.com/@coletdjnz', 'uploader_id': '@coletdjnz', 'uploader': 'cole-dlp-test-acc', + 'timestamp': 1659073275, + 'like_count': int, }, 'params': {'skip_download': True, 'extractor_args': {'youtube': {'lang': ['fr']}}}, 'expected_warnings': [r'Preferring "fr" translated fields'], @@ -2663,6 +2656,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'uploader': 'Projekt Melody', 'uploader_id': '@ProjektMelody', 'uploader_url': 'https://www.youtube.com/@ProjektMelody', + 'timestamp': 1577508724, }, 'params': {'extractor_args': {'youtube': {'player_client': ['tv_embedded']}}, 'format': '251-drc'}, }, @@ -2697,6 +2691,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'uploader_id': '@sana_natori', 'channel_is_verified': True, 'heatmap': 'count:100', + 'timestamp': 1671798112, }, }, { @@ -2766,6 +2761,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): 'uploader_url': 'https://www.youtube.com/@ChristopherSykesDocumentaries', 'uploader_id': '@ChristopherSykesDocumentaries', 'heatmap': 'count:100', + 'timestamp': 1211825920, }, 'params': { 'skip_download': True, @@ -4622,19 +4618,31 @@ def process_language(container, base_url, lang_code, sub_name, query): 'uploader_id': channel_handle, '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 # 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 - upload_date = ( - unified_strdate(get_first(microformats, 'uploadDate')) - 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', []) - ): + if not upload_date or (not timestamp and live_status in ('not_live', None)): + # this should be in UTC, as configured in the cookie/client context upload_date = strftime_or_none( self._parse_time_text(self._get_text(vpir, 'dateText'))) or upload_date + info['upload_date'] = upload_date + info['timestamp'] = timestamp 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 diff --git a/lib/yt_dlp/extractor/zapiks.py b/lib/yt_dlp/extractor/zapiks.py index 88f526bbc..2a12aa509 100644 --- a/lib/yt_dlp/extractor/zapiks.py +++ b/lib/yt_dlp/extractor/zapiks.py @@ -2,11 +2,11 @@ from .common import InfoExtractor from ..utils import ( + int_or_none, parse_duration, parse_iso8601, - xpath_with_ns, xpath_text, - int_or_none, + xpath_with_ns, ) diff --git a/lib/yt_dlp/extractor/zhihu.py b/lib/yt_dlp/extractor/zhihu.py index c24b33874..18b22a5c7 100644 --- a/lib/yt_dlp/extractor/zhihu.py +++ b/lib/yt_dlp/extractor/zhihu.py @@ -1,5 +1,5 @@ from .common import InfoExtractor -from ..utils import format_field, float_or_none, int_or_none +from ..utils import float_or_none, format_field, int_or_none class ZhihuIE(InfoExtractor): diff --git a/lib/yt_dlp/extractor/zingmp3.py b/lib/yt_dlp/extractor/zingmp3.py index ff5eac89a..909a7a3ae 100644 --- a/lib/yt_dlp/extractor/zingmp3.py +++ b/lib/yt_dlp/extractor/zingmp3.py @@ -10,8 +10,8 @@ int_or_none, join_nonempty, try_call, + url_or_none, urljoin, - url_or_none ) from ..utils.traversal import traverse_obj diff --git a/lib/yt_dlp/extractor/zype.py b/lib/yt_dlp/extractor/zype.py index 2f3b4c47f..8d3156d64 100644 --- a/lib/yt_dlp/extractor/zype.py +++ b/lib/yt_dlp/extractor/zype.py @@ -3,8 +3,8 @@ from .common import InfoExtractor from ..networking.exceptions import HTTPError from ..utils import ( - dict_get, ExtractorError, + dict_get, int_or_none, js_to_json, parse_iso8601, diff --git a/lib/yt_dlp/options.py b/lib/yt_dlp/options.py index faa1ee563..997b575cd 100644 --- a/lib/yt_dlp/options.py +++ b/lib/yt_dlp/options.py @@ -478,7 +478,7 @@ def _alias_callback(option, opt_str, value, parser, opts, nargs): }, 'aliases': { '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'], - '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'], '2023': [], } diff --git a/lib/yt_dlp/utils/_utils.py b/lib/yt_dlp/utils/_utils.py index b63766912..42803bb6d 100644 --- a/lib/yt_dlp/utils/_utils.py +++ b/lib/yt_dlp/utils/_utils.py @@ -1134,7 +1134,7 @@ def is_path_like(f): return isinstance(f, (str, bytes, os.PathLike)) -def extract_timezone(date_str): +def extract_timezone(date_str, default=None): m = re.search( r'''(?x) ^.{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 $) ''', date_str) + timezone = None + if not m: 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()) if timezone is not None: date_str = date_str[:-len(m.group('tz'))] - timezone = dt.timedelta(hours=timezone or 0) + timezone = dt.timedelta(hours=timezone) else: date_str = date_str[:-len(m.group('tz'))] - if not m.group('sign'): - timezone = dt.timedelta() - else: + if m.group('sign'): sign = 1 if m.group('sign') == '+' else -1 timezone = dt.timedelta( hours=sign * int(m.group('hours')), 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 @@ -1172,10 +1176,9 @@ def parse_iso8601(date_str, delimiter='T', timezone=None): date_str = re.sub(r'\.[0-9]+', '', date_str) - if timezone is None: - timezone, date_str = extract_timezone(date_str) + timezone, date_str = extract_timezone(date_str, timezone) - with contextlib.suppress(ValueError): + with contextlib.suppress(ValueError, TypeError): date_format = f'%Y-%m-%d{delimiter}%H:%M:%S' dt_ = dt.datetime.strptime(date_str, date_format) - timezone return calendar.timegm(dt_.timetuple()) @@ -2522,7 +2525,7 @@ def fixup(url): return False # "#" cannot be stripped out since it is part of the URI # However, it can be safely stripped out if following a whitespace - return re.split(r'\s#', url, 1)[0].rstrip() + return re.split(r'\s#', url, maxsplit=1)[0].rstrip() with contextlib.closing(batch_fd) as fd: return [url for url in map(fixup, fd) if url] diff --git a/lib/yt_dlp/version.py b/lib/yt_dlp/version.py index 22c2c048d..415dc0eaf 100644 --- a/lib/yt_dlp/version.py +++ b/lib/yt_dlp/version.py @@ -1,8 +1,8 @@ # Autogenerated by devscripts/update-version.py -__version__ = '2024.04.09' +__version__ = '2024.05.26' -RELEASE_GIT_HEAD = 'ff07792676f404ffff6ee61b5638c9dc1a33a37a' +RELEASE_GIT_HEAD = 'ae2af1104f80caf2f47544763a33db2c17a3e1de' VARIANT = None @@ -12,4 +12,4 @@ ORIGIN = 'yt-dlp/yt-dlp' -_pkg_version = '2024.04.09' +_pkg_version = '2024.05.26' diff --git a/lib/yt_dlp_version b/lib/yt_dlp_version index 937cdeccc..f05ba841a 100644 --- a/lib/yt_dlp_version +++ b/lib/yt_dlp_version @@ -1 +1 @@ -a2e9031605d87c469be9ce98dbbdf4960b727338 \ No newline at end of file +c53c2e40fde8f2e15c7c62f8ca1a5d9e90ddc079 \ No newline at end of file