mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
Merge branch 'master' of https://github.com/MediaBrowser/MediaBrowser
This commit is contained in:
commit
37aab84b2a
@ -1,13 +1,13 @@
|
|||||||
using System.IO;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities;
|
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Net;
|
using MediaBrowser.Controller.Net;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
|
using ServiceStack.Web;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using ServiceStack.Web;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Api
|
namespace MediaBrowser.Api
|
||||||
{
|
{
|
||||||
@ -52,11 +52,6 @@ namespace MediaBrowser.Api
|
|||||||
return ResultFactory.GetOptimizedResult(Request, result);
|
return ResultFactory.GetOptimizedResult(Request, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected object ToStreamResult(Stream stream, string contentType)
|
|
||||||
{
|
|
||||||
return ResultFactory.GetResult(stream, contentType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// To the optimized result using cache.
|
/// To the optimized result using cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -68,8 +68,6 @@ namespace MediaBrowser.Api.Library
|
|||||||
var rootFolderPath = user != null ? user.RootFolderPath : appPaths.DefaultUserViewsPath;
|
var rootFolderPath = user != null ? user.RootFolderPath : appPaths.DefaultUserViewsPath;
|
||||||
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
|
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);
|
||||||
|
|
||||||
ValidateNewMediaPath(fileSystem, rootFolderPath, path);
|
|
||||||
|
|
||||||
var shortcutFilename = Path.GetFileNameWithoutExtension(path);
|
var shortcutFilename = Path.GetFileNameWithoutExtension(path);
|
||||||
|
|
||||||
var lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension);
|
var lnk = Path.Combine(virtualFolderPath, shortcutFilename + ShortcutFileExtension);
|
||||||
@ -82,73 +80,5 @@ namespace MediaBrowser.Api.Library
|
|||||||
|
|
||||||
fileSystem.CreateShortcut(lnk, path);
|
fileSystem.CreateShortcut(lnk, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Validates that a new media path can be added
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="fileSystem">The file system.</param>
|
|
||||||
/// <param name="currentViewRootFolderPath">The current view root folder path.</param>
|
|
||||||
/// <param name="mediaPath">The media path.</param>
|
|
||||||
/// <exception cref="System.ArgumentException">
|
|
||||||
/// </exception>
|
|
||||||
private static void ValidateNewMediaPath(IFileSystem fileSystem, string currentViewRootFolderPath, string mediaPath)
|
|
||||||
{
|
|
||||||
var pathsInCurrentVIew = Directory.EnumerateFiles(currentViewRootFolderPath, ShortcutFileSearch, SearchOption.AllDirectories)
|
|
||||||
.Select(fileSystem.ResolveShortcut)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
// Don't allow duplicate sub-paths within the same user library, or it will result in duplicate items
|
|
||||||
// See comments in IsNewPathValid
|
|
||||||
var duplicate = pathsInCurrentVIew
|
|
||||||
.FirstOrDefault(p => !IsNewPathValid(fileSystem, mediaPath, p));
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(duplicate))
|
|
||||||
{
|
|
||||||
throw new ArgumentException(string.Format("The path cannot be added to the library because {0} already exists.", duplicate));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the current root folder doesn't already have a shortcut to the same path
|
|
||||||
duplicate = pathsInCurrentVIew
|
|
||||||
.FirstOrDefault(p => string.Equals(mediaPath, p, StringComparison.OrdinalIgnoreCase));
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(duplicate))
|
|
||||||
{
|
|
||||||
throw new ArgumentException(string.Format("The path {0} already exists in the library", mediaPath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Validates that a new path can be added based on an existing path
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="fileSystem">The file system.</param>
|
|
||||||
/// <param name="newPath">The new path.</param>
|
|
||||||
/// <param name="existingPath">The existing path.</param>
|
|
||||||
/// <returns><c>true</c> if [is new path valid] [the specified new path]; otherwise, <c>false</c>.</returns>
|
|
||||||
private static bool IsNewPathValid(IFileSystem fileSystem, string newPath, string existingPath)
|
|
||||||
{
|
|
||||||
// Example: D:\Movies is the existing path
|
|
||||||
// D:\ cannot be added
|
|
||||||
// Neither can D:\Movies\Kids
|
|
||||||
// A D:\Movies duplicate is ok here since that will be caught later
|
|
||||||
|
|
||||||
if (string.Equals(newPath, existingPath, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If enforceSubPathRestriction is true, validate the D:\Movies\Kids scenario
|
|
||||||
if (fileSystem.ContainsSubPath(existingPath, newPath))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate the D:\ scenario
|
|
||||||
if (fileSystem.ContainsSubPath(newPath, existingPath))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,6 +154,23 @@ namespace MediaBrowser.Api.LiveTv
|
|||||||
public string MaxEndDate { get; set; }
|
public string MaxEndDate { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Route("/LiveTv/Programs/Recommended", "GET")]
|
||||||
|
[Api(Description = "Gets available live tv epgs..")]
|
||||||
|
public class GetRecommendedPrograms : IReturn<QueryResult<ProgramInfoDto>>
|
||||||
|
{
|
||||||
|
[ApiMember(Name = "UserId", Description = "Optional filter by user id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET,POST")]
|
||||||
|
public string UserId { get; set; }
|
||||||
|
|
||||||
|
[ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||||
|
public int? Limit { get; set; }
|
||||||
|
|
||||||
|
[ApiMember(Name = "IsAiring", Description = "Optional. Filter by programs that are currently airing, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||||
|
public bool? IsAiring { get; set; }
|
||||||
|
|
||||||
|
[ApiMember(Name = "HasAired", Description = "Optional. Filter by programs that have completed airing, or not.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||||
|
public bool? HasAired { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
[Route("/LiveTv/Programs/{Id}", "GET")]
|
[Route("/LiveTv/Programs/{Id}", "GET")]
|
||||||
[Api(Description = "Gets a live tv program")]
|
[Api(Description = "Gets a live tv program")]
|
||||||
public class GetProgram : IReturn<ProgramInfoDto>
|
public class GetProgram : IReturn<ProgramInfoDto>
|
||||||
@ -331,6 +348,21 @@ namespace MediaBrowser.Api.LiveTv
|
|||||||
return ToOptimizedResult(result);
|
return ToOptimizedResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public object Get(GetRecommendedPrograms request)
|
||||||
|
{
|
||||||
|
var query = new RecommendedProgramQuery
|
||||||
|
{
|
||||||
|
UserId = request.UserId,
|
||||||
|
IsAiring = request.IsAiring,
|
||||||
|
Limit = request.Limit,
|
||||||
|
HasAired = request.HasAired
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = _liveTvManager.GetRecommendedPrograms(query, CancellationToken.None).Result;
|
||||||
|
|
||||||
|
return ToOptimizedResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
public object Post(GetPrograms request)
|
public object Post(GetPrograms request)
|
||||||
{
|
{
|
||||||
return Get(request);
|
return Get(request);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Common.MediaInfo;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
@ -734,7 +733,7 @@ namespace MediaBrowser.Api.Playback
|
|||||||
return "-";
|
return "-";
|
||||||
}
|
}
|
||||||
|
|
||||||
var type = InputType.AudioFile;
|
var type = InputType.File;
|
||||||
|
|
||||||
var inputPath = new[] { state.MediaPath };
|
var inputPath = new[] { state.MediaPath };
|
||||||
|
|
||||||
@ -1044,6 +1043,7 @@ namespace MediaBrowser.Api.Playback
|
|||||||
}
|
}
|
||||||
|
|
||||||
itemId = recording.Id;
|
itemId = recording.Id;
|
||||||
|
//state.RunTimeTicks = recording.RunTimeTicks;
|
||||||
state.SendInputOverStandardInput = recording.RecordingInfo.Status == RecordingStatus.InProgress;
|
state.SendInputOverStandardInput = recording.RecordingInfo.Status == RecordingStatus.InProgress;
|
||||||
}
|
}
|
||||||
else if (string.Equals(request.Type, "Channel", StringComparison.OrdinalIgnoreCase))
|
else if (string.Equals(request.Type, "Channel", StringComparison.OrdinalIgnoreCase))
|
||||||
@ -1092,6 +1092,7 @@ namespace MediaBrowser.Api.Playback
|
|||||||
: video.PlayableStreamFileNames.ToList();
|
: video.PlayableStreamFileNames.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.RunTimeTicks = item.RunTimeTicks;
|
||||||
itemId = item.Id;
|
itemId = item.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Common.MediaInfo;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.LiveTv;
|
using MediaBrowser.Controller.LiveTv;
|
||||||
|
using MediaBrowser.Controller.MediaInfo;
|
||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Model.Dto;
|
using MediaBrowser.Model.Dto;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Common.MediaInfo;
|
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.LiveTv;
|
using MediaBrowser.Controller.LiveTv;
|
||||||
|
using MediaBrowser.Controller.MediaInfo;
|
||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
using MediaBrowser.Model.Dto;
|
using MediaBrowser.Model.Dto;
|
||||||
@ -75,18 +75,23 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
/// <returns>System.Object.</returns>
|
/// <returns>System.Object.</returns>
|
||||||
protected object ProcessRequest(StreamRequest request)
|
protected object ProcessRequest(StreamRequest request)
|
||||||
{
|
{
|
||||||
var state = GetState(request, CancellationToken.None).Result;
|
return ProcessRequestAsync(request).Result;
|
||||||
|
|
||||||
return ProcessRequestAsync(state).Result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Processes the request async.
|
/// Processes the request async.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">The state.</param>
|
/// <param name="request">The request.</param>
|
||||||
/// <returns>Task{System.Object}.</returns>
|
/// <returns>Task{System.Object}.</returns>
|
||||||
public async Task<object> ProcessRequestAsync(StreamState state)
|
/// <exception cref="ArgumentException">
|
||||||
|
/// A video bitrate is required
|
||||||
|
/// or
|
||||||
|
/// An audio bitrate is required
|
||||||
|
/// </exception>
|
||||||
|
private async Task<object> ProcessRequestAsync(StreamRequest request)
|
||||||
{
|
{
|
||||||
|
var state = GetState(request, CancellationToken.None).Result;
|
||||||
|
|
||||||
if (!state.VideoRequest.VideoBitRate.HasValue && (!state.VideoRequest.VideoCodec.HasValue || state.VideoRequest.VideoCodec.Value != VideoCodecs.Copy))
|
if (!state.VideoRequest.VideoBitRate.HasValue && (!state.VideoRequest.VideoCodec.HasValue || state.VideoRequest.VideoCodec.Value != VideoCodecs.Copy))
|
||||||
{
|
{
|
||||||
throw new ArgumentException("A video bitrate is required");
|
throw new ArgumentException("A video bitrate is required");
|
||||||
@ -155,7 +160,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
/// <param name="state">The state.</param>
|
/// <param name="state">The state.</param>
|
||||||
/// <param name="audioBitrate">The audio bitrate.</param>
|
/// <param name="audioBitrate">The audio bitrate.</param>
|
||||||
/// <param name="videoBitrate">The video bitrate.</param>
|
/// <param name="videoBitrate">The video bitrate.</param>
|
||||||
private void GetPlaylistBitrates(StreamState state, out int audioBitrate, out int videoBitrate)
|
protected void GetPlaylistBitrates(StreamState state, out int audioBitrate, out int videoBitrate)
|
||||||
{
|
{
|
||||||
var audioBitrateParam = GetAudioBitrateParam(state);
|
var audioBitrateParam = GetAudioBitrateParam(state);
|
||||||
var videoBitrateParam = GetVideoBitrateParam(state);
|
var videoBitrateParam = GetVideoBitrateParam(state);
|
||||||
@ -269,7 +274,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
|
|
||||||
var threads = GetNumberOfThreads(false);
|
var threads = GetNumberOfThreads(false);
|
||||||
|
|
||||||
var args = string.Format("{0}{1} {2} {3} -i {4}{5} -map_metadata -1 -threads {6} {7} {8} -sc_threshold 0 {9} -hls_time 10 -start_number 0 -hls_list_size 1440 \"{10}\"",
|
var args = string.Format("{0}{1} {2} {3} -i {4}{5} -map_metadata -1 -threads {6} {7} {8} -sc_threshold 0 {9} -hls_time {10} -start_number 0 -hls_list_size 1440 \"{11}\"",
|
||||||
itsOffset,
|
itsOffset,
|
||||||
probeSize,
|
probeSize,
|
||||||
GetUserAgentParam(state.MediaPath),
|
GetUserAgentParam(state.MediaPath),
|
||||||
@ -280,6 +285,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
GetMapArgs(state),
|
GetMapArgs(state),
|
||||||
GetVideoArguments(state, performSubtitleConversions),
|
GetVideoArguments(state, performSubtitleConversions),
|
||||||
GetAudioArguments(state),
|
GetAudioArguments(state),
|
||||||
|
state.SegmentLength.ToString(UsCulture),
|
||||||
outputPath
|
outputPath
|
||||||
).Trim();
|
).Trim();
|
||||||
|
|
||||||
@ -291,10 +297,11 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
|
|
||||||
var bitrate = hlsVideoRequest.BaselineStreamAudioBitRate ?? 64000;
|
var bitrate = hlsVideoRequest.BaselineStreamAudioBitRate ?? 64000;
|
||||||
|
|
||||||
var lowBitrateParams = string.Format(" -threads {0} -vn -codec:a:0 libmp3lame -ac 2 -ab {2} -hls_time 10 -start_number 0 -hls_list_size 1440 \"{1}\"",
|
var lowBitrateParams = string.Format(" -threads {0} -vn -codec:a:0 libmp3lame -ac 2 -ab {1} -hls_time {2} -start_number 0 -hls_list_size 1440 \"{3}\"",
|
||||||
threads,
|
threads,
|
||||||
lowBitratePath,
|
bitrate / 2,
|
||||||
bitrate / 2);
|
state.SegmentLength.ToString(UsCulture),
|
||||||
|
lowBitratePath);
|
||||||
|
|
||||||
args += " " + lowBitrateParams;
|
args += " " + lowBitrateParams;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Common.MediaInfo;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.LiveTv;
|
using MediaBrowser.Controller.LiveTv;
|
||||||
|
using MediaBrowser.Controller.MediaInfo;
|
||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
|
using MediaBrowser.Model.Dto;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using ServiceStack;
|
using ServiceStack;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Playback.Hls
|
namespace MediaBrowser.Api.Playback.Hls
|
||||||
{
|
{
|
||||||
@ -28,6 +33,29 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
public int TimeStampOffsetMs { get; set; }
|
public int TimeStampOffsetMs { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Route("/Videos/{Id}/master.m3u8", "GET")]
|
||||||
|
[Api(Description = "Gets a video stream using HTTP live streaming.")]
|
||||||
|
public class GetMasterHlsVideoStream : VideoStreamRequest
|
||||||
|
{
|
||||||
|
[ApiMember(Name = "BaselineStreamAudioBitRate", Description = "Optional. Specify the audio bitrate for the baseline stream.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
|
||||||
|
public int? BaselineStreamAudioBitRate { get; set; }
|
||||||
|
|
||||||
|
[ApiMember(Name = "AppendBaselineStream", Description = "Optional. Whether or not to include a baseline audio-only stream in the master playlist.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
|
||||||
|
public bool AppendBaselineStream { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Route("/Videos/{Id}/main.m3u8", "GET")]
|
||||||
|
[Api(Description = "Gets a video stream using HTTP live streaming.")]
|
||||||
|
public class GetMainHlsVideoStream : VideoStreamRequest
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[Route("/Videos/{Id}/baseline.m3u8", "GET")]
|
||||||
|
[Api(Description = "Gets a video stream using HTTP live streaming.")]
|
||||||
|
public class GetBaselineHlsVideoStream : VideoStreamRequest
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class VideoHlsService
|
/// Class VideoHlsService
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -38,6 +66,128 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public object Get(GetMasterHlsVideoStream request)
|
||||||
|
{
|
||||||
|
var result = GetAsync(request).Result;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Get(GetMainHlsVideoStream request)
|
||||||
|
{
|
||||||
|
var result = GetPlaylistAsync(request, "main").Result;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Get(GetBaselineHlsVideoStream request)
|
||||||
|
{
|
||||||
|
var result = GetPlaylistAsync(request, "baseline").Result;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<object> GetPlaylistAsync(VideoStreamRequest request, string name)
|
||||||
|
{
|
||||||
|
var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
|
||||||
|
builder.AppendLine("#EXTM3U");
|
||||||
|
builder.AppendLine("#EXT-X-VERSION:3");
|
||||||
|
builder.AppendLine("#EXT-X-TARGETDURATION:" + state.SegmentLength.ToString(UsCulture));
|
||||||
|
builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");
|
||||||
|
|
||||||
|
var queryStringIndex = Request.RawUrl.IndexOf('?');
|
||||||
|
var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);
|
||||||
|
|
||||||
|
var seconds = TimeSpan.FromTicks(state.RunTimeTicks ?? 0).TotalSeconds;
|
||||||
|
|
||||||
|
var index = 0;
|
||||||
|
|
||||||
|
while (seconds > 0)
|
||||||
|
{
|
||||||
|
var length = seconds >= state.SegmentLength ? state.SegmentLength : seconds;
|
||||||
|
|
||||||
|
builder.AppendLine("#EXTINF:" + length.ToString(UsCulture));
|
||||||
|
|
||||||
|
builder.AppendLine(string.Format("hls/{0}/{1}.ts{2}" ,
|
||||||
|
|
||||||
|
name,
|
||||||
|
index.ToString(UsCulture),
|
||||||
|
queryString));
|
||||||
|
|
||||||
|
seconds -= state.SegmentLength;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.AppendLine("#EXT-X-ENDLIST");
|
||||||
|
|
||||||
|
var playlistText = builder.ToString();
|
||||||
|
|
||||||
|
return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<object> GetAsync(GetMasterHlsVideoStream request)
|
||||||
|
{
|
||||||
|
var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (!state.VideoRequest.VideoBitRate.HasValue && (!state.VideoRequest.VideoCodec.HasValue || state.VideoRequest.VideoCodec.Value != VideoCodecs.Copy))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("A video bitrate is required");
|
||||||
|
}
|
||||||
|
if (!state.Request.AudioBitRate.HasValue && (!state.Request.AudioCodec.HasValue || state.Request.AudioCodec.Value != AudioCodecs.Copy))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("An audio bitrate is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
int audioBitrate;
|
||||||
|
int videoBitrate;
|
||||||
|
GetPlaylistBitrates(state, out audioBitrate, out videoBitrate);
|
||||||
|
|
||||||
|
var appendBaselineStream = false;
|
||||||
|
var baselineStreamBitrate = 64000;
|
||||||
|
|
||||||
|
var hlsVideoRequest = state.VideoRequest as GetMasterHlsVideoStream;
|
||||||
|
if (hlsVideoRequest != null)
|
||||||
|
{
|
||||||
|
appendBaselineStream = hlsVideoRequest.AppendBaselineStream;
|
||||||
|
baselineStreamBitrate = hlsVideoRequest.BaselineStreamAudioBitRate ?? baselineStreamBitrate;
|
||||||
|
}
|
||||||
|
|
||||||
|
var playlistText = GetMasterPlaylistFileText(videoBitrate + audioBitrate, appendBaselineStream, baselineStreamBitrate);
|
||||||
|
|
||||||
|
return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetMasterPlaylistFileText(int bitrate, bool includeBaselineStream, int baselineStreamBitrate)
|
||||||
|
{
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
|
||||||
|
builder.AppendLine("#EXTM3U");
|
||||||
|
|
||||||
|
// Pad a little to satisfy the apple hls validator
|
||||||
|
var paddedBitrate = Convert.ToInt32(bitrate * 1.05);
|
||||||
|
|
||||||
|
var queryStringIndex = Request.RawUrl.IndexOf('?');
|
||||||
|
var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);
|
||||||
|
|
||||||
|
// Main stream
|
||||||
|
builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + paddedBitrate.ToString(UsCulture));
|
||||||
|
var playlistUrl = "main.m3u8" + queryString;
|
||||||
|
builder.AppendLine(playlistUrl);
|
||||||
|
|
||||||
|
// Low bitrate stream
|
||||||
|
if (includeBaselineStream)
|
||||||
|
{
|
||||||
|
builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + baselineStreamBitrate.ToString(UsCulture));
|
||||||
|
playlistUrl = "baseline.m3u8" + queryString;
|
||||||
|
builder.AppendLine(playlistUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the specified request.
|
/// Gets the specified request.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Common.MediaInfo;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Drawing;
|
using MediaBrowser.Controller.Drawing;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.LiveTv;
|
using MediaBrowser.Controller.LiveTv;
|
||||||
|
using MediaBrowser.Controller.MediaInfo;
|
||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using ServiceStack;
|
using ServiceStack;
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Common.MediaInfo;
|
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Drawing;
|
using MediaBrowser.Controller.Drawing;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.LiveTv;
|
using MediaBrowser.Controller.LiveTv;
|
||||||
|
using MediaBrowser.Controller.MediaInfo;
|
||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Model.Dto;
|
using MediaBrowser.Model.Dto;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Common.MediaInfo;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Drawing;
|
using MediaBrowser.Controller.Drawing;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.LiveTv;
|
using MediaBrowser.Controller.LiveTv;
|
||||||
|
using MediaBrowser.Controller.MediaInfo;
|
||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using ServiceStack;
|
using ServiceStack;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
using System.Threading;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Entities;
|
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Playback
|
namespace MediaBrowser.Api.Playback
|
||||||
{
|
{
|
||||||
@ -54,5 +54,9 @@ namespace MediaBrowser.Api.Playback
|
|||||||
public CancellationTokenSource StandardInputCancellationTokenSource { get; set; }
|
public CancellationTokenSource StandardInputCancellationTokenSource { get; set; }
|
||||||
|
|
||||||
public string LiveTvStreamId { get; set; }
|
public string LiveTvStreamId { get; set; }
|
||||||
|
|
||||||
|
public int SegmentLength = 10;
|
||||||
|
|
||||||
|
public long? RunTimeTicks;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,8 +65,6 @@
|
|||||||
<Compile Include="IO\IFileSystem.cs" />
|
<Compile Include="IO\IFileSystem.cs" />
|
||||||
<Compile Include="IO\ProgressStream.cs" />
|
<Compile Include="IO\ProgressStream.cs" />
|
||||||
<Compile Include="IO\StreamDefaults.cs" />
|
<Compile Include="IO\StreamDefaults.cs" />
|
||||||
<Compile Include="MediaInfo\MediaInfoResult.cs" />
|
|
||||||
<Compile Include="MediaInfo\IMediaEncoder.cs" />
|
|
||||||
<Compile Include="Net\BasePeriodicWebSocketListener.cs" />
|
<Compile Include="Net\BasePeriodicWebSocketListener.cs" />
|
||||||
<Compile Include="Configuration\IApplicationPaths.cs" />
|
<Compile Include="Configuration\IApplicationPaths.cs" />
|
||||||
<Compile Include="Net\HttpRequestOptions.cs" />
|
<Compile Include="Net\HttpRequestOptions.cs" />
|
||||||
|
@ -109,7 +109,7 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
public override string GetUserDataKey()
|
public override string GetUserDataKey()
|
||||||
{
|
{
|
||||||
var parent = Parent as MusicAlbum;
|
var parent = FindParent<MusicAlbum>();
|
||||||
|
|
||||||
if (parent != null)
|
if (parent != null)
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System.IO;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities;
|
|
||||||
using MediaBrowser.Model.LiveTv;
|
using MediaBrowser.Model.LiveTv;
|
||||||
using MediaBrowser.Model.Querying;
|
using MediaBrowser.Model.Querying;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -241,5 +240,14 @@ namespace MediaBrowser.Controller.LiveTv
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>GuideInfo.</returns>
|
/// <returns>GuideInfo.</returns>
|
||||||
GuideInfo GetGuideInfo();
|
GuideInfo GetGuideInfo();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the recommended programs.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="query">The query.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <returns>Task{QueryResult{ProgramInfoDto}}.</returns>
|
||||||
|
Task<QueryResult<ProgramInfoDto>> GetRecommendedPrograms(RecommendedProgramQuery query,
|
||||||
|
CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -37,7 +38,7 @@ namespace MediaBrowser.Controller.LiveTv
|
|||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task CancelSeriesTimerAsync(string timerId, CancellationToken cancellationToken);
|
Task CancelSeriesTimerAsync(string timerId, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes the recording asynchronous.
|
/// Deletes the recording asynchronous.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -77,7 +78,7 @@ namespace MediaBrowser.Controller.LiveTv
|
|||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task UpdateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken);
|
Task UpdateSeriesTimerAsync(SeriesTimerInfo info, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the channel image asynchronous. This only needs to be implemented if an image path or url cannot be supplied to ChannelInfo
|
/// Gets the channel image asynchronous. This only needs to be implemented if an image path or url cannot be supplied to ChannelInfo
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -102,7 +103,7 @@ namespace MediaBrowser.Controller.LiveTv
|
|||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task{ImageResponseInfo}.</returns>
|
/// <returns>Task{ImageResponseInfo}.</returns>
|
||||||
Task<StreamResponseInfo> GetProgramImageAsync(string programId, string channelId, CancellationToken cancellationToken);
|
Task<StreamResponseInfo> GetProgramImageAsync(string programId, string channelId, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the recordings asynchronous.
|
/// Gets the recordings asynchronous.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -123,21 +124,23 @@ namespace MediaBrowser.Controller.LiveTv
|
|||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task{TimerInfo}.</returns>
|
/// <returns>Task{TimerInfo}.</returns>
|
||||||
Task<SeriesTimerInfo> GetNewTimerDefaultsAsync(CancellationToken cancellationToken);
|
Task<SeriesTimerInfo> GetNewTimerDefaultsAsync(CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the series timers asynchronous.
|
/// Gets the series timers asynchronous.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task{IEnumerable{SeriesTimerInfo}}.</returns>
|
/// <returns>Task{IEnumerable{SeriesTimerInfo}}.</returns>
|
||||||
Task<IEnumerable<SeriesTimerInfo>> GetSeriesTimersAsync(CancellationToken cancellationToken);
|
Task<IEnumerable<SeriesTimerInfo>> GetSeriesTimersAsync(CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the programs asynchronous.
|
/// Gets the programs asynchronous.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="channelId">The channel identifier.</param>
|
/// <param name="channelId">The channel identifier.</param>
|
||||||
|
/// <param name="startDateUtc">The start date UTC.</param>
|
||||||
|
/// <param name="endDateUtc">The end date UTC.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task{IEnumerable{ProgramInfo}}.</returns>
|
/// <returns>Task{IEnumerable{ProgramInfo}}.</returns>
|
||||||
Task<IEnumerable<ProgramInfo>> GetProgramsAsync(string channelId, CancellationToken cancellationToken);
|
Task<IEnumerable<ProgramInfo>> GetProgramsAsync(string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the recording stream.
|
/// Gets the recording stream.
|
||||||
@ -162,5 +165,13 @@ namespace MediaBrowser.Controller.LiveTv
|
|||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task CloseLiveStream(string id, CancellationToken cancellationToken);
|
Task CloseLiveStream(string id, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Records the live stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">The identifier.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <returns>Task.</returns>
|
||||||
|
Task RecordLiveStream(string id, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Model.LiveTv;
|
using MediaBrowser.Model.LiveTv;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.LiveTv
|
namespace MediaBrowser.Controller.LiveTv
|
||||||
{
|
{
|
||||||
@ -28,6 +29,26 @@ namespace MediaBrowser.Controller.LiveTv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsAiring
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
|
||||||
|
return now >= ProgramInfo.StartDate && now < ProgramInfo.EndDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HasAired
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
|
||||||
|
return now >= ProgramInfo.EndDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override string GetClientTypeName()
|
public override string GetClientTypeName()
|
||||||
{
|
{
|
||||||
return "Program";
|
return "Program";
|
||||||
|
@ -125,6 +125,8 @@
|
|||||||
<Compile Include="LiveTv\SeriesTimerInfo.cs" />
|
<Compile Include="LiveTv\SeriesTimerInfo.cs" />
|
||||||
<Compile Include="LiveTv\TimerInfo.cs" />
|
<Compile Include="LiveTv\TimerInfo.cs" />
|
||||||
<Compile Include="Localization\ILocalizationManager.cs" />
|
<Compile Include="Localization\ILocalizationManager.cs" />
|
||||||
|
<Compile Include="MediaInfo\IMediaEncoder.cs" />
|
||||||
|
<Compile Include="MediaInfo\InternalMediaInfoResult.cs" />
|
||||||
<Compile Include="Net\IHasResultFactory.cs" />
|
<Compile Include="Net\IHasResultFactory.cs" />
|
||||||
<Compile Include="Net\IHttpResultFactory.cs" />
|
<Compile Include="Net\IHttpResultFactory.cs" />
|
||||||
<Compile Include="Net\IHttpServer.cs" />
|
<Compile Include="Net\IHttpServer.cs" />
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Common.MediaInfo;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Movies;
|
using MediaBrowser.Controller.Entities.Movies;
|
||||||
@ -178,7 +177,7 @@ namespace MediaBrowser.Controller.MediaInfo
|
|||||||
|
|
||||||
Directory.CreateDirectory(parentPath);
|
Directory.CreateDirectory(parentPath);
|
||||||
|
|
||||||
await _encoder.ExtractImage(inputPath, type, video.Video3DFormat, time, path, cancellationToken).ConfigureAwait(false);
|
await _encoder.ExtractImage(inputPath, type, false, video.Video3DFormat, time, path, cancellationToken).ConfigureAwait(false);
|
||||||
chapter.ImagePath = path;
|
chapter.ImagePath = path;
|
||||||
changesMade = true;
|
changesMade = true;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
|
|
||||||
namespace MediaBrowser.Common.MediaInfo
|
namespace MediaBrowser.Controller.MediaInfo
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interface IMediaEncoder
|
/// Interface IMediaEncoder
|
||||||
@ -27,12 +27,13 @@ namespace MediaBrowser.Common.MediaInfo
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="inputFiles">The input files.</param>
|
/// <param name="inputFiles">The input files.</param>
|
||||||
/// <param name="type">The type.</param>
|
/// <param name="type">The type.</param>
|
||||||
|
/// <param name="isAudio">if set to <c>true</c> [is audio].</param>
|
||||||
/// <param name="threedFormat">The threed format.</param>
|
/// <param name="threedFormat">The threed format.</param>
|
||||||
/// <param name="offset">The offset.</param>
|
/// <param name="offset">The offset.</param>
|
||||||
/// <param name="outputPath">The output path.</param>
|
/// <param name="outputPath">The output path.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task ExtractImage(string[] inputFiles, InputType type, Video3DFormat? threedFormat, TimeSpan? offset, string outputPath, CancellationToken cancellationToken);
|
Task ExtractImage(string[] inputFiles, InputType type, bool isAudio, Video3DFormat? threedFormat, TimeSpan? offset, string outputPath, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extracts the text subtitle.
|
/// Extracts the text subtitle.
|
||||||
@ -62,7 +63,7 @@ namespace MediaBrowser.Common.MediaInfo
|
|||||||
/// <param name="type">The type.</param>
|
/// <param name="type">The type.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task<MediaInfoResult> GetMediaInfo(string[] inputFiles, InputType type, CancellationToken cancellationToken);
|
Task<InternalMediaInfoResult> GetMediaInfo(string[] inputFiles, InputType type, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the probe size argument.
|
/// Gets the probe size argument.
|
||||||
@ -86,13 +87,9 @@ namespace MediaBrowser.Common.MediaInfo
|
|||||||
public enum InputType
|
public enum InputType
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The audio file
|
/// The file
|
||||||
/// </summary>
|
/// </summary>
|
||||||
AudioFile,
|
File,
|
||||||
/// <summary>
|
|
||||||
/// The video file
|
|
||||||
/// </summary>
|
|
||||||
VideoFile,
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The bluray
|
/// The bluray
|
||||||
/// </summary>
|
/// </summary>
|
@ -1,12 +1,12 @@
|
|||||||
using MediaBrowser.Model.Entities;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
using MediaBrowser.Model.Entities;
|
||||||
|
|
||||||
namespace MediaBrowser.Common.MediaInfo
|
namespace MediaBrowser.Controller.MediaInfo
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class MediaInfoResult
|
/// Class MediaInfoResult
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MediaInfoResult
|
public class InternalMediaInfoResult
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the streams.
|
/// Gets or sets the streams.
|
@ -3,7 +3,6 @@ using System.Collections.Generic;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using MediaBrowser.Common.MediaInfo;
|
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
|
|
||||||
@ -29,7 +28,7 @@ namespace MediaBrowser.Controller.MediaInfo
|
|||||||
{
|
{
|
||||||
var inputPath = isoMount == null ? new[] { videoPath } : new[] { isoMount.MountedPath };
|
var inputPath = isoMount == null ? new[] { videoPath } : new[] { isoMount.MountedPath };
|
||||||
|
|
||||||
type = InputType.VideoFile;
|
type = InputType.File;
|
||||||
|
|
||||||
switch (videoType)
|
switch (videoType)
|
||||||
{
|
{
|
||||||
@ -87,7 +86,7 @@ namespace MediaBrowser.Controller.MediaInfo
|
|||||||
/// <returns>InputType.</returns>
|
/// <returns>InputType.</returns>
|
||||||
public static InputType GetInputType(VideoType? videoType, IsoType? isoType)
|
public static InputType GetInputType(VideoType? videoType, IsoType? isoType)
|
||||||
{
|
{
|
||||||
var type = InputType.AudioFile;
|
var type = InputType.File;
|
||||||
|
|
||||||
if (videoType.HasValue)
|
if (videoType.HasValue)
|
||||||
{
|
{
|
||||||
@ -119,12 +118,22 @@ namespace MediaBrowser.Controller.MediaInfo
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<MediaStream> GetMediaStreams(MediaInfoResult data)
|
public static Model.Entities.MediaInfo GetMediaInfo(InternalMediaInfoResult data)
|
||||||
{
|
{
|
||||||
var internalStreams = data.streams ?? new MediaStreamInfo[] { };
|
var internalStreams = data.streams ?? new MediaStreamInfo[] { };
|
||||||
|
|
||||||
return internalStreams.Select(s => GetMediaStream(s, data.format))
|
var info = new Model.Entities.MediaInfo();
|
||||||
.Where(i => i != null);
|
|
||||||
|
info.MediaStreams = internalStreams.Select(s => GetMediaStream(s, data.format))
|
||||||
|
.Where(i => i != null)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (data.format != null)
|
||||||
|
{
|
||||||
|
info.Format = data.format.format_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
||||||
|
@ -223,7 +223,10 @@ namespace MediaBrowser.Model.Configuration
|
|||||||
public string TranscodingTempPath { get; set; }
|
public string TranscodingTempPath { get; set; }
|
||||||
|
|
||||||
public bool EnableAutomaticRestart { get; set; }
|
public bool EnableAutomaticRestart { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public LiveTvOptions LiveTvOptions { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
|
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -287,6 +290,8 @@ namespace MediaBrowser.Model.Configuration
|
|||||||
{
|
{
|
||||||
MaxBackdrops = 1
|
MaxBackdrops = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
LiveTvOptions = new LiveTvOptions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,4 +308,9 @@ namespace MediaBrowser.Model.Configuration
|
|||||||
HighQuality,
|
HighQuality,
|
||||||
MaxQuality
|
MaxQuality
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class LiveTvOptions
|
||||||
|
{
|
||||||
|
public int? GuideDays { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using MediaBrowser.Model.Entities;
|
using System.Diagnostics;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
@ -10,6 +11,7 @@ namespace MediaBrowser.Model.Dto
|
|||||||
/// This is strictly used as a data transfer object from the api layer.
|
/// This is strictly used as a data transfer object from the api layer.
|
||||||
/// This holds information about a BaseItem in a format that is convenient for the client.
|
/// This holds information about a BaseItem in a format that is convenient for the client.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[DebuggerDisplay("Name = {Name}, ID = {Id}, Type = {Type}")]
|
||||||
public class BaseItemDto : IHasProviderIds, INotifyPropertyChanged, IItemDto
|
public class BaseItemDto : IHasProviderIds, INotifyPropertyChanged, IItemDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Dto
|
namespace MediaBrowser.Model.Dto
|
||||||
@ -7,6 +8,7 @@ namespace MediaBrowser.Model.Dto
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is used by the api to get information about a Person within a BaseItem
|
/// This is used by the api to get information about a Person within a BaseItem
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[DebuggerDisplay("Name = {Name}, Role = {Role}, Type = {Type}")]
|
||||||
public class BaseItemPerson : INotifyPropertyChanged
|
public class BaseItemPerson : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Dto
|
namespace MediaBrowser.Model.Dto
|
||||||
@ -7,6 +8,7 @@ namespace MediaBrowser.Model.Dto
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class ChapterInfo
|
/// Class ChapterInfo
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[DebuggerDisplay("Name = {Name}")]
|
||||||
public class ChapterInfoDto : INotifyPropertyChanged
|
public class ChapterInfoDto : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Dto
|
namespace MediaBrowser.Model.Dto
|
||||||
@ -7,6 +8,7 @@ namespace MediaBrowser.Model.Dto
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class StudioDto
|
/// Class StudioDto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[DebuggerDisplay("Name = {Name}")]
|
||||||
public class StudioDto
|
public class StudioDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
@ -8,6 +9,7 @@ namespace MediaBrowser.Model.Dto
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class UserDto
|
/// Class UserDto
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[DebuggerDisplay("Name = {Name}, ID = {Id}, HasPassword = {HasPassword}")]
|
||||||
public class UserDto : INotifyPropertyChanged, IItemDto
|
public class UserDto : INotifyPropertyChanged, IItemDto
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Entities
|
namespace MediaBrowser.Model.Entities
|
||||||
@ -6,6 +7,7 @@ namespace MediaBrowser.Model.Entities
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is a stub class containing only basic information about an item
|
/// This is a stub class containing only basic information about an item
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[DebuggerDisplay("Name = {Name}, ID = {Id}, Type = {Type}")]
|
||||||
public class BaseItemInfo
|
public class BaseItemInfo
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Entities
|
namespace MediaBrowser.Model.Entities
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class MediaStream
|
/// Class MediaStream
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[DebuggerDisplay("StreamType = {Type}")]
|
||||||
public class MediaStream
|
public class MediaStream
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -145,4 +148,24 @@ namespace MediaBrowser.Model.Entities
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Subtitle
|
Subtitle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class MediaInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the media streams.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The media streams.</value>
|
||||||
|
public List<MediaStream> MediaStreams { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the format.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The format.</value>
|
||||||
|
public string Format { get; set; }
|
||||||
|
|
||||||
|
public MediaInfo()
|
||||||
|
{
|
||||||
|
MediaStreams = new List<MediaStream>();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,4 +32,31 @@ namespace MediaBrowser.Model.LiveTv
|
|||||||
ChannelIdList = new string[] { };
|
ChannelIdList = new string[] { };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class RecommendedProgramQuery
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the user identifier.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The user identifier.</value>
|
||||||
|
public string UserId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether this instance is airing.
|
||||||
|
/// </summary>
|
||||||
|
/// <value><c>true</c> if this instance is airing; otherwise, <c>false</c>.</value>
|
||||||
|
public bool? IsAiring { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether this instance has aired.
|
||||||
|
/// </summary>
|
||||||
|
/// <value><c>null</c> if [has aired] contains no value, <c>true</c> if [has aired]; otherwise, <c>false</c>.</value>
|
||||||
|
public bool? HasAired { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum number of items to return
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The limit.</value>
|
||||||
|
public int? Limit { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
using MediaBrowser.Model.Entities;
|
using System.Diagnostics;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Session
|
namespace MediaBrowser.Model.Session
|
||||||
{
|
{
|
||||||
|
[DebuggerDisplay("Client = {Client}, Username = {UserName}")]
|
||||||
public class SessionInfoDto : INotifyPropertyChanged
|
public class SessionInfoDto : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.MediaInfo;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
|
using MediaBrowser.Controller.MediaInfo;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
@ -163,7 +163,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
|
|
||||||
Directory.CreateDirectory(parentPath);
|
Directory.CreateDirectory(parentPath);
|
||||||
|
|
||||||
await _mediaEncoder.ExtractImage(new[] { item.Path }, InputType.AudioFile, null, null, path, cancellationToken).ConfigureAwait(false);
|
await _mediaEncoder.ExtractImage(new[] { item.Path }, InputType.File, true, null, null, path, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using MediaBrowser.Common.MediaInfo;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Configuration;
|
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.MediaInfo;
|
using MediaBrowser.Controller.MediaInfo;
|
||||||
using MediaBrowser.Controller.Providers;
|
using MediaBrowser.Controller.Providers;
|
||||||
@ -104,11 +103,11 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
/// <exception cref="System.ArgumentNullException">inputPath
|
/// <exception cref="System.ArgumentNullException">inputPath
|
||||||
/// or
|
/// or
|
||||||
/// cache</exception>
|
/// cache</exception>
|
||||||
protected async Task<MediaInfoResult> GetMediaInfo(BaseItem item, IIsoMount isoMount, CancellationToken cancellationToken)
|
protected async Task<InternalMediaInfoResult> GetMediaInfo(BaseItem item, IIsoMount isoMount, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
var type = InputType.AudioFile;
|
var type = InputType.File;
|
||||||
var inputPath = isoMount == null ? new[] { item.Path } : new[] { isoMount.MountedPath };
|
var inputPath = isoMount == null ? new[] { item.Path } : new[] { isoMount.MountedPath };
|
||||||
|
|
||||||
var video = item as Video;
|
var video = item as Video;
|
||||||
@ -146,7 +145,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
/// Normalizes the FF probe result.
|
/// Normalizes the FF probe result.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="result">The result.</param>
|
/// <param name="result">The result.</param>
|
||||||
protected void NormalizeFFProbeResult(MediaInfoResult result)
|
protected void NormalizeFFProbeResult(InternalMediaInfoResult result)
|
||||||
{
|
{
|
||||||
if (result.format != null && result.format.tags != null)
|
if (result.format != null && result.format.tags != null)
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.MediaInfo;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
@ -58,9 +57,9 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <param name="data">The data.</param>
|
/// <param name="data">The data.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
protected Task Fetch(Audio audio, CancellationToken cancellationToken, MediaInfoResult data)
|
protected Task Fetch(Audio audio, CancellationToken cancellationToken, InternalMediaInfoResult data)
|
||||||
{
|
{
|
||||||
var mediaStreams = MediaEncoderHelpers.GetMediaStreams(data).ToList();
|
var mediaStreams = MediaEncoderHelpers.GetMediaInfo(data).MediaStreams;
|
||||||
|
|
||||||
audio.HasEmbeddedImage = mediaStreams.Any(i => i.Type == MediaStreamType.Video);
|
audio.HasEmbeddedImage = mediaStreams.Any(i => i.Type == MediaStreamType.Video);
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using DvdLib.Ifo;
|
using DvdLib.Ifo;
|
||||||
using MediaBrowser.Common.MediaInfo;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Localization;
|
using MediaBrowser.Controller.Localization;
|
||||||
@ -310,7 +309,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
/// <param name="data">The data.</param>
|
/// <param name="data">The data.</param>
|
||||||
/// <param name="isoMount">The iso mount.</param>
|
/// <param name="isoMount">The iso mount.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
protected async Task Fetch(Video video, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken, MediaInfoResult data, IIsoMount isoMount)
|
protected async Task Fetch(Video video, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken, InternalMediaInfoResult data, IIsoMount isoMount)
|
||||||
{
|
{
|
||||||
if (data.format != null)
|
if (data.format != null)
|
||||||
{
|
{
|
||||||
@ -323,7 +322,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var mediaStreams = MediaEncoderHelpers.GetMediaStreams(data).ToList();
|
var mediaStreams = MediaEncoderHelpers.GetMediaInfo(data).MediaStreams;
|
||||||
|
|
||||||
var chapters = data.Chapters ?? new List<ChapterInfo>();
|
var chapters = data.Chapters ?? new List<ChapterInfo>();
|
||||||
|
|
||||||
@ -370,7 +369,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
/// <param name="video">The video.</param>
|
/// <param name="video">The video.</param>
|
||||||
/// <param name="force">if set to <c>true</c> [force].</param>
|
/// <param name="force">if set to <c>true</c> [force].</param>
|
||||||
/// <param name="data">The data.</param>
|
/// <param name="data">The data.</param>
|
||||||
private void FetchWtvInfo(Video video, bool force, MediaInfoResult data)
|
private void FetchWtvInfo(Video video, bool force, InternalMediaInfoResult data)
|
||||||
{
|
{
|
||||||
if (data.format == null || data.format.tags == null)
|
if (data.format == null || data.format.tags == null)
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.MediaInfo;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
@ -255,7 +254,7 @@ namespace MediaBrowser.Providers.MediaInfo
|
|||||||
|
|
||||||
var inputPath = MediaEncoderHelpers.GetInputArgument(video.Path, video.LocationType == LocationType.Remote, video.VideoType, video.IsoType, isoMount, video.PlayableStreamFileNames, 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);
|
await _mediaEncoder.ExtractImage(inputPath, type, false, video.Video3DFormat, imageOffset, path, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
video.PrimaryImagePath = path;
|
video.PrimaryImagePath = path;
|
||||||
}
|
}
|
||||||
|
@ -360,19 +360,11 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||||||
|
|
||||||
var compress = ShouldCompressResponse(requestContext, contentType);
|
var compress = ShouldCompressResponse(requestContext, contentType);
|
||||||
|
|
||||||
var hasOptions = GetStaticResult(requestContext, responseHeaders, contentType, factoryFn, compress, isHeadRequest);
|
var hasOptions = GetStaticResult(requestContext, responseHeaders, contentType, factoryFn, compress, isHeadRequest).Result;
|
||||||
|
|
||||||
return GetStaticResultTask(hasOptions, responseHeaders);
|
AddResponseHeaders(hasOptions, responseHeaders);
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<object> GetStaticResultTask(Task<IHasOptions> optionsTask,
|
return hasOptions;
|
||||||
IEnumerable<KeyValuePair<string, string>> responseHeaders)
|
|
||||||
{
|
|
||||||
var options = await optionsTask.ConfigureAwait(false);
|
|
||||||
|
|
||||||
AddResponseHeaders(options, responseHeaders);
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -670,4 +662,4 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,6 +2,7 @@
|
|||||||
using ServiceStack.Web;
|
using ServiceStack.Web;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@ -13,6 +14,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||||||
public class StreamWriter : IStreamWriter, IHasOptions
|
public class StreamWriter : IStreamWriter, IHasOptions
|
||||||
{
|
{
|
||||||
private ILogger Logger { get; set; }
|
private ILogger Logger { get; set; }
|
||||||
|
|
||||||
|
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the source stream.
|
/// Gets or sets the source stream.
|
||||||
@ -50,6 +53,11 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||||||
Logger = logger;
|
Logger = logger;
|
||||||
|
|
||||||
Options["Content-Type"] = contentType;
|
Options["Content-Type"] = contentType;
|
||||||
|
|
||||||
|
if (source.CanSeek)
|
||||||
|
{
|
||||||
|
Options["Content-Length"] = source.Length.ToString(UsCulture);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -368,7 +368,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private const string InternalVersionNumber = "2";
|
private const string InternalVersionNumber = "3";
|
||||||
|
|
||||||
public Guid GetInternalChannelId(string serviceName, string externalId)
|
public Guid GetInternalChannelId(string serviceName, string externalId)
|
||||||
{
|
{
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Drawing;
|
using MediaBrowser.Controller.Drawing;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.LiveTv;
|
using MediaBrowser.Controller.LiveTv;
|
||||||
|
using MediaBrowser.Controller.MediaInfo;
|
||||||
using MediaBrowser.Controller.Persistence;
|
using MediaBrowser.Controller.Persistence;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.LiveTv;
|
using MediaBrowser.Model.LiveTv;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
using MediaBrowser.Model.Querying;
|
using MediaBrowser.Model.Querying;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -23,30 +26,37 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class LiveTvManager
|
/// Class LiveTvManager
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class LiveTvManager : ILiveTvManager
|
public class LiveTvManager : ILiveTvManager, IDisposable
|
||||||
{
|
{
|
||||||
private readonly IServerApplicationPaths _appPaths;
|
private readonly IServerConfigurationManager _config;
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IItemRepository _itemRepo;
|
private readonly IItemRepository _itemRepo;
|
||||||
private readonly IUserManager _userManager;
|
private readonly IUserManager _userManager;
|
||||||
|
private readonly IUserDataManager _userDataManager;
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
private readonly IMediaEncoder _mediaEncoder;
|
||||||
|
|
||||||
private readonly LiveTvDtoService _tvDtoService;
|
private readonly LiveTvDtoService _tvDtoService;
|
||||||
|
|
||||||
private readonly List<ILiveTvService> _services = new List<ILiveTvService>();
|
private readonly List<ILiveTvService> _services = new List<ILiveTvService>();
|
||||||
|
|
||||||
|
private readonly ConcurrentDictionary<string, LiveStreamInfo> _openStreams =
|
||||||
|
new ConcurrentDictionary<string, LiveStreamInfo>();
|
||||||
|
|
||||||
private List<Guid> _channelIdList = new List<Guid>();
|
private List<Guid> _channelIdList = new List<Guid>();
|
||||||
private Dictionary<Guid, LiveTvProgram> _programs = new Dictionary<Guid, LiveTvProgram>();
|
private Dictionary<Guid, LiveTvProgram> _programs = new Dictionary<Guid, LiveTvProgram>();
|
||||||
|
|
||||||
public LiveTvManager(IServerApplicationPaths appPaths, IFileSystem fileSystem, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager)
|
public LiveTvManager(IServerConfigurationManager config, IFileSystem fileSystem, ILogger logger, IItemRepository itemRepo, IImageProcessor imageProcessor, IUserDataManager userDataManager, IDtoService dtoService, IUserManager userManager, ILibraryManager libraryManager, IMediaEncoder mediaEncoder)
|
||||||
{
|
{
|
||||||
_appPaths = appPaths;
|
_config = config;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_itemRepo = itemRepo;
|
_itemRepo = itemRepo;
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
_libraryManager = libraryManager;
|
_libraryManager = libraryManager;
|
||||||
|
_mediaEncoder = mediaEncoder;
|
||||||
|
_userDataManager = userDataManager;
|
||||||
|
|
||||||
_tvDtoService = new LiveTvDtoService(dtoService, userDataManager, imageProcessor, logger, _itemRepo);
|
_tvDtoService = new LiveTvDtoService(dtoService, userDataManager, imageProcessor, logger, _itemRepo);
|
||||||
}
|
}
|
||||||
@ -180,7 +190,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||||||
|
|
||||||
var recording = recordings.First(i => _tvDtoService.GetInternalRecordingId(service.Name, i.Id) == new Guid(id));
|
var recording = recordings.First(i => _tvDtoService.GetInternalRecordingId(service.Name, i.Id) == new Guid(id));
|
||||||
|
|
||||||
return await service.GetRecordingStream(recording.Id, cancellationToken).ConfigureAwait(false);
|
var result = await service.GetRecordingStream(recording.Id, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(result.Id))
|
||||||
|
{
|
||||||
|
_openStreams.AddOrUpdate(result.Id, result, (key, info) => result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<LiveStreamInfo> GetChannelStream(string id, CancellationToken cancellationToken)
|
public async Task<LiveStreamInfo> GetChannelStream(string id, CancellationToken cancellationToken)
|
||||||
@ -189,12 +206,19 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||||||
|
|
||||||
var channel = GetInternalChannel(id);
|
var channel = GetInternalChannel(id);
|
||||||
|
|
||||||
return await service.GetChannelStream(channel.ChannelInfo.Id, cancellationToken).ConfigureAwait(false);
|
var result = await service.GetChannelStream(channel.ChannelInfo.Id, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(result.Id))
|
||||||
|
{
|
||||||
|
_openStreams.AddOrUpdate(result.Id, result, (key, info) => result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<LiveTvChannel> GetChannel(ChannelInfo channelInfo, string serviceName, CancellationToken cancellationToken)
|
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));
|
var path = Path.Combine(_config.ApplicationPaths.ItemsByNamePath, "channels", _fileSystem.GetValidFilename(channelInfo.Name));
|
||||||
|
|
||||||
var fileInfo = new DirectoryInfo(path);
|
var fileInfo = new DirectoryInfo(path);
|
||||||
|
|
||||||
@ -407,7 +431,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||||||
}
|
}
|
||||||
|
|
||||||
var returnArray = programs
|
var returnArray = programs
|
||||||
.OrderBy(i => i.ProgramInfo.StartDate)
|
|
||||||
.Select(i =>
|
.Select(i =>
|
||||||
{
|
{
|
||||||
var channel = GetChannel(i);
|
var channel = GetChannel(i);
|
||||||
@ -429,6 +452,138 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<QueryResult<ProgramInfoDto>> GetRecommendedPrograms(RecommendedProgramQuery query, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
IEnumerable<LiveTvProgram> programs = _programs.Values;
|
||||||
|
|
||||||
|
var user = _userManager.GetUserById(new Guid(query.UserId));
|
||||||
|
|
||||||
|
// Avoid implicitly captured closure
|
||||||
|
var currentUser = user;
|
||||||
|
programs = programs.Where(i => i.IsParentalAllowed(currentUser));
|
||||||
|
|
||||||
|
if (query.IsAiring.HasValue)
|
||||||
|
{
|
||||||
|
var val = query.IsAiring.Value;
|
||||||
|
programs = programs.Where(i => i.IsAiring == val);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.HasAired.HasValue)
|
||||||
|
{
|
||||||
|
var val = query.HasAired.Value;
|
||||||
|
programs = programs.Where(i => i.HasAired == val);
|
||||||
|
}
|
||||||
|
|
||||||
|
var serviceName = ActiveService.Name;
|
||||||
|
|
||||||
|
var programList = programs.ToList();
|
||||||
|
|
||||||
|
var genres = programList.SelectMany(i => i.Genres)
|
||||||
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
|
.Select(i => _libraryManager.GetGenre(i))
|
||||||
|
.ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
programs = programList.OrderByDescending(i => GetRecommendationScore(i.ProgramInfo, user.Id, serviceName, genres))
|
||||||
|
.ThenBy(i => i.ProgramInfo.StartDate);
|
||||||
|
|
||||||
|
if (query.Limit.HasValue)
|
||||||
|
{
|
||||||
|
programs = programs.Take(query.Limit.Value)
|
||||||
|
.OrderBy(i => i.ProgramInfo.StartDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
var returnArray = programs
|
||||||
|
.Select(i =>
|
||||||
|
{
|
||||||
|
var channel = GetChannel(i);
|
||||||
|
|
||||||
|
var channelName = channel == null ? null : channel.ChannelInfo.Name;
|
||||||
|
|
||||||
|
return _tvDtoService.GetProgramInfoDto(i, channelName, user);
|
||||||
|
})
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
await AddRecordingInfo(returnArray, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var result = new QueryResult<ProgramInfoDto>
|
||||||
|
{
|
||||||
|
Items = returnArray,
|
||||||
|
TotalRecordCount = returnArray.Length
|
||||||
|
};
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetRecommendationScore(ProgramInfo program, Guid userId, string serviceName, Dictionary<string, Genre> genres)
|
||||||
|
{
|
||||||
|
var score = 0;
|
||||||
|
|
||||||
|
if (program.IsLive)
|
||||||
|
{
|
||||||
|
score++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (program.IsSeries && !program.IsRepeat)
|
||||||
|
{
|
||||||
|
score++;
|
||||||
|
}
|
||||||
|
|
||||||
|
var internalChannelId = _tvDtoService.GetInternalChannelId(serviceName, program.ChannelId);
|
||||||
|
var channel = GetInternalChannel(internalChannelId);
|
||||||
|
|
||||||
|
var channelUserdata = _userDataManager.GetUserData(userId, channel.GetUserDataKey());
|
||||||
|
|
||||||
|
if ((channelUserdata.Likes ?? false))
|
||||||
|
{
|
||||||
|
score += 2;
|
||||||
|
}
|
||||||
|
else if (!(channelUserdata.Likes ?? true))
|
||||||
|
{
|
||||||
|
score -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channelUserdata.IsFavorite)
|
||||||
|
{
|
||||||
|
score += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
score += GetGenreScore(program.Genres, userId, genres);
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetGenreScore(IEnumerable<string> programGenres, Guid userId, Dictionary<string, Genre> genres)
|
||||||
|
{
|
||||||
|
return programGenres.Select(i =>
|
||||||
|
{
|
||||||
|
var score = 0;
|
||||||
|
|
||||||
|
Genre genre;
|
||||||
|
|
||||||
|
if (genres.TryGetValue(i, out genre))
|
||||||
|
{
|
||||||
|
var genreUserdata = _userDataManager.GetUserData(userId, genre.GetUserDataKey());
|
||||||
|
|
||||||
|
if ((genreUserdata.Likes ?? false))
|
||||||
|
{
|
||||||
|
score++;
|
||||||
|
}
|
||||||
|
else if (!(genreUserdata.Likes ?? true))
|
||||||
|
{
|
||||||
|
score--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (genreUserdata.IsFavorite)
|
||||||
|
{
|
||||||
|
score += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
|
||||||
|
}).Sum();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task AddRecordingInfo(IEnumerable<ProgramInfoDto> programs, CancellationToken cancellationToken)
|
private async Task AddRecordingInfo(IEnumerable<ProgramInfoDto> programs, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var timers = await ActiveService.GetTimersAsync(cancellationToken).ConfigureAwait(false);
|
var timers = await ActiveService.GetTimersAsync(cancellationToken).ConfigureAwait(false);
|
||||||
@ -505,6 +660,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||||||
numComplete = 0;
|
numComplete = 0;
|
||||||
var programs = new List<LiveTvProgram>();
|
var programs = new List<LiveTvProgram>();
|
||||||
|
|
||||||
|
var guideDays = GetGuideDays(list.Count);
|
||||||
|
|
||||||
foreach (var item in list)
|
foreach (var item in list)
|
||||||
{
|
{
|
||||||
// Avoid implicitly captured closure
|
// Avoid implicitly captured closure
|
||||||
@ -512,7 +669,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var channelPrograms = await service.GetProgramsAsync(currentChannel.ChannelInfo.Id, cancellationToken).ConfigureAwait(false);
|
var start = DateTime.UtcNow.AddHours(-1);
|
||||||
|
var end = start.AddDays(guideDays);
|
||||||
|
|
||||||
|
var channelPrograms = await service.GetProgramsAsync(currentChannel.ChannelInfo.Id, start, end, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
var programTasks = channelPrograms.Select(program => GetProgram(program, currentChannel.ChannelInfo.ChannelType, service.Name, cancellationToken));
|
var programTasks = channelPrograms.Select(program => GetProgram(program, currentChannel.ChannelInfo.ChannelType, service.Name, cancellationToken));
|
||||||
var programEntities = await Task.WhenAll(programTasks).ConfigureAwait(false);
|
var programEntities = await Task.WhenAll(programTasks).ConfigureAwait(false);
|
||||||
@ -538,6 +698,23 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||||||
_programs = programs.ToDictionary(i => i.Id);
|
_programs = programs.ToDictionary(i => i.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private double GetGuideDays(int channelCount)
|
||||||
|
{
|
||||||
|
if (_config.Configuration.LiveTvOptions.GuideDays.HasValue)
|
||||||
|
{
|
||||||
|
return _config.Configuration.LiveTvOptions.GuideDays.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var programsPerDay = channelCount * 48;
|
||||||
|
|
||||||
|
const int maxPrograms = 32000;
|
||||||
|
|
||||||
|
var days = Math.Round(((double)maxPrograms) / programsPerDay);
|
||||||
|
|
||||||
|
// No less than 2, no more than 14
|
||||||
|
return Math.Max(2, Math.Min(days, 14));
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<IEnumerable<Tuple<string, ChannelInfo>>> GetChannels(ILiveTvService service, CancellationToken cancellationToken)
|
private async Task<IEnumerable<Tuple<string, ChannelInfo>>> GetChannels(ILiveTvService service, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var channels = await service.GetChannelsAsync(cancellationToken).ConfigureAwait(false);
|
var channels = await service.GetChannelsAsync(cancellationToken).ConfigureAwait(false);
|
||||||
@ -1047,5 +1224,36 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
|||||||
EndDate = endDate
|
EndDate = endDate
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly object _disposeLock = new object();
|
||||||
|
/// <summary>
|
||||||
|
/// Releases unmanaged and - optionally - managed resources.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||||
|
protected virtual void Dispose(bool dispose)
|
||||||
|
{
|
||||||
|
if (dispose)
|
||||||
|
{
|
||||||
|
lock (_disposeLock)
|
||||||
|
{
|
||||||
|
foreach (var stream in _openStreams.Values.ToList())
|
||||||
|
{
|
||||||
|
var task = CloseLiveStream(stream.Id, CancellationToken.None);
|
||||||
|
|
||||||
|
Task.WaitAll(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
_openStreams.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Common.MediaInfo;
|
using MediaBrowser.Controller.MediaInfo;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
@ -104,10 +104,10 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
|||||||
/// <param name="type">The type.</param>
|
/// <param name="type">The type.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
public Task<MediaInfoResult> GetMediaInfo(string[] inputFiles, InputType type,
|
public Task<InternalMediaInfoResult> GetMediaInfo(string[] inputFiles, InputType type,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return GetMediaInfoInternal(GetInputArgument(inputFiles, type), type != InputType.AudioFile,
|
return GetMediaInfoInternal(GetInputArgument(inputFiles, type), type != InputType.File,
|
||||||
GetProbeSizeArgument(type), cancellationToken);
|
GetProbeSizeArgument(type), cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,8 +125,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
|||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case InputType.Dvd:
|
case InputType.Dvd:
|
||||||
case InputType.VideoFile:
|
case InputType.File:
|
||||||
case InputType.AudioFile:
|
|
||||||
inputPath = GetConcatInputArgument(inputFiles);
|
inputPath = GetConcatInputArgument(inputFiles);
|
||||||
break;
|
break;
|
||||||
case InputType.Bluray:
|
case InputType.Bluray:
|
||||||
@ -173,7 +172,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
|||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task{MediaInfoResult}.</returns>
|
/// <returns>Task{MediaInfoResult}.</returns>
|
||||||
/// <exception cref="System.ApplicationException"></exception>
|
/// <exception cref="System.ApplicationException"></exception>
|
||||||
private async Task<MediaInfoResult> GetMediaInfoInternal(string inputPath, bool extractChapters,
|
private async Task<InternalMediaInfoResult> GetMediaInfoInternal(string inputPath, bool extractChapters,
|
||||||
string probeSizeArgument,
|
string probeSizeArgument,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@ -206,7 +205,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
|||||||
|
|
||||||
await _ffProbeResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
|
await _ffProbeResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
MediaInfoResult result;
|
InternalMediaInfoResult result;
|
||||||
string standardError = null;
|
string standardError = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -236,7 +235,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
|||||||
process.BeginErrorReadLine();
|
process.BeginErrorReadLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
result = _jsonSerializer.DeserializeFromStream<MediaInfoResult>(process.StandardOutput.BaseStream);
|
result = _jsonSerializer.DeserializeFromStream<InternalMediaInfoResult>(process.StandardOutput.BaseStream);
|
||||||
|
|
||||||
if (extractChapters)
|
if (extractChapters)
|
||||||
{
|
{
|
||||||
@ -307,7 +306,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="result">The result.</param>
|
/// <param name="result">The result.</param>
|
||||||
/// <param name="standardError">The standard error.</param>
|
/// <param name="standardError">The standard error.</param>
|
||||||
private void AddChapters(MediaInfoResult result, string standardError)
|
private void AddChapters(InternalMediaInfoResult result, string standardError)
|
||||||
{
|
{
|
||||||
var lines = standardError.Split('\n').Select(l => l.TrimStart());
|
var lines = standardError.Split('\n').Select(l => l.TrimStart());
|
||||||
|
|
||||||
@ -797,19 +796,20 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="inputFiles">The input files.</param>
|
/// <param name="inputFiles">The input files.</param>
|
||||||
/// <param name="type">The type.</param>
|
/// <param name="type">The type.</param>
|
||||||
|
/// <param name="isAudio">if set to <c>true</c> [is audio].</param>
|
||||||
/// <param name="threedFormat">The threed format.</param>
|
/// <param name="threedFormat">The threed format.</param>
|
||||||
/// <param name="offset">The offset.</param>
|
/// <param name="offset">The offset.</param>
|
||||||
/// <param name="outputPath">The output path.</param>
|
/// <param name="outputPath">The output path.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
/// <exception cref="System.ArgumentException">Must use inputPath list overload</exception>
|
/// <exception cref="System.ArgumentException">Must use inputPath list overload</exception>
|
||||||
public async Task ExtractImage(string[] inputFiles, InputType type, Video3DFormat? threedFormat, TimeSpan? offset, string outputPath, CancellationToken cancellationToken)
|
public async Task ExtractImage(string[] inputFiles, InputType type, bool isAudio, Video3DFormat? threedFormat, TimeSpan? offset, string outputPath, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var resourcePool = type == InputType.AudioFile ? _audioImageResourcePool : _videoImageResourcePool;
|
var resourcePool = isAudio ? _audioImageResourcePool : _videoImageResourcePool;
|
||||||
|
|
||||||
var inputArgument = GetInputArgument(inputFiles, type);
|
var inputArgument = GetInputArgument(inputFiles, type);
|
||||||
|
|
||||||
if (type != InputType.AudioFile)
|
if (!isAudio)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -6,7 +6,6 @@ using MediaBrowser.Common.Extensions;
|
|||||||
using MediaBrowser.Common.Implementations;
|
using MediaBrowser.Common.Implementations;
|
||||||
using MediaBrowser.Common.Implementations.ScheduledTasks;
|
using MediaBrowser.Common.Implementations.ScheduledTasks;
|
||||||
using MediaBrowser.Common.IO;
|
using MediaBrowser.Common.IO;
|
||||||
using MediaBrowser.Common.MediaInfo;
|
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Common.Progress;
|
using MediaBrowser.Common.Progress;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
@ -284,9 +283,6 @@ namespace MediaBrowser.ServerApplication
|
|||||||
|
|
||||||
DtoService = new DtoService(Logger, LibraryManager, UserManager, UserDataManager, ItemRepository, ImageProcessor);
|
DtoService = new DtoService(Logger, LibraryManager, UserManager, UserDataManager, ItemRepository, ImageProcessor);
|
||||||
RegisterSingleInstance(DtoService);
|
RegisterSingleInstance(DtoService);
|
||||||
|
|
||||||
LiveTvManager = new LiveTvManager(ApplicationPaths, FileSystemManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager);
|
|
||||||
RegisterSingleInstance(LiveTvManager);
|
|
||||||
progress.Report(15);
|
progress.Report(15);
|
||||||
|
|
||||||
var innerProgress = new ActionableProgress<double>();
|
var innerProgress = new ActionableProgress<double>();
|
||||||
@ -295,6 +291,9 @@ namespace MediaBrowser.ServerApplication
|
|||||||
await RegisterMediaEncoder(innerProgress).ConfigureAwait(false);
|
await RegisterMediaEncoder(innerProgress).ConfigureAwait(false);
|
||||||
progress.Report(90);
|
progress.Report(90);
|
||||||
|
|
||||||
|
LiveTvManager = new LiveTvManager(ServerConfigurationManager, FileSystemManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, MediaEncoder);
|
||||||
|
RegisterSingleInstance(LiveTvManager);
|
||||||
|
|
||||||
var displayPreferencesTask = Task.Run(async () => await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false));
|
var displayPreferencesTask = Task.Run(async () => await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false));
|
||||||
var itemsTask = Task.Run(async () => await ConfigureItemRepositories().ConfigureAwait(false));
|
var itemsTask = Task.Run(async () => await ConfigureItemRepositories().ConfigureAwait(false));
|
||||||
var userdataTask = Task.Run(async () => await ConfigureUserDataRepositories().ConfigureAwait(false));
|
var userdataTask = Task.Run(async () => await ConfigureUserDataRepositories().ConfigureAwait(false));
|
||||||
|
@ -505,6 +505,7 @@ namespace MediaBrowser.WebDashboard.Api
|
|||||||
"livetvtimer.js",
|
"livetvtimer.js",
|
||||||
"livetvseriestimer.js",
|
"livetvseriestimer.js",
|
||||||
"livetvseriestimers.js",
|
"livetvseriestimers.js",
|
||||||
|
"livetvsettings.js",
|
||||||
"livetvsuggested.js",
|
"livetvsuggested.js",
|
||||||
"livetvtimers.js",
|
"livetvtimers.js",
|
||||||
"loginpage.js",
|
"loginpage.js",
|
||||||
|
@ -438,7 +438,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
|
|||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
if (options.channelIds) {
|
if (options.channelIds && options.channelIds.length > 1800) {
|
||||||
|
|
||||||
return self.ajax({
|
return self.ajax({
|
||||||
type: "POST",
|
type: "POST",
|
||||||
@ -458,6 +458,17 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.getLiveTvRecommendedPrograms = function (options) {
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
return self.ajax({
|
||||||
|
type: "GET",
|
||||||
|
url: self.getUrl("LiveTv/Programs/Recommended", options),
|
||||||
|
dataType: "json"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
self.getLiveTvRecordings = function (options) {
|
self.getLiveTvRecordings = function (options) {
|
||||||
|
|
||||||
var url = self.getUrl("LiveTv/Recordings", options || {});
|
var url = self.getUrl("LiveTv/Recordings", options || {});
|
||||||
|
@ -184,6 +184,9 @@
|
|||||||
<Content Include="dashboard-ui\livetvseriestimer.html">
|
<Content Include="dashboard-ui\livetvseriestimer.html">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="dashboard-ui\livetvsettings.html">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Content Include="dashboard-ui\livetvtimers.html">
|
<Content Include="dashboard-ui\livetvtimers.html">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
@ -433,6 +436,9 @@
|
|||||||
<Content Include="dashboard-ui\scripts\livetvseriestimer.js">
|
<Content Include="dashboard-ui\scripts\livetvseriestimer.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="dashboard-ui\scripts\livetvsettings.js">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Content Include="dashboard-ui\scripts\livetvtimer.js">
|
<Content Include="dashboard-ui\scripts\livetvtimer.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
<package id="MediaBrowser.ApiClient.Javascript" version="3.0.224" targetFramework="net45" />
|
<package id="MediaBrowser.ApiClient.Javascript" version="3.0.226" targetFramework="net45" />
|
||||||
</packages>
|
</packages>
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>MediaBrowser.Common.Internal</id>
|
<id>MediaBrowser.Common.Internal</id>
|
||||||
<version>3.0.298</version>
|
<version>3.0.299</version>
|
||||||
<title>MediaBrowser.Common.Internal</title>
|
<title>MediaBrowser.Common.Internal</title>
|
||||||
<authors>Luke</authors>
|
<authors>Luke</authors>
|
||||||
<owners>ebr,Luke,scottisafool</owners>
|
<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>
|
<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>
|
<copyright>Copyright © Media Browser 2013</copyright>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency id="MediaBrowser.Common" version="3.0.298" />
|
<dependency id="MediaBrowser.Common" version="3.0.299" />
|
||||||
<dependency id="NLog" version="2.1.0" />
|
<dependency id="NLog" version="2.1.0" />
|
||||||
<dependency id="SimpleInjector" version="2.4.0" />
|
<dependency id="SimpleInjector" version="2.4.0" />
|
||||||
<dependency id="sharpcompress" version="0.10.2" />
|
<dependency id="sharpcompress" version="0.10.2" />
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>MediaBrowser.Common</id>
|
<id>MediaBrowser.Common</id>
|
||||||
<version>3.0.298</version>
|
<version>3.0.299</version>
|
||||||
<title>MediaBrowser.Common</title>
|
<title>MediaBrowser.Common</title>
|
||||||
<authors>Media Browser Team</authors>
|
<authors>Media Browser Team</authors>
|
||||||
<owners>ebr,Luke,scottisafool</owners>
|
<owners>ebr,Luke,scottisafool</owners>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>MediaBrowser.Server.Core</id>
|
<id>MediaBrowser.Server.Core</id>
|
||||||
<version>3.0.298</version>
|
<version>3.0.299</version>
|
||||||
<title>Media Browser.Server.Core</title>
|
<title>Media Browser.Server.Core</title>
|
||||||
<authors>Media Browser Team</authors>
|
<authors>Media Browser Team</authors>
|
||||||
<owners>ebr,Luke,scottisafool</owners>
|
<owners>ebr,Luke,scottisafool</owners>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
<description>Contains core components required to build plugins for Media Browser Server.</description>
|
<description>Contains core components required to build plugins for Media Browser Server.</description>
|
||||||
<copyright>Copyright © Media Browser 2013</copyright>
|
<copyright>Copyright © Media Browser 2013</copyright>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency id="MediaBrowser.Common" version="3.0.298" />
|
<dependency id="MediaBrowser.Common" version="3.0.299" />
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</metadata>
|
</metadata>
|
||||||
<files>
|
<files>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user