@ -1,5 +1,11 @@
from . common import InfoExtractor
from . . utils import js_to_json
from . . utils import (
ExtractorError ,
RegexNotFoundError ,
determine_ext ,
join_nonempty ,
js_to_json ,
)
import re
import json
import urllib . parse
@ -7,19 +13,72 @@ import base64
class RTPIE ( InfoExtractor ) :
_VALID_URL = r ' https?://(?: www\ .)?rtp \ .pt/play/ p(?P<program_id>[0-9]+)/(?P<id>[^/?#]+)/?'
_VALID_URL = r ' https?://(?: (?:(?: www\ .)?rtp \ .pt/play/ (?P<subarea>.*/)? p(?P<program_id>[0-9]+)/)|(?:arquivos\ .rtp \ .pt/conteudos/)) (?P<id>[^/?#]+)/?'
_TESTS = [ {
' url ' : ' http://www.rtp.pt/play/p405/e174042/paixoes-cruzadas ' ,
' md5 ' : ' e736ce0c665e459ddb818546220b4ef8 ' ,
' url ' : ' https://www.rtp.pt/play/p9165/e562949/por-do-sol ' ,
' info_dict ' : {
' id ' : ' e 174042 ' ,
' ext ' : ' mp 3 ' ,
' title ' : ' P aixões Cruzadas ' ,
' description ' : ' As paixões musicais de António Cartaxo e António Macedo ' ,
' thumbnail ' : r ' re:^https?://.* \ . jpg' ,
' id ' : ' e 562949 ' ,
' ext ' : ' mp 4 ' ,
' title ' : ' P ôr do Sol Episódio 1 ' ,
' description ' : ' Madalena Bourbon de Linhaça vive atormentada pelo segredo que esconde desde 1990. Matilde Bourbon de Linhaça sonha fugir com o seu amor proibido. O ' ,
' thumbnail ' : r ' re:^https?://.* \ . ( jpg|png) '
} ,
} , {
' url ' : ' http://www.rtp.pt/play/p831/a-quimica-das-coisas ' ,
' url ' : ' https://www.rtp.pt/play/p12646/e738493/telejornal ' ,
' info_dict ' : {
' id ' : ' e738493 ' ,
' ext ' : ' mp4 ' ,
' title ' : ' Telejornal de 01 jan 2024 PARTE 1 ' ,
' description ' : ' A mais rigorosa seleção de notícias, todos os dias às 20h00. De segunda a domingo, João Adelino Faria, José Rodrigues dos Santos e Ana Lourenço ' ,
' thumbnail ' : r ' re:^https?://.* \ .(jpg|png) '
} ,
} , {
' url ' : ' https://www.rtp.pt/play/p6646/e457262/grande-entrevista ' ,
' info_dict ' : {
' id ' : ' e457262 ' ,
' ext ' : ' mp4 ' ,
' title ' : ' Grande Entrevista Episódio 7 - de 19 fev 2020 ' ,
' description ' : ' Bruno Nogueira - É um dos mais originais humoristas portugueses e de maior êxito! Bruno Nogueira na Grande Entrevista com Vítor Gonçalves. ' ,
' thumbnail ' : r ' re:^https?://.* \ .(jpg|png) '
} ,
} , {
' url ' : ' https://www.rtp.pt/play/p8064/e750623/fronteira ' ,
' info_dict ' : {
' id ' : ' e750623 ' ,
' ext ' : ' mp4 ' ,
' title ' : ' Fronteira de 26 fev 2024 ' ,
' description ' : ' 1970. À aldeia de Fronteira chega um novo chefe de posto da Guarda Fiscal. Com convicções inabaláveis sobre a aplicação da Lei, rapidamente entr ' ,
' thumbnail ' : r ' re:^https?://.* \ .(jpg|png) '
} ,
} , {
' url ' : ' https://www.rtp.pt/play/estudoemcasa/p7776/e539826/portugues-1-ano ' ,
' info_dict ' : {
' id ' : ' e539826 ' ,
' ext ' : ' mp4 ' ,
' title ' : ' Português - 1.º ano , aula 45 - 27 abr 2021 - Estudo Em Casa - RTP ' ,
' description ' : ' A História do Pedrito Coelho, de Beatrix Potter. O dígrafo \' lh \' - A História do Pedrito Coelho, de Beatrix Potter. O dígrafo \' lh \' . ' ,
' thumbnail ' : r ' re:^https?://.* \ .(jpg|png) '
} ,
} , {
' url ' : ' https://www.rtp.pt/play/zigzag/p11099/e747372/coelhos-corajosos ' ,
' info_dict ' : {
' id ' : ' e747372 ' ,
' ext ' : ' mp4 ' ,
' title ' : ' Coelhos Corajosos Episódio 1 - de 12 fev 2024 - Zig Zag Play - RTP ' ,
' description ' : ' Boo e o seu irmão mais velho, Bop, vivem grandes aventuras com os seus amigos, e com os seus quatro irmãos pequeninos. Juntos e com muita coragem, e ' ,
' thumbnail ' : r ' re:^https?://.* \ .(jpg|png) '
} ,
} , {
' url ' : ' https://arquivos.rtp.pt/conteudos/liga-dos-ultimos-152/ ' ,
' info_dict ' : {
' id ' : ' liga-dos-ultimos-152 ' ,
' ext ' : ' mp4 ' ,
' title ' : ' Liga dos Últimos – RTP Arquivos ' ,
' description ' : ' Magazine desportivo, com apresentação de Álvaro Costa e comentários em estúdio do professor Hernâni Gonçalves e do sociólogo João Nuno Coelho. Destaque para os jogos de futebol das equipas dos escalões secundários de Portugal, com momentos dos jogos: Agrário de Lamas vs Pampilhoense e Apúlia vs Fragoso. ' ,
' thumbnail ' : r ' re:^https?://.* \ .(jpg|png) '
} ,
} , {
' url ' : ' https://www.rtp.pt/play/p510/aleixo-fm ' ,
' only_matching ' : True ,
} ]
@ -43,42 +102,78 @@ class RTPIE(InfoExtractor):
video_id = self . _match_id ( url )
webpage = self . _download_webpage ( url , video_id )
title = self . _html_search_meta (
' twitter:title ' , webpage , display_name = ' title ' , fatal = True )
f , config = self . _search_regex (
r ''' (?sx)
var \s + f \s * = \s * ( ? P < f > " .*? " | { [ ^ ; ] + ? } ) ; \s *
var \s + player1 \s + = \s + new \s + RTPPlayer \s * \( ( ? P < config > { ( ? : ( ? ! \* / ) . ) + ? } ) \) ; ( ? ! \s * \* / )
''' , webpage,
' player config ' , group = ( ' f ' , ' config ' ) )
f = self . _parse_json (
f , video_id ,
lambda data : self . __unobfuscate ( data , video_id = video_id ) )
config = self . _parse_json (
config , video_id ,
lambda data : self . __unobfuscate ( data , video_id = video_id ) )
title = self . _html_search_regex ( r ' <title>(.+?)</title> ' , webpage , ' title ' , default = ' ' )
# Raise error if episode is unavailable
if ' Este episódio não se encontra disponível ' in title :
raise ExtractorError ( ' Episode unavailable ' , expected = True )
# Replace irrelevant string in title
title = re . sub ( r ' - ?RTP Play - RTP ' , ' ' , title )
# Check if it's a program split in parts
part = self . _html_search_regex ( r ' section \ -parts.*<span.*>(.+?)</span>.*</ul> ' , webpage , ' part ' , default = None )
# Add program part identification to title if it exists
title = join_nonempty ( title , part , delim = ' ' )
try :
# Extract f and config from page
f , config = self . _search_regex (
r ''' (?sx)
var \s + f \s * = \s * ( ? P < f > " .*? " | { [ ^ ; ] + ? } ) ; \s *
var \s + player1 \s + = \s + new \s + RTPPlayer \s * \( ( ? P < config > { ( ? : ( ? ! \* / ) . ) + ? } ) \) ; ( ? ! \s * \* / )
''' , webpage,
' player config ' , group = ( ' f ' , ' config ' ) )
f = self . _parse_json (
f , video_id ,
lambda data : self . __unobfuscate ( data , video_id = video_id ) )
config = self . _parse_json (
config , video_id ,
lambda data : self . __unobfuscate ( data , video_id = video_id ) )
config [ ' file ' ] = f
except RegexNotFoundError :
# Estudo em Casa / Zig Zag / RTP Arquivos pages don't include f
config = self . _search_regex (
r ''' (?sx)
var \s + player1 \s + = \s + new \s + RTPPlayer \s * \( ( ? P < config > { ( ? : ( ? ! \* / ) . ) + ? } ) \) ; ( ? ! \s * \* / )
''' , webpage,
' just player config ' )
config = self . _parse_json (
config , video_id ,
lambda data : self . __unobfuscate ( data , video_id = video_id ) )
formats = [ ]
if isinstance ( f , dict ) :
f_hls = f . get ( ' hls ' )
if f_hls is not None :
formats . extend ( self . _extract_m3u8_formats (
f_hls , video_id , ' mp4 ' , ' m3u8_native ' , m3u8_id = ' hls ' ) )
file = config . get ( ' file ' )
if isinstance ( file , dict ) :
file_hls = file . get ( ' hls ' )
file_fps = file . get ( ' fps ' )
f_dash = f . get ( ' dash ' )
if f_dash is not None :
formats . extend ( self . _extract_mpd_formats ( f_dash , video_id , mpd_id = ' dash ' ) )
if file_hls is None and file_fps is not None :
file_hls = file_fps . replace ( ' drm-fps ' , ' hls ' )
formats . extend ( self . _extract_m3u8_formats (
file_hls , video_id , ' mp4 ' , ' m3u8_native ' , m3u8_id = ' hls ' ) )
else :
formats . append ( {
' format_id ' : ' f ' ,
' url ' : f ,
' vcodec ' : ' none ' if config . get ( ' mediaType ' ) == ' audio ' else None ,
} )
ext = determine_ext ( file )
subtitles = { }
if ext == ' m3u8 ' :
formats . extend ( self . _extract_m3u8_formats (
file , video_id , ' mp4 ' , ' m3u8_native ' , m3u8_id = ' hls ' ) )
else :
formats . append ( {
' format_id ' : ' f ' ,
' url ' : file ,
' vcodec ' : ' none ' if config . get ( ' mediaType ' ) == ' audio ' else None ,
} )
subtitles = { }
vtt = config . get ( ' vtt ' )
if vtt is not None :
for lcode , lname , url in vtt :