瀏覽代碼

begin work on daily episodes

Luke Pulverenti 10 年之前
父節點
當前提交
42b1416602
共有 33 個文件被更改,包括 349 次插入128 次删除
  1. 2 1
      MediaBrowser.Api/ItemUpdateService.cs
  2. 4 0
      MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs
  3. 4 1
      MediaBrowser.Common.Implementations/Devices/DeviceId.cs
  4. 4 0
      MediaBrowser.Common.Implementations/Security/MBLicenseFile.cs
  5. 11 0
      MediaBrowser.Common/Extensions/BaseExtensions.cs
  6. 4 0
      MediaBrowser.Common/Plugins/BasePlugin.cs
  7. 10 1
      MediaBrowser.Controller/Entities/TV/Episode.cs
  8. 51 27
      MediaBrowser.Controller/Entities/TV/Season.cs
  9. 7 0
      MediaBrowser.Controller/Library/IUserManager.cs
  10. 0 6
      MediaBrowser.LocalMetadata/BaseXmlProvider.cs
  11. 4 0
      MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
  12. 0 18
      MediaBrowser.Model/ApiClient/IApiClient.cs
  13. 4 0
      MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
  14. 4 0
      MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
  15. 4 0
      MediaBrowser.Providers/Music/FanArtArtistProvider.cs
  16. 4 0
      MediaBrowser.Providers/People/TvdbPersonImageProvider.cs
  17. 4 0
      MediaBrowser.Providers/TV/FanArtSeasonProvider.cs
  18. 4 0
      MediaBrowser.Providers/TV/FanartSeriesProvider.cs
  19. 36 27
      MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
  20. 7 4
      MediaBrowser.Providers/TV/SeriesPostScanTask.cs
  21. 14 1
      MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs
  22. 4 0
      MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs
  23. 4 0
      MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs
  24. 2 1
      MediaBrowser.Server.Implementations/Connect/ConnectManager.cs
  25. 5 0
      MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs
  26. 7 3
      MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
  27. 73 15
      MediaBrowser.Server.Implementations/Library/LibraryManager.cs
  28. 0 2
      MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
  29. 52 16
      MediaBrowser.Server.Implementations/Library/UserManager.cs
  30. 2 2
      MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
  31. 6 3
      MediaBrowser.Server.Implementations/Localization/Server/server.json
  32. 8 0
      MediaBrowser.Server.Implementations/News/NewsService.cs
  33. 4 0
      MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs

+ 2 - 1
MediaBrowser.Api/ItemUpdateService.cs

@@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.Localization;
 using MediaBrowser.Controller.Localization;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Providers;
@@ -73,7 +74,7 @@ namespace MediaBrowser.Api
             if (locationType == LocationType.FileSystem ||
             if (locationType == LocationType.FileSystem ||
                 locationType == LocationType.Offline)
                 locationType == LocationType.Offline)
             {
             {
-                if (!(item is ICollectionFolder) && !(item is UserView) && !(item is AggregateFolder))
+                if (!(item is ICollectionFolder) && !(item is UserView) && !(item is AggregateFolder) && !(item is LiveTvChannel) && !(item is IItemByName))
                 {
                 {
                     var collectionType = _libraryManager.GetInheritedContentType(item);
                     var collectionType = _libraryManager.GetInheritedContentType(item);
                     if (string.IsNullOrWhiteSpace(collectionType))
                     if (string.IsNullOrWhiteSpace(collectionType))

+ 4 - 0
MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs

@@ -223,6 +223,10 @@ namespace MediaBrowser.Common.Implementations.Configuration
             {
             {
                 return Activator.CreateInstance(configurationType);
                 return Activator.CreateInstance(configurationType);
             }
             }
+            catch (DirectoryNotFoundException)
+            {
+                return Activator.CreateInstance(configurationType);
+            }
             catch (Exception ex)
             catch (Exception ex)
             {
             {
                 Logger.ErrorException("Error loading configuration file: {0}", ex, path);
                 Logger.ErrorException("Error loading configuration file: {0}", ex, path);

+ 4 - 1
MediaBrowser.Common.Implementations/Devices/DeviceId.cs

@@ -38,7 +38,10 @@ namespace MediaBrowser.Common.Implementations.Devices
                     _logger.Error("Invalid value found in device id file");
                     _logger.Error("Invalid value found in device id file");
                 }
                 }
             }
             }
-            catch (FileNotFoundException ex)
+            catch (DirectoryNotFoundException)
+            {
+            }
+            catch (FileNotFoundException)
             {
             {
             }
             }
             catch (Exception ex)
             catch (Exception ex)

+ 4 - 0
MediaBrowser.Common.Implementations/Security/MBLicenseFile.cs

@@ -101,6 +101,10 @@ namespace MediaBrowser.Common.Implementations.Security
                 {
                 {
                     contents = File.ReadAllLines(licenseFile);
                     contents = File.ReadAllLines(licenseFile);
                 }
                 }
+                catch (DirectoryNotFoundException)
+                {
+                    (File.Create(licenseFile)).Close();
+                }
                 catch (FileNotFoundException)
                 catch (FileNotFoundException)
                 {
                 {
                     (File.Create(licenseFile)).Close();
                     (File.Create(licenseFile)).Close();

+ 11 - 0
MediaBrowser.Common/Extensions/BaseExtensions.cs

@@ -1,4 +1,6 @@
 using System;
 using System;
+using System.Globalization;
+using System.Linq;
 using System.Security.Cryptography;
 using System.Security.Cryptography;
 using System.Text;
 using System.Text;
 using System.Text.RegularExpressions;
 using System.Text.RegularExpressions;
@@ -54,6 +56,15 @@ namespace MediaBrowser.Common.Extensions
             return sb.ToString();
             return sb.ToString();
         }
         }
 
 
+        public static string RemoveDiacritics(this string text)
+        {
+            return String.Concat(
+                text.Normalize(NormalizationForm.FormD)
+                .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) !=
+                                              UnicodeCategory.NonSpacingMark)
+              ).Normalize(NormalizationForm.FormC);
+        }
+
         /// <summary>
         /// <summary>
         /// Gets the M d5.
         /// Gets the M d5.
         /// </summary>
         /// </summary>

