using System.Security.Cryptography;
using System.Text;
using YandexMusic.API.Models.Common;
using YandexMusic.API.Models.Track;
using YandexMusic.API.Requests.Track;
namespace YandexMusic.API;
/// API для работы с треками (получение, загрузка, метаданные).
public class YTrackAPI : YCommonAPI
{
public YTrackAPI(YandexMusicApi api) : base(api) { }
private static string BuildDownloadLink(YTrackDownloadInfo info, YStorageDownloadFile storageDownload)
{
var path = storageDownload.Path;
var host = storageDownload.Host;
var ts = storageDownload.Ts;
var s = storageDownload.S;
var codec = info.Codec;
var secret = $"XGRlBW9FXlekgbPrRHuSiA{path[1..]}{s}";
var md5Hash = MD5.HashData(Encoding.UTF8.GetBytes(secret));
var hmacsha1 = new HMACSHA1(md5Hash);
var sign = BitConverter.ToString(hmacsha1.ComputeHash(md5Hash)).Replace("-", "").ToLower();
return $"https://{host}/get-{codec}/{sign}/{ts}{path}";
}
public Task GetAsync(string trackId)
=> GetAsync(trackId);
public Task?> GetAsync(IEnumerable trackIds)
=> new YGetTracksBuilder(Api).ExecuteAsync(trackIds);
public Task?> GetMetadataForDownloadAsync(string trackKey, bool direct = false)
=> new YTrackDownloadInfoBuilder(Api).ExecuteAsync((trackKey, direct));
public Task?> GetMetadataForDownloadAsync(YTrack track, bool direct = false)
=> GetMetadataForDownloadAsync(track.GetKey().ToString(), direct);
public Task GetDownloadFileInfoAsync(YTrackDownloadInfo metadataInfo)
=> new YStorageDownloadFileBuilder(Api).ExecuteAsync(metadataInfo.DownloadInfoUrl);
public async Task GetFileLinkAsync(string trackKey)
{
var meta = await GetMetadataForDownloadAsync(trackKey);
var info = meta?.OrderByDescending(i => i.BitrateInKbps).FirstOrDefault(m => m.Codec == "mp3");
if (info == null) return null;
var storageDownload = await GetDownloadFileInfoAsync(info);
if (storageDownload == null) return null;
return BuildDownloadLink(info, storageDownload);
}
public Task GetFileLinkAsync(YTrack track)
=> GetFileLinkAsync(track.GetKey().ToString());
public async Task ExtractToFileAsync(string trackKey, string filePath)
{
var url = await GetFileLinkAsync(trackKey);
if (string.IsNullOrEmpty(url)) throw new Exception("Не удалось получить ссылку на трек");
using var response = await Api.HttpClient.GetAsync(url);
await using var fs = File.Create(filePath);
await response.Content.CopyToAsync(fs);
}
public Task ExtractToFileAsync(YTrack track, string filePath)
=> ExtractToFileAsync(track.GetKey().ToString(), filePath);
public async Task ExtractDataAsync(string trackKey)
{
var url = await GetFileLinkAsync(trackKey);
if (string.IsNullOrEmpty(url)) throw new Exception("Не удалось получить ссылку на трек");
return await Api.HttpClient.GetByteArrayAsync(url);
}
public Task ExtractDataAsync(YTrack track)
=> ExtractDataAsync(track.GetKey().ToString());
public async Task ExtractStreamAsync(string trackKey, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead)
{
var url = await GetFileLinkAsync(trackKey);
if (string.IsNullOrEmpty(url)) throw new Exception("Не удалось получить ссылку на трек");
var response = await Api.HttpClient.GetAsync(url, completionOption);
return await response.Content.ReadAsStreamAsync();
}
public Task ExtractStreamAsync(YTrack track, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead)
=> ExtractStreamAsync(track.GetKey().ToString(), completionOption);
public Task SendPlayTrackInfoAsync(
YTrack track,
string from,
bool fromCache = false,
string playId = "",
string playlistId = "",
double totalPlayedSeconds = 0,
double endPositionSeconds = 0)
=> new YSendTrackInfoBuilder(Api).ExecuteAsync((track, from, fromCache, playId, playlistId, totalPlayedSeconds, endPositionSeconds));
public Task GetSupplementAsync(string trackId)
=> new YGetTrackSupplementBuilder(Api).ExecuteAsync(trackId);
public Task GetSupplementAsync(YTrack track)
=> GetSupplementAsync(track.GetKey().ToString());
public Task GetSimilarAsync(string trackId)
=> new YGetTrackSimilarBuilder(Api).ExecuteAsync(trackId);
public Task GetSimilarAsync(YTrack track)
=> GetSimilarAsync(track.GetKey().ToString());
}