Преглед изворни кода

Merge pull request #2077 from MediaBrowser/dev

Dev
Luke пре 8 година
родитељ
комит
3e8303bd4b
29 измењених фајлова са 344 додато и 78 уклоњено
  1. 5 3
      MediaBrowser.Api/ApiEntryPoint.cs
  2. 145 2
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  3. 3 1
      MediaBrowser.Controller/Persistence/IItemRepository.cs
  4. 16 4
      MediaBrowser.Dlna/Profiles/DefaultProfile.cs
  5. 2 0
      MediaBrowser.Dlna/Profiles/DenonAvrProfile.cs
  6. 2 0
      MediaBrowser.Dlna/Profiles/DirectTvProfile.cs
  7. 3 1
      MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs
  8. 2 0
      MediaBrowser.Dlna/Profiles/LgTvProfile.cs
  9. 2 0
      MediaBrowser.Dlna/Profiles/LinksysDMA2100Profile.cs
  10. 2 0
      MediaBrowser.Dlna/Profiles/MediaMonkeyProfile.cs
  11. 2 0
      MediaBrowser.Dlna/Profiles/PopcornHourProfile.cs
  12. 2 0
      MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs
  13. 7 3
      MediaBrowser.Dlna/Profiles/Xml/Default.xml
  14. 4 1
      MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs
  15. 12 0
      MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
  16. 2 0
      MediaBrowser.Model/Configuration/ServerConfiguration.cs
  17. 35 15
      MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
  18. 12 2
      MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs
  19. 5 2
      MediaBrowser.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs
  20. 10 11
      MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs
  21. 5 2
      MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs
  22. 10 11
      MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs
  23. 5 2
      MediaBrowser.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs
  24. 10 11
      MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs
  25. 1 1
      MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
  26. 1 1
      MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
  27. 35 4
      MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
  28. 1 1
      MediaBrowser.Server.Implementations/packages.config
  29. 3 0
      MediaBrowser.Server.Startup.Common/ApplicationHost.cs

+ 5 - 3
MediaBrowser.Api/ApiEntryPoint.cs

@@ -192,13 +192,13 @@ namespace MediaBrowser.Api
 
                 _activeTranscodingJobs.Add(job);
 
-                ReportTranscodingProgress(job, state, null, null, null, null);
+                ReportTranscodingProgress(job, state, null, null, null, null, null);
 
                 return job;
             }
         }
 
-        public void ReportTranscodingProgress(TranscodingJob job, StreamState state, TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded)
+        public void ReportTranscodingProgress(TranscodingJob job, StreamState state, TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate)
         {
             var ticks = transcodingPosition.HasValue ? transcodingPosition.Value.Ticks : (long?)null;
 
@@ -208,6 +208,7 @@ namespace MediaBrowser.Api
                 job.CompletionPercentage = percentComplete;
                 job.TranscodingPositionTicks = ticks;
                 job.BytesTranscoded = bytesTranscoded;
+                job.BitRate = bitRate;
             }
 
             var deviceId = state.Request.DeviceId;
@@ -219,7 +220,7 @@ namespace MediaBrowser.Api
 
                 _sessionManager.ReportTranscodingInfo(deviceId, new TranscodingInfo
                 {
-                    Bitrate = state.TotalOutputBitrate,
+                    Bitrate = bitRate ?? state.TotalOutputBitrate,
                     AudioCodec = audioCodec,
                     VideoCodec = videoCodec,
                     Container = state.OutputContainer,
@@ -694,6 +695,7 @@ namespace MediaBrowser.Api
 
         public long? BytesDownloaded { get; set; }
         public long? BytesTranscoded { get; set; }
+        public int? BitRate { get; set; }
 
         public long? TranscodingPositionTicks { get; set; }
         public long? DownloadPositionTicks { get; set; }

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

@@ -22,6 +22,8 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using CommonIO;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller;
 
 namespace MediaBrowser.Api.Playback
 {
@@ -69,6 +71,9 @@ namespace MediaBrowser.Api.Playback
         protected IZipClient ZipClient { get; private set; }
         protected IJsonSerializer JsonSerializer { get; private set; }
 
+        public static IServerApplicationHost AppHost;
+        public static IHttpClient HttpClient;
+
         /// <summary>
         /// Initializes a new instance of the <see cref="BaseStreamingService" /> class.
         /// </summary>
@@ -1112,6 +1117,7 @@ namespace MediaBrowser.Api.Playback
             }
 
             StartThrottler(state, transcodingJob);
+            ReportUsage(state);
 
             return transcodingJob;
         }
@@ -1131,7 +1137,7 @@ namespace MediaBrowser.Api.Playback
             return state.InputProtocol == MediaProtocol.File &&
                 state.RunTimeTicks.HasValue &&
                 state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks &&
-                state.IsInputVideo && 
+                state.IsInputVideo &&
                 state.VideoType == VideoType.VideoFile &&
                 !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) &&
                 string.Equals(GetVideoEncoder(state), "libx264", StringComparison.OrdinalIgnoreCase);
