Exemple #1
0
def test_check_durations_too_short_gap() -> None:
    event_list = AssEventList()
    event_list.append(AssEvent(start=0, end=500, text="test"))
    event_list.append(AssEvent(start=600, end=900, text="test"))
    results = list(check_durations(event_list[0]))
    assert len(results) == 1
    assert results[0].text == "gap shorter than 250 ms (100 ms)"
def get_optimal_line_heights(
    api: Api, renderer: AssRenderer
) -> T.Dict[str, float]:
    TEST_LINE_COUNT = 20
    VIDEO_RES_X = 100
    VIDEO_RES_Y = TEST_LINE_COUNT * 300

    fake_meta = AssMeta()
    fake_meta.set("WrapStyle", 2)
    renderer.set_source(
        style_list=api.subs.styles,
        event_list=api.subs.events,
        meta=fake_meta,
        video_resolution=(VIDEO_RES_X, VIDEO_RES_Y),
    )

    ret = {}
    for style in api.subs.styles:
        event = AssEvent(
            start=0,
            end=1000,
            text="\\N".join(["gjMW"] * TEST_LINE_COUNT),
            style=style.name,
        )

        _frame_width, frame_height = measure_frame_size(api, renderer, event)
        line_height = frame_height / TEST_LINE_COUNT
        ret[event.style] = line_height
        api.log.debug(f"average height for {event.style}: {line_height}")
    return ret
Exemple #3
0
    def update_preview(self) -> None:
        selected_style = self._selected_style
        if not selected_style:
            self._preview_box.clear()
            return

        resolution = (self._preview_box.width(), self._preview_box.height())
        if resolution[0] <= 0 or resolution[1] <= 0:
            self._preview_box.clear()
            return

        fake_style = copy(selected_style)
        fake_style.name = "Default"
        if (
            self._api.video.current_stream
            and self._api.video.current_stream.is_ready
        ):
            fake_style.scale(
                resolution[1] / self._api.video.current_stream.height
            )
        fake_style_list = AssStyleList()
        fake_style_list.append(fake_style)

        fake_event = AssEvent(
            start=0,
            end=1000,
            text=self.preview_text.replace("\n", "\\N"),
            style=fake_style.name,
        )
        fake_event_list = AssEventList()
        fake_event_list.append(fake_event)

        fake_meta = AssMeta()

        image = PIL.Image.new(mode="RGBA", size=resolution)

        background_path = self._background_combobox.currentData()
        if background_path and background_path.exists():
            background = PIL.Image.open(background_path)
            for y in range(0, resolution[1], background.height):
                for x in range(0, resolution[0], background.width):
                    image.paste(background, (x, y))

        self._renderer.set_source(
            fake_style_list, fake_event_list, fake_meta, resolution
        )
        subs_image = self._renderer.render(
            time=0,
            aspect_ratio=(
                self._api.video.current_stream.aspect_ratio
                if self._api.video.current_stream
                else 1
            ),
        )
        image = PIL.Image.composite(subs_image, image, subs_image)

        image = PIL.ImageQt.ImageQt(image)
        image = QtGui.QImage(image)
        self._preview_box.setPixmap(QtGui.QPixmap.fromImage(image))
Exemple #4
0
def test_convert_to_smart_quotes() -> None:
    events = [
        AssEvent(text='"Infix". "Prefix…'),
        AssEvent(text="ignore"),
        AssEvent(text='…suffix"'),
    ]

    convert_to_smart_quotes(events, "„", "”")

    assert events[0].text == "„Infix”. „Prefix…"
    assert events[1].text == "ignore"
    assert events[2].text == "…suffix”"

    with pytest.raises(ProcessingError,
                       match="uneven double quotation mark count"):
        convert_to_smart_quotes(
            [AssEvent(text='"uneven quotation mark count')], "„", "”")
Exemple #5
0
def test_check_punctuation(text: str, violation_text: T.Optional[str]) -> None:
    event = AssEvent(text=text)
    results = list(check_punctuation("en_US", event))
    if violation_text is None:
        assert len(results) == 0
    else:
        assert len(results) == 1
        assert results[0].text == violation_text
