This commit is contained in:
Eric Reed 2013-09-25 14:32:36 -04:00
commit c02c0db35a
114 changed files with 2747 additions and 1354 deletions

View file

@ -57,3 +57,4 @@
- [Detector1](https://github.com/Detector1)
- [BlackIce013](https://github.com/blackice013)
- [mporcas] (https://github.com/mporcas)
- [tikuf] (https://github.com/tikuf/)

View file

@ -62,7 +62,7 @@ namespace MediaBrowser.Api
{
var jobCount = _activeTranscodingJobs.Count;
Parallel.ForEach(_activeTranscodingJobs, OnTranscodeKillTimerStopped);
Parallel.ForEach(_activeTranscodingJobs, KillTranscodingJob);
// Try to allow for some time to kill the ffmpeg processes and delete the partial stream files
if (jobCount > 0)
@ -84,7 +84,8 @@ namespace MediaBrowser.Api
/// <param name="process">The process.</param>
/// <param name="isVideo">if set to <c>true</c> [is video].</param>
/// <param name="startTimeTicks">The start time ticks.</param>
public void OnTranscodeBeginning(string path, TranscodingJobType type, Process process, bool isVideo, long? startTimeTicks)
/// <param name="sourcePath">The source path.</param>
public void OnTranscodeBeginning(string path, TranscodingJobType type, Process process, bool isVideo, long? startTimeTicks, string sourcePath)
{
lock (_activeTranscodingJobs)
{
@ -95,7 +96,8 @@ namespace MediaBrowser.Api
Process = process,
ActiveRequestCount = 1,
IsVideo = isVideo,
StartTimeTicks = startTimeTicks
StartTimeTicks = startTimeTicks,
SourcePath = sourcePath
});
}
}
@ -178,7 +180,7 @@ namespace MediaBrowser.Api
if (job.ActiveRequestCount == 0)
{
var timerDuration = type == TranscodingJobType.Progressive ? 1000 : 60000;
var timerDuration = type == TranscodingJobType.Progressive ? 1000 : 180000;
if (job.KillTimer == null)
{
@ -196,10 +198,47 @@ namespace MediaBrowser.Api
/// Called when [transcode kill timer stopped].
/// </summary>
/// <param name="state">The state.</param>
private async void OnTranscodeKillTimerStopped(object state)
private void OnTranscodeKillTimerStopped(object state)
{
var job = (TranscodingJob)state;
KillTranscodingJob(job);
}
/// <summary>
/// Kills the single transcoding job.
/// </summary>
/// <param name="sourcePath">The source path.</param>
internal void KillSingleTranscodingJob(string sourcePath)
{
if (string.IsNullOrEmpty(sourcePath))
{
throw new ArgumentNullException("sourcePath");
}
var jobs = new List<TranscodingJob>();
lock (_activeTranscodingJobs)
{
// This is really only needed for HLS.
// Progressive streams can stop on their own reliably
jobs.AddRange(_activeTranscodingJobs.Where(i => string.Equals(sourcePath, i.SourcePath) && i.Type == TranscodingJobType.Hls));
}
// This method of killing is a bit of a shortcut, but it saves clients from having to send a request just for that
// But we can only kill if there's one active job. If there are more we won't know which one to stop
if (jobs.Count == 1)
{
KillTranscodingJob(jobs.First());
}
}
/// <summary>
/// Kills the transcoding job.
/// </summary>
/// <param name="job">The job.</param>
private async void KillTranscodingJob(TranscodingJob job)
{
lock (_activeTranscodingJobs)
{
_activeTranscodingJobs.Remove(job);
@ -373,6 +412,7 @@ namespace MediaBrowser.Api
public bool IsVideo { get; set; }
public long? StartTimeTicks { get; set; }
public string SourcePath { get; set; }
}
/// <summary>

View file

@ -0,0 +1,181 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Logging;
using ServiceStack.Common.Web;
using ServiceStack.ServiceHost;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Api
{
public class AuthorizationRequestFilterAttribute : Attribute, IHasRequestFilter
{
//This property will be resolved by the IoC container
/// <summary>
/// Gets or sets the user manager.
/// </summary>
/// <value>The user manager.</value>
public IUserManager UserManager { get; set; }
public ISessionManager SessionManager { get; set; }
/// <summary>
/// Gets or sets the logger.
/// </summary>
/// <value>The logger.</value>
public ILogger Logger { get; set; }
/// <summary>
/// The request filter is executed before the service.
/// </summary>
/// <param name="request">The http request wrapper</param>
/// <param name="response">The http response wrapper</param>
/// <param name="requestDto">The request DTO</param>
public void RequestFilter(IHttpRequest request, IHttpResponse response, object requestDto)
{
//This code is executed before the service
var auth = GetAuthorization(request);
if (auth != null)
{
User user = null;
if (auth.ContainsKey("UserId"))
{
var userId = auth["UserId"];
if (!string.IsNullOrEmpty(userId))
{
user = UserManager.GetUserById(new Guid(userId));
}
}
string deviceId;
string device;
string client;
string version;
auth.TryGetValue("DeviceId", out deviceId);
auth.TryGetValue("Device", out device);
auth.TryGetValue("Client", out client);
auth.TryGetValue("Version", out version);
if (!string.IsNullOrEmpty(client) && !string.IsNullOrEmpty(deviceId) && !string.IsNullOrEmpty(device) && !string.IsNullOrEmpty(version))
{
SessionManager.LogConnectionActivity(client, version, deviceId, device, user);
}
}
}
/// <summary>
/// Gets the auth.
/// </summary>
/// <param name="httpReq">The HTTP req.</param>
/// <returns>Dictionary{System.StringSystem.String}.</returns>
public static Dictionary<string, string> GetAuthorization(IHttpRequest httpReq)
{
var auth = httpReq.Headers[HttpHeaders.Authorization];
return GetAuthorization(auth);
}
/// <summary>
/// Gets the authorization.
/// </summary>
/// <param name="httpReq">The HTTP req.</param>
/// <returns>Dictionary{System.StringSystem.String}.</returns>
public static AuthorizationInfo GetAuthorization(IRequestContext httpReq)
{
var header = httpReq.GetHeader("Authorization");
var auth = GetAuthorization(header);
string userId;
string deviceId;
string device;
string client;
string version;
auth.TryGetValue("UserId", out userId);
auth.TryGetValue("DeviceId", out deviceId);
auth.TryGetValue("Device", out device);
auth.TryGetValue("Client", out client);
auth.TryGetValue("Version", out version);
return new AuthorizationInfo
{
Client = client,
Device = device,
DeviceId = deviceId,
UserId = userId,
Version = version
};
}
/// <summary>
/// Gets the authorization.
/// </summary>
/// <param name="authorizationHeader">The authorization header.</param>
/// <returns>Dictionary{System.StringSystem.String}.</returns>
private static Dictionary<string, string> GetAuthorization(string authorizationHeader)
{
if (authorizationHeader == null) return null;
var parts = authorizationHeader.Split(' ');
// There should be at least to parts
if (parts.Length < 2) return null;
// It has to be a digest request
if (!string.Equals(parts[0], "MediaBrowser", StringComparison.OrdinalIgnoreCase))
{
return null;
}
// Remove uptil the first space
authorizationHeader = authorizationHeader.Substring(authorizationHeader.IndexOf(' '));
parts = authorizationHeader.Split(',');
var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var item in parts)
{
var param = item.Trim().Split(new[] { '=' }, 2);
result.Add(param[0], param[1].Trim(new[] { '"' }));
}
return result;
}
/// <summary>
/// A new shallow copy of this filter is used on every request.
/// </summary>
/// <returns>IHasRequestFilter.</returns>
public IHasRequestFilter Copy()
{
return this;
}
/// <summary>
/// Order in which Request Filters are executed.
/// &lt;0 Executed before global request filters
/// &gt;0 Executed after global request filters
/// </summary>
/// <value>The priority.</value>
public int Priority
{
get { return 0; }
}
}
public class AuthorizationInfo
{
public string UserId;
public string DeviceId;
public string Device;
public string Client;
public string Version;
}
}

View file

@ -2,9 +2,7 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Logging;
using ServiceStack.Common.Web;
using ServiceStack.ServiceHost;
using System;
using System.Collections.Generic;
@ -15,7 +13,7 @@ namespace MediaBrowser.Api
/// <summary>
/// Class BaseApiService
/// </summary>
[RequestFilter]
[AuthorizationRequestFilter]
public class BaseApiService : IHasResultFactory, IRestfulService
{
/// <summary>
@ -308,147 +306,4 @@ namespace MediaBrowser.Api
return item;
}
}
/// <summary>
/// Class RequestFilterAttribute
/// </summary>
public class RequestFilterAttribute : Attribute, IHasRequestFilter
{
//This property will be resolved by the IoC container
/// <summary>
/// Gets or sets the user manager.
/// </summary>
/// <value>The user manager.</value>
public IUserManager UserManager { get; set; }
public ISessionManager SessionManager { get; set; }
/// <summary>
/// Gets or sets the logger.
/// </summary>
/// <value>The logger.</value>
public ILogger Logger { get; set; }
/// <summary>
/// The request filter is executed before the service.
/// </summary>
/// <param name="request">The http request wrapper</param>
/// <param name="response">The http response wrapper</param>
/// <param name="requestDto">The request DTO</param>
public void RequestFilter(IHttpRequest request, IHttpResponse response, object requestDto)
{
//This code is executed before the service
var auth = GetAuthorization(request);
if (auth != null)
{
User user = null;
if (auth.ContainsKey("UserId"))
{
var userId = auth["UserId"];
if (!string.IsNullOrEmpty(userId))
{
user = UserManager.GetUserById(new Guid(userId));
}
}
string deviceId;
string device;
string client;
string version;
auth.TryGetValue("DeviceId", out deviceId);
auth.TryGetValue("Device", out device);
auth.TryGetValue("Client", out client);
auth.TryGetValue("Version", out version);
if (!string.IsNullOrEmpty(client) && !string.IsNullOrEmpty(deviceId) && !string.IsNullOrEmpty(device) && !string.IsNullOrEmpty(version))
{
SessionManager.LogConnectionActivity(client, version, deviceId, device, user);
}
}
}
/// <summary>
/// Gets the auth.
/// </summary>
/// <param name="httpReq">The HTTP req.</param>
/// <returns>Dictionary{System.StringSystem.String}.</returns>
public static Dictionary<string, string> GetAuthorization(IHttpRequest httpReq)
{
var auth = httpReq.Headers[HttpHeaders.Authorization];
return GetAuthorization(auth);
}
/// <summary>
/// Gets the authorization.
/// </summary>
/// <param name="httpReq">The HTTP req.</param>
/// <returns>Dictionary{System.StringSystem.String}.</returns>
public static Dictionary<string, string> GetAuthorization(IRequestContext httpReq)
{
var auth = httpReq.GetHeader("Authorization");
return GetAuthorization(auth);
}
/// <summary>
/// Gets the authorization.
/// </summary>
/// <param name="authorizationHeader">The authorization header.</param>
/// <returns>Dictionary{System.StringSystem.String}.</returns>
private static Dictionary<string, string> GetAuthorization(string authorizationHeader)
{
if (authorizationHeader == null) return null;
var parts = authorizationHeader.Split(' ');
// There should be at least to parts
if (parts.Length < 2) return null;
// It has to be a digest request
if (!string.Equals(parts[0], "MediaBrowser", StringComparison.OrdinalIgnoreCase))
{
return null;
}
// Remove uptil the first space
authorizationHeader = authorizationHeader.Substring(authorizationHeader.IndexOf(' '));
parts = authorizationHeader.Split(',');
var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var item in parts)
{
var param = item.Trim().Split(new[] { '=' }, 2);
result.Add(param[0], param[1].Trim(new[] { '"' }));
}
return result;
}
/// <summary>
/// A new shallow copy of this filter is used on every request.
/// </summary>
/// <returns>IHasRequestFilter.</returns>
public IHasRequestFilter Copy()
{
return this;
}
/// <summary>
/// Order in which Request Filters are executed.
/// &lt;0 Executed before global request filters
/// &gt;0 Executed after global request filters
/// </summary>
/// <value>The priority.</value>
public int Priority
{
get { return 0; }
}
}
}

View file

@ -13,6 +13,8 @@
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -36,29 +38,25 @@
<RunPostBuildEvent>Always</RunPostBuildEvent>
</PropertyGroup>
<ItemGroup>
<Reference Include="MoreLinq, Version=1.0.16006.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Common, Version=3.9.60.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Interfaces, Version=3.9.60.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Text, Version=3.9.59.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.XML" />
<Reference Include="MoreLinq">
<HintPath>..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Common">
<HintPath>..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Interfaces">
<HintPath>..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Text">
<HintPath>..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll</HintPath>
</Reference>
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\SharedVersion.cs">
@ -70,6 +68,7 @@
<Compile Include="DefaultTheme\Models.cs" />
<Compile Include="DisplayPreferencesService.cs" />
<Compile Include="EnvironmentService.cs" />
<Compile Include="AuthorizationRequestFilterAttribute.cs" />
<Compile Include="GamesService.cs" />
<Compile Include="Images\ImageByNameService.cs" />
<Compile Include="Images\ImageRequest.cs" />
@ -88,6 +87,8 @@
<Compile Include="PackageService.cs" />
<Compile Include="Playback\Hls\AudioHlsService.cs" />
<Compile Include="Playback\Hls\BaseHlsService.cs" />
<Compile Include="Playback\Hls\HlsSegmentResponseFilter.cs" />
<Compile Include="Playback\Hls\HlsSegmentService.cs" />
<Compile Include="Playback\Hls\VideoHlsService.cs" />
<Compile Include="Playback\Progressive\AudioService.cs" />
<Compile Include="Playback\Progressive\BaseProgressiveStreamingService.cs" />
@ -128,22 +129,24 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
<Project>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</Project>
<Name>MediaBrowser.Common</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj">
<Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
<Project>{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}</Project>
<Name>MediaBrowser.Controller</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
<Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
<Name>MediaBrowser.Model</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Folder Include="Filters\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>

View file

@ -613,7 +613,7 @@ namespace MediaBrowser.Api.Playback
EnableRaisingEvents = true
};
ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath, TranscodingJobType, process, video != null, state.Request.StartTimeTicks);
ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath, TranscodingJobType, process, video != null, state.Request.StartTimeTicks, state.Item.Path);
Logger.Info(process.StartInfo.FileName + " " + process.StartInfo.Arguments);

View file

@ -6,7 +6,6 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.IO;
using ServiceStack.ServiceHost;
using System;
using System.IO;
namespace MediaBrowser.Api.Playback.Hls
{
@ -20,27 +19,6 @@ namespace MediaBrowser.Api.Playback.Hls
}
/// <summary>
/// Class GetHlsAudioSegment
/// </summary>
[Route("/Audio/{Id}/hls/{SegmentId}/stream.mp3", "GET")]
[Route("/Audio/{Id}/hls/{SegmentId}/stream.aac", "GET")]
[Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
public class GetHlsAudioSegment
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
public string Id { get; set; }
/// <summary>
/// Gets or sets the segment id.
/// </summary>
/// <value>The segment id.</value>
public string SegmentId { get; set; }
}
/// <summary>
/// Class AudioHlsService
/// </summary>
@ -59,20 +37,6 @@ namespace MediaBrowser.Api.Playback.Hls
{
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetHlsAudioSegment request)
{
var file = request.SegmentId + Path.GetExtension(RequestContext.PathInfo);
file = Path.Combine(ApplicationPaths.EncodedMediaCachePath, file);
return ResultFactory.GetStaticFileResult(RequestContext, file, FileShare.ReadWrite);
}
/// <summary>
/// Gets the specified request.
/// </summary>

View file

@ -10,7 +10,6 @@ using MediaBrowser.Model.IO;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@ -213,29 +212,6 @@ namespace MediaBrowser.Api.Playback.Hls
return count;
}
protected void ExtendHlsTimer(string itemId, string playlistId)
{
var normalizedPlaylistId = playlistId.Replace("-low", string.Empty);
foreach (var playlist in Directory.EnumerateFiles(ApplicationPaths.EncodedMediaCachePath, "*.m3u8")
.Where(i => i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1)
.ToList())
{
ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
// Avoid implicitly captured closure
var playlist1 = playlist;
Task.Run(async () =>
{
// This is an arbitrary time period corresponding to when the request completes.
await Task.Delay(30000).ConfigureAwait(false);
ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist1, TranscodingJobType.Hls);
});
}
}
/// <summary>
/// Gets the command line arguments.
/// </summary>

View file

@ -0,0 +1,53 @@
using MediaBrowser.Controller;
using MediaBrowser.Model.Logging;
using ServiceStack.ServiceHost;
using ServiceStack.Text.Controller;
using System;
using System.IO;
using System.Linq;
namespace MediaBrowser.Api.Playback.Hls
{
public class HlsSegmentResponseFilter : Attribute, IHasResponseFilter
{
public ILogger Logger { get; set; }
public IServerApplicationPaths ApplicationPaths { get; set; }
public void ResponseFilter(IHttpRequest req, IHttpResponse res, object response)
{
var pathInfo = PathInfo.Parse(req.PathInfo);
var itemId = pathInfo.GetArgumentValue<string>(1);
var playlistId = pathInfo.GetArgumentValue<string>(3);
OnEndRequest(itemId, playlistId);
}
public IHasResponseFilter Copy()
{
return this;
}
public int Priority
{
get { return -1; }
}
/// <summary>
/// Called when [end request].
/// </summary>
/// <param name="itemId">The item id.</param>
/// <param name="playlistId">The playlist id.</param>
protected void OnEndRequest(string itemId, string playlistId)
{
Logger.Info("OnEndRequest " + playlistId);
var normalizedPlaylistId = playlistId.Replace("-low", string.Empty);
foreach (var playlist in Directory.EnumerateFiles(ApplicationPaths.EncodedMediaCachePath, "*.m3u8")
.Where(i => i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1)
.ToList())
{
ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls);
}
}
}
}

View file