+ 4 - 0
MediaBrowser.Common/Plugins/BasePlugin.cs

@@ -204,6 +204,10 @@ namespace MediaBrowser.Common.Plugins
             {
             {
                 return (TConfigurationType)XmlSerializer.DeserializeFromFile(typeof(TConfigurationType), path);
                 return (TConfigurationType)XmlSerializer.DeserializeFromFile(typeof(TConfigurationType), path);
             }
             }
+            catch (DirectoryNotFoundException)
+            {
+                return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType));
+            }
             catch (FileNotFoundException)
             catch (FileNotFoundException)
             {
             {
                 return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType));
                 return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType));

+ 10 - 1
MediaBrowser.Controller/Entities/TV/Episode.cs

@@ -1,11 +1,11 @@
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Users;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Runtime.Serialization;
 using System.Runtime.Serialization;
-using MediaBrowser.Model.Users;
 
 
 namespace MediaBrowser.Controller.Entities.TV
 namespace MediaBrowser.Controller.Entities.TV
 {
 {
@@ -178,6 +178,15 @@ namespace MediaBrowser.Controller.Entities.TV
             }
             }
         }
         }
 
 
+        [IgnoreDataMember]
+        public bool IsInSeasonFolder
+        {
+            get
+            {
+                return FindParent<Season>() != null;
+            }
+        }
+
         [IgnoreDataMember]
         [IgnoreDataMember]
         public string SeriesName
         public string SeriesName
         {
         {

+ 51 - 27
MediaBrowser.Controller/Entities/TV/Season.cs

@@ -1,13 +1,12 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Localization;
+using MediaBrowser.Controller.Localization;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Users;
+using MoreLinq;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Runtime.Serialization;
 using System.Runtime.Serialization;
-using MediaBrowser.Model.Users;
 
 
 namespace MediaBrowser.Controller.Entities.TV
 namespace MediaBrowser.Controller.Entities.TV
 {
 {
@@ -156,24 +155,6 @@ namespace MediaBrowser.Controller.Entities.TV
             return IndexNumber != null ? IndexNumber.Value.ToString("0000") : Name;
             return IndexNumber != null ? IndexNumber.Value.ToString("0000") : Name;
         }
         }
 
 
-        private IEnumerable<Episode> GetEpisodes()
-        {
-            var series = Series;
-
-            if (series != null && series.ContainsEpisodesWithoutSeasonFolders)
-            {
-                var seasonNumber = IndexNumber;
-
-                if (seasonNumber.HasValue)
-                {
-                    return series.RecursiveChildren.OfType<Episode>()
-                        .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value);
-                }
-            }
-
-            return Children.OfType<Episode>();
-        }
-
         [IgnoreDataMember]
         [IgnoreDataMember]
         public bool IsMissingSeason
         public bool IsMissingSeason
         {
         {
@@ -221,16 +202,32 @@ namespace MediaBrowser.Controller.Entities.TV
             var episodes = GetRecursiveChildren(user)
             var episodes = GetRecursiveChildren(user)
                 .OfType<Episode>();
                 .OfType<Episode>();
 
 
-            if (IndexNumber.HasValue)
+            var series = Series;
+
+            if (IndexNumber.HasValue && series != null)
             {
             {
-                var series = Series;
+                return series.GetEpisodes(user, IndexNumber.Value, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes);
+            }
 
 
-                if (series != null)
+            if (series != null && series.ContainsEpisodesWithoutSeasonFolders)
+            {
+                var seasonNumber = IndexNumber;
+                var list = episodes.ToList();
+
+                if (seasonNumber.HasValue)
                 {
                 {
-                    return series.GetEpisodes(user, IndexNumber.Value, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes);
+                    list.AddRange(series.GetRecursiveChildren(user).OfType<Episode>()
+                        .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value));
+                }
+                else
+                {
+                    list.AddRange(series.GetRecursiveChildren(user).OfType<Episode>()
+                        .Where(i => !i.ParentIndexNumber.HasValue));
                 }
                 }
-            }
 
 
+                episodes = list.DistinctBy(i => i.Id);
+            }
+            
             if (!includeMissingEpisodes)
             if (!includeMissingEpisodes)
             {
             {
                 episodes = episodes.Where(i => !i.IsMissingEpisode);
                 episodes = episodes.Where(i => !i.IsMissingEpisode);
@@ -245,6 +242,33 @@ namespace MediaBrowser.Controller.Entities.TV
                 .Cast<Episode>();
                 .Cast<Episode>();
         }
         }
 
 
+        private IEnumerable<Episode> GetEpisodes()
+        {
+            var episodes = RecursiveChildren.OfType<Episode>();
+            var series = Series;
+
+            if (series != null && series.ContainsEpisodesWithoutSeasonFolders)
+            {
+                var seasonNumber = IndexNumber;
+                var list = episodes.ToList();
+
+                if (seasonNumber.HasValue)
+                {
+                    list.AddRange(series.RecursiveChildren.OfType<Episode>()
+                        .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value));
+                }
+                else
+                {
+                    list.AddRange(series.RecursiveChildren.OfType<Episode>()
+                        .Where(i => !i.ParentIndexNumber.HasValue));
+                }
+
+                episodes = list.DistinctBy(i => i.Id);
+            }
+
+            return episodes;
+        }
+
         public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren)
         public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren)
         {
         {
             return GetEpisodes(user);
             return GetEpisodes(user);

+ 7 - 0
MediaBrowser.Controller/Library/IUserManager.cs

@@ -186,5 +186,12 @@ namespace MediaBrowser.Controller.Library
         /// <param name="userId">The user identifier.</param>
         /// <param name="userId">The user identifier.</param>
         /// <param name="userPolicy">The user policy.</param>
         /// <param name="userPolicy">The user policy.</param>
         Task UpdateUserPolicy(string userId, UserPolicy userPolicy);
         Task UpdateUserPolicy(string userId, UserPolicy userPolicy);
+
+        /// <summary>
+        /// Makes the valid username.
+        /// </summary>
+        /// <param name="username">The username.</param>
+        /// <returns>System.String.</returns>
+        string MakeValidUsername(string username);
     }
     }
 }
 }

+ 0 - 6
MediaBrowser.LocalMetadata/BaseXmlProvider.cs

