support conditions with direct play profiles

This commit is contained in:
Luke Pulverenti 2014-03-22 14:25:03 -04:00
parent 662929fe8b
commit 24246ba85f
8 changed files with 332 additions and 95 deletions

View file

@ -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

View file

@ -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" />

View file

@ -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>

View file

@ -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;
}
}
}

View 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;
}
}
}

View file

@ -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";

View file

@ -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;
}
}
}

View file

@ -67,6 +67,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
.Replace("!", " ")
.Replace("(", " ")
.Replace(")", " ")
.Replace(":", " ")
.Replace(",", " ")
.Replace("-", " ")
.Replace(" a ", String.Empty, StringComparison.OrdinalIgnoreCase)