use ffprobe -show_chapters command

pull/702/head
Luke Pulverenti 11 years ago
parent 0cfc20ac08
commit ae248b045a

@ -1,5 +1,4 @@
using MediaBrowser.Model.Entities; using System.Collections.Generic;
using System.Collections.Generic;
namespace MediaBrowser.Controller.MediaEncoding namespace MediaBrowser.Controller.MediaEncoding
{ {
@ -24,7 +23,18 @@ namespace MediaBrowser.Controller.MediaEncoding
/// Gets or sets the chapters. /// Gets or sets the chapters.
/// </summary> /// </summary>
/// <value>The chapters.</value> /// <value>The chapters.</value>
public List<ChapterInfo> Chapters { get; set; } public MediaChapter[] Chapters { get; set; }
}
public class MediaChapter
{
public int id { get; set; }
public string time_base { get; set; }
public long start { get; set; }
public string start_time { get; set; }
public long end { get; set; }
public string end_time { get; set; }
public Dictionary<string, string> tags { get; set; }
} }
/// <summary> /// <summary>

@ -50,12 +50,16 @@ namespace MediaBrowser.Providers.MediaInfo
return ItemUpdateType.MetadataImport; return ItemUpdateType.MetadataImport;
} }
private const string SchemaVersion = "1";
private async Task<InternalMediaInfoResult> GetMediaInfo(BaseItem item, CancellationToken cancellationToken) private async Task<InternalMediaInfoResult> GetMediaInfo(BaseItem item, CancellationToken cancellationToken)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
var idString = item.Id.ToString("N"); var idString = item.Id.ToString("N");
var cachePath = Path.Combine(_appPaths.CachePath, "ffprobe-audio", idString.Substring(0, 2), idString, "v" + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json"); var cachePath = Path.Combine(_appPaths.CachePath,
"ffprobe-audio",
idString.Substring(0, 2), idString, "v" + SchemaVersion + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json");
try try
{ {

@ -107,6 +107,8 @@ namespace MediaBrowser.Providers.MediaInfo
return ItemUpdateType.MetadataImport; return ItemUpdateType.MetadataImport;
} }
private const string SchemaVersion = "1";
private async Task<InternalMediaInfoResult> GetMediaInfo(BaseItem item, IIsoMount isoMount, CancellationToken cancellationToken) private async Task<InternalMediaInfoResult> GetMediaInfo(BaseItem item, IIsoMount isoMount, CancellationToken cancellationToken)
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
@ -114,7 +116,9 @@ namespace MediaBrowser.Providers.MediaInfo
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
var idString = item.Id.ToString("N"); var idString = item.Id.ToString("N");
var cachePath = Path.Combine(_appPaths.CachePath, "ffprobe-video", idString.Substring(0, 2), idString, "v" + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json"); var cachePath = Path.Combine(_appPaths.CachePath,
"ffprobe-video",
idString.Substring(0, 2), idString, "v" + SchemaVersion + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json");
try try
{ {
@ -161,7 +165,8 @@ namespace MediaBrowser.Providers.MediaInfo
var mediaStreams = MediaEncoderHelpers.GetMediaInfo(data).MediaStreams; var mediaStreams = MediaEncoderHelpers.GetMediaInfo(data).MediaStreams;
var chapters = data.Chapters ?? new List<ChapterInfo>(); var mediaChapters = (data.Chapters ?? new MediaChapter[] { }).ToList();
var chapters = mediaChapters.Select(GetChapterInfo).ToList();
if (video.VideoType == VideoType.BluRay || (video.IsoType.HasValue && video.IsoType.Value == IsoType.BluRay)) if (video.VideoType == VideoType.BluRay || (video.IsoType.HasValue && video.IsoType.Value == IsoType.BluRay))
{ {
@ -200,6 +205,24 @@ namespace MediaBrowser.Providers.MediaInfo
await _itemRepo.SaveChapters(video.Id, chapters, cancellationToken).ConfigureAwait(false); await _itemRepo.SaveChapters(video.Id, chapters, cancellationToken).ConfigureAwait(false);
} }
private ChapterInfo GetChapterInfo(MediaChapter chapter)
{
var info = new ChapterInfo();
if (chapter.tags != null)
{
string name;
if (chapter.tags.TryGetValue("title", out name))
{
info.Name = name;
}
}
info.StartPositionTicks = chapter.start/100;
return info;
}
private void FetchBdInfo(BaseItem item, List<ChapterInfo> chapters, List<MediaStream> mediaStreams, BlurayDiscInfo blurayInfo) private void FetchBdInfo(BaseItem item, List<ChapterInfo> chapters, List<MediaStream> mediaStreams, BlurayDiscInfo blurayInfo)
{ {
var video = (Video)item; var video = (Video)item;

@ -175,6 +175,10 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
string probeSizeArgument, string probeSizeArgument,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var args = extractChapters
? "{0} -i {1} -threads 0 -v info -print_format json -show_streams -show_chapters -show_format"
: "{0} -i {1} -threads 0 -v info -print_format json -show_streams -show_format";
var process = new Process var process = new Process
{ {
StartInfo = new ProcessStartInfo StartInfo = new ProcessStartInfo
@ -186,8 +190,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
RedirectStandardOutput = true, RedirectStandardOutput = true,
RedirectStandardError = true, RedirectStandardError = true,
FileName = FFProbePath, FileName = FFProbePath,
Arguments = string.Format( Arguments = string.Format(args,
"{0} -i {1} -threads 0 -v info -print_format json -show_streams -show_format",
probeSizeArgument, inputPath).Trim(), probeSizeArgument, inputPath).Trim(),
WindowStyle = ProcessWindowStyle.Hidden, WindowStyle = ProcessWindowStyle.Hidden,
@ -204,7 +207,6 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
await _ffProbeResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); await _ffProbeResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
InternalMediaInfoResult result; InternalMediaInfoResult result;
string standardError = null;
try try
{ {
@ -221,24 +223,9 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
try try
{ {
Task<string> standardErrorReadTask = null; process.BeginErrorReadLine();
// MUST read both stdout and stderr asynchronously or a deadlock may occurr
if (extractChapters)
{
standardErrorReadTask = process.StandardError.ReadToEndAsync();
}
else
{
process.BeginErrorReadLine();
}
result = _jsonSerializer.DeserializeFromStream<InternalMediaInfoResult>(process.StandardOutput.BaseStream); result = _jsonSerializer.DeserializeFromStream<InternalMediaInfoResult>(process.StandardOutput.BaseStream);
if (extractChapters)
{
standardError = await standardErrorReadTask.ConfigureAwait(false);
}
} }
catch catch
{ {
@ -282,11 +269,6 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
} }
} }
if (extractChapters && !string.IsNullOrEmpty(standardError))
{
AddChapters(result, standardError);
}
return result; return result;
} }
@ -295,66 +277,6 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
/// </summary> /// </summary>
protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
/// <summary>
/// Adds the chapters.
/// </summary>
/// <param name="result">The result.</param>
/// <param name="standardError">The standard error.</param>
private void AddChapters(InternalMediaInfoResult result, string standardError)
{
var lines = standardError.Split('\n').Select(l => l.TrimStart());
var chapters = new List<ChapterInfo>();
ChapterInfo lastChapter = null;
foreach (var line in lines)
{
if (line.StartsWith("Chapter", StringComparison.OrdinalIgnoreCase))
{
// Example:
// Chapter #0.2: start 400.534, end 4565.435
const string srch = "start ";
var start = line.IndexOf(srch, StringComparison.OrdinalIgnoreCase);
if (start == -1)
{
continue;
}
var subString = line.Substring(start + srch.Length);
subString = subString.Substring(0, subString.IndexOf(','));
double seconds;
if (double.TryParse(subString, NumberStyles.Any, UsCulture, out seconds))
{
lastChapter = new ChapterInfo
{
StartPositionTicks = TimeSpan.FromSeconds(seconds).Ticks
};
chapters.Add(lastChapter);
}
}
else if (line.StartsWith("title", StringComparison.OrdinalIgnoreCase))
{
if (lastChapter != null && string.IsNullOrEmpty(lastChapter.Name))
{
var index = line.IndexOf(':');
if (index != -1)
{
lastChapter.Name = line.Substring(index + 1).Trim().TrimEnd('\r');
}
}
}
}
result.Chapters = chapters;
}
/// <summary> /// <summary>
/// Processes the exited. /// Processes the exited.
/// </summary> /// </summary>

Loading…
Cancel
Save