mirror of
https://github.com/claunia/marechai.git
synced 2026-07-08 17:57:08 +00:00
Add YouTube video management functionality
- Implemented YouTubeUrlParser to extract video IDs from various YouTube URL formats. - Added a new SoftwareVideos page for managing videos associated with software. - Introduced methods in SoftwareService for creating, updating, and deleting video entries. - Enhanced the Software admin page with a button to navigate to the video management section. - Updated localization files to include new strings related to video management in multiple languages.
This commit is contained in:
85
Marechai.ApiClient/Models/CreateSoftwareVideoRequest.cs
Normal file
85
Marechai.ApiClient/Models/CreateSoftwareVideoRequest.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
// <auto-generated/>
|
||||
#pragma warning disable CS0618
|
||||
using Microsoft.Kiota.Abstractions.Extensions;
|
||||
using Microsoft.Kiota.Abstractions.Serialization;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System;
|
||||
namespace Marechai.ApiClient.Models
|
||||
{
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
#pragma warning disable CS1591
|
||||
public partial class CreateSoftwareVideoRequest : IAdditionalDataHolder, IParsable
|
||||
#pragma warning restore CS1591
|
||||
{
|
||||
/// <summary>Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well.</summary>
|
||||
public IDictionary<string, object> AdditionalData { get; set; }
|
||||
/// <summary>The provider property</summary>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public string? Provider { get; set; }
|
||||
#nullable restore
|
||||
#else
|
||||
public string Provider { get; set; }
|
||||
#endif
|
||||
/// <summary>The title property</summary>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public string? Title { get; set; }
|
||||
#nullable restore
|
||||
#else
|
||||
public string Title { get; set; }
|
||||
#endif
|
||||
/// <summary>The video_id property</summary>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public string? VideoId { get; set; }
|
||||
#nullable restore
|
||||
#else
|
||||
public string VideoId { get; set; }
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.ApiClient.Models.CreateSoftwareVideoRequest"/> and sets the default values.
|
||||
/// </summary>
|
||||
public CreateSoftwareVideoRequest()
|
||||
{
|
||||
AdditionalData = new Dictionary<string, object>();
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new instance of the appropriate class based on discriminator value
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="global::Marechai.ApiClient.Models.CreateSoftwareVideoRequest"/></returns>
|
||||
/// <param name="parseNode">The parse node to use to read the discriminator value and create the object</param>
|
||||
public static global::Marechai.ApiClient.Models.CreateSoftwareVideoRequest CreateFromDiscriminatorValue(IParseNode parseNode)
|
||||
{
|
||||
if(ReferenceEquals(parseNode, null)) throw new ArgumentNullException(nameof(parseNode));
|
||||
return new global::Marechai.ApiClient.Models.CreateSoftwareVideoRequest();
|
||||
}
|
||||
/// <summary>
|
||||
/// The deserialization information for the current model
|
||||
/// </summary>
|
||||
/// <returns>A IDictionary<string, Action<IParseNode>></returns>
|
||||
public virtual IDictionary<string, Action<IParseNode>> GetFieldDeserializers()
|
||||
{
|
||||
return new Dictionary<string, Action<IParseNode>>
|
||||
{
|
||||
{ "provider", n => { Provider = n.GetStringValue(); } },
|
||||
{ "title", n => { Title = n.GetStringValue(); } },
|
||||
{ "video_id", n => { VideoId = n.GetStringValue(); } },
|
||||
};
|
||||
}
|
||||
/// <summary>
|
||||
/// Serializes information the current object
|
||||
/// </summary>
|
||||
/// <param name="writer">Serialization writer to use to serialize this model</param>
|
||||
public virtual void Serialize(ISerializationWriter writer)
|
||||
{
|
||||
if(ReferenceEquals(writer, null)) throw new ArgumentNullException(nameof(writer));
|
||||
writer.WriteStringValue("provider", Provider);
|
||||
writer.WriteStringValue("title", Title);
|
||||
writer.WriteStringValue("video_id", VideoId);
|
||||
writer.WriteAdditionalData(AdditionalData);
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
65
Marechai.ApiClient/Models/UpdateSoftwareVideoRequest.cs
Normal file
65
Marechai.ApiClient/Models/UpdateSoftwareVideoRequest.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
// <auto-generated/>
|
||||
#pragma warning disable CS0618
|
||||
using Microsoft.Kiota.Abstractions.Extensions;
|
||||
using Microsoft.Kiota.Abstractions.Serialization;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System;
|
||||
namespace Marechai.ApiClient.Models
|
||||
{
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
#pragma warning disable CS1591
|
||||
public partial class UpdateSoftwareVideoRequest : IAdditionalDataHolder, IParsable
|
||||
#pragma warning restore CS1591
|
||||
{
|
||||
/// <summary>Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well.</summary>
|
||||
public IDictionary<string, object> AdditionalData { get; set; }
|
||||
/// <summary>The title property</summary>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public string? Title { get; set; }
|
||||
#nullable restore
|
||||
#else
|
||||
public string Title { get; set; }
|
||||
#endif
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="global::Marechai.ApiClient.Models.UpdateSoftwareVideoRequest"/> and sets the default values.
|
||||
/// </summary>
|
||||
public UpdateSoftwareVideoRequest()
|
||||
{
|
||||
AdditionalData = new Dictionary<string, object>();
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a new instance of the appropriate class based on discriminator value
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="global::Marechai.ApiClient.Models.UpdateSoftwareVideoRequest"/></returns>
|
||||
/// <param name="parseNode">The parse node to use to read the discriminator value and create the object</param>
|
||||
public static global::Marechai.ApiClient.Models.UpdateSoftwareVideoRequest CreateFromDiscriminatorValue(IParseNode parseNode)
|
||||
{
|
||||
if(ReferenceEquals(parseNode, null)) throw new ArgumentNullException(nameof(parseNode));
|
||||
return new global::Marechai.ApiClient.Models.UpdateSoftwareVideoRequest();
|
||||
}
|
||||
/// <summary>
|
||||
/// The deserialization information for the current model
|
||||
/// </summary>
|
||||
/// <returns>A IDictionary<string, Action<IParseNode>></returns>
|
||||
public virtual IDictionary<string, Action<IParseNode>> GetFieldDeserializers()
|
||||
{
|
||||
return new Dictionary<string, Action<IParseNode>>
|
||||
{
|
||||
{ "title", n => { Title = n.GetStringValue(); } },
|
||||
};
|
||||
}
|
||||
/// <summary>
|
||||
/// Serializes information the current object
|
||||
/// </summary>
|
||||
/// <param name="writer">Serialization writer to use to serialize this model</param>
|
||||
public virtual void Serialize(ISerializationWriter writer)
|
||||
{
|
||||
if(ReferenceEquals(writer, null)) throw new ArgumentNullException(nameof(writer));
|
||||
writer.WriteStringValue("title", Title);
|
||||
writer.WriteAdditionalData(AdditionalData);
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
@@ -60,14 +60,16 @@ namespace Marechai.ApiClient.Software.Item.Videos
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 400 status code</exception>
|
||||
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 401 status code</exception>
|
||||
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 403 status code</exception>
|
||||
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 404 status code</exception>
|
||||
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 409 status code</exception>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public async Task<global::Marechai.ApiClient.Models.SoftwareVideoDto?> PostAsync(global::Marechai.ApiClient.Models.SoftwareVideoDto body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
public async Task<global::Marechai.ApiClient.Models.SoftwareVideoDto?> PostAsync(global::Marechai.ApiClient.Models.CreateSoftwareVideoRequest body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public async Task<global::Marechai.ApiClient.Models.SoftwareVideoDto> PostAsync(global::Marechai.ApiClient.Models.SoftwareVideoDto body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
public async Task<global::Marechai.ApiClient.Models.SoftwareVideoDto> PostAsync(global::Marechai.ApiClient.Models.CreateSoftwareVideoRequest body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#endif
|
||||
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
|
||||
@@ -76,6 +78,8 @@ namespace Marechai.ApiClient.Software.Item.Videos
|
||||
{
|
||||
{ "400", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
{ "401", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
{ "403", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
{ "404", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
{ "409", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
};
|
||||
return await RequestAdapter.SendAsync<global::Marechai.ApiClient.Models.SoftwareVideoDto>(requestInfo, global::Marechai.ApiClient.Models.SoftwareVideoDto.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
|
||||
@@ -101,11 +105,11 @@ namespace Marechai.ApiClient.Software.Item.Videos
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public RequestInformation ToPostRequestInformation(global::Marechai.ApiClient.Models.SoftwareVideoDto body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
|
||||
public RequestInformation ToPostRequestInformation(global::Marechai.ApiClient.Models.CreateSoftwareVideoRequest body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public RequestInformation ToPostRequestInformation(global::Marechai.ApiClient.Models.SoftwareVideoDto body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
|
||||
public RequestInformation ToPostRequestInformation(global::Marechai.ApiClient.Models.CreateSoftwareVideoRequest body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
|
||||
{
|
||||
#endif
|
||||
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace Marechai.ApiClient.Software.Videos.Item
|
||||
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 401 status code</exception>
|
||||
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 403 status code</exception>
|
||||
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 404 status code</exception>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
@@ -50,6 +51,7 @@ namespace Marechai.ApiClient.Software.Videos.Item
|
||||
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
|
||||
{
|
||||
{ "401", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
{ "403", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
{ "404", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
};
|
||||
await RequestAdapter.SendNoContentAsync(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
|
||||
@@ -74,6 +76,33 @@ namespace Marechai.ApiClient.Software.Videos.Item
|
||||
};
|
||||
return await RequestAdapter.SendAsync<global::Marechai.ApiClient.Models.SoftwareVideoDto>(requestInfo, global::Marechai.ApiClient.Models.SoftwareVideoDto.CreateFromDiscriminatorValue, errorMapping, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
/// <param name="body">The request body</param>
|
||||
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 400 status code</exception>
|
||||
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 401 status code</exception>
|
||||
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 403 status code</exception>
|
||||
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 404 status code</exception>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public async Task PutAsync(global::Marechai.ApiClient.Models.UpdateSoftwareVideoRequest body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public async Task PutAsync(global::Marechai.ApiClient.Models.UpdateSoftwareVideoRequest body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
|
||||
{
|
||||
#endif
|
||||
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
|
||||
var requestInfo = ToPutRequestInformation(body, requestConfiguration);
|
||||
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
|
||||
{
|
||||
{ "400", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
{ "401", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
{ "403", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
{ "404", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
|
||||
};
|
||||
await RequestAdapter.SendNoContentAsync(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
/// <returns>A <see cref="RequestInformation"/></returns>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
@@ -106,6 +135,25 @@ namespace Marechai.ApiClient.Software.Videos.Item
|
||||
requestInfo.Headers.TryAdd("Accept", "application/json, text/plain;q=0.9");
|
||||
return requestInfo;
|
||||
}
|
||||
/// <returns>A <see cref="RequestInformation"/></returns>
|
||||
/// <param name="body">The request body</param>
|
||||
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public RequestInformation ToPutRequestInformation(global::Marechai.ApiClient.Models.UpdateSoftwareVideoRequest body, Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
|
||||
{
|
||||
#nullable restore
|
||||
#else
|
||||
public RequestInformation ToPutRequestInformation(global::Marechai.ApiClient.Models.UpdateSoftwareVideoRequest body, Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
|
||||
{
|
||||
#endif
|
||||
if(ReferenceEquals(body, null)) throw new ArgumentNullException(nameof(body));
|
||||
var requestInfo = new RequestInformation(Method.PUT, UrlTemplate, PathParameters);
|
||||
requestInfo.Configure(requestConfiguration);
|
||||
requestInfo.Headers.TryAdd("Accept", "application/json, text/plain;q=0.9");
|
||||
requestInfo.SetContentFromParsable(RequestAdapter, "application/json", body);
|
||||
return requestInfo;
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
|
||||
/// </summary>
|
||||
@@ -131,6 +179,14 @@ namespace Marechai.ApiClient.Software.Videos.Item
|
||||
public partial class VideosItemRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Configuration for the request such as headers, query parameters, and middleware options.
|
||||
/// </summary>
|
||||
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
|
||||
public partial class VideosItemRequestBuilderPutRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma warning restore CS0618
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"descriptionHash": "9CD33AD0F35A1022CC152B32FF35423674776116946B4399403175D7110CDDD843BF49C2C55C3C8F5E7C2704DB6C7ED36E9EF850622E1F9C5507CBB2B71EAE2D",
|
||||
"descriptionHash": "1D3DD10AC7F51E85403ABA2CCE8E12CF73B2B0FF44AE722917D1F577BD0CDA0E62E9E38E057F8CC57899B8390B95EA034BB3B25025C7EACA39C7A0BBAC24F1E6",
|
||||
"descriptionLocation": "../../../../../tmp/openapi.json",
|
||||
"lockFileVersion": "1.0.0",
|
||||
"kiotaVersion": "1.31.1",
|
||||
|
||||
46
Marechai.Data/Dtos/CreateSoftwareVideoRequest.cs
Normal file
46
Marechai.Data/Dtos/CreateSoftwareVideoRequest.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
/******************************************************************************
|
||||
// MARECHAI: Master repository of computing history artifacts information
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2003-2026 Natalia Portillo
|
||||
*******************************************************************************/
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Marechai.Data.Dtos;
|
||||
|
||||
public class CreateSoftwareVideoRequest
|
||||
{
|
||||
[Required]
|
||||
[StringLength(32)]
|
||||
[JsonPropertyName("provider")]
|
||||
public string Provider { get; set; } = null!;
|
||||
|
||||
[Required]
|
||||
[StringLength(64)]
|
||||
[JsonPropertyName("video_id")]
|
||||
public string VideoId { get; set; } = null!;
|
||||
|
||||
[StringLength(512)]
|
||||
[JsonPropertyName("title")]
|
||||
public string? Title { get; set; }
|
||||
}
|
||||
36
Marechai.Data/Dtos/UpdateSoftwareVideoRequest.cs
Normal file
36
Marechai.Data/Dtos/UpdateSoftwareVideoRequest.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
/******************************************************************************
|
||||
// MARECHAI: Master repository of computing history artifacts information
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2003-2026 Natalia Portillo
|
||||
*******************************************************************************/
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Marechai.Data.Dtos;
|
||||
|
||||
public class UpdateSoftwareVideoRequest
|
||||
{
|
||||
[StringLength(512)]
|
||||
[JsonPropertyName("title")]
|
||||
public string? Title { get; set; }
|
||||
}
|
||||
@@ -87,40 +87,90 @@ public class SoftwareVideosController(MarechaiContext context) : ControllerBase
|
||||
[ProducesResponseType(StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||||
public async Task<ActionResult<SoftwareVideoDto>> CreateAsync(ulong softwareId, [FromBody] SoftwareVideoDto dto)
|
||||
public async Task<ActionResult<SoftwareVideoDto>> CreateAsync(ulong softwareId,
|
||||
[FromBody] CreateSoftwareVideoRequest request)
|
||||
{
|
||||
string userId = User.FindFirstValue(ClaimTypes.Sid);
|
||||
|
||||
if(userId is null) return Unauthorized();
|
||||
|
||||
if(string.IsNullOrWhiteSpace(request.Provider) || string.IsNullOrWhiteSpace(request.VideoId))
|
||||
return BadRequest("Provider and Video ID are required.");
|
||||
|
||||
string provider = request.Provider.Trim();
|
||||
string videoId = request.VideoId.Trim();
|
||||
string title = string.IsNullOrWhiteSpace(request.Title) ? null : request.Title.Trim();
|
||||
|
||||
bool softwareExists = await context.Softwares.AnyAsync(s => s.Id == softwareId);
|
||||
|
||||
if(!softwareExists) return NotFound();
|
||||
|
||||
bool exists = await context.SoftwareVideos.AnyAsync(v => v.SoftwareId == softwareId &&
|
||||
v.Provider == dto.Provider &&
|
||||
v.VideoId == dto.VideoId);
|
||||
v.Provider == provider &&
|
||||
v.VideoId == videoId);
|
||||
|
||||
if(exists) return Conflict("This video is already linked to this software.");
|
||||
|
||||
var model = new SoftwareVideo
|
||||
{
|
||||
SoftwareId = softwareId,
|
||||
Provider = dto.Provider,
|
||||
VideoId = dto.VideoId,
|
||||
Title = dto.Title
|
||||
Provider = provider,
|
||||
VideoId = videoId,
|
||||
Title = title
|
||||
};
|
||||
|
||||
context.SoftwareVideos.Add(model);
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
dto.Id = model.Id;
|
||||
dto.SoftwareId = softwareId;
|
||||
string softwareName = await context.Softwares.Where(s => s.Id == softwareId)
|
||||
.Select(s => s.Name)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
var dto = new SoftwareVideoDto
|
||||
{
|
||||
Id = model.Id,
|
||||
SoftwareId = softwareId,
|
||||
SoftwareName = softwareName,
|
||||
Provider = provider,
|
||||
VideoId = videoId,
|
||||
Title = title
|
||||
};
|
||||
|
||||
return CreatedAtAction(nameof(GetAsync), new { id = model.Id }, dto);
|
||||
}
|
||||
|
||||
[HttpPut("{id:long}")]
|
||||
[Authorize(Roles = "Admin,UberAdmin")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> UpdateAsync(long id, [FromBody] UpdateSoftwareVideoRequest request)
|
||||
{
|
||||
string userId = User.FindFirstValue(ClaimTypes.Sid);
|
||||
|
||||
if(userId is null) return Unauthorized();
|
||||
|
||||
SoftwareVideo video = await context.SoftwareVideos.FindAsync(id);
|
||||
|
||||
if(video is null) return NotFound();
|
||||
|
||||
video.Title = string.IsNullOrWhiteSpace(request.Title) ? null : request.Title.Trim();
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpDelete("{id:long}")]
|
||||
[Authorize(Roles = "Admin,UberAdmin")]
|
||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> DeleteAsync(long id)
|
||||
{
|
||||
|
||||
72
Marechai/Helpers/YouTubeUrlParser.cs
Normal file
72
Marechai/Helpers/YouTubeUrlParser.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
/******************************************************************************
|
||||
// MARECHAI: Master repository of computing history artifacts information
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2003-2026 Natalia Portillo
|
||||
*******************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Marechai.Helpers;
|
||||
|
||||
public static partial class YouTubeUrlParser
|
||||
{
|
||||
[GeneratedRegex(@"^(?:https?://)?(?:www\.|m\.|music\.)?(?:youtube\.com|youtube-nocookie\.com)/(?:watch\?(?:[^&\s]*&)*v=|embed/|v/|shorts/|live/)([A-Za-z0-9_-]{11})",
|
||||
RegexOptions.IgnoreCase)]
|
||||
private static partial Regex YouTubeFullUrlRegex();
|
||||
|
||||
[GeneratedRegex(@"^(?:https?://)?youtu\.be/([A-Za-z0-9_-]{11})", RegexOptions.IgnoreCase)]
|
||||
private static partial Regex YouTubeShortUrlRegex();
|
||||
|
||||
[GeneratedRegex(@"^[A-Za-z0-9_-]{11}$")]
|
||||
private static partial Regex BareIdRegex();
|
||||
|
||||
/// <summary>
|
||||
/// Tries to extract a YouTube video ID from a URL or a bare 11-character ID. Accepts youtube.com/watch?v=,
|
||||
/// youtu.be/, youtube.com/embed/, youtube.com/shorts/, youtube.com/live/, and youtube-nocookie.com URLs.
|
||||
/// </summary>
|
||||
public static bool TryExtract(string input, out string videoId)
|
||||
{
|
||||
videoId = null;
|
||||
|
||||
if(string.IsNullOrWhiteSpace(input)) return false;
|
||||
|
||||
string trimmed = input.Trim();
|
||||
|
||||
Match match = YouTubeFullUrlRegex().Match(trimmed);
|
||||
|
||||
if(!match.Success) match = YouTubeShortUrlRegex().Match(trimmed);
|
||||
|
||||
if(match.Success)
|
||||
{
|
||||
videoId = match.Groups[1].Value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!BareIdRegex().IsMatch(trimmed)) return false;
|
||||
|
||||
videoId = trimmed;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,8 @@
|
||||
|
||||
<MudTooltip Text="@L["Promo Art"]"><MudIconButton Icon="@Icons.Material.Filled.PhotoLibrary" Size="Size.Small" OnClick="() => NavigateToPromoArt(context.Item)"/></MudTooltip>
|
||||
|
||||
<MudTooltip Text="@L["Videos"]"><MudIconButton Icon="@Icons.Material.Filled.VideoLibrary" Size="Size.Small" OnClick="() => NavigateToVideos(context.Item)"/></MudTooltip>
|
||||
|
||||
<MudTooltip Text="@L["Merge"]"><MudIconButton Icon="@Icons.Material.Filled.MergeType" Size="Size.Small" Color="Color.Warning" OnClick="() => OpenMergeDialog(context.Item)"/></MudTooltip>
|
||||
|
||||
<MudTooltip Text="@L["Delete"]"><MudIconButton Icon="@Icons.Material.Filled.Delete" Size="Size.Small" Color="Color.Error" OnClick="() => ConfirmDelete(context.Item)"/></MudTooltip>
|
||||
|
||||
@@ -64,6 +64,9 @@ public partial class Software
|
||||
void NavigateToPromoArt(SoftwareDto software) =>
|
||||
NavigationManager.NavigateTo($"/admin/software/{software.Id}/promo-art");
|
||||
|
||||
void NavigateToVideos(SoftwareDto software) =>
|
||||
NavigationManager.NavigateTo($"/admin/software/{software.Id}/videos");
|
||||
|
||||
async Task OpenDescriptionsDialog(SoftwareDto software)
|
||||
{
|
||||
DialogParameters<SoftwareDescriptionDialog> parameters = new()
|
||||
|
||||
164
Marechai/Pages/Admin/SoftwareVideos.razor
Normal file
164
Marechai/Pages/Admin/SoftwareVideos.razor
Normal file
@@ -0,0 +1,164 @@
|
||||
@page "/admin/software/{SoftwareId:int}/videos"
|
||||
@attribute [Authorize(Roles = "Admin, UberAdmin")]
|
||||
@using Marechai.ApiClient.Models
|
||||
@using Marechai.Services
|
||||
@inject SoftwareService SoftwareService
|
||||
@inject IStringLocalizer<SoftwareService> L
|
||||
@inject IDialogService DialogService
|
||||
@inject NavigationManager NavigationManager
|
||||
|
||||
<MudButton Variant="Variant.Text" StartIcon="@Icons.Material.Filled.ArrowBack" OnClick="@(() => NavigationManager.NavigateTo("/admin/software"))" Class="mb-4">@L["Back to Software"]</MudButton>
|
||||
|
||||
<MudPaper Class="pa-6 mb-6" Elevation="0" Style="background: linear-gradient(135deg, #b71c1c 0%, #e53935 100%); border-radius: 16px;">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="3">
|
||||
<MudIcon Icon="@Icons.Material.Filled.VideoLibrary" Size="Size.Large" Style="color: white;"/>
|
||||
<MudText Typo="Typo.h4" Style="color: white;">@L["Video Management"]</MudText>
|
||||
</MudStack>
|
||||
<MudText Typo="Typo.subtitle1" Style="color: rgba(255,255,255,0.85);" Class="mt-2">@(_softwareName ?? L["Manage software videos."])</MudText>
|
||||
</MudPaper>
|
||||
|
||||
@if(!string.IsNullOrWhiteSpace(_errorMessage))
|
||||
{
|
||||
<MudAlert Severity="Severity.Error" Class="mb-4" ShowCloseIcon="true" CloseIconClicked="() => _errorMessage = null">@_errorMessage</MudAlert>
|
||||
}
|
||||
|
||||
@if(!string.IsNullOrWhiteSpace(_successMessage))
|
||||
{
|
||||
<MudAlert Severity="Severity.Success" Class="mb-4" ShowCloseIcon="true" CloseIconClicked="() => _successMessage = null">@_successMessage</MudAlert>
|
||||
}
|
||||
|
||||
@if(_isLoading)
|
||||
{
|
||||
<div class="d-flex justify-center pa-8">
|
||||
<MudProgressCircular Color="Color.Primary" Indeterminate="true" Size="Size.Large"/>
|
||||
</div>
|
||||
return;
|
||||
}
|
||||
|
||||
@* ── Add Video Section ── *@
|
||||
<MudPaper Elevation="2" Class="pa-4 mb-6 rounded-lg">
|
||||
<MudText Typo="Typo.h6" Class="mb-3">@L["Link Video"]</MudText>
|
||||
<MudStack Spacing="3">
|
||||
<MudTextField @bind-Value="_urlInput"
|
||||
@bind-Value:after="OnUrlInputChanged"
|
||||
Label="@L["YouTube URL or Video ID"]"
|
||||
Variant="Variant.Outlined"
|
||||
Placeholder="https://www.youtube.com/watch?v=..."
|
||||
Immediate="true"
|
||||
DebounceInterval="200"/>
|
||||
|
||||
@if(!string.IsNullOrWhiteSpace(_detectedVideoId))
|
||||
{
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
|
||||
<MudIcon Icon="@Icons.Material.Filled.CheckCircle" Color="Color.Success"/>
|
||||
<MudText Typo="Typo.body2">@L["Detected Video ID"]:</MudText>
|
||||
<MudChip T="string" Size="Size.Small" Color="Color.Success">@_detectedVideoId</MudChip>
|
||||
</MudStack>
|
||||
}
|
||||
|
||||
<MudTextField @bind-Value="_title"
|
||||
Label="@L["Video Title"]"
|
||||
Variant="Variant.Outlined"
|
||||
MaxLength="512"/>
|
||||
|
||||
<MudExpansionPanels Elevation="0">
|
||||
<MudExpansionPanel Text="@L["Advanced"]">
|
||||
<MudStack Spacing="3">
|
||||
<MudTextField @bind-Value="_provider"
|
||||
Label="@L["Provider"]"
|
||||
Variant="Variant.Outlined"
|
||||
HelperText="@L["Provider name (e.g. YouTube)."]"
|
||||
MaxLength="32"/>
|
||||
<MudTextField @bind-Value="_manualVideoId"
|
||||
Label="@L["Video ID"]"
|
||||
Variant="Variant.Outlined"
|
||||
HelperText="@L["Use this when the URL above cannot be parsed (non-YouTube providers)."]"
|
||||
MaxLength="64"/>
|
||||
</MudStack>
|
||||
</MudExpansionPanel>
|
||||
</MudExpansionPanels>
|
||||
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.Link"
|
||||
OnClick="LinkVideoAsync"
|
||||
Disabled="_isSaving || !CanLink">
|
||||
@if(_isSaving)
|
||||
{
|
||||
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2"/>
|
||||
@L["Saving..."]
|
||||
}
|
||||
else
|
||||
{
|
||||
@L["Link Video"]
|
||||
}
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
|
||||
@* ── Videos Grid ── *@
|
||||
<MudPaper Elevation="2" Class="rounded-lg">
|
||||
<MudToolBar Dense="true">
|
||||
<MudText Typo="Typo.h6">@L["Videos"]</MudText>
|
||||
@if(_videos is not null)
|
||||
{
|
||||
<MudChip T="string" Size="Size.Small" Color="Color.Secondary" Class="ml-2">@_videos.Count</MudChip>
|
||||
}
|
||||
<MudSpacer/>
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Refresh" OnClick="LoadDataAsync"/>
|
||||
</MudToolBar>
|
||||
|
||||
@if(_videos is null || _videos.Count == 0)
|
||||
{
|
||||
<MudAlert Severity="Severity.Info" Class="ma-4">@L["No videos found for this software."]</MudAlert>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudGrid Class="pa-4">
|
||||
@foreach(SoftwareVideoDto video in _videos)
|
||||
{
|
||||
<MudItem xs="12" sm="6" md="4">
|
||||
<MudCard Elevation="1" Class="rounded-lg">
|
||||
<MudCardContent Class="pa-2">
|
||||
@if(string.Equals(video.Provider, "YouTube", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
<div style="position: relative; width: 100%; padding-bottom: 56.25%; border-radius: 8px; overflow: hidden;">
|
||||
<iframe src="@($"https://www.youtube-nocookie.com/embed/{video.VideoId}")"
|
||||
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowfullscreen
|
||||
loading="lazy"
|
||||
referrerpolicy="no-referrer-when-downgrade"></iframe>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined" Dense="true">
|
||||
@video.Provider: @video.VideoId
|
||||
</MudAlert>
|
||||
}
|
||||
</MudCardContent>
|
||||
<MudCardContent Class="pa-2">
|
||||
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center">
|
||||
<MudChip T="string" Size="Size.Small" Color="Color.Primary">@video.Provider</MudChip>
|
||||
<MudText Typo="Typo.caption" Class="text-truncate" Style="font-family: monospace;">@video.VideoId</MudText>
|
||||
</MudStack>
|
||||
<MudTextField T="string"
|
||||
Value="@video.Title"
|
||||
ValueChanged="@(t => OnTitleChangedAsync(video, t))"
|
||||
Label="@L["Title"]"
|
||||
Variant="Variant.Outlined"
|
||||
DebounceInterval="500"
|
||||
MaxLength="512"
|
||||
Class="mt-2"/>
|
||||
</MudCardContent>
|
||||
<MudCardActions>
|
||||
<MudButton Variant="Variant.Text" Color="Color.Error" Size="Size.Small"
|
||||
StartIcon="@Icons.Material.Filled.Delete"
|
||||
OnClick="() => ConfirmDeleteAsync(video)">@L["Delete"]</MudButton>
|
||||
</MudCardActions>
|
||||
</MudCard>
|
||||
</MudItem>
|
||||
}
|
||||
</MudGrid>
|
||||
}
|
||||
</MudPaper>
|
||||
168
Marechai/Pages/Admin/SoftwareVideos.razor.cs
Normal file
168
Marechai/Pages/Admin/SoftwareVideos.razor.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
/******************************************************************************
|
||||
// MARECHAI: Master repository of computing history artifacts information
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2003-2026 Natalia Portillo
|
||||
*******************************************************************************/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Marechai.ApiClient.Models;
|
||||
using Marechai.Helpers;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MudBlazor;
|
||||
|
||||
namespace Marechai.Pages.Admin;
|
||||
|
||||
public partial class SoftwareVideos
|
||||
{
|
||||
string _detectedVideoId;
|
||||
string _errorMessage;
|
||||
bool _isLoading = true;
|
||||
bool _isSaving;
|
||||
string _manualVideoId;
|
||||
string _provider = "YouTube";
|
||||
string _softwareName;
|
||||
string _successMessage;
|
||||
string _title;
|
||||
string _urlInput;
|
||||
List<SoftwareVideoDto> _videos;
|
||||
|
||||
[Parameter] public int SoftwareId { get; set; }
|
||||
|
||||
bool CanLink => !string.IsNullOrWhiteSpace(EffectiveProvider) && !string.IsNullOrWhiteSpace(EffectiveVideoId);
|
||||
|
||||
string EffectiveProvider => string.IsNullOrWhiteSpace(_manualVideoId)
|
||||
? "YouTube"
|
||||
: (_provider ?? string.Empty).Trim();
|
||||
|
||||
string EffectiveVideoId => !string.IsNullOrWhiteSpace(_manualVideoId)
|
||||
? _manualVideoId.Trim()
|
||||
: _detectedVideoId;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
SoftwareDto software = await SoftwareService.GetSoftwareByIdAsync(SoftwareId);
|
||||
_softwareName = software?.Name;
|
||||
await LoadDataAsync();
|
||||
}
|
||||
|
||||
async Task LoadDataAsync()
|
||||
{
|
||||
_isLoading = true;
|
||||
_videos = await SoftwareService.GetVideosBySoftwareAsync(SoftwareId);
|
||||
_isLoading = false;
|
||||
}
|
||||
|
||||
void OnUrlInputChanged()
|
||||
{
|
||||
_detectedVideoId = YouTubeUrlParser.TryExtract(_urlInput, out string id) ? id : null;
|
||||
}
|
||||
|
||||
async Task LinkVideoAsync()
|
||||
{
|
||||
_errorMessage = null;
|
||||
_successMessage = null;
|
||||
|
||||
if(!CanLink) return;
|
||||
|
||||
_isSaving = true;
|
||||
|
||||
try
|
||||
{
|
||||
(SoftwareVideoDto dto, string error) = await SoftwareService.CreateVideoAsync(SoftwareId,
|
||||
EffectiveProvider, EffectiveVideoId, string.IsNullOrWhiteSpace(_title) ? null : _title.Trim());
|
||||
|
||||
if(dto is not null)
|
||||
{
|
||||
_successMessage = L["Video linked successfully."];
|
||||
_urlInput = null;
|
||||
_detectedVideoId = null;
|
||||
_manualVideoId = null;
|
||||
_title = null;
|
||||
_provider = "YouTube";
|
||||
await LoadDataAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
_errorMessage = string.IsNullOrWhiteSpace(error) ? L["Failed to link video."] : error;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isSaving = false;
|
||||
}
|
||||
}
|
||||
|
||||
async Task OnTitleChangedAsync(SoftwareVideoDto video, string newTitle)
|
||||
{
|
||||
if(video.Title == newTitle) return;
|
||||
|
||||
(bool succeeded, string error) =
|
||||
await SoftwareService.UpdateVideoTitleAsync(video.Id!.Value, newTitle);
|
||||
|
||||
if(succeeded)
|
||||
{
|
||||
video.Title = newTitle;
|
||||
_successMessage = L["Video updated successfully."];
|
||||
}
|
||||
else
|
||||
{
|
||||
_errorMessage = string.IsNullOrWhiteSpace(error) ? L["Failed to update video."] : error;
|
||||
}
|
||||
}
|
||||
|
||||
async Task ConfirmDeleteAsync(SoftwareVideoDto video)
|
||||
{
|
||||
DialogParameters<DeleteConfirmDialog> parameters = new()
|
||||
{
|
||||
{
|
||||
x => x.ContentText,
|
||||
L["Are you sure you want to delete this video? This action cannot be undone."]
|
||||
}
|
||||
};
|
||||
|
||||
IDialogReference dialog =
|
||||
await DialogService.ShowAsync<DeleteConfirmDialog>(L["Delete Video"], parameters,
|
||||
new DialogOptions
|
||||
{
|
||||
MaxWidth = MaxWidth.ExtraSmall,
|
||||
FullWidth = true
|
||||
});
|
||||
|
||||
DialogResult result = await dialog.Result;
|
||||
|
||||
if(result is not { Canceled: false }) return;
|
||||
|
||||
(bool succeeded, string errorMessage) =
|
||||
await SoftwareService.DeleteVideoAsync(video.Id ?? 0);
|
||||
|
||||
if(succeeded)
|
||||
{
|
||||
_successMessage = L["Video deleted successfully."];
|
||||
await LoadDataAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
_errorMessage = string.IsNullOrWhiteSpace(errorMessage) ? L["Failed to delete video."] : errorMessage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1245,4 +1245,96 @@
|
||||
<value>Werbegrafik erfolgreich gelöscht.</value>
|
||||
<comment>Promo art deleted successfully.</comment>
|
||||
</data>
|
||||
<data name="Videos" xml:space="preserve">
|
||||
<value>Videos</value>
|
||||
<comment>Videos</comment>
|
||||
</data>
|
||||
<data name="Video Management" xml:space="preserve">
|
||||
<value>Videoverwaltung</value>
|
||||
<comment>Video Management</comment>
|
||||
</data>
|
||||
<data name="Manage software videos." xml:space="preserve">
|
||||
<value>Software-Videos verwalten.</value>
|
||||
<comment>Manage software videos.</comment>
|
||||
</data>
|
||||
<data name="Link Video" xml:space="preserve">
|
||||
<value>Video verknüpfen</value>
|
||||
<comment>Link Video</comment>
|
||||
</data>
|
||||
<data name="YouTube URL or Video ID" xml:space="preserve">
|
||||
<value>YouTube-URL oder Video-ID</value>
|
||||
<comment>YouTube URL or Video ID</comment>
|
||||
</data>
|
||||
<data name="Detected Video ID" xml:space="preserve">
|
||||
<value>Erkannte Video-ID</value>
|
||||
<comment>Detected Video ID</comment>
|
||||
</data>
|
||||
<data name="Provider" xml:space="preserve">
|
||||
<value>Anbieter</value>
|
||||
<comment>Provider</comment>
|
||||
</data>
|
||||
<data name="Video ID" xml:space="preserve">
|
||||
<value>Video-ID</value>
|
||||
<comment>Video ID</comment>
|
||||
</data>
|
||||
<data name="Video Title" xml:space="preserve">
|
||||
<value>Videotitel</value>
|
||||
<comment>Video Title</comment>
|
||||
</data>
|
||||
<data name="Title" xml:space="preserve">
|
||||
<value>Titel</value>
|
||||
<comment>Title</comment>
|
||||
</data>
|
||||
<data name="Advanced" xml:space="preserve">
|
||||
<value>Erweitert</value>
|
||||
<comment>Advanced</comment>
|
||||
</data>
|
||||
<data name="Provider name (e.g. YouTube)." xml:space="preserve">
|
||||
<value>Anbietername (z. B. YouTube).</value>
|
||||
<comment>Provider name (e.g. YouTube).</comment>
|
||||
</data>
|
||||
<data name="Use this when the URL above cannot be parsed (non-YouTube providers)." xml:space="preserve">
|
||||
<value>Verwenden Sie dies, wenn die obige URL nicht analysiert werden kann (Nicht-YouTube-Anbieter).</value>
|
||||
<comment>Use this when the URL above cannot be parsed (non-YouTube providers).</comment>
|
||||
</data>
|
||||
<data name="Saving..." xml:space="preserve">
|
||||
<value>Speichern...</value>
|
||||
<comment>Saving...</comment>
|
||||
</data>
|
||||
<data name="No videos found for this software." xml:space="preserve">
|
||||
<value>Keine Videos für diese Software gefunden.</value>
|
||||
<comment>No videos found for this software.</comment>
|
||||
</data>
|
||||
<data name="Delete Video" xml:space="preserve">
|
||||
<value>Video löschen</value>
|
||||
<comment>Delete Video</comment>
|
||||
</data>
|
||||
<data name="Are you sure you want to delete this video? This action cannot be undone." xml:space="preserve">
|
||||
<value>Möchten Sie dieses Video wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.</value>
|
||||
<comment>Are you sure you want to delete this video? This action cannot be undone.</comment>
|
||||
</data>
|
||||
<data name="Video linked successfully." xml:space="preserve">
|
||||
<value>Video erfolgreich verknüpft.</value>
|
||||
<comment>Video linked successfully.</comment>
|
||||
</data>
|
||||
<data name="Video updated successfully." xml:space="preserve">
|
||||
<value>Video erfolgreich aktualisiert.</value>
|
||||
<comment>Video updated successfully.</comment>
|
||||
</data>
|
||||
<data name="Video deleted successfully." xml:space="preserve">
|
||||
<value>Video erfolgreich gelöscht.</value>
|
||||
<comment>Video deleted successfully.</comment>
|
||||
</data>
|
||||
<data name="Failed to link video." xml:space="preserve">
|
||||
<value>Video konnte nicht verknüpft werden.</value>
|
||||
<comment>Failed to link video.</comment>
|
||||
</data>
|
||||
<data name="Failed to update video." xml:space="preserve">
|
||||
<value>Video konnte nicht aktualisiert werden.</value>
|
||||
<comment>Failed to update video.</comment>
|
||||
</data>
|
||||
<data name="Failed to delete video." xml:space="preserve">
|
||||
<value>Video konnte nicht gelöscht werden.</value>
|
||||
<comment>Failed to delete video.</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1139,4 +1139,76 @@
|
||||
<data name="Promo art deleted successfully." xml:space="preserve">
|
||||
<value>Promo art deleted successfully.</value>
|
||||
</data>
|
||||
<data name="Videos" xml:space="preserve">
|
||||
<value>Videos</value>
|
||||
</data>
|
||||
<data name="Video Management" xml:space="preserve">
|
||||
<value>Video Management</value>
|
||||
</data>
|
||||
<data name="Manage software videos." xml:space="preserve">
|
||||
<value>Manage software videos.</value>
|
||||
</data>
|
||||
<data name="Link Video" xml:space="preserve">
|
||||
<value>Link Video</value>
|
||||
</data>
|
||||
<data name="YouTube URL or Video ID" xml:space="preserve">
|
||||
<value>YouTube URL or Video ID</value>
|
||||
</data>
|
||||
<data name="Detected Video ID" xml:space="preserve">
|
||||
<value>Detected Video ID</value>
|
||||
</data>
|
||||
<data name="Provider" xml:space="preserve">
|
||||
<value>Provider</value>
|
||||
</data>
|
||||
<data name="Video ID" xml:space="preserve">
|
||||
<value>Video ID</value>
|
||||
</data>
|
||||
<data name="Video Title" xml:space="preserve">
|
||||
<value>Video Title</value>
|
||||
</data>
|
||||
<data name="Title" xml:space="preserve">
|
||||
<value>Title</value>
|
||||
</data>
|
||||
<data name="Advanced" xml:space="preserve">
|
||||
<value>Advanced</value>
|
||||
</data>
|
||||
<data name="Provider name (e.g. YouTube)." xml:space="preserve">
|
||||
<value>Provider name (e.g. YouTube).</value>
|
||||
</data>
|
||||
<data name="Use this when the URL above cannot be parsed (non-YouTube providers)." xml:space="preserve">
|
||||
<value>Use this when the URL above cannot be parsed (non-YouTube providers).</value>
|
||||
</data>
|
||||
<data name="Saving..." xml:space="preserve">
|
||||
<value>Saving...</value>
|
||||
</data>
|
||||
<data name="No videos found for this software." xml:space="preserve">
|
||||
<value>No videos found for this software.</value>
|
||||
</data>
|
||||
<data name="Delete Video" xml:space="preserve">
|
||||
<value>Delete Video</value>
|
||||
</data>
|
||||
<data name="Are you sure you want to delete this video? This action cannot be undone." xml:space="preserve">
|
||||
<value>Are you sure you want to delete this video? This action cannot be undone.</value>
|
||||
</data>
|
||||
<data name="Video linked successfully." xml:space="preserve">
|
||||
<value>Video linked successfully.</value>
|
||||
</data>
|
||||
<data name="Video updated successfully." xml:space="preserve">
|
||||
<value>Video updated successfully.</value>
|
||||
</data>
|
||||
<data name="Video deleted successfully." xml:space="preserve">
|
||||
<value>Video deleted successfully.</value>
|
||||
</data>
|
||||
<data name="Failed to link video." xml:space="preserve">
|
||||
<value>Failed to link video.</value>
|
||||
</data>
|
||||
<data name="Failed to update video." xml:space="preserve">
|
||||
<value>Failed to update video.</value>
|
||||
</data>
|
||||
<data name="Failed to delete video." xml:space="preserve">
|
||||
<value>Failed to delete video.</value>
|
||||
</data>
|
||||
<data name="Back to Software" xml:space="preserve">
|
||||
<value>Back to Software</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1187,4 +1187,96 @@
|
||||
<value>Arte promocional eliminado correctamente.</value>
|
||||
<comment>Promo art deleted successfully.</comment>
|
||||
</data>
|
||||
<data name="Videos" xml:space="preserve">
|
||||
<value>Vídeos</value>
|
||||
<comment>Videos</comment>
|
||||
</data>
|
||||
<data name="Video Management" xml:space="preserve">
|
||||
<value>Gestión de vídeos</value>
|
||||
<comment>Video Management</comment>
|
||||
</data>
|
||||
<data name="Manage software videos." xml:space="preserve">
|
||||
<value>Gestionar vídeos del software.</value>
|
||||
<comment>Manage software videos.</comment>
|
||||
</data>
|
||||
<data name="Link Video" xml:space="preserve">
|
||||
<value>Vincular vídeo</value>
|
||||
<comment>Link Video</comment>
|
||||
</data>
|
||||
<data name="YouTube URL or Video ID" xml:space="preserve">
|
||||
<value>URL de YouTube o ID del vídeo</value>
|
||||
<comment>YouTube URL or Video ID</comment>
|
||||
</data>
|
||||
<data name="Detected Video ID" xml:space="preserve">
|
||||
<value>ID de vídeo detectado</value>
|
||||
<comment>Detected Video ID</comment>
|
||||
</data>
|
||||
<data name="Provider" xml:space="preserve">
|
||||
<value>Proveedor</value>
|
||||
<comment>Provider</comment>
|
||||
</data>
|
||||
<data name="Video ID" xml:space="preserve">
|
||||
<value>ID del vídeo</value>
|
||||
<comment>Video ID</comment>
|
||||
</data>
|
||||
<data name="Video Title" xml:space="preserve">
|
||||
<value>Título del vídeo</value>
|
||||
<comment>Video Title</comment>
|
||||
</data>
|
||||
<data name="Title" xml:space="preserve">
|
||||
<value>Título</value>
|
||||
<comment>Title</comment>
|
||||
</data>
|
||||
<data name="Advanced" xml:space="preserve">
|
||||
<value>Avanzado</value>
|
||||
<comment>Advanced</comment>
|
||||
</data>
|
||||
<data name="Provider name (e.g. YouTube)." xml:space="preserve">
|
||||
<value>Nombre del proveedor (p. ej., YouTube).</value>
|
||||
<comment>Provider name (e.g. YouTube).</comment>
|
||||
</data>
|
||||
<data name="Use this when the URL above cannot be parsed (non-YouTube providers)." xml:space="preserve">
|
||||
<value>Úselo cuando la URL anterior no pueda analizarse (proveedores distintos de YouTube).</value>
|
||||
<comment>Use this when the URL above cannot be parsed (non-YouTube providers).</comment>
|
||||
</data>
|
||||
<data name="Saving..." xml:space="preserve">
|
||||
<value>Guardando...</value>
|
||||
<comment>Saving...</comment>
|
||||
</data>
|
||||
<data name="No videos found for this software." xml:space="preserve">
|
||||
<value>No se encontraron vídeos para este software.</value>
|
||||
<comment>No videos found for this software.</comment>
|
||||
</data>
|
||||
<data name="Delete Video" xml:space="preserve">
|
||||
<value>Eliminar vídeo</value>
|
||||
<comment>Delete Video</comment>
|
||||
</data>
|
||||
<data name="Are you sure you want to delete this video? This action cannot be undone." xml:space="preserve">
|
||||
<value>¿Está seguro de que desea eliminar este vídeo? Esta acción no se puede deshacer.</value>
|
||||
<comment>Are you sure you want to delete this video? This action cannot be undone.</comment>
|
||||
</data>
|
||||
<data name="Video linked successfully." xml:space="preserve">
|
||||
<value>Vídeo vinculado correctamente.</value>
|
||||
<comment>Video linked successfully.</comment>
|
||||
</data>
|
||||
<data name="Video updated successfully." xml:space="preserve">
|
||||
<value>Vídeo actualizado correctamente.</value>
|
||||
<comment>Video updated successfully.</comment>
|
||||
</data>
|
||||
<data name="Video deleted successfully." xml:space="preserve">
|
||||
<value>Vídeo eliminado correctamente.</value>
|
||||
<comment>Video deleted successfully.</comment>
|
||||
</data>
|
||||
<data name="Failed to link video." xml:space="preserve">
|
||||
<value>No se pudo vincular el vídeo.</value>
|
||||
<comment>Failed to link video.</comment>
|
||||
</data>
|
||||
<data name="Failed to update video." xml:space="preserve">
|
||||
<value>No se pudo actualizar el vídeo.</value>
|
||||
<comment>Failed to update video.</comment>
|
||||
</data>
|
||||
<data name="Failed to delete video." xml:space="preserve">
|
||||
<value>No se pudo eliminar el vídeo.</value>
|
||||
<comment>Failed to delete video.</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1245,4 +1245,96 @@
|
||||
<value>Art promotionnel supprimé avec succès.</value>
|
||||
<comment>Promo art deleted successfully.</comment>
|
||||
</data>
|
||||
<data name="Videos" xml:space="preserve">
|
||||
<value>Vidéos</value>
|
||||
<comment>Videos</comment>
|
||||
</data>
|
||||
<data name="Video Management" xml:space="preserve">
|
||||
<value>Gestion des vidéos</value>
|
||||
<comment>Video Management</comment>
|
||||
</data>
|
||||
<data name="Manage software videos." xml:space="preserve">
|
||||
<value>Gérer les vidéos du logiciel.</value>
|
||||
<comment>Manage software videos.</comment>
|
||||
</data>
|
||||
<data name="Link Video" xml:space="preserve">
|
||||
<value>Associer une vidéo</value>
|
||||
<comment>Link Video</comment>
|
||||
</data>
|
||||
<data name="YouTube URL or Video ID" xml:space="preserve">
|
||||
<value>URL YouTube ou identifiant de la vidéo</value>
|
||||
<comment>YouTube URL or Video ID</comment>
|
||||
</data>
|
||||
<data name="Detected Video ID" xml:space="preserve">
|
||||
<value>Identifiant de vidéo détecté</value>
|
||||
<comment>Detected Video ID</comment>
|
||||
</data>
|
||||
<data name="Provider" xml:space="preserve">
|
||||
<value>Fournisseur</value>
|
||||
<comment>Provider</comment>
|
||||
</data>
|
||||
<data name="Video ID" xml:space="preserve">
|
||||
<value>Identifiant de la vidéo</value>
|
||||
<comment>Video ID</comment>
|
||||
</data>
|
||||
<data name="Video Title" xml:space="preserve">
|
||||
<value>Titre de la vidéo</value>
|
||||
<comment>Video Title</comment>
|
||||
</data>
|
||||
<data name="Title" xml:space="preserve">
|
||||
<value>Titre</value>
|
||||
<comment>Title</comment>
|
||||
</data>
|
||||
<data name="Advanced" xml:space="preserve">
|
||||
<value>Avancé</value>
|
||||
<comment>Advanced</comment>
|
||||
</data>
|
||||
<data name="Provider name (e.g. YouTube)." xml:space="preserve">
|
||||
<value>Nom du fournisseur (par ex. YouTube).</value>
|
||||
<comment>Provider name (e.g. YouTube).</comment>
|
||||
</data>
|
||||
<data name="Use this when the URL above cannot be parsed (non-YouTube providers)." xml:space="preserve">
|
||||
<value>Utilisez ceci lorsque l'URL ci-dessus ne peut pas être analysée (fournisseurs autres que YouTube).</value>
|
||||
<comment>Use this when the URL above cannot be parsed (non-YouTube providers).</comment>
|
||||
</data>
|
||||
<data name="Saving..." xml:space="preserve">
|
||||
<value>Enregistrement...</value>
|
||||
<comment>Saving...</comment>
|
||||
</data>
|
||||
<data name="No videos found for this software." xml:space="preserve">
|
||||
<value>Aucune vidéo trouvée pour ce logiciel.</value>
|
||||
<comment>No videos found for this software.</comment>
|
||||
</data>
|
||||
<data name="Delete Video" xml:space="preserve">
|
||||
<value>Supprimer la vidéo</value>
|
||||
<comment>Delete Video</comment>
|
||||
</data>
|
||||
<data name="Are you sure you want to delete this video? This action cannot be undone." xml:space="preserve">
|
||||
<value>Êtes-vous sûr de vouloir supprimer cette vidéo ? Cette action ne peut pas être annulée.</value>
|
||||
<comment>Are you sure you want to delete this video? This action cannot be undone.</comment>
|
||||
</data>
|
||||
<data name="Video linked successfully." xml:space="preserve">
|
||||
<value>Vidéo associée avec succès.</value>
|
||||
<comment>Video linked successfully.</comment>
|
||||
</data>
|
||||
<data name="Video updated successfully." xml:space="preserve">
|
||||
<value>Vidéo mise à jour avec succès.</value>
|
||||
<comment>Video updated successfully.</comment>
|
||||
</data>
|
||||
<data name="Video deleted successfully." xml:space="preserve">
|
||||
<value>Vidéo supprimée avec succès.</value>
|
||||
<comment>Video deleted successfully.</comment>
|
||||
</data>
|
||||
<data name="Failed to link video." xml:space="preserve">
|
||||
<value>Échec de l'association de la vidéo.</value>
|
||||
<comment>Failed to link video.</comment>
|
||||
</data>
|
||||
<data name="Failed to update video." xml:space="preserve">
|
||||
<value>Échec de la mise à jour de la vidéo.</value>
|
||||
<comment>Failed to update video.</comment>
|
||||
</data>
|
||||
<data name="Failed to delete video." xml:space="preserve">
|
||||
<value>Échec de la suppression de la vidéo.</value>
|
||||
<comment>Failed to delete video.</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1157,4 +1157,96 @@
|
||||
<value>Arte promozionale eliminata con successo.</value>
|
||||
<comment>Promo art deleted successfully.</comment>
|
||||
</data>
|
||||
<data name="Videos" xml:space="preserve">
|
||||
<value>Video</value>
|
||||
<comment>Videos</comment>
|
||||
</data>
|
||||
<data name="Video Management" xml:space="preserve">
|
||||
<value>Gestione video</value>
|
||||
<comment>Video Management</comment>
|
||||
</data>
|
||||
<data name="Manage software videos." xml:space="preserve">
|
||||
<value>Gestisci i video del software.</value>
|
||||
<comment>Manage software videos.</comment>
|
||||
</data>
|
||||
<data name="Link Video" xml:space="preserve">
|
||||
<value>Collega video</value>
|
||||
<comment>Link Video</comment>
|
||||
</data>
|
||||
<data name="YouTube URL or Video ID" xml:space="preserve">
|
||||
<value>URL di YouTube o ID del video</value>
|
||||
<comment>YouTube URL or Video ID</comment>
|
||||
</data>
|
||||
<data name="Detected Video ID" xml:space="preserve">
|
||||
<value>ID del video rilevato</value>
|
||||
<comment>Detected Video ID</comment>
|
||||
</data>
|
||||
<data name="Provider" xml:space="preserve">
|
||||
<value>Fornitore</value>
|
||||
<comment>Provider</comment>
|
||||
</data>
|
||||
<data name="Video ID" xml:space="preserve">
|
||||
<value>ID del video</value>
|
||||
<comment>Video ID</comment>
|
||||
</data>
|
||||
<data name="Video Title" xml:space="preserve">
|
||||
<value>Titolo del video</value>
|
||||
<comment>Video Title</comment>
|
||||
</data>
|
||||
<data name="Title" xml:space="preserve">
|
||||
<value>Titolo</value>
|
||||
<comment>Title</comment>
|
||||
</data>
|
||||
<data name="Advanced" xml:space="preserve">
|
||||
<value>Avanzate</value>
|
||||
<comment>Advanced</comment>
|
||||
</data>
|
||||
<data name="Provider name (e.g. YouTube)." xml:space="preserve">
|
||||
<value>Nome del fornitore (es. YouTube).</value>
|
||||
<comment>Provider name (e.g. YouTube).</comment>
|
||||
</data>
|
||||
<data name="Use this when the URL above cannot be parsed (non-YouTube providers)." xml:space="preserve">
|
||||
<value>Usalo quando l'URL sopra non può essere analizzato (fornitori diversi da YouTube).</value>
|
||||
<comment>Use this when the URL above cannot be parsed (non-YouTube providers).</comment>
|
||||
</data>
|
||||
<data name="Saving..." xml:space="preserve">
|
||||
<value>Salvataggio in corso...</value>
|
||||
<comment>Saving...</comment>
|
||||
</data>
|
||||
<data name="No videos found for this software." xml:space="preserve">
|
||||
<value>Nessun video trovato per questo software.</value>
|
||||
<comment>No videos found for this software.</comment>
|
||||
</data>
|
||||
<data name="Delete Video" xml:space="preserve">
|
||||
<value>Elimina video</value>
|
||||
<comment>Delete Video</comment>
|
||||
</data>
|
||||
<data name="Are you sure you want to delete this video? This action cannot be undone." xml:space="preserve">
|
||||
<value>Sei sicuro di voler eliminare questo video? Questa azione non può essere annullata.</value>
|
||||
<comment>Are you sure you want to delete this video? This action cannot be undone.</comment>
|
||||
</data>
|
||||
<data name="Video linked successfully." xml:space="preserve">
|
||||
<value>Video collegato con successo.</value>
|
||||
<comment>Video linked successfully.</comment>
|
||||
</data>
|
||||
<data name="Video updated successfully." xml:space="preserve">
|
||||
<value>Video aggiornato con successo.</value>
|
||||
<comment>Video updated successfully.</comment>
|
||||
</data>
|
||||
<data name="Video deleted successfully." xml:space="preserve">
|
||||
<value>Video eliminato con successo.</value>
|
||||
<comment>Video deleted successfully.</comment>
|
||||
</data>
|
||||
<data name="Failed to link video." xml:space="preserve">
|
||||
<value>Impossibile collegare il video.</value>
|
||||
<comment>Failed to link video.</comment>
|
||||
</data>
|
||||
<data name="Failed to update video." xml:space="preserve">
|
||||
<value>Impossibile aggiornare il video.</value>
|
||||
<comment>Failed to update video.</comment>
|
||||
</data>
|
||||
<data name="Failed to delete video." xml:space="preserve">
|
||||
<value>Impossibile eliminare il video.</value>
|
||||
<comment>Failed to delete video.</comment>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -1514,4 +1514,69 @@ public class SoftwareService(Marechai.ApiClient.Client client, IRequestAdapter r
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(SoftwareVideoDto dto, string error)> CreateVideoAsync(int softwareId, string provider,
|
||||
string videoId,
|
||||
string title)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dto = await client.Software[softwareId]
|
||||
.Videos.PostAsync(new CreateSoftwareVideoRequest
|
||||
{
|
||||
Provider = provider,
|
||||
VideoId = videoId,
|
||||
Title = title
|
||||
});
|
||||
|
||||
return (dto, null);
|
||||
}
|
||||
catch(ApiException ex)
|
||||
{
|
||||
return (null, ExtractErrorMessage(ex));
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
return (null, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(bool succeeded, string error)> UpdateVideoTitleAsync(long id, string title)
|
||||
{
|
||||
try
|
||||
{
|
||||
await client.Software.Videos[id].PutAsync(new UpdateSoftwareVideoRequest
|
||||
{
|
||||
Title = title
|
||||
});
|
||||
|
||||
return (true, null);
|
||||
}
|
||||
catch(ApiException ex)
|
||||
{
|
||||
return (false, ExtractErrorMessage(ex));
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
return (false, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(bool succeeded, string error)> DeleteVideoAsync(long id)
|
||||
{
|
||||
try
|
||||
{
|
||||
await client.Software.Videos[id].DeleteAsync();
|
||||
|
||||
return (true, null);
|
||||
}
|
||||
catch(ApiException ex)
|
||||
{
|
||||
return (false, ExtractErrorMessage(ex));
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
return (false, ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user