support multi-episode files in local libraries (#240)
* add unused episode nfo reader * move episode number from episode to episode metadata * first pass at loading multi-episode metadata from nfo files * fix episode scanning * local multi-part episode fixes * code cleanup
This commit is contained in:
@@ -423,9 +423,9 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
episode.Id = existing.Id;
|
||||
|
||||
existing.Etag = episode.Etag;
|
||||
existing.EpisodeNumber = episode.EpisodeNumber;
|
||||
|
||||
// metadata
|
||||
// TODO: multiple metadata?
|
||||
EpisodeMetadata metadata = existing.EpisodeMetadata.Head();
|
||||
EpisodeMetadata incomingMetadata = episode.EpisodeMetadata.Head();
|
||||
metadata.Title = incomingMetadata.Title;
|
||||
@@ -435,6 +435,7 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
metadata.DateAdded = incomingMetadata.DateAdded;
|
||||
metadata.DateUpdated = DateTime.UtcNow;
|
||||
metadata.ReleaseDate = incomingMetadata.ReleaseDate;
|
||||
metadata.EpisodeNumber = incomingMetadata.EpisodeNumber;
|
||||
|
||||
// thumbnail
|
||||
Artwork incomingThumbnail =
|
||||
|
||||
@@ -423,9 +423,9 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
episode.Id = existing.Id;
|
||||
|
||||
existing.Etag = episode.Etag;
|
||||
existing.EpisodeNumber = episode.EpisodeNumber;
|
||||
|
||||
// metadata
|
||||
// TODO: multiple metadata?
|
||||
EpisodeMetadata metadata = existing.EpisodeMetadata.Head();
|
||||
EpisodeMetadata incomingMetadata = episode.EpisodeMetadata.Head();
|
||||
metadata.Title = incomingMetadata.Title;
|
||||
@@ -435,6 +435,7 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
metadata.DateAdded = incomingMetadata.DateAdded;
|
||||
metadata.DateUpdated = DateTime.UtcNow;
|
||||
metadata.ReleaseDate = incomingMetadata.ReleaseDate;
|
||||
metadata.EpisodeNumber = metadata.EpisodeNumber;
|
||||
|
||||
// thumbnail
|
||||
Artwork incomingThumbnail =
|
||||
|
||||
@@ -197,7 +197,7 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
.ThenInclude(s => s.ShowMetadata)
|
||||
.ThenInclude(sm => sm.Actors)
|
||||
.ThenInclude(a => a.Artwork)
|
||||
.OrderBy(em => em.Episode.EpisodeNumber)
|
||||
.OrderBy(em => em.EpisodeNumber)
|
||||
.Skip((pageNumber - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
@@ -521,12 +521,12 @@ namespace ErsatzTV.Infrastructure.Data.Repositories
|
||||
return ids;
|
||||
}
|
||||
|
||||
public async Task<Unit> SetEpisodeNumber(Episode episode, int episodeNumber)
|
||||
public async Task<Unit> RemoveMetadata(Episode episode, EpisodeMetadata metadata)
|
||||
{
|
||||
episode.EpisodeNumber = episodeNumber;
|
||||
episode.EpisodeMetadata.Remove(metadata);
|
||||
await _dbConnection.ExecuteAsync(
|
||||
@"UPDATE Episode SET EpisodeNumber = @EpisodeNumber WHERE Id = @Id",
|
||||
new { EpisodeNumber = episodeNumber, episode.Id });
|
||||
@"DELETE FROM EpisodeMetadata WHERE Id = @MetadataId",
|
||||
new { MetadataId = metadata.Id });
|
||||
return Unit.Default;
|
||||
}
|
||||
|
||||
|
||||
@@ -512,11 +512,6 @@ namespace ErsatzTV.Infrastructure.Emby
|
||||
EpisodeMetadata = new List<EpisodeMetadata> { metadata }
|
||||
};
|
||||
|
||||
if (item.IndexNumber.HasValue)
|
||||
{
|
||||
episode.EpisodeNumber = item.IndexNumber.Value;
|
||||
}
|
||||
|
||||
return episode;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -549,6 +544,11 @@ namespace ErsatzTV.Infrastructure.Emby
|
||||
Writers = Optional(item.People).Flatten().Collect(r => ProjectToWriter(r)).ToList()
|
||||
};
|
||||
|
||||
if (item.IndexNumber.HasValue)
|
||||
{
|
||||
metadata.EpisodeNumber = item.IndexNumber.Value;
|
||||
}
|
||||
|
||||
if (DateTime.TryParse(item.PremiereDate, out DateTime releaseDate))
|
||||
{
|
||||
metadata.ReleaseDate = releaseDate;
|
||||
|
||||
@@ -568,11 +568,6 @@ namespace ErsatzTV.Infrastructure.Jellyfin
|
||||
EpisodeMetadata = new List<EpisodeMetadata> { metadata }
|
||||
};
|
||||
|
||||
if (item.IndexNumber.HasValue)
|
||||
{
|
||||
episode.EpisodeNumber = item.IndexNumber.Value;
|
||||
}
|
||||
|
||||
return episode;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -605,6 +600,11 @@ namespace ErsatzTV.Infrastructure.Jellyfin
|
||||
Writers = Optional(item.People).Flatten().Collect(r => ProjectToWriter(r)).ToList()
|
||||
};
|
||||
|
||||
if (item.IndexNumber.HasValue)
|
||||
{
|
||||
metadata.EpisodeNumber = item.IndexNumber.Value;
|
||||
}
|
||||
|
||||
if (DateTime.TryParse(item.PremiereDate, out DateTime releaseDate))
|
||||
{
|
||||
metadata.ReleaseDate = releaseDate;
|
||||
|
||||
Generated
+2892
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Migrations
|
||||
{
|
||||
public partial class Add_EpisodeMetadataEpisodeNumber : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "EpisodeNumber",
|
||||
table: "EpisodeMetadata",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "EpisodeNumber",
|
||||
table: "EpisodeMetadata");
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+2892
File diff suppressed because it is too large
Load Diff
+17
@@ -0,0 +1,17 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Migrations
|
||||
{
|
||||
public partial class Update_EpisodeMetadataEpisodeNumber : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.Sql(
|
||||
@"UPDATE EpisodeMetadata SET EpisodeNumber = (SELECT EpisodeNumber FROM Episode WHERE Id = EpisodeMetadata.EpisodeId)");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
+2889
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Migrations
|
||||
{
|
||||
public partial class Remove_EpisodeEpisodeNumber : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "EpisodeNumber",
|
||||
table: "Episode");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "EpisodeNumber",
|
||||
table: "Episode",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,6 +348,9 @@ namespace ErsatzTV.Infrastructure.Migrations
|
||||
b.Property<int>("EpisodeId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MetadataKind")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
@@ -1421,9 +1424,6 @@ namespace ErsatzTV.Infrastructure.Migrations
|
||||
{
|
||||
b.HasBaseType("ErsatzTV.Core.Domain.MediaItem");
|
||||
|
||||
b.Property<int>("EpisodeNumber")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeasonId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
|
||||
@@ -655,7 +655,6 @@ namespace ErsatzTV.Infrastructure.Plex
|
||||
var episode = new PlexEpisode
|
||||
{
|
||||
Key = response.Key,
|
||||
EpisodeNumber = response.Index,
|
||||
EpisodeMetadata = new List<EpisodeMetadata> { metadata },
|
||||
MediaVersions = new List<MediaVersion> { version }
|
||||
};
|
||||
@@ -673,6 +672,7 @@ namespace ErsatzTV.Infrastructure.Plex
|
||||
MetadataKind = MetadataKind.External,
|
||||
Title = response.Title,
|
||||
SortTitle = _fallbackMetadataProvider.GetSortTitle(response.Title),
|
||||
EpisodeNumber = response.Index,
|
||||
Plot = response.Summary,
|
||||
Year = response.Year,
|
||||
Tagline = response.Tagline,
|
||||
|
||||
@@ -593,17 +593,15 @@ namespace ErsatzTV.Infrastructure.Search
|
||||
|
||||
try
|
||||
{
|
||||
var doc = new Document
|
||||
{
|
||||
new StringField(IdField, episode.Id.ToString(), Field.Store.YES),
|
||||
new StringField(TypeField, EpisodeType, Field.Store.NO),
|
||||
new TextField(TitleField, metadata.Title, Field.Store.NO),
|
||||
new StringField(SortTitleField, metadata.SortTitle.ToLowerInvariant(), Field.Store.NO),
|
||||
new TextField(LibraryNameField, episode.LibraryPath.Library.Name, Field.Store.NO),
|
||||
new StringField(LibraryIdField, episode.LibraryPath.Library.Id.ToString(), Field.Store.NO),
|
||||
new StringField(TitleAndYearField, GetTitleAndYear(metadata), Field.Store.NO),
|
||||
new StringField(JumpLetterField, GetJumpLetter(metadata), Field.Store.YES)
|
||||
};
|
||||
var doc = new Document();
|
||||
doc.Add(new StringField(IdField, episode.Id.ToString(), Field.Store.YES));
|
||||
doc.Add(new StringField(TypeField, EpisodeType, Field.Store.NO));
|
||||
doc.Add(new TextField(TitleField, metadata.Title, Field.Store.NO));
|
||||
doc.Add(new StringField(SortTitleField, metadata.SortTitle.ToLowerInvariant(), Field.Store.NO));
|
||||
doc.Add(new TextField(LibraryNameField, episode.LibraryPath.Library.Name, Field.Store.NO));
|
||||
doc.Add(new StringField(LibraryIdField, episode.LibraryPath.Library.Id.ToString(), Field.Store.NO));
|
||||
doc.Add(new StringField(TitleAndYearField, GetTitleAndYear(metadata), Field.Store.NO));
|
||||
doc.Add(new StringField(JumpLetterField, GetJumpLetter(metadata), Field.Store.YES));
|
||||
|
||||
AddLanguages(doc, episode.MediaVersions);
|
||||
|
||||
@@ -682,7 +680,7 @@ namespace ErsatzTV.Infrastructure.Search
|
||||
metadata switch
|
||||
{
|
||||
EpisodeMetadata em =>
|
||||
$"{em.Title}_{em.Year}_{em.Episode.Season.SeasonNumber}_{em.Episode.EpisodeNumber}"
|
||||
$"{em.Title}_{em.Year}_{em.Episode.Season.SeasonNumber}_{em.EpisodeNumber}"
|
||||
.ToLowerInvariant(),
|
||||
_ => $"{metadata.Title}_{metadata.Year}".ToLowerInvariant()
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user