store chapters in xml

pull/702/head
Luke Pulverenti 12 years ago
parent d2ec5126f4
commit d3acd04e66

@ -1,5 +1,6 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
@ -9,6 +10,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
namespace MediaBrowser.Controller.Providers
@ -266,7 +268,10 @@ namespace MediaBrowser.Controller.Providers
case "TagLines":
{
FetchFromTaglinesNode(reader.ReadSubtree(), item);
using (var subtree = reader.ReadSubtree())
{
FetchFromTaglinesNode(subtree, item);
}
break;
}
@ -591,28 +596,58 @@ namespace MediaBrowser.Controller.Providers
break;
case "Genres":
FetchFromGenresNode(reader.ReadSubtree(), item);
{
using (var subtree = reader.ReadSubtree())
{
FetchFromGenresNode(subtree, item);
}
break;
}
case "Tags":
FetchFromTagsNode(reader.ReadSubtree(), item);
{
using (var subtree = reader.ReadSubtree())
{
FetchFromTagsNode(subtree, item);
}
break;
}
case "Persons":
FetchDataFromPersonsNode(reader.ReadSubtree(), item);
{
using (var subtree = reader.ReadSubtree())
{
FetchDataFromPersonsNode(subtree, item);
}
break;
}
case "ParentalRating":
FetchFromParentalRatingNode(reader.ReadSubtree(), item);
{
using (var subtree = reader.ReadSubtree())
{
FetchFromParentalRatingNode(subtree, item);
}
break;
}
case "Studios":
FetchFromStudiosNode(reader.ReadSubtree(), item);
{
using (var subtree = reader.ReadSubtree())
{
FetchFromStudiosNode(subtree, item);
}
break;
}
case "MediaInfo":
FetchFromMediaInfoNode(reader.ReadSubtree(), item);
{
using (var subtree = reader.ReadSubtree())
{
FetchFromMediaInfoNode(subtree, item);
}
break;
}
default:
reader.Skip();
@ -636,8 +671,13 @@ namespace MediaBrowser.Controller.Providers
switch (reader.Name)
{
case "Video":
FetchFromMediaInfoVideoNode(reader.ReadSubtree(), item);
{
using (var subtree = reader.ReadSubtree())
{
FetchFromMediaInfoVideoNode(subtree, item);
}
break;
}
default:
reader.Skip();
@ -813,10 +853,13 @@ namespace MediaBrowser.Controller.Providers
case "Person":
case "Actor":
{
foreach (var person in GetPersonsFromXmlNode(reader.ReadSubtree()))
using (var subtree = reader.ReadSubtree())
{
foreach (var person in GetPersonsFromXmlNode(subtree))
{
item.AddPerson(person);
}
}
break;
}
@ -828,6 +871,86 @@ namespace MediaBrowser.Controller.Providers
}
}
protected async Task FetchChaptersFromXmlNode(Guid itemId, XmlReader reader, IItemRepository repository, CancellationToken cancellationToken)
{
using (reader)
{
var chapters = GetChaptersFromXmlNode(reader);
await repository.SaveChapters(itemId, chapters, cancellationToken).ConfigureAwait(false);
}
}
private IEnumerable<ChapterInfo> GetChaptersFromXmlNode(XmlReader reader)
{
var chapters = new List<ChapterInfo>();
reader.MoveToContent();
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "Chapter":
{
using (var subtree = reader.ReadSubtree())
{
chapters.Add(GetChapterInfoFromXmlNode(subtree));
}
break;
}
default:
reader.Skip();
break;
}
}
}
return chapters;
}
private ChapterInfo GetChapterInfoFromXmlNode(XmlReader reader)
{
var chapter = new ChapterInfo();
reader.MoveToContent();
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "StartPositionMs":
{
var val = reader.ReadElementContentAsString();
var ms = long.Parse(val, _usCulture);
chapter.StartPositionTicks = TimeSpan.FromMilliseconds(ms).Ticks;
break;
}
case "Name":
{
chapter.Name = reader.ReadElementContentAsString();
break;
}
default:
reader.Skip();
break;
}
}
}
return chapter;
}
/// <summary>
/// Fetches from studios node.
/// </summary>

@ -58,6 +58,7 @@
<Compile Include="MediaInfo\FFProbeAudioInfoProvider.cs" />
<Compile Include="MediaInfo\FFProbeVideoInfoProvider.cs" />
<Compile Include="Movies\BoxSetProviderFromXml.cs" />
<Compile Include="Movies\MovieXmlParser.cs" />
<Compile Include="Movies\FanArtMovieProvider.cs" />
<Compile Include="Movies\FanArtMovieUpdatesPrescanTask.cs" />
<Compile Include="Movies\MovieDbImagesProvider.cs" />

@ -1,9 +1,9 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Music;
using System;
using System.IO;
using System.Threading;
@ -17,10 +17,12 @@ namespace MediaBrowser.Providers.Movies
public class MovieProviderFromXml : BaseMetadataProvider
{
internal static MovieProviderFromXml Current { get; private set; }
private readonly IItemRepository _itemRepo;
public MovieProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager)
public MovieProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IItemRepository itemRepo)
: base(logManager, configurationManager)
{
_itemRepo = itemRepo;
Current = this;
}
@ -94,30 +96,9 @@ namespace MediaBrowser.Providers.Movies
try
{
var movie = item as Movie;
var video = (Video) item;
if (movie != null)
{
new BaseItemXmlParser<Movie>(Logger).Fetch(movie, path, cancellationToken);
}
else
{
var musicVideo = item as MusicVideo;
if (musicVideo != null)
{
new MusicVideoXmlParser(Logger).Fetch(musicVideo, path, cancellationToken);
}
else
{
var trailer = item as Trailer;
if (trailer != null)
{
new BaseItemXmlParser<Trailer>(Logger).Fetch(trailer, path, cancellationToken);
}
}
}
await new MovieXmlParser(Logger, _itemRepo).FetchAsync(video, path, cancellationToken).ConfigureAwait(false);
}
finally
{

@ -0,0 +1,60 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
namespace MediaBrowser.Providers.Movies
{
/// <summary>
/// Class EpisodeXmlParser
/// </summary>
public class MovieXmlParser : BaseItemXmlParser<Video>
{
private readonly IItemRepository _itemRepo;
private Task _chaptersTask = null;
public MovieXmlParser(ILogger logger, IItemRepository itemRepo)
: base(logger)
{
_itemRepo = itemRepo;
}
public async Task FetchAsync(Video item, string metadataFile, CancellationToken cancellationToken)
{
_chaptersTask = null;
Fetch(item, metadataFile, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
if (_chaptersTask != null)
{
await _chaptersTask.ConfigureAwait(false);
}
}
/// <summary>
/// Fetches the data from XML node.
/// </summary>
/// <param name="reader">The reader.</param>
/// <param name="item">The item.</param>
protected override void FetchDataFromXmlNode(XmlReader reader, Video item)
{
switch (reader.Name)
{
case "Chapters":
_chaptersTask = FetchChaptersFromXmlNode(item.Id, reader.ReadSubtree(), _itemRepo, CancellationToken.None);
break;
default:
base.FetchDataFromXmlNode(reader, item);
break;
}
}
}
}

@ -2,6 +2,7 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Providers.TV;
using System;
using System.Globalization;
@ -15,6 +16,7 @@ namespace MediaBrowser.Providers.Savers
public class EpisodeXmlSaver : IMetadataSaver
{
private readonly IServerConfigurationManager _config;
private readonly IItemRepository _itemRepository;
/// <summary>
/// Determines whether [is enabled for] [the specified item].
@ -38,9 +40,10 @@ namespace MediaBrowser.Providers.Savers
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
public EpisodeXmlSaver(IServerConfigurationManager config)
public EpisodeXmlSaver(IServerConfigurationManager config, IItemRepository itemRepository)
{
_config = config;
_itemRepository = itemRepository;
}
/// <summary>
@ -79,6 +82,7 @@ namespace MediaBrowser.Providers.Savers
XmlSaverHelpers.AddCommonNodes(item, builder);
XmlSaverHelpers.AddMediaInfo(episode, builder);
XmlSaverHelpers.AddChapters((Video)item, builder, _itemRepository);
builder.Append("</Item>");

@ -2,6 +2,7 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Providers.Movies;
using System;
using System.Globalization;
@ -18,10 +19,12 @@ namespace MediaBrowser.Providers.Savers
public class MovieXmlSaver : IMetadataSaver
{
private readonly IServerConfigurationManager _config;
private readonly IItemRepository _itemRepository;
public MovieXmlSaver(IServerConfigurationManager config)
public MovieXmlSaver(IServerConfigurationManager config, IItemRepository itemRepository)
{
_config = config;
_itemRepository = itemRepository;
}
/// <summary>
@ -94,6 +97,8 @@ namespace MediaBrowser.Providers.Savers
XmlSaverHelpers.AddMediaInfo((Video)item, builder);
XmlSaverHelpers.AddChapters((Video)item, builder, _itemRepository);
builder.Append("</Title>");
var xmlFilePath = GetSavePath(item);

@ -1,5 +1,6 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
@ -79,7 +80,8 @@ namespace MediaBrowser.Providers.Savers
"GamesDbId",
"BirthDate",
"DeathDate",
"LockedFields"
"LockedFields",
"Chapters"
});
var position = xml.ToString().LastIndexOf("</", StringComparison.OrdinalIgnoreCase);
@ -411,7 +413,27 @@ namespace MediaBrowser.Providers.Savers
builder.Append("</Persons>");
}
}
public static void AddChapters(Video item, StringBuilder builder, IItemRepository repository)
{
var chapters = repository.GetChapters(item.Id);
builder.Append("<Chapters>");
foreach (var chapter in chapters)
{
builder.Append("<Chapter>");
builder.Append("<Name>" + SecurityElement.Escape(chapter.Name) + "</Name>");
var time = TimeSpan.FromTicks(chapter.StartPositionTicks);
var ms = Convert.ToInt64(time.TotalMilliseconds);
builder.Append("<StartPositionMs>" + SecurityElement.Escape(ms.ToString(UsCulture)) + "</StartPositionMs>");
builder.Append("</Chapter>");
}
builder.Append("</Chapters>");
}
/// <summary>

