瀏覽代碼

add more sync buttons

Luke Pulverenti 10 年之前
父節點
當前提交
e55ab989d2
共有 46 個文件被更改,包括 380 次插入872 次删除
  1. 3 1
      MediaBrowser.Api/ApiEntryPoint.cs
  2. 24 5
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  3. 19 7
      MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
  4. 1 1
      MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
  5. 1 1
      MediaBrowser.Api/Playback/Hls/MpegDashService.cs
  6. 1 3
      MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
  7. 23 10
      MediaBrowser.Api/Sync/SyncService.cs
  8. 39 10
      MediaBrowser.Api/UserService.cs
  9. 2 0
      MediaBrowser.Controller/Entities/BaseItem.cs
  10. 8 1
      MediaBrowser.Controller/Entities/Movies/Movie.cs
  11. 3 46
      MediaBrowser.Controller/Entities/TV/Episode.cs
  12. 5 19
      MediaBrowser.Controller/Library/ILibraryManager.cs
  13. 7 9
      MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
  14. 8 0
      MediaBrowser.Model/ApiClient/IApiClient.cs
  15. 2 9
      MediaBrowser.Model/Configuration/ServerConfiguration.cs
  16. 7 1
      MediaBrowser.Model/Dto/BaseItemDto.cs
  17. 15 0
      MediaBrowser.Model/Querying/ItemFields.cs
  18. 2 0
      MediaBrowser.Model/Session/TranscodingInfo.cs
  19. 9 0
      MediaBrowser.Model/Sync/SyncItem.cs
  20. 20 17
      MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs
  21. 16 7
      MediaBrowser.Server.Implementations/Dto/DtoService.cs
  22. 12 9
      MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
  23. 60 13
      MediaBrowser.Server.Implementations/Library/LibraryManager.cs
  24. 11 13
      MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
  25. 3 14
      MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
  26. 3 2
      MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
  27. 21 352
      MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
  28. 14 5
      MediaBrowser.Server.Implementations/Library/UserManager.cs
  29. 1 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
  30. 2 1
      MediaBrowser.Server.Implementations/Localization/Server/server.json
  31. 1 1
      MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
  32. 1 1
      MediaBrowser.Server.Implementations/Sync/SyncScheduledTask.cs
  33. 1 1
      MediaBrowser.Server.Implementations/packages.config
  34. 0 1
      MediaBrowser.Server.Startup.Common/ApplicationHost.cs
  35. 0 1
      MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj
  36. 0 55
      MediaBrowser.Server.Startup.Common/Migrations/PlaylistImages.cs
  37. 0 1
      MediaBrowser.Tests/MediaBrowser.Tests.csproj
  38. 0 246
      MediaBrowser.Tests/Resolvers/TvUtilTests.cs
  39. 1 0
      MediaBrowser.WebDashboard/Api/PackageCreator.cs
  40. 21 0
      MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
  41. 5 0
      MediaBrowser.sln
  42. 2 2
      Nuget/MediaBrowser.Common.Internal.nuspec
  43. 1 1
      Nuget/MediaBrowser.Common.nuspec
  44. 1 1
      Nuget/MediaBrowser.Model.Signed.nuspec
  45. 2 2
      Nuget/MediaBrowser.Server.Core.nuspec
  46. 2 2
      SharedVersion.cs

+ 3 - 1
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)
                 });
             }
         }

+ 24 - 5
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";
+            }
+        }
+
         /// <summary>
         /// Gets the video bitrate to specify on the command line
         /// </summary>
@@ -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<MediaStream>();
 
                 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);
         }
         

+ 19 - 7
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<string, string>());
             }
 
             var audioBitrate = state.OutputAudioBitrate ?? 0;
@@ -144,6 +140,22 @@ namespace MediaBrowser.Api.Playback.Hls
             return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
         }
 
+        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();

+ 1 - 1
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)

+ 1 - 1
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";
 

+ 1 - 3
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)

+ 23 - 10
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<ItemFields>
+            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>
                     {
                         ItemFields.SyncInfo
                     }
-                }))
-                .ToList();
+                    }))
+                    .ToList();
 
-            result.Options = SyncHelper.GetSyncOptions(dtos);
+                result.Options = SyncHelper.GetSyncOptions(dtos);
+            }
 
             return ToOptimizedResult(result);
         }

+ 39 - 10
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
         /// </summary>
         /// <value>The id.</value>
         [ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public Guid Id { get; set; }
+        public string Id { get; set; }
     }
 
     /// <summary>
@@ -66,7 +67,7 @@ namespace MediaBrowser.Api
         /// </summary>
         /// <value>The id.</value>
         [ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
-        public Guid Id { get; set; }
+        public string Id { get; set; }
     }
 
     /// <summary>
@@ -80,7 +81,7 @@ namespace MediaBrowser.Api
         /// </summary>
         /// <value>The id.</value>
         [ApiMember(Name = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
-        public Guid Id { get; set; }
+        public string Id { get; set; }
 
         /// <summary>
         /// Gets or sets the password.
@@ -125,7 +126,7 @@ namespace MediaBrowser.Api
         /// Gets or sets the id.
         /// </summary>
         /// <value>The id.</value>
-        public Guid Id { get; set; }
+        public string Id { get; set; }
 
         /// <summary>
         /// Gets or sets the password.
@@ -155,6 +156,28 @@ namespace MediaBrowser.Api
     {
     }
 
+    /// <summary>
+    /// Class UpdateUser
+    /// </summary>
+    [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; }
+    }
+
+    /// <summary>
+    /// Class UpdateUser
+    /// </summary>
+    [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; }
+    }
+
     /// <summary>
     /// Class CreateUser
     /// </summary>
@@ -196,12 +219,6 @@ namespace MediaBrowser.Api
 
         public IAuthorizationContext AuthorizationContext { get; set; }
 
-        /// <summary>
-        /// Initializes a new instance of the <see cref="UserService" /> class.
-        /// </summary>
-        /// <param name="userManager">The user manager.</param>
-        /// <param name="dtoService">The dto service.</param>
-        /// <param name="sessionMananger">The session mananger.</param>
         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);
+        }
     }
 }

+ 2 - 0
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -45,6 +45,8 @@ namespace MediaBrowser.Controller.Entities
         /// </summary>
         public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg", ".tbn" };
 
+        public static readonly List<string> SupportedImageExtensionsList = SupportedImageExtensions.ToList();
+
         /// <summary>
         /// The trailer folder name
         /// </summary>

+ 8 - 1
MediaBrowser.Controller/Entities/Movies/Movie.cs

