Przeglądaj źródła

update metadata editor

Luke Pulverenti 10 lat temu
rodzic
commit
718545a79b
24 zmienionych plików z 248 dodań i 67 usunięć
  1. 9 2
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  2. 5 1
      MediaBrowser.Controller/Channels/ChannelVideoItem.cs
  3. 1 1
      MediaBrowser.Controller/Channels/IChannelMediaItem.cs
  4. 10 11
      MediaBrowser.Controller/Entities/Audio/Audio.cs
  5. 1 0
      MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
  6. 2 2
      MediaBrowser.Controller/Entities/BaseItem.cs
  7. 1 5
      MediaBrowser.Controller/Entities/TV/Season.cs
  8. 9 4
      MediaBrowser.Controller/Entities/Video.cs
  9. 4 0
      MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
  10. 5 0
      MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
  11. 4 0
      MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
  12. 47 1
      MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
  13. 1 1
      MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs
  14. 25 10
      MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
  15. 1 1
      MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs
  16. 1 2
      MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
  17. 2 2
      MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs
  18. 9 11
      MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
  19. 1 0
      MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
  20. 14 0
      MediaBrowser.Server.Implementations/Notifications/IConfigurableNotificationService.cs
  21. 20 1
      MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs
  22. 20 3
      MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs
  23. 50 9
      MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
  24. 6 0
      MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

+ 9 - 2
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -839,7 +839,7 @@ namespace MediaBrowser.Api.Playback
             }
 
             // leave blank so ffmpeg will decide
-            return string.Empty;
+            return null;
         }
 
         /// <summary>
@@ -849,7 +849,7 @@ namespace MediaBrowser.Api.Playback
         /// <returns>System.String.</returns>
         protected string GetInputArgument(StreamState state)
         {
-            var arg = string.Format("{1}-i {0}", GetInputPathArgument(state), GetVideoDecoder(state));
+            var arg = string.Format("-i {0}", GetInputPathArgument(state));
 
             if (state.SubtitleStream != null)
             {
@@ -1060,6 +1060,7 @@ namespace MediaBrowser.Api.Playback
                         var bytes = Encoding.UTF8.GetBytes(Environment.NewLine + line);
 
                         await target.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
+                        await target.FlushAsync().ConfigureAwait(false);
                     }
                 }
             }
@@ -2191,6 +2192,12 @@ namespace MediaBrowser.Api.Playback
                 inputModifier += " -re";
             }
 
+            var videoDecoder = GetVideoDecoder(state);
+            if (!string.IsNullOrWhiteSpace(videoDecoder))
+            {
+                inputModifier += " " + videoDecoder;
+            }
+
             return inputModifier;
         }
 

+ 5 - 1
MediaBrowser.Controller/Channels/ChannelVideoItem.cs

@@ -115,7 +115,11 @@ namespace MediaBrowser.Controller.Channels
             var info = GetItemLookupInfo<ChannelItemLookupInfo>();
 
             info.ContentType = ContentType;
-            info.ExtraType = ExtraType;
+
+            if (ExtraType.HasValue)
+            {
+                info.ExtraType = ExtraType.Value;
+            }
 
             return info;
         }

+ 1 - 1
MediaBrowser.Controller/Channels/IChannelMediaItem.cs

@@ -11,7 +11,7 @@ namespace MediaBrowser.Controller.Channels
 
         ChannelMediaContentType ContentType { get; set; }
 
-        ExtraType ExtraType { get; set; }
+        ExtraType? ExtraType { get; set; }
 
         List<ChannelMediaInfo> ChannelMediaSources { get; set; }
     }

+ 10 - 11
MediaBrowser.Controller/Entities/Audio/Audio.cs

