mirror of https://github.com/jellyfin/jellyfin.git
begin work on daily episodes
This commit is contained in:
parent
fef1d16cec
commit
42b1416602
|
@ -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))
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
{
|
|
||||||
var series = Series;
|
|
||||||
|
|
||||||
if (series != null)
|
if (IndexNumber.HasValue && series != null)
|
||||||
{
|
{
|
||||||
return series.GetEpisodes(user, IndexNumber.Value, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes);
|
return series.GetEpisodes(user, IndexNumber.Value, includeMissingEpisodes, includeVirtualUnairedEpisodes, episodes);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (series != null && series.ContainsEpisodesWithoutSeasonFolders)
|
||||||
|
{
|
||||||
|
var seasonNumber = IndexNumber;
|
||||||
|
var list = episodes.ToList();
|
||||||
|
|
||||||
|
if (seasonNumber.HasValue)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -612,6 +612,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||||
catch (FileNotFoundException)
|
catch (FileNotFoundException)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (DirectoryNotFoundException)
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (IOException ex)
|
catch (IOException ex)
|
||||||
{
|
{
|
||||||
|
|
|
@ -505,15 +505,6 @@ namespace MediaBrowser.Model.ApiClient
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task<PublicSystemInfo>.</returns>
|
/// <returns>Task<PublicSystemInfo>.</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>
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -82,6 +82,10 @@ namespace MediaBrowser.Providers.Music
|
||||||
catch (FileNotFoundException)
|
catch (FileNotFoundException)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (DirectoryNotFoundException)
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,6 +90,10 @@ namespace MediaBrowser.Providers.Music
|
||||||
catch (FileNotFoundException)
|
catch (FileNotFoundException)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (DirectoryNotFoundException)
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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,
|
IEnumerable<Tuple<int, int>> episodeLookup)
|
||||||
bool forceRemoveAll)
|
|
||||||
{
|
{
|
||||||
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,
|
IEnumerable<Tuple<int, int>> episodeLookup)
|
||||||
bool forceRemoveAll)
|
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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[] { };
|
||||||
|
|
|
@ -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[] { };
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
if (!episode.PremiereDate.HasValue)
|
||||||
{
|
{
|
||||||
episode.ParentIndexNumber = episodeInfo.SeasonNumber;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 (.)"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -256,6 +256,10 @@ namespace MediaBrowser.XbmcMetadata.Savers
|
||||||
catch (FileNotFoundException)
|
catch (FileNotFoundException)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (DirectoryNotFoundException)
|
||||||
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.WriteEndElement();
|
writer.WriteEndElement();
|
||||||
|
|
Loading…
Reference in New Issue