Exemple #6
0
def test_check_double_words(text, violation_text):
    event_list = AssEventList()
    event_list.append(AssEvent(text=text))
    results = list(check_double_words(event_list[0]))
    if violation_text is None:
        assert len(results) == 0
    else:
        assert len(results) == 1
        assert results[0].text == violation_text
Exemple #7
0
def test_check_ass_tags(text, violation_text_re):
    event_list = AssEventList()
    event_list.append(AssEvent(text=text))
    results = list(check_ass_tags(event_list[0]))
    if violation_text_re is None:
        assert len(results) == 0
    else:
        assert len(results) == 1
        assert re.match(violation_text_re, results[0].text)
Exemple #8
0
def _events_section_handler(
    line: str, ass_file: AssFile, ctx: _ReadContext
) -> None:
    if line.startswith("Format:"):
        _, rest = line.split(": ", 1)
        ctx.field_names = [p.strip() for p in rest.split(",")]
        return

    event_type, rest = line.split(": ", 1)
    field_values = rest.strip().split(",", len(ctx.field_names) - 1)
    field_dict = dict(zip(ctx.field_names, field_values))

    if event_type not in {"Comment", "Dialogue"}:
        raise ValueError(f'unknown event type: "{event_type}"')

    text = field_dict["Text"]
    note = ""
    match = re.search(r"{NOTE:(?P<note>[^}]*)}", text)
    if match:
        text = text[: match.start()] + text[match.end() :]
        note = unescape_ass_tag(match.group("note"))

    # ASS tags have centisecond precision
    start = _timestamp_to_ms(field_dict["Start"])
    end = _timestamp_to_ms(field_dict["End"])

    # refine times down to millisecond precision using novelty {TIME:…} tag,
    # but only if the times match the regular ASS times. This is so that
    # subtitle times modified outside of bubblesub with editors that do not
    # write the novelty {TIME:…} tag are not overwritten.
    match = re.search(r"{TIME:(?P<start>-?\d+),(?P<end>-?\d+)}", text)
    if match:
        text = text[: match.start()] + text[match.end() :]
        start_ms = int(match.group("start"))
        end_ms = int(match.group("end"))
        if 0 <= start_ms - start < 10:
            start = start_ms
        if 0 <= end_ms - end < 10:
            end = end_ms

    ass_file.events.append(
        AssEvent(
            layer=int(field_dict["Layer"]),
            start=start,
            end=end,
            style=field_dict["Style"],
            actor=field_dict["Name"],
            margin_left=int(field_dict["MarginL"]),
            margin_right=int(field_dict["MarginR"]),
            margin_vertical=int(field_dict["MarginV"]),
            effect=field_dict["Effect"],
            text=text,
            note=note,
            is_comment=event_type == "Comment",
        )
    )
Exemple #9
0
def test_check_quotes(
        text: str, expected_violations: T.List[T.Tuple[str,
                                                       LogLevel]]) -> None:
    event_list = AssEventList()
    event_list.append(AssEvent(text=text))
    results = list(check_quotes(event_list[0]))
    assert len(results) == len(expected_violations)
    for expected_violation, result in zip(expected_violations, results):
        violation_text_re, log_level = expected_violation
        assert re.match(violation_text_re, result.text)
        assert result.log_level == log_level
Exemple #10
0
def test_check_line_continuation(texts: T.List[str],
                                 violation_text: T.Optional[str]) -> None:
    event_list = AssEventList()
    for text in texts:
        event_list.append(AssEvent(text=text))

    results = []
    for event in event_list:
        results += list(check_line_continuation(event))

    if violation_text is None:
        assert len(results) == 0
    else:
        assert len(results) == 1
        assert results[0].text == violation_text
Exemple #11
0
def test_check_unnecessary_breaks(text, violation_text):
    api = MagicMock()
    api.video.current_stream.aspect_ratio = 1
    api.subs.meta = {"PlayResX": 1280}
    with patch(
            "quality_check.check_unnecessary_breaks.measure_frame_size",
            return_value=(100, 0),
    ):
        event_list = AssEventList()
        event_list.append(AssEvent(text=text))
        renderer = MagicMock()
        results = list(check_unnecessary_breaks(event_list[0], api, renderer))

    if violation_text is None:
        assert len(results) == 0
    else:
        assert len(results) == 1
        assert results[0].text == violation_text
Exemple #12
0
    async def _run(self, main_window: QtWidgets.QMainWindow) -> None:
        path = load_dialog(
            main_window, "Subtitles (*.ass *.srt);;All files (*.*)"
        )
        if not path:
            return

        source = pysubs2.load(str(path))
        with self.api.undo.capture():
            for line in source:
                self.api.subs.events.append(
                    AssEvent(
                        start=line.start,
                        end=line.end,
                        note=line.text,
                        style=self.api.subs.default_style_name,
                    )
                )
Exemple #13
0
def create_new_subtitle(api: Api, pts: int, by_end: bool) -> None:
    current_video_stream = api.video.current_stream
    if current_video_stream:
        pts = current_video_stream.align_pts_to_near_frame(pts)
    insertion_point = get_subtitle_insertion_point(api, pts, by_end)
    if current_video_stream:
        insertion_point.start = current_video_stream.align_pts_to_near_frame(
            insertion_point.start)
        insertion_point.end = current_video_stream.align_pts_to_near_frame(
            insertion_point.end)

    api.subs.events.insert(
        insertion_point.idx,
        AssEvent(
            start=insertion_point.start,
            end=insertion_point.end,
            style=api.subs.default_style_name,
        ),
    )
    api.subs.selected_indexes = [insertion_point.idx]
Exemple #14
0
    async def run(self) -> None:
        indexes = await self.args.origin.get_indexes()
        if self.args.dir == "before":
            idx, start, end = self._insert_before(indexes)
        elif self.args.dir == "after":
            idx, start, end = self._insert_after(indexes)
        else:
            raise AssertionError

        if not self.args.no_align and self.api.video.current_stream:
            start = self.api.video.current_stream.align_pts_to_near_frame(
                start)
            end = self.api.video.current_stream.align_pts_to_near_frame(end)

        with self.api.undo.capture():
            self.api.subs.events.insert(
                idx,
                AssEvent(
                    start=start,
                    end=end,
                    style=self.api.subs.default_style_name,
                ),
            )
            self.api.subs.selected_indexes = [idx]
Exemple #15
0
def test_check_durations_comment() -> None:
    event = AssEvent(start=0, end=100, text="test", is_comment=True)
    assert len(list(check_durations(event))) == 0
Exemple #16
0
def test_check_durations_empty_text() -> None:
    event = AssEvent(start=0, end=100)
    assert len(list(check_durations(event))) == 0
Exemple #17
0
 def set_subject_text(self, sub: AssEvent, value: str) -> None:
     sub.actor = value
Exemple #18
0
def test_violation_single_event() -> None:
    event_list = AssEventList()
    event_list.append(AssEvent(start=0, end=0))
    violation = Violation(event_list[0], "test")
    assert repr(violation) == "#1: test"
Exemple #19
0
 def set_subject_text(self, sub: AssEvent, value: str) -> None:
     sub.style = value
Exemple #20
0
def test_check_durations_too_short_long_text() -> None:
    event = AssEvent(start=0, end=100, text="test test test test test")
    results = list(check_durations(event))
    assert len(results) == 1
    assert results[0].text == "duration shorter than 500 ms"
Exemple #21
0
def test_check_durations_good_duration() -> None:
    event = AssEvent(start=0, end=501, text="test test test test test")
    assert len(list(check_durations(event))) == 0
Exemple #22
0
 def set_subject_text(self, sub: AssEvent, value: str) -> None:
     sub.note = value.replace("\n", "\\N")
Exemple #23
0
def test_check_durations_good_gap() -> None:
    event_list = AssEventList()
    event_list.append(AssEvent(start=0, end=500, text="test"))
    event_list.append(AssEvent(start=750, end=900, text="test"))
    assert len(list(check_durations(event_list[0]))) == 0
Exemple #24
0
def test_violation_multiple_events() -> None:
    event_list = AssEventList()
    event_list.append(AssEvent(start=0, end=0))
    event_list.append(AssEvent(start=0, end=0))
    violation = Violation([event_list[0], event_list[1]], "test")
    assert repr(violation) == "#1+#2: test"