commit 2ce2432d7dfe4fd17e094acb3111cc314f8168c8
parent 0de028a721bae45ee0fb7b42644c6022482c78f6
Author: oblique <psyberbits@gmail.com>
Date: Mon, 7 Apr 2014 22:44:40 +0300
Rewrite subsync in C
Since subsync was the only code that I wrote in Go and since I forgot
Go, I decided to rewrite it in C.
Diffstat:
M | Makefile | | | 11 | ++++++----- |
M | README.md | | | 6 | +++--- |
A | list.h | | | 108 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | subsync.c | | | 412 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | subsync.go | | | 296 | ------------------------------------------------------------------------------- |
5 files changed, 529 insertions(+), 304 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,10 +1,11 @@
-GOPATH ?= $(PWD)/godir
+CC := gcc
+CFLAGS += -Wall -Wextra -std=gnu99 -O2
+LIBS := -lm
all: subsync
-subsync: subsync.go
- GOPATH="$(GOPATH)" go get -d .
- GOPATH="$(GOPATH)" go build -o $@ subsync.go
+subsync: subsync.c list.h
+ $(CC) $(CFLAGS) $(LDFLAGS) $< $(LIBS) -o $@
clean:
- rm -rf subsync godir
+ @rm -f subsync
diff --git a/README.md b/README.md
@@ -1,6 +1,6 @@
-subsync is a simple CLI tool written in Go, that synchronizes SubRip
-(srt) subtitles automatically. You only have to know when the first
-and the last subtitle must be shown.
+subsync is a simple CLI tool that synchronizes SubRip (srt) subtitles
+automatically. You only have to know when the first and the last
+subtitle should be shown.
## examples
diff --git a/list.h b/list.h
@@ -0,0 +1,108 @@
+#ifndef __LIST_H
+#define __LIST_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+struct list_head {
+ struct list_head *prev;
+ struct list_head *next;
+};
+
+#define container_of(ptr, type, member) ({ \
+ const typeof(((type*)0)->member) *__mptr = (ptr); \
+ (type*)((uintptr_t)__mptr - offsetof(type, member)); \
+})
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)
+
+#define list_entry(ptr, type, member) container_of(ptr, type, member)
+#define list_first_entry(head_ptr, type, member) container_of((head_ptr)->next, type, member)
+#define list_last_entry(head_ptr, type, member) container_of((head_ptr)->prev, type, member)
+
+/* list_for_each - iterate over a list
+ * pos: the &struct list_head to use as a loop cursor.
+ * head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); \
+ pos = pos->next)
+
+/* list_for_each_safe - iterate over a list safe against removal of list entry
+ * pos: the &struct list_head to use as a loop cursor.
+ * n: another &struct list_head to use as temporary storage
+ * head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+static inline void
+init_list_head(struct list_head *list)
+{
+ list->prev = list;
+ list->next = list;
+}
+
+/* Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void
+list_del(struct list_head *entry)
+{
+ entry->prev->next = entry->next;
+ entry->next->prev = entry->prev;
+ entry->next = NULL;
+ entry->prev = NULL;
+}
+
+static inline void
+list_add(struct list_head *entry, struct list_head *head)
+{
+ entry->next = head->next;
+ entry->prev = head;
+ head->next->prev = entry;
+ head->next = entry;
+}
+
+static inline void
+list_add_tail(struct list_head *entry, struct list_head *head)
+{
+ entry->next = head;
+ entry->prev = head->prev;
+ head->prev->next = entry;
+ head->prev = entry;
+}
+
+static inline void
+list_move_tail(struct list_head *entry, struct list_head *head)
+{
+ list_del(entry);
+ list_add_tail(entry, head);
+}
+
+static inline int
+list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
+/* list_is_singular - tests whether a list has just one entry. */
+static inline int
+list_is_singular(const struct list_head *head)
+{
+ return !list_empty(head) && (head->next == head->prev);
+}
+
+/* list_is_last - tests whether @list is the last entry in list @head
+ * list: the entry to test
+ * head: the head of the list
+ */
+static inline int
+list_is_last(const struct list_head *list, const struct list_head *head)
+{
+ return list->next == head;
+}
+
+#endif /* __LIST_H */
diff --git a/subsync.c b/subsync.c
@@ -0,0 +1,412 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <getopt.h>
+#include <math.h>
+#include "list.h"
+
+#define VERSION "0.2.0"
+#define SUB_MAX_BUF 1024
+
+typedef uint64_t msec_t;
+#define PRImsec PRIu64
+
+struct srt_sub {
+ msec_t start;
+ msec_t end;
+ char *position;
+ char text[SUB_MAX_BUF];
+ struct list_head list;
+};
+
+void init_srt_sub(struct srt_sub *sub)
+{
+ assert(sub != NULL);
+ sub->start = 0;
+ sub->end = 0;
+ sub->position = NULL;
+ sub->text[0] = '\0';
+ sub->list.next = NULL;
+ sub->list.prev = NULL;
+}
+
+void free_srt_sub(struct srt_sub *sub)
+{
+ assert(sub != NULL);
+ if (sub->position)
+ free(sub->position);
+ free(sub);
+}
+
+void free_srt_sub_list(struct list_head *srt_head)
+{
+ struct list_head *pos, *tmp;
+ struct srt_sub *sub;
+
+ assert(srt_head != NULL);
+
+ list_for_each_safe(pos, tmp, srt_head) {
+ sub = list_entry(pos, struct srt_sub, list);
+ free_srt_sub(sub);
+ }
+}
+
+void *xmalloc(size_t size)
+{
+ void *m = malloc(size);
+
+ if (m == NULL)
+ abort();
+
+ return m;
+}
+
+/* converts hh:mm:ss[,.]mss to milliseconds */
+int timestr_to_msec(const char *time, msec_t *msecs)
+{
+ char *tmp;
+ msec_t h, m, s, ms;
+ int res;
+
+ assert(msecs != NULL || time != NULL);
+
+ tmp = strchr(time, '.');
+ if (tmp != NULL)
+ *tmp = ',';
+
+ res = sscanf(time, "%" PRImsec ":%" PRImsec ":%" PRImsec ",%" PRImsec, &h, &m, &s, &ms);
+ if (res != 4 || m >= 60 || s >= 60 || ms >= 1000) {
+ fprintf(stderr, "Parsing error: Can not convert `%s' to milliseconds\n", time);
+ return -1;
+ }
+
+ *msecs = h * 60 * 60 * 1000;
+ *msecs += m * 60 * 1000;
+ *msecs += s * 1000;
+ *msecs += ms;
+
+ return 0;
+}
+
+/* converts milliseconds to hh:mm:ss,mss */
+char *msec_to_timestr(msec_t msecs, char *timestr, size_t size)
+{
+ msec_t h, m, s, ms;
+
+ assert(timestr != NULL || size == 0);
+
+ h = msecs / (60 * 60 * 1000);
+ msecs %= 60 * 60 * 1000;
+ m = msecs / (60 * 1000);
+ msecs %= 60 * 1000;
+ s = msecs / 1000;
+ ms = msecs % 1000;
+
+ snprintf(timestr, size, "%02" PRImsec ":%02" PRImsec ":%02" PRImsec ",%03" PRImsec,
+ h, m, s, ms);
+
+ return timestr;
+}
+
+char *strip_eol(char *str)
+{
+ size_t i;
+
+ assert(str != NULL);
+
+ for (i = 0; str[i] != '\0'; i++) {
+ if (str[i] == '\n') {
+ str[i] = '\0';
+ if (i > 0 && str[i - 1] == '\r')
+ str[i - 1] = '\0';
+ return str;
+ }
+ }
+
+ return str;
+}
+
+/* read SubRip (srt) file */
+int read_srt(FILE *fin, struct list_head *srt_head)
+{
+ int state = 0;
+ char *s, buf[SUB_MAX_BUF];
+ struct srt_sub *sub = NULL;
+
+ assert(fin != NULL || srt_head != NULL);
+
+ while (1) {
+ s = fgets(buf, sizeof(buf), fin);
+ if (s == NULL)
+ break;
+
+ strip_eol(buf);
+
+ if (state == 0) {
+ /* drop empty lines */
+ if (buf[0] == '\0')
+ continue;
+ /* drop subtitle number */
+ state = 1;
+ } else if (state == 1) {
+ char start_time[20], end_time[20], position[50];
+ int res;
+
+ sub = xmalloc(sizeof(*sub));
+ init_srt_sub(sub);
+
+ /* parse start, end, and position */
+ res = sscanf(buf, "%19s --> %19s%49[^\n]", start_time, end_time, position);
+ if (res < 2) {
+ fprintf(stderr, "Parsing error: Wrong file format\n");
+ goto out_err;
+ }
+
+ if (res == 3)
+ sub->position = strdup(position);
+
+ res = timestr_to_msec(start_time, &sub->start);
+ if (res == -1)
+ goto out_err;
+
+ res = timestr_to_msec(end_time, &sub->end);
+ if (res == -1)
+ goto out_err;
+
+ state = 2;
+ } else if (state == 2) {
+ /* empty line indicates the end of the subtitle,
+ * so append it to the list */
+ if (buf[0] == '\0') {
+ list_add_tail(&sub->list, srt_head);
+ sub = NULL;
+ state = 0;
+ continue;
+ }
+ /* save subtitle text */
+ strncat(sub->text, buf, sizeof(sub->text) - strlen(sub->text) - 1);
+ strncat(sub->text, "\r\n", sizeof(sub->text) - strlen(sub->text) - 1);
+ }
+ }
+
+ if (ferror(fin)) {
+ fprintf(stderr, "read: File error\n");
+ goto out_err;
+ }
+
+ return 0;
+
+out_err:
+ if (sub != NULL)
+ free_srt_sub(sub);
+ free_srt_sub_list(srt_head);
+ return -1;
+}
+
+/* write SubRip (srt) file */
+void write_srt(FILE *fout, struct list_head *srt_head)
+{
+ struct list_head *pos;
+ struct srt_sub *sub;
+ unsigned int id = 1;
+ char tm[20];
+
+ assert(fout != NULL || srt_head != NULL);
+
+ list_for_each(pos, srt_head) {
+ sub = list_entry(pos, struct srt_sub, list);
+ fprintf(fout, "%u\r\n", id++);
+ fprintf(fout, "%s", msec_to_timestr(sub->start, tm, sizeof(tm)));
+ fprintf(fout, " --> ");
+ fprintf(fout, "%s", msec_to_timestr(sub->end, tm, sizeof(tm)));
+ if (sub->position)
+ fprintf(fout, "%s", sub->position);
+ fprintf(fout, "\r\n%s\r\n", sub->text);
+ }
+}
+
+/* synchronize subtitles by knowing the start time of the first and the last subtitle.
+ * to archive this we must use the linear equation: y = mx + b */
+void sync_srt(struct list_head *srt_head, msec_t synced_first, msec_t synced_last)
+{
+ long double slope, yint;
+ msec_t desynced_first, desynced_last;
+ struct list_head *pos;
+ struct srt_sub *sub;
+
+ assert(srt_head != NULL);
+
+ desynced_first = list_first_entry(srt_head, struct srt_sub, list)->start;
+ desynced_last = list_last_entry(srt_head, struct srt_sub, list)->start;
+
+ /* m = (y2 - y1) / (x2 - x1)
+ * m: slope
+ * y2: synced_last
+ * y1: synced_first
+ * x2: desynced_last
+ * x1: desynced_first */
+ slope = (long double)(synced_last - synced_first)
+ / (long double)(desynced_last - desynced_first);
+
+ /* b = y - mx
+ * b: yint
+ * y: synced_last
+ * m: slope
+ * x: desynced_last */
+ yint = synced_last - slope * desynced_last;
+
+ list_for_each(pos, srt_head) {
+ sub = list_entry(pos, struct srt_sub, list);
+ /* y = mx + b
+ * y: sub->start and sub->end
+ * m: slope
+ * x: sub->start and sub->end
+ * b: yint */
+ sub->start = llroundl(slope * sub->start + yint);
+ sub->end = llroundl(slope * sub->end + yint);
+ }
+}
+
+void usage(void)
+{
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr, " subsync [options]\n");
+ fprintf(stderr, "\nOptions:\n");
+ fprintf(stderr, " -h, --help Show this help\n");
+ fprintf(stderr, " -f, --first-sub Time of the first subtitle\n");
+ fprintf(stderr, " -l, --last-sub Time of the last subtitle\n");
+ fprintf(stderr, " -i, --input Input file\n");
+ fprintf(stderr, " -o, --output Output file");
+ fprintf(stderr, " (if not specified, it overwrites the input file)\n");
+ fprintf(stderr, " -v, --version Print version\n");
+ fprintf(stderr, "\nExample:\n");
+ fprintf(stderr, " subsync -f 00:01:33,492 -l 01:39:23,561 -i file.srt\n");
+}
+
+#define FLAG_F (1 << 0)
+#define FLAG_L (1 << 1)
+
+int main(int argc, char *argv[])
+{
+ struct list_head subs_head;
+ unsigned int flags = 0;
+ msec_t first_ms, last_ms;
+ char *input_path = NULL, *output_path = NULL;
+ FILE *fin = stdin, *fout = stdout;
+ int res;
+
+ if (argc <= 1) {
+ usage();
+ return 1;
+ }
+
+ init_list_head(&subs_head);
+
+ while (1) {
+ int c, option_index;
+ static struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"first-sub", required_argument, 0, 'f'},
+ {"last-sub", required_argument, 0, 'l'},
+ {"input", required_argument, 0, 'i'},
+ {"output", required_argument, 0, 'o'},
+ {"version", required_argument, 0, 'v'},
+ { 0, 0, 0, 0}
+ };
+
+ c = getopt_long(argc, argv, "f:l:i:o:hv", long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ usage();
+ return 0;
+ case 'f':
+ flags |= FLAG_F;
+ res = timestr_to_msec(optarg, &first_ms);
+ if (res == -1)
+ return 1;
+ break;
+ case 'l':
+ flags |= FLAG_L;
+ res = timestr_to_msec(optarg, &last_ms);
+ if (res == -1)
+ return 1;
+ break;
+ case 'i':
+ input_path = optarg;
+ break;
+ case 'o':
+ output_path = optarg;
+ break;
+ case 'v':
+ printf("%s\n", VERSION);
+ return 0;
+ default:
+ return 1;
+ }
+ }
+
+ if (optind < argc) {
+ int i;
+ fprintf(stderr, "Invalid argument%s:", argc - optind > 1 ? "s" : "");
+ for (i = optind; i < argc; i++)
+ fprintf(stderr, " %s", argv[i]);
+ fprintf(stderr, "\n");
+ return 1;
+ }
+
+ if (input_path == NULL) {
+ fprintf(stderr, "You must specify an input file with -i option.\n");
+ return 1;
+ }
+
+ if (output_path == NULL)
+ output_path = input_path;
+
+ /* read srt file */
+ if (strcmp(input_path, "-") != 0)
+ fin = fopen(input_path, "r");
+
+ if (fin == NULL) {
+ fprintf(stderr, "open: %s: %s\n", input_path, strerror(errno));
+ return 1;
+ }
+
+ res = read_srt(fin, &subs_head);
+ fclose(fin);
+ if (res == -1)
+ return 1;
+
+ /* if user didn't pass 'f' flag, then get the time of the first subtitle */
+ if (!(flags & FLAG_F))
+ first_ms = list_first_entry(&subs_head, struct srt_sub, list)->start;
+
+ /* if user didn't pass 'l' flag, then get the time of the last subtitle */
+ if (!(flags & FLAG_L))
+ last_ms = list_last_entry(&subs_head, struct srt_sub, list)->start;
+
+ /* sync subtitles */
+ sync_srt(&subs_head, first_ms, last_ms);
+
+ /* write subtitles */
+ if (strcmp(output_path, "-") != 0)
+ fout = fopen(output_path, "w");
+
+ if (fout == NULL) {
+ fprintf(stderr, "open: %s: %s\n", output_path, strerror(errno));
+ free_srt_sub_list(&subs_head);
+ return 1;
+ }
+
+ write_srt(fout, &subs_head);
+ fclose(fout);
+
+ free_srt_sub_list(&subs_head);
+ return 0;
+}
diff --git a/subsync.go b/subsync.go
@@ -1,295 +0,0 @@
-package main
-
-import (
- "fmt"
- "errors"
- "strings"
- "math"
- "os"
- "path"
- "io"
- "bufio"
- "container/list"
- "github.com/jessevdk/go-flags"
-)
-
-
-const (
- version = "0.1.3"
-)
-
-type subtitle struct {
- text string
- start uint
- end uint
-}
-
-func die(err error) {
- fmt.Fprintf(os.Stderr, "%v\n", err)
- os.Exit(1)
-}
-
-func roundFloat64(f float64) float64 {
- val := f - float64(int64(f))
- if val >= 0.5 {
- return math.Ceil(f)
- } else if val > 0 {
- return math.Floor(f)
- } else if val <= -0.5 {
- return math.Floor(f)
- } else if val < 0 {
- return math.Ceil(f)
- }
- return f
-}
-
-/* converts hh:mm:ss,mss to milliseconds */
-func time_to_msecs(tm string) (uint, error) {
- var msecs uint
- var h, m, s, ms uint
-
- tm = strings.Replace(tm, ".", ",", 1)
- num, err := fmt.Sscanf(tm, "%d:%d:%d,%d", &h, &m, &s, &ms)
-
- if num != 4 || err != nil {
- return 0, errors.New("Parsing error: Can not covert `" + tm + "' to milliseconds.")
- }
-
- msecs = h * 60 * 60 * 1000
- msecs += m * 60 * 1000
- msecs += s * 1000
- msecs += ms
-
- return msecs, nil
-}
-
-/* converts milliseconds to hh:mm:ss,mss */
-func msecs_to_time(msecs uint) string {
- var h, m, s, ms uint
-
- h = msecs / (60 * 60 * 1000)
- msecs %= 60 * 60 * 1000
- m = msecs / (60 * 1000)
- msecs %= 60 * 1000
- s = msecs / 1000
- ms = msecs % 1000
-
- tm := fmt.Sprintf("%02d:%02d:%02d,%03d", h, m, s, ms)
-
- return tm
-}
-
-/* read SubRip (srt) file */
-func read_srt(filename string) (*list.List, error) {
- var state int = 0
- var subs *list.List
- var sub *subtitle
-
- f, err := os.Open(filename)
- if err != nil {
- return nil, err
- }
- defer f.Close()
-
- r := bufio.NewReader(f)
- subs = list.New()
- sub = new(subtitle)
-
- for {
- var (
- isprefix bool = true
- err error = nil
- ln, line []byte
- )
-
- for isprefix && err == nil {
- line, isprefix, err = r.ReadLine()
- if err != nil && err != io.EOF {
- return nil, err
- }
- ln = append(ln, line...)
- }
-
- if state == 0 {
- if len(ln) == 0 {
- if err == io.EOF {
- break;
- }
- continue;
- }
- state = 1
- /* parse start, end times */
- } else if state == 1 {
- tm := strings.Split(string(ln), " ")
- if len(tm) != 3 || tm[1] != "-->" {
- return nil, errors.New("Parsing error: Wrong file format")
- }
- sub.start, err = time_to_msecs(tm[0])
- if err != nil {
- return nil, err
- }
- sub.end, err = time_to_msecs(tm[2])
- if err != nil {
- return nil, err
- }
- state = 2
- /* parse the actual subtitle text */
- } else if state == 2 {
- if len(ln) == 0 {
- subs.PushBack(sub)
- sub = new(subtitle)
- state = 0
- } else {
- sub.text += string(ln) + "\r\n"
- }
- }
-
- if err == io.EOF {
- break;
- }
- }
-
- return subs, nil
-}
-
-/* write SubRip (srt) file */
-func write_srt(filename string, subs *list.List) error {
- var id int = 0
-
- f, err := os.Create(filename)
- if err != nil {
- return err
- }
- defer f.Close()
-
- w := bufio.NewWriter(f)
- defer w.Flush()
-
- for e := subs.Front(); e != nil; e = e.Next() {
- id++
- sub := e.Value.(*subtitle)
- fmt.Fprintf(w, "%d\r\n", id)
- fmt.Fprintf(w, "%s --> %s\r\n", msecs_to_time(sub.start), msecs_to_time(sub.end))
- fmt.Fprintf(w, "%s\r\n", sub.text)
- }
-
- return nil
-}
-
-/* synchronize subtitles by knowing the time of the first and the last subtitle.
- * to archive this we must use the linear equation: y = mx + b */
-func sync_subs(subs *list.List, synced_first_ms uint, synced_last_ms uint) {
- var slope, yint float64
-
- desynced_first_ms := subs.Front().Value.(*subtitle).start
- desynced_last_ms := subs.Back().Value.(*subtitle).start
-
- /* m = (y2 - y1) / (x2 - x1)
- * m: slope
- * y2: synced_last_ms
- * y1: synced_first_ms
- * x2: desynced_last_ms
- * x1: desynced_first_ms */
- slope = float64(synced_last_ms - synced_first_ms) / float64(desynced_last_ms - desynced_first_ms)
- /* b = y - mx
- * b: yint
- * y: synced_last_ms
- * m: slope
- * x: desynced_last_ms */
- yint = float64(synced_last_ms) - slope * float64(desynced_last_ms)
-
- for e := subs.Front(); e != nil; e = e.Next() {
- sub := e.Value.(*subtitle)
- /* y = mx + b
- * y: sub.start and sub.end
- * m: slope
- * x: sub.start and sub.end
- * b: yint */
- sub.start = uint(roundFloat64(slope * float64(sub.start) + yint))
- sub.end = uint(roundFloat64(slope * float64(sub.end) + yint))
- }
-}
-
-func main() {
- var first_ms, last_ms uint
-
- var opts struct {
- FirstTm string `short:"f" long:"first-sub" description:"Time of first subtitle"`
- LastTm string `short:"l" long:"last-sub" description:"Time of last subtitle"`
- InputFl string `short:"i" long:"input" description:"Input file"`
- OutputFl string `short:"o" long:"output" description:"Output file"`
- PrintVersion bool `short:"v" long:"version" description:"Print version"`
- }
-
- _, err := flags.Parse(&opts)
- if err != nil {
- if err.(*flags.Error).Type == flags.ErrHelp {
- fmt.Fprintf(os.Stderr, "Example:\n")
- fmt.Fprintf(os.Stderr, " %s -f 00:01:33,492 -l 01:39:23,561 -i file.srt\n",
- path.Base(os.Args[0]))
- os.Exit(0)
- }
- os.Exit(1)
- }
-
- if opts.PrintVersion {
- fmt.Printf("subsync v%s\n", version)
- os.Exit(0)
- }
-
- if opts.InputFl == "" {
- fmt.Fprintf(os.Stderr, "You must specify an input file with -i option.\n")
- os.Exit(1)
- }
-
- if opts.FirstTm != "" {
- first_ms, err = time_to_msecs(opts.FirstTm)
- if err != nil {
- fmt.Fprintf(os.Stderr, "Please check the value of -f option.\n")
- die(err)
- }
- }
-
- if opts.LastTm != "" {
- last_ms, err = time_to_msecs(opts.LastTm)
- if err != nil {
- fmt.Fprintf(os.Stderr, "Please check the value of -l option.\n")
- die(err)
- }
- }
-
- /* if output file is not set, use the input file */
- if opts.OutputFl == "" {
- opts.OutputFl = opts.InputFl
- }
-
- subs, err := read_srt(opts.InputFl)
- if err != nil {
- die(err)
- }
-
- /* if time of the first synced subtitle is not set,
- * use the time of the first desynced subtitle */
- if opts.FirstTm == "" {
- first_ms = subs.Front().Value.(*subtitle).start
- }
-
- /* if time of the last synced subtitle is not set,
- * use the time of the last desynced subtitle */
- if opts.LastTm == "" {
- last_ms = subs.Back().Value.(*subtitle).start
- }
-
- if first_ms > last_ms {
- fmt.Fprintf(os.Stderr, "First subtitle can not be after last subtitle.\n")
- fmt.Fprintf(os.Stderr, "Please check the values of -f and/or -l options.\n")
- os.Exit(1)
- }
-
- sync_subs(subs, first_ms, last_ms)
-
- err = write_srt(opts.OutputFl, subs)
- if err != nil {
- die(err)
- }
-}
-\ No newline at end of file