@@ -1172,6 +1178,7 @@ namespace MediaBrowser.Api.Playback
             double? percent = null;
             TimeSpan? transcodingPosition = null;
             long? bytesTranscoded = null;
+            int? bitRate = null;
 
             var parts = line.Split(' ');
 
@@ -1235,11 +1242,32 @@ namespace MediaBrowser.Api.Playback
                         }
                     }
                 }
+                else if (part.StartsWith("bitrate=", StringComparison.OrdinalIgnoreCase))
+                {
+                    var rate = part.Split(new[] { '=' }, 2).Last();
+
+                    int? scale = null;
+                    if (rate.IndexOf("kbits/s", StringComparison.OrdinalIgnoreCase) != -1)
+                    {
+                        scale = 1024;
+                        rate = rate.Replace("kbits/s", string.Empty, StringComparison.OrdinalIgnoreCase);
+                    }
+
+                    if (scale.HasValue)
+                    {
+                        float val;
+
+                        if (float.TryParse(rate, NumberStyles.Any, UsCulture, out val))
+                        {
+                            bitRate = (int)Math.Ceiling(val * scale.Value);
+                        }
+                    }
+                }
             }
 
             if (framerate.HasValue || percent.HasValue)
             {
-                ApiEntryPoint.Instance.ReportTranscodingProgress(transcodingJob, state, transcodingPosition, framerate, percent, bytesTranscoded);
+                ApiEntryPoint.Instance.ReportTranscodingProgress(transcodingJob, state, transcodingPosition, framerate, percent, bytesTranscoded, bitRate);
             }
         }
 
@@ -2197,6 +2225,121 @@ namespace MediaBrowser.Api.Playback
             }
         }
 
+        private async void ReportUsage(StreamState state)
+        {
+            try
+            {
+                await ReportUsageInternal(state).ConfigureAwait(false);
+            }
+            catch
+            {
+
+            }
+        }
+
+        private Task ReportUsageInternal(StreamState state)
+        {
+            if (!ServerConfigurationManager.Configuration.EnableAnonymousUsageReporting)
+            {
+                return Task.FromResult(true);
+            }
+
+            if (!string.Equals(MediaEncoder.EncoderLocationType, "Default", StringComparison.OrdinalIgnoreCase))
+            {
+                return Task.FromResult(true);
+            }
+
+            var dict = new Dictionary<string, string>();
+
+            var outputAudio = GetAudioEncoder(state);
+            if (!string.IsNullOrWhiteSpace(outputAudio))
+            {
+                dict["outputAudio"] = outputAudio;
+            }
+
+            var outputVideo = GetVideoEncoder(state);
+            if (!string.IsNullOrWhiteSpace(outputVideo))
+            {
+                dict["outputVideo"] = outputVideo;
+            }
+
+            if (ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputAudio ?? string.Empty, StringComparer.OrdinalIgnoreCase) &&
+                ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputVideo ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+            {
+                return Task.FromResult(true);
+            }
+
+            dict["id"] = AppHost.SystemId;
+            dict["type"] = state.VideoRequest == null ? "Audio" : "Video";
+
+            var audioStream = state.AudioStream;
+            if (audioStream != null && !string.IsNullOrWhiteSpace(audioStream.Codec))
+            {
+                dict["inputAudio"] = audioStream.Codec;
+            }
+
+            var videoStream = state.VideoStream;
+            if (videoStream != null && !string.IsNullOrWhiteSpace(videoStream.Codec))
+            {
+                dict["inputVideo"] = videoStream.Codec;
+            }
+
+            var cert = GetType().Assembly.GetModules().First().GetSignerCertificate();
+            if (cert != null)
+            {
+                dict["assemblySig"] = cert.GetCertHashString();
+                dict["certSubject"] = cert.Subject ?? string.Empty;
+                dict["certIssuer"] = cert.Issuer ?? string.Empty;
+            }
+            else
+            {
+                return Task.FromResult(true);
+            }
+
+            if (state.SupportedAudioCodecs.Count > 0)
+            {
+                dict["supportedAudioCodecs"] = string.Join(",", state.SupportedAudioCodecs.ToArray());
+            }
+
+            var auth = AuthorizationContext.GetAuthorizationInfo(Request);
+
+            dict["appName"] = auth.Client ?? string.Empty;
+            dict["appVersion"] = auth.Version ?? string.Empty;
+            dict["device"] = auth.Device ?? string.Empty;
+            dict["deviceId"] = auth.DeviceId ?? string.Empty;
+            dict["context"] = "streaming";
+
+            //Logger.Info(JsonSerializer.SerializeToString(dict));
+            if (!ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputAudio ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+            {
+                var list = ServerConfigurationManager.Configuration.CodecsUsed.ToList();
+                list.Add(outputAudio);
+                ServerConfigurationManager.Configuration.CodecsUsed = list.ToArray();
+            }
+
+            if (!ServerConfigurationManager.Configuration.CodecsUsed.Contains(outputVideo ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+            {
+                var list = ServerConfigurationManager.Configuration.CodecsUsed.ToList();
+                list.Add(outputVideo);
+                ServerConfigurationManager.Configuration.CodecsUsed = list.ToArray();
+            }
+
+            ServerConfigurationManager.SaveConfiguration();
+
+            //Logger.Info(JsonSerializer.SerializeToString(dict));
+            var options = new HttpRequestOptions()
+            {
+                Url = "https://mb3admin.com/admin/service/transcoding/report",
+                CancellationToken = CancellationToken.None,
+                LogRequest = false,
+                LogErrors = false
+            };
+            options.RequestContent = JsonSerializer.SerializeToString(dict);
+            options.RequestContentType = "application/json";
+
+            return HttpClient.Post(options);
+        }
+
         /// <summary>
         /// Adds the dlna headers.
         /// </summary>

+ 3 - 1
MediaBrowser.Controller/Persistence/IItemRepository.cs

@@ -171,8 +171,10 @@ namespace MediaBrowser.Controller.Persistence
         QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query);
         QueryResult<Tuple<BaseItem, ItemCounts>> GetAllArtists(InternalItemsQuery query);
 
+        List<string> GetGameGenreNames();
+        List<string> GetMusicGenreNames();
         List<string> GetStudioNames();
-
+        List<string> GetGenreNames();
         List<string> GetAllArtistNames();
     }
 }

