|
|
|
@ -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
|
|
|
|
} |
|
|
|
} |
|
|
|
|