@@ -24,14 +24,20 @@ namespace MediaBrowser.Controller.Entities.Audio
         IThemeMedia,
         IArchivable
     {
-        public string FormatName { get; set; }
         public long? Size { get; set; }
         public string Container { get; set; }
         public int? TotalBitrate { get; set; }
         public List<string> Tags { get; set; }
-        public ExtraType ExtraType { get; set; }
+        public ExtraType? ExtraType { get; set; }
 
-        public bool IsThemeMedia { get; set; }
+        [IgnoreDataMember]
+        public bool IsThemeMedia
+        {
+            get
+            {
+                return ExtraType.HasValue && ExtraType.Value == Model.Entities.ExtraType.ThemeSong;
+            }
+        }
 
         public Audio()
         {
@@ -46,12 +52,6 @@ namespace MediaBrowser.Controller.Entities.Audio
             get { return LocationType == LocationType.FileSystem && RunTimeTicks.HasValue; }
         }
 
-        /// <summary>
-        /// Gets or sets a value indicating whether this instance has embedded image.
-        /// </summary>
-        /// <value><c>true</c> if this instance has embedded image; otherwise, <c>false</c>.</value>
-        public bool HasEmbeddedImage { get; set; }
-
         [IgnoreDataMember]
         protected override bool SupportsOwnedItems
         {
@@ -212,8 +212,7 @@ namespace MediaBrowser.Controller.Entities.Audio
                 Path = enablePathSubstituion ? GetMappedPath(i.Path, locationType) : i.Path,
                 RunTimeTicks = i.RunTimeTicks,
                 Container = i.Container,
-                Size = i.Size,
-                Formats = (i.FormatName ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList()
+                Size = i.Size
             };
 
             if (string.IsNullOrEmpty(info.Container))

+ 1 - 0
MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs

@@ -66,6 +66,7 @@ namespace MediaBrowser.Controller.Entities.Audio
         /// Gets the tracks.
         /// </summary>
         /// <value>The tracks.</value>
+        [IgnoreDataMember]
         public IEnumerable<Audio> Tracks
         {
             get

+ 2 - 2
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -880,7 +880,7 @@ namespace MediaBrowser.Controller.Entities
 
                 if (!i.IsThemeMedia)
                 {
-                    i.IsThemeMedia = true;
+                    i.ExtraType = ExtraType.ThemeVideo;
                     subOptions.ForceSave = true;
                 }
 
@@ -910,7 +910,7 @@ namespace MediaBrowser.Controller.Entities
 
                 if (!i.IsThemeMedia)
                 {
-                    i.IsThemeMedia = true;
+                    i.ExtraType = ExtraType.ThemeSong;
                     subOptions.ForceSave = true;
                 }
 

+ 1 - 5
MediaBrowser.Controller/Entities/TV/Season.cs

@@ -61,10 +61,6 @@ namespace MediaBrowser.Controller.Entities.TV
             return base.CreateUserDataKey();
         }
 
-        /// <summary>
-        /// The _series
-        /// </summary>
-        private Series _series;
         /// <summary>
         /// This Episode's Series Instance
         /// </summary>
@@ -72,7 +68,7 @@ namespace MediaBrowser.Controller.Entities.TV
         [IgnoreDataMember]
         public Series Series
         {
-            get { return _series ?? (_series = FindParent<Series>()); }
+            get { return FindParent<Series>(); }
         }
 
         [IgnoreDataMember]

+ 9 - 4
MediaBrowser.Controller/Entities/Video.cs

@@ -33,14 +33,20 @@ namespace MediaBrowser.Controller.Entities
         public List<string> LocalAlternateVersions { get; set; }
         public List<LinkedChild> LinkedAlternateVersions { get; set; }
 
-        public bool IsThemeMedia { get; set; }
+        [IgnoreDataMember]
+        public bool IsThemeMedia
+        {
+            get
+            {
+                return ExtraType.HasValue && ExtraType.Value == Model.Entities.ExtraType.ThemeVideo;
+            }
+        }
 
-        public string FormatName { get; set; }
         public long? Size { get; set; }
         public string Container { get; set; }
         public int? TotalBitrate { get; set; }
         public string ShortOverview { get; set; }
-        public ExtraType ExtraType { get; set; }
+        public ExtraType? ExtraType { get; set; }
 
         /// <summary>
         /// Gets or sets the preferred metadata country code.
@@ -498,7 +504,6 @@ namespace MediaBrowser.Controller.Entities
                 VideoType = i.VideoType,
                 Container = i.Container,
                 Size = i.Size,
-                Formats = (i.FormatName ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(),
                 Timestamp = i.Timestamp,
                 Type = type,
                 PlayableStreamFileNames = i.PlayableStreamFileNames.ToList(),

+ 4 - 0
MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs

@@ -18,6 +18,7 @@ namespace MediaBrowser.Controller.LiveTv
         public string ProviderImagePath { get; set; }
         public string ProviderImageUrl { get; set; }
         public string EpisodeTitle { get; set; }
+        [IgnoreDataMember]
         public bool IsSeries { get; set; }
         public string SeriesTimerId { get; set; }
         [IgnoreDataMember]
@@ -25,6 +26,7 @@ namespace MediaBrowser.Controller.LiveTv
         public RecordingStatus Status { get; set; }
         [IgnoreDataMember]
         public bool IsSports { get; set; }
+        [IgnoreDataMember]
         public bool IsNews { get; set; }
         [IgnoreDataMember]
         public bool IsKids { get; set; }
@@ -32,7 +34,9 @@ namespace MediaBrowser.Controller.LiveTv
         [IgnoreDataMember]
         public bool IsMovie { get; set; }
         public bool? IsHD { get; set; }
+        [IgnoreDataMember]
         public bool IsLive { get; set; }
+        [IgnoreDataMember]
         public bool IsPremiere { get; set; }
         public ChannelType ChannelType { get; set; }
         public string ProgramId { get; set; }

+ 5 - 0
MediaBrowser.Controller/LiveTv/LiveTvProgram.cs

@@ -125,18 +125,21 @@ namespace MediaBrowser.Controller.LiveTv
         /// 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>
+        [IgnoreDataMember]
         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>
+        [IgnoreDataMember]
         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>
+        [IgnoreDataMember]
         public bool IsNews { get; set; }
 
         /// <summary>
@@ -150,6 +153,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// 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>
+        [IgnoreDataMember]
         public bool IsPremiere { get; set; }
 
         /// <summary>
@@ -248,6 +252,7 @@ namespace MediaBrowser.Controller.LiveTv
             return info;
         }
 
+        [IgnoreDataMember]
         public override bool SupportsPeople
         {
             get

+ 4 - 0
MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs

@@ -18,6 +18,7 @@ namespace MediaBrowser.Controller.LiveTv
         public string ProviderImagePath { get; set; }
         public string ProviderImageUrl { get; set; }
         public string EpisodeTitle { get; set; }
+        [IgnoreDataMember]
         public bool IsSeries { get; set; }
         public string SeriesTimerId { get; set; }
         [IgnoreDataMember]
@@ -25,6 +26,7 @@ namespace MediaBrowser.Controller.LiveTv
         public RecordingStatus Status { get; set; }
         [IgnoreDataMember]
         public bool IsSports { get; set; }
+        [IgnoreDataMember]
         public bool IsNews { get; set; }
         [IgnoreDataMember]
         public bool IsKids { get; set; }
@@ -32,7 +34,9 @@ namespace MediaBrowser.Controller.LiveTv
         [IgnoreDataMember]
         public bool IsMovie { get; set; }
         public bool? IsHD { get; set; }
+        [IgnoreDataMember]
         public bool IsLive { get; set; }
+        [IgnoreDataMember]
         public bool IsPremiere { get; set; }
         public ChannelType ChannelType { get; set; }
         public string ProgramId { get; set; }

+ 47 - 1
MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs

@@ -342,9 +342,55 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 inputModifier += " -re";
             }
 
+            var videoDecoder = GetVideoDecoder(job);
+            if (!string.IsNullOrWhiteSpace(videoDecoder))
+            {
+                inputModifier += " " + videoDecoder;
+            }
+
             return inputModifier;
         }
 
+        /// <summary>
+        /// Gets the name of the output video codec
+        /// </summary>
+        /// <param name="state">The state.</param>
+        /// <returns>System.String.</returns>
+        protected string GetVideoDecoder(EncodingJob state)
+        {
+            if (string.Equals(GetEncodingOptions().HardwareVideoDecoder, "qsv", StringComparison.OrdinalIgnoreCase))
+            {
+                if (state.VideoStream != null && !string.IsNullOrWhiteSpace(state.VideoStream.Codec))
+                {
+                    switch (state.MediaSource.VideoStream.Codec.ToLower())
+                    {
+                        case "avc":
+                        case "h264":
+                            if (MediaEncoder.SupportsDecoder("h264_qsv"))
+                            {
+                                return "-c:v h264_qsv ";
+                            }
+                            break;
+                        case "mpeg2video":
+                            if (MediaEncoder.SupportsDecoder("mpeg2_qsv"))
+                            {
+                                return "-c:v mpeg2_qsv ";
+                            }
+                            break;
+                        case "vc1":
+                            if (MediaEncoder.SupportsDecoder("vc1_qsv"))
+                            {
+                                return "-c:v vc1_qsv ";
+                            }
+                            break;
+                    }
+                }
+            }
+
+            // leave blank so ffmpeg will decide
+            return null;
+        }
+
         private string GetUserAgentParam(EncodingJob job)
         {
             string useragent = null;
@@ -436,7 +482,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationToken).ConfigureAwait(false);
             }
 