@@ -153,7 +153,14 @@ namespace MediaBrowser.Controller.Entities.Movies
 
         public MovieInfo GetLookupInfo()
         {
-            return GetItemLookupInfo<MovieInfo>();
+            var info = GetItemLookupInfo<MovieInfo>();
+
+            if (!IsInMixedFolder)
+            {
+                info.Name = System.IO.Path.GetFileName(ContainingFolderPath);
+            }
+
+            return info;
         }
 
         public override bool BeforeMetadataRefresh()

+ 3 - 46
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;

+ 5 - 19
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);
 
         /// <summary>
-        /// Gets the season number from episode file.
+        /// Fills the missing episode numbers from path.
         /// </summary>
-        /// <param name="path">The path.</param>
-        /// <returns>System.Nullable&lt;System.Int32&gt;.</returns>
-        int? GetSeasonNumberFromEpisodeFile(string path);
-
-        /// <summary>
-        /// Gets the ending episode number from file.
-        /// </summary>
-        /// <param name="path">The path.</param>
-        /// <returns>System.Nullable&lt;System.Int32&gt;.</returns>
-        int? GetEndingEpisodeNumberFromFile(string path);
-
-        /// <summary>
-        /// Gets the episode number from file.
-        /// </summary>
-        /// <param name="path">The path.</param>
-        /// <param name="considerSeasonless">if set to <c>true</c> [consider seasonless].</param>
-        /// <returns>System.Nullable&lt;System.Int32&gt;.</returns>
-        int? GetEpisodeNumberFromFile(string path, bool considerSeasonless);
+        /// <param name="episode">The episode.</param>
+        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
+        bool FillMissingEpisodeNumbersFromPath(Episode episode);
 
         /// <summary>
         /// Parses the name.

+ 7 - 9
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<LocalImageInfo> 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<LocalImageInfo>();
@@ -402,13 +406,7 @@ namespace MediaBrowser.LocalMetadata.Images
 
         private FileSystemInfo GetImage(IEnumerable<FileSystemInfo> 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));
         }
     }
 }

+ 8 - 0
MediaBrowser.Model/ApiClient/IApiClient.cs

@@ -185,6 +185,14 @@ namespace MediaBrowser.Model.ApiClient
         /// <exception cref="ArgumentNullException">url</exception>
         Task<Stream> GetImageStreamAsync(string url, CancellationToken cancellationToken = default(CancellationToken));
 
+        /// <summary>
+        /// Updates the user configuration.
+        /// </summary>
+        /// <param name="userId">The user identifier.</param>
+        /// <param name="configuration">The configuration.</param>
+        /// <returns>Task.</returns>
+        Task UpdateUserConfiguration(string userId, UserConfiguration configuration);
+
         /// <summary>
         /// Gets a BaseItem
         /// </summary>

+ 2 - 9
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -144,12 +144,6 @@ namespace MediaBrowser.Model.Configuration
         /// <value>The image saving convention.</value>
         public ImageSavingConvention ImageSavingConvention { get; set; }
 
-        /// <summary>
-        /// Gets or sets a value indicating whether [enable people prefix sub folders].
-        /// </summary>
-        /// <value><c>true</c> if [enable people prefix sub folders]; otherwise, <c>false</c>.</value>
-        public bool EnablePeoplePrefixSubFolders { get; set; }
-
         /// <summary>
         /// Gets or sets the encoding quality.
         /// </summary>
@@ -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; }
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ServerConfiguration" /> 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();
 

+ 7 - 1
MediaBrowser.Model/Dto/BaseItemDto.cs

@@ -549,7 +549,13 @@ namespace MediaBrowser.Model.Dto
         /// Gets or sets a value indicating whether [supports playlists].
         /// </summary>
         /// <value><c>true</c> if [supports playlists]; otherwise, <c>false</c>.</value>
-        public bool SupportsPlaylists { get; set; }
+        public bool SupportsPlaylists
+        {
+            get
+            {
+                return RunTimeTicks.HasValue || IsFolder || IsGenre || IsMusicGenre || IsArtist;
+            }
+        }
 
         /// <summary>
         /// Determines whether the specified type is type.

+ 15 - 0
MediaBrowser.Model/Querying/ItemFields.cs

@@ -6,6 +6,11 @@ namespace MediaBrowser.Model.Querying
     /// </summary>
     public enum ItemFields
     {
+        /// <summary>
+        /// The air time
+        /// </summary>
+        AirTime,
+
         /// <summary>
         /// The alternate episode numbers
         /// </summary>
@@ -151,6 +156,11 @@ namespace MediaBrowser.Model.Querying
         /// </summary>
         Revenue,
 
+        /// <summary>
+        /// The season name
+        /// </summary>
+        SeasonName,
+
         /// <summary>
         /// The short overview
         /// </summary>
@@ -181,6 +191,11 @@ namespace MediaBrowser.Model.Querying
         /// </summary>
         SortName,
 
+        /// <summary>
+        /// The special episode numbers
+        /// </summary>
+        SpecialEpisodeNumbers,
+
         /// <summary>
         /// The studios of the item
         /// </summary>

+ 2 - 0
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; }

+ 9 - 0
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; }
+    }
+}

+ 20 - 17
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<double>();
-
-                var startingNumberComplete = numComplete;
-                innerProgress.RegisterAction(p =>
+                if (maxRefreshLevel > 0)
                 {
-                    double innerPercent = startingNumberComplete;
-                    innerPercent += (p / 100);
-                    innerPercent /= numItems;
-                    progress.Report(innerPercent * 100);
-                });
+                    var innerProgress = new ActionableProgress<double>();
 
-                try
-                {
-                    await GetAllItems(user, channelId, null, currentRefreshLevel, maxRefreshLevel, innerProgress, cancellationToken).ConfigureAwait(false);
-                }
-                catch (Exception ex)
-                {
-                    _logger.ErrorException("Error getting channel content", ex);
+                    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);
+                    }
                 }
 
                 numComplete++;

+ 16 - 7
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)
                     {

+ 12 - 9
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)

+ 60 - 13
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)

+ 11 - 13
MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs

@@ -82,8 +82,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
                 return ResolveVideos<Movie>(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<Movie>(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<MusicVideo>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType, false);
+                    return FindMovie<MusicVideo>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType);
                 }
 
                 if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase))
                 {
-                    return FindMovie<Video>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType, false);
+                    return FindMovie<Video>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType);
                 }
 
                 if (string.IsNullOrEmpty(collectionType))
