Доработан поиск артиста
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using PlaylistShared.Api.Entities;
|
using PlaylistShared.Api.Entities;
|
||||||
|
using PlaylistShared.Api.Extensions;
|
||||||
using PlaylistShared.Api.Services;
|
using PlaylistShared.Api.Services;
|
||||||
using PlaylistShared.Shared;
|
using PlaylistShared.Shared;
|
||||||
using PlaylistShared.Shared.Yandex;
|
using PlaylistShared.Shared.Yandex;
|
||||||
@@ -87,7 +88,13 @@ public class AudioController : ControllerBase
|
|||||||
{
|
{
|
||||||
Title = track.Title,
|
Title = track.Title,
|
||||||
CoverUri = track.CoverUri,
|
CoverUri = track.CoverUri,
|
||||||
Artists = track.Artists.Select(t => t.Name).ToList(),
|
Artists = track.Artists.Select(a => new YandexArtist
|
||||||
|
{
|
||||||
|
Id = a.Id,
|
||||||
|
Name = a.Name,
|
||||||
|
CoverUrl = a.Cover.GetUrl(),
|
||||||
|
Description = a.Description?.Text ?? string.Empty,
|
||||||
|
}).ToList(),
|
||||||
DurationMs = track.DurationMs,
|
DurationMs = track.DurationMs,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,13 +168,19 @@ public class SharedPlaylistController : ControllerBase
|
|||||||
{
|
{
|
||||||
return new YandexPlaylistData
|
return new YandexPlaylistData
|
||||||
{
|
{
|
||||||
Title = playlist.Title ?? "",
|
Title = playlist.Title,
|
||||||
Description = playlist.Description ?? "",
|
Description = playlist.Description,
|
||||||
Tracks = playlist.Tracks?.Select(t => new YandexTrack
|
Tracks = playlist.Tracks.Select(t => new YandexTrack
|
||||||
{
|
{
|
||||||
TrackId = t.Track?.Id ?? "",
|
TrackId = t.Track.Id,
|
||||||
Title = t.Track?.Title ?? "",
|
Title = t.Track.Title,
|
||||||
Artists = t.Track?.Artists?.Select(a => a.Name).ToList() ?? new List<string>(),
|
Artists = t.Track.Artists.Select(t => new YandexArtist()
|
||||||
|
{
|
||||||
|
Id = t.Id,
|
||||||
|
Name = t.Name,
|
||||||
|
CoverUrl = t.Cover.GetUrl(),
|
||||||
|
Description = t.Description?.Text ?? string.Empty,
|
||||||
|
}).ToList(),
|
||||||
DurationMs = (int)(t.Track?.DurationMs ?? 0),
|
DurationMs = (int)(t.Track?.DurationMs ?? 0),
|
||||||
CoverUri = t.Track?.CoverUri ?? ""
|
CoverUri = t.Track?.CoverUri ?? ""
|
||||||
}).ToList() ?? new List<YandexTrack>()
|
}).ToList() ?? new List<YandexTrack>()
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class YandexSearchController : ControllerBase
|
|||||||
public async Task<ActionResult<ApiResponse<YandexSearchResult>>> SearchQuery(
|
public async Task<ActionResult<ApiResponse<YandexSearchResult>>> SearchQuery(
|
||||||
[FromQuery] string query,
|
[FromQuery] string query,
|
||||||
[FromQuery] int limit = 20,
|
[FromQuery] int limit = 20,
|
||||||
[FromQuery] TrackSearchType? searchType = TrackSearchType.All,
|
[FromQuery] TrackSearchType searchType = TrackSearchType.All,
|
||||||
[FromQuery] bool byId = false,
|
[FromQuery] bool byId = false,
|
||||||
[FromQuery] string? shared_id = null)
|
[FromQuery] string? shared_id = null)
|
||||||
{
|
{
|
||||||
@@ -75,7 +75,7 @@ public class YandexSearchController : ControllerBase
|
|||||||
|
|
||||||
if (byId)
|
if (byId)
|
||||||
{
|
{
|
||||||
results = await _yandexService.SearchTracksByIdAsync(user, query, searchType.Value, limit);
|
results = await _yandexService.SearchTracksByIdAsync(user, query, searchType, limit);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.7" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.7" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="10.1.7" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="10.1.7" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="10.1.7" />
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="10.1.7" />
|
||||||
<PackageReference Include="YandexMusic" Version="0.0.7" />
|
<PackageReference Include="YandexMusic" Version="0.0.8" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -137,7 +137,13 @@ public class YandexMusicService
|
|||||||
{
|
{
|
||||||
TrackId = t.Id,
|
TrackId = t.Id,
|
||||||
Title = t.Title,
|
Title = t.Title,
|
||||||
Artists = t.Artists?.Select(a => a.Name).ToList() ?? new List<string>(),
|
Artists = t.Artists.Select(t => new YandexArtist()
|
||||||
|
{
|
||||||
|
Id = t.Id,
|
||||||
|
Name = t.Name,
|
||||||
|
CoverUrl = t.Cover.GetUrl(),
|
||||||
|
Description = t.Description?.Text ?? string.Empty,
|
||||||
|
}).ToList(),
|
||||||
CoverUri = t.CoverUri,
|
CoverUri = t.CoverUri,
|
||||||
DurationMs = t.DurationMs,
|
DurationMs = t.DurationMs,
|
||||||
}).ToList(),
|
}).ToList(),
|
||||||
@@ -149,7 +155,7 @@ public class YandexMusicService
|
|||||||
OwnerUid = p.Owner?.Uid ?? string.Empty,
|
OwnerUid = p.Owner?.Uid ?? string.Empty,
|
||||||
Title = p.Title,
|
Title = p.Title,
|
||||||
Description = p.Description,
|
Description = p.Description,
|
||||||
CoverUrl = p.CoverUri,
|
CoverUrl = string.IsNullOrEmpty(p.CoverUri) ? p.Cover.GetUrl() : p.CoverUri,
|
||||||
TrackCount = p.TrackCount,
|
TrackCount = p.TrackCount,
|
||||||
}).ToList(),
|
}).ToList(),
|
||||||
|
|
||||||
@@ -185,43 +191,114 @@ public class YandexMusicService
|
|||||||
int limit = 20
|
int limit = 20
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
YandexSearchResult result = new();
|
||||||
|
|
||||||
var client = await CreateClientAsync(user);
|
var client = await CreateClientAsync(user);
|
||||||
if (client == null) return new YandexSearchResult();
|
if (client == null) return result;
|
||||||
|
|
||||||
var ySerchType = searchType switch
|
if (searchType == TrackSearchType.All)
|
||||||
{
|
{
|
||||||
TrackSearchType.Artist => YandexMusic.API.Models.Common.YSearchType.Artist,
|
throw new Exception("Для поиска по ID необходимо указать конкретный тип (трек, альбом, исполнитель или плейлист).");
|
||||||
TrackSearchType.Album => YandexMusic.API.Models.Common.YSearchType.Album,
|
|
||||||
TrackSearchType.Playlist => YandexMusic.API.Models.Common.YSearchType.Playlist,
|
|
||||||
TrackSearchType.Track => YandexMusic.API.Models.Common.YSearchType.Track,
|
|
||||||
_ => YandexMusic.API.Models.Common.YSearchType.All
|
|
||||||
};
|
|
||||||
|
|
||||||
IEnumerable<YTrack> searchResult = searchType switch
|
|
||||||
{
|
|
||||||
TrackSearchType.Playlist => (await client.GetPlaylistAsync(id)).Tracks.Select(t => t.Track),
|
|
||||||
TrackSearchType.Track => (await client.GetTracksAsync([id])),
|
|
||||||
TrackSearchType.Album => (await client.GetAlbumAsync(id)).Volumes.SelectMany(t => t),
|
|
||||||
TrackSearchType.Artist => (await client.GetArtistAsync(id)).Albums.SelectMany(t => t.Volumes.SelectMany(v => v)),
|
|
||||||
_ => new List<YTrack>()
|
|
||||||
};
|
|
||||||
|
|
||||||
if (searchType != TrackSearchType.Track)
|
|
||||||
{
|
|
||||||
searchResult = searchResult.Distinct();
|
|
||||||
if (limit > 0) searchResult = searchResult.Take(limit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new YandexSearchResult()
|
else if (searchType == TrackSearchType.Track)
|
||||||
{
|
{
|
||||||
Tracks = searchResult.Select(t => new YandexTrack
|
var track = await client.GetTrackAsync(id);
|
||||||
|
|
||||||
|
if (track != null)
|
||||||
{
|
{
|
||||||
TrackId = t.Id,
|
result.Tracks = new List<YandexTrack>()
|
||||||
Title = t.Title,
|
{
|
||||||
Artists = t.Artists?.Select(a => a.Name).ToList() ?? new List<string>(),
|
new()
|
||||||
CoverUri = t.CoverUri ?? string.Empty,
|
{
|
||||||
DurationMs = t.DurationMs,
|
TrackId = track.Id,
|
||||||
}).ToList(),
|
Title = track.Title,
|
||||||
};
|
Artists = track.Artists.Select(t => new YandexArtist()
|
||||||
|
{
|
||||||
|
Id = t.Id,
|
||||||
|
Name = t.Name,
|
||||||
|
CoverUrl = t.Cover.GetUrl(),
|
||||||
|
Description = t.Description?.Text ?? string.Empty,
|
||||||
|
}).ToList(),
|
||||||
|
CoverUri = track.CoverUri ?? string.Empty,
|
||||||
|
DurationMs = track.DurationMs,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (searchType == TrackSearchType.Album)
|
||||||
|
{
|
||||||
|
var album = await client.GetAlbumAsync(id);
|
||||||
|
if (album != null)
|
||||||
|
{
|
||||||
|
result.Tracks = album.Volumes.SelectMany(v => v).Select(t => new YandexTrack
|
||||||
|
{
|
||||||
|
TrackId = t.Id,
|
||||||
|
Title = t.Title,
|
||||||
|
Artists = t.Artists.Select(t => new YandexArtist()
|
||||||
|
{
|
||||||
|
Id = t.Id,
|
||||||
|
Name = t.Name,
|
||||||
|
CoverUrl = t.Cover.GetUrl(),
|
||||||
|
Description = t.Description?.Text ?? string.Empty,
|
||||||
|
}).ToList(),
|
||||||
|
CoverUri = t.CoverUri ?? string.Empty,
|
||||||
|
DurationMs = t.DurationMs,
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (searchType == TrackSearchType.Artist)
|
||||||
|
{
|
||||||
|
var artist = await client.GetArtistAsync(id);
|
||||||
|
if (artist != null)
|
||||||
|
{
|
||||||
|
result.Albums = artist.Albums.Select(a => new YandexAlbum()
|
||||||
|
{
|
||||||
|
Id = a.Id,
|
||||||
|
Title = a.Title,
|
||||||
|
Artists = a.Artists.Select(t => new YandexArtist()
|
||||||
|
{
|
||||||
|
Id = t.Id,
|
||||||
|
Name = t.Name,
|
||||||
|
CoverUrl = t.Cover.GetUrl(),
|
||||||
|
Description = t.Description?.Text ?? string.Empty,
|
||||||
|
}).ToList(),
|
||||||
|
CoverUrl = string.IsNullOrEmpty(a.CoverUri) ? a.Cover.GetUrl() : a.CoverUri,
|
||||||
|
Description = a.Description,
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
result.Playlists = artist.Playlists.Select(p => new YandexPlaylist
|
||||||
|
{
|
||||||
|
Uuid = p.PlaylistUuid,
|
||||||
|
Kind = p.Kind,
|
||||||
|
OwnerUid = p.Owner?.Uid ?? string.Empty,
|
||||||
|
Title = p.Title,
|
||||||
|
Description = p.Description,
|
||||||
|
CoverUrl = p.Cover.GetUrl(),
|
||||||
|
TrackCount = p.TrackCount,
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
result.Tracks = artist.PopularTracks.Select(t => new YandexTrack
|
||||||
|
{
|
||||||
|
TrackId = t.Id,
|
||||||
|
Title = t.Title,
|
||||||
|
Artists = t.Artists.Select(a => new YandexArtist()
|
||||||
|
{
|
||||||
|
Id = a.Id,
|
||||||
|
Name = a.Name,
|
||||||
|
CoverUrl = a.Cover.GetUrl(),
|
||||||
|
Description = a.Description?.Text ?? string.Empty,
|
||||||
|
}).ToList(),
|
||||||
|
CoverUri = t.CoverUri ?? string.Empty,
|
||||||
|
DurationMs = t.DurationMs,
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
@if (CanPlay && (_isHovered || IsCurrentTrackPlaying))
|
@if (CanPlay && (_isHovered || IsCurrentTrackPlaying))
|
||||||
{
|
{
|
||||||
<MudItem class="play-overlay"
|
<MudItem class="play-overlay"
|
||||||
style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.6); display: flex; align-items: center; justify-content: center; border-radius: 4px;">
|
style="position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.6); display: flex; align-items: center; justify-content: center; border-radius: 4px; transition: opacity 0.2s ease; cursor: pointer;">
|
||||||
<MudIconButton Icon="@(IsCurrentTrackPlaying? Icons.Material.Filled.Pause : Icons.Material.Filled.PlayArrow)"
|
<MudIconButton Icon="@(IsCurrentTrackPlaying? Icons.Material.Filled.Pause : Icons.Material.Filled.PlayArrow)"
|
||||||
Color="Color.Inherit"
|
Color="Color.Inherit"
|
||||||
Size="Size.Large"
|
Size="Size.Large"
|
||||||
|
|||||||
@@ -40,14 +40,14 @@
|
|||||||
{
|
{
|
||||||
<MudExpansionPanels>
|
<MudExpansionPanels>
|
||||||
@* Секция исполнителей *@
|
@* Секция исполнителей *@
|
||||||
@if (ShouldShowSection(TrackSearchType.Artist))
|
@if (_searchResult?.Artists != null)
|
||||||
{
|
{
|
||||||
<MudExpansionPanel Text="Исполнители" Expanded="true">
|
<MudExpansionPanel Text="Исполнители" Expanded="true">
|
||||||
<MudGrid>
|
<MudGrid>
|
||||||
@foreach (var artist in _searchResult.Artists!)
|
@foreach (var artist in _searchResult.Artists)
|
||||||
{
|
{
|
||||||
<MudItem xs="12" sm="6" md="4" lg="3">
|
<MudItem xs="12" sm="6" md="3" lg="2">
|
||||||
<ArtistCard Item="artist" OnClick="() => SearchTracksByEntity(artist.Id, TrackSearchType.Artist)" />
|
<ArtistCard Item="artist" OnClick="() => SearchTracksByEntity(artist.Id, artist.Name, TrackSearchType.Artist)" />
|
||||||
</MudItem>
|
</MudItem>
|
||||||
}
|
}
|
||||||
</MudGrid>
|
</MudGrid>
|
||||||
@@ -55,14 +55,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@* Секция альбомов *@
|
@* Секция альбомов *@
|
||||||
@if (ShouldShowSection(TrackSearchType.Album))
|
@if (_searchResult?.Albums != null)
|
||||||
{
|
{
|
||||||
<MudExpansionPanel Text="Альбомы" Expanded="true">
|
<MudExpansionPanel Text="Альбомы" Expanded="true">
|
||||||
<MudGrid>
|
<MudGrid>
|
||||||
@foreach (var album in _searchResult.Albums!)
|
@foreach (var album in _searchResult.Albums)
|
||||||
{
|
{
|
||||||
<MudItem xs="12" sm="6" md="4" lg="3">
|
<MudItem xs="12" sm="6" md="3" lg="2">
|
||||||
<AddTrackSection Item="album" OnClick="() => SearchTracksByEntity(album.Id, TrackSearchType.Album)" />
|
<AlbumCard Item="album" OnClick="() => SearchTracksByEntity(album.Id, album.Title, TrackSearchType.Album)" />
|
||||||
</MudItem>
|
</MudItem>
|
||||||
}
|
}
|
||||||
</MudGrid>
|
</MudGrid>
|
||||||
@@ -70,14 +70,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@* Секция плейлистов *@
|
@* Секция плейлистов *@
|
||||||
@if (ShouldShowSection(TrackSearchType.Playlist))
|
@if (_searchResult?.Playlists != null)
|
||||||
{
|
{
|
||||||
<MudExpansionPanel Text="Плейлисты" Expanded="true">
|
<MudExpansionPanel Text="Плейлисты" Expanded="true">
|
||||||
<MudGrid>
|
<MudGrid>
|
||||||
@foreach (var playlist in _searchResult.Playlists!)
|
@foreach (var playlist in _searchResult.Playlists)
|
||||||
{
|
{
|
||||||
<MudItem xs="12" sm="6" md="4" lg="3">
|
<MudItem xs="12" sm="6" md="3" lg="2">
|
||||||
<PlaylistCard Item="playlist" OnClick="() => SearchTracksByEntity(playlist.Kind, TrackSearchType.Playlist)" />
|
<PlaylistCard Item="playlist" OnClick="() => SearchTracksByEntity(playlist.Uuid, playlist.Title, TrackSearchType.Playlist)" />
|
||||||
</MudItem>
|
</MudItem>
|
||||||
}
|
}
|
||||||
</MudGrid>
|
</MudGrid>
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@* Секция треков *@
|
@* Секция треков *@
|
||||||
@if (ShouldShowSection(TrackSearchType.Track))
|
@if (_searchResult?.Tracks != null)
|
||||||
{
|
{
|
||||||
<MudExpansionPanel Text="Треки" Expanded="true">
|
<MudExpansionPanel Text="Треки" Expanded="true">
|
||||||
<MudTable Items="@_searchResult.Tracks"
|
<MudTable Items="@_searchResult.Tracks"
|
||||||
@@ -123,22 +123,9 @@
|
|||||||
|
|
||||||
private string _searchQuery = "";
|
private string _searchQuery = "";
|
||||||
private bool _isSearching = false;
|
private bool _isSearching = false;
|
||||||
private bool _isFirstSearch = true;
|
|
||||||
private TrackSearchType _searchType = TrackSearchType.All;
|
private TrackSearchType _searchType = TrackSearchType.All;
|
||||||
private YandexSearchResult? _searchResult = null;
|
private YandexSearchResult? _searchResult = null;
|
||||||
|
|
||||||
private bool ShouldShowSection(TrackSearchType sectionType)
|
|
||||||
{
|
|
||||||
return sectionType switch
|
|
||||||
{
|
|
||||||
TrackSearchType.Track => _searchResult?.Tracks?.Any() == true,
|
|
||||||
TrackSearchType.Album => _searchResult?.Albums?.Any() == true,
|
|
||||||
TrackSearchType.Playlist => _searchResult?.Playlists?.Any() == true,
|
|
||||||
TrackSearchType.Artist => _searchResult?.Artists?.Any() == true,
|
|
||||||
_ => false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task OnSearchQueryChanged()
|
private async Task OnSearchQueryChanged()
|
||||||
{
|
{
|
||||||
await SearchTracks(byId: false);
|
await SearchTracks(byId: false);
|
||||||
@@ -155,7 +142,6 @@
|
|||||||
if (string.IsNullOrWhiteSpace(query))
|
if (string.IsNullOrWhiteSpace(query))
|
||||||
{
|
{
|
||||||
_searchResult = null;
|
_searchResult = null;
|
||||||
_isFirstSearch = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +162,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_isFirstSearch = false;
|
|
||||||
_isSearching = true;
|
_isSearching = true;
|
||||||
_searchResult = null;
|
_searchResult = null;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
@@ -212,10 +197,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SearchTracksByEntity(string entityId, TrackSearchType entityType)
|
private async Task SearchTracksByEntity(string entityId, string title, TrackSearchType entityType)
|
||||||
{
|
{
|
||||||
// Переключаем тип на треки и ищем по ID
|
// Переключаем тип и ищем по ID
|
||||||
_searchType = TrackSearchType.Track;
|
_searchType = entityType;
|
||||||
|
_searchQuery = title;
|
||||||
await SearchTracks(byId: true, forcedQuery: entityId);
|
await SearchTracks(byId: true, forcedQuery: entityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
@using PlaylistShared.Shared.Yandex
|
@using PlaylistShared.Shared.Yandex
|
||||||
|
|
||||||
<MudPaper Class="d-flex flex-column align-center pa-2 cursor-pointer" Elevation="0" OnClick="OnClick.InvokeAsync">
|
<MudItem Class="d-flex flex-column align-center pa-2 cursor-pointer" Elevation="0" @onclick="HandleClick">
|
||||||
@if (!string.IsNullOrEmpty(Item.CoverUrl))
|
@if (!string.IsNullOrEmpty(Item.CoverUrl))
|
||||||
{
|
{
|
||||||
<MudAvatar Image="@Item.CoverUrl.FormatCoverUrl(Size, Size)" Size="MudBlazor.Size.Large" />
|
<MudAvatar Size="MudBlazor.Size.Large">
|
||||||
|
<MudImage Src="@Item.CoverUrl.FormatCoverUrl(Size, Size)" />
|
||||||
|
</MudAvatar>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -15,10 +17,18 @@
|
|||||||
<MudText Typo="Typo.caption" Align="Align.Center" Color="Color.Secondary">
|
<MudText Typo="Typo.caption" Align="Align.Center" Color="Color.Secondary">
|
||||||
@string.Join(", ", Item.Artists.Select(a => a.Name))
|
@string.Join(", ", Item.Artists.Select(a => a.Name))
|
||||||
</MudText>
|
</MudText>
|
||||||
</MudPaper>
|
</MudItem>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter] public YandexAlbum Item { get; set; } = null!;
|
[Parameter] public YandexAlbum Item { get; set; } = null!;
|
||||||
[Parameter] public EventCallback OnClick { get; set; }
|
[Parameter] public EventCallback OnClick { get; set; }
|
||||||
[Parameter] public int Size { get; set; } = 50;
|
[Parameter] public int Size { get; set; } = 50;
|
||||||
|
|
||||||
|
private async Task HandleClick()
|
||||||
|
{
|
||||||
|
if (OnClick.HasDelegate)
|
||||||
|
{
|
||||||
|
await OnClick.InvokeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
@using PlaylistShared.Shared.Yandex
|
@using PlaylistShared.Shared.Yandex
|
||||||
|
|
||||||
<MudPaper Class="d-flex flex-column align-center pa-2 cursor-pointer" Elevation="0" OnClick="OnClick.InvokeAsync">
|
<MudItem Class="d-flex flex-column align-center pa-2 cursor-pointer" @onclick="HandleClick">
|
||||||
@if (!string.IsNullOrEmpty(Item.CoverUrl))
|
@if (!string.IsNullOrEmpty(Item.CoverUrl))
|
||||||
{
|
{
|
||||||
<MudAvatar Image="@Item.CoverUrl.FormatCoverUrl(Size, Size)" Size="MudBlazor.Size.Large" />
|
<MudAvatar Size="MudBlazor.Size.Large">
|
||||||
|
<MudImage Src="@Item.CoverUrl.FormatCoverUrl(Size, Size)" />
|
||||||
|
</MudAvatar>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -12,10 +14,18 @@
|
|||||||
</MudAvatar>
|
</MudAvatar>
|
||||||
}
|
}
|
||||||
<MudText Typo="Typo.body2" Align="Align.Center" Class="mt-2">@Item.Name</MudText>
|
<MudText Typo="Typo.body2" Align="Align.Center" Class="mt-2">@Item.Name</MudText>
|
||||||
</MudPaper>
|
</MudItem>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter] public YandexArtist Item { get; set; } = null!;
|
[Parameter] public YandexArtist Item { get; set; } = null!;
|
||||||
[Parameter] public EventCallback OnClick { get; set; }
|
[Parameter] public EventCallback OnClick { get; set; }
|
||||||
[Parameter] public int Size { get; set; } = 50;
|
[Parameter] public int Size { get; set; } = 50;
|
||||||
|
|
||||||
|
private async Task HandleClick()
|
||||||
|
{
|
||||||
|
if (OnClick.HasDelegate)
|
||||||
|
{
|
||||||
|
await OnClick.InvokeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
@using PlaylistShared.Shared.Yandex
|
@using PlaylistShared.Shared.Yandex
|
||||||
|
|
||||||
<MudPaper Class="d-flex flex-column align-center pa-2 cursor-pointer" Elevation="0" OnClick="OnClick.InvokeAsync">
|
<MudItem Class="d-flex flex-column align-center pa-2 cursor-pointer" Elevation="0" onclick="HandleClick">
|
||||||
@if (!string.IsNullOrEmpty(Item.CoverUrl))
|
@if (!string.IsNullOrEmpty(Item.CoverUrl))
|
||||||
{
|
{
|
||||||
<MudAvatar Image="@Item.CoverUrl.FormatCoverUrl(Size, Size)" Size="MudBlazor.Size.Large" />
|
<MudAvatar Size="MudBlazor.Size.Large">
|
||||||
|
<MudImage Src="@Item.CoverUrl.FormatCoverUrl(Size, Size)" />
|
||||||
|
</MudAvatar>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -12,10 +14,18 @@
|
|||||||
</MudAvatar>
|
</MudAvatar>
|
||||||
}
|
}
|
||||||
<MudText Typo="Typo.body2" Align="Align.Center" Class="mt-2">@Item.Title</MudText>
|
<MudText Typo="Typo.body2" Align="Align.Center" Class="mt-2">@Item.Title</MudText>
|
||||||
</MudPaper>
|
</MudItem>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter] public YandexPlaylist Item { get; set; } = null!;
|
[Parameter] public YandexPlaylist Item { get; set; } = null!;
|
||||||
[Parameter] public EventCallback OnClick { get; set; }
|
[Parameter] public EventCallback OnClick { get; set; }
|
||||||
[Parameter] public int Size { get; set; } = 50;
|
[Parameter] public int Size { get; set; } = 50;
|
||||||
|
|
||||||
|
private async Task HandleClick()
|
||||||
|
{
|
||||||
|
if (OnClick.HasDelegate)
|
||||||
|
{
|
||||||
|
await OnClick.InvokeAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -109,41 +109,19 @@ code {
|
|||||||
text-align: start;
|
text-align: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-cover-container {
|
/* Горизонтальный скролинг */
|
||||||
border-radius: 4px;
|
.horizontal-scroll {
|
||||||
overflow: hidden;
|
overflow-x: auto;
|
||||||
transition: transform 0.2s ease;
|
scroll-snap-type: x mandatory;
|
||||||
|
overflow-y: hidden; /* отключаем вертикальный скролл */
|
||||||
|
cursor: grab;
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-cover-container:hover {
|
.horizontal-scroll:active {
|
||||||
transform: scale(1.05);
|
cursor: grabbing;
|
||||||
}
|
|
||||||
|
|
||||||
.play-overlay {
|
|
||||||
transition: opacity 0.2s ease;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Фиксированный плеер внизу */
|
|
||||||
.fixed-player {
|
|
||||||
position: sticky;
|
|
||||||
display: flex;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
right: 0;
|
|
||||||
justify-content: center;
|
|
||||||
background-color: var(--mud-palette-background);
|
|
||||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Отступ снизу, когда плеер виден */
|
|
||||||
.page-with-player {
|
|
||||||
padding-bottom: 80px; /* Высота плеера (подберите под свою тему) */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* На мобильных устройствах можно уменьшить отступ */
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
.page-with-player {
|
|
||||||
padding-bottom: 100px; /* если плеер выше на мобильных */
|
|
||||||
}
|
}
|
||||||
|
/* Для WebKit (Chrome, Edge, Safari) можно включить горизонтальный скролл мышью */
|
||||||
|
.horizontal-scroll {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@ public class YandexTrack
|
|||||||
public string Title { get; set; } = string.Empty;
|
public string Title { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonPropertyName("artists")]
|
[JsonPropertyName("artists")]
|
||||||
public List<string> Artists { get; set; } = new();
|
public List<YandexArtist> Artists { get; set; } = new();
|
||||||
|
|
||||||
[JsonPropertyName("coverUri")]
|
[JsonPropertyName("coverUri")]
|
||||||
public string CoverUri { get; set; } = string.Empty;
|
public string CoverUri { get; set; } = string.Empty;
|
||||||
|
|||||||
Reference in New Issue
Block a user