@@ -28,8 +28,6 @@ namespace MediaBrowser.LocalMetadata
 
 
             var path = file.FullName;
             var path = file.FullName;
 
 
-            //await XmlProviderUtils.XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
             try
             try
             {
             {
                 result.Item = new T();
                 result.Item = new T();
@@ -45,10 +43,6 @@ namespace MediaBrowser.LocalMetadata
             {
             {
                 result.HasMetadata = false;
                 result.HasMetadata = false;
             }
             }
-            finally
-            {
-                //XmlProviderUtils.XmlParsingResourcePool.Release();
-            }
 
 
             return result;
             return result;
         }
         }

+ 4 - 0
MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs

@@ -612,6 +612,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 catch (FileNotFoundException)
                 catch (FileNotFoundException)
                 {
                 {
                     
                     
+                }
+                catch (DirectoryNotFoundException)
+                {
+
                 }
                 }
                 catch (IOException ex)
                 catch (IOException ex)
                 {
                 {

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

@@ -505,15 +505,6 @@ namespace MediaBrowser.Model.ApiClient
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task&lt;PublicSystemInfo&gt;.</returns>
         /// <returns>Task&lt;PublicSystemInfo&gt;.</returns>
         Task<PublicSystemInfo> GetPublicSystemInfoAsync(CancellationToken cancellationToken = default(CancellationToken));
         Task<PublicSystemInfo> GetPublicSystemInfoAsync(CancellationToken cancellationToken = default(CancellationToken));
-        
-        /// <summary>
-        /// Gets a person
-        /// </summary>
-        /// <param name="name">The name.</param>
-        /// <param name="userId">The user id.</param>
-        /// <returns>Task{BaseItemDto}.</returns>
-        /// <exception cref="ArgumentNullException">userId</exception>
-        Task<BaseItemDto> GetPersonAsync(string name, string userId);
 
 
         /// <summary>
         /// <summary>
         /// Gets a list of plugins installed on the server
         /// Gets a list of plugins installed on the server
@@ -967,15 +958,6 @@ namespace MediaBrowser.Model.ApiClient
         /// <exception cref="ArgumentNullException">item</exception>
         /// <exception cref="ArgumentNullException">item</exception>
         string GetPersonImageUrl(BaseItemPerson item, ImageOptions options);
         string GetPersonImageUrl(BaseItemPerson item, ImageOptions options);
 
 
-        /// <summary>
-        /// Gets an image url that can be used to download an image from the api
-        /// </summary>
-        /// <param name="name">The name of the person</param>
-        /// <param name="options">The options.</param>
-        /// <returns>System.String.</returns>
-        /// <exception cref="ArgumentNullException">name</exception>
-        string GetPersonImageUrl(string name, ImageOptions options);
-
         /// <summary>
         /// <summary>
         /// Gets an image url that can be used to download an image from the api
         /// Gets an image url that can be used to download an image from the api
         /// </summary>
         /// </summary>

+ 4 - 0
MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs

@@ -106,6 +106,10 @@ namespace MediaBrowser.Providers.Movies
                 {
                 {
                     // No biggie. Don't blow up
                     // No biggie. Don't blow up
                 }
                 }
+                catch (DirectoryNotFoundException)
+                {
+                    // No biggie. Don't blow up
+                }
             }
             }
 
 
             var language = item.GetPreferredMetadataLanguage();
             var language = item.GetPreferredMetadataLanguage();

+ 4 - 0
MediaBrowser.Providers/Music/FanArtAlbumProvider.cs

@@ -82,6 +82,10 @@ namespace MediaBrowser.Providers.Music
                 catch (FileNotFoundException)
                 catch (FileNotFoundException)
                 {
                 {
 
 
+                }
+                catch (DirectoryNotFoundException)
+                {
+
                 }
                 }
             }
             }
 
 

+ 4 - 0
MediaBrowser.Providers/Music/FanArtArtistProvider.cs

@@ -90,6 +90,10 @@ namespace MediaBrowser.Providers.Music
                 catch (FileNotFoundException)
                 catch (FileNotFoundException)
                 {
                 {
 
 
+                }
+                catch (DirectoryNotFoundException)
+                {
+
                 }
                 }
             }
             }
 
 

+ 4 - 0
MediaBrowser.Providers/People/TvdbPersonImageProvider.cs

@@ -83,6 +83,10 @@ namespace MediaBrowser.Providers.People
             {
             {
                 return null;
                 return null;
             }
             }
+            catch (DirectoryNotFoundException)
+            {
+                return null;
+            }
         }
         }
 
 
         private RemoteImageInfo GetImageInfo(string xmlFile, string personName, CancellationToken cancellationToken)
         private RemoteImageInfo GetImageInfo(string xmlFile, string personName, CancellationToken cancellationToken)

+ 4 - 0
MediaBrowser.Providers/TV/FanArtSeasonProvider.cs

@@ -98,6 +98,10 @@ namespace MediaBrowser.Providers.TV
                     {
                     {
                         // No biggie. Don't blow up
                         // No biggie. Don't blow up
                     }
                     }
+                    catch (DirectoryNotFoundException)
+                    {
+                        // No biggie. Don't blow up
+                    }
                 }
                 }
             }
             }
 
 

+ 4 - 0
MediaBrowser.Providers/TV/FanartSeriesProvider.cs

@@ -106,6 +106,10 @@ namespace MediaBrowser.Providers.TV
                 {
                 {
                     // No biggie. Don't blow up
                     // No biggie. Don't blow up
                 }
                 }
+                catch (DirectoryNotFoundException)
+                {
+                    // No biggie. Don't blow up
+                }
             }
             }
 
 
             var language = item.GetPreferredMetadataLanguage();
             var language = item.GetPreferredMetadataLanguage();

+ 36 - 27
MediaBrowser.Providers/TV/MissingEpisodeProvider.cs

@@ -2,6 +2,7 @@
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Localization;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
@@ -22,14 +23,16 @@ namespace MediaBrowser.Providers.TV
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private readonly ILibraryManager _libraryManager;
         private readonly ILibraryManager _libraryManager;
+        private readonly ILocalizationManager _localization;
 
 
         private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
         private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
 
 
