aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs71
-rw-r--r--MediaBrowser.Api/Playback/StreamRequest.cs8
-rw-r--r--MediaBrowser.Api/UserService.cs2
-rw-r--r--MediaBrowser.Controller/Entities/AdultVideo.cs22
-rw-r--r--MediaBrowser.Controller/Entities/AggregateFolder.cs2
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs48
-rw-r--r--MediaBrowser.Controller/Entities/CollectionFolder.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs2
-rw-r--r--MediaBrowser.Controller/Entities/IHasMetadata.cs6
-rw-r--r--MediaBrowser.Controller/Entities/Movies/Movie.cs30
-rw-r--r--MediaBrowser.Controller/Entities/Photo.cs24
-rw-r--r--MediaBrowser.Controller/Entities/TV/Episode.cs27
-rw-r--r--MediaBrowser.Controller/Entities/TV/Season.cs12
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs21
-rw-r--r--MediaBrowser.Controller/Entities/UserRootFolder.cs11
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs6
-rw-r--r--MediaBrowser.Controller/Library/ILibraryManager.cs15
-rw-r--r--MediaBrowser.Controller/Library/ItemResolveArgs.cs6
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj1
-rw-r--r--MediaBrowser.Controller/Providers/IRemoteImageProvider.cs11
-rw-r--r--MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs30
-rw-r--r--MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/GameGenres/GameGenreImageProvider.cs7
-rw-r--r--MediaBrowser.Providers/Genres/GenreImageProvider.cs7
-rw-r--r--MediaBrowser.Providers/Manager/MetadataService.cs13
-rw-r--r--MediaBrowser.Providers/Manager/ProviderManager.cs21
-rw-r--r--MediaBrowser.Providers/Manager/ProviderUtils.cs8
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj5
-rw-r--r--MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/Movies/MovieDbImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/Movies/MovieDbSearch.cs24
-rw-r--r--MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/Music/FanArtAlbumProvider.cs9
-rw-r--r--MediaBrowser.Providers/Music/FanArtArtistProvider.cs9
-rw-r--r--MediaBrowser.Providers/Music/LastFmImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/MusicGenres/MusicGenreImageProvider.cs7
-rw-r--r--MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs2
-rw-r--r--MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/People/TvdbPersonImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/Photos/ExifReader.cs613
-rw-r--r--MediaBrowser.Providers/Photos/ExifTags.cs132
-rw-r--r--MediaBrowser.Providers/Photos/PhotoHelper.cs113
-rw-r--r--MediaBrowser.Providers/Photos/PhotoMetadataService.cs32
-rw-r--r--MediaBrowser.Providers/Photos/PhotoProvider.cs137
-rw-r--r--MediaBrowser.Providers/Studios/StudiosImageProvider.cs7
-rw-r--r--MediaBrowser.Providers/TV/FanArtSeasonProvider.cs9
-rw-r--r--MediaBrowser.Providers/TV/FanartSeriesProvider.cs9
-rw-r--r--MediaBrowser.Providers/TV/MovieDbSeriesImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/TV/MovieDbSeriesProvider.cs132
-rw-r--r--MediaBrowser.Providers/TV/TvdbEpisodeImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs9
-rw-r--r--MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs9
-rw-r--r--MediaBrowser.Server.Implementations/Library/LibraryManager.cs24
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs9
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs4
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs15
-rw-r--r--MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs50
-rw-r--r--MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj1
-rw-r--r--MediaBrowser.ServerApplication/ApplicationHost.cs47
-rw-r--r--Nuget/MediaBrowser.Common.Internal.nuspec4
-rw-r--r--Nuget/MediaBrowser.Common.nuspec2
-rw-r--r--Nuget/MediaBrowser.Server.Core.nuspec4
63 files changed, 1564 insertions, 357 deletions
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index f91a35dca..fa4969878 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -1160,6 +1160,72 @@ namespace MediaBrowser.Api.Playback
}
/// <summary>
+ /// Parses the parameters.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ private void ParseParams(StreamRequest request)
+ {
+ var vals = request.Params.Split(';');
+
+ var videoRequest = request as VideoStreamRequest;
+
+ for (var i = 0; i < vals.Length; i++)
+ {
+ var val = vals[i];
+
+ if (string.IsNullOrWhiteSpace(val))
+ {
+ continue;
+ }
+
+ if (i == 0)
+ {
+ request.DeviceId = val;
+ }
+ else if (i == 1)
+ {
+ if (videoRequest != null)
+ {
+ videoRequest.VideoCodec = (VideoCodecs)Enum.Parse(typeof(VideoCodecs), val, true);
+ }
+ }
+ else if (i == 2)
+ {
+ request.AudioCodec = (AudioCodecs)Enum.Parse(typeof(AudioCodecs), val, true);
+ }
+ else if (i == 3)
+ {
+ if (videoRequest != null)
+ {
+ videoRequest.AudioStreamIndex = int.Parse(val, UsCulture);
+ }
+ }
+ else if (i == 4)
+ {
+ if (videoRequest != null)
+ {
+ videoRequest.SubtitleStreamIndex = int.Parse(val, UsCulture);
+ }
+ }
+ else if (i == 5)
+ {
+ if (videoRequest != null)
+ {
+ videoRequest.VideoBitRate = int.Parse(val, UsCulture);
+ }
+ }
+ else if (i == 6)
+ {
+ request.AudioBitRate = int.Parse(val, UsCulture);
+ }
+ else if (i == 7)
+ {
+ request.AudioChannels = int.Parse(val, UsCulture);
+ }
+ }
+ }
+
+ /// <summary>
/// Gets the state.
/// </summary>
/// <param name="request">The request.</param>
@@ -1167,6 +1233,11 @@ namespace MediaBrowser.Api.Playback
/// <returns>StreamState.</returns>
protected async Task<StreamState> GetState(StreamRequest request, CancellationToken cancellationToken)
{
+ if (!string.IsNullOrWhiteSpace(request.Params))
+ {
+ ParseParams(request);
+ }
+
if (request.ThrowDebugError)
{
throw new InvalidOperationException("You asked for a debug error, you got one.");
diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs
index 78682d54a..a73a8f0d9 100644
--- a/MediaBrowser.Api/Playback/StreamRequest.cs
+++ b/MediaBrowser.Api/Playback/StreamRequest.cs
@@ -61,15 +61,11 @@ namespace MediaBrowser.Api.Playback
public bool Static { get; set; }
/// <summary>
- /// This is an xbox 360 param that is used with dlna. If true the item's image should be returned instead of audio or video.
- /// No need to put this in api docs since it's dlna only
- /// </summary>
- public bool AlbumArt { get; set; }
-
- /// <summary>
/// For testing purposes
/// </summary>
public bool ThrowDebugError { get; set; }
+
+ public string Params { get; set; }
}
public class VideoStreamRequest : StreamRequest
diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs
index 9ddefdd05..8e94d2c83 100644
--- a/MediaBrowser.Api/UserService.cs
+++ b/MediaBrowser.Api/UserService.cs
@@ -189,7 +189,7 @@ namespace MediaBrowser.Api
{
throw new ArgumentNullException("xmlSerializer");
}
-
+
_xmlSerializer = xmlSerializer;
_userManager = userManager;
_dtoService = dtoService;
diff --git a/MediaBrowser.Controller/Entities/AdultVideo.cs b/MediaBrowser.Controller/Entities/AdultVideo.cs
index fc7632152..9791f7cf7 100644
--- a/MediaBrowser.Controller/Entities/AdultVideo.cs
+++ b/MediaBrowser.Controller/Entities/AdultVideo.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using MediaBrowser.Controller.Providers;
namespace MediaBrowser.Controller.Entities
{
@@ -22,5 +23,26 @@ namespace MediaBrowser.Controller.Entities
{
Taglines = new List<string>();
}
+
+ public override bool BeforeMetadataRefresh()
+ {
+ var hasChanges = base.BeforeMetadataRefresh();
+
+ if (!ProductionYear.HasValue)
+ {
+ int? yearInName = null;
+ string name;
+
+ NameParser.ParseName(Name, out name, out yearInName);
+
+ if (yearInName.HasValue)
+ {
+ ProductionYear = yearInName;
+ hasChanges = true;
+ }
+ }
+
+ return hasChanges;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs
index 5cabe1cfe..362096b5e 100644
--- a/MediaBrowser.Controller/Entities/AggregateFolder.cs
+++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs
@@ -66,7 +66,7 @@ namespace MediaBrowser.Controller.Entities
{
var path = ContainingFolderPath;
- var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, LibraryManager)
+ var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, LibraryManager, directoryService)
{
FileInfo = new DirectoryInfo(path),
Path = path,
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 8dcf08642..415b49f80 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -472,7 +472,7 @@ namespace MediaBrowser.Controller.Entities
/// Loads local trailers from the file system
/// </summary>
/// <returns>List{Video}.</returns>
- private IEnumerable<Trailer> LoadLocalTrailers(List<FileSystemInfo> fileSystemChildren)
+ private IEnumerable<Trailer> LoadLocalTrailers(List<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService)
{
var files = fileSystemChildren.OfType<DirectoryInfo>()
.Where(i => string.Equals(i.Name, TrailerFolderName, StringComparison.OrdinalIgnoreCase))
@@ -484,7 +484,7 @@ namespace MediaBrowser.Controller.Entities
.Where(i => System.IO.Path.GetFileNameWithoutExtension(i.Name).EndsWith(XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase) && !string.Equals(Path, i.FullName, StringComparison.OrdinalIgnoreCase))
);
- return LibraryManager.ResolvePaths<Trailer>(files, null).Select(video =>
+ return LibraryManager.ResolvePaths<Trailer>(files, directoryService, null).Select(video =>
{
// Try to retrieve it from the db. If we don't find it, use the resolved version
var dbItem = LibraryManager.GetItemById(video.Id) as Trailer;
@@ -504,7 +504,7 @@ namespace MediaBrowser.Controller.Entities
/// Loads the theme songs.
/// </summary>
/// <returns>List{Audio.Audio}.</returns>
- private IEnumerable<Audio.Audio> LoadThemeSongs(List<FileSystemInfo> fileSystemChildren)
+ private IEnumerable<Audio.Audio> LoadThemeSongs(List<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService)
{
var files = fileSystemChildren.OfType<DirectoryInfo>()
.Where(i => string.Equals(i.Name, ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
@@ -516,7 +516,7 @@ namespace MediaBrowser.Controller.Entities
.Where(i => string.Equals(System.IO.Path.GetFileNameWithoutExtension(i.Name), ThemeSongFilename, StringComparison.OrdinalIgnoreCase))
);
- return LibraryManager.ResolvePaths<Audio.Audio>(files, null).Select(audio =>
+ return LibraryManager.ResolvePaths<Audio.Audio>(files, directoryService, null).Select(audio =>
{
// Try to retrieve it from the db. If we don't find it, use the resolved version
var dbItem = LibraryManager.GetItemById(audio.Id) as Audio.Audio;
@@ -536,13 +536,13 @@ namespace MediaBrowser.Controller.Entities
/// Loads the video backdrops.
/// </summary>
/// <returns>List{Video}.</returns>
- private IEnumerable<Video> LoadThemeVideos(IEnumerable<FileSystemInfo> fileSystemChildren)
+ private IEnumerable<Video> LoadThemeVideos(IEnumerable<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService)
{
var files = fileSystemChildren.OfType<DirectoryInfo>()
.Where(i => string.Equals(i.Name, ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
.SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly));
- return LibraryManager.ResolvePaths<Video>(files, null).Select(item =>
+ return LibraryManager.ResolvePaths<Video>(files, directoryService, null).Select(item =>
{
// Try to retrieve it from the db. If we don't find it, use the resolved version
var dbItem = LibraryManager.GetItemById(item.Id) as Video;
@@ -579,15 +579,22 @@ namespace MediaBrowser.Controller.Entities
{
options.DirectoryService = options.DirectoryService ?? new DirectoryService(Logger);
- var files = locationType == LocationType.FileSystem || locationType == LocationType.Offline ?
- GetFileSystemChildren(options.DirectoryService).ToList() :
- new List<FileSystemInfo>();
+ try
+ {
+ var files = locationType == LocationType.FileSystem || locationType == LocationType.Offline ?
+ GetFileSystemChildren(options.DirectoryService).ToList() :
+ new List<FileSystemInfo>();
- var ownedItemsChanged = await RefreshedOwnedItems(options, files, cancellationToken).ConfigureAwait(false);
+ var ownedItemsChanged = await RefreshedOwnedItems(options, files, cancellationToken).ConfigureAwait(false);
- if (ownedItemsChanged)
+ if (ownedItemsChanged)
+ {
+ requiresSave = true;
+ }
+ }
+ catch (Exception ex)
{
- requiresSave = true;
+ Logger.ErrorException("Error refreshing owned items for {0}", ex, Path ?? Name);
}
}
@@ -650,7 +657,7 @@ namespace MediaBrowser.Controller.Entities
private async Task<bool> RefreshLocalTrailers(IHasTrailers item, MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
- var newItems = LoadLocalTrailers(fileSystemChildren).ToList();
+ var newItems = LoadLocalTrailers(fileSystemChildren, options.DirectoryService).ToList();
var newItemIds = newItems.Select(i => i.Id).ToList();
var itemsChanged = !item.LocalTrailerIds.SequenceEqual(newItemIds);
@@ -666,7 +673,7 @@ namespace MediaBrowser.Controller.Entities
private async Task<bool> RefreshThemeVideos(IHasThemeMedia item, MetadataRefreshOptions options, IEnumerable<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
- var newThemeVideos = LoadThemeVideos(fileSystemChildren).ToList();
+ var newThemeVideos = LoadThemeVideos(fileSystemChildren, options.DirectoryService).ToList();
var newThemeVideoIds = newThemeVideos.Select(i => i.Id).ToList();
@@ -686,7 +693,7 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
private async Task<bool> RefreshThemeSongs(IHasThemeMedia item, MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
- var newThemeSongs = LoadThemeSongs(fileSystemChildren).ToList();
+ var newThemeSongs = LoadThemeSongs(fileSystemChildren, options.DirectoryService).ToList();
var newThemeSongIds = newThemeSongs.Select(i => i.Id).ToList();
var themeSongsChanged = !item.ThemeSongIds.SequenceEqual(newThemeSongIds);
@@ -1422,20 +1429,19 @@ namespace MediaBrowser.Controller.Entities
}
/// <summary>
- /// This is called before any metadata refresh and returns ItemUpdateType indictating if changes were made, and what.
+ /// This is called before any metadata refresh and returns true or false indicating if changes were made
/// </summary>
- /// <returns>ItemUpdateType.</returns>
- public virtual ItemUpdateType BeforeMetadataRefresh()
+ public virtual bool BeforeMetadataRefresh()
{
- var updateType = ItemUpdateType.None;
+ var hasChanges = false;
if (string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(Path))
{
Name = System.IO.Path.GetFileNameWithoutExtension(Path);
- updateType = updateType | ItemUpdateType.MetadataEdit;
+ hasChanges = true;
}
- return updateType;
+ return hasChanges;
}
}
}
diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
index 416796b69..88219bcbf 100644
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs
@@ -70,7 +70,7 @@ namespace MediaBrowser.Controller.Entities
{
var path = ContainingFolderPath;
- var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, LibraryManager)
+ var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, LibraryManager, directoryService)
{
FileInfo = new DirectoryInfo(path),
Path = path,
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index cb14ed099..b718864d2 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -681,7 +681,7 @@ namespace MediaBrowser.Controller.Entities
/// <returns>IEnumerable{BaseItem}.</returns>
protected virtual IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
{
- return LibraryManager.ResolvePaths<BaseItem>(GetFileSystemChildren(directoryService), this);
+ return LibraryManager.ResolvePaths<BaseItem>(GetFileSystemChildren(directoryService), directoryService, this);
}
/// <summary>
diff --git a/MediaBrowser.Controller/Entities/IHasMetadata.cs b/MediaBrowser.Controller/Entities/IHasMetadata.cs
index 0285b6749..7182d086a 100644
--- a/MediaBrowser.Controller/Entities/IHasMetadata.cs
+++ b/MediaBrowser.Controller/Entities/IHasMetadata.cs
@@ -51,9 +51,9 @@ namespace MediaBrowser.Controller.Entities
Task UpdateToRepository(ItemUpdateType updateReason, CancellationToken cancellationToken);
/// <summary>
- /// This is called before any metadata refresh and returns ItemUpdateType indictating if changes were made, and what.
+ /// This is called before any metadata refresh and returns true or false indicating if changes were made
/// </summary>
- /// <returns>ItemUpdateType.</returns>
- ItemUpdateType BeforeMetadataRefresh();
+ /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
+ bool BeforeMetadataRefresh();
}
}
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs
index 8eba21df0..846a2ae7b 100644
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Providers;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using System;
@@ -117,7 +118,7 @@ namespace MediaBrowser.Controller.Entities.Movies
private async Task<bool> RefreshSpecialFeatures(MetadataRefreshOptions options, IEnumerable<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
- var newItems = LoadSpecialFeatures(fileSystemChildren).ToList();
+ var newItems = LoadSpecialFeatures(fileSystemChildren, options.DirectoryService).ToList();
var newItemIds = newItems.Select(i => i.Id).ToList();
var itemsChanged = !SpecialFeatureIds.SequenceEqual(newItemIds);
@@ -135,13 +136,13 @@ namespace MediaBrowser.Controller.Entities.Movies
/// Loads the special features.
/// </summary>
/// <returns>IEnumerable{Video}.</returns>
- private IEnumerable<Video> LoadSpecialFeatures(IEnumerable<FileSystemInfo> fileSystemChildren)
+ private IEnumerable<Video> LoadSpecialFeatures(IEnumerable<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService)
{
var files = fileSystemChildren.OfType<DirectoryInfo>()
.Where(i => string.Equals(i.Name, "extras", StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, "specials", StringComparison.OrdinalIgnoreCase))
.SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly));
- return LibraryManager.ResolvePaths<Video>(files, null).Select(video =>
+ return LibraryManager.ResolvePaths<Video>(files, directoryService, null).Select(video =>
{
// Try to retrieve it from the db. If we don't find it, use the resolved version
var dbItem = LibraryManager.GetItemById(video.Id) as Video;
@@ -166,5 +167,26 @@ namespace MediaBrowser.Controller.Entities.Movies
{
return GetItemLookupInfo<MovieInfo>();
}
+
+ public override bool BeforeMetadataRefresh()
+ {
+ var hasChanges = base.BeforeMetadataRefresh();
+
+ if (!ProductionYear.HasValue)
+ {
+ int? yearInName = null;
+ string name;
+
+ NameParser.ParseName(Name, out name, out yearInName);
+
+ if (yearInName.HasValue)
+ {
+ ProductionYear = yearInName;
+ hasChanges = true;
+ }
+ }
+
+ return hasChanges;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs
new file mode 100644
index 000000000..96995c315
--- /dev/null
+++ b/MediaBrowser.Controller/Entities/Photo.cs
@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+
+namespace MediaBrowser.Controller.Entities
+{
+ public class Photo : BaseItem, IHasTags, IHasTaglines
+ {
+ public List<string> Tags { get; set; }
+ public List<string> Taglines { get; set; }
+
+ public Photo()
+ {
+ Tags = new List<string>();
+ Taglines = new List<string>();
+ }
+
+ public override string MediaType
+ {
+ get
+ {
+ return Model.Entities.MediaType.Photo;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index daff3dd6c..1240bbb9f 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -263,32 +263,32 @@ namespace MediaBrowser.Controller.Entities.TV
return id;
}
- public override ItemUpdateType BeforeMetadataRefresh()
+ public override bool BeforeMetadataRefresh()
{
- var updateType = base.BeforeMetadataRefresh();
+ var hasChanges = base.BeforeMetadataRefresh();
var locationType = LocationType;
if (locationType == LocationType.FileSystem || locationType == LocationType.Offline)
{
if (!IndexNumber.HasValue && !string.IsNullOrEmpty(Path))
{
- IndexNumber = IndexNumber ?? TVUtils.GetEpisodeNumberFromFile(Path, Parent is Season);
+ IndexNumber = TVUtils.GetEpisodeNumberFromFile(Path, Parent is Season);
// If a change was made record it
if (IndexNumber.HasValue)
{
- updateType = updateType | ItemUpdateType.MetadataImport;
+ hasChanges = true;
}
}
if (!IndexNumberEnd.HasValue && !string.IsNullOrEmpty(Path))
{
- IndexNumberEnd = IndexNumberEnd ?? TVUtils.GetEndingEpisodeNumberFromFile(Path);
+ IndexNumberEnd = TVUtils.GetEndingEpisodeNumberFromFile(Path);
// If a change was made record it
if (IndexNumberEnd.HasValue)
{
- updateType = updateType | ItemUpdateType.MetadataImport;
+ hasChanges = true;
}
}
}
@@ -302,14 +302,25 @@ namespace MediaBrowser.Controller.Entities.TV
ParentIndexNumber = season.IndexNumber;
}
+ if (!ParentIndexNumber.HasValue && !string.IsNullOrEmpty(Path))
+ {
+ ParentIndexNumber = TVUtils.GetSeasonNumberFromPath(Path);
+
+ // If a change was made record it
+ if (ParentIndexNumber.HasValue)
+ {
+ hasChanges = true;
+ }
+ }
+
// If a change was made record it
if (ParentIndexNumber.HasValue)
{
- updateType = updateType | ItemUpdateType.MetadataImport;
+ hasChanges = true;
}
}
- return updateType;
+ return hasChanges;
}
}
}
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index 830ccb8a2..2847c397e 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -254,12 +254,12 @@ namespace MediaBrowser.Controller.Entities.TV
}
/// <summary>
- /// This is called before any metadata refresh and returns ItemUpdateType indictating if changes were made, and what.
+ /// This is called before any metadata refresh and returns true or false indicating if changes were made
/// </summary>
- /// <returns>ItemUpdateType.</returns>
- public override ItemUpdateType BeforeMetadataRefresh()
+ /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
+ public override bool BeforeMetadataRefresh()
{
- var updateType = base.BeforeMetadataRefresh();
+ var hasChanges = base.BeforeMetadataRefresh();
var locationType = LocationType;
@@ -272,12 +272,12 @@ namespace MediaBrowser.Controller.Entities.TV
// If a change was made record it
if (IndexNumber.HasValue)
{
- updateType = updateType | ItemUpdateType.MetadataImport;
+ hasChanges = true;
}
}
}
- return updateType;
+ return hasChanges;
}
}
}
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index 0e07654d6..4696c8a0f 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -228,5 +228,26 @@ namespace MediaBrowser.Controller.Entities.TV
{
return GetItemLookupInfo<SeriesInfo>();
}
+
+ public override bool BeforeMetadataRefresh()
+ {
+ var hasChanges = base.BeforeMetadataRefresh();
+
+ if (!ProductionYear.HasValue)
+ {
+ int? yearInName = null;
+ string name;
+
+ NameParser.ParseName(Name, out name, out yearInName);
+
+ if (yearInName.HasValue)
+ {
+ ProductionYear = yearInName;
+ hasChanges = true;
+ }
+ }
+
+ return hasChanges;
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs
index dc3d4c384..9a8d3e7f4 100644
--- a/MediaBrowser.Controller/Entities/UserRootFolder.cs
+++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs
@@ -1,5 +1,4 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Providers;
+using MediaBrowser.Controller.Providers;
using System.Collections.Generic;
using System.Linq;
@@ -20,17 +19,17 @@ namespace MediaBrowser.Controller.Entities
return base.GetNonCachedChildren(directoryService).Concat(LibraryManager.RootFolder.VirtualChildren);
}
- public override ItemUpdateType BeforeMetadataRefresh()
+ public override bool BeforeMetadataRefresh()
{
- var updateType = base.BeforeMetadataRefresh();
+ var hasChanges = base.BeforeMetadataRefresh();
if (string.Equals("default", Name, System.StringComparison.OrdinalIgnoreCase))
{
Name = "Default Media Library";
- updateType = updateType | ItemUpdateType.MetadataEdit;
+ hasChanges = true;
}
- return updateType;
+ return hasChanges;
}
}
}
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index e778b38bd..e16683d6d 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -192,7 +192,7 @@ namespace MediaBrowser.Controller.Entities
/// <returns>Task{System.Boolean}.</returns>
private async Task<bool> RefreshAdditionalParts(MetadataRefreshOptions options, IEnumerable<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken)
{
- var newItems = LoadAdditionalParts(fileSystemChildren).ToList();
+ var newItems = LoadAdditionalParts(fileSystemChildren, options.DirectoryService).ToList();
var newItemIds = newItems.Select(i => i.Id).ToList();
@@ -211,7 +211,7 @@ namespace MediaBrowser.Controller.Entities
/// Loads the additional parts.
/// </summary>
/// <returns>IEnumerable{Video}.</returns>
- private IEnumerable<Video> LoadAdditionalParts(IEnumerable<FileSystemInfo> fileSystemChildren)
+ private IEnumerable<Video> LoadAdditionalParts(IEnumerable<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService)
{
IEnumerable<FileSystemInfo> files;
@@ -242,7 +242,7 @@ namespace MediaBrowser.Controller.Entities
});
}
- return LibraryManager.ResolvePaths<Video>(files, null).Select(video =>
+ return LibraryManager.ResolvePaths<Video>(files, directoryService, null).Select(video =>
{
// Try to retrieve it from the db. If we don't find it, use the resolved version
var dbItem = LibraryManager.GetItemById(video.Id) as Video;
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 7c803e651..e5e7db13d 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Entities;
@@ -27,19 +28,29 @@ namespace MediaBrowser.Controller.Library
/// Resolves a path into a BaseItem
/// </summary>
/// <param name="fileInfo">The file info.</param>
+ /// <param name="directoryService">The directory service.</param>
/// <param name="parent">The parent.</param>
/// <returns>BaseItem.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
- BaseItem ResolvePath(FileSystemInfo fileInfo, Folder parent = null);
+ BaseItem ResolvePath(FileSystemInfo fileInfo, IDirectoryService directoryService, Folder parent = null);
/// <summary>
+ /// Resolves the path.
+ /// </summary>
+ /// <param name="fileInfo">The file information.</param>
+ /// <param name="parent">The parent.</param>
+ /// <returns>BaseItem.</returns>
+ BaseItem ResolvePath(FileSystemInfo fileInfo, Folder parent = null);
+
+ /// <summary>
/// Resolves a set of files into a list of BaseItem
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="files">The files.</param>
+ /// <param name="directoryService">The directory service.</param>
/// <param name="parent">The parent.</param>
/// <returns>List{``0}.</returns>
- List<T> ResolvePaths<T>(IEnumerable<FileSystemInfo> files, Folder parent)
+ List<T> ResolvePaths<T>(IEnumerable<FileSystemInfo> files, IDirectoryService directoryService, Folder parent)
where T : BaseItem;
/// <summary>
diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
index d84e7aa8c..c1fcdc9fe 100644
--- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs
+++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs
@@ -1,4 +1,5 @@
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
using System;
using System.Collections.Generic;
using System.IO;
@@ -18,15 +19,18 @@ namespace MediaBrowser.Controller.Library
private readonly IServerApplicationPaths _appPaths;
private readonly ILibraryManager _libraryManager;
+ public IDirectoryService DirectoryService { get; private set; }
+
/// <summary>
/// Initializes a new instance of the <see cref="ItemResolveArgs" /> class.
/// </summary>
/// <param name="appPaths">The app paths.</param>
/// <param name="libraryManager">The library manager.</param>
- public ItemResolveArgs(IServerApplicationPaths appPaths, ILibraryManager libraryManager)
+ public ItemResolveArgs(IServerApplicationPaths appPaths, ILibraryManager libraryManager, IDirectoryService directoryService)
{
_appPaths = appPaths;
_libraryManager = libraryManager;
+ DirectoryService = directoryService;
}
/// <summary>
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 18ac01c8b..b62492bfe 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -109,6 +109,7 @@
<Compile Include="Entities\LinkedChild.cs" />
<Compile Include="Entities\MusicVideo.cs" />
<Compile Include="Entities\IHasAwards.cs" />
+ <Compile Include="Entities\Photo.cs" />
<Compile Include="FileOrganization\IFileOrganizationService.cs" />
<Compile Include="Library\ILibraryPostScanTask.cs" />
<Compile Include="Library\IMetadataSaver.cs" />
diff --git a/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs b/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs
index 23fda2bfa..6b94547bb 100644
--- a/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs
+++ b/MediaBrowser.Controller/Providers/IRemoteImageProvider.cs
@@ -24,18 +24,9 @@ namespace MediaBrowser.Controller.Providers
/// Gets the images.
/// </summary>
/// <param name="item">The item.</param>
- /// <param name="imageType">Type of the image.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
- Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken);
-
- /// <summary>
- /// Gets the images.
- /// </summary>
- /// <param name="item">The item.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
- Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken);
+ Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken);
/// <summary>
/// Gets the image response.
diff --git a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs
index 079571ee9..42b637140 100644
--- a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs
+++ b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs
@@ -77,21 +77,21 @@ namespace MediaBrowser.Controller.Resolvers
/// <summary>
/// The audio file extensions
/// </summary>
- public static readonly string[] AudioFileExtensions = new[]
- {
- ".mp3",
- ".flac",
- ".wma",
- ".aac",
- ".acc",
- ".m4a",
- ".m4b",
- ".wav",
- ".ape",
- ".ogg",
- ".oga"
-
- };
+ public static readonly string[] AudioFileExtensions =
+ {
+ ".mp3",
+ ".flac",
+ ".wma",
+ ".aac",
+ ".acc",
+ ".m4a",
+ ".m4b",
+ ".wav",
+ ".ape",
+ ".ogg",
+ ".oga"
+
+ };
private static readonly Dictionary<string, string> AudioFileExtensionsDictionary = AudioFileExtensions.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs
index b7672a6a7..e82c6a1b9 100644
--- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs
+++ b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs
@@ -47,14 +47,7 @@ namespace MediaBrowser.Providers.BoxSets
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
-
- return images.Where(i => i.Type == imageType);
- }
-
- public async Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
var tmdbId = item.GetProviderId(MetadataProviders.Tmdb);
diff --git a/MediaBrowser.Providers/GameGenres/GameGenreImageProvider.cs b/MediaBrowser.Providers/GameGenres/GameGenreImageProvider.cs
index 85aa9f716..12f14676f 100644
--- a/MediaBrowser.Providers/GameGenres/GameGenreImageProvider.cs
+++ b/MediaBrowser.Providers/GameGenres/GameGenreImageProvider.cs
@@ -54,12 +54,7 @@ namespace MediaBrowser.Providers.GameGenres
};
}
- public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- return GetImages(item, imageType == ImageType.Primary, imageType == ImageType.Thumb, cancellationToken);
- }
-
- public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
return GetImages(item, true, true, cancellationToken);
}
diff --git a/MediaBrowser.Providers/Genres/GenreImageProvider.cs b/MediaBrowser.Providers/Genres/GenreImageProvider.cs
index 007eeab7b..4d19bb91e 100644
--- a/MediaBrowser.Providers/Genres/GenreImageProvider.cs
+++ b/MediaBrowser.Providers/Genres/GenreImageProvider.cs
@@ -55,12 +55,7 @@ namespace MediaBrowser.Providers.Genres
};
}
- public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- return GetImages(item, imageType == ImageType.Primary, imageType == ImageType.Thumb, cancellationToken);
- }
-
- public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
return GetImages(item, true, true, cancellationToken);
}
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs
index beece997d..8b085a05a 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -106,7 +106,10 @@ namespace MediaBrowser.Providers.Manager
if (providers.Count > 0 || !refreshResult.DateLastMetadataRefresh.HasValue)
{
- updateType = updateType | item.BeforeMetadataRefresh();
+ if (item.BeforeMetadataRefresh())
+ {
+ updateType = updateType | ItemUpdateType.MetadataImport;
+ }
}
if (providers.Count > 0)
@@ -416,7 +419,13 @@ namespace MediaBrowser.Providers.Manager
// Copy new provider id's that may have been obtained
foreach (var providerId in source.ProviderIds)
{
- lookupInfo.ProviderIds[providerId.Key] = providerId.Value;
+ var key = providerId.Key;
+
+ // Don't replace existing Id's.
+ if (!lookupInfo.ProviderIds.ContainsKey(key))
+ {
+ lookupInfo.ProviderIds[key] = providerId.Value;
+ }
}
}
diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs
index 44abeac43..43d4f20c1 100644
--- a/MediaBrowser.Providers/Manager/ProviderManager.cs
+++ b/MediaBrowser.Providers/Manager/ProviderManager.cs
@@ -237,32 +237,27 @@ namespace MediaBrowser.Providers.Manager
/// </summary>
/// <param name="item">The item.</param>
/// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="i">The i.</param>
+ /// <param name="provider">The provider.</param>
/// <param name="preferredLanguage">The preferred language.</param>
/// <param name="type">The type.</param>
/// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
- private async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken, IRemoteImageProvider i, string preferredLanguage, ImageType? type = null)
+ private async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken, IRemoteImageProvider provider, string preferredLanguage, ImageType? type = null)
{
try
{
+ var result = await provider.GetImages(item, cancellationToken).ConfigureAwait(false);
+
if (type.HasValue)
{
- var result = await i.GetImages(item, type.Value, cancellationToken).ConfigureAwait(false);
-
- return string.IsNullOrEmpty(preferredLanguage) ? result :
- FilterImages(result, preferredLanguage);
+ result = result.Where(i => i.Type == type.Value);
}
- else
- {
- var result = await i.GetAllImages(item, cancellationToken).ConfigureAwait(false);
- return string.IsNullOrEmpty(preferredLanguage) ? result :
- FilterImages(result, preferredLanguage);
- }
+ return string.IsNullOrEmpty(preferredLanguage) ? result :
+ FilterImages(result, preferredLanguage);
}
catch (Exception ex)
{
- _logger.ErrorException("{0} failed in GetImageInfos for type {1}", ex, i.GetType().Name, item.GetType().Name);
+ _logger.ErrorException("{0} failed in GetImageInfos for type {1}", ex, provider.GetType().Name, item.GetType().Name);
return new List<RemoteImageInfo>();
}
}
diff --git a/MediaBrowser.Providers/Manager/ProviderUtils.cs b/MediaBrowser.Providers/Manager/ProviderUtils.cs
index 6e994c9f2..025945461 100644
--- a/MediaBrowser.Providers/Manager/ProviderUtils.cs
+++ b/MediaBrowser.Providers/Manager/ProviderUtils.cs
@@ -157,7 +157,13 @@ namespace MediaBrowser.Providers.Manager
foreach (var id in source.ProviderIds)
{
- target.ProviderIds[id.Key] = id.Value;
+ var key = id.Key;
+
+ // Don't replace existing Id's.
+ if (!target.ProviderIds.ContainsKey(key))
+ {
+ target.ProviderIds[key] = id.Value;
+ }
}
MergeAlbumArtist(source, target, lockedFields, replaceData);
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 5cf4b2591..85f988344 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -151,6 +151,11 @@
<Compile Include="People\PersonMetadataService.cs" />
<Compile Include="People\PersonXmlProvider.cs" />
<Compile Include="People\MovieDbPersonProvider.cs" />
+ <Compile Include="Photos\ExifReader.cs" />
+ <Compile Include="Photos\ExifTags.cs" />
+ <Compile Include="Photos\PhotoHelper.cs" />
+ <Compile Include="Photos\PhotoMetadataService.cs" />
+ <Compile Include="Photos\PhotoProvider.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Manager\ProviderUtils.cs" />
<Compile Include="Savers\AlbumXmlSaver.cs" />
diff --git a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
index 26395e9cc..b11569495 100644
--- a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
+++ b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs
@@ -78,14 +78,7 @@ namespace MediaBrowser.Providers.Movies
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
-
- return images.Where(i => i.Type == imageType);
- }
-
- public async Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
var baseItem = (BaseItem)item;
var list = new List<RemoteImageInfo>();
diff --git a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs
index 8a38e294c..9828a20bc 100644
--- a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs
+++ b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs
@@ -58,14 +58,7 @@ namespace MediaBrowser.Providers.Movies
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
-
- return images.Where(i => i.Type == imageType);
- }
-
- public async Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
diff --git a/MediaBrowser.Providers/Movies/MovieDbSearch.cs b/MediaBrowser.Providers/Movies/MovieDbSearch.cs
index 5a2b865f5..5f5ece4a8 100644
--- a/MediaBrowser.Providers/Movies/MovieDbSearch.cs
+++ b/MediaBrowser.Providers/Movies/MovieDbSearch.cs
@@ -46,11 +46,8 @@ namespace MediaBrowser.Providers.Movies
private async Task<string> FindId(ItemLookupInfo idInfo, string searchType, CancellationToken cancellationToken)
{
- int? yearInName;
var name = idInfo.Name;
- NameParser.ParseName(name, out name, out yearInName);
-
- var year = idInfo.Year ?? yearInName;
+ var year = idInfo.Year;
_logger.Info("MovieDbProvider: Finding id for item: " + name);
var language = idInfo.MetadataLanguage.ToLower();
@@ -266,5 +263,24 @@ namespace MediaBrowser.Providers.Movies
public int total_results { get; set; }
}
+ public class TvResult
+ {
+ public string backdrop_path { get; set; }
+ public int id { get; set; }
+ public string original_name { get; set; }
+ public string first_air_date { get; set; }
+ public string poster_path { get; set; }
+ public double popularity { get; set; }
+ public string name { get; set; }
+ public double vote_average { get; set; }
+ public int vote_count { get; set; }
+ }
+
+ public class ExternalIdLookupResult
+ {
+ public List<object> movie_results { get; set; }
+ public List<object> person_results { get; set; }
+ public List<TvResult> tv_results { get; set; }
+ }
}
}
diff --git a/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs b/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs
index a8f3b8a6d..3de85593a 100644
--- a/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs
+++ b/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs
@@ -35,14 +35,7 @@ namespace MediaBrowser.Providers.Music
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
-
- return images.Where(i => i.Type == imageType);
- }
-
- public async Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
var id = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
diff --git a/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs b/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs
index 79238fd51..10a197b37 100644
--- a/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs
+++ b/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs
@@ -37,14 +37,7 @@ namespace MediaBrowser.Providers.Music
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
-
- return images.Where(i => i.Type == imageType);
- }
-
- public async Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
var id = item.GetProviderId(MetadataProviders.MusicBrainzArtist);
diff --git a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
index 8479b0c6e..93db4f5b4 100644
--- a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
+++ b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs
@@ -57,14 +57,7 @@ namespace MediaBrowser.Providers.Music
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
-
- return images.Where(i => i.Type == imageType);
- }
-
- public async Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
var album = (MusicAlbum)item;
diff --git a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
index 9b32ff4f2..d96178059 100644
--- a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
+++ b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs
@@ -69,14 +69,7 @@ namespace MediaBrowser.Providers.Music
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
-
- return images.Where(i => i.Type == imageType);
- }
-
- public async Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
var artist = (MusicArtist)item;
diff --git a/MediaBrowser.Providers/Music/LastFmImageProvider.cs b/MediaBrowser.Providers/Music/LastFmImageProvider.cs
index 701e74da0..b55a97351 100644
--- a/MediaBrowser.Providers/Music/LastFmImageProvider.cs
+++ b/MediaBrowser.Providers/Music/LastFmImageProvider.cs
@@ -48,14 +48,7 @@ namespace MediaBrowser.Providers.Music
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
-
- return images.Where(i => i.Type == imageType);
- }
-
- public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
diff --git a/MediaBrowser.Providers/MusicGenres/MusicGenreImageProvider.cs b/MediaBrowser.Providers/MusicGenres/MusicGenreImageProvider.cs
index 0867156e3..6c239ab30 100644
--- a/MediaBrowser.Providers/MusicGenres/MusicGenreImageProvider.cs
+++ b/MediaBrowser.Providers/MusicGenres/MusicGenreImageProvider.cs
@@ -55,12 +55,7 @@ namespace MediaBrowser.Providers.MusicGenres
};
}
- public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- return GetImages(item, imageType == ImageType.Primary, imageType == ImageType.Thumb, cancellationToken);
- }
-
- public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
return GetImages(item, true, true, cancellationToken);
}
diff --git a/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs b/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs
index db5f1b8ce..2e85e5401 100644
--- a/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs
+++ b/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs
@@ -7,8 +7,6 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Manager;
using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
namespace MediaBrowser.Providers.MusicGenres
{
diff --git a/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs b/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs
index 3f8278bda..2ac9fdfa0 100644
--- a/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs
+++ b/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs
@@ -50,14 +50,7 @@ namespace MediaBrowser.Providers.People
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
-
- return images.Where(i => i.Type == imageType);
- }
-
- public async Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
var person = (Person)item;
var id = person.GetProviderId(MetadataProviders.Tmdb);
diff --git a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs
index 769f07788..63d054664 100644
--- a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs
+++ b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs
@@ -54,14 +54,7 @@ namespace MediaBrowser.Providers.People
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
-
- return images.Where(i => i.Type == imageType);
- }
-
- public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
var seriesWithPerson = _library.RootFolder
.RecursiveChildren
diff --git a/MediaBrowser.Providers/Photos/ExifReader.cs b/MediaBrowser.Providers/Photos/ExifReader.cs
new file mode 100644
index 000000000..8526a7f2a
--- /dev/null
+++ b/MediaBrowser.Providers/Photos/ExifReader.cs
@@ -0,0 +1,613 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Text;
+
+namespace MediaBrowser.Providers.Photos
+{
+ /// <summary>
+ /// A class for reading Exif data from a JPEG file. The file will be open for reading for as long as the class exists.
+ /// <seealso cref="http://gvsoft.homedns.org/exif/Exif-explanation.html"/>
+ /// </summary>
+ public class ExifReader : IDisposable
+ {
+ private readonly FileStream fileStream = null;
+ private readonly BinaryReader reader = null;
+
+ /// <summary>
+ /// The catalogue of tag ids and their absolute offsets within the
+ /// file
+ /// </summary>
+ private Dictionary<ushort, long> catalogue;
+
+ /// <summary>
+ /// Indicates whether to read data using big or little endian byte aligns
+ /// </summary>
+ private bool isLittleEndian;
+
+ /// <summary>
+ /// The position in the filestream at which the TIFF header starts
+ /// </summary>
+ private long tiffHeaderStart;
+
+ public ExifReader(string fileName)
+ {
+ // JPEG encoding uses big endian (i.e. Motorola) byte aligns. The TIFF encoding
+ // found later in the document will specify the byte aligns used for the
+ // rest of the document.
+ isLittleEndian = false;
+
+ try
+ {
+ // Open the file in a stream
+ fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
+ reader = new BinaryReader(fileStream);
+
+ // Make sure the file's a JPEG.
+ if (ReadUShort() != 0xFFD8)
+ throw new Exception("File is not a valid JPEG");
+
+ // Scan to the start of the Exif content
+ ReadToExifStart();
+
+ // Create an index of all Exif tags found within the document
+ CreateTagIndex();
+ }
+ catch (Exception)
+ {
+ // If instantiation fails, make sure there's no mess left behind
+ Dispose();
+
+ throw;
+ }
+ }
+
+ #region TIFF methods
+
+ /// <summary>
+ /// Returns the length (in bytes) per component of the specified TIFF data type
+ /// </summary>
+ /// <returns></returns>
+ private byte GetTIFFFieldLength(ushort tiffDataType)
+ {
+ switch (tiffDataType)
+ {
+ case 1:
+ case 2:
+ case 6:
+ return 1;
+ case 3:
+ case 8:
+ return 2;
+ case 4:
+ case 7:
+ case 9:
+ case 11:
+ return 4;
+ case 5:
+ case 10:
+ case 12:
+ return 8;
+ default:
+ throw new Exception(string.Format("Unknown TIFF datatype: {0}", tiffDataType));
+ }
+ }
+
+ #endregion
+
+ #region Methods for reading data directly from the filestream
+
+ /// <summary>
+ /// Gets a 2 byte unsigned integer from the file
+ /// </summary>
+ /// <returns></returns>
+ private ushort ReadUShort()
+ {
+ return ToUShort(ReadBytes(2));
+ }
+
+ /// <summary>
+ /// Gets a 4 byte unsigned integer from the file
+ /// </summary>
+ /// <returns></returns>
+ private uint ReadUint()
+ {
+ return ToUint(ReadBytes(4));
+ }
+
+ private string ReadString(int chars)
+ {
+ return Encoding.ASCII.GetString(ReadBytes(chars));
+ }
+
+ private byte[] ReadBytes(int byteCount)
+ {
+ return reader.ReadBytes(byteCount);
+ }
+
+ /// <summary>
+ /// Reads some bytes from the specified TIFF offset
+ /// </summary>
+ /// <param name="tiffOffset"></param>
+ /// <param name="byteCount"></param>
+ /// <returns></returns>
+ private byte[] ReadBytes(ushort tiffOffset, int byteCount)
+ {
+ // Keep the current file offset
+ long originalOffset = fileStream.Position;
+
+ // Move to the TIFF offset and retrieve the data
+ fileStream.Seek(tiffOffset + tiffHeaderStart, SeekOrigin.Begin);
+
+ byte[] data = reader.ReadBytes(byteCount);
+
+ // Restore the file offset
+ fileStream.Position = originalOffset;
+
+ return data;
+ }
+
+ #endregion
+
+ #region Data conversion methods for interpreting datatypes from a byte array
+
+ /// <summary>
+ /// Converts 2 bytes to a ushort using the current byte aligns
+ /// </summary>
+ /// <returns></returns>
+ private ushort ToUShort(byte[] data)
+ {
+ if (isLittleEndian != BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+
+ return BitConverter.ToUInt16(data, 0);
+ }
+
+ /// <summary>
+ /// Converts 8 bytes to an unsigned rational using the current byte aligns.
+ /// </summary>
+ /// <param name="data"></param>
+ /// <returns></returns>
+ /// <seealso cref="ToRational"/>
+ private double ToURational(byte[] data)
+ {
+ var numeratorData = new byte[4];
+ var denominatorData = new byte[4];
+
+ Array.Copy(data, numeratorData, 4);
+ Array.Copy(data, 4, denominatorData, 0, 4);
+
+ uint numerator = ToUint(numeratorData);
+ uint denominator = ToUint(denominatorData);
+
+ return numerator / (double)denominator;
+ }
+
+ /// <summary>
+ /// Converts 8 bytes to a signed rational using the current byte aligns.
+ /// </summary>
+ /// <remarks>
+ /// A TIFF rational contains 2 4-byte integers, the first of which is
+ /// the numerator, and the second of which is the denominator.
+ /// </remarks>
+ /// <param name="data"></param>
+ /// <returns></returns>
+ private double ToRational(byte[] data)
+ {
+ var numeratorData = new byte[4];
+ var denominatorData = new byte[4];
+
+ Array.Copy(data, numeratorData, 4);
+ Array.Copy(data, 4, denominatorData, 0, 4);
+
+ int numerator = ToInt(numeratorData);
+ int denominator = ToInt(denominatorData);
+
+ return numerator / (double)denominator;
+ }
+
+ /// <summary>
+ /// Converts 4 bytes to a uint using the current byte aligns
+ /// </summary>
+ /// <returns></returns>
+ private uint ToUint(byte[] data)
+ {
+ if (isLittleEndian != BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+
+ return BitConverter.ToUInt32(data, 0);
+ }
+
+ /// <summary>
+ /// Converts 4 bytes to an int using the current byte aligns
+ /// </summary>
+ /// <returns></returns>
+ private int ToInt(byte[] data)
+ {
+ if (isLittleEndian != BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+
+ return BitConverter.ToInt32(data, 0);
+ }
+
+ private double ToDouble(byte[] data)
+ {
+ if (isLittleEndian != BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+
+ return BitConverter.ToDouble(data, 0);
+ }
+
+ private float ToSingle(byte[] data)
+ {
+ if (isLittleEndian != BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+
+ return BitConverter.ToSingle(data, 0);
+ }
+
+ private short ToShort(byte[] data)
+ {
+ if (isLittleEndian != BitConverter.IsLittleEndian)
+ Array.Reverse(data);
+
+ return BitConverter.ToInt16(data, 0);
+ }
+
+ private sbyte ToSByte(byte[] data)
+ {
+ // An sbyte should just be a byte with an offset range.
+ return (sbyte)(data[0] - byte.MaxValue);
+ }
+
+ /// <summary>
+ /// Retrieves an array from a byte array using the supplied converter
+ /// to read each individual element from the supplied byte array
+ /// </summary>
+ /// <param name="data"></param>
+ /// <param name="elementLengthBytes"></param>
+ /// <param name="converter"></param>
+ /// <returns></returns>
+ private Array GetArray<T>(byte[] data, int elementLengthBytes, ConverterMethod<T> converter)
+ {
+ Array convertedData = Array.CreateInstance(typeof(T), data.Length / elementLengthBytes);
+
+ var buffer = new byte[elementLengthBytes];
+
+ // Read each element from the array
+ for (int elementCount = 0; elementCount < data.Length / elementLengthBytes; elementCount++)
+ {
+ // Place the data for the current element into the buffer
+ Array.Copy(data, elementCount * elementLengthBytes, buffer, 0, elementLengthBytes);
+
+ // Process the data and place it into the output array
+ convertedData.SetValue(converter(buffer), elementCount);
+ }
+
+ return convertedData;
+ }
+
+ /// <summary>
+ /// A delegate used to invoke any of the data conversion methods
+ /// </summary>
+ /// <param name="data"></param>
+ /// <returns></returns>
+ private delegate T ConverterMethod<out T>(byte[] data);
+
+ #endregion
+
+ #region Stream seek methods - used to get to locations within the JPEG
+
+ /// <summary>
+ /// Scans to the Exif block
+ /// </summary>
+ private void ReadToExifStart()
+ {
+ // The file has a number of blocks (Exif/JFIF), each of which
+ // has a tag number followed by a length. We scan the document until the required tag (0xFFE1)
+ // is found. All tags start with FF, so a non FF tag indicates an error.
+
+ // Get the next tag.
+ byte markerStart;
+ byte markerNumber = 0;
+ while (((markerStart = reader.ReadByte()) == 0xFF) && (markerNumber = reader.ReadByte()) != 0xE1)
+ {
+ // Get the length of the data.
+ ushort dataLength = ReadUShort();
+
+ // Jump to the end of the data (note that the size field includes its own size)!
+ reader.BaseStream.Seek(dataLength - 2, SeekOrigin.Current);
+ }
+
+ // It's only success if we found the 0xFFE1 marker
+ if (markerStart != 0xFF || markerNumber != 0xE1)
+ throw new Exception("Could not find Exif data block");
+ }
+
+ /// <summary>
+ /// Reads through the Exif data and builds an index of all Exif tags in the document
+ /// </summary>
+ /// <returns></returns>
+ private void CreateTagIndex()
+ {
+ // The next 4 bytes are the size of the Exif data.
+ ReadUShort();
+
+ // Next is the Exif data itself. It starts with the ASCII "Exif" followed by 2 zero bytes.
+ if (ReadString(4) != "Exif")
+ throw new Exception("Exif data not found");
+
+ // 2 zero bytes
+ if (ReadUShort() != 0)
+ throw new Exception("Malformed Exif data");
+
+ // We're now into the TIFF format
+ tiffHeaderStart = reader.BaseStream.Position;
+
+ // What byte align will be used for the TIFF part of the document? II for Intel, MM for Motorola
+ isLittleEndian = ReadString(2) == "II";
+
+ // Next 2 bytes are always the same.
+ if (ReadUShort() != 0x002A)
+ throw new Exception("Error in TIFF data");
+
+ // Get the offset to the IFD (image file directory)
+ uint ifdOffset = ReadUint();
+
+ // Note that this offset is from the first byte of the TIFF header. Jump to the IFD.
+ fileStream.Position = ifdOffset + tiffHeaderStart;
+
+ // Catalogue this first IFD (there will be another IFD)
+ CatalogueIFD();
+
+ // There's more data stored in the subifd, the offset to which is found in tag 0x8769.
+ // As with all TIFF offsets, it will be relative to the first byte of the TIFF header.
+ uint offset;
+ if (!GetTagValue(0x8769, out offset))
+ throw new Exception("Unable to locate Exif data");
+
+ // Jump to the exif SubIFD
+ fileStream.Position = offset + tiffHeaderStart;
+
+ // Add the subIFD to the catalogue too
+ CatalogueIFD();
+
+ // Go to the GPS IFD and catalogue that too. It's an optional
+ // section.
+ if (GetTagValue(0x8825, out offset))
+ {
+ // Jump to the GPS SubIFD
+ fileStream.Position = offset + tiffHeaderStart;
+
+ // Add the subIFD to the catalogue too
+ CatalogueIFD();
+ }
+ }
+
+ #endregion
+
+ #region Exif data catalog and retrieval methods
+
+ public bool GetTagValue<T>(ExifTags tag, out T result)
+ {
+ return GetTagValue((ushort)tag, out result);
+ }
+
+ /// <summary>
+ /// Retrieves an Exif value with the requested tag ID
+ /// </summary>
+ /// <param name="tagID"></param>
+ /// <param name="result"></param>
+ /// <returns></returns>
+ public bool GetTagValue<T>(ushort tagID, out T result)
+ {
+ ushort tiffDataType;
+ uint numberOfComponents;
+ byte[] tagData = GetTagBytes(tagID, out tiffDataType, out numberOfComponents);
+
+ if (tagData == null)
+ {
+ result = default(T);
+ return false;
+ }
+
+ byte fieldLength = GetTIFFFieldLength(tiffDataType);
+
+ // Convert the data to the appropriate datatype. Note the weird boxing via object.
+ // The compiler doesn't like it otherwise.
+ switch (tiffDataType)
+ {
+ case 1:
+ // unsigned byte
+ if (numberOfComponents == 1)
+ result = (T)(object)tagData[0];
+ else
+ result = (T)(object)tagData;
+ return true;
+ case 2:
+ // ascii string
+ string str = Encoding.ASCII.GetString(tagData);
+
+ // There may be a null character within the string
+ int nullCharIndex = str.IndexOf('\0');
+ if (nullCharIndex != -1)
+ str = str.Substring(0, nullCharIndex);
+
+ // Special processing for dates.
+ if (typeof(T) == typeof(DateTime))
+ {
+ result =
+ (T)(object)DateTime.ParseExact(str, "yyyy:MM:dd HH:mm:ss", CultureInfo.InvariantCulture);
+ return true;
+ }
+
+ result = (T)(object)str;
+ return true;
+ case 3:
+ // unsigned short
+ if (numberOfComponents == 1)
+ result = (T)(object)ToUShort(tagData);
+ else
+ result = (T)(object)GetArray(tagData, fieldLength, ToUShort);
+ return true;
+ case 4:
+ // unsigned long
+ if (numberOfComponents == 1)
+ result = (T)(object)ToUint(tagData);
+ else
+ result = (T)(object)GetArray(tagData, fieldLength, ToUint);
+ return true;
+ case 5:
+ // unsigned rational
+ if (numberOfComponents == 1)
+ result = (T)(object)ToURational(tagData);
+ else
+ result = (T)(object)GetArray(tagData, fieldLength, ToURational);
+ return true;
+ case 6:
+ // signed byte
+ if (numberOfComponents == 1)
+ result = (T)(object)ToSByte(tagData);
+ else
+ result = (T)(object)GetArray(tagData, fieldLength, ToSByte);
+ return true;
+ case 7:
+ // undefined. Treat it as an unsigned integer.
+ if (numberOfComponents == 1)
+ result = (T)(object)ToUint(tagData);
+ else
+ result = (T)(object)GetArray(tagData, fieldLength, ToUint);
+ return true;
+ case 8:
+ // Signed short
+ if (numberOfComponents == 1)
+ result = (T)(object)ToShort(tagData);
+ else
+ result = (T)(object)GetArray(tagData, fieldLength, ToShort);
+ return true;
+ case 9:
+ // Signed long
+ if (numberOfComponents == 1)
+ result = (T)(object)ToInt(tagData);
+ else
+ result = (T)(object)GetArray(tagData, fieldLength, ToInt);
+ return true;
+ case 10:
+ // signed rational
+ if (numberOfComponents == 1)
+ result = (T)(object)ToRational(tagData);
+ else
+ result = (T)(object)GetArray(tagData, fieldLength, ToRational);
+ return true;
+ case 11:
+ // single float
+ if (numberOfComponents == 1)
+ result = (T)(object)ToSingle(tagData);
+ else
+ result = (T)(object)GetArray(tagData, fieldLength, ToSingle);
+ return true;
+ case 12:
+ // double float
+ if (numberOfComponents == 1)
+ result = (T)(object)ToDouble(tagData);
+ else
+ result = (T)(object)GetArray(tagData, fieldLength, ToDouble);
+ return true;
+ default:
+ throw new Exception(string.Format("Unknown TIFF datatype: {0}", tiffDataType));
+ }
+ }
+
+ /// <summary>
+ /// Gets the data in the specified tag ID, starting from before the IFD block.
+ /// </summary>
+ /// <param name="tiffDataType"></param>
+ /// <param name="numberOfComponents">The number of items which make up the data item - i.e. for a string, this will be the
+ /// number of characters in the string</param>
+ /// <param name="tagID"></param>
+ private byte[] GetTagBytes(ushort tagID, out ushort tiffDataType, out uint numberOfComponents)
+ {
+ // Get the tag's offset from the catalogue and do some basic error checks
+ if (fileStream == null || reader == null || catalogue == null || !catalogue.ContainsKey(tagID))
+ {
+ tiffDataType = 0;
+ numberOfComponents = 0;
+ return null;
+ }
+
+ long tagOffset = catalogue[tagID];
+
+ // Jump to the TIFF offset
+ fileStream.Position = tagOffset;
+
+ // Read the tag number from the file
+ ushort currentTagID = ReadUShort();
+
+ if (currentTagID != tagID)
+ throw new Exception("Tag number not at expected offset");
+
+ // Read the offset to the Exif IFD
+ tiffDataType = ReadUShort();
+ numberOfComponents = ReadUint();
+ byte[] tagData = ReadBytes(4);
+
+ // If the total space taken up by the field is longer than the
+ // 2 bytes afforded by the tagData, tagData will contain an offset
+ // to the actual data.
+ var dataSize = (int)(numberOfComponents * GetTIFFFieldLength(tiffDataType));
+
+ if (dataSize > 4)
+ {
+ ushort offsetAddress = ToUShort(tagData);
+ return ReadBytes(offsetAddress, dataSize);
+ }
+
+ // The value is stored in the tagData starting from the left
+ Array.Resize(ref tagData, dataSize);
+
+ return tagData;
+ }
+
+ /// <summary>
+ /// Records all Exif tags and their offsets within
+ /// the file from the current IFD
+ /// </summary>
+ private void CatalogueIFD()
+ {
+ if (catalogue == null)
+ catalogue = new Dictionary<ushort, long>();
+
+ // Assume we're just before the IFD.
+
+ // First 2 bytes is the number of entries in this IFD
+ ushort entryCount = ReadUShort();
+
+ for (ushort currentEntry = 0; currentEntry < entryCount; currentEntry++)
+ {
+ ushort currentTagNumber = ReadUShort();
+
+ // Record this in the catalogue
+ catalogue[currentTagNumber] = fileStream.Position - 2;
+
+ // Go to the end of this item (10 bytes, as each entry is 12 bytes long)
+ reader.BaseStream.Seek(10, SeekOrigin.Current);
+ }
+ }
+
+ #endregion
+
+ #region IDisposable Members
+
+ public void Dispose()
+ {
+ // Make sure the file handle is released
+ if (reader != null)
+ reader.Close();
+ if (fileStream != null)
+ fileStream.Close();
+ }
+
+ #endregion
+ }
+}
diff --git a/MediaBrowser.Providers/Photos/ExifTags.cs b/MediaBrowser.Providers/Photos/ExifTags.cs
new file mode 100644
index 000000000..39e153f2e
--- /dev/null
+++ b/MediaBrowser.Providers/Photos/ExifTags.cs
@@ -0,0 +1,132 @@
+
+namespace MediaBrowser.Providers.Photos
+{
+ /// <summary>
+ /// All exif tags as per the Exif standard 2.2, JEITA CP-2451
+ /// </summary>
+ public enum ExifTags : ushort
+ {
+ // IFD0 items
+ ImageWidth = 0x100,
+ ImageLength = 0x101,
+ BitsPerSample = 0x102,
+ Compression = 0x103,
+ PhotometricInterpretation = 0x106,
+ ImageDescription = 0x10E,
+ Make = 0x10F,
+ Model = 0x110,
+ StripOffsets = 0x111,
+ Orientation = 0x112,
+ SamplesPerPixel = 0x115,
+ RowsPerStrip = 0x116,
+ StripByteCounts = 0x117,
+ XResolution = 0x11A,
+ YResolution = 0x11B,
+ PlanarConfiguration = 0x11C,
+ ResolutionUnit = 0x128,
+ TransferFunction = 0x12D,
+ Software = 0x131,
+ DateTime = 0x132,
+ Artist = 0x13B,
+ WhitePoint = 0x13E,
+ PrimaryChromaticities = 0x13F,
+ JPEGInterchangeFormat = 0x201,
+ JPEGInterchangeFormatLength = 0x202,
+ YCbCrCoefficients = 0x211,
+ YCbCrSubSampling = 0x212,
+ YCbCrPositioning = 0x213,
+ ReferenceBlackWhite = 0x214,
+ Copyright = 0x8298,
+
+ // SubIFD items
+ ExposureTime = 0x829A,
+ FNumber = 0x829D,
+ ExposureProgram = 0x8822,
+ SpectralSensitivity = 0x8824,
+ ISOSpeedRatings = 0x8827,
+ OECF = 0x8828,
+ ExifVersion = 0x9000,
+ DateTimeOriginal = 0x9003,
+ DateTimeDigitized = 0x9004,
+ ComponentsConfiguration = 0x9101,
+ CompressedBitsPerPixel = 0x9102,
+ ShutterSpeedValue = 0x9201,
+ ApertureValue = 0x9202,
+ BrightnessValue = 0x9203,
+ ExposureBiasValue = 0x9204,
+ MaxApertureValue = 0x9205,
+ SubjectDistance = 0x9206,
+ MeteringMode = 0x9207,
+ LightSource = 0x9208,
+ Flash = 0x9209,
+ FocalLength = 0x920A,
+ SubjectArea = 0x9214,
+ MakerNote = 0x927C,
+ UserComment = 0x9286,
+ SubsecTime = 0x9290,
+ SubsecTimeOriginal = 0x9291,
+ SubsecTimeDigitized = 0x9292,
+ FlashpixVersion = 0xA000,
+ ColorSpace = 0xA001,
+ PixelXDimension = 0xA002,
+ PixelYDimension = 0xA003,
+ RelatedSoundFile = 0xA004,
+ FlashEnergy = 0xA20B,
+ SpatialFrequencyResponse = 0xA20C,
+ FocalPlaneXResolution = 0xA20E,
+ FocalPlaneYResolution = 0xA20F,
+ FocalPlaneResolutionUnit = 0xA210,
+ SubjectLocation = 0xA214,
+ ExposureIndex = 0xA215,
+ SensingMethod = 0xA217,
+ FileSource = 0xA300,
+ SceneType = 0xA301,
+ CFAPattern = 0xA302,
+ CustomRendered = 0xA401,
+ ExposureMode = 0xA402,
+ WhiteBalance = 0xA403,
+ DigitalZoomRatio = 0xA404,
+ FocalLengthIn35mmFilm = 0xA405,
+ SceneCaptureType = 0xA406,
+ GainControl = 0xA407,
+ Contrast = 0xA408,
+ Saturation = 0xA409,
+ Sharpness = 0xA40A,
+ DeviceSettingDescription = 0xA40B,
+ SubjectDistanceRange = 0xA40C,
+ ImageUniqueID = 0xA420,
+
+ // GPS subifd items
+ GPSVersionID = 0x0,
+ GPSLatitudeRef = 0x1,
+ GPSLatitude = 0x2,
+ GPSLongitudeRef = 0x3,
+ GPSLongitude = 0x4,
+ GPSAltitudeRef = 0x5,
+ GPSAltitude = 0x6,
+ GPSTimeStamp = 0x7,
+ GPSSatellites = 0x8,
+ GPSStatus = 0x9,
+ GPSMeasureMode = 0xA,
+ GPSDOP = 0xB,
+ GPSSpeedRef = 0xC,
+ GPSSpeed = 0xD,
+ GPSTrackRef = 0xE,
+ GPSTrack = 0xF,
+ GPSImgDirectionRef = 0x10,
+ GPSImgDirection = 0x11,
+ GPSMapDatum = 0x12,
+ GPSDestLatitudeRef = 0x13,
+ GPSDestLatitude = 0x14,
+ GPSDestLongitudeRef = 0x15,
+ GPSDestLongitude = 0x16,
+ GPSDestBearingRef = 0x17,
+ GPSDestBearing = 0x18,
+ GPSDestDistanceRef = 0x19,
+ GPSDestDistance = 0x1A,
+ GPSProcessingMethod = 0x1B,
+ GPSAreaInformation = 0x1C,
+ GPSDateStamp = 0x1D,
+ GPSDifferential = 0x1E
+ }
+}
diff --git a/MediaBrowser.Providers/Photos/PhotoHelper.cs b/MediaBrowser.Providers/Photos/PhotoHelper.cs
new file mode 100644
index 000000000..a5ce6f81f
--- /dev/null
+++ b/MediaBrowser.Providers/Photos/PhotoHelper.cs
@@ -0,0 +1,113 @@
+using MediaBrowser.Controller.Entities;
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MediaBrowser.Providers.Photos
+{
+ public static class PhotoHelper
+ {
+ public static List<BaseItem> ShuffleList(List<BaseItem> list)
+ {
+ var rnd = new Random(DateTime.Now.Second);
+ for (var i = 1; i < list.Count; i++)
+ {
+ var pos = rnd.Next(i + 1);
+ var x = list[i];
+ list[i] = list[pos];
+ list[pos] = x;
+ }
+ return list;
+ }
+
+ public static string Dec2Frac(double dbl)
+ {
+ char neg = ' ';
+ double dblDecimal = dbl;
+ if (dblDecimal == (int)dblDecimal) return dblDecimal.ToString(); //return no if it's not a decimal
+ if (dblDecimal < 0)
+ {
+ dblDecimal = Math.Abs(dblDecimal);
+ neg = '-';
+ }
+ var whole = (int)Math.Truncate(dblDecimal);
+ string decpart = dblDecimal.ToString().Replace(Math.Truncate(dblDecimal) + ".", "");
+ double rN = Convert.ToDouble(decpart);
+ double rD = Math.Pow(10, decpart.Length);
+
+ string rd = Recur(decpart);
+ int rel = Convert.ToInt32(rd);
+ if (rel != 0)
+ {
+ rN = rel;
+ rD = (int)Math.Pow(10, rd.Length) - 1;
+ }
+ //just a few prime factors for testing purposes
+ var primes = new[] { 47, 43, 37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2 };
+ foreach (int i in primes) ReduceNo(i, ref rD, ref rN);
+
+ rN = rN + (whole * rD);
+ return string.Format("{0}{1}/{2}", neg, rN, rD);
+ }
+
+ /// <summary>
+ /// Finds out the recurring decimal in a specified number
+ /// </summary>
+ /// <param name="db">Number to check</param>
+ /// <returns></returns>
+ private static string Recur(string db)
+ {
+ if (db.Length < 13) return "0";
+ var sb = new StringBuilder();
+ for (int i = 0; i < 7; i++)
+ {
+ sb.Append(db[i]);
+ int dlength = (db.Length / sb.ToString().Length);
+ int occur = Occurence(sb.ToString(), db);
+ if (dlength == occur || dlength == occur - sb.ToString().Length)
+ {
+ return sb.ToString();
+ }
+ }
+ return "0";
+ }
+
+ /// <summary>
+ /// Checks for number of occurence of specified no in a number
+ /// </summary>
+ /// <param name="s">The no to check occurence times</param>
+ /// <param name="check">The number where to check this</param>
+ /// <returns></returns>
+ private static int Occurence(string s, string check)
+ {
+ int i = 0;
+ int d = s.Length;
+ string ds = check;
+ for (int n = (ds.Length / d); n > 0; n--)
+ {
+ if (ds.Contains(s))
+ {
+ i++;
+ ds = ds.Remove(ds.IndexOf(s, System.StringComparison.Ordinal), d);
+ }
+ }
+ return i;
+ }
+
+ /// <summary>
+ /// Reduces a fraction given the numerator and denominator
+ /// </summary>
+ /// <param name="i">Number to use in an attempt to reduce fraction</param>
+ /// <param name="rD">the Denominator</param>
+ /// <param name="rN">the Numerator</param>
+ private static void ReduceNo(int i, ref double rD, ref double rN)
+ {
+ //keep reducing until divisibility ends
+ while ((rD % i) < 1e-10 && (rN % i) < 1e-10)
+ {
+ rN = rN / i;
+ rD = rD / i;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/Photos/PhotoMetadataService.cs b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
new file mode 100644
index 000000000..2a7895e16
--- /dev/null
+++ b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs
@@ -0,0 +1,32 @@
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Providers.Manager;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Providers.Photos
+{
+ class PhotoMetadataService : MetadataService<Photo, ItemLookupInfo>
+ {
+ public PhotoMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem)
+ : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem)
+ {
+ }
+
+ /// <summary>
+ /// Merges the specified source.
+ /// </summary>
+ /// <param name="source">The source.</param>
+ /// <param name="target">The target.</param>
+ /// <param name="lockedFields">The locked fields.</param>
+ /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
+ /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
+ protected override void MergeData(Photo source, Photo target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ {
+ ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/Photos/PhotoProvider.cs b/MediaBrowser.Providers/Photos/PhotoProvider.cs
new file mode 100644
index 000000000..23ad5230d
--- /dev/null
+++ b/MediaBrowser.Providers/Photos/PhotoProvider.cs
@@ -0,0 +1,137 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
+using System;
+using System.Globalization;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Providers.Photos
+{
+ public class PhotoProvider : ICustomMetadataProvider<Photo>, IHasChangeMonitor
+ {
+ private readonly ILogger _logger;
+
+ public PhotoProvider(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ public Task<ItemUpdateType> FetchAsync(Photo item, IDirectoryService directoryService, CancellationToken cancellationToken)
+ {
+ item.SetImagePath(ImageType.Primary, item.Path);
+
+ if (item.Path.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) || item.Path.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase))
+ {
+ try
+ {
+ using (var reader = new ExifReader(item.Path))
+ {
+ double aperture = 0;
+ double shutterSpeed = 0;
+
+ DateTime dateTaken;
+
+ string manufacturer;
+ string model;
+
+ int xResolution;
+ int yResolution;
+
+ reader.GetTagValue(ExifTags.FNumber, out aperture);
+ reader.GetTagValue(ExifTags.ExposureTime, out shutterSpeed);
+ reader.GetTagValue(ExifTags.DateTimeOriginal, out dateTaken);
+
+ reader.GetTagValue(ExifTags.Make, out manufacturer);
+ reader.GetTagValue(ExifTags.Model, out model);
+
+ reader.GetTagValue(ExifTags.XResolution, out xResolution);
+ reader.GetTagValue(ExifTags.YResolution, out yResolution);
+
+ if (dateTaken > DateTime.MinValue)
+ {
+ item.DateCreated = dateTaken;
+ item.PremiereDate = dateTaken;
+ item.ProductionYear = dateTaken.Year;
+ }
+
+ var cameraModel = manufacturer ?? string.Empty;
+ cameraModel += " ";
+ cameraModel += model ?? string.Empty;
+
+ item.Overview = "Taken " + dateTaken.ToString("F") + "\n" +
+ (!string.IsNullOrWhiteSpace(cameraModel) ? "With a " + cameraModel : "") +
+ (aperture > 0 && shutterSpeed > 0 ? " at f" + aperture.ToString(CultureInfo.InvariantCulture) + " and " + PhotoHelper.Dec2Frac(shutterSpeed) + "s" : "") + "\n"
+ + (xResolution > 0 ? "\n<br/>Resolution: " + xResolution + "x" + yResolution : "");
+ }
+
+ }
+ catch (Exception e)
+ {
+ _logger.ErrorException("Image Provider - Error reading image tag for {0}", e, item.Path);
+ }
+ }
+
+ //// Get additional tags from xmp
+ //try
+ //{
+ // using (var fs = new FileStream(item.Path, FileMode.Open, FileAccess.Read))
+ // {
+ // var bf = BitmapFrame.Create(fs);
+
+ // if (bf != null)
+ // {
+ // var data = (BitmapMetadata)bf.Metadata;
+ // if (data != null)
+ // {
+
+ // DateTime dateTaken;
+ // var cameraModel = "";
+
+ // DateTime.TryParse(data.DateTaken, out dateTaken);
+ // if (dateTaken > DateTime.MinValue) item.DateCreated = dateTaken;
+ // cameraModel = data.CameraModel;
+
+ // item.PremiereDate = dateTaken;
+ // item.ProductionYear = dateTaken.Year;
+ // item.Overview = "Taken " + dateTaken.ToString("F") + "\n" +
+ // (cameraModel != "" ? "With a " + cameraModel : "") +
+ // (aperture > 0 && shutterSpeed > 0 ? " at f" + aperture.ToString(CultureInfo.InvariantCulture) + " and " + PhotoHelper.Dec2Frac(shutterSpeed) + "s" : "") + "\n"
+ // + (bf.Width > 0 ? "\n<br/>Resolution: " + (int)bf.Width + "x" + (int)bf.Height : "");
+
+ // var photo = item as Photo;
+ // if (data.Keywords != null) item.Genres = photo.Tags = new List<string>(data.Keywords);
+ // item.Name = !string.IsNullOrWhiteSpace(data.Title) ? data.Title : item.Name;
+ // item.CommunityRating = data.Rating;
+ // if (!string.IsNullOrWhiteSpace(data.Subject)) photo.AddTagline(data.Subject);
+ // }
+ // }
+
+ // }
+ //}
+ //catch (NotSupportedException)
+ //{
+ // // No problem - move on
+ //}
+ //catch (Exception e)
+ //{
+ // _logger.ErrorException("Error trying to read extended data from {0}", e, item.Path);
+ //}
+
+ const ItemUpdateType result = ItemUpdateType.ImageUpdate | ItemUpdateType.MetadataImport;
+ return Task.FromResult(result);
+ }
+
+ public string Name
+ {
+ get { return "Embedded Information"; }
+ }
+
+ public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date)
+ {
+ return item.DateModified > date;
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
index 006ab827d..994b70902 100644
--- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
+++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
@@ -54,12 +54,7 @@ namespace MediaBrowser.Providers.Studios
};
}
- public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- return GetImages(item, imageType == ImageType.Primary, imageType == ImageType.Thumb, cancellationToken);
- }
-
- public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
return GetImages(item, true, true, cancellationToken);
}
diff --git a/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs b/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs
index 6f8da573d..908094bdd 100644
--- a/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs
+++ b/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs
@@ -58,14 +58,7 @@ namespace MediaBrowser.Providers.TV
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
-
- return images.Where(i => i.Type == imageType);
- }
-
- public async Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
diff --git a/MediaBrowser.Providers/TV/FanartSeriesProvider.cs b/MediaBrowser.Providers/TV/FanartSeriesProvider.cs
index f18189a2a..1b9f62dd4 100644
--- a/MediaBrowser.Providers/TV/FanartSeriesProvider.cs
+++ b/MediaBrowser.Providers/TV/FanartSeriesProvider.cs
@@ -69,14 +69,7 @@ namespace MediaBrowser.Providers.TV
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
-
- return images.Where(i => i.Type == imageType);
- }
-
- public async Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
diff --git a/MediaBrowser.Providers/TV/MovieDbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/MovieDbSeriesImageProvider.cs
index 9795b7b11..3516fcbd1 100644
--- a/MediaBrowser.Providers/TV/MovieDbSeriesImageProvider.cs
+++ b/MediaBrowser.Providers/TV/MovieDbSeriesImageProvider.cs
@@ -51,14 +51,7 @@ namespace MediaBrowser.Providers.TV
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
-
- return images.Where(i => i.Type == imageType);
- }
-
- public async Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
var list = new List<RemoteImageInfo>();
diff --git a/MediaBrowser.Providers/TV/MovieDbSeriesProvider.cs b/MediaBrowser.Providers/TV/MovieDbSeriesProvider.cs
index c169fba79..5dd8a056f 100644
--- a/MediaBrowser.Providers/TV/MovieDbSeriesProvider.cs
+++ b/MediaBrowser.Providers/TV/MovieDbSeriesProvider.cs
@@ -50,22 +50,37 @@ namespace MediaBrowser.Providers.TV
var result = new MetadataResult<Series>();
var tmdbId = info.GetProviderId(MetadataProviders.Tmdb);
- var imdbId = info.GetProviderId(MetadataProviders.Imdb);
- var tvdbId = info.GetProviderId(MetadataProviders.Tvdb);
- // Commenting our searching by imdb/tvdb because as of now it's not supported.
- // But this is how movies work so most likely this can eventually be enabled.
+ if (string.IsNullOrEmpty(tmdbId))
+ {
+ var imdbId = info.GetProviderId(MetadataProviders.Imdb);
- if (string.IsNullOrEmpty(tmdbId) /*&& string.IsNullOrEmpty(imdbId) && string.IsNullOrEmpty(tvdbId)*/)
+ if (!string.IsNullOrEmpty(imdbId))
+ {
+ tmdbId = await FindIdByExternalId(imdbId, "imdb_id", cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ if (string.IsNullOrEmpty(tmdbId))
+ {
+ var tvdbId = info.GetProviderId(MetadataProviders.Tvdb);
+
+ if (!string.IsNullOrEmpty(tvdbId))
+ {
+ tmdbId = await FindIdByExternalId(tvdbId, "tvdb_id", cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ if (string.IsNullOrEmpty(tmdbId))
{
tmdbId = await new MovieDbSearch(_logger, _jsonSerializer).FindSeriesId(info, cancellationToken).ConfigureAwait(false);
}
- if (!string.IsNullOrEmpty(tmdbId) /*|| !string.IsNullOrEmpty(imdbId) || !string.IsNullOrEmpty(tvdbId)*/)
+ if (!string.IsNullOrEmpty(tmdbId))
{
cancellationToken.ThrowIfCancellationRequested();
- result.Item = await FetchMovieData(tmdbId, imdbId, tvdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
+ result.Item = await FetchMovieData(tmdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
result.HasMetadata = result.Item != null;
}
@@ -73,43 +88,29 @@ namespace MediaBrowser.Providers.TV
return result;
}
- private async Task<Series> FetchMovieData(string tmdbId, string imdbId, string tvdbId, string language, string preferredCountryCode, CancellationToken cancellationToken)
+ private async Task<Series> FetchMovieData(string tmdbId, string language, string preferredCountryCode, CancellationToken cancellationToken)
{
string dataFilePath = null;
RootObject seriesInfo = null;
- // Id could be ImdbId or TmdbId
- if (string.IsNullOrEmpty(tmdbId))
+ if (!string.IsNullOrEmpty(tmdbId))
{
- if (string.IsNullOrWhiteSpace(imdbId))
- {
- seriesInfo = await FetchMainResult(imdbId, language, cancellationToken).ConfigureAwait(false);
- }
- if (seriesInfo == null)
- {
- if (string.IsNullOrWhiteSpace(imdbId))
- {
- seriesInfo = await FetchMainResult(tvdbId, language, cancellationToken).ConfigureAwait(false);
- }
- }
+ seriesInfo = await FetchMainResult(tmdbId, language, cancellationToken).ConfigureAwait(false);
+ }
- if (seriesInfo == null)
- {
- return null;
- }
+ if (seriesInfo == null)
+ {
+ return null;
+ }
- tmdbId = seriesInfo.id.ToString(_usCulture);
+ tmdbId = seriesInfo.id.ToString(_usCulture);
- dataFilePath = MovieDbProvider.Current.GetDataFilePath(tmdbId, language);
- Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath));
- _jsonSerializer.SerializeToFile(seriesInfo, dataFilePath);
- }
+ dataFilePath = MovieDbProvider.Current.GetDataFilePath(tmdbId, language);
+ Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath));
+ _jsonSerializer.SerializeToFile(seriesInfo, dataFilePath);
await EnsureSeriesInfo(tmdbId, language, cancellationToken).ConfigureAwait(false);
- dataFilePath = dataFilePath ?? GetDataFilePath(tmdbId, language);
- seriesInfo = seriesInfo ?? _jsonSerializer.DeserializeFromFile<RootObject>(dataFilePath);
-
var item = new Series();
ProcessMainInfo(item, preferredCountryCode, seriesInfo);
@@ -223,8 +224,6 @@ namespace MediaBrowser.Providers.TV
url += string.Format("&language={0}", language);
}
- RootObject mainResult;
-
cancellationToken.ThrowIfCancellationRequested();
using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
@@ -235,38 +234,8 @@ namespace MediaBrowser.Providers.TV
}).ConfigureAwait(false))
{
- mainResult = _jsonSerializer.DeserializeFromStream<RootObject>(json);
- }
-
- cancellationToken.ThrowIfCancellationRequested();
-
- if (mainResult != null && string.IsNullOrEmpty(mainResult.overview))
- {
- if (!string.IsNullOrEmpty(language) && !string.Equals(language, "en", StringComparison.OrdinalIgnoreCase))
- {
- _logger.Info("Couldn't find meta for language " + language + ". Trying English...");
-
- url = string.Format(GetTvInfo3, id, MovieDbProvider.ApiKey) + "&include_image_language=en,null&language=en";
-
- using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
- {
- Url = url,
- CancellationToken = cancellationToken,
- AcceptHeader = MovieDbProvider.AcceptHeader
-
- }).ConfigureAwait(false))
- {
- mainResult = _jsonSerializer.DeserializeFromStream<RootObject>(json);
- }
-
- if (String.IsNullOrEmpty(mainResult.overview))
- {
- _logger.Error("Unable to find information for (id:" + id + ")");
- return null;
- }
- }
+ return _jsonSerializer.DeserializeFromStream<RootObject>(json);
}
- return mainResult;
}
private readonly Task _cachedTask = Task.FromResult(true);
@@ -338,6 +307,37 @@ namespace MediaBrowser.Providers.TV
return false;
}
+ private async Task<string> FindIdByExternalId(string id, string externalSource, CancellationToken cancellationToken)
+ {
+ var url = string.Format("http://api.themoviedb.org/3/tv/find/{0}?api_key={1}&external_source={2}",
+ id,
+ MovieDbProvider.ApiKey,
+ externalSource);
+
+ using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions
+ {
+ Url = url,
+ CancellationToken = cancellationToken,
+ AcceptHeader = MovieDbProvider.AcceptHeader
+
+ }).ConfigureAwait(false))
+ {
+ var result = _jsonSerializer.DeserializeFromStream<MovieDbSearch.ExternalIdLookupResult>(json);
+
+ if (result != null && result.tv_results != null)
+ {
+ var tv = result.tv_results.FirstOrDefault();
+
+ if (tv != null)
+ {
+ return tv.id.ToString(_usCulture);
+ }
+ }
+ }
+
+ return null;
+ }
+
public class CreatedBy
{
public int id { get; set; }
diff --git a/MediaBrowser.Providers/TV/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TvdbEpisodeImageProvider.cs
index 0830b6713..36e349f60 100644
--- a/MediaBrowser.Providers/TV/TvdbEpisodeImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TvdbEpisodeImageProvider.cs
@@ -51,14 +51,7 @@ namespace MediaBrowser.Providers.TV
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
-
- return images.Where(i => i.Type == imageType);
- }
-
- public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
var episode = (Episode)item;
var series = episode.Series;
diff --git a/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs
index e4756ea71..c0c103b7f 100644
--- a/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs
@@ -59,14 +59,7 @@ namespace MediaBrowser.Providers.TV
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
-
- return images.Where(i => i.Type == imageType);
- }
-
- public async Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
var season = (Season)item;
var series = season.Series;
diff --git a/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs
index 765d17aa7..761c77443 100644
--- a/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs
@@ -59,14 +59,7 @@ namespace MediaBrowser.Providers.TV
};
}
- public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
- {
- var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
-
- return images.Where(i => i.Type == imageType);
- }
-
- public async Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
{
var series = (Series)item;
var seriesId = series.GetProviderId(MetadataProviders.Tvdb);
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index 735565e25..b42541204 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
@@ -462,21 +462,27 @@ namespace MediaBrowser.Server.Implementations.Library
return item;
}
+ public BaseItem ResolvePath(FileSystemInfo fileInfo, Folder parent = null)
+ {
+ return ResolvePath(fileInfo, new DirectoryService(_logger), parent);
+ }
+
/// <summary>
/// Resolves a path into a BaseItem
/// </summary>
/// <param name="fileInfo">The file info.</param>
+ /// <param name="directoryService">The directory service.</param>
/// <param name="parent">The parent.</param>
/// <returns>BaseItem.</returns>
- /// <exception cref="System.ArgumentNullException"></exception>
- public BaseItem ResolvePath(FileSystemInfo fileInfo, Folder parent = null)
+ /// <exception cref="System.ArgumentNullException">fileInfo</exception>
+ public BaseItem ResolvePath(FileSystemInfo fileInfo, IDirectoryService directoryService, Folder parent = null)
{
if (fileInfo == null)
{
throw new ArgumentNullException("fileInfo");
}
- var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, this)
+ var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths, this, directoryService)
{
Parent = parent,
Path = fileInfo.FullName,
@@ -497,8 +503,6 @@ namespace MediaBrowser.Server.Implementations.Library
// When resolving the root, we need it's grandchildren (children of user views)
var flattenFolderDepth = isPhysicalRoot ? 2 : 0;
- var directoryService = new DirectoryService(_logger);
-
var fileSystemDictionary = FileData.GetFilteredFileSystemEntries(directoryService, args.Path, _fileSystem, _logger, args, flattenFolderDepth: flattenFolderDepth, resolveShortcuts: isPhysicalRoot || args.IsVf);
// Need to remove subpaths that may have been resolved from shortcuts
@@ -555,9 +559,10 @@ namespace MediaBrowser.Server.Implementations.Library
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="files">The files.</param>
+ /// <param name="directoryService">The directory service.</param>
/// <param name="parent">The parent.</param>
/// <returns>List{``0}.</returns>
- public List<T> ResolvePaths<T>(IEnumerable<FileSystemInfo> files, Folder parent)
+ public List<T> ResolvePaths<T>(IEnumerable<FileSystemInfo> files, IDirectoryService directoryService, Folder parent)
where T : BaseItem
{
var list = new List<T>();
@@ -566,7 +571,7 @@ namespace MediaBrowser.Server.Implementations.Library
{
try
{
- var item = ResolvePath(f, parent) as T;
+ var item = ResolvePath(f, directoryService, parent) as T;
if (item != null)
{
@@ -594,10 +599,7 @@ namespace MediaBrowser.Server.Implementations.Library
{
var rootFolderPath = ConfigurationManager.ApplicationPaths.RootFolderPath;
- if (!Directory.Exists(rootFolderPath))
- {
- Directory.CreateDirectory(rootFolderPath);
- }
+ Directory.CreateDirectory(rootFolderPath);
var rootFolder = RetrieveItem(rootFolderPath.GetMBId(typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(new DirectoryInfo(rootFolderPath));
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
index f6fd11960..7e643cd99 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
using System;
@@ -62,14 +63,17 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
/// Determine if the supplied file data points to a music album
/// </summary>
/// <param name="path">The path.</param>
+ /// <param name="directoryService">The directory service.</param>
/// <returns><c>true</c> if [is music album] [the specified data]; otherwise, <c>false</c>.</returns>
- public static bool IsMusicAlbum(string path)
+ public static bool IsMusicAlbum(string path, IDirectoryService directoryService)
{
// If list contains at least 2 audio files or at least one and no video files consider it to contain music
var foundAudio = 0;
- foreach (var fullName in Directory.EnumerateFiles(path))
+ foreach (var file in directoryService.GetFiles(path))
{
+ var fullName = file.FullName;
+
if (EntityResolutionHelper.IsAudioFile(fullName))
{
// Don't resolve these into audio files
@@ -105,7 +109,6 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
if (ContainsMusic(args.FileSystemChildren)) return true;
}
-
return false;
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
index 54a32c367..4fa97fc9d 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
@@ -57,9 +57,11 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
{
return null;
}
+
+ var directoryService = args.DirectoryService;
// If we contain an album assume we are an artist folder
- return args.FileSystemChildren.Where(i => (i.Attributes & FileAttributes.Directory) == FileAttributes.Directory).Any(i => MusicAlbumResolver.IsMusicAlbum(i.FullName)) ? new MusicArtist() : null;
+ return args.FileSystemChildren.Where(i => (i.Attributes & FileAttributes.Directory) == FileAttributes.Directory).Any(i => MusicAlbumResolver.IsMusicAlbum(i.FullName, directoryService)) ? new MusicArtist() : null;
}
}
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index f355f4bf6..2d4540713 100644
--- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -4,6 +4,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
using System;
@@ -92,31 +93,31 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
if (args.Path.IndexOf("[trailers]", StringComparison.OrdinalIgnoreCase) != -1 ||
string.Equals(collectionType, CollectionType.Trailers, StringComparison.OrdinalIgnoreCase))
{
- return FindMovie<Trailer>(args.Path, args.Parent, args.FileSystemChildren);
+ return FindMovie<Trailer>(args.Path, args.Parent, args.FileSystemChildren, args.DirectoryService);
}
if (args.Path.IndexOf("[musicvideos]", StringComparison.OrdinalIgnoreCase) != -1 ||
string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
{
- return FindMovie<MusicVideo>(args.Path, args.Parent, args.FileSystemChildren);
+ return FindMovie<MusicVideo>(args.Path, args.Parent, args.FileSystemChildren, args.DirectoryService);
}
if (args.Path.IndexOf("[adultvideos]", StringComparison.OrdinalIgnoreCase) != -1 ||
string.Equals(collectionType, CollectionType.AdultVideos, StringComparison.OrdinalIgnoreCase))
{
- return FindMovie<AdultVideo>(args.Path, args.Parent, args.FileSystemChildren);
+ return FindMovie<AdultVideo>(args.Path, args.Parent, args.FileSystemChildren, args.DirectoryService);
}
if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase))
{
- return FindMovie<Video>(args.Path, args.Parent, args.FileSystemChildren);
+ return FindMovie<Video>(args.Path, args.Parent, args.FileSystemChildren, args.DirectoryService);
}
if (string.IsNullOrEmpty(collectionType) ||
string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase) ||
string.Equals(collectionType, CollectionType.BoxSets, StringComparison.OrdinalIgnoreCase))
{
- return FindMovie<Movie>(args.Path, args.Parent, args.FileSystemChildren);
+ return FindMovie<Movie>(args.Path, args.Parent, args.FileSystemChildren, args.DirectoryService);
}
return null;
@@ -203,7 +204,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
/// <param name="parent">The parent.</param>
/// <param name="fileSystemEntries">The file system entries.</param>
/// <returns>Movie.</returns>
- private T FindMovie<T>(string path, Folder parent, IEnumerable<FileSystemInfo> fileSystemEntries)
+ private T FindMovie<T>(string path, Folder parent, IEnumerable<FileSystemInfo> fileSystemEntries, IDirectoryService directoryService)
where T : Video, new()
{
var movies = new List<T>();
@@ -248,7 +249,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
continue;
}
- var childArgs = new ItemResolveArgs(_applicationPaths, _libraryManager)
+ var childArgs = new ItemResolveArgs(_applicationPaths, _libraryManager, directoryService)
{
FileInfo = child,
Path = child.FullName,
diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs
new file mode 100644
index 000000000..35261dc3b
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Library/Resolvers/PhotoResolver.cs
@@ -0,0 +1,50 @@
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using System;
+using System.Linq;
+
+namespace MediaBrowser.Server.Implementations.Library.Resolvers
+{
+ public class PhotoResolver : ItemResolver<Photo>
+ {
+ private readonly IServerApplicationPaths _applicationPaths;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PhotoResolver" /> class.
+ /// </summary>
+ /// <param name="applicationPaths">The application paths.</param>
+ public PhotoResolver(IServerApplicationPaths applicationPaths)
+ {
+ _applicationPaths = applicationPaths;
+ }
+
+ /// <summary>
+ /// Resolves the specified args.
+ /// </summary>
+ /// <param name="args">The args.</param>
+ /// <returns>Trailer.</returns>
+ protected override Photo Resolve(ItemResolveArgs args)
+ {
+ // Must be an image file within a photo collection
+ if (!args.IsDirectory && IsImageFile(args.Path) && string.Equals(args.GetCollectionType(), "photos", StringComparison.OrdinalIgnoreCase))
+ {
+ return new Photo
+ {
+ Path = args.Path
+ };
+ }
+
+ return null;
+ }
+
+ protected static string[] ImageExtensions = { ".tiff", ".jpg", ".png", ".aiff" };
+ protected bool IsImageFile(string path)
+ {
+ return !path.EndsWith("folder.jpg", StringComparison.OrdinalIgnoreCase)
+ && !path.EndsWith("backdrop.jpg", StringComparison.OrdinalIgnoreCase)
+ && ImageExtensions.Any(p => path.EndsWith(p, StringComparison.OrdinalIgnoreCase));
+ }
+
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
index 8489624fc..050af69d0 100644
--- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
+++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
@@ -140,6 +140,7 @@
<Compile Include="IO\LibraryMonitor.cs" />
<Compile Include="Library\CoreResolutionIgnoreRule.cs" />
<Compile Include="Library\LibraryManager.cs" />
+ <Compile Include="Library\Resolvers\PhotoResolver.cs" />
<Compile Include="Library\SearchEngine.cs" />
<Compile Include="Library\ResolverHelper.cs" />
<Compile Include="Library\Resolvers\Audio\AudioResolver.cs" />
diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs
index 695697f8f..5af08073b 100644
--- a/MediaBrowser.ServerApplication/ApplicationHost.cs
+++ b/MediaBrowser.ServerApplication/ApplicationHost.cs
@@ -31,7 +31,6 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Updates;
-using MediaBrowser.Providers;
using MediaBrowser.Providers.Manager;
using MediaBrowser.Server.Implementations;
using MediaBrowser.Server.Implementations.BdInfo;
@@ -242,6 +241,52 @@ namespace MediaBrowser.ServerApplication
LogManager.RemoveConsoleOutput();
}
+ public override Task Init(IProgress<double> progress)
+ {
+ DeleteDeprecatedModules();
+
+ return base.Init(progress);
+ }
+
+ private void DeleteDeprecatedModules()
+ {
+ try
+ {
+ File.Delete(Path.Combine(ApplicationPaths.PluginsPath, "MBPhoto.dll"));
+ }
+ catch (IOException)
+ {
+ // Not there, no big deal
+ }
+
+ try
+ {
+ Directory.Delete(Path.Combine(ApplicationPaths.DataPath, "remote-images"), true);
+ }
+ catch (IOException)
+ {
+ // Not there, no big deal
+ }
+
+ try
+ {
+ Directory.Delete(Path.Combine(ApplicationPaths.DataPath, "extracted-video-images"), true);
+ }
+ catch (IOException)
+ {
+ // Not there, no big deal
+ }
+
+ try
+ {
+ Directory.Delete(Path.Combine(ApplicationPaths.DataPath, "extracted-audio-images"), true);
+ }
+ catch (IOException)
+ {
+ // Not there, no big deal
+ }
+ }
+
/// <summary>
/// Registers resources that classes will depend on
/// </summary>
diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec
index 4d9fdaf08..8bc2ea226 100644
--- a/Nuget/MediaBrowser.Common.Internal.nuspec
+++ b/Nuget/MediaBrowser.Common.Internal.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common.Internal</id>
- <version>3.0.326</version>
+ <version>3.0.327</version>
<title>MediaBrowser.Common.Internal</title>
<authors>Luke</authors>
<owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
<description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.326" />
+ <dependency id="MediaBrowser.Common" version="3.0.327" />
<dependency id="NLog" version="2.1.0" />
<dependency id="SimpleInjector" version="2.4.1" />
<dependency id="sharpcompress" version="0.10.2" />
diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec
index e90771374..ced8ed35a 100644
--- a/Nuget/MediaBrowser.Common.nuspec
+++ b/Nuget/MediaBrowser.Common.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common</id>
- <version>3.0.326</version>
+ <version>3.0.327</version>
<title>MediaBrowser.Common</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>
diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec
index 2650eabb9..5d435b248 100644
--- a/Nuget/MediaBrowser.Server.Core.nuspec
+++ b/Nuget/MediaBrowser.Server.Core.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Server.Core</id>
- <version>3.0.326</version>
+ <version>3.0.327</version>
<title>Media Browser.Server.Core</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Media Browser Server.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.326" />
+ <dependency id="MediaBrowser.Common" version="3.0.327" />
</dependencies>
</metadata>
<files>