added IHasImages and IHasUserData

pull/702/head
Luke Pulverenti 11 years ago
parent e1e5d35434
commit cd859ac2e6

@ -1,17 +1,17 @@
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using ServiceStack;
using ServiceStack.Text.Controller;
using ServiceStack.Web;
using System;
using System.Collections.Generic;
using System.Drawing;
@ -19,8 +19,6 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ServiceStack.Text.Controller;
using ServiceStack.Web;
namespace MediaBrowser.Api.Images
{
@ -39,18 +37,6 @@ namespace MediaBrowser.Api.Images
public string Id { get; set; }
}
[Route("/LiveTv/Channels/{Id}/Images", "GET")]
[Api(Description = "Gets information about an item's images")]
public class GetChannelImageInfos : IReturn<List<ImageInfo>>
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
[Route("/Artists/{Name}/Images", "GET")]
[Route("/Genres/{Name}/Images", "GET")]
[Route("/GameGenres/{Name}/Images", "GET")]
@ -80,20 +66,7 @@ namespace MediaBrowser.Api.Images
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
[Route("/LiveTv/Channels/{Id}/Images/{Type}", "GET")]
[Route("/LiveTv/Channels/{Id}/Images/{Type}/{Index}", "GET")]
[Api(Description = "Gets an item image")]
public class GetChannelImage : ImageRequest
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Channel Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
/// <summary>
/// Class UpdateItemImageIndex
/// </summary>
@ -270,19 +243,6 @@ namespace MediaBrowser.Api.Images
public Guid Id { get; set; }
}
[Route("/LiveTv/Channels/{Id}/Images/{Type}", "DELETE")]
[Route("/LiveTv/Channels/{Id}/Images/{Type}/{Index}", "DELETE")]
[Api(Description = "Deletes an item image")]
public class DeleteChannelImage : DeleteImageRequest, IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Channel Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
public string Id { get; set; }
}
/// <summary>
/// Class PostUserImage
/// </summary>
@ -358,38 +318,13 @@ namespace MediaBrowser.Api.Images
public Stream RequestStream { get; set; }
}
[Route("/LiveTv/Channels/{Id}/Images/{Type}", "POST")]
[Route("/LiveTv/Channels/{Id}/Images/{Type}/{Index}", "POST")]
[Api(Description = "Posts an item image")]
public class PostChannelImage : DeleteImageRequest, IRequiresRequestStream, IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
/// <summary>
/// The raw Http Request Input Stream
/// </summary>
/// <value>The request stream.</value>
public Stream RequestStream { get; set; }
}
/// <summary>
/// Class ImageService
/// </summary>
public class ImageService : BaseApiService
{
/// <summary>
/// The _user manager
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
/// The _library manager
/// </summary>
private readonly ILibraryManager _libraryManager;
private readonly IApplicationPaths _appPaths;
@ -400,12 +335,11 @@ namespace MediaBrowser.Api.Images
private readonly IDtoService _dtoService;
private readonly IImageProcessor _imageProcessor;
private readonly ILiveTvManager _liveTv;
/// <summary>
/// Initializes a new instance of the <see cref="ImageService" /> class.
/// </summary>
public ImageService(IUserManager userManager, ILibraryManager libraryManager, IApplicationPaths appPaths, IProviderManager providerManager, IItemRepository itemRepo, IDtoService dtoService, IImageProcessor imageProcessor, ILiveTvManager liveTv)
public ImageService(IUserManager userManager, ILibraryManager libraryManager, IApplicationPaths appPaths, IProviderManager providerManager, IItemRepository itemRepo, IDtoService dtoService, IImageProcessor imageProcessor)
{
_userManager = userManager;
_libraryManager = libraryManager;
@ -414,7 +348,6 @@ namespace MediaBrowser.Api.Images
_itemRepo = itemRepo;
_dtoService = dtoService;
_imageProcessor = imageProcessor;
_liveTv = liveTv;
}
/// <summary>
@ -431,15 +364,6 @@ namespace MediaBrowser.Api.Images
return ToOptimizedResult(result);
}
public object Get(GetChannelImageInfos request)
{
var item = _liveTv.GetChannel(request.Id);
var result = GetItemImageInfos(item);
return ToOptimizedResult(result);
}
public object Get(GetItemByNameImageInfos request)
{
var result = GetItemByNameImageInfos(request);
@ -540,7 +464,7 @@ namespace MediaBrowser.Api.Images
return list;
}
private ImageInfo GetImageInfo(string path, BaseItem item, int? imageIndex, ImageType type)
private ImageInfo GetImageInfo(string path, IHasImages item, int? imageIndex, ImageType type)
{
try
{
@ -567,13 +491,6 @@ namespace MediaBrowser.Api.Images
}
}
public object Get(GetChannelImage request)
{
var item = _liveTv.GetChannel(request.Id);
return GetImage(request, item);
}
/// <summary>
/// Gets the specified request.
/// </summary>
@ -659,20 +576,6 @@ namespace MediaBrowser.Api.Images
Task.WaitAll(task);
}
public void Post(PostChannelImage request)
{
var pathInfo = PathInfo.Parse(Request.PathInfo);
var id = pathInfo.GetArgumentValue<string>(2);
request.Type = (ImageType)Enum.Parse(typeof(ImageType), pathInfo.GetArgumentValue<string>(4), true);
var item = _liveTv.GetChannel(id);
var task = PostImage(item, request.RequestStream, request.Type, Request.ContentType);
Task.WaitAll(task);
}
/// <summary>
/// Deletes the specified request.
/// </summary>
@ -699,15 +602,6 @@ namespace MediaBrowser.Api.Images
Task.WaitAll(task);
}
public void Delete(DeleteChannelImage request)
{
var item = _liveTv.GetChannel(request.Id);
var task = item.DeleteImage(request.Type, request.Index);
Task.WaitAll(task);
}
/// <summary>
/// Deletes the specified request.
/// </summary>
@ -762,71 +656,9 @@ namespace MediaBrowser.Api.Images
/// <param name="newIndex">The new index.</param>
/// <returns>Task.</returns>
/// <exception cref="System.ArgumentException">The change index operation is only applicable to backdrops and screenshots</exception>
private Task UpdateItemIndex(BaseItem item, ImageType type, int currentIndex, int newIndex)
private Task UpdateItemIndex(IHasImages item, ImageType type, int currentIndex, int newIndex)
{
string file1;
string file2;
if (type == ImageType.Screenshot)
{
var hasScreenshots = (IHasScreenshots)item;
file1 = hasScreenshots.ScreenshotImagePaths[currentIndex];
file2 = hasScreenshots.ScreenshotImagePaths[newIndex];
}
else if (type == ImageType.Backdrop)
{
file1 = item.BackdropImagePaths[currentIndex];
file2 = item.BackdropImagePaths[newIndex];
}
else
{
throw new ArgumentException("The change index operation is only applicable to backdrops and screenshots");
}
SwapFiles(file1, file2);
// Directory watchers should repeat this, but do a quick refresh first
return item.RefreshMetadata(CancellationToken.None, forceSave: true, allowSlowProviders: false);
}
/// <summary>
/// Swaps the files.
/// </summary>
/// <param name="file1">The file1.</param>
/// <param name="file2">The file2.</param>
private void SwapFiles(string file1, string file2)
{
Directory.CreateDirectory(_appPaths.TempDirectory);
var temp1 = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + ".tmp");
var temp2 = Path.Combine(_appPaths.TempDirectory, Guid.NewGuid() + ".tmp");
// Copying over will fail against hidden files
RemoveHiddenAttribute(file1);
RemoveHiddenAttribute(file2);
File.Copy(file1, temp1);
File.Copy(file2, temp2);
File.Copy(temp1, file2, true);
File.Copy(temp2, file1, true);
File.Delete(temp1);
File.Delete(temp2);
}
private void RemoveHiddenAttribute(string path)
{
var currentFile = new FileInfo(path);
// This will fail if the file is hidden
if (currentFile.Exists)
{
if ((currentFile.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
{
currentFile.Attributes &= ~FileAttributes.Hidden;
}
}
return item.SwapImages(type, currentIndex, newIndex);
}
/// <summary>
@ -837,7 +669,7 @@ namespace MediaBrowser.Api.Images
/// <returns>System.Object.</returns>
/// <exception cref="ResourceNotFoundException">
/// </exception>
private object GetImage(ImageRequest request, BaseItem item)
public object GetImage(ImageRequest request, IHasImages item)
{
var imagePath = GetImagePath(request, item);
@ -926,7 +758,7 @@ namespace MediaBrowser.Api.Images
/// <param name="request">The request.</param>
/// <param name="item">The item.</param>
/// <returns>System.String.</returns>
private string GetImagePath(ImageRequest request, BaseItem item)
private string GetImagePath(ImageRequest request, IHasImages item)
{
var index = request.Index ?? 0;
@ -941,7 +773,7 @@ namespace MediaBrowser.Api.Images
/// <param name="imageType">Type of the image.</param>
/// <param name="mimeType">Type of the MIME.</param>
/// <returns>Task.</returns>
private async Task PostImage(BaseItem entity, Stream inputStream, ImageType imageType, string mimeType)
public async Task PostImage(BaseItem entity, Stream inputStream, ImageType imageType, string mimeType)
{
using (var reader = new StreamReader(inputStream))
{

@ -2,12 +2,11 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using ServiceStack;
using ServiceStack.Web;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using ServiceStack.Web;
namespace MediaBrowser.Api.Images
{
@ -27,7 +26,7 @@ namespace MediaBrowser.Api.Images
/// Gets or sets the item.
/// </summary>
/// <value>The item.</value>
public BaseItem Item { get; set; }
public IHasImages Item { get; set; }
/// <summary>
/// The original image date modified
/// </summary>

@ -146,7 +146,7 @@ namespace MediaBrowser.Api
private async Task UpdateItem(UpdateChannel request)
{
var item = _liveTv.GetChannel(request.Id);
var item = _liveTv.GetInternalChannel(request.Id);
UpdateItem(request, item);

@ -0,0 +1,184 @@
using MediaBrowser.Api.Images;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using ServiceStack;
using ServiceStack.Text.Controller;
using ServiceStack.Web;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Api.LiveTv
{
[Route("/LiveTv/Channels/{Id}/Images/{Type}", "POST")]
[Route("/LiveTv/Channels/{Id}/Images/{Type}/{Index}", "POST")]
[Api(Description = "Posts an item image")]
public class PostChannelImage : DeleteImageRequest, IRequiresRequestStream, IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
/// <summary>
/// The raw Http Request Input Stream
/// </summary>
/// <value>The request stream.</value>
public Stream RequestStream { get; set; }
}
[Route("/LiveTv/Channels/{Id}/Images/{Type}", "DELETE")]
[Route("/LiveTv/Channels/{Id}/Images/{Type}/{Index}", "DELETE")]
[Api(Description = "Deletes an item image")]
public class DeleteChannelImage : DeleteImageRequest, IReturnVoid
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Channel Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
public string Id { get; set; }
}
[Route("/LiveTv/Channels/{Id}/Images/{Type}", "GET")]
[Route("/LiveTv/Channels/{Id}/Images/{Type}/{Index}", "GET")]
[Api(Description = "Gets an item image")]
public class GetChannelImage : ImageRequest
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Channel Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
[Route("/LiveTv/Recordings/{Id}/Images/{Type}", "GET")]
[Route("/LiveTv/Recordings/{Id}/Images/{Type}/{Index}", "GET")]
[Api(Description = "Gets an item image")]
public class GetRecordingImage : ImageRequest
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Recording Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
[Route("/LiveTv/Programs/{Id}/Images/{Type}", "GET")]
[Route("/LiveTv/Programs/{Id}/Images/{Type}/{Index}", "GET")]
[Api(Description = "Gets an item image")]
public class GetProgramImage : ImageRequest
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Program Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
[Route("/LiveTv/Channels/{Id}/Images", "GET")]
[Api(Description = "Gets information about an item's images")]
public class GetChannelImageInfos : IReturn<List<ImageInfo>>
{
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <value>The id.</value>
[ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
public string Id { get; set; }
}
public class LiveTvImageService : BaseApiService
{
private readonly ILiveTvManager _liveTv;
private readonly IUserManager _userManager;
private readonly ILibraryManager _libraryManager;
private readonly IApplicationPaths _appPaths;
private readonly IProviderManager _providerManager;
private readonly IItemRepository _itemRepo;
private readonly IDtoService _dtoService;
private readonly IImageProcessor _imageProcessor;
public LiveTvImageService(ILiveTvManager liveTv, IUserManager userManager, ILibraryManager libraryManager, IApplicationPaths appPaths, IProviderManager providerManager, IItemRepository itemRepo, IDtoService dtoService, IImageProcessor imageProcessor)
{
_liveTv = liveTv;
_userManager = userManager;
_libraryManager = libraryManager;
_appPaths = appPaths;
_providerManager = providerManager;
_itemRepo = itemRepo;
_dtoService = dtoService;
_imageProcessor = imageProcessor;
}
public object Get(GetChannelImageInfos request)
{
var item = _liveTv.GetInternalChannel(request.Id);
var result = GetImageService().GetItemImageInfos(item);
return ToOptimizedResult(result);
}
public object Get(GetChannelImage request)
{
var item = _liveTv.GetInternalChannel(request.Id);
return GetImageService().GetImage(request, item);
}
public object Get(GetRecordingImage request)
{
var item = _liveTv.GetInternalRecording(request.Id, CancellationToken.None).Result;
return GetImageService().GetImage(request, item);
}
public void Post(PostChannelImage request)
{
var pathInfo = PathInfo.Parse(Request.PathInfo);
var id = pathInfo.GetArgumentValue<string>(2);
request.Type = (ImageType)Enum.Parse(typeof(ImageType), pathInfo.GetArgumentValue<string>(4), true);
var item = _liveTv.GetInternalChannel(id);
var task = GetImageService().PostImage(item, request.RequestStream, request.Type, Request.ContentType);
Task.WaitAll(task);
}
public void Delete(DeleteChannelImage request)
{
var item = _liveTv.GetInternalChannel(request.Id);
var task = item.DeleteImage(request.Type, request.Index);
Task.WaitAll(task);
}
private ImageService GetImageService()
{
return new ImageService(_userManager, _libraryManager, _appPaths, _providerManager, _itemRepo, _dtoService,
_imageProcessor);
}
}
}

@ -90,6 +90,7 @@
<Compile Include="Library\LibraryHelpers.cs" />
<Compile Include="Library\LibraryService.cs" />
<Compile Include="Library\LibraryStructureService.cs" />
<Compile Include="LiveTv\LiveTvImageService.cs" />
<Compile Include="LiveTv\LiveTvService.cs" />
<Compile Include="LocalizationService.cs" />
<Compile Include="MoviesService.cs" />

@ -1,11 +1,9 @@
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.MediaInfo;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaInfo;
using MediaBrowser.Controller.Persistence;
@ -110,7 +108,7 @@ namespace MediaBrowser.Api.Playback
/// <returns>System.String.</returns>
protected virtual string GetOutputFileExtension(StreamState state)
{
return Path.GetExtension(state.Url);
return Path.GetExtension(state.RequestedUrl);
}
/// <summary>
@ -187,7 +185,7 @@ namespace MediaBrowser.Api.Playback
{
var args = string.Empty;
if (state.Item.LocationType == LocationType.Remote)
if (state.IsRemote)
{
return string.Empty;
}
@ -308,7 +306,7 @@ namespace MediaBrowser.Api.Playback
return args.Trim();
}
/// <summary>
/// If we're going to put a fixed size on the command line, this will calculate it
/// </summary>
@ -331,7 +329,7 @@ namespace MediaBrowser.Api.Playback
string.Equals(state.SubtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase) ||
string.Equals(state.SubtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase))
{
assSubtitleParam = GetTextSubtitleParam((Video)state.Item, state.SubtitleStream, request.StartTimeTicks, performTextSubtitleConversion);
assSubtitleParam = GetTextSubtitleParam(state, request.StartTimeTicks, performTextSubtitleConversion);
}
}
@ -402,14 +400,14 @@ namespace MediaBrowser.Api.Playback
/// <summary>
/// Gets the text subtitle param.
/// </summary>
/// <param name="video">The video.</param>
/// <param name="subtitleStream">The subtitle stream.</param>
/// <param name="state">The state.</param>
/// <param name="startTimeTicks">The start time ticks.</param>
/// <param name="performConversion">if set to <c>true</c> [perform conversion].</param>
/// <returns>System.String.</returns>
protected string GetTextSubtitleParam(Video video, MediaStream subtitleStream, long? startTimeTicks, bool performConversion)
protected string GetTextSubtitleParam(StreamState state, long? startTimeTicks, bool performConversion)
{
var path = subtitleStream.IsExternal ? GetConvertedAssPath(video, subtitleStream, startTimeTicks, performConversion) : GetExtractedAssPath(video, subtitleStream, startTimeTicks, performConversion);
var path = state.SubtitleStream.IsExternal ? GetConvertedAssPath(state.MediaPath, state.SubtitleStream, startTimeTicks, performConversion) :
GetExtractedAssPath(state, startTimeTicks, performConversion);
if (string.IsNullOrEmpty(path))
{
@ -422,22 +420,21 @@ namespace MediaBrowser.Api.Playback
/// <summary>
/// Gets the extracted ass path.
/// </summary>
/// <param name="video">The video.</param>
/// <param name="subtitleStream">The subtitle stream.</param>
/// <param name="state">The state.</param>
/// <param name="startTimeTicks">The start time ticks.</param>
/// <param name="performConversion">if set to <c>true</c> [perform conversion].</param>
/// <returns>System.String.</returns>
private string GetExtractedAssPath(Video video, MediaStream subtitleStream, long? startTimeTicks, bool performConversion)
private string GetExtractedAssPath(StreamState state, long? startTimeTicks, bool performConversion)
{
var offset = TimeSpan.FromTicks(startTimeTicks ?? 0);
var path = FFMpegManager.Instance.GetSubtitleCachePath(video, subtitleStream.Index, offset, ".ass");
var path = FFMpegManager.Instance.GetSubtitleCachePath(state.MediaPath, state.SubtitleStream, offset, ".ass");
if (performConversion)
{
InputType type;
var inputPath = MediaEncoderHelpers.GetInputArgument(video, null, out type);
var inputPath = MediaEncoderHelpers.GetInputArgument(state.MediaPath, state.IsRemote, state.VideoType, state.IsoType, null, state.PlayableStreamFileNames, out type);
try
{
@ -445,7 +442,7 @@ namespace MediaBrowser.Api.Playback
Directory.CreateDirectory(parentPath);
var task = MediaEncoder.ExtractTextSubtitle(inputPath, type, subtitleStream.Index, offset, path, CancellationToken.None);
var task = MediaEncoder.ExtractTextSubtitle(inputPath, type, state.SubtitleStream.Index, offset, path, CancellationToken.None);
Task.WaitAll(task);
}
@ -461,22 +458,16 @@ namespace MediaBrowser.Api.Playback
/// <summary>
/// Gets the converted ass path.
/// </summary>
/// <param name="video">The video.</param>
/// <param name="mediaPath">The media path.</param>
/// <param name="subtitleStream">The subtitle stream.</param>
/// <param name="startTimeTicks">The start time ticks.</param>
/// <param name="performConversion">if set to <c>true</c> [perform conversion].</param>
/// <returns>System.String.</returns>
private string GetConvertedAssPath(Video video, MediaStream subtitleStream, long? startTimeTicks, bool performConversion)
private string GetConvertedAssPath(string mediaPath, MediaStream subtitleStream, long? startTimeTicks, bool performConversion)
{
// If it's already ass, no conversion neccessary
//if (string.Equals(Path.GetExtension(subtitleStream.Path), ".ass", StringComparison.OrdinalIgnoreCase))
//{
// return subtitleStream.Path;
//}
var offset = TimeSpan.FromTicks(startTimeTicks ?? 0);
var path = FFMpegManager.Instance.GetSubtitleCachePath(video, subtitleStream.Index, offset, ".ass");
var path = FFMpegManager.Instance.GetSubtitleCachePath(mediaPath, subtitleStream, offset, ".ass");
if (performConversion)
{
@ -524,25 +515,15 @@ namespace MediaBrowser.Api.Playback
/// <summary>
/// Gets the probe size argument.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="mediaPath">The media path.</param>
/// <param name="isVideo">if set to <c>true</c> [is video].</param>
/// <param name="videoType">Type of the video.</param>
/// <param name="isoType">Type of the iso.</param>
/// <returns>System.String.</returns>
protected string GetProbeSizeArgument(BaseItem item)
protected string GetProbeSizeArgument(string mediaPath, bool isVideo, VideoType? videoType, IsoType? isoType)
{
var type = InputType.AudioFile;
if (item is Audio)
{
type = MediaEncoderHelpers.GetInputType(item.Path, null, null);
}
else
{
var video = item as Video;
if (video != null)
{
type = MediaEncoderHelpers.GetInputType(item.Path, video.VideoType, video.IsoType);
}
}
var type = !isVideo ? MediaEncoderHelpers.GetInputType(mediaPath, null, null) :
MediaEncoderHelpers.GetInputType(mediaPath, videoType, isoType);
return MediaEncoder.GetProbeSizeArgument(type);
}
@ -652,22 +633,19 @@ namespace MediaBrowser.Api.Playback
/// <summary>
/// Gets the input argument.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="isoMount">The iso mount.</param>
/// <param name="state">The state.</param>
/// <returns>System.String.</returns>
protected string GetInputArgument(BaseItem item, IIsoMount isoMount)
protected string GetInputArgument(StreamState state)
{
var type = InputType.AudioFile;
var inputPath = new[] { item.Path };
var video = item as Video;
var inputPath = new[] { state.MediaPath };
if (video != null)
if (state.IsInputVideo)
{
if (!(video.VideoType == VideoType.Iso && isoMount == null))
if (!(state.VideoType == VideoType.Iso && state.IsoMount == null))
{
inputPath = MediaEncoderHelpers.GetInputArgument(video, isoMount, out type);
inputPath = MediaEncoderHelpers.GetInputArgument(state.MediaPath, state.IsRemote, state.VideoType, state.IsoType, state.IsoMount, state.PlayableStreamFileNames, out type);
}
}
@ -686,11 +664,9 @@ namespace MediaBrowser.Api.Playback
Directory.CreateDirectory(parentPath);
var video = state.Item as Video;
if (video != null && video.VideoType == VideoType.Iso && video.IsoType.HasValue && IsoManager.CanMount(video.Path))
if (state.IsInputVideo && state.VideoType == VideoType.Iso && state.IsoType.HasValue && IsoManager.CanMount(state.MediaPath))
{
state.IsoMount = await IsoManager.Mount(video.Path, CancellationToken.None).ConfigureAwait(false);
state.IsoMount = await IsoManager.Mount(state.MediaPath, CancellationToken.None).ConfigureAwait(false);
}
var process = new Process
@ -715,7 +691,7 @@ namespace MediaBrowser.Api.Playback
EnableRaisingEvents = true
};
ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath, TranscodingJobType, process, video != null, state.Request.StartTimeTicks, state.Item.Path, state.Request.DeviceId);
ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath, TranscodingJobType, process, state.IsInputVideo, state.Request.StartTimeTicks, state.MediaPath, state.Request.DeviceId);
Logger.Info(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
@ -754,13 +730,13 @@ namespace MediaBrowser.Api.Playback
}
// Allow a small amount of time to buffer a little
if (state.Item is Video)
if (state.IsInputVideo)
{
await Task.Delay(500).ConfigureAwait(false);
}
// This is arbitrary, but add a little buffer time when internet streaming
if (state.Item.LocationType == LocationType.Remote)
if (state.IsRemote)
{
await Task.Delay(4000).ConfigureAwait(false);
}
@ -787,11 +763,11 @@ namespace MediaBrowser.Api.Playback
/// <summary>
/// Gets the user agent param.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="path">The path.</param>
/// <returns>System.String.</returns>
protected string GetUserAgentParam(BaseItem item)
protected string GetUserAgentParam(string path)
{
var useragent = GetUserAgent(item);
var useragent = GetUserAgent(path);
if (!string.IsNullOrEmpty(useragent))
{
@ -804,11 +780,11 @@ namespace MediaBrowser.Api.Playback
/// <summary>
/// Gets the user agent.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="path">The path.</param>
/// <returns>System.String.</returns>
protected string GetUserAgent(BaseItem item)
protected string GetUserAgent(string path)
{
if (item.Path.IndexOf("apple.com", StringComparison.OrdinalIgnoreCase) != -1)
if (path.IndexOf("apple.com", StringComparison.OrdinalIgnoreCase) != -1)
{
return "QuickTime/7.7.4";
}
@ -852,8 +828,6 @@ namespace MediaBrowser.Api.Playback
{
var item = DtoService.GetItemByDtoId(request.Id);
var media = (IHasMediaStreams)item;
var url = Request.PathInfo;
if (!request.AudioCodec.HasValue)
@ -863,11 +837,25 @@ namespace MediaBrowser.Api.Playback
var state = new StreamState
{
Item = item,
Request = request,
Url = url
RequestedUrl = url,
MediaPath = item.Path,
IsRemote = item.LocationType == LocationType.Remote
};
var video = item as Video;
if (video != null)
{
state.IsInputVideo = true;
state.VideoType = video.VideoType;
state.IsoType = video.IsoType;
state.PlayableStreamFileNames = video.PlayableStreamFileNames == null
? new List<string>()
: video.PlayableStreamFileNames.ToList();
}
var videoRequest = request as VideoStreamRequest;
var mediaStreams = ItemRepository.GetMediaStreams(new MediaStreamQuery

@ -4,7 +4,6 @@ using MediaBrowser.Common.MediaInfo;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
@ -247,7 +246,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// <returns>System.String.</returns>
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool performSubtitleConversions)
{
var probeSize = GetProbeSizeArgument(state.Item);
var probeSize = GetProbeSizeArgument(state.MediaPath, state.IsInputVideo, state.VideoType, state.IsoType);
var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream;
@ -262,9 +261,9 @@ namespace MediaBrowser.Api.Playback.Hls
var args = string.Format("{0}{1} {2} {3} -i {4}{5} -threads {6} {7} {8} -sc_threshold 0 {9} -hls_time 10 -start_number 0 -hls_list_size 1440 \"{10}\"",
itsOffset,
probeSize,
GetUserAgentParam(state.Item),
GetUserAgentParam(state.MediaPath),
GetFastSeekCommandLineParameter(state.Request),
GetInputArgument(state.Item, state.IsoMount),
GetInputArgument(state),
GetSlowSeekCommandLineParameter(state.Request),
threads,
GetMapArgs(state),
@ -275,7 +274,7 @@ namespace MediaBrowser.Api.Playback.Hls
if (hlsVideoRequest != null)
{
if (hlsVideoRequest.AppendBaselineStream && state.Item is Video)
if (hlsVideoRequest.AppendBaselineStream && state.IsInputVideo)
{
var lowBitratePath = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath) + "-low.m3u8");

@ -105,7 +105,7 @@ namespace MediaBrowser.Api.Playback.Progressive
return string.Format("{0} -i {1}{2} -threads {3}{4} {5} -id3v2_version 3 -write_id3v1 1 \"{6}\"",
GetFastSeekCommandLineParameter(request),
GetInputArgument(state.Item, state.IsoMount),
GetInputArgument(state),
GetSlowSeekCommandLineParameter(request),
threads,
vn,

@ -1,16 +1,12 @@
using MediaBrowser.Api.Images;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.MediaInfo;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using System.Collections.Generic;
using System.IO;
@ -51,9 +47,7 @@ namespace MediaBrowser.Api.Playback.Progressive
// Try to infer based on the desired video codec
if (videoRequest != null && videoRequest.VideoCodec.HasValue)
{
var video = state.Item as Video;
if (video != null)
if (state.IsInputVideo)
{
switch (videoRequest.VideoCodec.Value)
{
@ -72,9 +66,7 @@ namespace MediaBrowser.Api.Playback.Progressive
// Try to infer based on the desired audio codec
if (state.Request.AudioCodec.HasValue)
{
var audio = state.Item as Audio;
if (audio != null)
if (!state.IsInputVideo)
{
switch (state.Request.AudioCodec.Value)
{
@ -188,16 +180,11 @@ namespace MediaBrowser.Api.Playback.Progressive
{
var state = GetState(request);
if (request.AlbumArt)
{
return GetAlbumArtResponse(state);
}
var responseHeaders = new Dictionary<string, string>();
if (request.Static && state.Item.LocationType == LocationType.Remote)
if (request.Static && state.IsRemote)
{
return GetStaticRemoteStreamResult(state.Item, responseHeaders, isHeadRequest).Result;
return GetStaticRemoteStreamResult(state.MediaPath, responseHeaders, isHeadRequest).Result;
}
var outputPath = GetOutputFilePath(state);
@ -210,7 +197,7 @@ namespace MediaBrowser.Api.Playback.Progressive
if (request.Static)
{
return ResultFactory.GetStaticFileResult(Request, state.Item.Path, FileShare.Read, responseHeaders, isHeadRequest);
return ResultFactory.GetStaticFileResult(Request, state.MediaPath, FileShare.Read, responseHeaders, isHeadRequest);
}
if (outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive))
@ -224,19 +211,19 @@ namespace MediaBrowser.Api.Playback.Progressive
/// <summary>
/// Gets the static remote stream result.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="mediaPath">The media path.</param>
/// <param name="responseHeaders">The response headers.</param>
/// <param name="isHeadRequest">if set to <c>true</c> [is head request].</param>
/// <returns>Task{System.Object}.</returns>
private async Task<object> GetStaticRemoteStreamResult(BaseItem item, Dictionary<string, string> responseHeaders, bool isHeadRequest)
private async Task<object> GetStaticRemoteStreamResult(string mediaPath, Dictionary<string, string> responseHeaders, bool isHeadRequest)
{
responseHeaders["Accept-Ranges"] = "none";
var httpClient = new HttpClient();
using (var message = new HttpRequestMessage(HttpMethod.Get, item.Path))
using (var message = new HttpRequestMessage(HttpMethod.Get, mediaPath))
{
var useragent = GetUserAgent(item);
var useragent = GetUserAgent(mediaPath);
if (!string.IsNullOrEmpty(useragent))
{
@ -272,47 +259,6 @@ namespace MediaBrowser.Api.Playback.Progressive
}
}
/// <summary>
/// Gets the album art response.
/// </summary>
/// <param name="state">The state.</param>
/// <returns>System.Object.</returns>
private object GetAlbumArtResponse(StreamState state)
{
var request = new GetItemImage
{
MaxWidth = 800,
MaxHeight = 800,
Type = ImageType.Primary,
Id = state.Item.Id.ToString()
};
// Try and find some image to return
if (!state.Item.HasImage(ImageType.Primary))
{
if (state.Item.HasImage(ImageType.Backdrop))
{
request.Type = ImageType.Backdrop;
}
else if (state.Item.HasImage(ImageType.Thumb))
{
request.Type = ImageType.Thumb;
}
else if (state.Item.HasImage(ImageType.Logo))
{
request.Type = ImageType.Logo;
}
}
return new ImageService(UserManager, LibraryManager, ServerConfigurationManager.ApplicationPaths, null, ItemRepository, DtoService, ImageProcessor, null)
{
Logger = Logger,
Request = Request,
ResultFactory = ResultFactory
}.Get(request);
}
/// <summary>
/// Gets the stream result.
/// </summary>

@ -3,7 +3,6 @@ using MediaBrowser.Common.MediaInfo;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Model.IO;
@ -89,9 +88,7 @@ namespace MediaBrowser.Api.Playback.Progressive
/// <returns>System.String.</returns>
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool performSubtitleConversions)
{
var video = (Video)state.Item;
var probeSize = GetProbeSizeArgument(state.Item);
var probeSize = GetProbeSizeArgument(state.MediaPath, state.IsInputVideo, state.VideoType, state.IsoType);
// Get the output codec name
var videoCodec = GetVideoCodec(state.VideoRequest);
@ -108,9 +105,9 @@ namespace MediaBrowser.Api.Playback.Progressive
return string.Format("{0} {1} {2} -i {3}{4}{5} {6} {7} -threads {8} {9}{10} \"{11}\"",
probeSize,
GetUserAgentParam(state.Item),
GetUserAgentParam(state.MediaPath),
GetFastSeekCommandLineParameter(state.Request),
GetInputArgument(video, state.IsoMount),
GetInputArgument(state),
GetSlowSeekCommandLineParameter(state.Request),
keyFrame,
GetMapArgs(state),

@ -1,14 +1,13 @@
using System.IO;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using System.Collections.Generic;
using System.IO;
namespace MediaBrowser.Api.Playback
{
public class StreamState
{
public string Url { get; set; }
public string RequestedUrl { get; set; }
public StreamRequest Request { get; set; }
@ -29,12 +28,22 @@ namespace MediaBrowser.Api.Playback
public MediaStream SubtitleStream { get; set; }
public BaseItem Item { get; set; }
/// <summary>
/// Gets or sets the iso mount.
/// </summary>
/// <value>The iso mount.</value>
public IIsoMount IsoMount { get; set; }
public string MediaPath { get; set; }
public bool IsRemote { get; set; }
public bool IsInputVideo { get; set; }
public VideoType VideoType { get; set; }
public IsoType? IsoType { get; set; }
public List<string> PlayableStreamFileNames { get; set; }
}
}

@ -153,7 +153,7 @@ namespace MediaBrowser.Api
if (item.HasImage(ImageType.Primary))
{
result.PrimaryImageTag = _imageProcessor.GetImageCacheTag(item, ImageType.Primary, item.GetImage(ImageType.Primary));
result.PrimaryImageTag = _imageProcessor.GetImageCacheTag(item, ImageType.Primary, item.GetImagePath(ImageType.Primary));
}
var episode = item as Episode;

@ -216,6 +216,48 @@ namespace MediaBrowser.Common.Implementations.IO
return new FileStream(path, mode, access, share);
}
/// <summary>
/// Swaps the files.
/// </summary>
/// <param name="file1">The file1.</param>
/// <param name="file2">The file2.</param>
public void SwapFiles(string file1, string file2)
{
var temp1 = Path.GetTempFileName();
var temp2 = Path.GetTempFileName();
// Copying over will fail against hidden files
RemoveHiddenAttribute(file1);
RemoveHiddenAttribute(file2);
File.Copy(file1, temp1, true);
File.Copy(file2, temp2, true);
File.Copy(temp1, file2, true);
File.Copy(temp2, file1, true);
File.Delete(temp1);
File.Delete(temp2);
}
/// <summary>
/// Removes the hidden attribute.
/// </summary>
/// <param name="path">The path.</param>
private void RemoveHiddenAttribute(string path)
{
var currentFile = new FileInfo(path);
// This will fail if the file is hidden
if (currentFile.Exists)
{
if ((currentFile.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
{
currentFile.Attributes &= ~FileAttributes.Hidden;
}
}
}
}
/// <summary>

@ -74,5 +74,12 @@ namespace MediaBrowser.Common.IO
/// <param name="isAsync">if set to <c>true</c> [is asynchronous].</param>
/// <returns>FileStream.</returns>
FileStream GetFileStream(string path, FileMode mode, FileAccess access, FileShare share, bool isAsync = false);
/// <summary>
/// Swaps the files.
/// </summary>
/// <param name="file1">The file1.</param>
/// <param name="file2">The file2.</param>
void SwapFiles(string file1, string file2);
}
}

@ -47,7 +47,7 @@ namespace MediaBrowser.Controller.Drawing
/// <param name="item">The item.</param>
/// <param name="imageType">Type of the image.</param>
/// <returns>IEnumerable{IImageEnhancer}.</returns>
IEnumerable<IImageEnhancer> GetSupportedEnhancers(BaseItem item, ImageType imageType);
IEnumerable<IImageEnhancer> GetSupportedEnhancers(IHasImages item, ImageType imageType);
/// <summary>
/// Gets the image cache tag.
@ -56,7 +56,7 @@ namespace MediaBrowser.Controller.Drawing
/// <param name="imageType">Type of the image.</param>
/// <param name="imagePath">The image path.</param>
/// <returns>Guid.</returns>
Guid GetImageCacheTag(BaseItem item, ImageType imageType, string imagePath);
Guid GetImageCacheTag(IHasImages item, ImageType imageType, string imagePath);
/// <summary>
/// Gets the image cache tag.
@ -67,7 +67,7 @@ namespace MediaBrowser.Controller.Drawing
/// <param name="dateModified">The date modified.</param>
/// <param name="imageEnhancers">The image enhancers.</param>
/// <returns>Guid.</returns>
Guid GetImageCacheTag(BaseItem item, ImageType imageType, string originalImagePath, DateTime dateModified,
Guid GetImageCacheTag(IHasImages item, ImageType imageType, string originalImagePath, DateTime dateModified,
List<IImageEnhancer> imageEnhancers);
/// <summary>
@ -85,6 +85,6 @@ namespace MediaBrowser.Controller.Drawing
/// <param name="imageType">Type of the image.</param>
/// <param name="imageIndex">Index of the image.</param>
/// <returns>Task{System.String}.</returns>
Task<string> GetEnhancedImage(BaseItem item, ImageType imageType, int imageIndex);
Task<string> GetEnhancedImage(IHasImages item, ImageType imageType, int imageIndex);
}
}

@ -9,7 +9,7 @@ namespace MediaBrowser.Controller.Drawing
{
public class ImageProcessingOptions
{
public BaseItem Item { get; set; }
public IHasImages Item { get; set; }
public ImageType ImageType { get; set; }

@ -22,7 +22,7 @@ namespace MediaBrowser.Controller.Entities
/// <summary>
/// Class BaseItem
/// </summary>
public abstract class BaseItem : IHasProviderIds, ILibraryItem
public abstract class BaseItem : IHasProviderIds, ILibraryItem, IHasImages, IHasUserData
{
protected BaseItem()
{
@ -132,8 +132,8 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
public string PrimaryImagePath
{
get { return GetImage(ImageType.Primary); }
set { SetImage(ImageType.Primary, value); }
get { return this.GetImagePath(ImageType.Primary); }
set { this.SetImagePath(ImageType.Primary, value); }
}
/// <summary>
@ -1310,31 +1310,10 @@ namespace MediaBrowser.Controller.Entities
/// Gets an image
/// </summary>
/// <param name="type">The type.</param>
/// <returns>System.String.</returns>
/// <exception cref="System.ArgumentException">Backdrops should be accessed using Item.Backdrops</exception>
public string GetImage(ImageType type)
{
if (type == ImageType.Backdrop)
{
throw new ArgumentException("Backdrops should be accessed using Item.Backdrops");
}
if (type == ImageType.Screenshot)
{
throw new ArgumentException("Screenshots should be accessed using Item.Screenshots");
}
string val;
Images.TryGetValue(type, out val);
return val;
}
/// <summary>
/// Gets an image
/// </summary>
/// <param name="type">The type.</param>
/// <param name="imageIndex">Index of the image.</param>
/// <returns><c>true</c> if the specified type has image; otherwise, <c>false</c>.</returns>
/// <exception cref="System.ArgumentException">Backdrops should be accessed using Item.Backdrops</exception>
public bool HasImage(ImageType type)
public bool HasImage(ImageType type, int imageIndex)
{
if (type == ImageType.Backdrop)
{
@ -1345,16 +1324,10 @@ namespace MediaBrowser.Controller.Entities
throw new ArgumentException("Screenshots should be accessed using Item.Screenshots");
}
return !string.IsNullOrEmpty(GetImage(type));
return !string.IsNullOrEmpty(this.GetImagePath(type));
}
/// <summary>
/// Sets an image
/// </summary>
/// <param name="type">The type.</param>
/// <param name="path">The path.</param>
/// <exception cref="System.ArgumentException">Backdrops should be accessed using Item.Backdrops</exception>
public void SetImage(ImageType type, string path)
public void SetImagePath(ImageType type, int index, string path)
{
if (type == ImageType.Backdrop)
{
@ -1423,10 +1396,10 @@ namespace MediaBrowser.Controller.Entities
else
{
// Delete the source file
DeleteImagePath(GetImage(type));
DeleteImagePath(this.GetImagePath(type));
// Remove it from the item
SetImage(type, null);
this.SetImagePath(type, null);
}
// Refresh metadata
@ -1597,13 +1570,13 @@ namespace MediaBrowser.Controller.Entities
{
if (imageType == ImageType.Backdrop)
{
return BackdropImagePaths[imageIndex];
return BackdropImagePaths.Count > imageIndex ? BackdropImagePaths[imageIndex] : null;
}
if (imageType == ImageType.Screenshot)
{
var hasScreenshots = (IHasScreenshots)this;
return hasScreenshots.ScreenshotImagePaths[imageIndex];
return hasScreenshots.ScreenshotImagePaths.Count > imageIndex ? hasScreenshots.ScreenshotImagePaths[imageIndex] : null;
}
if (imageType == ImageType.Chapter)
@ -1611,7 +1584,9 @@ namespace MediaBrowser.Controller.Entities
return ItemRepository.GetChapter(Id, imageIndex).ImagePath;
}
return GetImage(imageType);
string val;
Images.TryGetValue(imageType, out val);
return val;
}
/// <summary>
@ -1658,5 +1633,21 @@ namespace MediaBrowser.Controller.Entities
{
return new[] { Path };
}
public Task SwapImages(ImageType type, int index1, int index2)
{
if (type != ImageType.Screenshot && type != ImageType.Backdrop)
{
throw new ArgumentException("The change index operation is only applicable to backdrops and screenshots");
}
var file1 = GetImagePath(type, index1);
var file2 = GetImagePath(type, index2);
FileSystem.SwapFiles(file1, file2);
// Directory watchers should repeat this, but do a quick refresh first
return RefreshMetadata(CancellationToken.None, forceSave: true, allowSlowProviders: false);
}
}
}

@ -0,0 +1,97 @@
using MediaBrowser.Model.Entities;
using System;
using System.Threading.Tasks;
namespace MediaBrowser.Controller.Entities
{
public interface IHasImages
{
/// <summary>
/// Gets the name.
/// </summary>
/// <value>The name.</value>
string Name { get; }
/// <summary>
/// Gets the path.
/// </summary>
/// <value>The path.</value>
string Path { get; }
/// <summary>
/// Gets the identifier.
/// </summary>
/// <value>The identifier.</value>
Guid Id { get; }
/// <summary>
/// Gets the image path.
/// </summary>
/// <param name="imageType">Type of the image.</param>
/// <param name="imageIndex">Index of the image.</param>
/// <returns>System.String.</returns>
string GetImagePath(ImageType imageType, int imageIndex);
/// <summary>
/// Gets the image date modified.
/// </summary>
/// <param name="imagePath">The image path.</param>
/// <returns>DateTime.</returns>
DateTime GetImageDateModified(string imagePath);
/// <summary>
/// Sets the image.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="index">The index.</param>
/// <param name="path">The path.</param>
void SetImagePath(ImageType type, int index, string path);
/// <summary>
/// Determines whether the specified type has image.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="imageIndex">Index of the image.</param>
/// <returns><c>true</c> if the specified type has image; otherwise, <c>false</c>.</returns>
bool HasImage(ImageType type, int imageIndex);
/// <summary>
/// Swaps the images.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="index1">The index1.</param>
/// <param name="index2">The index2.</param>
/// <returns>Task.</returns>
Task SwapImages(ImageType type, int index1, int index2);
}
public static class HasImagesExtensions
{
/// <summary>
/// Gets the image path.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="imageType">Type of the image.</param>
/// <returns>System.String.</returns>
public static string GetImagePath(this IHasImages item, ImageType imageType)
{
return item.GetImagePath(imageType, 0);
}
public static bool HasImage(this IHasImages item, ImageType imageType)
{
return item.HasImage(imageType, 0);
}
/// <summary>
/// Sets the image path.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="imageType">Type of the image.</param>
/// <param name="path">The path.</param>
public static void SetImagePath(this IHasImages item, ImageType imageType, string path)
{
item.SetImagePath(imageType, 0, path);
}
}
}

@ -0,0 +1,15 @@

namespace MediaBrowser.Controller.Entities
{
/// <summary>
/// Interface IHasUserData
/// </summary>
public interface IHasUserData
{
/// <summary>
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
string GetUserDataKey();
}
}

@ -183,7 +183,9 @@ namespace MediaBrowser.Controller.Entities.TV
episodes = episodes.Where(i => !i.IsVirtualUnaired);
}
return LibraryManager.Sort(episodes, user, new[] { ItemSortBy.AiredEpisodeOrder }, SortOrder.Ascending)
var sortBy = seasonNumber == 0 ? ItemSortBy.SortName : ItemSortBy.AiredEpisodeOrder;
return LibraryManager.Sort(episodes, user, new[] { sortBy }, SortOrder.Ascending)
.Cast<Episode>();
}

@ -25,7 +25,7 @@ namespace MediaBrowser.Controller.Library
/// <param name="reason">The reason.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task SaveUserData(Guid userId, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken);
Task SaveUserData(Guid userId, IHasUserData item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken);
/// <summary>
/// Gets the user data.

@ -37,6 +37,6 @@ namespace MediaBrowser.Controller.Library
/// Gets or sets the item.
/// </summary>
/// <value>The item.</value>
public BaseItem Item { get; set; }
public IHasUserData Item { get; set; }
}
}

@ -1,75 +0,0 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.LiveTv;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace MediaBrowser.Controller.LiveTv
{
public class Channel : BaseItem, IItemByName
{
public Channel()
{
UserItemCountList = new List<ItemByNameCounts>();
}
/// <summary>
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
public override string GetUserDataKey()
{
return "Channel-" + Name;
}
[IgnoreDataMember]
public List<ItemByNameCounts> UserItemCountList { get; set; }
/// <summary>
/// Gets or sets the number.
/// </summary>
/// <value>The number.</value>
public string ChannelNumber { get; set; }
/// <summary>
/// Get or sets the Id.
/// </summary>
/// <value>The id of the channel.</value>
public string ChannelId { get; set; }
/// <summary>
/// Gets or sets the name of the service.
/// </summary>
/// <value>The name of the service.</value>
public string ServiceName { get; set; }
/// <summary>
/// Gets or sets the type of the channel.
/// </summary>
/// <value>The type of the channel.</value>
public ChannelType ChannelType { get; set; }
public bool? HasProviderImage { get; set; }
protected override string CreateSortName()
{
double number = 0;
if (!string.IsNullOrEmpty(ChannelNumber))
{
double.TryParse(ChannelNumber, out number);
}
return number.ToString("000-") + (Name ?? string.Empty);
}
public override string MediaType
{
get
{
return ChannelType == ChannelType.Radio ? Model.Entities.MediaType.Audio : Model.Entities.MediaType.Video;
}
}
}
}

@ -32,9 +32,15 @@ namespace MediaBrowser.Controller.LiveTv
public ChannelType ChannelType { get; set; }
/// <summary>
/// Set this value to true or false if it is known via channel info whether there is an image or not.
/// Leave it null if the only way to determine is by requesting the image and handling the failure.
/// Supply the image path if it can be accessed directly from the file system
/// </summary>
public bool? HasImage { get; set; }
/// <value>The image path.</value>
public string ImagePath { get; set; }
/// <summary>
/// Supply the image url if it can be downloaded
/// </summary>
/// <value>The image URL.</value>
public string ImageUrl { get; set; }
}
}

@ -144,8 +144,16 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>Channel.</returns>
Channel GetChannel(string id);
LiveTvChannel GetInternalChannel(string id);
/// <summary>
/// Gets the recording.
/// </summary>
/// <param name="id">The identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>LiveTvRecording.</returns>
Task<LiveTvRecording> GetInternalRecording(string id, CancellationToken cancellationToken);
/// <summary>
/// Gets the program.
/// </summary>

@ -79,7 +79,7 @@ namespace MediaBrowser.Controller.LiveTv
Task UpdateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken);
/// <summary>
/// Gets the channel image asynchronous.
/// Gets the channel image asynchronous. This only needs to be implemented if an image path or url cannot be supplied to ChannelInfo
/// </summary>
/// <param name="channelId">The channel identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
@ -87,7 +87,7 @@ namespace MediaBrowser.Controller.LiveTv
Task<ImageResponseInfo> GetChannelImageAsync(string channelId, CancellationToken cancellationToken);
/// <summary>
/// Gets the recording image asynchronous.
/// Gets the recording image asynchronous. This only needs to be implemented if an image path or url cannot be supplied to RecordingInfo
/// </summary>
/// <param name="recordingId">The recording identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
@ -95,7 +95,7 @@ namespace MediaBrowser.Controller.LiveTv
Task<ImageResponseInfo> GetRecordingImageAsync(string recordingId, CancellationToken cancellationToken);
/// <summary>
/// Gets the program image asynchronous.
/// Gets the program image asynchronous. This only needs to be implemented if an image path or url cannot be supplied to ProgramInfo
/// </summary>
/// <param name="programId">The program identifier.</param>
/// <param name="channelId">The channel identifier.</param>

@ -0,0 +1,57 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.LiveTv;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace MediaBrowser.Controller.LiveTv
{
public class LiveTvChannel : BaseItem, IItemByName
{
public LiveTvChannel()
{
UserItemCountList = new List<ItemByNameCounts>();
}
/// <summary>
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
public override string GetUserDataKey()
{
return GetClientTypeName() + "-" + Name;
}
[IgnoreDataMember]
public List<ItemByNameCounts> UserItemCountList { get; set; }
public ChannelInfo ChannelInfo { get; set; }
public string ServiceName { get; set; }
protected override string CreateSortName()
{
double number = 0;
if (!string.IsNullOrEmpty(ChannelInfo.Number))
{
double.TryParse(ChannelInfo.Number, out number);
}
return number.ToString("000-") + (Name ?? string.Empty);
}
public override string MediaType
{
get
{
return ChannelInfo.ChannelType == ChannelType.Radio ? Model.Entities.MediaType.Audio : Model.Entities.MediaType.Video;
}
}
public override string GetClientTypeName()
{
return "Channel";
}
}
}

@ -0,0 +1,33 @@
using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Controller.LiveTv
{
public class LiveTvProgram : BaseItem
{
/// <summary>
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
public override string GetUserDataKey()
{
return GetClientTypeName() + "-" + Name;
}
public ProgramInfo ProgramInfo { get; set; }
public string ServiceName { get; set; }
public override string MediaType
{
get
{
return ProgramInfo.IsVideo ? Model.Entities.MediaType.Video : Model.Entities.MediaType.Audio;
}
}
public override string GetClientTypeName()
{
return "Program";
}
}
}

@ -0,0 +1,43 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
namespace MediaBrowser.Controller.LiveTv
{
public class LiveTvRecording : BaseItem
{
/// <summary>
/// Gets the user data key.
/// </summary>
/// <returns>System.String.</returns>
public override string GetUserDataKey()
{
return GetClientTypeName() + "-" + Name;
}
public RecordingInfo RecordingInfo { get; set; }
public string ServiceName { get; set; }
public override string MediaType
{
get
{
return RecordingInfo.ChannelType == ChannelType.Radio ? Model.Entities.MediaType.Audio : Model.Entities.MediaType.Video;
}
}
public override LocationType LocationType
{
get
{
return LocationType.Remote;
}
}
public override string GetClientTypeName()
{
return "Recording";
}
}
}

@ -98,10 +98,16 @@ namespace MediaBrowser.Controller.LiveTv
public string EpisodeTitle { get; set; }
/// <summary>
/// Set this value to true or false if it is known via program info whether there is an image or not.
/// Leave it null if the only way to determine is by requesting the image and handling the failure.
/// Supply the image path if it can be accessed directly from the file system
/// </summary>
public bool? HasImage { get; set; }
/// <value>The image path.</value>
public string ImagePath { get; set; }
/// <summary>
/// Supply the image url if it can be downloaded
/// </summary>
/// <value>The image URL.</value>
public string ImageUrl { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is movie.
@ -120,7 +126,13 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary>
/// <value><c>true</c> if this instance is series; otherwise, <c>false</c>.</value>
public bool IsSeries { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is video.
/// </summary>
/// <value><c>true</c> if this instance is video; otherwise, <c>false</c>.</value>
public bool IsVideo { get; set; }
public ProgramInfo()
{
Genres = new List<string>();

@ -114,10 +114,16 @@ namespace MediaBrowser.Controller.LiveTv
public float? CommunityRating { get; set; }
/// <summary>
/// Set this value to true or false if it is known via recording info whether there is an image or not.
/// Leave it null if the only way to determine is by requesting the image and handling the failure.
/// Supply the image path if it can be accessed directly from the file system
/// </summary>
public bool? HasImage { get; set; }
/// <value>The image path.</value>
public string ImagePath { get; set; }
/// <summary>
/// Supply the image url if it can be downloaded
/// </summary>
/// <value>The image URL.</value>
public string ImageUrl { get; set; }
public RecordingInfo()
{

@ -85,6 +85,7 @@
<Compile Include="Entities\IHasAspectRatio.cs" />
<Compile Include="Entities\IHasBudget.cs" />
<Compile Include="Entities\IHasCriticRating.cs" />
<Compile Include="Entities\IHasImages.cs" />
<Compile Include="Entities\IHasLanguage.cs" />
<Compile Include="Entities\IHasMediaStreams.cs" />
<Compile Include="Entities\IHasProductionLocations.cs" />
@ -94,6 +95,7 @@
<Compile Include="Entities\IHasTags.cs" />
<Compile Include="Entities\IHasThemeMedia.cs" />
<Compile Include="Entities\IHasTrailers.cs" />
<Compile Include="Entities\IHasUserData.cs" />
<Compile Include="Entities\IItemByName.cs" />
<Compile Include="Entities\ILibraryItem.cs" />
<Compile Include="Entities\ImageSourceInfo.cs" />
@ -106,11 +108,13 @@
<Compile Include="Library\ItemUpdateType.cs" />
<Compile Include="Library\IUserDataManager.cs" />
<Compile Include="Library\UserDataSaveEventArgs.cs" />
<Compile Include="LiveTv\Channel.cs" />
<Compile Include="LiveTv\LiveTvChannel.cs" />
<Compile Include="LiveTv\ChannelInfo.cs" />
<Compile Include="LiveTv\ILiveTvManager.cs" />
<Compile Include="LiveTv\ILiveTvService.cs" />
<Compile Include="LiveTv\ImageResponseInfo.cs" />
<Compile Include="LiveTv\LiveTvProgram.cs" />
<Compile Include="LiveTv\LiveTvRecording.cs" />
<Compile Include="LiveTv\ProgramInfo.cs" />
<Compile Include="LiveTv\RecordingInfo.cs" />
<Compile Include="LiveTv\SeriesTimerInfo.cs" />

@ -170,7 +170,7 @@ namespace MediaBrowser.Controller.MediaInfo
InputType type;
var inputPath = MediaEncoderHelpers.GetInputArgument(video, null, out type);
var inputPath = MediaEncoderHelpers.GetInputArgument(video.Path, false, video.VideoType, video.IsoType, null, video.PlayableStreamFileNames, out type);
try
{
@ -233,33 +233,23 @@ namespace MediaBrowser.Controller.MediaInfo
/// <summary>
/// Gets the subtitle cache path.
/// </summary>
/// <param name="input">The input.</param>
/// <param name="subtitleStreamIndex">Index of the subtitle stream.</param>
/// <param name="mediaPath">The media path.</param>
/// <param name="subtitleStream">The subtitle stream.</param>
/// <param name="offset">The offset.</param>
/// <param name="outputExtension">The output extension.</param>
/// <returns>System.String.</returns>
public string GetSubtitleCachePath(Video input, int subtitleStreamIndex, TimeSpan? offset, string outputExtension)
public string GetSubtitleCachePath(string mediaPath, MediaStream subtitleStream, TimeSpan? offset, string outputExtension)
{
var ticksParam = offset.HasValue ? "_" + offset.Value.Ticks : "";
var stream = _itemRepo.GetMediaStreams(new MediaStreamQuery
if (subtitleStream.IsExternal)
{
ItemId = input.Id,
Index = subtitleStreamIndex
}).FirstOrDefault();
if (stream == null)
{
return null;
ticksParam += _fileSystem.GetLastWriteTimeUtc(subtitleStream.Path).Ticks;
}
if (stream.IsExternal)
{
ticksParam += _fileSystem.GetLastWriteTimeUtc(stream.Path).Ticks;
}
var date = _fileSystem.GetLastWriteTimeUtc(mediaPath);
var filename = (input.Id + "_" + subtitleStreamIndex.ToString(_usCulture) + "_" + input.DateModified.Ticks.ToString(_usCulture) + ticksParam).GetMD5() + outputExtension;
var filename = (mediaPath + "_" + subtitleStream.Index.ToString(_usCulture) + "_" + date.Ticks.ToString(_usCulture) + ticksParam).GetMD5() + outputExtension;
var prefix = filename.Substring(0, 1);

@ -1,5 +1,8 @@
using MediaBrowser.Common.MediaInfo;
using MediaBrowser.Controller.Entities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MediaBrowser.Common.MediaInfo;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
@ -13,43 +16,47 @@ namespace MediaBrowser.Controller.MediaInfo
/// <summary>
/// Gets the input argument.
/// </summary>
/// <param name="video">The video.</param>
/// <param name="videoPath">The video path.</param>
/// <param name="isRemote">if set to <c>true</c> [is remote].</param>
/// <param name="videoType">Type of the video.</param>
/// <param name="isoType">Type of the iso.</param>
/// <param name="isoMount">The iso mount.</param>
/// <param name="playableStreamFileNames">The playable stream file names.</param>
/// <param name="type">The type.</param>
/// <returns>System.String[][].</returns>
public static string[] GetInputArgument(Video video, IIsoMount isoMount, out InputType type)
public static string[] GetInputArgument(string videoPath, bool isRemote, VideoType videoType, IsoType? isoType, IIsoMount isoMount, IEnumerable<string> playableStreamFileNames, out InputType type)
{
var inputPath = isoMount == null ? new[] { video.Path } : new[] { isoMount.MountedPath };
var inputPath = isoMount == null ? new[] { videoPath } : new[] { isoMount.MountedPath };
type = InputType.VideoFile;
switch (video.VideoType)
switch (videoType)
{
case VideoType.BluRay:
type = InputType.Bluray;
break;
case VideoType.Dvd:
type = InputType.Dvd;
inputPath = video.GetPlayableStreamFiles(inputPath[0]).ToArray();
inputPath = GetPlayableStreamFiles(inputPath[0], playableStreamFileNames).ToArray();
break;
case VideoType.Iso:
if (video.IsoType.HasValue)
if (isoType.HasValue)
{
switch (video.IsoType.Value)
switch (isoType.Value)
{
case IsoType.BluRay:
type = InputType.Bluray;
break;
case IsoType.Dvd:
type = InputType.Dvd;
inputPath = video.GetPlayableStreamFiles(inputPath[0]).ToArray();
inputPath = GetPlayableStreamFiles(inputPath[0], playableStreamFileNames).ToArray();
break;
}
}
break;
case VideoType.VideoFile:
{
if (video.LocationType == LocationType.Remote)
if (isRemote)
{
type = InputType.Url;
}
@ -60,6 +67,17 @@ namespace MediaBrowser.Controller.MediaInfo
return inputPath;
}
public static List<string> GetPlayableStreamFiles(string rootPath, IEnumerable<string> filenames)
{
var allFiles = Directory
.EnumerateFiles(rootPath, "*", SearchOption.AllDirectories)
.ToList();
return filenames.Select(name => allFiles.FirstOrDefault(f => string.Equals(Path.GetFileName(f), name, StringComparison.OrdinalIgnoreCase)))
.Where(f => !string.IsNullOrEmpty(f))
.ToList();
}
/// <summary>
/// Gets the type of the input.
/// </summary>

@ -14,7 +14,7 @@ namespace MediaBrowser.Controller.Providers
/// <param name="item">The item.</param>
/// <param name="imageType">Type of the image.</param>
/// <returns><c>true</c> if this enhancer will enhance the supplied image for the supplied item, <c>false</c> otherwise</returns>
bool Supports(BaseItem item, ImageType imageType);
bool Supports(IHasImages item, ImageType imageType);
/// <summary>
/// Gets the priority or order in which this enhancer should be run.
@ -28,7 +28,7 @@ namespace MediaBrowser.Controller.Providers
/// <param name="item">The item.</param>
/// <param name="imageType">Type of the image.</param>
/// <returns>Cache key relating to the current state of this item and configuration</returns>
string GetConfigurationCacheKey(BaseItem item, ImageType imageType);
string GetConfigurationCacheKey(IHasImages item, ImageType imageType);
/// <summary>
/// Gets the size of the enhanced image.
@ -38,7 +38,7 @@ namespace MediaBrowser.Controller.Providers
/// <param name="imageIndex">Index of the image.</param>
/// <param name="originalImageSize">Size of the original image.</param>
/// <returns>ImageSize.</returns>
ImageSize GetEnhancedImageSize(BaseItem item, ImageType imageType, int imageIndex, ImageSize originalImageSize);
ImageSize GetEnhancedImageSize(IHasImages item, ImageType imageType, int imageIndex, ImageSize originalImageSize);
/// <summary>
/// Enhances the image async.
@ -49,6 +49,6 @@ namespace MediaBrowser.Controller.Providers
/// <param name="imageIndex">Index of the image.</param>
/// <returns>Task{Image}.</returns>
/// <exception cref="System.ArgumentNullException"></exception>
Task<Image> EnhanceImageAsync(BaseItem item, Image originalImage, ImageType imageType, int imageIndex);
Task<Image> EnhanceImageAsync(IHasImages item, Image originalImage, ImageType imageType, int imageIndex);
}
}

@ -252,8 +252,8 @@ namespace MediaBrowser.Model.Configuration
EnableVideoImageExtraction = true;
EnableMovieChapterImageExtraction = true;
EnableEpisodeChapterImageExtraction = true;
EnableOtherVideoChapterImageExtraction = true;
EnableEpisodeChapterImageExtraction = false;
EnableOtherVideoChapterImageExtraction = false;
#if (DEBUG)
EnableDeveloperTools = true;

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.LiveTv
{
@ -108,6 +109,12 @@ namespace MediaBrowser.Model.LiveTv
/// <value>The episode title.</value>
public string EpisodeTitle { get; set; }
/// <summary>
/// Gets or sets the image tags.
/// </summary>
/// <value>The image tags.</value>
public Dictionary<ImageType, Guid> ImageTags { get; set; }
/// <summary>
/// Gets or sets the user data.
/// </summary>
@ -132,9 +139,16 @@ namespace MediaBrowser.Model.LiveTv
/// <value><c>true</c> if this instance is series; otherwise, <c>false</c>.</value>
public bool IsSeries { get; set; }
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
public string Type { get; set; }
public ProgramInfoDto()
{
Genres = new List<string>();
ImageTags = new Dictionary<ImageType, Guid>();
}
}

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Model.LiveTv
{
@ -136,15 +137,28 @@ namespace MediaBrowser.Model.LiveTv
/// <value>The audio.</value>
public ProgramAudio? Audio { get; set; }
/// <summary>
/// Gets or sets the image tags.
/// </summary>
/// <value>The image tags.</value>
public Dictionary<ImageType, Guid> ImageTags { get; set; }
/// <summary>
/// Gets or sets the user data.
/// </summary>
/// <value>The user data.</value>
public UserItemDataDto UserData { get; set; }
/// <summary>
/// Gets or sets the type.
/// </summary>
/// <value>The type.</value>
public string Type { get; set; }
public RecordingInfoDto()
{
Genres = new List<string>();
ImageTags = new Dictionary<ImageType, Guid>();
}
}
}

@ -16,6 +16,12 @@
/// </summary>
/// <value>The user identifier.</value>
public string UserId { get; set; }
/// <summary>
/// Gets or sets the identifier.
/// </summary>
/// <value>The identifier.</value>
public string Id { get; set; }
}
public class TimerQuery

@ -212,7 +212,7 @@ namespace MediaBrowser.Providers
if (image != null)
{
item.SetImage(ImageType.Logo, image.FullName);
item.SetImagePath(ImageType.Logo, image.FullName);
}
// Clearart
@ -220,7 +220,7 @@ namespace MediaBrowser.Providers
if (image != null)
{
item.SetImage(ImageType.Art, image.FullName);
item.SetImagePath(ImageType.Art, image.FullName);
}
// Disc
@ -229,7 +229,7 @@ namespace MediaBrowser.Providers
if (image != null)
{
item.SetImage(ImageType.Disc, image.FullName);
item.SetImagePath(ImageType.Disc, image.FullName);
}
// Box Image
@ -237,7 +237,7 @@ namespace MediaBrowser.Providers
if (image != null)
{
item.SetImage(ImageType.Box, image.FullName);
item.SetImagePath(ImageType.Box, image.FullName);
}
// BoxRear Image
@ -245,7 +245,7 @@ namespace MediaBrowser.Providers
if (image != null)
{
item.SetImage(ImageType.BoxRear, image.FullName);
item.SetImagePath(ImageType.BoxRear, image.FullName);
}
// Thumbnail Image
@ -253,7 +253,7 @@ namespace MediaBrowser.Providers
if (image != null)
{
item.SetImage(ImageType.Menu, image.FullName);
item.SetImagePath(ImageType.Menu, image.FullName);
}
PopulateBanner(item, args);
@ -311,7 +311,7 @@ namespace MediaBrowser.Providers
if (image != null)
{
item.SetImage(ImageType.Primary, image.FullName);
item.SetImagePath(ImageType.Primary, image.FullName);
}
}
@ -339,7 +339,7 @@ namespace MediaBrowser.Providers
if (image != null)
{
item.SetImage(ImageType.Banner, image.FullName);
item.SetImagePath(ImageType.Banner, image.FullName);
}
}
@ -367,7 +367,7 @@ namespace MediaBrowser.Providers
if (image != null)
{
item.SetImage(ImageType.Thumb, image.FullName);
item.SetImagePath(ImageType.Thumb, image.FullName);
}
}

@ -28,7 +28,7 @@ namespace MediaBrowser.Providers.LiveTv
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
public override bool Supports(BaseItem item)
{
return item is Channel;
return item is LiveTvChannel;
}
/// <summary>
@ -74,7 +74,7 @@ namespace MediaBrowser.Providers.LiveTv
try
{
new BaseItemXmlParser<Channel>(Logger).Fetch((Channel)item, path, cancellationToken);
new BaseItemXmlParser<LiveTvChannel>(Logger).Fetch((LiveTvChannel)item, path, cancellationToken);
}
finally
{

@ -115,7 +115,7 @@ namespace MediaBrowser.Providers.MediaInfo
if (video != null)
{
inputPath = MediaEncoderHelpers.GetInputArgument(video, isoMount, out type);
inputPath = MediaEncoderHelpers.GetInputArgument(video.Path, video.LocationType == LocationType.Remote, video.VideoType, video.IsoType, isoMount, video.PlayableStreamFileNames, out type);
}
return await MediaEncoder.GetMediaInfo(inputPath, type, cancellationToken).ConfigureAwait(false);

@ -253,7 +253,7 @@ namespace MediaBrowser.Providers.MediaInfo
InputType type;
var inputPath = MediaEncoderHelpers.GetInputArgument(video, isoMount, out type);
var inputPath = MediaEncoderHelpers.GetInputArgument(video.Path, video.LocationType == LocationType.Remote, video.VideoType, video.IsoType, isoMount, video.PlayableStreamFileNames, out type);
await _mediaEncoder.ExtractImage(inputPath, type, video.Video3DFormat, imageOffset, path, cancellationToken).ConfigureAwait(false);

@ -29,7 +29,7 @@ namespace MediaBrowser.Providers.Savers
// If new metadata has been downloaded or metadata was manually edited, proceed
if ((wasMetadataEdited || wasMetadataDownloaded))
{
return item is Channel;
return item is LiveTvChannel;
}
return false;

@ -594,7 +594,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
/// <param name="imagePath">The image path.</param>
/// <returns>Guid.</returns>
/// <exception cref="System.ArgumentNullException">item</exception>
public Guid GetImageCacheTag(BaseItem item, ImageType imageType, string imagePath)
public Guid GetImageCacheTag(IHasImages item, ImageType imageType, string imagePath)
{
if (item == null)
{
@ -623,7 +623,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
/// <param name="imageEnhancers">The image enhancers.</param>
/// <returns>Guid.</returns>
/// <exception cref="System.ArgumentNullException">item</exception>
public Guid GetImageCacheTag(BaseItem item, ImageType imageType, string originalImagePath, DateTime dateModified, List<IImageEnhancer> imageEnhancers)
public Guid GetImageCacheTag(IHasImages item, ImageType imageType, string originalImagePath, DateTime dateModified, List<IImageEnhancer> imageEnhancers)
{
if (item == null)
{
@ -660,7 +660,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
/// <param name="imageType">Type of the image.</param>
/// <param name="imageIndex">Index of the image.</param>
/// <returns>Task{System.String}.</returns>
public async Task<string> GetEnhancedImage(BaseItem item, ImageType imageType, int imageIndex)
public async Task<string> GetEnhancedImage(IHasImages item, ImageType imageType, int imageIndex)
{
var enhancers = GetSupportedEnhancers(item, imageType).ToList();
@ -673,7 +673,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
return result.Item1;
}
private async Task<Tuple<string, DateTime>> GetEnhancedImage(string originalImagePath, DateTime dateModified, BaseItem item,
private async Task<Tuple<string, DateTime>> GetEnhancedImage(string originalImagePath, DateTime dateModified, IHasImages item,
ImageType imageType, int imageIndex,
List<IImageEnhancer> enhancers)
{
@ -709,7 +709,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
/// <param name="supportedEnhancers">The supported enhancers.</param>
/// <returns>System.String.</returns>
/// <exception cref="System.ArgumentNullException">originalImagePath</exception>
private async Task<string> GetEnhancedImageInternal(string originalImagePath, DateTime dateModified, BaseItem item, ImageType imageType, int imageIndex, List<IImageEnhancer> supportedEnhancers)
private async Task<string> GetEnhancedImageInternal(string originalImagePath, DateTime dateModified, IHasImages item, ImageType imageType, int imageIndex, List<IImageEnhancer> supportedEnhancers)
{
if (string.IsNullOrEmpty(originalImagePath))
{
@ -782,7 +782,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
/// <param name="imageType">Type of the image.</param>
/// <param name="imageIndex">Index of the image.</param>
/// <returns>Task{EnhancedImage}.</returns>
private async Task<Image> ExecuteImageEnhancers(IEnumerable<IImageEnhancer> imageEnhancers, Image originalImage, BaseItem item, ImageType imageType, int imageIndex)
private async Task<Image> ExecuteImageEnhancers(IEnumerable<IImageEnhancer> imageEnhancers, Image originalImage, IHasImages item, ImageType imageType, int imageIndex)
{
var result = originalImage;
@ -900,7 +900,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
return Path.Combine(path, filename);
}
public IEnumerable<IImageEnhancer> GetSupportedEnhancers(BaseItem item, ImageType imageType)
public IEnumerable<IImageEnhancer> GetSupportedEnhancers(IHasImages item, ImageType imageType)
{
return ImageEnhancers.Where(i =>
{

@ -818,7 +818,7 @@ namespace MediaBrowser.Server.Implementations.Dto
{
dto.ParentLogoItemId = GetDtoId(parentWithLogo);
dto.ParentLogoImageTag = GetImageCacheTag(parentWithLogo, ImageType.Logo, parentWithLogo.GetImage(ImageType.Logo));
dto.ParentLogoImageTag = GetImageCacheTag(parentWithLogo, ImageType.Logo, parentWithLogo.GetImagePath(ImageType.Logo));
}
}
@ -831,7 +831,7 @@ namespace MediaBrowser.Server.Implementations.Dto
{
dto.ParentArtItemId = GetDtoId(parentWithImage);
dto.ParentArtImageTag = GetImageCacheTag(parentWithImage, ImageType.Art, parentWithImage.GetImage(ImageType.Art));
dto.ParentArtImageTag = GetImageCacheTag(parentWithImage, ImageType.Art, parentWithImage.GetImagePath(ImageType.Art));
}
}
@ -844,7 +844,7 @@ namespace MediaBrowser.Server.Implementations.Dto
{
dto.ParentThumbItemId = GetDtoId(parentWithImage);
dto.ParentThumbImageTag = GetImageCacheTag(parentWithImage, ImageType.Thumb, parentWithImage.GetImage(ImageType.Thumb));
dto.ParentThumbImageTag = GetImageCacheTag(parentWithImage, ImageType.Thumb, parentWithImage.GetImagePath(ImageType.Thumb));
}
}
@ -1037,7 +1037,7 @@ namespace MediaBrowser.Server.Implementations.Dto
if (series.HasImage(ImageType.Thumb))
{
dto.SeriesThumbImageTag = GetImageCacheTag(series, ImageType.Thumb, series.GetImage(ImageType.Thumb));
dto.SeriesThumbImageTag = GetImageCacheTag(series, ImageType.Thumb, series.GetImagePath(ImageType.Thumb));
}
var imagePath = series.PrimaryImagePath;

@ -49,7 +49,7 @@ namespace MediaBrowser.Server.Implementations.Library
/// userId
/// or
/// key</exception>
public async Task SaveUserData(Guid userId, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken)
public async Task SaveUserData(Guid userId, IHasUserData item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken)
{
if (userData == null)
{

@ -1,4 +1,5 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
@ -21,18 +22,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private readonly ILiveTvManager _liveTvManager;
private readonly IProviderManager _providerManager;
private readonly IFileSystem _fileSystem;
private readonly IHttpClient _httpClient;
public ChannelImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, ILiveTvManager liveTvManager, IProviderManager providerManager, IFileSystem fileSystem)
public ChannelImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, ILiveTvManager liveTvManager, IProviderManager providerManager, IFileSystem fileSystem, IHttpClient httpClient)
: base(logManager, configurationManager)
{
_liveTvManager = liveTvManager;
_providerManager = providerManager;
_fileSystem = fileSystem;
_httpClient = httpClient;
}
public override bool Supports(BaseItem item)
{
return item is Channel;
return item is LiveTvChannel;
}
protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
@ -48,21 +51,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return true;
}
var channel = (Channel)item;
if (channel.HasProviderImage ?? true)
try
{
try
{
await DownloadImage(item, cancellationToken).ConfigureAwait(false);
}
catch (HttpException ex)
await DownloadImage((LiveTvChannel)item, cancellationToken).ConfigureAwait(false);
}
catch (HttpException ex)
{
// Don't fail the provider on a 404
if (!ex.StatusCode.HasValue || ex.StatusCode.Value != HttpStatusCode.NotFound)
{
// Don't fail the provider on a 404
if (!ex.StatusCode.HasValue || ex.StatusCode.Value != HttpStatusCode.NotFound)
{
throw;
}
throw;
}
}
@ -70,20 +68,55 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return true;
}
private async Task DownloadImage(BaseItem item, CancellationToken cancellationToken)
private async Task DownloadImage(LiveTvChannel item, CancellationToken cancellationToken)
{
var channel = (Channel)item;
var channelInfo = item.ChannelInfo;
var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, channel.ServiceName, StringComparison.OrdinalIgnoreCase));
Stream imageStream = null;
string contentType = null;
if (service != null)
if (!string.IsNullOrEmpty(channelInfo.ImagePath))
{
var response = await service.GetChannelImageAsync(channel.ChannelId, cancellationToken).ConfigureAwait(false);
contentType = "image/" + Path.GetExtension(channelInfo.ImagePath).ToLower();
imageStream = _fileSystem.GetFileStream(channelInfo.ImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true);
}
else if (!string.IsNullOrEmpty(channelInfo.ImageUrl))
{
var options = new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = channelInfo.ImageUrl
};
var response = await _httpClient.GetResponse(options).ConfigureAwait(false);
if (!response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("Provider did not return an image content type.");
}
imageStream = response.Content;
contentType = response.ContentType;
}
else
{
var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, item.ServiceName, StringComparison.OrdinalIgnoreCase));
if (service != null)
{
var response = await service.GetChannelImageAsync(channelInfo.Id, cancellationToken).ConfigureAwait(false);
imageStream = response.Stream;
contentType = response.MimeType;
}
}
if (imageStream != null)
{
// Dummy up the original url
var url = channel.ServiceName + channel.ChannelId;
var url = item.ServiceName + channelInfo.Id;
await _providerManager.SaveImage(channel, response.Stream, response.MimeType, ImageType.Primary, null, url, cancellationToken).ConfigureAwait(false);
await _providerManager.SaveImage(item, imageStream, contentType, ImageType.Primary, null, url, cancellationToken).ConfigureAwait(false);
}
}

@ -135,11 +135,29 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return pattern;
}
public RecordingInfoDto GetRecordingInfoDto(RecordingInfo info, ILiveTvService service, User user = null)
/// <summary>
/// Convert the provider 0-5 scale to our 0-10 scale
/// </summary>
/// <param name="val"></param>
/// <returns></returns>
private float? GetClientCommunityRating(float? val)
{
if (!val.HasValue)
{
return null;
}
return val.Value * 2;
}
public RecordingInfoDto GetRecordingInfoDto(LiveTvRecording recording, ILiveTvService service, User user = null)
{
var info = recording.RecordingInfo;
var dto = new RecordingInfoDto
{
Id = GetInternalRecordingId(service.Name, info.Id).ToString("N"),
Type = recording.GetClientTypeName(),
ChannelName = info.ChannelName,
Overview = info.Overview,
EndDate = info.EndDate,
@ -154,7 +172,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
EpisodeTitle = info.EpisodeTitle,
ChannelType = info.ChannelType,
MediaType = info.ChannelType == ChannelType.Radio ? MediaType.Audio : MediaType.Video,
CommunityRating = info.CommunityRating,
CommunityRating = GetClientCommunityRating(info.CommunityRating),
OfficialRating = info.OfficialRating,
Audio = info.Audio,
IsHD = info.IsHD,
@ -162,9 +180,16 @@ namespace MediaBrowser.Server.Implementations.LiveTv
Url = info.Url
};
var imageTag = GetImageTag(recording);
if (imageTag.HasValue)
{
dto.ImageTags[ImageType.Primary] = imageTag.Value;
}
if (user != null)
{
//dto.UserData = _dtoService.GetUserItemDataDto(_userDataManager.GetUserData(user.Id, info.GetUserDataKey()));
dto.UserData = _dtoService.GetUserItemDataDto(_userDataManager.GetUserData(user.Id, recording.GetUserDataKey()));
}
var duration = info.EndDate - info.StartDate;
@ -184,18 +209,20 @@ namespace MediaBrowser.Server.Implementations.LiveTv
/// <param name="info">The info.</param>
/// <param name="user">The user.</param>
/// <returns>ChannelInfoDto.</returns>
public ChannelInfoDto GetChannelInfoDto(Channel info, User user = null)
public ChannelInfoDto GetChannelInfoDto(LiveTvChannel info, User user = null)
{
var channelInfo = info.ChannelInfo;
var dto = new ChannelInfoDto
{
Name = info.Name,
ServiceName = info.ServiceName,
ChannelType = info.ChannelType,
Number = info.ChannelNumber,
Type = info.GetType().Name,
ChannelType = channelInfo.ChannelType,
Number = channelInfo.Number,
Type = info.GetClientTypeName(),
Id = info.Id.ToString("N"),
MediaType = info.MediaType,
ExternalId = info.ChannelId
ExternalId = channelInfo.Id
};
if (user != null)
@ -203,7 +230,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
dto.UserData = _dtoService.GetUserItemDataDto(_userDataManager.GetUserData(user.Id, info.GetUserDataKey()));
}
var imageTag = GetLogoImageTag(info);
var imageTag = GetImageTag(info);
if (imageTag.HasValue)
{
@ -213,7 +240,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return dto;
}
public ProgramInfoDto GetProgramInfoDto(ProgramInfo program, Channel channel, User user = null)
public ProgramInfoDto GetProgramInfoDto(ProgramInfo program, LiveTvChannel channel, User user = null)
{
var dto = new ProgramInfoDto
{
@ -230,7 +257,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
IsHD = program.IsHD,
OriginalAirDate = program.OriginalAirDate,
Audio = program.Audio,
CommunityRating = program.CommunityRating,
CommunityRating = GetClientCommunityRating(program.CommunityRating),
AspectRatio = program.AspectRatio,
IsRepeat = program.IsRepeat,
EpisodeTitle = program.EpisodeTitle,
@ -248,7 +275,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return dto;
}
private Guid? GetLogoImageTag(Channel info)
private Guid? GetImageTag(BaseItem info)
{
var path = info.PrimaryImagePath;
@ -263,7 +290,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
}
catch (Exception ex)
{
_logger.ErrorException("Error getting channel image info for {0}", ex, info.Name);
_logger.ErrorException("Error getting image info for {0}", ex, info.Name);
}
return null;
@ -273,7 +300,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
var name = serviceName + externalId + channelName;
return name.ToLower().GetMBId(typeof(Channel));
return name.ToLower().GetMBId(typeof(LiveTvChannel));
}
public Guid GetInternalTimerId(string serviceName, string externalId)
@ -314,41 +341,53 @@ namespace MediaBrowser.Server.Implementations.LiveTv
Name = dto.Name,
StartDate = dto.StartDate,
Status = dto.Status,
SeriesTimerId = dto.ExternalSeriesTimerId,
PrePaddingSeconds = dto.PrePaddingSeconds,
PostPaddingSeconds = dto.PostPaddingSeconds,
IsPostPaddingRequired = dto.IsPostPaddingRequired,
IsPrePaddingRequired = dto.IsPrePaddingRequired,
Priority = dto.Priority
Priority = dto.Priority,
SeriesTimerId = dto.ExternalSeriesTimerId,
ProgramId = dto.ExternalProgramId,
ChannelId = dto.ExternalChannelId,
Id = dto.ExternalId
};
// Convert internal server id's to external tv provider id's
if (!isNew && !string.IsNullOrEmpty(dto.Id))
if (!isNew && !string.IsNullOrEmpty(dto.Id) && string.IsNullOrEmpty(info.Id))
{
var timer = await liveTv.GetTimer(dto.Id, cancellationToken).ConfigureAwait(false);
var timer = await liveTv.GetSeriesTimer(dto.Id, cancellationToken).ConfigureAwait(false);
info.Id = timer.ExternalId;
}
if (!string.IsNullOrEmpty(dto.SeriesTimerId))
if (!string.IsNullOrEmpty(dto.ChannelId) && string.IsNullOrEmpty(info.ChannelId))
{
var timer = await liveTv.GetSeriesTimer(dto.SeriesTimerId, cancellationToken).ConfigureAwait(false);
var channel = await liveTv.GetChannel(dto.ChannelId, cancellationToken).ConfigureAwait(false);
info.SeriesTimerId = timer.ExternalId;
if (channel != null)
{
info.ChannelId = channel.ExternalId;
}
}
if (!string.IsNullOrEmpty(dto.ChannelId))
if (!string.IsNullOrEmpty(dto.ProgramId) && string.IsNullOrEmpty(info.ProgramId))
{
var channel = await liveTv.GetChannel(dto.ChannelId, cancellationToken).ConfigureAwait(false);
var program = await liveTv.GetProgram(dto.ProgramId, cancellationToken).ConfigureAwait(false);
info.ChannelId = channel.ExternalId;
if (program != null)
{
info.ProgramId = program.ExternalId;
}
}
if (!string.IsNullOrEmpty(dto.ProgramId))
if (!string.IsNullOrEmpty(dto.SeriesTimerId) && string.IsNullOrEmpty(info.SeriesTimerId))
{
var program = await liveTv.GetProgram(dto.ProgramId, cancellationToken).ConfigureAwait(false);
var timer = await liveTv.GetSeriesTimer(dto.SeriesTimerId, cancellationToken).ConfigureAwait(false);
info.ProgramId = program.ExternalId;
if (timer != null)
{
info.SeriesTimerId = timer.ExternalId;
}
}
return info;
@ -371,29 +410,38 @@ namespace MediaBrowser.Server.Implementations.LiveTv
Priority = dto.Priority,
RecordAnyChannel = dto.RecordAnyChannel,
RecordAnyTime = dto.RecordAnyTime,
RecordNewOnly = dto.RecordNewOnly
RecordNewOnly = dto.RecordNewOnly,
ProgramId = dto.ExternalProgramId,
ChannelId = dto.ExternalChannelId,
Id = dto.ExternalId
};
// Convert internal server id's to external tv provider id's
if (!isNew && !string.IsNullOrEmpty(dto.Id))
if (!isNew && !string.IsNullOrEmpty(dto.Id) && string.IsNullOrEmpty(info.Id))
{
var timer = await liveTv.GetSeriesTimer(dto.Id, cancellationToken).ConfigureAwait(false);
info.Id = timer.ExternalId;
}
if (!string.IsNullOrEmpty(dto.ChannelId))
if (!string.IsNullOrEmpty(dto.ChannelId) && string.IsNullOrEmpty(info.ChannelId))
{
var channel = await liveTv.GetChannel(dto.ChannelId, cancellationToken).ConfigureAwait(false);
info.ChannelId = channel.ExternalId;
if (channel != null)
{
info.ChannelId = channel.ExternalId;
}
}
if (!string.IsNullOrEmpty(dto.ProgramId))
if (!string.IsNullOrEmpty(dto.ProgramId) && string.IsNullOrEmpty(info.ProgramId))
{
var program = await liveTv.GetProgram(dto.ProgramId, cancellationToken).ConfigureAwait(false);
info.ProgramId = program.ExternalId;
if (program != null)
{
info.ProgramId = program.ExternalId;
}
}
return info;

@ -36,7 +36,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
private readonly List<ILiveTvService> _services = new List<ILiveTvService>();
private List<Channel> _channels = new List<Channel>();
private List<LiveTvChannel> _channels = new List<LiveTvChannel>();
private List<ProgramInfoDto> _programs = new List<ProgramInfoDto>();
public LiveTvManager(IServerApplicationPaths appPaths, IFileSystem fileSystem, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, ILocalizationManager localization, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager)
@ -77,18 +77,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(new Guid(query.UserId));
IEnumerable<Channel> channels = _channels;
IEnumerable<LiveTvChannel> channels = _channels;
if (user != null)
{
channels = channels.Where(i => i.IsParentalAllowed(user, _localization))
channels = channels
.Where(i => i.IsParentalAllowed(user, _localization))
.OrderBy(i =>
{
double number = 0;
if (!string.IsNullOrEmpty(i.ChannelNumber))
if (!string.IsNullOrEmpty(i.ChannelInfo.Number))
{
double.TryParse(i.ChannelNumber, out number);
double.TryParse(i.ChannelInfo.Number, out number);
}
return number;
@ -100,9 +101,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
double number = 0;
if (!string.IsNullOrEmpty(i.ChannelNumber))
if (!string.IsNullOrEmpty(i.ChannelInfo.Number))
{
double.TryParse(i.ChannelNumber, out number);
double.TryParse(i.ChannelInfo.Number, out number);
}
return number;
@ -120,14 +121,25 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return Task.FromResult(result);
}
public Channel GetChannel(string id)
public LiveTvChannel GetInternalChannel(string id)
{
var guid = new Guid(id);
return _channels.FirstOrDefault(i => i.Id == guid);
}
private async Task<Channel> GetChannel(ChannelInfo channelInfo, string serviceName, CancellationToken cancellationToken)
public async Task<LiveTvRecording> GetInternalRecording(string id, CancellationToken cancellationToken)
{
var service = ActiveService;
var recordings = await service.GetRecordingsAsync(cancellationToken).ConfigureAwait(false);
var recording = recordings.FirstOrDefault(i => _tvDtoService.GetInternalRecordingId(service.Name, i.Id) == new Guid(id));
return await GetRecording(recording, service.Name, cancellationToken).ConfigureAwait(false);
}
private async Task<LiveTvChannel> GetChannel(ChannelInfo channelInfo, string serviceName, CancellationToken cancellationToken)
{
var path = Path.Combine(_appPaths.ItemsByNamePath, "channels", _fileSystem.GetValidFilename(serviceName), _fileSystem.GetValidFilename(channelInfo.Name));
@ -150,26 +162,25 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var id = _tvDtoService.GetInternalChannelId(serviceName, channelInfo.Id, channelInfo.Name);
var item = _itemRepo.RetrieveItem(id) as Channel;
var item = _itemRepo.RetrieveItem(id) as LiveTvChannel;
if (item == null)
{
item = new Channel
item = new LiveTvChannel
{
Name = channelInfo.Name,
Id = id,
DateCreated = _fileSystem.GetCreationTimeUtc(fileInfo),
DateModified = _fileSystem.GetLastWriteTimeUtc(fileInfo),
Path = path,
ChannelId = channelInfo.Id,
ChannelNumber = channelInfo.Number,
ServiceName = serviceName,
HasProviderImage = channelInfo.HasImage
Path = path
};
isNew = true;
}
item.ChannelInfo = channelInfo;
item.ServiceName = serviceName;
// Set this now so we don't cause additional file system access during provider executions
item.ResetResolveArgs(fileInfo);
@ -178,6 +189,35 @@ namespace MediaBrowser.Server.Implementations.LiveTv
return item;
}
private async Task<LiveTvRecording> GetRecording(RecordingInfo info, string serviceName, CancellationToken cancellationToken)
{
var isNew = false;
var id = _tvDtoService.GetInternalRecordingId(serviceName, info.Id);
var item = _itemRepo.RetrieveItem(id) as LiveTvRecording;
if (item == null)
{
item = new LiveTvRecording
{
Name = info.Name,
Id = id,
DateCreated = DateTime.UtcNow,
DateModified = DateTime.UtcNow
};
isNew = true;
}
item.RecordingInfo = info;
item.ServiceName = serviceName;
await item.RefreshMetadata(cancellationToken, forceSave: isNew, resetResolveArgs: false);
return item;
}
public Task<ProgramInfoDto> GetProgram(string id, CancellationToken cancellationToken, User user = null)
{
var program = _programs.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase));
@ -225,7 +265,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
var allChannels = await GetChannels(service, cancellationToken).ConfigureAwait(false);
var allChannelsList = allChannels.ToList();
var list = new List<Channel>();
var list = new List<LiveTvChannel>();
var programs = new List<ProgramInfoDto>();
var numComplete = 0;
@ -271,26 +311,34 @@ namespace MediaBrowser.Server.Implementations.LiveTv
public async Task<QueryResult<RecordingInfoDto>> GetRecordings(RecordingQuery query, CancellationToken cancellationToken)
{
var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(new Guid(query.UserId));
var service = ActiveService;
var list = new List<RecordingInfoDto>();
var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(new Guid(query.UserId));
if (ActiveService != null)
{
var recordings = await ActiveService.GetRecordingsAsync(cancellationToken).ConfigureAwait(false);
var list = new List<RecordingInfo>();
var dtos = recordings.Select(i => _tvDtoService.GetRecordingInfoDto(i, ActiveService, user));
var recordings = await service.GetRecordingsAsync(cancellationToken).ConfigureAwait(false);
list.AddRange(recordings);
list.AddRange(dtos);
if (!string.IsNullOrEmpty(query.ChannelId))
{
list = list
.Where(i => _tvDtoService.GetInternalChannelId(service.Name, i.ChannelId, i.ChannelName) == new Guid(query.ChannelId))
.ToList();
}
if (!string.IsNullOrEmpty(query.ChannelId))
if (!string.IsNullOrEmpty(query.Id))
{
list = list.Where(i => string.Equals(i.ChannelId, query.ChannelId))
list = list
.Where(i => _tvDtoService.GetInternalRecordingId(service.Name, i.Id) == new Guid(query.Id))
.ToList();
}
var returnArray = list.OrderByDescending(i => i.StartDate)
var entities = await GetEntities(list, service.Name, cancellationToken).ConfigureAwait(false);
var returnArray = entities
.Select(i => _tvDtoService.GetRecordingInfoDto(i, ActiveService, user))
.OrderByDescending(i => i.StartDate)
.ToArray();
return new QueryResult<RecordingInfoDto>
@ -300,6 +348,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv
};
}
private Task<LiveTvRecording[]> GetEntities(IEnumerable<RecordingInfo> recordings, string serviceName, CancellationToken cancellationToken)
{
var tasks = recordings.Select(i => GetRecording(i, serviceName, cancellationToken));
return Task.WhenAll(tasks);
}
private IEnumerable<ILiveTvService> GetServices(string serviceName, string channelId)
{
IEnumerable<ILiveTvService> services = _services;
@ -404,11 +459,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
var results = await GetRecordings(new RecordingQuery
{
UserId = user == null ? null : user.Id.ToString("N")
UserId = user == null ? null : user.Id.ToString("N"),
Id = id
}, cancellationToken).ConfigureAwait(false);
return results.Items.FirstOrDefault(i => string.Equals(i.Id, id, StringComparison.CurrentCulture));
return results.Items.FirstOrDefault();
}
public async Task<TimerInfoDto> GetTimer(string id, CancellationToken cancellationToken)

@ -0,0 +1,136 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.LiveTv
{
public class ProgramImageProvider : BaseMetadataProvider
{
private readonly ILiveTvManager _liveTvManager;
private readonly IProviderManager _providerManager;
private readonly IFileSystem _fileSystem;
private readonly IHttpClient _httpClient;
public ProgramImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, ILiveTvManager liveTvManager, IProviderManager providerManager, IFileSystem fileSystem, IHttpClient httpClient)
: base(logManager, configurationManager)
{
_liveTvManager = liveTvManager;
_providerManager = providerManager;
_fileSystem = fileSystem;
_httpClient = httpClient;
}
public override bool Supports(BaseItem item)
{
return item is LiveTvProgram;
}
protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
{
return !item.HasImage(ImageType.Primary);
}
public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
{
if (item.HasImage(ImageType.Primary))
{
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
try
{
await DownloadImage((LiveTvProgram)item, cancellationToken).ConfigureAwait(false);
}
catch (HttpException ex)
{
// Don't fail the provider on a 404
if (!ex.StatusCode.HasValue || ex.StatusCode.Value != HttpStatusCode.NotFound)
{
throw;
}
}
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
private async Task DownloadImage(LiveTvProgram item, CancellationToken cancellationToken)
{
var programInfo = item.ProgramInfo;
Stream imageStream = null;
string contentType = null;
if (!string.IsNullOrEmpty(programInfo.ImagePath))
{
contentType = "image/" + Path.GetExtension(programInfo.ImagePath).ToLower();
imageStream = _fileSystem.GetFileStream(programInfo.ImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true);
}
else if (!string.IsNullOrEmpty(programInfo.ImageUrl))
{
var options = new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = programInfo.ImageUrl
};
var response = await _httpClient.GetResponse(options).ConfigureAwait(false);
if (!response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("Provider did not return an image content type.");
}
imageStream = response.Content;
contentType = response.ContentType;
}
else
{
var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, item.ServiceName, StringComparison.OrdinalIgnoreCase));
if (service != null)
{
var response = await service.GetProgramImageAsync(programInfo.Id, programInfo.ChannelId, cancellationToken).ConfigureAwait(false);
imageStream = response.Stream;
contentType = response.MimeType;
}
}
if (imageStream != null)
{
// Dummy up the original url
var url = item.ServiceName + programInfo.Id;
await _providerManager.SaveImage(item, imageStream, contentType, ImageType.Primary, null, url, cancellationToken).ConfigureAwait(false);
}
}
public override MetadataProviderPriority Priority
{
get { return MetadataProviderPriority.Second; }
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate;
}
}
}
}

@ -0,0 +1,136 @@
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
namespace MediaBrowser.Server.Implementations.LiveTv
{
public class RecordingImageProvider : BaseMetadataProvider
{
private readonly ILiveTvManager _liveTvManager;
private readonly IProviderManager _providerManager;
private readonly IFileSystem _fileSystem;
private readonly IHttpClient _httpClient;
public RecordingImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, ILiveTvManager liveTvManager, IProviderManager providerManager, IFileSystem fileSystem, IHttpClient httpClient)
: base(logManager, configurationManager)
{
_liveTvManager = liveTvManager;
_providerManager = providerManager;
_fileSystem = fileSystem;
_httpClient = httpClient;
}
public override bool Supports(BaseItem item)
{
return item is LiveTvRecording;
}
protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
{
return !item.HasImage(ImageType.Primary);
}
public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
{
if (item.HasImage(ImageType.Primary))
{
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
try
{
await DownloadImage((LiveTvRecording)item, cancellationToken).ConfigureAwait(false);
}
catch (HttpException ex)
{
// Don't fail the provider on a 404
if (!ex.StatusCode.HasValue || ex.StatusCode.Value != HttpStatusCode.NotFound)
{
throw;
}
}
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
return true;
}
private async Task DownloadImage(LiveTvRecording item, CancellationToken cancellationToken)
{
var recordingInfo = item.RecordingInfo;
Stream imageStream = null;
string contentType = null;
if (!string.IsNullOrEmpty(recordingInfo.ImagePath))
{
contentType = "image/" + Path.GetExtension(recordingInfo.ImagePath).ToLower();
imageStream = _fileSystem.GetFileStream(recordingInfo.ImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true);
}
else if (!string.IsNullOrEmpty(recordingInfo.ImageUrl))
{
var options = new HttpRequestOptions
{
CancellationToken = cancellationToken,
Url = recordingInfo.ImageUrl
};
var response = await _httpClient.GetResponse(options).ConfigureAwait(false);
if (!response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("Provider did not return an image content type.");
}
imageStream = response.Content;
contentType = response.ContentType;
}
else
{
var service = _liveTvManager.Services.FirstOrDefault(i => string.Equals(i.Name, item.ServiceName, StringComparison.OrdinalIgnoreCase));
if (service != null)
{
var response = await service.GetRecordingImageAsync(recordingInfo.Id, cancellationToken).ConfigureAwait(false);
imageStream = response.Stream;
contentType = response.MimeType;
}
}
if (imageStream != null)
{
// Dummy up the original url
var url = item.ServiceName + recordingInfo.Id;
await _providerManager.SaveImage(item, imageStream, contentType, ImageType.Primary, null, url, cancellationToken).ConfigureAwait(false);
}
}
public override MetadataProviderPriority Priority
{
get { return MetadataProviderPriority.Second; }
}
public override ItemUpdateType ItemUpdateType
{
get
{
return ItemUpdateType.ImageUpdate;
}
}
}
}

@ -153,6 +153,8 @@
<Compile Include="LiveTv\ChannelImageProvider.cs" />
<Compile Include="LiveTv\LiveTvDtoService.cs" />
<Compile Include="LiveTv\LiveTvManager.cs" />
<Compile Include="LiveTv\ProgramImageProvider.cs" />
<Compile Include="LiveTv\RecordingImageProvider.cs" />
<Compile Include="LiveTv\RefreshChannelsScheduledTask.cs" />
<Compile Include="Localization\LocalizationManager.cs" />
<Compile Include="MediaEncoder\MediaEncoder.cs" />

@ -118,6 +118,8 @@ namespace MediaBrowser.Server.Implementations.Providers
imageIndex = hasScreenshots.ScreenshotImagePaths.Count;
}
var index = imageIndex ?? 0;
var paths = GetSavePaths(item, type, imageIndex, mimeType, saveLocally);
// If there are more than one output paths, the stream will need to be seekable
@ -132,7 +134,7 @@ namespace MediaBrowser.Server.Implementations.Providers
source = memoryStream;
}
var currentPath = GetCurrentImagePath(item, type, imageIndex);
var currentPath = GetCurrentImagePath(item, type, index);
using (source)
{
@ -152,7 +154,7 @@ namespace MediaBrowser.Server.Implementations.Providers
}
}
// Set the path into the BaseItem
// Set the path into the item
SetImagePath(item, type, imageIndex, paths[0], sourceUrl);
// Delete the current path
@ -257,27 +259,9 @@ namespace MediaBrowser.Server.Implementations.Providers
/// or
/// imageIndex
/// </exception>
private string GetCurrentImagePath(BaseItem item, ImageType type, int? imageIndex)
private string GetCurrentImagePath(IHasImages item, ImageType type, int imageIndex)
{
switch (type)
{
case ImageType.Screenshot:
var hasScreenshots = (IHasScreenshots)item;
if (!imageIndex.HasValue)
{
throw new ArgumentNullException("imageIndex");
}
return hasScreenshots.ScreenshotImagePaths.Count > imageIndex.Value ? hasScreenshots.ScreenshotImagePaths[imageIndex.Value] : null;
case ImageType.Backdrop:
if (!imageIndex.HasValue)
{
throw new ArgumentNullException("imageIndex");
}
return item.BackdropImagePaths.Count > imageIndex.Value ? item.BackdropImagePaths[imageIndex.Value] : null;
default:
return item.GetImage(type);
}
return item.GetImagePath(type, imageIndex);
}
/// <summary>
@ -336,7 +320,7 @@ namespace MediaBrowser.Server.Implementations.Providers
}
break;
default:
item.SetImage(type, path);
item.SetImagePath(type, path);
break;
}
}
@ -593,7 +577,7 @@ namespace MediaBrowser.Server.Implementations.Providers
/// <param name="imageFilename">The image filename.</param>
/// <param name="extension">The extension.</param>
/// <returns>System.String.</returns>
private string GetSavePathForItemInMixedFolder(BaseItem item, ImageType type, string imageFilename, string extension)
private string GetSavePathForItemInMixedFolder(IHasImages item, ImageType type, string imageFilename, string extension)
{
if (type == ImageType.Primary)
{

@ -96,7 +96,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks
// Limit to video files to reduce changes of ffmpeg crash dialog
foreach (var item in newItems
.Where(i => i.LocationType == LocationType.FileSystem && i.VideoType == VideoType.VideoFile && string.IsNullOrEmpty(i.PrimaryImagePath) && i.DefaultVideoStreamIndex.HasValue)
.Take(2))
.Take(1))
{
try
{

@ -242,19 +242,19 @@ namespace MediaBrowser.ServerApplication
}
if (item.HasImage(ImageType.Banner))
{
previews.Add(new PreviewItem(item.GetImage(ImageType.Banner), "Banner"));
previews.Add(new PreviewItem(item.GetImagePath(ImageType.Banner), "Banner"));
}
if (item.HasImage(ImageType.Logo))
{
previews.Add(new PreviewItem(item.GetImage(ImageType.Logo), "Logo"));
previews.Add(new PreviewItem(item.GetImagePath(ImageType.Logo), "Logo"));
}
if (item.HasImage(ImageType.Art))
{
previews.Add(new PreviewItem(item.GetImage(ImageType.Art), "Art"));
previews.Add(new PreviewItem(item.GetImagePath(ImageType.Art), "Art"));
}
if (item.HasImage(ImageType.Thumb))
{
previews.Add(new PreviewItem(item.GetImage(ImageType.Thumb), "Thumb"));
previews.Add(new PreviewItem(item.GetImagePath(ImageType.Thumb), "Thumb"));
}
previews.AddRange(
item.BackdropImagePaths.Select(

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common.Internal</id>
<version>3.0.278</version>
<version>3.0.281</version>
<title>MediaBrowser.Common.Internal</title>
<authors>Luke</authors>
<owners>ebr,Luke,scottisafool</owners>
@ -12,7 +12,7 @@
<description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
<dependency id="MediaBrowser.Common" version="3.0.278" />
<dependency id="MediaBrowser.Common" version="3.0.281" />
<dependency id="NLog" version="2.1.0" />
<dependency id="SimpleInjector" version="2.4.0" />
<dependency id="sharpcompress" version="0.10.2" />

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common</id>
<version>3.0.278</version>
<version>3.0.281</version>
<title>MediaBrowser.Common</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Server.Core</id>
<version>3.0.278</version>
<version>3.0.281</version>
<title>Media Browser.Server.Core</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>
@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Media Browser Server.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
<dependency id="MediaBrowser.Common" version="3.0.278" />
<dependency id="MediaBrowser.Common" version="3.0.281" />
</dependencies>
</metadata>
<files>

Loading…
Cancel
Save