mirror of
				https://github.com/iv-org/invidious.git
				synced 2025-11-04 06:31:57 +00:00 
			
		
		
		
	WebVTT::Builder: Add logic to escape special chars (#4414)
Note: WebVTT does allow some tags in the cue payload in some circumstances while this PR just blindly escapes everything: https://developer.mozilla.org/en-US/docs/Web/API/WebVTT_API#cue_payload_text_tags
This commit is contained in:
		@@ -1,34 +1,27 @@
 | 
			
		||||
require "../../spec_helper.cr"
 | 
			
		||||
 | 
			
		||||
MockLines = [
 | 
			
		||||
  {
 | 
			
		||||
    "start_time": Time::Span.new(seconds: 1),
 | 
			
		||||
    "end_time":   Time::Span.new(seconds: 2),
 | 
			
		||||
    "text":       "Line 1",
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    "start_time": Time::Span.new(seconds: 2),
 | 
			
		||||
    "end_time":   Time::Span.new(seconds: 3),
 | 
			
		||||
    "text":       "Line 2",
 | 
			
		||||
  },
 | 
			
		||||
]
 | 
			
		||||
MockLines                       = ["Line 1", "Line 2"]
 | 
			
		||||
MockLinesWithEscapableCharacter = ["<Line 1>", "&Line 2>", '\u200E' + "Line\u200F 3", "\u00A0Line 4"]
 | 
			
		||||
 | 
			
		||||
Spectator.describe "WebVTT::Builder" do
 | 
			
		||||
  it "correctly builds a vtt file" do
 | 
			
		||||
    result = WebVTT.build do |vtt|
 | 
			
		||||
      MockLines.each do |line|
 | 
			
		||||
        vtt.cue(line["start_time"], line["end_time"], line["text"])
 | 
			
		||||
      2.times do |i|
 | 
			
		||||
        vtt.cue(
 | 
			
		||||
          Time::Span.new(seconds: i),
 | 
			
		||||
          Time::Span.new(seconds: i + 1),
 | 
			
		||||
          MockLines[i]
 | 
			
		||||
        )
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    expect(result).to eq([
 | 
			
		||||
      "WEBVTT",
 | 
			
		||||
      "",
 | 
			
		||||
      "00:00:01.000 --> 00:00:02.000",
 | 
			
		||||
      "00:00:00.000 --> 00:00:01.000",
 | 
			
		||||
      "Line 1",
 | 
			
		||||
      "",
 | 
			
		||||
      "00:00:02.000 --> 00:00:03.000",
 | 
			
		||||
      "00:00:01.000 --> 00:00:02.000",
 | 
			
		||||
      "Line 2",
 | 
			
		||||
      "",
 | 
			
		||||
      "",
 | 
			
		||||
@@ -42,8 +35,12 @@ Spectator.describe "WebVTT::Builder" do
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    result = WebVTT.build(setting_fields) do |vtt|
 | 
			
		||||
      MockLines.each do |line|
 | 
			
		||||
        vtt.cue(line["start_time"], line["end_time"], line["text"])
 | 
			
		||||
      2.times do |i|
 | 
			
		||||
        vtt.cue(
 | 
			
		||||
          Time::Span.new(seconds: i),
 | 
			
		||||
          Time::Span.new(seconds: i + 1),
 | 
			
		||||
          MockLines[i]
 | 
			
		||||
        )
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@@ -52,13 +49,39 @@ Spectator.describe "WebVTT::Builder" do
 | 
			
		||||
      "Kind: captions",
 | 
			
		||||
      "Language: en",
 | 
			
		||||
      "",
 | 
			
		||||
      "00:00:01.000 --> 00:00:02.000",
 | 
			
		||||
      "00:00:00.000 --> 00:00:01.000",
 | 
			
		||||
      "Line 1",
 | 
			
		||||
      "",
 | 
			
		||||
      "00:00:02.000 --> 00:00:03.000",
 | 
			
		||||
      "00:00:01.000 --> 00:00:02.000",
 | 
			
		||||
      "Line 2",
 | 
			
		||||
      "",
 | 
			
		||||
      "",
 | 
			
		||||
    ].join('\n'))
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  it "properly escapes characters" do
 | 
			
		||||
    result = WebVTT.build do |vtt|
 | 
			
		||||
      4.times do |i|
 | 
			
		||||
        vtt.cue(Time::Span.new(seconds: i), Time::Span.new(seconds: i + 1), MockLinesWithEscapableCharacter[i])
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    expect(result).to eq([
 | 
			
		||||
      "WEBVTT",
 | 
			
		||||
      "",
 | 
			
		||||
      "00:00:00.000 --> 00:00:01.000",
 | 
			
		||||
      "<Line 1>",
 | 
			
		||||
      "",
 | 
			
		||||
      "00:00:01.000 --> 00:00:02.000",
 | 
			
		||||
      "&Line 2>",
 | 
			
		||||
      "",
 | 
			
		||||
      "00:00:02.000 --> 00:00:03.000",
 | 
			
		||||
      "‎Line‏ 3",
 | 
			
		||||
      "",
 | 
			
		||||
      "00:00:03.000 --> 00:00:04.000",
 | 
			
		||||
      " Line 4",
 | 
			
		||||
      "",
 | 
			
		||||
      "",
 | 
			
		||||
    ].join('\n'))
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 
 | 
			
		||||
@@ -4,13 +4,23 @@
 | 
			
		||||
module WebVTT
 | 
			
		||||
  # A WebVTT builder generates WebVTT files
 | 
			
		||||
  private class Builder
 | 
			
		||||
    # See https://developer.mozilla.org/en-US/docs/Web/API/WebVTT_API#cue_payload
 | 
			
		||||
    private ESCAPE_SUBSTITUTIONS = {
 | 
			
		||||
      '&'      => "&",
 | 
			
		||||
      '<'      => "<",
 | 
			
		||||
      '>'      => ">",
 | 
			
		||||
      '\u200E' => "‎",
 | 
			
		||||
      '\u200F' => "‏",
 | 
			
		||||
      '\u00A0' => " ",
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def initialize(@io : IO)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # Writes an vtt cue with the specified time stamp and contents
 | 
			
		||||
    def cue(start_time : Time::Span, end_time : Time::Span, text : String)
 | 
			
		||||
      timestamp(start_time, end_time)
 | 
			
		||||
      @io << text
 | 
			
		||||
      @io << self.escape(text)
 | 
			
		||||
      @io << "\n\n"
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
@@ -29,6 +39,10 @@ module WebVTT
 | 
			
		||||
      @io << '.' << timestamp.milliseconds.to_s.rjust(3, '0')
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    private def escape(text : String) : String
 | 
			
		||||
      return text.gsub(ESCAPE_SUBSTITUTIONS)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def document(setting_fields : Hash(String, String)? = nil, &)
 | 
			
		||||
      @io << "WEBVTT\n"
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user