@ -0,0 +1,147 @@
using MediaBrowser.Controller;
using ServiceStack.ServiceHost;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace MediaBrowser.Api.Playback.Hls
{
/// <summary>
/// Class GetHlsAudioSegment
/// </summary>
[Route("/Audio/{Id}/hls/{SegmentId}/stream.mp3", "GET")]
[Route("/Audio/{Id}/hls/{SegmentId}/stream.aac", "GET")]
[Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
public class GetHlsAudioSegment
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
public string Id { get; set; }
/// <summary>
/// Gets or sets the segment id.
/// </summary>
/// <value>The segment id.</value>
public string SegmentId { get; set; }
}
/// <summary>
/// Class GetHlsVideoSegment
/// </summary>
[Route("/Videos/{Id}/hls/{PlaylistId}/{SegmentId}.ts", "GET")]
[Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
public class GetHlsVideoSegment
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
public string Id { get; set; }
public string PlaylistId { get; set; }
/// <summary>
/// Gets or sets the segment id.
/// </summary>
/// <value>The segment id.</value>
public string SegmentId { get; set; }
}
/// <summary>
/// Class GetHlsVideoSegment
/// </summary>
[Route("/Videos/{Id}/hls/{PlaylistId}/stream.m3u8", "GET")]
[Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
public class GetHlsPlaylist
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
public string Id { get; set; }
public string PlaylistId { get; set; }
}
public class HlsSegmentService : BaseApiService
{
private readonly IServerApplicationPaths _appPaths;
public HlsSegmentService(IServerApplicationPaths appPaths)
{
_appPaths = appPaths;
}
public object Get(GetHlsPlaylist request)
{
OnBeginRequest(request.PlaylistId);
var file = request.PlaylistId + Path.GetExtension(RequestContext.PathInfo);
file = Path.Combine(_appPaths.EncodedMediaCachePath, file);
return ResultFactory.GetStaticFileResult(RequestContext, file, FileShare.ReadWrite);
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetHlsVideoSegment request)
{
var file = request.SegmentId + Path.GetExtension(RequestContext.PathInfo);
file = Path.Combine(_appPaths.EncodedMediaCachePath, file);
OnBeginRequest(request.PlaylistId);
return ResultFactory.GetStaticFileResult(RequestContext, file);
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetHlsAudioSegment request)
{
var file = request.SegmentId + Path.GetExtension(RequestContext.PathInfo);
file = Path.Combine(_appPaths.EncodedMediaCachePath, file);
return ResultFactory.GetStaticFileResult(RequestContext, file, FileShare.ReadWrite);
}
/// <summary>
/// Called when [begin request].
/// </summary>
/// <param name="playlistId">The playlist id.</param>
protected void OnBeginRequest(string playlistId)
{
var normalizedPlaylistId = playlistId.Replace("-low", string.Empty);
foreach (var playlist in Directory.EnumerateFiles(_appPaths.EncodedMediaCachePath, "*.m3u8")
.Where(i => i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1)
.ToList())
{
ExtendPlaylistTimer(playlist);
}
}
private void ExtendPlaylistTimer(string playlist)
{
ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
Task.Run(async () =>
{
await Task.Delay(20000).ConfigureAwait(false);
ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls);
});
}
}
}

View file

@ -5,7 +5,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Model.IO;
using ServiceStack.ServiceHost;
using System;
using System.IO;
namespace MediaBrowser.Api.Playback.Hls
{
@ -31,44 +30,6 @@ namespace MediaBrowser.Api.Playback.Hls
}
}
/// <summary>
/// Class GetHlsVideoSegment
/// </summary>
[Route("/Videos/{Id}/hls/{PlaylistId}/{SegmentId}.ts", "GET")]
[Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
public class GetHlsVideoSegment
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
public string Id { get; set; }
public string PlaylistId { get; set; }
/// <summary>
/// Gets or sets the segment id.
/// </summary>
/// <value>The segment id.</value>
public string SegmentId { get; set; }
}
/// <summary>
/// Class GetHlsVideoSegment
/// </summary>
[Route("/Videos/{Id}/hls/{PlaylistId}/stream.m3u8", "GET")]
[Api(Description = "Gets an Http live streaming segment file. Internal use only.")]
public class GetHlsPlaylist
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
public string Id { get; set; }
public string PlaylistId { get; set; }
}
/// <summary>
/// Class VideoHlsService
/// </summary>
@ -82,38 +43,12 @@ namespace MediaBrowser.Api.Playback.Hls
/// <param name="libraryManager">The library manager.</param>
/// <param name="isoManager">The iso manager.</param>
/// <param name="mediaEncoder">The media encoder.</param>
/// <param name="dtoService">The dto service.</param>
public VideoHlsService(IServerApplicationPaths appPaths, IUserManager userManager, ILibraryManager libraryManager, IIsoManager isoManager, IMediaEncoder mediaEncoder, IDtoService dtoService)
: base(appPaths, userManager, libraryManager, isoManager, mediaEncoder, dtoService)
{
}
/// <summary>
/// Gets the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.Object.</returns>
public object Get(GetHlsVideoSegment request)
{
ExtendHlsTimer(request.Id, request.PlaylistId);
var file = request.SegmentId + Path.GetExtension(RequestContext.PathInfo);
file = Path.Combine(ApplicationPaths.EncodedMediaCachePath, file);
return ResultFactory.GetStaticFileResult(RequestContext, file);
}
public object Get(GetHlsPlaylist request)
{
ExtendHlsTimer(request.Id, request.PlaylistId);
var file = request.PlaylistId + Path.GetExtension(RequestContext.PathInfo);
file = Path.Combine(ApplicationPaths.EncodedMediaCachePath, file);
return ResultFactory.GetStaticFileResult(RequestContext, file, FileShare.ReadWrite);
}
/// <summary>
/// Gets the specified request.
/// </summary>

View file

@ -1,7 +1,5 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Session;
using ServiceStack.ServiceHost;
using System;
@ -189,6 +187,7 @@ namespace MediaBrowser.Api
/// Initializes a new instance of the <see cref="SessionsService" /> class.
/// </summary>
/// <param name="sessionManager">The session manager.</param>
/// <param name="dtoService">The dto service.</param>
public SessionsService(ISessionManager sessionManager, IDtoService dtoService)
{
_sessionManager = sessionManager;
@ -214,277 +213,82 @@ namespace MediaBrowser.Api
public void Post(SendPlaystateCommand request)
{
var task = SendPlaystateCommand(request);
var command = new PlaystateRequest
{
Command = request.Command,
SeekPositionTicks = request.SeekPositionTicks
};
var task = _sessionManager.SendPlaystateCommand(request.Id, command, CancellationToken.None);
Task.WaitAll(task);
}
private async Task SendPlaystateCommand(SendPlaystateCommand request)
{
var session = _sessionManager.Sessions.FirstOrDefault(i => i.Id == request.Id);
if (session == null)
{
throw new ResourceNotFoundException(string.Format("Session {0} not found.", request.Id));
}
if (!session.SupportsRemoteControl)
{
throw new ArgumentException(string.Format("Session {0} does not support remote control.", session.Id));
}
var socket = session.WebSockets.OrderByDescending(i => i.LastActivityDate).FirstOrDefault(i => i.State == WebSocketState.Open);
if (socket != null)
{
try
{
await socket.SendAsync(new WebSocketMessage<PlaystateRequest>
{
MessageType = "Playstate",
Data = new PlaystateRequest
{
Command = request.Command,
SeekPositionTicks = request.SeekPositionTicks
}
}, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.ErrorException("Error sending web socket message", ex);
}
}
else
{
throw new InvalidOperationException("The requested session does not have an open web socket.");
}
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public void Post(BrowseTo request)
{
var task = BrowseTo(request);
var command = new BrowseRequest
{
Context = request.Context,
ItemId = request.ItemId,
ItemName = request.ItemName,
ItemType = request.ItemType
};
var task = _sessionManager.SendBrowseCommand(request.Id, command, CancellationToken.None);
Task.WaitAll(task);
}
/// <summary>
/// Browses to.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>Task.</returns>
/// <exception cref="ResourceNotFoundException"></exception>
/// <exception cref="System.ArgumentException"></exception>
/// <exception cref="System.InvalidOperationException">The requested session does not have an open web socket.</exception>
private async Task BrowseTo(BrowseTo request)
{
var session = _sessionManager.Sessions.FirstOrDefault(i => i.Id == request.Id);
if (session == null)
{
throw new ResourceNotFoundException(string.Format("Session {0} not found.", request.Id));
}
if (!session.SupportsRemoteControl)
{
throw new ArgumentException(string.Format("Session {0} does not support remote control.", session.Id));
}
var socket = session.WebSockets.OrderByDescending(i => i.LastActivityDate).FirstOrDefault(i => i.State == WebSocketState.Open);
if (socket != null)
{
try
{
await socket.SendAsync(new WebSocketMessage<BrowseTo>
{
MessageType = "Browse",
Data = request
}, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.ErrorException("Error sending web socket message", ex);
}
}
else
{
throw new InvalidOperationException("The requested session does not have an open web socket.");
}
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public void Post(SendSystemCommand request)
{
var task = SendSystemCommand(request);
var task = _sessionManager.SendSystemCommand(request.Id, request.Command, CancellationToken.None);
Task.WaitAll(task);
}
private async Task SendSystemCommand(SendSystemCommand request)
{
var session = _sessionManager.Sessions.FirstOrDefault(i => i.Id == request.Id);
if (session == null)
{
throw new ResourceNotFoundException(string.Format("Session {0} not found.", request.Id));
}
if (!session.SupportsRemoteControl)
{
throw new ArgumentException(string.Format("Session {0} does not support remote control.", session.Id));
}
var socket = session.WebSockets.OrderByDescending(i => i.LastActivityDate).FirstOrDefault(i => i.State == WebSocketState.Open);
if (socket != null)
{
try
{
await socket.SendAsync(new WebSocketMessage<string>
{
MessageType = "SystemCommand",
Data = request.Command.ToString()
}, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.ErrorException("Error sending web socket message", ex);
}
}
else
{
throw new InvalidOperationException("The requested session does not have an open web socket.");
}
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public void Post(SendMessageCommand request)
{
var task = SendMessageCommand(request);
var command = new MessageCommand
{
Header = string.IsNullOrEmpty(request.Header) ? "Message from Server" : request.Header,
TimeoutMs = request.TimeoutMs,
Text = request.Text
};
var task = _sessionManager.SendMessageCommand(request.Id, command, CancellationToken.None);
Task.WaitAll(task);
}
private async Task SendMessageCommand(SendMessageCommand request)
{
var session = _sessionManager.Sessions.FirstOrDefault(i => i.Id == request.Id);
if (session == null)
{
throw new ResourceNotFoundException(string.Format("Session {0} not found.", request.Id));
}
if (!session.SupportsRemoteControl)
{
throw new ArgumentException(string.Format("Session {0} does not support remote control.", session.Id));
}
var socket = session.WebSockets.OrderByDescending(i => i.LastActivityDate).FirstOrDefault(i => i.State == WebSocketState.Open);
if (socket != null)
{
try
{
await socket.SendAsync(new WebSocketMessage<MessageCommand>
{
MessageType = "MessageCommand",
Data = new MessageCommand
{
Header = string.IsNullOrEmpty(request.Header) ? "Message from Server" : request.Header,
TimeoutMs = request.TimeoutMs,
Text = request.Text
}
}, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.ErrorException("Error sending web socket message", ex);
}
}
else
{
throw new InvalidOperationException("The requested session does not have an open web socket.");
}
}
/// <summary>
/// Posts the specified request.
/// </summary>
/// <param name="request">The request.</param>
public void Post(Play request)
{
var task = Play(request);
var command = new PlayRequest
{
ItemIds = request.ItemIds.Split(',').ToArray(),
PlayCommand = request.PlayCommand,
StartPositionTicks = request.StartPositionTicks
};
var task = _sessionManager.SendPlayCommand(request.Id, command, CancellationToken.None);
Task.WaitAll(task);
}
/// <summary>
/// Plays the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <returns>Task.</returns>
/// <exception cref="ResourceNotFoundException"></exception>
/// <exception cref="System.ArgumentException"></exception>
/// <exception cref="System.InvalidOperationException">The requested session does not have an open web socket.</exception>
private async Task Play(Play request)
{
var session = _sessionManager.Sessions.FirstOrDefault(i => i.Id == request.Id);
if (session == null)
{
throw new ResourceNotFoundException(string.Format("Session {0} not found.", request.Id));
}
if (!session.SupportsRemoteControl)
{
throw new ArgumentException(string.Format("Session {0} does not support remote control.", session.Id));
}
var socket = session.WebSockets.OrderByDescending(i => i.LastActivityDate).FirstOrDefault(i => i.State == WebSocketState.Open);
if (socket != null)
{
try
{
await socket.SendAsync(new WebSocketMessage<PlayRequest>
{
MessageType = "Play",
Data = new PlayRequest
{
ItemIds = request.ItemIds.Split(',').ToArray(),
PlayCommand = request.PlayCommand,
StartPositionTicks = request.StartPositionTicks
}
}, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.ErrorException("Error sending web socket message", ex);
}
}
else
{
throw new InvalidOperationException("The requested session does not have an open web socket.");
}
}
}
}

View file

@ -186,7 +186,7 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "DatePlayed", Description = "The date the item was played (if any)", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")]
public DateTime? DatePlayed { get; set; }
/// <summary>
/// Gets or sets the id.
/// </summary>
@ -224,6 +224,13 @@ namespace MediaBrowser.Api.UserLibrary
[Api(Description = "Reports that a user has begun playing an item")]
public class OnPlaybackStart : IReturnVoid
{
public OnPlaybackStart()
{
// Have to default these until all clients have a chance to incorporate them
CanSeek = true;
QueueableMediaTypes = "Audio,Video,Book,Game";
}
/// <summary>
/// Gets or sets the user id.
/// </summary>
@ -237,6 +244,20 @@ namespace MediaBrowser.Api.UserLibrary
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="UpdateUserItemRating" /> is likes.
/// </summary>
/// <value><c>true</c> if likes; otherwise, <c>false</c>.</value>
[ApiMember(Name = "CanSeek", Description = "Indicates if the client can seek", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "POST")]
public bool CanSeek { get; set; }
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "QueueableMediaTypes", Description = "A list of media types that can be queued from this item, comma delimited. Audio,Video,Book,Game", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST", AllowMultiple = true)]
public string QueueableMediaTypes { get; set; }
}
/// <summary>
@ -378,6 +399,8 @@ namespace MediaBrowser.Api.UserLibrary
/// <param name="libraryManager">The library manager.</param>
/// <param name="userDataRepository">The user data repository.</param>
/// <param name="itemRepo">The item repo.</param>
/// <param name="sessionManager">The session manager.</param>
/// <param name="dtoService">The dto service.</param>
/// <exception cref="System.ArgumentNullException">jsonSerializer</exception>
public UserLibraryService(IUserManager userManager, ILibraryManager libraryManager, IUserDataRepository userDataRepository, IItemRepository itemRepo, ISessionManager sessionManager, IDtoService dtoService)
{
@ -640,19 +663,11 @@ namespace MediaBrowser.Api.UserLibrary
private SessionInfo GetSession()
{
var auth = RequestFilterAttribute.GetAuthorization(RequestContext);
var auth = AuthorizationRequestFilterAttribute.GetAuthorization(RequestContext);
string deviceId;
string client;
string version;
auth.TryGetValue("DeviceId", out deviceId);
auth.TryGetValue("Client", out client);
auth.TryGetValue("Version", out version);
return _sessionManager.Sessions.First(i => string.Equals(i.DeviceId, deviceId) &&
string.Equals(i.Client, client) &&
string.Equals(i.ApplicationVersion, version));
return _sessionManager.Sessions.First(i => string.Equals(i.DeviceId, auth.DeviceId) &&
string.Equals(i.Client, auth.Client) &&
string.Equals(i.ApplicationVersion, auth.Version));
}
/// <summary>
@ -665,7 +680,17 @@ namespace MediaBrowser.Api.UserLibrary
var item = _dtoService.GetItemByDtoId(request.Id, user.Id);
_sessionManager.OnPlaybackStart(item, GetSession().Id);
var queueableMediaTypes = (request.QueueableMediaTypes ?? string.Empty);
var info = new PlaybackInfo
{
CanSeek = request.CanSeek,
Item = item,
SessionId = GetSession().Id,
QueueableMediaTypes = queueableMediaTypes.Split(',').ToList()
};
_sessionManager.OnPlaybackStart(info);
}
/// <summary>
@ -693,7 +718,12 @@ namespace MediaBrowser.Api.UserLibrary
var item = _dtoService.GetItemByDtoId(request.Id, user.Id);
var task = _sessionManager.OnPlaybackStopped(item, request.PositionTicks, GetSession().Id);
// Kill the encoding
ApiEntryPoint.Instance.KillSingleTranscodingJob(item.Path);
var session = GetSession();
var task = _sessionManager.OnPlaybackStopped(item, request.PositionTicks, session.Id);
Task.WaitAll(task);
}

View file

@ -0,0 +1,87 @@
using MediaBrowser.Model.IO;
using SharpCompress.Archive.SevenZip;
using SharpCompress.Common;
using SharpCompress.Reader;
using System.IO;
namespace MediaBrowser.Common.Implementations.Archiving
{
/// <summary>
/// Class DotNetZipClient
/// </summary>
public class ZipClient : IZipClient
{
/// <summary>
/// Extracts all.
/// </summary>
/// <param name="sourceFile">The source file.</param>
/// <param name="targetPath">The target path.</param>
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAll(string sourceFile, string targetPath, bool overwriteExistingFiles)
{
using (var fileStream = File.OpenRead(sourceFile))
{
ExtractAll(fileStream, targetPath, overwriteExistingFiles);
}
}
/// <summary>
/// Extracts all.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="targetPath">The target path.</param>
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAll(Stream source, string targetPath, bool overwriteExistingFiles)
{
using (var reader = ReaderFactory.Open(source))
{
var options = ExtractOptions.ExtractFullPath;
if (overwriteExistingFiles)
{
options = options | ExtractOptions.Overwrite;
}
reader.WriteAllToDirectory(targetPath, options);
}
}
/// <summary>
/// Extracts all from7z.
/// </summary>
/// <param name="sourceFile">The source file.</param>
/// <param name="targetPath">The target path.</param>
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAllFrom7z(string sourceFile, string targetPath, bool overwriteExistingFiles)
{
using (var fileStream = File.OpenRead(sourceFile))
{
ExtractAllFrom7z(fileStream, targetPath, overwriteExistingFiles);
}
}
/// <summary>
/// Extracts all from7z.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="targetPath">The target path.</param>
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAllFrom7z(Stream source, string targetPath, bool overwriteExistingFiles)
{
using (var archive = SevenZipArchive.Open(source))
{
using (var reader = archive.ExtractAllEntries())
{
var options = ExtractOptions.ExtractFullPath;
if (overwriteExistingFiles)
{
options = options | ExtractOptions.Overwrite;
}
reader.WriteAllToDirectory(targetPath, options);
}
}
}
}
}

View file

@ -1,5 +1,6 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Implementations.Archiving;
using MediaBrowser.Common.Implementations.NetworkManagement;
using MediaBrowser.Common.Implementations.ScheduledTasks;
using MediaBrowser.Common.Implementations.Security;
@ -10,6 +11,7 @@ using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Common.Security;
using MediaBrowser.Common.Updates;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Updates;
@ -149,6 +151,12 @@ namespace MediaBrowser.Common.Implementations
/// <value>The installation manager.</value>
protected IInstallationManager InstallationManager { get; set; }
/// <summary>
/// Gets or sets the zip client.
/// </summary>
/// <value>The zip client.</value>
protected IZipClient ZipClient { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="BaseApplicationHost{TApplicationPathsType}"/> class.
/// </summary>
@ -202,12 +210,27 @@ namespace MediaBrowser.Common.Implementations
{
Resolve<ITaskManager>().AddTasks(GetExports<IScheduledTask>(false));
Task.Run(() => ConfigureAutoRunAtStartup());
Task.Run(() => ConfigureAutorun());
ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
});
}
/// <summary>
/// Configures the autorun.
/// </summary>
private void ConfigureAutorun()
{
try
{
ConfigureAutoRunAtStartup(ConfigurationManager.CommonConfiguration.RunAtStartup);
}
catch (Exception ex)
{
Logger.ErrorException("Error configuring autorun", ex);
}
}
/// <summary>
/// Gets the composable part assemblies.
/// </summary>
@ -281,6 +304,9 @@ namespace MediaBrowser.Common.Implementations
InstallationManager = new InstallationManager(Logger, this, ApplicationPaths, HttpClient, JsonSerializer, SecurityManager, NetworkManager, ConfigurationManager);
RegisterSingleInstance(InstallationManager);
ZipClient = new ZipClient();
RegisterSingleInstance(ZipClient);
});
}
@ -453,11 +479,6 @@ namespace MediaBrowser.Common.Implementations
}
}
/// <summary>
/// Defines the full path to our shortcut in the start menu
/// </summary>
protected abstract string ProductShortcutPath { get; }
/// <summary>
/// Handles the ConfigurationUpdated event of the ConfigurationManager control.
/// </summary>
@ -466,32 +487,10 @@ namespace MediaBrowser.Common.Implementations
/// <exception cref="System.NotImplementedException"></exception>
protected virtual void OnConfigurationUpdated(object sender, EventArgs e)
{
ConfigureAutoRunAtStartup();
ConfigureAutorun();
}
/// <summary>
/// Configures the auto run at startup.
/// </summary>
private void ConfigureAutoRunAtStartup()
{
if (ConfigurationManager.CommonConfiguration.RunAtStartup)
{
//Copy our shortut into the startup folder for this user
File.Copy(ProductShortcutPath, Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), Path.GetFileName(ProductShortcutPath) ?? "MBstartup.lnk"), true);
}
else
{
//Remove our shortcut from the startup folder for this user
try
{
File.Delete(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), Path.GetFileName(ProductShortcutPath) ?? "MBstartup.lnk"));
}
catch (FileNotFoundException)
{
//This is okay - trying to remove it anyway
}
}
}
protected abstract void ConfigureAutoRunAtStartup(bool autorun);
/// <summary>
/// Removes the plugin.