+ 16 - 4
MediaBrowser.Dlna/Profiles/DefaultProfile.cs

@@ -65,14 +65,26 @@ namespace MediaBrowser.Dlna.Profiles
             {
                 new DirectPlayProfile
                 {
-                    Container = "mp3,wma",
-                    Type = DlnaProfileType.Audio
+                    Container = "m4v,ts,mkv,avi,mpg,mpeg,mp4",
+                    VideoCodec = "h264",
+                    AudioCodec = "aac,mp3,ac3",
+                    Type = DlnaProfileType.Video
                 },
 
                 new DirectPlayProfile
                 {
-                    Container = "avi,mp4",
-                    Type = DlnaProfileType.Video
+                    Container = "mp3,wma,aac,wav",
+                    Type = DlnaProfileType.Audio
+                }
+            };
+
+            ResponseProfiles = new[]
+            {
+                new ResponseProfile
+                {
+                    Container = "m4v",
+                    Type = DlnaProfileType.Video,
+                    MimeType = "video/mp4"
                 }
             };
         }

+ 2 - 0
MediaBrowser.Dlna/Profiles/DenonAvrProfile.cs

@@ -24,6 +24,8 @@ namespace MediaBrowser.Dlna.Profiles
                     Type = DlnaProfileType.Audio
                 },
             };
+
+            ResponseProfiles = new ResponseProfile[] { };
         }
     }
 }

+ 2 - 0
MediaBrowser.Dlna/Profiles/DirectTvProfile.cs

@@ -112,6 +112,8 @@ namespace MediaBrowser.Dlna.Profiles
                     }
                 }
             };
+
+            ResponseProfiles = new ResponseProfile[] { };
         }
     }
 }

+ 3 - 1
MediaBrowser.Dlna/Profiles/Foobar2000Profile.cs

@@ -11,7 +11,7 @@ namespace MediaBrowser.Dlna.Profiles
             Name = "foobar2000";
 
             SupportedMediaTypes = "Audio";
-            
+
             Identification = new DeviceIdentification
             {
                 FriendlyName = @"foobar",
@@ -70,6 +70,8 @@ namespace MediaBrowser.Dlna.Profiles
                     Type = DlnaProfileType.Audio
                 }
             };
+
+            ResponseProfiles = new ResponseProfile[] { };
         }
     }
 }

+ 2 - 0
MediaBrowser.Dlna/Profiles/LgTvProfile.cs

@@ -198,6 +198,8 @@ namespace MediaBrowser.Dlna.Profiles
                     Method = SubtitleDeliveryMethod.External
                 }
             };
+
+            ResponseProfiles = new ResponseProfile[] { };
         }
     }
 }

+ 2 - 0
MediaBrowser.Dlna/Profiles/LinksysDMA2100Profile.cs

@@ -30,6 +30,8 @@ namespace MediaBrowser.Dlna.Profiles
                     Type = DlnaProfileType.Video
                 }
             };
+
+            ResponseProfiles = new ResponseProfile[] { };
         }
     }
 }

