Merge pull request #1082 from MediaBrowser/dev

3.0.5582.2
This commit is contained in:
Luke 2015-04-15 23:29:34 -04:00
commit 73688e3fcc
26 changed files with 415 additions and 329 deletions

View file

@ -34,7 +34,7 @@
<ItemGroup>
<Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ImageMagickSharp.1.0.0.14\lib\net45\ImageMagickSharp.dll</HintPath>
<HintPath>..\packages\ImageMagickSharp.1.0.0.15\lib\net45\ImageMagickSharp.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
@ -65,9 +65,6 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ImageMagick\UnplayedCountIndicator.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="ImageMagick\fonts\MontserratLight.otf" />
<EmbeddedResource Include="ImageMagick\fonts\robotoregular.ttf" />
@ -87,6 +84,9 @@
<Name>MediaBrowser.Model</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ImageMagickSharp" version="1.0.0.14" targetFramework="net45" />
<package id="ImageMagickSharp" version="1.0.0.15" targetFramework="net45" />
</packages>

View file

@ -156,6 +156,7 @@
<Compile Include="UserLibrary\PlaystateService.cs" />
<Compile Include="UserLibrary\StudiosService.cs" />
<Compile Include="UserLibrary\UserLibraryService.cs" />
<Compile Include="UserLibrary\UserViewsService.cs" />
<Compile Include="UserLibrary\YearsService.cs" />
<Compile Include="UserService.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />

View file

@ -38,20 +38,6 @@ namespace MediaBrowser.Api.UserLibrary
public string Id { get; set; }
}
[Route("/Users/{UserId}/Views", "GET")]
public class GetUserViews : IReturn<QueryResult<BaseItemDto>>
{
/// <summary>
/// Gets or sets the user id.
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string UserId { get; set; }
[ApiMember(Name = "IncludeExternalContent", Description = "Whether or not to include external views such as channels or live tv", IsRequired = true, DataType = "boolean", ParameterType = "query", Verb = "POST")]
public bool? IncludeExternalContent { get; set; }
}
/// <summary>
/// Class GetItem
/// </summary>
@ -345,37 +331,6 @@ namespace MediaBrowser.Api.UserLibrary
return ToOptimizedResult(dtos.ToList());
}
public async Task<object> Get(GetUserViews request)
{
var user = _userManager.GetUserById(request.UserId);
var query = new UserViewQuery
{
UserId = request.UserId
};
if (request.IncludeExternalContent.HasValue)
{
query.IncludeExternalContent = request.IncludeExternalContent.Value;
}
var folders = await _userViewManager.GetUserViews(query, CancellationToken.None).ConfigureAwait(false);
var dtoOptions = GetDtoOptions(request);
var dtos = folders.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user))
.ToArray();
var result = new QueryResult<BaseItemDto>
{
Items = dtos,
TotalRecordCount = dtos.Length
};
return ToOptimizedResult(result);
}
private List<BaseItemDto> GetAsync(GetSpecialFeatures request)
{
var user = _userManager.GetUserById(request.UserId);

View file

@ -0,0 +1,121 @@
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying;
using ServiceStack;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Api.UserLibrary
{
[Route("/Users/{UserId}/Views", "GET")]
public class GetUserViews : IReturn<QueryResult<BaseItemDto>>
{
/// <summary>
/// Gets or sets the user id.
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string UserId { get; set; }
[ApiMember(Name = "IncludeExternalContent", Description = "Whether or not to include external views such as channels or live tv", IsRequired = true, DataType = "boolean", ParameterType = "query", Verb = "POST")]
public bool? IncludeExternalContent { get; set; }
}
[Route("/Users/{UserId}/SpecialViewOptions", "GET")]
public class GetSpecialViewOptions : IReturn<List<SpecialViewOption>>
{
/// <summary>
/// Gets or sets the user id.
/// </summary>
/// <value>The user id.</value>
[ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string UserId { get; set; }
}
public class UserViewsService : BaseApiService
{
private readonly IUserManager _userManager;
private readonly IUserViewManager _userViewManager;
private readonly IDtoService _dtoService;
public UserViewsService(IUserManager userManager, IUserViewManager userViewManager, IDtoService dtoService)
{
_userManager = userManager;
_userViewManager = userViewManager;
_dtoService = dtoService;
}
public async Task<object> Get(GetUserViews request)
{
var query = new UserViewQuery
{
UserId = request.UserId
};
if (request.IncludeExternalContent.HasValue)
{
query.IncludeExternalContent = request.IncludeExternalContent.Value;
}
var folders = await _userViewManager.GetUserViews(query, CancellationToken.None).ConfigureAwait(false);
var dtoOptions = GetDtoOptions(request);
var user = _userManager.GetUserById(request.UserId);
var dtos = folders.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user))
.ToArray();
var result = new QueryResult<BaseItemDto>
{
Items = dtos,
TotalRecordCount = dtos.Length
};
return ToOptimizedResult(result);
}
public async Task<object> Get(GetSpecialViewOptions request)
{
var user = _userManager.GetUserById(request.UserId);
var views = user.RootFolder
.GetChildren(user, true)
.OfType<CollectionFolder>()
.Where(i => IsEligibleForSpecialView(i))
.ToList();
var list = views
.Select(i => new SpecialViewOption
{
Name = i.Name,
Id = i.Id.ToString("N")
})
.OrderBy(i => i.Name)
.ToList();
return ToOptimizedResult(list);
}
private bool IsEligibleForSpecialView(CollectionFolder view)
{
var types = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Games, CollectionType.Music };
return types.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
}
}
class SpecialViewOption
{
public string Name { get; set; }
public string Id { get; set; }
}
}