View file

@ -5,7 +5,6 @@ using NLog.Targets;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace MediaBrowser.Common.Implementations.Logging
{
@ -193,17 +192,14 @@ namespace MediaBrowser.Common.Implementations.Logging
if (LoggerLoaded != null)
{
Task.Run(() =>
try
{
try
{
LoggerLoaded(this, EventArgs.Empty);
}
catch (Exception ex)
{
GetLogger("Logger").ErrorException("Error in LoggerLoaded event", ex);
}
});
LoggerLoaded(this, EventArgs.Empty);
}
catch (Exception ex)
{
GetLogger("Logger").ErrorException("Error in LoggerLoaded event", ex);
}
}
}
}

View file

@ -13,6 +13,8 @@
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -35,17 +37,8 @@
<RunPostBuildEvent>Always</RunPostBuildEvent>
</PropertyGroup>
<ItemGroup>
<Reference Include="NLog, Version=2.0.1.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NLog.2.0.1.2\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Text, Version=3.9.59.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll</HintPath>
</Reference>
<Reference Include="SimpleInjector, Version=2.3.5.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\SimpleInjector.2.3.5\lib\net40-client\SimpleInjector.dll</HintPath>
<Reference Include="SharpCompress">
<HintPath>..\packages\sharpcompress.0.10.1.3\lib\net40\SharpCompress.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
@ -54,11 +47,21 @@
<Reference Include="System.Net" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="NLog">
<HintPath>..\packages\NLog.2.0.1.2\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Text">
<HintPath>..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll</HintPath>
</Reference>
<Reference Include="SimpleInjector">
<HintPath>..\packages\SimpleInjector.2.3.5\lib\net40-client\SimpleInjector.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
<Compile Include="Archiving\ZipClient.cs" />
<Compile Include="BaseApplicationHost.cs" />
<Compile Include="BaseApplicationPaths.cs" />
<Compile Include="Configuration\BaseConfigurationManager.cs" />
@ -88,11 +91,11 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
<Project>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</Project>
<Name>MediaBrowser.Common</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
<Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
<Name>MediaBrowser.Model</Name>
</ProjectReference>
</ItemGroup>

View file

@ -1,5 +1,4 @@
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Model.Logging;
@ -8,6 +7,7 @@ using MediaBrowser.Model.Tasks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MediaBrowser.Common.Implementations.ScheduledTasks
{
@ -77,6 +77,17 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
QueueScheduledTask<T>();
}
/// <summary>
/// Cancels if running
/// </summary>
/// <typeparam name="T"></typeparam>
public void CancelIfRunning<T>()
where T : IScheduledTask
{
var task = ScheduledTasks.First(t => t.ScheduledTask.GetType() == typeof(T));
((ScheduledTaskWorker)task).CancelIfRunning();
}
/// <summary>
/// Queues the scheduled task.
/// </summary>

View file

@ -54,33 +54,32 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
/// <returns>Task.</returns>
public Task Execute(CancellationToken cancellationToken, IProgress<double> progress)
{
return Task.Run(() =>
// Delete log files more than n days old
var minDateModified = DateTime.UtcNow.AddDays(-(ConfigurationManager.CommonConfiguration.LogFileRetentionDays));
var filesToDelete = new DirectoryInfo(ConfigurationManager.CommonApplicationPaths.LogDirectoryPath).EnumerateFileSystemInfos("*", SearchOption.AllDirectories)
.Where(f => f.LastWriteTimeUtc < minDateModified)
.ToList();
var index = 0;
foreach (var file in filesToDelete)
{
// Delete log files more than n days old
var minDateModified = DateTime.UtcNow.AddDays(-(ConfigurationManager.CommonConfiguration.LogFileRetentionDays));
double percent = index;
percent /= filesToDelete.Count;
var filesToDelete = new DirectoryInfo(ConfigurationManager.CommonApplicationPaths.LogDirectoryPath).EnumerateFileSystemInfos("*", SearchOption.AllDirectories)
.Where(f => f.LastWriteTimeUtc < minDateModified)
.ToList();
progress.Report(100 * percent);
var index = 0;
cancellationToken.ThrowIfCancellationRequested();
foreach (var file in filesToDelete)
{
double percent = index;
percent /= filesToDelete.Count;
File.Delete(file.FullName);
progress.Report(100 * percent);
index++;
}
cancellationToken.ThrowIfCancellationRequested();
progress.Report(100);
File.Delete(file.FullName);
index++;
}
progress.Report(100);
});
return Task.FromResult(true);
}
/// <summary>

View file

@ -58,7 +58,11 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks.Tasks
progress.Report(0);
return Task.Run(() => LogManager.ReloadLogger(ConfigurationManager.CommonConfiguration.EnableDebugLevelLogging ? LogSeverity.Debug : LogSeverity.Info));
LogManager.ReloadLogger(ConfigurationManager.CommonConfiguration.EnableDebugLevelLogging
? LogSeverity.Debug
: LogSeverity.Info);
return Task.FromResult(true);
}
/// <summary>

View file

@ -2,5 +2,6 @@
<packages>
<package id="NLog" version="2.0.1.2" targetFramework="net45" />
<package id="ServiceStack.Text" version="3.9.62" targetFramework="net45" />
<package id="sharpcompress" version="0.10.1.3" targetFramework="net45" />
<package id="SimpleInjector" version="2.3.5" targetFramework="net45" />
</packages>

View file

@ -13,6 +13,8 @@
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -32,26 +34,19 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>
</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="ServiceStack.Common, Version=3.9.60.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Interfaces, Version=3.9.60.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Text, Version=3.9.59.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="ServiceStack.Common">
<HintPath>..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Interfaces">
<HintPath>..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Text">
<HintPath>..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\SharedVersion.cs">
@ -113,7 +108,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
<Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
<Name>MediaBrowser.Model</Name>
</ProjectReference>
</ItemGroup>

View file

@ -21,6 +21,13 @@ namespace MediaBrowser.Common.ScheduledTasks
void CancelIfRunningAndQueue<T>()
where T : IScheduledTask;
/// <summary>
/// Cancels if running.
/// </summary>
/// <typeparam name="T"></typeparam>
void CancelIfRunning<T>()
where T : IScheduledTask;
/// <summary>
/// Queues the scheduled task.
/// </summary>

View file

@ -16,7 +16,6 @@ using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using MoreLinq;
namespace MediaBrowser.Controller.Entities
{
@ -171,6 +170,25 @@ namespace MediaBrowser.Controller.Entities
return ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken);
}
/// <summary>
/// Clears the children.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public Task ClearChildren(CancellationToken cancellationToken)
{
var items = ActualChildren.ToList();
ClearChildrenInternal();
foreach (var item in items)
{
LibraryManager.ReportItemRemoved(item);
}
return ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken);
}
#region Indexing
/// <summary>
@ -671,7 +689,7 @@ namespace MediaBrowser.Controller.Entities
var options = new ParallelOptions
{
MaxDegreeOfParallelism = 20
MaxDegreeOfParallelism = 10
};
Parallel.ForEach(nonCachedChildren, options, child =>
@ -733,6 +751,11 @@ namespace MediaBrowser.Controller.Entities
if (actualRemovals.Count > 0)
{
RemoveChildrenInternal(actualRemovals);
foreach (var item in actualRemovals)
{
LibraryManager.ReportItemRemoved(item);
}
}
await LibraryManager.CreateItems(newItems, cancellationToken).ConfigureAwait(false);
@ -781,7 +804,7 @@ namespace MediaBrowser.Controller.Entities
foreach (var tuple in list)
{
if (tasks.Count > 8)
if (tasks.Count > 5)
{
await Task.WhenAll(tasks).ConfigureAwait(false);
}

View file

@ -13,6 +13,8 @@
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -42,6 +44,8 @@
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<WarningLevel>4</WarningLevel>
<Optimize>false</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
@ -51,12 +55,9 @@
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="MoreLinq, Version=1.0.16006.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
@ -66,6 +67,9 @@
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="MoreLinq">
<HintPath>..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\SharedVersion.cs">
@ -165,6 +169,8 @@
<Compile Include="Kernel.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Providers\BaseMetadataProvider.cs" />
<Compile Include="Session\ISessionRemoteController.cs" />
<Compile Include="Session\PlaybackInfo.cs" />
<Compile Include="Session\SessionInfo.cs" />
<Compile Include="Sorting\IBaseItemComparer.cs" />
<Compile Include="Sorting\IUserBaseItemComparer.cs" />
@ -172,11 +178,11 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
<Project>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</Project>
<Name>MediaBrowser.Common</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
<Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
<Name>MediaBrowser.Model</Name>
</ProjectReference>
</ItemGroup>

View file

@ -385,7 +385,7 @@ namespace MediaBrowser.Controller.Providers
var sb = new StringBuilder();
var extensions = FileStampExtensionsDictionary;
var numExtensions = extensions.Count;
var numExtensions = FilestampExtensions.Length;
// Record the name of each file
// Need to sort these because accoring to msdn docs, our i/o methods are not guaranteed in any order

View file

@ -1,7 +1,9 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Session;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Session
@ -11,6 +13,12 @@ namespace MediaBrowser.Controller.Session
/// </summary>
public interface ISessionManager
{
/// <summary>
/// Adds the parts.
/// </summary>
/// <param name="remoteControllers">The remote controllers.</param>
void AddParts(IEnumerable<ISessionRemoteController> remoteControllers);
/// <summary>
/// Occurs when [playback start].
/// </summary>
@ -47,11 +55,9 @@ namespace MediaBrowser.Controller.Session
/// <summary>
/// Used to report that playback has started for an item
/// </summary>
/// <param name="item">The item.</param>
/// <param name="sessionId">The session id.</param>
/// <param name="info">The info.</param>
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
Task OnPlaybackStart(BaseItem item, Guid sessionId);
Task OnPlaybackStart(PlaybackInfo info);
/// <summary>
/// Used to report playback progress for an item
@ -59,6 +65,7 @@ namespace MediaBrowser.Controller.Session
/// <param name="item">The item.</param>
/// <param name="positionTicks">The position ticks.</param>
/// <param name="isPaused">if set to <c>true</c> [is paused].</param>
/// <param name="isMuted">if set to <c>true</c> [is muted].</param>
/// <param name="sessionId">The session id.</param>
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
@ -73,5 +80,50 @@ namespace MediaBrowser.Controller.Session
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
Task OnPlaybackStopped(BaseItem item, long? positionTicks, Guid sessionId);
/// <summary>
/// Sends the system command.
/// </summary>
/// <param name="sessionId">The session id.</param>
/// <param name="command">The command.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SendSystemCommand(Guid sessionId, SystemCommand command, CancellationToken cancellationToken);
/// <summary>
/// Sends the message command.
/// </summary>
/// <param name="sessionId">The session id.</param>
/// <param name="command">The command.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SendMessageCommand(Guid sessionId, MessageCommand command, CancellationToken cancellationToken);
/// <summary>
/// Sends the play command.
/// </summary>
/// <param name="sessionId">The session id.</param>
/// <param name="command">The command.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SendPlayCommand(Guid sessionId, PlayRequest command, CancellationToken cancellationToken);
/// <summary>
/// Sends the browse command.
/// </summary>
/// <param name="sessionId">The session id.</param>
/// <param name="command">The command.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SendBrowseCommand(Guid sessionId, BrowseRequest command, CancellationToken cancellationToken);
/// <summary>
/// Sends the playstate command.
/// </summary>
/// <param name="sessionId">The session id.</param>
/// <param name="command">The command.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SendPlaystateCommand(Guid sessionId, PlaystateRequest command, CancellationToken cancellationToken);
}
}

View file

@ -0,0 +1,61 @@
using MediaBrowser.Model.Session;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Session
{
public interface ISessionRemoteController
{
/// <summary>
/// Supportses the specified session.
/// </summary>
/// <param name="session">The session.</param>
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
bool Supports(SessionInfo session);
/// <summary>
/// Sends the system command.
/// </summary>
/// <param name="session">The session.</param>
/// <param name="command">The command.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SendSystemCommand(SessionInfo session, SystemCommand command, CancellationToken cancellationToken);
/// <summary>
/// Sends the message command.
/// </summary>
/// <param name="session">The session.</param>
/// <param name="command">The command.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SendMessageCommand(SessionInfo session, MessageCommand command, CancellationToken cancellationToken);
/// <summary>
/// Sends the play command.
/// </summary>
/// <param name="session">The session.</param>
/// <param name="command">The command.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SendPlayCommand(SessionInfo session, PlayRequest command, CancellationToken cancellationToken);
/// <summary>
/// Sends the browse command.
/// </summary>
/// <param name="session">The session.</param>
/// <param name="command">The command.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SendBrowseCommand(SessionInfo session, BrowseRequest command, CancellationToken cancellationToken);
/// <summary>
/// Sends the playstate command.
/// </summary>
/// <param name="session">The session.</param>
/// <param name="command">The command.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SendPlaystateCommand(SessionInfo session, PlaystateRequest command, CancellationToken cancellationToken);
}
}

View file

@ -0,0 +1,38 @@
using MediaBrowser.Controller.Entities;
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Session
{
public class PlaybackInfo
{
public PlaybackInfo()
{
QueueableMediaTypes = new List<string>();
}
/// <summary>
/// Gets or sets a value indicating whether this instance can seek.
/// </summary>
/// <value><c>true</c> if this instance can seek; otherwise, <c>false</c>.</value>
public bool CanSeek { get; set; }
/// <summary>
/// Gets or sets the queueable media types.
/// </summary>
/// <value>The queueable media types.</value>
public List<string> QueueableMediaTypes { get; set; }
/// <summary>
/// Gets or sets the item.
/// </summary>
/// <value>The item.</value>
public BaseItem Item { get; set; }
/// <summary>
/// Gets or sets the session id.
/// </summary>
/// <value>The session id.</value>
public Guid SessionId { get; set; }
}
}

View file

@ -15,8 +15,21 @@ namespace MediaBrowser.Controller.Session
public SessionInfo()
{
WebSockets = new List<IWebSocketConnection>();
QueueableMediaTypes = new List<string>();
}
/// <summary>
/// Gets or sets a value indicating whether this instance can seek.
/// </summary>
/// <value><c>true</c> if this instance can seek; otherwise, <c>false</c>.</value>
public bool CanSeek { get; set; }
/// <summary>
/// Gets or sets the queueable media types.
/// </summary>
/// <value>The queueable media types.</value>
public List<string> QueueableMediaTypes { get; set; }
/// <summary>
/// Gets or sets the id.
/// </summary>
@ -70,7 +83,7 @@ namespace MediaBrowser.Controller.Session
/// </summary>
/// <value>The name of the now viewing item.</value>
public string NowViewingItemName { get; set; }
/// <summary>
/// Gets or sets the now playing item.
/// </summary>
@ -94,7 +107,7 @@ namespace MediaBrowser.Controller.Session
/// </summary>
/// <value><c>true</c> if this instance is muted; otherwise, <c>false</c>.</value>
public bool IsMuted { get; set; }
/// <summary>
/// Gets or sets the device id.
/// </summary>
@ -126,7 +139,7 @@ namespace MediaBrowser.Controller.Session
return WebSockets.Any(i => i.State == WebSocketState.Open);
}
return (DateTime.UtcNow - LastActivityDate).TotalMinutes <= 5;
return (DateTime.UtcNow - LastActivityDate).TotalMinutes <= 10;
}
}

