Browse Source

fix ogg song metadata (#1030)

pull/1032/head
Jason Dove 4 years ago committed by GitHub
parent
commit
6a640d3708
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 2
      ErsatzTV.Core/Interfaces/Metadata/ILocalStatisticsProvider.cs
  3. 16
      ErsatzTV.Core/Metadata/LocalMetadataProvider.cs
  4. 160
      ErsatzTV.Core/Metadata/LocalStatisticsProvider.cs
  5. 2
      ErsatzTV.Core/Metadata/MetadataSongTag.cs

1
CHANGELOG.md

@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix removing Jellyfin and Emby libraries that have been deleted from the source media server - Fix removing Jellyfin and Emby libraries that have been deleted from the source media server
- Fix `Work-Ahead HLS Segmenter Limit` setting to properly limit number of channels that can work-ahead at once - Fix `Work-Ahead HLS Segmenter Limit` setting to properly limit number of channels that can work-ahead at once
- Include base path value in generated channel playlist (M3U) and channel guide (XMLTV) links - Include base path value in generated channel playlist (M3U) and channel guide (XMLTV) links
- Fix parsing song metadata from OGG audio files
### Added ### Added
- Add audio stream selector scripts for episodes and movies - Add audio stream selector scripts for episodes and movies

2
ErsatzTV.Core/Interfaces/Metadata/ILocalStatisticsProvider.cs

@ -12,5 +12,5 @@ public interface ILocalStatisticsProvider
MediaItem mediaItem, MediaItem mediaItem,
string mediaItemPath); string mediaItemPath);
Task<Either<BaseError, Dictionary<string, string>>> GetFormatTags(string ffprobePath, MediaItem mediaItem); Task<Either<BaseError, Dictionary<string, string>>> GetSongTags(string ffprobePath, MediaItem mediaItem);
} }

16
ErsatzTV.Core/Metadata/LocalMetadataProvider.cs

