diff --git a/MediaBrowser.Api/ApiService.cs b/MediaBrowser.Api/ApiService.cs index 03ea97fd0e..44ba988643 100644 --- a/MediaBrowser.Api/ApiService.cs +++ b/MediaBrowser.Api/ApiService.cs @@ -117,6 +117,22 @@ namespace MediaBrowser.Api }; } + Video video = item as Video; + + if (video != null) + { + dto.VideoInfo = new VideoInfo() + { + Height = video.Height, + Width = video.Width, + Codec = video.Codec, + VideoType = video.VideoType, + AudioStreams = video.AudioStreams, + Subtitles = video.Subtitles, + ScanType = video.ScanType + }; + } + return dto; } diff --git a/MediaBrowser.ApiInteraction/ApiClient.cs b/MediaBrowser.ApiInteraction/ApiClient.cs index f715e842e3..04a7ad2146 100644 --- a/MediaBrowser.ApiInteraction/ApiClient.cs +++ b/MediaBrowser.ApiInteraction/ApiClient.cs @@ -154,7 +154,7 @@ namespace MediaBrowser.ApiInteraction /// public Task GetImageStreamAsync(string url) { - return HttpClient.GetStreamAsync(url); + return GetStreamAsync(url); } /// @@ -169,7 +169,7 @@ namespace MediaBrowser.ApiInteraction url += "&id=" + id.ToString(); } - using (Stream stream = await HttpClient.GetStreamAsync(url).ConfigureAwait(false)) + using (Stream stream = await GetStreamAsync(url).ConfigureAwait(false)) { return JsonSerializer.DeserializeFromStream(stream); } @@ -182,7 +182,7 @@ namespace MediaBrowser.ApiInteraction { string url = ApiUrl + "/users"; - using (Stream stream = await HttpClient.GetStreamAsync(url).ConfigureAwait(false)) + using (Stream stream = await GetStreamAsync(url).ConfigureAwait(false)) { return JsonSerializer.DeserializeFromStream>(stream); } @@ -195,7 +195,7 @@ namespace MediaBrowser.ApiInteraction { string url = ApiUrl + "/genres?userId=" + userId.ToString(); - using (Stream stream = await HttpClient.GetStreamAsync(url).ConfigureAwait(false)) + using (Stream stream = await GetStreamAsync(url).ConfigureAwait(false)) { return JsonSerializer.DeserializeFromStream>>(stream); } @@ -208,7 +208,7 @@ namespace MediaBrowser.ApiInteraction { string url = ApiUrl + "/years?userId=" + userId.ToString(); - using (Stream stream = await HttpClient.GetStreamAsync(url).ConfigureAwait(false)) + using (Stream stream = await GetStreamAsync(url).ConfigureAwait(false)) { return JsonSerializer.DeserializeFromStream>>(stream); } @@ -221,7 +221,7 @@ namespace MediaBrowser.ApiInteraction { string url = ApiUrl + "/itemlist?listtype=itemswithyear&userId=" + userId.ToString() + "&name=" + name; - using (Stream stream = await HttpClient.GetStreamAsync(url).ConfigureAwait(false)) + using (Stream stream = await GetStreamAsync(url).ConfigureAwait(false)) { return JsonSerializer.DeserializeFromStream>(stream); } @@ -234,7 +234,7 @@ namespace MediaBrowser.ApiInteraction { string url = ApiUrl + "/itemlist?listtype=itemswithgenre&userId=" + userId.ToString() + "&name=" + name; - using (Stream stream = await HttpClient.GetStreamAsync(url).ConfigureAwait(false)) + using (Stream stream = await GetStreamAsync(url).ConfigureAwait(false)) { return JsonSerializer.DeserializeFromStream>(stream); } @@ -247,7 +247,7 @@ namespace MediaBrowser.ApiInteraction { string url = ApiUrl + "/itemlist?listtype=itemswithperson&userId=" + userId.ToString() + "&name=" + name; - using (Stream stream = await HttpClient.GetStreamAsync(url).ConfigureAwait(false)) + using (Stream stream = await GetStreamAsync(url).ConfigureAwait(false)) { return JsonSerializer.DeserializeFromStream>(stream); } @@ -262,7 +262,7 @@ namespace MediaBrowser.ApiInteraction url += "&persontype=" + personType; - using (Stream stream = await HttpClient.GetStreamAsync(url).ConfigureAwait(false)) + using (Stream stream = await GetStreamAsync(url).ConfigureAwait(false)) { return JsonSerializer.DeserializeFromStream>(stream); } @@ -275,7 +275,7 @@ namespace MediaBrowser.ApiInteraction { string url = ApiUrl + "/studios?userId=" + userId.ToString(); - using (Stream stream = await HttpClient.GetStreamAsync(url).ConfigureAwait(false)) + using (Stream stream = await GetStreamAsync(url).ConfigureAwait(false)) { return JsonSerializer.DeserializeFromStream>>(stream); } @@ -288,7 +288,7 @@ namespace MediaBrowser.ApiInteraction { string url = ApiUrl + "/itemlist?listtype=itemswithstudio&userId=" + userId.ToString() + "&name=" + name; - using (Stream stream = await HttpClient.GetStreamAsync(url).ConfigureAwait(false)) + using (Stream stream = await GetStreamAsync(url).ConfigureAwait(false)) { return JsonSerializer.DeserializeFromStream>(stream); } @@ -301,7 +301,7 @@ namespace MediaBrowser.ApiInteraction { string url = ApiUrl + "/studio?userId=" + userId.ToString() + "&name=" + name; - using (Stream stream = await HttpClient.GetStreamAsync(url).ConfigureAwait(false)) + using (Stream stream = await GetStreamAsync(url).ConfigureAwait(false)) { return JsonSerializer.DeserializeFromStream>(stream); } @@ -314,7 +314,7 @@ namespace MediaBrowser.ApiInteraction { string url = ApiUrl + "/genre?userId=" + userId.ToString() + "&name=" + name; - using (Stream stream = await HttpClient.GetStreamAsync(url).ConfigureAwait(false)) + using (Stream stream = await GetStreamAsync(url).ConfigureAwait(false)) { return JsonSerializer.DeserializeFromStream>(stream); } @@ -327,7 +327,7 @@ namespace MediaBrowser.ApiInteraction { string url = ApiUrl + "/person?userId=" + userId.ToString() + "&name=" + name; - using (Stream stream = await HttpClient.GetStreamAsync(url).ConfigureAwait(false)) + using (Stream stream = await GetStreamAsync(url).ConfigureAwait(false)) { return JsonSerializer.DeserializeFromStream>(stream); } @@ -340,12 +340,17 @@ namespace MediaBrowser.ApiInteraction { string url = ApiUrl + "/year?userId=" + userId.ToString() + "&year=" + year; - using (Stream stream = await HttpClient.GetStreamAsync(url).ConfigureAwait(false)) + using (Stream stream = await GetStreamAsync(url).ConfigureAwait(false)) { return JsonSerializer.DeserializeFromStream>(stream); } } + private Task GetStreamAsync(string url) + { + return GetStreamAsync(url); + } + public void Dispose() { HttpClient.Dispose(); diff --git a/MediaBrowser.Controller/FFMpeg/FFProbeResult.cs b/MediaBrowser.Controller/FFMpeg/FFProbeResult.cs index c7ad1c3ed8..8b2a8687e9 100644 --- a/MediaBrowser.Controller/FFMpeg/FFProbeResult.cs +++ b/MediaBrowser.Controller/FFMpeg/FFProbeResult.cs @@ -33,7 +33,7 @@ namespace MediaBrowser.Controller.FFMpeg public int channels { get; set; } //public int bits_per_sample { get; set; } //public string r_frame_rate { get; set; } - //public string avg_frame_rate { get; set; } + public string avg_frame_rate { get; set; } //public string time_base { get; set; } //public string start_time { get; set; } public string duration { get; set; } diff --git a/MediaBrowser.Controller/Providers/AudioInfoProvider.cs b/MediaBrowser.Controller/Providers/AudioInfoProvider.cs index 64b397a697..72e9d1e4cd 100644 --- a/MediaBrowser.Controller/Providers/AudioInfoProvider.cs +++ b/MediaBrowser.Controller/Providers/AudioInfoProvider.cs @@ -144,37 +144,30 @@ namespace MediaBrowser.Controller.Providers private int? GetDictionaryDiscValue(Dictionary tags) { - string[] keys = tags.Keys.ToArray(); + string disc = GetDictionaryValue(tags, "disc"); - for (int i = 0; i < keys.Length; i++) + if (!string.IsNullOrEmpty(disc)) { - string currentKey = keys[i]; + disc = disc.Split('/')[0]; - if ("disc".Equals(currentKey, StringComparison.OrdinalIgnoreCase)) + int num; + + if (int.TryParse(disc, out num)) { - string disc = tags[currentKey]; - - if (!string.IsNullOrEmpty(disc)) - { - disc = disc.Split('/')[0]; - - int num; - - if (int.TryParse(disc, out num)) - { - return num; - } - } - - break; + return num; } } return null; } - private string GetDictionaryValue(Dictionary tags, string key) + internal static string GetDictionaryValue(Dictionary tags, string key) { + if (tags == null) + { + return null; + } + string[] keys = tags.Keys.ToArray(); for (int i = 0; i < keys.Length; i++) diff --git a/MediaBrowser.Controller/Providers/VideoInfoProvider.cs b/MediaBrowser.Controller/Providers/VideoInfoProvider.cs index 96ca8ed358..f7a6c3b048 100644 --- a/MediaBrowser.Controller/Providers/VideoInfoProvider.cs +++ b/MediaBrowser.Controller/Providers/VideoInfoProvider.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Events; using MediaBrowser.Controller.FFMpeg; using MediaBrowser.Model.Entities; +using System.Collections.Generic; namespace MediaBrowser.Controller.Providers { @@ -61,11 +62,25 @@ namespace MediaBrowser.Controller.Providers video.BitRate = int.Parse(data.format.bit_rate); } - MediaStream videoStream = data.streams.FirstOrDefault(s => s.codec_type.Equals("video", StringComparison.OrdinalIgnoreCase)); + // For now, only read info about first video stream + // Files with multiple video streams are possible, but extremely rare + bool foundVideo = false; - if (videoStream != null) + foreach (MediaStream stream in data.streams) { - FetchFromVideoStream(video, videoStream); + if (stream.codec_type.Equals("video", StringComparison.OrdinalIgnoreCase)) + { + if (!foundVideo) + { + FetchFromVideoStream(video, stream); + } + + foundVideo = true; + } + else if (stream.codec_type.Equals("audio", StringComparison.OrdinalIgnoreCase)) + { + FetchFromAudioStream(video, stream); + } } } @@ -75,6 +90,45 @@ namespace MediaBrowser.Controller.Providers video.Width = stream.width; video.Height = stream.height; video.AspectRatio = stream.display_aspect_ratio; + + if (!string.IsNullOrEmpty(stream.avg_frame_rate)) + { + string[] parts = stream.avg_frame_rate.Split('/'); + + if (parts.Length == 2) + { + video.FrameRate = float.Parse(parts[0]) / float.Parse(parts[1]); + } + else + { + video.FrameRate = float.Parse(parts[0]); + } + } + } + + private void FetchFromAudioStream(Video video, MediaStream stream) + { + AudioStream audio = new AudioStream(); + + audio.Codec = stream.codec_name; + + if (!string.IsNullOrEmpty(stream.bit_rate)) + { + audio.BitRate = int.Parse(stream.bit_rate); + } + + audio.Channels = stream.channels; + + if (!string.IsNullOrEmpty(stream.sample_rate)) + { + audio.SampleRate = int.Parse(stream.sample_rate); + } + + audio.Language = AudioInfoProvider.GetDictionaryValue(stream.tags, "language"); + + List streams = (video.AudioStreams ?? new AudioStream[] { }).ToList(); + streams.Add(audio); + video.AudioStreams = streams; } /// diff --git a/MediaBrowser.Controller/Xml/BaseItemXmlParser.cs b/MediaBrowser.Controller/Xml/BaseItemXmlParser.cs index d39ec60829..50520ac258 100644 --- a/MediaBrowser.Controller/Xml/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Xml/BaseItemXmlParser.cs @@ -321,8 +321,15 @@ namespace MediaBrowser.Controller.Xml break; case "Subtitle": - FetchMediaInfoSubtitles(reader.ReadSubtree(), item); - break; + { + SubtitleStream stream = FetchMediaInfoSubtitles(reader.ReadSubtree()); + + List streams = (item.Subtitles ?? new SubtitleStream[] { }).ToList(); + streams.Add(stream); + item.Subtitles = streams; + + break; + } default: reader.Skip(); @@ -348,10 +355,6 @@ namespace MediaBrowser.Controller.Xml stream.IsDefault = reader.ReadElementContentAsString() == "True"; break; - case "Forced": - stream.IsForced = reader.ReadElementContentAsString() == "True"; - break; - case "BitRate": stream.BitRate = reader.ReadIntSafe(); break; @@ -451,9 +454,9 @@ namespace MediaBrowser.Controller.Xml } } - private void FetchMediaInfoSubtitles(XmlReader reader, Video item) + private SubtitleStream FetchMediaInfoSubtitles(XmlReader reader) { - List list = (item.Subtitles ?? new string[] { }).ToList(); + SubtitleStream stream = new SubtitleStream(); reader.MoveToContent(); @@ -464,15 +467,16 @@ namespace MediaBrowser.Controller.Xml switch (reader.Name) { case "Language": - { - string genre = reader.ReadElementContentAsString(); + stream.Language = reader.ReadElementContentAsString(); + break; - if (!string.IsNullOrWhiteSpace(genre)) - { - list.Add(genre); - } - break; - } + case "Default": + stream.IsDefault = reader.ReadElementContentAsString() == "True"; + break; + + case "Forced": + stream.IsForced = reader.ReadElementContentAsString() == "True"; + break; default: reader.Skip(); @@ -481,7 +485,7 @@ namespace MediaBrowser.Controller.Xml } } - item.Subtitles = list; + return stream; } private void FetchFromTaglinesNode(XmlReader reader, T item) diff --git a/MediaBrowser.Model/DTO/DTOBaseItem.cs b/MediaBrowser.Model/DTO/DTOBaseItem.cs index fb77c25957..53d921d45c 100644 --- a/MediaBrowser.Model/DTO/DTOBaseItem.cs +++ b/MediaBrowser.Model/DTO/DTOBaseItem.cs @@ -91,7 +91,8 @@ namespace MediaBrowser.Model.DTO public ItemSpecialCounts SpecialCounts { get; set; } public AudioInfo AudioInfo { get; set; } - + public VideoInfo VideoInfo { get; set; } + public bool IsType(Type type) { return IsType(type.Name); diff --git a/MediaBrowser.Model/DTO/VideoInfo.cs b/MediaBrowser.Model/DTO/VideoInfo.cs new file mode 100644 index 0000000000..5cff413f29 --- /dev/null +++ b/MediaBrowser.Model/DTO/VideoInfo.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Model.DTO +{ + public class VideoInfo + { + public string Codec { get; set; } + public int Height { get; set; } + public int Width { get; set; } + public string ScanType { get; set; } + + public VideoType VideoType { get; set; } + + public IEnumerable Subtitles { get; set; } + public IEnumerable AudioStreams { get; set; } + } +} diff --git a/MediaBrowser.Model/Entities/Video.cs b/MediaBrowser.Model/Entities/Video.cs index f947509c18..fab22abd37 100644 --- a/MediaBrowser.Model/Entities/Video.cs +++ b/MediaBrowser.Model/Entities/Video.cs @@ -6,7 +6,7 @@ namespace MediaBrowser.Model.Entities { public VideoType VideoType { get; set; } - public IEnumerable Subtitles { get; set; } + public IEnumerable Subtitles { get; set; } public IEnumerable AudioStreams { get; set; } public int Height { get; set; } @@ -25,6 +25,12 @@ namespace MediaBrowser.Model.Entities public int Channels { get; set; } public int SampleRate { get; set; } public bool IsDefault { get; set; } + } + + public class SubtitleStream + { + public string Language { get; set; } + public bool IsDefault { get; set; } public bool IsForced { get; set; } } diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 70e4c729e7..c341dd046b 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -34,6 +34,7 @@ +