aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS.md23
-rw-r--r--MediaBrowser.Api/ItemUpdateService.cs7
-rw-r--r--MediaBrowser.Api/UserLibrary/ItemByNameUserDataService.cs40
-rw-r--r--MediaBrowser.Api/UserLibrary/UserLibraryService.cs136
-rw-r--r--MediaBrowser.Controller/Dto/DtoBuilder.cs10
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs6
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs25
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs44
-rw-r--r--MediaBrowser.Controller/IO/FileData.cs6
-rw-r--r--MediaBrowser.Controller/Providers/BaseItemXmlParser.cs36
-rw-r--r--MediaBrowser.Model/ApiClient/IApiClient.cs8
-rw-r--r--MediaBrowser.Model/Entities/MetadataProviders.cs3
-rw-r--r--MediaBrowser.Providers/Movies/MovieDbProvider.cs233
-rw-r--r--MediaBrowser.Providers/Music/FanArtAlbumProvider.cs15
-rw-r--r--MediaBrowser.Providers/Savers/XmlSaverHelpers.cs11
-rw-r--r--MediaBrowser.Server.Implementations/EntryPoints/Notifications/RemoteNotifications.cs2
-rw-r--r--MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj2
-rw-r--r--MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs80
-rw-r--r--MediaBrowser.Server.Implementations/MediaEncoder/fonts/fonts.conf9
-rw-r--r--MediaBrowser.ServerApplication/ApplicationHost.cs2
-rw-r--r--MediaBrowser.WebDashboard/ApiClient.js66
-rw-r--r--MediaBrowser.WebDashboard/packages.config2
-rw-r--r--Nuget/MediaBrowser.Common.Internal.nuspec4
-rw-r--r--Nuget/MediaBrowser.Common.nuspec2
-rw-r--r--Nuget/MediaBrowser.Server.Core.nuspec4
-rw-r--r--README.md2
26 files changed, 343 insertions, 435 deletions
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 7bc7ba740..002ee8760 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -16,4 +16,27 @@
- [Logos302] (https://github.com/Logos302)
- [TheWorkz](https://github.com/TheWorkz)
- [mboehler](https://github.com/mboehler)
+ - [KaHooli](https://github.com/KaHooli)
+ - [xzener](https://github.com/xzener)
+ - [CBers] (https://github.com/CBers)
+ - [Sagaia] (https://github.com/Sagaia)
+ - [JHawk111](https://github.com/JHawk111)
+ - [David3663](https://github.com/david3663)
+ - [Smyken](https://github.com/Smyken)
+ - [doron1] (https://github.com/doron1)
+ - [brainfryd] (https://github.com/brainfryd)
+ - [DGMayor] (http://github.com/DGMayor)
+ - [Jon-theHTPC] (https://github.com/Jon-theHTPC)
+ - [aspdend] (https://github.com/aspdend)
+ - [RedshirtMB](https://github.com/RedshirtMB)
+ - [thealienamongus](https://github.com/thealienamongus)
+ - [brocass](https://github.com/brocass)
+ - [pjrollo2000](https://github.com/pjrollo2000)
+ - [abobader](https://github.com/abobader)
+ - [milli260876](https://github.com/milli260876)
+ - [vileboy] (https://github.com/vileboy)
+ - [starkadius] (https://github.com/starkadius)
+ - [wraslor](https://github.com/wraslor)
+ - [mrwebsmith](https://github.com/mrwebsmith)
+ - [rickster53](https://github.com/rickster53)
- [Tharnax] (https://github.com/Tharnax)
diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs
index 54b2c0ea7..09e175e30 100644
--- a/MediaBrowser.Api/ItemUpdateService.cs
+++ b/MediaBrowser.Api/ItemUpdateService.cs
@@ -273,13 +273,6 @@ namespace MediaBrowser.Api
song.Artist = request.Artists[0];
}
- var musicAlbum = item as MusicAlbum;
-
- if (musicAlbum != null)
- {
- musicAlbum.MusicBrainzReleaseGroupId = request.GetProviderId("MusicBrainzReleaseGroupId");
- }
-
var musicVideo = item as MusicVideo;
if (musicVideo != null)
diff --git a/MediaBrowser.Api/UserLibrary/ItemByNameUserDataService.cs b/MediaBrowser.Api/UserLibrary/ItemByNameUserDataService.cs
index 4196db497..1e990b3e9 100644
--- a/MediaBrowser.Api/UserLibrary/ItemByNameUserDataService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemByNameUserDataService.cs
@@ -1,5 +1,7 @@
-using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.Dto;
using ServiceStack.ServiceHost;
using ServiceStack.Text.Controller;
using System;
@@ -18,7 +20,7 @@ namespace MediaBrowser.Api.UserLibrary
[Route("/Users/{UserId}/Favorites/MusicGenres/{Name}", "POST")]
[Route("/Users/{UserId}/Favorites/GameGenres/{Name}", "POST")]
[Api(Description = "Marks something as a favorite")]
- public class MarkItemByNameFavorite : IReturnVoid
+ public class MarkItemByNameFavorite : IReturn<UserItemDataDto>
{
/// <summary>
/// Gets or sets the user id.
@@ -45,7 +47,7 @@ namespace MediaBrowser.Api.UserLibrary
[Route("/Users/{UserId}/Favorites/MusicGenres/{Name}", "DELETE")]
[Route("/Users/{UserId}/Favorites/GameGenres/{Name}", "DELETE")]
[Api(Description = "Unmarks something as a favorite")]
- public class UnmarkItemByNameFavorite : IReturnVoid
+ public class UnmarkItemByNameFavorite : IReturn<UserItemDataDto>
{
/// <summary>
/// Gets or sets the user id.
@@ -71,7 +73,7 @@ namespace MediaBrowser.Api.UserLibrary
[Route("/Users/{UserId}/Ratings/Genres/{Name}", "POST")]
[Route("/Users/{UserId}/Ratings/MusicGenres/{Name}", "POST")]
[Api(Description = "Updates a user's rating for an item")]
- public class UpdateItemByNameRating : IReturnVoid
+ public class UpdateItemByNameRating : IReturn<UserItemDataDto>
{
/// <summary>
/// Gets or sets the user id.
@@ -105,7 +107,7 @@ namespace MediaBrowser.Api.UserLibrary
[Route("/Users/{UserId}/Ratings/MusicGenres/{Name}", "DELETE")]
[Route("/Users/{UserId}/Ratings/GameGenres/{Name}", "DELETE")]
[Api(Description = "Deletes a user's saved personal rating for an item")]
- public class DeleteItemByNameRating : IReturnVoid
+ public class DeleteItemByNameRating : IReturn<UserItemDataDto>
{
/// <summary>
/// Gets or sets the user id.
@@ -152,56 +154,56 @@ namespace MediaBrowser.Api.UserLibrary
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Post(MarkItemByNameFavorite request)
+ public object Post(MarkItemByNameFavorite request)
{
var pathInfo = PathInfo.Parse(RequestContext.PathInfo);
var type = pathInfo.GetArgumentValue<string>(3);
var task = MarkFavorite(request.UserId, type, request.Name, true);
- Task.WaitAll(task);
+ return ToOptimizedResult(task.Result);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Post(UpdateItemByNameRating request)
+ public object Post(UpdateItemByNameRating request)
{
var pathInfo = PathInfo.Parse(RequestContext.PathInfo);
var type = pathInfo.GetArgumentValue<string>(3);
var task = MarkLike(request.UserId, type, request.Name, request.Likes);
- Task.WaitAll(task);
+ return ToOptimizedResult(task.Result);
}
/// <summary>
/// Deletes the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Delete(UnmarkItemByNameFavorite request)
+ public object Delete(UnmarkItemByNameFavorite request)
{
var pathInfo = PathInfo.Parse(RequestContext.PathInfo);
var type = pathInfo.GetArgumentValue<string>(3);
var task = MarkFavorite(request.UserId, type, request.Name, false);
- Task.WaitAll(task);
+ return ToOptimizedResult(task.Result);
}
/// <summary>
/// Deletes the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Delete(DeleteItemByNameRating request)
+ public object Delete(DeleteItemByNameRating request)
{
var pathInfo = PathInfo.Parse(RequestContext.PathInfo);
var type = pathInfo.GetArgumentValue<string>(3);
var task = MarkLike(request.UserId, type, request.Name, null);
- Task.WaitAll(task);
+ return ToOptimizedResult(task.Result);
}
/// <summary>
@@ -212,7 +214,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="name">The name.</param>
/// <param name="isFavorite">if set to <c>true</c> [is favorite].</param>
/// <returns>Task.</returns>
- protected async Task MarkFavorite(Guid userId, string type, string name, bool isFavorite)
+ protected async Task<UserItemDataDto> MarkFavorite(Guid userId, string type, string name, bool isFavorite)
{
var item = await GetItemByName(name, type, LibraryManager).ConfigureAwait(false);
@@ -225,6 +227,10 @@ namespace MediaBrowser.Api.UserLibrary
data.IsFavorite = isFavorite;
await UserDataRepository.SaveUserData(userId, key, data, CancellationToken.None).ConfigureAwait(false);
+
+ data = UserDataRepository.GetUserData(userId, key);
+
+ return DtoBuilder.GetUserItemDataDto(data);
}
/// <summary>
@@ -235,7 +241,7 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="name">The name.</param>
/// <param name="likes">if set to <c>true</c> [likes].</param>
/// <returns>Task.</returns>
- protected async Task MarkLike(Guid userId, string type, string name, bool? likes)
+ protected async Task<UserItemDataDto> MarkLike(Guid userId, string type, string name, bool? likes)
{
var item = await GetItemByName(name, type, LibraryManager).ConfigureAwait(false);
@@ -247,6 +253,10 @@ namespace MediaBrowser.Api.UserLibrary
data.Likes = likes;
await UserDataRepository.SaveUserData(userId, key, data, CancellationToken.None).ConfigureAwait(false);
+
+ data = UserDataRepository.GetUserData(userId, key);
+
+ return DtoBuilder.GetUserItemDataDto(data);
}
}
}
diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
index 46254435f..304ff27d0 100644
--- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
@@ -79,7 +79,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
[Route("/Users/{UserId}/FavoriteItems/{Id}", "POST")]
[Api(Description = "Marks an item as a favorite")]
- public class MarkFavoriteItem : IReturnVoid
+ public class MarkFavoriteItem : IReturn<UserItemDataDto>
{
/// <summary>
/// Gets or sets the user id.
@@ -101,7 +101,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
[Route("/Users/{UserId}/FavoriteItems/{Id}", "DELETE")]
[Api(Description = "Unmarks an item as a favorite")]
- public class UnmarkFavoriteItem : IReturnVoid
+ public class UnmarkFavoriteItem : IReturn<UserItemDataDto>
{
/// <summary>
/// Gets or sets the user id.
@@ -123,7 +123,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
[Route("/Users/{UserId}/Items/{Id}/Rating", "DELETE")]
[Api(Description = "Deletes a user's saved personal rating for an item")]
- public class DeleteUserItemRating : IReturnVoid
+ public class DeleteUserItemRating : IReturn<UserItemDataDto>
{
/// <summary>
/// Gets or sets the user id.
@@ -145,7 +145,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
[Route("/Users/{UserId}/Items/{Id}/Rating", "POST")]
[Api(Description = "Updates a user's rating for an item")]
- public class UpdateUserItemRating : IReturnVoid
+ public class UpdateUserItemRating : IReturn<UserItemDataDto>
{
/// <summary>
/// Gets or sets the user id.
@@ -174,7 +174,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
[Route("/Users/{UserId}/PlayedItems/{Id}", "POST")]
[Api(Description = "Marks an item as played")]
- public class MarkPlayedItem : IReturnVoid
+ public class MarkPlayedItem : IReturn<UserItemDataDto>
{
/// <summary>
/// Gets or sets the user id.
@@ -196,7 +196,7 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
[Route("/Users/{UserId}/PlayedItems/{Id}", "DELETE")]
[Api(Description = "Marks an item as unplayed")]
- public class MarkUnplayedItem : IReturnVoid
+ public class MarkUnplayedItem : IReturn<UserItemDataDto>
{
/// <summary>
/// Gets or sets the user id.
@@ -372,7 +372,6 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="itemRepo">The item repo.</param>
/// <exception cref="System.ArgumentNullException">jsonSerializer</exception>
public UserLibraryService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository, IItemRepository itemRepo, ISessionManager sessionManager)
- : base()
{
_userManager = userManager;
_libraryManager = libraryManager;
@@ -388,6 +387,13 @@ namespace MediaBrowser.Api.UserLibrary
/// <returns>System.Object.</returns>
public object Get(GetSpecialFeatures request)
{
+ var result = GetAsync(request).Result;
+
+ return ToOptimizedResult(result);
+ }
+
+ private Task<BaseItemDto[]> GetAsync(GetSpecialFeatures request)
+ {
var user = _userManager.GetUserById(request.UserId);
var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id);
@@ -399,14 +405,12 @@ namespace MediaBrowser.Api.UserLibrary
var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository, _itemRepo);
- var items = movie.SpecialFeatureIds
+ var tasks = movie.SpecialFeatureIds
.Select(_itemRepo.RetrieveItem)
.OrderBy(i => i.SortName)
- .Select(i => dtoBuilder.GetBaseItemDto(i, fields, user, movie))
- .Select(t => t.Result)
- .ToList();
+ .Select(i => dtoBuilder.GetBaseItemDto(i, fields, user, movie));
- return ToOptimizedResult(items);
+ return Task.WhenAll(tasks);
}
/// <summary>
@@ -416,6 +420,13 @@ namespace MediaBrowser.Api.UserLibrary
/// <returns>System.Object.</returns>
public object Get(GetLocalTrailers request)
{
+ var result = GetAsync(request).Result;
+
+ return ToOptimizedResult(result);
+ }
+
+ private Task<BaseItemDto[]> GetAsync(GetLocalTrailers request)
+ {
var user = _userManager.GetUserById(request.UserId);
var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id);
@@ -425,14 +436,12 @@ namespace MediaBrowser.Api.UserLibrary
var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userDataRepository, _itemRepo);
- var items = item.LocalTrailerIds
+ var tasks = item.LocalTrailerIds
.Select(_itemRepo.RetrieveItem)
.OrderBy(i => i.SortName)
- .Select(i => dtoBuilder.GetBaseItemDto(i, fields, user, item))
- .Select(t => t.Result)
- .ToList();
+ .Select(i => dtoBuilder.GetBaseItemDto(i, fields, user, item));
- return ToOptimizedResult(items);
+ return Task.WhenAll(tasks);
}
/// <summary>
@@ -497,34 +506,29 @@ namespace MediaBrowser.Api.UserLibrary
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Post(MarkFavoriteItem request)
+ public object Post(MarkFavoriteItem request)
{
- var user = _userManager.GetUserById(request.UserId);
+ var dto = MarkFavorite(request.UserId, request.Id, true).Result;
- var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id);
-
- // Get the user data for this item
- var key = item.GetUserDataKey();
-
- var data = _userDataRepository.GetUserData(user.Id, key);
-
- // Set favorite status
- data.IsFavorite = true;
-
- var task = _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None);
-
- Task.WaitAll(task);
+ return ToOptimizedResult(dto);
}
/// <summary>
/// Deletes the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Delete(UnmarkFavoriteItem request)
+ public object Delete(UnmarkFavoriteItem request)
{
- var user = _userManager.GetUserById(request.UserId);
+ var dto = MarkFavorite(request.UserId, request.Id, false).Result;
- var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id);
+ return ToOptimizedResult(dto);
+ }
+
+ private async Task<UserItemDataDto> MarkFavorite(Guid userId, string itemId, bool isFavorite)
+ {
+ var user = _userManager.GetUserById(userId);
+
+ var item = string.IsNullOrEmpty(itemId) ? user.RootFolder : DtoBuilder.GetItemByClientId(itemId, _userManager, _libraryManager, user.Id);
var key = item.GetUserDataKey();
@@ -532,68 +536,68 @@ namespace MediaBrowser.Api.UserLibrary
var data = _userDataRepository.GetUserData(user.Id, key);
// Set favorite status
- data.IsFavorite = false;
+ data.IsFavorite = isFavorite;
- var task = _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None);
+ await _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None).ConfigureAwait(false);
- Task.WaitAll(task);
+ data = _userDataRepository.GetUserData(user.Id, key);
+
+ return DtoBuilder.GetUserItemDataDto(data);
}
/// <summary>
/// Deletes the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Delete(DeleteUserItemRating request)
+ public object Delete(DeleteUserItemRating request)
{
- var user = _userManager.GetUserById(request.UserId);
-
- var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id);
-
- var key = item.GetUserDataKey();
-
- // Get the user data for this item
- var data = _userDataRepository.GetUserData(user.Id, key);
-
- data.Rating = null;
-
- var task = _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None);
+ var dto = UpdateUserItemRating(request.UserId, request.Id, null).Result;
- Task.WaitAll(task);
+ return ToOptimizedResult(dto);
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Post(UpdateUserItemRating request)
+ public object Post(UpdateUserItemRating request)
{
- var user = _userManager.GetUserById(request.UserId);
+ var dto = UpdateUserItemRating(request.UserId, request.Id, request.Likes).Result;
- var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : DtoBuilder.GetItemByClientId(request.Id, _userManager, _libraryManager, user.Id);
+ return ToOptimizedResult(dto);
+ }
+
+ private async Task<UserItemDataDto> UpdateUserItemRating(Guid userId, string itemId, bool? likes)
+ {
+ var user = _userManager.GetUserById(userId);
+
+ var item = string.IsNullOrEmpty(itemId) ? user.RootFolder : DtoBuilder.GetItemByClientId(itemId, _userManager, _libraryManager, user.Id);
var key = item.GetUserDataKey();
// Get the user data for this item
var data = _userDataRepository.GetUserData(user.Id, key);
- data.Likes = request.Likes;
+ data.Likes = likes;
- var task = _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None);
+ await _userDataRepository.SaveUserData(user.Id, key, data, CancellationToken.None).ConfigureAwait(false);
- Task.WaitAll(task);
- }
+ data = _userDataRepository.GetUserData(user.Id, key);
+ return DtoBuilder.GetUserItemDataDto(data);
+ }
+
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Post(MarkPlayedItem request)
+ public object Post(MarkPlayedItem request)
{
var user = _userManager.GetUserById(request.UserId);
var task = UpdatePlayedStatus(user, request.Id, true);
- Task.WaitAll(task);
+ return ToOptimizedResult(task.Result);
}
private SessionInfo GetSession()
@@ -660,13 +664,13 @@ namespace MediaBrowser.Api.UserLibrary
/// Deletes the specified request.
/// </summary>
/// <param name="request">The request.</param>
- public void Delete(MarkUnplayedItem request)
+ public object Delete(MarkUnplayedItem request)
{
var user = _userManager.GetUserById(request.UserId);
var task = UpdatePlayedStatus(user, request.Id, false);
- Task.WaitAll(task);
+ return ToOptimizedResult(task.Result);
}
/// <summary>
@@ -676,11 +680,13 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="itemId">The item id.</param>
/// <param name="wasPlayed">if set to <c>true</c> [was played].</param>
/// <returns>Task.</returns>
- private Task UpdatePlayedStatus(User user, string itemId, bool wasPlayed)
+ private async Task<UserItemDataDto> UpdatePlayedStatus(User user, string itemId, bool wasPlayed)
{
var item = DtoBuilder.GetItemByClientId(itemId, _userManager, _libraryManager, user.Id);
- return item.SetPlayedStatus(user, wasPlayed, _userDataRepository);
+ await item.SetPlayedStatus(user, wasPlayed, _userDataRepository).ConfigureAwait(false);
+
+ return DtoBuilder.GetUserItemDataDto(_userDataRepository.GetUserData(user.Id, item.GetUserDataKey()));
}
}
}
diff --git a/MediaBrowser.Controller/Dto/DtoBuilder.cs b/MediaBrowser.Controller/Dto/DtoBuilder.cs
index 85d2a2003..b2b858b6d 100644
--- a/MediaBrowser.Controller/Dto/DtoBuilder.cs
+++ b/MediaBrowser.Controller/Dto/DtoBuilder.cs
@@ -90,6 +90,11 @@ namespace MediaBrowser.Controller.Dto
}
}
+ if (fields.Contains(ItemFields.DisplayPreferencesId))
+ {
+ dto.DisplayPreferencesId = item.DisplayPreferencesId.ToString("N");
+ }
+
if (user != null)
{
AttachUserSpecificInfo(dto, item, user, fields);
@@ -272,11 +277,6 @@ namespace MediaBrowser.Controller.Dto
/// <param name="fields">The fields.</param>
private void AttachUserSpecificInfo(BaseItemDto dto, BaseItem item, User user, List<ItemFields> fields)
{
- if (item.IsFolder && fields.Contains(ItemFields.DisplayPreferencesId))
- {
- dto.DisplayPreferencesId = ((Folder) item).DisplayPreferencesId.ToString("N");
- }
-
if (item.IsFolder)
{
var hasItemCounts = fields.Contains(ItemFields.ItemCounts);
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
index 1526cf46a..1668b91d4 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
@@ -91,11 +91,5 @@ namespace MediaBrowser.Controller.Entities.Audio
{
return RecursiveChildren.OfType<Audio>().Any(i => i.HasArtist(artist));
}
-
- /// <summary>
- /// Gets or sets the music brainz release group id.
- /// </summary>
- /// <value>The music brainz release group id.</value>
- public string MusicBrainzReleaseGroupId { get; set; }
}
}
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index cc2e82b1f..a75dc47e1 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Localization;
@@ -84,6 +85,21 @@ namespace MediaBrowser.Controller.Entities
public Guid Id { get; set; }
/// <summary>
+ /// Return the id that should be used to key display prefs for this item.
+ /// Default is based on the type for everything except actual generic folders.
+ /// </summary>
+ /// <value>The display prefs id.</value>
+ [IgnoreDataMember]
+ public virtual Guid DisplayPreferencesId
+ {
+ get
+ {
+ var thisType = GetType();
+ return thisType == typeof(Folder) ? Id : thisType.FullName.GetMD5();
+ }
+ }
+
+ /// <summary>
/// Gets or sets the path.
/// </summary>
/// <value>The path.</value>
@@ -393,7 +409,12 @@ namespace MediaBrowser.Controller.Entities
{
get
{
- return ForcedSortName ?? _sortName ?? (_sortName = CreateSortName());
+ if (!string.IsNullOrEmpty(ForcedSortName))
+ {
+ return ForcedSortName;
+ }
+
+ return _sortName ?? (_sortName = CreateSortName());
}
}
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index ca43ca616..91f4504c2 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -16,6 +16,7 @@ using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
+using MoreLinq;
namespace MediaBrowser.Controller.Entities
{
@@ -66,21 +67,6 @@ namespace MediaBrowser.Controller.Entities
}
}
- /// <summary>
- /// Return the id that should be used to key display prefs for this item.
- /// Default is based on the type for everything except actual generic folders.
- /// </summary>
- /// <value>The display prefs id.</value>
- [IgnoreDataMember]
- public virtual Guid DisplayPreferencesId
- {
- get
- {
- var thisType = GetType();
- return thisType == typeof(Folder) ? Id : thisType.FullName.GetMD5();
- }
- }
-
public virtual List<LinkedChild> LinkedChildren { get; set; }
protected virtual bool SupportsShortcutChildren
@@ -974,7 +960,31 @@ namespace MediaBrowser.Controller.Entities
/// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param>
/// <returns>IEnumerable{BaseItem}.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
- public IEnumerable<BaseItem> GetRecursiveChildren(User user, bool includeLinkedChildren = false)
+ public IEnumerable<BaseItem> GetRecursiveChildren(User user, bool includeLinkedChildren = true)
+ {
+ if (user == null)
+ {
+ throw new ArgumentNullException();
+ }
+
+ var children = GetRecursiveChildrenInternal(user, includeLinkedChildren);
+
+ if (includeLinkedChildren)
+ {
+ children = children.DistinctBy(i => i.Id);
+ }
+
+ return children;
+ }
+
+ /// <summary>
+ /// Gets allowed recursive children of an item
+ /// </summary>
+ /// <param name="user">The user.</param>
+ /// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param>
+ /// <returns>IEnumerable{BaseItem}.</returns>
+ /// <exception cref="System.ArgumentNullException"></exception>
+ private IEnumerable<BaseItem> GetRecursiveChildrenInternal(User user, bool includeLinkedChildren)
{
if (user == null)
{
@@ -989,7 +999,7 @@ namespace MediaBrowser.Controller.Entities
if (subFolder != null)
{
- foreach (var subitem in subFolder.GetRecursiveChildren(user, includeLinkedChildren))
+ foreach (var subitem in subFolder.GetRecursiveChildrenInternal(user, includeLinkedChildren))
{
yield return subitem;
}
diff --git a/MediaBrowser.Controller/IO/FileData.cs b/MediaBrowser.Controller/IO/FileData.cs
index c1ca5336b..dc2b49c75 100644
--- a/MediaBrowser.Controller/IO/FileData.cs
+++ b/MediaBrowser.Controller/IO/FileData.cs
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using MoreLinq;
namespace MediaBrowser.Controller.IO
{
@@ -38,7 +39,10 @@ namespace MediaBrowser.Controller.IO
if (!resolveShortcuts && flattenFolderDepth == 0)
{
- return entries.ToDictionary(i => i.FullName, StringComparer.OrdinalIgnoreCase);
+ // Seeing dupes on some users file system for some reason
+ return entries
+ .DistinctBy(i => i.FullName, StringComparer.OrdinalIgnoreCase)
+ .ToDictionary(i => i.FullName, StringComparer.OrdinalIgnoreCase);
}
var dict = new Dictionary<string, FileSystemInfo>(StringComparer.OrdinalIgnoreCase);
diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
index 2761c51e0..ba4f84ac2 100644
--- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
+++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
@@ -173,8 +173,16 @@ namespace MediaBrowser.Controller.Providers
break;
}
case "SortTitle":
- item.ForcedSortName = reader.ReadElementContentAsString();
- break;
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.ForcedSortName = val;
+ }
+
+ break;
+ }
case "Overview":
case "Description":
@@ -503,7 +511,7 @@ namespace MediaBrowser.Controller.Providers
{
DateTime airDate;
- if (DateTime.TryParse(firstAired, out airDate) && airDate.Year > 1850)
+ if (DateTime.TryParseExact(firstAired, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out airDate) && airDate.Year > 1850)
{
item.PremiereDate = airDate.ToUniversalTime();
item.ProductionYear = airDate.Year;
@@ -522,7 +530,7 @@ namespace MediaBrowser.Controller.Providers
{
DateTime airDate;
- if (DateTime.TryParse(firstAired, out airDate) && airDate.Year > 1850)
+ if (DateTime.TryParseExact(firstAired, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out airDate) && airDate.Year > 1850)
{
item.EndDate = airDate.ToUniversalTime();
}
@@ -548,13 +556,23 @@ namespace MediaBrowser.Controller.Providers
break;
case "MusicbrainzId":
- var mbz = reader.ReadElementContentAsString();
- if (!string.IsNullOrWhiteSpace(mbz))
{
- item.SetProviderId(MetadataProviders.Musicbrainz, mbz);
+ var mbz = reader.ReadElementContentAsString();
+ if (!string.IsNullOrWhiteSpace(mbz))
+ {
+ item.SetProviderId(MetadataProviders.Musicbrainz, mbz);
+ }
+ break;
+ }
+ case "MusicBrainzReleaseGroupId":
+ {
+ var mbz = reader.ReadElementContentAsString();
+ if (!string.IsNullOrWhiteSpace(mbz))
+ {
+ item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, mbz);
+ }
+ break;
}
- break;
-
case "RottenTomatoesId":
var rtId = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(rtId))
diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs
index ecb13f83a..f919c2c83 100644
--- a/MediaBrowser.Model/ApiClient/IApiClient.cs
+++ b/MediaBrowser.Model/ApiClient/IApiClient.cs
@@ -437,7 +437,7 @@ namespace MediaBrowser.Model.ApiClient
/// <param name="wasPlayed">if set to <c>true</c> [was played].</param>
/// <returns>Task.</returns>
/// <exception cref="ArgumentNullException">itemId</exception>
- Task UpdatePlayedStatusAsync(string itemId, string userId, bool wasPlayed);
+ Task<UserItemDataDto> UpdatePlayedStatusAsync(string itemId, string userId, bool wasPlayed);
/// <summary>
/// Updates the favorite status async.
@@ -447,7 +447,7 @@ namespace MediaBrowser.Model.ApiClient
/// <param name="isFavorite">if set to <c>true</c> [is favorite].</param>
/// <returns>Task.</returns>
/// <exception cref="ArgumentNullException">itemId</exception>
- Task UpdateFavoriteStatusAsync(string itemId, string userId, bool isFavorite);
+ Task<UserItemDataDto> UpdateFavoriteStatusAsync(string itemId, string userId, bool isFavorite);
/// <summary>
/// Reports to the server that the user has begun playing an item
@@ -518,7 +518,7 @@ namespace MediaBrowser.Model.ApiClient
/// <param name="userId">The user id.</param>
/// <returns>Task{UserItemDataDto}.</returns>
/// <exception cref="ArgumentNullException">itemId</exception>
- Task ClearUserItemRatingAsync(string itemId, string userId);
+ Task<UserItemDataDto> ClearUserItemRatingAsync(string itemId, string userId);
/// <summary>
/// Updates a user's rating for an item, based on likes or dislikes
@@ -528,7 +528,7 @@ namespace MediaBrowser.Model.ApiClient
/// <param name="likes">if set to <c>true</c> [likes].</param>
/// <returns>Task.</returns>
/// <exception cref="ArgumentNullException">itemId</exception>
- Task UpdateUserItemRatingAsync(string itemId, string userId, bool likes);
+ Task<UserItemDataDto> UpdateUserItemRatingAsync(string itemId, string userId, bool likes);
/// <summary>
/// Authenticates a user and returns the result
diff --git a/MediaBrowser.Model/Entities/MetadataProviders.cs b/MediaBrowser.Model/Entities/MetadataProviders.cs
index b089b668c..db784a35f 100644
--- a/MediaBrowser.Model/Entities/MetadataProviders.cs
+++ b/MediaBrowser.Model/Entities/MetadataProviders.cs
@@ -34,6 +34,7 @@ namespace MediaBrowser.Model.Entities
/// <summary>
/// Tmdb Collection Id
/// </summary>
- TmdbCollection
+ TmdbCollection,
+ MusicBrainzReleaseGroup
}
}
diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs
index e80ba2b7d..01f532bae 100644
--- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs
+++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs
@@ -181,7 +181,6 @@ namespace MediaBrowser.Providers.Movies
private const string TmdbConfigUrl = "http://api.themoviedb.org/3/configuration?api_key={0}";
private const string Search3 = @"http://api.themoviedb.org/3/search/movie?api_key={1}&query={0}&language={2}";
- private const string AltTitleSearch = @"http://api.themoviedb.org/3/movie/{0}/alternative_titles?api_key={1}&country={2}";
private const string GetMovieInfo3 = @"http://api.themoviedb.org/3/movie/{0}?api_key={1}&language={2}&append_to_response=casts,releases,images,keywords,trailers";
private const string GetBoxSetInfo3 = @"http://api.themoviedb.org/3/collection/{0}?api_key={1}&language={2}&append_to_response=images";
@@ -248,8 +247,19 @@ namespace MediaBrowser.Providers.Movies
/// <returns>Task.</returns>
private async Task FetchMovieData(BaseItem item, CancellationToken cancellationToken)
{
- string id = item.GetProviderId(MetadataProviders.Tmdb) ?? await FindId(item, item.ProductionYear, cancellationToken).ConfigureAwait(false);
- if (id != null)
+ var id = item.GetProviderId(MetadataProviders.Tmdb);
+
+ if (string.IsNullOrEmpty(id))
+ {
+ id = item.GetProviderId(MetadataProviders.Imdb);
+ }
+
+ if (string.IsNullOrEmpty(id))
+ {
+ id = await FindId(item, cancellationToken).ConfigureAwait(false);
+ }
+
+ if (!string.IsNullOrEmpty(id))
{
Logger.Debug("MovieDbProvider - getting movie info with id: " + id);
@@ -291,19 +301,15 @@ namespace MediaBrowser.Providers.Movies
/// Finds the id.
/// </summary>
/// <param name="item">The item.</param>
- /// <param name="productionYear">The production year.</param>
/// <param name="cancellationToken">The cancellation token</param>
/// <returns>Task{System.String}.</returns>
- public async Task<string> FindId(BaseItem item, int? productionYear, CancellationToken cancellationToken)
+ public async Task<string> FindId(BaseItem item, CancellationToken cancellationToken)
{
- int? year;
+ int? yearInName;
string name = item.Name;
- ParseName(name, out name, out year);
+ ParseName(name, out name, out yearInName);
- if (year == null && productionYear != null)
- {
- year = productionYear;
- }
+ var year = item.ProductionYear ?? yearInName;
Logger.Info("MovieDbProvider: Finding id for movie: " + name);
string language = ConfigurationManager.Configuration.PreferredMetadataLanguage.ToLower();
@@ -392,95 +398,12 @@ namespace MediaBrowser.Providers.Movies
searchResult = JsonSerializer.DeserializeFromStream<TmdbMovieSearchResults>(json);
}
- if (searchResult == null || searchResult.results.Count == 0)
- {
- //try replacing numbers
- foreach (var pair in ReplaceStartNumbers)
- {
- if (name.StartsWith(pair.Key))
- {
- name = name.Remove(0, pair.Key.Length);
- name = pair.Value + name;
- }
- }
- foreach (var pair in ReplaceEndNumbers)
- {
- if (name.EndsWith(pair.Key))
- {
- name = name.Remove(name.IndexOf(pair.Key), pair.Key.Length);
- name = name + pair.Value;
- }
- }
- Logger.Info("MovieDBProvider - No results. Trying replacement numbers: " + name);
- url3 = string.Format(Search3, UrlEncode(name), ApiKey, language);
-
- using (var json = await GetMovieDbResponse(new HttpRequestOptions
- {
- Url = url3,
- CancellationToken = cancellationToken,
- AcceptHeader = AcceptHeader
-
- }).ConfigureAwait(false))
- {
- searchResult = JsonSerializer.DeserializeFromStream<TmdbMovieSearchResults>(json);
- }
- }
if (searchResult != null)
{
- string compName = GetComparableName(name, Logger);
foreach (var possible in searchResult.results)
{
- string matchedName = null;
+ string matchedName = possible.title;
string id = possible.id.ToString(CultureInfo.InvariantCulture);
- string n = possible.title;
- if (GetComparableName(n, Logger) == compName)
- {
- matchedName = n;
- }
- else
- {
- n = possible.original_title;
- if (GetComparableName(n, Logger) == compName)
- {
- matchedName = n;
- }
- }
-
- Logger.Debug("MovieDbProvider - " + compName + " didn't match " + n);
- //if main title matches we don't have to look for alternatives
- if (matchedName == null)
- {
- //that title didn't match - look for alternatives
- url3 = string.Format(AltTitleSearch, id, ApiKey, ConfigurationManager.Configuration.MetadataCountryCode);
-
- using (var json = await GetMovieDbResponse(new HttpRequestOptions
- {
- Url = url3,
- CancellationToken = cancellationToken,
- AcceptHeader = AcceptHeader
-
- }).ConfigureAwait(false))
- {
- var response = JsonSerializer.DeserializeFromStream<TmdbAltTitleResults>(json);
-
- if (response != null && response.titles != null)
- {
- foreach (var title in response.titles)
- {
- var t = GetComparableName(title.title, Logger);
- if (t == compName)
- {
- Logger.Debug("MovieDbProvider - " + compName +
- " matched " + t);
- matchedName = t;
- break;
- }
- Logger.Debug("MovieDbProvider - " + compName +
- " did not match " + t);
- }
- }
- }
- }
if (matchedName != null)
{
@@ -850,126 +773,6 @@ namespace MediaBrowser.Providers.Movies
}
}
- /// <summary>
- /// The remove
- /// </summary>
- const string Remove = "\"'!`?";
- // "Face/Off" support.
- /// <summary>
- /// The spacers
- /// </summary>
- const string Spacers = "/,.:;\\(){}[]+-_=–*"; // (there are not actually two - in the they are different char codes)
- /// <summary>
- /// The replace start numbers
- /// </summary>
- static readonly Dictionary<string, string> ReplaceStartNumbers = new Dictionary<string, string> {
- {"1 ","one "},
- {"2 ","two "},
- {"3 ","three "},
- {"4 ","four "},
- {"5 ","five "},
- {"6 ","six "},
- {"7 ","seven "},
- {"8 ","eight "},
- {"9 ","nine "},
- {"10 ","ten "},
- {"11 ","eleven "},
- {"12 ","twelve "},
- {"13 ","thirteen "},
- {"100 ","one hundred "},
- {"101 ","one hundred one "}
- };
-
- /// <summary>
- /// The replace end numbers
- /// </summary>
- static readonly Dictionary<string, string> ReplaceEndNumbers = new Dictionary<string, string> {
- {" 1"," i"},
- {" 2"," ii"},
- {" 3"," iii"},
- {" 4"," iv"},
- {" 5"," v"},
- {" 6"," vi"},
- {" 7"," vii"},
- {" 8"," viii"},
- {" 9"," ix"},
- {" 10"," x"}
- };
-
- /// <summary>
- /// Gets the name of the comparable.
- /// </summary>
- /// <param name="name">The name.</param>
- /// <param name="logger">The logger.</param>
- /// <returns>System.String.</returns>
- internal static string GetComparableName(string name, ILogger logger)
- {
- name = name.ToLower();
- name = name.Replace("á", "a");
- name = name.Replace("é", "e");
- name = name.Replace("í", "i");
- name = name.Replace("ó", "o");
- name = name.Replace("ú", "u");
- name = name.Replace("ü", "u");
- name = name.Replace("ñ", "n");
- foreach (var pair in ReplaceStartNumbers)
- {
- if (name.StartsWith(pair.Key))
- {
- name = name.Remove(0, pair.Key.Length);
- name = pair.Value + name;
- logger.Info("MovieDbProvider - Replaced Start Numbers: " + name);
- }
- }
- foreach (var pair in ReplaceEndNumbers)
- {
- if (name.EndsWith(pair.Key))
- {
- name = name.Remove(name.IndexOf(pair.Key), pair.Key.Length);
- name = name + pair.Value;
- logger.Info("MovieDbProvider - Replaced End Numbers: " + name);
- }
- }
- name = name.Normalize(NormalizationForm.FormKD);
- var sb = new StringBuilder();
- foreach (var c in name)
- {
- if (c >= 0x2B0 && c <= 0x0333)
- {
- // skip char modifier and diacritics
- }
- else if (Remove.IndexOf(c) > -1)
- {
- // skip chars we are removing
- }
- else if (Spacers.IndexOf(c) > -1)
- {
- sb.Append(" ");
- }
- else if (c == '&')
- {
- sb.Append(" and ");
- }
- else
- {
- sb.Append(c);
- }
- }
- name = sb.ToString();
- name = name.Replace(", the", "");
- name = name.Replace(" the ", " ");
- name = name.Replace("the ", "");
-
- string prev_name;
- do
- {
- prev_name = name;
- name = name.Replace(" ", " ");
- } while (name.Length != prev_name.Length);
-
- return name.Trim();
- }
-
#region Result Objects
diff --git a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
index b02128a37..fa7daaf25 100644
--- a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
+++ b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
@@ -182,10 +182,13 @@ namespace MediaBrowser.Providers.Music
var releaseEntryId = item.GetProviderId(MetadataProviders.Musicbrainz);
+ var musicBrainzReleaseGroupId = album.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
// Fanart uses the release group id so we'll have to get that now using the release entry id
- if (string.IsNullOrEmpty(album.MusicBrainzReleaseGroupId))
+ if (string.IsNullOrEmpty(musicBrainzReleaseGroupId))
{
- album.MusicBrainzReleaseGroupId = await GetReleaseGroupId(releaseEntryId, cancellationToken).ConfigureAwait(false);
+ musicBrainzReleaseGroupId = await GetReleaseGroupId(releaseEntryId, cancellationToken).ConfigureAwait(false);
+
+ album.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, musicBrainzReleaseGroupId);
}
var doc = new XmlDocument();
@@ -199,9 +202,9 @@ namespace MediaBrowser.Providers.Music
// Try try with the release entry Id, if that doesn't produce anything try the release group id
var node = doc.SelectSingleNode("//fanart/music/albums/album[@id=\"" + releaseEntryId + "\"]/cdart/@url");
- if (node == null && !string.IsNullOrEmpty(album.MusicBrainzReleaseGroupId))
+ if (node == null && !string.IsNullOrEmpty(musicBrainzReleaseGroupId))
{
- node = doc.SelectSingleNode("//fanart/music/albums/album[@id=\"" + album.MusicBrainzReleaseGroupId + "\"]/cdart/@url");
+ node = doc.SelectSingleNode("//fanart/music/albums/album[@id=\"" + musicBrainzReleaseGroupId + "\"]/cdart/@url");
}
var path = node != null ? node.Value : null;
@@ -218,9 +221,9 @@ namespace MediaBrowser.Providers.Music
// Try try with the release entry Id, if that doesn't produce anything try the release group id
var node = doc.SelectSingleNode("//fanart/music/albums/album[@id=\"" + releaseEntryId + "\"]/albumcover/@url");
- if (node == null && !string.IsNullOrEmpty(album.MusicBrainzReleaseGroupId))
+ if (node == null && !string.IsNullOrEmpty(musicBrainzReleaseGroupId))
{
- node = doc.SelectSingleNode("//fanart/music/albums/album[@id=\"" + album.MusicBrainzReleaseGroupId + "\"]/albumcover/@url");
+ node = doc.SelectSingleNode("//fanart/music/albums/album[@id=\"" + musicBrainzReleaseGroupId + "\"]/albumcover/@url");
}
var path = node != null ? node.Value : null;
diff --git a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs
index 5c9e90312..edeea611a 100644
--- a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs
+++ b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs
@@ -65,6 +65,7 @@ namespace MediaBrowser.Providers.Savers
"IMDbId",
"TMDbId",
"TVcomId",
+ "TvDbId",
"RottenTomatoesId",
"MusicbrainzId",
"TMDbCollectionId",
@@ -81,7 +82,8 @@ namespace MediaBrowser.Providers.Savers
"BirthDate",
"DeathDate",
"LockedFields",
- "Chapters"
+ "Chapters",
+ "MusicBrainzReleaseGroupId"
});
var position = xml.ToString().LastIndexOf("</", StringComparison.OrdinalIgnoreCase);
@@ -332,6 +334,13 @@ namespace MediaBrowser.Providers.Savers
builder.Append("<MusicbrainzId>" + SecurityElement.Escape(mbz) + "</MusicbrainzId>");
}
+ mbz = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
+
+ if (!string.IsNullOrEmpty(mbz))
+ {
+ builder.Append("<MusicBrainzReleaseGroupId>" + SecurityElement.Escape(mbz) + "</MusicBrainzReleaseGroupId>");
+ }
+
var gamesdb = item.GetProviderId(MetadataProviders.Gamesdb);
if (!string.IsNullOrEmpty(gamesdb))
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/RemoteNotifications.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/RemoteNotifications.cs
index e98a0a2c5..cb6097504 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/RemoteNotifications.cs
+++ b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/RemoteNotifications.cs
@@ -27,7 +27,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
private readonly INotificationsRepository _notificationsRepo;
private readonly IUserManager _userManager;
- private readonly TimeSpan _frequency = TimeSpan.FromHours(3);
+ private readonly TimeSpan _frequency = TimeSpan.FromHours(6);
private readonly TimeSpan _maxAge = TimeSpan.FromDays(31);
public RemoteNotifications(IApplicationPaths appPaths, ILogger logger, IHttpClient httpClient, IJsonSerializer json, INotificationsRepository notificationsRepo, IUserManager userManager)
diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
index d408e082a..4991db8e2 100644
--- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
+++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
@@ -230,8 +230,6 @@
<EmbeddedResource Include="MediaEncoder\readme.txt" />
</ItemGroup>
<ItemGroup>
- <EmbeddedResource Include="MediaEncoder\fonts\ARIALUNI.TTF" />
- <EmbeddedResource Include="MediaEncoder\fonts\fonts.conf" />
<EmbeddedResource Include="MediaEncoder\ffmpeg20130813.zip" />
<None Include="packages.config" />
</ItemGroup>
diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs
index f5b5ea6c0..2ce49aabb 100644
--- a/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs
+++ b/MediaBrowser.Server.Implementations/MediaEncoder/MediaEncoder.cs
@@ -1,6 +1,7 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.MediaInfo;
+using MediaBrowser.Common.Net;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
@@ -47,6 +48,8 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
/// <value>The json serializer.</value>
private readonly IJsonSerializer _jsonSerializer;
+ private readonly IHttpClient _httpClient;
+
/// <summary>
/// The video image resource pool
/// </summary>
@@ -81,12 +84,13 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
/// <param name="appPaths">The app paths.</param>
/// <param name="jsonSerializer">The json serializer.</param>
public MediaEncoder(ILogger logger, IZipClient zipClient, IApplicationPaths appPaths,
- IJsonSerializer jsonSerializer)
+ IJsonSerializer jsonSerializer, IHttpClient httpClient)
{
_logger = logger;
_zipClient = zipClient;
_appPaths = appPaths;
_jsonSerializer = jsonSerializer;
+ _httpClient = httpClient;
// Not crazy about this but it's the only way to suppress ffmpeg crash dialog boxes
SetErrorMode(ErrorModes.SEM_FAILCRITICALERRORS | ErrorModes.SEM_NOALIGNMENTFAULTEXCEPT |
@@ -194,22 +198,30 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
/// <param name="assembly">The assembly.</param>
/// <param name="zipFileResourcePath">The zip file resource path.</param>
/// <param name="targetPath">The target path.</param>
- private void ExtractTools(Assembly assembly, string zipFileResourcePath, string targetPath)
+ private async void ExtractTools(Assembly assembly, string zipFileResourcePath, string targetPath)
{
using (var resourceStream = assembly.GetManifestResourceStream(zipFileResourcePath))
{
_zipClient.ExtractAll(resourceStream, targetPath, false);
}
- ExtractFonts(assembly, targetPath);
+ try
+ {
+ await DownloadFonts(targetPath).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error getting ffmpeg font files", ex);
+ }
}
+ private const string FontUrl = "https://www.dropbox.com/s/9nb76tybcsw5xrk/ARIALUNI.zip?dl=1";
+
/// <summary>
/// Extracts the fonts.
/// </summary>
- /// <param name="assembly">The assembly.</param>
/// <param name="targetPath">The target path.</param>
- private async void ExtractFonts(Assembly assembly, string targetPath)
+ private async Task DownloadFonts(string targetPath)
{
var fontsDirectory = Path.Combine(targetPath, "fonts");
@@ -224,54 +236,44 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
if (!File.Exists(fontFile))
{
- using (var stream = assembly.GetManifestResourceStream(GetType().Namespace + ".fonts." + fontFilename))
+ var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
{
- using (
- var fileStream = new FileStream(fontFile, FileMode.Create, FileAccess.Write, FileShare.Read,
- StreamDefaults.DefaultFileStreamBufferSize,
- FileOptions.Asynchronous))
- {
- await stream.CopyToAsync(fileStream).ConfigureAwait(false);
- }
+ Url = FontUrl,
+ Progress = new Progress<double>()
+ });
+
+ _zipClient.ExtractAll(tempFile, fontsDirectory, true);
+
+ try
+ {
+ File.Delete(tempFile);
+ }
+ catch (IOException ex)
+ {
+ // Log this, but don't let it fail the operation
+ _logger.ErrorException("Error deleting temp file {0}", ex, tempFile);
}
}
- await ExtractFontConfigFile(assembly, fontsDirectory).ConfigureAwait(false);
+ await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false);
}
- /// <summary>
- /// Extracts the font config file.
- /// </summary>
- /// <param name="assembly">The assembly.</param>
- /// <param name="fontsDirectory">The fonts directory.</param>
- /// <returns>Task.</returns>
- private async Task ExtractFontConfigFile(Assembly assembly, string fontsDirectory)
+ private async Task WriteFontConfigFile(string fontsDirectory)
{
const string fontConfigFilename = "fonts.conf";
var fontConfigFile = Path.Combine(fontsDirectory, fontConfigFilename);
if (!File.Exists(fontConfigFile))
{
- using (
- var stream = assembly.GetManifestResourceStream(GetType().Namespace + ".fonts." + fontConfigFilename)
- )
- {
- using (var streamReader = new StreamReader(stream))
- {
- var contents = await streamReader.ReadToEndAsync().ConfigureAwait(false);
+ var contents = string.Format("<?xml version=\"1.0\"?><fontconfig><dir>{0}</dir><alias><family>Arial</family><prefer>Arial Unicode MS</prefer></alias></fontconfig>", fontsDirectory);
- contents = contents.Replace("<dir></dir>", "<dir>" + fontsDirectory + "</dir>");
+ var bytes = Encoding.UTF8.GetBytes(contents);
- var bytes = Encoding.UTF8.GetBytes(contents);
-
- using (
- var fileStream = new FileStream(fontConfigFile, FileMode.Create, FileAccess.Write,
- FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize,
- FileOptions.Asynchronous))
- {
- await fileStream.WriteAsync(bytes, 0, bytes.Length);
- }
- }
+ using (var fileStream = new FileStream(fontConfigFile, FileMode.Create, FileAccess.Write,
+ FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize,
+ FileOptions.Asynchronous))
+ {
+ await fileStream.WriteAsync(bytes, 0, bytes.Length);
}
}
}
diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/fonts/fonts.conf b/MediaBrowser.Server.Implementations/MediaEncoder/fonts/fonts.conf
deleted file mode 100644
index 648bdb7b2..000000000
--- a/MediaBrowser.Server.Implementations/MediaEncoder/fonts/fonts.conf
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0"?>
-<fontconfig>
-
-<dir></dir>
- <alias>
- <family>Arial</family>
- <prefer>Arial Unicode MS</prefer>
- </alias>
-</fontconfig> \ No newline at end of file
diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs
index 099b9a3df..eb8a73049 100644
--- a/MediaBrowser.ServerApplication/ApplicationHost.cs
+++ b/MediaBrowser.ServerApplication/ApplicationHost.cs
@@ -272,7 +272,7 @@ namespace MediaBrowser.ServerApplication
RegisterSingleInstance<ILibrarySearchEngine>(() => new LuceneSearchEngine(ApplicationPaths, LogManager, LibraryManager));
- MediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), ZipClient, ApplicationPaths, JsonSerializer);
+ MediaEncoder = new MediaEncoder(LogManager.GetLogger("MediaEncoder"), ZipClient, ApplicationPaths, JsonSerializer, HttpClient);
RegisterSingleInstance(MediaEncoder);
var clientConnectionManager = new SessionManager(UserDataRepository, ServerConfigurationManager, Logger, UserRepository);
diff --git a/MediaBrowser.WebDashboard/ApiClient.js b/MediaBrowser.WebDashboard/ApiClient.js
index a0d4b399d..8a7db8b76 100644
--- a/MediaBrowser.WebDashboard/ApiClient.js
+++ b/MediaBrowser.WebDashboard/ApiClient.js
@@ -2695,7 +2695,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: method,
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -2721,7 +2722,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: method,
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -2747,7 +2749,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: "POST",
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -2773,7 +2776,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: method,
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -2793,7 +2797,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: method,
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -2813,7 +2818,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: method,
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -2833,7 +2839,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: method,
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -2853,7 +2860,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: method,
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -2873,7 +2881,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: method,
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -2899,7 +2908,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: "POST",
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -2919,7 +2929,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: "POST",
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -2939,7 +2950,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: "POST",
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -2959,7 +2971,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: "POST",
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -2979,7 +2992,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: "POST",
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -2999,7 +3013,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: "POST",
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -3022,7 +3037,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: "DELETE",
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -3040,7 +3056,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: "DELETE",
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -3058,7 +3075,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: "DELETE",
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -3076,7 +3094,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: "DELETE",
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -3094,7 +3113,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: "DELETE",
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -3112,7 +3132,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: "DELETE",
- url: url
+ url: url,
+ dataType: "json"
});
};
@@ -3290,7 +3311,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
return self.ajax({
type: "DELETE",
- url: url
+ url: url,
+ dataType: "json"
});
};
diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config
index 517b8c1d2..11c76e205 100644
--- a/MediaBrowser.WebDashboard/packages.config
+++ b/MediaBrowser.WebDashboard/packages.config
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="MediaBrowser.ApiClient.Javascript" version="3.0.160" targetFramework="net45" />
+ <package id="MediaBrowser.ApiClient.Javascript" version="3.0.164" targetFramework="net45" />
<package id="ServiceStack.Common" version="3.9.58" targetFramework="net45" />
<package id="ServiceStack.Text" version="3.9.58" targetFramework="net45" />
</packages> \ No newline at end of file
diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec
index f1c8251b2..8bfd28da1 100644
--- a/Nuget/MediaBrowser.Common.Internal.nuspec
+++ b/Nuget/MediaBrowser.Common.Internal.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common.Internal</id>
- <version>3.0.183</version>
+ <version>3.0.184</version>
<title>MediaBrowser.Common.Internal</title>
<authors>Luke</authors>
<owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
<description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.183" />
+ <dependency id="MediaBrowser.Common" version="3.0.184" />
<dependency id="NLog" version="2.0.1.2" />
<dependency id="ServiceStack.Text" version="3.9.55" />
<dependency id="SimpleInjector" version="2.3.0" />
diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec
index 0f334c9c1..2ec4ebd58 100644
--- a/Nuget/MediaBrowser.Common.nuspec
+++ b/Nuget/MediaBrowser.Common.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common</id>
- <version>3.0.183</version>
+ <version>3.0.184</version>
<title>MediaBrowser.Common</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>
diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec
index bcf772e8f..14a401282 100644
--- a/Nuget/MediaBrowser.Server.Core.nuspec
+++ b/Nuget/MediaBrowser.Server.Core.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Server.Core</id>
- <version>3.0.183</version>
+ <version>3.0.184</version>
<title>Media Browser.Server.Core</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Media Browser Server.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.183" />
+ <dependency id="MediaBrowser.Common" version="3.0.184" />
</dependencies>
</metadata>
<files>
diff --git a/README.md b/README.md
index 130151f20..532c8c5ad 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
Media Browser
============
-Media Browser Server is a media cataloging and transcoding server built on top of other popular open source technologies such as **Service Stack**, **jQuery** and **jQuery mobile**.
+Media Browser Server is a home media server built on top of other popular open source technologies such as **Service Stack**, **jQuery**, **jQuery mobile** and **Lucene .NET**.
It features a REST-based api with built-in documention to facilitate plugin development.