예제 #1
0
        /// <summary>
        /// Reads WebVTT block from the reader.
        /// </summary>
        /// <param name="reader">Text reader to read the region block.</param>
        /// <returns>A task that represents the asynchronous read operation.</returns>
        private static async Task <BaseBlock> ReadBlockAsync(
            TextReader reader)
        {
            var line = await reader.ReadLineAsync()
                       .ConfigureAwait(false);

            if (string.IsNullOrEmpty(line))
            {
                return(null);
            }

            if (line.StartsWith(Constants.RegionToken))
            {
                return(await WebVttParser.ReadRegionAsync(line, reader)
                       .ConfigureAwait(false));
            }

            if (line.StartsWith(Constants.StyleToken))
            {
                return(await WebVttParser.ReadStyleAsync(line, reader)
                       .ConfigureAwait(false));
            }

            if (line.StartsWith(Constants.CommentToken))
            {
                return(await WebVttParser.ReadCommentAsync(line, reader)
                       .ConfigureAwait(false));
            }

            return(await ReadCueAsync(line, reader)
                   .ConfigureAwait(false));
        }
예제 #2
0
        /// <summary>
        /// Utility method to get anchor setting value.
        /// </summary>
        /// <param name="name">Setting name.</param>
        /// <param name="settings">Setting property bag.</param>
        /// <param name="value">If successful, contains setting value; otherwise default value.</param>
        /// <returns>True if the setting exists and is valid; otherwise false.</returns>
        private static bool TryGetAnchorSetting(
            string name,
            Dictionary <string, string> settings,
            out Anchor value)
        {
            value = default(Anchor);

            string stringValue;

            if (false == settings.TryGetValue(name, out stringValue) ||
                string.IsNullOrEmpty(stringValue))
            {
                return(false);
            }

            int index = stringValue.IndexOf(',');

            if (index < 2 || stringValue.Length - index < 3)
            {
                return(false);
            }

            if (false == WebVttParser.TryParsePercent(stringValue.Substring(0, index), out value.XPercent) ||
                false == WebVttParser.TryParsePercent(stringValue.Substring(index + 1, stringValue.Length - index - 1), out value.YPercent))
            {
                value = default(Anchor);
                return(false);
            }

            return(true);
        }
예제 #3
0
        /// <summary>
        /// Utility method to get position setting value.
        /// </summary>
        /// <param name="name">Setting name.</param>
        /// <param name="settings">Setting property bag.</param>
        /// <param name="value">If successful, contains setting value; otherwise default value.</param>
        /// <returns>True if the setting exists and is valid; otherwise false.</returns>
        private static bool TryGetPositionSetting(
            string name,
            Dictionary <string, string> settings,
            out PositionSettings value)
        {
            value = default(PositionSettings);

            string stringValue;

            if (false == settings.TryGetValue(name, out stringValue) ||
                string.IsNullOrEmpty(stringValue))
            {
                return(false);
            }

            string positionValue = stringValue;
            int    commaIndex    = stringValue.IndexOf(',');

            if (0 <= commaIndex)
            {
                if (stringValue.Length - commaIndex < 2)
                {
                    return(false);
                }

                string alignment = stringValue.Substring(commaIndex + 1, stringValue.Length - commaIndex - 1);
                if (string.Equals(Constants.LineLeftValue, alignment, StringComparison.OrdinalIgnoreCase))
                {
                    value.Alignment = PositionAlignment.LineLeft;
                }
                else if (string.Equals(Constants.CenterValue, alignment, StringComparison.OrdinalIgnoreCase))
                {
                    value.Alignment = PositionAlignment.Center;
                }
                else if (string.Equals(Constants.LineRightValue, alignment, StringComparison.OrdinalIgnoreCase))
                {
                    value.Alignment = PositionAlignment.LineRight;
                }
                else
                {
                    return(false);
                }

                positionValue = stringValue.Substring(0, commaIndex);
            }

            double percent;

            if (WebVttParser.TryParsePercent(positionValue, out percent))
            {
                value.PositionPercent = percent;
                return(true);
            }

            value = default(PositionSettings);
            return(false);
        }
예제 #4
0
        /// <summary>
        /// Utility method to parse single span from text input.
        /// </summary>
        /// <param name="input">Input string to process.</param>
        /// <param name="position">Position in the string to begin parsing.</param>
        /// <param name="span">If successful, contains span parsed from the input.</param>
        /// <returns>True if span was successfully parsed from the input; otherwise false.</returns>
        private static bool TryParseSpan(
            string input,
            ref int position,
            out Span span)
        {
            span = null;

            if (position >= input.Length || input[position] != '<')
            {
                return(false);
            }

            string        tagName;
            string        endTagName;
            string        annotation;
            List <string> classes;
            List <Span>   innerSpans;

            if (false == WebVttParser.TryParseTagStart(input, ref position, out tagName, out classes, out annotation))
            {
                return(false);
            }

            if (false == WebVttParser.TryParseSpans(input, ref position, out innerSpans))
            {
                return(false);
            }

            if (false == WebVttParser.TryParseTagEnd(input, ref position, out endTagName))
            {
                return(false);
            }

            if (endTagName != null && false == string.Equals(tagName, endTagName, StringComparison.Ordinal))
            {
                return(false);
            }

            SpanType spanType;

            if (false == WebVttParser.TryGetSpanTypeFromName(tagName, out spanType))
            {
                return(false);
            }

            span = new Span()
            {
                Type       = spanType,
                Children   = innerSpans != null && innerSpans.Count > 0 ? innerSpans.ToArray() : null,
                Annotation = annotation,
                Classes    = classes != null && classes.Count > 0 ? classes.ToArray() : null
            };

            return(true);
        }
예제 #5
0
        /// <summary>
        /// Utility method to get percent setting value.
        /// </summary>
        /// <param name="name">Setting name.</param>
        /// <param name="settings">Setting property bag.</param>
        /// <param name="value">If successful, contains setting value; otherwise default value.</param>
        /// <returns>True if the setting exists and is valid; otherwise false.</returns>
        private static bool TryGetPercentSetting(
            string name,
            Dictionary <string, string> settings,
            out double value)
        {
            string stringValue;

            if (settings.TryGetValue(name, out stringValue) &&
                false == string.IsNullOrEmpty(stringValue))
            {
                return(WebVttParser.TryParsePercent(stringValue, out value));
            }

            value = default(double);
            return(false);
        }
예제 #6
0
        /// <summary>
        /// Utility method to parse region or cue setting values.
        /// </summary>
        /// <param name="rawSettings">Raw settings string to parse.</param>
        /// <returns>Collection of setting values parsed from the input.</returns>
        private static Dictionary <string, string> ParseSettings(
            string rawSettings)
        {
            Dictionary <string, string> result = null;

            if (false == string.IsNullOrWhiteSpace(rawSettings))
            {
                int start   = 0;
                int current = 0;
                foreach (char c in rawSettings)
                {
                    bool last = rawSettings.Length - current == 1;
                    if (char.IsWhiteSpace(c) || last)
                    {
                        if (start < current || last)
                        {
                            KeyValuePair <string, string> setting;
                            if (WebVttParser.TryParseSettingValue(rawSettings.Substring(start, current - start + (last ? 1 : 0)), out setting))
                            {
                                if (result == null)
                                {
                                    result = new Dictionary <string, string>(5, StringComparer.OrdinalIgnoreCase);
                                }

                                result[setting.Key] = setting.Value;
                            }
                            else
                            {
                                throw new InvalidDataException(string.Format("Invalid setting value in '{0}'.", rawSettings));
                            }
                        }

                        current++;
                        start = current;
                    }
                    else
                    {
                        current++;
                    }
                }
            }

            return(result);
        }
예제 #7
0
        /// <summary>
        /// Utility method to get line setting value.
        /// </summary>
        /// <param name="name">Setting name.</param>
        /// <param name="settings">Setting property bag.</param>
        /// <param name="value">If successful, contains setting value; otherwise default value.</param>
        /// <returns>True if the setting exists and is valid; otherwise false.</returns>
        private static bool TryGetLineSetting(
            string name,
            Dictionary <string, string> settings,
            out LineSettings value)
        {
            value = default(LineSettings);

            string stringValue;

            if (false == settings.TryGetValue(name, out stringValue) ||
                string.IsNullOrEmpty(stringValue))
            {
                return(false);
            }

            string offsetValue = stringValue;
            int    commaIndex  = stringValue.IndexOf(',');

            if (0 <= commaIndex)
            {
                if (stringValue.Length - commaIndex < 2)
                {
                    return(false);
                }

                string alignment = stringValue.Substring(commaIndex + 1, stringValue.Length - commaIndex - 1);
                if (string.Equals(Constants.StartValue, alignment, StringComparison.OrdinalIgnoreCase))
                {
                    value.Alignment = LineAlignment.Start;
                }
                else if (string.Equals(Constants.CenterValue, alignment, StringComparison.OrdinalIgnoreCase))
                {
                    value.Alignment = LineAlignment.Center;
                }
                else if (string.Equals(Constants.EndValue, alignment, StringComparison.OrdinalIgnoreCase))
                {
                    value.Alignment = LineAlignment.End;
                }
                else
                {
                    return(false);
                }

                offsetValue = stringValue.Substring(0, commaIndex);
            }

            double percent;

            if (WebVttParser.TryParsePercent(offsetValue, out percent))
            {
                value.Percent = percent;
                return(true);
            }

            int number;

            if (int.TryParse(offsetValue, out number))
            {
                value.LineNumber = number;
                return(true);
            }

            value = default(LineSettings);
            return(false);
        }
예제 #8
0
        /// <summary>
        /// Reads WebVTT media captions from a given <see cref="TextReader"/>.
        /// </summary>
        /// <param name="reader">Text reader to read the captions.</param>
        /// <returns>A task that represents the asynchronous read operation.</returns>
        public static async Task <MediaCaptions> ReadMediaCaptionsAsync(
            TextReader reader)
        {
            if (reader == null)
            {
                throw new ArgumentNullException("reader");
            }

            // Process stream header line
            var line = await reader.ReadLineAsync()
                       .ConfigureAwait(false);

            if (string.IsNullOrWhiteSpace(line) ||
                line.Length < 6 ||
                false == line.StartsWith(Constants.WebVttHeaderToken) ||
                line.Length >= 7 && line[6] != '\t' && line[6] != ' ')
            {
                throw new InvalidDataException("The stream does not start with the correct WebVTT file signature.");
            }

            var result = new MediaCaptions();

            // Process (skip over) optional headers from the stream
            while (false == string.IsNullOrEmpty(line))
            {
                line = await reader.ReadLineAsync()
                       .ConfigureAwait(false);
            }

            // Process media caption blocks.
            List <RegionDefinition> regions = new List <RegionDefinition>();
            List <Style>            styles  = new List <Style>();
            List <Cue> cues = new List <Cue>();

            BaseBlock block;

            do
            {
                block = await WebVttParser.ReadBlockAsync(reader)
                        .ConfigureAwait(false);

                RegionDefinition region = block as RegionDefinition;
                Style            style  = block as Style;
                Cue cue = block as Cue;

                if (cues.Count == 0)
                {
                    if (region != null)
                    {
                        regions.Add(region);
                    }
                    else if (style != null)
                    {
                        styles.Add(style);
                    }
                }
                else if (region != null || style != null)
                {
                    throw new InvalidDataException("Region or style blocks cannot be mixed with cue blocks.");
                }

                if (cue != null)
                {
                    cues.Add(cue);
                }
            }while (block != null);

            if (regions.Count > 0)
            {
                result.Regions = regions.ToArray();
            }

            if (styles.Count > 0)
            {
                result.Styles = styles.ToArray();
            }

            if (cues.Count > 0)
            {
                result.Cues = cues.ToArray();
            }

            return(result);
        }