+ 2 - 0
MediaBrowser.Dlna/Profiles/MediaMonkeyProfile.cs

@@ -70,6 +70,8 @@ namespace MediaBrowser.Dlna.Profiles
                     Type = DlnaProfileType.Audio
                 }
             };
+
+            ResponseProfiles = new ResponseProfile[] { };
         }
     }
 }

+ 2 - 0
MediaBrowser.Dlna/Profiles/PopcornHourProfile.cs

@@ -200,6 +200,8 @@ namespace MediaBrowser.Dlna.Profiles
                     }
                 }
             };
+
+            ResponseProfiles = new ResponseProfile[] { };
         }
     }
 }

+ 2 - 0
MediaBrowser.Dlna/Profiles/SonyBlurayPlayer2013Profile.cs

@@ -181,6 +181,8 @@ namespace MediaBrowser.Dlna.Profiles
                     }
                 }
             };
+
+            ResponseProfiles = new ResponseProfile[] { };
         }
     }
 }

+ 7 - 3
MediaBrowser.Dlna/Profiles/Xml/Default.xml

@@ -29,8 +29,8 @@
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
   <XmlRootAttributes />
   <DirectPlayProfiles>
-    <DirectPlayProfile container="mp3,wma" type="Audio" />
-    <DirectPlayProfile container="avi,mp4" type="Video" />
+    <DirectPlayProfile container="m4v,ts,mkv,avi,mpg,mpeg,mp4" audioCodec="aac,mp3,ac3" videoCodec="h264" type="Video" />
+    <DirectPlayProfile container="mp3,wma,aac,wav" type="Audio" />
   </DirectPlayProfiles>
   <TranscodingProfiles>
     <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" forceLiveStream="false" enableSubtitlesInManifest="false" />
@@ -39,6 +39,10 @@
   </TranscodingProfiles>
   <ContainerProfiles />
   <CodecProfiles />
-  <ResponseProfiles />
+  <ResponseProfiles>
+    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
+      <Conditions />
+    </ResponseProfile>
+  </ResponseProfiles>
   <SubtitleProfiles />
 </Profile>

+ 4 - 1
MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs

@@ -51,7 +51,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
             var metadata = string.Empty;
             var vn = string.Empty;
 
-            if (!string.IsNullOrWhiteSpace(state.AlbumCoverPath))
+            var hasArt = !string.IsNullOrWhiteSpace(state.AlbumCoverPath);
+            hasArt = false;
+
+            if (hasArt)
             {
                 albumCoverInput = " -i \"" + state.AlbumCoverPath + "\"";
                 mapArgs = " -map 0:a -map 1:v -c:v copy";

+ 12 - 0
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -123,10 +123,22 @@ namespace MediaBrowser.MediaEncoding.Encoder
                     return "System";
                 }
 
+                if (IsDefaultPath(FFMpegPath))
+                {
+                    return "Default";
+                }
+
                 return "Custom";
             }
         }
 
+        private bool IsDefaultPath(string path)
+        {
+            var parentPath = Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "ffmpeg", "20160410");
+
+            return FileSystem.ContainsSubPath(parentPath, path);
+        }
+
         private bool IsSystemInstalledPath(string path)
         {
             if (path.IndexOf("/", StringComparison.Ordinal) == -1 && path.IndexOf("\\", StringComparison.Ordinal) == -1)

+ 2 - 0
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -202,6 +202,7 @@ namespace MediaBrowser.Model.Configuration
         public bool DisplaySpecialsWithinSeasons { get; set; }
         public bool DisplayCollectionsView { get; set; }
         public string[] LocalNetworkAddresses { get; set; }
+        public string[] CodecsUsed { get; set; }
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
@@ -210,6 +211,7 @@ namespace MediaBrowser.Model.Configuration
         {
             LocalNetworkAddresses = new string[] { };
             Migrations = new string[] { };
+            CodecsUsed = new string[] { };
             SqliteCacheSize = 0;
 
             EnableLocalizedGuids = true;

+ 35 - 15
MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs

@@ -43,13 +43,6 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             _providerManager = providerManager;
         }
 
-        public Task<FileOrganizationResult> OrganizeEpisodeFile(string path, CancellationToken cancellationToken)
-        {
-            var options = _config.GetAutoOrganizeOptions();
-
-            return OrganizeEpisodeFile(path, options, false, cancellationToken);
-        }
-
         public async Task<FileOrganizationResult> OrganizeEpisodeFile(string path, AutoOrganizeOptions options, bool overwriteExisting, CancellationToken cancellationToken)
         {
             _logger.Info("Sorting file {0}", path);
@@ -63,6 +56,8 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
                 FileSize = new FileInfo(path).Length
             };
 
+            try
+            { 
             if (_libraryMonitor.IsPathLocked(path))
             {
                 result.Status = FileSortingStatus.Failure;
@@ -148,6 +143,12 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             }
 
             await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
+            }
+            catch (Exception ex)
+            {
+                result.Status = FileSortingStatus.Failure;
+                result.StatusMessage = ex.Message;
+            }
 
             return result;
         }