-            if (state.MediaSource.RequiresOpening)
+            if (state.MediaSource.RequiresOpening && string.IsNullOrWhiteSpace(state.LiveStreamId))
             {
                 var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest
                 {

+ 1 - 1
MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs

@@ -56,7 +56,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
         public bool EnableMpegtsM2TsMode { get; set; }
         public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
         public long? EncodingDurationTicks { get; set; }
-        public string LiveTvStreamId { get; set; }
+        public string LiveStreamId { get; set; }
         public long? RunTimeTicks;
 
         public string ItemType { get; set; }

+ 25 - 10
MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs

@@ -326,26 +326,36 @@ namespace MediaBrowser.MediaEncoding.Encoder
         /// <returns>System.Nullable{System.Int32}.</returns>
         private int? GetNumAudioChannelsParam(EncodingJobOptions request, MediaStream audioStream, string outputAudioCodec)
         {
-            if (audioStream != null)
+            var inputChannels = audioStream == null
+                            ? null
+                            : audioStream.Channels;
+
+            if (inputChannels <= 0)
             {
-                var codec = outputAudioCodec ?? string.Empty;
+                inputChannels = null;
+            }
 
-                if (audioStream.Channels > 2 && codec.IndexOf("wma", StringComparison.OrdinalIgnoreCase) != -1)
-                {
-                    // wmav2 currently only supports two channel output
-                    return 2;
-                }
+            var codec = outputAudioCodec ?? string.Empty;
+
+            if (codec.IndexOf("wma", StringComparison.OrdinalIgnoreCase) != -1)
+            {
+                // wmav2 currently only supports two channel output
+                return Math.Min(2, inputChannels ?? 2);
             }
 
             if (request.MaxAudioChannels.HasValue)
             {
-                if (audioStream != null && audioStream.Channels.HasValue)
+                if (inputChannels.HasValue)
                 {
-                    return Math.Min(request.MaxAudioChannels.Value, audioStream.Channels.Value);
+                    return Math.Min(request.MaxAudioChannels.Value, inputChannels.Value);
                 }
 
+                var channelLimit = codec.IndexOf("mp3", StringComparison.OrdinalIgnoreCase) != -1
+                    ? 2
+                    : 6;
+
                 // If we don't have any media info then limit it to 5 to prevent encoding errors due to asking for too many channels
-                return Math.Min(request.MaxAudioChannels.Value, 5);
+                return Math.Min(request.MaxAudioChannels.Value, channelLimit);
             }
 
             return request.AudioChannels;
@@ -519,6 +529,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 return false;
             }
 
+            if (videoStream.IsAnamorphic ?? false)
+            {
+                return false;
+            }
+
             // Can't stream copy if we're burning in subtitles
             if (request.SubtitleStreamIndex.HasValue)
             {

+ 1 - 1
MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs

@@ -43,7 +43,7 @@ namespace MediaBrowser.Providers.MediaInfo
             var audio = (Audio)item;
 
             // Can't extract if we didn't find a video stream in the file
-            if (!audio.HasEmbeddedImage)
+            if (!audio.GetMediaSources(false).Take(1).SelectMany(i => i.MediaStreams).Any(i => i.Type == MediaStreamType.EmbeddedImage))
             {
                 return Task.FromResult(new DynamicImageResponse { HasImage = false });
             }

+ 1 - 2
MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs

@@ -103,9 +103,8 @@ namespace MediaBrowser.Providers.MediaInfo
         {
             var mediaStreams = mediaInfo.MediaStreams;
 
-            audio.FormatName = mediaInfo.Container;
+            //audio.FormatName = mediaInfo.Container;
             audio.TotalBitrate = mediaInfo.Bitrate;
-            audio.HasEmbeddedImage = mediaStreams.Any(i => i.Type == MediaStreamType.EmbeddedImage);
 
             audio.RunTimeTicks = mediaInfo.RunTimeTicks;
             audio.Size = mediaInfo.Size;

+ 2 - 2
MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs

@@ -190,8 +190,8 @@ namespace MediaBrowser.Providers.MediaInfo
             var mediaStreams = mediaInfo.MediaStreams;
 
             video.TotalBitrate = mediaInfo.Bitrate;
-            video.FormatName = (mediaInfo.Container ?? string.Empty)
-                .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase);
+            //video.FormatName = (mediaInfo.Container ?? string.Empty)
+            //    .Replace("matroska", "mkv", StringComparison.OrdinalIgnoreCase);
 
             // For dvd's this may not always be accurate, so don't set the runtime if the item already has one
             var needToSetRuntime = video.VideoType != VideoType.Dvd || video.RunTimeTicks == null || video.RunTimeTicks.Value == 0;

+ 9 - 11
MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -650,11 +650,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             {
                 await _libraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
             }
+            else if (string.IsNullOrWhiteSpace(info.Etag))
+            {
+                await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
+            }
             else
             {
-                if (string.IsNullOrWhiteSpace(info.Etag) || !string.Equals(info.Etag, item.Etag, StringComparison.OrdinalIgnoreCase))
+                // Increment this whenver some internal change deems it necessary
+                var etag = info.Etag + "1";
+
+                if (!string.Equals(etag, item.Etag, StringComparison.OrdinalIgnoreCase))
                 {
-                    item.Etag = info.Etag;
+                    item.Etag = etag;
                     await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
                 }
             }
@@ -1162,15 +1169,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
                     foreach (var program in channelPrograms)
                     {
-                        if (program.StartDate.Kind != DateTimeKind.Utc)
-                        {
-                            _logger.Error("{0} returned StartDate.DateTimeKind.{1} instead of UTC for program {2}", service.Name, program.StartDate.Kind.ToString(), program.Name);
-                        }
-                        else if (program.EndDate.Kind != DateTimeKind.Utc)
-                        {
-                            _logger.Error("{0} returned EndDate.DateTimeKind.{1} instead of UTC for program {2}", service.Name, program.EndDate.Kind.ToString(), program.Name);
-                        }
-
                         var programItem = await GetProgram(program, channelId, currentChannel.ChannelType, service.Name, cancellationToken).ConfigureAwait(false);
 
                         programs.Add(programItem.Id);

+ 1 - 0
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -232,6 +232,7 @@
     <Compile Include="Localization\LocalizationManager.cs" />
     <Compile Include="Logging\PatternsLogger.cs" />
     <Compile Include="MediaEncoder\EncodingManager.cs" />
+    <Compile Include="Notifications\IConfigurableNotificationService.cs" />
     <Compile Include="Persistence\BaseSqliteRepository.cs" />
     <Compile Include="Persistence\CleanDatabaseScheduledTask.cs" />
     <Compile Include="Social\SharingManager.cs" />

+ 14 - 0
MediaBrowser.Server.Implementations/Notifications/IConfigurableNotificationService.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.Notifications
+{
+    public interface IConfigurableNotificationService
+    {
+        bool IsHidden { get; }
+        bool IsEnabled(string notificationType);
+    }
+}

+ 20 - 1
MediaBrowser.Server.Implementations/Notifications/InternalNotificationService.cs

@@ -3,10 +3,11 @@ using MediaBrowser.Controller.Notifications;
 using MediaBrowser.Model.Notifications;
 using System.Threading;
 using System.Threading.Tasks;
+using System;
 
 namespace MediaBrowser.Server.Implementations.Notifications
 {
-    public class InternalNotificationService : INotificationService
+    public class InternalNotificationService : INotificationService, IConfigurableNotificationService
     {
         private readonly INotificationsRepository _repo;
 
@@ -36,6 +37,24 @@ namespace MediaBrowser.Server.Implementations.Notifications
 
         public bool IsEnabledForUser(User user)
         {
+            return user.Policy.IsAdministrator;
+        }
+
+        public bool IsHidden
+        {
+            get { return true; }
+        }
+
+        public bool IsEnabled(string notificationType)
+        {
+            if (notificationType.IndexOf("playback", StringComparison.OrdinalIgnoreCase) != -1)
+            {
+                return false;
+            }
+            if (notificationType.IndexOf("newlibrarycontent", StringComparison.OrdinalIgnoreCase) != -1)
+            {
+                return false;
+            }
             return true;
         }
     }

+ 20 - 3
MediaBrowser.Server.Implementations/Notifications/NotificationManager.cs

@@ -230,8 +230,19 @@ namespace MediaBrowser.Server.Implementations.Notifications
 
         private bool IsEnabled(INotificationService service, string notificationType)
         {
-            return string.IsNullOrEmpty(notificationType) ||
-                GetConfiguration().IsServiceEnabled(service.Name, notificationType);
+            if (string.IsNullOrEmpty(notificationType))
+            {
+                return true;
+            }
+
+            var configurable = service as IConfigurableNotificationService;
+
+            if (configurable != null)
+            {
+                return configurable.IsEnabled(notificationType);
+            }
+
+            return GetConfiguration().IsServiceEnabled(service.Name, notificationType);
         }
 
         public void AddParts(IEnumerable<INotificationService> services, IEnumerable<INotificationTypeFactory> notificationTypeFactories)
@@ -268,7 +279,13 @@ namespace MediaBrowser.Server.Implementations.Notifications
 
         public IEnumerable<NotificationServiceInfo> GetNotificationServices()
         {
-            return _services.Select(i => new NotificationServiceInfo
+            return _services.Where(i =>
+            {
+                var configurable = i as IConfigurableNotificationService;
+
+                return configurable == null || !configurable.IsHidden;
+
+            }).Select(i => new NotificationServiceInfo
             {
                 Name = i.Name,
                 Id = i.Name.GetMD5().ToString("N")

+ 50 - 9
MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs

@@ -72,7 +72,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
         private IDbCommand _deletePeopleCommand;
         private IDbCommand _savePersonCommand;
 
-        private const int LatestSchemaVersion = 7;
+        private const int LatestSchemaVersion = 9;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
@@ -177,6 +177,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
             _connection.AddColumn(_logger, "TypedBaseItems", "IsOffline", "BIT");
             _connection.AddColumn(_logger, "TypedBaseItems", "LocationType", "Text");
 
+            _connection.AddColumn(_logger, "TypedBaseItems", "IsSeries", "BIT");
+            _connection.AddColumn(_logger, "TypedBaseItems", "IsLive", "BIT");
+            _connection.AddColumn(_logger, "TypedBaseItems", "IsNews", "BIT");
+            _connection.AddColumn(_logger, "TypedBaseItems", "IsPremiere", "BIT");
+            
             PrepareStatements();
 
             _mediaStreamsRepository.Initialize();
@@ -199,6 +204,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
             "IsMovie",
             "IsSports",
             "IsKids",
+            "IsSeries",
+            "IsLive",
+            "IsNews",
+            "IsPremiere",
             "CommunityRating",
             "CustomRating",
             "IndexNumber",
@@ -222,6 +231,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 "IsKids",
                 "IsMovie",
                 "IsSports",
+                "IsSeries",
+                "IsLive",
+                "IsNews",
+                "IsPremiere",
                 "CommunityRating",
                 "CustomRating",
                 "IndexNumber",
@@ -369,12 +382,20 @@ namespace MediaBrowser.Server.Implementations.Persistence
                         _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsKids;
                         _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsMovie;
                         _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsSports;
+                        _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsSeries;
+                        _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsLive;
+                        _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsNews;
+                        _saveItemCommand.GetParameter(index++).Value = hasProgramAttributes.IsPremiere;
                     }
                     else
                     {
                         _saveItemCommand.GetParameter(index++).Value = null;
                         _saveItemCommand.GetParameter(index++).Value = null;
                         _saveItemCommand.GetParameter(index++).Value = null;
+                        _saveItemCommand.GetParameter(index++).Value = null;
+                        _saveItemCommand.GetParameter(index++).Value = null;
+                        _saveItemCommand.GetParameter(index++).Value = null;
+                        _saveItemCommand.GetParameter(index++).Value = null;
                     }
 
                     _saveItemCommand.GetParameter(index++).Value = item.CommunityRating;
@@ -563,26 +584,46 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 {
                     hasProgramAttributes.IsKids = reader.GetBoolean(8);
                 }
+
+                if (!reader.IsDBNull(9))
+                {
+                    hasProgramAttributes.IsSeries = reader.GetBoolean(9);
+                }
+
+                if (!reader.IsDBNull(10))
+                {
+                    hasProgramAttributes.IsLive = reader.GetBoolean(10);
+                }
+
+                if (!reader.IsDBNull(11))
+                {
+                    hasProgramAttributes.IsNews = reader.GetBoolean(11);
+                }
+
+                if (!reader.IsDBNull(12))
+                {
+                    hasProgramAttributes.IsPremiere = reader.GetBoolean(12);
+                }
             }
 
-            if (!reader.IsDBNull(9))
+            if (!reader.IsDBNull(13))
             {
-                item.CommunityRating = reader.GetFloat(9);
+                item.CommunityRating = reader.GetFloat(13);
             }
 
-            if (!reader.IsDBNull(10))
+            if (!reader.IsDBNull(14))
             {
-                item.CustomRating = reader.GetString(10);
+                item.CustomRating = reader.GetString(14);
             }
 
-            if (!reader.IsDBNull(11))
+            if (!reader.IsDBNull(15))
             {
-                item.IndexNumber = reader.GetInt32(11);
+                item.IndexNumber = reader.GetInt32(15);
             }
 
-            if (!reader.IsDBNull(12))
+            if (!reader.IsDBNull(16))
             {
-                item.IsLocked = reader.GetBoolean(12);
+                item.IsLocked = reader.GetBoolean(16);
             }
 
             return item;

+ 6 - 0
MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

@@ -173,6 +173,12 @@
     <Content Include="dashboard-ui\components\imageuploader\imageuploader.template.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="dashboard-ui\components\metadataeditor\metadataeditor.js">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="dashboard-ui\components\metadataeditor\metadataeditor.template.html">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\components\paperdialoghelper.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>