aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Controller')
-rw-r--r--MediaBrowser.Controller/Activity/IActivityManager.cs17
-rw-r--r--MediaBrowser.Controller/Activity/IActivityRepository.cs14
-rw-r--r--MediaBrowser.Controller/Channels/Channel.cs2
-rw-r--r--MediaBrowser.Controller/Channels/ChannelFolderItem.cs7
-rw-r--r--MediaBrowser.Controller/Channels/ChannelItemInfo.cs13
-rw-r--r--MediaBrowser.Controller/Channels/ChannelMediaInfo.cs2
-rw-r--r--MediaBrowser.Controller/Channels/IChannelFactory.cs5
-rw-r--r--MediaBrowser.Controller/Channels/IChannelManager.cs16
-rw-r--r--MediaBrowser.Controller/Chapters/IChapterManager.cs7
-rw-r--r--MediaBrowser.Controller/Collections/CollectionEvents.cs37
-rw-r--r--MediaBrowser.Controller/Collections/ICollectionManager.cs22
-rw-r--r--MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs13
-rw-r--r--MediaBrowser.Controller/Connect/IConnectManager.cs9
-rw-r--r--MediaBrowser.Controller/Drawing/IImageProcessor.cs7
-rw-r--r--MediaBrowser.Controller/Drawing/ImageExtensions.cs33
-rw-r--r--MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs30
-rw-r--r--MediaBrowser.Controller/Dto/IDtoService.cs26
-rw-r--r--MediaBrowser.Controller/Entities/Audio/Audio.cs65
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs52
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicArtist.cs19
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicGenre.cs7
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs103
-rw-r--r--MediaBrowser.Controller/Entities/BasePluginFolder.cs7
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs201
-rw-r--r--MediaBrowser.Controller/Entities/IHasImages.cs6
-rw-r--r--MediaBrowser.Controller/Entities/IHasMediaSources.cs6
-rw-r--r--MediaBrowser.Controller/Entities/IHasUserData.cs18
-rw-r--r--MediaBrowser.Controller/Entities/IThemeMedia.cs8
-rw-r--r--MediaBrowser.Controller/Entities/LinkedChild.cs18
-rw-r--r--MediaBrowser.Controller/Entities/Movies/BoxSet.cs38
-rw-r--r--MediaBrowser.Controller/Entities/Movies/Movie.cs3
-rw-r--r--MediaBrowser.Controller/Entities/Person.cs5
-rw-r--r--MediaBrowser.Controller/Entities/Photo.cs56
-rw-r--r--MediaBrowser.Controller/Entities/PhotoAlbum.cs21
-rw-r--r--MediaBrowser.Controller/Entities/Studio.cs9
-rw-r--r--MediaBrowser.Controller/Entities/TV/Episode.cs25
-rw-r--r--MediaBrowser.Controller/Entities/TV/Season.cs15
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs24
-rw-r--r--MediaBrowser.Controller/Entities/User.cs1
-rw-r--r--MediaBrowser.Controller/Entities/UserRootFolder.cs6
-rw-r--r--MediaBrowser.Controller/Entities/UserView.cs72
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs16
-rw-r--r--MediaBrowser.Controller/IServerApplicationHost.cs12
-rw-r--r--MediaBrowser.Controller/Library/IUserDataManager.cs9
-rw-r--r--MediaBrowser.Controller/Library/IUserManager.cs17
-rw-r--r--MediaBrowser.Controller/Library/IUserViewManager.cs2
-rw-r--r--MediaBrowser.Controller/Library/TVUtils.cs131
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvManager.cs17
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvRecording.cs4
-rw-r--r--MediaBrowser.Controller/LiveTv/LiveTvChannel.cs4
-rw-r--r--MediaBrowser.Controller/LiveTv/RecordingGroup.cs22
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj36
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs28
-rw-r--r--MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs4
-rw-r--r--MediaBrowser.Controller/MediaEncoding/InternalMediaInfoResult.cs6
-rw-r--r--MediaBrowser.Controller/MediaEncoding/MediaEncoderHelpers.cs5
-rw-r--r--MediaBrowser.Controller/Net/AuthenticatedAttribute.cs41
-rw-r--r--MediaBrowser.Controller/Net/AuthorizationInfo.cs37
-rw-r--r--MediaBrowser.Controller/Net/IAuthService.cs9
-rw-r--r--MediaBrowser.Controller/Net/IAuthorizationContext.cs14
-rw-r--r--MediaBrowser.Controller/Net/IHasAuthorization.cs12
-rw-r--r--MediaBrowser.Controller/Net/IHasResultFactory.cs3
-rw-r--r--MediaBrowser.Controller/Net/IHasSession.cs12
-rw-r--r--MediaBrowser.Controller/Net/IHttpResultFactory.cs41
-rw-r--r--MediaBrowser.Controller/Net/IHttpServer.cs12
-rw-r--r--MediaBrowser.Controller/Net/IRestfulService.cs1
-rw-r--r--MediaBrowser.Controller/Net/ISessionContext.cs13
-rw-r--r--MediaBrowser.Controller/Net/LoggedAttribute.cs73
-rw-r--r--MediaBrowser.Controller/Net/StaticResultOptions.cs42
-rw-r--r--MediaBrowser.Controller/Notifications/INotificationsRepository.cs12
-rw-r--r--MediaBrowser.Controller/Playlists/IPlaylistManager.cs49
-rw-r--r--MediaBrowser.Controller/Playlists/Playlist.cs157
-rw-r--r--MediaBrowser.Controller/Providers/BaseItemXmlParser.cs61
-rw-r--r--MediaBrowser.Controller/Providers/IMetadataProvider.cs1
-rw-r--r--MediaBrowser.Controller/Providers/ItemIdentities.cs5
-rw-r--r--MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs52
-rw-r--r--MediaBrowser.Controller/Providers/NameParser.cs4
-rw-r--r--MediaBrowser.Controller/RelatedMedia/IRelatedMediaProvider.cs17
-rw-r--r--MediaBrowser.Controller/Resolvers/BaseVideoResolver.cs19
-rw-r--r--MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs56
-rw-r--r--MediaBrowser.Controller/Security/AuthenticationInfo.cs61
-rw-r--r--MediaBrowser.Controller/Security/AuthenticationInfoQuery.cs42
-rw-r--r--MediaBrowser.Controller/Security/IAuthenticationRepository.cs39
-rw-r--r--MediaBrowser.Controller/Session/AuthenticationRequest.cs14
-rw-r--r--MediaBrowser.Controller/Session/ISessionManager.cs61
-rw-r--r--MediaBrowser.Controller/Session/SessionInfo.cs4
-rw-r--r--MediaBrowser.Controller/Subtitles/ISubtitleManager.cs17
-rw-r--r--MediaBrowser.Controller/Subtitles/SubtitleDownloadEventArgs.cs27
-rw-r--r--MediaBrowser.Controller/Sync/ICloudSyncProvider.cs12
-rw-r--r--MediaBrowser.Controller/Sync/ISyncManager.cs55
-rw-r--r--MediaBrowser.Controller/Sync/ISyncProvider.cs28
-rw-r--r--MediaBrowser.Controller/Sync/ISyncRepository.cs58
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&lt;QueryResult&lt;Channel&gt;&gt;.</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&lt;QueryResult&lt;BaseItem&gt;&gt;.</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&lt;QueryResult&lt;LiveTvChannel&gt;&gt;.</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&lt;QueryResult&lt;BaseItem&gt;&gt;.</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.
+ /// &lt;0 Executed before global request filters
+ /// &gt;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.
+ /// &lt;0 Executed before global request filters
+ /// &gt;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&lt;Playlist&gt;.</returns>
+ IEnumerable<Playlist> GetPlaylists(string userId);
+
+ /// <summary>
+ /// Creates the playlist.
+ /// </summary>
+ /// <param name="options">The options.</param>
+ /// <returns>Task&lt;Playlist&gt;.</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&lt;SyncJob&gt;.</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&lt;SyncTarget&gt;.</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&lt;SyncJob&gt;.</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);
+ }
+}