support conditions with direct play profiles
This commit is contained in:
parent
662929fe8b
commit
24246ba85f
|
@ -68,13 +68,15 @@ namespace MediaBrowser.Controller.Dlna
|
|||
public class ProfileCondition
|
||||
{
|
||||
public ProfileConditionType Condition { get; set; }
|
||||
public ProfileConditionValue Value { get; set; }
|
||||
public ProfileConditionValue Property { get; set; }
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
public enum DlnaProfileType
|
||||
{
|
||||
Audio = 0,
|
||||
Video = 1
|
||||
Video = 1,
|
||||
Photo = 2
|
||||
}
|
||||
|
||||
public enum ProfileConditionType
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
<Compile Include="PlayTo\PlaylistItem.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="PlayTo\PlaylistItemFactory.cs" />
|
||||
<Compile Include="PlayTo\PlayToManager.cs" />
|
||||
<Compile Include="PlayTo\PlayToServerEntryPoint.cs" />
|
||||
<Compile Include="PlayTo\ServiceAction.cs" />
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Dlna;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Controller.Session;
|
||||
|
@ -263,7 +264,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
|
||||
case PlaystateCommand.Seek:
|
||||
var playlistItem = Playlist.FirstOrDefault(p => p.PlayState == 1);
|
||||
if (playlistItem != null && playlistItem.Transcode && playlistItem.IsVideo && _currentItem != null)
|
||||
if (playlistItem != null && playlistItem.Transcode && playlistItem.MediaType == DlnaProfileType.Video && _currentItem != null)
|
||||
{
|
||||
var newItem = CreatePlaylistItem(_currentItem, command.SeekPositionTicks ?? 0, GetServerAddress());
|
||||
playlistItem.StartPositionTicks = newItem.StartPositionTicks;
|
||||
|
@ -394,11 +395,13 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
|
||||
var deviceInfo = _device.Properties;
|
||||
|
||||
var playlistItem = PlaylistItem.Create(item, _dlnaManager.GetProfile(deviceInfo.ToDeviceIdentification()));
|
||||
var playlistItem = GetPlaylistItem(item, _dlnaManager.GetProfile(deviceInfo.ToDeviceIdentification()));
|
||||
playlistItem.StartPositionTicks = startPostionTicks;
|
||||
|
||||
if (playlistItem.IsAudio)
|
||||
if (playlistItem.MediaType == DlnaProfileType.Audio)
|
||||
{
|
||||
playlistItem.StreamUrl = StreamHelper.GetAudioUrl(playlistItem, serverAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
playlistItem.StreamUrl = StreamHelper.GetVideoUrl(_device.Properties, playlistItem, streams, serverAddress);
|
||||
|
@ -412,6 +415,32 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
return playlistItem;
|
||||
}
|
||||
|
||||
private PlaylistItem GetPlaylistItem(BaseItem item, DeviceProfile profile)
|
||||
{
|
||||
var video = item as Video;
|
||||
|
||||
if (video != null)
|
||||
{
|
||||
return new PlaylistItemFactory(_itemRepository).Create(video, profile);
|
||||
}
|
||||
|
||||
var audio = item as Audio;
|
||||
|
||||
if (audio != null)
|
||||
{
|
||||
return new PlaylistItemFactory(_itemRepository).Create(audio, profile);
|
||||
}
|
||||
|
||||
var photo = item as Photo;
|
||||
|
||||
if (photo != null)
|
||||
{
|
||||
return new PlaylistItemFactory(_itemRepository).Create(photo, profile);
|
||||
}
|
||||
|
||||
throw new ArgumentException("Unrecognized item type.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays the items.
|
||||
/// </summary>
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
using MediaBrowser.Controller.Dlna;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Dlna.PlayTo
|
||||
{
|
||||
|
@ -11,11 +6,11 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
{
|
||||
public string ItemId { get; set; }
|
||||
|
||||
public string MediaSourceId { get; set; }
|
||||
|
||||
public bool Transcode { get; set; }
|
||||
|
||||
public bool IsVideo { get; set; }
|
||||
|
||||
public bool IsAudio { get; set; }
|
||||
public DlnaProfileType MediaType { get; set; }
|
||||
|
||||
public string FileFormat { get; set; }
|
||||
|
||||
|
@ -30,72 +25,5 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
public string Didl { get; set; }
|
||||
|
||||
public long StartPositionTicks { get; set; }
|
||||
|
||||
public static PlaylistItem Create(BaseItem item, DeviceProfile profile)
|
||||
{
|
||||
var playlistItem = new PlaylistItem
|
||||
{
|
||||
ItemId = item.Id.ToString()
|
||||
};
|
||||
|
||||
DlnaProfileType profileType;
|
||||
if (string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
playlistItem.IsVideo = true;
|
||||
profileType = DlnaProfileType.Video;
|
||||
}
|
||||
else
|
||||
{
|
||||
playlistItem.IsAudio = true;
|
||||
profileType = DlnaProfileType.Audio;
|
||||
}
|
||||
|
||||
var path = item.Path;
|
||||
|
||||
var directPlay = profile.DirectPlayProfiles.FirstOrDefault(i => i.Type == profileType && IsSupported(i, path));
|
||||
|
||||
if (directPlay != null)
|
||||
{
|
||||
playlistItem.Transcode = false;
|
||||
playlistItem.FileFormat = Path.GetExtension(path);
|
||||
playlistItem.MimeType = directPlay.MimeType;
|
||||
return playlistItem;
|
||||
}
|
||||
|
||||
var transcodingProfile = profile.TranscodingProfiles.FirstOrDefault(i => i.Type == profileType && IsSupported(profile, i, path));
|
||||
|
||||
if (transcodingProfile != null)
|
||||
{
|
||||
playlistItem.Transcode = true;
|
||||
//Just to make sure we have a "." for the url, remove it in case a user adds it or not
|
||||
playlistItem.FileFormat = "." + transcodingProfile.Container.TrimStart('.');
|
||||
|
||||
playlistItem.MimeType = transcodingProfile.MimeType;
|
||||
}
|
||||
|
||||
return playlistItem;
|
||||
}
|
||||
|
||||
private static bool IsSupported(DirectPlayProfile profile, string path)
|
||||
{
|
||||
var mediaContainer = Path.GetExtension(path);
|
||||
|
||||
if (!profile.Containers.Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Placeholder for future conditions
|
||||
|
||||
// TODO: Support codec list as additional restriction
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool IsSupported(DeviceProfile profile, TranscodingProfile transcodingProfile, string path)
|
||||
{
|
||||
// Placeholder for future conditions
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
278
MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
Normal file
278
MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
Normal file
|
@ -0,0 +1,278 @@
|
|||
using MediaBrowser.Controller.Dlna;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Persistence;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace MediaBrowser.Dlna.PlayTo
|
||||
{
|
||||
public class PlaylistItemFactory
|
||||
{
|
||||
private readonly IItemRepository _itemRepo;
|
||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||
|
||||
public PlaylistItemFactory(IItemRepository itemRepo)
|
||||
{
|
||||
_itemRepo = itemRepo;
|
||||
}
|
||||
|
||||
public PlaylistItem Create(Audio item, DeviceProfile profile)
|
||||
{
|
||||
var playlistItem = new PlaylistItem
|
||||
{
|
||||
ItemId = item.Id.ToString("N"),
|
||||
MediaType = DlnaProfileType.Audio
|
||||
};
|
||||
|
||||
var mediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery
|
||||
{
|
||||
ItemId = item.Id,
|
||||
Type = MediaStreamType.Audio
|
||||
});
|
||||
|
||||
var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
|
||||
|
||||
var directPlay = profile.DirectPlayProfiles
|
||||
.FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, audioStream));
|
||||
|
||||
if (directPlay != null)
|
||||
{
|
||||
playlistItem.Transcode = false;
|
||||
playlistItem.FileFormat = Path.GetExtension(item.Path);
|
||||
playlistItem.MimeType = directPlay.MimeType;
|
||||
|
||||
return playlistItem;
|
||||
}
|
||||
|
||||
var transcodingProfile = profile.TranscodingProfiles
|
||||
.FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(profile, i, item));
|
||||
|
||||
if (transcodingProfile != null)
|
||||
{
|
||||
playlistItem.Transcode = true;
|
||||
|
||||
playlistItem.FileFormat = "." + transcodingProfile.Container.TrimStart('.');
|
||||
playlistItem.MimeType = transcodingProfile.MimeType;
|
||||
}
|
||||
|
||||
return playlistItem;
|
||||
}
|
||||
|
||||
public PlaylistItem Create(Photo item, DeviceProfile profile)
|
||||
{
|
||||
var playlistItem = new PlaylistItem
|
||||
{
|
||||
ItemId = item.Id.ToString("N"),
|
||||
MediaType = DlnaProfileType.Photo
|
||||
};
|
||||
|
||||
var directPlay = profile.DirectPlayProfiles
|
||||
.FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item));
|
||||
|
||||
if (directPlay != null)
|
||||
{
|
||||
playlistItem.Transcode = false;
|
||||
playlistItem.FileFormat = Path.GetExtension(item.Path);
|
||||
playlistItem.MimeType = directPlay.MimeType;
|
||||
|
||||
return playlistItem;
|
||||
}
|
||||
|
||||
var transcodingProfile = profile.TranscodingProfiles
|
||||
.FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(profile, i, item));
|
||||
|
||||
if (transcodingProfile != null)
|
||||
{
|
||||
playlistItem.Transcode = true;
|
||||
|
||||
playlistItem.FileFormat = "." + transcodingProfile.Container.TrimStart('.');
|
||||
playlistItem.MimeType = transcodingProfile.MimeType;
|
||||
}
|
||||
|
||||
return playlistItem;
|
||||
}
|
||||
|
||||
public PlaylistItem Create(Video item, DeviceProfile profile)
|
||||
{
|
||||
var playlistItem = new PlaylistItem
|
||||
{
|
||||
ItemId = item.Id.ToString("N"),
|
||||
MediaType = DlnaProfileType.Video
|
||||
};
|
||||
|
||||
var mediaStreams = _itemRepo.GetMediaStreams(new MediaStreamQuery
|
||||
{
|
||||
ItemId = item.Id
|
||||
|
||||
}).ToList();
|
||||
|
||||
var audioStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Audio);
|
||||
var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video);
|
||||
|
||||
var directPlay = profile.DirectPlayProfiles
|
||||
.FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(i, item, videoStream, audioStream));
|
||||
|
||||
if (directPlay != null)
|
||||
{
|
||||
playlistItem.Transcode = false;
|
||||
playlistItem.FileFormat = Path.GetExtension(item.Path);
|
||||
playlistItem.MimeType = directPlay.MimeType;
|
||||
|
||||
return playlistItem;
|
||||
}
|
||||
|
||||
var transcodingProfile = profile.TranscodingProfiles
|
||||
.FirstOrDefault(i => i.Type == playlistItem.MediaType && IsSupported(profile, i, item));
|
||||
|
||||
if (transcodingProfile != null)
|
||||
{
|
||||
playlistItem.Transcode = true;
|
||||
|
||||
playlistItem.FileFormat = "." + transcodingProfile.Container.TrimStart('.');
|
||||
playlistItem.MimeType = transcodingProfile.MimeType;
|
||||
}
|
||||
|
||||
return playlistItem;
|
||||
}
|
||||
|
||||
private bool IsSupported(DirectPlayProfile profile, Photo item)
|
||||
{
|
||||
var mediaPath = item.Path;
|
||||
|
||||
var mediaContainer = Path.GetExtension(mediaPath);
|
||||
|
||||
if (!profile.Containers.Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!profile.Conditions.Any(i => IsConditionSatisfied(i, mediaPath, null, null)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsSupported(DirectPlayProfile profile, Audio item, MediaStream audioStream)
|
||||
{
|
||||
var mediaPath = item.Path;
|
||||
|
||||
var mediaContainer = Path.GetExtension(mediaPath);
|
||||
|
||||
if (!profile.Containers.Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!profile.Conditions.Any(i => IsConditionSatisfied(i, mediaPath, null, audioStream)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsSupported(DirectPlayProfile profile, Video item, MediaStream videoStream, MediaStream audioStream)
|
||||
{
|
||||
if (item.VideoType != VideoType.VideoFile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var mediaPath = item.Path;
|
||||
|
||||
var mediaContainer = Path.GetExtension(mediaPath);
|
||||
|
||||
if (!profile.Containers.Any(i => string.Equals("." + i.TrimStart('.'), mediaContainer, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!profile.Conditions.Any(i => IsConditionSatisfied(i, mediaPath, videoStream, audioStream)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsSupported(DeviceProfile profile, TranscodingProfile transcodingProfile, Audio item)
|
||||
{
|
||||
// Placeholder for future conditions
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsSupported(DeviceProfile profile, TranscodingProfile transcodingProfile, Photo item)
|
||||
{
|
||||
// Placeholder for future conditions
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsSupported(DeviceProfile profile, TranscodingProfile transcodingProfile, Video item)
|
||||
{
|
||||
// Placeholder for future conditions
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsConditionSatisfied(ProfileCondition condition, string mediaPath, MediaStream videoStream, MediaStream audioStream)
|
||||
{
|
||||
var actualValue = GetConditionValue(condition, mediaPath, videoStream, audioStream);
|
||||
|
||||
if (actualValue.HasValue)
|
||||
{
|
||||
long expected;
|
||||
if (long.TryParse("", NumberStyles.Any, _usCulture, out expected))
|
||||
{
|
||||
switch (condition.Condition)
|
||||
{
|
||||
case ProfileConditionType.Equals:
|
||||
return actualValue.Value == expected;
|
||||
case ProfileConditionType.GreaterThanEqual:
|
||||
return actualValue.Value >= expected;
|
||||
case ProfileConditionType.LessThanEqual:
|
||||
return actualValue.Value <= expected;
|
||||
case ProfileConditionType.NotEquals:
|
||||
return actualValue.Value != expected;
|
||||
default:
|
||||
throw new InvalidOperationException("Unexpected ProfileConditionType");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private long? GetConditionValue(ProfileCondition condition, string mediaPath, MediaStream videoStream, MediaStream audioStream)
|
||||
{
|
||||
switch (condition.Property)
|
||||
{
|
||||
case ProfileConditionValue.AudioBitrate:
|
||||
return audioStream == null ? null : audioStream.BitRate;
|
||||
case ProfileConditionValue.AudioChannels:
|
||||
return audioStream == null ? null : audioStream.Channels;
|
||||
case ProfileConditionValue.Filesize:
|
||||
return new FileInfo(mediaPath).Length;
|
||||
case ProfileConditionValue.VideoBitrate:
|
||||
return videoStream == null ? null : videoStream.BitRate;
|
||||
case ProfileConditionValue.VideoFramerate:
|
||||
return videoStream == null ? null : (ConvertToLong(videoStream.AverageFrameRate ?? videoStream.RealFrameRate));
|
||||
case ProfileConditionValue.VideoHeight:
|
||||
return videoStream == null ? null : videoStream.Height;
|
||||
case ProfileConditionValue.VideoWidth:
|
||||
return videoStream == null ? null : videoStream.Width;
|
||||
default:
|
||||
throw new InvalidOperationException("Unexpected Property");
|
||||
}
|
||||
}
|
||||
|
||||
private long? ConvertToLong(float? val)
|
||||
{
|
||||
return val.HasValue ? Convert.ToInt64(val.Value) : (long?)null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -60,7 +60,7 @@ namespace MediaBrowser.Dlna.PlayTo
|
|||
{
|
||||
contentFeatures = "DLNA.ORG_PN=MPEG_PS_PAL";
|
||||
}
|
||||
else if (item.IsVideo)
|
||||
else if (item.MediaType == Controller.Dlna.DlnaProfileType.Video)
|
||||
{
|
||||
//Default to AVI for video
|
||||
contentFeatures = "DLNA.ORG_PN=AVI";
|
||||
|
|
|
@ -180,7 +180,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
|||
result.StatusMessage = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (fileExists || otherDuplicatePaths.Count > 0)
|
||||
{
|
||||
result.Status = FileSortingStatus.SkippedExisting;
|
||||
|
@ -453,24 +453,22 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
|||
|
||||
private bool IsSameEpisode(string sourcePath, string newPath)
|
||||
{
|
||||
var sourceFileInfo = new FileInfo(sourcePath);
|
||||
var destinationFileInfo = new FileInfo(newPath);
|
||||
|
||||
FileInfo sourceFileInfo = new FileInfo(sourcePath);
|
||||
FileInfo destinationFileInfo = new FileInfo(newPath);
|
||||
|
||||
try
|
||||
try
|
||||
{
|
||||
if (sourceFileInfo.Length == destinationFileInfo.Length)
|
||||
{
|
||||
if (sourceFileInfo.Length == destinationFileInfo.Length)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
|||
.Replace("!", " ")
|
||||
.Replace("(", " ")
|
||||
.Replace(")", " ")
|
||||
.Replace(":", " ")
|
||||
.Replace(",", " ")
|
||||
.Replace("-", " ")
|
||||
.Replace(" a ", String.Empty, StringComparison.OrdinalIgnoreCase)
|
||||
|
|
Loading…
Reference in New Issue
Block a user