-        public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager)
+        public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization)
         {
         {
             _logger = logger;
             _logger = logger;
             _config = config;
             _config = config;
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
+            _localization = localization;
         }
         }
 
 
         public async Task Run(IEnumerable<IGrouping<string, Series>> series, CancellationToken cancellationToken)
         public async Task Run(IEnumerable<IGrouping<string, Series>> series, CancellationToken cancellationToken)
@@ -93,16 +96,16 @@ namespace MediaBrowser.Providers.TV
 
 
             var hasBadData = HasInvalidContent(group);
             var hasBadData = HasInvalidContent(group);
 
 
-            var anySeasonsRemoved = await RemoveObsoleteOrMissingSeasons(group, episodeLookup, false)
+            var anySeasonsRemoved = await RemoveObsoleteOrMissingSeasons(group, episodeLookup)
                 .ConfigureAwait(false);
                 .ConfigureAwait(false);
 
 
-            var anyEpisodesRemoved = await RemoveObsoleteOrMissingEpisodes(group, episodeLookup, false)
+            var anyEpisodesRemoved = await RemoveObsoleteOrMissingEpisodes(group, episodeLookup)
                 .ConfigureAwait(false);
                 .ConfigureAwait(false);
 
 
             var hasNewEpisodes = false;
             var hasNewEpisodes = false;
             var hasNewSeasons = false;
             var hasNewSeasons = false;
 
 
-            foreach (var series in group.Where(s => s.ContainsEpisodesWithoutSeasonFolders))
+            foreach (var series in group)
             {
             {
                 hasNewSeasons = await AddDummySeasonFolders(series, cancellationToken).ConfigureAwait(false);
                 hasNewSeasons = await AddDummySeasonFolders(series, cancellationToken).ConfigureAwait(false);
             }
             }
@@ -165,14 +168,15 @@ namespace MediaBrowser.Providers.TV
         /// <returns></returns>
         /// <returns></returns>
         private async Task<bool> AddDummySeasonFolders(Series series, CancellationToken cancellationToken)
         private async Task<bool> AddDummySeasonFolders(Series series, CancellationToken cancellationToken)
         {
         {
-            var existingEpisodes = series.RecursiveChildren
+            var episodesInSeriesFolder = series.RecursiveChildren
                 .OfType<Episode>()
                 .OfType<Episode>()
+                .Where(i => !i.IsInSeasonFolder)
                 .ToList();
                 .ToList();
 
 
             var hasChanges = false;
             var hasChanges = false;
 
 
             // Loop through the unique season numbers
             // Loop through the unique season numbers
-            foreach (var seasonNumber in existingEpisodes.Select(i => i.ParentIndexNumber ?? -1)
+            foreach (var seasonNumber in episodesInSeriesFolder.Select(i => i.ParentIndexNumber ?? -1)
                 .Where(i => i >= 0)
                 .Where(i => i >= 0)
                 .Distinct()
                 .Distinct()
                 .ToList())
                 .ToList())
@@ -188,6 +192,20 @@ namespace MediaBrowser.Providers.TV
                 }
                 }
             }
             }
 
 
+            // Unknown season - create a dummy season to put these under
+            if (episodesInSeriesFolder.Any(i => !i.ParentIndexNumber.HasValue))
+            {
+                var hasSeason = series.Children.OfType<Season>()
+                    .Any(i => !i.IndexNumber.HasValue);
+
+                if (!hasSeason)
+                {
+                    await AddSeason(series, null, cancellationToken).ConfigureAwait(false);
+
+                    hasChanges = true;
+                }
+            }
+
             return hasChanges;
             return hasChanges;
         }
         }
 
 
