From 3af978d2d688cb1e4be7c8d95155a6b8cc7456a3 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sat, 11 Jan 2014 14:04:58 +0000 Subject: Various work on SubRip. --- src/lib/content_factory.cc | 8 ++ src/lib/exceptions.cc | 8 +- src/lib/exceptions.h | 7 ++ src/lib/subrip.cc | 215 +++++++++++++++++++++++++++++++++++++++++++++ src/lib/subrip.h | 43 +++++++++ src/lib/subrip_content.cc | 93 ++++++++++++++++++++ src/lib/subrip_content.h | 35 ++++++++ src/lib/subrip_subtitle.h | 58 ++++++++++++ src/lib/wscript | 2 + 9 files changed, 468 insertions(+), 1 deletion(-) create mode 100644 src/lib/subrip.cc create mode 100644 src/lib/subrip.h create mode 100644 src/lib/subrip_content.cc create mode 100644 src/lib/subrip_content.h create mode 100644 src/lib/subrip_subtitle.h (limited to 'src') diff --git a/src/lib/content_factory.cc b/src/lib/content_factory.cc index bab22b8eb..825c80498 100644 --- a/src/lib/content_factory.cc +++ b/src/lib/content_factory.cc @@ -21,6 +21,7 @@ #include "ffmpeg_content.h" #include "image_content.h" #include "sndfile_content.h" +#include "subrip_content.h" #include "util.h" using std::string; @@ -39,6 +40,8 @@ content_factory (shared_ptr film, cxml::NodePtr node, int version) content.reset (new ImageContent (film, node, version)); } else if (type == "Sndfile") { content.reset (new SndfileContent (film, node, version)); + } else if (type == "SubRip") { + content.reset (new SubRipContent (film, node, version)); } return content; @@ -48,11 +51,16 @@ shared_ptr content_factory (shared_ptr film, boost::filesystem::path path) { shared_ptr content; + + string ext = path.extension().string (); + transform (ext.begin(), ext.end(), ext.begin(), ::tolower); if (valid_image_file (path)) { content.reset (new ImageContent (film, path)); } else if (SndfileContent::valid_file (path)) { content.reset (new SndfileContent (film, path)); + } else if (ext == ".srt") { + content.reset (new SubRipContent (film, path)); } else { content.reset (new FFmpegContent (film, path)); } diff --git a/src/lib/exceptions.cc b/src/lib/exceptions.cc index 8144f41b9..e05ac4ff0 100644 --- a/src/lib/exceptions.cc +++ b/src/lib/exceptions.cc @@ -56,8 +56,14 @@ MissingSettingError::MissingSettingError (string s) } -PixelFormatError::PixelFormatError (std::string o, AVPixelFormat f) +PixelFormatError::PixelFormatError (string o, AVPixelFormat f) : StringError (String::compose (_("Cannot handle pixel format %1 during %2"), f, o)) { } + +SubRipError::SubRipError (string saw, string expecting, boost::filesystem::path f) + : FileError (String::compose (_("Error in SubRip file: saw %1 while expecting %2"), saw, expecting), f) +{ + +} diff --git a/src/lib/exceptions.h b/src/lib/exceptions.h index c1240538f..61163c8d1 100644 --- a/src/lib/exceptions.h +++ b/src/lib/exceptions.h @@ -230,6 +230,13 @@ public: PixelFormatError (std::string o, AVPixelFormat f); }; +/** An error that occurs while parsing a SubRip file */ +class SubRipError : public FileError +{ +public: + SubRipError (std::string, std::string, boost::filesystem::path); +}; + /** A parent class for classes which have a need to catch and * re-throw exceptions. This is intended for classes * which run their own thread; they should do something like diff --git a/src/lib/subrip.cc b/src/lib/subrip.cc new file mode 100644 index 000000000..50931e12a --- /dev/null +++ b/src/lib/subrip.cc @@ -0,0 +1,215 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include "subrip.h" +#include "subrip_content.h" +#include "subrip_subtitle.h" +#include "cross.h" +#include "exceptions.h" + +#include "i18n.h" + +using std::string; +using std::list; +using std::vector; +using boost::shared_ptr; +using boost::lexical_cast; +using boost::algorithm::trim; + +SubRip::SubRip (shared_ptr content) +{ + FILE* f = fopen_boost (content->path (0), "r"); + if (!f) { + throw OpenFileError (content->path (0)); + } + + enum { + COUNTER, + METADATA, + CONTENT + } state = COUNTER; + + char buffer[256]; + int next_count = 1; + + boost::optional current; + list lines; + + while (!feof (f)) { + fgets (buffer, sizeof (buffer), f); + string line (buffer); + trim (line); + + switch (state) { + case COUNTER: + { + int x = 0; + try { + x = lexical_cast (line); + } catch (...) { + + } + + if (x == next_count) { + state = METADATA; + ++next_count; + current = SubRipSubtitle (); + } else { + throw SubRipError (line, _("a subtitle count"), content->path (0)); + } + } + break; + case METADATA: + { + vector p; + boost::algorithm::split (p, line, boost::algorithm::is_any_of (" ")); + if (p.size() != 3 && p.size() != 7) { + throw SubRipError (line, _("a time/position line"), content->path (0)); + } + + current->from = convert_time (p[0]); + current->to = convert_time (p[2]); + + if (p.size() > 3) { + current->x1 = convert_coordinate (p[3]); + current->x2 = convert_coordinate (p[4]); + current->y1 = convert_coordinate (p[5]); + current->y2 = convert_coordinate (p[6]); + } + break; + } + case CONTENT: + if (line.empty ()) { + state = COUNTER; + current->pieces = convert_content (lines); + _subtitles.push_back (current.get ()); + current.reset (); + lines.clear (); + } else { + lines.push_back (line); + } + break; + } + } + + fclose (f); +} + +Time +SubRip::convert_time (string t) +{ + Time r = 0; + + vector a; + boost::algorithm::split (a, t, boost::is_any_of (":")); + assert (a.size() == 3); + r += lexical_cast (a[0]) * 60 * 60 * TIME_HZ; + r += lexical_cast (a[1]) * 60 * TIME_HZ; + + vector b; + boost::algorithm::split (b, a[2], boost::is_any_of (",")); + r += lexical_cast (b[0]) * TIME_HZ; + r += lexical_cast (b[1]) * TIME_HZ / 1000; + + return r; +} + +int +SubRip::convert_coordinate (string t) +{ + vector a; + boost::algorithm::split (a, t, boost::is_any_of (":")); + assert (a.size() == 2); + return lexical_cast (a[1]); +} + +void +SubRip::maybe_content (list& pieces, SubRipSubtitlePiece& p) +{ + if (!p.text.empty ()) { + pieces.push_back (p); + p.text.clear (); + } +} + +list +SubRip::convert_content (list t) +{ + list pieces; + + SubRipSubtitlePiece p; + + enum { + TEXT, + TAG + } state = TEXT; + + string tag; + + /* XXX: missing support */ + /* XXX: nesting of tags e.g. foobarbazfredjim might + not work, I think. + */ + + for (list::const_iterator i = t.begin(); i != t.end(); ++i) { + for (size_t j = 0; j < i->size(); ++j) { + switch (state) { + case TEXT: + if ((*i)[j] == '<' || (*i)[j] == '{') { + state = TAG; + } else { + p.text += (*i)[j]; + } + break; + case TAG: + if ((*i)[j] == '>' || (*i)[j] == '}') { + if (tag == "b") { + maybe_content (pieces, p); + p.bold = true; + } else if (tag == "/b") { + maybe_content (pieces, p); + p.bold = false; + } else if (tag == "i") { + maybe_content (pieces, p); + p.italic = true; + } else if (tag == "/i") { + maybe_content (pieces, p); + p.italic = false; + } else if (tag == "u") { + maybe_content (pieces, p); + p.underline = true; + } else if (tag == "/u") { + maybe_content (pieces, p); + p.underline = false; + } + tag.clear (); + state = TEXT; + } else { + tag += (*i)[j]; + } + break; + } + } + } + + maybe_content (pieces, p); + + return pieces; +} diff --git a/src/lib/subrip.h b/src/lib/subrip.h new file mode 100644 index 000000000..a8d8104c4 --- /dev/null +++ b/src/lib/subrip.h @@ -0,0 +1,43 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "subrip_subtitle.h" + +class SubRipContent; +class subrip_time_test; +class subrip_coordinate_test; +class subrip_content_test; + +class SubRip +{ +public: + SubRip (boost::shared_ptr); + +private: + friend class subrip_time_test; + friend class subrip_coordinate_test; + friend class subrip_content_test; + + static Time convert_time (std::string); + static int convert_coordinate (std::string); + static std::list convert_content (std::list); + static void maybe_content (std::list &, SubRipSubtitlePiece &); + + std::list _subtitles; +}; diff --git a/src/lib/subrip_content.cc b/src/lib/subrip_content.cc new file mode 100644 index 000000000..79a1d4999 --- /dev/null +++ b/src/lib/subrip_content.cc @@ -0,0 +1,93 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "subrip_content.h" +#include "util.h" + +#include "i18n.h" + +using std::stringstream; +using std::string; +using boost::shared_ptr; + +SubRipContent::SubRipContent (shared_ptr film, boost::filesystem::path path) + : Content (film, path) + , SubtitleContent (film, path) +{ + +} + +SubRipContent::SubRipContent (shared_ptr film, shared_ptr node, int version) + : Content (film, node) + , SubtitleContent (film, node) +{ + +} + +void +SubRipContent::examine (boost::shared_ptr) +{ + +} + +string +SubRipContent::summary () const +{ + return path_summary() + " " + _("[subtitles]"); +} + +string +SubRipContent::technical_summary () const +{ + return Content::technical_summary() + " - " + _("SubRip subtitles"); +} + +string +SubRipContent::information () const +{ + +} + +void +SubRipContent::as_xml (xmlpp::Node* node) +{ + node->add_child("Type")->add_child_text ("SubRip"); + Content::as_xml (node); + SubtitleContent::as_xml (node); +} + +Time +SubRipContent::full_length () const +{ + +} + +string +SubRipContent::identifier () const +{ + LocaleGuard lg; + + stringstream s; + s << Content::identifier() + << "_" << subtitle_scale() + << "_" << subtitle_offset(); + + return s.str (); +} + diff --git a/src/lib/subrip_content.h b/src/lib/subrip_content.h new file mode 100644 index 000000000..1551081b6 --- /dev/null +++ b/src/lib/subrip_content.h @@ -0,0 +1,35 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "subtitle_content.h" + +class SubRipContent : public SubtitleContent +{ +public: + SubRipContent (boost::shared_ptr, boost::filesystem::path); + SubRipContent (boost::shared_ptr, boost::shared_ptr, int); + + void examine (boost::shared_ptr); + std::string summary () const; + std::string technical_summary () const; + std::string information () const; + void as_xml (xmlpp::Node *); + Time full_length () const; + std::string identifier () const; +}; diff --git a/src/lib/subrip_subtitle.h b/src/lib/subrip_subtitle.h new file mode 100644 index 000000000..933e0fc02 --- /dev/null +++ b/src/lib/subrip_subtitle.h @@ -0,0 +1,58 @@ +/* + Copyright (C) 2014 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef DCPOMATIC_SUBRIP_SUBTITLE_H +#define DCPOMATIC_SUBRIP_SUBTITLE_H + +#include +#include +#include "types.h" + +struct SubRipSubtitlePiece +{ + SubRipSubtitlePiece () + : bold (false) + , italic (false) + , underline (false) + {} + + std::string text; + bool bold; + bool italic; + bool underline; + libdcp::Color color; +}; + +struct SubRipSubtitle +{ + SubRipSubtitle () + : from (0) + , to (0) + {} + + Time from; + Time to; + boost::optional x1; + boost::optional x2; + boost::optional y1; + boost::optional y2; + std::list pieces; +}; + +#endif diff --git a/src/lib/wscript b/src/lib/wscript index 81a55a160..284baa725 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -50,6 +50,8 @@ sources = """ sndfile_content.cc sndfile_decoder.cc sound_processor.cc + subrip.cc + subrip_content.cc subtitle_content.cc subtitle_decoder.cc timer.cc -- cgit v1.2.3