@@ -156,6 +157,8 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
         {
             var result = _organizationService.GetResult(request.ResultId);
 
+            try
+            { 
             Series series = null;
 
             if (request.NewSeriesProviderIds.Count > 0)
@@ -207,6 +210,12 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
                 cancellationToken).ConfigureAwait(false);
 
             await _organizationService.SaveResult(result, CancellationToken.None).ConfigureAwait(false);
+            }
+            catch (Exception ex)
+            {
+                result.Status = FileSortingStatus.Failure;
+                result.StatusMessage = ex.Message;
+            }
 
             return result;
         }
@@ -263,16 +272,15 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
 
             var originalExtractedSeriesString = result.ExtractedName;
 
+            try
+            {
             // Proceed to sort the file
             var newPath = await GetNewPath(sourcePath, series, seasonNumber, episodeNumber, endingEpiosdeNumber, premiereDate, options.TvOptions, cancellationToken).ConfigureAwait(false);
 
             if (string.IsNullOrEmpty(newPath))
             {
                 var msg = string.Format("Unable to sort {0} because target path could not be determined.", sourcePath);
-                result.Status = FileSortingStatus.Failure;
-                result.StatusMessage = msg;
-                _logger.Warn(msg);
-                return;
+                throw new Exception(msg);
             }
 
             _logger.Info("Sorting file {0} to new path {1}", sourcePath, newPath);
@@ -347,6 +355,14 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
                     }
                 }
             }
+            }
+            catch (Exception ex)
+            {
+                result.Status = FileSortingStatus.Failure;
+                result.StatusMessage = ex.Message;
+                _logger.Warn(ex.Message);
+                return;
+            }
 
             if (rememberCorrection)
             {
@@ -505,7 +521,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             }
             catch (Exception ex)
             {
-                var errorMsg = string.Format("Failed to move file from {0} to {1}", result.OriginalPath, result.TargetPath);
+                var errorMsg = string.Format("Failed to move file from {0} to {1}: {2}", result.OriginalPath, result.TargetPath, ex.Message);
 
                 result.Status = FileSortingStatus.Failure;
                 result.StatusMessage = errorMsg;
@@ -616,7 +632,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             {
                 var msg = string.Format("No provider metadata found for {0} season {1} episode {2}", series.Name, seasonNumber, episodeNumber);
                 _logger.Warn(msg);
-                return null;
+                throw new Exception(msg);
             }
 
             var episodeName = episode.Name;
@@ -715,6 +731,11 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
 
             var pattern = endingEpisodeNumber.HasValue ? options.MultiEpisodeNamePattern : options.EpisodeNamePattern;
 
+            if (string.IsNullOrWhiteSpace(pattern))
+            {
+                throw new Exception("GetEpisodeFileName: Configured episode name pattern is empty!");
+            }
+
             var result = pattern.Replace("%sn", seriesName)
                 .Replace("%s.n", seriesName.Replace(" ", "."))
                 .Replace("%s_n", seriesName.Replace(" ", "_"))
@@ -759,8 +780,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
                 // There may be cases where reducing the title length may still not be sufficient to
                 // stay below maxLength
                 var msg = string.Format("Unable to generate an episode file name shorter than {0} characters to constrain to the max path limit", maxLength);
-                _logger.Warn(msg);
-                return string.Empty;
+                throw new Exception(msg);
             }
 
             return result;

+ 12 - 2
MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs

@@ -112,8 +112,13 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager,
                 _libraryMonitor, _providerManager);
 
-            await organizer.OrganizeEpisodeFile(result.OriginalPath, GetAutoOrganizeOptions(), true, CancellationToken.None)
+            var organizeResult = await organizer.OrganizeEpisodeFile(result.OriginalPath, GetAutoOrganizeOptions(), true, CancellationToken.None)
                     .ConfigureAwait(false);
+
+            if (organizeResult.Status != FileSortingStatus.Success)
+            {
+                throw new Exception(result.StatusMessage);
+            }
         }
 
         public Task ClearLog()
@@ -126,7 +131,12 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
             var organizer = new EpisodeFileOrganizer(this, _config, _fileSystem, _logger, _libraryManager,
                 _libraryMonitor, _providerManager);
 
-            await organizer.OrganizeWithCorrection(request, GetAutoOrganizeOptions(), CancellationToken.None).ConfigureAwait(false);
+            var result = await organizer.OrganizeWithCorrection(request, GetAutoOrganizeOptions(), CancellationToken.None).ConfigureAwait(false);
+
+            if (result.Status != FileSortingStatus.Success)
+            {
+                throw new Exception(result.StatusMessage);
+            }
         }
 
         public QueryResult<SmartMatchInfo> GetSmartMatchInfos(FileOrganizationResultQuery query)

