mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-07-08 23:00:51 +02:00
commit
367e17826e
|
@ -245,12 +245,8 @@ namespace MediaBrowser.Api
|
||||||
|
|
||||||
item.OriginalTitle = string.IsNullOrWhiteSpace(request.OriginalTitle) ? null : request.OriginalTitle;
|
item.OriginalTitle = string.IsNullOrWhiteSpace(request.OriginalTitle) ? null : request.OriginalTitle;
|
||||||
|
|
||||||
var hasCriticRating = item as IHasCriticRating;
|
item.CriticRating = request.CriticRating;
|
||||||
if (hasCriticRating != null)
|
item.CriticRatingSummary = request.CriticRatingSummary;
|
||||||
{
|
|
||||||
hasCriticRating.CriticRating = request.CriticRating;
|
|
||||||
hasCriticRating.CriticRatingSummary = request.CriticRatingSummary;
|
|
||||||
}
|
|
||||||
|
|
||||||
item.DisplayMediaType = request.DisplayMediaType;
|
item.DisplayMediaType = request.DisplayMediaType;
|
||||||
item.CommunityRating = request.CommunityRating;
|
item.CommunityRating = request.CommunityRating;
|
||||||
|
@ -279,11 +275,7 @@ namespace MediaBrowser.Api
|
||||||
item.Tagline = request.Taglines.FirstOrDefault();
|
item.Tagline = request.Taglines.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasShortOverview = item as IHasShortOverview;
|
item.ShortOverview = request.ShortOverview;
|
||||||
if (hasShortOverview != null)
|
|
||||||
{
|
|
||||||
hasShortOverview.ShortOverview = request.ShortOverview;
|
|
||||||
}
|
|
||||||
|
|
||||||
item.Keywords = request.Keywords;
|
item.Keywords = request.Keywords;
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ using System.Threading.Tasks;
|
||||||
using CommonIO;
|
using CommonIO;
|
||||||
using MediaBrowser.Api.Playback.Progressive;
|
using MediaBrowser.Api.Playback.Progressive;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
|
using MediaBrowser.Controller.Entities.TV;
|
||||||
using MediaBrowser.Server.Implementations.LiveTv.EmbyTV;
|
using MediaBrowser.Server.Implementations.LiveTv.EmbyTV;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.LiveTv
|
namespace MediaBrowser.Api.LiveTv
|
||||||
|
@ -390,6 +391,7 @@ namespace MediaBrowser.Api.LiveTv
|
||||||
public bool? EnableUserData { get; set; }
|
public bool? EnableUserData { get; set; }
|
||||||
|
|
||||||
public string SeriesTimerId { get; set; }
|
public string SeriesTimerId { get; set; }
|
||||||
|
public string LibrarySeriesId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fields to return within the items, in addition to basic information
|
/// Fields to return within the items, in addition to basic information
|
||||||
|
@ -990,6 +992,17 @@ namespace MediaBrowser.Api.LiveTv
|
||||||
query.SeriesTimerId = request.SeriesTimerId;
|
query.SeriesTimerId = request.SeriesTimerId;
|
||||||
query.Genres = (request.Genres ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
query.Genres = (request.Genres ?? String.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(request.LibrarySeriesId))
|
||||||
|
{
|
||||||
|
query.IsSeries = true;
|
||||||
|
|
||||||
|
var series = _libraryManager.GetItemById(request.LibrarySeriesId) as Series;
|
||||||
|
if (series != null)
|
||||||
|
{
|
||||||
|
query.Name = series.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var result = await _liveTvManager.GetPrograms(query, GetDtoOptions(request), CancellationToken.None).ConfigureAwait(false);
|
var result = await _liveTvManager.GetPrograms(query, GetDtoOptions(request), CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
||||||
return ToOptimizedResult(result);
|
return ToOptimizedResult(result);
|
||||||
|
|
|
@ -317,13 +317,32 @@ namespace MediaBrowser.Api.Playback
|
||||||
}
|
}
|
||||||
if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(encodingOptions.VaapiDevice))
|
if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(encodingOptions.VaapiDevice))
|
||||||
{
|
{
|
||||||
return GetAvailableEncoder("h264_vaapi", defaultEncoder);
|
if (IsVaapiSupported(state))
|
||||||
|
{
|
||||||
|
return GetAvailableEncoder("h264_vaapi", defaultEncoder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultEncoder;
|
return defaultEncoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IsVaapiSupported(StreamState state)
|
||||||
|
{
|
||||||
|
var videoStream = state.VideoStream;
|
||||||
|
|
||||||
|
if (videoStream != null)
|
||||||
|
{
|
||||||
|
// vaapi will throw an error with this input
|
||||||
|
// [vaapi @ 0x7faed8000960] No VAAPI support for codec mpeg4 profile -99.
|
||||||
|
if (string.Equals(videoStream.Codec, "mpeg4", StringComparison.OrdinalIgnoreCase) && videoStream.Level == -99)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private string GetAvailableEncoder(string preferredEncoder, string defaultEncoder)
|
private string GetAvailableEncoder(string preferredEncoder, string defaultEncoder)
|
||||||
{
|
{
|
||||||
if (MediaEncoder.SupportsEncoder(preferredEncoder))
|
if (MediaEncoder.SupportsEncoder(preferredEncoder))
|
||||||
|
|
|
@ -887,7 +887,6 @@ namespace MediaBrowser.Api.Playback.Hls
|
||||||
var mapArgs = state.IsOutputVideo ? GetMapArgs(state) : string.Empty;
|
var mapArgs = state.IsOutputVideo ? GetMapArgs(state) : string.Empty;
|
||||||
var enableSplittingOnNonKeyFrames = state.VideoRequest.EnableSplittingOnNonKeyFrames && string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase);
|
var enableSplittingOnNonKeyFrames = state.VideoRequest.EnableSplittingOnNonKeyFrames && string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase);
|
||||||
enableSplittingOnNonKeyFrames = false;
|
enableSplittingOnNonKeyFrames = false;
|
||||||
|
|
||||||
// TODO: check libavformat version for 57 50.100 and use -hls_flags split_by_time
|
// TODO: check libavformat version for 57 50.100 and use -hls_flags split_by_time
|
||||||
var hlsProtocolSupportsSplittingByTime = false;
|
var hlsProtocolSupportsSplittingByTime = false;
|
||||||
|
|
||||||
|
|
|
@ -125,7 +125,7 @@ namespace MediaBrowser.Api.Playback
|
||||||
|
|
||||||
SetDeviceSpecificData(item, result.MediaSource, profile, authInfo, request.MaxStreamingBitrate,
|
SetDeviceSpecificData(item, result.MediaSource, profile, authInfo, request.MaxStreamingBitrate,
|
||||||
request.StartTimeTicks ?? 0, result.MediaSource.Id, request.AudioStreamIndex,
|
request.StartTimeTicks ?? 0, result.MediaSource.Id, request.AudioStreamIndex,
|
||||||
request.SubtitleStreamIndex, request.PlaySessionId, request.UserId);
|
request.SubtitleStreamIndex, request.MaxAudioChannels, request.PlaySessionId, request.UserId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -167,7 +167,7 @@ namespace MediaBrowser.Api.Playback
|
||||||
{
|
{
|
||||||
var mediaSourceId = request.MediaSourceId;
|
var mediaSourceId = request.MediaSourceId;
|
||||||
|
|
||||||
SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate ?? profile.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex, request.UserId);
|
SetDeviceSpecificData(request.Id, info, profile, authInfo, request.MaxStreamingBitrate ?? profile.MaxStreamingBitrate, request.StartTimeTicks ?? 0, mediaSourceId, request.AudioStreamIndex, request.SubtitleStreamIndex, request.MaxAudioChannels, request.UserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ToOptimizedResult(info);
|
return ToOptimizedResult(info);
|
||||||
|
@ -230,13 +230,14 @@ namespace MediaBrowser.Api.Playback
|
||||||
string mediaSourceId,
|
string mediaSourceId,
|
||||||
int? audioStreamIndex,
|
int? audioStreamIndex,
|
||||||
int? subtitleStreamIndex,
|
int? subtitleStreamIndex,
|
||||||
|
int? maxAudioChannels,
|
||||||
string userId)
|
string userId)
|
||||||
{
|
{
|
||||||
var item = _libraryManager.GetItemById(itemId);
|
var item = _libraryManager.GetItemById(itemId);
|
||||||
|
|
||||||
foreach (var mediaSource in result.MediaSources)
|
foreach (var mediaSource in result.MediaSources)
|
||||||
{
|
{
|
||||||
SetDeviceSpecificData(item, mediaSource, profile, auth, maxBitrate, startTimeTicks, mediaSourceId, audioStreamIndex, subtitleStreamIndex, result.PlaySessionId, userId);
|
SetDeviceSpecificData(item, mediaSource, profile, auth, maxBitrate, startTimeTicks, mediaSourceId, audioStreamIndex, subtitleStreamIndex, maxAudioChannels, result.PlaySessionId, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
SortMediaSources(result, maxBitrate);
|
SortMediaSources(result, maxBitrate);
|
||||||
|
@ -251,6 +252,7 @@ namespace MediaBrowser.Api.Playback
|
||||||
string mediaSourceId,
|
string mediaSourceId,
|
||||||
int? audioStreamIndex,
|
int? audioStreamIndex,
|
||||||
int? subtitleStreamIndex,
|
int? subtitleStreamIndex,
|
||||||
|
int? maxAudioChannels,
|
||||||
string playSessionId,
|
string playSessionId,
|
||||||
string userId)
|
string userId)
|
||||||
{
|
{
|
||||||
|
@ -262,7 +264,8 @@ namespace MediaBrowser.Api.Playback
|
||||||
Context = EncodingContext.Streaming,
|
Context = EncodingContext.Streaming,
|
||||||
DeviceId = auth.DeviceId,
|
DeviceId = auth.DeviceId,
|
||||||
ItemId = item.Id.ToString("N"),
|
ItemId = item.Id.ToString("N"),
|
||||||
Profile = profile
|
Profile = profile,
|
||||||
|
MaxAudioChannels = maxAudioChannels
|
||||||
};
|
};
|
||||||
|
|
||||||
if (string.Equals(mediaSourceId, mediaSource.Id, StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(mediaSourceId, mediaSource.Id, StringComparison.OrdinalIgnoreCase))
|
||||||
|
|
|
@ -127,7 +127,9 @@ namespace MediaBrowser.Common.Implementations.Logging
|
||||||
{
|
{
|
||||||
for (var i = 0; i < paramList.Length; i++)
|
for (var i = 0; i < paramList.Length; i++)
|
||||||
{
|
{
|
||||||
message = message.Replace("{" + i + "}", paramList[i].ToString());
|
var obj = paramList[i];
|
||||||
|
|
||||||
|
message = message.Replace("{" + i + "}", (obj == null ? "null" : obj.ToString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Dlna
|
|
||||||
{
|
|
||||||
public interface ISsdpHandler
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -33,7 +33,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class BaseItem
|
/// Class BaseItem
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class BaseItem : IHasProviderIds, ILibraryItem, IHasImages, IHasUserData, IHasMetadata, IHasLookupInfo<ItemLookupInfo>
|
public abstract class BaseItem : IHasProviderIds, IHasImages, IHasUserData, IHasMetadata, IHasLookupInfo<ItemLookupInfo>
|
||||||
{
|
{
|
||||||
protected BaseItem()
|
protected BaseItem()
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,7 +7,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// Plugins derive from and export this class to create a folder that will appear in the root along
|
/// Plugins derive from and export this class to create a folder that will appear in the root along
|
||||||
/// with all the other actual physical folders in the system.
|
/// with all the other actual physical folders in the system.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class BasePluginFolder : Folder, ICollectionFolder, IByReferenceItem
|
public abstract class BasePluginFolder : Folder, ICollectionFolder
|
||||||
{
|
{
|
||||||
public virtual string CollectionType
|
public virtual string CollectionType
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// This is a marker class that tells us that a particular item type may be physically resolved
|
|
||||||
/// more than once within the library and we need to be sure to resolve them all to the same
|
|
||||||
/// instance of that item.
|
|
||||||
/// </summary>
|
|
||||||
public interface IByReferenceItem
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
namespace MediaBrowser.Controller.Entities
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Interface IHasCriticRating
|
|
||||||
/// </summary>
|
|
||||||
public interface IHasCriticRating
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the critic rating.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The critic rating.</value>
|
|
||||||
float? CriticRating { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the critic rating summary.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The critic rating summary.</value>
|
|
||||||
string CriticRatingSummary { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
|
||||||
{
|
|
||||||
public interface IHasShortOverview
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the short overview.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The short overview.</value>
|
|
||||||
string ShortOverview { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Interface ILibraryItem
|
|
||||||
/// </summary>
|
|
||||||
public interface ILibraryItem
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the name.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The name.</value>
|
|
||||||
string Name { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the id.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The id.</value>
|
|
||||||
Guid Id { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the path.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The path.</value>
|
|
||||||
string Path { get; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
|
||||||
{
|
|
||||||
public class ImageSourceInfo
|
|
||||||
{
|
|
||||||
public Guid ImagePathMD5 { get; set; }
|
|
||||||
public Guid ImageUrlMD5 { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,7 +15,7 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class Movie
|
/// Class Movie
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Movie : Video, IHasCriticRating, IHasSpecialFeatures, IHasBudget, IHasTrailers, IHasAwards, IHasMetascore, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping, IHasOriginalTitle
|
public class Movie : Video, IHasSpecialFeatures, IHasBudget, IHasTrailers, IHasAwards, IHasMetascore, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping, IHasOriginalTitle
|
||||||
{
|
{
|
||||||
public List<Guid> SpecialFeatureIds { get; set; }
|
public List<Guid> SpecialFeatureIds { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class Trailer
|
/// Class Trailer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Trailer : Video, IHasCriticRating, IHasBudget, IHasMetascore, IHasOriginalTitle, IHasLookupInfo<TrailerInfo>
|
public class Trailer : Video, IHasBudget, IHasMetascore, IHasOriginalTitle, IHasLookupInfo<TrailerInfo>
|
||||||
{
|
{
|
||||||
public Trailer()
|
public Trailer()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1668,16 +1668,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
var val = query.MinCriticRating.Value;
|
var val = query.MinCriticRating.Value;
|
||||||
|
|
||||||
var hasCriticRating = item as IHasCriticRating;
|
if (!(item.CriticRating.HasValue && item.CriticRating >= val))
|
||||||
|
|
||||||
if (hasCriticRating != null)
|
|
||||||
{
|
|
||||||
if (!(hasCriticRating.CriticRating.HasValue && hasCriticRating.CriticRating >= val))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
IHasAspectRatio,
|
IHasAspectRatio,
|
||||||
ISupportsPlaceHolders,
|
ISupportsPlaceHolders,
|
||||||
IHasMediaSources,
|
IHasMediaSources,
|
||||||
IHasShortOverview,
|
|
||||||
IThemeMedia
|
IThemeMedia
|
||||||
{
|
{
|
||||||
[IgnoreDataMember]
|
[IgnoreDataMember]
|
||||||
|
|
|
@ -78,14 +78,17 @@ namespace MediaBrowser.Controller.LiveTv
|
||||||
|
|
||||||
protected override string CreateSortName()
|
protected override string CreateSortName()
|
||||||
{
|
{
|
||||||
double number = 0;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Number))
|
if (!string.IsNullOrEmpty(Number))
|
||||||
{
|
{
|
||||||
double.TryParse(Number, NumberStyles.Any, CultureInfo.InvariantCulture, out number);
|
double number = 0;
|
||||||
|
|
||||||
|
if (double.TryParse(Number, NumberStyles.Any, CultureInfo.InvariantCulture, out number))
|
||||||
|
{
|
||||||
|
return number.ToString("00000-") + (Name ?? string.Empty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return number.ToString("00000-") + (Name ?? string.Empty);
|
return Number + "-" + (Name ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
[IgnoreDataMember]
|
[IgnoreDataMember]
|
||||||
|
|
|
@ -111,7 +111,6 @@
|
||||||
<Compile Include="Dlna\IDlnaManager.cs" />
|
<Compile Include="Dlna\IDlnaManager.cs" />
|
||||||
<Compile Include="Dlna\IEventManager.cs" />
|
<Compile Include="Dlna\IEventManager.cs" />
|
||||||
<Compile Include="Dlna\IMediaReceiverRegistrar.cs" />
|
<Compile Include="Dlna\IMediaReceiverRegistrar.cs" />
|
||||||
<Compile Include="Dlna\ISsdpHandler.cs" />
|
|
||||||
<Compile Include="Dlna\IUpnpService.cs" />
|
<Compile Include="Dlna\IUpnpService.cs" />
|
||||||
<Compile Include="Dlna\SsdpMessageEventArgs.cs" />
|
<Compile Include="Dlna\SsdpMessageEventArgs.cs" />
|
||||||
<Compile Include="Drawing\IImageProcessor.cs" />
|
<Compile Include="Drawing\IImageProcessor.cs" />
|
||||||
|
@ -132,10 +131,8 @@
|
||||||
<Compile Include="Entities\Game.cs" />
|
<Compile Include="Entities\Game.cs" />
|
||||||
<Compile Include="Entities\GameGenre.cs" />
|
<Compile Include="Entities\GameGenre.cs" />
|
||||||
<Compile Include="Entities\GameSystem.cs" />
|
<Compile Include="Entities\GameSystem.cs" />
|
||||||
<Compile Include="Entities\IByReferenceItem.cs" />
|
|
||||||
<Compile Include="Entities\IHasAspectRatio.cs" />
|
<Compile Include="Entities\IHasAspectRatio.cs" />
|
||||||
<Compile Include="Entities\IHasBudget.cs" />
|
<Compile Include="Entities\IHasBudget.cs" />
|
||||||
<Compile Include="Entities\IHasCriticRating.cs" />
|
|
||||||
<Compile Include="Entities\IHasDisplayOrder.cs" />
|
<Compile Include="Entities\IHasDisplayOrder.cs" />
|
||||||
<Compile Include="Entities\IHasId.cs" />
|
<Compile Include="Entities\IHasId.cs" />
|
||||||
<Compile Include="Entities\IHasImages.cs" />
|
<Compile Include="Entities\IHasImages.cs" />
|
||||||
|
@ -146,15 +143,12 @@
|
||||||
<Compile Include="Entities\IHasProgramAttributes.cs" />
|
<Compile Include="Entities\IHasProgramAttributes.cs" />
|
||||||
<Compile Include="Entities\IHasScreenshots.cs" />
|
<Compile Include="Entities\IHasScreenshots.cs" />
|
||||||
<Compile Include="Entities\IHasSeries.cs" />
|
<Compile Include="Entities\IHasSeries.cs" />
|
||||||
<Compile Include="Entities\IHasShortOverview.cs" />
|
|
||||||
<Compile Include="Entities\IHasSpecialFeatures.cs" />
|
<Compile Include="Entities\IHasSpecialFeatures.cs" />
|
||||||
<Compile Include="Entities\IHasStartDate.cs" />
|
<Compile Include="Entities\IHasStartDate.cs" />
|
||||||
<Compile Include="Entities\IHasTrailers.cs" />
|
<Compile Include="Entities\IHasTrailers.cs" />
|
||||||
<Compile Include="Entities\IHasUserData.cs" />
|
<Compile Include="Entities\IHasUserData.cs" />
|
||||||
<Compile Include="Entities\IHiddenFromDisplay.cs" />
|
<Compile Include="Entities\IHiddenFromDisplay.cs" />
|
||||||
<Compile Include="Entities\IItemByName.cs" />
|
<Compile Include="Entities\IItemByName.cs" />
|
||||||
<Compile Include="Entities\ILibraryItem.cs" />
|
|
||||||
<Compile Include="Entities\ImageSourceInfo.cs" />
|
|
||||||
<Compile Include="Entities\IMetadataContainer.cs" />
|
<Compile Include="Entities\IMetadataContainer.cs" />
|
||||||
<Compile Include="Entities\InternalItemsQuery.cs" />
|
<Compile Include="Entities\InternalItemsQuery.cs" />
|
||||||
<Compile Include="Entities\InternalPeopleQuery.cs" />
|
<Compile Include="Entities\InternalPeopleQuery.cs" />
|
||||||
|
|
|
@ -185,14 +185,12 @@ namespace MediaBrowser.Controller.Providers
|
||||||
{
|
{
|
||||||
var text = reader.ReadElementContentAsString();
|
var text = reader.ReadElementContentAsString();
|
||||||
|
|
||||||
var hasCriticRating = item as IHasCriticRating;
|
if (!string.IsNullOrEmpty(text))
|
||||||
|
|
||||||
if (hasCriticRating != null && !string.IsNullOrEmpty(text))
|
|
||||||
{
|
{
|
||||||
float value;
|
float value;
|
||||||
if (float.TryParse(text, NumberStyles.Any, _usCulture, out value))
|
if (float.TryParse(text, NumberStyles.Any, _usCulture, out value))
|
||||||
{
|
{
|
||||||
hasCriticRating.CriticRating = value;
|
item.CriticRating = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,12 +290,7 @@ namespace MediaBrowser.Controller.Providers
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(val))
|
if (!string.IsNullOrWhiteSpace(val))
|
||||||
{
|
{
|
||||||
var hasShortOverview = item as IHasShortOverview;
|
item.ShortOverview = val;
|
||||||
|
|
||||||
if (hasShortOverview != null)
|
|
||||||
{
|
|
||||||
hasShortOverview.ShortOverview = val;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -309,12 +302,7 @@ namespace MediaBrowser.Controller.Providers
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(val))
|
if (!string.IsNullOrWhiteSpace(val))
|
||||||
{
|
{
|
||||||
var hasCriticRating = item as IHasCriticRating;
|
item.CriticRatingSummary = val;
|
||||||
|
|
||||||
if (hasCriticRating != null)
|
|
||||||
{
|
|
||||||
hasCriticRating.CriticRatingSummary = val;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -12,7 +12,6 @@ namespace MediaBrowser.Controller.Providers
|
||||||
public Stream Stream { get; set; }
|
public Stream Stream { get; set; }
|
||||||
public ImageFormat Format { get; set; }
|
public ImageFormat Format { get; set; }
|
||||||
public bool HasImage { get; set; }
|
public bool HasImage { get; set; }
|
||||||
public string InternalCacheKey { get; set; }
|
|
||||||
|
|
||||||
public void SetFormatFromMimeType(string mimeType)
|
public void SetFormatFromMimeType(string mimeType)
|
||||||
{
|
{
|
||||||
|
|
|
@ -69,28 +69,8 @@ namespace MediaBrowser.Controller.Providers
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves the image.
|
/// Saves the image.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item">The item.</param>
|
|
||||||
/// <param name="source">The source.</param>
|
|
||||||
/// <param name="mimeType">Type of the MIME.</param>
|
|
||||||
/// <param name="type">The type.</param>
|
|
||||||
/// <param name="imageIndex">Index of the image.</param>
|
|
||||||
/// <param name="internalCacheKey">The internal cache key.</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, string internalCacheKey, CancellationToken cancellationToken);
|
Task SaveImage(IHasImages item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Saves the image.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="item">The item.</param>
|
|
||||||
/// <param name="source">The source.</param>
|
|
||||||
/// <param name="mimeType">Type of the MIME.</param>
|
|
||||||
/// <param name="type">The type.</param>
|
|
||||||
/// <param name="imageIndex">Index of the image.</param>
|
|
||||||
/// <param name="internalCacheKey">The internal cache key.</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>Task.</returns>
|
|
||||||
Task SaveImage(IHasImages item, string source, string mimeType, ImageType type, int? imageIndex, string internalCacheKey, CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the metadata providers.
|
/// Adds the metadata providers.
|
||||||
|
|
|
@ -585,10 +585,9 @@ namespace MediaBrowser.Dlna.Didl
|
||||||
{
|
{
|
||||||
var desc = item.Overview;
|
var desc = item.Overview;
|
||||||
|
|
||||||
var hasShortOverview = item as IHasShortOverview;
|
if (!string.IsNullOrEmpty(item.ShortOverview))
|
||||||
if (hasShortOverview != null && !string.IsNullOrEmpty(hasShortOverview.ShortOverview))
|
|
||||||
{
|
{
|
||||||
desc = hasShortOverview.ShortOverview;
|
desc = item.ShortOverview;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(desc))
|
if (!string.IsNullOrWhiteSpace(desc))
|
||||||
|
@ -697,16 +696,36 @@ namespace MediaBrowser.Dlna.Didl
|
||||||
|
|
||||||
private void AddPeople(BaseItem item, XmlElement element)
|
private void AddPeople(BaseItem item, XmlElement element)
|
||||||
{
|
{
|
||||||
var types = new[] { PersonType.Director, PersonType.Writer, PersonType.Producer, PersonType.Composer, "Creator" };
|
var types = new[]
|
||||||
|
{
|
||||||
|
PersonType.Director,
|
||||||
|
PersonType.Writer,
|
||||||
|
PersonType.Producer,
|
||||||
|
PersonType.Composer,
|
||||||
|
"Creator"
|
||||||
|
};
|
||||||
|
|
||||||
var people = _libraryManager.GetPeople(item);
|
var people = _libraryManager.GetPeople(item);
|
||||||
|
|
||||||
|
var index = 0;
|
||||||
|
|
||||||
|
// Seeing some LG models locking up due content with large lists of people
|
||||||
|
// The actual issue might just be due to processing a more metadata than it can handle
|
||||||
|
var limit = 10;
|
||||||
|
|
||||||
foreach (var actor in people)
|
foreach (var actor in people)
|
||||||
{
|
{
|
||||||
var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
|
var type = types.FirstOrDefault(i => string.Equals(i, actor.Type, StringComparison.OrdinalIgnoreCase) || string.Equals(i, actor.Role, StringComparison.OrdinalIgnoreCase))
|
||||||
?? PersonType.Actor;
|
?? PersonType.Actor;
|
||||||
|
|
||||||
AddValue(element, "upnp", type.ToLower(), actor.Name, NS_UPNP);
|
AddValue(element, "upnp", type.ToLower(), actor.Name, NS_UPNP);
|
||||||
|
|
||||||
|
index++;
|
||||||
|
|
||||||
|
if (index >= limit)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Controller.MediaEncoding;
|
using MediaBrowser.Controller.MediaEncoding;
|
||||||
using Rssdp;
|
using Rssdp;
|
||||||
|
using Rssdp.Infrastructure;
|
||||||
|
|
||||||
namespace MediaBrowser.Dlna.Main
|
namespace MediaBrowser.Dlna.Main
|
||||||
{
|
{
|
||||||
|
@ -154,8 +155,14 @@ namespace MediaBrowser.Dlna.Main
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void LogMessage(string msg)
|
||||||
|
{
|
||||||
|
//_logger.Debug(msg);
|
||||||
|
}
|
||||||
|
|
||||||
private void StartPublishing()
|
private void StartPublishing()
|
||||||
{
|
{
|
||||||
|
SsdpDevicePublisherBase.LogFunction = LogMessage;
|
||||||
_Publisher = new SsdpDevicePublisher();
|
_Publisher = new SsdpDevicePublisher();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,48 +242,71 @@ namespace MediaBrowser.Dlna.Main
|
||||||
|
|
||||||
var addressString = address.ToString();
|
var addressString = address.ToString();
|
||||||
|
|
||||||
var udn = (addressString).GetMD5().ToString("N");
|
var udn = CreateUuid(addressString);
|
||||||
|
|
||||||
var services = new List<string>
|
var fullService = "urn:schemas-upnp-org:device:MediaServer:1";
|
||||||
|
|
||||||
|
_logger.Info("Registering publisher for {0} on {1}", fullService, addressString);
|
||||||
|
|
||||||
|
var descriptorUri = "/dlna/" + udn + "/description.xml";
|
||||||
|
var uri = new Uri(_appHost.GetLocalApiUrl(address) + descriptorUri);
|
||||||
|
|
||||||
|
var device = new SsdpRootDevice
|
||||||
|
{
|
||||||
|
CacheLifetime = TimeSpan.FromSeconds(cacheLength), //How long SSDP clients can cache this info.
|
||||||
|
Location = uri, // Must point to the URL that serves your devices UPnP description document.
|
||||||
|
FriendlyName = "Emby Server",
|
||||||
|
Manufacturer = "Emby",
|
||||||
|
ModelName = "Emby Server",
|
||||||
|
Uuid = udn
|
||||||
|
// This must be a globally unique value that survives reboots etc. Get from storage or embedded hardware etc.
|
||||||
|
};
|
||||||
|
|
||||||
|
SetProperies(device, fullService);
|
||||||
|
_Publisher.AddDevice(device);
|
||||||
|
|
||||||
|
var embeddedDevices = new List<string>
|
||||||
{
|
{
|
||||||
"urn:schemas-upnp-org:device:MediaServer:1",
|
|
||||||
"urn:schemas-upnp-org:service:ContentDirectory:1",
|
"urn:schemas-upnp-org:service:ContentDirectory:1",
|
||||||
"urn:schemas-upnp-org:service:ConnectionManager:1",
|
"urn:schemas-upnp-org:service:ConnectionManager:1",
|
||||||
"urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1"
|
"urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1"
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var fullService in services)
|
foreach (var subDevice in embeddedDevices)
|
||||||
{
|
{
|
||||||
_logger.Info("Registering publisher for {0} on {1}", fullService, addressString);
|
var embeddedDevice = new SsdpEmbeddedDevice
|
||||||
|
|
||||||
var descriptorURI = "/dlna/" + udn + "/description.xml";
|
|
||||||
var uri = new Uri(_appHost.GetLocalApiUrl(address) + descriptorURI);
|
|
||||||
|
|
||||||
var service = fullService.Replace("urn:", string.Empty).Replace(":1", string.Empty);
|
|
||||||
|
|
||||||
var serviceParts = service.Split(':');
|
|
||||||
|
|
||||||
var deviceTypeNamespace = serviceParts[0].Replace('.', '-');
|
|
||||||
|
|
||||||
var device = new SsdpRootDevice
|
|
||||||
{
|
{
|
||||||
CacheLifetime = TimeSpan.FromSeconds(cacheLength), //How long SSDP clients can cache this info.
|
FriendlyName = device.FriendlyName,
|
||||||
Location = uri, // Must point to the URL that serves your devices UPnP description document.
|
Manufacturer = device.Manufacturer,
|
||||||
DeviceTypeNamespace = deviceTypeNamespace,
|
ModelName = device.ModelName,
|
||||||
DeviceClass = serviceParts[1],
|
|
||||||
DeviceType = serviceParts[2],
|
|
||||||
FriendlyName = "Emby Server",
|
|
||||||
Manufacturer = "Emby",
|
|
||||||
ModelName = "Emby Server",
|
|
||||||
Uuid = udn
|
Uuid = udn
|
||||||
// This must be a globally unique value that survives reboots etc. Get from storage or embedded hardware etc.
|
// This must be a globally unique value that survives reboots etc. Get from storage or embedded hardware etc.
|
||||||
};
|
};
|
||||||
|
|
||||||
_Publisher.AddDevice(device);
|
SetProperies(embeddedDevice, subDevice);
|
||||||
|
device.AddDevice(embeddedDevice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string CreateUuid(string text)
|
||||||
|
{
|
||||||
|
return text.GetMD5().ToString("N");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetProperies(SsdpDevice device, string fullDeviceType)
|
||||||
|
{
|
||||||
|
var service = fullDeviceType.Replace("urn:", string.Empty).Replace(":1", string.Empty);
|
||||||
|
|
||||||
|
var serviceParts = service.Split(':');
|
||||||
|
|
||||||
|
var deviceTypeNamespace = serviceParts[0].Replace('.', '-');
|
||||||
|
|
||||||
|
device.DeviceTypeNamespace = deviceTypeNamespace;
|
||||||
|
device.DeviceClass = serviceParts[1];
|
||||||
|
device.DeviceType = serviceParts[2];
|
||||||
|
}
|
||||||
|
|
||||||
private readonly object _syncLock = new object();
|
private readonly object _syncLock = new object();
|
||||||
private void StartPlayToManager()
|
private void StartPlayToManager()
|
||||||
{
|
{
|
||||||
|
|
|
@ -108,7 +108,6 @@
|
||||||
<Compile Include="Profiles\SonyBravia2014Profile.cs" />
|
<Compile Include="Profiles\SonyBravia2014Profile.cs" />
|
||||||
<Compile Include="Profiles\SonyPs4Profile.cs" />
|
<Compile Include="Profiles\SonyPs4Profile.cs" />
|
||||||
<Compile Include="Profiles\VlcProfile.cs" />
|
<Compile Include="Profiles\VlcProfile.cs" />
|
||||||
<Compile Include="Ssdp\DeviceDiscoveryInfo.cs" />
|
|
||||||
<Compile Include="Ssdp\Extensions.cs" />
|
<Compile Include="Ssdp\Extensions.cs" />
|
||||||
<Compile Include="PlayTo\PlaybackProgressEventArgs.cs" />
|
<Compile Include="PlayTo\PlaybackProgressEventArgs.cs" />
|
||||||
<Compile Include="PlayTo\PlaybackStoppedEventArgs.cs" />
|
<Compile Include="PlayTo\PlaybackStoppedEventArgs.cs" />
|
||||||
|
@ -130,7 +129,6 @@
|
||||||
<Compile Include="Service\BaseService.cs" />
|
<Compile Include="Service\BaseService.cs" />
|
||||||
<Compile Include="Service\ControlErrorHandler.cs" />
|
<Compile Include="Service\ControlErrorHandler.cs" />
|
||||||
<Compile Include="Service\ServiceXmlBuilder.cs" />
|
<Compile Include="Service\ServiceXmlBuilder.cs" />
|
||||||
<Compile Include="Ssdp\Datagram.cs" />
|
|
||||||
<Compile Include="Server\DescriptionXmlBuilder.cs" />
|
<Compile Include="Server\DescriptionXmlBuilder.cs" />
|
||||||
<Compile Include="Ssdp\DeviceDiscovery.cs" />
|
<Compile Include="Ssdp\DeviceDiscovery.cs" />
|
||||||
<Compile Include="PlayTo\SsdpHttpClient.cs" />
|
<Compile Include="PlayTo\SsdpHttpClient.cs" />
|
||||||
|
@ -160,8 +158,6 @@
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Server\Headers.cs" />
|
<Compile Include="Server\Headers.cs" />
|
||||||
<Compile Include="Server\UpnpDevice.cs" />
|
<Compile Include="Server\UpnpDevice.cs" />
|
||||||
<Compile Include="Ssdp\SsdpMessageBuilder.cs" />
|
|
||||||
<Compile Include="Ssdp\SsdpHandler.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
|
<ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj">
|
||||||
|
|
|
@ -1,126 +0,0 @@
|
||||||
using MediaBrowser.Model.Logging;
|
|
||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Dlna.Ssdp
|
|
||||||
{
|
|
||||||
public class Datagram
|
|
||||||
{
|
|
||||||
public EndPoint ToEndPoint { get; private set; }
|
|
||||||
public EndPoint FromEndPoint { get; private set; }
|
|
||||||
public string Message { get; private set; }
|
|
||||||
public bool IsBroadcast { get; private set; }
|
|
||||||
public bool EnableDebugLogging { get; private set; }
|
|
||||||
|
|
||||||
private readonly ILogger _logger;
|
|
||||||
|
|
||||||
public Datagram(EndPoint toEndPoint, EndPoint fromEndPoint, ILogger logger, string message, bool isBroadcast, bool enableDebugLogging)
|
|
||||||
{
|
|
||||||
Message = message;
|
|
||||||
_logger = logger;
|
|
||||||
EnableDebugLogging = enableDebugLogging;
|
|
||||||
IsBroadcast = isBroadcast;
|
|
||||||
FromEndPoint = fromEndPoint;
|
|
||||||
ToEndPoint = toEndPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Send()
|
|
||||||
{
|
|
||||||
var msg = Encoding.ASCII.GetBytes(Message);
|
|
||||||
|
|
||||||
var socket = CreateSocket();
|
|
||||||
|
|
||||||
if (socket == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FromEndPoint != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
socket.Bind(FromEndPoint);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (EnableDebugLogging)
|
|
||||||
{
|
|
||||||
_logger.ErrorException("Error binding datagram socket", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsBroadcast)
|
|
||||||
{
|
|
||||||
CloseSocket(socket, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
socket.BeginSendTo(msg, 0, msg.Length, SocketFlags.None, ToEndPoint, result =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
socket.EndSend(result);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (EnableDebugLogging)
|
|
||||||
{
|
|
||||||
_logger.ErrorException("Error sending Datagram to {0} from {1}: " + Message, ex, ToEndPoint, FromEndPoint == null ? "" : FromEndPoint.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
CloseSocket(socket, true);
|
|
||||||
}
|
|
||||||
}, null);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.ErrorException("Error sending Datagram to {0} from {1}: " + Message, ex, ToEndPoint, FromEndPoint == null ? "" : FromEndPoint.ToString());
|
|
||||||
CloseSocket(socket, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CloseSocket(Socket socket, bool logError)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
socket.Close();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (logError && EnableDebugLogging)
|
|
||||||
{
|
|
||||||
_logger.ErrorException("Error closing datagram socket", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Socket CreateSocket()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
|
||||||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
|
||||||
socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4);
|
|
||||||
|
|
||||||
if (IsBroadcast)
|
|
||||||
{
|
|
||||||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return socket;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.ErrorException("Error creating socket", ex);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
using MediaBrowser.Dlna.PlayTo;
|
|
||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Dlna.Ssdp
|
|
||||||
{
|
|
||||||
public class DeviceDiscoveryInfo
|
|
||||||
{
|
|
||||||
public Device Device { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The server's ip address that the device responded to
|
|
||||||
/// </summary>
|
|
||||||
public IPAddress LocalIpAddress { get; set; }
|
|
||||||
|
|
||||||
public Uri Uri { get; set; }
|
|
||||||
|
|
||||||
public string Usn { get; set; }
|
|
||||||
public string Nt { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,30 +9,6 @@ namespace MediaBrowser.Dlna.Ssdp
|
||||||
{
|
{
|
||||||
public static class Extensions
|
public static class Extensions
|
||||||
{
|
{
|
||||||
public static Task<int> ReceiveAsync(this Socket socket, byte[] buffer, int offset, int size)
|
|
||||||
{
|
|
||||||
var tcs = new TaskCompletionSource<int>(socket);
|
|
||||||
var remoteip = new IPEndPoint(IPAddress.Any, 0);
|
|
||||||
var endpoint = (EndPoint)remoteip;
|
|
||||||
|
|
||||||
socket.BeginReceiveFrom(buffer, offset, size, SocketFlags.None, ref endpoint, iar =>
|
|
||||||
{
|
|
||||||
var result = (TaskCompletionSource<int>)iar.AsyncState;
|
|
||||||
var iarSocket = (Socket)result.Task.AsyncState;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
result.TrySetResult(iarSocket.EndReceive(iar));
|
|
||||||
}
|
|
||||||
catch (Exception exc)
|
|
||||||
{
|
|
||||||
result.TrySetException(exc);
|
|
||||||
}
|
|
||||||
}, tcs);
|
|
||||||
|
|
||||||
return tcs.Task;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetValue(this XElement container, XName name)
|
public static string GetValue(this XElement container, XName name)
|
||||||
{
|
{
|
||||||
var node = container.Element(name);
|
var node = container.Element(name);
|
||||||
|
|
|
@ -1,328 +0,0 @@
|
||||||
using MediaBrowser.Common;
|
|
||||||
using MediaBrowser.Common.Configuration;
|
|
||||||
using MediaBrowser.Common.Events;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
|
||||||
using MediaBrowser.Controller.Dlna;
|
|
||||||
using MediaBrowser.Dlna.Server;
|
|
||||||
using MediaBrowser.Model.Logging;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.Win32;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Dlna.Ssdp
|
|
||||||
{
|
|
||||||
public class SsdpHandler : IDisposable, ISsdpHandler
|
|
||||||
{
|
|
||||||
private Socket _multicastSocket;
|
|
||||||
|
|
||||||
private readonly ILogger _logger;
|
|
||||||
private readonly IServerConfigurationManager _config;
|
|
||||||
|
|
||||||
const string SSDPAddr = "239.255.255.250";
|
|
||||||
const int SSDPPort = 1900;
|
|
||||||
private readonly string _serverSignature;
|
|
||||||
|
|
||||||
private readonly IPAddress _ssdpIp = IPAddress.Parse(SSDPAddr);
|
|
||||||
private readonly IPEndPoint _ssdpEndp = new IPEndPoint(IPAddress.Parse(SSDPAddr), SSDPPort);
|
|
||||||
|
|
||||||
private Timer _notificationTimer;
|
|
||||||
|
|
||||||
private bool _isDisposed;
|
|
||||||
private readonly Dictionary<string, List<UpnpDevice>> _devices = new Dictionary<string, List<UpnpDevice>>();
|
|
||||||
|
|
||||||
private readonly IApplicationHost _appHost;
|
|
||||||
|
|
||||||
private readonly int _unicastPort = 1901;
|
|
||||||
private UdpClient _unicastClient;
|
|
||||||
|
|
||||||
public SsdpHandler(ILogger logger, IServerConfigurationManager config, IApplicationHost appHost)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_config = config;
|
|
||||||
_appHost = appHost;
|
|
||||||
|
|
||||||
_config.NamedConfigurationUpdated += _config_ConfigurationUpdated;
|
|
||||||
_serverSignature = GenerateServerSignature();
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GenerateServerSignature()
|
|
||||||
{
|
|
||||||
var os = Environment.OSVersion;
|
|
||||||
var pstring = os.Platform.ToString();
|
|
||||||
switch (os.Platform)
|
|
||||||
{
|
|
||||||
case PlatformID.Win32NT:
|
|
||||||
case PlatformID.Win32S:
|
|
||||||
case PlatformID.Win32Windows:
|
|
||||||
pstring = "WIN";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return String.Format(
|
|
||||||
"{0}{1}/{2}.{3} UPnP/1.0 DLNADOC/1.5 Emby/{4}",
|
|
||||||
pstring,
|
|
||||||
IntPtr.Size * 8,
|
|
||||||
os.Version.Major,
|
|
||||||
os.Version.Minor,
|
|
||||||
_appHost.ApplicationVersion
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _config_ConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
|
|
||||||
{
|
|
||||||
if (string.Equals(e.Key, "dlna", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
ReloadAliveNotifier();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<UpnpDevice> RegisteredDevices
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
lock (_devices)
|
|
||||||
{
|
|
||||||
var devices = _devices.ToList();
|
|
||||||
|
|
||||||
return devices.SelectMany(i => i.Value).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Start()
|
|
||||||
{
|
|
||||||
DisposeSocket();
|
|
||||||
StopAliveNotifier();
|
|
||||||
|
|
||||||
RestartSocketListener();
|
|
||||||
ReloadAliveNotifier();
|
|
||||||
|
|
||||||
SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
|
|
||||||
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.Mode == PowerModes.Resume)
|
|
||||||
{
|
|
||||||
Start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async void SendDatagram(string msg,
|
|
||||||
EndPoint endpoint,
|
|
||||||
EndPoint localAddress,
|
|
||||||
bool isBroadcast,
|
|
||||||
int sendCount = 3)
|
|
||||||
{
|
|
||||||
var enableDebugLogging = _config.GetDlnaConfiguration().EnableDebugLog;
|
|
||||||
|
|
||||||
for (var i = 0; i < sendCount; i++)
|
|
||||||
{
|
|
||||||
if (i > 0)
|
|
||||||
{
|
|
||||||
await Task.Delay(500).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
var dgram = new Datagram(endpoint, localAddress, _logger, msg, isBroadcast, enableDebugLogging);
|
|
||||||
dgram.Send();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RestartSocketListener()
|
|
||||||
{
|
|
||||||
if (_isDisposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_multicastSocket = CreateMulticastSocket();
|
|
||||||
|
|
||||||
_logger.Info("MultiCast socket created");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.ErrorException("Error creating MultiCast socket", ex);
|
|
||||||
//StartSocketRetryTimer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_config.NamedConfigurationUpdated -= _config_ConfigurationUpdated;
|
|
||||||
SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
|
|
||||||
|
|
||||||
_isDisposed = true;
|
|
||||||
|
|
||||||
DisposeSocket();
|
|
||||||
StopAliveNotifier();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DisposeSocket()
|
|
||||||
{
|
|
||||||
if (_multicastSocket != null)
|
|
||||||
{
|
|
||||||
_multicastSocket.Close();
|
|
||||||
_multicastSocket.Dispose();
|
|
||||||
_multicastSocket = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Socket CreateMulticastSocket()
|
|
||||||
{
|
|
||||||
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
|
||||||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
|
|
||||||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
|
||||||
socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 4);
|
|
||||||
socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(_ssdpIp, 0));
|
|
||||||
|
|
||||||
socket.Bind(new IPEndPoint(IPAddress.Any, SSDPPort));
|
|
||||||
|
|
||||||
return socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void NotifyAll()
|
|
||||||
{
|
|
||||||
var enableDebugLogging = _config.GetDlnaConfiguration().EnableDebugLog;
|
|
||||||
|
|
||||||
if (enableDebugLogging)
|
|
||||||
{
|
|
||||||
_logger.Debug("Sending alive notifications");
|
|
||||||
}
|
|
||||||
foreach (var d in RegisteredDevices)
|
|
||||||
{
|
|
||||||
NotifyDevice(d, "alive", enableDebugLogging);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void NotifyDevice(UpnpDevice dev, string type, bool logMessage)
|
|
||||||
{
|
|
||||||
const string header = "NOTIFY * HTTP/1.1";
|
|
||||||
|
|
||||||
var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
// If needed later for non-server devices, these headers will need to be dynamic
|
|
||||||
values["HOST"] = "239.255.255.250:1900";
|
|
||||||
values["CACHE-CONTROL"] = "max-age = 600";
|
|
||||||
values["LOCATION"] = dev.Descriptor.ToString();
|
|
||||||
values["SERVER"] = _serverSignature;
|
|
||||||
values["NTS"] = "ssdp:" + type;
|
|
||||||
values["NT"] = dev.Type;
|
|
||||||
values["USN"] = dev.USN;
|
|
||||||
|
|
||||||
if (logMessage)
|
|
||||||
{
|
|
||||||
_logger.Debug("{0} said {1}", dev.USN, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
var msg = new SsdpMessageBuilder().BuildMessage(header, values);
|
|
||||||
|
|
||||||
SendDatagram(msg, _ssdpEndp, new IPEndPoint(dev.Address, 0), true, 2);
|
|
||||||
//SendUnicastRequest(msg, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RegisterNotification(string uuid, Uri descriptionUri, IPAddress address, IEnumerable<string> services)
|
|
||||||
{
|
|
||||||
lock (_devices)
|
|
||||||
{
|
|
||||||
List<UpnpDevice> list;
|
|
||||||
List<UpnpDevice> dl;
|
|
||||||
if (_devices.TryGetValue(uuid, out dl))
|
|
||||||
{
|
|
||||||
list = dl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
list = new List<UpnpDevice>();
|
|
||||||
_devices[uuid] = list;
|
|
||||||
}
|
|
||||||
|
|
||||||
list.AddRange(services.Select(i => new UpnpDevice(uuid, i, descriptionUri, address)));
|
|
||||||
|
|
||||||
NotifyAll();
|
|
||||||
_logger.Debug("Registered mount {0} at {1}", uuid, descriptionUri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UnregisterNotification(string uuid)
|
|
||||||
{
|
|
||||||
lock (_devices)
|
|
||||||
{
|
|
||||||
List<UpnpDevice> dl;
|
|
||||||
if (_devices.TryGetValue(uuid, out dl))
|
|
||||||
{
|
|
||||||
_devices.Remove(uuid);
|
|
||||||
foreach (var d in dl.ToList())
|
|
||||||
{
|
|
||||||
NotifyDevice(d, "byebye", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Debug("Unregistered mount {0}", uuid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly object _notificationTimerSyncLock = new object();
|
|
||||||
private int _aliveNotifierIntervalMs;
|
|
||||||
private void ReloadAliveNotifier()
|
|
||||||
{
|
|
||||||
var config = _config.GetDlnaConfiguration();
|
|
||||||
|
|
||||||
if (!config.BlastAliveMessages)
|
|
||||||
{
|
|
||||||
StopAliveNotifier();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var intervalMs = config.BlastAliveMessageIntervalSeconds * 1000;
|
|
||||||
|
|
||||||
if (_notificationTimer == null || _aliveNotifierIntervalMs != intervalMs)
|
|
||||||
{
|
|
||||||
lock (_notificationTimerSyncLock)
|
|
||||||
{
|
|
||||||
if (_notificationTimer == null)
|
|
||||||
{
|
|
||||||
_logger.Debug("Starting alive notifier");
|
|
||||||
const int initialDelayMs = 3000;
|
|
||||||
_notificationTimer = new Timer(state => NotifyAll(), null, initialDelayMs, intervalMs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.Debug("Updating alive notifier");
|
|
||||||
_notificationTimer.Change(intervalMs, intervalMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
_aliveNotifierIntervalMs = intervalMs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StopAliveNotifier()
|
|
||||||
{
|
|
||||||
lock (_notificationTimerSyncLock)
|
|
||||||
{
|
|
||||||
if (_notificationTimer != null)
|
|
||||||
{
|
|
||||||
_logger.Debug("Stopping alive notifier");
|
|
||||||
_notificationTimer.Dispose();
|
|
||||||
_notificationTimer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class UdpState
|
|
||||||
{
|
|
||||||
public UdpClient UdpClient;
|
|
||||||
public IPEndPoint EndPoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Dlna.Ssdp
|
|
||||||
{
|
|
||||||
public class SsdpMessageBuilder
|
|
||||||
{
|
|
||||||
public string BuildMessage(string header, Dictionary<string, string> values)
|
|
||||||
{
|
|
||||||
var builder = new StringBuilder();
|
|
||||||
|
|
||||||
const string argFormat = "{0}: {1}\r\n";
|
|
||||||
|
|
||||||
builder.AppendFormat("{0}\r\n", header);
|
|
||||||
|
|
||||||
foreach (var pair in values)
|
|
||||||
{
|
|
||||||
builder.AppendFormat(argFormat, pair.Key, pair.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.Append("\r\n");
|
|
||||||
|
|
||||||
return builder.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -258,18 +258,14 @@ namespace MediaBrowser.LocalMetadata.Savers
|
||||||
builder.Append("<Type>" + SecurityElement.Escape(item.DisplayMediaType) + "</Type>");
|
builder.Append("<Type>" + SecurityElement.Escape(item.DisplayMediaType) + "</Type>");
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasCriticRating = item as IHasCriticRating;
|
if (item.CriticRating.HasValue)
|
||||||
if (hasCriticRating != null)
|
|
||||||
{
|
{
|
||||||
if (hasCriticRating.CriticRating.HasValue)
|
builder.Append("<CriticRating>" + SecurityElement.Escape(item.CriticRating.Value.ToString(UsCulture)) + "</CriticRating>");
|
||||||
{
|
}
|
||||||
builder.Append("<CriticRating>" + SecurityElement.Escape(hasCriticRating.CriticRating.Value.ToString(UsCulture)) + "</CriticRating>");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(hasCriticRating.CriticRatingSummary))
|
if (!string.IsNullOrEmpty(item.CriticRatingSummary))
|
||||||
{
|
{
|
||||||
builder.Append("<CriticRatingSummary><![CDATA[" + hasCriticRating.CriticRatingSummary + "]]></CriticRatingSummary>");
|
builder.Append("<CriticRatingSummary><![CDATA[" + item.CriticRatingSummary + "]]></CriticRatingSummary>");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(item.Overview))
|
if (!string.IsNullOrEmpty(item.Overview))
|
||||||
|
@ -285,14 +281,10 @@ namespace MediaBrowser.LocalMetadata.Savers
|
||||||
builder.Append("<OriginalTitle>" + SecurityElement.Escape(hasOriginalTitle.OriginalTitle) + "</OriginalTitle>");
|
builder.Append("<OriginalTitle>" + SecurityElement.Escape(hasOriginalTitle.OriginalTitle) + "</OriginalTitle>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasShortOverview = item as IHasShortOverview;
|
if (!string.IsNullOrEmpty(item.ShortOverview))
|
||||||
if (hasShortOverview != null)
|
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(hasShortOverview.ShortOverview))
|
builder.Append("<ShortOverview><![CDATA[" + item.ShortOverview + "]]></ShortOverview>");
|
||||||
{
|
|
||||||
builder.Append("<ShortOverview><![CDATA[" + hasShortOverview.ShortOverview + "]]></ShortOverview>");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(item.CustomRating))
|
if (!string.IsNullOrEmpty(item.CustomRating))
|
||||||
|
|
|
@ -588,13 +588,32 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
}
|
}
|
||||||
if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(options.VaapiDevice))
|
if (string.Equals(hwType, "vaapi", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(options.VaapiDevice))
|
||||||
{
|
{
|
||||||
return GetAvailableEncoder(mediaEncoder, "h264_vaapi", defaultEncoder);
|
if (IsVaapiSupported(state))
|
||||||
|
{
|
||||||
|
return GetAvailableEncoder(mediaEncoder, "h264_vaapi", defaultEncoder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultEncoder;
|
return defaultEncoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool IsVaapiSupported(EncodingJob state)
|
||||||
|
{
|
||||||
|
var videoStream = state.VideoStream;
|
||||||
|
|
||||||
|
if (videoStream != null)
|
||||||
|
{
|
||||||
|
// vaapi will throw an error with this input
|
||||||
|
// [vaapi @ 0x7faed8000960] No VAAPI support for codec mpeg4 profile -99.
|
||||||
|
if (string.Equals(videoStream.Codec, "mpeg4", StringComparison.OrdinalIgnoreCase) && videoStream.Level == -99)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
internal static bool CanStreamCopyVideo(EncodingJobOptions request, MediaStream videoStream)
|
internal static bool CanStreamCopyVideo(EncodingJobOptions request, MediaStream videoStream)
|
||||||
{
|
{
|
||||||
if (videoStream.IsInterlaced)
|
if (videoStream.IsInterlaced)
|
||||||
|
|
|
@ -31,6 +31,7 @@ namespace MediaBrowser.Model.Dto
|
||||||
public bool RequiresOpening { get; set; }
|
public bool RequiresOpening { get; set; }
|
||||||
public string OpenToken { get; set; }
|
public string OpenToken { get; set; }
|
||||||
public bool RequiresClosing { get; set; }
|
public bool RequiresClosing { get; set; }
|
||||||
|
public bool SupportsProbing { get; set; }
|
||||||
public string LiveStreamId { get; set; }
|
public string LiveStreamId { get; set; }
|
||||||
public int? BufferMs { get; set; }
|
public int? BufferMs { get; set; }
|
||||||
|
|
||||||
|
@ -63,6 +64,7 @@ namespace MediaBrowser.Model.Dto
|
||||||
SupportsTranscoding = true;
|
SupportsTranscoding = true;
|
||||||
SupportsDirectStream = true;
|
SupportsDirectStream = true;
|
||||||
SupportsDirectPlay = true;
|
SupportsDirectPlay = true;
|
||||||
|
SupportsProbing = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int? DefaultAudioStreamIndex { get; set; }
|
public int? DefaultAudioStreamIndex { get; set; }
|
||||||
|
|
|
@ -16,6 +16,7 @@ namespace MediaBrowser.Model.LiveTv
|
||||||
public string RecordingEncodingFormat { get; set; }
|
public string RecordingEncodingFormat { get; set; }
|
||||||
public bool EnableRecordingSubfolders { get; set; }
|
public bool EnableRecordingSubfolders { get; set; }
|
||||||
public bool EnableOriginalAudioWithEncodedRecordings { get; set; }
|
public bool EnableOriginalAudioWithEncodedRecordings { get; set; }
|
||||||
|
public bool EnableOriginalVideoWithEncodedRecordings { get; set; }
|
||||||
|
|
||||||
public List<TunerHostInfo> TunerHosts { get; set; }
|
public List<TunerHostInfo> TunerHosts { get; set; }
|
||||||
public List<ListingsProviderInfo> ListingProviders { get; set; }
|
public List<ListingsProviderInfo> ListingProviders { get; set; }
|
||||||
|
|
|
@ -42,6 +42,7 @@ namespace MediaBrowser.Model.LiveTv
|
||||||
/// <value>The user identifier.</value>
|
/// <value>The user identifier.</value>
|
||||||
public string UserId { get; set; }
|
public string UserId { get; set; }
|
||||||
public string SeriesTimerId { get; set; }
|
public string SeriesTimerId { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The earliest date for which a program starts to return
|
/// The earliest date for which a program starts to return
|
||||||
|
|
|
@ -11,6 +11,7 @@ namespace MediaBrowser.Model.MediaInfo
|
||||||
public long? StartTimeTicks { get; set; }
|
public long? StartTimeTicks { get; set; }
|
||||||
public int? AudioStreamIndex { get; set; }
|
public int? AudioStreamIndex { get; set; }
|
||||||
public int? SubtitleStreamIndex { get; set; }
|
public int? SubtitleStreamIndex { get; set; }
|
||||||
|
public int? MaxAudioChannels { get; set; }
|
||||||
public string ItemId { get; set; }
|
public string ItemId { get; set; }
|
||||||
public DeviceProfile DeviceProfile { get; set; }
|
public DeviceProfile DeviceProfile { get; set; }
|
||||||
|
|
||||||
|
@ -24,6 +25,7 @@ namespace MediaBrowser.Model.MediaInfo
|
||||||
MaxStreamingBitrate = options.MaxBitrate;
|
MaxStreamingBitrate = options.MaxBitrate;
|
||||||
ItemId = options.ItemId;
|
ItemId = options.ItemId;
|
||||||
DeviceProfile = options.Profile;
|
DeviceProfile = options.Profile;
|
||||||
|
MaxAudioChannels = options.MaxAudioChannels;
|
||||||
|
|
||||||
VideoOptions videoOptions = options as VideoOptions;
|
VideoOptions videoOptions = options as VideoOptions;
|
||||||
if (videoOptions != null)
|
if (videoOptions != null)
|
||||||
|
|
|
@ -16,6 +16,8 @@ namespace MediaBrowser.Model.MediaInfo
|
||||||
|
|
||||||
public int? SubtitleStreamIndex { get; set; }
|
public int? SubtitleStreamIndex { get; set; }
|
||||||
|
|
||||||
|
public int? MaxAudioChannels { get; set; }
|
||||||
|
|
||||||
public string MediaSourceId { get; set; }
|
public string MediaSourceId { get; set; }
|
||||||
|
|
||||||
public string LiveStreamId { get; set; }
|
public string LiveStreamId { get; set; }
|
||||||
|
|
|
@ -100,6 +100,7 @@ namespace MediaBrowser.Model.Net
|
||||||
.ToDictionary(x => x.Key, x => x.First().Key, StringComparer.OrdinalIgnoreCase);
|
.ToDictionary(x => x.Key, x => x.First().Key, StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
dict["image/jpg"] = ".jpg";
|
dict["image/jpg"] = ".jpg";
|
||||||
|
dict["image/x-png"] = ".png";
|
||||||
|
|
||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ namespace MediaBrowser.Providers.Manager
|
||||||
return SaveImage(item, source, mimeType, type, imageIndex, null, cancellationToken);
|
return SaveImage(item, source, mimeType, type, imageIndex, null, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, string internalCacheKey, CancellationToken cancellationToken)
|
public async Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(mimeType))
|
if (string.IsNullOrEmpty(mimeType))
|
||||||
{
|
{
|
||||||
|
@ -109,9 +109,9 @@ namespace MediaBrowser.Providers.Manager
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrEmpty(internalCacheKey))
|
if (saveLocallyWithMedia.HasValue && !saveLocallyWithMedia.Value)
|
||||||
{
|
{
|
||||||
saveLocally = false;
|
saveLocally = saveLocallyWithMedia.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!imageIndex.HasValue && item.AllowsMultipleImages(type))
|
if (!imageIndex.HasValue && item.AllowsMultipleImages(type))
|
||||||
|
@ -356,6 +356,11 @@ namespace MediaBrowser.Providers.Manager
|
||||||
var season = item as Season;
|
var season = item as Season;
|
||||||
var extension = MimeTypes.ToExtension(mimeType);
|
var extension = MimeTypes.ToExtension(mimeType);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(extension))
|
||||||
|
{
|
||||||
|
throw new ArgumentException(string.Format("Unable to determine image file extension from mime type {0}", mimeType));
|
||||||
|
}
|
||||||
|
|
||||||
if (type == ImageType.Thumb && saveLocally)
|
if (type == ImageType.Thumb && saveLocally)
|
||||||
{
|
{
|
||||||
if (season != null && season.IndexNumber.HasValue)
|
if (season != null && season.IndexNumber.HasValue)
|
||||||
|
|
|
@ -156,14 +156,14 @@ namespace MediaBrowser.Providers.Manager
|
||||||
|
|
||||||
var stream = _fileSystem.GetFileStream(response.Path, FileMode.Open, FileAccess.Read, FileShare.Read, true);
|
var stream = _fileSystem.GetFileStream(response.Path, FileMode.Open, FileAccess.Read, FileShare.Read, true);
|
||||||
|
|
||||||
await _providerManager.SaveImage(item, stream, mimeType, imageType, null, response.InternalCacheKey, cancellationToken).ConfigureAwait(false);
|
await _providerManager.SaveImage(item, stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var mimeType = "image/" + response.Format.ToString().ToLower();
|
var mimeType = "image/" + response.Format.ToString().ToLower();
|
||||||
|
|
||||||
await _providerManager.SaveImage(item, response.Stream, mimeType, imageType, null, response.InternalCacheKey, cancellationToken).ConfigureAwait(false);
|
await _providerManager.SaveImage(item, response.Stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadedImages.Add(imageType);
|
downloadedImages.Add(imageType);
|
||||||
|
|
|
@ -353,19 +353,16 @@ namespace MediaBrowser.Providers.Manager
|
||||||
{
|
{
|
||||||
var updateType = ItemUpdateType.None;
|
var updateType = ItemUpdateType.None;
|
||||||
|
|
||||||
if (isFullRefresh || currentUpdateType > ItemUpdateType.None)
|
var folder = item as Folder;
|
||||||
|
if (folder != null && folder.SupportsDateLastMediaAdded)
|
||||||
{
|
{
|
||||||
var folder = item as Folder;
|
var items = folder.GetRecursiveChildren(i => !i.IsFolder).Select(i => i.DateCreated).ToList();
|
||||||
if (folder != null && folder.SupportsDateLastMediaAdded)
|
var date = items.Count == 0 ? (DateTime?)null : items.Max();
|
||||||
{
|
|
||||||
var items = folder.GetRecursiveChildren(i => !i.IsFolder).Select(i => i.DateCreated).ToList();
|
|
||||||
var date = items.Count == 0 ? (DateTime?)null : items.Max();
|
|
||||||
|
|
||||||
if ((!folder.DateLastMediaAdded.HasValue && date.HasValue) || folder.DateLastMediaAdded != date)
|
if ((!folder.DateLastMediaAdded.HasValue && date.HasValue) || folder.DateLastMediaAdded != date)
|
||||||
{
|
{
|
||||||
folder.DateLastMediaAdded = date;
|
folder.DateLastMediaAdded = date;
|
||||||
updateType = ItemUpdateType.MetadataEdit;
|
updateType = ItemUpdateType.MetadataImport;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -140,12 +140,7 @@ namespace MediaBrowser.Providers.Manager
|
||||||
return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger, _memoryStreamProvider).SaveImage(item, source, mimeType, type, imageIndex, cancellationToken);
|
return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger, _memoryStreamProvider).SaveImage(item, source, mimeType, type, imageIndex, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task SaveImage(IHasImages item, Stream source, string mimeType, ImageType type, int? imageIndex, string internalCacheKey, CancellationToken cancellationToken)
|
public Task SaveImage(IHasImages item, string source, string mimeType, ImageType type, int? imageIndex, bool? saveLocallyWithMedia, CancellationToken cancellationToken)
|
||||||
{
|
|
||||||
return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger, _memoryStreamProvider).SaveImage(item, source, mimeType, type, imageIndex, internalCacheKey, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task SaveImage(IHasImages item, string source, string mimeType, ImageType type, int? imageIndex, string internalCacheKey, CancellationToken cancellationToken)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(source))
|
if (string.IsNullOrWhiteSpace(source))
|
||||||
{
|
{
|
||||||
|
@ -154,7 +149,7 @@ namespace MediaBrowser.Providers.Manager
|
||||||
|
|
||||||
var fileStream = _fileSystem.GetFileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true);
|
var fileStream = _fileSystem.GetFileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true);
|
||||||
|
|
||||||
return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger, _memoryStreamProvider).SaveImage(item, fileStream, mimeType, type, imageIndex, internalCacheKey, cancellationToken);
|
return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger, _memoryStreamProvider).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(IHasImages item, RemoteImageQuery query, CancellationToken cancellationToken)
|
public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(IHasImages item, RemoteImageQuery query, CancellationToken cancellationToken)
|
||||||
|
|
|
@ -236,15 +236,9 @@ namespace MediaBrowser.Providers.Manager
|
||||||
|
|
||||||
private static void MergeShortOverview(BaseItem source, BaseItem target, List<MetadataFields> lockedFields, bool replaceData)
|
private static void MergeShortOverview(BaseItem source, BaseItem target, List<MetadataFields> lockedFields, bool replaceData)
|
||||||
{
|
{
|
||||||
var sourceHasShortOverview = source as IHasShortOverview;
|
if (replaceData || string.IsNullOrEmpty(target.ShortOverview))
|
||||||
var targetHasShortOverview = target as IHasShortOverview;
|
|
||||||
|
|
||||||
if (sourceHasShortOverview != null && targetHasShortOverview != null)
|
|
||||||
{
|
{
|
||||||
if (replaceData || string.IsNullOrEmpty(targetHasShortOverview.ShortOverview))
|
target.ShortOverview = source.ShortOverview;
|
||||||
{
|
|
||||||
targetHasShortOverview.ShortOverview = sourceHasShortOverview.ShortOverview;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,20 +305,14 @@ namespace MediaBrowser.Providers.Manager
|
||||||
|
|
||||||
private static void MergeCriticRating(BaseItem source, BaseItem target, List<MetadataFields> lockedFields, bool replaceData)
|
private static void MergeCriticRating(BaseItem source, BaseItem target, List<MetadataFields> lockedFields, bool replaceData)
|
||||||
{
|
{
|
||||||
var sourceCast = source as IHasCriticRating;
|
if (replaceData || !target.CriticRating.HasValue)
|
||||||
var targetCast = target as IHasCriticRating;
|
|
||||||
|
|
||||||
if (sourceCast != null && targetCast != null)
|
|
||||||
{
|
{
|
||||||
if (replaceData || !targetCast.CriticRating.HasValue)
|
target.CriticRating = source.CriticRating;
|
||||||
{
|
}
|
||||||
targetCast.CriticRating = sourceCast.CriticRating;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (replaceData || string.IsNullOrEmpty(targetCast.CriticRatingSummary))
|
if (replaceData || string.IsNullOrEmpty(target.CriticRatingSummary))
|
||||||
{
|
{
|
||||||
targetCast.CriticRatingSummary = sourceCast.CriticRatingSummary;
|
target.CriticRatingSummary = source.CriticRatingSummary;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,28 +66,24 @@ namespace MediaBrowser.Providers.Omdb
|
||||||
item.ProductionYear = year;
|
item.ProductionYear = year;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasCriticRating = item as IHasCriticRating;
|
// Seeing some bogus RT data on omdb for series, so filter it out here
|
||||||
if (hasCriticRating != null)
|
// RT doesn't even have tv series
|
||||||
{
|
int tomatoMeter;
|
||||||
// Seeing some bogus RT data on omdb for series, so filter it out here
|
|
||||||
// RT doesn't even have tv series
|
|
||||||
int tomatoMeter;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(result.tomatoMeter)
|
if (!string.IsNullOrEmpty(result.tomatoMeter)
|
||||||
&& int.TryParse(result.tomatoMeter, NumberStyles.Integer, _usCulture, out tomatoMeter)
|
&& int.TryParse(result.tomatoMeter, NumberStyles.Integer, _usCulture, out tomatoMeter)
|
||||||
&& tomatoMeter >= 0)
|
&& tomatoMeter >= 0)
|
||||||
{
|
{
|
||||||
hasCriticRating.CriticRating = tomatoMeter;
|
item.CriticRating = tomatoMeter;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(result.tomatoConsensus)
|
if (!string.IsNullOrEmpty(result.tomatoConsensus)
|
||||||
&& !string.Equals(result.tomatoConsensus, "No consensus yet.", StringComparison.OrdinalIgnoreCase))
|
&& !string.Equals(result.tomatoConsensus, "No consensus yet.", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
hasCriticRating.CriticRatingSummary = WebUtility.HtmlDecode(result.tomatoConsensus);
|
item.CriticRatingSummary = WebUtility.HtmlDecode(result.tomatoConsensus);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int voteCount;
|
int voteCount;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(result.imdbVotes)
|
if (!string.IsNullOrEmpty(result.imdbVotes)
|
||||||
&& int.TryParse(result.imdbVotes, NumberStyles.Number, _usCulture, out voteCount)
|
&& int.TryParse(result.imdbVotes, NumberStyles.Number, _usCulture, out voteCount)
|
||||||
|
@ -167,25 +163,21 @@ namespace MediaBrowser.Providers.Omdb
|
||||||
item.ProductionYear = year;
|
item.ProductionYear = year;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasCriticRating = item as IHasCriticRating;
|
// Seeing some bogus RT data on omdb for series, so filter it out here
|
||||||
if (hasCriticRating != null)
|
// RT doesn't even have tv series
|
||||||
|
int tomatoMeter;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(result.tomatoMeter)
|
||||||
|
&& int.TryParse(result.tomatoMeter, NumberStyles.Integer, _usCulture, out tomatoMeter)
|
||||||
|
&& tomatoMeter >= 0)
|
||||||
{
|
{
|
||||||
// Seeing some bogus RT data on omdb for series, so filter it out here
|
item.CriticRating = tomatoMeter;
|
||||||
// RT doesn't even have tv series
|
}
|
||||||
int tomatoMeter;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(result.tomatoMeter)
|
if (!string.IsNullOrEmpty(result.tomatoConsensus)
|
||||||
&& int.TryParse(result.tomatoMeter, NumberStyles.Integer, _usCulture, out tomatoMeter)
|
&& !string.Equals(result.tomatoConsensus, "No consensus yet.", StringComparison.OrdinalIgnoreCase))
|
||||||
&& tomatoMeter >= 0)
|
{
|
||||||
{
|
item.CriticRatingSummary = WebUtility.HtmlDecode(result.tomatoConsensus);
|
||||||
hasCriticRating.CriticRating = tomatoMeter;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(result.tomatoConsensus)
|
|
||||||
&& !string.Equals(result.tomatoConsensus, "No consensus yet.", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
hasCriticRating.CriticRatingSummary = WebUtility.HtmlDecode(result.tomatoConsensus);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int voteCount;
|
int voteCount;
|
||||||
|
@ -420,12 +412,8 @@ namespace MediaBrowser.Providers.Omdb
|
||||||
hasAwards.AwardSummary = WebUtility.HtmlDecode(result.Awards);
|
hasAwards.AwardSummary = WebUtility.HtmlDecode(result.Awards);
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasShortOverview = item as IHasShortOverview;
|
// Imdb plots are usually pretty short
|
||||||
if (hasShortOverview != null)
|
item.ShortOverview = result.Plot;
|
||||||
{
|
|
||||||
// Imdb plots are usually pretty short
|
|
||||||
hasShortOverview.ShortOverview = result.Plot;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if (!string.IsNullOrWhiteSpace(result.Director))
|
//if (!string.IsNullOrWhiteSpace(result.Director))
|
||||||
//{
|
//{
|
||||||
|
|
|
@ -45,6 +45,11 @@ namespace MediaBrowser.Server.Implementations.IO
|
||||||
|
|
||||||
private void AddAffectedPath(string path)
|
private void AddAffectedPath(string path)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(path))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("path");
|
||||||
|
}
|
||||||
|
|
||||||
if (!_affectedPaths.Contains(path, StringComparer.Ordinal))
|
if (!_affectedPaths.Contains(path, StringComparer.Ordinal))
|
||||||
{
|
{
|
||||||
_affectedPaths.Add(path);
|
_affectedPaths.Add(path);
|
||||||
|
@ -53,6 +58,11 @@ namespace MediaBrowser.Server.Implementations.IO
|
||||||
|
|
||||||
public void AddPath(string path)
|
public void AddPath(string path)
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(path))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("path");
|
||||||
|
}
|
||||||
|
|
||||||
lock (_timerLock)
|
lock (_timerLock)
|
||||||
{
|
{
|
||||||
AddAffectedPath(path);
|
AddAffectedPath(path);
|
||||||
|
|
|
@ -2838,9 +2838,13 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
|
|
||||||
private bool ValidateNetworkPath(string path)
|
private bool ValidateNetworkPath(string path)
|
||||||
{
|
{
|
||||||
if (Environment.OSVersion.Platform == PlatformID.Win32NT || !path.StartsWith("\\\\", StringComparison.OrdinalIgnoreCase))
|
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
|
||||||
{
|
{
|
||||||
return Directory.Exists(path);
|
// We can't validate protocol-based paths, so just allow them
|
||||||
|
if (path.IndexOf("://", StringComparison.OrdinalIgnoreCase) == -1)
|
||||||
|
{
|
||||||
|
return Directory.Exists(path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Without native support for unc, we cannot validate this when running under mono
|
// Without native support for unc, we cannot validate this when running under mono
|
||||||
|
|
|
@ -360,7 +360,6 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
|
|
||||||
public async Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, bool enableAutoClose, CancellationToken cancellationToken)
|
public async Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, bool enableAutoClose, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
enableAutoClose = false;
|
|
||||||
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|
|
@ -207,14 +207,18 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
||||||
// Find movies with their own folders
|
// Find movies with their own folders
|
||||||
if (args.IsDirectory)
|
if (args.IsDirectory)
|
||||||
{
|
{
|
||||||
|
var files = args.FileSystemChildren
|
||||||
|
.Where(i => !LibraryManager.IgnoreFile(i, args.Parent))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return null;
|
return FindMovie<MusicVideo>(args.Path, args.Parent, files, args.DirectoryService, collectionType, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return null;
|
return FindMovie<Video>(args.Path, args.Parent, files, args.DirectoryService, collectionType, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(collectionType))
|
if (string.IsNullOrEmpty(collectionType))
|
||||||
|
@ -222,6 +226,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
||||||
// Owned items will be caught by the plain video resolver
|
// Owned items will be caught by the plain video resolver
|
||||||
if (args.Parent == null)
|
if (args.Parent == null)
|
||||||
{
|
{
|
||||||
|
//return FindMovie<Video>(args.Path, args.Parent, files, args.DirectoryService, collectionType);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,21 +236,13 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
var files = args.FileSystemChildren
|
return FindMovie<Movie>(args.Path, args.Parent, files, args.DirectoryService, collectionType, true);
|
||||||
.Where(i => !LibraryManager.IgnoreFile(i, args.Parent))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
return FindMovie<Movie>(args.Path, args.Parent, files, args.DirectoryService, collectionType);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var files = args.FileSystemChildren
|
return FindMovie<Movie>(args.Path, args.Parent, files, args.DirectoryService, collectionType, true);
|
||||||
.Where(i => !LibraryManager.IgnoreFile(i, args.Parent))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
return FindMovie<Movie>(args.Path, args.Parent, files, args.DirectoryService, collectionType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -360,13 +357,8 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
||||||
/// Finds a movie based on a child file system entries
|
/// Finds a movie based on a child file system entries
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T"></typeparam>
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <param name="parent">The parent.</param>
|
|
||||||
/// <param name="fileSystemEntries">The file system entries.</param>
|
|
||||||
/// <param name="directoryService">The directory service.</param>
|
|
||||||
/// <param name="collectionType">Type of the collection.</param>
|
|
||||||
/// <returns>Movie.</returns>
|
/// <returns>Movie.</returns>
|
||||||
private T FindMovie<T>(string path, Folder parent, List<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, string collectionType)
|
private T FindMovie<T>(string path, Folder parent, List<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, string collectionType, bool allowFilesAsFolders)
|
||||||
where T : Video, new()
|
where T : Video, new()
|
||||||
{
|
{
|
||||||
var multiDiscFolders = new List<FileSystemMetadata>();
|
var multiDiscFolders = new List<FileSystemMetadata>();
|
||||||
|
@ -413,23 +405,27 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var supportsMultiVersion = !string.Equals(collectionType, CollectionType.HomeVideos) &&
|
if (allowFilesAsFolders)
|
||||||
!string.Equals(collectionType, CollectionType.Photos) &&
|
|
||||||
!string.Equals(collectionType, CollectionType.MusicVideos);
|
|
||||||
|
|
||||||
var result = ResolveVideos<T>(parent, fileSystemEntries, directoryService, supportsMultiVersion);
|
|
||||||
|
|
||||||
if (result.Items.Count == 1)
|
|
||||||
{
|
{
|
||||||
var movie = (T)result.Items[0];
|
// TODO: Allow GetMultiDiscMovie in here
|
||||||
movie.IsInMixedFolder = false;
|
var supportsMultiVersion = !string.Equals(collectionType, CollectionType.HomeVideos) &&
|
||||||
movie.Name = Path.GetFileName(movie.ContainingFolderPath);
|
!string.Equals(collectionType, CollectionType.Photos) &&
|
||||||
return movie;
|
!string.Equals(collectionType, CollectionType.MusicVideos);
|
||||||
}
|
|
||||||
|
|
||||||
if (result.Items.Count == 0 && multiDiscFolders.Count > 0)
|
var result = ResolveVideos<T>(parent, fileSystemEntries, directoryService, supportsMultiVersion);
|
||||||
{
|
|
||||||
return GetMultiDiscMovie<T>(multiDiscFolders, directoryService);
|
if (result.Items.Count == 1)
|
||||||
|
{
|
||||||
|
var movie = (T)result.Items[0];
|
||||||
|
movie.IsInMixedFolder = false;
|
||||||
|
movie.Name = Path.GetFileName(movie.ContainingFolderPath);
|
||||||
|
return movie;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.Items.Count == 0 && multiDiscFolders.Count > 0)
|
||||||
|
{
|
||||||
|
return GetMultiDiscMovie<T>(multiDiscFolders, directoryService);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -1021,7 +1021,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
{
|
{
|
||||||
var stream = new MediaSourceInfo
|
var stream = new MediaSourceInfo
|
||||||
{
|
{
|
||||||
Path = _appHost.GetLocalApiUrl("localhost") + "/LiveTv/LiveRecordings/" + recordingId + "/stream",
|
Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveRecordings/" + recordingId + "/stream",
|
||||||
Id = recordingId,
|
Id = recordingId,
|
||||||
SupportsDirectPlay = false,
|
SupportsDirectPlay = false,
|
||||||
SupportsDirectStream = true,
|
SupportsDirectStream = true,
|
||||||
|
@ -1854,23 +1854,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
ParentIndexNumber = program.SeasonNumber.Value,
|
ParentIndexNumber = program.SeasonNumber.Value,
|
||||||
IndexNumber = program.EpisodeNumber.Value,
|
IndexNumber = program.EpisodeNumber.Value,
|
||||||
AncestorIds = seriesIds,
|
AncestorIds = seriesIds,
|
||||||
ExcludeLocationTypes = new[] { LocationType.Virtual }
|
IsVirtualItem = false
|
||||||
});
|
|
||||||
|
|
||||||
if (result.TotalRecordCount > 0)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(program.EpisodeTitle))
|
|
||||||
{
|
|
||||||
var result = _libraryManager.GetItemsResult(new InternalItemsQuery
|
|
||||||
{
|
|
||||||
IncludeItemTypes = new[] { typeof(Episode).Name },
|
|
||||||
Name = program.EpisodeTitle,
|
|
||||||
AncestorIds = seriesIds,
|
|
||||||
ExcludeLocationTypes = new[] { LocationType.Virtual }
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.TotalRecordCount > 0)
|
if (result.TotalRecordCount > 0)
|
||||||
|
|
|
@ -52,7 +52,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
{
|
{
|
||||||
var format = _liveTvOptions.RecordingEncodingFormat;
|
var format = _liveTvOptions.RecordingEncodingFormat;
|
||||||
|
|
||||||
if (string.Equals(format, "mkv", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(format, "mkv", StringComparison.OrdinalIgnoreCase) || _liveTvOptions.EnableOriginalVideoWithEncodedRecordings)
|
||||||
{
|
{
|
||||||
return "mkv";
|
return "mkv";
|
||||||
}
|
}
|
||||||
|
@ -204,6 +204,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv.EmbyTV
|
||||||
|
|
||||||
private bool EncodeVideo(MediaSourceInfo mediaSource)
|
private bool EncodeVideo(MediaSourceInfo mediaSource)
|
||||||
{
|
{
|
||||||
|
if (_liveTvOptions.EnableOriginalAudioWithEncodedRecordings)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var mediaStreams = mediaSource.MediaStreams ?? new List<MediaStream>();
|
var mediaStreams = mediaSource.MediaStreams ?? new List<MediaStream>();
|
||||||
return !mediaStreams.Any(i => i.Type == MediaStreamType.Video && string.Equals(i.Codec, "h264", StringComparison.OrdinalIgnoreCase) && !i.IsInterlaced);
|
return !mediaStreams.Any(i => i.Type == MediaStreamType.Video && string.Equals(i.Codec, "h264", StringComparison.OrdinalIgnoreCase) && !i.IsInterlaced);
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,11 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||||
{
|
{
|
||||||
dto.ProgramInfo = _dtoService.GetBaseItemDto(program, new DtoOptions());
|
dto.ProgramInfo = _dtoService.GetBaseItemDto(program, new DtoOptions());
|
||||||
|
|
||||||
dto.ProgramInfo.TimerId = dto.Id;
|
if (info.Status != RecordingStatus.Cancelled && info.Status != RecordingStatus.Error)
|
||||||
|
{
|
||||||
|
dto.ProgramInfo.TimerId = dto.Id;
|
||||||
|
dto.ProgramInfo.Status = info.Status.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
dto.ProgramInfo.SeriesTimerId = dto.SeriesTimerId;
|
dto.ProgramInfo.SeriesTimerId = dto.SeriesTimerId;
|
||||||
}
|
}
|
||||||
|
|
|
@ -877,6 +877,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||||
SortOrder = query.SortOrder ?? SortOrder.Ascending,
|
SortOrder = query.SortOrder ?? SortOrder.Ascending,
|
||||||
EnableTotalRecordCount = query.EnableTotalRecordCount,
|
EnableTotalRecordCount = query.EnableTotalRecordCount,
|
||||||
TopParentIds = new[] { topFolder.Id.ToString("N") },
|
TopParentIds = new[] { topFolder.Id.ToString("N") },
|
||||||
|
Name = query.Name,
|
||||||
DtoOptions = options
|
DtoOptions = options
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1946,7 +1947,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
timers = timers.Where(i => !(i.Item1.Status == RecordingStatus.New));
|
timers = timers.Where(i => i.Item1.Status != RecordingStatus.New);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2304,7 +2305,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||||
|
|
||||||
var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false);
|
var info = await service.GetNewTimerDefaultsAsync(cancellationToken, programInfo).ConfigureAwait(false);
|
||||||
|
|
||||||
info.RecordAnyChannel = true;
|
|
||||||
info.RecordAnyTime = true;
|
info.RecordAnyTime = true;
|
||||||
info.Days = new List<DayOfWeek>
|
info.Days = new List<DayOfWeek>
|
||||||
{
|
{
|
||||||
|
|
|
@ -140,7 +140,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (stream.MediaStreams.Any(i => i.Index != -1))
|
if (!stream.SupportsProbing || stream.MediaStreams.Any(i => i.Index != -1))
|
||||||
{
|
{
|
||||||
await AddMediaInfo(stream, isAudio, cancellationToken).ConfigureAwait(false);
|
await AddMediaInfo(stream, isAudio, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
//OpenedMediaSource.Path = tempFile;
|
//OpenedMediaSource.Path = tempFile;
|
||||||
//OpenedMediaSource.ReadAtNativeFramerate = true;
|
//OpenedMediaSource.ReadAtNativeFramerate = true;
|
||||||
|
|
||||||
OpenedMediaSource.Path = _appHost.GetLocalApiUrl("localhost") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
|
OpenedMediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1") + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
|
||||||
OpenedMediaSource.Protocol = MediaProtocol.Http;
|
OpenedMediaSource.Protocol = MediaProtocol.Http;
|
||||||
OpenedMediaSource.SupportsDirectPlay = false;
|
OpenedMediaSource.SupportsDirectPlay = false;
|
||||||
OpenedMediaSource.SupportsDirectStream = true;
|
OpenedMediaSource.SupportsDirectStream = true;
|
||||||
|
|
|
@ -13,6 +13,7 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CommonIO;
|
using CommonIO;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.MediaEncoding;
|
using MediaBrowser.Controller.MediaEncoding;
|
||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
|
@ -24,12 +25,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
|
||||||
{
|
{
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
private readonly IHttpClient _httpClient;
|
private readonly IHttpClient _httpClient;
|
||||||
|
private readonly IServerApplicationHost _appHost;
|
||||||
|
|
||||||
public M3UTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient)
|
public M3UTunerHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost)
|
||||||
: base(config, logger, jsonSerializer, mediaEncoder)
|
: base(config, logger, jsonSerializer, mediaEncoder)
|
||||||
{
|
{
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
|
_appHost = appHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Type
|
public override string Type
|
||||||
|
@ -46,7 +49,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
|
||||||
|
|
||||||
protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
|
protected override async Task<IEnumerable<ChannelInfo>> GetChannelsInternal(TunerHostInfo info, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return await new M3uParser(Logger, _fileSystem, _httpClient).Parse(info.Url, ChannelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false);
|
return await new M3uParser(Logger, _fileSystem, _httpClient, _appHost).Parse(info.Url, ChannelIdPrefix, info.Id, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken)
|
public Task<List<LiveTvTunerInfo>> GetTunerInfos(CancellationToken cancellationToken)
|
||||||
|
@ -75,7 +78,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
|
||||||
|
|
||||||
public async Task Validate(TunerHostInfo info)
|
public async Task Validate(TunerHostInfo info)
|
||||||
{
|
{
|
||||||
using (var stream = await new M3uParser(Logger, _fileSystem, _httpClient).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
|
using (var stream = await new M3uParser(Logger, _fileSystem, _httpClient, _appHost).GetListingsStream(info.Url, CancellationToken.None).ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
||||||
using CommonIO;
|
using CommonIO;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.LiveTv;
|
using MediaBrowser.Controller.LiveTv;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
|
|
||||||
|
@ -18,12 +19,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
private readonly IHttpClient _httpClient;
|
private readonly IHttpClient _httpClient;
|
||||||
|
private readonly IServerApplicationHost _appHost;
|
||||||
|
|
||||||
public M3uParser(ILogger logger, IFileSystem fileSystem, IHttpClient httpClient)
|
public M3uParser(ILogger logger, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
|
_appHost = appHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<M3UChannel>> Parse(string url, string channelIdPrefix, string tunerHostId, CancellationToken cancellationToken)
|
public async Task<List<M3UChannel>> Parse(string url, string channelIdPrefix, string tunerHostId, CancellationToken cancellationToken)
|
||||||
|
@ -41,7 +44,13 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
|
||||||
{
|
{
|
||||||
if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return _httpClient.Get(url, cancellationToken);
|
return _httpClient.Get(new HttpRequestOptions
|
||||||
|
{
|
||||||
|
Url = url,
|
||||||
|
CancellationToken = cancellationToken,
|
||||||
|
// Some data providers will require a user agent
|
||||||
|
UserAgent = _appHost.FriendlyName + "/" + _appHost.ApplicationVersion
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return Task.FromResult(_fileSystem.OpenRead(url));
|
return Task.FromResult(_fileSystem.OpenRead(url));
|
||||||
}
|
}
|
||||||
|
@ -111,15 +120,31 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
|
||||||
channel.Number = "0";
|
channel.Number = "0";
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.ImageUrl = FindProperty("tvg-logo", extInf, null);
|
channel.ImageUrl = FindProperty("tvg-logo", extInf);
|
||||||
channel.Number = FindProperty("channel-id", extInf, channel.Number);
|
|
||||||
channel.Number = FindProperty("tvg-id", extInf, channel.Number);
|
var name = FindProperty("tvg-name", extInf);
|
||||||
channel.Name = FindProperty("tvg-id", extInf, channel.Name);
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
channel.Name = FindProperty("tvg-name", extInf, channel.Name);
|
{
|
||||||
|
name = FindProperty("tvg-id", extInf);
|
||||||
|
}
|
||||||
|
|
||||||
|
channel.Name = name;
|
||||||
|
|
||||||
|
var numberString = FindProperty("tvg-id", extInf);
|
||||||
|
if (string.IsNullOrWhiteSpace(numberString))
|
||||||
|
{
|
||||||
|
numberString = FindProperty("channel-id", extInf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(numberString))
|
||||||
|
{
|
||||||
|
channel.Number = numberString;
|
||||||
|
}
|
||||||
|
|
||||||
return channel;
|
return channel;
|
||||||
|
|
||||||
}
|
}
|
||||||
private string FindProperty(string property, string properties, string defaultResult = "")
|
private string FindProperty(string property, string properties)
|
||||||
{
|
{
|
||||||
var reg = new Regex(@"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase);
|
var reg = new Regex(@"([a-z0-9\-_]+)=\""([^""]+)\""", RegexOptions.IgnoreCase);
|
||||||
var matches = reg.Matches(properties);
|
var matches = reg.Matches(properties);
|
||||||
|
@ -130,7 +155,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts
|
||||||
return match.Groups[2].Value;
|
return match.Groups[2].Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return defaultResult;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ using CommonIO;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.LiveTv;
|
using MediaBrowser.Controller.LiveTv;
|
||||||
using MediaBrowser.Controller.MediaEncoding;
|
using MediaBrowser.Controller.MediaEncoding;
|
||||||
|
@ -25,12 +26,14 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
|
||||||
{
|
{
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
private readonly IHttpClient _httpClient;
|
private readonly IHttpClient _httpClient;
|
||||||
|
private readonly IServerApplicationHost _appHost;
|
||||||
|
|
||||||
public SatIpHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient)
|
public SatIpHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IFileSystem fileSystem, IHttpClient httpClient, IServerApplicationHost appHost)
|
||||||
: base(config, logger, jsonSerializer, mediaEncoder)
|
: base(config, logger, jsonSerializer, mediaEncoder)
|
||||||
{
|
{
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
|
_appHost = appHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
private const string ChannelIdPrefix = "sat_";
|
private const string ChannelIdPrefix = "sat_";
|
||||||
|
@ -39,7 +42,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.SatIp
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(tuner.M3UUrl))
|
if (!string.IsNullOrWhiteSpace(tuner.M3UUrl))
|
||||||
{
|
{
|
||||||
return await new M3uParser(Logger, _fileSystem, _httpClient).Parse(tuner.M3UUrl, ChannelIdPrefix, tuner.Id, cancellationToken).ConfigureAwait(false);
|
return await new M3uParser(Logger, _fileSystem, _httpClient, _appHost).Parse(tuner.M3UUrl, ChannelIdPrefix, tuner.Id, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
var channels = await new ChannelScan(Logger).Scan(tuner, cancellationToken).ConfigureAwait(false);
|
var channels = await new ChannelScan(Logger).Scan(tuner, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
|
@ -144,7 +144,7 @@ namespace MediaBrowser.Server.Implementations.Photos
|
||||||
return ItemUpdateType.None;
|
return ItemUpdateType.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
await ProviderManager.SaveImage(item, outputPath, "image/png", imageType, null, Guid.NewGuid().ToString("N"), cancellationToken).ConfigureAwait(false);
|
await ProviderManager.SaveImage(item, outputPath, "image/png", imageType, null, false, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
return ItemUpdateType.ImageUpdate;
|
return ItemUpdateType.ImageUpdate;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,9 +22,7 @@ namespace MediaBrowser.Server.Implementations.Sorting
|
||||||
|
|
||||||
private float GetValue(BaseItem x)
|
private float GetValue(BaseItem x)
|
||||||
{
|
{
|
||||||
var hasCriticRating = x as IHasCriticRating;
|
return x.CriticRating ?? 0;
|
||||||
|
|
||||||
return hasCriticRating == null ? 0 : hasCriticRating.CriticRating ?? 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -71,7 +71,7 @@ namespace MediaBrowser.ServerApplication
|
||||||
|
|
||||||
if (_isRunningAsService)
|
if (_isRunningAsService)
|
||||||
{
|
{
|
||||||
_canRestartService = CanRestartWindowsService();
|
//_canRestartService = CanRestartWindowsService();
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentProcess = Process.GetCurrentProcess();
|
var currentProcess = Process.GetCurrentProcess();
|
||||||
|
|
|
@ -986,9 +986,6 @@
|
||||||
<Content Include="dashboard-ui\scripts\userparentalcontrol.js">
|
<Content Include="dashboard-ui\scripts\userparentalcontrol.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="dashboard-ui\scripts\wizardservice.js">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Include="dashboard-ui\scripts\wizardsettings.js">
|
<Content Include="dashboard-ui\scripts\wizardsettings.js">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
@ -1034,9 +1031,6 @@
|
||||||
<Content Include="dashboard-ui\wizardlivetvtuner.html">
|
<Content Include="dashboard-ui\wizardlivetvtuner.html">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
<Content Include="dashboard-ui\wizardservice.html">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Include="dashboard-ui\wizardsettings.html">
|
<Content Include="dashboard-ui\wizardsettings.html">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
|
@ -294,14 +294,12 @@ namespace MediaBrowser.XbmcMetadata.Parsers
|
||||||
{
|
{
|
||||||
var text = reader.ReadElementContentAsString();
|
var text = reader.ReadElementContentAsString();
|
||||||
|
|
||||||
var hasCriticRating = item as IHasCriticRating;
|
if (!string.IsNullOrEmpty(text))
|
||||||
|
|
||||||
if (hasCriticRating != null && !string.IsNullOrEmpty(text))
|
|
||||||
{
|
{
|
||||||
float value;
|
float value;
|
||||||
if (float.TryParse(text, NumberStyles.Any, _usCulture, out value))
|
if (float.TryParse(text, NumberStyles.Any, _usCulture, out value))
|
||||||
{
|
{
|
||||||
hasCriticRating.CriticRating = value;
|
item.CriticRating = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,12 +386,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(val))
|
if (!string.IsNullOrWhiteSpace(val))
|
||||||
{
|
{
|
||||||
var hasShortOverview = item as IHasShortOverview;
|
item.ShortOverview = val;
|
||||||
|
|
||||||
if (hasShortOverview != null)
|
|
||||||
{
|
|
||||||
hasShortOverview.ShortOverview = val;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -418,12 +411,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(val))
|
if (!string.IsNullOrWhiteSpace(val))
|
||||||
{
|
{
|
||||||
var hasCriticRating = item as IHasCriticRating;
|
item.CriticRatingSummary = val;
|
||||||
|
|
||||||
if (hasCriticRating != null)
|
|
||||||
{
|
|
||||||
hasCriticRating.CriticRatingSummary = val;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -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.663</version>
|
<version>3.0.665</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 Emby Theater and Emby Server. Not intended for plugin developer consumption.</description>
|
<description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description>
|
||||||
<copyright>Copyright © Emby 2013</copyright>
|
<copyright>Copyright © Emby 2013</copyright>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency id="MediaBrowser.Common" version="3.0.663" />
|
<dependency id="MediaBrowser.Common" version="3.0.665" />
|
||||||
<dependency id="NLog" version="4.3.8" />
|
<dependency id="NLog" version="4.3.8" />
|
||||||
<dependency id="SimpleInjector" version="3.2.2" />
|
<dependency id="SimpleInjector" version="3.2.2" />
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
|
@ -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.663</version>
|
<version>3.0.665</version>
|
||||||
<title>MediaBrowser.Common</title>
|
<title>MediaBrowser.Common</title>
|
||||||
<authors>Emby Team</authors>
|
<authors>Emby 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.663</version>
|
<version>3.0.665</version>
|
||||||
<title>Media Browser.Server.Core</title>
|
<title>Media Browser.Server.Core</title>
|
||||||
<authors>Emby Team</authors>
|
<authors>Emby 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 Emby Server.</description>
|
<description>Contains core components required to build plugins for Emby Server.</description>
|
||||||
<copyright>Copyright © Emby 2013</copyright>
|
<copyright>Copyright © Emby 2013</copyright>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency id="MediaBrowser.Common" version="3.0.663" />
|
<dependency id="MediaBrowser.Common" version="3.0.665" />
|
||||||
<dependency id="Interfaces.IO" version="1.0.0.5" />
|
<dependency id="Interfaces.IO" version="1.0.0.5" />
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
|
Loading…
Reference in a new issue