View file

@ -1894,12 +1894,12 @@ namespace MediaBrowser.Controller.Entities
return video.RefreshMetadata(newOptions, cancellationToken);
}
public string GetEtag()
public string GetEtag(User user)
{
return string.Join("|", GetEtagValues().ToArray()).GetMD5().ToString("N");
return string.Join("|", GetEtagValues(user).ToArray()).GetMD5().ToString("N");
}
protected virtual List<string> GetEtagValues()
protected virtual List<string> GetEtagValues(User user)
{
return new List<string>
{

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
{
@ -9,6 +10,8 @@ namespace MediaBrowser.Controller.Entities
{
string CollectionType { get; }
string Path { get; }
string Name { get; }
Guid Id { get; }
IEnumerable<string> PhysicalLocations { get; }
}
}

View file

@ -247,7 +247,16 @@ namespace MediaBrowser.Controller.Entities
return GetFavoriteSongs(queryParent, user, query);
default:
return GetResult(GetMediaFolders(user).SelectMany(i => i.GetChildren(user, true)), queryParent, query);
{
if (queryParent is UserView)
{
return GetResult(GetMediaFolders(user).SelectMany(i => i.GetChildren(user, true)), queryParent, query);
}
else
{
return GetResult(queryParent.GetChildren(user, true), queryParent, query);
}
}
}
}

View file

@ -2,40 +2,78 @@
using MoreLinq;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace MediaBrowser.Controller.Library
{
public static class NameExtensions
{
public static bool AreEqual(string name1, string name2)
public static bool AreEqual(string x, string y)
{
name1 = NormalizeForComparison(name1);
name2 = NormalizeForComparison(name2);
if (string.IsNullOrWhiteSpace(x) && string.IsNullOrWhiteSpace(y))
{
return true;
}
return string.Equals(name1, name2, StringComparison.OrdinalIgnoreCase);
return string.Compare(x, y, CultureInfo.InvariantCulture, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) == 0;
}
public static bool EqualsAny(IEnumerable<string> names, string name)
public static bool EqualsAny(IEnumerable<string> names, string x)
{
name = NormalizeForComparison(name);
x = NormalizeForComparison(x);
return names.Any(i => string.Equals(NormalizeForComparison(i), name, StringComparison.OrdinalIgnoreCase));
return names.Any(y => string.Compare(x, y, CultureInfo.InvariantCulture, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) == 0);
}
private static string NormalizeForComparison(string name)
{
if (string.IsNullOrWhiteSpace(name))
if (name == null)
{
return string.Empty;
}
return name;
//return name.RemoveDiacritics();
}
private static string RemoveDiacritics(string name)
{
if (name == null)
{
return string.Empty;
}
//return name;
return name.RemoveDiacritics();
}
public static IEnumerable<string> DistinctNames(this IEnumerable<string> names)
{
return names.DistinctBy(NormalizeForComparison, StringComparer.OrdinalIgnoreCase);
return names.DistinctBy(RemoveDiacritics, StringComparer.OrdinalIgnoreCase);
}
}
class TextComparer : IComparer<string>, IEqualityComparer<string>
{
public int Compare(string x, string y)
{
if (string.IsNullOrWhiteSpace(x) && string.IsNullOrWhiteSpace(y))
{
return 0;
}
return string.Compare(x, y, CultureInfo.InvariantCulture, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace);
}
public bool Equals(string x, string y)
{
return Compare(x, y) == 0;
}
public int GetHashCode(string obj)
{
return (obj ?? string.Empty).GetHashCode();
}
}
}

View file

@ -107,7 +107,7 @@ namespace MediaBrowser.Controller.Persistence
/// </summary>
/// <param name="type">The type.</param>
/// <returns>IEnumerable{Guid}.</returns>
IEnumerable<Guid> GetItemsOfType(Type type);
IEnumerable<Guid> GetItemIdsOfType(Type type);
/// <summary>
/// Saves the children.
@ -133,6 +133,13 @@ namespace MediaBrowser.Controller.Persistence
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SaveMediaStreams(Guid id, IEnumerable<MediaStream> streams, CancellationToken cancellationToken);
/// <summary>
/// Gets the type of the items of.
/// </summary>
/// <param name="type">The type.</param>
/// <returns>IEnumerable&lt;BaseItem&gt;.</returns>
IEnumerable<BaseItem> GetItemsOfType(Type type);
}
}

View file

@ -489,32 +489,37 @@ namespace MediaBrowser.MediaEncoding.Encoder
await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
var processWrapper = new ProcessWrapper(process, this);
StartProcess(processWrapper);
bool ranToCompletion;
var memoryStream = new MemoryStream();
try
{
StartProcess(processWrapper);
#pragma warning disable 4014
// Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
process.StandardOutput.BaseStream.CopyToAsync(memoryStream);
// Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
process.StandardOutput.BaseStream.CopyToAsync(memoryStream);
#pragma warning restore 4014
// MUST read both stdout and stderr asynchronously or a deadlock may occurr
process.BeginErrorReadLine();
// MUST read both stdout and stderr asynchronously or a deadlock may occurr
process.BeginErrorReadLine();
var ranToCompletion = process.WaitForExit(10000);
ranToCompletion = process.WaitForExit(10000);
if (!ranToCompletion)
if (!ranToCompletion)
{
StopProcess(processWrapper, 1000, false);
}
}
finally
{
StopProcess(processWrapper, 1000, false);
resourcePool.Release();
}
resourcePool.Release();
var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
process.Dispose();
if (exitCode == -1 || memoryStream.Length == 0)
{
memoryStream.Dispose();
@ -594,7 +599,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
await resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
bool ranToCompletion;
bool ranToCompletion = false;
var processWrapper = new ProcessWrapper(process, this);
@ -609,19 +614,23 @@ namespace MediaBrowser.MediaEncoding.Encoder
bool isResponsive = true;
int lastCount = 0;
while (isResponsive && !process.WaitForExit(30000))
while (isResponsive)
{
if (process.WaitForExit(30000))
{
ranToCompletion = true;
break;
}
cancellationToken.ThrowIfCancellationRequested();
int jpegCount = Directory.GetFiles(targetDirectory)
var jpegCount = Directory.GetFiles(targetDirectory)
.Count(i => string.Equals(Path.GetExtension(i), ".jpg", StringComparison.OrdinalIgnoreCase));
isResponsive = (jpegCount > lastCount);
lastCount = jpegCount;
}
ranToCompletion = process.HasExited;
if (!ranToCompletion)
{
StopProcess(processWrapper, 1000, false);
@ -634,8 +643,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
process.Dispose();
if (exitCode == -1)
{
var msg = string.Format("ffmpeg image extraction failed for {0}", inputArgument);

View file

@ -49,7 +49,7 @@ namespace MediaBrowser.Model.Configuration
/// </summary>
/// <value><c>true</c> if [enable user specific user views]; otherwise, <c>false</c>.</value>
public bool EnableUserSpecificUserViews { get; set; }
/// <summary>
/// Gets or sets the value pointing to the file system where the ssl certiifcate is located..
/// </summary>

View file

@ -46,6 +46,7 @@ namespace MediaBrowser.Model.Configuration
public bool EnableCinemaMode { get; set; }
public string[] LatestItemsExcludes { get; set; }
public string[] PlainFolderViews { get; set; }
public bool HidePlayedInLatest { get; set; }
@ -62,6 +63,7 @@ namespace MediaBrowser.Model.Configuration
DisplayChannelsWithinViews = new string[] { };
ExcludeFoldersFromGrouping = new string[] { };
PlainFolderViews = new string[] { };
DisplayCollectionsView = true;
IncludeTrailersInSuggestions = true;

View file

@ -1,153 +0,0 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using MediaBrowser.Providers.Genres;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Providers.FolderImages
{
public class DefaultImageProvider : IRemoteImageProvider, IHasItemChangeMonitor, IHasOrder
{
private readonly IHttpClient _httpClient;
public DefaultImageProvider(IHttpClient httpClient)
{
_httpClient = httpClient;
}
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{
return new List<ImageType>
{
ImageType.Primary,
ImageType.Thumb
};
}
public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
var view = item as UserView;
if (view != null)
{
return GetImages(view.ViewType, view.ParentId != Guid.Empty, cancellationToken);
}
var folder = (ICollectionFolder)item;
return GetImages(folder.CollectionType, false, cancellationToken);
}
private Task<IEnumerable<RemoteImageInfo>> GetImages(string viewType, bool isSubView, CancellationToken cancellationToken)
{
var url = GetImageUrl(viewType, isSubView);
var list = new List<RemoteImageInfo>();
if (!string.IsNullOrWhiteSpace(url))
{
list.AddRange(new List<RemoteImageInfo>
{
new RemoteImageInfo
{
ProviderName = Name,
Url = url,
Type = ImageType.Primary
},
new RemoteImageInfo
{
ProviderName = Name,
Url = url,
Type = ImageType.Thumb
}
});
}
return Task.FromResult<IEnumerable<RemoteImageInfo>>(list);
}
private string GetImageUrl(string viewType, bool isSubView)
{
const string urlPrefix = "https://raw.githubusercontent.com/MediaBrowser/MediaBrowser.Resources/master/images/folders/";
if (string.Equals(viewType, CollectionType.Books, StringComparison.OrdinalIgnoreCase))
{
return null;
}
if (string.Equals(viewType, CollectionType.Games, StringComparison.OrdinalIgnoreCase))
{
return null;
}
if (string.Equals(viewType, CollectionType.Music, StringComparison.OrdinalIgnoreCase))
{
return null;
}
if (string.Equals(viewType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase))
{
return null;
}
if (string.Equals(viewType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
{
return null;
}
if (string.Equals(viewType, CollectionType.Channels, StringComparison.OrdinalIgnoreCase))
{
return null;
}
if (string.Equals(viewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
{
return null;
}
if (string.Equals(viewType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
{
return null;
}
if (isSubView)
{
return null;
}
return urlPrefix + "generic.png";
}
public string Name
{
get { return "Default Image Provider"; }
}
public bool Supports(IHasImages item)
{
return item is ICollectionFolder || item is UserView;
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = url,
ResourcePool = GenreImageProvider.ImageDownloadResourcePool
});
}
public bool HasChanged(IHasMetadata item, MetadataStatus status, IDirectoryService directoryService)
{
return GetSupportedImages(item).Any(i => !item.HasImage(i));
}
public int Order
{
get
{
// Run after the dynamic image provider
return 1;
}
}
}
}

View file

@ -83,7 +83,6 @@
<Compile Include="BoxSets\MovieDbBoxSetProvider.cs" />
<Compile Include="Channels\ChannelMetadataService.cs" />
<Compile Include="Chapters\ChapterManager.cs" />
<Compile Include="FolderImages\DefaultImageProvider.cs" />
<Compile Include="Folders\FolderMetadataService.cs" />
<Compile Include="Channels\AudioChannelItemMetadataService.cs" />
<Compile Include="Folders\UserViewMetadataService.cs" />

View file

@ -6,6 +6,7 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
using System;
using System.Collections.Generic;
@ -20,13 +21,15 @@ namespace MediaBrowser.Providers.MediaInfo
private readonly IMediaEncoder _mediaEncoder;
private readonly IServerConfigurationManager _config;
private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger;
public VideoImageProvider(IIsoManager isoManager, IMediaEncoder mediaEncoder, IServerConfigurationManager config, ILibraryManager libraryManager)
public VideoImageProvider(IIsoManager isoManager, IMediaEncoder mediaEncoder, IServerConfigurationManager config, ILibraryManager libraryManager, ILogger logger)
{
_isoManager = isoManager;
_mediaEncoder = mediaEncoder;
_config = config;
_libraryManager = libraryManager;
_logger = logger;
}
/// <summary>
@ -74,6 +77,7 @@ namespace MediaBrowser.Providers.MediaInfo
// Can't extract if we didn't find a video stream in the file
if (!video.DefaultVideoStreamIndex.HasValue)
{
_logger.Debug("Skipping image extraction due to missing DefaultVideoStreamIndex for {0}.", video.Path ?? string.Empty);
return Task.FromResult(new DynamicImageResponse { HasImage = false });
}

View file

@ -357,7 +357,10 @@ namespace MediaBrowser.Server.Implementations.Dto
: item.CanDownload(user);
}
if (fields.Contains(ItemFields.Etag))
{
dto.Etag = item.GetEtag(user);
}
return dto;
}

View file

@ -1678,11 +1678,6 @@ namespace MediaBrowser.Server.Implementations.Library
throw new ArgumentNullException("name");
}
if (string.IsNullOrWhiteSpace(viewType))
{
throw new ArgumentNullException("viewType");
}
var id = GetNewItemId("37_namedview_" + name + user.Id.ToString("N") + (parentId ?? string.Empty), typeof(UserView));
var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N"));
@ -1716,6 +1711,12 @@ namespace MediaBrowser.Server.Implementations.Library
isNew = true;
}
if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase))
{
item.ViewType = viewType;
await item.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
}
var refresh = isNew || (DateTime.UtcNow - item.DateLastSaved).TotalHours >= 12;
if (refresh)

View file

@ -53,6 +53,7 @@ namespace MediaBrowser.Server.Implementations.Library
.ToList();
var excludeFolderIds = user.Configuration.ExcludeFoldersFromGrouping.Select(i => new Guid(i)).ToList();
var plainFolderIds = user.Configuration.PlainFolderViews.Select(i => new Guid(i)).ToList();
var standaloneFolders = folders
.Where(i => UserView.IsExcludedFromGrouping(i) || excludeFolderIds.Contains(i.Id))
@ -72,13 +73,17 @@ namespace MediaBrowser.Server.Implementations.Library
var collectionFolder = folder as ICollectionFolder;
var folderViewType = collectionFolder == null ? null : collectionFolder.CollectionType;
if (string.IsNullOrWhiteSpace(folderViewType))
if (plainFolderIds.Contains(folder.Id))
{
list.Add(folder);
list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, false, string.Empty, user, cancellationToken).ConfigureAwait(false));
}
else if (!string.IsNullOrWhiteSpace(folderViewType))
{
list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, true, string.Empty, user, cancellationToken).ConfigureAwait(false));
}
else
{
list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, string.Empty, user, cancellationToken).ConfigureAwait(false));
list.Add(folder);
}
}
}
@ -87,44 +92,57 @@ namespace MediaBrowser.Server.Implementations.Library
list.AddRange(standaloneFolders);
}
if (foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase)) ||
foldersWithViewTypes.Any(i => string.IsNullOrWhiteSpace(i.CollectionType)))
var parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.CollectionType))
.ToList();
if (parents.Count > 0)
{
list.Add(await GetUserView(CollectionType.TvShows, string.Empty, user, cancellationToken).ConfigureAwait(false));
list.Add(await GetUserView(parents, CollectionType.TvShows, string.Empty, user, cancellationToken).ConfigureAwait(false));
}
if (foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase)) ||
foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase)))
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase) || string.Equals(i.CollectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.CollectionType))
.ToList();
if (parents.Count > 0)
{
list.Add(await GetUserView(CollectionType.Music, string.Empty, user, cancellationToken).ConfigureAwait(false));
list.Add(await GetUserView(parents, CollectionType.Music, string.Empty, user, cancellationToken).ConfigureAwait(false));
}
if (foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase)) ||
foldersWithViewTypes.Any(i => string.IsNullOrWhiteSpace(i.CollectionType)))
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.CollectionType))
.ToList();
if (parents.Count > 0)
{
list.Add(await GetUserView(CollectionType.Movies, string.Empty, user, cancellationToken).ConfigureAwait(false));
list.Add(await GetUserView(parents, CollectionType.Movies, string.Empty, user, cancellationToken).ConfigureAwait(false));
}
if (foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.Games, StringComparison.OrdinalIgnoreCase)))
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.Games, StringComparison.OrdinalIgnoreCase))
.ToList();
if (parents.Count > 0)
{
list.Add(await GetUserView(CollectionType.Games, string.Empty, user, cancellationToken).ConfigureAwait(false));
list.Add(await GetUserView(parents, CollectionType.Games, string.Empty, user, cancellationToken).ConfigureAwait(false));
}
if (foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase)))
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase))
.ToList();
if (parents.Count > 0)
{
//list.Add(_collectionManager.GetCollectionsFolder(user.Id.ToString("N")));
list.Add(await GetUserView(CollectionType.BoxSets, string.Empty, user, cancellationToken).ConfigureAwait(false));
list.Add(await GetUserView(parents, CollectionType.BoxSets, string.Empty, user, cancellationToken).ConfigureAwait(false));
}
if (foldersWithViewTypes.Any(i => string.Equals(i.CollectionType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase)))
parents = foldersWithViewTypes.Where(i => string.Equals(i.CollectionType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
.ToList();
if (parents.Count > 0)
{
//list.Add(_playlists.GetPlaylistsFolder(user.Id.ToString("N")));
list.Add(await GetUserView(CollectionType.Playlists, string.Empty, user, cancellationToken).ConfigureAwait(false));
list.Add(await GetUserView(parents, CollectionType.Playlists, string.Empty, user, cancellationToken).ConfigureAwait(false));
}
if (user.Configuration.DisplayFoldersView)
{
list.Add(await GetUserView(CollectionType.Folders, "zz_" + CollectionType.Folders, user, cancellationToken).ConfigureAwait(false));
list.Add(await GetUserView(new List<ICollectionFolder>(), CollectionType.Folders, "zz_" + CollectionType.Folders, user, cancellationToken).ConfigureAwait(false));
}
if (query.IncludeExternalContent)
@ -151,7 +169,7 @@ namespace MediaBrowser.Server.Implementations.Library
if (_liveTvManager.GetEnabledUsers().Select(i => i.Id.ToString("N")).Contains(query.UserId))
{
//list.Add(await _liveTvManager.GetInternalLiveTvFolder(query.UserId, cancellationToken).ConfigureAwait(false));
list.Add(await GetUserView(CollectionType.LiveTv, string.Empty, user, cancellationToken).ConfigureAwait(false));
list.Add(await GetUserView(new List<ICollectionFolder>(), CollectionType.LiveTv, string.Empty, user, cancellationToken).ConfigureAwait(false));
}
}
@ -182,16 +200,42 @@ namespace MediaBrowser.Server.Implementations.Library
return GetUserSubView(name, parentId, type, user, sortName, cancellationToken);
}
public Task<UserView> GetUserView(string type, string sortName, User user, CancellationToken cancellationToken)
public async Task<UserView> GetUserView(List<ICollectionFolder> parents, string viewType, string sortName, User user, CancellationToken cancellationToken)
{
var name = _localizationManager.GetLocalizedString("ViewType" + type);
if (parents.Count == 1 && parents.All(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase)))
{
var name = parents[0].Name;
var parentId = parents[0].Id;
return _libraryManager.GetNamedView(user, name, type, sortName, cancellationToken);
var enableRichView = !user.Configuration.PlainFolderViews.Contains(parentId.ToString("N"), StringComparer.OrdinalIgnoreCase);
if (_config.Configuration.EnableUserSpecificUserViews)
{
viewType = enableRichView ? viewType : null;
var view = await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false);
if (view.ParentId != parentId)
{
view.ParentId = parentId;
await view.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
}
return view;
}
viewType = enableRichView ? viewType : CollectionType.Folders;
return await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false);
}
else
{
var name = _localizationManager.GetLocalizedString("ViewType" + viewType);
return await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false);
}
}
public Task<UserView> GetUserView(Guid parentId, string name, string type, string sortName, User user, CancellationToken cancellationToken)
public Task<UserView> GetUserView(Guid parentId, string name, string viewType, bool enableRichView, string sortName, User user, CancellationToken cancellationToken)
{
return _libraryManager.GetNamedView(user, name, parentId.ToString("N"), type, sortName, cancellationToken);
return _libraryManager.GetNamedView(user, name, parentId.ToString("N"), viewType, sortName, cancellationToken);
}
public List<Tuple<BaseItem, List<BaseItem>>> GetLatestItems(LatestItemsQuery request)
@ -317,7 +361,7 @@ namespace MediaBrowser.Server.Implementations.Library
.RootFolder
.GetRecursiveChildren(filter);
}
private IEnumerable<BaseItem> GetItemsConfiguredForLatest(User user, Func<BaseItem, bool> filter)
{
// Avoid implicitly captured closure

View file

@ -54,11 +54,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
new ConcurrentDictionary<string, LiveStreamData>();
private List<Guid> _channelIdList = new List<Guid>();
private Dictionary<Guid, LiveTvProgram> _programs = new Dictionary<Guid, LiveTvProgram>();
private Dictionary<Guid, LiveTvProgram> _programs;
private readonly ConcurrentDictionary<Guid, bool> _refreshedPrograms = new ConcurrentDictionary<Guid, bool>();
private readonly SemaphoreSlim _refreshSemaphore = new SemaphoreSlim(1, 1);
public LiveTvManager(IApplicationHost appHost, IServerConfigurationManager config, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, ITaskManager taskManager, ILocalizationManager localization, IJsonSerializer jsonSerializer, IProviderManager providerManager)
{
_config = config;
@ -109,6 +107,37 @@ namespace MediaBrowser.Server.Implementations.LiveTv
_taskManager.CancelIfRunningAndQueue<RefreshChannelsScheduledTask>();
}
private readonly object _programsDataLock = new object();
private Dictionary<Guid, LiveTvProgram> GetProgramsDictionary()
{
if (_programs == null)
{
lock (_programsDataLock)
{
if (_programs == null)
{
var dict = new Dictionary<Guid, LiveTvProgram>();
foreach (var item in _itemRepo.GetItemsOfType(typeof (LiveTvProgram))
.Cast<LiveTvProgram>()
.ToList())
{
dict[item.Id] = item;
}
_programs = dict;
}
}
}
return _programs;
}
private IEnumerable<LiveTvProgram> GetPrograms()
{
return GetProgramsDictionary().Values;
}
public async Task<QueryResult<LiveTvChannel>> GetInternalChannels(LiveTvChannelQuery query, CancellationToken cancellationToken)
{
var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
@ -260,7 +289,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
LiveTvProgram obj = null;
_programs.TryGetValue(guid, out obj);
GetProgramsDictionary().TryGetValue(guid, out obj);
if (obj != null)
{
@ -597,7 +626,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
item.ProductionYear = info.ProductionYear;
item.PremiereDate = item.PremiereDate ?? info.OriginalAirDate;
await item.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
return item;
@ -691,7 +720,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public async Task<QueryResult<ProgramInfoDto>> GetPrograms(ProgramQuery query, CancellationToken cancellationToken)
{
IEnumerable<LiveTvProgram> programs = _programs.Values;
IEnumerable<LiveTvProgram> programs = GetPrograms();
if (query.MinEndDate.HasValue)
{
@ -806,7 +835,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public async Task<QueryResult<LiveTvProgram>> GetRecommendedProgramsInternal(RecommendedProgramQuery query, CancellationToken cancellationToken)
{
IEnumerable<LiveTvProgram> programs = _programs.Values;
IEnumerable<LiveTvProgram> programs = GetPrograms();
var user = _userManager.GetUserById(query.UserId);
@ -995,24 +1024,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv
internal async Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
{
await _refreshSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
var innerProgress = new ActionableProgress<double>();
innerProgress.RegisterAction(p => progress.Report(p * .9));
await RefreshChannelsInternal(innerProgress, cancellationToken).ConfigureAwait(false);
try
{
var innerProgress = new ActionableProgress<double>();
innerProgress.RegisterAction(p => progress.Report(p * .9));
await RefreshChannelsInternal(innerProgress, cancellationToken).ConfigureAwait(false);
innerProgress = new ActionableProgress<double>();
innerProgress.RegisterAction(p => progress.Report(90 + (p * .1)));
await CleanDatabaseInternal(progress, cancellationToken).ConfigureAwait(false);
innerProgress = new ActionableProgress<double>();
innerProgress.RegisterAction(p => progress.Report(90 + (p * .1)));
await CleanDatabaseInternal(progress, cancellationToken).ConfigureAwait(false);
RefreshIfNeeded(_programs.Values.Where(i => (i.StartDate - DateTime.UtcNow).TotalDays <= 1).ToList());
}
finally
{
_refreshSemaphore.Release();
}
RefreshIfNeeded(GetPrograms().Where(i => (i.StartDate - DateTime.UtcNow).TotalDays <= 1).ToList());
}
private async Task RefreshChannelsInternal(IProgress<double> progress, CancellationToken cancellationToken)
@ -1136,7 +1156,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
progress.Report(80 * percent + 10);
}
_programs = programs.ToDictionary(i => i.Id);
lock (_programsDataLock)
{
_programs = programs.ToDictionary(i => i.Id);
}
_refreshedPrograms.Clear();
progress.Report(90);
@ -1147,28 +1171,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv
progress.Report(100);
}
public async Task CleanDatabase(IProgress<double> progress, CancellationToken cancellationToken)
{
await _refreshSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
await DeleteOldPrograms(_programs.Keys.ToList(), progress, cancellationToken).ConfigureAwait(false);
}
finally
{
_refreshSemaphore.Release();
}
}
private Task CleanDatabaseInternal(IProgress<double> progress, CancellationToken cancellationToken)
{
return DeleteOldPrograms(_programs.Keys.ToList(), progress, cancellationToken);
return DeleteOldPrograms(GetProgramsDictionary().Keys.ToList(), progress, cancellationToken);
}
private async Task DeleteOldPrograms(List<Guid> currentIdList, IProgress<double> progress, CancellationToken cancellationToken)
{
var list = _itemRepo.GetItemsOfType(typeof(LiveTvProgram)).ToList();
var list = _itemRepo.GetItemIdsOfType(typeof(LiveTvProgram)).ToList();
var numComplete = 0;
@ -1549,7 +1559,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
var now = DateTime.UtcNow;
var program = _programs.Values
var program = GetPrograms()
.Where(i => string.Equals(externalChannelId, i.ExternalChannelId, StringComparison.OrdinalIgnoreCase))
.OrderBy(i => i.StartDate)
.SkipWhile(i => now >= (i.EndDate ?? DateTime.MinValue))
@ -1742,7 +1752,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
var dtoOptions = new DtoOptions();
dtoOptions.Fields.Remove(ItemFields.SyncInfo);
var recordingResult = await GetRecordings(new RecordingQuery
{
UserId = query.UserId
@ -1855,13 +1865,15 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public GuideInfo GetGuideInfo()
{
var programs = _programs.ToList();
var programs = GetPrograms().OrderBy(i => i.StartDate).ToList();
var startDate = _programs.Count == 0 ? DateTime.MinValue :
programs.Select(i => i.Value.StartDate).Min();
var startDate = programs.Count == 0 ?
DateTime.MinValue :
programs[0].StartDate;
var endDate = programs.Count == 0 ? DateTime.MinValue :
programs.Select(i => i.Value.StartDate).Max();
var endDate = programs.Count == 0 ?
DateTime.MinValue :
programs[programs.Count - 1].StartDate;
return new GuideInfo
{

View file

@ -1430,5 +1430,8 @@
"ButtonMyPreferencesWelcomeNo": "No thanks, I'll do it later.",
"MyPreferencesWelcomeMessage1": "We've presented your library in a way we think you'll enjoy. The appearance and grouping of content can be changed anytime by adjusting your preferences. Your preferences will apply to all Emby apps.",
"MyPreferencesWelcomeMessage2": "Would you like to set your preferences now?",
"ToAccessPreferencesHelp": "To access your preferences later, click your user icon in the top right header and select My Preferences."
"ToAccessPreferencesHelp": "To access your preferences later, click your user icon in the top right header and select My Preferences.",
"HeaderViewStyles": "View Styles",
"LabelSelectViewStyles": "Enable rich presentations for:",
"LabelSelectViewStylesHelp": "If enabled, views will be built with metadata to offer categories such as Suggestions, Latest, Genres, and more. If disabled, they'll be displayed with simple folders."
}

View file

@ -522,7 +522,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
}
public IEnumerable<Guid> GetItemsOfType(Type type)
public IEnumerable<BaseItem> GetItemsOfType(Type type)
{
if (type == null)
{
@ -530,7 +530,37 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
CheckDisposed();
using (var cmd = _connection.CreateCommand())
{
cmd.CommandText = "select type,data from TypedBaseItems where type = @type";
cmd.Parameters.Add(cmd, "@type", DbType.String).Value = type.FullName;
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
{
while (reader.Read())
{
var item = GetItem(reader);
if (item != null)
{
yield return item;
}
}
}
}
}
public IEnumerable<Guid> GetItemIdsOfType(Type type)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
CheckDisposed();
using (var cmd = _connection.CreateCommand())
{
cmd.CommandText = "select guid from TypedBaseItems where type = @type";

View file

@ -205,7 +205,7 @@ namespace MediaBrowser.Server.Implementations.Sync
{
Condition = ProfileConditionType.LessThanEqual,
Property = ProfileConditionValue.AudioChannels,
Value = "5",
Value = "6",
IsRequired = false
},
new ProfileCondition

View file

@ -62,7 +62,7 @@
<ItemGroup>
<Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ImageMagickSharp.1.0.0.14\lib\net45\ImageMagickSharp.dll</HintPath>
<HintPath>..\packages\ImageMagickSharp.1.0.0.15\lib\net45\ImageMagickSharp.dll</HintPath>
</Reference>
<Reference Include="MediaBrowser.IsoMounter">
<HintPath>..\packages\MediaBrowser.IsoMounting.3.0.69\lib\net45\MediaBrowser.IsoMounter.dll</HintPath>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ImageMagickSharp" version="1.0.0.14" targetFramework="net45" />
<package id="ImageMagickSharp" version="1.0.0.15" targetFramework="net45" />
<package id="MediaBrowser.IsoMounting" version="3.0.69" targetFramework="net45" />
<package id="System.Data.SQLite.Core" version="1.0.94.0" targetFramework="net45" />
</packages>

View file

@ -1,4 +1,4 @@
using System.Reflection;
//[assembly: AssemblyVersion("3.0.*")]
[assembly: AssemblyVersion("3.0.5582.1")]
[assembly: AssemblyVersion("3.0.5582.2")]