add webvtt read support, remove NOTE from webvtt write, some fixes, cleanup
authorNiki Roo <niki@nikiroo.be>
Mon, 16 May 2022 17:11:16 +0000 (19:11 +0200)
committerNiki Roo <niki@nikiroo.be>
Mon, 16 May 2022 17:11:16 +0000 (19:11 +0200)
README-fr.md
README.md
src/nsub/nsub.c
src/nsub/nsub.h
src/nsub/nsub_read_lrc.c
src/nsub/nsub_read_srt.c
src/nsub/nsub_read_webvtt.c [new file with mode: 0644]
src/nsub/nsub_write_lrc.c
src/nsub/nsub_write_srt.c
src/nsub/nsub_write_webvtt.c

index e665ab6e8dd1ebbaaaa625b51d58249bda233e95..4896417f2033b6dd7439656a8736f82475de5c47 100644 (file)
@@ -25,8 +25,6 @@ Il ne nécessite pas de librairies externes.
 - `SRT` fichiers sous-titres SubRip, ils accompagnent en général des films
 - `WebVTT` Web Video Text Tracks, un nouveau standard W3C
 
-Note : ce programme ne peut pas encore ouvrir des fichiers WebVTT (il supporte toutefois les 3 formats en écriture)
-
 ## Options
 
 - **--help** (or **-h**) : information sur la syntaxe du programme
index 7642dcc3b64b2fec4b556ddb310be4da876152a3..1468cd7fa5421df5d04cc3cf23b1ebda5e81a04c 100644 (file)
--- a/README.md
+++ b/README.md
@@ -25,8 +25,6 @@ It does not require external libraries.
 - `SRT` SubRip subtitle files, usually distributed with films
 - `WebVTT` Web Video Text Tracks, a new W3C standard
 
-Note: this program can not yet open WebVTT files (it supports all 3 formats as output, though)
-
 ## Options
 
 - **--help** (or **-h**): information about the syntax
index 221aeb17e75229aad9fb93badfc7df2e57aa602a..3df4da0c34c9f9f51e9fd1113c59c4dfd496d5ae 100644 (file)
@@ -115,6 +115,9 @@ song_t *nsub_read(FILE *in, NSUB_FORMAT fmt) {
        case NSUB_FMT_SRT:
                read_a_line = nsub_read_srt;
                break;
+       case NSUB_FMT_WEBVTT:
+               read_a_line = nsub_read_webvtt;
+               break;
        default:
                fprintf(stderr, "Unsupported read format %d\n", fmt);
                goto fail;
@@ -153,3 +156,100 @@ int nsub_write(FILE *out, song_t *song, NSUB_FORMAT fmt, int apply_offset) {
                return 0;
        }
 }
