diff options
| -rw-r--r-- | MediaBrowser.Api/ConfigurationService.cs | 33 | ||||
| -rw-r--r-- | MediaBrowser.Api/Library/LibraryService.cs | 2 | ||||
| -rw-r--r-- | MediaBrowser.Api/PlaylistService.cs | 15 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Playlists/IPlaylistManager.cs | 3 | ||||
| -rw-r--r-- | MediaBrowser.Controller/Playlists/Playlist.cs | 22 | ||||
| -rw-r--r-- | MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 16 | ||||
| -rw-r--r-- | MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj | 3 | ||||
| -rw-r--r-- | MediaBrowser.MediaEncoding/Subtitles/AssParser.cs | 71 | ||||
| -rw-r--r-- | MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs | 400 | ||||
| -rw-r--r-- | MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs | 7 | ||||
| -rw-r--r-- | MediaBrowser.Model/ApiClient/IApiClient.cs | 3 | ||||
| -rw-r--r-- | MediaBrowser.Model/Dto/BaseItemDto.cs | 8 | ||||
| -rw-r--r-- | MediaBrowser.Server.Implementations/Dto/DtoService.cs | 1 | ||||
| -rw-r--r-- | MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs | 24 | ||||
| -rw-r--r-- | MediaBrowser.Tests/MediaEncoding/Subtitles/SsaParserTests.cs | 2 |
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"); |
