diff options
Diffstat (limited to 'MediaBrowser.Controller')
92 files changed, 2154 insertions, 365 deletions
diff --git a/MediaBrowser.Controller/Activity/IActivityManager.cs b/MediaBrowser.Controller/Activity/IActivityManager.cs new file mode 100644 index 0000000000..7285489112 --- /dev/null +++ b/MediaBrowser.Controller/Activity/IActivityManager.cs @@ -0,0 +1,17 @@ +using MediaBrowser.Model.Activity; +using MediaBrowser.Model.Events; +using MediaBrowser.Model.Querying; +using System; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Activity +{ + public interface IActivityManager + { + event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated; + + Task Create(ActivityLogEntry entry); + + QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit); + } +} diff --git a/MediaBrowser.Controller/Activity/IActivityRepository.cs b/MediaBrowser.Controller/Activity/IActivityRepository.cs new file mode 100644 index 0000000000..7ccbc2e99b --- /dev/null +++ b/MediaBrowser.Controller/Activity/IActivityRepository.cs @@ -0,0 +1,14 @@ +using MediaBrowser.Model.Activity; +using MediaBrowser.Model.Querying; +using System; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Activity +{ + public interface IActivityRepository + { + Task Create(ActivityLogEntry entry); + + QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit); + } +} diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs index 3d9d0d381d..df38259b42 100644 --- a/MediaBrowser.Controller/Channels/Channel.cs +++ b/MediaBrowser.Controller/Channels/Channel.cs @@ -4,7 +4,7 @@ using System.Linq; namespace MediaBrowser.Controller.Channels { - public class Channel : BaseItem + public class Channel : Folder { public string OriginalChannelName { get; set; } diff --git a/MediaBrowser.Controller/Channels/ChannelFolderItem.cs b/MediaBrowser.Controller/Channels/ChannelFolderItem.cs index afc6493e4a..077138f3cc 100644 --- a/MediaBrowser.Controller/Channels/ChannelFolderItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelFolderItem.cs @@ -1,7 +1,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Channels; using MediaBrowser.Model.Configuration; -using System.Collections.Generic; namespace MediaBrowser.Controller.Channels { @@ -15,7 +14,6 @@ namespace MediaBrowser.Controller.Channels public ChannelFolderType ChannelFolderType { get; set; } public string OriginalImageUrl { get; set; } - public List<string> Tags { get; set; } protected override bool GetBlockUnratedValue(UserConfiguration config) { @@ -31,11 +29,6 @@ namespace MediaBrowser.Controller.Channels } } - public ChannelFolderItem() - { - Tags = new List<string>(); - } - public override string GetUserDataKey() { return ExternalId; diff --git a/MediaBrowser.Controller/Channels/ChannelItemInfo.cs b/MediaBrowser.Controller/Channels/ChannelItemInfo.cs index 707807bd5a..3c7df91c1c 100644 --- a/MediaBrowser.Controller/Channels/ChannelItemInfo.cs +++ b/MediaBrowser.Controller/Channels/ChannelItemInfo.cs @@ -10,6 +10,8 @@ namespace MediaBrowser.Controller.Channels { public string Name { get; set; } + public string SeriesName { get; set; } + public string Id { get; set; } public ChannelItemType Type { get; set; } @@ -28,8 +30,6 @@ namespace MediaBrowser.Controller.Channels public long? RunTimeTicks { get; set; } - public bool IsInfiniteStream { get; set; } - public string ImageUrl { get; set; } public ChannelMediaType MediaType { get; set; } @@ -43,9 +43,14 @@ namespace MediaBrowser.Controller.Channels public int? ProductionYear { get; set; } public DateTime? DateCreated { get; set; } - + + public int? IndexNumber { get; set; } + public int? ParentIndexNumber { get; set; } + public List<ChannelMediaInfo> MediaSources { get; set; } - + + public bool IsInfiniteStream { get; set; } + public ChannelItemInfo() { MediaSources = new List<ChannelMediaInfo>(); diff --git a/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs b/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs index 64b4804148..f16fd11205 100644 --- a/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs +++ b/MediaBrowser.Controller/Channels/ChannelMediaInfo.cs @@ -29,6 +29,8 @@ namespace MediaBrowser.Controller.Channels public MediaProtocol Protocol { get; set; } + public long? RunTimeTicks { get; set; } + public ChannelMediaInfo() { RequiredHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); diff --git a/MediaBrowser.Controller/Channels/IChannelFactory.cs b/MediaBrowser.Controller/Channels/IChannelFactory.cs index e275227ff9..c7ed925866 100644 --- a/MediaBrowser.Controller/Channels/IChannelFactory.cs +++ b/MediaBrowser.Controller/Channels/IChannelFactory.cs @@ -6,4 +6,9 @@ namespace MediaBrowser.Controller.Channels { IEnumerable<IChannel> GetChannels(); } + + public interface IFactoryChannel + { + + } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs index 744eab96e0..252e2aee53 100644 --- a/MediaBrowser.Controller/Channels/IChannelManager.cs +++ b/MediaBrowser.Controller/Channels/IChannelManager.cs @@ -44,6 +44,14 @@ namespace MediaBrowser.Controller.Channels Channel GetChannel(string id); /// <summary> + /// Gets the channels internal. + /// </summary> + /// <param name="query">The query.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task<QueryResult<Channel>>.</returns> + Task<QueryResult<Channel>> GetChannelsInternal(ChannelQuery query, CancellationToken cancellationToken); + + /// <summary> /// Gets the channels. /// </summary> /// <param name="query">The query.</param> @@ -76,6 +84,14 @@ namespace MediaBrowser.Controller.Channels Task<QueryResult<BaseItemDto>> GetChannelItems(ChannelItemQuery query, CancellationToken cancellationToken); /// <summary> + /// Gets the channel items internal. + /// </summary> + /// <param name="query">The query.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task<QueryResult<BaseItem>>.</returns> + Task<QueryResult<BaseItem>> GetChannelItemsInternal(ChannelItemQuery query, CancellationToken cancellationToken); + + /// <summary> /// Gets the cached channel item media sources. /// </summary> /// <param name="id">The identifier.</param> diff --git a/MediaBrowser.Controller/Chapters/IChapterManager.cs b/MediaBrowser.Controller/Chapters/IChapterManager.cs index b8f29d1ceb..676ef9c561 100644 --- a/MediaBrowser.Controller/Chapters/IChapterManager.cs +++ b/MediaBrowser.Controller/Chapters/IChapterManager.cs @@ -3,6 +3,7 @@ using MediaBrowser.Model.Chapters; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Chapters @@ -70,5 +71,11 @@ namespace MediaBrowser.Controller.Chapters /// </summary> /// <returns>IEnumerable{ChapterProviderInfo}.</returns> IEnumerable<ChapterProviderInfo> GetProviders(); + + /// <summary> + /// Gets the configuration. + /// </summary> + /// <returns>ChapterOptions.</returns> + ChapterOptions GetConfiguration(); } } diff --git a/MediaBrowser.Controller/Collections/CollectionEvents.cs b/MediaBrowser.Controller/Collections/CollectionEvents.cs new file mode 100644 index 0000000000..80f66a444a --- /dev/null +++ b/MediaBrowser.Controller/Collections/CollectionEvents.cs @@ -0,0 +1,37 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Collections +{ + public class CollectionCreatedEventArgs : EventArgs + { + /// <summary> + /// Gets or sets the collection. + /// </summary> + /// <value>The collection.</value> + public BoxSet Collection { get; set; } + + /// <summary> + /// Gets or sets the options. + /// </summary> + /// <value>The options.</value> + public CollectionCreationOptions Options { get; set; } + } + + public class CollectionModifiedEventArgs : EventArgs + { + /// <summary> + /// Gets or sets the collection. + /// </summary> + /// <value>The collection.</value> + public BoxSet Collection { get; set; } + + /// <summary> + /// Gets or sets the items changed. + /// </summary> + /// <value>The items changed.</value> + public List<BaseItem> ItemsChanged { get; set; } + } +} diff --git a/MediaBrowser.Controller/Collections/ICollectionManager.cs b/MediaBrowser.Controller/Collections/ICollectionManager.cs index b63c49f99a..9130f68d43 100644 --- a/MediaBrowser.Controller/Collections/ICollectionManager.cs +++ b/MediaBrowser.Controller/Collections/ICollectionManager.cs @@ -9,6 +9,21 @@ namespace MediaBrowser.Controller.Collections public interface ICollectionManager { /// <summary> + /// Occurs when [collection created]. + /// </summary> + event EventHandler<CollectionCreatedEventArgs> CollectionCreated; + + /// <summary> + /// Occurs when [items added to collection]. + /// </summary> + event EventHandler<CollectionModifiedEventArgs> ItemsAddedToCollection; + + /// <summary> + /// Occurs when [items removed from collection]. + /// </summary> + event EventHandler<CollectionModifiedEventArgs> ItemsRemovedFromCollection; + + /// <summary> /// Creates the collection. /// </summary> /// <param name="options">The options.</param> @@ -38,5 +53,12 @@ namespace MediaBrowser.Controller.Collections /// <param name="user">The user.</param> /// <returns>IEnumerable{BaseItem}.</returns> IEnumerable<BaseItem> CollapseItemsWithinBoxSets(IEnumerable<BaseItem> items, User user); + + /// <summary> + /// Gets the collections folder. + /// </summary> + /// <param name="userId">The user identifier.</param> + /// <returns>Folder.</returns> + Folder GetCollectionsFolder(string userId); } } diff --git a/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs b/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs index 6a2343a009..13c9f8d84b 100644 --- a/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs +++ b/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs @@ -1,7 +1,5 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Events; -using System; namespace MediaBrowser.Controller.Configuration { @@ -11,11 +9,6 @@ namespace MediaBrowser.Controller.Configuration public interface IServerConfigurationManager : IConfigurationManager { /// <summary> - /// Occurs when [configuration updating]. - /// </summary> - event EventHandler<GenericEventArgs<ServerConfiguration>> ConfigurationUpdating; - - /// <summary> /// Gets the application paths. /// </summary> /// <value>The application paths.</value> @@ -26,5 +19,11 @@ namespace MediaBrowser.Controller.Configuration /// </summary> /// <value>The configuration.</value> ServerConfiguration Configuration { get; } + + /// <summary> + /// Sets the preferred metadata service. + /// </summary> + /// <param name="service">The service.</param> + void DisableMetadataService(string service); } } diff --git a/MediaBrowser.Controller/Connect/IConnectManager.cs b/MediaBrowser.Controller/Connect/IConnectManager.cs new file mode 100644 index 0000000000..83f565472d --- /dev/null +++ b/MediaBrowser.Controller/Connect/IConnectManager.cs @@ -0,0 +1,9 @@ + +namespace MediaBrowser.Controller.Connect +{ + public interface IConnectManager + { + string WanIpAddress { get; } + string WanApiAddress { get; } + } +} diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs index 51466c4f92..a0128f1113 100644 --- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -73,6 +73,13 @@ namespace MediaBrowser.Controller.Drawing /// <param name="toStream">To stream.</param> /// <returns>Task.</returns> Task ProcessImage(ImageProcessingOptions options, Stream toStream); + + /// <summary> + /// Processes the image. + /// </summary> + /// <param name="options">The options.</param> + /// <returns>Task.</returns> + Task<string> ProcessImage(ImageProcessingOptions options); /// <summary> /// Gets the enhanced image. diff --git a/MediaBrowser.Controller/Drawing/ImageExtensions.cs b/MediaBrowser.Controller/Drawing/ImageExtensions.cs index c7e1968e7b..2511659c3f 100644 --- a/MediaBrowser.Controller/Drawing/ImageExtensions.cs +++ b/MediaBrowser.Controller/Drawing/ImageExtensions.cs @@ -72,39 +72,6 @@ namespace MediaBrowser.Controller.Drawing } /// <summary> - /// Determines whether [is pixel format supported by graphics object] [the specified format]. - /// </summary> - /// <param name="format">The format.</param> - /// <returns><c>true</c> if [is pixel format supported by graphics object] [the specified format]; otherwise, <c>false</c>.</returns> - public static bool IsPixelFormatSupportedByGraphicsObject(PixelFormat format) - { - // http://msdn.microsoft.com/en-us/library/system.drawing.graphics.fromimage.aspx - - if ((format & PixelFormat.Indexed) == PixelFormat.Indexed) - { - return false; - } - if ((format & PixelFormat.Undefined) == PixelFormat.Undefined) - { - return false; - } - if ((format & PixelFormat.DontCare) == PixelFormat.DontCare) - { - return false; - } - if ((format & PixelFormat.Format16bppArgb1555) == PixelFormat.Format16bppArgb1555) - { - return false; - } - if ((format & PixelFormat.Format16bppGrayScale) == PixelFormat.Format16bppGrayScale) - { - return false; - } - - return true; - } - - /// <summary> /// Crops an image by removing whitespace and transparency from the edges /// </summary> /// <param name="bmp">The BMP.</param> diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs index 912ad012d8..f4a76be006 100644 --- a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs +++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs @@ -2,6 +2,7 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Drawing; using System.Collections.Generic; +using System.IO; namespace MediaBrowser.Controller.Drawing { @@ -34,39 +35,36 @@ namespace MediaBrowser.Controller.Drawing public int? UnplayedCount { get; set; } public double? PercentPlayed { get; set; } - + public string BackgroundColor { get; set; } - public bool HasDefaultOptions() + public bool HasDefaultOptions(string originalImagePath) { - return HasDefaultOptionsWithoutSize() && - !Width.HasValue && - !Height.HasValue && - !MaxWidth.HasValue && + return HasDefaultOptionsWithoutSize(originalImagePath) && + !Width.HasValue && + !Height.HasValue && + !MaxWidth.HasValue && !MaxHeight.HasValue; } - public bool HasDefaultOptionsWithoutSize() + public bool HasDefaultOptionsWithoutSize(string originalImagePath) { return (!Quality.HasValue || Quality.Value == 100) && - IsOutputFormatDefault && + IsOutputFormatDefault(originalImagePath) && !AddPlayedIndicator && !PercentPlayed.HasValue && !UnplayedCount.HasValue && string.IsNullOrEmpty(BackgroundColor); } - private bool IsOutputFormatDefault + private bool IsOutputFormatDefault(string originalImagePath) { - get + if (OutputFormat == ImageOutputFormat.Original) { - if (OutputFormat == ImageOutputFormat.Original) - { - return true; - } - - return false; + return true; } + + return string.Equals(Path.GetExtension(originalImagePath), "." + OutputFormat); } } } diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs index 0482e140b0..fc15eedf04 100644 --- a/MediaBrowser.Controller/Dto/IDtoService.cs +++ b/MediaBrowser.Controller/Dto/IDtoService.cs @@ -2,6 +2,7 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; +using System; using System.Collections.Generic; namespace MediaBrowser.Controller.Dto @@ -16,6 +17,7 @@ namespace MediaBrowser.Controller.Dto /// </summary> /// <param name="user">The user.</param> /// <returns>UserDto.</returns> + [Obsolete] UserDto GetUserDto(User user); /// <summary> @@ -26,13 +28,6 @@ namespace MediaBrowser.Controller.Dto string GetDtoId(BaseItem item); /// <summary> - /// Gets the user item data dto. - /// </summary> - /// <param name="data">The data.</param> - /// <returns>UserItemDataDto.</returns> - UserItemDataDto GetUserItemDataDto(UserItemData data); - - /// <summary> /// Attaches the primary image aspect ratio. /// </summary> /// <param name="dto">The dto.</param> @@ -50,16 +45,6 @@ namespace MediaBrowser.Controller.Dto BaseItemDto GetBaseItemDto(BaseItem item, List<ItemFields> fields, User user = null, BaseItem owner = null); /// <summary> - /// Gets the item by name dto. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="fields">The fields.</param> - /// <param name="user">The user.</param> - /// <returns>BaseItemDto.</returns> - BaseItemDto GetItemByNameDto<T>(T item, List<ItemFields> fields, User user = null) - where T : BaseItem, IItemByName; - - /// <summary> /// Gets the chapter information dto. /// </summary> /// <param name="chapterInfo">The chapter information.</param> @@ -68,6 +53,13 @@ namespace MediaBrowser.Controller.Dto ChapterInfoDto GetChapterInfoDto(ChapterInfo chapterInfo, BaseItem item); /// <summary> + /// Gets the user item data dto. + /// </summary> + /// <param name="data">The data.</param> + /// <returns>UserItemDataDto.</returns> + UserItemDataDto GetUserItemDataDto(UserItemData data); + + /// <summary> /// Gets the item by name dto. /// </summary> /// <param name="item">The item.</param> diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index 0900cc1eff..25d41565ae 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -14,13 +14,14 @@ namespace MediaBrowser.Controller.Entities.Audio /// <summary> /// Class Audio /// </summary> - public class Audio : BaseItem, - IHasAlbumArtist, - IHasArtist, - IHasMusicGenres, - IHasLookupInfo<SongInfo>, + public class Audio : BaseItem, + IHasAlbumArtist, + IHasArtist, + IHasMusicGenres, + IHasLookupInfo<SongInfo>, IHasTags, - IHasMediaSources + IHasMediaSources, + IThemeMedia { public string FormatName { get; set; } public long? Size { get; set; } @@ -28,12 +29,21 @@ namespace MediaBrowser.Controller.Entities.Audio public int? TotalBitrate { get; set; } public List<string> Tags { get; set; } + public bool IsThemeMedia { get; set; } + public Audio() { Artists = new List<string>(); + AlbumArtists = new List<string>(); Tags = new List<string>(); } + [IgnoreDataMember] + public override bool SupportsAddingToPlaylist + { + get { return LocationType == LocationType.FileSystem && RunTimeTicks.HasValue; } + } + /// <summary> /// Gets or sets a value indicating whether this instance has embedded image. /// </summary> @@ -64,7 +74,16 @@ namespace MediaBrowser.Controller.Entities.Audio { get { - return Parents.OfType<MusicAlbum>().FirstOrDefault() ?? new MusicAlbum { Name = "<Unknown>" }; + return LatestItemsIndexContainer ?? new MusicAlbum { Name = "Unknown Album" }; + } + } + + [IgnoreDataMember] + public override Folder LatestItemsIndexContainer + { + get + { + return Parents.OfType<MusicAlbum>().FirstOrDefault(); } } @@ -74,12 +93,14 @@ namespace MediaBrowser.Controller.Entities.Audio /// <value>The artist.</value> public List<string> Artists { get; set; } + public List<string> AlbumArtists { get; set; } + [IgnoreDataMember] public List<string> AllArtists { get { - var list = AlbumArtists; + var list = AlbumArtists.ToList(); list.AddRange(Artists); @@ -88,41 +109,17 @@ namespace MediaBrowser.Controller.Entities.Audio } } - [IgnoreDataMember] - public List<string> AlbumArtists - { - get - { - var list = new List<string>(); - - if (!string.IsNullOrEmpty(AlbumArtist)) - { - list.Add(AlbumArtist); - } - - return list; - } - set - { - AlbumArtist = value.FirstOrDefault(); - } - } - /// <summary> /// Gets or sets the album. /// </summary> /// <value>The album.</value> public string Album { get; set; } - /// <summary> - /// Gets or sets the album artist. - /// </summary> - /// <value>The album artist.</value> - public string AlbumArtist { get; set; } /// <summary> /// Gets the type of the media. /// </summary> /// <value>The type of the media.</value> + [IgnoreDataMember] public override string MediaType { get @@ -204,7 +201,7 @@ namespace MediaBrowser.Controller.Entities.Audio private static MediaSourceInfo GetVersionInfo(Audio i, bool enablePathSubstituion) { var locationType = i.LocationType; - + var info = new MediaSourceInfo { Id = i.Id.ToString("N"), diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index a4d9278e53..dc9f83b3c9 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -11,15 +11,21 @@ namespace MediaBrowser.Controller.Entities.Audio /// <summary> /// Class MusicAlbum /// </summary> - public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasTags, IHasLookupInfo<AlbumInfo> + public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo<AlbumInfo> { public List<Guid> SoundtrackIds { get; set; } public MusicAlbum() { - Artists = new List<string>(); SoundtrackIds = new List<Guid>(); - Tags = new List<string>(); + Artists = new List<string>(); + AlbumArtists = new List<string>(); + } + + [IgnoreDataMember] + public override bool SupportsAddingToPlaylist + { + get { return true; } } [IgnoreDataMember] @@ -36,7 +42,7 @@ namespace MediaBrowser.Controller.Entities.Audio { get { - var list = AlbumArtists; + var list = AlbumArtists.ToList(); list.AddRange(Artists); @@ -45,31 +51,30 @@ namespace MediaBrowser.Controller.Entities.Audio } } + public List<string> AlbumArtists { get; set; } + [IgnoreDataMember] - public List<string> AlbumArtists + public string AlbumArtist { - get - { - var list = new List<string>(); - - if (!string.IsNullOrEmpty(AlbumArtist)) - { - list.Add(AlbumArtist); - } + get { return AlbumArtists.FirstOrDefault(); } + } - return list; - } - set + /// <summary> + /// Gets the tracks. + /// </summary> + /// <value>The tracks.</value> + public IEnumerable<Audio> Tracks + { + get { - AlbumArtist = value.FirstOrDefault(); + return RecursiveChildren.OfType<Audio>(); } } - /// <summary> - /// Gets or sets the tags. - /// </summary> - /// <value>The tags.</value> - public List<string> Tags { get; set; } + protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user) + { + return Tracks; + } /// <summary> /// Songs will group into us so don't also include us in the index @@ -124,8 +129,6 @@ namespace MediaBrowser.Controller.Entities.Audio return AllArtists.Contains(artist, StringComparer.OrdinalIgnoreCase); } - public string AlbumArtist { get; set; } - public List<string> Artists { get; set; } /// <summary> @@ -177,6 +180,7 @@ namespace MediaBrowser.Controller.Entities.Audio } } + [Obsolete] public class MusicAlbumDisc : Folder { diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 0a5d8eec03..070572b9b2 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Progress; +using System.Runtime.Serialization; +using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; @@ -13,16 +14,9 @@ namespace MediaBrowser.Controller.Entities.Audio /// <summary> /// Class MusicArtist /// </summary> - public class MusicArtist : Folder, IMetadataContainer, IItemByName, IHasMusicGenres, IHasDualAccess, IHasTags, IHasProductionLocations, IHasLookupInfo<ArtistInfo> + public class MusicArtist : Folder, IMetadataContainer, IItemByName, IHasMusicGenres, IHasDualAccess, IHasProductionLocations, IHasLookupInfo<ArtistInfo> { public bool IsAccessedByName { get; set; } - - /// <summary> - /// Gets or sets the tags. - /// </summary> - /// <value>The tags.</value> - public List<string> Tags { get; set; } - public List<string> ProductionLocations { get; set; } public override bool IsFolder @@ -33,6 +27,12 @@ namespace MediaBrowser.Controller.Entities.Audio } } + [IgnoreDataMember] + public override bool SupportsAddingToPlaylist + { + get { return true; } + } + protected override IEnumerable<BaseItem> ActualChildren { get @@ -60,7 +60,6 @@ namespace MediaBrowser.Controller.Entities.Audio public MusicArtist() { - Tags = new List<string>(); ProductionLocations = new List<string>(); } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs index bce9da4d15..928eb64630 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.Serialization; namespace MediaBrowser.Controller.Entities.Audio { @@ -18,6 +19,12 @@ namespace MediaBrowser.Controller.Entities.Audio return "MusicGenre-" + Name; } + [IgnoreDataMember] + public override bool SupportsAddingToPlaylist + { + get { return true; } + } + /// <summary> /// Returns the folder containing the item. /// If the item is a folder, it returns the folder itself diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index a2ff1b4fdf..26b28ec72b 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -2,10 +2,12 @@ using MediaBrowser.Common.IO; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Library; using MediaBrowser.Model.Logging; @@ -51,12 +53,30 @@ namespace MediaBrowser.Controller.Entities public List<ItemImageInfo> ImageInfos { get; set; } + [IgnoreDataMember] + public virtual bool SupportsAddingToPlaylist + { + get + { + return false; + } + } + /// <summary> /// Gets a value indicating whether this instance is in mixed folder. /// </summary> /// <value><c>true</c> if this instance is in mixed folder; otherwise, <c>false</c>.</value> public bool IsInMixedFolder { get; set; } + [IgnoreDataMember] + public virtual bool SupportsRemoteImageDownloading + { + get + { + return true; + } + } + private string _name; /// <summary> /// Gets or sets the name. @@ -134,6 +154,11 @@ namespace MediaBrowser.Controller.Entities } } + public virtual bool IsHiddenFromUser(User user) + { + return false; + } + [IgnoreDataMember] public virtual bool IsOwnedItem { @@ -168,6 +193,7 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] public virtual bool SupportsLocalMetadata { get @@ -213,6 +239,7 @@ namespace MediaBrowser.Controller.Entities public static IItemRepository ItemRepository { get; set; } public static IFileSystem FileSystem { get; set; } public static IUserDataManager UserDataManager { get; set; } + public static ILiveTvManager LiveTvManager { get; set; } /// <summary> /// Returns a <see cref="System.String" /> that represents this instance. @@ -523,10 +550,10 @@ namespace MediaBrowser.Controller.Entities .Where(i => string.Equals(i.Name, TrailerFolderName, StringComparison.OrdinalIgnoreCase)) .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly)) .ToList(); - + // Support plex/xbmc convention files.AddRange(fileSystemChildren.OfType<FileInfo>() - .Where(i => System.IO.Path.GetFileNameWithoutExtension(i.Name).EndsWith(XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase) && !string.Equals(Path, i.FullName, StringComparison.OrdinalIgnoreCase)) + .Where(i => FileSystem.GetFileNameWithoutExtension(i).EndsWith(XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase) && !string.Equals(Path, i.FullName, StringComparison.OrdinalIgnoreCase)) ); return LibraryManager.ResolvePaths<Trailer>(files, directoryService, null).Select(video => @@ -558,7 +585,7 @@ namespace MediaBrowser.Controller.Entities // Support plex/xbmc convention files.AddRange(fileSystemChildren.OfType<FileInfo>() - .Where(i => string.Equals(System.IO.Path.GetFileNameWithoutExtension(i.Name), ThemeSongFilename, StringComparison.OrdinalIgnoreCase)) + .Where(i => string.Equals(FileSystem.GetFileNameWithoutExtension(i), ThemeSongFilename, StringComparison.OrdinalIgnoreCase)) ); return LibraryManager.ResolvePaths<Audio.Audio>(files, directoryService, null).Select(audio => @@ -724,7 +751,18 @@ namespace MediaBrowser.Controller.Entities var themeVideosChanged = !item.ThemeVideoIds.SequenceEqual(newThemeVideoIds); - var tasks = newThemeVideos.Select(i => i.RefreshMetadata(options, cancellationToken)); + var tasks = newThemeVideos.Select(i => + { + var subOptions = new MetadataRefreshOptions(options); + + if (!i.IsThemeMedia) + { + i.IsThemeMedia = true; + subOptions.ForceSave = true; + } + + return i.RefreshMetadata(subOptions, cancellationToken); + }); await Task.WhenAll(tasks).ConfigureAwait(false); @@ -743,7 +781,18 @@ namespace MediaBrowser.Controller.Entities var themeSongsChanged = !item.ThemeSongIds.SequenceEqual(newThemeSongIds); - var tasks = newThemeSongs.Select(i => i.RefreshMetadata(options, cancellationToken)); + var tasks = newThemeSongs.Select(i => + { + var subOptions = new MetadataRefreshOptions(options); + + if (!i.IsThemeMedia) + { + i.IsThemeMedia = true; + subOptions.ForceSave = true; + } + + return i.RefreshMetadata(subOptions, cancellationToken); + }); await Task.WhenAll(tasks).ConfigureAwait(false); @@ -790,6 +839,12 @@ namespace MediaBrowser.Controller.Entities get { return null; } } + [IgnoreDataMember] + public virtual Folder LatestItemsIndexContainer + { + get { return null; } + } + /// <summary> /// Gets the user data key. /// </summary> @@ -994,6 +1049,18 @@ namespace MediaBrowser.Controller.Entities private BaseItem FindLinkedChild(LinkedChild info) { + if (!string.IsNullOrWhiteSpace(info.ItemName)) + { + if (string.Equals(info.ItemType, "musicgenre", StringComparison.OrdinalIgnoreCase)) + { + return LibraryManager.GetMusicGenre(info.ItemName); + } + if (string.Equals(info.ItemType, "musicartist", StringComparison.OrdinalIgnoreCase)) + { + return LibraryManager.GetArtist(info.ItemName); + } + } + if (!string.IsNullOrEmpty(info.Path)) { var itemByPath = LibraryManager.RootFolder.FindByPath(info.Path); @@ -1016,7 +1083,10 @@ namespace MediaBrowser.Controller.Entities { if (info.ItemYear.HasValue) { - return info.ItemYear.Value == (i.ProductionYear ?? -1); + if (info.ItemYear.Value != (i.ProductionYear ?? -1)) + { + return false; + } } return true; } @@ -1514,6 +1584,11 @@ namespace MediaBrowser.Controller.Entities public virtual bool IsUnplayed(User user) { + if (user == null) + { + throw new ArgumentNullException("user"); + } + var userdata = UserDataManager.GetUserData(user.Id, GetUserDataKey()); return userdata == null || !userdata.Played; @@ -1547,7 +1622,7 @@ namespace MediaBrowser.Controller.Entities if (string.IsNullOrEmpty(Name) && !string.IsNullOrEmpty(Path)) { - Name = System.IO.Path.GetFileNameWithoutExtension(Path); + Name = FileSystem.GetFileNameWithoutExtension(Path); hasChanges = true; } @@ -1566,5 +1641,19 @@ namespace MediaBrowser.Controller.Entities return path; } + + public virtual void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user) + { + if (RunTimeTicks.HasValue) + { + double pct = RunTimeTicks.Value; + + if (pct > 0) + { + pct = userData.PlaybackPositionTicks / pct; + dto.PlayedPercentage = 100 * pct; + } + } + } } } diff --git a/MediaBrowser.Controller/Entities/BasePluginFolder.cs b/MediaBrowser.Controller/Entities/BasePluginFolder.cs index e1383923f6..b30bd81b96 100644 --- a/MediaBrowser.Controller/Entities/BasePluginFolder.cs +++ b/MediaBrowser.Controller/Entities/BasePluginFolder.cs @@ -7,14 +7,9 @@ namespace MediaBrowser.Controller.Entities /// </summary> public abstract class BasePluginFolder : Folder, ICollectionFolder, IByReferenceItem { - protected BasePluginFolder() - { - DisplayMediaType = "CollectionFolder"; - } - public virtual string CollectionType { - get { return Model.Entities.CollectionType.BoxSets; } + get { return null; } } } } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 87b1cc7a3d..356f2b6034 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -4,8 +4,8 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MoreLinq; using System; using System.Collections; using System.Collections.Generic; @@ -14,18 +14,20 @@ using System.Linq; using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; +using MoreLinq; namespace MediaBrowser.Controller.Entities { /// <summary> /// Class Folder /// </summary> - public class Folder : BaseItem, IHasThemeMedia + public class Folder : BaseItem, IHasThemeMedia, IHasTags { public static IUserManager UserManager { get; set; } public List<Guid> ThemeSongIds { get; set; } public List<Guid> ThemeVideoIds { get; set; } + public List<string> Tags { get; set; } public Folder() { @@ -33,6 +35,13 @@ namespace MediaBrowser.Controller.Entities ThemeSongIds = new List<Guid>(); ThemeVideoIds = new List<Guid>(); + Tags = new List<string>(); + } + + [IgnoreDataMember] + public virtual bool IsPreSorted + { + get { return false; } } /// <summary> @@ -264,7 +273,7 @@ namespace MediaBrowser.Controller.Entities [IgnoreDataMember] public IEnumerable<BaseItem> Children { - get { return ActualChildren.Where(i => !i.IsHidden); } + get { return ActualChildren; } } /// <summary> @@ -445,11 +454,6 @@ namespace MediaBrowser.Controller.Entities cancellationToken.ThrowIfCancellationRequested(); - if (this is UserRootFolder) - { - var b = true; - } - foreach (var child in nonCachedChildren) { BaseItem currentChild; @@ -775,6 +779,11 @@ namespace MediaBrowser.Controller.Entities /// <exception cref="System.ArgumentNullException"></exception> public virtual IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren) { + return GetChildren(user, includeLinkedChildren, false); + } + + internal IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren, bool includeHidden) + { if (user == null) { throw new ArgumentNullException(); @@ -785,7 +794,7 @@ namespace MediaBrowser.Controller.Entities var list = new List<BaseItem>(); - var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, false, null); + var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, includeHidden, false); return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list; } @@ -801,10 +810,10 @@ namespace MediaBrowser.Controller.Entities /// <param name="user">The user.</param> /// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param> /// <param name="list">The list.</param> + /// <param name="includeHidden">if set to <c>true</c> [include hidden].</param> /// <param name="recursive">if set to <c>true</c> [recursive].</param> - /// <param name="filter">The filter.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - private bool AddChildrenToList(User user, bool includeLinkedChildren, List<BaseItem> list, bool recursive, Func<BaseItem, bool> filter) + private bool AddChildrenToList(User user, bool includeLinkedChildren, List<BaseItem> list, bool includeHidden, bool recursive) { var hasLinkedChildren = false; @@ -812,7 +821,7 @@ namespace MediaBrowser.Controller.Entities { if (child.IsVisible(user)) { - if (filter == null || filter(child)) + if (includeHidden || !child.IsHiddenFromUser(user)) { list.Add(child); } @@ -821,7 +830,7 @@ namespace MediaBrowser.Controller.Entities { var folder = (Folder)child; - if (folder.AddChildrenToList(user, includeLinkedChildren, list, true, filter)) + if (folder.AddChildrenToList(user, includeLinkedChildren, list, includeHidden, true)) { hasLinkedChildren = true; } @@ -831,13 +840,8 @@ namespace MediaBrowser.Controller.Entities if (includeLinkedChildren) { - foreach (var child in GetLinkedChildren()) + foreach (var child in GetLinkedChildren(user)) { - if (filter != null && !filter(child)) - { - continue; - } - if (child.IsVisible(user)) { hasLinkedChildren = true; @@ -857,20 +861,7 @@ namespace MediaBrowser.Controller.Entities /// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param> /// <returns>IEnumerable{BaseItem}.</returns> /// <exception cref="System.ArgumentNullException"></exception> - public IEnumerable<BaseItem> GetRecursiveChildren(User user, bool includeLinkedChildren = true) - { - return GetRecursiveChildren(user, null, includeLinkedChildren); - } - - /// <summary> - /// Gets the recursive children. - /// </summary> - /// <param name="user">The user.</param> - /// <param name="filter">The filter.</param> - /// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param> - /// <returns>IList{BaseItem}.</returns> - /// <exception cref="System.ArgumentNullException"></exception> - public IList<BaseItem> GetRecursiveChildren(User user, Func<BaseItem, bool> filter, bool includeLinkedChildren = true) + public virtual IEnumerable<BaseItem> GetRecursiveChildren(User user, bool includeLinkedChildren = true) { if (user == null) { @@ -879,7 +870,7 @@ namespace MediaBrowser.Controller.Entities var list = new List<BaseItem>(); - var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, true, filter); + var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, false, true); return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list; } @@ -890,19 +881,9 @@ namespace MediaBrowser.Controller.Entities /// <returns>IList{BaseItem}.</returns> public IList<BaseItem> GetRecursiveChildren() { - return GetRecursiveChildren(i => true); - } - - /// <summary> - /// Gets the recursive children. - /// </summary> - /// <param name="filter">The filter.</param> - /// <returns>IEnumerable{BaseItem}.</returns> - public IList<BaseItem> GetRecursiveChildren(Func<BaseItem, bool> filter) - { var list = new List<BaseItem>(); - AddChildrenToList(list, true, filter); + AddChildrenToList(list, true, null); return list; } @@ -943,6 +924,74 @@ namespace MediaBrowser.Controller.Entities .Where(i => i != null); } + protected virtual bool FilterLinkedChildrenPerUser + { + get + { + return false; + } + } + + public IEnumerable<BaseItem> GetLinkedChildren(User user) + { + if (!FilterLinkedChildrenPerUser || user == null) + { + return GetLinkedChildren(); + } + + var locations = user.RootFolder + .GetChildren(user, true) + .OfType<CollectionFolder>() + .SelectMany(i => i.PhysicalLocations) + .ToList(); + + return LinkedChildren + .Select(i => + { + var requiresPostFilter = true; + + if (!string.IsNullOrWhiteSpace(i.Path)) + { + requiresPostFilter = false; + + if (!locations.Any(l => FileSystem.ContainsSubPath(l, i.Path))) + { + return null; + } + } + + var child = GetLinkedChild(i); + + if (requiresPostFilter && child != null) + { + if (string.IsNullOrWhiteSpace(child.Path)) + { + Logger.Debug("Found LinkedChild with null path: {0}", child.Name); + return child; + } + + if (!locations.Any(l => FileSystem.ContainsSubPath(l, child.Path))) + { + return null; + } + } + + return child; + }) + .Where(i => i != null); + } + + /// <summary> + /// Gets the linked children. + /// </summary> + /// <returns>IEnumerable{BaseItem}.</returns> + public IEnumerable<Tuple<LinkedChild,BaseItem>> GetLinkedChildrenInfos() + { + return LinkedChildren + .Select(i => new Tuple<LinkedChild,BaseItem>(i, GetLinkedChild(i))) + .Where(i => i.Item2 != null); + } + protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken) { var changesFound = false; @@ -1104,9 +1153,67 @@ namespace MediaBrowser.Controller.Entities .All(i => i.IsUnplayed(user)); } - public IEnumerable<BaseItem> GetHiddenChildren() + public override void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user) { - return ActualChildren.Where(i => i.IsHidden); + var recursiveItemCount = 0; + var unplayed = 0; + + double totalPercentPlayed = 0; + + IEnumerable<BaseItem> children; + var folder = this; + + var season = folder as Season; + + if (season != null) + { + children = season.GetEpisodes(user).Where(i => i.LocationType != LocationType.Virtual); + } + else + { + children = folder.GetRecursiveChildren(user) + .Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual); + } + + // Loop through each recursive child + foreach (var child in children) + { + recursiveItemCount++; + + var isUnplayed = true; + + var itemUserData = UserDataManager.GetUserData(user.Id, child.GetUserDataKey()); + + // Incrememt totalPercentPlayed + if (itemUserData != null) + { + if (itemUserData.Played) + { + totalPercentPlayed += 100; + + isUnplayed = false; + } + else if (itemUserData.PlaybackPositionTicks > 0 && child.RunTimeTicks.HasValue && child.RunTimeTicks.Value > 0) + { + double itemPercent = itemUserData.PlaybackPositionTicks; + itemPercent /= child.RunTimeTicks.Value; + totalPercentPlayed += itemPercent; + } + } + + if (isUnplayed) + { + unplayed++; + } + } + + dto.UnplayedItemCount = unplayed; + + if (recursiveItemCount > 0) + { + dto.PlayedPercentage = totalPercentPlayed / recursiveItemCount; + dto.Played = dto.PlayedPercentage.Value >= 100; + } } } } diff --git a/MediaBrowser.Controller/Entities/IHasImages.cs b/MediaBrowser.Controller/Entities/IHasImages.cs index bac2263697..67acc1cae6 100644 --- a/MediaBrowser.Controller/Entities/IHasImages.cs +++ b/MediaBrowser.Controller/Entities/IHasImages.cs @@ -154,6 +154,12 @@ namespace MediaBrowser.Controller.Entities /// </summary> /// <value><c>true</c> if this instance is locked; otherwise, <c>false</c>.</value> bool IsLocked { get; } + + /// <summary> + /// Gets a value indicating whether [supports remote image downloading]. + /// </summary> + /// <value><c>true</c> if [supports remote image downloading]; otherwise, <c>false</c>.</value> + bool SupportsRemoteImageDownloading { get; } } public static class HasImagesExtensions diff --git a/MediaBrowser.Controller/Entities/IHasMediaSources.cs b/MediaBrowser.Controller/Entities/IHasMediaSources.cs index da040f296a..d487362f54 100644 --- a/MediaBrowser.Controller/Entities/IHasMediaSources.cs +++ b/MediaBrowser.Controller/Entities/IHasMediaSources.cs @@ -10,6 +10,12 @@ namespace MediaBrowser.Controller.Entities public interface IHasMediaSources { /// <summary> + /// Gets the identifier. + /// </summary> + /// <value>The identifier.</value> + Guid Id { get; } + + /// <summary> /// Gets the media sources. /// </summary> /// <param name="enablePathSubstitution">if set to <c>true</c> [enable path substitution].</param> diff --git a/MediaBrowser.Controller/Entities/IHasUserData.cs b/MediaBrowser.Controller/Entities/IHasUserData.cs index 780181a61d..d576d90c45 100644 --- a/MediaBrowser.Controller/Entities/IHasUserData.cs +++ b/MediaBrowser.Controller/Entities/IHasUserData.cs @@ -1,4 +1,6 @@ - +using MediaBrowser.Model.Dto; +using System; + namespace MediaBrowser.Controller.Entities { /// <summary> @@ -7,9 +9,23 @@ namespace MediaBrowser.Controller.Entities public interface IHasUserData { /// <summary> + /// Gets or sets the identifier. + /// </summary> + /// <value>The identifier.</value> + Guid Id { get; set; } + + /// <summary> /// Gets the user data key. /// </summary> /// <returns>System.String.</returns> string GetUserDataKey(); + + /// <summary> + /// Fills the user data dto values. + /// </summary> + /// <param name="dto">The dto.</param> + /// <param name="userData">The user data.</param> + /// <param name="user">The user.</param> + void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user); } } diff --git a/MediaBrowser.Controller/Entities/IThemeMedia.cs b/MediaBrowser.Controller/Entities/IThemeMedia.cs new file mode 100644 index 0000000000..b2eff230ff --- /dev/null +++ b/MediaBrowser.Controller/Entities/IThemeMedia.cs @@ -0,0 +1,8 @@ + +namespace MediaBrowser.Controller.Entities +{ + public interface IThemeMedia + { + bool IsThemeMedia { get; } + } +} diff --git a/MediaBrowser.Controller/Entities/LinkedChild.cs b/MediaBrowser.Controller/Entities/LinkedChild.cs index 1ae04e40f9..78e8e49594 100644 --- a/MediaBrowser.Controller/Entities/LinkedChild.cs +++ b/MediaBrowser.Controller/Entities/LinkedChild.cs @@ -13,11 +13,27 @@ namespace MediaBrowser.Controller.Entities public string ItemType { get; set; } public int? ItemYear { get; set; } + [IgnoreDataMember] + public string Id { get; set; } + /// <summary> /// Serves as a cache /// </summary> - [IgnoreDataMember] public Guid? ItemId { get; set; } + + public static LinkedChild Create(BaseItem item) + { + return new LinkedChild + { + Path = item.Path, + Type = LinkedChildType.Manual + }; + } + + public LinkedChild() + { + Id = Guid.NewGuid().ToString("N"); + } } public enum LinkedChildType diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index b0cb549f4b..705cf90575 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -6,6 +6,7 @@ using MediaBrowser.Model.Querying; using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; @@ -14,18 +15,25 @@ namespace MediaBrowser.Controller.Entities.Movies /// <summary> /// Class BoxSet /// </summary> - public class BoxSet : Folder, IHasTrailers, IHasTags, IHasKeywords, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IMetadataContainer + public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IMetadataContainer { public BoxSet() { RemoteTrailers = new List<MediaUrl>(); LocalTrailerIds = new List<Guid>(); - Tags = new List<string>(); DisplayOrder = ItemSortBy.PremiereDate; Keywords = new List<string>(); } + protected override bool FilterLinkedChildrenPerUser + { + get + { + return true; + } + } + public List<Guid> LocalTrailerIds { get; set; } /// <summary> @@ -38,7 +46,6 @@ namespace MediaBrowser.Controller.Entities.Movies /// Gets or sets the tags. /// </summary> /// <value>The tags.</value> - public List<string> Tags { get; set; } public List<string> Keywords { get; set; } public string PreferredMetadataLanguage { get; set; } @@ -60,6 +67,15 @@ namespace MediaBrowser.Controller.Entities.Movies return config.BlockUnratedItems.Contains(UnratedItem.Movie); } + [IgnoreDataMember] + public override bool IsPreSorted + { + get + { + return true; + } + } + public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren) { var children = base.GetChildren(user, includeLinkedChildren); @@ -75,7 +91,7 @@ namespace MediaBrowser.Controller.Entities.Movies // Sort by release date return LibraryManager.Sort(children, user, new[] { ItemSortBy.ProductionYear, ItemSortBy.PremiereDate, ItemSortBy.SortName }, SortOrder.Ascending); } - + // Default sorting return LibraryManager.Sort(children, user, new[] { ItemSortBy.ProductionYear, ItemSortBy.PremiereDate, ItemSortBy.SortName }, SortOrder.Ascending); } @@ -94,17 +110,9 @@ namespace MediaBrowser.Controller.Entities.Movies var totalItems = items.Count; var percentages = new Dictionary<Guid, double>(totalItems); - var tasks = new List<Task>(); - // Refresh songs foreach (var item in items) { - if (tasks.Count >= 3) - { - await Task.WhenAll(tasks).ConfigureAwait(false); - tasks.Clear(); - } - cancellationToken.ThrowIfCancellationRequested(); var innerProgress = new ActionableProgress<double>(); @@ -124,13 +132,9 @@ namespace MediaBrowser.Controller.Entities.Movies }); // Avoid implicitly captured closure - var taskChild = item; - tasks.Add(Task.Run(async () => await RefreshItem(taskChild, refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false), cancellationToken)); + await RefreshItem(item, refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false); } - await Task.WhenAll(tasks).ConfigureAwait(false); - tasks.Clear(); - // Refresh current item await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index 54cb3fcc94..5510c795a0 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -14,7 +14,7 @@ namespace MediaBrowser.Controller.Entities.Movies /// <summary> /// Class Movie /// </summary> - public class Movie : Video, IHasCriticRating, IHasSoundtracks, IHasProductionLocations, IHasBudget, IHasKeywords, IHasTrailers, IHasThemeMedia, IHasTaglines, IHasPreferredMetadataLanguage, IHasAwards, IHasMetascore, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping, IHasShortOverview + public class Movie : Video, IHasCriticRating, IHasSoundtracks, IHasProductionLocations, IHasBudget, IHasKeywords, IHasTrailers, IHasThemeMedia, IHasTaglines, IHasPreferredMetadataLanguage, IHasAwards, IHasMetascore, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping { public List<Guid> SpecialFeatureIds { get; set; } @@ -52,7 +52,6 @@ namespace MediaBrowser.Controller.Entities.Movies ProductionLocations = new List<string>(); } - public string ShortOverview { get; set; } public string AwardSummary { get; set; } public float? Metascore { get; set; } diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs index 1def47391a..fe8d618362 100644 --- a/MediaBrowser.Controller/Entities/Person.cs +++ b/MediaBrowser.Controller/Entities/Person.cs @@ -96,5 +96,10 @@ namespace MediaBrowser.Controller.Entities { return Name; } + + public bool IsType(string type) + { + return string.Equals(Type, type, StringComparison.OrdinalIgnoreCase) || string.Equals(Role, type, StringComparison.OrdinalIgnoreCase); + } } } diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs index 96995c315b..367db5dcb5 100644 --- a/MediaBrowser.Controller/Entities/Photo.cs +++ b/MediaBrowser.Controller/Entities/Photo.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Drawing; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; namespace MediaBrowser.Controller.Entities { @@ -13,6 +17,16 @@ namespace MediaBrowser.Controller.Entities Taglines = new List<string>(); } + [IgnoreDataMember] + public override bool SupportsLocalMetadata + { + get + { + return false; + } + } + + [IgnoreDataMember] public override string MediaType { get @@ -20,5 +34,45 @@ namespace MediaBrowser.Controller.Entities return Model.Entities.MediaType.Photo; } } + + [IgnoreDataMember] + public override Folder LatestItemsIndexContainer + { + get + { + return Album; + } + } + + + [IgnoreDataMember] + public PhotoAlbum Album + { + get + { + return Parents.OfType<PhotoAlbum>().FirstOrDefault(); + } + } + + public int? Width { get; set; } + public int? Height { get; set; } + public string CameraMake { get; set; } + public string CameraModel { get; set; } + public string Software { get; set; } + public double? ExposureTime { get; set; } + public double? FocalLength { get; set; } + public ImageOrientation? Orientation { get; set; } + public double? Aperture { get; set; } + public double? ShutterSpeed { get; set; } + + public double? Latitude { get; set; } + public double? Longitude { get; set; } + public double? Altitude { get; set; } + public int? IsoSpeedRating { get; set; } + + protected override bool GetBlockUnratedValue(UserConfiguration config) + { + return config.BlockUnratedItems.Contains(UnratedItem.Other); + } } } diff --git a/MediaBrowser.Controller/Entities/PhotoAlbum.cs b/MediaBrowser.Controller/Entities/PhotoAlbum.cs new file mode 100644 index 0000000000..7af4109f32 --- /dev/null +++ b/MediaBrowser.Controller/Entities/PhotoAlbum.cs @@ -0,0 +1,21 @@ +using MediaBrowser.Model.Configuration; +using System.Linq; + +namespace MediaBrowser.Controller.Entities +{ + public class PhotoAlbum : Folder + { + public override bool SupportsLocalMetadata + { + get + { + return false; + } + } + + protected override bool GetBlockUnratedValue(UserConfiguration config) + { + return config.BlockUnratedItems.Contains(UnratedItem.Other); + } + } +} diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs index 8271a3df20..0d934ad0a5 100644 --- a/MediaBrowser.Controller/Entities/Studio.cs +++ b/MediaBrowser.Controller/Entities/Studio.cs @@ -7,8 +7,15 @@ namespace MediaBrowser.Controller.Entities /// <summary> /// Class Studio /// </summary> - public class Studio : BaseItem, IItemByName + public class Studio : BaseItem, IItemByName, IHasTags { + public List<string> Tags { get; set; } + + public Studio() + { + Tags = new List<string>(); + } + /// <summary> /// Gets the user data key. /// </summary> diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index b9630a66f4..b95c7df9c5 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -91,7 +91,16 @@ namespace MediaBrowser.Controller.Entities.TV { get { - return FindParent<Season>(); + return Season; + } + } + + [IgnoreDataMember] + public override Folder LatestItemsIndexContainer + { + get + { + return Series; } } @@ -181,6 +190,20 @@ namespace MediaBrowser.Controller.Entities.TV } [IgnoreDataMember] + public override bool SupportsRemoteImageDownloading + { + get + { + if (IsMissingEpisode) + { + return false; + } + + return true; + } + } + + [IgnoreDataMember] public bool IsMissingEpisode { get diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index cf39cda899..6804b29b73 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -29,6 +29,21 @@ namespace MediaBrowser.Controller.Entities.TV } } + [IgnoreDataMember] + public override bool SupportsAddingToPlaylist + { + get { return true; } + } + + [IgnoreDataMember] + public override bool IsPreSorted + { + get + { + return true; + } + } + /// <summary> /// We want to group into our Series /// </summary> diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index a9a2b024f4..d3b95eb0c6 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -13,7 +13,7 @@ namespace MediaBrowser.Controller.Entities.TV /// <summary> /// Class Series /// </summary> - public class Series : Folder, IHasSoundtracks, IHasTrailers, IHasTags, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo<SeriesInfo> + public class Series : Folder, IHasSoundtracks, IHasTrailers, IHasPreferredMetadataLanguage, IHasDisplayOrder, IHasLookupInfo<SeriesInfo> { public List<Guid> SpecialFeatureIds { get; set; } public List<Guid> SoundtrackIds { get; set; } @@ -36,10 +36,24 @@ namespace MediaBrowser.Controller.Entities.TV SoundtrackIds = new List<Guid>(); RemoteTrailers = new List<MediaUrl>(); LocalTrailerIds = new List<Guid>(); - Tags = new List<string>(); DisplaySpecialsWithSeasons = true; } + [IgnoreDataMember] + public override bool SupportsAddingToPlaylist + { + get { return true; } + } + + [IgnoreDataMember] + public override bool IsPreSorted + { + get + { + return true; + } + } + public bool DisplaySpecialsWithSeasons { get; set; } public List<Guid> LocalTrailerIds { get; set; } @@ -52,12 +66,6 @@ namespace MediaBrowser.Controller.Entities.TV public string DisplayOrder { get; set; } /// <summary> - /// Gets or sets the tags. - /// </summary> - /// <value>The tags.</value> - public List<string> Tags { get; set; } - - /// <summary> /// Gets or sets the status. /// </summary> /// <value>The status.</value> diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs index b9c419722d..e4fd929ffd 100644 --- a/MediaBrowser.Controller/Entities/User.cs +++ b/MediaBrowser.Controller/Entities/User.cs @@ -24,6 +24,7 @@ namespace MediaBrowser.Controller.Entities /// </summary> /// <value>The password.</value> public string Password { get; set; } + public string LocalPassword { get; set; } /// <summary> /// Gets or sets the path. diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs index e5a8135c2d..6404e71ec6 100644 --- a/MediaBrowser.Controller/Entities/UserRootFolder.cs +++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Dto; using System; using System.Collections.Generic; using System.Linq; @@ -51,5 +52,10 @@ namespace MediaBrowser.Controller.Entities LibraryManager.RegisterItem(item); } } + + public override void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user) + { + // Nothing meaninful here and will only waste resources + } } } diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index ce188554cd..2abc71752c 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -1,15 +1,20 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.LiveTv; using System; using System.Collections.Generic; using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace MediaBrowser.Controller.Entities { public class UserView : Folder { public string ViewType { get; set; } + public static IUserViewManager UserViewManager { get; set; } public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren) { @@ -17,17 +22,50 @@ namespace MediaBrowser.Controller.Entities switch (ViewType) { + case CollectionType.LiveTvChannels: + return LiveTvManager.GetInternalChannels(new LiveTvChannelQuery + { + UserId = user.Id.ToString("N") + + }, CancellationToken.None).Result.Items; + case CollectionType.LiveTvRecordingGroups: + return LiveTvManager.GetInternalRecordings(new RecordingQuery + { + UserId = user.Id.ToString("N"), + Status = RecordingStatus.Completed + + }, CancellationToken.None).Result.Items; + case CollectionType.LiveTv: + return GetLiveTvFolders(user).Result; + case CollectionType.Folders: + return user.RootFolder.GetChildren(user, includeLinkedChildren); case CollectionType.Games: - return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)).OfType<GameSystem>(); + return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)) + .OfType<GameSystem>(); case CollectionType.BoxSets: - return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)).OfType<BoxSet>(); + return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)) + .OfType<BoxSet>(); case CollectionType.TvShows: - return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)).OfType<Series>(); + return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)) + .OfType<Series>(); + case CollectionType.Trailers: + return mediaFolders.SelectMany(i => i.GetRecursiveChildren(user, includeLinkedChildren)) + .OfType<Trailer>(); default: return mediaFolders.SelectMany(i => i.GetChildren(user, includeLinkedChildren)); } } + private async Task<IEnumerable<BaseItem>> GetLiveTvFolders(User user) + { + var list = new List<BaseItem>(); + + list.Add(await UserViewManager.GetUserView(CollectionType.LiveTvChannels, user, string.Empty, CancellationToken.None).ConfigureAwait(false)); + list.Add(await UserViewManager.GetUserView(CollectionType.LiveTvRecordingGroups, user, string.Empty, CancellationToken.None).ConfigureAwait(false)); + + return list; + } + protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user) { return GetChildren(user, false); @@ -38,7 +76,7 @@ namespace MediaBrowser.Controller.Entities var excludeFolderIds = user.Configuration.ExcludeFoldersFromGrouping.Select(i => new Guid(i)).ToList(); return user.RootFolder - .GetChildren(user, true) + .GetChildren(user, true, true) .OfType<Folder>() .Where(i => !excludeFolderIds.Contains(i.Id) && !IsExcludedFromGrouping(i)); } @@ -64,4 +102,30 @@ namespace MediaBrowser.Controller.Entities return standaloneTypes.Contains(collectionFolder.CollectionType ?? string.Empty); } } + + public class SpecialFolder : Folder + { + public SpecialFolderType SpecialFolderType { get; set; } + public string ItemTypeName { get; set; } + public string ParentId { get; set; } + + public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren) + { + var parent = (Folder)LibraryManager.GetItemById(new Guid(ParentId)); + + if (SpecialFolderType == SpecialFolderType.ItemsByType) + { + var items = parent.GetRecursiveChildren(user, includeLinkedChildren); + + return items.Where(i => string.Equals(i.GetType().Name, ItemTypeName, StringComparison.OrdinalIgnoreCase)); + } + + return new List<BaseItem>(); + } + } + + public enum SpecialFolderType + { + ItemsByType = 1 + } } diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 61404949e4..492a4a02f9 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -23,7 +23,9 @@ namespace MediaBrowser.Controller.Entities IHasAspectRatio, IHasTags, ISupportsPlaceHolders, - IHasMediaSources + IHasMediaSources, + IHasShortOverview, + IThemeMedia { public bool IsMultiPart { get; set; } public bool HasLocalAlternateVersions { get; set; } @@ -32,10 +34,13 @@ namespace MediaBrowser.Controller.Entities public List<Guid> AdditionalPartIds { get; set; } public List<Guid> LocalAlternateVersionIds { get; set; } + public bool IsThemeMedia { get; set; } + public string FormatName { get; set; } public long? Size { get; set; } public string Container { get; set; } public int? TotalBitrate { get; set; } + public string ShortOverview { get; set; } /// <summary> /// Gets or sets the timestamp. @@ -54,6 +59,12 @@ namespace MediaBrowser.Controller.Entities } [IgnoreDataMember] + public override bool SupportsAddingToPlaylist + { + get { return LocationType == LocationType.FileSystem && RunTimeTicks.HasValue; } + } + + [IgnoreDataMember] public int MediaSourceCount { get @@ -228,6 +239,7 @@ namespace MediaBrowser.Controller.Entities /// Gets the type of the media. /// </summary> /// <value>The type of the media.</value> + [IgnoreDataMember] public override string MediaType { get @@ -324,7 +336,7 @@ namespace MediaBrowser.Controller.Entities { if ((i.Attributes & FileAttributes.Directory) == FileAttributes.Directory) { - return !string.Equals(i.FullName, path, StringComparison.OrdinalIgnoreCase) && EntityResolutionHelper.IsMultiPartFolder(i.FullName) && EntityResolutionHelper.IsMultiPartFile(i.Name); + return !string.Equals(i.FullName, path, StringComparison.OrdinalIgnoreCase) && EntityResolutionHelper.IsMultiPartFolder(i.FullName); } return false; diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 21f5fa87a3..2af37e84dc 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -40,5 +40,17 @@ namespace MediaBrowser.Controller /// </summary> /// <value><c>true</c> if this instance has update available; otherwise, <c>false</c>.</value> bool HasUpdateAvailable { get; } + + /// <summary> + /// Gets or sets the server identifier. + /// </summary> + /// <value>The server identifier.</value> + string ServerId { get; } + + /// <summary> + /// Gets the name of the friendly. + /// </summary> + /// <value>The name of the friendly.</value> + string FriendlyName { get; } } } diff --git a/MediaBrowser.Controller/Library/IUserDataManager.cs b/MediaBrowser.Controller/Library/IUserDataManager.cs index 2bec9e3deb..9db91e7f22 100644 --- a/MediaBrowser.Controller/Library/IUserDataManager.cs +++ b/MediaBrowser.Controller/Library/IUserDataManager.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using System; using System.Threading; @@ -34,5 +35,13 @@ namespace MediaBrowser.Controller.Library /// <param name="key">The key.</param> /// <returns>Task{UserItemData}.</returns> UserItemData GetUserData(Guid userId, string key); + + /// <summary> + /// Gets the user data dto. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="user">The user.</param> + /// <returns>UserItemDataDto.</returns> + UserItemDataDto GetUserDataDto(IHasUserData item, User user); } } diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index 010caa2337..a5d949c8a2 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -1,5 +1,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Events; using System; using System.Collections.Generic; @@ -31,6 +32,7 @@ namespace MediaBrowser.Controller.Library event EventHandler<GenericEventArgs<User>> UserCreated; event EventHandler<GenericEventArgs<User>> UserConfigurationUpdated; + event EventHandler<GenericEventArgs<User>> UserPasswordChanged; /// <summary> /// Updates the configuration. @@ -50,12 +52,13 @@ namespace MediaBrowser.Controller.Library /// <summary> /// Authenticates a User and returns a result indicating whether or not it succeeded /// </summary> - /// <param name="user">The user.</param> + /// <param name="username">The username.</param> /// <param name="password">The password.</param> + /// <param name="remoteEndPoint">The remote end point.</param> /// <returns>Task{System.Boolean}.</returns> /// <exception cref="System.ArgumentNullException">user</exception> - Task<bool> AuthenticateUser(User user, string password); - + Task<bool> AuthenticateUser(string username, string password, string remoteEndPoint); + /// <summary> /// Refreshes metadata for each user /// </summary> @@ -113,5 +116,13 @@ namespace MediaBrowser.Controller.Library /// <param name="newPassword">The new password.</param> /// <returns>Task.</returns> Task ChangePassword(User user, string newPassword); + + /// <summary> + /// Gets the user dto. + /// </summary> + /// <param name="user">The user.</param> + /// <param name="remoteEndPoint">The remote end point.</param> + /// <returns>UserDto.</returns> + UserDto GetUserDto(User user, string remoteEndPoint = null); } } diff --git a/MediaBrowser.Controller/Library/IUserViewManager.cs b/MediaBrowser.Controller/Library/IUserViewManager.cs index 7c352d97a7..908525a2f8 100644 --- a/MediaBrowser.Controller/Library/IUserViewManager.cs +++ b/MediaBrowser.Controller/Library/IUserViewManager.cs @@ -9,5 +9,7 @@ namespace MediaBrowser.Controller.Library public interface IUserViewManager { Task<IEnumerable<Folder>> GetUserViews(UserViewQuery query, CancellationToken cancellationToken); + + Task<UserView> GetUserView(string type, User user, string sortName, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/Library/TVUtils.cs b/MediaBrowser.Controller/Library/TVUtils.cs index 64f3a3b4b7..34486182b7 100644 --- a/MediaBrowser.Controller/Library/TVUtils.cs +++ b/MediaBrowser.Controller/Library/TVUtils.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Entities; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; using System; @@ -7,6 +8,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Text.RegularExpressions; +using MediaBrowser.Model.Logging; namespace MediaBrowser.Controller.Library { @@ -98,19 +100,19 @@ namespace MediaBrowser.Controller.Library private static readonly Regex[] EpisodeExpressionsWithoutSeason = { new Regex( - @".*[\\\/](?<epnumber>\d{1,3})\.\w+$", + @".*[\\\/](?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*\.\w+$", RegexOptions.Compiled), // "01.avi" new Regex( - @".*(\\|\/)(?<epnumber>\d{1,2})\s?-\s?[^\\\/]*$", + @".*(\\|\/)(?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*\s?-\s?[^\\\/]*$", RegexOptions.Compiled), // "01 - blah.avi", "01-blah.avi" new Regex( - @".*(\\|\/)(?<epnumber>\d{1,2})\.[^\\\/]+$", + @".*(\\|\/)(?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*\.[^\\\/]+$", RegexOptions.Compiled), // "01.blah.avi" new Regex( - @".*[\\\/][^\\\/]* - (?<epnumber>\d{1,3})[^\\\/]*$", + @".*[\\\/][^\\\/]* - (?<epnumber>\d{1,3})(-(?<endingepnumber>\d{2,3}))*[^\\\/]*$", RegexOptions.Compiled), // "blah - 01.avi", "blah 2 - 01.avi", "blah - 01 blah.avi", "blah 2 - 01 blah", "blah - 01 - blah.avi", "blah 2 - 01 - blah" }; @@ -124,11 +126,27 @@ namespace MediaBrowser.Controller.Library { var filename = Path.GetFileName(path); - if (string.Equals(path, "specials", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(filename, "specials", StringComparison.OrdinalIgnoreCase)) { return 0; } + int val; + if (int.TryParse(filename, NumberStyles.Integer, CultureInfo.InvariantCulture, out val)) + { + return val; + } + + if (filename.StartsWith("s", StringComparison.OrdinalIgnoreCase)) + { + var testFilename = filename.Substring(1); + + if (int.TryParse(testFilename, NumberStyles.Integer, CultureInfo.InvariantCulture, out val)) + { + return val; + } + } + // Look for one of the season folder names foreach (var name in SeasonFolderNames) { @@ -175,7 +193,7 @@ namespace MediaBrowser.Controller.Library return null; } - return int.Parse(path.Substring(numericStart, length)); + return int.Parse(path.Substring(numericStart, length), CultureInfo.InvariantCulture); } /// <summary> @@ -183,20 +201,64 @@ namespace MediaBrowser.Controller.Library /// </summary> /// <param name="path">The path.</param> /// <param name="directoryService">The directory service.</param> + /// <param name="fileSystem">The file system.</param> /// <returns><c>true</c> if [is season folder] [the specified path]; otherwise, <c>false</c>.</returns> - private static bool IsSeasonFolder(string path, IDirectoryService directoryService) + private static bool IsSeasonFolder(string path, IDirectoryService directoryService, IFileSystem fileSystem) { + var seasonNumber = GetSeasonNumberFromPath(path); + var hasSeasonNumber = seasonNumber != null; + + if (!hasSeasonNumber) + { + return false; + } + // It's a season folder if it's named as such and does not contain any audio files, apart from theme.mp3 - return GetSeasonNumberFromPath(path) != null && !directoryService.GetFiles(path).Any(i => EntityResolutionHelper.IsAudioFile(i.FullName) && !string.Equals(Path.GetFileNameWithoutExtension(i.FullName), BaseItem.ThemeSongFilename)); + foreach (var fileSystemInfo in directoryService.GetFileSystemEntries(path)) + { + var attributes = fileSystemInfo.Attributes; + + if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden) + { + continue; + } + + // Can't enforce this because files saved by Bitcasa are always marked System + //if ((attributes & FileAttributes.System) == FileAttributes.System) + //{ + // continue; + //} + + if ((attributes & FileAttributes.Directory) == FileAttributes.Directory) + { + //if (IsBadFolder(fileSystemInfo.Name)) + //{ + // return false; + //} + } + else + { + if (EntityResolutionHelper.IsAudioFile(fileSystemInfo.FullName) && + !string.Equals(fileSystem.GetFileNameWithoutExtension(fileSystemInfo), BaseItem.ThemeSongFilename)) + { + return false; + } + } + } + + return true; } /// <summary> /// Determines whether [is series folder] [the specified path]. /// </summary> /// <param name="path">The path.</param> + /// <param name="considerSeasonlessEntries">if set to <c>true</c> [consider seasonless entries].</param> /// <param name="fileSystemChildren">The file system children.</param> + /// <param name="directoryService">The directory service.</param> + /// <param name="fileSystem">The file system.</param> /// <returns><c>true</c> if [is series folder] [the specified path]; otherwise, <c>false</c>.</returns> - public static bool IsSeriesFolder(string path, bool considerSeasonlessSeries, IEnumerable<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService) + public static bool IsSeriesFolder(string path, bool considerSeasonlessEntries, IEnumerable<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService, IFileSystem fileSystem, ILogger logger) { // A folder with more than 3 non-season folders in will not becounted as a series var nonSeriesFolders = 0; @@ -207,25 +269,35 @@ namespace MediaBrowser.Controller.Library if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden) { + //logger.Debug("Igoring series file or folder marked hidden: {0}", child.FullName); continue; } - if ((attributes & FileAttributes.System) == FileAttributes.System) - { - continue; - } + // Can't enforce this because files saved by Bitcasa are always marked System + //if ((attributes & FileAttributes.System) == FileAttributes.System) + //{ + // logger.Debug("Igoring series subfolder marked system: {0}", child.FullName); + // continue; + //} if ((attributes & FileAttributes.Directory) == FileAttributes.Directory) { - if (IsSeasonFolder(child.FullName, directoryService)) + if (IsSeasonFolder(child.FullName, directoryService, fileSystem)) { + logger.Debug("{0} is a series because of season folder {1}.", path, child.FullName); return true; } - nonSeriesFolders++; + if (IsBadFolder(child.Name)) + { + logger.Debug("Invalid folder under series: {0}", child.FullName); + + nonSeriesFolders++; + } if (nonSeriesFolders >= 3) { + logger.Debug("{0} not a series due to 3 or more invalid folders.", path); return false; } } @@ -235,7 +307,7 @@ namespace MediaBrowser.Controller.Library if (EntityResolutionHelper.IsVideoFile(fullName) || EntityResolutionHelper.IsVideoPlaceHolder(fullName)) { - if (GetEpisodeNumberFromFile(fullName, considerSeasonlessSeries).HasValue) + if (GetEpisodeNumberFromFile(fullName, considerSeasonlessEntries).HasValue) { return true; } @@ -243,9 +315,28 @@ namespace MediaBrowser.Controller.Library } } + logger.Debug("{0} is not a series folder.", path); return false; } + private static bool IsBadFolder(string name) + { + if (string.Equals(name, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + if (string.Equals(name, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + if (string.Equals(name, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + return !EntityResolutionHelper.IgnoreFolders.Contains(name, StringComparer.OrdinalIgnoreCase); + } + /// <summary> /// Episodes the number from file. /// </summary> @@ -284,6 +375,12 @@ namespace MediaBrowser.Controller.Library if (m.Success && !string.IsNullOrEmpty(m.Groups["endingepnumber"].Value)) return ParseEpisodeNumber(m.Groups["endingepnumber"].Value); } + foreach (var r in EpisodeExpressionsWithoutSeason) + { + var m = r.Match(fl); + if (m.Success && !string.IsNullOrEmpty(m.Groups["endingepnumber"].Value)) + return ParseEpisodeNumber(m.Groups["endingepnumber"].Value); + } return null; } diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index d40ecb463c..b1c6ebffc8 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -280,5 +280,22 @@ namespace MediaBrowser.Controller.LiveTv /// </summary> /// <returns>IEnumerable{User}.</returns> IEnumerable<User> GetEnabledUsers(); + + /// <summary> + /// Gets the internal channels. + /// </summary> + /// <param name="query">The query.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task<QueryResult<LiveTvChannel>>.</returns> + Task<QueryResult<LiveTvChannel>> GetInternalChannels(LiveTvChannelQuery query, + CancellationToken cancellationToken); + + /// <summary> + /// Gets the internal recordings. + /// </summary> + /// <param name="query">The query.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task<QueryResult<BaseItem>>.</returns> + Task<QueryResult<BaseItem>> GetInternalRecordings(RecordingQuery query, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs b/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs index 23610351e7..ba1cb30436 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Controller.LiveTv { - public interface ILiveTvRecording : IHasImages, IHasMediaSources + public interface ILiveTvRecording : IHasImages, IHasMediaSources, IHasUserData { string ServiceName { get; set; } @@ -20,8 +20,6 @@ namespace MediaBrowser.Controller.LiveTv string GetClientTypeName(); - string GetUserDataKey(); - bool IsParentalAllowed(User user); Task RefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken); diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index 01fed68c8a..f2fa912cad 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -3,13 +3,13 @@ using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.LiveTv; +using MediaBrowser.Model.MediaInfo; using System.Collections.Generic; using System.Linq; -using MediaBrowser.Model.MediaInfo; namespace MediaBrowser.Controller.LiveTv { - public class LiveTvChannel : BaseItem, IItemByName, IHasMediaSources + public class LiveTvChannel : BaseItem, IHasMediaSources { /// <summary> /// Gets the user data key. diff --git a/MediaBrowser.Controller/LiveTv/RecordingGroup.cs b/MediaBrowser.Controller/LiveTv/RecordingGroup.cs new file mode 100644 index 0000000000..7bd810b8d1 --- /dev/null +++ b/MediaBrowser.Controller/LiveTv/RecordingGroup.cs @@ -0,0 +1,22 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Configuration; + +namespace MediaBrowser.Controller.LiveTv +{ + public class RecordingGroup : Folder + { + protected override bool GetBlockUnratedValue(UserConfiguration config) + { + // Don't block. + return false; + } + + public override bool SupportsLocalMetadata + { + get + { + return false; + } + } + } +} diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 1310e77973..6a78fa5d9f 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -48,6 +48,9 @@ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> </PropertyGroup> <ItemGroup> + <Reference Include="MoreLinq"> + <HintPath>..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll</HintPath> + </Reference> <Reference Include="System" /> <Reference Include="System.Core" /> <Reference Include="System.Data" /> @@ -57,9 +60,6 @@ <Reference Include="Microsoft.CSharp" /> <Reference Include="System.Xml" /> <Reference Include="System.Xml.Linq" /> - <Reference Include="MoreLinq"> - <HintPath>..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll</HintPath> - </Reference> <Reference Include="ServiceStack.Interfaces"> <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath> </Reference> @@ -68,6 +68,8 @@ <Compile Include="..\SharedVersion.cs"> <Link>Properties\SharedVersion.cs</Link> </Compile> + <Compile Include="Activity\IActivityManager.cs" /> + <Compile Include="Activity\IActivityRepository.cs" /> <Compile Include="Channels\ChannelFolderItem.cs" /> <Compile Include="Channels\ChannelItemInfo.cs" /> <Compile Include="Channels\ChannelItemResult.cs" /> @@ -95,7 +97,9 @@ <Compile Include="Chapters\IChapterProvider.cs" /> <Compile Include="Chapters\ChapterResponse.cs" /> <Compile Include="Collections\CollectionCreationOptions.cs" /> + <Compile Include="Collections\CollectionEvents.cs" /> <Compile Include="Collections\ICollectionManager.cs" /> + <Compile Include="Connect\IConnectManager.cs" /> <Compile Include="Dlna\ControlRequest.cs" /> <Compile Include="Dlna\ControlResponse.cs" /> <Compile Include="Dlna\DlnaIconResponse.cs" /> @@ -147,10 +151,12 @@ <Compile Include="Entities\ISupportsBoxSetGrouping.cs" /> <Compile Include="Entities\ISupportsPlaceHolders.cs" /> <Compile Include="Entities\ItemImageInfo.cs" /> + <Compile Include="Entities\IThemeMedia.cs" /> <Compile Include="Entities\LinkedChild.cs" /> <Compile Include="Entities\MusicVideo.cs" /> <Compile Include="Entities\IHasAwards.cs" /> <Compile Include="Entities\Photo.cs" /> + <Compile Include="Entities\PhotoAlbum.cs" /> <Compile Include="Entities\UserView.cs" /> <Compile Include="FileOrganization\IFileOrganizationService.cs" /> <Compile Include="Library\DeleteOptions.cs" /> @@ -165,6 +171,7 @@ <Compile Include="Library\LibraryManagerExtensions.cs" /> <Compile Include="Library\PlaybackStopEventArgs.cs" /> <Compile Include="Library\UserDataSaveEventArgs.cs" /> + <Compile Include="LiveTv\RecordingGroup.cs" /> <Compile Include="LiveTv\RecordingStatusChangedEventArgs.cs" /> <Compile Include="LiveTv\ILiveTvRecording.cs" /> <Compile Include="LiveTv\LiveStreamInfo.cs" /> @@ -195,10 +202,19 @@ <Compile Include="MediaEncoding\ISubtitleEncoder.cs" /> <Compile Include="MediaEncoding\MediaStreamSelector.cs" /> <Compile Include="MediaEncoding\VideoEncodingOptions.cs" /> + <Compile Include="Net\AuthenticatedAttribute.cs" /> + <Compile Include="Net\AuthorizationInfo.cs" /> + <Compile Include="Net\IAuthorizationContext.cs" /> + <Compile Include="Net\IAuthService.cs" /> + <Compile Include="Net\IHasAuthorization.cs" /> <Compile Include="Net\IHasResultFactory.cs" /> + <Compile Include="Net\IHasSession.cs" /> <Compile Include="Net\IHttpResultFactory.cs" /> <Compile Include="Net\IHttpServer.cs" /> <Compile Include="Net\IRestfulService.cs" /> + <Compile Include="Net\ISessionContext.cs" /> + <Compile Include="Net\LoggedAttribute.cs" /> + <Compile Include="Net\StaticResultOptions.cs" /> <Compile Include="News\INewsService.cs" /> <Compile Include="Notifications\INotificationManager.cs" /> <Compile Include="Notifications\INotificationService.cs" /> @@ -208,6 +224,8 @@ <Compile Include="Notifications\UserNotification.cs" /> <Compile Include="Persistence\IFileOrganizationRepository.cs" /> <Compile Include="Persistence\MediaStreamQuery.cs" /> + <Compile Include="Playlists\IPlaylistManager.cs" /> + <Compile Include="Playlists\Playlist.cs" /> <Compile Include="Providers\DirectoryService.cs" /> <Compile Include="Providers\ICustomMetadataProvider.cs" /> <Compile Include="Providers\IExternalId.cs" /> @@ -224,7 +242,12 @@ <Compile Include="Providers\IMetadataService.cs" /> <Compile Include="Providers\IRemoteMetadataProvider.cs" /> <Compile Include="Providers\VideoContentType.cs" /> + <Compile Include="RelatedMedia\IRelatedMediaProvider.cs" /> + <Compile Include="Security\AuthenticationInfo.cs" /> + <Compile Include="Security\AuthenticationInfoQuery.cs" /> + <Compile Include="Security\IAuthenticationRepository.cs" /> <Compile Include="Security\IEncryptionManager.cs" /> + <Compile Include="Session\AuthenticationRequest.cs" /> <Compile Include="Subtitles\ISubtitleManager.cs" /> <Compile Include="Subtitles\ISubtitleProvider.cs" /> <Compile Include="Providers\ItemIdentifier.cs" /> @@ -304,8 +327,13 @@ <Compile Include="Sorting\IUserBaseItemComparer.cs" /> <Compile Include="Providers\BaseItemXmlParser.cs" /> <Compile Include="Sorting\SortExtensions.cs" /> + <Compile Include="Subtitles\SubtitleDownloadEventArgs.cs" /> <Compile Include="Subtitles\SubtitleResponse.cs" /> <Compile Include="Subtitles\SubtitleSearchRequest.cs" /> + <Compile Include="Sync\ICloudSyncProvider.cs" /> + <Compile Include="Sync\ISyncManager.cs" /> + <Compile Include="Sync\ISyncProvider.cs" /> + <Compile Include="Sync\ISyncRepository.cs" /> <Compile Include="Themes\IAppThemeManager.cs" /> <Compile Include="Themes\InternalThemeImage.cs" /> </ItemGroup> @@ -332,7 +360,7 @@ xcopy "$(TargetPath)" "$(SolutionDir)\Nuget\dlls\" /y /d /r /i <PreBuildEvent> </PreBuildEvent> </PropertyGroup> - <Import Project="$(SolutionDir)\.nuget\nuget.targets" Condition=" '$(ConfigurationName)' != 'Release Mono' " /> + <Import Project="$(SolutionDir)\.nuget\NuGet.targets" /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets. <Target Name="BeforeBuild"> diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index f7e8554d1b..38c2c83c47 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -44,6 +44,27 @@ namespace MediaBrowser.Controller.MediaEncoding Task<Stream> ExtractVideoImage(string[] inputFiles, MediaProtocol protocol, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken); /// <summary> + /// Extracts the video images on interval. + /// </summary> + /// <param name="inputFiles">The input files.</param> + /// <param name="protocol">The protocol.</param> + /// <param name="threedFormat">The threed format.</param> + /// <param name="interval">The interval.</param> + /// <param name="targetDirectory">The target directory.</param> + /// <param name="filenamePrefix">The filename prefix.</param> + /// <param name="maxWidth">The maximum width.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task ExtractVideoImagesOnInterval(string[] inputFiles, + MediaProtocol protocol, + Video3DFormat? threedFormat, + TimeSpan interval, + string targetDirectory, + string filenamePrefix, + int? maxWidth, + CancellationToken cancellationToken); + + /// <summary> /// Gets the media info. /// </summary> /// <param name="inputFiles">The input files.</param> @@ -68,5 +89,12 @@ namespace MediaBrowser.Controller.MediaEncoding /// <param name="protocol">The protocol.</param> /// <returns>System.String.</returns> string GetInputArgument(string[] inputFiles, MediaProtocol protocol); + + /// <summary> + /// Gets the time parameter. + /// </summary> + /// <param name="ticks">The ticks.</param> + /// <returns>System.String.</returns> + string GetTimeParameter(long ticks); } } diff --git a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs index 6e9bcef2ea..9e32fc32b0 100644 --- a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs @@ -13,6 +13,7 @@ namespace MediaBrowser.Controller.MediaEncoding /// <param name="inputFormat">The input format.</param> /// <param name="outputFormat">The output format.</param> /// <param name="startTimeTicks">The start time ticks.</param> + /// <param name="endTimeTicks">The end time ticks.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{Stream}.</returns> Task<Stream> ConvertSubtitles( @@ -20,6 +21,7 @@ namespace MediaBrowser.Controller.MediaEncoding string inputFormat, string outputFormat, long startTimeTicks, + long? endTimeTicks, CancellationToken cancellationToken); /// <summary> @@ -30,6 +32,7 @@ namespace MediaBrowser.Controller.MediaEncoding /// <param name="subtitleStreamIndex">Index of the subtitle stream.</param> /// <param name="outputFormat">The output format.</param> /// <param name="startTimeTicks">The start time ticks.</param> + /// <param name="endTimeTicks">The end time ticks.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{Stream}.</returns> Task<Stream> GetSubtitles(string itemId, @@ -37,6 +40,7 @@ namespace MediaBrowser.Controller.MediaEncoding int subtitleStreamIndex, string outputFormat, long startTimeTicks, + long? endTimeTicks, CancellationToken cancellationToken); /// <summary> diff --git a/MediaBrowser.Controller/MediaEncoding/InternalMediaInfoResult.cs b/MediaBrowser.Controller/MediaEncoding/InternalMediaInfoResult.cs index 39d1c32202..796fdb723a 100644 --- a/MediaBrowser.Controller/MediaEncoding/InternalMediaInfoResult.cs +++ b/MediaBrowser.Controller/MediaEncoding/InternalMediaInfoResult.cs @@ -313,6 +313,12 @@ namespace MediaBrowser.Controller.MediaEncoding public string bit_rate { get; set; } /// <summary> + /// Gets or sets the probe_score. + /// </summary> + /// <value>The probe_score.</value> + public int probe_score { get; set; } + + /// <summary> /// Gets or sets the tags. /// </summary> /// <value>The tags.</value> diff --git a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs index 17ab0f31c7..50a1c4e4fc 100644 --- a/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs +++ b/MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs @@ -127,8 +127,9 @@ namespace MediaBrowser.Controller.MediaEncoding stream.BitDepth = GetBitDepth(stream.PixelFormat); - stream.IsAnamorphic = string.Equals(streamInfo.sample_aspect_ratio, "0:1", - StringComparison.OrdinalIgnoreCase); + stream.IsAnamorphic = string.Equals(streamInfo.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase) || + string.Equals(stream.AspectRatio, "2.35:1", StringComparison.OrdinalIgnoreCase) || + string.Equals(stream.AspectRatio, "2.40:1", StringComparison.OrdinalIgnoreCase); } else { diff --git a/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs new file mode 100644 index 0000000000..567d20f39c --- /dev/null +++ b/MediaBrowser.Controller/Net/AuthenticatedAttribute.cs @@ -0,0 +1,41 @@ +using ServiceStack.Web; +using System; + +namespace MediaBrowser.Controller.Net +{ + public class AuthenticatedAttribute : Attribute, IHasRequestFilter + { + public IAuthService AuthService { get; set; } + + /// <summary> + /// The request filter is executed before the service. + /// </summary> + /// <param name="request">The http request wrapper</param> + /// <param name="response">The http response wrapper</param> + /// <param name="requestDto">The request DTO</param> + public void RequestFilter(IRequest request, IResponse response, object requestDto) + { + AuthService.Authenticate(request, response, requestDto); + } + + /// <summary> + /// A new shallow copy of this filter is used on every request. + /// </summary> + /// <returns>IHasRequestFilter.</returns> + public IHasRequestFilter Copy() + { + return this; + } + + /// <summary> + /// Order in which Request Filters are executed. + /// <0 Executed before global request filters + /// >0 Executed after global request filters + /// </summary> + /// <value>The priority.</value> + public int Priority + { + get { return 0; } + } + } +} diff --git a/MediaBrowser.Controller/Net/AuthorizationInfo.cs b/MediaBrowser.Controller/Net/AuthorizationInfo.cs new file mode 100644 index 0000000000..d7dcb60f0e --- /dev/null +++ b/MediaBrowser.Controller/Net/AuthorizationInfo.cs @@ -0,0 +1,37 @@ + +namespace MediaBrowser.Controller.Net +{ + public class AuthorizationInfo + { + /// <summary> + /// Gets or sets the user identifier. + /// </summary> + /// <value>The user identifier.</value> + public string UserId { get; set; } + /// <summary> + /// Gets or sets the device identifier. + /// </summary> + /// <value>The device identifier.</value> + public string DeviceId { get; set; } + /// <summary> + /// Gets or sets the device. + /// </summary> + /// <value>The device.</value> + public string Device { get; set; } + /// <summary> + /// Gets or sets the client. + /// </summary> + /// <value>The client.</value> + public string Client { get; set; } + /// <summary> + /// Gets or sets the version. + /// </summary> + /// <value>The version.</value> + public string Version { get; set; } + /// <summary> + /// Gets or sets the token. + /// </summary> + /// <value>The token.</value> + public string Token { get; set; } + } +} diff --git a/MediaBrowser.Controller/Net/IAuthService.cs b/MediaBrowser.Controller/Net/IAuthService.cs new file mode 100644 index 0000000000..41859395b6 --- /dev/null +++ b/MediaBrowser.Controller/Net/IAuthService.cs @@ -0,0 +1,9 @@ +using ServiceStack.Web; + +namespace MediaBrowser.Controller.Net +{ + public interface IAuthService + { + void Authenticate(IRequest request, IResponse response, object requestDto); + } +} diff --git a/MediaBrowser.Controller/Net/IAuthorizationContext.cs b/MediaBrowser.Controller/Net/IAuthorizationContext.cs new file mode 100644 index 0000000000..9cf5623700 --- /dev/null +++ b/MediaBrowser.Controller/Net/IAuthorizationContext.cs @@ -0,0 +1,14 @@ +using ServiceStack.Web; + +namespace MediaBrowser.Controller.Net +{ + public interface IAuthorizationContext + { + /// <summary> + /// Gets the authorization information. + /// </summary> + /// <param name="requestContext">The request context.</param> + /// <returns>AuthorizationInfo.</returns> + AuthorizationInfo GetAuthorizationInfo(IRequest requestContext); + } +} diff --git a/MediaBrowser.Controller/Net/IHasAuthorization.cs b/MediaBrowser.Controller/Net/IHasAuthorization.cs new file mode 100644 index 0000000000..6fc70159dd --- /dev/null +++ b/MediaBrowser.Controller/Net/IHasAuthorization.cs @@ -0,0 +1,12 @@ + +namespace MediaBrowser.Controller.Net +{ + public interface IHasAuthorization + { + /// <summary> + /// Gets or sets the authorization context. + /// </summary> + /// <value>The authorization context.</value> + IAuthorizationContext AuthorizationContext { get; set; } + } +} diff --git a/MediaBrowser.Controller/Net/IHasResultFactory.cs b/MediaBrowser.Controller/Net/IHasResultFactory.cs index a87113d1f4..3988b8d615 100644 --- a/MediaBrowser.Controller/Net/IHasResultFactory.cs +++ b/MediaBrowser.Controller/Net/IHasResultFactory.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Common.Net; -using ServiceStack.Web; +using ServiceStack.Web; namespace MediaBrowser.Controller.Net { diff --git a/MediaBrowser.Controller/Net/IHasSession.cs b/MediaBrowser.Controller/Net/IHasSession.cs new file mode 100644 index 0000000000..e762c1e844 --- /dev/null +++ b/MediaBrowser.Controller/Net/IHasSession.cs @@ -0,0 +1,12 @@ + +namespace MediaBrowser.Controller.Net +{ + public interface IHasSession + { + /// <summary> + /// Gets or sets the session context. + /// </summary> + /// <value>The session context.</value> + ISessionContext SessionContext { get; set; } + } +} diff --git a/MediaBrowser.Controller/Net/IHttpResultFactory.cs b/MediaBrowser.Controller/Net/IHttpResultFactory.cs index f7984c32cc..526bf4be2e 100644 --- a/MediaBrowser.Controller/Net/IHttpResultFactory.cs +++ b/MediaBrowser.Controller/Net/IHttpResultFactory.cs @@ -1,8 +1,8 @@ -using System; +using ServiceStack.Web; +using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; -using ServiceStack.Web; namespace MediaBrowser.Controller.Net { @@ -80,41 +80,38 @@ namespace MediaBrowser.Controller.Net /// <param name="responseHeaders">The response headers.</param> /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param> /// <returns>System.Object.</returns> - object GetStaticResult(IRequest requestContext, Guid cacheKey, DateTime? lastDateModified, - TimeSpan? cacheDuration, string contentType, Func<Task<Stream>> factoryFn, - IDictionary<string, string> responseHeaders = null, bool isHeadRequest = false); + object GetStaticResult(IRequest requestContext, + Guid cacheKey, + DateTime? lastDateModified, + TimeSpan? cacheDuration, + string contentType, Func<Task<Stream>> factoryFn, + IDictionary<string, string> responseHeaders = null, + bool isHeadRequest = false); /// <summary> - /// Gets the static file result. + /// Gets the static result. /// </summary> /// <param name="requestContext">The request context.</param> - /// <param name="path">The path.</param> - /// <param name="fileShare">The file share.</param> - /// <param name="responseHeaders">The response headers.</param> - /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param> + /// <param name="options">The options.</param> /// <returns>System.Object.</returns> - object GetStaticFileResult(IRequest requestContext, string path, FileShare fileShare = FileShare.Read, IDictionary<string, string> responseHeaders = null, bool isHeadRequest = false); + object GetStaticResult(IRequest requestContext, StaticResultOptions options); /// <summary> /// Gets the static file result. /// </summary> /// <param name="requestContext">The request context.</param> /// <param name="path">The path.</param> - /// <param name="contentType">Type of the content.</param> /// <param name="fileShare">The file share.</param> - /// <param name="responseHeaders">The response headers.</param> - /// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param> /// <returns>System.Object.</returns> - object GetStaticFileResult(IRequest requestContext, string path, string contentType, FileShare fileShare = FileShare.Read, IDictionary<string, string> responseHeaders = null, bool isHeadRequest = false); - + object GetStaticFileResult(IRequest requestContext, string path, FileShare fileShare = FileShare.Read); + /// <summary> - /// Gets the optimized serialized result using cache. + /// Gets the static file result. /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="request">The request.</param> - /// <param name="result">The result.</param> + /// <param name="requestContext">The request context.</param> + /// <param name="options">The options.</param> /// <returns>System.Object.</returns> - object GetOptimizedSerializedResultUsingCache<T>(IRequest request, T result) - where T : class; + object GetStaticFileResult(IRequest requestContext, + StaticFileResultOptions options); } } diff --git a/MediaBrowser.Controller/Net/IHttpServer.cs b/MediaBrowser.Controller/Net/IHttpServer.cs index 20f07c74de..5b179d479a 100644 --- a/MediaBrowser.Controller/Net/IHttpServer.cs +++ b/MediaBrowser.Controller/Net/IHttpServer.cs @@ -22,12 +22,6 @@ namespace MediaBrowser.Controller.Net void StartServer(IEnumerable<string> urlPrefixes); /// <summary> - /// Gets a value indicating whether [supports web sockets]. - /// </summary> - /// <value><c>true</c> if [supports web sockets]; otherwise, <c>false</c>.</value> - bool SupportsWebSockets { get; } - - /// <summary> /// Gets the local end points. /// </summary> /// <value>The local end points.</value> @@ -39,12 +33,6 @@ namespace MediaBrowser.Controller.Net void Stop(); /// <summary> - /// Gets or sets a value indicating whether [enable HTTP request logging]. - /// </summary> - /// <value><c>true</c> if [enable HTTP request logging]; otherwise, <c>false</c>.</value> - bool EnableHttpRequestLogging { get; set; } - - /// <summary> /// Occurs when [web socket connected]. /// </summary> event EventHandler<WebSocketConnectEventArgs> WebSocketConnected; diff --git a/MediaBrowser.Controller/Net/IRestfulService.cs b/MediaBrowser.Controller/Net/IRestfulService.cs index f55012b734..7d07bb0941 100644 --- a/MediaBrowser.Controller/Net/IRestfulService.cs +++ b/MediaBrowser.Controller/Net/IRestfulService.cs @@ -5,6 +5,7 @@ namespace MediaBrowser.Controller.Net /// <summary> /// Interface IRestfulService /// </summary> + [Logged] public interface IRestfulService : IService { } diff --git a/MediaBrowser.Controller/Net/ISessionContext.cs b/MediaBrowser.Controller/Net/ISessionContext.cs new file mode 100644 index 0000000000..31ccd53731 --- /dev/null +++ b/MediaBrowser.Controller/Net/ISessionContext.cs @@ -0,0 +1,13 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Session; +using ServiceStack.Web; + +namespace MediaBrowser.Controller.Net +{ + public interface ISessionContext + { + SessionInfo GetSession(IRequest requestContext); + + User GetUser(IRequest requestContext); + } +} diff --git a/MediaBrowser.Controller/Net/LoggedAttribute.cs b/MediaBrowser.Controller/Net/LoggedAttribute.cs new file mode 100644 index 0000000000..6df72f7a7e --- /dev/null +++ b/MediaBrowser.Controller/Net/LoggedAttribute.cs @@ -0,0 +1,73 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Session; +using MediaBrowser.Model.Logging; +using ServiceStack.Web; +using System; + +namespace MediaBrowser.Controller.Net +{ + public class LoggedAttribute : Attribute, IHasRequestFilter + { + public ILogger Logger { get; set; } + public IUserManager UserManager { get; set; } + public ISessionManager SessionManager { get; set; } + public IAuthorizationContext AuthorizationContext { get; set; } + + /// <summary> + /// The request filter is executed before the service. + /// </summary> + /// <param name="request">The http request wrapper</param> + /// <param name="response">The http response wrapper</param> + /// <param name="requestDto">The request DTO</param> + public void RequestFilter(IRequest request, IResponse response, object requestDto) + { + //This code is executed before the service + var auth = AuthorizationContext.GetAuthorizationInfo(request); + + if (auth != null) + { + User user = null; + + if (!string.IsNullOrWhiteSpace(auth.UserId)) + { + var userId = auth.UserId; + + user = UserManager.GetUserById(new Guid(userId)); + } + + string deviceId = auth.DeviceId; + string device = auth.Device; + string client = auth.Client; + string version = auth.Version; + + if (!string.IsNullOrEmpty(client) && !string.IsNullOrEmpty(deviceId) && !string.IsNullOrEmpty(device) && !string.IsNullOrEmpty(version)) + { + var remoteEndPoint = request.RemoteIp; + + SessionManager.LogSessionActivity(client, version, deviceId, device, remoteEndPoint, user); + } + } + } + + /// <summary> + /// A new shallow copy of this filter is used on every request. + /// </summary> + /// <returns>IHasRequestFilter.</returns> + public IHasRequestFilter Copy() + { + return this; + } + + /// <summary> + /// Order in which Request Filters are executed. + /// <0 Executed before global request filters + /// >0 Executed after global request filters + /// </summary> + /// <value>The priority.</value> + public int Priority + { + get { return 0; } + } + } +} diff --git a/MediaBrowser.Controller/Net/StaticResultOptions.cs b/MediaBrowser.Controller/Net/StaticResultOptions.cs new file mode 100644 index 0000000000..fde08c269d --- /dev/null +++ b/MediaBrowser.Controller/Net/StaticResultOptions.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Net +{ + public class StaticResultOptions + { + public string ContentType { get; set; } + public TimeSpan? CacheDuration { get; set; } + public DateTime? DateLastModified { get; set; } + public Guid CacheKey { get; set; } + + public Func<Task<Stream>> ContentFactory { get; set; } + + public bool IsHeadRequest { get; set; } + + public IDictionary<string, string> ResponseHeaders { get; set; } + + public bool Throttle { get; set; } + public long ThrottleLimit { get; set; } + public long MinThrottlePosition { get; set; } + + public StaticResultOptions() + { + ResponseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + } + } + + public class StaticFileResultOptions : StaticResultOptions + { + public string Path { get; set; } + + public FileShare FileShare { get; set; } + + public StaticFileResultOptions() + { + FileShare = FileShare.Read; + } + } +} diff --git a/MediaBrowser.Controller/Notifications/INotificationsRepository.cs b/MediaBrowser.Controller/Notifications/INotificationsRepository.cs index 87b89e79c8..254e56e059 100644 --- a/MediaBrowser.Controller/Notifications/INotificationsRepository.cs +++ b/MediaBrowser.Controller/Notifications/INotificationsRepository.cs @@ -16,10 +16,6 @@ namespace MediaBrowser.Controller.Notifications /// </summary> event EventHandler<NotificationUpdateEventArgs> NotificationAdded; /// <summary> - /// Occurs when [notification updated]. - /// </summary> - event EventHandler<NotificationUpdateEventArgs> NotificationUpdated; - /// <summary> /// Occurs when [notifications marked read]. /// </summary> event EventHandler<NotificationReadEventArgs> NotificationsMarkedRead; @@ -38,14 +34,6 @@ namespace MediaBrowser.Controller.Notifications NotificationResult GetNotifications(NotificationQuery query); /// <summary> - /// Gets the notification. - /// </summary> - /// <param name="id">The id.</param> - /// <param name="userId">The user id.</param> - /// <returns>Notification.</returns> - Notification GetNotification(string id, string userId); - - /// <summary> /// Adds the notification. /// </summary> /// <param name="notification">The notification.</param> diff --git a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs new file mode 100644 index 0000000000..cbe0b97a4e --- /dev/null +++ b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs @@ -0,0 +1,49 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Playlists; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Playlists +{ + public interface IPlaylistManager + { + /// <summary> + /// Gets the playlists. + /// </summary> + /// <param name="userId">The user identifier.</param> + /// <returns>IEnumerable<Playlist>.</returns> + IEnumerable<Playlist> GetPlaylists(string userId); + + /// <summary> + /// Creates the playlist. + /// </summary> + /// <param name="options">The options.</param> + /// <returns>Task<Playlist>.</returns> + Task<PlaylistCreationResult> CreatePlaylist(PlaylistCreationRequest options); + + /// <summary> + /// Adds to playlist. + /// </summary> + /// <param name="playlistId">The playlist identifier.</param> + /// <param name="itemIds">The item ids.</param> + /// <param name="userId">The user identifier.</param> + /// <returns>Task.</returns> + Task AddToPlaylist(string playlistId, IEnumerable<string> itemIds, string userId); + + /// <summary> + /// Removes from playlist. + /// </summary> + /// <param name="playlistId">The playlist identifier.</param> + /// <param name="entryIds">The entry ids.</param> + /// <returns>Task.</returns> + Task RemoveFromPlaylist(string playlistId, IEnumerable<string> entryIds); + + /// <summary> + /// Gets the playlists folder. + /// </summary> + /// <param name="userId">The user identifier.</param> + /// <returns>Folder.</returns> + Folder GetPlaylistsFolder(string userId); + + } +} diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs new file mode 100644 index 0000000000..5da810a91b --- /dev/null +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -0,0 +1,157 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Querying; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; + +namespace MediaBrowser.Controller.Playlists +{ + public class Playlist : Folder + { + public string OwnerUserId { get; set; } + + protected override bool FilterLinkedChildrenPerUser + { + get + { + return true; + } + } + + public override IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren) + { + return GetPlayableItems(user); + } + + public override IEnumerable<BaseItem> GetRecursiveChildren(User user, bool includeLinkedChildren = true) + { + return GetPlayableItems(user); + } + + public IEnumerable<Tuple<LinkedChild, BaseItem>> GetManageableItems() + { + return GetLinkedChildrenInfos(); + } + + private IEnumerable<BaseItem> GetPlayableItems(User user) + { + return GetPlaylistItems(MediaType, base.GetChildren(user, true), user); + } + + public static IEnumerable<BaseItem> GetPlaylistItems(string playlistMediaType, IEnumerable<BaseItem> inputItems, User user) + { + if (user != null) + { + inputItems = inputItems.Where(i => i.IsVisible(user)); + } + + return inputItems.SelectMany(i => GetPlaylistItems(i, user)) + .Where(m => string.Equals(m.MediaType, playlistMediaType, StringComparison.OrdinalIgnoreCase)); + } + + private static IEnumerable<BaseItem> GetPlaylistItems(BaseItem i, User user) + { + var musicGenre = i as MusicGenre; + if (musicGenre != null) + { + var items = user == null + ? LibraryManager.RootFolder.GetRecursiveChildren() + : user.RootFolder.GetRecursiveChildren(user, true); + + var songs = items + .OfType<Audio>() + .Where(a => a.Genres.Contains(musicGenre.Name, StringComparer.OrdinalIgnoreCase)); + + return LibraryManager.Sort(songs, user, new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }, SortOrder.Ascending); + } + + var musicArtist = i as MusicArtist; + if (musicArtist != null) + { + var items = user == null + ? LibraryManager.RootFolder.GetRecursiveChildren() + : user.RootFolder.GetRecursiveChildren(user, true); + + var songs = items + .OfType<Audio>() + .Where(a => a.HasArtist(musicArtist.Name)); + + return LibraryManager.Sort(songs, user, new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }, SortOrder.Ascending); + } + + // Grab these explicitly to avoid the sorting that will happen below + var collection = i as BoxSet; + if (collection != null) + { + var items = user == null + ? collection.Children + : collection.GetChildren(user, true); + + return items + .Where(m => !m.IsFolder); + } + + // Grab these explicitly to avoid the sorting that will happen below + var season = i as Season; + if (season != null) + { + var items = user == null + ? season.Children + : season.GetChildren(user, true); + + return items + .Where(m => !m.IsFolder); + } + + var folder = i as Folder; + + if (folder != null) + { + var items = user == null + ? folder.GetRecursiveChildren() + : folder.GetRecursiveChildren(user, true); + + items = items + .Where(m => !m.IsFolder); + + return LibraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending); + } + + return new[] { i }; + } + + [IgnoreDataMember] + public override bool IsPreSorted + { + get + { + return true; + } + } + + public string PlaylistMediaType { get; set; } + + public override string MediaType + { + get + { + return PlaylistMediaType; + } + } + + public void SetMediaType(string value) + { + PlaylistMediaType = value; + } + + public override bool IsVisible(User user) + { + return base.IsVisible(user) && string.Equals(user.Id.ToString("N"), OwnerUserId); + } + } +} diff --git a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs index 3cb90d360e..06d7382972 100644 --- a/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs +++ b/MediaBrowser.Controller/Providers/BaseItemXmlParser.cs @@ -1283,6 +1283,67 @@ namespace MediaBrowser.Controller.Providers return new[] { personInfo }; } + protected LinkedChild GetLinkedChild(XmlReader reader) + { + reader.MoveToContent(); + + var linkedItem = new LinkedChild + { + Type = LinkedChildType.Manual + }; + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "Name": + { + linkedItem.ItemName = reader.ReadElementContentAsString(); + break; + } + + case "Path": + { + linkedItem.Path = reader.ReadElementContentAsString(); + break; + } + + case "Type": + { + linkedItem.ItemType = reader.ReadElementContentAsString(); + break; + } + + case "Year": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + linkedItem.ItemYear = rval; + } + } + + break; + } + + default: + reader.Skip(); + break; + } + } + } + + return string.IsNullOrWhiteSpace(linkedItem.ItemName) || string.IsNullOrWhiteSpace(linkedItem.ItemType) ? null : linkedItem; + } + + /// <summary> /// Used to split names of comma or pipe delimeted genres and people /// </summary> diff --git a/MediaBrowser.Controller/Providers/IMetadataProvider.cs b/MediaBrowser.Controller/Providers/IMetadataProvider.cs index d33b2c9eb0..52cd6fceac 100644 --- a/MediaBrowser.Controller/Providers/IMetadataProvider.cs +++ b/MediaBrowser.Controller/Providers/IMetadataProvider.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.Entities; +using System.Collections.Generic; namespace MediaBrowser.Controller.Providers { diff --git a/MediaBrowser.Controller/Providers/ItemIdentities.cs b/MediaBrowser.Controller/Providers/ItemIdentities.cs index 93d468af0e..8d24f6c1fb 100644 --- a/MediaBrowser.Controller/Providers/ItemIdentities.cs +++ b/MediaBrowser.Controller/Providers/ItemIdentities.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs index 692d6db909..dd07979d34 100644 --- a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs +++ b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs @@ -1,6 +1,7 @@ -using System; +using MediaBrowser.Model.Entities; +using System; using System.Collections.Generic; -using MediaBrowser.Model.Entities; +using System.Linq; namespace MediaBrowser.Controller.Providers { @@ -18,6 +19,23 @@ namespace MediaBrowser.Controller.Providers /// </summary> [Obsolete] public bool ForceSave { get; set; } + + public MetadataRefreshOptions() + { + MetadataRefreshMode = MetadataRefreshMode.Default; + } + + public MetadataRefreshOptions(MetadataRefreshOptions copy) + { + MetadataRefreshMode = copy.MetadataRefreshMode; + ForceSave = copy.ForceSave; + ReplaceAllMetadata = copy.ReplaceAllMetadata; + + ImageRefreshMode = copy.ImageRefreshMode; + DirectoryService = copy.DirectoryService; + ReplaceAllImages = copy.ReplaceAllImages; + ReplaceImages = copy.ReplaceImages.ToList(); + } } public class ImageRefreshOptions @@ -38,48 +56,54 @@ namespace MediaBrowser.Controller.Providers public bool IsReplacingImage(ImageType type) { - return ReplaceAllImages || ReplaceImages.Contains(type); + return ImageRefreshMode == ImageRefreshMode.FullRefresh && + (ReplaceAllImages || ReplaceImages.Contains(type)); } } public enum MetadataRefreshMode { /// <summary> - /// Providers will be executed based on default rules + /// The none /// </summary> - EnsureMetadata = 0, + None = 0, /// <summary> - /// No providers will be executed + /// The validation only /// </summary> - None = 1, + ValidationOnly = 1, /// <summary> - /// All providers will be executed to search for new metadata + /// Providers will be executed based on default rules /// </summary> - FullRefresh = 2, + Default = 2, /// <summary> - /// The validation only + /// All providers will be executed to search for new metadata /// </summary> - ValidationOnly = 3 + FullRefresh = 3 } public enum ImageRefreshMode { /// <summary> + /// The none + /// </summary> + None = 0, + + /// <summary> /// The default /// </summary> - Default = 0, + Default = 1, /// <summary> /// Existing images will be validated /// </summary> - ValidationOnly = 1, + ValidationOnly = 2, /// <summary> /// All providers will be executed to search for new metadata /// </summary> - FullRefresh = 2 + FullRefresh = 3 } } diff --git a/MediaBrowser.Controller/Providers/NameParser.cs b/MediaBrowser.Controller/Providers/NameParser.cs index 726f0e60e3..cdd0974eac 100644 --- a/MediaBrowser.Controller/Providers/NameParser.cs +++ b/MediaBrowser.Controller/Providers/NameParser.cs @@ -5,13 +5,13 @@ namespace MediaBrowser.Controller.Providers { public static class NameParser { - static readonly Regex[] NameMatches = new[] { + static readonly Regex[] NameMatches = + { new Regex(@"(?<name>.*)\((?<year>\d{4})\)"), // matches "My Movie (2001)" and gives us the name and the year new Regex(@"(?<name>.*)(\.(?<year>\d{4})(\.|$)).*$"), new Regex(@"(?<name>.*)") // last resort matches the whole string as the name }; - /// <summary> /// Parses the name. /// </summary> diff --git a/MediaBrowser.Controller/RelatedMedia/IRelatedMediaProvider.cs b/MediaBrowser.Controller/RelatedMedia/IRelatedMediaProvider.cs new file mode 100644 index 0000000000..1cf5742b26 --- /dev/null +++ b/MediaBrowser.Controller/RelatedMedia/IRelatedMediaProvider.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.RelatedMedia +{ + public interface IRelatedMediaProvider + { + /// <summary> + /// Gets the name. + /// </summary> + /// <value>The name.</value> + string Name { get; } + } +} diff --git a/MediaBrowser.Controller/Resolvers/BaseVideoResolver.cs b/MediaBrowser.Controller/Resolvers/BaseVideoResolver.cs index 06bee2ad1e..038d8d48bf 100644 --- a/MediaBrowser.Controller/Resolvers/BaseVideoResolver.cs +++ b/MediaBrowser.Controller/Resolvers/BaseVideoResolver.cs @@ -108,6 +108,25 @@ namespace MediaBrowser.Controller.Resolvers { item.Video3DFormat = Video3DFormat.HalfTopAndBottom; } + else + { + // Support Xbmc conventions: + // http://wiki.xbmc.org/index.php?title=3D + var name = Path.GetFileName(item.Path); + + name = name.Replace('.', ' ').Replace('_', ' ').Replace('-', ' '); + + if (name.IndexOf(" 3d hsbs ", StringComparison.OrdinalIgnoreCase) != -1 || + name.IndexOf(" 3d sbs ", StringComparison.OrdinalIgnoreCase) != -1) + { + item.Video3DFormat = Video3DFormat.HalfSideBySide; + } + else if (name.IndexOf(" 3d htab ", StringComparison.OrdinalIgnoreCase) != -1 || + name.IndexOf(" 3d tab ", StringComparison.OrdinalIgnoreCase) != -1) + { + item.Video3DFormat = Video3DFormat.HalfTopAndBottom; + } + } } } } diff --git a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs index 42178c44c1..7c68448e54 100644 --- a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs +++ b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.IO; +using System.Globalization; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using System; @@ -15,6 +16,21 @@ namespace MediaBrowser.Controller.Resolvers public static class EntityResolutionHelper { /// <summary> + /// Any folder named in this list will be ignored - can be added to at runtime for extensibility + /// </summary> + public static readonly List<string> IgnoreFolders = new List<string> + { + "metadata", + "ps3_update", + "ps3_vprm", + "extrafanart", + "extrathumbs", + ".actors", + ".wd_tv" + + }; + + /// <summary> /// Any extension in this list is considered a video file - can be added to at runtime for extensibility /// </summary> public static List<string> VideoFileExtensions = new List<string> @@ -174,6 +190,44 @@ namespace MediaBrowser.Controller.Resolvers } /// <summary> + /// Determines whether [is multi disc album folder] [the specified path]. + /// </summary> + /// <param name="path">The path.</param> + /// <returns><c>true</c> if [is multi disc album folder] [the specified path]; otherwise, <c>false</c>.</returns> + public static bool IsMultiDiscAlbumFolder(string path) + { + var filename = Path.GetFileName(path); + + if (string.IsNullOrWhiteSpace(filename)) + { + return false; + } + + // Normalize + // Remove whitespace + filename = filename.Replace("-", string.Empty); + filename = Regex.Replace(filename, @"\s+", ""); + + var prefixes = new[] { "disc", "cd", "disk" }; + + foreach (var prefix in prefixes) + { + if (filename.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) == 0) + { + var tmp = filename.Substring(prefix.Length); + + int val; + if (int.TryParse(tmp, NumberStyles.Any, CultureInfo.InvariantCulture, out val)) + { + return true; + } + } + } + + return false; + } + + /// <summary> /// Ensures DateCreated and DateModified have values /// </summary> /// <param name="fileSystem">The file system.</param> diff --git a/MediaBrowser.Controller/Security/AuthenticationInfo.cs b/MediaBrowser.Controller/Security/AuthenticationInfo.cs new file mode 100644 index 0000000000..dd5eec1f92 --- /dev/null +++ b/MediaBrowser.Controller/Security/AuthenticationInfo.cs @@ -0,0 +1,61 @@ +using System; + +namespace MediaBrowser.Controller.Security +{ + public class AuthenticationInfo + { + /// <summary> + /// Gets or sets the identifier. + /// </summary> + /// <value>The identifier.</value> + public string Id { get; set; } + + /// <summary> + /// Gets or sets the access token. + /// </summary> + /// <value>The access token.</value> + public string AccessToken { get; set; } + + /// <summary> + /// Gets or sets the device identifier. + /// </summary> + /// <value>The device identifier.</value> + public string DeviceId { get; set; } + + /// <summary> + /// Gets or sets the name of the application. + /// </summary> + /// <value>The name of the application.</value> + public string AppName { get; set; } + + /// <summary> + /// Gets or sets the name of the device. + /// </summary> + /// <value>The name of the device.</value> + public string DeviceName { get; set; } + + /// <summary> + /// Gets or sets the user identifier. + /// </summary> + /// <value>The user identifier.</value> + public string UserId { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is active. + /// </summary> + /// <value><c>true</c> if this instance is active; otherwise, <c>false</c>.</value> + public bool IsActive { get; set; } + + /// <summary> + /// Gets or sets the date created. + /// </summary> + /// <value>The date created.</value> + public DateTime DateCreated { get; set; } + + /// <summary> + /// Gets or sets the date revoked. + /// </summary> + /// <value>The date revoked.</value> + public DateTime? DateRevoked { get; set; } + } +} diff --git a/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs b/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs new file mode 100644 index 0000000000..3234b0350f --- /dev/null +++ b/MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs @@ -0,0 +1,42 @@ + +namespace MediaBrowser.Controller.Security +{ + public class AuthenticationInfoQuery + { + /// <summary> + /// Gets or sets the device identifier. + /// </summary> + /// <value>The device identifier.</value> + public string DeviceId { get; set; } + + /// <summary> + /// Gets or sets the user identifier. + /// </summary> + /// <value>The user identifier.</value> + public string UserId { get; set; } + + /// <summary> + /// Gets or sets the access token. + /// </summary> + /// <value>The access token.</value> + public string AccessToken { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is active. + /// </summary> + /// <value><c>null</c> if [is active] contains no value, <c>true</c> if [is active]; otherwise, <c>false</c>.</value> + public bool? IsActive { get; set; } + + /// <summary> + /// Gets or sets the start index. + /// </summary> + /// <value>The start index.</value> + public int? StartIndex { get; set; } + + /// <summary> + /// Gets or sets the limit. + /// </summary> + /// <value>The limit.</value> + public int? Limit { get; set; } + } +} diff --git a/MediaBrowser.Controller/Security/IAuthenticationRepository.cs b/MediaBrowser.Controller/Security/IAuthenticationRepository.cs new file mode 100644 index 0000000000..219b07028d --- /dev/null +++ b/MediaBrowser.Controller/Security/IAuthenticationRepository.cs @@ -0,0 +1,39 @@ +using MediaBrowser.Model.Querying; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Security +{ + public interface IAuthenticationRepository + { + /// <summary> + /// Creates the specified information. + /// </summary> + /// <param name="info">The information.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task Create(AuthenticationInfo info, CancellationToken cancellationToken); + + /// <summary> + /// Updates the specified information. + /// </summary> + /// <param name="info">The information.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task Update(AuthenticationInfo info, CancellationToken cancellationToken); + + /// <summary> + /// Gets the specified query. + /// </summary> + /// <param name="query">The query.</param> + /// <returns>QueryResult{AuthenticationInfo}.</returns> + QueryResult<AuthenticationInfo> Get(AuthenticationInfoQuery query); + + /// <summary> + /// Gets the specified identifier. + /// </summary> + /// <param name="id">The identifier.</param> + /// <returns>AuthenticationInfo.</returns> + AuthenticationInfo Get(string id); + } +} diff --git a/MediaBrowser.Controller/Session/AuthenticationRequest.cs b/MediaBrowser.Controller/Session/AuthenticationRequest.cs new file mode 100644 index 0000000000..38871e8147 --- /dev/null +++ b/MediaBrowser.Controller/Session/AuthenticationRequest.cs @@ -0,0 +1,14 @@ + +namespace MediaBrowser.Controller.Session +{ + public class AuthenticationRequest + { + public string Username { get; set; } + public string Password { get; set; } + public string App { get; set; } + public string AppVersion { get; set; } + public string DeviceId { get; set; } + public string DeviceName { get; set; } + public string RemoteEndPoint { get; set; } + } +} diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs index 8c2ece131f..f715ce7703 100644 --- a/MediaBrowser.Controller/Session/ISessionManager.cs +++ b/MediaBrowser.Controller/Session/ISessionManager.cs @@ -1,7 +1,9 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Events; using MediaBrowser.Model.Session; +using MediaBrowser.Model.Users; using System; using System.Collections.Generic; using System.Threading; @@ -45,6 +47,16 @@ namespace MediaBrowser.Controller.Session /// Occurs when [capabilities changed]. /// </summary> event EventHandler<SessionEventArgs> CapabilitiesChanged; + + /// <summary> + /// Occurs when [authentication failed]. + /// </summary> + event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationFailed; + + /// <summary> + /// Occurs when [authentication succeeded]. + /// </summary> + event EventHandler<GenericEventArgs<AuthenticationRequest>> AuthenticationSucceeded; /// <summary> /// Gets the sessions. @@ -206,19 +218,14 @@ namespace MediaBrowser.Controller.Session /// <param name="sessionId">The session identifier.</param> /// <param name="item">The item.</param> void ReportNowViewingItem(string sessionId, BaseItemInfo item); - + /// <summary> /// Authenticates the new session. /// </summary> - /// <param name="user">The user.</param> - /// <param name="password">The password.</param> - /// <param name="clientType">Type of the client.</param> - /// <param name="appVersion">The application version.</param> - /// <param name="deviceId">The device identifier.</param> - /// <param name="deviceName">Name of the device.</param> - /// <param name="remoteEndPoint">The remote end point.</param> + /// <param name="request">The request.</param> + /// <param name="isLocal">if set to <c>true</c> [is local].</param> /// <returns>Task{SessionInfo}.</returns> - Task<SessionInfo> AuthenticateNewSession(User user, string password, string clientType, string appVersion, string deviceId, string deviceName, string remoteEndPoint); + Task<AuthenticationResult> AuthenticateNewSession(AuthenticationRequest request, bool isLocal); /// <summary> /// Reports the capabilities. @@ -239,5 +246,41 @@ namespace MediaBrowser.Controller.Session /// </summary> /// <param name="deviceId">The device identifier.</param> void ClearTranscodingInfo(string deviceId); + + /// <summary> + /// Gets the session. + /// </summary> + /// <param name="deviceId">The device identifier.</param> + /// <param name="client">The client.</param> + /// <param name="version">The version.</param> + /// <returns>SessionInfo.</returns> + SessionInfo GetSession(string deviceId, string client, string version); + + /// <summary> + /// Validates the security token. + /// </summary> + /// <param name="accessToken">The access token.</param> + void ValidateSecurityToken(string accessToken); + + /// <summary> + /// Logouts the specified access token. + /// </summary> + /// <param name="accessToken">The access token.</param> + /// <returns>Task.</returns> + Task Logout(string accessToken); + + /// <summary> + /// Revokes the user tokens. + /// </summary> + /// <param name="userId">The user identifier.</param> + /// <returns>Task.</returns> + Task RevokeUserTokens(string userId); + + /// <summary> + /// Revokes the token. + /// </summary> + /// <param name="id">The identifier.</param> + /// <returns>Task.</returns> + Task RevokeToken(string id); } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Session/SessionInfo.cs b/MediaBrowser.Controller/Session/SessionInfo.cs index 6f27f6cb28..53a8d5a7cb 100644 --- a/MediaBrowser.Controller/Session/SessionInfo.cs +++ b/MediaBrowser.Controller/Session/SessionInfo.cs @@ -123,7 +123,7 @@ namespace MediaBrowser.Controller.Session public List<string> SupportedCommands { get; set; } public TranscodingInfo TranscodingInfo { get; set; } - + /// <summary> /// Gets a value indicating whether this instance is active. /// </summary> @@ -156,7 +156,7 @@ namespace MediaBrowser.Controller.Session public bool ContainsUser(Guid userId) { - return (UserId ?? Guid.Empty) == UserId || AdditionalUsers.Any(i => userId == new Guid(i.UserId)); + return (UserId ?? Guid.Empty) == userId || AdditionalUsers.Any(i => userId == new Guid(i.UserId)); } } } diff --git a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs index 1d66d1505b..0c814c0d4c 100644 --- a/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs +++ b/MediaBrowser.Controller/Subtitles/ISubtitleManager.cs @@ -1,5 +1,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Providers; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -9,6 +10,16 @@ namespace MediaBrowser.Controller.Subtitles public interface ISubtitleManager { /// <summary> + /// Occurs when [subtitle download failure]. + /// </summary> + event EventHandler<SubtitleDownloadFailureEventArgs> SubtitleDownloadFailure; + + /// <summary> + /// Occurs when [subtitles downloaded]. + /// </summary> + event EventHandler<SubtitleDownloadEventArgs> SubtitlesDownloaded; + + /// <summary> /// Adds the parts. /// </summary> /// <param name="subtitleProviders">The subtitle providers.</param> @@ -31,7 +42,7 @@ namespace MediaBrowser.Controller.Subtitles /// <param name="request">The request.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{IEnumerable{RemoteSubtitleInfo}}.</returns> - Task<IEnumerable<RemoteSubtitleInfo>> SearchSubtitles(SubtitleSearchRequest request, + Task<IEnumerable<RemoteSubtitleInfo>> SearchSubtitles(SubtitleSearchRequest request, CancellationToken cancellationToken); /// <summary> @@ -41,8 +52,8 @@ namespace MediaBrowser.Controller.Subtitles /// <param name="subtitleId">The subtitle identifier.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - Task DownloadSubtitles(Video video, - string subtitleId, + Task DownloadSubtitles(Video video, + string subtitleId, CancellationToken cancellationToken); /// <summary> diff --git a/MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs b/MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs new file mode 100644 index 0000000000..1d204f2cbd --- /dev/null +++ b/MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs @@ -0,0 +1,27 @@ +using System; +using MediaBrowser.Controller.Entities; + +namespace MediaBrowser.Controller.Subtitles +{ + public class SubtitleDownloadEventArgs + { + public BaseItem Item { get; set; } + + public string Format { get; set; } + + public string Language { get; set; } + + public bool IsForced { get; set; } + + public string Provider { get; set; } + } + + public class SubtitleDownloadFailureEventArgs + { + public BaseItem Item { get; set; } + + public string Provider { get; set; } + + public Exception Exception { get; set; } + } +} diff --git a/MediaBrowser.Controller/Sync/ICloudSyncProvider.cs b/MediaBrowser.Controller/Sync/ICloudSyncProvider.cs new file mode 100644 index 0000000000..f93360c641 --- /dev/null +++ b/MediaBrowser.Controller/Sync/ICloudSyncProvider.cs @@ -0,0 +1,12 @@ + +namespace MediaBrowser.Controller.Sync +{ + public interface ICloudSyncProvider + { + /// <summary> + /// Gets the name. + /// </summary> + /// <value>The name.</value> + string Name { get; } + } +} diff --git a/MediaBrowser.Controller/Sync/ISyncManager.cs b/MediaBrowser.Controller/Sync/ISyncManager.cs new file mode 100644 index 0000000000..1d5ab7d3e7 --- /dev/null +++ b/MediaBrowser.Controller/Sync/ISyncManager.cs @@ -0,0 +1,55 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Querying; +using MediaBrowser.Model.Sync; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Sync +{ + public interface ISyncManager + { + /// <summary> + /// Creates the job. + /// </summary> + /// <param name="request">The request.</param> + /// <returns>Task.</returns> + Task<SyncJobCreationResult> CreateJob(SyncJobRequest request); + + /// <summary> + /// Gets the jobs. + /// </summary> + /// <returns>QueryResult<SyncJob>.</returns> + QueryResult<SyncJob> GetJobs(SyncJobQuery query); + + /// <summary> + /// Gets the job. + /// </summary> + /// <param name="id">The identifier.</param> + /// <returns>SyncJob.</returns> + SyncJob GetJob(string id); + + /// <summary> + /// Cancels the job. + /// </summary> + /// <param name="id">The identifier.</param> + /// <returns>Task.</returns> + Task CancelJob(string id); + + /// <summary> + /// Adds the parts. + /// </summary> + void AddParts(IEnumerable<ISyncProvider> providers); + + /// <summary> + /// Gets the synchronize targets. + /// </summary> + IEnumerable<SyncTarget> GetSyncTargets(string userId); + + /// <summary> + /// Supportses the synchronize. + /// </summary> + /// <param name="item">The item.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> + bool SupportsSync(BaseItem item); + } +} diff --git a/MediaBrowser.Controller/Sync/ISyncProvider.cs b/MediaBrowser.Controller/Sync/ISyncProvider.cs new file mode 100644 index 0000000000..89f61b80e4 --- /dev/null +++ b/MediaBrowser.Controller/Sync/ISyncProvider.cs @@ -0,0 +1,28 @@ +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Sync; +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Sync +{ + public interface ISyncProvider + { + /// <summary> + /// Gets the name. + /// </summary> + /// <value>The name.</value> + string Name { get; } + + /// <summary> + /// Gets the synchronize targets. + /// </summary> + /// <returns>IEnumerable<SyncTarget>.</returns> + IEnumerable<SyncTarget> GetSyncTargets(); + + /// <summary> + /// Gets the device profile. + /// </summary> + /// <param name="target">The target.</param> + /// <returns>DeviceProfile.</returns> + DeviceProfile GetDeviceProfile(SyncTarget target); + } +} diff --git a/MediaBrowser.Controller/Sync/ISyncRepository.cs b/MediaBrowser.Controller/Sync/ISyncRepository.cs new file mode 100644 index 0000000000..9cce69bdc2 --- /dev/null +++ b/MediaBrowser.Controller/Sync/ISyncRepository.cs @@ -0,0 +1,58 @@ +using MediaBrowser.Model.Querying; +using MediaBrowser.Model.Sync; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Sync +{ + public interface ISyncRepository + { + /// <summary> + /// Gets the job. + /// </summary> + /// <param name="id">The identifier.</param> + /// <returns>SyncJob.</returns> + SyncJob GetJob(string id); + + /// <summary> + /// Creates the specified job. + /// </summary> + /// <param name="job">The job.</param> + /// <returns>Task.</returns> + Task Create(SyncJob job); + + /// <summary> + /// Updates the specified job. + /// </summary> + /// <param name="job">The job.</param> + /// <returns>Task.</returns> + Task Update(SyncJob job); + + /// <summary> + /// Gets the jobs. + /// </summary> + /// <param name="query">The query.</param> + /// <returns>QueryResult<SyncJob>.</returns> + QueryResult<SyncJob> GetJobs(SyncJobQuery query); + + /// <summary> + /// Gets the job item. + /// </summary> + /// <param name="id">The identifier.</param> + /// <returns>SyncJobItem.</returns> + SyncJobItem GetJobItem(string id); + + /// <summary> + /// Creates the specified job item. + /// </summary> + /// <param name="jobItem">The job item.</param> + /// <returns>Task.</returns> + Task Create(SyncJobItem jobItem); + + /// <summary> + /// Updates the specified job item. + /// </summary> + /// <param name="jobItem">The job item.</param> + /// <returns>Task.</returns> + Task Update(SyncJobItem jobItem); + } +} |
