I am having trouble getting a google safe search lookup working. Here is the code I am trying and I am always getting {} in the response
var request = require("request-promise")
var body = {
"client": {
"clientId": "myclientid",
"clientVersion": "1.0"
},
"threatInfo": {
"threatTypes": ["MALWARE"],
"platformTypes": ["WINDOWS", "LINUX"],
"threatEntryTypes": ["URL"],
"threatEntries": [
{"url": "http://some-malicious-url"}
]
}
}
var options = {
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
},
method: "POST",
url: "https://safebrowsing.googleapis.com/v4/threatMatches:find?key=my-api-key",
form: body
}
request(options).then(function (data){
console.log(data)
})
Not sure if I understood the docs correctly or if I am missing any param from my request. I have tried with different urls. Tried searching for malicious urls form web which were identified by my browser as malicious but those also returned blank response. Here is the documentation I am following
I also tried their threatLists:list API and that always gives an error
curl -XGET https://safebrowsing.googleapis.com/v4/threatLists:list?key=my_api_key
Here is the output that I get:
<!DOCTYPE html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
<title>Error 404 (Not Found)!!1</title>
<style>
*{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}#media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}#media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}#media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
</style>
<a href=//www.google.com/><span id=logo aria-label=Google></span></a>
<p><b>404.</b> <ins>That’s an error.</ins>
<p>The requested URL <code>/v4/threatLists:list?key=my_api_key</code> was not found on this server. <ins>That’s all we know.</ins>
threatLists working example:
threatLists works without :list. e.g. curl https://safebrowsing.googleapis.com/v4/threatLists?key=YOUR_KEY
threatMatches working example:
Method: POST
Content-Type:application/json
https://safebrowsing.googleapis.com/v4/threatMatches:find?key=YOUR_KEY
Body:
{
"client": {
"clientId": "testing",
"clientVersion": "0.0.1"
},
"threatInfo": {
"threatTypes": ["MALWARE","SOCIAL_ENGINEERING","UNWANTED_SOFTWARE","MALICIOUS_BINARY"],
"platformTypes": ["ANY_PLATFORM"],
"threatEntryTypes": ["URL"],
"threatEntries": [
{"url": "http://goooogleadsence.biz/"}
]
}
}
The code is correct. I have the same...
It seems Chrome browser has an other (more accurate) source for safe browsing. Maybe they keep these fresh data exclusively for some specific clients.
The result is the same whatever lookup protocol version you can use.
And this is the same if you use the update API.
If someone knows, really, why... I am very interested in this information.
Please make sure that you are appending "/" in you URLs in the request. It is important to do that.
In this case, your URL would become http://some-malicious-url/.
Let me know if this works!
Related
In my chrome extension (manifest V3) I want to import some scripts like jquery and more.
Inside my backgound.js I have:
try {
importScripts('/js/jquery-3.4.1.min.js', '/js/common.js');
} catch (e) {
console.error('importScripts: ' + e);
}
...
calling to getCookie...
inside common.js I have function like:
async function getCookie(key) {
return ...;
}
but when I load the extension I get the error:
background.js:22 importScripts: TypeError: Cannot read property 'createElement' of undefined
This error comes from the Jquery library
and after I get another error:
Uncaught (in promise) ReferenceError: getCookie is not defined
because the error in jquery it doesn't load the common script? how can I fix that?
Is there a more stable solution to import the scripts? so that error in one script will not cause a fail to other scripts?
Posting the working solution for me: import the scripts from npm into background service worker:
In my manifest.json adding "type": "module" to my background script:
"background": {"service_worker": "background.js" , "type":"module"}
Inside my background.js simply importing desired module script:
import Dexie from "/node_modules/dexie/dist/modern/dexie.min.mjs"
REMAKRS:
Please notice that from Manifest Version 3 in order to invoke script into web-page from your background service workers you need to use chrome.scripting.executeScript. Example:
//background.js
let [tab] = await chrome.tabs.query({active: true, currentWindow: true})
//invoke function
await chrome.scripting.executeScript({
target: {tabId: tab.id},
function: colorSelectedText,
args: [tab.title]
})
});
//OR
//invoke file
await chrome.scripting.executeScript({
target: {tabId: tab.id},
files: ['your_script.js']
})
});
Desirable scripts must be in same parent folder as your manifest.json (wasn't working when I was trying to use two dots ../path)
You can package your extension application with the version of jquery you would like to use . then you add it as part of your service workers. This is how my manifest.json looks like
{
"name": "Foo Bar",
"description": "NA",
"version": "1.0",
"manifest_version": 3,
"permissions": [
"storage"
],
"action": {
"default_popup": "popup.html"
},
"background": { "service_workers": ["bg-loader.js","/js/jquery-3.6.0.min.js" ]}
}
I have a bg-loader.js which i use to import my js logic script where i have my jquery functions
try {
importScripts('/js/index.js' /*, and so on */);
} catch (e) {
console.error(e);
}
Then in my index.html i add my jquery script to my popup.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>test</title>
<link rel="stylesheet" href="css/styles.css">
</head>
<body>
</body>
<script type="text/javascript" src="js/jquery-3.6.0.min.js"></script>
<script type="text/javascript" src="js/index.js"></script>
</html>
I am working on a project where I will need to play HLS encrypted video (.m3u8) file. I am using CloudFront and signed cookies to secure the content. I am able to play .m3u8 file without signed cookies but when I use signed cookies then cookies do not get send in requests.
I am using the alternate domain for CloudFront distribution and I confirm that apart from .m3u8 file I am able to access all other files using signed cookies.
After research, I found that if I set withCredentials to true like the following code then signed cookies will be send in request:
player.ready(function() {
player.src({
src: 'https://protected.example.com/output-plain/art.m3u8',
type: 'application/x-mpegURL',
withCredentials: true
});
});
This code works and signed cookies are getting send in request however I started getting a new error which is:
Access to XMLHttpRequest at 'https://protected.example.com/output-plain/art.m3u8undefined' from origin 'https://example.com' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
Then I found that I have to set Access-Control-Allow-Credentials to true. however, this does not work for me.
I am using video.js library, I have also tried hls.js and getting the same error and stuck at same place.
I am stuck on this issue for the last 7 days and I think AWS docs are really overwhelming, I have referred many questions on SO and issues on Github but still no luck. Hope someone will help me here.
Here is the screenshot of CloudFront distribution behavior:
And here is my php and js code; index.php:
<?php
header("Access-Control-Allow-Origin: https://protected.example.com");
header("Access-Control-Allow-Credentials: true");
header("Access-Control-Max-Age: 1000");
header("Access-Control-Allow-Headers: X-Requested-With, Content-Type, Origin, Cache-Control, Pragma, Authorization, Accept, Accept-Encoding");
header("Access-Control-Allow-Methods: PUT, POST, GET, OPTIONS, DELETE");
?>
<!DOCTYPE html>
<html>
<head>
<link href="https://vjs.zencdn.net/7.10.2/video-js.css" rel="stylesheet" />
</head>
<body>
<video
id="video_player"
class="video-js"
controls
preload="auto"
poster="//vjs.zencdn.net/v/oceans.png"
data-setup='{}'
width=600 height=300>
</video>
<script src="https://vjs.zencdn.net/7.10.2/video.js"></script>
<script>
var player = videojs('video_player')
player.responsive(true);
player.ready(function() {
player.src({
src: 'https://protected.example.com/output-plain/art.m3u8',
type: 'application/x-mpegURL',
withCredentials: true
});
});
</script>
</body>
</html>
Here is S3 bucked CORS Policy:
[
{
"AllowedHeaders": [
""
],
"AllowedMethods": [
"POST",
"GET",
"PUT",
"HEAD"
],
"AllowedOrigins": [
"*"
],
"ExposeHeaders": []
}
]
Thank you in advance.
The answer is within the browser's error message, "The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'." Your S3 bucket's CORS policy cannot use a wildcard for the AllowedOrigins value.
Also your empty AllowedHeaders value may be removing the Host value from the preflight request check, so let's set it to a wildcard just to be safe.
If you update your S3 bucket's CORS policy to this, it should resolve the issue:
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"POST",
"GET",
"PUT",
"HEAD"
],
"AllowedOrigins": [
"https://example.com",
"https://protected.example.com"
],
"ExposeHeaders": []
}
]
I have a big deeply nested JS object that is send to the Express.js server after it gets JSON.strify() and turned it into an string. I tried the same thing with a smaller and shallowly nested object, then it worked fine.
Here's the big object:
{
channelName: 'PewDiePie',
channelTag: 'UC-lHJZR3Gqxm24_Vd_AJ5Yw',
channelLogoLink: 'https://yt3.ggpht.com/a/AATXAJzlZzr16izsGHBCHIkO3H7n-UiHyZPCJFEPiQ=s88-c-k-c0xffffffff-no-rj-mo',
unseenVideoTitles: [
[
'I Made The WORST Minecraft MISTAKE There Is. .. - Part 40',
'The MOST Dangerous Place In Minecraft!',
'I Found The New Biome in Minecraft! (Nether Update)',
'I\'m Back in Minecraft! - Part 39'
]
],
videoThumbnailLinks: [
[
'https://i.ytimg.com/vi/8HHZiNdrZGA/mqdefault.jpg',
'https://i.ytimg.com/vi/evcMQ7Lk8NU/mqdefault.jpg',
'https://i.ytimg.com/vi/aOXAtnb-grk/mqdefault.jpg',
'https://i.ytimg.com/vi/1B1f9PGLbIs/mqdefault.jpg'
]
],
videoLinks: [
[
'https://www.youtube.com/watch?v=8HHZiNdrZGA',
'https://www.youtube.com/watch?v=evcMQ7Lk8NU',
'https://www.youtube.com/watch?v=aOXAtnb-grk',
'https://www.youtube.com/watch?v=1B1f9PGLbIs'
]
],
videoUploadTime: [
[
'2020-03-13',
'2020-03-31',
'2020-03-25',
'2020-03-06'
]
]
}
Here's the code in Express.js:
router.get('/query/:stringifiedObj', (req, res) => {
const retrievedObj = JSON.parse(req.params.stringifiedObj);
const uid = retrievedObj.uid;
delete retrievedObj.uid;
console.log(uid, retrievedObj);
res.status(200).send(uid);
});
The error I get is:
HTTP/1.1 404 Not Found
X-Powered-By: Express
Content-Security-Policy: default-src 'none'
X-Content-Type-Options: nosniff
Content-Type: text/html; charset=utf-8
Content-Length: 955
Date: Sat, 04 Apr 2020 11:42:17 GMT
Connection: close
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot GET /api/query/%7B%22channelName%22:%22PewDiePie%22,%22channelTag%22:%22UC-lHJZR3Gqxm24_Vd_AJ5Yw%22,%22channelLogoLink%22:%22https://yt3.ggpht.com/a/AATXAJzlZzr16izsGHBCHIkO3H7n-UiHyZPCJFEPiQ=s88-c-k-c0xffffffff-no-rj-mo%22,%22unseenVideoTitles%22:[[%22I%20Made%20The%20WORST%20Minecraft%20MISTAKE%20There%20Is.%20..%20-%20Part%2040%22,%22The%20MOST%20Dangerous%20Place%20In%20Minecraft!%22,%22I%20Found%20The%20New%20Biome%20in%20Minecraft!%20(Nether%20Update)%22,%22I%27m%20Back%20in%20Minecraft!%20-%20Part%2039%22]],%22videoThumbnailLinks%22:[[%22https://i.ytimg.com/vi/8HHZiNdrZGA/mqdefault.jpg%22,%22https://i.ytimg.com/vi/evcMQ7Lk8NU/mqdefault.jpg%22,%22https://i.ytimg.com/vi/aOXAtnb-grk/mqdefault.jpg%22,%22https://i.ytimg.com/vi/1B1f9PGLbIs/mqdefault.jpg%22]],%22videoLinks%22:[[%22https://www.youtube.com/watch</pre>
</body>
</html>
Here's the string:
{"channelName":"PewDiePie","channelTag":"UC-lHJZR3Gqxm24_Vd_AJ5Yw","channelLogoLink":"https://yt3.ggpht.com/a/AATXAJzlZzr16izsGHBCHIkO3H7n-UiHyZPCJFEPiQ=s88-c-k-c0xffffffff-no-rj-mo","unseenVideoTitles":[["I Made The WORST Minecraft MISTAKE There Is. .. - Part 40","The MOST Dangerous Place In Minecraft!","I Found The New Biome in Minecraft! (Nether Update)","I'm Back in Minecraft! - Part 39"]],"videoThumbnailLinks":[["https://i.ytimg.com/vi/8HHZiNdrZGA/mqdefault.jpg","https://i.ytimg.com/vi/evcMQ7Lk8NU/mqdefault.jpg","https://i.ytimg.com/vi/aOXAtnb-grk/mqdefault.jpg","https://i.ytimg.com/vi/1B1f9PGLbIs/mqdefault.jpg"]],"videoLinks":[["https://www.youtube.com/watch?v=8HHZiNdrZGA","https://www.youtube.com/watch?v=evcMQ7Lk8NU","https://www.youtube.com/watch?v=aOXAtnb-grk","https://www.youtube.com/watch?v=1B1f9PGLbIs"]],"videoUploadTime":[["2020-03-13","2020-03-31","2020-03-25","2020-03-06"]]}
I have built a Google Action using Dialogflow and having trouble releasing an updated version to Alpha today.
The updated action works OK in Draft and I submitted it for Alpha deployment. The Actions console is showing the release as "Deployed". However, when I try to access the Alpha version I get an "isn't responding right now" response (from the simulator, Home device and Assistant smartphone app). The backend fulfilment service which is shared by Draft and Alpha is working OK.
I can see the following in the Debug console on the simulator:
{
"response": "MyAction Test isn't responding right now. Try again soon.",
"expectUserResponse": false,
"conversationToken": "EucBS2o5Uk...",
"audioResponse": "//NExAAP+H...",
"ssmlMarkList": [],
"debugInfo": {
"assistantToAgentDebug": {
"curlCommand": "curl -v 'https://api.api.ai/api/integrations/google?token=XXXXX&versionId=20' -H 'Content-Type: application/json;charset=UTF-8' -H 'Google-Actions-API-Version: 2' -H 'Authorization: XXXXX' -A Google-ActionsOnGoogle/1.0 -X POST -d '{\"user\":{\"accessToken\":\"eyXXXXX\",\"locale\":\"en-US\",\"lastSeen\":\"2019-09-12T09:58:03Z\",\"userStorage\":\"{\\\"data\\\":{\\\"welcomes\\\":53,\\\"intentCounts\\\":{\\\"BALANCE\\\":2}}}\",\"userVerificationStatus\":\"VERIFIED\"},\"conversation\":{\"conversationId\":\"ABXXXXXX\",\"type\":\"NEW\"},\"inputs\":[{\"intent\":\"actions.intent.MAIN\",\"rawInputs\":[{\"inputType\":\"KEYBOARD\",\"query\":\"Talk to MyAction Test\"}]}],\"surface\":{\"capabilities\":[{\"name\":\"actions.capability.AUDIO_OUTPUT\"},{\"name\":\"actions.capability.SCREEN_OUTPUT\"},{\"name\":\"actions.capability.ACCOUNT_LINKING\"},{\"name\":\"actions.capability.WEB_BROWSER\"},{\"name\":\"actions.capability.MEDIA_RESPONSE_AUDIO\"}]},\"availableSurfaces\":[{\"capabilities\":[{\"name\":\"actions.capability.WEB_BROWSER\"},{\"name\":\"actions.capability.SCREEN_OUTPUT\"},{\"name\":\"actions.capability.AUDIO_OUTPUT\"}]}]}'",
"assistantToAgentJson": "{\"user\":{\"accessToken\":\"eyXXXXX\",\"locale\":\"en-US\",\"lastSeen\":\"2019-09-12T09:58:03Z\",\"userStorage\":\"{\\\"data\\\":{\\\"welcomes\\\":53,\\\"intentCounts\\\":{\\\"BALANCE\\\":2}}}\",\"userVerificationStatus\":\"VERIFIED\"},\"conversation\":{\"conversationId\":\"ABXXXXX\",\"type\":\"NEW\"},\"inputs\":[{\"intent\":\"actions.intent.MAIN\",\"rawInputs\":[{\"inputType\":\"KEYBOARD\",\"query\":\"Talk to MyAction Test\"}]}],\"surface\":{\"capabilities\":[{\"name\":\"actions.capability.AUDIO_OUTPUT\"},{\"name\":\"actions.capability.SCREEN_OUTPUT\"},{\"name\":\"actions.capability.ACCOUNT_LINKING\"},{\"name\":\"actions.capability.WEB_BROWSER\"},{\"name\":\"actions.capability.MEDIA_RESPONSE_AUDIO\"}]},\"availableSurfaces\":[{\"capabilities\":[{\"name\":\"actions.capability.WEB_BROWSER\"},{\"name\":\"actions.capability.SCREEN_OUTPUT\"},{\"name\":\"actions.capability.AUDIO_OUTPUT\"}]}]}",
"delegatedRequest": {
"delegatedRequest": ""
}
},
"agentToAssistantDebug": {
"agentToAssistantJson": "<!DOCTYPE html><html lang=en><meta charset=utf-8><meta name=viewport content=\"initial-scale=1, minimum-scale=1, width=device-width\"><title>Error 400 (Not Found)!!1</title><style nonce=\"2YtDuE0THEDAcE3p1uuIgA\">*{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{color:#222;text-align:unset;margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px;}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}pre{white-space:pre-wrap;}ins{color:#777;text-decoration:none}a img{border:0}#media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}#media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}#media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}</style><div id=\"af-error-container\"><a href=//www.google.com><span id=logo aria-label=Google></span></a><p><b>400.</b> <ins>That’s an error.</ins><p>The requested URL was not found on this server. <ins>That’s all we know.</ins></div>",
"delegatedResponse": {
"delegatedResponse": ""
}
},
"sharedDebugInfoList": [],
"conversationBuilderExecutionEventsList": []
},
"visualResponse": {
"visualElementsList": [
{
"displayText": {
"content": "MyAction Test isn't responding right now. Try again soon."
}
}
],
"suggestionsList": [],
"agentLogoUrl": ""
},
"clientError": 0,
"is3pResponse": true,
"clientOperationList": [
{
"operationType": 4,
"startIndicatorPayLoad": {
"status": 1
}
},
{
"operationType": 7,
"exitIndicatorPayLoad": {
"status": 1
}
}
],
"projectName": "",
"renderedHtml": ""
}
And the Stackdriver log is showing:
2019-09-12 11:56:49.381 BST App returned an HTTP error. State: URL_ERROR
{
insertId: "1iedwiug2dbgn68"
labels: {
channel: "preview"
querystream: "GOOGLE_USER"
source: "AOG_REQUEST_RESPONSE"
}
logName: "projects/myaction-test/logs/actions.googleapis.com%2Factions"
receiveTimestamp: "2019-09-12T10:56:49.389233862Z"
resource: {
labels: {…}
type: "assistant_action"
}
severity: "ERROR"
textPayload: "App returned an HTTP error. State: URL_ERROR"
timestamp: "2019-09-12T10:56:49.381445860Z"
trace: "projects/XXXXX/traces/ABXXXXX"
}
Looking at Dialogflow, the Environments / versions view shows the status of the release as "Deploying", so there seems to be a mismatch between the Action and Dialogflow deployments..?
I have tried re-releasing to Alpha (original attempt was nearly 4 hours ago), but the same problem/ errors still exist. Both attempted releases are showing as "Deploying" in Dialogflow.
Has anyone seen this before? Any idea what the problem may be or how to resolve it?
Thanks!
No solution yet but I have opened a support request with logs that have the same error.
Haven't received a support response yet but did find a workaround.
Inside of the Dialogflow agent settings > Environments and under draft I was able to view all versions. The latest version that I was looking for did say "Ready". When I clicked on the three dots to the right I loaded it to draft. Then in the Actions on Google console I went to Deploy, then the alpha section and clicked "Submit for alpha". Almost immediately after deploying and seeing "Deployed", the skill was available again for the alpha users.
Definitely don't remember doing this previously.
Finally got a response from Google who said there was an "issue" that day. Seems to be working OK now
So I'm trying to stream soundcloud with soundmanager2, but i got this error from my background event page:
GET chrome-extension://api.soundcloud.com/tracks/155143944/stream?
client_id=d47c763873f2jan403dac26b62e3a820 net::ERR_FAILED chrome-
extension://api.soundcloud.com/tracks/155143944/stream?
client_id=d47c763873f2jan403dac26b62e3a820:1
T/tracks/155143944-0.7370116342790425: HTML5 error, code 4 soundmanager2.js:1191
T/tracks/155143944-0.7370116342790425: Failed to load / invalid sound? Zero-length
duration reported. (chrome-extension://api.soundcloud.com/tracks/155143944/stream?
client_id=d47c763873f2jan403dac26b62e3a820) soundmanager2.js:1189
Here is my code:
Manifest:
"permissions": [
"tabs",
"*://soundcloud.com/*"
],
"background": {
"scripts": ["soundmanager2/script/soundmanager2.js", "soundcloud_SDK.js",
"background.js"],
"persistent": false
},
Background.js:
SC.initialize({
client_id: "d47c763873f2jan403dac26b62e3a820"
});
soundManager.setup({
url: 'soundmanager2/swf',
onready: function() {
SC.stream("/tracks/155143944", function(sound){
sound.play();
});
}
});
This seems like an issue with protocol-relative URLs.
Look in the source files for strings like //api.soundcloud.com/ and put https: in front of it.
And maybe also location.protocol + '//api.soundcloud.com/' -> https://api.soundcloud.com/.