Добавлены права на воспроизведение.
This commit is contained in:
@@ -13,15 +13,18 @@ public class AudioController : ControllerBase
|
|||||||
{
|
{
|
||||||
private readonly UserManager<ApplicationUser> _userManager;
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
private readonly YandexMusicService _yandexService;
|
private readonly YandexMusicService _yandexService;
|
||||||
|
private readonly SharedPlaylistService _sharedService;
|
||||||
private readonly JwtService _jwtService;
|
private readonly JwtService _jwtService;
|
||||||
|
|
||||||
public AudioController(
|
public AudioController(
|
||||||
UserManager<ApplicationUser> userManager,
|
UserManager<ApplicationUser> userManager,
|
||||||
YandexMusicService yandexService,
|
YandexMusicService yandexService,
|
||||||
|
SharedPlaylistService sharedService,
|
||||||
JwtService jwtService)
|
JwtService jwtService)
|
||||||
{
|
{
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_yandexService = yandexService;
|
_yandexService = yandexService;
|
||||||
|
_sharedService = sharedService;
|
||||||
_jwtService = jwtService;
|
_jwtService = jwtService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,17 +32,18 @@ public class AudioController : ControllerBase
|
|||||||
/// Потоковое воспроизведение трека из Яндекс.Музыки.
|
/// Потоковое воспроизведение трека из Яндекс.Музыки.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="trackId">ID трека (например, "21696942").</param>
|
/// <param name="trackId">ID трека (например, "21696942").</param>
|
||||||
|
/// <param name="access_token">gwt пользователя</param>
|
||||||
|
/// <param name="shared_id">ID расшаренного плейлиста</param>
|
||||||
[HttpGet("track/{trackId}")]
|
[HttpGet("track/{trackId}")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public async Task<IActionResult> StreamTrack(string trackId, [FromQuery] string? access_token = null)
|
public async Task<IActionResult> StreamTrack(string trackId, [FromQuery] string? access_token = null, [FromQuery] string? shared_id = null)
|
||||||
{
|
{
|
||||||
var user = await GetUserFromToken(access_token);
|
var user = await GetUserFromToken(access_token);
|
||||||
if (user == null)
|
if (user == null) user = await GetUserFromSharedPlaylistId(shared_id);
|
||||||
return Unauthorized();
|
if (user == null) return Unauthorized();
|
||||||
|
|
||||||
var streamUrl = await _yandexService.GetTrackFileUrlAsync(user, trackId);
|
var streamUrl = await _yandexService.GetTrackFileUrlAsync(user, trackId);
|
||||||
if (string.IsNullOrEmpty(streamUrl))
|
if (string.IsNullOrEmpty(streamUrl)) return NotFound();
|
||||||
return NotFound();
|
|
||||||
|
|
||||||
var httpClient = new HttpClient();
|
var httpClient = new HttpClient();
|
||||||
var request = new HttpRequestMessage(HttpMethod.Get, streamUrl);
|
var request = new HttpRequestMessage(HttpMethod.Get, streamUrl);
|
||||||
@@ -56,11 +60,11 @@ public class AudioController : ControllerBase
|
|||||||
Response.ContentType = response.Content.Headers.ContentType?.ToString() ?? "audio/mpeg";
|
Response.ContentType = response.Content.Headers.ContentType?.ToString() ?? "audio/mpeg";
|
||||||
|
|
||||||
if (response.Content.Headers.Contains("Content-Range"))
|
if (response.Content.Headers.Contains("Content-Range"))
|
||||||
Response.Headers.Add("Content-Range", response.Content.Headers.ContentRange?.ToString());
|
Response.Headers.Append("Content-Range", response.Content.Headers.ContentRange?.ToString());
|
||||||
if (response.Headers.Contains("Accept-Ranges"))
|
if (response.Headers.Contains("Accept-Ranges"))
|
||||||
Response.Headers.Add("Accept-Ranges", response.Headers.AcceptRanges?.ToString());
|
Response.Headers.Append("Accept-Ranges", response.Headers.AcceptRanges?.ToString());
|
||||||
if (response.Content.Headers.Contains("Content-Length"))
|
if (response.Content.Headers.Contains("Content-Length"))
|
||||||
Response.Headers.Add("Content-Length", response.Content.Headers.ContentLength?.ToString());
|
Response.Headers.Append("Content-Length", response.Content.Headers.ContentLength?.ToString());
|
||||||
|
|
||||||
await response.Content.CopyToAsync(Response.Body);
|
await response.Content.CopyToAsync(Response.Body);
|
||||||
return new EmptyResult();
|
return new EmptyResult();
|
||||||
@@ -68,17 +72,28 @@ public class AudioController : ControllerBase
|
|||||||
|
|
||||||
private async Task<ApplicationUser?> GetUserFromToken(string? token)
|
private async Task<ApplicationUser?> GetUserFromToken(string? token)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(token))
|
if (string.IsNullOrEmpty(token)) return null;
|
||||||
return null;
|
|
||||||
|
|
||||||
var principal = _jwtService.ValidateToken(token);
|
var principal = _jwtService.ValidateToken(token);
|
||||||
if (principal == null)
|
if (principal == null) return null;
|
||||||
return null;
|
|
||||||
|
|
||||||
var userId = principal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
var userId = principal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
|
||||||
if (string.IsNullOrEmpty(userId))
|
if (string.IsNullOrEmpty(userId)) return null;
|
||||||
return null;
|
|
||||||
|
|
||||||
return await _userManager.FindByIdAsync(userId);
|
return await _userManager.FindByIdAsync(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<ApplicationUser?> GetUserFromSharedPlaylistId(string? sharedId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(sharedId)) return null;
|
||||||
|
|
||||||
|
var playlist = await _sharedService.GetEntityByTokenAsync(sharedId);
|
||||||
|
|
||||||
|
if (playlist == null) return null;
|
||||||
|
|
||||||
|
if (!await _sharedService.CanPlayEveryoneAsync(playlist)) return null;
|
||||||
|
|
||||||
|
return await _userManager.FindByIdAsync(playlist.CreatorUserId.ToString());
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -87,8 +87,9 @@ public class PlaylistsController : ControllerBase
|
|||||||
Title = playlist.Title,
|
Title = playlist.Title,
|
||||||
Description = playlist.Description,
|
Description = playlist.Description,
|
||||||
ViewPermission = ViewPermission.Everyone,
|
ViewPermission = ViewPermission.Everyone,
|
||||||
|
PlayPermission = ViewPermission.Everyone,
|
||||||
AddPermission = EditPermission.AuthorizedOnly,
|
AddPermission = EditPermission.AuthorizedOnly,
|
||||||
RemovePermission = EditPermission.AddedByUserOnly
|
RemovePermission = EditPermission.AddedByUserOnly,
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = await _sharedService.CreateAsync(userId, dto);
|
var result = await _sharedService.CreateAsync(userId, dto);
|
||||||
|
|||||||
547
PlaylistShared.Api/Data/Migrations/20260414094124_AddSharedPermissions.Designer.cs
generated
Normal file
547
PlaylistShared.Api/Data/Migrations/20260414094124_AddSharedPermissions.Designer.cs
generated
Normal file
@@ -0,0 +1,547 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using PlaylistShared.Api.Data;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace PlaylistShared.Api.Data.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
[Migration("20260414094124_AddSharedPermissions")]
|
||||||
|
partial class AddSharedPermissions
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.5")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||||
|
|
||||||
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("RoleNameIndex")
|
||||||
|
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<Guid>("RoleId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("RoleId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken()
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||||
|
.HasColumnType("datetimeoffset");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("RefreshToken")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RefreshTokenExpiryUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("nvarchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("YandexAccessToken")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("YandexId")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<string>("YandexRefreshToken")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("YandexTokenExpiryUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasDatabaseName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("UserNameIndex")
|
||||||
|
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylist", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<int>("AddPermission")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("CoverUrl")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid>("CreatorUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsDeleted")
|
||||||
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<int>("PlayPermission")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("RemovePermission")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ShareToken")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(255)
|
||||||
|
.HasColumnType("nvarchar(255)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<int>("ViewPermission")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("YandexPlaylistKind")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.Property<string>("YandexPlaylistOwnerUid")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatorUserId");
|
||||||
|
|
||||||
|
b.HasIndex("ShareToken")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("SharedPlaylists");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.TrackAdditionLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAtUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("AddedByUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("SessionId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(449)");
|
||||||
|
|
||||||
|
b.Property<Guid>("SharedPlaylistId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("TrackId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AddedByUserId");
|
||||||
|
|
||||||
|
b.HasIndex("SessionId");
|
||||||
|
|
||||||
|
b.HasIndex("SharedPlaylistId", "TrackId");
|
||||||
|
|
||||||
|
b.ToTable("TrackAdditionLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("SessionId")
|
||||||
|
.HasMaxLength(449)
|
||||||
|
.HasColumnType("nvarchar(449)");
|
||||||
|
|
||||||
|
b.Property<Guid?>("AssociatedUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("ClientIpAddress")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("FirstSeenUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<string>("UserAgent")
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
|
b.HasKey("SessionId");
|
||||||
|
|
||||||
|
b.HasIndex("AssociatedUserId");
|
||||||
|
|
||||||
|
b.ToTable("UserSessions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TrackRemovalLog", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<DateTime>("RemovedAtUtc")
|
||||||
|
.HasColumnType("datetime2");
|
||||||
|
|
||||||
|
b.Property<Guid?>("RemovedByUserId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("SessionId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(449)");
|
||||||
|
|
||||||
|
b.Property<Guid>("SharedPlaylistId")
|
||||||
|
.HasColumnType("uniqueidentifier");
|
||||||
|
|
||||||
|
b.Property<string>("TrackId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("nvarchar(450)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RemovedByUserId");
|
||||||
|
|
||||||
|
b.HasIndex("SessionId");
|
||||||
|
|
||||||
|
b.HasIndex("SharedPlaylistId", "TrackId");
|
||||||
|
|
||||||
|
b.ToTable("TrackRemovalLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole<System.Guid>", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<System.Guid>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", null)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylist", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "Creator")
|
||||||
|
.WithMany("OwnedPlaylists")
|
||||||
|
.HasForeignKey("CreatorUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Creator");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.TrackAdditionLog", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "AddedByUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AddedByUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.UserSession", "Session")
|
||||||
|
.WithMany("TrackAdditionLogs")
|
||||||
|
.HasForeignKey("SessionId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.SharedPlaylist", "SharedPlaylist")
|
||||||
|
.WithMany("TrackAdditionLogs")
|
||||||
|
.HasForeignKey("SharedPlaylistId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("AddedByUser");
|
||||||
|
|
||||||
|
b.Navigation("Session");
|
||||||
|
|
||||||
|
b.Navigation("SharedPlaylist");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "User")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AssociatedUserId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("User");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("TrackRemovalLog", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.ApplicationUser", "RemovedByUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RemovedByUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict);
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.UserSession", "Session")
|
||||||
|
.WithMany("TrackRemovalLogs")
|
||||||
|
.HasForeignKey("SessionId")
|
||||||
|
.OnDelete(DeleteBehavior.Restrict)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("PlaylistShared.Api.Entities.SharedPlaylist", "SharedPlaylist")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("SharedPlaylistId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("RemovedByUser");
|
||||||
|
|
||||||
|
b.Navigation("Session");
|
||||||
|
|
||||||
|
b.Navigation("SharedPlaylist");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("OwnedPlaylists");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.SharedPlaylist", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("TrackAdditionLogs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("PlaylistShared.Api.Entities.UserSession", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("TrackAdditionLogs");
|
||||||
|
|
||||||
|
b.Navigation("TrackRemovalLogs");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace PlaylistShared.Api.Data.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddSharedPermissions : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "PlayPermission",
|
||||||
|
table: "SharedPlaylists",
|
||||||
|
type: "int",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "PlayPermission",
|
||||||
|
table: "SharedPlaylists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -261,6 +261,9 @@ namespace PlaylistShared.Api.Data.Migrations
|
|||||||
b.Property<bool>("IsDeleted")
|
b.Property<bool>("IsDeleted")
|
||||||
.HasColumnType("bit");
|
.HasColumnType("bit");
|
||||||
|
|
||||||
|
b.Property<int>("PlayPermission")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
b.Property<int>("RemovePermission")
|
b.Property<int>("RemovePermission")
|
||||||
.HasColumnType("int");
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ public class SharedPlaylist
|
|||||||
public bool IsDeleted { get; set; }
|
public bool IsDeleted { get; set; }
|
||||||
public string ShareToken { get; set; } = null!;
|
public string ShareToken { get; set; } = null!;
|
||||||
public ViewPermission ViewPermission { get; set; }
|
public ViewPermission ViewPermission { get; set; }
|
||||||
|
public ViewPermission PlayPermission { get; set; }
|
||||||
public EditPermission AddPermission { get; set; }
|
public EditPermission AddPermission { get; set; }
|
||||||
public EditPermission RemovePermission { get; set; }
|
public EditPermission RemovePermission { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ public class Program
|
|||||||
|
|
||||||
// DbContext
|
// DbContext
|
||||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||||
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));// Identity
|
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
|
||||||
|
// Identity
|
||||||
builder.Services.AddIdentity<ApplicationUser, IdentityRole<Guid>>(options =>
|
builder.Services.AddIdentity<ApplicationUser, IdentityRole<Guid>>(options =>
|
||||||
{
|
{
|
||||||
options.User.RequireUniqueEmail = true;
|
options.User.RequireUniqueEmail = true;
|
||||||
@@ -101,17 +102,9 @@ public class Program
|
|||||||
|
|
||||||
builder.Services.AddCors(options =>
|
builder.Services.AddCors(options =>
|
||||||
{
|
{
|
||||||
options.AddPolicy("Development", policy =>
|
|
||||||
{
|
|
||||||
policy.WithOrigins("http://localhost:5053", "https://localhost:7225", "http://localhost:5181", "https://api.playlistshare.frigat.duckdns.org", "https://playlistshare.frigat.duckdns.org")
|
|
||||||
.AllowAnyMethod()
|
|
||||||
.AllowAnyHeader()
|
|
||||||
.AllowCredentials();
|
|
||||||
});
|
|
||||||
|
|
||||||
options.AddPolicy("Production", policy =>
|
options.AddPolicy("Production", policy =>
|
||||||
{
|
{
|
||||||
policy.WithOrigins("https://api.playlistshare.frigat.duckdns.org", "https://playlistshare.frigat.duckdns.org")
|
policy.WithOrigins(builder.Configuration.GetSection("Cors:Origins").Get<string[]>())
|
||||||
.AllowAnyMethod()
|
.AllowAnyMethod()
|
||||||
.AllowAnyHeader()
|
.AllowAnyHeader()
|
||||||
.AllowCredentials();
|
.AllowCredentials();
|
||||||
@@ -131,17 +124,14 @@ public class Program
|
|||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
app.UseSwaggerUI();
|
app.UseSwaggerUI();
|
||||||
|
|
||||||
if (app.Environment.IsDevelopment())
|
|
||||||
{
|
|
||||||
app.UseCors("Development");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
app.UseHttpsRedirection();
|
|
||||||
app.UseCors("Production");
|
app.UseCors("Production");
|
||||||
|
|
||||||
|
if (!app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseHttpsRedirection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
app.UseSession();
|
app.UseSession();
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ public class SharedPlaylistService
|
|||||||
CreatedAt = DateTime.UtcNow,
|
CreatedAt = DateTime.UtcNow,
|
||||||
UpdatedAt = DateTime.UtcNow,
|
UpdatedAt = DateTime.UtcNow,
|
||||||
ShareToken = GenerateToken(),
|
ShareToken = GenerateToken(),
|
||||||
|
PlayPermission = dto.PlayPermission,
|
||||||
ViewPermission = dto.ViewPermission,
|
ViewPermission = dto.ViewPermission,
|
||||||
AddPermission = dto.AddPermission,
|
AddPermission = dto.AddPermission,
|
||||||
RemovePermission = dto.RemovePermission
|
RemovePermission = dto.RemovePermission
|
||||||
@@ -63,6 +64,7 @@ public class SharedPlaylistService
|
|||||||
var entity = await _db.SharedPlaylists.FindAsync(playlistId);
|
var entity = await _db.SharedPlaylists.FindAsync(playlistId);
|
||||||
if (entity == null) return null;
|
if (entity == null) return null;
|
||||||
entity.ViewPermission = dto.ViewPermission;
|
entity.ViewPermission = dto.ViewPermission;
|
||||||
|
entity.PlayPermission = dto.PlayPermission;
|
||||||
entity.AddPermission = dto.AddPermission;
|
entity.AddPermission = dto.AddPermission;
|
||||||
entity.RemovePermission = dto.RemovePermission;
|
entity.RemovePermission = dto.RemovePermission;
|
||||||
entity.UpdatedAt = DateTime.UtcNow;
|
entity.UpdatedAt = DateTime.UtcNow;
|
||||||
@@ -80,6 +82,18 @@ public class SharedPlaylistService
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> CanPlayAsync(SharedPlaylist playlist, Guid? currentUserId)
|
||||||
|
{
|
||||||
|
if (currentUserId == playlist.CreatorUserId) return true;
|
||||||
|
return playlist.PlayPermission == ViewPermission.Everyone ||
|
||||||
|
(playlist.PlayPermission == ViewPermission.AuthorizedOnly && currentUserId.HasValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> CanPlayEveryoneAsync(SharedPlaylist playlist)
|
||||||
|
{
|
||||||
|
return playlist.PlayPermission == ViewPermission.Everyone;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<bool> CanViewAsync(SharedPlaylist playlist, Guid? currentUserId)
|
public async Task<bool> CanViewAsync(SharedPlaylist playlist, Guid? currentUserId)
|
||||||
{
|
{
|
||||||
if (currentUserId == playlist.CreatorUserId) return true;
|
if (currentUserId == playlist.CreatorUserId) return true;
|
||||||
|
|||||||
@@ -7,6 +7,12 @@
|
|||||||
"Issuer": "PlaylistShared.Api",
|
"Issuer": "PlaylistShared.Api",
|
||||||
"Audience": "PlaylistShared.Client"
|
"Audience": "PlaylistShared.Client"
|
||||||
},
|
},
|
||||||
|
"Cors": {
|
||||||
|
"Origins": [
|
||||||
|
"https://api.playlistshare.frigat.duckdns.org",
|
||||||
|
"https://playlistshare.frigat.duckdns.org"
|
||||||
|
]
|
||||||
|
},
|
||||||
"Yandex": {
|
"Yandex": {
|
||||||
"ClientId": "0916685f8a3641ca8fc382dbccf77236",
|
"ClientId": "0916685f8a3641ca8fc382dbccf77236",
|
||||||
"ClientSecret": "f7398893cd814f8b84b85aeb2a0a6698"
|
"ClientSecret": "f7398893cd814f8b84b85aeb2a0a6698"
|
||||||
|
|||||||
@@ -65,6 +65,9 @@
|
|||||||
/// <summary>Требовать ли авторизацию для воспроизведения (по умолчанию true).</summary>
|
/// <summary>Требовать ли авторизацию для воспроизведения (по умолчанию true).</summary>
|
||||||
[Parameter] public bool RequireAuth { get; set; } = true;
|
[Parameter] public bool RequireAuth { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>ID расшаренного плейлиста.</summary>
|
||||||
|
[Parameter] public string SharedPlaylistId { get; set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>Событие при завершении трека.</summary>
|
/// <summary>Событие при завершении трека.</summary>
|
||||||
[Parameter] public EventCallback OnTrackEnded { get; set; }
|
[Parameter] public EventCallback OnTrackEnded { get; set; }
|
||||||
|
|
||||||
@@ -139,16 +142,16 @@
|
|||||||
var tokens = await TokenStorage.GetTokensAsync();
|
var tokens = await TokenStorage.GetTokensAsync();
|
||||||
var accessToken = tokens.token;
|
var accessToken = tokens.token;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(accessToken))
|
if (string.IsNullOrWhiteSpace(accessToken) && string.IsNullOrWhiteSpace(SharedPlaylistId))
|
||||||
{
|
{
|
||||||
Snackbar.Add("Токен авторизации не найден. Пожалуйста, войдите заново.", Severity.Error);
|
Snackbar.Add("Токен авторизации не найден. Пожалуйста, войдите заново.", Severity.Error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var streamUrl = new Uri(Http.BaseAddress, $"/api/audio/track/{trackId}").ToString();
|
var streamUrl = new Uri(Http.BaseAddress!, $"/api/audio/track/{trackId}").ToString();
|
||||||
|
|
||||||
await EnsureAudioModuleAsync();
|
await EnsureAudioModuleAsync();
|
||||||
await _audioElement!.InvokeVoidAsync("loadAndPlay", streamUrl, accessToken);
|
await _audioElement!.InvokeVoidAsync("loadAndPlay", streamUrl, accessToken, SharedPlaylistId);
|
||||||
_isPlaying = true;
|
_isPlaying = true;
|
||||||
StartProgressTimer();
|
StartProgressTimer();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
|
|||||||
@@ -50,6 +50,12 @@
|
|||||||
<MudSelectItem Value="ViewPermission.AuthorizedOnly">Только авторизованные</MudSelectItem>
|
<MudSelectItem Value="ViewPermission.AuthorizedOnly">Только авторизованные</MudSelectItem>
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
</MudItem>
|
</MudItem>
|
||||||
|
<MudItem xs="12" sm="4">
|
||||||
|
<MudSelect T="ViewPermission" Label="Воспроизведение" @bind-Value="_editPermissions.PlayPermission" Variant="Variant.Outlined" FullWidth="true">
|
||||||
|
<MudSelectItem Value="ViewPermission.Everyone">Все</MudSelectItem>
|
||||||
|
<MudSelectItem Value="ViewPermission.AuthorizedOnly">Только авторизованные</MudSelectItem>
|
||||||
|
</MudSelect>
|
||||||
|
</MudItem>
|
||||||
<MudItem xs="12" sm="4">
|
<MudItem xs="12" sm="4">
|
||||||
<MudSelect T="EditPermission" Label="Добавление треков" @bind-Value="_editPermissions.AddPermission" Variant="Variant.Outlined" FullWidth="true">
|
<MudSelect T="EditPermission" Label="Добавление треков" @bind-Value="_editPermissions.AddPermission" Variant="Variant.Outlined" FullWidth="true">
|
||||||
<MudSelectItem Value="EditPermission.Everyone">Все</MudSelectItem>
|
<MudSelectItem Value="EditPermission.Everyone">Все</MudSelectItem>
|
||||||
@@ -143,6 +149,8 @@
|
|||||||
<MudTd DataLabel="#" Style="font-weight: normal;">@context.Index</MudTd>
|
<MudTd DataLabel="#" Style="font-weight: normal;">@context.Index</MudTd>
|
||||||
<MudTd DataLabel="Обложка">
|
<MudTd DataLabel="Обложка">
|
||||||
@if (!string.IsNullOrEmpty(context.CoverUri))
|
@if (!string.IsNullOrEmpty(context.CoverUri))
|
||||||
|
{
|
||||||
|
@if (@_canPlay)
|
||||||
{
|
{
|
||||||
<TrackCoverWithPlay CoverUrl="@context.CoverUri"
|
<TrackCoverWithPlay CoverUrl="@context.CoverUri"
|
||||||
TrackId="@context.Id"
|
TrackId="@context.Id"
|
||||||
@@ -150,6 +158,12 @@
|
|||||||
IsPlaying="@(_currentTrackId == context.Id && _isPlaying)"
|
IsPlaying="@(_currentTrackId == context.Id && _isPlaying)"
|
||||||
OnPlay="PlayTrack" />
|
OnPlay="PlayTrack" />
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
<MudImage Src="@FormatCoverUrl(context.CoverUri, "50x50")" Height="50" Width="50" Class="rounded" Style="display: block;" />
|
||||||
|
}
|
||||||
|
}
|
||||||
</MudTd>
|
</MudTd>
|
||||||
<MudTd DataLabel="Название">
|
<MudTd DataLabel="Название">
|
||||||
<MudLink Href="@($"https://music.yandex.ru/track/{context.Id}")" Target="_blank" Underline="Underline.Hover">
|
<MudLink Href="@($"https://music.yandex.ru/track/{context.Id}")" Target="_blank" Underline="Underline.Hover">
|
||||||
@@ -174,7 +188,7 @@
|
|||||||
|
|
||||||
<!-- Фиксированный плеер внизу -->
|
<!-- Фиксированный плеер внизу -->
|
||||||
<div class="fixed-player" style="display: @(_isPlayerVisible ? "block" : "none");">
|
<div class="fixed-player" style="display: @(_isPlayerVisible ? "block" : "none");">
|
||||||
<AudioPlayer @ref="_audioPlayer" OnTrackEnded="OnTrackEnded" />
|
<AudioPlayer @ref="_audioPlayer" OnTrackEnded="OnTrackEnded" RequireAuth="false" SharedPlaylistId="@Token"/>
|
||||||
</div>
|
</div>
|
||||||
</MudContainer>
|
</MudContainer>
|
||||||
|
|
||||||
@@ -191,6 +205,7 @@
|
|||||||
private bool _loading = true;
|
private bool _loading = true;
|
||||||
private bool _isAuthenticated;
|
private bool _isAuthenticated;
|
||||||
private bool _isCreator;
|
private bool _isCreator;
|
||||||
|
private bool _canPlay;
|
||||||
private bool _canAdd;
|
private bool _canAdd;
|
||||||
private bool _canRemove;
|
private bool _canRemove;
|
||||||
private UpdatePermissionsDto _editPermissions = new();
|
private UpdatePermissionsDto _editPermissions = new();
|
||||||
@@ -211,14 +226,17 @@
|
|||||||
await LoadPlaylist();
|
await LoadPlaylist();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LoadPlaylist()
|
private async Task ConfigurePermissions()
|
||||||
{
|
{
|
||||||
try
|
if (_playlist is null)
|
||||||
{
|
{
|
||||||
var response = await Http.GetFromJsonAsync<ApiResponse<SharedPlaylistDto>>($"/api/sharedplaylist/{Token}");
|
_isCreator = false;
|
||||||
if (response?.Success == true)
|
_canAdd = false;
|
||||||
|
_canRemove = false;
|
||||||
|
_canPlay = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
_playlist = response.Data;
|
|
||||||
_isCreator = _playlist.CreatorUserId.ToString() == _currentUserId;
|
_isCreator = _playlist.CreatorUserId.ToString() == _currentUserId;
|
||||||
|
|
||||||
_canAdd = _isCreator
|
_canAdd = _isCreator
|
||||||
@@ -229,16 +247,35 @@
|
|||||||
|| _playlist.RemovePermission == EditPermission.Everyone
|
|| _playlist.RemovePermission == EditPermission.Everyone
|
||||||
|| (_playlist.RemovePermission == EditPermission.AuthorizedOnly && _isAuthenticated);
|
|| (_playlist.RemovePermission == EditPermission.AuthorizedOnly && _isAuthenticated);
|
||||||
|
|
||||||
|
_canPlay = _isCreator
|
||||||
|
|| _playlist.PlayPermission == ViewPermission.Everyone
|
||||||
|
|| (_playlist.PlayPermission == ViewPermission.AuthorizedOnly && _isAuthenticated);
|
||||||
|
|
||||||
if (_isCreator && _isAuthenticated)
|
if (_isCreator && _isAuthenticated)
|
||||||
{
|
{
|
||||||
_editPermissions = new UpdatePermissionsDto
|
_editPermissions = new UpdatePermissionsDto
|
||||||
{
|
{
|
||||||
ViewPermission = _playlist.ViewPermission,
|
ViewPermission = _playlist.ViewPermission,
|
||||||
AddPermission = _playlist.AddPermission,
|
AddPermission = _playlist.AddPermission,
|
||||||
RemovePermission = _playlist.RemovePermission
|
RemovePermission = _playlist.RemovePermission,
|
||||||
|
PlayPermission = _playlist.PlayPermission,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task LoadPlaylist()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await Http.GetFromJsonAsync<ApiResponse<SharedPlaylistDto>>($"/api/sharedplaylist/{Token}");
|
||||||
|
if (response?.Success == true)
|
||||||
|
{
|
||||||
|
_playlist = response.Data;
|
||||||
|
|
||||||
|
await ConfigurePermissions();
|
||||||
|
|
||||||
await LoadTracks();
|
await LoadTracks();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -371,11 +408,8 @@
|
|||||||
if (result?.Success == true)
|
if (result?.Success == true)
|
||||||
{
|
{
|
||||||
_playlist = result.Data;
|
_playlist = result.Data;
|
||||||
Snackbar.Add("Права доступа обновлены", Severity.Success);
|
|
||||||
_canAdd = _isCreator || _playlist.AddPermission == EditPermission.Everyone ||
|
await ConfigurePermissions();
|
||||||
(_playlist.AddPermission == EditPermission.AuthorizedOnly && _isAuthenticated);
|
|
||||||
_canRemove = _isCreator || _playlist.RemovePermission == EditPermission.Everyone ||
|
|
||||||
(_playlist.RemovePermission == EditPermission.AuthorizedOnly && _isAuthenticated);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,74 +16,6 @@
|
|||||||
<link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
|
<link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
|
||||||
<link rel="apple-touch-icon" sizes="192x192" href="icon-192.png" />
|
<link rel="apple-touch-icon" sizes="192x192" href="icon-192.png" />
|
||||||
<script type="importmap"></script>
|
<script type="importmap"></script>
|
||||||
<style>
|
|
||||||
html, body {
|
|
||||||
background-color: #1a1a27 !important;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Кастомный спиннер в стиле MudBlazor (тёмная тема) */
|
|
||||||
.loading-progress {
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-progress circle {
|
|
||||||
fill: none;
|
|
||||||
stroke: #2a2833;
|
|
||||||
stroke-width: 4;
|
|
||||||
transform-origin: 50% 50%;
|
|
||||||
animation: spin 1.5s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-progress circle:last-child {
|
|
||||||
stroke: #7e6fff;
|
|
||||||
stroke-dasharray: 126;
|
|
||||||
stroke-dashoffset: 126;
|
|
||||||
animation: dash 1.5s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
100% {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes dash {
|
|
||||||
0% {
|
|
||||||
stroke-dashoffset: 126;
|
|
||||||
}
|
|
||||||
|
|
||||||
50% {
|
|
||||||
stroke-dashoffset: 63;
|
|
||||||
transform: rotate(135deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
stroke-dashoffset: 126;
|
|
||||||
transform: rotate(450deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-progress-text {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 16px;
|
|
||||||
color: #b2b0bf;
|
|
||||||
font-family: 'Roboto', sans-serif;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Убираем белые вспышки */
|
|
||||||
#app {
|
|
||||||
background-color: #1a1a27;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -10,9 +10,10 @@
|
|||||||
return isNaN(num) ? 0 : num;
|
return isNaN(num) ? 0 : num;
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadAndPlay = (src, token) => {
|
const loadAndPlay = (src, token, sharedPlaylistId) => {
|
||||||
const url = new URL(src, window.location.href);
|
const url = new URL(src, window.location.href);
|
||||||
if (token) url.searchParams.set('access_token', token);
|
if (token) url.searchParams.set('access_token', token);
|
||||||
|
if (sharedPlaylistId) url.searchParams.set('shared_id', sharedPlaylistId);
|
||||||
audio.src = url.toString();
|
audio.src = url.toString();
|
||||||
audio.load();
|
audio.load();
|
||||||
durationReady = false;
|
durationReady = false;
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ public class SharePlaylistDto
|
|||||||
[JsonPropertyName("viewPermission")]
|
[JsonPropertyName("viewPermission")]
|
||||||
public ViewPermission ViewPermission { get; set; }
|
public ViewPermission ViewPermission { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Права на воспроизведение.</summary>
|
||||||
|
[JsonPropertyName("playPermission")]
|
||||||
|
public ViewPermission PlayPermission { get; set; }
|
||||||
|
|
||||||
/// <summary>Права на добавление треков.</summary>
|
/// <summary>Права на добавление треков.</summary>
|
||||||
[JsonPropertyName("addPermission")]
|
[JsonPropertyName("addPermission")]
|
||||||
public EditPermission AddPermission { get; set; }
|
public EditPermission AddPermission { get; set; }
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ public class SharedPlaylistDto
|
|||||||
[JsonPropertyName("viewPermission")]
|
[JsonPropertyName("viewPermission")]
|
||||||
public ViewPermission ViewPermission { get; set; }
|
public ViewPermission ViewPermission { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Права на воспроизведение.</summary>
|
||||||
|
[JsonPropertyName("playPermission")]
|
||||||
|
public ViewPermission PlayPermission { get; set; }
|
||||||
|
|
||||||
/// <summary>Права на добавление треков.</summary>
|
/// <summary>Права на добавление треков.</summary>
|
||||||
[JsonPropertyName("addPermission")]
|
[JsonPropertyName("addPermission")]
|
||||||
public EditPermission AddPermission { get; set; }
|
public EditPermission AddPermission { get; set; }
|
||||||
|
|||||||
@@ -6,15 +6,19 @@ namespace PlaylistShared.Shared.Shared;
|
|||||||
/// <summary>Запрос на обновление прав доступа шеринг-плейлиста.</summary>
|
/// <summary>Запрос на обновление прав доступа шеринг-плейлиста.</summary>
|
||||||
public class UpdatePermissionsDto
|
public class UpdatePermissionsDto
|
||||||
{
|
{
|
||||||
/// <summary>Новые права на просмотр.</summary>
|
/// <summary>Права на просмотр.</summary>
|
||||||
[JsonPropertyName("viewPermission")]
|
[JsonPropertyName("viewPermission")]
|
||||||
public ViewPermission ViewPermission { get; set; }
|
public ViewPermission ViewPermission { get; set; }
|
||||||
|
|
||||||
/// <summary>Новые права на добавление треков.</summary>
|
/// <summary>Права на воспроизведение треков.</summary>
|
||||||
|
[JsonPropertyName("playPermission")]
|
||||||
|
public ViewPermission PlayPermission { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Права на добавление треков.</summary>
|
||||||
[JsonPropertyName("addPermission")]
|
[JsonPropertyName("addPermission")]
|
||||||
public EditPermission AddPermission { get; set; }
|
public EditPermission AddPermission { get; set; }
|
||||||
|
|
||||||
/// <summary>Новые права на удаление треков.</summary>
|
/// <summary>Права на удаление треков.</summary>
|
||||||
[JsonPropertyName("removePermission")]
|
[JsonPropertyName("removePermission")]
|
||||||
public EditPermission RemovePermission { get; set; }
|
public EditPermission RemovePermission { get; set; }
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user