Image 01
profile-image

Pataglu

Pataglu Kadolle Paris, France
VLSub

VLC Extensions by exebetche 815 comments

I've lost some of my hair to make it work on my PC without any result. The same for my old mac.
Now I have a new mac, I try again, and it still doesn't work.
The text is copied in a .lua extension in the path described below.
But what's next ? How to use it ? Nothing is happening ! - Oct 06 2012
Subtitles finder

VLC Extensions by ale5000 41 comments

Ok, I hope this will be my last question :
How do I use it once it's in the right folder ?
I open a movie in VLC, and then ? How to find the subtitles ?
- Apr 19 2012
Subtitles finder

VLC Extensions by ale5000 41 comments

Thanks again for your help !
I found the folder on MAC.
The path is : content/MacOs/share/lua/playlist
- Apr 19 2012
Subtitles finder

VLC Extensions by ale5000 41 comments

Thanks for your help.
Yet I'm not successful : What do I do after the creating the .luac ? I cannot open it with vlc. Should I copy it somewhere ? - Apr 18 2012
hx milky

VLC Skins by r34 6 comments

I thought that addons for vlc were new (released with the 2.0 version. How come I can see comment from 2008 ?
I don't understand... - Mar 19 2012
Subtitles finder

VLC Extensions by ale5000 41 comments

I'm stuck at the very begining of the processus : I just can't download any file.
When I click on the download button I have a page with text on it and I have no idea what I should do with it.

"--[[
Subtitles

Copyright © 2009-2011 VideoLAN and AUTHORS

Authors: ale5000 (Based on the script made by Jean-Philippe André)

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.
--]]

