mirror of https://github.com/jellyfin/jellyfin.git
store chapters in xml
This commit is contained in:
parent
d2ec5126f4
commit
d3acd04e66
|
@ -1,5 +1,6 @@
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
using System;
|
using System;
|
||||||
|
@ -9,6 +10,7 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Providers
|
namespace MediaBrowser.Controller.Providers
|
||||||
|
@ -266,7 +268,10 @@ namespace MediaBrowser.Controller.Providers
|
||||||
|
|
||||||
case "TagLines":
|
case "TagLines":
|
||||||
{
|
{
|
||||||
FetchFromTaglinesNode(reader.ReadSubtree(), item);
|
using (var subtree = reader.ReadSubtree())
|
||||||
|
{
|
||||||
|
FetchFromTaglinesNode(subtree, item);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -591,28 +596,58 @@ namespace MediaBrowser.Controller.Providers
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "Genres":
|
case "Genres":
|
||||||
FetchFromGenresNode(reader.ReadSubtree(), item);
|
{
|
||||||
break;
|
using (var subtree = reader.ReadSubtree())
|
||||||
|
{
|
||||||
|
FetchFromGenresNode(subtree, item);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "Tags":
|
case "Tags":
|
||||||
FetchFromTagsNode(reader.ReadSubtree(), item);
|
{
|
||||||
break;
|
using (var subtree = reader.ReadSubtree())
|
||||||
|
{
|
||||||
|
FetchFromTagsNode(subtree, item);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "Persons":
|
case "Persons":
|
||||||
FetchDataFromPersonsNode(reader.ReadSubtree(), item);
|
{
|
||||||
break;
|
using (var subtree = reader.ReadSubtree())
|
||||||
|
{
|
||||||
|
FetchDataFromPersonsNode(subtree, item);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "ParentalRating":
|
case "ParentalRating":
|
||||||
FetchFromParentalRatingNode(reader.ReadSubtree(), item);
|
{
|
||||||
break;
|
using (var subtree = reader.ReadSubtree())
|
||||||
|
{
|
||||||
|
FetchFromParentalRatingNode(subtree, item);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "Studios":
|
case "Studios":
|
||||||
FetchFromStudiosNode(reader.ReadSubtree(), item);
|
{
|
||||||
break;
|
using (var subtree = reader.ReadSubtree())
|
||||||
|
{
|
||||||
|
FetchFromStudiosNode(subtree, item);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "MediaInfo":
|
case "MediaInfo":
|
||||||
FetchFromMediaInfoNode(reader.ReadSubtree(), item);
|
{
|
||||||
break;
|
using (var subtree = reader.ReadSubtree())
|
||||||
|
{
|
||||||
|
FetchFromMediaInfoNode(subtree, item);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
reader.Skip();
|
reader.Skip();
|
||||||
|
@ -636,8 +671,13 @@ namespace MediaBrowser.Controller.Providers
|
||||||
switch (reader.Name)
|
switch (reader.Name)
|
||||||
{
|
{
|
||||||
case "Video":
|
case "Video":
|
||||||
FetchFromMediaInfoVideoNode(reader.ReadSubtree(), item);
|
{
|
||||||
break;
|
using (var subtree = reader.ReadSubtree())
|
||||||
|
{
|
||||||
|
FetchFromMediaInfoVideoNode(subtree, item);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
reader.Skip();
|
reader.Skip();
|
||||||
|
@ -813,9 +853,12 @@ namespace MediaBrowser.Controller.Providers
|
||||||
case "Person":
|
case "Person":
|
||||||
case "Actor":
|
case "Actor":
|
||||||
{
|
{
|
||||||
foreach (var person in GetPersonsFromXmlNode(reader.ReadSubtree()))
|
using (var subtree = reader.ReadSubtree())
|
||||||
{
|
{
|
||||||
item.AddPerson(person);
|
foreach (var person in GetPersonsFromXmlNode(subtree))
|
||||||
|
{
|
||||||
|
item.AddPerson(person);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
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>
|
/// <summary>
|
||||||
/// Fetches from studios node.
|
/// Fetches from studios node.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
<Compile Include="MediaInfo\FFProbeAudioInfoProvider.cs" />
|
<Compile Include="MediaInfo\FFProbeAudioInfoProvider.cs" />
|
||||||
<Compile Include="MediaInfo\FFProbeVideoInfoProvider.cs" />
|
<Compile Include="MediaInfo\FFProbeVideoInfoProvider.cs" />
|
||||||
<Compile Include="Movies\BoxSetProviderFromXml.cs" />
|
<Compile Include="Movies\BoxSetProviderFromXml.cs" />
|
||||||
|
<Compile Include="Movies\MovieXmlParser.cs" />
|
||||||
<Compile Include="Movies\FanArtMovieProvider.cs" />
|
<Compile Include="Movies\FanArtMovieProvider.cs" />
|
||||||
<Compile Include="Movies\FanArtMovieUpdatesPrescanTask.cs" />
|
<Compile Include="Movies\FanArtMovieUpdatesPrescanTask.cs" />
|
||||||
<Compile Include="Movies\MovieDbImagesProvider.cs" />
|
<Compile Include="Movies\MovieDbImagesProvider.cs" />
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Movies;
|
using MediaBrowser.Controller.Entities.Movies;
|
||||||
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
using MediaBrowser.Providers.Music;
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
@ -17,10 +17,12 @@ namespace MediaBrowser.Providers.Movies
|
||||||
public class MovieProviderFromXml : BaseMetadataProvider
|
public class MovieProviderFromXml : BaseMetadataProvider
|
||||||
{
|
{
|
||||||
internal static MovieProviderFromXml Current { get; private set; }
|
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)
|
: base(logManager, configurationManager)
|
||||||
{
|
{
|
||||||
|
_itemRepo = itemRepo;
|
||||||
Current = this;
|
Current = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,30 +96,9 @@ namespace MediaBrowser.Providers.Movies
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var movie = item as Movie;
|
var video = (Video) item;
|
||||||
|
|
||||||
if (movie != null)
|
await new MovieXmlParser(Logger, _itemRepo).FetchAsync(video, path, cancellationToken).ConfigureAwait(false);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
finally
|
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;
|
||||||
using MediaBrowser.Controller.Entities.TV;
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Providers.TV;
|
using MediaBrowser.Providers.TV;
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
@ -15,6 +16,7 @@ namespace MediaBrowser.Providers.Savers
|
||||||
public class EpisodeXmlSaver : IMetadataSaver
|
public class EpisodeXmlSaver : IMetadataSaver
|
||||||
{
|
{
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
|
private readonly IItemRepository _itemRepository;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether [is enabled for] [the specified item].
|
/// Determines whether [is enabled for] [the specified item].
|
||||||
|
@ -38,9 +40,10 @@ namespace MediaBrowser.Providers.Savers
|
||||||
|
|
||||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||||
|
|
||||||
public EpisodeXmlSaver(IServerConfigurationManager config)
|
public EpisodeXmlSaver(IServerConfigurationManager config, IItemRepository itemRepository)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
|
_itemRepository = itemRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -79,6 +82,7 @@ namespace MediaBrowser.Providers.Savers
|
||||||
|
|
||||||
XmlSaverHelpers.AddCommonNodes(item, builder);
|
XmlSaverHelpers.AddCommonNodes(item, builder);
|
||||||
XmlSaverHelpers.AddMediaInfo(episode, builder);
|
XmlSaverHelpers.AddMediaInfo(episode, builder);
|
||||||
|
XmlSaverHelpers.AddChapters((Video)item, builder, _itemRepository);
|
||||||
|
|
||||||
builder.Append("</Item>");
|
builder.Append("</Item>");
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Movies;
|
using MediaBrowser.Controller.Entities.Movies;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Providers.Movies;
|
using MediaBrowser.Providers.Movies;
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
@ -18,10 +19,12 @@ namespace MediaBrowser.Providers.Savers
|
||||||
public class MovieXmlSaver : IMetadataSaver
|
public class MovieXmlSaver : IMetadataSaver
|
||||||
{
|
{
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
|
private readonly IItemRepository _itemRepository;
|
||||||
|
|
||||||
public MovieXmlSaver(IServerConfigurationManager config)
|
public MovieXmlSaver(IServerConfigurationManager config, IItemRepository itemRepository)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
|
_itemRepository = itemRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -94,6 +97,8 @@ namespace MediaBrowser.Providers.Savers
|
||||||
|
|
||||||
XmlSaverHelpers.AddMediaInfo((Video)item, builder);
|
XmlSaverHelpers.AddMediaInfo((Video)item, builder);
|
||||||
|
|
||||||
|
XmlSaverHelpers.AddChapters((Video)item, builder, _itemRepository);
|
||||||
|
|
||||||
builder.Append("</Title>");
|
builder.Append("</Title>");
|
||||||
|
|
||||||
var xmlFilePath = GetSavePath(item);
|
var xmlFilePath = GetSavePath(item);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.TV;
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -79,7 +80,8 @@ namespace MediaBrowser.Providers.Savers
|
||||||
"GamesDbId",
|
"GamesDbId",
|
||||||
"BirthDate",
|
"BirthDate",
|
||||||
"DeathDate",
|
"DeathDate",
|
||||||
"LockedFields"
|
"LockedFields",
|
||||||
|
"Chapters"
|
||||||
});
|
});
|
||||||
|
|
||||||
var position = xml.ToString().LastIndexOf("</", StringComparison.OrdinalIgnoreCase);
|
var position = xml.ToString().LastIndexOf("</", StringComparison.OrdinalIgnoreCase);
|
||||||
|
@ -411,7 +413,27 @@ namespace MediaBrowser.Providers.Savers
|
||||||
|
|
||||||
builder.Append("</Persons>");
|
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>
|
/// <summary>
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.TV;
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Model.Logging;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.TV
|
namespace MediaBrowser.Providers.TV
|
||||||
{
|
{
|
||||||
|
@ -17,10 +18,12 @@ namespace MediaBrowser.Providers.TV
|
||||||
public class EpisodeProviderFromXml : BaseMetadataProvider
|
public class EpisodeProviderFromXml : BaseMetadataProvider
|
||||||
{
|
{
|
||||||
internal static EpisodeProviderFromXml Current { get; private set; }
|
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)
|
: base(logManager, configurationManager)
|
||||||
{
|
{
|
||||||
|
_itemRepo = itemRepo;
|
||||||
Current = this;
|
Current = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +101,7 @@ namespace MediaBrowser.Providers.TV
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
new EpisodeXmlParser(Logger).Fetch((Episode)item, metadataFile, cancellationToken);
|
await new EpisodeXmlParser(Logger, _itemRepo).FetchAsync((Episode)item, metadataFile, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
using MediaBrowser.Controller.Entities.TV;
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
||||||
namespace MediaBrowser.Providers.TV
|
namespace MediaBrowser.Providers.TV
|
||||||
|
@ -11,13 +14,28 @@ namespace MediaBrowser.Providers.TV
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class EpisodeXmlParser : BaseItemXmlParser<Episode>
|
public class EpisodeXmlParser : BaseItemXmlParser<Episode>
|
||||||
{
|
{
|
||||||
/// <summary>
|
private readonly IItemRepository _itemRepo;
|
||||||
/// Initializes a new instance of the <see cref="EpisodeXmlParser" /> class.
|
|
||||||
/// </summary>
|
private Task _chaptersTask = null;
|
||||||
/// <param name="logger">The logger.</param>
|
|
||||||
public EpisodeXmlParser(ILogger logger)
|
public EpisodeXmlParser(ILogger logger, IItemRepository itemRepo)
|
||||||
: base(logger)
|
: 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>
|
/// <summary>
|
||||||
|
@ -29,7 +47,13 @@ namespace MediaBrowser.Providers.TV
|
||||||
{
|
{
|
||||||
switch (reader.Name)
|
switch (reader.Name)
|
||||||
{
|
{
|
||||||
|
case "Chapters":
|
||||||
|
|
||||||
|
_chaptersTask = FetchChaptersFromXmlNode(item.Id, reader.ReadSubtree(), _itemRepo, CancellationToken.None);
|
||||||
|
break;
|
||||||
|
|
||||||
case "Episode":
|
case "Episode":
|
||||||
|
|
||||||
//MB generated metadata is within an "Episode" node
|
//MB generated metadata is within an "Episode" node
|
||||||
using (var subTree = reader.ReadSubtree())
|
using (var subTree = reader.ReadSubtree())
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,16 +2,12 @@
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
|
||||||
using MediaBrowser.Controller.Entities.TV;
|
|
||||||
using MediaBrowser.Controller.IO;
|
using MediaBrowser.Controller.IO;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
using MediaBrowser.Model.Net;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -25,12 +21,6 @@ namespace MediaBrowser.Server.Implementations.Providers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ProviderManager : IProviderManager
|
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>
|
/// <summary>
|
||||||
/// The _logger
|
/// The _logger
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -72,19 +62,6 @@ namespace MediaBrowser.Server.Implementations.Providers
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
ConfigurationManager = configurationManager;
|
ConfigurationManager = configurationManager;
|
||||||
_directoryWatchers = directoryWatchers;
|
_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>
|
/// <summary>
|
||||||
|
@ -217,8 +194,6 @@ namespace MediaBrowser.Server.Implementations.Providers
|
||||||
// This provides the ability to cancel just this one provider
|
// This provides the ability to cancel just this one provider
|
||||||
var innerCancellationTokenSource = new CancellationTokenSource();
|
var innerCancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
OnProviderRefreshBeginning(provider, item, innerCancellationTokenSource);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var changed = await provider.FetchAsync(item, force, CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, innerCancellationTokenSource.Token).Token).ConfigureAwait(false);
|
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
|
finally
|
||||||
{
|
{
|
||||||
innerCancellationTokenSource.Dispose();
|
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…
Reference in New Issue