Skip to content

Instantly share code, notes, and snippets.

@andersonvom
Last active April 3, 2023 18:55
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andersonvom/4950960 to your computer and use it in GitHub Desktop.
Save andersonvom/4950960 to your computer and use it in GitHub Desktop.
Lua script for VLC to download subtitles from OpenSubtitles.org
--[[
VLSub Extension for VLC media player 1.1 and 2.0
Copyright 2010 Guillaume Le Maout
Authors: Guillaume Le Maout
Contact: http://addons.videolan.org/messages/?action=newmessage&username=exebetche
Bug report: http://addons.videolan.org/content/show.php/?content=148752
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
--]]
-- Extension description
function descriptor()
return { title = "VLsub" ;
version = "0.6" ;
author = "exebetche" ;
url = 'http://www.opensubtitles.org/';
shortdesc = "VLsub";
description = "<center><b>VLsub</b></center>"
.. "Dowload subtitles from OpenSubtitles.org" ;
capabilities = { "input-listener", "meta-listener" }
}
end
-- Global variables
dlg = nil -- Dialog
conflocation = 'subdownloader.conf'
url = "http://api.opensubtitles.org/xml-rpc"
progressBarSize = 40
interface_state = 0
result_state = {}
default_language = "pob"
function set_default_language()
if default_language then
for k,v in ipairs(languages) do
if v[2] == default_language then
table.insert(languages, 1, v)
return true
end
end
end
end
function activate()
vlc.msg.dbg("[VLsub] Welcome")
set_default_language()
create_dialog()
openSub.getFileInfo()
openSub.getMovieInfo()
--~ openSub.request("LogIn")
end
function deactivate()
if openSub.token then
openSub.LogOut()
end
vlc.msg.dbg("[VLsub] Bye bye!")
end
function close()
vlc.deactivate()
end
function meta_changed()
openSub.getFileInfo()
openSub.getMovieInfo()
if tmp_method_id == "hash" then
searchHash()
elseif tmp_method_id == "imdb" then
widget.get("title").input:set_text(openSub.movie.name)
widget.get("season").input:set_text(openSub.movie.seasonNumber)
widget.get("episode").input:set_text(openSub.movie.episodeNumber)
end
end
function input_changed()
return false
end
openSub = {
itemStore = nil,
actionLabel = "",
conf = {
url = "http://api.opensubtitles.org/xml-rpc",
userAgentHTTP = "VLSub",
useragent = "VLSub 0.6",
username = "",
password = "",
language = "",
downloadSub = true,
removeTag = true,
justgetlink = false
},
session = {
loginTime = 0,
token = ""
},
file = {
uri = nil,
ext = nil,
name = nil,
path = nil,
dir = nil,
hash = nil,
bytesize = nil,
fps = nil,
timems = nil,
frames = nil
},
movie = {
name = "",
season = "",
episode = "",
imdbid = nil,
imdbidShow = nil,
imdbidEpisode = nil,
imdbRequest = nil,
year = nil,
releasename = nil,
aka = nil
},
sub = {
id = nil,
authorcomment = nil,
hash = nil,
idfile = nil,
filename = nil,
content = nil,
IDSubMovieFile = nil,
score = nil,
comment = nil,
bad = nil,
languageid = nil
},
request = function(methodName)
local params = openSub.methods[methodName].params()
local reqTable = openSub.getMethodBase(methodName, params)
local request = "<?xml version='1.0'?>"..dump_xml(reqTable)
local host, path = parse_url(openSub.conf.url)
local header = {
"POST "..path.." HTTP/1.1",
"Host: "..host,
"User-Agent: "..openSub.conf.userAgentHTTP,
"Content-Type: text/xml",
"Content-Length: "..string.len(request),
"",
""
}
request = table.concat(header, "\r\n")..request
local response
local status, responseStr = http_req(host, 80, request)
if status == 200 then
response = parse_xmlrpc(responseStr)
if (response and response.status == "200 OK") then
vlc.msg.dbg(responseStr)
return openSub.methods[methodName].callback(response)
elseif response then
setError("code "..response.status.."("..status..")")
return false
else
setError("Server not responding")
return false
end
elseif status == 401 then
setError("Request unauthorized")
response = parse_xmlrpc(responseStr)
if openSub.session.token ~= response.token then
setMessage("Session expired, retrying")
openSub.session.token = response.token
openSub.request(methodName)
end
return false
elseif status == 503 then
setError("Server overloaded, please retry later")
return false
end
end,
getMethodBase = function(methodName, param)
if openSub.methods[methodName].methodName then
methodName = openSub.methods[methodName].methodName
end
local request = {
methodCall={
methodName=methodName,
params={ param=param }}}
return request
end,
methods = {
LogIn = {
params = function()
openSub.actionLabel = "Logging in"
return {
{ value={ string=openSub.conf.username } },
{ value={ string=openSub.conf.password } },
{ value={ string=openSub.conf.language } },
{ value={ string=openSub.conf.useragent } }
}
end,
callback = function(resp)
openSub.session.token = resp.token
openSub.session.loginTime = os.time()
return true
end
},
LogOut = {
params = function()
openSub.actionLabel = "Logging out"
return {
{ value={ string=openSub.session.token } }
}
end,
callback = function()
return true
end
},
NoOperation = {
params = function()
return {
{ value={ string=openSub.session.token } }
}
end,
callback = function()
return true
end
},
SearchSubtitlesByHash = {
methodName = "SearchSubtitles",
params = function()
openSub.actionLabel = "Searching subtitles"
setMessage(openSub.actionLabel..": "..progressBarContent(0))
return {
{ value={ string=openSub.session.token } },
{ value={
array={
data={
value={
struct={
member={
{ name="sublanguageid", value={ string=openSub.sub.languageid } },
{ name="moviehash", value={ string=openSub.file.hash } },
{ name="moviebytesize", value={ double=openSub.file.bytesize } } }}}}}}}
}
end,
callback = function(resp)
openSub.itemStore = resp.data
if openSub.itemStore ~= "0" then
return true
else
openSub.itemStore = nil
return false
end
end
},
SearchMoviesOnIMDB = {
params = function()
openSub.actionLabel = "Searching movie on IMDB"
setMessage(openSub.actionLabel..": "..progressBarContent(0))
return {
{ value={ string=openSub.session.token } },
{ value={ string=openSub.movie.imdbRequest } }
}
end,
callback = function(resp)
openSub.itemStore = resp.data
if openSub.itemStore ~= "0" then
return true
else
openSub.itemStore = nil
return false
end
end
},
SearchSubtitlesByIdIMDB = {
methodName = "SearchSubtitles",
params = function()
openSub.actionLabel = "Searching subtitles"
setMessage(openSub.actionLabel..": "..progressBarContent(0))
return {
{ value={ string=openSub.session.token } },
{ value={
array={
data={
value={
struct={
member={
{ name="sublanguageid", value={ string=openSub.sub.languageid } },
{ name="imdbid", value={ string=openSub.movie.imdbid } } }}}}}}}
}
end,
callback = function(resp)
openSub.itemStore = resp.data
if openSub.itemStore ~= "0" then
return true
else
openSub.itemStore = nil
return false
end
end
},
GetIMDBMovieDetails = {
params = function()
return {
{ value={ string=openSub.session.token } },
{ value={ string=openSub.movie.imdbid } }
}
end,
callback = function(resp)
print(dump_xml(resp))
end
},
IsTVserie = {
methodName = "GetIMDBMovieDetails",
params = function()
return {
{ value={ string=openSub.session.token } },
{ value={ string=openSub.movie.imdbid } }
}
end,
callback = function(resp)
return (string.lower(resp.data.kind)=="tv series")
end
}
},
getInputItem = function()
return vlc.item or vlc.input.item()
end,
getFileInfo = function()
local item = openSub.getInputItem()
if not item then
return false
else
local file = openSub.file
local parsed_uri = vlc.net.url_parse(item:uri())
file.uri = item:uri()
file.protocol = parsed_uri["protocol"]
file.path = vlc.strings.decode_uri(parsed_uri["path"])
--correction needed for windows
local windowPath = string.match(file.path, "^/(%a:/.+)$")
if windowPath then
file.path = windowPath
end
file.dir, file.completeName = string.match(file.path, "^([^\n]-/?)([^/]+)$")
file.name, file.ext = string.match(file.path, "([^/]-)%.?([^%.]*)$")
if file.ext == "part" then
file.name, file.ext = string.match(file.name, "^([^/]+)%.([^%.]+)$")
end
file.cleanName = string.gsub(file.name, "[%._]", " ")
end
end,
getMovieInfo = function()
if not openSub.file.name then
return false
end
local showName, seasonNumber, episodeNumber = string.match(openSub.file.cleanName, "(.+)[sS](%d%d)[eE](%d%d).*")
if not showName then
showName, seasonNumber, episodeNumber = string.match(openSub.file.cleanName, "(.+)(%d)[xX](%d%d).*")
end
if showName then
openSub.movie.name = showName
openSub.movie.seasonNumber = seasonNumber
openSub.movie.episodeNumber = episodeNumber
else
openSub.movie.name = openSub.file.cleanName
openSub.movie.seasonNumber = ""
openSub.movie.episodeNumber = ""
end
end,
getMovieHash = function()
openSub.actionLabel = "Calculating movie hash"
setMessage(openSub.actionLabel..": "..progressBarContent(0))
local item = openSub.getInputItem()
if not item then
setError("Please use this method during playing")
return false
end
openSub.getFileInfo()
if openSub.file.protocol ~= "file" then
setError("This method works with local file only (for now)")
return false
end
local path = openSub.file.path
if not path then
setError("File not found")
return false
end
local file = assert(io.open(path, "rb"))
if not file then
setError("File not found")
return false
end
local i = 1
local a = {0, 0, 0, 0, 0, 0, 0, 0}
local hash = ""
local size = file:seek("end")
file:seek("set", 0)
local bytes = file:read(65536)
file:seek("set", size-65536)
bytes = bytes..file:read("*all")
file:close ()
for b in string.gfind(string.format("%16X ", size), "..") do
d = tonumber(b, 16)
if type(d) ~= "nil" then a[9-i] = d end
i=i+1
end
i = 1
for b in string.gfind(bytes, ".") do
a[i] = a[i] + string.byte(b)
d = math.floor(a[i]/255)
if d>=1 then
a[i] = a[i] - d * 256
if i<8 then a[i+1] = a[i+1] + d end
end
i=i+1
if i==9 then i=1 end
end
for i=8, 1, -1 do
hash = hash..string.format("%02x",a[i])
end
openSub.file.bytesize = size
openSub.file.hash = hash
return true
end,
getImdbEpisodeId = function(season, episode)
openSub.actionLabel = "Searching episode id on IMDB"
setMessage(openSub.actionLabel..": "..progressBarContent(0))
local IMDBurl = "http://www.imdb.com/title/tt"..openSub.movie.imdbid.."/episodes/_ajax?season="..season
local host, path = parse_url(IMDBurl)
local stream = vlc.stream(IMDBurl)
local data = ""
while data do
data = stream:read(65536)
local id = string.match(data, 'data%-const="tt(%d+)"[^>]+>\r?\n<img[^>]+>\r?\n<div> S'..season..', Ep'..episode)
return id
end
return false
end,
getImdbEpisodeIdYQL = function(season, episode)
openSub.actionLabel = "Searching episode on IMDB"
setMessage(openSub.actionLabel..": "..progressBarContent(0))
local url = "http://pipes.yahoo.com/pipes/pipe.run?_id=5f525406f2b2b376eeb20b97a216bcb1&_render=json&imdbid="..openSub.movie.imdbid.."&season="..season.."&episode="..episode
local host, path = parse_url(url)
local header = {
"GET "..path.." HTTP/1.1",
"Host: "..host,
"User-Agent: "..openSub.conf.userAgentHTTP,
"",
""
}
local request = table.concat(header, "\r\n")
local fd = vlc.net.connect_tcp(host, 80)
local data = ""
if fd >= 0 then
local pollfds = {}
pollfds[fd] = vlc.net.POLLIN
vlc.net.send(fd, request)
vlc.net.poll(pollfds)
data = vlc.net.recv(fd, 2048)
print(data)
end
setMessage(openSub.actionLabel..": "..progressBarContent(100))
local id = string.match(data, '"content":"(%d+)"')
return id
end,
getImdbEpisodeIdGoogle = function(season, episode, title)
openSub.actionLabel = "Searching episode on IMDB"
setMessage(openSub.actionLabel..": "..progressBarContent(0))
local query = 'site:imdb.com tv episode "'..title..'" (#'..season..'.'..episode..')'
local url = "https://www.google.com/uds/GwebSearch?hl=fr&source=gsc&gss=.com&gl=www.google.com&context=1&key=notsupplied&v=1.0&&q="..vlc.strings.encode_uri_component(query)
local host, path = parse_url(url)
local header = {
"GET "..path.." HTTP/1.1",
"Host: "..host,
"User-Agent: "..openSub.conf.userAgentHTTP,
"",
""
}
local request = table.concat(header, "\r\n")
local fd = vlc.net.connect_tcp(host, 80)
local data = ""
if fd >= 0 then
local pollfds = {}
pollfds[fd] = vlc.net.POLLIN
vlc.net.send(fd, request)
vlc.net.poll(pollfds)
data = vlc.net.recv(fd, 2048)
--print(data)
end
setMessage(openSub.actionLabel..": "..progressBarContent(100))
local id = string.match(data, '"url":"http://www.imdb.com/title/tt(%d+)/"')
return id
end,
loadSubtitles = function(url, fileDir, SubFileName, target)
openSub.actionLabel = "Downloading subtitle"
setMessage(openSub.actionLabel..": "..progressBarContent(0))
local resp = get(url)
local subfileURI = ""
if resp then
local tmpFileName = fileDir..SubFileName..".zip"
local tmpFile = assert(io.open(tmpFileName, "wb"))
tmpFile:write(resp)
tmpFile:close()
subfileURI = "zip://"..make_uri(tmpFileName.."!/"..SubFileName)
if target then
local stream = vlc.stream(subfileURI)
local data = ""
local subfile = assert(io.open(target, "w")) -- FIXME: check for file presence before overwrite (maybe ask what to do)
while data do
if openSub.conf.removeTag then
subfile:write(remove_tag(data).."\r\n")
else
subfile:write(data.."\r\n")
end
data = stream:readline()
end
subfile:close()
subfileURI = make_uri(target, true)
end
os.remove(tmpFileName)
end
local item = vlc.item or vlc.input.item()
if item then
vlc.input.add_subtitle(subfileURI)
else
setError("No current input, unable to add subtitles "..target)
end
end
}
function make_uri(str, encode)
local iswindowPath = string.match(str, "^%a:/.+$")
-- vlc.msg.dbg(iswindowPath)
if encode then
local encodedPath = ""
for w in string.gmatch(str, "/([^/]+)") do
vlc.msg.dbg(w)
encodedPath = encodedPath.."/"..vlc.strings.encode_uri_component(w)
end
str = encodedPath
end
if iswindowPath then
return "file:///"..str
else
return "file://"..str
end
end
function searchHash()
if not hasAssociatedResult() then
openSub.sub.languageid = languages[widget.getVal("language")][2]
openSub.getMovieHash()
associatedResult()
if openSub.file.hash then
openSub.request("SearchSubtitlesByHash")
display_subtitles()
end
else
local selection = widget.getVal("hashmainlist")
if #selection > 0 then
download_subtitles(selection)
end
end
end
function searchIMBD()
local title = trim(widget.getVal("title"))
local old_title = trim(widget.get("title").value)
local season = tonumber(widget.getVal("season"))
local old_season = tonumber(widget.get("season").value)
local episode = tonumber(widget.getVal("episode"))
local old_episode = tonumber(widget.get("episode").value)
local language = languages[widget.getVal("language")][2]
local selection = widget.getVal("imdbmainlist")
local sel = (#selection > 0)
local newTitle = (title ~= old_title)
local newEpisode = (season ~= old_season or episode ~= old_episode)
local newLanguage = (language ~= openSub.sub.languageid)
local movie = openSub.movie
local imdbResults = {}
widget.get("title").value = title
widget.get("season").value = season
widget.get("episode").value = episode
openSub.sub.languageid = language
if newTitle then
movie.imdbRequest = title
movie.imdbid = nil
movie.imdbidShow = nil
if openSub.request("SearchMoviesOnIMDB") then -- search exact match
local lowerTitle = string.lower(title)
local itemTitle = ""
for i, item in ipairs(openSub.itemStore) do
-- itemTitle = string.match(item.title, "[%s\"]*([^%(\"]*)[%s\"']*%(?")
item.cleanTitle = string.match(item.title, "[%s\"]*([^%(\"]*)[%s\"']*%(?")
-- vlc.msg.dbg(itemTitle)
--[[if string.lower(itemTitle) == lowerTitle then
movie.imdbid = item.id
break
end]]
table.insert(imdbResults, item.title)
end
if not movie.imdbid then
widget.setVal("imdbmainlist")
widget.setVal("imdbmainlist", imdbResults)
end
end
end
if not movie.imdbid and sel then
local index = selection[1][1]
local item = openSub.itemStore[index]
movie.imdbid = item.id
movie.title = item.cleanTitle
movie.imdbidShow = movie.imdbid
newEpisode = true
end
if movie.imdbid then
if season and episode and (newTitle or newEpisode) then
if not newTitle then
movie.imdbid = movie.imdbidShow
end
movie.imdbidEpisode = openSub.getImdbEpisodeIdGoogle(season, episode, movie.title)
-- movie.imdbidEpisode = openSub.getImdbEpisodeId(season, episode)
if movie.imdbidEpisode then
vlc.msg.dbg("Episode imdbid: "..movie.imdbidEpisode)
movie.imdbidShow = movie.imdbid
movie.imdbid = movie.imdbidEpisode
elseif openSub.request("IsTVserie") then
movie.imdbidEpisode = openSub.getImdbEpisodeIdYQL(season, episode)
if movie.imdbidEpisode then
movie.imdbidShow = movie.imdbid
movie.imdbid = movie.imdbidEpisode
else
setError("Season/episode don't match for this title")
end
else
setError("Title not referenced as a TV serie on IMDB")
--~ -- , choose an other one and/or empty episode/season field")
widget.setVal("imdbmainlist", imdbResults)
end
end
if newTitle or newEpisode or newLanguage then
openSub.request("SearchSubtitlesByIdIMDB")
display_subtitles()
elseif sel and openSub.itemStore then
download_subtitles(selection)
end
end
end
function associatedResult()
local item = openSub.getInputItem()
if not item then return false end
result_state[tmp_method_id] = item:uri()
end
function hasAssociatedResult()
local item = openSub.getInputItem()
if not item then return false end
return (result_state[tmp_method_id] == item:uri())
end
function display_subtitles()
local list = tmp_method_id.."mainlist"
widget.setVal(list)
if openSub.itemStore then
for i, item in ipairs(openSub.itemStore) do
widget.setVal(list, item.SubFileName.." ("..item.SubSumCD.." CD)")
end
else
widget.setVal(list, "No result")
end
end
function download_subtitles(selection)
local list = tmp_method_id.."mainlist"
widget.resetSel(list) -- reset selection
local index = selection[1][1]
local item = openSub.itemStore[index]
local subfileTarget = ""
if openSub.file.dir and openSub.file.name then
subfileTarget = openSub.file.dir..openSub.file.name.."."..openSub.sub.languageid.."."..item.SubFormat
else
subfileTarget = os.tmpname() --FIXME: ask the user where to put it instaed
end
if openSub.conf.justgetlink then
setMessage("Link: <a href='"..item.ZipDownloadLink.."'>"..item.ZipDownloadLink.."</a>")
else
openSub.loadSubtitles(item.ZipDownloadLink, openSub.file.dir, item.SubFileName, subfileTarget)
close() -- Close dialog right after downloading subtitle
end
end
widget = {
stack = {},
meta = {},
registered_table = {},
main_table = {},
set_node = function(node, parent)
local left = parent.left
for k, l in pairs(node) do --parse items
local tmpTop = parent.height
local tmpLeft = left
local ltmpLeft = l.left
local ltmpTop = l.top
local tmphidden = l.hidden
l.top = parent.height + parent.top
l.left = left
l.parent = parent
if l.display == "none" or parent.hidden then
l.hidden = true
else
l.hidden = false
end
if l.type == "div" then --that's a container
l.display = (l.display or "block")
l.height = 1
for _, newNode in ipairs(l.content) do --parse lines
widget.set_node(newNode, l)
l.height = l.height+1
end
l.height = l.height - 1
left = left - 1
else --that's an item
l.display = (l.display or "inline")
if not l.input then
tmphidden = true
end
if tmphidden and not l.hidden then --~ create
widget.create(l)
elseif not tmphidden and l.hidden then --~ destroy
widget.destroy(l)
end
if not l.hidden and (ltmpTop ~= l.top or ltmpLeft ~= l.left) then
if l.input then --~ destroy
widget.destroy(l)
end
--~ recreate
widget.create(l)
end
end
--~ Store reference ID
if l.id and not widget.registered_table[l.id] then
widget.registered_table[l.id] = l
end
if l.display == "block" then
parent.height = parent.height + (l.height or 1)
left = parent.left
elseif l.display == "none" then
parent.height = (tmpTop or parent.height)
left = (tmpLeft or left)
elseif l.display == "inline" then
left = left + (l.width or 1)
end
end
end,
set_interface = function(intf_map)
local root = {left = 1, top = 0, height = 0, hidden = false}
widget.set_node(intf_map, root)
end,
destroy = function(w)
dlg:del_widget(w.input)
--~ w.input = nil
--~ w.value = nil
if widget.registered_table[w.id] then
widget.registered_table[w.id] = nil
end
end,
create = function(w)
local cur_widget
if w.type == "button" then
cur_widget = dlg:add_button(w.value or "", w.callback, w.left, w.top, w.width or 1, w.height or 1)
elseif w.type == "label" then
cur_widget = dlg:add_label(w.value or "", w.left, w.top, w.width or 1, w.height or 1)
elseif w.type == "html" then
cur_widget = dlg:add_html(w.value or "", w.left, w.top, w.width or 1, w.height or 1)
elseif w.type == "text_input" then
cur_widget = dlg:add_text_input(w.value or "", w.left, w.top, w.width or 1, w.height or 1)
elseif w.type == "password" then
cur_widget = dlg:add_password(w.value or "", w.left, w.top, w.width or 1, w.height or 1)
elseif w.type == "check_box" then
cur_widget = dlg:add_check_box(w.value or "", w.left, w.top, w.width or 1, w.height or 1)
elseif w.type == "dropdown" then
cur_widget = dlg:add_dropdown(w.left, w.top, w.width or 1, w.height or 1)
elseif w.type == "list" then
cur_widget = dlg:add_list(w.left, w.top, w.width or 1, w.height or 1)
elseif w.type == "image" then
end
if w.type == "dropdown" or w.type == "list" then
if type(w.value) == "table" then
for k, l in ipairs(w.value) do
if type(l) == "table" then
cur_widget:add_value(l[1], k)
else
cur_widget:add_value(l, k)
end
end
end
end
if w.type and w.type ~= "div" then
w.input = cur_widget
end
end,
get = function(h)
return widget.registered_table[h]
end,
setVal = function(h, val, index)
widget.set_val(widget.registered_table[h], val, index)
end,
set_val = function(w, val, index)
local input = w.input
local t = w.type
if t == "button" or
t == "label" or
t == "html" or
t == "text_input" or
t == "password" then
if type(val) == "string" then
input:set_text(val)
w.value = val
end
elseif t == "check_box" then
if type(val) == "bool" then
input:set_checked(val)
else
input:set_text(val)
end
elseif t == "dropdown" or t == "list" then
if val and index then
input:add_value(val, index)
w.value[index] = val
elseif val and not index then
if type(val) == "table" then
for k, l in ipairs(val) do
input:add_value(l, k)
table.insert(w.value, l)
end
else
input:add_value(val, #w.value+1)
table.insert(w.value, val)
end
elseif not val and not index then
input:clear()
w.value = nil
w.value = {}
end
end
end,
getVal = function(h, typeval)
if not widget.registered_table[h] then print(h) return false end
return widget.get_val(widget.registered_table[h], typeval)
end,
get_val = function(w, typeval)
local input = w.input
local t = w.type
if t == "button" or
t == "label" or
t == "html" or
t == "text_input" or
t == "password" then
return input:get_text()
elseif t == "check_box" then
if typeval == "checked" then
return input:get_checked()
else
return input:get_text()
end
elseif t == "dropdown" then
return input:get_value()
elseif t == "list" then
local selection = input:get_selection()
local output = {}
for index, name in pairs(selection)do
table.insert(output, {index, name})
end
return output
end
end,
resetSel = function(h, typeval)
local w = widget.registered_table[h]
local val = w.value
widget.set_val(w)
widget.set_val(w, val)
end
}
function create_dialog()
dlg = vlc.dialog("VLSub")
widget.set_interface(interface)
end
function set_interface()
local method_index = widget.getVal("method")
local method_id = methods[method_index][2]
if tmp_method_id then
if tmp_method_id == method_id then
return false
end
widget.get(tmp_method_id).display = "none"
else
openSub.request("LogIn")
end
tmp_method_id = method_id
widget.get(method_id).display = "block"
widget.set_interface(interface)
setMessage("")
if method_id == "hash" then
searchHash()
elseif method_id == "imdb" then
if openSub.file.name and not hasAssociatedResult() then
associatedResult()
widget.get("title").input:set_text(openSub.movie.name)
widget.get("season").input:set_text(openSub.movie.seasonNumber)
widget.get("episode").input:set_text(openSub.movie.episodeNumber)
end
end
end
function progressBarContent(pct)
local content = "<span style='color:#181; font-family: monospace;'>"
local accomplished = math.ceil(progressBarSize*pct/100)
local left = progressBarSize - accomplished
content = content .. string.rep ("░", accomplished)
content = content .. "</span>"
content = content .. string.rep ("|", left)
return content
end
function setError(str)
setMessage("<span style='color:#B23'>Error: "..str.."</span>")
end
function setMessage(str)
if widget.get("message") then
widget.setVal("message", str)
dlg:update()
end
end
function get(url)
local host, path = parse_url(url)
local header = {
"GET "..path.." HTTP/1.1",
"Host: "..host,
"User-Agent: "..openSub.conf.userAgentHTTP,
--~ "TE: identity", -- useless, and that's a shame
"",
""
}
local request = table.concat(header, "\r\n")
local response
local status, response = http_req(host, 80, request)
if status == 200 then
return response
else
return false
end
end
function http_req(host, port, request)
local fd = vlc.net.connect_tcp(host, port)
if fd >= 0 then
local pollfds = {}
pollfds[fd] = vlc.net.POLLIN
vlc.net.send(fd, request)
vlc.net.poll(pollfds)
local response = vlc.net.recv(fd, 1024)
local headerStr, body = string.match(response, "(.-\r?\n)\r?\n(.*)")
local header = parse_header(headerStr)
local contentLength = tonumber(header["Content-Length"])
local TransferEncoding = header["Transfer-Encoding"]
local status = tonumber(header["statuscode"])
local bodyLenght = string.len(body)
local pct = 0
if status ~= 200 then return status end
while contentLength and bodyLenght < contentLength do
vlc.net.poll(pollfds)
response = vlc.net.recv(fd, 1024)
if response then
body = body..response
else
vlc.net.close(fd)
return false
end
bodyLenght = string.len(body)
pct = bodyLenght / contentLength * 100
setMessage(openSub.actionLabel..": "..progressBarContent(pct))
end
vlc.net.close(fd)
return status, body
end
return ""
end
function parse_header(data)
local header = {}
for name, s, val in string.gfind(data, "([^%s:]+)(:?)%s([^\n]+)\r?\n") do
if s == "" then header['statuscode'] = tonumber(string.sub (val, 1 , 3))
else header[name] = val end
end
return header
end
function parse_url(url)
local url_parsed = vlc.net.url_parse(url)
return url_parsed["host"], url_parsed["path"], url_parsed["option"]
end
function parse_xml(data)
local tree = {}
local stack = {}
local tmp = {}
local level = 0
table.insert(stack, tree)
for op, tag, p, empty, val in string.gmatch(data, "<(%/?)([%w:]+)(.-)(%/?)>[%s\r\n\t]*([^<]*)") do
if op=="/" then
if level>1 then
level = level - 1
table.remove(stack)
end
else
level = level + 1
if val == "" then
if type(stack[level][tag]) == "nil" then
stack[level][tag] = {}
table.insert(stack, stack[level][tag])
else
if type(stack[level][tag][1]) == "nil" then
tmp = nil
tmp = stack[level][tag]
stack[level][tag] = nil
stack[level][tag] = {}
table.insert(stack[level][tag], tmp)
end
tmp = nil
tmp = {}
table.insert(stack[level][tag], tmp)
table.insert(stack, tmp)
end
else
if type(stack[level][tag]) == "nil" then
stack[level][tag] = {}
end
stack[level][tag] = vlc.strings.resolve_xml_special_chars(val)
table.insert(stack, {})
end
if empty ~= "" then
stack[level][tag] = ""
level = level - 1
table.remove(stack)
end
end
end
return tree
end
function parse_xmlrpc(data)
local tree = {}
local stack = {}
local tmp = {}
local tmpTag = ""
local level = 0
table.insert(stack, tree)
for op, tag, p, empty, val in string.gmatch(data, "<(%/?)([%w:]+)(.-)(%/?)>[%s\r\n\t]*([^<]*)") do
if op=="/" then
if tag == "member" or tag == "array" then
if level>0 then
level = level - 1
table.remove(stack)
end
end
elseif tag == "name" then
level = level + 1
if val~=""then tmpTag = vlc.strings.resolve_xml_special_chars(val) end
if type(stack[level][tmpTag]) == "nil" then
stack[level][tmpTag] = {}
table.insert(stack, stack[level][tmpTag])
else
tmp = nil
tmp = {}
table.insert(stack[level-1], tmp)
stack[level] = nil
stack[level] = tmp
table.insert(stack, tmp)
end
if empty ~= "" then
level = level - 1
stack[level][tmpTag] = ""
table.remove(stack)
end
elseif tag == "array" then
level = level + 1
tmp = nil
tmp = {}
table.insert(stack[level], tmp)
table.insert(stack, tmp)
elseif val ~= "" then
stack[level][tmpTag] = vlc.strings.resolve_xml_special_chars(val)
end
end
return tree
end
function dump_xml(data)
local level = 0
local stack = {}
local dump = ""
local function parse(data, stack)
for k,v in pairs(data) do
if type(k)=="string" then
--~ print(k)
dump = dump.."\r\n"..string.rep (" ", level).."<"..k..">"
table.insert(stack, k)
level = level + 1
elseif type(k)=="number" and k ~= 1 then
dump = dump.."\r\n"..string.rep (" ", level-1).."<"..stack[level]..">"
end
if type(v)=="table" then
parse(v, stack)
elseif type(v)=="string" then
dump = dump..vlc.strings.convert_xml_special_chars(v)
elseif type(v)=="number" then
dump = dump..v
end
if type(k)=="string" then
if type(v)=="table" then
dump = dump.."\r\n"..string.rep (" ", level-1).."</"..k..">"
else
dump = dump.."</"..k..">"
end
table.remove(stack)
level = level - 1
elseif type(k)=="number" and k ~= #data then
if type(v)=="table" then
dump = dump.."\r\n"..string.rep (" ", level-1).."</"..stack[level]..">"
else
dump = dump.."</"..stack[level]..">"
end
end
end
end
parse(data, stack)
return dump
end
function trim(str)
if not str then return "" end
return string.gsub(str, "^%s*(.-)%s*$", "%1")
end
function remove_tag(str)
return string.gsub(str, "{[^}]+}", "")
end
languages = {
{'All', 'all'},
{'Albanian', 'alb'},
{'Arabic', 'ara'},
{'Armenian', 'arm'},
{'Malay', 'may'},
{'Bosnian', 'bos'},
{'Bulgarian', 'bul'},
{'Catalan', 'cat'},
{'Basque', 'eus'},
{'Chinese (China)', 'chi'},
{'Croatian', 'hrv'},
{'Czech', 'cze'},
{'Danish', 'dan'},
{'Dutch', 'dut'},
{'English (US)', 'eng'},
{'English (UK)', 'bre'},
{'Esperanto', 'epo'},
{'Estonian', 'est'},
{'Finnish', 'fin'},
{'French', 'fre'},
{'Galician', 'glg'},
{'Georgian', 'geo'},
{'German', 'ger'},
{'Greek', 'ell'},
{'Hebrew', 'heb'},
{'Hungarian', 'hun'},
{'Indonesian', 'ind'},
{'Italian', 'ita'},
{'Japanese', 'jpn'},
{'Kazakh', 'kaz'},
{'Korean', 'kor'},
{'Latvian', 'lav'},
{'Lithuanian', 'lit'},
{'Luxembourgish', 'ltz'},
{'Macedonian', 'mac'},
{'Norwegian', 'nor'},
{'Persian', 'per'},
{'Polish', 'pol'},
{'Portuguese (Portugal)', 'por'},
{'Portuguese (Brazil)', 'pob'},
{'Romanian', 'rum'},
{'Russian', 'rus'},
{'Serbian', 'scc'},
{'Slovak', 'slo'},
{'Slovenian', 'slv'},
{'Spanish (Spain)', 'spa'},
{'Swedish', 'swe'},
{'Thai', 'tha'},
{'Turkish', 'tur'},
{'Ukrainian', 'ukr'},
{'Vietnamese', 'vie'}
}
methods = {
{"Video hash", "hash"},
{"IMDB ID", "imdb"}
}
interface = {
{
id = "header",
type = "div",
content = {
{
{ type = "label", value = "Search method:" },
{
type = "dropdown",
value = methods,
id = "method",
width = 2
},
{ type = "button", value = "Go", callback = set_interface }
},
{
{ type = "label", value = "Language:" },
{ type = "dropdown", value = languages, id = "language" , width = 2 }
}
}
},
{
id = "hash",
type = "div",
display = "none",
content = {
{
{ type = "list", width = 4, id = "hashmainlist" }
},{
{ type = "span", width = 2},
{ type = "button", value = "Ok", callback = searchHash },
{ type = "button", value = "Close", callback = close }
}
}
},
{
id = "imdb",
type = "div",
display = "none",
content = {
{
{ type = "label", value = "Title:"},
{ type = "text_input", value = openSub.movie.name or "", id = "title" }
},{
{ type = "label", value = "Season (series):"},
{ type = "text_input", value = openSub.movie.seasonNumber or "", id = "season" }
},{
{ type = "label", value = "Episode (series):"},
{ type = "text_input", value = openSub.movie.episodeNumber or "", id = "episode" },
{ type = "button", value = "Ok", callback = searchIMBD },
{ type = "button", value = "Close", callback = close }
},{
{ type = "list", width = 4, id = "imdbmainlist" }
}
}
},
{
id = "progressBar",
type = "div",
content = {
{
{ type = "label", width = 4, value = "Powered by <a href='http://www.opensubtitles.org/'>opensubtitles.org</a>", id = "message" }
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment