diff --git a/Emby.Common.Implementations/Networking/NetworkManager.cs b/Emby.Common.Implementations/Networking/NetworkManager.cs index 2b84b2aa1..2f218656c 100644 --- a/Emby.Common.Implementations/Networking/NetworkManager.cs +++ b/Emby.Common.Implementations/Networking/NetworkManager.cs @@ -500,11 +500,7 @@ namespace Emby.Common.Implementations.Networking { return IpAddressInfo.IPv6Loopback; } - return new IpAddressInfo - { - Address = address.ToString(), - AddressFamily = address.AddressFamily == AddressFamily.InterNetworkV6 ? IpAddressFamily.InterNetworkV6 : IpAddressFamily.InterNetwork - }; + return new IpAddressInfo(address.ToString(), address.AddressFamily == AddressFamily.InterNetworkV6 ? IpAddressFamily.InterNetworkV6 : IpAddressFamily.InterNetwork); } public async Task GetHostAddressesAsync(string host) diff --git a/Emby.Server.Implementations/FileOrganization/TvFolderOrganizer.cs b/Emby.Server.Implementations/FileOrganization/TvFolderOrganizer.cs index b035fdcfd..c885f03ac 100644 --- a/Emby.Server.Implementations/FileOrganization/TvFolderOrganizer.cs +++ b/Emby.Server.Implementations/FileOrganization/TvFolderOrganizer.cs @@ -53,9 +53,29 @@ namespace Emby.Server.Implementations.FileOrganization return false; } + private bool IsValidWatchLocation(string path, List libraryFolderPaths) + { + if (IsPathAlreadyInMediaLibrary(path, libraryFolderPaths)) + { + _logger.Info("Folder {0} is not eligible for auto-organize because it is also part of an Emby library", path); + return false; + } + + return true; + } + + private bool IsPathAlreadyInMediaLibrary(string path, List libraryFolderPaths) + { + return libraryFolderPaths.Any(i => string.Equals(i, path, StringComparison.Ordinal) || _fileSystem.ContainsSubPath(i, path)); + } + public async Task Organize(AutoOrganizeOptions options, CancellationToken cancellationToken, IProgress progress) { - var watchLocations = options.TvOptions.WatchLocations.ToList(); + var libraryFolderPaths = _libraryManager.GetVirtualFolders().SelectMany(i => i.Locations).ToList(); + + var watchLocations = options.TvOptions.WatchLocations + .Where(i => IsValidWatchLocation(i, libraryFolderPaths)) + .ToList(); var eligibleFiles = watchLocations.SelectMany(GetFilesToOrganize) .OrderBy(_fileSystem.GetCreationTimeUtc) @@ -72,17 +92,24 @@ namespace Emby.Server.Implementations.FileOrganization foreach (var file in eligibleFiles) { + cancellationToken.ThrowIfCancellationRequested(); + var organizer = new EpisodeFileOrganizer(_organizationService, _config, _fileSystem, _logger, _libraryManager, _libraryMonitor, _providerManager); try { var result = await organizer.OrganizeEpisodeFile(file.FullName, options, options.TvOptions.OverwriteExistingEpisodes, cancellationToken).ConfigureAwait(false); + if (result.Status == FileSortingStatus.Success && !processedFolders.Contains(file.DirectoryName, StringComparer.OrdinalIgnoreCase)) { processedFolders.Add(file.DirectoryName); } } + catch (OperationCanceledException) + { + break; + } catch (Exception ex) { _logger.ErrorException("Error organizing episode {0}", ex, file.FullName); diff --git a/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs b/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs index 9a8a930bd..3906df000 100644 --- a/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; namespace Emby.Server.Implementations.LiveTv @@ -29,6 +30,8 @@ namespace Emby.Server.Implementations.LiveTv var now = DateTime.UtcNow; + var allowVideoStreamCopy = mediaSource.MediaStreams.Any(i => i.Type == MediaStreamType.Video && i.AllowStreamCopy); + var info = await _mediaEncoder.GetMediaInfo(new MediaInfoRequest { InputPath = mediaSource.Path, @@ -73,6 +76,8 @@ namespace Emby.Server.Implementations.LiveTv var videoStream = mediaSource.MediaStreams.FirstOrDefault(i => i.Type == MediaBrowser.Model.Entities.MediaStreamType.Video); if (videoStream != null) { + videoStream.AllowStreamCopy = allowVideoStreamCopy; + if (!videoStream.BitRate.HasValue) { var width = videoStream.Width ?? 1920; diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index b9e73b62e..1c43b4188 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -261,7 +261,7 @@ namespace Emby.Server.Implementations.LiveTv return info.Item1; } - public Task> GetChannelStream(string id, string mediaSourceId, CancellationToken cancellationToken) + public Task> GetChannelStream(string id, string mediaSourceId, CancellationToken cancellationToken) { return GetLiveStream(id, mediaSourceId, true, cancellationToken); } @@ -323,7 +323,7 @@ namespace Emby.Server.Implementations.LiveTv return _services.FirstOrDefault(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase)); } - private async Task> GetLiveStream(string id, string mediaSourceId, bool isChannel, CancellationToken cancellationToken) + private async Task> GetLiveStream(string id, string mediaSourceId, bool isChannel, CancellationToken cancellationToken) { if (string.Equals(id, mediaSourceId, StringComparison.OrdinalIgnoreCase)) { @@ -334,7 +334,6 @@ namespace Emby.Server.Implementations.LiveTv bool isVideo; ILiveTvService service; IDirectStreamProvider directStreamProvider = null; - var assumeInterlaced = false; if (isChannel) { @@ -383,12 +382,7 @@ namespace Emby.Server.Implementations.LiveTv Normalize(info, service, isVideo); - if (!(service is EmbyTV.EmbyTV)) - { - assumeInterlaced = true; - } - - return new Tuple(info, directStreamProvider, assumeInterlaced); + return new Tuple(info, directStreamProvider); } private void Normalize(MediaSourceInfo mediaSource, ILiveTvService service, bool isVideo) @@ -492,6 +486,12 @@ namespace Emby.Server.Implementations.LiveTv { stream.NalLengthSize = "0"; } + + if (stream.Type == MediaStreamType.Video) + { + stream.IsInterlaced = true; + stream.AllowStreamCopy = false; + } } } } diff --git a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs index 747e0fdd3..a9c449f83 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvMediaSourceProvider.cs @@ -104,7 +104,7 @@ namespace Emby.Server.Implementations.LiveTv openKeys.Add(item.Id.ToString("N")); openKeys.Add(source.Id ?? string.Empty); source.OpenToken = string.Join(StreamIdDelimeterString, openKeys.ToArray()); - } + } // Dummy this up so that direct play checks can still run if (string.IsNullOrEmpty(source.Path) && source.Protocol == MediaProtocol.Http) @@ -142,7 +142,7 @@ namespace Emby.Server.Implementations.LiveTv { if (!stream.SupportsProbing || stream.MediaStreams.Any(i => i.Index != -1)) { - await AddMediaInfo(stream, isAudio, cancellationToken).ConfigureAwait(false); + AddMediaInfo(stream, isAudio, cancellationToken); } else { @@ -158,7 +158,7 @@ namespace Emby.Server.Implementations.LiveTv return new Tuple(stream, directStreamProvider); } - private async Task AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken) + private void AddMediaInfo(MediaSourceInfo mediaSource, bool isAudio, CancellationToken cancellationToken) { mediaSource.DefaultSubtitleStreamIndex = null; diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index 5242c5b1f..c8fa6be8c 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -157,7 +157,7 @@ namespace MediaBrowser.Controller.LiveTv /// The media source identifier. /// The cancellation token. /// Task{StreamResponseInfo}. - Task> GetChannelStream(string id, string mediaSourceId, CancellationToken cancellationToken); + Task> GetChannelStream(string id, string mediaSourceId, CancellationToken cancellationToken); /// /// Gets the program. diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 530ff1343..dd31d39b1 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -743,6 +743,11 @@ namespace MediaBrowser.Controller.MediaEncoding public bool CanStreamCopyVideo(EncodingJobInfo state, MediaStream videoStream) { + if (!videoStream.AllowStreamCopy) + { + return false; + } + var request = state.BaseRequest; if (videoStream.IsInterlaced) @@ -883,6 +888,11 @@ namespace MediaBrowser.Controller.MediaEncoding public bool CanStreamCopyAudio(EncodingJobInfo state, MediaStream audioStream, List supportedAudioCodecs) { + if (!audioStream.AllowStreamCopy) + { + return false; + } + var request = state.BaseRequest; // Source and target codecs must match diff --git a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs index 8ae20f604..f147f1d61 100644 --- a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs +++ b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs @@ -268,7 +268,7 @@ namespace MediaBrowser.LocalMetadata.Images { var imageFiles = _fileSystem.GetFiles(path, BaseItem.SupportedImageExtensions, false, false); - images.AddRange(imageFiles.Select(i => new LocalImageInfo + images.AddRange(imageFiles.Where(i => i.Length > 0).Select(i => new LocalImageInfo { FileInfo = i, Type = ImageType.Backdrop @@ -398,7 +398,7 @@ namespace MediaBrowser.LocalMetadata.Images private FileSystemMetadata GetImage(IEnumerable files, string name) { - return files.FirstOrDefault(i => !i.IsDirectory && string.Equals(name, _fileSystem.GetFileNameWithoutExtension(i), StringComparison.OrdinalIgnoreCase)); + return files.FirstOrDefault(i => !i.IsDirectory && string.Equals(name, _fileSystem.GetFileNameWithoutExtension(i), StringComparison.OrdinalIgnoreCase) && i.Length > 0); } } } diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 3cd3e7dde..133b9e566 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -13,6 +13,11 @@ namespace MediaBrowser.Model.Entities [DebuggerDisplay("StreamType = {Type}")] public class MediaStream { + public MediaStream() + { + AllowStreamCopy = true; + } + /// /// Gets or sets the codec. /// @@ -153,6 +158,8 @@ namespace MediaBrowser.Model.Entities public bool? IsAVC { get; set; } + public bool AllowStreamCopy { get; set; } + /// /// Gets or sets the channel layout. /// diff --git a/MediaBrowser.Model/Net/IpAddressInfo.cs b/MediaBrowser.Model/Net/IpAddressInfo.cs index 00a16c03d..57a0039c4 100644 --- a/MediaBrowser.Model/Net/IpAddressInfo.cs +++ b/MediaBrowser.Model/Net/IpAddressInfo.cs @@ -12,13 +12,13 @@ namespace MediaBrowser.Model.Net public string Address { get; set; } public IpAddressFamily AddressFamily { get; set; } - public IpAddressInfo() - { - - } - public IpAddressInfo(string address, IpAddressFamily addressFamily) { + if (string.IsNullOrWhiteSpace(address)) + { + throw new ArgumentNullException("address"); + } + Address = address; AddressFamily = addressFamily; } diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index cc464e689..e9dc4c54f 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -247,10 +247,7 @@ namespace Rssdp.Infrastructure { await SendMessageIfSocketNotDisposed(messageData, new IpEndPointInfo { - IpAddress = new IpAddressInfo - { - Address = SsdpConstants.MulticastLocalAdminAddress - }, + IpAddress = new IpAddressInfo(SsdpConstants.MulticastLocalAdminAddress, IpAddressFamily.InterNetwork), Port = SsdpConstants.MulticastPort }, cancellationToken).ConfigureAwait(false); diff --git a/SharedVersion.cs b/SharedVersion.cs index f2c3a7cb0..d2454d088 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.9.2")] +[assembly: AssemblyVersion("3.2.9.3")]