FileMetaReader.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. using FFmpeg.AutoGen;
  8. using FNZCM2.Abstractions.Models.Metadata;
  9. using MimeDetective;
  10. namespace FNZCM2.Abstractions.Utility;
  11. public static class FileMetaReader
  12. {
  13. private static readonly ContentInspector Inspector;
  14. static FileMetaReader() => Inspector = new ContentInspectorBuilder { Definitions = MimeDetective.Definitions.Default.All() }.Build();
  15. public static FileMetaEntry? Read(string path, long fileId, out string err)
  16. {
  17. var fileType = FileType.Unknown;
  18. AudioInfo? audio = null;
  19. PictureInfo? pic = null;
  20. int? bitRate = null;
  21. int? dur = null;
  22. MetaInfo meta = null;
  23. List<AVCodecID> codecs = new();
  24. var ir = Inspector.Inspect(path);
  25. var irm = ir.ByMimeType().FirstOrDefault();
  26. if (irm == null)
  27. {
  28. }
  29. else if (irm.MimeType.StartsWith("audio/", StringComparison.InvariantCultureIgnoreCase)) fileType = FileType.Audio;
  30. else if (irm.MimeType.StartsWith("video/", StringComparison.InvariantCultureIgnoreCase)) fileType = FileType.Video;
  31. else if (irm.MimeType.StartsWith("image/", StringComparison.InvariantCultureIgnoreCase)) fileType = FileType.Picture;
  32. else if (irm.MimeType.StartsWith("text/", StringComparison.InvariantCultureIgnoreCase)) fileType = FileType.Text;
  33. else if (irm.MimeType.Equals("application/pdf", StringComparison.InvariantCultureIgnoreCase)) fileType = FileType.Doc;
  34. var frr = FfMpegReader.Read(path, out err);
  35. if (fileType == FileType.Unknown && frr != null)
  36. {
  37. if (frr.Streams.Any(p => p.Type == AVMediaType.AVMEDIA_TYPE_VIDEO)
  38. && frr.Streams.Any(p => p.Type == AVMediaType.AVMEDIA_TYPE_AUDIO)) fileType = FileType.Video;
  39. }
  40. if (fileType is FileType.Unknown or FileType.Text or FileType.Doc)
  41. {
  42. err = null;
  43. return new FileMetaEntry
  44. {
  45. FileId = fileId,
  46. FileType = fileType,
  47. Audio = audio,
  48. Picture = pic,
  49. BitRate = bitRate,
  50. DurationMs = dur,
  51. Meta = meta,
  52. Codecs = codecs.ToArray(),
  53. };
  54. }
  55. if (frr == null)
  56. {
  57. return new FileMetaEntry
  58. {
  59. FileId = fileId,
  60. FileType = fileType,
  61. Audio = audio,
  62. Picture = pic,
  63. BitRate = bitRate,
  64. DurationMs = dur,
  65. Meta = meta,
  66. Codecs = codecs.ToArray(),
  67. };
  68. }
  69. switch (fileType)
  70. {
  71. case FileType.Picture:
  72. {
  73. var s = frr.Streams.FirstOrDefault(p => p.Type == AVMediaType.AVMEDIA_TYPE_VIDEO);
  74. pic = s?.PictureInfo;
  75. if (s != null) codecs.Add(s.Codec);
  76. }
  77. break;
  78. case FileType.Audio:
  79. {
  80. dur = frr.DurationMs;
  81. bitRate = frr.BitRate;
  82. var s = frr.Streams.FirstOrDefault(p => p.Type == AVMediaType.AVMEDIA_TYPE_AUDIO);
  83. audio = s?.AudioInfo;
  84. if (s != null) codecs.Add(s.Codec);
  85. }
  86. break;
  87. case FileType.Video:
  88. {
  89. dur = frr.DurationMs;
  90. bitRate = frr.BitRate;
  91. var sv = frr.Streams.FirstOrDefault(p => p.Type == AVMediaType.AVMEDIA_TYPE_VIDEO);
  92. pic = sv?.PictureInfo;
  93. if (sv != null) codecs.Add(sv.Codec);
  94. var sa = frr.Streams.FirstOrDefault(p => p.Type == AVMediaType.AVMEDIA_TYPE_AUDIO);
  95. audio = sa?.AudioInfo;
  96. if (sa != null) codecs.Add(sa.Codec);
  97. }
  98. break;
  99. }
  100. if (frr.MetaData?.Count > 0)
  101. {
  102. var other = new Dictionary<string, string>();
  103. string? a = null;
  104. string? b = null;
  105. string? c = null;
  106. int? d = null;
  107. int? g = null;
  108. string? e = null;
  109. string? t = null;
  110. string? p = null;
  111. string? v = null;
  112. string? q = null;
  113. string? r = null;
  114. string? l = null;
  115. int? n = null;
  116. int? o = null;
  117. foreach (var kv in frr.MetaData)
  118. {
  119. switch (kv.Key)
  120. {
  121. case "album": a = kv.Value; break;
  122. case "catalogid": c = kv.Value; break;
  123. case "composer": q = kv.Value; break;
  124. case "title": t = kv.Value; break;
  125. case "event": e = kv.Value; break;
  126. case "genre": l = kv.Value; break;
  127. case "album_artist":
  128. case "album artist":
  129. if (b != null) other[kv.Key] = kv.Value; else b = kv.Value;
  130. break;
  131. case "artist":
  132. case "performer":
  133. if (p != null) other[kv.Key] = kv.Value; else p = kv.Value;
  134. break;
  135. case "vocal":
  136. case "voice":
  137. if (v != null) other[kv.Key] = kv.Value; else v = kv.Value;
  138. break;
  139. case "date":
  140. if (DateTime.TryParseExact(kv.Value, "yyyy", null, DateTimeStyles.None, out var dtYear))
  141. {
  142. r = $"{dtYear.Year}-??-??";
  143. }
  144. else if (DateTime.TryParseExact(kv.Value, "yyyy.MM", null, DateTimeStyles.None, out var dtDotYm))
  145. {
  146. r = $"{dtDotYm.Year}-{dtDotYm.Month:00}-??";
  147. }
  148. else if (DateTime.TryParseExact(kv.Value, "yyyy.MM.dd", null, DateTimeStyles.None, out var dtDotYmd))
  149. {
  150. r = $"{dtDotYmd.Year}-{dtDotYmd.Month:00}-{dtDotYmd.Day:00}";
  151. }
  152. else if (DateTime.TryParseExact(kv.Value, "yyyy-MM-dd", null, DateTimeStyles.None, out var dtDaYmd))
  153. {
  154. r = $"{dtDaYmd.Year}-{dtDaYmd.Month:00}-{dtDaYmd.Day:00}";
  155. }
  156. else if (DateTime.TryParse(kv.Value, out var pd))
  157. {
  158. r = $"{pd.Year}-{pd.Month:00}-{pd.Day:00}";
  159. }
  160. else
  161. {
  162. other[kv.Key] = kv.Value;
  163. }
  164. break;
  165. case "disc":
  166. var parts = kv.Value.Split('/');
  167. if (parts.Length == 2 && int.TryParse(parts[0], out var d1) && int.TryParse(parts[1], out var d2))
  168. {
  169. n = d1; o = d2;
  170. }
  171. else if (int.TryParse(kv.Value, out var disc))
  172. d = disc;
  173. else
  174. goto default;
  175. break;
  176. case "disctotal":
  177. case "totaldiscs":
  178. if (int.TryParse(kv.Value, out var dt))
  179. g = dt;
  180. else
  181. goto default;
  182. break;
  183. case "tracktotal":
  184. case "totaltracks":
  185. if (int.TryParse(kv.Value, out var to))
  186. o = to;
  187. else
  188. goto default;
  189. break;
  190. case "track":
  191. if (int.TryParse(kv.Value, out var tn))
  192. n = tn;
  193. else
  194. goto default;
  195. break;
  196. case "discid": other[kv.Key] = kv.Value; break;
  197. case "comment": other[kv.Key] = kv.Value; break;
  198. case "origin": other[kv.Key] = kv.Value; break;
  199. case "originaltitle": other[kv.Key] = kv.Value; break;
  200. case "mastering": other[kv.Key] = kv.Value; break;
  201. case "replaygain_album_gain": other[kv.Key] = kv.Value; break;
  202. case "replaygain_album_peak": other[kv.Key] = kv.Value; break;
  203. case "replaygain_track_gain": other[kv.Key] = kv.Value; break;
  204. case "replaygain_track_peak": other[kv.Key] = kv.Value; break;
  205. case "engineer": other[kv.Key] = kv.Value; break;
  206. case "catalog": other[kv.Key] = kv.Value; break;
  207. case "remixer": other[kv.Key] = kv.Value; break;
  208. case "lyricist": other[kv.Key] = kv.Value; break;
  209. case "guitar": other[kv.Key] = kv.Value; break;
  210. case "encoder": other[kv.Key] = kv.Value; break;
  211. case "itunes_cddb_1": other[kv.Key] = kv.Value; break;
  212. case "synthesizer": other[kv.Key] = kv.Value; break;
  213. case "acoustic guitar": other[kv.Key] = kv.Value; break;
  214. case "midi guitar": other[kv.Key] = kv.Value; break;
  215. case "chorus": other[kv.Key] = kv.Value; break;
  216. case "bass": other[kv.Key] = kv.Value; break;
  217. case "drums": other[kv.Key] = kv.Value; break;
  218. case "viola": other[kv.Key] = kv.Value; break;
  219. case "violin": other[kv.Key] = kv.Value; break;
  220. case "isrc": other[kv.Key] = kv.Value; break;
  221. case "wind synthesizer": other[kv.Key] = kv.Value; break;
  222. case "saxophone": other[kv.Key] = kv.Value; break;
  223. case "piano": other[kv.Key] = kv.Value; break;
  224. case "arrange": other[kv.Key] = kv.Value; break;
  225. case "check": other[kv.Key] = kv.Value; break;
  226. case "retrievable": other[kv.Key] = kv.Value; break;
  227. case "source": other[kv.Key] = kv.Value; break;
  228. case "vpc": other[kv.Key] = kv.Value; break;
  229. default:
  230. if (kv.Key.StartsWith("cue_track") && kv.Key.EndsWith("_artist"))
  231. {
  232. other[kv.Key] = kv.Value;
  233. }
  234. else
  235. {
  236. other[kv.Key] = kv.Value;
  237. }
  238. break;
  239. }
  240. }
  241. meta = new()
  242. {
  243. Album = a,
  244. AlbumArtist = b,
  245. Catalog = c,
  246. Disc = d,
  247. DiscTotal = g,
  248. Event = e,
  249. Title = t,
  250. Artist = p,
  251. Vocal = v,
  252. Composer = q,
  253. Release = r,
  254. Track = n,
  255. TrackTotal = o,
  256. Other = other.AsReadOnly(),
  257. Genre = l,
  258. };
  259. }
  260. //throw new NotImplementedException();
  261. return new FileMetaEntry
  262. {
  263. FileId = fileId,
  264. FileType = fileType,
  265. Audio = audio,
  266. Picture = pic,
  267. BitRate = bitRate,
  268. DurationMs = dur,
  269. Meta = meta,
  270. Codecs = codecs.ToArray(),
  271. };
  272. }
  273. }