@@ -292,8 +310,7 @@ namespace MediaBrowser.Providers.TV
         /// Removes the virtual entry after a corresponding physical version has been added
         /// Removes the virtual entry after a corresponding physical version has been added
         /// </summary>
         /// </summary>
         private async Task<bool> RemoveObsoleteOrMissingEpisodes(IEnumerable<Series> series, 
         private async Task<bool> RemoveObsoleteOrMissingEpisodes(IEnumerable<Series> series, 
-            IEnumerable<Tuple<int, int>> episodeLookup, 
-            bool forceRemoveAll)
+            IEnumerable<Tuple<int, int>> episodeLookup)
         {
         {
             var existingEpisodes = (from s in series
             var existingEpisodes = (from s in series
                                     let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1)
                                     let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1)
@@ -312,11 +329,6 @@ namespace MediaBrowser.Providers.TV
             var episodesToRemove = virtualEpisodes
             var episodesToRemove = virtualEpisodes
                 .Where(i =>
                 .Where(i =>
                 {
                 {
-                    if (forceRemoveAll)
-                    {
-                        return true;
-                    }
-
                     if (i.Episode.IndexNumber.HasValue && i.Episode.ParentIndexNumber.HasValue)
                     if (i.Episode.IndexNumber.HasValue && i.Episode.ParentIndexNumber.HasValue)
                     {
                     {
                         var seasonNumber = i.Episode.ParentIndexNumber.Value + i.SeasonOffset;
                         var seasonNumber = i.Episode.ParentIndexNumber.Value + i.SeasonOffset;
@@ -362,11 +374,9 @@ namespace MediaBrowser.Providers.TV
         /// </summary>
         /// </summary>
         /// <param name="series">The series.</param>
         /// <param name="series">The series.</param>
         /// <param name="episodeLookup">The episode lookup.</param>
         /// <param name="episodeLookup">The episode lookup.</param>
-        /// <param name="forceRemoveAll">if set to <c>true</c> [force remove all].</param>
         /// <returns>Task{System.Boolean}.</returns>
         /// <returns>Task{System.Boolean}.</returns>
         private async Task<bool> RemoveObsoleteOrMissingSeasons(IEnumerable<Series> series, 
         private async Task<bool> RemoveObsoleteOrMissingSeasons(IEnumerable<Series> series, 
-            IEnumerable<Tuple<int, int>> episodeLookup,
-            bool forceRemoveAll)
+            IEnumerable<Tuple<int, int>> episodeLookup)
         {
         {
             var existingSeasons = (from s in series
             var existingSeasons = (from s in series
                                    let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1)
                                    let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1)
@@ -385,11 +395,6 @@ namespace MediaBrowser.Providers.TV
             var seasonsToRemove = virtualSeasons
             var seasonsToRemove = virtualSeasons
                 .Where(i =>
                 .Where(i =>
                 {
                 {
-                    if (forceRemoveAll)
-                    {
-                        return true;
-                    }
-
                     if (i.Season.IndexNumber.HasValue)
                     if (i.Season.IndexNumber.HasValue)
                     {
                     {
                         var seasonNumber = i.Season.IndexNumber.Value + i.SeasonOffset;
                         var seasonNumber = i.Season.IndexNumber.Value + i.SeasonOffset;
@@ -409,7 +414,9 @@ namespace MediaBrowser.Providers.TV
                         return false;
                         return false;
                     }
                     }
 
 
-                    return true;
+                    // Season does not have a number
+                    // Remove if there are no episodes directly in series without a season number
+                    return i.Season.Series.RecursiveChildren.OfType<Episode>().All(s => s.ParentIndexNumber.HasValue || s.IsInSeasonFolder);
                 })
                 })
                 .ToList();
                 .ToList();
 
 
@@ -472,20 +479,22 @@ namespace MediaBrowser.Providers.TV
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{Season}.</returns>
         /// <returns>Task{Season}.</returns>
         private async Task<Season> AddSeason(Series series, 
         private async Task<Season> AddSeason(Series series, 
-            int seasonNumber, 
+            int? seasonNumber, 
             CancellationToken cancellationToken)
             CancellationToken cancellationToken)
         {
         {
-            _logger.Info("Creating Season {0} entry for {1}", seasonNumber, series.Name);
+            var seasonName = seasonNumber == 0 ? 
+                _config.Configuration.SeasonZeroDisplayName :
+                (seasonNumber.HasValue ? string.Format(_localization.GetLocalizedString("NameSeasonNumber"), seasonNumber.Value.ToString(UsCulture)) : _localization.GetLocalizedString("NameSeasonUnknown"));
 
 
-            var name = seasonNumber == 0 ? _config.Configuration.SeasonZeroDisplayName : string.Format("Season {0}", seasonNumber.ToString(UsCulture));
+            _logger.Info("Creating Season {0} entry for {1}", seasonName, series.Name);
 
 
             var season = new Season
             var season = new Season
             {
             {
-                Name = name,
+                Name = seasonName,
                 IndexNumber = seasonNumber,
                 IndexNumber = seasonNumber,
                 Parent = series,
                 Parent = series,
                 DisplayMediaType = typeof(Season).Name,
                 DisplayMediaType = typeof(Season).Name,
-                Id = (series.Id + seasonNumber.ToString(UsCulture) + name).GetMBId(typeof(Season))
+                Id = (series.Id + (seasonNumber ?? -1).ToString(UsCulture) + seasonName).GetMBId(typeof(Season))
             };
             };
 
 
             await series.AddChild(season, cancellationToken).ConfigureAwait(false);
             await series.AddChild(season, cancellationToken).ConfigureAwait(false);

+ 7 - 4
MediaBrowser.Providers/TV/SeriesPostScanTask.cs

@@ -1,11 +1,12 @@
-using System.Collections.Generic;
-using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Localization;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
 using System;
 using System;
+using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
@@ -25,12 +26,14 @@ namespace MediaBrowser.Providers.TV
         private readonly ILibraryManager _libraryManager;
         private readonly ILibraryManager _libraryManager;
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
         private readonly ILogger _logger;
         private readonly ILogger _logger;
+        private readonly ILocalizationManager _localization;
 
 
-        public SeriesPostScanTask(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config)
+        public SeriesPostScanTask(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, ILocalizationManager localization)
         {
         {
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
             _logger = logger;
             _logger = logger;
             _config = config;
             _config = config;
+            _localization = localization;
         }
         }
 
 
         public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
         public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
@@ -47,7 +50,7 @@ namespace MediaBrowser.Providers.TV
 
 
             var seriesGroups = FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList();
             var seriesGroups = FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList();
 
 
-            await new MissingEpisodeProvider(_logger, _config, _libraryManager).Run(seriesGroups, cancellationToken).ConfigureAwait(false);
+            await new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization).Run(seriesGroups, cancellationToken).ConfigureAwait(false);
 
 
             var numComplete = 0;
             var numComplete = 0;
 
 

+ 14 - 1
MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs

@@ -72,6 +72,10 @@ namespace MediaBrowser.Providers.TV
                 {
                 {
                     // Don't fail the provider because this will just keep on going and going.
                     // Don't fail the provider because this will just keep on going and going.
                 }
                 }
+                catch (DirectoryNotFoundException)
+                {
+                    // Don't fail the provider because this will just keep on going and going.
+                }
             }
             }
 
 
             return list;
             return list;
@@ -101,6 +105,10 @@ namespace MediaBrowser.Providers.TV
                 {
                 {
                     // Don't fail the provider because this will just keep on going and going.
                     // Don't fail the provider because this will just keep on going and going.
                 }
                 }
+                catch (DirectoryNotFoundException)
+                {
+                    // Don't fail the provider because this will just keep on going and going.
+                }
             }
             }
 
 
             return result;
             return result;
@@ -208,8 +216,9 @@ namespace MediaBrowser.Providers.TV
         /// Fetches the episode data.
         /// Fetches the episode data.
         /// </summary>
         /// </summary>
         /// <param name="id">The identifier.</param>
         /// <param name="id">The identifier.</param>
+        /// <param name="identity">The identity.</param>
         /// <param name="seriesDataPath">The series data path.</param>
         /// <param name="seriesDataPath">The series data path.</param>
-        /// <param name="seriesProviderIds"></param>
+        /// <param name="seriesProviderIds">The series provider ids.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{System.Boolean}.</returns>
         /// <returns>Task{System.Boolean}.</returns>
         private Episode FetchEpisodeData(EpisodeInfo id, EpisodeIdentity identity, string seriesDataPath, Dictionary<string, string> seriesProviderIds, CancellationToken cancellationToken)
         private Episode FetchEpisodeData(EpisodeInfo id, EpisodeIdentity identity, string seriesDataPath, Dictionary<string, string> seriesProviderIds, CancellationToken cancellationToken)
@@ -279,6 +288,10 @@ namespace MediaBrowser.Providers.TV
                 {
                 {
                     break;
                     break;
                 }
                 }
+                catch (DirectoryNotFoundException)
+                {
+                    break;
+                }
 
 
                 episodeNumber++;
                 episodeNumber++;
             }
             }

+ 4 - 0
MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs

@@ -94,6 +94,10 @@ namespace MediaBrowser.Providers.TV
                 {
                 {
                     // No tvdb data yet. Don't blow up
                     // No tvdb data yet. Don't blow up
                 }
                 }
+                catch (DirectoryNotFoundException)
+                {
+                    // No tvdb data yet. Don't blow up
+                }
             }
             }
 
 
             return new RemoteImageInfo[] { };
             return new RemoteImageInfo[] { };

+ 4 - 0
MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs

@@ -87,6 +87,10 @@ namespace MediaBrowser.Providers.TV
                 {
                 {
                     // No tvdb data yet. Don't blow up
                     // No tvdb data yet. Don't blow up
                 }
                 }
+                catch (DirectoryNotFoundException)
+                {
+                    // No tvdb data yet. Don't blow up
+                }
             }
             }
 
 
             return new RemoteImageInfo[] { };
             return new RemoteImageInfo[] { };

+ 2 - 1
MediaBrowser.Server.Implementations/Connect/ConnectManager.cs

@@ -1,4 +1,5 @@
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
@@ -789,7 +790,7 @@ namespace MediaBrowser.Server.Implementations.Connect
                         if (user == null)
                         if (user == null)
                         {
                         {
                             // Add user
                             // Add user
-                            user = await _userManager.CreateUser(connectEntry.UserName).ConfigureAwait(false);
+                            user = await _userManager.CreateUser(_userManager.MakeValidUsername(connectEntry.UserName)).ConfigureAwait(false);
 
 
                             user.ConnectUserName = connectEntry.UserName;
                             user.ConnectUserName = connectEntry.UserName;
                             user.ConnectUserId = connectEntry.UserId;
                             user.ConnectUserId = connectEntry.UserId;

+ 5 - 0
MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs

@@ -78,6 +78,11 @@ namespace MediaBrowser.Server.Implementations.Drawing
                 // No biggie
                 // No biggie
                 sizeDictionary = new Dictionary<Guid, ImageSize>();
                 sizeDictionary = new Dictionary<Guid, ImageSize>();
             }
             }
+            catch (DirectoryNotFoundException)
+            {
+                // No biggie
+                sizeDictionary = new Dictionary<Guid, ImageSize>();
+            }
             catch (Exception ex)
             catch (Exception ex)
             {
             {
                 logger.ErrorException("Error parsing image size cache file", ex);
                 logger.ErrorException("Error parsing image size cache file", ex);

+ 7 - 3
MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs

@@ -459,11 +459,11 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
 
 
         private bool IsSameEpisode(string sourcePath, string newPath)
         private bool IsSameEpisode(string sourcePath, string newPath)
         {
         {
-            var sourceFileInfo = new FileInfo(sourcePath);
-            var destinationFileInfo = new FileInfo(newPath);
-
             try
             try
             {
             {
+                var sourceFileInfo = new FileInfo(sourcePath);
+                var destinationFileInfo = new FileInfo(newPath);
+
                 if (sourceFileInfo.Length == destinationFileInfo.Length)
                 if (sourceFileInfo.Length == destinationFileInfo.Length)
                 {
                 {
                     return true;
                     return true;
@@ -473,6 +473,10 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             {
             {
                 return false;
                 return false;
             }
             }
+            catch (DirectoryNotFoundException)
+            {
+                return false;
+            }
 
 
             return false;
             return false;
         }
         }

+ 73 - 15
MediaBrowser.Server.Implementations/Library/LibraryManager.cs

@@ -1755,9 +1755,12 @@ namespace MediaBrowser.Server.Implementations.Library
             var resolver = new EpisodeResolver(new ExtendedNamingOptions(),
             var resolver = new EpisodeResolver(new ExtendedNamingOptions(),
                 new Naming.Logging.NullLogger());
                 new Naming.Logging.NullLogger());
 
 
+            var fileType = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd || episode.VideoType == VideoType.HdDvd ? 
+                FileInfoType.Directory : 
+                FileInfoType.File;
+
             var locationType = episode.LocationType;
             var locationType = episode.LocationType;
 
 
-            var fileType = /*args.IsDirectory ? FileInfoType.Directory :*/ FileInfoType.File;
             var episodeInfo = locationType == LocationType.FileSystem || locationType == LocationType.Offline ?
             var episodeInfo = locationType == LocationType.FileSystem || locationType == LocationType.Offline ?
                 resolver.Resolve(episode.Path, fileType) :
                 resolver.Resolve(episode.Path, fileType) :
                 new Naming.TV.EpisodeInfo();
                 new Naming.TV.EpisodeInfo();
@@ -1769,29 +1772,42 @@ namespace MediaBrowser.Server.Implementations.Library
 
 
             var changed = false;
             var changed = false;
 
 
-            if (!episode.IndexNumber.HasValue)
+            if (episodeInfo.IsByDate)
             {
             {
-                episode.IndexNumber = episodeInfo.EpisodeNumber;
-
                 if (episode.IndexNumber.HasValue)
                 if (episode.IndexNumber.HasValue)
                 {
                 {
+                    episode.IndexNumber = null;
                     changed = true;
                     changed = true;
                 }
                 }
-            }
-
-            if (!episode.IndexNumberEnd.HasValue)
-            {
-                episode.IndexNumberEnd = episodeInfo.EndingEpsiodeNumber;
 
 
                 if (episode.IndexNumberEnd.HasValue)
                 if (episode.IndexNumberEnd.HasValue)
                 {
                 {
+                    episode.IndexNumberEnd = null;
                     changed = true;
                     changed = true;
                 }
                 }
-            }
 
 
-            if (!episode.ParentIndexNumber.HasValue)
-            {
-                episode.ParentIndexNumber = episodeInfo.SeasonNumber;
+                if (!episode.PremiereDate.HasValue)
+                {
+                    if (episodeInfo.Year.HasValue && episodeInfo.Month.HasValue && episodeInfo.Day.HasValue)
+                    {
+                        episode.PremiereDate = new DateTime(episodeInfo.Year.Value, episodeInfo.Month.Value, episodeInfo.Day.Value).ToUniversalTime();
+                    }
+
+                    if (episode.PremiereDate.HasValue)
+                    {
+                        changed = true;
+                    }
+                }
+
+                if (!episode.ProductionYear.HasValue)
+                {
+                    episode.ProductionYear = episodeInfo.Year;
+
+                    if (episode.ProductionYear.HasValue)
+                    {
+                        changed = true;
+                    }
+                }
 
 
                 if (!episode.ParentIndexNumber.HasValue)
                 if (!episode.ParentIndexNumber.HasValue)
                 {
                 {
@@ -1801,11 +1817,53 @@ namespace MediaBrowser.Server.Implementations.Library
                     {
                     {
                         episode.ParentIndexNumber = season.IndexNumber;
                         episode.ParentIndexNumber = season.IndexNumber;
                     }
                     }
+
+                    if (episode.ParentIndexNumber.HasValue)
+                    {
+                        changed = true;
+                    }
+                }
+            }
+            else
+            {
+                if (!episode.IndexNumber.HasValue)
+                {
+                    episode.IndexNumber = episodeInfo.EpisodeNumber;
+
+                    if (episode.IndexNumber.HasValue)
+                    {
+                        changed = true;
+                    }
                 }
                 }
 
 
-                if (episode.ParentIndexNumber.HasValue)
+                if (!episode.IndexNumberEnd.HasValue)
                 {
                 {
-                    changed = true;
+                    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;
+                    }
                 }
                 }
             }
             }
 
 

+ 0 - 2
MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs

@@ -1,7 +1,5 @@
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
-using MediaBrowser.Naming.Common;
-using MediaBrowser.Naming.IO;
 using System.Linq;
 using System.Linq;
 
 
 namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
 namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV

+ 52 - 16
MediaBrowser.Server.Implementations/Library/UserManager.cs

@@ -171,6 +171,38 @@ namespace MediaBrowser.Server.Implementations.Library
             return AuthenticateUser(username, passwordSha1, null, remoteEndPoint);
             return AuthenticateUser(username, passwordSha1, null, remoteEndPoint);
         }
         }
 
 
+        public bool IsValidUsername(string username)
+        {
+            // Usernames can contain letters (a-z), numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)
+            return username.All(IsValidCharacter);
+        }
+
+        private bool IsValidCharacter(char i)
+        {
+            return char.IsLetterOrDigit(i) || char.Equals(i, '-') || char.Equals(i, '_') || char.Equals(i, '\'') ||
+                   char.Equals(i, '.');
+        }
+
+        public string MakeValidUsername(string username)
+        {
+            if (IsValidUsername(username))
+            {
+                return username;
+            }
+            
+            // Usernames can contain letters (a-z), numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)
+            var builder = new StringBuilder();
+
+            foreach (var c in username)
+            {
+                if (IsValidCharacter(c))
+                {
+                    builder.Append(c);
+                }
+            }
+            return builder.ToString();
+        }
+
         public async Task<bool> AuthenticateUser(string username, string passwordSha1, string passwordMd5, string remoteEndPoint)
         public async Task<bool> AuthenticateUser(string username, string passwordSha1, string passwordMd5, string remoteEndPoint)
         {
         {
             if (string.IsNullOrWhiteSpace(username))
             if (string.IsNullOrWhiteSpace(username))
@@ -178,7 +210,8 @@ namespace MediaBrowser.Server.Implementations.Library
                 throw new ArgumentNullException("username");
                 throw new ArgumentNullException("username");
             }
             }
 
 
-            var user = Users.FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
+            var user = Users
+                .FirstOrDefault(i => string.Equals(username, i.Name, StringComparison.OrdinalIgnoreCase));
 
 
             if (user == null)
             if (user == null)
             {
             {
@@ -203,20 +236,6 @@ namespace MediaBrowser.Server.Implementations.Library
                 }
                 }
             }
             }
 
 
-            // Maybe user accidently entered connect credentials. let's be flexible
-            if (!success && user.ConnectLinkType.HasValue && !string.IsNullOrWhiteSpace(passwordMd5))
-            {
-                try
-                {
-                    await _connectFactory().Authenticate(user.ConnectUserName, passwordMd5).ConfigureAwait(false);
-                    success = true;
-                }
-                catch
-                {
-
-                }
-            }
-
             // Update LastActivityDate and LastLoginDate, then save
             // Update LastActivityDate and LastLoginDate, then save
             if (success)
             if (success)
             {
             {
@@ -273,7 +292,7 @@ namespace MediaBrowser.Server.Implementations.Library
             // There always has to be at least one user.
             // There always has to be at least one user.
             if (users.Count == 0)
             if (users.Count == 0)
             {
             {
-                var name = Environment.UserName;
+                var name = MakeValidUsername(Environment.UserName);
 
 
                 var user = InstantiateNewUser(name, false);
                 var user = InstantiateNewUser(name, false);
 
 
@@ -477,6 +496,11 @@ namespace MediaBrowser.Server.Implementations.Library
                 throw new ArgumentNullException("name");
                 throw new ArgumentNullException("name");
             }
             }
 
 
+            if (!IsValidUsername(name))
+            {
+                throw new ArgumentException("Only alphanumeric characters are allowed.");
+            }
+
             if (Users.Any(u => u.Name.Equals(name, StringComparison.OrdinalIgnoreCase)))
             if (Users.Any(u => u.Name.Equals(name, StringComparison.OrdinalIgnoreCase)))
             {
             {
                 throw new ArgumentException(string.Format("A user with the name '{0}' already exists.", name));
                 throw new ArgumentException(string.Format("A user with the name '{0}' already exists.", name));
@@ -803,6 +827,10 @@ namespace MediaBrowser.Server.Implementations.Library
                     return (UserPolicy)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), path);
                     return (UserPolicy)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), path);
                 }
                 }
             }
             }
