aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke <luke.pulverenti@gmail.com>2016-08-19 14:57:01 -0400
committerGitHub <noreply@github.com>2016-08-19 14:57:01 -0400
commit0780889c51819c9f1c3b01f5bcdae9806c219c10 (patch)
treee91e8542e997c25992fd24e0b40fd7a765c45d62
parentac8c4ccc3ce53a4eafbd0493e9fe333d95b28730 (diff)
parent003f61c8914706c6ea1d211644dabf5a5fbd759e (diff)
Merge pull request #2080 from MediaBrowser/beta
Beta
-rw-r--r--MediaBrowser.Api/ApiEntryPoint.cs8
-rw-r--r--MediaBrowser.Api/BaseApiService.cs4
-rw-r--r--MediaBrowser.Api/IHasDtoOptions.cs1
-rw-r--r--MediaBrowser.Api/Images/ImageService.cs4
-rw-r--r--MediaBrowser.Api/Library/LibraryStructureService.cs32
-rw-r--r--MediaBrowser.Api/LiveTv/LiveTvService.cs27
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs176
-rw-r--r--MediaBrowser.Api/Playback/MediaInfoService.cs14
-rw-r--r--MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs16
-rw-r--r--MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs29
-rw-r--r--MediaBrowser.Api/Playback/StreamRequest.cs1
-rw-r--r--MediaBrowser.Api/PlaylistService.cs14
-rw-r--r--MediaBrowser.Api/SimilarItemsHelper.cs14
-rw-r--r--MediaBrowser.Api/Sync/SyncHelper.cs4
-rw-r--r--MediaBrowser.Api/Sync/SyncService.cs3
-rw-r--r--MediaBrowser.Api/TvShowsService.cs54
-rw-r--r--MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs1
-rw-r--r--MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs3
-rw-r--r--MediaBrowser.Api/UserLibrary/ItemsService.cs34
-rw-r--r--MediaBrowser.Api/UserLibrary/UserLibraryService.cs34
-rw-r--r--MediaBrowser.Controller/Dto/DtoOptions.cs4
-rw-r--r--MediaBrowser.Controller/Entities/AggregateFolder.cs59
-rw-r--r--MediaBrowser.Controller/Entities/Audio/Audio.cs9
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicArtist.cs57
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicGenre.cs51
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs26
-rw-r--r--MediaBrowser.Controller/Entities/Book.cs2
-rw-r--r--MediaBrowser.Controller/Entities/CollectionFolder.cs71
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs33
-rw-r--r--MediaBrowser.Controller/Entities/Game.cs2
-rw-r--r--MediaBrowser.Controller/Entities/GameGenre.cs50
-rw-r--r--MediaBrowser.Controller/Entities/Genre.cs51
-rw-r--r--MediaBrowser.Controller/Entities/IHasMetadata.cs10
-rw-r--r--MediaBrowser.Controller/Entities/InternalItemsQuery.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Movies/BoxSet.cs36
-rw-r--r--MediaBrowser.Controller/Entities/Movies/Movie.cs10
-rw-r--r--MediaBrowser.Controller/Entities/Person.cs66
-rw-r--r--MediaBrowser.Controller/Entities/Photo.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Studio.cs51
-rw-r--r--MediaBrowser.Controller/Entities/TV/Season.cs61
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs223
-rw-r--r--MediaBrowser.Controller/Entities/Trailer.cs10
-rw-r--r--MediaBrowser.Controller/Entities/UserRootFolder.cs40
-rw-r--r--MediaBrowser.Controller/Entities/UserViewBuilder.cs80
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs27
-rw-r--r--MediaBrowser.Controller/Entities/Year.cs43
-rw-r--r--MediaBrowser.Controller/IServerApplicationPaths.cs2
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs19
-rw-r--r--MediaBrowser.Controller/Library/ItemResolveArgs.cs9
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj1
-rw-r--r--MediaBrowser.Controller/Net/IAsyncStreamSource.cs18
-rw-r--r--MediaBrowser.Controller/Net/IHttpResultFactory.cs2
-rw-r--r--MediaBrowser.Controller/Persistence/IItemRepository.cs6
-rw-r--r--MediaBrowser.Controller/Playlists/Playlist.cs12
-rw-r--r--MediaBrowser.Dlna/DlnaManager.cs7
-rw-r--r--MediaBrowser.Dlna/PlayTo/Device.cs39
-rw-r--r--MediaBrowser.Dlna/PlayTo/PlayToController.cs24
-rw-r--r--MediaBrowser.Dlna/Profiles/DefaultProfile.cs20
-rw-r--r--MediaBrowser.Dlna/Profiles/DenonAvrProfile.cs2
-rw-r--r--MediaBrowser.Dlna/Profiles/DirectTvProfile.cs2
-rw-r--r--MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs4
-rw-r--r--MediaBrowser.Dlna/Profiles/LgTvProfile.cs2
-rw-r--r--MediaBrowser.Dlna/Profiles/LinksysDMA2100Profile.cs2
-rw-r--r--MediaBrowser.Dlna/Profiles/MediaMonkeyProfile.cs2
-rw-r--r--MediaBrowser.Dlna/Profiles/PopcornHourProfile.cs2
-rw-r--r--MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs2
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Default.xml10
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs5
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs26
-rw-r--r--MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs7
-rw-r--r--MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj3
-rw-r--r--MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj3
-rw-r--r--MediaBrowser.Model/Configuration/LibraryOptions.cs13
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs4
-rw-r--r--MediaBrowser.Model/Dlna/ResolutionNormalizer.cs5
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs13
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs2
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs14
-rw-r--r--MediaBrowser.Model/Dto/ItemCounts.cs1
-rw-r--r--MediaBrowser.Model/Dto/MediaSourceInfo.cs1
-rw-r--r--MediaBrowser.Model/Entities/VirtualFolderInfo.cs3
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs6
-rw-r--r--MediaBrowser.Model/LiveTv/ProgramQuery.cs2
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj1
-rw-r--r--MediaBrowser.Model/Sync/SyncJobQuery.cs1
-rw-r--r--MediaBrowser.Model/Users/UserPolicy.cs2
-rw-r--r--MediaBrowser.Providers/Manager/MetadataService.cs22
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs9
-rw-r--r--MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs100
-rw-r--r--MediaBrowser.Providers/TV/DummySeasonProvider.cs4
-rw-r--r--MediaBrowser.Providers/TV/SeasonMetadataService.cs21
-rw-r--r--MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Dto/DtoService.cs164
-rw-r--r--MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs50
-rw-r--r--MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs14
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/AsyncStreamWriter.cs (renamed from MediaBrowser.Server.Implementations/HttpServer/AsyncStreamWriterFunc.cs)35
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs50
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs8
-rw-r--r--MediaBrowser.Server.Implementations/IO/FileRefresher.cs12
-rw-r--r--MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs15
-rw-r--r--MediaBrowser.Server.Implementations/Library/LibraryManager.cs169
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs6
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs31
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs12
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs11
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs3
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs3
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs6
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs7
-rw-r--r--MediaBrowser.Server.Implementations/Library/SearchEngine.cs20
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs7
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/ArtistsValidator.cs18
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs7
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs21
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs7
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs21
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs7
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs21
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs2
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/StudiosPostScanTask.cs7
-rw-r--r--MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs18
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs5
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs36
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs2
-rw-r--r--MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj4
-rw-r--r--MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs120
-rw-r--r--MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs2
-rw-r--r--MediaBrowser.Server.Implementations/ServerApplicationPaths.cs8
-rw-r--r--MediaBrowser.Server.Implementations/Session/SessionManager.cs3
-rw-r--r--MediaBrowser.Server.Implementations/Sync/SyncRepository.cs14
-rw-r--r--MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs2
-rw-r--r--MediaBrowser.Server.Implementations/packages.config2
-rw-r--r--MediaBrowser.Server.Mac/Emby.Server.Mac.csproj70
-rw-r--r--MediaBrowser.Server.Startup.Common/ApplicationHost.cs39
-rw-r--r--MediaBrowser.ServerApplication/App.config2
-rw-r--r--MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj2
-rw-r--r--MediaBrowser.WebDashboard/Api/DashboardService.cs63
-rw-r--r--MediaBrowser.WebDashboard/Api/PackageCreator.cs12
-rw-r--r--MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj41
-rw-r--r--MediaBrowser.WebDashboard/packages.config2
-rw-r--r--MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs2
-rw-r--r--SharedVersion.cs2
145 files changed, 2255 insertions, 949 deletions
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index bb9d2b864..7c5f7cde4 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -192,13 +192,13 @@ namespace MediaBrowser.Api
_activeTranscodingJobs.Add(job);
- ReportTranscodingProgress(job, state, null, null, null, null);
+ ReportTranscodingProgress(job, state, null, null, null, null, null);
return job;
}
}
- public void ReportTranscodingProgress(TranscodingJob job, StreamState state, TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded)
+ public void ReportTranscodingProgress(TranscodingJob job, StreamState state, TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate)
{
var ticks = transcodingPosition.HasValue ? transcodingPosition.Value.Ticks : (long?)null;
@@ -208,6 +208,7 @@ namespace MediaBrowser.Api
job.CompletionPercentage = percentComplete;
job.TranscodingPositionTicks = ticks;
job.BytesTranscoded = bytesTranscoded;
+ job.BitRate = bitRate;
}
var deviceId = state.Request.DeviceId;
@@ -219,7 +220,7 @@ namespace MediaBrowser.Api
_sessionManager.ReportTranscodingInfo(deviceId, new TranscodingInfo
{
- Bitrate = state.TotalOutputBitrate,
+ Bitrate = bitRate ?? state.TotalOutputBitrate,
AudioCodec = audioCodec,
VideoCodec = videoCodec,
Container = state.OutputContainer,
@@ -694,6 +695,7 @@ namespace MediaBrowser.Api
public long? BytesDownloaded { get; set; }
public long? BytesTranscoded { get; set; }
+ public int? BitRate { get; set; }
public long? TranscodingPositionTicks { get; set; }
public long? DownloadPositionTicks { get; set; }
diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs
index 44a367be0..3ff432d74 100644
--- a/MediaBrowser.Api/BaseApiService.cs
+++ b/MediaBrowser.Api/BaseApiService.cs
@@ -139,6 +139,10 @@ namespace MediaBrowser.Api
{
options.ImageTypeLimit = hasDtoOptions.ImageTypeLimit.Value;
}
+ if (hasDtoOptions.EnableUserData.HasValue)
+ {
+ options.EnableUserData = hasDtoOptions.EnableUserData.Value;
+ }
if (!string.IsNullOrWhiteSpace(hasDtoOptions.EnableImageTypes))
{
diff --git a/MediaBrowser.Api/IHasDtoOptions.cs b/MediaBrowser.Api/IHasDtoOptions.cs
index dac366113..6ed1670c2 100644
--- a/MediaBrowser.Api/IHasDtoOptions.cs
+++ b/MediaBrowser.Api/IHasDtoOptions.cs
@@ -4,6 +4,7 @@ namespace MediaBrowser.Api
public interface IHasDtoOptions : IHasItemFields
{
bool? EnableImages { get; set; }
+ bool? EnableUserData { get; set; }
int? ImageTypeLimit { get; set; }
diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs
index 5866ad15b..3280358df 100644
--- a/MediaBrowser.Api/Images/ImageService.cs
+++ b/MediaBrowser.Api/Images/ImageService.cs
@@ -573,11 +573,9 @@ namespace MediaBrowser.Api.Images
var outputFormats = GetOutputFormats(request, imageInfo, cropwhitespace, supportedImageEnhancers);
- var cacheGuid = new Guid(_imageProcessor.GetImageCacheTag(item, imageInfo, supportedImageEnhancers));
-
TimeSpan? cacheDuration = null;
- if (!string.IsNullOrEmpty(request.Tag) && cacheGuid == new Guid(request.Tag))
+ if (!string.IsNullOrEmpty(request.Tag))
{
cacheDuration = TimeSpan.FromDays(365);
}
diff --git a/MediaBrowser.Api/Library/LibraryStructureService.cs b/MediaBrowser.Api/Library/LibraryStructureService.cs
index 3cf0d5d93..72966a7cd 100644
--- a/MediaBrowser.Api/Library/LibraryStructureService.cs
+++ b/MediaBrowser.Api/Library/LibraryStructureService.cs
@@ -10,6 +10,9 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using CommonIO;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Configuration;
namespace MediaBrowser.Api.Library
{
@@ -52,6 +55,8 @@ namespace MediaBrowser.Api.Library
/// </summary>
/// <value>The path.</value>
public string[] Paths { get; set; }
+
+ public LibraryOptions LibraryOptions { get; set; }
}
[Route("/Library/VirtualFolders", "DELETE")]
@@ -136,6 +141,14 @@ namespace MediaBrowser.Api.Library
public bool RefreshLibrary { get; set; }
}
+ [Route("/Library/VirtualFolders/LibraryOptions", "POST")]
+ public class UpdateLibraryOptions : IReturnVoid
+ {
+ public string Id { get; set; }
+
+ public LibraryOptions LibraryOptions { get; set; }
+ }
+
/// <summary>
/// Class LibraryStructureService
/// </summary>
@@ -184,13 +197,22 @@ namespace MediaBrowser.Api.Library
return ToOptimizedSerializedResultUsingCache(result);
}
+ public void Post(UpdateLibraryOptions request)
+ {
+ var collectionFolder = (CollectionFolder)_libraryManager.GetItemById(request.Id);
+
+ collectionFolder.UpdateLibraryOptions(request.LibraryOptions);
+ }
+
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public void Post(AddVirtualFolder request)
{
- _libraryManager.AddVirtualFolder(request.Name, request.CollectionType, request.Paths, request.RefreshLibrary);
+ var libraryOptions = request.LibraryOptions ?? new LibraryOptions();
+
+ _libraryManager.AddVirtualFolder(request.Name, request.CollectionType, request.Paths, libraryOptions, request.RefreshLibrary);
}
/// <summary>
@@ -214,12 +236,12 @@ namespace MediaBrowser.Api.Library
var currentPath = Path.Combine(rootFolderPath, request.Name);
var newPath = Path.Combine(rootFolderPath, request.NewName);
- if (!_fileSystem.DirectoryExists(currentPath))
+ if (!_fileSystem.DirectoryExists(currentPath))
{
throw new DirectoryNotFoundException("The media collection does not exist");
}
- if (!string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase) && _fileSystem.DirectoryExists(newPath))
+ if (!string.Equals(currentPath, newPath, StringComparison.OrdinalIgnoreCase) && _fileSystem.DirectoryExists(newPath))
{
throw new ArgumentException("There is already a media collection with the name " + newPath + ".");
}
@@ -234,11 +256,11 @@ namespace MediaBrowser.Api.Library
//Create an unique name
var temporaryName = Guid.NewGuid().ToString();
var temporaryPath = Path.Combine(rootFolderPath, temporaryName);
- _fileSystem.MoveDirectory(currentPath, temporaryPath);
+ _fileSystem.MoveDirectory(currentPath, temporaryPath);
currentPath = temporaryPath;
}
- _fileSystem.MoveDirectory(currentPath, newPath);
+ _fileSystem.MoveDirectory(currentPath, newPath);
}
finally
{
diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index 91157cd11..545ac162f 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -82,6 +82,9 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "AddCurrentProgram", Description = "Optional. Adds current program info to each channel", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public bool AddCurrentProgram { get; set; }
+ [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+ public bool? EnableUserData { get; set; }
+
public GetChannels()
{
AddCurrentProgram = true;
@@ -149,6 +152,9 @@ namespace MediaBrowser.Api.LiveTv
public bool EnableTotalRecordCount { get; set; }
+ [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+ public bool? EnableUserData { get; set; }
+
public GetRecordings()
{
EnableTotalRecordCount = true;
@@ -271,6 +277,9 @@ namespace MediaBrowser.Api.LiveTv
[ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string EnableImageTypes { get; set; }
+ [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+ public bool? EnableUserData { get; set; }
+
/// <summary>
/// Fields to return within the items, in addition to basic information
/// </summary>
@@ -331,6 +340,9 @@ namespace MediaBrowser.Api.LiveTv
/// <value>The fields.</value>
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string Fields { get; set; }
+
+ [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+ public bool? EnableUserData { get; set; }
}
[Route("/LiveTv/Programs/{Id}", "GET", Summary = "Gets a live tv program")]
@@ -726,7 +738,12 @@ namespace MediaBrowser.Api.LiveTv
var user = string.IsNullOrEmpty(request.UserId) ? null : _userManager.GetUserById(request.UserId);
- var returnArray = (await _dtoService.GetBaseItemDtos(channelResult.Items, GetDtoOptions(Request), user).ConfigureAwait(false)).ToArray();
+ var options = GetDtoOptions(request);
+ RemoveFields(options);
+
+ options.AddCurrentProgram = request.AddCurrentProgram;
+
+ var returnArray = (await _dtoService.GetBaseItemDtos(channelResult.Items, options, user).ConfigureAwait(false)).ToArray();
var result = new QueryResult<BaseItemDto>
{
@@ -737,6 +754,14 @@ namespace MediaBrowser.Api.LiveTv
return ToOptimizedSerializedResultUsingCache(result);
}
+ private void RemoveFields(DtoOptions options)
+ {
+ options.Fields.Remove(ItemFields.CanDelete);
+ options.Fields.Remove(ItemFields.CanDownload);
+ options.Fields.Remove(ItemFields.DisplayPreferencesId);
+ options.Fields.Remove(ItemFields.Etag);
+ }
+
public object Get(GetChannel request)
{
var user = string.IsNullOrWhiteSpace(request.UserId) ? null : _userManager.GetUserById(request.UserId);
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 164d607d2..f52505c04 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -22,6 +22,8 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using CommonIO;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller;
namespace MediaBrowser.Api.Playback
{
@@ -69,6 +71,9 @@ namespace MediaBrowser.Api.Playback
protected IZipClient ZipClient { get; private set; }
protected IJsonSerializer JsonSerializer { get; private set; }
+ public static IServerApplicationHost AppHost;
+ public static IHttpClient HttpClient;
+
/// <summary>
/// Initializes a new instance of the <see cref="BaseStreamingService" /> class.
/// </summary>
@@ -1055,14 +1060,14 @@ namespace MediaBrowser.Api.Playback
var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
Logger.Info(commandLineLogMessage);
- var logFilePrefix = "transcode";
+ var logFilePrefix = "ffmpeg-transcode";
if (state.VideoRequest != null && string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
- logFilePrefix = "directstream";
+ logFilePrefix = "ffmpeg-directstream";
}
else if (state.VideoRequest != null && string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
- logFilePrefix = "remux";
+ logFilePrefix = "ffmpeg-remux";
}
var logFilePath = Path.Combine(ServerConfigurationManager.ApplicationPaths.LogDirectoryPath, logFilePrefix + "-" + Guid.NewGuid() + ".txt");
@@ -1112,28 +1117,30 @@ namespace MediaBrowser.Api.Playback
}
StartThrottler(state, transcodingJob);
+ ReportUsage(state);
return transcodingJob;
}
private void StartThrottler(StreamState state, TranscodingJob transcodingJob)
{
- if (EnableThrottling(state) && state.InputProtocol == MediaProtocol.File &&
- state.RunTimeTicks.HasValue &&
- state.VideoType == VideoType.VideoFile &&
- !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
+ if (EnableThrottling(state) && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
{
- if (state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks && state.IsInputVideo)
- {
- transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger, ServerConfigurationManager);
- state.TranscodingThrottler.Start();
- }
+ transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger, ServerConfigurationManager);
+ state.TranscodingThrottler.Start();
}
}
protected virtual bool EnableThrottling(StreamState state)
{
- return true;
+ // do not use throttling with hardware encoders
+ return state.InputProtocol == MediaProtocol.File &&
+ state.RunTimeTicks.HasValue &&
+ state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks &&
+ state.IsInputVideo &&
+ state.VideoType == VideoType.VideoFile &&
+ !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) &&
+ string.Equals(GetVideoEncoder(state), "libx264", StringComparison.OrdinalIgnoreCase);
}
private async Task StartStreamingLog(TranscodingJob transcodingJob, StreamState state, Stream source, Stream target)
@@ -1171,6 +1178,7 @@ namespace MediaBrowser.Api.Playback
double? percent = null;
TimeSpan? transcodingPosition = null;
long? bytesTranscoded = null;
+ int? bitRate = null;
var parts = line.Split(' ');
@@ -1234,11 +1242,32 @@ namespace MediaBrowser.Api.Playback
}
}
}
+ else if (part.StartsWith("bitrate=", StringComparison.OrdinalIgnoreCase))
+ {
+ var rate = part.Split(new[] { '=' }, 2).Last();
+
+ int? scale = null;
+ if (rate.IndexOf("kbits/s", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ scale = 1024;
+ rate = rate.Replace("kbits/s", string.Empty, StringComparison.OrdinalIgnoreCase);
+ }
+
+ if (scale.HasValue)
+ {
+ float val;
+
+ if (float.TryParse(rate, NumberStyles.Any, UsCulture, out val))
+ {
+ bitRate = (int)Math.Ceiling(val * scale.Value);
+ }
+ }
+ }
}
if (framerate.HasValue || percent.HasValue)
{
- ApiEntryPoint.Instance.ReportTranscodingProgress(transcodingJob, state, transcodingPosition, framerate, percent, bytesTranscoded);
+ ApiEntryPoint.Instance.ReportTranscodingProgress(transcodingJob, state, transcodingPosition, framerate, percent, bytesTranscoded, bitRate);
}
}
@@ -1588,6 +1617,10 @@ namespace MediaBrowser.Api.Playback
videoRequest.EnableSubtitlesInManifest = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
}
}
+ else if (i == 29)
+ {
+ request.Tag = val;
+ }
}
}
@@ -2192,6 +2225,121 @@ namespace MediaBrowser.Api.Playback
}
}
+ private async void ReportUsage(StreamState state)
+ {
+ try
+ {
+ await ReportUsageInternal(state).ConfigureAwait(false);
+ }
+ catch
+ {
+
+ }
+ }
+
+ private Task ReportUsageInternal(StreamState state)
+ {
+ if (!ServerConfigurationManager.Configuration.EnableAnonymousUsageReporting)
+ {
+ return Task.FromResult(true);
+ }
+
+ if (!string.Equals(MediaEncoder.EncoderLocationType, "Default", StringComparison.OrdinalIgnoreCase))
+ {
+ return Task.FromResult(true);
+ }
+
+ var dict = new Dictionary<string, string>();
+
+ var outputAudio = GetAudioEncoder(state);
+ if (!string.IsNullOrWhiteSpace(outputAudio))
+ {
+ dict["outputAudio"] = outputAudio;
+ }
+
+ var outputVideo = GetVideoEncoder(state);
+ if (!string.IsNullOrWhiteSpace(outputVideo))
+ {
+ dict["outputVideo"] = outputVideo;
+ }
+
+ if (ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputAudio ?? string.Empty, StringComparer.OrdinalIgnoreCase) &&
+ ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputVideo ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ {
+ return Task.FromResult(true);
+ }
+
+ dict["id"] = AppHost.SystemId;
+ dict["type"] = state.VideoRequest == null ? "Audio" : "Video";
+
+ var audioStream = state.AudioStream;
+ if (audioStream != null && !string.IsNullOrWhiteSpace(audioStream.Codec))
+ {
+ dict["inputAudio"] = audioStream.Codec;
+ }
+
+ var videoStream = state.VideoStream;
+ if (videoStream != null && !string.IsNullOrWhiteSpace(videoStream.Codec))
+ {
+ dict["inputVideo"] = videoStream.Codec;
+ }
+
+ var cert = GetType().Assembly.GetModules().First().GetSignerCertificate();
+ if (cert != null)
+ {
+ dict["assemblySig"] = cert.GetCertHashString();
+ dict["certSubject"] = cert.Subject ?? string.Empty;
+ dict["certIssuer"] = cert.Issuer ?? string.Empty;
+ }
+ else
+ {
+ return Task.FromResult(true);
+ }
+
+ if (state.SupportedAudioCodecs.Count > 0)
+ {
+ dict["supportedAudioCodecs"] = string.Join(",", state.SupportedAudioCodecs.ToArray());
+ }
+
+ var auth = AuthorizationContext.GetAuthorizationInfo(Request);
+
+ dict["appName"] = auth.Client ?? string.Empty;
+ dict["appVersion"] = auth.Version ?? string.Empty;
+ dict["device"] = auth.Device ?? string.Empty;
+ dict["deviceId"] = auth.DeviceId ?? string.Empty;
+ dict["context"] = "streaming";
+
+ //Logger.Info(JsonSerializer.SerializeToString(dict));
+ if (!ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputAudio ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ {
+ var list = ServerConfigurationManager.Configuration.CodecsUsed.ToList();
+ list.Add(outputAudio);
+ ServerConfigurationManager.Configuration.CodecsUsed = list.ToArray();
+ }
+
+ if (!ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputVideo ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ {
+ var list = ServerConfigurationManager.Configuration.CodecsUsed.ToList();
+ list.Add(outputVideo);
+ ServerConfigurationManager.Configuration.CodecsUsed = list.ToArray();
+ }
+
+ ServerConfigurationManager.SaveConfiguration();
+
+ //Logger.Info(JsonSerializer.SerializeToString(dict));
+ var options = new HttpRequestOptions()
+ {
+ Url = "https://mb3admin.com/admin/service/transcoding/report",
+ CancellationToken = CancellationToken.None,
+ LogRequest = false,
+ LogErrors = false
+ };
+ options.RequestContent = JsonSerializer.SerializeToString(dict);
+ options.RequestContentType = "application/json";
+
+ return HttpClient.Post(options);
+ }
+
/// <summary>
/// Adds the dlna headers.
/// </summary>
diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs
index 0b989784c..91e62b4e3 100644
--- a/MediaBrowser.Api/Playback/MediaInfoService.cs
+++ b/MediaBrowser.Api/Playback/MediaInfoService.cs
@@ -284,6 +284,13 @@ namespace MediaBrowser.Api.Playback
options.ForceDirectPlay = true;
}
}
+ else if (item is Video)
+ {
+ if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing)
+ {
+ options.ForceDirectPlay = true;
+ }
+ }
// The MediaSource supports direct stream, now test to see if the client supports it
var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
@@ -315,6 +322,13 @@ namespace MediaBrowser.Api.Playback
options.ForceDirectStream = true;
}
}
+ else if (item is Video)
+ {
+ if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing)
+ {
+ options.ForceDirectStream = true;
+ }
+ }
// The MediaSource supports direct stream, now test to see if the client supports it
var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
index d6dea0fe5..b8cb6b14f 100644
--- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
@@ -154,12 +154,20 @@ namespace MediaBrowser.Api.Playback.Progressive
using (state)
{
+ TimeSpan? cacheDuration = null;
+
+ if (!string.IsNullOrEmpty(request.Tag))
+ {
+ cacheDuration = TimeSpan.FromDays(365);
+ }
+
return await ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
{
ResponseHeaders = responseHeaders,
ContentType = contentType,
IsHeadRequest = isHeadRequest,
- Path = state.MediaPath
+ Path = state.MediaPath,
+ CacheDuration = cacheDuration
}).ConfigureAwait(false);
}
@@ -362,9 +370,9 @@ namespace MediaBrowser.Api.Playback.Progressive
outputHeaders[item.Key] = item.Value;
}
- Func<Stream, Task> streamWriter = stream => new ProgressiveFileCopier(FileSystem, job, Logger).StreamFile(outputPath, stream, CancellationToken.None);
+ var streamSource = new ProgressiveFileCopier(FileSystem, outputPath, outputHeaders, job, Logger, CancellationToken.None);
- return ResultFactory.GetAsyncStreamWriter(streamWriter, outputHeaders);
+ return ResultFactory.GetAsyncStreamWriter(streamSource);
}
finally
{
@@ -383,7 +391,7 @@ namespace MediaBrowser.Api.Playback.Progressive
if (totalBitrate > 0 && state.RunTimeTicks.HasValue)
{
- return Convert.ToInt64(totalBitrate * TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalSeconds);
+ return Convert.ToInt64(totalBitrate * TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalSeconds / 8);
}
return null;
diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
index 63d71b85e..0a9a44641 100644
--- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
+++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
@@ -4,38 +4,55 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
using CommonIO;
+using MediaBrowser.Controller.Net;
+using System.Collections.Generic;
+using ServiceStack.Web;
namespace MediaBrowser.Api.Playback.Progressive
{
- public class ProgressiveFileCopier
+ public class ProgressiveFileCopier : IAsyncStreamSource, IHasOptions
{
private readonly IFileSystem _fileSystem;
private readonly TranscodingJob _job;
private readonly ILogger _logger;
+ private readonly string _path;
+ private readonly CancellationToken _cancellationToken;
+ private readonly Dictionary<string, string> _outputHeaders;
// 256k
private const int BufferSize = 81920;
private long _bytesWritten = 0;
- public ProgressiveFileCopier(IFileSystem fileSystem, TranscodingJob job, ILogger logger)
+ public ProgressiveFileCopier(IFileSystem fileSystem, string path, Dictionary<string, string> outputHeaders, TranscodingJob job, ILogger logger, CancellationToken cancellationToken)
{
_fileSystem = fileSystem;
+ _path = path;
+ _outputHeaders = outputHeaders;
_job = job;
_logger = logger;
+ _cancellationToken = cancellationToken;
}
- public async Task StreamFile(string path, Stream outputStream, CancellationToken cancellationToken)
+ public IDictionary<string, string> Options
+ {
+ get
+ {
+ return _outputHeaders;
+ }
+ }
+
+ public async Task WriteToAsync(Stream outputStream)
{
try
{
var eofCount = 0;
- using (var fs = _fileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
+ using (var fs = _fileSystem.GetFileStream(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
{
while (eofCount < 15)
{
- var bytesRead = await CopyToAsyncInternal(fs, outputStream, BufferSize, cancellationToken).ConfigureAwait(false);
+ var bytesRead = await CopyToAsyncInternal(fs, outputStream, BufferSize, _cancellationToken).ConfigureAwait(false);
//var position = fs.Position;
//_logger.Debug("Streamed {0} bytes to position {1} from file {2}", bytesRead, position, path);
@@ -46,7 +63,7 @@ namespace MediaBrowser.Api.Playback.Progressive
{
eofCount++;
}
- await Task.Delay(100, cancellationToken).ConfigureAwait(false);
+ await Task.Delay(100, _cancellationToken).ConfigureAwait(false);
}
else
{
diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs
index a8ca6aaa3..e1a577f52 100644
--- a/MediaBrowser.Api/Playback/StreamRequest.cs
+++ b/MediaBrowser.Api/Playback/StreamRequest.cs
@@ -74,6 +74,7 @@ namespace MediaBrowser.Api.Playback
public string Params { get; set; }
public string PlaySessionId { get; set; }
public string LiveStreamId { get; set; }
+ public string Tag { get; set; }
}
public class VideoStreamRequest : StreamRequest
diff --git a/MediaBrowser.Api/PlaylistService.cs b/MediaBrowser.Api/PlaylistService.cs
index 604227a15..969399288 100644
--- a/MediaBrowser.Api/PlaylistService.cs
+++ b/MediaBrowser.Api/PlaylistService.cs
@@ -72,7 +72,7 @@ namespace MediaBrowser.Api
}
[Route("/Playlists/{Id}/Items", "GET", Summary = "Gets the original items of a playlist")]
- public class GetPlaylistItems : IReturn<QueryResult<BaseItemDto>>, IHasItemFields
+ public class GetPlaylistItems : IReturn<QueryResult<BaseItemDto>>, IHasDtoOptions
{
[ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
public string Id { get; set; }
@@ -104,6 +104,18 @@ namespace MediaBrowser.Api
/// <value>The fields.</value>
[ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, CriticRatingSummary, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string Fields { get; set; }
+
+ [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+ public bool? EnableImages { get; set; }
+
+ [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+ public bool? EnableUserData { get; set; }
+
+ [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public int? ImageTypeLimit { get; set; }
+
+ [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string EnableImageTypes { get; set; }
}
[Authenticated]
diff --git a/MediaBrowser.Api/SimilarItemsHelper.cs b/MediaBrowser.Api/SimilarItemsHelper.cs
index a1e47bd8f..1621c8056 100644
--- a/MediaBrowser.Api/SimilarItemsHelper.cs
+++ b/MediaBrowser.Api/SimilarItemsHelper.cs
@@ -29,8 +29,20 @@ namespace MediaBrowser.Api
public string ExcludeArtistIds { get; set; }
}
- public class BaseGetSimilarItems : IReturn<ItemsResult>, IHasItemFields
+ public class BaseGetSimilarItems : IReturn<ItemsResult>, IHasDtoOptions
{
+ [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+ public bool? EnableImages { get; set; }
+
+ [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+ public bool? EnableUserData { get; set; }
+
+ [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+ public int? ImageTypeLimit { get; set; }
+
+ [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string EnableImageTypes { get; set; }
+
/// <summary>
/// Gets or sets the user id.
/// </summary>
diff --git a/MediaBrowser.Api/Sync/SyncHelper.cs b/MediaBrowser.Api/Sync/SyncHelper.cs
index 0d3e8707d..2f857000c 100644
--- a/MediaBrowser.Api/Sync/SyncHelper.cs
+++ b/MediaBrowser.Api/Sync/SyncHelper.cs
@@ -24,7 +24,7 @@ namespace MediaBrowser.Api.Sync
}
break;
}
- if (item.IsFolder && !item.IsMusicGenre && !item.IsArtist && !item.IsType("musicalbum") && !item.IsGameGenre)
+ if (item.IsFolderItem && !item.IsMusicGenre && !item.IsArtist && !item.IsType("musicalbum") && !item.IsGameGenre)
{
options.Add(SyncJobOption.Quality);
options.Add(SyncJobOption.Profile);
@@ -44,7 +44,7 @@ namespace MediaBrowser.Api.Sync
{
if (item.SupportsSync ?? false)
{
- if (item.IsFolder || item.IsGameGenre || item.IsMusicGenre || item.IsGenre || item.IsArtist || item.IsStudio || item.IsPerson)
+ if (item.IsFolderItem || item.IsGameGenre || item.IsMusicGenre || item.IsGenre || item.IsArtist || item.IsStudio || item.IsPerson)
{
options.Add(SyncJobOption.SyncNewContent);
options.Add(SyncJobOption.ItemLimit);
diff --git a/MediaBrowser.Api/Sync/SyncService.cs b/MediaBrowser.Api/Sync/SyncService.cs
index a15ce216f..b9544d71b 100644
--- a/MediaBrowser.Api/Sync/SyncService.cs
+++ b/MediaBrowser.Api/Sync/SyncService.cs
@@ -66,6 +66,7 @@ namespace MediaBrowser.Api.Sync
public string Id { get; set; }
}
+ [Route("/Sync/Items/Cancel", "POST", Summary = "Cancels items from a sync target")]
[Route("/Sync/{TargetId}/Items", "DELETE", Summary = "Cancels items from a sync target")]
public class CancelItems : IReturnVoid
{
@@ -211,7 +212,7 @@ namespace MediaBrowser.Api.Sync
return ToOptimizedResult(result);
}
- public void Delete(CancelItems request)
+ public void Any(CancelItems request)
{
var itemIds = request.ItemIds.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs
index 3f248ea8f..daaa6343d 100644
--- a/MediaBrowser.Api/TvShowsService.cs
+++ b/MediaBrowser.Api/TvShowsService.cs
@@ -69,6 +69,9 @@ namespace MediaBrowser.Api
[ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string EnableImageTypes { get; set; }
+
+ [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+ public bool? EnableUserData { get; set; }
}
[Route("/Shows/Upcoming", "GET", Summary = "Gets a list of upcoming episodes")]
@@ -117,6 +120,9 @@ namespace MediaBrowser.Api
[ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string EnableImageTypes { get; set; }
+
+ [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+ public bool? EnableUserData { get; set; }
}
[Route("/Shows/{Id}/Similar", "GET", Summary = "Finds tv shows similar to a given one.")]
@@ -184,6 +190,10 @@ namespace MediaBrowser.Api
[ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string EnableImageTypes { get; set; }
+
+ [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+ public bool? EnableUserData { get; set; }
+
}
[Route("/Shows/{Id}/Seasons", "GET", Summary = "Gets seasons for a tv series")]
@@ -226,6 +236,10 @@ namespace MediaBrowser.Api
[ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string EnableImageTypes { get; set; }
+
+ [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+ public bool? EnableUserData { get; set; }
+
}
/// <summary>
@@ -409,23 +423,14 @@ namespace MediaBrowser.Api
throw new ResourceNotFoundException("No series exists with Id " + request.Id);
}
- var seasons = series.GetSeasons(user);
-
- if (request.IsSpecialSeason.HasValue)
+ var seasons = (await series.GetItems(new InternalItemsQuery(user)
{
- var val = request.IsSpecialSeason.Value;
+ IsMissing = request.IsMissing,
+ IsVirtualUnaired = request.IsVirtualUnaired,
+ IsSpecialSeason = request.IsSpecialSeason,
+ AdjacentTo = request.AdjacentTo
- seasons = seasons.Where(i => i.IsSpecialSeason == val);
- }
-
- seasons = FilterVirtualSeasons(request, seasons);
-
- // This must be the last filter
- if (!string.IsNullOrEmpty(request.AdjacentTo))
- {
- seasons = UserViewBuilder.FilterForAdjacency(seasons, request.AdjacentTo)
- .Cast<Season>();
- }
+ }).ConfigureAwait(false)).Items.OfType<Season>();
var dtoOptions = GetDtoOptions(request);
@@ -439,23 +444,6 @@ namespace MediaBrowser.Api
};
}
- private IEnumerable<Season> FilterVirtualSeasons(GetSeasons request, IEnumerable<Season> items)
- {
- if (request.IsMissing.HasValue)
- {
- var val = request.IsMissing.Value;
- items = items.Where(i => (i.IsMissingSeason) == val);
- }
-
- if (request.IsVirtualUnaired.HasValue)
- {
- var val = request.IsVirtualUnaired.Value;
- items = items.Where(i => i.IsVirtualUnaired == val);
- }
-
- return items;
- }
-
public async Task<object> Get(GetEpisodes request)
{
var user = _userManager.GetUserById(request.UserId);
@@ -490,7 +478,7 @@ namespace MediaBrowser.Api
}
else
{
- episodes = series.GetEpisodes(user, season);
+ episodes = series.GetSeasonEpisodes(user, season);
}
}
else
diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
index 852a3d59e..94a6a7ef1 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
@@ -214,6 +214,7 @@ namespace MediaBrowser.Api.UserLibrary
dto.AlbumCount = counts.AlbumCount;
dto.SongCount = counts.SongCount;
dto.GameCount = counts.GameCount;
+ dto.ArtistCount = counts.ArtistCount;
}
/// <summary>
diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
index 3e9a541c0..96acb1f60 100644
--- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
+++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
@@ -226,6 +226,9 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
public bool? EnableImages { get; set; }
+ [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+ public bool? EnableUserData { get; set; }
+
[ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? ImageTypeLimit { get; set; }
diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs
index 97a81b790..ce7905b42 100644
--- a/MediaBrowser.Api/UserLibrary/ItemsService.cs
+++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs
@@ -149,39 +149,35 @@ namespace MediaBrowser.Api.UserLibrary
item = user == null ? _libraryManager.RootFolder : user.RootFolder;
}
- // Default list type = children
-
- var folder = item as Folder;
- if (folder == null)
- {
- folder = user == null ? _libraryManager.RootFolder : _libraryManager.GetUserRootFolder();
- }
-
if (!string.IsNullOrEmpty(request.Ids))
{
- request.Recursive = true;
var query = GetItemsQuery(request, user);
- var result = await folder.GetItems(query).ConfigureAwait(false);
-
- if (string.IsNullOrWhiteSpace(request.SortBy))
+ var specificItems = _libraryManager.GetItemList(query).ToArray();
+ if (query.SortBy.Length == 0)
{
var ids = query.ItemIds.ToList();
// Try to preserve order
- result.Items = result.Items.OrderBy(i => ids.IndexOf(i.Id.ToString("N"))).ToArray();
+ specificItems = specificItems.OrderBy(i => ids.IndexOf(i.Id.ToString("N"))).ToArray();
}
-
- return result;
+ return new QueryResult<BaseItem>
+ {
+ Items = specificItems.ToArray(),
+ TotalRecordCount = specificItems.Length
+ };
}
- if (request.Recursive)
+ // Default list type = children
+
+ var folder = item as Folder;
+ if (folder == null)
{
- return await folder.GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
+ folder = user == null ? _libraryManager.RootFolder : _libraryManager.GetUserRootFolder();
}
- if (user == null)
+ if (request.Recursive || !string.IsNullOrEmpty(request.Ids) || user == null)
{
- return await folder.GetItems(GetItemsQuery(request, null)).ConfigureAwait(false);
+ return await folder.GetItems(GetItemsQuery(request, user)).ConfigureAwait(false);
}
var userRoot = item as UserRootFolder;
diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
index 3be11bdc5..c392ef463 100644
--- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
+++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs
@@ -12,6 +12,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using CommonIO;
+using MediaBrowser.Controller.Providers;
namespace MediaBrowser.Api.UserLibrary
{
@@ -244,6 +246,9 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string EnableImageTypes { get; set; }
+ [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+ public bool? EnableUserData { get; set; }
+
public GetLatestMedia()
{
Limit = 20;
@@ -262,14 +267,16 @@ namespace MediaBrowser.Api.UserLibrary
private readonly ILibraryManager _libraryManager;
private readonly IDtoService _dtoService;
private readonly IUserViewManager _userViewManager;
+ private readonly IFileSystem _fileSystem;
- public UserLibraryService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, IUserViewManager userViewManager)
+ public UserLibraryService(IUserManager userManager, ILibraryManager libraryManager, IUserDataManager userDataRepository, IDtoService dtoService, IUserViewManager userViewManager, IFileSystem fileSystem)
{
_userManager = userManager;
_libraryManager = libraryManager;
_userDataRepository = userDataRepository;
_dtoService = dtoService;
_userViewManager = userViewManager;
+ _fileSystem = fileSystem;
}
/// <summary>
@@ -426,12 +433,14 @@ namespace MediaBrowser.Api.UserLibrary
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
- public object Get(GetItem request)
+ public async Task<object> Get(GetItem request)
{
var user = _userManager.GetUserById(request.UserId);
var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id);
+ await RefreshItemOnDemandIfNeeded(item).ConfigureAwait(false);
+
var dtoOptions = GetDtoOptions(request);
var result = _dtoService.GetBaseItemDto(item, dtoOptions, user);
@@ -439,6 +448,27 @@ namespace MediaBrowser.Api.UserLibrary
return ToOptimizedSerializedResultUsingCache(result);
}
+ private async Task RefreshItemOnDemandIfNeeded(BaseItem item)
+ {
+ if (item is Person)
+ {
+ var hasMetdata = !string.IsNullOrWhiteSpace(item.Overview) && item.HasImage(ImageType.Primary);
+ var performFullRefresh = !hasMetdata && (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= 3;
+
+ if (!hasMetdata)
+ {
+ var options = new MetadataRefreshOptions(_fileSystem)
+ {
+ MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
+ ImageRefreshMode = ImageRefreshMode.FullRefresh,
+ ForceSave = performFullRefresh
+ };
+
+ await item.RefreshMetadata(options, CancellationToken.None).ConfigureAwait(false);
+ }
+ }
+ }
+
/// <summary>
/// Gets the specified request.
/// </summary>
diff --git a/MediaBrowser.Controller/Dto/DtoOptions.cs b/MediaBrowser.Controller/Dto/DtoOptions.cs
index d627cc67a..e69b64948 100644
--- a/MediaBrowser.Controller/Dto/DtoOptions.cs
+++ b/MediaBrowser.Controller/Dto/DtoOptions.cs
@@ -19,12 +19,16 @@ namespace MediaBrowser.Controller.Dto
public bool EnableImages { get; set; }
public bool AddProgramRecordingInfo { get; set; }
public string DeviceId { get; set; }
+ public bool EnableUserData { get; set; }
+ public bool AddCurrentProgram { get; set; }
public DtoOptions()
{
Fields = new List<ItemFields>();
ImageTypeLimit = int.MaxValue;
EnableImages = true;
+ EnableUserData = true;
+ AddCurrentProgram = true;
Fields = Enum.GetNames(typeof (ItemFields))
.Select(i => (ItemFields) Enum.Parse(typeof (ItemFields), i, true))
diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs
index 7b4231010..efc450248 100644
--- a/MediaBrowser.Controller/Entities/AggregateFolder.cs
+++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs
@@ -5,6 +5,8 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
+using System.Threading;
+using System.Threading.Tasks;
using CommonIO;
using MediaBrowser.Controller.Providers;
@@ -67,6 +69,31 @@ namespace MediaBrowser.Controller.Entities
return CreateResolveArgs(directoryService, true).FileSystemChildren;
}
+ private List<Guid> _childrenIds = null;
+ private readonly object _childIdsLock = new object();
+ protected override IEnumerable<BaseItem> LoadChildren()
+ {
+ lock (_childIdsLock)
+ {
+ if (_childrenIds == null || _childrenIds.Count == 0)
+ {
+ var list = base.LoadChildren().ToList();
+ _childrenIds = list.Select(i => i.Id).ToList();
+ return list;
+ }
+
+ return _childrenIds.Select(LibraryManager.GetItemById).Where(i => i != null).ToList();
+ }
+ }
+
+ private void ClearCache()
+ {
+ lock (_childIdsLock)
+ {
+ _childrenIds = null;
+ }
+ }
+
private bool _requiresRefresh;
public override bool RequiresRefresh()
{
@@ -89,6 +116,8 @@ namespace MediaBrowser.Controller.Entities
public override bool BeforeMetadataRefresh()
{
+ ClearCache();
+
var changed = base.BeforeMetadataRefresh() || _requiresRefresh;
_requiresRefresh = false;
return changed;
@@ -96,9 +125,11 @@ namespace MediaBrowser.Controller.Entities
private ItemResolveArgs CreateResolveArgs(IDirectoryService directoryService, bool setPhysicalLocations)
{
+ ClearCache();
+
var path = ContainingFolderPath;
- var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths , directoryService)
+ var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, directoryService)
{
FileInfo = FileSystem.GetDirectoryInfo(path),
Path = path,
@@ -135,7 +166,22 @@ namespace MediaBrowser.Controller.Entities
return args;
}
-
+
+ protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
+ {
+ return base.GetNonCachedChildren(directoryService).Concat(_virtualChildren);
+ }
+
+ protected override async Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
+ {
+ ClearCache();
+
+ await base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService)
+ .ConfigureAwait(false);
+
+ ClearCache();
+ }
+
/// <summary>
/// Adds the virtual child.
/// </summary>
@@ -152,15 +198,6 @@ namespace MediaBrowser.Controller.Entities
}
/// <summary>
- /// Get the children of this folder from the actual file system
- /// </summary>
- /// <returns>IEnumerable{BaseItem}.</returns>
- protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
- {
- return base.GetNonCachedChildren(directoryService).Concat(_virtualChildren);
- }
-
- /// <summary>
/// Finds the virtual child.
/// </summary>
/// <param name="id">The id.</param>
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs
index 1897511af..1af55a389 100644
--- a/MediaBrowser.Controller/Entities/Audio/Audio.cs
+++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs
@@ -5,9 +5,11 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo;
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Channels;
namespace MediaBrowser.Controller.Entities.Audio
@@ -47,7 +49,7 @@ namespace MediaBrowser.Controller.Entities.Audio
}
[IgnoreDataMember]
- public override bool EnableForceSaveOnDateModifiedChange
+ public override bool EnableRefreshOnDateModifiedChange
{
get { return true; }
}
@@ -266,6 +268,11 @@ namespace MediaBrowser.Controller.Entities.Audio
Size = i.Size
};
+ if (info.Protocol == MediaProtocol.File)
+ {
+ info.ETag = i.DateModified.Ticks.ToString(CultureInfo.InvariantCulture).GetMD5().ToString("N");
+ }
+
if (string.IsNullOrEmpty(info.Container))
{
if (!string.IsNullOrWhiteSpace(i.Path) && locationType != LocationType.Remote && locationType != LocationType.Virtual)
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index 6790a1bcf..81d1deaa2 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -169,13 +169,9 @@ namespace MediaBrowser.Controller.Entities.Audio
list.Add("Artist-" + (item.Name ?? string.Empty).RemoveDiacritics());
return list;
}
-
- public override string PresentationUniqueKey
+ public override string CreatePresentationUniqueKey()
{
- get
- {
- return "Artist-" + (Name ?? string.Empty).RemoveDiacritics();
- }
+ return "Artist-" + (Name ?? string.Empty).RemoveDiacritics();
}
protected override bool GetBlockUnratedValue(UserPolicy config)
{
@@ -274,5 +270,54 @@ namespace MediaBrowser.Controller.Entities.Audio
return false;
}
}
+
+ public static string GetPath(string name, bool normalizeName = true)
+ {
+ // Trim the period at the end because windows will have a hard time with that
+ var validName = normalizeName ?
+ FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
+ name;
+
+ return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.ArtistsPath, validName);
+ }
+
+ private string GetRebasedPath()
+ {
+ return GetPath(System.IO.Path.GetFileName(Path), false);
+ }
+
+ public override bool RequiresRefresh()
+ {
+ if (IsAccessedByName)
+ {
+ var newPath = GetRebasedPath();
+ if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+ {
+ Logger.Debug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath);
+ return true;
+ }
+ }
+ return base.RequiresRefresh();
+ }
+
+ /// <summary>
+ /// This is called before any metadata refresh and returns true or false indicating if changes were made
+ /// </summary>
+ public override bool BeforeMetadataRefresh()
+ {
+ var hasChanges = base.BeforeMetadataRefresh();
+
+ if (IsAccessedByName)
+ {
+ var newPath = GetRebasedPath();
+ if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+ {
+ Path = newPath;
+ hasChanges = true;
+ }
+ }
+
+ return hasChanges;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
index 798bc79fb..bd991d9f4 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
@@ -18,13 +18,9 @@ namespace MediaBrowser.Controller.Entities.Audio
list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
return list;
}
-
- public override string PresentationUniqueKey
+ public override string CreatePresentationUniqueKey()
{
- get
- {
- return GetUserDataKeys()[0];
- }
+ return GetUserDataKeys()[0];
}
[IgnoreDataMember]
@@ -96,5 +92,48 @@ namespace MediaBrowser.Controller.Entities.Audio
return LibraryManager.GetItemList(query);
}
+
+ public static string GetPath(string name, bool normalizeName = true)
+ {
+ // Trim the period at the end because windows will have a hard time with that
+ var validName = normalizeName ?
+ FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
+ name;
+
+ return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.MusicGenrePath, validName);
+ }
+
+ private string GetRebasedPath()
+ {
+ return GetPath(System.IO.Path.GetFileName(Path), false);
+ }
+
+ public override bool RequiresRefresh()
+ {
+ var newPath = GetRebasedPath();
+ if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+ {
+ Logger.Debug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath);
+ return true;
+ }
+ return base.RequiresRefresh();
+ }
+
+ /// <summary>
+ /// This is called before any metadata refresh and returns true or false indicating if changes were made
+ /// </summary>
+ public override bool BeforeMetadataRefresh()
+ {
+ var hasChanges = base.BeforeMetadataRefresh();
+
+ var newPath = GetRebasedPath();
+ if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+ {
+ Path = newPath;
+ hasChanges = true;
+ }
+
+ return hasChanges;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 8d00f38be..cbbb9a89a 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -455,7 +455,7 @@ namespace MediaBrowser.Controller.Entities
public DateTime DateLastRefreshed { get; set; }
[IgnoreDataMember]
- public virtual bool EnableForceSaveOnDateModifiedChange
+ public virtual bool EnableRefreshOnDateModifiedChange
{
get { return false; }
}
@@ -951,7 +951,7 @@ namespace MediaBrowser.Controller.Entities
.Where(i => !i.IsDirectory && string.Equals(FileSystem.GetFileNameWithoutExtension(i), ThemeSongFilename, StringComparison.OrdinalIgnoreCase))
);
- return LibraryManager.ResolvePaths(files, directoryService, null)
+ return LibraryManager.ResolvePaths(files, directoryService, null, new LibraryOptions())
.OfType<Audio.Audio>()
.Select(audio =>
{
@@ -981,7 +981,7 @@ namespace MediaBrowser.Controller.Entities
.Where(i => string.Equals(i.Name, ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
.SelectMany(i => directoryService.GetFiles(i.FullName));
- return LibraryManager.ResolvePaths(files, directoryService, null)
+ return LibraryManager.ResolvePaths(files, directoryService, null, new LibraryOptions())
.OfType<Video>()
.Select(item =>
{
@@ -1194,10 +1194,17 @@ namespace MediaBrowser.Controller.Entities
get { return null; }
}
+ public virtual string CreatePresentationUniqueKey()
+ {
+ return Id.ToString("N");
+ }
+
[IgnoreDataMember]
- public virtual string PresentationUniqueKey
+ public string PresentationUniqueKey { get; set; }
+
+ public string GetPresentationUniqueKey()
{
- get { return Id.ToString("N"); }
+ return PresentationUniqueKey ?? CreatePresentationUniqueKey();
}
public virtual bool RequiresRefresh()
@@ -2206,6 +2213,15 @@ namespace MediaBrowser.Controller.Entities
}
}
+ [IgnoreDataMember]
+ public virtual bool StopRefreshIfLocalMetadataFound
+ {
+ get
+ {
+ return true;
+ }
+ }
+
public virtual IEnumerable<Guid> GetIdsForAncestorQuery()
{
return new[] { Id };
diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs
index 59ab95437..56f9fa784 100644
--- a/MediaBrowser.Controller/Entities/Book.cs
+++ b/MediaBrowser.Controller/Entities/Book.cs
@@ -35,7 +35,7 @@ namespace MediaBrowser.Controller.Entities
}
[IgnoreDataMember]
- public override bool EnableForceSaveOnDateModifiedChange
+ public override bool EnableRefreshOnDateModifiedChange
{
get { return true; }
}
diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
index 8bf9919f2..e120f2e23 100644
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs
@@ -3,11 +3,15 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using CommonIO;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Serialization;
using MoreLinq;
namespace MediaBrowser.Controller.Entities
@@ -18,6 +22,8 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public class CollectionFolder : Folder, ICollectionFolder
{
+ public static IXmlSerializer XmlSerializer { get; set; }
+
public CollectionFolder()
{
PhysicalLocationsList = new List<string>();
@@ -39,6 +45,71 @@ namespace MediaBrowser.Controller.Entities
public string CollectionType { get; set; }
+ private static readonly Dictionary<string, LibraryOptions> LibraryOptions = new Dictionary<string, LibraryOptions>();
+ public LibraryOptions GetLibraryOptions()
+ {
+ lock (LibraryOptions)
+ {
+ LibraryOptions options;
+ if (!LibraryOptions.TryGetValue(Path, out options))
+ {
+ options = LoadLibraryOptions();
+ LibraryOptions[Path] = options;
+ }
+
+ return options;
+ }
+ }
+
+ private LibraryOptions LoadLibraryOptions()
+ {
+ try
+ {
+ var result = XmlSerializer.DeserializeFromFile(typeof(LibraryOptions), GetLibraryOptionsPath(Path)) as LibraryOptions;
+
+ if (result == null)
+ {
+ return new LibraryOptions();
+ }
+
+ return result;
+ }
+ catch (FileNotFoundException)
+ {
+ return new LibraryOptions();
+ }
+ catch (DirectoryNotFoundException)
+ {
+ return new LibraryOptions();
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error loading library options", ex);
+
+ return new LibraryOptions();
+ }
+ }
+
+ private static string GetLibraryOptionsPath(string path)
+ {
+ return System.IO.Path.Combine(path, "options.xml");
+ }
+
+ public void UpdateLibraryOptions(LibraryOptions options)
+ {
+ SaveLibraryOptions(Path, options);
+ }
+
+ public static void SaveLibraryOptions(string path, LibraryOptions options)
+ {
+ lock (LibraryOptions)
+ {
+ LibraryOptions[path] = options;
+
+ XmlSerializer.SerializeToFile(options, GetLibraryOptionsPath(path));
+ }
+ }
+
/// <summary>
/// Allow different display preferences for each collection folder
/// </summary>
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index c1728ce38..b5c76c0eb 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -13,6 +13,7 @@ using System.Threading;
using System.Threading.Tasks;
using CommonIO;
using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Model.Channels;
@@ -273,6 +274,7 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
protected virtual IEnumerable<BaseItem> LoadChildren()
{
+ //Logger.Debug("Loading children from {0} {1} {2}", GetType().Name, Id, Path);
//just load our children from the repo - the library will be validated and maintained in other processes
return GetCachedChildren();
}
@@ -643,8 +645,9 @@ namespace MediaBrowser.Controller.Entities
protected virtual IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
{
var collectionType = LibraryManager.GetContentType(this);
+ var libraryOptions = LibraryManager.GetLibraryOptions(this);
- return LibraryManager.ResolvePaths(GetFileSystemChildren(directoryService), directoryService, this, collectionType);
+ return LibraryManager.ResolvePaths(GetFileSystemChildren(directoryService), directoryService, this, libraryOptions, collectionType);
}
/// <summary>
@@ -699,7 +702,7 @@ namespace MediaBrowser.Controller.Entities
items = GetRecursiveChildren(user, query);
}
- return PostFilterAndSort(items, query);
+ return PostFilterAndSort(items, query, true, true);
}
if (!(this is UserRootFolder) && !(this is AggregateFolder))
@@ -900,7 +903,15 @@ namespace MediaBrowser.Controller.Entities
if (query.ItemIds.Length > 0)
{
var specificItems = query.ItemIds.Select(LibraryManager.GetItemById).Where(i => i != null).ToList();
- return Task.FromResult(PostFilterAndSort(specificItems, query));
+
+ if (query.SortBy.Length == 0)
+ {
+ var ids = query.ItemIds.ToList();
+
+ // Try to preserve order
+ specificItems = specificItems.OrderBy(i => ids.IndexOf(i.Id.ToString("N"))).ToList();
+ }
+ return Task.FromResult(PostFilterAndSort(specificItems, query, true, true));
}
return GetItemsInternal(query);
@@ -956,12 +967,12 @@ namespace MediaBrowser.Controller.Entities
: GetChildren(user, true).Where(filter);
}
- return PostFilterAndSort(items, query);
+ return PostFilterAndSort(items, query, true, true);
}
- protected QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items, InternalItemsQuery query)
+ protected QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items, InternalItemsQuery query, bool collapseBoxSetItems, bool enableSorting)
{
- return UserViewBuilder.PostFilterAndSort(items, this, null, query, LibraryManager, ConfigurationManager);
+ return UserViewBuilder.PostFilterAndSort(items, this, null, query, LibraryManager, ConfigurationManager, collapseBoxSetItems, enableSorting);
}
public virtual IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren)
@@ -1425,7 +1436,7 @@ namespace MediaBrowser.Controller.Entities
itemDto.RecursiveItemCount = allItemsQueryResult.TotalRecordCount;
}
- double recursiveItemCount = allItemsQueryResult.TotalRecordCount;
+ var recursiveItemCount = allItemsQueryResult.TotalRecordCount;
double unplayedCount = unplayedQueryResult.TotalRecordCount;
if (recursiveItemCount > 0)
@@ -1435,6 +1446,14 @@ namespace MediaBrowser.Controller.Entities
dto.Played = dto.PlayedPercentage.Value >= 100;
dto.UnplayedItemCount = unplayedQueryResult.TotalRecordCount;
}
+
+ if (itemDto != null)
+ {
+ if (this is Season || this is MusicAlbum)
+ {
+ itemDto.ChildCount = recursiveItemCount;
+ }
+ }
}
}
} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs
index 54386a179..24910498f 100644
--- a/MediaBrowser.Controller/Entities/Game.cs
+++ b/MediaBrowser.Controller/Entities/Game.cs
@@ -34,7 +34,7 @@ namespace MediaBrowser.Controller.Entities
}
[IgnoreDataMember]
- public override bool EnableForceSaveOnDateModifiedChange
+ public override bool EnableRefreshOnDateModifiedChange
{
get { return true; }
}
diff --git a/MediaBrowser.Controller/Entities/GameGenre.cs b/MediaBrowser.Controller/Entities/GameGenre.cs
index 45e766c0f..6448828fb 100644
--- a/MediaBrowser.Controller/Entities/GameGenre.cs
+++ b/MediaBrowser.Controller/Entities/GameGenre.cs
@@ -16,12 +16,9 @@ namespace MediaBrowser.Controller.Entities
return list;
}
- public override string PresentationUniqueKey
+ public override string CreatePresentationUniqueKey()
{
- get
- {
- return GetUserDataKeys()[0];
- }
+ return GetUserDataKeys()[0];
}
/// <summary>
@@ -87,5 +84,48 @@ namespace MediaBrowser.Controller.Entities
return false;
}
}
+
+ public static string GetPath(string name, bool normalizeName = true)
+ {
+ // Trim the period at the end because windows will have a hard time with that
+ var validName = normalizeName ?
+ FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
+ name;
+
+ return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.GameGenrePath, validName);
+ }
+
+ private string GetRebasedPath()
+ {
+ return GetPath(System.IO.Path.GetFileName(Path), false);
+ }
+
+ public override bool RequiresRefresh()
+ {
+ var newPath = GetRebasedPath();
+ if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+ {
+ Logger.Debug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath);
+ return true;
+ }
+ return base.RequiresRefresh();
+ }
+
+ /// <summary>
+ /// This is called before any metadata refresh and returns true or false indicating if changes were made
+ /// </summary>
+ public override bool BeforeMetadataRefresh()
+ {
+ var hasChanges = base.BeforeMetadataRefresh();
+
+ var newPath = GetRebasedPath();
+ if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+ {
+ Path = newPath;
+ hasChanges = true;
+ }
+
+ return hasChanges;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs
index cc5aebb2a..1736ba8c7 100644
--- a/MediaBrowser.Controller/Entities/Genre.cs
+++ b/MediaBrowser.Controller/Entities/Genre.cs
@@ -19,13 +19,9 @@ namespace MediaBrowser.Controller.Entities
list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
return list;
}
-
- public override string PresentationUniqueKey
+ public override string CreatePresentationUniqueKey()
{
- get
- {
- return GetUserDataKeys()[0];
- }
+ return GetUserDataKeys()[0];
}
/// <summary>
@@ -91,5 +87,48 @@ namespace MediaBrowser.Controller.Entities
return false;
}
}
+
+ public static string GetPath(string name, bool normalizeName = true)
+ {
+ // Trim the period at the end because windows will have a hard time with that
+ var validName = normalizeName ?
+ FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
+ name;
+
+ return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.GenrePath, validName);
+ }
+
+ private string GetRebasedPath()
+ {
+ return GetPath(System.IO.Path.GetFileName(Path), false);
+ }
+
+ public override bool RequiresRefresh()
+ {
+ var newPath = GetRebasedPath();
+ if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+ {
+ Logger.Debug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath);
+ return true;
+ }
+ return base.RequiresRefresh();
+ }
+
+ /// <summary>
+ /// This is called before any metadata refresh and returns true or false indicating if changes were made
+ /// </summary>
+ public override bool BeforeMetadataRefresh()
+ {
+ var hasChanges = base.BeforeMetadataRefresh();
+
+ var newPath = GetRebasedPath();
+ if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+ {
+ Path = newPath;
+ hasChanges = true;
+ }
+
+ return hasChanges;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/IHasMetadata.cs b/MediaBrowser.Controller/Entities/IHasMetadata.cs
index 378c4a390..26f425ff0 100644
--- a/MediaBrowser.Controller/Entities/IHasMetadata.cs
+++ b/MediaBrowser.Controller/Entities/IHasMetadata.cs
@@ -32,7 +32,7 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
/// <value>The date last refreshed.</value>
DateTime DateLastRefreshed { get; set; }
-
+
/// <summary>
/// This is called before any metadata refresh and returns true or false indicating if changes were made
/// </summary>
@@ -52,6 +52,12 @@ namespace MediaBrowser.Controller.Entities
bool RequiresRefresh();
- bool EnableForceSaveOnDateModifiedChange { get; }
+ bool EnableRefreshOnDateModifiedChange { get; }
+
+ string PresentationUniqueKey { get; set; }
+
+ string GetPresentationUniqueKey();
+ string CreatePresentationUniqueKey();
+ bool StopRefreshIfLocalMetadataFound { get; }
}
}
diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
index 69cab5ec5..deea63112 100644
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
@@ -37,6 +37,7 @@ namespace MediaBrowser.Controller.Entities
public string[] Genres { get; set; }
public string[] Keywords { get; set; }
+ public bool? IsSpecialSeason { get; set; }
public bool? IsMissing { get; set; }
public bool? IsUnaired { get; set; }
public bool? IsVirtualUnaired { get; set; }
@@ -50,6 +51,7 @@ namespace MediaBrowser.Controller.Entities
public string PresentationUniqueKey { get; set; }
public string Path { get; set; }
+ public string PathNotStartsWith { get; set; }
public string Name { get; set; }
public string SlugName { get; set; }
diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
index 4effc162e..6d5278f1f 100644
--- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
+++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs
@@ -62,6 +62,26 @@ namespace MediaBrowser.Controller.Entities.Movies
return UnratedItem.Movie;
}
+ protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
+ {
+ if (IsLegacyBoxSet)
+ {
+ return base.GetNonCachedChildren(directoryService);
+ }
+ return new List<BaseItem>();
+ }
+
+ protected override IEnumerable<BaseItem> LoadChildren()
+ {
+ if (IsLegacyBoxSet)
+ {
+ return base.LoadChildren();
+ }
+
+ // Save a trip to the database
+ return new List<BaseItem>();
+ }
+
[IgnoreDataMember]
public override bool IsPreSorted
{
@@ -76,7 +96,21 @@ namespace MediaBrowser.Controller.Entities.Movies
{
get
{
- return true;
+ if (IsLegacyBoxSet)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ [IgnoreDataMember]
+ private bool IsLegacyBoxSet
+ {
+ get
+ {
+ return !FileSystem.ContainsSubPath(ConfigurationManager.ApplicationPaths.DataPath, Path);
}
}
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs
index c7a833c58..f0270497c 100644
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs
@@ -179,5 +179,15 @@ namespace MediaBrowser.Controller.Entities.Movies
return list;
}
+
+ [IgnoreDataMember]
+ public override bool StopRefreshIfLocalMetadataFound
+ {
+ get
+ {
+ // Need people id's from internet metadata
+ return false;
+ }
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs
index 8ef0d70bf..f21bc0a71 100644
--- a/MediaBrowser.Controller/Entities/Person.cs
+++ b/MediaBrowser.Controller/Entities/Person.cs
@@ -26,13 +26,9 @@ namespace MediaBrowser.Controller.Entities
list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
return list;
}
-
- public override string PresentationUniqueKey
+ public override string CreatePresentationUniqueKey()
{
- get
- {
- return GetUserDataKeys()[0];
- }
+ return GetUserDataKeys()[0];
}
public PersonLookupInfo GetLookupInfo()
@@ -126,6 +122,64 @@ namespace MediaBrowser.Controller.Entities
return false;
}
}
+
+ public static string GetPath(string name, bool normalizeName = true)
+ {
+ // Trim the period at the end because windows will have a hard time with that
+ var validFilename = normalizeName ?
+ FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
+ name;
+
+ string subFolderPrefix = null;
+
+ foreach (char c in validFilename)
+ {
+ if (char.IsLetterOrDigit(c))
+ {
+ subFolderPrefix = c.ToString();
+ break;
+ }
+ }
+
+ var path = ConfigurationManager.ApplicationPaths.PeoplePath;
+
+ return string.IsNullOrEmpty(subFolderPrefix) ?
+ System.IO.Path.Combine(path, validFilename) :
+ System.IO.Path.Combine(path, subFolderPrefix, validFilename);
+ }
+
+ private string GetRebasedPath()
+ {
+ return GetPath(System.IO.Path.GetFileName(Path), false);
+ }
+
+ public override bool RequiresRefresh()
+ {
+ var newPath = GetRebasedPath();
+ if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+ {
+ Logger.Debug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath);
+ return true;
+ }
+ return base.RequiresRefresh();
+ }
+
+ /// <summary>
+ /// This is called before any metadata refresh and returns true or false indicating if changes were made
+ /// </summary>
+ public override bool BeforeMetadataRefresh()
+ {
+ var hasChanges = base.BeforeMetadataRefresh();
+
+ var newPath = GetRebasedPath();
+ if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+ {
+ Path = newPath;
+ hasChanges = true;
+ }
+
+ return hasChanges;
+ }
}
/// <summary>
diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs
index 804ea04a5..965616eb5 100644
--- a/MediaBrowser.Controller/Entities/Photo.cs
+++ b/MediaBrowser.Controller/Entities/Photo.cs
@@ -52,7 +52,7 @@ namespace MediaBrowser.Controller.Entities
}
[IgnoreDataMember]
- public override bool EnableForceSaveOnDateModifiedChange
+ public override bool EnableRefreshOnDateModifiedChange
{
get { return true; }
}
diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs
index 762798b55..04b09b744 100644
--- a/MediaBrowser.Controller/Entities/Studio.cs
+++ b/MediaBrowser.Controller/Entities/Studio.cs
@@ -18,13 +18,9 @@ namespace MediaBrowser.Controller.Entities
list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
return list;
}
-
- public override string PresentationUniqueKey
+ public override string CreatePresentationUniqueKey()
{
- get
- {
- return GetUserDataKeys()[0];
- }
+ return GetUserDataKeys()[0];
}
/// <summary>
@@ -89,5 +85,48 @@ namespace MediaBrowser.Controller.Entities
return false;
}
}
+
+ public static string GetPath(string name, bool normalizeName = true)
+ {
+ // Trim the period at the end because windows will have a hard time with that
+ var validName = normalizeName ?
+ FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
+ name;
+
+ return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.StudioPath, validName);
+ }
+
+ private string GetRebasedPath()
+ {
+ return GetPath(System.IO.Path.GetFileName(Path), false);
+ }
+
+ public override bool RequiresRefresh()
+ {
+ var newPath = GetRebasedPath();
+ if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+ {
+ Logger.Debug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath);
+ return true;
+ }
+ return base.RequiresRefresh();
+ }
+
+ /// <summary>
+ /// This is called before any metadata refresh and returns true or false indicating if changes were made
+ /// </summary>
+ public override bool BeforeMetadataRefresh()
+ {
+ var hasChanges = base.BeforeMetadataRefresh();
+
+ var newPath = GetRebasedPath();
+ if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+ {
+ Path = newPath;
+ hasChanges = true;
+ }
+
+ return hasChanges;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index f6ca19005..842b2fd60 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -85,7 +85,11 @@ namespace MediaBrowser.Controller.Entities.TV
public override int GetChildCount(User user)
{
- return GetChildren(user, true).Count();
+ Logger.Debug("Season {0} getting child cound", (Path ?? Name));
+ var result = GetChildren(user, true).Count();
+ Logger.Debug("Season {0} child cound: ", result);
+
+ return result;
}
/// <summary>
@@ -114,22 +118,18 @@ namespace MediaBrowser.Controller.Entities.TV
}
}
- [IgnoreDataMember]
- public override string PresentationUniqueKey
+ public override string CreatePresentationUniqueKey()
{
- get
+ if (IndexNumber.HasValue)
{
- if (IndexNumber.HasValue)
+ var series = Series;
+ if (series != null)
{
- var series = Series;
- if (series != null)
- {
- return series.PresentationUniqueKey + "-" + (IndexNumber ?? 0).ToString("000");
- }
+ return series.PresentationUniqueKey + "-" + (IndexNumber ?? 0).ToString("000");
}
-
- return base.PresentationUniqueKey;
}
+
+ return base.CreatePresentationUniqueKey();
}
/// <summary>
@@ -141,24 +141,6 @@ namespace MediaBrowser.Controller.Entities.TV
return IndexNumber != null ? IndexNumber.Value.ToString("0000") : Name;
}
- [IgnoreDataMember]
- public bool IsMissingSeason
- {
- get { return (IsVirtualItem) && !IsUnaired; }
- }
-
- [IgnoreDataMember]
- public bool IsVirtualUnaired
- {
- get { return (IsVirtualItem) && IsUnaired; }
- }
-
- [IgnoreDataMember]
- public bool IsSpecialSeason
- {
- get { return (IndexNumber ?? -1) == 0; }
- }
-
protected override Task<QueryResult<BaseItem>> GetItemsInternal(InternalItemsQuery query)
{
if (query.User == null)
@@ -170,10 +152,15 @@ namespace MediaBrowser.Controller.Entities.TV
Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
+ var id = Guid.NewGuid().ToString("N");
+
+ Logger.Debug("Season.GetItemsInternal entering GetEpisodes. Request id: " + id);
var items = GetEpisodes(user).Where(filter);
- var result = PostFilterAndSort(items, query);
+ Logger.Debug("Season.GetItemsInternal entering PostFilterAndSort. Request id: " + id);
+ var result = PostFilterAndSort(items, query, false, false);
+ Logger.Debug("Season.GetItemsInternal complete. Request id: " + id);
return Task.FromResult(result);
}
@@ -184,19 +171,17 @@ namespace MediaBrowser.Controller.Entities.TV
/// <returns>IEnumerable{Episode}.</returns>
public IEnumerable<Episode> GetEpisodes(User user)
{
- var config = user.Configuration;
-
- return GetEpisodes(Series, user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes);
+ return GetEpisodes(Series, user);
}
- public IEnumerable<Episode> GetEpisodes(Series series, User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)
+ public IEnumerable<Episode> GetEpisodes(Series series, User user)
{
- return GetEpisodes(series, user, includeMissingEpisodes, includeVirtualUnairedEpisodes, null);
+ return GetEpisodes(series, user, null);
}
- public IEnumerable<Episode> GetEpisodes(Series series, User user, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable<Episode> allSeriesEpisodes)
+ public IEnumerable<Episode> GetEpisodes(Series series, User user, IEnumerable<Episode> allSeriesEpisodes)
{
- return series.GetEpisodes(user, this, includeMissingEpisodes, includeVirtualUnairedEpisodes, allSeriesEpisodes);
+ return series.GetSeasonEpisodes(user, this, allSeriesEpisodes);
}
public IEnumerable<Episode> GetEpisodes()
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index ad35b3d36..4915cfedc 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -96,19 +96,29 @@ namespace MediaBrowser.Controller.Entities.TV
}
}
- [IgnoreDataMember]
- public override string PresentationUniqueKey
+ public override string CreatePresentationUniqueKey()
{
- get
+ var userdatakeys = GetUserDataKeys();
+
+ if (userdatakeys.Count > 1)
{
- var userdatakeys = GetUserDataKeys();
+ return AddLibrariesToPresentationUniqueKey(userdatakeys[0]);
+ }
+ return base.CreatePresentationUniqueKey();
+ }
- if (userdatakeys.Count > 1)
- {
- return userdatakeys[0];
- }
- return base.PresentationUniqueKey;
+ private string AddLibrariesToPresentationUniqueKey(string key)
+ {
+ var folders = LibraryManager.GetCollectionFolders(this)
+ .Select(i => i.Id.ToString("N"))
+ .ToArray();
+
+ if (folders.Length == 0)
+ {
+ return key;
}
+
+ return key + "-" + string.Join("-", folders);
}
private static string GetUniqueSeriesKey(BaseItem series)
@@ -117,7 +127,7 @@ namespace MediaBrowser.Controller.Entities.TV
{
return series.Id.ToString("N");
}
- return series.PresentationUniqueKey;
+ return series.GetPresentationUniqueKey();
}
public override int GetChildCount(User user)
@@ -197,7 +207,30 @@ namespace MediaBrowser.Controller.Entities.TV
{
var config = user.Configuration;
- return GetSeasons(user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes);
+ var seriesKey = GetUniqueSeriesKey(this);
+
+ Logger.Debug("GetSeasons SeriesKey: {0}", seriesKey);
+ var query = new InternalItemsQuery(user)
+ {
+ AncestorWithPresentationUniqueKey = seriesKey,
+ IncludeItemTypes = new[] {typeof (Season).Name},
+ SortBy = new[] {ItemSortBy.SortName}
+ };
+
+ if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes)
+ {
+ query.IsVirtualItem = false;
+ }
+ else if (!config.DisplayMissingEpisodes)
+ {
+ query.IsMissing = false;
+ }
+ else if (!config.DisplayUnairedEpisodes)
+ {
+ query.IsVirtualUnaired = false;
+ }
+
+ return LibraryManager.GetItemList(query).Cast<Season>();
}
protected override Task<QueryResult<BaseItem>> GetItemsInternal(InternalItemsQuery query)
@@ -227,55 +260,43 @@ namespace MediaBrowser.Controller.Entities.TV
Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
var items = GetSeasons(user).Where(filter);
- var result = PostFilterAndSort(items, query);
+ var result = PostFilterAndSort(items, query, false, true);
return Task.FromResult(result);
}
- public IEnumerable<Season> GetSeasons(User user, bool includeMissingSeasons, bool includeVirtualUnaired)
+ public IEnumerable<Episode> GetEpisodes(User user)
{
- IEnumerable<Season> seasons;
+ var seriesKey = GetUniqueSeriesKey(this);
+ Logger.Debug("GetEpisodes seriesKey: {0}", seriesKey);
- seasons = LibraryManager.GetItemList(new InternalItemsQuery(user)
+ var query = new InternalItemsQuery(user)
{
- AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(this),
- IncludeItemTypes = new[] { typeof(Season).Name },
- SortBy = new[] { ItemSortBy.SortName }
-
- }).Cast<Season>();
-
- if (!includeMissingSeasons)
+ AncestorWithPresentationUniqueKey = seriesKey,
+ IncludeItemTypes = new[] {typeof (Episode).Name, typeof (Season).Name},
+ SortBy = new[] {ItemSortBy.SortName}
+ };
+ var config = user.Configuration;
+ if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes)
{
- seasons = seasons.Where(i => !(i.IsMissingSeason));
+ query.IsVirtualItem = false;
}
- if (!includeVirtualUnaired)
+ else if (!config.DisplayMissingEpisodes)
{
- seasons = seasons.Where(i => !i.IsVirtualUnaired);
+ query.IsMissing = false;
}
-
- return seasons;
- }
-
- public IEnumerable<Episode> GetEpisodes(User user)
- {
- var config = user.Configuration;
-
- return GetEpisodes(user, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes);
- }
-
- public IEnumerable<Episode> GetEpisodes(User user, bool includeMissing, bool includeVirtualUnaired)
- {
- var allItems = LibraryManager.GetItemList(new InternalItemsQuery(user)
+ else if (!config.DisplayUnairedEpisodes)
{
- AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(this),
- IncludeItemTypes = new[] { typeof(Episode).Name, typeof(Season).Name },
- SortBy = new[] { ItemSortBy.SortName }
+ query.IsVirtualUnaired = false;
+ }
- }).ToList();
+ var allItems = LibraryManager.GetItemList(query).ToList();
+
+ Logger.Debug("GetEpisodes return {0} items from database", allItems.Count);
var allSeriesEpisodes = allItems.OfType<Episode>().ToList();
var allEpisodes = allItems.OfType<Season>()
- .SelectMany(i => i.GetEpisodes(this, user, includeMissing, includeVirtualUnaired, allSeriesEpisodes))
+ .SelectMany(i => i.GetEpisodes(this, user, allSeriesEpisodes))
.Reverse()
.ToList();
@@ -352,78 +373,68 @@ namespace MediaBrowser.Controller.Entities.TV
progress.Report(100);
}
- public IEnumerable<Episode> GetEpisodes(User user, Season season)
- {
- var config = user.Configuration;
-
- return GetEpisodes(user, season, config.DisplayMissingEpisodes, config.DisplayUnairedEpisodes);
- }
-
private IEnumerable<Episode> GetAllEpisodes(User user)
{
- return LibraryManager.GetItemList(new InternalItemsQuery(user)
+ Logger.Debug("Series.GetAllEpisodes entering GetItemList");
+
+ var result = LibraryManager.GetItemList(new InternalItemsQuery(user)
{
AncestorWithPresentationUniqueKey = GetUniqueSeriesKey(this),
IncludeItemTypes = new[] { typeof(Episode).Name },
SortBy = new[] { ItemSortBy.SortName }
- }).Cast<Episode>();
- }
+ }).Cast<Episode>().ToList();
- public IEnumerable<Episode> GetEpisodes(User user, Season parentSeason, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes)
- {
- IEnumerable<Episode> episodes = GetAllEpisodes(user);
+ Logger.Debug("Series.GetAllEpisodes returning {0} episodes", result.Count);
- return GetEpisodes(user, parentSeason, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes);
+ return result;
}
- public IEnumerable<Episode> GetEpisodes(User user, Season parentSeason, bool includeMissingEpisodes, bool includeVirtualUnairedEpisodes, IEnumerable<Episode> allSeriesEpisodes)
+ public IEnumerable<Episode> GetSeasonEpisodes(User user, Season parentSeason)
{
- if (allSeriesEpisodes == null)
+ var seriesKey = GetUniqueSeriesKey(this);
+ Logger.Debug("GetSeasonEpisodes seriesKey: {0}", seriesKey);
+
+ var query = new InternalItemsQuery(user)
{
- return GetEpisodes(user, parentSeason, includeMissingEpisodes, includeVirtualUnairedEpisodes);
+ AncestorWithPresentationUniqueKey = seriesKey,
+ IncludeItemTypes = new[] { typeof(Episode).Name },
+ SortBy = new[] { ItemSortBy.SortName }
+ };
+ var config = user.Configuration;
+ if (!config.DisplayMissingEpisodes && !config.DisplayUnairedEpisodes)
+ {
+ query.IsVirtualItem = false;
}
-
- var episodes = FilterEpisodesBySeason(allSeriesEpisodes, parentSeason, ConfigurationManager.Configuration.DisplaySpecialsWithinSeasons);
-
- if (!includeMissingEpisodes)
+ else if (!config.DisplayMissingEpisodes)
{
- episodes = episodes.Where(i => !i.IsMissingEpisode);
+ query.IsMissing = false;
}
- if (!includeVirtualUnairedEpisodes)
+ else if (!config.DisplayUnairedEpisodes)
{
- episodes = episodes.Where(i => !i.IsVirtualUnaired);
+ query.IsVirtualUnaired = false;
}
- var sortBy = (parentSeason.IndexNumber ?? -1) == 0 ? ItemSortBy.SortName : ItemSortBy.AiredEpisodeOrder;
+ var allItems = LibraryManager.GetItemList(query).OfType<Episode>();
- return LibraryManager.Sort(episodes, user, new[] { sortBy }, SortOrder.Ascending)
- .Cast<Episode>();
+ return GetSeasonEpisodes(user, parentSeason, allItems);
}
- /// <summary>
- /// Filters the episodes by season.
- /// </summary>
- public static IEnumerable<Episode> FilterEpisodesBySeason(IEnumerable<Episode> episodes, int seasonNumber, bool includeSpecials)
+ public IEnumerable<Episode> GetSeasonEpisodes(User user, Season parentSeason, IEnumerable<Episode> allSeriesEpisodes)
{
- if (!includeSpecials || seasonNumber < 1)
+ if (allSeriesEpisodes == null)
{
- return episodes.Where(i => (i.ParentIndexNumber ?? -1) == seasonNumber);
+ Logger.Debug("GetSeasonEpisodes allSeriesEpisodes is null");
+ return GetSeasonEpisodes(user, parentSeason);
}
- return episodes.Where(i =>
- {
- var episode = i;
-
- if (episode != null)
- {
- var currentSeasonNumber = episode.AiredSeasonNumber;
+ Logger.Debug("GetSeasonEpisodes FilterEpisodesBySeason");
+ var episodes = FilterEpisodesBySeason(allSeriesEpisodes, parentSeason, ConfigurationManager.Configuration.DisplaySpecialsWithinSeasons);
- return currentSeasonNumber.HasValue && currentSeasonNumber.Value == seasonNumber;
- }
+ var sortBy = (parentSeason.IndexNumber ?? -1) == 0 ? ItemSortBy.SortName : ItemSortBy.AiredEpisodeOrder;
- return false;
- });
+ return LibraryManager.Sort(episodes, user, new[] { sortBy }, SortOrder.Ascending)
+ .Cast<Episode>();
}
/// <summary>
@@ -454,6 +465,32 @@ namespace MediaBrowser.Controller.Entities.TV
});
}
+ /// <summary>
+ /// Filters the episodes by season.
+ /// </summary>
+ public static IEnumerable<Episode> FilterEpisodesBySeason(IEnumerable<Episode> episodes, int seasonNumber, bool includeSpecials)
+ {
+ if (!includeSpecials || seasonNumber < 1)
+ {
+ return episodes.Where(i => (i.ParentIndexNumber ?? -1) == seasonNumber);
+ }
+
+ return episodes.Where(i =>
+ {
+ var episode = i;
+
+ if (episode != null)
+ {
+ var currentSeasonNumber = episode.AiredSeasonNumber;
+
+ return currentSeasonNumber.HasValue && currentSeasonNumber.Value == seasonNumber;
+ }
+
+ return false;
+ });
+ }
+
+
protected override bool GetBlockUnratedValue(UserPolicy config)
{
return config.BlockUnratedItems.Contains(UnratedItem.Series);
@@ -509,5 +546,15 @@ namespace MediaBrowser.Controller.Entities.TV
return list;
}
+
+ [IgnoreDataMember]
+ public override bool StopRefreshIfLocalMetadataFound
+ {
+ get
+ {
+ // Need people id's from internet metadata
+ return false;
+ }
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs
index 6604be977..306ce35ec 100644
--- a/MediaBrowser.Controller/Entities/Trailer.cs
+++ b/MediaBrowser.Controller/Entities/Trailer.cs
@@ -124,5 +124,15 @@ namespace MediaBrowser.Controller.Entities
return list;
}
+
+ [IgnoreDataMember]
+ public override bool StopRefreshIfLocalMetadataFound
+ {
+ get
+ {
+ // Need people id's from internet metadata
+ return false;
+ }
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs
index 4549d0d1d..aff1f5e28 100644
--- a/MediaBrowser.Controller/Entities/UserRootFolder.cs
+++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs
@@ -16,6 +16,31 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public class UserRootFolder : Folder
{
+ private List<Guid> _childrenIds = null;
+ private readonly object _childIdsLock = new object();
+ protected override IEnumerable<BaseItem> LoadChildren()
+ {
+ lock (_childIdsLock)
+ {
+ if (_childrenIds == null)
+ {
+ var list = base.LoadChildren().ToList();
+ _childrenIds = list.Select(i => i.Id).ToList();
+ return list;
+ }
+
+ return _childrenIds.Select(LibraryManager.GetItemById).Where(i => i != null).ToList();
+ }
+ }
+
+ private void ClearCache()
+ {
+ lock (_childIdsLock)
+ {
+ _childrenIds = null;
+ }
+ }
+
protected override async Task<QueryResult<BaseItem>> GetItemsInternal(InternalItemsQuery query)
{
if (query.Recursive)
@@ -33,7 +58,7 @@ namespace MediaBrowser.Controller.Entities
var user = query.User;
Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager);
- return PostFilterAndSort(result.Where(filter), query);
+ return PostFilterAndSort(result.Where(filter), query, true, true);
}
public override int GetChildCount(User user)
@@ -69,6 +94,8 @@ namespace MediaBrowser.Controller.Entities
public override bool BeforeMetadataRefresh()
{
+ ClearCache();
+
var hasChanges = base.BeforeMetadataRefresh();
if (string.Equals("default", Name, StringComparison.OrdinalIgnoreCase))
@@ -80,11 +107,22 @@ namespace MediaBrowser.Controller.Entities
return hasChanges;
}
+ protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
+ {
+ ClearCache();
+
+ return base.GetNonCachedChildren(directoryService);
+ }
+
protected override async Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
{
+ ClearCache();
+
await base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService)
.ConfigureAwait(false);
+ ClearCache();
+
// Not the best way to handle this, but it solves an issue
// CollectionFolders aren't always getting saved after changes
// This means that grabbing the item by Id may end up returning the old one
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index 11ed26931..9f3acc3fc 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -424,7 +424,7 @@ namespace MediaBrowser.Controller.Entities
query.SortBy = new string[] { };
- return PostFilterAndSort(items, parent, null, query);
+ return PostFilterAndSort(items, parent, null, query, false, true);
}
private QueryResult<BaseItem> GetFavoriteSongs(Folder parent, User user, InternalItemsQuery query)
@@ -780,7 +780,7 @@ namespace MediaBrowser.Controller.Entities
{
items = items.Where(i => Filter(i, query.User, query, _userDataManager, _libraryManager));
- return PostFilterAndSort(items, queryParent, null, query, _libraryManager, _config);
+ return PostFilterAndSort(items, queryParent, null, query, _libraryManager, _config, true, true);
}
public static bool FilterItem(BaseItem item, InternalItemsQuery query)
@@ -791,9 +791,11 @@ namespace MediaBrowser.Controller.Entities
private QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items,
BaseItem queryParent,
int? totalRecordLimit,
- InternalItemsQuery query)
+ InternalItemsQuery query,
+ bool collapseBoxSetItems,
+ bool enableSorting)
{
- return PostFilterAndSort(items, queryParent, totalRecordLimit, query, _libraryManager, _config);
+ return PostFilterAndSort(items, queryParent, totalRecordLimit, query, _libraryManager, _config, collapseBoxSetItems, enableSorting);
}
public static QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items,
@@ -801,7 +803,9 @@ namespace MediaBrowser.Controller.Entities
int? totalRecordLimit,
InternalItemsQuery query,
ILibraryManager libraryManager,
- IServerConfigurationManager configurationManager)
+ IServerConfigurationManager configurationManager,
+ bool collapseBoxSetItems,
+ bool enableSorting)
{
var user = query.User;
@@ -810,7 +814,10 @@ namespace MediaBrowser.Controller.Entities
query.IsVirtualUnaired,
query.IsUnaired);
- items = CollapseBoxSetItemsIfNeeded(items, query, queryParent, user, configurationManager);
+ if (collapseBoxSetItems)
+ {
+ items = CollapseBoxSetItemsIfNeeded(items, query, queryParent, user, configurationManager);
+ }
// This must be the last filter
if (!string.IsNullOrEmpty(query.AdjacentTo))
@@ -818,7 +825,7 @@ namespace MediaBrowser.Controller.Entities
items = FilterForAdjacency(items, query.AdjacentTo);
}
- return Sort(items, totalRecordLimit, query, libraryManager);
+ return SortAndPage(items, totalRecordLimit, query, libraryManager, enableSorting);
}
public static IEnumerable<BaseItem> CollapseBoxSetItemsIfNeeded(IEnumerable<BaseItem> items,
@@ -1093,8 +1100,6 @@ namespace MediaBrowser.Controller.Entities
bool? isVirtualUnaired,
bool? isUnaired)
{
- items = FilterVirtualSeasons(items, isMissing, isVirtualUnaired, isUnaired);
-
if (isMissing.HasValue)
{
var val = isMissing.Value;
@@ -1140,65 +1145,14 @@ namespace MediaBrowser.Controller.Entities
return items;
}
- private static IEnumerable<BaseItem> FilterVirtualSeasons(
- IEnumerable<BaseItem> items,
- bool? isMissing,
- bool? isVirtualUnaired,
- bool? isUnaired)
- {
- if (isMissing.HasValue)
- {
- var val = isMissing.Value;
- items = items.Where(i =>
- {
- var e = i as Season;
- if (e != null)
- {
- return (e.IsMissingSeason) == val;
- }
- return true;
- });
- }
-
- if (isUnaired.HasValue)
- {
- var val = isUnaired.Value;
- items = items.Where(i =>
- {
- var e = i as Season;
- if (e != null)
- {
- return e.IsUnaired == val;
- }
- return true;
- });
- }
-
- if (isVirtualUnaired.HasValue)
- {
- var val = isVirtualUnaired.Value;
- items = items.Where(i =>
- {
- var e = i as Season;
- if (e != null)
- {
- return e.IsVirtualUnaired == val;
- }
- return true;
- });
- }
-
- return items;
- }
-
- public static QueryResult<BaseItem> Sort(IEnumerable<BaseItem> items,
+ public static QueryResult<BaseItem> SortAndPage(IEnumerable<BaseItem> items,
int? totalRecordLimit,
InternalItemsQuery query,
- ILibraryManager libraryManager)
+ ILibraryManager libraryManager, bool enableSorting)
{
var user = query.User;
- items = items.DistinctBy(i => i.PresentationUniqueKey, StringComparer.OrdinalIgnoreCase);
+ items = items.DistinctBy(i => i.GetPresentationUniqueKey(), StringComparer.OrdinalIgnoreCase);
if (query.SortBy.Length > 0)
{
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index eba1e466a..8809f155c 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -12,6 +12,7 @@ using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using CommonIO;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Channels;
namespace MediaBrowser.Controller.Entities
@@ -44,24 +45,23 @@ namespace MediaBrowser.Controller.Entities
}
}
- [IgnoreDataMember]
- public override string PresentationUniqueKey
+ public override string CreatePresentationUniqueKey()
{
- get
+ if (!string.IsNullOrWhiteSpace(PrimaryVersionId))
{
- if (!string.IsNullOrWhiteSpace(PrimaryVersionId))
- {
- return PrimaryVersionId;
- }
-
- return base.PresentationUniqueKey;
+ return PrimaryVersionId;
}
+
+ return base.CreatePresentationUniqueKey();
}
[IgnoreDataMember]
- public override bool EnableForceSaveOnDateModifiedChange
+ public override bool EnableRefreshOnDateModifiedChange
{
- get { return true; }
+ get
+ {
+ return VideoType == VideoType.VideoFile || VideoType == VideoType.Iso;
+ }
}
public int? TotalBitrate { get; set; }
@@ -612,6 +612,11 @@ namespace MediaBrowser.Controller.Entities
SupportsDirectStream = i.VideoType == VideoType.VideoFile
};
+ if (info.Protocol == MediaProtocol.File)
+ {
+ info.ETag = i.DateModified.Ticks.ToString(CultureInfo.InvariantCulture).GetMD5().ToString("N");
+ }
+
if (i.IsShortcut)
{
info.Path = i.ShortcutPath;
diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs
index db896f1fc..4197ea93e 100644
--- a/MediaBrowser.Controller/Entities/Year.cs
+++ b/MediaBrowser.Controller/Entities/Year.cs
@@ -112,5 +112,48 @@ namespace MediaBrowser.Controller.Entities
return false;
}
}
+
+ public static string GetPath(string name, bool normalizeName = true)
+ {
+ // Trim the period at the end because windows will have a hard time with that
+ var validName = normalizeName ?
+ FileSystem.GetValidFilename(name).Trim().TrimEnd('.') :
+ name;
+
+ return System.IO.Path.Combine(ConfigurationManager.ApplicationPaths.YearPath, validName);
+ }
+
+ private string GetRebasedPath()
+ {
+ return GetPath(System.IO.Path.GetFileName(Path), false);
+ }
+
+ public override bool RequiresRefresh()
+ {
+ var newPath = GetRebasedPath();
+ if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+ {
+ Logger.Debug("{0} path has changed from {1} to {2}", GetType().Name, Path, newPath);
+ return true;
+ }
+ return base.RequiresRefresh();
+ }
+
+ /// <summary>
+ /// This is called before any metadata refresh and returns true or false indicating if changes were made
+ /// </summary>
+ public override bool BeforeMetadataRefresh()
+ {
+ var hasChanges = base.BeforeMetadataRefresh();
+
+ var newPath = GetRebasedPath();
+ if (!string.Equals(Path, newPath, StringComparison.Ordinal))
+ {
+ Path = newPath;
+ hasChanges = true;
+ }
+
+ return hasChanges;
+ }
}
}
diff --git a/MediaBrowser.Controller/IServerApplicationPaths.cs b/MediaBrowser.Controller/IServerApplicationPaths.cs
index c07934d0b..c89a60a6f 100644
--- a/MediaBrowser.Controller/IServerApplicationPaths.cs
+++ b/MediaBrowser.Controller/IServerApplicationPaths.cs
@@ -106,5 +106,7 @@ namespace MediaBrowser.Controller
/// </summary>
/// <value>The internal metadata path.</value>
string InternalMetadataPath { get; }
+
+ string ArtistsPath { get; }
}
} \ No newline at end of file
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index ff7f2fe67..0862e3eaf 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -11,6 +11,8 @@ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using CommonIO;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
namespace MediaBrowser.Controller.Library
@@ -32,15 +34,11 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// Resolves a set of files into a list of BaseItem
/// </summary>
- /// <param name="files">The files.</param>
- /// <param name="directoryService">The directory service.</param>
- /// <param name="parent">The parent.</param>
- /// <param name="collectionType">Type of the collection.</param>
- /// <returns>List{``0}.</returns>
IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files,
IDirectoryService directoryService,
- Folder parent, string
- collectionType = null);
+ Folder parent,
+ LibraryOptions libraryOptions,
+ string collectionType = null);
/// <summary>
/// Gets the root folder.
@@ -397,6 +395,9 @@ namespace MediaBrowser.Controller.Library
/// <returns><c>true</c> if [is audio file] [the specified path]; otherwise, <c>false</c>.</returns>
bool IsAudioFile(string path);
+ bool IsAudioFile(string path, LibraryOptions libraryOptions);
+ bool IsVideoFile(string path, LibraryOptions libraryOptions);
+
/// <summary>
/// Gets the season number from path.
/// </summary>
@@ -453,6 +454,8 @@ namespace MediaBrowser.Controller.Library
/// <returns>IEnumerable&lt;Folder&gt;.</returns>
IEnumerable<Folder> GetCollectionFolders(BaseItem item);
+ LibraryOptions GetLibraryOptions(BaseItem item);
+
/// <summary>
/// Gets the people.
/// </summary>
@@ -551,7 +554,7 @@ namespace MediaBrowser.Controller.Library
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
bool IgnoreFile(FileSystemMetadata file, BaseItem parent);
- void AddVirtualFolder(string name, string collectionType, string[] mediaPaths, bool refreshLibrary);
+ void AddVirtualFolder(string name, string collectionType, string[] mediaPaths, LibraryOptions options, bool refreshLibrary);
void RemoveVirtualFolder(string name, bool refreshLibrary);
void AddMediaPath(string virtualFolderName, string path);
void RemoveMediaPath(string virtualFolderName, string path);
diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
index ea3199b31..ec0ac325b 100644
--- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs
+++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
@@ -5,6 +5,8 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using CommonIO;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.Configuration;
namespace MediaBrowser.Controller.Library
{
@@ -51,6 +53,13 @@ namespace MediaBrowser.Controller.Library
}
}
+ public LibraryOptions LibraryOptions { get; set; }
+
+ public LibraryOptions GetLibraryOptions()
+ {
+ return LibraryOptions ?? (LibraryOptions = (Parent == null ? new LibraryOptions() : BaseItem.LibraryManager.GetLibraryOptions(Parent)));
+ }
+
/// <summary>
/// Gets or sets the file system dictionary.
/// </summary>
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 0462117cb..e7eaa1dc0 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -236,6 +236,7 @@
<Compile Include="Net\IAuthorizationContext.cs" />
<Compile Include="Net\IAuthService.cs" />
<Compile Include="Net\IHasAuthorization.cs" />
+ <Compile Include="Net\IAsyncStreamSource.cs" />
<Compile Include="Net\IHasResultFactory.cs" />
<Compile Include="Net\IHasSession.cs" />
<Compile Include="Net\IHttpResultFactory.cs" />
diff --git a/MediaBrowser.Controller/Net/IAsyncStreamSource.cs b/MediaBrowser.Controller/Net/IAsyncStreamSource.cs
new file mode 100644
index 000000000..0f41f60a5
--- /dev/null
+++ b/MediaBrowser.Controller/Net/IAsyncStreamSource.cs
@@ -0,0 +1,18 @@
+using ServiceStack.Web;
+using System.IO;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Net
+{
+ /// <summary>
+ /// Interface IAsyncStreamSource
+ /// Enables asynchronous writing to http resonse streams
+ /// </summary>
+ public interface IAsyncStreamSource
+ {
+ /// <summary>
+ /// Asynchronously write to the response stream.
+ /// </summary>
+ Task WriteToAsync(Stream responseStream);
+ }
+}
diff --git a/MediaBrowser.Controller/Net/IHttpResultFactory.cs b/MediaBrowser.Controller/Net/IHttpResultFactory.cs
index 49d4614d8..8fdb1ce37 100644
--- a/MediaBrowser.Controller/Net/IHttpResultFactory.cs
+++ b/MediaBrowser.Controller/Net/IHttpResultFactory.cs
@@ -28,7 +28,7 @@ namespace MediaBrowser.Controller.Net
/// <returns>System.Object.</returns>
object GetResult(object content, string contentType, IDictionary<string,string> responseHeaders = null);
- object GetAsyncStreamWriter(Func<Stream,Task> streamWriter, IDictionary<string, string> responseHeaders = null);
+ object GetAsyncStreamWriter(IAsyncStreamSource streamSource);
/// <summary>
/// Gets the optimized result.
diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs
index 437f0e157..87937869d 100644
--- a/MediaBrowser.Controller/Persistence/IItemRepository.cs
+++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs
@@ -170,6 +170,12 @@ namespace MediaBrowser.Controller.Persistence
QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query);
QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query);
QueryResult<Tuple<BaseItem, ItemCounts>> GetAllArtists(InternalItemsQuery query);
+
+ List<string> GetGameGenreNames();
+ List<string> GetMusicGenreNames();
+ List<string> GetStudioNames();
+ List<string> GetGenreNames();
+ List<string> GetAllArtistNames();
}
}
diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs
index 5ffe3d5da..654b97d7d 100644
--- a/MediaBrowser.Controller/Playlists/Playlist.cs
+++ b/MediaBrowser.Controller/Playlists/Playlist.cs
@@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
+using MediaBrowser.Controller.Providers;
namespace MediaBrowser.Controller.Playlists
{
@@ -58,11 +59,22 @@ namespace MediaBrowser.Controller.Playlists
return true;
}
+ protected override IEnumerable<BaseItem> LoadChildren()
+ {
+ // Save a trip to the database
+ return new List<BaseItem>();
+ }
+
public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren)
{
return GetPlayableItems(user).Result;
}
+ protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
+ {
+ return new List<BaseItem>();
+ }
+
public override IEnumerable<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query)
{
var items = GetPlayableItems(user).Result;
diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs
index 931cc208f..a01d73451 100644
--- a/MediaBrowser.Dlna/DlnaManager.cs
+++ b/MediaBrowser.Dlna/DlnaManager.cs
@@ -73,8 +73,13 @@ namespace MediaBrowser.Dlna
lock (_profiles)
{
var list = _profiles.Values.ToList();
- return list.Select(i => i.Item2).OrderBy(i => i.Name);
+ return list
+ .OrderBy(i => i.Item1.Info.Type == DeviceProfileType.User ? 0 : 1)
+ .ThenBy(i => i.Item1.Info.Name)
+ .Select(i => i.Item2)
+ .ToList();
}
+
}
public DeviceProfile GetDefaultProfile()
diff --git a/MediaBrowser.Dlna/PlayTo/Device.cs b/MediaBrowser.Dlna/PlayTo/Device.cs
index 1ec7a4ce0..6ad5899da 100644
--- a/MediaBrowser.Dlna/PlayTo/Device.cs
+++ b/MediaBrowser.Dlna/PlayTo/Device.cs
@@ -7,6 +7,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using System.Net;
using System.Security;
using System.Threading;
using System.Threading.Tasks;
@@ -91,6 +92,7 @@ namespace MediaBrowser.Dlna.PlayTo
private readonly IServerConfigurationManager _config;
public DateTime DateLastActivity { get; private set; }
+ public Action OnDeviceUnavailable { get; set; }
public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger, IServerConfigurationManager config)
{
@@ -134,6 +136,9 @@ namespace MediaBrowser.Dlna.PlayTo
private async void RefreshVolume()
{
+ if (_disposed)
+ return;
+
try
{
await GetVolume().ConfigureAwait(false);
@@ -149,6 +154,9 @@ namespace MediaBrowser.Dlna.PlayTo
private bool _timerActive;
private void RestartTimer()
{
+ if (_disposed)
+ return;
+
if (!_timerActive)
{
lock (_timerLock)
@@ -169,6 +177,9 @@ namespace MediaBrowser.Dlna.PlayTo
/// </summary>
private void RestartTimerInactive()
{
+ if (_disposed)
+ return;
+
if (_timerActive)
{
lock (_timerLock)
@@ -398,6 +409,7 @@ namespace MediaBrowser.Dlna.PlayTo
#region Get data
private int _successiveStopCount;
+ private int _connectFailureCount;
private async void TimerCallback(object sender)
{
if (_disposed)
@@ -435,6 +447,8 @@ namespace MediaBrowser.Dlna.PlayTo
}
}
+ _connectFailureCount = 0;
+
if (_disposed)
return;
@@ -455,8 +469,33 @@ namespace MediaBrowser.Dlna.PlayTo
}
}
}
+ catch (WebException ex)
+ {
+ if (_disposed)
+ return;
+
+ _logger.ErrorException("Error updating device info for {0}", ex, Properties.Name);
+
+ _successiveStopCount++;
+ _connectFailureCount++;
+
+ if (_successiveStopCount >= maxSuccessiveStopReturns)
+ {
+ RestartTimerInactive();
+ }
+ if (_connectFailureCount >= maxSuccessiveStopReturns)
+ {
+ if (OnDeviceUnavailable != null)
+ {
+ OnDeviceUnavailable();
+ }
+ }
+ }
catch (Exception ex)
{
+ if (_disposed)
+ return;
+
_logger.ErrorException("Error updating device info for {0}", ex, Properties.Name);
_successiveStopCount++;
diff --git a/MediaBrowser.Dlna/PlayTo/PlayToController.cs b/MediaBrowser.Dlna/PlayTo/PlayToController.cs
index 873ae5ae4..7429330bd 100644
--- a/MediaBrowser.Dlna/PlayTo/PlayToController.cs
+++ b/MediaBrowser.Dlna/PlayTo/PlayToController.cs
@@ -103,11 +103,25 @@ namespace MediaBrowser.Dlna.PlayTo
_device.PlaybackProgress += _device_PlaybackProgress;
_device.PlaybackStopped += _device_PlaybackStopped;
_device.MediaChanged += _device_MediaChanged;
+ _device.OnDeviceUnavailable = OnDeviceUnavailable;
+
_device.Start();
_deviceDiscovery.DeviceLeft += _deviceDiscovery_DeviceLeft;
}
+ private void OnDeviceUnavailable()
+ {
+ try
+ {
+ _sessionManager.ReportSessionEnded(_session.Id);
+ }
+ catch
+ {
+ // Could throw if the session is already gone
+ }
+ }
+
void _deviceDiscovery_DeviceLeft(object sender, SsdpMessageEventArgs e)
{
string nts;
@@ -125,14 +139,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (usn.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) != -1 ||
nt.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) != -1)
{
- try
- {
- _sessionManager.ReportSessionEnded(_session.Id);
- }
- catch
- {
- // Could throw if the session is already gone
- }
+ OnDeviceUnavailable();
}
}
}
@@ -647,6 +654,7 @@ namespace MediaBrowser.Dlna.PlayTo
_device.PlaybackStopped -= _device_PlaybackStopped;
_device.MediaChanged -= _device_MediaChanged;
_deviceDiscovery.DeviceLeft -= _deviceDiscovery_DeviceLeft;
+ _device.OnDeviceUnavailable = null;
_device.Dispose();
}
diff --git a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs b/MediaBrowser.Dlna/Profiles/DefaultProfile.cs
index 76797c0e3..e4f6d337f 100644
--- a/MediaBrowser.Dlna/Profiles/DefaultProfile.cs
+++ b/MediaBrowser.Dlna/Profiles/DefaultProfile.cs
@@ -65,14 +65,26 @@ namespace MediaBrowser.Dlna.Profiles
{
new DirectPlayProfile
{
- Container = "mp3,wma",
- Type = DlnaProfileType.Audio
+ Container = "m4v,ts,mkv,avi,mpg,mpeg,mp4",
+ VideoCodec = "h264",
+ AudioCodec = "aac,mp3,ac3",
+ Type = DlnaProfileType.Video
},
new DirectPlayProfile
{
- Container = "avi,mp4",
- Type = DlnaProfileType.Video
+ Container = "mp3,wma,aac,wav",
+ Type = DlnaProfileType.Audio
+ }
+ };
+
+ ResponseProfiles = new[]
+ {
+ new ResponseProfile
+ {
+ Container = "m4v",
+ Type = DlnaProfileType.Video,
+ MimeType = "video/mp4"
}
};
}
diff --git a/MediaBrowser.Dlna/Profiles/DenonAvrProfile.cs b/MediaBrowser.Dlna/Profiles/DenonAvrProfile.cs
index f8451bdfd..fb498c4ce 100644
--- a/MediaBrowser.Dlna/Profiles/DenonAvrProfile.cs
+++ b/MediaBrowser.Dlna/Profiles/DenonAvrProfile.cs
@@ -24,6 +24,8 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Audio
},
};
+
+ ResponseProfiles = new ResponseProfile[] { };
}
}
}
diff --git a/MediaBrowser.Dlna/Profiles/DirectTvProfile.cs b/MediaBrowser.Dlna/Profiles/DirectTvProfile.cs
index 585f8652e..c2a007a31 100644
--- a/MediaBrowser.Dlna/Profiles/DirectTvProfile.cs
+++ b/MediaBrowser.Dlna/Profiles/DirectTvProfile.cs
@@ -112,6 +112,8 @@ namespace MediaBrowser.Dlna.Profiles
}
}
};
+
+ ResponseProfiles = new ResponseProfile[] { };
}
}
}
diff --git a/MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs b/MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs
index 45cbbef6c..2c1919c00 100644
--- a/MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs
+++ b/MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs
@@ -11,7 +11,7 @@ namespace MediaBrowser.Dlna.Profiles
Name = "foobar2000";
SupportedMediaTypes = "Audio";
-
+
Identification = new DeviceIdentification
{
FriendlyName = @"foobar",
@@ -70,6 +70,8 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Audio
}
};
+
+ ResponseProfiles = new ResponseProfile[] { };
}
}
}
diff --git a/MediaBrowser.Dlna/Profiles/LgTvProfile.cs b/MediaBrowser.Dlna/Profiles/LgTvProfile.cs
index ab9a6a51f..af81fabe4 100644
--- a/MediaBrowser.Dlna/Profiles/LgTvProfile.cs
+++ b/MediaBrowser.Dlna/Profiles/LgTvProfile.cs
@@ -198,6 +198,8 @@ namespace MediaBrowser.Dlna.Profiles
Method = SubtitleDeliveryMethod.External
}
};
+
+ ResponseProfiles = new ResponseProfile[] { };
}
}
}
diff --git a/MediaBrowser.Dlna/Profiles/LinksysDMA2100Profile.cs b/MediaBrowser.Dlna/Profiles/LinksysDMA2100Profile.cs
index da00d9e86..2488cf542 100644
--- a/MediaBrowser.Dlna/Profiles/LinksysDMA2100Profile.cs
+++ b/MediaBrowser.Dlna/Profiles/LinksysDMA2100Profile.cs
@@ -30,6 +30,8 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Video
}
};
+
+ ResponseProfiles = new ResponseProfile[] { };
}
}
}
diff --git a/MediaBrowser.Dlna/Profiles/MediaMonkeyProfile.cs b/MediaBrowser.Dlna/Profiles/MediaMonkeyProfile.cs
index 7163252db..eef847852 100644
--- a/MediaBrowser.Dlna/Profiles/MediaMonkeyProfile.cs
+++ b/MediaBrowser.Dlna/Profiles/MediaMonkeyProfile.cs
@@ -70,6 +70,8 @@ namespace MediaBrowser.Dlna.Profiles
Type = DlnaProfileType.Audio
}
};
+
+ ResponseProfiles = new ResponseProfile[] { };
}
}
}
diff --git a/MediaBrowser.Dlna/Profiles/PopcornHourProfile.cs b/MediaBrowser.Dlna/Profiles/PopcornHourProfile.cs
index c98609393..0e1210afb 100644
--- a/MediaBrowser.Dlna/Profiles/PopcornHourProfile.cs
+++ b/MediaBrowser.Dlna/Profiles/PopcornHourProfile.cs
@@ -200,6 +200,8 @@ namespace MediaBrowser.Dlna.Profiles
}
}
};
+
+ ResponseProfiles = new ResponseProfile[] { };
}
}
}
diff --git a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs
index dbade8170..cfc793c01 100644
--- a/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs
+++ b/MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs
@@ -181,6 +181,8 @@ namespace MediaBrowser.Dlna.Profiles
}
}
};
+
+ ResponseProfiles = new ResponseProfile[] { };
}
}
}
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Default.xml b/MediaBrowser.Dlna/Profiles/Xml/Default.xml
index 8fae68632..732b5bade 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Default.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Default.xml
@@ -29,8 +29,8 @@
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<XmlRootAttributes />
<DirectPlayProfiles>
- <DirectPlayProfile container="mp3,wma" type="Audio" />
- <DirectPlayProfile container="avi,mp4" type="Video" />
+ <DirectPlayProfile container="m4v,ts,mkv,avi,mpg,mpeg,mp4" audioCodec="aac,mp3,ac3" videoCodec="h264" type="Video" />
+ <DirectPlayProfile container="mp3,wma,aac,wav" type="Audio" />
</DirectPlayProfiles>
<TranscodingProfiles>
<TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" forceLiveStream="false" enableSubtitlesInManifest="false" />
@@ -39,6 +39,10 @@
</TranscodingProfiles>
<ContainerProfiles />
<CodecProfiles />
- <ResponseProfiles />
+ <ResponseProfiles>
+ <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
+ <Conditions />
+ </ResponseProfile>
+ </ResponseProfiles>
<SubtitleProfiles />
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs
index cd1575fa5..f42582270 100644
--- a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs
@@ -51,7 +51,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
var metadata = string.Empty;
var vn = string.Empty;
- if (!string.IsNullOrWhiteSpace(state.AlbumCoverPath))
+ var hasArt = !string.IsNullOrWhiteSpace(state.AlbumCoverPath);
+ hasArt = false;
+
+ if (hasArt)
{
albumCoverInput = " -i \"" + state.AlbumCoverPath + "\"";
mapArgs = " -map 0:a -map 1:v -c:v copy";
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 08ab99f9b..f488be11a 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -123,10 +123,22 @@ namespace MediaBrowser.MediaEncoding.Encoder
return "System";
}
+ if (IsDefaultPath(FFMpegPath))
+ {
+ return "Default";
+ }
+
return "Custom";
}
}
+ private bool IsDefaultPath(string path)
+ {
+ var parentPath = Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "ffmpeg", "20160410");
+
+ return FileSystem.ContainsSubPath(parentPath, path);
+ }
+
private bool IsSystemInstalledPath(string path)
{
if (path.IndexOf("/", StringComparison.Ordinal) == -1 && path.IndexOf("\\", StringComparison.Ordinal) == -1)
@@ -343,14 +355,16 @@ namespace MediaBrowser.MediaEncoding.Encoder
// If that doesn't pan out, then do a recursive search
var files = Directory.GetFiles(path);
- var ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase));
- var ffprobePath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase));
+ var excludeExtensions = new[] { ".c" };
+
+ var ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
+ var ffprobePath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
if (string.IsNullOrWhiteSpace(ffmpegPath) || !File.Exists(ffmpegPath))
{
files = Directory.GetFiles(path, "*", SearchOption.AllDirectories);
- ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase));
+ ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
if (!string.IsNullOrWhiteSpace(ffmpegPath))
{
@@ -874,8 +888,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
var mapArg = imageStreamIndex.HasValue ? (" -map 0:v:" + imageStreamIndex.Value.ToString(CultureInfo.InvariantCulture)) : string.Empty;
// Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case.
- var args = useIFrame ? string.Format("-i {0}{3} -threads 1 -v quiet -vframes 1 -vf \"{2},thumbnail=30\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg) :
- string.Format("-i {0}{3} -threads 1 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg);
+ var args = useIFrame ? string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail=30\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg) :
+ string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg);
var probeSize = GetProbeSizeArgument(new[] { inputPath }, protocol);
@@ -980,7 +994,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
FileSystem.CreateDirectory(targetDirectory);
var outputPath = Path.Combine(targetDirectory, filenamePrefix + "%05d.jpg");
- var args = string.Format("-i {0} -threads 1 -v quiet -vf \"{2}\" -f image2 \"{1}\"", inputArgument, outputPath, vf);
+ var args = string.Format("-i {0} -threads 0 -v quiet -vf \"{2}\" -f image2 \"{1}\"", inputArgument, outputPath, vf);
var probeSize = GetProbeSizeArgument(new[] { inputArgument }, protocol);
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index 9e9bc0780..700af682b 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -9,6 +9,7 @@ using System.Linq;
using System.Text;
using System.Xml;
using CommonIO;
+using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
@@ -793,7 +794,7 @@ namespace MediaBrowser.MediaEncoding.Probing
if (!string.IsNullOrWhiteSpace(artists))
{
audio.Artists = SplitArtists(artists, new[] { '/', ';' }, false)
- .Distinct(StringComparer.OrdinalIgnoreCase)
+ .DistinctNames()
.ToList();
}
else
@@ -806,7 +807,7 @@ namespace MediaBrowser.MediaEncoding.Probing
else
{
audio.Artists = SplitArtists(artist, _nameDelimiters, true)
- .Distinct(StringComparer.OrdinalIgnoreCase)
+ .DistinctNames()
.ToList();
}
}
@@ -828,7 +829,7 @@ namespace MediaBrowser.MediaEncoding.Probing
else
{
audio.AlbumArtists = SplitArtists(albumArtist, _nameDelimiters, true)
- .Distinct(StringComparer.OrdinalIgnoreCase)
+ .DistinctNames()
.ToList();
}
diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
index 7a9589c98..351740e6e 100644
--- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
+++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
@@ -205,6 +205,9 @@
<Compile Include="..\MediaBrowser.Model\Configuration\ImageSavingConvention.cs">
<Link>Configuration\ImageSavingConvention.cs</Link>
</Compile>
+ <Compile Include="..\MediaBrowser.Model\Configuration\LibraryOptions.cs">
+ <Link>Configuration\LibraryOptions.cs</Link>
+ </Compile>
<Compile Include="..\MediaBrowser.Model\Configuration\MetadataConfiguration.cs">
<Link>Configuration\MetadataConfiguration.cs</Link>
</Compile>
diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
index 420b536ae..7df8f3126 100644
--- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
+++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
@@ -177,6 +177,9 @@
<Compile Include="..\MediaBrowser.Model\Configuration\ImageSavingConvention.cs">
<Link>Configuration\ImageSavingConvention.cs</Link>
</Compile>
+ <Compile Include="..\MediaBrowser.Model\Configuration\LibraryOptions.cs">
+ <Link>Configuration\LibraryOptions.cs</Link>
+ </Compile>
<Compile Include="..\MediaBrowser.Model\Configuration\MetadataConfiguration.cs">
<Link>Configuration\MetadataConfiguration.cs</Link>
</Compile>
diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs
new file mode 100644
index 000000000..e15df37c1
--- /dev/null
+++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs
@@ -0,0 +1,13 @@
+namespace MediaBrowser.Model.Configuration
+{
+ public class LibraryOptions
+ {
+ public bool EnableArchiveMediaFiles { get; set; }
+ public bool EnablePhotos { get; set; }
+
+ public LibraryOptions()
+ {
+ EnablePhotos = true;
+ }
+ }
+}
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index 58b74ba64..63d452bce 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -180,8 +180,6 @@ namespace MediaBrowser.Model.Configuration
public NameValuePair[] ContentTypes { get; set; }
- public bool EnableAudioArchiveFiles { get; set; }
- public bool EnableVideoArchiveFiles { get; set; }
public int RemoteClientBitrateLimit { get; set; }
public AutoOnOff EnableLibraryMonitor { get; set; }
@@ -204,6 +202,7 @@ namespace MediaBrowser.Model.Configuration
public bool DisplaySpecialsWithinSeasons { get; set; }
public bool DisplayCollectionsView { get; set; }
public string[] LocalNetworkAddresses { get; set; }
+ public string[] CodecsUsed { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
@@ -212,6 +211,7 @@ namespace MediaBrowser.Model.Configuration
{
LocalNetworkAddresses = new string[] { };
Migrations = new string[] { };
+ CodecsUsed = new string[] { };
SqliteCacheSize = 0;
EnableLocalizedGuids = true;
diff --git a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
index ed18fed65..fb353b016 100644
--- a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
+++ b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using MediaBrowser.Model.Extensions;
namespace MediaBrowser.Model.Dlna
{
@@ -59,8 +60,8 @@ namespace MediaBrowser.Model.Dlna
private static double GetVideoBitrateScaleFactor(string codec)
{
- if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase))
+ if (StringHelper.EqualsIgnoreCase(codec, "h265") ||
+ StringHelper.EqualsIgnoreCase(codec, "hevc"))
{
return .5;
}
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index 80e81a41a..0710417c8 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -570,7 +570,7 @@ namespace MediaBrowser.Model.Dlna
playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue);
}
- int audioBitrate = GetAudioBitrate(options.GetMaxBitrate(), playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec, audioStream);
+ int audioBitrate = GetAudioBitrate(playlistItem.SubProtocol, options.GetMaxBitrate(), playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec, audioStream);
playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate);
int? maxBitrateSetting = options.GetMaxBitrate();
@@ -593,7 +593,7 @@ namespace MediaBrowser.Model.Dlna
return playlistItem;
}
- private int GetAudioBitrate(int? maxTotalBitrate, int? targetAudioChannels, string targetAudioCodec, MediaStream audioStream)
+ private int GetAudioBitrate(string subProtocol, int? maxTotalBitrate, int? targetAudioChannels, string targetAudioCodec, MediaStream audioStream)
{
var defaultBitrate = 128000;
if (StringHelper.EqualsIgnoreCase(targetAudioCodec, "ac3"))
@@ -611,7 +611,14 @@ namespace MediaBrowser.Model.Dlna
{
if (StringHelper.EqualsIgnoreCase(targetAudioCodec, "ac3"))
{
- defaultBitrate = Math.Max(448000, defaultBitrate);
+ if (string.Equals(subProtocol, "hls", StringComparison.OrdinalIgnoreCase))
+ {
+ defaultBitrate = Math.Max(384000, defaultBitrate);
+ }
+ else
+ {
+ defaultBitrate = Math.Max(448000, defaultBitrate);
+ }
}
else
{
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs
index f95c6a070..02239aa48 100644
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/StreamInfo.cs
@@ -252,6 +252,8 @@ namespace MediaBrowser.Model.Dlna
list.Add(new NameValuePair("TranscodingMaxAudioChannels", item.TranscodingMaxAudioChannels.HasValue ? StringHelper.ToStringCultureInvariant(item.TranscodingMaxAudioChannels.Value) : string.Empty));
list.Add(new NameValuePair("EnableSubtitlesInManifest", item.EnableSubtitlesInManifest.ToString().ToLower()));
+ list.Add(new NameValuePair("Tag", item.MediaSource.ETag ?? string.Empty));
+
return list;
}
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index 6edf743fb..348a781ae 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -346,7 +346,16 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets a value indicating whether this instance is folder.
/// </summary>
/// <value><c>true</c> if this instance is folder; otherwise, <c>false</c>.</value>
- public bool IsFolder { get; set; }
+ public bool? IsFolder { get; set; }
+
+ [IgnoreDataMember]
+ public bool IsFolderItem
+ {
+ get
+ {
+ return IsFolder ?? false;
+ }
+ }
/// <summary>
/// Gets or sets the parent id.
@@ -656,7 +665,7 @@ namespace MediaBrowser.Model.Dto
{
get
{
- return RunTimeTicks.HasValue || IsFolder || IsGenre || IsMusicGenre || IsArtist;
+ return RunTimeTicks.HasValue || IsFolderItem || IsGenre || IsMusicGenre || IsArtist;
}
}
@@ -837,6 +846,7 @@ namespace MediaBrowser.Model.Dto
/// </summary>
/// <value>The album count.</value>
public int? AlbumCount { get; set; }
+ public int? ArtistCount { get; set; }
/// <summary>
/// Gets or sets the music video count.
/// </summary>
diff --git a/MediaBrowser.Model/Dto/ItemCounts.cs b/MediaBrowser.Model/Dto/ItemCounts.cs
index 07ddfa1ac..66c3dfebc 100644
--- a/MediaBrowser.Model/Dto/ItemCounts.cs
+++ b/MediaBrowser.Model/Dto/ItemCounts.cs
@@ -25,6 +25,7 @@
/// </summary>
/// <value>The game count.</value>
public int GameCount { get; set; }
+ public int ArtistCount { get; set; }
/// <summary>
/// Gets or sets the game system count.
/// </summary>
diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs
index 4e3e60063..bb07d9cb6 100644
--- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs
+++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs
@@ -20,6 +20,7 @@ namespace MediaBrowser.Model.Dto
public string Name { get; set; }
+ public string ETag { get; set; }
public long? RunTimeTicks { get; set; }
public bool ReadAtNativeFramerate { get; set; }
public bool SupportsTranscoding { get; set; }
diff --git a/MediaBrowser.Model/Entities/VirtualFolderInfo.cs b/MediaBrowser.Model/Entities/VirtualFolderInfo.cs
index 1161ab005..d8ec04ff6 100644
--- a/MediaBrowser.Model/Entities/VirtualFolderInfo.cs
+++ b/MediaBrowser.Model/Entities/VirtualFolderInfo.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using MediaBrowser.Model.Configuration;
namespace MediaBrowser.Model.Entities
{
@@ -25,6 +26,8 @@ namespace MediaBrowser.Model.Entities
/// <value>The type of the collection.</value>
public string CollectionType { get; set; }
+ public LibraryOptions LibraryOptions { get; set; }
+
/// <summary>
/// Initializes a new instance of the <see cref="VirtualFolderInfo"/> class.
/// </summary>
diff --git a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs
index 3a6ad0444..0ece1e4a0 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs
@@ -59,5 +59,11 @@ namespace MediaBrowser.Model.LiveTv
/// </summary>
/// <value><c>true</c> if [add current program]; otherwise, <c>false</c>.</value>
public bool AddCurrentProgram { get; set; }
+ public bool EnableUserData { get; set; }
+
+ public LiveTvChannelQuery()
+ {
+ EnableUserData = true;
+ }
}
}
diff --git a/MediaBrowser.Model/LiveTv/ProgramQuery.cs b/MediaBrowser.Model/LiveTv/ProgramQuery.cs
index 0141191c1..bf459237a 100644
--- a/MediaBrowser.Model/LiveTv/ProgramQuery.cs
+++ b/MediaBrowser.Model/LiveTv/ProgramQuery.cs
@@ -15,9 +15,11 @@ namespace MediaBrowser.Model.LiveTv
SortBy = new string[] { };
Genres = new string[] { };
EnableTotalRecordCount = true;
+ EnableUserData = true;
}
public bool EnableTotalRecordCount { get; set; }
+ public bool EnableUserData { get; set; }
/// <summary>
/// Fields to return within the items, in addition to basic information
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index e3c1e52a5..db70b8606 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -95,6 +95,7 @@
<Compile Include="Configuration\CinemaModeConfiguration.cs" />
<Compile Include="Configuration\EncodingOptions.cs" />
<Compile Include="Configuration\FanartOptions.cs" />
+ <Compile Include="Configuration\LibraryOptions.cs" />
<Compile Include="Configuration\MetadataConfiguration.cs" />
<Compile Include="Configuration\PeopleMetadataOptions.cs" />
<Compile Include="Configuration\XbmcMetadataOptions.cs" />
diff --git a/MediaBrowser.Model/Sync/SyncJobQuery.cs b/MediaBrowser.Model/Sync/SyncJobQuery.cs
index e86ec929f..bb99b5d5f 100644
--- a/MediaBrowser.Model/Sync/SyncJobQuery.cs
+++ b/MediaBrowser.Model/Sync/SyncJobQuery.cs
@@ -23,6 +23,7 @@ namespace MediaBrowser.Model.Sync
/// </summary>
/// <value>The user identifier.</value>
public string UserId { get; set; }
+ public string ExcludeTargetIds { get; set; }
/// <summary>
/// Gets or sets the status.
/// </summary>
diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs
index 16b4b673d..3917b1662 100644
--- a/MediaBrowser.Model/Users/UserPolicy.cs
+++ b/MediaBrowser.Model/Users/UserPolicy.cs
@@ -41,6 +41,7 @@ namespace MediaBrowser.Model.Users
public bool EnableMediaPlayback { get; set; }
public bool EnableAudioPlaybackTranscoding { get; set; }
public bool EnableVideoPlaybackTranscoding { get; set; }
+ public bool EnablePlaybackRemuxing { get; set; }
public bool EnableContentDeletion { get; set; }
public bool EnableContentDownloading { get; set; }
@@ -76,6 +77,7 @@ namespace MediaBrowser.Model.Users
EnableMediaPlayback = true;
EnableAudioPlaybackTranscoding = true;
EnableVideoPlaybackTranscoding = true;
+ EnablePlaybackRemuxing = true;
EnableLiveTvManagement = true;
EnableLiveTvAccess = true;
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs
index ac942d1a7..2a69948b1 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -42,6 +42,13 @@ namespace MediaBrowser.Providers.Manager
var config = ProviderManager.GetMetadataOptions(item);
var updateType = ItemUpdateType.None;
+ var requiresRefresh = false;
+
+ if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
+ {
+ // TODO: If this returns true, should we instead just change metadata refresh mode to Full?
+ requiresRefresh = item.RequiresRefresh();
+ }
var itemImageProvider = new ItemImageProvider(Logger, ProviderManager, ServerConfigurationManager, FileSystem);
var localImagesFailed = false;
@@ -70,14 +77,10 @@ namespace MediaBrowser.Providers.Manager
bool hasRefreshedMetadata = true;
bool hasRefreshedImages = true;
- var requiresRefresh = false;
// Next run metadata providers
if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
{
- // TODO: If this returns true, should we instead just change metadata refresh mode to Full?
- requiresRefresh = item.RequiresRefresh();
-
var providers = GetProviders(item, refreshOptions, requiresRefresh)
.ToList();
@@ -149,7 +152,7 @@ namespace MediaBrowser.Providers.Manager
if (file != null)
{
var fileLastWriteTime = file.LastWriteTimeUtc;
- if (item.EnableForceSaveOnDateModifiedChange && fileLastWriteTime != item.DateModified)
+ if (item.EnableRefreshOnDateModifiedChange && fileLastWriteTime != item.DateModified)
{
Logger.Debug("Date modified for {0}. Old date {1} new date {2} Id {3}", item.Path, item.DateModified, fileLastWriteTime, item.Id);
requiresRefresh = true;
@@ -284,6 +287,13 @@ namespace MediaBrowser.Providers.Manager
updateType |= SaveCumulativeRunTimeTicks(item, isFullRefresh, currentUpdateType);
updateType |= SaveDateLastMediaAdded(item, isFullRefresh, currentUpdateType);
+ var presentationUniqueKey = item.CreatePresentationUniqueKey();
+ if (!string.Equals(item.PresentationUniqueKey, presentationUniqueKey, StringComparison.Ordinal))
+ {
+ item.PresentationUniqueKey = presentationUniqueKey;
+ updateType |= ItemUpdateType.MetadataImport;
+ }
+
return updateType;
}
@@ -525,7 +535,7 @@ namespace MediaBrowser.Providers.Manager
}
// Local metadata is king - if any is found don't run remote providers
- if (!options.ReplaceAllMetadata && (!hasLocalMetadata || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh))
+ if (!options.ReplaceAllMetadata && (!hasLocalMetadata || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || !item.StopRefreshIfLocalMetadataFound))
{
var remoteResult = await ExecuteRemoteProviders(temp, logName, id, providers.OfType<IRemoteMetadataProvider<TItemType, TIdType>>(), cancellationToken)
.ConfigureAwait(false);
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
index 11280cff2..0df8b6c4b 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs
@@ -171,10 +171,13 @@ namespace MediaBrowser.Providers.MediaInfo
public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
{
- var file = directoryService.GetFile(item.Path);
- if (file != null && file.LastWriteTimeUtc != item.DateModified)
+ if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path))
{
- return true;
+ var file = directoryService.GetFile(item.Path);
+ if (file != null && file.LastWriteTimeUtc != item.DateModified)
+ {
+ return true;
+ }
}
if (item.SupportsLocalMetadata)
diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
index fb08f00c1..280e92beb 100644
--- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
+++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs
@@ -194,10 +194,13 @@ namespace MediaBrowser.Providers.MediaInfo
public bool HasChanged(IHasMetadata item, IDirectoryService directoryService)
{
- var file = directoryService.GetFile(item.Path);
- if (file != null && file.LastWriteTimeUtc != item.DateModified)
+ if (item.EnableRefreshOnDateModifiedChange)
{
- return true;
+ var file = directoryService.GetFile(item.Path);
+ if (file != null && file.LastWriteTimeUtc != item.DateModified)
+ {
+ return true;
+ }
}
return false;
diff --git a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
index 1710ec2b0..a0ce80610 100644
--- a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
+++ b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
@@ -81,46 +81,24 @@ namespace MediaBrowser.Providers.Music
private IEnumerable<RemoteSearchResult> GetResultsFromResponse(XmlDocument doc)
{
- var ns = new XmlNamespaceManager(doc.NameTable);
- ns.AddNamespace("mb", MusicBrainzBaseUrl + "/ns/mmd-2.0#");
-
- var list = new List<RemoteSearchResult>();
-
- var nodes = doc.SelectNodes("//mb:release-list/mb:release", ns);
-
- if (nodes != null)
+ return ReleaseResult.Parse(doc).Select(i =>
{
- foreach (var node in nodes.Cast<XmlNode>())
+ var result = new RemoteSearchResult
{
- if (node.Attributes != null)
- {
- string name = null;
-
- string mbzId = node.Attributes["id"].Value;
-
- var nameNode = node.SelectSingleNode("//mb:title", ns);
-
- if (nameNode != null)
- {
- name = nameNode.InnerText;
- }
-
- if (!string.IsNullOrWhiteSpace(mbzId) && !string.IsNullOrWhiteSpace(name))
- {
- var result = new RemoteSearchResult
- {
- Name = name
- };
-
- result.SetProviderId(MetadataProviders.MusicBrainzAlbum, mbzId);
+ Name = i.Title
+ };
- list.Add(result);
- }
- }
+ if (!string.IsNullOrWhiteSpace(i.ReleaseId))
+ {
+ result.SetProviderId(MetadataProviders.MusicBrainzAlbum, i.ReleaseId);
+ }
+ if (!string.IsNullOrWhiteSpace(i.ReleaseGroupId))
+ {
+ result.SetProviderId(MetadataProviders.MusicBrainzAlbum, i.ReleaseGroupId);
}
- }
- return list;
+ return result;
+ });
}
public async Task<MetadataResult<MusicAlbum>> GetMetadata(AlbumInfo id, CancellationToken cancellationToken)
@@ -208,7 +186,7 @@ namespace MediaBrowser.Providers.Music
var doc = await GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false);
- return ReleaseResult.Parse(doc);
+ return ReleaseResult.Parse(doc, 1).FirstOrDefault();
}
private async Task<ReleaseResult> GetReleaseResultByArtistName(string albumName, string artistName, CancellationToken cancellationToken)
@@ -219,32 +197,32 @@ namespace MediaBrowser.Providers.Music
var doc = await GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false);
- return ReleaseResult.Parse(doc);
+ return ReleaseResult.Parse(doc, 1).FirstOrDefault();
}
private class ReleaseResult
{
public string ReleaseId;
public string ReleaseGroupId;
+ public string Title;
- public static ReleaseResult Parse(XmlDocument doc)
+ public static List<ReleaseResult> Parse(XmlDocument doc, int? limit = null)
{
var docElem = doc.DocumentElement;
+ var list = new List<ReleaseResult>();
if (docElem == null)
{
- return new ReleaseResult();
+ return list;
}
var releaseList = docElem.FirstChild;
if (releaseList == null)
{
- return new ReleaseResult();
+ return list;
}
var nodes = releaseList.ChildNodes;
- string releaseId = null;
- string releaseGroupId = null;
if (nodes != null)
{
@@ -252,18 +230,42 @@ namespace MediaBrowser.Providers.Music
{
if (string.Equals(node.Name, "release", StringComparison.OrdinalIgnoreCase))
{
- releaseId = node.Attributes["id"].Value;
- releaseGroupId = GetReleaseGroupIdFromReleaseNode(node);
- break;
+ var releaseId = node.Attributes["id"].Value;
+ var releaseGroupId = GetReleaseGroupIdFromReleaseNode(node);
+
+ list.Add(new ReleaseResult
+ {
+ ReleaseId = releaseId,
+ ReleaseGroupId = releaseGroupId,
+ Title = GetTitleFromReleaseNode(node)
+ });
+
+ if (limit.HasValue && list.Count >= limit.Value)
+ {
+ break;
+ }
}
}
}
- return new ReleaseResult
+ return list;
+ }
+
+ private static string GetTitleFromReleaseNode(XmlNode node)
+ {
+ var subNodes = node.ChildNodes;
+ if (subNodes != null)
{
- ReleaseId = releaseId,
- ReleaseGroupId = releaseGroupId
- };
+ foreach (var subNode in subNodes.Cast<XmlNode>())
+ {
+ if (string.Equals(subNode.Name, "title", StringComparison.OrdinalIgnoreCase))
+ {
+ return subNode.InnerText;
+ }
+ }
+ }
+
+ return null;
}
private static string GetReleaseGroupIdFromReleaseNode(XmlNode node)
diff --git a/MediaBrowser.Providers/TV/DummySeasonProvider.cs b/MediaBrowser.Providers/TV/DummySeasonProvider.cs
index baea0a06d..fe0ad78be 100644
--- a/MediaBrowser.Providers/TV/DummySeasonProvider.cs
+++ b/MediaBrowser.Providers/TV/DummySeasonProvider.cs
@@ -112,7 +112,9 @@ namespace MediaBrowser.Providers.TV
IndexNumber = seasonNumber,
Id = _libraryManager.GetNewItemId((series.Id + (seasonNumber ?? -1).ToString(_usCulture) + seasonName), typeof(Season)),
IsVirtualItem = isVirtualItem,
- SeriesId = series.Id
+ SeriesId = series.Id,
+ SeriesName = series.Name,
+ SeriesSortName = series.SortName
};
season.SetParent(series);
diff --git a/MediaBrowser.Providers/TV/SeasonMetadataService.cs b/MediaBrowser.Providers/TV/SeasonMetadataService.cs
index addab3918..cf04a1418 100644
--- a/MediaBrowser.Providers/TV/SeasonMetadataService.cs
+++ b/MediaBrowser.Providers/TV/SeasonMetadataService.cs
@@ -35,26 +35,17 @@ namespace MediaBrowser.Providers.TV
updateType |= SaveIsVirtualItem(item, episodes);
}
- if (updateType <= ItemUpdateType.None)
+ if (!string.Equals(item.SeriesName, item.FindSeriesName(), StringComparison.Ordinal))
{
- if (!string.Equals(item.SeriesName, item.FindSeriesName(), StringComparison.Ordinal))
- {
- updateType |= ItemUpdateType.MetadataImport;
- }
+ updateType |= ItemUpdateType.MetadataImport;
}
- if (updateType <= ItemUpdateType.None)
+ if (!string.Equals(item.SeriesSortName, item.FindSeriesSortName(), StringComparison.Ordinal))
{
- if (!string.Equals(item.SeriesSortName, item.FindSeriesSortName(), StringComparison.Ordinal))
- {
- updateType |= ItemUpdateType.MetadataImport;
- }
+ updateType |= ItemUpdateType.MetadataImport;
}
- if (updateType <= ItemUpdateType.None)
+ if (item.SeriesId != item.FindSeriesId())
{
- if (item.SeriesId != item.FindSeriesId())
- {
- updateType |= ItemUpdateType.MetadataImport;
- }
+ updateType |= ItemUpdateType.MetadataImport;
}
return updateType;
diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs b/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs
index cb95bfd14..6cd9e9620 100644
--- a/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs
+++ b/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs
@@ -8,7 +8,7 @@ namespace MediaBrowser.Server.Implementations.Collections
public class CollectionsDynamicFolder : IVirtualFolderCreator
{
private readonly IApplicationPaths _appPaths;
- private IFileSystem _fileSystem;
+ private readonly IFileSystem _fileSystem;
public CollectionsDynamicFolder(IApplicationPaths appPaths, IFileSystem fileSystem)
{
diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
index cc165da6a..be68162ca 100644
--- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs
+++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
@@ -329,7 +329,7 @@ namespace MediaBrowser.Server.Implementations.Dto
if (user != null)
{
- await AttachUserSpecificInfo(dto, item, user, fields).ConfigureAwait(false);
+ await AttachUserSpecificInfo(dto, item, user, options).ConfigureAwait(false);
}
var hasMediaSources = item as IHasMediaSources;
@@ -408,12 +408,19 @@ namespace MediaBrowser.Server.Implementations.Dto
private void SetItemByNameInfo(BaseItem item, BaseItemDto dto, List<BaseItem> taggedItems, User user = null)
{
- if (item is MusicArtist || item is MusicGenre)
+ if (item is MusicArtist)
{
dto.AlbumCount = taggedItems.Count(i => i is MusicAlbum);
dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo);
dto.SongCount = taggedItems.Count(i => i is Audio);
}
+ else if (item is MusicGenre)
+ {
+ dto.ArtistCount = taggedItems.Count(i => i is MusicArtist);
+ dto.AlbumCount = taggedItems.Count(i => i is MusicAlbum);
+ dto.MusicVideoCount = taggedItems.Count(i => i is MusicVideo);
+ dto.SongCount = taggedItems.Count(i => i is Audio);
+ }
else if (item is GameGenre)
{
dto.GameCount = taggedItems.Count(i => i is Game);
@@ -422,6 +429,7 @@ namespace MediaBrowser.Server.Implementations.Dto
{
// This populates them all and covers Genre, Person, Studio, Year
+ dto.ArtistCount = taggedItems.Count(i => i is MusicArtist);
dto.AlbumCount = taggedItems.Count(i => i is MusicAlbum);
dto.EpisodeCount = taggedItems.Count(i => i is Episode);
dto.GameCount = taggedItems.Count(i => i is Game);
@@ -438,19 +446,20 @@ namespace MediaBrowser.Server.Implementations.Dto
/// <summary>
/// Attaches the user specific info.
/// </summary>
- /// <param name="dto">The dto.</param>
- /// <param name="item">The item.</param>
- /// <param name="user">The user.</param>
- /// <param name="fields">The fields.</param>
- private async Task AttachUserSpecificInfo(BaseItemDto dto, BaseItem item, User user, List<ItemFields> fields)
+ private async Task AttachUserSpecificInfo(BaseItemDto dto, BaseItem item, User user, DtoOptions dtoOptions)
{
+ var fields = dtoOptions.Fields;
+
if (item.IsFolder)
{
var folder = (Folder)item;
- dto.UserData = await _userDataRepository.GetUserDataDto(item, dto, user).ConfigureAwait(false);
+ if (dtoOptions.EnableUserData)
+ {
+ dto.UserData = await _userDataRepository.GetUserDataDto(item, dto, user).ConfigureAwait(false);
+ }
- if (item.SourceType == SourceType.Library)
+ if (!dto.ChildCount.HasValue && item.SourceType == SourceType.Library)
{
dto.ChildCount = GetChildCount(folder, user);
}
@@ -468,7 +477,10 @@ namespace MediaBrowser.Server.Implementations.Dto
else
{
- dto.UserData = _userDataRepository.GetUserDataDto(item, user).Result;
+ if (dtoOptions.EnableUserData)
+ {
+ dto.UserData = _userDataRepository.GetUserDataDto(item, user).Result;
+ }
}
dto.PlayAccess = item.GetPlayAccess(user);
@@ -476,7 +488,10 @@ namespace MediaBrowser.Server.Implementations.Dto
if (fields.Contains(ItemFields.BasicSyncInfo) || fields.Contains(ItemFields.SyncInfo))
{
var userCanSync = user != null && user.Policy.EnableSync;
- dto.SupportsSync = userCanSync && _syncManager.SupportsSync(item);
+ if (userCanSync && _syncManager.SupportsSync(item))
+ {
+ dto.SupportsSync = true;
+ }
}
if (fields.Contains(ItemFields.SeasonUserData))
@@ -940,20 +955,23 @@ namespace MediaBrowser.Server.Implementations.Dto
dto.Genres = item.Genres;
}
- dto.ImageTags = new Dictionary<ImageType, string>();
-
- // Prevent implicitly captured closure
- var currentItem = item;
- foreach (var image in currentItem.ImageInfos.Where(i => !currentItem.AllowsMultipleImages(i.Type))
- .ToList())
+ if (options.EnableImages)
{
- if (options.GetImageLimit(image.Type) > 0)
- {
- var tag = GetImageCacheTag(item, image);
+ dto.ImageTags = new Dictionary<ImageType, string>();
- if (tag != null)
+ // Prevent implicitly captured closure
+ var currentItem = item;
+ foreach (var image in currentItem.ImageInfos.Where(i => !currentItem.AllowsMultipleImages(i.Type))
+ .ToList())
+ {
+ if (options.GetImageLimit(image.Type) > 0)
{
- dto.ImageTags[image.Type] = tag;
+ var tag = GetImageCacheTag(item, image);
+
+ if (tag != null)
+ {
+ dto.ImageTags[image.Type] = tag;
+ }
}
}
}
@@ -961,7 +979,16 @@ namespace MediaBrowser.Server.Implementations.Dto
dto.Id = GetDtoId(item);
dto.IndexNumber = item.IndexNumber;
dto.ParentIndexNumber = item.ParentIndexNumber;
- dto.IsFolder = item.IsFolder;
+
+ if (item.IsFolder)
+ {
+ dto.IsFolder = true;
+ }
+ else if (item is IHasMediaSources)
+ {
+ dto.IsFolder = false;
+ }
+
dto.MediaType = item.MediaType;
dto.LocationType = item.LocationType;
if (item.IsHD.HasValue && item.IsHD.Value)
@@ -1504,97 +1531,6 @@ namespace MediaBrowser.Server.Implementations.Dto
}
/// <summary>
- /// Since it can be slow to make all of these calculations independently, this method will provide a way to do them all at once
- /// </summary>
- /// <param name="folder">The folder.</param>
- /// <param name="user">The user.</param>
- /// <param name="dto">The dto.</param>
- /// <param name="fields">The fields.</param>
- /// <param name="syncProgress">The synchronize progress.</param>
- /// <returns>Task.</returns>
- private async Task SetSpecialCounts(Folder folder, User user, BaseItemDto dto, List<ItemFields> fields, Dictionary<string, SyncJobItemStatus> syncProgress)
- {
- var recursiveItemCount = 0;
- var unplayed = 0;
-
- double totalPercentPlayed = 0;
- double totalSyncPercent = 0;
-
- var children = await folder.GetItems(new InternalItemsQuery
- {
- IsFolder = false,
- Recursive = true,
- ExcludeLocationTypes = new[] { LocationType.Virtual },
- User = user
-
- }).ConfigureAwait(false);
-
- // Loop through each recursive child
- foreach (var child in children.Items)
- {
- var userdata = _userDataRepository.GetUserData(user, child);
-
- recursiveItemCount++;
-
- var isUnplayed = true;
-
- // Incrememt totalPercentPlayed
- if (userdata != null)
- {
- if (userdata.Played)
- {
- totalPercentPlayed += 100;
-
- isUnplayed = false;
- }
- else if (userdata.PlaybackPositionTicks > 0 && child.RunTimeTicks.HasValue && child.RunTimeTicks.Value > 0)
- {
- double itemPercent = userdata.PlaybackPositionTicks;
- itemPercent /= child.RunTimeTicks.Value;
- totalPercentPlayed += itemPercent;
- }
- }
-
- if (isUnplayed)
- {
- unplayed++;
- }
-
- double percent = 0;
- SyncJobItemStatus syncItemProgress;
- if (syncProgress.TryGetValue(child.Id.ToString("N"), out syncItemProgress))
- {
- switch (syncItemProgress)
- {
- case SyncJobItemStatus.Synced:
- percent = 100;
- break;
- case SyncJobItemStatus.Converting:
- case SyncJobItemStatus.ReadyToTransfer:
- case SyncJobItemStatus.Transferring:
- percent = 50;
- break;
- }
- }
- totalSyncPercent += percent;
- }
-
- dto.RecursiveItemCount = recursiveItemCount;
- dto.UserData.UnplayedItemCount = unplayed;
-
- if (recursiveItemCount > 0)
- {
- dto.UserData.PlayedPercentage = totalPercentPlayed / recursiveItemCount;
-
- var pct = totalSyncPercent / recursiveItemCount;
- if (pct > 0)
- {
- dto.SyncPercent = pct;
- }
- }
- }
-
- /// <summary>
/// Attaches the primary image aspect ratio.
/// </summary>
/// <param name="dto">The dto.</param>
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
index 2109f8d59..39992b65d 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
+++ b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
@@ -43,13 +43,6 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
_providerManager = providerManager;
}
- public Task<FileOrganizationResult> OrganizeEpisodeFile(string path, CancellationToken cancellationToken)
- {
- var options = _config.GetAutoOrganizeOptions();
-
- return OrganizeEpisodeFile(path, options, false, cancellationToken);
- }
-
public async Task<FileOrganizationResult> OrganizeEpisodeFile(string path, AutoOrganizeOptions options, bool overwriteExisting, CancellationToken cancellationToken)
{
_logger.Info("Sorting file {0}", path);
@@ -63,6 +56,8 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
FileSize = new FileInfo(path).Length
};
+ try
+ {
if (_libraryMonitor.IsPathLocked(path))
{
result.Status = FileSortingStatus.Failure;
@@ -148,6 +143,12 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
}
await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ result.Status = FileSortingStatus.Failure;
+ result.StatusMessage = ex.Message;
+ }
return result;
}
@@ -156,6 +157,8 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{
var result = _organizationService.GetResult(request.ResultId);
+ try
+ {
Series series = null;
if (request.NewSeriesProviderIds.Count > 0)
@@ -207,6 +210,12 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
cancellationToken).ConfigureAwait(false);
await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ result.Status = FileSortingStatus.Failure;
+ result.StatusMessage = ex.Message;
+ }
return result;
}
@@ -263,16 +272,15 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
var originalExtractedSeriesString = result.ExtractedName;
+ try
+ {
// Proceed to sort the file
var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, premiereDate, options.TvOptions, cancellationToken).ConfigureAwait(false);
if (string.IsNullOrEmpty(newPath))
{
var msg = string.Format("Unable to sort {0} because target path could not be determined.", sourcePath);
- result.Status = FileSortingStatus.Failure;
- result.StatusMessage = msg;
- _logger.Warn(msg);
- return;
+ throw new Exception(msg);
}
_logger.Info("Sorting file {0} to new path {1}", sourcePath, newPath);
@@ -347,6 +355,14 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
}
}
}
+ }
+ catch (Exception ex)
+ {
+ result.Status = FileSortingStatus.Failure;
+ result.StatusMessage = ex.Message;
+ _logger.Warn(ex.Message);
+ return;
+ }
if (rememberCorrection)
{
@@ -505,7 +521,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
}
catch (Exception ex)
{
- var errorMsg = string.Format("Failed to move file from {0} to {1}", result.OriginalPath, result.TargetPath);
+ var errorMsg = string.Format("Failed to move file from {0} to {1}: {2}", result.OriginalPath, result.TargetPath, ex.Message);
result.Status = FileSortingStatus.Failure;
result.StatusMessage = errorMsg;
@@ -616,7 +632,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{
var msg = string.Format("No provider metadata found for {0} season {1} episode {2}", series.Name, seasonNumber, episodeNumber);
_logger.Warn(msg);
- return null;
+ throw new Exception(msg);
}
var episodeName = episode.Name;
@@ -715,6 +731,11 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
var pattern = endingEpisodeNumber.HasValue ? options.MultiEpisodeNamePattern : options.EpisodeNamePattern;
+ if (string.IsNullOrWhiteSpace(pattern))
+ {
+ throw new Exception("GetEpisodeFileName: Configured episode name pattern is empty!");
+ }
+
var result = pattern.Replace("%sn", seriesName)
.Replace("%s.n", seriesName.Replace(" ", "."))
.Replace("%s_n", seriesName.Replace(" ", "_"))
@@ -759,8 +780,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
// There may be cases where reducing the title length may still not be sufficient to
// stay below maxLength
var msg = string.Format("Unable to generate an episode file name shorter than {0} characters to constrain to the max path limit", maxLength);
- _logger.Warn(msg);
- return string.Empty;
+ throw new Exception(msg);
}
return result;
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs b/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs
index 60d515e12..9e16613e6 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs
+++ b/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs
@@ -112,8 +112,13 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager,
_libraryMonitor, _providerManager);
- await organizer.OrganizeEpisodeFile(result.OriginalPath, GetAutoOrganizeOptions(), true, CancellationToken.None)
+ var organizeResult = await organizer.OrganizeEpisodeFile(result.OriginalPath, GetAutoOrganizeOptions(), true, CancellationToken.None)
.ConfigureAwait(false);
+
+ if (organizeResult.Status != FileSortingStatus.Success)
+ {
+ throw new Exception(result.StatusMessage);
+ }
}
public Task ClearLog()
@@ -126,7 +131,12 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager,
_libraryMonitor, _providerManager);
- await organizer.OrganizeWithCorrection(request, GetAutoOrganizeOptions(), CancellationToken.None).ConfigureAwait(false);
+ var result = await organizer.OrganizeWithCorrection(request, GetAutoOrganizeOptions(), CancellationToken.None).ConfigureAwait(false);
+
+ if (result.Status != FileSortingStatus.Success)
+ {
+ throw new Exception(result.StatusMessage);
+ }
}
public QueryResult<SmartMatchInfo> GetSmartMatchInfos(FileOrganizationResultQuery query)
diff --git a/MediaBrowser.Server.Implementations/HttpServer/AsyncStreamWriterFunc.cs b/MediaBrowser.Server.Implementations/HttpServer/AsyncStreamWriter.cs
index 5aa01c706..e44b0c6af 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/AsyncStreamWriterFunc.cs
+++ b/MediaBrowser.Server.Implementations/HttpServer/AsyncStreamWriter.cs
@@ -4,38 +4,41 @@ using System.IO;
using System.Threading.Tasks;
using ServiceStack;
using ServiceStack.Web;
+using MediaBrowser.Controller.Net;
namespace MediaBrowser.Server.Implementations.HttpServer
{
- public class AsyncStreamWriterFunc : IStreamWriter, IAsyncStreamWriter, IHasOptions
+ public class AsyncStreamWriter : IStreamWriter, IAsyncStreamWriter, IHasOptions
{
/// <summary>
/// Gets or sets the source stream.
/// </summary>
/// <value>The source stream.</value>
- private Func<Stream, Task> Writer { get; set; }
-
- /// <summary>
- /// Gets the options.
- /// </summary>
- /// <value>The options.</value>
- public IDictionary<string, string> Options { get; private set; }
+ private IAsyncStreamSource _source;
public Action OnComplete { get; set; }
public Action OnError { get; set; }
/// <summary>
- /// Initializes a new instance of the <see cref="StreamWriter" /> class.
+ /// Initializes a new instance of the <see cref="AsyncStreamWriter" /> class.
/// </summary>
- public AsyncStreamWriterFunc(Func<Stream, Task> writer, IDictionary<string, string> headers)
+ public AsyncStreamWriter(IAsyncStreamSource source)
{
- Writer = writer;
+ _source = source;
+ }
- if (headers == null)
+ public IDictionary<string, string> Options
+ {
+ get
{
- headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ var hasOptions = _source as IHasOptions;
+ if (hasOptions != null)
+ {
+ return hasOptions.Options;
+ }
+
+ return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
- Options = headers;
}
/// <summary>
@@ -44,13 +47,13 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// <param name="responseStream">The response stream.</param>
public void WriteTo(Stream responseStream)
{
- var task = Writer(responseStream);
+ var task = _source.WriteToAsync(responseStream);
Task.WaitAll(task);
}
public async Task WriteToAsync(Stream responseStream)
{
- await Writer(responseStream).ConfigureAwait(false);
+ await _source.WriteToAsync(responseStream).ConfigureAwait(false);
}
}
}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
index 90055d8ec..633208739 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -331,6 +331,46 @@ namespace MediaBrowser.Server.Implementations.HttpServer
return url;
}
+ private string NormalizeConfiguredLocalAddress(string address)
+ {
+ var index = address.Trim('/').IndexOf('/');
+
+ if (index != -1)
+ {
+ address = address.Substring(index + 1);
+ }
+
+ return address.Trim('/');
+ }
+
+ private bool ValidateHost(Uri url)
+ {
+ var hosts = _config
+ .Configuration
+ .LocalNetworkAddresses
+ .Select(NormalizeConfiguredLocalAddress)
+ .ToList();
+
+ if (hosts.Count == 0)
+ {
+ return true;
+ }
+
+ var host = url.Host ?? string.Empty;
+
+ _logger.Debug("Validating host {0}", host);
+
+ if (_networkManager.IsInPrivateAddressSpace(host))
+ {
+ hosts.Add("localhost");
+ hosts.Add("127.0.0.1");
+
+ return hosts.Any(i => host.IndexOf(i, StringComparison.OrdinalIgnoreCase) != -1);
+ }
+
+ return true;
+ }
+
/// <summary>
/// Overridable method that can be used to implement a custom hnandler
/// </summary>
@@ -350,6 +390,16 @@ namespace MediaBrowser.Server.Implementations.HttpServer
return ;
}
+ if (!ValidateHost(url))
+ {
+ httpRes.StatusCode = 400;
+ httpRes.ContentType = "text/plain";
+ httpRes.Write("Invalid host");
+
+ httpRes.Close();
+ return;
+ }
+
var operationName = httpReq.OperationName;
var localPath = url.LocalPath;
diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs
index c0a2a5eb3..c55e98388 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs
+++ b/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs
@@ -275,7 +275,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// <returns>System.Object.</returns>
private object GetCachedResult(IRequest requestContext, IDictionary<string, string> responseHeaders, Guid cacheKey, string cacheKeyString, DateTime? lastDateModified, TimeSpan? cacheDuration, string contentType)
{
- responseHeaders["ETag"] = cacheKeyString;
+ responseHeaders["ETag"] = string.Format("\"{0}\"", cacheKeyString);
if (IsNotModified(requestContext, cacheKey, lastDateModified, cacheDuration))
{
@@ -534,7 +534,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
if (lastDateModified.HasValue && (string.IsNullOrEmpty(cacheKey) || cacheDuration.HasValue))
{
AddAgeHeader(responseHeaders, lastDateModified);
- responseHeaders["LastModified"] = lastDateModified.Value.ToString("r");
+ responseHeaders["Last-Modified"] = lastDateModified.Value.ToString("r");
}
if (cacheDuration.HasValue)
@@ -704,9 +704,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer
throw error;
}
- public object GetAsyncStreamWriter(Func<Stream, Task> streamWriter, IDictionary<string, string> responseHeaders = null)
+ public object GetAsyncStreamWriter(IAsyncStreamSource streamSource)
{
- return new AsyncStreamWriterFunc(streamWriter, responseHeaders);
+ return new AsyncStreamWriter(streamSource);
}
}
} \ No newline at end of file
diff --git a/MediaBrowser.Server.Implementations/IO/FileRefresher.cs b/MediaBrowser.Server.Implementations/IO/FileRefresher.cs
index f48beacb5..2f4605c5c 100644
--- a/MediaBrowser.Server.Implementations/IO/FileRefresher.cs
+++ b/MediaBrowser.Server.Implementations/IO/FileRefresher.cs
@@ -61,6 +61,11 @@ namespace MediaBrowser.Server.Implementations.IO
public void RestartTimer()
{
+ if (_disposed)
+ {
+ return;
+ }
+
lock (_timerLock)
{
if (_timer == null)
@@ -254,6 +259,11 @@ namespace MediaBrowser.Server.Implementations.IO
// File may have been deleted
return false;
}
+ catch (UnauthorizedAccessException)
+ {
+ Logger.Debug("No write permission for: {0}.", path);
+ return false;
+ }
catch (IOException)
{
//the file is unavailable because it is:
@@ -281,8 +291,10 @@ namespace MediaBrowser.Server.Implementations.IO
}
}
+ private bool _disposed;
public void Dispose()
{
+ _disposed = true;
DisposeTimer();
}
}
diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
index 8c2b927e3..ea9e58ee4 100644
--- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
+++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
@@ -38,7 +38,6 @@ namespace MediaBrowser.Server.Implementations.IO
/// </summary>
private readonly IReadOnlyList<string> _alwaysIgnoreFiles = new List<string>
{
- "thumbs.db",
"small.jpg",
"albumart.jpg",
@@ -47,6 +46,16 @@ namespace MediaBrowser.Server.Implementations.IO
"TempSBE"
};
+ private readonly IReadOnlyList<string> _alwaysIgnoreExtensions = new List<string>
+ {
+ // thumbs.db
+ ".db",
+
+ // bts sync files
+ ".bts",
+ ".sync"
+ };
+
/// <summary>
/// Add the path to our temporary ignore list. Use when writing to a path within our listening scope.
/// </summary>
@@ -411,7 +420,9 @@ namespace MediaBrowser.Server.Implementations.IO
var filename = Path.GetFileName(path);
- var monitorPath = !(!string.IsNullOrEmpty(filename) && _alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase));
+ var monitorPath = !string.IsNullOrEmpty(filename) &&
+ !_alwaysIgnoreFiles.Contains(filename, StringComparer.OrdinalIgnoreCase) &&
+ !_alwaysIgnoreExtensions.Contains(Path.GetExtension(path) ?? string.Empty, StringComparer.OrdinalIgnoreCase);
// Ignore certain files
var tempIgnorePaths = _tempIgnoredPaths.Keys.ToList();
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index 055fde504..cc3a7e41f 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
@@ -364,7 +364,7 @@ namespace MediaBrowser.Server.Implementations.Library
if (item.IsFolder)
{
- if (!(item is ICollectionFolder) && !(item is UserView) && !(item is Channel))
+ if (!(item is ICollectionFolder) && !(item is UserView) && !(item is Channel) && !(item is AggregateFolder))
{
if (item.SourceType != SourceType.Library)
{
@@ -556,7 +556,12 @@ namespace MediaBrowser.Server.Implementations.Library
return ResolvePath(fileInfo, new DirectoryService(_logger, _fileSystem), null, parent);
}
- private BaseItem ResolvePath(FileSystemMetadata fileInfo, IDirectoryService directoryService, IItemResolver[] resolvers, Folder parent = null, string collectionType = null)
+ private BaseItem ResolvePath(FileSystemMetadata fileInfo,
+ IDirectoryService directoryService,
+ IItemResolver[] resolvers,
+ Folder parent = null,
+ string collectionType = null,
+ LibraryOptions libraryOptions = null)
{
if (fileInfo == null)
{
@@ -575,7 +580,8 @@ namespace MediaBrowser.Server.Implementations.Library
Parent = parent,
Path = fullPath,
FileInfo = fileInfo,
- CollectionType = collectionType
+ CollectionType = collectionType,
+ LibraryOptions = libraryOptions
};
// Return null if ignore rules deem that we should do so
@@ -653,12 +659,17 @@ namespace MediaBrowser.Server.Implementations.Library
return !args.ContainsFileSystemEntryByName(".ignore");
}
- public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files, IDirectoryService directoryService, Folder parent, string collectionType)
+ public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files, IDirectoryService directoryService, Folder parent, LibraryOptions libraryOptions, string collectionType)
{
- return ResolvePaths(files, directoryService, parent, collectionType, EntityResolvers);
+ return ResolvePaths(files, directoryService, parent, libraryOptions, collectionType, EntityResolvers);
}
- public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files, IDirectoryService directoryService, Folder parent, string collectionType, IItemResolver[] resolvers)
+ public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files,
+ IDirectoryService directoryService,
+ Folder parent,
+ LibraryOptions libraryOptions,
+ string collectionType,
+ IItemResolver[] resolvers)
{
var fileList = files.Where(i => !IgnoreFile(i, parent)).ToList();
@@ -679,22 +690,27 @@ namespace MediaBrowser.Server.Implementations.Library
{
ResolverHelper.SetInitialItemValues(item, parent, _fileSystem, this, directoryService);
}
- items.AddRange(ResolveFileList(result.ExtraFiles, directoryService, parent, collectionType, resolvers));
+ items.AddRange(ResolveFileList(result.ExtraFiles, directoryService, parent, collectionType, resolvers, libraryOptions));
return items;
}
}
}
- return ResolveFileList(fileList, directoryService, parent, collectionType, resolvers);
+ return ResolveFileList(fileList, directoryService, parent, collectionType, resolvers, libraryOptions);
}
- private IEnumerable<BaseItem> ResolveFileList(IEnumerable<FileSystemMetadata> fileList, IDirectoryService directoryService, Folder parent, string collectionType, IItemResolver[] resolvers)
+ private IEnumerable<BaseItem> ResolveFileList(IEnumerable<FileSystemMetadata> fileList,
+ IDirectoryService directoryService,
+ Folder parent,
+ string collectionType,
+ IItemResolver[] resolvers,
+ LibraryOptions libraryOptions)
{
return fileList.Select(f =>
{
try
{
- return ResolvePath(f, directoryService, resolvers, parent, collectionType);
+ return ResolvePath(f, directoryService, resolvers, parent, collectionType, libraryOptions);
}
catch (Exception ex)
{
@@ -813,7 +829,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// <returns>Task{Person}.</returns>
public Person GetPerson(string name)
{
- return GetItemByName<Person>(ConfigurationManager.ApplicationPaths.PeoplePath, name);
+ return CreateItemByName<Person>(Person.GetPath(name), name);
}
/// <summary>
@@ -823,7 +839,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// <returns>Task{Studio}.</returns>
public Studio GetStudio(string name)
{
- return GetItemByName<Studio>(ConfigurationManager.ApplicationPaths.StudioPath, name);
+ return CreateItemByName<Studio>(Studio.GetPath(name), name);
}
/// <summary>
@@ -833,7 +849,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// <returns>Task{Genre}.</returns>
public Genre GetGenre(string name)
{
- return GetItemByName<Genre>(ConfigurationManager.ApplicationPaths.GenrePath, name);
+ return CreateItemByName<Genre>(Genre.GetPath(name), name);
}
/// <summary>
@@ -843,7 +859,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// <returns>Task{MusicGenre}.</returns>
public MusicGenre GetMusicGenre(string name)
{
- return GetItemByName<MusicGenre>(ConfigurationManager.ApplicationPaths.MusicGenrePath, name);
+ return CreateItemByName<MusicGenre>(MusicGenre.GetPath(name), name);
}
/// <summary>
@@ -853,15 +869,10 @@ namespace MediaBrowser.Server.Implementations.Library
/// <returns>Task{GameGenre}.</returns>
public GameGenre GetGameGenre(string name)
{
- return GetItemByName<GameGenre>(ConfigurationManager.ApplicationPaths.GameGenrePath, name);
+ return CreateItemByName<GameGenre>(GameGenre.GetPath(name), name);
}
/// <summary>
- /// The us culture
- /// </summary>
- private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
- /// <summary>
/// Gets a Year
/// </summary>
/// <param name="value">The value.</param>
@@ -874,19 +885,9 @@ namespace MediaBrowser.Server.Implementations.Library
throw new ArgumentOutOfRangeException("Years less than or equal to 0 are invalid.");
}
- return GetItemByName<Year>(ConfigurationManager.ApplicationPaths.YearPath, value.ToString(UsCulture));
- }
+ var name = value.ToString(CultureInfo.InvariantCulture);
- /// <summary>
- /// Gets the artists path.
- /// </summary>
- /// <value>The artists path.</value>
- public string ArtistsPath
- {
- get
- {
- return Path.Combine(ConfigurationManager.ApplicationPaths.ItemsByNamePath, "artists");
- }
+ return CreateItemByName<Year>(Year.GetPath(name), name);
}
/// <summary>
@@ -896,48 +897,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// <returns>Task{Genre}.</returns>
public MusicArtist GetArtist(string name)
{
- return GetItemByName<MusicArtist>(ArtistsPath, name);
- }
-
- private T GetItemByName<T>(string path, string name)
- where T : BaseItem, new()
- {
- if (string.IsNullOrWhiteSpace(path))
- {
- throw new ArgumentNullException("path");
- }
-
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentNullException("name");
- }
-
- // Trim the period at the end because windows will have a hard time with that
- var validFilename = _fileSystem.GetValidFilename(name)
- .Trim()
- .TrimEnd('.');
-
- string subFolderPrefix = null;
-
- var type = typeof(T);
-
- if (type == typeof(Person))
- {
- foreach (char c in validFilename)
- {
- if (char.IsLetterOrDigit(c))
- {
- subFolderPrefix = c.ToString();
- break;
- }
- }
- }
-
- var fullPath = string.IsNullOrEmpty(subFolderPrefix) ?
- Path.Combine(path, validFilename) :
- Path.Combine(path, subFolderPrefix, validFilename);
-
- return CreateItemByName<T>(fullPath, name);
+ return CreateItemByName<MusicArtist>(MusicArtist.GetPath(name), name);
}
private T CreateItemByName<T>(string path, string name)
@@ -1207,7 +1167,7 @@ namespace MediaBrowser.Server.Implementations.Library
.Select(dir => GetVirtualFolderInfo(dir, topLibraryFolders));
}
- private VirtualFolderInfo GetVirtualFolderInfo(string dir, List<BaseItem> collectionFolders)
+ private VirtualFolderInfo GetVirtualFolderInfo(string dir, List<BaseItem> allCollectionFolders)
{
var info = new VirtualFolderInfo
{
@@ -1221,7 +1181,7 @@ namespace MediaBrowser.Server.Implementations.Library
CollectionType = GetCollectionType(dir)
};
- var libraryFolder = collectionFolders.FirstOrDefault(i => string.Equals(i.Path, dir, StringComparison.OrdinalIgnoreCase));
+ var libraryFolder = allCollectionFolders.FirstOrDefault(i => string.Equals(i.Path, dir, StringComparison.OrdinalIgnoreCase));
if (libraryFolder != null && libraryFolder.HasImage(ImageType.Primary))
{
@@ -1233,6 +1193,12 @@ namespace MediaBrowser.Server.Implementations.Library
info.ItemId = libraryFolder.Id.ToString("N");
}
+ var collectionFolder = libraryFolder as CollectionFolder;
+ if (collectionFolder != null)
+ {
+ info.LibraryOptions = collectionFolder.GetLibraryOptions();
+ }
+
return info;
}
@@ -1499,7 +1465,12 @@ namespace MediaBrowser.Server.Implementations.Library
private void AddUserToQuery(InternalItemsQuery query, User user)
{
- if (query.AncestorIds.Length == 0 && !query.ParentId.HasValue && query.ChannelIds.Length == 0 && query.TopParentIds.Length == 0 && string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey))
+ if (query.AncestorIds.Length == 0 &&
+ !query.ParentId.HasValue &&
+ query.ChannelIds.Length == 0 &&
+ query.TopParentIds.Length == 0 &&
+ string.IsNullOrWhiteSpace(query.AncestorWithPresentationUniqueKey)
+ && query.ItemIds.Length == 0)
{
var userViews = _userviewManager().GetUserViews(new UserViewQuery
{
@@ -1891,6 +1862,15 @@ namespace MediaBrowser.Server.Implementations.Library
.Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path, StringComparer.OrdinalIgnoreCase));
}
+ public LibraryOptions GetLibraryOptions(BaseItem item)
+ {
+ var collectionFolder = GetCollectionFolders(item)
+ .OfType<CollectionFolder>()
+ .FirstOrDefault();
+
+ return collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions();
+ }
+
public string GetContentType(BaseItem item)
{
string configuredContentType = GetConfiguredContentType(item, false);
@@ -2242,18 +2222,28 @@ namespace MediaBrowser.Server.Implementations.Library
return item;
}
- public bool IsVideoFile(string path)
+ public bool IsVideoFile(string path, LibraryOptions libraryOptions)
{
- var resolver = new VideoResolver(GetNamingOptions(), new PatternsLogger());
+ var resolver = new VideoResolver(GetNamingOptions(libraryOptions), new PatternsLogger());
return resolver.IsVideoFile(path);
}
- public bool IsAudioFile(string path)
+ public bool IsVideoFile(string path)
{
- var parser = new AudioFileParser(GetNamingOptions());
+ return IsVideoFile(path, new LibraryOptions());
+ }
+
+ public bool IsAudioFile(string path, LibraryOptions libraryOptions)
+ {
+ var parser = new AudioFileParser(GetNamingOptions(libraryOptions));
return parser.IsAudioFile(path);
}
+ public bool IsAudioFile(string path)
+ {
+ return IsAudioFile(path, new LibraryOptions());
+ }
+
public int? GetSeasonNumberFromPath(string path)
{
return new SeasonPathParser(GetNamingOptions(), new RegexProvider()).Parse(path, true, true).SeasonNumber;
@@ -2380,19 +2370,24 @@ namespace MediaBrowser.Server.Implementations.Library
public NamingOptions GetNamingOptions()
{
+ return GetNamingOptions(new LibraryOptions());
+ }
+
+ public NamingOptions GetNamingOptions(LibraryOptions libraryOptions)
+ {
var options = new ExtendedNamingOptions();
// These cause apps to have problems
options.AudioFileExtensions.Remove(".m3u");
options.AudioFileExtensions.Remove(".wpl");
- if (!ConfigurationManager.Configuration.EnableAudioArchiveFiles)
+ if (!libraryOptions.EnableArchiveMediaFiles)
{
options.AudioFileExtensions.Remove(".rar");
options.AudioFileExtensions.Remove(".zip");
}
- if (!ConfigurationManager.Configuration.EnableVideoArchiveFiles)
+ if (!libraryOptions.EnableArchiveMediaFiles)
{
options.VideoFileExtensions.Remove(".rar");
options.VideoFileExtensions.Remove(".zip");
@@ -2443,7 +2438,7 @@ namespace MediaBrowser.Server.Implementations.Library
new GenericVideoResolver<Trailer>(this)
};
- return ResolvePaths(files, directoryService, null, null, resolvers)
+ return ResolvePaths(files, directoryService, null, new LibraryOptions(), null, resolvers)
.OfType<Trailer>()
.Select(video =>
{
@@ -2487,7 +2482,7 @@ namespace MediaBrowser.Server.Implementations.Library
files.AddRange(currentVideo.Extras.Where(i => !string.Equals(i.ExtraType, "trailer", StringComparison.OrdinalIgnoreCase)).Select(i => _fileSystem.GetFileInfo(i.Path)));
}
- return ResolvePaths(files, directoryService, null, null)
+ return ResolvePaths(files, directoryService, null, new LibraryOptions(), null)
.OfType<Video>()
.Select(video =>
{
@@ -2665,7 +2660,7 @@ namespace MediaBrowser.Server.Implementations.Library
throw new InvalidOperationException();
}
- public void AddVirtualFolder(string name, string collectionType, string[] mediaPaths, bool refreshLibrary)
+ public void AddVirtualFolder(string name, string collectionType, string[] mediaPaths, LibraryOptions options, bool refreshLibrary)
{
if (string.IsNullOrWhiteSpace(name))
{
@@ -2708,6 +2703,8 @@ namespace MediaBrowser.Server.Implementations.Library
}
}
+ CollectionFolder.SaveLibraryOptions(virtualFolderPath, options);
+
if (mediaPaths != null)
{
foreach (var path in mediaPaths)
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
index b4cda39cd..039a17100 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/AudioResolver.cs
@@ -37,14 +37,16 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
if (!args.IsDirectory)
{
- if (_libraryManager.IsAudioFile(args.Path))
+ var libraryOptions = args.GetLibraryOptions();
+
+ if (_libraryManager.IsAudioFile(args.Path, libraryOptions))
{
var collectionType = args.GetCollectionType();
var isMixed = string.IsNullOrWhiteSpace(collectionType);
// For conflicting extensions, give priority to videos
- if (isMixed && _libraryManager.IsVideoFile(args.Path))
+ if (isMixed && _libraryManager.IsVideoFile(args.Path, libraryOptions))
{
return null;
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
index 9f8293cb5..1a8295800 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
@@ -10,6 +10,8 @@ using System;
using System.Collections.Generic;
using System.IO;
using CommonIO;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.Configuration;
namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
{
@@ -72,12 +74,9 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
/// <summary>
/// Determine if the supplied file data points to a music album
/// </summary>
- /// <param name="path">The path.</param>
- /// <param name="directoryService">The directory service.</param>
- /// <returns><c>true</c> if [is music album] [the specified data]; otherwise, <c>false</c>.</returns>
- public bool IsMusicAlbum(string path, IDirectoryService directoryService)
+ public bool IsMusicAlbum(string path, IDirectoryService directoryService, LibraryOptions libraryOptions)
{
- return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService, _logger, _fileSystem, _libraryManager);
+ return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService, _logger, _fileSystem, libraryOptions, _libraryManager);
}
/// <summary>
@@ -91,7 +90,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
if (args.IsDirectory)
{
//if (args.Parent is MusicArtist) return true; //saves us from testing children twice
- if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService, _logger, _fileSystem, _libraryManager)) return true;
+ if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService, _logger, _fileSystem, args.GetLibraryOptions(), _libraryManager)) return true;
}
return false;
@@ -100,18 +99,12 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
/// <summary>
/// Determine if the supplied list contains what we should consider music
/// </summary>
- /// <param name="list">The list.</param>
- /// <param name="allowSubfolders">if set to <c>true</c> [allow subfolders].</param>
- /// <param name="directoryService">The directory service.</param>
- /// <param name="logger">The logger.</param>
- /// <param name="fileSystem">The file system.</param>
- /// <param name="libraryManager">The library manager.</param>
- /// <returns><c>true</c> if the specified list contains music; otherwise, <c>false</c>.</returns>
private bool ContainsMusic(IEnumerable<FileSystemMetadata> list,
bool allowSubfolders,
IDirectoryService directoryService,
ILogger logger,
IFileSystem fileSystem,
+ LibraryOptions libraryOptions,
ILibraryManager libraryManager)
{
var discSubfolderCount = 0;
@@ -124,11 +117,11 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
if (allowSubfolders)
{
var path = fileSystemInfo.FullName;
- var isMultiDisc = IsMultiDiscFolder(path);
+ var isMultiDisc = IsMultiDiscFolder(path, libraryOptions);
if (isMultiDisc)
{
- var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryManager);
+ var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryOptions, libraryManager);
if (hasMusic)
{
@@ -138,7 +131,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
}
else
{
- var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryManager);
+ var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryOptions, libraryManager);
if (hasMusic)
{
@@ -151,7 +144,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
var fullName = fileSystemInfo.FullName;
- if (libraryManager.IsAudioFile(fullName))
+ if (libraryManager.IsAudioFile(fullName, libraryOptions))
{
return true;
}
@@ -165,9 +158,9 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
return discSubfolderCount > 0;
}
- private bool IsMultiDiscFolder(string path)
+ private bool IsMultiDiscFolder(string path, LibraryOptions libraryOptions)
{
- var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
+ var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(libraryOptions);
var parser = new AlbumParser(namingOptions, new PatternsLogger());
var result = parser.ParseMultiPart(path);
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
index e3c991e7e..e819af06f 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
@@ -72,7 +72,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager);
// If we contain an album assume we are an artist folder
- return args.FileSystemChildren.Where(i => (i.Attributes & FileAttributes.Directory) == FileAttributes.Directory).Any(i => albumResolver.IsMusicAlbum(i.FullName, directoryService)) ? new MusicArtist() : null;
+ return args.FileSystemChildren.Where(i => (i.Attributes & FileAttributes.Directory) == FileAttributes.Directory).Any(i => albumResolver.IsMusicAlbum(i.FullName, directoryService, args.GetLibraryOptions())) ? new MusicArtist() : null;
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index 703a33856..d0042a990 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -133,7 +133,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
return null;
}
- if (LibraryManager.IsVideoFile(args.Path) || videoInfo.IsStub)
+ if (LibraryManager.IsVideoFile(args.Path, args.GetLibraryOptions()) || videoInfo.IsStub)
{
var path = args.Path;
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index 37d1e163f..655840753 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -48,6 +48,12 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
string collectionType,
IDirectoryService directoryService)
{
+ if (parent != null && parent.Path != null && parent.Path.IndexOf("disney", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ var b = true;
+ var a = b;
+ }
+
var result = ResolveMultipleInternal(parent, files, collectionType, directoryService);
if (result != null)
@@ -197,6 +203,12 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
/// <returns>Video.</returns>
protected override Video Resolve(ItemResolveArgs args)
{
+ if (args.Path != null && args.Path.IndexOf("disney", StringComparison.OrdinalIgnoreCase) != -1)
+ {
+ var b = true;
+ var a = b;
+ }
+
var collectionType = args.GetCollectionType();
if (IsInvalid(args.Parent, collectionType))
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs
index 9dd30edde..3f9475480 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs
@@ -6,6 +6,8 @@ using System;
using System.IO;
using System.Linq;
using CommonIO;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.Configuration;
namespace MediaBrowser.Server.Implementations.Library.Resolvers
{
@@ -32,15 +34,16 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
// Must be an image file within a photo collection
var collectionType = args.GetCollectionType();
+
if (string.Equals(collectionType, CollectionType.Photos, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase))
+ (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase) && args.GetLibraryOptions().EnablePhotos))
{
if (IsImageFile(args.Path, _imageProcessor))
{
var filename = Path.GetFileNameWithoutExtension(args.Path);
// Make sure the image doesn't belong to a video file
- if (args.DirectoryService.GetFiles(Path.GetDirectoryName(args.Path)).Any(i => IsOwnedByMedia(i, filename)))
+ if (args.DirectoryService.GetFiles(Path.GetDirectoryName(args.Path)).Any(i => IsOwnedByMedia(args.GetLibraryOptions(), i, filename)))
{
return null;
}
@@ -56,9 +59,9 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
return null;
}
- private bool IsOwnedByMedia(FileSystemMetadata file, string imageFilename)
+ private bool IsOwnedByMedia(LibraryOptions libraryOptions, FileSystemMetadata file, string imageFilename)
{
- if (_libraryManager.IsVideoFile(file.FullName) && imageFilename.StartsWith(Path.GetFileNameWithoutExtension(file.Name), StringComparison.OrdinalIgnoreCase))
+ if (_libraryManager.IsVideoFile(file.FullName, libraryOptions) && imageFilename.StartsWith(Path.GetFileNameWithoutExtension(file.Name), StringComparison.OrdinalIgnoreCase))
{
return true;
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
index 144f788a7..7bb66ed89 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/SpecialFolderResolver.cs
@@ -50,7 +50,8 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
{
return new CollectionFolder
{
- CollectionType = GetCollectionType(args)
+ CollectionType = GetCollectionType(args),
+ PhysicalLocationsList = args.PhysicalLocations.ToList()
};
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
index 2f940eb1d..6edc4a009 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
@@ -56,10 +56,13 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
if (series != null)
{
episode.SeriesId = series.Id;
+ episode.SeriesName = series.Name;
+ episode.SeriesSortName = series.SortName;
}
if (season != null)
{
episode.SeasonId = season.Id;
+ episode.SeasonName = season.Name;
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
index eeac1345e..fc4929748 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
@@ -43,9 +43,11 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
var season = new Season
{
IndexNumber = new SeasonPathParser(namingOptions, new RegexProvider()).Parse(args.Path, true, true).SeasonNumber,
- SeriesId = series.Id
+ SeriesId = series.Id,
+ SeriesSortName = series.SortName,
+ SeriesName = series.Name
};
-
+
if (season.IndexNumber.HasValue && season.IndexNumber.Value == 0)
{
season.Name = _config.Configuration.SeasonZeroDisplayName;
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
index 45ba2ddbb..aefb29f1a 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
@@ -12,6 +12,8 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using CommonIO;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Model.Configuration;
namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
{
@@ -83,7 +85,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
{
return null;
}
- if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, false))
+ if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, args.GetLibraryOptions(), false))
{
return new Series
{
@@ -104,6 +106,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
IFileSystem fileSystem,
ILogger logger,
ILibraryManager libraryManager,
+ LibraryOptions libraryOptions,
bool isTvContentType)
{
foreach (var child in fileSystemChildren)
@@ -134,7 +137,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
else
{
string fullName = child.FullName;
- if (libraryManager.IsVideoFile(fullName))
+ if (libraryManager.IsVideoFile(fullName, libraryOptions))
{
if (isTvContentType)
{
diff --git a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs b/MediaBrowser.Server.Implementations/Library/SearchEngine.cs
index cf6f070d0..a6d6b5cb8 100644
--- a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs
+++ b/MediaBrowser.Server.Implementations/Library/SearchEngine.cs
@@ -166,12 +166,12 @@ namespace MediaBrowser.Server.Implementations.Library
ExcludeItemTypes = excludeItemTypes.ToArray(),
IncludeItemTypes = includeItemTypes.ToArray(),
Limit = query.Limit,
- IncludeItemsByName = true
-
+ IncludeItemsByName = true,
+ IsVirtualItem = false
});
// Add search hints based on item name
- hints.AddRange(mediaItems.Where(IncludeInSearch).Select(item =>
+ hints.AddRange(mediaItems.Select(item =>
{
var index = GetIndex(item.Name, searchTerm, terms);
@@ -187,20 +187,6 @@ namespace MediaBrowser.Server.Implementations.Library
return Task.FromResult(returnValue);
}
- private bool IncludeInSearch(BaseItem item)
- {
- var episode = item as Episode;
-
- if (episode != null)
- {
- if (episode.IsMissingEpisode)
- {
- return false;
- }
- }
- return true;
- }
-
/// <summary>
/// Gets the index.
/// </summary>
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs
index 079867ddd..91b035a35 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/ArtistsPostScanTask.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Model.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Controller.Persistence;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
@@ -16,15 +17,17 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger;
+ private readonly IItemRepository _itemRepo;
/// <summary>
/// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
- public ArtistsPostScanTask(ILibraryManager libraryManager, ILogger logger)
+ public ArtistsPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
+ _itemRepo = itemRepo;
}
/// <summary>
@@ -35,7 +38,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// <returns>Task.</returns>
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
- return new ArtistsValidator(_libraryManager, _logger).Run(progress, cancellationToken);
+ return new ArtistsValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/ArtistsValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/ArtistsValidator.cs
index 353be1a44..3dcdbeae9 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/ArtistsValidator.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/ArtistsValidator.cs
@@ -7,6 +7,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Persistence;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
@@ -24,16 +25,18 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// The _logger
/// </summary>
private readonly ILogger _logger;
+ private readonly IItemRepository _itemRepo;
/// <summary>
/// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
- public ArtistsValidator(ILibraryManager libraryManager, ILogger logger)
+ public ArtistsValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
+ _itemRepo = itemRepo;
}
/// <summary>
@@ -44,18 +47,17 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// <returns>Task.</returns>
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
- var items = _libraryManager.GetAllArtists(new InternalItemsQuery())
- .Items
- .Select(i => i.Item1)
- .ToList();
+ var names = _itemRepo.GetAllArtistNames();
var numComplete = 0;
- var count = items.Count;
+ var count = names.Count;
- foreach (var item in items)
+ foreach (var name in names)
{
try
{
+ var item = _libraryManager.GetArtist(name);
+
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
@@ -65,7 +67,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
}
catch (Exception ex)
{
- _logger.ErrorException("Error refreshing {0}", ex, item.Name);
+ _logger.ErrorException("Error refreshing {0}", ex, name);
}
numComplete++;
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs
index 8be2e436f..f3891180e 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Model.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Controller.Persistence;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
@@ -16,16 +17,18 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger;
+ private readonly IItemRepository _itemRepo;
/// <summary>
/// Initializes a new instance of the <see cref="GameGenresPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
- public GameGenresPostScanTask(ILibraryManager libraryManager, ILogger logger)
+ public GameGenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
+ _itemRepo = itemRepo;
}
/// <summary>
@@ -36,7 +39,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// <returns>Task.</returns>
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
- return new GameGenresValidator(_libraryManager, _logger).Run(progress, cancellationToken);
+ return new GameGenresValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs
index 72864790b..b06c0b3b9 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs
@@ -5,6 +5,7 @@ using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Controller.Persistence;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
@@ -19,11 +20,13 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// The _logger
/// </summary>
private readonly ILogger _logger;
+ private readonly IItemRepository _itemRepo;
- public GameGenresValidator(ILibraryManager libraryManager, ILogger logger)
+ public GameGenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
+ _itemRepo = itemRepo;
}
/// <summary>
@@ -34,21 +37,17 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// <returns>Task.</returns>
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
- var items = _libraryManager.GetGameGenres(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(Game).Name }
- })
- .Items
- .Select(i => i.Item1)
- .ToList();
+ var names = _itemRepo.GetGameGenreNames();
var numComplete = 0;
- var count = items.Count;
+ var count = names.Count;
- foreach (var item in items)
+ foreach (var name in names)
{
try
{
+ var item = _libraryManager.GetGameGenre(name);
+
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
@@ -58,7 +57,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
}
catch (Exception ex)
{
- _logger.ErrorException("Error refreshing {0}", ex, item.Name);
+ _logger.ErrorException("Error refreshing {0}", ex, name);
}
numComplete++;
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs
index a1c34676c..ed2429769 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs
@@ -2,6 +2,7 @@
using System;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Logging;
namespace MediaBrowser.Server.Implementations.Library.Validators
@@ -13,16 +14,18 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger;
+ private readonly IItemRepository _itemRepo;
/// <summary>
/// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
- public GenresPostScanTask(ILibraryManager libraryManager, ILogger logger)
+ public GenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
+ _itemRepo = itemRepo;
}
/// <summary>
@@ -33,7 +36,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// <returns>Task.</returns>
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
- return new GenresValidator(_libraryManager, _logger).Run(progress, cancellationToken);
+ return new GenresValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs
index 6a62d7fe4..f35bb5136 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs
@@ -6,6 +6,7 @@ using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Controller.Persistence;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
@@ -15,16 +16,18 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// The _library manager
/// </summary>
private readonly ILibraryManager _libraryManager;
+ private readonly IItemRepository _itemRepo;
/// <summary>
/// The _logger
/// </summary>
private readonly ILogger _logger;
- public GenresValidator(ILibraryManager libraryManager, ILogger logger)
+ public GenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
+ _itemRepo = itemRepo;
}
/// <summary>
@@ -35,21 +38,17 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// <returns>Task.</returns>
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
- var items = _libraryManager.GetGenres(new InternalItemsQuery
- {
- ExcludeItemTypes = new[] { typeof(Audio).Name, typeof(MusicArtist).Name, typeof(MusicAlbum).Name, typeof(MusicVideo).Name, typeof(Game).Name }
- })
- .Items
- .Select(i => i.Item1)
- .ToList();
+ var names = _itemRepo.GetGenreNames();
var numComplete = 0;
- var count = items.Count;
+ var count = names.Count;
- foreach (var item in items)
+ foreach (var name in names)
{
try
{
+ var item = _libraryManager.GetGenre(name);
+
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
@@ -59,7 +58,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
}
catch (Exception ex)
{
- _logger.ErrorException("Error refreshing {0}", ex, item.Name);
+ _logger.ErrorException("Error refreshing {0}", ex, name);
}
numComplete++;
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs
index dbcab0832..777532ff8 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Model.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Controller.Persistence;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
@@ -16,16 +17,18 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger;
+ private readonly IItemRepository _itemRepo;
/// <summary>
/// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
/// <param name="logger">The logger.</param>
- public MusicGenresPostScanTask(ILibraryManager libraryManager, ILogger logger)
+ public MusicGenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
+ _itemRepo = itemRepo;
}
/// <summary>
@@ -36,7 +39,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// <returns>Task.</returns>
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
- return new MusicGenresValidator(_libraryManager, _logger).Run(progress, cancellationToken);
+ return new MusicGenresValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs
index 2668d84e9..2be99f106 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Persistence;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
@@ -20,11 +21,13 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// The _logger
/// </summary>
private readonly ILogger _logger;
+ private readonly IItemRepository _itemRepo;
- public MusicGenresValidator(ILibraryManager libraryManager, ILogger logger)
+ public MusicGenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
+ _itemRepo = itemRepo;
}
/// <summary>
@@ -35,21 +38,17 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// <returns>Task.</returns>
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
- var items = _libraryManager.GetMusicGenres(new InternalItemsQuery
- {
- IncludeItemTypes = new[] { typeof(Audio).Name, typeof(MusicArtist).Name, typeof(MusicAlbum).Name, typeof(MusicVideo).Name }
- })
- .Items
- .Select(i => i.Item1)
- .ToList();
+ var names = _itemRepo.GetMusicGenreNames();
var numComplete = 0;
- var count = items.Count;
+ var count = names.Count;
- foreach (var item in items)
+ foreach (var name in names)
{
try
{
+ var item = _libraryManager.GetMusicGenre(name);
+
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
@@ -59,7 +58,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
}
catch (Exception ex)
{
- _logger.ErrorException("Error refreshing {0}", ex, item.Name);
+ _logger.ErrorException("Error refreshing {0}", ex, name);
}
numComplete++;
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs
index 191c7ef28..d90b9615b 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/PeopleValidator.cs
@@ -126,7 +126,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
var item = _libraryManager.GetPerson(person.Key);
var hasMetdata = !string.IsNullOrWhiteSpace(item.Overview);
- var performFullRefresh = !hasMetdata && (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= 60;
+ var performFullRefresh = !hasMetdata && (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= 30;
var defaultMetadataRefreshMode = performFullRefresh
? MetadataRefreshMode.FullRefresh
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/StudiosPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/StudiosPostScanTask.cs
index 0ff609da1..77c6d5146 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/StudiosPostScanTask.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/StudiosPostScanTask.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Model.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Controller.Persistence;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
@@ -17,15 +18,17 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
private readonly ILibraryManager _libraryManager;
private readonly ILogger _logger;
+ private readonly IItemRepository _itemRepo;
/// <summary>
/// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
/// </summary>
/// <param name="libraryManager">The library manager.</param>
- public StudiosPostScanTask(ILibraryManager libraryManager, ILogger logger)
+ public StudiosPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
+ _itemRepo = itemRepo;
}
/// <summary>
@@ -36,7 +39,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// <returns>Task.</returns>
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
- return new StudiosValidator(_libraryManager, _logger).Run(progress, cancellationToken);
+ return new StudiosValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs b/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs
index 722b74891..a19b8158a 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/StudiosValidator.cs
@@ -5,6 +5,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Persistence;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
@@ -15,15 +16,17 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// </summary>
private readonly ILibraryManager _libraryManager;
+ private readonly IItemRepository _itemRepo;
/// <summary>
/// The _logger
/// </summary>
private readonly ILogger _logger;
- public StudiosValidator(ILibraryManager libraryManager, ILogger logger)
+ public StudiosValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
{
_libraryManager = libraryManager;
_logger = logger;
+ _itemRepo = itemRepo;
}
/// <summary>
@@ -34,18 +37,17 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// <returns>Task.</returns>
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
- var items = _libraryManager.GetStudios(new InternalItemsQuery())
- .Items
- .Select(i => i.Item1)
- .ToList();
+ var names = _itemRepo.GetStudioNames();
var numComplete = 0;
- var count = items.Count;
+ var count = names.Count;
- foreach (var item in items)
+ foreach (var name in names)
{
try
{
+ var item = _libraryManager.GetStudio(name);
+
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (OperationCanceledException)
@@ -55,7 +57,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
}
catch (Exception ex)
{
- _logger.ErrorException("Error refreshing {0}", ex, item.Name);
+ _logger.ErrorException("Error refreshing {0}", ex, name);
}
numComplete++;
diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index ee8ab7c25..6acb0783e 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -28,6 +28,7 @@ using CommonIO;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Power;
+using MediaBrowser.Model.Configuration;
using Microsoft.Win32;
namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
@@ -143,7 +144,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
try
{
- _libraryManager.AddVirtualFolder(recordingFolder.Name, recordingFolder.CollectionType, pathsToCreate.ToArray(), true);
+ _libraryManager.AddVirtualFolder(recordingFolder.Name, recordingFolder.CollectionType, pathsToCreate.ToArray(), new LibraryOptions(), true);
}
catch (Exception ex)
{
@@ -1138,7 +1139,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
var organize = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager, _libraryMonitor, _providerManager);
- var result = await organize.OrganizeEpisodeFile(path, CancellationToken.None).ConfigureAwait(false);
+ var result = await organize.OrganizeEpisodeFile(path, _config.GetAutoOrganizeOptions(), false, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
index 09d156661..ccbcb910d 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -951,6 +951,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var queryResult = _libraryManager.QueryItems(internalQuery);
+ RemoveFields(options);
+
var returnArray = (await _dtoService.GetBaseItemDtos(queryResult.Items, options, user).ConfigureAwait(false)).ToArray();
var result = new QueryResult<BaseItemDto>
@@ -1031,6 +1033,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var user = _userManager.GetUserById(query.UserId);
+ RemoveFields(options);
+
var returnArray = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user).ConfigureAwait(false)).ToArray();
var result = new QueryResult<BaseItemDto>
@@ -1662,6 +1666,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var internalResult = await GetInternalRecordings(query, cancellationToken).ConfigureAwait(false);
+ RemoveFields(options);
+
var returnArray = (await _dtoService.GetBaseItemDtos(internalResult.Items, options, user).ConfigureAwait(false)).ToArray();
return new QueryResult<BaseItemDto>
@@ -1922,7 +1928,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var channelIds = tuples.Select(i => i.Item2.Id.ToString("N")).Distinct().ToArray();
- var programs = _libraryManager.GetItemList(new InternalItemsQuery(user)
+ var programs = options.AddCurrentProgram ? _libraryManager.GetItemList(new InternalItemsQuery(user)
{
IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
ChannelIds = channelIds,
@@ -1932,7 +1938,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
SortBy = new[] { "StartDate" },
TopParentIds = new[] { GetInternalLiveTvFolder(CancellationToken.None).Result.Id.ToString("N") }
- }).ToList();
+ }).ToList() : new List<BaseItem>();
+
+ RemoveFields(options);
foreach (var tuple in tuples)
{
@@ -1944,14 +1952,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv
dto.ChannelType = channel.ChannelType;
dto.ServiceName = GetService(channel).Name;
- dto.MediaSources = channel.GetMediaSources(true).ToList();
+ if (options.Fields.Contains(ItemFields.MediaSources))
+ {
+ dto.MediaSources = channel.GetMediaSources(true).ToList();
+ }
var channelIdString = channel.Id.ToString("N");
- var currentProgram = programs.FirstOrDefault(i => string.Equals(i.ChannelId, channelIdString));
-
- if (currentProgram != null)
+ if (options.AddCurrentProgram)
{
- dto.CurrentProgram = _dtoService.GetBaseItemDto(currentProgram, options, user);
+ var currentProgram = programs.FirstOrDefault(i => string.Equals(i.ChannelId, channelIdString));
+
+ if (currentProgram != null)
+ {
+ dto.CurrentProgram = _dtoService.GetBaseItemDto(currentProgram, options, user);
+ }
}
}
}
@@ -2447,6 +2461,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return _dtoService.GetBaseItemDto(folder, new DtoOptions(), user);
}
+ private void RemoveFields(DtoOptions options)
+ {
+ options.Fields.Remove(ItemFields.CanDelete);
+ options.Fields.Remove(ItemFields.CanDownload);
+ options.Fields.Remove(ItemFields.DisplayPreferencesId);
+ options.Fields.Remove(ItemFields.Etag);
+ }
+
public async Task<Folder> GetInternalLiveTvFolder(CancellationToken cancellationToken)
{
var name = _localization.GetLocalizedString("ViewTypeLiveTV");
diff --git a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index 5b83e7cbe..5c508aacd 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
@@ -136,7 +136,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
RequiresOpening = false,
RequiresClosing = false,
- ReadAtNativeFramerate = true
+ ReadAtNativeFramerate = false
};
return new List<MediaSourceInfo> { mediaSource };
diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
index 52723688f..7fa9ee32c 100644
--- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
+++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
@@ -46,7 +46,7 @@
<HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
</Reference>
<Reference Include="Emby.XmlTv, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <HintPath>..\packages\Emby.XmlTv.1.0.0.55\lib\net45\Emby.XmlTv.dll</HintPath>
+ <HintPath>..\packages\Emby.XmlTv.1.0.0.56\lib\net45\Emby.XmlTv.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="INIFileParser, Version=2.3.0.0, Culture=neutral, PublicKeyToken=79af7b307b65cf3c, processorArchitecture=MSIL">
@@ -156,7 +156,7 @@
<Compile Include="EntryPoints\ServerEventNotifier.cs" />
<Compile Include="EntryPoints\UserDataChangeNotifier.cs" />
<Compile Include="FileOrganization\OrganizerScheduledTask.cs" />
- <Compile Include="HttpServer\AsyncStreamWriterFunc.cs" />
+ <Compile Include="HttpServer\AsyncStreamWriter.cs" />
<Compile Include="HttpServer\IHttpListener.cs" />
<Compile Include="HttpServer\Security\AuthorizationContext.cs" />
<Compile Include="HttpServer\ContainerAdapter.cs" />
diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
index b4f8245ed..c3eee6d35 100644
--- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
+++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
@@ -412,7 +412,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
"SeasonName",
"SeasonId",
"SeriesId",
- "SeriesSortName"
+ "SeriesSortName",
+ "PresentationUniqueKey"
};
private readonly string[] _mediaStreamSaveColumns =
@@ -918,7 +919,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
_saveItemCommand.GetParameter(index++).Value = GetCleanValue(item.Name);
}
- _saveItemCommand.GetParameter(index++).Value = item.PresentationUniqueKey;
+ _saveItemCommand.GetParameter(index++).Value = item.GetPresentationUniqueKey();
_saveItemCommand.GetParameter(index++).Value = item.SlugName;
_saveItemCommand.GetParameter(index++).Value = item.OriginalTitle;
@@ -1454,6 +1455,12 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
index++;
+ if (!reader.IsDBNull(index))
+ {
+ item.PresentationUniqueKey = reader.GetString(index);
+ }
+ index++;
+
return item;
}
@@ -2145,7 +2152,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
if (query.User != null)
{
- query.SortBy = new[] { ItemSortBy.IsPlayed, "SimilarityScore", ItemSortBy.Random };
+ query.SortBy = new[] { "SimilarityScore", ItemSortBy.Random };
}
else
{
@@ -2230,7 +2237,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
if (string.Equals(name, ItemSortBy.OfficialRating, StringComparison.OrdinalIgnoreCase))
{
- return new Tuple<string, bool>("ParentalRatingValue", false);
+ return new Tuple<string, bool>("InheritedParentalRatingValue", false);
}
if (string.Equals(name, ItemSortBy.Studio, StringComparison.OrdinalIgnoreCase))
{
@@ -3088,6 +3095,17 @@ namespace MediaBrowser.Server.Implementations.Persistence
whereClauses.Add("LocationType<>'Virtual'");
}
}
+ if (query.IsSpecialSeason.HasValue)
+ {
+ if (query.IsSpecialSeason.Value)
+ {
+ whereClauses.Add("IndexNumber = 0");
+ }
+ else
+ {
+ whereClauses.Add("IndexNumber <> 0");
+ }
+ }
if (query.IsUnaired.HasValue)
{
if (query.IsUnaired.Value)
@@ -3134,17 +3152,17 @@ namespace MediaBrowser.Server.Implementations.Persistence
}
if (query.ItemIds.Length > 0)
{
- var excludeIds = new List<string>();
+ var includeIds = new List<string>();
var index = 0;
foreach (var id in query.ItemIds)
{
- excludeIds.Add("Guid = @IncludeId" + index);
+ includeIds.Add("Guid = @IncludeId" + index);
cmd.Parameters.Add(cmd, "@IncludeId" + index, DbType.Guid).Value = new Guid(id);
index++;
}
- whereClauses.Add(string.Join(" OR ", excludeIds.ToArray()));
+ whereClauses.Add(string.Join(" OR ", includeIds.ToArray()));
}
if (query.ExcludeItemIds.Length > 0)
{
@@ -3473,7 +3491,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
using (var cmd = _connection.CreateCommand())
{
- cmd.CommandText = "select Guid,InheritedParentalRatingValue,(select Max(ParentalRatingValue, (select COALESCE(MAX(ParentalRatingValue),0) from TypedBaseItems where guid in (Select AncestorId from AncestorIds where ItemId=Outer.guid)))) as NewInheritedParentalRatingValue from typedbaseitems as Outer where InheritedParentalRatingValue <> NewInheritedParentalRatingValue";
+ cmd.CommandText = "select Guid,InheritedParentalRatingValue,(select Max(InheritedParentalRatingValue, (select COALESCE(MAX(InheritedParentalRatingValue),0) from TypedBaseItems where guid in (Select AncestorId from AncestorIds where ItemId=Outer.guid)))) as NewInheritedParentalRatingValue from typedbaseitems as Outer where InheritedParentalRatingValue <> NewInheritedParentalRatingValue";
using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
{
@@ -3862,6 +3880,83 @@ namespace MediaBrowser.Server.Implementations.Persistence
return GetItemValues(query, new[] { 2 }, typeof(MusicGenre).FullName);
}
+ public List<string> GetStudioNames()
+ {
+ return GetItemValueNames(new[] { 3 }, new List<string>(), new List<string>());
+ }
+
+ public List<string> GetAllArtistNames()
+ {
+ return GetItemValueNames(new[] { 0, 1 }, new List<string>(), new List<string>());
+ }
+
+ public List<string> GetMusicGenreNames()
+ {
+ return GetItemValueNames(new[] { 2 }, new List<string> { "Audio", "MusicVideo", "MusicAlbum", "MusicArtist" }, new List<string>());
+ }
+
+ public List<string> GetGameGenreNames()
+ {
+ return GetItemValueNames(new[] { 2 }, new List<string> { "Game" }, new List<string>());
+ }
+
+ public List<string> GetGenreNames()
+ {
+ return GetItemValueNames(new[] { 2 }, new List<string>(), new List<string> { "Audio", "MusicVideo", "MusicAlbum", "MusicArtist", "Game", "GameSystem" });
+ }
+
+ private List<string> GetItemValueNames(int[] itemValueTypes, List<string> withItemTypes, List<string> excludeItemTypes)
+ {
+ CheckDisposed();
+
+ withItemTypes = withItemTypes.SelectMany(MapIncludeItemTypes).ToList();
+ excludeItemTypes = excludeItemTypes.SelectMany(MapIncludeItemTypes).ToList();
+
+ var now = DateTime.UtcNow;
+
+ var typeClause = itemValueTypes.Length == 1 ?
+ ("Type=" + itemValueTypes[0].ToString(CultureInfo.InvariantCulture)) :
+ ("Type in (" + string.Join(",", itemValueTypes.Select(i => i.ToString(CultureInfo.InvariantCulture)).ToArray()) + ")");
+
+ var list = new List<string>();
+
+ using (var cmd = _connection.CreateCommand())
+ {
+ cmd.CommandText = "Select Value From ItemValues where " + typeClause;
+
+ if (withItemTypes.Count > 0)
+ {
+ var typeString = string.Join(",", withItemTypes.Select(i => "'" + i + "'").ToArray());
+ cmd.CommandText += " AND ItemId In (select guid from typedbaseitems where type in (" + typeString + "))";
+ }
+ if (excludeItemTypes.Count > 0)
+ {
+ var typeString = string.Join(",", excludeItemTypes.Select(i => "'" + i + "'").ToArray());
+ cmd.CommandText += " AND ItemId not In (select guid from typedbaseitems where type in (" + typeString + "))";
+ }
+
+ cmd.CommandText += " Group By CleanValue";
+
+ var commandBehavior = CommandBehavior.SequentialAccess | CommandBehavior.SingleResult;
+
+ using (var reader = cmd.ExecuteReader(commandBehavior))
+ {
+ LogQueryTime("GetItemValueNames", cmd, now);
+
+ while (reader.Read())
+ {
+ if (!reader.IsDBNull(0))
+ {
+ list.Add(reader.GetString(0));
+ }
+ }
+ }
+
+ }
+
+ return list;
+ }
+
private QueryResult<Tuple<BaseItem, ItemCounts>> GetItemValues(InternalItemsQuery query, int[] itemValueTypes, string returnType)
{
if (query == null)
@@ -3968,7 +4063,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
AlbumArtistStartsWithOrGreater = query.AlbumArtistStartsWithOrGreater,
Tags = query.Tags,
OfficialRatings = query.OfficialRatings,
- Genres = query.GenreIds,
+ GenreIds = query.GenreIds,
+ Genres = query.Genres,
Years = query.Years
};
@@ -4034,7 +4130,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
? (CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)
: CommandBehavior.SequentialAccess;
- Logger.Debug("GetItemValues: " + cmd.CommandText);
+ //Logger.Debug("GetItemValues: " + cmd.CommandText);
using (var reader = cmd.ExecuteReader(commandBehavior))
{
@@ -4121,6 +4217,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
{
counts.AlbumCount = value;
}
+ else if (string.Equals(typeName, typeof(MusicArtist).FullName, StringComparison.OrdinalIgnoreCase))
+ {
+ counts.ArtistCount = value;
+ }
else if (string.Equals(typeName, typeof(Audio).FullName, StringComparison.OrdinalIgnoreCase))
{
counts.SongCount = value;
diff --git a/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs b/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
index 20324215b..ff0e4a0e0 100644
--- a/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
+++ b/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs
@@ -22,7 +22,7 @@ namespace MediaBrowser.Server.Implementations.Playlists
protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
{
- return GetRecursiveChildren(i => i is Playlist);
+ return base.GetEligibleChildrenForRecursiveChildren(user).OfType<Playlist>();
}
public override bool IsHidden
diff --git a/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs b/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs
index 8a04f29a2..237d49fda 100644
--- a/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs
+++ b/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs
@@ -88,6 +88,14 @@ namespace MediaBrowser.Server.Implementations
}
}
+ public string ArtistsPath
+ {
+ get
+ {
+ return Path.Combine(ItemsByNamePath, "artists");
+ }
+ }
+
/// <summary>
/// Gets the path to the Genre directory
/// </summary>
diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
index 84aab5e1f..f495e557a 100644
--- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs
+++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs
@@ -1001,7 +1001,8 @@ namespace MediaBrowser.Server.Implementations.Session
var series = episode.Series;
if (series != null)
{
- var episodes = series.GetEpisodes(user, false, false)
+ var episodes = series.GetEpisodes(user)
+ .Where(i => !i.IsVirtualItem)
.SkipWhile(i => i.Id != episode.Id)
.ToList();
diff --git a/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs b/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs
index d7c77e655..6b7bcfa01 100644
--- a/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs
+++ b/MediaBrowser.Server.Implementations/Sync/SyncRepository.cs
@@ -414,6 +414,20 @@ namespace MediaBrowser.Server.Implementations.Sync
whereClauses.Add("TargetId=@TargetId");
cmd.Parameters.Add(cmd, "@TargetId", DbType.String).Value = query.TargetId;
}
+ if (!string.IsNullOrWhiteSpace(query.ExcludeTargetIds))
+ {
+ var excludeIds = (query.ExcludeTargetIds ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
+ if (excludeIds.Length == 1)
+ {
+ whereClauses.Add("TargetId<>@ExcludeTargetId");
+ cmd.Parameters.Add(cmd, "@ExcludeTargetId", DbType.String).Value = excludeIds[0];
+ }
+ else if (excludeIds.Length > 1)
+ {
+ whereClauses.Add("TargetId<>@ExcludeTargetId");
+ cmd.Parameters.Add(cmd, "@ExcludeTargetId", DbType.String).Value = excludeIds[0];
+ }
+ }
if (!string.IsNullOrWhiteSpace(query.UserId))
{
whereClauses.Add("UserId=@UserId");
diff --git a/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs b/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs
index ddc1de9cd..03e8a9178 100644
--- a/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs
+++ b/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs
@@ -152,7 +152,7 @@ namespace MediaBrowser.Server.Implementations.TV
{
return series.Id.ToString("N");
}
- return series.PresentationUniqueKey;
+ return series.GetPresentationUniqueKey();
}
/// <summary>
diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config
index bac5a7c5d..746dc7f62 100644
--- a/MediaBrowser.Server.Implementations/packages.config
+++ b/MediaBrowser.Server.Implementations/packages.config
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
- <package id="Emby.XmlTv" version="1.0.0.55" targetFramework="net45" />
+ <package id="Emby.XmlTv" version="1.0.0.56" targetFramework="net45" />
<package id="ini-parser" version="2.3.0" targetFramework="net45" />
<package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
<package id="MediaBrowser.Naming" version="1.0.0.55" targetFramework="net45" />
diff --git a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
index 236b0879f..104923f8e 100644
--- a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
+++ b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
@@ -639,6 +639,18 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\syncsettings.html">
<Link>Resources\dashboard-ui\syncsettings.html</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\touchicon.png">
+ <Link>Resources\dashboard-ui\touchicon.png</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\touchicon114.png">
+ <Link>Resources\dashboard-ui\touchicon114.png</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\touchicon144.png">
+ <Link>Resources\dashboard-ui\touchicon144.png</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\touchicon72.png">
+ <Link>Resources\dashboard-ui\touchicon72.png</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\tv.html">
<Link>Resources\dashboard-ui\tv.html</Link>
</BundleResource>
@@ -1005,8 +1017,14 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\apiclient.js">
<Link>Resources\dashboard-ui\bower_components\emby-apiclient\apiclient.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\appstorage.js">
- <Link>Resources\dashboard-ui\bower_components\emby-apiclient\appstorage.js</Link>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\appstorage-cache.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-apiclient\appstorage-cache.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\appstorage-localstorage.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-apiclient\appstorage-localstorage.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\appstorage-memory.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-apiclient\appstorage-memory.js</Link>
</BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\bower.json">
<Link>Resources\dashboard-ui\bower_components\emby-apiclient\bower.json</Link>
@@ -1050,9 +1068,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\sync\serversync.js">
<Link>Resources\dashboard-ui\bower_components\emby-apiclient\sync\serversync.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-icons\emby-icons.html">
- <Link>Resources\dashboard-ui\bower_components\emby-icons\emby-icons.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\.bower.json">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\.bower.json</Link>
</BundleResource>
@@ -1251,6 +1266,12 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\emby-slider\emby-slider.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\emby-slider\emby-slider.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\emby-tabs\emby-tabs.css">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\emby-tabs\emby-tabs.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\emby-tabs\emby-tabs.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\emby-tabs\emby-tabs.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\emby-textarea\emby-textarea.css">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\emby-textarea\emby-textarea.css</Link>
</BundleResource>
@@ -3603,6 +3624,12 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\viewcontainer-lite.js">
<Link>Resources\dashboard-ui\components\viewcontainer-lite.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\appfooter\appfooter.css">
+ <Link>Resources\dashboard-ui\components\appfooter\appfooter.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\appfooter\appfooter.js">
+ <Link>Resources\dashboard-ui\components\appfooter\appfooter.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\channelmapper\channelmapper.js">
<Link>Resources\dashboard-ui\components\channelmapper\channelmapper.js</Link>
</BundleResource>
@@ -3612,6 +3639,12 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\directorybrowser\directorybrowser.js">
<Link>Resources\dashboard-ui\components\directorybrowser\directorybrowser.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\dockedtabs\dockedtabs.css">
+ <Link>Resources\dashboard-ui\components\dockedtabs\dockedtabs.css</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\dockedtabs\dockedtabs.js">
+ <Link>Resources\dashboard-ui\components\dockedtabs\dockedtabs.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\fileorganizer\fileorganizer.js">
<Link>Resources\dashboard-ui\components\fileorganizer\fileorganizer.js</Link>
</BundleResource>
@@ -3657,6 +3690,12 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\imageuploader\imageuploader.template.html">
<Link>Resources\dashboard-ui\components\imageuploader\imageuploader.template.html</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\libraryoptionseditor\libraryoptionseditor.js">
+ <Link>Resources\dashboard-ui\components\libraryoptionseditor\libraryoptionseditor.js</Link>
+ </BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\libraryoptionseditor\libraryoptionseditor.template.html">
+ <Link>Resources\dashboard-ui\components\libraryoptionseditor\libraryoptionseditor.template.html</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\medialibrarycreator\medialibrarycreator.js">
<Link>Resources\dashboard-ui\components\medialibrarycreator\medialibrarycreator.js</Link>
</BundleResource>
@@ -3675,6 +3714,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\navdrawer\navdrawer.js">
<Link>Resources\dashboard-ui\components\navdrawer\navdrawer.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\syncjoblist\syncjoblist.js">
+ <Link>Resources\dashboard-ui\components\syncjoblist\syncjoblist.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\components\tvproviders\schedulesdirect.js">
<Link>Resources\dashboard-ui\components\tvproviders\schedulesdirect.js</Link>
</BundleResource>
@@ -3750,9 +3792,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\logo.png">
<Link>Resources\dashboard-ui\css\images\logo.png</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\logo536.png">
- <Link>Resources\dashboard-ui\css\images\logo536.png</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\mblogoicon.png">
<Link>Resources\dashboard-ui\css\images\mblogoicon.png</Link>
</BundleResource>
@@ -3762,18 +3801,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\rotten.png">
<Link>Resources\dashboard-ui\css\images\rotten.png</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\touchicon.png">
- <Link>Resources\dashboard-ui\css\images\touchicon.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\touchicon114.png">
- <Link>Resources\dashboard-ui\css\images\touchicon114.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\touchicon144.png">
- <Link>Resources\dashboard-ui\css\images\touchicon144.png</Link>
- </BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\touchicon72.png">
- <Link>Resources\dashboard-ui\css\images\touchicon72.png</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\userflyoutdefault.png">
<Link>Resources\dashboard-ui\css\images\userflyoutdefault.png</Link>
</BundleResource>
@@ -4434,6 +4461,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\ar.json">
<Link>Resources\dashboard-ui\strings\ar.json</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\be-BY.json">
+ <Link>Resources\dashboard-ui\strings\be-BY.json</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\strings\bg-BG.json">
<Link>Resources\dashboard-ui\strings\bg-BG.json</Link>
</BundleResource>
diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs
index ce99f0a24..8cb1d4f0d 100644
--- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs
+++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs
@@ -101,6 +101,7 @@ using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using CommonIO;
+using MediaBrowser.Api.Playback;
using MediaBrowser.Common.Implementations.Updates;
namespace MediaBrowser.Server.Startup.Common
@@ -765,6 +766,9 @@ namespace MediaBrowser.Server.Startup.Common
UserView.PlaylistManager = PlaylistManager;
BaseItem.CollectionManager = CollectionManager;
BaseItem.MediaSourceManager = MediaSourceManager;
+ CollectionFolder.XmlSerializer = XmlSerializer;
+ BaseStreamingService.AppHost = this;
+ BaseStreamingService.HttpClient = HttpClient;
}
/// <summary>
@@ -820,42 +824,11 @@ namespace MediaBrowser.Server.Startup.Common
private string CertificatePath { get; set; }
- private string NormalizeConfiguredLocalAddress(string address)
- {
- var index = address.Trim('/').IndexOf('/');
-
- if (index != -1)
- {
- address = address.Substring(index + 1);
- }
-
- return address.Trim('/');
- }
private IEnumerable<string> GetUrlPrefixes()
{
- var hosts = ServerConfigurationManager
- .Configuration
- .LocalNetworkAddresses
- .Select(NormalizeConfiguredLocalAddress)
- .ToList();
+ var hosts = new List<string>();
- if (hosts.Count == 0)
- {
- hosts.Add("+");
- }
-
- if (!hosts.Contains("+", StringComparer.OrdinalIgnoreCase))
- {
- if (!hosts.Contains("localhost", StringComparer.OrdinalIgnoreCase))
- {
- hosts.Add("localhost");
- }
-
- if (!hosts.Contains("127.0.0.1", StringComparer.OrdinalIgnoreCase))
- {
- hosts.Add("127.0.0.1");
- }
- }
+ hosts.Add("+");
return hosts.SelectMany(i =>
{
diff --git a/MediaBrowser.ServerApplication/App.config b/MediaBrowser.ServerApplication/App.config
index 6d840c191..fa7bc9f89 100644
--- a/MediaBrowser.ServerApplication/App.config
+++ b/MediaBrowser.ServerApplication/App.config
@@ -17,7 +17,7 @@
<add key="ClientSettingsProvider.ServiceUri" value=""/>
</appSettings>
<startup useLegacyV2RuntimeActivationPolicy="true">
- <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/>
+ <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/>
</startup>
<runtime>
<gcAllowVeryLargeObjects enabled="true"/>
diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
index b01d8c43f..9c5470358 100644
--- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
+++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
@@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MediaBrowser.ServerApplication</RootNamespace>
<AssemblyName>MediaBrowser.ServerApplication</AssemblyName>
- <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
+ <TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<TargetFrameworkProfile />
diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs
index 34ad32111..601ebfba7 100644
--- a/MediaBrowser.WebDashboard/Api/DashboardService.cs
+++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs
@@ -17,7 +17,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CommonIO;
-using WebMarkupMin.Core.Minifiers;
+using WebMarkupMin.Core;
namespace MediaBrowser.WebDashboard.Api
{
@@ -58,6 +58,11 @@ namespace MediaBrowser.WebDashboard.Api
{
}
+ [Route("/web/staticfiles", "GET")]
+ public class GetCacheFiles
+ {
+ }
+
/// <summary>
/// Class GetDashboardResource
/// </summary>
@@ -140,6 +145,27 @@ namespace MediaBrowser.WebDashboard.Api
return ResultFactory.GetStaticResult(Request, page.Plugin.Version.ToString().GetMD5(), null, null, MimeTypes.GetMimeType("page.html"), () => GetPackageCreator().ModifyHtml("dummy.html", page.GetHtmlStream(), null, _appHost.ApplicationVersion.ToString(), null, false));
}
+ public object Get(GetCacheFiles request)
+ {
+ var allFiles = GetCacheFileList();
+
+ return ResultFactory.GetOptimizedResult(Request, _jsonSerializer.SerializeToString(allFiles));
+ }
+
+ private List<string> GetCacheFileList()
+ {
+ var creator = GetPackageCreator();
+ var directory = creator.DashboardUIPath;
+
+ var skipExtensions = GetUndeployedExtensions();
+
+ return
+ Directory.GetFiles(directory, "*", SearchOption.AllDirectories)
+ .Where(i => !skipExtensions.Contains(Path.GetExtension(i) ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+ .Select(i => i.Replace(directory, string.Empty, StringComparison.OrdinalIgnoreCase).Replace("\\", "/").TrimStart('/') + "?v=" + _appHost.ApplicationVersion.ToString())
+ .ToList();
+ }
+
/// <summary>
/// Gets the specified request.
/// </summary>
@@ -274,6 +300,21 @@ namespace MediaBrowser.WebDashboard.Api
return new PackageCreator(_fileSystem, _localization, Logger, _serverConfigurationManager, _jsonSerializer);
}
+ private List<string> GetUndeployedExtensions()
+ {
+ var list = new List<string>();
+
+ list.Add(".log");
+ list.Add(".txt");
+ list.Add(".map");
+ list.Add(".md");
+ list.Add(".gz");
+ list.Add(".bat");
+ list.Add(".sh");
+
+ return list;
+ }
+
public async Task<object> Get(GetDashboardPackage request)
{
var path = Path.Combine(_serverConfigurationManager.ApplicationPaths.ProgramDataPath,
@@ -296,12 +337,9 @@ namespace MediaBrowser.WebDashboard.Api
var appVersion = _appHost.ApplicationVersion.ToString();
- var mode = request.Mode;
+ File.WriteAllText(Path.Combine(path, "staticfiles"), _jsonSerializer.SerializeToString(GetCacheFileList()));
- if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase))
- {
- _fileSystem.DeleteFile(Path.Combine(path, "scripts", "registrationservices.js"));
- }
+ var mode = request.Mode;
// Try to trim the output size a bit
var bowerPath = Path.Combine(path, "bower_components");
@@ -313,14 +351,9 @@ namespace MediaBrowser.WebDashboard.Api
//bowerPath = versionedBowerPath;
}
- DeleteFilesByExtension(bowerPath, ".log");
- DeleteFilesByExtension(bowerPath, ".txt");
- DeleteFilesByExtension(bowerPath, ".map");
- DeleteFilesByExtension(bowerPath, ".md");
+ GetUndeployedExtensions().ForEach(i => DeleteFilesByExtension(bowerPath, i));
+
DeleteFilesByExtension(bowerPath, ".json", "strings\\");
- DeleteFilesByExtension(bowerPath, ".gz");
- DeleteFilesByExtension(bowerPath, ".bat");
- DeleteFilesByExtension(bowerPath, ".sh");
DeleteFilesByName(bowerPath, "copying", true);
DeleteFilesByName(bowerPath, "license", true);
DeleteFilesByName(bowerPath, "license-mit", true);
@@ -359,13 +392,11 @@ namespace MediaBrowser.WebDashboard.Api
//DeleteFoldersByName(Path.Combine(bowerPath, "Sortable"), "meteor");
//DeleteFoldersByName(Path.Combine(bowerPath, "Sortable"), "st");
//DeleteFoldersByName(Path.Combine(bowerPath, "Swiper"), "src");
-
+
if (string.Equals(mode, "cordova", StringComparison.OrdinalIgnoreCase))
{
// Delete things that are unneeded in an attempt to keep the output as trim as possible
_fileSystem.DeleteDirectory(Path.Combine(path, "css", "images", "tour"), true);
-
- _fileSystem.DeleteFile(Path.Combine(path, "thirdparty", "jquerymobile-1.4.5", "jquery.mobile-1.4.5.min.map"));
}
else
{
diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
index b7b1f1dfd..65e7fa5d3 100644
--- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs
+++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs
@@ -11,8 +11,6 @@ using System.Threading.Tasks;
using CommonIO;
using MediaBrowser.Controller.Net;
using WebMarkupMin.Core;
-using WebMarkupMin.Core.Minifiers;
-using WebMarkupMin.Core.Settings;
namespace MediaBrowser.WebDashboard.Api
{
@@ -372,12 +370,12 @@ namespace MediaBrowser.WebDashboard.Api
sb.Append("<meta property=\"fb:app_id\" content=\"1618309211750238\">");
// http://developer.apple.com/library/ios/#DOCUMENTATION/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html
- sb.Append("<link rel=\"apple-touch-icon\" href=\"css/images/touchicon.png\">");
- sb.Append("<link rel=\"apple-touch-icon\" sizes=\"72x72\" href=\"css/images/touchicon72.png\">");
- sb.Append("<link rel=\"apple-touch-icon\" sizes=\"114x114\" href=\"css/images/touchicon114.png\">");
+ sb.Append("<link rel=\"apple-touch-icon\" href=\"touchicon.png\">");
+ sb.Append("<link rel=\"apple-touch-icon\" sizes=\"72x72\" href=\"touchicon72.png\">");
+ sb.Append("<link rel=\"apple-touch-icon\" sizes=\"114x114\" href=\"touchicon114.png\">");
sb.Append("<link rel=\"apple-touch-startup-image\" href=\"css/images/iossplash.png\">");
sb.Append("<link rel=\"shortcut icon\" href=\"css/images/favicon.ico\">");
- sb.Append("<meta name=\"msapplication-TileImage\" content=\"css/images/touchicon144.png\">");
+ sb.Append("<meta name=\"msapplication-TileImage\" content=\"touchicon144.png\">");
sb.Append("<meta name=\"msapplication-TileColor\" content=\"#333333\">");
sb.Append("<meta name=\"theme-color\" content=\"#43A047\">");
@@ -431,7 +429,7 @@ namespace MediaBrowser.WebDashboard.Api
var files = new List<string>();
- files.Add("bower_components/requirejs/require.js");
+ files.Add("bower_components/requirejs/require.js" + versionString);
files.Add("scripts/site.js" + versionString);
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
index cc064dc81..61facf8ec 100644
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
@@ -63,9 +63,9 @@
<Reference Include="ServiceStack.Interfaces">
<HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath>
</Reference>
- <Reference Include="WebMarkupMin.Core, Version=1.0.1.0, Culture=neutral, PublicKeyToken=99472178d266584b, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\WebMarkupMin.Core.1.0.1\lib\net40\WebMarkupMin.Core.dll</HintPath>
+ <Reference Include="WebMarkupMin.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=99472178d266584b, processorArchitecture=MSIL">
+ <HintPath>..\packages\WebMarkupMin.Core.2.1.0\lib\net40-client\WebMarkupMin.Core.dll</HintPath>
+ <Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
@@ -101,6 +101,12 @@
<Content Include="dashboard-ui\autoorganizesmart.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\components\appfooter\appfooter.css">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\components\appfooter\appfooter.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\components\apphost.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -116,6 +122,12 @@
<Content Include="dashboard-ui\components\directorybrowser\directorybrowser.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\components\dockedtabs\dockedtabs.css">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\components\dockedtabs\dockedtabs.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\components\favoriteitems.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -143,6 +155,12 @@
<Content Include="dashboard-ui\components\guestinviter\guestinviter.template.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\components\libraryoptionseditor\libraryoptionseditor.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="dashboard-ui\components\libraryoptionseditor\libraryoptionseditor.template.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\components\navdrawer\navdrawer.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -155,6 +173,9 @@
<Content Include="dashboard-ui\components\remotecontrolautoplay.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\components\syncjoblist\syncjoblist.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\components\tvproviders\xmltv.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -233,10 +254,7 @@
<Content Include="dashboard-ui\css\images\empty.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\css\images\logo536.png">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="dashboard-ui\css\images\touchicon144.png">
+ <Content Include="dashboard-ui\touchicon144.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\css\images\tour\admin\help.png">
@@ -1196,13 +1214,13 @@
</Content>
</ItemGroup>
<ItemGroup>
- <Content Include="dashboard-ui\css\images\touchicon.png">
+ <Content Include="dashboard-ui\touchicon.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\css\images\touchicon114.png">
+ <Content Include="dashboard-ui\touchicon114.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\css\images\touchicon72.png">
+ <Content Include="dashboard-ui\touchicon72.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
@@ -1554,6 +1572,9 @@
<None Include="dashboard-ui\manifest.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
+ <None Include="dashboard-ui\strings\be-BY.json">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
<None Include="dashboard-ui\strings\fr-CA.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config
index a2d13fdf5..3637c6c84 100644
--- a/MediaBrowser.WebDashboard/packages.config
+++ b/MediaBrowser.WebDashboard/packages.config
@@ -2,5 +2,5 @@
<packages>
<package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
- <package id="WebMarkupMin.Core" version="1.0.1" targetFramework="net45" />
+ <package id="WebMarkupMin.Core" version="2.1.0" targetFramework="net45" />
</packages> \ No newline at end of file
diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
index 2e34135a6..0c8501e62 100644
--- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
+++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs
@@ -231,7 +231,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
DateTime added;
if (DateTime.TryParseExact(val, BaseNfoSaver.DateAddedFormat, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out added))
{
- item.EndDate = added.ToUniversalTime();
+ item.DateCreated = added.ToUniversalTime();
}
else if (DateTime.TryParse(val, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out added))
{
diff --git a/SharedVersion.cs b/SharedVersion.cs
index a81b44de7..5e789de3d 100644
--- a/SharedVersion.cs
+++ b/SharedVersion.cs
@@ -1,4 +1,4 @@
using System.Reflection;
//[assembly: AssemblyVersion("3.1.*")]
-[assembly: AssemblyVersion("3.0.6060")]
+[assembly: AssemblyVersion("3.1.114")]