mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-09-06 11:34:57 +02:00
Merge branch 'master' of https://github.com/MediaBrowser/MediaBrowser
This commit is contained in:
commit
c02c0db35a
114 changed files with 2747 additions and 1354 deletions
|
@ -57,3 +57,4 @@
|
|||
- [Detector1](https://github.com/Detector1)
|
||||
- [BlackIce013](https://github.com/blackice013)
|
||||
- [mporcas] (https://github.com/mporcas)
|
||||
- [tikuf] (https://github.com/tikuf/)
|
||||
|
|
|
@ -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>
|
||||
|
|
181
MediaBrowser.Api/AuthorizationRequestFilterAttribute.cs
Normal file
181
MediaBrowser.Api/AuthorizationRequestFilterAttribute.cs
Normal 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.
|
||||
/// <0 Executed before global request filters
|
||||
/// >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;
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
/// <0 Executed before global request filters
|
||||
/// >0 Executed after global request filters
|
||||
/// </summary>
|
||||
/// <value>The priority.</value>
|
||||
public int Priority
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
53
MediaBrowser.Api/Playback/Hls/HlsSegmentResponseFilter.cs
Normal file
53
MediaBrowser.Api/Playback/Hls/HlsSegmentResponseFilter.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
147
MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
Normal file
147
MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
Normal 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
87
MediaBrowser.Common.Implementations/Archiving/ZipClient.cs
Normal file
87
MediaBrowser.Common.Implementations/Archiving/ZipClient.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
61
MediaBrowser.Controller/Session/ISessionRemoteController.cs
Normal file
61
MediaBrowser.Controller/Session/ISessionRemoteController.cs
Normal 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);
|
||||
}
|
||||
}
|
38
MediaBrowser.Controller/Session/PlaybackInfo.cs
Normal file
38
MediaBrowser.Controller/Session/PlaybackInfo.cs
Normal 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; }
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
68
MediaBrowser.Mono.sln
Normal 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
|
19
MediaBrowser.Mono.userprefs
Normal file
19
MediaBrowser.Mono.userprefs
Normal 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>
|
|
@ -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>
|
||||
|
|
|
@ -178,6 +178,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||
}
|
||||
}
|
||||
|
||||
SetLastRefreshed(item, DateTime.UtcNow);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -159,6 +159,10 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
|
|||
{
|
||||
previouslyFailedImages = new List<string>();
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
previouslyFailedImages = new List<string>();
|
||||
}
|
||||
|
||||
foreach (var video in videos)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
36
MediaBrowser.Server.Mono/FFMpeg/FFMpegDownloader.cs
Normal file
36
MediaBrowser.Server.Mono/FFMpeg/FFMpegDownloader.cs
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
16
MediaBrowser.Server.Mono/MainWindow.cs
Normal file
16
MediaBrowser.Server.Mono/MainWindow.cs
Normal 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;
|
||||
}
|
||||
}
|
119
MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
Normal file
119
MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
Normal 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>
|
22
MediaBrowser.Server.Mono/Native/Assemblies.cs
Normal file
22
MediaBrowser.Server.Mono/Native/Assemblies.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
20
MediaBrowser.Server.Mono/Native/Autorun.cs
Normal file
20
MediaBrowser.Server.Mono/Native/Autorun.cs
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
25
MediaBrowser.Server.Mono/Native/HttpMessageHandlerFactory.cs
Normal file
25
MediaBrowser.Server.Mono/Native/HttpMessageHandlerFactory.cs
Normal 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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
25
MediaBrowser.Server.Mono/Native/NativeApp.cs
Normal file
25
MediaBrowser.Server.Mono/Native/NativeApp.cs
Normal 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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
26
MediaBrowser.Server.Mono/Native/ServerAuthorization.cs
Normal file
26
MediaBrowser.Server.Mono/Native/ServerAuthorization.cs
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
36
MediaBrowser.Server.Mono/Native/Sqlite.cs
Normal file
36
MediaBrowser.Server.Mono/Native/Sqlite.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
16
MediaBrowser.Server.Mono/Program.cs
Normal file
16
MediaBrowser.Server.Mono/Program.cs
Normal 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 ();
|
||||
}
|
||||
}
|
||||
}
|
22
MediaBrowser.Server.Mono/Properties/AssemblyInfo.cs
Normal file
22
MediaBrowser.Server.Mono/Properties/AssemblyInfo.cs
Normal 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("")]
|
||||
|
20
MediaBrowser.Server.Mono/gtk-gui/MainWindow.cs
Normal file
20
MediaBrowser.Server.Mono/gtk-gui/MainWindow.cs
Normal 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);
|
||||
}
|
||||
}
|
29
MediaBrowser.Server.Mono/gtk-gui/generated.cs
Normal file
29
MediaBrowser.Server.Mono/gtk-gui/generated.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
20
MediaBrowser.Server.Mono/gtk-gui/gui.stetic
Normal file
20
MediaBrowser.Server.Mono/gtk-gui/gui.stetic
Normal 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>
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
302
MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs
Normal file
302
MediaBrowser.ServerApplication/FFMpeg/FFMpegDownloader.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
24
MediaBrowser.ServerApplication/FFMpeg/FFMpegInfo.cs
Normal file
24
MediaBrowser.ServerApplication/FFMpeg/FFMpegInfo.cs
Normal 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; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
8f1dfd62d31e48c31bef4b9ccc0e514f46650a79
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
3496b2cde22e7c4cb56b480dd2da637167d51e78
|
|
@ -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.
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 (
|
||||
|
|
25
MediaBrowser.ServerApplication/Native/Assemblies.cs
Normal file
25
MediaBrowser.ServerApplication/Native/Assemblies.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
31
MediaBrowser.ServerApplication/Native/Autorun.cs
Normal file
31
MediaBrowser.ServerApplication/Native/Autorun.cs
Normal 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
Loading…
Reference in a new issue