@@ -193,8 +192,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
                     return FindMovie<Movie>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.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 FindMovie<Movie>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType);
                 }
@@ -216,8 +214,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
             }
 
             // To find a movie file, the collection type must be movies or boxsets
-            else if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase) ||
-                string.Equals(collectionType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase))
+            else if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
             {
                 item = ResolveVideo<Movie>(args, true);
             }
@@ -277,9 +274,8 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
         /// <param name="fileSystemEntries">The file system entries.</param>
         /// <param name="directoryService">The directory service.</param>
         /// <param name="collectionType">Type of the collection.</param>
-        /// <param name="supportMultiVersion">if set to <c>true</c> [support multi version].</param>
         /// <returns>Movie.</returns>
-        private T FindMovie<T>(string path, Folder parent, List<FileSystemInfo> fileSystemEntries, IDirectoryService directoryService, string collectionType, bool supportMultiVersion = true)
+        private T FindMovie<T>(string path, Folder parent, List<FileSystemInfo> fileSystemEntries, IDirectoryService directoryService, string collectionType)
             where T : Video, new()
         {
             var multiDiscFolders = new List<FileSystemInfo>();
@@ -328,8 +324,11 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
             
             var result = ResolveVideos<T>(parent, fileSystemEntries, directoryService, collectionType);
 
+            var supportsMultiVersion = !string.Equals(collectionType, CollectionType.HomeVideos) &&
+                                       !string.Equals(collectionType, CollectionType.MusicVideos);
+
             // Test for multi-editions
-            if (result.Items.Count > 1 && supportMultiVersion)
+            if (result.Items.Count > 1 && supportsMultiVersion)
             {
                 var filenamePrefix = Path.GetFileName(path);
 
@@ -474,7 +473,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
                 CollectionType.Movies,
                 CollectionType.HomeVideos,
                 CollectionType.MusicVideos,
-                CollectionType.BoxSets,
                 CollectionType.Movies
             };
 

+ 3 - 14
MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs

@@ -1,5 +1,7 @@
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Naming.Common;
+using MediaBrowser.Naming.IO;
 using System.Linq;
 
 namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
@@ -37,23 +39,10 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
             }
 
             // If the parent is a Season or Series, then this is an Episode if the VideoResolver returns something
-            if (season != null || parent is Series || parent.Parents.OfType<Series>().Any())
+            if (season != null || args.HasParent<Series>())
             {
                 var episode = ResolveVideo<Episode>(args, false);
 
-                if (episode != null)
-                {
-                    if (season != null)
-                    {
-                        episode.ParentIndexNumber = season.IndexNumber;
-                    }
-
-                    if (episode.ParentIndexNumber == null)
-                    {
-                        episode.ParentIndexNumber = SeriesResolver.GetSeasonNumberFromEpisodeFile(args.Path);
-                    }
-                }
-
                 return episode;
             }
 

+ 3 - 2
MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs

@@ -1,7 +1,8 @@
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Entities;
+using MediaBrowser.Naming.Common;
+using MediaBrowser.Naming.TV;
 
 namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
 {
@@ -35,7 +36,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
             {
                 var season = new Season
                 {
-                    IndexNumber = SeriesResolver.GetSeasonNumberFromPath(args.Path, CollectionType.TvShows)
+                    IndexNumber = new SeasonPathParser(new ExtendedNamingOptions(), new RegexProvider()).Parse(args.Path, true).SeasonNumber
                 };
                 
                 if (season.IndexNumber.HasValue && season.IndexNumber.Value == 0)

+ 21 - 352
MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs

@@ -1,5 +1,4 @@
 using MediaBrowser.Common.IO;
-using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
@@ -9,10 +8,10 @@ using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using System;
 using System.Collections.Generic;
-using System.Globalization;
 using System.IO;
-using System.Linq;
-using System.Text.RegularExpressions;
+using MediaBrowser.Naming.Common;
+using MediaBrowser.Naming.IO;
+using MediaBrowser.Naming.TV;
 
 namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
 {
@@ -67,13 +66,11 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
 
                 var collectionType = args.GetCollectionType();
 
-                var isTvShowsFolder = string.Equals(collectionType, CollectionType.TvShows,
-                    StringComparison.OrdinalIgnoreCase);
+                var isTvShowsFolder = string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase);
 
                 // If there's a collection type and it's not tv, it can't be a series
                 if (!string.IsNullOrEmpty(collectionType) &&
-                    !isTvShowsFolder &&
-                    !string.Equals(collectionType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase))
+                    !isTvShowsFolder)
                 {
                     return null;
                 }
@@ -123,7 +120,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
 
                 if ((attributes & FileAttributes.Directory) == FileAttributes.Directory)
                 {
-                    if (IsSeasonFolder(child.FullName, collectionType, directoryService, fileSystem))
+                    if (IsSeasonFolder(child.FullName, collectionType))
                     {
                         //logger.Debug("{0} is a series because of season folder {1}.", path, child.FullName);
                         return true;
@@ -137,7 +134,17 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
                     {
                         var isTvShowsFolder = string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase);
 
-                        if (GetEpisodeNumberFromFile(fullName, isTvShowsFolder).HasValue)
+                        // We can fast track this for known tv folders
+                        if (isTvShowsFolder)
+                        {
+                            return true;
+                        }
+
+                        var resolver = new Naming.TV.EpisodeResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger());
+
+                        var episodeInfo = resolver.Resolve(fullName, FileInfoType.File, isTvShowsFolder, false);
+
+                        if (episodeInfo != null && (episodeInfo.EpisodeNumber.HasValue || episodeInfo.IsByDate))
                         {
                             return true;
                         }
@@ -172,351 +179,13 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
         /// </summary>
         /// <param name="path">The path.</param>
         /// <param name="collectionType">Type of the collection.</param>
-        /// <param name="directoryService">The directory service.</param>
-        /// <param name="fileSystem">The file system.</param>
         /// <returns><c>true</c> if [is season folder] [the specified path]; otherwise, <c>false</c>.</returns>
-        private static bool IsSeasonFolder(string path, string collectionType, IDirectoryService directoryService, IFileSystem fileSystem)
-        {
-            var seasonNumber = GetSeasonNumberFromPath(path, collectionType);
-            var hasSeasonNumber = seasonNumber != null;
-
-            if (!hasSeasonNumber)
-            {
-                return false;
-            }
-
-            //// It's a season folder if it's named as such and does not contain any audio files, apart from theme.mp3
-            //foreach (var fileSystemInfo in directoryService.GetFileSystemEntries(path))
-            //{
-            //    var attributes = fileSystemInfo.Attributes;
-
-            //    if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
-            //    {
-            //        continue;
-            //    }
-
-            //    // Can't enforce this because files saved by Bitcasa are always marked System
-            //    //if ((attributes & FileAttributes.System) == FileAttributes.System)
-            //    //{
-            //    //    continue;
-            //    //}
-
-            //    if ((attributes & FileAttributes.Directory) == FileAttributes.Directory)
-            //    {
-            //        //if (IsBadFolder(fileSystemInfo.Name))
-            //        //{
-            //        //    return false;
-            //        //}
-            //    }
-            //    else
-            //    {
-            //        if (EntityResolutionHelper.IsAudioFile(fileSystemInfo.FullName) &&
-            //            !string.Equals(fileSystem.GetFileNameWithoutExtension(fileSystemInfo), BaseItem.ThemeSongFilename))
-            //        {
-            //            return false;
-            //        }
-            //    }
-            //}
-
-            return true;
-        }
-
-        /// <summary>
-        /// A season folder must contain one of these somewhere in the name
-        /// </summary>
-        private static readonly string[] SeasonFolderNames =
-        {
-            "season",
-            "sæson",
-            "temporada",
-            "saison",
-            "staffel",
-            "series",
-            "сезон"
-        };
-
-        /// <summary>
-        /// Used to detect paths that represent episodes, need to make sure they don't also
-        /// match movie titles like "2001 A Space..."
-        /// Currently we limit the numbers here to 2 digits to try and avoid this
-        /// </summary>
-        private static readonly Regex[] EpisodeExpressions =
-        {
-            new Regex(
-                @".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})[^\\\/]*$",
-                RegexOptions.Compiled),
-            new Regex(
-                @".*(\\|\/)[sS](?<seasonnumber>\d{1,4})[x,X]?[eE](?<epnumber>\d{1,3})[^\\\/]*$",
-                RegexOptions.Compiled),
-            new Regex(
-                @".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))[^\\\/]*$",
-                RegexOptions.Compiled),
-            new Regex(
-                @".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})[^\\\/]*$",
-                RegexOptions.Compiled)
-        };
-        private static readonly Regex[] MultipleEpisodeExpressions =
-        {
-            new Regex(
-                @".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )\d{1,4}[eExX](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
-                RegexOptions.Compiled),
-            new Regex(
-                @".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )\d{1,4}[xX][eE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
-                RegexOptions.Compiled),
-            new Regex(
-                @".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
-                RegexOptions.Compiled),
-            new Regex(
-                @".*(\\|\/)[sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3})(-[xE]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
-                RegexOptions.Compiled),
-            new Regex(
-                @".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )\d{1,4}[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
-                RegexOptions.Compiled),
-            new Regex(
-                @".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )\d{1,4}[xX][eE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
-                RegexOptions.Compiled),
-            new Regex(
-                @".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
-                RegexOptions.Compiled),
-            new Regex(
-                @".*(\\|\/)(?<seriesname>((?![sS]?\d{1,4}[xX]\d{1,3})[^\\\/])*)?([sS]?(?<seasonnumber>\d{1,4})[xX](?<epnumber>\d{1,3}))(-[xX]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
-                RegexOptions.Compiled),
-            new Regex(
-                @".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})((-| - )?[xXeE](?<endingepnumber>\d{1,3}))+[^\\\/]*$",
-                RegexOptions.Compiled),
-            new Regex(
-                @".*(\\|\/)(?<seriesname>[^\\\/]*)[sS](?<seasonnumber>\d{1,4})[xX\.]?[eE](?<epnumber>\d{1,3})(-[xX]?[eE]?(?<endingepnumber>\d{1,3}))+[^\\\/]*$",
-                RegexOptions.Compiled)
-        };
-
-        /// <summary>
-        /// To avoid the following matching movies they are only valid when contained in a folder which has been matched as a being season, or the media type is TV series
-        /// </summary>
-        private static readonly Regex[] EpisodeExpressionsWithoutSeason =
+        private static bool IsSeasonFolder(string path, string collectionType)
         {
-            new Regex(
-                @".*[\\\/](?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*\.\w+$",
-                RegexOptions.Compiled),
-            // "01.avi"
-            new Regex(
-                @".*(\\|\/)(?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*\s?-\s?[^\\\/]*$",
-                RegexOptions.Compiled),
-            // "01 - blah.avi", "01-blah.avi"
-             new Regex(
-                @".*(\\|\/)(?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*\.[^\\\/]+$",
-                RegexOptions.Compiled),
-            // "01.blah.avi"
-            new Regex(
-                @".*[\\\/][^\\\/]* - (?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*[^\\\/]*$",
-                RegexOptions.Compiled),
-            // "blah - 01.avi", "blah 2 - 01.avi", "blah - 01 blah.avi", "blah 2 - 01 blah", "blah - 01 - blah.avi", "blah 2 - 01 - blah"
-        };
+            var isTvFolder = string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase);
+            var seasonNumber = new SeasonPathParser(new ExtendedNamingOptions(), new RegexProvider()).Parse(path, isTvFolder).SeasonNumber;
 
-        public static int? GetSeasonNumberFromPath(string path)
-        {
-            return GetSeasonNumberFromPath(path, CollectionType.TvShows);
-        }
-
-        /// <summary>
-        /// Gets the season number from path.
-        /// </summary>
-        /// <param name="path">The path.</param>
-        /// <param name="collectionType">Type of the collection.</param>
-        /// <returns>System.Nullable{System.Int32}.</returns>
-        public static int? GetSeasonNumberFromPath(string path, string collectionType)
-        {
-            var filename = Path.GetFileName(path);
-
-            if (string.Equals(collectionType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
-            {
-                if (string.Equals(filename, "specials", StringComparison.OrdinalIgnoreCase))
-                {
-                    return 0;
-                }
-            }
-
-            int val;
-            if (int.TryParse(filename, NumberStyles.Integer, CultureInfo.InvariantCulture, out val))
-            {
-                return val;
-            }
-
-            if (filename.StartsWith("s", StringComparison.OrdinalIgnoreCase))
-            {
-                var testFilename = filename.Substring(1);
-
-                if (int.TryParse(testFilename, NumberStyles.Integer, CultureInfo.InvariantCulture, out val))
-                {
-                    return val;
-                }
-            }
-
-            // Look for one of the season folder names
-            foreach (var name in SeasonFolderNames)
-            {
-                var index = filename.IndexOf(name, StringComparison.OrdinalIgnoreCase);
-
-                if (index != -1)
-                {
-                    return GetSeasonNumberFromPathSubstring(filename.Substring(index + name.Length));
-                }
-            }
-
-            return null;
-        }
-
-        /// <summary>
-        /// Extracts the season number from the second half of the Season folder name (everything after "Season", or "Staffel")
-        /// </summary>
-        /// <param name="path">The path.</param>
-        /// <returns>System.Nullable{System.Int32}.</returns>
-        private static int? GetSeasonNumberFromPathSubstring(string path)
-        {
-            var numericStart = -1;
-            var length = 0;
-
-            // Find out where the numbers start, and then keep going until they end
-            for (var i = 0; i < path.Length; i++)
-            {
-                if (char.IsNumber(path, i))
-                {
-                    if (numericStart == -1)
-                    {
-                        numericStart = i;
-                    }
-                    length++;
-                }
-                else if (numericStart != -1)
-                {
-                    break;
-                }
-            }
-
-            if (numericStart == -1)
-            {
-                return null;
-            }
-
-            return int.Parse(path.Substring(numericStart, length), CultureInfo.InvariantCulture);
-        }
-
-        /// <summary>
-        /// Episodes the number from file.
-        /// </summary>
-        /// <param name="fullPath">The full path.</param>
-        /// <param name="considerSeasonlessNames">if set to <c>true</c> [is in season].</param>
-        /// <returns>System.String.</returns>
-        public static int? GetEpisodeNumberFromFile(string fullPath, bool considerSeasonlessNames)
-        {
-            string fl = fullPath.ToLower();
-            foreach (var r in EpisodeExpressions)
-            {
-                Match m = r.Match(fl);
-                if (m.Success)
-                    return ParseEpisodeNumber(m.Groups["epnumber"].Value);
-            }
-            if (considerSeasonlessNames)
-            {
-                var match = EpisodeExpressionsWithoutSeason.Select(r => r.Match(fl))
-                    .FirstOrDefault(m => m.Success);
-
-                if (match != null)
-                {
-                    return ParseEpisodeNumber(match.Groups["epnumber"].Value);
-                }
-            }
-
-            return null;
-        }
-
-        public static int? GetEndingEpisodeNumberFromFile(string fullPath)
-        {
-            var fl = fullPath.ToLower();
-            foreach (var r in MultipleEpisodeExpressions)
-            {
-                var m = r.Match(fl);
-                if (m.Success && !string.IsNullOrEmpty(m.Groups["endingepnumber"].Value))
-                    return ParseEpisodeNumber(m.Groups["endingepnumber"].Value);
-            }
-            foreach (var r in EpisodeExpressionsWithoutSeason)
-            {
-                var m = r.Match(fl);
-                if (m.Success && !string.IsNullOrEmpty(m.Groups["endingepnumber"].Value))
-                    return ParseEpisodeNumber(m.Groups["endingepnumber"].Value);
-            }
-            return null;
-        }
-
-        /// <summary>
-        /// Seasons the number from episode file.
-        /// </summary>
-        /// <param name="fullPath">The full path.</param>
-        /// <returns>System.String.</returns>
-        public static int? GetSeasonNumberFromEpisodeFile(string fullPath)
-        {
-            string fl = fullPath.ToLower();
-            foreach (var r in EpisodeExpressions)
-            {
-                Match m = r.Match(fl);
-                if (m.Success)
-                {
-                    Group g = m.Groups["seasonnumber"];
-                    if (g != null)
-                    {
-                        var val = g.Value;
-
-                        if (!string.IsNullOrWhiteSpace(val))
-                        {
-                            int num;
-
-                            if (int.TryParse(val, NumberStyles.Integer, UsCulture, out num))
-                            {
-                                return num;
-                            }
-                        }
-                    }
-                    return null;
-                }
-            }
-            return null;
-        }
-
-        public static string GetSeriesNameFromEpisodeFile(string fullPath)
-        {
-            var fl = fullPath.ToLower();
-            foreach (var r in EpisodeExpressions)
-            {
-                var m = r.Match(fl);
-                if (m.Success)
-                {
-                    var g = m.Groups["seriesname"];
-                    if (g != null)
-                    {
-                        var val = g.Value;
-
-                        if (!string.IsNullOrWhiteSpace(val))
-                        {
-                            return val;
-                        }
-                    }
-                    return null;
-                }
-            }
-            return null;
-        }
-
-        private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
-        private static int? ParseEpisodeNumber(string val)
-        {
-            int num;
-
-            if (!string.IsNullOrEmpty(val) && int.TryParse(val, NumberStyles.Integer, UsCulture, out num))
-            {
-                return num;
-            }
-
-            return null;
+            return seasonNumber.HasValue;
         }
 
         /// <summary>

+ 14 - 5
MediaBrowser.Server.Implementations/Library/UserManager.cs

@@ -781,20 +781,29 @@ namespace MediaBrowser.Server.Implementations.Library
             {
                 lock (_policySyncLock)
                 {
-                    return (UserPolicy)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), path);
+                    return (UserPolicy) _xmlSerializer.DeserializeFromFile(typeof (UserPolicy), path);
                 }
             }
+            catch (FileNotFoundException)
+            {
+                return GetDefaultPolicy(user);
+            }
             catch (Exception ex)
             {
                 _logger.ErrorException("Error reading policy file: {0}", ex, path);
 
-                return new UserPolicy
-                {
-                    EnableSync = !user.ConnectLinkType.HasValue || user.ConnectLinkType.Value != UserLinkType.Guest
-                };
+                return GetDefaultPolicy(user);
             }
         }
 