예제 #9
0
        /// <summary>
        /// Reads WebVTT Cue block from the reader.
        /// </summary>
        /// <param name="firstLine">First line of the block read from the reader.</param>
        /// <param name="reader">Text reader to read the style block.</param>
        /// <returns>A task that represents the asynchronous read operation.</returns>
        private static async Task <Cue> ReadCueAsync(
            string firstLine,
            TextReader reader)
        {
            string line    = firstLine;
            var    content = new StringBuilder(100);

            Cue result = new Cue();

            if (false == line.Contains(Constants.ArrowToken))
            {
                result.Id = line;
                line      = await reader.ReadLineAsync()
                            .ConfigureAwait(false);
            }

            int position = 0;

            result.Start = ParseTimeSpan(line, ref position);

            while (position < line.Length && char.IsWhiteSpace(line, position))
            {
                position++;
            }

            if (0 != string.CompareOrdinal(line, position, Constants.ArrowToken, 0, Constants.ArrowToken.Length))
            {
                throw new InvalidDataException(string.Format("Invalid characters found in cue timings '{0}' at position {1}.", line, position));
            }

            position += Constants.ArrowToken.Length;

            while (position < line.Length && char.IsWhiteSpace(line, position))
            {
                position++;
            }

            result.End = ParseTimeSpan(line, ref position);

            if (result.End < result.Start)
            {
                throw new InvalidDataException(string.Format("Cue start time is greater than end time in '{0}'.", line));
            }

            if (position < line.Length)
            {
                var settings = line.Substring(position).TrimStart('\t', ' ').TrimEnd('\t', ' ');
                if (false == string.IsNullOrWhiteSpace(settings))
                {
                    result.RawSettings = settings;

                    var parsedSettings = WebVttParser.ParseSettings(settings);
                    if (parsedSettings != null)
                    {
                        VerticalTextLayout vertical;
                        if (WebVttParser.TryGetVerticalTextLayoutSetting(Constants.VerticalName, parsedSettings, out vertical))
                        {
                            result.Vertical = vertical;
                        }

                        LineSettings lineSetting;
                        if (WebVttParser.TryGetLineSetting(Constants.LineName, parsedSettings, out lineSetting))
                        {
                            result.Line = lineSetting;
                        }

                        PositionSettings positionSetting;
                        if (WebVttParser.TryGetPositionSetting(Constants.PositionName, parsedSettings, out positionSetting))
                        {
                            result.Position = positionSetting;
                        }

                        double percent;
                        if (WebVttParser.TryGetPercentSetting(Constants.SizeName, parsedSettings, out percent))
                        {
                            result.SizePercent = percent;
                        }

                        TextAlignment alignment;
                        if (WebVttParser.TryGetAlignmentSetting(Constants.AlignName, parsedSettings, out alignment))
                        {
                            result.Alignment = alignment;
                        }

                        string name;
                        if (WebVttParser.TryGetStringSetting(Constants.RegionName, parsedSettings, out name))
                        {
                            result.Region = name;
                        }
                    }
                }
            }

            while (false == string.IsNullOrEmpty(line))
            {
                line = await reader.ReadLineAsync()
                       .ConfigureAwait(false);

                if (false == string.IsNullOrEmpty(line))
                {
                    if (line.Contains(Constants.ArrowToken))
                    {
                        throw new InvalidDataException(string.Format("Cue must not contain '{0}'.", Constants.ArrowToken));
                    }

                    content.SafeAppendLine(line);
                }
            }

            if (content.Length > 0)
            {
                result.RawContent = content.ToString();

                List <Span> spans;

                position = 0;
                if (WebVttParser.TryParseSpans(result.RawContent, ref position, out spans) &&
                    spans != null &&
                    spans.Count > 0)
                {
                    result.Content = spans.ToArray();
                }
            }

            return(result);
        }