+
+int nsub_to_ms(const char line[], char deci_sym) {
+       // 00:00:17,400
+
+       /* should not happen! */
+       /* note: also, we assume max 3 decimal digits */
+       if (!nsub_is_timing(line, deci_sym, 3)) {
+               fprintf(stderr,
+                               "Warning: called nsub_to_ms with bad input [%s], ignoring...\n",
+                               line);
+               return 0;
+       }
+
+       int mults[] = { 1, 1000, 60000, 3600000 };
+
+       int group[4] = { 0, 0, 0, 0 };
+       int igroup = -1;
+
+       char mtmp[3] = { 1, 10, 100 };
+       int itmp = 0;
+
+       int has_milli = 0;
+
+       size_t end = strlen(line) - 1;
+
+       for (size_t i = end; i >= 0; i--) {
+               char car = line[i];
+
+               int digit = (car >= '0' && car <= '9');
+               int dot = car == deci_sym;
+               int col = (car == ':');
+
+               if (!digit && !dot && !col) {
+                       break;
+               }
+
+               if (digit) {
+                       if (itmp == 0)
+                               igroup++;
+
+                       group[igroup] += mtmp[itmp] * (car - (int) '0');
+                       itmp++;
+               } else {
+                       if (dot)
+                               has_milli = 1;
+
+                       itmp = 0;
+               }
+       }
+
+       int total = 0;
+       int multOffset = (has_milli ? 0 : 1);
+       for (int i = 0; i <= igroup; i++) {
+               total += mults[i + multOffset] * group[i];
+       }
+
+       return total;
+}
+
+int nsub_is_timing(const char line[], char deci_sym, int max_deci) {
+       // 00:00:14,800
+
+       int digits = 0;
+       int groups = 0;
+       int sep = 0;
+
+       int max_digits = 2;
+       int max_groups = 3;
+
+       for (char *ptr = (char *) line; *ptr; ptr++) {
+               int digit = (*ptr >= '0' && *ptr <= '9');
+               int col = (*ptr == ':');
+               int dot = *ptr == deci_sym;
+
+               if (digit) {
+                       digits++;
+               } else if (col) {
+                       digits = 0;
+                       groups++;
+               } else if (dot) {
+                       digits = 0;
+                       max_digits = max_deci;
+                       sep++;
+               } else {
+                       return 0;
+               }
+
+               if (digits > max_digits)
+                       return 0;
+               if (groups > max_groups)
+                       return 0;
+               if (sep > 1)
+                       return 0;
+       }
+
+       return 1;
+}
index 008b1cca2f34178fa7683eb6939e48fbfcc58a0a..170443723200aebf006ce66ddef2591f09373af4 100644 (file)
@@ -146,6 +146,40 @@ void song_add_meta(song_t *song, char *key, char *value);
 
 /* Read */
 
+/**
+ * Convert a text line into milliseconds (for instance, 00:00:17,400).
+ * Text <b>must</b> be conform, though less groups or less digits per group
+ * is allowed.
+ *
+ * @note maximum number of groups: 4,
+ *              maximum number of digits per
+ *                     group: 2 except decimal group which is allowed up to 3
+ *
+ * @param line the line to convert
+ * @param deci_sep the decimal separator symbol (usually '.' or ',')
+ * @param max_deci maximum number of digits for the decimal value (max is 3)
+ *
+ * @return the number of milliseconds it means
+ */
+int nsub_to_ms(const char line[], char deci_sym);
+
+/**
+ * Validate that the given line is a timing (for instance, 00:00:17,400).
+ * Text <b>must</b> be conform, though less groups or less digits per group
+ * is allowed.
+ *
+ * @note maximum number of groups: 4,
+ *              maximum number of digits per
+ *                     group: 2 except decimal group which is allowed up to 3
+ *
+ * @param line the line to check
+ * @param deci_sep the decimal separator symbol (usually '.' or ',')
+ * @param max_deci maximum number of digits for the decimal value
+ *
+ * @return TRUE if it is
+ */
+int nsub_is_timing(const char line[], char deci_sym, int max_deci);
+
 song_t *nsub_read(FILE *in, NSUB_FORMAT fmt);
 int nsub_read_lrc(song_t *song, char *line);
 int nsub_read_webvtt(song_t *song, char *line);
index 35cbb1505b5c75ccc60fb0c9dcfafd75597bf3a2..796424c7ab6d56ffdfd5ff02dc1834b6aba60201 100644 (file)
 /* Declarations */
 
 // test if this is an offset line
-int is_lrc_offset(char *line);
+static int is_lrc_offset(char *line);
 // test if it is a timed lyric
-int is_lrc_lyric(char *line, int*end);
+static int is_lrc_lyric(char *line, int*end);
 // test if this is a meta line
-int is_lrc_meta(char *line, int *colon, int *end);
+static int is_lrc_meta(char *line, int *colon, int *end);
 // count the ms in the line "[(00:0)0:14.80]" or "[offset: +0:12]"
-int lrc_millisec(char *line);
+static int lrc_millisec(char *line);
 
 /* Public */
 
