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