View file

@ -497,9 +497,11 @@ namespace MediaBrowser.Model.ApiClient
/// </summary>
/// <param name="itemId">The item id.</param>
/// <param name="userId">The user id.</param>
/// <param name="isSeekable">if set to <c>true</c> [is seekable].</param>
/// <param name="queueableMediaTypes">The list of media types that the client is capable of queuing onto the playlist. See MediaType class.</param>
/// <returns>Task{UserItemDataDto}.</returns>
/// <exception cref="ArgumentNullException">itemId</exception>
Task ReportPlaybackStartAsync(string itemId, string userId);
Task ReportPlaybackStartAsync(string itemId, string userId, bool isSeekable, List<string> queueableMediaTypes);
/// <summary>
/// Reports playback progress to the server

View file

@ -22,5 +22,21 @@ namespace MediaBrowser.Model.IO
/// <param name="targetPath">The target path.</param>
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
void ExtractAll(Stream source, string targetPath, bool overwriteExistingFiles);
/// <summary>
/// Extracts all from7z.
/// </summary>
/// <param name="sourceFile">The source file.</param>
/// <param name="targetPath">The target path.</param>
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
void ExtractAllFrom7z(string sourceFile, string targetPath, bool overwriteExistingFiles);
/// <summary>
/// Extracts all from7z.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="targetPath">The target path.</param>
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
void ExtractAllFrom7z(Stream source, string targetPath, bool overwriteExistingFiles);
}
}

View file

@ -14,6 +14,8 @@
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<FodyPath>..\packages\Fody.1.17.0.0</FodyPath>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -162,14 +164,13 @@
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="PropertyChanged, Version=1.41.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\PropertyChanged.Fody.1.41.0.0\Lib\NET35\PropertyChanged.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="PropertyChanged">
<HintPath>..\packages\PropertyChanged.Fody.1.41.0.0\Lib\NET35\PropertyChanged.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>

View file

@ -1,11 +1,24 @@
using System.ComponentModel;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using System.ComponentModel;
namespace MediaBrowser.Model.Session
{
public class SessionInfoDto : INotifyPropertyChanged
{
/// <summary>
/// Gets or sets a value indicating whether this instance can seek.
/// </summary>
/// <value><c>true</c> if this instance can seek; otherwise, <c>false</c>.</value>
public bool CanSeek { get; set; }
/// <summary>
/// Gets or sets the queueable media types.
/// </summary>
/// <value>The queueable media types.</value>
public List<string> QueueableMediaTypes { get; set; }
/// <summary>
/// Gets or sets the id.
/// </summary>

68
MediaBrowser.Mono.sln Normal file
View file

@ -0,0 +1,68 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Model", "MediaBrowser.Model\MediaBrowser.Model.csproj", "{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Common", "MediaBrowser.Common\MediaBrowser.Common.csproj", "{9142EEFA-7570-41E1-BFCC-468BB571AF2F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Common.Implementations", "MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj", "{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Controller", "MediaBrowser.Controller\MediaBrowser.Controller.csproj", "{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Providers", "MediaBrowser.Providers\MediaBrowser.Providers.csproj", "{442B5058-DCAF-4263-BB6A-F21E31120A1B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Server.Implementations", "MediaBrowser.Server.Implementations\MediaBrowser.Server.Implementations.csproj", "{2E781478-814D-4A48-9D80-BFF206441A65}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.WebDashboard", "MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj", "{5624B7B5-B5A7-41D8-9F10-CC5611109619}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Api", "MediaBrowser.Api\MediaBrowser.Api.csproj", "{4FD51AC5-2C16-4308-A993-C3A84F3B4582}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MediaBrowser.Server.Mono", "MediaBrowser.Server.Mono\MediaBrowser.Server.Mono.csproj", "{A7FE75CD-3CB4-4E71-A5BF-5347721EC8E0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|x86.ActiveCfg = Debug|x86
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Debug|x86.Build.0 = Debug|x86
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release|x86.ActiveCfg = Release|x86
{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}.Release|x86.Build.0 = Release|x86
{2E781478-814D-4A48-9D80-BFF206441A65}.Debug|x86.ActiveCfg = Debug|Any CPU
{2E781478-814D-4A48-9D80-BFF206441A65}.Debug|x86.Build.0 = Debug|Any CPU
{2E781478-814D-4A48-9D80-BFF206441A65}.Release|x86.ActiveCfg = Release|Any CPU
{2E781478-814D-4A48-9D80-BFF206441A65}.Release|x86.Build.0 = Release|Any CPU
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Debug|x86.ActiveCfg = Debug|Any CPU
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Debug|x86.Build.0 = Debug|Any CPU
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release|x86.ActiveCfg = Release|Any CPU
{442B5058-DCAF-4263-BB6A-F21E31120A1B}.Release|x86.Build.0 = Release|Any CPU
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Debug|x86.ActiveCfg = Debug|Any CPU
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Debug|x86.Build.0 = Debug|Any CPU
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release|x86.ActiveCfg = Release|Any CPU
{4FD51AC5-2C16-4308-A993-C3A84F3B4582}.Release|x86.Build.0 = Release|Any CPU
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Debug|x86.ActiveCfg = Debug|Any CPU
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Debug|x86.Build.0 = Debug|Any CPU
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release|x86.ActiveCfg = Release|Any CPU
{5624B7B5-B5A7-41D8-9F10-CC5611109619}.Release|x86.Build.0 = Release|Any CPU
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Debug|x86.ActiveCfg = Debug|Any CPU
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Debug|x86.Build.0 = Debug|Any CPU
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release|x86.ActiveCfg = Release|Any CPU
{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}.Release|x86.Build.0 = Release|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Debug|x86.ActiveCfg = Debug|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Debug|x86.Build.0 = Debug|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release|x86.ActiveCfg = Release|Any CPU
{9142EEFA-7570-41E1-BFCC-468BB571AF2F}.Release|x86.Build.0 = Release|Any CPU
{A7FE75CD-3CB4-4E71-A5BF-5347721EC8E0}.Debug|x86.ActiveCfg = Debug|x86
{A7FE75CD-3CB4-4E71-A5BF-5347721EC8E0}.Debug|x86.Build.0 = Debug|x86
{A7FE75CD-3CB4-4E71-A5BF-5347721EC8E0}.Release|x86.ActiveCfg = Release|x86
{A7FE75CD-3CB4-4E71-A5BF-5347721EC8E0}.Release|x86.Build.0 = Release|x86
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|x86.ActiveCfg = Debug|Any CPU
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Debug|x86.Build.0 = Debug|Any CPU
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|x86.ActiveCfg = Release|Any CPU
{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
StartupItem = MediaBrowser.Server.Mono\MediaBrowser.Server.Mono.csproj
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,19 @@
<Properties>
<MonoDevelop.Ide.Workspace ActiveConfiguration="Debug|x86" />
<MonoDevelop.Ide.Workbench ActiveDocument="d:\Development\MediaBrowser\MediaBrowser.Server.Mono\Native\Sqlite.cs">
<Files>
<File FileName="MediaBrowser.Server.Mono\Program.cs" Line="1" Column="1" />
<File FileName="MediaBrowser.Server.Mono\MainWindow.cs" Line="8" Column="12" />
<File FileName="MediaBrowser.Server.Mono\Native\Autorun.cs" Line="17" Column="4" />
<File FileName="MediaBrowser.Server.Mono\Native\ServerAuthorization.cs" Line="23" Column="1" />
<File FileName="MediaBrowser.Server.Mono\FFMpeg\FFMpegDownloader.cs" Line="7" Column="14" />
<File FileName="MediaBrowser.ServerApplication\ApplicationHost.cs" Line="548" Column="61" />
<File FileName="MediaBrowser.Server.Mono\Native\NativeApp.cs" Line="22" Column="13" />
<File FileName="d:\Development\MediaBrowser\MediaBrowser.Server.Mono\Native\Sqlite.cs" Line="21" Column="14" />
</Files>
</MonoDevelop.Ide.Workbench>
<MonoDevelop.Ide.DebuggingService.Breakpoints>
<BreakpointStore />
</MonoDevelop.Ide.DebuggingService.Breakpoints>
<MonoDevelop.Ide.DebuggingService.PinnedWatches />
</Properties>

View file

@ -13,6 +13,8 @@
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -32,10 +34,6 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="MoreLinq, Version=1.0.16006.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Net" />
@ -44,6 +42,9 @@
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="MoreLinq">
<HintPath>..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Extensions\XDocumentExtensions.cs" />
@ -116,15 +117,15 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
<Project>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</Project>
<Name>MediaBrowser.Common</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj">
<Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
<Project>{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}</Project>
<Name>MediaBrowser.Controller</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
<Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
<Name>MediaBrowser.Model</Name>
</ProjectReference>
</ItemGroup>

View file

@ -178,6 +178,7 @@ namespace MediaBrowser.Providers.MediaInfo
}
}
SetLastRefreshed(item, DateTime.UtcNow);
return true;
}

View file

@ -1,5 +1,4 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;

View file

@ -23,7 +23,9 @@ namespace MediaBrowser.Providers.Music
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
return Task.Run(() => RunInternal(progress, cancellationToken));
RunInternal(progress, cancellationToken);
return Task.FromResult(true);
}
private void RunInternal(IProgress<double> progress, CancellationToken cancellationToken)

View file

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Configuration;
using System.Collections.Generic;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
@ -58,7 +59,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
XmlSaverHelpers.Save(builder, xmlFilePath, new string[] { });
XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> { });
// Set last refreshed so that the provider doesn't trigger after the file save
PersonProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow);

View file

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Configuration;
using System.Collections.Generic;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
@ -70,7 +71,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
XmlSaverHelpers.Save(builder, xmlFilePath, new string[] { });
XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> { });
// Set last refreshed so that the provider doesn't trigger after the file save
ArtistProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow);

View file

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Configuration;
using System.Collections.Generic;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
@ -57,7 +58,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
XmlSaverHelpers.Save(builder, xmlFilePath, new string[] { });
XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> { });
BoxSetProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow);
}

View file

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Configuration;
using System.Collections.Generic;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@ -87,7 +88,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
XmlSaverHelpers.Save(builder, xmlFilePath, new[]
XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
{
"FirstAired",
"SeasonNumber",

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@ -77,7 +78,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
XmlSaverHelpers.Save(builder, xmlFilePath, new string[] { });
XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> { });
FolderProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow);
}

View file

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Configuration;
using System.Collections.Generic;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Providers.Movies;
@ -66,7 +67,7 @@ namespace MediaBrowser.Providers.Savers
if (!string.IsNullOrEmpty(game.GameSystem))
{
builder.Append("<GameSystem><![CDATA[" + game.GameSystem + "]]></GameSystem>");
builder.Append("<GameSystem>" + SecurityElement.Escape(game.GameSystem) + "</GameSystem>");
}
XmlSaverHelpers.AddCommonNodes(item, builder);
@ -75,7 +76,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
XmlSaverHelpers.Save(builder, xmlFilePath, new[]
XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
{
"Players",
"GameSystem"

View file

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Configuration;
using System.Collections.Generic;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
@ -103,7 +104,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
XmlSaverHelpers.Save(builder, xmlFilePath, new[]
XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
{
"IMDBrating",
"Description",

View file

@ -1,4 +1,5 @@
using System.Security;
using System.Collections.Generic;
using System.Security;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Providers.Movies;
@ -57,7 +58,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
XmlSaverHelpers.Save(builder, xmlFilePath, new[]
XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
{
"PlaceOfBirth"
});

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
@ -57,7 +58,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
XmlSaverHelpers.Save(builder, xmlFilePath, new string[] { });
XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> { });
SeasonProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow);
}

View file

@ -1,4 +1,5 @@
using MediaBrowser.Controller.Configuration;
using System.Collections.Generic;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@ -105,7 +106,7 @@ namespace MediaBrowser.Providers.Savers
var xmlFilePath = GetSavePath(item);
XmlSaverHelpers.Save(builder, xmlFilePath, new[]
XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
{
"id",
"SeriesName",
@ -113,7 +114,10 @@ namespace MediaBrowser.Providers.Savers
"Network",
"Airs_Time",
"Airs_DayOfWeek",
"FirstAired"
"FirstAired",
// Don't preserve old series node
"Series"
});
// Set last refreshed so that the provider doesn't trigger after the file save

View file

@ -29,13 +29,11 @@ namespace MediaBrowser.Providers.Savers
/// <param name="xml">The XML.</param>
/// <param name="path">The path.</param>
/// <param name="xmlTagsUsed">The XML tags used.</param>
public static void Save(StringBuilder xml, string path, IEnumerable<string> xmlTagsUsed)
public static void Save(StringBuilder xml, string path, List<string> xmlTagsUsed)
{
if (File.Exists(path))
{
var tags = xmlTagsUsed.ToList();
tags.AddRange(new[]
xmlTagsUsed.AddRange(new[]
{
"MediaInfo",
"ContentRating",
@ -88,7 +86,7 @@ namespace MediaBrowser.Providers.Savers
});
var position = xml.ToString().LastIndexOf("</", StringComparison.OrdinalIgnoreCase);
xml.Insert(position, GetCustomTags(path, tags));
xml.Insert(position, GetCustomTags(path, xmlTagsUsed));
}
var xmlDocument = new XmlDocument();
@ -142,17 +140,46 @@ namespace MediaBrowser.Providers.Savers
/// <param name="path">The path.</param>
/// <param name="xmlTagsUsed">The XML tags used.</param>
/// <returns>System.String.</returns>
private static string GetCustomTags(string path, ICollection<string> xmlTagsUsed)
private static string GetCustomTags(string path, IEnumerable<string> xmlTagsUsed)
{
var doc = new XmlDocument();
doc.Load(path);
var settings = new XmlReaderSettings
{
CheckCharacters = false,
IgnoreProcessingInstructions = true,
IgnoreComments = true,
ValidationType = ValidationType.None
};
var nodes = doc.DocumentElement.ChildNodes.Cast<XmlNode>()
.Where(i => !xmlTagsUsed.Contains(i.Name))
.Select(i => i.OuterXml)
.ToArray();
var tagsDictionary = xmlTagsUsed.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
return string.Join(Environment.NewLine, nodes);
var builder = new StringBuilder();
using (var streamReader = new StreamReader(path, Encoding.UTF8))
{
// Use XmlReader for best performance
using (var reader = XmlReader.Create(streamReader, settings))
{
reader.MoveToContent();
// Loop through each element
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (!tagsDictionary.ContainsKey(reader.Name))
{
builder.AppendLine(reader.ReadOuterXml());
}
else
{
reader.Skip();
}
}
}
}
}
return builder.ToString();
}
/// <summary>

View file

@ -21,7 +21,9 @@ namespace MediaBrowser.Providers.TV
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
return Task.Run(() => RunInternal(progress, cancellationToken));
RunInternal(progress, cancellationToken);
return Task.FromResult(true);
}
private void RunInternal(IProgress<double> progress, CancellationToken cancellationToken)

View file

@ -121,7 +121,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
}
catch (IOException)
{
// Cache file doesn't exist or is currently being written ro
// Cache file doesn't exist or is currently being written to
}
var semaphore = GetLock(cacheFilePath);
@ -129,21 +129,24 @@ namespace MediaBrowser.Server.Implementations.Drawing
await semaphore.WaitAsync().ConfigureAwait(false);
// Check again in case of lock contention
if (File.Exists(cacheFilePath))
try
{
try
{
using (var fileStream = new FileStream(cacheFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous))
{
await fileStream.CopyToAsync(toStream).ConfigureAwait(false);
return;
}
}
finally
using (var fileStream = new FileStream(cacheFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous))
{
await fileStream.CopyToAsync(toStream).ConfigureAwait(false);
semaphore.Release();
return;
}
}
catch (IOException)
{
// Cache file doesn't exist or is currently being written to
}
catch
{
semaphore.Release();
throw;
}
try
{
@ -188,12 +191,10 @@ namespace MediaBrowser.Server.Implementations.Drawing
var bytes = outputMemoryStream.ToArray();
var outputTask = toStream.WriteAsync(bytes, 0, bytes.Length);
await toStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
// kick off a task to cache the result
var cacheTask = CacheResizedImage(cacheFilePath, bytes);
await Task.WhenAll(outputTask, cacheTask).ConfigureAwait(false);
CacheResizedImage(cacheFilePath, bytes, semaphore);
}
}
}
@ -202,12 +203,51 @@ namespace MediaBrowser.Server.Implementations.Drawing
}
}
}
finally
catch
{
semaphore.Release();
throw;
}
}
/// <summary>
/// Caches the resized image.
/// </summary>
/// <param name="cacheFilePath">The cache file path.</param>
/// <param name="bytes">The bytes.</param>
/// <param name="semaphore">The semaphore.</param>
private void CacheResizedImage(string cacheFilePath, byte[] bytes, SemaphoreSlim semaphore)
{
Task.Run(async () =>
{
try
{
var parentPath = Path.GetDirectoryName(cacheFilePath);
if (!Directory.Exists(parentPath))
{
Directory.CreateDirectory(parentPath);
}
// Save to the cache location
using (var cacheFileStream = new FileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous))
{
// Save to the filestream
await cacheFileStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
}
}
catch (Exception ex)
{
_logger.ErrorException("Error writing to image cache file {0}", ex, cacheFilePath);
}
finally
{
semaphore.Release();
}
});
}
/// <summary>
/// Sets the color of the background.
/// </summary>
@ -363,28 +403,6 @@ namespace MediaBrowser.Server.Implementations.Drawing
return croppedImagePath;
}
/// <summary>
/// Caches the resized image.
/// </summary>
/// <param name="cacheFilePath">The cache file path.</param>
/// <param name="bytes">The bytes.</param>
private async Task CacheResizedImage(string cacheFilePath, byte[] bytes)
{
var parentPath = Path.GetDirectoryName(cacheFilePath);
if (!Directory.Exists(parentPath))
{
Directory.CreateDirectory(parentPath);
}
// Save to the cache location
using (var cacheFileStream = new FileStream(cacheFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous))
{
// Save to the filestream
await cacheFileStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
}
}
/// <summary>
/// Gets the cache file path based on a set of parameters
/// </summary>