@@ -106,48 +106,79 @@ int nsub_read_lrc(song_t *song, char *line) {
 
 /* Private */
 
-int is_lrc_offset(char *line) {
-       const char offset[] = { "[offset" };
-       int i = 0;
-       while (line[i] && offset[i] && line[i] == offset[i])
-               i++;
+static int is_lrc_offset(char *line) {
+       // [offset: +0:12]
 
-       if (!offset[i]) {
-               while (line[i] == ' ')
-                       i++;
+       // skip spaces
+       while (*line == ' ')
+               line++;
 
-               if (line[i] == ':')
-                       return 1;
+       // skip [offset
+       char *offset = { "[offset" };
+       while (*line && *offset && *offset == *line) {
+               line++;
+               offset++;
        }
+       if (*offset)
+               return 0;
 
-       return 0;
-}
+       // skip spaces then ':'
+       while (*line == ' ')
+               line++;
+       if (*line && *line != ':')
+               return 0;
+       line++;
 
-int is_lrc_lyric(char *line, int *end) {
-       if (line[0] != '[')
+       // allow sign
+       if (*line == '-' || *line == '+')
+               line++;
+       while (*line == ' ')
+               line++;
+
+       // find end
+       int i = 0;
+       while (line[i] && line[i] != ']')
+               i++;
+       if (line[i] != ']')
                return 0;
 
-       *end = 0;
+       // validate timing
+       cstring_t*tmp = cstring_substring(line, 0, i);
+       int ok = nsub_is_timing(tmp->string, '.', 2);
+       free_cstring(tmp);
+       return ok;
+}
 
-       for (int i = 1; line[i]; i++) {
-               char car = line[i];
+static int is_lrc_lyric(char *line, int *end) {
+       // "[(00:0)0:14.80] bla bla bla"
 
-               if (car == ']') {
-                       *end = i;
-                       return i >= 2;
-               }
+       *end = 0;
 
-               int digit = (car >= '0' && car <= '9');
-               int sep = (car == ':' || car == '.' || car == ' ');
+       // skip spaces
+       while (*line == ' ')
+               line++;
 
-               if (!digit && !sep)
-                       break;
-       }
+       // skip [
+       if (*line != '[')
+               return 0;
+       line++;
+
+       // find end
+       int i = 0;
+       while (line[i] && line[i] != ']')
+               i++;
+       if (line[i] != ']')
+               return 0;
+       *end = i;
 
-       return 0;
+       // validate timing
+       cstring_t*tmp = cstring_substring(line, 0, i);
+       int ok = nsub_is_timing(tmp->string, '.', 2);
+       free_cstring(tmp);
+       return ok;
 }
 
-int is_lrc_meta(char *line, int *colon, int *end) {
+static int is_lrc_meta(char *line, int *colon, int *end) {
        if (line[0] != '[')
                return 0;
 
@@ -169,63 +200,50 @@ int is_lrc_meta(char *line, int *colon, int *end) {
        return (*colon) && (*end);
 }
 
-int lrc_millisec(char *line) {
-       int mults[] = { 1000, 10000, 60000, 600000, 3600000, 36000000 };
+static int lrc_millisec(char *line) {
+       // count the ms in the line "[(00:0)0:14.80]" or "[offset: +0:12]"
 
-       size_t end = 0;
-       for (size_t i = 0; line[i]; i++) {
-               if (line[i] == ']') {
-                       end = i - 1;
-                       break;
-               }
-       }
-
-       int total = 0;
-       for (size_t i = end; i >= 0; i--) {
-               char car = line[i];
-
-               int digit = (car >= '0' && car <= '9');
-               int sign = (car == '-' || car == '+');
-               int dot = car == '.';
-               int col = (car == ':');
+       int sign = 1;
+       int dummy = 0;
 
-               if (!digit && !sign && !dot && !col) {
-                       break;
-               }
-
-               if (dot) {
-                       int mult = 1;
-
-                       int j = i + 3;
-                       while (j > end) {
-                               mult *= 10;
-                               j--;
-                       }
-
-                       for (; j > i; j--) {
-                               total += mult * ((int) line[j] - (int) '0');
-                               mult *= 10;
-                       }
+       // skip spaces
+       while (*line == ' ')
+               line++;
 
-                       end = i - 1;
-                       break;
+       if (is_lrc_lyric(line, &dummy)) {
+               // skip [
+               line++;
+       } else if (is_lrc_offset(line)) {
+               // skip [offset:
+               while (*line != ':')
+                       line++;
+               while (*line == ' ')
+                       line++;
+
+               // allow sign
+               if (*line == '+') {
+                       line++;
+               } else if (*line == '-') {
+                       line++;
+                       sign = -1;
                }
+               while (*line == ' ')
+                       line++;
+       } else {
+               /* should not happen! */
+               fprintf(stderr,
+                               "Warning: called lrc_millisec with bad input [%s], ignoring...\n",
+                               line);
+               return 0;
        }
 
-       int imult = 0;
-       for (size_t i = end; 1; i--) {
-               char car = line[i];
-               int digit = (car >= '0' && car <= '9');
-
-               if (digit)
-                       total += (car - '0') * mults[imult++];
-
-               if (car == '-')
-                       total = -total;
-
-               if (i == 0)
-                       break;
-       }
+       // find end
+       int i = 0;
+       while (line[i] != ']')
+               i++;
 
-       return total;
+       cstring_t*tmp = cstring_substring(line, 0, i);
+       int ms = nsub_to_ms(tmp->string, '.');
+       free_cstring(tmp);
+       return sign * ms;
 }
index 23ed04fbf6a4e28de55ac5f2160d1b95da1d4943..30becc4b5726b1ffb9871f028311e0ed6abc38b8 100644 (file)
 
 /* Declarations */
 
-static int is_srt_id(const char line[]);
-static int is_srt_timing(const char line[]);
-static int get_start(const char line[]);
-static int get_stop(const char line[]);
-static int to_ms(const char line[]);
+static int is_srt_id(char *line);
+static int is_srt_timing(char *line);
+static int get_start(char *line);
+static int get_stop(char *line);
 
 int nsub_read_srt(song_t *song, char *line) {
        int empty = 1;
@@ -48,15 +47,15 @@ int nsub_read_srt(song_t *song, char *line) {
 
        if (is_srt_id(line)) {
                int new_count = atoi(line);
-
                if (new_count != count + 1) {
                        fprintf(stderr,
-                                       "Warning: line %zu is out of order (it is numbered %i), ignoring order...",
+                                       "Warning: line %zu is out of order (it is numbered %i), ignoring order...\n",
                                        count, new_count);
                }
 
                song_add_lyric(song, 0, 0, NULL, NULL);
        } else if (is_srt_timing(line)) {
+               // no headers in srt
                if (!lyric) {
                        return 0;
                }
@@ -70,7 +69,7 @@ int nsub_read_srt(song_t *song, char *line) {
 
                char *text = lyric->text;
                if (text)
-                       text = cstring_concat(text, "\n", line);
+                       text = cstring_concat(text, "\n", line, NULL);
                else
                        text = strdup(line);
 
@@ -83,7 +82,7 @@ int nsub_read_srt(song_t *song, char *line) {
 
 /* Private */
 
-static int is_srt_id(const char line[]) {
+static int is_srt_id(char *line) {
        for (char *ptr = (char *) line; *ptr; ptr++) {
                switch (*ptr) {
                case '0':
@@ -106,161 +105,115 @@ static int is_srt_id(const char line[]) {
        return 1;
 }
 
-static int is_srt_timing(const char line[]) {
+static int is_srt_timing(char *line) {
        // Canonical example:
-       // 00:00:14,800 --> 00:00:17,400
-
-       int vals = 0;
-       int vals_groups = 0;
-       int sep = 0;
-       int deci = 0;
-
-       for (char *ptr = (char *) line; *ptr; ptr++) {
-               switch (*ptr) {
-               case ' ': // ignore space if not in sep
-                       if (sep && sep < 2)
-                               return 0;
-                       break;
-
-               case '0': // count a new numeric
-               case '1':
-               case '2':
-               case '3':
-               case '4':
-               case '5':
-               case '6':
-               case '7':
-               case '8':
-               case '9':
-                       vals++;
-                       break;
-
-               case ',': // we just did a group
-               case ':':
-                       if (*ptr == ',')
-                               deci = 1;
-
-                       if (vals < 1 || vals > 2)
-                               return 0;
-
-                       vals = 0;
-                       vals_groups++;
-                       break;
+       // 00:00:14,800 --> 00:00:17,400 align: center
 
-               case '-': // Separator (-->)
-                       if (!sep) {
-                               vals_groups++;
+       cstring_t *tmp;
+       int ok;
+       int i;
 
-                               if (vals < 1 || (vals > 2 && !deci) || vals > 3)
-                                       return 0;
+       // skip spaces
+       while (*line == ' ')
+               line++;
 
-                               if (vals_groups < 1 || (vals_groups > 3 && !deci)
-                                               || vals_groups > 4)
-                                       return 0;
-
-                               vals = 0;
-                               vals_groups = 0;
-                               deci = 0;
-                       }
+       // check part 1
+       i = 0;
+       while (line[i] && line[i] != ' ' && line[i] != '-')
+               i++;
+       if (line[i] != ' ' && line[i] != '-')
+               return 0;
+       tmp = cstring_substring(line, 0, i);
+       ok = nsub_is_timing(tmp->string, ',', 3);
+       free_cstring(tmp);
+       if (!ok)
+               return 0;
 
-                       if (sep > 2)
-                               return 0;
+       // skip part 1
+       line += i;
 
-                       sep++;
-                       break;
+       // skip spaces
+       while (*line == ' ')
+               line++;
 
-               case '>': // Separator (-->)
-                       if (sep != 2)
-                               return 0;
-
-                       sep++;
-                       break;
-               }
+       // skip -->
+       char *arrow = { "-->" };
+       while (*line && *arrow && *arrow == *line) {
+               line++;
+               arrow++;
        }
-
-       if (vals < 1 || (vals > 2 && !deci) || vals > 3)
+       if (*arrow)
                return 0;
 
-       if (vals_groups < 1 || (vals_groups > 3 && !deci) || vals_groups > 4)
-               return 0;
+       // skip spaces
+       while (*line == ' ')
+               line++;
 
-       if (sep != 3)
-               return 0;
+       // find end
+       i = 0;
+       while (line[i] && line[i] != ' ')
+               i++;
 
-       return 1;
+       // validate part 2
+       tmp = cstring_substring(line, 0, i);
+       ok = nsub_is_timing(tmp->string, ',', 3);
+       free_cstring(tmp);
+       return !!ok;
 }
 
-static int get_start(const char line[]) {
-       char *ptr = (char *) line;
-       while (*ptr == ' ')
-               ptr++;
+static int get_start(char *line) {
+       // 00:00:14,800 --> 00:00:17,400 align: center
 
-       size_t i;
-       for (i = 0; ptr[i] != ' ' && ptr[i] != '-'; i++)
-               ;
+       if (!is_srt_timing(line)) {
+               /* should not happen! */
+               fprintf(stderr,
+                               "Warning: called get_start with bad input [%s], ignoring...\n",
+                               line);
+               return 0;
+       }
 
-       cstring_t*start = cstring_substring(ptr, 0, i);
-       int ms = to_ms(start->string);
-       free_cstring(start);
-       return ms;
-}
+       // skip spaces
+       while (*line == ' ')
+               line++;
 
-static int get_stop(const char line[]) {
-       char *ptr = (char *) line;
-       while (*ptr != '>')
-               ptr++;
-       ptr++;
-       while (*ptr == ' ')
-               ptr++;
+       // find end
+       int i = 0;
+       while (line[i] && line[i] != ' ' && line[i] != '-')
+               i++;
 
-       return to_ms(ptr);
+       cstring_t*start = cstring_substring(line, 0, i);
+       int ms = nsub_to_ms(start->string, ',');
+       free_cstring(start);
+       return ms;
 }
 
-static int to_ms(const char line[]) {
-       // 00:00:17,400
-
-       int mults[] = { 1, 1000, 60000, 3600000 };
-
-       int group[4] = { 0, 0, 0, 0 };
-       int igroup = -1;
-
-       char mtmp[3] = { 1, 10, 100 };
-       int itmp = 0;
+static int get_stop(char *line) {
+       // 00:00:14,800 --> 00:00:17,400 align: center
 
-       int has_milli = 0;
-
-       size_t end = strlen(line) - 1;
-
-       for (size_t i = end; i >= 0; i--) {
-               char car = line[i];
-
-               int digit = (car >= '0' && car <= '9');
-               int dot = car == ',';
-               int col = (car == ':');
-
-               if (!digit && !dot && !col) {
-                       break;
-               }
-
-               if (digit) {
-                       if (itmp == 0)
-                               igroup++;
+       if (!is_srt_timing(line)) {
+               /* should not happen! */
+               fprintf(stderr,
+                               "Warning: called get_stop with bad input [%s], ignoring...\n",
+                               line);
+               return 0;
+       }
 
-                       group[igroup] += mtmp[itmp] * (car - (int) '0');
-                       itmp++;
-               } else {
-                       if (dot)
-                               has_milli = 1;
+       // skip to >
+       while (*line != '>')
+               line++;
+       line++;
 
-                       itmp = 0;
-               }
-       }
+       // skip spaces
+       while (*line == ' ')
+               line++;
 
-       int total = 0;
-       int multOffset = (has_milli ? 0 : 1);
-       for (int i = 0; i <= igroup; i++) {
-               total += mults[i + multOffset] * group[i];
-       }
+       // find end
+       int i = 0;
+       while (line[i] && line[i] != ' ')
+               i++;
 
-       return total;
+       cstring_t*end = cstring_substring(line, 0, i);
+       int ms = nsub_to_ms(end->string, ',');
+       free_cstring(end);
+       return ms;
 }
diff --git a/src/nsub/nsub_read_webvtt.c b/src/nsub/nsub_read_webvtt.c
new file mode 100644 (file)
index 0000000..35a3977
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * NSub: Subtitle/Lyrics conversion program (webvtt/srt/lrc)
+ *
+ * Copyright (C) 2022 Niki Roo
+ *
+ * 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 3 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "nsub.h"
+#include "utils/utils.h"
+
+/* Declarations */
+
+static int is_srt_id(char *line);
+static int is_srt_timing(char *line);
+static int get_start(char *line);
+static int get_stop(char *line);
+
+int nsub_read_webvtt(song_t *song, char *line) {
+       int empty = 1;
+       for (int i = 0; empty && line[i]; i++) {
+               if (line[i] != ' ')
+                       empty = 0;
+       }
+
+       if (empty)
+               return 1;
+
+       size_t count = array_count(song->lyrics);
+       lyric_t *lyric = NULL;
+       if (count)
+               lyric = array_get(song->lyrics, array_count(song->lyrics) - 1);
+
+       if (is_srt_id(line)) {
+               int new_count = atoi(line);
+               if (new_count != count + 1) {
+                       fprintf(stderr,
+                                       "Warning: line %zu is out of order (it is numbered %i), ignoring order...\n",
+                                       count, new_count);
+               }
+       } else if (is_srt_timing(line)) {
+               song_add_lyric(song, 0, 0, NULL, NULL);
+               lyric = array_get(song->lyrics, array_count(song->lyrics) - 1);
+
+               lyric->start = get_start(line);
+               lyric->stop = get_stop(line);
+       } else {
+               // a header has been found
+               if (!lyric) {
+                       return 1;
+               }
+
+               char *text = lyric->text;
+               if (text)
+                       text = cstring_concat(text, "\n", line, NULL);
+               else
+                       text = strdup(line);
+
+               free(lyric->text);
+               lyric->text = text;
+       }
+
+       return 1;
+}
+
+/* Private */
+
+static int is_srt_id(char *line) {
+       for (char *ptr = (char *) line; *ptr; ptr++) {
+               int digit = (*ptr >= '0' && *ptr <= '9');
+               int space = (*ptr == ' ');
+
+               if (!digit && !space)
+                       return 0;
+       }
+
+       return 1;
+}
+
+static int is_srt_timing(char *line) {
+       // Canonical example:
+       // 00:00:14.800 --> 00:00:17.400 align: center
+
+       cstring_t *tmp;
+       int ok;
+       int i;
+
+       // skip spaces
+       while (*line == ' ')
+               line++;
+
+       // check part 1
+       i = 0;
+       while (line[i] && line[i] != ' ' && line[i] != '-')
+               i++;
+       if (!line[i])
+               return 0;
+       tmp = cstring_substring(line, 0, i);
+       ok = nsub_is_timing(tmp->string, '.', 3);
+       free_cstring(tmp);
+       if (!ok)
+               return 0;
+
+       // skip part 1
+       line += i;
+
+       // skip spaces
+       while (*line == ' ')
+               line++;
+
+       // skip -->
+       char *arrow = { "-->" };
+       while (*line && *arrow && *arrow == *line) {
+               line++;
+               arrow++;
+       }
+       if (*arrow)
+               return 0;
+
+       // skip spaces
+       while (*line == ' ')
+               line++;
+
+       // find end
+       i = 0;
+       while (line[i] && line[i] != ' ')
+               i++;
+
+       // validate part 2
+       tmp = cstring_substring(line, 0, i);
+       ok = nsub_is_timing(tmp->string, '.', 3);
+       free_cstring(tmp);
+       return !!ok;
+}
+
+static int get_start(char *line) {
+       // 00:00:14,800 --> 00:00:17,400 align: center
+
+       if (!is_srt_timing(line)) {
+               /* should not happen! */
+               fprintf(stderr,
+                               "Warning: called get_start with bad input [%s], ignoring...\n",
+                               line);
+               return 0;
+       }
+
+       // skip spaces
+       while (*line == ' ')
+               line++;
+
+       // find end
+       int i = 0;
+       while (line[i] && line[i] != ' ' && line[i] != '-')
+               i++;
+
+       cstring_t*start = cstring_substring(line, 0, i);
+       int ms = nsub_to_ms(start->string, '.');
+       free_cstring(start);
+       return ms;
+}
+
+static int get_stop(char *line) {
+       // 00:00:14,800 --> 00:00:17,400 align: center
+
+       if (!is_srt_timing(line)) {
+               /* should not happen! */
+               fprintf(stderr,
+                               "Warning: called get_stop with bad input [%s], ignoring...\n",
+                               line);
+               return 0;
+       }
+
+       // skip to >
+       while (*line != '>')
+               line++;
+       line++;
+
+       // skip spaces
+       while (*line == ' ')
+               line++;
+
+       // find end
+       int i = 0;
+       while (line[i] && line[i] != ' ')
+               i++;
+
+       cstring_t*end = cstring_substring(line, 0, i);
+       int ms = nsub_to_ms(end->string, '.');
+       free_cstring(end);
+       return ms;
+}
index a7224b23462fe99c6b305fb90cabedafa7662a9f..9d391f390a6e0e8170653d065f43b8294be04fdd 100644 (file)
@@ -20,6 +20,7 @@
 #include <stdio.h>
 
 #include "nsub.h"
+#include "utils/utils.h"
 
 /* Declarations */
 
@@ -88,17 +89,37 @@ void nsub_write_lrc_lyric(FILE *out, lyric_t *lyric, int offset) {
        }
 
        if (lyric->type == NSUB_COMMENT || lyric->type == NSUB_UNKNOWN) {
-               fprintf(out, "-- %s\n", lyric->text);
+               cstring_t *tmp = cstring_clone(lyric->text);
+               if (tmp) {
+                       cstring_replace(tmp, "\n", "\\n");
+               }
+
+               fprintf(out, "-- %s\n", tmp->string);
                lrc_last_stop = 0;
+
+               free_cstring(tmp);
                return;
        }
 
-       if (lyric->name)
-               fprintf(out, "-- %s\n", lyric->name);
+       if (lyric->name) {
+               cstring_t *tmp = cstring_clone(lyric->name);
+               if (tmp) {
+                       cstring_replace(tmp, "\n", "\\n");
+               }
+
+               fprintf(out, "-- %s\n", tmp->string);
+
+               free_cstring(tmp);
+       }
 
        char *time = nsub_lrc_time_str(lyric->start + offset, 0);
-       fprintf(out, "[%s] %s\n", time, lyric->text);
+       cstring_t *tmp = cstring_clone(lyric->text);
+       if (tmp) {
+               cstring_replace(tmp, "\n", "\\n");
+       }
+       fprintf(out, "[%s] %s\n", time, tmp->string);
        free(time);
+       free_cstring(tmp);
 
        lrc_last_stop = lyric->stop + offset;
 }
index 57743a98f2845ca7f6681b3f16adba9646e9c128..141bdb146a666249f857fef6acc649a24e342179 100644 (file)
@@ -20,6 +20,7 @@
 #include <stdio.h>
 
 #include "nsub.h"
+#include "utils/utils.h"
 
 /* Declarations */
 
@@ -64,12 +65,15 @@ void nsub_write_srt_lyric(FILE *out, lyric_t *lyric, int offset) {
                return;
        }
 
+       // Num is mandatory for srt
+       fprintf(out, "%d\n", lyric->num);
+
        //if (lyric->name)
        // not supported, ignored
 
        char *start = nsub_srt_time_str(lyric->start + offset, 0);
        char *stop = nsub_srt_time_str(lyric->stop + offset, 0);
-       fprintf(out, "%d\n%s --> %s\n%s\n\n", lyric->num, start, stop, lyric->text);
+       fprintf(out, "%s --> %s\n%s\n\n", start, stop, lyric->text);
        free(start);
        free(stop);
 }
index b73090bfb2eb64a16068f0f0bb7d917fba67039b..1b6c571f08c2e65afa9206b4161ee8230d61b4cb 100644 (file)
@@ -43,7 +43,8 @@ int nsub_write_webvtt(FILE *out, song_t *song, NSUB_FORMAT fmt,
        // metas
        array_loop(song->metas, meta, meta_t)
        {
-               fprintf(out, "NOTE META %s: %s\n\n", meta->key, meta->value);
+               // Not always supported by clients, so disabled:
+               //fprintf(out, "NOTE META %s: %s\n\n", meta->key, meta->value);
        }
 
        // offset is not supported in WebVTT (so, always applied)
@@ -53,8 +54,9 @@ int nsub_write_webvtt(FILE *out, song_t *song, NSUB_FORMAT fmt,
 
        // other metas
        {
-               fprintf(out,
-                               "NOTE META created by: nsub (https://github.com/nikiroo/nsub)]\n");
+               // Not always supported by clients, so disabled:
+               //fprintf(out,
+               // "NOTE META created by: nsub (https://github.com/nikiroo/nsub)]\n");
        }
 
        // lyrics
@@ -79,8 +81,12 @@ void nsub_write_webvtt_lyric(FILE *out, lyric_t *lyric, int offset) {
                return;
        }
 
-       if (lyric->name)
-               fprintf(out, "%s\n", lyric->name);
+       // Num is optional for WebVTT, but maybe easier for clients
+       fprintf(out, "%d\n", lyric->num);
+
+       // Not always supported by clients, so disabled:
+       // if (lyric->name)
+       //fprintf(out, "%s\n", lyric->name);
 
        char *start = nsub_webvtt_time_str(lyric->start + offset, 0);
        char *stop = nsub_webvtt_time_str(lyric->stop + offset, 0);