+            catch (DirectoryNotFoundException)
+            {
+                return GetDefaultPolicy(user);
+            }
             catch (FileNotFoundException)
             catch (FileNotFoundException)
             {
             {
                 return GetDefaultPolicy(user);
                 return GetDefaultPolicy(user);
@@ -840,6 +868,8 @@ namespace MediaBrowser.Server.Implementations.Library
             
             
             var path = GetPolifyFilePath(user);
             var path = GetPolifyFilePath(user);
 
 
+            Directory.CreateDirectory(Path.GetDirectoryName(path));
+
             lock (_policySyncLock)
             lock (_policySyncLock)
             {
             {
                 _xmlSerializer.SerializeToFile(userPolicy, path);
                 _xmlSerializer.SerializeToFile(userPolicy, path);
@@ -900,6 +930,10 @@ namespace MediaBrowser.Server.Implementations.Library
                     return (UserConfiguration)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), path);
                     return (UserConfiguration)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), path);
                 }
                 }
             }
             }
+            catch (DirectoryNotFoundException)
+            {
+                return new UserConfiguration();
+            }
             catch (FileNotFoundException)
             catch (FileNotFoundException)
             {
             {
                 return new UserConfiguration();
                 return new UserConfiguration();
@@ -930,6 +964,8 @@ namespace MediaBrowser.Server.Implementations.Library
                 config = _jsonSerializer.DeserializeFromString<UserConfiguration>(json);
                 config = _jsonSerializer.DeserializeFromString<UserConfiguration>(json);
             }
             }
 
 