View file

@ -237,7 +237,9 @@ namespace MediaBrowser.Server.Implementations.Dto
NowViewingItemId = session.NowViewingItemId,
NowViewingItemName = session.NowViewingItemName,
NowViewingItemType = session.NowViewingItemType,
ApplicationVersion = session.ApplicationVersion
ApplicationVersion = session.ApplicationVersion,
CanSeek = session.CanSeek,
QueueableMediaTypes = session.QueueableMediaTypes
};
if (session.NowPlayingItem != null)

View file

@ -5,7 +5,7 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Server.Implementations.Udp;
using System.Net.Sockets;
namespace MediaBrowser.ServerApplication.EntryPoints
namespace MediaBrowser.Server.Implementations.EntryPoints
{
/// <summary>
/// Class UdpServerEntryPoint
@ -35,6 +35,8 @@ namespace MediaBrowser.ServerApplication.EntryPoints
/// </summary>
private readonly IHttpServer _httpServer;
public const int PortNumber = 7359;
/// <summary>
/// Initializes a new instance of the <see cref="UdpServerEntryPoint"/> class.
/// </summary>
@ -59,7 +61,7 @@ namespace MediaBrowser.ServerApplication.EntryPoints
try
{
udpServer.Start(ApplicationHost.UdpServerPort);
udpServer.Start(PortNumber);
UdpServer = udpServer;
}

View file

@ -314,6 +314,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// <param name="context">The CTX.</param>
private async void ProcessHttpRequestAsync(HttpListenerContext context)
{
var date = DateTime.Now;
LogHttpRequest(context);
if (context.Request.IsWebSocketRequest)
@ -360,7 +362,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer
var url = context.Request.Url.ToString();
var endPoint = context.Request.RemoteEndPoint;
LogResponse(context, url, endPoint);
var duration = DateTime.Now - date;
LogResponse(context, url, endPoint, duration);
}
catch (Exception ex)
@ -461,14 +465,15 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// <param name="ctx">The CTX.</param>
/// <param name="url">The URL.</param>
/// <param name="endPoint">The end point.</param>
private void LogResponse(HttpListenerContext ctx, string url, IPEndPoint endPoint)
/// <param name="duration">The duration.</param>
private void LogResponse(HttpListenerContext ctx, string url, IPEndPoint endPoint, TimeSpan duration)
{
if (!EnableHttpRequestLogging)
{
return;
}
var statusode = ctx.Response.StatusCode;
var statusCode = ctx.Response.StatusCode;
var log = new StringBuilder();
@ -476,7 +481,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer
log.AppendLine("Headers: " + string.Join(",", ctx.Response.Headers.AllKeys.Select(k => k + "=" + ctx.Response.Headers[k])));
var msg = "Http Response Sent (" + statusode + ") to " + endPoint;
var responseTime = string.Format(". Response time: {0} ms", duration.TotalMilliseconds);
var msg = "Response code " + statusCode + " sent to " + endPoint + responseTime;
_logger.LogMultiline(msg, LogSeverity.Debug, log);
}

View file

@ -829,10 +829,6 @@ namespace MediaBrowser.Server.Implementations.Library
/// <returns>Task.</returns>
public async Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress)
{
const int maxTasks = 3;
var tasks = new List<Task>();
var people = RootFolder.RecursiveChildren
.SelectMany(c => c.People)
.DistinctBy(p => p.Name, StringComparer.OrdinalIgnoreCase)
@ -842,47 +838,27 @@ namespace MediaBrowser.Server.Implementations.Library
foreach (var person in people)
{
if (tasks.Count > maxTasks)
{
await Task.WhenAll(tasks).ConfigureAwait(false);
tasks.Clear();
cancellationToken.ThrowIfCancellationRequested();
// Safe cancellation point, when there are no pending tasks
cancellationToken.ThrowIfCancellationRequested();
try
{
var item = GetPerson(person.Name);
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (IOException ex)
{
_logger.ErrorException("Error validating IBN entry {0}", ex, person.Name);
}
// Avoid accessing the foreach variable within the closure
var currentPerson = person;
// Update progress
numComplete++;
double percent = numComplete;
percent /= people.Count;
tasks.Add(Task.Run(async () =>
{
cancellationToken.ThrowIfCancellationRequested();
try
{
var item = GetPerson(currentPerson.Name);
await item.RefreshMetadata(cancellationToken).ConfigureAwait(false);
}
catch (IOException ex)
{
_logger.ErrorException("Error validating IBN entry {0}", ex, currentPerson.Name);
}
// Update progress
lock (progress)
{
numComplete++;
double percent = numComplete;
percent /= people.Count;
progress.Report(100 * percent);
}
}));
progress.Report(100 * percent);
}
await Task.WhenAll(tasks).ConfigureAwait(false);
progress.Report(100);
_logger.Info("People validation complete");
@ -956,7 +932,9 @@ namespace MediaBrowser.Server.Implementations.Library
public Task ValidateMediaLibrary(IProgress<double> progress, CancellationToken cancellationToken)
{
// Just run the scheduled task so that the user can see it
return Task.Run(() => _taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>());
_taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>();
return Task.FromResult(true);
}
/// <summary>

View file

@ -41,8 +41,6 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// <returns>Task.</returns>
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
var allItems = _libraryManager.RootFolder.RecursiveChildren.OfType<Game>().ToList();
var userLibraries = _userManager.Users
.Select(i => new Tuple<Guid, List<Game>>(i.Id, i.RootFolder.GetRecursiveChildren(i).OfType<Game>().ToList()))
.ToList();
@ -79,6 +77,10 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
{
await UpdateItemByNameCounts(name, cancellationToken, masterDictionary[name]).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// Don't clutter the log
}
catch (Exception ex)
{
_logger.ErrorException("Error updating counts for {0}", ex, name);

View file

@ -42,16 +42,10 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// <returns>Task.</returns>
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
var allItems = _libraryManager.RootFolder.RecursiveChildren
.Where(i => !(i is IHasMusicGenres) && !(i is Game))
.ToList();
var userLibraries = _userManager.Users
.Select(i => new Tuple<Guid, List<BaseItem>>(i.Id, i.RootFolder.GetRecursiveChildren(i).Where(m => !(m is IHasMusicGenres) && !(m is Game)).ToList()))
.ToList();
var allLibraryItems = allItems;
var masterDictionary = new Dictionary<string, Dictionary<Guid, Dictionary<CountType, int>>>(StringComparer.OrdinalIgnoreCase);
// Populate counts of items
@ -84,6 +78,10 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
{
await UpdateItemByNameCounts(name, cancellationToken, masterDictionary[name]).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// Don't clutter the log
}
catch (Exception ex)
{
_logger.ErrorException("Error updating counts for {0}", ex, name);

View file

@ -42,16 +42,10 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// <returns>Task.</returns>
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
var allItems = _libraryManager.RootFolder.RecursiveChildren
.Where(i => i is IHasMusicGenres)
.ToList();
var userLibraries = _userManager.Users
.Select(i => new Tuple<Guid, List<BaseItem>>(i.Id, i.RootFolder.GetRecursiveChildren(i).Where(m => m is IHasMusicGenres).ToList()))
.ToList();
var allLibraryItems = allItems;
var masterDictionary = new Dictionary<string, Dictionary<Guid, Dictionary<CountType, int>>>(StringComparer.OrdinalIgnoreCase);
// Populate counts of items
@ -84,6 +78,10 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
{
await UpdateItemByNameCounts(name, cancellationToken, masterDictionary[name]).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// Don't clutter the log
}
catch (Exception ex)
{
_logger.ErrorException("Error updating counts for {0}", ex, name);

View file

@ -41,7 +41,9 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// <returns>Task.</returns>
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
return Task.Run(() => RunInternal(progress, cancellationToken));
RunInternal(progress, cancellationToken);
return Task.FromResult(true);
}
private void RunInternal(IProgress<double> progress, CancellationToken cancellationToken)

View file

@ -41,14 +41,10 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
/// <returns>Task.</returns>
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
var allItems = _libraryManager.RootFolder.RecursiveChildren.ToList();
var userLibraries = _userManager.Users
.Select(i => new Tuple<Guid, List<BaseItem>>(i.Id, i.RootFolder.GetRecursiveChildren(i).ToList()))
.ToList();
var allLibraryItems = allItems;
var masterDictionary = new Dictionary<string, Dictionary<Guid, Dictionary<CountType, int>>>(StringComparer.OrdinalIgnoreCase);
// Populate counts of items
@ -81,6 +77,10 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
{
await UpdateItemByNameCounts(name, cancellationToken, masterDictionary[name]).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// Don't clutter the log
}
catch (Exception ex)
{
_logger.ErrorException("Error updating counts for {0}", ex, name);

View file

@ -13,6 +13,8 @@
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -35,62 +37,14 @@
<Reference Include="Alchemy">
<HintPath>..\packages\Alchemy.2.2.1\lib\net40\Alchemy.dll</HintPath>
</Reference>
<Reference Include="BdInfo, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\MediaBrowser.BdInfo.1.0.0.2\lib\net45\BdInfo.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib">
<HintPath>..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference>
<Reference Include="Lucene.Net">
<HintPath>..\packages\Lucene.Net.3.0.3\lib\NET40\Lucene.Net.dll</HintPath>
</Reference>
<Reference Include="MoreLinq, Version=1.0.16006.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll</HintPath>
</Reference>
<Reference Include="ServiceStack, Version=3.9.60.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.3.9.62\lib\net35\ServiceStack.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Api.Swagger, Version=3.9.59.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Api.Swagger.3.9.59\lib\net35\ServiceStack.Api.Swagger.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Common, Version=3.9.60.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Interfaces, Version=3.9.60.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.OrmLite.SqlServer, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.OrmLite.SqlServer.3.9.43\lib\ServiceStack.OrmLite.SqlServer.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Redis, Version=3.9.43.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Redis.3.9.43\lib\net35\ServiceStack.Redis.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.ServiceInterface, Version=3.9.60.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.3.9.62\lib\net35\ServiceStack.ServiceInterface.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Text, Version=3.9.59.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data.SQLite, Version=1.0.88.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\System.Data.SQLite.x86.1.0.88.0\lib\net45\System.Data.SQLite.dll</HintPath>
</Reference>
<Reference Include="System.Data.SQLite.Linq, Version=1.0.88.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\System.Data.SQLite.x86.1.0.88.0\lib\net45\System.Data.SQLite.Linq.dll</HintPath>
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.Reactive.Core">
<HintPath>..\packages\Rx-Core.2.1.30214.0\lib\Net45\System.Reactive.Core.dll</HintPath>
@ -106,6 +60,42 @@
<Reference Include="System.Data" />
<Reference Include="System.Web" />
<Reference Include="System.Xml" />
<Reference Include="BDInfo">
<HintPath>..\packages\MediaBrowser.BdInfo.1.0.0.2\lib\net45\BdInfo.dll</HintPath>
</Reference>
<Reference Include="MoreLinq">
<HintPath>..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll</HintPath>
</Reference>
<Reference Include="ServiceStack">
<HintPath>..\packages\ServiceStack.3.9.62\lib\net35\ServiceStack.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Api.Swagger">
<HintPath>..\packages\ServiceStack.Api.Swagger.3.9.59\lib\net35\ServiceStack.Api.Swagger.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Common">
<HintPath>..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Common.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Interfaces">
<HintPath>..\packages\ServiceStack.Common.3.9.62\lib\net35\ServiceStack.Interfaces.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.OrmLite.SqlServer">
<HintPath>..\packages\ServiceStack.OrmLite.SqlServer.3.9.43\lib\ServiceStack.OrmLite.SqlServer.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Redis">
<HintPath>..\packages\ServiceStack.Redis.3.9.43\lib\net35\ServiceStack.Redis.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.ServiceInterface">
<HintPath>..\packages\ServiceStack.3.9.62\lib\net35\ServiceStack.ServiceInterface.dll</HintPath>
</Reference>
<Reference Include="ServiceStack.Text">
<HintPath>..\packages\ServiceStack.Text.3.9.62\lib\net35\ServiceStack.Text.dll</HintPath>
</Reference>
<Reference Include="System.Data.SQLite">
<HintPath>..\packages\System.Data.SQLite.x86.1.0.88.0\lib\net45\System.Data.SQLite.dll</HintPath>
</Reference>
<Reference Include="System.Data.SQLite.Linq">
<HintPath>..\packages\System.Data.SQLite.x86.1.0.88.0\lib\net45\System.Data.SQLite.Linq.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\SharedVersion.cs">
@ -123,6 +113,7 @@
<Compile Include="EntryPoints\Notifications\RemoteNotifications.cs" />
<Compile Include="EntryPoints\Notifications\WebSocketNotifier.cs" />
<Compile Include="EntryPoints\RefreshUsersMetadata.cs" />
<Compile Include="EntryPoints\UdpServerEntryPoint.cs" />
<Compile Include="EntryPoints\WebSocketEvents.cs" />
<Compile Include="HttpServer\HttpResultFactory.cs" />
<Compile Include="HttpServer\HttpServer.cs" />
@ -183,6 +174,7 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="Session\SessionWebSocketListener.cs" />
<Compile Include="Session\WebSocketController.cs" />
<Compile Include="Sorting\AlbumArtistComparer.cs" />
<Compile Include="Sorting\AlbumComparer.cs" />
<Compile Include="Sorting\AlbumCountComparer.cs" />
@ -222,19 +214,19 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj">
<Project>{c4d2573a-3fd3-441f-81af-174ac4cd4e1d}</Project>
<Project>{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}</Project>
<Name>MediaBrowser.Common.Implementations</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
<Project>{9142eefa-7570-41e1-bfcc-468bb571af2f}</Project>
<Project>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</Project>
<Name>MediaBrowser.Common</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj">
<Project>{17e1f4e6-8abd-4fe5-9ecf-43d4b6087ba2}</Project>
<Project>{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}</Project>
<Name>MediaBrowser.Controller</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
<Project>{7eeeb4bb-f3e8-48fc-b4c5-70f0fff8329b}</Project>
<Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
<Name>MediaBrowser.Model</Name>
</ProjectReference>
</ItemGroup>

View file

@ -57,7 +57,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
/// <summary>
/// The FF probe resource pool
/// </summary>
private readonly SemaphoreSlim _ffProbeResourcePool = new SemaphoreSlim(2, 2);
private readonly SemaphoreSlim _ffProbeResourcePool = new SemaphoreSlim(1, 1);
public string FFMpegPath { get; private set; }

View file

@ -333,17 +333,16 @@ namespace MediaBrowser.Server.Implementations.Persistence
/// <returns>Task.</returns>
public Task SaveCriticReviews(Guid itemId, IEnumerable<ItemReview> criticReviews)
{
return Task.Run(() =>
if (!Directory.Exists(_criticReviewsPath))
{
if (!Directory.Exists(_criticReviewsPath))
{
Directory.CreateDirectory(_criticReviewsPath);
}
Directory.CreateDirectory(_criticReviewsPath);
}
var path = Path.Combine(_criticReviewsPath, itemId + ".json");
var path = Path.Combine(_criticReviewsPath, itemId + ".json");
_jsonSerializer.SerializeToFile(criticReviews.ToList(), path);
});
_jsonSerializer.SerializeToFile(criticReviews.ToList(), path);
return Task.FromResult(true);
}
/// <summary>

View file

@ -102,6 +102,18 @@ namespace MediaBrowser.Server.Implementations.Providers
using (source)
{
// If the file is currently hidden we'll have to remove that or the save will fail
var file = new FileInfo(path);
// This will fail if the file is hidden
if (file.Exists)
{
if ((file.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
{
file.Attributes &= ~FileAttributes.Hidden;
}
}
using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous))
{
await source.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, cancellationToken).ConfigureAwait(false);

View file

@ -159,6 +159,10 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
{
previouslyFailedImages = new List<string>();
}
catch (DirectoryNotFoundException)
{
previouslyFailedImages = new List<string>();
}
foreach (var video in videos)
{

View file

@ -1,4 +1,5 @@
using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
@ -6,6 +7,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Session;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@ -75,6 +77,12 @@ namespace MediaBrowser.Server.Implementations.Session
_userRepository = userRepository;
}
private List<ISessionRemoteController> _remoteControllers;
public void AddParts(IEnumerable<ISessionRemoteController> remoteControllers)
{
_remoteControllers = remoteControllers.ToList();
}
/// <summary>
/// Gets all connections.
/// </summary>
@ -122,7 +130,7 @@ namespace MediaBrowser.Server.Implementations.Session
var activityDate = DateTime.UtcNow;
var session = GetSessionInfo(clientType, appVersion, deviceId, deviceName, user);
session.LastActivityDate = activityDate;
if (user == null)
@ -207,25 +215,33 @@ namespace MediaBrowser.Server.Implementations.Session
/// <summary>
/// Used to report that playback has started for an item
/// </summary>
/// <param name="item">The item.</param>
/// <param name="sessionId">The session id.</param>
/// <param name="info">The info.</param>
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
public async Task OnPlaybackStart(BaseItem item, Guid sessionId)
/// <exception cref="System.ArgumentNullException">info</exception>
public async Task OnPlaybackStart(PlaybackInfo info)
{
if (item == null)
if (info == null)
{
throw new ArgumentNullException();
throw new ArgumentNullException("info");
}
if (info.SessionId == Guid.Empty)
{
throw new ArgumentNullException("info");
}
var session = Sessions.First(i => i.Id.Equals(sessionId));
var session = Sessions.First(i => i.Id.Equals(info.SessionId));
var item = info.Item;
UpdateNowPlayingItem(session, item, false, false);
session.CanSeek = info.CanSeek;
session.QueueableMediaTypes = info.QueueableMediaTypes;
var key = item.GetUserDataKey();
var user = session.User;
var data = _userDataRepository.GetUserData(user.Id, key);
data.PlayCount++;
@ -313,7 +329,7 @@ namespace MediaBrowser.Server.Implementations.Session
{
throw new ArgumentOutOfRangeException("positionTicks");
}
var session = Sessions.First(i => i.Id.Equals(sessionId));
RemoveNowPlayingItem(session, item);
@ -321,7 +337,7 @@ namespace MediaBrowser.Server.Implementations.Session
var key = item.GetUserDataKey();
var user = session.User;
var data = _userDataRepository.GetUserData(user.Id, key);
if (positionTicks.HasValue)
@ -400,5 +416,118 @@ namespace MediaBrowser.Server.Implementations.Session
data.PlaybackPositionTicks = positionTicks;
}
/// <summary>
/// Gets the session for remote control.
/// </summary>
/// <param name="sessionId">The session id.</param>
/// <returns>SessionInfo.</returns>
/// <exception cref="ResourceNotFoundException"></exception>
private SessionInfo GetSessionForRemoteControl(Guid sessionId)
{
var session = Sessions.First(i => i.Id.Equals(sessionId));
if (session == null)
{
throw new ResourceNotFoundException(string.Format("Session {0} not found.", sessionId));
}
if (!session.SupportsRemoteControl)
{
throw new ArgumentException(string.Format("Session {0} does not support remote control.", session.Id));
}
return session;
}
/// <summary>
/// Gets the controllers.
/// </summary>
/// <param name="session">The session.</param>
/// <returns>IEnumerable{ISessionRemoteController}.</returns>
private IEnumerable<ISessionRemoteController> GetControllers(SessionInfo session)
{
return _remoteControllers.Where(i => i.Supports(session));
}
/// <summary>
/// Sends the system command.
/// </summary>
/// <param name="sessionId">The session id.</param>
/// <param name="command">The command.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public Task SendSystemCommand(Guid sessionId, SystemCommand command, CancellationToken cancellationToken)
{
var session = GetSessionForRemoteControl(sessionId);
var tasks = GetControllers(session).Select(i => i.SendSystemCommand(session, command, cancellationToken));
return Task.WhenAll(tasks);
}
/// <summary>
/// Sends the message command.
/// </summary>
/// <param name="sessionId">The session id.</param>
/// <param name="command">The command.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public Task SendMessageCommand(Guid sessionId, MessageCommand command, CancellationToken cancellationToken)
{
var session = GetSessionForRemoteControl(sessionId);
var tasks = GetControllers(session).Select(i => i.SendMessageCommand(session, command, cancellationToken));
return Task.WhenAll(tasks);
}
/// <summary>
/// Sends the play command.
/// </summary>
/// <param name="sessionId">The session id.</param>
/// <param name="command">The command.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public Task SendPlayCommand(Guid sessionId, PlayRequest command, CancellationToken cancellationToken)
{
var session = GetSessionForRemoteControl(sessionId);
var tasks = GetControllers(session).Select(i => i.SendPlayCommand(session, command, cancellationToken));
return Task.WhenAll(tasks);
}
/// <summary>
/// Sends the browse command.
/// </summary>
/// <param name="sessionId">The session id.</param>
/// <param name="command">The command.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public Task SendBrowseCommand(Guid sessionId, BrowseRequest command, CancellationToken cancellationToken)
{
var session = GetSessionForRemoteControl(sessionId);
var tasks = GetControllers(session).Select(i => i.SendBrowseCommand(session, command, cancellationToken));
return Task.WhenAll(tasks);
}
/// <summary>
/// Sends the playstate command.
/// </summary>
/// <param name="sessionId">The session id.</param>
/// <param name="command">The command.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
public Task SendPlaystateCommand(Guid sessionId, PlaystateRequest command, CancellationToken cancellationToken)
{
var session = GetSessionForRemoteControl(sessionId);
var tasks = GetControllers(session).Select(i => i.SendPlaystateCommand(session, command, cancellationToken));
return Task.WhenAll(tasks);
}
}
}

View file

@ -101,16 +101,7 @@ namespace MediaBrowser.Server.Implementations.Session
}
else if (string.Equals(message.MessageType, "PlaybackStart", StringComparison.OrdinalIgnoreCase))
{
_logger.Debug("Received PlaybackStart message");
var session = _sessionManager.Sessions.FirstOrDefault(i => i.WebSockets.Contains(message.Connection));
if (session != null && session.User != null)
{
var item = _dtoService.GetItemByDtoId(message.Data);
_sessionManager.OnPlaybackStart(item, session.Id);
}
ReportPlaybackStart(message);
}
else if (string.Equals(message.MessageType, "PlaybackProgress", StringComparison.OrdinalIgnoreCase))
{
@ -170,5 +161,46 @@ namespace MediaBrowser.Server.Implementations.Session
return _trueTaskResult;
}
/// <summary>
/// Reports the playback start.
/// </summary>
/// <param name="message">The message.</param>
private void ReportPlaybackStart(WebSocketMessageInfo message)
{
_logger.Debug("Received PlaybackStart message");
var session = _sessionManager.Sessions
.FirstOrDefault(i => i.WebSockets.Contains(message.Connection));
if (session != null && session.User != null)
{
var vals = message.Data.Split('|');
var item = _dtoService.GetItemByDtoId(vals[0]);
var queueableMediaTypes = string.Empty;
var canSeek = true;
if (vals.Length > 1)
{
canSeek = string.Equals(vals[1], "true", StringComparison.OrdinalIgnoreCase);
}
if (vals.Length > 2)
{
queueableMediaTypes = vals[2];
}
var info = new PlaybackInfo
{
CanSeek = canSeek,
Item = item,
SessionId = session.Id,
QueueableMediaTypes = queueableMediaTypes.Split(',').ToList()
};
_sessionManager.OnPlaybackStart(info);
}
}
}
}

View file

@ -0,0 +1,92 @@
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Session;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Session
{
public class WebSocketController : ISessionRemoteController
{
public bool Supports(SessionInfo session)
{
return session.WebSockets.Any(i => i.State == WebSocketState.Open);
}
private IWebSocketConnection GetSocket(SessionInfo session)
{
var socket = session.WebSockets.OrderByDescending(i => i.LastActivityDate).FirstOrDefault(i => i.State == WebSocketState.Open);
if (socket == null)
{
throw new InvalidOperationException("The requested session does not have an open web socket.");
}
return socket;
}
public Task SendSystemCommand(SessionInfo session, SystemCommand command, CancellationToken cancellationToken)
{
var socket = GetSocket(session);
return socket.SendAsync(new WebSocketMessage<string>
{
MessageType = "SystemCommand",
Data = command.ToString()
}, cancellationToken);
}
public Task SendMessageCommand(SessionInfo session, MessageCommand command, CancellationToken cancellationToken)
{
var socket = GetSocket(session);
return socket.SendAsync(new WebSocketMessage<MessageCommand>
{
MessageType = "MessageCommand",
Data = command
}, cancellationToken);
}
public Task SendPlayCommand(SessionInfo session, PlayRequest command, CancellationToken cancellationToken)
{
var socket = GetSocket(session);
return socket.SendAsync(new WebSocketMessage<PlayRequest>
{
MessageType = "Play",
Data = command
}, cancellationToken);
}
public Task SendBrowseCommand(SessionInfo session, BrowseRequest command, CancellationToken cancellationToken)
{
var socket = GetSocket(session);
return socket.SendAsync(new WebSocketMessage<BrowseRequest>
{
MessageType = "Browse",
Data = command
}, cancellationToken);
}
public Task SendPlaystateCommand(SessionInfo session, PlaystateRequest command, CancellationToken cancellationToken)
{
var socket = GetSocket(session);
return socket.SendAsync(new WebSocketMessage<PlaystateRequest>
{
MessageType = "Playstate",
Data = command
}, cancellationToken);
}
}
}

View file

@ -92,7 +92,9 @@ namespace MediaBrowser.Server.Implementations.WebSocket
/// <returns>Task.</returns>
public Task SendAsync(byte[] bytes, WebSocketMessageType type, bool endOfMessage, CancellationToken cancellationToken)
{
return Task.Run(() => UserContext.Send(bytes));
UserContext.Send(bytes);
return Task.FromResult(true);
}
/// <summary>

View file

@ -0,0 +1,36 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.ServerApplication.FFMpeg
{
public class FFMpegDownloader
{
private readonly IHttpClient _httpClient;
private readonly IApplicationPaths _appPaths;
private readonly ILogger _logger;
private readonly IZipClient _zipClient;
public FFMpegDownloader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient)
{
_logger = logger;
_appPaths = appPaths;
_httpClient = httpClient;
_zipClient = zipClient;
}
public Task<FFMpegInfo> GetFFMpegInfo()
{
return Task.FromResult (new FFMpegInfo());
}
}
}