+ 5 - 2
MediaBrowser.Server.Implementations/Library/Validators/GameGenresPostScanTask.cs

@@ -3,6 +3,7 @@ using MediaBrowser.Model.Logging;
 using System;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Controller.Persistence;
 
 namespace MediaBrowser.Server.Implementations.Library.Validators
 {
@@ -16,16 +17,18 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         /// </summary>
         private readonly ILibraryManager _libraryManager;
         private readonly ILogger _logger;
+        private readonly IItemRepository _itemRepo;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="GameGenresPostScanTask" /> class.
         /// </summary>
         /// <param name="libraryManager">The library manager.</param>
         /// <param name="logger">The logger.</param>
-        public GameGenresPostScanTask(ILibraryManager libraryManager, ILogger logger)
+        public GameGenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
         {
             _libraryManager = libraryManager;
             _logger = logger;
+            _itemRepo = itemRepo;
         }
 
         /// <summary>
@@ -36,7 +39,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         /// <returns>Task.</returns>
         public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
         {
-            return new GameGenresValidator(_libraryManager, _logger).Run(progress, cancellationToken);
+            return new GameGenresValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
         }
     }
 }

+ 10 - 11
MediaBrowser.Server.Implementations/Library/Validators/GameGenresValidator.cs

@@ -5,6 +5,7 @@ using System;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Controller.Persistence;
 
 namespace MediaBrowser.Server.Implementations.Library.Validators
 {
@@ -19,11 +20,13 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         /// The _logger
         /// </summary>
         private readonly ILogger _logger;
+        private readonly IItemRepository _itemRepo;
 
-        public GameGenresValidator(ILibraryManager libraryManager, ILogger logger)
+        public GameGenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
         {
             _libraryManager = libraryManager;
             _logger = logger;
+            _itemRepo = itemRepo;
         }
 
         /// <summary>
@@ -34,21 +37,17 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         /// <returns>Task.</returns>
         public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
         {
-            var items = _libraryManager.GetGameGenres(new InternalItemsQuery
-            {
-                IncludeItemTypes = new[] { typeof(Game).Name }
-            })
-                .Items
-                .Select(i => i.Item1)
-                .ToList();
+            var names = _itemRepo.GetGameGenreNames();
 
             var numComplete = 0;
-            var count = items.Count;
+            var count = names.Count;
 
-            foreach (var item in items)
+            foreach (var name in names)
             {
                 try
                 {
+                    var item = _libraryManager.GetGameGenre(name);
+
                     await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
                 }
                 catch (OperationCanceledException)
@@ -58,7 +57,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
                 }
                 catch (Exception ex)
                 {
-                    _logger.ErrorException("Error refreshing {0}", ex, item.Name);
+                    _logger.ErrorException("Error refreshing {0}", ex, name);
                 }
 
                 numComplete++;

+ 5 - 2
MediaBrowser.Server.Implementations/Library/Validators/GenresPostScanTask.cs

@@ -2,6 +2,7 @@
 using System;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Controller.Persistence;
 using MediaBrowser.Model.Logging;
 
 namespace MediaBrowser.Server.Implementations.Library.Validators
@@ -13,16 +14,18 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         /// </summary>
         private readonly ILibraryManager _libraryManager;
         private readonly ILogger _logger;
+        private readonly IItemRepository _itemRepo;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
         /// </summary>
         /// <param name="libraryManager">The library manager.</param>
         /// <param name="logger">The logger.</param>
-        public GenresPostScanTask(ILibraryManager libraryManager, ILogger logger)
+        public GenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
         {
             _libraryManager = libraryManager;
             _logger = logger;
+            _itemRepo = itemRepo;
         }
 
         /// <summary>
@@ -33,7 +36,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         /// <returns>Task.</returns>
         public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
         {
-            return new GenresValidator(_libraryManager, _logger).Run(progress, cancellationToken);
+            return new GenresValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
         }
     }
 }

+ 10 - 11
MediaBrowser.Server.Implementations/Library/Validators/GenresValidator.cs