+        private UserPolicy GetDefaultPolicy(User user)
+        {
+            return new UserPolicy
+            {
+                EnableSync = true
+            };
+        }
+
         private readonly object _policySyncLock = new object();
         public async Task UpdateUserPolicy(string userId, UserPolicy userPolicy)
         {

+ 1 - 1
MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json

@@ -650,7 +650,7 @@
     "OptionLow": "Low",
     "HeaderSettings": "Settings",
     "OptionAutomaticallySyncNewContent": "Automatically sync new content",
-    "OptionAutomaticallySyncNewContentHelp": "New content added to these folders will be automatically synced to the device.",
+    "OptionAutomaticallySyncNewContentHelp": "New content added to this category will be automatically synced to the device.",
     "OptionSyncUnwatchedVideosOnly": "Sync unwatched videos only",
     "OptionSyncUnwatchedVideosOnlyHelp": "Only unwatched videos will be synced, and videos will be removed from the device as they are watched.",
     "LabelItemLimit": "Item limit:",

+ 2 - 1
MediaBrowser.Server.Implementations/Localization/Server/server.json

@@ -1295,5 +1295,6 @@
     "LabelEnableSingleImageInDidlLimit": "Limit to single embedded image",
     "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl.",
     "TabActivity": "Activity",
-    "TitleSync": "Sync"
+    "TitleSync": "Sync",
+    "OptionAllowSyncContent":  "Allow syncing media to devices"
 }

