add song album_artist metadata (#673)
This commit is contained in:
@@ -13,10 +13,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
### Added
|
||||
- Add automated error reporting via Bugsnag
|
||||
- This can be disabled by editing the `appsettings.json` file or by setting the `Bugsnag:Enable` environment variable to `false`
|
||||
- Add `album_artist` to song metadata and to search index
|
||||
|
||||
### Changed
|
||||
- Framerate normalization will never normalize framerate below 24fps
|
||||
- Instead, content with a lower framerate will be normalized up to 24fps
|
||||
- `Shuffle In Order` will group songs by album artist instead of by track artist
|
||||
|
||||
## [0.4.2-alpha] - 2022-02-26
|
||||
### Fixed
|
||||
|
||||
@@ -4,6 +4,7 @@ public class SongMetadata : Metadata
|
||||
{
|
||||
public string Album { get; set; }
|
||||
public string Artist { get; set; }
|
||||
public string AlbumArtist { get; set; }
|
||||
public string Date { get; set; }
|
||||
public string Track { get; set; }
|
||||
public int SongId { get; set; }
|
||||
|
||||
@@ -232,6 +232,11 @@ public class LocalMetadataProvider : ILocalMetadataProvider
|
||||
result.Artist = artist;
|
||||
}
|
||||
|
||||
if (tags.TryGetValue(MetadataFormatTag.AlbumArtist, out string albumArtist))
|
||||
{
|
||||
result.AlbumArtist = albumArtist;
|
||||
}
|
||||
|
||||
if (tags.TryGetValue(MetadataFormatTag.Date, out string date))
|
||||
{
|
||||
result.Date = date;
|
||||
@@ -766,6 +771,7 @@ public class LocalMetadataProvider : ILocalMetadataProvider
|
||||
{
|
||||
existing.Title = metadata.Title;
|
||||
existing.Artist = metadata.Artist;
|
||||
existing.AlbumArtist = metadata.AlbumArtist;
|
||||
existing.Album = metadata.Album;
|
||||
existing.Date = metadata.Date;
|
||||
existing.Track = metadata.Track;
|
||||
|
||||
@@ -88,9 +88,20 @@ public class LocalStatisticsProvider : ILocalStatisticsProvider
|
||||
result.Add(MetadataFormatTag.Album, ffprobe.format.tags.album);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(ffprobe?.format?.tags?.albumArtist))
|
||||
{
|
||||
result.Add(MetadataFormatTag.AlbumArtist, ffprobe.format.tags.albumArtist);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(ffprobe?.format?.tags?.artist))
|
||||
{
|
||||
result.Add(MetadataFormatTag.Artist, ffprobe.format.tags.artist);
|
||||
|
||||
// if no album artist is present, use the track artist
|
||||
if (!result.ContainsKey(MetadataFormatTag.AlbumArtist))
|
||||
{
|
||||
result.Add(MetadataFormatTag.AlbumArtist, ffprobe.format.tags.artist);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(ffprobe?.format?.tags?.date))
|
||||
@@ -366,6 +377,8 @@ public class LocalStatisticsProvider : ILocalStatisticsProvider
|
||||
public record FFprobeFormatTags(
|
||||
string title,
|
||||
string artist,
|
||||
[property: JsonProperty(PropertyName = "album artist")]
|
||||
string albumArtist,
|
||||
string album,
|
||||
string track,
|
||||
string genre,
|
||||
|
||||
@@ -4,6 +4,7 @@ public static class MetadataFormatTag
|
||||
{
|
||||
public static readonly string Album = "album";
|
||||
public static readonly string Artist = "artist";
|
||||
public static readonly string AlbumArtist = "albumartist";
|
||||
public static readonly string Date = "date";
|
||||
public static readonly string Genre = "genre";
|
||||
public static readonly string Title = "title";
|
||||
|
||||
@@ -312,7 +312,7 @@ public class MediaCollectionRepository : IMediaCollectionRepository
|
||||
|
||||
var allArtists = items.OfType<Song>()
|
||||
.SelectMany(s => s.SongMetadata)
|
||||
.Map(sm => sm.Artist)
|
||||
.Map(sm => sm.AlbumArtist)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
@@ -324,7 +324,7 @@ public class MediaCollectionRepository : IMediaCollectionRepository
|
||||
var songArtistCollections = new Dictionary<int, List<MediaItem>>();
|
||||
foreach (Song song in items.OfType<Song>())
|
||||
{
|
||||
int key = allArtists.IndexOf(song.SongMetadata.HeadOrNone().Match(sm => sm.Artist, string.Empty));
|
||||
int key = allArtists.IndexOf(song.SongMetadata.HeadOrNone().Match(sm => sm.AlbumArtist, string.Empty));
|
||||
|
||||
List<MediaItem> list = songArtistCollections.ContainsKey(key)
|
||||
? songArtistCollections[key]
|
||||
|
||||
@@ -24,7 +24,7 @@ public class MetadataRepository : IMetadataRepository
|
||||
|
||||
public async Task<bool> Update(Metadata metadata)
|
||||
{
|
||||
await using TvContext dbContext = _dbContextFactory.CreateDbContext();
|
||||
await using TvContext dbContext = await _dbContextFactory.CreateDbContextAsync();
|
||||
dbContext.Entry(metadata).State = EntityState.Modified;
|
||||
return await dbContext.SaveChangesAsync() > 0;
|
||||
}
|
||||
|
||||
+3886
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Migrations
|
||||
{
|
||||
public partial class Add_SongMetadataAlbumArtist : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "AlbumArtist",
|
||||
table: "SongMetadata",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AlbumArtist",
|
||||
table: "SongMetadata");
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+3886
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,31 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ErsatzTV.Infrastructure.Migrations
|
||||
{
|
||||
public partial class Reset_SongMetadataAlbumArtist : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.Sql(
|
||||
@"UPDATE LibraryPath SET LastScan = '0001-01-01 00:00:00' WHERE Id IN
|
||||
(SELECT LP.Id FROM LibraryPath LP INNER JOIN Library L on L.Id = LP.LibraryId WHERE MediaKind = 5)");
|
||||
|
||||
migrationBuilder.Sql(
|
||||
@"UPDATE Library SET LastScan = '0001-01-01 00:00:00' WHERE MediaKind = 5");
|
||||
|
||||
migrationBuilder.Sql(
|
||||
@"UPDATE SongMetadata SET DateUpdated = '0001-01-01 00:00:00'");
|
||||
|
||||
migrationBuilder.Sql(
|
||||
@"UPDATE LibraryFolder SET Etag = NULL WHERE Id IN
|
||||
(SELECT LF.Id FROM LibraryFolder LF INNER JOIN LibraryPath LP on LF.LibraryPathId = LP.Id INNER JOIN Library L on LP.LibraryId = L.Id WHERE MediaKind = 5)");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1672,6 +1672,9 @@ namespace ErsatzTV.Infrastructure.Migrations
|
||||
b.Property<string>("Album")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AlbumArtist")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Artist")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ public sealed class SearchIndex : ISearchIndex
|
||||
private const string MinutesField = "minutes";
|
||||
private const string ArtistField = "artist";
|
||||
private const string StateField = "state";
|
||||
private const string AlbumArtistField = "album_artist";
|
||||
|
||||
public const string MovieType = "movie";
|
||||
public const string ShowType = "show";
|
||||
@@ -894,6 +895,11 @@ public sealed class SearchIndex : ISearchIndex
|
||||
doc.Add(new TextField(ArtistField, metadata.Artist, Field.Store.NO));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(metadata.AlbumArtist))
|
||||
{
|
||||
doc.Add(new TextField(AlbumArtistField, metadata.AlbumArtist, Field.Store.NO));
|
||||
}
|
||||
|
||||
foreach (Tag tag in metadata.Tags)
|
||||
{
|
||||
doc.Add(new TextField(TagField, tag.Name, Field.Store.NO));
|
||||
|
||||
@@ -103,7 +103,8 @@ The following fields are available for searching songs:
|
||||
|
||||
- `title`: The song title, or the filename of the song (without extension)
|
||||
- `album`: The song album
|
||||
- `artist`: The song artist
|
||||
- `artist`: The song/track artist
|
||||
- `album_artist`: The album artist
|
||||
- `genre`: The song genre
|
||||
- `tag`: All of the song's parent folders
|
||||
- `minutes`: the rounded-up whole number duration of the song in minutes
|
||||
|
||||
Reference in New Issue
Block a user