@@ -6,6 +6,7 @@ using System;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Controller.Persistence;
 
 namespace MediaBrowser.Server.Implementations.Library.Validators
 {
@@ -15,16 +16,18 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         /// The _library manager
         /// </summary>
         private readonly ILibraryManager _libraryManager;
+        private readonly IItemRepository _itemRepo;
 
         /// <summary>
         /// The _logger
         /// </summary>
         private readonly ILogger _logger;
 
-        public GenresValidator(ILibraryManager libraryManager, ILogger logger)
+        public GenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
         {
             _libraryManager = libraryManager;
             _logger = logger;
+            _itemRepo = itemRepo;
         }
 
         /// <summary>
@@ -35,21 +38,17 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         /// <returns>Task.</returns>
         public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
         {
-            var items = _libraryManager.GetGenres(new InternalItemsQuery
-            {
-                ExcludeItemTypes = new[] { typeof(Audio).Name, typeof(MusicArtist).Name, typeof(MusicAlbum).Name, typeof(MusicVideo).Name, typeof(Game).Name }
-            })
-                .Items
-                .Select(i => i.Item1)
-                .ToList();
+            var names = _itemRepo.GetGenreNames();
 
             var numComplete = 0;
-            var count = items.Count;
+            var count = names.Count;
 
-            foreach (var item in items)
+            foreach (var name in names)
             {
                 try
                 {
+                    var item = _libraryManager.GetGenre(name);
+
                     await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
                 }
                 catch (OperationCanceledException)
@@ -59,7 +58,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
                 }
                 catch (Exception ex)
                 {
-                    _logger.ErrorException("Error refreshing {0}", ex, item.Name);
+                    _logger.ErrorException("Error refreshing {0}", ex, name);
                 }
 
                 numComplete++;

+ 5 - 2
MediaBrowser.Server.Implementations/Library/Validators/MusicGenresPostScanTask.cs

@@ -3,6 +3,7 @@ using MediaBrowser.Model.Logging;
 using System;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Controller.Persistence;
 
 namespace MediaBrowser.Server.Implementations.Library.Validators
 {
@@ -16,16 +17,18 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         /// </summary>
         private readonly ILibraryManager _libraryManager;
         private readonly ILogger _logger;
+        private readonly IItemRepository _itemRepo;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ArtistsPostScanTask" /> class.
         /// </summary>
         /// <param name="libraryManager">The library manager.</param>
         /// <param name="logger">The logger.</param>
-        public MusicGenresPostScanTask(ILibraryManager libraryManager, ILogger logger)
+        public MusicGenresPostScanTask(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
         {
             _libraryManager = libraryManager;
             _logger = logger;
+            _itemRepo = itemRepo;
         }
 
         /// <summary>
@@ -36,7 +39,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         /// <returns>Task.</returns>
         public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
         {
-            return new MusicGenresValidator(_libraryManager, _logger).Run(progress, cancellationToken);
+            return new MusicGenresValidator(_libraryManager, _logger, _itemRepo).Run(progress, cancellationToken);
         }
     }
 }

+ 10 - 11
MediaBrowser.Server.Implementations/Library/Validators/MusicGenresValidator.cs

@@ -6,6 +6,7 @@ using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Persistence;
 
 namespace MediaBrowser.Server.Implementations.Library.Validators
 {
@@ -20,11 +21,13 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         /// The _logger
         /// </summary>
         private readonly ILogger _logger;
+        private readonly IItemRepository _itemRepo;
 
-        public MusicGenresValidator(ILibraryManager libraryManager, ILogger logger)
+        public MusicGenresValidator(ILibraryManager libraryManager, ILogger logger, IItemRepository itemRepo)
         {
             _libraryManager = libraryManager;
             _logger = logger;
+            _itemRepo = itemRepo;
         }
 
         /// <summary>
@@ -35,21 +38,17 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         /// <returns>Task.</returns>
         public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
         {
-            var items = _libraryManager.GetMusicGenres(new InternalItemsQuery
-            {
-                IncludeItemTypes = new[] { typeof(Audio).Name, typeof(MusicArtist).Name, typeof(MusicAlbum).Name, typeof(MusicVideo).Name }
-            })
-                .Items
-                .Select(i => i.Item1)
-                .ToList();
+            var names = _itemRepo.GetMusicGenreNames();
 
             var numComplete = 0;
-            var count = items.Count;
+            var count = names.Count;
 
-            foreach (var item in items)
+            foreach (var name in names)
             {
                 try
                 {
+                    var item = _libraryManager.GetMusicGenre(name);
+
                     await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
                 }
                 catch (OperationCanceledException)
@@ -59,7 +58,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
                 }
                 catch (Exception ex)
                 {
-                    _logger.ErrorException("Error refreshing {0}", ex, item.Name);
+                    _logger.ErrorException("Error refreshing {0}", ex, name);
                 }
 
                 numComplete++;

+ 1 - 1
MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -1139,7 +1139,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
 
                         var organize = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager, _libraryMonitor, _providerManager);
 
-                        var result = await organize.OrganizeEpisodeFile(path, CancellationToken.None).ConfigureAwait(false);
+                        var result = await organize.OrganizeEpisodeFile(path, _config.GetAutoOrganizeOptions(), false, CancellationToken.None).ConfigureAwait(false);
                     }
                     catch (Exception ex)
                     {

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

@@ -46,7 +46,7 @@
       <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
     </Reference>
     <Reference Include="Emby.XmlTv, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
-      <HintPath>..\packages\Emby.XmlTv.1.0.0.55\lib\net45\Emby.XmlTv.dll</HintPath>
+      <HintPath>..\packages\Emby.XmlTv.1.0.0.56\lib\net45\Emby.XmlTv.dll</HintPath>
       <Private>True</Private>
     </Reference>
     <Reference Include="INIFileParser, Version=2.3.0.0, Culture=neutral, PublicKeyToken=79af7b307b65cf3c, processorArchitecture=MSIL">

+ 35 - 4
MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs

@@ -3882,18 +3882,36 @@ namespace MediaBrowser.Server.Implementations.Persistence
 
         public List<string> GetStudioNames()
         {
-            return GetItemValueNames(new[] { 3 });
+            return GetItemValueNames(new[] { 3 }, new List<string>(), new List<string>());
         }
 
         public List<string> GetAllArtistNames()
         {
-            return GetItemValueNames(new[] { 0, 1 });
+            return GetItemValueNames(new[] { 0, 1 }, new List<string>(), new List<string>());
         }
 
-        private List<string> GetItemValueNames(int[] itemValueTypes)
+        public List<string> GetMusicGenreNames()
+        {
+            return GetItemValueNames(new[] { 2 }, new List<string> { "Audio", "MusicVideo", "MusicAlbum", "MusicArtist" }, new List<string>());
+        }
+
+        public List<string> GetGameGenreNames()
+        {
+            return GetItemValueNames(new[] { 2 }, new List<string> { "Game" }, new List<string>());
+        }
+
+        public List<string> GetGenreNames()
+        {
+            return GetItemValueNames(new[] { 2 }, new List<string>(), new List<string> { "Audio", "MusicVideo", "MusicAlbum", "MusicArtist", "Game", "GameSystem" });
+        }
+
+        private List<string> GetItemValueNames(int[] itemValueTypes, List<string> withItemTypes, List<string> excludeItemTypes)
         {
             CheckDisposed();
 
+            withItemTypes = withItemTypes.SelectMany(MapIncludeItemTypes).ToList();
+            excludeItemTypes = excludeItemTypes.SelectMany(MapIncludeItemTypes).ToList();
+
             var now = DateTime.UtcNow;
 
             var typeClause = itemValueTypes.Length == 1 ?
@@ -3904,7 +3922,20 @@ namespace MediaBrowser.Server.Implementations.Persistence
 
             using (var cmd = _connection.CreateCommand())
             {
-                cmd.CommandText = "Select Value From ItemValues where " + typeClause + " Group By CleanValue";
+                cmd.CommandText = "Select Value From ItemValues where " + typeClause;
+
+                if (withItemTypes.Count > 0)
+                {
+                    var typeString = string.Join(",", withItemTypes.Select(i => "'" + i + "'").ToArray());
+                    cmd.CommandText += " AND ItemId In (select guid from typedbaseitems where type in (" + typeString + "))";
+                }
+                if (excludeItemTypes.Count > 0)
+                {
+                    var typeString = string.Join(",", excludeItemTypes.Select(i => "'" + i + "'").ToArray());
+                    cmd.CommandText += " AND ItemId not In (select guid from typedbaseitems where type in (" + typeString + "))";
+                }
+
+                cmd.CommandText += " Group By CleanValue";
 
                 var commandBehavior = CommandBehavior.SequentialAccess | CommandBehavior.SingleResult;
 

+ 1 - 1
MediaBrowser.Server.Implementations/packages.config

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
-  <package id="Emby.XmlTv" version="1.0.0.55" targetFramework="net45" />
+  <package id="Emby.XmlTv" version="1.0.0.56" targetFramework="net45" />
   <package id="ini-parser" version="2.3.0" targetFramework="net45" />
   <package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
   <package id="MediaBrowser.Naming" version="1.0.0.55" targetFramework="net45" />

+ 3 - 0
MediaBrowser.Server.Startup.Common/ApplicationHost.cs

@@ -101,6 +101,7 @@ using System.Reflection;
 using System.Threading;
 using System.Threading.Tasks;
 using CommonIO;
+using MediaBrowser.Api.Playback;
 using MediaBrowser.Common.Implementations.Updates;
 
 namespace MediaBrowser.Server.Startup.Common
@@ -766,6 +767,8 @@ namespace MediaBrowser.Server.Startup.Common
             BaseItem.CollectionManager = CollectionManager;
             BaseItem.MediaSourceManager = MediaSourceManager;
             CollectionFolder.XmlSerializer = XmlSerializer;
+            BaseStreamingService.AppHost = this;
+            BaseStreamingService.HttpClient = HttpClient;
         }
 
         /// <summary>