using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; using FFmpeg.AutoGen; using FNZCM2.Abstractions.Models.Metadata; using MimeDetective; namespace FNZCM2.Abstractions.Utility; public static class FileMetaReader { private static readonly ContentInspector Inspector; static FileMetaReader() => Inspector = new ContentInspectorBuilder { Definitions = MimeDetective.Definitions.Default.All() }.Build(); public static FileMetaEntry? Read(string path, long fileId, out string err) { var fileType = FileType.Unknown; AudioInfo? audio = null; PictureInfo? pic = null; int? bitRate = null; int? dur = null; MetaInfo meta = null; List codecs = new(); var ir = Inspector.Inspect(path); var irm = ir.ByMimeType().FirstOrDefault(); if (irm == null) { } else if (irm.MimeType.StartsWith("audio/", StringComparison.InvariantCultureIgnoreCase)) fileType = FileType.Audio; else if (irm.MimeType.StartsWith("video/", StringComparison.InvariantCultureIgnoreCase)) fileType = FileType.Video; else if (irm.MimeType.StartsWith("image/", StringComparison.InvariantCultureIgnoreCase)) fileType = FileType.Picture; else if (irm.MimeType.StartsWith("text/", StringComparison.InvariantCultureIgnoreCase)) fileType = FileType.Text; else if (irm.MimeType.Equals("application/pdf", StringComparison.InvariantCultureIgnoreCase)) fileType = FileType.Doc; var frr = FfMpegReader.Read(path, out err); if (fileType == FileType.Unknown && frr != null) { if (frr.Streams.Any(p => p.Type == AVMediaType.AVMEDIA_TYPE_VIDEO) && frr.Streams.Any(p => p.Type == AVMediaType.AVMEDIA_TYPE_AUDIO)) fileType = FileType.Video; } if (fileType is FileType.Unknown or FileType.Text or FileType.Doc) { err = null; return new FileMetaEntry { FileId = fileId, FileType = fileType, Audio = audio, Picture = pic, BitRate = bitRate, DurationMs = dur, Meta = meta, Codecs = codecs.ToArray(), }; } if (frr == null) { return new FileMetaEntry { FileId = fileId, FileType = fileType, Audio = audio, Picture = pic, BitRate = bitRate, DurationMs = dur, Meta = meta, Codecs = codecs.ToArray(), }; } switch (fileType) { case FileType.Picture: { var s = frr.Streams.FirstOrDefault(p => p.Type == AVMediaType.AVMEDIA_TYPE_VIDEO); pic = s?.PictureInfo; if (s != null) codecs.Add(s.Codec); } break; case FileType.Audio: { dur = frr.DurationMs; bitRate = frr.BitRate; var s = frr.Streams.FirstOrDefault(p => p.Type == AVMediaType.AVMEDIA_TYPE_AUDIO); audio = s?.AudioInfo; if (s != null) codecs.Add(s.Codec); } break; case FileType.Video: { dur = frr.DurationMs; bitRate = frr.BitRate; var sv = frr.Streams.FirstOrDefault(p => p.Type == AVMediaType.AVMEDIA_TYPE_VIDEO); pic = sv?.PictureInfo; if (sv != null) codecs.Add(sv.Codec); var sa = frr.Streams.FirstOrDefault(p => p.Type == AVMediaType.AVMEDIA_TYPE_AUDIO); audio = sa?.AudioInfo; if (sa != null) codecs.Add(sa.Codec); } break; } if (frr.MetaData?.Count > 0) { var other = new Dictionary(); string? a = null; string? b = null; string? c = null; int? d = null; int? g = null; string? e = null; string? t = null; string? p = null; string? v = null; string? q = null; string? r = null; string? l = null; int? n = null; int? o = null; foreach (var kv in frr.MetaData) { switch (kv.Key) { case "album": a = kv.Value; break; case "catalogid": c = kv.Value; break; case "composer": q = kv.Value; break; case "title": t = kv.Value; break; case "event": e = kv.Value; break; case "genre": l = kv.Value; break; case "album_artist": case "album artist": if (b != null) other[kv.Key] = kv.Value; else b = kv.Value; break; case "artist": case "performer": if (p != null) other[kv.Key] = kv.Value; else p = kv.Value; break; case "vocal": case "voice": if (v != null) other[kv.Key] = kv.Value; else v = kv.Value; break; case "date": if (DateTime.TryParseExact(kv.Value, "yyyy", null, DateTimeStyles.None, out var dtYear)) { r = $"{dtYear.Year}-??-??"; } else if (DateTime.TryParseExact(kv.Value, "yyyy.MM", null, DateTimeStyles.None, out var dtDotYm)) { r = $"{dtDotYm.Year}-{dtDotYm.Month:00}-??"; } else if (DateTime.TryParseExact(kv.Value, "yyyy.MM.dd", null, DateTimeStyles.None, out var dtDotYmd)) { r = $"{dtDotYmd.Year}-{dtDotYmd.Month:00}-{dtDotYmd.Day:00}"; } else if (DateTime.TryParseExact(kv.Value, "yyyy-MM-dd", null, DateTimeStyles.None, out var dtDaYmd)) { r = $"{dtDaYmd.Year}-{dtDaYmd.Month:00}-{dtDaYmd.Day:00}"; } else if (DateTime.TryParse(kv.Value, out var pd)) { r = $"{pd.Year}-{pd.Month:00}-{pd.Day:00}"; } else { other[kv.Key] = kv.Value; } break; case "disc": var parts = kv.Value.Split('/'); if (parts.Length == 2 && int.TryParse(parts[0], out var d1) && int.TryParse(parts[1], out var d2)) { n = d1; o = d2; } else if (int.TryParse(kv.Value, out var disc)) d = disc; else goto default; break; case "disctotal": case "totaldiscs": if (int.TryParse(kv.Value, out var dt)) g = dt; else goto default; break; case "tracktotal": case "totaltracks": if (int.TryParse(kv.Value, out var to)) o = to; else goto default; break; case "track": if (int.TryParse(kv.Value, out var tn)) n = tn; else goto default; break; case "discid": other[kv.Key] = kv.Value; break; case "comment": other[kv.Key] = kv.Value; break; case "origin": other[kv.Key] = kv.Value; break; case "originaltitle": other[kv.Key] = kv.Value; break; case "mastering": other[kv.Key] = kv.Value; break; case "replaygain_album_gain": other[kv.Key] = kv.Value; break; case "replaygain_album_peak": other[kv.Key] = kv.Value; break; case "replaygain_track_gain": other[kv.Key] = kv.Value; break; case "replaygain_track_peak": other[kv.Key] = kv.Value; break; case "engineer": other[kv.Key] = kv.Value; break; case "catalog": other[kv.Key] = kv.Value; break; case "remixer": other[kv.Key] = kv.Value; break; case "lyricist": other[kv.Key] = kv.Value; break; case "guitar": other[kv.Key] = kv.Value; break; case "encoder": other[kv.Key] = kv.Value; break; case "itunes_cddb_1": other[kv.Key] = kv.Value; break; case "synthesizer": other[kv.Key] = kv.Value; break; case "acoustic guitar": other[kv.Key] = kv.Value; break; case "midi guitar": other[kv.Key] = kv.Value; break; case "chorus": other[kv.Key] = kv.Value; break; case "bass": other[kv.Key] = kv.Value; break; case "drums": other[kv.Key] = kv.Value; break; case "viola": other[kv.Key] = kv.Value; break; case "violin": other[kv.Key] = kv.Value; break; case "isrc": other[kv.Key] = kv.Value; break; case "wind synthesizer": other[kv.Key] = kv.Value; break; case "saxophone": other[kv.Key] = kv.Value; break; case "piano": other[kv.Key] = kv.Value; break; case "arrange": other[kv.Key] = kv.Value; break; case "check": other[kv.Key] = kv.Value; break; case "retrievable": other[kv.Key] = kv.Value; break; case "source": other[kv.Key] = kv.Value; break; case "vpc": other[kv.Key] = kv.Value; break; default: if (kv.Key.StartsWith("cue_track") && kv.Key.EndsWith("_artist")) { other[kv.Key] = kv.Value; } else { other[kv.Key] = kv.Value; } break; } } meta = new() { Album = a, AlbumArtist = b, Catalog = c, Disc = d, DiscTotal = g, Event = e, Title = t, Artist = p, Vocal = v, Composer = q, Release = r, Track = n, TrackTotal = o, Other = other.AsReadOnly(), Genre = l, }; } //throw new NotImplementedException(); return new FileMetaEntry { FileId = fileId, FileType = fileType, Audio = audio, Picture = pic, BitRate = bitRate, DurationMs = dur, Meta = meta, Codecs = codecs.ToArray(), }; } }