Сегодня будет достаточно длинная, но полезная статья о том, как реализовать загрузчик музыки из vk.com (вконтакте) на C#. Зачем это нужно, ведь существует огромное количество сервисов, которые могут это сделать? Как минимум, уметь работать с API VK это хорошо, а уметь делать это на C# — еще лучше). Да и использовать свой «велосипед» порой намного приятнее…
Для начала сформулируем требования, для нашего приложения. Приложение должно уметь авторизоваться под нашей учётной записью, получать список мелодий у заданного пользователя либо группы, производить фильтрацию списка для поиска требуемой мелодии, возможность прослушать мелодию до ей загрузки, возможность загрузки отдельных мелодий или всего списка сразу в заданный пользователем каталог.
Начнем по порядку. Для того, чтобы приложение имело возможность работать с API «вконтекте» оно должно получить соответствующие разрешения. Чтобы это сделать, необходимо создать новое приложения на сайте vk.com. Для этого зайдём в раздел «разработчиками» (http://vk.com/dev) и выберем «Создать приложение». Записываем название приложения, выбираем тип «Standalone-приложение» и нажимаем «Подключить приложение».
После этого, придётся подтвердить код по СМС, а после его подтверждения мы увидим страницу для заполнения информации о приложении. Внеся требуемые изменения (при желании) необходимо нажать на «Сохранить изменения», после чего перейти на вкладку «Настройки», на которой можно увидеть ID нашего приложения.
Данный ID следуем записать (или запомнить), так как в дальнейшем он нам понадобится.
Теперь время переходить к созданию самого приложения. Для этого создаём новый проект типа «Wpf Application». Для удобства, сразу набросаем простенький интерфейс, выглядеть это будет примерно так.
Также добавим на форму элемент типа «WebBrowser», который будет занимать всё пространство на форме, но отключим его отображения выбрав параметр Visibility="Hidden". Элемент WebBrowser будет использоваться для авторизации на сайте Vk.
Теперь зайдём в файл App.config и в раздел appSettings вставим 3 строки.
<add key="VKAppId" value="4742982" />
<add key="VKScope" value="8" />
<add key="VKRedirectUri" value="http://oauth.vk.com/blank.html" />
- VKAppId — ID приложения, созданного на первом этапе.
- VKScope — права, которые требуются приложению, для доступа к музыке будет достаточно указать «8». Более подробно можно узнать здесь: http://vk.com/dev/permissions
- VKRedirectUri — адрес, на который будет передан access_token.
Теперь зайдём в файл MainWindow.xaml.cs в нём найдём конструктор MainWindow() и добавим в его строки:
webBrowser.Visibility = Visibility.Visible;
webBrowser.Navigate(String.Format("https://oauth.vk.com/authorize?client_id={0}&scope={1}&redirect_uri={2}&display=page&response_type=token", ConfigurationSettings.AppSettings["VKAppId"], ConfigurationSettings.AppSettings["VKScope"], ConfigurationSettings.AppSettings["VKRedirectUri"]));
webBrowser — это имя ранее добавленного компонента WebBrowser. Visibility.Visible делает его видимым, сам элемент перерывает остальные элементы и отображает страницу, переданную в методе Navigate.
Теперь создадим класс Vk следующего вида:
public static class Vk
{
public static string AccessToken { get; set; }
public static string UserId { get; set; }
}
Использование статического класса позволит получить глобальный доступ к переменным из любой части приложения.
-
AccessToken — хранит токен доступа, который мы получим после авторзации;
- UserId — идентификатор пользователя, под которым авторизовались.
Теперь для элемента webBrowser добавим обработку события Navigated, которое срабатывает после загрузки страницы.
private void WebBrowserNavigated(object sender, NavigationEventArgs e)
{
var clearUriFragment = e.Uri.Fragment.Replace("#", "").Trim();
var parameters = HttpUtility.ParseQueryString(clearUriFragment);
Vk.AccessToken = parameters.Get("access_token");
Vk.UserId = parameters.Get("user_id");
if (Vk.AccessToken != null && Vk.UserId != null)
{
webBrowser.Visibility = Visibility.Hidden;
}
}
В первой строке происходит удаление символа «#», который может помешать дальнейшей обработке строки.
HttpUtility.ParseQueryString(clearUriFragment); — разбивает строку на фрагменты.
HttpUtility находится здесь:
System.Web.HttpUtility
В следующих двух строчках происходит попытка получения токена доступа и идентификатора авторизованного пользователя.
Если требуемые данные получены, то происходит сокрытие элемента WebBrowser.
При авторизации, могут появляться разрешающие сообщения, это связато с тем, что WebBrowser работает через установленный Internet Explorer.
Сейчас, если мы запустим приложение и введём данные для входа, то увидим каркас приложения, без каких-либо обработчиков.
Теперь нужно создать классы, для работы с пользователями, группами и пользователями Vk. Зайдём на страницу описания API Vk http://vk.com/page-1_2369282. Нам понадобятся запросы users.get, groups.getById, audio.get. Заходим в каждый из этих методов и копируем пример ответа в формате JSON.
Для user.get пример будет следующим
{"response":[{"uid":"1","first_name":"Павел","last_name":"Дуров",
"photo":"http:\/\/cs109.vkontakte.ru\/u00001\/c_df2abf56.jpg"},
{"uid":"6492","first_name":"Andrew","last_name":"Rogozov",
"photo":"http:\/\/cs537.vkontakte.ru\/u06492\/c_28629f1d.jpg"}]}
Теперь создаём новый класс VkUsers. Заходим в его, удаляем сам класс и делаем следующее, выбираем EDIT – Paste Special – Paste JSON As Classes. Это позволит автоматически создать класс на основе примера JSON ответа, останется только переименовать полученные классы. В итоге получаем классы следующего содержания.
public class VkUsers
{
public UserResponse[] response { get; set; }
}
public class UserResponse
{
public string uid { get; set; }
public string first_name { get; set; }
public string last_name { get; set; }
public string photo { get; set; }
}
Для работы с группами и альбомами поступаем аналогичным образом.
Теперь добавляем в класс MainWindow 2 элемента, для хранения всех музыкальных композиций, и плеер для прослушивания нужной композиции.
public AlbumResponse[] AllComposition { get; private set; }
private readonly MediaPlayer _player = new MediaPlayer();
Теперь добавим функции, осуществляющие загрузки альбомов пользователя либо группы. Функция загрузки альбомов пользователя будет выглядеть следующим образом.
private void LoadUserCompositions(string userId)
{
var str = string.Format("https://api.vk.com/method/users.get?uids={0}",
userId);
var responseText = VkRequest(str);
try
{
var users = JsonConvert.DeserializeObject<VkUsers>(responseText);
var uid = users.response[0].uid;
str = string.Format("https://api.vk.com/method/audio.get?uid={0}&access_token={1}",
uid, Vk.AccessToken);
responseText = VkRequest(str);
var album = JsonConvert.DeserializeObject<VkAlbum>(responseText);
AllComposition = album.response;
albumCompositions.ItemsSource = album.response;
Title = String.Format("Музыка пользователя: <{0} {1}> загружена", users.response[0].first_name,
users.response[0].last_name);
}
catch (Exception) { }
}
Загрузка альбомов из группы будет аналогичной, её можно увидеть в исходниках приложения.
Теперь перейдём к обработчику кнопки «
Загрузить список композиций». Его код будет следующим
if (userName.Text == "" && groupName.Text == "")
{
LoadUserCompositions(Vk.UserId);
return;
}
if (userName.Text != "")
{
LoadUserCompositions(userName.Text);
return;
}
if (groupName.Text != "")
{
LoadGroupCompositions(groupName.Text);
}
Теперь добавим воспроизведение выделенной композиции при нажатии правой клавиши мыши. Добавим обработчик события щелчка правой кнопки мыши на элемент типа «DataGrid». Со следующим кодом.
var selected = (AlbumResponse)albumCompositions.SelectedItem;
if (selected != null)
{
_player.Open(new Uri(selected.url, UriKind.RelativeOrAbsolute));
_player.Play();
compositionName.Text = String.Format("Играет: {0}", selected.title);
stopPlayer.IsEnabled = true;
}
При нажатии кнопки «
Остановить» необходимо выполнить
_player.Stop() и сделать кнопку недоступной для нажатия.
Следующим шагом будет добавление фильтра выводимых песен. Для этого на поле фильтра вешаем событие изменения содержимого с кодом как в примере:
var str = filter.Text.ToLower();
if (AllComposition != null && AllComposition.Any())
{
if (String.IsNullOrEmpty(str))
{
albumCompositions.ItemsSource = AllComposition;
return;
}
albumCompositions.ItemsSource =
AllComposition.Where(
x =>
x.artist.ToLower().IndexOf(str, StringComparison.Ordinal) >= 0 ||
x.title.ToLower().IndexOf(str, StringComparison.Ordinal) > 0);
}
Оно позволит отобразить только те композиции, название которых, либо автор содержат введённый текст.

Осталось реализовать загрузку композиций. Функция загрузки композиции будет выглядеть следующим образом.
private async Task DownloadCompositions(AlbumResponse composition, String path)
{
try
{
if (path[path.Length - 1] != '\\')
{
path = path + "\\";
}
var fileName = composition.title;
if (fileName.Length > 40)
{
fileName = fileName.Substring(0, 40);
}
fileName = fileName.Replace(":", "").Replace("\\", "").Replace("/", "").Replace("*", "").Replace("?", "").Replace("\"", "");
using (var client = new WebClient())
{
client.DownloadProgressChanged += (o, args) =>
{
progressBar.Value = args.ProgressPercentage;
};
client.DownloadFileCompleted += (o, args) =>
{
progressBar.Value = 0;
compositionName.Text = "";
};
compositionName.Text = composition.title;
await client.DownloadFileTaskAsync(new Uri(composition.url), path + fileName + ".mp3");
}
}
catch (Exception)
{
}
}
В параметрах передаются композиция для загрузки и путь для сохранения (каталог). Далее проверяем, содержит ли каталог на конце символ «\», если его нет, то добавляем, это необходимо для формирования корректного пути сохранения композиции. Дальше очищаем название композиции от не валидных символов. Теперь создаём WebClient который будет отвечать за загрузку композиции. Имя композиции делаем не больше 40 символов (если кому-то нравятся длинные файлы, то эту операцию можно вообще убрать). В событии DownloadProgressChanged описываем изменение значения прогресс бара. В событии DownloadFileCompleted действия, которые необходимо выполнить после загрузки композиции. DownloadFileTaskAsync позволяет загружать требуемый файл в асинхронном режиме, для того чтобы функция дождалась завершения загрузки, необходимо прописать await, а саму функцию пометить как async.
Код, который будет скачивать все выделенные композиции будет выглядеть следующим образом.
var selectedBuf = albumCompositions.SelectedItems;
var selected = selectedBuf.Cast<albumresponse>().ToList();
if (selected.Count > 0)
{
var dialog = new CommonOpenFileDialog {IsFolderPicker = true};
var result = dialog.ShowDialog();
if (result == CommonFileDialogResult.Ok)
{
var path = dialog.FileName;
foreach (AlbumResponse composition in selected)
{
await DownloadCompositions(composition, path);
}
}
}
Как можно увидеть, выделенные элементы копируются в новую переменную. Это необходимо для того, чтобы не возникало проблем с повторным выделением при сохранении нескольких композиций. Чтобы работал диалог выбора каталога для сохранения, необходимо добавить пакет «WindowsAPICodePack-Shell» через NuGet. Если каталог был выбран, то происходит скачивание всех выбранных композиций в указанный каталог.
Спасибо всем, кто смог дочитать до конца. Исходный код приложения можно увидеть в репозитории GitHub: https://github.com/flash2048/VkMusicDownload.
При желании программа легко может быть дополнена такими возможностями как:
-
Поиск песен по ключевым словам среди всех композиций Vk.
- Скачивания вашей музыки, музыки друзей и друзей друзей без повторений.
- При наличии огромного желания и ресурсов можно попробовать сказать все композиции вконтакте, разложив их на какие-то условные группы.
При наличии нескольких часов свободного времени обязательно попробуйте поработать с
API Vk, оно оказалось на редкость дружелюбным и понятным…
Если появились какие-то вопросы — не стесняйтесь писать их в комментариях либо личными сообщениями.
Приятного программирования…
Дополнение:
Исправлена ошибка - WebBrouser переименован на WebBrowser
Добавлена возможность перематывания мелодии.
P.S. Целью данной статьи не было создание плеера. Статья носит исключительно обучающий характер.
Если в Вас есть проблемы с запуском кода, можете написать мне, мы свяжемся в Skype и попробуем разобраться.