mirror of
https://github.com/sphildreth/roadie
synced 2025-02-16 21:18:26 +00:00
Work for roadie-vuejs
This commit is contained in:
parent
30b5bf0beb
commit
1ca2d2e063
11 changed files with 263 additions and 87 deletions
|
@ -5,6 +5,7 @@ using Microsoft.Extensions.Configuration;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Roadie.Api.Models;
|
||||
using Roadie.Api.Services;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Identity;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
@ -25,6 +26,7 @@ namespace Roadie.Api.Controllers
|
|||
private readonly SignInManager<ApplicationUser> signInManager;
|
||||
private readonly ITokenService tokenService;
|
||||
private readonly UserManager<ApplicationUser> userManager;
|
||||
private IRoadieSettings RoadieSettings { get; }
|
||||
|
||||
public AccountController(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
|
@ -38,6 +40,10 @@ namespace Roadie.Api.Controllers
|
|||
this.configuration = configuration;
|
||||
this.logger = logger;
|
||||
this.tokenService = tokenService;
|
||||
|
||||
this.RoadieSettings = new RoadieSettings();
|
||||
configuration.GetSection("RoadieSettings").Bind(this.RoadieSettings);
|
||||
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
|
@ -57,15 +63,18 @@ namespace Roadie.Api.Controllers
|
|||
var user = await userManager.FindByNameAsync(model.Username);
|
||||
var now = DateTime.UtcNow;
|
||||
user.LastLogin = now;
|
||||
user.LastApiAccess = now;
|
||||
user.LastUpdated = now;
|
||||
await userManager.UpdateAsync(user);
|
||||
var t = await this.tokenService.GenerateToken(user, this.userManager);
|
||||
this.logger.LogInformation($"Successfully authenticated User [{ model.Username}]");
|
||||
var avatarUrl = $"{this.Request.Scheme}://{this.Request.Host}/images/user/{ user.RoadieId }/{ this.RoadieSettings.ThumbnailImageSize.Width }/{ this.RoadieSettings.ThumbnailImageSize.Height }";
|
||||
return Ok(new
|
||||
{
|
||||
Id = user.RoadieId,
|
||||
Username = user.UserName,
|
||||
user.Email,
|
||||
user.LastLogin,
|
||||
avatarUrl = avatarUrl,
|
||||
Token = t
|
||||
});
|
||||
}
|
||||
|
|
|
@ -55,10 +55,11 @@ namespace Roadie.Api.Controllers
|
|||
|
||||
[HttpGet]
|
||||
[ProducesResponseType(200)]
|
||||
public async Task<IActionResult> List([FromQuery]PagedRequest request, string inc)
|
||||
public async Task<IActionResult> List([FromQuery]PagedRequest request, string inc, bool? doRandomize = false)
|
||||
{
|
||||
var result = await this.ReleaseService.List(user: await this.CurrentUserModel(),
|
||||
request: request);
|
||||
request: request,
|
||||
doRandomize: doRandomize ?? false);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||
|
|
|
@ -7,6 +7,7 @@ using Roadie.Api.Services;
|
|||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Identity;
|
||||
using Roadie.Library.Models.Pagination;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -16,7 +17,7 @@ namespace Roadie.Api.Controllers
|
|||
[Produces("application/json")]
|
||||
[Route("users")]
|
||||
[ApiController]
|
||||
// [Authorize]
|
||||
[Authorize]
|
||||
public class UserController : EntityControllerBase
|
||||
{
|
||||
private IUserService UserService { get; }
|
||||
|
@ -57,6 +58,44 @@ namespace Roadie.Api.Controllers
|
|||
//}
|
||||
|
||||
|
||||
[HttpPost("setArtistRating/{releaseId}/{rating}")]
|
||||
[ProducesResponseType(200)]
|
||||
public async Task<IActionResult> SetArtistRating(Guid releaseId, short rating)
|
||||
{
|
||||
var result = await this.UserService.SetArtistRating(releaseId, await this.CurrentUserModel(), rating);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||
}
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
[HttpPost("setReleaseRating/{releaseId}/{rating}")]
|
||||
[ProducesResponseType(200)]
|
||||
public async Task<IActionResult> SetReleaseRating(Guid releaseId, short rating)
|
||||
{
|
||||
var result = await this.UserService.SetReleaseRating(releaseId, await this.CurrentUserModel(), rating);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||
}
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
[HttpPost("setTrackRating/{releaseId}/{rating}")]
|
||||
[ProducesResponseType(200)]
|
||||
public async Task<IActionResult> SetTrackRating(Guid releaseId, short rating)
|
||||
{
|
||||
var result = await this.UserService.SetTrackRating(releaseId, await this.CurrentUserModel(), rating);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
return StatusCode((int)HttpStatusCode.InternalServerError);
|
||||
}
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[HttpGet]
|
||||
[ProducesResponseType(200)]
|
||||
public async Task<IActionResult> List([FromQuery]PagedRequest request)
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
using Roadie.Library.Models.Pagination;
|
||||
using Roadie.Library;
|
||||
using Roadie.Library.Identity;
|
||||
using Roadie.Library.Models.Pagination;
|
||||
using Roadie.Library.Models.Users;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Roadie.Api.Services
|
||||
|
@ -7,5 +10,8 @@ namespace Roadie.Api.Services
|
|||
public interface IUserService
|
||||
{
|
||||
Task<PagedResult<UserList>> List(PagedRequest request);
|
||||
Task<OperationResult<bool>> SetReleaseRating(Guid releaseId, User roadieUser, short rating);
|
||||
Task<OperationResult<bool>> SetArtistRating(Guid artistId, User roadieUser, short rating);
|
||||
Task<OperationResult<bool>> SetTrackRating(Guid trackId, User roadieUser, short rating);
|
||||
}
|
||||
}
|
|
@ -134,7 +134,8 @@ namespace Roadie.Api.Services
|
|||
favoriteReleaseIds = (from a in this.DbContext.Releases
|
||||
join ur in this.DbContext.UserReleases on a.Id equals ur.ReleaseId
|
||||
where ur.IsFavorite ?? false
|
||||
select a.Id
|
||||
where (roadieUser == null || ur.UserId == roadieUser.Id)
|
||||
select a.Id
|
||||
).ToArray();
|
||||
}
|
||||
int[] genreReleaseIds = new int[0];
|
||||
|
@ -160,6 +161,9 @@ namespace Roadie.Api.Services
|
|||
request.Order = "ASC";
|
||||
}
|
||||
}
|
||||
//
|
||||
// TODO list should honor disliked artist and albums
|
||||
//
|
||||
var result = (from r in this.DbContext.Releases.Include("Artist")
|
||||
join a in this.DbContext.Artists on r.ArtistId equals a.Id
|
||||
where (request.FilterMinimumRating == null || r.Rating >= request.FilterMinimumRating.Value)
|
||||
|
@ -322,6 +326,10 @@ namespace Roadie.Api.Services
|
|||
// }
|
||||
// }
|
||||
//}
|
||||
if(request.FilterFavoriteOnly)
|
||||
{
|
||||
rows = rows.OrderBy(x => x.UserRating.Rating).ToArray();
|
||||
}
|
||||
sw.Stop();
|
||||
return new Library.Models.Pagination.PagedResult<ReleaseList>
|
||||
{
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using Mapster;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Roadie.Library;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Encoding;
|
||||
|
@ -11,6 +12,7 @@ using Roadie.Library.Utility;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using data = Roadie.Library.Data;
|
||||
|
||||
namespace Roadie.Api.Services
|
||||
|
@ -217,6 +219,136 @@ namespace Roadie.Api.Services
|
|||
}, ApplicationUser.CacheRegionUrn(id.Value));
|
||||
}
|
||||
|
||||
|
||||
protected async Task<OperationResult<bool>> SetArtistRating(Guid artistId, ApplicationUser user, short rating)
|
||||
{
|
||||
var artist = this.GetArtist(artistId);
|
||||
if (artist == null)
|
||||
{
|
||||
return new OperationResult<bool>(true, $"Invalid Artist Id [{ artistId }]");
|
||||
}
|
||||
var userArtist = user.ArtistRatings.FirstOrDefault(x => x.ArtistId == artist.Id);
|
||||
if (userArtist == null)
|
||||
{
|
||||
userArtist = new data.UserArtist
|
||||
{
|
||||
Rating = rating,
|
||||
UserId = user.Id,
|
||||
ArtistId = artist.Id
|
||||
};
|
||||
this.DbContext.UserArtists.Add(userArtist);
|
||||
}
|
||||
else
|
||||
{
|
||||
userArtist.Rating = rating;
|
||||
userArtist.LastUpdated = DateTime.UtcNow;
|
||||
}
|
||||
await this.DbContext.SaveChangesAsync();
|
||||
|
||||
var sql = "UPDATE `artist` set lastUpdated = UTC_DATE(), rating = (SELECT cast(avg(ur.rating) as signed) " +
|
||||
"FROM `userartist` ur " +
|
||||
"where artistId = {0}) " +
|
||||
"WHERE id = {0};";
|
||||
await this.DbContext.Database.ExecuteSqlCommandAsync(sql, artist.Id);
|
||||
|
||||
this.CacheManager.ClearRegion(user.CacheRegion);
|
||||
this.CacheManager.ClearRegion(artist.CacheRegion);
|
||||
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
IsSuccess = true,
|
||||
Data = true
|
||||
};
|
||||
}
|
||||
|
||||
protected async Task<OperationResult<bool>> SetReleaseRating(Guid releaseId, ApplicationUser user, short rating)
|
||||
{
|
||||
var release = this.GetRelease(releaseId);
|
||||
if (release == null)
|
||||
{
|
||||
return new OperationResult<bool>(true, $"Invalid Release Id [{ releaseId }]");
|
||||
}
|
||||
var userRelease = user.ReleaseRatings.FirstOrDefault(x => x.ReleaseId == release.Id);
|
||||
var now = DateTime.UtcNow;
|
||||
if (userRelease == null)
|
||||
{
|
||||
userRelease = new data.UserRelease
|
||||
{
|
||||
Rating = rating,
|
||||
UserId = user.Id,
|
||||
ReleaseId = release.Id
|
||||
};
|
||||
this.DbContext.UserReleases.Add(userRelease);
|
||||
}
|
||||
else
|
||||
{
|
||||
userRelease.Rating = rating;
|
||||
userRelease.LastUpdated = now;
|
||||
}
|
||||
await this.DbContext.SaveChangesAsync();
|
||||
|
||||
var sql = "UPDATE `release` set lastUpdated = UTC_DATE(), rating = (SELECT cast(avg(ur.rating) as signed) " +
|
||||
"FROM `userrelease` ur " +
|
||||
"where releaseId = {0}) " +
|
||||
"WHERE id = {0};";
|
||||
await this.DbContext.Database.ExecuteSqlCommandAsync(sql, release.Id);
|
||||
|
||||
this.CacheManager.ClearRegion(user.CacheRegion);
|
||||
this.CacheManager.ClearRegion(release.CacheRegion);
|
||||
this.CacheManager.ClearRegion(release.Artist.CacheRegion);
|
||||
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
IsSuccess = true,
|
||||
Data = true
|
||||
};
|
||||
}
|
||||
|
||||
protected async Task<OperationResult<bool>> SetTrackRating(Guid trackId, ApplicationUser user, short rating)
|
||||
{
|
||||
var track = this.GetTrack(trackId);
|
||||
if (track == null)
|
||||
{
|
||||
return new OperationResult<bool>(true, $"Invalid Track Id [{ trackId }]");
|
||||
}
|
||||
var userTrack = user.TrackRatings.FirstOrDefault(x => x.TrackId == track.Id);
|
||||
if (userTrack == null)
|
||||
{
|
||||
userTrack = new data.UserTrack
|
||||
{
|
||||
Rating = rating,
|
||||
UserId = user.Id,
|
||||
TrackId = track.Id
|
||||
};
|
||||
this.DbContext.UserTracks.Add(userTrack);
|
||||
}
|
||||
else
|
||||
{
|
||||
userTrack.Rating = rating;
|
||||
userTrack.LastUpdated = DateTime.UtcNow;
|
||||
}
|
||||
await this.DbContext.SaveChangesAsync();
|
||||
|
||||
var sql = "UPDATE `track` set lastUpdated = UTC_DATE(), rating = (SELECT cast(avg(ur.rating) as signed) " +
|
||||
"FROM `usertrack` ur " +
|
||||
"where trackId = {0}) " +
|
||||
"WHERE id = {0};";
|
||||
await this.DbContext.Database.ExecuteSqlCommandAsync(sql, track.Id);
|
||||
|
||||
this.CacheManager.ClearRegion(user.CacheRegion);
|
||||
this.CacheManager.ClearRegion(track.CacheRegion);
|
||||
this.CacheManager.ClearRegion(track.ReleaseMedia.Release.CacheRegion);
|
||||
this.CacheManager.ClearRegion(track.ReleaseMedia.Release.Artist.CacheRegion);
|
||||
|
||||
return new OperationResult<bool>
|
||||
{
|
||||
IsSuccess = true,
|
||||
Data = true
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected Image MakeArtistThumbnailImage(Guid id)
|
||||
{
|
||||
return MakeThumbnailImage(id, "artist");
|
||||
|
|
|
@ -1942,111 +1942,45 @@ namespace Roadie.Api.Services
|
|||
return this.MusicFolders().First(x => x.id == 2);
|
||||
}
|
||||
|
||||
private async Task<subsonic.SubsonicOperationResult<bool>> SetArtistRating(Guid artistId, ApplicationUser user, short rating)
|
||||
private new async Task<subsonic.SubsonicOperationResult<bool>> SetArtistRating(Guid artistId, ApplicationUser user, short rating)
|
||||
{
|
||||
var artist = this.GetArtist(artistId);
|
||||
if (artist == null)
|
||||
var r = await base.SetArtistRating(artistId, user, rating);
|
||||
if(r.IsNotFoundResult)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<bool>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Artist Id [{ artistId }]");
|
||||
}
|
||||
var userArtist = user.ArtistRatings.FirstOrDefault(x => x.ArtistId == artist.Id);
|
||||
if (userArtist == null)
|
||||
{
|
||||
userArtist = new data.UserArtist
|
||||
{
|
||||
Rating = rating,
|
||||
UserId = user.Id,
|
||||
ArtistId = artist.Id
|
||||
};
|
||||
this.DbContext.UserArtists.Add(userArtist);
|
||||
}
|
||||
else
|
||||
{
|
||||
userArtist.Rating = rating;
|
||||
userArtist.LastUpdated = DateTime.UtcNow;
|
||||
}
|
||||
await this.DbContext.SaveChangesAsync();
|
||||
|
||||
this.CacheManager.ClearRegion(user.CacheRegion);
|
||||
this.CacheManager.ClearRegion(artist.CacheRegion);
|
||||
|
||||
return new subsonic.SubsonicOperationResult<bool>
|
||||
{
|
||||
IsSuccess = true,
|
||||
Data = true
|
||||
IsSuccess = r.IsSuccess,
|
||||
Data = r.IsSuccess
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<subsonic.SubsonicOperationResult<bool>> SetReleaseRating(Guid releaseId, ApplicationUser user, short rating)
|
||||
private new async Task<subsonic.SubsonicOperationResult<bool>> SetReleaseRating(Guid releaseId, ApplicationUser user, short rating)
|
||||
{
|
||||
var release = this.GetRelease(releaseId);
|
||||
if (release == null)
|
||||
var r = await base.SetReleaseRating(releaseId, user, rating);
|
||||
if (r.IsNotFoundResult)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<bool>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Release Id [{ releaseId }]");
|
||||
}
|
||||
var userRelease = user.ReleaseRatings.FirstOrDefault(x => x.ReleaseId == release.Id);
|
||||
if (userRelease == null)
|
||||
{
|
||||
userRelease = new data.UserRelease
|
||||
{
|
||||
Rating = rating,
|
||||
UserId = user.Id,
|
||||
ReleaseId = release.Id
|
||||
};
|
||||
this.DbContext.UserReleases.Add(userRelease);
|
||||
}
|
||||
else
|
||||
{
|
||||
userRelease.Rating = rating;
|
||||
userRelease.LastUpdated = DateTime.UtcNow;
|
||||
}
|
||||
await this.DbContext.SaveChangesAsync();
|
||||
|
||||
this.CacheManager.ClearRegion(user.CacheRegion);
|
||||
this.CacheManager.ClearRegion(release.CacheRegion);
|
||||
this.CacheManager.ClearRegion(release.Artist.CacheRegion);
|
||||
|
||||
return new subsonic.SubsonicOperationResult<bool>
|
||||
{
|
||||
IsSuccess = true,
|
||||
Data = true
|
||||
IsSuccess = r.IsSuccess,
|
||||
Data = r.IsSuccess
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<subsonic.SubsonicOperationResult<bool>> SetTrackRating(Guid trackId, ApplicationUser user, short rating)
|
||||
private new async Task<subsonic.SubsonicOperationResult<bool>> SetTrackRating(Guid trackId, ApplicationUser user, short rating)
|
||||
{
|
||||
var track = this.GetTrack(trackId);
|
||||
if (track == null)
|
||||
var r = await base.SetTrackRating(trackId, user, rating);
|
||||
if (r.IsNotFoundResult)
|
||||
{
|
||||
return new subsonic.SubsonicOperationResult<bool>(subsonic.ErrorCodes.TheRequestedDataWasNotFound, $"Invalid Track Id [{ trackId }]");
|
||||
}
|
||||
var userTrack = user.TrackRatings.FirstOrDefault(x => x.TrackId == track.Id);
|
||||
if (userTrack == null)
|
||||
{
|
||||
userTrack = new data.UserTrack
|
||||
{
|
||||
Rating = rating,
|
||||
UserId = user.Id,
|
||||
TrackId = track.Id
|
||||
};
|
||||
this.DbContext.UserTracks.Add(userTrack);
|
||||
}
|
||||
else
|
||||
{
|
||||
userTrack.Rating = rating;
|
||||
userTrack.LastUpdated = DateTime.UtcNow;
|
||||
}
|
||||
await this.DbContext.SaveChangesAsync();
|
||||
|
||||
this.CacheManager.ClearRegion(user.CacheRegion);
|
||||
this.CacheManager.ClearRegion(track.CacheRegion);
|
||||
this.CacheManager.ClearRegion(track.ReleaseMedia.Release.CacheRegion);
|
||||
this.CacheManager.ClearRegion(track.ReleaseMedia.Release.Artist.CacheRegion);
|
||||
|
||||
return new subsonic.SubsonicOperationResult<bool>
|
||||
{
|
||||
IsSuccess = true,
|
||||
Data = true
|
||||
IsSuccess = r.IsSuccess,
|
||||
Data = r.IsSuccess
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ namespace Roadie.Api.Services
|
|||
{
|
||||
new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()),
|
||||
new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName),
|
||||
new Claim(JwtRegisteredClaimNames.Email, user.Email),
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||
new Claim(JwtRegisteredClaimNames.Iat, utcNow.ToString())
|
||||
}.Union(userRoles);
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Roadie.Library;
|
||||
using Roadie.Library.Caching;
|
||||
using Roadie.Library.Configuration;
|
||||
using Roadie.Library.Encoding;
|
||||
using Roadie.Library.Identity;
|
||||
using Roadie.Library.Models;
|
||||
using Roadie.Library.Models.Pagination;
|
||||
using Roadie.Library.Models.Statistics;
|
||||
|
@ -150,5 +152,35 @@ namespace Roadie.Api.Services
|
|||
Rows = rows
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<OperationResult<bool>> SetArtistRating(Guid artistId, User roadieUser, short rating)
|
||||
{
|
||||
var user = this.GetUser(roadieUser.UserId);
|
||||
if (user == null)
|
||||
{
|
||||
return new OperationResult<bool>(true, $"Invalid User [{ roadieUser }]");
|
||||
}
|
||||
return await base.SetArtistRating(artistId, user, rating);
|
||||
}
|
||||
|
||||
public async Task<OperationResult<bool>> SetReleaseRating(Guid releaseId, User roadieUser, short rating)
|
||||
{
|
||||
var user = this.GetUser(roadieUser.UserId);
|
||||
if (user == null)
|
||||
{
|
||||
return new OperationResult<bool>(true, $"Invalid User [{ roadieUser }]");
|
||||
}
|
||||
return await base.SetReleaseRating(releaseId, user, rating);
|
||||
}
|
||||
|
||||
public async Task<OperationResult<bool>> SetTrackRating(Guid trackId, User roadieUser, short rating)
|
||||
{
|
||||
var user = this.GetUser(roadieUser.UserId);
|
||||
if (user == null)
|
||||
{
|
||||
return new OperationResult<bool>(true, $"Invalid User [{ roadieUser }]");
|
||||
}
|
||||
return await base.SetTrackRating(trackId, user, rating);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -165,6 +165,7 @@ namespace Roadie.Api
|
|||
config.TokenValidationParameters = new TokenValidationParameters()
|
||||
{
|
||||
IssuerSigningKey = securityKey,
|
||||
|
||||
ValidateAudience = true,
|
||||
ValidAudience = this._configuration["Tokens:Audience"],
|
||||
ValidateIssuer = true,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using Mapster;
|
||||
using Newtonsoft.Json;
|
||||
using Roadie.Library.Enums;
|
||||
using Roadie.Library.Extensions;
|
||||
using Roadie.Library.Models.Users;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -47,5 +48,17 @@ namespace Roadie.Library.Models.Releases
|
|||
public DataToken Genre { get; set; }
|
||||
public DateTime? LastPlayed { get; set; }
|
||||
public int? Duration { get; set; }
|
||||
public string DurationTime
|
||||
{
|
||||
get
|
||||
{
|
||||
if(!this.Duration.HasValue)
|
||||
{
|
||||
return "--:--";
|
||||
}
|
||||
return TimeSpan.FromSeconds(this.Duration.Value / 1000).ToString(@"hh\:mm\:ss");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue