@ -5,6 +5,7 @@ import re
import json
import base64
import zlib
import xml.etree.ElementTree
from hashlib import sha1
from math import pow , sqrt , floor
@ -17,6 +18,7 @@ from ..utils import (
intlist_to_bytes ,
unified_strdate ,
clean_html ,
urlencode_postdata ,
)
from ..aes import (
aes_cbc_decrypt ,
@ -51,6 +53,26 @@ class CrunchyrollIE(InfoExtractor):
' 1080 ' : ( ' 80 ' , ' 108 ' ) ,
}
def _login ( self ) :
( username , password ) = self . _get_login_info ( )
if username is None :
return
self . report_login ( )
login_url = ' https://www.crunchyroll.com/?a=formhandler '
data = urlencode_postdata ( {
' formname ' : ' RpcApiUser_Login ' ,
' name ' : username ,
' password ' : password ,
} )
login_request = compat_urllib_request . Request ( login_url , data )
login_request . add_header ( ' Content-Type ' , ' application/x-www-form-urlencoded ' )
self . _download_webpage ( login_request , None , False , ' Wrong login info ' )
def _real_initialize ( self ) :
self . _login ( )
def _decrypt_subtitles ( self , data , iv , id ) :
data = bytes_to_intlist ( data )
iv = bytes_to_intlist ( iv )
@ -97,6 +119,75 @@ class CrunchyrollIE(InfoExtractor):
output + = ' %d \n %s --> %s \n %s \n \n ' % ( i , start , end , text )
return output
def _convert_subtitles_to_ass ( self , subtitles ) :
output = ' '
def ass_bool ( strvalue ) :
assvalue = ' 0 '
if strvalue == ' 1 ' :
assvalue = ' -1 '
return assvalue
sub_root = xml . etree . ElementTree . fromstring ( subtitles )
if not sub_root :
return output
output = ' [Script Info] \n '
output + = ' Title: %s \n ' % sub_root . attrib [ " title " ]
output + = ' ScriptType: v4.00+ \n '
output + = ' WrapStyle: %s \n ' % sub_root . attrib [ " wrap_style " ]
output + = ' PlayResX: %s \n ' % sub_root . attrib [ " play_res_x " ]
output + = ' PlayResY: %s \n ' % sub_root . attrib [ " play_res_y " ]
output + = """ ScaledBorderAndShadow: yes
[ V4 + Styles ]
Format : Name , Fontname , Fontsize , PrimaryColour , SecondaryColour , OutlineColour , BackColour , Bold , Italic , Underline , StrikeOut , ScaleX , ScaleY , Spacing , Angle , BorderStyle , Outline , Shadow , Alignment , MarginL , MarginR , MarginV , Encoding
"""
for style in sub_root . findall ( ' ./styles/style ' ) :
output + = ' Style: ' + style . attrib [ " name " ]
output + = ' , ' + style . attrib [ " font_name " ]
output + = ' , ' + style . attrib [ " font_size " ]
output + = ' , ' + style . attrib [ " primary_colour " ]
output + = ' , ' + style . attrib [ " secondary_colour " ]
output + = ' , ' + style . attrib [ " outline_colour " ]
output + = ' , ' + style . attrib [ " back_colour " ]
output + = ' , ' + ass_bool ( style . attrib [ " bold " ] )
output + = ' , ' + ass_bool ( style . attrib [ " italic " ] )
output + = ' , ' + ass_bool ( style . attrib [ " underline " ] )
output + = ' , ' + ass_bool ( style . attrib [ " strikeout " ] )
output + = ' , ' + style . attrib [ " scale_x " ]
output + = ' , ' + style . attrib [ " scale_y " ]
output + = ' , ' + style . attrib [ " spacing " ]
output + = ' , ' + style . attrib [ " angle " ]
output + = ' , ' + style . attrib [ " border_style " ]
output + = ' , ' + style . attrib [ " outline " ]
output + = ' , ' + style . attrib [ " shadow " ]
output + = ' , ' + style . attrib [ " alignment " ]
output + = ' , ' + style . attrib [ " margin_l " ]
output + = ' , ' + style . attrib [ " margin_r " ]
output + = ' , ' + style . attrib [ " margin_v " ]
output + = ' , ' + style . attrib [ " encoding " ]
output + = ' \n '
output + = """
[ Events ]
Format : Layer , Start , End , Style , Name , MarginL , MarginR , MarginV , Effect , Text
"""
for event in sub_root . findall ( ' ./events/event ' ) :
output + = ' Dialogue: 0 '
output + = ' , ' + event . attrib [ " start " ]
output + = ' , ' + event . attrib [ " end " ]
output + = ' , ' + event . attrib [ " style " ]
output + = ' , ' + event . attrib [ " name " ]
output + = ' , ' + event . attrib [ " margin_l " ]
output + = ' , ' + event . attrib [ " margin_r " ]
output + = ' , ' + event . attrib [ " margin_v " ]
output + = ' , ' + event . attrib [ " effect " ]
output + = ' , ' + event . attrib [ " text " ]
output + = ' \n '
return output
def _real_extract ( self , url ) :
mobj = re . match ( self . _VALID_URL , url )
video_id = mobj . group ( ' video_id ' )
@ -158,6 +249,7 @@ class CrunchyrollIE(InfoExtractor):
} )
subtitles = { }
sub_format = self . _downloader . params . get ( ' subtitlesformat ' , ' srt ' )
for sub_id , sub_name in re . findall ( r ' \ ?ssid=([0-9]+) " title= " ([^ " ]+) ' , webpage ) :
sub_page = self . _download_webpage ( ' http://www.crunchyroll.com/xml/?req=RpcApiSubtitle_GetXml&subtitle_script_id= ' + sub_id , \
video_id , note = ' Downloading subtitles for ' + sub_name )
@ -174,7 +266,10 @@ class CrunchyrollIE(InfoExtractor):
lang_code = self . _search_regex ( r ' lang_code=[ " \' ]([^ " \' ]+) ' , subtitle , ' subtitle_lang_code ' , fatal = False )
if not lang_code :
continue
subtitles [ lang_code ] = self . _convert_subtitles_to_srt ( subtitle )
if sub_format == ' ass ' :
subtitles [ lang_code ] = self . _convert_subtitles_to_ass ( subtitle )
else :
subtitles [ lang_code ] = self . _convert_subtitles_to_srt ( subtitle )
return {
' id ' : video_id ,