universal media edit page created
This commit is contained in:
@@ -4,9 +4,21 @@ namespace WatchIt.Common.Model.Media;
|
|||||||
|
|
||||||
public abstract class MediaPoster
|
public abstract class MediaPoster
|
||||||
{
|
{
|
||||||
|
#region PROPERTIES
|
||||||
|
|
||||||
[JsonPropertyName("image")]
|
[JsonPropertyName("image")]
|
||||||
public required byte[] Image { get; set; }
|
public required byte[] Image { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("mime_type")]
|
[JsonPropertyName("mime_type")]
|
||||||
public required string MimeType { get; set; }
|
public required string MimeType { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PUBLIC METHODS
|
||||||
|
|
||||||
|
public override string ToString() => $"data:{MimeType};base64,{Convert.ToBase64String(Image)}";
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,24 @@
|
|||||||
using WatchIt.Database.Model.Media;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using WatchIt.Database.Model.Media;
|
||||||
|
|
||||||
namespace WatchIt.Common.Model.Media;
|
namespace WatchIt.Common.Model.Media;
|
||||||
|
|
||||||
public class MediaPosterRequest : MediaPoster
|
public class MediaPosterRequest : MediaPoster
|
||||||
{
|
{
|
||||||
|
#region CONSTRUCTORS
|
||||||
|
|
||||||
|
public MediaPosterRequest() {}
|
||||||
|
|
||||||
|
[SetsRequiredMembers]
|
||||||
|
public MediaPosterRequest(MediaPosterResponse response)
|
||||||
|
{
|
||||||
|
Image = response.Image;
|
||||||
|
MimeType = response.MimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
public MediaPosterImage CreateMediaPosterImage() => new MediaPosterImage
|
public MediaPosterImage CreateMediaPosterImage() => new MediaPosterImage
|
||||||
{
|
{
|
||||||
Image = Image,
|
Image = Image,
|
||||||
@@ -14,6 +29,6 @@ public class MediaPosterRequest : MediaPoster
|
|||||||
{
|
{
|
||||||
item.Image = Image;
|
item.Image = Image;
|
||||||
item.MimeType = MimeType;
|
item.MimeType = MimeType;
|
||||||
item.UploadDate = DateTime.Now;
|
item.UploadDate = DateTime.UtcNow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,7 +107,7 @@ public class MediaController(IMediaControllerService mediaControllerService)
|
|||||||
|
|
||||||
[HttpPut("{id}/poster")]
|
[HttpPut("{id}/poster")]
|
||||||
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
|
||||||
[ProducesResponseType(typeof(void), StatusCodes.Status200OK)]
|
[ProducesResponseType(typeof(MediaPosterResponse), StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
|
||||||
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
[ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)]
|
||||||
[ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)]
|
[ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)]
|
||||||
|
|||||||
@@ -267,7 +267,8 @@ public class MediaControllerService(DatabaseContext database, IUserService userS
|
|||||||
|
|
||||||
await database.SaveChangesAsync();
|
await database.SaveChangesAsync();
|
||||||
|
|
||||||
return RequestResult.Ok();
|
MediaPosterResponse returnData = new MediaPosterResponse(media.MediaPosterImage);
|
||||||
|
return RequestResult.Ok(returnData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RequestResult> DeleteMediaPoster(long mediaId)
|
public async Task<RequestResult> DeleteMediaPoster(long mediaId)
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
using WatchIt.Common.Model.Series;
|
||||||
|
|
||||||
|
namespace WatchIt.WebAPI.Validators.Movies;
|
||||||
|
|
||||||
|
public class SeriesRequestValidator : AbstractValidator<SeriesRequest>
|
||||||
|
{
|
||||||
|
public SeriesRequestValidator()
|
||||||
|
{
|
||||||
|
RuleFor(x => x.Title).NotEmpty().MaximumLength(250);
|
||||||
|
RuleFor(x => x.OriginalTitle).MaximumLength(250);
|
||||||
|
RuleFor(x => x.Description).MaximumLength(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,6 @@ public interface IMediaWebAPIService
|
|||||||
Task GetPhotoMediaRandomBackground(long mediaId, Action<MediaPhotoResponse>? successAction = null, Action? notFoundAction = null);
|
Task GetPhotoMediaRandomBackground(long mediaId, Action<MediaPhotoResponse>? successAction = null, Action? notFoundAction = null);
|
||||||
Task GetPhotoRandomBackground(Action<MediaPhotoResponse>? successAction = null, Action? notFoundAction = null);
|
Task GetPhotoRandomBackground(Action<MediaPhotoResponse>? successAction = null, Action? notFoundAction = null);
|
||||||
Task GetPoster(long mediaId, Action<MediaPosterResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? notFoundAction = null);
|
Task GetPoster(long mediaId, Action<MediaPosterResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? notFoundAction = null);
|
||||||
Task PutPoster(long mediaId, MediaPosterRequest data, Action? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null);
|
Task PutPoster(long mediaId, MediaPosterRequest data, Action<MediaPosterResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null);
|
||||||
Task DeletePoster(long mediaId, Action? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null);
|
Task DeletePoster(long mediaId, Action? successAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null);
|
||||||
}
|
}
|
||||||
@@ -169,7 +169,7 @@ public class MediaWebAPIService(IHttpClientService httpClientService, IConfigura
|
|||||||
.ExecuteAction();
|
.ExecuteAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task PutPoster(long mediaId, MediaPosterRequest data, Action? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null)
|
public async Task PutPoster(long mediaId, MediaPosterRequest data, Action<MediaPosterResponse>? successAction = null, Action<IDictionary<string, string[]>>? badRequestAction = null, Action? unauthorizedAction = null, Action? forbiddenAction = null)
|
||||||
{
|
{
|
||||||
string url = GetUrl(EndpointsConfiguration.Media.PutPoster, mediaId);
|
string url = GetUrl(EndpointsConfiguration.Media.PutPoster, mediaId);
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
<!-- CSS -->
|
<!-- CSS -->
|
||||||
<link rel="stylesheet" href="app.css?version=0.1.0.16"/>
|
<link rel="stylesheet" href="app.css?version=0.1.0.16"/>
|
||||||
<link rel="stylesheet" href="WatchIt.Website.styles.css?version=0.1.0.25"/>
|
<link rel="stylesheet" href="WatchIt.Website.styles.css?version=0.1.0.29"/>
|
||||||
|
|
||||||
<!-- BOOTSTRAP -->
|
<!-- BOOTSTRAP -->
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="rounded-3 panel panel-regular p-4">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
<div class="text-danger icon-size">⚠︎</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
<h3 class="text-danger">An error occured while loading a page</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if (!string.IsNullOrWhiteSpace(ErrorMessage))
|
||||||
|
{
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
<p>@ErrorMessage</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace WatchIt.Website.Components;
|
||||||
|
|
||||||
|
public partial class ErrorComponent : ComponentBase
|
||||||
|
{
|
||||||
|
#region PARAMETERS
|
||||||
|
|
||||||
|
[Parameter] public string? ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
/* CLASSES */
|
||||||
|
|
||||||
|
.icon-size {
|
||||||
|
font-size: 80px;
|
||||||
|
}
|
||||||
@@ -80,7 +80,7 @@ public partial class MediaFormComponent : ComponentBase
|
|||||||
|
|
||||||
private async Task SavePoster()
|
private async Task SavePoster()
|
||||||
{
|
{
|
||||||
void SuccessAction()
|
void SuccessAction(MediaPosterResponse data)
|
||||||
{
|
{
|
||||||
_actualPosterBase64 = _posterBase64;
|
_actualPosterBase64 = _posterBase64;
|
||||||
_actualPosterMediaType = _posterMediaType;
|
_actualPosterMediaType = _posterMediaType;
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
<div class="row">
|
|
||||||
<div class="col rounded-3 panel panel-regular m-1">
|
|
||||||
<div>
|
|
||||||
<h2 class="text-danger">Sorry. You have no permission to view this site.</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
@if (_loaded)
|
@if (_loaded)
|
||||||
{
|
{
|
||||||
<div class="container-xl">
|
<div class="container-xl">
|
||||||
<div class="row align-items-center m-1 my-2 rounded-3 header panel panel-header z-3">
|
<div class="row align-items-center m-1 my-2 mb-3 rounded-3 header panel panel-header z-3">
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<a class="logo" href="/">
|
<a class="logo" href="/">
|
||||||
WatchIt
|
WatchIt
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col z-0">
|
<div class="col z-0 p-1">
|
||||||
@Body
|
@Body
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
<PageTitle>WatchIt administrator panel</PageTitle>
|
<PageTitle>WatchIt administrator panel</PageTitle>
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
@if (_loaded)
|
@if (_loaded)
|
||||||
{
|
{
|
||||||
<div class="container-fluid">
|
if (_authenticated)
|
||||||
@if (_authenticated)
|
|
||||||
{
|
{
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col rounded-3 panel panel-regular m-1">
|
<div class="col rounded-3 panel panel-regular m-1">
|
||||||
@@ -13,10 +13,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<a class="col rounded-3 panel panel-regular m-1" href="/movies/new">
|
<a class="col rounded-3 panel panel-regular m-1" href="/media/new/movies">
|
||||||
<p class="text-center text-decorations-none">New movie</p>
|
<p class="text-center text-decorations-none">New movie</p>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="col rounded-3 panel panel-regular m-1">
|
<div class="col rounded-3 panel panel-regular m-1">
|
||||||
<p class="text-center">New TV series</p>
|
<p class="text-center">New TV series</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -27,7 +26,11 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<NoPermissionComponent/>
|
<ErrorComponent ErrorMessage="You do not have permission to view this site"/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<LoadingComponent/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
}
|
|
||||||
@@ -66,7 +66,9 @@ public partial class AuthPage
|
|||||||
|
|
||||||
#region METHODS
|
#region METHODS
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
{
|
{
|
||||||
if (await AuthenticationService.GetAuthenticationStatusAsync())
|
if (await AuthenticationService.GetAuthenticationStatusAsync())
|
||||||
{
|
{
|
||||||
@@ -88,6 +90,8 @@ public partial class AuthPage
|
|||||||
await MediaWebAPIService.GetPhotoRandomBackground(backgroundSuccess);
|
await MediaWebAPIService.GetPhotoRandomBackground(backgroundSuccess);
|
||||||
|
|
||||||
_loaded = true;
|
_loaded = true;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Login()
|
private async Task Login()
|
||||||
|
|||||||
230
WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor
Normal file
230
WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
@using WatchIt.Common.Model.Movies
|
||||||
|
@using WatchIt.Common.Model.Series
|
||||||
|
|
||||||
|
@page "/media/{id:long}/edit"
|
||||||
|
@page "/media/new/{type}"
|
||||||
|
|
||||||
|
|
||||||
|
<PageTitle>
|
||||||
|
WatchIt -
|
||||||
|
@if (_loaded)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(_error) && _user?.IsAdmin == true)
|
||||||
|
{
|
||||||
|
if (_media is not null)
|
||||||
|
{
|
||||||
|
@($"Edit \"")@(_media.Title)@("\"")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_movieRequest is null)
|
||||||
|
{
|
||||||
|
@("New TV series")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@("New movie")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@("Error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
@("Loading")
|
||||||
|
}
|
||||||
|
</PageTitle>
|
||||||
|
|
||||||
|
<div class="container-fluid">
|
||||||
|
@if (_loaded)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(_error))
|
||||||
|
{
|
||||||
|
if (_user?.IsAdmin == true)
|
||||||
|
{
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<div class="rounded-3 panel panel-regular p-2">
|
||||||
|
<h2 class="m-0 mx-2 mb-1 p-0">@(_media is not null ? "Edit" : "Create new") @(_movieRequest is not null ? "movie" : "series")@(_media is not null ? $" \"{_media.Title}\"" : string.Empty)</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mt-3 gx-3">
|
||||||
|
<div class="col-auto">
|
||||||
|
<div class="rounded-3 panel panel-regular p-4 h-100">
|
||||||
|
<div class="container-fluid p-0">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<img class="rounded-2 shadow object-fit-cover" src="@(_mediaPosterRequest is not null ? _mediaPosterRequest.ToString() : "assets/poster.png")" alt="poster" width="300" height="500"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row mt-4">
|
||||||
|
<div class="col">
|
||||||
|
<InputFile class="form-control" OnChange="LoadPoster" disabled="@(_media is null)" autocomplete="off" style="width: 300px;"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if (_mediaPosterChanged || _mediaPosterSaved is not null)
|
||||||
|
{
|
||||||
|
<div class="row mt-4 gx-1" style="width: 300px;">
|
||||||
|
@if (_mediaPosterChanged)
|
||||||
|
{
|
||||||
|
<div class="col">
|
||||||
|
<button type="button" class="btn btn-secondary btn-block btn-stretch-x" @onclick="SavePoster" disabled=@(!Id.HasValue || _mediaPosterSaving || _mediaPosterDeleting) autocomplete="off">
|
||||||
|
@if (!_mediaPosterSaving)
|
||||||
|
{
|
||||||
|
<span class="sr-only">Save poster</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||||
|
<span class="sr-only">Saving...</span>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<button type="button" class="btn btn-danger btn-block btn-stretch-x" @onclick="CancelPoster" disabled=@(!Id.HasValue || _mediaPosterSaving || _mediaPosterDeleting) autocomplete="off">Drop changes</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else if (_mediaPosterSaved is not null)
|
||||||
|
{
|
||||||
|
<div class="col">
|
||||||
|
<button type="button" class="btn btn-danger btn-block btn-stretch-x" @onclick="DeletePoster" disabled=@(!Id.HasValue || _mediaPosterSaving || _mediaPosterDeleting) autocomplete="off">
|
||||||
|
@if (!_mediaPosterSaving)
|
||||||
|
{
|
||||||
|
<span class="sr-only">Delete poster</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||||
|
<span class="sr-only">Deleting...</span>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="rounded-3 panel panel-regular p-4 h-100">
|
||||||
|
<EditForm Model="_mediaRequest">
|
||||||
|
<AntiforgeryToken/>
|
||||||
|
<div class="container-fluid p-0">
|
||||||
|
<div class="row form-group mb-1">
|
||||||
|
<label for="title" class="col-2 col-form-label">Title*</label>
|
||||||
|
<div class="col-10">
|
||||||
|
<InputText id="title" class="form-control" @bind-Value="_mediaRequest!.Title"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row form-group my-1">
|
||||||
|
<label for="og-title" class="col-2 col-form-label">Original title</label>
|
||||||
|
<div class="col-10">
|
||||||
|
<InputText id="og-title" class="form-control" @bind-Value="_mediaRequest!.OriginalTitle"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row form-group my-1">
|
||||||
|
<label for="desc" class="col-2 col-form-label">Description</label>
|
||||||
|
<div class="col-10">
|
||||||
|
<InputTextArea id="desc" class="form-control" @bind-Value="_mediaRequest!.Description"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row form-group my-1">
|
||||||
|
<label for="rel-date" class="col-2 col-form-label">Release date</label>
|
||||||
|
<div class="col-4">
|
||||||
|
<InputDate TValue="DateOnly?" id="rel-date" class="form-control" @bind-Value="_mediaRequest!.ReleaseDate"/>
|
||||||
|
</div>
|
||||||
|
<label for="length" class="col-2 col-form-label">Length</label>
|
||||||
|
<div class="col-4">
|
||||||
|
<InputNumber TValue="short?" id="length" class="form-control" @bind-Value="_mediaRequest!.Length"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if (_mediaRequest is MovieRequest)
|
||||||
|
{
|
||||||
|
<div class="row form-group mt-1">
|
||||||
|
<label for="budget" class="col-2 col-form-label">Budget</label>
|
||||||
|
<div class="col-10">
|
||||||
|
<InputNumber TValue="decimal?" id="budget" class="form-control" @bind-Value="((MovieRequest)_mediaRequest)!.Budget"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="row form-group mt-1">
|
||||||
|
<label class="col-2 col-form-label">Has ended</label>
|
||||||
|
<div class="col-10 col-form-label">
|
||||||
|
<div class="d-flex gap-3">
|
||||||
|
<InputRadioGroup TValue="bool" @bind-Value="((SeriesRequest)_mediaRequest)!.HasEnded">
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<InputRadio TValue="bool" Value="true"/>
|
||||||
|
Yes
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<InputRadio TValue="bool" Value="false"/>
|
||||||
|
No
|
||||||
|
</div>
|
||||||
|
</InputRadioGroup>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="row mt-4">
|
||||||
|
<div class="col">
|
||||||
|
<div class="d-flex justify-content-end align-items-center gap-3">
|
||||||
|
@if (!string.IsNullOrWhiteSpace(_basicDataError))
|
||||||
|
{
|
||||||
|
<div class="text-danger">@_basicDataError</div>
|
||||||
|
}
|
||||||
|
<button type="button" class="btn btn-secondary" @onclick="SaveBasicData">
|
||||||
|
@if (!_basicDataSaving)
|
||||||
|
{
|
||||||
|
<span class="sr-only">Save</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||||
|
<span class="sr-only">Saving...</span>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</EditForm>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<ErrorComponent ErrorMessage="You do not have permission to view this site"/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<ErrorComponent ErrorMessage="@_error"/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<LoadingComponent/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@if (_background is not null)
|
||||||
|
{
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-image: url('@($"data:{_background.MimeType};base64,{Convert.ToBase64String(_background.Image)}")') !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo, .main-button {
|
||||||
|
background-image: linear-gradient(45deg, @($"#{BitConverter.ToString(_background.Background.FirstGradientColor).Replace("-", string.Empty)}"), @($"#{BitConverter.ToString(_background.Background.SecondGradientColor).Replace("-", string.Empty)}")) !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
}
|
||||||
254
WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor.cs
Normal file
254
WatchIt.Website/WatchIt.Website/Pages/MediaEditPage.razor.cs
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
using WatchIt.Common.Model.Media;
|
||||||
|
using WatchIt.Common.Model.Movies;
|
||||||
|
using WatchIt.Common.Model.Series;
|
||||||
|
using WatchIt.Website.Services.Utility.Authentication;
|
||||||
|
using WatchIt.Website.Services.WebAPI.Media;
|
||||||
|
using WatchIt.Website.Services.WebAPI.Movies;
|
||||||
|
using WatchIt.Website.Services.WebAPI.Series;
|
||||||
|
|
||||||
|
namespace WatchIt.Website.Pages;
|
||||||
|
|
||||||
|
public partial class MediaEditPage : ComponentBase
|
||||||
|
{
|
||||||
|
#region SERVICES
|
||||||
|
|
||||||
|
[Inject] public NavigationManager NavigationManager { get; set; } = default!;
|
||||||
|
[Inject] public IAuthenticationService AuthenticationService { get; set; } = default!;
|
||||||
|
[Inject] public IMediaWebAPIService MediaWebAPIService { get; set; } = default!;
|
||||||
|
[Inject] public IMoviesWebAPIService MoviesWebAPIService { get; set; } = default!;
|
||||||
|
[Inject] public ISeriesWebAPIService SeriesWebAPIService { get; set; } = default!;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PARAMETERS
|
||||||
|
|
||||||
|
[Parameter] public long? Id { get; set; }
|
||||||
|
[Parameter] public string? Type { get; set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region FIELDS
|
||||||
|
|
||||||
|
private bool _loaded = false;
|
||||||
|
private string? _error;
|
||||||
|
|
||||||
|
private User? _user;
|
||||||
|
|
||||||
|
private MediaPhotoResponse? _background;
|
||||||
|
|
||||||
|
private MediaResponse? _media;
|
||||||
|
private MovieRequest? _movieRequest;
|
||||||
|
private SeriesRequest? _seriesRequest;
|
||||||
|
private Media? _mediaRequest => _movieRequest is not null ? _movieRequest : _seriesRequest;
|
||||||
|
private bool _basicDataSaving;
|
||||||
|
private string? _basicDataError;
|
||||||
|
|
||||||
|
private MediaPosterResponse? _mediaPosterSaved;
|
||||||
|
private MediaPosterRequest? _mediaPosterRequest;
|
||||||
|
private bool _mediaPosterChanged;
|
||||||
|
private bool _mediaPosterSaving;
|
||||||
|
private bool _mediaPosterDeleting;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PRIVATE METHODS
|
||||||
|
|
||||||
|
#region Main
|
||||||
|
|
||||||
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
List<Task> step1Tasks = new List<Task>();
|
||||||
|
List<Task> step2Tasks = new List<Task>();
|
||||||
|
List<Task> endTasks = new List<Task>();
|
||||||
|
|
||||||
|
// STEP 0
|
||||||
|
step1Tasks.AddRange(
|
||||||
|
[
|
||||||
|
Task.Run(async () => _user = await AuthenticationService.GetUserAsync())
|
||||||
|
]);
|
||||||
|
|
||||||
|
// STEP 1
|
||||||
|
await Task.WhenAll(step1Tasks);
|
||||||
|
if (_user is not null && _user.IsAdmin)
|
||||||
|
{
|
||||||
|
step2Tasks.AddRange(
|
||||||
|
[
|
||||||
|
InitializeMedia()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// STEP 2
|
||||||
|
await Task.WhenAll(step2Tasks);
|
||||||
|
if (_user is not null && _user.IsAdmin && _media is not null)
|
||||||
|
{
|
||||||
|
endTasks.AddRange(
|
||||||
|
[
|
||||||
|
MediaWebAPIService.GetPhotoMediaRandomBackground(Id.Value, data => _background = data),
|
||||||
|
MediaWebAPIService.GetPoster(Id.Value, data =>
|
||||||
|
{
|
||||||
|
_mediaPosterSaved = data;
|
||||||
|
_mediaPosterRequest = new MediaPosterRequest(data);
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.WhenAll(endTasks);
|
||||||
|
|
||||||
|
_loaded = true;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task InitializeMedia()
|
||||||
|
{
|
||||||
|
if (Id.HasValue)
|
||||||
|
{
|
||||||
|
await MediaWebAPIService.GetMedia(Id.Value, data => _media = data, () => NavigationManager.NavigateTo("/media/new/movie"));
|
||||||
|
if (_media.Type == MediaType.Movie)
|
||||||
|
{
|
||||||
|
await MoviesWebAPIService.GetMovie(Id.Value, data => _movieRequest = new MovieRequest(data));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await SeriesWebAPIService.GetSeries(Id.Value, data => _seriesRequest = new SeriesRequest(data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(Type) && Type == "series")
|
||||||
|
{
|
||||||
|
_seriesRequest = new SeriesRequest
|
||||||
|
{
|
||||||
|
Title = string.Empty
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_movieRequest = new MovieRequest
|
||||||
|
{
|
||||||
|
Title = string.Empty
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Poster
|
||||||
|
|
||||||
|
private async Task LoadPoster(InputFileChangeEventArgs args)
|
||||||
|
{
|
||||||
|
if (args.File.ContentType.StartsWith("image"))
|
||||||
|
{
|
||||||
|
Stream stream = args.File.OpenReadStream(5242880);
|
||||||
|
byte[] array;
|
||||||
|
using (MemoryStream ms = new MemoryStream())
|
||||||
|
{
|
||||||
|
await stream.CopyToAsync(ms);
|
||||||
|
array = ms.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
_mediaPosterRequest = new MediaPosterRequest()
|
||||||
|
{
|
||||||
|
Image = array,
|
||||||
|
MimeType = args.File.ContentType
|
||||||
|
};
|
||||||
|
_mediaPosterChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SavePoster()
|
||||||
|
{
|
||||||
|
void Success(MediaPosterResponse data)
|
||||||
|
{
|
||||||
|
_mediaPosterSaved = data;
|
||||||
|
_mediaPosterRequest = new MediaPosterRequest(data);
|
||||||
|
_mediaPosterChanged = false;
|
||||||
|
_mediaPosterSaving = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_mediaPosterSaving = true;
|
||||||
|
await MediaWebAPIService.PutPoster(Id.Value, _mediaPosterRequest, Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CancelPoster()
|
||||||
|
{
|
||||||
|
_mediaPosterRequest = _mediaPosterSaved is not null ? new MediaPosterRequest(_mediaPosterSaved) : null;
|
||||||
|
_mediaPosterChanged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DeletePoster()
|
||||||
|
{
|
||||||
|
void Success()
|
||||||
|
{
|
||||||
|
_mediaPosterSaved = null;
|
||||||
|
_mediaPosterRequest = null;
|
||||||
|
_mediaPosterChanged = false;
|
||||||
|
_mediaPosterDeleting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_mediaPosterDeleting = true;
|
||||||
|
await MediaWebAPIService.DeletePoster(Id.Value, Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Basic data
|
||||||
|
|
||||||
|
private async Task SaveBasicData()
|
||||||
|
{
|
||||||
|
void SuccessPost(long id)
|
||||||
|
{
|
||||||
|
_basicDataSaving = false;
|
||||||
|
NavigationManager.NavigateTo($"/media/{id}/edit", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BadRequest(IDictionary<string, string[]> errors)
|
||||||
|
{
|
||||||
|
_basicDataError = errors.SelectMany(x => x.Value).FirstOrDefault();
|
||||||
|
if (!string.IsNullOrWhiteSpace(_basicDataError))
|
||||||
|
{
|
||||||
|
_basicDataSaving = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_basicDataSaving = true;
|
||||||
|
if (_media is null)
|
||||||
|
{
|
||||||
|
if (_movieRequest is not null)
|
||||||
|
{
|
||||||
|
await MoviesWebAPIService.PostMovie(_movieRequest, data => SuccessPost(data.Id), BadRequest);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await SeriesWebAPIService.PostSeries(_seriesRequest, data => SuccessPost(data.Id), BadRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_movieRequest is not null)
|
||||||
|
{
|
||||||
|
await MoviesWebAPIService.PutMovie(Id.Value, _movieRequest, () => _basicDataSaving = false, BadRequest);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await SeriesWebAPIService.PutSeries(Id.Value, _seriesRequest, () => _basicDataSaving = false, BadRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -24,14 +24,14 @@ else
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="container-fluid p-1 gy-2 gx-2">
|
<div class="container-fluid">
|
||||||
@if (_loaded)
|
@if (_loaded)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(_error))
|
if (string.IsNullOrWhiteSpace(_error))
|
||||||
{
|
{
|
||||||
<div class="row mt-9">
|
<div class="row mt-9">
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<img class="rounded-2 shadow" src="@(_poster is not null ? $"data:{_poster.MimeType};base64,{Encoding.UTF8.GetString(_poster.Image)}" : "assets/poster.png")" alt="poster" width="200" height="333"/>
|
<img class="rounded-2 shadow object-fit-cover" src="@(_poster is not null ? _poster.ToString() : "assets/poster.png")" alt="poster" width="200" height="333"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="d-flex h-100">
|
<div class="d-flex h-100">
|
||||||
@@ -187,18 +187,12 @@ else
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<div class="row">
|
<ErrorComponent ErrorMessage="@_error"/>
|
||||||
<div class="col rounded-3 panel panel-regular m-1">
|
|
||||||
<div>
|
|
||||||
<h2 class="text-danger">@_error</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<LoadingPageComponent/>
|
<LoadingComponent/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ public partial class MediaPage : ComponentBase
|
|||||||
|
|
||||||
#region PARAMETERS
|
#region PARAMETERS
|
||||||
|
|
||||||
[Parameter]
|
[Parameter] public long Id { get; set; }
|
||||||
public long Id { get; set; }
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
@page "/movies/new"
|
|
||||||
@page "/movies/{id:long}/edit"
|
|
||||||
|
|
||||||
<PageTitle>WatchIt - New movie</PageTitle>
|
|
||||||
|
|
||||||
@if (_loaded)
|
|
||||||
{
|
|
||||||
<div class="container-fluid">
|
|
||||||
@if (_authenticated)
|
|
||||||
{
|
|
||||||
<div class="row">
|
|
||||||
<div class="col rounded-3 panel panel-regular m-1">
|
|
||||||
<h2>@(Id is null ? "Create new movie" : $"Edit movie \"{_movieInfo.Title}\"")</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<MediaFormComponent Data=@(_movieData) SaveDataAction=SaveData Id=@(Id) SaveDataErrors=@(_movieDataErrors) SaveDataInfo=@(_movieDataInfo)/>
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
<NoPermissionComponent/>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using Microsoft.AspNetCore.Components;
|
|
||||||
using WatchIt.Common.Model.Movies;
|
|
||||||
using WatchIt.Website.Services.Utility.Authentication;
|
|
||||||
using WatchIt.Website.Services.WebAPI.Movies;
|
|
||||||
|
|
||||||
namespace WatchIt.Website.Pages;
|
|
||||||
|
|
||||||
public partial class MovieEditPage : ComponentBase
|
|
||||||
{
|
|
||||||
#region SERVICES
|
|
||||||
|
|
||||||
[Inject] public NavigationManager NavigationManager { get; set; } = default!;
|
|
||||||
[Inject] public IAuthenticationService AuthenticationService { get; set; } = default!;
|
|
||||||
[Inject] public IMoviesWebAPIService MoviesWebAPIService { get; set; } = default!;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region PARAMETERS
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public long? Id { get; set; }
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region FIELDS
|
|
||||||
|
|
||||||
private bool _loaded = false;
|
|
||||||
private bool _authenticated = false;
|
|
||||||
|
|
||||||
private MovieResponse? _movieInfo = null;
|
|
||||||
|
|
||||||
private MovieRequest _movieData = new MovieRequest { Title = string.Empty };
|
|
||||||
private IEnumerable<string>? _movieDataErrors = null;
|
|
||||||
private string? _movieDataInfo = null;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region PRIVATE METHODS
|
|
||||||
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
||||||
{
|
|
||||||
if (firstRender)
|
|
||||||
{
|
|
||||||
User? user = await AuthenticationService.GetUserAsync();
|
|
||||||
if (user is not null && user.IsAdmin)
|
|
||||||
{
|
|
||||||
_authenticated = true;
|
|
||||||
|
|
||||||
await LoadData();
|
|
||||||
}
|
|
||||||
_loaded = true;
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LoadData()
|
|
||||||
{
|
|
||||||
if (Id is not null)
|
|
||||||
{
|
|
||||||
await MoviesWebAPIService.GetMovie(Id.Value, GetSuccessAction, NoIdAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
void GetSuccessAction(MovieResponse data)
|
|
||||||
{
|
|
||||||
_movieInfo = data;
|
|
||||||
_movieData = new MovieRequest(_movieInfo);
|
|
||||||
}
|
|
||||||
void NoIdAction() => NavigationManager.NavigateTo("/movies/new", true); // await for all
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SaveData()
|
|
||||||
{
|
|
||||||
_movieDataErrors = null;
|
|
||||||
_movieDataInfo = null;
|
|
||||||
if (Id is null)
|
|
||||||
{
|
|
||||||
await MoviesWebAPIService.PostMovie(_movieData, PostSuccessAction, BadRequestAction, NoPermissionsAction, NoPermissionsAction);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await MoviesWebAPIService.PutMovie(Id.Value, _movieData, PutSuccessAction, BadRequestAction, NoPermissionsAction, NoPermissionsAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
void PutSuccessAction() => _movieDataInfo = "Data saved";
|
|
||||||
void PostSuccessAction(MovieResponse data) => NavigationManager.NavigateTo($"/movies/{data.Id}/edit", true);
|
|
||||||
void BadRequestAction(IDictionary<string, string[]> errors) => _movieDataErrors = errors.SelectMany(x => x.Value);
|
|
||||||
void NoPermissionsAction() => NavigationManager.Refresh(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
@@ -8,6 +8,7 @@ using WatchIt.Website.Services.Utility.Tokens;
|
|||||||
using WatchIt.Website.Services.WebAPI.Accounts;
|
using WatchIt.Website.Services.WebAPI.Accounts;
|
||||||
using WatchIt.Website.Services.WebAPI.Media;
|
using WatchIt.Website.Services.WebAPI.Media;
|
||||||
using WatchIt.Website.Services.WebAPI.Movies;
|
using WatchIt.Website.Services.WebAPI.Movies;
|
||||||
|
using WatchIt.Website.Services.WebAPI.Series;
|
||||||
|
|
||||||
namespace WatchIt.Website;
|
namespace WatchIt.Website;
|
||||||
|
|
||||||
@@ -62,6 +63,7 @@ public static class Program
|
|||||||
builder.Services.AddScoped<IAccountsWebAPIService, AccountsWebAPIService>();
|
builder.Services.AddScoped<IAccountsWebAPIService, AccountsWebAPIService>();
|
||||||
builder.Services.AddSingleton<IMediaWebAPIService, MediaWebAPIService>();
|
builder.Services.AddSingleton<IMediaWebAPIService, MediaWebAPIService>();
|
||||||
builder.Services.AddSingleton<IMoviesWebAPIService, MoviesWebAPIService>();
|
builder.Services.AddSingleton<IMoviesWebAPIService, MoviesWebAPIService>();
|
||||||
|
builder.Services.AddSingleton<ISeriesWebAPIService, SeriesWebAPIService>();
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
<ProjectReference Include="..\WatchIt.Website.Services\WatchIt.Website.Services.WebAPI\WatchIt.Website.Services.WebAPI.Accounts\WatchIt.Website.Services.WebAPI.Accounts.csproj" />
|
<ProjectReference Include="..\WatchIt.Website.Services\WatchIt.Website.Services.WebAPI\WatchIt.Website.Services.WebAPI.Accounts\WatchIt.Website.Services.WebAPI.Accounts.csproj" />
|
||||||
<ProjectReference Include="..\WatchIt.Website.Services\WatchIt.Website.Services.WebAPI\WatchIt.Website.Services.WebAPI.Media\WatchIt.Website.Services.WebAPI.Media.csproj" />
|
<ProjectReference Include="..\WatchIt.Website.Services\WatchIt.Website.Services.WebAPI\WatchIt.Website.Services.WebAPI.Media\WatchIt.Website.Services.WebAPI.Media.csproj" />
|
||||||
<ProjectReference Include="..\WatchIt.Website.Services\WatchIt.Website.Services.WebAPI\WatchIt.Website.Services.WebAPI.Movies\WatchIt.Website.Services.WebAPI.Movies.csproj" />
|
<ProjectReference Include="..\WatchIt.Website.Services\WatchIt.Website.Services.WebAPI\WatchIt.Website.Services.WebAPI.Movies\WatchIt.Website.Services.WebAPI.Movies.csproj" />
|
||||||
|
<ProjectReference Include="..\WatchIt.Website.Services\WatchIt.Website.Services.WebAPI\WatchIt.Website.Services.WebAPI.Series\WatchIt.Website.Services.WebAPI.Series.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
Reference in New Issue
Block a user