+ 1 - 1
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -51,7 +51,7 @@
     </Reference>
     <Reference Include="MediaBrowser.Naming, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\MediaBrowser.Naming.1.0.0.18\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath>
+      <HintPath>..\packages\MediaBrowser.Naming.1.0.0.21\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath>
     </Reference>
     <Reference Include="Mono.Nat, Version=1.2.21.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>

+ 1 - 1
MediaBrowser.Server.Implementations/Sync/SyncScheduledTask.cs

@@ -61,7 +61,7 @@ namespace MediaBrowser.Server.Implementations.Sync
 
         public bool IsHidden
         {
-            get { return true; }
+            get { return false; }
         }
 
         public bool IsEnabled

+ 1 - 1
MediaBrowser.Server.Implementations/packages.config

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="MediaBrowser.Naming" version="1.0.0.18" targetFramework="net45" />
+  <package id="MediaBrowser.Naming" version="1.0.0.21" targetFramework="net45" />
   <package id="Mono.Nat" version="1.2.21.0" targetFramework="net45" />
   <package id="morelinq" version="1.1.0" targetFramework="net45" />
 </packages>

+ 0 - 1
MediaBrowser.Server.Startup.Common/ApplicationHost.cs

@@ -363,7 +363,6 @@ namespace MediaBrowser.Server.Startup.Common
             var migrations = new List<IVersionMigration>
             {
                 new MigrateUserFolders(ApplicationPaths),
-                new PlaylistImages(ServerConfigurationManager),
                 new RenameXbmcOptions(ServerConfigurationManager),
                 new RenameXmlOptions(ServerConfigurationManager),
                 new DeprecatePlugins(ApplicationPaths),

+ 0 - 1
MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj

@@ -67,7 +67,6 @@
     <Compile Include="Migrations\DeprecatePlugins.cs" />
     <Compile Include="Migrations\IVersionMigration.cs" />
     <Compile Include="Migrations\MigrateUserFolders.cs" />
-    <Compile Include="Migrations\PlaylistImages.cs" />
     <Compile Include="Migrations\RenameXbmcOptions.cs" />
     <Compile Include="Migrations\RenameXmlOptions.cs" />
     <Compile Include="NativeEnvironment.cs" />

+ 0 - 55
MediaBrowser.Server.Startup.Common/Migrations/PlaylistImages.cs

@@ -1,55 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using System.IO;
-using System.Linq;
-
-namespace MediaBrowser.Server.Startup.Common.Migrations
-{
-    public class PlaylistImages : IVersionMigration
-    {
-        private readonly IServerConfigurationManager _config;
-
-        public PlaylistImages(IServerConfigurationManager config)
-        {
-            _config = config;
-        }
-
-        public void Run()
-        {
-            if (!_config.Configuration.PlaylistImagesDeleted)
-            {
-                DeletePlaylistImages();
-                _config.Configuration.PlaylistImagesDeleted = true;
-                _config.SaveConfiguration();
-            }
-        }
-
-        private void DeletePlaylistImages()
-        {
-            try
-            {
-                var path = Path.Combine(_config.ApplicationPaths.DataPath, "playlists");
-
-                var files = Directory.GetFiles(path, "*", SearchOption.AllDirectories)
-                    .Where(i => BaseItem.SupportedImageExtensions.Contains(Path.GetExtension(i) ?? string.Empty))
-                    .ToList();
-
-                foreach (var file in files)
-                {
-                    try
-                    {
-                        File.Delete(file);
-                    }
-                    catch (IOException)
-                    {
-
-                    }
-                }
-            }
-            catch (IOException)
-            {
-
-            }
-        }
-    }
-}

+ 0 - 1
MediaBrowser.Tests/MediaBrowser.Tests.csproj

@@ -53,7 +53,6 @@
     <Compile Include="MediaEncoding\Subtitles\AssParserTests.cs" />
     <Compile Include="MediaEncoding\Subtitles\SrtParserTests.cs" />
     <Compile Include="MediaEncoding\Subtitles\VttWriterTest.cs" />
-    <Compile Include="Resolvers\TvUtilTests.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
   <ItemGroup>

+ 0 - 246
MediaBrowser.Tests/Resolvers/TvUtilTests.cs

@@ -1,246 +0,0 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Server.Implementations.Library.Resolvers.TV;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-namespace MediaBrowser.Tests.Resolvers
-{
-    [TestClass]
-    public class TvUtilTests
-    {
-        [TestMethod]
-        public void TestGetEpisodeNumberFromFile()
-        {
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 02\S02E03 blah.avi", true));
-
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 1\01x02 blah.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 1\S01x02 blah.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 1\S01E02 blah.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 1\S01xE02 blah.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 1\seriesname 01x02 blah.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 1\seriesname S01x02 blah.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 1\seriesname S01E02 blah.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 1\seriesname S01xE02 blah.avi", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2\Elementary - 02x03 - 02x04 - 02x15 - Ep Name.ext", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2\02x03 - 02x04 - 02x15 - Ep Name.ext", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2\02x03-04-15 - Ep Name.ext", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2\Elementary - 02x03-04-15 - Ep Name.ext", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 02\02x03-E15 - Ep Name.ext", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 02\Elementary - 02x03-E15 - Ep Name.ext", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 02\02x03 - x04 - x15 - Ep Name.ext", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 02\Elementary - 02x03 - x04 - x15 - Ep Name.ext", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 02\02x03x04x15 - Ep Name.ext", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 02\Elementary - 02x03x04x15 - Ep Name.ext", true));
-            Assert.AreEqual(23, SeriesResolver.GetEpisodeNumberFromFile(@"Season 1\Elementary - S01E23-E24-E26 - The Woman.mp4", true));
-            Assert.AreEqual(23, SeriesResolver.GetEpisodeNumberFromFile(@"Season 1\S01E23-E24-E26 - The Woman.mp4", true));
-            Assert.AreEqual(9, SeriesResolver.GetEpisodeNumberFromFile(@"Season 25\The Simpsons.S25E09.Steal this episode.mp4", true));
-            Assert.AreEqual(8, SeriesResolver.GetEpisodeNumberFromFile(@"The Simpsons\The Simpsons.S25E08.Steal this episode.mp4", false));
-            Assert.AreEqual(136, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2\[HorribleSubs] Hunter X Hunter - 136 [720p].mkv",true));
-
-            //Four Digits seasons
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\2009x02 blah.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\S2009x02 blah.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\S2009E02 blah.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\S2009xE02 blah.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\seriesname 2009x02 blah.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\seriesname S2009x02 blah.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\seriesname S2009E02 blah.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\seriesname S2009xE02 blah.avi", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\Elementary - 2009x03 - 2009x04 - 2009x15 - Ep Name.ext", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\2009x03 - 2009x04 - 2009x15 - Ep Name.ext", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\2009x03-04-15 - Ep Name.ext", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\Elementary - 2009x03-04-15 - Ep Name.ext", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\2009x03-E15 - Ep Name.ext", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\Elementary - 2009x03-E15 - Ep Name.ext", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\2009x03 - x04 - x15 - Ep Name.ext", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\Elementary - 2009x03 - x04 - x15 - Ep Name.ext", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\2009x03x04x15 - Ep Name.ext", true));
-            Assert.AreEqual(03, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\Elementary - 2009x03x04x15 - Ep Name.ext", true));
-            Assert.AreEqual(23, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\Elementary - S2009E23-E24-E26 - The Woman.mp4", true));
-            Assert.AreEqual(23, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2009\S2009E23-E24-E26 - The Woman.mp4", true));
-
-            //Without season number
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 1\02 - blah.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2\02 - blah 14 blah.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 1\02 - blah-02 a.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"Season 2\02.avi", true));
-
-            //Without seasons
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"The Simpsons\02.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"The Simpsons\02 - Ep Name.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"The Simpsons\02-Ep Name.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"The Simpsons\02.EpName.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"The Simpsons\The Simpsons - 02.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"The Simpsons\The Simpsons - 02 - Ep Name.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"The Simpsons\The Simpsons - 02 Ep Name.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"The Simpsons\The Simpsons 5 - 02 - Ep Name.avi", true));
-            Assert.AreEqual(02, SeriesResolver.GetEpisodeNumberFromFile(@"The Simpsons\The Simpsons 5 - 02 Ep Name.avi", true));
-        }
-
-        [TestMethod]
-        public void TestGetEndingEpisodeNumberFromFile()
-        {
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 1\4x01 – 20 Hours in America (1).mkv"));
-            
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 1\01x02 blah.avi"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 1\S01x02 blah.avi"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 1\S01E02 blah.avi"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 1\S01xE02 blah.avi"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 1\seriesname 01x02 blah.avi"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 1\seriesname S01x02 blah.avi"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 1\seriesname S01E02 blah.avi"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 1\seriesname S01xE02 blah.avi"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2\02x03 - 04 Ep Name.ext"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2\My show name 02x03 - 04 Ep Name.ext"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2\Elementary - 02x03 - 02x04 - 02x15 - Ep Name.ext"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2\02x03 - 02x04 - 02x15 - Ep Name.ext"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2\02x03-04-15 - Ep Name.ext"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2\Elementary - 02x03-04-15 - Ep Name.ext"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 02\02x03-E15 - Ep Name.ext"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 02\Elementary - 02x03-E15 - Ep Name.ext"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 02\02x03 - x04 - x15 - Ep Name.ext"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 02\Elementary - 02x03 - x04 - x15 - Ep Name.ext"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 02\02x03x04x15 - Ep Name.ext"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 02\Elementary - 02x03x04x15 - Ep Name.ext"));
-            Assert.AreEqual(26, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 1\Elementary - S01E23-E24-E26 - The Woman.mp4"));
-            Assert.AreEqual(26, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 1\S01E23-E24-E26 - The Woman.mp4"));
-
-
-            //Four Digits seasons
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\2009x02 blah.avi"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\S2009x02 blah.avi"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\S2009E02 blah.avi"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\S2009xE02 blah.avi"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\seriesname 2009x02 blah.avi"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\seriesname S2009x02 blah.avi"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\seriesname S2009E02 blah.avi"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\seriesname S2009xE02 blah.avi"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\Elementary - 2009x03 - 2009x04 - 2009x15 - Ep Name.ext"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\2009x03 - 2009x04 - 2009x15 - Ep Name.ext"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\2009x03-04-15 - Ep Name.ext"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\Elementary - 2009x03-04-15 - Ep Name.ext"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\2009x03-E15 - Ep Name.ext"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\Elementary - 2009x03-E15 - Ep Name.ext"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\2009x03 - x04 - x15 - Ep Name.ext"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\Elementary - 2009x03 - x04 - x15 - Ep Name.ext"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\2009x03x04x15 - Ep Name.ext"));
-            Assert.AreEqual(15, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\Elementary - 2009x03x04x15 - Ep Name.ext"));
-            Assert.AreEqual(26, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\Elementary - S2009E23-E24-E26 - The Woman.mp4"));
-            Assert.AreEqual(26, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2009\S2009E23-E24-E26 - The Woman.mp4"));
-
-            //Without season number
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 1\02 - blah.avi"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2\02 - blah 14 blah.avi"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 1\02 - blah-02 a.avi"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2\02.avi"));
-
-            Assert.AreEqual(3, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 1\02-03 - blah.avi"));
-            Assert.AreEqual(4, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2\02-04 - blah 14 blah.avi"));
-            Assert.AreEqual(5, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 1\02-05 - blah-02 a.avi"));
-            Assert.AreEqual(4, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2\02-04.avi"));
-            Assert.AreEqual(null, SeriesResolver.GetEndingEpisodeNumberFromFile(@"Season 2\[HorribleSubs] Hunter X Hunter - 136 [720p].mkv"));
-
-        }
-
-        [TestMethod]
-        public void TestGetSeasonNumberFromPath() {
-
-            Assert.AreEqual(02, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"\Show\Season 02\S02E03 blah.avi"));
-            
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 1"));
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 1"));
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 1"));
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 1"));
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 1"));
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 1"));
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 1"));
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 1"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 02"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 02"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 02"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 02"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 02"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 02"));
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 1"));
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 1"));
-
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Seinfeld\S02"));
-            
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Seinfeld\2"));
-            
-            //Four Digits seasons
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromPath(@"\Drive\Season 2009"));
-        }
-
-        [TestMethod]
-        public void TestGetSeasonNumberFromEpisodeFile()
-        {
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 1\01x02 blah.avi"));
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 1\S01x02 blah.avi"));
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 1\S01E02 blah.avi"));
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 1\S01xE02 blah.avi"));
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 1\seriesname 01x02 blah.avi"));
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 1\seriesname S01x02 blah.avi"));
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 1\seriesname S01E02 blah.avi"));
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 1\seriesname S01xE02 blah.avi"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2\Elementary - 02x03 - 02x04 - 02x15 - Ep Name.ext"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2\02x03 - 02x04 - 02x15 - Ep Name.ext"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2\02x03-04-15 - Ep Name.ext"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2\Elementary - 02x03-04-15 - Ep Name.ext"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 02\02x03-E15 - Ep Name.ext"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 02\Elementary - 02x03-E15 - Ep Name.ext"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 02\02x03 - x04 - x15 - Ep Name.ext"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 02\Elementary - 02x03 - x04 - x15 - Ep Name.ext"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 02\02x03x04x15 - Ep Name.ext"));
-            Assert.AreEqual(2, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 02\Elementary - 02x03x04x15 - Ep Name.ext"));
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 1\Elementary - S01E23-E24-E26 - The Woman.mp4"));
-            Assert.AreEqual(1, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 1\S01E23-E24-E26 - The Woman.mp4"));
-
-            //Four Digits seasons
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\2009x02 blah.avi"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\S2009x02 blah.avi"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\S2009E02 blah.avi"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\S2009xE02 blah.avi"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\seriesname 2009x02 blah.avi"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\seriesname S2009x02 blah.avi"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\seriesname S2009E02 blah.avi"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\seriesname S2009xE02 blah.avi"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\Elementary - 2009x03 - 2009x04 - 2009x15 - Ep Name.ext"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\2009x03 - 2009x04 - 2009x15 - Ep Name.ext"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\2009x03-04-15 - Ep Name.ext"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\Elementary - 2009x03-04-15 - Ep Name.ext"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\2009x03-E15 - Ep Name.ext"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\Elementary - 2009x03-E15 - Ep Name.ext"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\2009x03 - x04 - x15 - Ep Name.ext"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\Elementary - 2009x03 - x04 - x15 - Ep Name.ext"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\2009x03x04x15 - Ep Name.ext"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\Elementary - 2009x03x04x15 - Ep Name.ext"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\Elementary - S2009E23-E24-E26 - The Woman.mp4"));
-            Assert.AreEqual(2009, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 2009\S2009E23-E24-E26 - The Woman.mp4"));
-            Assert.AreEqual(25, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"Season 25\The Simpsons.S25E09.Steal this episode.mp4"));
-            Assert.AreEqual(25, SeriesResolver.GetSeasonNumberFromEpisodeFile(@"The Simpsons\The Simpsons.S25E09.Steal this episode.mp4"));
-        }
-    }
-}

+ 1 - 0
MediaBrowser.WebDashboard/Api/PackageCreator.cs

@@ -197,6 +197,7 @@ namespace MediaBrowser.WebDashboard.Api
                             {
                                 "thirdparty/jquerymobile-1.4.5/jquery.mobile-1.4.5.min.css",
                                 "thirdparty/swipebox-master/css/swipebox.min.css" + versionString,
+                                "thirdparty/fontawesome/css/font-awesome.min.css" + versionString,
                                 "css/all.css" + versionString
                             };
 

+ 21 - 0
MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

@@ -963,6 +963,15 @@
     <Content Include="dashboard-ui\thirdparty\cast_sender.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="dashboard-ui\thirdparty\fontawesome\css\font-awesome.css">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="dashboard-ui\thirdparty\fontawesome\css\font-awesome.min.css">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="dashboard-ui\thirdparty\fontawesome\fonts\fontawesome-webfont.svg">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\thirdparty\jquery-2.1.1.min.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -2917,6 +2926,18 @@
     <None Include="dashboard-ui\css\fonts\RobotoThin.woff">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>
+    <None Include="dashboard-ui\thirdparty\fontawesome\fonts\fontawesome-webfont.eot">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="dashboard-ui\thirdparty\fontawesome\fonts\fontawesome-webfont.ttf">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="dashboard-ui\thirdparty\fontawesome\fonts\fontawesome-webfont.woff">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Include="dashboard-ui\thirdparty\fontawesome\fonts\FontAwesome.otf">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
     <None Include="dashboard-ui\thirdparty\jquerymobile-1.4.4\jquery.mobile-1.4.3.min.map">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>

+ 5 - 0
MediaBrowser.sln

@@ -7,7 +7,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{F0E0E6
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8C5D6ABC-D277-407B-8061-3AA04251D539}"
 	ProjectSection(SolutionItems) = preProject
+		Performance1.psess = Performance1.psess
 		Performance19.psess = Performance19.psess
+		Performance2.psess = Performance2.psess
 	EndProjectSection
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget (2)", ".nuget (2)", "{E60FB157-87E2-4A41-8B04-27EA49B63B4D}"
@@ -516,4 +518,7 @@ Global
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 	EndGlobalSection
+	GlobalSection(Performance) = preSolution
+		HasPerformanceSessions = true
+	EndGlobalSection
 EndGlobal

+ 2 - 2
Nuget/MediaBrowser.Common.Internal.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common.Internal</id>
-        <version>3.0.521</version>
+        <version>3.0.522</version>
         <title>MediaBrowser.Common.Internal</title>
         <authors>Luke</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.521" />
+            <dependency id="MediaBrowser.Common" version="3.0.522" />
             <dependency id="NLog" version="3.1.0.0" />
             <dependency id="SimpleInjector" version="2.6.1" />
             <dependency id="sharpcompress" version="0.10.2" />

+ 1 - 1
Nuget/MediaBrowser.Common.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common</id>
-        <version>3.0.521</version>
+        <version>3.0.522</version>
         <title>MediaBrowser.Common</title>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>

+ 1 - 1
Nuget/MediaBrowser.Model.Signed.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Model.Signed</id>
-        <version>3.0.521</version>
+        <version>3.0.522</version>
         <title>MediaBrowser.Model - Signed Edition</title>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>

+ 2 - 2
Nuget/MediaBrowser.Server.Core.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Server.Core</id>
-        <version>3.0.521</version>
+        <version>3.0.522</version>
         <title>Media Browser.Server.Core</title>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains core components required to build plugins for Media Browser Server.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.521" />
+            <dependency id="MediaBrowser.Common" version="3.0.522" />
         </dependencies>
     </metadata>
     <files>

+ 2 - 2
SharedVersion.cs

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