|
|
|
@ -16,36 +16,42 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Provides a base class for parsing metadata xml.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <typeparam name="T"></typeparam>
|
|
|
|
|
/// <typeparam name="T">Type of item xml parser.</typeparam>
|
|
|
|
|
public class BaseItemXmlParser<T>
|
|
|
|
|
where T : BaseItem
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The logger.
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected ILogger<BaseItemXmlParser<T>> Logger { get; private set; }
|
|
|
|
|
|
|
|
|
|
protected IProviderManager ProviderManager { get; private set; }
|
|
|
|
|
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
|
|
|
|
|
|
|
|
|
private Dictionary<string, string> _validProviderIds;
|
|
|
|
|
private Dictionary<string, string>? _validProviderIds;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Initializes a new instance of the <see cref="BaseItemXmlParser{T}" /> class.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="logger">The logger.</param>
|
|
|
|
|
/// <param name="logger">Instance of the <see cref="ILogger{BaseItemXmlParser}"/> interface.</param>
|
|
|
|
|
/// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
|
|
|
|
|
public BaseItemXmlParser(ILogger<BaseItemXmlParser<T>> logger, IProviderManager providerManager)
|
|
|
|
|
{
|
|
|
|
|
Logger = logger;
|
|
|
|
|
ProviderManager = providerManager;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the logger.
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected ILogger<BaseItemXmlParser<T>> Logger { get; private set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the provider manager.
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected IProviderManager ProviderManager { get; private set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Fetches metadata for an item from one xml file.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="item">The item.</param>
|
|
|
|
|
/// <param name="metadataFile">The metadata file.</param>
|
|
|
|
|
/// <param name="cancellationToken">The cancellation token.</param>
|
|
|
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|
|
|
|
/// <exception cref="ArgumentNullException">Item is null.</exception>
|
|
|
|
|
public void Fetch(MetadataResult<T> item, string metadataFile, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
if (item == null)
|
|
|
|
@ -58,7 +64,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
throw new ArgumentException("The metadata file was empty or null.", nameof(metadataFile));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var settings = new XmlReaderSettings()
|
|
|
|
|
var settings = new XmlReaderSettings
|
|
|
|
|
{
|
|
|
|
|
ValidationType = ValidationType.None,
|
|
|
|
|
CheckCharacters = false,
|
|
|
|
@ -98,10 +104,9 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
{
|
|
|
|
|
item.ResetPeople();
|
|
|
|
|
|
|
|
|
|
using (var fileStream = File.OpenRead(metadataFile))
|
|
|
|
|
using (var streamReader = new StreamReader(fileStream, encoding))
|
|
|
|
|
using (var reader = XmlReader.Create(streamReader, settings))
|
|
|
|
|
{
|
|
|
|
|
using var fileStream = File.OpenRead(metadataFile);
|
|
|
|
|
using var streamReader = new StreamReader(fileStream, encoding);
|
|
|
|
|
using var reader = XmlReader.Create(streamReader, settings);
|
|
|
|
|
reader.MoveToContent();
|
|
|
|
|
reader.Read();
|
|
|
|
|
|
|
|
|
@ -120,9 +125,6 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Fetches metadata from one Xml Element.
|
|
|
|
@ -235,10 +237,9 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(val))
|
|
|
|
|
{
|
|
|
|
|
var person = item as Person;
|
|
|
|
|
if (person != null)
|
|
|
|
|
if (item is Person person)
|
|
|
|
|
{
|
|
|
|
|
person.ProductionLocations = new string[] { val };
|
|
|
|
|
person.ProductionLocations = new[] { val };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -259,7 +260,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}).Where(i => i.HasValue).Select(i => i.Value).ToArray();
|
|
|
|
|
}).Where(i => i.HasValue).Select(i => i!.Value).ToArray();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
@ -288,7 +289,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
{
|
|
|
|
|
using (var subtree = reader.ReadSubtree())
|
|
|
|
|
{
|
|
|
|
|
FetchFromCountriesNode(subtree, item);
|
|
|
|
|
FetchFromCountriesNode(subtree);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
@ -393,6 +394,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case "Writer":
|
|
|
|
|
{
|
|
|
|
|
foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonType.Writer }))
|
|
|
|
@ -410,14 +412,14 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
|
|
|
|
|
case "Actors":
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
var actors = reader.ReadInnerXml();
|
|
|
|
|
|
|
|
|
|
if (actors.Contains("<"))
|
|
|
|
|
if (actors.Contains("<", StringComparison.Ordinal))
|
|
|
|
|
{
|
|
|
|
|
// This is one of the mis-named "Actors" full nodes created by MB2
|
|
|
|
|
// Create a reader and pass it to the persons node processor
|
|
|
|
|
FetchDataFromPersonsNode(XmlReader.Create(new StringReader("<Persons>" + actors + "</Persons>")), itemResult);
|
|
|
|
|
using var xmlReader = XmlReader.Create(new StringReader($"<Persons>{actors}</Persons>"));
|
|
|
|
|
FetchDataFromPersonsNode(xmlReader, itemResult);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
@ -483,11 +485,9 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
{
|
|
|
|
|
if (!reader.IsEmptyElement)
|
|
|
|
|
{
|
|
|
|
|
using (var subtree = reader.ReadSubtree())
|
|
|
|
|
{
|
|
|
|
|
using var subtree = reader.ReadSubtree();
|
|
|
|
|
FetchDataFromTrailersNode(subtree, item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
reader.Read();
|
|
|
|
@ -514,7 +514,6 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
case "Rating":
|
|
|
|
|
case "IMDBrating":
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
var rating = reader.ReadElementContentAsString();
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(rating))
|
|
|
|
@ -576,11 +575,9 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
{
|
|
|
|
|
if (!reader.IsEmptyElement)
|
|
|
|
|
{
|
|
|
|
|
using (var subtree = reader.ReadSubtree())
|
|
|
|
|
{
|
|
|
|
|
using var subtree = reader.ReadSubtree();
|
|
|
|
|
FetchFromGenresNode(subtree, item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
reader.Read();
|
|
|
|
@ -593,11 +590,9 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
{
|
|
|
|
|
if (!reader.IsEmptyElement)
|
|
|
|
|
{
|
|
|
|
|
using (var subtree = reader.ReadSubtree())
|
|
|
|
|
{
|
|
|
|
|
using var subtree = reader.ReadSubtree();
|
|
|
|
|
FetchFromTagsNode(subtree, item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
reader.Read();
|
|
|
|
@ -610,11 +605,9 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
{
|
|
|
|
|
if (!reader.IsEmptyElement)
|
|
|
|
|
{
|
|
|
|
|
using (var subtree = reader.ReadSubtree())
|
|
|
|
|
{
|
|
|
|
|
using var subtree = reader.ReadSubtree();
|
|
|
|
|
FetchDataFromPersonsNode(subtree, itemResult);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
reader.Read();
|
|
|
|
@ -627,11 +620,9 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
{
|
|
|
|
|
if (!reader.IsEmptyElement)
|
|
|
|
|
{
|
|
|
|
|
using (var subtree = reader.ReadSubtree())
|
|
|
|
|
{
|
|
|
|
|
using var subtree = reader.ReadSubtree();
|
|
|
|
|
FetchFromStudiosNode(subtree, item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
reader.Read();
|
|
|
|
@ -644,15 +635,12 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
{
|
|
|
|
|
if (!reader.IsEmptyElement)
|
|
|
|
|
{
|
|
|
|
|
using (var subtree = reader.ReadSubtree())
|
|
|
|
|
{
|
|
|
|
|
var hasShares = item as IHasShares;
|
|
|
|
|
if (hasShares != null)
|
|
|
|
|
using var subtree = reader.ReadSubtree();
|
|
|
|
|
if (item is IHasShares hasShares)
|
|
|
|
|
{
|
|
|
|
|
FetchFromSharesNode(subtree, hasShares);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
reader.Read();
|
|
|
|
@ -665,9 +653,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
{
|
|
|
|
|
var val = reader.ReadElementContentAsString();
|
|
|
|
|
|
|
|
|
|
var video = item as Video;
|
|
|
|
|
|
|
|
|
|
if (video != null)
|
|
|
|
|
if (item is Video video)
|
|
|
|
|
{
|
|
|
|
|
if (string.Equals("HSBS", val, StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
@ -697,7 +683,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
string readerName = reader.Name;
|
|
|
|
|
if (_validProviderIds.TryGetValue(readerName, out string providerIdValue))
|
|
|
|
|
if (_validProviderIds!.TryGetValue(readerName, out string providerIdValue))
|
|
|
|
|
{
|
|
|
|
|
var id = reader.ReadElementContentAsString();
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(id))
|
|
|
|
@ -788,7 +774,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
|
|
|
|
|
case "CanEdit":
|
|
|
|
|
{
|
|
|
|
|
share.CanEdit = string.Equals(reader.ReadElementContentAsString(), true.ToString(), StringComparison.OrdinalIgnoreCase);
|
|
|
|
|
share.CanEdit = string.Equals(reader.ReadElementContentAsString(), true.ToString(CultureInfo.InvariantCulture), StringComparison.OrdinalIgnoreCase);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -806,7 +792,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
return share;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void FetchFromCountriesNode(XmlReader reader, T item)
|
|
|
|
|
private void FetchFromCountriesNode(XmlReader reader)
|
|
|
|
|
{
|
|
|
|
|
reader.MoveToContent();
|
|
|
|
|
reader.Read();
|
|
|
|
@ -1143,6 +1129,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case "SortOrder":
|
|
|
|
|
{
|
|
|
|
|
var val = reader.ReadElementContentAsString();
|
|
|
|
@ -1169,23 +1156,19 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var personInfo = new PersonInfo
|
|
|
|
|
{
|
|
|
|
|
Name = name.Trim(),
|
|
|
|
|
Role = role,
|
|
|
|
|
Type = type,
|
|
|
|
|
SortOrder = sortOrder
|
|
|
|
|
};
|
|
|
|
|
var personInfo = new PersonInfo { Name = name.Trim(), Role = role, Type = type, SortOrder = sortOrder };
|
|
|
|
|
|
|
|
|
|
return new[] { personInfo };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected LinkedChild GetLinkedChild(XmlReader reader)
|
|
|
|
|
{
|
|
|
|
|
var linkedItem = new LinkedChild
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Get linked child.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="reader">The xml reader.</param>
|
|
|
|
|
/// <returns>The linked child.</returns>
|
|
|
|
|
protected LinkedChild? GetLinkedChild(XmlReader reader)
|
|
|
|
|
{
|
|
|
|
|
Type = LinkedChildType.Manual
|
|
|
|
|
};
|
|
|
|
|
var linkedItem = new LinkedChild { Type = LinkedChildType.Manual };
|
|
|
|
|
|
|
|
|
|
reader.MoveToContent();
|
|
|
|
|
reader.Read();
|
|
|
|
@ -1202,6 +1185,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
linkedItem.Path = reader.ReadElementContentAsString();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case "ItemId":
|
|
|
|
|
{
|
|
|
|
|
linkedItem.LibraryItemId = reader.ReadElementContentAsString();
|
|
|
|
@ -1228,7 +1212,12 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected Share GetShare(XmlReader reader)
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Get share.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="reader">The xml reader.</param>
|
|
|
|
|
/// <returns>The share.</returns>
|
|
|
|
|
protected Share? GetShare(XmlReader reader)
|
|
|
|
|
{
|
|
|
|
|
var item = new Share();
|
|
|
|
|
|
|
|
|
@ -1276,19 +1265,19 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Used to split names of comma or pipe delimeted genres and people.
|
|
|
|
|
/// Used to split names of comma or pipe delimited genres and people.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="value">The value.</param>
|
|
|
|
|
/// <returns>IEnumerable{System.String}.</returns>
|
|
|
|
|
private IEnumerable<string> SplitNames(string value)
|
|
|
|
|
{
|
|
|
|
|
value = value ?? string.Empty;
|
|
|
|
|
value ??= string.Empty;
|
|
|
|
|
|
|
|
|
|
// Only split by comma if there is no pipe in the string
|
|
|
|
|
// We have to be careful to not split names like Matthew, Jr.
|
|
|
|
|
var separator = value.IndexOf('|') == -1 && value.IndexOf(';') == -1 ? new[] { ',' } : new[] { '|', ';' };
|
|
|
|
|
var separator = value.IndexOf('|', StringComparison.Ordinal) == -1
|
|
|
|
|
&& value.IndexOf(';', StringComparison.Ordinal) == -1 ? new[] { ',' } : new[] { '|', ';' };
|
|
|
|
|
|
|
|
|
|
value = value.Trim().Trim(separator);
|
|
|
|
|
|
|
|
|
@ -1296,7 +1285,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Provides an additional overload for string.split
|
|
|
|
|
/// Provides an additional overload for string.split.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="val">The val.</param>
|
|
|
|
|
/// <param name="separators">The separators.</param>
|
|
|
|
|