fixes #916 - Support reading playstate data from nfo's

This commit is contained in:
Luke Pulverenti
2014-09-26 22:28:13 -04:00
parent a260432fdf
commit eab030df7f
64 changed files with 709 additions and 519 deletions

View File

@@ -6,6 +6,7 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.XbmcMetadata.Configuration;
using MediaBrowser.XbmcMetadata.Savers;
using System;
using System.Linq;
@@ -36,20 +37,19 @@ namespace MediaBrowser.XbmcMetadata
void _libraryManager_ItemUpdated(object sender, ItemChangeEventArgs e)
{
// TODO: Need a more accurate check here to see if xbmc metadata saving is enabled.
// This is probably good enough, but no guarantee
var userId = _config.GetNfoConfiguration().UserId;
if (string.IsNullOrWhiteSpace(userId))
{
return;
}
if (e.UpdateReason == ItemUpdateType.ImageUpdate)
{
var person = e.Item as Person;
if (person != null)
{
var config = _config.GetNfoConfiguration();
if (!config.SaveImagePathsInNfo)
{
return;
}
var items = _libraryManager.RootFolder.RecursiveChildren;
items = person.GetTaggedItems(items).ToList();
@@ -63,22 +63,13 @@ namespace MediaBrowser.XbmcMetadata
void _userDataManager_UserDataSaved(object sender, UserDataSaveEventArgs e)
{
var userId = _config.GetNfoConfiguration().UserId;
if (string.IsNullOrWhiteSpace(userId))
{
return;
}
if (e.SaveReason == UserDataSaveReason.PlaybackFinished || e.SaveReason == UserDataSaveReason.TogglePlayed)
if (e.SaveReason == UserDataSaveReason.PlaybackFinished || e.SaveReason == UserDataSaveReason.TogglePlayed || e.SaveReason == UserDataSaveReason.UpdateUserRating)
{
var item = e.Item as BaseItem;
if (item != null)
if (!string.IsNullOrWhiteSpace(_config.GetNfoConfiguration().UserId))
{
if (item is Video)
{
SaveMetadataForItem(item, ItemUpdateType.MetadataEdit);
}
SaveMetadataForItem(item, ItemUpdateType.MetadataEdit);
}
}
}
@@ -99,7 +90,7 @@ namespace MediaBrowser.XbmcMetadata
try
{
await _providerManager.SaveMetadata(item, updateReason).ConfigureAwait(false);
await _providerManager.SaveMetadata(item, updateReason, new[] { BaseNfoSaver.SaverName }).ConfigureAwait(false);
}
catch (Exception ex)
{

View File

@@ -3,15 +3,13 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.XbmcMetadata.Configuration;
using MediaBrowser.XbmcMetadata.Savers;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Xml;
using MediaBrowser.XbmcMetadata.Savers;
namespace MediaBrowser.XbmcMetadata.Parsers
{
@@ -41,10 +39,12 @@ namespace MediaBrowser.XbmcMetadata.Parsers
/// Fetches metadata for an item from one xml file
/// </summary>
/// <param name="item">The item.</param>
/// <param name="userDataList">The user data list.</param>
/// <param name="metadataFile">The metadata file.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <exception cref="System.ArgumentNullException"></exception>
public void Fetch(T item, string metadataFile, CancellationToken cancellationToken)
/// <exception cref="System.ArgumentNullException">
/// </exception>
public void Fetch(T item, List<UserItemData> userDataList, string metadataFile, CancellationToken cancellationToken)
{
if (item == null)
{
@@ -64,17 +64,18 @@ namespace MediaBrowser.XbmcMetadata.Parsers
ValidationType = ValidationType.None
};
Fetch(item, metadataFile, settings, cancellationToken);
Fetch(item, userDataList, metadataFile, settings, cancellationToken);
}
/// <summary>
/// Fetches the specified item.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="userDataList">The user data list.</param>
/// <param name="metadataFile">The metadata file.</param>
/// <param name="settings">The settings.</param>
/// <param name="cancellationToken">The cancellation token.</param>
private void Fetch(T item, string metadataFile, XmlReaderSettings settings, CancellationToken cancellationToken)
private void Fetch(T item, List<UserItemData> userDataList, string metadataFile, XmlReaderSettings settings, CancellationToken cancellationToken)
{
using (var streamReader = BaseNfoSaver.GetStreamReader(metadataFile))
{
@@ -90,15 +91,17 @@ namespace MediaBrowser.XbmcMetadata.Parsers
if (reader.NodeType == XmlNodeType.Element)
{
FetchDataFromXmlNode(reader, item);
FetchDataFromXmlNode(reader, item, userDataList);
}
}
}
}
}
protected virtual void FetchDataFromXmlNode(XmlReader reader, T item)
protected virtual void FetchDataFromXmlNode(XmlReader reader, T item, List<UserItemData> userDataList)
{
var userDataUserId = _config.GetNfoConfiguration().UserId;
switch (reader.Name)
{
// DateCreated
@@ -571,7 +574,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
case "releasedate":
{
var formatString = _config.GetNfoConfiguration().ReleaseDateFormat;
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
@@ -798,12 +801,176 @@ namespace MediaBrowser.XbmcMetadata.Parsers
break;
}
case "watched":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
bool parsedValue;
if (bool.TryParse(val, out parsedValue))
{
if (!string.IsNullOrWhiteSpace(userDataUserId))
{
var userData = GetOrAdd(userDataList, userDataUserId);
userData.Played = parsedValue;
}
}
}
break;
}
case "playcount":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
int parsedValue;
if (int.TryParse(val, NumberStyles.Integer, _usCulture, out parsedValue))
{
if (!string.IsNullOrWhiteSpace(userDataUserId))
{
var userData = GetOrAdd(userDataList, userDataUserId);
userData.PlayCount = parsedValue;
}
}
}
break;
}
case "lastplayed":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
DateTime parsedValue;
if (DateTime.TryParseExact(val, "yyyy-MM-dd HH:mm:ss", _usCulture, DateTimeStyles.None, out parsedValue))
{
if (!string.IsNullOrWhiteSpace(userDataUserId))
{
var userData = GetOrAdd(userDataList, userDataUserId);
userData.LastPlayedDate = parsedValue;
}
}
}
break;
}
case "resume":
{
using (var subtree = reader.ReadSubtree())
{
if (!string.IsNullOrWhiteSpace(userDataUserId))
{
var userData = GetOrAdd(userDataList, userDataUserId);
FetchFromResumeNode(subtree, item, userData);
}
}
break;
}
case "isuserfavorite":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
bool parsedValue;
if (bool.TryParse(val, out parsedValue))
{
if (!string.IsNullOrWhiteSpace(userDataUserId))
{
var userData = GetOrAdd(userDataList, userDataUserId);
userData.IsFavorite = parsedValue;
}
}
}
break;
}
case "userrating":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
double parsedValue;
if (double.TryParse(val, NumberStyles.Any, _usCulture, out parsedValue))
{
if (!string.IsNullOrWhiteSpace(userDataUserId))
{
var userData = GetOrAdd(userDataList, userDataUserId);
userData.Rating = parsedValue;
}
}
}
break;
}
default:
reader.Skip();
break;
}
}
private UserItemData GetOrAdd(List<UserItemData> userDataList, string userId)
{
var userData = userDataList.FirstOrDefault(i => string.Equals(userId, i.UserId.ToString("N"), StringComparison.OrdinalIgnoreCase));
if (userData == null)
{
userData = new UserItemData()
{
UserId = new Guid(userId)
};
userDataList.Add(userData);
}
return userData;
}
private void FetchFromResumeNode(XmlReader reader, T item, UserItemData userData)
{
reader.MoveToContent();
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "position":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
double parsedValue;
if (double.TryParse(val, NumberStyles.Any, _usCulture, out parsedValue))
{
userData.PlaybackPositionTicks = TimeSpan.FromSeconds(parsedValue).Ticks;
}
}
break;
}
default:
reader.Skip();
break;
}
}
}
}
private void FetchFromFileInfoNode(XmlReader reader, T item)
{
reader.MoveToContent();

View File

@@ -1,4 +1,5 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
@@ -21,7 +22,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
}
public void Fetch(Episode item,
public void Fetch(Episode item,
List<UserItemData> userDataList,
List<LocalImageInfo> images,
List<ChapterInfo> chapters,
string metadataFile,
@@ -31,7 +33,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
_chaptersFound = chapters;
_xmlPath = metadataFile;
Fetch(item, metadataFile, cancellationToken);
Fetch(item, userDataList, metadataFile, cancellationToken);
}
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
@@ -41,7 +43,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers
/// </summary>
/// <param name="reader">The reader.</param>
/// <param name="item">The item.</param>
protected override void FetchDataFromXmlNode(XmlReader reader, Episode item)
/// <param name="userDataList">The user data list.</param>
protected override void FetchDataFromXmlNode(XmlReader reader, Episode item, List<UserItemData> userDataList)
{
switch (reader.Name)
{
@@ -203,7 +206,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
default:
base.FetchDataFromXmlNode(reader, item);
base.FetchDataFromXmlNode(reader, item, userDataList);
break;
}
}

View File

@@ -17,14 +17,15 @@ namespace MediaBrowser.XbmcMetadata.Parsers
{
}
public void Fetch(Video item,
public void Fetch(Video item,
List<UserItemData> userDataList,
List<ChapterInfo> chapters,
string metadataFile,
CancellationToken cancellationToken)
{
_chaptersFound = chapters;
Fetch(item, metadataFile, cancellationToken);
Fetch(item, userDataList, metadataFile, cancellationToken);
}
/// <summary>
@@ -32,7 +33,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers
/// </summary>
/// <param name="reader">The reader.</param>
/// <param name="item">The item.</param>
protected override void FetchDataFromXmlNode(XmlReader reader, Video item)
/// <param name="userDataList">The user data list.</param>
protected override void FetchDataFromXmlNode(XmlReader reader, Video item, List<UserItemData> userDataList)
{
switch (reader.Name)
{
@@ -89,7 +91,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
// break;
default:
base.FetchDataFromXmlNode(reader, item);
base.FetchDataFromXmlNode(reader, item, userDataList);
break;
}
}

View File

@@ -1,6 +1,8 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Model.Logging;
using System.Collections.Generic;
using System.Xml;
namespace MediaBrowser.XbmcMetadata.Parsers
@@ -16,7 +18,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers
/// </summary>
/// <param name="reader">The reader.</param>
/// <param name="item">The item.</param>
protected override void FetchDataFromXmlNode(XmlReader reader, Season item)
/// <param name="userDataList">The user data list.</param>
protected override void FetchDataFromXmlNode(XmlReader reader, Season item, List<UserItemData> userDataList)
{
switch (reader.Name)
{
@@ -37,7 +40,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
}
default:
base.FetchDataFromXmlNode(reader, item);
base.FetchDataFromXmlNode(reader, item, userDataList);
break;
}
}

View File

@@ -1,9 +1,11 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Xml;
namespace MediaBrowser.XbmcMetadata.Parsers
@@ -19,7 +21,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers
/// </summary>
/// <param name="reader">The reader.</param>
/// <param name="item">The item.</param>
protected override void FetchDataFromXmlNode(XmlReader reader, Series item)
/// <param name="userDataList">The user data list.</param>
protected override void FetchDataFromXmlNode(XmlReader reader, Series item, List<UserItemData> userDataList)
{
switch (reader.Name)
{
@@ -85,7 +88,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
}
default:
base.FetchDataFromXmlNode(reader, item);
base.FetchDataFromXmlNode(reader, item, userDataList);
break;
}
}

