mirror of
				https://gitlab.com/ytdl-org/youtube-dl.git
				synced 2025-11-04 01:57:08 -05:00 
			
		
		
		
	[shahid] add support for show pages(closes #7401)
This commit is contained in:
		
							
								
								
									
										78
									
								
								youtube_dl/extractor/aws.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								youtube_dl/extractor/aws.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
				
			|||||||
 | 
					# coding: utf-8
 | 
				
			||||||
 | 
					from __future__ import unicode_literals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import datetime
 | 
				
			||||||
 | 
					import hashlib
 | 
				
			||||||
 | 
					import hmac
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .common import InfoExtractor
 | 
				
			||||||
 | 
					from ..compat import compat_urllib_parse_urlencode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AWSIE(InfoExtractor):
 | 
				
			||||||
 | 
					    _AWS_ALGORITHM = 'AWS4-HMAC-SHA256'
 | 
				
			||||||
 | 
					    _AWS_REGION = 'us-east-1'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _aws_execute_api(self, aws_dict, video_id, query=None):
 | 
				
			||||||
 | 
					        query = query or {}
 | 
				
			||||||
 | 
					        amz_date = datetime.datetime.utcnow().strftime('%Y%m%dT%H%M%SZ')
 | 
				
			||||||
 | 
					        date = amz_date[:8]
 | 
				
			||||||
 | 
					        headers = {
 | 
				
			||||||
 | 
					            'Accept': 'application/json',
 | 
				
			||||||
 | 
					            'Host': self._AWS_PROXY_HOST,
 | 
				
			||||||
 | 
					            'X-Amz-Date': amz_date,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        session_token = aws_dict.get('session_token')
 | 
				
			||||||
 | 
					        if session_token:
 | 
				
			||||||
 | 
					            headers['X-Amz-Security-Token'] = session_token
 | 
				
			||||||
 | 
					        headers['X-Api-Key'] = self._AWS_API_KEY
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def aws_hash(s):
 | 
				
			||||||
 | 
					            return hashlib.sha256(s.encode('utf-8')).hexdigest()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Task 1: http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
 | 
				
			||||||
 | 
					        canonical_querystring = compat_urllib_parse_urlencode(query)
 | 
				
			||||||
 | 
					        canonical_headers = ''
 | 
				
			||||||
 | 
					        for header_name, header_value in headers.items():
 | 
				
			||||||
 | 
					            canonical_headers += '%s:%s\n' % (header_name.lower(), header_value)
 | 
				
			||||||
 | 
					        signed_headers = ';'.join([header.lower() for header in headers.keys()])
 | 
				
			||||||
 | 
					        canonical_request = '\n'.join([
 | 
				
			||||||
 | 
					            'GET',
 | 
				
			||||||
 | 
					            aws_dict['uri'],
 | 
				
			||||||
 | 
					            canonical_querystring,
 | 
				
			||||||
 | 
					            canonical_headers,
 | 
				
			||||||
 | 
					            signed_headers,
 | 
				
			||||||
 | 
					            aws_hash('')
 | 
				
			||||||
 | 
					        ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Task 2: http://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
 | 
				
			||||||
 | 
					        credential_scope_list = [date, self._AWS_REGION, 'execute-api', 'aws4_request']
 | 
				
			||||||
 | 
					        credential_scope = '/'.join(credential_scope_list)
 | 
				
			||||||
 | 
					        string_to_sign = '\n'.join([self._AWS_ALGORITHM, amz_date, credential_scope, aws_hash(canonical_request)])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Task 3: http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
 | 
				
			||||||
 | 
					        def aws_hmac(key, msg):
 | 
				
			||||||
 | 
					            return hmac.new(key, msg.encode('utf-8'), hashlib.sha256)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def aws_hmac_digest(key, msg):
 | 
				
			||||||
 | 
					            return aws_hmac(key, msg).digest()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def aws_hmac_hexdigest(key, msg):
 | 
				
			||||||
 | 
					            return aws_hmac(key, msg).hexdigest()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        k_signing = ('AWS4' + aws_dict['secret_key']).encode('utf-8')
 | 
				
			||||||
 | 
					        for value in credential_scope_list:
 | 
				
			||||||
 | 
					            k_signing = aws_hmac_digest(k_signing, value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        signature = aws_hmac_hexdigest(k_signing, string_to_sign)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Task 4: http://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html
 | 
				
			||||||
 | 
					        headers['Authorization'] = ', '.join([
 | 
				
			||||||
 | 
					            '%s Credential=%s/%s' % (self._AWS_ALGORITHM, aws_dict['access_key'], credential_scope),
 | 
				
			||||||
 | 
					            'SignedHeaders=%s' % signed_headers,
 | 
				
			||||||
 | 
					            'Signature=%s' % signature,
 | 
				
			||||||
 | 
					        ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return self._download_json(
 | 
				
			||||||
 | 
					            'https://%s%s%s' % (self._AWS_PROXY_HOST, aws_dict['uri'], '?' + canonical_querystring if canonical_querystring else ''),
 | 
				
			||||||
 | 
					            video_id, headers=headers)
 | 
				
			||||||
@@ -927,7 +927,10 @@ from .sendtonews import SendtoNewsIE
 | 
				
			|||||||
from .servingsys import ServingSysIE
 | 
					from .servingsys import ServingSysIE
 | 
				
			||||||
from .servus import ServusIE
 | 
					from .servus import ServusIE
 | 
				
			||||||
from .sexu import SexuIE
 | 
					from .sexu import SexuIE
 | 
				
			||||||
from .shahid import ShahidIE
 | 
					from .shahid import (
 | 
				
			||||||
 | 
					    ShahidIE,
 | 
				
			||||||
 | 
					    ShahidShowIE,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from .shared import (
 | 
					from .shared import (
 | 
				
			||||||
    SharedIE,
 | 
					    SharedIE,
 | 
				
			||||||
    VivoIE,
 | 
					    VivoIE,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,13 +1,11 @@
 | 
				
			|||||||
# coding: utf-8
 | 
					# coding: utf-8
 | 
				
			||||||
from __future__ import unicode_literals
 | 
					from __future__ import unicode_literals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import datetime
 | 
					 | 
				
			||||||
import json
 | 
					import json
 | 
				
			||||||
import hashlib
 | 
					import hashlib
 | 
				
			||||||
import hmac
 | 
					 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .common import InfoExtractor
 | 
					from .aws import AWSIE
 | 
				
			||||||
from .anvato import AnvatoIE
 | 
					from .anvato import AnvatoIE
 | 
				
			||||||
from ..utils import (
 | 
					from ..utils import (
 | 
				
			||||||
    smuggle_url,
 | 
					    smuggle_url,
 | 
				
			||||||
@@ -16,7 +14,7 @@ from ..utils import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ScrippsNetworksWatchIE(InfoExtractor):
 | 
					class ScrippsNetworksWatchIE(AWSIE):
 | 
				
			||||||
    IE_NAME = 'scrippsnetworks:watch'
 | 
					    IE_NAME = 'scrippsnetworks:watch'
 | 
				
			||||||
    _VALID_URL = r'''(?x)
 | 
					    _VALID_URL = r'''(?x)
 | 
				
			||||||
                    https?://
 | 
					                    https?://
 | 
				
			||||||
@@ -64,44 +62,27 @@ class ScrippsNetworksWatchIE(InfoExtractor):
 | 
				
			|||||||
        'travelchannel': 'trav',
 | 
					        'travelchannel': 'trav',
 | 
				
			||||||
        'geniuskitchen': 'genius',
 | 
					        'geniuskitchen': 'genius',
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    _SNI_HOST = 'web.api.video.snidigital.com'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    _AWS_REGION = 'us-east-1'
 | 
					 | 
				
			||||||
    _AWS_IDENTITY_ID_JSON = json.dumps({
 | 
					 | 
				
			||||||
        'IdentityId': '%s:7655847c-0ae7-4d9b-80d6-56c062927eb3' % _AWS_REGION
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    _AWS_USER_AGENT = 'aws-sdk-js/2.80.0 callback'
 | 
					 | 
				
			||||||
    _AWS_API_KEY = 'E7wSQmq0qK6xPrF13WmzKiHo4BQ7tip4pQcSXVl1'
 | 
					    _AWS_API_KEY = 'E7wSQmq0qK6xPrF13WmzKiHo4BQ7tip4pQcSXVl1'
 | 
				
			||||||
    _AWS_SERVICE = 'execute-api'
 | 
					    _AWS_PROXY_HOST = 'web.api.video.snidigital.com'
 | 
				
			||||||
    _AWS_REQUEST = 'aws4_request'
 | 
					 | 
				
			||||||
    _AWS_SIGNED_HEADERS = ';'.join([
 | 
					 | 
				
			||||||
        'host', 'x-amz-date', 'x-amz-security-token', 'x-api-key'])
 | 
					 | 
				
			||||||
    _AWS_CANONICAL_REQUEST_TEMPLATE = '''GET
 | 
					 | 
				
			||||||
%(uri)s
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
host:%(host)s
 | 
					    _AWS_USER_AGENT = 'aws-sdk-js/2.80.0 callback'
 | 
				
			||||||
x-amz-date:%(date)s
 | 
					 | 
				
			||||||
x-amz-security-token:%(token)s
 | 
					 | 
				
			||||||
x-api-key:%(key)s
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
%(signed_headers)s
 | 
					 | 
				
			||||||
%(payload_hash)s'''
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _real_extract(self, url):
 | 
					    def _real_extract(self, url):
 | 
				
			||||||
        mobj = re.match(self._VALID_URL, url)
 | 
					        mobj = re.match(self._VALID_URL, url)
 | 
				
			||||||
        site_id, video_id = mobj.group('site', 'id')
 | 
					        site_id, video_id = mobj.group('site', 'id')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def aws_hash(s):
 | 
					        aws_identity_id_json = json.dumps({
 | 
				
			||||||
            return hashlib.sha256(s.encode('utf-8')).hexdigest()
 | 
					            'IdentityId': '%s:7655847c-0ae7-4d9b-80d6-56c062927eb3' % self._AWS_REGION
 | 
				
			||||||
 | 
					        }).encode('utf-8')
 | 
				
			||||||
        token = self._download_json(
 | 
					        token = self._download_json(
 | 
				
			||||||
            'https://cognito-identity.us-east-1.amazonaws.com/', video_id,
 | 
					            'https://cognito-identity.%s.amazonaws.com/' % self._AWS_REGION, video_id,
 | 
				
			||||||
            data=self._AWS_IDENTITY_ID_JSON.encode('utf-8'),
 | 
					            data=aws_identity_id_json,
 | 
				
			||||||
            headers={
 | 
					            headers={
 | 
				
			||||||
                'Accept': '*/*',
 | 
					                'Accept': '*/*',
 | 
				
			||||||
                'Content-Type': 'application/x-amz-json-1.1',
 | 
					                'Content-Type': 'application/x-amz-json-1.1',
 | 
				
			||||||
                'Referer': url,
 | 
					                'Referer': url,
 | 
				
			||||||
                'X-Amz-Content-Sha256': aws_hash(self._AWS_IDENTITY_ID_JSON),
 | 
					                'X-Amz-Content-Sha256': hashlib.sha256(aws_identity_id_json).hexdigest(),
 | 
				
			||||||
                'X-Amz-Target': 'AWSCognitoIdentityService.GetOpenIdToken',
 | 
					                'X-Amz-Target': 'AWSCognitoIdentityService.GetOpenIdToken',
 | 
				
			||||||
                'X-Amz-User-Agent': self._AWS_USER_AGENT,
 | 
					                'X-Amz-User-Agent': self._AWS_USER_AGENT,
 | 
				
			||||||
            })['Token']
 | 
					            })['Token']
 | 
				
			||||||
@@ -124,64 +105,12 @@ x-api-key:%(key)s
 | 
				
			|||||||
                sts, './/{https://sts.amazonaws.com/doc/2011-06-15/}%s' % key,
 | 
					                sts, './/{https://sts.amazonaws.com/doc/2011-06-15/}%s' % key,
 | 
				
			||||||
                fatal=True)
 | 
					                fatal=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        access_key_id = get('AccessKeyId')
 | 
					        mcp_id = self._aws_execute_api({
 | 
				
			||||||
        secret_access_key = get('SecretAccessKey')
 | 
					            'uri': '/1/web/brands/%s/episodes/scrid/%s' % (self._SNI_TABLE[site_id], video_id),
 | 
				
			||||||
        session_token = get('SessionToken')
 | 
					            'access_key': get('AccessKeyId'),
 | 
				
			||||||
 | 
					            'secret_key': get('SecretAccessKey'),
 | 
				
			||||||
        # Task 1: http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
 | 
					            'session_token': get('SessionToken'),
 | 
				
			||||||
        uri = '/1/web/brands/%s/episodes/scrid/%s' % (self._SNI_TABLE[site_id], video_id)
 | 
					        }, video_id)['results'][0]['mcpId']
 | 
				
			||||||
        datetime_now = datetime.datetime.utcnow().strftime('%Y%m%dT%H%M%SZ')
 | 
					 | 
				
			||||||
        date = datetime_now[:8]
 | 
					 | 
				
			||||||
        canonical_string = self._AWS_CANONICAL_REQUEST_TEMPLATE % {
 | 
					 | 
				
			||||||
            'uri': uri,
 | 
					 | 
				
			||||||
            'host': self._SNI_HOST,
 | 
					 | 
				
			||||||
            'date': datetime_now,
 | 
					 | 
				
			||||||
            'token': session_token,
 | 
					 | 
				
			||||||
            'key': self._AWS_API_KEY,
 | 
					 | 
				
			||||||
            'signed_headers': self._AWS_SIGNED_HEADERS,
 | 
					 | 
				
			||||||
            'payload_hash': aws_hash(''),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Task 2: http://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
 | 
					 | 
				
			||||||
        credential_string = '/'.join([date, self._AWS_REGION, self._AWS_SERVICE, self._AWS_REQUEST])
 | 
					 | 
				
			||||||
        string_to_sign = '\n'.join([
 | 
					 | 
				
			||||||
            'AWS4-HMAC-SHA256', datetime_now, credential_string,
 | 
					 | 
				
			||||||
            aws_hash(canonical_string)])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # Task 3: http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
 | 
					 | 
				
			||||||
        def aws_hmac(key, msg):
 | 
					 | 
				
			||||||
            return hmac.new(key, msg.encode('utf-8'), hashlib.sha256)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def aws_hmac_digest(key, msg):
 | 
					 | 
				
			||||||
            return aws_hmac(key, msg).digest()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        def aws_hmac_hexdigest(key, msg):
 | 
					 | 
				
			||||||
            return aws_hmac(key, msg).hexdigest()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        k_secret = 'AWS4' + secret_access_key
 | 
					 | 
				
			||||||
        k_date = aws_hmac_digest(k_secret.encode('utf-8'), date)
 | 
					 | 
				
			||||||
        k_region = aws_hmac_digest(k_date, self._AWS_REGION)
 | 
					 | 
				
			||||||
        k_service = aws_hmac_digest(k_region, self._AWS_SERVICE)
 | 
					 | 
				
			||||||
        k_signing = aws_hmac_digest(k_service, self._AWS_REQUEST)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        signature = aws_hmac_hexdigest(k_signing, string_to_sign)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        auth_header = ', '.join([
 | 
					 | 
				
			||||||
            'AWS4-HMAC-SHA256 Credential=%s' % '/'.join(
 | 
					 | 
				
			||||||
                [access_key_id, date, self._AWS_REGION, self._AWS_SERVICE, self._AWS_REQUEST]),
 | 
					 | 
				
			||||||
            'SignedHeaders=%s' % self._AWS_SIGNED_HEADERS,
 | 
					 | 
				
			||||||
            'Signature=%s' % signature,
 | 
					 | 
				
			||||||
        ])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        mcp_id = self._download_json(
 | 
					 | 
				
			||||||
            'https://%s%s' % (self._SNI_HOST, uri), video_id, headers={
 | 
					 | 
				
			||||||
                'Accept': '*/*',
 | 
					 | 
				
			||||||
                'Referer': url,
 | 
					 | 
				
			||||||
                'Authorization': auth_header,
 | 
					 | 
				
			||||||
                'X-Amz-Date': datetime_now,
 | 
					 | 
				
			||||||
                'X-Amz-Security-Token': session_token,
 | 
					 | 
				
			||||||
                'X-Api-Key': self._AWS_API_KEY,
 | 
					 | 
				
			||||||
            })['results'][0]['mcpId']
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return self.url_result(
 | 
					        return self.url_result(
 | 
				
			||||||
            smuggle_url(
 | 
					            smuggle_url(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,22 +1,53 @@
 | 
				
			|||||||
# coding: utf-8
 | 
					# coding: utf-8
 | 
				
			||||||
from __future__ import unicode_literals
 | 
					from __future__ import unicode_literals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import re
 | 
					 | 
				
			||||||
import json
 | 
					import json
 | 
				
			||||||
 | 
					import math
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .common import InfoExtractor
 | 
					from .aws import AWSIE
 | 
				
			||||||
from ..compat import compat_HTTPError
 | 
					from ..compat import compat_HTTPError
 | 
				
			||||||
from ..utils import (
 | 
					from ..utils import (
 | 
				
			||||||
 | 
					    clean_html,
 | 
				
			||||||
    ExtractorError,
 | 
					    ExtractorError,
 | 
				
			||||||
 | 
					    InAdvancePagedList,
 | 
				
			||||||
    int_or_none,
 | 
					    int_or_none,
 | 
				
			||||||
    parse_iso8601,
 | 
					    parse_iso8601,
 | 
				
			||||||
    str_or_none,
 | 
					    str_or_none,
 | 
				
			||||||
    urlencode_postdata,
 | 
					    urlencode_postdata,
 | 
				
			||||||
    clean_html,
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ShahidIE(InfoExtractor):
 | 
					class ShahidBaseIE(AWSIE):
 | 
				
			||||||
 | 
					    _AWS_PROXY_HOST = 'api2.shahid.net'
 | 
				
			||||||
 | 
					    _AWS_API_KEY = '2RRtuMHx95aNI1Kvtn2rChEuwsCogUd4samGPjLh'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _handle_error(self, e):
 | 
				
			||||||
 | 
					        fail_data = self._parse_json(
 | 
				
			||||||
 | 
					            e.cause.read().decode('utf-8'), None, fatal=False)
 | 
				
			||||||
 | 
					        if fail_data:
 | 
				
			||||||
 | 
					            faults = fail_data.get('faults', [])
 | 
				
			||||||
 | 
					            faults_message = ', '.join([clean_html(fault['userMessage']) for fault in faults if fault.get('userMessage')])
 | 
				
			||||||
 | 
					            if faults_message:
 | 
				
			||||||
 | 
					                raise ExtractorError(faults_message, expected=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _call_api(self, path, video_id, request=None):
 | 
				
			||||||
 | 
					        query = {}
 | 
				
			||||||
 | 
					        if request:
 | 
				
			||||||
 | 
					            query['request'] = json.dumps(request)
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            return self._aws_execute_api({
 | 
				
			||||||
 | 
					                'uri': '/proxy/v2/' + path,
 | 
				
			||||||
 | 
					                'access_key': 'AKIAI6X4TYCIXM2B7MUQ',
 | 
				
			||||||
 | 
					                'secret_key': '4WUUJWuFvtTkXbhaWTDv7MhO+0LqoYDWfEnUXoWn',
 | 
				
			||||||
 | 
					            }, video_id, query)
 | 
				
			||||||
 | 
					        except ExtractorError as e:
 | 
				
			||||||
 | 
					            if isinstance(e.cause, compat_HTTPError):
 | 
				
			||||||
 | 
					                self._handle_error(e)
 | 
				
			||||||
 | 
					            raise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ShahidIE(ShahidBaseIE):
 | 
				
			||||||
    _NETRC_MACHINE = 'shahid'
 | 
					    _NETRC_MACHINE = 'shahid'
 | 
				
			||||||
    _VALID_URL = r'https?://shahid\.mbc\.net/ar/(?:serie|show|movie)s/[^/]+/(?P<type>episode|clip|movie)-(?P<id>\d+)'
 | 
					    _VALID_URL = r'https?://shahid\.mbc\.net/ar/(?:serie|show|movie)s/[^/]+/(?P<type>episode|clip|movie)-(?P<id>\d+)'
 | 
				
			||||||
    _TESTS = [{
 | 
					    _TESTS = [{
 | 
				
			||||||
@@ -41,26 +72,13 @@ class ShahidIE(InfoExtractor):
 | 
				
			|||||||
        'only_matching': True
 | 
					        'only_matching': True
 | 
				
			||||||
    }]
 | 
					    }]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _api2_request(self, *args, **kwargs):
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            return self._download_json(*args, **kwargs)
 | 
					 | 
				
			||||||
        except ExtractorError as e:
 | 
					 | 
				
			||||||
            if isinstance(e.cause, compat_HTTPError):
 | 
					 | 
				
			||||||
                fail_data = self._parse_json(
 | 
					 | 
				
			||||||
                    e.cause.read().decode('utf-8'), None, fatal=False)
 | 
					 | 
				
			||||||
                if fail_data:
 | 
					 | 
				
			||||||
                    faults = fail_data.get('faults', [])
 | 
					 | 
				
			||||||
                    faults_message = ', '.join([clean_html(fault['userMessage']) for fault in faults if fault.get('userMessage')])
 | 
					 | 
				
			||||||
                    if faults_message:
 | 
					 | 
				
			||||||
                        raise ExtractorError(faults_message, expected=True)
 | 
					 | 
				
			||||||
            raise
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _real_initialize(self):
 | 
					    def _real_initialize(self):
 | 
				
			||||||
        email, password = self._get_login_info()
 | 
					        email, password = self._get_login_info()
 | 
				
			||||||
        if email is None:
 | 
					        if email is None:
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        user_data = self._api2_request(
 | 
					        try:
 | 
				
			||||||
 | 
					            user_data = self._download_json(
 | 
				
			||||||
                'https://shahid.mbc.net/wd/service/users/login',
 | 
					                'https://shahid.mbc.net/wd/service/users/login',
 | 
				
			||||||
                None, 'Logging in', data=json.dumps({
 | 
					                None, 'Logging in', data=json.dumps({
 | 
				
			||||||
                    'email': email,
 | 
					                    'email': email,
 | 
				
			||||||
@@ -69,6 +87,10 @@ class ShahidIE(InfoExtractor):
 | 
				
			|||||||
                }).encode('utf-8'), headers={
 | 
					                }).encode('utf-8'), headers={
 | 
				
			||||||
                    'Content-Type': 'application/json; charset=UTF-8',
 | 
					                    'Content-Type': 'application/json; charset=UTF-8',
 | 
				
			||||||
                })['user']
 | 
					                })['user']
 | 
				
			||||||
 | 
					        except ExtractorError as e:
 | 
				
			||||||
 | 
					            if isinstance(e.cause, compat_HTTPError):
 | 
				
			||||||
 | 
					                self._handle_error(e)
 | 
				
			||||||
 | 
					            raise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._download_webpage(
 | 
					        self._download_webpage(
 | 
				
			||||||
            'https://shahid.mbc.net/populateContext',
 | 
					            'https://shahid.mbc.net/populateContext',
 | 
				
			||||||
@@ -81,25 +103,13 @@ class ShahidIE(InfoExtractor):
 | 
				
			|||||||
                'sessionId': user_data['sessionId'],
 | 
					                'sessionId': user_data['sessionId'],
 | 
				
			||||||
            }))
 | 
					            }))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _get_api_data(self, response):
 | 
					 | 
				
			||||||
        data = response.get('data', {})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        error = data.get('error')
 | 
					 | 
				
			||||||
        if error:
 | 
					 | 
				
			||||||
            raise ExtractorError(
 | 
					 | 
				
			||||||
                '%s returned error: %s' % (self.IE_NAME, '\n'.join(error.values())),
 | 
					 | 
				
			||||||
                expected=True)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return data
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _real_extract(self, url):
 | 
					    def _real_extract(self, url):
 | 
				
			||||||
        page_type, video_id = re.match(self._VALID_URL, url).groups()
 | 
					        page_type, video_id = re.match(self._VALID_URL, url).groups()
 | 
				
			||||||
        if page_type == 'clip':
 | 
					        if page_type == 'clip':
 | 
				
			||||||
            page_type = 'episode'
 | 
					            page_type = 'episode'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        playout = self._api2_request(
 | 
					        playout = self._call_api(
 | 
				
			||||||
            'https://api2.shahid.net/proxy/v2/playout/url/' + video_id,
 | 
					            'playout/url/' + video_id, video_id)['playout']
 | 
				
			||||||
            video_id, 'Downloading player JSON')['playout']
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if playout.get('drm'):
 | 
					        if playout.get('drm'):
 | 
				
			||||||
            raise ExtractorError('This video is DRM protected.', expected=True)
 | 
					            raise ExtractorError('This video is DRM protected.', expected=True)
 | 
				
			||||||
@@ -107,13 +117,27 @@ class ShahidIE(InfoExtractor):
 | 
				
			|||||||
        formats = self._extract_m3u8_formats(playout['url'], video_id, 'mp4')
 | 
					        formats = self._extract_m3u8_formats(playout['url'], video_id, 'mp4')
 | 
				
			||||||
        self._sort_formats(formats)
 | 
					        self._sort_formats(formats)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        video = self._get_api_data(self._download_json(
 | 
					        # video = self._call_api(
 | 
				
			||||||
 | 
					        #     'product/id', video_id, {
 | 
				
			||||||
 | 
					        #         'id': video_id,
 | 
				
			||||||
 | 
					        #         'productType': 'ASSET',
 | 
				
			||||||
 | 
					        #         'productSubType': page_type.upper()
 | 
				
			||||||
 | 
					        #     })['productModel']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        response = self._download_json(
 | 
				
			||||||
            'http://api.shahid.net/api/v1_1/%s/%s' % (page_type, video_id),
 | 
					            'http://api.shahid.net/api/v1_1/%s/%s' % (page_type, video_id),
 | 
				
			||||||
            video_id, 'Downloading video JSON', query={
 | 
					            video_id, 'Downloading video JSON', query={
 | 
				
			||||||
                'apiKey': 'sh@hid0nlin3',
 | 
					                'apiKey': 'sh@hid0nlin3',
 | 
				
			||||||
                'hash': 'b2wMCTHpSmyxGqQjJFOycRmLSex+BpTK/ooxy6vHaqs=',
 | 
					                'hash': 'b2wMCTHpSmyxGqQjJFOycRmLSex+BpTK/ooxy6vHaqs=',
 | 
				
			||||||
            }))[page_type]
 | 
					            })
 | 
				
			||||||
 | 
					        data = response.get('data', {})
 | 
				
			||||||
 | 
					        error = data.get('error')
 | 
				
			||||||
 | 
					        if error:
 | 
				
			||||||
 | 
					            raise ExtractorError(
 | 
				
			||||||
 | 
					                '%s returned error: %s' % (self.IE_NAME, '\n'.join(error.values())),
 | 
				
			||||||
 | 
					                expected=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        video = data[page_type]
 | 
				
			||||||
        title = video['title']
 | 
					        title = video['title']
 | 
				
			||||||
        categories = [
 | 
					        categories = [
 | 
				
			||||||
            category['name']
 | 
					            category['name']
 | 
				
			||||||
@@ -135,3 +159,57 @@ class ShahidIE(InfoExtractor):
 | 
				
			|||||||
            'episode_id': video_id,
 | 
					            'episode_id': video_id,
 | 
				
			||||||
            'formats': formats,
 | 
					            'formats': formats,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ShahidShowIE(ShahidBaseIE):
 | 
				
			||||||
 | 
					    _VALID_URL = r'https?://shahid\.mbc\.net/ar/(?:show|serie)s/[^/]+/(?:show|series)-(?P<id>\d+)'
 | 
				
			||||||
 | 
					    _TESTS = [{
 | 
				
			||||||
 | 
					        'url': 'https://shahid.mbc.net/ar/shows/%D8%B1%D8%A7%D9%85%D8%B2-%D9%82%D8%B1%D8%B4-%D8%A7%D9%84%D8%A8%D8%AD%D8%B1/show-79187',
 | 
				
			||||||
 | 
					        'info_dict': {
 | 
				
			||||||
 | 
					            'id': '79187',
 | 
				
			||||||
 | 
					            'title': 'رامز قرش البحر',
 | 
				
			||||||
 | 
					            'description': 'md5:c88fa7e0f02b0abd39d417aee0d046ff',
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'playlist_mincount': 32,
 | 
				
			||||||
 | 
					    }, {
 | 
				
			||||||
 | 
					        'url': 'https://shahid.mbc.net/ar/series/How-to-live-Longer-(The-Big-Think)/series-291861',
 | 
				
			||||||
 | 
					        'only_matching': True
 | 
				
			||||||
 | 
					    }]
 | 
				
			||||||
 | 
					    _PAGE_SIZE = 30
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _real_extract(self, url):
 | 
				
			||||||
 | 
					        show_id = self._match_id(url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        product = self._call_api(
 | 
				
			||||||
 | 
					            'playableAsset', show_id, {'showId': show_id})['productModel']
 | 
				
			||||||
 | 
					        playlist = product['playlist']
 | 
				
			||||||
 | 
					        playlist_id = playlist['id']
 | 
				
			||||||
 | 
					        show = product.get('show', {})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def page_func(page_num):
 | 
				
			||||||
 | 
					            playlist = self._call_api(
 | 
				
			||||||
 | 
					                'product/playlist', show_id, {
 | 
				
			||||||
 | 
					                    'playListId': playlist_id,
 | 
				
			||||||
 | 
					                    'pageNumber': page_num,
 | 
				
			||||||
 | 
					                    'pageSize': 30,
 | 
				
			||||||
 | 
					                    'sorts': [{
 | 
				
			||||||
 | 
					                        'order': 'DESC',
 | 
				
			||||||
 | 
					                        'type': 'SORTDATE'
 | 
				
			||||||
 | 
					                    }],
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					            for product in playlist.get('productList', {}).get('products', []):
 | 
				
			||||||
 | 
					                product_url = product.get('productUrl', []).get('url')
 | 
				
			||||||
 | 
					                if not product_url:
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					                yield self.url_result(
 | 
				
			||||||
 | 
					                    product_url, 'Shahid',
 | 
				
			||||||
 | 
					                    str_or_none(product.get('id')),
 | 
				
			||||||
 | 
					                    product.get('title'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        entries = InAdvancePagedList(
 | 
				
			||||||
 | 
					            page_func,
 | 
				
			||||||
 | 
					            math.ceil(playlist['count'] / self._PAGE_SIZE),
 | 
				
			||||||
 | 
					            self._PAGE_SIZE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return self.playlist_result(
 | 
				
			||||||
 | 
					            entries, show_id, show.get('title'), show.get('description'))
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user