110 lines
4.9 KiB
C#
110 lines
4.9 KiB
C#
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;
|
||
|
||
/// <summary>API для работы с треками (получение, загрузка, метаданные).</summary>
|
||
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 async Task<YTrack?> GetAsync(string trackId)
|
||
=> (await GetAsync([trackId]))?.FirstOrDefault();
|
||
|
||
public Task<List<YTrack>?> GetAsync(IEnumerable<string> trackIds)
|
||
=> new YGetTracksBuilder(Api).ExecuteAsync(trackIds);
|
||
public Task<List<YTrackDownloadInfo>?> GetMetadataForDownloadAsync(string trackKey, bool direct = false)
|
||
=> new YTrackDownloadInfoBuilder(Api).ExecuteAsync((trackKey, direct));
|
||
|
||
public Task<List<YTrackDownloadInfo>?> GetMetadataForDownloadAsync(YTrack track, bool direct = false)
|
||
=> GetMetadataForDownloadAsync(track.GetKey().ToString(), direct);
|
||
|
||
public Task<YStorageDownloadFile?> GetDownloadFileInfoAsync(YTrackDownloadInfo metadataInfo)
|
||
=> new YStorageDownloadFileBuilder(Api).ExecuteAsync(metadataInfo.DownloadInfoUrl);
|
||
|
||
public async Task<string?> 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<string?> 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<byte[]> ExtractDataAsync(string trackKey)
|
||
{
|
||
var url = await GetFileLinkAsync(trackKey);
|
||
if (string.IsNullOrEmpty(url)) throw new Exception("Не удалось получить ссылку на трек");
|
||
return await Api.HttpClient.GetByteArrayAsync(url);
|
||
}
|
||
|
||
public Task<byte[]> ExtractDataAsync(YTrack track)
|
||
=> ExtractDataAsync(track.GetKey().ToString());
|
||
|
||
public async Task<Stream> 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<Stream> ExtractStreamAsync(YTrack track, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead)
|
||
=> ExtractStreamAsync(track.GetKey().ToString(), completionOption);
|
||
|
||
public Task<string?> 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<YTrackSupplement?> GetSupplementAsync(string trackId)
|
||
=> new YGetTrackSupplementBuilder(Api).ExecuteAsync(trackId);
|
||
|
||
public Task<YTrackSupplement?> GetSupplementAsync(YTrack track)
|
||
=> GetSupplementAsync(track.GetKey().ToString());
|
||
|
||
public Task<YTrackSimilar?> GetSimilarAsync(string trackId)
|
||
=> new YGetTrackSimilarBuilder(Api).ExecuteAsync(trackId);
|
||
|
||
public Task<YTrackSimilar?> GetSimilarAsync(YTrack track)
|
||
=> GetSimilarAsync(track.GetKey().ToString());
|
||
} |