diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs
index 020908ddd5..272dff3ecc 100644
--- a/MediaBrowser.Api/ItemUpdateService.cs
+++ b/MediaBrowser.Api/ItemUpdateService.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Providers;
@@ -73,7 +74,7 @@ namespace MediaBrowser.Api
if (locationType == LocationType.FileSystem ||
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);
if (string.IsNullOrWhiteSpace(collectionType))
diff --git a/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs b/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs
index 89f6229ace..c53947e443 100644
--- a/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs
+++ b/MediaBrowser.Common.Implementations/Configuration/BaseConfigurationManager.cs
@@ -223,6 +223,10 @@ namespace MediaBrowser.Common.Implementations.Configuration
{
return Activator.CreateInstance(configurationType);
}
+ catch (DirectoryNotFoundException)
+ {
+ return Activator.CreateInstance(configurationType);
+ }
catch (Exception ex)
{
Logger.ErrorException("Error loading configuration file: {0}", ex, path);
diff --git a/MediaBrowser.Common.Implementations/Devices/DeviceId.cs b/MediaBrowser.Common.Implementations/Devices/DeviceId.cs
index 5af236026e..7c0dc1e1f0 100644
--- a/MediaBrowser.Common.Implementations/Devices/DeviceId.cs
+++ b/MediaBrowser.Common.Implementations/Devices/DeviceId.cs
@@ -38,7 +38,10 @@ namespace MediaBrowser.Common.Implementations.Devices
_logger.Error("Invalid value found in device id file");
}
}
- catch (FileNotFoundException ex)
+ catch (DirectoryNotFoundException)
+ {
+ }
+ catch (FileNotFoundException)
{
}
catch (Exception ex)
diff --git a/MediaBrowser.Common.Implementations/Security/MBLicenseFile.cs b/MediaBrowser.Common.Implementations/Security/MBLicenseFile.cs
index 8f3225f4e9..63381efcdf 100644
--- a/MediaBrowser.Common.Implementations/Security/MBLicenseFile.cs
+++ b/MediaBrowser.Common.Implementations/Security/MBLicenseFile.cs
@@ -101,6 +101,10 @@ namespace MediaBrowser.Common.Implementations.Security
{
contents = File.ReadAllLines(licenseFile);
}
+ catch (DirectoryNotFoundException)
+ {
+ (File.Create(licenseFile)).Close();
+ }
catch (FileNotFoundException)
{
(File.Create(licenseFile)).Close();
diff --git a/MediaBrowser.Common/Extensions/BaseExtensions.cs b/MediaBrowser.Common/Extensions/BaseExtensions.cs
index 8e96373f44..4c94f3aa20 100644
--- a/MediaBrowser.Common/Extensions/BaseExtensions.cs
+++ b/MediaBrowser.Common/Extensions/BaseExtensions.cs
@@ -1,4 +1,6 @@
using System;
+using System.Globalization;
+using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
@@ -54,6 +56,15 @@ namespace MediaBrowser.Common.Extensions
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);
+ }
+
///
/// Gets the M d5.
///
diff --git a/MediaBrowser.Common/Plugins/BasePlugin.cs b/MediaBrowser.Common/Plugins/BasePlugin.cs
index 1a536b4ffb..ce068463ea 100644
--- a/MediaBrowser.Common/Plugins/BasePlugin.cs
+++ b/MediaBrowser.Common/Plugins/BasePlugin.cs
@@ -204,6 +204,10 @@ namespace MediaBrowser.Common.Plugins
{
return (TConfigurationType)XmlSerializer.DeserializeFromFile(typeof(TConfigurationType), path);
}
+ catch (DirectoryNotFoundException)
+ {
+ return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType));
+ }
catch (FileNotFoundException)
{
return (TConfigurationType)Activator.CreateInstance(typeof(TConfigurationType));
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index 6b6f07d4c2..6b67cebc88 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -1,11 +1,11 @@
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.TV
{
@@ -178,6 +178,15 @@ namespace MediaBrowser.Controller.Entities.TV
}
}
+ [IgnoreDataMember]
+ public bool IsInSeasonFolder
+ {
+ get
+ {
+ return FindParent() != null;
+ }
+ }
+
[IgnoreDataMember]
public string SeriesName
{
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index ceddbbc3b7..54db12b6f8 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/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.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Users;
+using MoreLinq;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
-using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.TV
{
@@ -156,24 +155,6 @@ namespace MediaBrowser.Controller.Entities.TV
return IndexNumber != null ? IndexNumber.Value.ToString("0000") : Name;
}
- private IEnumerable GetEpisodes()
- {
- var series = Series;
-
- if (series != null && series.ContainsEpisodesWithoutSeasonFolders)
- {
- var seasonNumber = IndexNumber;
-
- if (seasonNumber.HasValue)
- {
- return series.RecursiveChildren.OfType()
- .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value);
- }
- }
-
- return Children.OfType();
- }
-
[IgnoreDataMember]
public bool IsMissingSeason
{
@@ -221,16 +202,32 @@ namespace MediaBrowser.Controller.Entities.TV
var episodes = GetRecursiveChildren(user)
.OfType();
- 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()
+ .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value));
+ }
+ else
+ {
+ list.AddRange(series.GetRecursiveChildren(user).OfType()
+ .Where(i => !i.ParentIndexNumber.HasValue));
}
- }
+ episodes = list.DistinctBy(i => i.Id);
+ }
+
if (!includeMissingEpisodes)
{
episodes = episodes.Where(i => !i.IsMissingEpisode);
@@ -245,6 +242,33 @@ namespace MediaBrowser.Controller.Entities.TV
.Cast();
}
+ private IEnumerable GetEpisodes()
+ {
+ var episodes = RecursiveChildren.OfType();
+ var series = Series;
+
+ if (series != null && series.ContainsEpisodesWithoutSeasonFolders)
+ {
+ var seasonNumber = IndexNumber;
+ var list = episodes.ToList();
+
+ if (seasonNumber.HasValue)
+ {
+ list.AddRange(series.RecursiveChildren.OfType()
+ .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == seasonNumber.Value));
+ }
+ else
+ {
+ list.AddRange(series.RecursiveChildren.OfType()
+ .Where(i => !i.ParentIndexNumber.HasValue));
+ }
+
+ episodes = list.DistinctBy(i => i.Id);
+ }
+
+ return episodes;
+ }
+
public override IEnumerable GetChildren(User user, bool includeLinkedChildren)
{
return GetEpisodes(user);
diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs
index f7fbc9c20e..f5846973eb 100644
--- a/MediaBrowser.Controller/Library/IUserManager.cs
+++ b/MediaBrowser.Controller/Library/IUserManager.cs
@@ -186,5 +186,12 @@ namespace MediaBrowser.Controller.Library
/// The user identifier.
/// The user policy.
Task UpdateUserPolicy(string userId, UserPolicy userPolicy);
+
+ ///
+ /// Makes the valid username.
+ ///
+ /// The username.
+ /// System.String.
+ string MakeValidUsername(string username);
}
}
diff --git a/MediaBrowser.LocalMetadata/BaseXmlProvider.cs b/MediaBrowser.LocalMetadata/BaseXmlProvider.cs
index 82e7809e89..74e3b61caa 100644
--- a/MediaBrowser.LocalMetadata/BaseXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/BaseXmlProvider.cs
@@ -28,8 +28,6 @@ namespace MediaBrowser.LocalMetadata
var path = file.FullName;
- //await XmlProviderUtils.XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
try
{
result.Item = new T();
@@ -45,10 +43,6 @@ namespace MediaBrowser.LocalMetadata
{
result.HasMetadata = false;
}
- finally
- {
- //XmlProviderUtils.XmlParsingResourcePool.Release();
- }
return result;
}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index 7c512840b7..67c9123f57 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -612,6 +612,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles
catch (FileNotFoundException)
{
+ }
+ catch (DirectoryNotFoundException)
+ {
+
}
catch (IOException ex)
{
diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs
index dde6ca0b12..0181325fe7 100644
--- a/MediaBrowser.Model/ApiClient/IApiClient.cs
+++ b/MediaBrowser.Model/ApiClient/IApiClient.cs
@@ -505,15 +505,6 @@ namespace MediaBrowser.Model.ApiClient
/// The cancellation token.
/// Task<PublicSystemInfo>.
Task GetPublicSystemInfoAsync(CancellationToken cancellationToken = default(CancellationToken));
-
- ///
- /// Gets a person
- ///
- /// The name.
- /// The user id.
- /// Task{BaseItemDto}.
- /// userId
- Task GetPersonAsync(string name, string userId);
///
/// Gets a list of plugins installed on the server
@@ -967,15 +958,6 @@ namespace MediaBrowser.Model.ApiClient
/// item
string GetPersonImageUrl(BaseItemPerson item, ImageOptions options);
- ///
- /// Gets an image url that can be used to download an image from the api
- ///
- /// The name of the person
- /// The options.
- /// System.String.
- /// name
- string GetPersonImageUrl(string name, ImageOptions options);
-
///
/// Gets an image url that can be used to download an image from the api
///
diff --git a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
index a7ccf3f6ec..6813f2ff5e 100644
--- a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
+++ b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
@@ -106,6 +106,10 @@ namespace MediaBrowser.Providers.Movies
{
// No biggie. Don't blow up
}
+ catch (DirectoryNotFoundException)
+ {
+ // No biggie. Don't blow up
+ }
}
var language = item.GetPreferredMetadataLanguage();
diff --git a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
index 94d682f44b..123ff9e290 100644
--- a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
+++ b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
@@ -82,6 +82,10 @@ namespace MediaBrowser.Providers.Music
catch (FileNotFoundException)
{
+ }
+ catch (DirectoryNotFoundException)
+ {
+
}
}
diff --git a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
index a8df95fd1b..6f633cfc87 100644
--- a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
+++ b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
@@ -90,6 +90,10 @@ namespace MediaBrowser.Providers.Music
catch (FileNotFoundException)
{
+ }
+ catch (DirectoryNotFoundException)
+ {
+
}
}
diff --git a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs
index 63d0546647..253acc13f1 100644
--- a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs
+++ b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs
@@ -83,6 +83,10 @@ namespace MediaBrowser.Providers.People
{
return null;
}
+ catch (DirectoryNotFoundException)
+ {
+ return null;
+ }
}
private RemoteImageInfo GetImageInfo(string xmlFile, string personName, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs b/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs
index 05244af747..9f0cd4ff12 100644
--- a/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs
+++ b/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs
@@ -98,6 +98,10 @@ namespace MediaBrowser.Providers.TV
{
// No biggie. Don't blow up
}
+ catch (DirectoryNotFoundException)
+ {
+ // No biggie. Don't blow up
+ }
}
}
diff --git a/MediaBrowser.Providers/TV/FanartSeriesProvider.cs b/MediaBrowser.Providers/TV/FanartSeriesProvider.cs
index afc71698bb..8ba25e9f1f 100644
--- a/MediaBrowser.Providers/TV/FanartSeriesProvider.cs
+++ b/MediaBrowser.Providers/TV/FanartSeriesProvider.cs
@@ -106,6 +106,10 @@ namespace MediaBrowser.Providers.TV
{
// No biggie. Don't blow up
}
+ catch (DirectoryNotFoundException)
+ {
+ // No biggie. Don't blow up
+ }
}
var language = item.GetPreferredMetadataLanguage();
diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
index 21d41ca00c..0b52956de5 100644
--- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
@@ -22,14 +23,16 @@ namespace MediaBrowser.Providers.TV
private readonly IServerConfigurationManager _config;
private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager;
+ private readonly ILocalizationManager _localization;
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;
_config = config;
_libraryManager = libraryManager;
+ _localization = localization;
}
public async Task Run(IEnumerable> series, CancellationToken cancellationToken)
@@ -93,16 +96,16 @@ namespace MediaBrowser.Providers.TV
var hasBadData = HasInvalidContent(group);
- var anySeasonsRemoved = await RemoveObsoleteOrMissingSeasons(group, episodeLookup, false)
+ var anySeasonsRemoved = await RemoveObsoleteOrMissingSeasons(group, episodeLookup)
.ConfigureAwait(false);
- var anyEpisodesRemoved = await RemoveObsoleteOrMissingEpisodes(group, episodeLookup, false)
+ var anyEpisodesRemoved = await RemoveObsoleteOrMissingEpisodes(group, episodeLookup)
.ConfigureAwait(false);
var hasNewEpisodes = 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);
}
@@ -165,14 +168,15 @@ namespace MediaBrowser.Providers.TV
///
private async Task AddDummySeasonFolders(Series series, CancellationToken cancellationToken)
{
- var existingEpisodes = series.RecursiveChildren
+ var episodesInSeriesFolder = series.RecursiveChildren
.OfType()
+ .Where(i => !i.IsInSeasonFolder)
.ToList();
var hasChanges = false;
// 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)
.Distinct()
.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()
+ .Any(i => !i.IndexNumber.HasValue);
+
+ if (!hasSeason)
+ {
+ await AddSeason(series, null, cancellationToken).ConfigureAwait(false);
+
+ hasChanges = true;
+ }
+ }
+
return hasChanges;
}
@@ -292,8 +310,7 @@ namespace MediaBrowser.Providers.TV
/// Removes the virtual entry after a corresponding physical version has been added
///
private async Task RemoveObsoleteOrMissingEpisodes(IEnumerable series,
- IEnumerable> episodeLookup,
- bool forceRemoveAll)
+ IEnumerable> episodeLookup)
{
var existingEpisodes = (from s in series
let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1)
@@ -312,11 +329,6 @@ namespace MediaBrowser.Providers.TV
var episodesToRemove = virtualEpisodes
.Where(i =>
{
- if (forceRemoveAll)
- {
- return true;
- }
-
if (i.Episode.IndexNumber.HasValue && i.Episode.ParentIndexNumber.HasValue)
{
var seasonNumber = i.Episode.ParentIndexNumber.Value + i.SeasonOffset;
@@ -362,11 +374,9 @@ namespace MediaBrowser.Providers.TV
///
/// The series.
/// The episode lookup.
- /// if set to true [force remove all].
/// Task{System.Boolean}.
private async Task RemoveObsoleteOrMissingSeasons(IEnumerable series,
- IEnumerable> episodeLookup,
- bool forceRemoveAll)
+ IEnumerable> episodeLookup)
{
var existingSeasons = (from s in series
let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1)
@@ -385,11 +395,6 @@ namespace MediaBrowser.Providers.TV
var seasonsToRemove = virtualSeasons
.Where(i =>
{
- if (forceRemoveAll)
- {
- return true;
- }
-
if (i.Season.IndexNumber.HasValue)
{
var seasonNumber = i.Season.IndexNumber.Value + i.SeasonOffset;
@@ -409,7 +414,9 @@ namespace MediaBrowser.Providers.TV
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().All(s => s.ParentIndexNumber.HasValue || s.IsInSeasonFolder);
})
.ToList();
@@ -472,20 +479,22 @@ namespace MediaBrowser.Providers.TV
/// The cancellation token.
/// Task{Season}.
private async Task AddSeason(Series series,
- int seasonNumber,
+ int? seasonNumber,
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
{
- Name = name,
+ Name = seasonName,
IndexNumber = seasonNumber,
Parent = series,
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);
diff --git a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs
index d350d2fe42..e1986a7c19 100644
--- a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs
+++ b/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.Library;
+using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -25,12 +26,14 @@ namespace MediaBrowser.Providers.TV
private readonly ILibraryManager _libraryManager;
private readonly IServerConfigurationManager _config;
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;
_logger = logger;
_config = config;
+ _localization = localization;
}
public Task Run(IProgress progress, CancellationToken cancellationToken)
@@ -47,7 +50,7 @@ namespace MediaBrowser.Providers.TV
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;
diff --git a/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs
index ef9f5427c4..52c1ab7dde 100644
--- a/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs
+++ b/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.
}
+ catch (DirectoryNotFoundException)
+ {
+ // Don't fail the provider because this will just keep on going and going.
+ }
}
return list;
@@ -101,6 +105,10 @@ namespace MediaBrowser.Providers.TV
{
// 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;
@@ -208,8 +216,9 @@ namespace MediaBrowser.Providers.TV
/// Fetches the episode data.
///
/// The identifier.
+ /// The identity.
/// The series data path.
- ///
+ /// The series provider ids.
/// The cancellation token.
/// Task{System.Boolean}.
private Episode FetchEpisodeData(EpisodeInfo id, EpisodeIdentity identity, string seriesDataPath, Dictionary seriesProviderIds, CancellationToken cancellationToken)
@@ -279,6 +288,10 @@ namespace MediaBrowser.Providers.TV
{
break;
}
+ catch (DirectoryNotFoundException)
+ {
+ break;
+ }
episodeNumber++;
}
diff --git a/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs
index efafeae96e..1ebd7bed55 100644
--- a/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs
@@ -94,6 +94,10 @@ namespace MediaBrowser.Providers.TV
{
// No tvdb data yet. Don't blow up
}
+ catch (DirectoryNotFoundException)
+ {
+ // No tvdb data yet. Don't blow up
+ }
}
return new RemoteImageInfo[] { };
diff --git a/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs
index 9cc09c40c9..08913d3b49 100644
--- a/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs
@@ -87,6 +87,10 @@ namespace MediaBrowser.Providers.TV
{
// No tvdb data yet. Don't blow up
}
+ catch (DirectoryNotFoundException)
+ {
+ // No tvdb data yet. Don't blow up
+ }
}
return new RemoteImageInfo[] { };
diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs
index 376dc3548b..67d8445438 100644
--- a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs
+++ b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs
@@ -1,4 +1,5 @@
using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
@@ -789,7 +790,7 @@ namespace MediaBrowser.Server.Implementations.Connect
if (user == null)
{
// 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.ConnectUserId = connectEntry.UserId;
diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs
index b141fea1ed..967c78c50f 100644
--- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs
+++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs
@@ -78,6 +78,11 @@ namespace MediaBrowser.Server.Implementations.Drawing
// No biggie
sizeDictionary = new Dictionary();
}
+ catch (DirectoryNotFoundException)
+ {
+ // No biggie
+ sizeDictionary = new Dictionary();
+ }
catch (Exception ex)
{
logger.ErrorException("Error parsing image size cache file", ex);
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
index 3b5e34520f..432ea1f696 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
+++ b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
@@ -459,11 +459,11 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
private bool IsSameEpisode(string sourcePath, string newPath)
{
- var sourceFileInfo = new FileInfo(sourcePath);
- var destinationFileInfo = new FileInfo(newPath);
-
try
{
+ var sourceFileInfo = new FileInfo(sourcePath);
+ var destinationFileInfo = new FileInfo(newPath);
+
if (sourceFileInfo.Length == destinationFileInfo.Length)
{
return true;
@@ -473,6 +473,10 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
{
return false;
}
+ catch (DirectoryNotFoundException)
+ {
+ return false;
+ }
return false;
}
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index d52288f871..66125784c1 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
@@ -1755,9 +1755,12 @@ namespace MediaBrowser.Server.Implementations.Library
var resolver = new EpisodeResolver(new ExtendedNamingOptions(),
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 fileType = /*args.IsDirectory ? FileInfoType.Directory :*/ FileInfoType.File;
var episodeInfo = locationType == LocationType.FileSystem || locationType == LocationType.Offline ?
resolver.Resolve(episode.Path, fileType) :
new Naming.TV.EpisodeInfo();
@@ -1769,29 +1772,42 @@ namespace MediaBrowser.Server.Implementations.Library
var changed = false;
- if (!episode.IndexNumber.HasValue)
+ if (episodeInfo.IsByDate)
{
- episode.IndexNumber = episodeInfo.EpisodeNumber;
-
if (episode.IndexNumber.HasValue)
{
+ episode.IndexNumber = null;
changed = true;
}
- }
-
- if (!episode.IndexNumberEnd.HasValue)
- {
- episode.IndexNumberEnd = episodeInfo.EndingEpsiodeNumber;
if (episode.IndexNumberEnd.HasValue)
{
+ episode.IndexNumberEnd = null;
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)
{
@@ -1801,11 +1817,53 @@ namespace MediaBrowser.Server.Implementations.Library
{
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;
+ }
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
index 39b0a93cc1..1a873f01e6 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
@@ -1,7 +1,5 @@
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
diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs
index 1d58ad074f..02d7c1be1a 100644
--- a/MediaBrowser.Server.Implementations/Library/UserManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs
@@ -171,6 +171,38 @@ namespace MediaBrowser.Server.Implementations.Library
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 AuthenticateUser(string username, string passwordSha1, string passwordMd5, string remoteEndPoint)
{
if (string.IsNullOrWhiteSpace(username))
@@ -178,7 +210,8 @@ namespace MediaBrowser.Server.Implementations.Library
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)
{
@@ -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
if (success)
{
@@ -273,7 +292,7 @@ namespace MediaBrowser.Server.Implementations.Library
// There always has to be at least one user.
if (users.Count == 0)
{
- var name = Environment.UserName;
+ var name = MakeValidUsername(Environment.UserName);
var user = InstantiateNewUser(name, false);
@@ -477,6 +496,11 @@ namespace MediaBrowser.Server.Implementations.Library
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)))
{
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);
}
}
+ catch (DirectoryNotFoundException)
+ {
+ return GetDefaultPolicy(user);
+ }
catch (FileNotFoundException)
{
return GetDefaultPolicy(user);
@@ -840,6 +868,8 @@ namespace MediaBrowser.Server.Implementations.Library
var path = GetPolifyFilePath(user);
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
+
lock (_policySyncLock)
{
_xmlSerializer.SerializeToFile(userPolicy, path);
@@ -900,6 +930,10 @@ namespace MediaBrowser.Server.Implementations.Library
return (UserConfiguration)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), path);
}
}
+ catch (DirectoryNotFoundException)
+ {
+ return new UserConfiguration();
+ }
catch (FileNotFoundException)
{
return new UserConfiguration();
@@ -930,6 +964,8 @@ namespace MediaBrowser.Server.Implementations.Library
config = _jsonSerializer.DeserializeFromString(json);
}
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
+
lock (_configSyncLock)
{
_xmlSerializer.SerializeToFile(config, path);
diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
index 6952002398..827a2388df 100644
--- a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
+++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
@@ -419,7 +419,7 @@
"HeaderMediaLocations": "Media Locations",
"LabelFolderTypeValue": "Folder type: {0}",
"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",
"FolderTypeMusic": "Music",
"FolderTypeAdultVideos": "Adult videos",
@@ -658,5 +658,5 @@
"LabelItemLimitHelp": "Optional. Set a limit to the number of items that will be synced.",
"MessageBookPluginRequired": "Requires installation of the Bookshelf 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"
}
diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json
index 9a515d492f..c0af139500 100644
--- a/MediaBrowser.Server.Implementations/Localization/Server/server.json
+++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json
@@ -37,7 +37,7 @@
"ButtonOk": "Ok",
"ButtonCancel": "Cancel",
"ButtonNew": "New",
- "FolderTypeMixed": "Mixed videos",
+ "FolderTypeMixed": "Mixed content",
"FolderTypeMovies": "Movies",
"FolderTypeMusic": "Music",
"FolderTypeAdultVideos": "Adult videos",
@@ -48,7 +48,7 @@
"FolderTypeBooks": "Books",
"FolderTypeTvShows": "TV",
"FolderTypeInherit": "Inherit",
- "LabelContentType": "Content type:",
+ "LabelContentType": "Content type:",
"HeaderSetupLibrary": "Setup your media library",
"ButtonAddMediaFolder": "Add media folder",
"LabelFolderType": "Folder type:",
@@ -1307,5 +1307,8 @@
"LabelEnableSingleImageInDidlLimitHelp": "Some devices will not render properly if multiple images are embedded within Didl.",
"TabActivity": "Activity",
"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 (.)"
}
diff --git a/MediaBrowser.Server.Implementations/News/NewsService.cs b/MediaBrowser.Server.Implementations/News/NewsService.cs
index 9eeadfab7b..684363d01a 100644
--- a/MediaBrowser.Server.Implementations/News/NewsService.cs
+++ b/MediaBrowser.Server.Implementations/News/NewsService.cs
@@ -26,6 +26,14 @@ namespace MediaBrowser.Server.Implementations.News
{
return GetProductNewsInternal(query);
}
+ catch (DirectoryNotFoundException)
+ {
+ // No biggie
+ return new QueryResult
+ {
+ Items = new NewsItem[] { }
+ };
+ }
catch (FileNotFoundException)
{
// No biggie
diff --git a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
index 86e92530f1..0f1d53ea63 100644
--- a/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
+++ b/MediaBrowser.XbmcMetadata/Savers/BaseNfoSaver.cs
@@ -256,6 +256,10 @@ namespace MediaBrowser.XbmcMetadata.Savers
catch (FileNotFoundException)
{
+ }
+ catch (DirectoryNotFoundException)
+ {
+
}
writer.WriteEndElement();