diff --git a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs index b1205710a..c3810a9e6 100644 --- a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs +++ b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs @@ -15,6 +15,7 @@ using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Threading.Tasks; using MediaBrowser.Controller.MediaEncoding; using Rssdp; @@ -220,9 +221,11 @@ namespace MediaBrowser.Dlna.Main } var cacheLength = _config.GetDlnaConfiguration().BlastAliveMessageIntervalSeconds * 2; - _Publisher.SupportPnpRootDevice = true; + _Publisher.SupportPnpRootDevice = false; - foreach (var address in await _appHost.GetLocalIpAddresses().ConfigureAwait(false)) + var addresses = (await _appHost.GetLocalIpAddresses().ConfigureAwait(false)).ToList(); + + foreach (var address in addresses) { //if (IPAddress.IsLoopback(address)) //{ @@ -334,17 +337,11 @@ namespace MediaBrowser.Dlna.Main { var devices = _Publisher.Devices.ToList(); - Parallel.ForEach(devices, device => + foreach (var device in devices) { - try - { - _Publisher.RemoveDevice(device); - } - catch (Exception ex) - { - _logger.ErrorException("Error sending bye bye", ex); - } - }); + var task = _Publisher.RemoveDevice(device); + Task.WaitAll(task); + } //foreach (var device in devices) //{ // try diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs index 4c6254330..207486f26 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs @@ -54,7 +54,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV { if (args.IsDirectory) { - if (args.HasParent()) + if (args.HasParent() || args.HasParent()) { return null; } @@ -80,7 +80,8 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV { return null; } - if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, args.GetLibraryOptions(), false)) + if (IsSeriesFolder(args.Path, args.FileSystemChildren, args.DirectoryService, _fileSystem, _logger, _libraryManager, args.GetLibraryOptions(), false) || + args.ContainsFileSystemEntryByName("tvshow.nfo")) { return new Series { diff --git a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 3eb032ba8..b508110cf 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -22,12 +22,15 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Xml; using CommonIO; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.FileOrganization; using Microsoft.Win32; namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV @@ -867,10 +870,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV } } - private string GetRecordingPath(TimerInfo timer) + private string GetRecordingPath(TimerInfo timer, out string seriesPath) { var recordPath = RecordingPath; var config = GetConfiguration(); + seriesPath = null; if (timer.IsProgramSeries) { @@ -892,6 +896,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV // Can't use the year here in the folder name because it is the year of the episode, not the series. recordPath = Path.Combine(recordPath, folderName); + seriesPath = recordPath; + if (timer.SeasonNumber.HasValue) { folderName = string.Format("Season {0}", timer.SeasonNumber.Value.ToString(CultureInfo.InvariantCulture)); @@ -980,7 +986,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV RecordingHelper.CopyProgramInfoToTimerInfo(programInfo, timer); } - var recordPath = GetRecordingPath(timer); + string seriesPath = null; + var recordPath = GetRecordingPath(timer, out seriesPath); var recordingStatus = RecordingStatus.New; var isResourceOpen = false; SemaphoreSlim semaphore = null; @@ -1064,7 +1071,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV timer.Status = RecordingStatus.Completed; _timerProvider.Delete(timer); - OnSuccessfulRecording(timer.IsProgramSeries, recordPath); + OnSuccessfulRecording(timer, recordPath, seriesPath); } else if (DateTime.UtcNow < timer.EndDate) { @@ -1132,27 +1139,73 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV return new DirectRecorder(_logger, _httpClient, _fileSystem); } - private async void OnSuccessfulRecording(bool isSeries, string path) + private async void OnSuccessfulRecording(TimerInfo timer, string path, string seriesPath) { - if (GetConfiguration().EnableAutoOrganize) + if (timer.IsProgramSeries && GetConfiguration().EnableAutoOrganize) { - if (isSeries) + try { - try - { - // this is to account for the library monitor holding a lock for additional time after the change is complete. - // ideally this shouldn't be hard-coded - await Task.Delay(30000).ConfigureAwait(false); + // this is to account for the library monitor holding a lock for additional time after the change is complete. + // ideally this shouldn't be hard-coded + await Task.Delay(30000).ConfigureAwait(false); - var organize = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager, _libraryMonitor, _providerManager); + var organize = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager, _libraryMonitor, _providerManager); - var result = await organize.OrganizeEpisodeFile(path, _config.GetAutoOrganizeOptions(), false, CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) + var result = await organize.OrganizeEpisodeFile(path, _config.GetAutoOrganizeOptions(), false, CancellationToken.None).ConfigureAwait(false); + + if (result.Status == FileSortingStatus.Success) { - _logger.ErrorException("Error processing new recording", ex); + return; } } + catch (Exception ex) + { + _logger.ErrorException("Error processing new recording", ex); + } + } + + SaveNfo(timer, path, seriesPath); + } + + private void SaveNfo(TimerInfo timer, string recordingPath, string seriesPath) + { + if (timer.IsProgramSeries) + { + SaveSeriesNfo(timer, recordingPath, seriesPath); + } + } + + private void SaveSeriesNfo(TimerInfo timer, string recordingPath, string seriesPath) + { + var nfoPath = Path.Combine(seriesPath, "tvshow.nfo"); + + if (File.Exists(nfoPath)) + { + return; + } + + using (var stream = _fileSystem.GetFileStream(nfoPath, FileMode.Create, FileAccess.Write, FileShare.Read)) + { + var settings = new XmlWriterSettings + { + Indent = true, + Encoding = Encoding.UTF8, + CloseOutput = false + }; + + using (XmlWriter writer = XmlWriter.Create(stream, settings)) + { + writer.WriteStartDocument(true); + writer.WriteStartElement("tvshow"); + + if (!string.IsNullOrWhiteSpace(timer.Name)) + { + writer.WriteElementString("title", timer.Name); + } + + writer.WriteEndElement(); + writer.WriteEndDocument(); + } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index d863c4587..c30263388 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -171,22 +171,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings var data = images[imageIndex].data ?? new List(); data = data.OrderByDescending(GetSizeOrder).ToList(); - programEntry.primaryImage = GetProgramImage(ApiUrl, data, "Logo", true); + programEntry.primaryImage = GetProgramImage(ApiUrl, data, "Logo", true, 1280); //programEntry.thumbImage = GetProgramImage(ApiUrl, data, "Iconic", false); //programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ?? // GetProgramImage(ApiUrl, data, "Banner-L1", false) ?? // GetProgramImage(ApiUrl, data, "Banner-LO", false) ?? // GetProgramImage(ApiUrl, data, "Banner-LOT", false); - - if (!string.IsNullOrWhiteSpace(programEntry.thumbImage)) - { - var b = true; - } - - if (!string.IsNullOrWhiteSpace(programEntry.bannerImage)) - { - var b = true; - } } } @@ -201,10 +191,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings private int GetSizeOrder(ScheduleDirect.ImageData image) { - if (!string.IsNullOrWhiteSpace(image.size)) + if (!string.IsNullOrWhiteSpace(image.height)) { int value; - if (int.TryParse(image.size, out value)) + if (int.TryParse(image.height, out value)) { return value; } @@ -517,20 +507,43 @@ namespace MediaBrowser.Server.Implementations.LiveTv.Listings return date; } - private string GetProgramImage(string apiUrl, List images, string category, bool returnDefaultImage) + private string GetProgramImage(string apiUrl, List images, string category, bool returnDefaultImage, int desiredWidth) { string url = null; - var logoIndex = images.FindIndex(i => string.Equals(i.category, category, StringComparison.OrdinalIgnoreCase)); - if (logoIndex == -1) + var matches = images + .Where(i => string.Equals(i.category, category, StringComparison.OrdinalIgnoreCase)) + .ToList(); + + if (matches.Count == 0) { if (!returnDefaultImage) { return null; } - logoIndex = 0; + matches = images; } - var uri = images[logoIndex].uri; + + var match = matches.FirstOrDefault(i => + { + if (!string.IsNullOrWhiteSpace(i.width)) + { + int value; + if (int.TryParse(i.width, out value)) + { + return value <= desiredWidth; + } + } + + return false; + }) ?? matches.FirstOrDefault(); + + if (match == null) + { + return null; + } + + var uri = match.uri; if (!string.IsNullOrWhiteSpace(uri)) { diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index 5d4fba32d..90e688733 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -33,6 +33,7 @@ namespace MediaBrowser.ServerApplication private static ILogger _logger; private static bool _isRunningAsService = false; + private static bool _canRestartService = false; private static bool _appHostDisposed; [DllImport("kernel32.dll", SetLastError = true)] @@ -68,6 +69,11 @@ namespace MediaBrowser.ServerApplication var options = new StartupOptions(); _isRunningAsService = options.ContainsOption("-service"); + if (_isRunningAsService) + { + _canRestartService = CanRestartWindowsService(); + } + var currentProcess = Process.GetCurrentProcess(); var applicationPath = currentProcess.MainModule.FileName; @@ -261,7 +267,14 @@ namespace MediaBrowser.ServerApplication { get { - return !_isRunningAsService; + if (_isRunningAsService) + { + return _canRestartService; + } + else + { + return true; + } } } @@ -273,7 +286,14 @@ namespace MediaBrowser.ServerApplication { get { - return !_isRunningAsService; + if (_isRunningAsService) + { + return _canRestartService; + } + else + { + return true; + } } } @@ -614,7 +634,11 @@ namespace MediaBrowser.ServerApplication { DisposeAppHost(); - if (!_isRunningAsService) + if (_isRunningAsService) + { + RestartWindowsService(); + } + else { //_logger.Info("Hiding server notify icon"); //_serverNotifyIcon.Visible = false; @@ -669,6 +693,47 @@ namespace MediaBrowser.ServerApplication } } + private static void RestartWindowsService() + { + _logger.Info("Restarting background service"); + + var startInfo = new ProcessStartInfo + { + FileName = "cmd.exe", + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + Verb = "runas", + ErrorDialog = false, + Arguments = String.Format("/c sc stop {0} & sc start {0}", BackgroundService.GetExistingServiceName()) + }; + Process.Start(startInfo); + } + + private static bool CanRestartWindowsService() + { + var startInfo = new ProcessStartInfo + { + FileName = "cmd.exe", + CreateNoWindow = true, + WindowStyle = ProcessWindowStyle.Hidden, + Verb = "runas", + ErrorDialog = false, + Arguments = String.Format("/c sc query {0}", BackgroundService.GetExistingServiceName()) + }; + using (var process = Process.Start(startInfo)) + { + process.WaitForExit(); + if (process.ExitCode == 0) + { + return true; + } + else + { + return false; + } + } + } + private static async Task InstallVcredist2013IfNeeded(ApplicationHost appHost, ILogger logger) { // Reference