diff options
Diffstat (limited to 'MediaBrowser.Model')
344 files changed, 20520 insertions, 0 deletions
diff --git a/MediaBrowser.Model/Activity/ActivityLogEntry.cs b/MediaBrowser.Model/Activity/ActivityLogEntry.cs new file mode 100644 index 000000000..1f4bff10d --- /dev/null +++ b/MediaBrowser.Model/Activity/ActivityLogEntry.cs @@ -0,0 +1,68 @@ +using MediaBrowser.Model.Logging; +using System; + +namespace MediaBrowser.Model.Activity +{ + public class ActivityLogEntry + { + /// <summary> + /// Gets or sets the identifier. + /// </summary> + /// <value>The identifier.</value> + public long Id { get; set; } + + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the overview. + /// </summary> + /// <value>The overview.</value> + public string Overview { get; set; } + + /// <summary> + /// Gets or sets the short overview. + /// </summary> + /// <value>The short overview.</value> + public string ShortOverview { get; set; } + + /// <summary> + /// Gets or sets the type. + /// </summary> + /// <value>The type.</value> + public string Type { get; set; } + + /// <summary> + /// Gets or sets the item identifier. + /// </summary> + /// <value>The item identifier.</value> + public string ItemId { get; set; } + + /// <summary> + /// Gets or sets the date. + /// </summary> + /// <value>The date.</value> + public DateTime Date { get; set; } + + /// <summary> + /// Gets or sets the user identifier. + /// </summary> + /// <value>The user identifier.</value> + public Guid UserId { get; set; } + + /// <summary> + /// Gets or sets the user primary image tag. + /// </summary> + /// <value>The user primary image tag.</value> + public string UserPrimaryImageTag { get; set; } + + /// <summary> + /// Gets or sets the log severity. + /// </summary> + /// <value>The log severity.</value> + public LogSeverity Severity { get; set; } + } +} diff --git a/MediaBrowser.Model/Activity/IActivityManager.cs b/MediaBrowser.Model/Activity/IActivityManager.cs new file mode 100644 index 000000000..7fff26987 --- /dev/null +++ b/MediaBrowser.Model/Activity/IActivityManager.cs @@ -0,0 +1,17 @@ +using System; +using MediaBrowser.Model.Events; +using MediaBrowser.Model.Querying; + +namespace MediaBrowser.Model.Activity +{ + public interface IActivityManager + { + event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated; + + void Create(ActivityLogEntry entry); + + QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit); + + QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? x, int? y); + } +} diff --git a/MediaBrowser.Model/Activity/IActivityRepository.cs b/MediaBrowser.Model/Activity/IActivityRepository.cs new file mode 100644 index 000000000..5a2c52400 --- /dev/null +++ b/MediaBrowser.Model/Activity/IActivityRepository.cs @@ -0,0 +1,12 @@ +using System; +using MediaBrowser.Model.Querying; + +namespace MediaBrowser.Model.Activity +{ + public interface IActivityRepository + { + void Create(ActivityLogEntry entry); + + QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? z, int? startIndex, int? limit); + } +} diff --git a/MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs b/MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs new file mode 100644 index 000000000..e2f780605 --- /dev/null +++ b/MediaBrowser.Model/ApiClient/ServerDiscoveryInfo.cs @@ -0,0 +1,27 @@ + +namespace MediaBrowser.Model.ApiClient +{ + public class ServerDiscoveryInfo + { + /// <summary> + /// Gets or sets the address. + /// </summary> + /// <value>The address.</value> + public string Address { get; set; } + /// <summary> + /// Gets or sets the server identifier. + /// </summary> + /// <value>The server identifier.</value> + public string Id { get; set; } + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + /// <summary> + /// Gets or sets the endpoint address. + /// </summary> + /// <value>The endpoint address.</value> + public string EndpointAddress { get; set; } + } +} diff --git a/MediaBrowser.Model/Branding/BrandingOptions.cs b/MediaBrowser.Model/Branding/BrandingOptions.cs new file mode 100644 index 000000000..3b207d345 --- /dev/null +++ b/MediaBrowser.Model/Branding/BrandingOptions.cs @@ -0,0 +1,17 @@ + +namespace MediaBrowser.Model.Branding +{ + public class BrandingOptions + { + /// <summary> + /// Gets or sets the login disclaimer. + /// </summary> + /// <value>The login disclaimer.</value> + public string LoginDisclaimer { get; set; } + /// <summary> + /// Gets or sets the custom CSS. + /// </summary> + /// <value>The custom CSS.</value> + public string CustomCss { get; set; } + } +} diff --git a/MediaBrowser.Model/Channels/ChannelFeatures.cs b/MediaBrowser.Model/Channels/ChannelFeatures.cs new file mode 100644 index 000000000..39b40cabc --- /dev/null +++ b/MediaBrowser.Model/Channels/ChannelFeatures.cs @@ -0,0 +1,85 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Model.Channels +{ + public class ChannelFeatures + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the identifier. + /// </summary> + /// <value>The identifier.</value> + public string Id { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance can search. + /// </summary> + /// <value><c>true</c> if this instance can search; otherwise, <c>false</c>.</value> + public bool CanSearch { get; set; } + + /// <summary> + /// Gets or sets the media types. + /// </summary> + /// <value>The media types.</value> + public ChannelMediaType[] MediaTypes { get; set; } + + /// <summary> + /// Gets or sets the content types. + /// </summary> + /// <value>The content types.</value> + public ChannelMediaContentType[] ContentTypes { get; set; } + + /// <summary> + /// Represents the maximum number of records the channel allows retrieving at a time + /// </summary> + public int? MaxPageSize { get; set; } + + /// <summary> + /// Gets or sets the automatic refresh levels. + /// </summary> + /// <value>The automatic refresh levels.</value> + public int? AutoRefreshLevels { get; set; } + + /// <summary> + /// Gets or sets the default sort orders. + /// </summary> + /// <value>The default sort orders.</value> + public ChannelItemSortField[] DefaultSortFields { get; set; } + + /// <summary> + /// Indicates if a sort ascending/descending toggle is supported or not. + /// </summary> + public bool SupportsSortOrderToggle { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [supports latest media]. + /// </summary> + /// <value><c>true</c> if [supports latest media]; otherwise, <c>false</c>.</value> + public bool SupportsLatestMedia { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance can filter. + /// </summary> + /// <value><c>true</c> if this instance can filter; otherwise, <c>false</c>.</value> + public bool CanFilter { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [supports content downloading]. + /// </summary> + /// <value><c>true</c> if [supports content downloading]; otherwise, <c>false</c>.</value> + public bool SupportsContentDownloading { get; set; } + + public ChannelFeatures() + { + MediaTypes = new ChannelMediaType[] { }; + ContentTypes = new ChannelMediaContentType[] { }; + + DefaultSortFields = new ChannelItemSortField[] { }; + } + } +} diff --git a/MediaBrowser.Model/Channels/ChannelFolderType.cs b/MediaBrowser.Model/Channels/ChannelFolderType.cs new file mode 100644 index 000000000..7c97afd02 --- /dev/null +++ b/MediaBrowser.Model/Channels/ChannelFolderType.cs @@ -0,0 +1,17 @@ +namespace MediaBrowser.Model.Channels +{ + public enum ChannelFolderType + { + Container = 0, + + MusicAlbum = 1, + + PhotoAlbum = 2, + + MusicArtist = 3, + + Series = 4, + + Season = 5 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Channels/ChannelInfo.cs b/MediaBrowser.Model/Channels/ChannelInfo.cs new file mode 100644 index 000000000..36e3c17d9 --- /dev/null +++ b/MediaBrowser.Model/Channels/ChannelInfo.cs @@ -0,0 +1,30 @@ + +namespace MediaBrowser.Model.Channels +{ + public class ChannelInfo + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the identifier. + /// </summary> + /// <value>The identifier.</value> + public string Id { get; set; } + + /// <summary> + /// Gets or sets the home page URL. + /// </summary> + /// <value>The home page URL.</value> + public string HomePageUrl { get; set; } + + /// <summary> + /// Gets or sets the features. + /// </summary> + /// <value>The features.</value> + public ChannelFeatures Features { get; set; } + } +} diff --git a/MediaBrowser.Model/Channels/ChannelItemSortField.cs b/MediaBrowser.Model/Channels/ChannelItemSortField.cs new file mode 100644 index 000000000..6b5015b77 --- /dev/null +++ b/MediaBrowser.Model/Channels/ChannelItemSortField.cs @@ -0,0 +1,13 @@ +namespace MediaBrowser.Model.Channels +{ + public enum ChannelItemSortField + { + Name = 0, + CommunityRating = 1, + PremiereDate = 2, + DateCreated = 3, + Runtime = 4, + PlayCount = 5, + CommunityPlayCount = 6 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Channels/ChannelMediaContentType.cs b/MediaBrowser.Model/Channels/ChannelMediaContentType.cs new file mode 100644 index 000000000..efb5021c0 --- /dev/null +++ b/MediaBrowser.Model/Channels/ChannelMediaContentType.cs @@ -0,0 +1,23 @@ +namespace MediaBrowser.Model.Channels +{ + public enum ChannelMediaContentType + { + Clip = 0, + + Podcast = 1, + + Trailer = 2, + + Movie = 3, + + Episode = 4, + + Song = 5, + + MovieExtra = 6, + + TvExtra = 7, + + GameExtra = 8 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Channels/ChannelMediaType.cs b/MediaBrowser.Model/Channels/ChannelMediaType.cs new file mode 100644 index 000000000..102cb6644 --- /dev/null +++ b/MediaBrowser.Model/Channels/ChannelMediaType.cs @@ -0,0 +1,11 @@ +namespace MediaBrowser.Model.Channels +{ + public enum ChannelMediaType + { + Audio = 0, + + Video = 1, + + Photo = 2 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Channels/ChannelQuery.cs b/MediaBrowser.Model/Channels/ChannelQuery.cs new file mode 100644 index 000000000..6b5e95d69 --- /dev/null +++ b/MediaBrowser.Model/Channels/ChannelQuery.cs @@ -0,0 +1,52 @@ +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Querying; +using System; + +namespace MediaBrowser.Model.Channels +{ + public class ChannelQuery + { + /// <summary> + /// Fields to return within the items, in addition to basic information + /// </summary> + /// <value>The fields.</value> + public ItemFields[] Fields { get; set; } + public bool? EnableImages { get; set; } + public int? ImageTypeLimit { get; set; } + public ImageType[] EnableImageTypes { get; set; } + + /// <summary> + /// Gets or sets the user identifier. + /// </summary> + /// <value>The user identifier.</value> + public Guid UserId { get; set; } + + /// <summary> + /// Skips over a given number of items within the results. Use for paging. + /// </summary> + /// <value>The start index.</value> + public int? StartIndex { get; set; } + + /// <summary> + /// The maximum number of items to return + /// </summary> + /// <value>The limit.</value> + public int? Limit { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [supports latest items]. + /// </summary> + /// <value><c>true</c> if [supports latest items]; otherwise, <c>false</c>.</value> + public bool? SupportsLatestItems { get; set; } + + public bool? SupportsMediaDeletion { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is favorite. + /// </summary> + /// <value><c>null</c> if [is favorite] contains no value, <c>true</c> if [is favorite]; otherwise, <c>false</c>.</value> + public bool? IsFavorite { get; set; } + public bool? IsRecordingsFolder { get; set; } + public bool RefreshLatestChannelItems { get; set; } + } +} diff --git a/MediaBrowser.Model/Collections/CollectionCreationResult.cs b/MediaBrowser.Model/Collections/CollectionCreationResult.cs new file mode 100644 index 000000000..689625ac3 --- /dev/null +++ b/MediaBrowser.Model/Collections/CollectionCreationResult.cs @@ -0,0 +1,9 @@ +using System; + +namespace MediaBrowser.Model.Collections +{ + public class CollectionCreationResult + { + public Guid Id { get; set; } + } +} diff --git a/MediaBrowser.Model/Configuration/AccessSchedule.cs b/MediaBrowser.Model/Configuration/AccessSchedule.cs new file mode 100644 index 000000000..3a66cf5bb --- /dev/null +++ b/MediaBrowser.Model/Configuration/AccessSchedule.cs @@ -0,0 +1,22 @@ + +namespace MediaBrowser.Model.Configuration +{ + public class AccessSchedule + { + /// <summary> + /// Gets or sets the day of week. + /// </summary> + /// <value>The day of week.</value> + public DynamicDayOfWeek DayOfWeek { get; set; } + /// <summary> + /// Gets or sets the start hour. + /// </summary> + /// <value>The start hour.</value> + public double StartHour { get; set; } + /// <summary> + /// Gets or sets the end hour. + /// </summary> + /// <value>The end hour.</value> + public double EndHour { get; set; } + } +} diff --git a/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs b/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs new file mode 100644 index 000000000..b5b0101cb --- /dev/null +++ b/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs @@ -0,0 +1,57 @@ +using MediaBrowser.Model.Updates; + +namespace MediaBrowser.Model.Configuration +{ + /// <summary> + /// Serves as a common base class for the Server and UI application Configurations + /// ProtoInclude tells Protobuf about subclasses, + /// The number 50 can be any number, so long as it doesn't clash with any of the ProtoMember numbers either here or in subclasses. + /// </summary> + public class BaseApplicationConfiguration + { + /// <summary> + /// Gets or sets a value indicating whether [enable debug level logging]. + /// </summary> + /// <value><c>true</c> if [enable debug level logging]; otherwise, <c>false</c>.</value> + public bool EnableDebugLevelLogging { get; set; } + + /// <summary> + /// Enable automatically and silently updating of the application + /// </summary> + /// <value><c>true</c> if [enable auto update]; otherwise, <c>false</c>.</value> + public bool EnableAutoUpdate { get; set; } + + /// <summary> + /// The number of days we should retain log files + /// </summary> + /// <value>The log file retention days.</value> + public int LogFileRetentionDays { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [run at startup]. + /// </summary> + /// <value><c>true</c> if [run at startup]; otherwise, <c>false</c>.</value> + public bool RunAtStartup { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is first run. + /// </summary> + /// <value><c>true</c> if this instance is first run; otherwise, <c>false</c>.</value> + public bool IsStartupWizardCompleted { get; set; } + + /// <summary> + /// Gets or sets the cache path. + /// </summary> + /// <value>The cache path.</value> + public string CachePath { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="BaseApplicationConfiguration" /> class. + /// </summary> + public BaseApplicationConfiguration() + { + EnableAutoUpdate = true; + LogFileRetentionDays = 3; + } + } +} diff --git a/MediaBrowser.Model/Configuration/DynamicDayOfWeek.cs b/MediaBrowser.Model/Configuration/DynamicDayOfWeek.cs new file mode 100644 index 000000000..1c7de11fd --- /dev/null +++ b/MediaBrowser.Model/Configuration/DynamicDayOfWeek.cs @@ -0,0 +1,17 @@ + +namespace MediaBrowser.Model.Configuration +{ + public enum DynamicDayOfWeek + { + Sunday = 0, + Monday = 1, + Tuesday = 2, + Wednesday = 3, + Thursday = 4, + Friday = 5, + Saturday = 6, + Everyday = 7, + Weekday = 8, + Weekend = 9 + } +} diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs new file mode 100644 index 000000000..fbc5e1b37 --- /dev/null +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -0,0 +1,36 @@ + +namespace MediaBrowser.Model.Configuration +{ + public class EncodingOptions + { + public int EncodingThreadCount { get; set; } + public string TranscodingTempPath { get; set; } + public double DownMixAudioBoost { get; set; } + public bool EnableThrottling { get; set; } + public int ThrottleDelaySeconds { get; set; } + public string HardwareAccelerationType { get; set; } + public string EncoderAppPath { get; set; } + public string VaapiDevice { get; set; } + public int H264Crf { get; set; } + public string H264Preset { get; set; } + public string DeinterlaceMethod { get; set; } + public bool EnableHardwareEncoding { get; set; } + public bool EnableSubtitleExtraction { get; set; } + + public string[] HardwareDecodingCodecs { get; set; } + + public EncodingOptions() + { + DownMixAudioBoost = 2; + EnableThrottling = true; + ThrottleDelaySeconds = 180; + EncodingThreadCount = -1; + // This is a DRM device that is almost guaranteed to be there on every intel platform, plus it's the default one in ffmpeg if you don't specify anything + VaapiDevice = "/dev/dri/renderD128"; + H264Crf = 23; + EnableHardwareEncoding = true; + EnableSubtitleExtraction = true; + HardwareDecodingCodecs = new string[] { "h264", "vc1" }; + } + } +} diff --git a/MediaBrowser.Model/Configuration/FanartOptions.cs b/MediaBrowser.Model/Configuration/FanartOptions.cs new file mode 100644 index 000000000..6924b25d7 --- /dev/null +++ b/MediaBrowser.Model/Configuration/FanartOptions.cs @@ -0,0 +1,12 @@ + +namespace MediaBrowser.Model.Configuration +{ + public class FanartOptions + { + /// <summary> + /// Gets or sets the user API key. + /// </summary> + /// <value>The user API key.</value> + public string UserApiKey { get; set; } + } +} diff --git a/MediaBrowser.Model/Configuration/ImageOption.cs b/MediaBrowser.Model/Configuration/ImageOption.cs new file mode 100644 index 000000000..ade0af83e --- /dev/null +++ b/MediaBrowser.Model/Configuration/ImageOption.cs @@ -0,0 +1,29 @@ +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Model.Configuration +{ + public class ImageOption + { + /// <summary> + /// Gets or sets the type. + /// </summary> + /// <value>The type.</value> + public ImageType Type { get; set; } + /// <summary> + /// Gets or sets the limit. + /// </summary> + /// <value>The limit.</value> + public int Limit { get; set; } + + /// <summary> + /// Gets or sets the minimum width. + /// </summary> + /// <value>The minimum width.</value> + public int MinWidth { get; set; } + + public ImageOption() + { + Limit = 1; + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Configuration/ImageSavingConvention.cs b/MediaBrowser.Model/Configuration/ImageSavingConvention.cs new file mode 100644 index 000000000..611678e67 --- /dev/null +++ b/MediaBrowser.Model/Configuration/ImageSavingConvention.cs @@ -0,0 +1,8 @@ +namespace MediaBrowser.Model.Configuration +{ + public enum ImageSavingConvention + { + Legacy, + Compatible + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs new file mode 100644 index 000000000..a271d43af --- /dev/null +++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs @@ -0,0 +1,444 @@ +using System; +using MediaBrowser.Model.Entities; +using System.Collections.Generic; + +namespace MediaBrowser.Model.Configuration +{ + public class LibraryOptions + { + public bool EnableArchiveMediaFiles { get; set; } + public bool EnablePhotos { get; set; } + public bool EnableRealtimeMonitor { get; set; } + public bool EnableChapterImageExtraction { get; set; } + public bool ExtractChapterImagesDuringLibraryScan { get; set; } + public bool DownloadImagesInAdvance { get; set; } + public MediaPathInfo[] PathInfos { get; set; } + + public bool SaveLocalMetadata { get; set; } + public bool EnableInternetProviders { get; set; } + public bool ImportMissingEpisodes { get; set; } + public bool EnableAutomaticSeriesGrouping { get; set; } + public bool EnableEmbeddedTitles { get; set; } + + public int AutomaticRefreshIntervalDays { get; set; } + + /// <summary> + /// Gets or sets the preferred metadata language. + /// </summary> + /// <value>The preferred metadata language.</value> + public string PreferredMetadataLanguage { get; set; } + + /// <summary> + /// Gets or sets the metadata country code. + /// </summary> + /// <value>The metadata country code.</value> + public string MetadataCountryCode { get; set; } + + public string SeasonZeroDisplayName { get; set; } + public string[] MetadataSavers { get; set; } + public string[] DisabledLocalMetadataReaders { get; set; } + public string[] LocalMetadataReaderOrder { get; set; } + + public string[] DisabledSubtitleFetchers { get; set; } + public string[] SubtitleFetcherOrder { get; set; } + + public bool SkipSubtitlesIfEmbeddedSubtitlesPresent { get; set; } + public bool SkipSubtitlesIfAudioTrackMatches { get; set; } + public string[] SubtitleDownloadLanguages { get; set; } + public bool RequirePerfectSubtitleMatch { get; set; } + public bool SaveSubtitlesWithMedia { get; set; } + + public TypeOptions[] TypeOptions { get; set; } + + public TypeOptions GetTypeOptions(string type) + { + foreach (var options in TypeOptions) + { + if (string.Equals(options.Type, type, StringComparison.OrdinalIgnoreCase)) + { + return options; + } + } + + return null; + } + + public LibraryOptions() + { + TypeOptions = new TypeOptions[] { }; + DisabledSubtitleFetchers = new string[] { }; + SubtitleFetcherOrder = new string[] { }; + DisabledLocalMetadataReaders = new string[] { }; + + SkipSubtitlesIfAudioTrackMatches = true; + RequirePerfectSubtitleMatch = true; + + EnablePhotos = true; + SaveSubtitlesWithMedia = true; + EnableRealtimeMonitor = true; + PathInfos = new MediaPathInfo[] { }; + EnableInternetProviders = true; + EnableAutomaticSeriesGrouping = true; + SeasonZeroDisplayName = "Specials"; + } + } + + public class MediaPathInfo + { + public string Path { get; set; } + public string NetworkPath { get; set; } + } + + public class TypeOptions + { + public string Type { get; set; } + public string[] MetadataFetchers { get; set; } + public string[] MetadataFetcherOrder { get; set; } + + public string[] ImageFetchers { get; set; } + public string[] ImageFetcherOrder { get; set; } + public ImageOption[] ImageOptions { get; set; } + + public ImageOption GetImageOptions(ImageType type) + { + foreach (ImageOption i in ImageOptions) + { + if (i.Type == type) + { + return i; + } + } + + ImageOption[] options; + if (DefaultImageOptions.TryGetValue(Type, out options)) + { + foreach (ImageOption i in options) + { + if (i.Type == type) + { + return i; + } + } + } + + return DefaultInstance; + } + + public int GetLimit(ImageType type) + { + return GetImageOptions(type).Limit; + } + + public int GetMinWidth(ImageType type) + { + return GetImageOptions(type).MinWidth; + } + + public bool IsEnabled(ImageType type) + { + return GetLimit(type) > 0; + } + + public TypeOptions() + { + MetadataFetchers = new string[] { }; + MetadataFetcherOrder = new string[] { }; + ImageFetchers = new string[] { }; + ImageFetcherOrder = new string[] { }; + ImageOptions = new ImageOption[] { }; + } + + public static Dictionary<string, ImageOption[]> DefaultImageOptions = new Dictionary<string, ImageOption[]> + { + { + "Movie", new [] + { + new ImageOption + { + Limit = 1, + MinWidth = 1280, + Type = ImageType.Backdrop + }, + + // Don't download this by default as it's rarely used. + new ImageOption + { + Limit = 0, + Type = ImageType.Art + }, + + // Don't download this by default as it's rarely used. + new ImageOption + { + Limit = 0, + Type = ImageType.Disc + }, + + new ImageOption + { + Limit = 1, + Type = ImageType.Primary + }, + + new ImageOption + { + Limit = 0, + Type = ImageType.Banner + }, + + new ImageOption + { + Limit = 1, + Type = ImageType.Thumb + }, + + new ImageOption + { + Limit = 1, + Type = ImageType.Logo + } + } + }, + { + "MusicVideo", new [] + { + new ImageOption + { + Limit = 1, + MinWidth = 1280, + Type = ImageType.Backdrop + }, + + // Don't download this by default as it's rarely used. + new ImageOption + { + Limit = 0, + Type = ImageType.Art + }, + + // Don't download this by default as it's rarely used. + new ImageOption + { + Limit = 0, + Type = ImageType.Disc + }, + + new ImageOption + { + Limit = 1, + Type = ImageType.Primary + }, + + new ImageOption + { + Limit = 0, + Type = ImageType.Banner + }, + + new ImageOption + { + Limit = 1, + Type = ImageType.Thumb + }, + + new ImageOption + { + Limit = 1, + Type = ImageType.Logo + } + } + }, + { + "Series", new [] + { + new ImageOption + { + Limit = 1, + MinWidth = 1280, + Type = ImageType.Backdrop + }, + + // Don't download this by default as it's rarely used. + new ImageOption + { + Limit = 0, + Type = ImageType.Art + }, + + new ImageOption + { + Limit = 1, + Type = ImageType.Primary + }, + + new ImageOption + { + Limit = 1, + Type = ImageType.Banner + }, + + new ImageOption + { + Limit = 1, + Type = ImageType.Thumb + }, + + new ImageOption + { + Limit = 1, + Type = ImageType.Logo + } + } + }, + { + "MusicAlbum", new [] + { + new ImageOption + { + Limit = 0, + MinWidth = 1280, + Type = ImageType.Backdrop + }, + + // Don't download this by default as it's rarely used. + new ImageOption + { + Limit = 0, + Type = ImageType.Disc + } + } + }, + { + "MusicArtist", new [] + { + new ImageOption + { + Limit = 1, + MinWidth = 1280, + Type = ImageType.Backdrop + }, + + // Don't download this by default + // They do look great, but most artists won't have them, which means a banner view isn't really possible + new ImageOption + { + Limit = 0, + Type = ImageType.Banner + }, + + // Don't download this by default + // Generally not used + new ImageOption + { + Limit = 0, + Type = ImageType.Art + }, + + new ImageOption + { + Limit = 1, + Type = ImageType.Logo + } + } + }, + { + "BoxSet", new [] + { + new ImageOption + { + Limit = 1, + MinWidth = 1280, + Type = ImageType.Backdrop + }, + + new ImageOption + { + Limit = 1, + Type = ImageType.Primary + }, + + new ImageOption + { + Limit = 1, + Type = ImageType.Thumb + }, + + new ImageOption + { + Limit = 1, + Type = ImageType.Logo + }, + + // Don't download this by default as it's rarely used. + new ImageOption + { + Limit = 0, + Type = ImageType.Art + }, + + // Don't download this by default as it's rarely used. + new ImageOption + { + Limit = 0, + Type = ImageType.Disc + }, + + // Don't download this by default as it's rarely used. + new ImageOption + { + Limit = 0, + Type = ImageType.Banner + } + } + }, + { + "Season", new [] + { + new ImageOption + { + Limit = 0, + MinWidth = 1280, + Type = ImageType.Backdrop + }, + + new ImageOption + { + Limit = 1, + Type = ImageType.Primary + }, + + new ImageOption + { + Limit = 0, + Type = ImageType.Banner + }, + + new ImageOption + { + Limit = 0, + Type = ImageType.Thumb + } + } + }, + { + "Episode", new [] + { + new ImageOption + { + Limit = 0, + MinWidth = 1280, + Type = ImageType.Backdrop + }, + + new ImageOption + { + Limit = 1, + Type = ImageType.Primary + } + } + } + }; + + public static ImageOption DefaultInstance = new ImageOption(); + } +} diff --git a/MediaBrowser.Model/Configuration/MetadataConfiguration.cs b/MediaBrowser.Model/Configuration/MetadataConfiguration.cs new file mode 100644 index 000000000..d1658e5d6 --- /dev/null +++ b/MediaBrowser.Model/Configuration/MetadataConfiguration.cs @@ -0,0 +1,13 @@ + +namespace MediaBrowser.Model.Configuration +{ + public class MetadataConfiguration + { + public bool UseFileCreationTimeForDateAdded { get; set; } + + public MetadataConfiguration() + { + UseFileCreationTimeForDateAdded = true; + } + } +} diff --git a/MediaBrowser.Model/Configuration/MetadataOptions.cs b/MediaBrowser.Model/Configuration/MetadataOptions.cs new file mode 100644 index 000000000..26cfee085 --- /dev/null +++ b/MediaBrowser.Model/Configuration/MetadataOptions.cs @@ -0,0 +1,38 @@ +using MediaBrowser.Model.Extensions; +using System; + +namespace MediaBrowser.Model.Configuration +{ + /// <summary> + /// Class MetadataOptions. + /// </summary> + public class MetadataOptions + { + public string ItemType { get; set; } + + public string[] DisabledMetadataSavers { get; set; } + public string[] LocalMetadataReaderOrder { get; set; } + + public string[] DisabledMetadataFetchers { get; set; } + public string[] MetadataFetcherOrder { get; set; } + + public string[] DisabledImageFetchers { get; set; } + public string[] ImageFetcherOrder { get; set; } + + public MetadataOptions() + { + DisabledMetadataSavers = new string[] { }; + LocalMetadataReaderOrder = new string[] { }; + + DisabledMetadataFetchers = new string[] { }; + MetadataFetcherOrder = new string[] { }; + DisabledImageFetchers = new string[] { }; + ImageFetcherOrder = new string[] { }; + } + + public bool IsMetadataSaverEnabled(string name) + { + return !ListHelper.ContainsIgnoreCase(DisabledMetadataSavers, name); + } + } +} diff --git a/MediaBrowser.Model/Configuration/MetadataPlugin.cs b/MediaBrowser.Model/Configuration/MetadataPlugin.cs new file mode 100644 index 000000000..f3e0ce106 --- /dev/null +++ b/MediaBrowser.Model/Configuration/MetadataPlugin.cs @@ -0,0 +1,17 @@ +namespace MediaBrowser.Model.Configuration +{ + public class MetadataPlugin + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the type. + /// </summary> + /// <value>The type.</value> + public MetadataPluginType Type { get; set; } + } +} diff --git a/MediaBrowser.Model/Configuration/MetadataPluginSummary.cs b/MediaBrowser.Model/Configuration/MetadataPluginSummary.cs new file mode 100644 index 000000000..80142cf43 --- /dev/null +++ b/MediaBrowser.Model/Configuration/MetadataPluginSummary.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Model.Configuration +{ + public class MetadataPluginSummary + { + /// <summary> + /// Gets or sets the type of the item. + /// </summary> + /// <value>The type of the item.</value> + public string ItemType { get; set; } + + /// <summary> + /// Gets or sets the plugins. + /// </summary> + /// <value>The plugins.</value> + public MetadataPlugin[] Plugins { get; set; } + + /// <summary> + /// Gets or sets the supported image types. + /// </summary> + /// <value>The supported image types.</value> + public ImageType[] SupportedImageTypes { get; set; } + + public MetadataPluginSummary() + { + SupportedImageTypes = new ImageType[] { }; + Plugins = new MetadataPlugin[] { }; + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Configuration/MetadataPluginType.cs b/MediaBrowser.Model/Configuration/MetadataPluginType.cs new file mode 100644 index 000000000..5ba0b395e --- /dev/null +++ b/MediaBrowser.Model/Configuration/MetadataPluginType.cs @@ -0,0 +1,16 @@ +namespace MediaBrowser.Model.Configuration +{ + /// <summary> + /// Enum MetadataPluginType + /// </summary> + public enum MetadataPluginType + { + LocalImageProvider, + ImageFetcher, + ImageSaver, + LocalMetadataProvider, + MetadataFetcher, + MetadataSaver, + SubtitleFetcher + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs new file mode 100644 index 000000000..58a839167 --- /dev/null +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -0,0 +1,313 @@ +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using System; + +namespace MediaBrowser.Model.Configuration +{ + /// <summary> + /// Represents the server configuration. + /// </summary> + public class ServerConfiguration : BaseApplicationConfiguration + { + public const int DefaultHttpPort = 8096; + public const int DefaultHttpsPort = 8920; + + /// <summary> + /// Gets or sets a value indicating whether [enable u pn p]. + /// </summary> + /// <value><c>true</c> if [enable u pn p]; otherwise, <c>false</c>.</value> + public bool EnableUPnP { get; set; } + + /// <summary> + /// Gets or sets the public mapped port. + /// </summary> + /// <value>The public mapped port.</value> + public int PublicPort { get; set; } + + /// <summary> + /// Gets or sets the public HTTPS port. + /// </summary> + /// <value>The public HTTPS port.</value> + public int PublicHttpsPort { get; set; } + + /// <summary> + /// Gets or sets the HTTP server port number. + /// </summary> + /// <value>The HTTP server port number.</value> + public int HttpServerPortNumber { get; set; } + + /// <summary> + /// Gets or sets the HTTPS server port number. + /// </summary> + /// <value>The HTTPS server port number.</value> + public int HttpsPortNumber { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [use HTTPS]. + /// </summary> + /// <value><c>true</c> if [use HTTPS]; otherwise, <c>false</c>.</value> + public bool EnableHttps { get; set; } + public bool EnableNormalizedItemByNameIds { get; set; } + + /// <summary> + /// Gets or sets the value pointing to the file system where the ssl certiifcate is located.. + /// </summary> + /// <value>The value pointing to the file system where the ssl certiifcate is located..</value> + public string CertificatePath { get; set; } + public string CertificatePassword { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is port authorized. + /// </summary> + /// <value><c>true</c> if this instance is port authorized; otherwise, <c>false</c>.</value> + public bool IsPortAuthorized { get; set; } + + public bool AutoRunWebApp { get; set; } + public bool EnableRemoteAccess { get; set; } + public bool CameraUploadUpgraded { get; set; } + public bool CollectionsUpgraded { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [enable case sensitive item ids]. + /// </summary> + /// <value><c>true</c> if [enable case sensitive item ids]; otherwise, <c>false</c>.</value> + public bool EnableCaseSensitiveItemIds { get; set; } + + public bool DisableLiveTvChannelUserDataName { get; set; } + + /// <summary> + /// Gets or sets the metadata path. + /// </summary> + /// <value>The metadata path.</value> + public string MetadataPath { get; set; } + public string MetadataNetworkPath { get; set; } + + /// <summary> + /// Gets or sets the preferred metadata language. + /// </summary> + /// <value>The preferred metadata language.</value> + public string PreferredMetadataLanguage { get; set; } + + /// <summary> + /// Gets or sets the metadata country code. + /// </summary> + /// <value>The metadata country code.</value> + public string MetadataCountryCode { get; set; } + + /// <summary> + /// Characters to be replaced with a ' ' in strings to create a sort name + /// </summary> + /// <value>The sort replace characters.</value> + public string[] SortReplaceCharacters { get; set; } + + /// <summary> + /// Characters to be removed from strings to create a sort name + /// </summary> + /// <value>The sort remove characters.</value> + public string[] SortRemoveCharacters { get; set; } + + /// <summary> + /// Words to be removed from strings to create a sort name + /// </summary> + /// <value>The sort remove words.</value> + public string[] SortRemoveWords { get; set; } + + /// <summary> + /// Gets or sets the minimum percentage of an item that must be played in order for playstate to be updated. + /// </summary> + /// <value>The min resume PCT.</value> + public int MinResumePct { get; set; } + + /// <summary> + /// Gets or sets the maximum percentage of an item that can be played while still saving playstate. If this percentage is crossed playstate will be reset to the beginning and the item will be marked watched. + /// </summary> + /// <value>The max resume PCT.</value> + public int MaxResumePct { get; set; } + + /// <summary> + /// Gets or sets the minimum duration that an item must have in order to be eligible for playstate updates.. + /// </summary> + /// <value>The min resume duration seconds.</value> + public int MinResumeDurationSeconds { get; set; } + + /// <summary> + /// The delay in seconds that we will wait after a file system change to try and discover what has been added/removed + /// Some delay is necessary with some items because their creation is not atomic. It involves the creation of several + /// different directories and files. + /// </summary> + /// <value>The file watcher delay.</value> + public int LibraryMonitorDelay { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [enable dashboard response caching]. + /// Allows potential contributors without visual studio to modify production dashboard code and test changes. + /// </summary> + /// <value><c>true</c> if [enable dashboard response caching]; otherwise, <c>false</c>.</value> + public bool EnableDashboardResponseCaching { get; set; } + + /// <summary> + /// Allows the dashboard to be served from a custom path. + /// </summary> + /// <value>The dashboard source path.</value> + public string DashboardSourcePath { get; set; } + + /// <summary> + /// Gets or sets the image saving convention. + /// </summary> + /// <value>The image saving convention.</value> + public ImageSavingConvention ImageSavingConvention { get; set; } + + public MetadataOptions[] MetadataOptions { get; set; } + + public bool EnableAutomaticRestart { get; set; } + public bool SkipDeserializationForBasicTypes { get; set; } + + public string ServerName { get; set; } + public string WanDdns { get; set; } + + public string UICulture { get; set; } + + public bool SaveMetadataHidden { get; set; } + + public NameValuePair[] ContentTypes { get; set; } + + public int RemoteClientBitrateLimit { get; set; } + + public int SchemaVersion { get; set; } + + public bool EnableAnonymousUsageReporting { get; set; } + public bool EnableFolderView { get; set; } + public bool EnableGroupingIntoCollections { get; set; } + public bool DisplaySpecialsWithinSeasons { get; set; } + public string[] LocalNetworkSubnets { get; set; } + public string[] LocalNetworkAddresses { get; set; } + public string[] CodecsUsed { get; set; } + public bool EnableExternalContentInSuggestions { get; set; } + public bool RequireHttps { get; set; } + public bool IsBehindProxy { get; set; } + public bool EnableNewOmdbSupport { get; set; } + + public string[] RemoteIPFilter { get; set; } + public bool IsRemoteIPFilterBlacklist { get; set; } + + public int ImageExtractionTimeoutMs { get; set; } + + public PathSubstitution[] PathSubstitutions { get; set; } + public bool EnableSimpleArtistDetection { get; set; } + + public string[] UninstalledPlugins { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="ServerConfiguration" /> class. + /// </summary> + public ServerConfiguration() + { + UninstalledPlugins = new string[] { }; + RemoteIPFilter = new string[] { }; + LocalNetworkSubnets = new string[] { }; + LocalNetworkAddresses = new string[] { }; + CodecsUsed = new string[] { }; + ImageExtractionTimeoutMs = 0; + PathSubstitutions = new PathSubstitution[] { }; + EnableSimpleArtistDetection = true; + + DisplaySpecialsWithinSeasons = true; + EnableExternalContentInSuggestions = true; + + ImageSavingConvention = ImageSavingConvention.Compatible; + PublicPort = DefaultHttpPort; + PublicHttpsPort = DefaultHttpsPort; + HttpServerPortNumber = DefaultHttpPort; + HttpsPortNumber = DefaultHttpsPort; + EnableHttps = true; + EnableDashboardResponseCaching = true; + EnableAnonymousUsageReporting = true; + EnableCaseSensitiveItemIds = true; + + EnableAutomaticRestart = true; + AutoRunWebApp = true; + EnableRemoteAccess = true; + + EnableUPnP = true; + MinResumePct = 5; + MaxResumePct = 90; + + // 5 minutes + MinResumeDurationSeconds = 300; + + LibraryMonitorDelay = 60; + + ContentTypes = new NameValuePair[] { }; + + PreferredMetadataLanguage = "en"; + MetadataCountryCode = "US"; + + SortReplaceCharacters = new[] { ".", "+", "%" }; + SortRemoveCharacters = new[] { ",", "&", "-", "{", "}", "'" }; + SortRemoveWords = new[] { "the", "a", "an" }; + + UICulture = "en-us"; + + MetadataOptions = new[] + { + new MetadataOptions {ItemType = "Book"}, + + new MetadataOptions + { + ItemType = "Movie" + }, + + new MetadataOptions + { + ItemType = "MusicVideo", + DisabledMetadataFetchers = new []{ "The Open Movie Database" }, + DisabledImageFetchers = new []{ "The Open Movie Database", "FanArt" } + }, + + new MetadataOptions + { + ItemType = "Series", + DisabledMetadataFetchers = new []{ "TheMovieDb" }, + DisabledImageFetchers = new []{ "TheMovieDb" } + }, + + new MetadataOptions + { + ItemType = "MusicAlbum", + DisabledMetadataFetchers = new []{ "TheAudioDB" } + }, + + new MetadataOptions + { + ItemType = "MusicArtist", + DisabledMetadataFetchers = new []{ "TheAudioDB" } + }, + + new MetadataOptions + { + ItemType = "BoxSet" + }, + + new MetadataOptions + { + ItemType = "Season", + DisabledMetadataFetchers = new []{ "TheMovieDb" }, + DisabledImageFetchers = new [] { "FanArt" } + }, + + new MetadataOptions + { + ItemType = "Episode", + DisabledMetadataFetchers = new []{ "The Open Movie Database", "TheMovieDb" }, + DisabledImageFetchers = new []{ "The Open Movie Database", "TheMovieDb" } + } + }; + } + } + + public class PathSubstitution + { + public string From { get; set; } + public string To { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs b/MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs new file mode 100644 index 000000000..fbee912d9 --- /dev/null +++ b/MediaBrowser.Model/Configuration/SubtitlePlaybackMode.cs @@ -0,0 +1,11 @@ +namespace MediaBrowser.Model.Configuration +{ + public enum SubtitlePlaybackMode + { + Default = 0, + Always = 1, + OnlyForced = 2, + None = 3, + Smart = 4 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Configuration/UnratedItem.cs b/MediaBrowser.Model/Configuration/UnratedItem.cs new file mode 100644 index 000000000..1082d684b --- /dev/null +++ b/MediaBrowser.Model/Configuration/UnratedItem.cs @@ -0,0 +1,16 @@ +namespace MediaBrowser.Model.Configuration +{ + public enum UnratedItem + { + Movie, + Trailer, + Series, + Music, + Game, + Book, + LiveTvChannel, + LiveTvProgram, + ChannelContent, + Other + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs new file mode 100644 index 000000000..34b0be095 --- /dev/null +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -0,0 +1,66 @@ +using System; + +namespace MediaBrowser.Model.Configuration +{ + /// <summary> + /// Class UserConfiguration + /// </summary> + public class UserConfiguration + { + /// <summary> + /// Gets or sets the audio language preference. + /// </summary> + /// <value>The audio language preference.</value> + public string AudioLanguagePreference { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [play default audio track]. + /// </summary> + /// <value><c>true</c> if [play default audio track]; otherwise, <c>false</c>.</value> + public bool PlayDefaultAudioTrack { get; set; } + + /// <summary> + /// Gets or sets the subtitle language preference. + /// </summary> + /// <value>The subtitle language preference.</value> + public string SubtitleLanguagePreference { get; set; } + + public bool DisplayMissingEpisodes { get; set; } + + public string[] GroupedFolders { get; set; } + + public SubtitlePlaybackMode SubtitleMode { get; set; } + public bool DisplayCollectionsView { get; set; } + + public bool EnableLocalPassword { get; set; } + + public string[] OrderedViews { get; set; } + + public string[] LatestItemsExcludes { get; set; } + public string[] MyMediaExcludes { get; set; } + + public bool HidePlayedInLatest { get; set; } + + public bool RememberAudioSelections { get; set; } + public bool RememberSubtitleSelections { get; set; } + public bool EnableNextEpisodeAutoPlay { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="UserConfiguration" /> class. + /// </summary> + public UserConfiguration() + { + EnableNextEpisodeAutoPlay = true; + RememberAudioSelections = true; + RememberSubtitleSelections = true; + + HidePlayedInLatest = true; + PlayDefaultAudioTrack = true; + + LatestItemsExcludes = new string[] {}; + OrderedViews = new string[] {}; + MyMediaExcludes = new string[] {}; + GroupedFolders = new string[] {}; + } + } +} diff --git a/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs b/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs new file mode 100644 index 000000000..de8a59a3d --- /dev/null +++ b/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs @@ -0,0 +1,23 @@ + +namespace MediaBrowser.Model.Configuration +{ + public class XbmcMetadataOptions + { + public string UserId { get; set; } + + public string ReleaseDateFormat { get; set; } + + public bool SaveImagePathsInNfo { get; set; } + public bool EnablePathSubstitution { get; set; } + + public bool EnableExtraThumbsDuplication { get; set; } + + public XbmcMetadataOptions() + { + ReleaseDateFormat = "yyyy-MM-dd"; + + SaveImagePathsInNfo = true; + EnablePathSubstitution = true; + } + } +} diff --git a/MediaBrowser.Model/Connect/ConnectAuthenticationExchangeResult.cs b/MediaBrowser.Model/Connect/ConnectAuthenticationExchangeResult.cs new file mode 100644 index 000000000..c60224045 --- /dev/null +++ b/MediaBrowser.Model/Connect/ConnectAuthenticationExchangeResult.cs @@ -0,0 +1,17 @@ + +namespace MediaBrowser.Model.Connect +{ + public class ConnectAuthenticationExchangeResult + { + /// <summary> + /// Gets or sets the local user identifier. + /// </summary> + /// <value>The local user identifier.</value> + public string LocalUserId { get; set; } + /// <summary> + /// Gets or sets the access token. + /// </summary> + /// <value>The access token.</value> + public string AccessToken { get; set; } + } +} diff --git a/MediaBrowser.Model/Connect/ConnectAuthenticationResult.cs b/MediaBrowser.Model/Connect/ConnectAuthenticationResult.cs new file mode 100644 index 000000000..0df035e57 --- /dev/null +++ b/MediaBrowser.Model/Connect/ConnectAuthenticationResult.cs @@ -0,0 +1,17 @@ + +namespace MediaBrowser.Model.Connect +{ + public class ConnectAuthenticationResult + { + /// <summary> + /// Gets or sets the user. + /// </summary> + /// <value>The user.</value> + public ConnectUser User { get; set; } + /// <summary> + /// Gets or sets the access token. + /// </summary> + /// <value>The access token.</value> + public string AccessToken { get; set; } + } +} diff --git a/MediaBrowser.Model/Connect/ConnectAuthorization.cs b/MediaBrowser.Model/Connect/ConnectAuthorization.cs new file mode 100644 index 000000000..84dbf5c36 --- /dev/null +++ b/MediaBrowser.Model/Connect/ConnectAuthorization.cs @@ -0,0 +1,21 @@ +using System; + +namespace MediaBrowser.Model.Connect +{ + public class ConnectAuthorization + { + public string ConnectUserId { get; set; } + public string UserName { get; set; } + public string ImageUrl { get; set; } + public string Id { get; set; } + public string[] EnabledLibraries { get; set; } + public bool EnableLiveTv { get; set; } + public string[] EnabledChannels { get; set; } + + public ConnectAuthorization() + { + EnabledLibraries = new string[] {}; + EnabledChannels = new string[] {}; + } + } +} diff --git a/MediaBrowser.Model/Connect/ConnectAuthorizationRequest.cs b/MediaBrowser.Model/Connect/ConnectAuthorizationRequest.cs new file mode 100644 index 000000000..ba4e9f5cf --- /dev/null +++ b/MediaBrowser.Model/Connect/ConnectAuthorizationRequest.cs @@ -0,0 +1,19 @@ +using System; + +namespace MediaBrowser.Model.Connect +{ + public class ConnectAuthorizationRequest + { + public string SendingUserId { get; set; } + public string ConnectUserName { get; set; } + public string[] EnabledLibraries { get; set; } + public bool EnableLiveTv { get; set; } + public string[] EnabledChannels { get; set; } + + public ConnectAuthorizationRequest() + { + EnabledLibraries = new string[] {}; + EnabledChannels = new string[] {}; + } + } +} diff --git a/MediaBrowser.Model/Connect/ConnectUser.cs b/MediaBrowser.Model/Connect/ConnectUser.cs new file mode 100644 index 000000000..da290da12 --- /dev/null +++ b/MediaBrowser.Model/Connect/ConnectUser.cs @@ -0,0 +1,12 @@ + +namespace MediaBrowser.Model.Connect +{ + public class ConnectUser + { + public string Id { get; set; } + public string Name { get; set; } + public string Email { get; set; } + public bool IsActive { get; set; } + public string ImageUrl { get; set; } + } +} diff --git a/MediaBrowser.Model/Connect/ConnectUserQuery.cs b/MediaBrowser.Model/Connect/ConnectUserQuery.cs new file mode 100644 index 000000000..a7dc649a8 --- /dev/null +++ b/MediaBrowser.Model/Connect/ConnectUserQuery.cs @@ -0,0 +1,11 @@ + +namespace MediaBrowser.Model.Connect +{ + public class ConnectUserQuery + { + public string Id { get; set; } + public string Name { get; set; } + public string Email { get; set; } + public string NameOrEmail { get; set; } + } +} diff --git a/MediaBrowser.Model/Connect/UserLinkType.cs b/MediaBrowser.Model/Connect/UserLinkType.cs new file mode 100644 index 000000000..4ac5bfde1 --- /dev/null +++ b/MediaBrowser.Model/Connect/UserLinkType.cs @@ -0,0 +1,15 @@ + +namespace MediaBrowser.Model.Connect +{ + public enum UserLinkType + { + /// <summary> + /// The linked user + /// </summary> + LinkedUser = 0, + /// <summary> + /// The guest + /// </summary> + Guest = 1 + } +} diff --git a/MediaBrowser.Model/Cryptography/ICryptoProvider.cs b/MediaBrowser.Model/Cryptography/ICryptoProvider.cs new file mode 100644 index 000000000..7a82dee52 --- /dev/null +++ b/MediaBrowser.Model/Cryptography/ICryptoProvider.cs @@ -0,0 +1,13 @@ +using System; +using System.IO; + +namespace MediaBrowser.Model.Cryptography +{ + public interface ICryptoProvider + { + Guid GetMD5(string str); + byte[] ComputeMD5(Stream str); + byte[] ComputeMD5(byte[] bytes); + byte[] ComputeSHA1(byte[] bytes); + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Devices/ContentUploadHistory.cs b/MediaBrowser.Model/Devices/ContentUploadHistory.cs new file mode 100644 index 000000000..2b344df24 --- /dev/null +++ b/MediaBrowser.Model/Devices/ContentUploadHistory.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Model.Devices +{ + public class ContentUploadHistory + { + public string DeviceId { get; set; } + public LocalFileInfo[] FilesUploaded { get; set; } + + public ContentUploadHistory() + { + FilesUploaded = new LocalFileInfo[] { }; + } + } +} diff --git a/MediaBrowser.Model/Devices/DeviceInfo.cs b/MediaBrowser.Model/Devices/DeviceInfo.cs new file mode 100644 index 000000000..590dc5eb3 --- /dev/null +++ b/MediaBrowser.Model/Devices/DeviceInfo.cs @@ -0,0 +1,69 @@ +using MediaBrowser.Model.Session; +using System; + +namespace MediaBrowser.Model.Devices +{ + public class DeviceInfo + { + /// <summary> + /// Gets or sets the name of the reported. + /// </summary> + /// <value>The name of the reported.</value> + public string ReportedName { get; set; } + /// <summary> + /// Gets or sets the name of the custom. + /// </summary> + /// <value>The name of the custom.</value> + public string CustomName { get; set; } + /// <summary> + /// Gets or sets the camera upload path. + /// </summary> + /// <value>The camera upload path.</value> + public string CameraUploadPath { get; set; } + + public string Name { get; set; } + + /// <summary> + /// Gets or sets the identifier. + /// </summary> + /// <value>The identifier.</value> + public string Id { get; set; } + /// <summary> + /// Gets or sets the last name of the user. + /// </summary> + /// <value>The last name of the user.</value> + public string LastUserName { 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 application version. + /// </summary> + /// <value>The application version.</value> + public string AppVersion { get; set; } + /// <summary> + /// Gets or sets the last user identifier. + /// </summary> + /// <value>The last user identifier.</value> + public Guid LastUserId { get; set; } + /// <summary> + /// Gets or sets the date last modified. + /// </summary> + /// <value>The date last modified.</value> + public DateTime DateLastActivity { get; set; } + /// <summary> + /// Gets or sets the capabilities. + /// </summary> + /// <value>The capabilities.</value> + public ClientCapabilities Capabilities { get; set; } + + public DeviceInfo() + { + Capabilities = new ClientCapabilities(); + } + + public string IconUrl { get; set; } + } +} diff --git a/MediaBrowser.Model/Devices/DeviceQuery.cs b/MediaBrowser.Model/Devices/DeviceQuery.cs new file mode 100644 index 000000000..9ceea1ea8 --- /dev/null +++ b/MediaBrowser.Model/Devices/DeviceQuery.cs @@ -0,0 +1,22 @@ + +namespace MediaBrowser.Model.Devices +{ + public class DeviceQuery + { + /// <summary> + /// Gets or sets a value indicating whether [supports unique identifier]. + /// </summary> + /// <value><c>null</c> if [supports unique identifier] contains no value, <c>true</c> if [supports unique identifier]; otherwise, <c>false</c>.</value> + public bool? SupportsPersistentIdentifier { get; set; } + /// <summary> + /// Gets or sets a value indicating whether [supports synchronize]. + /// </summary> + /// <value><c>null</c> if [supports synchronize] contains no value, <c>true</c> if [supports synchronize]; otherwise, <c>false</c>.</value> + public bool? SupportsSync { get; set; } + /// <summary> + /// Gets or sets the user identifier. + /// </summary> + /// <value>The user identifier.</value> + public string UserId { get; set; } + } +} diff --git a/MediaBrowser.Model/Devices/DevicesOptions.cs b/MediaBrowser.Model/Devices/DevicesOptions.cs new file mode 100644 index 000000000..5bcdd1a82 --- /dev/null +++ b/MediaBrowser.Model/Devices/DevicesOptions.cs @@ -0,0 +1,24 @@ +using System; + +namespace MediaBrowser.Model.Devices +{ + public class DevicesOptions + { + public string[] EnabledCameraUploadDevices { get; set; } + public string CameraUploadPath { get; set; } + public bool EnableCameraUploadSubfolders { get; set; } + + public DevicesOptions() + { + EnabledCameraUploadDevices = new string[] {}; + } + } + + public class DeviceOptions + { + public string[] EnabledCameraUploadDevices { get; set; } + public string CameraUploadPath { get; set; } + public bool EnableCameraUploadSubfolders { get; set; } + public string CustomName { get; set; } + } +} diff --git a/MediaBrowser.Model/Devices/LocalFileInfo.cs b/MediaBrowser.Model/Devices/LocalFileInfo.cs new file mode 100644 index 000000000..e7a78bf8b --- /dev/null +++ b/MediaBrowser.Model/Devices/LocalFileInfo.cs @@ -0,0 +1,11 @@ + +namespace MediaBrowser.Model.Devices +{ + public class LocalFileInfo + { + public string Name { get; set; } + public string Id { get; set; } + public string Album { get; set; } + public string MimeType { get; set; } + } +} diff --git a/MediaBrowser.Model/Diagnostics/IProcess.cs b/MediaBrowser.Model/Diagnostics/IProcess.cs new file mode 100644 index 000000000..7cd26af00 --- /dev/null +++ b/MediaBrowser.Model/Diagnostics/IProcess.cs @@ -0,0 +1,21 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +namespace MediaBrowser.Model.Diagnostics +{ + public interface IProcess : IDisposable + { + event EventHandler Exited; + + void Kill(); + bool WaitForExit(int timeMs); + Task<bool> WaitForExitAsync(int timeMs); + int ExitCode { get; } + void Start(); + StreamWriter StandardInput { get; } + StreamReader StandardError { get; } + StreamReader StandardOutput { get; } + ProcessOptions StartInfo { get; } + } +} diff --git a/MediaBrowser.Model/Diagnostics/IProcessFactory.cs b/MediaBrowser.Model/Diagnostics/IProcessFactory.cs new file mode 100644 index 000000000..a60c4b4fb --- /dev/null +++ b/MediaBrowser.Model/Diagnostics/IProcessFactory.cs @@ -0,0 +1,24 @@ +using System; + +namespace MediaBrowser.Model.Diagnostics +{ + public interface IProcessFactory + { + IProcess Create(ProcessOptions options); + } + + public class ProcessOptions + { + public String FileName { get; set; } + public String Arguments { get; set; } + public String WorkingDirectory { get; set; } + public bool CreateNoWindow { get; set; } + public bool UseShellExecute { get; set; } + public bool EnableRaisingEvents { get; set; } + public bool ErrorDialog { get; set; } + public bool RedirectStandardError { get; set; } + public bool RedirectStandardInput { get; set; } + public bool RedirectStandardOutput { get; set; } + public bool IsHidden { get; set; } + } +} diff --git a/MediaBrowser.Model/Dlna/AudioOptions.cs b/MediaBrowser.Model/Dlna/AudioOptions.cs new file mode 100644 index 000000000..189f64635 --- /dev/null +++ b/MediaBrowser.Model/Dlna/AudioOptions.cs @@ -0,0 +1,87 @@ +using MediaBrowser.Model.Dto; +using System.Collections.Generic; +using System; + +namespace MediaBrowser.Model.Dlna +{ + /// <summary> + /// Class AudioOptions. + /// </summary> + public class AudioOptions + { + public AudioOptions() + { + Context = EncodingContext.Streaming; + + EnableDirectPlay = true; + EnableDirectStream = true; + } + + public bool EnableDirectPlay { get; set; } + public bool EnableDirectStream { get; set; } + public bool ForceDirectPlay { get; set; } + public bool ForceDirectStream { get; set; } + + public Guid ItemId { get; set; } + public MediaSourceInfo[] MediaSources { get; set; } + public DeviceProfile Profile { get; set; } + + /// <summary> + /// Optional. Only needed if a specific AudioStreamIndex or SubtitleStreamIndex are requested. + /// </summary> + public string MediaSourceId { get; set; } + + public string DeviceId { get; set; } + + /// <summary> + /// Allows an override of supported number of audio channels + /// Example: DeviceProfile supports five channel, but user only has stereo speakers + /// </summary> + public int? MaxAudioChannels { get; set; } + + /// <summary> + /// The application's configured quality setting + /// </summary> + public long? MaxBitrate { get; set; } + + /// <summary> + /// Gets or sets the context. + /// </summary> + /// <value>The context.</value> + public EncodingContext Context { get; set; } + + /// <summary> + /// Gets or sets the audio transcoding bitrate. + /// </summary> + /// <value>The audio transcoding bitrate.</value> + public int? AudioTranscodingBitrate { get; set; } + + /// <summary> + /// Gets the maximum bitrate. + /// </summary> + /// <returns>System.Nullable<System.Int32>.</returns> + public long? GetMaxBitrate(bool isAudio) + { + if (MaxBitrate.HasValue) + { + return MaxBitrate; + } + + if (Profile != null) + { + if (Context == EncodingContext.Static) + { + if (isAudio && Profile.MaxStaticMusicBitrate.HasValue) + { + return Profile.MaxStaticMusicBitrate; + } + return Profile.MaxStaticBitrate; + } + + return Profile.MaxStreamingBitrate; + } + + return null; + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/CodecProfile.cs b/MediaBrowser.Model/Dlna/CodecProfile.cs new file mode 100644 index 000000000..6d143962d --- /dev/null +++ b/MediaBrowser.Model/Dlna/CodecProfile.cs @@ -0,0 +1,68 @@ +using MediaBrowser.Model.Extensions; +using System.Collections.Generic; +using System.Xml.Serialization; +using MediaBrowser.Model.Dlna; + +namespace MediaBrowser.Model.Dlna +{ + public class CodecProfile + { + [XmlAttribute("type")] + public CodecType Type { get; set; } + + public ProfileCondition[] Conditions { get; set; } + + public ProfileCondition[] ApplyConditions { get; set; } + + [XmlAttribute("codec")] + public string Codec { get; set; } + + [XmlAttribute("container")] + public string Container { get; set; } + + public CodecProfile() + { + Conditions = new ProfileCondition[] {}; + ApplyConditions = new ProfileCondition[] { }; + } + + public string[] GetCodecs() + { + return ContainerProfile.SplitValue(Codec); + } + + private bool ContainsContainer(string container) + { + return ContainerProfile.ContainsContainer(Container, container); + } + + public bool ContainsAnyCodec(string codec, string container) + { + return ContainsAnyCodec(ContainerProfile.SplitValue(codec), container); + } + + public bool ContainsAnyCodec(string[] codec, string container) + { + if (!ContainsContainer(container)) + { + return false; + } + + var codecs = GetCodecs(); + if (codecs.Length == 0) + { + return true; + } + + foreach (var val in codec) + { + if (ListHelper.ContainsIgnoreCase(codecs, val)) + { + return true; + } + } + + return false; + } + } +} diff --git a/MediaBrowser.Model/Dlna/CodecType.cs b/MediaBrowser.Model/Dlna/CodecType.cs new file mode 100644 index 000000000..415cae7ac --- /dev/null +++ b/MediaBrowser.Model/Dlna/CodecType.cs @@ -0,0 +1,9 @@ +namespace MediaBrowser.Model.Dlna +{ + public enum CodecType + { + Video = 0, + VideoAudio = 1, + Audio = 2 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs new file mode 100644 index 000000000..a550ee982 --- /dev/null +++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs @@ -0,0 +1,284 @@ +using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.MediaInfo; +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace MediaBrowser.Model.Dlna +{ + public class ConditionProcessor + { + public bool IsVideoConditionSatisfied(ProfileCondition condition, + int? width, + int? height, + int? videoBitDepth, + int? videoBitrate, + string videoProfile, + double? videoLevel, + float? videoFramerate, + int? packetLength, + TransportStreamTimestamp? timestamp, + bool? isAnamorphic, + bool? isInterlaced, + int? refFrames, + int? numVideoStreams, + int? numAudioStreams, + string videoCodecTag, + bool? isAvc ) + { + switch (condition.Property) + { + case ProfileConditionValue.IsInterlaced: + return IsConditionSatisfied(condition, isInterlaced); + case ProfileConditionValue.IsAnamorphic: + return IsConditionSatisfied(condition, isAnamorphic); + case ProfileConditionValue.IsAvc: + return IsConditionSatisfied(condition, isAvc); + case ProfileConditionValue.VideoFramerate: + return IsConditionSatisfied(condition, videoFramerate); + case ProfileConditionValue.VideoLevel: + return IsConditionSatisfied(condition, videoLevel); + case ProfileConditionValue.VideoProfile: + return IsConditionSatisfied(condition, videoProfile); + case ProfileConditionValue.VideoCodecTag: + return IsConditionSatisfied(condition, videoCodecTag); + case ProfileConditionValue.PacketLength: + return IsConditionSatisfied(condition, packetLength); + case ProfileConditionValue.VideoBitDepth: + return IsConditionSatisfied(condition, videoBitDepth); + case ProfileConditionValue.VideoBitrate: + return IsConditionSatisfied(condition, videoBitrate); + case ProfileConditionValue.Height: + return IsConditionSatisfied(condition, height); + case ProfileConditionValue.Width: + return IsConditionSatisfied(condition, width); + case ProfileConditionValue.RefFrames: + return IsConditionSatisfied(condition, refFrames); + case ProfileConditionValue.NumAudioStreams: + return IsConditionSatisfied(condition, numAudioStreams); + case ProfileConditionValue.NumVideoStreams: + return IsConditionSatisfied(condition, numVideoStreams); + case ProfileConditionValue.VideoTimestamp: + return IsConditionSatisfied(condition, timestamp); + default: + return true; + } + } + + public bool IsImageConditionSatisfied(ProfileCondition condition, int? width, int? height) + { + switch (condition.Property) + { + case ProfileConditionValue.Height: + return IsConditionSatisfied(condition, height); + case ProfileConditionValue.Width: + return IsConditionSatisfied(condition, width); + default: + throw new ArgumentException("Unexpected condition on image file: " + condition.Property); + } + } + + public bool IsAudioConditionSatisfied(ProfileCondition condition, int? audioChannels, int? audioBitrate, int? audioSampleRate, int? audioBitDepth) + { + switch (condition.Property) + { + case ProfileConditionValue.AudioBitrate: + return IsConditionSatisfied(condition, audioBitrate); + case ProfileConditionValue.AudioChannels: + return IsConditionSatisfied(condition, audioChannels); + case ProfileConditionValue.AudioSampleRate: + return IsConditionSatisfied(condition, audioSampleRate); + case ProfileConditionValue.AudioBitDepth: + return IsConditionSatisfied(condition, audioBitDepth); + default: + throw new ArgumentException("Unexpected condition on audio file: " + condition.Property); + } + } + + public bool IsVideoAudioConditionSatisfied(ProfileCondition condition, + int? audioChannels, + int? audioBitrate, + int? audioSampleRate, + int? audioBitDepth, + string audioProfile, + bool? isSecondaryTrack) + { + switch (condition.Property) + { + case ProfileConditionValue.AudioProfile: + return IsConditionSatisfied(condition, audioProfile); + case ProfileConditionValue.AudioBitrate: + return IsConditionSatisfied(condition, audioBitrate); + case ProfileConditionValue.AudioChannels: + return IsConditionSatisfied(condition, audioChannels); + case ProfileConditionValue.IsSecondaryAudio: + return IsConditionSatisfied(condition, isSecondaryTrack); + case ProfileConditionValue.AudioSampleRate: + return IsConditionSatisfied(condition, audioSampleRate); + case ProfileConditionValue.AudioBitDepth: + return IsConditionSatisfied(condition, audioBitDepth); + default: + throw new ArgumentException("Unexpected condition on audio file: " + condition.Property); + } + } + + private bool IsConditionSatisfied(ProfileCondition condition, int? currentValue) + { + if (!currentValue.HasValue) + { + // If the value is unknown, it satisfies if not marked as required + return !condition.IsRequired; + } + + int expected; + if (int.TryParse(condition.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out expected)) + { + switch (condition.Condition) + { + case ProfileConditionType.Equals: + case ProfileConditionType.EqualsAny: + return currentValue.Value.Equals(expected); + case ProfileConditionType.GreaterThanEqual: + return currentValue.Value >= expected; + case ProfileConditionType.LessThanEqual: + return currentValue.Value <= expected; + case ProfileConditionType.NotEquals: + return !currentValue.Value.Equals(expected); + default: + throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition); + } + } + + return false; + } + + private bool IsConditionSatisfied(ProfileCondition condition, string currentValue) + { + if (string.IsNullOrEmpty(currentValue)) + { + // If the value is unknown, it satisfies if not marked as required + return !condition.IsRequired; + } + + string expected = condition.Value; + + switch (condition.Condition) + { + case ProfileConditionType.EqualsAny: + { + return ListHelper.ContainsIgnoreCase(expected.Split('|'), currentValue); + } + case ProfileConditionType.Equals: + return StringHelper.EqualsIgnoreCase(currentValue, expected); + case ProfileConditionType.NotEquals: + return !StringHelper.EqualsIgnoreCase(currentValue, expected); + default: + throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition); + } + } + + private bool IsConditionSatisfied(ProfileCondition condition, bool? currentValue) + { + if (!currentValue.HasValue) + { + // If the value is unknown, it satisfies if not marked as required + return !condition.IsRequired; + } + + bool expected; + if (bool.TryParse(condition.Value, out expected)) + { + switch (condition.Condition) + { + case ProfileConditionType.Equals: + return currentValue.Value == expected; + case ProfileConditionType.NotEquals: + return currentValue.Value != expected; + default: + throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition); + } + } + + return false; + } + + private bool IsConditionSatisfied(ProfileCondition condition, float currentValue) + { + if (currentValue <= 0) + { + // If the value is unknown, it satisfies if not marked as required + return !condition.IsRequired; + } + + float expected; + if (float.TryParse(condition.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out expected)) + { + switch (condition.Condition) + { + case ProfileConditionType.Equals: + return currentValue.Equals(expected); + case ProfileConditionType.GreaterThanEqual: + return currentValue >= expected; + case ProfileConditionType.LessThanEqual: + return currentValue <= expected; + case ProfileConditionType.NotEquals: + return !currentValue.Equals(expected); + default: + throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition); + } + } + + return false; + } + + private bool IsConditionSatisfied(ProfileCondition condition, double? currentValue) + { + if (!currentValue.HasValue) + { + // If the value is unknown, it satisfies if not marked as required + return !condition.IsRequired; + } + + double expected; + if (double.TryParse(condition.Value, NumberStyles.Any, CultureInfo.InvariantCulture, out expected)) + { + switch (condition.Condition) + { + case ProfileConditionType.Equals: + return currentValue.Value.Equals(expected); + case ProfileConditionType.GreaterThanEqual: + return currentValue.Value >= expected; + case ProfileConditionType.LessThanEqual: + return currentValue.Value <= expected; + case ProfileConditionType.NotEquals: + return !currentValue.Value.Equals(expected); + default: + throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition); + } + } + + return false; + } + + private bool IsConditionSatisfied(ProfileCondition condition, TransportStreamTimestamp? timestamp) + { + if (!timestamp.HasValue) + { + // If the value is unknown, it satisfies if not marked as required + return !condition.IsRequired; + } + + TransportStreamTimestamp expected = (TransportStreamTimestamp)Enum.Parse(typeof(TransportStreamTimestamp), condition.Value, true); + + switch (condition.Condition) + { + case ProfileConditionType.Equals: + return timestamp == expected; + case ProfileConditionType.NotEquals: + return timestamp != expected; + default: + throw new InvalidOperationException("Unexpected ProfileConditionType: " + condition.Condition); + } + } + } +} diff --git a/MediaBrowser.Model/Dlna/ContainerProfile.cs b/MediaBrowser.Model/Dlna/ContainerProfile.cs new file mode 100644 index 000000000..3fb0682b0 --- /dev/null +++ b/MediaBrowser.Model/Dlna/ContainerProfile.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Xml.Serialization; +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Extensions; + +namespace MediaBrowser.Model.Dlna +{ + public class ContainerProfile + { + [XmlAttribute("type")] + public DlnaProfileType Type { get; set; } + public ProfileCondition[] Conditions { get; set; } + + [XmlAttribute("container")] + public string Container { get; set; } + + public ContainerProfile() + { + Conditions = new ProfileCondition[] { }; + } + + public string[] GetContainers() + { + return SplitValue(Container); + } + + private static readonly string[] EmptyStringArray = Array.Empty<string>(); + + public static string[] SplitValue(string value) + { + if (string.IsNullOrEmpty(value)) + { + return EmptyStringArray; + } + + return value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + } + + public bool ContainsContainer(string container) + { + var containers = GetContainers(); + + return ContainsContainer(containers, container); + } + + public static bool ContainsContainer(string profileContainers, string inputContainer) + { + var isNegativeList = false; + if (profileContainers != null && profileContainers.StartsWith("-")) + { + isNegativeList = true; + profileContainers = profileContainers.Substring(1); + } + + return ContainsContainer(SplitValue(profileContainers), isNegativeList, inputContainer); + } + + public static bool ContainsContainer(string[] profileContainers, string inputContainer) + { + return ContainsContainer(profileContainers, false, inputContainer); + } + + public static bool ContainsContainer(string[] profileContainers, bool isNegativeList, string inputContainer) + { + if (profileContainers.Length == 0) + { + return true; + } + + if (isNegativeList) + { + var allInputContainers = SplitValue(inputContainer); + + foreach (var container in allInputContainers) + { + if (ListHelper.ContainsIgnoreCase(profileContainers, container)) + { + return false; + } + } + + return true; + } + else + { + var allInputContainers = SplitValue(inputContainer); + + foreach (var container in allInputContainers) + { + if (ListHelper.ContainsIgnoreCase(profileContainers, container)) + { + return true; + } + } + + return false; + } + } + } +} diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs new file mode 100644 index 000000000..966c4a8ce --- /dev/null +++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs @@ -0,0 +1,236 @@ +using MediaBrowser.Model.MediaInfo; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MediaBrowser.Model.Dlna +{ + public class ContentFeatureBuilder + { + private readonly DeviceProfile _profile; + + public ContentFeatureBuilder(DeviceProfile profile) + { + _profile = profile; + } + + public string BuildImageHeader(string container, + int? width, + int? height, + bool isDirectStream, + string orgPn = null) + { + string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetImageOrgOpValue(); + + // 0 = native, 1 = transcoded + var orgCi = isDirectStream ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1"; + + DlnaFlags flagValue = DlnaFlags.BackgroundTransferMode | + DlnaFlags.InteractiveTransferMode | + DlnaFlags.DlnaV15; + + string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}", + DlnaMaps.FlagsToString(flagValue)); + + ResponseProfile mediaProfile = _profile.GetImageMediaProfile(container, + width, + height); + + if (string.IsNullOrEmpty(orgPn)) + { + orgPn = mediaProfile == null ? null : mediaProfile.OrgPn; + } + + if (string.IsNullOrEmpty(orgPn)) + { + orgPn = GetImageOrgPnValue(container, width, height); + } + + string contentFeatures = string.IsNullOrEmpty(orgPn) ? string.Empty : "DLNA.ORG_PN=" + orgPn; + + return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';'); + } + + public string BuildAudioHeader(string container, + string audioCodec, + int? audioBitrate, + int? audioSampleRate, + int? audioChannels, + int? audioBitDepth, + bool isDirectStream, + long? runtimeTicks, + TranscodeSeekInfo transcodeSeekInfo) + { + // first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none + string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks > 0, isDirectStream, transcodeSeekInfo); + + // 0 = native, 1 = transcoded + string orgCi = isDirectStream ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1"; + + DlnaFlags flagValue = DlnaFlags.StreamingTransferMode | + DlnaFlags.BackgroundTransferMode | + DlnaFlags.InteractiveTransferMode | + DlnaFlags.DlnaV15; + + //if (isDirectStream) + //{ + // flagValue = flagValue | DlnaFlags.ByteBasedSeek; + //} + //else if (runtimeTicks.HasValue) + //{ + // flagValue = flagValue | DlnaFlags.TimeBasedSeek; + //} + + string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}", + DlnaMaps.FlagsToString(flagValue)); + + ResponseProfile mediaProfile = _profile.GetAudioMediaProfile(container, + audioCodec, + audioChannels, + audioBitrate, + audioSampleRate, + audioBitDepth); + + string orgPn = mediaProfile == null ? null : mediaProfile.OrgPn; + + if (string.IsNullOrEmpty(orgPn)) + { + orgPn = GetAudioOrgPnValue(container, audioBitrate, audioSampleRate, audioChannels); + } + + string contentFeatures = string.IsNullOrEmpty(orgPn) ? string.Empty : "DLNA.ORG_PN=" + orgPn; + + return (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';'); + } + + public List<string> BuildVideoHeader(string container, + string videoCodec, + string audioCodec, + int? width, + int? height, + int? bitDepth, + int? videoBitrate, + TransportStreamTimestamp timestamp, + bool isDirectStream, + long? runtimeTicks, + string videoProfile, + double? videoLevel, + float? videoFramerate, + int? packetLength, + TranscodeSeekInfo transcodeSeekInfo, + bool? isAnamorphic, + bool? isInterlaced, + int? refFrames, + int? numVideoStreams, + int? numAudioStreams, + string videoCodecTag, + bool? isAvc) + { + // first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none + string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks > 0, isDirectStream, transcodeSeekInfo); + + // 0 = native, 1 = transcoded + string orgCi = isDirectStream ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1"; + + DlnaFlags flagValue = DlnaFlags.StreamingTransferMode | + DlnaFlags.BackgroundTransferMode | + DlnaFlags.InteractiveTransferMode | + DlnaFlags.DlnaV15; + + //if (isDirectStream) + //{ + // flagValue = flagValue | DlnaFlags.ByteBasedSeek; + //} + //else if (runtimeTicks.HasValue) + //{ + // flagValue = flagValue | DlnaFlags.TimeBasedSeek; + //} + + string dlnaflags = string.Format(";DLNA.ORG_FLAGS={0}", + DlnaMaps.FlagsToString(flagValue)); + + ResponseProfile mediaProfile = _profile.GetVideoMediaProfile(container, + audioCodec, + videoCodec, + width, + height, + bitDepth, + videoBitrate, + videoProfile, + videoLevel, + videoFramerate, + packetLength, + timestamp, + isAnamorphic, + isInterlaced, + refFrames, + numVideoStreams, + numAudioStreams, + videoCodecTag, + isAvc); + + List<string> orgPnValues = new List<string>(); + + if (mediaProfile != null && !string.IsNullOrEmpty(mediaProfile.OrgPn)) + { + orgPnValues.AddRange(mediaProfile.OrgPn.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)); + } + else + { + foreach (string s in GetVideoOrgPnValue(container, videoCodec, audioCodec, width, height, timestamp)) + { + orgPnValues.Add(s); + break; + } + } + + List<string> contentFeatureList = new List<string>(); + + foreach (string orgPn in orgPnValues) + { + string contentFeatures = string.IsNullOrEmpty(orgPn) ? string.Empty : "DLNA.ORG_PN=" + orgPn; + + var value = (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';'); + + contentFeatureList.Add(value); + } + + if (orgPnValues.Count == 0) + { + string contentFeatures = string.Empty; + + var value = (contentFeatures + orgOp + orgCi + dlnaflags).Trim(';'); + + contentFeatureList.Add(value); + } + + return contentFeatureList; + } + + private string GetImageOrgPnValue(string container, int? width, int? height) + { + MediaFormatProfile? format = new MediaFormatProfileResolver() + .ResolveImageFormat(container, + width, + height); + + return format.HasValue ? format.Value.ToString() : null; + } + + private string GetAudioOrgPnValue(string container, int? audioBitrate, int? audioSampleRate, int? audioChannels) + { + MediaFormatProfile? format = new MediaFormatProfileResolver() + .ResolveAudioFormat(container, + audioBitrate, + audioSampleRate, + audioChannels); + + return format.HasValue ? format.Value.ToString() : null; + } + + private string[] GetVideoOrgPnValue(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestamp) + { + return new MediaFormatProfileResolver().ResolveVideoFormat(container, videoCodec, audioCodec, width, height, timestamp); + } + } +} diff --git a/MediaBrowser.Model/Dlna/DeviceIdentification.cs b/MediaBrowser.Model/Dlna/DeviceIdentification.cs new file mode 100644 index 000000000..97f4409da --- /dev/null +++ b/MediaBrowser.Model/Dlna/DeviceIdentification.cs @@ -0,0 +1,61 @@ +namespace MediaBrowser.Model.Dlna +{ + public class DeviceIdentification + { + /// <summary> + /// Gets or sets the name of the friendly. + /// </summary> + /// <value>The name of the friendly.</value> + public string FriendlyName { get; set; } + /// <summary> + /// Gets or sets the model number. + /// </summary> + /// <value>The model number.</value> + public string ModelNumber { get; set; } + /// <summary> + /// Gets or sets the serial number. + /// </summary> + /// <value>The serial number.</value> + public string SerialNumber { get; set; } + /// <summary> + /// Gets or sets the name of the model. + /// </summary> + /// <value>The name of the model.</value> + public string ModelName { get; set; } + /// <summary> + /// Gets or sets the model description. + /// </summary> + /// <value>The model description.</value> + public string ModelDescription { get; set; } + /// <summary> + /// Gets or sets the device description. + /// </summary> + /// <value>The device description.</value> + public string DeviceDescription { get; set; } + /// <summary> + /// Gets or sets the model URL. + /// </summary> + /// <value>The model URL.</value> + public string ModelUrl { get; set; } + /// <summary> + /// Gets or sets the manufacturer. + /// </summary> + /// <value>The manufacturer.</value> + public string Manufacturer { get; set; } + /// <summary> + /// Gets or sets the manufacturer URL. + /// </summary> + /// <value>The manufacturer URL.</value> + public string ManufacturerUrl { get; set; } + /// <summary> + /// Gets or sets the headers. + /// </summary> + /// <value>The headers.</value> + public HttpHeaderInfo[] Headers { get; set; } + + public DeviceIdentification() + { + Headers = new HttpHeaderInfo[] {}; + } + } +} diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs new file mode 100644 index 000000000..4bf7d6b8d --- /dev/null +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -0,0 +1,327 @@ +using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.MediaInfo; +using System.Collections.Generic; +using System.Xml.Serialization; +using System; + +namespace MediaBrowser.Model.Dlna +{ + [XmlRoot("Profile")] + public class DeviceProfile + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + [XmlIgnore] + public string Id { get; set; } + + /// <summary> + /// Gets or sets the identification. + /// </summary> + /// <value>The identification.</value> + public MediaBrowser.Model.Dlna.DeviceIdentification Identification { get; set; } + + public string FriendlyName { get; set; } + public string Manufacturer { get; set; } + public string ManufacturerUrl { get; set; } + public string ModelName { get; set; } + public string ModelDescription { get; set; } + public string ModelNumber { get; set; } + public string ModelUrl { get; set; } + public string SerialNumber { get; set; } + + public bool EnableAlbumArtInDidl { get; set; } + public bool EnableSingleAlbumArtLimit { get; set; } + public bool EnableSingleSubtitleLimit { get; set; } + + public string SupportedMediaTypes { get; set; } + + public string UserId { get; set; } + + public string AlbumArtPn { get; set; } + + public int MaxAlbumArtWidth { get; set; } + public int MaxAlbumArtHeight { get; set; } + + public int? MaxIconWidth { get; set; } + public int? MaxIconHeight { get; set; } + + public long? MaxStreamingBitrate { get; set; } + public long? MaxStaticBitrate { get; set; } + + public int? MusicStreamingTranscodingBitrate { get; set; } + public int? MaxStaticMusicBitrate { get; set; } + + /// <summary> + /// Controls the content of the aggregationFlags element in the urn:schemas-sonycom:av namespace. + /// </summary> + public string SonyAggregationFlags { get; set; } + + public string ProtocolInfo { get; set; } + + public int TimelineOffsetSeconds { get; set; } + public bool RequiresPlainVideoItems { get; set; } + public bool RequiresPlainFolders { get; set; } + + public bool EnableMSMediaReceiverRegistrar { get; set; } + public bool IgnoreTranscodeByteRangeRequests { get; set; } + + public XmlAttribute[] XmlRootAttributes { get; set; } + + /// <summary> + /// Gets or sets the direct play profiles. + /// </summary> + /// <value>The direct play profiles.</value> + public DirectPlayProfile[] DirectPlayProfiles { get; set; } + + /// <summary> + /// Gets or sets the transcoding profiles. + /// </summary> + /// <value>The transcoding profiles.</value> + public TranscodingProfile[] TranscodingProfiles { get; set; } + + public ContainerProfile[] ContainerProfiles { get; set; } + + public CodecProfile[] CodecProfiles { get; set; } + public ResponseProfile[] ResponseProfiles { get; set; } + + public SubtitleProfile[] SubtitleProfiles { get; set; } + + public DeviceProfile() + { + DirectPlayProfiles = new DirectPlayProfile[] { }; + TranscodingProfiles = new TranscodingProfile[] { }; + ResponseProfiles = new ResponseProfile[] { }; + CodecProfiles = new CodecProfile[] { }; + ContainerProfiles = new ContainerProfile[] { }; + SubtitleProfiles = Array.Empty<SubtitleProfile>(); + + XmlRootAttributes = new XmlAttribute[] { }; + + SupportedMediaTypes = "Audio,Photo,Video"; + MaxStreamingBitrate = 8000000; + MaxStaticBitrate = 8000000; + MusicStreamingTranscodingBitrate = 128000; + } + + public string[] GetSupportedMediaTypes() + { + return ContainerProfile.SplitValue(SupportedMediaTypes); + } + + public TranscodingProfile GetAudioTranscodingProfile(string container, string audioCodec) + { + container = (container ?? string.Empty).TrimStart('.'); + + foreach (var i in TranscodingProfiles) + { + if (i.Type != MediaBrowser.Model.Dlna.DlnaProfileType.Audio) + { + continue; + } + + if (!StringHelper.EqualsIgnoreCase(container, i.Container)) + { + continue; + } + + if (!ListHelper.ContainsIgnoreCase(i.GetAudioCodecs(), audioCodec ?? string.Empty)) + { + continue; + } + + return i; + } + return null; + } + + public TranscodingProfile GetVideoTranscodingProfile(string container, string audioCodec, string videoCodec) + { + container = (container ?? string.Empty).TrimStart('.'); + + foreach (var i in TranscodingProfiles) + { + if (i.Type != MediaBrowser.Model.Dlna.DlnaProfileType.Video) + { + continue; + } + + if (!StringHelper.EqualsIgnoreCase(container, i.Container)) + { + continue; + } + + if (!ListHelper.ContainsIgnoreCase(i.GetAudioCodecs(), audioCodec ?? string.Empty)) + { + continue; + } + + if (!StringHelper.EqualsIgnoreCase(videoCodec, i.VideoCodec ?? string.Empty)) + { + continue; + } + + return i; + } + return null; + } + + public ResponseProfile GetAudioMediaProfile(string container, string audioCodec, int? audioChannels, int? audioBitrate, int? audioSampleRate, int? audioBitDepth) + { + foreach (var i in ResponseProfiles) + { + if (i.Type != DlnaProfileType.Audio) + { + continue; + } + + if (!ContainerProfile.ContainsContainer(i.GetContainers(), container)) + { + continue; + } + + var audioCodecs = i.GetAudioCodecs(); + if (audioCodecs.Length > 0 && !ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec ?? string.Empty)) + { + continue; + } + + var conditionProcessor = new ConditionProcessor(); + + var anyOff = false; + foreach (ProfileCondition c in i.Conditions) + { + if (!conditionProcessor.IsAudioConditionSatisfied(GetModelProfileCondition(c), audioChannels, audioBitrate, audioSampleRate, audioBitDepth)) + { + anyOff = true; + break; + } + } + + if (anyOff) + { + continue; + } + + return i; + } + return null; + } + + private ProfileCondition GetModelProfileCondition(ProfileCondition c) + { + return new ProfileCondition + { + Condition = c.Condition, + IsRequired = c.IsRequired, + Property = c.Property, + Value = c.Value + }; + } + + public ResponseProfile GetImageMediaProfile(string container, int? width, int? height) + { + foreach (var i in ResponseProfiles) + { + if (i.Type != DlnaProfileType.Photo) + { + continue; + } + + if (!ContainerProfile.ContainsContainer(i.GetContainers(), container)) + { + continue; + } + + var conditionProcessor = new ConditionProcessor(); + + var anyOff = false; + foreach (ProfileCondition c in i.Conditions) + { + if (!conditionProcessor.IsImageConditionSatisfied(GetModelProfileCondition(c), width, height)) + { + anyOff = true; + break; + } + } + + if (anyOff) + { + continue; + } + + return i; + } + return null; + } + + public ResponseProfile GetVideoMediaProfile(string container, + string audioCodec, + string videoCodec, + int? width, + int? height, + int? bitDepth, + int? videoBitrate, + string videoProfile, + double? videoLevel, + float? videoFramerate, + int? packetLength, + TransportStreamTimestamp timestamp, + bool? isAnamorphic, + bool? isInterlaced, + int? refFrames, + int? numVideoStreams, + int? numAudioStreams, + string videoCodecTag, + bool? isAvc) + { + foreach (var i in ResponseProfiles) + { + if (i.Type != DlnaProfileType.Video) + { + continue; + } + + if (!ContainerProfile.ContainsContainer(i.GetContainers(), container)) + { + continue; + } + + var audioCodecs = i.GetAudioCodecs(); + if (audioCodecs.Length > 0 && !ListHelper.ContainsIgnoreCase(audioCodecs, audioCodec ?? string.Empty)) + { + continue; + } + + var videoCodecs = i.GetVideoCodecs(); + if (videoCodecs.Length > 0 && !ListHelper.ContainsIgnoreCase(videoCodecs, videoCodec ?? string.Empty)) + { + continue; + } + + var conditionProcessor = new ConditionProcessor(); + + var anyOff = false; + foreach (ProfileCondition c in i.Conditions) + { + if (!conditionProcessor.IsVideoConditionSatisfied(GetModelProfileCondition(c), width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)) + { + anyOff = true; + break; + } + } + + if (anyOff) + { + continue; + } + + return i; + } + return null; + } + } +} diff --git a/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs b/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs new file mode 100644 index 000000000..b2afdf292 --- /dev/null +++ b/MediaBrowser.Model/Dlna/DeviceProfileInfo.cs @@ -0,0 +1,24 @@ + +namespace MediaBrowser.Model.Dlna +{ + public class DeviceProfileInfo + { + /// <summary> + /// Gets or sets the identifier. + /// </summary> + /// <value>The identifier.</value> + public string Id { get; set; } + + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the type. + /// </summary> + /// <value>The type.</value> + public DeviceProfileType Type { get; set; } + } +} diff --git a/MediaBrowser.Model/Dlna/DeviceProfileType.cs b/MediaBrowser.Model/Dlna/DeviceProfileType.cs new file mode 100644 index 000000000..f881a4539 --- /dev/null +++ b/MediaBrowser.Model/Dlna/DeviceProfileType.cs @@ -0,0 +1,8 @@ +namespace MediaBrowser.Model.Dlna +{ + public enum DeviceProfileType + { + System = 0, + User = 1 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/DirectPlayProfile.cs b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs new file mode 100644 index 000000000..4279b545d --- /dev/null +++ b/MediaBrowser.Model/Dlna/DirectPlayProfile.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Xml.Serialization; + +namespace MediaBrowser.Model.Dlna +{ + public class DirectPlayProfile + { + [XmlAttribute("container")] + public string Container { get; set; } + + [XmlAttribute("audioCodec")] + public string AudioCodec { get; set; } + + [XmlAttribute("videoCodec")] + public string VideoCodec { get; set; } + + [XmlAttribute("type")] + public DlnaProfileType Type { get; set; } + + public bool SupportsContainer(string container) + { + return ContainerProfile.ContainsContainer(Container, container); + } + + public bool SupportsVideoCodec(string codec) + { + return ContainerProfile.ContainsContainer(VideoCodec, codec); + } + + public bool SupportsAudioCodec(string codec) + { + return ContainerProfile.ContainsContainer(AudioCodec, codec); + } + } +} diff --git a/MediaBrowser.Model/Dlna/DlnaFlags.cs b/MediaBrowser.Model/Dlna/DlnaFlags.cs new file mode 100644 index 000000000..b981e8455 --- /dev/null +++ b/MediaBrowser.Model/Dlna/DlnaFlags.cs @@ -0,0 +1,48 @@ +using System; + +namespace MediaBrowser.Model.Dlna +{ + [Flags] + public enum DlnaFlags : ulong + { + /*! <i>Background</i> transfer mode. + For use with upload and download transfers to and from the server. + The primary difference between \ref DH_TransferMode_Interactive and + \ref DH_TransferMode_Bulk is that the latter assumes that the user + is not relying on the transfer for immediately rendering the content + and there are no issues with causing a buffer overflow if the + receiver uses TCP flow control to reduce total throughput. + */ + BackgroundTransferMode = 1 << 22, + + ByteBasedSeek = 1 << 29, + ConnectionStall = 1 << 21, + + DlnaV15 = 1 << 20, + + /*! <i>Interactive</i> transfer mode. + For best effort transfer of images and non-real-time transfers. + URIs with image content usually support \ref DH_TransferMode_Bulk too. + The primary difference between \ref DH_TransferMode_Interactive and + \ref DH_TransferMode_Bulk is that the former assumes that the + transfer is intended for immediate rendering. + */ + InteractiveTransferMode = 1 << 23, + + PlayContainer = 1 << 28, + RtspPause = 1 << 25, + S0Increase = 1 << 27, + SenderPaced = 1L << 31, + SnIncrease = 1 << 26, + + /*! <i>Streaming</i> transfer mode. + The server transmits at a throughput sufficient for real-time playback of + audio or video. URIs with audio or video often support the + \ref DH_TransferMode_Interactive and \ref DH_TransferMode_Bulk transfer modes. + The most well-known exception to this general claim is for live streams. + */ + StreamingTransferMode = 1 << 24, + + TimeBasedSeek = 1 << 30 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/DlnaMaps.cs b/MediaBrowser.Model/Dlna/DlnaMaps.cs new file mode 100644 index 000000000..8dadc32d6 --- /dev/null +++ b/MediaBrowser.Model/Dlna/DlnaMaps.cs @@ -0,0 +1,56 @@ +namespace MediaBrowser.Model.Dlna +{ + public class DlnaMaps + { + private static readonly string DefaultStreaming = + FlagsToString(DlnaFlags.StreamingTransferMode | + DlnaFlags.BackgroundTransferMode | + DlnaFlags.ConnectionStall | + DlnaFlags.ByteBasedSeek | + DlnaFlags.DlnaV15); + + private static readonly string DefaultInteractive = + FlagsToString(DlnaFlags.InteractiveTransferMode | + DlnaFlags.BackgroundTransferMode | + DlnaFlags.ConnectionStall | + DlnaFlags.ByteBasedSeek | + DlnaFlags.DlnaV15); + + public static string FlagsToString(DlnaFlags flags) + { + return string.Format("{0:X8}{1:D24}", (ulong)flags, 0); + } + + public static string GetOrgOpValue(bool hasKnownRuntime, bool isDirectStream, TranscodeSeekInfo profileTranscodeSeekInfo) + { + if (hasKnownRuntime) + { + string orgOp = string.Empty; + + // Time-based seeking currently only possible when transcoding + orgOp += isDirectStream ? "0" : "1"; + + // Byte-based seeking only possible when not transcoding + orgOp += isDirectStream || profileTranscodeSeekInfo == TranscodeSeekInfo.Bytes ? "1" : "0"; + + return orgOp; + } + + // No seeking is available if we don't know the content runtime + return "00"; + } + + public static string GetImageOrgOpValue() + { + string orgOp = string.Empty; + + // Time-based seeking currently only possible when transcoding + orgOp += "0"; + + // Byte-based seeking only possible when not transcoding + orgOp += "0"; + + return orgOp; + } + } +} diff --git a/MediaBrowser.Model/Dlna/DlnaProfileType.cs b/MediaBrowser.Model/Dlna/DlnaProfileType.cs new file mode 100644 index 000000000..1bad14081 --- /dev/null +++ b/MediaBrowser.Model/Dlna/DlnaProfileType.cs @@ -0,0 +1,9 @@ +namespace MediaBrowser.Model.Dlna +{ + public enum DlnaProfileType + { + Audio = 0, + Video = 1, + Photo = 2 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/EncodingContext.cs b/MediaBrowser.Model/Dlna/EncodingContext.cs new file mode 100644 index 000000000..f83d8ddc8 --- /dev/null +++ b/MediaBrowser.Model/Dlna/EncodingContext.cs @@ -0,0 +1,8 @@ +namespace MediaBrowser.Model.Dlna +{ + public enum EncodingContext + { + Streaming = 0, + Static = 1 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/HeaderMatchType.cs b/MediaBrowser.Model/Dlna/HeaderMatchType.cs new file mode 100644 index 000000000..7a0d5c24f --- /dev/null +++ b/MediaBrowser.Model/Dlna/HeaderMatchType.cs @@ -0,0 +1,9 @@ +namespace MediaBrowser.Model.Dlna +{ + public enum HeaderMatchType + { + Equals = 0, + Regex = 1, + Substring = 2 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs b/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs new file mode 100644 index 000000000..b4fa4e0af --- /dev/null +++ b/MediaBrowser.Model/Dlna/HttpHeaderInfo.cs @@ -0,0 +1,17 @@ +using System.Xml.Serialization; +using MediaBrowser.Model.Dlna; + +namespace MediaBrowser.Model.Dlna +{ + public class HttpHeaderInfo + { + [XmlAttribute("name")] + public string Name { get; set; } + + [XmlAttribute("value")] + public string Value { get; set; } + + [XmlAttribute("match")] + public HeaderMatchType Match { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs b/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs new file mode 100644 index 000000000..70191ff23 --- /dev/null +++ b/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs @@ -0,0 +1,11 @@ +using System; +using MediaBrowser.Model.Events; + +namespace MediaBrowser.Model.Dlna +{ + public interface IDeviceDiscovery + { + event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered; + event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceLeft; + } +} diff --git a/MediaBrowser.Model/Dlna/ITranscoderSupport.cs b/MediaBrowser.Model/Dlna/ITranscoderSupport.cs new file mode 100644 index 000000000..14723bd27 --- /dev/null +++ b/MediaBrowser.Model/Dlna/ITranscoderSupport.cs @@ -0,0 +1,25 @@ +namespace MediaBrowser.Model.Dlna +{ + public interface ITranscoderSupport + { + bool CanEncodeToAudioCodec(string codec); + bool CanEncodeToSubtitleCodec(string codec); + bool CanExtractSubtitles(string codec); + } + + public class FullTranscoderSupport : ITranscoderSupport + { + public bool CanEncodeToAudioCodec(string codec) + { + return true; + } + public bool CanEncodeToSubtitleCodec(string codec) + { + return true; + } + public bool CanExtractSubtitles(string codec) + { + return true; + } + } +} diff --git a/MediaBrowser.Model/Dlna/MediaFormatProfile.cs b/MediaBrowser.Model/Dlna/MediaFormatProfile.cs new file mode 100644 index 000000000..f3d04335f --- /dev/null +++ b/MediaBrowser.Model/Dlna/MediaFormatProfile.cs @@ -0,0 +1,113 @@ + +namespace MediaBrowser.Model.Dlna +{ + public enum MediaFormatProfile + { + MP3, + WMA_BASE, + WMA_FULL, + LPCM16_44_MONO, + LPCM16_44_STEREO, + LPCM16_48_MONO, + LPCM16_48_STEREO, + AAC_ISO, + AAC_ISO_320, + AAC_ADTS, + AAC_ADTS_320, + FLAC, + OGG, + + JPEG_SM, + JPEG_MED, + JPEG_LRG, + JPEG_TN, + PNG_LRG, + PNG_TN, + GIF_LRG, + RAW, + + MPEG1, + MPEG_PS_PAL, + MPEG_PS_NTSC, + MPEG_TS_SD_EU, + MPEG_TS_SD_EU_ISO, + MPEG_TS_SD_EU_T, + MPEG_TS_SD_NA, + MPEG_TS_SD_NA_ISO, + MPEG_TS_SD_NA_T, + MPEG_TS_SD_KO, + MPEG_TS_SD_KO_ISO, + MPEG_TS_SD_KO_T, + MPEG_TS_JP_T, + AVI, + MATROSKA, + FLV, + DVR_MS, + WTV, + OGV, + AVC_MP4_MP_SD_AAC_MULT5, + AVC_MP4_MP_SD_MPEG1_L3, + AVC_MP4_MP_SD_AC3, + AVC_MP4_MP_HD_720p_AAC, + AVC_MP4_MP_HD_1080i_AAC, + AVC_MP4_HP_HD_AAC, + AVC_TS_MP_HD_AAC_MULT5, + AVC_TS_MP_HD_AAC_MULT5_T, + AVC_TS_MP_HD_AAC_MULT5_ISO, + AVC_TS_MP_HD_MPEG1_L3, + AVC_TS_MP_HD_MPEG1_L3_T, + AVC_TS_MP_HD_MPEG1_L3_ISO, + AVC_TS_MP_HD_AC3, + AVC_TS_MP_HD_AC3_T, + AVC_TS_MP_HD_AC3_ISO, + AVC_TS_HP_HD_MPEG1_L2_T, + AVC_TS_HP_HD_MPEG1_L2_ISO, + AVC_TS_MP_SD_AAC_MULT5, + AVC_TS_MP_SD_AAC_MULT5_T, + AVC_TS_MP_SD_AAC_MULT5_ISO, + AVC_TS_MP_SD_MPEG1_L3, + AVC_TS_MP_SD_MPEG1_L3_T, + AVC_TS_MP_SD_MPEG1_L3_ISO, + AVC_TS_HP_SD_MPEG1_L2_T, + AVC_TS_HP_SD_MPEG1_L2_ISO, + AVC_TS_MP_SD_AC3, + AVC_TS_MP_SD_AC3_T, + AVC_TS_MP_SD_AC3_ISO, + AVC_TS_HD_DTS_T, + AVC_TS_HD_DTS_ISO, + WMVMED_BASE, + WMVMED_FULL, + WMVMED_PRO, + WMVHIGH_FULL, + WMVHIGH_PRO, + VC1_ASF_AP_L1_WMA, + VC1_ASF_AP_L2_WMA, + VC1_ASF_AP_L3_WMA, + VC1_TS_AP_L1_AC3_ISO, + VC1_TS_AP_L2_AC3_ISO, + VC1_TS_HD_DTS_ISO, + VC1_TS_HD_DTS_T, + MPEG4_P2_MP4_ASP_AAC, + MPEG4_P2_MP4_SP_L6_AAC, + MPEG4_P2_MP4_NDSD, + MPEG4_P2_TS_ASP_AAC, + MPEG4_P2_TS_ASP_AAC_T, + MPEG4_P2_TS_ASP_AAC_ISO, + MPEG4_P2_TS_ASP_MPEG1_L3, + MPEG4_P2_TS_ASP_MPEG1_L3_T, + MPEG4_P2_TS_ASP_MPEG1_L3_ISO, + MPEG4_P2_TS_ASP_MPEG2_L2, + MPEG4_P2_TS_ASP_MPEG2_L2_T, + MPEG4_P2_TS_ASP_MPEG2_L2_ISO, + MPEG4_P2_TS_ASP_AC3, + MPEG4_P2_TS_ASP_AC3_T, + MPEG4_P2_TS_ASP_AC3_ISO, + AVC_TS_HD_50_LPCM_T, + AVC_MP4_LPCM, + MPEG4_P2_3GPP_SP_L0B_AAC, + MPEG4_P2_3GPP_SP_L0B_AMR, + AVC_3GPP_BL_QCIF15_AAC, + MPEG4_H263_3GPP_P0_L10_AMR, + MPEG4_H263_MP4_P0_L10_AAC + } +} diff --git a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs new file mode 100644 index 000000000..b6f329387 --- /dev/null +++ b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs @@ -0,0 +1,439 @@ +using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.MediaInfo; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MediaBrowser.Model.Dlna +{ + public class MediaFormatProfileResolver + { + public string[] ResolveVideoFormat(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType) + { + return ResolveVideoFormatInternal(container, videoCodec, audioCodec, width, height, timestampType) + .Select(i => i.ToString()) + .ToArray(); + } + + private MediaFormatProfile[] ResolveVideoFormatInternal(string container, string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType) + { + if (StringHelper.EqualsIgnoreCase(container, "asf")) + { + MediaFormatProfile? val = ResolveVideoASFFormat(videoCodec, audioCodec, width, height); + return val.HasValue ? new MediaFormatProfile[] { val.Value } : new MediaFormatProfile[] { }; + } + + if (StringHelper.EqualsIgnoreCase(container, "mp4")) + { + MediaFormatProfile? val = ResolveVideoMP4Format(videoCodec, audioCodec, width, height); + return val.HasValue ? new MediaFormatProfile[] { val.Value } : new MediaFormatProfile[] { }; + } + + if (StringHelper.EqualsIgnoreCase(container, "avi")) + return new MediaFormatProfile[] { MediaFormatProfile.AVI }; + + if (StringHelper.EqualsIgnoreCase(container, "mkv")) + return new MediaFormatProfile[] { MediaFormatProfile.MATROSKA }; + + if (StringHelper.EqualsIgnoreCase(container, "mpeg2ps") || + StringHelper.EqualsIgnoreCase(container, "ts")) + + return new MediaFormatProfile[] { MediaFormatProfile.MPEG_PS_NTSC, MediaFormatProfile.MPEG_PS_PAL }; + + if (StringHelper.EqualsIgnoreCase(container, "mpeg1video")) + return new MediaFormatProfile[] { MediaFormatProfile.MPEG1 }; + + if (StringHelper.EqualsIgnoreCase(container, "mpeg2ts") || + StringHelper.EqualsIgnoreCase(container, "mpegts") || + StringHelper.EqualsIgnoreCase(container, "m2ts")) + { + + return ResolveVideoMPEG2TSFormat(videoCodec, audioCodec, width, height, timestampType); + } + + if (StringHelper.EqualsIgnoreCase(container, "flv")) + return new MediaFormatProfile[] { MediaFormatProfile.FLV }; + + if (StringHelper.EqualsIgnoreCase(container, "wtv")) + return new MediaFormatProfile[] { MediaFormatProfile.WTV }; + + if (StringHelper.EqualsIgnoreCase(container, "3gp")) + { + MediaFormatProfile? val = ResolveVideo3GPFormat(videoCodec, audioCodec); + return val.HasValue ? new MediaFormatProfile[] { val.Value } : new MediaFormatProfile[] { }; + } + + if (StringHelper.EqualsIgnoreCase(container, "ogv") || StringHelper.EqualsIgnoreCase(container, "ogg")) + return new MediaFormatProfile[] { MediaFormatProfile.OGV }; + + return new MediaFormatProfile[] { }; + } + + private MediaFormatProfile[] ResolveVideoMPEG2TSFormat(string videoCodec, string audioCodec, int? width, int? height, TransportStreamTimestamp timestampType) + { + string suffix = ""; + + switch (timestampType) + { + case TransportStreamTimestamp.None: + suffix = "_ISO"; + break; + case TransportStreamTimestamp.Valid: + suffix = "_T"; + break; + } + + string resolution = "S"; + if ((width.HasValue && width.Value > 720) || (height.HasValue && height.Value > 576)) + { + resolution = "H"; + } + + if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg2video")) + { + List<MediaFormatProfile> list = new List<MediaFormatProfile>(); + + list.Add(ValueOf("MPEG_TS_SD_NA" + suffix)); + list.Add(ValueOf("MPEG_TS_SD_EU" + suffix)); + list.Add(ValueOf("MPEG_TS_SD_KO" + suffix)); + + if ((timestampType == TransportStreamTimestamp.Valid) && StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + { + list.Add(MediaFormatProfile.MPEG_TS_JP_T); + } + return list.ToArray(list.Count); + } + if (StringHelper.EqualsIgnoreCase(videoCodec, "h264")) + { + if (StringHelper.EqualsIgnoreCase(audioCodec, "lpcm")) + return new MediaFormatProfile[] { MediaFormatProfile.AVC_TS_HD_50_LPCM_T }; + + if (StringHelper.EqualsIgnoreCase(audioCodec, "dts")) + { + if (timestampType == TransportStreamTimestamp.None) + { + return new MediaFormatProfile[] { MediaFormatProfile.AVC_TS_HD_DTS_ISO }; + } + return new MediaFormatProfile[] { MediaFormatProfile.AVC_TS_HD_DTS_T }; + } + + if (StringHelper.EqualsIgnoreCase(audioCodec, "mp2")) + { + if (timestampType == TransportStreamTimestamp.None) + { + return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_HP_{0}D_MPEG1_L2_ISO", resolution)) }; + } + + return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_HP_{0}D_MPEG1_L2_T", resolution)) }; + } + + if (StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_AAC_MULT5{1}", resolution, suffix)) }; + + if (StringHelper.EqualsIgnoreCase(audioCodec, "mp3")) + return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_MPEG1_L3{1}", resolution, suffix)) }; + + if (string.IsNullOrEmpty(audioCodec) || + StringHelper.EqualsIgnoreCase(audioCodec, "ac3")) + return new MediaFormatProfile[] { ValueOf(string.Format("AVC_TS_MP_{0}D_AC3{1}", resolution, suffix)) }; + } + else if (StringHelper.EqualsIgnoreCase(videoCodec, "vc1")) + { + if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "ac3")) + { + if ((width.HasValue && width.Value > 720) || (height.HasValue && height.Value > 576)) + { + return new MediaFormatProfile[] { MediaFormatProfile.VC1_TS_AP_L2_AC3_ISO }; + } + return new MediaFormatProfile[] { MediaFormatProfile.VC1_TS_AP_L1_AC3_ISO }; + } + if (StringHelper.EqualsIgnoreCase(audioCodec, "dts")) + { + suffix = StringHelper.EqualsIgnoreCase(suffix, "_ISO") ? suffix : "_T"; + + return new MediaFormatProfile[] { ValueOf(string.Format("VC1_TS_HD_DTS{0}", suffix)) }; + } + + } + else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg4") || StringHelper.EqualsIgnoreCase(videoCodec, "msmpeg4")) + { + if (StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_AAC{0}", suffix)) }; + if (StringHelper.EqualsIgnoreCase(audioCodec, "mp3")) + return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_MPEG1_L3{0}", suffix)) }; + if (StringHelper.EqualsIgnoreCase(audioCodec, "mp2")) + return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_MPEG2_L2{0}", suffix)) }; + if (StringHelper.EqualsIgnoreCase(audioCodec, "ac3")) + return new MediaFormatProfile[] { ValueOf(string.Format("MPEG4_P2_TS_ASP_AC3{0}", suffix)) }; + } + + return new MediaFormatProfile[]{}; + } + + private MediaFormatProfile ValueOf(string value) + { + return (MediaFormatProfile)Enum.Parse(typeof(MediaFormatProfile), value, true); + } + + private MediaFormatProfile? ResolveVideoMP4Format(string videoCodec, string audioCodec, int? width, int? height) + { + if (StringHelper.EqualsIgnoreCase(videoCodec, "h264")) + { + if (StringHelper.EqualsIgnoreCase(audioCodec, "lpcm")) + return MediaFormatProfile.AVC_MP4_LPCM; + if (string.IsNullOrEmpty(audioCodec) || + StringHelper.EqualsIgnoreCase(audioCodec, "ac3")) + { + return MediaFormatProfile.AVC_MP4_MP_SD_AC3; + } + if (StringHelper.EqualsIgnoreCase(audioCodec, "mp3")) + { + return MediaFormatProfile.AVC_MP4_MP_SD_MPEG1_L3; + } + if (width.HasValue && height.HasValue) + { + if ((width.Value <= 720) && (height.Value <= 576)) + { + if (StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + return MediaFormatProfile.AVC_MP4_MP_SD_AAC_MULT5; + } + else if ((width.Value <= 1280) && (height.Value <= 720)) + { + if (StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + return MediaFormatProfile.AVC_MP4_MP_HD_720p_AAC; + } + else if ((width.Value <= 1920) && (height.Value <= 1080)) + { + if (StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + { + return MediaFormatProfile.AVC_MP4_MP_HD_1080i_AAC; + } + } + } + } + else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg4") || + StringHelper.EqualsIgnoreCase(videoCodec, "msmpeg4")) + { + if (width.HasValue && height.HasValue && width.Value <= 720 && height.Value <= 576) + { + if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + return MediaFormatProfile.MPEG4_P2_MP4_ASP_AAC; + if (StringHelper.EqualsIgnoreCase(audioCodec, "ac3") || StringHelper.EqualsIgnoreCase(audioCodec, "mp3")) + { + return MediaFormatProfile.MPEG4_P2_MP4_NDSD; + } + } + else if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + { + return MediaFormatProfile.MPEG4_P2_MP4_SP_L6_AAC; + } + } + else if (StringHelper.EqualsIgnoreCase(videoCodec, "h263") && StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + { + return MediaFormatProfile.MPEG4_H263_MP4_P0_L10_AAC; + } + + return null; + } + + private MediaFormatProfile? ResolveVideo3GPFormat(string videoCodec, string audioCodec) + { + if (StringHelper.EqualsIgnoreCase(videoCodec, "h264")) + { + if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "aac")) + return MediaFormatProfile.AVC_3GPP_BL_QCIF15_AAC; + } + else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg4") || + StringHelper.EqualsIgnoreCase(videoCodec, "msmpeg4")) + { + if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma")) + return MediaFormatProfile.MPEG4_P2_3GPP_SP_L0B_AAC; + if (StringHelper.EqualsIgnoreCase(audioCodec, "amrnb")) + return MediaFormatProfile.MPEG4_P2_3GPP_SP_L0B_AMR; + } + else if (StringHelper.EqualsIgnoreCase(videoCodec, "h263") && StringHelper.EqualsIgnoreCase(audioCodec, "amrnb")) + { + return MediaFormatProfile.MPEG4_H263_3GPP_P0_L10_AMR; + } + + return null; + } + + private MediaFormatProfile? ResolveVideoASFFormat(string videoCodec, string audioCodec, int? width, int? height) + { + if (StringHelper.EqualsIgnoreCase(videoCodec, "wmv") && + (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma") || StringHelper.EqualsIgnoreCase(videoCodec, "wmapro"))) + { + + if (width.HasValue && height.HasValue) + { + if ((width.Value <= 720) && (height.Value <= 576)) + { + if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma")) + { + return MediaFormatProfile.WMVMED_FULL; + } + return MediaFormatProfile.WMVMED_PRO; + } + } + + if (string.IsNullOrEmpty(audioCodec) || StringHelper.EqualsIgnoreCase(audioCodec, "wma")) + { + return MediaFormatProfile.WMVHIGH_FULL; + } + return MediaFormatProfile.WMVHIGH_PRO; + } + + if (StringHelper.EqualsIgnoreCase(videoCodec, "vc1")) + { + if (width.HasValue && height.HasValue) + { + if ((width.Value <= 720) && (height.Value <= 576)) + return MediaFormatProfile.VC1_ASF_AP_L1_WMA; + if ((width.Value <= 1280) && (height.Value <= 720)) + return MediaFormatProfile.VC1_ASF_AP_L2_WMA; + if ((width.Value <= 1920) && (height.Value <= 1080)) + return MediaFormatProfile.VC1_ASF_AP_L3_WMA; + } + } + else if (StringHelper.EqualsIgnoreCase(videoCodec, "mpeg2video")) + { + return MediaFormatProfile.DVR_MS; + } + + return null; + } + + public MediaFormatProfile? ResolveAudioFormat(string container, int? bitrate, int? frequency, int? channels) + { + if (StringHelper.EqualsIgnoreCase(container, "asf")) + return ResolveAudioASFFormat(bitrate); + + if (StringHelper.EqualsIgnoreCase(container, "mp3")) + return MediaFormatProfile.MP3; + + if (StringHelper.EqualsIgnoreCase(container, "lpcm")) + return ResolveAudioLPCMFormat(frequency, channels); + + if (StringHelper.EqualsIgnoreCase(container, "mp4") || + StringHelper.EqualsIgnoreCase(container, "aac")) + return ResolveAudioMP4Format(bitrate); + + if (StringHelper.EqualsIgnoreCase(container, "adts")) + return ResolveAudioADTSFormat(bitrate); + + if (StringHelper.EqualsIgnoreCase(container, "flac")) + return MediaFormatProfile.FLAC; + + if (StringHelper.EqualsIgnoreCase(container, "oga") || + StringHelper.EqualsIgnoreCase(container, "ogg")) + return MediaFormatProfile.OGG; + + return null; + } + + private MediaFormatProfile ResolveAudioASFFormat(int? bitrate) + { + if (bitrate.HasValue && bitrate.Value <= 193) + { + return MediaFormatProfile.WMA_BASE; + } + return MediaFormatProfile.WMA_FULL; + } + + private MediaFormatProfile? ResolveAudioLPCMFormat(int? frequency, int? channels) + { + if (frequency.HasValue && channels.HasValue) + { + if (frequency.Value == 44100 && channels.Value == 1) + { + return MediaFormatProfile.LPCM16_44_MONO; + } + if (frequency.Value == 44100 && channels.Value == 2) + { + return MediaFormatProfile.LPCM16_44_STEREO; + } + if (frequency.Value == 48000 && channels.Value == 1) + { + return MediaFormatProfile.LPCM16_48_MONO; + } + if (frequency.Value == 48000 && channels.Value == 2) + { + return MediaFormatProfile.LPCM16_48_STEREO; + } + + return null; + } + + return MediaFormatProfile.LPCM16_48_STEREO; + } + + private MediaFormatProfile ResolveAudioMP4Format(int? bitrate) + { + if (bitrate.HasValue && bitrate.Value <= 320) + { + return MediaFormatProfile.AAC_ISO_320; + } + return MediaFormatProfile.AAC_ISO; + } + + private MediaFormatProfile ResolveAudioADTSFormat(int? bitrate) + { + if (bitrate.HasValue && bitrate.Value <= 320) + { + return MediaFormatProfile.AAC_ADTS_320; + } + return MediaFormatProfile.AAC_ADTS; + } + + public MediaFormatProfile? ResolveImageFormat(string container, int? width, int? height) + { + if (StringHelper.EqualsIgnoreCase(container, "jpeg") || + StringHelper.EqualsIgnoreCase(container, "jpg")) + return ResolveImageJPGFormat(width, height); + + if (StringHelper.EqualsIgnoreCase(container, "png")) + return ResolveImagePNGFormat(width, height); + + if (StringHelper.EqualsIgnoreCase(container, "gif")) + return MediaFormatProfile.GIF_LRG; + + if (StringHelper.EqualsIgnoreCase(container, "raw")) + return MediaFormatProfile.RAW; + + return null; + } + + private MediaFormatProfile ResolveImageJPGFormat(int? width, int? height) + { + if (width.HasValue && height.HasValue) + { + if ((width.Value <= 160) && (height.Value <= 160)) + return MediaFormatProfile.JPEG_TN; + + if ((width.Value <= 640) && (height.Value <= 480)) + return MediaFormatProfile.JPEG_SM; + + if ((width.Value <= 1024) && (height.Value <= 768)) + { + return MediaFormatProfile.JPEG_MED; + } + + return MediaFormatProfile.JPEG_LRG; + } + + return MediaFormatProfile.JPEG_SM; + } + + private MediaFormatProfile ResolveImagePNGFormat(int? width, int? height) + { + if (width.HasValue && height.HasValue) + { + if ((width.Value <= 160) && (height.Value <= 160)) + return MediaFormatProfile.PNG_TN; + } + + return MediaFormatProfile.PNG_LRG; + } + } +} diff --git a/MediaBrowser.Model/Dlna/PlaybackErrorCode.cs b/MediaBrowser.Model/Dlna/PlaybackErrorCode.cs new file mode 100644 index 000000000..4ed412985 --- /dev/null +++ b/MediaBrowser.Model/Dlna/PlaybackErrorCode.cs @@ -0,0 +1,10 @@ + +namespace MediaBrowser.Model.Dlna +{ + public enum PlaybackErrorCode + { + NotAllowed = 0, + NoCompatibleStream = 1, + RateLimitExceeded = 2 + } +} diff --git a/MediaBrowser.Model/Dlna/ProfileCondition.cs b/MediaBrowser.Model/Dlna/ProfileCondition.cs new file mode 100644 index 000000000..9234a2713 --- /dev/null +++ b/MediaBrowser.Model/Dlna/ProfileCondition.cs @@ -0,0 +1,38 @@ +using System.Xml.Serialization; + +namespace MediaBrowser.Model.Dlna +{ + public class ProfileCondition + { + [XmlAttribute("condition")] + public ProfileConditionType Condition { get; set; } + + [XmlAttribute("property")] + public ProfileConditionValue Property { get; set; } + + [XmlAttribute("value")] + public string Value { get; set; } + + [XmlAttribute("isRequired")] + public bool IsRequired { get; set; } + + public ProfileCondition() + { + IsRequired = true; + } + + public ProfileCondition(ProfileConditionType condition, ProfileConditionValue property, string value) + : this(condition, property, value, false) + { + + } + + public ProfileCondition(ProfileConditionType condition, ProfileConditionValue property, string value, bool isRequired) + { + Condition = condition; + Property = property; + Value = value; + IsRequired = isRequired; + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/ProfileConditionType.cs b/MediaBrowser.Model/Dlna/ProfileConditionType.cs new file mode 100644 index 000000000..b0a94c5b3 --- /dev/null +++ b/MediaBrowser.Model/Dlna/ProfileConditionType.cs @@ -0,0 +1,11 @@ +namespace MediaBrowser.Model.Dlna +{ + public enum ProfileConditionType + { + Equals = 0, + NotEquals = 1, + LessThanEqual = 2, + GreaterThanEqual = 3, + EqualsAny = 4 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs new file mode 100644 index 000000000..a96e9ac36 --- /dev/null +++ b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs @@ -0,0 +1,29 @@ +namespace MediaBrowser.Model.Dlna +{ + public enum ProfileConditionValue + { + AudioChannels = 0, + AudioBitrate = 1, + AudioProfile = 2, + Width = 3, + Height = 4, + Has64BitOffsets = 5, + PacketLength = 6, + VideoBitDepth = 7, + VideoBitrate = 8, + VideoFramerate = 9, + VideoLevel = 10, + VideoProfile = 11, + VideoTimestamp = 12, + IsAnamorphic = 13, + RefFrames = 14, + NumAudioStreams = 16, + NumVideoStreams = 17, + IsSecondaryAudio = 18, + VideoCodecTag = 19, + IsAvc = 20, + IsInterlaced = 21, + AudioSampleRate = 22, + AudioBitDepth = 23 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/ResolutionConfiguration.cs b/MediaBrowser.Model/Dlna/ResolutionConfiguration.cs new file mode 100644 index 000000000..8efdb0660 --- /dev/null +++ b/MediaBrowser.Model/Dlna/ResolutionConfiguration.cs @@ -0,0 +1,14 @@ +namespace MediaBrowser.Model.Dlna +{ + public class ResolutionConfiguration + { + public int MaxWidth { get; set; } + public int MaxBitrate { get; set; } + + public ResolutionConfiguration(int maxWidth, int maxBitrate) + { + MaxWidth = maxWidth; + MaxBitrate = maxBitrate; + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs new file mode 100644 index 000000000..4fdf4972f --- /dev/null +++ b/MediaBrowser.Model/Dlna/ResolutionNormalizer.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using MediaBrowser.Model.Extensions; + +namespace MediaBrowser.Model.Dlna +{ + public class ResolutionNormalizer + { + private static readonly ResolutionConfiguration[] Configurations = + new [] + { + new ResolutionConfiguration(426, 320000), + new ResolutionConfiguration(640, 400000), + new ResolutionConfiguration(720, 950000), + new ResolutionConfiguration(1280, 2500000), + new ResolutionConfiguration(1920, 4000000), + new ResolutionConfiguration(3840, 35000000) + }; + + public static ResolutionOptions Normalize(int? inputBitrate, + int? unused1, + int? unused2, + int outputBitrate, + string inputCodec, + string outputCodec, + int? maxWidth, + int? maxHeight) + { + // If the bitrate isn't changing, then don't downlscale the resolution + if (inputBitrate.HasValue && outputBitrate >= inputBitrate.Value) + { + if (maxWidth.HasValue || maxHeight.HasValue) + { + return new ResolutionOptions + { + MaxWidth = maxWidth, + MaxHeight = maxHeight + }; + } + } + + var resolutionConfig = GetResolutionConfiguration(outputBitrate); + if (resolutionConfig != null) + { + var originvalValue = maxWidth; + + maxWidth = Math.Min(resolutionConfig.MaxWidth, maxWidth ?? resolutionConfig.MaxWidth); + if (!originvalValue.HasValue || originvalValue.Value != maxWidth.Value) + { + maxHeight = null; + } + } + + return new ResolutionOptions + { + MaxWidth = maxWidth, + MaxHeight = maxHeight + }; + } + + private static ResolutionConfiguration GetResolutionConfiguration(int outputBitrate) + { + ResolutionConfiguration previousOption = null; + + foreach (var config in Configurations) + { + if (outputBitrate <= config.MaxBitrate) + { + return previousOption ?? config; + } + + previousOption = config; + } + + return null; + } + + private static double GetVideoBitrateScaleFactor(string codec) + { + if (StringHelper.EqualsIgnoreCase(codec, "h265") || + StringHelper.EqualsIgnoreCase(codec, "hevc") || + StringHelper.EqualsIgnoreCase(codec, "vp9")) + { + return .5; + } + return 1; + } + + public static int ScaleBitrate(int bitrate, string inputVideoCodec, string outputVideoCodec) + { + var inputScaleFactor = GetVideoBitrateScaleFactor(inputVideoCodec); + var outputScaleFactor = GetVideoBitrateScaleFactor(outputVideoCodec); + var scaleFactor = outputScaleFactor/inputScaleFactor; + var newBitrate = scaleFactor*bitrate; + + return Convert.ToInt32(newBitrate); + } + } +} diff --git a/MediaBrowser.Model/Dlna/ResolutionOptions.cs b/MediaBrowser.Model/Dlna/ResolutionOptions.cs new file mode 100644 index 000000000..6b711cfa0 --- /dev/null +++ b/MediaBrowser.Model/Dlna/ResolutionOptions.cs @@ -0,0 +1,8 @@ +namespace MediaBrowser.Model.Dlna +{ + public class ResolutionOptions + { + public int? MaxWidth { get; set; } + public int? MaxHeight { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/ResponseProfile.cs b/MediaBrowser.Model/Dlna/ResponseProfile.cs new file mode 100644 index 000000000..742253fa3 --- /dev/null +++ b/MediaBrowser.Model/Dlna/ResponseProfile.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using System.Xml.Serialization; +using MediaBrowser.Model.Dlna; + +namespace MediaBrowser.Model.Dlna +{ + public class ResponseProfile + { + [XmlAttribute("container")] + public string Container { get; set; } + + [XmlAttribute("audioCodec")] + public string AudioCodec { get; set; } + + [XmlAttribute("videoCodec")] + public string VideoCodec { get; set; } + + [XmlAttribute("type")] + public DlnaProfileType Type { get; set; } + + [XmlAttribute("orgPn")] + public string OrgPn { get; set; } + + [XmlAttribute("mimeType")] + public string MimeType { get; set; } + + public ProfileCondition[] Conditions { get; set; } + + public ResponseProfile() + { + Conditions = new ProfileCondition[] {}; + } + + public string[] GetContainers() + { + return ContainerProfile.SplitValue(Container); + } + + public string[] GetAudioCodecs() + { + return ContainerProfile.SplitValue(AudioCodec); + } + + public string[] GetVideoCodecs() + { + return ContainerProfile.SplitValue(VideoCodec); + } + } +} diff --git a/MediaBrowser.Model/Dlna/SearchCriteria.cs b/MediaBrowser.Model/Dlna/SearchCriteria.cs new file mode 100644 index 000000000..533605d89 --- /dev/null +++ b/MediaBrowser.Model/Dlna/SearchCriteria.cs @@ -0,0 +1,75 @@ +using MediaBrowser.Model.Extensions; +using System; +using System.Text.RegularExpressions; + +namespace MediaBrowser.Model.Dlna +{ + public class SearchCriteria + { + public SearchType SearchType { get; set; } + + /// <summary> + /// Splits the specified string. + /// </summary> + /// <param name="str">The string.</param> + /// <param name="term">The term.</param> + /// <param name="limit">The limit.</param> + /// <returns>System.String[].</returns> + private string[] RegexSplit(string str, string term, int limit) + { + return new Regex(term).Split(str, limit); + } + + /// <summary> + /// Splits the specified string. + /// </summary> + /// <param name="str">The string.</param> + /// <param name="term">The term.</param> + /// <returns>System.String[].</returns> + private string[] RegexSplit(string str, string term) + { + return Regex.Split(str, term, RegexOptions.IgnoreCase); + } + + public SearchCriteria(string search) + { + if (string.IsNullOrEmpty(search)) + { + throw new ArgumentNullException("search"); + } + + SearchType = SearchType.Unknown; + + String[] factors = RegexSplit(search, "(and|or)"); + foreach (String factor in factors) + { + String[] subFactors = RegexSplit(factor.Trim().Trim('(').Trim(')').Trim(), "\\s", 3); + + if (subFactors.Length == 3) + { + + if (StringHelper.EqualsIgnoreCase("upnp:class", subFactors[0]) && + (StringHelper.EqualsIgnoreCase("=", subFactors[1]) || StringHelper.EqualsIgnoreCase("derivedfrom", subFactors[1]))) + { + if (StringHelper.EqualsIgnoreCase("\"object.item.imageItem\"", subFactors[2]) || StringHelper.EqualsIgnoreCase("\"object.item.imageItem.photo\"", subFactors[2])) + { + SearchType = SearchType.Image; + } + else if (StringHelper.EqualsIgnoreCase("\"object.item.videoItem\"", subFactors[2])) + { + SearchType = SearchType.Video; + } + else if (StringHelper.EqualsIgnoreCase("\"object.container.playlistContainer\"", subFactors[2])) + { + SearchType = SearchType.Playlist; + } + else if (StringHelper.EqualsIgnoreCase("\"object.container.album.musicAlbum\"", subFactors[2])) + { + SearchType = SearchType.MusicAlbum; + } + } + } + } + } + } +} diff --git a/MediaBrowser.Model/Dlna/SearchType.cs b/MediaBrowser.Model/Dlna/SearchType.cs new file mode 100644 index 000000000..27b207879 --- /dev/null +++ b/MediaBrowser.Model/Dlna/SearchType.cs @@ -0,0 +1,12 @@ +namespace MediaBrowser.Model.Dlna +{ + public enum SearchType + { + Unknown = 0, + Audio = 1, + Image = 2, + Video = 3, + Playlist = 4, + MusicAlbum = 5 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/SortCriteria.cs b/MediaBrowser.Model/Dlna/SortCriteria.cs new file mode 100644 index 000000000..600a2f58e --- /dev/null +++ b/MediaBrowser.Model/Dlna/SortCriteria.cs @@ -0,0 +1,17 @@ +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Model.Dlna +{ + public class SortCriteria + { + public SortOrder SortOrder + { + get { return SortOrder.Ascending; } + } + + public SortCriteria(string value) + { + + } + } +} diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs new file mode 100644 index 000000000..840abf618 --- /dev/null +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -0,0 +1,1900 @@ +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.Session; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace MediaBrowser.Model.Dlna +{ + public class StreamBuilder + { + private readonly ILogger _logger; + private readonly ITranscoderSupport _transcoderSupport; + + public StreamBuilder(ITranscoderSupport transcoderSupport, ILogger logger) + { + _transcoderSupport = transcoderSupport; + _logger = logger; + } + + public StreamBuilder(ILogger logger) + : this(new FullTranscoderSupport(), logger) + { + } + + public StreamInfo BuildAudioItem(AudioOptions options) + { + ValidateAudioInput(options); + + var mediaSources = new List<MediaSourceInfo>(); + foreach (MediaSourceInfo i in options.MediaSources) + { + if (string.IsNullOrEmpty(options.MediaSourceId) || + StringHelper.EqualsIgnoreCase(i.Id, options.MediaSourceId)) + { + mediaSources.Add(i); + } + } + + var streams = new List<StreamInfo>(); + foreach (MediaSourceInfo i in mediaSources) + { + StreamInfo streamInfo = BuildAudioItem(i, options); + if (streamInfo != null) + { + streams.Add(streamInfo); + } + } + + foreach (StreamInfo stream in streams) + { + stream.DeviceId = options.DeviceId; + stream.DeviceProfileId = options.Profile.Id; + } + + return GetOptimalStream(streams, options.GetMaxBitrate(true) ?? 0); + } + + public StreamInfo BuildVideoItem(VideoOptions options) + { + ValidateInput(options); + + var mediaSources = new List<MediaSourceInfo>(); + foreach (MediaSourceInfo i in options.MediaSources) + { + if (string.IsNullOrEmpty(options.MediaSourceId) || + StringHelper.EqualsIgnoreCase(i.Id, options.MediaSourceId)) + { + mediaSources.Add(i); + } + } + + var streams = new List<StreamInfo>(); + foreach (MediaSourceInfo i in mediaSources) + { + StreamInfo streamInfo = BuildVideoItem(i, options); + if (streamInfo != null) + { + streams.Add(streamInfo); + } + } + + foreach (StreamInfo stream in streams) + { + stream.DeviceId = options.DeviceId; + stream.DeviceProfileId = options.Profile.Id; + } + + return GetOptimalStream(streams, options.GetMaxBitrate(false) ?? 0); + } + + private StreamInfo GetOptimalStream(List<StreamInfo> streams, long maxBitrate) + { + var sorted = SortMediaSources(streams, maxBitrate); + + foreach (StreamInfo stream in sorted) + { + return stream; + } + + return null; + } + + private StreamInfo[] SortMediaSources(List<StreamInfo> streams, long maxBitrate) + { + return streams.OrderBy(i => + { + // Nothing beats direct playing a file + if (i.PlayMethod == PlayMethod.DirectPlay && i.MediaSource.Protocol == MediaProtocol.File) + { + return 0; + } + + return 1; + + }).ThenBy(i => + { + switch (i.PlayMethod) + { + // Let's assume direct streaming a file is just as desirable as direct playing a remote url + case PlayMethod.DirectStream: + case PlayMethod.DirectPlay: + return 0; + default: + return 1; + } + + }).ThenBy(i => + { + switch (i.MediaSource.Protocol) + { + case MediaProtocol.File: + return 0; + default: + return 1; + } + + }).ThenBy(i => + { + if (maxBitrate > 0) + { + if (i.MediaSource.Bitrate.HasValue) + { + return Math.Abs(i.MediaSource.Bitrate.Value - maxBitrate); + } + } + + return 0; + + }).ThenBy(streams.IndexOf).ToArray(); + } + + private TranscodeReason? GetTranscodeReasonForFailedCondition(ProfileCondition condition) + { + switch (condition.Property) + { + case ProfileConditionValue.AudioBitrate: + if (condition.Condition == ProfileConditionType.LessThanEqual) + { + return TranscodeReason.AudioBitrateNotSupported; + } + return TranscodeReason.AudioBitrateNotSupported; + + case ProfileConditionValue.AudioChannels: + if (condition.Condition == ProfileConditionType.LessThanEqual) + { + return TranscodeReason.AudioChannelsNotSupported; + } + return TranscodeReason.AudioChannelsNotSupported; + + case ProfileConditionValue.AudioProfile: + return TranscodeReason.AudioProfileNotSupported; + + case ProfileConditionValue.AudioSampleRate: + return TranscodeReason.AudioSampleRateNotSupported; + + case ProfileConditionValue.Has64BitOffsets: + // TODO + return null; + + case ProfileConditionValue.Height: + return TranscodeReason.VideoResolutionNotSupported; + + case ProfileConditionValue.IsAnamorphic: + return TranscodeReason.AnamorphicVideoNotSupported; + + case ProfileConditionValue.IsAvc: + // TODO + return null; + + case ProfileConditionValue.IsInterlaced: + return TranscodeReason.InterlacedVideoNotSupported; + + case ProfileConditionValue.IsSecondaryAudio: + return TranscodeReason.SecondaryAudioNotSupported; + + case ProfileConditionValue.NumAudioStreams: + // TODO + return null; + + case ProfileConditionValue.NumVideoStreams: + // TODO + return null; + + case ProfileConditionValue.PacketLength: + // TODO + return null; + + case ProfileConditionValue.RefFrames: + return TranscodeReason.RefFramesNotSupported; + + case ProfileConditionValue.VideoBitDepth: + return TranscodeReason.VideoBitDepthNotSupported; + + case ProfileConditionValue.AudioBitDepth: + return TranscodeReason.AudioBitDepthNotSupported; + + case ProfileConditionValue.VideoBitrate: + return TranscodeReason.VideoBitrateNotSupported; + + case ProfileConditionValue.VideoCodecTag: + return TranscodeReason.VideoCodecNotSupported; + + case ProfileConditionValue.VideoFramerate: + return TranscodeReason.VideoFramerateNotSupported; + + case ProfileConditionValue.VideoLevel: + return TranscodeReason.VideoLevelNotSupported; + + case ProfileConditionValue.VideoProfile: + return TranscodeReason.VideoProfileNotSupported; + + case ProfileConditionValue.VideoTimestamp: + // TODO + return null; + + case ProfileConditionValue.Width: + return TranscodeReason.VideoResolutionNotSupported; + + default: + return null; + } + } + + public static string NormalizeMediaSourceFormatIntoSingleContainer(string inputContainer, string unused1, DeviceProfile profile, DlnaProfileType type) + { + if (string.IsNullOrEmpty(inputContainer)) + { + return null; + } + + var formats = ContainerProfile.SplitValue(inputContainer); + + if (formats.Length == 1) + { + return formats[0]; + } + + if (profile != null) + { + foreach (var format in formats) + { + foreach (var directPlayProfile in profile.DirectPlayProfiles) + { + if (directPlayProfile.Type == type) + { + if (directPlayProfile.SupportsContainer(format)) + { + return format; + } + } + } + } + } + + return formats[0]; + } + + private StreamInfo BuildAudioItem(MediaSourceInfo item, AudioOptions options) + { + var transcodeReasons = new List<TranscodeReason>(); + + StreamInfo playlistItem = new StreamInfo + { + ItemId = options.ItemId, + MediaType = DlnaProfileType.Audio, + MediaSource = item, + RunTimeTicks = item.RunTimeTicks, + Context = options.Context, + DeviceProfile = options.Profile + }; + + if (options.ForceDirectPlay) + { + playlistItem.PlayMethod = PlayMethod.DirectPlay; + playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, item.Path, options.Profile, DlnaProfileType.Audio); + return playlistItem; + } + + if (options.ForceDirectStream) + { + playlistItem.PlayMethod = PlayMethod.DirectStream; + playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, item.Path, options.Profile, DlnaProfileType.Audio); + return playlistItem; + } + + MediaStream audioStream = item.GetDefaultAudioStream(null); + + var directPlayInfo = GetAudioDirectPlayMethods(item, audioStream, options); + + var directPlayMethods = directPlayInfo.Item1; + transcodeReasons.AddRange(directPlayInfo.Item2); + + ConditionProcessor conditionProcessor = new ConditionProcessor(); + + int? inputAudioChannels = audioStream == null ? null : audioStream.Channels; + int? inputAudioBitrate = audioStream == null ? null : audioStream.BitDepth; + int? inputAudioSampleRate = audioStream == null ? null : audioStream.SampleRate; + int? inputAudioBitDepth = audioStream == null ? null : audioStream.BitDepth; + + if (directPlayMethods.Count > 0) + { + string audioCodec = audioStream == null ? null : audioStream.Codec; + + // Make sure audio codec profiles are satisfied + var conditions = new List<ProfileCondition>(); + foreach (CodecProfile i in options.Profile.CodecProfiles) + { + if (i.Type == CodecType.Audio && i.ContainsAnyCodec(audioCodec, item.Container)) + { + bool applyConditions = true; + foreach (ProfileCondition applyCondition in i.ApplyConditions) + { + if (!conditionProcessor.IsAudioConditionSatisfied(applyCondition, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth)) + { + LogConditionFailure(options.Profile, "AudioCodecProfile", applyCondition, item); + applyConditions = false; + break; + } + } + + if (applyConditions) + { + foreach (ProfileCondition c in i.Conditions) + { + conditions.Add(c); + } + } + } + } + + bool all = true; + foreach (ProfileCondition c in conditions) + { + if (!conditionProcessor.IsAudioConditionSatisfied(c, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth)) + { + LogConditionFailure(options.Profile, "AudioCodecProfile", c, item); + var transcodeReason = GetTranscodeReasonForFailedCondition(c); + if (transcodeReason.HasValue) + { + transcodeReasons.Add(transcodeReason.Value); + } + all = false; + break; + } + } + + if (all) + { + if (directPlayMethods.Contains(PlayMethod.DirectStream)) + { + playlistItem.PlayMethod = PlayMethod.DirectStream; + } + + playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, item.Path, options.Profile, DlnaProfileType.Audio); + + return playlistItem; + } + } + + TranscodingProfile transcodingProfile = null; + foreach (TranscodingProfile i in options.Profile.TranscodingProfiles) + { + if (i.Type == playlistItem.MediaType && i.Context == options.Context) + { + if (_transcoderSupport.CanEncodeToAudioCodec(i.AudioCodec ?? i.Container)) + { + transcodingProfile = i; + break; + } + } + } + + if (transcodingProfile != null) + { + if (!item.SupportsTranscoding) + { + return null; + } + + SetStreamInfoOptionsFromTranscodingProfile(playlistItem, transcodingProfile); + + var audioCodecProfiles = new List<CodecProfile>(); + foreach (CodecProfile i in options.Profile.CodecProfiles) + { + if (i.Type == CodecType.Audio && i.ContainsAnyCodec(transcodingProfile.AudioCodec, transcodingProfile.Container)) + { + audioCodecProfiles.Add(i); + } + + if (audioCodecProfiles.Count >= 1) break; + } + + var audioTranscodingConditions = new List<ProfileCondition>(); + foreach (CodecProfile i in audioCodecProfiles) + { + bool applyConditions = true; + foreach (ProfileCondition applyCondition in i.ApplyConditions) + { + if (!conditionProcessor.IsAudioConditionSatisfied(applyCondition, inputAudioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth)) + { + LogConditionFailure(options.Profile, "AudioCodecProfile", applyCondition, item); + applyConditions = false; + break; + } + } + + if (applyConditions) + { + foreach (ProfileCondition c in i.Conditions) + { + audioTranscodingConditions.Add(c); + } + } + } + + ApplyTranscodingConditions(playlistItem, audioTranscodingConditions, null, true, true); + + // Honor requested max channels + playlistItem.GlobalMaxAudioChannels = options.MaxAudioChannels; + + var configuredBitrate = options.GetMaxBitrate(true); + + long transcodingBitrate = options.AudioTranscodingBitrate ?? + (options.Context == EncodingContext.Streaming ? options.Profile.MusicStreamingTranscodingBitrate : null) ?? + configuredBitrate ?? + 128000; + + if (configuredBitrate.HasValue) + { + transcodingBitrate = Math.Min(configuredBitrate.Value, transcodingBitrate); + } + + var longBitrate = Math.Min(transcodingBitrate, playlistItem.AudioBitrate ?? transcodingBitrate); + playlistItem.AudioBitrate = longBitrate > int.MaxValue ? int.MaxValue : Convert.ToInt32(longBitrate); + } + + playlistItem.TranscodeReasons = transcodeReasons.ToArray(); + return playlistItem; + } + + private long? GetBitrateForDirectPlayCheck(MediaSourceInfo item, AudioOptions options, bool isAudio) + { + if (item.Protocol == MediaProtocol.File) + { + return options.Profile.MaxStaticBitrate; + } + + return options.GetMaxBitrate(isAudio); + } + + private Tuple<List<PlayMethod>, List<TranscodeReason>> GetAudioDirectPlayMethods(MediaSourceInfo item, MediaStream audioStream, AudioOptions options) + { + var transcodeReasons = new List<TranscodeReason>(); + + DirectPlayProfile directPlayProfile = null; + foreach (DirectPlayProfile i in options.Profile.DirectPlayProfiles) + { + if (i.Type == DlnaProfileType.Audio && IsAudioDirectPlaySupported(i, item, audioStream)) + { + directPlayProfile = i; + break; + } + } + + var playMethods = new List<PlayMethod>(); + + if (directPlayProfile != null) + { + // While options takes the network and other factors into account. Only applies to direct stream + if (item.SupportsDirectStream) + { + if (IsAudioEligibleForDirectPlay(item, options.GetMaxBitrate(true) ?? 0, PlayMethod.DirectStream)) + { + if (options.EnableDirectStream) + { + playMethods.Add(PlayMethod.DirectStream); + } + } + else + { + transcodeReasons.Add(TranscodeReason.ContainerBitrateExceedsLimit); + } + } + + // The profile describes what the device supports + // If device requirements are satisfied then allow both direct stream and direct play + if (item.SupportsDirectPlay) + { + if (IsAudioEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true) ?? 0, PlayMethod.DirectPlay)) + { + if (options.EnableDirectPlay) + { + playMethods.Add(PlayMethod.DirectPlay); + } + } + else + { + transcodeReasons.Add(TranscodeReason.ContainerBitrateExceedsLimit); + } + } + } + else + { + transcodeReasons.InsertRange(0, GetTranscodeReasonsFromDirectPlayProfile(item, null, audioStream, options.Profile.DirectPlayProfiles)); + + _logger.Info("Profile: {0}, No direct play profiles found for Path: {1}", + options.Profile.Name ?? "Unknown Profile", + item.Path ?? "Unknown path"); + } + + if (playMethods.Count > 0) + { + transcodeReasons.Clear(); + } + else + { + transcodeReasons = transcodeReasons.Distinct().ToList(); + } + + return new Tuple<List<PlayMethod>, List<TranscodeReason>>(playMethods, transcodeReasons); + } + + private List<TranscodeReason> GetTranscodeReasonsFromDirectPlayProfile(MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream, IEnumerable<DirectPlayProfile> directPlayProfiles) + { + var list = new List<TranscodeReason>(); + var containerSupported = false; + var audioSupported = false; + var videoSupported = false; + + foreach (var profile in directPlayProfiles) + { + audioSupported = false; + videoSupported = false; + + // Check container type + if (profile.SupportsContainer(item.Container)) + { + containerSupported = true; + + if (videoStream != null) + { + if (profile.SupportsVideoCodec(videoStream.Codec)) + { + videoSupported = true; + } + } + + if (audioStream != null) + { + if (profile.SupportsAudioCodec(audioStream.Codec)) + { + audioSupported = true; + } + } + + if (videoSupported && audioSupported) + { + break; + } + } + } + + if (!containerSupported) + { + list.Add(TranscodeReason.ContainerNotSupported); + } + + if (videoStream != null && !videoSupported) + { + list.Add(TranscodeReason.VideoCodecNotSupported); + } + + if (audioStream != null && !audioSupported) + { + list.Add(TranscodeReason.AudioCodecNotSupported); + } + + return list; + } + + private int? GetDefaultSubtitleStreamIndex(MediaSourceInfo item, SubtitleProfile[] subtitleProfiles) + { + int highestScore = -1; + + foreach (MediaStream stream in item.MediaStreams) + { + if (stream.Type == MediaStreamType.Subtitle && stream.Score.HasValue) + { + if (stream.Score.Value > highestScore) + { + highestScore = stream.Score.Value; + } + } + } + + var topStreams = new List<MediaStream>(); + foreach (MediaStream stream in item.MediaStreams) + { + if (stream.Type == MediaStreamType.Subtitle && stream.Score.HasValue && stream.Score.Value == highestScore) + { + topStreams.Add(stream); + } + } + + // If multiple streams have an equal score, try to pick the most efficient one + if (topStreams.Count > 1) + { + foreach (MediaStream stream in topStreams) + { + foreach (SubtitleProfile profile in subtitleProfiles) + { + if (profile.Method == SubtitleDeliveryMethod.External && StringHelper.EqualsIgnoreCase(profile.Format, stream.Codec)) + { + return stream.Index; + } + } + } + } + + // If no optimization panned out, just use the original default + return item.DefaultSubtitleStreamIndex; + } + + private void SetStreamInfoOptionsFromTranscodingProfile(StreamInfo playlistItem, TranscodingProfile transcodingProfile) + { + if (string.IsNullOrEmpty(transcodingProfile.AudioCodec)) + { + playlistItem.AudioCodecs = Array.Empty<string>(); + } + else + { + playlistItem.AudioCodecs = transcodingProfile.AudioCodec.Split(','); + } + + playlistItem.Container = transcodingProfile.Container; + playlistItem.EstimateContentLength = transcodingProfile.EstimateContentLength; + playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo; + + if (string.IsNullOrEmpty(transcodingProfile.VideoCodec)) + { + playlistItem.VideoCodecs = Array.Empty<string>(); + } + else + { + playlistItem.VideoCodecs = transcodingProfile.VideoCodec.Split(','); + } + + playlistItem.CopyTimestamps = transcodingProfile.CopyTimestamps; + playlistItem.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest; + playlistItem.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode; + + playlistItem.BreakOnNonKeyFrames = transcodingProfile.BreakOnNonKeyFrames; + + if (transcodingProfile.MinSegments > 0) + { + playlistItem.MinSegments = transcodingProfile.MinSegments; + } + if (transcodingProfile.SegmentLength > 0) + { + playlistItem.SegmentLength = transcodingProfile.SegmentLength; + } + playlistItem.SubProtocol = transcodingProfile.Protocol; + + if (!string.IsNullOrEmpty(transcodingProfile.MaxAudioChannels)) + { + int transcodingMaxAudioChannels; + if (int.TryParse(transcodingProfile.MaxAudioChannels, NumberStyles.Any, CultureInfo.InvariantCulture, out transcodingMaxAudioChannels)) + { + playlistItem.TranscodingMaxAudioChannels = transcodingMaxAudioChannels; + } + } + } + + private StreamInfo BuildVideoItem(MediaSourceInfo item, VideoOptions options) + { + if (item == null) + { + throw new ArgumentNullException("item"); + } + + var transcodeReasons = new List<TranscodeReason>(); + + StreamInfo playlistItem = new StreamInfo + { + ItemId = options.ItemId, + MediaType = DlnaProfileType.Video, + MediaSource = item, + RunTimeTicks = item.RunTimeTicks, + Context = options.Context, + DeviceProfile = options.Profile + }; + + playlistItem.SubtitleStreamIndex = options.SubtitleStreamIndex ?? GetDefaultSubtitleStreamIndex(item, options.Profile.SubtitleProfiles); + MediaStream subtitleStream = playlistItem.SubtitleStreamIndex.HasValue ? item.GetMediaStream(MediaStreamType.Subtitle, playlistItem.SubtitleStreamIndex.Value) : null; + + MediaStream audioStream = item.GetDefaultAudioStream(options.AudioStreamIndex ?? item.DefaultAudioStreamIndex); + if (audioStream != null) + { + playlistItem.AudioStreamIndex = audioStream.Index; + } + + MediaStream videoStream = item.VideoStream; + + // TODO: This doesn't accout for situation of device being able to handle media bitrate, but wifi connection not fast enough + var directPlayEligibilityResult = IsEligibleForDirectPlay(item, GetBitrateForDirectPlayCheck(item, options, true) ?? 0, subtitleStream, options, PlayMethod.DirectPlay); + var directStreamEligibilityResult = IsEligibleForDirectPlay(item, options.GetMaxBitrate(false) ?? 0, subtitleStream, options, PlayMethod.DirectStream); + bool isEligibleForDirectPlay = options.EnableDirectPlay && (options.ForceDirectPlay || directPlayEligibilityResult.Item1); + bool isEligibleForDirectStream = options.EnableDirectStream && (options.ForceDirectStream || directStreamEligibilityResult.Item1); + + _logger.Info("Profile: {0}, Path: {1}, isEligibleForDirectPlay: {2}, isEligibleForDirectStream: {3}", + options.Profile.Name ?? "Unknown Profile", + item.Path ?? "Unknown path", + isEligibleForDirectPlay, + isEligibleForDirectStream); + + if (isEligibleForDirectPlay || isEligibleForDirectStream) + { + // See if it can be direct played + var directPlayInfo = GetVideoDirectPlayProfile(options, item, videoStream, audioStream, isEligibleForDirectPlay, isEligibleForDirectStream); + var directPlay = directPlayInfo.Item1; + + if (directPlay != null) + { + playlistItem.PlayMethod = directPlay.Value; + playlistItem.Container = NormalizeMediaSourceFormatIntoSingleContainer(item.Container, item.Path, options.Profile, DlnaProfileType.Video); + + if (subtitleStream != null) + { + SubtitleProfile subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, directPlay.Value, _transcoderSupport, item.Container, null); + + playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method; + playlistItem.SubtitleFormat = subtitleProfile.Format; + } + + return playlistItem; + } + + transcodeReasons.AddRange(directPlayInfo.Item2); + } + + if (directPlayEligibilityResult.Item2.HasValue) + { + transcodeReasons.Add(directPlayEligibilityResult.Item2.Value); + } + + if (directStreamEligibilityResult.Item2.HasValue) + { + transcodeReasons.Add(directStreamEligibilityResult.Item2.Value); + } + + // Can't direct play, find the transcoding profile + TranscodingProfile transcodingProfile = null; + foreach (TranscodingProfile i in options.Profile.TranscodingProfiles) + { + if (i.Type == playlistItem.MediaType && i.Context == options.Context) + { + transcodingProfile = i; + break; + } + } + + if (transcodingProfile != null) + { + if (!item.SupportsTranscoding) + { + return null; + } + + if (subtitleStream != null) + { + SubtitleProfile subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, PlayMethod.Transcode, _transcoderSupport, transcodingProfile.Container, transcodingProfile.Protocol); + + playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method; + playlistItem.SubtitleFormat = subtitleProfile.Format; + playlistItem.SubtitleCodecs = new[] { subtitleProfile.Format }; + } + + playlistItem.PlayMethod = PlayMethod.Transcode; + + SetStreamInfoOptionsFromTranscodingProfile(playlistItem, transcodingProfile); + + ConditionProcessor conditionProcessor = new ConditionProcessor(); + + var isFirstAppliedCodecProfile = true; + foreach (CodecProfile i in options.Profile.CodecProfiles) + { + if (i.Type == CodecType.Video && i.ContainsAnyCodec(transcodingProfile.VideoCodec, transcodingProfile.Container)) + { + bool applyConditions = true; + foreach (ProfileCondition applyCondition in i.ApplyConditions) + { + int? width = videoStream == null ? null : videoStream.Width; + int? height = videoStream == null ? null : videoStream.Height; + int? bitDepth = videoStream == null ? null : videoStream.BitDepth; + int? videoBitrate = videoStream == null ? null : videoStream.BitRate; + double? videoLevel = videoStream == null ? null : videoStream.Level; + string videoProfile = videoStream == null ? null : videoStream.Profile; + float videoFramerate = videoStream == null ? 0 : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate ?? 0; + bool? isAnamorphic = videoStream == null ? null : videoStream.IsAnamorphic; + bool? isInterlaced = videoStream == null ? (bool?)null : videoStream.IsInterlaced; + string videoCodecTag = videoStream == null ? null : videoStream.CodecTag; + bool? isAvc = videoStream == null ? null : videoStream.IsAVC; + + TransportStreamTimestamp? timestamp = videoStream == null ? TransportStreamTimestamp.None : item.Timestamp; + int? packetLength = videoStream == null ? null : videoStream.PacketLength; + int? refFrames = videoStream == null ? null : videoStream.RefFrames; + + int? numAudioStreams = item.GetStreamCount(MediaStreamType.Audio); + int? numVideoStreams = item.GetStreamCount(MediaStreamType.Video); + + if (!conditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)) + { + //LogConditionFailure(options.Profile, "VideoCodecProfile.ApplyConditions", applyCondition, item); + applyConditions = false; + break; + } + } + + if (applyConditions) + { + var transcodingVideoCodecs = ContainerProfile.SplitValue(transcodingProfile.VideoCodec); + foreach (var transcodingVideoCodec in transcodingVideoCodecs) + { + if (i.ContainsAnyCodec(transcodingVideoCodec, transcodingProfile.Container)) + { + ApplyTranscodingConditions(playlistItem, i.Conditions, transcodingVideoCodec, true, isFirstAppliedCodecProfile); + isFirstAppliedCodecProfile = false; + } + } + } + } + } + + // Honor requested max channels + playlistItem.GlobalMaxAudioChannels = options.MaxAudioChannels; + + int audioBitrate = GetAudioBitrate(playlistItem.SubProtocol, options.GetMaxBitrate(false) ?? 0, playlistItem.TargetAudioCodec, audioStream, playlistItem); + playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate); + + isFirstAppliedCodecProfile = true; + foreach (CodecProfile i in options.Profile.CodecProfiles) + { + if (i.Type == CodecType.VideoAudio && i.ContainsAnyCodec(transcodingProfile.AudioCodec, transcodingProfile.Container)) + { + bool applyConditions = true; + foreach (ProfileCondition applyCondition in i.ApplyConditions) + { + bool? isSecondaryAudio = audioStream == null ? null : item.IsSecondaryAudio(audioStream); + int? inputAudioBitrate = audioStream == null ? null : audioStream.BitRate; + int? audioChannels = audioStream == null ? null : audioStream.Channels; + string audioProfile = audioStream == null ? null : audioStream.Profile; + int? inputAudioSampleRate = audioStream == null ? null : audioStream.SampleRate; + int? inputAudioBitDepth = audioStream == null ? null : audioStream.BitDepth; + + if (!conditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, audioProfile, isSecondaryAudio)) + { + //LogConditionFailure(options.Profile, "VideoCodecProfile.ApplyConditions", applyCondition, item); + applyConditions = false; + break; + } + } + + if (applyConditions) + { + var transcodingAudioCodecs = ContainerProfile.SplitValue(transcodingProfile.AudioCodec); + foreach (var transcodingAudioCodec in transcodingAudioCodecs) + { + if (i.ContainsAnyCodec(transcodingAudioCodec, transcodingProfile.Container)) + { + ApplyTranscodingConditions(playlistItem, i.Conditions, transcodingAudioCodec, true, isFirstAppliedCodecProfile); + isFirstAppliedCodecProfile = false; + } + } + } + } + } + + var maxBitrateSetting = options.GetMaxBitrate(false); + // Honor max rate + if (maxBitrateSetting.HasValue) + { + var availableBitrateForVideo = maxBitrateSetting.Value; + + if (playlistItem.AudioBitrate.HasValue) + { + availableBitrateForVideo -= playlistItem.AudioBitrate.Value; + } + + // Make sure the video bitrate is lower than bitrate settings but at least 64k + long currentValue = playlistItem.VideoBitrate ?? availableBitrateForVideo; + var longBitrate = Math.Max(Math.Min(availableBitrateForVideo, currentValue), 64000); + playlistItem.VideoBitrate = longBitrate >= int.MaxValue ? int.MaxValue : Convert.ToInt32(longBitrate); + } + } + + playlistItem.TranscodeReasons = transcodeReasons.ToArray(); + + return playlistItem; + } + + private int GetDefaultAudioBitrateIfUnknown(MediaStream audioStream) + { + if ((audioStream.Channels ?? 0) >= 6) + { + return 384000; + } + + return 192000; + } + + private int GetAudioBitrate(string subProtocol, long maxTotalBitrate, string[] targetAudioCodecs, MediaStream audioStream, StreamInfo item) + { + var targetAudioCodec = targetAudioCodecs.Length == 0 ? null : targetAudioCodecs[0]; + + var targetAudioChannels = item.GetTargetAudioChannels(targetAudioCodec); + + int defaultBitrate = audioStream == null ? 192000 : audioStream.BitRate ?? GetDefaultAudioBitrateIfUnknown(audioStream); + + // Reduce the bitrate if we're downmixing + if (targetAudioChannels.HasValue && audioStream != null && audioStream.Channels.HasValue && targetAudioChannels.Value < audioStream.Channels.Value) + { + defaultBitrate = targetAudioChannels.Value <= 2 ? 128000 : 192000; + } + + int encoderAudioBitrateLimit = int.MaxValue; + + if (audioStream != null) + { + // Seeing webm encoding failures when source has 1 audio channel and 22k bitrate. + // Any attempts to transcode over 64k will fail + if (audioStream.Channels.HasValue && + audioStream.Channels.Value == 1) + { + if ((audioStream.BitRate ?? 0) < 64000) + { + encoderAudioBitrateLimit = 64000; + } + } + } + + if (maxTotalBitrate > 0) + { + defaultBitrate = Math.Min(GetMaxAudioBitrateForTotalBitrate(maxTotalBitrate), defaultBitrate); + } + + return Math.Min(defaultBitrate, encoderAudioBitrateLimit); + } + + private int GetMaxAudioBitrateForTotalBitrate(long totalBitrate) + { + if (totalBitrate <= 640000) + { + return 128000; + } + + if (totalBitrate <= 2000000) + { + return 384000; + } + + if (totalBitrate <= 3000000) + { + return 448000; + } + + return 640000; + } + + private Tuple<PlayMethod?, List<TranscodeReason>> GetVideoDirectPlayProfile(VideoOptions options, + MediaSourceInfo mediaSource, + MediaStream videoStream, + MediaStream audioStream, + bool isEligibleForDirectPlay, + bool isEligibleForDirectStream) + { + DeviceProfile profile = options.Profile; + + if (options.ForceDirectPlay) + { + return new Tuple<PlayMethod?, List<TranscodeReason>>(PlayMethod.DirectPlay, new List<TranscodeReason>()); + } + if (options.ForceDirectStream) + { + return new Tuple<PlayMethod?, List<TranscodeReason>>(PlayMethod.DirectStream, new List<TranscodeReason>()); + } + + // See if it can be direct played + DirectPlayProfile directPlay = null; + foreach (DirectPlayProfile i in profile.DirectPlayProfiles) + { + if (i.Type == DlnaProfileType.Video && IsVideoDirectPlaySupported(i, mediaSource, videoStream, audioStream)) + { + directPlay = i; + break; + } + } + + if (directPlay == null) + { + _logger.Info("Profile: {0}, No direct play profiles found for Path: {1}", + profile.Name ?? "Unknown Profile", + mediaSource.Path ?? "Unknown path"); + + return new Tuple<PlayMethod?, List<TranscodeReason>>(null, GetTranscodeReasonsFromDirectPlayProfile(mediaSource, videoStream, audioStream, profile.DirectPlayProfiles)); + } + + string container = mediaSource.Container; + + var conditions = new List<ProfileCondition>(); + foreach (ContainerProfile i in profile.ContainerProfiles) + { + if (i.Type == DlnaProfileType.Video && + i.ContainsContainer(container)) + { + foreach (ProfileCondition c in i.Conditions) + { + conditions.Add(c); + } + } + } + + ConditionProcessor conditionProcessor = new ConditionProcessor(); + + int? width = videoStream == null ? null : videoStream.Width; + int? height = videoStream == null ? null : videoStream.Height; + int? bitDepth = videoStream == null ? null : videoStream.BitDepth; + int? videoBitrate = videoStream == null ? null : videoStream.BitRate; + double? videoLevel = videoStream == null ? null : videoStream.Level; + string videoProfile = videoStream == null ? null : videoStream.Profile; + float videoFramerate = videoStream == null ? 0 : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate ?? 0; + bool? isAnamorphic = videoStream == null ? null : videoStream.IsAnamorphic; + bool? isInterlaced = videoStream == null ? (bool?)null : videoStream.IsInterlaced; + string videoCodecTag = videoStream == null ? null : videoStream.CodecTag; + bool? isAvc = videoStream == null ? null : videoStream.IsAVC; + + int? audioBitrate = audioStream == null ? null : audioStream.BitRate; + int? audioChannels = audioStream == null ? null : audioStream.Channels; + string audioProfile = audioStream == null ? null : audioStream.Profile; + int? audioSampleRate = audioStream == null ? null : audioStream.SampleRate; + int? audioBitDepth = audioStream == null ? null : audioStream.BitDepth; + + TransportStreamTimestamp? timestamp = videoStream == null ? TransportStreamTimestamp.None : mediaSource.Timestamp; + int? packetLength = videoStream == null ? null : videoStream.PacketLength; + int? refFrames = videoStream == null ? null : videoStream.RefFrames; + + int? numAudioStreams = mediaSource.GetStreamCount(MediaStreamType.Audio); + int? numVideoStreams = mediaSource.GetStreamCount(MediaStreamType.Video); + + // Check container conditions + foreach (ProfileCondition i in conditions) + { + if (!conditionProcessor.IsVideoConditionSatisfied(i, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)) + { + LogConditionFailure(profile, "VideoContainerProfile", i, mediaSource); + + var transcodeReason = GetTranscodeReasonForFailedCondition(i); + var transcodeReasons = transcodeReason.HasValue + ? new List<TranscodeReason> { transcodeReason.Value } + : new List<TranscodeReason> { }; + + return new Tuple<PlayMethod?, List<TranscodeReason>>(null, transcodeReasons); + } + } + + string videoCodec = videoStream == null ? null : videoStream.Codec; + + conditions = new List<ProfileCondition>(); + foreach (CodecProfile i in profile.CodecProfiles) + { + if (i.Type == CodecType.Video && i.ContainsAnyCodec(videoCodec, container)) + { + bool applyConditions = true; + foreach (ProfileCondition applyCondition in i.ApplyConditions) + { + if (!conditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)) + { + //LogConditionFailure(profile, "VideoCodecProfile.ApplyConditions", applyCondition, mediaSource); + applyConditions = false; + break; + } + } + + if (applyConditions) + { + foreach (ProfileCondition c in i.Conditions) + { + conditions.Add(c); + } + } + } + } + + foreach (ProfileCondition i in conditions) + { + if (!conditionProcessor.IsVideoConditionSatisfied(i, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc)) + { + LogConditionFailure(profile, "VideoCodecProfile", i, mediaSource); + + var transcodeReason = GetTranscodeReasonForFailedCondition(i); + var transcodeReasons = transcodeReason.HasValue + ? new List<TranscodeReason> { transcodeReason.Value } + : new List<TranscodeReason> { }; + + return new Tuple<PlayMethod?, List<TranscodeReason>>(null, transcodeReasons); + } + } + + if (audioStream != null) + { + string audioCodec = audioStream.Codec; + + conditions = new List<ProfileCondition>(); + bool? isSecondaryAudio = audioStream == null ? null : mediaSource.IsSecondaryAudio(audioStream); + + foreach (CodecProfile i in profile.CodecProfiles) + { + if (i.Type == CodecType.VideoAudio && i.ContainsAnyCodec(audioCodec, container)) + { + bool applyConditions = true; + foreach (ProfileCondition applyCondition in i.ApplyConditions) + { + if (!conditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, audioBitrate, audioSampleRate, audioBitDepth, audioProfile, isSecondaryAudio)) + { + //LogConditionFailure(profile, "VideoAudioCodecProfile.ApplyConditions", applyCondition, mediaSource); + applyConditions = false; + break; + } + } + + if (applyConditions) + { + foreach (ProfileCondition c in i.Conditions) + { + conditions.Add(c); + } + } + } + } + + foreach (ProfileCondition i in conditions) + { + if (!conditionProcessor.IsVideoAudioConditionSatisfied(i, audioChannels, audioBitrate, audioSampleRate, audioBitDepth, audioProfile, isSecondaryAudio)) + { + LogConditionFailure(profile, "VideoAudioCodecProfile", i, mediaSource); + + var transcodeReason = GetTranscodeReasonForFailedCondition(i); + var transcodeReasons = transcodeReason.HasValue + ? new List<TranscodeReason> { transcodeReason.Value } + : new List<TranscodeReason> { }; + + return new Tuple<PlayMethod?, List<TranscodeReason>>(null, transcodeReasons); + } + } + } + + if (isEligibleForDirectStream && mediaSource.SupportsDirectStream) + { + return new Tuple<PlayMethod?, List<TranscodeReason>>(PlayMethod.DirectStream, new List<TranscodeReason>()); + } + + return new Tuple<PlayMethod?, List<TranscodeReason>>(null, new List<TranscodeReason> { TranscodeReason.ContainerBitrateExceedsLimit }); + } + + private void LogConditionFailure(DeviceProfile profile, string type, ProfileCondition condition, MediaSourceInfo mediaSource) + { + _logger.Info("Profile: {0}, DirectPlay=false. Reason={1}.{2} Condition: {3}. ConditionValue: {4}. IsRequired: {5}. Path: {6}", + type, + profile.Name ?? "Unknown Profile", + condition.Property, + condition.Condition, + condition.Value ?? string.Empty, + condition.IsRequired, + mediaSource.Path ?? "Unknown path"); + } + + private ValueTuple<bool, TranscodeReason?> IsEligibleForDirectPlay(MediaSourceInfo item, + long maxBitrate, + MediaStream subtitleStream, + VideoOptions options, + PlayMethod playMethod) + { + if (subtitleStream != null) + { + SubtitleProfile subtitleProfile = GetSubtitleProfile(item, subtitleStream, options.Profile.SubtitleProfiles, playMethod, _transcoderSupport, item.Container, null); + + if (subtitleProfile.Method != SubtitleDeliveryMethod.External && subtitleProfile.Method != SubtitleDeliveryMethod.Embed) + { + _logger.Info("Not eligible for {0} due to unsupported subtitles", playMethod); + return new ValueTuple<bool, TranscodeReason?>(false, TranscodeReason.SubtitleCodecNotSupported); + } + } + + var result = IsAudioEligibleForDirectPlay(item, maxBitrate, playMethod); + + if (result) + { + return new ValueTuple<bool, TranscodeReason?>(result, null); + } + + return new ValueTuple<bool, TranscodeReason?>(result, TranscodeReason.ContainerBitrateExceedsLimit); + } + + public static SubtitleProfile GetSubtitleProfile(MediaSourceInfo mediaSource, MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, PlayMethod playMethod, ITranscoderSupport transcoderSupport, string outputContainer, string transcodingSubProtocol) + { + if (!subtitleStream.IsExternal && (playMethod != PlayMethod.Transcode || !string.Equals(transcodingSubProtocol, "hls", StringComparison.OrdinalIgnoreCase))) + { + // Look for supported embedded subs of the same format + foreach (SubtitleProfile profile in subtitleProfiles) + { + if (!profile.SupportsLanguage(subtitleStream.Language)) + { + continue; + } + + if (profile.Method != SubtitleDeliveryMethod.Embed) + { + continue; + } + + if (!ContainerProfile.ContainsContainer(profile.Container, outputContainer)) + { + continue; + } + + if (playMethod == PlayMethod.Transcode && !IsSubtitleEmbedSupported(subtitleStream, profile, transcodingSubProtocol, outputContainer)) + { + continue; + } + + if (subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format) && StringHelper.EqualsIgnoreCase(profile.Format, subtitleStream.Codec)) + { + return profile; + } + } + + // Look for supported embedded subs of a convertible format + foreach (SubtitleProfile profile in subtitleProfiles) + { + if (!profile.SupportsLanguage(subtitleStream.Language)) + { + continue; + } + + if (profile.Method != SubtitleDeliveryMethod.Embed) + { + continue; + } + + if (!ContainerProfile.ContainsContainer(profile.Container, outputContainer)) + { + continue; + } + + if (playMethod == PlayMethod.Transcode && !IsSubtitleEmbedSupported(subtitleStream, profile, transcodingSubProtocol, outputContainer)) + { + continue; + } + + if (subtitleStream.IsTextSubtitleStream && subtitleStream.SupportsSubtitleConversionTo(profile.Format)) + { + return profile; + } + } + } + + // Look for an external or hls profile that matches the stream type (text/graphical) and doesn't require conversion + return GetExternalSubtitleProfile(mediaSource, subtitleStream, subtitleProfiles, playMethod, transcoderSupport, false) ?? + GetExternalSubtitleProfile(mediaSource, subtitleStream, subtitleProfiles, playMethod, transcoderSupport, true) ?? + new SubtitleProfile + { + Method = SubtitleDeliveryMethod.Encode, + Format = subtitleStream.Codec + }; + } + + private static bool IsSubtitleEmbedSupported(MediaStream subtitleStream, SubtitleProfile subtitleProfile, string transcodingSubProtocol, string transcodingContainer) + { + if (!string.IsNullOrEmpty(transcodingContainer)) + { + var normalizedContainers = ContainerProfile.SplitValue(transcodingContainer); + + if (ContainerProfile.ContainsContainer(normalizedContainers, "ts")) + { + return false; + } + if (ContainerProfile.ContainsContainer(normalizedContainers, "mpegts")) + { + return false; + } + if (ContainerProfile.ContainsContainer(normalizedContainers, "mp4")) + { + return false; + } + if (ContainerProfile.ContainsContainer(normalizedContainers, "mkv") || + ContainerProfile.ContainsContainer(normalizedContainers, "matroska")) + { + return true; + } + } + + return false; + } + + private static SubtitleProfile GetExternalSubtitleProfile(MediaSourceInfo mediaSource, MediaStream subtitleStream, SubtitleProfile[] subtitleProfiles, PlayMethod playMethod, ITranscoderSupport transcoderSupport, bool allowConversion) + { + foreach (SubtitleProfile profile in subtitleProfiles) + { + if (profile.Method != SubtitleDeliveryMethod.External && profile.Method != SubtitleDeliveryMethod.Hls) + { + continue; + } + + if (profile.Method == SubtitleDeliveryMethod.Hls && playMethod != PlayMethod.Transcode) + { + continue; + } + + if (!profile.SupportsLanguage(subtitleStream.Language)) + { + continue; + } + + if (!subtitleStream.IsExternal && !transcoderSupport.CanExtractSubtitles(subtitleStream.Codec)) + { + continue; + } + + if ((profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) || + (profile.Method == SubtitleDeliveryMethod.Hls && subtitleStream.IsTextSubtitleStream)) + { + bool requiresConversion = !StringHelper.EqualsIgnoreCase(subtitleStream.Codec, profile.Format); + + if (!requiresConversion) + { + return profile; + } + + if (!allowConversion) + { + continue; + } + + // TODO: Build this into subtitleStream.SupportsExternalStream + if (mediaSource.IsInfiniteStream) + { + continue; + } + + if (subtitleStream.IsTextSubtitleStream && subtitleStream.SupportsExternalStream && subtitleStream.SupportsSubtitleConversionTo(profile.Format)) + { + return profile; + } + } + } + + return null; + } + + private bool IsAudioEligibleForDirectPlay(MediaSourceInfo item, long maxBitrate, PlayMethod playMethod) + { + // Don't restrict by bitrate if coming from an external domain + if (item.IsRemote) + { + return true; + } + + var requestedMaxBitrate = maxBitrate > 0 ? maxBitrate : 1000000; + + // If we don't know the bitrate, then force a transcode if requested max bitrate is under 40 mbps + var itemBitrate = item.Bitrate ?? + 40000000; + + if (itemBitrate > requestedMaxBitrate) + { + _logger.Info("Bitrate exceeds " + playMethod + " limit: media bitrate: {0}, max bitrate: {1}", itemBitrate.ToString(CultureInfo.InvariantCulture), requestedMaxBitrate.ToString(CultureInfo.InvariantCulture)); + return false; + } + + return true; + } + + private void ValidateInput(VideoOptions options) + { + ValidateAudioInput(options); + + if (options.AudioStreamIndex.HasValue && string.IsNullOrEmpty(options.MediaSourceId)) + { + throw new ArgumentException("MediaSourceId is required when a specific audio stream is requested"); + } + + if (options.SubtitleStreamIndex.HasValue && string.IsNullOrEmpty(options.MediaSourceId)) + { + throw new ArgumentException("MediaSourceId is required when a specific subtitle stream is requested"); + } + } + + private void ValidateAudioInput(AudioOptions options) + { + if (options.ItemId.Equals(Guid.Empty)) + { + throw new ArgumentException("ItemId is required"); + } + if (string.IsNullOrEmpty(options.DeviceId)) + { + throw new ArgumentException("DeviceId is required"); + } + if (options.Profile == null) + { + throw new ArgumentException("Profile is required"); + } + if (options.MediaSources == null) + { + throw new ArgumentException("MediaSources is required"); + } + } + + private void ApplyTranscodingConditions(StreamInfo item, List<CodecProfile> codecProfiles) + { + foreach (var profile in codecProfiles) + { + ApplyTranscodingConditions(item, profile); + } + } + + private void ApplyTranscodingConditions(StreamInfo item, CodecProfile codecProfile) + { + var codecs = ContainerProfile.SplitValue(codecProfile.Codec); + if (codecs.Length == 0) + { + ApplyTranscodingConditions(item, codecProfile.Conditions, null, true, true); + return; + } + + var enableNonQualified = true; + + foreach (var codec in codecs) + { + ApplyTranscodingConditions(item, codecProfile.Conditions, codec, true, enableNonQualified); + enableNonQualified = false; + } + } + + private void ApplyTranscodingConditions(StreamInfo item, IEnumerable<ProfileCondition> conditions, string qualifier, bool enableQualifiedConditions, bool enableNonQualifiedConditions) + { + foreach (ProfileCondition condition in conditions) + { + string value = condition.Value; + + if (string.IsNullOrEmpty(value)) + { + continue; + } + + // No way to express this + if (condition.Condition == ProfileConditionType.GreaterThanEqual) + { + continue; + } + + switch (condition.Property) + { + case ProfileConditionValue.AudioBitrate: + { + if (!enableNonQualifiedConditions) + { + continue; + } + + int num; + if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num)) + { + if (condition.Condition == ProfileConditionType.Equals) + { + item.AudioBitrate = num; + } + else if (condition.Condition == ProfileConditionType.LessThanEqual) + { + item.AudioBitrate = Math.Min(num, item.AudioBitrate ?? num); + } + else if (condition.Condition == ProfileConditionType.GreaterThanEqual) + { + item.AudioBitrate = Math.Max(num, item.AudioBitrate ?? num); + } + } + break; + } + case ProfileConditionValue.AudioChannels: + { + if (string.IsNullOrEmpty(qualifier)) + { + if (!enableNonQualifiedConditions) + { + continue; + } + } + else + { + if (!enableQualifiedConditions) + { + continue; + } + } + + int num; + if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num)) + { + if (condition.Condition == ProfileConditionType.Equals) + { + item.SetOption(qualifier, "audiochannels", num.ToString(CultureInfo.InvariantCulture)); + } + else if (condition.Condition == ProfileConditionType.LessThanEqual) + { + item.SetOption(qualifier, "audiochannels", Math.Min(num, item.GetTargetAudioChannels(qualifier) ?? num).ToString(CultureInfo.InvariantCulture)); + } + else if (condition.Condition == ProfileConditionType.GreaterThanEqual) + { + item.SetOption(qualifier, "audiochannels", Math.Max(num, item.GetTargetAudioChannels(qualifier) ?? num).ToString(CultureInfo.InvariantCulture)); + } + } + break; + } + case ProfileConditionValue.IsAvc: + { + if (!enableNonQualifiedConditions) + { + continue; + } + + bool isAvc; + if (bool.TryParse(value, out isAvc)) + { + if (isAvc && condition.Condition == ProfileConditionType.Equals) + { + item.RequireAvc = true; + } + else if (!isAvc && condition.Condition == ProfileConditionType.NotEquals) + { + item.RequireAvc = true; + } + } + break; + } + case ProfileConditionValue.IsAnamorphic: + { + if (!enableNonQualifiedConditions) + { + continue; + } + + bool isAnamorphic; + if (bool.TryParse(value, out isAnamorphic)) + { + if (isAnamorphic && condition.Condition == ProfileConditionType.Equals) + { + item.RequireNonAnamorphic = true; + } + else if (!isAnamorphic && condition.Condition == ProfileConditionType.NotEquals) + { + item.RequireNonAnamorphic = true; + } + } + break; + } + case ProfileConditionValue.IsInterlaced: + { + if (string.IsNullOrEmpty(qualifier)) + { + if (!enableNonQualifiedConditions) + { + continue; + } + } + else + { + if (!enableQualifiedConditions) + { + continue; + } + } + + bool isInterlaced; + if (bool.TryParse(value, out isInterlaced)) + { + if (!isInterlaced && condition.Condition == ProfileConditionType.Equals) + { + item.SetOption(qualifier, "deinterlace", "true"); + } + else if (isInterlaced && condition.Condition == ProfileConditionType.NotEquals) + { + item.SetOption(qualifier, "deinterlace", "true"); + } + } + break; + } + case ProfileConditionValue.AudioProfile: + case ProfileConditionValue.Has64BitOffsets: + case ProfileConditionValue.PacketLength: + case ProfileConditionValue.NumAudioStreams: + case ProfileConditionValue.NumVideoStreams: + case ProfileConditionValue.IsSecondaryAudio: + case ProfileConditionValue.VideoTimestamp: + { + // Not supported yet + break; + } + case ProfileConditionValue.RefFrames: + { + if (string.IsNullOrEmpty(qualifier)) + { + if (!enableNonQualifiedConditions) + { + continue; + } + } + else + { + if (!enableQualifiedConditions) + { + continue; + } + } + + int num; + if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num)) + { + if (condition.Condition == ProfileConditionType.Equals) + { + item.SetOption(qualifier, "maxrefframes", num.ToString(CultureInfo.InvariantCulture)); + } + else if (condition.Condition == ProfileConditionType.LessThanEqual) + { + item.SetOption(qualifier, "maxrefframes", Math.Min(num, item.GetTargetRefFrames(qualifier) ?? num).ToString(CultureInfo.InvariantCulture)); + } + else if (condition.Condition == ProfileConditionType.GreaterThanEqual) + { + item.SetOption(qualifier, "maxrefframes", Math.Max(num, item.GetTargetRefFrames(qualifier) ?? num).ToString(CultureInfo.InvariantCulture)); + } + } + break; + } + case ProfileConditionValue.VideoBitDepth: + { + if (string.IsNullOrEmpty(qualifier)) + { + if (!enableNonQualifiedConditions) + { + continue; + } + } + else + { + if (!enableQualifiedConditions) + { + continue; + } + } + + int num; + if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num)) + { + if (condition.Condition == ProfileConditionType.Equals) + { + item.SetOption(qualifier, "videobitdepth", num.ToString(CultureInfo.InvariantCulture)); + } + else if (condition.Condition == ProfileConditionType.LessThanEqual) + { + item.SetOption(qualifier, "videobitdepth", Math.Min(num, item.GetTargetVideoBitDepth(qualifier) ?? num).ToString(CultureInfo.InvariantCulture)); + } + else if (condition.Condition == ProfileConditionType.GreaterThanEqual) + { + item.SetOption(qualifier, "videobitdepth", Math.Max(num, item.GetTargetVideoBitDepth(qualifier) ?? num).ToString(CultureInfo.InvariantCulture)); + } + } + break; + } + case ProfileConditionValue.VideoProfile: + { + if (string.IsNullOrEmpty(qualifier)) + { + continue; + } + + if (!string.IsNullOrEmpty(value)) + { + // change from split by | to comma + + // strip spaces to avoid having to encode + var values = value + .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + + if (condition.Condition == ProfileConditionType.Equals || condition.Condition == ProfileConditionType.EqualsAny) + { + item.SetOption(qualifier, "profile", string.Join(",", values)); + } + } + break; + } + case ProfileConditionValue.Height: + { + if (!enableNonQualifiedConditions) + { + continue; + } + + int num; + if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num)) + { + if (condition.Condition == ProfileConditionType.Equals) + { + item.MaxHeight = num; + } + else if (condition.Condition == ProfileConditionType.LessThanEqual) + { + item.MaxHeight = Math.Min(num, item.MaxHeight ?? num); + } + else if (condition.Condition == ProfileConditionType.GreaterThanEqual) + { + item.MaxHeight = Math.Max(num, item.MaxHeight ?? num); + } + } + break; + } + case ProfileConditionValue.VideoBitrate: + { + if (!enableNonQualifiedConditions) + { + continue; + } + + int num; + if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num)) + { + if (condition.Condition == ProfileConditionType.Equals) + { + item.VideoBitrate = num; + } + else if (condition.Condition == ProfileConditionType.LessThanEqual) + { + item.VideoBitrate = Math.Min(num, item.VideoBitrate ?? num); + } + else if (condition.Condition == ProfileConditionType.GreaterThanEqual) + { + item.VideoBitrate = Math.Max(num, item.VideoBitrate ?? num); + } + } + break; + } + case ProfileConditionValue.VideoFramerate: + { + if (!enableNonQualifiedConditions) + { + continue; + } + + float num; + if (float.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num)) + { + if (condition.Condition == ProfileConditionType.Equals) + { + item.MaxFramerate = num; + } + else if (condition.Condition == ProfileConditionType.LessThanEqual) + { + item.MaxFramerate = Math.Min(num, item.MaxFramerate ?? num); + } + else if (condition.Condition == ProfileConditionType.GreaterThanEqual) + { + item.MaxFramerate = Math.Max(num, item.MaxFramerate ?? num); + } + } + break; + } + case ProfileConditionValue.VideoLevel: + { + if (string.IsNullOrEmpty(qualifier)) + { + continue; + } + + int num; + if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num)) + { + if (condition.Condition == ProfileConditionType.Equals) + { + item.SetOption(qualifier, "level", num.ToString(CultureInfo.InvariantCulture)); + } + else if (condition.Condition == ProfileConditionType.LessThanEqual) + { + item.SetOption(qualifier, "level", Math.Min(num, item.GetTargetVideoLevel(qualifier) ?? num).ToString(CultureInfo.InvariantCulture)); + } + else if (condition.Condition == ProfileConditionType.GreaterThanEqual) + { + item.SetOption(qualifier, "level", Math.Max(num, item.GetTargetVideoLevel(qualifier) ?? num).ToString(CultureInfo.InvariantCulture)); + } + } + break; + } + case ProfileConditionValue.Width: + { + if (!enableNonQualifiedConditions) + { + continue; + } + + int num; + if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num)) + { + if (condition.Condition == ProfileConditionType.Equals) + { + item.MaxWidth = num; + } + else if (condition.Condition == ProfileConditionType.LessThanEqual) + { + item.MaxWidth = Math.Min(num, item.MaxWidth ?? num); + } + else if (condition.Condition == ProfileConditionType.GreaterThanEqual) + { + item.MaxWidth = Math.Max(num, item.MaxWidth ?? num); + } + } + break; + } + default: + break; + } + } + } + + private bool IsAudioDirectPlaySupported(DirectPlayProfile profile, MediaSourceInfo item, MediaStream audioStream) + { + // Check container type + if (!profile.SupportsContainer(item.Container)) + { + return false; + } + + // Check audio codec + string audioCodec = audioStream == null ? null : audioStream.Codec; + if (!profile.SupportsAudioCodec(audioCodec)) + { + return false; + } + + return true; + } + + private bool IsVideoDirectPlaySupported(DirectPlayProfile profile, MediaSourceInfo item, MediaStream videoStream, MediaStream audioStream) + { + // Check container type + if (!profile.SupportsContainer(item.Container)) + { + return false; + } + + // Check video codec + string videoCodec = videoStream == null ? null : videoStream.Codec; + if (!profile.SupportsVideoCodec(videoCodec)) + { + return false; + } + + // Check audio codec + if (audioStream != null) + { + string audioCodec = audioStream == null ? null : audioStream.Codec; + if (!profile.SupportsAudioCodec(audioCodec)) + { + return false; + } + } + + return true; + } + } +} diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs new file mode 100644 index 000000000..46a1cd68b --- /dev/null +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -0,0 +1,1092 @@ +using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.MediaInfo; +using MediaBrowser.Model.Session; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace MediaBrowser.Model.Dlna +{ + /// <summary> + /// Class StreamInfo. + /// </summary> + public class StreamInfo + { + public StreamInfo() + { + AudioCodecs = new string[] { }; + VideoCodecs = new string[] { }; + SubtitleCodecs = new string[] { }; + TranscodeReasons = new TranscodeReason[] { }; + StreamOptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + } + + public void SetOption(string qualifier, string name, string value) + { + if (string.IsNullOrEmpty(qualifier)) + { + SetOption(name, value); + } + else + { + SetOption(qualifier + "-" + name, value); + } + } + + public void SetOption(string name, string value) + { + StreamOptions[name] = value; + } + + public string GetOption(string qualifier, string name) + { + var value = GetOption(qualifier + "-" + name); + + if (string.IsNullOrEmpty(value)) + { + value = GetOption(name); + } + + return value; + } + + public string GetOption(string name) + { + string value; + if (StreamOptions.TryGetValue(name, out value)) + { + return value; + } + + return null; + } + + public Guid ItemId { get; set; } + + public PlayMethod PlayMethod { get; set; } + public EncodingContext Context { get; set; } + + public DlnaProfileType MediaType { get; set; } + + public string Container { get; set; } + + public string SubProtocol { get; set; } + + public long StartPositionTicks { get; set; } + + public int? SegmentLength { get; set; } + public int? MinSegments { get; set; } + public bool BreakOnNonKeyFrames { get; set; } + + public bool RequireAvc { get; set; } + public bool RequireNonAnamorphic { get; set; } + public bool CopyTimestamps { get; set; } + public bool EnableMpegtsM2TsMode { get; set; } + public bool EnableSubtitlesInManifest { get; set; } + public string[] AudioCodecs { get; set; } + public string[] VideoCodecs { get; set; } + + public int? AudioStreamIndex { get; set; } + + public int? SubtitleStreamIndex { get; set; } + + public int? TranscodingMaxAudioChannels { get; set; } + public int? GlobalMaxAudioChannels { get; set; } + + public int? AudioBitrate { get; set; } + + public int? VideoBitrate { get; set; } + + public int? MaxWidth { get; set; } + public int? MaxHeight { get; set; } + + public float? MaxFramerate { get; set; } + + public DeviceProfile DeviceProfile { get; set; } + public string DeviceProfileId { get; set; } + public string DeviceId { get; set; } + + public long? RunTimeTicks { get; set; } + + public TranscodeSeekInfo TranscodeSeekInfo { get; set; } + + public bool EstimateContentLength { get; set; } + + public MediaSourceInfo MediaSource { get; set; } + + public string[] SubtitleCodecs { get; set; } + public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; } + public string SubtitleFormat { get; set; } + + public string PlaySessionId { get; set; } + public TranscodeReason[] TranscodeReasons { get; set; } + + public Dictionary<string, string> StreamOptions { get; private set; } + + public string MediaSourceId + { + get + { + return MediaSource == null ? null : MediaSource.Id; + } + } + + public bool IsDirectStream + { + get + { + return PlayMethod == PlayMethod.DirectStream || + PlayMethod == PlayMethod.DirectPlay; + } + } + + public string ToUrl(string baseUrl, string accessToken) + { + if (PlayMethod == PlayMethod.DirectPlay) + { + return MediaSource.Path; + } + + if (string.IsNullOrEmpty(baseUrl)) + { + throw new ArgumentNullException(baseUrl); + } + + List<string> list = new List<string>(); + foreach (NameValuePair pair in BuildParams(this, accessToken)) + { + if (string.IsNullOrEmpty(pair.Value)) + { + continue; + } + + // Try to keep the url clean by omitting defaults + if (StringHelper.EqualsIgnoreCase(pair.Name, "StartTimeTicks") && + StringHelper.EqualsIgnoreCase(pair.Value, "0")) + { + continue; + } + if (StringHelper.EqualsIgnoreCase(pair.Name, "SubtitleStreamIndex") && + StringHelper.EqualsIgnoreCase(pair.Value, "-1")) + { + continue; + } + if (StringHelper.EqualsIgnoreCase(pair.Name, "Static") && + StringHelper.EqualsIgnoreCase(pair.Value, "false")) + { + continue; + } + + var encodedValue = pair.Value.Replace(" ", "%20"); + + list.Add(string.Format("{0}={1}", pair.Name, encodedValue)); + } + + string queryString = string.Join("&", list.ToArray(list.Count)); + + return GetUrl(baseUrl, queryString); + } + + private string GetUrl(string baseUrl, string queryString) + { + if (string.IsNullOrEmpty(baseUrl)) + { + throw new ArgumentNullException(baseUrl); + } + + string extension = string.IsNullOrEmpty(Container) ? string.Empty : "." + Container; + + baseUrl = baseUrl.TrimEnd('/'); + + if (MediaType == DlnaProfileType.Audio) + { + if (StringHelper.EqualsIgnoreCase(SubProtocol, "hls")) + { + return string.Format("{0}/audio/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString); + } + + return string.Format("{0}/audio/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); + } + + if (StringHelper.EqualsIgnoreCase(SubProtocol, "hls")) + { + return string.Format("{0}/videos/{1}/master.m3u8?{2}", baseUrl, ItemId, queryString); + } + + return string.Format("{0}/videos/{1}/stream{2}?{3}", baseUrl, ItemId, extension, queryString); + } + + private static List<NameValuePair> BuildParams(StreamInfo item, string accessToken) + { + List<NameValuePair> list = new List<NameValuePair>(); + + string audioCodecs = item.AudioCodecs.Length == 0 ? + string.Empty : + string.Join(",", item.AudioCodecs); + + string videoCodecs = item.VideoCodecs.Length == 0 ? + string.Empty : + string.Join(",", item.VideoCodecs); + + list.Add(new NameValuePair("DeviceProfileId", item.DeviceProfileId ?? string.Empty)); + list.Add(new NameValuePair("DeviceId", item.DeviceId ?? string.Empty)); + list.Add(new NameValuePair("MediaSourceId", item.MediaSourceId ?? string.Empty)); + list.Add(new NameValuePair("Static", item.IsDirectStream.ToString().ToLower())); + list.Add(new NameValuePair("VideoCodec", videoCodecs)); + list.Add(new NameValuePair("AudioCodec", audioCodecs)); + list.Add(new NameValuePair("AudioStreamIndex", item.AudioStreamIndex.HasValue ? item.AudioStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); + list.Add(new NameValuePair("SubtitleStreamIndex", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleStreamIndex.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); + list.Add(new NameValuePair("VideoBitrate", item.VideoBitrate.HasValue ? item.VideoBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); + list.Add(new NameValuePair("AudioBitrate", item.AudioBitrate.HasValue ? item.AudioBitrate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); + + list.Add(new NameValuePair("MaxFramerate", item.MaxFramerate.HasValue ? item.MaxFramerate.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); + list.Add(new NameValuePair("MaxWidth", item.MaxWidth.HasValue ? item.MaxWidth.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); + list.Add(new NameValuePair("MaxHeight", item.MaxHeight.HasValue ? item.MaxHeight.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); + + long startPositionTicks = item.StartPositionTicks; + + var isHls = StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls"); + + if (isHls) + { + list.Add(new NameValuePair("StartTimeTicks", string.Empty)); + } + else + { + list.Add(new NameValuePair("StartTimeTicks", startPositionTicks.ToString(CultureInfo.InvariantCulture))); + } + + list.Add(new NameValuePair("PlaySessionId", item.PlaySessionId ?? string.Empty)); + list.Add(new NameValuePair("api_key", accessToken ?? string.Empty)); + + string liveStreamId = item.MediaSource == null ? null : item.MediaSource.LiveStreamId; + list.Add(new NameValuePair("LiveStreamId", liveStreamId ?? string.Empty)); + + list.Add(new NameValuePair("SubtitleMethod", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? item.SubtitleDeliveryMethod.ToString() : string.Empty)); + + + if (!item.IsDirectStream) + { + if (item.RequireNonAnamorphic) + { + list.Add(new NameValuePair("RequireNonAnamorphic", item.RequireNonAnamorphic.ToString().ToLower())); + } + + list.Add(new NameValuePair("TranscodingMaxAudioChannels", item.TranscodingMaxAudioChannels.HasValue ? item.TranscodingMaxAudioChannels.Value.ToString(CultureInfo.InvariantCulture) : string.Empty)); + + if (item.EnableSubtitlesInManifest) + { + list.Add(new NameValuePair("EnableSubtitlesInManifest", item.EnableSubtitlesInManifest.ToString().ToLower())); + } + + if (item.EnableMpegtsM2TsMode) + { + list.Add(new NameValuePair("EnableMpegtsM2TsMode", item.EnableMpegtsM2TsMode.ToString().ToLower())); + } + + if (item.EstimateContentLength) + { + list.Add(new NameValuePair("EstimateContentLength", item.EstimateContentLength.ToString().ToLower())); + } + + if (item.TranscodeSeekInfo != TranscodeSeekInfo.Auto) + { + list.Add(new NameValuePair("TranscodeSeekInfo", item.TranscodeSeekInfo.ToString().ToLower())); + } + + if (item.CopyTimestamps) + { + list.Add(new NameValuePair("CopyTimestamps", item.CopyTimestamps.ToString().ToLower())); + } + + list.Add(new NameValuePair("RequireAvc", item.RequireAvc.ToString().ToLower())); + } + + list.Add(new NameValuePair("Tag", item.MediaSource.ETag ?? string.Empty)); + + string subtitleCodecs = item.SubtitleCodecs.Length == 0 ? + string.Empty : + string.Join(",", item.SubtitleCodecs); + + list.Add(new NameValuePair("SubtitleCodec", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed ? subtitleCodecs : string.Empty)); + + if (isHls) + { + list.Add(new NameValuePair("SegmentContainer", item.Container ?? string.Empty)); + + if (item.SegmentLength.HasValue) + { + list.Add(new NameValuePair("SegmentLength", item.SegmentLength.Value.ToString(CultureInfo.InvariantCulture))); + } + + if (item.MinSegments.HasValue) + { + list.Add(new NameValuePair("MinSegments", item.MinSegments.Value.ToString(CultureInfo.InvariantCulture))); + } + + list.Add(new NameValuePair("BreakOnNonKeyFrames", item.BreakOnNonKeyFrames.ToString())); + } + + foreach (var pair in item.StreamOptions) + { + if (string.IsNullOrEmpty(pair.Value)) + { + continue; + } + + // strip spaces to avoid having to encode h264 profile names + list.Add(new NameValuePair(pair.Key, pair.Value.Replace(" ", ""))); + } + + if (!item.IsDirectStream) + { + list.Add(new NameValuePair("TranscodeReasons", string.Join(",", item.TranscodeReasons.Distinct().Select(i => i.ToString()).ToArray()))); + } + + return list; + } + + public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken) + { + return GetExternalSubtitles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken); + } + + public List<SubtitleStreamInfo> GetExternalSubtitles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken) + { + List<SubtitleStreamInfo> list = GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, enableAllProfiles, baseUrl, accessToken); + List<SubtitleStreamInfo> newList = new List<SubtitleStreamInfo>(); + + // First add the selected track + foreach (SubtitleStreamInfo stream in list) + { + if (stream.DeliveryMethod == SubtitleDeliveryMethod.External) + { + newList.Add(stream); + } + } + + return newList; + } + + public List<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, string baseUrl, string accessToken) + { + return GetSubtitleProfiles(transcoderSupport, includeSelectedTrackOnly, false, baseUrl, accessToken); + } + + public List<SubtitleStreamInfo> GetSubtitleProfiles(ITranscoderSupport transcoderSupport, bool includeSelectedTrackOnly, bool enableAllProfiles, string baseUrl, string accessToken) + { + List<SubtitleStreamInfo> list = new List<SubtitleStreamInfo>(); + + // HLS will preserve timestamps so we can just grab the full subtitle stream + long startPositionTicks = StringHelper.EqualsIgnoreCase(SubProtocol, "hls") + ? 0 + : (PlayMethod == PlayMethod.Transcode && !CopyTimestamps ? StartPositionTicks : 0); + + // First add the selected track + if (SubtitleStreamIndex.HasValue) + { + foreach (MediaStream stream in MediaSource.MediaStreams) + { + if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value) + { + AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks); + } + } + } + + if (!includeSelectedTrackOnly) + { + foreach (MediaStream stream in MediaSource.MediaStreams) + { + if (stream.Type == MediaStreamType.Subtitle && (!SubtitleStreamIndex.HasValue || stream.Index != SubtitleStreamIndex.Value)) + { + AddSubtitleProfiles(list, stream, transcoderSupport, enableAllProfiles, baseUrl, accessToken, startPositionTicks); + } + } + } + + return list; + } + + private void AddSubtitleProfiles(List<SubtitleStreamInfo> list, MediaStream stream, ITranscoderSupport transcoderSupport, bool enableAllProfiles, string baseUrl, string accessToken, long startPositionTicks) + { + if (enableAllProfiles) + { + foreach (SubtitleProfile profile in DeviceProfile.SubtitleProfiles) + { + SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, new[] { profile }, transcoderSupport); + + list.Add(info); + } + } + else + { + SubtitleStreamInfo info = GetSubtitleStreamInfo(stream, baseUrl, accessToken, startPositionTicks, DeviceProfile.SubtitleProfiles, transcoderSupport); + + list.Add(info); + } + } + + private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream, string baseUrl, string accessToken, long startPositionTicks, SubtitleProfile[] subtitleProfiles, ITranscoderSupport transcoderSupport) + { + SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(MediaSource, stream, subtitleProfiles, PlayMethod, transcoderSupport, Container, SubProtocol); + SubtitleStreamInfo info = new SubtitleStreamInfo + { + IsForced = stream.IsForced, + Language = stream.Language, + Name = stream.Language ?? "Unknown", + Format = subtitleProfile.Format, + Index = stream.Index, + DeliveryMethod = subtitleProfile.Method, + DisplayTitle = stream.DisplayTitle + }; + + if (info.DeliveryMethod == SubtitleDeliveryMethod.External) + { + if (MediaSource.Protocol == MediaProtocol.File || !StringHelper.EqualsIgnoreCase(stream.Codec, subtitleProfile.Format) || !stream.IsExternal) + { + info.Url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}", + baseUrl, + ItemId, + MediaSourceId, + stream.Index.ToString(CultureInfo.InvariantCulture), + startPositionTicks.ToString(CultureInfo.InvariantCulture), + subtitleProfile.Format); + + if (!string.IsNullOrEmpty(accessToken)) + { + info.Url += "?api_key=" + accessToken; + } + + info.IsExternalUrl = false; + } + else + { + info.Url = stream.Path; + info.IsExternalUrl = true; + } + } + + return info; + } + + /// <summary> + /// Returns the audio stream that will be used + /// </summary> + public MediaStream TargetAudioStream + { + get + { + if (MediaSource != null) + { + return MediaSource.GetDefaultAudioStream(AudioStreamIndex); + } + + return null; + } + } + + /// <summary> + /// Returns the video stream that will be used + /// </summary> + public MediaStream TargetVideoStream + { + get + { + if (MediaSource != null) + { + return MediaSource.VideoStream; + } + + return null; + } + } + + /// <summary> + /// Predicts the audio sample rate that will be in the output stream + /// </summary> + public int? TargetAudioSampleRate + { + get + { + MediaStream stream = TargetAudioStream; + return stream == null ? null : stream.SampleRate; + } + } + + /// <summary> + /// Predicts the audio sample rate that will be in the output stream + /// </summary> + public int? TargetAudioBitDepth + { + get + { + if (IsDirectStream) + { + return TargetAudioStream == null ? (int?)null : TargetAudioStream.BitDepth; + } + + var targetAudioCodecs = TargetAudioCodec; + var audioCodec = targetAudioCodecs.Length == 0 ? null : targetAudioCodecs[0]; + if (!string.IsNullOrEmpty(audioCodec)) + { + return GetTargetAudioBitDepth(audioCodec); + } + + return TargetAudioStream == null ? (int?)null : TargetAudioStream.BitDepth; + } + } + + /// <summary> + /// Predicts the audio sample rate that will be in the output stream + /// </summary> + public int? TargetVideoBitDepth + { + get + { + if (IsDirectStream) + { + return TargetVideoStream == null ? (int?)null : TargetVideoStream.BitDepth; + } + + var targetVideoCodecs = TargetVideoCodec; + var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; + if (!string.IsNullOrEmpty(videoCodec)) + { + return GetTargetVideoBitDepth(videoCodec); + } + + return TargetVideoStream == null ? (int?)null : TargetVideoStream.BitDepth; + } + } + + /// <summary> + /// Gets the target reference frames. + /// </summary> + /// <value>The target reference frames.</value> + public int? TargetRefFrames + { + get + { + if (IsDirectStream) + { + return TargetVideoStream == null ? (int?)null : TargetVideoStream.RefFrames; + } + + var targetVideoCodecs = TargetVideoCodec; + var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; + if (!string.IsNullOrEmpty(videoCodec)) + { + return GetTargetRefFrames(videoCodec); + } + + return TargetVideoStream == null ? (int?)null : TargetVideoStream.RefFrames; + } + } + + /// <summary> + /// Predicts the audio sample rate that will be in the output stream + /// </summary> + public float? TargetFramerate + { + get + { + MediaStream stream = TargetVideoStream; + return MaxFramerate.HasValue && !IsDirectStream + ? MaxFramerate + : stream == null ? null : stream.AverageFrameRate ?? stream.RealFrameRate; + } + } + + /// <summary> + /// Predicts the audio sample rate that will be in the output stream + /// </summary> + public double? TargetVideoLevel + { + get + { + if (IsDirectStream) + { + return TargetVideoStream == null ? (double?)null : TargetVideoStream.Level; + } + + var targetVideoCodecs = TargetVideoCodec; + var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; + if (!string.IsNullOrEmpty(videoCodec)) + { + return GetTargetVideoLevel(videoCodec); + } + + return TargetVideoStream == null ? (double?)null : TargetVideoStream.Level; + } + } + + public int? GetTargetVideoBitDepth(string codec) + { + var value = GetOption(codec, "videobitdepth"); + if (string.IsNullOrEmpty(value)) + { + return null; + } + + int result; + if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result)) + { + return result; + } + + return null; + } + + public int? GetTargetAudioBitDepth(string codec) + { + var value = GetOption(codec, "audiobitdepth"); + if (string.IsNullOrEmpty(value)) + { + return null; + } + + int result; + if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result)) + { + return result; + } + + return null; + } + + public double? GetTargetVideoLevel(string codec) + { + var value = GetOption(codec, "level"); + if (string.IsNullOrEmpty(value)) + { + return null; + } + + double result; + if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out result)) + { + return result; + } + + return null; + } + + public int? GetTargetRefFrames(string codec) + { + var value = GetOption(codec, "maxrefframes"); + if (string.IsNullOrEmpty(value)) + { + return null; + } + + int result; + if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out result)) + { + return result; + } + + return null; + } + + /// <summary> + /// Predicts the audio sample rate that will be in the output stream + /// </summary> + public int? TargetPacketLength + { + get + { + MediaStream stream = TargetVideoStream; + return !IsDirectStream + ? null + : stream == null ? null : stream.PacketLength; + } + } + + /// <summary> + /// Predicts the audio sample rate that will be in the output stream + /// </summary> + public string TargetVideoProfile + { + get + { + if (IsDirectStream) + { + return TargetVideoStream == null ? null : TargetVideoStream.Profile; + } + + var targetVideoCodecs = TargetVideoCodec; + var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; + if (!string.IsNullOrEmpty(videoCodec)) + { + return GetOption(videoCodec, "profile"); + } + + return TargetVideoStream == null ? null : TargetVideoStream.Profile; + } + } + + /// <summary> + /// Gets the target video codec tag. + /// </summary> + /// <value>The target video codec tag.</value> + public string TargetVideoCodecTag + { + get + { + MediaStream stream = TargetVideoStream; + return !IsDirectStream + ? null + : stream == null ? null : stream.CodecTag; + } + } + + /// <summary> + /// Predicts the audio bitrate that will be in the output stream + /// </summary> + public int? TargetAudioBitrate + { + get + { + MediaStream stream = TargetAudioStream; + return AudioBitrate.HasValue && !IsDirectStream + ? AudioBitrate + : stream == null ? null : stream.BitRate; + } + } + + /// <summary> + /// Predicts the audio channels that will be in the output stream + /// </summary> + public int? TargetAudioChannels + { + get + { + if (IsDirectStream) + { + return TargetAudioStream == null ? (int?)null : TargetAudioStream.Channels; + } + + var targetAudioCodecs = TargetAudioCodec; + var codec = targetAudioCodecs.Length == 0 ? null : targetAudioCodecs[0]; + if (!string.IsNullOrEmpty(codec)) + { + return GetTargetRefFrames(codec); + } + + return TargetAudioStream == null ? (int?)null : TargetAudioStream.Channels; + } + } + + public int? GetTargetAudioChannels(string codec) + { + var defaultValue = GlobalMaxAudioChannels; + + var value = GetOption(codec, "audiochannels"); + if (string.IsNullOrEmpty(value)) + { + return defaultValue; + } + + int result; + if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result)) + { + return Math.Min(result, defaultValue ?? result); + } + + return defaultValue; + } + + /// <summary> + /// Predicts the audio codec that will be in the output stream + /// </summary> + public string[] TargetAudioCodec + { + get + { + MediaStream stream = TargetAudioStream; + + string inputCodec = stream == null ? null : stream.Codec; + + if (IsDirectStream) + { + return string.IsNullOrEmpty(inputCodec) ? new string[] { } : new[] { inputCodec }; + } + + foreach (string codec in AudioCodecs) + { + if (StringHelper.EqualsIgnoreCase(codec, inputCodec)) + { + return string.IsNullOrEmpty(codec) ? new string[] { } : new[] { codec }; + } + } + + return AudioCodecs; + } + } + + public string[] TargetVideoCodec + { + get + { + MediaStream stream = TargetVideoStream; + + string inputCodec = stream == null ? null : stream.Codec; + + if (IsDirectStream) + { + return string.IsNullOrEmpty(inputCodec) ? new string[] { } : new[] { inputCodec }; + } + + foreach (string codec in VideoCodecs) + { + if (StringHelper.EqualsIgnoreCase(codec, inputCodec)) + { + return string.IsNullOrEmpty(codec) ? new string[] { } : new[] { codec }; + } + } + + return VideoCodecs; + } + } + + /// <summary> + /// Predicts the audio channels that will be in the output stream + /// </summary> + public long? TargetSize + { + get + { + if (IsDirectStream) + { + return MediaSource.Size; + } + + if (RunTimeTicks.HasValue) + { + int? totalBitrate = TargetTotalBitrate; + + double totalSeconds = RunTimeTicks.Value; + // Convert to ms + totalSeconds /= 10000; + // Convert to seconds + totalSeconds /= 1000; + + return totalBitrate.HasValue ? + Convert.ToInt64(totalBitrate.Value * totalSeconds) : + (long?)null; + } + + return null; + } + } + + public int? TargetVideoBitrate + { + get + { + MediaStream stream = TargetVideoStream; + + return VideoBitrate.HasValue && !IsDirectStream + ? VideoBitrate + : stream == null ? null : stream.BitRate; + } + } + + public TransportStreamTimestamp TargetTimestamp + { + get + { + TransportStreamTimestamp defaultValue = StringHelper.EqualsIgnoreCase(Container, "m2ts") + ? TransportStreamTimestamp.Valid + : TransportStreamTimestamp.None; + + return !IsDirectStream + ? defaultValue + : MediaSource == null ? defaultValue : MediaSource.Timestamp ?? TransportStreamTimestamp.None; + } + } + + public int? TargetTotalBitrate + { + get + { + return (TargetAudioBitrate ?? 0) + (TargetVideoBitrate ?? 0); + } + } + + public bool? IsTargetAnamorphic + { + get + { + if (IsDirectStream) + { + return TargetVideoStream == null ? null : TargetVideoStream.IsAnamorphic; + } + + return false; + } + } + + public bool? IsTargetInterlaced + { + get + { + if (IsDirectStream) + { + return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced; + } + + var targetVideoCodecs = TargetVideoCodec; + var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0]; + if (!string.IsNullOrEmpty(videoCodec)) + { + if (string.Equals(GetOption(videoCodec, "deinterlace"), "true", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + } + + return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced; + } + } + + public bool? IsTargetAVC + { + get + { + if (IsDirectStream) + { + return TargetVideoStream == null ? null : TargetVideoStream.IsAVC; + } + + return true; + } + } + + public int? TargetWidth + { + get + { + MediaStream videoStream = TargetVideoStream; + + if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue) + { + ImageSize size = new ImageSize + { + Width = videoStream.Width.Value, + Height = videoStream.Height.Value + }; + + double? maxWidth = MaxWidth.HasValue ? (double)MaxWidth.Value : (double?)null; + double? maxHeight = MaxHeight.HasValue ? (double)MaxHeight.Value : (double?)null; + + ImageSize newSize = DrawingUtils.Resize(size, + 0, + 0, + maxWidth ?? 0, + maxHeight ?? 0); + + return Convert.ToInt32(newSize.Width); + } + + return MaxWidth; + } + } + + public int? TargetHeight + { + get + { + MediaStream videoStream = TargetVideoStream; + + if (videoStream != null && videoStream.Width.HasValue && videoStream.Height.HasValue) + { + ImageSize size = new ImageSize + { + Width = videoStream.Width.Value, + Height = videoStream.Height.Value + }; + + double? maxWidth = MaxWidth.HasValue ? (double)MaxWidth.Value : (double?)null; + double? maxHeight = MaxHeight.HasValue ? (double)MaxHeight.Value : (double?)null; + + ImageSize newSize = DrawingUtils.Resize(size, + 0, + 0, + maxWidth ?? 0, + maxHeight ?? 0); + + return Convert.ToInt32(newSize.Height); + } + + return MaxHeight; + } + } + + public int? TargetVideoStreamCount + { + get + { + if (IsDirectStream) + { + return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue); + } + return GetMediaStreamCount(MediaStreamType.Video, 1); + } + } + + public int? TargetAudioStreamCount + { + get + { + if (IsDirectStream) + { + return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue); + } + return GetMediaStreamCount(MediaStreamType.Audio, 1); + } + } + + private int? GetMediaStreamCount(MediaStreamType type, int limit) + { + var count = MediaSource.GetStreamCount(type); + + if (count.HasValue) + { + count = Math.Min(count.Value, limit); + } + + return count; + } + + public List<MediaStream> GetSelectableAudioStreams() + { + return GetSelectableStreams(MediaStreamType.Audio); + } + + public List<MediaStream> GetSelectableSubtitleStreams() + { + return GetSelectableStreams(MediaStreamType.Subtitle); + } + + public List<MediaStream> GetSelectableStreams(MediaStreamType type) + { + List<MediaStream> list = new List<MediaStream>(); + + foreach (MediaStream stream in MediaSource.MediaStreams) + { + if (type == stream.Type) + { + list.Add(stream); + } + } + + return list; + } + } +} diff --git a/MediaBrowser.Model/Dlna/SubtitleDeliveryMethod.cs b/MediaBrowser.Model/Dlna/SubtitleDeliveryMethod.cs new file mode 100644 index 000000000..b4e13c5ba --- /dev/null +++ b/MediaBrowser.Model/Dlna/SubtitleDeliveryMethod.cs @@ -0,0 +1,22 @@ +namespace MediaBrowser.Model.Dlna +{ + public enum SubtitleDeliveryMethod + { + /// <summary> + /// The encode + /// </summary> + Encode = 0, + /// <summary> + /// The embed + /// </summary> + Embed = 1, + /// <summary> + /// The external + /// </summary> + External = 2, + /// <summary> + /// The HLS + /// </summary> + Hls = 3 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/SubtitleProfile.cs b/MediaBrowser.Model/Dlna/SubtitleProfile.cs new file mode 100644 index 000000000..a7e61e69a --- /dev/null +++ b/MediaBrowser.Model/Dlna/SubtitleProfile.cs @@ -0,0 +1,46 @@ +using MediaBrowser.Model.Extensions; +using System.Collections.Generic; +using System.Xml.Serialization; +using MediaBrowser.Model.Dlna; + +namespace MediaBrowser.Model.Dlna +{ + public class SubtitleProfile + { + [XmlAttribute("format")] + public string Format { get; set; } + + [XmlAttribute("method")] + public SubtitleDeliveryMethod Method { get; set; } + + [XmlAttribute("didlMode")] + public string DidlMode { get; set; } + + [XmlAttribute("language")] + public string Language { get; set; } + + [XmlAttribute("container")] + public string Container { get; set; } + + public string[] GetLanguages() + { + return ContainerProfile.SplitValue(Language); + } + + public bool SupportsLanguage(string subLanguage) + { + if (string.IsNullOrEmpty(Language)) + { + return true; + } + + if (string.IsNullOrEmpty(subLanguage)) + { + subLanguage = "und"; + } + + var languages = GetLanguages(); + return languages.Length == 0 || ListHelper.ContainsIgnoreCase(languages, subLanguage); + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs b/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs new file mode 100644 index 000000000..7a89308dc --- /dev/null +++ b/MediaBrowser.Model/Dlna/SubtitleStreamInfo.cs @@ -0,0 +1,15 @@ +namespace MediaBrowser.Model.Dlna +{ + public class SubtitleStreamInfo + { + public string Url { get; set; } + public string Language { get; set; } + public string Name { get; set; } + public bool IsForced { get; set; } + public string Format { get; set; } + public string DisplayTitle { get; set; } + public int Index { get; set; } + public SubtitleDeliveryMethod DeliveryMethod { get; set; } + public bool IsExternalUrl { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs b/MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs new file mode 100644 index 000000000..564ce5c60 --- /dev/null +++ b/MediaBrowser.Model/Dlna/TranscodeSeekInfo.cs @@ -0,0 +1,8 @@ +namespace MediaBrowser.Model.Dlna +{ + public enum TranscodeSeekInfo + { + Auto = 0, + Bytes = 1 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/TranscodingProfile.cs b/MediaBrowser.Model/Dlna/TranscodingProfile.cs new file mode 100644 index 000000000..8453fdf6d --- /dev/null +++ b/MediaBrowser.Model/Dlna/TranscodingProfile.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using System.Xml.Serialization; +using MediaBrowser.Model.Dlna; + +namespace MediaBrowser.Model.Dlna +{ + public class TranscodingProfile + { + [XmlAttribute("container")] + public string Container { get; set; } + + [XmlAttribute("type")] + public DlnaProfileType Type { get; set; } + + [XmlAttribute("videoCodec")] + public string VideoCodec { get; set; } + + [XmlAttribute("audioCodec")] + public string AudioCodec { get; set; } + + [XmlAttribute("protocol")] + public string Protocol { get; set; } + + [XmlAttribute("estimateContentLength")] + public bool EstimateContentLength { get; set; } + + [XmlAttribute("enableMpegtsM2TsMode")] + public bool EnableMpegtsM2TsMode { get; set; } + + [XmlAttribute("transcodeSeekInfo")] + public TranscodeSeekInfo TranscodeSeekInfo { get; set; } + + [XmlAttribute("copyTimestamps")] + public bool CopyTimestamps { get; set; } + + [XmlAttribute("context")] + public EncodingContext Context { get; set; } + + [XmlAttribute("enableSubtitlesInManifest")] + public bool EnableSubtitlesInManifest { get; set; } + + [XmlAttribute("maxAudioChannels")] + public string MaxAudioChannels { get; set; } + + [XmlAttribute("minSegments")] + public int MinSegments { get; set; } + + [XmlAttribute("segmentLength")] + public int SegmentLength { get; set; } + + [XmlAttribute("breakOnNonKeyFrames")] + public bool BreakOnNonKeyFrames { get; set; } + + public string[] GetAudioCodecs() + { + return ContainerProfile.SplitValue(AudioCodec); + } + } +} diff --git a/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs b/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs new file mode 100644 index 000000000..f4b9d1e9b --- /dev/null +++ b/MediaBrowser.Model/Dlna/UpnpDeviceInfo.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using MediaBrowser.Model.Net; + +namespace MediaBrowser.Model.Dlna +{ + public class UpnpDeviceInfo + { + public Uri Location { get; set; } + public Dictionary<string, string> Headers { get; set; } + public IpAddressInfo LocalIpAddress { get; set; } + public int LocalPort { get; set; } + } +} diff --git a/MediaBrowser.Model/Dlna/VideoOptions.cs b/MediaBrowser.Model/Dlna/VideoOptions.cs new file mode 100644 index 000000000..041d2cd5d --- /dev/null +++ b/MediaBrowser.Model/Dlna/VideoOptions.cs @@ -0,0 +1,11 @@ +namespace MediaBrowser.Model.Dlna +{ + /// <summary> + /// Class VideoOptions. + /// </summary> + public class VideoOptions : AudioOptions + { + public int? AudioStreamIndex { get; set; } + public int? SubtitleStreamIndex { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/XmlAttribute.cs b/MediaBrowser.Model/Dlna/XmlAttribute.cs new file mode 100644 index 000000000..e8e13ba0d --- /dev/null +++ b/MediaBrowser.Model/Dlna/XmlAttribute.cs @@ -0,0 +1,13 @@ +using System.Xml.Serialization; + +namespace MediaBrowser.Model.Dlna +{ + public class XmlAttribute + { + [XmlAttribute("name")] + public string Name { get; set; } + + [XmlAttribute("value")] + public string Value { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Drawing/DrawingUtils.cs b/MediaBrowser.Model/Drawing/DrawingUtils.cs new file mode 100644 index 000000000..e6235cb06 --- /dev/null +++ b/MediaBrowser.Model/Drawing/DrawingUtils.cs @@ -0,0 +1,91 @@ +namespace MediaBrowser.Model.Drawing +{ + /// <summary> + /// Class DrawingUtils + /// </summary> + public static class DrawingUtils + { + /// <summary> + /// Resizes a set of dimensions + /// </summary> + /// <param name="size">The original size object</param> + /// <param name="width">A new fixed width, if desired</param> + /// <param name="height">A new fixed height, if desired</param> + /// <param name="maxWidth">A max fixed width, if desired</param> + /// <param name="maxHeight">A max fixed height, if desired</param> + /// <returns>A new size object</returns> + public static ImageSize Resize(ImageSize size, + double width, + double height, + double maxWidth, + double maxHeight) + { + double newWidth = size.Width; + double newHeight = size.Height; + + if (width > 0 && height > 0) + { + newWidth = width; + newHeight = height; + } + + else if (height > 0) + { + newWidth = GetNewWidth(newHeight, newWidth, height); + newHeight = height; + } + + else if (width > 0) + { + newHeight = GetNewHeight(newHeight, newWidth, width); + newWidth = width; + } + + if (maxHeight > 0 && maxHeight < newHeight) + { + newWidth = GetNewWidth(newHeight, newWidth, maxHeight); + newHeight = maxHeight; + } + + if (maxWidth > 0 && maxWidth < newWidth) + { + newHeight = GetNewHeight(newHeight, newWidth, maxWidth); + newWidth = maxWidth; + } + + return new ImageSize { Width = newWidth, Height = newHeight }; + } + + /// <summary> + /// Gets the new width. + /// </summary> + /// <param name="currentHeight">Height of the current.</param> + /// <param name="currentWidth">Width of the current.</param> + /// <param name="newHeight">The new height.</param> + /// <returns>System.Double.</returns> + private static double GetNewWidth(double currentHeight, double currentWidth, double newHeight) + { + double scaleFactor = newHeight; + scaleFactor /= currentHeight; + scaleFactor *= currentWidth; + + return scaleFactor; + } + + /// <summary> + /// Gets the new height. + /// </summary> + /// <param name="currentHeight">Height of the current.</param> + /// <param name="currentWidth">Width of the current.</param> + /// <param name="newWidth">The new width.</param> + /// <returns>System.Double.</returns> + private static double GetNewHeight(double currentHeight, double currentWidth, double newWidth) + { + double scaleFactor = newWidth; + scaleFactor /= currentWidth; + scaleFactor *= currentHeight; + + return scaleFactor; + } + } +} diff --git a/MediaBrowser.Model/Drawing/ImageFormat.cs b/MediaBrowser.Model/Drawing/ImageFormat.cs new file mode 100644 index 000000000..0172c9754 --- /dev/null +++ b/MediaBrowser.Model/Drawing/ImageFormat.cs @@ -0,0 +1,30 @@ + +namespace MediaBrowser.Model.Drawing +{ + /// <summary> + /// Enum ImageOutputFormat + /// </summary> + public enum ImageFormat + { + /// <summary> + /// The BMP + /// </summary> + Bmp, + /// <summary> + /// The GIF + /// </summary> + Gif, + /// <summary> + /// The JPG + /// </summary> + Jpg, + /// <summary> + /// The PNG + /// </summary> + Png, + /// <summary> + /// The webp + /// </summary> + Webp + } +} diff --git a/MediaBrowser.Model/Drawing/ImageOrientation.cs b/MediaBrowser.Model/Drawing/ImageOrientation.cs new file mode 100644 index 000000000..c320a8224 --- /dev/null +++ b/MediaBrowser.Model/Drawing/ImageOrientation.cs @@ -0,0 +1,15 @@ + +namespace MediaBrowser.Model.Drawing +{ + public enum ImageOrientation + { + TopLeft = 1, + TopRight = 2, + BottomRight = 3, + BottomLeft = 4, + LeftTop = 5, + RightTop = 6, + RightBottom = 7, + LeftBottom = 8, + } +} diff --git a/MediaBrowser.Model/Drawing/ImageSize.cs b/MediaBrowser.Model/Drawing/ImageSize.cs new file mode 100644 index 000000000..c2b0291bd --- /dev/null +++ b/MediaBrowser.Model/Drawing/ImageSize.cs @@ -0,0 +1,93 @@ +using System.Globalization; + +namespace MediaBrowser.Model.Drawing +{ + /// <summary> + /// Struct ImageSize + /// </summary> + public struct ImageSize + { + private double _height; + private double _width; + + /// <summary> + /// Gets or sets the height. + /// </summary> + /// <value>The height.</value> + public double Height + { + get + { + return _height; + } + set + { + _height = value; + } + } + + /// <summary> + /// Gets or sets the width. + /// </summary> + /// <value>The width.</value> + public double Width + { + get { return _width; } + set { _width = value; } + } + + public bool Equals(ImageSize size) + { + return Width.Equals(size.Width) && Height.Equals(size.Height); + } + + public override string ToString() + { + return string.Format("{0}-{1}", Width, Height); + } + + public ImageSize(string value) + { + _width = 0; + + _height = 0; + + ParseValue(value); + } + + public ImageSize(int width, int height) + { + _width = width; + _height = height; + } + + public ImageSize(double width, double height) + { + _width = width; + _height = height; + } + + private void ParseValue(string value) + { + if (!string.IsNullOrEmpty(value)) + { + string[] parts = value.Split('-'); + + if (parts.Length == 2) + { + double val; + + if (double.TryParse(parts[0], NumberStyles.Any, CultureInfo.InvariantCulture, out val)) + { + _width = val; + } + + if (double.TryParse(parts[1], NumberStyles.Any, CultureInfo.InvariantCulture, out val)) + { + _height = val; + } + } + } + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs new file mode 100644 index 000000000..3ddbc4150 --- /dev/null +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -0,0 +1,791 @@ +using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.Library; +using MediaBrowser.Model.LiveTv; +using MediaBrowser.Model.Providers; +using MediaBrowser.Model.Sync; +using System; +using System.Collections.Generic; +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Model.Dto +{ + /// <summary> + /// This is strictly used as a data transfer object from the api layer. + /// This holds information about a BaseItem in a format that is convenient for the client. + /// </summary> + public class BaseItemDto : IHasProviderIds, IItemDto, IHasServerId + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + public string OriginalTitle { get; set; } + + /// <summary> + /// Gets or sets the server identifier. + /// </summary> + /// <value>The server identifier.</value> + public string ServerId { get; set; } + + /// <summary> + /// Gets or sets the id. + /// </summary> + /// <value>The id.</value> + public Guid Id { get; set; } + + /// <summary> + /// Gets or sets the etag. + /// </summary> + /// <value>The etag.</value> + public string Etag { get; set; } + + /// <summary> + /// Gets or sets the type of the source. + /// </summary> + /// <value>The type of the source.</value> + public string SourceType { get; set; } + + /// <summary> + /// Gets or sets the playlist item identifier. + /// </summary> + /// <value>The playlist item identifier.</value> + public string PlaylistItemId { get; set; } + + /// <summary> + /// Gets or sets the date created. + /// </summary> + /// <value>The date created.</value> + public DateTime? DateCreated { get; set; } + + public DateTime? DateLastMediaAdded { get; set; } + public string ExtraType { get; set; } + + public int? AirsBeforeSeasonNumber { get; set; } + public int? AirsAfterSeasonNumber { get; set; } + public int? AirsBeforeEpisodeNumber { get; set; } + public bool? DisplaySpecialsWithSeasons { get; set; } + public bool? CanDelete { get; set; } + public bool? CanDownload { get; set; } + + public bool? HasSubtitles { get; set; } + + public string PreferredMetadataLanguage { get; set; } + public string PreferredMetadataCountryCode { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [supports synchronize]. + /// </summary> + public bool? SupportsSync { get; set; } + + public string Container { get; set; } + + /// <summary> + /// Gets or sets the name of the sort. + /// </summary> + /// <value>The name of the sort.</value> + public string SortName { get; set; } + public string ForcedSortName { get; set; } + + /// <summary> + /// Gets or sets the video3 D format. + /// </summary> + /// <value>The video3 D format.</value> + public Video3DFormat? Video3DFormat { get; set; } + + /// <summary> + /// Gets or sets the premiere date. + /// </summary> + /// <value>The premiere date.</value> + public DateTime? PremiereDate { get; set; } + + /// <summary> + /// Gets or sets the external urls. + /// </summary> + /// <value>The external urls.</value> + public ExternalUrl[] ExternalUrls { get; set; } + + /// <summary> + /// Gets or sets the media versions. + /// </summary> + /// <value>The media versions.</value> + public MediaSourceInfo[] MediaSources { get; set; } + + /// <summary> + /// Gets or sets the critic rating. + /// </summary> + /// <value>The critic rating.</value> + public float? CriticRating { get; set; } + + /// <summary> + /// Gets or sets the game system. + /// </summary> + /// <value>The game system.</value> + public string GameSystem { get; set; } + + public string[] ProductionLocations { get; set; } + + public string[] MultiPartGameFiles { get; set; } + + /// <summary> + /// Gets or sets the path. + /// </summary> + /// <value>The path.</value> + public string Path { get; set; } + + public bool? EnableMediaSourceDisplay { get; set; } + + /// <summary> + /// Gets or sets the official rating. + /// </summary> + /// <value>The official rating.</value> + public string OfficialRating { get; set; } + + /// <summary> + /// Gets or sets the custom rating. + /// </summary> + /// <value>The custom rating.</value> + public string CustomRating { get; set; } + + /// <summary> + /// Gets or sets the channel identifier. + /// </summary> + /// <value>The channel identifier.</value> + public Guid ChannelId { get; set; } + public string ChannelName { get; set; } + public string ServiceName { get; set; } + + /// <summary> + /// Gets or sets the overview. + /// </summary> + /// <value>The overview.</value> + public string Overview { get; set; } + + /// <summary> + /// Gets or sets the taglines. + /// </summary> + /// <value>The taglines.</value> + public string[] Taglines { get; set; } + + /// <summary> + /// Gets or sets the genres. + /// </summary> + /// <value>The genres.</value> + public string[] Genres { get; set; } + + /// <summary> + /// Gets or sets the community rating. + /// </summary> + /// <value>The community rating.</value> + public float? CommunityRating { get; set; } + + /// <summary> + /// Gets or sets the cumulative run time ticks. + /// </summary> + /// <value>The cumulative run time ticks.</value> + public long? CumulativeRunTimeTicks { get; set; } + + /// <summary> + /// Gets or sets the run time ticks. + /// </summary> + /// <value>The run time ticks.</value> + public long? RunTimeTicks { get; set; } + + /// <summary> + /// Gets or sets the play access. + /// </summary> + /// <value>The play access.</value> + public PlayAccess? PlayAccess { get; set; } + + /// <summary> + /// Gets or sets the aspect ratio. + /// </summary> + /// <value>The aspect ratio.</value> + public string AspectRatio { get; set; } + + /// <summary> + /// Gets or sets the production year. + /// </summary> + /// <value>The production year.</value> + public int? ProductionYear { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is place holder. + /// </summary> + /// <value><c>null</c> if [is place holder] contains no value, <c>true</c> if [is place holder]; otherwise, <c>false</c>.</value> + public bool? IsPlaceHolder { get; set; } + + /// <summary> + /// Gets or sets the number. + /// </summary> + /// <value>The number.</value> + public string Number { get; set; } + public string ChannelNumber { get; set; } + + /// <summary> + /// Gets or sets the index number. + /// </summary> + /// <value>The index number.</value> + public int? IndexNumber { get; set; } + + /// <summary> + /// Gets or sets the index number end. + /// </summary> + /// <value>The index number end.</value> + public int? IndexNumberEnd { get; set; } + + /// <summary> + /// Gets or sets the parent index number. + /// </summary> + /// <value>The parent index number.</value> + public int? ParentIndexNumber { get; set; } + + /// <summary> + /// Gets or sets the trailer urls. + /// </summary> + /// <value>The trailer urls.</value> + public MediaUrl[] RemoteTrailers { get; set; } + + /// <summary> + /// Gets or sets the provider ids. + /// </summary> + /// <value>The provider ids.</value> + public Dictionary<string, string> ProviderIds { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is HD. + /// </summary> + /// <value><c>null</c> if [is HD] contains no value, <c>true</c> if [is HD]; otherwise, <c>false</c>.</value> + public bool? IsHD { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is folder. + /// </summary> + /// <value><c>true</c> if this instance is folder; otherwise, <c>false</c>.</value> + public bool? IsFolder { get; set; } + + /// <summary> + /// Gets or sets the parent id. + /// </summary> + /// <value>The parent id.</value> + public Guid ParentId { get; set; } + + /// <summary> + /// Gets or sets the type. + /// </summary> + /// <value>The type.</value> + public string Type { get; set; } + + /// <summary> + /// Gets or sets the people. + /// </summary> + /// <value>The people.</value> + public BaseItemPerson[] People { get; set; } + + /// <summary> + /// Gets or sets the studios. + /// </summary> + /// <value>The studios.</value> + public NameGuidPair[] Studios { get; set; } + + public NameGuidPair[] GenreItems { get; set; } + + /// <summary> + /// If the item does not have a logo, this will hold the Id of the Parent that has one. + /// </summary> + /// <value>The parent logo item id.</value> + public string ParentLogoItemId { get; set; } + + /// <summary> + /// If the item does not have any backdrops, this will hold the Id of the Parent that has one. + /// </summary> + /// <value>The parent backdrop item id.</value> + public string ParentBackdropItemId { get; set; } + + /// <summary> + /// Gets or sets the parent backdrop image tags. + /// </summary> + /// <value>The parent backdrop image tags.</value> + public string[] ParentBackdropImageTags { get; set; } + + /// <summary> + /// Gets or sets the local trailer count. + /// </summary> + /// <value>The local trailer count.</value> + public int? LocalTrailerCount { get; set; } + + /// <summary> + /// User data for this item based on the user it's being requested for + /// </summary> + /// <value>The user data.</value> + public UserItemDataDto UserData { get; set; } + + /// <summary> + /// Gets or sets the recursive item count. + /// </summary> + /// <value>The recursive item count.</value> + public int? RecursiveItemCount { get; set; } + + /// <summary> + /// Gets or sets the child count. + /// </summary> + /// <value>The child count.</value> + public int? ChildCount { get; set; } + + /// <summary> + /// Gets or sets the name of the series. + /// </summary> + /// <value>The name of the series.</value> + public string SeriesName { get; set; } + + /// <summary> + /// Gets or sets the series id. + /// </summary> + /// <value>The series id.</value> + public Guid SeriesId { get; set; } + + /// <summary> + /// Gets or sets the season identifier. + /// </summary> + /// <value>The season identifier.</value> + public Guid SeasonId { get; set; } + + /// <summary> + /// Gets or sets the special feature count. + /// </summary> + /// <value>The special feature count.</value> + public int? SpecialFeatureCount { get; set; } + + /// <summary> + /// Gets or sets the display preferences id. + /// </summary> + /// <value>The display preferences id.</value> + public string DisplayPreferencesId { get; set; } + + /// <summary> + /// Gets or sets the status. + /// </summary> + /// <value>The status.</value> + public string Status { get; set; } + + /// <summary> + /// Gets or sets the air time. + /// </summary> + /// <value>The air time.</value> + public string AirTime { get; set; } + + /// <summary> + /// Gets or sets the air days. + /// </summary> + /// <value>The air days.</value> + public DayOfWeek[] AirDays { get; set; } + + /// <summary> + /// Gets or sets the tags. + /// </summary> + /// <value>The tags.</value> + public string[] Tags { get; set; } + + /// <summary> + /// Gets or sets the primary image aspect ratio, after image enhancements. + /// </summary> + /// <value>The primary image aspect ratio.</value> + public double? PrimaryImageAspectRatio { get; set; } + + /// <summary> + /// Gets or sets the artists. + /// </summary> + /// <value>The artists.</value> + public string[] Artists { get; set; } + + /// <summary> + /// Gets or sets the artist items. + /// </summary> + /// <value>The artist items.</value> + public NameGuidPair[] ArtistItems { get; set; } + + /// <summary> + /// Gets or sets the album. + /// </summary> + /// <value>The album.</value> + public string Album { get; set; } + + /// <summary> + /// Gets or sets the type of the collection. + /// </summary> + /// <value>The type of the collection.</value> + public string CollectionType { get; set; } + + /// <summary> + /// Gets or sets the display order. + /// </summary> + /// <value>The display order.</value> + public string DisplayOrder { get; set; } + + /// <summary> + /// Gets or sets the album id. + /// </summary> + /// <value>The album id.</value> + public Guid AlbumId { get; set; } + /// <summary> + /// Gets or sets the album image tag. + /// </summary> + /// <value>The album image tag.</value> + public string AlbumPrimaryImageTag { get; set; } + + /// <summary> + /// Gets or sets the series primary image tag. + /// </summary> + /// <value>The series primary image tag.</value> + public string SeriesPrimaryImageTag { get; set; } + + /// <summary> + /// Gets or sets the album artist. + /// </summary> + /// <value>The album artist.</value> + public string AlbumArtist { get; set; } + + /// <summary> + /// Gets or sets the album artists. + /// </summary> + /// <value>The album artists.</value> + public NameGuidPair[] AlbumArtists { get; set; } + + /// <summary> + /// Gets or sets the name of the season. + /// </summary> + /// <value>The name of the season.</value> + public string SeasonName { get; set; } + + /// <summary> + /// Gets or sets the media streams. + /// </summary> + /// <value>The media streams.</value> + public MediaStream[] MediaStreams { get; set; } + + /// <summary> + /// Gets or sets the type of the video. + /// </summary> + /// <value>The type of the video.</value> + public VideoType? VideoType { get; set; } + + /// <summary> + /// Gets or sets the part count. + /// </summary> + /// <value>The part count.</value> + public int? PartCount { get; set; } + public int? MediaSourceCount { get; set; } + + /// <summary> + /// Determines whether the specified type is type. + /// </summary> + /// <param name="type">The type.</param> + /// <returns><c>true</c> if the specified type is type; otherwise, <c>false</c>.</returns> + public bool IsType(Type type) + { + return IsType(type.Name); + } + + /// <summary> + /// Determines whether the specified type is type. + /// </summary> + /// <param name="type">The type.</param> + /// <returns><c>true</c> if the specified type is type; otherwise, <c>false</c>.</returns> + public bool IsType(string type) + { + return StringHelper.EqualsIgnoreCase(Type, type); + } + + /// <summary> + /// Gets or sets the image tags. + /// </summary> + /// <value>The image tags.</value> + public Dictionary<ImageType, string> ImageTags { get; set; } + + /// <summary> + /// Gets or sets the backdrop image tags. + /// </summary> + /// <value>The backdrop image tags.</value> + public string[] BackdropImageTags { get; set; } + + /// <summary> + /// Gets or sets the screenshot image tags. + /// </summary> + /// <value>The screenshot image tags.</value> + public string[] ScreenshotImageTags { get; set; } + + /// <summary> + /// Gets or sets the parent logo image tag. + /// </summary> + /// <value>The parent logo image tag.</value> + public string ParentLogoImageTag { get; set; } + + /// <summary> + /// If the item does not have a art, this will hold the Id of the Parent that has one. + /// </summary> + /// <value>The parent art item id.</value> + public string ParentArtItemId { get; set; } + + /// <summary> + /// Gets or sets the parent art image tag. + /// </summary> + /// <value>The parent art image tag.</value> + public string ParentArtImageTag { get; set; } + + /// <summary> + /// Gets or sets the series thumb image tag. + /// </summary> + /// <value>The series thumb image tag.</value> + public string SeriesThumbImageTag { get; set; } + + /// <summary> + /// Gets or sets the series studio. + /// </summary> + /// <value>The series studio.</value> + public string SeriesStudio { get; set; } + + /// <summary> + /// Gets or sets the parent thumb item id. + /// </summary> + /// <value>The parent thumb item id.</value> + public string ParentThumbItemId { get; set; } + + /// <summary> + /// Gets or sets the parent thumb image tag. + /// </summary> + /// <value>The parent thumb image tag.</value> + public string ParentThumbImageTag { get; set; } + + /// <summary> + /// Gets or sets the parent primary image item identifier. + /// </summary> + /// <value>The parent primary image item identifier.</value> + public string ParentPrimaryImageItemId { get; set; } + + /// <summary> + /// Gets or sets the parent primary image tag. + /// </summary> + /// <value>The parent primary image tag.</value> + public string ParentPrimaryImageTag { get; set; } + + /// <summary> + /// Gets or sets the chapters. + /// </summary> + /// <value>The chapters.</value> + public List<ChapterInfo> Chapters { get; set; } + + /// <summary> + /// Gets or sets the type of the location. + /// </summary> + /// <value>The type of the location.</value> + public LocationType? LocationType { get; set; } + + /// <summary> + /// Gets or sets the type of the iso. + /// </summary> + /// <value>The type of the iso.</value> + public IsoType? IsoType { get; set; } + + /// <summary> + /// Gets or sets the type of the media. + /// </summary> + /// <value>The type of the media.</value> + public string MediaType { get; set; } + + /// <summary> + /// Gets or sets the end date. + /// </summary> + /// <value>The end date.</value> + public DateTime? EndDate { get; set; } + + /// <summary> + /// Gets or sets the home page URL. + /// </summary> + /// <value>The home page URL.</value> + public string HomePageUrl { get; set; } + + /// <summary> + /// Gets or sets the locked fields. + /// </summary> + /// <value>The locked fields.</value> + public MetadataFields[] LockedFields { get; set; } + + /// <summary> + /// Gets or sets the trailer count. + /// </summary> + /// <value>The trailer count.</value> + public int? TrailerCount { get; set; } + /// <summary> + /// Gets or sets the movie count. + /// </summary> + /// <value>The movie count.</value> + public int? MovieCount { get; set; } + /// <summary> + /// Gets or sets the series count. + /// </summary> + /// <value>The series count.</value> + public int? SeriesCount { get; set; } + public int? ProgramCount { get; set; } + /// <summary> + /// Gets or sets the episode count. + /// </summary> + /// <value>The episode count.</value> + public int? EpisodeCount { get; set; } + /// <summary> + /// Gets or sets the game count. + /// </summary> + /// <value>The game count.</value> + public int? GameCount { get; set; } + /// <summary> + /// Gets or sets the song count. + /// </summary> + /// <value>The song count.</value> + public int? SongCount { get; set; } + /// <summary> + /// Gets or sets the album count. + /// </summary> + /// <value>The album count.</value> + public int? AlbumCount { get; set; } + public int? ArtistCount { get; set; } + /// <summary> + /// Gets or sets the music video count. + /// </summary> + /// <value>The music video count.</value> + public int? MusicVideoCount { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [enable internet providers]. + /// </summary> + /// <value><c>true</c> if [enable internet providers]; otherwise, <c>false</c>.</value> + public bool? LockData { get; set; } + + 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? ImageOrientation { 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; } + + /// <summary> + /// Used by RecordingGroup + /// </summary> + public int? RecordingCount { get; set; } + + /// <summary> + /// Gets or sets the series timer identifier. + /// </summary> + /// <value>The series timer identifier.</value> + public string SeriesTimerId { get; set; } + + /// <summary> + /// Gets or sets the program identifier. + /// </summary> + /// <value>The program identifier.</value> + public string ProgramId { get; set; } + + /// <summary> + /// Gets or sets the channel primary image tag. + /// </summary> + /// <value>The channel primary image tag.</value> + public string ChannelPrimaryImageTag { get; set; } + + /// <summary> + /// The start date of the recording, in UTC. + /// </summary> + public DateTime? StartDate { get; set; } + + /// <summary> + /// Gets or sets the completion percentage. + /// </summary> + /// <value>The completion percentage.</value> + public double? CompletionPercentage { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is repeat. + /// </summary> + /// <value><c>true</c> if this instance is repeat; otherwise, <c>false</c>.</value> + public bool? IsRepeat { get; set; } + + /// <summary> + /// Gets or sets the episode title. + /// </summary> + /// <value>The episode title.</value> + public string EpisodeTitle { get; set; } + + /// <summary> + /// Gets or sets the type of the channel. + /// </summary> + /// <value>The type of the channel.</value> + public ChannelType? ChannelType { get; set; } + + /// <summary> + /// Gets or sets the audio. + /// </summary> + /// <value>The audio.</value> + public ProgramAudio? Audio { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is movie. + /// </summary> + /// <value><c>true</c> if this instance is movie; otherwise, <c>false</c>.</value> + public bool? IsMovie { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is sports. + /// </summary> + /// <value><c>true</c> if this instance is sports; otherwise, <c>false</c>.</value> + public bool? IsSports { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is series. + /// </summary> + /// <value><c>true</c> if this instance is series; otherwise, <c>false</c>.</value> + public bool? IsSeries { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is live. + /// </summary> + /// <value><c>true</c> if this instance is live; otherwise, <c>false</c>.</value> + public bool? IsLive { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is news. + /// </summary> + /// <value><c>true</c> if this instance is news; otherwise, <c>false</c>.</value> + public bool? IsNews { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is kids. + /// </summary> + /// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value> + public bool? IsKids { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is premiere. + /// </summary> + /// <value><c>true</c> if this instance is premiere; otherwise, <c>false</c>.</value> + public bool? IsPremiere { get; set; } + + /// <summary> + /// Gets or sets the timer identifier. + /// </summary> + /// <value>The timer identifier.</value> + public string TimerId { get; set; } + /// <summary> + /// Gets or sets the current program. + /// </summary> + /// <value>The current program.</value> + public BaseItemDto CurrentProgram { get; set; } + } +} diff --git a/MediaBrowser.Model/Dto/BaseItemPerson.cs b/MediaBrowser.Model/Dto/BaseItemPerson.cs new file mode 100644 index 000000000..35b4e9249 --- /dev/null +++ b/MediaBrowser.Model/Dto/BaseItemPerson.cs @@ -0,0 +1,53 @@ +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Model.Dto +{ + /// <summary> + /// This is used by the api to get information about a Person within a BaseItem + /// </summary> + public class BaseItemPerson + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the identifier. + /// </summary> + /// <value>The identifier.</value> + public string Id { get; set; } + + /// <summary> + /// Gets or sets the role. + /// </summary> + /// <value>The role.</value> + public string Role { get; set; } + + /// <summary> + /// Gets or sets the type. + /// </summary> + /// <value>The type.</value> + public string Type { get; set; } + + /// <summary> + /// Gets or sets the primary image tag. + /// </summary> + /// <value>The primary image tag.</value> + public string PrimaryImageTag { get; set; } + + /// <summary> + /// Gets a value indicating whether this instance has primary image. + /// </summary> + /// <value><c>true</c> if this instance has primary image; otherwise, <c>false</c>.</value> + [IgnoreDataMember] + public bool HasPrimaryImage + { + get + { + return PrimaryImageTag != null; + } + } + } +} diff --git a/MediaBrowser.Model/Dto/ChapterInfoDto.cs b/MediaBrowser.Model/Dto/ChapterInfoDto.cs new file mode 100644 index 000000000..b68b55e70 --- /dev/null +++ b/MediaBrowser.Model/Dto/ChapterInfoDto.cs @@ -0,0 +1,38 @@ +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Model.Dto +{ + /// <summary> + /// Class ChapterInfo + /// </summary> + public class ChapterInfoDto + { + /// <summary> + /// Gets or sets the start position ticks. + /// </summary> + /// <value>The start position ticks.</value> + public long StartPositionTicks { get; set; } + + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the image tag. + /// </summary> + /// <value>The image tag.</value> + public string ImageTag { get; set; } + + /// <summary> + /// Gets a value indicating whether this instance has image. + /// </summary> + /// <value><c>true</c> if this instance has image; otherwise, <c>false</c>.</value> + [IgnoreDataMember] + public bool HasImage + { + get { return ImageTag != null; } + } + } +} diff --git a/MediaBrowser.Model/Dto/GameSystemSummary.cs b/MediaBrowser.Model/Dto/GameSystemSummary.cs new file mode 100644 index 000000000..252868b66 --- /dev/null +++ b/MediaBrowser.Model/Dto/GameSystemSummary.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using System; + +namespace MediaBrowser.Model.Dto +{ + /// <summary> + /// Class GameSystemSummary + /// </summary> + public class GameSystemSummary + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string DisplayName { get; set; } + + /// <summary> + /// Gets or sets the game count. + /// </summary> + /// <value>The game count.</value> + public int GameCount { get; set; } + + /// <summary> + /// Gets or sets the game extensions. + /// </summary> + /// <value>The game extensions.</value> + public string[] GameFileExtensions { get; set; } + + /// <summary> + /// Gets or sets the client installed game count. + /// </summary> + /// <value>The client installed game count.</value> + public int ClientInstalledGameCount { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="GameSystemSummary"/> class. + /// </summary> + public GameSystemSummary() + { + GameFileExtensions = new string[] {}; + } + } +} diff --git a/MediaBrowser.Model/Dto/IHasServerId.cs b/MediaBrowser.Model/Dto/IHasServerId.cs new file mode 100644 index 000000000..0515203da --- /dev/null +++ b/MediaBrowser.Model/Dto/IHasServerId.cs @@ -0,0 +1,8 @@ + +namespace MediaBrowser.Model.Dto +{ + public interface IHasServerId + { + string ServerId { get; } + } +} diff --git a/MediaBrowser.Model/Dto/IItemDto.cs b/MediaBrowser.Model/Dto/IItemDto.cs new file mode 100644 index 000000000..3e7d1c608 --- /dev/null +++ b/MediaBrowser.Model/Dto/IItemDto.cs @@ -0,0 +1,15 @@ + +namespace MediaBrowser.Model.Dto +{ + /// <summary> + /// Interface IItemDto + /// </summary> + public interface IItemDto + { + /// <summary> + /// Gets or sets the primary image aspect ratio. + /// </summary> + /// <value>The primary image aspect ratio.</value> + double? PrimaryImageAspectRatio { get; set; } + } +} diff --git a/MediaBrowser.Model/Dto/ImageByNameInfo.cs b/MediaBrowser.Model/Dto/ImageByNameInfo.cs new file mode 100644 index 000000000..b7921d993 --- /dev/null +++ b/MediaBrowser.Model/Dto/ImageByNameInfo.cs @@ -0,0 +1,32 @@ + +namespace MediaBrowser.Model.Dto +{ + public class ImageByNameInfo + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + /// <summary> + /// Gets or sets the theme. + /// </summary> + /// <value>The theme.</value> + public string Theme { get; set; } + /// <summary> + /// Gets or sets the context. + /// </summary> + /// <value>The context.</value> + public string Context { get; set; } + /// <summary> + /// Gets or sets the length of the file. + /// </summary> + /// <value>The length of the file.</value> + public long FileLength { get; set; } + /// <summary> + /// Gets or sets the format. + /// </summary> + /// <value>The format.</value> + public string Format { get; set; } + } +} diff --git a/MediaBrowser.Model/Dto/ImageInfo.cs b/MediaBrowser.Model/Dto/ImageInfo.cs new file mode 100644 index 000000000..5eabb16a5 --- /dev/null +++ b/MediaBrowser.Model/Dto/ImageInfo.cs @@ -0,0 +1,51 @@ +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Model.Dto +{ + /// <summary> + /// Class ImageInfo + /// </summary> + public class ImageInfo + { + /// <summary> + /// Gets or sets the type of the image. + /// </summary> + /// <value>The type of the image.</value> + public ImageType ImageType { get; set; } + + /// <summary> + /// Gets or sets the index of the image. + /// </summary> + /// <value>The index of the image.</value> + public int? ImageIndex { get; set; } + + /// <summary> + /// The image tag + /// </summary> + public string ImageTag; + + /// <summary> + /// Gets or sets the path. + /// </summary> + /// <value>The path.</value> + public string Path { get; set; } + + /// <summary> + /// Gets or sets the height. + /// </summary> + /// <value>The height.</value> + public int? Height { get; set; } + + /// <summary> + /// Gets or sets the width. + /// </summary> + /// <value>The width.</value> + public int? Width { get; set; } + + /// <summary> + /// Gets or sets the size. + /// </summary> + /// <value>The size.</value> + public long Size { get; set; } + } +} diff --git a/MediaBrowser.Model/Dto/ImageOptions.cs b/MediaBrowser.Model/Dto/ImageOptions.cs new file mode 100644 index 000000000..98bd0279a --- /dev/null +++ b/MediaBrowser.Model/Dto/ImageOptions.cs @@ -0,0 +1,110 @@ +using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Model.Dto +{ + /// <summary> + /// Class ImageOptions + /// </summary> + public class ImageOptions + { + /// <summary> + /// Gets or sets the type of the image. + /// </summary> + /// <value>The type of the image.</value> + public ImageType ImageType { get; set; } + + /// <summary> + /// Gets or sets the index of the image. + /// </summary> + /// <value>The index of the image.</value> + public int? ImageIndex { get; set; } + + /// <summary> + /// Gets or sets the width. + /// </summary> + /// <value>The width.</value> + public int? Width { get; set; } + + /// <summary> + /// Gets or sets the height. + /// </summary> + /// <value>The height.</value> + public int? Height { get; set; } + + /// <summary> + /// Gets or sets the width of the max. + /// </summary> + /// <value>The width of the max.</value> + public int? MaxWidth { get; set; } + + /// <summary> + /// Gets or sets the height of the max. + /// </summary> + /// <value>The height of the max.</value> + public int? MaxHeight { get; set; } + + /// <summary> + /// Gets or sets the quality. + /// </summary> + /// <value>The quality.</value> + public int? Quality { get; set; } + + /// <summary> + /// Gets or sets the image tag. + /// If set this will result in strong, unconditional response caching + /// </summary> + /// <value>The hash.</value> + public string Tag { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [crop whitespace]. + /// </summary> + /// <value><c>null</c> if [crop whitespace] contains no value, <c>true</c> if [crop whitespace]; otherwise, <c>false</c>.</value> + public bool? CropWhitespace { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [enable image enhancers]. + /// </summary> + /// <value><c>true</c> if [enable image enhancers]; otherwise, <c>false</c>.</value> + public bool EnableImageEnhancers { get; set; } + + /// <summary> + /// Gets or sets the format. + /// </summary> + /// <value>The format.</value> + public ImageFormat? Format { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [add played indicator]. + /// </summary> + /// <value><c>true</c> if [add played indicator]; otherwise, <c>false</c>.</value> + public bool AddPlayedIndicator { get; set; } + + /// <summary> + /// Gets or sets the percent played. + /// </summary> + /// <value>The percent played.</value> + public int? PercentPlayed { get; set; } + + /// <summary> + /// Gets or sets the un played count. + /// </summary> + /// <value>The un played count.</value> + public int? UnPlayedCount { get; set; } + + /// <summary> + /// Gets or sets the color of the background. + /// </summary> + /// <value>The color of the background.</value> + public string BackgroundColor { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="ImageOptions" /> class. + /// </summary> + public ImageOptions() + { + EnableImageEnhancers = true; + } + } +} diff --git a/MediaBrowser.Model/Dto/ItemCounts.cs b/MediaBrowser.Model/Dto/ItemCounts.cs new file mode 100644 index 000000000..8ceb3a86b --- /dev/null +++ b/MediaBrowser.Model/Dto/ItemCounts.cs @@ -0,0 +1,67 @@ +namespace MediaBrowser.Model.Dto +{ + /// <summary> + /// Class LibrarySummary + /// </summary> + public class ItemCounts + { + /// <summary> + /// Gets or sets the movie count. + /// </summary> + /// <value>The movie count.</value> + public int MovieCount { get; set; } + /// <summary> + /// Gets or sets the series count. + /// </summary> + /// <value>The series count.</value> + public int SeriesCount { get; set; } + /// <summary> + /// Gets or sets the episode count. + /// </summary> + /// <value>The episode count.</value> + public int EpisodeCount { get; set; } + /// <summary> + /// Gets or sets the game count. + /// </summary> + /// <value>The game count.</value> + public int GameCount { get; set; } + public int ArtistCount { get; set; } + public int ProgramCount { get; set; } + /// <summary> + /// Gets or sets the game system count. + /// </summary> + /// <value>The game system count.</value> + public int GameSystemCount { get; set; } + /// <summary> + /// Gets or sets the trailer count. + /// </summary> + /// <value>The trailer count.</value> + public int TrailerCount { get; set; } + /// <summary> + /// Gets or sets the song count. + /// </summary> + /// <value>The song count.</value> + public int SongCount { get; set; } + /// <summary> + /// Gets or sets the album count. + /// </summary> + /// <value>The album count.</value> + public int AlbumCount { get; set; } + /// <summary> + /// Gets or sets the music video count. + /// </summary> + /// <value>The music video count.</value> + public int MusicVideoCount { get; set; } + /// <summary> + /// Gets or sets the box set count. + /// </summary> + /// <value>The box set count.</value> + public int BoxSetCount { get; set; } + /// <summary> + /// Gets or sets the book count. + /// </summary> + /// <value>The book count.</value> + public int BookCount { get; set; } + public int ItemCount { get; set; } + } +} diff --git a/MediaBrowser.Model/Dto/ItemIndex.cs b/MediaBrowser.Model/Dto/ItemIndex.cs new file mode 100644 index 000000000..96cef622b --- /dev/null +++ b/MediaBrowser.Model/Dto/ItemIndex.cs @@ -0,0 +1,21 @@ + +namespace MediaBrowser.Model.Dto +{ + /// <summary> + /// Class ItemIndex + /// </summary> + public class ItemIndex + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the item count. + /// </summary> + /// <value>The item count.</value> + public int ItemCount { get; set; } + } +} diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs new file mode 100644 index 000000000..d1d068fb6 --- /dev/null +++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs @@ -0,0 +1,236 @@ +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.MediaInfo; +using System.Collections.Generic; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Session; +using System; + +namespace MediaBrowser.Model.Dto +{ + public class MediaSourceInfo + { + public MediaProtocol Protocol { get; set; } + public string Id { get; set; } + + public string Path { get; set; } + + public string EncoderPath { get; set; } + public MediaProtocol? EncoderProtocol { get; set; } + + public MediaSourceType Type { get; set; } + + public string Container { get; set; } + public long? Size { get; set; } + + public string Name { get; set; } + + /// <summary> + /// Differentiate internet url vs local network + /// </summary> + public bool IsRemote { get; set; } + + public string ETag { get; set; } + public long? RunTimeTicks { get; set; } + public bool ReadAtNativeFramerate { get; set; } + public bool IgnoreDts { get; set; } + public bool IgnoreIndex { get; set; } + public bool GenPtsInput { get; set; } + public bool SupportsTranscoding { get; set; } + public bool SupportsDirectStream { get; set; } + public bool SupportsDirectPlay { get; set; } + public bool IsInfiniteStream { get; set; } + public bool RequiresOpening { get; set; } + public string OpenToken { get; set; } + public bool RequiresClosing { get; set; } + public string LiveStreamId { get; set; } + public int? BufferMs { get; set; } + + public bool RequiresLooping { get; set; } + + public bool SupportsProbing { get; set; } + + public VideoType? VideoType { get; set; } + + public IsoType? IsoType { get; set; } + + public Video3DFormat? Video3DFormat { get; set; } + + public List<MediaStream> MediaStreams { get; set; } + + public string[] Formats { get; set; } + + public int? Bitrate { get; set; } + + public TransportStreamTimestamp? Timestamp { get; set; } + public Dictionary<string, string> RequiredHttpHeaders { get; set; } + + public string TranscodingUrl { get; set; } + public string TranscodingSubProtocol { get; set; } + public string TranscodingContainer { get; set; } + + public int? AnalyzeDurationMs { get; set; } + + public MediaSourceInfo() + { + Formats = new string[] { }; + MediaStreams = new List<MediaStream>(); + RequiredHttpHeaders = new Dictionary<string, string>(); + SupportsTranscoding = true; + SupportsDirectStream = true; + SupportsDirectPlay = true; + SupportsProbing = true; + } + + public void InferTotalBitrate(bool force = false) + { + if (MediaStreams == null) + { + return; + } + + if (!force && Bitrate.HasValue) + { + return; + } + + var bitrate = 0; + foreach (var stream in MediaStreams) + { + if (!stream.IsExternal) + { + bitrate += stream.BitRate ?? 0; + } + } + + if (bitrate > 0) + { + Bitrate = bitrate; + } + } + + [IgnoreDataMember] + public TranscodeReason[] TranscodeReasons { get; set; } + + public int? DefaultAudioStreamIndex { get; set; } + public int? DefaultSubtitleStreamIndex { get; set; } + + [IgnoreDataMember] + public MediaStream DefaultAudioStream + { + get { return GetDefaultAudioStream(DefaultAudioStreamIndex); } + } + + public MediaStream GetDefaultAudioStream(int? defaultIndex) + { + if (defaultIndex.HasValue) + { + var val = defaultIndex.Value; + + foreach (MediaStream i in MediaStreams) + { + if (i.Type == MediaStreamType.Audio && i.Index == val) + { + return i; + } + } + } + + foreach (MediaStream i in MediaStreams) + { + if (i.Type == MediaStreamType.Audio && i.IsDefault) + { + return i; + } + } + + foreach (MediaStream i in MediaStreams) + { + if (i.Type == MediaStreamType.Audio) + { + return i; + } + } + + return null; + } + + [IgnoreDataMember] + public MediaStream VideoStream + { + get + { + foreach (MediaStream i in MediaStreams) + { + if (i.Type == MediaStreamType.Video) + { + return i; + } + } + + return null; + } + } + + public MediaStream GetMediaStream(MediaStreamType type, int index) + { + foreach (MediaStream i in MediaStreams) + { + if (i.Type == type && i.Index == index) + { + return i; + } + } + + return null; + } + + public int? GetStreamCount(MediaStreamType type) + { + int numMatches = 0; + int numStreams = 0; + + foreach (MediaStream i in MediaStreams) + { + numStreams++; + if (i.Type == type) + { + numMatches++; + } + } + + if (numStreams == 0) + { + return null; + } + + return numMatches; + } + + public bool? IsSecondaryAudio(MediaStream stream) + { + // Look for the first audio track marked as default + foreach (MediaStream currentStream in MediaStreams) + { + if (currentStream.Type == MediaStreamType.Audio && currentStream.IsDefault) + { + if (currentStream.Index != stream.Index) + { + return true; + } + } + } + + // Look for the first audio track + foreach (MediaStream currentStream in MediaStreams) + { + if (currentStream.Type == MediaStreamType.Audio) + { + return currentStream.Index != stream.Index; + } + } + + return null; + } + } +} diff --git a/MediaBrowser.Model/Dto/MediaSourceType.cs b/MediaBrowser.Model/Dto/MediaSourceType.cs new file mode 100644 index 000000000..e04978502 --- /dev/null +++ b/MediaBrowser.Model/Dto/MediaSourceType.cs @@ -0,0 +1,9 @@ +namespace MediaBrowser.Model.Dto +{ + public enum MediaSourceType + { + Default = 0, + Grouping = 1, + Placeholder = 2 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dto/MetadataEditorInfo.cs b/MediaBrowser.Model/Dto/MetadataEditorInfo.cs new file mode 100644 index 000000000..aa8b33c81 --- /dev/null +++ b/MediaBrowser.Model/Dto/MetadataEditorInfo.cs @@ -0,0 +1,27 @@ +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.Providers; +using System.Collections.Generic; + +namespace MediaBrowser.Model.Dto +{ + public class MetadataEditorInfo + { + public ParentalRating[] ParentalRatingOptions { get; set; } + public CountryInfo[] Countries { get; set; } + public CultureDto[] Cultures { get; set; } + public ExternalIdInfo[] ExternalIdInfos { get; set; } + + public string ContentType { get; set; } + public NameValuePair[] ContentTypeOptions { get; set; } + + public MetadataEditorInfo() + { + ParentalRatingOptions = new ParentalRating[] { }; + Countries = new CountryInfo[] { }; + Cultures = new CultureDto[] { }; + ExternalIdInfos = new ExternalIdInfo[] { }; + ContentTypeOptions = new NameValuePair[] { }; + } + } +} diff --git a/MediaBrowser.Model/Dto/NameIdPair.cs b/MediaBrowser.Model/Dto/NameIdPair.cs new file mode 100644 index 000000000..50318ac95 --- /dev/null +++ b/MediaBrowser.Model/Dto/NameIdPair.cs @@ -0,0 +1,24 @@ +using System; + +namespace MediaBrowser.Model.Dto +{ + public class NameIdPair + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + /// <summary> + /// Gets or sets the identifier. + /// </summary> + /// <value>The identifier.</value> + public string Id { get; set; } + } + + public class NameGuidPair + { + public string Name { get; set; } + public Guid Id { get; set; } + } +} diff --git a/MediaBrowser.Model/Dto/NameValuePair.cs b/MediaBrowser.Model/Dto/NameValuePair.cs new file mode 100644 index 000000000..a6e687949 --- /dev/null +++ b/MediaBrowser.Model/Dto/NameValuePair.cs @@ -0,0 +1,28 @@ + +namespace MediaBrowser.Model.Dto +{ + public class NameValuePair + { + public NameValuePair() + { + + } + + public NameValuePair(string name, string value) + { + Name = name; + Value = value; + } + + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + /// <summary> + /// Gets or sets the value. + /// </summary> + /// <value>The value.</value> + public string Value { get; set; } + } +} diff --git a/MediaBrowser.Model/Dto/RatingType.cs b/MediaBrowser.Model/Dto/RatingType.cs new file mode 100644 index 000000000..f151adce9 --- /dev/null +++ b/MediaBrowser.Model/Dto/RatingType.cs @@ -0,0 +1,8 @@ +namespace MediaBrowser.Model.Dto +{ + public enum RatingType + { + Score, + Likes + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dto/RecommendationDto.cs b/MediaBrowser.Model/Dto/RecommendationDto.cs new file mode 100644 index 000000000..79d3d6c6f --- /dev/null +++ b/MediaBrowser.Model/Dto/RecommendationDto.cs @@ -0,0 +1,15 @@ +using System; + +namespace MediaBrowser.Model.Dto +{ + public class RecommendationDto + { + public BaseItemDto[] Items { get; set; } + + public RecommendationType RecommendationType { get; set; } + + public string BaselineItemName { get; set; } + + public Guid CategoryId { get; set; } + } +} diff --git a/MediaBrowser.Model/Dto/RecommendationType.cs b/MediaBrowser.Model/Dto/RecommendationType.cs new file mode 100644 index 000000000..1adf9b082 --- /dev/null +++ b/MediaBrowser.Model/Dto/RecommendationType.cs @@ -0,0 +1,17 @@ +namespace MediaBrowser.Model.Dto +{ + public enum RecommendationType + { + SimilarToRecentlyPlayed = 0, + + SimilarToLikedItem = 1, + + HasDirectorFromRecentlyPlayed = 2, + + HasActorFromRecentlyPlayed = 3, + + HasLikedDirector = 4, + + HasLikedActor = 5 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Dto/UserDto.cs b/MediaBrowser.Model/Dto/UserDto.cs new file mode 100644 index 000000000..f42c495ad --- /dev/null +++ b/MediaBrowser.Model/Dto/UserDto.cs @@ -0,0 +1,125 @@ +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Connect; +using MediaBrowser.Model.Users; +using System; +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Model.Dto +{ + /// <summary> + /// Class UserDto + /// </summary> + public class UserDto : IItemDto, IHasServerId + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the server identifier. + /// </summary> + /// <value>The server identifier.</value> + public string ServerId { get; set; } + + /// <summary> + /// Gets or sets the name of the server. + /// This is not used by the server and is for client-side usage only. + /// </summary> + /// <value>The name of the server.</value> + public string ServerName { get; set; } + + /// <summary> + /// Gets or sets the name of the connect user. + /// </summary> + /// <value>The name of the connect user.</value> + public string ConnectUserName { get; set; } + /// <summary> + /// Gets or sets the connect user identifier. + /// </summary> + /// <value>The connect user identifier.</value> + public string ConnectUserId { get; set; } + /// <summary> + /// Gets or sets the type of the connect link. + /// </summary> + /// <value>The type of the connect link.</value> + public UserLinkType? ConnectLinkType { get; set; } + + /// <summary> + /// Gets or sets the id. + /// </summary> + /// <value>The id.</value> + public Guid Id { get; set; } + + /// <summary> + /// Gets or sets the primary image tag. + /// </summary> + /// <value>The primary image tag.</value> + public string PrimaryImageTag { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance has password. + /// </summary> + /// <value><c>true</c> if this instance has password; otherwise, <c>false</c>.</value> + public bool HasPassword { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance has configured password. + /// </summary> + /// <value><c>true</c> if this instance has configured password; otherwise, <c>false</c>.</value> + public bool HasConfiguredPassword { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance has configured easy password. + /// </summary> + /// <value><c>true</c> if this instance has configured easy password; otherwise, <c>false</c>.</value> + public bool HasConfiguredEasyPassword { get; set; } + + public bool? EnableAutoLogin { get; set; } + + /// <summary> + /// Gets or sets the last login date. + /// </summary> + /// <value>The last login date.</value> + public DateTime? LastLoginDate { get; set; } + + /// <summary> + /// Gets or sets the last activity date. + /// </summary> + /// <value>The last activity date.</value> + public DateTime? LastActivityDate { get; set; } + + /// <summary> + /// Gets or sets the configuration. + /// </summary> + /// <value>The configuration.</value> + public UserConfiguration Configuration { get; set; } + + /// <summary> + /// Gets or sets the policy. + /// </summary> + /// <value>The policy.</value> + public UserPolicy Policy { get; set; } + + /// <summary> + /// Gets or sets the primary image aspect ratio. + /// </summary> + /// <value>The primary image aspect ratio.</value> + public double? PrimaryImageAspectRatio { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="UserDto"/> class. + /// </summary> + public UserDto() + { + Configuration = new UserConfiguration(); + Policy = new UserPolicy(); + } + + public override string ToString() + { + return Name ?? base.ToString(); + } + } +} diff --git a/MediaBrowser.Model/Dto/UserItemDataDto.cs b/MediaBrowser.Model/Dto/UserItemDataDto.cs new file mode 100644 index 000000000..507dbb06d --- /dev/null +++ b/MediaBrowser.Model/Dto/UserItemDataDto.cs @@ -0,0 +1,76 @@ +using System; + +namespace MediaBrowser.Model.Dto +{ + /// <summary> + /// Class UserItemDataDto + /// </summary> + public class UserItemDataDto + { + /// <summary> + /// Gets or sets the rating. + /// </summary> + /// <value>The rating.</value> + public double? Rating { get; set; } + + /// <summary> + /// Gets or sets the played percentage. + /// </summary> + /// <value>The played percentage.</value> + public double? PlayedPercentage { get; set; } + + /// <summary> + /// Gets or sets the unplayed item count. + /// </summary> + /// <value>The unplayed item count.</value> + public int? UnplayedItemCount { get; set; } + + /// <summary> + /// Gets or sets the playback position ticks. + /// </summary> + /// <value>The playback position ticks.</value> + public long PlaybackPositionTicks { get; set; } + + /// <summary> + /// Gets or sets the play count. + /// </summary> + /// <value>The play count.</value> + public int PlayCount { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is favorite. + /// </summary> + /// <value><c>true</c> if this instance is favorite; otherwise, <c>false</c>.</value> + public bool IsFavorite { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this <see cref="UserItemDataDto" /> is likes. + /// </summary> + /// <value><c>null</c> if [likes] contains no value, <c>true</c> if [likes]; otherwise, <c>false</c>.</value> + public bool? Likes { get; set; } + + /// <summary> + /// Gets or sets the last played date. + /// </summary> + /// <value>The last played date.</value> + public DateTime? LastPlayedDate { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this <see cref="UserItemDataDto" /> is played. + /// </summary> + /// <value><c>true</c> if played; otherwise, <c>false</c>.</value> + public bool Played { get; set; } + + /// <summary> + /// Gets or sets the key. + /// </summary> + /// <value>The key.</value> + public string Key { get; set; } + + /// <summary> + /// Gets or sets the item identifier. + /// </summary> + /// <value>The item identifier.</value> + public string ItemId { get; set; } + } +} diff --git a/MediaBrowser.Model/Entities/ChapterInfo.cs b/MediaBrowser.Model/Entities/ChapterInfo.cs new file mode 100644 index 000000000..c24ca553b --- /dev/null +++ b/MediaBrowser.Model/Entities/ChapterInfo.cs @@ -0,0 +1,31 @@ +using System; + +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Class ChapterInfo + /// </summary> + public class ChapterInfo + { + /// <summary> + /// Gets or sets the start position ticks. + /// </summary> + /// <value>The start position ticks.</value> + public long StartPositionTicks { get; set; } + + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the image path. + /// </summary> + /// <value>The image path.</value> + public string ImagePath { get; set; } + public DateTime ImageDateModified { get; set; } + + public string ImageTag { get; set; } + } +} diff --git a/MediaBrowser.Model/Entities/CollectionType.cs b/MediaBrowser.Model/Entities/CollectionType.cs new file mode 100644 index 000000000..f49e73c16 --- /dev/null +++ b/MediaBrowser.Model/Entities/CollectionType.cs @@ -0,0 +1,58 @@ +namespace MediaBrowser.Model.Entities +{ + public static class CollectionType + { + public const string Movies = "movies"; + + public const string TvShows = "tvshows"; + + public const string Music = "music"; + + public const string MusicVideos = "musicvideos"; + + public const string Trailers = "trailers"; + + public const string HomeVideos = "homevideos"; + + public const string BoxSets = "boxsets"; + + public const string Books = "books"; + public const string Photos = "photos"; + public const string Games = "games"; + public const string LiveTv = "livetv"; + public const string Playlists = "playlists"; + public const string Folders = "folders"; + } + + public static class SpecialFolder + { + public const string TvShowSeries = "TvShowSeries"; + public const string TvGenres = "TvGenres"; + public const string TvGenre = "TvGenre"; + public const string TvLatest = "TvLatest"; + public const string TvNextUp = "TvNextUp"; + public const string TvResume = "TvResume"; + public const string TvFavoriteSeries = "TvFavoriteSeries"; + public const string TvFavoriteEpisodes = "TvFavoriteEpisodes"; + + public const string MovieLatest = "MovieLatest"; + public const string MovieResume = "MovieResume"; + public const string MovieMovies = "MovieMovies"; + public const string MovieCollections = "MovieCollections"; + public const string MovieFavorites = "MovieFavorites"; + public const string MovieGenres = "MovieGenres"; + public const string MovieGenre = "MovieGenre"; + + public const string MusicArtists = "MusicArtists"; + public const string MusicAlbumArtists = "MusicAlbumArtists"; + public const string MusicAlbums = "MusicAlbums"; + public const string MusicGenres = "MusicGenres"; + public const string MusicLatest = "MusicLatest"; + public const string MusicPlaylists = "MusicPlaylists"; + public const string MusicSongs = "MusicSongs"; + public const string MusicFavorites = "MusicFavorites"; + public const string MusicFavoriteArtists = "MusicFavoriteArtists"; + public const string MusicFavoriteAlbums = "MusicFavoriteAlbums"; + public const string MusicFavoriteSongs = "MusicFavoriteSongs"; + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Entities/DisplayPreferences.cs b/MediaBrowser.Model/Entities/DisplayPreferences.cs new file mode 100644 index 000000000..dc386f775 --- /dev/null +++ b/MediaBrowser.Model/Entities/DisplayPreferences.cs @@ -0,0 +1,99 @@ +using MediaBrowser.Model.Drawing; +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Defines the display preferences for any item that supports them (usually Folders) + /// </summary> + public class DisplayPreferences + { + /// <summary> + /// The image scale + /// </summary> + private const double ImageScale = .9; + + /// <summary> + /// Initializes a new instance of the <see cref="DisplayPreferences" /> class. + /// </summary> + public DisplayPreferences() + { + RememberIndexing = false; + PrimaryImageHeight = 250; + PrimaryImageWidth = 250; + ShowBackdrop = true; + CustomPrefs = new Dictionary<string, string>(); + } + + /// <summary> + /// Gets or sets the user id. + /// </summary> + /// <value>The user id.</value> + public string Id { get; set; } + /// <summary> + /// Gets or sets the type of the view. + /// </summary> + /// <value>The type of the view.</value> + public string ViewType { get; set; } + /// <summary> + /// Gets or sets the sort by. + /// </summary> + /// <value>The sort by.</value> + public string SortBy { get; set; } + /// <summary> + /// Gets or sets the index by. + /// </summary> + /// <value>The index by.</value> + public string IndexBy { get; set; } + /// <summary> + /// Gets or sets a value indicating whether [remember indexing]. + /// </summary> + /// <value><c>true</c> if [remember indexing]; otherwise, <c>false</c>.</value> + public bool RememberIndexing { get; set; } + /// <summary> + /// Gets or sets the height of the primary image. + /// </summary> + /// <value>The height of the primary image.</value> + public int PrimaryImageHeight { get; set; } + /// <summary> + /// Gets or sets the width of the primary image. + /// </summary> + /// <value>The width of the primary image.</value> + public int PrimaryImageWidth { get; set; } + /// <summary> + /// Gets or sets the custom prefs. + /// </summary> + /// <value>The custom prefs.</value> + public Dictionary<string, string> CustomPrefs { get; set; } + /// <summary> + /// Gets or sets the scroll direction. + /// </summary> + /// <value>The scroll direction.</value> + public ScrollDirection ScrollDirection { get; set; } + /// <summary> + /// Gets or sets a value indicating whether to show backdrops on this item. + /// </summary> + /// <value><c>true</c> if showing backdrops; otherwise, <c>false</c>.</value> + public bool ShowBackdrop { get; set; } + /// <summary> + /// Gets or sets a value indicating whether [remember sorting]. + /// </summary> + /// <value><c>true</c> if [remember sorting]; otherwise, <c>false</c>.</value> + public bool RememberSorting { get; set; } + /// <summary> + /// Gets or sets the sort order. + /// </summary> + /// <value>The sort order.</value> + public SortOrder SortOrder { get; set; } + /// <summary> + /// Gets or sets a value indicating whether [show sidebar]. + /// </summary> + /// <value><c>true</c> if [show sidebar]; otherwise, <c>false</c>.</value> + public bool ShowSidebar { get; set; } + /// <summary> + /// Gets or sets the client + /// </summary> + public string Client { get; set; } + } +} diff --git a/MediaBrowser.Model/Entities/EmptyRequestResult.cs b/MediaBrowser.Model/Entities/EmptyRequestResult.cs new file mode 100644 index 000000000..5c9a725fd --- /dev/null +++ b/MediaBrowser.Model/Entities/EmptyRequestResult.cs @@ -0,0 +1,7 @@ + +namespace MediaBrowser.Model.Entities +{ + public class EmptyRequestResult + { + } +} diff --git a/MediaBrowser.Model/Entities/ExtraType.cs b/MediaBrowser.Model/Entities/ExtraType.cs new file mode 100644 index 000000000..ab8da58c0 --- /dev/null +++ b/MediaBrowser.Model/Entities/ExtraType.cs @@ -0,0 +1,16 @@ + +namespace MediaBrowser.Model.Entities +{ + public enum ExtraType + { + Clip = 1, + Trailer = 2, + BehindTheScenes = 3, + DeletedScene = 4, + Interview = 5, + Scene = 6, + Sample = 7, + ThemeSong = 8, + ThemeVideo = 9 + } +} diff --git a/MediaBrowser.Model/Entities/IHasProviderIds.cs b/MediaBrowser.Model/Entities/IHasProviderIds.cs new file mode 100644 index 000000000..796850dbd --- /dev/null +++ b/MediaBrowser.Model/Entities/IHasProviderIds.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Since BaseItem and DTOBaseItem both have ProviderIds, this interface helps avoid code repition by using extension methods + /// </summary> + public interface IHasProviderIds + { + /// <summary> + /// Gets or sets the provider ids. + /// </summary> + /// <value>The provider ids.</value> + Dictionary<string, string> ProviderIds { get; set; } + } +} diff --git a/MediaBrowser.Model/Entities/ImageType.cs b/MediaBrowser.Model/Entities/ImageType.cs new file mode 100644 index 000000000..6e0ba717f --- /dev/null +++ b/MediaBrowser.Model/Entities/ImageType.cs @@ -0,0 +1,58 @@ + +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Enum ImageType + /// </summary> + public enum ImageType + { + /// <summary> + /// The primary + /// </summary> + Primary = 0, + /// <summary> + /// The art + /// </summary> + Art = 1, + /// <summary> + /// The backdrop + /// </summary> + Backdrop = 2, + /// <summary> + /// The banner + /// </summary> + Banner = 3, + /// <summary> + /// The logo + /// </summary> + Logo = 4, + /// <summary> + /// The thumb + /// </summary> + Thumb = 5, + /// <summary> + /// The disc + /// </summary> + Disc = 6, + /// <summary> + /// The box + /// </summary> + Box = 7, + /// <summary> + /// The screenshot + /// </summary> + Screenshot = 8, + /// <summary> + /// The menu + /// </summary> + Menu = 9, + /// <summary> + /// The chapter image + /// </summary> + Chapter = 10, + /// <summary> + /// The box rear + /// </summary> + BoxRear = 11 + } +} diff --git a/MediaBrowser.Model/Entities/IsoType.cs b/MediaBrowser.Model/Entities/IsoType.cs new file mode 100644 index 000000000..567b98ab9 --- /dev/null +++ b/MediaBrowser.Model/Entities/IsoType.cs @@ -0,0 +1,17 @@ +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Enum IsoType + /// </summary> + public enum IsoType + { + /// <summary> + /// The DVD + /// </summary> + Dvd, + /// <summary> + /// The blu ray + /// </summary> + BluRay + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Entities/LibraryUpdateInfo.cs b/MediaBrowser.Model/Entities/LibraryUpdateInfo.cs new file mode 100644 index 000000000..dfab9add2 --- /dev/null +++ b/MediaBrowser.Model/Entities/LibraryUpdateInfo.cs @@ -0,0 +1,62 @@ +using System; + +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Class LibraryUpdateInfo + /// </summary> + public class LibraryUpdateInfo + { + /// <summary> + /// Gets or sets the folders added to. + /// </summary> + /// <value>The folders added to.</value> + public string[] FoldersAddedTo { get; set; } + /// <summary> + /// Gets or sets the folders removed from. + /// </summary> + /// <value>The folders removed from.</value> + public string[] FoldersRemovedFrom { get; set; } + + /// <summary> + /// Gets or sets the items added. + /// </summary> + /// <value>The items added.</value> + public string[] ItemsAdded { get; set; } + + /// <summary> + /// Gets or sets the items removed. + /// </summary> + /// <value>The items removed.</value> + public string[] ItemsRemoved { get; set; } + + /// <summary> + /// Gets or sets the items updated. + /// </summary> + /// <value>The items updated.</value> + public string[] ItemsUpdated { get; set; } + + public string[] CollectionFolders { get; set; } + + public bool IsEmpty + { + get + { + return FoldersAddedTo.Length == 0 && FoldersRemovedFrom.Length == 0 && ItemsAdded.Length == 0 && ItemsRemoved.Length == 0 && ItemsUpdated.Length == 0 && CollectionFolders.Length == 0; + } + } + + /// <summary> + /// Initializes a new instance of the <see cref="LibraryUpdateInfo"/> class. + /// </summary> + public LibraryUpdateInfo() + { + FoldersAddedTo = new string[] { }; + FoldersRemovedFrom = new string[] { }; + ItemsAdded = new string[] { }; + ItemsRemoved = new string[] { }; + ItemsUpdated = new string[] { }; + CollectionFolders = new string[] { }; + } + } +} diff --git a/MediaBrowser.Model/Entities/LocationType.cs b/MediaBrowser.Model/Entities/LocationType.cs new file mode 100644 index 000000000..84de803aa --- /dev/null +++ b/MediaBrowser.Model/Entities/LocationType.cs @@ -0,0 +1,26 @@ + +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Enum LocationType + /// </summary> + public enum LocationType + { + /// <summary> + /// The file system + /// </summary> + FileSystem = 0, + /// <summary> + /// The remote + /// </summary> + Remote = 1, + /// <summary> + /// The virtual + /// </summary> + Virtual = 2, + /// <summary> + /// The offline + /// </summary> + Offline = 3 + } +} diff --git a/MediaBrowser.Model/Entities/MBRegistrationRecord.cs b/MediaBrowser.Model/Entities/MBRegistrationRecord.cs new file mode 100644 index 000000000..00176fb34 --- /dev/null +++ b/MediaBrowser.Model/Entities/MBRegistrationRecord.cs @@ -0,0 +1,14 @@ +using System; + +namespace MediaBrowser.Model.Entities +{ + public class MBRegistrationRecord + { + public DateTime ExpirationDate { get; set; } + public bool IsRegistered { get; set; } + public bool RegChecked { get; set; } + public bool RegError { get; set; } + public bool TrialVersion { get; set; } + public bool IsValid { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs new file mode 100644 index 000000000..fc208459d --- /dev/null +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -0,0 +1,475 @@ +using System; +using System.Collections.Generic; +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.MediaInfo; +using System.Globalization; + +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Class MediaStream + /// </summary> + public class MediaStream + { + /// <summary> + /// Gets or sets the codec. + /// </summary> + /// <value>The codec.</value> + public string Codec { get; set; } + + /// <summary> + /// Gets or sets the codec tag. + /// </summary> + /// <value>The codec tag.</value> + public string CodecTag { get; set; } + + /// <summary> + /// Gets or sets the language. + /// </summary> + /// <value>The language.</value> + public string Language { get; set; } + + public string ColorTransfer { get; set; } + public string ColorPrimaries { get; set; } + public string ColorSpace { get; set; } + + /// <summary> + /// Gets or sets the comment. + /// </summary> + /// <value>The comment.</value> + public string Comment { get; set; } + + public string TimeBase { get; set; } + public string CodecTimeBase { get; set; } + + public string Title { get; set; } + + public string VideoRange + { + get + { + if (Type != MediaStreamType.Video) + { + return null; + } + + var colorTransfer = ColorTransfer; + + if (string.Equals(colorTransfer, "smpte2084", StringComparison.OrdinalIgnoreCase)) + { + return "HDR"; + } + + return "SDR"; + } + } + + public string DisplayTitle + { + get + { + if (Type == MediaStreamType.Audio) + { + //if (!string.IsNullOrEmpty(Title)) + //{ + // return AddLanguageIfNeeded(Title); + //} + + List<string> attributes = new List<string>(); + + if (!string.IsNullOrEmpty(Language)) + { + attributes.Add(StringHelper.FirstToUpper(Language)); + } + if (!string.IsNullOrEmpty(Codec) && !StringHelper.EqualsIgnoreCase(Codec, "dca")) + { + attributes.Add(AudioCodec.GetFriendlyName(Codec)); + } + else if (!string.IsNullOrEmpty(Profile) && !StringHelper.EqualsIgnoreCase(Profile, "lc")) + { + attributes.Add(Profile); + } + + if (!string.IsNullOrEmpty(ChannelLayout)) + { + attributes.Add(ChannelLayout); + } + else if (Channels.HasValue) + { + attributes.Add(Channels.Value.ToString(CultureInfo.InvariantCulture) + " ch"); + } + if (IsDefault) + { + attributes.Add("Default"); + } + + return string.Join(" ", attributes.ToArray(attributes.Count)); + } + + if (Type == MediaStreamType.Video) + { + List<string> attributes = new List<string>(); + + var resolutionText = GetResolutionText(); + + if (!string.IsNullOrEmpty(resolutionText)) + { + attributes.Add(resolutionText); + } + + if (!string.IsNullOrEmpty(Codec)) + { + attributes.Add(Codec.ToUpper()); + } + + return string.Join(" ", attributes.ToArray(attributes.Count)); + } + + if (Type == MediaStreamType.Subtitle) + { + //if (!string.IsNullOrEmpty(Title)) + //{ + // return AddLanguageIfNeeded(Title); + //} + + List<string> attributes = new List<string>(); + + if (!string.IsNullOrEmpty(Language)) + { + attributes.Add(StringHelper.FirstToUpper(Language)); + } + else + { + attributes.Add("Und"); + } + + if (IsDefault) + { + attributes.Add("Default"); + } + + if (IsForced) + { + attributes.Add("Forced"); + } + + string name = string.Join(" ", attributes.ToArray(attributes.Count)); + + return name; + } + + if (Type == MediaStreamType.Video) + { + + } + + return null; + } + } + + private string GetResolutionText() + { + var i = this; + + if (i.Width.HasValue && i.Height.HasValue) + { + var width = i.Width.Value; + var height = i.Height.Value; + + if (width >= 3800 || height >= 2000) + { + return "4K"; + } + if (width >= 2500) + { + if (i.IsInterlaced) + { + return "1440I"; + } + return "1440P"; + } + if (width >= 1900 || height >= 1000) + { + if (i.IsInterlaced) + { + return "1080I"; + } + return "1080P"; + } + if (width >= 1260 || height >= 700) + { + if (i.IsInterlaced) + { + return "720I"; + } + return "720P"; + } + if (width >= 700 || height >= 440) + { + + if (i.IsInterlaced) + { + return "480I"; + } + return "480P"; + } + + return "SD"; + } + return null; + } + + private string AddLanguageIfNeeded(string title) + { + if (!string.IsNullOrEmpty(Language) && + !string.Equals(Language, "und", StringComparison.OrdinalIgnoreCase) && + !IsLanguageInTitle(title, Language)) + { + title = StringHelper.FirstToUpper(Language) + " " + title; + } + + return title; + } + + private bool IsLanguageInTitle(string title, string language) + { + if (title.IndexOf(Language, StringComparison.OrdinalIgnoreCase) != -1) + { + return true; + } + + return false; + } + + public string NalLengthSize { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is interlaced. + /// </summary> + /// <value><c>true</c> if this instance is interlaced; otherwise, <c>false</c>.</value> + public bool IsInterlaced { get; set; } + + public bool? IsAVC { get; set; } + + /// <summary> + /// Gets or sets the channel layout. + /// </summary> + /// <value>The channel layout.</value> + public string ChannelLayout { get; set; } + + /// <summary> + /// Gets or sets the bit rate. + /// </summary> + /// <value>The bit rate.</value> + public int? BitRate { get; set; } + + /// <summary> + /// Gets or sets the bit depth. + /// </summary> + /// <value>The bit depth.</value> + public int? BitDepth { get; set; } + + /// <summary> + /// Gets or sets the reference frames. + /// </summary> + /// <value>The reference frames.</value> + public int? RefFrames { get; set; } + + /// <summary> + /// Gets or sets the length of the packet. + /// </summary> + /// <value>The length of the packet.</value> + public int? PacketLength { get; set; } + + /// <summary> + /// Gets or sets the channels. + /// </summary> + /// <value>The channels.</value> + public int? Channels { get; set; } + + /// <summary> + /// Gets or sets the sample rate. + /// </summary> + /// <value>The sample rate.</value> + public int? SampleRate { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is default. + /// </summary> + /// <value><c>true</c> if this instance is default; otherwise, <c>false</c>.</value> + public bool IsDefault { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is forced. + /// </summary> + /// <value><c>true</c> if this instance is forced; otherwise, <c>false</c>.</value> + public bool IsForced { get; set; } + + /// <summary> + /// Gets or sets the height. + /// </summary> + /// <value>The height.</value> + public int? Height { get; set; } + + /// <summary> + /// Gets or sets the width. + /// </summary> + /// <value>The width.</value> + public int? Width { get; set; } + + /// <summary> + /// Gets or sets the average frame rate. + /// </summary> + /// <value>The average frame rate.</value> + public float? AverageFrameRate { get; set; } + + /// <summary> + /// Gets or sets the real frame rate. + /// </summary> + /// <value>The real frame rate.</value> + public float? RealFrameRate { get; set; } + + /// <summary> + /// Gets or sets the profile. + /// </summary> + /// <value>The profile.</value> + public string Profile { get; set; } + + /// <summary> + /// Gets or sets the type. + /// </summary> + /// <value>The type.</value> + public MediaStreamType Type { get; set; } + + /// <summary> + /// Gets or sets the aspect ratio. + /// </summary> + /// <value>The aspect ratio.</value> + public string AspectRatio { get; set; } + + /// <summary> + /// Gets or sets the index. + /// </summary> + /// <value>The index.</value> + public int Index { get; set; } + + /// <summary> + /// Gets or sets the score. + /// </summary> + /// <value>The score.</value> + public int? Score { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is external. + /// </summary> + /// <value><c>true</c> if this instance is external; otherwise, <c>false</c>.</value> + public bool IsExternal { get; set; } + + /// <summary> + /// Gets or sets the method. + /// </summary> + /// <value>The method.</value> + public SubtitleDeliveryMethod? DeliveryMethod { get; set; } + /// <summary> + /// Gets or sets the delivery URL. + /// </summary> + /// <value>The delivery URL.</value> + public string DeliveryUrl { get; set; } + /// <summary> + /// Gets or sets a value indicating whether this instance is external URL. + /// </summary> + /// <value><c>null</c> if [is external URL] contains no value, <c>true</c> if [is external URL]; otherwise, <c>false</c>.</value> + public bool? IsExternalUrl { get; set; } + + public bool IsTextSubtitleStream + { + get + { + if (Type != MediaStreamType.Subtitle) return false; + + if (string.IsNullOrEmpty(Codec) && !IsExternal) + { + return false; + } + + return IsTextFormat(Codec); + } + } + + public static bool IsTextFormat(string format) + { + string codec = format ?? string.Empty; + + // sub = external .sub file + + return codec.IndexOf("pgs", StringComparison.OrdinalIgnoreCase) == -1 && + codec.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) == -1 && + codec.IndexOf("dvbsub", StringComparison.OrdinalIgnoreCase) == -1 && + !StringHelper.EqualsIgnoreCase(codec, "sub") && + !StringHelper.EqualsIgnoreCase(codec, "dvb_subtitle"); + } + + public bool SupportsSubtitleConversionTo(string toCodec) + { + if (!IsTextSubtitleStream) + { + return false; + } + + var fromCodec = Codec; + + // Can't convert from this + if (StringHelper.EqualsIgnoreCase(fromCodec, "ass")) + { + return false; + } + if (StringHelper.EqualsIgnoreCase(fromCodec, "ssa")) + { + return false; + } + + // Can't convert to this + if (StringHelper.EqualsIgnoreCase(toCodec, "ass")) + { + return false; + } + if (StringHelper.EqualsIgnoreCase(toCodec, "ssa")) + { + return false; + } + + return true; + } + + /// <summary> + /// Gets or sets a value indicating whether [supports external stream]. + /// </summary> + /// <value><c>true</c> if [supports external stream]; otherwise, <c>false</c>.</value> + public bool SupportsExternalStream { get; set; } + + /// <summary> + /// Gets or sets the filename. + /// </summary> + /// <value>The filename.</value> + public string Path { get; set; } + + /// <summary> + /// Gets or sets the pixel format. + /// </summary> + /// <value>The pixel format.</value> + public string PixelFormat { get; set; } + + /// <summary> + /// Gets or sets the level. + /// </summary> + /// <value>The level.</value> + public double? Level { get; set; } + + /// <summary> + /// Gets a value indicating whether this instance is anamorphic. + /// </summary> + /// <value><c>true</c> if this instance is anamorphic; otherwise, <c>false</c>.</value> + public bool? IsAnamorphic { get; set; } + } +} diff --git a/MediaBrowser.Model/Entities/MediaStreamType.cs b/MediaBrowser.Model/Entities/MediaStreamType.cs new file mode 100644 index 000000000..084a411f9 --- /dev/null +++ b/MediaBrowser.Model/Entities/MediaStreamType.cs @@ -0,0 +1,25 @@ +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Enum MediaStreamType + /// </summary> + public enum MediaStreamType + { + /// <summary> + /// The audio + /// </summary> + Audio, + /// <summary> + /// The video + /// </summary> + Video, + /// <summary> + /// The subtitle + /// </summary> + Subtitle, + /// <summary> + /// The embedded image + /// </summary> + EmbeddedImage + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Entities/MediaType.cs b/MediaBrowser.Model/Entities/MediaType.cs new file mode 100644 index 000000000..0c9bde6fb --- /dev/null +++ b/MediaBrowser.Model/Entities/MediaType.cs @@ -0,0 +1,30 @@ + +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Class MediaType + /// </summary> + public class MediaType + { + /// <summary> + /// The video + /// </summary> + public const string Video = "Video"; + /// <summary> + /// The audio + /// </summary> + public const string Audio = "Audio"; + /// <summary> + /// The game + /// </summary> + public const string Game = "Game"; + /// <summary> + /// The photo + /// </summary> + public const string Photo = "Photo"; + /// <summary> + /// The book + /// </summary> + public const string Book = "Book"; + } +} diff --git a/MediaBrowser.Model/Entities/MediaUrl.cs b/MediaBrowser.Model/Entities/MediaUrl.cs new file mode 100644 index 000000000..2e17bba8a --- /dev/null +++ b/MediaBrowser.Model/Entities/MediaUrl.cs @@ -0,0 +1,9 @@ + +namespace MediaBrowser.Model.Entities +{ + public class MediaUrl + { + public string Url { get; set; } + public string Name { get; set; } + } +} diff --git a/MediaBrowser.Model/Entities/MetadataFields.cs b/MediaBrowser.Model/Entities/MetadataFields.cs new file mode 100644 index 000000000..85f2da31e --- /dev/null +++ b/MediaBrowser.Model/Entities/MetadataFields.cs @@ -0,0 +1,46 @@ + +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Enum MetadataFields + /// </summary> + public enum MetadataFields + { + /// <summary> + /// The cast + /// </summary> + Cast, + /// <summary> + /// The genres + /// </summary> + Genres, + /// <summary> + /// The production locations + /// </summary> + ProductionLocations, + /// <summary> + /// The studios + /// </summary> + Studios, + /// <summary> + /// The tags + /// </summary> + Tags, + /// <summary> + /// The name + /// </summary> + Name, + /// <summary> + /// The overview + /// </summary> + Overview, + /// <summary> + /// The runtime + /// </summary> + Runtime, + /// <summary> + /// The official rating + /// </summary> + OfficialRating + } +} diff --git a/MediaBrowser.Model/Entities/MetadataProviders.cs b/MediaBrowser.Model/Entities/MetadataProviders.cs new file mode 100644 index 000000000..efd4339d5 --- /dev/null +++ b/MediaBrowser.Model/Entities/MetadataProviders.cs @@ -0,0 +1,41 @@ + +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Enum MetadataProviders + /// </summary> + public enum MetadataProviders + { + Gamesdb = 1, + /// <summary> + /// The imdb + /// </summary> + Imdb = 2, + /// <summary> + /// The TMDB + /// </summary> + Tmdb = 3, + /// <summary> + /// The TVDB + /// </summary> + Tvdb = 4, + /// <summary> + /// The tvcom + /// </summary> + Tvcom = 5, + /// <summary> + /// Tmdb Collection Id + /// </summary> + TmdbCollection = 7, + MusicBrainzAlbum = 8, + MusicBrainzAlbumArtist = 9, + MusicBrainzArtist = 10, + MusicBrainzReleaseGroup = 11, + Zap2It = 12, + TvRage = 15, + AudioDbArtist = 16, + AudioDbAlbum = 17, + MusicBrainzTrack = 18, + TvMaze = 19 + } +} diff --git a/MediaBrowser.Model/Entities/PackageReviewInfo.cs b/MediaBrowser.Model/Entities/PackageReviewInfo.cs new file mode 100644 index 000000000..52500a41e --- /dev/null +++ b/MediaBrowser.Model/Entities/PackageReviewInfo.cs @@ -0,0 +1,38 @@ +using System; + +namespace MediaBrowser.Model.Entities +{ + public class PackageReviewInfo + { + /// <summary> + /// The package id (database key) for this review + /// </summary> + public int id { get; set; } + + /// <summary> + /// The rating value + /// </summary> + public int rating { get; set; } + + /// <summary> + /// Whether or not this review recommends this item + /// </summary> + public bool recommend { get; set; } + + /// <summary> + /// A short description of the review + /// </summary> + public string title { get; set; } + + /// <summary> + /// A full review + /// </summary> + public string review { get; set; } + + /// <summary> + /// Time of review + /// </summary> + public DateTime timestamp { get; set; } + + } +} diff --git a/MediaBrowser.Model/Entities/ParentalRating.cs b/MediaBrowser.Model/Entities/ParentalRating.cs new file mode 100644 index 000000000..302c1e299 --- /dev/null +++ b/MediaBrowser.Model/Entities/ParentalRating.cs @@ -0,0 +1,32 @@ + +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Class ParentalRating + /// </summary> + public class ParentalRating + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the value. + /// </summary> + /// <value>The value.</value> + public int Value { get; set; } + + public ParentalRating() + { + + } + + public ParentalRating(string name, int value) + { + Name = name; + Value = value; + } + } +} diff --git a/MediaBrowser.Model/Entities/PersonType.cs b/MediaBrowser.Model/Entities/PersonType.cs new file mode 100644 index 000000000..bc274972d --- /dev/null +++ b/MediaBrowser.Model/Entities/PersonType.cs @@ -0,0 +1,42 @@ + +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Struct PersonType + /// </summary> + public class PersonType + { + /// <summary> + /// The actor + /// </summary> + public const string Actor = "Actor"; + /// <summary> + /// The director + /// </summary> + public const string Director = "Director"; + /// <summary> + /// The composer + /// </summary> + public const string Composer = "Composer"; + /// <summary> + /// The writer + /// </summary> + public const string Writer = "Writer"; + /// <summary> + /// The guest star + /// </summary> + public const string GuestStar = "GuestStar"; + /// <summary> + /// The producer + /// </summary> + public const string Producer = "Producer"; + /// <summary> + /// The conductor + /// </summary> + public const string Conductor = "Conductor"; + /// <summary> + /// The lyricist + /// </summary> + public const string Lyricist = "Lyricist"; + } +} diff --git a/MediaBrowser.Model/Entities/PluginSecurityInfo.cs b/MediaBrowser.Model/Entities/PluginSecurityInfo.cs new file mode 100644 index 000000000..5cab55013 --- /dev/null +++ b/MediaBrowser.Model/Entities/PluginSecurityInfo.cs @@ -0,0 +1,21 @@ + +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Class PluginSecurityInfo + /// </summary> + public class PluginSecurityInfo + { + /// <summary> + /// Gets or sets the supporter key. + /// </summary> + /// <value>The supporter key.</value> + public string SupporterKey { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is MB supporter. + /// </summary> + /// <value><c>true</c> if this instance is MB supporter; otherwise, <c>false</c>.</value> + public bool IsMBSupporter { get; set; } + } +} diff --git a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs new file mode 100644 index 000000000..e10232baa --- /dev/null +++ b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Class ProviderIdsExtensions + /// </summary> + public static class ProviderIdsExtensions + { + /// <summary> + /// Determines whether [has provider identifier] [the specified instance]. + /// </summary> + /// <param name="instance">The instance.</param> + /// <param name="provider">The provider.</param> + /// <returns><c>true</c> if [has provider identifier] [the specified instance]; otherwise, <c>false</c>.</returns> + public static bool HasProviderId(this IHasProviderIds instance, MetadataProviders provider) + { + return !string.IsNullOrEmpty(instance.GetProviderId(provider.ToString())); + } + + /// <summary> + /// Gets a provider id + /// </summary> + /// <param name="instance">The instance.</param> + /// <param name="provider">The provider.</param> + /// <returns>System.String.</returns> + public static string GetProviderId(this IHasProviderIds instance, MetadataProviders provider) + { + return instance.GetProviderId(provider.ToString()); + } + + /// <summary> + /// Gets a provider id + /// </summary> + /// <param name="instance">The instance.</param> + /// <param name="name">The name.</param> + /// <returns>System.String.</returns> + public static string GetProviderId(this IHasProviderIds instance, string name) + { + if (instance == null) + { + throw new ArgumentNullException("instance"); + } + + if (instance.ProviderIds == null) + { + return null; + } + + string id; + instance.ProviderIds.TryGetValue(name, out id); + return id; + } + + /// <summary> + /// Sets a provider id + /// </summary> + /// <param name="instance">The instance.</param> + /// <param name="name">The name.</param> + /// <param name="value">The value.</param> + public static void SetProviderId(this IHasProviderIds instance, string name, string value) + { + if (instance == null) + { + throw new ArgumentNullException("instance"); + } + + // If it's null remove the key from the dictionary + if (string.IsNullOrEmpty(value)) + { + if (instance.ProviderIds != null) + { + if (instance.ProviderIds.ContainsKey(name)) + { + instance.ProviderIds.Remove(name); + } + } + } + else + { + // Ensure it exists + if (instance.ProviderIds == null) + { + instance.ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + } + + instance.ProviderIds[name] = value; + } + } + + /// <summary> + /// Sets a provider id + /// </summary> + /// <param name="instance">The instance.</param> + /// <param name="provider">The provider.</param> + /// <param name="value">The value.</param> + public static void SetProviderId(this IHasProviderIds instance, MetadataProviders provider, string value) + { + instance.SetProviderId(provider.ToString(), value); + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Entities/ScrollDirection.cs b/MediaBrowser.Model/Entities/ScrollDirection.cs new file mode 100644 index 000000000..ed2210300 --- /dev/null +++ b/MediaBrowser.Model/Entities/ScrollDirection.cs @@ -0,0 +1,17 @@ +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Enum ScrollDirection + /// </summary> + public enum ScrollDirection + { + /// <summary> + /// The horizontal + /// </summary> + Horizontal, + /// <summary> + /// The vertical + /// </summary> + Vertical + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Entities/SeriesStatus.cs b/MediaBrowser.Model/Entities/SeriesStatus.cs new file mode 100644 index 000000000..d04a2856c --- /dev/null +++ b/MediaBrowser.Model/Entities/SeriesStatus.cs @@ -0,0 +1,18 @@ + +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Enum SeriesStatus + /// </summary> + public enum SeriesStatus + { + /// <summary> + /// The continuing + /// </summary> + Continuing, + /// <summary> + /// The ended + /// </summary> + Ended + } +} diff --git a/MediaBrowser.Model/Entities/SortOrder.cs b/MediaBrowser.Model/Entities/SortOrder.cs new file mode 100644 index 000000000..5130449ba --- /dev/null +++ b/MediaBrowser.Model/Entities/SortOrder.cs @@ -0,0 +1,17 @@ +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Enum SortOrder + /// </summary> + public enum SortOrder + { + /// <summary> + /// The ascending + /// </summary> + Ascending, + /// <summary> + /// The descending + /// </summary> + Descending + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Entities/TrailerType.cs b/MediaBrowser.Model/Entities/TrailerType.cs new file mode 100644 index 000000000..085f461cf --- /dev/null +++ b/MediaBrowser.Model/Entities/TrailerType.cs @@ -0,0 +1,11 @@ +namespace MediaBrowser.Model.Entities +{ + public enum TrailerType + { + ComingSoonToTheaters = 1, + ComingSoonToDvd = 2, + ComingSoonToStreaming = 3, + Archive = 4, + LocalTrailer = 5 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Entities/UserDataSaveReason.cs b/MediaBrowser.Model/Entities/UserDataSaveReason.cs new file mode 100644 index 000000000..d9691f395 --- /dev/null +++ b/MediaBrowser.Model/Entities/UserDataSaveReason.cs @@ -0,0 +1,34 @@ + +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Enum UserDataSaveReason + /// </summary> + public enum UserDataSaveReason + { + /// <summary> + /// The playback start + /// </summary> + PlaybackStart = 1, + /// <summary> + /// The playback progress + /// </summary> + PlaybackProgress = 2, + /// <summary> + /// The playback finished + /// </summary> + PlaybackFinished = 3, + /// <summary> + /// The toggle played + /// </summary> + TogglePlayed = 4, + /// <summary> + /// The update user rating + /// </summary> + UpdateUserRating = 5, + /// <summary> + /// The import + /// </summary> + Import = 6 + } +} diff --git a/MediaBrowser.Model/Entities/Video3DFormat.cs b/MediaBrowser.Model/Entities/Video3DFormat.cs new file mode 100644 index 000000000..722df4281 --- /dev/null +++ b/MediaBrowser.Model/Entities/Video3DFormat.cs @@ -0,0 +1,12 @@ + +namespace MediaBrowser.Model.Entities +{ + public enum Video3DFormat + { + HalfSideBySide, + FullSideBySide, + FullTopAndBottom, + HalfTopAndBottom, + MVC + } +} diff --git a/MediaBrowser.Model/Entities/VideoType.cs b/MediaBrowser.Model/Entities/VideoType.cs new file mode 100644 index 000000000..05c2fa32c --- /dev/null +++ b/MediaBrowser.Model/Entities/VideoType.cs @@ -0,0 +1,26 @@ + +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Enum VideoType + /// </summary> + public enum VideoType + { + /// <summary> + /// The video file + /// </summary> + VideoFile, + /// <summary> + /// The iso + /// </summary> + Iso, + /// <summary> + /// The DVD + /// </summary> + Dvd, + /// <summary> + /// The blu ray + /// </summary> + BluRay + } +} diff --git a/MediaBrowser.Model/Entities/VirtualFolderInfo.cs b/MediaBrowser.Model/Entities/VirtualFolderInfo.cs new file mode 100644 index 000000000..68f713295 --- /dev/null +++ b/MediaBrowser.Model/Entities/VirtualFolderInfo.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using MediaBrowser.Model.Configuration; +using System; + +namespace MediaBrowser.Model.Entities +{ + /// <summary> + /// Used to hold information about a user's list of configured virtual folders + /// </summary> + public class VirtualFolderInfo + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the locations. + /// </summary> + /// <value>The locations.</value> + public string[] Locations { get; set; } + + /// <summary> + /// Gets or sets the type of the collection. + /// </summary> + /// <value>The type of the collection.</value> + public string CollectionType { get; set; } + + public LibraryOptions LibraryOptions { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="VirtualFolderInfo"/> class. + /// </summary> + public VirtualFolderInfo() + { + Locations = new string[] {}; + } + + /// <summary> + /// Gets or sets the item identifier. + /// </summary> + /// <value>The item identifier.</value> + public string ItemId { get; set; } + + /// <summary> + /// Gets or sets the primary image item identifier. + /// </summary> + /// <value>The primary image item identifier.</value> + public string PrimaryImageItemId { get; set; } + + public double? RefreshProgress { get; set; } + public string RefreshStatus { get; set; } + } +} diff --git a/MediaBrowser.Model/Events/GenericEventArgs.cs b/MediaBrowser.Model/Events/GenericEventArgs.cs new file mode 100644 index 000000000..3c558577a --- /dev/null +++ b/MediaBrowser.Model/Events/GenericEventArgs.cs @@ -0,0 +1,33 @@ +using System; + +namespace MediaBrowser.Model.Events +{ + /// <summary> + /// Provides a generic EventArgs subclass that can hold any kind of object + /// </summary> + /// <typeparam name="T"></typeparam> + public class GenericEventArgs<T> : EventArgs + { + /// <summary> + /// Gets or sets the argument. + /// </summary> + /// <value>The argument.</value> + public T Argument { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="GenericEventArgs{T}"/> class. + /// </summary> + /// <param name="arg">The argument.</param> + public GenericEventArgs(T arg) + { + Argument = arg; + } + + /// <summary> + /// Initializes a new instance of the <see cref="GenericEventArgs{T}"/> class. + /// </summary> + public GenericEventArgs() + { + } + } +} diff --git a/MediaBrowser.Model/Extensions/LinqExtensions.cs b/MediaBrowser.Model/Extensions/LinqExtensions.cs new file mode 100644 index 000000000..09ace42e8 --- /dev/null +++ b/MediaBrowser.Model/Extensions/LinqExtensions.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Model.Extensions +{ + // MoreLINQ - Extensions to LINQ to Objects + // Copyright (c) 2008 Jonathan Skeet. All rights reserved. + // + // Licensed under the Apache License, Version 2.0 (the "License"); + // you may not use this file except in compliance with the License. + // You may obtain a copy of the License at + // + // http://www.apache.org/licenses/LICENSE-2.0 + // + // Unless required by applicable law or agreed to in writing, software + // distributed under the License is distributed on an "AS IS" BASIS, + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + // See the License for the specific language governing permissions and + // limitations under the License. + + public static class LinqExtensions + { + /// <summary> + /// Returns all distinct elements of the given source, where "distinctness" + /// is determined via a projection and the default equality comparer for the projected type. + /// </summary> + /// <remarks> + /// This operator uses deferred execution and streams the results, although + /// a set of already-seen keys is retained. If a key is seen multiple times, + /// only the first element with that key is returned. + /// </remarks> + /// <typeparam name="TSource">Type of the source sequence</typeparam> + /// <typeparam name="TKey">Type of the projected element</typeparam> + /// <param name="source">Source sequence</param> + /// <param name="keySelector">Projection for determining "distinctness"</param> + /// <returns>A sequence consisting of distinct elements from the source sequence, + /// comparing them by the specified key projection.</returns> + + public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, + Func<TSource, TKey> keySelector) + { + return source.DistinctBy(keySelector, null); + } + + public static TSource[] ToArray<TSource>(this IEnumerable<TSource> source, int count) + { + if (source == null) throw new ArgumentNullException("source"); + if (count < 0) throw new ArgumentOutOfRangeException("count"); + var array = new TSource[count]; + int i = 0; + foreach (var item in source) + { + array[i++] = item; + } + return array; + } + + /// <summary> + /// Returns all distinct elements of the given source, where "distinctness" + /// is determined via a projection and the specified comparer for the projected type. + /// </summary> + /// <remarks> + /// This operator uses deferred execution and streams the results, although + /// a set of already-seen keys is retained. If a key is seen multiple times, + /// only the first element with that key is returned. + /// </remarks> + /// <typeparam name="TSource">Type of the source sequence</typeparam> + /// <typeparam name="TKey">Type of the projected element</typeparam> + /// <param name="source">Source sequence</param> + /// <param name="keySelector">Projection for determining "distinctness"</param> + /// <param name="comparer">The equality comparer to use to determine whether or not keys are equal. + /// If null, the default equality comparer for <c>TSource</c> is used.</param> + /// <returns>A sequence consisting of distinct elements from the source sequence, + /// comparing them by the specified key projection.</returns> + + public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, + Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer) + { + if (source == null) throw new ArgumentNullException("source"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + return DistinctByImpl(source, keySelector, comparer); + } + + private static IEnumerable<TSource> DistinctByImpl<TSource, TKey>(IEnumerable<TSource> source, + Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer) + { + var knownKeys = new HashSet<TKey>(comparer); + foreach (var element in source) + { + if (knownKeys.Add(keySelector(element))) + { + yield return element; + } + } + } + } +} diff --git a/MediaBrowser.Model/Extensions/ListHelper.cs b/MediaBrowser.Model/Extensions/ListHelper.cs new file mode 100644 index 000000000..243ae3105 --- /dev/null +++ b/MediaBrowser.Model/Extensions/ListHelper.cs @@ -0,0 +1,24 @@ +using System; + +namespace MediaBrowser.Model.Extensions +{ + public static class ListHelper + { + public static bool ContainsIgnoreCase(string[] list, string value) + { + if (value == null) + { + throw new ArgumentNullException("value"); + } + + foreach (var item in list) + { + if (string.Equals(item, value, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + return false; + } + } +} diff --git a/MediaBrowser.Model/Extensions/StringHelper.cs b/MediaBrowser.Model/Extensions/StringHelper.cs new file mode 100644 index 000000000..fa79d09db --- /dev/null +++ b/MediaBrowser.Model/Extensions/StringHelper.cs @@ -0,0 +1,57 @@ +using System; +using System.Text; + +namespace MediaBrowser.Model.Extensions +{ + /// <summary> + /// Isolating these helpers allow this entire project to be easily converted to Java + /// </summary> + public static class StringHelper + { + /// <summary> + /// Equalses the ignore case. + /// </summary> + /// <param name="str1">The STR1.</param> + /// <param name="str2">The STR2.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> + public static bool EqualsIgnoreCase(string str1, string str2) + { + return string.Equals(str1, str2, StringComparison.OrdinalIgnoreCase); + } + + /// <summary> + /// Replaces the specified STR. + /// </summary> + /// <param name="str">The STR.</param> + /// <param name="oldValue">The old value.</param> + /// <param name="newValue">The new value.</param> + /// <param name="comparison">The comparison.</param> + /// <returns>System.String.</returns> + public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison) + { + var sb = new StringBuilder(); + + var previousIndex = 0; + var index = str.IndexOf(oldValue, comparison); + + while (index != -1) + { + sb.Append(str.Substring(previousIndex, index - previousIndex)); + sb.Append(newValue); + index += oldValue.Length; + + previousIndex = index; + index = str.IndexOf(oldValue, index, comparison); + } + + sb.Append(str.Substring(previousIndex)); + + return sb.ToString(); + } + + public static string FirstToUpper(this string str) + { + return string.IsNullOrEmpty(str) ? "" : str.Substring(0, 1).ToUpper() + str.Substring(1); + } + } +} diff --git a/MediaBrowser.Model/Globalization/CountryInfo.cs b/MediaBrowser.Model/Globalization/CountryInfo.cs new file mode 100644 index 000000000..16aea8436 --- /dev/null +++ b/MediaBrowser.Model/Globalization/CountryInfo.cs @@ -0,0 +1,33 @@ + +namespace MediaBrowser.Model.Globalization +{ + /// <summary> + /// Class CountryInfo + /// </summary> + public class CountryInfo + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the display name. + /// </summary> + /// <value>The display name.</value> + public string DisplayName { get; set; } + + /// <summary> + /// Gets or sets the name of the two letter ISO region. + /// </summary> + /// <value>The name of the two letter ISO region.</value> + public string TwoLetterISORegionName { get; set; } + + /// <summary> + /// Gets or sets the name of the three letter ISO region. + /// </summary> + /// <value>The name of the three letter ISO region.</value> + public string ThreeLetterISORegionName { get; set; } + } +} diff --git a/MediaBrowser.Model/Globalization/CultureDto.cs b/MediaBrowser.Model/Globalization/CultureDto.cs new file mode 100644 index 000000000..6d79aaebb --- /dev/null +++ b/MediaBrowser.Model/Globalization/CultureDto.cs @@ -0,0 +1,52 @@ +using global::System; + +namespace MediaBrowser.Model.Globalization +{ + /// <summary> + /// Class CultureDto + /// </summary> + public class CultureDto + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the display name. + /// </summary> + /// <value>The display name.</value> + public string DisplayName { get; set; } + + /// <summary> + /// Gets or sets the name of the two letter ISO language. + /// </summary> + /// <value>The name of the two letter ISO language.</value> + public string TwoLetterISOLanguageName { get; set; } + + /// <summary> + /// Gets or sets the name of the three letter ISO language. + /// </summary> + /// <value>The name of the three letter ISO language.</value> + public string ThreeLetterISOLanguageName + { + get + { + var vals = ThreeLetterISOLanguageNames; + if (vals.Length > 0) + { + return vals[0]; + } + return null; + } + } + + public string[] ThreeLetterISOLanguageNames { get; set; } + + public CultureDto() + { + ThreeLetterISOLanguageNames = new string[] {}; + } + } +} diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs new file mode 100644 index 000000000..9c7a937f3 --- /dev/null +++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using MediaBrowser.Model.Entities; +using System.Globalization; + +namespace MediaBrowser.Model.Globalization +{ + /// <summary> + /// Interface ILocalizationManager + /// </summary> + public interface ILocalizationManager + { + /// <summary> + /// Gets the cultures. + /// </summary> + /// <returns>IEnumerable{CultureDto}.</returns> + CultureDto[] GetCultures(); + /// <summary> + /// Gets the countries. + /// </summary> + /// <returns>IEnumerable{CountryInfo}.</returns> + CountryInfo[] GetCountries(); + /// <summary> + /// Gets the parental ratings. + /// </summary> + /// <returns>IEnumerable{ParentalRating}.</returns> + ParentalRating[] GetParentalRatings(); + /// <summary> + /// Gets the rating level. + /// </summary> + /// <param name="rating">The rating.</param> + /// <returns>System.Int32.</returns> + int? GetRatingLevel(string rating); + + /// <summary> + /// Gets the localized string. + /// </summary> + /// <param name="phrase">The phrase.</param> + /// <param name="culture">The culture.</param> + /// <returns>System.String.</returns> + string GetLocalizedString(string phrase, string culture); + + /// <summary> + /// Gets the localized string. + /// </summary> + /// <param name="phrase">The phrase.</param> + /// <returns>System.String.</returns> + string GetLocalizedString(string phrase); + + /// <summary> + /// Gets the localization options. + /// </summary> + /// <returns>IEnumerable{LocalizatonOption}.</returns> + LocalizatonOption[] GetLocalizationOptions(); + + string RemoveDiacritics(string text); + + string NormalizeFormKD(string text); + + bool HasUnicodeCategory(string value, UnicodeCategory category); + + CultureDto FindLanguageInfo(string language); + } +} diff --git a/MediaBrowser.Model/Globalization/LocalizatonOption.cs b/MediaBrowser.Model/Globalization/LocalizatonOption.cs new file mode 100644 index 000000000..61749cbc3 --- /dev/null +++ b/MediaBrowser.Model/Globalization/LocalizatonOption.cs @@ -0,0 +1,8 @@ +namespace MediaBrowser.Model.Globalization +{ + public class LocalizatonOption + { + public string Name { get; set; } + public string Value { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/IO/FileSystemEntryInfo.cs b/MediaBrowser.Model/IO/FileSystemEntryInfo.cs new file mode 100644 index 000000000..f17e2e5c3 --- /dev/null +++ b/MediaBrowser.Model/IO/FileSystemEntryInfo.cs @@ -0,0 +1,27 @@ + +namespace MediaBrowser.Model.IO +{ + /// <summary> + /// Class FileSystemEntryInfo + /// </summary> + public class FileSystemEntryInfo + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the path. + /// </summary> + /// <value>The path.</value> + public string Path { get; set; } + + /// <summary> + /// Gets or sets the type. + /// </summary> + /// <value>The type.</value> + public FileSystemEntryType Type { get; set; } + } +} diff --git a/MediaBrowser.Model/IO/FileSystemEntryType.cs b/MediaBrowser.Model/IO/FileSystemEntryType.cs new file mode 100644 index 000000000..e7c67c606 --- /dev/null +++ b/MediaBrowser.Model/IO/FileSystemEntryType.cs @@ -0,0 +1,25 @@ +namespace MediaBrowser.Model.IO +{ + /// <summary> + /// Enum FileSystemEntryType + /// </summary> + public enum FileSystemEntryType + { + /// <summary> + /// The file + /// </summary> + File, + /// <summary> + /// The directory + /// </summary> + Directory, + /// <summary> + /// The network computer + /// </summary> + NetworkComputer, + /// <summary> + /// The network share + /// </summary> + NetworkShare + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/IO/FileSystemMetadata.cs b/MediaBrowser.Model/IO/FileSystemMetadata.cs new file mode 100644 index 000000000..665bc255c --- /dev/null +++ b/MediaBrowser.Model/IO/FileSystemMetadata.cs @@ -0,0 +1,54 @@ +using System; + +namespace MediaBrowser.Model.IO +{ + public class FileSystemMetadata + { + /// <summary> + /// Gets or sets a value indicating whether this <see cref="FileSystemMetadata"/> is exists. + /// </summary> + /// <value><c>true</c> if exists; otherwise, <c>false</c>.</value> + public bool Exists { get; set; } + /// <summary> + /// Gets or sets the full name. + /// </summary> + /// <value>The full name.</value> + public string FullName { get; set; } + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + /// <summary> + /// Gets or sets the extension. + /// </summary> + /// <value>The extension.</value> + public string Extension { get; set; } + /// <summary> + /// Gets or sets the length. + /// </summary> + /// <value>The length.</value> + public long Length { get; set; } + /// <summary> + /// Gets or sets the name of the directory. + /// </summary> + /// <value>The name of the directory.</value> + public string DirectoryName { get; set; } + + /// <summary> + /// Gets or sets the last write time UTC. + /// </summary> + /// <value>The last write time UTC.</value> + public DateTime LastWriteTimeUtc { get; set; } + /// <summary> + /// Gets or sets the creation time UTC. + /// </summary> + /// <value>The creation time UTC.</value> + public DateTime CreationTimeUtc { get; set; } + /// <summary> + /// Gets a value indicating whether this instance is directory. + /// </summary> + /// <value><c>true</c> if this instance is directory; otherwise, <c>false</c>.</value> + public bool IsDirectory { get; set; } + } +} diff --git a/MediaBrowser.Model/IO/IFileSystem.cs b/MediaBrowser.Model/IO/IFileSystem.cs new file mode 100644 index 000000000..3200affd6 --- /dev/null +++ b/MediaBrowser.Model/IO/IFileSystem.cs @@ -0,0 +1,454 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace MediaBrowser.Model.IO +{ + /// <summary> + /// Interface IFileSystem + /// </summary> + public interface IFileSystem + { + void AddShortcutHandler(IShortcutHandler handler); + + /// <summary> + /// Determines whether the specified filename is shortcut. + /// </summary> + /// <param name="filename">The filename.</param> + /// <returns><c>true</c> if the specified filename is shortcut; otherwise, <c>false</c>.</returns> + bool IsShortcut(string filename); + + /// <summary> + /// Resolves the shortcut. + /// </summary> + /// <param name="filename">The filename.</param> + /// <returns>System.String.</returns> + string ResolveShortcut(string filename); + + /// <summary> + /// Creates the shortcut. + /// </summary> + /// <param name="shortcutPath">The shortcut path.</param> + /// <param name="target">The target.</param> + void CreateShortcut(string shortcutPath, string target); + + string MakeAbsolutePath(string folderPath, string filePath); + + /// <summary> + /// Returns a <see cref="FileSystemMetadata"/> object for the specified file or directory path. + /// </summary> + /// <param name="path">A path to a file or directory.</param> + /// <returns>A <see cref="FileSystemMetadata"/> object.</returns> + /// <remarks>If the specified path points to a directory, the returned <see cref="FileSystemMetadata"/> object's + /// <see cref="FileSystemMetadata.IsDirectory"/> property will be set to true and all other properties will reflect the properties of the directory.</remarks> + FileSystemMetadata GetFileSystemInfo(string path); + + /// <summary> + /// Returns a <see cref="FileSystemMetadata"/> object for the specified file path. + /// </summary> + /// <param name="path">A path to a file.</param> + /// <returns>A <see cref="FileSystemMetadata"/> object.</returns> + /// <remarks><para>If the specified path points to a directory, the returned <see cref="FileSystemMetadata"/> object's + /// <see cref="FileSystemMetadata.IsDirectory"/> property and the <see cref="FileSystemMetadata.Exists"/> property will both be set to false.</para> + /// <para>For automatic handling of files <b>and</b> directories, use <see cref="GetFileSystemInfo"/>.</para></remarks> + FileSystemMetadata GetFileInfo(string path); + + /// <summary> + /// Returns a <see cref="FileSystemMetadata"/> object for the specified directory path. + /// </summary> + /// <param name="path">A path to a directory.</param> + /// <returns>A <see cref="FileSystemMetadata"/> object.</returns> + /// <remarks><para>If the specified path points to a file, the returned <see cref="FileSystemMetadata"/> object's + /// <see cref="FileSystemMetadata.IsDirectory"/> property will be set to true and the <see cref="FileSystemMetadata.Exists"/> property will be set to false.</para> + /// <para>For automatic handling of files <b>and</b> directories, use <see cref="GetFileSystemInfo"/>.</para></remarks> + FileSystemMetadata GetDirectoryInfo(string path); + + /// <summary> + /// Gets the valid filename. + /// </summary> + /// <param name="filename">The filename.</param> + /// <returns>System.String.</returns> + string GetValidFilename(string filename); + + /// <summary> + /// Gets the creation time UTC. + /// </summary> + /// <param name="info">The information.</param> + /// <returns>DateTime.</returns> + DateTime GetCreationTimeUtc(FileSystemMetadata info); + + /// <summary> + /// Gets the creation time UTC. + /// </summary> + /// <param name="path">The path.</param> + /// <returns>DateTime.</returns> + DateTime GetCreationTimeUtc(string path); + + /// <summary> + /// Gets the last write time UTC. + /// </summary> + /// <param name="info">The information.</param> + /// <returns>DateTime.</returns> + DateTime GetLastWriteTimeUtc(FileSystemMetadata info); + + /// <summary> + /// Gets the last write time UTC. + /// </summary> + /// <param name="path">The path.</param> + /// <returns>DateTime.</returns> + DateTime GetLastWriteTimeUtc(string path); + + /// <summary> + /// Gets the file stream. + /// </summary> + /// <param name="path">The path.</param> + /// <param name="mode">The mode.</param> + /// <param name="access">The access.</param> + /// <param name="share">The share.</param> + /// <param name="isAsync">if set to <c>true</c> [is asynchronous].</param> + /// <returns>FileStream.</returns> + Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, bool isAsync = false); + + Stream GetFileStream(string path, FileOpenMode mode, FileAccessMode access, FileShareMode share, FileOpenOptions fileOpenOptions); + + /// <summary> + /// Opens the read. + /// </summary> + /// <param name="path">The path.</param> + /// <returns>Stream.</returns> + Stream OpenRead(String path); + + string DefaultDirectory { get; } + + /// <summary> + /// Swaps the files. + /// </summary> + /// <param name="file1">The file1.</param> + /// <param name="file2">The file2.</param> + void SwapFiles(string file1, string file2); + + bool AreEqual(string path1, string path2); + + /// <summary> + /// Determines whether [contains sub path] [the specified parent path]. + /// </summary> + /// <param name="parentPath">The parent path.</param> + /// <param name="path">The path.</param> + /// <returns><c>true</c> if [contains sub path] [the specified parent path]; otherwise, <c>false</c>.</returns> + bool ContainsSubPath(string parentPath, string path); + + /// <summary> + /// Determines whether [is root path] [the specified path]. + /// </summary> + /// <param name="path">The path.</param> + /// <returns><c>true</c> if [is root path] [the specified path]; otherwise, <c>false</c>.</returns> + bool IsRootPath(string path); + + /// <summary> + /// Normalizes the path. + /// </summary> + /// <param name="path">The path.</param> + /// <returns>System.String.</returns> + string NormalizePath(string path); + + string GetDirectoryName(string path); + + /// <summary> + /// Gets the file name without extension. + /// </summary> + /// <param name="info">The information.</param> + /// <returns>System.String.</returns> + string GetFileNameWithoutExtension(FileSystemMetadata info); + + /// <summary> + /// Gets the file name without extension. + /// </summary> + /// <param name="path">The path.</param> + /// <returns>System.String.</returns> + string GetFileNameWithoutExtension(string path); + + /// <summary> + /// Determines whether [is path file] [the specified path]. + /// </summary> + /// <param name="path">The path.</param> + /// <returns><c>true</c> if [is path file] [the specified path]; otherwise, <c>false</c>.</returns> + bool IsPathFile(string path); + + /// <summary> + /// Deletes the file. + /// </summary> + /// <param name="path">The path.</param> + void DeleteFile(string path); + + /// <summary> + /// Deletes the directory. + /// </summary> + /// <param name="path">The path.</param> + /// <param name="recursive">if set to <c>true</c> [recursive].</param> + void DeleteDirectory(string path, bool recursive); + + /// <summary> + /// Gets the directories. + /// </summary> + /// <param name="path">The path.</param> + /// <param name="recursive">if set to <c>true</c> [recursive].</param> + /// <returns>IEnumerable<DirectoryInfo>.</returns> + IEnumerable<FileSystemMetadata> GetDirectories(string path, bool recursive = false); + + /// <summary> + /// Gets the files. + /// </summary> + IEnumerable<FileSystemMetadata> GetFiles(string path, bool recursive = false); + + IEnumerable<FileSystemMetadata> GetFiles(string path, string [] extensions, bool enableCaseSensitiveExtensions, bool recursive); + + /// <summary> + /// Gets the file system entries. + /// </summary> + /// <param name="path">The path.</param> + /// <param name="recursive">if set to <c>true</c> [recursive].</param> + /// <returns>IEnumerable<FileSystemMetadata>.</returns> + IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path, bool recursive = false); + + /// <summary> + /// Creates the directory. + /// </summary> + /// <param name="path">The path.</param> + void CreateDirectory(string path); + + /// <summary> + /// Copies the file. + /// </summary> + /// <param name="source">The source.</param> + /// <param name="target">The target.</param> + /// <param name="overwrite">if set to <c>true</c> [overwrite].</param> + void CopyFile(string source, string target, bool overwrite); + + /// <summary> + /// Moves the file. + /// </summary> + /// <param name="source">The source.</param> + /// <param name="target">The target.</param> + void MoveFile(string source, string target); + + /// <summary> + /// Moves the directory. + /// </summary> + /// <param name="source">The source.</param> + /// <param name="target">The target.</param> + void MoveDirectory(string source, string target); + + /// <summary> + /// Directories the exists. + /// </summary> + /// <param name="path">The path.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> + bool DirectoryExists(string path); + + /// <summary> + /// Files the exists. + /// </summary> + /// <param name="path">The path.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> + bool FileExists(string path); + + /// <summary> + /// Reads all text. + /// </summary> + /// <param name="path">The path.</param> + /// <returns>System.String.</returns> + string ReadAllText(string path); + + byte[] ReadAllBytes(string path); + + void WriteAllBytes(string path, byte[] bytes); + + /// <summary> + /// Writes all text. + /// </summary> + /// <param name="path">The path.</param> + /// <param name="text">The text.</param> + void WriteAllText(string path, string text); + + /// <summary> + /// Writes all text. + /// </summary> + /// <param name="path">The path.</param> + /// <param name="text">The text.</param> + /// <param name="encoding">The encoding.</param> + void WriteAllText(string path, string text, Encoding encoding); + + /// <summary> + /// Reads all text. + /// </summary> + /// <param name="path">The path.</param> + /// <param name="encoding">The encoding.</param> + /// <returns>System.String.</returns> + string ReadAllText(string path, Encoding encoding); + + string[] ReadAllLines(string path); + + void WriteAllLines(string path, IEnumerable<string> lines); + + /// <summary> + /// Gets the directory paths. + /// </summary> + /// <param name="path">The path.</param> + /// <param name="recursive">if set to <c>true</c> [recursive].</param> + /// <returns>IEnumerable<System.String>.</returns> + IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false); + + /// <summary> + /// Gets the file paths. + /// </summary> + /// <param name="path">The path.</param> + /// <param name="recursive">if set to <c>true</c> [recursive].</param> + /// <returns>IEnumerable<System.String>.</returns> + IEnumerable<string> GetFilePaths(string path, bool recursive = false); + IEnumerable<string> GetFilePaths(string path, string[] extensions, bool enableCaseSensitiveExtensions, bool recursive); + + /// <summary> + /// Gets the file system entry paths. + /// </summary> + /// <param name="path">The path.</param> + /// <param name="recursive">if set to <c>true</c> [recursive].</param> + /// <returns>IEnumerable<System.String>.</returns> + IEnumerable<string> GetFileSystemEntryPaths(string path, bool recursive = false); + + void SetHidden(string path, bool isHidden); + void SetReadOnly(string path, bool readOnly); + void SetAttributes(string path, bool isHidden, bool readOnly); + + char DirectorySeparatorChar { get; } + + string GetFullPath(string path); + + List<FileSystemMetadata> GetDrives(); + + void SetExecutable(string path); + } + + public enum FileOpenMode + { + // + // Summary: + // Specifies that the operating system should create a new file. This requires System.Security.Permissions.FileIOPermissionAccess.Write + // permission. If the file already exists, an System.IO.IOException exception is + // thrown. + CreateNew = 1, + // + // Summary: + // Specifies that the operating system should create a new file. If the file already + // exists, it will be overwritten. This requires System.Security.Permissions.FileIOPermissionAccess.Write + // permission. FileMode.Create is equivalent to requesting that if the file does + // not exist, use System.IO.FileMode.CreateNew; otherwise, use System.IO.FileMode.Truncate. + // If the file already exists but is a hidden file, an System.UnauthorizedAccessException + // exception is thrown. + Create = 2, + // + // Summary: + // Specifies that the operating system should open an existing file. The ability + // to open the file is dependent on the value specified by the System.IO.FileAccess + // enumeration. A System.IO.FileNotFoundException exception is thrown if the file + // does not exist. + Open = 3, + // + // Summary: + // Specifies that the operating system should open a file if it exists; otherwise, + // a new file should be created. If the file is opened with FileAccess.Read, System.Security.Permissions.FileIOPermissionAccess.Read + // permission is required. If the file access is FileAccess.Write, System.Security.Permissions.FileIOPermissionAccess.Write + // permission is required. If the file is opened with FileAccess.ReadWrite, both + // System.Security.Permissions.FileIOPermissionAccess.Read and System.Security.Permissions.FileIOPermissionAccess.Write + // permissions are required. + OpenOrCreate = 4 + } + + public enum FileAccessMode + { + // + // Summary: + // Read access to the file. Data can be read from the file. Combine with Write for + // read/write access. + Read = 1, + // + // Summary: + // Write access to the file. Data can be written to the file. Combine with Read + // for read/write access. + Write = 2 + } + + public enum FileShareMode + { + // + // Summary: + // Declines sharing of the current file. Any request to open the file (by this process + // or another process) will fail until the file is closed. + None = 0, + // + // Summary: + // Allows subsequent opening of the file for reading. If this flag is not specified, + // any request to open the file for reading (by this process or another process) + // will fail until the file is closed. However, even if this flag is specified, + // additional permissions might still be needed to access the file. + Read = 1, + // + // Summary: + // Allows subsequent opening of the file for writing. If this flag is not specified, + // any request to open the file for writing (by this process or another process) + // will fail until the file is closed. However, even if this flag is specified, + // additional permissions might still be needed to access the file. + Write = 2, + // + // Summary: + // Allows subsequent opening of the file for reading or writing. If this flag is + // not specified, any request to open the file for reading or writing (by this process + // or another process) will fail until the file is closed. However, even if this + // flag is specified, additional permissions might still be needed to access the + // file. + ReadWrite = 3 + } + + // + // Summary: + // Represents advanced options for creating a System.IO.FileStream object. + [Flags] + public enum FileOpenOptions + { + // + // Summary: + // Indicates that the system should write through any intermediate cache and go + // directly to disk. + WriteThrough = int.MinValue, + // + // Summary: + // Indicates that no additional options should be used when creating a System.IO.FileStream + // object. + None = 0, + // + // Summary: + // Indicates that a file is encrypted and can be decrypted only by using the same + // user account used for encryption. + Encrypted = 16384, + // + // Summary: + // Indicates that a file is automatically deleted when it is no longer in use. + DeleteOnClose = 67108864, + // + // Summary: + // Indicates that the file is to be accessed sequentially from beginning to end. + // The system can use this as a hint to optimize file caching. If an application + // moves the file pointer for random access, optimum caching may not occur; however, + // correct operation is still guaranteed. + SequentialScan = 134217728, + // + // Summary: + // Indicates that the file is accessed randomly. The system can use this as a hint + // to optimize file caching. + RandomAccess = 268435456, + // + // Summary: + // Indicates that a file can be used for asynchronous reading and writing. + Asynchronous = 1073741824 + } +} diff --git a/MediaBrowser.Model/IO/IIsoManager.cs b/MediaBrowser.Model/IO/IIsoManager.cs new file mode 100644 index 000000000..92c4d5aee --- /dev/null +++ b/MediaBrowser.Model/IO/IIsoManager.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Model.IO +{ + public interface IIsoManager : IDisposable + { + /// <summary> + /// Mounts the specified iso path. + /// </summary> + /// <param name="isoPath">The iso path.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>IsoMount.</returns> + /// <exception cref="ArgumentNullException">isoPath</exception> + /// <exception cref="IOException">Unable to create mount.</exception> + Task<IIsoMount> Mount(string isoPath, CancellationToken cancellationToken); + + /// <summary> + /// Determines whether this instance can mount the specified path. + /// </summary> + /// <param name="path">The path.</param> + /// <returns><c>true</c> if this instance can mount the specified path; otherwise, <c>false</c>.</returns> + bool CanMount(string path); + + /// <summary> + /// Adds the parts. + /// </summary> + /// <param name="mounters">The mounters.</param> + void AddParts(IEnumerable<IIsoMounter> mounters); + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/IO/IIsoMount.cs b/MediaBrowser.Model/IO/IIsoMount.cs new file mode 100644 index 000000000..4f8f8b5d2 --- /dev/null +++ b/MediaBrowser.Model/IO/IIsoMount.cs @@ -0,0 +1,22 @@ +using System; + +namespace MediaBrowser.Model.IO +{ + /// <summary> + /// Interface IIsoMount + /// </summary> + public interface IIsoMount : IDisposable + { + /// <summary> + /// Gets or sets the iso path. + /// </summary> + /// <value>The iso path.</value> + string IsoPath { get; } + + /// <summary> + /// Gets the mounted path. + /// </summary> + /// <value>The mounted path.</value> + string MountedPath { get; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/IO/IIsoMounter.cs b/MediaBrowser.Model/IO/IIsoMounter.cs new file mode 100644 index 000000000..7efbc2024 --- /dev/null +++ b/MediaBrowser.Model/IO/IIsoMounter.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Model.IO +{ + public interface IIsoMounter : IDisposable + { + /// <summary> + /// Mounts the specified iso path. + /// </summary> + /// <param name="isoPath">The iso path.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>IsoMount.</returns> + /// <exception cref="ArgumentNullException">isoPath</exception> + /// <exception cref="IOException">Unable to create mount.</exception> + Task<IIsoMount> Mount(string isoPath, CancellationToken cancellationToken); + + /// <summary> + /// Determines whether this instance can mount the specified path. + /// </summary> + /// <param name="path">The path.</param> + /// <returns><c>true</c> if this instance can mount the specified path; otherwise, <c>false</c>.</returns> + bool CanMount(string path); + + /// <summary> + /// Gets the name. + /// </summary> + /// <value>The name.</value> + string Name { get; } + } +} diff --git a/MediaBrowser.Model/IO/IShortcutHandler.cs b/MediaBrowser.Model/IO/IShortcutHandler.cs new file mode 100644 index 000000000..16255e51f --- /dev/null +++ b/MediaBrowser.Model/IO/IShortcutHandler.cs @@ -0,0 +1,25 @@ + +namespace MediaBrowser.Model.IO +{ + public interface IShortcutHandler + { + /// <summary> + /// Gets the extension. + /// </summary> + /// <value>The extension.</value> + string Extension { get; } + /// <summary> + /// Resolves the specified shortcut path. + /// </summary> + /// <param name="shortcutPath">The shortcut path.</param> + /// <returns>System.String.</returns> + string Resolve(string shortcutPath); + /// <summary> + /// Creates the specified shortcut path. + /// </summary> + /// <param name="shortcutPath">The shortcut path.</param> + /// <param name="targetPath">The target path.</param> + /// <returns>System.String.</returns> + void Create(string shortcutPath, string targetPath); + } +} diff --git a/MediaBrowser.Model/IO/IStreamHelper.cs b/MediaBrowser.Model/IO/IStreamHelper.cs new file mode 100644 index 000000000..7ed6015c0 --- /dev/null +++ b/MediaBrowser.Model/IO/IStreamHelper.cs @@ -0,0 +1,19 @@ +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Model.IO +{ + public interface IStreamHelper + { + Task CopyToAsync(Stream source, Stream destination, int bufferSize, Action onStarted, CancellationToken cancellationToken); + + Task CopyToAsync(Stream source, Stream destination, int bufferSize, int emptyReadLimit, CancellationToken cancellationToken); + + Task<int> CopyToAsync(Stream source, Stream destination, CancellationToken cancellationToken); + Task CopyToAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken); + + Task CopyUntilCancelled(Stream source, Stream target, int bufferSize, CancellationToken cancellationToken); + } +} diff --git a/MediaBrowser.Model/IO/IZipClient.cs b/MediaBrowser.Model/IO/IZipClient.cs new file mode 100644 index 000000000..c1dfc6cd6 --- /dev/null +++ b/MediaBrowser.Model/IO/IZipClient.cs @@ -0,0 +1,69 @@ +using System.IO; + +namespace MediaBrowser.Model.IO +{ + /// <summary> + /// Interface IZipClient + /// </summary> + public interface IZipClient + { + /// <summary> + /// Extracts all. + /// </summary> + /// <param name="sourceFile">The source file.</param> + /// <param name="targetPath">The target path.</param> + /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param> + void ExtractAll(string sourceFile, string targetPath, bool overwriteExistingFiles); + + /// <summary> + /// Extracts all. + /// </summary> + /// <param name="source">The source.</param> + /// <param name="targetPath">The target path.</param> + /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param> + void ExtractAll(Stream source, string targetPath, bool overwriteExistingFiles); + + void ExtractAllFromGz(Stream source, string targetPath, bool overwriteExistingFiles); + void ExtractFirstFileFromGz(Stream source, string targetPath, string defaultFileName); + + /// <summary> + /// Extracts all from zip. + /// </summary> + /// <param name="source">The source.</param> + /// <param name="targetPath">The target path.</param> + /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param> + void ExtractAllFromZip(Stream source, string targetPath, bool overwriteExistingFiles); + + /// <summary> + /// Extracts all from7z. + /// </summary> + /// <param name="sourceFile">The source file.</param> + /// <param name="targetPath">The target path.</param> + /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param> + void ExtractAllFrom7z(string sourceFile, string targetPath, bool overwriteExistingFiles); + + /// <summary> + /// Extracts all from7z. + /// </summary> + /// <param name="source">The source.</param> + /// <param name="targetPath">The target path.</param> + /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param> + void ExtractAllFrom7z(Stream source, string targetPath, bool overwriteExistingFiles); + + /// <summary> + /// Extracts all from tar. + /// </summary> + /// <param name="sourceFile">The source file.</param> + /// <param name="targetPath">The target path.</param> + /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param> + void ExtractAllFromTar(string sourceFile, string targetPath, bool overwriteExistingFiles); + + /// <summary> + /// Extracts all from tar. + /// </summary> + /// <param name="source">The source.</param> + /// <param name="targetPath">The target path.</param> + /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param> + void ExtractAllFromTar(Stream source, string targetPath, bool overwriteExistingFiles); + } +} diff --git a/MediaBrowser.Model/IO/StreamDefaults.cs b/MediaBrowser.Model/IO/StreamDefaults.cs new file mode 100644 index 000000000..1e99ff4b5 --- /dev/null +++ b/MediaBrowser.Model/IO/StreamDefaults.cs @@ -0,0 +1,19 @@ + +namespace MediaBrowser.Model.IO +{ + /// <summary> + /// Class StreamDefaults + /// </summary> + public static class StreamDefaults + { + /// <summary> + /// The default copy to buffer size + /// </summary> + public const int DefaultCopyToBufferSize = 81920; + + /// <summary> + /// The default file stream buffer size + /// </summary> + public const int DefaultFileStreamBufferSize = 81920; + } +} diff --git a/MediaBrowser.Model/Library/PlayAccess.cs b/MediaBrowser.Model/Library/PlayAccess.cs new file mode 100644 index 000000000..6ec845fc7 --- /dev/null +++ b/MediaBrowser.Model/Library/PlayAccess.cs @@ -0,0 +1,9 @@ + +namespace MediaBrowser.Model.Library +{ + public enum PlayAccess + { + Full = 0, + None = 1 + } +} diff --git a/MediaBrowser.Model/Library/UserViewQuery.cs b/MediaBrowser.Model/Library/UserViewQuery.cs new file mode 100644 index 000000000..8b60cfd98 --- /dev/null +++ b/MediaBrowser.Model/Library/UserViewQuery.cs @@ -0,0 +1,33 @@ +using System; + +namespace MediaBrowser.Model.Library +{ + public class UserViewQuery + { + /// <summary> + /// Gets or sets the user identifier. + /// </summary> + /// <value>The user identifier.</value> + public Guid UserId { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [include external content]. + /// </summary> + /// <value><c>true</c> if [include external content]; otherwise, <c>false</c>.</value> + public bool IncludeExternalContent { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [include hidden]. + /// </summary> + /// <value><c>true</c> if [include hidden]; otherwise, <c>false</c>.</value> + public bool IncludeHidden { get; set; } + + public string[] PresetViews { get; set; } + + public UserViewQuery() + { + IncludeExternalContent = true; + PresetViews = new string[] {}; + } + } +} diff --git a/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs b/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs new file mode 100644 index 000000000..b5bd6ced0 --- /dev/null +++ b/MediaBrowser.Model/LiveTv/BaseTimerInfoDto.cs @@ -0,0 +1,127 @@ +using MediaBrowser.Model.Dto; +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Model.LiveTv +{ + public class BaseTimerInfoDto : IHasServerId + { + /// <summary> + /// Id of the recording. + /// </summary> + public string Id { get; set; } + + public string Type { get; set; } + + /// <summary> + /// Gets or sets the server identifier. + /// </summary> + /// <value>The server identifier.</value> + public string ServerId { get; set; } + + /// <summary> + /// Gets or sets the external identifier. + /// </summary> + /// <value>The external identifier.</value> + public string ExternalId { get; set; } + + /// <summary> + /// ChannelId of the recording. + /// </summary> + public Guid ChannelId { get; set; } + + /// <summary> + /// Gets or sets the external channel identifier. + /// </summary> + /// <value>The external channel identifier.</value> + public string ExternalChannelId { get; set; } + + /// <summary> + /// ChannelName of the recording. + /// </summary> + public string ChannelName { get; set; } + + public string ChannelPrimaryImageTag { get; set; } + + /// <summary> + /// Gets or sets the program identifier. + /// </summary> + /// <value>The program identifier.</value> + public string ProgramId { get; set; } + + /// <summary> + /// Gets or sets the external program identifier. + /// </summary> + /// <value>The external program identifier.</value> + public string ExternalProgramId { get; set; } + + /// <summary> + /// Name of the recording. + /// </summary> + public string Name { get; set; } + + /// <summary> + /// Description of the recording. + /// </summary> + public string Overview { get; set; } + + /// <summary> + /// The start date of the recording, in UTC. + /// </summary> + public DateTime StartDate { get; set; } + + /// <summary> + /// The end date of the recording, in UTC. + /// </summary> + public DateTime EndDate { get; set; } + + /// <summary> + /// Gets or sets the name of the service. + /// </summary> + /// <value>The name of the service.</value> + public string ServiceName { get; set; } + + /// <summary> + /// Gets or sets the priority. + /// </summary> + /// <value>The priority.</value> + public int Priority { get; set; } + + /// <summary> + /// Gets or sets the pre padding seconds. + /// </summary> + /// <value>The pre padding seconds.</value> + public int PrePaddingSeconds { get; set; } + + /// <summary> + /// Gets or sets the post padding seconds. + /// </summary> + /// <value>The post padding seconds.</value> + public int PostPaddingSeconds { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is pre padding required. + /// </summary> + /// <value><c>true</c> if this instance is pre padding required; otherwise, <c>false</c>.</value> + public bool IsPrePaddingRequired { get; set; } + + /// <summary> + /// If the item does not have any backdrops, this will hold the Id of the Parent that has one. + /// </summary> + /// <value>The parent backdrop item id.</value> + public string ParentBackdropItemId { get; set; } + + /// <summary> + /// Gets or sets the parent backdrop image tags. + /// </summary> + /// <value>The parent backdrop image tags.</value> + public string[] ParentBackdropImageTags { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is post padding required. + /// </summary> + /// <value><c>true</c> if this instance is post padding required; otherwise, <c>false</c>.</value> + public bool IsPostPaddingRequired { get; set; } + public KeepUntil KeepUntil { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/LiveTv/ChannelType.cs b/MediaBrowser.Model/LiveTv/ChannelType.cs new file mode 100644 index 000000000..bca16f839 --- /dev/null +++ b/MediaBrowser.Model/LiveTv/ChannelType.cs @@ -0,0 +1,19 @@ + +namespace MediaBrowser.Model.LiveTv +{ + /// <summary> + /// Enum ChannelType + /// </summary> + public enum ChannelType + { + /// <summary> + /// The TV + /// </summary> + TV, + + /// <summary> + /// The radio + /// </summary> + Radio + } +} diff --git a/MediaBrowser.Model/LiveTv/DayPattern.cs b/MediaBrowser.Model/LiveTv/DayPattern.cs new file mode 100644 index 000000000..8251795dc --- /dev/null +++ b/MediaBrowser.Model/LiveTv/DayPattern.cs @@ -0,0 +1,9 @@ +namespace MediaBrowser.Model.LiveTv +{ + public enum DayPattern + { + Daily, + Weekdays, + Weekends + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/LiveTv/GuideInfo.cs b/MediaBrowser.Model/LiveTv/GuideInfo.cs new file mode 100644 index 000000000..c21f6d871 --- /dev/null +++ b/MediaBrowser.Model/LiveTv/GuideInfo.cs @@ -0,0 +1,19 @@ +using System; + +namespace MediaBrowser.Model.LiveTv +{ + public class GuideInfo + { + /// <summary> + /// Gets or sets the start date. + /// </summary> + /// <value>The start date.</value> + public DateTime StartDate { get; set; } + + /// <summary> + /// Gets or sets the end date. + /// </summary> + /// <value>The end date.</value> + public DateTime EndDate { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs new file mode 100644 index 000000000..e3abd5974 --- /dev/null +++ b/MediaBrowser.Model/LiveTv/LiveTvChannelQuery.cs @@ -0,0 +1,104 @@ +using MediaBrowser.Model.Entities; +using System; + +namespace MediaBrowser.Model.LiveTv +{ + /// <summary> + /// Class ChannelQuery. + /// </summary> + public class LiveTvChannelQuery + { + /// <summary> + /// Gets or sets the type of the channel. + /// </summary> + /// <value>The type of the channel.</value> + public ChannelType? ChannelType { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is favorite. + /// </summary> + /// <value><c>null</c> if [is favorite] contains no value, <c>true</c> if [is favorite]; otherwise, <c>false</c>.</value> + public bool? IsFavorite { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is liked. + /// </summary> + /// <value><c>null</c> if [is liked] contains no value, <c>true</c> if [is liked]; otherwise, <c>false</c>.</value> + public bool? IsLiked { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is disliked. + /// </summary> + /// <value><c>null</c> if [is disliked] contains no value, <c>true</c> if [is disliked]; otherwise, <c>false</c>.</value> + public bool? IsDisliked { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [enable favorite sorting]. + /// </summary> + /// <value><c>true</c> if [enable favorite sorting]; otherwise, <c>false</c>.</value> + public bool EnableFavoriteSorting { get; set; } + + /// <summary> + /// Gets or sets the user identifier. + /// </summary> + /// <value>The user identifier.</value> + public Guid UserId { get; set; } + + /// <summary> + /// Skips over a given number of items within the results. Use for paging. + /// </summary> + /// <value>The start index.</value> + public int? StartIndex { get; set; } + + /// <summary> + /// The maximum number of items to return + /// </summary> + /// <value>The limit.</value> + public int? Limit { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [add current program]. + /// </summary> + /// <value><c>true</c> if [add current program]; otherwise, <c>false</c>.</value> + public bool AddCurrentProgram { get; set; } + public bool EnableUserData { get; set; } + + /// <summary> + /// Used to specific whether to return news or not + /// </summary> + /// <remarks>If set to null, all programs will be returned</remarks> + public bool? IsNews { get; set; } + + /// <summary> + /// Used to specific whether to return movies or not + /// </summary> + /// <remarks>If set to null, all programs will be returned</remarks> + public bool? IsMovie { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is kids. + /// </summary> + /// <value><c>null</c> if [is kids] contains no value, <c>true</c> if [is kids]; otherwise, <c>false</c>.</value> + public bool? IsKids { get; set; } + /// <summary> + /// Gets or sets a value indicating whether this instance is sports. + /// </summary> + /// <value><c>null</c> if [is sports] contains no value, <c>true</c> if [is sports]; otherwise, <c>false</c>.</value> + public bool? IsSports { get; set; } + public bool? IsSeries { get; set; } + + public string[] SortBy { get; set; } + + /// <summary> + /// The sort order to return results with + /// </summary> + /// <value>The sort order.</value> + public SortOrder? SortOrder { get; set; } + + public LiveTvChannelQuery() + { + EnableUserData = true; + SortBy = new string[] {}; + } + } +} diff --git a/MediaBrowser.Model/LiveTv/LiveTvInfo.cs b/MediaBrowser.Model/LiveTv/LiveTvInfo.cs new file mode 100644 index 000000000..331b1101b --- /dev/null +++ b/MediaBrowser.Model/LiveTv/LiveTvInfo.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System; + +namespace MediaBrowser.Model.LiveTv +{ + public class LiveTvInfo + { + /// <summary> + /// Gets or sets the services. + /// </summary> + /// <value>The services.</value> + public LiveTvServiceInfo[] Services { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is enabled. + /// </summary> + /// <value><c>true</c> if this instance is enabled; otherwise, <c>false</c>.</value> + public bool IsEnabled { get; set; } + + /// <summary> + /// Gets or sets the enabled users. + /// </summary> + /// <value>The enabled users.</value> + public string[] EnabledUsers { get; set; } + + public LiveTvInfo() + { + Services = new LiveTvServiceInfo[] { }; + EnabledUsers = new string[] {}; + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs new file mode 100644 index 000000000..eb4f20f9e --- /dev/null +++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs @@ -0,0 +1,88 @@ +using MediaBrowser.Model.Dto; +using System; + +namespace MediaBrowser.Model.LiveTv +{ + public class LiveTvOptions + { + public int? GuideDays { get; set; } + public string RecordingPath { get; set; } + public string MovieRecordingPath { get; set; } + public string SeriesRecordingPath { get; set; } + public bool EnableRecordingSubfolders { get; set; } + public bool EnableOriginalAudioWithEncodedRecordings { get; set; } + + public TunerHostInfo[] TunerHosts { get; set; } + public ListingsProviderInfo[] ListingProviders { get; set; } + + public int PrePaddingSeconds { get; set; } + public int PostPaddingSeconds { get; set; } + + public string[] MediaLocationsCreated { get; set; } + + public string RecordingPostProcessor { get; set; } + public string RecordingPostProcessorArguments { get; set; } + + public LiveTvOptions() + { + TunerHosts = new TunerHostInfo[] { }; + ListingProviders = new ListingsProviderInfo[] { }; + MediaLocationsCreated = new string[] { }; + RecordingPostProcessorArguments = "\"{path}\""; + } + } + + public class TunerHostInfo + { + public string Id { get; set; } + public string Url { get; set; } + public string Type { get; set; } + public string DeviceId { get; set; } + public string FriendlyName { get; set; } + public bool ImportFavoritesOnly { get; set; } + public bool AllowHWTranscoding { get; set; } + public bool EnableStreamLooping { get; set; } + public string Source { get; set; } + public int TunerCount { get; set; } + public string UserAgent { get; set; } + + public TunerHostInfo() + { + AllowHWTranscoding = true; + } + } + + public class ListingsProviderInfo + { + public string Id { get; set; } + public string Type { get; set; } + public string Username { get; set; } + public string Password { get; set; } + public string ListingsId { get; set; } + public string ZipCode { get; set; } + public string Country { get; set; } + public string Path { get; set; } + + public string[] EnabledTuners { get; set; } + public bool EnableAllTuners { get; set; } + public string[] NewsCategories { get; set; } + public string[] SportsCategories { get; set; } + public string[] KidsCategories { get; set; } + public string[] MovieCategories { get; set; } + public NameValuePair[] ChannelMappings { get; set; } + public string MoviePrefix { get; set; } + public string PreferredLanguage { get; set; } + public string UserAgent { get; set; } + + public ListingsProviderInfo() + { + NewsCategories = new string[] { "news", "journalism", "documentary", "current affairs" }; + SportsCategories = new string[] { "sports", "basketball", "baseball", "football" }; + KidsCategories = new string[] { "kids", "family", "children", "childrens", "disney" }; + MovieCategories = new string[] { "movie" }; + EnabledTuners = new string[] { }; + EnableAllTuners = true; + ChannelMappings = new NameValuePair[] {}; + } + } +} diff --git a/MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs b/MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs new file mode 100644 index 000000000..23eedfc88 --- /dev/null +++ b/MediaBrowser.Model/LiveTv/LiveTvServiceInfo.cs @@ -0,0 +1,58 @@ +using System; + +namespace MediaBrowser.Model.LiveTv +{ + /// <summary> + /// Class ServiceInfo + /// </summary> + public class LiveTvServiceInfo + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the home page URL. + /// </summary> + /// <value>The home page URL.</value> + public string HomePageUrl { get; set; } + + /// <summary> + /// Gets or sets the status. + /// </summary> + /// <value>The status.</value> + public LiveTvServiceStatus Status { get; set; } + + /// <summary> + /// Gets or sets the status message. + /// </summary> + /// <value>The status message.</value> + public string StatusMessage { get; set; } + + /// <summary> + /// Gets or sets the version. + /// </summary> + /// <value>The version.</value> + public string Version { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance has update available. + /// </summary> + /// <value><c>true</c> if this instance has update available; otherwise, <c>false</c>.</value> + public bool HasUpdateAvailable { get; set; } + /// <summary> + /// Gets or sets a value indicating whether this instance is visible. + /// </summary> + /// <value><c>true</c> if this instance is visible; otherwise, <c>false</c>.</value> + public bool IsVisible { get; set; } + + public string[] Tuners { get; set; } + + public LiveTvServiceInfo() + { + Tuners = new string[] { }; + } + } +} diff --git a/MediaBrowser.Model/LiveTv/LiveTvServiceStatus.cs b/MediaBrowser.Model/LiveTv/LiveTvServiceStatus.cs new file mode 100644 index 000000000..20fe84500 --- /dev/null +++ b/MediaBrowser.Model/LiveTv/LiveTvServiceStatus.cs @@ -0,0 +1,8 @@ +namespace MediaBrowser.Model.LiveTv +{ + public enum LiveTvServiceStatus + { + Ok = 0, + Unavailable = 1 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/LiveTv/LiveTvTunerStatus.cs b/MediaBrowser.Model/LiveTv/LiveTvTunerStatus.cs new file mode 100644 index 000000000..055199fca --- /dev/null +++ b/MediaBrowser.Model/LiveTv/LiveTvTunerStatus.cs @@ -0,0 +1,10 @@ +namespace MediaBrowser.Model.LiveTv +{ + public enum LiveTvTunerStatus + { + Available = 0, + Disabled = 1, + RecordingTv = 2, + LiveTv = 3 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/LiveTv/ProgramAudio.cs b/MediaBrowser.Model/LiveTv/ProgramAudio.cs new file mode 100644 index 000000000..9a272492c --- /dev/null +++ b/MediaBrowser.Model/LiveTv/ProgramAudio.cs @@ -0,0 +1,12 @@ +namespace MediaBrowser.Model.LiveTv +{ + public enum ProgramAudio + { + Mono, + Stereo, + Dolby, + DolbyDigital, + Thx, + Atmos + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/LiveTv/ProgramQuery.cs b/MediaBrowser.Model/LiveTv/ProgramQuery.cs new file mode 100644 index 000000000..89160948c --- /dev/null +++ b/MediaBrowser.Model/LiveTv/ProgramQuery.cs @@ -0,0 +1,117 @@ +using MediaBrowser.Model.Entities; +using System; +using MediaBrowser.Model.Querying; + +namespace MediaBrowser.Model.LiveTv +{ + /// <summary> + /// Class ProgramQuery. + /// </summary> + public class ProgramQuery + { + public ProgramQuery() + { + ChannelIds = new Guid[] { }; + OrderBy = new Tuple<string, SortOrder>[] { }; + Genres = new string[] {}; + GenreIds = new Guid[] { }; + EnableTotalRecordCount = true; + EnableUserData = true; + } + + public bool EnableTotalRecordCount { get; set; } + public bool EnableUserData { get; set; } + + /// <summary> + /// Fields to return within the items, in addition to basic information + /// </summary> + /// <value>The fields.</value> + public ItemFields[] Fields { get; set; } + public bool? EnableImages { get; set; } + public int? ImageTypeLimit { get; set; } + public ImageType[] EnableImageTypes { get; set; } + + /// <summary> + /// Gets or sets the channel ids. + /// </summary> + /// <value>The channel ids.</value> + public Guid[] ChannelIds { get; set; } + + /// <summary> + /// Gets or sets the user identifier. + /// </summary> + /// <value>The user identifier.</value> + public string UserId { get; set; } + public string SeriesTimerId { get; set; } + public string Name { get; set; } + + /// <summary> + /// The earliest date for which a program starts to return + /// </summary> + public DateTime? MinStartDate { get; set; } + + /// <summary> + /// The latest date for which a program starts to return + /// </summary> + public DateTime? MaxStartDate { get; set; } + + /// <summary> + /// The earliest date for which a program ends to return + /// </summary> + public DateTime? MinEndDate { get; set; } + + /// <summary> + /// The latest date for which a program ends to return + /// </summary> + public DateTime? MaxEndDate { get; set; } + + /// <summary> + /// Used to specific whether to return news or not + /// </summary> + /// <remarks>If set to null, all programs will be returned</remarks> + public bool? IsNews { get; set; } + + /// <summary> + /// Used to specific whether to return movies or not + /// </summary> + /// <remarks>If set to null, all programs will be returned</remarks> + public bool? IsMovie { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is kids. + /// </summary> + /// <value><c>null</c> if [is kids] contains no value, <c>true</c> if [is kids]; otherwise, <c>false</c>.</value> + public bool? IsKids { get; set; } + /// <summary> + /// Gets or sets a value indicating whether this instance is sports. + /// </summary> + /// <value><c>null</c> if [is sports] contains no value, <c>true</c> if [is sports]; otherwise, <c>false</c>.</value> + public bool? IsSports { get; set; } + + /// <summary> + /// Skips over a given number of items within the results. Use for paging. + /// </summary> + public int? StartIndex { get; set; } + public bool? IsSeries { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance has aired. + /// </summary> + /// <value><c>null</c> if [has aired] contains no value, <c>true</c> if [has aired]; otherwise, <c>false</c>.</value> + public bool? HasAired { get; set; } + + /// <summary> + /// The maximum number of items to return + /// </summary> + public int? Limit { get; set; } + + public Tuple<string, SortOrder>[] OrderBy { get; set; } + + /// <summary> + /// Limit results to items containing specific genres + /// </summary> + /// <value>The genres.</value> + public Guid[] GenreIds { get; set; } + public string[] Genres { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs b/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs new file mode 100644 index 000000000..9972c4c3f --- /dev/null +++ b/MediaBrowser.Model/LiveTv/RecommendedProgramQuery.cs @@ -0,0 +1,73 @@ +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Querying; +using System; + +namespace MediaBrowser.Model.LiveTv +{ + public class RecommendedProgramQuery + { + /// <summary> + /// Fields to return within the items, in addition to basic information + /// </summary> + /// <value>The fields.</value> + public ItemFields[] Fields { get; set; } + public bool? EnableImages { get; set; } + public int? ImageTypeLimit { get; set; } + public ImageType[] EnableImageTypes { get; set; } + public Guid[] GenreIds { get; set; } + + public bool EnableTotalRecordCount { get; set; } + + public RecommendedProgramQuery() + { + EnableTotalRecordCount = true; + GenreIds = new Guid[] { }; + } + + /// <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 airing. + /// </summary> + /// <value><c>true</c> if this instance is airing; otherwise, <c>false</c>.</value> + public bool? IsAiring { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance has aired. + /// </summary> + /// <value><c>null</c> if [has aired] contains no value, <c>true</c> if [has aired]; otherwise, <c>false</c>.</value> + public bool? HasAired { get; set; } + + /// <summary> + /// The maximum number of items to return + /// </summary> + /// <value>The limit.</value> + public int? Limit { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is movie. + /// </summary> + /// <value><c>null</c> if [is movie] contains no value, <c>true</c> if [is movie]; otherwise, <c>false</c>.</value> + public bool? IsNews { get; set; } + /// <summary> + /// Gets or sets a value indicating whether this instance is movie. + /// </summary> + /// <value><c>null</c> if [is movie] contains no value, <c>true</c> if [is movie]; otherwise, <c>false</c>.</value> + public bool? IsMovie { get; set; } + public bool? IsSeries { get; set; } + /// <summary> + /// Gets or sets a value indicating whether this instance is kids. + /// </summary> + /// <value><c>null</c> if [is kids] contains no value, <c>true</c> if [is kids]; otherwise, <c>false</c>.</value> + public bool? IsKids { get; set; } + /// <summary> + /// Gets or sets a value indicating whether this instance is sports. + /// </summary> + /// <value><c>null</c> if [is sports] contains no value, <c>true</c> if [is sports]; otherwise, <c>false</c>.</value> + public bool? IsSports { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/LiveTv/RecordingQuery.cs b/MediaBrowser.Model/LiveTv/RecordingQuery.cs new file mode 100644 index 000000000..7d20441a5 --- /dev/null +++ b/MediaBrowser.Model/LiveTv/RecordingQuery.cs @@ -0,0 +1,82 @@ +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Querying; +using System; + +namespace MediaBrowser.Model.LiveTv +{ + /// <summary> + /// Class RecordingQuery. + /// </summary> + public class RecordingQuery + { + /// <summary> + /// Gets or sets the channel identifier. + /// </summary> + /// <value>The channel identifier.</value> + public string ChannelId { get; set; } + + /// <summary> + /// Gets or sets the user identifier. + /// </summary> + /// <value>The user identifier.</value> + public Guid UserId { get; set; } + + /// <summary> + /// Gets or sets the identifier. + /// </summary> + /// <value>The identifier.</value> + public string Id { get; set; } + + /// <summary> + /// Skips over a given number of items within the results. Use for paging. + /// </summary> + /// <value>The start index.</value> + public int? StartIndex { get; set; } + + /// <summary> + /// The maximum number of items to return + /// </summary> + /// <value>The limit.</value> + public int? Limit { get; set; } + + /// <summary> + /// Gets or sets the status. + /// </summary> + /// <value>The status.</value> + public RecordingStatus? Status { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is in progress. + /// </summary> + /// <value><c>null</c> if [is in progress] contains no value, <c>true</c> if [is in progress]; otherwise, <c>false</c>.</value> + public bool? IsInProgress { get; set; } + + /// <summary> + /// Gets or sets the series timer identifier. + /// </summary> + /// <value>The series timer identifier.</value> + public string SeriesTimerId { get; set; } + + /// <summary> + /// Fields to return within the items, in addition to basic information + /// </summary> + /// <value>The fields.</value> + public ItemFields[] Fields { get; set; } + public bool? EnableImages { get; set; } + public bool? IsLibraryItem { get; set; } + public bool? IsNews { get; set; } + public bool? IsMovie { get; set; } + public bool? IsSeries { get; set; } + public bool? IsKids { get; set; } + public bool? IsSports { get; set; } + public int? ImageTypeLimit { get; set; } + public ImageType[] EnableImageTypes { get; set; } + + public bool EnableTotalRecordCount { get; set; } + + public RecordingQuery() + { + EnableTotalRecordCount = true; + } + } +} diff --git a/MediaBrowser.Model/LiveTv/RecordingStatus.cs b/MediaBrowser.Model/LiveTv/RecordingStatus.cs new file mode 100644 index 000000000..496e6f421 --- /dev/null +++ b/MediaBrowser.Model/LiveTv/RecordingStatus.cs @@ -0,0 +1,14 @@ + +namespace MediaBrowser.Model.LiveTv +{ + public enum RecordingStatus + { + New, + InProgress, + Completed, + Cancelled, + ConflictedOk, + ConflictedNotOk, + Error + } +} diff --git a/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs b/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs new file mode 100644 index 000000000..593996352 --- /dev/null +++ b/MediaBrowser.Model/LiveTv/SeriesTimerInfoDto.cs @@ -0,0 +1,92 @@ +using MediaBrowser.Model.Entities; +using System; +using System.Collections.Generic; +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Model.LiveTv +{ + /// <summary> + /// Class SeriesTimerInfoDto. + /// </summary> + public class SeriesTimerInfoDto : BaseTimerInfoDto + { + public SeriesTimerInfoDto() + { + ImageTags = new Dictionary<ImageType, string>(); + Days = new DayOfWeek[] { }; + Type = "SeriesTimer"; + } + + /// <summary> + /// Gets or sets a value indicating whether [record any time]. + /// </summary> + /// <value><c>true</c> if [record any time]; otherwise, <c>false</c>.</value> + public bool RecordAnyTime { get; set; } + + public bool SkipEpisodesInLibrary { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [record any channel]. + /// </summary> + /// <value><c>true</c> if [record any channel]; otherwise, <c>false</c>.</value> + public bool RecordAnyChannel { get; set; } + + public int KeepUpTo { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [record new only]. + /// </summary> + /// <value><c>true</c> if [record new only]; otherwise, <c>false</c>.</value> + public bool RecordNewOnly { get; set; } + + /// <summary> + /// Gets or sets the days. + /// </summary> + /// <value>The days.</value> + public DayOfWeek[] Days { get; set; } + + /// <summary> + /// Gets or sets the day pattern. + /// </summary> + /// <value>The day pattern.</value> + public DayPattern? DayPattern { get; set; } + + /// <summary> + /// Gets or sets the image tags. + /// </summary> + /// <value>The image tags.</value> + public Dictionary<ImageType, string> ImageTags { get; set; } + + /// <summary> + /// Gets or sets the parent thumb item id. + /// </summary> + /// <value>The parent thumb item id.</value> + public string ParentThumbItemId { get; set; } + + /// <summary> + /// Gets or sets the parent thumb image tag. + /// </summary> + /// <value>The parent thumb image tag.</value> + public string ParentThumbImageTag { get; set; } + + /// <summary> + /// Gets or sets the parent primary image item identifier. + /// </summary> + /// <value>The parent primary image item identifier.</value> + public string ParentPrimaryImageItemId { get; set; } + + /// <summary> + /// Gets or sets the parent primary image tag. + /// </summary> + /// <value>The parent primary image tag.</value> + public string ParentPrimaryImageTag { get; set; } + } + + public enum KeepUntil + { + UntilDeleted, + UntilSpaceNeeded, + UntilWatched, + UntilDate + } +} diff --git a/MediaBrowser.Model/LiveTv/SeriesTimerQuery.cs b/MediaBrowser.Model/LiveTv/SeriesTimerQuery.cs new file mode 100644 index 000000000..95260cc0e --- /dev/null +++ b/MediaBrowser.Model/LiveTv/SeriesTimerQuery.cs @@ -0,0 +1,19 @@ +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Model.LiveTv +{ + public class SeriesTimerQuery + { + /// <summary> + /// Gets or sets the sort by - SortName, Priority + /// </summary> + /// <value>The sort by.</value> + public string SortBy { get; set; } + + /// <summary> + /// Gets or sets the sort order. + /// </summary> + /// <value>The sort order.</value> + public SortOrder SortOrder { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/LiveTv/TimerInfoDto.cs b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs new file mode 100644 index 000000000..d1aa3118f --- /dev/null +++ b/MediaBrowser.Model/LiveTv/TimerInfoDto.cs @@ -0,0 +1,43 @@ +using MediaBrowser.Model.Dto; + +namespace MediaBrowser.Model.LiveTv +{ + public class TimerInfoDto : BaseTimerInfoDto + { + public TimerInfoDto() + { + Type = "Timer"; + } + + /// <summary> + /// Gets or sets the status. + /// </summary> + /// <value>The status.</value> + public RecordingStatus Status { get; set; } + + /// <summary> + /// Gets or sets the series timer identifier. + /// </summary> + /// <value>The series timer identifier.</value> + public string SeriesTimerId { get; set; } + + /// <summary> + /// Gets or sets the external series timer identifier. + /// </summary> + /// <value>The external series timer identifier.</value> + public string ExternalSeriesTimerId { get; set; } + + /// <summary> + /// Gets or sets the run time ticks. + /// </summary> + /// <value>The run time ticks.</value> + public long? RunTimeTicks { get; set; } + + /// <summary> + /// Gets or sets the program information. + /// </summary> + /// <value>The program information.</value> + public BaseItemDto ProgramInfo { get; set; } + + } +} diff --git a/MediaBrowser.Model/LiveTv/TimerQuery.cs b/MediaBrowser.Model/LiveTv/TimerQuery.cs new file mode 100644 index 000000000..c6202680c --- /dev/null +++ b/MediaBrowser.Model/LiveTv/TimerQuery.cs @@ -0,0 +1,23 @@ +namespace MediaBrowser.Model.LiveTv +{ + public class TimerQuery + { + /// <summary> + /// Gets or sets the channel identifier. + /// </summary> + /// <value>The channel identifier.</value> + public string ChannelId { get; set; } + + public string Id { get; set; } + + /// <summary> + /// Gets or sets the series timer identifier. + /// </summary> + /// <value>The series timer identifier.</value> + public string SeriesTimerId { get; set; } + + public bool? IsActive { get; set; } + + public bool? IsScheduled { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Logging/IConsoleLogger.cs b/MediaBrowser.Model/Logging/IConsoleLogger.cs new file mode 100644 index 000000000..a8c282d65 --- /dev/null +++ b/MediaBrowser.Model/Logging/IConsoleLogger.cs @@ -0,0 +1,7 @@ +namespace MediaBrowser.Model.Logging +{ + public interface IConsoleLogger + { + void WriteLine(string message); + } +} diff --git a/MediaBrowser.Model/Logging/ILogManager.cs b/MediaBrowser.Model/Logging/ILogManager.cs new file mode 100644 index 000000000..e6a10cf18 --- /dev/null +++ b/MediaBrowser.Model/Logging/ILogManager.cs @@ -0,0 +1,56 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Model.Logging +{ + /// <summary> + /// Interface ILogManager + /// </summary> + public interface ILogManager + { + /// <summary> + /// Gets or sets the log level. + /// </summary> + /// <value>The log level.</value> + LogSeverity LogSeverity { get; set; } + + /// <summary> + /// Gets or sets the exception message prefix. + /// </summary> + /// <value>The exception message prefix.</value> + string ExceptionMessagePrefix { get; set; } + + /// <summary> + /// Gets the logger. + /// </summary> + /// <param name="name">The name.</param> + /// <returns>ILogger.</returns> + ILogger GetLogger(string name); + + /// <summary> + /// Reloads the logger. + /// </summary> + Task ReloadLogger(LogSeverity severity, CancellationToken cancellationToken); + + /// <summary> + /// Occurs when [logger loaded]. + /// </summary> + event EventHandler LoggerLoaded; + + /// <summary> + /// Flushes this instance. + /// </summary> + void Flush(); + + /// <summary> + /// Adds the console output. + /// </summary> + void AddConsoleOutput(); + + /// <summary> + /// Removes the console output. + /// </summary> + void RemoveConsoleOutput(); + } +} diff --git a/MediaBrowser.Model/Logging/ILogger.cs b/MediaBrowser.Model/Logging/ILogger.cs new file mode 100644 index 000000000..be9d6fc50 --- /dev/null +++ b/MediaBrowser.Model/Logging/ILogger.cs @@ -0,0 +1,78 @@ +using System; +using System.Text; + +namespace MediaBrowser.Model.Logging +{ + /// <summary> + /// Interface ILogger + /// </summary> + public interface ILogger + { + /// <summary> + /// Infoes the specified message. + /// </summary> + /// <param name="message">The message.</param> + /// <param name="paramList">The param list.</param> + void Info(string message, params object[] paramList); + + /// <summary> + /// Errors the specified message. + /// </summary> + /// <param name="message">The message.</param> + /// <param name="paramList">The param list.</param> + void Error(string message, params object[] paramList); + + /// <summary> + /// Warns the specified message. + /// </summary> + /// <param name="message">The message.</param> + /// <param name="paramList">The param list.</param> + void Warn(string message, params object[] paramList); + + /// <summary> + /// Debugs the specified message. + /// </summary> + /// <param name="message">The message.</param> + /// <param name="paramList">The param list.</param> + void Debug(string message, params object[] paramList); + + /// <summary> + /// Fatals the specified message. + /// </summary> + /// <param name="message">The message.</param> + /// <param name="paramList">The param list.</param> + void Fatal(string message, params object[] paramList); + + /// <summary> + /// Fatals the exception. + /// </summary> + /// <param name="message">The message.</param> + /// <param name="exception">The exception.</param> + /// <param name="paramList">The param list.</param> + void FatalException(string message, Exception exception, params object[] paramList); + + /// <summary> + /// Logs the exception. + /// </summary> + /// <param name="message">The message.</param> + /// <param name="exception">The exception.</param> + /// <param name="paramList">The param list.</param> + void ErrorException(string message, Exception exception, params object[] paramList); + + /// <summary> + /// Logs the multiline. + /// </summary> + /// <param name="message">The message.</param> + /// <param name="severity">The severity.</param> + /// <param name="additionalContent">Content of the additional.</param> + void LogMultiline(string message, LogSeverity severity, StringBuilder additionalContent); + + /// <summary> + /// Logs the specified severity. + /// </summary> + /// <param name="severity">The severity.</param> + /// <param name="message">The message.</param> + /// <param name="paramList">The parameter list.</param> + void Log(LogSeverity severity, string message, params object[] paramList); + } +} diff --git a/MediaBrowser.Model/Logging/LogHelper.cs b/MediaBrowser.Model/Logging/LogHelper.cs new file mode 100644 index 000000000..cf1c02186 --- /dev/null +++ b/MediaBrowser.Model/Logging/LogHelper.cs @@ -0,0 +1,97 @@ +using System; +using System.Text; + +namespace MediaBrowser.Model.Logging +{ + /// <summary> + /// Class LogHelper + /// </summary> + public static class LogHelper + { + /// <summary> + /// Gets the log message. + /// </summary> + /// <param name="exception">The exception.</param> + /// <returns>StringBuilder.</returns> + public static StringBuilder GetLogMessage(Exception exception) + { + if (exception == null) + { + throw new ArgumentNullException("exception"); + } + + var messageText = new StringBuilder(); + + messageText.AppendLine(exception.ToString()); + + messageText.AppendLine(exception.GetType().FullName); + + LogExceptionData(messageText, exception); + + messageText.AppendLine(exception.StackTrace ?? "No Stack Trace Available"); + + // Log the InnerExceptions, if any + AppendInnerExceptions(messageText, exception); + + messageText.AppendLine(string.Empty); + + return messageText; + } + + /// <summary> + /// Appends the inner exceptions. + /// </summary> + /// <param name="messageText">The message text.</param> + /// <param name="e">The e.</param> + private static void AppendInnerExceptions(StringBuilder messageText, Exception e) + { + var aggregate = e as AggregateException; + + if (aggregate != null && aggregate.InnerExceptions != null) + { + foreach (var ex in aggregate.InnerExceptions) + { + AppendInnerException(messageText, ex); + AppendInnerExceptions(messageText, ex); + } + } + + else if (e.InnerException != null) + { + AppendInnerException(messageText, e.InnerException); + AppendInnerExceptions(messageText, e.InnerException); + } + } + + /// <summary> + /// Appends the inner exception. + /// </summary> + /// <param name="messageText">The message text.</param> + /// <param name="e">The e.</param> + private static void AppendInnerException(StringBuilder messageText, Exception e) + { + messageText.AppendLine("InnerException: " + e.GetType().FullName); + messageText.AppendLine(e.ToString()); + + LogExceptionData(messageText, e); + + if (e.StackTrace != null) + { + messageText.AppendLine(e.StackTrace); + } + } + + /// <summary> + /// Logs the exception data. + /// </summary> + /// <param name="messageText">The message text.</param> + /// <param name="e">The e.</param> + private static void LogExceptionData(StringBuilder messageText, Exception e) + { + foreach (var key in e.Data.Keys) + { + messageText.AppendLine(key + ": " + e.Data[key]); + } + } + } +} diff --git a/MediaBrowser.Model/Logging/LogSeverity.cs b/MediaBrowser.Model/Logging/LogSeverity.cs new file mode 100644 index 000000000..ae0487289 --- /dev/null +++ b/MediaBrowser.Model/Logging/LogSeverity.cs @@ -0,0 +1,30 @@ + +namespace MediaBrowser.Model.Logging +{ + /// <summary> + /// Enum LogSeverity + /// </summary> + public enum LogSeverity + { + /// <summary> + /// The info + /// </summary> + Info, + /// <summary> + /// The debug + /// </summary> + Debug, + /// <summary> + /// The warn + /// </summary> + Warn, + /// <summary> + /// The error + /// </summary> + Error, + /// <summary> + /// The fatal + /// </summary> + Fatal + } +} diff --git a/MediaBrowser.Model/Logging/NullLogger.cs b/MediaBrowser.Model/Logging/NullLogger.cs new file mode 100644 index 000000000..d211d2567 --- /dev/null +++ b/MediaBrowser.Model/Logging/NullLogger.cs @@ -0,0 +1,44 @@ +using System; +using System.Text; + +namespace MediaBrowser.Model.Logging +{ + public class NullLogger : ILogger + { + public void Info(string message, params object[] paramList) + { + } + + public void Error(string message, params object[] paramList) + { + } + + public void Warn(string message, params object[] paramList) + { + } + + public void Debug(string message, params object[] paramList) + { + } + + public void Fatal(string message, params object[] paramList) + { + } + + public void FatalException(string message, Exception exception, params object[] paramList) + { + } + + public void Log(LogSeverity severity, string message, params object[] paramList) + { + } + + public void ErrorException(string message, Exception exception, params object[] paramList) + { + } + + public void LogMultiline(string message, LogSeverity severity, StringBuilder additionalContent) + { + } + } +} diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj new file mode 100644 index 000000000..1b1a24b68 --- /dev/null +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -0,0 +1,12 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>netstandard2.0</TargetFramework> + <GenerateAssemblyInfo>false</GenerateAssemblyInfo> + </PropertyGroup> + + <ItemGroup> + <Compile Include="..\SharedVersion.cs"/> + </ItemGroup> + +</Project> diff --git a/MediaBrowser.Model/MediaInfo/AudioCodec.cs b/MediaBrowser.Model/MediaInfo/AudioCodec.cs new file mode 100644 index 000000000..93aba2f43 --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/AudioCodec.cs @@ -0,0 +1,26 @@ +namespace MediaBrowser.Model.MediaInfo +{ + public class AudioCodec + { + public const string AAC = "aac"; + public const string MP3 = "mp3"; + public const string AC3 = "ac3"; + + public static string GetFriendlyName(string codec) + { + if (string.IsNullOrEmpty(codec)) return ""; + + switch (codec.ToLower()) + { + case "ac3": + return "Dolby Digital"; + case "eac3": + return "Dolby Digital+"; + case "dca": + return "DTS"; + default: + return codec.ToUpper(); + } + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs b/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs new file mode 100644 index 000000000..1b573fba7 --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/BlurayDiscInfo.cs @@ -0,0 +1,37 @@ +using MediaBrowser.Model.Entities; +using System.Collections.Generic; + +namespace MediaBrowser.Model.MediaInfo +{ + /// <summary> + /// Represents the result of BDInfo output + /// </summary> + public class BlurayDiscInfo + { + /// <summary> + /// Gets or sets the media streams. + /// </summary> + /// <value>The media streams.</value> + public MediaStream[] MediaStreams { get; set; } + + /// <summary> + /// Gets or sets the run time ticks. + /// </summary> + /// <value>The run time ticks.</value> + public long? RunTimeTicks { get; set; } + + /// <summary> + /// Gets or sets the files. + /// </summary> + /// <value>The files.</value> + public string[] Files { get; set; } + + public string PlaylistName { get; set; } + + /// <summary> + /// Gets or sets the chapters. + /// </summary> + /// <value>The chapters.</value> + public double[] Chapters { get; set; } + } +} diff --git a/MediaBrowser.Model/MediaInfo/Container.cs b/MediaBrowser.Model/MediaInfo/Container.cs new file mode 100644 index 000000000..3762edf9f --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/Container.cs @@ -0,0 +1,9 @@ + +namespace MediaBrowser.Model.MediaInfo +{ + public class Container + { + public const string MP4 = "mp4"; + public const string MKV = "mkv"; + } +} diff --git a/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs b/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs new file mode 100644 index 000000000..78d5b197f --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/IBlurayExaminer.cs @@ -0,0 +1,16 @@ + +namespace MediaBrowser.Model.MediaInfo +{ + /// <summary> + /// Interface IBlurayExaminer + /// </summary> + public interface IBlurayExaminer + { + /// <summary> + /// Gets the disc info. + /// </summary> + /// <param name="path">The path.</param> + /// <returns>BlurayDiscInfo.</returns> + BlurayDiscInfo GetDiscInfo(string path); + } +} diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs new file mode 100644 index 000000000..d36aa9944 --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs @@ -0,0 +1,47 @@ +using MediaBrowser.Model.Dlna; +using System; + +namespace MediaBrowser.Model.MediaInfo +{ + public class LiveStreamRequest + { + public string OpenToken { get; set; } + public Guid UserId { get; set; } + public string PlaySessionId { get; set; } + public long? MaxStreamingBitrate { get; set; } + public long? StartTimeTicks { get; set; } + public int? AudioStreamIndex { get; set; } + public int? SubtitleStreamIndex { get; set; } + public int? MaxAudioChannels { get; set; } + public Guid ItemId { get; set; } + public DeviceProfile DeviceProfile { get; set; } + + public bool EnableDirectPlay { get; set; } + public bool EnableDirectStream { get; set; } + public MediaProtocol[] DirectPlayProtocols { get; set; } + + public LiveStreamRequest() + { + EnableDirectPlay = true; + EnableDirectStream = true; + DirectPlayProtocols = new MediaProtocol[] { MediaProtocol.Http }; + } + + public LiveStreamRequest(AudioOptions options) + { + MaxStreamingBitrate = options.MaxBitrate; + ItemId = options.ItemId; + DeviceProfile = options.Profile; + MaxAudioChannels = options.MaxAudioChannels; + + DirectPlayProtocols = new MediaProtocol[] { MediaProtocol.Http }; + + VideoOptions videoOptions = options as VideoOptions; + if (videoOptions != null) + { + AudioStreamIndex = videoOptions.AudioStreamIndex; + SubtitleStreamIndex = videoOptions.SubtitleStreamIndex; + } + } + } +} diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs b/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs new file mode 100644 index 000000000..e79e37a71 --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/LiveStreamResponse.cs @@ -0,0 +1,9 @@ +using MediaBrowser.Model.Dto; + +namespace MediaBrowser.Model.MediaInfo +{ + public class LiveStreamResponse + { + public MediaSourceInfo MediaSource { get; set; } + } +} diff --git a/MediaBrowser.Model/MediaInfo/MediaInfo.cs b/MediaBrowser.Model/MediaInfo/MediaInfo.cs new file mode 100644 index 000000000..55545e23a --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/MediaInfo.cs @@ -0,0 +1,68 @@ +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Model.MediaInfo +{ + public class MediaInfo : MediaSourceInfo, IHasProviderIds + { + private static readonly string[] EmptyStringArray = new string[] {}; + + public ChapterInfo[] Chapters { get; set; } + + /// <summary> + /// Gets or sets the album. + /// </summary> + /// <value>The album.</value> + public string Album { get; set; } + /// <summary> + /// Gets or sets the artists. + /// </summary> + /// <value>The artists.</value> + public string[] Artists { get; set; } + /// <summary> + /// Gets or sets the album artists. + /// </summary> + /// <value>The album artists.</value> + public string[] AlbumArtists { get; set; } + /// <summary> + /// Gets or sets the studios. + /// </summary> + /// <value>The studios.</value> + public string[] Studios { get; set; } + public string[] Genres { get; set; } + public int? IndexNumber { get; set; } + public int? ParentIndexNumber { get; set; } + public int? ProductionYear { get; set; } + public DateTime? PremiereDate { get; set; } + public BaseItemPerson[] People { get; set; } + public Dictionary<string, string> ProviderIds { get; set; } + /// <summary> + /// Gets or sets the official rating. + /// </summary> + /// <value>The official rating.</value> + public string OfficialRating { get; set; } + /// <summary> + /// Gets or sets the official rating description. + /// </summary> + /// <value>The official rating description.</value> + public string OfficialRatingDescription { get; set; } + /// <summary> + /// Gets or sets the overview. + /// </summary> + /// <value>The overview.</value> + public string Overview { get; set; } + + public MediaInfo() + { + Chapters = new ChapterInfo[] { }; + Artists = new string[] {}; + AlbumArtists = EmptyStringArray; + Studios = new string[] {}; + Genres = new string[] {}; + People = new BaseItemPerson[] { }; + ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/MediaInfo/MediaProtocol.cs b/MediaBrowser.Model/MediaInfo/MediaProtocol.cs new file mode 100644 index 000000000..5882ecde0 --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/MediaProtocol.cs @@ -0,0 +1,13 @@ +namespace MediaBrowser.Model.MediaInfo +{ + public enum MediaProtocol + { + File = 0, + Http = 1, + Rtmp = 2, + Rtsp = 3, + Udp = 4, + Rtp = 5, + Ftp = 6 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs new file mode 100644 index 000000000..c68c047f6 --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs @@ -0,0 +1,50 @@ +using MediaBrowser.Model.Dlna; +using System.Collections.Generic; +using System; + +namespace MediaBrowser.Model.MediaInfo +{ + public class PlaybackInfoRequest + { + public Guid Id { get; set; } + + public Guid UserId { get; set; } + + public long? MaxStreamingBitrate { get; set; } + + public long? StartTimeTicks { get; set; } + + public int? AudioStreamIndex { get; set; } + + public int? SubtitleStreamIndex { get; set; } + + public int? MaxAudioChannels { get; set; } + + public string MediaSourceId { get; set; } + + public string LiveStreamId { get; set; } + + public DeviceProfile DeviceProfile { get; set; } + + public bool EnableDirectPlay { get; set; } + public bool EnableDirectStream { get; set; } + public bool EnableTranscoding { get; set; } + public bool AllowVideoStreamCopy { get; set; } + public bool AllowAudioStreamCopy { get; set; } + public bool IsPlayback { get; set; } + public bool AutoOpenLiveStream { get; set; } + + public MediaProtocol[] DirectPlayProtocols { get; set; } + + public PlaybackInfoRequest() + { + EnableDirectPlay = true; + EnableDirectStream = true; + EnableTranscoding = true; + AllowVideoStreamCopy = true; + AllowAudioStreamCopy = true; + IsPlayback = true; + DirectPlayProtocols = new MediaProtocol[] { MediaProtocol.Http }; + } + } +} diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs new file mode 100644 index 000000000..b38fec7d4 --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoResponse.cs @@ -0,0 +1,32 @@ +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Dto; +using System.Collections.Generic; + +namespace MediaBrowser.Model.MediaInfo +{ + public class PlaybackInfoResponse + { + /// <summary> + /// Gets or sets the media sources. + /// </summary> + /// <value>The media sources.</value> + public MediaSourceInfo[] MediaSources { get; set; } + + /// <summary> + /// Gets or sets the play session identifier. + /// </summary> + /// <value>The play session identifier.</value> + public string PlaySessionId { get; set; } + + /// <summary> + /// Gets or sets the error code. + /// </summary> + /// <value>The error code.</value> + public PlaybackErrorCode? ErrorCode { get; set; } + + public PlaybackInfoResponse() + { + MediaSources = new MediaSourceInfo[] { }; + } + } +} diff --git a/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs b/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs new file mode 100644 index 000000000..60b0bb54d --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs @@ -0,0 +1,13 @@ +namespace MediaBrowser.Model.MediaInfo +{ + public class SubtitleFormat + { + public const string SRT = "srt"; + public const string SSA = "ssa"; + public const string ASS = "ass"; + public const string VTT = "vtt"; + public const string SUB = "sub"; + public const string SMI = "smi"; + public const string TTML = "ttml"; + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/MediaInfo/SubtitleTrackEvent.cs b/MediaBrowser.Model/MediaInfo/SubtitleTrackEvent.cs new file mode 100644 index 000000000..b4ab6ed97 --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/SubtitleTrackEvent.cs @@ -0,0 +1,11 @@ + +namespace MediaBrowser.Model.MediaInfo +{ + public class SubtitleTrackEvent + { + public string Id { get; set; } + public string Text { get; set; } + public long StartPositionTicks { get; set; } + public long EndPositionTicks { get; set; } + } +} diff --git a/MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs b/MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs new file mode 100644 index 000000000..d3a3bb1d0 --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Model.MediaInfo +{ + public class SubtitleTrackInfo + { + public SubtitleTrackEvent[] TrackEvents { get; set; } + + public SubtitleTrackInfo() + { + TrackEvents = new SubtitleTrackEvent[] { }; + } + } +} diff --git a/MediaBrowser.Model/MediaInfo/TransportStreamTimestamp.cs b/MediaBrowser.Model/MediaInfo/TransportStreamTimestamp.cs new file mode 100644 index 000000000..4c808a8dc --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/TransportStreamTimestamp.cs @@ -0,0 +1,9 @@ +namespace MediaBrowser.Model.MediaInfo +{ + public enum TransportStreamTimestamp + { + None, + Zero, + Valid + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/MediaInfo/VideoCodec.cs b/MediaBrowser.Model/MediaInfo/VideoCodec.cs new file mode 100644 index 000000000..81755dac9 --- /dev/null +++ b/MediaBrowser.Model/MediaInfo/VideoCodec.cs @@ -0,0 +1,14 @@ +namespace MediaBrowser.Model.MediaInfo +{ + public class VideoCodec + { + public const string H263 = "h263"; + public const string H264 = "h264"; + public const string H265 = "h265"; + public const string MPEG4 = "mpeg4"; + public const string MPEG1 = "mpeg1video"; + public const string MPEG2 = "mpeg2video"; + public const string MSMPEG4 = "msmpeg4"; + public const string VC1 = "vc1"; + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Net/EndPointInfo.cs b/MediaBrowser.Model/Net/EndPointInfo.cs new file mode 100644 index 000000000..5a158e785 --- /dev/null +++ b/MediaBrowser.Model/Net/EndPointInfo.cs @@ -0,0 +1,8 @@ +namespace MediaBrowser.Model.Net +{ + public class EndPointInfo + { + public bool IsLocal { get; set; } + public bool IsInNetwork { get; set; } + } +} diff --git a/MediaBrowser.Model/Net/HttpException.cs b/MediaBrowser.Model/Net/HttpException.cs new file mode 100644 index 000000000..698b1bc7e --- /dev/null +++ b/MediaBrowser.Model/Net/HttpException.cs @@ -0,0 +1,43 @@ +using System; +using System.Net; + +namespace MediaBrowser.Model.Net +{ + /// <summary> + /// Class HttpException + /// </summary> + public class HttpException : Exception + { + /// <summary> + /// Gets or sets the status code. + /// </summary> + /// <value>The status code.</value> + public HttpStatusCode? StatusCode { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is timed out. + /// </summary> + /// <value><c>true</c> if this instance is timed out; otherwise, <c>false</c>.</value> + public bool IsTimedOut { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="HttpException" /> class. + /// </summary> + /// <param name="message">The message.</param> + /// <param name="innerException">The inner exception.</param> + public HttpException(string message, Exception innerException) + : base(message, innerException) + { + + } + + /// <summary> + /// Initializes a new instance of the <see cref="HttpException" /> class. + /// </summary> + /// <param name="message">The message.</param> + public HttpException(string message) + : base(message) + { + } + } +} diff --git a/MediaBrowser.Model/Net/HttpResponse.cs b/MediaBrowser.Model/Net/HttpResponse.cs new file mode 100644 index 000000000..f4bd8e681 --- /dev/null +++ b/MediaBrowser.Model/Net/HttpResponse.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; + +namespace MediaBrowser.Model.Net +{ + public class HttpResponse : IDisposable + { + /// <summary> + /// Gets or sets the type of the content. + /// </summary> + /// <value>The type of the content.</value> + public string ContentType { get; set; } + + /// <summary> + /// Gets or sets the response URL. + /// </summary> + /// <value>The response URL.</value> + public string ResponseUrl { get; set; } + + /// <summary> + /// Gets or sets the content. + /// </summary> + /// <value>The content.</value> + public Stream Content { get; set; } + + /// <summary> + /// Gets or sets the status code. + /// </summary> + /// <value>The status code.</value> + public HttpStatusCode StatusCode { get; set; } + + /// <summary> + /// Gets or sets the length of the content. + /// </summary> + /// <value>The length of the content.</value> + public long? ContentLength { get; set; } + + /// <summary> + /// Gets or sets the headers. + /// </summary> + /// <value>The headers.</value> + public Dictionary<string, string> Headers { get; set; } + + private readonly IDisposable _disposable; + + public HttpResponse(IDisposable disposable) + { + _disposable = disposable; + } + public HttpResponse() + { + } + + public void Dispose() + { + if (_disposable != null) + { + _disposable.Dispose(); + } + } + } +} diff --git a/MediaBrowser.Model/Net/IAcceptSocket.cs b/MediaBrowser.Model/Net/IAcceptSocket.cs new file mode 100644 index 000000000..af5a1fcfb --- /dev/null +++ b/MediaBrowser.Model/Net/IAcceptSocket.cs @@ -0,0 +1,15 @@ +using System; + +namespace MediaBrowser.Model.Net +{ + public class SocketCreateException : Exception + { + public SocketCreateException(string errorCode, Exception originalException) + : base(errorCode, originalException) + { + ErrorCode = errorCode; + } + + public string ErrorCode { get; private set; } + } +} diff --git a/MediaBrowser.Model/Net/ISocket.cs b/MediaBrowser.Model/Net/ISocket.cs new file mode 100644 index 000000000..6a6781026 --- /dev/null +++ b/MediaBrowser.Model/Net/ISocket.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Model.Net +{ + /// <summary> + /// Provides a common interface across platforms for UDP sockets used by this SSDP implementation. + /// </summary> + public interface ISocket : IDisposable + { + IpAddressInfo LocalIPAddress { get; } + + Task<SocketReceiveResult> ReceiveAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken); + + int Receive(byte[] buffer, int offset, int count); + + IAsyncResult BeginReceive(byte[] buffer, int offset, int count, AsyncCallback callback); + SocketReceiveResult EndReceive(IAsyncResult result); + + /// <summary> + /// Sends a UDP message to a particular end point (uni or multicast). + /// </summary> + Task SendToAsync(byte[] buffer, int offset, int bytes, IpEndPointInfo endPoint, CancellationToken cancellationToken); + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Net/ISocketFactory.cs b/MediaBrowser.Model/Net/ISocketFactory.cs new file mode 100644 index 000000000..6a4b99600 --- /dev/null +++ b/MediaBrowser.Model/Net/ISocketFactory.cs @@ -0,0 +1,49 @@ + +using System.IO; + +namespace MediaBrowser.Model.Net +{ + /// <summary> + /// Implemented by components that can create a platform specific UDP socket implementation, and wrap it in the cross platform <see cref="ISocket"/> interface. + /// </summary> + public interface ISocketFactory + { + + /// <summary> + /// Createa a new unicast socket using the specified local port number. + /// </summary> + /// <param name="localPort">The local port to bind to.</param> + /// <returns>A <see cref="ISocket"/> implementation.</returns> + ISocket CreateUdpSocket(int localPort); + + ISocket CreateUdpBroadcastSocket(int localPort); + + ISocket CreateTcpSocket(IpAddressInfo remoteAddress, int remotePort); + + /// <summary> + /// Createa a new unicast socket using the specified local port number. + /// </summary> + ISocket CreateSsdpUdpSocket(IpAddressInfo localIp, int localPort); + + /// <summary> + /// Createa a new multicast socket using the specified multicast IP address, multicast time to live and local port. + /// </summary> + /// <param name="ipAddress">The multicast IP address to bind to.</param> + /// <param name="multicastTimeToLive">The multicast time to live value. Actually a maximum number of network hops for UDP packets.</param> + /// <param name="localPort">The local port to bind to.</param> + /// <returns>A <see cref="ISocket"/> implementation.</returns> + ISocket CreateUdpMulticastSocket(string ipAddress, int multicastTimeToLive, int localPort); + + Stream CreateNetworkStream(ISocket socket, bool ownsSocket); + } + + public enum SocketType + { + Stream + } + + public enum ProtocolType + { + Tcp + } +} diff --git a/MediaBrowser.Model/Net/IpAddressInfo.cs b/MediaBrowser.Model/Net/IpAddressInfo.cs new file mode 100644 index 000000000..520f8fb54 --- /dev/null +++ b/MediaBrowser.Model/Net/IpAddressInfo.cs @@ -0,0 +1,37 @@ +using System; + +namespace MediaBrowser.Model.Net +{ + public class IpAddressInfo + { + public static IpAddressInfo Any = new IpAddressInfo("0.0.0.0", IpAddressFamily.InterNetwork); + public static IpAddressInfo IPv6Any = new IpAddressInfo("00000000000000000000", IpAddressFamily.InterNetworkV6); + public static IpAddressInfo Loopback = new IpAddressInfo("127.0.0.1", IpAddressFamily.InterNetwork); + public static IpAddressInfo IPv6Loopback = new IpAddressInfo("::1", IpAddressFamily.InterNetworkV6); + + public string Address { get; set; } + public IpAddressFamily AddressFamily { get; set; } + + public IpAddressInfo(string address, IpAddressFamily addressFamily) + { + Address = address; + AddressFamily = addressFamily; + } + + public bool Equals(IpAddressInfo address) + { + return string.Equals(address.Address, Address, StringComparison.OrdinalIgnoreCase); + } + + public override String ToString() + { + return Address; + } + } + + public enum IpAddressFamily + { + InterNetwork, + InterNetworkV6 + } +} diff --git a/MediaBrowser.Model/Net/IpEndPointInfo.cs b/MediaBrowser.Model/Net/IpEndPointInfo.cs new file mode 100644 index 000000000..b5cadc429 --- /dev/null +++ b/MediaBrowser.Model/Net/IpEndPointInfo.cs @@ -0,0 +1,30 @@ +using System; +using System.Globalization; + +namespace MediaBrowser.Model.Net +{ + public class IpEndPointInfo + { + public IpAddressInfo IpAddress { get; set; } + + public int Port { get; set; } + + public IpEndPointInfo() + { + + } + + public IpEndPointInfo(IpAddressInfo address, int port) + { + IpAddress = address; + Port = port; + } + + public override string ToString() + { + var ipAddresString = IpAddress == null ? string.Empty : IpAddress.ToString(); + + return ipAddresString + ":" + Port.ToString(CultureInfo.InvariantCulture); + } + } +} diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs new file mode 100644 index 000000000..d66d62fea --- /dev/null +++ b/MediaBrowser.Model/Net/MimeTypes.cs @@ -0,0 +1,346 @@ +using MediaBrowser.Model.Extensions; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace MediaBrowser.Model.Net +{ + /// <summary> + /// Class MimeTypes + /// </summary> + public static class MimeTypes + { + /// <summary> + /// Any extension in this list is considered a video file - can be added to at runtime for extensibility + /// </summary> + private static readonly string[] VideoFileExtensions = new string[] + { + ".mkv", + ".m2t", + ".m2ts", + ".img", + ".iso", + ".mk3d", + ".ts", + ".rmvb", + ".mov", + ".avi", + ".mpg", + ".mpeg", + ".wmv", + ".mp4", + ".divx", + ".dvr-ms", + ".wtv", + ".ogm", + ".ogv", + ".asf", + ".m4v", + ".flv", + ".f4v", + ".3gp", + ".webm", + ".mts", + ".m2v", + ".rec" + }; + + private static Dictionary<string, string> GetVideoFileExtensionsDictionary() + { + Dictionary<string, string> dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + + foreach (string ext in VideoFileExtensions) + { + dict[ext] = ext; + } + + return dict; + } + + private static readonly Dictionary<string, string> VideoFileExtensionsDictionary = GetVideoFileExtensionsDictionary(); + + // http://en.wikipedia.org/wiki/Internet_media_type + // Add more as needed + + private static Dictionary<string, string> GetMimeTypeLookup() + { + Dictionary<string, string> dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + + dict.Add(".jpg", "image/jpeg"); + dict.Add(".jpeg", "image/jpeg"); + dict.Add(".tbn", "image/jpeg"); + dict.Add(".png", "image/png"); + dict.Add(".gif", "image/gif"); + dict.Add(".tiff", "image/tiff"); + dict.Add(".webp", "image/webp"); + dict.Add(".ico", "image/vnd.microsoft.icon"); + dict.Add(".mpg", "video/mpeg"); + dict.Add(".mpeg", "video/mpeg"); + dict.Add(".ogv", "video/ogg"); + dict.Add(".mov", "video/quicktime"); + dict.Add(".webm", "video/webm"); + dict.Add(".mkv", "video/x-matroska"); + dict.Add(".wmv", "video/x-ms-wmv"); + dict.Add(".flv", "video/x-flv"); + dict.Add(".avi", "video/x-msvideo"); + dict.Add(".asf", "video/x-ms-asf"); + dict.Add(".m4v", "video/x-m4v"); + dict.Add(".m4s", "video/mp4"); + dict.Add(".cbz", "application/x-cbz"); + dict.Add(".cbr", "application/epub+zip"); + dict.Add(".epub", "application/epub+zip"); + dict.Add(".pdf", "application/pdf"); + dict.Add(".mobi", "application/x-mobipocket-ebook"); + + dict.Add(".ass", "text/x-ssa"); + dict.Add(".ssa", "text/x-ssa"); + + return dict; + } + + private static readonly Dictionary<string, string> MimeTypeLookup = GetMimeTypeLookup(); + + private static readonly Dictionary<string, string> ExtensionLookup = CreateExtensionLookup(); + + private static Dictionary<string, string> CreateExtensionLookup() + { + var dict = MimeTypeLookup + .GroupBy(i => i.Value) + .ToDictionary(x => x.Key, x => x.First().Key, StringComparer.OrdinalIgnoreCase); + + dict["image/jpg"] = ".jpg"; + dict["image/x-png"] = ".png"; + + return dict; + } + + public static string GetMimeType(string path) + { + return GetMimeType(path, true); + } + + /// <summary> + /// Gets the type of the MIME. + /// </summary> + public static string GetMimeType(string path, bool enableStreamDefault) + { + if (string.IsNullOrEmpty(path)) + { + throw new ArgumentNullException("path"); + } + + var ext = Path.GetExtension(path) ?? string.Empty; + + string result; + if (MimeTypeLookup.TryGetValue(ext, out result)) + { + return result; + } + + // Type video + if (StringHelper.EqualsIgnoreCase(ext, ".3gp")) + { + return "video/3gpp"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".3g2")) + { + return "video/3gpp2"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".ts")) + { + return "video/mp2t"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".mpd")) + { + return "video/vnd.mpeg.dash.mpd"; + } + + // Catch-all for all video types that don't require specific mime types + if (VideoFileExtensionsDictionary.ContainsKey(ext)) + { + return "video/" + ext.TrimStart('.').ToLower(); + } + + // Type text + if (StringHelper.EqualsIgnoreCase(ext, ".css")) + { + return "text/css"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".csv")) + { + return "text/csv"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".html")) + { + return "text/html; charset=UTF-8"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".htm")) + { + return "text/html; charset=UTF-8"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".txt")) + { + return "text/plain"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".xml")) + { + return "application/xml"; + } + + // Type audio + if (StringHelper.EqualsIgnoreCase(ext, ".mp3")) + { + return "audio/mpeg"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".m4a")) + { + return "audio/mp4"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".aac")) + { + return "audio/mp4"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".webma")) + { + return "audio/webm"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".wav")) + { + return "audio/wav"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".wma")) + { + return "audio/x-ms-wma"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".flac")) + { + return "audio/flac"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".aac")) + { + return "audio/x-aac"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".ogg")) + { + return "audio/ogg"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".oga")) + { + return "audio/ogg"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".opus")) + { + return "audio/ogg"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".ac3")) + { + return "audio/ac3"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".dsf")) + { + return "audio/dsf"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".m4b")) + { + return "audio/m4b"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".xsp")) + { + return "audio/xsp"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".dsp")) + { + return "audio/dsp"; + } + + // Playlists + if (StringHelper.EqualsIgnoreCase(ext, ".m3u8")) + { + return "application/x-mpegURL"; + } + + // Misc + if (StringHelper.EqualsIgnoreCase(ext, ".dll")) + { + return "application/octet-stream"; + } + + // Web + if (StringHelper.EqualsIgnoreCase(ext, ".js")) + { + return "application/x-javascript"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".json")) + { + return "application/json"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".map")) + { + return "application/x-javascript"; + } + + if (StringHelper.EqualsIgnoreCase(ext, ".woff")) + { + return "font/woff"; + } + + if (StringHelper.EqualsIgnoreCase(ext, ".ttf")) + { + return "font/ttf"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".eot")) + { + return "application/vnd.ms-fontobject"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".svg")) + { + return "image/svg+xml"; + } + if (StringHelper.EqualsIgnoreCase(ext, ".svgz")) + { + return "image/svg+xml"; + } + + if (StringHelper.EqualsIgnoreCase(ext, ".srt")) + { + return "text/plain"; + } + + if (StringHelper.EqualsIgnoreCase(ext, ".vtt")) + { + return "text/vtt"; + } + + if (StringHelper.EqualsIgnoreCase(ext, ".ttml")) + { + return "application/ttml+xml"; + } + + if (enableStreamDefault) + { + return "application/octet-stream"; + } + + return null; + } + + public static string ToExtension(string mimeType) + { + if (string.IsNullOrEmpty(mimeType)) + { + throw new ArgumentNullException("mimeType"); + } + + // handle text/html; charset=UTF-8 + mimeType = mimeType.Split(';')[0]; + + string result; + if (ExtensionLookup.TryGetValue(mimeType, out result)) + { + return result; + } + return null; + } + } +} diff --git a/MediaBrowser.Model/Net/NetworkShare.cs b/MediaBrowser.Model/Net/NetworkShare.cs new file mode 100644 index 000000000..5ce84eeed --- /dev/null +++ b/MediaBrowser.Model/Net/NetworkShare.cs @@ -0,0 +1,31 @@ + +namespace MediaBrowser.Model.Net +{ + public class NetworkShare + { + /// <summary> + /// The name of the computer that this share belongs to + /// </summary> + public string Server { get; set; } + + /// <summary> + /// Share name + /// </summary> + public string Name { get; set; } + + /// <summary> + /// Local path + /// </summary> + public string Path { get; set; } + + /// <summary> + /// Share type + /// </summary> + public NetworkShareType ShareType { get; set; } + + /// <summary> + /// Comment + /// </summary> + public string Remark { get; set; } + } +} diff --git a/MediaBrowser.Model/Net/NetworkShareType.cs b/MediaBrowser.Model/Net/NetworkShareType.cs new file mode 100644 index 000000000..41dc9003e --- /dev/null +++ b/MediaBrowser.Model/Net/NetworkShareType.cs @@ -0,0 +1,30 @@ + +namespace MediaBrowser.Model.Net +{ + /// <summary> + /// Enum NetworkShareType + /// </summary> + public enum NetworkShareType + { + /// <summary> + /// Disk share + /// </summary> + Disk, + /// <summary> + /// Printer share + /// </summary> + Printer, + /// <summary> + /// Device share + /// </summary> + Device, + /// <summary> + /// IPC share + /// </summary> + Ipc, + /// <summary> + /// Special share + /// </summary> + Special + } +} diff --git a/MediaBrowser.Model/Net/SocketReceiveResult.cs b/MediaBrowser.Model/Net/SocketReceiveResult.cs new file mode 100644 index 000000000..483e2297b --- /dev/null +++ b/MediaBrowser.Model/Net/SocketReceiveResult.cs @@ -0,0 +1,25 @@ + +namespace MediaBrowser.Model.Net +{ + /// <summary> + /// Used by the sockets wrapper to hold raw data received from a UDP socket. + /// </summary> + public sealed class SocketReceiveResult + { + /// <summary> + /// The buffer to place received data into. + /// </summary> + public byte[] Buffer { get; set; } + + /// <summary> + /// The number of bytes received. + /// </summary> + public int ReceivedBytes { get; set; } + + /// <summary> + /// The <see cref="IpEndPointInfo"/> the data was received from. + /// </summary> + public IpEndPointInfo RemoteEndPoint { get; set; } + public IpAddressInfo LocalIPAddress { get; set; } + } +} diff --git a/MediaBrowser.Model/Net/WebSocketMessage.cs b/MediaBrowser.Model/Net/WebSocketMessage.cs new file mode 100644 index 000000000..c049a96ef --- /dev/null +++ b/MediaBrowser.Model/Net/WebSocketMessage.cs @@ -0,0 +1,24 @@ + +namespace MediaBrowser.Model.Net +{ + /// <summary> + /// Class WebSocketMessage + /// </summary> + /// <typeparam name="T"></typeparam> + public class WebSocketMessage<T> + { + /// <summary> + /// Gets or sets the type of the message. + /// </summary> + /// <value>The type of the message.</value> + public string MessageType { get; set; } + public string MessageId { get; set; } + public string ServerId { get; set; } + /// <summary> + /// Gets or sets the data. + /// </summary> + /// <value>The data.</value> + public T Data { get; set; } + } + +} diff --git a/MediaBrowser.Model/News/INewsService.cs b/MediaBrowser.Model/News/INewsService.cs new file mode 100644 index 000000000..4c92664d9 --- /dev/null +++ b/MediaBrowser.Model/News/INewsService.cs @@ -0,0 +1,17 @@ +using MediaBrowser.Model.Querying; + +namespace MediaBrowser.Model.News +{ + /// <summary> + /// Interface INewsFeed + /// </summary> + public interface INewsService + { + /// <summary> + /// Gets the product news. + /// </summary> + /// <param name="query">The query.</param> + /// <returns>QueryResult{NewsItem}.</returns> + QueryResult<NewsItem> GetProductNews(NewsQuery query); + } +} diff --git a/MediaBrowser.Model/News/NewsItem.cs b/MediaBrowser.Model/News/NewsItem.cs new file mode 100644 index 000000000..2a05c420a --- /dev/null +++ b/MediaBrowser.Model/News/NewsItem.cs @@ -0,0 +1,14 @@ +using System; + +namespace MediaBrowser.Model.News +{ + public class NewsItem + { + public string Title { get; set; } + public string Link { get; set; } + public string Description { get; set; } + public string DescriptionHtml { get; set; } + public string Guid { get; set; } + public DateTime Date { get; set; } + } +} diff --git a/MediaBrowser.Model/News/NewsQuery.cs b/MediaBrowser.Model/News/NewsQuery.cs new file mode 100644 index 000000000..567888921 --- /dev/null +++ b/MediaBrowser.Model/News/NewsQuery.cs @@ -0,0 +1,9 @@ +namespace MediaBrowser.Model.News +{ + public class NewsQuery + { + public int? StartIndex { get; set; } + + public int? Limit { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Notifications/NotificationLevel.cs b/MediaBrowser.Model/Notifications/NotificationLevel.cs new file mode 100644 index 000000000..a49ee2fe6 --- /dev/null +++ b/MediaBrowser.Model/Notifications/NotificationLevel.cs @@ -0,0 +1,10 @@ + +namespace MediaBrowser.Model.Notifications +{ + public enum NotificationLevel + { + Normal = 0, + Warning = 1, + Error = 2 + } +} diff --git a/MediaBrowser.Model/Notifications/NotificationOption.cs b/MediaBrowser.Model/Notifications/NotificationOption.cs new file mode 100644 index 000000000..cda9e311d --- /dev/null +++ b/MediaBrowser.Model/Notifications/NotificationOption.cs @@ -0,0 +1,56 @@ +using System; + +namespace MediaBrowser.Model.Notifications +{ + public class NotificationOption + { + public string Type { get; set; } + + /// <summary> + /// User Ids to not monitor (it's opt out) + /// </summary> + public string[] DisabledMonitorUsers { get; set; } + + /// <summary> + /// User Ids to send to (if SendToUserMode == Custom) + /// </summary> + public string[] SendToUsers { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this <see cref="NotificationOption"/> is enabled. + /// </summary> + /// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value> + public bool Enabled { get; set; } + + /// <summary> + /// Gets or sets the title format string. + /// </summary> + /// <value>The title format string.</value> + public string Title { get; set; } + + /// <summary> + /// Gets or sets the description. + /// </summary> + /// <value>The description.</value> + public string Description { get; set; } + + /// <summary> + /// Gets or sets the disabled services. + /// </summary> + /// <value>The disabled services.</value> + public string[] DisabledServices { get; set; } + + /// <summary> + /// Gets or sets the send to user mode. + /// </summary> + /// <value>The send to user mode.</value> + public SendToUserType SendToUserMode { get; set; } + + public NotificationOption() + { + DisabledServices = new string[] {}; + DisabledMonitorUsers = new string[] {}; + SendToUsers = new string[] {}; + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Notifications/NotificationOptions.cs b/MediaBrowser.Model/Notifications/NotificationOptions.cs new file mode 100644 index 000000000..158e00b39 --- /dev/null +++ b/MediaBrowser.Model/Notifications/NotificationOptions.cs @@ -0,0 +1,132 @@ +using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.Users; +using System; + +namespace MediaBrowser.Model.Notifications +{ + public class NotificationOptions + { + public NotificationOption[] Options { get; set; } + + public NotificationOptions() + { + Options = new[] + { + new NotificationOption + { + Type = NotificationType.TaskFailed.ToString(), + Enabled = true, + SendToUserMode = SendToUserType.Admins + }, + new NotificationOption + { + Type = NotificationType.ServerRestartRequired.ToString(), + Enabled = true, + SendToUserMode = SendToUserType.Admins + }, + new NotificationOption + { + Type = NotificationType.ApplicationUpdateAvailable.ToString(), + Enabled = true, + SendToUserMode = SendToUserType.Admins + }, + new NotificationOption + { + Type = NotificationType.ApplicationUpdateInstalled.ToString(), + Enabled = true, + SendToUserMode = SendToUserType.Admins + }, + new NotificationOption + { + Type = NotificationType.PluginUpdateInstalled.ToString(), + Enabled = true, + SendToUserMode = SendToUserType.Admins + }, + new NotificationOption + { + Type = NotificationType.PluginUninstalled.ToString(), + Enabled = true, + SendToUserMode = SendToUserType.Admins + }, + new NotificationOption + { + Type = NotificationType.InstallationFailed.ToString(), + Enabled = true, + SendToUserMode = SendToUserType.Admins + }, + new NotificationOption + { + Type = NotificationType.PluginInstalled.ToString(), + Enabled = true, + SendToUserMode = SendToUserType.Admins + }, + new NotificationOption + { + Type = NotificationType.PluginError.ToString(), + Enabled = true, + SendToUserMode = SendToUserType.Admins + }, + new NotificationOption + { + Type = NotificationType.UserLockedOut.ToString(), + Enabled = true, + SendToUserMode = SendToUserType.Admins + } + }; + } + + public NotificationOption GetOptions(string type) + { + foreach (NotificationOption i in Options) + { + if (StringHelper.EqualsIgnoreCase(type, i.Type)) return i; + } + return null; + } + + public bool IsEnabled(string type) + { + NotificationOption opt = GetOptions(type); + + return opt != null && opt.Enabled; + } + + public bool IsServiceEnabled(string service, string notificationType) + { + NotificationOption opt = GetOptions(notificationType); + + return opt == null || + !ListHelper.ContainsIgnoreCase(opt.DisabledServices, service); + } + + public bool IsEnabledToMonitorUser(string type, Guid userId) + { + NotificationOption opt = GetOptions(type); + + return opt != null && opt.Enabled && + !ListHelper.ContainsIgnoreCase(opt.DisabledMonitorUsers, userId.ToString("")); + } + + public bool IsEnabledToSendToUser(string type, string userId, UserPolicy userPolicy) + { + NotificationOption opt = GetOptions(type); + + if (opt != null && opt.Enabled) + { + if (opt.SendToUserMode == SendToUserType.All) + { + return true; + } + + if (opt.SendToUserMode == SendToUserType.Admins && userPolicy.IsAdministrator) + { + return true; + } + + return ListHelper.ContainsIgnoreCase(opt.SendToUsers, userId); + } + + return false; + } + } +} diff --git a/MediaBrowser.Model/Notifications/NotificationRequest.cs b/MediaBrowser.Model/Notifications/NotificationRequest.cs new file mode 100644 index 000000000..9c89e40fb --- /dev/null +++ b/MediaBrowser.Model/Notifications/NotificationRequest.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Model.Notifications +{ + public class NotificationRequest + { + public string Name { get; set; } + + public string Description { get; set; } + + public string Url { get; set; } + + public NotificationLevel Level { get; set; } + + public Guid[] UserIds { get; set; } + + public DateTime Date { get; set; } + + /// <summary> + /// The corresponding type name used in configuration. Not for display. + /// </summary> + public string NotificationType { get; set; } + + public Dictionary<string, string> Variables { get; set; } + + public SendToUserType? SendToUserMode { get; set; } + + public NotificationRequest() + { + UserIds = Array.Empty<Guid>(); + Date = DateTime.UtcNow; + } + } +} diff --git a/MediaBrowser.Model/Notifications/NotificationServiceInfo.cs b/MediaBrowser.Model/Notifications/NotificationServiceInfo.cs new file mode 100644 index 000000000..0ffe7d4ae --- /dev/null +++ b/MediaBrowser.Model/Notifications/NotificationServiceInfo.cs @@ -0,0 +1,8 @@ +namespace MediaBrowser.Model.Notifications +{ + public class NotificationServiceInfo + { + public string Name { get; set; } + public string Id { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Notifications/NotificationType.cs b/MediaBrowser.Model/Notifications/NotificationType.cs new file mode 100644 index 000000000..eefd15808 --- /dev/null +++ b/MediaBrowser.Model/Notifications/NotificationType.cs @@ -0,0 +1,24 @@ +namespace MediaBrowser.Model.Notifications +{ + public enum NotificationType + { + ApplicationUpdateAvailable, + ApplicationUpdateInstalled, + AudioPlayback, + GamePlayback, + VideoPlayback, + AudioPlaybackStopped, + GamePlaybackStopped, + VideoPlaybackStopped, + InstallationFailed, + PluginError, + PluginInstalled, + PluginUpdateInstalled, + PluginUninstalled, + NewLibraryContent, + ServerRestartRequired, + TaskFailed, + CameraImageUploaded, + UserLockedOut + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs b/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs new file mode 100644 index 000000000..4c3283d8e --- /dev/null +++ b/MediaBrowser.Model/Notifications/NotificationTypeInfo.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System; + +namespace MediaBrowser.Model.Notifications +{ + public class NotificationTypeInfo + { + public string Type { get; set; } + + public string Name { get; set; } + + public bool Enabled { get; set; } + + public string Category { get; set; } + + public bool IsBasedOnUserEvent { get; set; } + + public string DefaultTitle { get; set; } + + public string DefaultDescription { get; set; } + + public string[] Variables { get; set; } + + public NotificationTypeInfo() + { + Variables = new string[] {}; + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Notifications/SendToUserType.cs b/MediaBrowser.Model/Notifications/SendToUserType.cs new file mode 100644 index 000000000..1998d3102 --- /dev/null +++ b/MediaBrowser.Model/Notifications/SendToUserType.cs @@ -0,0 +1,9 @@ +namespace MediaBrowser.Model.Notifications +{ + public enum SendToUserType + { + All = 0, + Admins = 1, + Custom = 2 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs b/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs new file mode 100644 index 000000000..4c5c8bf51 --- /dev/null +++ b/MediaBrowser.Model/Playlists/PlaylistCreationRequest.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System; + +namespace MediaBrowser.Model.Playlists +{ + public class PlaylistCreationRequest + { + public string Name { get; set; } + + public Guid[] ItemIdList { get; set; } + + public string MediaType { get; set; } + + public Guid UserId { get; set; } + + public PlaylistCreationRequest() + { + ItemIdList = Array.Empty<Guid>(); + } + } +} diff --git a/MediaBrowser.Model/Playlists/PlaylistCreationResult.cs b/MediaBrowser.Model/Playlists/PlaylistCreationResult.cs new file mode 100644 index 000000000..bbab8a18d --- /dev/null +++ b/MediaBrowser.Model/Playlists/PlaylistCreationResult.cs @@ -0,0 +1,8 @@ + +namespace MediaBrowser.Model.Playlists +{ + public class PlaylistCreationResult + { + public string Id { get; set; } + } +} diff --git a/MediaBrowser.Model/Playlists/PlaylistItemQuery.cs b/MediaBrowser.Model/Playlists/PlaylistItemQuery.cs new file mode 100644 index 000000000..0f6a0c8c5 --- /dev/null +++ b/MediaBrowser.Model/Playlists/PlaylistItemQuery.cs @@ -0,0 +1,37 @@ +using MediaBrowser.Model.Querying; + +namespace MediaBrowser.Model.Playlists +{ + public class PlaylistItemQuery + { + /// <summary> + /// Gets or sets the identifier. + /// </summary> + /// <value>The identifier.</value> + public string Id { 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 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; } + + /// <summary> + /// Gets or sets the fields. + /// </summary> + /// <value>The fields.</value> + public ItemFields[] Fields { get; set; } + } +} diff --git a/MediaBrowser.Model/Plugins/BasePluginConfiguration.cs b/MediaBrowser.Model/Plugins/BasePluginConfiguration.cs new file mode 100644 index 000000000..9a8bfadd1 --- /dev/null +++ b/MediaBrowser.Model/Plugins/BasePluginConfiguration.cs @@ -0,0 +1,10 @@ + +namespace MediaBrowser.Model.Plugins +{ + /// <summary> + /// Class BasePluginConfiguration + /// </summary> + public class BasePluginConfiguration + { + } +} diff --git a/MediaBrowser.Model/Plugins/IHasWebPages.cs b/MediaBrowser.Model/Plugins/IHasWebPages.cs new file mode 100644 index 000000000..0745c3c60 --- /dev/null +++ b/MediaBrowser.Model/Plugins/IHasWebPages.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Model.Plugins +{ + public interface IHasWebPages + { + IEnumerable<PluginPageInfo> GetPages(); + } +} diff --git a/MediaBrowser.Model/Plugins/PluginInfo.cs b/MediaBrowser.Model/Plugins/PluginInfo.cs new file mode 100644 index 000000000..e7b16b0ec --- /dev/null +++ b/MediaBrowser.Model/Plugins/PluginInfo.cs @@ -0,0 +1,45 @@ +using System; + +namespace MediaBrowser.Model.Plugins +{ + /// <summary> + /// This is a serializable stub class that is used by the api to provide information about installed plugins. + /// </summary> + public class PluginInfo + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the version. + /// </summary> + /// <value>The version.</value> + public string Version { get; set; } + + /// <summary> + /// Gets or sets the name of the configuration file. + /// </summary> + /// <value>The name of the configuration file.</value> + public string ConfigurationFileName { get; set; } + + /// <summary> + /// Gets or sets the description. + /// </summary> + /// <value>The description.</value> + public string Description { get; set; } + + /// <summary> + /// Gets or sets the unique id. + /// </summary> + /// <value>The unique id.</value> + public string Id { get; set; } + /// <summary> + /// Gets or sets the image URL. + /// </summary> + /// <value>The image URL.</value> + public string ImageUrl { get; set; } + } +} diff --git a/MediaBrowser.Model/Plugins/PluginPageInfo.cs b/MediaBrowser.Model/Plugins/PluginPageInfo.cs new file mode 100644 index 000000000..045a0072c --- /dev/null +++ b/MediaBrowser.Model/Plugins/PluginPageInfo.cs @@ -0,0 +1,17 @@ +namespace MediaBrowser.Model.Plugins +{ + public class PluginPageInfo + { + public string Name { get; set; } + + public string DisplayName { get; set; } + + public string EmbeddedResourcePath { get; set; } + + public bool EnableInMainMenu { get; set; } + + public string MenuSection { get; set; } + + public string MenuIcon { get; set; } + } +} diff --git a/MediaBrowser.Model/Properties/AssemblyInfo.cs b/MediaBrowser.Model/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..fabfd908b --- /dev/null +++ b/MediaBrowser.Model/Properties/AssemblyInfo.cs @@ -0,0 +1,23 @@ +using System.Reflection; +using System.Resources; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MediaBrowser.Model")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MediaBrowser.Model")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: NeutralResourcesLanguage("en")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +//
\ No newline at end of file diff --git a/MediaBrowser.Model/Providers/ExternalIdInfo.cs b/MediaBrowser.Model/Providers/ExternalIdInfo.cs new file mode 100644 index 000000000..2c5cfe91b --- /dev/null +++ b/MediaBrowser.Model/Providers/ExternalIdInfo.cs @@ -0,0 +1,24 @@ + +namespace MediaBrowser.Model.Providers +{ + public class ExternalIdInfo + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the key. + /// </summary> + /// <value>The key.</value> + public string Key { get; set; } + + /// <summary> + /// Gets or sets the URL format string. + /// </summary> + /// <value>The URL format string.</value> + public string UrlFormatString { get; set; } + } +} diff --git a/MediaBrowser.Model/Providers/ExternalUrl.cs b/MediaBrowser.Model/Providers/ExternalUrl.cs new file mode 100644 index 000000000..fb744f446 --- /dev/null +++ b/MediaBrowser.Model/Providers/ExternalUrl.cs @@ -0,0 +1,17 @@ +namespace MediaBrowser.Model.Providers +{ + public class ExternalUrl + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the type of the item. + /// </summary> + /// <value>The type of the item.</value> + public string Url { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Providers/ImageProviderInfo.cs b/MediaBrowser.Model/Providers/ImageProviderInfo.cs new file mode 100644 index 000000000..199552640 --- /dev/null +++ b/MediaBrowser.Model/Providers/ImageProviderInfo.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Model.Providers +{ + /// <summary> + /// Class ImageProviderInfo. + /// </summary> + public class ImageProviderInfo + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + public ImageType[] SupportedImages { get; set; } + + public ImageProviderInfo() + { + SupportedImages = new ImageType[] { }; + } + } +} diff --git a/MediaBrowser.Model/Providers/RemoteImageInfo.cs b/MediaBrowser.Model/Providers/RemoteImageInfo.cs new file mode 100644 index 000000000..6db7f77bd --- /dev/null +++ b/MediaBrowser.Model/Providers/RemoteImageInfo.cs @@ -0,0 +1,71 @@ +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Model.Providers +{ + /// <summary> + /// Class RemoteImageInfo + /// </summary> + public class RemoteImageInfo + { + /// <summary> + /// Gets or sets the name of the provider. + /// </summary> + /// <value>The name of the provider.</value> + public string ProviderName { get; set; } + + /// <summary> + /// Gets or sets the URL. + /// </summary> + /// <value>The URL.</value> + public string Url { get; set; } + + /// <summary> + /// Gets a url used for previewing a smaller version + /// </summary> + public string ThumbnailUrl { get; set; } + + /// <summary> + /// Gets or sets the height. + /// </summary> + /// <value>The height.</value> + public int? Height { get; set; } + + /// <summary> + /// Gets or sets the width. + /// </summary> + /// <value>The width.</value> + public int? Width { get; set; } + + /// <summary> + /// Gets or sets the community rating. + /// </summary> + /// <value>The community rating.</value> + public double? CommunityRating { get; set; } + + /// <summary> + /// Gets or sets the vote count. + /// </summary> + /// <value>The vote count.</value> + public int? VoteCount { get; set; } + + /// <summary> + /// Gets or sets the language. + /// </summary> + /// <value>The language.</value> + public string Language { get; set; } + + /// <summary> + /// Gets or sets the type. + /// </summary> + /// <value>The type.</value> + public ImageType Type { get; set; } + + /// <summary> + /// Gets or sets the type of the rating. + /// </summary> + /// <value>The type of the rating.</value> + public RatingType RatingType { get; set; } + } + +} diff --git a/MediaBrowser.Model/Providers/RemoteImageQuery.cs b/MediaBrowser.Model/Providers/RemoteImageQuery.cs new file mode 100644 index 000000000..8d5231a25 --- /dev/null +++ b/MediaBrowser.Model/Providers/RemoteImageQuery.cs @@ -0,0 +1,15 @@ +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Model.Providers +{ + public class RemoteImageQuery + { + public string ProviderName { get; set; } + + public ImageType? ImageType { get; set; } + + public bool IncludeDisabledProviders { get; set; } + + public bool IncludeAllLanguages { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Providers/RemoteImageResult.cs b/MediaBrowser.Model/Providers/RemoteImageResult.cs new file mode 100644 index 000000000..7e38badfc --- /dev/null +++ b/MediaBrowser.Model/Providers/RemoteImageResult.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Model.Providers +{ + /// <summary> + /// Class RemoteImageResult. + /// </summary> + public class RemoteImageResult + { + /// <summary> + /// Gets or sets the images. + /// </summary> + /// <value>The images.</value> + public RemoteImageInfo[] Images { get; set; } + + /// <summary> + /// Gets or sets the total record count. + /// </summary> + /// <value>The total record count.</value> + public int TotalRecordCount { get; set; } + + /// <summary> + /// Gets or sets the providers. + /// </summary> + /// <value>The providers.</value> + public string[] Providers { get; set; } + } +} diff --git a/MediaBrowser.Model/Providers/RemoteSearchResult.cs b/MediaBrowser.Model/Providers/RemoteSearchResult.cs new file mode 100644 index 000000000..b63cf2a9f --- /dev/null +++ b/MediaBrowser.Model/Providers/RemoteSearchResult.cs @@ -0,0 +1,46 @@ +using MediaBrowser.Model.Entities; +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Model.Providers +{ + public class RemoteSearchResult : IHasProviderIds + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + /// <summary> + /// Gets or sets the provider ids. + /// </summary> + /// <value>The provider ids.</value> + public Dictionary<string, string> ProviderIds { get; set; } + /// <summary> + /// Gets or sets the year. + /// </summary> + /// <value>The year.</value> + public int? ProductionYear { get; set; } + public int? IndexNumber { get; set; } + public int? IndexNumberEnd { get; set; } + public int? ParentIndexNumber { get; set; } + + public DateTime? PremiereDate { get; set; } + + public string ImageUrl { get; set; } + + public string SearchProviderName { get; set; } + + public string GameSystem { get; set; } + public string Overview { get; set; } + + public RemoteSearchResult AlbumArtist { get; set; } + public RemoteSearchResult[] Artists { get; set; } + + public RemoteSearchResult() + { + ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + Artists = new RemoteSearchResult[] { }; + } + } +} diff --git a/MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs b/MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs new file mode 100644 index 000000000..0a4a52cd5 --- /dev/null +++ b/MediaBrowser.Model/Providers/RemoteSubtitleInfo.cs @@ -0,0 +1,19 @@ +using System; + +namespace MediaBrowser.Model.Providers +{ + public class RemoteSubtitleInfo + { + public string ThreeLetterISOLanguageName { get; set; } + public string Id { get; set; } + public string ProviderName { get; set; } + public string Name { get; set; } + public string Format { get; set; } + public string Author { get; set; } + public string Comment { get; set; } + public DateTime? DateCreated { get; set; } + public float? CommunityRating { get; set; } + public int? DownloadCount { get; set; } + public bool? IsHashMatch { get; set; } + } +} diff --git a/MediaBrowser.Model/Providers/SubtitleOptions.cs b/MediaBrowser.Model/Providers/SubtitleOptions.cs new file mode 100644 index 000000000..f67b8870d --- /dev/null +++ b/MediaBrowser.Model/Providers/SubtitleOptions.cs @@ -0,0 +1,27 @@ +using System; + +namespace MediaBrowser.Model.Providers +{ + public class SubtitleOptions + { + public bool SkipIfEmbeddedSubtitlesPresent { get; set; } + public bool SkipIfAudioTrackMatches { get; set; } + public string[] DownloadLanguages { get; set; } + public bool DownloadMovieSubtitles { get; set; } + public bool DownloadEpisodeSubtitles { get; set; } + + public string OpenSubtitlesUsername { get; set; } + public string OpenSubtitlesPasswordHash { get; set; } + public bool IsOpenSubtitleVipAccount { get; set; } + + public bool RequirePerfectMatch { get; set; } + + public SubtitleOptions() + { + DownloadLanguages = new string[] {}; + + SkipIfAudioTrackMatches = true; + RequirePerfectMatch = true; + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Providers/SubtitleProviderInfo.cs b/MediaBrowser.Model/Providers/SubtitleProviderInfo.cs new file mode 100644 index 000000000..ecce18bd5 --- /dev/null +++ b/MediaBrowser.Model/Providers/SubtitleProviderInfo.cs @@ -0,0 +1,8 @@ +namespace MediaBrowser.Model.Providers +{ + public class SubtitleProviderInfo + { + public string Name { get; set; } + public string Id { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Querying/AllThemeMediaResult.cs b/MediaBrowser.Model/Querying/AllThemeMediaResult.cs new file mode 100644 index 000000000..89640eb65 --- /dev/null +++ b/MediaBrowser.Model/Querying/AllThemeMediaResult.cs @@ -0,0 +1,20 @@ +namespace MediaBrowser.Model.Querying +{ + public class AllThemeMediaResult + { + public ThemeMediaResult ThemeVideosResult { get; set; } + + public ThemeMediaResult ThemeSongsResult { get; set; } + + public ThemeMediaResult SoundtrackSongsResult { get; set; } + + public AllThemeMediaResult() + { + ThemeVideosResult = new ThemeMediaResult(); + + ThemeSongsResult = new ThemeMediaResult(); + + SoundtrackSongsResult = new ThemeMediaResult(); + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Querying/EpisodeQuery.cs b/MediaBrowser.Model/Querying/EpisodeQuery.cs new file mode 100644 index 000000000..78fe943e3 --- /dev/null +++ b/MediaBrowser.Model/Querying/EpisodeQuery.cs @@ -0,0 +1,62 @@ + +namespace MediaBrowser.Model.Querying +{ + public class EpisodeQuery + { + /// <summary> + /// Gets or sets the user identifier. + /// </summary> + /// <value>The user identifier.</value> + public string UserId { get; set; } + /// <summary> + /// Gets or sets the season identifier. + /// </summary> + /// <value>The season identifier.</value> + public string SeasonId { get; set; } + /// <summary> + /// Gets or sets the series identifier. + /// </summary> + /// <value>The series identifier.</value> + public string SeriesId { get; set; } + /// <summary> + /// Gets or sets a value indicating whether this instance is missing. + /// </summary> + /// <value><c>null</c> if [is missing] contains no value, <c>true</c> if [is missing]; otherwise, <c>false</c>.</value> + public bool? IsMissing { get; set; } + /// <summary> + /// Gets or sets a value indicating whether this instance is virtual unaired. + /// </summary> + /// <value><c>null</c> if [is virtual unaired] contains no value, <c>true</c> if [is virtual unaired]; otherwise, <c>false</c>.</value> + public bool? IsVirtualUnaired { get; set; } + /// <summary> + /// Gets or sets the season number. + /// </summary> + /// <value>The season number.</value> + public int? SeasonNumber { get; set; } + /// <summary> + /// Gets or sets the fields. + /// </summary> + /// <value>The fields.</value> + public ItemFields[] Fields { 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; } + /// <summary> + /// Gets or sets the start item identifier. + /// </summary> + /// <value>The start item identifier.</value> + public string StartItemId { get; set; } + + public EpisodeQuery() + { + Fields = new ItemFields[] { }; + } + } +} diff --git a/MediaBrowser.Model/Querying/ItemCountsQuery.cs b/MediaBrowser.Model/Querying/ItemCountsQuery.cs new file mode 100644 index 000000000..0bf681537 --- /dev/null +++ b/MediaBrowser.Model/Querying/ItemCountsQuery.cs @@ -0,0 +1,21 @@ + +namespace MediaBrowser.Model.Querying +{ + /// <summary> + /// Class ItemCountsQuery + /// </summary> + public class ItemCountsQuery + { + /// <summary> + /// Gets or sets the user id. + /// </summary> + /// <value>The user id.</value> + public string UserId { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is favorite. + /// </summary> + /// <value><c>null</c> if [is favorite] contains no value, <c>true</c> if [is favorite]; otherwise, <c>false</c>.</value> + public bool? IsFavorite { get; set; } + } +} diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs new file mode 100644 index 000000000..92fa3822b --- /dev/null +++ b/MediaBrowser.Model/Querying/ItemFields.cs @@ -0,0 +1,222 @@ +namespace MediaBrowser.Model.Querying +{ + /// <summary> + /// Used to control the data that gets attached to DtoBaseItems + /// </summary> + public enum ItemFields + { + /// <summary> + /// The air time + /// </summary> + AirTime, + + /// <summary> + /// The can delete + /// </summary> + CanDelete, + + /// <summary> + /// The can download + /// </summary> + CanDownload, + + /// <summary> + /// The channel information + /// </summary> + ChannelInfo, + + /// <summary> + /// The chapters + /// </summary> + Chapters, + + ChildCount, + + /// <summary> + /// The cumulative run time ticks + /// </summary> + CumulativeRunTimeTicks, + + /// <summary> + /// The custom rating + /// </summary> + CustomRating, + + /// <summary> + /// The date created of the item + /// </summary> + DateCreated, + + /// <summary> + /// The date last media added + /// </summary> + DateLastMediaAdded, + + /// <summary> + /// Item display preferences + /// </summary> + DisplayPreferencesId, + + /// <summary> + /// The etag + /// </summary> + Etag, + + /// <summary> + /// The external urls + /// </summary> + ExternalUrls, + + /// <summary> + /// Genres + /// </summary> + Genres, + + /// <summary> + /// The home page URL + /// </summary> + HomePageUrl, + + /// <summary> + /// The item counts + /// </summary> + ItemCounts, + + /// <summary> + /// The media source count + /// </summary> + MediaSourceCount, + + /// <summary> + /// The media versions + /// </summary> + MediaSources, + + OriginalTitle, + + /// <summary> + /// The item overview + /// </summary> + Overview, + + /// <summary> + /// The id of the item's parent + /// </summary> + ParentId, + + /// <summary> + /// The physical path of the item + /// </summary> + Path, + + /// <summary> + /// The list of people for the item + /// </summary> + People, + + PlayAccess, + + /// <summary> + /// The production locations + /// </summary> + ProductionLocations, + + /// <summary> + /// Imdb, tmdb, etc + /// </summary> + ProviderIds, + + /// <summary> + /// The aspect ratio of the primary image + /// </summary> + PrimaryImageAspectRatio, + + RecursiveItemCount, + + /// <summary> + /// The settings + /// </summary> + Settings, + + /// <summary> + /// The screenshot image tags + /// </summary> + ScreenshotImageTags, + + SeriesPrimaryImage, + + /// <summary> + /// The series studio + /// </summary> + SeriesStudio, + + /// <summary> + /// The sort name of the item + /// </summary> + SortName, + + /// <summary> + /// The special episode numbers + /// </summary> + SpecialEpisodeNumbers, + + /// <summary> + /// The studios of the item + /// </summary> + Studios, + + BasicSyncInfo, + /// <summary> + /// The synchronize information + /// </summary> + SyncInfo, + + /// <summary> + /// The taglines of the item + /// </summary> + Taglines, + + /// <summary> + /// The tags + /// </summary> + Tags, + + /// <summary> + /// The trailer url of the item + /// </summary> + RemoteTrailers, + + /// <summary> + /// The media streams + /// </summary> + MediaStreams, + + /// <summary> + /// The season user data + /// </summary> + SeasonUserData, + + /// <summary> + /// The service name + /// </summary> + ServiceName, + ThemeSongIds, + ThemeVideoIds, + ExternalEtag, + PresentationUniqueKey, + InheritedParentalRatingValue, + ExternalSeriesId, + SeriesPresentationUniqueKey, + DateLastRefreshed, + DateLastSaved, + RefreshState, + ChannelImage, + EnableMediaSourceDisplay, + Width, + Height, + ExtraIds, + LocalTrailerCount, + IsHD, + SpecialFeatureCount + } +} diff --git a/MediaBrowser.Model/Querying/ItemFilter.cs b/MediaBrowser.Model/Querying/ItemFilter.cs new file mode 100644 index 000000000..ff28bd08c --- /dev/null +++ b/MediaBrowser.Model/Querying/ItemFilter.cs @@ -0,0 +1,46 @@ + +namespace MediaBrowser.Model.Querying +{ + /// <summary> + /// Enum ItemFilter + /// </summary> + public enum ItemFilter + { + /// <summary> + /// The item is a folder + /// </summary> + IsFolder = 1, + /// <summary> + /// The item is not folder + /// </summary> + IsNotFolder = 2, + /// <summary> + /// The item is unplayed + /// </summary> + IsUnplayed = 3, + /// <summary> + /// The item is played + /// </summary> + IsPlayed = 4, + /// <summary> + /// The item is a favorite + /// </summary> + IsFavorite = 5, + /// <summary> + /// The item is resumable + /// </summary> + IsResumable = 7, + /// <summary> + /// The likes + /// </summary> + Likes = 8, + /// <summary> + /// The dislikes + /// </summary> + Dislikes = 9, + /// <summary> + /// The is favorite or likes + /// </summary> + IsFavoriteOrLikes = 10 + } +} diff --git a/MediaBrowser.Model/Querying/ItemSortBy.cs b/MediaBrowser.Model/Querying/ItemSortBy.cs new file mode 100644 index 000000000..66bdc8aa5 --- /dev/null +++ b/MediaBrowser.Model/Querying/ItemSortBy.cs @@ -0,0 +1,81 @@ + +namespace MediaBrowser.Model.Querying +{ + /// <summary> + /// These represent sort orders that are known by the core + /// </summary> + public static class ItemSortBy + { + public const string AiredEpisodeOrder = "AiredEpisodeOrder"; + /// <summary> + /// The album + /// </summary> + public const string Album = "Album"; + /// <summary> + /// The album artist + /// </summary> + public const string AlbumArtist = "AlbumArtist"; + /// <summary> + /// The artist + /// </summary> + public const string Artist = "Artist"; + /// <summary> + /// The date created + /// </summary> + public const string DateCreated = "DateCreated"; + /// <summary> + /// The official rating + /// </summary> + public const string OfficialRating = "OfficialRating"; + /// <summary> + /// The date played + /// </summary> + public const string DatePlayed = "DatePlayed"; + /// <summary> + /// The premiere date + /// </summary> + public const string PremiereDate = "PremiereDate"; + public const string StartDate = "StartDate"; + /// <summary> + /// The sort name + /// </summary> + public const string SortName = "SortName"; + public const string Name = "Name"; + /// <summary> + /// The random + /// </summary> + public const string Random = "Random"; + /// <summary> + /// The runtime + /// </summary> + public const string Runtime = "Runtime"; + /// <summary> + /// The community rating + /// </summary> + public const string CommunityRating = "CommunityRating"; + /// <summary> + /// The production year + /// </summary> + public const string ProductionYear = "ProductionYear"; + /// <summary> + /// The play count + /// </summary> + public const string PlayCount = "PlayCount"; + /// <summary> + /// The critic rating + /// </summary> + public const string CriticRating = "CriticRating"; + public const string IsFolder = "IsFolder"; + public const string IsUnplayed = "IsUnplayed"; + public const string IsPlayed = "IsPlayed"; + public const string SeriesSortName = "SeriesSortName"; + public const string VideoBitRate = "VideoBitRate"; + public const string AirTime = "AirTime"; + public const string Studio = "Studio"; + public const string Players = "Players"; + public const string GameSystem = "GameSystem"; + public const string IsFavoriteOrLiked = "IsFavoriteOrLiked"; + public const string DateLastContentAdded = "DateLastContentAdded"; + public const string SeriesDatePlayed = "SeriesDatePlayed"; + } +} diff --git a/MediaBrowser.Model/Querying/LatestItemsQuery.cs b/MediaBrowser.Model/Querying/LatestItemsQuery.cs new file mode 100644 index 000000000..88b079595 --- /dev/null +++ b/MediaBrowser.Model/Querying/LatestItemsQuery.cs @@ -0,0 +1,76 @@ +using System; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Model.Querying +{ + public class LatestItemsQuery + { + /// <summary> + /// The user to localize search results for + /// </summary> + /// <value>The user id.</value> + public Guid UserId { get; set; } + + /// <summary> + /// Specify this to localize the search to a specific item or folder. Omit to use the root. + /// </summary> + /// <value>The parent id.</value> + public Guid ParentId { get; set; } + + /// <summary> + /// Skips over a given number of items within the results. Use for paging. + /// </summary> + /// <value>The start index.</value> + public int? StartIndex { get; set; } + + /// <summary> + /// The maximum number of items to return + /// </summary> + /// <value>The limit.</value> + public int? Limit { get; set; } + + /// <summary> + /// Fields to return within the items, in addition to basic information + /// </summary> + /// <value>The fields.</value> + public ItemFields[] Fields { get; set; } + + /// <summary> + /// Gets or sets the include item types. + /// </summary> + /// <value>The include item types.</value> + public string[] IncludeItemTypes { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is played. + /// </summary> + /// <value><c>null</c> if [is played] contains no value, <c>true</c> if [is played]; otherwise, <c>false</c>.</value> + public bool? IsPlayed { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [group items]. + /// </summary> + /// <value><c>true</c> if [group items]; otherwise, <c>false</c>.</value> + public bool GroupItems { get; set; } + /// <summary> + /// Gets or sets a value indicating whether [enable images]. + /// </summary> + /// <value><c>null</c> if [enable images] contains no value, <c>true</c> if [enable images]; otherwise, <c>false</c>.</value> + public bool? EnableImages { get; set; } + /// <summary> + /// Gets or sets the image type limit. + /// </summary> + /// <value>The image type limit.</value> + public int? ImageTypeLimit { get; set; } + /// <summary> + /// Gets or sets the enable image types. + /// </summary> + /// <value>The enable image types.</value> + public ImageType[] EnableImageTypes { get; set; } + + public LatestItemsQuery() + { + EnableImageTypes = new ImageType[] {}; + } + } +} diff --git a/MediaBrowser.Model/Querying/MovieRecommendationQuery.cs b/MediaBrowser.Model/Querying/MovieRecommendationQuery.cs new file mode 100644 index 000000000..91417a4a7 --- /dev/null +++ b/MediaBrowser.Model/Querying/MovieRecommendationQuery.cs @@ -0,0 +1,39 @@ + +namespace MediaBrowser.Model.Querying +{ + public class MovieRecommendationQuery + { + /// <summary> + /// Gets or sets the user identifier. + /// </summary> + /// <value>The user identifier.</value> + public string UserId { get; set; } + /// <summary> + /// Gets or sets the parent identifier. + /// </summary> + /// <value>The parent identifier.</value> + public string ParentId { get; set; } + /// <summary> + /// Gets or sets the item limit. + /// </summary> + /// <value>The item limit.</value> + public int ItemLimit { get; set; } + /// <summary> + /// Gets or sets the category limit. + /// </summary> + /// <value>The category limit.</value> + public int CategoryLimit { get; set; } + /// <summary> + /// Gets or sets the fields. + /// </summary> + /// <value>The fields.</value> + public ItemFields[] Fields { get; set; } + + public MovieRecommendationQuery() + { + ItemLimit = 10; + CategoryLimit = 6; + Fields = new ItemFields[] { }; + } + } +} diff --git a/MediaBrowser.Model/Querying/NextUpQuery.cs b/MediaBrowser.Model/Querying/NextUpQuery.cs new file mode 100644 index 000000000..d20ff99c2 --- /dev/null +++ b/MediaBrowser.Model/Querying/NextUpQuery.cs @@ -0,0 +1,67 @@ +using MediaBrowser.Model.Entities; +using System; + +namespace MediaBrowser.Model.Querying +{ + public class NextUpQuery + { + /// <summary> + /// Gets or sets the user id. + /// </summary> + /// <value>The user id.</value> + public Guid UserId { get; set; } + + /// <summary> + /// Gets or sets the parent identifier. + /// </summary> + /// <value>The parent identifier.</value> + public string ParentId { get; set; } + + /// <summary> + /// Gets or sets the series id. + /// </summary> + /// <value>The series id.</value> + public string SeriesId { get; set; } + + /// <summary> + /// Skips over a given number of items within the results. Use for paging. + /// </summary> + /// <value>The start index.</value> + public int? StartIndex { get; set; } + + /// <summary> + /// The maximum number of items to return + /// </summary> + /// <value>The limit.</value> + public int? Limit { get; set; } + + /// <summary> + /// Fields to return within the items, in addition to basic information + /// </summary> + /// <value>The fields.</value> + public ItemFields[] Fields { get; set; } + /// <summary> + /// Gets or sets a value indicating whether [enable images]. + /// </summary> + /// <value><c>null</c> if [enable images] contains no value, <c>true</c> if [enable images]; otherwise, <c>false</c>.</value> + public bool? EnableImages { get; set; } + /// <summary> + /// Gets or sets the image type limit. + /// </summary> + /// <value>The image type limit.</value> + public int? ImageTypeLimit { get; set; } + /// <summary> + /// Gets or sets the enable image types. + /// </summary> + /// <value>The enable image types.</value> + public ImageType[] EnableImageTypes { get; set; } + + public bool EnableTotalRecordCount { get; set; } + + public NextUpQuery() + { + EnableImageTypes = new ImageType[] {}; + EnableTotalRecordCount = true; + } + } +} diff --git a/MediaBrowser.Model/Querying/QueryFilters.cs b/MediaBrowser.Model/Querying/QueryFilters.cs new file mode 100644 index 000000000..992bba303 --- /dev/null +++ b/MediaBrowser.Model/Querying/QueryFilters.cs @@ -0,0 +1,32 @@ +using MediaBrowser.Model.Dto; +using System; + +namespace MediaBrowser.Model.Querying +{ + public class QueryFiltersLegacy + { + public string[] Genres { get; set; } + public string[] Tags { get; set; } + public string[] OfficialRatings { get; set; } + public int[] Years { get; set; } + + public QueryFiltersLegacy() + { + Genres = new string[] {}; + Tags = new string[] {}; + OfficialRatings = new string[] {}; + Years = new int[] { }; + } + } + public class QueryFilters + { + public NameGuidPair[] Genres { get; set; } + public string[] Tags { get; set; } + + public QueryFilters() + { + Tags = new string[] {}; + Genres = new NameGuidPair[] { }; + } + } +} diff --git a/MediaBrowser.Model/Querying/QueryResult.cs b/MediaBrowser.Model/Querying/QueryResult.cs new file mode 100644 index 000000000..6f9923d08 --- /dev/null +++ b/MediaBrowser.Model/Querying/QueryResult.cs @@ -0,0 +1,23 @@ + +namespace MediaBrowser.Model.Querying +{ + public class QueryResult<T> + { + /// <summary> + /// Gets or sets the items. + /// </summary> + /// <value>The items.</value> + public T[] Items { get; set; } + + /// <summary> + /// The total number of records available + /// </summary> + /// <value>The total record count.</value> + public int TotalRecordCount { get; set; } + + public QueryResult() + { + Items = new T[] { }; + } + } +} diff --git a/MediaBrowser.Model/Querying/SessionQuery.cs b/MediaBrowser.Model/Querying/SessionQuery.cs new file mode 100644 index 000000000..fa7df315c --- /dev/null +++ b/MediaBrowser.Model/Querying/SessionQuery.cs @@ -0,0 +1,14 @@ + +namespace MediaBrowser.Model.Querying +{ + /// <summary> + /// Class SessionQuery + /// </summary> + public class SessionQuery + { + /// <summary> + /// Filter by sessions that are allowed to be controlled by a given user + /// </summary> + public string ControllableByUserId { get; set; } + } +} diff --git a/MediaBrowser.Model/Querying/SimilarItemsQuery.cs b/MediaBrowser.Model/Querying/SimilarItemsQuery.cs new file mode 100644 index 000000000..0dd491550 --- /dev/null +++ b/MediaBrowser.Model/Querying/SimilarItemsQuery.cs @@ -0,0 +1,29 @@ +namespace MediaBrowser.Model.Querying +{ + public class SimilarItemsQuery + { + /// <summary> + /// The user to localize search results for + /// </summary> + /// <value>The user id.</value> + public string UserId { get; set; } + + /// <summary> + /// Gets or sets the id. + /// </summary> + /// <value>The id.</value> + public string Id { get; set; } + + /// <summary> + /// The maximum number of items to return + /// </summary> + /// <value>The limit.</value> + public int? Limit { get; set; } + + /// <summary> + /// Fields to return within the items, in addition to basic information + /// </summary> + /// <value>The fields.</value> + public ItemFields[] Fields { get; set; } + } +} diff --git a/MediaBrowser.Model/Querying/ThemeMediaResult.cs b/MediaBrowser.Model/Querying/ThemeMediaResult.cs new file mode 100644 index 000000000..eae102bae --- /dev/null +++ b/MediaBrowser.Model/Querying/ThemeMediaResult.cs @@ -0,0 +1,17 @@ +using MediaBrowser.Model.Dto; +using System; + +namespace MediaBrowser.Model.Querying +{ + /// <summary> + /// Class ThemeMediaResult + /// </summary> + public class ThemeMediaResult : QueryResult<BaseItemDto> + { + /// <summary> + /// Gets or sets the owner id. + /// </summary> + /// <value>The owner id.</value> + public Guid OwnerId { get; set; } + } +} diff --git a/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs b/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs new file mode 100644 index 000000000..665b980eb --- /dev/null +++ b/MediaBrowser.Model/Querying/UpcomingEpisodesQuery.cs @@ -0,0 +1,57 @@ +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Model.Querying +{ + public class UpcomingEpisodesQuery + { + /// <summary> + /// Gets or sets the user id. + /// </summary> + /// <value>The user id.</value> + public string UserId { get; set; } + + /// <summary> + /// Gets or sets the parent identifier. + /// </summary> + /// <value>The parent identifier.</value> + public string ParentId { get; set; } + + /// <summary> + /// Skips over a given number of items within the results. Use for paging. + /// </summary> + /// <value>The start index.</value> + public int? StartIndex { get; set; } + + /// <summary> + /// The maximum number of items to return + /// </summary> + /// <value>The limit.</value> + public int? Limit { get; set; } + + /// <summary> + /// Fields to return within the items, in addition to basic information + /// </summary> + /// <value>The fields.</value> + public ItemFields[] Fields { get; set; } + /// <summary> + /// Gets or sets a value indicating whether [enable images]. + /// </summary> + /// <value><c>null</c> if [enable images] contains no value, <c>true</c> if [enable images]; otherwise, <c>false</c>.</value> + public bool? EnableImages { get; set; } + /// <summary> + /// Gets or sets the image type limit. + /// </summary> + /// <value>The image type limit.</value> + public int? ImageTypeLimit { get; set; } + /// <summary> + /// Gets or sets the enable image types. + /// </summary> + /// <value>The enable image types.</value> + public ImageType[] EnableImageTypes { get; set; } + + public UpcomingEpisodesQuery() + { + EnableImageTypes = new ImageType[] {}; + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Querying/UserQuery.cs b/MediaBrowser.Model/Querying/UserQuery.cs new file mode 100644 index 000000000..48dbd30aa --- /dev/null +++ b/MediaBrowser.Model/Querying/UserQuery.cs @@ -0,0 +1,9 @@ + +namespace MediaBrowser.Model.Querying +{ + public class UserQuery + { + public bool? IsHidden { get; set; } + public bool? IsDisabled { get; set; } + } +} diff --git a/MediaBrowser.Model/Reflection/IAssemblyInfo.cs b/MediaBrowser.Model/Reflection/IAssemblyInfo.cs new file mode 100644 index 000000000..e8e9c414c --- /dev/null +++ b/MediaBrowser.Model/Reflection/IAssemblyInfo.cs @@ -0,0 +1,14 @@ +using System; +using System.IO; +using System.Reflection; + +namespace MediaBrowser.Model.Reflection +{ + public interface IAssemblyInfo + { + Stream GetManifestResourceStream(Type type, string resource); + string[] GetManifestResourceNames(Type type); + + Assembly[] GetCurrentAssemblies(); + } +} diff --git a/MediaBrowser.Model/Search/SearchHint.cs b/MediaBrowser.Model/Search/SearchHint.cs new file mode 100644 index 000000000..daa3566cf --- /dev/null +++ b/MediaBrowser.Model/Search/SearchHint.cs @@ -0,0 +1,158 @@ +using System; + +namespace MediaBrowser.Model.Search +{ + /// <summary> + /// Class SearchHintResult + /// </summary> + public class SearchHint + { + /// <summary> + /// Gets or sets the item id. + /// </summary> + /// <value>The item id.</value> + public Guid ItemId { get; set; } + + public Guid Id { get; set; } + + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the matched term. + /// </summary> + /// <value>The matched term.</value> + public string MatchedTerm { get; set; } + + /// <summary> + /// Gets or sets the index number. + /// </summary> + /// <value>The index number.</value> + public int? IndexNumber { get; set; } + + /// <summary> + /// Gets or sets the production year. + /// </summary> + /// <value>The production year.</value> + public int? ProductionYear { get; set; } + + /// <summary> + /// Gets or sets the parent index number. + /// </summary> + /// <value>The parent index number.</value> + public int? ParentIndexNumber { get; set; } + + /// <summary> + /// Gets or sets the image tag. + /// </summary> + /// <value>The image tag.</value> + public string PrimaryImageTag { get; set; } + + /// <summary> + /// Gets or sets the thumb image tag. + /// </summary> + /// <value>The thumb image tag.</value> + public string ThumbImageTag { get; set; } + + /// <summary> + /// Gets or sets the thumb image item identifier. + /// </summary> + /// <value>The thumb image item identifier.</value> + public string ThumbImageItemId { get; set; } + + /// <summary> + /// Gets or sets the backdrop image tag. + /// </summary> + /// <value>The backdrop image tag.</value> + public string BackdropImageTag { get; set; } + + /// <summary> + /// Gets or sets the backdrop image item identifier. + /// </summary> + /// <value>The backdrop image item identifier.</value> + public string BackdropImageItemId { get; set; } + + /// <summary> + /// Gets or sets the type. + /// </summary> + /// <value>The type.</value> + public string Type { get; set; } + + public bool? IsFolder { get; set; } + + /// <summary> + /// Gets or sets the run time ticks. + /// </summary> + /// <value>The run time ticks.</value> + public long? RunTimeTicks { get; set; } + + /// <summary> + /// Gets or sets the type of the media. + /// </summary> + /// <value>The type of the media.</value> + public string MediaType { get; set; } + + public DateTime? StartDate { get; set; } + public DateTime? EndDate { get; set; } + + /// <summary> + /// Gets or sets the series. + /// </summary> + /// <value>The series.</value> + public string Series { get; set; } + + public string Status { get; set; } + + /// <summary> + /// Gets or sets the album. + /// </summary> + /// <value>The album.</value> + public string Album { get; set; } + public Guid AlbumId { get; set; } + + /// <summary> + /// Gets or sets the album artist. + /// </summary> + /// <value>The album artist.</value> + public string AlbumArtist { get; set; } + + /// <summary> + /// Gets or sets the artists. + /// </summary> + /// <value>The artists.</value> + public string[] Artists { get; set; } + + /// <summary> + /// Gets or sets the song count. + /// </summary> + /// <value>The song count.</value> + public int? SongCount { get; set; } + + /// <summary> + /// Gets or sets the episode count. + /// </summary> + /// <value>The episode count.</value> + public int? EpisodeCount { get; set; } + + /// <summary> + /// Gets or sets the channel identifier. + /// </summary> + /// <value>The channel identifier.</value> + public Guid ChannelId { get; set; } + + /// <summary> + /// Gets or sets the name of the channel. + /// </summary> + /// <value>The name of the channel.</value> + public string ChannelName { get; set; } + + /// <summary> + /// Gets or sets the primary image aspect ratio. + /// </summary> + /// <value>The primary image aspect ratio.</value> + public double? PrimaryImageAspectRatio { get; set; } + } +} diff --git a/MediaBrowser.Model/Search/SearchHintResult.cs b/MediaBrowser.Model/Search/SearchHintResult.cs new file mode 100644 index 000000000..372528f82 --- /dev/null +++ b/MediaBrowser.Model/Search/SearchHintResult.cs @@ -0,0 +1,21 @@ + +namespace MediaBrowser.Model.Search +{ + /// <summary> + /// Class SearchHintResult + /// </summary> + public class SearchHintResult + { + /// <summary> + /// Gets or sets the search hints. + /// </summary> + /// <value>The search hints.</value> + public SearchHint[] SearchHints { get; set; } + + /// <summary> + /// Gets or sets the total record count. + /// </summary> + /// <value>The total record count.</value> + public int TotalRecordCount { get; set; } + } +} diff --git a/MediaBrowser.Model/Search/SearchQuery.cs b/MediaBrowser.Model/Search/SearchQuery.cs new file mode 100644 index 000000000..6a1861c8e --- /dev/null +++ b/MediaBrowser.Model/Search/SearchQuery.cs @@ -0,0 +1,65 @@ +using System; + +namespace MediaBrowser.Model.Search +{ + public class SearchQuery + { + /// <summary> + /// The user to localize search results for + /// </summary> + /// <value>The user id.</value> + public Guid UserId { get; set; } + + /// <summary> + /// Gets or sets the search term. + /// </summary> + /// <value>The search term.</value> + public string SearchTerm { get; set; } + + /// <summary> + /// Skips over a given number of items within the results. Use for paging. + /// </summary> + /// <value>The start index.</value> + public int? StartIndex { get; set; } + + /// <summary> + /// The maximum number of items to return + /// </summary> + /// <value>The limit.</value> + public int? Limit { get; set; } + + public bool IncludePeople { get; set; } + public bool IncludeMedia { get; set; } + public bool IncludeGenres { get; set; } + public bool IncludeStudios { get; set; } + public bool IncludeArtists { get; set; } + + public string[] MediaTypes { get; set; } + public string[] IncludeItemTypes { get; set; } + public string[] ExcludeItemTypes { get; set; } + public string ParentId { get; set; } + + public bool? IsMovie { get; set; } + + public bool? IsSeries { get; set; } + + public bool? IsNews { get; set; } + + public bool? IsKids { get; set; } + + public bool? IsSports { get; set; } + + public SearchQuery() + { + IncludeArtists = true; + IncludeGenres = true; + IncludeMedia = true; + IncludePeople = true; + IncludeStudios = true; + + MediaTypes = new string[] {}; + IncludeItemTypes = new string[] {}; + ExcludeItemTypes = new string[] {}; + } + } +} diff --git a/MediaBrowser.Model/Serialization/IJsonSerializer.cs b/MediaBrowser.Model/Serialization/IJsonSerializer.cs new file mode 100644 index 000000000..a582beb7f --- /dev/null +++ b/MediaBrowser.Model/Serialization/IJsonSerializer.cs @@ -0,0 +1,91 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +namespace MediaBrowser.Model.Serialization +{ + public interface IJsonSerializer + { + /// <summary> + /// Serializes to stream. + /// </summary> + /// <param name="obj">The obj.</param> + /// <param name="stream">The stream.</param> + /// <exception cref="System.ArgumentNullException">obj</exception> + void SerializeToStream(object obj, Stream stream); + + /// <summary> + /// Serializes to file. + /// </summary> + /// <param name="obj">The obj.</param> + /// <param name="file">The file.</param> + /// <exception cref="System.ArgumentNullException">obj</exception> + void SerializeToFile(object obj, string file); + + /// <summary> + /// Deserializes from file. + /// </summary> + /// <param name="type">The type.</param> + /// <param name="file">The file.</param> + /// <returns>System.Object.</returns> + /// <exception cref="System.ArgumentNullException">type</exception> + object DeserializeFromFile(Type type, string file); + + /// <summary> + /// Deserializes from file. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="file">The file.</param> + /// <returns>``0.</returns> + /// <exception cref="System.ArgumentNullException">file</exception> + T DeserializeFromFile<T>(string file) + where T : class; + + /// <summary> + /// Deserializes from stream. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="stream">The stream.</param> + /// <returns>``0.</returns> + /// <exception cref="System.ArgumentNullException">stream</exception> + T DeserializeFromStream<T>(Stream stream); + + /// <summary> + /// Deserializes from string. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="text">The text.</param> + /// <returns>``0.</returns> + /// <exception cref="System.ArgumentNullException">text</exception> + T DeserializeFromString<T>(string text); + + /// <summary> + /// Deserializes from stream. + /// </summary> + /// <param name="stream">The stream.</param> + /// <param name="type">The type.</param> + /// <returns>System.Object.</returns> + /// <exception cref="System.ArgumentNullException">stream</exception> + object DeserializeFromStream(Stream stream, Type type); + + /// <summary> + /// Deserializes from string. + /// </summary> + /// <param name="json">The json.</param> + /// <param name="type">The type.</param> + /// <returns>System.Object.</returns> + /// <exception cref="System.ArgumentNullException">json</exception> + object DeserializeFromString(string json, Type type); + + /// <summary> + /// Serializes to string. + /// </summary> + /// <param name="obj">The obj.</param> + /// <returns>System.String.</returns> + /// <exception cref="System.ArgumentNullException">obj</exception> + string SerializeToString(object obj); + + Task<object> DeserializeFromStreamAsync(Stream stream, Type type); + Task<T> DeserializeFromStreamAsync<T>(Stream stream); + } +} diff --git a/MediaBrowser.Model/Serialization/IXmlSerializer.cs b/MediaBrowser.Model/Serialization/IXmlSerializer.cs new file mode 100644 index 000000000..b26b673f3 --- /dev/null +++ b/MediaBrowser.Model/Serialization/IXmlSerializer.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; + +namespace MediaBrowser.Model.Serialization +{ + public interface IXmlSerializer + { + /// <summary> + /// Deserializes from stream. + /// </summary> + /// <param name="type">The type.</param> + /// <param name="stream">The stream.</param> + /// <returns>System.Object.</returns> + object DeserializeFromStream(Type type, Stream stream); + + /// <summary> + /// Serializes to stream. + /// </summary> + /// <param name="obj">The obj.</param> + /// <param name="stream">The stream.</param> + void SerializeToStream(object obj, Stream stream); + + /// <summary> + /// Serializes to file. + /// </summary> + /// <param name="obj">The obj.</param> + /// <param name="file">The file.</param> + void SerializeToFile(object obj, string file); + + /// <summary> + /// Deserializes from file. + /// </summary> + /// <param name="type">The type.</param> + /// <param name="file">The file.</param> + /// <returns>System.Object.</returns> + object DeserializeFromFile(Type type, string file); + + /// <summary> + /// Deserializes from bytes. + /// </summary> + /// <param name="type">The type.</param> + /// <param name="buffer">The buffer.</param> + /// <returns>System.Object.</returns> + object DeserializeFromBytes(Type type, byte[] buffer); + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Serialization/IgnoreDataMemberAttribute.cs b/MediaBrowser.Model/Serialization/IgnoreDataMemberAttribute.cs new file mode 100644 index 000000000..8e23edc24 --- /dev/null +++ b/MediaBrowser.Model/Serialization/IgnoreDataMemberAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace MediaBrowser.Model.Serialization +{ + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = false)] + public sealed class IgnoreDataMemberAttribute : Attribute + { + public IgnoreDataMemberAttribute() + { + } + } +} diff --git a/MediaBrowser.Model/Services/ApiMemberAttribute.cs b/MediaBrowser.Model/Services/ApiMemberAttribute.cs new file mode 100644 index 000000000..4a2831775 --- /dev/null +++ b/MediaBrowser.Model/Services/ApiMemberAttribute.cs @@ -0,0 +1,61 @@ +using System; + +namespace MediaBrowser.Model.Services +{ + [AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)] + public class ApiMemberAttribute : Attribute + { + /// <summary> + /// Gets or sets verb to which applies attribute. By default applies to all verbs. + /// </summary> + public string Verb { get; set; } + + /// <summary> + /// Gets or sets parameter type: It can be only one of the following: path, query, body, form, or header. + /// </summary> + public string ParameterType { get; set; } + + /// <summary> + /// Gets or sets unique name for the parameter. Each name must be unique, even if they are associated with different paramType values. + /// </summary> + /// <remarks> + /// <para> + /// Other notes on the name field: + /// If paramType is body, the name is used only for UI and codegeneration. + /// If paramType is path, the name field must correspond to the associated path segment from the path field in the api object. + /// If paramType is query, the name field corresponds to the query param name. + /// </para> + /// </remarks> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the human-readable description for the parameter. + /// </summary> + public string Description { get; set; } + + /// <summary> + /// For path, query, and header paramTypes, this field must be a primitive. For body, this can be a complex or container datatype. + /// </summary> + public string DataType { get; set; } + + /// <summary> + /// For path, this is always true. Otherwise, this field tells the client whether or not the field must be supplied. + /// </summary> + public bool IsRequired { get; set; } + + /// <summary> + /// For query params, this specifies that a comma-separated list of values can be passed to the API. For path and body types, this field cannot be true. + /// </summary> + public bool AllowMultiple { get; set; } + + /// <summary> + /// Gets or sets route to which applies attribute, matches using StartsWith. By default applies to all routes. + /// </summary> + public string Route { get; set; } + + /// <summary> + /// Whether to exclude this property from being included in the ModelSchema + /// </summary> + public bool ExcludeInSchema { get; set; } + } +} diff --git a/MediaBrowser.Model/Services/HttpUtility.cs b/MediaBrowser.Model/Services/HttpUtility.cs new file mode 100644 index 000000000..5cc0cc37d --- /dev/null +++ b/MediaBrowser.Model/Services/HttpUtility.cs @@ -0,0 +1,923 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using MediaBrowser.Model.Services; +using MediaBrowser.Model.Extensions; + +namespace MediaBrowser.Model.Services +{ + public static class MyHttpUtility + { + // Must be sorted + static readonly long[] entities = new long[] { + (long)'A' << 56 | (long)'E' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, + (long)'A' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'A' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'A' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'A' << 56 | (long)'l' << 48 | (long)'p' << 40 | (long)'h' << 32 | (long)'a' << 24, + (long)'A' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'g' << 24, + (long)'A' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, + (long)'A' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'B' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32, + (long)'C' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'d' << 32 | (long)'i' << 24 | (long)'l' << 16, + (long)'C' << 56 | (long)'h' << 48 | (long)'i' << 40, + (long)'D' << 56 | (long)'a' << 48 | (long)'g' << 40 | (long)'g' << 32 | (long)'e' << 24 | (long)'r' << 16, + (long)'D' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'t' << 32 | (long)'a' << 24, + (long)'E' << 56 | (long)'T' << 48 | (long)'H' << 40, + (long)'E' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'E' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'E' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'E' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8, + (long)'E' << 56 | (long)'t' << 48 | (long)'a' << 40, + (long)'E' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'G' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'m' << 32 | (long)'a' << 24, + (long)'I' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'I' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'I' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'I' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'a' << 32, + (long)'I' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'K' << 56 | (long)'a' << 48 | (long)'p' << 40 | (long)'p' << 32 | (long)'a' << 24, + (long)'L' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'b' << 32 | (long)'d' << 24 | (long)'a' << 16, + (long)'M' << 56 | (long)'u' << 48, + (long)'N' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, + (long)'N' << 56 | (long)'u' << 48, + (long)'O' << 56 | (long)'E' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, + (long)'O' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'O' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'O' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'O' << 56 | (long)'m' << 48 | (long)'e' << 40 | (long)'g' << 32 | (long)'a' << 24, + (long)'O' << 56 | (long)'m' << 48 | (long)'i' << 40 | (long)'c' << 32 | (long)'r' << 24 | (long)'o' << 16 | (long)'n' << 8, + (long)'O' << 56 | (long)'s' << 48 | (long)'l' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'h' << 16, + (long)'O' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, + (long)'O' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'P' << 56 | (long)'h' << 48 | (long)'i' << 40, + (long)'P' << 56 | (long)'i' << 48, + (long)'P' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24, + (long)'P' << 56 | (long)'s' << 48 | (long)'i' << 40, + (long)'R' << 56 | (long)'h' << 48 | (long)'o' << 40, + (long)'S' << 56 | (long)'c' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'o' << 24 | (long)'n' << 16, + (long)'S' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24, + (long)'T' << 56 | (long)'H' << 48 | (long)'O' << 40 | (long)'R' << 32 | (long)'N' << 24, + (long)'T' << 56 | (long)'a' << 48 | (long)'u' << 40, + (long)'T' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24, + (long)'U' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'U' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'U' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'U' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8, + (long)'U' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'X' << 56 | (long)'i' << 48, + (long)'Y' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'Y' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'Z' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32, + (long)'a' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'a' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'a' << 56 | (long)'c' << 48 | (long)'u' << 40 | (long)'t' << 32 | (long)'e' << 24, + (long)'a' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, + (long)'a' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'a' << 56 | (long)'l' << 48 | (long)'e' << 40 | (long)'f' << 32 | (long)'s' << 24 | (long)'y' << 16 | (long)'m' << 8, + (long)'a' << 56 | (long)'l' << 48 | (long)'p' << 40 | (long)'h' << 32 | (long)'a' << 24, + (long)'a' << 56 | (long)'m' << 48 | (long)'p' << 40, + (long)'a' << 56 | (long)'n' << 48 | (long)'d' << 40, + (long)'a' << 56 | (long)'n' << 48 | (long)'g' << 40, + (long)'a' << 56 | (long)'p' << 48 | (long)'o' << 40 | (long)'s' << 32, + (long)'a' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'g' << 24, + (long)'a' << 56 | (long)'s' << 48 | (long)'y' << 40 | (long)'m' << 32 | (long)'p' << 24, + (long)'a' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, + (long)'a' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'b' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, + (long)'b' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32, + (long)'b' << 56 | (long)'r' << 48 | (long)'v' << 40 | (long)'b' << 32 | (long)'a' << 24 | (long)'r' << 16, + (long)'b' << 56 | (long)'u' << 48 | (long)'l' << 40 | (long)'l' << 32, + (long)'c' << 56 | (long)'a' << 48 | (long)'p' << 40, + (long)'c' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'d' << 32 | (long)'i' << 24 | (long)'l' << 16, + (long)'c' << 56 | (long)'e' << 48 | (long)'d' << 40 | (long)'i' << 32 | (long)'l' << 24, + (long)'c' << 56 | (long)'e' << 48 | (long)'n' << 40 | (long)'t' << 32, + (long)'c' << 56 | (long)'h' << 48 | (long)'i' << 40, + (long)'c' << 56 | (long)'i' << 48 | (long)'r' << 40 | (long)'c' << 32, + (long)'c' << 56 | (long)'l' << 48 | (long)'u' << 40 | (long)'b' << 32 | (long)'s' << 24, + (long)'c' << 56 | (long)'o' << 48 | (long)'n' << 40 | (long)'g' << 32, + (long)'c' << 56 | (long)'o' << 48 | (long)'p' << 40 | (long)'y' << 32, + (long)'c' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'r' << 24, + (long)'c' << 56 | (long)'u' << 48 | (long)'p' << 40, + (long)'c' << 56 | (long)'u' << 48 | (long)'r' << 40 | (long)'r' << 32 | (long)'e' << 24 | (long)'n' << 16, + (long)'d' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'d' << 56 | (long)'a' << 48 | (long)'g' << 40 | (long)'g' << 32 | (long)'e' << 24 | (long)'r' << 16, + (long)'d' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'d' << 56 | (long)'e' << 48 | (long)'g' << 40, + (long)'d' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'t' << 32 | (long)'a' << 24, + (long)'d' << 56 | (long)'i' << 48 | (long)'a' << 40 | (long)'m' << 32 | (long)'s' << 24, + (long)'d' << 56 | (long)'i' << 48 | (long)'v' << 40 | (long)'i' << 32 | (long)'d' << 24 | (long)'e' << 16, + (long)'e' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'e' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'e' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'e' << 56 | (long)'m' << 48 | (long)'p' << 40 | (long)'t' << 32 | (long)'y' << 24, + (long)'e' << 56 | (long)'m' << 48 | (long)'s' << 40 | (long)'p' << 32, + (long)'e' << 56 | (long)'n' << 48 | (long)'s' << 40 | (long)'p' << 32, + (long)'e' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8, + (long)'e' << 56 | (long)'q' << 48 | (long)'u' << 40 | (long)'i' << 32 | (long)'v' << 24, + (long)'e' << 56 | (long)'t' << 48 | (long)'a' << 40, + (long)'e' << 56 | (long)'t' << 48 | (long)'h' << 40, + (long)'e' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'e' << 56 | (long)'u' << 48 | (long)'r' << 40 | (long)'o' << 32, + (long)'e' << 56 | (long)'x' << 48 | (long)'i' << 40 | (long)'s' << 32 | (long)'t' << 24, + (long)'f' << 56 | (long)'n' << 48 | (long)'o' << 40 | (long)'f' << 32, + (long)'f' << 56 | (long)'o' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'l' << 24 | (long)'l' << 16, + (long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'1' << 24 | (long)'2' << 16, + (long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'1' << 24 | (long)'4' << 16, + (long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'3' << 24 | (long)'4' << 16, + (long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'l' << 24, + (long)'g' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'m' << 32 | (long)'a' << 24, + (long)'g' << 56 | (long)'e' << 48, + (long)'g' << 56 | (long)'t' << 48, + (long)'h' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'h' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'h' << 56 | (long)'e' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'t' << 24 | (long)'s' << 16, + (long)'h' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'l' << 32 | (long)'i' << 24 | (long)'p' << 16, + (long)'i' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'i' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'i' << 56 | (long)'e' << 48 | (long)'x' << 40 | (long)'c' << 32 | (long)'l' << 24, + (long)'i' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'i' << 56 | (long)'m' << 48 | (long)'a' << 40 | (long)'g' << 32 | (long)'e' << 24, + (long)'i' << 56 | (long)'n' << 48 | (long)'f' << 40 | (long)'i' << 32 | (long)'n' << 24, + (long)'i' << 56 | (long)'n' << 48 | (long)'t' << 40, + (long)'i' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'a' << 32, + (long)'i' << 56 | (long)'q' << 48 | (long)'u' << 40 | (long)'e' << 32 | (long)'s' << 24 | (long)'t' << 16, + (long)'i' << 56 | (long)'s' << 48 | (long)'i' << 40 | (long)'n' << 32, + (long)'i' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'k' << 56 | (long)'a' << 48 | (long)'p' << 40 | (long)'p' << 32 | (long)'a' << 24, + (long)'l' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'l' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'b' << 32 | (long)'d' << 24 | (long)'a' << 16, + (long)'l' << 56 | (long)'a' << 48 | (long)'n' << 40 | (long)'g' << 32, + (long)'l' << 56 | (long)'a' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, + (long)'l' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'l' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'i' << 32 | (long)'l' << 24, + (long)'l' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, + (long)'l' << 56 | (long)'e' << 48, + (long)'l' << 56 | (long)'f' << 48 | (long)'l' << 40 | (long)'o' << 32 | (long)'o' << 24 | (long)'r' << 16, + (long)'l' << 56 | (long)'o' << 48 | (long)'w' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'t' << 16, + (long)'l' << 56 | (long)'o' << 48 | (long)'z' << 40, + (long)'l' << 56 | (long)'r' << 48 | (long)'m' << 40, + (long)'l' << 56 | (long)'s' << 48 | (long)'a' << 40 | (long)'q' << 32 | (long)'u' << 24 | (long)'o' << 16, + (long)'l' << 56 | (long)'s' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, + (long)'l' << 56 | (long)'t' << 48, + (long)'m' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'r' << 32, + (long)'m' << 56 | (long)'d' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'h' << 24, + (long)'m' << 56 | (long)'i' << 48 | (long)'c' << 40 | (long)'r' << 32 | (long)'o' << 24, + (long)'m' << 56 | (long)'i' << 48 | (long)'d' << 40 | (long)'d' << 32 | (long)'o' << 24 | (long)'t' << 16, + (long)'m' << 56 | (long)'i' << 48 | (long)'n' << 40 | (long)'u' << 32 | (long)'s' << 24, + (long)'m' << 56 | (long)'u' << 48, + (long)'n' << 56 | (long)'a' << 48 | (long)'b' << 40 | (long)'l' << 32 | (long)'a' << 24, + (long)'n' << 56 | (long)'b' << 48 | (long)'s' << 40 | (long)'p' << 32, + (long)'n' << 56 | (long)'d' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'h' << 24, + (long)'n' << 56 | (long)'e' << 48, + (long)'n' << 56 | (long)'i' << 48, + (long)'n' << 56 | (long)'o' << 48 | (long)'t' << 40, + (long)'n' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'i' << 32 | (long)'n' << 24, + (long)'n' << 56 | (long)'s' << 48 | (long)'u' << 40 | (long)'b' << 32, + (long)'n' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, + (long)'n' << 56 | (long)'u' << 48, + (long)'o' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'o' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'o' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, + (long)'o' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'o' << 56 | (long)'l' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'e' << 24, + (long)'o' << 56 | (long)'m' << 48 | (long)'e' << 40 | (long)'g' << 32 | (long)'a' << 24, + (long)'o' << 56 | (long)'m' << 48 | (long)'i' << 40 | (long)'c' << 32 | (long)'r' << 24 | (long)'o' << 16 | (long)'n' << 8, + (long)'o' << 56 | (long)'p' << 48 | (long)'l' << 40 | (long)'u' << 32 | (long)'s' << 24, + (long)'o' << 56 | (long)'r' << 48, + (long)'o' << 56 | (long)'r' << 48 | (long)'d' << 40 | (long)'f' << 32, + (long)'o' << 56 | (long)'r' << 48 | (long)'d' << 40 | (long)'m' << 32, + (long)'o' << 56 | (long)'s' << 48 | (long)'l' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'h' << 16, + (long)'o' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, + (long)'o' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24 | (long)'s' << 16, + (long)'o' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'p' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'a' << 32, + (long)'p' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'t' << 32, + (long)'p' << 56 | (long)'e' << 48 | (long)'r' << 40 | (long)'m' << 32 | (long)'i' << 24 | (long)'l' << 16, + (long)'p' << 56 | (long)'e' << 48 | (long)'r' << 40 | (long)'p' << 32, + (long)'p' << 56 | (long)'h' << 48 | (long)'i' << 40, + (long)'p' << 56 | (long)'i' << 48, + (long)'p' << 56 | (long)'i' << 48 | (long)'v' << 40, + (long)'p' << 56 | (long)'l' << 48 | (long)'u' << 40 | (long)'s' << 32 | (long)'m' << 24 | (long)'n' << 16, + (long)'p' << 56 | (long)'o' << 48 | (long)'u' << 40 | (long)'n' << 32 | (long)'d' << 24, + (long)'p' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24, + (long)'p' << 56 | (long)'r' << 48 | (long)'o' << 40 | (long)'d' << 32, + (long)'p' << 56 | (long)'r' << 48 | (long)'o' << 40 | (long)'p' << 32, + (long)'p' << 56 | (long)'s' << 48 | (long)'i' << 40, + (long)'q' << 56 | (long)'u' << 48 | (long)'o' << 40 | (long)'t' << 32, + (long)'r' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'r' << 56 | (long)'a' << 48 | (long)'d' << 40 | (long)'i' << 32 | (long)'c' << 24, + (long)'r' << 56 | (long)'a' << 48 | (long)'n' << 40 | (long)'g' << 32, + (long)'r' << 56 | (long)'a' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, + (long)'r' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'r' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'i' << 32 | (long)'l' << 24, + (long)'r' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, + (long)'r' << 56 | (long)'e' << 48 | (long)'a' << 40 | (long)'l' << 32, + (long)'r' << 56 | (long)'e' << 48 | (long)'g' << 40, + (long)'r' << 56 | (long)'f' << 48 | (long)'l' << 40 | (long)'o' << 32 | (long)'o' << 24 | (long)'r' << 16, + (long)'r' << 56 | (long)'h' << 48 | (long)'o' << 40, + (long)'r' << 56 | (long)'l' << 48 | (long)'m' << 40, + (long)'r' << 56 | (long)'s' << 48 | (long)'a' << 40 | (long)'q' << 32 | (long)'u' << 24 | (long)'o' << 16, + (long)'r' << 56 | (long)'s' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, + (long)'s' << 56 | (long)'b' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, + (long)'s' << 56 | (long)'c' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'o' << 24 | (long)'n' << 16, + (long)'s' << 56 | (long)'d' << 48 | (long)'o' << 40 | (long)'t' << 32, + (long)'s' << 56 | (long)'e' << 48 | (long)'c' << 40 | (long)'t' << 32, + (long)'s' << 56 | (long)'h' << 48 | (long)'y' << 40, + (long)'s' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24, + (long)'s' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24 | (long)'f' << 16, + (long)'s' << 56 | (long)'i' << 48 | (long)'m' << 40, + (long)'s' << 56 | (long)'p' << 48 | (long)'a' << 40 | (long)'d' << 32 | (long)'e' << 24 | (long)'s' << 16, + (long)'s' << 56 | (long)'u' << 48 | (long)'b' << 40, + (long)'s' << 56 | (long)'u' << 48 | (long)'b' << 40 | (long)'e' << 32, + (long)'s' << 56 | (long)'u' << 48 | (long)'m' << 40, + (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40, + (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'1' << 32, + (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'2' << 32, + (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'3' << 32, + (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'e' << 32, + (long)'s' << 56 | (long)'z' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, + (long)'t' << 56 | (long)'a' << 48 | (long)'u' << 40, + (long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'r' << 32 | (long)'e' << 24 | (long)'4' << 16, + (long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24, + (long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24 | (long)'s' << 16 | (long)'y' << 8 | (long)'m' << 0, + (long)'t' << 56 | (long)'h' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'s' << 24 | (long)'p' << 16, + (long)'t' << 56 | (long)'h' << 48 | (long)'o' << 40 | (long)'r' << 32 | (long)'n' << 24, + (long)'t' << 56 | (long)'i' << 48 | (long)'l' << 40 | (long)'d' << 32 | (long)'e' << 24, + (long)'t' << 56 | (long)'i' << 48 | (long)'m' << 40 | (long)'e' << 32 | (long)'s' << 24, + (long)'t' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'d' << 32 | (long)'e' << 24, + (long)'u' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'u' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'u' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'u' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'u' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'u' << 56 | (long)'m' << 48 | (long)'l' << 40, + (long)'u' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'h' << 24, + (long)'u' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8, + (long)'u' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'w' << 56 | (long)'e' << 48 | (long)'i' << 40 | (long)'e' << 32 | (long)'r' << 24 | (long)'p' << 16, + (long)'x' << 56 | (long)'i' << 48, + (long)'y' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'y' << 56 | (long)'e' << 48 | (long)'n' << 40, + (long)'y' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'z' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32, + (long)'z' << 56 | (long)'w' << 48 | (long)'j' << 40, + (long)'z' << 56 | (long)'w' << 48 | (long)'n' << 40 | (long)'j' << 32 + }; + + static readonly char[] entities_values = new char[] { + '\u00C6', + '\u00C1', + '\u00C2', + '\u00C0', + '\u0391', + '\u00C5', + '\u00C3', + '\u00C4', + '\u0392', + '\u00C7', + '\u03A7', + '\u2021', + '\u0394', + '\u00D0', + '\u00C9', + '\u00CA', + '\u00C8', + '\u0395', + '\u0397', + '\u00CB', + '\u0393', + '\u00CD', + '\u00CE', + '\u00CC', + '\u0399', + '\u00CF', + '\u039A', + '\u039B', + '\u039C', + '\u00D1', + '\u039D', + '\u0152', + '\u00D3', + '\u00D4', + '\u00D2', + '\u03A9', + '\u039F', + '\u00D8', + '\u00D5', + '\u00D6', + '\u03A6', + '\u03A0', + '\u2033', + '\u03A8', + '\u03A1', + '\u0160', + '\u03A3', + '\u00DE', + '\u03A4', + '\u0398', + '\u00DA', + '\u00DB', + '\u00D9', + '\u03A5', + '\u00DC', + '\u039E', + '\u00DD', + '\u0178', + '\u0396', + '\u00E1', + '\u00E2', + '\u00B4', + '\u00E6', + '\u00E0', + '\u2135', + '\u03B1', + '\u0026', + '\u2227', + '\u2220', + '\u0027', + '\u00E5', + '\u2248', + '\u00E3', + '\u00E4', + '\u201E', + '\u03B2', + '\u00A6', + '\u2022', + '\u2229', + '\u00E7', + '\u00B8', + '\u00A2', + '\u03C7', + '\u02C6', + '\u2663', + '\u2245', + '\u00A9', + '\u21B5', + '\u222A', + '\u00A4', + '\u21D3', + '\u2020', + '\u2193', + '\u00B0', + '\u03B4', + '\u2666', + '\u00F7', + '\u00E9', + '\u00EA', + '\u00E8', + '\u2205', + '\u2003', + '\u2002', + '\u03B5', + '\u2261', + '\u03B7', + '\u00F0', + '\u00EB', + '\u20AC', + '\u2203', + '\u0192', + '\u2200', + '\u00BD', + '\u00BC', + '\u00BE', + '\u2044', + '\u03B3', + '\u2265', + '\u003E', + '\u21D4', + '\u2194', + '\u2665', + '\u2026', + '\u00ED', + '\u00EE', + '\u00A1', + '\u00EC', + '\u2111', + '\u221E', + '\u222B', + '\u03B9', + '\u00BF', + '\u2208', + '\u00EF', + '\u03BA', + '\u21D0', + '\u03BB', + '\u2329', + '\u00AB', + '\u2190', + '\u2308', + '\u201C', + '\u2264', + '\u230A', + '\u2217', + '\u25CA', + '\u200E', + '\u2039', + '\u2018', + '\u003C', + '\u00AF', + '\u2014', + '\u00B5', + '\u00B7', + '\u2212', + '\u03BC', + '\u2207', + '\u00A0', + '\u2013', + '\u2260', + '\u220B', + '\u00AC', + '\u2209', + '\u2284', + '\u00F1', + '\u03BD', + '\u00F3', + '\u00F4', + '\u0153', + '\u00F2', + '\u203E', + '\u03C9', + '\u03BF', + '\u2295', + '\u2228', + '\u00AA', + '\u00BA', + '\u00F8', + '\u00F5', + '\u2297', + '\u00F6', + '\u00B6', + '\u2202', + '\u2030', + '\u22A5', + '\u03C6', + '\u03C0', + '\u03D6', + '\u00B1', + '\u00A3', + '\u2032', + '\u220F', + '\u221D', + '\u03C8', + '\u0022', + '\u21D2', + '\u221A', + '\u232A', + '\u00BB', + '\u2192', + '\u2309', + '\u201D', + '\u211C', + '\u00AE', + '\u230B', + '\u03C1', + '\u200F', + '\u203A', + '\u2019', + '\u201A', + '\u0161', + '\u22C5', + '\u00A7', + '\u00AD', + '\u03C3', + '\u03C2', + '\u223C', + '\u2660', + '\u2282', + '\u2286', + '\u2211', + '\u2283', + '\u00B9', + '\u00B2', + '\u00B3', + '\u2287', + '\u00DF', + '\u03C4', + '\u2234', + '\u03B8', + '\u03D1', + '\u2009', + '\u00FE', + '\u02DC', + '\u00D7', + '\u2122', + '\u21D1', + '\u00FA', + '\u2191', + '\u00FB', + '\u00F9', + '\u00A8', + '\u03D2', + '\u03C5', + '\u00FC', + '\u2118', + '\u03BE', + '\u00FD', + '\u00A5', + '\u00FF', + '\u03B6', + '\u200D', + '\u200C' + }; + + #region Methods + + static void WriteCharBytes(IList buf, char ch, Encoding e) + { + if (ch > 255) + { + foreach (byte b in e.GetBytes(new char[] { ch })) + buf.Add(b); + } + else + buf.Add((byte)ch); + } + + public static string UrlDecode(string s, Encoding e) + { + if (null == s) + return null; + + if (s.IndexOf('%') == -1 && s.IndexOf('+') == -1) + return s; + + if (e == null) + e = Encoding.UTF8; + + long len = s.Length; + var bytes = new List<byte>(); + int xchar; + char ch; + + for (int i = 0; i < len; i++) + { + ch = s[i]; + if (ch == '%' && i + 2 < len && s[i + 1] != '%') + { + if (s[i + 1] == 'u' && i + 5 < len) + { + // unicode hex sequence + xchar = GetChar(s, i + 2, 4); + if (xchar != -1) + { + WriteCharBytes(bytes, (char)xchar, e); + i += 5; + } + else + WriteCharBytes(bytes, '%', e); + } + else if ((xchar = GetChar(s, i + 1, 2)) != -1) + { + WriteCharBytes(bytes, (char)xchar, e); + i += 2; + } + else + { + WriteCharBytes(bytes, '%', e); + } + continue; + } + + if (ch == '+') + WriteCharBytes(bytes, ' ', e); + else + WriteCharBytes(bytes, ch, e); + } + + byte[] buf = bytes.ToArray(bytes.Count); + bytes = null; + return e.GetString(buf, 0, buf.Length); + + } + + static int GetInt(byte b) + { + char c = (char)b; + if (c >= '0' && c <= '9') + return c - '0'; + + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + + return -1; + } + + static int GetChar(string str, int offset, int length) + { + int val = 0; + int end = length + offset; + for (int i = offset; i < end; i++) + { + char c = str[i]; + if (c > 127) + return -1; + + int current = GetInt((byte)c); + if (current == -1) + return -1; + val = (val << 4) + current; + } + + return val; + } + + static bool TryConvertKeyToEntity(string key, out char value) + { + var token = CalculateKeyValue(key); + if (token == 0) + { + value = '\0'; + return false; + } + + var idx = Array.BinarySearch(entities, token); + if (idx < 0) + { + value = '\0'; + return false; + } + + value = entities_values[idx]; + return true; + } + + static long CalculateKeyValue(string s) + { + if (s.Length > 8) + return 0; + + long key = 0; + for (int i = 0; i < s.Length; ++i) + { + long ch = s[i]; + if (ch > 'z' || ch < '0') + return 0; + + key |= ch << ((7 - i) * 8); + } + + return key; + } + + /// <summary> + /// Decodes an HTML-encoded string and returns the decoded string. + /// </summary> + /// <param name="s">The HTML string to decode. </param> + /// <returns>The decoded text.</returns> + public static string HtmlDecode(string s) + { + if (s == null) + throw new ArgumentNullException("s"); + + if (s.IndexOf('&') == -1) + return s; + + StringBuilder entity = new StringBuilder(); + StringBuilder output = new StringBuilder(); + int len = s.Length; + // 0 -> nothing, + // 1 -> right after '&' + // 2 -> between '&' and ';' but no '#' + // 3 -> '#' found after '&' and getting numbers + int state = 0; + int number = 0; + int digit_start = 0; + bool hex_number = false; + + for (int i = 0; i < len; i++) + { + char c = s[i]; + if (state == 0) + { + if (c == '&') + { + entity.Append(c); + state = 1; + } + else + { + output.Append(c); + } + continue; + } + + if (c == '&') + { + state = 1; + if (digit_start > 0) + { + entity.Append(s, digit_start, i - digit_start); + digit_start = 0; + } + + output.Append(entity.ToString()); + entity.Length = 0; + entity.Append('&'); + continue; + } + + switch (state) + { + case 1: + if (c == ';') + { + state = 0; + output.Append(entity.ToString()); + output.Append(c); + entity.Length = 0; + break; + } + + number = 0; + hex_number = false; + if (c != '#') + { + state = 2; + } + else + { + state = 3; + } + entity.Append(c); + + break; + case 2: + entity.Append(c); + if (c == ';') + { + string key = entity.ToString(); + state = 0; + entity.Length = 0; + + if (key.Length > 1) + { + var skey = key.Substring(1, key.Length - 2); + if (TryConvertKeyToEntity(skey, out c)) + { + output.Append(c); + break; + } + } + + output.Append(key); + } + + break; + case 3: + if (c == ';') + { + if (number < 0x10000) + { + output.Append((char)number); + } + else + { + output.Append((char)(0xd800 + ((number - 0x10000) >> 10))); + output.Append((char)(0xdc00 + ((number - 0x10000) & 0x3ff))); + } + state = 0; + entity.Length = 0; + digit_start = 0; + break; + } + + if (c == 'x' || c == 'X' && !hex_number) + { + digit_start = i; + hex_number = true; + break; + } + + if (Char.IsDigit(c)) + { + if (digit_start == 0) + digit_start = i; + + number = number * (hex_number ? 16 : 10) + ((int)c - '0'); + break; + } + + if (hex_number) + { + if (c >= 'a' && c <= 'f') + { + number = number * 16 + 10 + ((int)c - 'a'); + break; + } + if (c >= 'A' && c <= 'F') + { + number = number * 16 + 10 + ((int)c - 'A'); + break; + } + } + + state = 2; + if (digit_start > 0) + { + entity.Append(s, digit_start, i - digit_start); + digit_start = 0; + } + + entity.Append(c); + break; + } + } + + if (entity.Length > 0) + { + output.Append(entity); + } + else if (digit_start > 0) + { + output.Append(s, digit_start, s.Length - digit_start); + } + return output.ToString(); + } + + public static QueryParamCollection ParseQueryString(string query) + { + return ParseQueryString(query, Encoding.UTF8); + } + + public static QueryParamCollection ParseQueryString(string query, Encoding encoding) + { + if (query == null) + throw new ArgumentNullException("query"); + if (encoding == null) + throw new ArgumentNullException("encoding"); + if (query.Length == 0 || (query.Length == 1 && query[0] == '?')) + return new QueryParamCollection(); + if (query[0] == '?') + query = query.Substring(1); + + QueryParamCollection result = new QueryParamCollection(); + ParseQueryString(query, encoding, result); + return result; + } + + internal static void ParseQueryString(string query, Encoding encoding, QueryParamCollection result) + { + if (query.Length == 0) + return; + + string decoded = HtmlDecode(query); + int decodedLength = decoded.Length; + int namePos = 0; + bool first = true; + while (namePos <= decodedLength) + { + int valuePos = -1, valueEnd = -1; + for (int q = namePos; q < decodedLength; q++) + { + if (valuePos == -1 && decoded[q] == '=') + { + valuePos = q + 1; + } + else if (decoded[q] == '&') + { + valueEnd = q; + break; + } + } + + if (first) + { + first = false; + if (decoded[namePos] == '?') + namePos++; + } + + string name, value; + if (valuePos == -1) + { + name = null; + valuePos = namePos; + } + else + { + name = UrlDecode(decoded.Substring(namePos, valuePos - namePos - 1), encoding); + } + if (valueEnd < 0) + { + namePos = -1; + valueEnd = decoded.Length; + } + else + { + namePos = valueEnd + 1; + } + value = UrlDecode(decoded.Substring(valuePos, valueEnd - valuePos), encoding); + + result.Add(name, value); + if (namePos == -1) + break; + } + } + #endregion // Methods + } +} diff --git a/MediaBrowser.Model/Services/IAsyncStreamWriter.cs b/MediaBrowser.Model/Services/IAsyncStreamWriter.cs new file mode 100644 index 000000000..b10e12813 --- /dev/null +++ b/MediaBrowser.Model/Services/IAsyncStreamWriter.cs @@ -0,0 +1,11 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Model.Services +{ + public interface IAsyncStreamWriter + { + Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken); + } +} diff --git a/MediaBrowser.Model/Services/IHasHeaders.cs b/MediaBrowser.Model/Services/IHasHeaders.cs new file mode 100644 index 000000000..35e652b0f --- /dev/null +++ b/MediaBrowser.Model/Services/IHasHeaders.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Model.Services +{ + public interface IHasHeaders + { + IDictionary<string, string> Headers { get; } + } +} diff --git a/MediaBrowser.Model/Services/IHasRequestFilter.cs b/MediaBrowser.Model/Services/IHasRequestFilter.cs new file mode 100644 index 000000000..2164179d5 --- /dev/null +++ b/MediaBrowser.Model/Services/IHasRequestFilter.cs @@ -0,0 +1,21 @@ + +namespace MediaBrowser.Model.Services +{ + public interface IHasRequestFilter + { + /// <summary> + /// Order in which Request Filters are executed. + /// <0 Executed before global request filters + /// >0 Executed after global request filters + /// </summary> + int Priority { get; } + + /// <summary> + /// The request filter is executed before the service. + /// </summary> + /// <param name="req">The http request wrapper</param> + /// <param name="res">The http response wrapper</param> + /// <param name="requestDto">The request DTO</param> + void RequestFilter(IRequest req, IResponse res, object requestDto); + } +} diff --git a/MediaBrowser.Model/Services/IHttpRequest.cs b/MediaBrowser.Model/Services/IHttpRequest.cs new file mode 100644 index 000000000..e1480f30a --- /dev/null +++ b/MediaBrowser.Model/Services/IHttpRequest.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace MediaBrowser.Model.Services +{ + public interface IHttpRequest : IRequest + { + /// <summary> + /// The HttpResponse + /// </summary> + IHttpResponse HttpResponse { get; } + + /// <summary> + /// The HTTP Verb + /// </summary> + string HttpMethod { get; } + + /// <summary> + /// The IP Address of the X-Forwarded-For header, null if null or empty + /// </summary> + string XForwardedFor { get; } + + /// <summary> + /// The Port number of the X-Forwarded-Port header, null if null or empty + /// </summary> + int? XForwardedPort { get; } + + /// <summary> + /// The http or https scheme of the X-Forwarded-Proto header, null if null or empty + /// </summary> + string XForwardedProtocol { get; } + + /// <summary> + /// The value of the X-Real-IP header, null if null or empty + /// </summary> + string XRealIp { get; } + + /// <summary> + /// The value of the Accept HTTP Request Header + /// </summary> + string Accept { get; } + } +} diff --git a/MediaBrowser.Model/Services/IHttpResponse.cs b/MediaBrowser.Model/Services/IHttpResponse.cs new file mode 100644 index 000000000..cd9c07d46 --- /dev/null +++ b/MediaBrowser.Model/Services/IHttpResponse.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace MediaBrowser.Model.Services +{ + public interface IHttpResponse : IResponse + { + //ICookies Cookies { get; } + + /// <summary> + /// Adds a new Set-Cookie instruction to Response + /// </summary> + /// <param name="cookie"></param> + void SetCookie(Cookie cookie); + + /// <summary> + /// Removes all pending Set-Cookie instructions + /// </summary> + void ClearCookies(); + } +} diff --git a/MediaBrowser.Model/Services/IHttpResult.cs b/MediaBrowser.Model/Services/IHttpResult.cs new file mode 100644 index 000000000..b912ef023 --- /dev/null +++ b/MediaBrowser.Model/Services/IHttpResult.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace MediaBrowser.Model.Services +{ + public interface IHttpResult : IHasHeaders + { + /// <summary> + /// The HTTP Response Status + /// </summary> + int Status { get; set; } + + /// <summary> + /// The HTTP Response Status Code + /// </summary> + HttpStatusCode StatusCode { get; set; } + + /// <summary> + /// The HTTP Response ContentType + /// </summary> + string ContentType { get; set; } + + /// <summary> + /// Response DTO + /// </summary> + object Response { get; set; } + + /// <summary> + /// Holds the request call context + /// </summary> + IRequest RequestContext { get; set; } + } +} diff --git a/MediaBrowser.Model/Services/IRequest.cs b/MediaBrowser.Model/Services/IRequest.cs new file mode 100644 index 000000000..681bab294 --- /dev/null +++ b/MediaBrowser.Model/Services/IRequest.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.IO; + +namespace MediaBrowser.Model.Services +{ + public interface IRequest + { + /// <summary> + /// The underlying ASP.NET or HttpListener HttpRequest + /// </summary> + object OriginalRequest { get; } + + IResponse Response { get; } + + /// <summary> + /// The name of the service being called (e.g. Request DTO Name) + /// </summary> + string OperationName { get; set; } + + /// <summary> + /// The Verb / HttpMethod or Action for this request + /// </summary> + string Verb { get; } + + /// <summary> + /// The Request DTO, after it has been deserialized. + /// </summary> + object Dto { get; set; } + + /// <summary> + /// The request ContentType + /// </summary> + string ContentType { get; } + + bool IsLocal { get; } + + string UserAgent { get; } + + IDictionary<string, Cookie> Cookies { get; } + + /// <summary> + /// The expected Response ContentType for this request + /// </summary> + string ResponseContentType { get; set; } + + /// <summary> + /// Attach any data to this request that all filters and services can access. + /// </summary> + Dictionary<string, object> Items { get; } + + QueryParamCollection Headers { get; } + + QueryParamCollection QueryString { get; } + + Task<QueryParamCollection> GetFormData(); + + string RawUrl { get; } + + string AbsoluteUri { get; } + + /// <summary> + /// The Remote Ip as reported by Request.UserHostAddress + /// </summary> + string UserHostAddress { get; } + + /// <summary> + /// The Remote Ip as reported by X-Forwarded-For, X-Real-IP or Request.UserHostAddress + /// </summary> + string RemoteIp { get; } + + /// <summary> + /// The value of the Authorization Header used to send the Api Key, null if not available + /// </summary> + string Authorization { get; } + + /// <summary> + /// e.g. is https or not + /// </summary> + bool IsSecureConnection { get; } + + string[] AcceptTypes { get; } + + string PathInfo { get; } + + Stream InputStream { get; } + + long ContentLength { get; } + + /// <summary> + /// Access to the multi-part/formdata files posted on this request + /// </summary> + IHttpFile[] Files { get; } + + /// <summary> + /// The value of the Referrer, null if not available + /// </summary> + Uri UrlReferrer { get; } + } + + public interface IHttpFile + { + string Name { get; } + string FileName { get; } + long ContentLength { get; } + string ContentType { get; } + Stream InputStream { get; } + } + + public interface IRequiresRequest + { + IRequest Request { get; set; } + } + + public interface IResponse + { + IRequest Request { get; } + + int StatusCode { get; set; } + + string StatusDescription { get; set; } + + string ContentType { get; set; } + + void AddHeader(string name, string value); + + string GetHeader(string name); + + void Redirect(string url); + + Stream OutputStream { get; } + + /// <summary> + /// Signal that this response has been handled and no more processing should be done. + /// When used in a request or response filter, no more filters or processing is done on this request. + /// </summary> + void Close(); + + /// <summary> + /// Gets a value indicating whether this instance is closed. + /// </summary> + bool IsClosed { get; } + + void SetContentLength(long contentLength); + + //Add Metadata to Response + Dictionary<string, object> Items { get; } + + QueryParamCollection Headers { get; } + + Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken); + + bool SendChunked { get; set; } + } +} diff --git a/MediaBrowser.Model/Services/IRequiresRequestStream.cs b/MediaBrowser.Model/Services/IRequiresRequestStream.cs new file mode 100644 index 000000000..0b8ac3ed3 --- /dev/null +++ b/MediaBrowser.Model/Services/IRequiresRequestStream.cs @@ -0,0 +1,12 @@ +using System.IO; + +namespace MediaBrowser.Model.Services +{ + public interface IRequiresRequestStream + { + /// <summary> + /// The raw Http Request Input Stream + /// </summary> + Stream RequestStream { get; set; } + } +} diff --git a/MediaBrowser.Model/Services/IService.cs b/MediaBrowser.Model/Services/IService.cs new file mode 100644 index 000000000..3e0ff280b --- /dev/null +++ b/MediaBrowser.Model/Services/IService.cs @@ -0,0 +1,12 @@ + +namespace MediaBrowser.Model.Services +{ + // marker interface + public interface IService + { + } + + public interface IReturn { } + public interface IReturn<T> : IReturn { } + public interface IReturnVoid : IReturn { } +} diff --git a/MediaBrowser.Model/Services/IStreamWriter.cs b/MediaBrowser.Model/Services/IStreamWriter.cs new file mode 100644 index 000000000..1fc11049e --- /dev/null +++ b/MediaBrowser.Model/Services/IStreamWriter.cs @@ -0,0 +1,9 @@ +using System.IO; + +namespace MediaBrowser.Model.Services +{ + public interface IStreamWriter + { + void WriteTo(Stream responseStream); + } +} diff --git a/MediaBrowser.Model/Services/QueryParamCollection.cs b/MediaBrowser.Model/Services/QueryParamCollection.cs new file mode 100644 index 000000000..6f8a76598 --- /dev/null +++ b/MediaBrowser.Model/Services/QueryParamCollection.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Extensions; + +namespace MediaBrowser.Model.Services +{ + public class QueryParamCollection : List<NameValuePair> + { + public QueryParamCollection() + { + + } + + public QueryParamCollection(IDictionary<string, string> headers) + { + foreach (var pair in headers) + { + Add(pair.Key, pair.Value); + } + } + + private StringComparison GetStringComparison() + { + return StringComparison.OrdinalIgnoreCase; + } + + private StringComparer GetStringComparer() + { + return StringComparer.OrdinalIgnoreCase; + } + + public string GetKey(int index) + { + return this[index].Name; + } + + public string Get(int index) + { + return this[index].Value; + } + + public virtual string[] GetValues(int index) + { + return new[] { Get(index) }; + } + + /// <summary> + /// Adds a new query parameter. + /// </summary> + public virtual void Add(string key, string value) + { + Add(new NameValuePair(key, value)); + } + + public virtual void Set(string key, string value) + { + if (string.IsNullOrEmpty(value)) + { + var parameters = GetItems(key); + + foreach (var p in parameters) + { + Remove(p); + } + + return; + } + + foreach (var pair in this) + { + var stringComparison = GetStringComparison(); + + if (string.Equals(key, pair.Name, stringComparison)) + { + pair.Value = value; + return; + } + } + + Add(key, value); + } + + /// <summary> + /// Removes all parameters of the given name. + /// </summary> + /// <returns>The number of parameters that were removed</returns> + /// <exception cref="ArgumentNullException"><paramref name="name" /> is null.</exception> + public virtual int Remove(string name) + { + return RemoveAll(p => p.Name == name); + } + + public string Get(string name) + { + var stringComparison = GetStringComparison(); + + foreach (var pair in this) + { + if (string.Equals(pair.Name, name, stringComparison)) + { + return pair.Value; + } + } + + return null; + } + + public virtual List<NameValuePair> GetItems(string name) + { + var stringComparison = GetStringComparison(); + + var list = new List<NameValuePair>(); + + foreach (var pair in this) + { + if (string.Equals(pair.Name, name, stringComparison)) + { + list.Add(pair); + } + } + + return list; + } + + public virtual List<string> GetValues(string name) + { + var stringComparison = GetStringComparison(); + + var list = new List<string>(); + + foreach (var pair in this) + { + if (string.Equals(pair.Name, name, stringComparison)) + { + list.Add(pair.Value); + } + } + + return list; + } + + public Dictionary<string, string> ToDictionary() + { + var stringComparer = GetStringComparer(); + + var headers = new Dictionary<string, string>(stringComparer); + + foreach (var pair in this) + { + headers[pair.Name] = pair.Value; + } + + return headers; + } + + public IEnumerable<string> Keys + { + get + { + var keys = new string[this.Count]; + + for (var i = 0; i < keys.Length; i++) + { + keys[i] = this[i].Name; + } + + return keys; + } + } + + /// <summary> + /// Gets or sets a query parameter value by name. A query may contain multiple values of the same name + /// (i.e. "x=1&x=2"), in which case the value is an array, which works for both getting and setting. + /// </summary> + /// <param name="name">The query parameter name</param> + /// <returns>The query parameter value or array of values</returns> + public string this[string name] + { + get { return Get(name); } + set + { + Set(name, value); + //var parameters = this.Where(p => p.Name == name).ToArray(); + //var values = new[] { value }; + + //for (int i = 0; ; i++) + //{ + // if (i < parameters.Length && i < values.Length) + // { + // if (values[i] == null) + // Remove(parameters[i]); + // else if (values[i] is NameValuePair) + // this[IndexOf(parameters[i])] = (NameValuePair)values[i]; + // else + // parameters[i].Value = values[i]; + // } + // else if (i < parameters.Length) + // Remove(parameters[i]); + // else if (i < values.Length) + // { + // if (values[i] != null) + // { + // if (values[i] is NameValuePair) + // Add((NameValuePair)values[i]); + // else + // Add(name, values[i]); + // } + // } + // else + // break; + //} + } + } + + private string GetQueryStringValue(NameValuePair pair) + { + return pair.Name + "=" + pair.Value; + } + + public override String ToString() + { + var vals = this.Select(GetQueryStringValue).ToArray(this.Count); + + return string.Join("&", vals); + } + } +} diff --git a/MediaBrowser.Model/Services/RouteAttribute.cs b/MediaBrowser.Model/Services/RouteAttribute.cs new file mode 100644 index 000000000..264500e60 --- /dev/null +++ b/MediaBrowser.Model/Services/RouteAttribute.cs @@ -0,0 +1,148 @@ +using System; + +namespace MediaBrowser.Model.Services +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] + public class RouteAttribute : Attribute + { + /// <summary> + /// <para>Initializes an instance of the <see cref="RouteAttribute"/> class.</para> + /// </summary> + /// <param name="path"> + /// <para>The path template to map to the request. See + /// <see cref="Path">RouteAttribute.Path</see> + /// for details on the correct format.</para> + /// </param> + public RouteAttribute(string path) + : this(path, null) + { + } + + /// <summary> + /// <para>Initializes an instance of the <see cref="RouteAttribute"/> class.</para> + /// </summary> + /// <param name="path"> + /// <para>The path template to map to the request. See + /// <see cref="Path">RouteAttribute.Path</see> + /// for details on the correct format.</para> + /// </param> + /// <param name="verbs">A comma-delimited list of HTTP verbs supported by the + /// service. If unspecified, all verbs are assumed to be supported.</param> + public RouteAttribute(string path, string verbs) + { + Path = path; + Verbs = verbs; + } + + /// <summary> + /// Gets or sets the path template to be mapped to the request. + /// </summary> + /// <value> + /// A <see cref="String"/> value providing the path mapped to + /// the request. Never <see langword="null"/>. + /// </value> + /// <remarks> + /// <para>Some examples of valid paths are:</para> + /// + /// <list> + /// <item>"/Inventory"</item> + /// <item>"/Inventory/{Category}/{ItemId}"</item> + /// <item>"/Inventory/{ItemPath*}"</item> + /// </list> + /// + /// <para>Variables are specified within "{}" + /// brackets. Each variable in the path is mapped to the same-named property + /// on the request DTO. At runtime, ServiceStack will parse the + /// request URL, extract the variable values, instantiate the request DTO, + /// and assign the variable values into the corresponding request properties, + /// prior to passing the request DTO to the service object for processing.</para> + /// + /// <para>It is not necessary to specify all request properties as + /// variables in the path. For unspecified properties, callers may provide + /// values in the query string. For example: the URL + /// "http://services/Inventory?Category=Books&ItemId=12345" causes the same + /// request DTO to be processed as "http://services/Inventory/Books/12345", + /// provided that the paths "/Inventory" (which supports the first URL) and + /// "/Inventory/{Category}/{ItemId}" (which supports the second URL) + /// are both mapped to the request DTO.</para> + /// + /// <para>Please note that while it is possible to specify property values + /// in the query string, it is generally considered to be less RESTful and + /// less desirable than to specify them as variables in the path. Using the + /// query string to specify property values may also interfere with HTTP + /// caching.</para> + /// + /// <para>The final variable in the path may contain a "*" suffix + /// to grab all remaining segments in the path portion of the request URL and assign + /// them to a single property on the request DTO. + /// For example, if the path "/Inventory/{ItemPath*}" is mapped to the request DTO, + /// then the request URL "http://services/Inventory/Books/12345" will result + /// in a request DTO whose ItemPath property contains "Books/12345". + /// You may only specify one such variable in the path, and it must be positioned at + /// the end of the path.</para> + /// </remarks> + public string Path { get; set; } + + /// <summary> + /// Gets or sets short summary of what the route does. + /// </summary> + public string Summary { get; set; } + + public string Description { get; set; } + + public bool IsHidden { get; set; } + + /// <summary> + /// Gets or sets longer text to explain the behaviour of the route. + /// </summary> + public string Notes { get; set; } + + /// <summary> + /// Gets or sets a comma-delimited list of HTTP verbs supported by the service, such as + /// "GET,PUT,POST,DELETE". + /// </summary> + /// <value> + /// A <see cref="String"/> providing a comma-delimited list of HTTP verbs supported + /// by the service, <see langword="null"/> or empty if all verbs are supported. + /// </value> + public string Verbs { get; set; } + + /// <summary> + /// Used to rank the precedences of route definitions in reverse routing. + /// i.e. Priorities below 0 are auto-generated have less precedence. + /// </summary> + public int Priority { get; set; } + + protected bool Equals(RouteAttribute other) + { + return base.Equals(other) + && string.Equals(Path, other.Path) + && string.Equals(Summary, other.Summary) + && string.Equals(Notes, other.Notes) + && string.Equals(Verbs, other.Verbs) + && Priority == other.Priority; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((RouteAttribute)obj); + } + + public override int GetHashCode() + { + unchecked + { + var hashCode = base.GetHashCode(); + hashCode = (hashCode * 397) ^ (Path != null ? Path.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (Summary != null ? Summary.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (Notes != null ? Notes.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (Verbs != null ? Verbs.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ Priority; + return hashCode; + } + } + } +} diff --git a/MediaBrowser.Model/Session/BrowseRequest.cs b/MediaBrowser.Model/Session/BrowseRequest.cs new file mode 100644 index 000000000..0a13c0549 --- /dev/null +++ b/MediaBrowser.Model/Session/BrowseRequest.cs @@ -0,0 +1,27 @@ + +namespace MediaBrowser.Model.Session +{ + /// <summary> + /// Class BrowseRequest + /// </summary> + public class BrowseRequest + { + /// <summary> + /// Artist, Genre, Studio, Person, or any kind of BaseItem + /// </summary> + /// <value>The type of the item.</value> + public string ItemType { get; set; } + + /// <summary> + /// Gets or sets the item id. + /// </summary> + /// <value>The item id.</value> + public string ItemId { get; set; } + + /// <summary> + /// Gets or sets the name of the item. + /// </summary> + /// <value>The name of the item.</value> + public string ItemName { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Session/ClientCapabilities.cs b/MediaBrowser.Model/Session/ClientCapabilities.cs new file mode 100644 index 000000000..0a780b910 --- /dev/null +++ b/MediaBrowser.Model/Session/ClientCapabilities.cs @@ -0,0 +1,33 @@ +using MediaBrowser.Model.Dlna; +using System; + +namespace MediaBrowser.Model.Session +{ + public class ClientCapabilities + { + public string[] PlayableMediaTypes { get; set; } + + public string[] SupportedCommands { get; set; } + + public bool SupportsMediaControl { get; set; } + public bool SupportsContentUploading { get; set; } + public string MessageCallbackUrl { get; set; } + public string PushToken { get; set; } + public string PushTokenType { get; set; } + + public bool SupportsPersistentIdentifier { get; set; } + public bool SupportsSync { get; set; } + + public DeviceProfile DeviceProfile { get; set; } + + public string AppStoreUrl { get; set; } + public string IconUrl { get; set; } + + public ClientCapabilities() + { + PlayableMediaTypes = new string[] {}; + SupportedCommands = new string[] {}; + SupportsPersistentIdentifier = true; + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Session/GeneralCommand.cs b/MediaBrowser.Model/Session/GeneralCommand.cs new file mode 100644 index 000000000..5cfe3e67b --- /dev/null +++ b/MediaBrowser.Model/Session/GeneralCommand.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using System; + +namespace MediaBrowser.Model.Session +{ + public class GeneralCommand + { + public string Name { get; set; } + + public Guid ControllingUserId { get; set; } + + public Dictionary<string, string> Arguments { get; set; } + + public GeneralCommand() + { + Arguments = new Dictionary<string, string>(); + } + } +} diff --git a/MediaBrowser.Model/Session/GeneralCommandType.cs b/MediaBrowser.Model/Session/GeneralCommandType.cs new file mode 100644 index 000000000..9044dc3ec --- /dev/null +++ b/MediaBrowser.Model/Session/GeneralCommandType.cs @@ -0,0 +1,46 @@ +namespace MediaBrowser.Model.Session +{ + /// <summary> + /// This exists simply to identify a set of known commands. + /// </summary> + public enum GeneralCommandType + { + MoveUp = 0, + MoveDown = 1, + MoveLeft = 2, + MoveRight = 3, + PageUp = 4, + PageDown = 5, + PreviousLetter = 6, + NextLetter = 7, + ToggleOsd = 8, + ToggleContextMenu = 9, + Select = 10, + Back = 11, + TakeScreenshot = 12, + SendKey = 13, + SendString = 14, + GoHome = 15, + GoToSettings = 16, + VolumeUp = 17, + VolumeDown = 18, + Mute = 19, + Unmute = 20, + ToggleMute = 21, + SetVolume = 22, + SetAudioStreamIndex = 23, + SetSubtitleStreamIndex = 24, + ToggleFullscreen = 25, + DisplayContent = 26, + GoToSearch = 27, + DisplayMessage = 28, + SetRepeatMode = 29, + ChannelUp = 30, + ChannelDown = 31, + SetMaxStreamingBitrate = 31, + Guide = 32, + ToggleStats = 33, + PlayMediaSource = 34, + PlayTrailers = 35 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Session/MessageCommand.cs b/MediaBrowser.Model/Session/MessageCommand.cs new file mode 100644 index 000000000..b028765ed --- /dev/null +++ b/MediaBrowser.Model/Session/MessageCommand.cs @@ -0,0 +1,12 @@ + +namespace MediaBrowser.Model.Session +{ + public class MessageCommand + { + public string Header { get; set; } + + public string Text { get; set; } + + public long? TimeoutMs { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Session/PlayCommand.cs b/MediaBrowser.Model/Session/PlayCommand.cs new file mode 100644 index 000000000..3a5a951d7 --- /dev/null +++ b/MediaBrowser.Model/Session/PlayCommand.cs @@ -0,0 +1,29 @@ +namespace MediaBrowser.Model.Session +{ + /// <summary> + /// Enum PlayCommand + /// </summary> + public enum PlayCommand + { + /// <summary> + /// The play now + /// </summary> + PlayNow = 0, + /// <summary> + /// The play next + /// </summary> + PlayNext = 1, + /// <summary> + /// The play last + /// </summary> + PlayLast = 2, + /// <summary> + /// The play instant mix + /// </summary> + PlayInstantMix = 3, + /// <summary> + /// The play shuffle + /// </summary> + PlayShuffle = 4 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Session/PlayMethod.cs b/MediaBrowser.Model/Session/PlayMethod.cs new file mode 100644 index 000000000..87b728627 --- /dev/null +++ b/MediaBrowser.Model/Session/PlayMethod.cs @@ -0,0 +1,9 @@ +namespace MediaBrowser.Model.Session +{ + public enum PlayMethod + { + Transcode = 0, + DirectStream = 1, + DirectPlay = 2 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Session/PlayRequest.cs b/MediaBrowser.Model/Session/PlayRequest.cs new file mode 100644 index 000000000..2ee489f96 --- /dev/null +++ b/MediaBrowser.Model/Session/PlayRequest.cs @@ -0,0 +1,43 @@ +using MediaBrowser.Model.Services; +using System; + +namespace MediaBrowser.Model.Session +{ + /// <summary> + /// Class PlayRequest + /// </summary> + public class PlayRequest + { + /// <summary> + /// Gets or sets the item ids. + /// </summary> + /// <value>The item ids.</value> + [ApiMember(Name = "ItemIds", Description = "The ids of the items to play, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)] + public Guid[] ItemIds { get; set; } + + /// <summary> + /// Gets or sets the start position ticks that the first item should be played at + /// </summary> + /// <value>The start position ticks.</value> + [ApiMember(Name = "StartPositionTicks", Description = "The starting position of the first item.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + public long? StartPositionTicks { get; set; } + + /// <summary> + /// Gets or sets the play command. + /// </summary> + /// <value>The play command.</value> + [ApiMember(Name = "PlayCommand", Description = "The type of play command to issue (PlayNow, PlayNext, PlayLast). Clients who have not yet implemented play next and play last may play now.", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] + public PlayCommand PlayCommand { get; set; } + + /// <summary> + /// Gets or sets the controlling user identifier. + /// </summary> + /// <value>The controlling user identifier.</value> + public Guid ControllingUserId { get; set; } + + public int? SubtitleStreamIndex { get; set; } + public int? AudioStreamIndex { get; set; } + public string MediaSourceId { get; set; } + public int? StartIndex { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Session/PlaybackProgressInfo.cs b/MediaBrowser.Model/Session/PlaybackProgressInfo.cs new file mode 100644 index 000000000..ce6b2875e --- /dev/null +++ b/MediaBrowser.Model/Session/PlaybackProgressInfo.cs @@ -0,0 +1,119 @@ +using MediaBrowser.Model.Dto; +using System; + +namespace MediaBrowser.Model.Session +{ + /// <summary> + /// Class PlaybackProgressInfo. + /// </summary> + public class PlaybackProgressInfo + { + /// <summary> + /// Gets or sets a value indicating whether this instance can seek. + /// </summary> + /// <value><c>true</c> if this instance can seek; otherwise, <c>false</c>.</value> + public bool CanSeek { get; set; } + + /// <summary> + /// Gets or sets the item. + /// </summary> + /// <value>The item.</value> + public BaseItemDto Item { get; set; } + + /// <summary> + /// Gets or sets the item identifier. + /// </summary> + /// <value>The item identifier.</value> + public Guid ItemId { get; set; } + + /// <summary> + /// Gets or sets the session id. + /// </summary> + /// <value>The session id.</value> + public string SessionId { get; set; } + + /// <summary> + /// Gets or sets the media version identifier. + /// </summary> + /// <value>The media version identifier.</value> + public string MediaSourceId { get; set; } + + /// <summary> + /// Gets or sets the index of the audio stream. + /// </summary> + /// <value>The index of the audio stream.</value> + public int? AudioStreamIndex { get; set; } + + /// <summary> + /// Gets or sets the index of the subtitle stream. + /// </summary> + /// <value>The index of the subtitle stream.</value> + public int? SubtitleStreamIndex { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is paused. + /// </summary> + /// <value><c>true</c> if this instance is paused; otherwise, <c>false</c>.</value> + public bool IsPaused { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is muted. + /// </summary> + /// <value><c>true</c> if this instance is muted; otherwise, <c>false</c>.</value> + public bool IsMuted { get; set; } + + /// <summary> + /// Gets or sets the position ticks. + /// </summary> + /// <value>The position ticks.</value> + public long? PositionTicks { get; set; } + + public long? PlaybackStartTimeTicks { get; set; } + + /// <summary> + /// Gets or sets the volume level. + /// </summary> + /// <value>The volume level.</value> + public int? VolumeLevel { get; set; } + + public int? Brightness { get; set; } + + public string AspectRatio { get; set; } + + /// <summary> + /// Gets or sets the play method. + /// </summary> + /// <value>The play method.</value> + public PlayMethod PlayMethod { get; set; } + /// <summary> + /// Gets or sets the live stream identifier. + /// </summary> + /// <value>The live stream identifier.</value> + public string LiveStreamId { get; set; } + /// <summary> + /// Gets or sets the play session identifier. + /// </summary> + /// <value>The play session identifier.</value> + public string PlaySessionId { get; set; } + /// <summary> + /// Gets or sets the repeat mode. + /// </summary> + /// <value>The repeat mode.</value> + public RepeatMode RepeatMode { get; set; } + + public QueueItem[] NowPlayingQueue { get; set; } + public string PlaylistItemId { get; set; } + } + + public enum RepeatMode + { + RepeatNone = 0, + RepeatAll = 1, + RepeatOne = 2 + } + + public class QueueItem { + public Guid Id { get; set; } + public string PlaylistItemId { get; set; } + } +} diff --git a/MediaBrowser.Model/Session/PlaybackStartInfo.cs b/MediaBrowser.Model/Session/PlaybackStartInfo.cs new file mode 100644 index 000000000..f6f496e4e --- /dev/null +++ b/MediaBrowser.Model/Session/PlaybackStartInfo.cs @@ -0,0 +1,10 @@ + +namespace MediaBrowser.Model.Session +{ + /// <summary> + /// Class PlaybackStartInfo. + /// </summary> + public class PlaybackStartInfo : PlaybackProgressInfo + { + } +} diff --git a/MediaBrowser.Model/Session/PlaybackStopInfo.cs b/MediaBrowser.Model/Session/PlaybackStopInfo.cs new file mode 100644 index 000000000..6f3351eef --- /dev/null +++ b/MediaBrowser.Model/Session/PlaybackStopInfo.cs @@ -0,0 +1,57 @@ +using MediaBrowser.Model.Dto; +using System; + +namespace MediaBrowser.Model.Session +{ + /// <summary> + /// Class PlaybackStopInfo. + /// </summary> + public class PlaybackStopInfo + { + /// <summary> + /// Gets or sets the item. + /// </summary> + /// <value>The item.</value> + public BaseItemDto Item { get; set; } + /// <summary> + /// Gets or sets the item identifier. + /// </summary> + /// <value>The item identifier.</value> + public Guid ItemId { get; set; } + /// <summary> + /// Gets or sets the session id. + /// </summary> + /// <value>The session id.</value> + public string SessionId { get; set; } + /// <summary> + /// Gets or sets the media version identifier. + /// </summary> + /// <value>The media version identifier.</value> + public string MediaSourceId { get; set; } + /// <summary> + /// Gets or sets the position ticks. + /// </summary> + /// <value>The position ticks.</value> + public long? PositionTicks { get; set; } + /// <summary> + /// Gets or sets the live stream identifier. + /// </summary> + /// <value>The live stream identifier.</value> + public string LiveStreamId { get; set; } + /// <summary> + /// Gets or sets the play session identifier. + /// </summary> + /// <value>The play session identifier.</value> + public string PlaySessionId { get; set; } + /// <summary> + /// Gets or sets a value indicating whether this <see cref="PlaybackStopInfo"/> is failed. + /// </summary> + /// <value><c>true</c> if failed; otherwise, <c>false</c>.</value> + public bool Failed { get; set; } + + public string NextMediaType { get; set; } + + public string PlaylistItemId { get; set; } + public QueueItem[] NowPlayingQueue { get; set; } + } +} diff --git a/MediaBrowser.Model/Session/PlayerStateInfo.cs b/MediaBrowser.Model/Session/PlayerStateInfo.cs new file mode 100644 index 000000000..f78842e29 --- /dev/null +++ b/MediaBrowser.Model/Session/PlayerStateInfo.cs @@ -0,0 +1,65 @@ +namespace MediaBrowser.Model.Session +{ + public class PlayerStateInfo + { + /// <summary> + /// Gets or sets the now playing position ticks. + /// </summary> + /// <value>The now playing position ticks.</value> + public long? PositionTicks { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance can seek. + /// </summary> + /// <value><c>true</c> if this instance can seek; otherwise, <c>false</c>.</value> + public bool CanSeek { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is paused. + /// </summary> + /// <value><c>true</c> if this instance is paused; otherwise, <c>false</c>.</value> + public bool IsPaused { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is muted. + /// </summary> + /// <value><c>true</c> if this instance is muted; otherwise, <c>false</c>.</value> + public bool IsMuted { get; set; } + + /// <summary> + /// Gets or sets the volume level. + /// </summary> + /// <value>The volume level.</value> + public int? VolumeLevel { get; set; } + + /// <summary> + /// Gets or sets the index of the now playing audio stream. + /// </summary> + /// <value>The index of the now playing audio stream.</value> + public int? AudioStreamIndex { get; set; } + + /// <summary> + /// Gets or sets the index of the now playing subtitle stream. + /// </summary> + /// <value>The index of the now playing subtitle stream.</value> + public int? SubtitleStreamIndex { get; set; } + + /// <summary> + /// Gets or sets the now playing media version identifier. + /// </summary> + /// <value>The now playing media version identifier.</value> + public string MediaSourceId { get; set; } + + /// <summary> + /// Gets or sets the play method. + /// </summary> + /// <value>The play method.</value> + public PlayMethod? PlayMethod { get; set; } + + /// <summary> + /// Gets or sets the repeat mode. + /// </summary> + /// <value>The repeat mode.</value> + public RepeatMode RepeatMode { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Session/PlaystateCommand.cs b/MediaBrowser.Model/Session/PlaystateCommand.cs new file mode 100644 index 000000000..3b70d5454 --- /dev/null +++ b/MediaBrowser.Model/Session/PlaystateCommand.cs @@ -0,0 +1,43 @@ + +namespace MediaBrowser.Model.Session +{ + /// <summary> + /// Enum PlaystateCommand + /// </summary> + public enum PlaystateCommand + { + /// <summary> + /// The stop + /// </summary> + Stop, + /// <summary> + /// The pause + /// </summary> + Pause, + /// <summary> + /// The unpause + /// </summary> + Unpause, + /// <summary> + /// The next track + /// </summary> + NextTrack, + /// <summary> + /// The previous track + /// </summary> + PreviousTrack, + /// <summary> + /// The seek + /// </summary> + Seek, + /// <summary> + /// The rewind + /// </summary> + Rewind, + /// <summary> + /// The fast forward + /// </summary> + FastForward, + PlayPause + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Session/PlaystateRequest.cs b/MediaBrowser.Model/Session/PlaystateRequest.cs new file mode 100644 index 000000000..8a046b503 --- /dev/null +++ b/MediaBrowser.Model/Session/PlaystateRequest.cs @@ -0,0 +1,15 @@ +namespace MediaBrowser.Model.Session +{ + public class PlaystateRequest + { + public PlaystateCommand Command { get; set; } + + public long? SeekPositionTicks { get; set; } + + /// <summary> + /// Gets or sets the controlling user identifier. + /// </summary> + /// <value>The controlling user identifier.</value> + public string ControllingUserId { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Session/SessionInfoDto.cs b/MediaBrowser.Model/Session/SessionInfoDto.cs new file mode 100644 index 000000000..ca14107df --- /dev/null +++ b/MediaBrowser.Model/Session/SessionInfoDto.cs @@ -0,0 +1,117 @@ +using MediaBrowser.Model.Dto; +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Model.Session +{ + public class SessionInfoDto + { + /// <summary> + /// Gets or sets the supported commands. + /// </summary> + /// <value>The supported commands.</value> + public string[] SupportedCommands { get; set; } + + /// <summary> + /// Gets or sets the playable media types. + /// </summary> + /// <value>The playable media types.</value> + public string[] PlayableMediaTypes { get; set; } + + /// <summary> + /// Gets or sets the id. + /// </summary> + /// <value>The id.</value> + public string Id { get; set; } + + public string ServerId { get; set; } + + /// <summary> + /// Gets or sets the user id. + /// </summary> + /// <value>The user id.</value> + public string UserId { get; set; } + + /// <summary> + /// Gets or sets the user primary image tag. + /// </summary> + /// <value>The user primary image tag.</value> + public string UserPrimaryImageTag { get; set; } + + public string RemoteEndPoint { get; set; } + + /// <summary> + /// Gets or sets the name of the user. + /// </summary> + /// <value>The name of the user.</value> + public string UserName { get; set; } + + /// <summary> + /// Gets or sets the additional users present. + /// </summary> + /// <value>The additional users present.</value> + public SessionUserInfo[] AdditionalUsers { get; set; } + + /// <summary> + /// Gets or sets the application version. + /// </summary> + /// <value>The application version.</value> + public string ApplicationVersion { get; set; } + + /// <summary> + /// Gets or sets the type of the client. + /// </summary> + /// <value>The type of the client.</value> + public string Client { get; set; } + + /// <summary> + /// Gets or sets the last activity date. + /// </summary> + /// <value>The last activity date.</value> + public DateTime LastActivityDate { get; set; } + + /// <summary> + /// Gets or sets the name of the device. + /// </summary> + /// <value>The name of the device.</value> + public string DeviceName { get; set; } + + public string DeviceType { get; set; } + + /// <summary> + /// Gets or sets the now playing item. + /// </summary> + /// <value>The now playing item.</value> + public BaseItemDto NowPlayingItem { get; set; } + + /// <summary> + /// Gets or sets the device id. + /// </summary> + /// <value>The device id.</value> + public string DeviceId { get; set; } + + /// <summary> + /// Gets or sets the application icon URL. + /// </summary> + /// <value>The application icon URL.</value> + public string AppIconUrl { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [supports remote control]. + /// </summary> + /// <value><c>true</c> if [supports remote control]; otherwise, <c>false</c>.</value> + public bool SupportsRemoteControl { get; set; } + + public PlayerStateInfo PlayState { get; set; } + + public TranscodingInfo TranscodingInfo { get; set; } + + public SessionInfoDto() + { + AdditionalUsers = new SessionUserInfo[] { }; + + PlayableMediaTypes = new string[] {}; + SupportedCommands = new string[] {}; + } + } +} diff --git a/MediaBrowser.Model/Session/SessionUserInfo.cs b/MediaBrowser.Model/Session/SessionUserInfo.cs new file mode 100644 index 000000000..7746bc2d6 --- /dev/null +++ b/MediaBrowser.Model/Session/SessionUserInfo.cs @@ -0,0 +1,21 @@ +using System; + +namespace MediaBrowser.Model.Session +{ + /// <summary> + /// Class SessionUserInfo. + /// </summary> + public class SessionUserInfo + { + /// <summary> + /// Gets or sets the user identifier. + /// </summary> + /// <value>The user identifier.</value> + public Guid UserId { get; set; } + /// <summary> + /// Gets or sets the name of the user. + /// </summary> + /// <value>The name of the user.</value> + public string UserName { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Session/TranscodingInfo.cs b/MediaBrowser.Model/Session/TranscodingInfo.cs new file mode 100644 index 000000000..ed86d2358 --- /dev/null +++ b/MediaBrowser.Model/Session/TranscodingInfo.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Model.Session +{ + public class TranscodingInfo + { + public string AudioCodec { get; set; } + public string VideoCodec { get; set; } + public string Container { get; set; } + public bool IsVideoDirect { get; set; } + public bool IsAudioDirect { get; set; } + public int? Bitrate { get; set; } + + public float? Framerate { get; set; } + public double? CompletionPercentage { get; set; } + + public int? Width { get; set; } + public int? Height { get; set; } + public int? AudioChannels { get; set; } + + public TranscodeReason[] TranscodeReasons { get; set; } + + public TranscodingInfo() + { + TranscodeReasons = new TranscodeReason[] { }; + } + } + + public enum TranscodeReason + { + ContainerNotSupported = 0, + VideoCodecNotSupported = 1, + AudioCodecNotSupported = 2, + ContainerBitrateExceedsLimit = 3, + AudioBitrateNotSupported = 4, + AudioChannelsNotSupported = 5, + VideoResolutionNotSupported = 6, + UnknownVideoStreamInfo = 7, + UnknownAudioStreamInfo = 8, + AudioProfileNotSupported = 9, + AudioSampleRateNotSupported = 10, + AnamorphicVideoNotSupported = 11, + InterlacedVideoNotSupported = 12, + SecondaryAudioNotSupported = 13, + RefFramesNotSupported = 14, + VideoBitDepthNotSupported = 15, + VideoBitrateNotSupported = 16, + VideoFramerateNotSupported = 17, + VideoLevelNotSupported = 18, + VideoProfileNotSupported = 19, + AudioBitDepthNotSupported = 20, + SubtitleCodecNotSupported = 21, + DirectPlayError = 22 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Session/UserDataChangeInfo.cs b/MediaBrowser.Model/Session/UserDataChangeInfo.cs new file mode 100644 index 000000000..c6b03200d --- /dev/null +++ b/MediaBrowser.Model/Session/UserDataChangeInfo.cs @@ -0,0 +1,23 @@ +using MediaBrowser.Model.Dto; +using System.Collections.Generic; + +namespace MediaBrowser.Model.Session +{ + /// <summary> + /// Class UserDataChangeInfo + /// </summary> + public class UserDataChangeInfo + { + /// <summary> + /// Gets or sets the user id. + /// </summary> + /// <value>The user id.</value> + public string UserId { get; set; } + + /// <summary> + /// Gets or sets the user data list. + /// </summary> + /// <value>The user data list.</value> + public UserItemDataDto[] UserDataList { get; set; } + } +} diff --git a/MediaBrowser.Model/Sync/SyncCategory.cs b/MediaBrowser.Model/Sync/SyncCategory.cs new file mode 100644 index 000000000..e0d748685 --- /dev/null +++ b/MediaBrowser.Model/Sync/SyncCategory.cs @@ -0,0 +1,19 @@ + +namespace MediaBrowser.Model.Sync +{ + public enum SyncCategory + { + /// <summary> + /// The latest + /// </summary> + Latest = 0, + /// <summary> + /// The next up + /// </summary> + NextUp = 1, + /// <summary> + /// The resume + /// </summary> + Resume = 2 + } +} diff --git a/MediaBrowser.Model/Sync/SyncJob.cs b/MediaBrowser.Model/Sync/SyncJob.cs new file mode 100644 index 000000000..0cf9312a1 --- /dev/null +++ b/MediaBrowser.Model/Sync/SyncJob.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using MediaBrowser.Model.Sync; + +namespace MediaBrowser.Model.Sync +{ + public class SyncJob + { + /// <summary> + /// Gets or sets the identifier. + /// </summary> + /// <value>The identifier.</value> + public string Id { get; set; } + /// <summary> + /// Gets or sets the device identifier. + /// </summary> + /// <value>The device identifier.</value> + public string TargetId { get; set; } + /// <summary> + /// Gets or sets the name of the target. + /// </summary> + /// <value>The name of the target.</value> + public string TargetName { get; set; } + /// <summary> + /// Gets or sets the quality. + /// </summary> + /// <value>The quality.</value> + public string Quality { get; set; } + /// <summary> + /// Gets or sets the bitrate. + /// </summary> + /// <value>The bitrate.</value> + public int? Bitrate { get; set; } + /// <summary> + /// Gets or sets the profile. + /// </summary> + /// <value>The profile.</value> + public string Profile { get; set; } + /// <summary> + /// Gets or sets the category. + /// </summary> + /// <value>The category.</value> + public SyncCategory? Category { get; set; } + /// <summary> + /// Gets or sets the parent identifier. + /// </summary> + /// <value>The parent identifier.</value> + public string ParentId { get; set; } + /// <summary> + /// Gets or sets the current progress. + /// </summary> + /// <value>The current progress.</value> + public double? Progress { get; set; } + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + /// <summary> + /// Gets or sets the status. + /// </summary> + /// <value>The status.</value> + public SyncJobStatus Status { 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 [unwatched only]. + /// </summary> + /// <value><c>true</c> if [unwatched only]; otherwise, <c>false</c>.</value> + public bool UnwatchedOnly { get; set; } + /// <summary> + /// Gets or sets a value indicating whether [synchronize new content]. + /// </summary> + /// <value><c>true</c> if [synchronize new content]; otherwise, <c>false</c>.</value> + public bool SyncNewContent { get; set; } + /// <summary> + /// Gets or sets the item limit. + /// </summary> + /// <value>The item limit.</value> + public int? ItemLimit { get; set; } + /// <summary> + /// Gets or sets the requested item ids. + /// </summary> + /// <value>The requested item ids.</value> + public Guid[] RequestedItemIds { 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 last modified. + /// </summary> + /// <value>The date last modified.</value> + public DateTime DateLastModified { get; set; } + /// <summary> + /// Gets or sets the item count. + /// </summary> + /// <value>The item count.</value> + public int ItemCount { get; set; } + + public string ParentName { get; set; } + public string PrimaryImageItemId { get; set; } + public string PrimaryImageTag { get; set; } + + public SyncJob() + { + RequestedItemIds = new Guid[] { }; + } + } +} diff --git a/MediaBrowser.Model/Sync/SyncJobStatus.cs b/MediaBrowser.Model/Sync/SyncJobStatus.cs new file mode 100644 index 000000000..2d1d30802 --- /dev/null +++ b/MediaBrowser.Model/Sync/SyncJobStatus.cs @@ -0,0 +1,14 @@ + +namespace MediaBrowser.Model.Sync +{ + public enum SyncJobStatus + { + Queued = 0, + Converting = 1, + ReadyToTransfer = 2, + Transferring = 3, + Completed = 4, + CompletedWithError = 5, + Failed = 6 + } +} diff --git a/MediaBrowser.Model/Sync/SyncTarget.cs b/MediaBrowser.Model/Sync/SyncTarget.cs new file mode 100644 index 000000000..8901f0f27 --- /dev/null +++ b/MediaBrowser.Model/Sync/SyncTarget.cs @@ -0,0 +1,17 @@ + +namespace MediaBrowser.Model.Sync +{ + public class SyncTarget + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + /// <summary> + /// Gets or sets the identifier. + /// </summary> + /// <value>The identifier.</value> + public string Id { get; set; } + } +} diff --git a/MediaBrowser.Model/System/Architecture.cs b/MediaBrowser.Model/System/Architecture.cs new file mode 100644 index 000000000..73f78cd58 --- /dev/null +++ b/MediaBrowser.Model/System/Architecture.cs @@ -0,0 +1,10 @@ +namespace MediaBrowser.Model.System +{ + public enum Architecture + { + X86 = 0, + X64 = 1, + Arm = 2, + Arm64 = 3 + } +} diff --git a/MediaBrowser.Model/System/IEnvironmentInfo.cs b/MediaBrowser.Model/System/IEnvironmentInfo.cs new file mode 100644 index 000000000..8cf25a365 --- /dev/null +++ b/MediaBrowser.Model/System/IEnvironmentInfo.cs @@ -0,0 +1,24 @@ + +namespace MediaBrowser.Model.System +{ + public interface IEnvironmentInfo + { + MediaBrowser.Model.System.OperatingSystem OperatingSystem { get; } + string OperatingSystemName { get; } + string OperatingSystemVersion { get; } + Architecture SystemArchitecture { get; } + string GetEnvironmentVariable(string name); + void SetProcessEnvironmentVariable(string name, string value); + string StackTrace { get; } + char PathSeparator { get; } + } + + public enum OperatingSystem + { + Windows, + Linux, + OSX, + BSD, + Android + } +} diff --git a/MediaBrowser.Model/System/IPowerManagement.cs b/MediaBrowser.Model/System/IPowerManagement.cs new file mode 100644 index 000000000..03907568c --- /dev/null +++ b/MediaBrowser.Model/System/IPowerManagement.cs @@ -0,0 +1,11 @@ +using System; + +namespace MediaBrowser.Model.System +{ + public interface IPowerManagement + { + void PreventSystemStandby(); + void AllowSystemStandby(); + void ScheduleWake(DateTime wakeTimeUtc, string displayName); + } +} diff --git a/MediaBrowser.Model/System/ISystemEvents.cs b/MediaBrowser.Model/System/ISystemEvents.cs new file mode 100644 index 000000000..dec8ed8c0 --- /dev/null +++ b/MediaBrowser.Model/System/ISystemEvents.cs @@ -0,0 +1,12 @@ +using System; + +namespace MediaBrowser.Model.System +{ + public interface ISystemEvents + { + event EventHandler Resume; + event EventHandler Suspend; + event EventHandler SessionLogoff; + event EventHandler SystemShutdown; + } +} diff --git a/MediaBrowser.Model/System/LogFile.cs b/MediaBrowser.Model/System/LogFile.cs new file mode 100644 index 000000000..ba409c542 --- /dev/null +++ b/MediaBrowser.Model/System/LogFile.cs @@ -0,0 +1,31 @@ +using System; + +namespace MediaBrowser.Model.System +{ + public class LogFile + { + /// <summary> + /// Gets or sets the date created. + /// </summary> + /// <value>The date created.</value> + public DateTime DateCreated { get; set; } + + /// <summary> + /// Gets or sets the date modified. + /// </summary> + /// <value>The date modified.</value> + public DateTime DateModified { get; set; } + + /// <summary> + /// Gets or sets the size. + /// </summary> + /// <value>The size.</value> + public long Size { get; set; } + + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + } +} diff --git a/MediaBrowser.Model/System/PublicSystemInfo.cs b/MediaBrowser.Model/System/PublicSystemInfo.cs new file mode 100644 index 000000000..b9a3260b0 --- /dev/null +++ b/MediaBrowser.Model/System/PublicSystemInfo.cs @@ -0,0 +1,41 @@ +namespace MediaBrowser.Model.System +{ + public class PublicSystemInfo + { + /// <summary> + /// Gets or sets the local address. + /// </summary> + /// <value>The local address.</value> + public string LocalAddress { get; set; } + + /// <summary> + /// Gets or sets the wan address. + /// </summary> + /// <value>The wan address.</value> + public string WanAddress { get; set; } + + /// <summary> + /// Gets or sets the name of the server. + /// </summary> + /// <value>The name of the server.</value> + public string ServerName { get; set; } + + /// <summary> + /// Gets or sets the version. + /// </summary> + /// <value>The version.</value> + public string Version { get; set; } + + /// <summary> + /// Gets or sets the operating sytem. + /// </summary> + /// <value>The operating sytem.</value> + public string OperatingSystem { get; set; } + + /// <summary> + /// Gets or sets the id. + /// </summary> + /// <value>The id.</value> + public string Id { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs new file mode 100644 index 000000000..c790731c6 --- /dev/null +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -0,0 +1,140 @@ +using MediaBrowser.Model.Updates; +using System.Collections.Generic; +using System; + +namespace MediaBrowser.Model.System +{ + /// <summary> + /// Class SystemInfo + /// </summary> + public class SystemInfo : PublicSystemInfo + { + public PackageVersionClass SystemUpdateLevel { get; set; } + + /// <summary> + /// Gets or sets the display name of the operating system. + /// </summary> + /// <value>The display name of the operating system.</value> + public string OperatingSystemDisplayName { get; set; } + + public string PackageName { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance has pending restart. + /// </summary> + /// <value><c>true</c> if this instance has pending restart; otherwise, <c>false</c>.</value> + public bool HasPendingRestart { get; set; } + + public bool IsShuttingDown { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [supports library monitor]. + /// </summary> + /// <value><c>true</c> if [supports library monitor]; otherwise, <c>false</c>.</value> + public bool SupportsLibraryMonitor { get; set; } + + /// <summary> + /// Gets or sets the web socket port number. + /// </summary> + /// <value>The web socket port number.</value> + public int WebSocketPortNumber { get; set; } + + /// <summary> + /// Gets or sets the completed installations. + /// </summary> + /// <value>The completed installations.</value> + public InstallationInfo[] CompletedInstallations { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance can self restart. + /// </summary> + /// <value><c>true</c> if this instance can self restart; otherwise, <c>false</c>.</value> + public bool CanSelfRestart { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance can self update. + /// </summary> + /// <value><c>true</c> if this instance can self update; otherwise, <c>false</c>.</value> + public bool CanSelfUpdate { get; set; } + + public bool CanLaunchWebBrowser { get; set; } + + /// <summary> + /// Gets or sets the program data path. + /// </summary> + /// <value>The program data path.</value> + public string ProgramDataPath { get; set; } + + /// <summary> + /// Gets or sets the items by name path. + /// </summary> + /// <value>The items by name path.</value> + public string ItemsByNamePath { get; set; } + + /// <summary> + /// Gets or sets the cache path. + /// </summary> + /// <value>The cache path.</value> + public string CachePath { get; set; } + + /// <summary> + /// Gets or sets the log path. + /// </summary> + /// <value>The log path.</value> + public string LogPath { get; set; } + + /// <summary> + /// Gets or sets the internal metadata path. + /// </summary> + /// <value>The internal metadata path.</value> + public string InternalMetadataPath { get; set; } + + /// <summary> + /// Gets or sets the transcoding temporary path. + /// </summary> + /// <value>The transcoding temporary path.</value> + public string TranscodingTempPath { get; set; } + + /// <summary> + /// Gets or sets the HTTP server port number. + /// </summary> + /// <value>The HTTP server port number.</value> + public int HttpServerPortNumber { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [enable HTTPS]. + /// </summary> + /// <value><c>true</c> if [enable HTTPS]; otherwise, <c>false</c>.</value> + public bool SupportsHttps { get; set; } + + /// <summary> + /// Gets or sets the HTTPS server port number. + /// </summary> + /// <value>The HTTPS server port number.</value> + public int HttpsPortNumber { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance has update available. + /// </summary> + /// <value><c>true</c> if this instance has update available; otherwise, <c>false</c>.</value> + public bool HasUpdateAvailable { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [supports automatic run at startup]. + /// </summary> + /// <value><c>true</c> if [supports automatic run at startup]; otherwise, <c>false</c>.</value> + public bool SupportsAutoRunAtStartup { get; set; } + + public string EncoderLocationType { get; set; } + + public Architecture SystemArchitecture { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="SystemInfo" /> class. + /// </summary> + public SystemInfo() + { + CompletedInstallations = new InstallationInfo[] { }; + } + } +} diff --git a/MediaBrowser.Model/System/WakeOnLanInfo.cs b/MediaBrowser.Model/System/WakeOnLanInfo.cs new file mode 100644 index 000000000..cde867176 --- /dev/null +++ b/MediaBrowser.Model/System/WakeOnLanInfo.cs @@ -0,0 +1,13 @@ +namespace MediaBrowser.Model.System +{ + public class WakeOnLanInfo + { + public string MacAddress { get; set; } + public int Port { get; set; } + + public WakeOnLanInfo() + { + Port = 9; + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs b/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs new file mode 100644 index 000000000..ed981a905 --- /dev/null +++ b/MediaBrowser.Model/Tasks/IConfigurableScheduledTask.cs @@ -0,0 +1,18 @@ +namespace MediaBrowser.Model.Tasks +{ + public interface IConfigurableScheduledTask + { + /// <summary> + /// Gets a value indicating whether this instance is hidden. + /// </summary> + /// <value><c>true</c> if this instance is hidden; otherwise, <c>false</c>.</value> + bool IsHidden { get; } + /// <summary> + /// Gets a value indicating whether this instance is enabled. + /// </summary> + /// <value><c>true</c> if this instance is enabled; otherwise, <c>false</c>.</value> + bool IsEnabled { get; } + + bool IsLogged { get; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Tasks/IScheduledTask.cs b/MediaBrowser.Model/Tasks/IScheduledTask.cs new file mode 100644 index 000000000..81ba239ad --- /dev/null +++ b/MediaBrowser.Model/Tasks/IScheduledTask.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Model.Tasks +{ + /// <summary> + /// Interface IScheduledTaskWorker + /// </summary> + public interface IScheduledTask + { + /// <summary> + /// Gets the name of the task + /// </summary> + /// <value>The name.</value> + string Name { get; } + + string Key { get; } + + /// <summary> + /// Gets the description. + /// </summary> + /// <value>The description.</value> + string Description { get; } + + /// <summary> + /// Gets the category. + /// </summary> + /// <value>The category.</value> + string Category { get; } + + /// <summary> + /// Executes the task + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <param name="progress">The progress.</param> + /// <returns>Task.</returns> + Task Execute(CancellationToken cancellationToken, IProgress<double> progress); + + /// <summary> + /// Gets the default triggers. + /// </summary> + /// <returns>IEnumerable{BaseTaskTrigger}.</returns> + IEnumerable<TaskTriggerInfo> GetDefaultTriggers(); + } +} diff --git a/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs b/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs new file mode 100644 index 000000000..415207f8f --- /dev/null +++ b/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs @@ -0,0 +1,76 @@ +using System; +using MediaBrowser.Model.Events; + +namespace MediaBrowser.Model.Tasks +{ + /// <summary> + /// Interface IScheduledTaskWorker + /// </summary> + public interface IScheduledTaskWorker : IDisposable + { + /// <summary> + /// Occurs when [task progress]. + /// </summary> + event EventHandler<GenericEventArgs<double>> TaskProgress; + + /// <summary> + /// Gets or sets the scheduled task. + /// </summary> + /// <value>The scheduled task.</value> + IScheduledTask ScheduledTask { get; } + + /// <summary> + /// Gets the last execution result. + /// </summary> + /// <value>The last execution result.</value> + TaskResult LastExecutionResult { get; } + + /// <summary> + /// Gets the name. + /// </summary> + /// <value>The name.</value> + string Name { get; } + + /// <summary> + /// Gets the description. + /// </summary> + /// <value>The description.</value> + string Description { get; } + + /// <summary> + /// Gets the category. + /// </summary> + /// <value>The category.</value> + string Category { get; } + + /// <summary> + /// Gets the state. + /// </summary> + /// <value>The state.</value> + TaskState State { get; } + + /// <summary> + /// Gets the current progress. + /// </summary> + /// <value>The current progress.</value> + double? CurrentProgress { get; } + + /// <summary> + /// Gets the triggers that define when the task will run + /// </summary> + /// <value>The triggers.</value> + /// <exception cref="ArgumentNullException">value</exception> + TaskTriggerInfo[] Triggers { get; set; } + + /// <summary> + /// Gets the unique id. + /// </summary> + /// <value>The unique id.</value> + string Id { get; } + + /// <summary> + /// Reloads the trigger events. + /// </summary> + void ReloadTriggerEvents(); + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Tasks/ITaskManager.cs b/MediaBrowser.Model/Tasks/ITaskManager.cs new file mode 100644 index 000000000..cbc18032c --- /dev/null +++ b/MediaBrowser.Model/Tasks/ITaskManager.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using MediaBrowser.Model.Events; + +namespace MediaBrowser.Model.Tasks +{ + public interface ITaskManager : IDisposable + { + /// <summary> + /// Gets the list of Scheduled Tasks + /// </summary> + /// <value>The scheduled tasks.</value> + IScheduledTaskWorker[] ScheduledTasks { get; } + + /// <summary> + /// Cancels if running and queue. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="options">Task options.</param> + void CancelIfRunningAndQueue<T>(TaskOptions options) + where T : IScheduledTask; + + /// <summary> + /// Cancels if running and queue. + /// </summary> + /// <typeparam name="T"></typeparam> + void CancelIfRunningAndQueue<T>() + where T : IScheduledTask; + + /// <summary> + /// Cancels if running. + /// </summary> + /// <typeparam name="T"></typeparam> + void CancelIfRunning<T>() + where T : IScheduledTask; + + /// <summary> + /// Queues the scheduled task. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="options">Task options.</param> + void QueueScheduledTask<T>(TaskOptions options) + where T : IScheduledTask; + + /// <summary> + /// Queues the scheduled task. + /// </summary> + /// <typeparam name="T"></typeparam> + void QueueScheduledTask<T>() + where T : IScheduledTask; + + void QueueIfNotRunning<T>() + where T : IScheduledTask; + + /// <summary> + /// Queues the scheduled task. + /// </summary> + void QueueScheduledTask(IScheduledTask task, TaskOptions options); + + /// <summary> + /// Adds the tasks. + /// </summary> + /// <param name="tasks">The tasks.</param> + void AddTasks(IEnumerable<IScheduledTask> tasks); + + void Cancel(IScheduledTaskWorker task); + Task Execute(IScheduledTaskWorker task, TaskOptions options); + + void Execute<T>() + where T : IScheduledTask; + + event EventHandler<GenericEventArgs<IScheduledTaskWorker>> TaskExecuting; + event EventHandler<TaskCompletionEventArgs> TaskCompleted; + + void RunTaskOnNextStartup(string key); + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Tasks/ITaskTrigger.cs b/MediaBrowser.Model/Tasks/ITaskTrigger.cs new file mode 100644 index 000000000..7c804348a --- /dev/null +++ b/MediaBrowser.Model/Tasks/ITaskTrigger.cs @@ -0,0 +1,32 @@ +using System; +using MediaBrowser.Model.Events; +using MediaBrowser.Model.Logging; + +namespace MediaBrowser.Model.Tasks +{ + /// <summary> + /// Interface ITaskTrigger + /// </summary> + public interface ITaskTrigger + { + /// <summary> + /// Fires when the trigger condition is satisfied and the task should run + /// </summary> + event EventHandler<EventArgs> Triggered; + + /// <summary> + /// Gets or sets the options of this task. + /// </summary> + TaskOptions TaskOptions { get; set; } + + /// <summary> + /// Stars waiting for the trigger action + /// </summary> + void Start(TaskResult lastResult, ILogger logger, string taskName, bool isApplicationStartup); + + /// <summary> + /// Stops waiting for the trigger action + /// </summary> + void Stop(); + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs b/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs new file mode 100644 index 000000000..2dec79e93 --- /dev/null +++ b/MediaBrowser.Model/Tasks/ScheduledTaskHelpers.cs @@ -0,0 +1,44 @@ + +namespace MediaBrowser.Model.Tasks +{ + /// <summary> + /// Class ScheduledTaskHelpers + /// </summary> + public static class ScheduledTaskHelpers + { + /// <summary> + /// Gets the task info. + /// </summary> + /// <param name="task">The task.</param> + /// <returns>TaskInfo.</returns> + public static TaskInfo GetTaskInfo(IScheduledTaskWorker task) + { + var isHidden = false; + + var configurableTask = task.ScheduledTask as IConfigurableScheduledTask; + + if (configurableTask != null) + { + isHidden = configurableTask.IsHidden; + } + + string key = task.ScheduledTask.Key; + + return new TaskInfo + { + Name = task.Name, + CurrentProgressPercentage = task.CurrentProgress, + State = task.State, + Id = task.Id, + LastExecutionResult = task.LastExecutionResult, + + Triggers = task.Triggers, + + Description = task.Description, + Category = task.Category, + IsHidden = isHidden, + Key = key + }; + } + } +} diff --git a/MediaBrowser.Model/Tasks/SystemEvent.cs b/MediaBrowser.Model/Tasks/SystemEvent.cs new file mode 100644 index 000000000..4d49a38cc --- /dev/null +++ b/MediaBrowser.Model/Tasks/SystemEvent.cs @@ -0,0 +1,14 @@ + +namespace MediaBrowser.Model.Tasks +{ + /// <summary> + /// Enum SystemEvent + /// </summary> + public enum SystemEvent + { + /// <summary> + /// The wake from sleep + /// </summary> + WakeFromSleep = 0 + } +} diff --git a/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs b/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs new file mode 100644 index 000000000..be9eaa613 --- /dev/null +++ b/MediaBrowser.Model/Tasks/TaskCompletionEventArgs.cs @@ -0,0 +1,11 @@ +using System; + +namespace MediaBrowser.Model.Tasks +{ + public class TaskCompletionEventArgs : EventArgs + { + public IScheduledTaskWorker Task { get; set; } + + public TaskResult Result { get; set; } + } +} diff --git a/MediaBrowser.Model/Tasks/TaskCompletionStatus.cs b/MediaBrowser.Model/Tasks/TaskCompletionStatus.cs new file mode 100644 index 000000000..6ba5ba5e4 --- /dev/null +++ b/MediaBrowser.Model/Tasks/TaskCompletionStatus.cs @@ -0,0 +1,29 @@ + +namespace MediaBrowser.Model.Tasks +{ + /// <summary> + /// Enum TaskCompletionStatus + /// </summary> + public enum TaskCompletionStatus + { + /// <summary> + /// The completed + /// </summary> + Completed, + + /// <summary> + /// The failed + /// </summary> + Failed, + + /// <summary> + /// Manually cancelled by the user + /// </summary> + Cancelled, + + /// <summary> + /// Aborted due to a system failure or shutdown + /// </summary> + Aborted + } +} diff --git a/MediaBrowser.Model/Tasks/TaskInfo.cs b/MediaBrowser.Model/Tasks/TaskInfo.cs new file mode 100644 index 000000000..8792ce952 --- /dev/null +++ b/MediaBrowser.Model/Tasks/TaskInfo.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Model.Tasks +{ + /// <summary> + /// Class TaskInfo + /// </summary> + public class TaskInfo + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the state of the task. + /// </summary> + /// <value>The state of the task.</value> + public TaskState State { get; set; } + + /// <summary> + /// Gets or sets the progress. + /// </summary> + /// <value>The progress.</value> + public double? CurrentProgressPercentage { get; set; } + + /// <summary> + /// Gets or sets the id. + /// </summary> + /// <value>The id.</value> + public string Id { get; set; } + + /// <summary> + /// Gets or sets the last execution result. + /// </summary> + /// <value>The last execution result.</value> + public TaskResult LastExecutionResult { get; set; } + + /// <summary> + /// Gets or sets the triggers. + /// </summary> + /// <value>The triggers.</value> + public TaskTriggerInfo[] Triggers { get; set; } + + /// <summary> + /// Gets or sets the description. + /// </summary> + /// <value>The description.</value> + public string Description { get; set; } + + /// <summary> + /// Gets or sets the category. + /// </summary> + /// <value>The category.</value> + public string Category { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is hidden. + /// </summary> + /// <value><c>true</c> if this instance is hidden; otherwise, <c>false</c>.</value> + public bool IsHidden { get; set; } + + /// <summary> + /// Gets or sets the key. + /// </summary> + /// <value>The key.</value> + public string Key { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="TaskInfo"/> class. + /// </summary> + public TaskInfo() + { + Triggers = new TaskTriggerInfo[]{}; + } + } +} diff --git a/MediaBrowser.Model/Tasks/TaskOptions.cs b/MediaBrowser.Model/Tasks/TaskOptions.cs new file mode 100644 index 000000000..caca154a9 --- /dev/null +++ b/MediaBrowser.Model/Tasks/TaskOptions.cs @@ -0,0 +1,8 @@ + +namespace MediaBrowser.Model.Tasks +{ + public class TaskOptions + { + public long? MaxRuntimeTicks { get; set; } + } +} diff --git a/MediaBrowser.Model/Tasks/TaskResult.cs b/MediaBrowser.Model/Tasks/TaskResult.cs new file mode 100644 index 000000000..39eacdf66 --- /dev/null +++ b/MediaBrowser.Model/Tasks/TaskResult.cs @@ -0,0 +1,58 @@ +using System; + +namespace MediaBrowser.Model.Tasks +{ + /// <summary> + /// Class TaskExecutionInfo + /// </summary> + public class TaskResult + { + /// <summary> + /// Gets or sets the start time UTC. + /// </summary> + /// <value>The start time UTC.</value> + public DateTime StartTimeUtc { get; set; } + + /// <summary> + /// Gets or sets the end time UTC. + /// </summary> + /// <value>The end time UTC.</value> + public DateTime EndTimeUtc { get; set; } + + /// <summary> + /// Gets or sets the status. + /// </summary> + /// <value>The status.</value> + public TaskCompletionStatus Status { get; set; } + + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the key. + /// </summary> + /// <value>The key.</value> + public string Key { get; set; } + + /// <summary> + /// Gets or sets the id. + /// </summary> + /// <value>The id.</value> + public string Id { get; set; } + + /// <summary> + /// Gets or sets the error message. + /// </summary> + /// <value>The error message.</value> + public string ErrorMessage { get; set; } + + /// <summary> + /// Gets or sets the long error message. + /// </summary> + /// <value>The long error message.</value> + public string LongErrorMessage { get; set; } + } +} diff --git a/MediaBrowser.Model/Tasks/TaskState.cs b/MediaBrowser.Model/Tasks/TaskState.cs new file mode 100644 index 000000000..889ce6875 --- /dev/null +++ b/MediaBrowser.Model/Tasks/TaskState.cs @@ -0,0 +1,22 @@ + +namespace MediaBrowser.Model.Tasks +{ + /// <summary> + /// Enum TaskState + /// </summary> + public enum TaskState + { + /// <summary> + /// The idle + /// </summary> + Idle, + /// <summary> + /// The cancelling + /// </summary> + Cancelling, + /// <summary> + /// The running + /// </summary> + Running + } +} diff --git a/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs b/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs new file mode 100644 index 000000000..901a300d0 --- /dev/null +++ b/MediaBrowser.Model/Tasks/TaskTriggerInfo.cs @@ -0,0 +1,52 @@ +using System; + +namespace MediaBrowser.Model.Tasks +{ + /// <summary> + /// Class TaskTriggerInfo + /// </summary> + public class TaskTriggerInfo + { + /// <summary> + /// Gets or sets the type. + /// </summary> + /// <value>The type.</value> + public string Type { get; set; } + + /// <summary> + /// Gets or sets the time of day. + /// </summary> + /// <value>The time of day.</value> + public long? TimeOfDayTicks { get; set; } + + /// <summary> + /// Gets or sets the interval. + /// </summary> + /// <value>The interval.</value> + public long? IntervalTicks { get; set; } + + /// <summary> + /// Gets or sets the system event. + /// </summary> + /// <value>The system event.</value> + public SystemEvent? SystemEvent { get; set; } + + /// <summary> + /// Gets or sets the day of week. + /// </summary> + /// <value>The day of week.</value> + public DayOfWeek? DayOfWeek { get; set; } + + /// <summary> + /// Gets or sets the maximum runtime ticks. + /// </summary> + /// <value>The maximum runtime ticks.</value> + public long? MaxRuntimeTicks { get; set; } + + public const string TriggerDaily = "DailyTrigger"; + public const string TriggerWeekly = "WeeklyTrigger"; + public const string TriggerInterval = "IntervalTrigger"; + public const string TriggerSystemEvent = "SystemEventTrigger"; + public const string TriggerStartup = "StartupTrigger"; + } +} diff --git a/MediaBrowser.Model/Text/ITextEncoding.cs b/MediaBrowser.Model/Text/ITextEncoding.cs new file mode 100644 index 000000000..619d90a2b --- /dev/null +++ b/MediaBrowser.Model/Text/ITextEncoding.cs @@ -0,0 +1,14 @@ +using System.IO; +using System.Text; + +namespace MediaBrowser.Model.Text +{ + public interface ITextEncoding + { + Encoding GetASCIIEncoding(); + + string GetDetectedEncodingName(byte[] bytes, int size, string language, bool enableLanguageDetection); + Encoding GetDetectedEncoding(byte[] bytes, int size, string language, bool enableLanguageDetection); + Encoding GetEncodingFromCharset(string charset); + } +} diff --git a/MediaBrowser.Model/Threading/ITimer.cs b/MediaBrowser.Model/Threading/ITimer.cs new file mode 100644 index 000000000..42090250b --- /dev/null +++ b/MediaBrowser.Model/Threading/ITimer.cs @@ -0,0 +1,10 @@ +using System; + +namespace MediaBrowser.Model.Threading +{ + public interface ITimer : IDisposable + { + void Change(TimeSpan dueTime, TimeSpan period); + void Change(int dueTimeMs, int periodMs); + } +} diff --git a/MediaBrowser.Model/Threading/ITimerFactory.cs b/MediaBrowser.Model/Threading/ITimerFactory.cs new file mode 100644 index 000000000..5f3df1738 --- /dev/null +++ b/MediaBrowser.Model/Threading/ITimerFactory.cs @@ -0,0 +1,10 @@ +using System; + +namespace MediaBrowser.Model.Threading +{ + public interface ITimerFactory + { + ITimer Create(Action<object> callback, object state, TimeSpan dueTime, TimeSpan period); + ITimer Create(Action<object> callback, object state, int dueTimeMs, int periodMs); + } +} diff --git a/MediaBrowser.Model/Updates/CheckForUpdateResult.cs b/MediaBrowser.Model/Updates/CheckForUpdateResult.cs new file mode 100644 index 000000000..ff0bba197 --- /dev/null +++ b/MediaBrowser.Model/Updates/CheckForUpdateResult.cs @@ -0,0 +1,30 @@ + +namespace MediaBrowser.Model.Updates +{ + /// <summary> + /// Class CheckForUpdateResult + /// </summary> + public class CheckForUpdateResult + { + /// <summary> + /// Gets or sets a value indicating whether this instance is update available. + /// </summary> + /// <value><c>true</c> if this instance is update available; otherwise, <c>false</c>.</value> + public bool IsUpdateAvailable { get; set; } + + /// <summary> + /// Gets or sets the available version. + /// </summary> + /// <value>The available version.</value> + public string AvailableVersion + { + get { return Package != null ? Package.versionStr : "0.0.0.1"; } + set { } // need this for the serializer + } + + /// <summary> + /// Get or sets package information for an available update + /// </summary> + public PackageVersionInfo Package { get; set; } + } +} diff --git a/MediaBrowser.Model/Updates/InstallationInfo.cs b/MediaBrowser.Model/Updates/InstallationInfo.cs new file mode 100644 index 000000000..09b4975a8 --- /dev/null +++ b/MediaBrowser.Model/Updates/InstallationInfo.cs @@ -0,0 +1,46 @@ +using System; + +namespace MediaBrowser.Model.Updates +{ + /// <summary> + /// Class InstallationInfo + /// </summary> + public class InstallationInfo + { + /// <summary> + /// Gets or sets the id. + /// </summary> + /// <value>The id.</value> + public Guid Id { get; set; } + + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the assembly guid. + /// </summary> + /// <value>The guid of the assembly.</value> + public string AssemblyGuid { get; set; } + + /// <summary> + /// Gets or sets the version. + /// </summary> + /// <value>The version.</value> + public string Version { get; set; } + + /// <summary> + /// Gets or sets the update class. + /// </summary> + /// <value>The update class.</value> + public PackageVersionClass UpdateClass { get; set; } + + /// <summary> + /// Gets or sets the percent complete. + /// </summary> + /// <value>The percent complete.</value> + public double? PercentComplete { get; set; } + } +} diff --git a/MediaBrowser.Model/Updates/PackageInfo.cs b/MediaBrowser.Model/Updates/PackageInfo.cs new file mode 100644 index 000000000..e46d59fc0 --- /dev/null +++ b/MediaBrowser.Model/Updates/PackageInfo.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Model.Updates +{ + /// <summary> + /// Class PackageInfo + /// </summary> + public class PackageInfo + { + /// <summary> + /// The internal id of this package. + /// </summary> + /// <value>The id.</value> + public string id { get; set; } + + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string name { get; set; } + + /// <summary> + /// Gets or sets the short description. + /// </summary> + /// <value>The short description.</value> + public string shortDescription { get; set; } + + /// <summary> + /// Gets or sets the overview. + /// </summary> + /// <value>The overview.</value> + public string overview { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is premium. + /// </summary> + /// <value><c>true</c> if this instance is premium; otherwise, <c>false</c>.</value> + public bool isPremium { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is adult only content. + /// </summary> + /// <value><c>true</c> if this instance is adult; otherwise, <c>false</c>.</value> + public bool adult { get; set; } + + /// <summary> + /// Gets or sets the rich desc URL. + /// </summary> + /// <value>The rich desc URL.</value> + public string richDescUrl { get; set; } + + /// <summary> + /// Gets or sets the thumb image. + /// </summary> + /// <value>The thumb image.</value> + public string thumbImage { get; set; } + + /// <summary> + /// Gets or sets the preview image. + /// </summary> + /// <value>The preview image.</value> + public string previewImage { get; set; } + + /// <summary> + /// Gets or sets the type. + /// </summary> + /// <value>The type.</value> + public string type { get; set; } + + /// <summary> + /// Gets or sets the target filename. + /// </summary> + /// <value>The target filename.</value> + public string targetFilename { get; set; } + + /// <summary> + /// Gets or sets the owner. + /// </summary> + /// <value>The owner.</value> + public string owner { get; set; } + + /// <summary> + /// Gets or sets the category. + /// </summary> + /// <value>The category.</value> + public string category { get; set; } + + /// <summary> + /// Gets or sets the catalog tile color. + /// </summary> + /// <value>The owner.</value> + public string tileColor { get; set; } + + /// <summary> + /// Gets or sets the feature id of this package (if premium). + /// </summary> + /// <value>The feature id.</value> + public string featureId { get; set; } + + /// <summary> + /// Gets or sets the registration info for this package (if premium). + /// </summary> + /// <value>The registration info.</value> + public string regInfo { get; set; } + + /// <summary> + /// Gets or sets the price for this package (if premium). + /// </summary> + /// <value>The price.</value> + public float price { get; set; } + + /// <summary> + /// Gets or sets the target system for this plug-in (Server, MBTheater, MBClassic). + /// </summary> + /// <value>The target system.</value> + public PackageTargetSystem targetSystem { get; set; } + + /// <summary> + /// The guid of the assembly associated with this package (if a plug-in). + /// This is used to identify the proper item for automatic updates. + /// </summary> + /// <value>The name.</value> + public string guid { get; set; } + + /// <summary> + /// Gets or sets the total number of ratings for this package. + /// </summary> + /// <value>The total ratings.</value> + public int? totalRatings { get; set; } + + /// <summary> + /// Gets or sets the average rating for this package . + /// </summary> + /// <value>The rating.</value> + public float avgRating { get; set; } + + /// <summary> + /// Gets or sets whether or not this package is registered. + /// </summary> + /// <value>True if registered.</value> + public bool isRegistered { get; set; } + + /// <summary> + /// Gets or sets the expiration date for this package. + /// </summary> + /// <value>Expiration Date.</value> + public DateTime expDate { get; set; } + + /// <summary> + /// Gets or sets the versions. + /// </summary> + /// <value>The versions.</value> + public PackageVersionInfo[] versions { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [enable in application store]. + /// </summary> + /// <value><c>true</c> if [enable in application store]; otherwise, <c>false</c>.</value> + public bool enableInAppStore { get; set; } + + /// <summary> + /// Gets or sets the installs. + /// </summary> + /// <value>The installs.</value> + public int installs { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="PackageInfo"/> class. + /// </summary> + public PackageInfo() + { + versions = new PackageVersionInfo[] { }; + } + } +} diff --git a/MediaBrowser.Model/Updates/PackageTargetSystem.cs b/MediaBrowser.Model/Updates/PackageTargetSystem.cs new file mode 100644 index 000000000..c80dddde3 --- /dev/null +++ b/MediaBrowser.Model/Updates/PackageTargetSystem.cs @@ -0,0 +1,21 @@ +namespace MediaBrowser.Model.Updates +{ + /// <summary> + /// Enum PackageType + /// </summary> + public enum PackageTargetSystem + { + /// <summary> + /// Server + /// </summary> + Server, + /// <summary> + /// MB Theater + /// </summary> + MBTheater, + /// <summary> + /// MB Classic + /// </summary> + MBClassic + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Updates/PackageVersionClass.cs b/MediaBrowser.Model/Updates/PackageVersionClass.cs new file mode 100644 index 000000000..3f51e1b3c --- /dev/null +++ b/MediaBrowser.Model/Updates/PackageVersionClass.cs @@ -0,0 +1,21 @@ +namespace MediaBrowser.Model.Updates +{ + /// <summary> + /// Enum PackageVersionClass + /// </summary> + public enum PackageVersionClass + { + /// <summary> + /// The release + /// </summary> + Release = 0, + /// <summary> + /// The beta + /// </summary> + Beta = 1, + /// <summary> + /// The dev + /// </summary> + Dev = 2 + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Updates/PackageVersionInfo.cs b/MediaBrowser.Model/Updates/PackageVersionInfo.cs new file mode 100644 index 000000000..3ac518187 --- /dev/null +++ b/MediaBrowser.Model/Updates/PackageVersionInfo.cs @@ -0,0 +1,95 @@ +using System; +using MediaBrowser.Model.Serialization; + +namespace MediaBrowser.Model.Updates +{ + /// <summary> + /// Class PackageVersionInfo + /// </summary> + public class PackageVersionInfo + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string name { get; set; } + + /// <summary> + /// Gets or sets the guid. + /// </summary> + /// <value>The guid.</value> + public string guid { get; set; } + + /// <summary> + /// Gets or sets the version STR. + /// </summary> + /// <value>The version STR.</value> + public string versionStr { get; set; } + + /// <summary> + /// The _version + /// </summary> + private Version _version; + /// <summary> + /// Gets or sets the version. + /// Had to make this an interpreted property since Protobuf can't handle Version + /// </summary> + /// <value>The version.</value> + [IgnoreDataMember] + public Version version + { + get { return _version ?? (_version = new Version(ValueOrDefault(versionStr, "0.0.0.1"))); } + } + + /// <summary> + /// Values the or default. + /// </summary> + /// <param name="str">The STR.</param> + /// <param name="def">The def.</param> + /// <returns>System.String.</returns> + private static string ValueOrDefault(string str, string def) + { + return string.IsNullOrEmpty(str) ? def : str; + } + + /// <summary> + /// Gets or sets the classification. + /// </summary> + /// <value>The classification.</value> + public PackageVersionClass classification { get; set; } + + /// <summary> + /// Gets or sets the description. + /// </summary> + /// <value>The description.</value> + public string description { get; set; } + + /// <summary> + /// Gets or sets the required version STR. + /// </summary> + /// <value>The required version STR.</value> + public string requiredVersionStr { get; set; } + + /// <summary> + /// Gets or sets the source URL. + /// </summary> + /// <value>The source URL.</value> + public string sourceUrl { get; set; } + + /// <summary> + /// Gets or sets the source URL. + /// </summary> + /// <value>The source URL.</value> + public string checksum { get; set; } + + /// <summary> + /// Gets or sets the target filename. + /// </summary> + /// <value>The target filename.</value> + public string targetFilename { get; set; } + + public string infoUrl { get; set; } + + public string runtimes { get; set; } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Users/ForgotPasswordAction.cs b/MediaBrowser.Model/Users/ForgotPasswordAction.cs new file mode 100644 index 000000000..f75b1d74b --- /dev/null +++ b/MediaBrowser.Model/Users/ForgotPasswordAction.cs @@ -0,0 +1,10 @@ + +namespace MediaBrowser.Model.Users +{ + public enum ForgotPasswordAction + { + ContactAdmin = 0, + PinCode = 1, + InNetworkRequired = 2 + } +} diff --git a/MediaBrowser.Model/Users/ForgotPasswordResult.cs b/MediaBrowser.Model/Users/ForgotPasswordResult.cs new file mode 100644 index 000000000..7dbb1e96b --- /dev/null +++ b/MediaBrowser.Model/Users/ForgotPasswordResult.cs @@ -0,0 +1,23 @@ +using System; + +namespace MediaBrowser.Model.Users +{ + public class ForgotPasswordResult + { + /// <summary> + /// Gets or sets the action. + /// </summary> + /// <value>The action.</value> + public ForgotPasswordAction Action { get; set; } + /// <summary> + /// Gets or sets the pin file. + /// </summary> + /// <value>The pin file.</value> + public string PinFile { get; set; } + /// <summary> + /// Gets or sets the pin expiration date. + /// </summary> + /// <value>The pin expiration date.</value> + public DateTime? PinExpirationDate { get; set; } + } +} diff --git a/MediaBrowser.Model/Users/PinRedeemResult.cs b/MediaBrowser.Model/Users/PinRedeemResult.cs new file mode 100644 index 000000000..6a01bf2d4 --- /dev/null +++ b/MediaBrowser.Model/Users/PinRedeemResult.cs @@ -0,0 +1,17 @@ + +namespace MediaBrowser.Model.Users +{ + public class PinRedeemResult + { + /// <summary> + /// Gets or sets a value indicating whether this <see cref="PinRedeemResult"/> is success. + /// </summary> + /// <value><c>true</c> if success; otherwise, <c>false</c>.</value> + public bool Success { get; set; } + /// <summary> + /// Gets or sets the users reset. + /// </summary> + /// <value>The users reset.</value> + public string[] UsersReset { get; set; } + } +} diff --git a/MediaBrowser.Model/Users/UserAction.cs b/MediaBrowser.Model/Users/UserAction.cs new file mode 100644 index 000000000..5f401b9f0 --- /dev/null +++ b/MediaBrowser.Model/Users/UserAction.cs @@ -0,0 +1,15 @@ +using System; + +namespace MediaBrowser.Model.Users +{ + public class UserAction + { + public string Id { get; set; } + public string ServerId { get; set; } + public Guid UserId { get; set; } + public Guid ItemId { get; set; } + public UserActionType Type { get; set; } + public DateTime Date { get; set; } + public long? PositionTicks { get; set; } + } +} diff --git a/MediaBrowser.Model/Users/UserActionType.cs b/MediaBrowser.Model/Users/UserActionType.cs new file mode 100644 index 000000000..493de6272 --- /dev/null +++ b/MediaBrowser.Model/Users/UserActionType.cs @@ -0,0 +1,8 @@ + +namespace MediaBrowser.Model.Users +{ + public enum UserActionType + { + PlayedItem = 0 + } +} diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs new file mode 100644 index 000000000..8bddafb5a --- /dev/null +++ b/MediaBrowser.Model/Users/UserPolicy.cs @@ -0,0 +1,121 @@ +using MediaBrowser.Model.Configuration; +using System; + +namespace MediaBrowser.Model.Users +{ + public class UserPolicy + { + /// <summary> + /// Gets or sets a value indicating whether this instance is administrator. + /// </summary> + /// <value><c>true</c> if this instance is administrator; otherwise, <c>false</c>.</value> + public bool IsAdministrator { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is hidden. + /// </summary> + /// <value><c>true</c> if this instance is hidden; otherwise, <c>false</c>.</value> + public bool IsHidden { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether this instance is disabled. + /// </summary> + /// <value><c>true</c> if this instance is disabled; otherwise, <c>false</c>.</value> + public bool IsDisabled { get; set; } + + /// <summary> + /// Gets or sets the max parental rating. + /// </summary> + /// <value>The max parental rating.</value> + public int? MaxParentalRating { get; set; } + + public string[] BlockedTags { get; set; } + public bool EnableUserPreferenceAccess { get; set; } + public AccessSchedule[] AccessSchedules { get; set; } + public UnratedItem[] BlockUnratedItems { get; set; } + public bool EnableRemoteControlOfOtherUsers { get; set; } + public bool EnableSharedDeviceControl { get; set; } + public bool EnableRemoteAccess { get; set; } + + public bool EnableLiveTvManagement { get; set; } + public bool EnableLiveTvAccess { get; set; } + + public bool EnableMediaPlayback { get; set; } + public bool EnableAudioPlaybackTranscoding { get; set; } + public bool EnableVideoPlaybackTranscoding { get; set; } + public bool EnablePlaybackRemuxing { get; set; } + + public bool EnableContentDeletion { get; set; } + public string[] EnableContentDeletionFromFolders { get; set; } + public bool EnableContentDownloading { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [enable synchronize]. + /// </summary> + /// <value><c>true</c> if [enable synchronize]; otherwise, <c>false</c>.</value> + public bool EnableSyncTranscoding { get; set; } + public bool EnableMediaConversion { get; set; } + + public string[] EnabledDevices { get; set; } + public bool EnableAllDevices { get; set; } + + public string[] EnabledChannels { get; set; } + public bool EnableAllChannels { get; set; } + + public string[] EnabledFolders { get; set; } + public bool EnableAllFolders { get; set; } + + public int InvalidLoginAttemptCount { get; set; } + + public bool EnablePublicSharing { get; set; } + + public string[] BlockedMediaFolders { get; set; } + public string[] BlockedChannels { get; set; } + + public int RemoteClientBitrateLimit { get; set; } + public string AuthenticationProviderId { get; set; } + + public UserPolicy() + { + EnableContentDeletion = true; + EnableContentDeletionFromFolders = new string[] { }; + + EnableSyncTranscoding = true; + EnableMediaConversion = true; + + EnableMediaPlayback = true; + EnableAudioPlaybackTranscoding = true; + EnableVideoPlaybackTranscoding = true; + EnablePlaybackRemuxing = true; + + EnableLiveTvManagement = true; + EnableLiveTvAccess = true; + + // Without this on by default, admins won't be able to do this + // Improve in the future + EnableLiveTvManagement = true; + + EnableSharedDeviceControl = true; + + BlockedTags = new string[] { }; + BlockUnratedItems = new UnratedItem[] { }; + + EnableUserPreferenceAccess = true; + + AccessSchedules = new AccessSchedule[] { }; + + EnableAllChannels = true; + EnabledChannels = new string[] { }; + + EnableAllFolders = true; + EnabledFolders = new string[] { }; + + EnabledDevices = new string[] { }; + EnableAllDevices = true; + + EnableContentDownloading = true; + EnablePublicSharing = true; + EnableRemoteAccess = true; + } + } +}
\ No newline at end of file diff --git a/MediaBrowser.Model/Xml/IXmlReaderSettingsFactory.cs b/MediaBrowser.Model/Xml/IXmlReaderSettingsFactory.cs new file mode 100644 index 000000000..b9628ec3e --- /dev/null +++ b/MediaBrowser.Model/Xml/IXmlReaderSettingsFactory.cs @@ -0,0 +1,9 @@ +using System.Xml; + +namespace MediaBrowser.Model.Xml +{ + public interface IXmlReaderSettingsFactory + { + XmlReaderSettings Create(bool enableValidation); + } +} |