+            Directory.CreateDirectory(Path.GetDirectoryName(path));
+            
             lock (_configSyncLock)
             lock (_configSyncLock)
             {
             {
                 _xmlSerializer.SerializeToFile(config, path);
                 _xmlSerializer.SerializeToFile(config, path);

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

@@ -419,7 +419,7 @@
     "HeaderMediaLocations": "Media Locations",
     "HeaderMediaLocations": "Media Locations",
     "LabelFolderTypeValue": "Folder type: {0}",
     "LabelFolderTypeValue": "Folder type: {0}",
     "LabelPathSubstitutionHelp": "Optional: Path substitution can map server paths to network shares that clients can access for direct playback.",
     "LabelPathSubstitutionHelp": "Optional: Path substitution can map server paths to network shares that clients can access for direct playback.",
-    "FolderTypeMixed": "Mixed videos",
+    "FolderTypeMixed": "Mixed content",
     "FolderTypeMovies": "Movies",
     "FolderTypeMovies": "Movies",
     "FolderTypeMusic": "Music",
     "FolderTypeMusic": "Music",
     "FolderTypeAdultVideos": "Adult videos",
     "FolderTypeAdultVideos": "Adult videos",
@@ -658,5 +658,5 @@
     "LabelItemLimitHelp": "Optional. Set a limit to the number of items that will be synced.",
     "LabelItemLimitHelp": "Optional. Set a limit to the number of items that will be synced.",
     "MessageBookPluginRequired": "Requires installation of the Bookshelf plugin",
     "MessageBookPluginRequired": "Requires installation of the Bookshelf plugin",
     "MessageGamePluginRequired": "Requires installation of the GameBrowser plugin",
     "MessageGamePluginRequired": "Requires installation of the GameBrowser plugin",
-    "MessageMixedContentHelp":  "Content will be displayed with as a plain folder structure"
+    "MessageMixedContentHelp":  "Content will be displayed as a plain folder structure"
 }
 }

