2016-10-02 13:39:18 +02:00
# coding: utf-8
2014-01-17 03:29:41 +01:00
from __future__ import unicode_literals
2013-09-14 21:41:49 +02:00
import itertools
2017-09-01 10:08:24 +02:00
import re
2020-08-05 19:10:25 +02:00
import time
import random
2013-06-23 20:57:44 +02:00
2015-10-17 18:23:46 +02:00
from . common import (
InfoExtractor ,
SearchInfoExtractor
)
2014-12-13 12:24:42 +01:00
from . . compat import (
2019-12-31 09:49:29 +01:00
compat_HTTPError ,
compat_kwargs ,
2013-06-23 20:57:44 +02:00
compat_str ,
2013-08-21 17:06:37 +02:00
compat_urlparse ,
2014-12-13 12:24:42 +01:00
)
from . . utils import (
2019-12-31 21:31:22 +01:00
error_to_compat_str ,
2013-06-23 20:57:44 +02:00
ExtractorError ,
2019-04-22 19:39:16 +02:00
float_or_none ,
2019-10-27 17:52:46 +01:00
HEADRequest ,
2014-06-07 15:51:01 +02:00
int_or_none ,
2019-04-22 19:39:16 +02:00
KNOWN_EXTENSIONS ,
mimetype2ext ,
str_or_none ,
2019-02-10 17:44:08 +01:00
try_get ,
unified_timestamp ,
2017-09-03 11:18:24 +02:00
update_url_query ,
2019-02-02 17:40:06 +01:00
url_or_none ,
2020-03-22 09:24:07 +01:00
urlhandle_detect_ext ,
2017-09-03 11:18:24 +02:00
)
2013-06-23 20:57:44 +02:00
2019-10-27 17:52:46 +01:00
class SoundcloudEmbedIE ( InfoExtractor ) :
2019-12-09 14:38:12 +01:00
_VALID_URL = r ' https?://(?:w|player|p) \ .soundcloud \ .com/player/?.*? \ burl=(?P<id>.+) '
_TEST = {
# from https://www.soundi.fi/uutiset/ennakkokuuntelussa-timo-kaukolammen-station-to-station-to-station-julkaisua-juhlitaan-tanaan-g-livelabissa/
' url ' : ' https://w.soundcloud.com/player/?visual=true&url=https % 3A %2F %2F api.soundcloud.com %2F playlists %2F 922213810&show_artwork=true&maxwidth=640&maxheight=960&dnt=1&secret_token=s-ziYey ' ,
' only_matching ' : True ,
}
2019-10-27 17:52:46 +01:00
@staticmethod
def _extract_urls ( webpage ) :
return [ m . group ( ' url ' ) for m in re . finditer (
r ' <iframe[^>]+src=([ " \' ])(?P<url>(?:https?://)?(?:w \ .)?soundcloud \ .com/player.+?) \ 1 ' ,
webpage ) ]
def _real_extract ( self , url ) :
2019-12-09 14:38:12 +01:00
query = compat_urlparse . parse_qs (
compat_urlparse . urlparse ( url ) . query )
api_url = query [ ' url ' ] [ 0 ]
secret_token = query . get ( ' secret_token ' )
if secret_token :
api_url = update_url_query ( api_url , { ' secret_token ' : secret_token [ 0 ] } )
return self . url_result ( api_url )
2019-10-27 17:52:46 +01:00
2013-06-23 20:57:44 +02:00
class SoundcloudIE ( InfoExtractor ) :
""" Information extractor for soundcloud.com
To access the media , the uid of the song and a stream token
must be extracted from the page source and the script must make
a request to media . soundcloud . com / crossdomain . xml . Then
the media can be grabbed by requesting from an url composed
of the stream token and uid
"""
2014-05-05 03:12:41 +02:00
_VALID_URL = r ''' (?x)^(?:https?://)?
2013-12-19 16:39:01 +01:00
( ? : ( ? : ( ? : www \. | m \. ) ? soundcloud \. com /
2017-07-29 13:41:42 +02:00
( ? ! stations / track )
2013-12-09 19:57:00 +01:00
( ? P < uploader > [ \w \d - ] + ) /
2019-02-01 22:00:29 +01:00
( ? ! ( ? : tracks | albums | sets ( ? : / . + ? ) ? | reposts | likes | spotlight ) / ? ( ? : $ | [ ? #]))
2014-08-28 00:58:24 +02:00
( ? P < title > [ \w \d - ] + ) / ?
2013-12-09 17:08:58 +01:00
( ? P < token > [ ^ ? ] + ? ) ? ( ? : [ ? ] . * ) ? $ )
2019-10-27 17:52:46 +01:00
| ( ? : api ( ? : - v2 ) ? \. soundcloud \. com / tracks / ( ? P < track_id > \d + )
2014-12-03 11:49:53 +01:00
( ? : / ? \? secret_token = ( ? P < secret_token > [ ^ & ] + ) ) ? )
2013-07-24 14:39:21 +02:00
)
'''
2014-01-17 03:29:41 +01:00
IE_NAME = ' soundcloud '
2013-11-09 18:06:09 +01:00
_TESTS = [
{
2014-01-17 03:29:41 +01:00
' url ' : ' http://soundcloud.com/ethmusic/lostin-powers-she-so-heavy ' ,
' md5 ' : ' ebef0a451b909710ed1d7787dddbf0d7 ' ,
' info_dict ' : {
2014-10-25 22:32:01 +02:00
' id ' : ' 62986583 ' ,
' ext ' : ' mp3 ' ,
2019-02-10 17:44:08 +01:00
' title ' : ' Lostin Powers - She so Heavy (SneakPreview) Adrian Ackers Blueprint 1 ' ,
2014-10-25 22:32:01 +02:00
' description ' : ' No Downloads untill we record the finished version this weekend, i was too pumped n i had to post it , earl is prolly gonna b hella p.o \' d ' ,
' uploader ' : ' E.T. ExTerrestrial Music ' ,
2019-10-27 17:52:46 +01:00
' uploader_id ' : ' 1571244 ' ,
2019-02-10 17:44:08 +01:00
' timestamp ' : 1349920598 ,
' upload_date ' : ' 20121011 ' ,
2019-04-22 19:39:16 +02:00
' duration ' : 143.216 ,
2016-09-18 11:53:05 +02:00
' license ' : ' all-rights-reserved ' ,
2019-02-10 17:44:08 +01:00
' view_count ' : int ,
' like_count ' : int ,
' comment_count ' : int ,
' repost_count ' : int ,
2013-11-09 18:06:09 +01:00
}
} ,
2020-03-22 09:24:07 +01:00
# geo-restricted
2013-11-09 18:06:09 +01:00
{
2014-01-17 03:29:41 +01:00
' url ' : ' https://soundcloud.com/the-concept-band/goldrushed-mastered?in=the-concept-band/sets/the-royal-concept-ep ' ,
' info_dict ' : {
' id ' : ' 47127627 ' ,
' ext ' : ' mp3 ' ,
' title ' : ' Goldrushed ' ,
2014-03-09 12:20:34 +01:00
' description ' : ' From Stockholm Sweden \r \n Povel / Magnus / Filip / David \r \n www.theroyalconcept.com ' ,
2014-01-17 03:29:41 +01:00
' uploader ' : ' The Royal Concept ' ,
2019-10-27 17:52:46 +01:00
' uploader_id ' : ' 9615865 ' ,
2019-02-10 17:44:08 +01:00
' timestamp ' : 1337635207 ,
2014-01-17 03:29:41 +01:00
' upload_date ' : ' 20120521 ' ,
2020-03-22 09:24:07 +01:00
' duration ' : 227.155 ,
2016-09-18 11:53:05 +02:00
' license ' : ' all-rights-reserved ' ,
2019-02-10 17:44:08 +01:00
' view_count ' : int ,
' like_count ' : int ,
' comment_count ' : int ,
' repost_count ' : int ,
2013-11-09 18:06:09 +01:00
} ,
} ,
2013-12-09 17:08:58 +01:00
# private link
{
2014-01-17 03:29:41 +01:00
' url ' : ' https://soundcloud.com/jaimemf/youtube-dl-test-video-a-y-baw/s-8Pjrp ' ,
' md5 ' : ' aa0dd32bfea9b0c5ef4f02aacd080604 ' ,
' info_dict ' : {
' id ' : ' 123998367 ' ,
' ext ' : ' mp3 ' ,
' title ' : ' Youtube - Dl Test Video \' \' Ä↭ ' ,
' description ' : ' test chars: \" \' / \\ ä↭ ' ,
2019-02-10 17:44:08 +01:00
' uploader ' : ' jaimeMF ' ,
2019-10-27 17:52:46 +01:00
' uploader_id ' : ' 69767071 ' ,
2019-02-10 17:44:08 +01:00
' timestamp ' : 1386604920 ,
2014-01-17 03:29:41 +01:00
' upload_date ' : ' 20131209 ' ,
2019-04-22 19:39:16 +02:00
' duration ' : 9.927 ,
2016-09-18 11:53:05 +02:00
' license ' : ' all-rights-reserved ' ,
2019-02-10 17:44:08 +01:00
' view_count ' : int ,
' like_count ' : int ,
' comment_count ' : int ,
' repost_count ' : int ,
2013-12-09 17:08:58 +01:00
} ,
} ,
2014-09-18 08:02:03 +02:00
# private link (alt format)
{
' url ' : ' https://api.soundcloud.com/tracks/123998367?secret_token=s-8Pjrp ' ,
' md5 ' : ' aa0dd32bfea9b0c5ef4f02aacd080604 ' ,
' info_dict ' : {
' id ' : ' 123998367 ' ,
' ext ' : ' mp3 ' ,
' title ' : ' Youtube - Dl Test Video \' \' Ä↭ ' ,
' description ' : ' test chars: \" \' / \\ ä↭ ' ,
2019-02-10 17:44:08 +01:00
' uploader ' : ' jaimeMF ' ,
2019-10-27 17:52:46 +01:00
' uploader_id ' : ' 69767071 ' ,
2019-02-10 17:44:08 +01:00
' timestamp ' : 1386604920 ,
2014-09-18 08:02:03 +02:00
' upload_date ' : ' 20131209 ' ,
2019-04-22 19:39:16 +02:00
' duration ' : 9.927 ,
2016-09-18 11:53:05 +02:00
' license ' : ' all-rights-reserved ' ,
2019-02-10 17:44:08 +01:00
' view_count ' : int ,
' like_count ' : int ,
' comment_count ' : int ,
' repost_count ' : int ,
2014-09-18 08:02:03 +02:00
} ,
} ,
2013-12-10 13:04:21 +01:00
# downloadable song
{
2014-07-15 14:18:06 +02:00
' url ' : ' https://soundcloud.com/oddsamples/bus-brakes ' ,
2014-07-23 01:41:44 +02:00
' md5 ' : ' 7624f2351f8a3b2e7cd51522496e7631 ' ,
2014-01-17 03:29:41 +01:00
' info_dict ' : {
2014-07-15 14:18:06 +02:00
' id ' : ' 128590877 ' ,
2014-07-23 01:41:44 +02:00
' ext ' : ' mp3 ' ,
2014-07-15 14:18:06 +02:00
' title ' : ' Bus Brakes ' ,
2014-10-25 22:32:01 +02:00
' description ' : ' md5:0053ca6396e8d2fd7b7e1595ef12ab66 ' ,
2014-07-15 14:18:06 +02:00
' uploader ' : ' oddsamples ' ,
2019-10-27 17:52:46 +01:00
' uploader_id ' : ' 73680509 ' ,
2019-02-10 17:44:08 +01:00
' timestamp ' : 1389232924 ,
2014-07-15 14:18:06 +02:00
' upload_date ' : ' 20140109 ' ,
2019-04-22 19:39:16 +02:00
' duration ' : 17.346 ,
2016-09-18 11:53:05 +02:00
' license ' : ' cc-by-sa ' ,
2019-02-10 17:44:08 +01:00
' view_count ' : int ,
' like_count ' : int ,
' comment_count ' : int ,
' repost_count ' : int ,
2013-12-10 13:04:21 +01:00
} ,
} ,
2017-09-03 11:18:24 +02:00
# private link, downloadable format
{
' url ' : ' https://soundcloud.com/oriuplift/uponly-238-no-talking-wav/s-AyZUd ' ,
' md5 ' : ' 64a60b16e617d41d0bef032b7f55441e ' ,
' info_dict ' : {
' id ' : ' 340344461 ' ,
' ext ' : ' wav ' ,
' title ' : ' Uplifting Only 238 [No Talking] (incl. Alex Feed Guestmix) (Aug 31, 2017) [wav] ' ,
' description ' : ' md5:fa20ee0fca76a3d6df8c7e57f3715366 ' ,
' uploader ' : ' Ori Uplift Music ' ,
2019-10-27 17:52:46 +01:00
' uploader_id ' : ' 12563093 ' ,
2019-02-10 17:44:08 +01:00
' timestamp ' : 1504206263 ,
2017-09-03 11:18:24 +02:00
' upload_date ' : ' 20170831 ' ,
2019-04-22 19:39:16 +02:00
' duration ' : 7449.096 ,
2017-09-03 11:18:24 +02:00
' license ' : ' all-rights-reserved ' ,
2019-02-10 17:44:08 +01:00
' view_count ' : int ,
' like_count ' : int ,
' comment_count ' : int ,
' repost_count ' : int ,
2017-09-03 11:18:24 +02:00
} ,
} ,
2018-01-05 02:25:42 +01:00
# no album art, use avatar pic for thumbnail
{
' url ' : ' https://soundcloud.com/garyvee/sideways-prod-mad-real ' ,
' md5 ' : ' 59c7872bc44e5d99b7211891664760c2 ' ,
' info_dict ' : {
' id ' : ' 309699954 ' ,
' ext ' : ' mp3 ' ,
' title ' : ' Sideways (Prod. Mad Real) ' ,
' description ' : ' md5:d41d8cd98f00b204e9800998ecf8427e ' ,
' uploader ' : ' garyvee ' ,
2019-10-27 17:52:46 +01:00
' uploader_id ' : ' 2366352 ' ,
2019-02-10 17:44:08 +01:00
' timestamp ' : 1488152409 ,
2018-01-05 02:25:42 +01:00
' upload_date ' : ' 20170226 ' ,
2019-04-22 19:39:16 +02:00
' duration ' : 207.012 ,
2018-01-05 02:25:42 +01:00
' thumbnail ' : r ' re:https?://.* \ .jpg ' ,
' license ' : ' all-rights-reserved ' ,
2019-02-10 17:44:08 +01:00
' view_count ' : int ,
' like_count ' : int ,
' comment_count ' : int ,
' repost_count ' : int ,
2018-01-05 02:25:42 +01:00
} ,
' params ' : {
' skip_download ' : True ,
} ,
} ,
2019-04-22 19:39:16 +02:00
{
' url ' : ' https://soundcloud.com/giovannisarani/mezzo-valzer ' ,
' md5 ' : ' e22aecd2bc88e0e4e432d7dcc0a1abf7 ' ,
' info_dict ' : {
' id ' : ' 583011102 ' ,
' ext ' : ' mp3 ' ,
' title ' : ' Mezzo Valzer ' ,
' description ' : ' md5:4138d582f81866a530317bae316e8b61 ' ,
2020-01-23 23:20:48 +01:00
' uploader ' : ' Micronie ' ,
2019-10-27 17:52:46 +01:00
' uploader_id ' : ' 3352531 ' ,
2019-04-22 19:39:16 +02:00
' timestamp ' : 1551394171 ,
' upload_date ' : ' 20190228 ' ,
' duration ' : 180.157 ,
' thumbnail ' : r ' re:https?://.* \ .jpg ' ,
' license ' : ' all-rights-reserved ' ,
' view_count ' : int ,
' like_count ' : int ,
' comment_count ' : int ,
' repost_count ' : int ,
} ,
2020-04-10 12:24:21 +02:00
} ,
{
# with AAC HQ format available via OAuth token
' url ' : ' https://soundcloud.com/wandw/the-chainsmokers-ft-daya-dont-let-me-down-ww-remix-1 ' ,
' only_matching ' : True ,
} ,
2013-11-09 18:06:09 +01:00
]
2013-06-23 20:57:44 +02:00
2019-10-27 17:52:46 +01:00
_API_V2_BASE = ' https://api-v2.soundcloud.com/ '
_BASE_URL = ' https://soundcloud.com/ '
_IMAGE_REPL_RE = r ' -([0-9a-z]+) \ .jpg '
_ARTWORK_MAP = {
' mini ' : 16 ,
' tiny ' : 20 ,
' small ' : 32 ,
' badge ' : 47 ,
' t67x67 ' : 67 ,
' large ' : 100 ,
' t300x300 ' : 300 ,
' crop ' : 400 ,
' t500x500 ' : 500 ,
' original ' : 0 ,
}
2016-07-28 17:16:05 +02:00
2019-12-31 21:31:22 +01:00
def _store_client_id ( self , client_id ) :
self . _downloader . cache . store ( ' soundcloud ' , ' client_id ' , client_id )
2019-12-31 09:49:29 +01:00
def _update_client_id ( self ) :
webpage = self . _download_webpage ( ' https://soundcloud.com/ ' , None )
for src in reversed ( re . findall ( r ' <script[^>]+src= " ([^ " ]+) " ' , webpage ) ) :
script = self . _download_webpage ( src , None , fatal = False )
if script :
client_id = self . _search_regex (
r ' client_id \ s*: \ s* " ([0-9a-zA-Z] {32} ) " ' ,
script , ' client id ' , default = None )
if client_id :
self . _CLIENT_ID = client_id
2019-12-31 21:31:22 +01:00
self . _store_client_id ( client_id )
2019-12-31 09:49:29 +01:00
return
raise ExtractorError ( ' Unable to extract client id ' )
def _download_json ( self , * args , * * kwargs ) :
2019-12-31 21:31:22 +01:00
non_fatal = kwargs . get ( ' fatal ' ) is False
if non_fatal :
del kwargs [ ' fatal ' ]
2019-12-31 09:49:29 +01:00
query = kwargs . get ( ' query ' , { } ) . copy ( )
for _ in range ( 2 ) :
query [ ' client_id ' ] = self . _CLIENT_ID
kwargs [ ' query ' ] = query
try :
return super ( SoundcloudIE , self ) . _download_json ( * args , * * compat_kwargs ( kwargs ) )
except ExtractorError as e :
if isinstance ( e . cause , compat_HTTPError ) and e . cause . code == 401 :
2019-12-31 21:31:22 +01:00
self . _store_client_id ( None )
2019-12-31 09:49:29 +01:00
self . _update_client_id ( )
continue
2019-12-31 21:31:22 +01:00
elif non_fatal :
self . _downloader . report_warning ( error_to_compat_str ( e ) )
return False
2019-12-31 09:49:29 +01:00
raise
2020-08-05 19:10:25 +02:00
_CLIENT_ID = ' EXLwg5lHTO2dslU5EePe3xkw0m1h86Cd '
_USER_AGENT = ' Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 Edg/84.0.522.50 ' #MS EDGE CHROME-BASED
_USERNAME = ' '
_PASSWORD = ' '
_KEY = ' 0763ed7314c69015fd4a0dc16bbf4b90 '
2019-12-31 09:49:29 +01:00
def _real_initialize ( self ) :
2020-08-05 19:10:25 +02:00
self . _CLIENT_ID , _CLIENT_ID = self . _downloader . cache . load ( ' soundcloud ' , ' client_id ' ) or ' EXLwg5lHTO2dslU5EePe3xkw0m1h86Cd ' #'YUKXoArFcqrlQn9tfNHvvyfnDISj04zk'
self . _USERNAME , self . _PASSWORD = self . _get_login_info ( )
_USERNAME = self . _USERNAME
_PASSWORD = self . _PASSWORD
self . _login ( )
def _login ( self ) :
username , password = self . _get_login_info ( )
if username is None :
return
'''
EXAMPLE
{ " client_id " : " EXLwg5lHTO2dslU5EePe3xkw0m1h86Cd " , " recaptcha_pubkey " : " 6Ld72JcUAAAAAItDloUGqg6H38KK5j08VuQlegV1 " , " recaptcha_response " : null , " credentials " : { " identifier " : user , " password " : password } , " signature " : " 8:33-1-53791-767-2073600-1028-25-25:3b9024:4 " , " device_id " : " 71542-609622-728970-275967 " , " user_agent " : " Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 Edg/84.0.522.50 " }
call_payload = {
client_id : u . clientId ,
recaptcha_pubkey : p ,
recaptcha_response : 0 < n . length ? n : null ,
credentials : {
identifier : e ,
password : t
} ,
signature : l . sign ( e , u . clientId , c ) ,
device_id : u . deviceId ,
user_agent : h
}
'''
'''
var t , n , o = [ " touchmove " , " webdriver " , " __webdriver_script_fn " , " Chromium PDF Viewer " , " $cdc_asdjflasutopfhvcZLmcfl_ " , " keydown " , " typeof process " , " encodeURIComponent " , " getAttribute " , " document " , " unescape " , " 256 " , " call " , " Chrome PDF Viewer " , " _Selenium_IDE_Recorder " , " width " , " TestUA " , " location " , " name " , " forEach " , " length " , " Chrome PDF Plugin " , " 512 " , " mozInnerScreenY " , " Date " , " addEventListener " , " Native Client " , " 424242 " , " exports " , " height " , " Widevine Content Decryption Module " , " mousemove " , " keyup " , " 1024 " , " 420 " , " https: " , " 128 " , " plugins " , " charCodeAt " , " 4096 " , " typeof global " , " undefined " , " now " , " msLaunchUri " , " screen " , " eval " , " safari " , " navigator " , " WebKit built-in PDF " , " isTrusted " ] ;
t = o ,
n = 219 ,
function ( e ) {
for ( ; - - e ; )
t . push ( t . shift ( ) )
} ( + + n ) ;
var g = function ( e , t ) {
return o [ e - = 0 ]
} ;
2020-08-06 00:47:37 +02:00
e [ g ( " 0x9 " ) ] = { #e = function 162
2020-08-05 19:10:25 +02:00
initialize : function ( ) {
v = window [ g ( " 0x5 " ) ] . now ( ) ;
var e = function ( e ) {
try {
if ( ! 1 == = e [ g ( " 0x1e " ) ] )
return
} catch ( e ) { }
w + = 1
} ;
window [ g ( " 0x6 " ) ] ( g ( " 0xc " ) , e ) ,
window [ g ( " 0x6 " ) ] ( g ( " 0x1f " ) , e ) ,
window [ g ( " 0x6 " ) ] ( g ( " 0x24 " ) , function ( ) {
b + = 1
} ) ,
window . addEventListener ( g ( " 0xd " ) , function ( ) {
k + = 1
} ) ,
window [ g ( " 0x6 " ) ] ( " click " , function ( ) {
_ + = 1
} )
}
'''
2020-08-06 00:47:37 +02:00
#after shifting the list
#["forEach", "length", "Chrome PDF Plugin", "512", "mozInnerScreenY", "Date", "addEventListener", "Native Client", "424242", "exports", "height", "Widevine Content Decryption Module", "mousemove", "keyup", "1024", "420", "https:", "128", "plugins", "charCodeAt", "4096", "typeof global", "undefined", "now", "msLaunchUri", "screen", "eval", "safari", "navigator", "WebKit built-in PDF", "isTrusted", "touchmove", "webdriver", "__webdriver_script_fn", "Chromium PDF Viewer", "$cdc_asdjflasutopfhvcZLmcfl_", "keydown", "typeof process", "encodeURIComponent", "getAttribute", "document", "unescape", "256", "call", "Chrome PDF Viewer", "_Selenium_IDE_Recorder", "width", "TestUA", "location", "name"]
2020-08-05 19:10:25 +02:00
'''
sign : function ( e , t , n , o ) { e = " tom_heidel@web.de " , t = " EXLwg5lHTO2dslU5EePe3xkw0m1h86Cd " , n = " 0763ed7314c69015fd4a0dc16bbf4b90 " , o = undefined
r = " Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 "
}
## ignore
#var g = function(e, t) {
# return o[e -= 0]
#};
#Return value: "$cdc_asdjflasutopfhvcZLmcfl_"
var s = window [ g ( " 0x5 " ) ] [ g ( " 0x17 " ) ] ( ) - v #v was created earlier in initialize
g ( " 0x5 " ) = Date
g ( " 0x17 " ) = now
v = Timestamp in milliseconds e . g . 1596580552126
Return value s : 725321
u = 0 ;
try {
u = window . screen [ g ( " 0x2e " ) ] * window [ g ( " 0x19 " ) ] [ g ( " 0xa " ) ]
g ( " 0x2e " ) = width
g ( " 0x19 " ) = screen
g ( " 0xa " ) = height
Return value u : 2073600
var l = + g ( " 0xe " ) ;
l = 1024
try {
[ ] [ g ( " 0x0 " ) ] [ g ( " 0x2b " ) ] ( window [ g ( " 0x1c " ) ] [ g ( " 0x12 " ) ] , function ( e ) {
var t = e [ g ( " 0x31 " ) ] ;
t == = g ( " 0x22 " ) & & ( l + = 1 ) ,
t == = g ( " 0x2c " ) & & ( l + = 2 ) ,
t == = g ( " 0x7 " ) & & ( l + = 4 ) ,
t == = g ( " 0xb " ) & & ( l + = 8 ) ,
t == = g ( " 0x2 " ) & & ( l + = 16 ) ,
" Java Appvar Plug-in " == = t & & ( l + = 32 ) ,
" Shockwave Flash " == = t & & ( l + = 64 ) ,
" Edge PDF Viewer " == = t & & ( l + = + g ( " 0x11 " ) ) ,
t == = g ( " 0x1d " ) & & ( l + = + g ( " 0x2a " ) )
} )
}
g ( " 0x0 " ) = forEach
g ( " 0x2b " ) = call
g ( " 0x1c " ) = navigator
g ( " 0x12 " ) = plugins
} catch ( e ) { }
o & & ( r = g ( " 0x2f " ) ) ;
o & & ( i = + g ( " 0x14 " ) ) ;
o & & ( a = + g ( " 0x14 " ) ) ;
o & & ( w = 42 ) ;
o & & ( s = + g ( " 0x8 " ) ) ;
o & & ( u = 420 * + g ( " 0xf " ) ) ;
o & & ( l = + g ( " 0xe " ) ) ;
o & & ( b = 7 ) ;
o & & ( k = 9 ) ;
o & & ( _ = 0 ) ;
e = " tom_heidel@web.de "
o = undefined , r = " Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 "
a = 33
i = 1
a = 33
s = 725321
u = 2073600
l = 1046
b = 2
k = 2
_ = 5
w = 1049
y = " 8 "
n = " 0763ed7314c69015fd4a0dc16bbf4b90 " #same as c in l.sign
for ( var d = [ a , i , s , w , u , l , b , k ] . join ( " - " ) , c = _ , p = n + y + d + r + e + t + d + n , h = window [ g ( " 0x29 " ) ] ( window [ g ( " 0x26 " ) ] ( p ) ) , m = 8011470 , f = 0 ; f < h [ g ( " 0x1 " ) ] ; f + = 1 )
m = ( m >> 1 ) + ( ( 1 & m ) << 23 ) ,
m + = h [ g ( " 0x13 " ) ] ( f ) ,
m & = 16777215 ;
return y + " : " + d + " : " + m . toString ( 16 ) + " : " + c
}
d = " 33-1-725321-1049-2073600-1046-2-2 "
c = 5
p = " 0763ed7314c69015fd4a0dc16bbf4b90833-1-725321-1049-2073600-1046-2-2Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36tom_heidel@web.deEXLwg5lHTO2dslU5EePe3xkw0m1h86Cd33-1-725321-1049-2073600-1046-2-20763ed7314c69015fd4a0dc16bbf4b90 "
g ( " 0x29 " ) = unescape
g ( " 0x26 " ) = encodeURIComponent
h = " 0763ed7314c69015fd4a0dc16bbf4b90833-1-725321-1049-2073600-1046-2-2Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36tom_heidel@web.deEXLwg5lHTO2dslU5EePe3xkw0m1h86Cd33-1-725321-1049-2073600-1046-2-20763ed7314c69015fd4a0dc16bbf4b90 "
m = 8011470
f = 0
g ( " 0x1 " ) = length
m = 4005735 #calculated m = (m >> 1) + ((1 & m) << 23)
g ( " 0x13 " ) = charCodeAt
m = 4005783 #calculated m += h[g("0x13")](f)
. - . - . - . after all h . length iterations
m = 245670
Return value : " 8:33-1-725321-1049-2073600-1046-2-2:3bfa6:5 " #signature
# user-agent string is processed in signature calculating
# this probably means if a signature with user-agent A is given you cannot change it to B when logging in
'''
login_form = {
' client_id ' : self . _CLIENT_ID ,
' recaptcha_pubkey ' : ' null ' ,
' recaptcha_response ' : ' null ' ,
' credentials ' : {
' identifier ' : username ,
' password ' : password
} ,
' signature ' : ' ' , #l.sign(username, self._CLIENT_ID, c='0763ed7314c69015fd4a0dc16bbf4b90'), #c is extracted from web_auth js around (reading upwards from) function api.callEndpoint("verifyIdentifier"
' device_id ' : ' 00000-000000-000000-000000 ' ,
' user_agent ' : self . _USER_AGENT
}
'''
login = self . _call_api (
' sessions.json ' , None ,
' Logging in ' , post_data = login_form )
'''
# signature generation
def sign ( self ) : #, username=_USERNAME, client_id=_CLIENT_ID, key=_KEY):
zero = 0
i = a = 1
u = 0 #u is actually screenWidth * screenHeight for 1920*1080 = 2073600
mU = 2073600
l = 1024 #1046?
timestamp = millis = int ( round ( time . time ( ) * 1000 ) )
mTimestamp = timestamp - ( timestamp - random . randint ( 50000 , 850000 ) ) #hacky timestamp difference
uTimestamp = random . randint ( 50000 , 850000 )
w = 42 #1049?
b = k = 2 #25?
underscore = 0 #4, 5?
#d = '-'.join([str(mInt) for mInt in [a, i, s, w, u, l, b, k]])
d = ' - ' . join ( [ str ( mInt ) for mInt in [ a , i , zero , w , u , l , b , k ] ] )
print ( d )
c = underscore
n = _KEY = ' 0763ed7314c69015fd4a0dc16bbf4b90 '
y = ' 8 ' #some kind of version??
r = _USER_AGENT = " Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 "
e = _USERNAME = " tom_heidel@web.de "
t = _CLIENT_ID = ' EXLwg5lHTO2dslU5EePe3xkw0m1h86Cd '
p = n + y + d + r + e + t + d + n
h = p
m = 8011470
f = 0
for f in range ( f , len ( h ) ) :
m = ( m >> 1 ) + ( ( 1 & m ) << 23 )
m + = ord ( h [ f ] )
m & = 16777215
# c is not even needed
out = str ( y ) + ' : ' + str ( d ) + ' : ' + format ( m , ' x ' ) + ' : ' + str ( c )
return out
###
2019-12-31 09:49:29 +01:00
2013-07-24 14:05:14 +02:00
@classmethod
def _resolv_url ( cls , url ) :
2019-12-31 09:49:29 +01:00
return SoundcloudIE . _API_V2_BASE + ' resolve?url= ' + url
2013-07-24 14:05:14 +02:00
2020-03-22 09:24:07 +01:00
def _extract_info_dict ( self , info , full_title = None , secret_token = None ) :
2013-11-09 18:06:09 +01:00
track_id = compat_str ( info [ ' id ' ] )
2019-02-10 17:44:08 +01:00
title = info [ ' title ' ]
2019-04-22 19:39:16 +02:00
format_urls = set ( )
2014-03-24 02:21:17 +01:00
formats = [ ]
2017-09-01 10:08:24 +02:00
query = { ' client_id ' : self . _CLIENT_ID }
2019-10-27 17:52:46 +01:00
if secret_token :
2017-09-01 10:08:24 +02:00
query [ ' secret_token ' ] = secret_token
2019-10-27 17:52:46 +01:00
2019-11-11 09:53:04 +01:00
if info . get ( ' downloadable ' ) and info . get ( ' has_downloads_left ' ) :
2020-03-22 09:24:07 +01:00
download_url = update_url_query (
self . _API_V2_BASE + ' tracks/ ' + track_id + ' /download ' , query )
redirect_url = ( self . _download_json ( download_url , track_id , fatal = False ) or { } ) . get ( ' redirectUri ' )
if redirect_url :
urlh = self . _request_webpage (
HEADRequest ( redirect_url ) , track_id , fatal = False )
if urlh :
format_url = urlh . geturl ( )
format_urls . add ( format_url )
formats . append ( {
' format_id ' : ' download ' ,
' ext ' : urlhandle_detect_ext ( urlh ) or ' mp3 ' ,
' filesize ' : int_or_none ( urlh . headers . get ( ' Content-Length ' ) ) ,
' url ' : format_url ,
' preference ' : 10 ,
} )
2014-03-24 02:21:17 +01:00
2019-10-27 17:52:46 +01:00
def invalid_url ( url ) :
2020-01-14 22:13:10 +01:00
return not url or url in format_urls
2019-04-22 19:39:16 +02:00
2020-01-14 22:13:10 +01:00
def add_format ( f , protocol , is_preview = False ) :
2019-10-27 17:52:46 +01:00
mobj = re . search ( r ' \ .(?P<abr> \ d+) \ .(?P<ext>[0-9a-z] { 3,4})(?=[/?]) ' , stream_url )
if mobj :
for k , v in mobj . groupdict ( ) . items ( ) :
if not f . get ( k ) :
f [ k ] = v
format_id_list = [ ]
if protocol :
format_id_list . append ( protocol )
2020-04-10 12:24:21 +02:00
ext = f . get ( ' ext ' )
if ext == ' aac ' :
2020-04-09 13:50:45 +02:00
f [ ' abr ' ] = ' 256 '
2019-10-27 17:52:46 +01:00
for k in ( ' ext ' , ' abr ' ) :
v = f . get ( k )
if v :
format_id_list . append ( v )
2020-01-14 22:13:10 +01:00
preview = is_preview or re . search ( r ' /(?:preview|playlist)/0/30/ ' , f [ ' url ' ] )
if preview :
format_id_list . append ( ' preview ' )
2019-10-27 17:52:46 +01:00
abr = f . get ( ' abr ' )
if abr :
f [ ' abr ' ] = int ( abr )
2020-04-10 12:24:21 +02:00
if protocol == ' hls ' :
protocol = ' m3u8 ' if ext == ' aac ' else ' m3u8_native '
else :
protocol = ' http '
2019-10-27 17:52:46 +01:00
f . update ( {
' format_id ' : ' _ ' . join ( format_id_list ) ,
2020-04-10 12:24:21 +02:00
' protocol ' : protocol ,
2020-01-14 22:13:10 +01:00
' preference ' : - 10 if preview else None ,
2019-10-27 17:52:46 +01:00
} )
formats . append ( f )
2019-04-22 19:39:16 +02:00
# New API
transcodings = try_get (
info , lambda x : x [ ' media ' ] [ ' transcodings ' ] , list ) or [ ]
for t in transcodings :
if not isinstance ( t , dict ) :
2017-01-27 17:16:30 +01:00
continue
2019-04-22 19:39:16 +02:00
format_url = url_or_none ( t . get ( ' url ' ) )
2020-01-14 22:13:10 +01:00
if not format_url :
2019-04-22 19:39:16 +02:00
continue
stream = self . _download_json (
2019-10-27 17:52:46 +01:00
format_url , track_id , query = query , fatal = False )
2019-04-22 19:39:16 +02:00
if not isinstance ( stream , dict ) :
continue
stream_url = url_or_none ( stream . get ( ' url ' ) )
2019-10-27 17:52:46 +01:00
if invalid_url ( stream_url ) :
2019-04-22 19:39:16 +02:00
continue
format_urls . add ( stream_url )
2019-10-27 17:52:46 +01:00
stream_format = t . get ( ' format ' ) or { }
protocol = stream_format . get ( ' protocol ' )
2019-04-22 19:39:16 +02:00
if protocol != ' hls ' and ' /hls ' in format_url :
protocol = ' hls '
ext = None
preset = str_or_none ( t . get ( ' preset ' ) )
if preset :
ext = preset . split ( ' _ ' ) [ 0 ]
2019-10-27 17:52:46 +01:00
if ext not in KNOWN_EXTENSIONS :
ext = mimetype2ext ( stream_format . get ( ' mime_type ' ) )
add_format ( {
2019-04-22 19:39:16 +02:00
' url ' : stream_url ,
' ext ' : ext ,
2020-01-14 22:13:10 +01:00
} , ' http ' if protocol == ' progressive ' else protocol ,
t . get ( ' snipped ' ) or ' /preview/ ' in format_url )
2019-10-27 17:52:46 +01:00
2017-01-27 17:16:30 +01:00
for f in formats :
f [ ' vcodec ' ] = ' none '
2013-11-25 20:30:41 +01:00
2020-03-22 09:24:07 +01:00
if not formats and info . get ( ' policy ' ) == ' BLOCK ' :
self . raise_geo_restricted ( )
2015-03-02 17:39:32 +01:00
self . _sort_formats ( formats )
2013-11-21 13:16:19 +01:00
2019-10-27 17:52:46 +01:00
user = info . get ( ' user ' ) or { }
thumbnails = [ ]
artwork_url = info . get ( ' artwork_url ' )
thumbnail = artwork_url or user . get ( ' avatar_url ' )
if isinstance ( thumbnail , compat_str ) :
if re . search ( self . _IMAGE_REPL_RE , thumbnail ) :
for image_id , size in self . _ARTWORK_MAP . items ( ) :
i = {
' id ' : image_id ,
' url ' : re . sub ( self . _IMAGE_REPL_RE , ' - %s .jpg ' % image_id , thumbnail ) ,
}
if image_id == ' tiny ' and not artwork_url :
size = 18
elif image_id == ' original ' :
i [ ' preference ' ] = 10
if size :
i . update ( {
' width ' : size ,
' height ' : size ,
} )
thumbnails . append ( i )
else :
thumbnails = [ { ' url ' : thumbnail } ]
def extract_count ( key ) :
return int_or_none ( info . get ( ' %s _count ' % key ) )
return {
' id ' : track_id ,
' uploader ' : user . get ( ' username ' ) ,
' uploader_id ' : str_or_none ( user . get ( ' id ' ) ) or user . get ( ' permalink ' ) ,
' uploader_url ' : user . get ( ' permalink_url ' ) ,
' timestamp ' : unified_timestamp ( info . get ( ' created_at ' ) ) ,
' title ' : title ,
' description ' : info . get ( ' description ' ) ,
' thumbnails ' : thumbnails ,
' duration ' : float_or_none ( info . get ( ' duration ' ) , 1000 ) ,
' webpage_url ' : info . get ( ' permalink_url ' ) ,
' license ' : info . get ( ' license ' ) ,
' view_count ' : extract_count ( ' playback ' ) ,
' like_count ' : extract_count ( ' favoritings ' ) or extract_count ( ' likes ' ) ,
' comment_count ' : extract_count ( ' comment ' ) ,
' repost_count ' : extract_count ( ' reposts ' ) ,
' genre ' : info . get ( ' genre ' ) ,
' formats ' : formats
}
2013-07-24 14:05:14 +02:00
2013-06-23 20:57:44 +02:00
def _real_extract ( self , url ) :
2019-10-27 17:52:46 +01:00
mobj = re . match ( self . _VALID_URL , url )
2013-06-23 20:57:44 +02:00
2013-07-24 14:39:21 +02:00
track_id = mobj . group ( ' track_id ' )
2016-09-18 11:53:05 +02:00
2019-12-31 09:49:29 +01:00
query = { }
2019-10-27 17:52:46 +01:00
if track_id :
info_json_url = self . _API_V2_BASE + ' tracks/ ' + track_id
2013-07-24 14:39:21 +02:00
full_title = track_id
2014-09-18 08:02:03 +02:00
token = mobj . group ( ' secret_token ' )
if token :
2019-10-27 17:52:46 +01:00
query [ ' secret_token ' ] = token
2013-07-24 14:39:21 +02:00
else :
2019-10-27 17:52:46 +01:00
full_title = resolve_title = ' %s / %s ' % mobj . group ( ' uploader ' , ' title ' )
2013-12-09 17:08:58 +01:00
token = mobj . group ( ' token ' )
if token :
resolve_title + = ' / %s ' % token
2019-10-27 17:52:46 +01:00
info_json_url = self . _resolv_url ( self . _BASE_URL + resolve_title )
2014-11-23 20:41:03 +01:00
2019-04-22 19:39:16 +02:00
info = self . _download_json (
2020-03-22 09:24:07 +01:00
info_json_url , full_title , ' Downloading info JSON ' , query = query )
return self . _extract_info_dict ( info , full_title , token )
2013-06-23 20:57:44 +02:00
2014-05-05 03:12:41 +02:00
2016-09-24 14:29:49 +02:00
class SoundcloudPlaylistBaseIE ( SoundcloudIE ) :
2020-01-23 23:20:48 +01:00
def _extract_set ( self , playlist , token = None ) :
playlist_id = compat_str ( playlist [ ' id ' ] )
tracks = playlist . get ( ' tracks ' ) or [ ]
if not all ( [ t . get ( ' permalink_url ' ) for t in tracks ] ) and token :
tracks = self . _download_json (
self . _API_V2_BASE + ' tracks ' , playlist_id ,
' Downloading tracks ' , query = {
' ids ' : ' , ' . join ( [ compat_str ( t [ ' id ' ] ) for t in tracks ] ) ,
' playlistId ' : playlist_id ,
' playlistSecretToken ' : token ,
} )
2019-10-27 17:52:46 +01:00
entries = [ ]
for track in tracks :
track_id = str_or_none ( track . get ( ' id ' ) )
url = track . get ( ' permalink_url ' )
if not url :
if not track_id :
continue
url = self . _API_V2_BASE + ' tracks/ ' + track_id
if token :
url + = ' ?secret_token= ' + token
entries . append ( self . url_result (
url , SoundcloudIE . ie_key ( ) , track_id ) )
2020-01-23 23:20:48 +01:00
return self . playlist_result (
entries , playlist_id ,
playlist . get ( ' title ' ) ,
playlist . get ( ' description ' ) )
2016-09-24 14:18:01 +02:00
2016-09-24 14:29:49 +02:00
class SoundcloudSetIE ( SoundcloudPlaylistBaseIE ) :
2015-03-30 17:03:38 +02:00
_VALID_URL = r ' https?://(?:(?:www|m) \ .)?soundcloud \ .com/(?P<uploader>[ \ w \ d-]+)/sets/(?P<slug_title>[ \ w \ d-]+)(?:/(?P<token>[^?/]+))? '
2014-01-17 03:29:41 +01:00
IE_NAME = ' soundcloud:set '
2014-08-28 00:58:24 +02:00
_TESTS = [ {
' url ' : ' https://soundcloud.com/the-concept-band/sets/the-royal-concept-ep ' ,
' info_dict ' : {
2015-02-01 15:24:38 +01:00
' id ' : ' 2284613 ' ,
2014-08-28 00:58:24 +02:00
' title ' : ' The Royal Concept EP ' ,
2020-01-23 23:20:48 +01:00
' description ' : ' md5:71d07087c7a449e8941a70a29e34671e ' ,
2014-08-28 00:58:24 +02:00
} ,
2017-06-27 16:26:46 +02:00
' playlist_mincount ' : 5 ,
2016-08-30 20:56:15 +02:00
} , {
' url ' : ' https://soundcloud.com/the-concept-band/sets/the-royal-concept-ep/token ' ,
' only_matching ' : True ,
2014-08-28 00:58:24 +02:00
} ]
2013-06-23 20:57:44 +02:00
def _real_extract ( self , url ) :
mobj = re . match ( self . _VALID_URL , url )
2019-10-27 17:52:46 +01:00
full_title = ' %s /sets/ %s ' % mobj . group ( ' uploader ' , ' slug_title ' )
2014-09-18 11:35:11 +02:00
token = mobj . group ( ' token ' )
if token :
full_title + = ' / ' + token
2013-06-23 20:57:44 +02:00
2019-10-27 17:52:46 +01:00
info = self . _download_json ( self . _resolv_url (
self . _BASE_URL + full_title ) , full_title )
2013-06-23 20:57:44 +02:00
if ' errors ' in info :
2015-04-17 19:24:30 +02:00
msgs = ( compat_str ( err [ ' error_message ' ] ) for err in info [ ' errors ' ] )
raise ExtractorError ( ' unable to download video webpage: %s ' % ' , ' . join ( msgs ) )
2013-06-23 20:57:44 +02:00
2020-01-23 23:20:48 +01:00
return self . _extract_set ( info , token )
2013-09-14 21:41:49 +02:00
2020-01-23 23:20:48 +01:00
class SoundcloudPagedPlaylistBaseIE ( SoundcloudIE ) :
2017-07-29 13:41:42 +02:00
def _extract_playlist ( self , base_url , playlist_id , playlist_title ) :
COMMON_QUERY = {
2020-05-15 15:12:31 +02:00
' limit ' : 80000 ,
2017-07-29 13:41:42 +02:00
' linked_partitioning ' : ' 1 ' ,
}
query = COMMON_QUERY . copy ( )
query [ ' offset ' ] = 0
2019-10-27 17:52:46 +01:00
next_href = base_url
2017-07-29 13:41:42 +02:00
entries = [ ]
for i in itertools . count ( ) :
response = self . _download_json (
2019-10-27 17:52:46 +01:00
next_href , playlist_id ,
' Downloading track page %s ' % ( i + 1 ) , query = query )
2017-07-29 13:41:42 +02:00
collection = response [ ' collection ' ]
2019-02-01 22:00:29 +01:00
if not isinstance ( collection , list ) :
collection = [ ]
# Empty collection may be returned, in this case we proceed
# straight to next_href
2017-07-29 13:41:42 +02:00
2019-02-02 17:40:06 +01:00
def resolve_entry ( candidates ) :
for cand in candidates :
if not isinstance ( cand , dict ) :
continue
permalink_url = url_or_none ( cand . get ( ' permalink_url ' ) )
if not permalink_url :
continue
return self . url_result (
permalink_url ,
2019-10-27 17:52:46 +01:00
SoundcloudIE . ie_key ( ) if SoundcloudIE . suitable ( permalink_url ) else None ,
str_or_none ( cand . get ( ' id ' ) ) , cand . get ( ' title ' ) )
2017-07-29 13:41:42 +02:00
for e in collection :
2019-02-02 17:40:06 +01:00
entry = resolve_entry ( ( e , e . get ( ' track ' ) , e . get ( ' playlist ' ) ) )
if entry :
entries . append ( entry )
2017-07-29 13:41:42 +02:00
next_href = response . get ( ' next_href ' )
if not next_href :
break
2019-10-27 17:52:46 +01:00
next_href = response [ ' next_href ' ]
parsed_next_href = compat_urlparse . urlparse ( next_href )
query = compat_urlparse . parse_qs ( parsed_next_href . query )
query . update ( COMMON_QUERY )
2017-07-29 13:41:42 +02:00
return {
' _type ' : ' playlist ' ,
' id ' : playlist_id ,
' title ' : playlist_title ,
' entries ' : entries ,
}
class SoundcloudUserIE ( SoundcloudPagedPlaylistBaseIE ) :
2015-07-31 23:50:03 +02:00
_VALID_URL = r ''' (?x)
https ? : / /
( ? : ( ? : www | m ) \. ) ? soundcloud \. com /
( ? P < user > [ ^ / ] + )
( ? : /
2019-02-01 22:00:29 +01:00
( ? P < rsrc > tracks | albums | sets | reposts | likes | spotlight )
2015-07-31 23:50:03 +02:00
) ?
/ ? ( ? : [ ? #].*)?$
'''
2014-01-17 03:29:41 +01:00
IE_NAME = ' soundcloud:user '
2014-08-28 00:58:24 +02:00
_TESTS = [ {
2019-02-01 22:11:32 +01:00
' url ' : ' https://soundcloud.com/soft-cell-official ' ,
2014-08-28 00:58:24 +02:00
' info_dict ' : {
2019-02-01 22:11:32 +01:00
' id ' : ' 207965082 ' ,
' title ' : ' Soft Cell (All) ' ,
2014-08-28 00:58:24 +02:00
} ,
2019-02-01 22:11:32 +01:00
' playlist_mincount ' : 28 ,
2014-08-28 00:58:24 +02:00
} , {
2019-02-01 22:11:32 +01:00
' url ' : ' https://soundcloud.com/soft-cell-official/tracks ' ,
2014-08-28 00:58:24 +02:00
' info_dict ' : {
2019-02-01 22:11:32 +01:00
' id ' : ' 207965082 ' ,
' title ' : ' Soft Cell (Tracks) ' ,
2014-08-28 00:58:24 +02:00
} ,
2019-02-01 22:11:32 +01:00
' playlist_mincount ' : 27 ,
2015-06-24 16:49:22 +02:00
} , {
2019-02-01 22:11:32 +01:00
' url ' : ' https://soundcloud.com/soft-cell-official/albums ' ,
' info_dict ' : {
' id ' : ' 207965082 ' ,
' title ' : ' Soft Cell (Albums) ' ,
} ,
' playlist_mincount ' : 1 ,
} , {
' url ' : ' https://soundcloud.com/jcv246/sets ' ,
2015-07-30 20:54:26 +02:00
' info_dict ' : {
2019-02-01 22:11:32 +01:00
' id ' : ' 12982173 ' ,
2019-10-27 17:52:46 +01:00
' title ' : ' Jordi / cv (Sets) ' ,
2015-07-30 20:54:26 +02:00
} ,
2016-09-24 14:18:01 +02:00
' playlist_mincount ' : 2 ,
2015-07-30 20:54:26 +02:00
} , {
2019-02-01 22:11:32 +01:00
' url ' : ' https://soundcloud.com/jcv246/reposts ' ,
2015-07-30 20:54:26 +02:00
' info_dict ' : {
2019-02-01 22:11:32 +01:00
' id ' : ' 12982173 ' ,
' title ' : ' Jordi / cv (Reposts) ' ,
2015-07-30 20:54:26 +02:00
} ,
2019-02-01 22:11:32 +01:00
' playlist_mincount ' : 6 ,
2015-07-30 20:54:26 +02:00
} , {
2019-02-01 22:11:32 +01:00
' url ' : ' https://soundcloud.com/clalberg/likes ' ,
2015-07-30 20:54:26 +02:00
' info_dict ' : {
2019-02-01 22:11:32 +01:00
' id ' : ' 11817582 ' ,
' title ' : ' clalberg (Likes) ' ,
2015-07-30 20:54:26 +02:00
} ,
2019-02-01 22:11:32 +01:00
' playlist_mincount ' : 5 ,
2015-07-30 20:54:26 +02:00
} , {
' url ' : ' https://soundcloud.com/grynpyret/spotlight ' ,
' info_dict ' : {
' id ' : ' 7098329 ' ,
2017-06-27 16:26:46 +02:00
' title ' : ' Grynpyret (Spotlight) ' ,
2015-07-30 20:54:26 +02:00
} ,
' playlist_mincount ' : 1 ,
2014-08-28 00:58:24 +02:00
} ]
2013-09-14 21:41:49 +02:00
2015-07-30 20:54:26 +02:00
_BASE_URL_MAP = {
2019-10-27 17:52:46 +01:00
' all ' : ' stream/users/ %s ' ,
' tracks ' : ' users/ %s /tracks ' ,
' albums ' : ' users/ %s /albums ' ,
' sets ' : ' users/ %s /playlists ' ,
' reposts ' : ' stream/users/ %s /reposts ' ,
' likes ' : ' users/ %s /likes ' ,
' spotlight ' : ' users/ %s /spotlight ' ,
2015-07-30 20:54:26 +02:00
}
2013-09-14 21:41:49 +02:00
def _real_extract ( self , url ) :
mobj = re . match ( self . _VALID_URL , url )
uploader = mobj . group ( ' user ' )
2014-05-05 03:12:41 +02:00
user = self . _download_json (
2019-10-27 17:52:46 +01:00
self . _resolv_url ( self . _BASE_URL + uploader ) ,
uploader , ' Downloading user info ' )
2015-07-30 20:54:26 +02:00
resource = mobj . group ( ' rsrc ' ) or ' all '
2017-07-29 13:41:42 +02:00
return self . _extract_playlist (
2019-10-27 17:52:46 +01:00
self . _API_V2_BASE + self . _BASE_URL_MAP [ resource ] % user [ ' id ' ] ,
str_or_none ( user . get ( ' id ' ) ) ,
' %s ( %s ) ' % ( user [ ' username ' ] , resource . capitalize ( ) ) )
2016-01-07 20:54:31 +01:00
2013-09-14 21:41:49 +02:00
2017-07-29 13:41:42 +02:00
class SoundcloudTrackStationIE ( SoundcloudPagedPlaylistBaseIE ) :
_VALID_URL = r ' https?://(?:(?:www|m) \ .)?soundcloud \ .com/stations/track/[^/]+/(?P<id>[^/?#&]+) '
IE_NAME = ' soundcloud:trackstation '
_TESTS = [ {
' url ' : ' https://soundcloud.com/stations/track/officialsundial/your-text ' ,
' info_dict ' : {
' id ' : ' 286017854 ' ,
2019-10-27 17:52:46 +01:00
' title ' : ' Track station: your text ' ,
2017-07-29 13:41:42 +02:00
} ,
' playlist_mincount ' : 47 ,
} ]
2015-07-30 20:54:26 +02:00
2017-07-29 13:41:42 +02:00
def _real_extract ( self , url ) :
track_name = self . _match_id ( url )
2015-07-30 20:54:26 +02:00
2019-10-27 17:52:46 +01:00
track = self . _download_json ( self . _resolv_url ( url ) , track_name )
2017-07-29 13:41:42 +02:00
track_id = self . _search_regex (
2019-10-27 17:52:46 +01:00
r ' soundcloud:track-stations:( \ d+) ' , track [ ' id ' ] , ' track id ' )
2013-09-14 21:41:49 +02:00
2017-07-29 13:41:42 +02:00
return self . _extract_playlist (
2019-10-27 17:52:46 +01:00
self . _API_V2_BASE + ' stations/ %s /tracks ' % track [ ' id ' ] ,
track_id , ' Track station: %s ' % track [ ' title ' ] )
2014-05-05 03:12:41 +02:00
2016-09-24 14:29:49 +02:00
class SoundcloudPlaylistIE ( SoundcloudPlaylistBaseIE ) :
2019-10-27 17:52:46 +01:00
_VALID_URL = r ' https?://api(?:-v2)? \ .soundcloud \ .com/playlists/(?P<id>[0-9]+)(?:/? \ ?secret_token=(?P<token>[^&]+?))?$ '
2014-05-05 03:12:41 +02:00
IE_NAME = ' soundcloud:playlist '
2014-09-18 18:57:04 +02:00
_TESTS = [ {
2017-06-27 16:20:18 +02:00
' url ' : ' https://api.soundcloud.com/playlists/4110309 ' ,
2014-09-18 18:57:04 +02:00
' info_dict ' : {
' id ' : ' 4110309 ' ,
' title ' : ' TILT Brass - Bowery Poetry Club, August \' 03 [Non-Site SCR 02] ' ,
' description ' : ' re:.*?TILT Brass - Bowery Poetry Club ' ,
} ,
' playlist_count ' : 6 ,
} ]
2014-05-05 03:12:41 +02:00
def _real_extract ( self , url ) :
mobj = re . match ( self . _VALID_URL , url )
playlist_id = mobj . group ( ' id ' )
2019-12-31 09:49:29 +01:00
query = { }
2014-09-18 11:35:11 +02:00
token = mobj . group ( ' token ' )
if token :
2019-10-27 17:52:46 +01:00
query [ ' secret_token ' ] = token
2014-09-18 11:35:11 +02:00
2014-05-05 03:12:41 +02:00
data = self . _download_json (
2019-10-27 17:52:46 +01:00
self . _API_V2_BASE + ' playlists/ ' + playlist_id ,
playlist_id , ' Downloading playlist ' , query = query )
2014-05-05 03:12:41 +02:00
2020-01-23 23:20:48 +01:00
return self . _extract_set ( data , token )
2015-10-17 18:23:46 +02:00
class SoundcloudSearchIE ( SearchInfoExtractor , SoundcloudIE ) :
IE_NAME = ' soundcloud:search '
IE_DESC = ' Soundcloud search '
2015-10-30 23:56:07 +01:00
_MAX_RESULTS = float ( ' inf ' )
2015-10-17 18:23:46 +02:00
_TESTS = [ {
' url ' : ' scsearch15:post-avant jazzcore ' ,
' info_dict ' : {
' title ' : ' post-avant jazzcore ' ,
} ,
' playlist_count ' : 15 ,
} ]
_SEARCH_KEY = ' scsearch '
2015-10-30 23:56:07 +01:00
_MAX_RESULTS_PER_PAGE = 200
_DEFAULT_RESULTS_PER_PAGE = 50
2015-10-17 18:23:46 +02:00
def _get_collection ( self , endpoint , collection_id , * * query ) :
2015-11-21 19:49:58 +01:00
limit = min (
2015-10-30 23:56:07 +01:00
query . get ( ' limit ' , self . _DEFAULT_RESULTS_PER_PAGE ) ,
self . _MAX_RESULTS_PER_PAGE )
2019-10-27 17:52:46 +01:00
query . update ( {
' limit ' : limit ,
' linked_partitioning ' : 1 ,
' offset ' : 0 ,
} )
next_url = update_url_query ( self . _API_V2_BASE + endpoint , query )
2015-10-17 18:23:46 +02:00
collected_results = 0
2015-11-21 16:21:21 +01:00
for i in itertools . count ( 1 ) :
2015-11-21 15:04:35 +01:00
response = self . _download_json (
2015-11-21 16:21:21 +01:00
next_url , collection_id , ' Downloading page {0} ' . format ( i ) ,
2015-11-21 15:04:35 +01:00
' Unable to download API page ' )
2015-10-17 18:23:46 +02:00
2015-11-21 16:21:21 +01:00
collection = response . get ( ' collection ' , [ ] )
if not collection :
break
2015-10-17 18:23:46 +02:00
2015-11-21 16:21:21 +01:00
collection = list ( filter ( bool , collection ) )
2015-10-17 18:23:46 +02:00
collected_results + = len ( collection )
2015-11-21 16:21:21 +01:00
for item in collection :
yield self . url_result ( item [ ' uri ' ] , SoundcloudIE . ie_key ( ) )
2015-10-17 18:23:46 +02:00
2015-11-21 16:21:21 +01:00
if not collection or collected_results > = limit :
2015-10-17 18:23:46 +02:00
break
2015-11-21 15:04:35 +01:00
next_url = response . get ( ' next_href ' )
2015-11-21 16:21:21 +01:00
if not next_url :
break
2015-10-17 18:23:46 +02:00
def _get_n_results ( self , query , n ) :
2019-10-27 17:52:46 +01:00
tracks = self . _get_collection ( ' search/tracks ' , query , limit = n , q = query )
2015-11-21 16:21:21 +01:00
return self . playlist_result ( tracks , playlist_title = query )