Comments (21)Free to use C# fix for the Seeqpod.com API
Added by mike | March 26th, 2009 | 18:03Categories: code for developers mashups silverlight update
Seeqpod pulls a fast one
A few hours ago we discovered that SeeqPod.com changed their API without notice. For us this meant that some of our audio playing capabilities were impaired. It seems they started using an authentication method that requires changing the HTTP Get request’s headers.
Our trusty development team, always up for a challenge, was put on code-red-alert mode to produce a quick fix so our users could resume hearing music ASAP. Half an hour ago team member Yuval Kesten finished writing the code that will enable us to utilize the new Seeqpod API and we’ve deceided to share his solution with the world for everyone to enjoy.
The code changes for the Seeqpod API
The following C# (Silverlight CLR) is free for anyone to use and enjoy (no copyrights).
Enjoy & pass it on…
: )
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Net;
using System.IO;namespace SeeqPod
{
class Program
{
static void Main(string[] args)
{
const string apiKey = “”;
const string apiUid = “”;string timeStamp = GenerateTimeStamp();
HMACSHA1 hmac = new HMACSHA1();
hmac.Key = Encoding.UTF8.GetBytes(apiKey);
string data = “/api/v0.2/music/search/nirvana/” + timeStamp;
byte[] call = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
string sCall = BytesToHex(call);var wc = HttpWebRequest.Create(“http://www.seeqpod.com/api/v0.2/music/search/nirvana/”);
wc.Headers.Add(“Seeqpod-uid”, apiUid);
wc.Headers.Add(“Seeqpod-call-signature”, sCall);
wc.Headers.Add(“Seeqpod-timestamp”, timeStamp);
var response = wc.GetResponse();// Get the stream associated with the response.
Stream receiveStream = response.GetResponseStream();// Pipes the stream to a higher level stream reader with the required encoding format.
StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);Console.WriteLine(“Response stream received.”);
Console.WriteLine(readStream.ReadToEnd());
response.Close();Console.ReadKey();
}
private static string GenerateTimeStamp()
{
TimeSpan ts = (DateTime.UtcNow – new DateTime(1970, 1, 1, 0, 0, 0, 0));
return ts.TotalSeconds.ToString();
}private static string BytesToHex(byte[] input)
{
StringBuilder sb = new StringBuilder();foreach (byte b in input)
{
sb.Append(string.Format(“{0:x2}”, b));
}return sb.ToString();
}
}
}



