aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Common.Implementations/IO/LnkShortcutHandler.cs (renamed from MediaBrowser.ServerApplication/Native/LnkShortcutHandler.cs)3
-rw-r--r--Emby.Common.Implementations/IO/ManagedFileSystem.cs11
-rw-r--r--Emby.Common.Implementations/Net/NetAcceptSocket.cs43
-rw-r--r--Emby.Common.Implementations/Net/SocketFactory.cs28
-rw-r--r--Emby.Common.Implementations/Net/UdpSocket.cs18
-rw-r--r--Emby.Dlna/Didl/DidlBuilder.cs4
-rw-r--r--Emby.Dlna/PlayTo/PlayToController.cs2
-rw-r--r--Emby.Dlna/Profiles/DefaultProfile.cs6
-rw-r--r--Emby.Dlna/Profiles/DirectTvProfile.cs9
-rw-r--r--Emby.Dlna/Profiles/DishHopperJoeyProfile.cs8
-rw-r--r--Emby.Dlna/Profiles/LinksysDMA2100Profile.cs9
-rw-r--r--Emby.Dlna/Profiles/PopcornHourProfile.cs9
-rw-r--r--Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs9
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2010Profile.cs9
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2011Profile.cs9
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2012Profile.cs9
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2013Profile.cs9
-rw-r--r--Emby.Dlna/Profiles/SonyBravia2014Profile.cs9
-rw-r--r--Emby.Dlna/Profiles/SonyPs3Profile.cs9
-rw-r--r--Emby.Dlna/Profiles/SonyPs4Profile.cs9
-rw-r--r--Emby.Dlna/Profiles/Xbox360Profile.cs9
-rw-r--r--Emby.Dlna/Profiles/XboxOneProfile.cs9
-rw-r--r--Emby.Dlna/Profiles/Xml/Default.xml1
-rw-r--r--Emby.Dlna/Profiles/Xml/Denon AVR.xml1
-rw-r--r--Emby.Dlna/Profiles/Xml/MediaMonkey.xml1
-rw-r--r--Emby.Dlna/Profiles/Xml/foobar2000.xml1
-rw-r--r--Emby.Drawing.ImageMagick/ImageMagickEncoder.cs4
-rw-r--r--Emby.Drawing/ImageProcessor.cs20
-rw-r--r--Emby.Server.Core/ApplicationHost.cs59
-rw-r--r--Emby.Server.Core/HttpServerFactory.cs4
-rw-r--r--Emby.Server.Core/IO/LibraryMonitor.cs2
-rw-r--r--Emby.Server.Core/Localization/TextLocalizer.cs10
-rw-r--r--Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs32
-rw-r--r--Emby.Server.Implementations/Devices/DeviceManager.cs7
-rw-r--r--Emby.Server.Implementations/Dto/DtoService.cs4
-rw-r--r--Emby.Server.Implementations/Emby.Server.Implementations.csproj7
-rw-r--r--Emby.Server.Implementations/HttpServer/FileWriter.cs191
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs11
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpResultFactory.cs42
-rw-r--r--Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs6
-rw-r--r--Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs8
-rw-r--r--Emby.Server.Implementations/IO/FileRefresher.cs8
-rw-r--r--Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs2
-rw-r--r--Emby.Server.Implementations/Library/LibraryManager.cs65
-rw-r--r--Emby.Server.Implementations/Library/UserManager.cs5
-rw-r--r--Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs91
-rw-r--r--Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs9
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs7
-rw-r--r--Emby.Server.Implementations/LiveTv/LiveTvManager.cs66
-rw-r--r--Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs2
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs27
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs158
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs115
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs125
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs10
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs17
-rw-r--r--Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs19
-rw-r--r--Emby.Server.Implementations/Localization/LocalizationManager.cs1
-rw-r--r--Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs32
-rw-r--r--Emby.Server.Implementations/Services/ResponseHelper.cs82
-rw-r--r--Emby.Server.Implementations/Windows/LoopUtil.cs (renamed from MediaBrowser.ServerApplication/Native/LoopbackUtil.cs)8
-rw-r--r--Emby.Server.Implementations/packages.config2
-rw-r--r--MediaBrowser.Api/LiveTv/LiveTvService.cs66
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs14
-rw-r--r--MediaBrowser.Api/Playback/Hls/BaseHlsService.cs28
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs41
-rw-r--r--MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs2
-rw-r--r--MediaBrowser.Api/Playback/Hls/VideoHlsService.cs12
-rw-r--r--MediaBrowser.Api/Playback/MediaInfoService.cs68
-rw-r--r--MediaBrowser.Api/Playback/Progressive/AudioService.cs3
-rw-r--r--MediaBrowser.Api/Playback/StreamRequest.cs1
-rw-r--r--MediaBrowser.Api/StartupWizardService.cs76
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicArtist.cs7
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicGenre.cs7
-rw-r--r--MediaBrowser.Controller/Entities/GameGenre.cs7
-rw-r--r--MediaBrowser.Controller/Entities/Genre.cs7
-rw-r--r--MediaBrowser.Controller/Entities/IHasImages.cs2
-rw-r--r--MediaBrowser.Controller/Entities/Person.cs7
-rw-r--r--MediaBrowser.Controller/Entities/Studio.cs7
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs3
-rw-r--r--MediaBrowser.Controller/Entities/Year.cs7
-rw-r--r--MediaBrowser.Controller/LiveTv/ILiveTvManager.cs11
-rw-r--r--MediaBrowser.Controller/LiveTv/ITunerHost.cs2
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs81
-rw-r--r--MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs4
-rw-r--r--MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs8
-rw-r--r--MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs2
-rw-r--r--MediaBrowser.Controller/Net/StaticResultOptions.cs13
-rw-r--r--MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs2
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs7
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs10
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs57
-rw-r--r--MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs29
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs19
-rw-r--r--MediaBrowser.Model/Configuration/LibraryOptions.cs2
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs1
-rw-r--r--MediaBrowser.Model/Dlna/ProfileConditionValue.cs3
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs34
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs52
-rw-r--r--MediaBrowser.Model/Dto/MediaSourceInfo.cs2
-rw-r--r--MediaBrowser.Model/Dto/UserDto.cs4
-rw-r--r--MediaBrowser.Model/IO/IFileSystem.cs2
-rw-r--r--MediaBrowser.Model/LiveTv/LiveTvOptions.cs4
-rw-r--r--MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs2
-rw-r--r--MediaBrowser.Model/Net/IAcceptSocket.cs3
-rw-r--r--MediaBrowser.Model/Net/ISocketFactory.cs2
-rw-r--r--MediaBrowser.Model/Net/MimeTypes.cs3
-rw-r--r--MediaBrowser.Model/Querying/ItemFields.cs2
-rw-r--r--MediaBrowser.Model/Services/IAsyncStreamWriter.cs5
-rw-r--r--MediaBrowser.Model/Services/IRequest.cs6
-rw-r--r--MediaBrowser.Providers/Manager/ImageSaver.cs1
-rw-r--r--MediaBrowser.Providers/Manager/MetadataService.cs21
-rw-r--r--MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs120
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs2
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs2
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs2
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs2
-rw-r--r--MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs46
-rw-r--r--MediaBrowser.Server.Mac/Emby.Server.Mac.csproj24
-rw-r--r--MediaBrowser.Server.Mac/MacAppHost.cs36
-rw-r--r--MediaBrowser.Server.Mac/Main.cs7
-rw-r--r--MediaBrowser.Server.Mono/Native/MonoFileSystem.cs5
-rw-r--r--MediaBrowser.Server.Mono/Program.cs7
-rw-r--r--MediaBrowser.ServerApplication/MainStartup.cs8
-rw-r--r--MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj2
-rw-r--r--MediaBrowser.ServerApplication/WindowsAppHost.cs7
-rw-r--r--Nuget/MediaBrowser.Common.nuspec2
-rw-r--r--Nuget/MediaBrowser.Server.Core.nuspec4
-rw-r--r--SharedVersion.cs2
-rw-r--r--SocketHttpListener.Portable/Net/EndPointListener.cs6
-rw-r--r--SocketHttpListener.Portable/Net/EndPointManager.cs3
-rw-r--r--SocketHttpListener.Portable/Net/HttpConnection.cs16
-rw-r--r--SocketHttpListener.Portable/Net/HttpListener.cs12
-rw-r--r--SocketHttpListener.Portable/Net/HttpListenerContext.cs6
-rw-r--r--SocketHttpListener.Portable/Net/HttpListenerRequest.cs2
-rw-r--r--SocketHttpListener.Portable/Net/HttpListenerResponse.cs14
-rw-r--r--SocketHttpListener.Portable/Net/ResponseStream.cs84
137 files changed, 1731 insertions, 951 deletions
diff --git a/MediaBrowser.ServerApplication/Native/LnkShortcutHandler.cs b/Emby.Common.Implementations/IO/LnkShortcutHandler.cs
index b4a87b9b4..5d5f46057 100644
--- a/MediaBrowser.ServerApplication/Native/LnkShortcutHandler.cs
+++ b/Emby.Common.Implementations/IO/LnkShortcutHandler.cs
@@ -6,7 +6,7 @@ using System.Security;
using System.Text;
using MediaBrowser.Model.IO;
-namespace MediaBrowser.ServerApplication.Native
+namespace Emby.Common.Implementations.IO
{
public class LnkShortcutHandler :IShortcutHandler
{
@@ -35,7 +35,6 @@ namespace MediaBrowser.ServerApplication.Native
/// <summary>
/// Class NativeMethods
/// </summary>
- [SuppressUnmanagedCodeSecurity]
public static class NativeMethods
{
/// <summary>
diff --git a/Emby.Common.Implementations/IO/ManagedFileSystem.cs b/Emby.Common.Implementations/IO/ManagedFileSystem.cs
index 3fe20f659..2c9388a41 100644
--- a/Emby.Common.Implementations/IO/ManagedFileSystem.cs
+++ b/Emby.Common.Implementations/IO/ManagedFileSystem.cs
@@ -5,6 +5,7 @@ using System.Linq;
using System.Text;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.System;
namespace Emby.Common.Implementations.IO
{
@@ -18,17 +19,17 @@ namespace Emby.Common.Implementations.IO
private readonly bool _supportsAsyncFileStreams;
private char[] _invalidFileNameChars;
private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>();
- private bool EnableFileSystemRequestConcat = true;
+ private bool EnableFileSystemRequestConcat;
private string _tempPath;
- public ManagedFileSystem(ILogger logger, bool supportsAsyncFileStreams, bool enableManagedInvalidFileNameChars, bool enableFileSystemRequestConcat, string tempPath)
+ public ManagedFileSystem(ILogger logger, IEnvironmentInfo environmentInfo, string tempPath)
{
Logger = logger;
- _supportsAsyncFileStreams = supportsAsyncFileStreams;
+ _supportsAsyncFileStreams = true;
_tempPath = tempPath;
- EnableFileSystemRequestConcat = enableFileSystemRequestConcat;
- SetInvalidFileNameChars(enableManagedInvalidFileNameChars);
+ EnableFileSystemRequestConcat = false;
+ SetInvalidFileNameChars(environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows);
}
public void AddShortcutHandler(IShortcutHandler handler)
diff --git a/Emby.Common.Implementations/Net/NetAcceptSocket.cs b/Emby.Common.Implementations/Net/NetAcceptSocket.cs
index 731ad1b74..3721709e6 100644
--- a/Emby.Common.Implementations/Net/NetAcceptSocket.cs
+++ b/Emby.Common.Implementations/Net/NetAcceptSocket.cs
@@ -2,6 +2,7 @@
using System.Net;
using System.Net.Sockets;
using System.Threading;
+using System.Threading.Tasks;
using Emby.Common.Implementations.Networking;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Logging;
@@ -59,7 +60,7 @@ namespace Emby.Common.Implementations.Net
#if NET46
Socket.Close();
#else
- Socket.Dispose();
+ Socket.Dispose();
#endif
}
@@ -96,6 +97,46 @@ namespace Emby.Common.Implementations.Net
_acceptor.StartAccept();
}
+#if NET46
+ public Task SendFile(string path, byte[] preBuffer, byte[] postBuffer, CancellationToken cancellationToken)
+ {
+ var options = TransmitFileOptions.UseKernelApc;
+
+ var completionSource = new TaskCompletionSource<bool>();
+
+ var result = Socket.BeginSendFile(path, preBuffer, postBuffer, options, new AsyncCallback(FileSendCallback), new Tuple<Socket, string, TaskCompletionSource<bool>>(Socket, path, completionSource));
+
+ return completionSource.Task;
+ }
+
+ private void FileSendCallback(IAsyncResult ar)
+ {
+ // Retrieve the socket from the state object.
+ Tuple<Socket, string, TaskCompletionSource<bool>> data = (Tuple<Socket, string, TaskCompletionSource<bool>>)ar.AsyncState;
+
+ var client = data.Item1;
+ var path = data.Item2;
+ var taskCompletion = data.Item3;
+
+ // Complete sending the data to the remote device.
+ try {
+ client.EndSendFile(ar);
+ taskCompletion.TrySetResult(true);
+}
+ catch(SocketException ex){
+ _logger.Info("Socket.SendFile failed for {0}. error code {1}", path, ex.SocketErrorCode);
+ taskCompletion.TrySetException(ex);
+}catch(Exception ex){
+ taskCompletion.TrySetException(ex);
+}
+ }
+#else
+ public Task SendFile(string path, byte[] preBuffer, byte[] postBuffer, CancellationToken cancellationToken)
+ {
+ throw new NotImplementedException();
+ }
+#endif
+
public void Dispose()
{
Socket.Dispose();
diff --git a/Emby.Common.Implementations/Net/SocketFactory.cs b/Emby.Common.Implementations/Net/SocketFactory.cs
index 523f4da85..0f4306a6b 100644
--- a/Emby.Common.Implementations/Net/SocketFactory.cs
+++ b/Emby.Common.Implementations/Net/SocketFactory.cs
@@ -59,6 +59,7 @@ namespace Emby.Common.Implementations.Net
if (remotePort < 0) throw new ArgumentException("remotePort cannot be less than zero.", "remotePort");
var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
+
try
{
retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
@@ -96,10 +97,31 @@ namespace Emby.Common.Implementations.Net
}
}
+ public ISocket CreateUdpBroadcastSocket(int localPort)
+ {
+ if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort");
+
+ var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp);
+ try
+ {
+ retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
+ retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
+
+ return new UdpSocket(retVal, localPort, IPAddress.Any);
+ }
+ catch
+ {
+ if (retVal != null)
+ retVal.Dispose();
+
+ throw;
+ }
+ }
+
/// <summary>
- /// Creates a new UDP acceptSocket that is a member of the SSDP multicast local admin group and binds it to the specified local port.
- /// </summary>
- /// <returns>An implementation of the <see cref="ISocket"/> interface used by RSSDP components to perform acceptSocket operations.</returns>
+ /// Creates a new UDP acceptSocket that is a member of the SSDP multicast local admin group and binds it to the specified local port.
+ /// </summary>
+ /// <returns>An implementation of the <see cref="ISocket"/> interface used by RSSDP components to perform acceptSocket operations.</returns>
public ISocket CreateSsdpUdpSocket(IpAddressInfo localIpAddress, int localPort)
{
if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort");
diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs
index 8e2a1da6f..b85245ba1 100644
--- a/Emby.Common.Implementations/Net/UdpSocket.cs
+++ b/Emby.Common.Implementations/Net/UdpSocket.cs
@@ -60,6 +60,8 @@ namespace Emby.Common.Implementations.Net
var state = new AsyncReceiveState(_Socket, receivedFromEndPoint);
state.TaskCompletionSource = tcs;
+ cancellationToken.Register(() => tcs.TrySetCanceled());
+
#if NETSTANDARD1_6
_Socket.ReceiveFromAsync(new ArraySegment<Byte>(state.Buffer), SocketFlags.None, state.RemoteEndPoint)
.ContinueWith((task, asyncState) =>
@@ -160,7 +162,7 @@ namespace Emby.Common.Implementations.Net
var bytesRead = receiveData();
var ipEndPoint = state.RemoteEndPoint as IPEndPoint;
- state.TaskCompletionSource.SetResult(
+ state.TaskCompletionSource.TrySetResult(
new SocketReceiveResult
{
Buffer = state.Buffer,
@@ -172,18 +174,18 @@ namespace Emby.Common.Implementations.Net
}
catch (ObjectDisposedException)
{
- state.TaskCompletionSource.SetCanceled();
+ state.TaskCompletionSource.TrySetCanceled();
}
catch (SocketException se)
{
if (se.SocketErrorCode != SocketError.Interrupted && se.SocketErrorCode != SocketError.OperationAborted && se.SocketErrorCode != SocketError.Shutdown)
- state.TaskCompletionSource.SetException(se);
+ state.TaskCompletionSource.TrySetException(se);
else
- state.TaskCompletionSource.SetCanceled();
+ state.TaskCompletionSource.TrySetCanceled();
}
catch (Exception ex)
{
- state.TaskCompletionSource.SetException(ex);
+ state.TaskCompletionSource.TrySetException(ex);
}
}
@@ -206,7 +208,7 @@ namespace Emby.Common.Implementations.Net
var bytesRead = state.Socket.EndReceiveFrom(asyncResult, ref state.RemoteEndPoint);
var ipEndPoint = state.RemoteEndPoint as IPEndPoint;
- state.TaskCompletionSource.SetResult(
+ state.TaskCompletionSource.TrySetResult(
new SocketReceiveResult
{
Buffer = state.Buffer,
@@ -218,11 +220,11 @@ namespace Emby.Common.Implementations.Net
}
catch (ObjectDisposedException)
{
- state.TaskCompletionSource.SetCanceled();
+ state.TaskCompletionSource.TrySetCanceled();
}
catch (Exception ex)
{
- state.TaskCompletionSource.SetException(ex);
+ state.TaskCompletionSource.TrySetException(ex);
}
#endif
}
diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs
index d048dcde1..c7eae91dd 100644
--- a/Emby.Dlna/Didl/DidlBuilder.cs
+++ b/Emby.Dlna/Didl/DidlBuilder.cs
@@ -208,7 +208,7 @@ namespace Emby.Dlna.Didl
var targetHeight = streamInfo.TargetHeight;
var contentFeatureList = new ContentFeatureBuilder(_profile).BuildVideoHeader(streamInfo.Container,
- streamInfo.VideoCodec,
+ streamInfo.TargetVideoCodec,
streamInfo.TargetAudioCodec,
targetWidth,
targetHeight,
@@ -352,7 +352,7 @@ namespace Emby.Dlna.Didl
var mediaProfile = _profile.GetVideoMediaProfile(streamInfo.Container,
streamInfo.TargetAudioCodec,
- streamInfo.VideoCodec,
+ streamInfo.TargetVideoCodec,
streamInfo.TargetAudioBitrate,
targetWidth,
targetHeight,
diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs
index 3c07e95db..b73332c4b 100644
--- a/Emby.Dlna/PlayTo/PlayToController.cs
+++ b/Emby.Dlna/PlayTo/PlayToController.cs
@@ -542,7 +542,7 @@ namespace Emby.Dlna.PlayTo
{
var list = new ContentFeatureBuilder(profile)
.BuildVideoHeader(streamInfo.Container,
- streamInfo.VideoCodec,
+ streamInfo.TargetVideoCodec,
streamInfo.TargetAudioCodec,
streamInfo.TargetWidth,
streamInfo.TargetHeight,
diff --git a/Emby.Dlna/Profiles/DefaultProfile.cs b/Emby.Dlna/Profiles/DefaultProfile.cs
index c670f268e..f05a4869a 100644
--- a/Emby.Dlna/Profiles/DefaultProfile.cs
+++ b/Emby.Dlna/Profiles/DefaultProfile.cs
@@ -83,6 +83,12 @@ namespace Emby.Dlna.Profiles
{
Format = "srt",
Method = SubtitleDeliveryMethod.Embed
+ },
+
+ new SubtitleProfile
+ {
+ Format = "srt",
+ Method = SubtitleDeliveryMethod.External,
}
};
diff --git a/Emby.Dlna/Profiles/DirectTvProfile.cs b/Emby.Dlna/Profiles/DirectTvProfile.cs
index 153d55204..bb9ce903c 100644
--- a/Emby.Dlna/Profiles/DirectTvProfile.cs
+++ b/Emby.Dlna/Profiles/DirectTvProfile.cs
@@ -113,6 +113,15 @@ namespace Emby.Dlna.Profiles
}
};
+ SubtitleProfiles = new[]
+ {
+ new SubtitleProfile
+ {
+ Format = "srt",
+ Method = SubtitleDeliveryMethod.Embed
+ }
+ };
+
ResponseProfiles = new ResponseProfile[] { };
}
}
diff --git a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
index 89e0697c1..331312bec 100644
--- a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
+++ b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
@@ -214,6 +214,14 @@ namespace Emby.Dlna.Profiles
}
};
+ SubtitleProfiles = new[]
+ {
+ new SubtitleProfile
+ {
+ Format = "srt",
+ Method = SubtitleDeliveryMethod.Embed
+ }
+ };
}
}
}
diff --git a/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs b/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs
index 2b31ab55f..d2f5fef76 100644
--- a/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs
+++ b/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs
@@ -40,6 +40,15 @@ namespace Emby.Dlna.Profiles
MimeType = "video/mp4"
}
};
+
+ SubtitleProfiles = new[]
+ {
+ new SubtitleProfile
+ {
+ Format = "srt",
+ Method = SubtitleDeliveryMethod.Embed
+ }
+ };
}
}
}
diff --git a/Emby.Dlna/Profiles/PopcornHourProfile.cs b/Emby.Dlna/Profiles/PopcornHourProfile.cs
index d13b5c6ad..ac8fba84c 100644
--- a/Emby.Dlna/Profiles/PopcornHourProfile.cs
+++ b/Emby.Dlna/Profiles/PopcornHourProfile.cs
@@ -210,6 +210,15 @@ namespace Emby.Dlna.Profiles
MimeType = "video/mp4"
}
};
+
+ SubtitleProfiles = new[]
+ {
+ new SubtitleProfile
+ {
+ Format = "srt",
+ Method = SubtitleDeliveryMethod.Embed
+ }
+ };
}
}
}
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs b/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs
index c67bd85b2..d9eab28e1 100644
--- a/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs
+++ b/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs
@@ -269,6 +269,15 @@ namespace Emby.Dlna.Profiles
Type = DlnaProfileType.Audio
}
};
+
+ SubtitleProfiles = new[]
+ {
+ new SubtitleProfile
+ {
+ Format = "srt",
+ Method = SubtitleDeliveryMethod.Embed
+ }
+ };
}
}
}
diff --git a/Emby.Dlna/Profiles/SonyBravia2010Profile.cs b/Emby.Dlna/Profiles/SonyBravia2010Profile.cs
index 518550371..60a99561f 100644
--- a/Emby.Dlna/Profiles/SonyBravia2010Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2010Profile.cs
@@ -351,6 +351,15 @@ namespace Emby.Dlna.Profiles
}
}
};
+
+ SubtitleProfiles = new[]
+ {
+ new SubtitleProfile
+ {
+ Format = "srt",
+ Method = SubtitleDeliveryMethod.Embed
+ }
+ };
}
}
}
diff --git a/Emby.Dlna/Profiles/SonyBravia2011Profile.cs b/Emby.Dlna/Profiles/SonyBravia2011Profile.cs
index 427820a33..346845d9d 100644
--- a/Emby.Dlna/Profiles/SonyBravia2011Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2011Profile.cs
@@ -374,6 +374,15 @@ namespace Emby.Dlna.Profiles
}
}
};
+
+ SubtitleProfiles = new[]
+ {
+ new SubtitleProfile
+ {
+ Format = "srt",
+ Method = SubtitleDeliveryMethod.Embed
+ }
+ };
}
}
}
diff --git a/Emby.Dlna/Profiles/SonyBravia2012Profile.cs b/Emby.Dlna/Profiles/SonyBravia2012Profile.cs
index 206ca554c..23a39a922 100644
--- a/Emby.Dlna/Profiles/SonyBravia2012Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2012Profile.cs
@@ -292,6 +292,15 @@ namespace Emby.Dlna.Profiles
}
}
};
+
+ SubtitleProfiles = new[]
+ {
+ new SubtitleProfile
+ {
+ Format = "srt",
+ Method = SubtitleDeliveryMethod.Embed
+ }
+ };
}
}
}
diff --git a/Emby.Dlna/Profiles/SonyBravia2013Profile.cs b/Emby.Dlna/Profiles/SonyBravia2013Profile.cs
index c618c9990..2d36d3414 100644
--- a/Emby.Dlna/Profiles/SonyBravia2013Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2013Profile.cs
@@ -310,6 +310,15 @@ namespace Emby.Dlna.Profiles
}
}
};
+
+ SubtitleProfiles = new[]
+ {
+ new SubtitleProfile
+ {
+ Format = "srt",
+ Method = SubtitleDeliveryMethod.Embed
+ }
+ };
}
}
}
diff --git a/Emby.Dlna/Profiles/SonyBravia2014Profile.cs b/Emby.Dlna/Profiles/SonyBravia2014Profile.cs
index c30bcfc85..2c871a5d6 100644
--- a/Emby.Dlna/Profiles/SonyBravia2014Profile.cs
+++ b/Emby.Dlna/Profiles/SonyBravia2014Profile.cs
@@ -310,6 +310,15 @@ namespace Emby.Dlna.Profiles
}
}
};
+
+ SubtitleProfiles = new[]
+ {
+ new SubtitleProfile
+ {
+ Format = "srt",
+ Method = SubtitleDeliveryMethod.Embed
+ }
+ };
}
}
}
diff --git a/Emby.Dlna/Profiles/SonyPs3Profile.cs b/Emby.Dlna/Profiles/SonyPs3Profile.cs
index 001ef2bd8..2a0490f9a 100644
--- a/Emby.Dlna/Profiles/SonyPs3Profile.cs
+++ b/Emby.Dlna/Profiles/SonyPs3Profile.cs
@@ -255,6 +255,15 @@ namespace Emby.Dlna.Profiles
Type = DlnaProfileType.Audio
}
};
+
+ SubtitleProfiles = new[]
+ {
+ new SubtitleProfile
+ {
+ Format = "srt",
+ Method = SubtitleDeliveryMethod.Embed
+ }
+ };
}
}
}
diff --git a/Emby.Dlna/Profiles/SonyPs4Profile.cs b/Emby.Dlna/Profiles/SonyPs4Profile.cs
index 832733184..c7f70f989 100644
--- a/Emby.Dlna/Profiles/SonyPs4Profile.cs
+++ b/Emby.Dlna/Profiles/SonyPs4Profile.cs
@@ -264,6 +264,15 @@ namespace Emby.Dlna.Profiles
MimeType = "video/mp4"
}
};
+
+ SubtitleProfiles = new[]
+ {
+ new SubtitleProfile
+ {
+ Format = "srt",
+ Method = SubtitleDeliveryMethod.Embed
+ }
+ };
}
}
}
diff --git a/Emby.Dlna/Profiles/Xbox360Profile.cs b/Emby.Dlna/Profiles/Xbox360Profile.cs
index 9f0130856..7bdcd2a6f 100644
--- a/Emby.Dlna/Profiles/Xbox360Profile.cs
+++ b/Emby.Dlna/Profiles/Xbox360Profile.cs
@@ -312,6 +312,15 @@ namespace Emby.Dlna.Profiles
}
}
};
+
+ SubtitleProfiles = new[]
+ {
+ new SubtitleProfile
+ {
+ Format = "srt",
+ Method = SubtitleDeliveryMethod.Embed
+ }
+ };
}
}
}
diff --git a/Emby.Dlna/Profiles/XboxOneProfile.cs b/Emby.Dlna/Profiles/XboxOneProfile.cs
index 8994082ad..e17640f2f 100644
--- a/Emby.Dlna/Profiles/XboxOneProfile.cs
+++ b/Emby.Dlna/Profiles/XboxOneProfile.cs
@@ -357,6 +357,15 @@ namespace Emby.Dlna.Profiles
MimeType = "video/mp4"
}
};
+
+ SubtitleProfiles = new[]
+ {
+ new SubtitleProfile
+ {
+ Format = "srt",
+ Method = SubtitleDeliveryMethod.Embed
+ }
+ };
}
}
}
diff --git a/Emby.Dlna/Profiles/Xml/Default.xml b/Emby.Dlna/Profiles/Xml/Default.xml
index b5a5d24b6..b0b0d7ac1 100644
--- a/Emby.Dlna/Profiles/Xml/Default.xml
+++ b/Emby.Dlna/Profiles/Xml/Default.xml
@@ -46,5 +46,6 @@
</ResponseProfiles>
<SubtitleProfiles>
<SubtitleProfile format="srt" method="Embed" />
+ <SubtitleProfile format="srt" method="External" />
</SubtitleProfiles>
</Profile> \ No newline at end of file
diff --git a/Emby.Dlna/Profiles/Xml/Denon AVR.xml b/Emby.Dlna/Profiles/Xml/Denon AVR.xml
index 6ddad3146..00b56c03d 100644
--- a/Emby.Dlna/Profiles/Xml/Denon AVR.xml
+++ b/Emby.Dlna/Profiles/Xml/Denon AVR.xml
@@ -46,5 +46,6 @@
<ResponseProfiles />
<SubtitleProfiles>
<SubtitleProfile format="srt" method="Embed" />
+ <SubtitleProfile format="srt" method="External" />
</SubtitleProfiles>
</Profile> \ No newline at end of file
diff --git a/Emby.Dlna/Profiles/Xml/MediaMonkey.xml b/Emby.Dlna/Profiles/Xml/MediaMonkey.xml
index 48dfbf9bb..9b726ad4c 100644
--- a/Emby.Dlna/Profiles/Xml/MediaMonkey.xml
+++ b/Emby.Dlna/Profiles/Xml/MediaMonkey.xml
@@ -52,5 +52,6 @@
<ResponseProfiles />
<SubtitleProfiles>
<SubtitleProfile format="srt" method="Embed" />
+ <SubtitleProfile format="srt" method="External" />
</SubtitleProfiles>
</Profile> \ No newline at end of file
diff --git a/Emby.Dlna/Profiles/Xml/foobar2000.xml b/Emby.Dlna/Profiles/Xml/foobar2000.xml
index b793e2274..779e59310 100644
--- a/Emby.Dlna/Profiles/Xml/foobar2000.xml
+++ b/Emby.Dlna/Profiles/Xml/foobar2000.xml
@@ -52,5 +52,6 @@
<ResponseProfiles />
<SubtitleProfiles>
<SubtitleProfile format="srt" method="Embed" />
+ <SubtitleProfile format="srt" method="External" />
</SubtitleProfiles>
</Profile> \ No newline at end of file
diff --git a/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs b/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs
index 39088c94b..77482d56b 100644
--- a/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs
+++ b/Emby.Drawing.ImageMagick/ImageMagickEncoder.cs
@@ -44,7 +44,9 @@ namespace Emby.Drawing.ImageMagick
"cr2",
"crw",
"dng",
- "nef",
+
+ // Remove until supported
+ //"nef",
"orf",
"pef",
"arw",
diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs
index a15f75c9a..5b04ceea2 100644
--- a/Emby.Drawing/ImageProcessor.cs
+++ b/Emby.Drawing/ImageProcessor.cs
@@ -238,7 +238,7 @@ namespace Emby.Drawing
var outputFormat = GetOutputFormat(options.SupportedOutputFormats[0]);
var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.Blur, options.BackgroundColor, options.ForegroundLayer);
- var imageProcessingLockTaken = false;
+ //var imageProcessingLockTaken = false;
try
{
@@ -253,9 +253,9 @@ namespace Emby.Drawing
var tmpPath = Path.ChangeExtension(Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString("N")), Path.GetExtension(cacheFilePath));
_fileSystem.CreateDirectory(Path.GetDirectoryName(tmpPath));
- await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
+ //await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false);
- imageProcessingLockTaken = true;
+ //imageProcessingLockTaken = true;
_imageEncoder.EncodeImage(originalImagePath, tmpPath, AutoOrient(options.Item), newWidth, newHeight, quality, options, outputFormat);
CopyFile(tmpPath, cacheFilePath);
@@ -273,13 +273,13 @@ namespace Emby.Drawing
// Just spit out the original file if all the options are default
return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
}
- finally
- {
- if (imageProcessingLockTaken)
- {
- _imageProcessingSemaphore.Release();
- }
- }
+ //finally
+ //{
+ // if (imageProcessingLockTaken)
+ // {
+ // _imageProcessingSemaphore.Release();
+ // }
+ //}
}
private void CopyFile(string src, string destination)
diff --git a/Emby.Server.Core/ApplicationHost.cs b/Emby.Server.Core/ApplicationHost.cs
index 4425d1a0b..971378ea7 100644
--- a/Emby.Server.Core/ApplicationHost.cs
+++ b/Emby.Server.Core/ApplicationHost.cs
@@ -61,7 +61,7 @@ using System.Threading;
using System.Threading.Tasks;
using Emby.Common.Implementations;
using Emby.Common.Implementations.Archiving;
-using Emby.Common.Implementations.Networking;
+using Emby.Common.Implementations.IO;
using Emby.Common.Implementations.Reflection;
using Emby.Common.Implementations.Serialization;
using Emby.Common.Implementations.TextEncoding;
@@ -93,7 +93,7 @@ using Emby.Server.Implementations.Social;
using Emby.Server.Implementations.Channels;
using Emby.Server.Implementations.Collections;
using Emby.Server.Implementations.Dto;
-using Emby.Server.Implementations.EntryPoints;
+using Emby.Server.Implementations.IO;
using Emby.Server.Implementations.FileOrganization;
using Emby.Server.Implementations.HttpServer;
using Emby.Server.Implementations.HttpServer.Security;
@@ -107,7 +107,7 @@ using Emby.Server.Implementations.Playlists;
using Emby.Server.Implementations;
using Emby.Server.Implementations.ServerManager;
using Emby.Server.Implementations.Session;
-using Emby.Server.Implementations.Social;
+using Emby.Server.Implementations.Windows;
using Emby.Server.Implementations.TV;
using Emby.Server.Implementations.Updates;
using MediaBrowser.Model.Activity;
@@ -294,6 +294,13 @@ namespace Emby.Server.Core
ImageEncoder = imageEncoder;
SetBaseExceptionMessage();
+
+ if (environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows)
+ {
+ fileSystem.AddShortcutHandler(new LnkShortcutHandler());
+ }
+
+ fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
}
private Version _version;
@@ -606,7 +613,7 @@ namespace Emby.Server.Core
CertificatePath = GetCertificatePath(true);
Certificate = GetCertificate(CertificatePath);
- HttpServer = HttpServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, NetworkManager, MemoryStreamFactory, "Emby", "web/index.html", textEncoding, SocketFactory, CryptographyProvider, JsonSerializer, XmlSerializer, EnvironmentInfo, Certificate, SupportsDualModeSockets);
+ HttpServer = HttpServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, NetworkManager, MemoryStreamFactory, "Emby", "web/index.html", textEncoding, SocketFactory, CryptographyProvider, JsonSerializer, XmlSerializer, EnvironmentInfo, Certificate, FileSystemManager, SupportsDualModeSockets);
HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading");
RegisterSingleInstance(HttpServer, false);
progress.Report(10);
@@ -796,17 +803,25 @@ namespace Emby.Server.Core
info.FFMpegFilename = "ffmpeg";
info.FFProbeFilename = "ffprobe";
info.ArchiveType = "7z";
- info.Version = "20160215";
+ info.Version = "20170308";
info.DownloadUrls = GetLinuxDownloadUrls();
}
else if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows)
{
info.FFMpegFilename = "ffmpeg.exe";
info.FFProbeFilename = "ffprobe.exe";
- info.Version = "20160410";
+ info.Version = "20170308";
info.ArchiveType = "7z";
info.DownloadUrls = GetWindowsDownloadUrls();
}
+ else if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.OSX)
+ {
+ info.FFMpegFilename = "ffmpeg";
+ info.FFProbeFilename = "ffprobe";
+ info.ArchiveType = "7z";
+ info.Version = "20170308";
+ info.DownloadUrls = GetMacDownloadUrls();
+ }
else
{
// No version available - user requirement
@@ -816,6 +831,20 @@ namespace Emby.Server.Core
return info;
}
+ private string[] GetMacDownloadUrls()
+ {
+ switch (EnvironmentInfo.SystemArchitecture)
+ {
+ case Architecture.X64:
+ return new[]
+ {
+ "https://embydata.com/downloads/ffmpeg/osx/ffmpeg-x64-20170308.7z"
+ };
+ }
+
+ return new string[] { };
+ }
+
private string[] GetWindowsDownloadUrls()
{
switch (EnvironmentInfo.SystemArchitecture)
@@ -823,12 +852,12 @@ namespace Emby.Server.Core
case Architecture.X64:
return new[]
{
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160410-win64.7z"
+ "https://embydata.com/downloads/ffmpeg/windows/ffmpeg-20170308-win64.7z"
};
case Architecture.X86:
return new[]
{
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/windows/ffmpeg-20160410-win32.7z"
+ "https://embydata.com/downloads/ffmpeg/windows/ffmpeg-20170308-win32.7z"
};
}
@@ -842,12 +871,12 @@ namespace Emby.Server.Core
case Architecture.X64:
return new[]
{
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-64bit-static.7z"
+ "https://embydata.com/downloads/ffmpeg/linux/ffmpeg-git-20170301-64bit-static.7z"
};
case Architecture.X86:
return new[]
{
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/linux/ffmpeg-git-20160215-32bit-static.7z"
+ "https://embydata.com/downloads/ffmpeg/linux/ffmpeg-git-20170301-32bit-static.7z"
};
}
@@ -1716,12 +1745,10 @@ namespace Emby.Server.Core
public void EnableLoopback(string appName)
{
- EnableLoopbackInternal(appName);
- }
-
- protected virtual void EnableLoopbackInternal(string appName)
- {
-
+ if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows)
+ {
+ LoopUtil.Run(appName);
+ }
}
private void RegisterModules()
diff --git a/Emby.Server.Core/HttpServerFactory.cs b/Emby.Server.Core/HttpServerFactory.cs
index deed3c6f3..dfd435c33 100644
--- a/Emby.Server.Core/HttpServerFactory.cs
+++ b/Emby.Server.Core/HttpServerFactory.cs
@@ -45,6 +45,7 @@ namespace Emby.Server.Core
IXmlSerializer xml,
IEnvironmentInfo environment,
ICertificate certificate,
+ IFileSystem fileSystem,
bool enableDualModeSockets)
{
var logger = logManager.GetLogger("HttpServer");
@@ -65,7 +66,8 @@ namespace Emby.Server.Core
certificate,
new StreamFactory(),
GetParseFn,
- enableDualModeSockets);
+ enableDualModeSockets,
+ fileSystem);
}
private static Func<string, object> GetParseFn(Type propertyType)
diff --git a/Emby.Server.Core/IO/LibraryMonitor.cs b/Emby.Server.Core/IO/LibraryMonitor.cs
index baa705111..4df9b930e 100644
--- a/Emby.Server.Core/IO/LibraryMonitor.cs
+++ b/Emby.Server.Core/IO/LibraryMonitor.cs
@@ -417,7 +417,7 @@ namespace Emby.Server.Core.IO
{
try
{
- Logger.Debug("Changed detected of type " + e.ChangeType + " to " + e.FullPath);
+ //Logger.Debug("Changed detected of type " + e.ChangeType + " to " + e.FullPath);
var path = e.FullPath;
diff --git a/Emby.Server.Core/Localization/TextLocalizer.cs b/Emby.Server.Core/Localization/TextLocalizer.cs
index 6690c6263..1e8ccbbfa 100644
--- a/Emby.Server.Core/Localization/TextLocalizer.cs
+++ b/Emby.Server.Core/Localization/TextLocalizer.cs
@@ -39,7 +39,15 @@ namespace Emby.Server.Core.Localization
}
}
- return text.Normalize(form);
+ try
+ {
+ return text.Normalize(form);
+ }
+ catch (ArgumentException)
+ {
+ // if it still fails, return the original text
+ return text;
+ }
}
private static string StripInvalidUnicodeCharacters(string str)
diff --git a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
index 2819a249f..0096f2284 100644
--- a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
+++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs
@@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Entities.Audio;
@@ -22,13 +23,15 @@ namespace Emby.Server.Implementations.Data
private readonly IItemRepository _itemRepo;
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
+ private readonly IApplicationPaths _appPaths;
- public CleanDatabaseScheduledTask(ILibraryManager libraryManager, IItemRepository itemRepo, ILogger logger, IFileSystem fileSystem)
+ public CleanDatabaseScheduledTask(ILibraryManager libraryManager, IItemRepository itemRepo, ILogger logger, IFileSystem fileSystem, IApplicationPaths appPaths)
{
_libraryManager = libraryManager;
_itemRepo = itemRepo;
_logger = logger;
_fileSystem = fileSystem;
+ _appPaths = appPaths;
}
public string Name
@@ -150,13 +153,27 @@ namespace Emby.Server.Implementations.Data
try
{
- if (_fileSystem.FileExists(path) || _fileSystem.DirectoryExists(path))
+ var isPathInLibrary = false;
+
+ if (allLibraryPaths.Any(i => path.StartsWith(i, StringComparison.Ordinal)) ||
+ allLibraryPaths.Contains(path, StringComparer.Ordinal) ||
+ path.StartsWith(_appPaths.ProgramDataPath, StringComparison.Ordinal))
{
- continue;
+ isPathInLibrary = true;
+
+ if (_fileSystem.FileExists(path) || _fileSystem.DirectoryExists(path))
+ {
+ continue;
+ }
}
var libraryItem = _libraryManager.GetItemById(item.Item1);
+ if (libraryItem == null)
+ {
+ continue;
+ }
+
if (libraryItem.IsTopParent)
{
continue;
@@ -180,7 +197,14 @@ namespace Emby.Server.Implementations.Data
continue;
}
- _logger.Info("Deleting item from database {0} because path no longer exists. type: {1} path: {2}", libraryItem.Name, libraryItem.GetType().Name, libraryItemPath ?? string.Empty);
+ if (isPathInLibrary)
+ {
+ _logger.Info("Deleting item from database {0} because path no longer exists. type: {1} path: {2}", libraryItem.Name, libraryItem.GetType().Name, libraryItemPath ?? string.Empty);
+ }
+ else
+ {
+ _logger.Info("Deleting item from database {0} because path is no longer in the server library. type: {1} path: {2}", libraryItem.Name, libraryItem.GetType().Name, libraryItemPath ?? string.Empty);
+ }
await libraryItem.OnFileDeleted().ConfigureAwait(false);
}
diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs
index 88c0ea203..588b42a09 100644
--- a/Emby.Server.Implementations/Devices/DeviceManager.cs
+++ b/Emby.Server.Implementations/Devices/DeviceManager.cs
@@ -195,13 +195,12 @@ namespace Emby.Server.Implementations.Devices
}
var config = _config.GetUploadOptions();
- if (!string.IsNullOrWhiteSpace(config.CameraUploadPath))
+ var path = config.CameraUploadPath;
+ if (string.IsNullOrWhiteSpace(path))
{
- return config.CameraUploadPath;
+ path = DefaultCameraUploadsPath;
}
- var path = DefaultCameraUploadsPath;
-
if (config.EnableCameraUploadSubfolders)
{
path = Path.Combine(path, _fileSystem.GetValidFilename(device.Name));
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 8b6b388db..d477008a5 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -491,7 +491,7 @@ namespace Emby.Server.Implementations.Dto
}
}
- if (!(item is LiveTvProgram))
+ if (!(item is LiveTvProgram) || fields.Contains(ItemFields.PlayAccess))
{
dto.PlayAccess = item.GetPlayAccess(user);
}
@@ -1639,7 +1639,7 @@ namespace Emby.Server.Implementations.Dto
var width = size.Width;
var height = size.Height;
- if (width == 0 || height == 0)
+ if (width.Equals(0) || height.Equals(0))
{
return null;
}
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index c704d0e4e..ecd86d507 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -84,6 +84,7 @@
<Compile Include="FileOrganization\NameUtils.cs" />
<Compile Include="FileOrganization\OrganizerScheduledTask.cs" />
<Compile Include="FileOrganization\TvFolderOrganizer.cs" />
+ <Compile Include="HttpServer\FileWriter.cs" />
<Compile Include="HttpServer\GetSwaggerResource.cs" />
<Compile Include="HttpServer\HttpListenerHost.cs" />
<Compile Include="HttpServer\HttpResultFactory.cs" />
@@ -170,7 +171,6 @@
<Compile Include="LiveTv\RefreshChannelsScheduledTask.cs" />
<Compile Include="LiveTv\TunerHosts\BaseTunerHost.cs" />
<Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunManager.cs" />
- <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunDiscovery.cs" />
<Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunHost.cs" />
<Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunHttpStream.cs" />
<Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunUdpStream.cs" />
@@ -269,6 +269,7 @@
<Compile Include="Updates\InstallationManager.cs" />
<Compile Include="UserViews\CollectionFolderImageProvider.cs" />
<Compile Include="UserViews\DynamicImageProvider.cs" />
+ <Compile Include="Windows\LoopUtil.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Localization\iso6392.txt" />
@@ -302,8 +303,8 @@
<HintPath>..\packages\Emby.XmlTv.1.0.7\lib\portable-net45+win8\Emby.XmlTv.dll</HintPath>
<Private>True</Private>
</Reference>
- <Reference Include="MediaBrowser.Naming, Version=1.0.6201.24431, Culture=neutral, processorArchitecture=MSIL">
- <HintPath>..\packages\MediaBrowser.Naming.1.0.4\lib\portable-net45+win8\MediaBrowser.Naming.dll</HintPath>
+ <Reference Include="MediaBrowser.Naming, Version=1.0.6279.25941, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\packages\MediaBrowser.Naming.1.0.5\lib\portable-net45+win8\MediaBrowser.Naming.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="SQLitePCL.pretty, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs
new file mode 100644
index 000000000..d230a9b91
--- /dev/null
+++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs
@@ -0,0 +1,191 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Services;
+
+namespace Emby.Server.Implementations.HttpServer
+{
+ public class FileWriter : IHttpResult
+ {
+ private ILogger Logger { get; set; }
+
+ private string RangeHeader { get; set; }
+ private bool IsHeadRequest { get; set; }
+
+ private long RangeStart { get; set; }
+ private long RangeEnd { get; set; }
+ private long RangeLength { get; set; }
+ private long TotalContentLength { get; set; }
+
+ public Action OnComplete { get; set; }
+ public Action OnError { get; set; }
+ private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
+ public List<Cookie> Cookies { get; private set; }
+
+ public FileShareMode FileShare { get; set; }
+
+ /// <summary>
+ /// The _options
+ /// </summary>
+ private readonly IDictionary<string, string> _options = new Dictionary<string, string>();
+ /// <summary>
+ /// Gets the options.
+ /// </summary>
+ /// <value>The options.</value>
+ public IDictionary<string, string> Headers
+ {
+ get { return _options; }
+ }
+
+ public string Path { get; set; }
+
+ public FileWriter(string path, string contentType, string rangeHeader, ILogger logger, IFileSystem fileSystem)
+ {
+ if (string.IsNullOrEmpty(contentType))
+ {
+ throw new ArgumentNullException("contentType");
+ }
+
+ Path = path;
+ Logger = logger;
+ RangeHeader = rangeHeader;
+
+ Headers["Content-Type"] = contentType;
+
+ TotalContentLength = fileSystem.GetFileInfo(path).Length;
+
+ if (string.IsNullOrWhiteSpace(rangeHeader))
+ {
+ Headers["Content-Length"] = TotalContentLength.ToString(UsCulture);
+ StatusCode = HttpStatusCode.OK;
+ }
+ else
+ {
+ Headers["Accept-Ranges"] = "bytes";
+ StatusCode = HttpStatusCode.PartialContent;
+ SetRangeValues();
+ }
+
+ FileShare = FileShareMode.Read;
+ Cookies = new List<Cookie>();
+ }
+
+ /// <summary>
+ /// Sets the range values.
+ /// </summary>
+ private void SetRangeValues()
+ {
+ var requestedRange = RequestedRanges[0];
+
+ // If the requested range is "0-", we can optimize by just doing a stream copy
+ if (!requestedRange.Value.HasValue)
+ {
+ RangeEnd = TotalContentLength - 1;
+ }
+ else
+ {
+ RangeEnd = requestedRange.Value.Value;
+ }
+
+ RangeStart = requestedRange.Key;
+ RangeLength = 1 + RangeEnd - RangeStart;
+
+ // Content-Length is the length of what we're serving, not the original content
+ Headers["Content-Length"] = RangeLength.ToString(UsCulture);
+ Headers["Content-Range"] = string.Format("bytes {0}-{1}/{2}", RangeStart, RangeEnd, TotalContentLength);
+ }
+
+ /// <summary>
+ /// The _requested ranges
+ /// </summary>
+ private List<KeyValuePair<long, long?>> _requestedRanges;
+ /// <summary>
+ /// Gets the requested ranges.
+ /// </summary>
+ /// <value>The requested ranges.</value>
+ protected List<KeyValuePair<long, long?>> RequestedRanges
+ {
+ get
+ {
+ if (_requestedRanges == null)
+ {
+ _requestedRanges = new List<KeyValuePair<long, long?>>();
+
+ // Example: bytes=0-,32-63
+ var ranges = RangeHeader.Split('=')[1].Split(',');
+
+ foreach (var range in ranges)
+ {
+ var vals = range.Split('-');
+
+ long start = 0;
+ long? end = null;
+
+ if (!string.IsNullOrEmpty(vals[0]))
+ {
+ start = long.Parse(vals[0], UsCulture);
+ }
+ if (!string.IsNullOrEmpty(vals[1]))
+ {
+ end = long.Parse(vals[1], UsCulture);
+ }
+
+ _requestedRanges.Add(new KeyValuePair<long, long?>(start, end));
+ }
+ }
+
+ return _requestedRanges;
+ }
+ }
+
+ public async Task WriteToAsync(IResponse response, CancellationToken cancellationToken)
+ {
+ try
+ {
+ // Headers only
+ if (IsHeadRequest)
+ {
+ return;
+ }
+
+ if (string.IsNullOrWhiteSpace(RangeHeader) || (RangeStart <= 0 && RangeEnd >= TotalContentLength - 1))
+ {
+ Logger.Info("Transmit file {0}", Path);
+ await response.TransmitFile(Path, 0, 0, FileShare, cancellationToken).ConfigureAwait(false);
+ return;
+ }
+
+ await response.TransmitFile(Path, RangeStart, RangeEnd, FileShare, cancellationToken).ConfigureAwait(false);
+ }
+ finally
+ {
+ if (OnComplete != null)
+ {
+ OnComplete();
+ }
+ }
+ }
+
+ public string ContentType { get; set; }
+
+ public IRequest RequestContext { get; set; }
+
+ public object Response { get; set; }
+
+ public int Status { get; set; }
+
+ public HttpStatusCode StatusCode
+ {
+ get { return (HttpStatusCode)Status; }
+ set { Status = (int)value; }
+ }
+
+ public string StatusDescription { get; set; }
+
+ }
+}
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index 6fcdab874..6d15cc619 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -52,6 +52,7 @@ namespace Emby.Server.Implementations.HttpServer
private readonly ISocketFactory _socketFactory;
private readonly ICryptoProvider _cryptoProvider;
+ private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _jsonSerializer;
private readonly IXmlSerializer _xmlSerializer;
private readonly ICertificate _certificate;
@@ -70,8 +71,7 @@ namespace Emby.Server.Implementations.HttpServer
ILogger logger,
IServerConfigurationManager config,
string serviceName,
- string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer, IEnvironmentInfo environment, ICertificate certificate, IStreamFactory streamFactory, Func<Type, Func<string, object>> funcParseFn, bool enableDualModeSockets)
- : base()
+ string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer, IEnvironmentInfo environment, ICertificate certificate, IStreamFactory streamFactory, Func<Type, Func<string, object>> funcParseFn, bool enableDualModeSockets, IFileSystem fileSystem)
{
Instance = this;
@@ -89,6 +89,7 @@ namespace Emby.Server.Implementations.HttpServer
_streamFactory = streamFactory;
_funcParseFn = funcParseFn;
_enableDualModeSockets = enableDualModeSockets;
+ _fileSystem = fileSystem;
_config = config;
_logger = logger;
@@ -226,7 +227,8 @@ namespace Emby.Server.Implementations.HttpServer
_cryptoProvider,
_streamFactory,
_enableDualModeSockets,
- GetRequest);
+ GetRequest,
+ _fileSystem);
}
private IHttpRequest GetRequest(HttpListenerContext httpContext)
@@ -579,7 +581,7 @@ namespace Emby.Server.Implementations.HttpServer
}
else
{
- ErrorHandler(new FileNotFoundException(), httpReq);
+ ErrorHandler(new FileNotFoundException(), httpReq, false);
}
}
catch (OperationCanceledException ex)
@@ -633,7 +635,6 @@ namespace Emby.Server.Implementations.HttpServer
return null;
}
-
private void Write(IResponse response, string text)
{
var bOutput = Encoding.UTF8.GetBytes(text);
diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
index 6bfd83110..310161d41 100644
--- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
@@ -474,10 +474,6 @@ namespace Emby.Server.Implementations.HttpServer
{
throw new ArgumentNullException("cacheKey");
}
- if (options.ContentFactory == null)
- {
- throw new ArgumentNullException("factoryFn");
- }
var key = cacheKey.ToString("N");
@@ -560,30 +556,44 @@ namespace Emby.Server.Implementations.HttpServer
{
var rangeHeader = requestContext.Headers.Get("Range");
- var stream = await factoryFn().ConfigureAwait(false);
+ if (!isHeadRequest && !string.IsNullOrWhiteSpace(options.Path))
+ {
+ return new FileWriter(options.Path, contentType, rangeHeader, _logger, _fileSystem)
+ {
+ OnComplete = options.OnComplete,
+ OnError = options.OnError,
+ FileShare = options.FileShare
+ };
+ }
if (!string.IsNullOrEmpty(rangeHeader))
{
+ var stream = await factoryFn().ConfigureAwait(false);
+
return new RangeRequestWriter(rangeHeader, stream, contentType, isHeadRequest, _logger)
{
OnComplete = options.OnComplete
};
}
+ else
+ {
+ var stream = await factoryFn().ConfigureAwait(false);
- responseHeaders["Content-Length"] = stream.Length.ToString(UsCulture);
+ responseHeaders["Content-Length"] = stream.Length.ToString(UsCulture);
- if (isHeadRequest)
- {
- stream.Dispose();
+ if (isHeadRequest)
+ {
+ stream.Dispose();
- return GetHttpResult(new byte[] { }, contentType, true);
- }
+ return GetHttpResult(new byte[] { }, contentType, true);
+ }
- return new StreamWriter(stream, contentType, _logger)
- {
- OnComplete = options.OnComplete,
- OnError = options.OnError
- };
+ return new StreamWriter(stream, contentType, _logger)
+ {
+ OnComplete = options.OnComplete,
+ OnError = options.OnError
+ };
+ }
}
using (var stream = await factoryFn().ConfigureAwait(false))
diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs
index 652fc4f83..b11b2fe88 100644
--- a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs
+++ b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs
@@ -27,10 +27,11 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
private readonly ISocketFactory _socketFactory;
private readonly ICryptoProvider _cryptoProvider;
private readonly IStreamFactory _streamFactory;
+ private readonly IFileSystem _fileSystem;
private readonly Func<HttpListenerContext, IHttpRequest> _httpRequestFactory;
private readonly bool _enableDualMode;
- public WebSocketSharpListener(ILogger logger, ICertificate certificate, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, bool enableDualMode, Func<HttpListenerContext, IHttpRequest> httpRequestFactory)
+ public WebSocketSharpListener(ILogger logger, ICertificate certificate, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, bool enableDualMode, Func<HttpListenerContext, IHttpRequest> httpRequestFactory, IFileSystem fileSystem)
{
_logger = logger;
_certificate = certificate;
@@ -42,6 +43,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
_streamFactory = streamFactory;
_enableDualMode = enableDualMode;
_httpRequestFactory = httpRequestFactory;
+ _fileSystem = fileSystem;
}
public Action<Exception, IRequest, bool> ErrorHandler { get; set; }
@@ -54,7 +56,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
public void Start(IEnumerable<string> urlPrefixes)
{
if (_listener == null)
- _listener = new HttpListener(_logger, _cryptoProvider, _streamFactory, _socketFactory, _networkManager, _textEncoding, _memoryStreamProvider);
+ _listener = new HttpListener(_logger, _cryptoProvider, _streamFactory, _socketFactory, _networkManager, _textEncoding, _memoryStreamProvider, _fileSystem);
_listener.EnableDualMode = _enableDualMode;
diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs
index 36f795411..fd30b227f 100644
--- a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs
+++ b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs
@@ -3,6 +3,9 @@ using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using SocketHttpListener.Net;
using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse;
@@ -189,5 +192,10 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp
public void ClearCookies()
{
}
+
+ public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
+ {
+ return _response.TransmitFile(path, offset, count, fileShareMode, cancellationToken);
+ }
}
}
diff --git a/Emby.Server.Implementations/IO/FileRefresher.cs b/Emby.Server.Implementations/IO/FileRefresher.cs
index d7f2ffa43..c64672685 100644
--- a/Emby.Server.Implementations/IO/FileRefresher.cs
+++ b/Emby.Server.Implementations/IO/FileRefresher.cs
@@ -160,10 +160,10 @@ namespace Emby.Server.Implementations.IO
.DistinctBy(i => i.Id)
.ToList();
- foreach (var p in paths)
- {
- Logger.Info(p + " reports change.");
- }
+ //foreach (var p in paths)
+ //{
+ // Logger.Info(p + " reports change.");
+ //}
// If the root folder changed, run the library task so the user can see it
if (itemsToRefresh.Any(i => i is AggregateFolder))
diff --git a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
index 7a36691df..38908c2bd 100644
--- a/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
+++ b/Emby.Server.Implementations/Images/BaseDynamicImageProvider.cs
@@ -258,7 +258,7 @@ namespace Emby.Server.Implementations.Images
{
return await CreateSquareCollage(item, itemsWithImages, outputPath).ConfigureAwait(false);
}
- if (item is Playlist || item is MusicGenre)
+ if (item is Playlist || item is MusicGenre || item is Genre || item is GameGenre)
{
return await CreateSquareCollage(item, itemsWithImages, outputPath).ConfigureAwait(false);
}
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index 56bffc233..026486efc 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -409,24 +409,46 @@ namespace Emby.Server.Implementations.Library
if (options.DeleteFileLocation && locationType != LocationType.Remote && locationType != LocationType.Virtual)
{
+ // Assume only the first is required
+ // Add this flag to GetDeletePaths if required in the future
+ var isRequiredForDelete = true;
+
foreach (var fileSystemInfo in item.GetDeletePaths().ToList())
{
- if (fileSystemInfo.IsDirectory)
+ try
{
- _logger.Debug("Deleting path {0}", fileSystemInfo.FullName);
- _fileSystem.DeleteDirectory(fileSystemInfo.FullName, true);
+ if (fileSystemInfo.IsDirectory)
+ {
+ _logger.Debug("Deleting path {0}", fileSystemInfo.FullName);
+ _fileSystem.DeleteDirectory(fileSystemInfo.FullName, true);
+ }
+ else
+ {
+ _logger.Debug("Deleting path {0}", fileSystemInfo.FullName);
+ _fileSystem.DeleteFile(fileSystemInfo.FullName);
+ }
}
- else
+ catch (IOException)
+ {
+ if (isRequiredForDelete)
+ {
+ throw;
+ }
+ }
+ catch (UnauthorizedAccessException)
{
- _logger.Debug("Deleting path {0}", fileSystemInfo.FullName);
- _fileSystem.DeleteFile(fileSystemInfo.FullName);
+ if (isRequiredForDelete)
+ {
+ throw;
+ }
}
+
+ isRequiredForDelete = false;
}
if (parent != null)
{
- await parent.ValidateChildren(new Progress<double>(), CancellationToken.None)
- .ConfigureAwait(false);
+ await parent.ValidateChildren(new Progress<double>(), CancellationToken.None, new MetadataRefreshOptions(_fileSystem), false) .ConfigureAwait(false);
}
}
else if (parent != null)
@@ -492,6 +514,11 @@ namespace Emby.Server.Implementations.Library
public Guid GetNewItemId(string key, Type type)
{
+ return GetNewItemIdInternal(key, type, false);
+ }
+
+ private Guid GetNewItemIdInternal(string key, Type type, bool forceCaseInsensitive)
+ {
if (string.IsNullOrWhiteSpace(key))
{
throw new ArgumentNullException("key");
@@ -509,7 +536,7 @@ namespace Emby.Server.Implementations.Library
.Replace("/", "\\");
}
- if (!ConfigurationManager.Configuration.EnableCaseSensitiveItemIds)
+ if (forceCaseInsensitive || !ConfigurationManager.Configuration.EnableCaseSensitiveItemIds)
{
key = key.ToLower();
}
@@ -843,7 +870,7 @@ namespace Emby.Server.Implementations.Library
/// <returns>Task{Person}.</returns>
public Person GetPerson(string name)
{
- return CreateItemByName<Person>(Person.GetPath(name), name);
+ return CreateItemByName<Person>(Person.GetPath, name);
}
/// <summary>
@@ -853,7 +880,7 @@ namespace Emby.Server.Implementations.Library
/// <returns>Task{Studio}.</returns>
public Studio GetStudio(string name)
{
- return CreateItemByName<Studio>(Studio.GetPath(name), name);
+ return CreateItemByName<Studio>(Studio.GetPath, name);
}
/// <summary>
@@ -863,7 +890,7 @@ namespace Emby.Server.Implementations.Library
/// <returns>Task{Genre}.</returns>
public Genre GetGenre(string name)
{
- return CreateItemByName<Genre>(Genre.GetPath(name), name);
+ return CreateItemByName<Genre>(Genre.GetPath, name);
}
/// <summary>
@@ -873,7 +900,7 @@ namespace Emby.Server.Implementations.Library
/// <returns>Task{MusicGenre}.</returns>
public MusicGenre GetMusicGenre(string name)
{
- return CreateItemByName<MusicGenre>(MusicGenre.GetPath(name), name);
+ return CreateItemByName<MusicGenre>(MusicGenre.GetPath, name);
}
/// <summary>
@@ -883,7 +910,7 @@ namespace Emby.Server.Implementations.Library
/// <returns>Task{GameGenre}.</returns>
public GameGenre GetGameGenre(string name)
{
- return CreateItemByName<GameGenre>(GameGenre.GetPath(name), name);
+ return CreateItemByName<GameGenre>(GameGenre.GetPath, name);
}
/// <summary>
@@ -901,7 +928,7 @@ namespace Emby.Server.Implementations.Library
var name = value.ToString(CultureInfo.InvariantCulture);
- return CreateItemByName<Year>(Year.GetPath(name), name);
+ return CreateItemByName<Year>(Year.GetPath, name);
}
/// <summary>
@@ -911,10 +938,10 @@ namespace Emby.Server.Implementations.Library
/// <returns>Task{Genre}.</returns>
public MusicArtist GetArtist(string name)
{
- return CreateItemByName<MusicArtist>(MusicArtist.GetPath(name), name);
+ return CreateItemByName<MusicArtist>(MusicArtist.GetPath, name);
}
- private T CreateItemByName<T>(string path, string name)
+ private T CreateItemByName<T>(Func<string,string> getPathFn, string name)
where T : BaseItem, new()
{
if (typeof(T) == typeof(MusicArtist))
@@ -935,7 +962,9 @@ namespace Emby.Server.Implementations.Library
}
}
- var id = GetNewItemId(path, typeof(T));
+ var path = getPathFn(name);
+ var forceCaseInsensitiveId = ConfigurationManager.Configuration.EnableNormalizedItemByNameIds;
+ var id = GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId);
var item = GetItemById(id) as T;
diff --git a/Emby.Server.Implementations/Library/UserManager.cs b/Emby.Server.Implementations/Library/UserManager.cs
index eb0d0cf9b..3b11a4767 100644
--- a/Emby.Server.Implementations/Library/UserManager.cs
+++ b/Emby.Server.Implementations/Library/UserManager.cs
@@ -434,6 +434,11 @@ namespace Emby.Server.Implementations.Library
Policy = user.Policy
};
+ if (!hasPassword && Users.Count() == 1)
+ {
+ dto.EnableAutoLogin = true;
+ }
+
var image = user.GetImageInfo(ImageType.Primary, 0);
if (image != null)
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 926d82f94..eea562524 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -1276,6 +1276,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return;
}
+ var registration = await _liveTvManager.GetRegistrationInfo("dvr").ConfigureAwait(false);
+ if (!registration.IsValid)
+ {
+ _logger.Warn("Emby Premiere required to use Emby DVR.");
+ OnTimerOutOfDate(timer);
+ return;
+ }
+
var activeRecordingInfo = new ActiveRecordingInfo
{
CancellationTokenSource = new CancellationTokenSource(),
@@ -2319,6 +2327,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
UpdateExistingTimerWithNewMetadata(existingTimer, timer);
+ // Needed by ShouldCancelTimerForSeriesTimer
+ timer.IsManual = existingTimer.IsManual;
+
if (ShouldCancelTimerForSeriesTimer(seriesTimer, timer))
{
existingTimer.Status = RecordingStatus.Cancelled;
@@ -2531,6 +2542,86 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public ProgramInfo Program { get; set; }
public CancellationTokenSource CancellationTokenSource { get; set; }
}
+
+ private const int TunerDiscoveryDurationMs = 3000;
+
+ public async Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken)
+ {
+ var list = new List<TunerHostInfo>();
+
+ var configuredDeviceIds = GetConfiguration().TunerHosts
+ .Where(i => !string.IsNullOrWhiteSpace(i.DeviceId))
+ .Select(i => i.DeviceId)
+ .ToList();
+
+ foreach (var host in _liveTvManager.TunerHosts)
+ {
+ var discoveredDevices = await DiscoverDevices(host, TunerDiscoveryDurationMs, cancellationToken).ConfigureAwait(false);
+
+ if (newDevicesOnly)
+ {
+ discoveredDevices = discoveredDevices.Where(d => !configuredDeviceIds.Contains(d.DeviceId, StringComparer.OrdinalIgnoreCase))
+ .ToList();
+ }
+ list.AddRange(discoveredDevices);
+ }
+
+ return list;
+ }
+
+ public async Task ScanForTunerDeviceChanges(CancellationToken cancellationToken)
+ {
+ foreach (var host in _liveTvManager.TunerHosts)
+ {
+ await ScanForTunerDeviceChanges(host, cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ private async Task ScanForTunerDeviceChanges(ITunerHost host, CancellationToken cancellationToken)
+ {
+ var discoveredDevices = await DiscoverDevices(host, TunerDiscoveryDurationMs, cancellationToken).ConfigureAwait(false);
+
+ var configuredDevices = GetConfiguration().TunerHosts
+ .Where(i => string.Equals(i.Type, host.Type, StringComparison.OrdinalIgnoreCase))
+ .ToList();
+
+ foreach (var device in discoveredDevices)
+ {
+ var configuredDevice = configuredDevices.FirstOrDefault(i => string.Equals(i.DeviceId, device.DeviceId, StringComparison.OrdinalIgnoreCase));
+
+ if (configuredDevice != null)
+ {
+ if (!string.Equals(device.Url, configuredDevice.Url, StringComparison.OrdinalIgnoreCase))
+ {
+ _logger.Info("Tuner url has changed from {0} to {1}", configuredDevice.Url, device.Url);
+
+ configuredDevice.Url = device.Url;
+ await _liveTvManager.SaveTunerHost(configuredDevice).ConfigureAwait(false);
+ }
+ }
+ }
+ }
+
+ private async Task<List<TunerHostInfo>> DiscoverDevices(ITunerHost host, int discoveryDuationMs, CancellationToken cancellationToken)
+ {
+ try
+ {
+ var discoveredDevices = await host.DiscoverDevices(discoveryDuationMs, cancellationToken).ConfigureAwait(false);
+
+ foreach (var device in discoveredDevices)
+ {
+ _logger.Info("Discovered tuner device {0} at {1}", host.Name, device.Url);
+ }
+
+ return discoveredDevices;
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error discovering tuner devices", ex);
+
+ return new List<TunerHostInfo>();
+ }
+ }
}
public static class ConfigurationExtension
{
diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
index c22bb1171..21c4006a6 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
@@ -205,6 +205,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
programInfo.ShowId = uniqueString.GetMD5().ToString("N");
+
+ // If we don't have valid episode info, assume it's a unique program, otherwise recordings might be skipped
+ if (programInfo.IsSeries && !programInfo.IsRepeat)
+ {
+ if ((programInfo.EpisodeNumber ?? 0) == 0)
+ {
+ programInfo.ShowId = programInfo.ShowId + programInfo.StartDate.Ticks.ToString(CultureInfo.InvariantCulture);
+ }
+ }
}
// Construct an id from the channel and start date
diff --git a/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs b/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs
index e2f973699..9a8a930bd 100644
--- a/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveStreamHelper.cs
@@ -6,7 +6,6 @@ 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
@@ -16,6 +15,8 @@ namespace Emby.Server.Implementations.LiveTv
private readonly IMediaEncoder _mediaEncoder;
private readonly ILogger _logger;
+ const int AnalyzeDurationMs = 2000;
+
public LiveStreamHelper(IMediaEncoder mediaEncoder, ILogger logger)
{
_mediaEncoder = mediaEncoder;
@@ -34,7 +35,7 @@ namespace Emby.Server.Implementations.LiveTv
Protocol = mediaSource.Protocol,
MediaType = isAudio ? DlnaProfileType.Audio : DlnaProfileType.Video,
ExtractChapters = false,
- AnalyzeDurationSections = 2
+ AnalyzeDurationMs = AnalyzeDurationMs
}, cancellationToken).ConfigureAwait(false);
@@ -98,6 +99,8 @@ namespace Emby.Server.Implementations.LiveTv
// Try to estimate this
mediaSource.InferTotalBitrate(true);
+
+ mediaSource.AnalyzeDurationMs = AnalyzeDurationMs;
}
}
}
diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
index 654777b3c..8406d44a7 100644
--- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -150,6 +150,21 @@ namespace Emby.Server.Implementations.LiveTv
get { return _listingProviders; }
}
+ public List<NameIdPair> GetTunerHostTypes()
+ {
+ return _tunerHosts.OrderBy(i => i.Name).Select(i => new NameIdPair
+ {
+ Name = i.Name,
+ Id = i.Type
+
+ }).ToList();
+ }
+
+ public Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken)
+ {
+ return EmbyTV.EmbyTV.Current.DiscoverTuners(newDevicesOnly, cancellationToken);
+ }
+
void service_DataSourceChanged(object sender, EventArgs e)
{
if (!_isDisposed)
@@ -1180,6 +1195,8 @@ namespace Emby.Server.Implementations.LiveTv
{
EmbyTV.EmbyTV.Current.CreateRecordingFolders();
+ await EmbyTV.EmbyTV.Current.ScanForTunerDeviceChanges(cancellationToken).ConfigureAwait(false);
+
var numComplete = 0;
double progressPerService = _services.Count == 0
? 0
@@ -1596,6 +1613,7 @@ namespace Emby.Server.Implementations.LiveTv
IsFolder = false,
IsVirtualItem = false,
Limit = query.Limit,
+ StartIndex = query.StartIndex,
SortBy = new[] { ItemSortBy.DateCreated },
SortOrder = SortOrder.Descending,
EnableTotalRecordCount = query.EnableTotalRecordCount,
@@ -2747,7 +2765,7 @@ namespace Emby.Server.Implementations.LiveTv
private bool IsLiveTvEnabled(User user)
{
- return user.Policy.EnableLiveTvAccess && (Services.Count > 1 || GetConfiguration().TunerHosts.Count(i => i.IsEnabled) > 0);
+ return user.Policy.EnableLiveTvAccess && (Services.Count > 1 || GetConfiguration().TunerHosts.Count > 0);
}
public IEnumerable<User> GetEnabledUsers()
@@ -2985,7 +3003,7 @@ namespace Emby.Server.Implementations.LiveTv
if (string.Equals(feature, "dvr-l", StringComparison.OrdinalIgnoreCase))
{
var config = GetConfiguration();
- if (config.TunerHosts.Count(i => i.IsEnabled) > 0 &&
+ if (config.TunerHosts.Count > 0 &&
config.ListingProviders.Count(i => (i.EnableAllTuners || i.EnabledTuners.Length > 0) && string.Equals(i.Type, SchedulesDirect.TypeName, StringComparison.OrdinalIgnoreCase)) > 0)
{
return Task.FromResult(new MBRegistrationRecord
@@ -2999,50 +3017,6 @@ namespace Emby.Server.Implementations.LiveTv
return _security.GetRegistrationStatus(feature);
}
- public List<NameValuePair> GetSatIniMappings()
- {
- return new List<NameValuePair>();
- //var names = GetType().Assembly.GetManifestResourceNames().Where(i => i.IndexOf("SatIp.ini", StringComparison.OrdinalIgnoreCase) != -1).ToList();
-
- //return names.Select(GetSatIniMappings).Where(i => i != null).DistinctBy(i => i.Value.Split('|')[0]).ToList();
- }
-
- public NameValuePair GetSatIniMappings(string resource)
- {
- return new NameValuePair();
- //using (var stream = GetType().Assembly.GetManifestResourceStream(resource))
- //{
- // using (var reader = new StreamReader(stream))
- // {
- // var parser = new StreamIniDataParser();
- // IniData data = parser.ReadData(reader);
-
- // var satType1 = data["SATTYPE"]["1"];
- // var satType2 = data["SATTYPE"]["2"];
-
- // if (string.IsNullOrWhiteSpace(satType2))
- // {
- // return null;
- // }
-
- // var srch = "SatIp.ini.";
- // var filename = Path.GetFileName(resource);
-
- // return new NameValuePair
- // {
- // Name = satType1 + " " + satType2,
- // Value = satType2 + "|" + filename.Substring(filename.IndexOf(srch) + srch.Length)
- // };
- // }
- //}
- }
-
- public Task<List<ChannelInfo>> GetSatChannelScanResult(TunerHostInfo info, CancellationToken cancellationToken)
- {
- return Task.FromResult(new List<ChannelInfo>());
- //return new TunerHosts.SatIp.ChannelScan(_logger).Scan(info, cancellationToken);
- }
-
public Task<List<ChannelInfo>> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken)
{
var info = GetConfiguration().ListingProviders.First(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
diff --git a/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
index f2806292d..5582d8f35 100644
--- a/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
+++ b/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs
@@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.LiveTv
public bool IsHidden
{
- get { return _liveTvManager.Services.Count == 1 && GetConfiguration().TunerHosts.Count(i => i.IsEnabled) == 0; }
+ get { return _liveTvManager.Services.Count == 1 && GetConfiguration().TunerHosts.Count == 0; }
}
public bool IsEnabled
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
index 5ac3812b0..c9cd289e7 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
@@ -69,7 +69,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
protected virtual List<TunerHostInfo> GetTunerHosts()
{
return GetConfiguration().TunerHosts
- .Where(i => i.IsEnabled && string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase))
+ .Where(i => string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase))
.ToList();
}
@@ -135,8 +135,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
// Check to make sure the tuner is available
// If there's only one tuner, don't bother with the check and just let the tuner be the one to throw an error
- if (hostsWithChannel.Count > 1 &&
- !await IsAvailable(host, channelId, cancellationToken).ConfigureAwait(false))
+ if (hostsWithChannel.Count > 1 && !await IsAvailable(host, channelId, cancellationToken).ConfigureAwait(false))
{
Logger.Error("Tuner is not currently available");
continue;
@@ -208,6 +207,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
foreach (var host in hostsWithChannel)
{
+ if (!channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
try
{
var liveStream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false);
@@ -243,7 +247,22 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
protected abstract Task<bool> IsAvailableInternal(TunerHostInfo tuner, string channelId, CancellationToken cancellationToken);
- protected abstract bool IsValidChannelId(string channelId);
+ protected virtual string ChannelIdPrefix
+ {
+ get
+ {
+ return Type + "_";
+ }
+ }
+ protected virtual bool IsValidChannelId(string channelId)
+ {
+ if (string.IsNullOrWhiteSpace(channelId))
+ {
+ throw new ArgumentNullException("channelId");
+ }
+
+ return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase);
+ }
protected LiveTvOptions GetConfiguration()
{
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs
deleted file mode 100644
index 336469c50..000000000
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs
+++ /dev/null
@@ -1,158 +0,0 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.LiveTv;
-using MediaBrowser.Controller.Plugins;
-using MediaBrowser.Model.Extensions;
-using MediaBrowser.Model.LiveTv;
-using MediaBrowser.Model.Logging;
-using System;
-using System.Linq;
-using System.Threading;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Events;
-using MediaBrowser.Model.Serialization;
-
-namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
-{
- public class HdHomerunDiscovery : IServerEntryPoint
- {
- private readonly IDeviceDiscovery _deviceDiscovery;
- private readonly IServerConfigurationManager _config;
- private readonly ILogger _logger;
- private readonly ILiveTvManager _liveTvManager;
- private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
- private readonly IHttpClient _httpClient;
- private readonly IJsonSerializer _json;
-
- public HdHomerunDiscovery(IDeviceDiscovery deviceDiscovery, IServerConfigurationManager config, ILogger logger, ILiveTvManager liveTvManager, IHttpClient httpClient, IJsonSerializer json)
- {
- _deviceDiscovery = deviceDiscovery;
- _config = config;
- _logger = logger;
- _liveTvManager = liveTvManager;
- _httpClient = httpClient;
- _json = json;
- }
-
- public void Run()
- {
- _deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered;
- }
-
- void _deviceDiscovery_DeviceDiscovered(object sender, GenericEventArgs<UpnpDeviceInfo> e)
- {
- string server = null;
- var info = e.Argument;
-
- if (info.Headers.TryGetValue("SERVER", out server) && server.IndexOf("HDHomeRun", StringComparison.OrdinalIgnoreCase) != -1)
- {
- string location;
- if (info.Headers.TryGetValue("Location", out location))
- {
- //_logger.Debug("HdHomerun found at {0}", location);
-
- // Just get the beginning of the url
- Uri uri;
- if (Uri.TryCreate(location, UriKind.Absolute, out uri))
- {
- var apiUrl = location.Replace(uri.LocalPath, String.Empty, StringComparison.OrdinalIgnoreCase)
- .TrimEnd('/');
-
- //_logger.Debug("HdHomerun api url: {0}", apiUrl);
- AddDevice(apiUrl);
- }
- }
- }
- }
-
- private async void AddDevice(string url)
- {
- await _semaphore.WaitAsync().ConfigureAwait(false);
-
- try
- {
- var options = GetConfiguration();
-
- if (options.TunerHosts.Any(i =>
- string.Equals(i.Type, HdHomerunHost.DeviceType, StringComparison.OrdinalIgnoreCase) &&
- UriEquals(i.Url, url)))
- {
- return;
- }
-
- // Strip off the port
- url = new Uri(url).GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Port, UriFormat.UriEscaped).TrimEnd('/');
-
- // Test it by pulling down the lineup
- using (var stream = await _httpClient.Get(new HttpRequestOptions
- {
- Url = string.Format("{0}/discover.json", url),
- CancellationToken = CancellationToken.None,
- BufferContent = false
- }))
- {
- var response = _json.DeserializeFromStream<HdHomerunHost.DiscoverResponse>(stream);
-
- var existing = GetConfiguration().TunerHosts
- .FirstOrDefault(i => string.Equals(i.Type, HdHomerunHost.DeviceType, StringComparison.OrdinalIgnoreCase) && string.Equals(i.DeviceId, response.DeviceID, StringComparison.OrdinalIgnoreCase));
-
- if (existing == null)
- {
- await _liveTvManager.SaveTunerHost(new TunerHostInfo
- {
- Type = HdHomerunHost.DeviceType,
- Url = url,
- DeviceId = response.DeviceID
-
- }).ConfigureAwait(false);
- }
- else
- {
- if (!string.Equals(existing.Url, url, StringComparison.OrdinalIgnoreCase))
- {
- existing.Url = url;
- await _liveTvManager.SaveTunerHost(existing).ConfigureAwait(false);
- }
- }
- }
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error saving device", ex);
- }
- finally
- {
- _semaphore.Release();
- }
- }
-
- private bool UriEquals(string savedUri, string location)
- {
- return string.Equals(NormalizeUrl(location), NormalizeUrl(savedUri), StringComparison.OrdinalIgnoreCase);
- }
-
- private string NormalizeUrl(string url)
- {
- if (!url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- {
- url = "http://" + url;
- }
-
- url = url.TrimEnd('/');
-
- // Strip off the port
- return new Uri(url).GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Port, UriFormat.UriEscaped);
- }
-
- private LiveTvOptions GetConfiguration()
- {
- return _config.GetConfiguration<LiveTvOptions>("livetv");
- }
-
- public void Dispose()
- {
- }
- }
-}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index af3f1359f..d9c0807de 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -56,7 +56,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
get { return "hdhomerun"; }
}
- private const string ChannelIdPrefix = "hdhr_";
+ protected override string ChannelIdPrefix
+ {
+ get
+ {
+ return "hdhr_";
+ }
+ }
private string GetChannelId(TunerHostInfo info, Channels i)
{
@@ -69,9 +75,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private async Task<List<Channels>> GetLineup(TunerHostInfo info, CancellationToken cancellationToken)
{
+ var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
+
var options = new HttpRequestOptions
{
- Url = string.Format("{0}/lineup.json", GetApiUrl(info, false)),
+ Url = model.LineupURL,
CancellationToken = cancellationToken,
BufferContent = false
};
@@ -213,7 +221,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
var list = new List<LiveTvTunerInfo>();
foreach (var host in GetConfiguration().TunerHosts
- .Where(i => i.IsEnabled && string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase)))
+ .Where(i => string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase)))
{
try
{
@@ -451,7 +459,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
string nal = null;
- var url = info.Url;
+ var url = GetApiUrl(info, false);
var id = channelId;
id += "_" + url.GetMD5().ToString("N");
@@ -557,26 +565,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
return list;
}
- protected override bool IsValidChannelId(string channelId)
- {
- if (string.IsNullOrWhiteSpace(channelId))
- {
- throw new ArgumentNullException("channelId");
- }
-
- return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase);
- }
-
protected override async Task<LiveStream> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
{
var profile = streamId.Split('_')[0];
Logger.Info("GetChannelStream: channel id: {0}. stream id: {1} profile: {2}", channelId, streamId, profile);
- if (!channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase))
- {
- throw new ArgumentException("Channel not found");
- }
var hdhrId = GetHdHrIdFromChannelId(channelId);
var channels = await GetChannels(info, true, CancellationToken.None).ConfigureAwait(false);
@@ -586,28 +580,23 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
if (hdhomerunChannel != null && hdhomerunChannel.IsLegacyTuner)
{
- var modelInfo = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
var mediaSource = GetLegacyMediaSource(info, hdhrId, channelInfo);
+ var modelInfo = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
- var liveStream = new HdHomerunUdpStream(mediaSource, streamId, hdhomerunChannel.Url, modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager);
- return liveStream;
+ return new HdHomerunUdpStream(mediaSource, streamId, new LegacyHdHomerunChannelCommands(hdhomerunChannel.Url), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager);
}
else
{
var mediaSource = GetMediaSource(info, hdhrId, channelInfo, profile);
+ //var modelInfo = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
- var liveStream = new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost);
- return liveStream;
+ return new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost);
+ //return new HdHomerunUdpStream(mediaSource, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager);
}
}
public async Task Validate(TunerHostInfo info)
{
- if (!info.IsEnabled)
- {
- return;
- }
-
lock (_modelCache)
{
_modelCache.Clear();
@@ -650,5 +639,75 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
public string LineupURL { get; set; }
public int TunerCount { get; set; }
}
+
+ public async Task<List<TunerHostInfo>> DiscoverDevices(int discoveryDurationMs, CancellationToken cancellationToken)
+ {
+ cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(new CancellationTokenSource(discoveryDurationMs).Token, cancellationToken).Token;
+ var list = new List<TunerHostInfo>();
+
+ // Create udp broadcast discovery message
+ byte[] discBytes = { 0, 2, 0, 12, 1, 4, 255, 255, 255, 255, 2, 4, 255, 255, 255, 255, 115, 204, 125, 143 };
+ using (var udpClient = _socketFactory.CreateUdpBroadcastSocket(0))
+ {
+ // Need a way to set the Receive timeout on the socket otherwise this might never timeout?
+ try
+ {
+ await udpClient.SendAsync(discBytes, discBytes.Length, new IpEndPointInfo(new IpAddressInfo("255.255.255.255", IpAddressFamily.InterNetwork), 65001), cancellationToken);
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ var response = await udpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false);
+ var deviceIp = response.RemoteEndPoint.IpAddress.Address;
+
+ // check to make sure we have enough bytes received to be a valid message and make sure the 2nd byte is the discover reply byte
+ if (response.ReceivedBytes > 13 && response.Buffer[1] == 3)
+ {
+ var deviceAddress = "http://" + deviceIp;
+
+ var info = await TryGetTunerHostInfo(deviceAddress, cancellationToken).ConfigureAwait(false);
+
+ if (info != null)
+ {
+ list.Add(info);
+ }
+ }
+ }
+
+ }
+ catch (OperationCanceledException)
+ {
+ }
+ catch
+ {
+ // Socket timeout indicates all messages have been received.
+ }
+ }
+
+ return list;
+ }
+
+ private async Task<TunerHostInfo> TryGetTunerHostInfo(string url, CancellationToken cancellationToken)
+ {
+ var hostInfo = new TunerHostInfo
+ {
+ Type = Type,
+ Url = url
+ };
+
+ try
+ {
+ var modelInfo = await GetModelInfo(hostInfo, false, cancellationToken).ConfigureAwait(false);
+
+ hostInfo.DeviceId = modelInfo.DeviceID;
+ hostInfo.FriendlyName = modelInfo.FriendlyName;
+
+ return hostInfo;
+ }
+ catch
+ {
+ // logged at lower levels
+ }
+
+ return null;
+ }
}
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
index a6e9491a4..2c678d9f8 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
@@ -9,6 +9,60 @@ using MediaBrowser.Model.Net;
namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
+ public interface IHdHomerunChannelCommands
+ {
+ IEnumerable<Tuple<string, string>> GetCommands();
+ }
+
+ public class LegacyHdHomerunChannelCommands : IHdHomerunChannelCommands
+ {
+ private string _channel;
+ private string _program;
+ public LegacyHdHomerunChannelCommands(string url)
+ {
+ // parse url for channel and program
+ var regExp = new Regex(@"\/ch(\d+)-?(\d*)");
+ var match = regExp.Match(url);
+ if (match.Success)
+ {
+ _channel = match.Groups[1].Value;
+ _program = match.Groups[2].Value;
+ }
+ }
+
+ public IEnumerable<Tuple<string, string>> GetCommands()
+ {
+ var commands = new List<Tuple<string, string>>();
+
+ if (!String.IsNullOrEmpty(_channel))
+ commands.Add(Tuple.Create("channel", _channel));
+
+ if (!String.IsNullOrEmpty(_program))
+ commands.Add(Tuple.Create("program", _program));
+ return commands;
+ }
+ }
+
+ public class HdHomerunChannelCommands : IHdHomerunChannelCommands
+ {
+ private string _channel;
+
+ public HdHomerunChannelCommands(string channel)
+ {
+ _channel = channel;
+ }
+
+ public IEnumerable<Tuple<string, string>> GetCommands()
+ {
+ var commands = new List<Tuple<string, string>>();
+
+ if (!String.IsNullOrEmpty(_channel))
+ commands.Add(Tuple.Create("vchannel", _channel));
+
+ return commands;
+ }
+ }
+
public class HdHomerunManager : IDisposable
{
public static int HdHomeRunPort = 65001;
@@ -57,16 +111,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
return string.Equals(returnVal, "none", StringComparison.OrdinalIgnoreCase);
}
- public async Task StartStreaming(IpAddressInfo remoteIp, IpAddressInfo localIp, int localPort, string url, int numTuners, CancellationToken cancellationToken)
+ public async Task StartStreaming(IpAddressInfo remoteIp, IpAddressInfo localIp, int localPort, IHdHomerunChannelCommands commands, int numTuners, CancellationToken cancellationToken)
{
_remoteIp = remoteIp;
- // parse url for channel and program
- string frequency, program;
- if (!ParseUrl(url, out frequency, out program))
- {
- return;
- }
-
+
using (var tcpClient = _socketFactory.CreateTcpSocket(_remoteIp, HdHomeRunPort))
{
if (!_lockkey.HasValue)
@@ -92,29 +140,21 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal))
continue;
- var channelMsg = CreateSetMessage(i, "channel", frequency, _lockkey.Value);
- await tcpClient.SendAsync(channelMsg, channelMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
- await tcpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false);
- // parse response to make sure it worked
- if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal))
+ var commandList = commands.GetCommands();
+ foreach(Tuple<string,string> command in commandList)
{
- await ReleaseLockkey(tcpClient).ConfigureAwait(false);
- continue;
- }
-
- if (program != String.Empty)
- {
- var programMsg = CreateSetMessage(i, "program", program, _lockkey.Value);
- await tcpClient.SendAsync(programMsg, programMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
- await tcpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false);
+ var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, _lockkey.Value);
+ await tcpClient.SendAsync(channelMsg, channelMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false);
+ response = await tcpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false);
// parse response to make sure it worked
if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal))
{
await ReleaseLockkey(tcpClient).ConfigureAwait(false);
continue;
}
- }
+ }
+
var targetValue = String.Format("rtp://{0}:{1}", localIp, localPort);
var targetMsg = CreateSetMessage(i, "target", targetValue, _lockkey.Value);
@@ -132,6 +172,29 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
}
}
+ public async Task ChangeChannel(IHdHomerunChannelCommands commands, CancellationToken cancellationToken)
+ {
+ if (!_lockkey.HasValue)
+ return;
+
+ using (var tcpClient = _socketFactory.CreateTcpSocket(_remoteIp, HdHomeRunPort))
+ {
+ var commandList = commands.GetCommands();
+ foreach (Tuple<string, string> command in commandList)
+ {
+ var channelMsg = CreateSetMessage(_activeTuner, command.Item1, command.Item2, _lockkey.Value);
+ await tcpClient.SendAsync(channelMsg, channelMsg.Length, new IpEndPointInfo(_remoteIp, HdHomeRunPort), cancellationToken).ConfigureAwait(false);
+ var response = await tcpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false);
+ // parse response to make sure it worked
+ string returnVal;
+ if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal))
+ {
+ return;
+ }
+ }
+ }
+ }
+
public async Task StopStreaming()
{
if (!_lockkey.HasValue)
@@ -154,22 +217,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
await tcpClient.ReceiveAsync(CancellationToken.None).ConfigureAwait(false);
}
- private static bool ParseUrl(string url, out string frequency, out string program)
- {
- frequency = String.Empty;
- program = String.Empty;
- var regExp = new Regex(@"\/ch(\d+)-?(\d*)");
- var match = regExp.Match(url);
- if (match.Success)
- {
- frequency = match.Groups[1].Value;
- program = match.Groups[2].Value;
- return true;
- }
-
- return false;
- }
-
private static byte[] CreateGetMessage(int tuner, string name)
{
var byteName = Encoding.UTF8.GetBytes(String.Format("/tuner{0}/{1}\0", tuner, name));
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
index 8c749b1b5..a881d0ea1 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs
@@ -29,11 +29,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private readonly CancellationTokenSource _liveStreamCancellationTokenSource = new CancellationTokenSource();
private readonly TaskCompletionSource<bool> _liveStreamTaskCompletionSource = new TaskCompletionSource<bool>();
private readonly MulticastStream _multicastStream;
- private readonly string _channelUrl;
+ private readonly IHdHomerunChannelCommands _channelCommands;
private readonly int _numTuners;
private readonly INetworkManager _networkManager;
- public HdHomerunUdpStream(MediaSourceInfo mediaSource, string originalStreamId, string channelUrl, int numTuners, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager)
+ public HdHomerunUdpStream(MediaSourceInfo mediaSource, string originalStreamId, IHdHomerunChannelCommands channelCommands, int numTuners, IFileSystem fileSystem, IHttpClient httpClient, ILogger logger, IServerApplicationPaths appPaths, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager)
: base(mediaSource)
{
_fileSystem = fileSystem;
@@ -45,7 +45,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
_networkManager = networkManager;
OriginalStreamId = originalStreamId;
_multicastStream = new MulticastStream(_logger);
- _channelUrl = channelUrl;
+ _channelCommands = channelCommands;
_numTuners = numTuners;
}
@@ -118,10 +118,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
try
{
// send url to start streaming
- await hdHomerunManager.StartStreaming(remoteAddress, localAddress, localPort, _channelUrl, _numTuners, cancellationToken).ConfigureAwait(false);
+ await hdHomerunManager.StartStreaming(remoteAddress, localAddress, localPort, _channelCommands, _numTuners, cancellationToken).ConfigureAwait(false);
var response = await udpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false);
- _logger.Info("Opened HDHR UDP stream from {0}", _channelUrl);
+ _logger.Info("Opened HDHR UDP stream from {0}", remoteAddress);
if (!cancellationToken.IsCancellationRequested)
{
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
index a939cec7b..4ec70f802 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
@@ -46,8 +46,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
get { return "M3U Tuner"; }
}
- private const string ChannelIdPrefix = "m3u_";
-
protected override async Task<List<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
{
var result = await new M3uParser(Logger, _fileSystem, _httpClient, _appHost).Parse(info.Url, ChannelIdPrefix, info.Id, !info.EnableTvgId, cancellationToken).ConfigureAwait(false);
@@ -87,16 +85,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
}
}
- protected override bool IsValidChannelId(string channelId)
- {
- if (string.IsNullOrWhiteSpace(channelId))
- {
- throw new ArgumentNullException("channelId");
- }
-
- return channelId.StartsWith(ChannelIdPrefix, StringComparison.OrdinalIgnoreCase);
- }
-
protected override async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(TunerHostInfo info, string channelId, CancellationToken cancellationToken)
{
var urlHash = info.Url.GetMD5().ToString("N");
@@ -176,5 +164,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{
return Task.FromResult(true);
}
+
+ public Task<List<TunerHostInfo>> DiscoverDevices(int discoveryDurationMs, CancellationToken cancellationToken)
+ {
+ return Task.FromResult(new List<TunerHostInfo>());
+ }
}
} \ No newline at end of file
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs
index df83d4341..90ff36441 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs
@@ -16,7 +16,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
private const int BufferSize = 81920;
private CancellationToken _cancellationToken;
private readonly ILogger _logger;
- private readonly ConcurrentQueue<byte[]> _sharedBuffer = new ConcurrentQueue<byte[]>();
public MulticastStream(ILogger logger)
{
@@ -38,14 +37,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
byte[] copy = new byte[bytesRead];
Buffer.BlockCopy(buffer, 0, copy, 0, bytesRead);
- _sharedBuffer.Enqueue(copy);
-
- while (_sharedBuffer.Count > 3000)
- {
- byte[] bytes;
- _sharedBuffer.TryDequeue(out bytes);
- }
-
var allStreams = _outputStreams.ToList();
foreach (var stream in allStreams)
{
@@ -74,16 +65,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
OnFinished = OnFinished
};
- var list = new List<byte>();
- foreach (var bytes in _sharedBuffer)
- {
- list.AddRange(bytes);
- }
-
- _logger.Info("QueueStream started with {0} initial bytes", list.Count);
-
- result.Queue(list.ToArray());
-
_outputStreams.TryAdd(result.Id, result);
result.Start(_cancellationToken);
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index 120f445c2..64a41acef 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -408,6 +408,7 @@ namespace Emby.Server.Implementations.Localization
new LocalizatonOption{ Name="Italian", Value="it"},
new LocalizatonOption{ Name="Kazakh", Value="kk"},
new LocalizatonOption{ Name="Norwegian Bokmål", Value="nb"},
+ new LocalizatonOption{ Name="Persian", Value="fa"},
new LocalizatonOption{ Name="Polish", Value="pl"},
new LocalizatonOption{ Name="Portuguese (Brazil)", Value="pt-BR"},
new LocalizatonOption{ Name="Portuguese (Portugal)", Value="pt-PT"},
diff --git a/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs b/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs
index ef7d6dba8..9514c12ca 100644
--- a/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs
+++ b/Emby.Server.Implementations/Playlists/PlaylistImageProvider.cs
@@ -11,6 +11,7 @@ using System.Linq;
using System.Threading.Tasks;
using Emby.Server.Implementations.Images;
using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Library;
@@ -101,4 +102,35 @@ namespace Emby.Server.Implementations.Playlists
//}
}
+ public class GenreImageProvider : BaseDynamicImageProvider<Genre>
+ {
+ private readonly ILibraryManager _libraryManager;
+
+ public GenreImageProvider(IFileSystem fileSystem, IProviderManager providerManager, IApplicationPaths applicationPaths, IImageProcessor imageProcessor, ILibraryManager libraryManager) : base(fileSystem, providerManager, applicationPaths, imageProcessor)
+ {
+ _libraryManager = libraryManager;
+ }
+
+ protected override Task<List<BaseItem>> GetItemsWithImages(IHasImages item)
+ {
+ var items = _libraryManager.GetItemList(new InternalItemsQuery
+ {
+ Genres = new[] { item.Name },
+ IncludeItemTypes = new[] { typeof(Series).Name, typeof(Movie).Name },
+ SortBy = new[] { ItemSortBy.Random },
+ Limit = 4,
+ Recursive = true,
+ ImageTypes = new[] { ImageType.Primary }
+
+ }).ToList();
+
+ return Task.FromResult(GetFinalItems(items));
+ }
+
+ //protected override Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
+ //{
+ // return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary);
+ //}
+ }
+
}
diff --git a/Emby.Server.Implementations/Services/ResponseHelper.cs b/Emby.Server.Implementations/Services/ResponseHelper.cs
index d4ce1cabf..3c2af60db 100644
--- a/Emby.Server.Implementations/Services/ResponseHelper.cs
+++ b/Emby.Server.Implementations/Services/ResponseHelper.cs
@@ -12,45 +12,6 @@ namespace Emby.Server.Implementations.Services
{
public static class ResponseHelper
{
- private static async Task<bool> WriteToOutputStream(IResponse response, object result)
- {
- var asyncStreamWriter = result as IAsyncStreamWriter;
- if (asyncStreamWriter != null)
- {
- await asyncStreamWriter.WriteToAsync(response.OutputStream, CancellationToken.None).ConfigureAwait(false);
- return true;
- }
-
- var streamWriter = result as IStreamWriter;
- if (streamWriter != null)
- {
- streamWriter.WriteTo(response.OutputStream);
- return true;
- }
-
- var stream = result as Stream;
- if (stream != null)
- {
- using (stream)
- {
- await stream.CopyToAsync(response.OutputStream).ConfigureAwait(false);
- return true;
- }
- }
-
- var bytes = result as byte[];
- if (bytes != null)
- {
- response.ContentType = "application/octet-stream";
- response.SetContentLength(bytes.Length);
-
- await response.OutputStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
- return true;
- }
-
- return false;
- }
-
public static Task WriteToResponse(IResponse httpRes, IRequest httpReq, object result)
{
if (result == null)
@@ -141,16 +102,51 @@ namespace Emby.Server.Implementations.Services
response.ContentType += "; charset=utf-8";
}
- var writeToOutputStreamResult = await WriteToOutputStream(response, result).ConfigureAwait(false);
- if (writeToOutputStreamResult)
+ var asyncStreamWriter = result as IAsyncStreamWriter;
+ if (asyncStreamWriter != null)
{
+ await asyncStreamWriter.WriteToAsync(response.OutputStream, CancellationToken.None).ConfigureAwait(false);
+ return;
+ }
+
+ var streamWriter = result as IStreamWriter;
+ if (streamWriter != null)
+ {
+ streamWriter.WriteTo(response.OutputStream);
+ return;
+ }
+
+ var fileWriter = result as FileWriter;
+ if (fileWriter != null)
+ {
+ await fileWriter.WriteToAsync(response, CancellationToken.None).ConfigureAwait(false);
+ return;
+ }
+
+ var stream = result as Stream;
+ if (stream != null)
+ {
+ using (stream)
+ {
+ await stream.CopyToAsync(response.OutputStream).ConfigureAwait(false);
+ return;
+ }
+ }
+
+ var bytes = result as byte[];
+ if (bytes != null)
+ {
+ response.ContentType = "application/octet-stream";
+ response.SetContentLength(bytes.Length);
+
+ await response.OutputStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
return;
}
var responseText = result as string;
if (responseText != null)
{
- var bytes = Encoding.UTF8.GetBytes(responseText);
+ bytes = Encoding.UTF8.GetBytes(responseText);
response.SetContentLength(bytes.Length);
await response.OutputStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
return;
@@ -163,7 +159,7 @@ namespace Emby.Server.Implementations.Services
{
var contentType = request.ResponseContentType;
var serializer = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType);
-
+
using (var ms = new MemoryStream())
{
serializer(result, ms);
diff --git a/MediaBrowser.ServerApplication/Native/LoopbackUtil.cs b/Emby.Server.Implementations/Windows/LoopUtil.cs
index 5b260685b..6eded2cec 100644
--- a/MediaBrowser.ServerApplication/Native/LoopbackUtil.cs
+++ b/Emby.Server.Implementations/Windows/LoopUtil.cs
@@ -5,7 +5,7 @@ using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
-namespace MediaBrowser.ServerApplication.Native
+namespace Emby.Server.Implementations.Windows
{
/// <summary>
/// http://blogs.msdn.com/b/fiddler/archive/2011/12/10/fiddler-windows-8-apps-enable-LoopUtil-network-isolation-exemption.aspx
@@ -71,12 +71,12 @@ namespace MediaBrowser.ServerApplication.Native
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool ConvertStringSidToSid(string strSid, out IntPtr pSid);
- [DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)]
+ [DllImport("advapi32", /*CharSet = CharSet.Auto,*/ SetLastError = true)]
static extern bool ConvertSidToStringSid(
[MarshalAs(UnmanagedType.LPArray)] byte[] pSID,
out IntPtr ptrSid);
- [DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)]
+ [DllImport("advapi32", /*CharSet = CharSet.Auto,*/ SetLastError = true)]
static extern bool ConvertSidToStringSid(IntPtr pSid, out string strSid);
// Use this API to convert a string reference (e.g. "@{blah.pri?ms-resource://whatever}") into a plain string
@@ -178,7 +178,7 @@ namespace MediaBrowser.ServerApplication.Native
foreach (var app in Apps)
{
- if ((app.appContainerName ?? string.Empty).IndexOf(appName, StringComparison.OrdinalIgnoreCase) != -1 ||
+ if ((app.appContainerName ?? string.Empty).IndexOf(appName, StringComparison.OrdinalIgnoreCase) != -1 ||
(app.displayName ?? string.Empty).IndexOf(appName, StringComparison.OrdinalIgnoreCase) != -1)
{
if (!app.LoopUtil)
diff --git a/Emby.Server.Implementations/packages.config b/Emby.Server.Implementations/packages.config
index 7e638e171..ccabbc27b 100644
--- a/Emby.Server.Implementations/packages.config
+++ b/Emby.Server.Implementations/packages.config
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Emby.XmlTv" version="1.0.7" targetFramework="portable45-net45+win8" />
- <package id="MediaBrowser.Naming" version="1.0.4" targetFramework="portable45-net45+win8" />
+ <package id="MediaBrowser.Naming" version="1.0.5" targetFramework="portable45-net45+win8" />
<package id="SQLitePCL.pretty" version="1.1.0" targetFramework="portable45-net45+win8" />
<package id="SQLitePCLRaw.core" version="1.1.2" targetFramework="portable45-net45+win8" />
<package id="UniversalDetector" version="1.0.1" targetFramework="portable45-net45+win8" />
diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs
index dc4e57155..2d8744f70 100644
--- a/MediaBrowser.Api/LiveTv/LiveTvService.cs
+++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs
@@ -582,13 +582,13 @@ namespace MediaBrowser.Api.LiveTv
}
[Route("/LiveTv/ListingProviders/Default", "GET")]
- [Authenticated(AllowBeforeStartupWizard = true)]
+ [Authenticated]
public class GetDefaultListingProvider : ListingsProviderInfo, IReturn<ListingsProviderInfo>
{
}
[Route("/LiveTv/ListingProviders", "POST", Summary = "Adds a listing provider")]
- [Authenticated(AllowBeforeStartupWizard = true)]
+ [Authenticated]
public class AddListingProvider : ListingsProviderInfo, IReturn<ListingsProviderInfo>
{
public bool ValidateLogin { get; set; }
@@ -596,7 +596,7 @@ namespace MediaBrowser.Api.LiveTv
}
[Route("/LiveTv/ListingProviders", "DELETE", Summary = "Deletes a listing provider")]
- [Authenticated(AllowBeforeStartupWizard = true)]
+ [Authenticated]
public class DeleteListingProvider : IReturnVoid
{
[ApiMember(Name = "Id", Description = "Provider id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "DELETE")]
@@ -604,7 +604,7 @@ namespace MediaBrowser.Api.LiveTv
}
[Route("/LiveTv/ListingProviders/Lineups", "GET", Summary = "Gets available lineups")]
- [Authenticated(AllowBeforeStartupWizard = true)]
+ [Authenticated]
public class GetLineups : IReturn<List<NameIdPair>>
{
[ApiMember(Name = "Id", Description = "Provider id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
@@ -621,13 +621,13 @@ namespace MediaBrowser.Api.LiveTv
}
[Route("/LiveTv/ListingProviders/SchedulesDirect/Countries", "GET", Summary = "Gets available lineups")]
- [Authenticated(AllowBeforeStartupWizard = true)]
+ [Authenticated]
public class GetSchedulesDirectCountries
{
}
[Route("/LiveTv/ChannelMappingOptions")]
- [Authenticated(AllowBeforeStartupWizard = true)]
+ [Authenticated]
public class GetChannelMappingOptions
{
[ApiMember(Name = "Id", Description = "Provider id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
@@ -635,7 +635,7 @@ namespace MediaBrowser.Api.LiveTv
}
[Route("/LiveTv/ChannelMappings")]
- [Authenticated(AllowBeforeStartupWizard = true)]
+ [Authenticated]
public class SetChannelMapping
{
[ApiMember(Name = "Id", Description = "Provider id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
@@ -660,20 +660,6 @@ namespace MediaBrowser.Api.LiveTv
public string Feature { get; set; }
}
- [Route("/LiveTv/TunerHosts/Satip/IniMappings", "GET", Summary = "Gets available mappings")]
- [Authenticated(AllowBeforeStartupWizard = true)]
- public class GetSatIniMappings : IReturn<List<NameValuePair>>
- {
-
- }
-
- [Route("/LiveTv/TunerHosts/Satip/ChannelScan", "GET", Summary = "Scans for available channels")]
- [Authenticated(AllowBeforeStartupWizard = true)]
- public class GetSatChannnelScanResult : TunerHostInfo
- {
-
- }
-
[Route("/LiveTv/LiveStreamFiles/{Id}/stream.{Container}", "GET", Summary = "Gets a live tv channel")]
public class GetLiveStreamFile
{
@@ -687,6 +673,20 @@ namespace MediaBrowser.Api.LiveTv
public string Id { get; set; }
}
+ [Route("/LiveTv/TunerHosts/Types", "GET")]
+ [Authenticated]
+ public class GetTunerHostTypes : IReturn<List<NameIdPair>>
+ {
+
+ }
+
+ [Route("/LiveTv/Tuners/Discvover", "GET")]
+ [Authenticated]
+ public class DiscoverTuners : IReturn<List<TunerHostInfo>>
+ {
+ public bool NewDevicesOnly { get; set; }
+ }
+
public class LiveTvService : BaseApiService
{
private readonly ILiveTvManager _liveTvManager;
@@ -712,6 +712,12 @@ namespace MediaBrowser.Api.LiveTv
_sessionContext = sessionContext;
}
+ public object Get(GetTunerHostTypes request)
+ {
+ var list = _liveTvManager.GetTunerHostTypes();
+ return ToOptimizedResult(list);
+ }
+
public object Get(GetLiveRecordingFile request)
{
var path = _liveTvManager.GetEmbyTvActiveRecordingPath(request.Id);
@@ -731,6 +737,12 @@ namespace MediaBrowser.Api.LiveTv
};
}
+ public async Task<object> Get(DiscoverTuners request)
+ {
+ var result = await _liveTvManager.DiscoverTuners(request.NewDevicesOnly, CancellationToken.None).ConfigureAwait(false);
+ return ToOptimizedResult(result);
+ }
+
public async Task<object> Get(GetLiveStreamFile request)
{
var directStreamProvider = (await _liveTvManager.GetEmbyTvLiveStream(request.Id).ConfigureAwait(false)) as IDirectStreamProvider;
@@ -749,13 +761,6 @@ namespace MediaBrowser.Api.LiveTv
return ToOptimizedResult(new ListingsProviderInfo());
}
- public async Task<object> Get(GetSatChannnelScanResult request)
- {
- var result = await _liveTvManager.GetSatChannelScanResult(request, CancellationToken.None).ConfigureAwait(false);
-
- return ToOptimizedResult(result);
- }
-
public async Task<object> Get(GetLiveTvRegistrationInfo request)
{
var result = await _liveTvManager.GetRegistrationInfo(request.Feature).ConfigureAwait(false);
@@ -803,11 +808,6 @@ namespace MediaBrowser.Api.LiveTv
return ToOptimizedResult(result);
}
- public object Get(GetSatIniMappings request)
- {
- return ToOptimizedResult(_liveTvManager.GetSatIniMappings());
- }
-
public async Task<object> Get(GetSchedulesDirectCountries request)
{
// https://json.schedulesdirect.org/20141201/available/countries
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index e1559cabf..b3a00cc92 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -697,6 +697,20 @@ namespace MediaBrowser.Api.Playback
{
request.SubtitleCodec = val;
}
+ else if (i == 31)
+ {
+ if (videoRequest != null)
+ {
+ videoRequest.RequireNonAnamorphic = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
+ }
+ }
+ else if (i == 32)
+ {
+ if (videoRequest != null)
+ {
+ videoRequest.DeInterlace = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
+ }
+ }
}
}
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index aaca1793c..98115b840 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -41,9 +41,16 @@ namespace MediaBrowser.Api.Playback.Hls
/// <summary>
/// Gets the segment file extension.
/// </summary>
- /// <param name="state">The state.</param>
- /// <returns>System.String.</returns>
- protected abstract string GetSegmentFileExtension(StreamState state);
+ protected string GetSegmentFileExtension(StreamRequest request)
+ {
+ var segmentContainer = request.SegmentContainer;
+ if (!string.IsNullOrWhiteSpace(segmentContainer))
+ {
+ return "." + segmentContainer;
+ }
+
+ return ".ts";
+ }
/// <summary>
/// Gets the type of the transcoding job.
@@ -261,11 +268,17 @@ namespace MediaBrowser.Api.Playback.Hls
var useGenericSegmenter = false;
if (useGenericSegmenter)
{
- var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state);
+ var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state.Request);
var timeDeltaParam = String.Empty;
- return string.Format("{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} {10} -individual_header_trailer 0 -segment_format mpegts -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"",
+ var segmentFormat = GetSegmentFileExtension(state.Request).TrimStart('.');
+ if (string.Equals(segmentFormat, "ts", StringComparison.OrdinalIgnoreCase))
+ {
+ segmentFormat = "mpegts";
+ }
+
+ return string.Format("{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} {10} -individual_header_trailer 0 -segment_format {11} -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"",
inputModifier,
EncodingHelper.GetInputArgument(state, encodingOptions),
threads,
@@ -276,7 +289,8 @@ namespace MediaBrowser.Api.Playback.Hls
startNumberParam,
outputPath,
outputTsArg,
- timeDeltaParam
+ timeDeltaParam,
+ segmentFormat
).Trim();
}
@@ -286,7 +300,7 @@ namespace MediaBrowser.Api.Playback.Hls
var args = string.Format("{0} {1} {2} -map_metadata -1 -map_chapters -1 -threads {3} {4} {5} -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero {6} -hls_time {7} -individual_header_trailer 0 -start_number {8} -hls_list_size {9}{10} -y \"{11}\"",
itsOffset,
inputModifier,
- EncodingHelper.GetInputArgument(state, encodingOptions),
+ EncodingHelper.GetInputArgument(state, encodingOptions),
threads,
EncodingHelper.GetMapArgs(state),
GetVideoArguments(state),
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index 1074a8bc1..f77a66f8e 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -65,7 +65,7 @@ namespace MediaBrowser.Api.Playback.Hls
{
}
- [Route("/Videos/{Id}/hls1/{PlaylistId}/{SegmentId}.ts", "GET")]
+ [Route("/Videos/{Id}/hls1/{PlaylistId}/{SegmentId}.{SegmentContainer}", "GET")]
public class GetHlsVideoSegment : VideoStreamRequest
{
public string PlaylistId { get; set; }
@@ -77,8 +77,7 @@ namespace MediaBrowser.Api.Playback.Hls
public string SegmentId { get; set; }
}
- [Route("/Audio/{Id}/hls1/{PlaylistId}/{SegmentId}.aac", "GET")]
- [Route("/Audio/{Id}/hls1/{PlaylistId}/{SegmentId}.ts", "GET")]
+ [Route("/Audio/{Id}/hls1/{PlaylistId}/{SegmentId}.{SegmentContainer}", "GET")]
public class GetHlsAudioSegment : StreamRequest
{
public string PlaylistId { get; set; }
@@ -158,7 +157,7 @@ namespace MediaBrowser.Api.Playback.Hls
var segmentPath = GetSegmentPath(state, playlistPath, requestedIndex);
- var segmentExtension = GetSegmentFileExtension(state);
+ var segmentExtension = GetSegmentFileExtension(state.Request);
TranscodingJob job = null;
@@ -420,7 +419,7 @@ namespace MediaBrowser.Api.Playback.Hls
var filename = Path.GetFileNameWithoutExtension(playlist);
- return Path.Combine(folder, filename + index.ToString(UsCulture) + GetSegmentFileExtension(state));
+ return Path.Combine(folder, filename + index.ToString(UsCulture) + GetSegmentFileExtension(state.Request));
}
private async Task<object> GetSegmentResult(StreamState state, string playlistPath,
@@ -740,7 +739,7 @@ namespace MediaBrowser.Api.Playback.Hls
name,
index.ToString(UsCulture),
- GetSegmentFileExtension(isOutputVideo),
+ GetSegmentFileExtension(request),
queryString));
index++;
@@ -848,7 +847,7 @@ namespace MediaBrowser.Api.Playback.Hls
var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
- args += " " + EncodingHelper.GetVideoQualityParam(state, EncodingHelper.GetH264Encoder(state, encodingOptions), encodingOptions, GetDefaultH264Preset()) + keyFrameArg;
+ args += " " + EncodingHelper.GetVideoQualityParam(state, codec, encodingOptions, GetDefaultH264Preset()) + keyFrameArg;
//args += " -mixed-refs 0 -refs 3 -x264opts b_pyramid=0:weightb=0:weightp=0";
@@ -897,7 +896,7 @@ namespace MediaBrowser.Api.Playback.Hls
if (useGenericSegmenter)
{
- var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state);
+ var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state.Request);
var timeDeltaParam = String.Empty;
@@ -907,7 +906,13 @@ namespace MediaBrowser.Api.Playback.Hls
timeDeltaParam = string.Format("-segment_time_delta -{0}", startTime);
}
- return string.Format("{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} {10} -individual_header_trailer 0 -segment_format mpegts -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"",
+ var segmentFormat = GetSegmentFileExtension(state.Request).TrimStart('.');
+ if (string.Equals(segmentFormat, "ts", StringComparison.OrdinalIgnoreCase))
+ {
+ segmentFormat = "mpegts";
+ }
+
+ return string.Format("{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} {10} -individual_header_trailer 0 -segment_format {11} -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"",
inputModifier,
EncodingHelper.GetInputArgument(state, encodingOptions),
threads,
@@ -918,7 +923,8 @@ namespace MediaBrowser.Api.Playback.Hls
startNumberParam,
outputPath,
outputTsArg,
- timeDeltaParam
+ timeDeltaParam,
+ segmentFormat
).Trim();
}
@@ -935,20 +941,5 @@ namespace MediaBrowser.Api.Playback.Hls
outputPath
).Trim();
}
-
- /// <summary>
- /// Gets the segment file extension.
- /// </summary>
- /// <param name="state">The state.</param>
- /// <returns>System.String.</returns>
- protected override string GetSegmentFileExtension(StreamState state)
- {
- return GetSegmentFileExtension(state.IsOutputVideo);
- }
-
- protected string GetSegmentFileExtension(bool isOutputVideo)
- {
- return isOutputVideo ? ".ts" : ".ts";
- }
}
} \ No newline at end of file
diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
index f3683c6cb..bf78e16b3 100644
--- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
+++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
@@ -63,7 +63,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// <summary>
/// Class GetHlsVideoSegment
/// </summary>
- [Route("/Videos/{Id}/hls/{PlaylistId}/{SegmentId}.ts", "GET")]
+ [Route("/Videos/{Id}/hls/{PlaylistId}/{SegmentId}.{SegmentContainer}", "GET")]
public class GetHlsVideoSegmentLegacy : VideoStreamRequest
{
public string PlaylistId { get; set; }
diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
index c9c6acc1b..f9164c36f 100644
--- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
@@ -99,7 +99,7 @@ namespace MediaBrowser.Api.Playback.Hls
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.VideoRequest.SubtitleMethod == SubtitleDeliveryMethod.Encode;
var encodingOptions = ApiEntryPoint.Instance.GetEncodingOptions();
- args += " " + EncodingHelper.GetVideoQualityParam(state, EncodingHelper.GetH264Encoder(state, encodingOptions), encodingOptions, GetDefaultH264Preset()) + keyFrameArg;
+ args += " " + EncodingHelper.GetVideoQualityParam(state, codec, encodingOptions, GetDefaultH264Preset()) + keyFrameArg;
// Add resolution params, if specified
if (!hasGraphicalSubs)
@@ -118,16 +118,6 @@ namespace MediaBrowser.Api.Playback.Hls
return args;
}
- /// <summary>
- /// Gets the segment file extension.
- /// </summary>
- /// <param name="state">The state.</param>
- /// <returns>System.String.</returns>
- protected override string GetSegmentFileExtension(StreamState state)
- {
- return ".ts";
- }
-
public VideoHlsService(IServerConfigurationManager serverConfig, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IDlnaManager dlnaManager, ISubtitleEncoder subtitleEncoder, IDeviceManager deviceManager, IMediaSourceManager mediaSourceManager, IZipClient zipClient, IJsonSerializer jsonSerializer, IAuthorizationContext authorizationContext) : base(serverConfig, userManager, libraryManager, isoManager, mediaEncoder, fileSystem, dlnaManager, subtitleEncoder, deviceManager, mediaSourceManager, zipClient, jsonSerializer, authorizationContext)
{
}
diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs
index ed8449b83..4e4e8858e 100644
--- a/MediaBrowser.Api/Playback/MediaInfoService.cs
+++ b/MediaBrowser.Api/Playback/MediaInfoService.cs
@@ -127,7 +127,7 @@ namespace MediaBrowser.Api.Playback
SetDeviceSpecificData(item, result.MediaSource, profile, authInfo, request.MaxStreamingBitrate,
request.StartTimeTicks ?? 0, result.MediaSource.Id, request.AudioStreamIndex,
- request.SubtitleStreamIndex, request.MaxAudioChannels, request.PlaySessionId, request.UserId, true, true, true);
+ request.SubtitleStreamIndex, request.MaxAudioChannels, request.PlaySessionId, request.UserId, true, true, true, true);
}
else
{
@@ -169,7 +169,7 @@ namespace MediaBrowser.Api.Playback
{
var mediaSourceId = request.MediaSourceId;
- SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate ?? profile.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex, request.MaxAudioChannels, request.UserId, request.EnableDirectPlay, request.EnableDirectStream, request.EnableTranscoding);
+ SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate ?? profile.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex, request.MaxAudioChannels, request.UserId, request.EnableDirectPlay, request.ForceDirectPlayRemoteMediaSource, request.EnableDirectStream, request.EnableTranscoding);
}
return info;
@@ -253,6 +253,7 @@ namespace MediaBrowser.Api.Playback
int? maxAudioChannels,
string userId,
bool enableDirectPlay,
+ bool forceDirectPlayRemoteMediaSource,
bool enableDirectStream,
bool enableTranscoding)
{
@@ -260,7 +261,7 @@ namespace MediaBrowser.Api.Playback
foreach (var mediaSource in result.MediaSources)
{
- SetDeviceSpecificData(item, mediaSource, profile, auth, maxBitrate, startTimeTicks, mediaSourceId, audioStreamIndex, subtitleStreamIndex, maxAudioChannels, result.PlaySessionId, userId, enableDirectPlay, enableDirectStream, enableTranscoding);
+ SetDeviceSpecificData(item, mediaSource, profile, auth, maxBitrate, startTimeTicks, mediaSourceId, audioStreamIndex, subtitleStreamIndex, maxAudioChannels, result.PlaySessionId, userId, enableDirectPlay, forceDirectPlayRemoteMediaSource, enableDirectStream, enableTranscoding);
}
SortMediaSources(result, maxBitrate);
@@ -279,6 +280,7 @@ namespace MediaBrowser.Api.Playback
string playSessionId,
string userId,
bool enableDirectPlay,
+ bool forceDirectPlayRemoteMediaSource,
bool enableDirectStream,
bool enableTranscoding)
{
@@ -318,43 +320,49 @@ namespace MediaBrowser.Api.Playback
if (mediaSource.SupportsDirectPlay)
{
- var supportsDirectStream = mediaSource.SupportsDirectStream;
+ if (mediaSource.IsRemote && forceDirectPlayRemoteMediaSource)
+ {
+ }
+ else
+ {
+ var supportsDirectStream = mediaSource.SupportsDirectStream;
- // Dummy this up to fool StreamBuilder
- mediaSource.SupportsDirectStream = true;
- options.MaxBitrate = maxBitrate;
+ // Dummy this up to fool StreamBuilder
+ mediaSource.SupportsDirectStream = true;
+ options.MaxBitrate = maxBitrate;
- if (item is Audio)
- {
- if (!user.Policy.EnableAudioPlaybackTranscoding)
+ if (item is Audio)
{
- options.ForceDirectPlay = true;
+ if (!user.Policy.EnableAudioPlaybackTranscoding)
+ {
+ options.ForceDirectPlay = true;
+ }
}
- }
- else if (item is Video)
- {
- if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing)
+ else if (item is Video)
{
- options.ForceDirectPlay = true;
+ if (!user.Policy.EnableAudioPlaybackTranscoding && !user.Policy.EnableVideoPlaybackTranscoding && !user.Policy.EnablePlaybackRemuxing)
+ {
+ options.ForceDirectPlay = true;
+ }
}
- }
- // The MediaSource supports direct stream, now test to see if the client supports it
- var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
- streamBuilder.BuildAudioItem(options) :
- streamBuilder.BuildVideoItem(options);
+ // The MediaSource supports direct stream, now test to see if the client supports it
+ var streamInfo = string.Equals(item.MediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase) ?
+ streamBuilder.BuildAudioItem(options) :
+ streamBuilder.BuildVideoItem(options);
- if (streamInfo == null || !streamInfo.IsDirectStream)
- {
- mediaSource.SupportsDirectPlay = false;
- }
+ if (streamInfo == null || !streamInfo.IsDirectStream)
+ {
+ mediaSource.SupportsDirectPlay = false;
+ }
- // Set this back to what it was
- mediaSource.SupportsDirectStream = supportsDirectStream;
+ // Set this back to what it was
+ mediaSource.SupportsDirectStream = supportsDirectStream;
- if (streamInfo != null)
- {
- SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
+ if (streamInfo != null)
+ {
+ SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token);
+ }
}
}
diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs
index 56d48baea..a64523e40 100644
--- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs
@@ -31,7 +31,8 @@ namespace MediaBrowser.Api.Playback.Progressive
/// <summary>
/// Class AudioService
/// </summary>
- [Authenticated]
+ // TODO: In order to autheneticate this in the future, Dlna playback will require updating
+ //[Authenticated]
public class AudioService : BaseProgressiveStreamingService
{
/// <summary>
diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs
index f223c99ef..d1bf9c120 100644
--- a/MediaBrowser.Api/Playback/StreamRequest.cs
+++ b/MediaBrowser.Api/Playback/StreamRequest.cs
@@ -41,6 +41,7 @@ namespace MediaBrowser.Api.Playback
public string PlaySessionId { get; set; }
public string LiveStreamId { get; set; }
public string Tag { get; set; }
+ public string SegmentContainer { get; set; }
}
public class VideoStreamRequest : StreamRequest
diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs
index e010f122c..b0f52dd85 100644
--- a/MediaBrowser.Api/StartupWizardService.cs
+++ b/MediaBrowser.Api/StartupWizardService.cs
@@ -1,12 +1,9 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Controller;
+using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Connect;
using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Configuration;
-using MediaBrowser.Model.LiveTv;
using System;
using System.Linq;
using System.Threading.Tasks;
@@ -52,16 +49,14 @@ namespace MediaBrowser.Api
private readonly IServerApplicationHost _appHost;
private readonly IUserManager _userManager;
private readonly IConnectManager _connectManager;
- private readonly ILiveTvManager _liveTvManager;
private readonly IMediaEncoder _mediaEncoder;
- public StartupWizardService(IServerConfigurationManager config, IServerApplicationHost appHost, IUserManager userManager, IConnectManager connectManager, ILiveTvManager liveTvManager, IMediaEncoder mediaEncoder)
+ public StartupWizardService(IServerConfigurationManager config, IServerApplicationHost appHost, IUserManager userManager, IConnectManager connectManager, IMediaEncoder mediaEncoder)
{
_config = config;
_appHost = appHost;
_userManager = userManager;
_connectManager = connectManager;
- _liveTvManager = liveTvManager;
_mediaEncoder = mediaEncoder;
}
@@ -92,20 +87,6 @@ namespace MediaBrowser.Api
PreferredMetadataLanguage = _config.Configuration.PreferredMetadataLanguage
};
- var tvConfig = GetLiveTVConfiguration();
-
- if (tvConfig.TunerHosts.Count > 0)
- {
- result.LiveTvTunerPath = tvConfig.TunerHosts[0].Url;
- result.LiveTvTunerType = tvConfig.TunerHosts[0].Type;
- }
-
- if (tvConfig.ListingProviders.Count > 0)
- {
- result.LiveTvGuideProviderId = tvConfig.ListingProviders[0].Id;
- result.LiveTvGuideProviderType = tvConfig.ListingProviders[0].Type;
- }
-
return result;
}
@@ -120,6 +101,7 @@ namespace MediaBrowser.Api
config.EnableSeriesPresentationUniqueKey = true;
config.EnableLocalizedGuids = true;
config.EnableSimpleArtistDetection = true;
+ config.EnableNormalizedItemByNameIds = true;
}
public void Post(UpdateStartupConfiguration request)
@@ -128,9 +110,6 @@ namespace MediaBrowser.Api
_config.Configuration.MetadataCountryCode = request.MetadataCountryCode;
_config.Configuration.PreferredMetadataLanguage = request.PreferredMetadataLanguage;
_config.SaveConfiguration();
-
- var task = UpdateTuners(request);
- Task.WaitAll(task);
}
public object Get(GetStartupUser request)
@@ -165,51 +144,6 @@ namespace MediaBrowser.Api
return result;
}
-
- private async Task UpdateTuners(UpdateStartupConfiguration request)
- {
- var config = GetLiveTVConfiguration();
- var save = false;
-
- if (string.IsNullOrWhiteSpace(request.LiveTvTunerPath) ||
- string.IsNullOrWhiteSpace(request.LiveTvTunerType))
- {
- if (config.TunerHosts.Count > 0)
- {
- config.TunerHosts.Clear();
- save = true;
- }
- }
- else
- {
- if (!config.TunerHosts.Any(i => string.Equals(i.Type, request.LiveTvTunerType, StringComparison.OrdinalIgnoreCase) && string.Equals(i.Url, request.LiveTvTunerPath, StringComparison.OrdinalIgnoreCase)))
- {
- // Add tuner
- await _liveTvManager.SaveTunerHost(new TunerHostInfo
- {
- IsEnabled = true,
- Type = request.LiveTvTunerType,
- Url = request.LiveTvTunerPath
-
- }).ConfigureAwait(false);
- }
- }
-
- if (save)
- {
- SaveLiveTVConfiguration(config);
- }
- }
-
- private void SaveLiveTVConfiguration(LiveTvOptions config)
- {
- _config.SaveConfiguration("livetv", config);
- }
-
- private LiveTvOptions GetLiveTVConfiguration()
- {
- return _config.GetConfiguration<LiveTvOptions>("livetv");
- }
}
public class StartupConfiguration
@@ -217,10 +151,6 @@ namespace MediaBrowser.Api
public string UICulture { get; set; }
public string MetadataCountryCode { get; set; }
public string PreferredMetadataLanguage { get; set; }
- public string LiveTvTunerType { get; set; }
- public string LiveTvTunerPath { get; set; }
- public string LiveTvGuideProviderId { get; set; }
- public string LiveTvGuideProviderType { get; set; }
}
public class StartupInfo
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index 20b2529c0..8d83f8a35 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -289,7 +289,12 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
- public static string GetPath(string name, bool normalizeName = true)
+ public static string GetPath(string name)
+ {
+ return GetPath(name, true);
+ }
+
+ public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validName = normalizeName ?
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
index 74679b474..e26e0dfce 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
@@ -118,7 +118,12 @@ namespace MediaBrowser.Controller.Entities.Audio
return LibraryManager.GetItemList(query);
}
- public static string GetPath(string name, bool normalizeName = true)
+ public static string GetPath(string name)
+ {
+ return GetPath(name, true);
+ }
+
+ public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validName = normalizeName ?
diff --git a/MediaBrowser.Controller/Entities/GameGenre.cs b/MediaBrowser.Controller/Entities/GameGenre.cs
index 22a8675c5..4187167b9 100644
--- a/MediaBrowser.Controller/Entities/GameGenre.cs
+++ b/MediaBrowser.Controller/Entities/GameGenre.cs
@@ -96,7 +96,12 @@ namespace MediaBrowser.Controller.Entities
}
}
- public static string GetPath(string name, bool normalizeName = true)
+ public static string GetPath(string name)
+ {
+ return GetPath(name, true);
+ }
+
+ public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validName = normalizeName ?
diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs
index 1b746ae51..9769efdd0 100644
--- a/MediaBrowser.Controller/Entities/Genre.cs
+++ b/MediaBrowser.Controller/Entities/Genre.cs
@@ -108,7 +108,12 @@ namespace MediaBrowser.Controller.Entities
}
}
- public static string GetPath(string name, bool normalizeName = true)
+ public static string GetPath(string name)
+ {
+ return GetPath(name, true);
+ }
+
+ public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validName = normalizeName ?
diff --git a/MediaBrowser.Controller/Entities/IHasImages.cs b/MediaBrowser.Controller/Entities/IHasImages.cs
index 4c033dc00..2104bee09 100644
--- a/MediaBrowser.Controller/Entities/IHasImages.cs
+++ b/MediaBrowser.Controller/Entities/IHasImages.cs
@@ -206,6 +206,8 @@ namespace MediaBrowser.Controller.Entities
void SetImage(ItemImageInfo image, int index);
double? GetDefaultPrimaryImageAspectRatio();
+
+ int? ProductionYear { get; set; }
}
public static class HasImagesExtensions
diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs
index ee1aea938..b68681d36 100644
--- a/MediaBrowser.Controller/Entities/Person.cs
+++ b/MediaBrowser.Controller/Entities/Person.cs
@@ -133,7 +133,12 @@ namespace MediaBrowser.Controller.Entities
}
}
- public static string GetPath(string name, bool normalizeName = true)
+ public static string GetPath(string name)
+ {
+ return GetPath(name, true);
+ }
+
+ public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validFilename = normalizeName ?
diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs
index b8ad691a9..2e5e6ce43 100644
--- a/MediaBrowser.Controller/Entities/Studio.cs
+++ b/MediaBrowser.Controller/Entities/Studio.cs
@@ -114,7 +114,12 @@ namespace MediaBrowser.Controller.Entities
}
}
- public static string GetPath(string name, bool normalizeName = true)
+ public static string GetPath(string name)
+ {
+ return GetPath(name, true);
+ }
+
+ public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validName = normalizeName ?
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index 78f907d61..890626419 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -614,7 +614,8 @@ namespace MediaBrowser.Controller.Entities
Timestamp = i.Timestamp,
Type = type,
PlayableStreamFileNames = i.PlayableStreamFileNames.ToList(),
- SupportsDirectStream = i.VideoType == VideoType.VideoFile
+ SupportsDirectStream = i.VideoType == VideoType.VideoFile,
+ IsRemote = i.IsShortcut
};
if (info.Protocol == MediaProtocol.File)
diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs
index 75fb69435..b352950a0 100644
--- a/MediaBrowser.Controller/Entities/Year.cs
+++ b/MediaBrowser.Controller/Entities/Year.cs
@@ -122,7 +122,12 @@ namespace MediaBrowser.Controller.Entities
}
}
- public static string GetPath(string name, bool normalizeName = true)
+ public static string GetPath(string name)
+ {
+ return GetPath(name, true);
+ }
+
+ public static string GetPath(string name, bool normalizeName)
{
// Trim the period at the end because windows will have a hard time with that
var validName = normalizeName ?
diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
index a908d2d3f..5242c5b1f 100644
--- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
+++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
@@ -376,19 +376,14 @@ namespace MediaBrowser.Controller.LiveTv
/// <returns>Task.</returns>
Task OnRecordingFileDeleted(BaseItem recording);
- /// <summary>
- /// Gets the sat ini mappings.
- /// </summary>
- /// <returns>List&lt;NameValuePair&gt;.</returns>
- List<NameValuePair> GetSatIniMappings();
-
- Task<List<ChannelInfo>> GetSatChannelScanResult(TunerHostInfo info, CancellationToken cancellationToken);
-
Task<List<ChannelInfo>> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken);
Task<List<ChannelInfo>> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken);
List<IListingsProvider> ListingProviders { get; }
+ List<NameIdPair> GetTunerHostTypes();
+ Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken);
+
event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCancelled;
event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCreated;
diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs
index 5615649c2..fc344298b 100644
--- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs
+++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs
@@ -44,6 +44,8 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task&lt;List&lt;MediaSourceInfo&gt;&gt;.</returns>
Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken);
+
+ Task<List<TunerHostInfo>> DiscoverDevices(int discoveryDurationMs, CancellationToken cancellationToken);
}
public interface IConfigurableTunerHost
{
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 8afee9f35..c73170fcf 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -179,6 +179,17 @@ namespace MediaBrowser.Controller.MediaEncoding
{
return null;
}
+ // Seeing reported failures here, not sure yet if this is related to specfying input format
+ if (string.Equals(container, "m4v", StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
+ // obviously don't do this for strm files
+ if (string.Equals(container, "strm", StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
return container;
}
@@ -464,21 +475,6 @@ namespace MediaBrowser.Controller.MediaEncoding
}
/// <summary>
- /// Gets the probe size argument.
- /// </summary>
- /// <param name="state">The state.</param>
- /// <returns>System.String.</returns>
- public string GetProbeSizeArgument(EncodingJobInfo state)
- {
- if (state.PlayableStreamFileNames.Count > 0)
- {
- return _mediaEncoder.GetProbeSizeAndAnalyzeDurationArgument(state.PlayableStreamFileNames.ToArray(), state.InputProtocol);
- }
-
- return _mediaEncoder.GetProbeSizeAndAnalyzeDurationArgument(new[] { state.MediaPath }, state.InputProtocol);
- }
-
- /// <summary>
/// Gets the text subtitle param.
/// </summary>
/// <param name="state">The state.</param>
@@ -741,12 +737,18 @@ namespace MediaBrowser.Controller.MediaEncoding
if (videoStream.IsInterlaced)
{
- return false;
+ if (request.DeInterlace)
+ {
+ return false;
+ }
}
if (videoStream.IsAnamorphic ?? false)
{
- return false;
+ if (request.RequireNonAnamorphic)
+ {
+ return false;
+ }
}
// Can't stream copy if we're burning in subtitles
@@ -1204,11 +1206,6 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
- if (type == MediaStreamType.Video)
- {
- streams = streams.Where(i => !string.Equals(i.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase)).ToList();
- }
-
if (returnFirstIfNoIndex && type == MediaStreamType.Audio)
{
return streams.FirstOrDefault(i => i.Channels.HasValue && i.Channels.Value > 0) ??
@@ -1456,12 +1453,43 @@ namespace MediaBrowser.Controller.MediaEncoding
}
}
+ public static string GetProbeSizeArgument(int numInputFiles)
+ {
+ return numInputFiles > 1 ? "-probesize 1G" : "";
+ }
+
+ public static string GetAnalyzeDurationArgument(int numInputFiles)
+ {
+ return numInputFiles > 1 ? "-analyzeduration 200M" : "";
+ }
+
public string GetInputModifier(EncodingJobInfo state, EncodingOptions encodingOptions)
{
var inputModifier = string.Empty;
- var probeSize = GetProbeSizeArgument(state);
- inputModifier += " " + probeSize;
+ var numInputFiles = state.PlayableStreamFileNames.Count > 0 ? state.PlayableStreamFileNames.Count : 1;
+ var probeSizeArgument = GetProbeSizeArgument(numInputFiles);
+
+ string analyzeDurationArgument;
+ if (state.MediaSource.AnalyzeDurationMs.HasValue)
+ {
+ analyzeDurationArgument = "-analyzeduration " + (state.MediaSource.AnalyzeDurationMs.Value * 1000).ToString(CultureInfo.InvariantCulture);
+ }
+ else
+ {
+ analyzeDurationArgument = GetAnalyzeDurationArgument(numInputFiles);
+ }
+
+ if (!string.IsNullOrWhiteSpace(probeSizeArgument))
+ {
+ inputModifier += " " + probeSizeArgument;
+ }
+
+ if (!string.IsNullOrWhiteSpace(analyzeDurationArgument))
+ {
+ inputModifier += " " + analyzeDurationArgument;
+ }
+
inputModifier = inputModifier.Trim();
var userAgentParam = GetUserAgentParam(state);
@@ -1658,6 +1686,11 @@ namespace MediaBrowser.Controller.MediaEncoding
case "h264":
if (_mediaEncoder.SupportsDecoder("h264_qsv"))
{
+ // qsv decoder does not support 10-bit input
+ if ((state.VideoStream.BitDepth ?? 8) > 8)
+ {
+ return null;
+ }
return "-c:v h264_qsv ";
}
break;
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
index 4bb180d4b..6baf87a04 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
@@ -46,7 +46,7 @@ namespace MediaBrowser.Controller.MediaEncoding
AudioBitRate = info.AudioBitrate;
AudioSampleRate = info.TargetAudioSampleRate;
DeviceProfile = deviceProfile;
- VideoCodec = info.VideoCodec;
+ VideoCodec = info.TargetVideoCodec;
VideoBitRate = info.VideoBitrate;
AudioStreamIndex = info.AudioStreamIndex;
MaxRefFrames = info.MaxRefFrames;
@@ -185,6 +185,8 @@ namespace MediaBrowser.Controller.MediaEncoding
[ApiMember(Name = "MaxVideoBitDepth", Description = "Optional.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
public int? MaxVideoBitDepth { get; set; }
public bool RequireAvc { get; set; }
+ public bool DeInterlace { get; set; }
+ public bool RequireNonAnamorphic { get; set; }
public int? TranscodingMaxAudioChannels { get; set; }
public int? CpuCoreLimit { get; set; }
public string OutputContainer { get; set; }
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index 45aaa8e8e..78ed1dc59 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -79,14 +79,6 @@ namespace MediaBrowser.Controller.MediaEncoding
Task<MediaInfo> GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken);
/// <summary>
- /// Gets the probe size argument.
- /// </summary>
- /// <param name="inputFiles">The input files.</param>
- /// <param name="protocol">The protocol.</param>
- /// <returns>System.String.</returns>
- string GetProbeSizeAndAnalyzeDurationArgument(string[] inputFiles, MediaProtocol protocol);
-
- /// <summary>
/// Gets the input argument.
/// </summary>
/// <param name="inputFiles">The input files.</param>
diff --git a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs
index 9ff7567d4..0785ee29f 100644
--- a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs
+++ b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs
@@ -15,7 +15,7 @@ namespace MediaBrowser.Controller.MediaEncoding
public IIsoMount MountedIso { get; set; }
public VideoType VideoType { get; set; }
public List<string> PlayableStreamFileNames { get; set; }
- public int AnalyzeDurationSections { get; set; }
+ public int AnalyzeDurationMs { get; set; }
public MediaInfoRequest()
{
diff --git a/MediaBrowser.Controller/Net/StaticResultOptions.cs b/MediaBrowser.Controller/Net/StaticResultOptions.cs
index 272fa8b3b..62c13fbb5 100644
--- a/MediaBrowser.Controller/Net/StaticResultOptions.cs
+++ b/MediaBrowser.Controller/Net/StaticResultOptions.cs
@@ -23,21 +23,18 @@ namespace MediaBrowser.Controller.Net
public Action OnComplete { get; set; }
public Action OnError { get; set; }
+ public string Path { get; set; }
+
+ public FileShareMode FileShare { get; set; }
+
public StaticResultOptions()
{
ResponseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ FileShare = FileShareMode.Read;
}
}
public class StaticFileResultOptions : StaticResultOptions
{
- public string Path { get; set; }
-
- public FileShareMode FileShare { get; set; }
-
- public StaticFileResultOptions()
- {
- FileShare = FileShareMode.Read;
- }
}
}
diff --git a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
index 2f7059fec..c986e2005 100644
--- a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
@@ -177,7 +177,7 @@ namespace MediaBrowser.LocalMetadata.Images
"default"
};
- if (item is MusicAlbum || item is MusicArtist || item is PhotoAlbum)
+ if (item is MusicAlbum || item is MusicArtist || item is PhotoAlbum || item is Person)
{
// these prefer folder
names.Insert(0, "poster");
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index b37e783b8..9c1189f6c 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
@@ -90,6 +90,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
var required = new[]
{
"h264_qsv",
+ "hevc_qsv",
"mpeg2_qsv",
"vc1_qsv"
};
@@ -134,9 +135,13 @@ namespace MediaBrowser.MediaEncoding.Encoder
"libvorbis",
"srt",
"h264_nvenc",
+ "hevc_nvenc",
"h264_qsv",
+ "hevc_qsv",
"h264_omx",
+ "hevc_omx",
"h264_vaapi",
+ "hevc_vaapi",
"ac3"
};
@@ -205,4 +210,4 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
}
}
-} \ No newline at end of file
+}
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs
index e547f2fae..498df214f 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncodingUtils.cs
@@ -61,15 +61,5 @@ namespace MediaBrowser.MediaEncoding.Encoder
// Quotes are valid path characters in linux and they need to be escaped here with a leading \
return path.Replace("\"", "\\\"");
}
-
- public static string GetProbeSizeArgument(int numInputFiles)
- {
- return numInputFiles > 1 ? "-probesize 1G" : "";
- }
-
- public static string GetAnalyzeDurationArgument(int numInputFiles)
- {
- return numInputFiles > 1 ? "-analyzeduration 200M" : "";
- }
}
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index e7737b6a6..580f5c615 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -523,17 +523,17 @@ namespace MediaBrowser.MediaEncoding.Encoder
var inputFiles = MediaEncoderHelpers.GetInputArgument(FileSystem, request.InputPath, request.Protocol, request.MountedIso, request.PlayableStreamFileNames);
- var probeSize = EncodingUtils.GetProbeSizeArgument(inputFiles.Length);
+ var probeSize = EncodingHelper.GetProbeSizeArgument(inputFiles.Length);
string analyzeDuration;
- if (request.AnalyzeDurationSections > 0)
+ if (request.AnalyzeDurationMs > 0)
{
analyzeDuration = "-analyzeduration " +
- (request.AnalyzeDurationSections * 1000000).ToString(CultureInfo.InvariantCulture);
+ (request.AnalyzeDurationMs * 1000).ToString(CultureInfo.InvariantCulture);
}
else
{
- analyzeDuration = EncodingUtils.GetAnalyzeDurationArgument(inputFiles.Length);
+ analyzeDuration = EncodingHelper.GetAnalyzeDurationArgument(inputFiles.Length);
}
probeSize = probeSize + " " + analyzeDuration;
@@ -558,31 +558,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
/// <summary>
- /// Gets the probe size argument.
- /// </summary>
- /// <param name="inputFiles">The input files.</param>
- /// <param name="protocol">The protocol.</param>
- /// <returns>System.String.</returns>
- public string GetProbeSizeAndAnalyzeDurationArgument(string[] inputFiles, MediaProtocol protocol)
- {
- var results = new List<string>();
-
- var probeSize = EncodingUtils.GetProbeSizeArgument(inputFiles.Length);
- var analyzeDuration = EncodingUtils.GetAnalyzeDurationArgument(inputFiles.Length);
-
- if (!string.IsNullOrWhiteSpace(probeSize))
- {
- results.Add(probeSize);
- }
-
- if (!string.IsNullOrWhiteSpace(analyzeDuration))
- {
- results.Add(analyzeDuration);
- }
- return string.Join(" ", results.ToArray());
- }
-
- /// <summary>
/// Gets the media info internal.
/// </summary>
/// <returns>Task{MediaInfoResult}.</returns>
@@ -984,11 +959,17 @@ namespace MediaBrowser.MediaEncoding.Encoder
var args = useIFrame ? string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}{4}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, thumbnail) :
string.Format("-i {0}{3} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg);
- var probeSize = GetProbeSizeAndAnalyzeDurationArgument(new[] { inputPath }, protocol);
+ var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1);
+ var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1);
+
+ if (!string.IsNullOrWhiteSpace(probeSizeArgument))
+ {
+ args = probeSizeArgument + " " + args;
+ }
- if (!string.IsNullOrEmpty(probeSize))
+ if (!string.IsNullOrWhiteSpace(analyzeDurationArgument))
{
- args = probeSize + " " + args;
+ args = analyzeDurationArgument + " " + args;
}
if (offset.HasValue)
@@ -1092,11 +1073,17 @@ namespace MediaBrowser.MediaEncoding.Encoder
var args = string.Format("-i {0} -threads 0 -v quiet -vf \"{2}\" -f image2 \"{1}\"", inputArgument, outputPath, vf);
- var probeSize = GetProbeSizeAndAnalyzeDurationArgument(new[] { inputArgument }, protocol);
+ var probeSizeArgument = EncodingHelper.GetProbeSizeArgument(1);
+ var analyzeDurationArgument = EncodingHelper.GetAnalyzeDurationArgument(1);
+
+ if (!string.IsNullOrWhiteSpace(probeSizeArgument))
+ {
+ args = probeSizeArgument + " " + args;
+ }
- if (!string.IsNullOrEmpty(probeSize))
+ if (!string.IsNullOrWhiteSpace(analyzeDurationArgument))
{
- args = probeSize + " " + args;
+ args = analyzeDurationArgument + " " + args;
}
var process = _processFactory.Create(new ProcessOptions
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index 256c38597..7927ddb6a 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -558,13 +558,36 @@ namespace MediaBrowser.MediaEncoding.Probing
? MediaStreamType.EmbeddedImage
: MediaStreamType.Video;
+ stream.AverageFrameRate = GetFrameRate(streamInfo.avg_frame_rate);
+ stream.RealFrameRate = GetFrameRate(streamInfo.r_frame_rate);
+
+ if (isAudio || string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase) ||
+ string.Equals(stream.Codec, "png", StringComparison.OrdinalIgnoreCase))
+ {
+ stream.Type = MediaStreamType.EmbeddedImage;
+ }
+ else if (string.Equals(stream.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase))
+ {
+ // How to differentiate between video and embedded image?
+ // The only difference I've seen thus far is presence of codec tag, also embedded images have high (unusual) framerates
+ if (!string.IsNullOrWhiteSpace(stream.CodecTag))
+ {
+ stream.Type = MediaStreamType.Video;
+ }
+ else
+ {
+ stream.Type = MediaStreamType.EmbeddedImage;
+ }
+ }
+ else
+ {
+ stream.Type = MediaStreamType.Video;
+ }
+
stream.Width = streamInfo.width;
stream.Height = streamInfo.height;
stream.AspectRatio = GetAspectRatio(streamInfo);
- stream.AverageFrameRate = GetFrameRate(streamInfo.avg_frame_rate);
- stream.RealFrameRate = GetFrameRate(streamInfo.r_frame_rate);
-
if (streamInfo.bits_per_sample > 0)
{
stream.BitDepth = streamInfo.bits_per_sample;
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index 4a1bf5305..77b976206 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -734,16 +734,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
}
- var charsetFromLanguage = string.IsNullOrWhiteSpace(language)
- ? null
- : GetSubtitleFileCharacterSetFromLanguage(language);
-
- // This assumption should only be made for external subtitles
- if (!string.IsNullOrWhiteSpace(charsetFromLanguage) && !string.Equals(charsetFromLanguage, "windows-1252", StringComparison.OrdinalIgnoreCase))
- {
- return charsetFromLanguage;
- }
-
var charset = await DetectCharset(path, language, protocol, cancellationToken).ConfigureAwait(false);
if (!string.IsNullOrWhiteSpace(charset))
@@ -756,7 +746,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
return charset;
}
- return charsetFromLanguage;
+ if (!string.IsNullOrWhiteSpace(language))
+ {
+ return GetSubtitleFileCharacterSetFromLanguage(language);
+ }
+
+ return null;
}
public string GetSubtitleFileCharacterSetFromLanguage(string language)
@@ -854,4 +849,4 @@ namespace MediaBrowser.MediaEncoding.Subtitles
throw new ArgumentOutOfRangeException("protocol");
}
}
-}
+} \ No newline at end of file
diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs
index 7af0acc59..9cd656fa7 100644
--- a/MediaBrowser.Model/Configuration/LibraryOptions.cs
+++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs
@@ -16,6 +16,8 @@
public bool EnableAutomaticSeriesGrouping { get; set; }
public bool EnableEmbeddedTitles { get; set; }
+ public int AutomaticRefreshIntervalDays { get; set; }
+
/// <summary>
/// Gets or sets the preferred metadata language.
/// </summary>
diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
index c2b1e3c89..0562d0ac5 100644
--- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs
+++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs
@@ -48,6 +48,7 @@ namespace MediaBrowser.Model.Configuration
public bool EnableHttps { get; set; }
public bool EnableSeriesPresentationUniqueKey { get; set; }
public bool EnableLocalizedGuids { get; set; }
+ public bool EnableNormalizedItemByNameIds { get; set; }
/// <summary>
/// Gets or sets the value pointing to the file system where the ssl certiifcate is located..
diff --git a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs
index 7e2002f17..dbd574f86 100644
--- a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs
+++ b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs
@@ -21,6 +21,7 @@
NumVideoStreams = 17,
IsSecondaryAudio = 18,
VideoCodecTag = 19,
- IsAvc = 20
+ IsAvc = 20,
+ IsInterlaced = 21
}
} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index 480eb23a5..80d3ea3fb 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -231,6 +231,7 @@ namespace MediaBrowser.Model.Dlna
{
playlistItem.AudioCodecs = transcodingProfile.AudioCodec.Split(',');
}
+
playlistItem.SubProtocol = transcodingProfile.Protocol;
List<CodecProfile> audioCodecProfiles = new List<CodecProfile>();
@@ -479,7 +480,7 @@ namespace MediaBrowser.Model.Dlna
playlistItem.AudioCodecs = transcodingProfile.AudioCodec.Split(',');
- playlistItem.VideoCodec = transcodingProfile.VideoCodec;
+ playlistItem.VideoCodecs = transcodingProfile.VideoCodec.Split(',');
playlistItem.CopyTimestamps = transcodingProfile.CopyTimestamps;
playlistItem.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest;
@@ -1137,6 +1138,37 @@ namespace MediaBrowser.Model.Dlna
break;
}
case ProfileConditionValue.IsAnamorphic:
+ {
+ bool isAnamorphic;
+ if (bool.TryParse(value, out isAnamorphic))
+ {
+ if (isAnamorphic && condition.Condition == ProfileConditionType.Equals)
+ {
+ item.RequireNonAnamorphic = true;
+ }
+ else if (!isAnamorphic && condition.Condition == ProfileConditionType.NotEquals)
+ {
+ item.RequireNonAnamorphic = true;
+ }
+ }
+ break;
+ }
+ case ProfileConditionValue.IsInterlaced:
+ {
+ bool isInterlaced;
+ if (bool.TryParse(value, out isInterlaced))
+ {
+ if (isInterlaced && condition.Condition == ProfileConditionType.Equals)
+ {
+ item.DeInterlace = true;
+ }
+ else if (!isInterlaced && condition.Condition == ProfileConditionType.NotEquals)
+ {
+ item.DeInterlace = true;
+ }
+ }
+ break;
+ }
case ProfileConditionValue.AudioProfile:
case ProfileConditionValue.Has64BitOffsets:
case ProfileConditionValue.PacketLength:
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs
index a85e6085b..7e42e7fae 100644
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/StreamInfo.cs
@@ -18,6 +18,7 @@ namespace MediaBrowser.Model.Dlna
public StreamInfo()
{
AudioCodecs = new string[] { };
+ VideoCodecs = new string[] { };
SubtitleCodecs = new string[] { };
}
@@ -34,13 +35,15 @@ namespace MediaBrowser.Model.Dlna
public long StartPositionTicks { get; set; }
- public string VideoCodec { get; set; }
public string VideoProfile { get; set; }
public bool RequireAvc { get; set; }
+ public bool DeInterlace { get; set; }
+ public bool RequireNonAnamorphic { get; set; }
public bool CopyTimestamps { get; set; }
public bool EnableSubtitlesInManifest { get; set; }
public string[] AudioCodecs { get; set; }
+ public string[] VideoCodecs { get; set; }
public int? AudioStreamIndex { get; set; }
@@ -204,11 +207,15 @@ namespace MediaBrowser.Model.Dlna
string.Empty :
string.Join(",", item.AudioCodecs);
+ string videoCodecs = item.VideoCodecs.Length == 0 ?
+ string.Empty :
+ string.Join(",", item.VideoCodecs);
+
list.Add(new NameValuePair("DeviceProfileId", item.DeviceProfileId ?? string.Empty));
list.Add(new NameValuePair("DeviceId", item.DeviceId ?? string.Empty));
list.Add(new NameValuePair("MediaSourceId", item.MediaSourceId ?? string.Empty));
list.Add(new NameValuePair("Static", item.IsDirectStream.ToString().ToLower()));
- list.Add(new NameValuePair("VideoCodec", item.VideoCodec ?? string.Empty));
+ list.Add(new NameValuePair("VideoCodec", videoCodecs));
list.Add(new NameValuePair("AudioCodec", audioCodecs));
list.Add(new NameValuePair("AudioStreamIndex", item.AudioStreamIndex.HasValue ? StringHelper.ToStringCultureInvariant(item.AudioStreamIndex.Value) : string.Empty));
list.Add(new NameValuePair("SubtitleStreamIndex", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External ? StringHelper.ToStringCultureInvariant(item.SubtitleStreamIndex.Value) : string.Empty));
@@ -232,7 +239,9 @@ namespace MediaBrowser.Model.Dlna
// }
//}
- if (StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls") && !forceStartPosition)
+ var isHls = StringHelper.EqualsIgnoreCase(item.SubProtocol, "hls");
+
+ if (isHls && !forceStartPosition)
{
list.Add(new NameValuePair("StartTimeTicks", string.Empty));
}
@@ -276,6 +285,14 @@ namespace MediaBrowser.Model.Dlna
list.Add(new NameValuePair("SubtitleCodec", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed ? subtitleCodecs : string.Empty));
+ list.Add(new NameValuePair("RequireNonAnamorphic", item.RequireNonAnamorphic.ToString().ToLower()));
+ list.Add(new NameValuePair("DeInterlace", item.DeInterlace.ToString().ToLower()));
+
+ if (!isDlna && isHls)
+ {
+ list.Add(new NameValuePair("SegmentContainer", item.Container ?? string.Empty));
+ }
+
return list;
}
@@ -609,9 +626,34 @@ namespace MediaBrowser.Model.Dlna
}
}
+ public string TargetVideoCodec
+ {
+ get
+ {
+ MediaStream stream = TargetVideoStream;
+
+ string inputCodec = stream == null ? null : stream.Codec;
+
+ if (IsDirectStream)
+ {
+ return inputCodec;
+ }
+
+ foreach (string codec in VideoCodecs)
+ {
+ if (StringHelper.EqualsIgnoreCase(codec, inputCodec))
+ {
+ return codec;
+ }
+ }
+
+ return VideoCodecs.Length == 0 ? null : VideoCodecs[0];
+ }
+ }
+
/// <summary>
- /// Predicts the audio channels that will be in the output stream
- /// </summary>
+ /// Predicts the audio channels that will be in the output stream
+ /// </summary>
public long? TargetSize
{
get
diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs
index d20911a7f..d416cfd96 100644
--- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs
+++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs
@@ -60,6 +60,8 @@ namespace MediaBrowser.Model.Dto
public string TranscodingSubProtocol { get; set; }
public string TranscodingContainer { get; set; }
+ public int? AnalyzeDurationMs { get; set; }
+
public MediaSourceInfo()
{
Formats = new List<string>();
diff --git a/MediaBrowser.Model/Dto/UserDto.cs b/MediaBrowser.Model/Dto/UserDto.cs
index f9e3f7718..37a554166 100644
--- a/MediaBrowser.Model/Dto/UserDto.cs
+++ b/MediaBrowser.Model/Dto/UserDto.cs
@@ -89,7 +89,9 @@ namespace MediaBrowser.Model.Dto
/// </summary>
/// <value><c>true</c> if this instance has configured easy password; otherwise, <c>false</c>.</value>
public bool HasConfiguredEasyPassword { get; set; }
-
+
+ public bool? EnableAutoLogin { get; set; }
+
/// <summary>
/// Gets or sets the last login date.
/// </summary>
diff --git a/MediaBrowser.Model/IO/IFileSystem.cs b/MediaBrowser.Model/IO/IFileSystem.cs
index f6d1bb351..f90119cf3 100644
--- a/MediaBrowser.Model/IO/IFileSystem.cs
+++ b/MediaBrowser.Model/IO/IFileSystem.cs
@@ -10,6 +10,8 @@ namespace MediaBrowser.Model.IO
/// </summary>
public interface IFileSystem
{
+ void AddShortcutHandler(IShortcutHandler handler);
+
/// <summary>
/// Determines whether the specified filename is shortcut.
/// </summary>
diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
index e2c6b0503..6a0fdede3 100644
--- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
+++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs
@@ -36,6 +36,7 @@ namespace MediaBrowser.Model.LiveTv
MediaLocationsCreated = new string[] { };
RecordingEncodingFormat = "mp4";
RecordingPostProcessorArguments = "\"{path}\"";
+ EnableRecordingEncoding = true;
}
}
@@ -45,14 +46,13 @@ namespace MediaBrowser.Model.LiveTv
public string Url { get; set; }
public string Type { get; set; }
public string DeviceId { get; set; }
+ public string FriendlyName { get; set; }
public bool ImportFavoritesOnly { get; set; }
public bool AllowHWTranscoding { get; set; }
- public bool IsEnabled { get; set; }
public bool EnableTvgId { get; set; }
public TunerHostInfo()
{
- IsEnabled = true;
AllowHWTranscoding = true;
}
}
diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
index 57a2254b0..16c9464ac 100644
--- a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
+++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs
@@ -27,9 +27,11 @@ namespace MediaBrowser.Model.MediaInfo
public bool EnableDirectPlay { get; set; }
public bool EnableDirectStream { get; set; }
public bool EnableTranscoding { get; set; }
+ public bool ForceDirectPlayRemoteMediaSource { get; set; }
public PlaybackInfoRequest()
{
+ ForceDirectPlayRemoteMediaSource = true;
EnableDirectPlay = true;
EnableDirectStream = true;
EnableTranscoding = true;
diff --git a/MediaBrowser.Model/Net/IAcceptSocket.cs b/MediaBrowser.Model/Net/IAcceptSocket.cs
index cac23b337..4262e2390 100644
--- a/MediaBrowser.Model/Net/IAcceptSocket.cs
+++ b/MediaBrowser.Model/Net/IAcceptSocket.cs
@@ -1,4 +1,6 @@
using System;
+using System.Threading;
+using System.Threading.Tasks;
namespace MediaBrowser.Model.Net
{
@@ -13,6 +15,7 @@ namespace MediaBrowser.Model.Net
void Bind(IpEndPointInfo endpoint);
void Connect(IpEndPointInfo endPoint);
void StartAccept(Action<IAcceptSocket> onAccept, Func<bool> isClosed);
+ Task SendFile(string path, byte[] preBuffer, byte[] postBuffer, CancellationToken cancellationToken);
}
public class SocketCreateException : Exception
diff --git a/MediaBrowser.Model/Net/ISocketFactory.cs b/MediaBrowser.Model/Net/ISocketFactory.cs
index 4b70f3362..e7dbf6cb1 100644
--- a/MediaBrowser.Model/Net/ISocketFactory.cs
+++ b/MediaBrowser.Model/Net/ISocketFactory.cs
@@ -14,6 +14,8 @@ namespace MediaBrowser.Model.Net
/// <returns>A <see cref="ISocket"/> implementation.</returns>
ISocket CreateUdpSocket(int localPort);
+ ISocket CreateUdpBroadcastSocket(int localPort);
+
ISocket CreateTcpSocket(IpAddressInfo remoteAddress, int remotePort);
/// <summary>
diff --git a/MediaBrowser.Model/Net/MimeTypes.cs b/MediaBrowser.Model/Net/MimeTypes.cs
index 902253e80..790f8a8f6 100644
--- a/MediaBrowser.Model/Net/MimeTypes.cs
+++ b/MediaBrowser.Model/Net/MimeTypes.cs
@@ -322,6 +322,9 @@ namespace MediaBrowser.Model.Net
throw new ArgumentNullException("mimeType");
}
+ // handle text/html; charset=UTF-8
+ mimeType = mimeType.Split(';')[0];
+
string result;
if (ExtensionLookup.TryGetValue(mimeType, out result))
{
diff --git a/MediaBrowser.Model/Querying/ItemFields.cs b/MediaBrowser.Model/Querying/ItemFields.cs
index 9698e374f..82e4acaec 100644
--- a/MediaBrowser.Model/Querying/ItemFields.cs
+++ b/MediaBrowser.Model/Querying/ItemFields.cs
@@ -151,6 +151,8 @@
/// </summary>
People,
+ PlayAccess,
+
/// <summary>
/// The production locations
/// </summary>
diff --git a/MediaBrowser.Model/Services/IAsyncStreamWriter.cs b/MediaBrowser.Model/Services/IAsyncStreamWriter.cs
index b10e12813..f61a94f6e 100644
--- a/MediaBrowser.Model/Services/IAsyncStreamWriter.cs
+++ b/MediaBrowser.Model/Services/IAsyncStreamWriter.cs
@@ -8,4 +8,9 @@ namespace MediaBrowser.Model.Services
{
Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken);
}
+
+ public interface IFileWriter
+ {
+ Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken);
+ }
}
diff --git a/MediaBrowser.Model/Services/IRequest.cs b/MediaBrowser.Model/Services/IRequest.cs
index e9a9f1c5b..115ba25ce 100644
--- a/MediaBrowser.Model/Services/IRequest.cs
+++ b/MediaBrowser.Model/Services/IRequest.cs
@@ -2,6 +2,9 @@
using System.Collections.Generic;
using System.IO;
using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.Model.Services
{
@@ -151,6 +154,7 @@ namespace MediaBrowser.Model.Services
//Add Metadata to Response
Dictionary<string, object> Items { get; }
- }
+ Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken);
+ }
}
diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs
index a65453a78..75c4b46b2 100644
--- a/MediaBrowser.Providers/Manager/ImageSaver.cs
+++ b/MediaBrowser.Providers/Manager/ImageSaver.cs
@@ -410,6 +410,7 @@ namespace MediaBrowser.Providers.Manager
var folderName = item is MusicAlbum ||
item is MusicArtist ||
item is PhotoAlbum ||
+ item is Person ||
(saveLocally && _config.Configuration.ImageSavingConvention == ImageSavingConvention.Legacy) ?
"folder" :
"poster";
diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs
index bdfe13c1d..59a2278a1 100644
--- a/MediaBrowser.Providers/Manager/MetadataService.cs
+++ b/MediaBrowser.Providers/Manager/MetadataService.cs
@@ -45,12 +45,21 @@ namespace MediaBrowser.Providers.Manager
var updateType = ItemUpdateType.None;
var requiresRefresh = false;
+ var libraryOptions = LibraryManager.GetLibraryOptions((BaseItem)item);
+
if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None)
{
// TODO: If this returns true, should we instead just change metadata refresh mode to Full?
requiresRefresh = item.RequiresRefresh();
}
+ if (!requiresRefresh &&
+ libraryOptions.AutomaticRefreshIntervalDays > 0 &&
+ (DateTime.UtcNow - item.DateLastRefreshed).TotalDays >= libraryOptions.AutomaticRefreshIntervalDays)
+ {
+ requiresRefresh = true;
+ }
+
var itemImageProvider = new ItemImageProvider(Logger, ProviderManager, ServerConfigurationManager, FileSystem);
var localImagesFailed = false;
@@ -116,8 +125,6 @@ namespace MediaBrowser.Providers.Manager
}
}
- LibraryOptions libraryOptions = null;
-
// Next run remote image providers, but only if local image providers didn't throw an exception
if (!localImagesFailed && refreshOptions.ImageRefreshMode != ImageRefreshMode.ValidationOnly)
{
@@ -125,11 +132,6 @@ namespace MediaBrowser.Providers.Manager
if (providers.Count > 0)
{
- if (libraryOptions == null)
- {
- libraryOptions = LibraryManager.GetLibraryOptions((BaseItem)item);
- }
-
var result = await itemImageProvider.RefreshImages(itemOfType, libraryOptions, providers, refreshOptions, config, cancellationToken).ConfigureAwait(false);
updateType = updateType | result.UpdateType;
@@ -177,11 +179,6 @@ namespace MediaBrowser.Providers.Manager
item.DateLastRefreshed = default(DateTime);
}
- if (libraryOptions == null)
- {
- libraryOptions = LibraryManager.GetLibraryOptions((BaseItem)item);
- }
-
// Save to database
await SaveItem(metadataResult, libraryOptions, updateType, cancellationToken).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
index e6eb0951d..4aefb62c8 100644
--- a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
+++ b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs
@@ -45,14 +45,21 @@ namespace MediaBrowser.Providers.Music
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(AlbumInfo searchInfo, CancellationToken cancellationToken)
{
var releaseId = searchInfo.GetReleaseId();
+ var releaseGroupId = searchInfo.GetReleaseGroupId();
string url = null;
var isNameSearch = false;
+ bool forceMusicBrainzProper = false;
if (!string.IsNullOrEmpty(releaseId))
{
url = string.Format("/ws/2/release/?query=reid:{0}", releaseId);
}
+ else if (!string.IsNullOrEmpty(releaseGroupId))
+ {
+ url = string.Format("/ws/2/release?release-group={0}", releaseGroupId);
+ forceMusicBrainzProper = true;
+ }
else
{
var artistMusicBrainzId = searchInfo.GetMusicBrainzArtistId();
@@ -75,7 +82,7 @@ namespace MediaBrowser.Providers.Music
if (!string.IsNullOrWhiteSpace(url))
{
- using (var stream = await GetMusicBrainzResponse(url, isNameSearch, cancellationToken).ConfigureAwait(false))
+ using (var stream = await GetMusicBrainzResponse(url, isNameSearch, forceMusicBrainzProper, cancellationToken).ConfigureAwait(false))
{
return GetResultsFromResponse(stream);
}
@@ -131,7 +138,14 @@ namespace MediaBrowser.Providers.Music
Item = new MusicAlbum()
};
- if (string.IsNullOrEmpty(releaseId))
+ // If we have a release group Id but not a release Id...
+ if (string.IsNullOrWhiteSpace(releaseId) && !string.IsNullOrWhiteSpace(releaseGroupId))
+ {
+ releaseId = await GetReleaseIdFromReleaseGroupId(releaseGroupId, cancellationToken).ConfigureAwait(false);
+ result.HasMetadata = true;
+ }
+
+ if (string.IsNullOrWhiteSpace(releaseId))
{
var artistMusicBrainzId = id.GetMusicBrainzArtistId();
@@ -139,13 +153,13 @@ namespace MediaBrowser.Providers.Music
if (releaseResult != null)
{
- if (!string.IsNullOrEmpty(releaseResult.ReleaseId))
+ if (!string.IsNullOrWhiteSpace(releaseResult.ReleaseId))
{
releaseId = releaseResult.ReleaseId;
result.HasMetadata = true;
}
- if (!string.IsNullOrEmpty(releaseResult.ReleaseGroupId))
+ if (!string.IsNullOrWhiteSpace(releaseResult.ReleaseGroupId))
{
releaseGroupId = releaseResult.ReleaseGroupId;
result.HasMetadata = true;
@@ -157,13 +171,13 @@ namespace MediaBrowser.Providers.Music
}
// If we have a release Id but not a release group Id...
- if (!string.IsNullOrEmpty(releaseId) && string.IsNullOrEmpty(releaseGroupId))
+ if (!string.IsNullOrWhiteSpace(releaseId) && string.IsNullOrWhiteSpace(releaseGroupId))
{
- releaseGroupId = await GetReleaseGroupId(releaseId, cancellationToken).ConfigureAwait(false);
+ releaseGroupId = await GetReleaseGroupFromReleaseId(releaseId, cancellationToken).ConfigureAwait(false);
result.HasMetadata = true;
}
- if (!string.IsNullOrEmpty(releaseId) || !string.IsNullOrEmpty(releaseGroupId))
+ if (!string.IsNullOrWhiteSpace(releaseId) || !string.IsNullOrWhiteSpace(releaseGroupId))
{
result.HasMetadata = true;
}
@@ -411,13 +425,42 @@ namespace MediaBrowser.Providers.Music
}
}
+ private async Task<string> GetReleaseIdFromReleaseGroupId(string releaseGroupId, CancellationToken cancellationToken)
+ {
+ var url = string.Format("/ws/2/release?release-group={0}", releaseGroupId);
+
+ using (var stream = await GetMusicBrainzResponse(url, true, true, cancellationToken).ConfigureAwait(false))
+ {
+ using (var oReader = new StreamReader(stream, Encoding.UTF8))
+ {
+ var settings = _xmlSettings.Create(false);
+
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
+
+ using (var reader = XmlReader.Create(oReader, settings))
+ {
+ var result = ReleaseResult.Parse(reader).FirstOrDefault();
+
+ if (result != null)
+ {
+ return result.ReleaseId;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
/// <summary>
/// Gets the release group id internal.
/// </summary>
/// <param name="releaseEntryId">The release entry id.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{System.String}.</returns>
- private async Task<string> GetReleaseGroupId(string releaseEntryId, CancellationToken cancellationToken)
+ private async Task<string> GetReleaseGroupFromReleaseId(string releaseEntryId, CancellationToken cancellationToken)
{
var url = string.Format("/ws/2/release-group/?query=reid:{0}", releaseEntryId);
@@ -514,11 +557,11 @@ namespace MediaBrowser.Providers.Music
private List<MbzUrl> _mbzUrls = null;
private MbzUrl _chosenUrl;
- private async Task<MbzUrl> GetMbzUrl()
+ private async Task<MbzUrl> GetMbzUrl(bool forceMusicBrainzProper = false)
{
if (_chosenUrl == null || _mbzUrls == null || (DateTime.UtcNow.Ticks - _lastMbzUrlQueryTicks) > TimeSpan.FromHours(12).Ticks)
{
- var urls = await RefreshMzbUrls().ConfigureAwait(false);
+ var urls = await RefreshMzbUrls(forceMusicBrainzProper).ConfigureAwait(false);
if (urls.Count > 1)
{
@@ -533,31 +576,44 @@ namespace MediaBrowser.Providers.Music
return _chosenUrl;
}
- private async Task<List<MbzUrl>> RefreshMzbUrls()
+ private async Task<List<MbzUrl>> RefreshMzbUrls(bool forceMusicBrainzProper = false)
{
List<MbzUrl> list;
- try
+ if (forceMusicBrainzProper)
{
- var options = new HttpRequestOptions
+ list = new List<MbzUrl>
{
- Url = "https://mb3admin.com/admin/service/standards/musicBrainzUrls",
- UserAgent = _appHost.Name + "/" + _appHost.ApplicationVersion
+ new MbzUrl
+ {
+ url = MusicBrainzBaseUrl,
+ throttleMs = 1000
+ }
};
-
- using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
+ }
+ else
+ {
+ try
{
- var results = _json.DeserializeFromStream<List<MbzUrl>>(stream);
+ var options = new HttpRequestOptions
+ {
+ Url = "https://mb3admin.com/admin/service/standards/musicBrainzUrls",
+ UserAgent = _appHost.Name + "/" + _appHost.ApplicationVersion
+ };
+
+ using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
+ {
+ var results = _json.DeserializeFromStream<List<MbzUrl>>(stream);
- list = results;
+ list = results;
+ }
+ _lastMbzUrlQueryTicks = DateTime.UtcNow.Ticks;
}
- _lastMbzUrlQueryTicks = DateTime.UtcNow.Ticks;
- }
- catch (Exception ex)
- {
- _logger.ErrorException("Error getting music brainz info", ex);
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error getting music brainz info", ex);
- list = new List<MbzUrl>
+ list = new List<MbzUrl>
{
new MbzUrl
{
@@ -565,6 +621,7 @@ namespace MediaBrowser.Providers.Music
throttleMs = 1000
}
};
+ }
}
_mbzUrls = list.ToList();
@@ -572,16 +629,17 @@ namespace MediaBrowser.Providers.Music
return list;
}
+ internal Task<Stream> GetMusicBrainzResponse(string url, bool isSearch, CancellationToken cancellationToken)
+ {
+ return GetMusicBrainzResponse(url, isSearch, false, cancellationToken);
+ }
+
/// <summary>
/// Gets the music brainz response.
/// </summary>
- /// <param name="url">The URL.</param>
- /// <param name="isSearch">if set to <c>true</c> [is search].</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task{XmlDocument}.</returns>
- internal async Task<Stream> GetMusicBrainzResponse(string url, bool isSearch, CancellationToken cancellationToken)
+ internal async Task<Stream> GetMusicBrainzResponse(string url, bool isSearch, bool forceMusicBrainzProper, CancellationToken cancellationToken)
{
- var urlInfo = await GetMbzUrl().ConfigureAwait(false);
+ var urlInfo = await GetMbzUrl(forceMusicBrainzProper).ConfigureAwait(false);
var throttleMs = urlInfo.throttleMs;
if (throttleMs > 0)
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs
index 46be61486..4a52b972f 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs
@@ -119,7 +119,7 @@ namespace MediaBrowser.Providers.TV
if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) &&
(searchInfo.IndexNumber.HasValue || searchInfo.PremiereDate.HasValue))
{
- await TvdbSeriesProvider.Current.EnsureSeriesInfo(searchInfo.SeriesProviderIds, searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false);
+ await TvdbSeriesProvider.Current.EnsureSeriesInfo(searchInfo.SeriesProviderIds, null, null, searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false);
var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, searchInfo.SeriesProviderIds);
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs
index f6af365fd..72bd62d9f 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs
@@ -389,7 +389,7 @@ namespace MediaBrowser.Providers.TV
_fileSystem.CreateDirectory(seriesDataPath);
- return TvdbSeriesProvider.Current.DownloadSeriesZip(id, MetadataProviders.Tvdb.ToString(), seriesDataPath, lastTvDbUpdateTime, preferredMetadataLanguage, cancellationToken);
+ return TvdbSeriesProvider.Current.DownloadSeriesZip(id, MetadataProviders.Tvdb.ToString(), null, null, seriesDataPath, lastTvDbUpdateTime, preferredMetadataLanguage, cancellationToken);
}
}
}
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs
index 2c4ccddd7..e68b7ad1d 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs
@@ -75,7 +75,7 @@ namespace MediaBrowser.Providers.TV
var seriesProviderIds = series.ProviderIds;
var seasonNumber = season.IndexNumber.Value;
- var seriesDataPath = await TvdbSeriesProvider.Current.EnsureSeriesInfo(seriesProviderIds, series.GetPreferredMetadataLanguage(), cancellationToken).ConfigureAwait(false);
+ var seriesDataPath = await TvdbSeriesProvider.Current.EnsureSeriesInfo(seriesProviderIds, series.Name, series.ProductionYear, series.GetPreferredMetadataLanguage(), cancellationToken).ConfigureAwait(false);
if (!string.IsNullOrWhiteSpace(seriesDataPath))
{
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs
index 97eedfa92..cdb9ac51e 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs
@@ -70,7 +70,7 @@ namespace MediaBrowser.Providers.TV
{
var language = item.GetPreferredMetadataLanguage();
- var seriesDataPath = await TvdbSeriesProvider.Current.EnsureSeriesInfo(item.ProviderIds, language, cancellationToken).ConfigureAwait(false);
+ var seriesDataPath = await TvdbSeriesProvider.Current.EnsureSeriesInfo(item.ProviderIds, item.Name, item.ProductionYear, language, cancellationToken).ConfigureAwait(false);
var path = Path.Combine(seriesDataPath, "banners.xml");
diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
index 8769dc801..c340d03f5 100644
--- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs
@@ -109,7 +109,7 @@ namespace MediaBrowser.Providers.TV
if (IsValidSeries(itemId.ProviderIds))
{
- await EnsureSeriesInfo(itemId.ProviderIds, itemId.MetadataLanguage, cancellationToken).ConfigureAwait(false);
+ await EnsureSeriesInfo(itemId.ProviderIds, itemId.Name, itemId.Year, itemId.MetadataLanguage, cancellationToken).ConfigureAwait(false);
result.Item = new Series();
result.HasMetadata = true;
@@ -160,24 +160,11 @@ namespace MediaBrowser.Providers.TV
/// <summary>
/// Downloads the series zip.
/// </summary>
- /// <param name="seriesId">The series id.</param>
- /// <param name="idType">Type of the identifier.</param>
- /// <param name="seriesDataPath">The series data path.</param>
- /// <param name="lastTvDbUpdateTime">The last tv database update time.</param>
- /// <param name="preferredMetadataLanguage">The preferred metadata language.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>Task.</returns>
- /// <exception cref="System.ArgumentNullException">seriesId</exception>
- internal async Task DownloadSeriesZip(string seriesId, string idType, string seriesDataPath, long? lastTvDbUpdateTime, string preferredMetadataLanguage, CancellationToken cancellationToken)
+ internal async Task DownloadSeriesZip(string seriesId, string idType, string seriesName, int? seriesYear, string seriesDataPath, long? lastTvDbUpdateTime, string preferredMetadataLanguage, CancellationToken cancellationToken)
{
- if (string.IsNullOrWhiteSpace(seriesId))
- {
- throw new ArgumentNullException("seriesId");
- }
-
try
{
- await DownloadSeriesZip(seriesId, idType, seriesDataPath, lastTvDbUpdateTime, preferredMetadataLanguage, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
+ await DownloadSeriesZip(seriesId, idType, seriesName, seriesYear, seriesDataPath, lastTvDbUpdateTime, preferredMetadataLanguage, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
return;
}
catch (HttpException ex)
@@ -190,11 +177,11 @@ namespace MediaBrowser.Providers.TV
if (!string.Equals(preferredMetadataLanguage, "en", StringComparison.OrdinalIgnoreCase))
{
- await DownloadSeriesZip(seriesId, idType, seriesDataPath, lastTvDbUpdateTime, "en", preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
+ await DownloadSeriesZip(seriesId, idType, seriesName, seriesYear, seriesDataPath, lastTvDbUpdateTime, "en", preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
}
}
- private async Task DownloadSeriesZip(string seriesId, string idType, string seriesDataPath, long? lastTvDbUpdateTime, string preferredMetadataLanguage, string saveAsMetadataLanguage, CancellationToken cancellationToken)
+ private async Task DownloadSeriesZip(string seriesId, string idType, string seriesName, int? seriesYear, string seriesDataPath, long? lastTvDbUpdateTime, string preferredMetadataLanguage, string saveAsMetadataLanguage, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(seriesId))
{
@@ -206,6 +193,23 @@ namespace MediaBrowser.Providers.TV
seriesId = await GetSeriesByRemoteId(seriesId, idType, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
}
+ // If searching by remote id came up empty, then do a regular search
+ if (string.IsNullOrWhiteSpace(seriesId) && !string.IsNullOrWhiteSpace(seriesName))
+ {
+ var searchInfo = new SeriesInfo
+ {
+ Name = seriesName,
+ Year = seriesYear,
+ MetadataLanguage = preferredMetadataLanguage
+ };
+ var results = await GetSearchResults(searchInfo, cancellationToken).ConfigureAwait(false);
+ var result = results.FirstOrDefault();
+ if (result != null)
+ {
+ seriesId = result.GetProviderId(MetadataProviders.Tvdb);
+ }
+ }
+
if (string.IsNullOrWhiteSpace(seriesId))
{
throw new ArgumentNullException("seriesId");
@@ -385,7 +389,7 @@ namespace MediaBrowser.Providers.TV
}
private SemaphoreSlim _ensureSemaphore = new SemaphoreSlim(1, 1);
- internal async Task<string> EnsureSeriesInfo(Dictionary<string, string> seriesProviderIds, string preferredMetadataLanguage, CancellationToken cancellationToken)
+ internal async Task<string> EnsureSeriesInfo(Dictionary<string, string> seriesProviderIds, string seriesName, int? seriesYear, string preferredMetadataLanguage, CancellationToken cancellationToken)
{
await _ensureSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
@@ -400,7 +404,7 @@ namespace MediaBrowser.Providers.TV
// The post-scan task will take care of updates so we don't need to re-download here
if (!IsCacheValid(seriesDataPath, preferredMetadataLanguage))
{
- await DownloadSeriesZip(seriesId, MetadataProviders.Tvdb.ToString(), seriesDataPath, null, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
+ await DownloadSeriesZip(seriesId, MetadataProviders.Tvdb.ToString(), seriesName, seriesYear, seriesDataPath, null, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
}
return seriesDataPath;
@@ -414,7 +418,7 @@ namespace MediaBrowser.Providers.TV
// The post-scan task will take care of updates so we don't need to re-download here
if (!IsCacheValid(seriesDataPath, preferredMetadataLanguage))
{
- await DownloadSeriesZip(seriesId, MetadataProviders.Imdb.ToString(), seriesDataPath, null, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
+ await DownloadSeriesZip(seriesId, MetadataProviders.Imdb.ToString(), seriesName, seriesYear, seriesDataPath, null, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
}
return seriesDataPath;
diff --git a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
index eb5e9880e..4d9f3e7c4 100644
--- a/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
+++ b/MediaBrowser.Server.Mac/Emby.Server.Mac.csproj
@@ -329,9 +329,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\favicon.ico">
<Link>Resources\dashboard-ui\favicon.ico</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\favorites.html">
- <Link>Resources\dashboard-ui\favorites.html</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\forgotpassword.html">
<Link>Resources\dashboard-ui\forgotpassword.html</Link>
</BundleResource>
@@ -665,9 +662,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\localassetmanager.js">
<Link>Resources\dashboard-ui\bower_components\emby-apiclient\localassetmanager.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\nullassetmanager.js">
- <Link>Resources\dashboard-ui\bower_components\emby-apiclient\nullassetmanager.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\serverdiscovery-chrome.js">
<Link>Resources\dashboard-ui\bower_components\emby-apiclient\serverdiscovery-chrome.js</Link>
</BundleResource>
@@ -698,9 +692,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\sync\multiserversync.js">
<Link>Resources\dashboard-ui\bower_components\emby-apiclient\sync\multiserversync.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\sync\offlineusersync.js">
- <Link>Resources\dashboard-ui\bower_components\emby-apiclient\sync\offlineusersync.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\sync\serversync.js">
<Link>Resources\dashboard-ui\bower_components\emby-apiclient\sync\serversync.js</Link>
</BundleResource>
@@ -710,9 +701,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\sync\useractionrepository.js">
<Link>Resources\dashboard-ui\bower_components\emby-apiclient\sync\useractionrepository.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-apiclient\sync\userrepository.js">
- <Link>Resources\dashboard-ui\bower_components\emby-apiclient\sync\userrepository.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\appsettings.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\appsettings.js</Link>
</BundleResource>
@@ -1205,6 +1193,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playback\autoplaydetect.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playback\autoplaydetect.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playback\brightnessosd.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playback\brightnessosd.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playback\iconosd.css">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playback\iconosd.css</Link>
</BundleResource>
@@ -1214,6 +1205,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playback\nowplayinghelper.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playback\nowplayinghelper.js</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playback\playaccessvalidation.js">
+ <Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playback\playaccessvalidation.js</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\bower_components\emby-webcomponents\playback\playbackmanager.js">
<Link>Resources\dashboard-ui\bower_components\emby-webcomponents\playback\playbackmanager.js</Link>
</BundleResource>
@@ -1886,6 +1880,9 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\site.css">
<Link>Resources\dashboard-ui\css\site.css</Link>
</BundleResource>
+ <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\videoosd.css">
+ <Link>Resources\dashboard-ui\css\videoosd.css</Link>
+ </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\css\images\ani_equalizer_black.gif">
<Link>Resources\dashboard-ui\css\images\ani_equalizer_black.gif</Link>
</BundleResource>
@@ -2228,9 +2225,6 @@
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\episodes.js">
<Link>Resources\dashboard-ui\scripts\episodes.js</Link>
</BundleResource>
- <BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\favorites.js">
- <Link>Resources\dashboard-ui\scripts\favorites.js</Link>
- </BundleResource>
<BundleResource Include="..\MediaBrowser.WebDashboard\dashboard-ui\scripts\forgotpassword.js">
<Link>Resources\dashboard-ui\scripts\forgotpassword.js</Link>
</BundleResource>
diff --git a/MediaBrowser.Server.Mac/MacAppHost.cs b/MediaBrowser.Server.Mac/MacAppHost.cs
index 59e72e71e..f84e96126 100644
--- a/MediaBrowser.Server.Mac/MacAppHost.cs
+++ b/MediaBrowser.Server.Mac/MacAppHost.cs
@@ -56,42 +56,6 @@ namespace MediaBrowser.Server.Mac
return new SyncManager();
}
- protected override FFMpegInstallInfo GetFfmpegInstallInfo()
- {
- var info = new FFMpegInstallInfo();
-
- info.ArchiveType = "7z";
-
- switch (EnvironmentInfo.SystemArchitecture)
- {
- case Architecture.X64:
- info.Version = "20160124";
- break;
- case Architecture.X86:
- info.Version = "20150110";
- break;
- }
-
- info.DownloadUrls = GetDownloadUrls();
-
- return info;
- }
-
- private string[] GetDownloadUrls()
- {
- switch (EnvironmentInfo.SystemArchitecture)
- {
- case Architecture.X64:
- return new[]
- {
- "https://github.com/MediaBrowser/Emby.Resources/raw/master/ffmpeg/osx/ffmpeg-x64-2.8.5.7z"
- };
- }
-
- // No version available
- return new string[] { };
- }
-
protected override void RestartInternal()
{
MainClass.Restart();
diff --git a/MediaBrowser.Server.Mac/Main.cs b/MediaBrowser.Server.Mac/Main.cs
index debd5f539..d703f7d0d 100644
--- a/MediaBrowser.Server.Mac/Main.cs
+++ b/MediaBrowser.Server.Mac/Main.cs
@@ -105,12 +105,11 @@ namespace MediaBrowser.Server.Mac
// Allow all https requests
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
- var fileSystem = new MonoFileSystem(logManager.GetLogger("FileSystem"), false, false, appPaths.TempDirectory);
- fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
+ var environmentInfo = GetEnvironmentInfo();
- _fileSystem = fileSystem;
+ var fileSystem = new MonoFileSystem(logManager.GetLogger("FileSystem"), environmentInfo, appPaths.TempDirectory);
- var environmentInfo = GetEnvironmentInfo();
+ _fileSystem = fileSystem;
var imageEncoder = ImageEncoderHelper.GetImageEncoder(_logger,
logManager,
diff --git a/MediaBrowser.Server.Mono/Native/MonoFileSystem.cs b/MediaBrowser.Server.Mono/Native/MonoFileSystem.cs
index a5dc691a7..91c064efe 100644
--- a/MediaBrowser.Server.Mono/Native/MonoFileSystem.cs
+++ b/MediaBrowser.Server.Mono/Native/MonoFileSystem.cs
@@ -1,13 +1,14 @@
using Emby.Common.Implementations.IO;
using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.System;
using Mono.Unix.Native;
namespace MediaBrowser.Server.Mono.Native
{
public class MonoFileSystem : ManagedFileSystem
{
- public MonoFileSystem(ILogger logger, bool supportsAsyncFileStreams, bool enableManagedInvalidFileNameChars, string tempPath)
- : base(logger, supportsAsyncFileStreams, enableManagedInvalidFileNameChars, true, tempPath)
+ public MonoFileSystem(ILogger logger, IEnvironmentInfo environment, string tempPath)
+ : base(logger, environment, tempPath)
{
}
diff --git a/MediaBrowser.Server.Mono/Program.cs b/MediaBrowser.Server.Mono/Program.cs
index 649283410..66851f7e9 100644
--- a/MediaBrowser.Server.Mono/Program.cs
+++ b/MediaBrowser.Server.Mono/Program.cs
@@ -114,12 +114,11 @@ namespace MediaBrowser.Server.Mono
// Allow all https requests
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate { return true; });
- var fileSystem = new MonoFileSystem(logManager.GetLogger("FileSystem"), false, false, appPaths.TempDirectory);
- fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
+ var environmentInfo = GetEnvironmentInfo();
- FileSystem = fileSystem;
+ var fileSystem = new MonoFileSystem(logManager.GetLogger("FileSystem"), environmentInfo, appPaths.TempDirectory);
- var environmentInfo = GetEnvironmentInfo();
+ FileSystem = fileSystem;
var imageEncoder = ImageEncoderHelper.GetImageEncoder(_logger, logManager, fileSystem, options, () => _appHost.HttpClient, appPaths);
diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs
index b41e7607c..b02c5d6ac 100644
--- a/MediaBrowser.ServerApplication/MainStartup.cs
+++ b/MediaBrowser.ServerApplication/MainStartup.cs
@@ -331,9 +331,9 @@ namespace MediaBrowser.ServerApplication
/// <param name="options">The options.</param>
private static void RunApplication(ServerApplicationPaths appPaths, ILogManager logManager, bool runService, StartupOptions options)
{
- var fileSystem = new ManagedFileSystem(logManager.GetLogger("FileSystem"), true, true, false, appPaths.TempDirectory);
- fileSystem.AddShortcutHandler(new LnkShortcutHandler());
- fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
+ var environmentInfo = new EnvironmentInfo();
+
+ var fileSystem = new ManagedFileSystem(logManager.GetLogger("FileSystem"), environmentInfo, appPaths.TempDirectory);
var imageEncoder = ImageEncoderHelper.GetImageEncoder(_logger, logManager, fileSystem, options, () => _appHost.HttpClient, appPaths);
@@ -345,7 +345,7 @@ namespace MediaBrowser.ServerApplication
fileSystem,
new PowerManagement(),
"emby.windows.zip",
- new EnvironmentInfo(),
+ environmentInfo,
imageEncoder,
new Server.Startup.Common.SystemEvents(logManager.GetLogger("SystemEvents")),
new RecyclableMemoryStreamProvider(),
diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
index 7ebbc3809..63e10df76 100644
--- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
+++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
@@ -142,8 +142,6 @@
<DependentUpon>MainForm.cs</DependentUpon>
</Compile>
<Compile Include="MainStartup.cs" />
- <Compile Include="Native\LnkShortcutHandler.cs" />
- <Compile Include="Native\LoopbackUtil.cs" />
<Compile Include="Native\PowerManagement.cs" />
<Compile Include="Native\Standby.cs" />
<Compile Include="Native\ServerAuthorization.cs" />
diff --git a/MediaBrowser.ServerApplication/WindowsAppHost.cs b/MediaBrowser.ServerApplication/WindowsAppHost.cs
index cc899462a..2d3d8a85b 100644
--- a/MediaBrowser.ServerApplication/WindowsAppHost.cs
+++ b/MediaBrowser.ServerApplication/WindowsAppHost.cs
@@ -4,12 +4,14 @@ using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices.ComTypes;
+using Emby.Common.Implementations.IO;
using Emby.Server.CinemaMode;
using Emby.Server.Connect;
using Emby.Server.Core;
using Emby.Server.Implementations;
using Emby.Server.Implementations.EntryPoints;
using Emby.Server.Implementations.FFMpeg;
+using Emby.Server.Implementations.Windows;
using Emby.Server.Sync;
using MediaBrowser.Controller.Connect;
using MediaBrowser.Controller.Sync;
@@ -107,11 +109,6 @@ namespace MediaBrowser.ServerApplication
}
}
- protected override void EnableLoopbackInternal(string appName)
- {
- LoopUtil.Run(appName);
- }
-
public override bool SupportsRunningAsService
{
get
diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec
index 95cee8be1..54133b718 100644
--- a/Nuget/MediaBrowser.Common.nuspec
+++ b/Nuget/MediaBrowser.Common.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common</id>
- <version>3.0.695</version>
+ <version>3.0.697</version>
<title>Emby.Common</title>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>
diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec
index f703690a9..e59460c94 100644
--- a/Nuget/MediaBrowser.Server.Core.nuspec
+++ b/Nuget/MediaBrowser.Server.Core.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Server.Core</id>
- <version>3.0.695</version>
+ <version>3.0.697</version>
<title>Emby.Server.Core</title>
<authors>Emby Team</authors>
<owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Emby Server.</description>
<copyright>Copyright © Emby 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.695" />
+ <dependency id="MediaBrowser.Common" version="3.0.696" />
</dependencies>
</metadata>
<files>
diff --git a/SharedVersion.cs b/SharedVersion.cs
index 5628aba38..1c2997eed 100644
--- a/SharedVersion.cs
+++ b/SharedVersion.cs
@@ -1,3 +1,3 @@
using System.Reflection;
-[assembly: AssemblyVersion("3.2.5.5")]
+[assembly: AssemblyVersion("3.2.8.1")]
diff --git a/SocketHttpListener.Portable/Net/EndPointListener.cs b/SocketHttpListener.Portable/Net/EndPointListener.cs
index c7642d5d1..4f1a17fc0 100644
--- a/SocketHttpListener.Portable/Net/EndPointListener.cs
+++ b/SocketHttpListener.Portable/Net/EndPointListener.cs
@@ -32,8 +32,9 @@ namespace SocketHttpListener.Net
private readonly ISocketFactory _socketFactory;
private readonly ITextEncoding _textEncoding;
private readonly IMemoryStreamFactory _memoryStreamFactory;
+ private readonly IFileSystem _fileSystem;
- public EndPointListener(HttpListener listener, IpAddressInfo addr, int port, bool secure, ICertificate cert, ILogger logger, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding)
+ public EndPointListener(HttpListener listener, IpAddressInfo addr, int port, bool secure, ICertificate cert, ILogger logger, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem)
{
this.listener = listener;
_logger = logger;
@@ -42,6 +43,7 @@ namespace SocketHttpListener.Net
_socketFactory = socketFactory;
_memoryStreamFactory = memoryStreamFactory;
_textEncoding = textEncoding;
+ _fileSystem = fileSystem;
this.secure = secure;
this.cert = cert;
@@ -107,7 +109,7 @@ namespace SocketHttpListener.Net
return;
}
- HttpConnection conn = await HttpConnection.Create(_logger, accepted, listener, listener.secure, listener.cert, _cryptoProvider, _streamFactory, _memoryStreamFactory, _textEncoding).ConfigureAwait(false);
+ HttpConnection conn = await HttpConnection.Create(_logger, accepted, listener, listener.secure, listener.cert, _cryptoProvider, _streamFactory, _memoryStreamFactory, _textEncoding, _fileSystem).ConfigureAwait(false);
//_logger.Debug("Adding unregistered connection to {0}. Id: {1}", accepted.RemoteEndPoint, connectionId);
lock (listener.unregistered)
diff --git a/SocketHttpListener.Portable/Net/EndPointManager.cs b/SocketHttpListener.Portable/Net/EndPointManager.cs
index 797684b3e..11f774915 100644
--- a/SocketHttpListener.Portable/Net/EndPointManager.cs
+++ b/SocketHttpListener.Portable/Net/EndPointManager.cs
@@ -5,6 +5,7 @@ using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading.Tasks;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
using SocketHttpListener.Primitives;
@@ -105,7 +106,7 @@ namespace SocketHttpListener.Net
}
else
{
- epl = new EndPointListener(listener, addr, port, secure, listener.Certificate, logger, listener.CryptoProvider, listener.StreamFactory, listener.SocketFactory, listener.MemoryStreamFactory, listener.TextEncoding);
+ epl = new EndPointListener(listener, addr, port, secure, listener.Certificate, logger, listener.CryptoProvider, listener.StreamFactory, listener.SocketFactory, listener.MemoryStreamFactory, listener.TextEncoding, listener.FileSystem);
p[port] = epl;
}
diff --git a/SocketHttpListener.Portable/Net/HttpConnection.cs b/SocketHttpListener.Portable/Net/HttpConnection.cs
index 4a2cf3f5b..5fe47fc63 100644
--- a/SocketHttpListener.Portable/Net/HttpConnection.cs
+++ b/SocketHttpListener.Portable/Net/HttpConnection.cs
@@ -35,13 +35,14 @@ namespace SocketHttpListener.Net
ICertificate cert;
Stream ssl_stream;
- private ILogger _logger;
+ private readonly ILogger _logger;
private readonly ICryptoProvider _cryptoProvider;
private readonly IMemoryStreamFactory _memoryStreamFactory;
private readonly ITextEncoding _textEncoding;
private readonly IStreamFactory _streamFactory;
+ private readonly IFileSystem _fileSystem;
- private HttpConnection(ILogger logger, IAcceptSocket sock, EndPointListener epl, bool secure, ICertificate cert, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding)
+ private HttpConnection(ILogger logger, IAcceptSocket sock, EndPointListener epl, bool secure, ICertificate cert, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem)
{
_logger = logger;
this.sock = sock;
@@ -51,6 +52,7 @@ namespace SocketHttpListener.Net
_cryptoProvider = cryptoProvider;
_memoryStreamFactory = memoryStreamFactory;
_textEncoding = textEncoding;
+ _fileSystem = fileSystem;
_streamFactory = streamFactory;
}
@@ -82,9 +84,9 @@ namespace SocketHttpListener.Net
Init();
}
- public static async Task<HttpConnection> Create(ILogger logger, IAcceptSocket sock, EndPointListener epl, bool secure, ICertificate cert, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding)
+ public static async Task<HttpConnection> Create(ILogger logger, IAcceptSocket sock, EndPointListener epl, bool secure, ICertificate cert, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem)
{
- var connection = new HttpConnection(logger, sock, epl, secure, cert, cryptoProvider, streamFactory, memoryStreamFactory, textEncoding);
+ var connection = new HttpConnection(logger, sock, epl, secure, cert, cryptoProvider, streamFactory, memoryStreamFactory, textEncoding, fileSystem);
await connection.InitStream().ConfigureAwait(false);
@@ -121,7 +123,7 @@ namespace SocketHttpListener.Net
position = 0;
input_state = InputState.RequestLine;
line_state = LineState.None;
- context = new HttpListenerContext(this, _logger, _cryptoProvider, _memoryStreamFactory, _textEncoding);
+ context = new HttpListenerContext(this, _logger, _cryptoProvider, _memoryStreamFactory, _textEncoding, _fileSystem);
}
public bool IsClosed
@@ -213,7 +215,9 @@ namespace SocketHttpListener.Net
if (context.Response.SendChunked || isExpect100Continue || context.Request.IsWebSocketRequest || true)
{
- o_stream = new ResponseStream(stream, context.Response, _memoryStreamFactory, _textEncoding);
+ var supportsDirectSocketAccess = !context.Response.SendChunked && !isExpect100Continue && !secure;
+
+ o_stream = new ResponseStream(stream, context.Response, _memoryStreamFactory, _textEncoding, _fileSystem, sock, supportsDirectSocketAccess);
}
else
{
diff --git a/SocketHttpListener.Portable/Net/HttpListener.cs b/SocketHttpListener.Portable/Net/HttpListener.cs
index 2b0f75d01..c2e7acd8e 100644
--- a/SocketHttpListener.Portable/Net/HttpListener.cs
+++ b/SocketHttpListener.Portable/Net/HttpListener.cs
@@ -18,6 +18,7 @@ namespace SocketHttpListener.Net
internal ICryptoProvider CryptoProvider { get; private set; }
internal IStreamFactory StreamFactory { get; private set; }
internal ISocketFactory SocketFactory { get; private set; }
+ internal IFileSystem FileSystem { get; private set; }
internal ITextEncoding TextEncoding { get; private set; }
internal IMemoryStreamFactory MemoryStreamFactory { get; private set; }
internal INetworkManager NetworkManager { get; private set; }
@@ -39,7 +40,7 @@ namespace SocketHttpListener.Net
public Action<HttpListenerContext> OnContext { get; set; }
- public HttpListener(ILogger logger, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory)
+ public HttpListener(ILogger logger, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem)
{
_logger = logger;
CryptoProvider = cryptoProvider;
@@ -48,19 +49,20 @@ namespace SocketHttpListener.Net
NetworkManager = networkManager;
TextEncoding = textEncoding;
MemoryStreamFactory = memoryStreamFactory;
+ FileSystem = fileSystem;
prefixes = new HttpListenerPrefixCollection(logger, this);
registry = new Dictionary<HttpListenerContext, HttpListenerContext>();
connections = new Dictionary<HttpConnection, HttpConnection>();
auth_schemes = AuthenticationSchemes.Anonymous;
}
- public HttpListener(ICertificate certificate, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory)
- :this(new NullLogger(), certificate, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory)
+ public HttpListener(ICertificate certificate, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem)
+ :this(new NullLogger(), certificate, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory, fileSystem)
{
}
- public HttpListener(ILogger logger, ICertificate certificate, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory)
- : this(logger, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory)
+ public HttpListener(ILogger logger, ICertificate certificate, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem)
+ : this(logger, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory, fileSystem)
{
_certificate = certificate;
}
diff --git a/SocketHttpListener.Portable/Net/HttpListenerContext.cs b/SocketHttpListener.Portable/Net/HttpListenerContext.cs
index 182fd2d2a..58d769f22 100644
--- a/SocketHttpListener.Portable/Net/HttpListenerContext.cs
+++ b/SocketHttpListener.Portable/Net/HttpListenerContext.cs
@@ -18,20 +18,18 @@ namespace SocketHttpListener.Net
HttpConnection cnc;
string error;
int err_status = 400;
- private readonly ILogger _logger;
private readonly ICryptoProvider _cryptoProvider;
private readonly IMemoryStreamFactory _memoryStreamFactory;
private readonly ITextEncoding _textEncoding;
- internal HttpListenerContext(HttpConnection cnc, ILogger logger, ICryptoProvider cryptoProvider, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding)
+ internal HttpListenerContext(HttpConnection cnc, ILogger logger, ICryptoProvider cryptoProvider, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem)
{
this.cnc = cnc;
- _logger = logger;
_cryptoProvider = cryptoProvider;
_memoryStreamFactory = memoryStreamFactory;
_textEncoding = textEncoding;
request = new HttpListenerRequest(this, _textEncoding);
- response = new HttpListenerResponse(this, _logger, _textEncoding);
+ response = new HttpListenerResponse(this, logger, _textEncoding, fileSystem);
}
internal int ErrorStatus
diff --git a/SocketHttpListener.Portable/Net/HttpListenerRequest.cs b/SocketHttpListener.Portable/Net/HttpListenerRequest.cs
index 811cc6ddb..cfbd49203 100644
--- a/SocketHttpListener.Portable/Net/HttpListenerRequest.cs
+++ b/SocketHttpListener.Portable/Net/HttpListenerRequest.cs
@@ -456,7 +456,7 @@ namespace SocketHttpListener.Net
public long ContentLength64
{
- get { return content_length; }
+ get { return is_chunked ? -1 : content_length; }
}
public string ContentType
diff --git a/SocketHttpListener.Portable/Net/HttpListenerResponse.cs b/SocketHttpListener.Portable/Net/HttpListenerResponse.cs
index 9a5862cb9..d9f91c0cc 100644
--- a/SocketHttpListener.Portable/Net/HttpListenerResponse.cs
+++ b/SocketHttpListener.Portable/Net/HttpListenerResponse.cs
@@ -3,6 +3,9 @@ using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Text;
using SocketHttpListener.Primitives;
@@ -32,12 +35,14 @@ namespace SocketHttpListener.Net
private readonly ILogger _logger;
private readonly ITextEncoding _textEncoding;
+ private readonly IFileSystem _fileSystem;
- internal HttpListenerResponse(HttpListenerContext context, ILogger logger, ITextEncoding textEncoding)
+ internal HttpListenerResponse(HttpListenerContext context, ILogger logger, ITextEncoding textEncoding, IFileSystem fileSystem)
{
this.context = context;
_logger = logger;
_textEncoding = textEncoding;
+ _fileSystem = fileSystem;
}
internal bool CloseConnection
@@ -366,7 +371,7 @@ namespace SocketHttpListener.Net
{
if (chunked)
{
- return ;
+ return;
}
Version v = context.Request.ProtocolVersion;
@@ -509,5 +514,10 @@ namespace SocketHttpListener.Net
cookies.Add(cookie);
}
+
+ public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
+ {
+ return ((ResponseStream)OutputStream).TransmitFile(path, offset, count, fileShareMode, cancellationToken);
+ }
}
} \ No newline at end of file
diff --git a/SocketHttpListener.Portable/Net/ResponseStream.cs b/SocketHttpListener.Portable/Net/ResponseStream.cs
index a79a18791..19821f954 100644
--- a/SocketHttpListener.Portable/Net/ResponseStream.cs
+++ b/SocketHttpListener.Portable/Net/ResponseStream.cs
@@ -5,6 +5,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Net;
using MediaBrowser.Model.Text;
using SocketHttpListener.Primitives;
@@ -22,12 +23,18 @@ namespace SocketHttpListener.Net
Stream stream;
private readonly IMemoryStreamFactory _memoryStreamFactory;
private readonly ITextEncoding _textEncoding;
+ private readonly IFileSystem _fileSystem;
+ private readonly IAcceptSocket _socket;
+ private readonly bool _supportsDirectSocketAccess;
- internal ResponseStream(Stream stream, HttpListenerResponse response, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding)
+ internal ResponseStream(Stream stream, HttpListenerResponse response, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IAcceptSocket socket, bool supportsDirectSocketAccess)
{
this.response = response;
_memoryStreamFactory = memoryStreamFactory;
_textEncoding = textEncoding;
+ _fileSystem = fileSystem;
+ _socket = socket;
+ _supportsDirectSocketAccess = supportsDirectSocketAccess;
this.stream = stream;
}
@@ -299,5 +306,80 @@ namespace SocketHttpListener.Net
{
throw new NotSupportedException();
}
+
+ public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
+ {
+ //if (_supportsDirectSocketAccess && offset == 0 && count == 0 && !response.SendChunked)
+ //{
+ // return TransmitFileOverSocket(path, offset, count, cancellationToken);
+ //}
+ return TransmitFileManaged(path, offset, count, fileShareMode, cancellationToken);
+ }
+
+ private readonly byte[] _emptyBuffer = new byte[] { };
+ private async Task TransmitFileOverSocket(string path, long offset, long count, CancellationToken cancellationToken)
+ {
+ MemoryStream ms = GetHeaders(response, _memoryStreamFactory, false);
+
+ var buffer = new byte[] {};
+ if (ms != null)
+ {
+ ms.Position = 0;
+
+ byte[] msBuffer;
+ _memoryStreamFactory.TryGetBuffer(ms, out msBuffer);
+ buffer = msBuffer;
+ }
+
+ await _socket.SendFile(path, buffer, _emptyBuffer, cancellationToken).ConfigureAwait(false);
+ }
+
+ private async Task TransmitFileManaged(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken)
+ {
+ var chunked = response.SendChunked;
+
+ if (!chunked)
+ {
+ await WriteAsync(_emptyBuffer, 0, 0, cancellationToken).ConfigureAwait(false);
+ }
+
+ using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShareMode, true))
+ {
+ if (offset > 0)
+ {
+ fs.Position = offset;
+ }
+
+ var targetStream = chunked ? this : stream;
+
+ if (count > 0)
+ {
+ await CopyToInternalAsync(fs, targetStream, count, cancellationToken).ConfigureAwait(false);
+ }
+ else
+ {
+ await fs.CopyToAsync(targetStream, 81920, cancellationToken).ConfigureAwait(false);
+ }
+ }
+ }
+
+ private async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken)
+ {
+ var array = new byte[81920];
+ int count;
+ while ((count = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0)
+ {
+ var bytesToCopy = Math.Min(count, copyLength);
+
+ await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToCopy), cancellationToken).ConfigureAwait(false);
+
+ copyLength -= bytesToCopy;
+
+ if (copyLength <= 0)
+ {
+ break;
+ }
+ }
+ }
}
}