SlideShare ist ein Scribd-Unternehmen logo
1 von 12
--[[
 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" }
}

Weitere ähnliche Inhalte

Was ist angesagt?

Python Code Camp for Professionals 1/4
Python Code Camp for Professionals 1/4Python Code Camp for Professionals 1/4
Python Code Camp for Professionals 1/4DEVCON
 
Chat application in java using swing and socket programming.
Chat application in java using swing and socket programming.Chat application in java using swing and socket programming.
Chat application in java using swing and socket programming.Kuldeep Jain
 
Python Code Camp for Professionals 2/4
Python Code Camp for Professionals 2/4Python Code Camp for Professionals 2/4
Python Code Camp for Professionals 2/4DEVCON
 
Why and How Powershell will rule the Command Line - Barcamp LA 4
Why and How Powershell will rule the Command Line - Barcamp LA 4Why and How Powershell will rule the Command Line - Barcamp LA 4
Why and How Powershell will rule the Command Line - Barcamp LA 4Ilya Haykinson
 
Flask RESTful Flask HTTPAuth
Flask RESTful Flask HTTPAuthFlask RESTful Flask HTTPAuth
Flask RESTful Flask HTTPAuthEueung Mulyana
 
BUILDING MODERN PYTHON WEB FRAMEWORKS USING FLASK WITH NEIL GREY
BUILDING MODERN PYTHON WEB FRAMEWORKS USING FLASK WITH NEIL GREYBUILDING MODERN PYTHON WEB FRAMEWORKS USING FLASK WITH NEIL GREY
BUILDING MODERN PYTHON WEB FRAMEWORKS USING FLASK WITH NEIL GREYCodeCore
 
Python RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutionsPython RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutionsSolution4Future
 
Oracle Enterprise Manager 12c: EMCLI Crash Course
Oracle Enterprise Manager 12c: EMCLI Crash CourseOracle Enterprise Manager 12c: EMCLI Crash Course
Oracle Enterprise Manager 12c: EMCLI Crash CourseGokhan Atil
 
Sunil phani's take on windows powershell
Sunil phani's take on windows powershellSunil phani's take on windows powershell
Sunil phani's take on windows powershellSunil Phani
 
OSCamp #4 on Foreman | CLI tools with Foreman by Martin Bačovský
OSCamp #4 on Foreman | CLI tools with Foreman by Martin BačovskýOSCamp #4 on Foreman | CLI tools with Foreman by Martin Bačovský
OSCamp #4 on Foreman | CLI tools with Foreman by Martin BačovskýNETWAYS
 
[Strukelj] Why will Java 7.0 be so cool
[Strukelj] Why will Java 7.0 be so cool[Strukelj] Why will Java 7.0 be so cool
[Strukelj] Why will Java 7.0 be so cooljavablend
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐいHisateru Tanaka
 
Python and EM CLI: The Enterprise Management Super Tools
Python and EM CLI: The Enterprise Management Super ToolsPython and EM CLI: The Enterprise Management Super Tools
Python and EM CLI: The Enterprise Management Super ToolsSeth Miller
 
Building Content Types with Dexterity
Building Content Types with DexterityBuilding Content Types with Dexterity
Building Content Types with DexterityDavid Glick
 
Construire son JDK en 10 étapes
Construire son JDK en 10 étapesConstruire son JDK en 10 étapes
Construire son JDK en 10 étapesJosé Paumard
 
Powershell Training
Powershell TrainingPowershell Training
Powershell TrainingFahad Noaman
 

Was ist angesagt? (20)

Python Code Camp for Professionals 1/4
Python Code Camp for Professionals 1/4Python Code Camp for Professionals 1/4
Python Code Camp for Professionals 1/4
 
Chat application in java using swing and socket programming.
Chat application in java using swing and socket programming.Chat application in java using swing and socket programming.
Chat application in java using swing and socket programming.
 
Python Code Camp for Professionals 2/4
Python Code Camp for Professionals 2/4Python Code Camp for Professionals 2/4
Python Code Camp for Professionals 2/4
 
Changelog
ChangelogChangelog
Changelog
 
Why and How Powershell will rule the Command Line - Barcamp LA 4
Why and How Powershell will rule the Command Line - Barcamp LA 4Why and How Powershell will rule the Command Line - Barcamp LA 4
Why and How Powershell will rule the Command Line - Barcamp LA 4
 
Play!ng with scala
Play!ng with scalaPlay!ng with scala
Play!ng with scala
 
Flask RESTful Flask HTTPAuth
Flask RESTful Flask HTTPAuthFlask RESTful Flask HTTPAuth
Flask RESTful Flask HTTPAuth
 
BUILDING MODERN PYTHON WEB FRAMEWORKS USING FLASK WITH NEIL GREY
BUILDING MODERN PYTHON WEB FRAMEWORKS USING FLASK WITH NEIL GREYBUILDING MODERN PYTHON WEB FRAMEWORKS USING FLASK WITH NEIL GREY
BUILDING MODERN PYTHON WEB FRAMEWORKS USING FLASK WITH NEIL GREY
 
Python RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutionsPython RESTful webservices with Python: Flask and Django solutions
Python RESTful webservices with Python: Flask and Django solutions
 
Oracle Enterprise Manager 12c: EMCLI Crash Course
Oracle Enterprise Manager 12c: EMCLI Crash CourseOracle Enterprise Manager 12c: EMCLI Crash Course
Oracle Enterprise Manager 12c: EMCLI Crash Course
 
Sunil phani's take on windows powershell
Sunil phani's take on windows powershellSunil phani's take on windows powershell
Sunil phani's take on windows powershell
 
OSCamp #4 on Foreman | CLI tools with Foreman by Martin Bačovský
OSCamp #4 on Foreman | CLI tools with Foreman by Martin BačovskýOSCamp #4 on Foreman | CLI tools with Foreman by Martin Bačovský
OSCamp #4 on Foreman | CLI tools with Foreman by Martin Bačovský
 
Rest in flask
Rest in flaskRest in flask
Rest in flask
 
[Strukelj] Why will Java 7.0 be so cool
[Strukelj] Why will Java 7.0 be so cool[Strukelj] Why will Java 7.0 be so cool
[Strukelj] Why will Java 7.0 be so cool
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい
 
Powershell alias
Powershell aliasPowershell alias
Powershell alias
 
Python and EM CLI: The Enterprise Management Super Tools
Python and EM CLI: The Enterprise Management Super ToolsPython and EM CLI: The Enterprise Management Super Tools
Python and EM CLI: The Enterprise Management Super Tools
 
Building Content Types with Dexterity
Building Content Types with DexterityBuilding Content Types with Dexterity
Building Content Types with Dexterity
 
Construire son JDK en 10 étapes
Construire son JDK en 10 étapesConstruire son JDK en 10 étapes
Construire son JDK en 10 étapes
 
Powershell Training
Powershell TrainingPowershell Training
Powershell Training
 

Andere mochten auch

Andere mochten auch (6)

Fields Magazine DAR Region V
Fields Magazine DAR Region VFields Magazine DAR Region V
Fields Magazine DAR Region V
 
Advertising Banners on Fashion Most Wanted
Advertising Banners on Fashion Most WantedAdvertising Banners on Fashion Most Wanted
Advertising Banners on Fashion Most Wanted
 
Personal Branding for Small Business Owners
Personal Branding for Small Business OwnersPersonal Branding for Small Business Owners
Personal Branding for Small Business Owners
 
Sketsa+wajah
Sketsa+wajahSketsa+wajah
Sketsa+wajah
 
Pengurusan jenazah
Pengurusan jenazahPengurusan jenazah
Pengurusan jenazah
 
Career management
Career managementCareer management
Career management
 

Ähnlich wie Sub.luac

The Ring programming language version 1.5.2 book - Part 7 of 181
The Ring programming language version 1.5.2 book - Part 7 of 181The Ring programming language version 1.5.2 book - Part 7 of 181
The Ring programming language version 1.5.2 book - Part 7 of 181Mahmoud Samir Fayed
 
The Ring programming language version 1.8 book - Part 95 of 202
The Ring programming language version 1.8 book - Part 95 of 202The Ring programming language version 1.8 book - Part 95 of 202
The Ring programming language version 1.8 book - Part 95 of 202Mahmoud Samir Fayed
 
Advanced debugging  techniques in different environments
Advanced debugging  techniques in different environmentsAdvanced debugging  techniques in different environments
Advanced debugging  techniques in different environmentsAndrii Soldatenko
 
Host any project in che with stacks & chefiles
Host any project in che with stacks & chefilesHost any project in che with stacks & chefiles
Host any project in che with stacks & chefilesFlorent BENOIT
 
Exploring Clojurescript
Exploring ClojurescriptExploring Clojurescript
Exploring ClojurescriptLuke Donnet
 
Flask patterns
Flask patternsFlask patterns
Flask patternsit-people
 
Take the next step with git
Take the next step with gitTake the next step with git
Take the next step with gitKarin Taliga
 
Ansible Project Deploy (phpbenelux 2015)
Ansible Project Deploy (phpbenelux 2015)Ansible Project Deploy (phpbenelux 2015)
Ansible Project Deploy (phpbenelux 2015)Ramon de la Fuente
 
Apache ant
Apache antApache ant
Apache antkoniik
 
An introduction to maven gradle and sbt
An introduction to maven gradle and sbtAn introduction to maven gradle and sbt
An introduction to maven gradle and sbtFabio Fumarola
 
IzPack at LyonJUG'11
IzPack at LyonJUG'11IzPack at LyonJUG'11
IzPack at LyonJUG'11julien.ponge
 

Ähnlich wie Sub.luac (20)

Devtools cheatsheet
Devtools cheatsheetDevtools cheatsheet
Devtools cheatsheet
 
Devtools cheatsheet
Devtools cheatsheetDevtools cheatsheet
Devtools cheatsheet
 
The Ring programming language version 1.5.2 book - Part 7 of 181
The Ring programming language version 1.5.2 book - Part 7 of 181The Ring programming language version 1.5.2 book - Part 7 of 181
The Ring programming language version 1.5.2 book - Part 7 of 181
 
End-to-end testing with geb
End-to-end testing with gebEnd-to-end testing with geb
End-to-end testing with geb
 
Symfony2 revealed
Symfony2 revealedSymfony2 revealed
Symfony2 revealed
 
Simple build tool
Simple build toolSimple build tool
Simple build tool
 
Sbt for mere mortals
Sbt for mere mortalsSbt for mere mortals
Sbt for mere mortals
 
The Ring programming language version 1.8 book - Part 95 of 202
The Ring programming language version 1.8 book - Part 95 of 202The Ring programming language version 1.8 book - Part 95 of 202
The Ring programming language version 1.8 book - Part 95 of 202
 
Advanced debugging  techniques in different environments
Advanced debugging  techniques in different environmentsAdvanced debugging  techniques in different environments
Advanced debugging  techniques in different environments
 
Host any project in che with stacks & chefiles
Host any project in che with stacks & chefilesHost any project in che with stacks & chefiles
Host any project in che with stacks & chefiles
 
Exploring Clojurescript
Exploring ClojurescriptExploring Clojurescript
Exploring Clojurescript
 
Flask patterns
Flask patternsFlask patterns
Flask patterns
 
Take the next step with git
Take the next step with gitTake the next step with git
Take the next step with git
 
Grails Plugins
Grails PluginsGrails Plugins
Grails Plugins
 
Play 2.0
Play 2.0Play 2.0
Play 2.0
 
Ansible Project Deploy (phpbenelux 2015)
Ansible Project Deploy (phpbenelux 2015)Ansible Project Deploy (phpbenelux 2015)
Ansible Project Deploy (phpbenelux 2015)
 
Grails plugin
Grails pluginGrails plugin
Grails plugin
 
Apache ant
Apache antApache ant
Apache ant
 
An introduction to maven gradle and sbt
An introduction to maven gradle and sbtAn introduction to maven gradle and sbt
An introduction to maven gradle and sbt
 
IzPack at LyonJUG'11
IzPack at LyonJUG'11IzPack at LyonJUG'11
IzPack at LyonJUG'11
 

Kürzlich hochgeladen

VIP Call Girl Bhilai Aashi 8250192130 Independent Escort Service Bhilai
VIP Call Girl Bhilai Aashi 8250192130 Independent Escort Service BhilaiVIP Call Girl Bhilai Aashi 8250192130 Independent Escort Service Bhilai
VIP Call Girl Bhilai Aashi 8250192130 Independent Escort Service BhilaiSuhani Kapoor
 
CFO_SB_Career History_Multi Sector Experience
CFO_SB_Career History_Multi Sector ExperienceCFO_SB_Career History_Multi Sector Experience
CFO_SB_Career History_Multi Sector ExperienceSanjay Bokadia
 
Dubai Call Girls Starlet O525547819 Call Girls Dubai Showen Dating
Dubai Call Girls Starlet O525547819 Call Girls Dubai Showen DatingDubai Call Girls Starlet O525547819 Call Girls Dubai Showen Dating
Dubai Call Girls Starlet O525547819 Call Girls Dubai Showen Datingkojalkojal131
 
女王大学硕士毕业证成绩单(加急办理)认证海外毕业证
女王大学硕士毕业证成绩单(加急办理)认证海外毕业证女王大学硕士毕业证成绩单(加急办理)认证海外毕业证
女王大学硕士毕业证成绩单(加急办理)认证海外毕业证obuhobo
 
Booking open Available Pune Call Girls Ambegaon Khurd 6297143586 Call Hot In...
Booking open Available Pune Call Girls Ambegaon Khurd  6297143586 Call Hot In...Booking open Available Pune Call Girls Ambegaon Khurd  6297143586 Call Hot In...
Booking open Available Pune Call Girls Ambegaon Khurd 6297143586 Call Hot In...Call Girls in Nagpur High Profile
 
VIP Russian Call Girls Amravati Chhaya 8250192130 Independent Escort Service ...
VIP Russian Call Girls Amravati Chhaya 8250192130 Independent Escort Service ...VIP Russian Call Girls Amravati Chhaya 8250192130 Independent Escort Service ...
VIP Russian Call Girls Amravati Chhaya 8250192130 Independent Escort Service ...Suhani Kapoor
 
Dubai Call Girls Demons O525547819 Call Girls IN DUbai Natural Big Boody
Dubai Call Girls Demons O525547819 Call Girls IN DUbai Natural Big BoodyDubai Call Girls Demons O525547819 Call Girls IN DUbai Natural Big Boody
Dubai Call Girls Demons O525547819 Call Girls IN DUbai Natural Big Boodykojalkojal131
 
Final Completion Certificate of Marketing Management Internship
Final Completion Certificate of Marketing Management InternshipFinal Completion Certificate of Marketing Management Internship
Final Completion Certificate of Marketing Management InternshipSoham Mondal
 
Delhi Call Girls South Delhi 9711199171 ☎✔👌✔ Whatsapp Hard And Sexy Vip Call
Delhi Call Girls South Delhi 9711199171 ☎✔👌✔ Whatsapp Hard And Sexy Vip CallDelhi Call Girls South Delhi 9711199171 ☎✔👌✔ Whatsapp Hard And Sexy Vip Call
Delhi Call Girls South Delhi 9711199171 ☎✔👌✔ Whatsapp Hard And Sexy Vip Callshivangimorya083
 
Zeeman Effect normal and Anomalous zeeman effect
Zeeman Effect normal and Anomalous zeeman effectZeeman Effect normal and Anomalous zeeman effect
Zeeman Effect normal and Anomalous zeeman effectPriyanshuRawat56
 
Low Rate Call Girls Cuttack Anika 8250192130 Independent Escort Service Cuttack
Low Rate Call Girls Cuttack Anika 8250192130 Independent Escort Service CuttackLow Rate Call Girls Cuttack Anika 8250192130 Independent Escort Service Cuttack
Low Rate Call Girls Cuttack Anika 8250192130 Independent Escort Service CuttackSuhani Kapoor
 
VIP High Profile Call Girls Jamshedpur Aarushi 8250192130 Independent Escort ...
VIP High Profile Call Girls Jamshedpur Aarushi 8250192130 Independent Escort ...VIP High Profile Call Girls Jamshedpur Aarushi 8250192130 Independent Escort ...
VIP High Profile Call Girls Jamshedpur Aarushi 8250192130 Independent Escort ...Suhani Kapoor
 
Resumes, Cover Letters, and Applying Online
Resumes, Cover Letters, and Applying OnlineResumes, Cover Letters, and Applying Online
Resumes, Cover Letters, and Applying OnlineBruce Bennett
 
CALL ON ➥8923113531 🔝Call Girls Nishatganj Lucknow best sexual service
CALL ON ➥8923113531 🔝Call Girls Nishatganj Lucknow best sexual serviceCALL ON ➥8923113531 🔝Call Girls Nishatganj Lucknow best sexual service
CALL ON ➥8923113531 🔝Call Girls Nishatganj Lucknow best sexual serviceanilsa9823
 
TEST BANK For Evidence-Based Practice for Nurses Appraisal and Application of...
TEST BANK For Evidence-Based Practice for Nurses Appraisal and Application of...TEST BANK For Evidence-Based Practice for Nurses Appraisal and Application of...
TEST BANK For Evidence-Based Practice for Nurses Appraisal and Application of...robinsonayot
 
PM Job Search Council Info Session - PMI Silver Spring Chapter
PM Job Search Council Info Session - PMI Silver Spring ChapterPM Job Search Council Info Session - PMI Silver Spring Chapter
PM Job Search Council Info Session - PMI Silver Spring ChapterHector Del Castillo, CPM, CPMM
 
VIP Call Girls in Cuttack Aarohi 8250192130 Independent Escort Service Cuttack
VIP Call Girls in Cuttack Aarohi 8250192130 Independent Escort Service CuttackVIP Call Girls in Cuttack Aarohi 8250192130 Independent Escort Service Cuttack
VIP Call Girls in Cuttack Aarohi 8250192130 Independent Escort Service CuttackSuhani Kapoor
 
Production Day 1.pptxjvjbvbcbcb bj bvcbj
Production Day 1.pptxjvjbvbcbcb bj bvcbjProduction Day 1.pptxjvjbvbcbcb bj bvcbj
Production Day 1.pptxjvjbvbcbcb bj bvcbjLewisJB
 
VIP Call Girl Bhiwandi Aashi 8250192130 Independent Escort Service Bhiwandi
VIP Call Girl Bhiwandi Aashi 8250192130 Independent Escort Service BhiwandiVIP Call Girl Bhiwandi Aashi 8250192130 Independent Escort Service Bhiwandi
VIP Call Girl Bhiwandi Aashi 8250192130 Independent Escort Service BhiwandiSuhani Kapoor
 
VIP Call Girls Service Saharanpur Aishwarya 8250192130 Independent Escort Ser...
VIP Call Girls Service Saharanpur Aishwarya 8250192130 Independent Escort Ser...VIP Call Girls Service Saharanpur Aishwarya 8250192130 Independent Escort Ser...
VIP Call Girls Service Saharanpur Aishwarya 8250192130 Independent Escort Ser...Suhani Kapoor
 

Kürzlich hochgeladen (20)

VIP Call Girl Bhilai Aashi 8250192130 Independent Escort Service Bhilai
VIP Call Girl Bhilai Aashi 8250192130 Independent Escort Service BhilaiVIP Call Girl Bhilai Aashi 8250192130 Independent Escort Service Bhilai
VIP Call Girl Bhilai Aashi 8250192130 Independent Escort Service Bhilai
 
CFO_SB_Career History_Multi Sector Experience
CFO_SB_Career History_Multi Sector ExperienceCFO_SB_Career History_Multi Sector Experience
CFO_SB_Career History_Multi Sector Experience
 
Dubai Call Girls Starlet O525547819 Call Girls Dubai Showen Dating
Dubai Call Girls Starlet O525547819 Call Girls Dubai Showen DatingDubai Call Girls Starlet O525547819 Call Girls Dubai Showen Dating
Dubai Call Girls Starlet O525547819 Call Girls Dubai Showen Dating
 
女王大学硕士毕业证成绩单(加急办理)认证海外毕业证
女王大学硕士毕业证成绩单(加急办理)认证海外毕业证女王大学硕士毕业证成绩单(加急办理)认证海外毕业证
女王大学硕士毕业证成绩单(加急办理)认证海外毕业证
 
Booking open Available Pune Call Girls Ambegaon Khurd 6297143586 Call Hot In...
Booking open Available Pune Call Girls Ambegaon Khurd  6297143586 Call Hot In...Booking open Available Pune Call Girls Ambegaon Khurd  6297143586 Call Hot In...
Booking open Available Pune Call Girls Ambegaon Khurd 6297143586 Call Hot In...
 
VIP Russian Call Girls Amravati Chhaya 8250192130 Independent Escort Service ...
VIP Russian Call Girls Amravati Chhaya 8250192130 Independent Escort Service ...VIP Russian Call Girls Amravati Chhaya 8250192130 Independent Escort Service ...
VIP Russian Call Girls Amravati Chhaya 8250192130 Independent Escort Service ...
 
Dubai Call Girls Demons O525547819 Call Girls IN DUbai Natural Big Boody
Dubai Call Girls Demons O525547819 Call Girls IN DUbai Natural Big BoodyDubai Call Girls Demons O525547819 Call Girls IN DUbai Natural Big Boody
Dubai Call Girls Demons O525547819 Call Girls IN DUbai Natural Big Boody
 
Final Completion Certificate of Marketing Management Internship
Final Completion Certificate of Marketing Management InternshipFinal Completion Certificate of Marketing Management Internship
Final Completion Certificate of Marketing Management Internship
 
Delhi Call Girls South Delhi 9711199171 ☎✔👌✔ Whatsapp Hard And Sexy Vip Call
Delhi Call Girls South Delhi 9711199171 ☎✔👌✔ Whatsapp Hard And Sexy Vip CallDelhi Call Girls South Delhi 9711199171 ☎✔👌✔ Whatsapp Hard And Sexy Vip Call
Delhi Call Girls South Delhi 9711199171 ☎✔👌✔ Whatsapp Hard And Sexy Vip Call
 
Zeeman Effect normal and Anomalous zeeman effect
Zeeman Effect normal and Anomalous zeeman effectZeeman Effect normal and Anomalous zeeman effect
Zeeman Effect normal and Anomalous zeeman effect
 
Low Rate Call Girls Cuttack Anika 8250192130 Independent Escort Service Cuttack
Low Rate Call Girls Cuttack Anika 8250192130 Independent Escort Service CuttackLow Rate Call Girls Cuttack Anika 8250192130 Independent Escort Service Cuttack
Low Rate Call Girls Cuttack Anika 8250192130 Independent Escort Service Cuttack
 
VIP High Profile Call Girls Jamshedpur Aarushi 8250192130 Independent Escort ...
VIP High Profile Call Girls Jamshedpur Aarushi 8250192130 Independent Escort ...VIP High Profile Call Girls Jamshedpur Aarushi 8250192130 Independent Escort ...
VIP High Profile Call Girls Jamshedpur Aarushi 8250192130 Independent Escort ...
 
Resumes, Cover Letters, and Applying Online
Resumes, Cover Letters, and Applying OnlineResumes, Cover Letters, and Applying Online
Resumes, Cover Letters, and Applying Online
 
CALL ON ➥8923113531 🔝Call Girls Nishatganj Lucknow best sexual service
CALL ON ➥8923113531 🔝Call Girls Nishatganj Lucknow best sexual serviceCALL ON ➥8923113531 🔝Call Girls Nishatganj Lucknow best sexual service
CALL ON ➥8923113531 🔝Call Girls Nishatganj Lucknow best sexual service
 
TEST BANK For Evidence-Based Practice for Nurses Appraisal and Application of...
TEST BANK For Evidence-Based Practice for Nurses Appraisal and Application of...TEST BANK For Evidence-Based Practice for Nurses Appraisal and Application of...
TEST BANK For Evidence-Based Practice for Nurses Appraisal and Application of...
 
PM Job Search Council Info Session - PMI Silver Spring Chapter
PM Job Search Council Info Session - PMI Silver Spring ChapterPM Job Search Council Info Session - PMI Silver Spring Chapter
PM Job Search Council Info Session - PMI Silver Spring Chapter
 
VIP Call Girls in Cuttack Aarohi 8250192130 Independent Escort Service Cuttack
VIP Call Girls in Cuttack Aarohi 8250192130 Independent Escort Service CuttackVIP Call Girls in Cuttack Aarohi 8250192130 Independent Escort Service Cuttack
VIP Call Girls in Cuttack Aarohi 8250192130 Independent Escort Service Cuttack
 
Production Day 1.pptxjvjbvbcbcb bj bvcbj
Production Day 1.pptxjvjbvbcbcb bj bvcbjProduction Day 1.pptxjvjbvbcbcb bj bvcbj
Production Day 1.pptxjvjbvbcbcb bj bvcbj
 
VIP Call Girl Bhiwandi Aashi 8250192130 Independent Escort Service Bhiwandi
VIP Call Girl Bhiwandi Aashi 8250192130 Independent Escort Service BhiwandiVIP Call Girl Bhiwandi Aashi 8250192130 Independent Escort Service Bhiwandi
VIP Call Girl Bhiwandi Aashi 8250192130 Independent Escort Service Bhiwandi
 
VIP Call Girls Service Saharanpur Aishwarya 8250192130 Independent Escort Ser...
VIP Call Girls Service Saharanpur Aishwarya 8250192130 Independent Escort Ser...VIP Call Girls Service Saharanpur Aishwarya 8250192130 Independent Escort Ser...
VIP Call Girls Service Saharanpur Aishwarya 8250192130 Independent Escort Ser...
 

Sub.luac

  • 1. --[[ 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
  • 2. 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" }
  • 3. 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
  • 4. 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
  • 5. 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()
  • 6. 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()
  • 7. 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
  • 8. 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...")
  • 9. 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
  • 10. 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 = {}
  • 11. 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
  • 12. 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" } }