1# -*- coding: utf-8 -*-
2
3# Copyright (C) 2005 Osmo Salomaa
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18"""Previewing subtitles with a video player."""
19
20import aeidon
21import gaupol
22import tempfile
23
24from aeidon.i18n   import _
25from gi.repository import GLib
26from gi.repository import Gtk
27
28
29class PreviewAgent(aeidon.Delegate):
30
31    """Previewing subtitles with a video player."""
32
33    def _check_process_state(self, page, process, fout, command):
34        """Check if `process` has terminated or not."""
35        if process.poll() is None: return True
36        self._handle_output(process, fout, command)
37        return False # to not check again.
38
39    def _handle_output(self, process, fout, command):
40        """Handle output of finished `process`."""
41        fout.close()
42        with open(fout.name, "r") as f:
43            output = f.read().splitlines()
44        output = "\n".join(output[:100])
45        print("\nPREVIEW: {}\n{}".format(command, output))
46        aeidon.temp.remove(fout.name)
47        if process.returncode == 0: return
48        dialog = gaupol.PreviewErrorDialog(self.window, output)
49        gaupol.util.flash_dialog(dialog)
50
51    @aeidon.deco.export
52    def _on_preview_activate(self, *args):
53        """Preview from selected position with a video player."""
54        page = self.get_current_page()
55        rows = page.view.get_selected_rows()
56        row = (rows[0] if rows else 0)
57        position = page.project.subtitles[row].start
58        col = page.view.get_focus()[1]
59        if col == page.view.columns.TRAN_TEXT:
60            doc = aeidon.documents.TRAN
61        else: # Any other column previews the main file.
62            doc = aeidon.documents.MAIN
63        self.preview(page, position, doc)
64
65    @aeidon.deco.export
66    def preview(self, page, position, doc, temp=False):
67        """Preview from `position` with a video player."""
68        command = gaupol.util.get_preview_command()
69        offset = gaupol.conf.preview.offset
70        encoding = ("utf_8" if gaupol.conf.preview.force_utf_8 else None)
71        try:
72            process, command, fout = page.project.preview(
73                position, doc, command, offset, encoding, temp)
74        except aeidon.ProcessError as error:
75            return self._show_process_error_dialog(str(error))
76        except (IOError, OSError) as error:
77            return self._show_io_error_dialog(str(error))
78        except UnicodeError:
79            return self._show_encoding_error_dialog()
80        # GLib.child_watch_add does not appear to work on Windows,
81        # so let's watch the process by polling it at regular intervals.
82        GLib.timeout_add(1000,
83                         self._check_process_state,
84                         page, process, fout, command)
85
86    @aeidon.deco.export
87    def preview_changes(self, page, row, doc, method, args=None, kwargs=None):
88        """Preview changes caused by `method` with a video player."""
89        subtitles = [x.copy() for x in page.project.subtitles]
90        framerate = page.project.framerate
91        blocked = page.project.block_all()
92        method(register=None, *(args or ()), **(kwargs or {}))
93        position = page.project.subtitles[row].start
94        page.project.unblock_all(blocked)
95        self.preview(page, position, doc, temp=True)
96        page.project.set_framerate(framerate, register=None)
97        page.project.subtitles = subtitles
98
99    def _show_encoding_error_dialog(self):
100        """Show an error dialog after failing to encode file."""
101        title = _('Failed to encode subtitle file to temporary directory "{}"').format(tempfile.gettempdir())
102        message = _("Subtitle data could not be encoded to a temporary file for preview with the current character encoding. Please first save the subtitle file with a different character encoding.")
103        dialog = gaupol.ErrorDialog(self.window, title, message)
104        dialog.add_button(_("_OK"), Gtk.ResponseType.OK)
105        dialog.set_default_response(Gtk.ResponseType.OK)
106        gaupol.util.flash_dialog(dialog)
107
108    def _show_io_error_dialog(self, message):
109        """Show an error dialog after failing to write file."""
110        title = _('Failed to save subtitle file to temporary directory "{}"').format(tempfile.gettempdir())
111        dialog = gaupol.ErrorDialog(self.window, title, message)
112        dialog.add_button(_("_OK"), Gtk.ResponseType.OK)
113        dialog.set_default_response(Gtk.ResponseType.OK)
114        gaupol.util.flash_dialog(dialog)
115
116    def _show_process_error_dialog(self, message):
117        """Show an error dialog after failing to launch video player."""
118        title = _("Failed to launch video player")
119        dialog = gaupol.ErrorDialog(self.window, title, message)
120        dialog.add_button(_("_OK"), Gtk.ResponseType.OK)
121        dialog.set_default_response(Gtk.ResponseType.OK)
122        gaupol.util.flash_dialog(dialog)
123