@ -277,7 +277,7 @@ public class LocalMetadataProvider : ILocalMetadataProvider
try try
{ {
Either<BaseError, Dictionary<string, string>> maybeTags = Either<BaseError, Dictionary<string, string>> maybeTags =
await _localStatisticsProvider.GetFormatTags(ffprobePath, song); await _localStatisticsProvider.GetSongTags(ffprobePath, song);
foreach (Dictionary<string, string> tags in maybeTags.RightToSeq()) foreach (Dictionary<string, string> tags in maybeTags.RightToSeq())
{ {
@ -297,37 +297,37 @@ public class LocalMetadataProvider : ILocalMetadataProvider
Tags = new List<Tag>() Tags = new List<Tag>()
}; };
if (tags.TryGetValue(MetadataFormatTag.Album, out string album)) if (tags.TryGetValue(MetadataSongTag.Album, out string album))
{ {
result.Album = album; result.Album = album;
} }
if (tags.TryGetValue(MetadataFormatTag.Artist, out string artist)) if (tags.TryGetValue(MetadataSongTag.Artist, out string artist))
{ {
result.Artist = artist; result.Artist = artist;
} }
if (tags.TryGetValue(MetadataFormatTag.AlbumArtist, out string albumArtist)) if (tags.TryGetValue(MetadataSongTag.AlbumArtist, out string albumArtist))
{ {
result.AlbumArtist = albumArtist; result.AlbumArtist = albumArtist;
} }
if (tags.TryGetValue(MetadataFormatTag.Date, out string date)) if (tags.TryGetValue(MetadataSongTag.Date, out string date))
{ {
result.Date = date; result.Date = date;
} }
if (tags.TryGetValue(MetadataFormatTag.Genre, out string genre)) if (tags.TryGetValue(MetadataSongTag.Genre, out string genre))
{ {
result.Genres.AddRange(SplitGenres(genre).Map(n => new Genre { Name = n })); result.Genres.AddRange(SplitGenres(genre).Map(n => new Genre { Name = n }));
} }
if (tags.TryGetValue(MetadataFormatTag.Title, out string title)) if (tags.TryGetValue(MetadataSongTag.Title, out string title))
{ {
result.Title = title; result.Title = title;
} }
if (tags.TryGetValue(MetadataFormatTag.Track, out string track)) if (tags.TryGetValue(MetadataSongTag.Track, out string track))
{ {
result.Track = track; result.Track = track;
} }

160
ErsatzTV.Core/Metadata/LocalStatisticsProvider.cs

@ -82,7 +82,7 @@ public class LocalStatisticsProvider : ILocalStatisticsProvider
} }
} }
public async Task<Either<BaseError, Dictionary<string, string>>> GetFormatTags( public async Task<Either<BaseError, Dictionary<string, string>>> GetSongTags(
string ffprobePath, string ffprobePath,
MediaItem mediaItem) MediaItem mediaItem)
{ {
@ -90,55 +90,109 @@ public class LocalStatisticsProvider : ILocalStatisticsProvider
{ {
string mediaItemPath = mediaItem.GetHeadVersion().MediaFiles.Head().Path; string mediaItemPath = mediaItem.GetHeadVersion().MediaFiles.Head().Path;
Either<BaseError, FFprobe> maybeProbe = await GetProbeOutput(ffprobePath, mediaItemPath); Either<BaseError, FFprobe> maybeProbe = await GetProbeOutput(ffprobePath, mediaItemPath);
return maybeProbe.Match( foreach (BaseError error in maybeProbe.LeftToSeq())
ffprobe => {
{ return error;
var result = new Dictionary<string, string>(); }
if (!string.IsNullOrWhiteSpace(ffprobe?.format?.tags?.album)) Option<FFprobeTags> maybeFormatTags = maybeProbe.RightToSeq()
{ .Map(p => p?.format?.tags ?? FFprobeTags.Empty)
result.Add(MetadataFormatTag.Album, ffprobe.format.tags.album); .HeadOrNone();
}
if (!string.IsNullOrWhiteSpace(ffprobe?.format?.tags?.albumArtist)) Option<FFprobeTags> maybeAudioTags = maybeProbe.RightToSeq()
{ .Bind(p => p.streams.Filter(s => s.codec_type == "audio").HeadOrNone())
result.Add(MetadataFormatTag.AlbumArtist, ffprobe.format.tags.albumArtist); .Map(s => s.tags ?? FFprobeTags.Empty)
} .HeadOrNone();
if (!string.IsNullOrWhiteSpace(ffprobe?.format?.tags?.artist)) foreach (FFprobeTags formatTags in maybeFormatTags)
{ foreach (FFprobeTags audioTags in maybeAudioTags)
result.Add(MetadataFormatTag.Artist, ffprobe.format.tags.artist); {
var result = new Dictionary<string, string>();
// if no album artist is present, use the track artist // album
if (!result.ContainsKey(MetadataFormatTag.AlbumArtist)) if (!string.IsNullOrWhiteSpace(formatTags.album))
{ {
result.Add(MetadataFormatTag.AlbumArtist, ffprobe.format.tags.artist); result.Add(MetadataSongTag.Album, formatTags.album);
} }
} else if (!string.IsNullOrWhiteSpace(audioTags.album))
{
result.Add(MetadataSongTag.Album, audioTags.album);
}
if (!string.IsNullOrWhiteSpace(ffprobe?.format?.tags?.date)) // album artist
{ if (!string.IsNullOrWhiteSpace(formatTags.albumArtist))
result.Add(MetadataFormatTag.Date, ffprobe.format.tags.date); {
} result.Add(MetadataSongTag.AlbumArtist, formatTags.albumArtist);
}
else if (!string.IsNullOrWhiteSpace(audioTags.albumArtist))
{
result.Add(MetadataSongTag.AlbumArtist, audioTags.albumArtist);
}
if (!string.IsNullOrWhiteSpace(ffprobe?.format?.tags?.genre)) // artist
{ if (!string.IsNullOrWhiteSpace(formatTags.artist))
result.Add(MetadataFormatTag.Genre, ffprobe.format.tags.genre); {
} result.Add(MetadataSongTag.Artist, formatTags.artist);
if (!string.IsNullOrWhiteSpace(ffprobe?.format?.tags?.title)) // if no album artist is present, use the track artist
if (!result.ContainsKey(MetadataSongTag.AlbumArtist))
{ {
result.Add(MetadataFormatTag.Title, ffprobe.format.tags.title); result.Add(MetadataSongTag.AlbumArtist, formatTags.artist);
} }
}
else if (!string.IsNullOrWhiteSpace(audioTags.artist))
{
result.Add(MetadataSongTag.Artist, audioTags.artist);
if (!string.IsNullOrWhiteSpace(ffprobe?.format?.tags?.track)) // if no album artist is present, use the track artist
if (!result.ContainsKey(MetadataSongTag.AlbumArtist))
{ {
result.Add(MetadataFormatTag.Track, ffprobe.format.tags.track); result.Add(MetadataSongTag.AlbumArtist, audioTags.artist);
} }
}
return Right<BaseError, Dictionary<string, string>>(result); // date
}, if (!string.IsNullOrWhiteSpace(formatTags.date))
Left<BaseError, Dictionary<string, string>>); {
result.Add(MetadataSongTag.Date, formatTags.date);
}
else if (!string.IsNullOrWhiteSpace(audioTags.date))
{
result.Add(MetadataSongTag.Date, audioTags.date);
}
// genre
if (!string.IsNullOrWhiteSpace(formatTags.genre))
{
result.Add(MetadataSongTag.Genre, formatTags.genre);
}
else if (!string.IsNullOrWhiteSpace(audioTags.genre))
{
result.Add(MetadataSongTag.Genre, audioTags.genre);
}
// title
if (!string.IsNullOrWhiteSpace(formatTags.title))
{
result.Add(MetadataSongTag.Title, formatTags.title);
}
else if (!string.IsNullOrWhiteSpace(audioTags.title))
{
result.Add(MetadataSongTag.Title, audioTags.title);
}
// track
if (!string.IsNullOrWhiteSpace(formatTags.track))
{
result.Add(MetadataSongTag.Track, formatTags.track);
}
else if (!string.IsNullOrWhiteSpace(audioTags.track))
{
result.Add(MetadataSongTag.Track, audioTags.track);
}
return result;
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -146,6 +200,8 @@ public class LocalStatisticsProvider : ILocalStatisticsProvider
_client.Notify(ex); _client.Notify(ex);
return BaseError.New(ex.Message); return BaseError.New(ex.Message);
} }
return BaseError.New("BUG - this should never happen");
} }
private async Task<bool> ApplyVersionUpdate(MediaItem mediaItem, MediaVersion version, string filePath) private async Task<bool> ApplyVersionUpdate(MediaItem mediaItem, MediaVersion version, string filePath)
@ -467,22 +523,10 @@ public class LocalStatisticsProvider : ILocalStatisticsProvider
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
public record FFprobe(FFprobeFormat format, List<FFprobeStream> streams, List<FFprobeChapter> chapters); public record FFprobe(FFprobeFormat format, List<FFprobeStream> streams, List<FFprobeChapter> chapters);
public record FFprobeFormat(string duration, FFprobeFormatTags tags); public record FFprobeFormat(string duration, FFprobeTags tags);
public record FFprobeDisposition(int @default, int forced, int attached_pic); public record FFprobeDisposition(int @default, int forced, int attached_pic);
public record FFprobeTags(string language, string title, string filename, string mimetype);
public record FFprobeFormatTags(
string title,
string artist,
[property: JsonProperty(PropertyName = "album_artist")]
string albumArtist,
string album,
string track,
string genre,
string date);
public record FFprobeStream( public record FFprobeStream(
int index, int index,
string codec_name, string codec_name,
@ -505,5 +549,21 @@ public class LocalStatisticsProvider : ILocalStatisticsProvider
string start_time, string start_time,
string end_time, string end_time,
FFprobeTags tags); FFprobeTags tags);
public record FFprobeTags(
string language,
string title,
string filename,
string mimetype,
string artist,
[property: JsonProperty(PropertyName = "album_artist")]
string albumArtist,
string album,
string track,
string genre,
string date)
{
public static readonly FFprobeTags Empty = new(null, null, null, null, null, null, null, null, null, null);
}
// ReSharper restore InconsistentNaming // ReSharper restore InconsistentNaming
} }

2
ErsatzTV.Core/Metadata/MetadataFormatTag.cs → ErsatzTV.Core/Metadata/MetadataSongTag.cs

@ -1,6 +1,6 @@
namespace ErsatzTV.Core.Metadata; namespace ErsatzTV.Core.Metadata;
public static class MetadataFormatTag public static class MetadataSongTag
{ {
public static readonly string Album = "album"; public static readonly string Album = "album";
public static readonly string Artist = "artist"; public static readonly string Artist = "artist";
Loading…
Cancel
Save