require 'srt' describe SRT do describe SRT::Line do describe "#new" do let(:line) { SRT::Line.new } it "should create an empty subtitle" do line.should be_empty end end describe "#time_str" do let(:line) { SRT::Line.new } before do line.start_time = 224.2 line.end_time = 244.578 end it "should produce timecodes that match the internal float values" do line.time_str.should eq("00:03:44,200 --> 00:04:04,578") end end end describe SRT::File do describe ".parse_id" do it "should convert the id string (#[id]) to an int representing the sequence number" do SRT::File.parse_id("#317").should eq(317) end end describe ".parse_timecode" do it "should convert the SRT timecode format to a float representing seconds" do SRT::File.parse_timecode("01:03:44,200").should eq(3824.2) end end describe ".parse_timespan" do it "should convert a timespan string ([+|-][amount][h|m|s|ms]) to a float representing seconds" do SRT::File.parse_timespan("-3.5m").should eq(-210) end it "should convert a timespan string ([+|-][amount][h|m|s|ms]) to a float representing seconds" do SRT::File.parse_timespan("-1s").should eq(-1) end it "should convert a timespan string ([+|-][amount][h|m|s|ms]) to a float representing seconds" do SRT::File.parse_timespan("100ms").should eq(0.1) end end describe ".parse_framerate" do it "should convert a framerate string ([number]fps) to a float representing seconds" do SRT::File.parse_framerate("23.976fps").should eq(23.976) end end shared_examples_for "an SRT file" do context "when parsing a properly formatted BSG SRT file" do it "should return an SRT::File" do subject.class.should eq(SRT::File) end it "should have 600 lines" do subject.lines.size.should eq(600) end it "should have no errors" do subject.errors.should be_empty end it "should have the expected sequence number on the first subtitle" do subject.lines.first.sequence.should eq(1) end it "should have the expected timecodes on the first subtitle" do subject.lines.first.time_str.should eq("00:00:02,110 --> 00:00:04,578") end it "should have the expected text on the first subtitle" do subject.lines.first.text.should eq(["(male narrator) Previously", "on Battlestar Galactica."]) end it "should have the expected sequence number on the last subtitle" do subject.lines.last.sequence.should eq(600) end it "should have the expected timecodes on the last subtitle" do subject.lines.last.time_str.should eq("00:43:26,808 --> 00:43:28,139") end it "should have the expected text on the last subtitle" do subject.lines.last.text.should eq(["Thank you."]) end end end describe ".parse with uncommon formats" do context "when parsing a spanish language WOTW SRT file with unknown encoding" do let(:file) { SRT::File.parse(File.open("./spec/wotw-dubious.srt")) } it "should parse" do file.class.should eq(SRT::File) end it "should have 1123 lines" do file.lines.size.should eq(1123) end it "should have no errors" do file.errors.should be_empty end end context "when parsing a dummy SRT file containing display coordinates" do let(:file) { SRT::File.parse(File.open("./spec/coordinates-dummy.srt")) } it "should return an SRT::File" do file.class.should eq(SRT::File) end it "should have 3 lines" do file.lines.size.should eq(3) end it "should have no errors" do file.errors.should be_empty end it "should have the expected display coordinates on the first subtitle" do file.lines.first.display_coordinates.should eq("X1:100 X2:600 Y1:1 Y2:4") end it "should have the expected display coordinates on the last subtitle" do file.lines.last.display_coordinates.should eq("X1:1 X2:333 Y1:50 Y2:29") end end end describe SRT::File, "when initialized with a valid BSG SRT string" do subject { SRT::File.parse(File.read("./spec/bsg-s01e01.srt")) } it_should_behave_like "an SRT file" end describe SRT::File, "when initialized with a valid BSG SRT File" do subject { SRT::File.parse(File.open("./spec/bsg-s01e01.srt")) } it_should_behave_like "an SRT file" end describe "#append" do context "when calling it on the first (part1) of two seperate SRT files for Black Swan" do let(:part1) { SRT::File.parse(File.open("./spec/blackswan-part1.srt")) } let(:part2) { SRT::File.parse(File.open("./spec/blackswan-part2.srt")) } context "when passing { \"00:53:57,241\" => part2 }" do before { part1.append({ "00:53:57,241" => part2 }) } it "should have grown to 808 subtitles" do part1.lines.length.should eq(808) end it "should have appended subtitles starting with sequence number 448" do part1.lines[447].sequence.should eq(448) end it "should have appended subtitles ending with sequence number 808" do part1.lines.last.sequence.should eq(808) end it "should have appended subtitles relatively from 00:53:57,241" do part1.lines[447].time_str.should eq("00:54:02,152 --> 00:54:04,204") end end context "when passing { \"+7.241s\" => part2 }" do before { part1.append({ "+7.241s" => part2 }) } it "should have appended subtitles relatively from +7.241s after the previously last subtitle" do part1.lines[447].time_str.should eq("00:54:02,283 --> 00:54:04,335") end end end end describe "#split" do context "when calling it on a properly formatted BSG SRT file" do let(:file) { SRT::File.parse(File.open("./spec/bsg-s01e01.srt")) } context "when passing { :at => \"00:19:24,500\" }" do let(:result) { file.split( :at => "00:19:24,500" ) } it "should return an array containing two SRT::File instances" do result.length.should eq(2) result[0].class.should eq(SRT::File) result[1].class.should eq(SRT::File) end it "should include a subtitle that overlaps a splitting point in the first file" do result[0].lines.last.text.should eq(["I'll see you guys in combat."]) end it "should make an overlapping subtitle end at the splitting point in the first file" do result[0].lines.last.time_str.should eq("00:19:23,901 --> 00:19:24,500") end it "should include a subtitle that overlaps a splitting point in the second file as well" do result[1].lines.first.text.should eq(["I'll see you guys in combat."]) end it "should make an overlapping subtitle remain at the beginning in the second file" do result[1].lines.first.time_str.should eq("00:00:00,000 --> 00:00:01,528") end it "should shift back all timecodes of the second file relative to the new file beginning" do result[1].lines[1].time_str.should eq("00:00:01,737 --> 00:00:03,466") end end context "when passing { :at => \"00:19:24,500\", :timeshift => false }" do let(:result) { file.split( :at => "00:19:24,500", :timeshift => false ) } it "should return an array containing two SRT::File instances" do result.length.should eq(2) result[0].class.should eq(SRT::File) result[1].class.should eq(SRT::File) end it "should include a subtitle that overlaps a splitting point in the first file" do result[0].lines.last.text.should eq(["I'll see you guys in combat."]) end it "should not make an overlapping subtitle end at the splitting point in the first file" do result[0].lines.last.time_str.should eq("00:19:23,901 --> 00:19:26,028") end it "should include a subtitle that overlaps a splitting point in the second file as well" do result[1].lines.first.text.should eq(["I'll see you guys in combat."]) end it "should not make an overlapping subtitle remain at the beginning in the second file" do result[1].lines.first.time_str.should eq("00:19:23,901 --> 00:19:26,028") end it "should not shift back timecodes of the second file relative to the new file beginning" do result[1].lines[1].time_str.should eq("00:19:26,237 --> 00:19:27,966") end end context "when passing { :at => [\"00:15:00,000\", \"00:30:00,000\"] }" do let(:result) { file.split( :at => ["00:15:00,000", "00:30:00,000"] ) } it "should return an array containing three SRT::File instances" do result.length.should eq(3) result[0].class.should eq(SRT::File) result[1].class.should eq(SRT::File) result[2].class.should eq(SRT::File) end it "should let subtitles start at sequence number #1 in all three files" do result[0].lines.first.sequence.should eq(1) result[1].lines.first.sequence.should eq(1) result[2].lines.first.sequence.should eq(1) end it "should put 176 subtitles in the first file" do result[0].lines.length.should eq(176) result[0].lines.last.sequence.should eq(176) end it "should put 213 subtitles in the second file" do result[1].lines.length.should eq(213) result[1].lines.last.sequence.should eq(213) end it "should put 212 subtitles in the third file" do result[2].lines.length.should eq(212) result[2].lines.last.sequence.should eq(212) end end end end describe "#timeshift" do context "when calling it on a properly formatted BSG SRT file" do let(:file) { SRT::File.parse(File.open("./spec/bsg-s01e01.srt")) } context "when passing { :all => \"+2.5s\" }" do before { file.timeshift({ :all => "+2.5s" }) } it "should have timecodes shifted forward by 2.5s for subtitle #24" do file.lines[23].time_str.should eq("00:01:59,291 --> 00:02:00,815") end it "should have timecodes shifted forward by 2.5s for subtitle #43" do file.lines[42].time_str.should eq("00:03:46,164 --> 00:03:47,631") end end context "when passing { \"25fps\" => \"23.976fps\" }" do before { file.timeshift({ "25fps" => "23.976fps" }) } it "should have correctly scaled timecodes for subtitle #24" do file.lines[23].time_str.should eq("00:01:52,007 --> 00:01:53,469") end it "should have correctly scaled timecodes for subtitle #43" do file.lines[42].time_str.should eq("00:03:34,503 --> 00:03:35,910") end end context "when passing { \"#24\" => \"00:03:53,582\", \"#42\" => \"00:04:24,656\" }" do before { file.timeshift({ "#24" => "00:03:53,582", "#42" => "00:04:24,656" }) } it "should have shifted timecodes for subtitle #24" do file.lines[23].time_str.should eq("00:03:53,582 --> 00:03:54,042") end it "should have differently shifted timecodes for subtitle #43" do file.lines[41].time_str.should eq("00:04:24,656 --> 00:04:25,298") end end context "when passing { 180 => \"+1s\", 264 => \"+1.5s\" }" do before { file.timeshift({ 180 => "+1s", 264 => "+1.5s" }) } it "should have shifted by +1s at 180 seconds" do file.lines[23].time_str.should eq("00:01:57,415 --> 00:01:58,948") end it "should have shifted by +1.5s at 264 seconds" do file.lines[41].time_str.should eq("00:03:40,997 --> 00:03:43,136") end end end context "when calling it on a spanish language WOTW SRT file with unknown encoding" do let(:file) { SRT::File.parse(File.open("./spec/wotw-dubious.srt")) } context "when passing { :all => \"-2.7m\" }" do before { file.timeshift({ :all => "-2.7m" }) } it "should have dumped 16 lines with now negative timecodes, leaving 1107" do file.lines.size.should eq(1107) end end context "when passing { \"00:03:25,430\" => \"00:00:44,200\", \"01:49:29,980\" => \"01:46:35,600\" }" do before { file.timeshift({ "00:03:25,430" => "00:00:44,200", "01:49:29,980" => "01:46:35,600" }) } it "should have dumped 16 lines with now negative timecodes, leaving 1107" do file.lines.size.should eq(1107) end end end describe "#to_s" do context "when calling it on a short SRT file" do let(:file) { SRT::File.parse(File.open("./spec/bsg-s01e01.srt")) } before { file.lines = file.lines[0..2] } it "should produce the exactly correct output" do OUTPUT =< 00:00:04,578 (male narrator) Previously on Battlestar Galactica. 2 00:00:05,313 --> 00:00:06,871 Now you're telling me you're a machine. 3 00:00:07,014 --> 00:00:08,003 The robot. END file.to_s.should eq(OUTPUT) end end end end end end