@ -1,13 +1,14 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Logging;
namespace MediaBrowser.Providers.TV
{
@ -17,10 +18,12 @@ namespace MediaBrowser.Providers.TV
public class EpisodeProviderFromXml : BaseMetadataProvider
{
internal static EpisodeProviderFromXml Current { get; private set; }
private readonly IItemRepository _itemRepo;
public EpisodeProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager)
public EpisodeProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IItemRepository itemRepo)
: base(logManager, configurationManager)
{
_itemRepo = itemRepo;
Current = this;
}
@ -98,7 +101,7 @@ namespace MediaBrowser.Providers.TV
try
{
new EpisodeXmlParser(Logger).Fetch((Episode)item, metadataFile, cancellationToken);
await new EpisodeXmlParser(Logger, _itemRepo).FetchAsync((Episode)item, metadataFile, cancellationToken).ConfigureAwait(false);
}
finally
{

@ -1,7 +1,10 @@
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
namespace MediaBrowser.Providers.TV
@ -11,13 +14,28 @@ namespace MediaBrowser.Providers.TV
/// </summary>
public class EpisodeXmlParser : BaseItemXmlParser<Episode>
{
/// <summary>
/// Initializes a new instance of the <see cref="EpisodeXmlParser" /> class.
/// </summary>
/// <param name="logger">The logger.</param>
public EpisodeXmlParser(ILogger logger)
private readonly IItemRepository _itemRepo;
private Task _chaptersTask = null;
public EpisodeXmlParser(ILogger logger, IItemRepository itemRepo)
: base(logger)
{
_itemRepo = itemRepo;
}
public async Task FetchAsync(Episode item, string metadataFile, CancellationToken cancellationToken)
{
_chaptersTask = null;
Fetch(item, metadataFile, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
if (_chaptersTask != null)
{
await _chaptersTask.ConfigureAwait(false);
}
}
/// <summary>
@ -29,7 +47,13 @@ namespace MediaBrowser.Providers.TV
{
switch (reader.Name)
{
case "Chapters":
_chaptersTask = FetchChaptersFromXmlNode(item.Id, reader.ReadSubtree(), _itemRepo, CancellationToken.None);
break;
case "Episode":
//MB generated metadata is within an "Episode" node
using (var subTree = reader.ReadSubtree())
{

@ -2,16 +2,12 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -25,12 +21,6 @@ namespace MediaBrowser.Server.Implementations.Providers
/// </summary>
public class ProviderManager : IProviderManager
{
/// <summary>
/// The currently running metadata providers
/// </summary>
private readonly ConcurrentDictionary<string, Tuple<BaseMetadataProvider, BaseItem, CancellationTokenSource>> _currentlyRunningProviders =
new ConcurrentDictionary<string, Tuple<BaseMetadataProvider, BaseItem, CancellationTokenSource>>();
/// <summary>
/// The _logger
/// </summary>
@ -72,19 +62,6 @@ namespace MediaBrowser.Server.Implementations.Providers
_httpClient = httpClient;
ConfigurationManager = configurationManager;
_directoryWatchers = directoryWatchers;
configurationManager.ConfigurationUpdated += configurationManager_ConfigurationUpdated;
}
/// <summary>
/// Handles the ConfigurationUpdated event of the configurationManager control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
void configurationManager_ConfigurationUpdated(object sender, EventArgs e)
{
// Validate currently executing providers, in the background
Task.Run(() => ValidateCurrentlyRunningProviders());
}
/// <summary>
@ -217,8 +194,6 @@ namespace MediaBrowser.Server.Implementations.Providers
// This provides the ability to cancel just this one provider
var innerCancellationTokenSource = new CancellationTokenSource();
OnProviderRefreshBeginning(provider, item, innerCancellationTokenSource);
try
{
var changed = await provider.FetchAsync(item, force, CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, innerCancellationTokenSource.Token).Token).ConfigureAwait(false);
@ -253,70 +228,6 @@ namespace MediaBrowser.Server.Implementations.Providers
finally
{
innerCancellationTokenSource.Dispose();
OnProviderRefreshCompleted(provider, item);
}
}
/// <summary>
/// Notifies the kernal that a provider has begun refreshing
/// </summary>
/// <param name="provider">The provider.</param>
/// <param name="item">The item.</param>
/// <param name="cancellationTokenSource">The cancellation token source.</param>
public void OnProviderRefreshBeginning(BaseMetadataProvider provider, BaseItem item, CancellationTokenSource cancellationTokenSource)
{
var key = item.Id + provider.GetType().Name;
Tuple<BaseMetadataProvider, BaseItem, CancellationTokenSource> current;
if (_currentlyRunningProviders.TryGetValue(key, out current))
{
try
{
current.Item3.Cancel();
}
catch (ObjectDisposedException)
{
}
}
var tuple = new Tuple<BaseMetadataProvider, BaseItem, CancellationTokenSource>(provider, item, cancellationTokenSource);
_currentlyRunningProviders.AddOrUpdate(key, tuple, (k, v) => tuple);
}
/// <summary>
/// Notifies the kernal that a provider has completed refreshing
/// </summary>
/// <param name="provider">The provider.</param>
/// <param name="item">The item.</param>
public void OnProviderRefreshCompleted(BaseMetadataProvider provider, BaseItem item)
{
var key = item.Id + provider.GetType().Name;
Tuple<BaseMetadataProvider, BaseItem, CancellationTokenSource> current;
if (_currentlyRunningProviders.TryRemove(key, out current))
{
current.Item3.Dispose();
}
}
/// <summary>
/// Validates the currently running providers and cancels any that should not be run due to configuration changes
/// </summary>
private void ValidateCurrentlyRunningProviders()
{
var enableInternetProviders = ConfigurationManager.Configuration.EnableInternetProviders;
var internetProviderExcludeTypes = ConfigurationManager.Configuration.InternetProviderExcludeTypes;
foreach (var tuple in _currentlyRunningProviders.Values
.Where(p => p.Item1.RequiresInternet && (!enableInternetProviders || internetProviderExcludeTypes.Contains(p.Item2.GetType().Name, StringComparer.OrdinalIgnoreCase)))
.ToList())
{
tuple.Item3.Cancel();
}
}

Loading…
Cancel
Save