View File

@@ -23,7 +23,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
protected override void Fetch(LocalMetadataResult<MusicAlbum> result, string path, CancellationToken cancellationToken)
{
new BaseNfoParser<MusicAlbum>(_logger, _config).Fetch(result.Item, path, cancellationToken);
new BaseNfoParser<MusicAlbum>(_logger, _config).Fetch(result.Item, result.UserDataLIst, path, cancellationToken);
}
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

View File

@@ -23,7 +23,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
protected override void Fetch(LocalMetadataResult<MusicArtist> result, string path, CancellationToken cancellationToken)
{
new BaseNfoParser<MusicArtist>(_logger, _config).Fetch(result.Item, path, cancellationToken);
new BaseNfoParser<MusicArtist>(_logger, _config).Fetch(result.Item, result.UserDataLIst, path, cancellationToken);
}
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

View File

@@ -2,6 +2,7 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Logging;
using MediaBrowser.XbmcMetadata.Savers;
using System;
using System.IO;
using System.Threading;
@@ -27,8 +28,6 @@ namespace MediaBrowser.XbmcMetadata.Providers
var path = file.FullName;
//await XmlProviderUtils.XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
result.Item = new T();
@@ -44,10 +43,6 @@ namespace MediaBrowser.XbmcMetadata.Providers
{
result.HasMetadata = false;
}
//finally
//{
// XmlProviderUtils.XmlParsingResourcePool.Release();
//}
return result;
}
@@ -77,7 +72,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
{
get
{
return "Xbmc Nfo";
return BaseNfoSaver.SaverName;
}
}
}