View file

@ -0,0 +1,16 @@
using System;
using Gtk;
public partial class MainWindow: Gtk.Window
{
public MainWindow (): base (Gtk.WindowType.Toplevel)
{
Build ();
}
protected void OnDeleteEvent (object sender, DeleteEventArgs a)
{
Application.Quit ();
a.RetVal = true;
}
}

View file

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{A7FE75CD-3CB4-4E71-A5BF-5347721EC8E0}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>MediaBrowser.Server.Mono</RootNamespace>
<AssemblyName>MediaBrowser.Server.Mono</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x86</PlatformTarget>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<DebugType>full</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x86</PlatformTarget>
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="Mono.Posix, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756" />
<Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="glib-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="glade-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="pango-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="atk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Data" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="gtk-gui\gui.stetic">
<LogicalName>gui.stetic</LogicalName>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Compile Include="gtk-gui\generated.cs" />
<Compile Include="MainWindow.cs" />
<Compile Include="gtk-gui\MainWindow.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="..\MediaBrowser.ServerApplication\EntryPoints\StartupWizard.cs">
<Link>EntryPoints\StartupWizard.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.ServerApplication\Native\BrowserLauncher.cs">
<Link>Native\BrowserLauncher.cs</Link>
</Compile>
<Compile Include="Native\Autorun.cs" />
<Compile Include="Native\ServerAuthorization.cs" />
<Compile Include="FFMpeg\FFMpegDownloader.cs" />
<Compile Include="..\MediaBrowser.ServerApplication\FFMpeg\FFMpegInfo.cs">
<Link>FFMpeg\FFMpegInfo.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.ServerApplication\ApplicationHost.cs">
<Link>ApplicationHost.cs</Link>
</Compile>
<Compile Include="Native\HttpMessageHandlerFactory.cs" />
<Compile Include="Native\Assemblies.cs" />
<Compile Include="Native\Sqlite.cs" />
<Compile Include="Native\NativeApp.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
<ProjectReference Include="..\MediaBrowser.WebDashboard\MediaBrowser.WebDashboard.csproj">
<Project>{5624B7B5-B5A7-41D8-9F10-CC5611109619}</Project>
<Name>MediaBrowser.WebDashboard</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Server.Implementations\MediaBrowser.Server.Implementations.csproj">
<Project>{2E781478-814D-4A48-9D80-BFF206441A65}</Project>
<Name>MediaBrowser.Server.Implementations</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Providers\MediaBrowser.Providers.csproj">
<Project>{442B5058-DCAF-4263-BB6A-F21E31120A1B}</Project>
<Name>MediaBrowser.Providers</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj">
<Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project>
<Name>MediaBrowser.Model</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj">
<Project>{17E1F4E6-8ABD-4FE5-9ECF-43D4B6087BA2}</Project>
<Name>MediaBrowser.Controller</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Common.Implementations\MediaBrowser.Common.Implementations.csproj">
<Project>{C4D2573A-3FD3-441F-81AF-174AC4CD4E1D}</Project>
<Name>MediaBrowser.Common.Implementations</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
<Project>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</Project>
<Name>MediaBrowser.Common</Name>
</ProjectReference>
<ProjectReference Include="..\MediaBrowser.Api\MediaBrowser.Api.csproj">
<Project>{4FD51AC5-2C16-4308-A993-C3A84F3B4582}</Project>
<Name>MediaBrowser.Api</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="EntryPoints\" />
<Folder Include="Implementations\" />
<Folder Include="Native\" />
<Folder Include="FFMpeg\" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,22 @@
using System.Collections.Generic;
using System.Reflection;
namespace MediaBrowser.ServerApplication.Native
{
/// <summary>
/// Class Assemblies
/// </summary>
public static class Assemblies
{
/// <summary>
/// Gets the assemblies with parts.
/// </summary>
/// <returns>List{Assembly}.</returns>
public static List<Assembly> GetAssembliesWithParts()
{
var list = new List<Assembly>();
return list;
}
}
}

View file

@ -0,0 +1,20 @@
using System;
using System.IO;
namespace MediaBrowser.ServerApplication.Native
{
/// <summary>
/// Class Autorun
/// </summary>
public static class Autorun
{
/// <summary>
/// Configures the specified autorun.
/// </summary>
/// <param name="autorun">if set to <c>true</c> [autorun].</param>
public static void Configure(bool autorun)
{
}
}
}

View file

@ -0,0 +1,25 @@
using System.Net;
using System.Net.Cache;
using System.Net.Http;
namespace MediaBrowser.ServerApplication.Native
{
/// <summary>
/// Class HttpMessageHandlerFactory
/// </summary>
public static class HttpMessageHandlerFactory
{
/// <summary>
/// Gets the HTTP message handler.
/// </summary>
/// <param name="enableHttpCompression">if set to <c>true</c> [enable HTTP compression].</param>
/// <returns>HttpMessageHandler.</returns>
public static HttpMessageHandler GetHttpMessageHandler(bool enableHttpCompression)
{
return new HttpClientHandler
{
AutomaticDecompression = enableHttpCompression ? DecompressionMethods.Deflate : DecompressionMethods.None
};
}
}
}

View file

@ -0,0 +1,25 @@

namespace MediaBrowser.ServerApplication.Native
{
/// <summary>
/// Class NativeApp
/// </summary>
public static class NativeApp
{
/// <summary>
/// Shutdowns this instance.
/// </summary>
public static void Shutdown()
{
}
/// <summary>
/// Restarts this instance.
/// </summary>
public static void Restart()
{
}
}
}

View file

@ -0,0 +1,26 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
namespace MediaBrowser.ServerApplication.Native
{
/// <summary>
/// Class Authorization
/// </summary>
public static class ServerAuthorization
{
/// <summary>
/// Authorizes the server.
/// </summary>
/// <param name="httpServerPort">The HTTP server port.</param>
/// <param name="httpServerUrlPrefix">The HTTP server URL prefix.</param>
/// <param name="webSocketPort">The web socket port.</param>
/// <param name="udpPort">The UDP port.</param>
/// <param name="tempDirectory">The temp directory.</param>
public static void AuthorizeServer(int httpServerPort, string httpServerUrlPrefix, int webSocketPort, int udpPort, string tempDirectory)
{
}
}
}

View file

@ -0,0 +1,36 @@
using System.Data;
using System.Data.SQLite;
using System.Threading.Tasks;
namespace MediaBrowser.ServerApplication.Native
{
/// <summary>
/// Class Sqlite
/// </summary>
public static class Sqlite
{
/// <summary>
/// Connects to db.
/// </summary>
/// <param name="dbPath">The db path.</param>
/// <returns>Task{IDbConnection}.</returns>
/// <exception cref="System.ArgumentNullException">dbPath</exception>
public static async Task<IDbConnection> OpenDatabase(string dbPath)
{
var connectionstr = new SQLiteConnectionStringBuilder
{
PageSize = 4096,
CacheSize = 4096,
SyncMode = SynchronizationModes.Normal,
DataSource = dbPath,
JournalMode = SQLiteJournalModeEnum.Wal
};
var connection = new SQLiteConnection(connectionstr.ConnectionString);
await connection.OpenAsync().ConfigureAwait(false);
return connection;
}
}
}

View file

@ -0,0 +1,16 @@
using System;
using Gtk;
namespace MediaBrowser.Server.Mono
{
class MainClass
{
public static void Main (string[] args)
{
Application.Init ();
MainWindow win = new MainWindow ();
win.Show ();
Application.Run ();
}
}
}

View file

@ -0,0 +1,22 @@
using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following attributes.
// Change them to the values specific to your project.
[assembly: AssemblyTitle ("MediaBrowser.Server.Mono")]
[assembly: AssemblyDescription ("")]
[assembly: AssemblyConfiguration ("")]
[assembly: AssemblyCompany ("")]
[assembly: AssemblyProduct ("")]
[assembly: AssemblyCopyright ("Luke")]
[assembly: AssemblyTrademark ("")]
[assembly: AssemblyCulture ("")]
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
[assembly: AssemblyVersion ("1.0.*")]
// The following attributes are used to specify the signing key for the assembly,
// if desired. See the Mono documentation for more information about signing.
//[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("")]

View file

@ -0,0 +1,20 @@
// This file has been generated by the GUI designer. Do not modify.
public partial class MainWindow
{
protected virtual void Build ()
{
global::Stetic.Gui.Initialize (this);
// Widget MainWindow
this.Name = "MainWindow";
this.Title = global::Mono.Unix.Catalog.GetString ("MainWindow");
this.WindowPosition = ((global::Gtk.WindowPosition)(4));
if ((this.Child != null)) {
this.Child.ShowAll ();
}
this.DefaultWidth = 400;
this.DefaultHeight = 300;
this.Show ();
this.DeleteEvent += new global::Gtk.DeleteEventHandler (this.OnDeleteEvent);
}
}

View file

