Используем Last.fm API с помощью C#

в 21:11, , рубрики: .net, api, last.fm api, скробблинг, метки: , , ,

Приветствую Вас, Читатели!
image
В этом посте я рассказал о своей программе для скробблинга треков на Last.fm. Теперь я хочу рассказать Вам, как на языке C# наладить взаимодействие с Last.fm API на примере скробблинга трека.

Первое, что нам нужно сделать — это зарегистрировать свой API аккаунт. Заходим в свой профиль и переходим по этой ссылке. После заполнения нужных полей мы получим так называемые API Key и секретный ключ. Они понадобятся для идентификации вашего клиента сервисом и получения ключа сессии. Проблем с их получением у меня не возникло.

Так как скробблинг трека требует получения ключа сессии, то нас будут интересовать следующие методы:
Получение токена
Получение ключа сессии
Скроббл трека

Упрощённая схема такова — мы получаем токен, используя наш API Key, потом получаем ключ сессии, используя токен и секретный ключ, потом скробблим трек, используя ключ сессии, токен и ещё несколько параметров.

Также нам понадобится использование двух классов — HttpWebRequest и HttpWebResponse для осуществления HTTP-запросов.

Итак, приступим к рассмотрению кода.
Сначала метод для получения сессии:

// создаём объект HttpWebRequest через статический метод Create класса WebRequest, явно приводим результат к HttpWebRequest. В параметрах указываем страницу, которая указана в API, в качестве параметров - method=auth.gettoken и наш API Key
HttpWebRequest tokenRequest = (HttpWebRequest)WebRequest.Create("http://ws.audioscrobbler.com/2.0/?method=auth.gettoken&api_key=" + ApiKey); 

// получаем ответ сервера
HttpWebResponse tokenResponse = (HttpWebResponse)tokenRequest.GetResponse(); 

// и полностью считываем его в строку
string tokenResult = new StreamReader(tokenResponse.GetResponseStream(), Encoding.UTF8).ReadToEnd(); 

// извлекаем то, что нам нужно. Можно сделать и через парсинг XML (видимо, я о нём ещё не знал в тот момент, когда писал этот код).
string token = String.Empty;
for (int i = tokenResult.IndexOf("<token>") + 7; i < tokenResult.IndexOf("</token"); i++)
{
	token += tokenResult[i];
}

// запускаем в браузере по умолчанию страницу http://www.last.fm/api/auth/ c параметрами API Key и только что полученным токеном)
Process s = Process.Start("http://www.last.fm/api/auth/?api_key=" + ApiKey + "&token=" + token);

// запускается страница, где у пользователя спрашивается, можно ли разрешить данному приложению доступ к профилю.

// ждём подтверждения от пользователя
DialogResult d = MessageBox.Show("Вы подтвердили доступ?", "Подтверждение", MessageBoxButtons.OKCancel, MessageBoxIcon.Question);

// если пользователь дал согласие
if (d == DialogResult.OK)
{
	// создаём сигнатуру для получения сессии (указываем API Key, метод, токен и наш секретный ключ, всё это без символов '&' и '='
	string tmp = "api_key" + ApiKey + "methodauth.getsessiontoken" + token + mySecret; 
	
	// хешируем это алгоритмом MD5 (думаю, у вас не будет проблем найти его в Интернете)
	string sig = MD5(tmp);
	
	// получаем сессию похожим способом
	HttpWebRequest sessionRequest = (HttpWebRequest)WebRequest.Create("http://ws.audioscrobbler.com/2.0/?method=auth.getsession&token=" + token + "&api_key=" + ApiKey + "&api_sig=" + sig);
	// уже не помню, зачем это свойство выставлять в true, но это обязательно. Зачем-то им нужно перенаправление.
	sessionRequest.AllowAutoRedirect = true;

// получаем ответ
	HttpWebResponse sessionResponse = (HttpWebResponse)sessionRequest.GetResponse();
	string sessionResult = new StreamReader(sessionResponse.GetResponseStream(),
								   Encoding.UTF8).ReadToEnd();

	// извлечение сессии (опять же проще использовать XML парсер)
	for (int i = sessionResult.IndexOf("<key>") + 5; i < sessionResult.IndexOf("</key>"); i++)
	{
		sessionKey += sessionResult[i];
	}
}

Итак, полдела сделано, двигаемся дальше. Теперь нужно заскробблить трек.

