support more embedded video metadata
This commit is contained in:
parent
bfa1b30cab
commit
76eb1c46e3
|
@ -129,7 +129,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||
/// <param name="request">The request.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task.</returns>
|
||||
public Task<Model.MediaInfo.MediaInfo> GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken)
|
||||
public Task<MediaInfo> GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
var extractChapters = request.MediaType == DlnaProfileType.Video && request.ExtractChapters;
|
||||
|
||||
|
@ -175,7 +175,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task{MediaInfoResult}.</returns>
|
||||
/// <exception cref="System.ApplicationException">ffprobe failed - streams and format are both null.</exception>
|
||||
private async Task<Model.MediaInfo.MediaInfo> GetMediaInfoInternal(string inputPath,
|
||||
private async Task<MediaInfo> GetMediaInfoInternal(string inputPath,
|
||||
string primaryPath,
|
||||
MediaProtocol protocol,
|
||||
bool extractChapters,
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||
|
||||
public MediaInfo GetMediaInfo(InternalMediaInfoResult data, VideoType videoType, bool isAudio, string path, MediaProtocol protocol)
|
||||
{
|
||||
var info = new Model.MediaInfo.MediaInfo
|
||||
var info = new MediaInfo
|
||||
{
|
||||
Path = path,
|
||||
Protocol = protocol
|
||||
|
@ -56,40 +56,81 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||
}
|
||||
}
|
||||
|
||||
if (isAudio)
|
||||
var tags = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
var tagStreamType = isAudio ? "audio" : "video";
|
||||
|
||||
if (data.streams != null)
|
||||
{
|
||||
SetAudioRuntimeTicks(data, info);
|
||||
var tagStream = data.streams.FirstOrDefault(i => string.Equals(i.codec_type, tagStreamType, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
var tags = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// tags are normally located under data.format, but we've seen some cases with ogg where they're part of the audio stream
|
||||
// so let's create a combined list of both
|
||||
|
||||
if (data.streams != null)
|
||||
if (tagStream != null && tagStream.tags != null)
|
||||
{
|
||||
var audioStream = data.streams.FirstOrDefault(i => string.Equals(i.codec_type, "audio", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (audioStream != null && audioStream.tags != null)
|
||||
{
|
||||
foreach (var pair in audioStream.tags)
|
||||
{
|
||||
tags[pair.Key] = pair.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data.format != null && data.format.tags != null)
|
||||
{
|
||||
foreach (var pair in data.format.tags)
|
||||
foreach (var pair in tagStream.tags)
|
||||
{
|
||||
tags[pair.Key] = pair.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data.format != null && data.format.tags != null)
|
||||
{
|
||||
foreach (var pair in data.format.tags)
|
||||
{
|
||||
tags[pair.Key] = pair.Value;
|
||||
}
|
||||
}
|
||||
|
||||
FetchGenres(info, tags);
|
||||
var overview = FFProbeHelpers.GetDictionaryValue(tags, "description");
|
||||
if (!string.IsNullOrWhiteSpace(overview))
|
||||
{
|
||||
info.Overview = overview;
|
||||
}
|
||||
|
||||
var title = FFProbeHelpers.GetDictionaryValue(tags, "title");
|
||||
if (!string.IsNullOrWhiteSpace(title))
|
||||
{
|
||||
info.Name = title;
|
||||
}
|
||||
|
||||
info.ProductionYear = FFProbeHelpers.GetDictionaryNumericValue(tags, "date");
|
||||
|
||||
// Several different forms of retaildate
|
||||
info.PremiereDate = FFProbeHelpers.GetDictionaryDateTime(tags, "retaildate") ??
|
||||
FFProbeHelpers.GetDictionaryDateTime(tags, "retail date") ??
|
||||
FFProbeHelpers.GetDictionaryDateTime(tags, "retail_date") ??
|
||||
FFProbeHelpers.GetDictionaryDateTime(tags, "date");
|
||||
|
||||
if (isAudio)
|
||||
{
|
||||
SetAudioRuntimeTicks(data, info);
|
||||
|
||||
// tags are normally located under data.format, but we've seen some cases with ogg where they're part of the audio stream
|
||||
// so let's create a combined list of both
|
||||
|
||||
SetAudioInfoFromTags(info, tags);
|
||||
}
|
||||
else
|
||||
{
|
||||
var iTunEXTC = FFProbeHelpers.GetDictionaryValue(tags, "iTunEXTC");
|
||||
if (!string.IsNullOrWhiteSpace(iTunEXTC))
|
||||
{
|
||||
var parts = iTunEXTC.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
// Example
|
||||
// mpaa|G|100|For crude humor
|
||||
if (parts.Length == 4)
|
||||
{
|
||||
info.OfficialRating = parts[1];
|
||||
info.OfficialRatingDescription = parts[3];
|
||||
}
|
||||
}
|
||||
|
||||
var itunesXml = FFProbeHelpers.GetDictionaryValue(tags, "iTunMOVI");
|
||||
if (!string.IsNullOrWhiteSpace(itunesXml))
|
||||
{
|
||||
FetchFromItunesInfo(itunesXml, info);
|
||||
}
|
||||
|
||||
if (data.format != null && !string.IsNullOrEmpty(data.format.duration))
|
||||
{
|
||||
info.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(data.format.duration, _usCulture)).Ticks;
|
||||
|
@ -108,6 +149,11 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||
return info;
|
||||
}
|
||||
|
||||
private void FetchFromItunesInfo(string xml, MediaInfo info)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts ffprobe stream info to our MediaStream class
|
||||
/// </summary>
|
||||
|
@ -430,16 +476,8 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||
}
|
||||
}
|
||||
|
||||
private void SetAudioInfoFromTags(Model.MediaInfo.MediaInfo audio, Dictionary<string, string> tags)
|
||||
private void SetAudioInfoFromTags(MediaInfo audio, Dictionary<string, string> tags)
|
||||
{
|
||||
var title = FFProbeHelpers.GetDictionaryValue(tags, "title");
|
||||
|
||||
// Only set Name if title was found in the dictionary
|
||||
if (!string.IsNullOrEmpty(title))
|
||||
{
|
||||
audio.Title = title;
|
||||
}
|
||||
|
||||
var composer = FFProbeHelpers.GetDictionaryValue(tags, "composer");
|
||||
if (!string.IsNullOrWhiteSpace(composer))
|
||||
{
|
||||
|
@ -511,22 +549,12 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||
// Disc number
|
||||
audio.ParentIndexNumber = GetDictionaryDiscValue(tags, "disc");
|
||||
|
||||
audio.ProductionYear = FFProbeHelpers.GetDictionaryNumericValue(tags, "date");
|
||||
|
||||
// Several different forms of retaildate
|
||||
audio.PremiereDate = FFProbeHelpers.GetDictionaryDateTime(tags, "retaildate") ??
|
||||
FFProbeHelpers.GetDictionaryDateTime(tags, "retail date") ??
|
||||
FFProbeHelpers.GetDictionaryDateTime(tags, "retail_date") ??
|
||||
FFProbeHelpers.GetDictionaryDateTime(tags, "date");
|
||||
|
||||
// If we don't have a ProductionYear try and get it from PremiereDate
|
||||
if (audio.PremiereDate.HasValue && !audio.ProductionYear.HasValue)
|
||||
{
|
||||
audio.ProductionYear = audio.PremiereDate.Value.ToLocalTime().Year;
|
||||
}
|
||||
|
||||
FetchGenres(audio, tags);
|
||||
|
||||
// There's several values in tags may or may not be present
|
||||
FetchStudios(audio, tags, "organization");
|
||||
FetchStudios(audio, tags, "ensemble");
|
||||
|
@ -693,7 +721,7 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||
/// </summary>
|
||||
/// <param name="info">The information.</param>
|
||||
/// <param name="tags">The tags.</param>
|
||||
private void FetchGenres(Model.MediaInfo.MediaInfo info, Dictionary<string, string> tags)
|
||||
private void FetchGenres(MediaInfo info, Dictionary<string, string> tags)
|
||||
{
|
||||
var val = FFProbeHelpers.GetDictionaryValue(tags, "genre");
|
||||
|
||||
|
@ -764,7 +792,7 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||
|
||||
private const int MaxSubtitleDescriptionExtractionLength = 100; // When extracting subtitles, the maximum length to consider (to avoid invalid filenames)
|
||||
|
||||
private void FetchWtvInfo(Model.MediaInfo.MediaInfo video, InternalMediaInfoResult data)
|
||||
private void FetchWtvInfo(MediaInfo video, InternalMediaInfoResult data)
|
||||
{
|
||||
if (data.format == null || data.format.tags == null)
|
||||
{
|
||||
|
@ -775,15 +803,16 @@ namespace MediaBrowser.MediaEncoding.Probing
|
|||
|
||||
if (!string.IsNullOrWhiteSpace(genres))
|
||||
{
|
||||
//genres = FFProbeHelpers.GetDictionaryValue(data.format.tags, "genre");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(genres))
|
||||
{
|
||||
video.Genres = genres.Split(new[] { ';', '/', ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
var genreList = genres.Split(new[] { ';', '/', ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Where(i => !string.IsNullOrWhiteSpace(i))
|
||||
.Select(i => i.Trim())
|
||||
.ToList();
|
||||
|
||||
// If this is empty then don't overwrite genres that might have been fetched earlier
|
||||
if (genreList.Count > 0)
|
||||
{
|
||||
video.Genres = genreList;
|
||||
}
|
||||
}
|
||||
|
||||
var officialRating = FFProbeHelpers.GetDictionaryValue(data.format.tags, "WM/ParentalRating");
|
||||
|
|
|
@ -9,11 +9,6 @@ namespace MediaBrowser.Model.MediaInfo
|
|||
{
|
||||
public List<ChapterInfo> Chapters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title.
|
||||
/// </summary>
|
||||
/// <value>The title.</value>
|
||||
public string Title { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the album.
|
||||
/// </summary>
|
||||
|
@ -47,6 +42,11 @@ namespace MediaBrowser.Model.MediaInfo
|
|||
/// <value>The official rating.</value>
|
||||
public string OfficialRating { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the official rating description.
|
||||
/// </summary>
|
||||
/// <value>The official rating description.</value>
|
||||
public string OfficialRatingDescription { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the overview.
|
||||
/// </summary>
|
||||
/// <value>The overview.</value>
|
||||
|
|
|
@ -125,9 +125,9 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||
private async Task FetchDataFromTags(Audio audio, Model.MediaInfo.MediaInfo data)
|
||||
{
|
||||
// Only set Name if title was found in the dictionary
|
||||
if (!string.IsNullOrEmpty(data.Title))
|
||||
if (!string.IsNullOrEmpty(data.Name))
|
||||
{
|
||||
audio.Name = data.Title;
|
||||
audio.Name = data.Name;
|
||||
}
|
||||
|
||||
if (!audio.LockedFields.Contains(MetadataFields.Cast))
|
||||
|
|
|
@ -383,6 +383,11 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(data.OfficialRatingDescription) || isFullRefresh)
|
||||
{
|
||||
video.OfficialRatingDescription = data.OfficialRatingDescription;
|
||||
}
|
||||
|
||||
if (!video.LockedFields.Contains(MetadataFields.Genres))
|
||||
{
|
||||
if (video.Genres.Count == 0 || isFullRefresh)
|
||||
|
@ -437,6 +442,13 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||
video.ParentIndexNumber = data.ParentIndexNumber;
|
||||
}
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(data.Name))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(video.Name) || string.Equals(video.Name, Path.GetFileNameWithoutExtension(video.Path), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
video.Name = data.Name;
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't have a ProductionYear try and get it from PremiereDate
|
||||
if (video.PremiereDate.HasValue && !video.ProductionYear.HasValue)
|
||||
|
|
|
@ -140,6 +140,9 @@
|
|||
<Content Include="dashboard-ui\components\remotecontrolautoplay.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\devices\windowsphone\wp.css">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="dashboard-ui\legacy\buttonenabled.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
@ -254,9 +257,6 @@
|
|||
<Content Include="dashboard-ui\favorites.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Include="dashboard-ui\legacy\deferred.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<Content Include="dashboard-ui\livetvguideprovider.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
|
Loading…
Reference in New Issue
Block a user