@ -0,0 +1,29 @@
// This file has been generated by the GUI designer. Do not modify.
namespace Stetic
{
internal class Gui
{
private static bool initialized;
internal static void Initialize (Gtk.Widget iconRenderer)
{
if ((Stetic.Gui.initialized == false)) {
Stetic.Gui.initialized = true;
}
}
}
internal class ActionGroups
{
public static Gtk.ActionGroup GetActionGroup (System.Type type)
{
return Stetic.ActionGroups.GetActionGroup (type.FullName);
}
public static Gtk.ActionGroup GetActionGroup (string name)
{
return null;
}
}
}

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<stetic-interface>
<configuration>
<images-root-path>..</images-root-path>
<target-gtk-version>2.12</target-gtk-version>
</configuration>
<import>
<widget-library name="glade-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
<widget-library name="../bin/Debug/MediaBrowser.Server.Mono.exe" internal="true" />
</import>
<widget class="Gtk.Window" id="MainWindow" design-size="400 300">
<property name="MemberName" />
<property name="Title" translatable="yes">MainWindow</property>
<property name="WindowPosition">CenterOnParent</property>
<signal name="DeleteEvent" handler="OnDeleteEvent" />
<child>
<placeholder />
</child>
</widget>
</stetic-interface>

View file

@ -154,58 +154,5 @@ namespace MediaBrowser.ServerApplication
{
Dispatcher.Invoke(Shutdown);
}
/// <summary>
/// Opens the dashboard page.
/// </summary>
/// <param name="page">The page.</param>
/// <param name="loggedInUser">The logged in user.</param>
/// <param name="configurationManager">The configuration manager.</param>
/// <param name="appHost">The app host.</param>
public static void OpenDashboardPage(string page, User loggedInUser, IServerConfigurationManager configurationManager, IServerApplicationHost appHost)
{
var url = "http://localhost:" + configurationManager.Configuration.HttpServerPortNumber + "/" +
appHost.WebApplicationName + "/dashboard/" + page;
OpenUrl(url);
}
/// <summary>
/// Opens the URL.
/// </summary>
/// <param name="url">The URL.</param>
public static void OpenUrl(string url)
{
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = url
},
EnableRaisingEvents = true
};
process.Exited += ProcessExited;
try
{
process.Start();
}
catch (Exception ex)
{
MessageBox.Show("There was an error launching your web browser. Please check your defualt browser settings.");
}
}
/// <summary>
/// Processes the exited.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
static void ProcessExited(object sender, EventArgs e)
{
((Process)sender).Dispose();
}
}
}

View file

@ -24,7 +24,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Controller.Session;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.IsoMounter;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.MediaInfo;
@ -36,6 +35,7 @@ using MediaBrowser.Server.Implementations.BdInfo;
using MediaBrowser.Server.Implementations.Configuration;
using MediaBrowser.Server.Implementations.Drawing;
using MediaBrowser.Server.Implementations.Dto;
using MediaBrowser.Server.Implementations.EntryPoints;
using MediaBrowser.Server.Implementations.HttpServer;
using MediaBrowser.Server.Implementations.IO;
using MediaBrowser.Server.Implementations.Library;
@ -46,16 +46,14 @@ using MediaBrowser.Server.Implementations.Providers;
using MediaBrowser.Server.Implementations.ServerManager;
using MediaBrowser.Server.Implementations.Session;
using MediaBrowser.Server.Implementations.WebSocket;
using MediaBrowser.ServerApplication.Implementations;
using MediaBrowser.ServerApplication.FFMpeg;
using MediaBrowser.ServerApplication.Native;
using MediaBrowser.WebDashboard.Api;
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Diagnostics;
using System.Data;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Cache;
using System.Net.Http;
using System.Reflection;
using System.Threading;
@ -68,8 +66,6 @@ namespace MediaBrowser.ServerApplication
/// </summary>
public class ApplicationHost : BaseApplicationHost<ServerApplicationPaths>, IServerApplicationHost
{
internal const int UdpServerPort = 7359;
/// <summary>
/// Gets the server kernel.
/// </summary>
@ -142,11 +138,6 @@ namespace MediaBrowser.ServerApplication
/// <value>The provider manager.</value>
private IProviderManager ProviderManager { get; set; }
/// <summary>
/// Gets or sets the zip client.
/// </summary>
/// <value>The zip client.</value>
private IZipClient ZipClient { get; set; }
/// <summary>
/// Gets or sets the HTTP server.
/// </summary>
/// <value>The HTTP server.</value>
@ -161,6 +152,7 @@ namespace MediaBrowser.ServerApplication
private IMediaEncoder MediaEncoder { get; set; }
private IIsoManager IsoManager { get; set; }
private ISessionManager SessionManager { get; set; }
private ILocalizationManager LocalizationManager { get; set; }
@ -174,14 +166,6 @@ namespace MediaBrowser.ServerApplication
private IItemRepository ItemRepository { get; set; }
private INotificationsRepository NotificationsRepository { get; set; }
/// <summary>
/// The full path to our startmenu shortcut
/// </summary>
protected override string ProductShortcutPath
{
get { return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), "Media Browser 3", "Media Browser Server.lnk"); }
}
private Task<IHttpServer> _httpServerCreationTask;
/// <summary>
@ -255,8 +239,7 @@ namespace MediaBrowser.ServerApplication
RegisterSingleInstance<IBlurayExaminer>(() => new BdInfoExaminer());
ZipClient = new DotNetZipClient();
RegisterSingleInstance(ZipClient);
var mediaEncoderTask = RegisterMediaEncoder();
UserDataRepository = new SqliteUserDataRepository(ApplicationPaths, JsonSerializer, LogManager);
RegisterSingleInstance(UserDataRepository);
@ -284,10 +267,8 @@ namespace MediaBrowser.ServerApplication
RegisterSingleInstance<ILibrarySearchEngine>(() => new LuceneSearchEngine(ApplicationPaths, LogManager, LibraryManager));
await RegisterMediaEncoder().ConfigureAwait(false);
var clientConnectionManager = new SessionManager(UserDataRepository, ServerConfigurationManager, Logger, UserRepository);
RegisterSingleInstance<ISessionManager>(clientConnectionManager);
SessionManager = new SessionManager(UserDataRepository, ServerConfigurationManager, Logger, UserRepository);
RegisterSingleInstance<ISessionManager>(SessionManager);
HttpServer = await _httpServerCreationTask.ConfigureAwait(false);
RegisterSingleInstance(HttpServer, false);
@ -310,7 +291,7 @@ namespace MediaBrowser.ServerApplication
await ConfigureNotificationsRepository().ConfigureAwait(false);
await Task.WhenAll(itemsTask, displayPreferencesTask, userdataTask).ConfigureAwait(false);
await Task.WhenAll(itemsTask, displayPreferencesTask, userdataTask, mediaEncoderTask).ConfigureAwait(false);
SetKernelProperties();
}
@ -406,27 +387,14 @@ namespace MediaBrowser.ServerApplication
/// <param name="dbPath">The db path.</param>
/// <returns>Task{IDbConnection}.</returns>
/// <exception cref="System.ArgumentNullException">dbPath</exception>
private static async Task<SQLiteConnection> ConnectToDb(string dbPath)
private static Task<IDbConnection> ConnectToDb(string dbPath)
{
if (string.IsNullOrEmpty(dbPath))
{
throw new ArgumentNullException("dbPath");
}
var connectionstr = new SQLiteConnectionStringBuilder
{
PageSize = 4096,
CacheSize = 4096,
SyncMode = SynchronizationModes.Normal,
DataSource = dbPath,
JournalMode = SQLiteJournalModeEnum.Wal
};
var connection = new SQLiteConnection(connectionstr.ConnectionString);
await connection.OpenAsync().ConfigureAwait(false);
return connection;
return Sqlite.OpenDatabase(dbPath);
}
/// <summary>
@ -477,6 +445,8 @@ namespace MediaBrowser.ServerApplication
IsoManager.AddParts(GetExports<IIsoMounter>());
SessionManager.AddParts(GetExports<ISessionRemoteController>());
ImageProcessor.AddParts(GetExports<IImageEnhancer>());
}
@ -527,7 +497,6 @@ namespace MediaBrowser.ServerApplication
{
NotifyPendingRestart();
}
}
/// <summary>
@ -544,7 +513,7 @@ namespace MediaBrowser.ServerApplication
Logger.ErrorException("Error sending server restart web socket message", ex);
}
MainStartup.Restart();
NativeApp.Restart();
}
/// <summary>
@ -568,44 +537,44 @@ namespace MediaBrowser.ServerApplication
/// <returns>IEnumerable{Assembly}.</returns>
protected override IEnumerable<Assembly> GetComposablePartAssemblies()
{
var list = Directory.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly)
.Select(LoadAssembly)
.Where(a => a != null)
.ToList();
// Gets all plugin assemblies by first reading all bytes of the .dll and calling Assembly.Load against that
// This will prevent the .dll file from getting locked, and allow us to replace it when needed
foreach (var pluginAssembly in Directory
.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.TopDirectoryOnly)
.Select(LoadAssembly).Where(a => a != null))
{
yield return pluginAssembly;
}
// Include composable parts in the Api assembly
yield return typeof(ApiEntryPoint).Assembly;
list.Add(typeof(ApiEntryPoint).Assembly);
// Include composable parts in the Dashboard assembly
yield return typeof(DashboardInfo).Assembly;
list.Add(typeof(DashboardInfo).Assembly);
// Include composable parts in the Model assembly
yield return typeof(SystemInfo).Assembly;
list.Add(typeof(SystemInfo).Assembly);
// Include composable parts in the Common assembly
yield return typeof(IApplicationHost).Assembly;
list.Add(typeof(IApplicationHost).Assembly);
// Include composable parts in the Controller assembly
yield return typeof(Kernel).Assembly;
list.Add(typeof(Kernel).Assembly);
// Include composable parts in the Providers assembly
yield return typeof(ImagesByNameProvider).Assembly;
list.Add(typeof(ImagesByNameProvider).Assembly);
// Common implementations
yield return typeof(TaskManager).Assembly;
list.Add(typeof(TaskManager).Assembly);
// Server implementations
yield return typeof(ServerApplicationPaths).Assembly;
list.Add(typeof(ServerApplicationPaths).Assembly);
// Pismo
yield return typeof(PismoIsoManager).Assembly;
list.AddRange(Assemblies.GetAssembliesWithParts());
// Include composable parts in the running assembly
yield return GetType().Assembly;
list.Add(GetType().Assembly);
return list;
}
private readonly string _systemId = Environment.MachineName.GetMD5().ToString();
@ -664,7 +633,7 @@ namespace MediaBrowser.ServerApplication
Logger.ErrorException("Error sending server shutdown web socket message", ex);
}
MainStartup.Shutdown();
NativeApp.Shutdown();
}
/// <summary>
@ -674,36 +643,16 @@ namespace MediaBrowser.ServerApplication
{
Logger.Info("Requesting administrative access to authorize http server");
// Create a temp file path to extract the bat file to
var tmpFile = Path.Combine(ConfigurationManager.CommonApplicationPaths.TempDirectory, Guid.NewGuid() + ".bat");
// Extract the bat file
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MediaBrowser.ServerApplication.RegisterServer.bat"))
try
{
using (var fileStream = File.Create(tmpFile))
{
stream.CopyTo(fileStream);
}
ServerAuthorization.AuthorizeServer(ServerConfigurationManager.Configuration.HttpServerPortNumber,
HttpServerUrlPrefix, ServerConfigurationManager.Configuration.LegacyWebSocketPortNumber,
UdpServerEntryPoint.PortNumber,
ConfigurationManager.CommonApplicationPaths.TempDirectory);
}
var startInfo = new ProcessStartInfo
catch (Exception ex)
{
FileName = tmpFile,
Arguments = string.Format("{0} {1} {2} {3}", ServerConfigurationManager.Configuration.HttpServerPortNumber,
HttpServerUrlPrefix,
UdpServerPort,
ServerConfigurationManager.Configuration.LegacyWebSocketPortNumber),
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
Verb = "runas",
ErrorDialog = false
};
using (var process = Process.Start(startInfo))
{
process.WaitForExit();
Logger.ErrorException("Error authorizing server", ex);
}
}
@ -713,8 +662,7 @@ namespace MediaBrowser.ServerApplication
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="progress">The progress.</param>
/// <returns>Task{CheckForUpdateResult}.</returns>
public override async Task<CheckForUpdateResult> CheckForApplicationUpdate(CancellationToken cancellationToken,
IProgress<double> progress)
public override async Task<CheckForUpdateResult> CheckForApplicationUpdate(CancellationToken cancellationToken, IProgress<double> progress)
{
var availablePackages = await InstallationManager.GetAvailablePackagesWithoutRegistrationInfo(cancellationToken).ConfigureAwait(false);
@ -745,11 +693,12 @@ namespace MediaBrowser.ServerApplication
/// <returns>HttpMessageHandler.</returns>
protected override HttpMessageHandler GetHttpMessageHandler(bool enableHttpCompression)
{
return new WebRequestHandler
{
CachePolicy = new RequestCachePolicy(RequestCacheLevel.Revalidate),
AutomaticDecompression = enableHttpCompression ? DecompressionMethods.Deflate : DecompressionMethods.None
};
return HttpMessageHandlerFactory.GetHttpMessageHandler(enableHttpCompression);
}
protected override void ConfigureAutoRunAtStartup(bool autorun)
{
Autorun.Configure(autorun);
}
}
}

View file

@ -3,9 +3,10 @@ using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Logging;
using System.ComponentModel;
using System;
using System.Linq;
using System.Windows;
using System.Windows.Forms;
using MediaBrowser.ServerApplication.Native;
namespace MediaBrowser.ServerApplication.EntryPoints
{
@ -31,9 +32,10 @@ namespace MediaBrowser.ServerApplication.EntryPoints
/// </summary>
/// <param name="appHost">The app host.</param>
/// <param name="userManager">The user manager.</param>
public StartupWizard(IServerApplicationHost appHost, IUserManager userManager, IServerConfigurationManager configurationManager)
public StartupWizard(IServerApplicationHost appHost, IUserManager userManager, IServerConfigurationManager configurationManager, ILogger logger)
{
_appHost = appHost;
_logger = logger;
_userManager = userManager;
_configurationManager = configurationManager;
}
@ -58,9 +60,9 @@ namespace MediaBrowser.ServerApplication.EntryPoints
try
{
App.OpenDashboardPage("wizardstart.html", user, _configurationManager, _appHost);
BrowserLauncher.OpenDashboardPage("wizardstart.html", user, _configurationManager, _appHost, _logger);
}
catch (Win32Exception ex)
catch (Exception ex)
{
_logger.ErrorException("Error launching startup wizard", ex);
@ -75,4 +77,4 @@ namespace MediaBrowser.ServerApplication.EntryPoints
{
}
}
}
}

View file

