[Solved]How to get a redirected URL?
BlitzMax Forums/BlitzMax Programming/[Solved]How to get a redirected URL?
| ||
Hi everyone! Is there a way to get a redirected URL? My issue is that Fmod doesn't support this, so in some cases I can't stream an URL because the server redirects the connection to somewhere else and adds tokens to the end of the url. If I add the main URL manually to my browser window it works fine. If I use the same new URL in my player via (CreateStreamURL(..)) it works too. But only for my session and not for other users. Example URLs I got from ambushradio.gr: MP3 (redirected) http://stream.radiojar.com/k0kda1hmb Compatible with: Windows Media Player, WinAmp, iTunes, VLC. In addition it would nice, if I could automatically extract urls from the playlists. I don't, know if there are modules that do this? M3U (playlist file) http://stream.radiojar.com/k0kda1hmb.m3u PLS (playlist file) http://stream.radiojar.com/k0kda1hmb.pls The most popular streaming format, compatible with most media players. Example app (requires Brucey's Fmod module and MaxGUI): Thanks in advance! Grisu |
| ||
Not sure if this works in your context, but you can communicate directly to port 80 of the server and query it. I don't remember how to do that from within blitzmax, but know that you can do it. Here's a sample with telnet, to show you a proof of concept: TELNET stream.radiojar.com 80 then send the following text: GET /k0kda1hmb.pls HTTP/1.1 HOST: stream.radiojar.com then send two enters (hostname is necessary since most modern servers will allow countless websites to share the same IP. If you don't specify the host, then the default website listening on that port will answer, which may not be the one you're trying to reach) in this case, the server responds with: HTTP/1.1 200 OK Cache-Control: no-cache Content-Type: audio/x-scpls X-Cloud-Trace-Context: 8c37b5d7545f46238070b04a07281783 Date: Sun, 08 Jan 2017 10:11:09 GMT Server: Google Frontend Content-Length: 113 [Playlist] NumberOfEntries=1 File1=http://162.13.25.174/k0kda1hmb.mp3 Title1=k0kda1hmb Length=-1 Version=2 I'm assuming that this line contains the relevant info for you: File1=http://162.13.25.174/k0kda1hmb.mp3 If I try to navigate directly to that URL in my browser, it starts playing the MP3 immediately. --- NOTE: Trying the .m3u link you posted, the response showed the following location: http://162.13.25.174/k0kda1hmb then telnetting to 162.13.25.174 port 80 and requesting k0kda1hmb , it returned the following: HTTP/1.1 302 Found location: http://ample-02.radiojar.com:80/k0kda1hmb?rj-ttl=5&rj-token=AAABWX2gmAWSiBc8WiNfR_g6-L2i5OW-QsbEblJ2MFUx9zs0J6ENIQ Transfer-Encoding: chunked Connection: Keep-Alive the HTTP 302 means a temporary redirect (301 is permanent redirect), and the next line shows where the redirected location is. Pulling up that ample-02.radiojar.com URL plays the MP3 directly in my browser. |
| ||
Already had used this URL in my example code above. Sadly, it doesn't work. You need some sort of "token sting" at the end, else it won't start streaming, i.e. http://ample-02.radiojar.com/k0kda1hmb.mp3?rj-ttl=5&rj-token=AAABWX2iWGq0oQU-CxNjopVaLLvpyP_u4M6wObJEZXfUR4tBoX8R0Q |
| ||
Updated post above. |
| ||
Same result with: http://162.13.25.174/k0kda1hmb. :( If I copy and paste the full url from the browser window it works, but no other user can use it. Somehow VLC and other players get around this. I would need to get the "final" url and use this for the OpenStreamURL() function instead. |
| ||
@ grisu Did you check that part of xlsior's bigger post? There the token was assigned - so you could build that url. I think the other players support "sessions" (like "curl" would do) so the token is transported correctly and you do not need to extract it on your own. bye Ron |
| ||
I read the post, but I don't know how I could write a code that gives me that information. Adding a function to add the token to the streaming URL should be easy. So I need to check, if I get a HTTP 302 (redirected) or 301 (static) response from an url. Then get the new URL +token for 302. And use the whole string in my player. Just an idea: Can't I use libcurl() to do the job? |
| ||
Hi Grisu, you can use libCurl for the task. I'm just guessing that simply copying the redirect url with token isn't enough. The answer would be to process libCurls write-function directly to get the valid stream (just guessing :-)). The url information is found in header. -Henri |
| ||
This Code Archives example checks for redirected URLs: BlitzGet MaxDeluxe |
| ||
For fun, here is a libCurl version of getting the header and possible processing the stream file (prototype). This does the same thing as James's example.SuperStrict Framework BaH.libcurl Import brl.System Import brl.StandardIO Local curl:TCurlEasy = TCurlEasy.Create() curl.setOptInt(CURLOPT_HEADER, 1) curl.setOptString(CURLOPT_URL, "http://162.13.25.174/k0kda1hmb.mp3") curl.setHeaderCallback( processHeader ) Local res:Int = curl.perform() curl.cleanup() Function processHeader:Int(buffer:Byte Ptr, size:Int, data:Object) Local s:String = String.FromBytes(buffer, size).Trim() 'Check header start If s.StartsWith("HTTP/") Local pos:Int = s.Find(" ") Local code:Int = Int( s[pos..size].Trim() ) DebugLog code EndIf Local pos:Int 'Get location part of the header If s.StartsWith("location:") Local pos:Int = 10 Local content:String = s[pos..size].Trim() DebugLog content EndIf Return size EndFunction Function processDownload:Int(buffer:Byte Ptr, size:Int, data:Object) Print "Start writing..." + size If Not size Then Return size If Not TStream(data) Then Notify("Error: WriteStream not found.", True); Return False 'If Not fStream Then fStream = WriteStream(fName) TStream(data).WriteBytes(buffer, size) Return size EndFunction Playlist files are simple text files which you can easily extract the url / file info by parsing them line at a time seeking keywords. -Henri |
| ||
Thanks all for the help. I went with Henri's code. With the following functions I precheck an url to find out, if it's a redirected one. If it is, it should replace the "station_url" string with the new url. But only then. I can't figure out how to make this work 100%. There are some urls that "break" the function. :( Function Get_Redirected_Url(station_url:String) Local curl:TCurlEasy = TCurlEasy.Create() curl.setOptInt(CURLOPT_HEADER, 1) curl.setOptString(CURLOPT_URL, station_url) curl.setHeaderCallback( processHeader ) Local res:Int = curl.perform() curl.cleanup() End Function Function ProcessHeader:Int(buffer:Byte Ptr, size:Int, data:Object) ' Used to find redirected station urls! Local s:String = String.FromBytes(buffer, size).Trim() Local redir:Int=0 'Check header start If s.StartsWith("HTTP/") Local pos:Int = s.Find(" ") Local code:Int = Int( s[pos..size].Trim() ) 'Print code 'DebugLog code If code <> 302 Then Return 0 EndIf Local pos:Int 'Get location part of the header If s.StartsWith("location:") Local pos:Int = 10 Local content:String = s[pos..size].Trim() station_url:String=content ' Print station_url 'DebugLog "> "+content EndIf Return size EndFunction |
| ||
I switched over to James code. Less fancy, but it seems to work better. ;) Any way to "improve" this code further? Please let me know. Function GetMovedUrL:Int(url:String) If Not url Then Return False Local success:Int = False ' File 302 with new url Local done:Int = False ' Exit loop (for retries, etc) Local host:String Local file:String Local bytestoread:Int Local date:String Local server:String Local contenttype:String Local location:String Local pos:Int Repeat If Left (url, 7) = "http://" url= Right (url, Len (url) - 7) EndIf Local slash:Int = Instr (url, "/") If slash host = Left (url, slash - 1) file = Right (url, Len (url) - slash + 1) Else host = url file = "/" EndIf Local http:TSocket = CreateTCPSocket () If http If ConnectSocket (http, HostIp (host), 80) Local www:TStream = CreateSocketStream (http) WriteLine www, "GET " + file + " HTTP/1.1" ' "GET /" gets default page... WriteLine www, "Host: " + host WriteLine www, "User-Agent: BlitzGet Deluxe" WriteLine www, "Accept: */*" WriteLine www, "" Local response:String = ReadLine (www) 'Print "Server response: " + response Local replycode:String If Left (response, 5) = "HTTP/" pos = Instr (response, " ") replycode = Mid (response, pos + 1, 3) EndIf Local header:String Repeat header = ReadLine (www) Local reply:String = "" pos = Instr (header, ": ") If pos reply = Left (header, pos + 1) EndIf Select Lower (reply) Case "content-length: " bytestoread = Int (Right (header, Len (header) - Len (reply))) Case "location: " location = Right (header, Len (header) - Len (reply)) 'Print location End Select 'If header Then 'Print header ' Skip blank line (if header = "" then nothing is printed)... Until header$ = "" Or (Eof (www)) Select replycode$ Case "200" ' File found... 'Print "found" done:Int=True Case "404" ' File Not found... 'Print "not found" done:Int=True Case "301" ' File permanently moved... ' station_url:String = location ' Print "301 "+location done:Int=True ' success:Int=True Case "302" ' File temporarily moved... station_url:String = location ' Print "302 "+location done:Int=True success:Int=True Case "303" ' File moved... ' station_url:String = location done:Int=True Case "307" ' Naughty... ' station_url:String = location done:Int=True ' Default ' done:Int=True End Select EndIf CloseSocket http EndIf done:Int=True Until done Return success End Function Will test it for stability over the rest of the weekend. Grisu |
| ||
If someone wants to check it out, I uploaded a build here: http://www.mediafire.com/file/3twn21a4cle5n1h/prp_ambush.zip The zip file only includes the Ambush Radio station to keep it small. |Edit| Replaced by the full version: http://pocketradio.awardspace.info/downloads.html |
| ||
Glad you got it working. My example was just a proof of consept really. For informational purposes can you give some urls that broke the code ? I'm interested :-) -Henri |
| ||
One example is GB Radio: http://184.154.202.243:8163 E.g. the "location" string gave back data like it was streaming already. |
| ||
... James code. Less fancy, but it seems to work better. ;) Woo-hoo! It's definitely not pretty, but seems to work well... You might want to update the User-Agent string, ie. the name of your client, which is sent to the server: WriteLine www, "User-Agent: BlitzGet Deluxe" |
| ||
Oh, didn't notice that. Thanks. Will change it to something else for the next release. |