Skip to content

Instantly share code, notes, and snippets.

@nainemom
Last active April 26, 2024 04:03
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save nainemom/0ac55c458f5a644959a069a16ccfb00b to your computer and use it in GitHub Desktop.
Save nainemom/0ac55c458f5a644959a069a16ccfb00b to your computer and use it in GitHub Desktop.
MPV script to translate current line of subtitle
-- Simple mpv script to translate subtitle using crow (online) or sdcv (offline) dictionaries.
-- INSTALLATION:
-- To install it, first install crow (https://crow-translate.github.io) and/or sdcv (http://dushistov.github.io/sdcv/) on your marchine with your favorite dicts and then,
-- copy this file to '~/.config/mpv/scripts/' (Linux) or '%AppData%\mpv\scripts\' (Windows).
-- sdcv help:
-- you can download offline dicts from http://download.huzheng.org/Quick/ (or anywhere else) and copy extracted dic folder to '~/.stardict/dic/'
-- CONFIGURATION:
local config = {
auto_start = false, -- false means this app need to start via key_binding. true means this app starts automatickly when video pauses.
key_binding = 'CTRL+SPACE', -- key binding to manual start this app.
dictionary = 'crow', -- one of 'sdcv', 'crow'.
crow_options = { -- no need to care if you are using sdcv.
source_language = 'en', -- check crow cli app for more options.
target_language = 'fa' -- check crow cli app for more options.
},
sdvc_options = { -- no need to care if you are using crow.
dictionary_name = 'quick_eng-persian-e2009', -- sdcv dictionary (need to be installed in '~/.stardict/dic/' or '%AppData%\stardict\dic\' on your marchine and should be accessable by sdcv). Tip: you can download mentioned dict from http://download.huzheng.org/Quick
},
}
-- USAGE:
-- To use it, when subtitle is showing something just press CTRL+SPACE key and select your favorite
-- part of visibled subtitle with CTRL+ARROW_KEYS and hit CTRL+ENTER to translate.
local utils = require 'mp.utils'
local ass = {
start = mp.get_property_osd('osd-ass-cc/0')..'{\\a2}',
stop = mp.get_property_osd('osd-ass-cc/1'),
color = '{\\1c&H00CCFF&}',
white = '{\\1c&HFFFFFF&}'
}
function split (input)
local t = {}
for _str in string.gmatch(input, '([^'..'\n'..']+)') do
for str in string.gmatch(_str, '([^'..' '..']+)') do
local replaced_str = str:gsub('[^a-zA-Z0-9_-\\ \']', '')
table.insert(t, replaced_str)
end
end
return t
end
function length(input)
local count = 0
for _ in pairs(input) do count = count + 1 end
return count
end
function show(str)
mp.osd_message(str, 9999)
end
function clear()
mp.osd_message('')
end
function _draw(input, highlighted, translated)
local result = ''
if highlighted == nil then
result = result..ass.color..input..ass.white
else
result = result..ass.white..input:gsub(highlighted, ass.color..highlighted..ass.white)
end
clear()
show(ass.start..translated..'\n---------------------------\n'..result..ass.stop)
end
function _translate(input)
local ret = ''
if config.dictionary == 'sdcv' then
local script = { args = {'sdcv' ,'-n', '-u', config.sdvc_options.dictionary_name, input } }
local result = utils.subprocess(script)
local i = 1
for str in string.gmatch(result.stdout:gsub('<BR>', '\n'), '([^'..'\n'..']+)') do
if i > 2 then
ret = ret..str..' \n'
end
i = i + 1
end
ret = ret:sub(1, -2)
ret = ret..'،'
else
local script = { args = { 'crow', '-l', config.crow_options.source_language, '-t', config.crow_options.target_language, '-b', input } }
local result = utils.subprocess(script)
ret = result.stdout:sub(1, -2)
end
return ret
end
function _get_range(arr, start_index, end_index)
local ret = ''
for k, v in pairs(arr) do
if k == start_index then
ret = v
elseif (k > start_index and k <= end_index) then
ret = ret..' '..v
end
end
return ret
end
local selection_start = 0
local selection_end = 0
local translated = nil
local is_showing = false
local sub = ''
local parsed_sub = {}
local sub_length = 0
function auto_draw()
local highlighted = _get_range(parsed_sub, selection_start, selection_end)
if translated == nil then
_draw(sub, highlighted, 'Press CTRL+ENTER to translate.')
else
_draw(sub, highlighted, translated)
end
end
function start()
mp.set_property_bool('pause', true)
mp.add_timeout(0.1, function ()
clear()
mp.set_property_bool('sub-visibility', false)
is_showing = true
selection_start = 1
selection_end = 1
sub = mp.get_property('sub-text')
parsed_sub = split(sub)
sub_length = length(parsed_sub)
translated = nil
auto_draw()
mp.add_key_binding('CTRL+LEFT', 'move_prev_1', move_prev, 'repeatable')
mp.add_key_binding('LEFT', 'move_prev_2', move_prev, 'repeatable')
mp.add_key_binding('CTRL+UP', 'move_prev_3', move_prev, 'repeatable')
mp.add_key_binding('UP', 'move_prev_4', move_prev, 'repeatable')
mp.add_key_binding('SHIFT+LEFT', 'add_prev_1', add_prev, 'repeatable')
mp.add_key_binding('SHIFT+UP', 'add_prev_2', add_prev, 'repeatable')
mp.add_key_binding('CTRL+RIGHT', 'move_next_1', move_next, 'repeatable')
mp.add_key_binding('RIGHT', 'move_next_2', move_next, 'repeatable')
mp.add_key_binding('CTRL+DOWN', 'move_next_3', move_next, 'repeatable')
mp.add_key_binding('DOWN', 'move_next_4', move_next, 'repeatable')
mp.add_key_binding('SHIFT+RIGHT', 'add_next_1', add_next, 'repeatable')
mp.add_key_binding('SHIFT+DOWN', 'add_next_2', add_next, 'repeatable')
mp.add_key_binding('CTRL+ENTER', 'translate_1', translate)
mp.add_key_binding('ENTER', 'translate_2', translate)
mp.add_key_binding('SHIFT+ENTER', 'translate_3', translate)
mp.add_key_binding('CTRL+ESC', 'stop_1', stop)
mp.add_key_binding('ESC', 'stop_2', stop)
mp.add_key_binding('SHIFT+ESC', 'stop_3', stop)
end)
end
function stop()
clear()
mp.set_property_bool('sub-visibility', true)
mp.remove_key_binding('move_prev_1')
mp.remove_key_binding('move_prev_2')
mp.remove_key_binding('move_prev_3')
mp.remove_key_binding('move_prev_4')
mp.remove_key_binding('add_prev_1')
mp.remove_key_binding('add_prev_2')
mp.remove_key_binding('move_next_1')
mp.remove_key_binding('move_next_2')
mp.remove_key_binding('move_next_3')
mp.remove_key_binding('move_next_4')
mp.remove_key_binding('add_next_1')
mp.remove_key_binding('add_next_2')
mp.remove_key_binding('translate_1')
mp.remove_key_binding('translate_2')
mp.remove_key_binding('translate_3')
mp.remove_key_binding('stop_1')
mp.remove_key_binding('stop_2')
mp.remove_key_binding('stop_3')
is_showing = false
end
function move_next()
if is_showing == false then
return
end
selection_start = selection_start + 1
selection_end = selection_start
if selection_start > sub_length then
selection_start = 1
selection_end = 1
end
translated = nil
auto_draw()
end
function add_next()
if is_showing == false then
return
end
selection_end = selection_end + 1
if selection_end > sub_length then
selection_end = sub_length
end
translated = nil
auto_draw()
end
function move_prev()
if is_showing == false then
return
end
selection_start = selection_end - 1
selection_end = selection_start
if selection_start < 1 then
selection_start = sub_length
selection_end = sub_length
end
translated = nil
auto_draw()
end
function add_prev()
if is_showing == false then
return
end
selection_end = selection_end - 1
if selection_end < selection_start then
selection_end = selection_start
end
translated = nil
auto_draw()
end
function translate()
local highlighted = _get_range(parsed_sub, selection_start, selection_end)
if config.dictionary == 'sdcv' then
translated = _translate(highlighted)
else
local waiting_text = 'Translating...'
_draw(sub, highlighted, waiting_text)
translated = _translate(highlighted)
end
_draw(sub, highlighted, translated)
end
function on_pause_change(name, value)
if value == false then
stop()
elseif value == true and config.auto_start == true then
start()
end
end
mp.observe_property('pause', 'bool', on_pause_change)
mp.add_key_binding(config.key_binding, 'start', start)
Copy link

ghost commented Nov 11, 2022

Working with all languages I've tested except with Chinese. Text is garbled, probably because of a locale issue?

@MohdNasir997
Copy link

japanese has same issue.

@NaiveInvestigator
Copy link

this script is awesome! Could we also add an option where it will translate the whole subtitle track and then save it in another subtitle file, beside the subtitle file

@cu1tur4l
Copy link

cu1tur4l commented Mar 24, 2024

Hello,
I can't get it to work. I installed crow, changed the .lua to my language and script does not work. It shows up, but does not output anything after displaying "translating" message. Do I need to change anything in crow? (I've never used it before). I'm using windows, when I was trying to look up codes using CLI I found out that cli usage for windows is kinda broken, so I suspect that's the reason why it is not working.
crow-translate/crow-translate#296
This is what I found, so it probably needs "| more"? But I don't know how to do it, I do not know how to code or anything, my "skills" end on cmd commands like cd, ping, ipconfig xd.
I tried to redneck this:

local script = { args = { 'crow', '-l', config.crow_options.source_language, '-t', config.crow_options.target_language, '-b', input, '| more' } }

but surprise surprise, it did not work.
send help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment