diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index d386373d4b..95f7ef6944 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -185,7 +185,9 @@ namespace MediaBrowser.Api
CompletionPercentage = percentComplete,
Width = state.OutputWidth,
Height = state.OutputHeight,
- AudioChannels = state.OutputAudioChannels
+ AudioChannels = state.OutputAudioChannels,
+ IsAudioDirect = string.Equals(state.OutputAudioCodec, "copy", StringComparison.OrdinalIgnoreCase),
+ IsVideoDirect = string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)
});
}
}
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 12ccfb6b1d..1a8c1d849e 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -302,6 +302,21 @@ namespace MediaBrowser.Api.Playback
}
}
+ protected string H264Encoder
+ {
+ get
+ {
+ var lib = ServerConfigurationManager.Configuration.H264Encoder;
+
+ if (!string.IsNullOrWhiteSpace(lib))
+ {
+ return lib;
+ }
+
+ return "libx264";
+ }
+ }
+
///
/// Gets the video bitrate to specify on the command line
///
@@ -318,7 +333,7 @@ namespace MediaBrowser.Api.Playback
var qualitySetting = GetQualitySetting();
- if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(videoCodec, H264Encoder, StringComparison.OrdinalIgnoreCase))
{
switch (qualitySetting)
{
@@ -761,7 +776,7 @@ namespace MediaBrowser.Api.Playback
{
if (string.Equals(codec, "h264", StringComparison.OrdinalIgnoreCase))
{
- return "libx264";
+ return H264Encoder;
}
if (string.Equals(codec, "vpx", StringComparison.OrdinalIgnoreCase))
{
@@ -1562,9 +1577,6 @@ namespace MediaBrowser.Api.Playback
mediaStreams = new List();
state.DeInterlace = true;
- state.OutputAudioSync = "1000";
- state.InputVideoSync = "-1";
- state.InputAudioSync = "1";
// Just to prevent this from being null and causing other methods to fail
state.MediaPath = string.Empty;
@@ -1696,6 +1708,13 @@ namespace MediaBrowser.Api.Playback
state.InputFileSize = mediaSource.Size;
state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate;
+ if (state.ReadInputAtNativeFramerate)
+ {
+ state.OutputAudioSync = "1000";
+ state.InputVideoSync = "-1";
+ state.InputAudioSync = "1";
+ }
+
AttachMediaStreamInfo(state, mediaSource.MediaStreams, videoRequest, requestedUrl);
}
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index c963636fdf..c2a9b963c5 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Common.IO;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
@@ -6,7 +7,6 @@ using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.IO;
using System;
using System.Collections.Generic;
@@ -119,11 +119,7 @@ namespace MediaBrowser.Api.Playback.Hls
if (isLive)
{
- //var file = request.PlaylistId + Path.GetExtension(Request.PathInfo);
-
- //file = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, file);
-
- return ResultFactory.GetStaticFileResult(Request, playlist, FileShare.ReadWrite);
+ return ResultFactory.GetResult(GetLivePlaylistText(playlist, state.SegmentLength), MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary());
}
var audioBitrate = state.OutputAudioBitrate ?? 0;
@@ -144,6 +140,22 @@ namespace MediaBrowser.Api.Playback.Hls
return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary());
}
+ private string GetLivePlaylistText(string path, int segmentLength)
+ {
+ using (var stream = FileSystem.GetFileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
+ {
+ using (var reader = new StreamReader(stream))
+ {
+ var text = reader.ReadToEnd();
+
+ var newDuration = "#EXT-X-TARGETDURATION:" + segmentLength.ToString(UsCulture) + Environment.NewLine + "#EXT-X-ALLOW-CACHE:NO";
+
+ // ffmpeg pads the reported length by a full second
+ return text.Replace("#EXT-X-TARGETDURATION:" + (segmentLength + 1).ToString(UsCulture), newDuration, StringComparison.OrdinalIgnoreCase);
+ }
+ }
+ }
+
private string GetMasterPlaylistFileText(string firstPlaylist, int bitrate, bool includeBaselineStream, int baselineStreamBitrate)
{
var builder = new StringBuilder();
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index fe3bd12fb1..7903724e8d 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -651,7 +651,7 @@ namespace MediaBrowser.Api.Playback.Hls
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream;
- var args = "-codec:v:0 " + codec + " " + GetVideoQualityParam(state, "libx264", true) + keyFrameArg;
+ var args = "-codec:v:0 " + codec + " " + GetVideoQualityParam(state, H264Encoder, true) + keyFrameArg;
// Add resolution params, if specified
if (!hasGraphicalSubs)
diff --git a/MediaBrowser.Api/Playback/Hls/MpegDashService.cs b/MediaBrowser.Api/Playback/Hls/MpegDashService.cs
index 260a4c467e..ca46df05d0 100644
--- a/MediaBrowser.Api/Playback/Hls/MpegDashService.cs
+++ b/MediaBrowser.Api/Playback/Hls/MpegDashService.cs
@@ -594,7 +594,7 @@ namespace MediaBrowser.Api.Playback.Hls
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream;
- var args = "-codec:v:0 " + codec + " " + GetVideoQualityParam(state, "libx264", true) + keyFrameArg;
+ var args = "-codec:v:0 " + codec + " " + GetVideoQualityParam(state, H264Encoder, true) + keyFrameArg;
args += " -r 24 -g 24";
diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
index 06fa4065c2..14f7175a9c 100644
--- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
@@ -9,8 +9,6 @@ using MediaBrowser.Model.IO;
using ServiceStack;
using System;
using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
namespace MediaBrowser.Api.Playback.Hls
{
@@ -147,7 +145,7 @@ namespace MediaBrowser.Api.Playback.Hls
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream;
- var args = "-codec:v:0 " + codec + " " + GetVideoQualityParam(state, "libx264", true) + keyFrameArg;
+ var args = "-codec:v:0 " + codec + " " + GetVideoQualityParam(state, H264Encoder, true) + keyFrameArg;
// Add resolution params, if specified
if (!hasGraphicalSubs)
diff --git a/MediaBrowser.Api/Sync/SyncService.cs b/MediaBrowser.Api/Sync/SyncService.cs
index cefb0e46e8..26e4a2669a 100644
--- a/MediaBrowser.Api/Sync/SyncService.cs
+++ b/MediaBrowser.Api/Sync/SyncService.cs
@@ -55,8 +55,14 @@ namespace MediaBrowser.Api.Sync
[ApiMember(Name = "UserId", Description = "UserId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public string UserId { get; set; }
- [ApiMember(Name = "ItemIds", Description = "ItemIds", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
+ [ApiMember(Name = "ItemIds", Description = "ItemIds", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string ItemIds { get; set; }
+
+ [ApiMember(Name = "ParentId", Description = "ParentId", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string ParentId { get; set; }
+
+ [ApiMember(Name = "Category", Description = "Category", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public SyncCategory? Category { get; set; }
}
[Route("/Sync/JobItems/{Id}/Transferred", "POST", Summary = "Reports that a sync job item has successfully been transferred.")]
@@ -155,19 +161,26 @@ namespace MediaBrowser.Api.Sync
result.Targets = _syncManager.GetSyncTargets(request.UserId)
.ToList();
- var dtos = request.ItemIds.Split(',')
- .Select(_libraryManager.GetItemById)
- .Where(i => i != null)
- .Select(i => _dtoService.GetBaseItemDto(i, new DtoOptions
- {
- Fields = new List
+ if (request.Category.HasValue)
+ {
+ result.Options = SyncHelper.GetSyncOptions(request.Category.Value);
+ }
+ else
+ {
+ var dtos = request.ItemIds.Split(',')
+ .Select(_libraryManager.GetItemById)
+ .Where(i => i != null)
+ .Select(i => _dtoService.GetBaseItemDto(i, new DtoOptions
+ {
+ Fields = new List
{
ItemFields.SyncInfo
}
- }))
- .ToList();
+ }))
+ .ToList();
- result.Options = SyncHelper.GetSyncOptions(dtos);
+ result.Options = SyncHelper.GetSyncOptions(dtos);
+ }
return ToOptimizedResult(result);
}
diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs
index 4b720c775c..9c8216a03c 100644
--- a/MediaBrowser.Api/UserService.cs
+++ b/MediaBrowser.Api/UserService.cs
@@ -5,6 +5,7 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
+using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Connect;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Users;
@@ -51,7 +52,7 @@ namespace MediaBrowser.Api
///
/// The id.
[ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
- public Guid Id { get; set; }
+ public string Id { get; set; }
}
///
@@ -66,7 +67,7 @@ namespace MediaBrowser.Api
///
/// The id.
[ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
- public Guid Id { get; set; }
+ public string Id { get; set; }
}
///
@@ -80,7 +81,7 @@ namespace MediaBrowser.Api
///
/// The id.
[ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
- public Guid Id { get; set; }
+ public string Id { get; set; }
///
/// Gets or sets the password.
@@ -125,7 +126,7 @@ namespace MediaBrowser.Api
/// Gets or sets the id.
///
/// The id.
- public Guid Id { get; set; }
+ public string Id { get; set; }
///
/// Gets or sets the password.
@@ -155,6 +156,28 @@ namespace MediaBrowser.Api
{
}
+ ///
+ /// Class UpdateUser
+ ///
+ [Route("/Users/{Id}/Policy", "POST", Summary = "Updates a user policy")]
+ [Authenticated(Roles = "admin")]
+ public class UpdateUserPolicy : UserPolicy, IReturnVoid
+ {
+ [ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string Id { get; set; }
+ }
+
+ ///
+ /// Class UpdateUser
+ ///
+ [Route("/Users/{Id}/Configuration", "POST", Summary = "Updates a user configuration")]
+ [Authenticated]
+ public class UpdateUserConfiguration : UserConfiguration, IReturnVoid
+ {
+ [ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+ public string Id { get; set; }
+ }
+
///
/// Class CreateUser
///
@@ -196,12 +219,6 @@ namespace MediaBrowser.Api
public IAuthorizationContext AuthorizationContext { get; set; }
- ///
- /// Initializes a new instance of the class.
- ///
- /// The user manager.
- /// The dto service.
- /// The session mananger.
public UserService(IUserManager userManager, IDtoService dtoService, ISessionManager sessionMananger, IServerConfigurationManager config, INetworkManager networkManager)
{
_userManager = userManager;
@@ -495,5 +512,17 @@ namespace MediaBrowser.Api
{
return _userManager.RedeemPasswordResetPin(request.Pin);
}
+
+ public void Post(UpdateUserConfiguration request)
+ {
+ var user = _userManager.GetUserById(request.Id);
+ user.UpdateConfiguration(request);
+ }
+
+ public void Post(UpdateUserPolicy request)
+ {
+ var task = _userManager.UpdateUserPolicy(request.Id, request);
+ Task.WaitAll(task);
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index ed950b1c5e..1d57c46e69 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -45,6 +45,8 @@ namespace MediaBrowser.Controller.Entities
///
public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg", ".tbn" };
+ public static readonly List SupportedImageExtensionsList = SupportedImageExtensions.ToList();
+
///
/// The trailer folder name
///
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs
index e749d89e4f..30bf0fefc5 100644
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs
@@ -153,7 +153,14 @@ namespace MediaBrowser.Controller.Entities.Movies
public MovieInfo GetLookupInfo()
{
- return GetItemLookupInfo();
+ var info = GetItemLookupInfo();
+
+ if (!IsInMixedFolder)
+ {
+ info.Name = System.IO.Path.GetFileName(ContainingFolderPath);
+ }
+
+ return info;
}
public override bool BeforeMetadataRefresh()
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index cc0fc6812c..e270bbdf30 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -1,5 +1,4 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using System;
@@ -301,51 +300,9 @@ namespace MediaBrowser.Controller.Entities.TV
{
var hasChanges = base.BeforeMetadataRefresh();
- var locationType = LocationType;
- if (locationType == LocationType.FileSystem || locationType == LocationType.Offline)
+ if (LibraryManager.FillMissingEpisodeNumbersFromPath(this))
{
- if (!IndexNumber.HasValue && !string.IsNullOrEmpty(Path))
- {
- IndexNumber = LibraryManager.GetEpisodeNumberFromFile(Path, true);
-
- // If a change was made record it
- if (IndexNumber.HasValue)
- {
- hasChanges = true;
- }
- }
-
- if (!IndexNumberEnd.HasValue && !string.IsNullOrEmpty(Path))
- {
- IndexNumberEnd = LibraryManager.GetEndingEpisodeNumberFromFile(Path);
-
- // If a change was made record it
- if (IndexNumberEnd.HasValue)
- {
- hasChanges = true;
- }
- }
- }
-
- if (!ParentIndexNumber.HasValue)
- {
- var season = Season;
-
- if (season != null)
- {
- ParentIndexNumber = season.IndexNumber;
- }
-
- if (!ParentIndexNumber.HasValue && !string.IsNullOrEmpty(Path))
- {
- ParentIndexNumber = LibraryManager.GetSeasonNumberFromEpisodeFile(Path);
- }
-
- // If a change was made record it
- if (ParentIndexNumber.HasValue)
- {
- hasChanges = true;
- }
+ hasChanges = true;
}
return hasChanges;
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 33dea4dca6..6d8f89226a 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Controller.Sorting;
@@ -340,26 +341,11 @@ namespace MediaBrowser.Controller.Library
int? GetSeasonNumberFromPath(string path);
///
- /// Gets the season number from episode file.
+ /// Fills the missing episode numbers from path.
///
- /// The path.
- /// System.Nullable<System.Int32>.
- int? GetSeasonNumberFromEpisodeFile(string path);
-
- ///
- /// Gets the ending episode number from file.
- ///
- /// The path.
- /// System.Nullable<System.Int32>.
- int? GetEndingEpisodeNumberFromFile(string path);
-
- ///
- /// Gets the episode number from file.
- ///
- /// The path.
- /// if set to true [consider seasonless].
- /// System.Nullable<System.Int32>.
- int? GetEpisodeNumberFromFile(string path, bool considerSeasonless);
+ /// The episode.
+ /// true if XXXX, false otherwise.
+ bool FillMissingEpisodeNumbersFromPath(Episode episode);
///
/// Parses the name.
diff --git a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
index 044d29a1b8..9b1cce915b 100644
--- a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
@@ -76,11 +76,14 @@ namespace MediaBrowser.LocalMetadata.Images
{
return directoryService.GetFileSystemEntries(path)
.Where(i => BaseItem.SupportedImageExtensions.Contains(i.Extension, StringComparer.OrdinalIgnoreCase) ||
- (i.Attributes & FileAttributes.Directory) == FileAttributes.Directory);
+ (i.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
+
+ .OrderBy(i => BaseItem.SupportedImageExtensionsList.IndexOf(i.Extension ?? string.Empty));
}
return directoryService.GetFiles(path)
- .Where(i => BaseItem.SupportedImageExtensions.Contains(i.Extension, StringComparer.OrdinalIgnoreCase));
+ .Where(i => BaseItem.SupportedImageExtensions.Contains(i.Extension, StringComparer.OrdinalIgnoreCase))
+ .OrderBy(i => BaseItem.SupportedImageExtensionsList.IndexOf(i.Extension ?? string.Empty));
}
public List GetImages(IHasImages item, IDirectoryService directoryService)
@@ -109,6 +112,7 @@ namespace MediaBrowser.LocalMetadata.Images
return !string.IsNullOrEmpty(ext) &&
BaseItem.SupportedImageExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
})
+ .OrderBy(i => BaseItem.SupportedImageExtensionsList.IndexOf(i.Extension ?? string.Empty))
.ToList();
var list = new List();
@@ -402,13 +406,7 @@ namespace MediaBrowser.LocalMetadata.Images
private FileSystemInfo GetImage(IEnumerable files, string name)
{
- var candidates = files
- .Where(i => string.Equals(name, _fileSystem.GetFileNameWithoutExtension(i), StringComparison.OrdinalIgnoreCase))
- .ToList();
-
- return BaseItem.SupportedImageExtensions
- .Select(i => candidates.FirstOrDefault(c => string.Equals(c.Extension, i, StringComparison.OrdinalIgnoreCase)))
- .FirstOrDefault(i => i != null);
+ return files.FirstOrDefault(i => ((i.Attributes & FileAttributes.Directory) != FileAttributes.Directory) && string.Equals(name, _fileSystem.GetFileNameWithoutExtension(i), StringComparison.OrdinalIgnoreCase));
}
}
}
diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs
index 9521f85381..dde6ca0b12 100644
--- a/MediaBrowser.Model/ApiClient/IApiClient.cs
+++ b/MediaBrowser.Model/ApiClient/IApiClient.cs
@@ -185,6 +185,14 @@ namespace MediaBrowser.Model.ApiClient
/// url
Task GetImageStreamAsync(string url, CancellationToken cancellationToken = default(CancellationToken));
+ ///
+ /// Updates the user configuration.
+ ///
+ /// The user identifier.
+ /// The configuration.
+ /// Task.
+ Task UpdateUserConfiguration(string userId, UserConfiguration configuration);
+
///
/// Gets a BaseItem
///
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index b9eaf70010..59dd04f338 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -144,12 +144,6 @@ namespace MediaBrowser.Model.Configuration
/// The image saving convention.
public ImageSavingConvention ImageSavingConvention { get; set; }
- ///
- /// Gets or sets a value indicating whether [enable people prefix sub folders].
- ///
- /// true if [enable people prefix sub folders]; otherwise, false.
- public bool EnablePeoplePrefixSubFolders { get; set; }
-
///
/// Gets or sets the encoding quality.
///
@@ -179,8 +173,7 @@ namespace MediaBrowser.Model.Configuration
public string[] InsecureApps7 { get; set; }
public bool SaveMetadataHidden { get; set; }
-
- public bool PlaylistImagesDeleted { get; set; }
+ public string H264Encoder { get; set; }
///
/// Initializes a new instance of the class.
@@ -195,7 +188,6 @@ namespace MediaBrowser.Model.Configuration
EnableDashboardResponseCaching = true;
EnableAutomaticRestart = true;
- EnablePeoplePrefixSubFolders = true;
EnableUPnP = true;
DownMixAudioBoost = 2;
@@ -225,6 +217,7 @@ namespace MediaBrowser.Model.Configuration
EnableRealtimeMonitor = true;
UICulture = "en-us";
+ H264Encoder = "libx264";
PeopleMetadataOptions = new PeopleMetadataOptions();
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index 71cefa0766..87fdce7996 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -549,7 +549,13 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets a value indicating whether [supports playlists].
///
/// true if [supports playlists]; otherwise, false.
- public bool SupportsPlaylists { get; set; }
+ public bool SupportsPlaylists
+ {
+ get
+ {
+ return RunTimeTicks.HasValue || IsFolder || IsGenre || IsMusicGenre || IsArtist;
+ }
+ }
///
/// Determines whether the specified type is type.
diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs
index 19e30cd8a7..875894c077 100644
--- a/MediaBrowser.Model/Querying/ItemFields.cs
+++ b/MediaBrowser.Model/Querying/ItemFields.cs
@@ -6,6 +6,11 @@ namespace MediaBrowser.Model.Querying
///
public enum ItemFields
{
+ ///
+ /// The air time
+ ///
+ AirTime,
+
///
/// The alternate episode numbers
///
@@ -151,6 +156,11 @@ namespace MediaBrowser.Model.Querying
///
Revenue,
+ ///
+ /// The season name
+ ///
+ SeasonName,
+
///
/// The short overview
///
@@ -181,6 +191,11 @@ namespace MediaBrowser.Model.Querying
///
SortName,
+ ///
+ /// The special episode numbers
+ ///
+ SpecialEpisodeNumbers,
+
///
/// The studios of the item
///
diff --git a/MediaBrowser.Model/Session/TranscodingInfo.cs b/MediaBrowser.Model/Session/TranscodingInfo.cs
index b3ab32a448..e646d80d35 100644
--- a/MediaBrowser.Model/Session/TranscodingInfo.cs
+++ b/MediaBrowser.Model/Session/TranscodingInfo.cs
@@ -5,6 +5,8 @@ namespace MediaBrowser.Model.Session
public string AudioCodec { get; set; }
public string VideoCodec { get; set; }
public string Container { get; set; }
+ public bool IsVideoDirect { get; set; }
+ public bool IsAudioDirect { get; set; }
public int? Bitrate { get; set; }
public float? Framerate { get; set; }
diff --git a/MediaBrowser.Model/Sync/SyncItem.cs b/MediaBrowser.Model/Sync/SyncItem.cs
new file mode 100644
index 0000000000..d50ae98c99
--- /dev/null
+++ b/MediaBrowser.Model/Sync/SyncItem.cs
@@ -0,0 +1,9 @@
+using MediaBrowser.Model.Dto;
+
+namespace MediaBrowser.Model.Sync
+{
+ public class SyncItem
+ {
+ public BaseItemDto Item { get; set; }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs b/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs
index 72c524ec56..d266cca6c0 100644
--- a/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs
+++ b/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs
@@ -72,26 +72,29 @@ namespace MediaBrowser.Server.Implementations.Channels
var features = _channelManager.GetChannelFeatures(channelId);
const int currentRefreshLevel = 1;
- var maxRefreshLevel = features.AutoRefreshLevels ?? 1;
+ var maxRefreshLevel = features.AutoRefreshLevels ?? 0;
- var innerProgress = new ActionableProgress();
+ if (maxRefreshLevel > 0)
+ {
+ var innerProgress = new ActionableProgress();
- var startingNumberComplete = numComplete;
- innerProgress.RegisterAction(p =>
- {
- double innerPercent = startingNumberComplete;
- innerPercent += (p / 100);
- innerPercent /= numItems;
- progress.Report(innerPercent * 100);
- });
+ var startingNumberComplete = numComplete;
+ innerProgress.RegisterAction(p =>
+ {
+ double innerPercent = startingNumberComplete;
+ innerPercent += (p / 100);
+ innerPercent /= numItems;
+ progress.Report(innerPercent * 100);
+ });
- try
- {
- await GetAllItems(user, channelId, null, currentRefreshLevel, maxRefreshLevel, innerProgress, cancellationToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting channel content", ex);
+ try
+ {
+ await GetAllItems(user, channelId, null, currentRefreshLevel, maxRefreshLevel, innerProgress, cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error getting channel content", ex);
+ }
}
numComplete++;
diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
index 1020a43735..3579f443fa 100644
--- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs
+++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
@@ -121,8 +121,6 @@ namespace MediaBrowser.Server.Implementations.Dto
ServerId = _appHost.SystemId
};
- dto.SupportsPlaylists = item.SupportsAddingToPlaylist;
-
if (fields.Contains(ItemFields.People))
{
AttachPeople(dto, item);
@@ -1132,15 +1130,22 @@ namespace MediaBrowser.Server.Implementations.Dto
dto.AbsoluteEpisodeNumber = episode.AbsoluteEpisodeNumber;
}
- dto.AirsAfterSeasonNumber = episode.AirsAfterSeasonNumber;
- dto.AirsBeforeEpisodeNumber = episode.AirsBeforeEpisodeNumber;
- dto.AirsBeforeSeasonNumber = episode.AirsBeforeSeasonNumber;
+ //if (fields.Contains(ItemFields.SpecialEpisodeNumbers))
+ {
+ dto.AirsAfterSeasonNumber = episode.AirsAfterSeasonNumber;
+ dto.AirsBeforeEpisodeNumber = episode.AirsBeforeEpisodeNumber;
+ dto.AirsBeforeSeasonNumber = episode.AirsBeforeSeasonNumber;
+ }
var episodeSeason = episode.Season;
if (episodeSeason != null)
{
dto.SeasonId = episodeSeason.Id.ToString("N");
- dto.SeasonName = episodeSeason.Name;
+
+ if (fields.Contains(ItemFields.SeasonName))
+ {
+ dto.SeasonName = episodeSeason.Name;
+ }
}
if (fields.Contains(ItemFields.SeriesGenres))
@@ -1180,7 +1185,11 @@ namespace MediaBrowser.Server.Implementations.Dto
{
dto.SeriesId = GetDtoId(series);
dto.SeriesName = series.Name;
- dto.AirTime = series.AirTime;
+
+ if (fields.Contains(ItemFields.AirTime))
+ {
+ dto.AirTime = series.AirTime;
+ }
if (options.GetImageLimit(ImageType.Thumb) > 0)
{
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
index d4625d4028..3b5e34520f 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
+++ b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
@@ -4,11 +4,11 @@ using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.FileOrganization;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
-using MediaBrowser.Controller.Resolvers;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.FileOrganization;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Naming.Common;
+using MediaBrowser.Naming.IO;
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -16,8 +16,6 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Server.Implementations.Library;
-using MediaBrowser.Server.Implementations.Library.Resolvers.TV;
namespace MediaBrowser.Server.Implementations.FileOrganization
{
@@ -57,18 +55,23 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
FileSize = new FileInfo(path).Length
};
- var seriesName = SeriesResolver.GetSeriesNameFromEpisodeFile(path);
+ var resolver = new Naming.TV.EpisodeResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger());
+
+ var episodeInfo = resolver.Resolve(path, FileInfoType.File) ??
+ new Naming.TV.EpisodeInfo();
+
+ var seriesName = episodeInfo.SeriesName;
if (!string.IsNullOrEmpty(seriesName))
{
- var season = SeriesResolver.GetSeasonNumberFromEpisodeFile(path);
+ var season = episodeInfo.SeasonNumber;
result.ExtractedSeasonNumber = season;
if (season.HasValue)
{
// Passing in true will include a few extra regex's
- var episode = SeriesResolver.GetEpisodeNumberFromFile(path, true);
+ var episode = episodeInfo.EpisodeNumber;
result.ExtractedEpisodeNumber = episode;
@@ -76,7 +79,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{
_logger.Debug("Extracted information from {0}. Series name {1}, Season {2}, Episode {3}", path, seriesName, season, episode);
- var endingEpisodeNumber = SeriesResolver.GetEndingEpisodeNumberFromFile(path);
+ var endingEpisodeNumber = episodeInfo.EndingEpsiodeNumber;
result.ExtractedEndingEpisodeNumber = endingEpisodeNumber;
@@ -251,7 +254,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
var folder = Path.GetDirectoryName(targetPath);
var targetFileNameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(targetPath);
-
+
try
{
var filesOfOtherExtensions = Directory.EnumerateFiles(folder, "*", SearchOption.TopDirectoryOnly)
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index 4cb39778c5..d61a00ac39 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
@@ -18,8 +18,8 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Naming.Audio;
using MediaBrowser.Naming.Common;
using MediaBrowser.Naming.IO;
+using MediaBrowser.Naming.TV;
using MediaBrowser.Naming.Video;
-using MediaBrowser.Server.Implementations.Library.Resolvers.TV;
using MediaBrowser.Server.Implementations.Library.Validators;
using MediaBrowser.Server.Implementations.ScheduledTasks;
using System;
@@ -862,7 +862,7 @@ namespace MediaBrowser.Server.Implementations.Library
var type = typeof(T);
- if (type == typeof(Person) && ConfigurationManager.Configuration.EnablePeoplePrefixSubFolders)
+ if (type == typeof(Person))
{
subFolderPrefix = validFilename.Substring(0, 1);
}
@@ -1708,22 +1708,69 @@ namespace MediaBrowser.Server.Implementations.Library
public int? GetSeasonNumberFromPath(string path)
{
- return SeriesResolver.GetSeasonNumberFromPath(path, CollectionType.TvShows);
+ return new SeasonPathParser(new ExtendedNamingOptions(), new RegexProvider()).Parse(path, true).SeasonNumber;
}
- public int? GetSeasonNumberFromEpisodeFile(string path)
+ public bool FillMissingEpisodeNumbersFromPath(Episode episode)
{
- return SeriesResolver.GetSeasonNumberFromEpisodeFile(path);
- }
+ var resolver = new EpisodeResolver(new ExtendedNamingOptions(),
+ new Naming.Logging.NullLogger());
- public int? GetEndingEpisodeNumberFromFile(string path)
- {
- return SeriesResolver.GetEndingEpisodeNumberFromFile(path);
- }
+ var locationType = episode.LocationType;
+
+ var fileType = /*args.IsDirectory ? FileInfoType.Directory :*/ FileInfoType.File;
+ var episodeInfo = locationType == LocationType.FileSystem || locationType == LocationType.Offline ?
+ resolver.Resolve(episode.Path, fileType) :
+ new Naming.TV.EpisodeInfo();
- public int? GetEpisodeNumberFromFile(string path, bool considerSeasonless)
- {
- return SeriesResolver.GetEpisodeNumberFromFile(path, considerSeasonless);
+ if (episodeInfo == null)
+ {
+ episodeInfo = new Naming.TV.EpisodeInfo();
+ }
+
+ var changed = false;
+
+ if (!episode.IndexNumber.HasValue)
+ {
+ episode.IndexNumber = episodeInfo.EpisodeNumber;
+
+ if (episode.IndexNumber.HasValue)
+ {
+ changed = true;
+ }
+ }
+
+ if (!episode.IndexNumberEnd.HasValue)
+ {
+ episode.IndexNumberEnd = episodeInfo.EndingEpsiodeNumber;
+
+ if (episode.IndexNumberEnd.HasValue)
+ {
+ changed = true;
+ }
+ }
+
+ if (!episode.ParentIndexNumber.HasValue)
+ {
+ episode.ParentIndexNumber = episodeInfo.SeasonNumber;
+
+ if (!episode.ParentIndexNumber.HasValue)
+ {
+ var season = episode.Season;
+
+ if (season != null)
+ {
+ episode.ParentIndexNumber = season.IndexNumber;
+ }
+ }
+
+ if (episode.ParentIndexNumber.HasValue)
+ {
+ changed = true;
+ }
+ }
+
+ return changed;
}
public ItemLookupInfo ParseName(string name)
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index 276b99d3a2..0e5ed18251 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -82,8 +82,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
return ResolveVideos(parent, files, directoryService, collectionType);
}
- if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(collectionType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
{
return ResolveVideos(parent, files, directoryService, collectionType);
}
@@ -117,7 +116,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
FullName = i.FullName,
Type = FileInfoType.File
- }).ToList()).ToList();
+ }).ToList(), false).ToList();
var result = new MultiItemResolverResult
{
@@ -168,12 +167,12 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
{
if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
{
- return FindMovie(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType, false);
+ return FindMovie(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType);
}
if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase))
{
- return FindMovie