March 27th, 2009 at 5:12 am
Thanks for this, since i currently use the seeqpod API. One question though, could you show example code of it being downloaded and into an xml file?
March 27th, 2009 at 8:04 am
[...] and they are having trouble accessing the service. Semantic social search company HeadUp has published a solution to that [...]
March 27th, 2009 at 7:40 pm
I’ve been using a syntax more like:
“http://www.seeqpod.com/api/seeq/search?rm=1&q=” + txtSearch.Text + “&n=10″
…where “n” is the max results and “rm” is the media type (music, video, documents).
Is your code for getting cached music… or do you HAVE code to use for retrieving cached files? That’s what my main issue is at the moment.
March 27th, 2009 at 7:42 pm
The most bothersome thing about this change is that they say, “we’ve changed our api. look at api.php for changes” … or something like that.
YET IT HAS THE SAME OLD DOCUMENTATION!!
March 29th, 2009 at 5:29 am
This is wrong, you have incorrect length in there
private static string GenerateTimeStamp()
{
TimeSpan ts = (DateTime.UtcNow – new DateTime(1970, 1, 1, 0, 0, 0, 0));
return ts.TotalSeconds.ToString();
}
———-
This is correct
DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0);
TimeSpan span = DateTime.UtcNow – epoch;
int elapsed = (int)span.TotalSeconds;
string timeStamp = Convert.ToString(elapsed);
Console.WriteLine(elapsed);
March 29th, 2009 at 9:50 am
Thanks for noticing this and putting up the fix!
: )
March 30th, 2009 at 12:00 am
I sent a mail to seeqpod team for notice them their documentation were hard commented and difficul to understand.
I hope they will answer me, i would like to develop software using flex in action script.
March 30th, 2009 at 2:12 am
i’m writing in PHP and it looks similar to what you have, but still returns an error “mismatched call signature”
any thoughts?
array(
‘method’=>”GET”,
‘header’=>”Seeqpod-uid: “.UID.”\r\n” .
“Seeqpod-timestamp: “.$curTime.”\r\n” .
“Seeqpod-call-signature: “.$hash.”\r\n”
)
);
$headers = stream_context_create($opts);
$content = file_get_contents($url2, false, $headers);
March 30th, 2009 at 4:09 am
Noticed something in the API that is wrong in regards to cached music:
API:
http://www.seeqpod.com/api/v0.2/music/cachec//
couldn’t get it to work so I changed the typo:
http://www.seeqpod.com/api/v0.2/music/cache//
Do the same as the headup example adding the time stamp etc:
/api/v0.2/music/cache/e199ac53fd15ffba436da31a66a373ad9f47a59e/1238378672
or
string data = “/api/v0.2/music/cache/” + mp3_url_id + “/” + timeStamp;
Thanks Yuval & team for the blog post.
J.
March 30th, 2009 at 11:08 am
Hello guys, here are the source code for ActionScript that i used for my flex app:
var AppiUID:String=”MyUID”;
var AppiKey:String=”MyKEY”;
var MyDate:Date=new Date();
var Time:Number=(MyDate.getTime() / 1000);
var Cryptage:Object=new Object;
Cryptage=SHA1;
var MyHmac:String;
MyHmac=HMAC.hash(AppiKey, “/api/v0.2/music/search/coldplay/” + Time, Cryptage);
seeqpod.url=”http://www.seeqpod.com/api/v0.2/music/search/coldplay/”;
seeqpod.headers["Seeqpod-uid"]=AppiUID;
seeqpod.headers["Seeqpod-call-signature"]=MyHmac;
seeqpod.headers["Seeqpod-timestamp"]=Time;
seeqpod.send();
Important : You have to download corelib -> http://code.google.com/p/as3corelib/
Then you have to import like a basic import.
And use HTTPService in MXML :
Then i use ResultHandler function for add object to my arraycollection who will be my dataprovider of my datagrid…
See ya guys !
March 31st, 2009 at 4:09 am
I keep getting a “mismatched call signature error” and I’m writing in PHP.
Does anyone have a solution in PHP???
I’m using hash_hmac(“sha1″,$url,APIKEY)
and I’m setting my headers through stream_context_create()
March 31st, 2009 at 10:25 am
Hi Guys!
I think that all of this code sharing is great!
The million$ question (or 0.003$…) is what are you going to do the day they start charging money?
We at Semantinet are checking the alternatives as we speak but we still haven’t found such a large source which returns mp3s (and not flash players…) – I would really like to hear your thoughts as well.
Contact me at http://twitter.com/yuvalkesten or here on this blog
March 31st, 2009 at 9:37 pm
yeah, i’ve got the search to work with the code recommendations above, but still get the mismatched call signature when trying to pull from the cache.
if you’re unsure of the reason for getting the cached file, you may at one time run into an issue where either the mp3 won’t play at all OR you’ll get some other weird mp3 play (i.e. my latest one being some windows system startup sound).
April 1st, 2009 at 1:41 am
ok, sorry to keep posting to here, but i was ecstatic to even SEE something about this issue in a google search result.
so here’s how i’ve been testing. i went ahead to the seeqpod website with firefox (LiveHTTP Headers plugin installed) and did a search/play that needed to grab a cached file (via HTTP POST):
http://www.seeqpod.com/api/v0.2/music/cache/000c16f1bc9b3aaca06c1d81af6794f770891a9f
…where that long code is the mp3_url_id.
the HTTP POST data from “Live HTTP Headers” showed the following variables:
Seeqpod%5Fcall%5Fsignature=b87c1b8ea4530e45dc4309b8b25a53c7d19bbfc9&Seeqpod%5Fuid=69c976acce988bb343f1a90fa299856b2d65dc34&Seeqpod%5Ftimestamp=1238540522&random=G8pGs1Lrn
from that, i extracted the timestamp and call signature… ran the HMAC code with my appID and Jason’s example… but can clearly see why seeqpod is telling me the signatures don’t match. i’m guessing we just have the hash computing data wrong. i’m pretty sure %5F is the code for an underscore, but that doesn’t seem to matter, cause i can’t even get the call signatures in my HMAC to match with the hardcoded timestamp.
hope that makes any sense.
April 1st, 2009 at 7:39 am
I ran across in an example in Python – might help someone:
http://www.seeqpod.com/sampleClient.py
Jeremy – it sounds like something is funky with your timestamp/hashing. Can you post a little code or something?
Yuval – can you elaborate on this pay model?
I haven’t heard about it. Seeqpod did file chapter 11 today, but I read its more for asset protection than closing the doors.
Jason
April 1st, 2009 at 9:18 am
Could anyone help me, to implement a Java fix?
here is what ive done until now:
String uId = “37f4dc9c6e435a790f4ebeb882a90d668b159b5d”;
String apiK =”6fcfb11f176038e0934c2d3a05fb1fd854122c49″;
String timeStamp = String.valueOf(System.currentTimeMillis()/1000);
System.out.println(timeStamp);
String urlstr = “http://www.seeqpod.com/api/v0.2/music/search/”+this.titel+”/”+timeStamp;
// the url part for the hmac
String hmacstr = “/api/v0.2/music/search/”+this.titel+”/”+timeStamp;
url = new URL(urlstr);
System.out.println(timeStamp);
//hmac
String sCall = Signature.calculateRFC2104HMAC(hmacstr, apiK);
System.out.println(sCall);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//add header infos
conn.setRequestProperty(“Seeqpod-uid”, uId);
conn.setRequestProperty(“Seeqpod-call-signature”, sCall);
conn.setRequestProperty(“Seeqpod-timestamp”, timeStamp);
conn.connect();
//http status code
System.out.println(conn.getResponseCode());
/print the response
BufferedReader in = new BufferedReader(
new InputStreamReader(
conn.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
—————————————————————–
/// Generate the hmac sha-1 hex code
public static String calculateRFC2104HMAC(String data, String key)
throws java.security.SignatureException
{
String result;
try {
// get an hmac_sha1 key from the raw key bytes
SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), HMAC_SHA1_ALGORITHM);
// get an hmac_sha1 Mac instance and initialize with the signing key
Mac mac = Mac.getInstance(HMAC_SHA1_ALGORITHM);
mac.init(signingKey);
//compute the hmac on input data bytes
byte[] rawHmac = mac.doFinal(data.getBytes());
// base16-encode the hmac
result = Encoding.getHexString(rawHmac);
System.out.println(Encoding.EncodeBase16(rawHmac));
} catch (Exception e) {
throw new SignatureException(“Failed to generate HMAC : ” + e.getMessage());
}
return result;
}
———————————————————–
//// byte[] to HEX string
public static String getHexString(byte[] b){
String result = “”;
for (int i=0; i < b.length; i++) {
result +=
Integer.toString( ( b[i] & 0xff ) + 0×100, 16).substring( 1 );
}
return result;
}
///———————————————————-
/* *** another way to generate the hex
private static final char[] kDigits = { ‘0′, ‘1′, ‘2′, ‘3′, ‘4′, ‘5′, ‘6′, ‘7′, ‘8′, ‘9′, ‘a’,
‘b’, ‘c’, ‘d’, ‘e’, ‘f’ };
public static char[] bytesToHex(byte[] raw) {
int length = raw.length;
char[] hex = new char[length * 2];
for (int i = 0; i > 4;
int lowIndex = value & 0×0f;
hex[i * 2 + 0] = kDigits[highIndex];
hex[i * 2 + 1] = kDigits[lowIndex];
}
return hex;
}
*/
——————————-
The error response i got was:
mismatched call signature
any ideas?
greets jonas
btw: sry 4 my bad english xD
April 2nd, 2009 at 4:32 pm
I’m not really sure what it could be. I tried using the same method as the search method (which works), only substituting the “/api/v0.2/music/cache/[mp3_url_id]“. Since that didn’t work, I then sniffed out the HTTP POST header data on seeqpod’s actual site. Instead of setting “Seeqpod-uid” headers, it was sending “Seeqpod_uid” POST variables. From that, this is what i wrote:
private string getCachedFile(string mp3ID, string origSearch)
{
DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0);
TimeSpan span = DateTime.UtcNow – epoch;
int elapsed = (int)span.TotalSeconds;
string timestamp = Convert.ToString(elapsed);
HMACSHA1 hmac = new HMACSHA1();
hmac.Key = Encoding.UTF8.GetBytes(key);
string data = “/api/v0.2/music/cache/” + mp3ID + “/” + timestamp;
byte[] call = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
string sCall = BytesToHex(call);
string strResponse;
string variables = “Seeqpod_uid=” + uid + “&Seeqpod_call_signature=” + sCall + “&Seeqpod_timestamp=” + timestamp;
byte[] bytes = Encoding.ASCII.GetBytes(variables);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(“http://www.seeqpod.com/api/v0.2/music/cache/” + mp3ID);
request.Method = “POST”;
request.ContentType = “application/x-www-form-urlencoded”;
request.ContentLength = bytes.Length;
Stream os = request.GetRequestStream();
os.Write(bytes, 0, bytes.Length);
os.Close();
var response = request.GetResponse();
StreamReader readStream = new StreamReader(response.GetResponseStream());
strResponse = readStream.ReadToEnd();
readStream.Close();
response.Close();
return strResponse;
}
April 14th, 2009 at 11:58 pm
Hi there Jim,
I also wrestled with this problem in PHP before finally finding that I had to include a trailing slash on the request URL. Annoying!
April 16th, 2009 at 5:48 pm
Hi Liam, Hi Jim,
You can share code PHP?
April 28th, 2009 at 2:42 pm
my dtunes on my phone is still eefed up does anyone know how i can go back to my happy free to download music life on my i phone 3g. seeqpod is totally not even loading anymore . somebody fix it please im begging you.
November 7th, 2009 at 2:53 am
I’m a total convert to FireFox having run IE for about 10 years! Oh well, I’m making up for lost time time now. I run about 5 add-ons inclusing Fire.FM which is just awesome! Rocking on!