mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Adding a TrackIndex for tracks, support duplicated values & slugs for tracks that are not subtitles
This commit is contained in:
parent
aac1975a75
commit
2138f0909b
@ -103,19 +103,13 @@ namespace Kyoo.Models
|
|||||||
StreamType.Font => "font.",
|
StreamType.Font => "font.",
|
||||||
_ => ""
|
_ => ""
|
||||||
};
|
};
|
||||||
string slug = $"{Episode.Slug}.{type}{Language}{(TrackIndex != 0 ? TrackIndex : "")}";
|
string index = TrackIndex != 0 ? $"-{TrackIndex}" : string.Empty;
|
||||||
if (IsForced)
|
string codec = Codec switch
|
||||||
slug += "-forced";
|
|
||||||
switch (Codec)
|
|
||||||
{
|
{
|
||||||
case "ass":
|
"subrip" => ".srt",
|
||||||
slug += ".ass";
|
{} x => $".{x}"
|
||||||
break;
|
};
|
||||||
case "subrip":
|
return $"{Episode.Slug}.{type}{Language}{index}{(IsForced ? "-forced" : "")}{codec}";
|
||||||
slug += ".srt";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return slug;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ namespace Kyoo.Controllers
|
|||||||
&& x.IsForced == y.IsForced
|
&& x.IsForced == y.IsForced
|
||||||
&& x.Codec == y.Codec
|
&& x.Codec == y.Codec
|
||||||
&& x.Type == y.Type);
|
&& x.Type == y.Type);
|
||||||
return _tracks.CreateIfNotExists(x, true);
|
return _tracks.Create(x);
|
||||||
}).ToListAsync();
|
}).ToListAsync();
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ using System.Linq.Expressions;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Kyoo.Models;
|
using Kyoo.Models;
|
||||||
|
using Kyoo.Models.Exceptions;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Kyoo.Controllers
|
namespace Kyoo.Controllers
|
||||||
@ -12,7 +13,7 @@ namespace Kyoo.Controllers
|
|||||||
{
|
{
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
private readonly DatabaseContext _database;
|
private readonly DatabaseContext _database;
|
||||||
protected override Expression<Func<Track, object>> DefaultSort => x => x.ID;
|
protected override Expression<Func<Track, object>> DefaultSort => x => x.TrackIndex;
|
||||||
|
|
||||||
|
|
||||||
public TrackRepository(DatabaseContext database) : base(database)
|
public TrackRepository(DatabaseContext database) : base(database)
|
||||||
@ -44,7 +45,7 @@ namespace Kyoo.Controllers
|
|||||||
public Task<Track> Get(string slug, StreamType type)
|
public Task<Track> Get(string slug, StreamType type)
|
||||||
{
|
{
|
||||||
Match match = Regex.Match(slug,
|
Match match = Regex.Match(slug,
|
||||||
@"(?<show>.*)-s(?<season>\d+)e(?<episode>\d+)\.(?<language>.{0,3})(?<forced>-forced)?(\..*)?");
|
@"(?<show>.*)-s(?<season>\d+)e(?<episode>\d+)(\.(?<type>\w*))?\.(?<language>.{0,3})(?<forced>-forced)?(\..*)?");
|
||||||
|
|
||||||
if (!match.Success)
|
if (!match.Success)
|
||||||
{
|
{
|
||||||
@ -61,6 +62,8 @@ namespace Kyoo.Controllers
|
|||||||
int episodeNumber = match.Groups["episode"].Success ? int.Parse(match.Groups["episode"].Value) : -1;
|
int episodeNumber = match.Groups["episode"].Success ? int.Parse(match.Groups["episode"].Value) : -1;
|
||||||
string language = match.Groups["language"].Value;
|
string language = match.Groups["language"].Value;
|
||||||
bool forced = match.Groups["forced"].Success;
|
bool forced = match.Groups["forced"].Success;
|
||||||
|
if (match.Groups["type"].Success)
|
||||||
|
type = Enum.Parse<StreamType>(match.Groups["type"].Value, true);
|
||||||
|
|
||||||
if (type == StreamType.Unknown)
|
if (type == StreamType.Unknown)
|
||||||
{
|
{
|
||||||
@ -94,7 +97,13 @@ namespace Kyoo.Controllers
|
|||||||
|
|
||||||
await base.Create(obj);
|
await base.Create(obj);
|
||||||
_database.Entry(obj).State = EntityState.Added;
|
_database.Entry(obj).State = EntityState.Added;
|
||||||
await _database.SaveChangesAsync($"Trying to insert a duplicated track (slug {obj.Slug} already exists).");
|
await _database.SaveOrRetry(obj, (x, i) =>
|
||||||
|
{
|
||||||
|
if (i > 10)
|
||||||
|
throw new DuplicatedItemException($"More than 10 same tracks exists {x.Slug}. Aborting...");
|
||||||
|
x.TrackIndex++;
|
||||||
|
return x;
|
||||||
|
});
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,6 +185,9 @@ namespace Kyoo
|
|||||||
modelBuilder.Entity<Episode>()
|
modelBuilder.Entity<Episode>()
|
||||||
.HasIndex(x => new {x.ShowID, x.SeasonNumber, x.EpisodeNumber, x.AbsoluteNumber})
|
.HasIndex(x => new {x.ShowID, x.SeasonNumber, x.EpisodeNumber, x.AbsoluteNumber})
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
modelBuilder.Entity<Track>()
|
||||||
|
.HasIndex(x => new {x.EpisodeID, x.Type, x.Language, x.TrackIndex, x.IsForced})
|
||||||
|
.IsUnique();
|
||||||
}
|
}
|
||||||
|
|
||||||
public T GetTemporaryObject<T>(T model)
|
public T GetTemporaryObject<T>(T model)
|
||||||
@ -301,6 +304,33 @@ namespace Kyoo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<T> SaveOrRetry<T>(T obj, Func<T, int, T> onFail, CancellationToken cancellationToken = new())
|
||||||
|
{
|
||||||
|
return SaveOrRetry(obj, onFail, 0, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<T> SaveOrRetry<T>(T obj,
|
||||||
|
Func<T, int, T> onFail,
|
||||||
|
int recurse,
|
||||||
|
CancellationToken cancellationToken = new())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await base.SaveChangesAsync(true, cancellationToken);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
catch (DbUpdateException ex) when (IsDuplicateException(ex))
|
||||||
|
{
|
||||||
|
recurse++;
|
||||||
|
return await SaveOrRetry(onFail(obj, recurse), onFail, recurse, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (DbUpdateException)
|
||||||
|
{
|
||||||
|
DiscardChanges();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static bool IsDuplicateException(Exception ex)
|
private static bool IsDuplicateException(Exception ex)
|
||||||
{
|
{
|
||||||
return ex.InnerException is PostgresException {SqlState: PostgresErrorCodes.UniqueViolation};
|
return ex.InnerException is PostgresException {SqlState: PostgresErrorCodes.UniqueViolation};
|
||||||
|
@ -10,7 +10,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|||||||
namespace Kyoo.Models.DatabaseMigrations.Internal
|
namespace Kyoo.Models.DatabaseMigrations.Internal
|
||||||
{
|
{
|
||||||
[DbContext(typeof(DatabaseContext))]
|
[DbContext(typeof(DatabaseContext))]
|
||||||
[Migration("20210317180448_Initial")]
|
[Migration("20210317220956_Initial")]
|
||||||
partial class Initial
|
partial class Initial
|
||||||
{
|
{
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
@ -499,7 +499,8 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
|||||||
|
|
||||||
b.HasKey("ID");
|
b.HasKey("ID");
|
||||||
|
|
||||||
b.HasIndex("EpisodeID");
|
b.HasIndex("EpisodeID", "Type", "Language", "TrackIndex", "IsForced")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
b.ToTable("Tracks");
|
b.ToTable("Tracks");
|
||||||
});
|
});
|
@ -543,9 +543,10 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
|||||||
unique: true);
|
unique: true);
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Tracks_EpisodeID",
|
name: "IX_Tracks_EpisodeID_Type_Language_TrackIndex_IsForced",
|
||||||
table: "Tracks",
|
table: "Tracks",
|
||||||
column: "EpisodeID");
|
columns: new[] { "EpisodeID", "Type", "Language", "TrackIndex", "IsForced" },
|
||||||
|
unique: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
protected override void Down(MigrationBuilder migrationBuilder)
|
@ -497,7 +497,8 @@ namespace Kyoo.Models.DatabaseMigrations.Internal
|
|||||||
|
|
||||||
b.HasKey("ID");
|
b.HasKey("ID");
|
||||||
|
|
||||||
b.HasIndex("EpisodeID");
|
b.HasIndex("EpisodeID", "Type", "Language", "TrackIndex", "IsForced")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
b.ToTable("Tracks");
|
b.ToTable("Tracks");
|
||||||
});
|
});
|
||||||
|
@ -196,7 +196,7 @@ namespace Kyoo
|
|||||||
ctx.Response.Headers.Remove("X-Powered-By");
|
ctx.Response.Headers.Remove("X-Powered-By");
|
||||||
ctx.Response.Headers.Remove("Server");
|
ctx.Response.Headers.Remove("Server");
|
||||||
ctx.Response.Headers.Add("Feature-Policy", "autoplay 'self'; fullscreen");
|
ctx.Response.Headers.Add("Feature-Policy", "autoplay 'self'; fullscreen");
|
||||||
ctx.Response.Headers.Add("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'");
|
ctx.Response.Headers.Add("Content-Security-Policy", "default-src 'self'; script-src 'self' blob: 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'");
|
||||||
ctx.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
|
ctx.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
|
||||||
ctx.Response.Headers.Add("Referrer-Policy", "no-referrer");
|
ctx.Response.Headers.Add("Referrer-Policy", "no-referrer");
|
||||||
ctx.Response.Headers.Add("Access-Control-Allow-Origin", "null");
|
ctx.Response.Headers.Add("Access-Control-Allow-Origin", "null");
|
||||||
|
@ -35,7 +35,7 @@ namespace Kyoo.Api
|
|||||||
return BadRequest(new {error = ex.Message});
|
return BadRequest(new {error = ex.Message});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subtitle == null)
|
if (subtitle == null || subtitle.Type != StreamType.Subtitle)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
|
|
||||||
if (subtitle.Codec == "subrip" && extension == "vtt")
|
if (subtitle.Codec == "subrip" && extension == "vtt")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user