예제 #10
0
        /// <summary>
        /// Reads WebVTT Region definition block from the stream.
        /// </summary>
        /// <param name="firstLine">First line of the block read from the reader.</param>
        /// <param name="reader">Text reader to read the region block.</param>
        /// <returns>A task that represents the asynchronous read operation.</returns>
        private static async Task <RegionDefinition> ReadRegionAsync(
            string firstLine,
            TextReader reader)
        {
            string line = firstLine;

            if (line.Length > Constants.RegionToken.Length &&
                false == string.IsNullOrWhiteSpace(line.Substring(Constants.RegionToken.Length)))
            {
                throw new InvalidDataException(string.Format("Invalid characters found after region definition header: {0}", line));
            }

            var content = new StringBuilder(100);

            while (false == string.IsNullOrEmpty(line))
            {
                line = await reader.ReadLineAsync()
                       .ConfigureAwait(false);

                if (false == string.IsNullOrEmpty(line))
                {
                    if (line.Contains(Constants.ArrowToken))
                    {
                        throw new InvalidDataException(string.Format("Region definition must not contain '{0}'.", Constants.ArrowToken));
                    }

                    content.SafeAppendLine(line);
                }
            }

            var result = new RegionDefinition()
            {
                RawContent = content.Length > 0 ? content.ToString() : null
            };

            if (result.RawContent != null)
            {
                var settings = WebVttParser.ParseSettings(result.RawContent);

                if (settings != null)
                {
                    string value;
                    if (WebVttParser.TryGetStringSetting(Constants.RegionIdName, settings, out value))
                    {
                        result.Id = value;
                    }

                    double percent;
                    if (WebVttParser.TryGetPercentSetting(Constants.WidthName, settings, out percent))
                    {
                        result.WidthPercent = percent;
                    }

                    Anchor anchor;
                    if (WebVttParser.TryGetAnchorSetting(Constants.RegionAnchorName, settings, out anchor))
                    {
                        result.RegionAnchor = anchor;
                    }

                    if (WebVttParser.TryGetAnchorSetting(Constants.ViewPortAnchorName, settings, out anchor))
                    {
                        result.ViewPortAnchor = anchor;
                    }

                    if (WebVttParser.TryGetStringSetting(Constants.ScrollName, settings, out value))
                    {
                        result.Scroll = string.Equals(value, Constants.ScrollUpValue, StringComparison.OrdinalIgnoreCase);
                    }

                    int lines;
                    if (WebVttParser.TryGetIntSetting(Constants.LinesName, settings, out lines))
                    {
                        result.Lines = lines;
                    }
                }
            }

            return(result);
        }
예제 #11
0
        /// <summary>
        /// Utility method to parse cue content and create a span syntax tree.
        /// </summary>
        /// <param name="input">Input string to process.</param>
        /// <param name="position">Position in the string to begin parsing.</param>
        /// <param name="spans">If successful, contains the list of spans parsed from the input.</param>
        /// <returns>True if successfully parsed spans from the input; otherwise false.</returns>
        private static bool TryParseSpans(
            string input,
            ref int position,
            out List <Span> spans)
        {
            spans = null;

            if (string.IsNullOrEmpty(input))
            {
                return(false);
            }

            List <Span> result    = null;
            int         textStart = position;

            for (; position < input.Length; position++)
            {
                char c    = input[position];
                bool last = position == input.Length - 1;

                if (c == '<' || last || c == '\r' || c == '\n')
                {
                    if (textStart < position || last && c != '<')
                    {
                        WebVttParser.SafeAddSpan(
                            new Span()
                        {
                            Type = SpanType.Text,
                            Text = input.Substring(textStart, position - textStart + (last ? 1 : 0))
                        },
                            ref result);
                    }

                    if (false == last)
                    {
                        if (c == '<' && input[position + 1] == '/')
                        {
                            break;
                        }

                        if (c == '<')
                        {
                            Span span;
                            if (false == WebVttParser.TryParseSpan(input, ref position, out span))
                            {
                                return(false);
                            }

                            WebVttParser.SafeAddSpan(span, ref result);
                        }

                        textStart = position + 1;
                    }
                    else if (c == '<')
                    {
                        return(false);
                    }
                }
            }

            spans = result;
            return(true);
        }