aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Api
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Api')
-rw-r--r--MediaBrowser.Api/BaseApiService.cs12
-rw-r--r--MediaBrowser.Api/ConfigurationService.cs24
-rw-r--r--MediaBrowser.Api/Images/ImageService.cs63
-rw-r--r--MediaBrowser.Api/Images/ImageWriter.cs96
-rw-r--r--MediaBrowser.Api/ItemLookupService.cs19
-rw-r--r--MediaBrowser.Api/ItemUpdateService.cs11
-rw-r--r--MediaBrowser.Api/Library/LibraryService.cs2
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj3
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs27
-rw-r--r--MediaBrowser.Api/Playback/EndlessStreamCopy.cs32
-rw-r--r--MediaBrowser.Api/Playback/Hls/BaseHlsService.cs5
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs21
-rw-r--r--MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs7
-rw-r--r--MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs16
-rw-r--r--MediaBrowser.Api/Sync/SyncService.cs130
-rw-r--r--MediaBrowser.Api/UserService.cs92
16 files changed, 350 insertions, 210 deletions
diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs
index 09eb1ea41..727ee6fbc 100644
--- a/MediaBrowser.Api/BaseApiService.cs
+++ b/MediaBrowser.Api/BaseApiService.cs
@@ -125,7 +125,7 @@ namespace MediaBrowser.Api
return ResultFactory.GetStaticFileResult(Request, path);
}
- private readonly char[] _dashReplaceChars = new[] { '?', '/' };
+ private readonly char[] _dashReplaceChars = { '?', '/' };
private const char SlugChar = '-';
protected MusicArtist GetArtist(string name, ILibraryManager libraryManager)
@@ -168,6 +168,11 @@ namespace MediaBrowser.Api
{
var user = userManager.GetUserById(userId.Value);
+ if (user == null)
+ {
+ throw new ArgumentException("User not found");
+ }
+
return folder.GetRecursiveChildren(user);
}
@@ -177,6 +182,11 @@ namespace MediaBrowser.Api
{
var user = userManager.GetUserById(userId.Value);
+ if (user == null)
+ {
+ throw new ArgumentException("User not found");
+ }
+
return userManager.GetUserById(userId.Value).RootFolder.GetRecursiveChildren(user);
}
diff --git a/MediaBrowser.Api/ConfigurationService.cs b/MediaBrowser.Api/ConfigurationService.cs
index c83028bb2..29848cfc0 100644
--- a/MediaBrowser.Api/ConfigurationService.cs
+++ b/MediaBrowser.Api/ConfigurationService.cs
@@ -122,42 +122,44 @@ namespace MediaBrowser.Api
return ToOptimizedResult(result);
}
+ const string XbmcMetadata = "Xbmc Nfo";
+ const string MediaBrowserMetadata = "Media Browser Xml";
+
public void Post(AutoSetMetadataOptions request)
{
var service = AutoDetectMetadataService();
Logger.Info("Setting preferred metadata format to " + service);
- _configurationManager.SetPreferredMetadataService(service);
+ var serviceToDisable = string.Equals(service, XbmcMetadata) ?
+ MediaBrowserMetadata :
+ XbmcMetadata;
+
+ _configurationManager.DisableMetadataService(serviceToDisable);
_configurationManager.SaveConfiguration();
}
private string AutoDetectMetadataService()
{
- const string xbmc = "Xbmc Nfo";
- const string mb = "Media Browser Xml";
-
var paths = _libraryManager.GetDefaultVirtualFolders()
.SelectMany(i => i.Locations)
.Distinct(StringComparer.OrdinalIgnoreCase)
.Select(i => new DirectoryInfo(i))
.ToList();
- if (paths.Select(i => i.EnumerateFiles("*.xml", SearchOption.AllDirectories))
- .SelectMany(i => i)
+ if (paths.SelectMany(i => i.EnumerateFiles("*.xml", SearchOption.AllDirectories))
.Any())
{
- return xbmc;
+ return XbmcMetadata;
}
- if (paths.Select(i => i.EnumerateFiles("*.xml", SearchOption.AllDirectories))
- .SelectMany(i => i)
+ if (paths.SelectMany(i => i.EnumerateFiles("*.xml", SearchOption.AllDirectories))
.Any(i => string.Equals(i.Name, "series.xml", StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, "movie.xml", StringComparison.OrdinalIgnoreCase)))
{
- return mb;
+ return MediaBrowserMetadata;
}
- return xbmc;
+ return XbmcMetadata;
}
/// <summary>
diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs
index deaefe019..30db91da8 100644
--- a/MediaBrowser.Api/Images/ImageService.cs
+++ b/MediaBrowser.Api/Images/ImageService.cs
@@ -535,9 +535,6 @@ namespace MediaBrowser.Api.Images
throw new ResourceNotFoundException(string.Format("{0} does not have an image of type {1}", item.Name, request.Type));
}
- // See if we can avoid a file system lookup by looking for the file in ResolveArgs
- var originalFileImageDateModified = imageInfo.DateModified;
-
var supportedImageEnhancers = request.EnableImageEnhancers ? _imageProcessor.ImageEnhancers.Where(i =>
{
try
@@ -564,25 +561,59 @@ namespace MediaBrowser.Api.Images
cacheDuration = TimeSpan.FromDays(365);
}
- // Avoid implicitly captured closure
- var currentItem = item;
- var currentRequest = request;
-
var responseHeaders = new Dictionary<string, string>
{
{"transferMode.dlna.org", "Interactive"},
{"realTimeInfo.dlna.org", "DLNA.ORG_TLAG=*"}
};
- return ToCachedResult(cacheGuid, originalFileImageDateModified, cacheDuration, () => new ImageWriter
+ return GetImageResult(item,
+ request,
+ imageInfo,
+ supportedImageEnhancers,
+ contentType,
+ cacheDuration,
+ responseHeaders)
+ .Result;
+ }
+
+ private async Task<object> GetImageResult(IHasImages item,
+ ImageRequest request,
+ ItemImageInfo image,
+ List<IImageEnhancer> enhancers,
+ string contentType,
+ TimeSpan? cacheDuration,
+ IDictionary<string,string> headers)
+ {
+ var cropwhitespace = request.Type == ImageType.Logo || request.Type == ImageType.Art;
+
+ if (request.CropWhitespace.HasValue)
{
- Item = currentItem,
- Request = currentRequest,
- Enhancers = supportedImageEnhancers,
- Image = imageInfo,
- ImageProcessor = _imageProcessor
+ cropwhitespace = request.CropWhitespace.Value;
+ }
- }, contentType, responseHeaders);
+ var options = new ImageProcessingOptions
+ {
+ CropWhiteSpace = cropwhitespace,
+ Enhancers = enhancers,
+ Height = request.Height,
+ ImageIndex = request.Index ?? 0,
+ Image = image,
+ Item = item,
+ MaxHeight = request.MaxHeight,
+ MaxWidth = request.MaxWidth,
+ Quality = request.Quality,
+ Width = request.Width,
+ OutputFormat = request.Format,
+ AddPlayedIndicator = request.AddPlayedIndicator,
+ PercentPlayed = request.PercentPlayed,
+ UnplayedCount = request.UnplayedCount,
+ BackgroundColor = request.BackgroundColor
+ };
+
+ var file = await _imageProcessor.ProcessImage(options).ConfigureAwait(false);
+
+ return ResultFactory.GetStaticFileResult(Request, file, contentType, cacheDuration, FileShare.Read, headers);
}
private string GetMimeType(ImageOutputFormat format, string path)
@@ -603,6 +634,10 @@ namespace MediaBrowser.Api.Images
{
return Common.Net.MimeTypes.GetMimeType("i.png");
}
+ if (format == ImageOutputFormat.Webp)
+ {
+ return Common.Net.MimeTypes.GetMimeType("i.webp");
+ }
return Common.Net.MimeTypes.GetMimeType(path);
}
diff --git a/MediaBrowser.Api/Images/ImageWriter.cs b/MediaBrowser.Api/Images/ImageWriter.cs
deleted file mode 100644
index b6638d9df..000000000
--- a/MediaBrowser.Api/Images/ImageWriter.cs
+++ /dev/null
@@ -1,96 +0,0 @@
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Entities;
-using ServiceStack.Web;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Api.Images
-{
- /// <summary>
- /// Class ImageWriter
- /// </summary>
- public class ImageWriter : IStreamWriter, IHasOptions
- {
- public List<IImageEnhancer> Enhancers;
-
- /// <summary>
- /// Gets or sets the request.
- /// </summary>
- /// <value>The request.</value>
- public ImageRequest Request { get; set; }
- /// <summary>
- /// Gets or sets the item.
- /// </summary>
- /// <value>The item.</value>
- public IHasImages Item { get; set; }
- /// <summary>
- /// The original image date modified
- /// </summary>
- public ItemImageInfo Image;
-
- public IImageProcessor ImageProcessor { get; set; }
-
- /// <summary>
- /// The _options
- /// </summary>
- private readonly IDictionary<string, string> _options = new Dictionary<string, string>();
- /// <summary>
- /// Gets the options.
- /// </summary>
- /// <value>The options.</value>
- public IDictionary<string, string> Options
- {
- get { return _options; }
- }
-
- /// <summary>
- /// Writes to.
- /// </summary>
- /// <param name="responseStream">The response stream.</param>
- public void WriteTo(Stream responseStream)
- {
- var task = WriteToAsync(responseStream);
-
- Task.WaitAll(task);
- }
-
- /// <summary>
- /// Writes to async.
- /// </summary>
- /// <param name="responseStream">The response stream.</param>
- /// <returns>Task.</returns>
- private Task WriteToAsync(Stream responseStream)
- {
- var cropwhitespace = Request.Type == ImageType.Logo || Request.Type == ImageType.Art;
-
- if (Request.CropWhitespace.HasValue)
- {
- cropwhitespace = Request.CropWhitespace.Value;
- }
-
- var options = new ImageProcessingOptions
- {
- CropWhiteSpace = cropwhitespace,
- Enhancers = Enhancers,
- Height = Request.Height,
- ImageIndex = Request.Index ?? 0,
- Image = Image,
- Item = Item,
- MaxHeight = Request.MaxHeight,
- MaxWidth = Request.MaxWidth,
- Quality = Request.Quality,
- Width = Request.Width,
- OutputFormat = Request.Format,
- AddPlayedIndicator = Request.AddPlayedIndicator,
- PercentPlayed = Request.PercentPlayed,
- UnplayedCount = Request.UnplayedCount,
- BackgroundColor = Request.BackgroundColor
- };
-
- return ImageProcessor.ProcessImage(options, responseStream);
- }
- }
-}
diff --git a/MediaBrowser.Api/ItemLookupService.cs b/MediaBrowser.Api/ItemLookupService.cs
index a08f28533..2ce7997a1 100644
--- a/MediaBrowser.Api/ItemLookupService.cs
+++ b/MediaBrowser.Api/ItemLookupService.cs
@@ -224,16 +224,23 @@ namespace MediaBrowser.Api
}
}
- var task = item.RefreshMetadata(new MetadataRefreshOptions
+ var service = new ItemRefreshService(_libraryManager)
{
+ Logger = Logger,
+ Request = Request,
+ ResultFactory = ResultFactory,
+ SessionContext = SessionContext
+ };
+
+ service.Post(new RefreshItem
+ {
+ Id = request.Id,
MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
ImageRefreshMode = ImageRefreshMode.FullRefresh,
ReplaceAllMetadata = true,
- ReplaceAllImages = true
-
- }, CancellationToken.None);
-
- Task.WaitAll(task);
+ ReplaceAllImages = true,
+ Recursive = true
+ });
}
/// <summary>
diff --git a/MediaBrowser.Api/ItemUpdateService.cs b/MediaBrowser.Api/ItemUpdateService.cs
index db6c6ce53..1fa1a5509 100644
--- a/MediaBrowser.Api/ItemUpdateService.cs
+++ b/MediaBrowser.Api/ItemUpdateService.cs
@@ -65,6 +65,11 @@ namespace MediaBrowser.Api
}
}
+ private DateTime NormalizeDateTime(DateTime val)
+ {
+ return DateTime.SpecifyKind(val, DateTimeKind.Utc);
+ }
+
private void UpdateItem(BaseItemDto request, BaseItem item)
{
item.Name = request.Name;
@@ -140,11 +145,11 @@ namespace MediaBrowser.Api
if (request.DateCreated.HasValue)
{
- item.DateCreated = request.DateCreated.Value.ToUniversalTime();
+ item.DateCreated = NormalizeDateTime(request.DateCreated.Value);
}
- item.EndDate = request.EndDate.HasValue ? request.EndDate.Value.ToUniversalTime() : (DateTime?)null;
- item.PremiereDate = request.PremiereDate.HasValue ? request.PremiereDate.Value.ToUniversalTime() : (DateTime?)null;
+ item.EndDate = request.EndDate.HasValue ? NormalizeDateTime(request.EndDate.Value) : (DateTime?)null;
+ item.PremiereDate = request.PremiereDate.HasValue ? NormalizeDateTime(request.PremiereDate.Value) : (DateTime?)null;
item.ProductionYear = request.ProductionYear;
item.OfficialRating = request.OfficialRating;
item.CustomRating = request.CustomRating;
diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs
index 10aa23126..f6588714e 100644
--- a/MediaBrowser.Api/Library/LibraryService.cs
+++ b/MediaBrowser.Api/Library/LibraryService.cs
@@ -220,7 +220,7 @@ namespace MediaBrowser.Api.Library
[Api(Description = "Reports that new episodes of a series have been added by an external source")]
public class PostUpdatedSeries : IReturnVoid
{
- [ApiMember(Name = "TvdbId", Description = "Tvdb Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ [ApiMember(Name = "TvdbId", Description = "Tvdb Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
public string TvdbId { get; set; }
}
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index 6a1e45e25..f3857ce30 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -85,7 +85,6 @@
<Compile Include="Images\ImageByNameService.cs" />
<Compile Include="Images\ImageRequest.cs" />
<Compile Include="Images\ImageService.cs" />
- <Compile Include="Images\ImageWriter.cs" />
<Compile Include="Music\InstantMixService.cs" />
<Compile Include="ItemLookupService.cs" />
<Compile Include="ItemRefreshService.cs" />
@@ -102,7 +101,6 @@
<Compile Include="PackageReviewService.cs" />
<Compile Include="PackageService.cs" />
<Compile Include="Playback\BifService.cs" />
- <Compile Include="Playback\EndlessStreamCopy.cs" />
<Compile Include="Playback\Hls\BaseHlsService.cs" />
<Compile Include="Playback\Hls\DynamicHlsService.cs" />
<Compile Include="Playback\Hls\HlsSegmentService.cs" />
@@ -123,6 +121,7 @@
<Compile Include="SearchService.cs" />
<Compile Include="SessionsService.cs" />
<Compile Include="SimilarItemsHelper.cs" />
+ <Compile Include="Sync\SyncService.cs" />
<Compile Include="SystemService.cs" />
<Compile Include="Movies\TrailersService.cs" />
<Compile Include="TvShowsService.cs" />
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 5d772527c..5cae4c3f7 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -1,5 +1,4 @@
-using MediaBrowser.Api.Playback.Hls;
-using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Configuration;
@@ -608,7 +607,7 @@ namespace MediaBrowser.Api.Playback
}
}
- // TODO: Perhaps also use original_size=1920x800
+ // TODO: Perhaps also use original_size=1920x800 ??
return string.Format("subtitles=filename='{0}'{1},setpts=PTS -{2}/TB",
subtitlePath.Replace('\\', '/').Replace(":/", "\\:/"),
charsetParam,
@@ -818,7 +817,7 @@ namespace MediaBrowser.Api.Playback
state.MediaPath = streamInfo.Path;
state.InputProtocol = MediaProtocol.File;
- await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false);
+ await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
}
else if (!string.IsNullOrEmpty(streamInfo.Url))
{
@@ -839,7 +838,7 @@ namespace MediaBrowser.Api.Playback
state.MediaPath = streamInfo.Path;
state.InputProtocol = MediaProtocol.File;
- await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false);
+ await Task.Delay(1500, cancellationTokenSource.Token).ConfigureAwait(false);
}
else if (!string.IsNullOrEmpty(streamInfo.Url))
{
@@ -944,19 +943,6 @@ namespace MediaBrowser.Api.Playback
{
await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false);
}
-
- // Allow a small amount of time to buffer a little
- // But not with HLS because it already has it's own wait
- if (state.IsInputVideo && TranscodingJobType != TranscodingJobType.Hls)
- {
- await Task.Delay(500, cancellationTokenSource.Token).ConfigureAwait(false);
- }
-
- // This is arbitrary, but add a little buffer time when internet streaming
- if (state.InputProtocol != MediaProtocol.File)
- {
- await Task.Delay(2500, cancellationTokenSource.Token).ConfigureAwait(false);
- }
}
private async void StartStreamingLog(StreamState state, Stream source, Stream target)
@@ -1459,11 +1445,6 @@ namespace MediaBrowser.Api.Playback
state.RunTimeTicks = recording.RunTimeTicks;
- if (recording.RecordingInfo.Status == RecordingStatus.InProgress)
- {
- await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
- }
-
state.OutputAudioSync = "1000";
state.InputVideoSync = "-1";
state.InputAudioSync = "1";
diff --git a/MediaBrowser.Api/Playback/EndlessStreamCopy.cs b/MediaBrowser.Api/Playback/EndlessStreamCopy.cs
deleted file mode 100644
index 40586261f..000000000
--- a/MediaBrowser.Api/Playback/EndlessStreamCopy.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Api.Playback
-{
- public class EndlessStreamCopy
- {
- public async Task CopyStream(Stream source, Stream target, CancellationToken cancellationToken)
- {
- long position = 0;
-
- while (!cancellationToken.IsCancellationRequested)
- {
- await source.CopyToAsync(target, 81920, cancellationToken).ConfigureAwait(false);
-
- var fsPosition = source.Position;
-
- var bytesRead = fsPosition - position;
-
- //Logger.Debug("Streamed {0} bytes from file {1}", bytesRead, path);
-
- if (bytesRead == 0)
- {
- await Task.Delay(100, cancellationToken).ConfigureAwait(false);
- }
-
- position = fsPosition;
- }
- }
- }
-}
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index 8eff75533..317c4455a 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -104,7 +104,10 @@ namespace MediaBrowser.Api.Playback.Hls
}
else
{
- await ApiEntryPoint.Instance.KillTranscodingJobs(state.Request.DeviceId, TranscodingJobType.Hls, p => true, false).ConfigureAwait(false);
+ if (!string.IsNullOrWhiteSpace(state.Request.DeviceId))
+ {
+ await ApiEntryPoint.Instance.KillTranscodingJobs(state.Request.DeviceId, TranscodingJobType.Hls, p => true, false).ConfigureAwait(false);
+ }
// If the playlist doesn't already exist, startup ffmpeg
try
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index 6657bf6de..a098d26da 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -342,7 +342,7 @@ namespace MediaBrowser.Api.Playback.Hls
AppendPlaylist(builder, playlistUrl, totalBitrate);
- if (state.VideoRequest.VideoBitRate.HasValue)
+ if (EnableAdaptiveBitrateStreaming(state))
{
var requestedVideoBitrate = state.VideoRequest.VideoBitRate.Value;
@@ -360,6 +360,17 @@ namespace MediaBrowser.Api.Playback.Hls
return builder.ToString();
}
+ private bool EnableAdaptiveBitrateStreaming(StreamState state)
+ {
+ if (string.IsNullOrWhiteSpace(state.MediaPath))
+ {
+ // Opening live streams is so slow it's not even worth it
+ return false;
+ }
+
+ return state.VideoRequest.VideoBitRate.HasValue;
+ }
+
private void AppendPlaylist(StringBuilder builder, string url, int bitrate)
{
builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + bitrate.ToString(UsCulture));
@@ -368,8 +379,8 @@ namespace MediaBrowser.Api.Playback.Hls
private int GetBitrateVariation(int bitrate)
{
- // By default, vary by just 200k
- var variation = 200000;
+ // By default, vary by just 100k
+ var variation = 100000;
if (bitrate >= 10000000)
{
@@ -391,6 +402,10 @@ namespace MediaBrowser.Api.Playback.Hls
{
variation = 300000;
}
+ else if (bitrate >= 600000)
+ {
+ variation = 200000;
+ }
return variation;
}
diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
index dd8960649..80428269b 100644
--- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
@@ -149,7 +149,7 @@ namespace MediaBrowser.Api.Playback.Progressive
using (state)
{
- return ResultFactory.GetStaticFileResult(Request, state.MediaPath, contentType, FileShare.Read, responseHeaders, isHeadRequest);
+ return ResultFactory.GetStaticFileResult(Request, state.MediaPath, contentType, null, FileShare.Read, responseHeaders, isHeadRequest);
}
}
@@ -160,7 +160,7 @@ namespace MediaBrowser.Api.Playback.Progressive
try
{
- return ResultFactory.GetStaticFileResult(Request, outputPath, contentType, FileShare.Read, responseHeaders, isHeadRequest);
+ return ResultFactory.GetStaticFileResult(Request, outputPath, contentType, null, FileShare.Read, responseHeaders, isHeadRequest);
}
finally
{
@@ -285,7 +285,8 @@ namespace MediaBrowser.Api.Playback.Progressive
state.Dispose();
}
- var result = new ProgressiveStreamWriter(outputPath, Logger, FileSystem);
+ var job = ApiEntryPoint.Instance.GetTranscodingJob(outputPath, TranscodingJobType.Progressive);
+ var result = new ProgressiveStreamWriter(outputPath, Logger, FileSystem, job);
result.Options["Content-Type"] = contentType;
diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
index 36ae7f100..a4c55443c 100644
--- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
+++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
@@ -12,6 +12,7 @@ namespace MediaBrowser.Api.Playback.Progressive
private string Path { get; set; }
private ILogger Logger { get; set; }
private readonly IFileSystem _fileSystem;
+ private readonly TranscodingJob _job;
/// <summary>
/// The _options
@@ -32,11 +33,12 @@ namespace MediaBrowser.Api.Playback.Progressive
/// <param name="path">The path.</param>
/// <param name="logger">The logger.</param>
/// <param name="fileSystem">The file system.</param>
- public ProgressiveStreamWriter(string path, ILogger logger, IFileSystem fileSystem)
+ public ProgressiveStreamWriter(string path, ILogger logger, IFileSystem fileSystem, TranscodingJob job)
{
Path = path;
Logger = logger;
_fileSystem = fileSystem;
+ _job = job;
}
/// <summary>
@@ -59,7 +61,8 @@ namespace MediaBrowser.Api.Playback.Progressive
{
try
{
- await new ProgressiveFileCopier(_fileSystem).StreamFile(Path, responseStream).ConfigureAwait(false);
+ await new ProgressiveFileCopier(_fileSystem, _job)
+ .StreamFile(Path, responseStream).ConfigureAwait(false);
}
catch
{
@@ -77,10 +80,12 @@ namespace MediaBrowser.Api.Playback.Progressive
public class ProgressiveFileCopier
{
private readonly IFileSystem _fileSystem;
+ private readonly TranscodingJob _job;
- public ProgressiveFileCopier(IFileSystem fileSystem)
+ public ProgressiveFileCopier(IFileSystem fileSystem, TranscodingJob job)
{
_fileSystem = fileSystem;
+ _job = job;
}
public async Task StreamFile(string path, Stream outputStream)
@@ -102,7 +107,10 @@ namespace MediaBrowser.Api.Playback.Progressive
if (bytesRead == 0)
{
- eofCount++;
+ if (_job == null || _job.HasExited)
+ {
+ eofCount++;
+ }
await Task.Delay(100).ConfigureAwait(false);
}
else
diff --git a/MediaBrowser.Api/Sync/SyncService.cs b/MediaBrowser.Api/Sync/SyncService.cs
new file mode 100644
index 000000000..57b5fa3f9
--- /dev/null
+++ b/MediaBrowser.Api/Sync/SyncService.cs
@@ -0,0 +1,130 @@
+using MediaBrowser.Controller.Net;
+using MediaBrowser.Controller.Sync;
+using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Sync;
+using ServiceStack;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Api.Sync
+{
+ [Route("/Sync/Jobs/{Id}", "DELETE", Summary = "Cancels a sync job.")]
+ public class CancelSyncJob : IReturnVoid
+ {
+ [ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Id { get; set; }
+ }
+
+ [Route("/Sync/Schedules/{Id}", "DELETE", Summary = "Cancels a sync job.")]
+ public class CancelSyncSchedule : IReturnVoid
+ {
+ [ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Id { get; set; }
+ }
+
+ [Route("/Sync/Jobs/{Id}", "GET", Summary = "Gets a sync job.")]
+ public class GetSyncJob : IReturn<SyncJob>
+ {
+ [ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Id { get; set; }
+ }
+
+ [Route("/Sync/Schedules/{Id}", "GET", Summary = "Gets a sync job.")]
+ public class GetSyncSchedule : IReturn<SyncSchedule>
+ {
+ [ApiMember(Name = "Id", Description = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Id { get; set; }
+ }
+
+ [Route("/Sync/Jobs", "GET", Summary = "Gets sync jobs.")]
+ public class GetSyncJobs : IReturn<QueryResult<SyncJob>>
+ {
+ }
+
+ [Route("/Sync/Schedules", "GET", Summary = "Gets sync schedules.")]
+ public class GetSyncSchedules : IReturn<QueryResult<SyncSchedule>>
+ {
+ }
+
+ [Route("/Sync/Jobs", "POST", Summary = "Gets sync jobs.")]
+ public class CreateSyncJob : SyncJobRequest
+ {
+ }
+
+ [Route("/Sync/Schedules", "POST", Summary = "Gets sync schedules.")]
+ public class CreateSyncSchedule : SyncScheduleRequest
+ {
+ }
+
+ [Authenticated]
+ public class SyncService : BaseApiService
+ {
+ private readonly ISyncManager _syncManager;
+
+ public SyncService(ISyncManager syncManager)
+ {
+ _syncManager = syncManager;
+ }
+
+ public object Get(GetSyncJobs request)
+ {
+ var result = _syncManager.GetJobs(new SyncJobQuery
+ {
+
+ });
+
+ return ToOptimizedResult(result);
+ }
+
+ public object Get(GetSyncSchedules request)
+ {
+ var result = _syncManager.GetSchedules(new SyncScheduleQuery
+ {
+
+ });
+
+ return ToOptimizedResult(result);
+ }
+
+ public object Get(GetSyncJob request)
+ {
+ var result = _syncManager.GetJob(request.Id);
+
+ return ToOptimizedResult(result);
+ }
+
+ public object Get(GetSyncSchedule request)
+ {
+ var result = _syncManager.GetSchedule(request.Id);
+
+ return ToOptimizedResult(result);
+ }
+
+ public void Delete(CancelSyncJob request)
+ {
+ var task = _syncManager.CancelJob(request.Id);
+
+ Task.WaitAll(task);
+ }
+
+ public void Delete(CancelSyncSchedule request)
+ {
+ var task = _syncManager.CancelSchedule(request.Id);
+
+ Task.WaitAll(task);
+ }
+
+ public void Post(CreateSyncJob request)
+ {
+ var task = _syncManager.CreateJob(request);
+
+ Task.WaitAll(task);
+ }
+
+ public void Post(CreateSyncSchedule request)
+ {
+ var task = _syncManager.CreateSchedule(request);
+
+ Task.WaitAll(task);
+ }
+ }
+}
diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs
index 0edd013df..b7a43c237 100644
--- a/MediaBrowser.Api/UserService.cs
+++ b/MediaBrowser.Api/UserService.cs
@@ -1,4 +1,5 @@
using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library;
@@ -11,6 +12,7 @@ using ServiceStack.Text.Controller;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Net;
using System.Threading.Tasks;
namespace MediaBrowser.Api
@@ -168,6 +170,7 @@ namespace MediaBrowser.Api
private readonly IDtoService _dtoService;
private readonly ISessionManager _sessionMananger;
private readonly IServerConfigurationManager _config;
+ private readonly INetworkManager _networkManager;
public IAuthorizationContext AuthorizationContext { get; set; }
@@ -178,12 +181,13 @@ namespace MediaBrowser.Api
/// <param name="dtoService">The dto service.</param>
/// <param name="sessionMananger">The session mananger.</param>
/// <exception cref="System.ArgumentNullException">xmlSerializer</exception>
- public UserService(IUserManager userManager, IDtoService dtoService, ISessionManager sessionMananger, IServerConfigurationManager config)
+ public UserService(IUserManager userManager, IDtoService dtoService, ISessionManager sessionMananger, IServerConfigurationManager config, INetworkManager networkManager)
{
_userManager = userManager;
_dtoService = dtoService;
_sessionMananger = sessionMananger;
_config = config;
+ _networkManager = networkManager;
}
public object Get(GetPublicUsers request)
@@ -200,18 +204,65 @@ namespace MediaBrowser.Api
});
}
- // TODO: Add or is authenticated
- if (_sessionMananger.IsInLocalNetwork(Request.RemoteIp))
+ // TODO: Uncomment this once all clients can handle an empty user list.
+ return Get(new GetUsers
{
- return Get(new GetUsers
+ IsHidden = false,
+ IsDisabled = false
+ });
+
+ //// TODO: Add or is authenticated
+ //if (Request.IsLocal || IsInLocalNetwork(Request.RemoteIp))
+ //{
+ // return Get(new GetUsers
+ // {
+ // IsHidden = false,
+ // IsDisabled = false
+ // });
+ //}
+
+ //// Return empty when external
+ //return ToOptimizedResult(new List<UserDto>());
+ }
+
+ private bool IsInLocalNetwork(string remoteEndpoint)
+ {
+ if (string.IsNullOrWhiteSpace(remoteEndpoint))
+ {
+ throw new ArgumentNullException("remoteEndpoint");
+ }
+
+ IPAddress address;
+ if (!IPAddress.TryParse(remoteEndpoint, out address))
+ {
+ return true;
+ }
+
+ const int lengthMatch = 4;
+
+ if (remoteEndpoint.Length >= lengthMatch)
+ {
+ var prefix = remoteEndpoint.Substring(0, lengthMatch);
+
+ if (_networkManager.GetLocalIpAddresses()
+ .Any(i => i.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)))
{
- IsHidden = false,
- IsDisabled = false
- });
+ return true;
+ }
}
- // Return empty when external
- return ToOptimizedResult(new List<UserDto>());
+ // Private address space:
+ // http://en.wikipedia.org/wiki/Private_network
+
+ return
+
+ // If url was requested with computer name, we may see this
+ remoteEndpoint.IndexOf("::", StringComparison.OrdinalIgnoreCase) != -1 ||
+
+ remoteEndpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase) ||
+ remoteEndpoint.StartsWith("192.", StringComparison.OrdinalIgnoreCase) ||
+ remoteEndpoint.StartsWith("172.", StringComparison.OrdinalIgnoreCase) ||
+ remoteEndpoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase);
}
/// <summary>
@@ -273,6 +324,10 @@ namespace MediaBrowser.Api
throw new ResourceNotFoundException("User not found");
}
+ var revokeTask = _sessionMananger.RevokeUserTokens(user.Id.ToString("N"));
+
+ Task.WaitAll(revokeTask);
+
var task = _userManager.DeleteUser(user);
Task.WaitAll(task);
@@ -302,8 +357,25 @@ namespace MediaBrowser.Api
{
var auth = AuthorizationContext.GetAuthorizationInfo(Request);
+ if (string.IsNullOrWhiteSpace(auth.Client))
+ {
+ auth.Client = "Unknown app";
+ }
+ if (string.IsNullOrWhiteSpace(auth.Device))
+ {
+ auth.Device = "Unknown device";
+ }
+ if (string.IsNullOrWhiteSpace(auth.Version))
+ {
+ auth.Version = "Unknown version";
+ }
+ if (string.IsNullOrWhiteSpace(auth.DeviceId))
+ {
+ auth.DeviceId = "Unknown device id";
+ }
+
var result = _sessionMananger.AuthenticateNewSession(request.Username, request.Password, auth.Client, auth.Version,
- auth.DeviceId, auth.Device, Request.RemoteIp).Result;
+ auth.DeviceId, auth.Device, Request.RemoteIp, Request.IsLocal).Result;
return ToOptimizedResult(result);
}