@ -0,0 +1,302 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.ServerApplication.FFMpeg
{
public class FFMpegDownloader
{
private readonly IHttpClient _httpClient;
private readonly IApplicationPaths _appPaths;
private readonly ILogger _logger;
private readonly IZipClient _zipClient;
private const string Version = "ffmpeg20130904";
private readonly string[] _fontUrls = new[]
{
"https://www.dropbox.com/s/pj847twf7riq0j7/ARIALUNI.7z?dl=1"
};
private readonly string[] _ffMpegUrls = new[]
{
"https://github.com/MediaBrowser/MediaBrowser/raw/master/MediaBrowser.ServerApplication/FFMpeg/ffmpeg-20130904-git-f974289-win32-static.7z",
"http://ffmpeg.zeranoe.com/builds/win32/static/ffmpeg-20130904-git-f974289-win32-static.7z",
"https://www.dropbox.com/s/a81cb2ob23fwcfs/ffmpeg-20130904-git-f974289-win32-static.7z?dl=1"
};
public FFMpegDownloader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient)
{
_logger = logger;
_appPaths = appPaths;
_httpClient = httpClient;
_zipClient = zipClient;
}
public async Task<FFMpegInfo> GetFFMpegInfo()
{
var versionedDirectoryPath = Path.Combine(GetMediaToolsPath(true), Version);
var info = new FFMpegInfo
{
ProbePath = Path.Combine(versionedDirectoryPath, "ffprobe.exe"),
Path = Path.Combine(versionedDirectoryPath, "ffmpeg.exe"),
Version = Version
};
if (!Directory.Exists(versionedDirectoryPath))
{
Directory.CreateDirectory(versionedDirectoryPath);
}
var tasks = new List<Task>();
if (!File.Exists(info.ProbePath) || !File.Exists(info.Path))
{
tasks.Add(DownloadFFMpeg(info));
}
tasks.Add(DownloadFonts(versionedDirectoryPath));
await Task.WhenAll(tasks).ConfigureAwait(false);
return info;
}
private async Task DownloadFFMpeg(FFMpegInfo info)
{
foreach (var url in _ffMpegUrls)
{
try
{
var tempFile = await DownloadFFMpeg(info, url).ConfigureAwait(false);
ExtractFFMpeg(tempFile, Path.GetDirectoryName(info.Path));
return;
}
catch (HttpException ex)
{
}
}
throw new ApplicationException("Unable to download required components. Please try again later.");
}
private Task<string> DownloadFFMpeg(FFMpegInfo info, string url)
{
return _httpClient.GetTempFile(new HttpRequestOptions
{
Url = url,
CancellationToken = CancellationToken.None,
Progress = new Progress<double>(),
// Make it look like a browser
// Try to hide that we're direct linking
UserAgent = "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.47 Safari/537.36"
});
}
private void ExtractFFMpeg(string tempFile, string targetFolder)
{
_logger.Debug("Extracting ffmpeg from {0}", tempFile);
var tempFolder = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString());
if (!Directory.Exists(tempFolder))
{
Directory.CreateDirectory(tempFolder);
}
try
{
Extract7zArchive(tempFile, tempFolder);
var files = Directory.EnumerateFiles(tempFolder, "*.exe", SearchOption.AllDirectories).ToList();
foreach (var file in files)
{
File.Copy(file, Path.Combine(targetFolder, Path.GetFileName(file)));
}
}
finally
{
DeleteFile(tempFile);
}
}
private void Extract7zArchive(string archivePath, string targetPath)
{
_zipClient.ExtractAllFrom7z(archivePath, targetPath, true);
}
private void DeleteFile(string path)
{
try
{
File.Delete(path);
}
catch (IOException ex)
{
_logger.ErrorException("Error deleting temp file {0}", ex, path);
}
}
/// <summary>
/// Extracts the fonts.
/// </summary>
/// <param name="targetPath">The target path.</param>
private async Task DownloadFonts(string targetPath)
{
try
{
var fontsDirectory = Path.Combine(targetPath, "fonts");
if (!Directory.Exists(fontsDirectory))
{
Directory.CreateDirectory(fontsDirectory);
}
const string fontFilename = "ARIALUNI.TTF";
var fontFile = Path.Combine(fontsDirectory, fontFilename);
if (!File.Exists(fontFile))
{
await DownloadFontFile(fontsDirectory, fontFilename).ConfigureAwait(false);
}
await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false);
}
catch (HttpException ex)
{
// Don't let the server crash because of this
_logger.ErrorException("Error downloading ffmpeg font files", ex);
}
catch (Exception ex)
{
// Don't let the server crash because of this
_logger.ErrorException("Error writing ffmpeg font files", ex);
}
}
/// <summary>
/// Downloads the font file.
/// </summary>
/// <param name="fontsDirectory">The fonts directory.</param>
/// <param name="fontFilename">The font filename.</param>
/// <returns>Task.</returns>
private async Task DownloadFontFile(string fontsDirectory, string fontFilename)
{
var existingFile = Directory
.EnumerateFiles(_appPaths.ProgramDataPath, fontFilename, SearchOption.AllDirectories)
.FirstOrDefault();
if (existingFile != null)
{
try
{
File.Copy(existingFile, Path.Combine(fontsDirectory, fontFilename), true);
return;
}
catch (IOException ex)
{
// Log this, but don't let it fail the operation
_logger.ErrorException("Error copying file", ex);
}
}
string tempFile = null;
foreach (var url in _fontUrls)
{
try
{
tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
{
Url = url,
Progress = new Progress<double>()
}).ConfigureAwait(false);
break;
}
catch (Exception ex)
{
// The core can function without the font file, so handle this
_logger.ErrorException("Failed to download ffmpeg font file from {0}", ex, url);
}
}
if (string.IsNullOrEmpty(tempFile))
{
return;
}
Extract7zArchive(tempFile, fontsDirectory);
try
{
File.Delete(tempFile);
}
catch (IOException ex)
{
// Log this, but don't let it fail the operation
_logger.ErrorException("Error deleting temp file {0}", ex, tempFile);
}
}
/// <summary>
/// Writes the font config file.
/// </summary>
/// <param name="fontsDirectory">The fonts directory.</param>
/// <returns>Task.</returns>
private async Task WriteFontConfigFile(string fontsDirectory)
{
const string fontConfigFilename = "fonts.conf";
var fontConfigFile = Path.Combine(fontsDirectory, fontConfigFilename);
if (!File.Exists(fontConfigFile))
{
var contents = string.Format("<?xml version=\"1.0\"?><fontconfig><dir>{0}</dir><alias><family>Arial</family><prefer>Arial Unicode MS</prefer></alias></fontconfig>", fontsDirectory);
var bytes = Encoding.UTF8.GetBytes(contents);
using (var fileStream = new FileStream(fontConfigFile, FileMode.Create, FileAccess.Write,
FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize,
FileOptions.Asynchronous))
{
await fileStream.WriteAsync(bytes, 0, bytes.Length);
}
}
}
/// <summary>
/// Gets the media tools path.
/// </summary>
/// <param name="create">if set to <c>true</c> [create].</param>
/// <returns>System.String.</returns>
private string GetMediaToolsPath(bool create)
{
var path = Path.Combine(_appPaths.ProgramDataPath, "ffmpeg");
if (create && !Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
return path;
}
}
}

View file

@ -0,0 +1,24 @@
namespace MediaBrowser.ServerApplication.FFMpeg
{
/// <summary>
/// Class FFMpegInfo
/// </summary>
public class FFMpegInfo
{
/// <summary>
/// Gets or sets the path.
/// </summary>
/// <value>The path.</value>
public string Path { get; set; }
/// <summary>
/// Gets or sets the probe path.
/// </summary>
/// <value>The probe path.</value>
public string ProbePath { get; set; }
/// <summary>
/// Gets or sets the version.
/// </summary>
/// <value>The version.</value>
public string Version { get; set; }
}
}

View file

@ -0,0 +1 @@
8f1dfd62d31e48c31bef4b9ccc0e514f46650a79

View file

@ -1,43 +0,0 @@
using Ionic.Zip;
using MediaBrowser.Model.IO;
using System.IO;
namespace MediaBrowser.ServerApplication.Implementations
{
/// <summary>
/// Class DotNetZipClient
/// </summary>
public class DotNetZipClient : IZipClient
{
/// <summary>
/// Extracts all.
/// </summary>
/// <param name="sourceFile">The source file.</param>
/// <param name="targetPath">The target path.</param>
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAll(string sourceFile, string targetPath, bool overwriteExistingFiles)
{
using (var fileStream = File.OpenRead(sourceFile))
{
using (var zipFile = ZipFile.Read(fileStream))
{
zipFile.ExtractAll(targetPath, overwriteExistingFiles ? ExtractExistingFileAction.OverwriteSilently : ExtractExistingFileAction.DoNotOverwrite);
}
}
}
/// <summary>
/// Extracts all.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="targetPath">The target path.</param>
/// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
public void ExtractAll(Stream source, string targetPath, bool overwriteExistingFiles)
{
using (var zipFile = ZipFile.Read(source))
{
zipFile.ExtractAll(targetPath, overwriteExistingFiles ? ExtractExistingFileAction.OverwriteSilently : ExtractExistingFileAction.DoNotOverwrite);
}
}
}
}

View file

@ -1,205 +0,0 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Logging;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace MediaBrowser.ServerApplication.Implementations
{
public class FFMpegDownloader
{
private readonly IZipClient _zipClient;
private readonly IHttpClient _httpClient;
private readonly IApplicationPaths _appPaths;
private readonly ILogger _logger;
public FFMpegDownloader(ILogger logger, IApplicationPaths appPaths, IHttpClient httpClient, IZipClient zipClient)
{
_logger = logger;
_appPaths = appPaths;
_httpClient = httpClient;
_zipClient = zipClient;
}
public async Task<FFMpegInfo> GetFFMpegInfo()
{
var assembly = GetType().Assembly;
var prefix = GetType().Namespace + ".";
var srch = prefix + "ffmpeg";
var resource = assembly.GetManifestResourceNames().First(r => r.StartsWith(srch));
var filename =
resource.Substring(resource.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) + prefix.Length);
var versionedDirectoryPath = Path.Combine(GetMediaToolsPath(true),
Path.GetFileNameWithoutExtension(filename));
if (!Directory.Exists(versionedDirectoryPath))
{
Directory.CreateDirectory(versionedDirectoryPath);
}
await ExtractTools(assembly, resource, versionedDirectoryPath).ConfigureAwait(false);
return new FFMpegInfo
{
ProbePath = Path.Combine(versionedDirectoryPath, "ffprobe.exe"),
Path = Path.Combine(versionedDirectoryPath, "ffmpeg.exe"),
Version = Path.GetFileNameWithoutExtension(versionedDirectoryPath)
};
}
/// <summary>
/// Extracts the tools.
/// </summary>
/// <param name="assembly">The assembly.</param>
/// <param name="zipFileResourcePath">The zip file resource path.</param>
/// <param name="targetPath">The target path.</param>
private async Task ExtractTools(Assembly assembly, string zipFileResourcePath, string targetPath)
{
using (var resourceStream = assembly.GetManifestResourceStream(zipFileResourcePath))
{
_zipClient.ExtractAll(resourceStream, targetPath, false);
}
try
{
await DownloadFonts(targetPath).ConfigureAwait(false);
}
catch (Exception ex)
{
_logger.ErrorException("Error getting ffmpeg font files", ex);
}
}
private const string FontUrl = "https://www.dropbox.com/s/9nb76tybcsw5xrk/ARIALUNI.zip?dl=1";
/// <summary>
/// Extracts the fonts.
/// </summary>
/// <param name="targetPath">The target path.</param>
private async Task DownloadFonts(string targetPath)
{
var fontsDirectory = Path.Combine(targetPath, "fonts");
if (!Directory.Exists(fontsDirectory))
{
Directory.CreateDirectory(fontsDirectory);
}
const string fontFilename = "ARIALUNI.TTF";
var fontFile = Path.Combine(fontsDirectory, fontFilename);
if (!File.Exists(fontFile))
{
await DownloadFontFile(fontsDirectory, fontFilename).ConfigureAwait(false);
}
await WriteFontConfigFile(fontsDirectory).ConfigureAwait(false);
}
/// <summary>
/// Downloads the font file.
/// </summary>
/// <param name="fontsDirectory">The fonts directory.</param>
/// <param name="fontFilename">The font filename.</param>
/// <returns>Task.</returns>
private async Task DownloadFontFile(string fontsDirectory, string fontFilename)
{
var existingFile = Directory
.EnumerateFiles(_appPaths.ProgramDataPath, fontFilename, SearchOption.AllDirectories)
.FirstOrDefault();
if (existingFile != null)
{
try
{
File.Copy(existingFile, Path.Combine(fontsDirectory, fontFilename), true);
return;
}
catch (IOException ex)
{
// Log this, but don't let it fail the operation
_logger.ErrorException("Error copying file", ex);
}
}
var tempFile = await _httpClient.GetTempFile(new HttpRequestOptions
{
Url = FontUrl,
Progress = new Progress<double>()
});
_zipClient.ExtractAll(tempFile, fontsDirectory, true);
try
{
File.Delete(tempFile);
}
catch (IOException ex)
{
// Log this, but don't let it fail the operation
_logger.ErrorException("Error deleting temp file {0}", ex, tempFile);
}
}
/// <summary>
/// Writes the font config file.
/// </summary>
/// <param name="fontsDirectory">The fonts directory.</param>
/// <returns>Task.</returns>
private async Task WriteFontConfigFile(string fontsDirectory)
{
const string fontConfigFilename = "fonts.conf";
var fontConfigFile = Path.Combine(fontsDirectory, fontConfigFilename);
if (!File.Exists(fontConfigFile))
{
var contents = string.Format("<?xml version=\"1.0\"?><fontconfig><dir>{0}</dir><alias><family>Arial</family><prefer>Arial Unicode MS</prefer></alias></fontconfig>", fontsDirectory);
var bytes = Encoding.UTF8.GetBytes(contents);
using (var fileStream = new FileStream(fontConfigFile, FileMode.Create, FileAccess.Write,
FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize,
FileOptions.Asynchronous))
{
await fileStream.WriteAsync(bytes, 0, bytes.Length);
}
}
}
/// <summary>
/// Gets the media tools path.
/// </summary>
/// <param name="create">if set to <c>true</c> [create].</param>
/// <returns>System.String.</returns>
private string GetMediaToolsPath(bool create)
{
var path = Path.Combine(_appPaths.ProgramDataPath, "ffmpeg");
if (create && !Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
return path;
}
}
public class FFMpegInfo
{
public string Path { get; set; }
public string ProbePath { get; set; }
public string Version { get; set; }
}
}

View file

@ -1 +0,0 @@
3496b2cde22e7c4cb56b480dd2da637167d51e78

View file

@ -1,5 +0,0 @@
This is the 32-bit static build of ffmpeg, located at:
http://ffmpeg.zeranoe.com/builds/
The zip file contains both ffmpeg and ffprobe, and is suffixed with the date of the build.

View file

@ -11,7 +11,6 @@ using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace MediaBrowser.ServerApplication

View file

@ -12,6 +12,7 @@ using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Threading;
using MediaBrowser.ServerApplication.Native;
namespace MediaBrowser.ServerApplication
{
@ -188,19 +189,19 @@ namespace MediaBrowser.ServerApplication
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
void cmdApiDocs_Click(object sender, EventArgs e)
{
App.OpenUrl("http://localhost:" + _configurationManager.Configuration.HttpServerPortNumber + "/" +
_appHost.WebApplicationName + "/metadata");
BrowserLauncher.OpenUrl("http://localhost:" + _configurationManager.Configuration.HttpServerPortNumber + "/" +
_appHost.WebApplicationName + "/metadata", _logger);
}
void cmdSwaggerApiDocs_Click(object sender, EventArgs e)
{
App.OpenUrl("http://localhost:" + _configurationManager.Configuration.HttpServerPortNumber + "/" +
_appHost.WebApplicationName + "/swagger-ui/index.html");
BrowserLauncher.OpenUrl("http://localhost:" + _configurationManager.Configuration.HttpServerPortNumber + "/" +
_appHost.WebApplicationName + "/swagger-ui/index.html", _logger);
}
void cmdGithubWiki_Click(object sender, EventArgs e)
{
App.OpenUrl("https://github.com/MediaBrowser/MediaBrowser/wiki");
BrowserLauncher.OpenUrl("https://github.com/MediaBrowser/MediaBrowser/wiki", _logger);
}
/// <summary>
@ -254,7 +255,7 @@ namespace MediaBrowser.ServerApplication
/// </summary>
private void OpenDashboard(User loggedInUser)
{
App.OpenDashboardPage("dashboard.html", loggedInUser, _configurationManager, _appHost);
BrowserLauncher.OpenDashboardPage("dashboard.html", loggedInUser, _configurationManager, _appHost, _logger);
}
/// <summary>
@ -264,7 +265,7 @@ namespace MediaBrowser.ServerApplication
/// <param name="e">The <see cref="RoutedEventArgs" /> instance containing the event data.</param>
private void cmVisitCT_click(object sender, RoutedEventArgs e)
{
App.OpenUrl("http://community.mediabrowser.tv/");
BrowserLauncher.OpenUrl("http://community.mediabrowser.tv/", _logger);
}
/// <summary>
@ -275,7 +276,7 @@ namespace MediaBrowser.ServerApplication
private void cmdBrowseLibrary_click(object sender, RoutedEventArgs e)
{
var user = _userManager.Users.FirstOrDefault(u => u.Configuration.IsAdministrator);
App.OpenDashboardPage("index.html", user, _configurationManager, _appHost);
BrowserLauncher.OpenDashboardPage("index.html", user, _configurationManager, _appHost, _logger);
}
/// <summary>

View file

@ -130,10 +130,6 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\MediaBrowser.IsoMounting.3.0.56\lib\net45\MediaBrowser.IsoMounter.dll</HintPath>
</Reference>
<Reference Include="MoreLinq, Version=1.0.16006.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\morelinq.1.0.16006\lib\net35\MoreLinq.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=2.0.1.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\NLog.2.0.1.2\lib\net45\NLog.dll</HintPath>
@ -187,7 +183,6 @@
<Reference Include="System.Net" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.Runtime.Remoting" />
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
@ -209,12 +204,19 @@
<SubType>Component</SubType>
</Compile>
<Compile Include="EntryPoints\StartupWizard.cs" />
<Compile Include="EntryPoints\UdpServerEntryPoint.cs" />
<Compile Include="Implementations\FFMpegDownloader.cs" />
<Compile Include="FFMpeg\FFMpegInfo.cs" />
<Compile Include="Native\Assemblies.cs" />
<Compile Include="Native\HttpMessageHandlerFactory.cs" />
<Compile Include="Native\NativeApp.cs" />
<Compile Include="Native\ServerAuthorization.cs" />
<Compile Include="Native\Autorun.cs" />
<Compile Include="Native\BrowserLauncher.cs" />
<Compile Include="FFMpeg\FFMpegDownloader.cs" />
<Compile Include="MainStartup.cs" />
<Compile Include="BackgroundServiceInstaller.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Native\Sqlite.cs" />
<Compile Include="Splash\SplashWindow.xaml.cs">
<DependentUpon>SplashWindow.xaml</DependentUpon>
</Compile>
@ -242,7 +244,6 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="ApplicationHost.cs" />
<Compile Include="Implementations\DotNetZipClient.cs" />
<Compile Include="LibraryExplorer.xaml.cs">
<DependentUpon>LibraryExplorer.xaml</DependentUpon>
</Compile>
@ -278,14 +279,15 @@
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="app.manifest" />
<EmbeddedResource Include="Implementations\ffmpeg20130904.zip" />
<None Include="FFMpeg\ARIALUNI.7z" />
<None Include="FFMpeg\ffmpeg-20130904-git-f974289-win32-static.7z" />
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<AppDesigner Include="Properties\" />
<EmbeddedResource Include="RegisterServer.bat" />
<EmbeddedResource Include="Native\RegisterServer.bat" />
</ItemGroup>
<ItemGroup>
<None Include="App.config">
@ -390,9 +392,6 @@
<ItemGroup>
<Resource Include="Resources\Images\mb3logo800.png" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Implementations\readme.txt" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>if $(ConfigurationName) == Release (

View file

@ -0,0 +1,25 @@
using MediaBrowser.IsoMounter;
using System.Collections.Generic;
using System.Reflection;
namespace MediaBrowser.ServerApplication.Native
{
/// <summary>
/// Class Assemblies
/// </summary>
public static class Assemblies
{
/// <summary>
/// Gets the assemblies with parts.
/// </summary>
/// <returns>List{Assembly}.</returns>
public static List<Assembly> GetAssembliesWithParts()
{
var list = new List<Assembly>();
list.Add(typeof(PismoIsoManager).Assembly);
return list;
}
}
}

View file

@ -0,0 +1,31 @@
using System;
using System.IO;
namespace MediaBrowser.ServerApplication.Native
{
/// <summary>
/// Class Autorun
/// </summary>
public static class Autorun
{
/// <summary>
/// Configures the specified autorun.
/// </summary>
/// <param name="autorun">if set to <c>true</c> [autorun].</param>
public static void Configure(bool autorun)
{
var shortcutPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), "Media Browser 3", "Media Browser Server.lnk");
if (autorun)
{
//Copy our shortut into the startup folder for this user
File.Copy(shortcutPath, Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), Path.GetFileName(shortcutPath) ?? "MBstartup.lnk"), true);
}
else
{
//Remove our shortcut from the startup folder for this user
File.Delete(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Startup), Path.GetFileName(shortcutPath) ?? "MBstartup.lnk"));
}
}
}
}

Some files were not shown because too many files have changed in this diff Show more