--[[
Notes
- Because of internal changes in VLC, the minimum version of VLC required is now: 1.1.0 git - 2010-03-21
- VLC support only uncompressed .rar files

Know issues
- The search fail if you click the button two times consecutively


Changelog

1.04
- Updated for the latest GIT build (now almost all lua bugs are fixed)
- Bumped the minimum version required
- Minor changes

1.03
- Fixed the duplicated names that were appearing in the search
- Fixed the empty names that were appearing in the search
- Now there is a message when it doesn't find anything
- Minor optimizations
- Minor fixes

1.02
- Fix for the latest GIT build

1.01
- Changed version schema
- Now there isn't any dialog showed by default, you can still open them from the menu
- Now it is more clear when the script is working
- Added some dlg:flush() that were missing (this fix the elements in the dialog that weren't updated)
- Now it catch the fail of vlc.stream
- Minor fixes

0.1.8
- Now you can open a new dialog while the previous is still open
- Fixed getting title in the new version of VLC
- Now using dlg:flush() where appropriate
- Workarounded some bugs in VLC
IMPORTANT: Now it no longer work on old versions of VLC

0.1.7
- Removed the button "+"; it is useless since the title is updated automatically
- The download dialog can now be hidden/showed
- Added the "Load from url..." dialog
- The code of parse_archive() is now a separate function called by both "Load from url..." and the normal subtitles download dialog.
- Minor changes

0.1.6
- Some variables made local
- Minor fixes
- Added .rar archive support ("stream_filter_rar" is incomplete and can only use one file in the archive, need a fix in VLC)
Note: VLC support only uncompressed .rar files
]]

-- Global variables
dlg = nil

dialog_is_opened = false
dialog_is_hidden = false
update_title_needed = false
-- -Common
website = nil
language = nil
main_text_input = nil
search_button = nil
load_button = nil
-- -Dialog "Download subtitles"
subtitles_list = nil
subtitles_result = nil
-- -Dialog "Load subtitles from url..."
type_text_input = nil

function descriptor()
return {
title = "Subtitles";
version = "1.04";
author = "ale5000";
url = 'http://ale5000.altervista.org/vlc/extensions/subtitles-mod.lua';
description = "<center><b>Subtitles</b></center>"
.. "Get the subtitles of movies from the internet, currently only from OpenSubtitles.org<br /><br />"
.. "<img src='http://static.opensubtitles.org/favicon.ico' /> Subtitles service allowed by <a href='http://www.opensubtitles.org/'>www.OpenSubtitles.org</a><br />"
.. "<br /><b>(Based on the script made by Jean-Philippe André)</b>";
shortdesc = "Get the subtitles of movies from the internet, currently only from OpenSubtitles.org";
capabilities = { "menu"; "input-listener"--[[; "meta-listener"]] }
}
end

function menu()
if not dialog_is_hidden then
return { "Download", "Upload", "Load from url...", "About" }
else
return { "Show" }
end
end

-- Function triggered when the extension is activated
function activate()
vlc.msg.dbg(_VERSION)
vlc.msg.dbg("[Subtitles] Activating")
trigger_menu(1)
return true
end

-- Function triggered when the extension is deactivated
function deactivate()
if dialog_is_opened then
close()
else
reset_variables()
dlg = nil
end

vlc.msg.dbg("[Subtitles] Deactivated")
return true
end

function reset_variables()
update_title_needed = false
-- Common
website = nil
language = nil
main_text_input = nil
search_button = nil
load_button = nil
-- Dialog "Download subtitles"
subtitles_list = nil
subtitles_result = nil
-- Dialog "Load subtitles from url..."
type_text_input = nil
end

-- Function triggered when the dialog is closed
function close()
dialog_is_opened = false
dialog_is_hidden = false
vlc.msg.dbg("[Subtitles] Closing dialog")
reset_variables()

if dlg ~= nil then dlg:delete() end
dlg = nil
return true
end

-- Current input changed
function input_changed()
vlc.msg.dbg("[Subtitles] Input is changed")
update_title()
end

-- Current input item meta changed
--[[function meta_changed()
end]]

function update_title()
if dialog_is_hidden or not update_title_needed then return true end

vlc.msg.dbg("[Subtitles] Updating title")
local item = vlc.input.item()
if item == nil then return false end

local title = item:name() -- It return the internal title or the filename if the first is missing
if title ~= nil then
title = string.gsub(title, "(.*)%.%w+$", "%1") -- Removes file extension
if title ~= "" then
main_text_input:set_text(title)
dlg:update()
return true
end
end

return false
end

function show_dialog_download()
-- column, row, colspan, rowspan
dlg:add_label("<right><b>Database: </b></right>", 1, 1, 1, 1)
website = dlg:add_dropdown(2, 1, 3, 1)

dlg:add_label("<right><b>Language: </b></right>", 1, 2, 1, 1)
language = dlg:add_dropdown(2, 2, 3, 1)

dlg:add_label("<right><b>Search: </b></right>", 1, 3, 1, 1)
main_text_input = dlg:add_text_input("", 2, 3, 1, 1)
search_button = dlg:add_button("Search", click_search, 3, 3, 1, 1)
dlg:add_button("Hide", hide_dialog, 4, 3, 1, 1)

for idx, ws in ipairs(websites) do
website:add_value(ws.title, idx)
end
for idx, ws in ipairs(languages) do
language:add_value(ws.title, idx)
end

update_title_needed = true
update_title()

dlg:update()
return true
end

function show_dialog_upload()
-- column, row, colspan, rowspan
dlg:add_label("<right><b>Database: </b></right>", 1, 1, 1, 1)
website = dlg:add_dropdown(2, 1, 4, 1)

-- FIXME: Link is NOT yet working
dlg:add_label("<center><a href='http://www.opensubtitles.org/upload'>Upload to OpenSubtitles.org</a></center>", 1, 2, 2, 1)

for idx, ws in ipairs(websites) do
website:add_value(ws.title, idx)
end

dlg:update()
return true
end

function show_dialog_load_url()
-- column, row, colspan, rowspan
dlg:add_label("<right><b>URL: </b></right>", 1, 1, 1, 1)
main_text_input = dlg:add_text_input("", 2, 1, 3, 1)

dlg:add_label("<right><b>Type: </b></right>", 1, 2, 1, 1)
type_text_input = dlg:add_text_input("", 2, 2, 1, 1)
dlg:add_label("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;", 3, 2, 1, 1) -- Spacer
load_button = dlg:add_button("Load", click_load_from_url_button, 4, 2, 1, 1)

dlg:update()
return true
end

function show_dialog_about()
local data = descriptor()

-- column, row, colspan, rowspan
dlg:add_label("<center><b>" .. data.title .. " " .. data.version .. "</b></center>", 1, 1, 1, 1)
dlg:add_html(data.description, 1, 2, 1, 1)

dlg:update()
return true
end

function sleep(sec)
local t = vlc.misc.mdate()
vlc.misc.mwait(t + sec*1000*1000)
end

function new_dialog(title)
dlg = vlc.dialog(title)
end

function hide_dialog()
dialog_is_hidden = true
dlg:hide()
end

-- Function triggered when a element from the menu is selected
function trigger_menu(id)
if dialog_is_hidden then
if dlg == nil then
vlc.msg.err("[Subtitles] Dialog pointer lost")
close()
return false;
end

dialog_is_hidden = false
dlg:show()
return true
end
if(dialog_is_opened) then close() end

dialog_is_opened = true
if id == 1 then
new_dialog("Download subtitles")
return show_dialog_download()
elseif id == 2 then
new_dialog("Upload subtitles")
return show_dialog_upload()
elseif id == 3 then
new_dialog("Load subtitles from url...")
return show_dialog_load_url()
elseif id == 4 then
new_dialog("About")
return show_dialog_about()
end

vlc.msg.err("[Subtitles] Invalid menu id: "..id)
return false
end

function click_search()
vlc.msg.dbg("[Subtitles] Clicked search button from \"Download subtitles\" dialog")
local search_term = main_text_input:get_text()
if(search_term == "") then return false end

local old_button_name = search_button:get_text()
search_button:set_text("Wait...")
if subtitles_list ~= nil then subtitles_list:clear() end
dlg:update()

subtitles_result = nil

local idx = website:get_value()
local idx2 = language:get_value()
if idx < 1 or idx2 < 1 then vlc.msg.err("[Subtitles] Invalid index in dropdown") search_button:set_text(old_button_name) return false end

local ws = websites[idx]
local lang = languages[idx2]
local url = ws.urlfunc(search_term)

-- vlc.msg.info("[Subtitles] Url: '" .. url .. "'")
local stream = vlc.stream(url)
if stream == nil then vlc.msg.err("[Subtitles] The site of subtitles isn't reachable") search_button:set_text(old_button_name) return false end

local reading = "blah"
local xmlpage = ""
while(reading ~= nil and reading ~= "") do
reading = stream:read(65653)
if(reading) then
xmlpage = xmlpage .. reading
end
end
if xmlpage == "" then search_button:set_text(old_button_name) return false end

subtitles_result = ws.parsefunc(xmlpage)

if subtitles_list == nil then
subtitles_list = dlg:add_list(1, 4, 4, 1)
load_button = dlg:add_button("Load selected subtitles", click_load_from_search_button, 1, 5, 4, 1)
end

if not subtitles_result then
subtitles_result = {}
subtitles_result[1]= { url = "-1" }
subtitles_list:add_value("Nothing found", 1)
search_button:set_text(old_button_name)
dlg:update()
return false
end

for idx, res in ipairs(subtitles_result) do
if(not res.language or lang.tag == "all" or lang.tag == res.language) then
subtitles_list:add_value("["..res.language.."] "..res.name, idx)
end
end

search_button:set_text(old_button_name)
dlg:update()
return true
end

function load_unknown_subtitles(url, language)
vlc.msg.dbg("[Subtitles] Loading "..language.." subtitle: "..url)
vlc.input.add_subtitle(url)
end

function load_subtitles_in_the_archive(dataBuffer, language)
local buffer_length = dataBuffer:len()
local files_found_in_the_compressed_file = 0
local subtitles_found_in_the_compressed_file = 0
local endIdx = 1
local srturl, extension

-- Find subtitles
while(endIdx < buffer_length) do
_, endIdx, srturl, extension = dataBuffer:find("<location>([^<]+)%.(%a%a%a?)</location>", endIdx)
if(srturl == nil ) then break end

--vlc.msg.dbg("[Subtitles] File found in the archive: " .. srturl .. extension)
files_found_in_the_compressed_file = files_found_in_the_compressed_file + 1
srturl = string.gsub(srturl, "^(%a%a%a)://", "%1://http://")

if(extension == "ass" or extension == "ssa" or extension == "srt" or extension == "smi" or extension == "sub" or extension == "rt" or extension == "txt" or extension == "mpl") then
subtitles_found_in_the_compressed_file = subtitles_found_in_the_compressed_file + 1
vlc.msg.dbg("[Subtitles] Loading "..language.." subtitle: "..srturl)
vlc.input.add_subtitle(srturl.."."..extension)
end
end
vlc.msg.info("[Subtitles] Files found in the compressed file: "..files_found_in_the_compressed_file)
vlc.msg.info("[Subtitles] Subtitles found in the compressed file: "..subtitles_found_in_the_compressed_file)

if(subtitles_found_in_the_compressed_file > 0) then return true end

vlc.msg.warn("[Subtitles] No subtitles found in the compressed file")
return false
end

function parse_archive(url, language)
if url == "-1" then vlc.msg.dbg("[Subtitles] Dummy result") return true end

local stream = vlc.stream(url)
if stream == nil then vlc.msg.err("[Subtitles] The site of subtitles isn't reachable") return false end
stream:addfilter("zip,stream_filter_rar")

local data = stream:read(2048)
if(data == nil or data:find("<?xml version", 1, true) ~= 1) then
vlc.msg.info("[Subtitles] Type: RAR or unknown file")
load_unknown_subtitles(url, language)
else
vlc.msg.info("[Subtitles] Type: ZIP file")
local dataBuffer = ""
while(data ~= nil and data ~= "") do
vlc.msg.dbg("Buffering...")
dataBuffer = dataBuffer..data
data = stream:read(8192)
end
load_subtitles_in_the_archive(dataBuffer, language)
end
--vlc.msg.dbg("[Subtitles] Subtitle data: "..dataBuffer)

return true
end

function click_load_from_search_button()
vlc.msg.dbg("[Subtitles] Clicked load button from \"Download subtitles\" dialog")
if(not vlc.input.is_playing()) then
vlc.msg.warn("[Subtitles] You cannot load subtitles if you aren't playing any file")
return true
end
local old_button_name = load_button:get_text()
load_button:set_text("Wait...")
dlg:update()

local selection = subtitles_list:get_selection()
local index, name

for index, name in pairs(selection) do
vlc.msg.dbg("[Subtitles] Selected the item "..index.." with the name: "..name)
vlc.msg.dbg("[Subtitles] URL: "..subtitles_result[index].url)

parse_archive(subtitles_result[index].url, subtitles_result[index].language) -- ZIP, RAR or unknown file
end

load_button:set_text(old_button_name)
dlg:update()
return true
end

function click_load_from_url_button()
vlc.msg.dbg("[Subtitles] Clicked load button in \"Load subtitles from url...\" dialog")
if(not vlc.input.is_playing()) then
vlc.msg.warn("[Subtitles] You cannot load subtitles if you aren't playing any file")
return true
end
local old_button_name = load_button:get_text()
load_button:set_text("Wait...")
type_text_input:set_text("")
dlg:update()

local url_to_load = main_text_input:get_text()
if(url_to_load == "") then return false end
vlc.msg.dbg("[Subtitles] URL: "..url_to_load)

local _, ext_pos, extension = url_to_load:find("%.(%a%a%a?)", -4)

if(ext_pos == url_to_load:len()) then
type_text_input:set_text(extension)
if(extension == "ass" or extension == "ssa" or extension == "srt" or extension == "smi" or extension == "sub" or extension == "rt" or extension == "txt" or extension == "mpl") then
load_button:set_text(old_button_name)
dlg:update()
return vlc.input.add_subtitle(url_to_load)
end
end

local result = parse_archive(url_to_load, "")
if not result then
vlc.msg.info("[Subtitles] Waiting 5 seconds before retry...")
sleep(5)
result = parse_archive(url_to_load, "")
end

load_button:set_text(old_button_name)
dlg:update()
return result
end



-- XML Parsing
function parseargs(s)
local arg = {}
string.gsub(s, "(%w+)=([\"'])(.-)%2", function (w, _, a)
arg[w] = a
end)
return arg
end

function collect(s)
local stack = {}
local top = {}
table.insert(stack, top)
local ni,c,label,xarg, empty
local i, j = 1, 1
while true do
ni,j,c,label,xarg, empty = string.find(s, "<(%/?)([%w:]+)(.-)(%/?)>", i)
if not ni then break end
local text = string.sub(s, i, ni-1)
if not string.find(text, "^%s*$") then
table.insert(top, text)
end
if empty == "/" then -- empty element tag
table.insert(top, {label=label, xarg=parseargs(xarg), empty=1})
elseif c == "" then -- start tag
top = {label=label, xarg=parseargs(xarg)}
table.insert(stack, top) -- new level
else -- end tag
local toclose = table.remove(stack) -- remove top
top = stack[#stack]
if #stack < 1 then
error("nothing to close with "..label)
end
if toclose.label ~= label then
error("trying to close "..toclose.label.." with "..label)
end
table.insert(top, toclose)
end
i = j+1
end
local text = string.sub(s, i)
if not string.find(text, "^%s*$") then
table.insert(stack[#stack], text)
end
if #stack > 1 then
error("unclosed "..stack[stack.n].label)
end
return stack[1]
end


--[[
Websites configurations
]]--

-- OpenSubtitles.org
-- This function uses first version of OS API. It will probably fail in
-- the future. We'll need a XML-RPC key btw
function urlOpenSub(search_term)
-- base = "http://api.opensubtitles.org/xml-rpc"
-- lang = "eng"
base = "http://api.opensubtitles.org/en/search/"
-- if(lang) then base = base .. "sublanguageid-" .. lang .. "/"
search_term = string.gsub(search_term, "%%", "%%37")
search_term = string.gsub(search_term, " ", "%%20")
return base .. "moviename-" .. search_term .. "/simplexml"
-- http://api.opensubtitles.org/en/search/moviename- .. search_term .. /simplexml
end

function parseOpenSub(xmltext)
vlc.msg.dbg("[Subtitles] Parsing XML data...")
local xmltext = string.gsub(xmltext, "<%?xml version=\"1%.0\" encoding=\"utf-8\"%?>", "")
local xmldata = collect(xmltext)
for a,b in pairs(xmldata) do
if type(b) == "table" then
if b.label == "search" then
xmldata = b
break
end
end
end


if xmldata == nil then return nil end

-- Subtitles information data
local subname = {}
local sub_movie = {}
local suburl = {}
local sublang = {}
local sub_language = {}
local subformat = {}
local subfilenum = {}
local subnum = 1
local baseurl = ""

-- Let's browse iteratively the 'xmldata' tree
-- OK, the variables' names aren't explicit enough, but just remember a couple
-- a,b contains the index (a) and the data (b) of the table, which might also be a table
for a,b in pairs(xmldata) do
if type(b) == "table" then
if b.label == "results" then
for c,d in pairs(b) do
if type(d) == "table" then
if d.label == "subtitle" then
for e,f in pairs(d) do
if type(f) == "table" then
if f.label == "releasename" then
if f[1] ~= nil then subname[subnum] = f[1]
else subname[subnum] = "" end
elseif f.label == "movie" then
if f[1] ~= nil then sub_movie[subnum] = f[1]
else sub_movie[subnum] = "" end
elseif f.label == "download" then
if f[1] ~= nil then suburl[subnum] = f[1]
else suburl[subnum] = "" end
elseif f.label == "iso639" then -- language
if f[1] ~= nil then sublang[subnum] = f[1]
else sublang[subnum] = "" end
elseif f.label == "language" then -- language -- not use yet
if f[1] ~= nil then sub_language[subnum] = f[1]
else sub_language[subnum] = "" end
elseif f.label == "format" then
if f[1] ~= nil then subformat[subnum] = f[1]
else subformat[subnum] = "" end
end
end
end
subnum = subnum + 1
end
end
end
elseif b.label == "base" then
baseurl = b[1]
end
end
end

if subnum <= 1 then
return nil
end

ret = {}

for i = 1,(subnum - 1) do
fullURL = baseurl .. "/" .. suburl[i]
realName = string.gsub( subname[i], "<..CDATA.", "" )
realName = string.gsub( realName, "..>", "" )
if realName == "" then
realName = string.gsub( sub_movie[i], "<..CDATA.", "" )
realName = string.gsub( realName, "..>", "" )
end

ret[i] = { name = realName,
url = fullURL,
language = sublang[i],
extension = ".zip" }
vlc.msg.dbg("[Subtitles] Found subtitle " .. i .. ": ")
vlc.msg.dbg(realName)
vlc.msg.dbg(fullURL)
end

return ret
end

-- These tables must be after all function definitions
websites = {
{ title = "OpenSubtitles.org",
urlfunc = urlOpenSub,
parsefunc = parseOpenSub } --[[;
{ title = "Fake (OS)",
urlfunc = url2,
parsefunc = parse2 }]]
}

languages = {
{ title = "All", tag = "all" },
{ title = "English", tag = "en" },
{ title = "Chinese", tag = "zh" },
{ title = "Finnish", tag = "fi" },
{ title = "French", tag = "fr" },
{ title = "German", tag = "de" },
{ title = "Italian", tag = "it" },
{ title = "Japanese", tag = "ja" },
{ title = "Polish", tag = "pl" },
{ title = "Portuguese", tag = "pt" },
{ title = "Russian", tag = "ru" },
{ title = "Spanish", tag = "es" }
}" - Mar 19 2012
Subtitles finder

VLC Extensions by ale5000 41 comments

I'm stuck at the very begining of the processus : I just can't download any file.
When I click on the download button I have a page with text on it and I have no idea what I should do with it.

"--[[
Subtitles

Copyright © 2009-2011 VideoLAN and AUTHORS

Authors: ale5000 (Based on the script made by Jean-Philippe André)

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.
--]]

--[[
Notes
- Because of internal changes in VLC, the minimum version of VLC required is now: 1.1.0 git - 2010-03-21
- VLC support only uncompressed .rar files

Know issues
- The search fail if you click the button two times consecutively


Changelog

1.04
- Updated for the latest GIT build (now almost all lua bugs are fixed)
- Bumped the minimum version required
- Minor changes

1.03
- Fixed the duplicated names that were appearing in the search
- Fixed the empty names that were appearing in the search
- Now there is a message when it doesn't find anything
- Minor optimizations
- Minor fixes

1.02
- Fix for the latest GIT build

1.01
- Changed version schema
- Now there isn't any dialog showed by default, you can still open them from the menu
- Now it is more clear when the script is working
- Added some dlg:flush() that were missing (this fix the elements in the dialog that weren't updated)
- Now it catch the fail of vlc.stream
- Minor fixes

0.1.8
- Now you can open a new dialog while the previous is still open
- Fixed getting title in the new version of VLC
- Now using dlg:flush() where appropriate
- Workarounded some bugs in VLC
IMPORTANT: Now it no longer work on old versions of VLC

0.1.7
- Removed the button "+"; it is useless since the title is updated automatically
- The download dialog can now be hidden/showed
- Added the "Load from url..." dialog
- The code of parse_archive() is now a separate function called by both "Load from url..." and the normal subtitles download dialog.
- Minor changes

0.1.6
- Some variables made local
- Minor fixes
- Added .rar archive support ("stream_filter_rar" is incomplete and can only use one file in the archive, need a fix in VLC)
Note: VLC support only uncompressed .rar files
]]

-- Global variables
dlg = nil

dialog_is_opened = false
dialog_is_hidden = false
update_title_needed = false
-- -Common
website = nil
language = nil
main_text_input = nil
search_button = nil
load_button = nil
-- -Dialog "Download subtitles"
subtitles_list = nil
subtitles_result = nil
-- -Dialog "Load subtitles from url..."
type_text_input = nil

function descriptor()
return {
title = "Subtitles";
version = "1.04";
author = "ale5000";
url = 'http://ale5000.altervista.org/vlc/extensions/subtitles-mod.lua';
description = "<center><b>Subtitles</b></center>"
.. "Get the subtitles of movies from the internet, currently only from OpenSubtitles.org<br /><br />"
.. "<img src='http://static.opensubtitles.org/favicon.ico' /> Subtitles service allowed by <a href='http://www.opensubtitles.org/'>www.OpenSubtitles.org</a><br />"
.. "<br /><b>(Based on the script made by Jean-Philippe André)</b>";
shortdesc = "Get the subtitles of movies from the internet, currently only from OpenSubtitles.org";
capabilities = { "menu"; "input-listener"--[[; "meta-listener"]] }
}
end

function menu()
if not dialog_is_hidden then
return { "Download", "Upload", "Load from url...", "About" }
else
return { "Show" }
end
end

-- Function triggered when the extension is activated
function activate()
vlc.msg.dbg(_VERSION)
vlc.msg.dbg("[Subtitles] Activating")
trigger_menu(1)
return true
end

-- Function triggered when the extension is deactivated
function deactivate()
if dialog_is_opened then
close()
else
reset_variables()
dlg = nil
end

vlc.msg.dbg("[Subtitles] Deactivated")
return true
end

function reset_variables()
update_title_needed = false
-- Common
website = nil
language = nil
main_text_input = nil
search_button = nil
load_button = nil
-- Dialog "Download subtitles"
subtitles_list = nil
subtitles_result = nil
-- Dialog "Load subtitles from url..."
type_text_input = nil
end

-- Function triggered when the dialog is closed
function close()
dialog_is_opened = false
dialog_is_hidden = false
vlc.msg.dbg("[Subtitles] Closing dialog")
reset_variables()

if dlg ~= nil then dlg:delete() end
dlg = nil
return true
end

-- Current input changed
function input_changed()
vlc.msg.dbg("[Subtitles] Input is changed")
update_title()
end

-- Current input item meta changed
--[[function meta_changed()
end]]

function update_title()
if dialog_is_hidden or not update_title_needed then return true end

vlc.msg.dbg("[Subtitles] Updating title")
local item = vlc.input.item()
if item == nil then return false end

local title = item:name() -- It return the internal title or the filename if the first is missing
if title ~= nil then
title = string.gsub(title, "(.*)%.%w+$", "%1") -- Removes file extension
if title ~= "" then
main_text_input:set_text(title)
dlg:update()
return true
end
end

return false
end

function show_dialog_download()
-- column, row, colspan, rowspan
dlg:add_label("<right><b>Database: </b></right>", 1, 1, 1, 1)
website = dlg:add_dropdown(2, 1, 3, 1)

dlg:add_label("<right><b>Language: </b></right>", 1, 2, 1, 1)
language = dlg:add_dropdown(2, 2, 3, 1)

dlg:add_label("<right><b>Search: </b></right>", 1, 3, 1, 1)
main_text_input = dlg:add_text_input("", 2, 3, 1, 1)
search_button = dlg:add_button("Search", click_search, 3, 3, 1, 1)
dlg:add_button("Hide", hide_dialog, 4, 3, 1, 1)

for idx, ws in ipairs(websites) do
website:add_value(ws.title, idx)
end
for idx, ws in ipairs(languages) do
language:add_value(ws.title, idx)
end

update_title_needed = true
update_title()

dlg:update()
return true
end

function show_dialog_upload()
-- column, row, colspan, rowspan
dlg:add_label("<right><b>Database: </b></right>", 1, 1, 1, 1)
website = dlg:add_dropdown(2, 1, 4, 1)

-- FIXME: Link is NOT yet working
dlg:add_label("<center><a href='http://www.opensubtitles.org/upload'>Upload to OpenSubtitles.org</a></center>", 1, 2, 2, 1)

for idx, ws in ipairs(websites) do
website:add_value(ws.title, idx)
end

dlg:update()
return true
end

function show_dialog_load_url()
-- column, row, colspan, rowspan
dlg:add_label("<right><b>URL: </b></right>", 1, 1, 1, 1)
main_text_input = dlg:add_text_input("", 2, 1, 3, 1)

dlg:add_label("<right><b>Type: </b></right>", 1, 2, 1, 1)
type_text_input = dlg:add_text_input("", 2, 2, 1, 1)
dlg:add_label("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;", 3, 2, 1, 1) -- Spacer
load_button = dlg:add_button("Load", click_load_from_url_button, 4, 2, 1, 1)

dlg:update()
return true
end

function show_dialog_about()
local data = descriptor()

-- column, row, colspan, rowspan
dlg:add_label("<center><b>" .. data.title .. " " .. data.version .. "</b></center>", 1, 1, 1, 1)
dlg:add_html(data.description, 1, 2, 1, 1)

dlg:update()
return true
end

function sleep(sec)
local t = vlc.misc.mdate()
vlc.misc.mwait(t + sec*1000*1000)
end

function new_dialog(title)
dlg = vlc.dialog(title)
end

function hide_dialog()
dialog_is_hidden = true
dlg:hide()
end

-- Function triggered when a element from the menu is selected
function trigger_menu(id)
if dialog_is_hidden then
if dlg == nil then
vlc.msg.err("[Subtitles] Dialog pointer lost")
close()
return false;
end

dialog_is_hidden = false
dlg:show()
return true
end
if(dialog_is_opened) then close() end

dialog_is_opened = true
if id == 1 then
new_dialog("Download subtitles")
return show_dialog_download()
elseif id == 2 then
new_dialog("Upload subtitles")
return show_dialog_upload()
elseif id == 3 then
new_dialog("Load subtitles from url...")
return show_dialog_load_url()
elseif id == 4 then
new_dialog("About")
return show_dialog_about()
end

vlc.msg.err("[Subtitles] Invalid menu id: "..id)
return false
end

function click_search()
vlc.msg.dbg("[Subtitles] Clicked search button from \"Download subtitles\" dialog")
local search_term = main_text_input:get_text()
if(search_term == "") then return false end

local old_button_name = search_button:get_text()
search_button:set_text("Wait...")
if subtitles_list ~= nil then subtitles_list:clear() end
dlg:update()

subtitles_result = nil

local idx = website:get_value()
local idx2 = language:get_value()
if idx < 1 or idx2 < 1 then vlc.msg.err("[Subtitles] Invalid index in dropdown") search_button:set_text(old_button_name) return false end

local ws = websites[idx]
local lang = languages[idx2]
local url = ws.urlfunc(search_term)

-- vlc.msg.info("[Subtitles] Url: '" .. url .. "'")
local stream = vlc.stream(url)
if stream == nil then vlc.msg.err("[Subtitles] The site of subtitles isn't reachable") search_button:set_text(old_button_name) return false end

local reading = "blah"
local xmlpage = ""
while(reading ~= nil and reading ~= "") do
reading = stream:read(65653)
if(reading) then
xmlpage = xmlpage .. reading
end
end
if xmlpage == "" then search_button:set_text(old_button_name) return false end

subtitles_result = ws.parsefunc(xmlpage)

if subtitles_list == nil then
subtitles_list = dlg:add_list(1, 4, 4, 1)
load_button = dlg:add_button("Load selected subtitles", click_load_from_search_button, 1, 5, 4, 1)
end

if not subtitles_result then
subtitles_result = {}
subtitles_result[1]= { url = "-1" }
subtitles_list:add_value("Nothing found", 1)
search_button:set_text(old_button_name)
dlg:update()
return false
end

for idx, res in ipairs(subtitles_result) do
if(not res.language or lang.tag == "all" or lang.tag == res.language) then
subtitles_list:add_value("["..res.language.."] "..res.name, idx)
end
end

search_button:set_text(old_button_name)
dlg:update()
return true
end

function load_unknown_subtitles(url, language)
vlc.msg.dbg("[Subtitles] Loading "..language.." subtitle: "..url)
vlc.input.add_subtitle(url)
end

function load_subtitles_in_the_archive(dataBuffer, language)
local buffer_length = dataBuffer:len()
local files_found_in_the_compressed_file = 0
local subtitles_found_in_the_compressed_file = 0
local endIdx = 1
local srturl, extension

-- Find subtitles
while(endIdx < buffer_length) do
_, endIdx, srturl, extension = dataBuffer:find("<location>([^<]+)%.(%a%a%a?)</location>", endIdx)
if(srturl == nil ) then break end

--vlc.msg.dbg("[Subtitles] File found in the archive: " .. srturl .. extension)
files_found_in_the_compressed_file = files_found_in_the_compressed_file + 1
srturl = string.gsub(srturl, "^(%a%a%a)://", "%1://http://")

if(extension == "ass" or extension == "ssa" or extension == "srt" or extension == "smi" or extension == "sub" or extension == "rt" or extension == "txt" or extension == "mpl") then
subtitles_found_in_the_compressed_file = subtitles_found_in_the_compressed_file + 1
vlc.msg.dbg("[Subtitles] Loading "..language.." subtitle: "..srturl)
vlc.input.add_subtitle(srturl.."."..extension)
end
end
vlc.msg.info("[Subtitles] Files found in the compressed file: "..files_found_in_the_compressed_file)
vlc.msg.info("[Subtitles] Subtitles found in the compressed file: "..subtitles_found_in_the_compressed_file)

if(subtitles_found_in_the_compressed_file > 0) then return true end

vlc.msg.warn("[Subtitles] No subtitles found in the compressed file")
return false
end

function parse_archive(url, language)
if url == "-1" then vlc.msg.dbg("[Subtitles] Dummy result") return true end

local stream = vlc.stream(url)
if stream == nil then vlc.msg.err("[Subtitles] The site of subtitles isn't reachable") return false end
stream:addfilter("zip,stream_filter_rar")

local data = stream:read(2048)
if(data == nil or data:find("<?xml version", 1, true) ~= 1) then
vlc.msg.info("[Subtitles] Type: RAR or unknown file")
load_unknown_subtitles(url, language)
else
vlc.msg.info("[Subtitles] Type: ZIP file")
local dataBuffer = ""
while(data ~= nil and data ~= "") do
vlc.msg.dbg("Buffering...")
dataBuffer = dataBuffer..data
data = stream:read(8192)
end
load_subtitles_in_the_archive(dataBuffer, language)
end
--vlc.msg.dbg("[Subtitles] Subtitle data: "..dataBuffer)

return true
end

function click_load_from_search_button()
vlc.msg.dbg("[Subtitles] Clicked load button from \"Download subtitles\" dialog")
if(not vlc.input.is_playing()) then
vlc.msg.warn("[Subtitles] You cannot load subtitles if you aren't playing any file")
return true
end
local old_button_name = load_button:get_text()
load_button:set_text("Wait...")
dlg:update()

local selection = subtitles_list:get_selection()
local index, name

for index, name in pairs(selection) do
vlc.msg.dbg("[Subtitles] Selected the item "..index.." with the name: "..name)
vlc.msg.dbg("[Subtitles] URL: "..subtitles_result[index].url)

parse_archive(subtitles_result[index].url, subtitles_result[index].language) -- ZIP, RAR or unknown file
end

load_button:set_text(old_button_name)
dlg:update()
return true
end

function click_load_from_url_button()
vlc.msg.dbg("[Subtitles] Clicked load button in \"Load subtitles from url...\" dialog")
if(not vlc.input.is_playing()) then
vlc.msg.warn("[Subtitles] You cannot load subtitles if you aren't playing any file")
return true
end
local old_button_name = load_button:get_text()
load_button:set_text("Wait...")
type_text_input:set_text("")
dlg:update()

local url_to_load = main_text_input:get_text()
if(url_to_load == "") then return false end
vlc.msg.dbg("[Subtitles] URL: "..url_to_load)

local _, ext_pos, extension = url_to_load:find("%.(%a%a%a?)", -4)

if(ext_pos == url_to_load:len()) then
type_text_input:set_text(extension)
if(extension == "ass" or extension == "ssa" or extension == "srt" or extension == "smi" or extension == "sub" or extension == "rt" or extension == "txt" or extension == "mpl") then
load_button:set_text(old_button_name)
dlg:update()
return vlc.input.add_subtitle(url_to_load)
end
end

local result = parse_archive(url_to_load, "")
if not result then
vlc.msg.info("[Subtitles] Waiting 5 seconds before retry...")
sleep(5)
result = parse_archive(url_to_load, "")
end

load_button:set_text(old_button_name)
dlg:update()
return result
end



-- XML Parsing
function parseargs(s)
local arg = {}
string.gsub(s, "(%w+)=([\"'])(.-)%2", function (w, _, a)
arg[w] = a
end)
return arg
end

function collect(s)
local stack = {}
local top = {}
table.insert(stack, top)
local ni,c,label,xarg, empty
local i, j = 1, 1
while true do
ni,j,c,label,xarg, empty = string.find(s, "<(%/?)([%w:]+)(.-)(%/?)>", i)
if not ni then break end
local text = string.sub(s, i, ni-1)
if not string.find(text, "^%s*$") then
table.insert(top, text)
end
if empty == "/" then -- empty element tag
table.insert(top, {label=label, xarg=parseargs(xarg), empty=1})
elseif c == "" then -- start tag
top = {label=label, xarg=parseargs(xarg)}
table.insert(stack, top) -- new level
else -- end tag
local toclose = table.remove(stack) -- remove top
top = stack[#stack]
if #stack < 1 then
error("nothing to close with "..label)
end
if toclose.label ~= label then
error("trying to close "..toclose.label.." with "..label)
end
table.insert(top, toclose)
end
i = j+1
end
local text = string.sub(s, i)
if not string.find(text, "^%s*$") then
table.insert(stack[#stack], text)
end
if #stack > 1 then
error("unclosed "..stack[stack.n].label)
end
return stack[1]
end


--[[
Websites configurations
]]--

-- OpenSubtitles.org
-- This function uses first version of OS API. It will probably fail in
-- the future. We'll need a XML-RPC key btw
function urlOpenSub(search_term)
-- base = "http://api.opensubtitles.org/xml-rpc"
-- lang = "eng"
base = "http://api.opensubtitles.org/en/search/"
-- if(lang) then base = base .. "sublanguageid-" .. lang .. "/"
search_term = string.gsub(search_term, "%%", "%%37")
search_term = string.gsub(search_term, " ", "%%20")
return base .. "moviename-" .. search_term .. "/simplexml"
-- http://api.opensubtitles.org/en/search/moviename- .. search_term .. /simplexml
end

function parseOpenSub(xmltext)
vlc.msg.dbg("[Subtitles] Parsing XML data...")
local xmltext = string.gsub(xmltext, "<%?xml version=\"1%.0\" encoding=\"utf-8\"%?>", "")
local xmldata = collect(xmltext)
for a,b in pairs(xmldata) do
if type(b) == "table" then
if b.label == "search" then
xmldata = b
break
end
end
end


if xmldata == nil then return nil end

-- Subtitles information data
local subname = {}
local sub_movie = {}
local suburl = {}
local sublang = {}
local sub_language = {}
local subformat = {}
local subfilenum = {}
local subnum = 1
local baseurl = ""

-- Let's browse iteratively the 'xmldata' tree
-- OK, the variables' names aren't explicit enough, but just remember a couple
-- a,b contains the index (a) and the data (b) of the table, which might also be a table
for a,b in pairs(xmldata) do
if type(b) == "table" then
if b.label == "results" then
for c,d in pairs(b) do
if type(d) == "table" then
if d.label == "subtitle" then
for e,f in pairs(d) do
if type(f) == "table" then
if f.label == "releasename" then
if f[1] ~= nil then subname[subnum] = f[1]
else subname[subnum] = "" end
elseif f.label == "movie" then
if f[1] ~= nil then sub_movie[subnum] = f[1]
else sub_movie[subnum] = "" end
elseif f.label == "download" then
if f[1] ~= nil then suburl[subnum] = f[1]
else suburl[subnum] = "" end
elseif f.label == "iso639" then -- language
if f[1] ~= nil then sublang[subnum] = f[1]
else sublang[subnum] = "" end
elseif f.label == "language" then -- language -- not use yet
if f[1] ~= nil then sub_language[subnum] = f[1]
else sub_language[subnum] = "" end
elseif f.label == "format" then
if f[1] ~= nil then subformat[subnum] = f[1]
else subformat[subnum] = "" end
end
end
end
subnum = subnum + 1
end
end
end
elseif b.label == "base" then
baseurl = b[1]
end
end
end

if subnum <= 1 then
return nil
end

ret = {}

for i = 1,(subnum - 1) do
fullURL = baseurl .. "/" .. suburl[i]
realName = string.gsub( subname[i], "<..CDATA.", "" )
realName = string.gsub( realName, "..>", "" )
if realName == "" then
realName = string.gsub( sub_movie[i], "<..CDATA.", "" )
realName = string.gsub( realName, "..>", "" )
end

ret[i] = { name = realName,
url = fullURL,
language = sublang[i],
extension = ".zip" }
vlc.msg.dbg("[Subtitles] Found subtitle " .. i .. ": ")
vlc.msg.dbg(realName)
vlc.msg.dbg(fullURL)
end

return ret
end

-- These tables must be after all function definitions
websites = {
{ title = "OpenSubtitles.org",
urlfunc = urlOpenSub,
parsefunc = parseOpenSub } --[[;
{ title = "Fake (OS)",
urlfunc = url2,
parsefunc = parse2 }]]
}

languages = {
{ title = "All", tag = "all" },
{ title = "English", tag = "en" },
{ title = "Chinese", tag = "zh" },
{ title = "Finnish", tag = "fi" },
{ title = "French", tag = "fr" },
{ title = "German", tag = "de" },
{ title = "Italian", tag = "it" },
{ title = "Japanese", tag = "ja" },
{ title = "Polish", tag = "pl" },
{ title = "Portuguese", tag = "pt" },
{ title = "Russian", tag = "ru" },
{ title = "Spanish", tag = "es" }
}" - Mar 19 2012