// узнаем UNIX-время для текущего момента
TimeSpan rtime = DateTime.Now - (new DateTime(1970, 1, 1, 0, 0, 0));
TimeSpan t1 = new TimeSpan(3, 0, 0);
rtime -= t1; // вычитаем три часа, чтобы не было несоответствия из-за разницы в часовых поясах
// получаем количество секунд
int timestamp = (int)rtime.TotalSeconds;

// формируем строку запроса
string submissionReqString = String.Empty;

//добавляем параметры (указываем метод, сессию и API Key):
submissionReqString += "method=track.scrobble&sk=" + sessionKey + "&api_key=" + ApiKey;
           
// добавляем только обязательную информацию о треке (исполнитель, трек, время прослушивания, альбом), кодируя их с помощью статического метода UrlEncode класса HttpUtility.
submissionReqString += "&artist=" + HttpUtility.UrlEncode(artist);
submissionReqString += "&track=" + HttpUtility.UrlEncode(track);
submissionReqString += "& timestamp=" + timestamp.ToString(); // в этой строке не должно быть пробела между & и t. Просто почему-то Хабр неправильно отображает этот участок, если пробел убрать.
submissionReqString += "&album=" + HttpUtility.UrlEncode(album);

// формируем сигнатуру (параметры должны записываться сплошняком (без символов '&' и '=' и в алфавитном порядке):
string signature = String.Empty;

// сначала добавляем альбом
signature += "album" + album;

// потом API Key
signature += "api_key" + ApiKey;
           
// исполнитель		   
signature += "artist" + artist;

// метод и ключ сессии
signature += "methodtrack.scrobblesk" + sessionKey;

// время
signature += "timestamp" + timestamp;

// имя трека
signature += "track" + track;

// добавляем секретный код в конец
signature += mySecret; 

// добавляем сформированную и захешированную MD5 сигнатуру к строке запроса
submissionReqString += "&api_sig=" + MD5(signature);

// и на этот раз делаем POST запрос на нужную страницу
HttpWebRequest submissionRequest = (HttpWebRequest)WebRequest.Create("http://ws.audioscrobbler.com/2.0/"); // адрес запроса без параметров

// очень важная строка. Долго я мучался, пока не выяснил, что она обязательно должна быть
submissionRequest.ServicePoint.Expect100Continue = false;

// Настраиваем параметры запроса
submissionRequest.UserAgent = "Mozilla/5.0";
// Указываем метод отправки данных скрипту, в случае с POST обязательно
submissionRequest.Method = "POST"; 
// В случае с POST обязательная строка
submissionRequest.ContentType = "application/x-www-form-urlencoded"; 

// ставим таймаут, чтобы программа не повисла при неудаче обращения к серверу, а выкинула Exception
submissionRequest.Timeout = 6000;

// Преобразуем данные в соответствующую кодировку, получаем массив байтов из строки с параметрами (UTF8 обязательно)
byte[] EncodedPostParams = Encoding.UTF8.GetBytes(submissionReqString); 
submissionRequest.ContentLength = EncodedPostParams.Length;

// Записываем данные в поток запроса (массив байтов, откуда начинаем, сколько записываем)
submissionRequest.GetRequestStream().Write(EncodedPostParams, 0, EncodedPostParams.Length); 
// закрываем поток
submissionRequest.GetRequestStream().Close(); 

// получаем ответ сервера
HttpWebResponse submissionResponse = (HttpWebResponse)submissionRequest.GetResponse(); 

// считываем поток ответа
string submissionResult = new StreamReader(submissionResponse.GetResponseStream(), Encoding.UTF8).ReadToEnd(); 
// разбор полётов. Если ответ не содержит status="ok", то дело плохо, выкидываем Exception и где-нибудь ловим его.
if (!submissionResult.Contains("status="ok"")) 
         throw new Exception("Треки не отправлены! Причина - " + submissionResult);

// иначе всё хорошо, выходим из метода и оповещаем пользователя, что трек заскробблен.

Таким образом, треки скробблятся без ввода пароля. Это обновлённый API, раньше ввод пароля требовался (это можно понять из моей программы). Сохранив куда-нибудь ключ сессии, вам не нужно будет каждый раз просить пользователя подтверждать доступ (ключ будет действовать до тех пор, пока пользователь не «отключит» ваш клиент от своего профиля на этой странице).

Основные ссылки я привёл в начале статьи. Пожалуй, остаётся добавить ссылку на Last.fm форум для использующих API (только на английском). Без его использования я вряд ли дошёл бы до конца.

На этом всё. Надеюсь, я достаточно подробно описал каждый свой шаг. Буду рад ответить на Ваши вопросы, если таковые возникнут.
Спасибо за внимание.

Автор: v_decadence


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js