take photos into the core
This commit is contained in:
parent
9254c37d52
commit
eec9e04825
|
@ -1159,6 +1159,72 @@ namespace MediaBrowser.Api.Playback
|
|||
return null;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
|
@ -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.");
|
||||
|
|
|
@ -60,16 +60,12 @@ namespace MediaBrowser.Api.Playback
|
|||
[ApiMember(Name = "Static", Description = "Optional. If true, the original file will be streamed statically without any encoding. Use either no url extension or the original file extension. true/false", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||
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
|
||||
|
|
|
@ -189,7 +189,7 @@ namespace MediaBrowser.Api
|
|||
{
|
||||
throw new ArgumentNullException("xmlSerializer");
|
||||
}
|
||||
|
||||
|
||||
_xmlSerializer = xmlSerializer;
|
||||
_userManager = userManager;
|
||||
_dtoService = dtoService;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>();
|
||||
|
||||
var ownedItemsChanged = await RefreshedOwnedItems(options, files, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (ownedItemsChanged)
|
||||
try
|
||||
{
|
||||
requiresSave = true;
|
||||
var files = locationType == LocationType.FileSystem || locationType == LocationType.Offline ?
|
||||
GetFileSystemChildren(options.DirectoryService).ToList() :
|
||||
new List<FileSystemInfo>();
|
||||
|
||||
var ownedItemsChanged = await RefreshedOwnedItems(options, files, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (ownedItemsChanged)
|
||||
{
|
||||
requiresSave = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
24
MediaBrowser.Controller/Entities/Photo.cs
Normal file
24
MediaBrowser.Controller/Entities/Photo.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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>();
|
||||
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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>();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
613
MediaBrowser.Providers/Photos/ExifReader.cs
Normal file
613
MediaBrowser.Providers/Photos/ExifReader.cs
Normal file
|
@ -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
|
||||
}
|
||||
}
|
132
MediaBrowser.Providers/Photos/ExifTags.cs
Normal file
132
MediaBrowser.Providers/Photos/ExifTags.cs
Normal file
|
@ -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
|
||||
}
|
||||
}
|
113
MediaBrowser.Providers/Photos/PhotoHelper.cs
Normal file
113
MediaBrowser.Providers/Photos/PhotoHelper.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
32
MediaBrowser.Providers/Photos/PhotoMetadataService.cs
Normal file
32
MediaBrowser.Providers/Photos/PhotoMetadataService.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
137
MediaBrowser.Providers/Photos/PhotoProvider.cs
Normal file
137
MediaBrowser.Providers/Photos/PhotoProvider.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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>();
|
||||
|
||||
|
|
|
@ -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>();
|
||||
|
||||
|
|
|
@ -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>();
|
||||
|
||||
|
|
|
@ -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,42 +88,28 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
if (seriesInfo == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
tmdbId = seriesInfo.id.ToString(_usCulture);
|
||||
|
||||
dataFilePath = MovieDbProvider.Current.GetDataFilePath(tmdbId, language);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath));
|
||||
_jsonSerializer.SerializeToFile(seriesInfo, dataFilePath);
|
||||
seriesInfo = await FetchMainResult(tmdbId, language, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await EnsureSeriesInfo(tmdbId, language, cancellationToken).ConfigureAwait(false);
|
||||
if (seriesInfo == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
dataFilePath = dataFilePath ?? GetDataFilePath(tmdbId, language);
|
||||
seriesInfo = seriesInfo ?? _jsonSerializer.DeserializeFromFile<RootObject>(dataFilePath);
|
||||
tmdbId = seriesInfo.id.ToString(_usCulture);
|
||||
|
||||
dataFilePath = MovieDbProvider.Current.GetDataFilePath(tmdbId, language);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(dataFilePath));
|
||||
_jsonSerializer.SerializeToFile(seriesInfo, dataFilePath);
|
||||
|
||||
await EnsureSeriesInfo(tmdbId, language, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var item = new Series();
|
||||
|
||||
|
@ -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);
|
||||
return _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 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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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" />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue
Block a user