aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MediaBrowser.Api/ConfigurationService.cs33
-rw-r--r--MediaBrowser.Api/Library/LibraryService.cs2
-rw-r--r--MediaBrowser.Api/PlaylistService.cs15
-rw-r--r--MediaBrowser.Controller/Playlists/IPlaylistManager.cs3
-rw-r--r--MediaBrowser.Controller/Playlists/Playlist.cs22
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs16
-rw-r--r--MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj3
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/AssParser.cs71
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs400
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs7
-rw-r--r--MediaBrowser.Model/ApiClient/IApiClient.cs3
-rw-r--r--MediaBrowser.Model/Dto/BaseItemDto.cs8
-rw-r--r--MediaBrowser.Server.Implementations/Dto/DtoService.cs1
-rw-r--r--MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs24
-rw-r--r--MediaBrowser.Tests/MediaEncoding/Subtitles/SsaParserTests.cs2
15 files changed, 514 insertions, 96 deletions
diff --git a/MediaBrowser.Api/ConfigurationService.cs b/MediaBrowser.Api/ConfigurationService.cs
index 1e7f9d8b0..7b6e5ed19 100644
--- a/MediaBrowser.Api/ConfigurationService.cs
+++ b/MediaBrowser.Api/ConfigurationService.cs
@@ -141,22 +141,29 @@ namespace MediaBrowser.Api
private string AutoDetectMetadataService()
{
- var paths = _libraryManager.GetDefaultVirtualFolders()
- .SelectMany(i => i.Locations)
- .Distinct(StringComparer.OrdinalIgnoreCase)
- .Select(i => new DirectoryInfo(i))
- .ToList();
-
- if (paths.SelectMany(i => i.EnumerateFiles("*.xml", SearchOption.AllDirectories))
- .Any())
+ try
{
- return XbmcMetadata;
+ var paths = _libraryManager.GetDefaultVirtualFolders()
+ .SelectMany(i => i.Locations)
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .Select(i => new DirectoryInfo(i))
+ .ToList();
+
+ if (paths.SelectMany(i => i.EnumerateFiles("*.xml", SearchOption.AllDirectories))
+ .Any())
+ {
+ return XbmcMetadata;
+ }
+
+ if (paths.SelectMany(i => i.EnumerateFiles("*.xml", SearchOption.AllDirectories))
+ .Any(i => string.Equals(i.Name, "series.xml", StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, "movie.xml", StringComparison.OrdinalIgnoreCase)))
+ {
+ return MediaBrowserMetadata;
+ }
}
-
- if (paths.SelectMany(i => i.EnumerateFiles("*.xml", SearchOption.AllDirectories))
- .Any(i => string.Equals(i.Name, "series.xml", StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, "movie.xml", StringComparison.OrdinalIgnoreCase)))
+ catch (Exception)
{
- return MediaBrowserMetadata;
+
}
return XbmcMetadata;
diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs
index 10a3117a1..34b930a6a 100644
--- a/MediaBrowser.Api/Library/LibraryService.cs
+++ b/MediaBrowser.Api/Library/LibraryService.cs
@@ -286,7 +286,7 @@ namespace MediaBrowser.Api.Library
public void Post(PostUpdatedSeries request)
{
-
+ Task.Run(() => _libraryManager.ValidateMediaLibrary(new Progress<double>(), CancellationToken.None));
}
public object Get(GetFile request)
diff --git a/MediaBrowser.Api/PlaylistService.cs b/MediaBrowser.Api/PlaylistService.cs
index 5cba348a5..5f4ced12e 100644
--- a/MediaBrowser.Api/PlaylistService.cs
+++ b/MediaBrowser.Api/PlaylistService.cs
@@ -36,6 +36,13 @@ namespace MediaBrowser.Api
[ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
public string Id { get; set; }
+
+ /// <summary>
+ /// Gets or sets the user id.
+ /// </summary>
+ /// <value>The user id.</value>
+ [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string UserId { get; set; }
}
[Route("/Playlists/{Id}/Items", "DELETE", Summary = "Removes items from a playlist")]
@@ -58,8 +65,8 @@ namespace MediaBrowser.Api
/// Gets or sets the user id.
/// </summary>
/// <value>The user id.</value>
- [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
- public Guid? UserId { get; set; }
+ [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+ public string UserId { get; set; }
/// <summary>
/// Skips over a given number of items within the results. Use for paging.
@@ -115,7 +122,7 @@ namespace MediaBrowser.Api
public void Post(AddToPlaylist request)
{
- var task = _playlistManager.AddToPlaylist(request.Id, request.Ids.Split(','));
+ var task = _playlistManager.AddToPlaylist(request.Id, request.Ids.Split(','), request.UserId);
Task.WaitAll(task);
}
@@ -130,7 +137,7 @@ namespace MediaBrowser.Api
public object Get(GetPlaylistItems request)
{
var playlist = (Playlist)_libraryManager.GetItemById(request.Id);
- var user = request.UserId.HasValue ? _userManager.GetUserById(request.UserId.Value) : null;
+ var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(new Guid(request.UserId)) : null;
var items = playlist.GetManageableItems().ToArray();
diff --git a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
index c67a8a3d5..cbe0b97a4 100644
--- a/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
+++ b/MediaBrowser.Controller/Playlists/IPlaylistManager.cs
@@ -26,8 +26,9 @@ namespace MediaBrowser.Controller.Playlists
/// </summary>
/// <param name="playlistId">The playlist identifier.</param>
/// <param name="itemIds">The item ids.</param>
+ /// <param name="userId">The user identifier.</param>
/// <returns>Task.</returns>
- Task AddToPlaylist(string playlistId, IEnumerable<string> itemIds);
+ Task AddToPlaylist(string playlistId, IEnumerable<string> itemIds, string userId);
/// <summary>
/// Removes from playlist.
diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs
index 84fcbb91a..2659a7c13 100644
--- a/MediaBrowser.Controller/Playlists/Playlist.cs
+++ b/MediaBrowser.Controller/Playlists/Playlist.cs
@@ -49,7 +49,7 @@ namespace MediaBrowser.Controller.Playlists
}
return inputItems.SelectMany(i => GetPlaylistItems(i, user))
- .Where(m => string.Equals(m.MediaType, playlistMediaType, StringComparison.OrdinalIgnoreCase));
+ .Where(m => string.Equals(m.MediaType, playlistMediaType, StringComparison.OrdinalIgnoreCase));
}
private static IEnumerable<BaseItem> GetPlaylistItems(BaseItem i, User user)
@@ -57,25 +57,31 @@ namespace MediaBrowser.Controller.Playlists
var musicGenre = i as MusicGenre;
if (musicGenre != null)
{
- var songs = user.RootFolder
- .GetRecursiveChildren(user)
+ var items = user == null
+ ? LibraryManager.RootFolder.GetRecursiveChildren()
+ : user.RootFolder.GetRecursiveChildren(user, true);
+
+ var songs = items
.OfType<Audio>()
.Where(a => a.Genres.Contains(musicGenre.Name, StringComparer.OrdinalIgnoreCase));
- return LibraryManager.Sort(songs, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending);
+ return LibraryManager.Sort(songs, user, new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }, SortOrder.Ascending);
}
var musicArtist = i as MusicArtist;
if (musicArtist != null)
{
- var songs = user.RootFolder
- .GetRecursiveChildren(user)
+ var items = user == null
+ ? LibraryManager.RootFolder.GetRecursiveChildren()
+ : user.RootFolder.GetRecursiveChildren(user, true);
+
+ var songs = items
.OfType<Audio>()
.Where(a => a.HasArtist(musicArtist.Name));
- return LibraryManager.Sort(songs, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending);
+ return LibraryManager.Sort(songs, user, new[] { ItemSortBy.AlbumArtist, ItemSortBy.Album, ItemSortBy.SortName }, SortOrder.Ascending);
}
-
+
var folder = i as Folder;
if (folder != null)
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index ce761ff9c..82d5e0344 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -70,22 +70,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
/// <summary>
- /// The _semaphoreLocks
- /// </summary>
- private readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphoreLocks =
- new ConcurrentDictionary<string, SemaphoreSlim>();
-
- /// <summary>
- /// Gets the lock.
- /// </summary>
- /// <param name="filename">The filename.</param>
- /// <returns>System.Object.</returns>
- private SemaphoreSlim GetLock(string filename)
- {
- return _semaphoreLocks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1));
- }
-
- /// <summary>
/// Gets the media info.
/// </summary>
/// <param name="inputFiles">The input files.</param>
diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
index e3d9d905a..94e3575a1 100644
--- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
+++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
@@ -63,6 +63,7 @@
<Compile Include="Subtitles\ISubtitleWriter.cs" />
<Compile Include="Subtitles\SrtParser.cs" />
<Compile Include="Subtitles\SrtWriter.cs" />
+ <Compile Include="Subtitles\AssParser.cs" />
<Compile Include="Subtitles\SsaParser.cs" />
<Compile Include="Subtitles\SubtitleEncoder.cs" />
<Compile Include="Subtitles\SubtitleTrackInfo.cs" />
@@ -96,4 +97,4 @@
<Target Name="AfterBuild">
</Target>
-->
-</Project>
+</Project> \ No newline at end of file
diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
new file mode 100644
index 000000000..e5a727428
--- /dev/null
+++ b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Threading;
+
+namespace MediaBrowser.MediaEncoding.Subtitles
+{
+ public class AssParser : ISubtitleParser
+ {
+ private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
+ public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken)
+ {
+ var trackInfo = new SubtitleTrackInfo();
+ var eventIndex = 1;
+ using (var reader = new StreamReader(stream))
+ {
+ string line;
+ while (reader.ReadLine() != "[Events]")
+ {}
+ var headers = ParseFieldHeaders(reader.ReadLine());
+
+ while ((line = reader.ReadLine()) != null)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (string.IsNullOrWhiteSpace(line))
+ {
+ continue;
+ }
+ if(line.StartsWith("["))
+ break;
+ if(string.IsNullOrEmpty(line))
+ continue;
+ var subEvent = new SubtitleTrackEvent { Id = eventIndex.ToString(_usCulture) };
+ eventIndex++;
+ var sections = line.Substring(10).Split(',');
+
+ subEvent.StartPositionTicks = GetTicks(sections[headers["Start"]]);
+ subEvent.EndPositionTicks = GetTicks(sections[headers["End"]]);
+ subEvent.Text = string.Join(",", sections.Skip(headers["Text"]));
+ subEvent.Text = Regex.Replace(subEvent.Text, @"\{(\\[\w]+\(?([\w\d]+,?)+\)?)+\}", string.Empty, RegexOptions.IgnoreCase);
+
+ trackInfo.TrackEvents.Add(subEvent);
+ }
+ }
+ return trackInfo;
+ }
+
+ long GetTicks(string time)
+ {
+ TimeSpan span;
+ return TimeSpan.TryParseExact(time, @"h\:mm\:ss\.ff", _usCulture, out span)
+ ? span.Ticks: 0;
+ }
+
+ private Dictionary<string,int> ParseFieldHeaders(string line) {
+ var fields = line.Substring(8).Split(',').Select(x=>x.Trim()).ToList();
+
+ var result = new Dictionary<string, int> {
+ {"Start", fields.IndexOf("Start")},
+ {"End", fields.IndexOf("End")},
+ {"Text", fields.IndexOf("Text")}
+ };
+ return result;
+ }
+ }
+}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
index e21804f6c..4b11d55ce 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
@@ -1,71 +1,391 @@
using System;
-using System.Collections.Generic;
-using System.Globalization;
using System.IO;
-using System.Linq;
-using System.Text.RegularExpressions;
+using System.Text;
using System.Threading;
namespace MediaBrowser.MediaEncoding.Subtitles
{
+ /// <summary>
+ /// Credit to https://github.com/SubtitleEdit/subtitleedit/blob/a299dc4407a31796364cc6ad83f0d3786194ba22/src/Logic/SubtitleFormats/SubStationAlpha.cs
+ /// </summary>
public class SsaParser : ISubtitleParser
{
- private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken)
{
var trackInfo = new SubtitleTrackInfo();
- var eventIndex = 1;
+
using (var reader = new StreamReader(stream))
{
+ bool eventsStarted = false;
+
+ string[] format = "Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text".Split(',');
+ int indexLayer = 0;
+ int indexStart = 1;
+ int indexEnd = 2;
+ int indexStyle = 3;
+ int indexName = 4;
+ int indexEffect = 8;
+ int indexText = 9;
+ int lineNumber = 0;
+
+ var header = new StringBuilder();
+
string line;
- while (reader.ReadLine() != "[Events]")
- {}
- var headers = ParseFieldHeaders(reader.ReadLine());
while ((line = reader.ReadLine()) != null)
{
cancellationToken.ThrowIfCancellationRequested();
-
- if (string.IsNullOrWhiteSpace(line))
+
+ lineNumber++;
+ if (!eventsStarted)
+ header.AppendLine(line);
+
+ if (line.Trim().ToLower() == "[events]")
{
- continue;
+ eventsStarted = true;
}
- if(line.StartsWith("["))
- break;
- if(string.IsNullOrEmpty(line))
- continue;
- var subEvent = new SubtitleTrackEvent { Id = eventIndex.ToString(_usCulture) };
- eventIndex++;
- var sections = line.Substring(10).Split(',');
-
- subEvent.StartPositionTicks = GetTicks(sections[headers["Start"]]);
- subEvent.EndPositionTicks = GetTicks(sections[headers["End"]]);
- subEvent.Text = string.Join(",", sections.Skip(headers["Text"]));
- subEvent.Text = Regex.Replace(subEvent.Text, @"\{(\\[\w]+\(?([\w\d]+,?)+\)?)+\}", string.Empty, RegexOptions.IgnoreCase);
-
- trackInfo.TrackEvents.Add(subEvent);
- }
+ else if (!string.IsNullOrEmpty(line) && line.Trim().StartsWith(";"))
+ {
+ // skip comment lines
+ }
+ else if (eventsStarted && line.Trim().Length > 0)
+ {
+ string s = line.Trim().ToLower();
+ if (s.StartsWith("format:"))
+ {
+ if (line.Length > 10)
+ {
+ format = line.ToLower().Substring(8).Split(',');
+ for (int i = 0; i < format.Length; i++)
+ {
+ if (format[i].Trim().ToLower() == "layer")
+ indexLayer = i;
+ else if (format[i].Trim().ToLower() == "start")
+ indexStart = i;
+ else if (format[i].Trim().ToLower() == "end")
+ indexEnd = i;
+ else if (format[i].Trim().ToLower() == "text")
+ indexText = i;
+ else if (format[i].Trim().ToLower() == "effect")
+ indexEffect = i;
+ else if (format[i].Trim().ToLower() == "style")
+ indexStyle = i;
+ }
+ }
+ }
+ else if (!string.IsNullOrEmpty(s))
+ {
+ string text = string.Empty;
+ string start = string.Empty;
+ string end = string.Empty;
+ string style = string.Empty;
+ string layer = string.Empty;
+ string effect = string.Empty;
+ string name = string.Empty;
+
+ string[] splittedLine;
+
+ if (s.StartsWith("dialogue:"))
+ splittedLine = line.Substring(10).Split(',');
+ else
+ splittedLine = line.Split(',');
+
+ for (int i = 0; i < splittedLine.Length; i++)
+ {
+ if (i == indexStart)
+ start = splittedLine[i].Trim();
+ else if (i == indexEnd)
+ end = splittedLine[i].Trim();
+ else if (i == indexLayer)
+ layer = splittedLine[i];
+ else if (i == indexEffect)
+ effect = splittedLine[i];
+ else if (i == indexText)
+ text = splittedLine[i];
+ else if (i == indexStyle)
+ style = splittedLine[i];
+ else if (i == indexName)
+ name = splittedLine[i];
+ else if (i > indexText)
+ text += "," + splittedLine[i];
+ }
+
+ try
+ {
+ var p = new SubtitleTrackEvent();
+
+ p.StartPositionTicks = GetTimeCodeFromString(start);
+ p.EndPositionTicks = GetTimeCodeFromString(end);
+ p.Text = GetFormattedText(text);
+
+ trackInfo.TrackEvents.Add(p);
+ }
+ catch
+ {
+ }
+ }
+ }
+ }
+
+ //if (header.Length > 0)
+ //subtitle.Header = header.ToString();
+
+ //subtitle.Renumber(1);
}
return trackInfo;
}
- long GetTicks(string time)
+ private static long GetTimeCodeFromString(string time)
+ {
+ // h:mm:ss.cc
+ string[] timeCode = time.Split(':', '.');
+ return new TimeSpan(0, int.Parse(timeCode[0]),
+ int.Parse(timeCode[1]),
+ int.Parse(timeCode[2]),
+ int.Parse(timeCode[3]) * 10).Ticks;
+ }
+
+ public static string GetFormattedText(string text)
+ {
+ text = text.Replace("\\N", Environment.NewLine).Replace("\\n", Environment.NewLine);
+ bool italic = false;
+
+ for (int i = 0; i < 10; i++) // just look ten times...
+ {
+ if (text.Contains(@"{\fn"))
+ {
+ int start = text.IndexOf(@"{\fn");
+ int end = text.IndexOf('}', start);
+ if (end > 0 && !text.Substring(start).StartsWith("{\\fn}"))
+ {
+ string fontName = text.Substring(start + 4, end - (start + 4));
+ string extraTags = string.Empty;
+ CheckAndAddSubTags(ref fontName, ref extraTags, out italic);
+ text = text.Remove(start, end - start + 1);
+ if (italic)
+ text = text.Insert(start, "<font face=\"" + fontName + "\"" + extraTags + "><i>");
+ else
+ text = text.Insert(start, "<font face=\"" + fontName + "\"" + extraTags + ">");
+
+ int indexOfEndTag = text.IndexOf("{\\fn}", start);
+ if (indexOfEndTag > 0)
+ text = text.Remove(indexOfEndTag, "{\\fn}".Length).Insert(indexOfEndTag, "</font>");
+ else
+ text += "</font>";
+ }
+ }
+
+ if (text.Contains(@"{\fs"))
+ {
+ int start = text.IndexOf(@"{\fs");
+ int end = text.IndexOf('}', start);
+ if (end > 0 && !text.Substring(start).StartsWith("{\\fs}"))
+ {
+ string fontSize = text.Substring(start + 4, end - (start + 4));
+ string extraTags = string.Empty;
+ CheckAndAddSubTags(ref fontSize, ref extraTags, out italic);
+ if (IsInteger(fontSize))
+ {
+ text = text.Remove(start, end - start + 1);
+ if (italic)
+ text = text.Insert(start, "<font size=\"" + fontSize + "\"" + extraTags + "><i>");
+ else
+ text = text.Insert(start, "<font size=\"" + fontSize + "\"" + extraTags + ">");
+
+ int indexOfEndTag = text.IndexOf("{\\fs}", start);
+ if (indexOfEndTag > 0)
+ text = text.Remove(indexOfEndTag, "{\\fs}".Length).Insert(indexOfEndTag, "</font>");
+ else
+ text += "</font>";
+ }
+ }
+ }
+
+ if (text.Contains(@"{\c"))
+ {
+ int start = text.IndexOf(@"{\c");
+ int end = text.IndexOf('}', start);
+ if (end > 0 && !text.Substring(start).StartsWith("{\\c}"))
+ {
+ string color = text.Substring(start + 4, end - (start + 4));
+ string extraTags = string.Empty;
+ CheckAndAddSubTags(ref color, ref extraTags, out italic);
+
+ color = color.Replace("&", string.Empty).TrimStart('H');
+ color = color.PadLeft(6, '0');
+
+ // switch to rrggbb from bbggrr
+ color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2);
+ color = color.ToLower();
+
+ text = text.Remove(start, end - start + 1);
+ if (italic)
+ text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + "><i>");
+ else
+ text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + ">");
+ int indexOfEndTag = text.IndexOf("{\\c}", start);
+ if (indexOfEndTag > 0)
+ text = text.Remove(indexOfEndTag, "{\\c}".Length).Insert(indexOfEndTag, "</font>");
+ else
+ text += "</font>";
+ }
+ }
+
+ if (text.Contains(@"{\1c")) // "1" specifices primary color
+ {
+ int start = text.IndexOf(@"{\1c");
+ int end = text.IndexOf('}', start);
+ if (end > 0 && !text.Substring(start).StartsWith("{\\1c}"))
+ {
+ string color = text.Substring(start + 5, end - (start + 5));
+ string extraTags = string.Empty;
+ CheckAndAddSubTags(ref color, ref extraTags, out italic);
+
+ color = color.Replace("&", string.Empty).TrimStart('H');
+ color = color.PadLeft(6, '0');
+
+ // switch to rrggbb from bbggrr
+ color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2);
+ color = color.ToLower();
+
+ text = text.Remove(start, end - start + 1);
+ if (italic)
+ text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + "><i>");
+ else
+ text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + ">");
+ text += "</font>";
+ }
+ }
+
+ }
+
+ text = text.Replace(@"{\i1}", "<i>");
+ text = text.Replace(@"{\i0}", "</i>");
+ text = text.Replace(@"{\i}", "</i>");
+ if (CountTagInText(text, "<i>") > CountTagInText(text, "</i>"))
+ text += "</i>";
+
+ text = text.Replace(@"{\u1}", "<u>");
+ text = text.Replace(@"{\u0}", "</u>");
+ text = text.Replace(@"{\u}", "</u>");
+ if (CountTagInText(text, "<u>") > CountTagInText(text, "</u>"))
+ text += "</u>";
+
+ text = text.Replace(@"{\b1}", "<b>");
+ text = text.Replace(@"{\b0}", "</b>");
+ text = text.Replace(@"{\b}", "</b>");
+ if (CountTagInText(text, "<b>") > CountTagInText(text, "</b>"))
+ text += "</b>";
+
+ return text;
+ }
+
+ private static bool IsInteger(string s)
{
- TimeSpan span;
- return TimeSpan.TryParseExact(time, @"h\:mm\:ss\.ff", _usCulture, out span)
- ? span.Ticks: 0;
+ int i;
+ if (int.TryParse(s, out i))
+ return true;
+ return false;
}
- private Dictionary<string,int> ParseFieldHeaders(string line) {
- var fields = line.Substring(8).Split(',').Select(x=>x.Trim()).ToList();
+ private static int CountTagInText(string text, string tag)
+ {
+ int count = 0;
+ int index = text.IndexOf(tag);
+ while (index >= 0)
+ {
+ count++;
+ if (index == text.Length)
+ return count;
+ index = text.IndexOf(tag, index + 1);
+ }
+ return count;
+ }
+
+ private static void CheckAndAddSubTags(ref string tagName, ref string extraTags, out bool italic)
+ {
+ italic = false;
+ int indexOfSPlit = tagName.IndexOf(@"\");
+ if (indexOfSPlit > 0)
+ {
+ string rest = tagName.Substring(indexOfSPlit).TrimStart('\\');
+ tagName = tagName.Remove(indexOfSPlit);
+
+ for (int i = 0; i < 10; i++)
+ {
+ if (rest.StartsWith("fs") && rest.Length > 2)
+ {
+ indexOfSPlit = rest.IndexOf(@"\");
+ string fontSize = rest;
+ if (indexOfSPlit > 0)
+ {
+ fontSize = rest.Substring(0, indexOfSPlit);
+ rest = rest.Substring(indexOfSPlit).TrimStart('\\');
+ }
+ else
+ {
+ rest = string.Empty;
+ }
+ extraTags += " size=\"" + fontSize.Substring(2) + "\"";
+ }
+ else if (rest.StartsWith("fn") && rest.Length > 2)
+ {
+ indexOfSPlit = rest.IndexOf(@"\");
+ string fontName = rest;
+ if (indexOfSPlit > 0)
+ {
+ fontName = rest.Substring(0, indexOfSPlit);
+ rest = rest.Substring(indexOfSPlit).TrimStart('\\');
+ }
+ else
+ {
+ rest = string.Empty;
+ }
+ extraTags += " face=\"" + fontName.Substring(2) + "\"";
+ }
+ else if (rest.StartsWith("c") && rest.Length > 2)
+ {
+ indexOfSPlit = rest.IndexOf(@"\");
+ string fontColor = rest;
+ if (indexOfSPlit > 0)
+ {
+ fontColor = rest.Substring(0, indexOfSPlit);
+ rest = rest.Substring(indexOfSPlit).TrimStart('\\');
+ }
+ else
+ {
+ rest = string.Empty;
+ }
+
+ string color = fontColor.Substring(2);
+ color = color.Replace("&", string.Empty).TrimStart('H');
+ color = color.PadLeft(6, '0');
+ // switch to rrggbb from bbggrr
+ color = "#" + color.Remove(color.Length - 6) + color.Substring(color.Length - 2, 2) + color.Substring(color.Length - 4, 2) + color.Substring(color.Length - 6, 2);
+ color = color.ToLower();
- var result = new Dictionary<string, int> {
- {"Start", fields.IndexOf("Start")},
- {"End", fields.IndexOf("End")},
- {"Text", fields.IndexOf("Text")}
- };
- return result;
+ extraTags += " color=\"" + color + "\"";
+ }
+ else if (rest.StartsWith("i1") && rest.Length > 1)
+ {
+ indexOfSPlit = rest.IndexOf(@"\");
+ italic = true;
+ if (indexOfSPlit > 0)
+ {
+ rest = rest.Substring(indexOfSPlit).TrimStart('\\');
+ }
+ else
+ {
+ rest = string.Empty;
+ }
+ }
+ else if (rest.Length > 0 && rest.Contains("\\"))
+ {
+ indexOfSPlit = rest.IndexOf(@"\");
+ rest = rest.Substring(indexOfSPlit).TrimStart('\\');
+ }
+ }
+ }
}
}
}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index 82e331dd8..c083cad4e 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -239,11 +239,14 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
return new SrtParser();
}
- if (string.Equals(format, SubtitleFormat.SSA, StringComparison.OrdinalIgnoreCase) ||
- string.Equals(format, SubtitleFormat.ASS, StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(format, SubtitleFormat.SSA, StringComparison.OrdinalIgnoreCase))
{
return new SsaParser();
}
+ if (string.Equals(format, SubtitleFormat.ASS, StringComparison.OrdinalIgnoreCase))
+ {
+ return new AssParser();
+ }
if (throwIfMissing)
{
diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs
index af37a2be0..4ccafd590 100644
--- a/MediaBrowser.Model/ApiClient/IApiClient.cs
+++ b/MediaBrowser.Model/ApiClient/IApiClient.cs
@@ -1271,8 +1271,9 @@ namespace MediaBrowser.Model.ApiClient
/// </summary>
/// <param name="playlistId">The playlist identifier.</param>
/// <param name="itemIds">The item ids.</param>
+ /// <param name="userId">The user identifier.</param>
/// <returns>Task.</returns>
- Task AddToPlaylist(string playlistId, IEnumerable<string> itemIds);
+ Task AddToPlaylist(string playlistId, IEnumerable<string> itemIds, string userId);
/// <summary>
/// Removes from playlist.
diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs
index d3e61792f..0dfd27a72 100644
--- a/MediaBrowser.Model/Dto/BaseItemDto.cs
+++ b/MediaBrowser.Model/Dto/BaseItemDto.cs
@@ -1,12 +1,12 @@
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.Library;
+using MediaBrowser.Model.Providers;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.Serialization;
-using MediaBrowser.Model.Providers;
namespace MediaBrowser.Model.Dto
{
@@ -228,6 +228,12 @@ namespace MediaBrowser.Model.Dto
public int? ProductionYear { get; set; }
/// <summary>
+ /// Gets or sets the recursive unplayed item count.
+ /// </summary>
+ /// <value>The recursive unplayed item count.</value>
+ public int? RecursiveUnplayedItemCount { get; set; }
+
+ /// <summary>
/// Gets or sets the season count.
/// </summary>
/// <value>The season count.</value>
diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
index 2010b495c..2c683991c 100644
--- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs
+++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
@@ -1366,6 +1366,7 @@ namespace MediaBrowser.Server.Implementations.Dto
dto.RecursiveItemCount = recursiveItemCount;
dto.UserData.UnplayedItemCount = unplayed;
+ dto.RecursiveUnplayedItemCount = unplayed;
if (recursiveItemCount > 0)
{
diff --git a/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs b/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs
index 07c937389..ee2114aa4 100644
--- a/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs
+++ b/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs
@@ -82,7 +82,7 @@ namespace MediaBrowser.Server.Implementations.Playlists
if (folder != null)
{
options.MediaType = folder.GetRecursiveChildren()
- .Where(i => !i.IsFolder)
+ .Where(i => !i.IsFolder && i.SupportsAddingToPlaylist)
.Select(i => i.MediaType)
.FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
}
@@ -100,6 +100,8 @@ namespace MediaBrowser.Server.Implementations.Playlists
throw new ArgumentException("A playlist media type is required.");
}
+ var user = _userManager.GetUserById(new Guid(options.UserId));
+
var path = Path.Combine(parentFolder.Path, folderName);
path = GetTargetPath(path);
@@ -126,7 +128,7 @@ namespace MediaBrowser.Server.Implementations.Playlists
if (options.ItemIdList.Count > 0)
{
- await AddToPlaylist(playlist.Id.ToString("N"), options.ItemIdList);
+ await AddToPlaylistInternal(playlist.Id.ToString("N"), options.ItemIdList, user);
}
return new PlaylistCreationResult
@@ -151,14 +153,21 @@ namespace MediaBrowser.Server.Implementations.Playlists
return path;
}
- private IEnumerable<BaseItem> GetPlaylistItems(IEnumerable<string> itemIds, string playlistMediaType)
+ private IEnumerable<BaseItem> GetPlaylistItems(IEnumerable<string> itemIds, string playlistMediaType, User user)
{
var items = itemIds.Select(i => _libraryManager.GetItemById(i)).Where(i => i != null);
- return Playlist.GetPlaylistItems(playlistMediaType, items, null);
+ return Playlist.GetPlaylistItems(playlistMediaType, items, user);
}
- public async Task AddToPlaylist(string playlistId, IEnumerable<string> itemIds)
+ public Task AddToPlaylist(string playlistId, IEnumerable<string> itemIds, string userId)
+ {
+ var user = string.IsNullOrWhiteSpace(userId) ? null : _userManager.GetUserById(new Guid(userId));
+
+ return AddToPlaylistInternal(playlistId, itemIds, user);
+ }
+
+ private async Task AddToPlaylistInternal(string playlistId, IEnumerable<string> itemIds, User user)
{
var playlist = _libraryManager.GetItemById(playlistId) as Playlist;
@@ -170,7 +179,9 @@ namespace MediaBrowser.Server.Implementations.Playlists
var list = new List<LinkedChild>();
var itemList = new List<BaseItem>();
- var items = GetPlaylistItems(itemIds, playlist.MediaType).ToList();
+ var items = GetPlaylistItems(itemIds, playlist.MediaType, user)
+ .Where(i => i.SupportsAddingToPlaylist)
+ .ToList();
foreach (var item in items)
{
@@ -183,7 +194,6 @@ namespace MediaBrowser.Server.Implementations.Playlists
await playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
await playlist.RefreshMetadata(new MetadataRefreshOptions
{
-
ForceSave = true
}, CancellationToken.None).ConfigureAwait(false);
diff --git a/MediaBrowser.Tests/MediaEncoding/Subtitles/SsaParserTests.cs b/MediaBrowser.Tests/MediaEncoding/Subtitles/SsaParserTests.cs
index 3c278ae41..d869146fd 100644
--- a/MediaBrowser.Tests/MediaEncoding/Subtitles/SsaParserTests.cs
+++ b/MediaBrowser.Tests/MediaEncoding/Subtitles/SsaParserTests.cs
@@ -39,7 +39,7 @@ namespace MediaBrowser.Tests.MediaEncoding.Subtitles {
}
};
- var sut = new SsaParser();
+ var sut = new AssParser();
var stream = File.OpenRead(@"MediaEncoding\Subtitles\TestSubtitles\data.ssa");