View File

@@ -28,7 +28,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
{
var chapters = new List<ChapterInfo>();
new MovieNfoParser(_logger, _config).Fetch(result.Item, chapters, path, cancellationToken);
new MovieNfoParser(_logger, _config).Fetch(result.Item, result.UserDataLIst, chapters, path, cancellationToken);
result.Chapters = chapters;
}

View File

@@ -28,7 +28,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
var images = new List<LocalImageInfo>();
var chapters = new List<ChapterInfo>();
new EpisodeNfoParser(_logger, _config).Fetch(result.Item, images, chapters, path, cancellationToken);
new EpisodeNfoParser(_logger, _config).Fetch(result.Item, result.UserDataLIst, images, chapters, path, cancellationToken);
result.Images = images;
result.Chapters = chapters;

View File

@@ -23,7 +23,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
protected override void Fetch(LocalMetadataResult<Season> result, string path, CancellationToken cancellationToken)
{
new SeasonNfoParser(_logger, _config).Fetch(result.Item, path, cancellationToken);
new SeasonNfoParser(_logger, _config).Fetch(result.Item, result.UserDataLIst, path, cancellationToken);
}
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

View File

@@ -23,7 +23,7 @@ namespace MediaBrowser.XbmcMetadata.Providers
protected override void Fetch(LocalMetadataResult<Series> result, string path, CancellationToken cancellationToken)
{
new SeriesNfoParser(_logger, _config).Fetch(result.Item, path, cancellationToken);
new SeriesNfoParser(_logger, _config).Fetch(result.Item, result.UserDataLIst, path, cancellationToken);
}
protected override FileSystemInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)

