aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichalis Adamidis <gsnerf@gsnerf.de>2014-08-06 21:06:34 +0200
committerMichalis Adamidis <gsnerf@gsnerf.de>2014-08-06 21:06:34 +0200
commitb957e7c7b91257d7f33f9890b9a14445a80164ea (patch)
tree3a6082b1bdf717d0f28ef96f14a70c1265071ac4
parent7994f0dcd9082cc657e07dbff6ecc4e638f1f527 (diff)
parent284bd3e9f562ae499ad8d8b778392196ac99ed3a (diff)
Merge branch 'master' of https://github.com/MediaBrowser/MediaBrowser
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs7
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs71
-rw-r--r--MediaBrowser.Api/Playback/StreamRequest.cs6
-rw-r--r--MediaBrowser.Api/Playback/StreamState.cs3
-rw-r--r--MediaBrowser.Api/PlaylistService.cs7
-rw-r--r--MediaBrowser.Api/Subtitles/SubtitleService.cs82
-rw-r--r--MediaBrowser.Api/SystemService.cs2
-rw-r--r--MediaBrowser.Common/Net/MimeTypes.cs5
-rw-r--r--MediaBrowser.Controller/Entities/Audio/Audio.cs5
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs5
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicArtist.cs5
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicGenre.cs5
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs8
-rw-r--r--MediaBrowser.Controller/Entities/LinkedChild.cs8
-rw-r--r--MediaBrowser.Controller/Entities/TV/Season.cs5
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs5
-rw-r--r--MediaBrowser.Controller/Entities/Video.cs5
-rw-r--r--MediaBrowser.Controller/IServerApplicationHost.cs6
-rw-r--r--MediaBrowser.Controller/Library/TVUtils.cs2
-rw-r--r--MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs4
-rw-r--r--MediaBrowser.Controller/Playlists/IPlaylistManager.cs4
-rw-r--r--MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs25
-rw-r--r--MediaBrowser.Dlna/Didl/DidlBuilder.cs56
-rw-r--r--MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs5
-rw-r--r--MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs5
-rw-r--r--MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs5
-rw-r--r--MediaBrowser.Dlna/Profiles/Windows81Profile.cs8
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Android.xml2
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Default.xml2
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml2
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml2
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml2
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml2
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml9
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml9
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml2
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml2
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml2
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml2
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml2
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml2
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml2
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml2
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Windows 8 RT.xml5
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Windows Phone.xml2
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml2
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml2
-rw-r--r--MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml2
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs14
-rw-r--r--MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs24
-rw-r--r--MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs12
-rw-r--r--MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj1
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs38
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs59
-rw-r--r--MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs5
-rw-r--r--MediaBrowser.Model/Dlna/DeviceProfile.cs6
-rw-r--r--MediaBrowser.Model/Dlna/DlnaMaps.cs2
-rw-r--r--MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs15
-rw-r--r--MediaBrowser.Model/Dlna/StreamBuilder.cs56
-rw-r--r--MediaBrowser.Model/Dlna/StreamInfo.cs80
-rw-r--r--MediaBrowser.Model/Dlna/SubtitleProfile.cs12
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs12
-rw-r--r--MediaBrowser.Model/MediaInfo/SubtitleFormat.cs1
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj1
-rw-r--r--MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs47
-rw-r--r--MediaBrowser.Server.Implementations/Dto/DtoService.cs14
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs25
-rw-r--r--MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json3
-rw-r--r--MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs24
-rw-r--r--MediaBrowser.Server.Implementations/Udp/UdpServer.cs2
-rw-r--r--MediaBrowser.ServerApplication/ApplicationHost.cs12
-rw-r--r--Nuget/MediaBrowser.Common.Internal.nuspec4
-rw-r--r--Nuget/MediaBrowser.Common.nuspec2
-rw-r--r--Nuget/MediaBrowser.Model.Signed.nuspec2
-rw-r--r--Nuget/MediaBrowser.Server.Core.nuspec4
75 files changed, 689 insertions, 201 deletions
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 1963ad10a..9d54458a6 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -1605,6 +1605,8 @@ namespace MediaBrowser.Api.Playback
{
state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true);
}
+
+ state.AllMediaStreams = mediaStreams;
}
private async Task<MediaSourceInfo> GetChannelMediaInfo(string id,
@@ -1640,7 +1642,10 @@ namespace MediaBrowser.Api.Playback
// Can't stream copy if we're burning in subtitles
if (request.SubtitleStreamIndex.HasValue)
{
- return false;
+ if (request.SubtitleMethod == SubtitleDeliveryMethod.Encode)
+ {
+ return false;
+ }
}
// Source and target codecs must match
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index 10543351b..42fa63fb7 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -5,6 +5,8 @@ using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding;
+using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using ServiceStack;
using System;
@@ -18,8 +20,7 @@ using System.Threading.Tasks;
namespace MediaBrowser.Api.Playback.Hls
{
- [Route("/Videos/{Id}/master.m3u8", "GET")]
- [Api(Description = "Gets a video stream using HTTP live streaming.")]
+ [Route("/Videos/{Id}/master.m3u8", "GET", Summary = "Gets a video stream using HTTP live streaming.")]
public class GetMasterHlsVideoStream : VideoStreamRequest
{
public bool EnableAdaptiveBitrateStreaming { get; set; }
@@ -30,8 +31,7 @@ namespace MediaBrowser.Api.Playback.Hls
}
}
- [Route("/Videos/{Id}/main.m3u8", "GET")]
- [Api(Description = "Gets a video stream using HTTP live streaming.")]
+ [Route("/Videos/{Id}/main.m3u8", "GET", Summary = "Gets a video stream using HTTP live streaming.")]
public class GetMainHlsVideoStream : VideoStreamRequest
{
}
@@ -359,7 +359,17 @@ namespace MediaBrowser.Api.Playback.Hls
var playlistUrl = (state.RunTimeTicks ?? 0) > 0 ? "main.m3u8" : "live.m3u8";
playlistUrl += queryString;
- AppendPlaylist(builder, playlistUrl, totalBitrate);
+ var request = (GetMasterHlsVideoStream) state.Request;
+
+ var subtitleStreams = state.AllMediaStreams
+ .Where(i => i.IsTextSubtitleStream)
+ .ToList();
+
+ var subtitleGroup = subtitleStreams.Count > 0 && request.SubtitleMethod == SubtitleDeliveryMethod.Hls ?
+ "subs" :
+ null;
+
+ AppendPlaylist(builder, playlistUrl, totalBitrate, subtitleGroup);
if (EnableAdaptiveBitrateStreaming(state))
{
@@ -369,16 +379,52 @@ namespace MediaBrowser.Api.Playback.Hls
var variation = GetBitrateVariation(totalBitrate);
var newBitrate = totalBitrate - variation;
- AppendPlaylist(builder, playlistUrl.Replace(requestedVideoBitrate.ToString(UsCulture), (requestedVideoBitrate - variation).ToString(UsCulture)), newBitrate);
+ AppendPlaylist(builder, playlistUrl.Replace(requestedVideoBitrate.ToString(UsCulture), (requestedVideoBitrate - variation).ToString(UsCulture)), newBitrate, subtitleGroup);
variation *= 2;
newBitrate = totalBitrate - variation;
- AppendPlaylist(builder, playlistUrl.Replace(requestedVideoBitrate.ToString(UsCulture), (requestedVideoBitrate - variation).ToString(UsCulture)), newBitrate);
+ AppendPlaylist(builder, playlistUrl.Replace(requestedVideoBitrate.ToString(UsCulture), (requestedVideoBitrate - variation).ToString(UsCulture)), newBitrate, subtitleGroup);
+ }
+
+ if (!string.IsNullOrWhiteSpace(subtitleGroup))
+ {
+ AddSubtitles(state, subtitleStreams, builder);
}
return builder.ToString();
}
+ private void AddSubtitles(StreamState state, IEnumerable<MediaStream> subtitles, StringBuilder builder)
+ {
+ var selectedIndex = state.SubtitleStream == null ? (int?)null : state.SubtitleStream.Index;
+
+ foreach (var stream in subtitles)
+ {
+ const string format = "#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"{0}\",DEFAULT={1},FORCED={2},URI=\"{3}\",LANGUAGE=\"{4}\"";
+
+ var name = stream.Language;
+
+ var isDefault = selectedIndex.HasValue && selectedIndex.Value == stream.Index;
+ var isForced = stream.IsForced;
+
+ if (string.IsNullOrWhiteSpace(name)) name = stream.Codec ?? "Unknown";
+
+ var url = string.Format("{0}/Subtitles/{1}/subtitles.m3u8?SegmentLength={2}",
+ state.Request.MediaSourceId,
+ stream.Index.ToString(UsCulture),
+ 30.ToString(UsCulture));
+
+ var line = string.Format(format,
+ name,
+ isDefault ? "YES" : "NO",
+ isForced ? "YES" : "NO",
+ url,
+ stream.Language ?? "Unknown");
+
+ builder.AppendLine(line);
+ }
+ }
+
private bool EnableAdaptiveBitrateStreaming(StreamState state)
{
var request = state.Request as GetMasterHlsVideoStream;
@@ -397,9 +443,16 @@ namespace MediaBrowser.Api.Playback.Hls
return state.VideoRequest.VideoBitRate.HasValue;
}
- private void AppendPlaylist(StringBuilder builder, string url, int bitrate)
+ private void AppendPlaylist(StringBuilder builder, string url, int bitrate, string subtitleGroup)
{
- builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + bitrate.ToString(UsCulture));
+ var header = "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + bitrate.ToString(UsCulture);
+
+ if (!string.IsNullOrWhiteSpace(subtitleGroup))
+ {
+ header += string.Format(",SUBTITLES=\"{0}\"", subtitleGroup);
+ }
+
+ builder.AppendLine(header);
builder.AppendLine(url);
}
diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs
index dfb57ef0d..c72ead949 100644
--- a/MediaBrowser.Api/Playback/StreamRequest.cs
+++ b/MediaBrowser.Api/Playback/StreamRequest.cs
@@ -1,4 +1,5 @@
-using ServiceStack;
+using MediaBrowser.Model.Dlna;
+using ServiceStack;
namespace MediaBrowser.Api.Playback
{
@@ -160,6 +161,9 @@ namespace MediaBrowser.Api.Playback
[ApiMember(Name = "Level", Description = "Optional. Specify a level for the h264 profile, e.g. 3, 3.1.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string Level { get; set; }
+ [ApiMember(Name = "SubtitleDeliveryMethod", Description = "Optional. Specify the subtitle delivery method.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public SubtitleDeliveryMethod SubtitleMethod { get; set; }
+
/// <summary>
/// Gets a value indicating whether this instance has fixed resolution.
/// </summary>
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
index c6f454447..1d3ff939a 100644
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ b/MediaBrowser.Api/Playback/StreamState.cs
@@ -38,6 +38,8 @@ namespace MediaBrowser.Api.Playback
public string InputContainer { get; set; }
+ public List<MediaStream> AllMediaStreams { get; set; }
+
public MediaStream AudioStream { get; set; }
public MediaStream VideoStream { get; set; }
public MediaStream SubtitleStream { get; set; }
@@ -78,6 +80,7 @@ namespace MediaBrowser.Api.Playback
SupportedAudioCodecs = new List<string>();
PlayableStreamFileNames = new List<string>();
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ AllMediaStreams = new List<MediaStream>();
}
public string InputAudioSync { get; set; }
diff --git a/MediaBrowser.Api/PlaylistService.cs b/MediaBrowser.Api/PlaylistService.cs
index b4d2e2f0f..2e3d38f46 100644
--- a/MediaBrowser.Api/PlaylistService.cs
+++ b/MediaBrowser.Api/PlaylistService.cs
@@ -41,6 +41,9 @@ namespace MediaBrowser.Api
{
[ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
public string Id { get; set; }
+
+ [ApiMember(Name = "EntryIds", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "DELETE")]
+ public string EntryIds { get; set; }
}
[Route("/Playlists/{Id}/Items", "GET", Summary = "Gets the original items of a playlist")]
@@ -122,9 +125,9 @@ namespace MediaBrowser.Api
public void Delete(RemoveFromPlaylist request)
{
- //var task = _playlistManager.RemoveFromPlaylist(request.Id, request.Ids.Split(',').Select(i => new Guid(i)));
+ var task = _playlistManager.RemoveFromPlaylist(request.Id, request.EntryIds.Split(','));
- //Task.WaitAll(task);
+ Task.WaitAll(task);
}
public object Get(GetPlaylistItems request)
diff --git a/MediaBrowser.Api/Subtitles/SubtitleService.cs b/MediaBrowser.Api/Subtitles/SubtitleService.cs
index 3e692cb22..dc5799239 100644
--- a/MediaBrowser.Api/Subtitles/SubtitleService.cs
+++ b/MediaBrowser.Api/Subtitles/SubtitleService.cs
@@ -1,6 +1,4 @@
-using System.IO;
-using System.Linq;
-using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Net;
@@ -11,6 +9,10 @@ using MediaBrowser.Model.Providers;
using ServiceStack;
using System;
using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -69,7 +71,8 @@ namespace MediaBrowser.Api.Subtitles
public string Id { get; set; }
}
- [Route("/Videos/{Id}/{MediaSourceId}/Subtitles/{Index}/Stream.{Format}", "GET", Summary = "Gets subtitles in a specified format (vtt).")]
+ [Route("/Videos/{Id}/{MediaSourceId}/Subtitles/{Index}/Stream.{Format}", "GET", Summary = "Gets subtitles in a specified format.")]
+ [Route("/Videos/{Id}/{MediaSourceId}/Subtitles/{Index}/{StartPositionTicks}/Stream.{Format}", "GET", Summary = "Gets subtitles in a specified format.")]
public class GetSubtitle
{
/// <summary>
@@ -90,6 +93,29 @@ namespace MediaBrowser.Api.Subtitles
[ApiMember(Name = "StartPositionTicks", Description = "StartPositionTicks", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public long StartPositionTicks { get; set; }
+
+ [ApiMember(Name = "EndPositionTicks", Description = "EndPositionTicks", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public long? EndPositionTicks { get; set; }
+ }
+
+ [Route("/Videos/{Id}/{MediaSourceId}/Subtitles/{Index}/subtitles.m3u8", "GET", Summary = "Gets an HLS subtitle playlist.")]
+ public class GetSubtitlePlaylist
+ {
+ /// <summary>
+ /// Gets or sets the id.
+ /// </summary>
+ /// <value>The id.</value>
+ [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string Id { get; set; }
+
+ [ApiMember(Name = "MediaSourceId", Description = "MediaSourceId", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+ public string MediaSourceId { get; set; }
+
+ [ApiMember(Name = "Index", Description = "The subtitle stream index", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
+ public int Index { get; set; }
+
+ [ApiMember(Name = "SegmentLength", Description = "The subtitle srgment length", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
+ public int SegmentLength { get; set; }
}
public class SubtitleService : BaseApiService
@@ -105,6 +131,53 @@ namespace MediaBrowser.Api.Subtitles
_subtitleEncoder = subtitleEncoder;
}
+ public object Get(GetSubtitlePlaylist request)
+ {
+ var item = (Video)_libraryManager.GetItemById(new Guid(request.Id));
+
+ var mediaSource = item.GetMediaSources(false)
+ .First(i => string.Equals(i.Id, request.MediaSourceId ?? request.Id));
+
+ var builder = new StringBuilder();
+
+ var runtime = mediaSource.RunTimeTicks ?? -1;
+
+ if (runtime <= 0)
+ {
+ throw new ArgumentException("HLS Subtitles are not supported for this media.");
+ }
+
+ builder.AppendLine("#EXTM3U");
+ builder.AppendLine("#EXT-X-TARGETDURATION:" + request.SegmentLength.ToString(CultureInfo.InvariantCulture));
+ builder.AppendLine("#EXT-X-VERSION:3");
+ builder.AppendLine("#EXT-X-MEDIA-SEQUENCE:0");
+
+ long positionTicks = 0;
+ var segmentLengthTicks = TimeSpan.FromSeconds(request.SegmentLength).Ticks;
+
+ while (positionTicks < runtime)
+ {
+ var remaining = runtime - positionTicks;
+ var lengthTicks = Math.Min(remaining, segmentLengthTicks);
+
+ builder.AppendLine("#EXTINF:" + TimeSpan.FromTicks(lengthTicks).TotalSeconds.ToString(CultureInfo.InvariantCulture));
+
+ var endPositionTicks = Math.Min(runtime, positionTicks + segmentLengthTicks);
+
+ var url = string.Format("stream.srt?StartPositionTicks={0}&EndPositionTicks={1}",
+ positionTicks.ToString(CultureInfo.InvariantCulture),
+ endPositionTicks.ToString(CultureInfo.InvariantCulture));
+
+ builder.AppendLine(url);
+
+ positionTicks += segmentLengthTicks;
+ }
+
+ builder.AppendLine("#EXT-X-ENDLIST");
+
+ return ResultFactory.GetResult(builder.ToString(), Common.Net.MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary<string, string>());
+ }
+
public object Get(GetSubtitle request)
{
if (string.IsNullOrEmpty(request.Format))
@@ -132,6 +205,7 @@ namespace MediaBrowser.Api.Subtitles
request.Index,
request.Format,
request.StartPositionTicks,
+ request.EndPositionTicks,
CancellationToken.None).ConfigureAwait(false);
}
diff --git a/MediaBrowser.Api/SystemService.cs b/MediaBrowser.Api/SystemService.cs
index e31e66d19..259b1d892 100644
--- a/MediaBrowser.Api/SystemService.cs
+++ b/MediaBrowser.Api/SystemService.cs
@@ -71,6 +71,8 @@ namespace MediaBrowser.Api
/// Initializes a new instance of the <see cref="SystemService" /> class.
/// </summary>
/// <param name="appHost">The app host.</param>
+ /// <param name="appPaths">The application paths.</param>
+ /// <param name="fileSystem">The file system.</param>
/// <exception cref="System.ArgumentNullException">jsonSerializer</exception>
public SystemService(IServerApplicationHost appHost, IApplicationPaths appPaths, IFileSystem fileSystem)
{
diff --git a/MediaBrowser.Common/Net/MimeTypes.cs b/MediaBrowser.Common/Net/MimeTypes.cs
index 0740bf6d1..ee3b7dad6 100644
--- a/MediaBrowser.Common/Net/MimeTypes.cs
+++ b/MediaBrowser.Common/Net/MimeTypes.cs
@@ -236,6 +236,11 @@ namespace MediaBrowser.Common.Net
return "text/vtt";
}
+ if (ext.Equals(".ttml", StringComparison.OrdinalIgnoreCase))
+ {
+ return "application/ttml+xml";
+ }
+
if (ext.Equals(".bif", StringComparison.OrdinalIgnoreCase))
{
return "application/octet-stream";
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs
index 32d3dd5c8..d3085cb68 100644
--- a/MediaBrowser.Controller/Entities/Audio/Audio.cs
+++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs
@@ -34,6 +34,11 @@ namespace MediaBrowser.Controller.Entities.Audio
Tags = new List<string>();
}
+ public override bool SupportsAddingToPlaylist
+ {
+ get { return LocationType == LocationType.FileSystem && RunTimeTicks.HasValue; }
+ }
+
/// <summary>
/// Gets or sets a value indicating whether this instance has embedded image.
/// </summary>
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
index 695b1fd57..152d76782 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
@@ -21,6 +21,11 @@ namespace MediaBrowser.Controller.Entities.Audio
SoundtrackIds = new List<Guid>();
}
+ public override bool SupportsAddingToPlaylist
+ {
+ get { return true; }
+ }
+
[IgnoreDataMember]
public MusicArtist MusicArtist
{
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index 1544da7bc..de527b68b 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -26,6 +26,11 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
+ public override bool SupportsAddingToPlaylist
+ {
+ get { return true; }
+ }
+
protected override IEnumerable<BaseItem> ActualChildren
{
get
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
index bce9da4d1..f1dc56ac6 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
@@ -18,6 +18,11 @@ namespace MediaBrowser.Controller.Entities.Audio
return "MusicGenre-" + Name;
}
+ public override bool SupportsAddingToPlaylist
+ {
+ get { return true; }
+ }
+
/// <summary>
/// Returns the folder containing the item.
/// If the item is a folder, it returns the folder itself
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index a476f555f..fdffa60d0 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -52,6 +52,14 @@ namespace MediaBrowser.Controller.Entities
public List<ItemImageInfo> ImageInfos { get; set; }
+ public virtual bool SupportsAddingToPlaylist
+ {
+ get
+ {
+ return false;
+ }
+ }
+
/// <summary>
/// Gets a value indicating whether this instance is in mixed folder.
/// </summary>
diff --git a/MediaBrowser.Controller/Entities/LinkedChild.cs b/MediaBrowser.Controller/Entities/LinkedChild.cs
index c77fe18c4..eb94b37db 100644
--- a/MediaBrowser.Controller/Entities/LinkedChild.cs
+++ b/MediaBrowser.Controller/Entities/LinkedChild.cs
@@ -13,6 +13,9 @@ namespace MediaBrowser.Controller.Entities
public string ItemType { get; set; }
public int? ItemYear { get; set; }
+ [IgnoreDataMember]
+ public string Id { get; set; }
+
/// <summary>
/// Serves as a cache
/// </summary>
@@ -27,6 +30,11 @@ namespace MediaBrowser.Controller.Entities
Type = LinkedChildType.Manual
};
}
+
+ public LinkedChild()
+ {
+ Id = Guid.NewGuid().ToString("N");
+ }
}
public enum LinkedChildType
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index 3977d869c..b82a400fe 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -29,6 +29,11 @@ namespace MediaBrowser.Controller.Entities.TV
}
}
+ public override bool SupportsAddingToPlaylist
+ {
+ get { return true; }
+ }
+
[IgnoreDataMember]
public override bool IsPreSorted
{
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index 27ca8b18d..856ed4fdf 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -39,6 +39,11 @@ namespace MediaBrowser.Controller.Entities.TV
DisplaySpecialsWithSeasons = true;
}
+ public override bool SupportsAddingToPlaylist
+ {
+ get { return true; }
+ }
+
[IgnoreDataMember]
public override bool IsPreSorted
{
diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs
index 5685edc81..ff4c5dd90 100644
--- a/MediaBrowser.Controller/Entities/Video.cs
+++ b/MediaBrowser.Controller/Entities/Video.cs
@@ -55,6 +55,11 @@ namespace MediaBrowser.Controller.Entities
LinkedAlternateVersions = new List<LinkedChild>();
}
+ public override bool SupportsAddingToPlaylist
+ {
+ get { return LocationType == LocationType.FileSystem && RunTimeTicks.HasValue; }
+ }
+
[IgnoreDataMember]
public int MediaSourceCount
{
diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs
index 49061e05c..2af37e84d 100644
--- a/MediaBrowser.Controller/IServerApplicationHost.cs
+++ b/MediaBrowser.Controller/IServerApplicationHost.cs
@@ -46,5 +46,11 @@ namespace MediaBrowser.Controller
/// </summary>
/// <value>The server identifier.</value>
string ServerId { get; }
+
+ /// <summary>
+ /// Gets the name of the friendly.
+ /// </summary>
+ /// <value>The name of the friendly.</value>
+ string FriendlyName { get; }
}
}
diff --git a/MediaBrowser.Controller/Library/TVUtils.cs b/MediaBrowser.Controller/Library/TVUtils.cs
index 541dfd226..d8d836597 100644
--- a/MediaBrowser.Controller/Library/TVUtils.cs
+++ b/MediaBrowser.Controller/Library/TVUtils.cs
@@ -126,7 +126,7 @@ namespace MediaBrowser.Controller.Library
{
var filename = Path.GetFileName(path);
- if (string.Equals(path, "specials", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(filename, "specials", StringComparison.OrdinalIgnoreCase))
{
return 0;
}
diff --git a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
index 6e9bcef2e..9e32fc32b 100644
--- a/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/ISubtitleEncoder.cs
@@ -13,6 +13,7 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <param name="inputFormat">The input format.</param>
/// <param name="outputFormat">The output format.</param>
/// <param name="startTimeTicks">The start time ticks.</param>
+ /// <param name="endTimeTicks">The end time ticks.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{Stream}.</returns>
Task<Stream> ConvertSubtitles(
@@ -20,6 +21,7 @@ namespace MediaBrowser.Controller.MediaEncoding
string inputFormat,
string outputFormat,
long startTimeTicks,
+ long? endTimeTicks,
CancellationToken cancellationToken);
/// <summary>
@@ -30,6 +32,7 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <param name="subtitleStreamIndex">Index of the subtitle stream.</param>
/// <param name="outputFormat">The output format.</param>
/// <param name="startTimeTicks">The start time ticks.</param>
+ /// <param name="endTimeTicks">The end time ticks.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{Stream}.</returns>
Task<Stream> GetSubtitles(string itemId,
@@ -37,6 +40,7 @@ namespace MediaBrowser.Controller.MediaEncoding
int subtitleStreamIndex,
string outputFormat,
long startTimeTicks,
+ long? endTimeTicks,
CancellationToken cancellationToken);
/// <summary>
diff --git a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
index 2923c11c5..f5939ad96 100644
--- a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
+++ b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
@@ -32,9 +32,9 @@ namespace MediaBrowser.Controller.Playlists
/// Removes from playlist.
/// </summary>
/// <param name="playlistId">The playlist identifier.</param>
- /// <param name="indeces">The indeces.</param>
+ /// <param name="entryIds">The entry ids.</param>
/// <returns>Task.</returns>
- Task RemoveFromPlaylist(string playlistId, IEnumerable<int> indeces);
+ Task RemoveFromPlaylist(string playlistId, IEnumerable<string> entryIds);
/// <summary>
/// Gets the playlists folder.
diff --git a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs
index 4eb6baeed..05d822185 100644
--- a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs
+++ b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs
@@ -183,19 +183,34 @@ namespace MediaBrowser.Dlna.ContentDirectory
//didl.SetAttribute("xmlns:sec", NS_SEC);
result.AppendChild(didl);
- var folder = (Folder)GetItemFromObjectId(id, user);
-
- var childrenResult = (await GetChildrenSorted(folder, user, sortCriteria, start, requested).ConfigureAwait(false));
+ var item = GetItemFromObjectId(id, user);
- var totalCount = childrenResult.TotalRecordCount;
+ var totalCount = 0;
if (string.Equals(flag, "BrowseMetadata"))
{
- result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, folder, totalCount, filter));
+ var folder = item as Folder;
+
+ if (folder == null)
+ {
+ result.DocumentElement.AppendChild(_didlBuilder.GetItemElement(result, item, deviceId, filter));
+ }
+ else
+ {
+ var childrenResult = (await GetChildrenSorted(folder, user, sortCriteria, start, requested).ConfigureAwait(false));
+ totalCount = childrenResult.TotalRecordCount;
+
+ result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, folder, totalCount, filter));
+ }
provided++;
}
else
{
+ var folder = (Folder)item;
+
+ var childrenResult = (await GetChildrenSorted(folder, user, sortCriteria, start, requested).ConfigureAwait(false));
+ totalCount = childrenResult.TotalRecordCount;
+
provided = childrenResult.Items.Length;
foreach (var i in childrenResult.Items)
diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs
index ec86f69e7..a5a97567a 100644
--- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs
+++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Common.Net;
+using System.IO;
+using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
@@ -13,6 +14,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml;
+using MediaBrowser.Common.Extensions;
namespace MediaBrowser.Dlna.Didl
{
@@ -101,7 +103,7 @@ namespace MediaBrowser.Dlna.Didl
{
var sources = _user == null ? video.GetMediaSources(true).ToList() : video.GetMediaSources(true, _user).ToList();
- streamInfo = new StreamBuilder().BuildVideoItem(new VideoOptions
+ streamInfo = new StreamBuilder().BuildVideoItem(new VideoOptions
{
ItemId = video.Id.ToString("N"),
MediaSources = sources,
@@ -137,6 +139,23 @@ namespace MediaBrowser.Dlna.Didl
{
AddVideoResource(container, video, deviceId, filter, contentFeature, streamInfo);
}
+
+ foreach (var subtitle in streamInfo.GetExternalSubtitles(_serverAddress))
+ {
+ AddSubtitleElement(container, subtitle);
+ }
+ }
+
+ private void AddSubtitleElement(XmlElement container, SubtitleStreamInfo info)
+ {
+ var res = container.OwnerDocument.CreateElement(string.Empty, "res", NS_DIDL);
+
+ res.InnerText = info.Url;
+
+ // TODO: Remove this hard-coding
+ res.SetAttribute("protocolInfo", "http-get:*:text/srt:*");
+
+ container.AppendChild(res);
}
private void AddVideoResource(XmlElement container, Video video, string deviceId, Filter filter, string contentFeatures, StreamInfo streamInfo)
@@ -598,9 +617,11 @@ namespace MediaBrowser.Dlna.Didl
}
AddImageResElement(item, element, 4096, 4096, "jpg");
+ AddImageResElement(item, element, 4096, 4096, "png");
AddImageResElement(item, element, 1024, 768, "jpg");
AddImageResElement(item, element, 640, 480, "jpg");
AddImageResElement(item, element, 160, 160, "jpg");
+ AddImageResElement(item, element, 160, 160, "png");
}
private void AddImageResElement(BaseItem item, XmlElement element, int maxWidth, int maxHeight, string format)
@@ -623,7 +644,7 @@ namespace MediaBrowser.Dlna.Didl
var width = albumartUrlInfo.Width;
var height = albumartUrlInfo.Height;
- var contentFeatures = new ContentFeatureBuilder(_profile).BuildImageHeader(format, width, height);
+ var contentFeatures = new ContentFeatureBuilder(_profile).BuildImageHeader(format, width, height, imageInfo.IsDirectStream);
res.SetAttribute("protocolInfo", String.Format(
"http-get:*:{0}:{1}",
@@ -631,6 +652,14 @@ namespace MediaBrowser.Dlna.Didl
contentFeatures
));
+ res.SetAttribute("colorDepth", "24");
+
+ if (imageInfo.IsDirectStream)
+ {
+ // TODO: Add file size
+ //res.SetAttribute("size", imageInfo.Size.Value.ToString(_usCulture));
+ }
+
if (width.HasValue && height.HasValue)
{
res.SetAttribute("resolution", string.Format("{0}x{1}", width.Value, height.Value));
@@ -705,7 +734,8 @@ namespace MediaBrowser.Dlna.Didl
Type = type,
ImageTag = tag,
Width = width,
- Height = height
+ Height = height,
+ File = imageInfo.Path
};
}
@@ -717,6 +747,10 @@ namespace MediaBrowser.Dlna.Didl
internal int? Width;
internal int? Height;
+
+ internal bool IsDirectStream;
+
+ internal string File;
}
class ImageUrlInfo
@@ -741,6 +775,8 @@ namespace MediaBrowser.Dlna.Didl
var width = info.Width;
var height = info.Height;
+ info.IsDirectStream = false;
+
if (width.HasValue && height.HasValue)
{
var newSize = DrawingUtils.Resize(new ImageSize
@@ -752,6 +788,18 @@ namespace MediaBrowser.Dlna.Didl
width = Convert.ToInt32(newSize.Width);
height = Convert.ToInt32(newSize.Height);
+
+ var inputFormat = (Path.GetExtension(info.File) ?? string.Empty)
+ .TrimStart('.')
+ .Replace("jpeg", "jpg", StringComparison.OrdinalIgnoreCase);
+
+ var normalizedFormat = format
+ .Replace("jpeg", "jpg", StringComparison.OrdinalIgnoreCase);
+
+ if (string.Equals(inputFormat, normalizedFormat, StringComparison.OrdinalIgnoreCase))
+ {
+ info.IsDirectStream = maxWidth >= width.Value && maxHeight >= height.Value;
+ }
}
return new ImageUrlInfo
diff --git a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
index 796ccb004..83d7f322d 100644
--- a/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
+++ b/MediaBrowser.Dlna/PlayTo/PlaylistItemFactory.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Dlna;
+using MediaBrowser.Model.Session;
using System;
using System.Globalization;
using System.IO;
@@ -29,7 +30,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (directPlay != null)
{
- playlistItem.StreamInfo.IsDirectStream = true;
+ playlistItem.StreamInfo.PlayMethod = PlayMethod.DirectStream;
playlistItem.StreamInfo.Container = Path.GetExtension(item.Path);
return playlistItem;
@@ -40,7 +41,7 @@ namespace MediaBrowser.Dlna.PlayTo
if (transcodingProfile != null)
{
- playlistItem.StreamInfo.IsDirectStream = true;
+ playlistItem.StreamInfo.PlayMethod = PlayMethod.Transcode;
playlistItem.StreamInfo.Container = "." + transcodingProfile.Container.TrimStart('.');
}
diff --git a/MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs b/MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs
index d12b3598c..533f4ecf4 100644
--- a/MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs
+++ b/MediaBrowser.Dlna/Profiles/PanasonicVieraProfile.cs
@@ -193,11 +193,12 @@ namespace MediaBrowser.Dlna.Profiles
}
};
- SoftSubtitleProfiles = new[]
+ SubtitleProfiles = new[]
{
new SubtitleProfile
{
- Format = "srt"
+ Format = "srt",
+ Method = SubtitleDeliveryMethod.External
}
};
}
diff --git a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs b/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs
index b90c906fb..3dc1967dd 100644
--- a/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs
+++ b/MediaBrowser.Dlna/Profiles/SamsungSmartTvProfile.cs
@@ -339,11 +339,12 @@ namespace MediaBrowser.Dlna.Profiles
}
};
- SoftSubtitleProfiles = new[]
+ SubtitleProfiles = new[]
{
new SubtitleProfile
{
- Format = "smi"
+ Format = "smi",
+ Method = SubtitleDeliveryMethod.External
}
};
}
diff --git a/MediaBrowser.Dlna/Profiles/Windows81Profile.cs b/MediaBrowser.Dlna/Profiles/Windows81Profile.cs
index 921019f8e..20d0834e1 100644
--- a/MediaBrowser.Dlna/Profiles/Windows81Profile.cs
+++ b/MediaBrowser.Dlna/Profiles/Windows81Profile.cs
@@ -155,6 +155,14 @@ namespace MediaBrowser.Dlna.Profiles
}
};
+ SubtitleProfiles = new[]
+ {
+ new SubtitleProfile
+ {
+ Format = "ttml",
+ Method = SubtitleDeliveryMethod.External
+ }
+ };
}
}
}
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Android.xml b/MediaBrowser.Dlna/Profiles/Xml/Android.xml
index b7a6cc19c..efab21021 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Android.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Android.xml
@@ -65,6 +65,4 @@
</CodecProfile>
</CodecProfiles>
<ResponseProfiles />
- <SoftSubtitleProfiles />
- <ExternalSubtitleProfiles />
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Default.xml b/MediaBrowser.Dlna/Profiles/Xml/Default.xml
index 6aafbe86e..164ea943d 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Default.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Default.xml
@@ -35,6 +35,4 @@
<ContainerProfiles />
<CodecProfiles />
<ResponseProfiles />
- <SoftSubtitleProfiles />
- <ExternalSubtitleProfiles />
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml b/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml
index 28fe6e0c9..9a4322b68 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml
@@ -39,6 +39,4 @@
<ContainerProfiles />
<CodecProfiles />
<ResponseProfiles />
- <SoftSubtitleProfiles />
- <ExternalSubtitleProfiles />
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml b/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml
index f0cf1e96c..ee18a5efa 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml
@@ -73,6 +73,4 @@
</CodecProfile>
</CodecProfiles>
<ResponseProfiles />
- <SoftSubtitleProfiles />
- <ExternalSubtitleProfiles />
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml b/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml
index 775c7e466..43516a378 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml
@@ -39,6 +39,4 @@
<ContainerProfiles />
<CodecProfiles />
<ResponseProfiles />
- <SoftSubtitleProfiles />
- <ExternalSubtitleProfiles />
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml b/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml
index 1461c2255..1d63d3885 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml
@@ -45,6 +45,4 @@
<ContainerProfiles />
<CodecProfiles />
<ResponseProfiles />
- <SoftSubtitleProfiles />
- <ExternalSubtitleProfiles />
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml b/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml
index 5b5125b30..2ae5524fa 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml
@@ -68,10 +68,7 @@
</CodecProfile>
</CodecProfiles>
<ResponseProfiles />
- <SoftSubtitleProfiles>
- <SubtitleProfile>
- <Format>srt</Format>
- </SubtitleProfile>
- </SoftSubtitleProfiles>
- <ExternalSubtitleProfiles />
+ <SubtitleProfiles>
+ <SubtitleProfile format="srt" method="External" />
+ </SubtitleProfiles>
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml b/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml
index 209c029b5..b8edd8ff5 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml
@@ -106,10 +106,7 @@
<Conditions />
</ResponseProfile>
</ResponseProfiles>
- <SoftSubtitleProfiles>
- <SubtitleProfile>
- <Format>smi</Format>
- </SubtitleProfile>
- </SoftSubtitleProfiles>
- <ExternalSubtitleProfiles />
+ <SubtitleProfiles>
+ <SubtitleProfile format="smi" method="External" />
+ </SubtitleProfiles>
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
index 2e32b77c6..c3c13cdc3 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
@@ -71,6 +71,4 @@
</CodecProfile>
</CodecProfiles>
<ResponseProfiles />
- <SoftSubtitleProfiles />
- <ExternalSubtitleProfiles />
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
index 87ba6e33b..d48fc34b1 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
@@ -99,6 +99,4 @@
<Conditions />
</ResponseProfile>
</ResponseProfiles>
- <SoftSubtitleProfiles />
- <ExternalSubtitleProfiles />
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml
index 698bb44b1..319936e0a 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml
@@ -107,6 +107,4 @@
<Conditions />
</ResponseProfile>
</ResponseProfiles>
- <SoftSubtitleProfiles />
- <ExternalSubtitleProfiles />
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml
index f07536fcb..541ecf828 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml
@@ -110,6 +110,4 @@
<Conditions />
</ResponseProfile>
</ResponseProfiles>
- <SoftSubtitleProfiles />
- <ExternalSubtitleProfiles />
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml
index a99e4fa1e..a99dc6b4e 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml
@@ -93,6 +93,4 @@
<Conditions />
</ResponseProfile>
</ResponseProfiles>
- <SoftSubtitleProfiles />
- <ExternalSubtitleProfiles />
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml
index 3d4661621..40e55e98a 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml
@@ -93,6 +93,4 @@
<Conditions />
</ResponseProfile>
</ResponseProfiles>
- <SoftSubtitleProfiles />
- <ExternalSubtitleProfiles />
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml
index 55f89e3eb..4499b905d 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml
@@ -93,6 +93,4 @@
<Conditions />
</ResponseProfile>
</ResponseProfiles>
- <SoftSubtitleProfiles />
- <ExternalSubtitleProfiles />
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml
index 5d12b65c8..4e0231a53 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml
@@ -78,6 +78,4 @@
<Conditions />
</ResponseProfile>
</ResponseProfiles>
- <SoftSubtitleProfiles />
- <ExternalSubtitleProfiles />
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Windows 8 RT.xml b/MediaBrowser.Dlna/Profiles/Xml/Windows 8 RT.xml
index 61ee59549..dec3d02ef 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Windows 8 RT.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Windows 8 RT.xml
@@ -62,6 +62,7 @@
</CodecProfile>
</CodecProfiles>
<ResponseProfiles />
- <SoftSubtitleProfiles />
- <ExternalSubtitleProfiles />
+ <SubtitleProfiles>
+ <SubtitleProfile format="ttml" method="External" />
+ </SubtitleProfiles>
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Windows Phone.xml b/MediaBrowser.Dlna/Profiles/Xml/Windows Phone.xml
index 12b7fe9c9..7c3414b25 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Windows Phone.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Windows Phone.xml
@@ -76,6 +76,4 @@
</CodecProfile>
</CodecProfiles>
<ResponseProfiles />
- <SoftSubtitleProfiles />
- <ExternalSubtitleProfiles />
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml b/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml
index f3e153130..b629fa518 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml
@@ -100,6 +100,4 @@
<Conditions />
</ResponseProfile>
</ResponseProfiles>
- <SoftSubtitleProfiles />
- <ExternalSubtitleProfiles />
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml b/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml
index 7e057a3f9..5f86a94a9 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml
@@ -90,6 +90,4 @@
<Conditions />
</ResponseProfile>
</ResponseProfiles>
- <SoftSubtitleProfiles />
- <ExternalSubtitleProfiles />
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml b/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml
index d09911308..40ad2bc78 100644
--- a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml
+++ b/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml
@@ -45,6 +45,4 @@
<ContainerProfiles />
<CodecProfiles />
<ResponseProfiles />
- <SoftSubtitleProfiles />
- <ExternalSubtitleProfiles />
</Profile> \ No newline at end of file
diff --git a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs
index 83bc6a49e..c7f974200 100644
--- a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs
@@ -18,6 +18,20 @@ namespace MediaBrowser.LocalMetadata.Parsers
{
switch (reader.Name)
{
+ case "OwnerUserId":
+ {
+ item.OwnerUserId = reader.ReadElementContentAsString();
+
+ break;
+ }
+
+ case "PlaylistMediaType":
+ {
+ item.PlaylistMediaType = reader.ReadElementContentAsString();
+
+ break;
+ }
+
case "PlaylistItems":
using (var subReader = reader.ReadSubtree())
diff --git a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs
index abc7e3b3f..1541c2176 100644
--- a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Entities;
+using System.Security;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Playlists;
using System.Collections.Generic;
@@ -42,17 +43,34 @@ namespace MediaBrowser.LocalMetadata.Savers
/// <returns>Task.</returns>
public void Save(IHasMetadata item, CancellationToken cancellationToken)
{
+ var playlist = (Playlist)item;
+
var builder = new StringBuilder();
builder.Append("<Item>");
- XmlSaverHelpers.AddCommonNodes((Playlist)item, builder);
+ if (!string.IsNullOrEmpty(playlist.OwnerUserId))
+ {
+ builder.Append("<OwnerUserId>" + SecurityElement.Escape(playlist.OwnerUserId) + "</OwnerUserId>");
+ }
+
+ if (!string.IsNullOrEmpty(playlist.PlaylistMediaType))
+ {
+ builder.Append("<PlaylistMediaType>" + SecurityElement.Escape(playlist.PlaylistMediaType) + "</PlaylistMediaType>");
+ }
+
+ XmlSaverHelpers.AddCommonNodes(playlist, builder);
builder.Append("</Item>");
var xmlFilePath = GetSavePath(item);
- XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> { });
+ XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
+ {
+ "OwnerUserId",
+ "PlaylistMediaType"
+
+ });
}
/// <summary>
diff --git a/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs b/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs
index a007a95cf..0801b7358 100644
--- a/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs
+++ b/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs
@@ -704,7 +704,7 @@ namespace MediaBrowser.LocalMetadata.Savers
public static void AddLinkedChildren(Folder item, StringBuilder builder, string pluralNodeName, string singularNodeName)
{
var items = item.LinkedChildren
- .Where(i => i.Type == LinkedChildType.Manual && !string.IsNullOrWhiteSpace(i.ItemName))
+ .Where(i => i.Type == LinkedChildType.Manual)
.ToList();
if (items.Count == 0)
@@ -717,14 +717,20 @@ namespace MediaBrowser.LocalMetadata.Savers
{
builder.Append("<" + singularNodeName + ">");
- builder.Append("<Type>" + SecurityElement.Escape(link.ItemType) + "</Type>");
+ if (!string.IsNullOrWhiteSpace(link.ItemType))
+ {
+ builder.Append("<Type>" + SecurityElement.Escape(link.ItemType) + "</Type>");
+ }
if (link.ItemYear.HasValue)
{
builder.Append("<Year>" + SecurityElement.Escape(link.ItemYear.Value.ToString(UsCulture)) + "</Year>");
}
- builder.Append("<Path>" + SecurityElement.Escape((link.Path ?? string.Empty)) + "</Path>");
+ if (!string.IsNullOrWhiteSpace(link.Path))
+ {
+ builder.Append("<Path>" + SecurityElement.Escape((link.Path)) + "</Path>");
+ }
builder.Append("</" + singularNodeName + ">");
}
diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
index 46c680730..aef99f0d1 100644
--- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
+++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
@@ -66,6 +66,7 @@
<Compile Include="Subtitles\SsaParser.cs" />
<Compile Include="Subtitles\SubtitleEncoder.cs" />
<Compile Include="Subtitles\SubtitleTrackInfo.cs" />
+ <Compile Include="Subtitles\TtmlWriter.cs" />
<Compile Include="Subtitles\VttWriter.cs" />
</ItemGroup>
<ItemGroup>
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index ab9cd546a..82e331dd8 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -48,6 +48,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
string inputFormat,
string outputFormat,
long startTimeTicks,
+ long? endTimeTicks,
CancellationToken cancellationToken)
{
var ms = new MemoryStream();
@@ -56,6 +57,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
// Return the original without any conversions, if possible
if (startTimeTicks == 0 &&
+ !endTimeTicks.HasValue &&
string.Equals(inputFormat, outputFormat, StringComparison.OrdinalIgnoreCase))
{
await stream.CopyToAsync(ms, 81920, cancellationToken).ConfigureAwait(false);
@@ -64,7 +66,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
var trackInfo = await GetTrackInfo(stream, inputFormat, cancellationToken).ConfigureAwait(false);
- UpdateStartingPosition(trackInfo, startTimeTicks);
+ FilterEvents(trackInfo, startTimeTicks, endTimeTicks, false);
var writer = GetWriter(outputFormat);
@@ -81,19 +83,30 @@ namespace MediaBrowser.MediaEncoding.Subtitles
return ms;
}
- private void UpdateStartingPosition(SubtitleTrackInfo track, long startPositionTicks)
+ private void FilterEvents(SubtitleTrackInfo track, long startPositionTicks, long? endTimeTicks, bool preserveTimestamps)
{
- if (startPositionTicks == 0) return;
+ // Drop subs that are earlier than what we're looking for
+ track.TrackEvents = track.TrackEvents
+ .SkipWhile(i => (i.StartPositionTicks - startPositionTicks) < 0 || (i.EndPositionTicks - startPositionTicks) < 0)
+ .ToList();
- foreach (var trackEvent in track.TrackEvents)
+ if (endTimeTicks.HasValue)
{
- trackEvent.EndPositionTicks -= startPositionTicks;
- trackEvent.StartPositionTicks -= startPositionTicks;
+ var endTime = endTimeTicks.Value;
+
+ track.TrackEvents = track.TrackEvents
+ .TakeWhile(i => i.StartPositionTicks <= endTime)
+ .ToList();
}
- track.TrackEvents = track.TrackEvents
- .SkipWhile(i => i.StartPositionTicks < 0 || i.EndPositionTicks < 0)
- .ToList();
+ if (!preserveTimestamps)
+ {
+ foreach (var trackEvent in track.TrackEvents)
+ {
+ trackEvent.EndPositionTicks -= startPositionTicks;
+ trackEvent.StartPositionTicks -= startPositionTicks;
+ }
+ }
}
public async Task<Stream> GetSubtitles(string itemId,
@@ -101,6 +114,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
int subtitleStreamIndex,
string outputFormat,
long startTimeTicks,
+ long? endTimeTicks,
CancellationToken cancellationToken)
{
var subtitle = await GetSubtitleStream(itemId, mediaSourceId, subtitleStreamIndex, cancellationToken)
@@ -110,7 +124,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
var inputFormat = subtitle.Item2;
- return await ConvertSubtitles(stream, inputFormat, outputFormat, startTimeTicks, cancellationToken).ConfigureAwait(false);
+ return await ConvertSubtitles(stream, inputFormat, outputFormat, startTimeTicks, endTimeTicks, cancellationToken).ConfigureAwait(false);
}
}
@@ -254,6 +268,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
return new VttWriter();
}
+ if (string.Equals(format, SubtitleFormat.TTML, StringComparison.OrdinalIgnoreCase))
+ {
+ return new TtmlWriter();
+ }
throw new ArgumentException("Unsupported format: " + format);
}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs
new file mode 100644
index 000000000..a937175f0
--- /dev/null
+++ b/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs
@@ -0,0 +1,59 @@
+using System;
+using System.IO;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading;
+
+namespace MediaBrowser.MediaEncoding.Subtitles
+{
+ public class TtmlWriter : ISubtitleWriter
+ {
+ public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
+ {
+ // Example: https://github.com/zmalltalker/ttml2vtt/blob/master/data/sample.xml
+ // Parser example: https://github.com/mozilla/popcorn-js/blob/master/parsers/parserTTML/popcorn.parserTTML.js
+
+ using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
+ {
+ writer.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
+ writer.WriteLine("<tt xmlns=\"http://www.w3.org/ns/ttml\" xmlns:tts=\"http://www.w3.org/2006/04/ttaf1#styling\" lang=\"no\">");
+
+ writer.WriteLine("<head>");
+ writer.WriteLine("<styling>");
+ writer.WriteLine("<style id=\"italic\" tts:fontStyle=\"italic\" />");
+ writer.WriteLine("<style id=\"left\" tts:textAlign=\"left\" />");
+ writer.WriteLine("<style id=\"center\" tts:textAlign=\"center\" />");
+ writer.WriteLine("<style id=\"right\" tts:textAlign=\"right\" />");
+ writer.WriteLine("</styling>");
+ writer.WriteLine("</head>");
+
+ writer.WriteLine("<body>");
+ writer.WriteLine("<div>");
+
+ foreach (var trackEvent in info.TrackEvents)
+ {
+ var text = trackEvent.Text;
+
+ text = Regex.Replace(text, @"\\N", "<br/>", RegexOptions.IgnoreCase);
+
+ writer.WriteLine("<p begin=\"{0}\" dur=\"{1}\">{2}</p>",
+ trackEvent.StartPositionTicks,
+ (trackEvent.EndPositionTicks - trackEvent.StartPositionTicks),
+ text);
+ }
+
+ writer.WriteLine("</div>");
+ writer.WriteLine("</body>");
+
+ writer.WriteLine("</tt>");
+ }
+ }
+
+ private string FormatTime(long ticks)
+ {
+ var time = TimeSpan.FromTicks(ticks);
+
+ return string.Format(@"{0:hh\:mm\:ss\,fff}", time);
+ }
+ }
+}
diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
index 5417c5b82..c25e93c38 100644
--- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
+++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
@@ -14,12 +14,13 @@ namespace MediaBrowser.Model.Dlna
public string BuildImageHeader(string container,
int? width,
- int? height)
+ int? height,
+ bool isDirectStream)
{
string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetImageOrgOpValue();
// 0 = native, 1 = transcoded
- const string orgCi = ";DLNA.ORG_CI=0";
+ var orgCi = isDirectStream ? ";DLNA.ORG_CI=0" : ";DLNA.ORG_CI=1";
DlnaFlags flagValue = DlnaFlags.StreamingTransferMode |
DlnaFlags.BackgroundTransferMode |
diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs
index 630e7fa60..5366a538c 100644
--- a/MediaBrowser.Model/Dlna/DeviceProfile.cs
+++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs
@@ -90,8 +90,7 @@ namespace MediaBrowser.Model.Dlna
public CodecProfile[] CodecProfiles { get; set; }
public ResponseProfile[] ResponseProfiles { get; set; }
- public SubtitleProfile[] SoftSubtitleProfiles { get; set; }
- public SubtitleProfile[] ExternalSubtitleProfiles { get; set; }
+ public SubtitleProfile[] SubtitleProfiles { get; set; }
public DeviceProfile()
{
@@ -100,9 +99,6 @@ namespace MediaBrowser.Model.Dlna
ResponseProfiles = new ResponseProfile[] { };
CodecProfiles = new CodecProfile[] { };
ContainerProfiles = new ContainerProfile[] { };
-
- SoftSubtitleProfiles = new SubtitleProfile[] { };
- ExternalSubtitleProfiles = new SubtitleProfile[] { };
XmlRootAttributes = new XmlAttribute[] { };
diff --git a/MediaBrowser.Model/Dlna/DlnaMaps.cs b/MediaBrowser.Model/Dlna/DlnaMaps.cs
index 036c1fc74..9a84eb195 100644
--- a/MediaBrowser.Model/Dlna/DlnaMaps.cs
+++ b/MediaBrowser.Model/Dlna/DlnaMaps.cs
@@ -48,7 +48,7 @@
orgOp += "0";
// Byte-based seeking only possible when not transcoding
- orgOp += "1";
+ orgOp += "0";
return orgOp;
}
diff --git a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
index 3b4cc30ac..5101bbe5a 100644
--- a/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
+++ b/MediaBrowser.Model/Dlna/MediaFormatProfileResolver.cs
@@ -385,7 +385,7 @@ namespace MediaBrowser.Model.Dlna
return ResolveImageJPGFormat(width, height);
if (StringHelper.EqualsIgnoreCase(container, "png"))
- return MediaFormatProfile.PNG_LRG;
+ return ResolveImagePNGFormat(width, height);
if (StringHelper.EqualsIgnoreCase(container, "gif"))
return MediaFormatProfile.GIF_LRG;
@@ -401,7 +401,7 @@ namespace MediaBrowser.Model.Dlna
if (width.HasValue && height.HasValue)
{
if ((width.Value <= 160) && (height.Value <= 160))
- return MediaFormatProfile.JPEG_SM;
+ return MediaFormatProfile.JPEG_TN;
if ((width.Value <= 640) && (height.Value <= 480))
return MediaFormatProfile.JPEG_SM;
@@ -416,5 +416,16 @@ namespace MediaBrowser.Model.Dlna
return MediaFormatProfile.JPEG_SM;
}
+
+ private MediaFormatProfile ResolveImagePNGFormat(int? width, int? height)
+ {
+ if (width.HasValue && height.HasValue)
+ {
+ if ((width.Value <= 160) && (height.Value <= 160))
+ return MediaFormatProfile.PNG_TN;
+ }
+
+ return MediaFormatProfile.PNG_LRG;
+ }
}
}
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index 6e929564e..72bf88b70 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.MediaInfo;
+using MediaBrowser.Model.Session;
using System;
using System.Collections.Generic;
@@ -9,7 +10,7 @@ namespace MediaBrowser.Model.Dlna
{
public class StreamBuilder
{
- private string[] _serverTextSubtitleOutputs = new string[] { "srt", "vtt" };
+ private readonly string[] _serverTextSubtitleOutputs = { "srt", "vtt", "ttml" };
public StreamInfo BuildAudioItem(AudioOptions options)
{
@@ -158,7 +159,7 @@ namespace MediaBrowser.Model.Dlna
if (all)
{
- playlistItem.IsDirectStream = true;
+ playlistItem.PlayMethod = PlayMethod.DirectStream;
playlistItem.Container = item.Container;
return playlistItem;
@@ -179,7 +180,7 @@ namespace MediaBrowser.Model.Dlna
if (transcodingProfile != null)
{
- playlistItem.IsDirectStream = false;
+ playlistItem.PlayMethod = PlayMethod.Transcode;
playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
playlistItem.EstimateContentLength = transcodingProfile.EstimateContentLength;
playlistItem.Container = transcodingProfile.Container;
@@ -252,12 +253,12 @@ namespace MediaBrowser.Model.Dlna
if (directPlay != null)
{
- playlistItem.IsDirectStream = true;
+ playlistItem.PlayMethod = PlayMethod.DirectStream;
playlistItem.Container = item.Container;
if (subtitleStream != null)
{
- playlistItem.SubtitleDeliveryMethod = GetDirectStreamSubtitleDeliveryMethod(subtitleStream, options);
+ playlistItem.SubtitleDeliveryMethod = GetSubtitleDeliveryMethod(subtitleStream, options);
}
return playlistItem;
@@ -279,10 +280,10 @@ namespace MediaBrowser.Model.Dlna
{
if (subtitleStream != null)
{
- playlistItem.SubtitleDeliveryMethod = GetTranscodedSubtitleDeliveryMethod(subtitleStream, options);
+ playlistItem.SubtitleDeliveryMethod = GetSubtitleDeliveryMethod(subtitleStream, options);
}
- playlistItem.IsDirectStream = false;
+ playlistItem.PlayMethod = PlayMethod.Transcode;
playlistItem.Container = transcodingProfile.Container;
playlistItem.EstimateContentLength = transcodingProfile.EstimateContentLength;
playlistItem.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
@@ -499,9 +500,9 @@ namespace MediaBrowser.Model.Dlna
return false;
}
- SubtitleDeliveryMethod subtitleMethod = GetDirectStreamSubtitleDeliveryMethod(subtitleStream, options);
+ SubtitleDeliveryMethod subtitleMethod = GetSubtitleDeliveryMethod(subtitleStream, options);
- if (subtitleMethod != SubtitleDeliveryMethod.External && subtitleMethod != SubtitleDeliveryMethod.Direct)
+ if (subtitleMethod != SubtitleDeliveryMethod.External && subtitleMethod != SubtitleDeliveryMethod.Embed)
{
return false;
}
@@ -510,41 +511,14 @@ namespace MediaBrowser.Model.Dlna
return IsAudioEligibleForDirectPlay(item, maxBitrate);
}
- private SubtitleDeliveryMethod GetDirectStreamSubtitleDeliveryMethod(MediaStream subtitleStream,
- VideoOptions options)
- {
- if (subtitleStream.IsTextSubtitleStream)
- {
- string subtitleFormat = NormalizeSubtitleFormat(subtitleStream.Codec);
-
- bool supportsDirect = ContainsSubtitleFormat(options.Profile.SoftSubtitleProfiles, new[] { subtitleFormat });
-
- if (supportsDirect)
- {
- return SubtitleDeliveryMethod.Direct;
- }
-
- // See if the device can retrieve the subtitles externally
- bool supportsSubsExternally = options.Context == EncodingContext.Streaming &&
- ContainsSubtitleFormat(options.Profile.ExternalSubtitleProfiles, _serverTextSubtitleOutputs);
-
- if (supportsSubsExternally)
- {
- return SubtitleDeliveryMethod.External;
- }
- }
-
- return SubtitleDeliveryMethod.Encode;
- }
-
- private SubtitleDeliveryMethod GetTranscodedSubtitleDeliveryMethod(MediaStream subtitleStream,
+ private SubtitleDeliveryMethod GetSubtitleDeliveryMethod(MediaStream subtitleStream,
VideoOptions options)
{
if (subtitleStream.IsTextSubtitleStream)
{
// See if the device can retrieve the subtitles externally
bool supportsSubsExternally = options.Context == EncodingContext.Streaming &&
- ContainsSubtitleFormat(options.Profile.ExternalSubtitleProfiles, _serverTextSubtitleOutputs);
+ ContainsSubtitleFormat(options.Profile.SubtitleProfiles, SubtitleDeliveryMethod.External, _serverTextSubtitleOutputs);
if (supportsSubsExternally)
{
@@ -552,7 +526,7 @@ namespace MediaBrowser.Model.Dlna
}
// See if the device can retrieve the subtitles externally
- bool supportsEmbedded = ContainsSubtitleFormat(options.Profile.SoftSubtitleProfiles, _serverTextSubtitleOutputs);
+ bool supportsEmbedded = ContainsSubtitleFormat(options.Profile.SubtitleProfiles, SubtitleDeliveryMethod.Embed, _serverTextSubtitleOutputs);
if (supportsEmbedded)
{
@@ -573,11 +547,11 @@ namespace MediaBrowser.Model.Dlna
return codec;
}
- private bool ContainsSubtitleFormat(SubtitleProfile[] profiles, string[] formats)
+ private bool ContainsSubtitleFormat(SubtitleProfile[] profiles, SubtitleDeliveryMethod method, string[] formats)
{
foreach (SubtitleProfile profile in profiles)
{
- if (ListHelper.ContainsIgnoreCase(formats, profile.Format))
+ if (method == profile.Method && ListHelper.ContainsIgnoreCase(formats, profile.Format))
{
return true;
}
diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs
index 8bf9f2a04..563a2f19a 100644
--- a/MediaBrowser.Model/Dlna/StreamInfo.cs
+++ b/MediaBrowser.Model/Dlna/StreamInfo.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.MediaInfo;
+using MediaBrowser.Model.Session;
using System;
using System.Collections.Generic;
@@ -15,7 +16,7 @@ namespace MediaBrowser.Model.Dlna
{
public string ItemId { get; set; }
- public bool IsDirectStream { get; set; }
+ public PlayMethod PlayMethod { get; set; }
public DlnaProfileType MediaType { get; set; }
@@ -59,6 +60,7 @@ namespace MediaBrowser.Model.Dlna
public MediaSourceInfo MediaSource { get; set; }
public SubtitleDeliveryMethod SubtitleDeliveryMethod { get; set; }
+ public string SubtitleFormat { get; set; }
public string MediaSourceId
{
@@ -68,6 +70,11 @@ namespace MediaBrowser.Model.Dlna
}
}
+ public bool IsDirectStream
+ {
+ get { return PlayMethod == PlayMethod.DirectStream; }
+ }
+
public string ToUrl(string baseUrl)
{
return ToDlnaUrl(baseUrl);
@@ -124,6 +131,55 @@ namespace MediaBrowser.Model.Dlna
return string.Format("Params={0}", string.Join(";", list.ToArray()));
}
+ public List<SubtitleStreamInfo> GetExternalSubtitles(string baseUrl)
+ {
+ if (string.IsNullOrEmpty(baseUrl))
+ {
+ throw new ArgumentNullException(baseUrl);
+ }
+
+ List<SubtitleStreamInfo> list = new List<SubtitleStreamInfo>();
+
+ if (SubtitleDeliveryMethod != SubtitleDeliveryMethod.External)
+ {
+ return list;
+ }
+
+ if (!SubtitleStreamIndex.HasValue)
+ {
+ return list;
+ }
+
+ // HLS will preserve timestamps so we can just grab the full subtitle stream
+ long startPositionTicks = StringHelper.EqualsIgnoreCase(Protocol, "hls")
+ ? 0
+ : StartPositionTicks;
+
+ string url = string.Format("{0}/Videos/{1}/{2}/Subtitles/{3}/{4}/Stream.{5}",
+ baseUrl,
+ ItemId,
+ MediaSourceId,
+ StringHelper.ToStringCultureInvariant(SubtitleStreamIndex.Value),
+ StringHelper.ToStringCultureInvariant(startPositionTicks),
+ SubtitleFormat);
+
+ foreach (MediaStream stream in MediaSource.MediaStreams)
+ {
+ if (stream.Type == MediaStreamType.Subtitle && stream.Index == SubtitleStreamIndex.Value)
+ {
+ list.Add(new SubtitleStreamInfo
+ {
+ Url = url,
+ IsForced = stream.IsForced,
+ Language = stream.Language,
+ Name = stream.Language ?? "Unknown"
+ });
+ }
+ }
+
+ return list;
+ }
+
/// <summary>
/// Returns the audio stream that will be used
/// </summary>
@@ -137,7 +193,7 @@ namespace MediaBrowser.Model.Dlna
{
foreach (MediaStream i in MediaSource.MediaStreams)
{
- if (i.Index == AudioStreamIndex.Value && i.Type == MediaStreamType.Audio)
+ if (i.Index == AudioStreamIndex.Value && i.Type == MediaStreamType.Audio)
return i;
}
return null;
@@ -437,16 +493,24 @@ namespace MediaBrowser.Model.Dlna
/// </summary>
Encode = 0,
/// <summary>
- /// Internal format is supported natively
- /// </summary>
- Direct = 1,
- /// <summary>
/// The embed
/// </summary>
- Embed = 2,
+ Embed = 1,
/// <summary>
/// The external
/// </summary>
- External = 3
+ External = 2,
+ /// <summary>
+ /// The HLS
+ /// </summary>
+ Hls = 3
+ }
+
+ public class SubtitleStreamInfo
+ {
+ public string Url { get; set; }
+ public string Language { get; set; }
+ public string Name { get; set; }
+ public bool IsForced { get; set; }
}
}
diff --git a/MediaBrowser.Model/Dlna/SubtitleProfile.cs b/MediaBrowser.Model/Dlna/SubtitleProfile.cs
index ec38d96ae..189515835 100644
--- a/MediaBrowser.Model/Dlna/SubtitleProfile.cs
+++ b/MediaBrowser.Model/Dlna/SubtitleProfile.cs
@@ -1,8 +1,16 @@
-
+using System.Xml.Serialization;
+
namespace MediaBrowser.Model.Dlna
{
public class SubtitleProfile
{
+ [XmlAttribute("format")]
public string Format { get; set; }
+
+ [XmlAttribute("protocol")]
+ public string Protocol { get; set; }
+
+ [XmlAttribute("method")]
+ public SubtitleDeliveryMethod Method { get; set; }
}
-}
+} \ No newline at end of file
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index 9581b5740..32bf4b816 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -526,6 +526,12 @@ namespace MediaBrowser.Model.Dto
}
/// <summary>
+ /// Gets or sets a value indicating whether [supports playlists].
+ /// </summary>
+ /// <value><c>true</c> if [supports playlists]; otherwise, <c>false</c>.</value>
+ public bool SupportsPlaylists { get; set; }
+
+ /// <summary>
/// Determines whether the specified type is type.
/// </summary>
/// <param name="type">The type.</param>
@@ -632,12 +638,6 @@ namespace MediaBrowser.Model.Dto
public string MediaType { get; set; }
/// <summary>
- /// Gets or sets the overview HTML.
- /// </summary>
- /// <value>The overview HTML.</value>
- public string OverviewHtml { get; set; }
-
- /// <summary>
/// Gets or sets the end date.
/// </summary>
/// <value>The end date.</value>
diff --git a/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs b/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
index 7e9cdf53f..60b0bb54d 100644
--- a/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
+++ b/MediaBrowser.Model/MediaInfo/SubtitleFormat.cs
@@ -8,5 +8,6 @@
public const string VTT = "vtt";
public const string SUB = "sub";
public const string SMI = "smi";
+ public const string TTML = "ttml";
}
} \ No newline at end of file
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 120e90bd2..fb0421678 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -147,6 +147,7 @@
<Compile Include="Photos\PhotoHelper.cs" />
<Compile Include="Photos\PhotoMetadataService.cs" />
<Compile Include="Photos\PhotoProvider.cs" />
+ <Compile Include="Playlists\PlaylistMetadataService.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Manager\ProviderUtils.cs" />
<Compile Include="Studios\StudiosImageProvider.cs" />
diff --git a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
new file mode 100644
index 000000000..d7615eb02
--- /dev/null
+++ b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs
@@ -0,0 +1,47 @@
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Playlists;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Providers.Manager;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Providers.Playlists
+{
+ class PlaylistMetadataService : MetadataService<Playlist, ItemLookupInfo>
+ {
+ public PlaylistMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem)
+ : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem)
+ {
+ }
+
+ /// <summary>
+ /// Merges the specified source.
+ /// </summary>
+ /// <param name="source">The source.</param>
+ /// <param name="target">The target.</param>
+ /// <param name="lockedFields">The locked fields.</param>
+ /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
+ /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
+ protected override void MergeData(Playlist source, Playlist target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+ {
+ ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+
+ if (replaceData || string.IsNullOrEmpty(target.PlaylistMediaType))
+ {
+ target.PlaylistMediaType = source.PlaylistMediaType;
+ }
+
+ if (replaceData || string.IsNullOrEmpty(target.OwnerUserId))
+ {
+ target.OwnerUserId = source.OwnerUserId;
+ }
+
+ if (mergeMetadataSettings)
+ {
+ target.LinkedChildren = source.LinkedChildren;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
index e3a386841..ace794b19 100644
--- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs
+++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
@@ -112,6 +112,8 @@ namespace MediaBrowser.Server.Implementations.Dto
var dto = new BaseItemDto();
+ dto.SupportsPlaylists = item.SupportsAddingToPlaylist;
+
if (fields.Contains(ItemFields.People))
{
AttachPeople(dto, item);
@@ -849,17 +851,7 @@ namespace MediaBrowser.Server.Implementations.Dto
if (fields.Contains(ItemFields.Overview))
{
- // TODO: Remove this after a while, since it's been moved to the providers
- if (item is MusicArtist)
- {
- var strippedOverview = string.IsNullOrEmpty(item.Overview) ? item.Overview : item.Overview.StripHtml();
-
- dto.Overview = strippedOverview;
- }
- else
- {
- dto.Overview = item.Overview;
- }
+ dto.Overview = item.Overview;
}
if (fields.Contains(ItemFields.ShortOverview))
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
index 251acb02d..ad2b50365 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -12,7 +12,6 @@ using MediaBrowser.Controller.Localization;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Sorting;
-using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
@@ -551,24 +550,28 @@ namespace MediaBrowser.Server.Implementations.LiveTv
};
}
- if (!string.IsNullOrEmpty(info.Path))
- {
- item.Path = info.Path;
- }
- else if (!string.IsNullOrEmpty(info.Url))
- {
- item.Path = info.Url;
- }
-
isNew = true;
}
item.RecordingInfo = info;
item.ServiceName = serviceName;
+ var originalPath = item.Path;
+
+ if (!string.IsNullOrEmpty(info.Path))
+ {
+ item.Path = info.Path;
+ }
+ else if (!string.IsNullOrEmpty(info.Url))
+ {
+ item.Path = info.Url;
+ }
+
+ var pathChanged = !string.Equals(originalPath, item.Path);
+
await item.RefreshMetadata(new MetadataRefreshOptions
{
- ForceSave = isNew
+ ForceSave = isNew || pathChanged
}, cancellationToken);
diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
index ad4ba7dc0..1c207b0a2 100644
--- a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
+++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
@@ -334,5 +334,6 @@
"OptionNewPlaylist": "New playlist...",
"MessageAddedToPlaylistSuccess": "Ok",
"ButtonViewSeriesRecording": "View series recording",
- "ValueOriginalAirDate": "Original air date: {0}"
+ "ValueOriginalAirDate": "Original air date: {0}",
+ "ButtonRemoveFromPlaylist": "Remove from playlist"
}
diff --git a/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs b/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs
index 79b673283..6ab306c0b 100644
--- a/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs
@@ -190,9 +190,29 @@ namespace MediaBrowser.Server.Implementations.Playlists
}, CancellationToken.None).ConfigureAwait(false);
}
- public Task RemoveFromPlaylist(string playlistId, IEnumerable<int> indeces)
+ public async Task RemoveFromPlaylist(string playlistId, IEnumerable<string> entryIds)
{
- throw new NotImplementedException();
+ var playlist = _libraryManager.GetItemById(playlistId) as Playlist;
+
+ if (playlist == null)
+ {
+ throw new ArgumentException("No Playlist exists with the supplied Id");
+ }
+
+ var children = playlist.LinkedChildren.ToList();
+
+ var idList = entryIds.ToList();
+
+ var removals = children.Where(i => idList.Contains(i.Id));
+
+ playlist.LinkedChildren = children.Except(removals)
+ .ToList();
+
+ await playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
+ await playlist.RefreshMetadata(new MetadataRefreshOptions
+ {
+ ForceSave = true
+ }, CancellationToken.None).ConfigureAwait(false);
}
public Folder GetPlaylistsFolder(string userId)
diff --git a/MediaBrowser.Server.Implementations/Udp/UdpServer.cs b/MediaBrowser.Server.Implementations/Udp/UdpServer.cs
index e4626bec6..9028b540f 100644
--- a/MediaBrowser.Server.Implementations/Udp/UdpServer.cs
+++ b/MediaBrowser.Server.Implementations/Udp/UdpServer.cs
@@ -124,7 +124,7 @@ namespace MediaBrowser.Server.Implementations.Udp
{
Address = serverAddress,
Id = _appHost.ServerId,
- Name = _appHost.Name
+ Name = _appHost.FriendlyName
};
await SendAsync(Encoding.UTF8.GetBytes(_json.SerializeToString(response)), endpoint);
diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs
index 993cc4e1a..a4ac7e0ba 100644
--- a/MediaBrowser.ServerApplication/ApplicationHost.cs
+++ b/MediaBrowser.ServerApplication/ApplicationHost.cs
@@ -1058,10 +1058,20 @@ namespace MediaBrowser.ServerApplication
SupportsAutoRunAtStartup = SupportsAutoRunAtStartup,
TranscodingTempPath = ApplicationPaths.TranscodingTempPath,
IsRunningAsService = IsRunningAsService,
- ServerName = string.IsNullOrWhiteSpace(ServerConfigurationManager.Configuration.ServerName) ? Environment.MachineName : ServerConfigurationManager.Configuration.ServerName
+ ServerName = FriendlyName
};
}
+ public string FriendlyName
+ {
+ get
+ {
+ return string.IsNullOrWhiteSpace(ServerConfigurationManager.Configuration.ServerName)
+ ? Environment.MachineName
+ : ServerConfigurationManager.Configuration.ServerName;
+ }
+ }
+
public int HttpServerPort
{
get { return ServerConfigurationManager.Configuration.HttpServerPortNumber; }
diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec
index ce34a1b30..78218d9ce 100644
--- a/Nuget/MediaBrowser.Common.Internal.nuspec
+++ b/Nuget/MediaBrowser.Common.Internal.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common.Internal</id>
- <version>3.0.422</version>
+ <version>3.0.423</version>
<title>MediaBrowser.Common.Internal</title>
<authors>Luke</authors>
<owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
<description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.422" />
+ <dependency id="MediaBrowser.Common" version="3.0.423" />
<dependency id="NLog" version="3.1.0.0" />
<dependency id="SimpleInjector" version="2.5.2" />
<dependency id="sharpcompress" version="0.10.2" />
diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec
index 5f09afa68..2a0fbc9af 100644
--- a/Nuget/MediaBrowser.Common.nuspec
+++ b/Nuget/MediaBrowser.Common.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>MediaBrowser.Common</id>
- <version>3.0.422</version>
+ <version>3.0.423</version>
<title>MediaBrowser.Common</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>
diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec
index df036cc98..2724c2088 100644
--- a/Nuget/MediaBrowser.Model.Signed.nuspec
+++ b/Nuget/MediaBrowser.Model.Signed.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>MediaBrowser.Model.Signed</id>
- <version>3.0.422</version>
+ <version>3.0.423</version>
<title>MediaBrowser.Model - Signed Edition</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>
diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec
index ee54f248b..0f48a1976 100644
--- a/Nuget/MediaBrowser.Server.Core.nuspec
+++ b/Nuget/MediaBrowser.Server.Core.nuspec
@@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>MediaBrowser.Server.Core</id>
- <version>3.0.422</version>
+ <version>3.0.423</version>
<title>Media Browser.Server.Core</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Media Browser Server.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.422" />
+ <dependency id="MediaBrowser.Common" version="3.0.423" />
</dependencies>
</metadata>
<files>