+ 6 - 3
MediaBrowser.Server.Implementations/Localization/Server/server.json

@@ -37,7 +37,7 @@
     "ButtonOk": "Ok",
     "ButtonOk": "Ok",
     "ButtonCancel": "Cancel",
     "ButtonCancel": "Cancel",
     "ButtonNew": "New",
     "ButtonNew": "New",
-    "FolderTypeMixed": "Mixed videos",
+    "FolderTypeMixed": "Mixed content",
     "FolderTypeMovies": "Movies",
     "FolderTypeMovies": "Movies",
     "FolderTypeMusic": "Music",
     "FolderTypeMusic": "Music",
     "FolderTypeAdultVideos": "Adult videos",
     "FolderTypeAdultVideos": "Adult videos",
@@ -48,7 +48,7 @@
     "FolderTypeBooks": "Books",
     "FolderTypeBooks": "Books",
     "FolderTypeTvShows": "TV",
     "FolderTypeTvShows": "TV",
     "FolderTypeInherit": "Inherit",
     "FolderTypeInherit": "Inherit",
-    "LabelContentType":  "Content type:",
+    "LabelContentType": "Content type:",
     "HeaderSetupLibrary": "Setup your media library",
     "HeaderSetupLibrary": "Setup your media library",
     "ButtonAddMediaFolder": "Add media folder",
     "ButtonAddMediaFolder": "Add media folder",
     "LabelFolderType": "Folder type:",
     "LabelFolderType": "Folder type:",
@@ -1307,5 +1307,8 @@
     "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl.",
     "LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl.",
     "TabActivity": "Activity",
     "TabActivity": "Activity",
     "TitleSync": "Sync",
     "TitleSync": "Sync",
-    "OptionAllowSyncContent":  "Allow syncing media to devices"
+    "OptionAllowSyncContent": "Allow syncing media to devices",
+    "NameSeasonUnknown": "Season Unknown",
+    "NameSeasonNumber": "Season {0}",
+    "LabelNewUserNameHelp": "Usernames can contain letters (a-z), numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)"
 }
 }

+ 8 - 0
MediaBrowser.Server.Implementations/News/NewsService.cs

@@ -26,6 +26,14 @@ namespace MediaBrowser.Server.Implementations.News
             {
             {
                 return GetProductNewsInternal(query);
                 return GetProductNewsInternal(query);
             }
             }
+            catch (DirectoryNotFoundException)
+            {
+                // No biggie
+                return new QueryResult<NewsItem>
+                {
+                    Items = new NewsItem[] { }
+                };
+            }
             catch (FileNotFoundException)
             catch (FileNotFoundException)
             {
             {
                 // No biggie
                 // No biggie

+ 4 - 0
MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs

@@ -256,6 +256,10 @@ namespace MediaBrowser.XbmcMetadata.Savers
                 catch (FileNotFoundException)
                 catch (FileNotFoundException)
                 {
                 {
 
 
+                }
+                catch (DirectoryNotFoundException)
+                {
+
                 }
                 }
 
 
                 writer.WriteEndElement();
                 writer.WriteEndElement();