diff --git a/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs b/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs
index 68e25e2f7b..4b9a43867f 100644
--- a/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs
+++ b/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs
@@ -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
diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
index a7ee05cf32..800fb1b236 100644
--- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
+++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj
@@ -66,6 +66,7 @@
Code
+
diff --git a/MediaBrowser.Dlna/PlayTo/DlnaController.cs b/MediaBrowser.Dlna/PlayTo/DlnaController.cs
index bda737ad2d..e94663802f 100644
--- a/MediaBrowser.Dlna/PlayTo/DlnaController.cs
+++ b/MediaBrowser.Dlna/PlayTo/DlnaController.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.");
+ }
+
///
/// Plays the items.
///
diff --git a/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs b/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs
index 4f776807e2..3524575631 100644
--- a/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs
+++ b/MediaBrowser.Dlna/PlayTo/PlaylistItem.cs
@@ -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;
- }
}
}
\ No newline at end of file
diff --git a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
new file mode 100644
index 0000000000..2eb51e2149
--- /dev/null
+++ b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
@@ -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;
+ }
+ }
+}
diff --git a/MediaBrowser.Dlna/PlayTo/StreamHelper.cs b/MediaBrowser.Dlna/PlayTo/StreamHelper.cs
index 97d208aa37..f5025fdd61 100644
--- a/MediaBrowser.Dlna/PlayTo/StreamHelper.cs
+++ b/MediaBrowser.Dlna/PlayTo/StreamHelper.cs
@@ -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";
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
index 5d326f1caa..d448118860 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
+++ b/MediaBrowser.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs
@@ -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
- {
- if (sourceFileInfo.Length == destinationFileInfo.Length)
- {
- return true;
- }
- }
- catch (FileNotFoundException)
+ try
+ {
+ if (sourceFileInfo.Length == destinationFileInfo.Length)
{
- return false;
+ return true;
}
-
+ }
+ catch (FileNotFoundException)
+ {
return false;
+ }
+ return false;
}
}
}
diff --git a/MediaBrowser.Server.Implementations/FileOrganization/NameUtils.cs b/MediaBrowser.Server.Implementations/FileOrganization/NameUtils.cs
index 23d0363cb8..7f936791a7 100644
--- a/MediaBrowser.Server.Implementations/FileOrganization/NameUtils.cs
+++ b/MediaBrowser.Server.Implementations/FileOrganization/NameUtils.cs
@@ -67,6 +67,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
.Replace("!", " ")
.Replace("(", " ")
.Replace(")", " ")
+ .Replace(":", " ")
.Replace(",", " ")
.Replace("-", " ")
.Replace(" a ", String.Empty, StringComparison.OrdinalIgnoreCase)