View File

@@ -95,7 +95,10 @@ namespace MediaBrowser.XbmcMetadata.Savers
"musicbrainzalbumid",
"musicbrainzreleasegroupid",
"tvdbid",
"collectionitem"
"collectionitem",
"isuserfavorite",
"userrating"
}.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
@@ -117,6 +120,14 @@ namespace MediaBrowser.XbmcMetadata.Savers
protected ILogger Logger { get; private set; }
public string Name
{
get
{
return SaverName;
}
}
public static string SaverName
{
get
{
@@ -852,11 +863,6 @@ namespace MediaBrowser.XbmcMetadata.Savers
private static void AddUserData(BaseItem item, XmlWriter writer, IUserManager userManager, IUserDataManager userDataRepo, XbmcMetadataOptions options)
{
if (!(item is Video))
{
return;
}
var userId = options.UserId;
if (string.IsNullOrWhiteSpace(userId))
{
@@ -877,20 +883,30 @@ namespace MediaBrowser.XbmcMetadata.Savers
var userdata = userDataRepo.GetUserData(user.Id, item.GetUserDataKey());
writer.WriteElementString("playcount", userdata.PlayCount.ToString(UsCulture));
writer.WriteElementString("watched", userdata.Played.ToString().ToLower());
writer.WriteElementString("isuserfavorite", userdata.IsFavorite.ToString().ToLower());
if (userdata.LastPlayedDate.HasValue)
if (userdata.Rating.HasValue)
{
writer.WriteElementString("lastplayed", userdata.LastPlayedDate.Value.ToString("yyyy-MM-dd HH:mm:ss").ToLower());
writer.WriteElementString("userrating", userdata.Rating.Value.ToString(CultureInfo.InvariantCulture).ToLower());
}
writer.WriteStartElement("resume");
if (!item.IsFolder)
{
writer.WriteElementString("playcount", userdata.PlayCount.ToString(UsCulture));
writer.WriteElementString("watched", userdata.Played.ToString().ToLower());
var runTimeTicks = item.RunTimeTicks ?? 0;
if (userdata.LastPlayedDate.HasValue)
{
writer.WriteElementString("lastplayed", userdata.LastPlayedDate.Value.ToString("yyyy-MM-dd HH:mm:ss").ToLower());
}
writer.WriteElementString("position", TimeSpan.FromTicks(userdata.PlaybackPositionTicks).TotalSeconds.ToString(UsCulture));
writer.WriteElementString("total", TimeSpan.FromTicks(runTimeTicks).TotalSeconds.ToString(UsCulture));
writer.WriteStartElement("resume");
var runTimeTicks = item.RunTimeTicks ?? 0;
writer.WriteElementString("position", TimeSpan.FromTicks(userdata.PlaybackPositionTicks).TotalSeconds.ToString(UsCulture));
writer.WriteElementString("total", TimeSpan.FromTicks(runTimeTicks).TotalSeconds.ToString(UsCulture));
}
writer.WriteEndElement();
}