]> git.sesse.net Git - vlc/commitdiff
Update VLSub.lua
authorJean-Baptiste Kempf <jb@videolan.org>
Sun, 22 Sep 2013 19:43:26 +0000 (21:43 +0200)
committerJean-Baptiste Kempf <jb@videolan.org>
Sun, 22 Sep 2013 19:43:26 +0000 (21:43 +0200)
share/lua/extensions/VLSub.lua

index 38016d14055a696fb40af4158a4aaa9338431064..adc5ef24c68bc8fe2d3c1a1cd801097ec4f81980 100644 (file)
@@ -1,6 +1,6 @@
 --[[
  VLSub Extension for VLC media player 1.1 and 2.0
- Copyright 2010 - 2013 Guillaume Le Maout
+ Copyright 2013 Guillaume Le Maout
 
  Authors:  Guillaume Le Maout
  Contact: http://addons.videolan.org/messages/?action=newmessage&username=exebetche
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 --]]
 
+
+                                               --[[ Global var ]]--
+
+-- You can set here your default language by replacing nil with your language code (see below)
+-- Example:
+-- language = "fre",
+-- language = "ger",
+-- language = "eng",
+-- ...
+
+local options = {
+       language = nil,
+       downloadBehaviour = 'save',
+       langExt = false,
+       removeTag = false,
+       showMediaInformation = true,
+       progressBarSize = 80,
+       intLang = 'eng',
+       translations_avail = {
+               eng = 'English',
+               cze = 'Czech',
+               dan = 'Danish',
+               fre = 'Français',
+               ell = 'Greek',
+               baq = 'Basque',
+               pob = 'Brazilian Portuguese',
+               slo = 'Slovak',
+               spa = 'Spanish',
+               swe = 'Swedish',
+               ukr = 'Ukrainian'
+       },
+       translation = {
+               int_all = 'All',
+               int_descr = 'Download subtitles from OpenSubtitles.org',
+               int_research = 'Research',
+               int_config = 'Config',
+               int_configuration = 'Configuration',
+               int_help = 'Help',
+               int_search_hash = 'Search by hash',
+               int_search_name = 'Search by name',
+               int_title = 'Title',
+               int_season = 'Season (series)',
+               int_episode = 'Episode (series)',
+               int_show_help = 'Show help',
+               int_show_conf = 'Show config',
+               int_dowload_sel = 'Download selection',
+               int_close = 'Close',
+               int_ok = 'Ok',
+               int_save = 'Save',
+               int_cancel = 'Cancel',
+               int_bool_true = 'Yes',
+               int_bool_false = 'No',
+               int_search_transl = 'Search translations',
+               int_searching_transl = 'Searching translations ...',
+               int_int_lang = 'Interface language',
+               int_default_lang = 'Subtitles language',
+               int_dowload_behav = 'What to do with subtitles',
+               int_dowload_save = 'Load and save',
+               int_dowload_load = 'Load only',
+               int_dowload_manual =  'Manual download',
+               int_display_code = 'Display language code in file name',
+               int_remove_tag = 'Remove tags',
+               int_vlsub_work_dir = 'VLSub working directory',
+               int_os_username = 'Username',
+               int_os_password = 'Password',
+               int_help_mess = " Download subtittles from <a href='http://www.opensubtitles.org/'>opensubtitles.org</a> and display them while watching a video.<br>"..
+                       " <br>"..
+                       " <b><u>Usage:</u></b><br>"..
+                       " <br>"..
+                       " VLSub is meant to be used while your watching the video, so start it first (if nothing is playing you will get a link to download the subtitles in your browser).<br>"..
+                       " <br>"..
+                       " Choose the language for your subtitles and click on the button corresponding to one of the two research method provided by VLSub:<br>"..
+                       " <br>"..
+                       " <b>Method 1: Search by hash</b><br>"..
+                       " It is recommended to try this method first, because it performs a research based on the video file print, so you can find subtitles synchronized with your video.<br>"..
+                       " <br>"..
+                       " <b>Method 2: Search by name</b><br>"..
+                       " If you have no luck with the first method, just check the title is correct before clicking. If you search subtitles for a serie, you can also provide a season and episode number.<br>"..
+                       " <br>"..
+                       " <b>Downloading Subtitles</b><br>"..
+                       " Select one subtitle in the list and click on 'Download'.<br>"..
+                       " It will be put in the same directory that your video, with the same name (different extension)"..
+                       " so Vlc will load them automatically the next time you'll start the video.<br>"..
+                       " <br>"..
+                       " <b>/!\\ Beware :</b> Existing subtitles are overwrited without asking confirmation, so put them elsewhere if thet're important.<br>"..
+                       " <br>"..
+                       " Find more Vlc extensions at <a href='http://addons.videolan.org'>addons.videolan.org</a>.",
+
+               action_login = 'Logging in',
+               action_logout = 'Logging out',
+               action_noop = 'Checking session',
+               action_search = 'Searching subtitles',
+               action_hash = 'Calculating movie hash',
+
+               mess_success = 'Success',
+               mess_error = 'Error',
+               mess_no_response = 'Server not responding',
+               mess_unauthorized = 'Request unauthorized',
+               mess_expired = 'Session expired, retrying',
+               mess_overloaded = 'Server overloaded, please retry later',
+               mess_no_input = 'Please use this method during playing',
+               mess_not_local = 'This method works with local file only (for now)',
+               mess_not_found = 'File not found',
+               mess_not_found2 = 'File not found (illegal character?)',
+               mess_no_selection = 'No subtitles selected',
+               mess_save_fail = 'Unable to save subtitles',
+               mess_click_link = 'Click here to open the file',
+               mess_complete = 'Research complete',
+               mess_no_res = 'No result',
+               mess_res = 'result(s)',
+               mess_loaded = 'Subtitles loaded',
+               mess_downloading = 'Downloading subtitle',
+               mess_dowload_link = 'Download link',
+               mess_err_conf_access ='Can\'t fount a suitable path to save config, please set it manually',
+               mess_err_wrong_path ='the path contains illegal character, please correct it'
+       }
+}
+
+local languages = {
+       {'alb', 'Albanian'},
+       {'ara', 'Arabic'},
+       {'arm', 'Armenian'},
+       {'baq', 'Basque'},
+       {'ben', 'Bengali'},
+       {'bos', 'Bosnian'},
+       {'bre', 'Breton'},
+       {'bul', 'Bulgarian'},
+       {'bur', 'Burmese'},
+       {'cat', 'Catalan'},
+       {'chi', 'Chinese'},
+       {'hrv', 'Croatian'},
+       {'cze', 'Czech'},
+       {'dan', 'Danish'},
+       {'dut', 'Dutch'},
+       {'eng', 'English'},
+       {'epo', 'Esperanto'},
+       {'est', 'Estonian'},
+       {'fin', 'Finnish'},
+       {'fre', 'French'},
+       {'glg', 'Galician'},
+       {'geo', 'Georgian'},
+       {'ger', 'German'},
+       {'ell', 'Greek'},
+       {'heb', 'Hebrew'},
+       {'hin', 'Hindi'},
+       {'hun', 'Hungarian'},
+       {'ice', 'Icelandic'},
+       {'ind', 'Indonesian'},
+       {'ita', 'Italian'},
+       {'jpn', 'Japanese'},
+       {'kaz', 'Kazakh'},
+       {'khm', 'Khmer'},
+       {'kor', 'Korean'},
+       {'lav', 'Latvian'},
+       {'lit', 'Lithuanian'},
+       {'ltz', 'Luxembourgish'},
+       {'mac', 'Macedonian'},
+       {'may', 'Malay'},
+       {'mal', 'Malayalam'},
+       {'mon', 'Mongolian'},
+       {'nor', 'Norwegian'},
+       {'oci', 'Occitan'},
+       {'per', 'Persian'},
+       {'pol', 'Polish'},
+       {'por', 'Portuguese'},
+       {'pob', 'Brazilian Portuguese'},
+       {'rum', 'Romanian'},
+       {'rus', 'Russian'},
+       {'scc', 'Serbian'},
+       {'sin', 'Sinhalese'},
+       {'slo', 'Slovak'},
+       {'slv', 'Slovenian'},
+       {'spa', 'Spanish'},
+       {'swa', 'Swahili'},
+       {'swe', 'Swedish'},
+       {'syr', 'Syriac'},
+       {'tgl', 'Tagalog'},
+       {'tel', 'Telugu'},
+       {'tha', 'Thai'},
+       {'tur', 'Turkish'},
+       {'ukr', 'Ukrainian'},
+       {'urd', 'Urdu'},
+       {'vie', 'Vietnamese'}
+}
+
+-- Languages code conversion table: iso-639-1 to iso-639-3
+-- See https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
+local lang_os_to_iso = {
+       sq = "alb",
+       ar = "ara",
+       hy = "arm",
+       eu = "baq",
+       bn = "ben",
+       bs = "bos",
+       br = "bre",
+       bg = "bul",
+       my = "bur",
+       ca = "cat",
+       zh = "chi",
+       hr = "hrv",
+       cs = "cze",
+       da = "dan",
+       nl = "dut",
+       en = "eng",
+       eo = "epo",
+       et = "est",
+       fi = "fin",
+       fr = "fre",
+       gl = "glg",
+       ka = "geo",
+       de = "ger",
+       el = "ell",
+       he = "heb",
+       hi = "hin",
+       hu = "hun",
+       is = "ice",
+       id = "ind",
+       it = "ita",
+       ja = "jpn",
+       kk = "kaz",
+       km = "khm",
+       ko = "kor",
+       lv = "lav",
+       lt = "lit",
+       lb = "ltz",
+       mk = "mac",
+       ms = "may",
+       ml = "mal",
+       mn = "mon",
+       no = "nor",
+       oc = "oci",
+       fa = "per",
+       pl = "pol",
+       pt = "por",
+       po = "pob",
+       ro = "rum",
+       ru = "rus",
+       sr = "scc",
+       si = "sin",
+       sk = "slo",
+       sl = "slv",
+       es = "spa",
+       sw = "swa",
+       sv = "swe",
+       tl = "tgl",
+       te = "tel",
+       th = "tha",
+       tr = "tur",
+       uk = "ukr",
+       ur = "urd",
+       vi = "vie"
+}
+
+local dlg = nil
+local input_table = {} -- General widget id reference
+local select_conf = {} -- Drop down widget / option table association
+
+                                               --[[ Vlc extension stuff ]]--
+
 function descriptor()
-       return { title = "VLsub 0.9" ;
-               version = "0.9" ;
-               author = "exebetche" ;
-               url = 'http://www.opensubtitles.org/';
+       return {
+               title = "VLsub 0.9.10",
+               version = "0.9.10",
+               author = "exebetche",
+               url = 'http://www.opensubtitles.org/',
                shortdesc = "VLsub";
-               description = "<center><b>VLsub</b></center>"
-                               .. "Dowload subtitles from OpenSubtitles.org" ;
-               capabilities = { "input-listener", "meta-listener" }
+               description = options.translation.int_descr,
+               capabilities = {"menu", "input-listener" }
        }
 end
 
-require "os"
-
--- Global variables
-dlg = nil     -- Dialog
---~ conflocation = 'subdownloader.conf'
-url = "http://api.opensubtitles.org/xml-rpc"
-progressBarSize = 70
+function activate()
+       vlc.msg.dbg("[VLsub] Welcome")
 
---~ default_language = "fre"
-default_language = nil
-refresh_toggle = false
+    check_config()
 
-function set_default_language()
-       if default_language then
-               for k,v in ipairs(languages) do
-                       if v[2] == default_language then
-                               table.insert(languages, 1, v)
-                               return true
-                       end
-               end
+    if vlc.input.item() then
+               openSub.getFileInfo()
+               openSub.getMovieInfo()
        end
-end
 
-function activate()
-    vlc.msg.dbg("[VLsub] Welcome")
-    set_default_language()
+    show_main()
+end
 
-    create_dialog()
-       openSub.request("LogIn")
-       update_fields()
+function close()
+       deactivate()
 end
 
 function deactivate()
-       if openSub.token then
-               openSub.LogOut()
-       end
     vlc.msg.dbg("[VLsub] Bye bye!")
+    if dlg then
+               dlg:hide()
+       end
+
+       if openSub.session.token and openSub.session.token ~= "" then
+               openSub.request("LogOut")
+       end
+   vlc.deactivate()
 end
 
-function close()
-    vlc.deactivate()
+function menu()
+       return {
+               lang.int_research,
+               lang.int_config,
+               lang.int_help
+       }
 end
 
 function meta_changed()
-       update_fields()
+       return false
 end
 
 function input_changed()
-       --~ Crash !?
-       --~ wait(3)
-       --~ update_fields()
+       collectgarbage()
+       set_interface_main()
+       collectgarbage()
 end
 
-function update_fields()
+                                               --[[ Interface data ]]--
+
+function interface_main()
+       dlg:add_label(lang["int_default_lang"]..':', 1, 1, 1, 1)
+       input_table['language'] =  dlg:add_dropdown(2, 1, 2, 1)
+       dlg:add_button(lang["int_search_hash"], searchHash, 4, 1, 1, 1)
+
+       dlg:add_label(lang["int_title"]..':', 1, 2, 1, 1)
+       input_table['title'] = dlg:add_text_input(openSub.movie.title or "", 2, 2, 2, 1)
+       dlg:add_button(lang["int_search_name"], searchIMBD, 4, 2, 1, 1)
+       dlg:add_label(lang["int_season"]..':', 1, 3, 1, 1)
+       input_table['seasonNumber'] = dlg:add_text_input(openSub.movie.seasonNumber or "", 2, 3, 2, 1)
+       dlg:add_label(lang["int_episode"]..':', 1, 4, 1, 1)
+       input_table['episodeNumber'] = dlg:add_text_input(openSub.movie.episodeNumber or "", 2, 4, 2, 1)
+       input_table['mainlist'] = dlg:add_list(1, 5, 4, 1)
+       input_table['message'] = nil
+       input_table['message'] = dlg:add_label(' ', 1, 6, 4, 1)
+       dlg:add_button(lang["int_show_help"], show_help, 1, 7, 1, 1)
+       dlg:add_button('   '..lang["int_show_conf"]..'   ', show_conf, 2, 7, 1, 1)
+       dlg:add_button(lang["int_dowload_sel"], download_subtitles, 3, 7, 1, 1)
+       dlg:add_button(lang["int_close"], deactivate, 4, 7, 1, 1)
+
+       assoc_select_conf('language', 'language', openSub.conf.languages, 2, lang["int_all"])
+       display_subtitles()
+end
+
+function set_interface_main()
+       -- Update movie title and co. if video input change
+       if not type(input_table['title']) == 'userdata' then return false end
+
        openSub.getFileInfo()
        openSub.getMovieInfo()
 
-       if openSub.movie.name ~= nil then
-               widget.get("title").input:set_text(openSub.movie.name)
+       input_table['title']:set_text(openSub.movie.title or "")
+       input_table['episodeNumber']:set_text(openSub.movie.episodeNumber or "")
+       input_table['seasonNumber']:set_text(openSub.movie.seasonNumber or "")
+end
+
+function interface_config()
+       input_table['intLangLab'] = dlg:add_label(lang["int_int_lang"]..':', 1, 1, 1, 1)
+       input_table['intLangBut'] = dlg:add_button(lang["int_search_transl"], get_available_translations, 2, 1, 1, 1)
+       input_table['intLang'] = dlg:add_dropdown(3, 1, 1, 1)
+       dlg:add_label(lang["int_default_lang"]..':', 1, 2, 2, 1)
+       input_table['default_language'] = dlg:add_dropdown(3, 2, 1, 1)
+       dlg:add_label(lang["int_dowload_behav"]..':', 1, 3, 2, 1)
+       input_table['downloadBehaviour'] = dlg:add_dropdown(3, 3, 1, 1)
+
+       dlg:add_label(lang["int_display_code"]..':', 1, 4, 0, 1)
+       input_table['langExt'] = dlg:add_dropdown(3, 4, 1, 1)
+       dlg:add_label(lang["int_remove_tag"]..':', 1, 5, 0, 1)
+       input_table['removeTag'] = dlg:add_dropdown(3, 5, 1, 1)
+
+       if openSub.conf.dirPath then
+               if openSub.conf.os == "win" then
+                       dlg     :add_label("<a href='file:///"..openSub.conf.dirPath.."'>"..lang["int_vlsub_work_dir"].."</a>", 1, 6, 2, 1)
+               else
+                       dlg     :add_label("<a href='"..openSub.conf.dirPath.."'>"..lang["int_vlsub_work_dir"].."</a>", 1, 6, 2, 1)
+               end
+       else
+               dlg     :add_label(lang["int_vlsub_work_dir"], 1, 6, 2, 1)
+       end
+
+       input_table['dir_path'] = dlg:add_text_input(openSub.conf.dirPath, 2, 6, 2, 1)
+
+       dlg:add_label(lang["int_os_username"]..':', 1, 7, 0, 1)
+       input_table['os_username'] = dlg:add_text_input(openSub.option.os_username or "", 2, 7, 2, 1)
+       dlg:add_label(lang["int_os_password"]..':', 1, 8, 0, 1)
+       input_table['os_password'] = dlg:add_text_input(openSub.option.os_password or "", 2, 8, 2, 1)
+
+       input_table['message'] = nil
+       input_table['message'] = dlg:add_label(' ', 1, 9, 3, 1)
+
+       dlg:add_button(lang["int_cancel"], show_main, 2, 10, 1, 1)
+       dlg:add_button(lang["int_save"], apply_config, 3, 10, 1, 1)
+
+       input_table['langExt']:add_value(lang["int_bool_"..tostring(openSub.option.langExt)], 1)
+       input_table['langExt']:add_value(lang["int_bool_"..tostring(not openSub.option.langExt)], 2)
+       input_table['removeTag']:add_value(lang["int_bool_"..tostring(openSub.option.removeTag)], 1)
+       input_table['removeTag']:add_value(lang["int_bool_"..tostring(not openSub.option.removeTag)], 2)
+
+       assoc_select_conf('intLang', 'intLang', openSub.conf.translations_avail, 2)
+       assoc_select_conf('default_language', 'language', openSub.conf.languages, 2, lang["int_all"])
+       assoc_select_conf('downloadBehaviour', 'downloadBehaviour', openSub.conf.downloadBehaviours, 1)
+end
+
+function interface_help()
+       local help_html = lang["int_help_mess"]
+
+       input_table['help'] = dlg:add_html(help_html, 1, 1, 4, 1)
+       dlg:add_label(string.rep ("&nbsp;", 100), 1, 2, 3, 1)
+       dlg:add_button(lang["int_ok"], show_main, 4, 2, 1, 1)
+end
+
+function trigger_menu(dlg_id)
+       if dlg_id == 1 then
+               close_dlg()
+               dlg = vlc.dialog(openSub.conf.useragent)
+               interface_main()
+       elseif dlg_id == 2 then
+               close_dlg()
+               dlg = vlc.dialog(openSub.conf.useragent..': '..lang["int_configuration"])
+               interface_config()
+       elseif dlg_id == 3 then
+               close_dlg()
+               dlg = vlc.dialog(openSub.conf.useragent..': '..lang["int_help"])
+               interface_help()
+       end
+       collectgarbage() --~ !important
+end
+
+function show_main()
+       trigger_menu(1)
+end
+
+function show_conf()
+       trigger_menu(2)
+end
+
+function show_help()
+       trigger_menu(3)
+end
+
+function close_dlg()
+       vlc.msg.dbg("[VLSub] Closing dialog")
+
+       if dlg ~= nil then
+               --~ dlg:delete() -- Throw an error
+               dlg:hide()
+       end
+
+       dlg = nil
+       input_table = nil
+       input_table = {}
+       collectgarbage() --~ !important
+end
+
+                                               --[[ Drop down / config association]]--
+
+function assoc_select_conf(select_id, option, conf, ind, default)
+-- Helper for i/o interaction betwenn drop down and option list (lang...)
+       select_conf[select_id] = {cf = conf, opt  = option, dflt = default, ind = ind}
+       set_default_option(select_id)
+       display_select(select_id)
+end
+
+function set_default_option(select_id)
+-- Put the selected option of a list in first place of the associated table
+       local opt = select_conf[select_id].opt
+       local cfg = select_conf[select_id].cf
+       local ind = select_conf[select_id].ind
+       if openSub.option[opt] then
+               table.sort(cfg, function(a, b)
+                       if a[1] == openSub.option[opt] then
+                               return true
+                       elseif b[1] == openSub.option[opt] then
+                               return false
+                       else
+                               return a[ind] < b[ind]
+                       end
+               end)
        end
+end
+
+function display_select(select_id)
+-- Display the drop down values with an optionnal default value at the top
+       local conf = select_conf[select_id].cf
+       local opt = select_conf[select_id].opt
+       local option = openSub.option[opt]
+       local default = select_conf[select_id].dflt
+       local default_isset = false
 
-       if openSub.movie.seasonNumber ~= nil then
-               widget.get("season").input:set_text(openSub.movie.seasonNumber)
+       if not default then
+               default_isset = true
        end
 
-       if openSub.movie.episodeNumber ~= nil then
-               widget.get("episode").input:set_text(openSub.movie.episodeNumber)
+       for k, l in ipairs(conf) do
+               if default_isset then
+                       input_table[select_id]:add_value(l[2], k)
+               else
+                       if option then
+                               input_table[select_id]:add_value(l[2], k)
+                               input_table[select_id]:add_value(default, 0)
+                       else
+                               input_table[select_id]:add_value(default, 0)
+                               input_table[select_id]:add_value(l[2], k)
+                       end
+                       default_isset = true
+               end
        end
 end
 
+                                               --[[ Config & interface localization]]--
+
+function check_config()
+       -- Make a copy of english translation to use it as default
+       -- in case some element aren't translated in other translations
+       eng_translation = {}
+       for k, v in pairs(openSub.option.translation) do
+               eng_translation[k] = v
+       end
+
+       -- Get available translation full name from code
+       trsl_names = {}
+       for i, lg in ipairs(languages) do
+               trsl_names[lg[1]] = lg[2]
+       end
+
+       if is_window_path(vlc.config.datadir()) then
+               openSub.conf.os = "win"
+               slash = "\\"
+       else
+               openSub.conf.os = "lin"
+               slash = "/"
+       end
+
+       local path_generic = {"lua", "extensions", "userdata", "vlsub"}
+       local dirPath = slash..table.concat(path_generic, slash)
+       local filePath  = slash.."vlsub_conf.xml"
+       local config_saved = false
+       sub_dir = slash.."vlsub_subtitles"
+
+       -- Check if config file path is stored in vlc config
+       local other_dirs = {}
+
+       for path in vlc.config.get("sub-autodetect-path"):gmatch("[^,]+") do
+               if path:match(".*"..sub_dir.."$") then
+                       openSub.conf.dirPath = path:gsub("%s*(.*)"..sub_dir.."%s*$", "%1")
+                       config_saved = true
+               end
+               table.insert(other_dirs, path)
+       end
+
+       -- if not stored in vlc config
+       -- try to find a suitable config file path
+
+    if openSub.conf.dirPath then
+               if not is_dir(openSub.conf.dirPath) and
+               (openSub.conf.os == "lin"  or
+               is_win_safe(openSub.conf.dirPath)) then
+                       mkdir_p(openSub.conf.dirPath)
+               end
+    else
+               local userdatadir = vlc.config.userdatadir()
+               local datadir = vlc.config.datadir()
+
+               -- check if the config already exist
+               if file_exist(userdatadir..dirPath..filePath) then
+                       openSub.conf.dirPath = userdatadir..dirPath
+                       config_saved = true
+               elseif file_exist(datadir..dirPath..filePath) then
+                       openSub.conf.dirPath = datadir..dirPath
+                       config_saved = true
+               else
+                       local extension_path = slash..path_generic[1]
+                               ..slash..path_generic[2]
+
+                       -- use the same folder as the extension if accessible
+                       if is_dir(userdatadir..extension_path)
+                       and file_touch(userdatadir..dirPath..filePath) then
+                                       openSub.conf.dirPath = userdatadir..dirPath
+                       elseif file_touch(datadir..dirPath..filePath) then
+                               openSub.conf.dirPath = datadir..dirPath
+                       end
+
+                       -- try to create working dir in user folder
+                       if not openSub.conf.dirPath
+                       and is_dir(userdatadir) then
+                               if not is_dir(userdatadir..dirPath) then
+                                       mkdir_p(userdatadir..dirPath)
+                               end
+                               if is_dir(userdatadir..dirPath) and
+                               file_touch(userdatadir..dirPath..filePath) then
+                                       openSub.conf.dirPath = userdatadir..dirPath
+                               end
+                       end
+
+                       -- try to create working dir in vlc folder
+                       if not openSub.conf.dirPath and
+                       is_dir(datadir) then
+                               if not is_dir(datadir..dirPath) then
+                                       mkdir_p(datadir..dirPath)
+                               end
+                               if file_touch(datadir..dirPath..filePath) then
+                                       openSub.conf.dirPath = datadir..dirPath
+                               end
+                       end
+               end
+       end
+
+       if openSub.conf.dirPath then
+               vlc.msg.dbg("[VLSub] Working directory: " ..
+                       (openSub.conf.dirPath or "not found"))
+
+               openSub.conf.filePath = openSub.conf.dirPath..filePath
+               openSub.conf.localePath = openSub.conf.dirPath..slash.."locale"
+
+               if config_saved
+               and file_exist(openSub.conf.filePath) then
+                       vlc.msg.dbg("[VLSub] Loading config file: "..openSub.conf.filePath)
+                       load_config()
+               else
+                       vlc.msg.dbg("[VLSub] No config file")
+                       getenv_lang()
+                       config_saved = save_config()
+                       if not config_saved then
+                               vlc.msg.dbg("[VLSub] Unable to save config")
+                       end
+               end
+
+               -- Check presence of a translation file in "%vlsub_directory%/locale"
+               -- Add translation files to available translation list
+
+               local file_list = list_dir(openSub.conf.localePath)
+               local translations_avail = openSub.conf.translations_avail
+               if file_list then
+                       for i, file_name in ipairs(file_list) do
+                               local lg =  string.gsub(file_name, "^(%w%w%w).xml$", "%1")
+                               if lg and not translations_avail[lg] then
+                                       table.insert(translations_avail, {lg, trsl_names[lg]})
+                               end
+                       end
+               end
+
+               -- Load selected translation from file
+               if openSub.option.intLang ~= "eng"
+               and not openSub.conf.translated
+               then
+                       local transl_file_path = openSub.conf.localePath..slash..openSub.option.intLang..".xml"
+                       if file_exist(transl_file_path) then
+                               vlc.msg.dbg("[VLSub] Loadin translation from file: " .. transl_file_path)
+                               load_transl(transl_file_path)
+                       end
+               end
+       else
+               vlc.msg.dbg("[VLSub] Unable fount a suitable path to save config, please set it manually")
+       end
+
+       lang = nil
+       lang = options.translation -- just a shortcut
+
+       SetDownloadBehaviours()
+       if not openSub.conf.dirPath then
+               setError(lang["mess_err_conf_access"])
+       end
+
+       -- Set table list of available traduction from assoc. array
+       -- so it is sortable
+
+       for k, l in pairs(openSub.option.translations_avail) do
+               if k == openSub.option.int_research then
+                       table.insert(openSub.conf.translations_avail, 1, {k, l})
+               else
+                       table.insert(openSub.conf.translations_avail, {k, l})
+               end
+       end
+       collectgarbage()
+end
+
+function load_config()
+-- Overwrite default conf with loaded conf
+       local tmpFile = io.open(openSub.conf.filePath, "rb")
+       if not tmpFile then return false end
+       local resp = tmpFile:read("*all")
+       tmpFile:flush()
+       tmpFile:close()
+       local option = parse_xml(resp)
+
+       for key, value in pairs(option) do
+               if type(value) == "table" then
+                       if key == "translation" then
+                               openSub.conf.translated = true
+                               for k, v in pairs(value) do
+                                       openSub.option.translation[k] = v
+                               end
+                       else
+                               openSub.option[key] = value
+                       end
+               else
+                       if value == "true" then
+                               openSub.option[key] = true
+                       elseif value == "false" then
+                               openSub.option[key] = false
+                       else
+                               openSub.option[key] = value
+                       end
+               end
+       end
+       collectgarbage()
+end
+
+function load_transl(path)
+-- Overwrite default conf with loaded conf
+       local tmpFile = assert(io.open(path, "rb"))
+       local resp = tmpFile:read("*all")
+       tmpFile:flush()
+       tmpFile:close()
+       openSub.option.translation = nil
+
+       openSub.option.translation = parse_xml(resp)
+       collectgarbage()
+end
+
+function apply_translation()
+-- Overwrite default conf with loaded conf
+       for k, v in pairs(eng_translation) do
+               if not openSub.option.translation[k] then
+                       openSub.option.translation[k] = eng_translation[k]
+               end
+       end
+end
+
+function getenv_lang()
+-- Retrieve the user OS language
+       local os_lang = os.getenv("LANG")
+
+       if os_lang then -- unix, mac
+               os_lang = string.sub(os_lang, 0, 2)
+               if type(lang_os_to_iso[os_lang]) then
+                       openSub.option.language = lang_os_to_iso[os_lang]
+               end
+       else -- Windows
+               local lang_w = string.match(os.setlocale("", "collate"), "^[^_]+")
+               for i, v in ipairs(openSub.conf.languages) do
+                 if v[2] == lang_w then
+                       openSub.option.language = v[1]
+                 end
+               end
+       end
+end
+
+function apply_config()
+-- Apply user config selection to local config
+       local lg_sel = input_table['intLang']:get_value()
+       local sel_val
+       local opt
+
+       if lg_sel and lg_sel ~= 1
+       and openSub.conf.translations_avail[lg_sel] then
+               local lg = openSub.conf.translations_avail[lg_sel][1]
+               set_translation(lg)
+               SetDownloadBehaviours()
+       end
+
+       for select_id, v in pairs(select_conf) do
+               if input_table[select_id] and select_conf[select_id] then
+                       sel_val = input_table[select_id]:get_value()
+                       opt = select_conf[select_id].opt
+
+                       if sel_val == 0 then
+                               openSub.option[opt] = nil
+                       else
+                               openSub.option[opt] = select_conf[select_id].cf[sel_val][1]
+                       end
+
+                       set_default_option(select_id)
+               end
+       end
+
+
+       openSub.option.os_username = input_table['os_username']:get_text()
+       openSub.option.os_password = input_table['os_password']:get_text()
+
+       if input_table["langExt"]:get_value() == 2 then
+               openSub.option.langExt = not openSub.option.langExt
+       end
+
+       if input_table["removeTag"]:get_value() == 2 then
+               openSub.option.removeTag = not openSub.option.removeTag
+       end
+
+       -- Set a custom working directory
+       local dir_path = input_table['dir_path']:get_text()
+       local dir_path_err = false
+       if trim(dir_path) == "" then dir_path = nil end
+
+       if dir_path ~= openSub.conf.dirPath then
+               if openSub.conf.os == "lin"
+               or is_win_safe(dir_path)
+               or not dir_path then
+                       local other_dirs = {}
+
+                       for path in vlc.config.get("sub-autodetect-path"):gmatch("[^,]+") do
+                               path = trim(path)
+                               if path ~= (openSub.conf.dirPath or "")..sub_dir then
+                                       table.insert(other_dirs, path)
+                               end
+                       end
+                       openSub.conf.dirPath = dir_path
+                       if dir_path then
+                               table.insert(other_dirs,
+                               string.gsub(dir_path, "^(.-)[\\/]?$", "%1")..sub_dir)
+
+                               if not is_dir(dir_path) then
+                                       mkdir_p(dir_path)
+                               end
+
+                               openSub.conf.filePath = openSub.conf.dirPath..slash.."vlsub_conf.xml"
+                               openSub.conf.localePath = openSub.conf.dirPath..slash.."locale"
+                       else
+                               openSub.conf.filePath = nil
+                               openSub.conf.localePath = nil
+                       end
+                       vlc.config.set("sub-autodetect-path", table.concat(other_dirs, ", "))
+               else
+                       dir_path_err = true
+                       setError(lang["mess_err_wrong_path"].."<br><b>"..string.gsub(dir_path, "[^%:%w%p%s§¤]+", "<span style='color:#B23'>%1</span>").."</b>")
+               end
+       end
+
+       if openSub.conf.dirPath and
+       not dir_path_err then
+               local config_saved = save_config()
+               trigger_menu(1)
+               if not config_saved then
+                       setError(lang["mess_err_conf_access"])
+               end
+       else
+               setError(lang["mess_err_conf_access"])
+       end
+end
+
+function save_config()
+-- Dump local config into config file
+       if openSub.conf.dirPath
+       and openSub.conf.filePath then
+               vlc.msg.dbg("[VLSub] Saving config file:  " .. openSub.conf.filePath)
+
+               if file_touch(openSub.conf.filePath) then
+                       local tmpFile = assert(io.open(openSub.conf.filePath, "wb"))
+                       local resp = dump_xml(openSub.option)
+                       tmpFile:write(resp)
+                       tmpFile:flush()
+                       tmpFile:close()
+                       tmpFile = nil
+               else
+                       return false
+               end
+               collectgarbage()
+               return true
+       else
+               vlc.msg.dbg("[VLSub] Unable fount a suitable path to save config, please set it manually")
+               setError(lang["mess_err_conf_access"])
+               return false
+       end
+end
+
+function SetDownloadBehaviours()
+       openSub.conf.downloadBehaviours = nil
+       openSub.conf.downloadBehaviours = {
+               {'save', lang["int_dowload_save"]},
+               {'load', lang["int_dowload_load"]},
+               {'manual', lang["int_dowload_manual"]}
+       }
+end
+
+function get_available_translations()
+-- Get all available translation files from the internet
+-- (drop previous direct download from github repo because of problem with github https CA certficate on OS X an XP)
+-- https://github.com/exebetche/vlsub/tree/master/locale
+
+       local translations_url = "http://addons.videolan.org/CONTENT/content-files/148752-vlsub_translations.xml"
+
+       if input_table['intLangBut']:get_text() == lang["int_search_transl"] then
+               openSub.actionLabel = lang["int_searching_transl"]
+
+               local translations_content, lol = get(translations_url)
+
+               all_trsl = parse_xml(translations_content)
+               local lg, trsl
+
+               for lg, trsl in pairs(all_trsl) do
+                       if lg ~= options.intLang[1] and not openSub.option.translations_avail[lg] then
+                               openSub.option.translations_avail[lg] = trsl_names[lg] or ""
+                               table.insert(openSub.conf.translations_avail, {lg, trsl_names[lg]})
+                               input_table['intLang']:add_value(trsl_names[lg], #openSub.conf.translations_avail)
+                       end
+               end
+
+               setMessage(success_tag(lang["mess_complete"]))
+               collectgarbage()
+       end
+end
+
+function set_translation(lg)
+       openSub.option.translation = nil
+       openSub.option.translation = {}
+
+       if lg == 'eng' then
+               for k, v in pairs(eng_translation) do
+                       openSub.option.translation[k] = v
+               end
+       else
+               -- If translation file exists in /locale directory load it
+               if openSub.conf.localePath
+               and file_exist(openSub.conf.localePath..slash..lg..".xml") then
+                       local transl_file_path = openSub.conf.localePath..slash..lg..".xml"
+                       vlc.msg.dbg("[VLSub] Loading translation from file: " .. transl_file_path)
+                       load_transl(transl_file_path)
+                       apply_translation()
+               else
+               -- Load translation file from internet
+                       if not all_trsl then
+                               get_available_translations()
+                       end
+
+                       if not all_trsl or not all_trsl[lg] then
+                               vlc.msg.dbg("[VLSub] Error, translation not found")
+                               return false
+                       end
+                       openSub.option.translation = all_trsl[lg]
+                       apply_translation()
+                       all_trsl = nil
+               end
+       end
+
+       lang = nil
+       lang = openSub.option.translation
+       collectgarbage()
+end
+
+                                               --[[ Core ]]--
+
 openSub = {
        itemStore = nil,
        actionLabel = "",
        conf = {
                url = "http://api.opensubtitles.org/xml-rpc",
+               path = nil,
                userAgentHTTP = "VLSub",
                useragent = "VLSub 0.9",
-               username = "",
-               password = "",
-               language = "",
-               downloadSub = true,
-               removeTag = false,
-               justgetlink = false
+               translations_avail = {},
+               downloadBehaviours = nil,
+               languages = languages
        },
+       option = options,
        session = {
                loginTime = 0,
                token = ""
        },
        file = {
+               hasInput = false,
                uri = nil,
                ext = nil,
                name = nil,
                path = nil,
+               protocol = nil,
+               cleanName = nil,
                dir = nil,
                hash = nil,
                bytesize = nil,
@@ -134,22 +994,10 @@ openSub = {
                frames = nil
        },
        movie = {
-               name = "",
-               season = "",
-               episode = ""
-       },
-       sub = {
-               id = nil,
-               authorcomment = nil,
-               hash = nil,
-               idfile = nil,
-               filename = nil,
-               content = nil,
-               IDSubMovieFile = nil,
-               score = nil,
-               comment = nil,
-               bad = nil,
-               languageid = nil
+               title = "",
+               seasonNumber = "",
+               episodeNumber = "",
+               sublanguageid = ""
        },
        request = function(methodName)
                local params = openSub.methods[methodName].params()
@@ -172,12 +1020,15 @@ openSub = {
 
                if status == 200 then
                        response = parse_xmlrpc(responseStr)
-                       --~ vlc.msg.dbg(responseStr)
-                       if (response and response.status == "200 OK") then
-                               return openSub.methods[methodName].callback(response)
-                       elseif response then
-                               setError("code "..response.status.."("..status..")")
-                               return false
+                       if response then
+                               if response.status == "200 OK" then
+                                       return openSub.methods[methodName].callback(response)
+                               elseif response.status == "406 No session" then
+                                       openSub.request("LogIn")
+                               elseif response then
+                                       setError("code '"..response.status.."' ("..status..")")
+                                       return false
+                               end
                        else
                                setError("Server not responding")
                                return false
@@ -213,11 +1064,11 @@ openSub = {
        methods = {
                LogIn = {
                        params = function()
-                               openSub.actionLabel = "Logging in"
+                               openSub.actionLabel = lang["action_login"]
                                return {
-                                       { value={ string=openSub.conf.username } },
-                                       { value={ string=openSub.conf.password } },
-                                       { value={ string=openSub.conf.language } },
+                                       { value={ string=openSub.option.os_username } },
+                                       { value={ string=openSub.option.os_password } },
+                                       { value={ string=openSub.movie.sublanguageid } },
                                        { value={ string=openSub.conf.useragent } }
                                }
                        end,
@@ -229,7 +1080,7 @@ openSub = {
                },
                LogOut = {
                        params = function()
-                               openSub.actionLabel = "Logging out"
+                               openSub.actionLabel = lang["action_logout"]
                                return {
                                        { value={ string=openSub.session.token } }
                                }
@@ -240,18 +1091,19 @@ openSub = {
                },
                NoOperation = {
                        params = function()
+                               openSub.actionLabel = lang["action_noop"]
                                return {
                                        { value={ string=openSub.session.token } }
                                }
                        end,
-                       callback = function()
+                       callback = function(resp)
                                return true
                        end
                },
                SearchSubtitlesByHash = {
                        methodName = "SearchSubtitles",
                        params = function()
-                               openSub.actionLabel = "Searching subtitles"
+                               openSub.actionLabel = lang["action_search"]
                                setMessage(openSub.actionLabel..": "..progressBarContent(0))
 
                                return {
@@ -262,39 +1114,39 @@ openSub = {
                                                        value={
                                                          struct={
                                                                member={
-                                                                 { name="sublanguageid", value={ string=openSub.sub.languageid } },
-                                                                 { name="moviehash", value={ string=openSub.file.hash } },
-                                                                 { name="moviebytesize", value={ double=openSub.file.bytesize } } }}}}}}}
+                                                                 { name="sublanguageid", value={
+                                                                       string=openSub.movie.sublanguageid } },
+                                                                 { name="moviehash", value={
+                                                                       string=openSub.file.hash } },
+                                                                 { name="moviebytesize", value={
+                                                                       double=openSub.file.bytesize } } }}}}}}}
                                }
                        end,
                        callback = function(resp)
                                openSub.itemStore = resp.data
-
-                               if openSub.itemStore ~= "0" then
-                                       return true
-                               else
-                                       openSub.itemStore = nil
-                                       return false
-                               end
                        end
                },
                SearchSubtitles = {
                        methodName = "SearchSubtitles",
                        params = function()
-                               openSub.actionLabel = "Searching subtitles"
+                               openSub.actionLabel = lang["action_search"]
                                setMessage(openSub.actionLabel..": "..progressBarContent(0))
 
                                local member = {
-                                                 { name="sublanguageid", value={ string=openSub.sub.languageid } },
-                                                 { name="query", value={ string=openSub.movie.name } } }
+                                                 { name="sublanguageid", value={
+                                                       string=openSub.movie.sublanguageid } },
+                                                 { name="query", value={
+                                                       string=openSub.movie.title } } }
 
 
-                               if openSub.movie.season ~= nil then
-                                       table.insert(member, { name="season", value={ string=openSub.movie.season } })
+                               if openSub.movie.seasonNumber ~= nil then
+                                       table.insert(member, { name="season", value={
+                                               string=openSub.movie.seasonNumber } })
                                end
 
-                               if openSub.movie.episode ~= nil then
-                                       table.insert(member, { name="episode", value={ string=openSub.movie.episode } })
+                               if openSub.movie.episodeNumber ~= nil then
+                                       table.insert(member, { name="episode", value={
+                                               string=openSub.movie.episodeNumber } })
                                end
 
                                return {
@@ -310,13 +1162,6 @@ openSub = {
                        end,
                        callback = function(resp)
                                openSub.itemStore = resp.data
-
-                               if openSub.itemStore ~= "0" then
-                                       return true
-                               else
-                                       openSub.itemStore = nil
-                                       return false
-                               end
                        end
                }
        },
@@ -324,36 +1169,65 @@ openSub = {
                return vlc.item or vlc.input.item()
        end,
        getFileInfo = function()
+       -- Get video file path, name, extension from input uri
                local item = openSub.getInputItem()
                local file = openSub.file
                if not item then
                        file.hasInput = false;
-                       file.cleanName = "";
-                       return false
+                       file.cleanName = nil;
+                       file.protocol = nil;
+                       file.path = nil;
+                       file.ext = nil;
+                       file.uri = nil;
                else
+                       vlc.msg.dbg("[VLSub] Video URI: "..item:uri())
                        local parsed_uri = vlc.net.url_parse(item:uri())
                        file.uri = item:uri()
                        file.protocol = parsed_uri["protocol"]
-                       file.path = vlc.strings.decode_uri(parsed_uri["path"])
-                       --correction needed for windows
-                       local windowPath = string.match(file.path, "^/(%a:/.+)$")
-                       if windowPath then
-                               file.path = windowPath
+                       file.path = parsed_uri["path"]
+
+               -- Corrections
+
+                       -- For windows
+                       file.path = string.match(file.path, "^/(%a:/.+)$") or file.path
+
+                       -- For file in archive
+                       local archive_path, name_in_archive = string.match(file.path, '^([^!]+)!/([^!/]*)$')
+                       if archive_path and archive_path ~= "" then
+                               file.path = string.gsub(archive_path, '\063', '%%')
+                               file.path = vlc.strings.decode_uri(file.path)
+                               file.completeName = string.gsub(name_in_archive, '\063', '%%')
+                               file.completeName = vlc.strings.decode_uri(file.completeName)
+                               file.is_archive = true
+                       else -- "classic" input
+                               file.path = vlc.strings.decode_uri(file.path)
+                               file.dir, file.completeName = string.match(file.path, '^(.+/)([^/]*)$')
+
+                               local file_stat = vlc.net.stat(file.path)
+                               if file_stat
+                               then
+                                       file.stat = file_stat
+                               end
+
+                               file.is_archive = false
                        end
-                       file.dir, file.completeName = string.match(file.path, "^([^\n]-/?)([^/]+)$")
-                       file.name, file.ext = string.match(file.path, "([^/]-)%.?([^%.]*)$")
+
+                       file.name, file.ext = string.match(file.completeName, '^([^/]-)%.?([^%.]*)$')
 
                        if file.ext == "part" then
-                               file.name, file.ext = string.match(file.name, "^([^/]+)%.([^%.]+)$")
+                               file.name, file.ext = string.match(file.name, '^([^/]+)%.([^%.]+)$')
                        end
+
                        file.hasInput = true;
                        file.cleanName = string.gsub(file.name, "[%._]", " ")
-                       vlc.msg.dbg(file.cleanName)
+                       vlc.msg.dbg("[VLSub] file info "..(dump_xml(file)))
                end
+               collectgarbage()
        end,
        getMovieInfo = function()
+       -- Clean video file name and check for season/episode pattern in title
                if not openSub.file.name then
-                       openSub.movie.name = ""
+                       openSub.movie.title = ""
                        openSub.movie.seasonNumber = ""
                        openSub.movie.episodeNumber = ""
                        return false
@@ -366,538 +1240,390 @@ openSub = {
                end
 
                if showName then
-                       openSub.movie.name = showName
+                       openSub.movie.title = showName
                        openSub.movie.seasonNumber = seasonNumber
                        openSub.movie.episodeNumber = episodeNumber
                else
-                       openSub.movie.name = openSub.file.cleanName
+                       openSub.movie.title = openSub.file.cleanName
                        openSub.movie.seasonNumber = ""
                        openSub.movie.episodeNumber = ""
-
-                       vlc.msg.dbg(openSub.movie.name)
                end
+               collectgarbage()
        end,
        getMovieHash = function()
-               openSub.actionLabel = "Calculating movie hash"
+       -- Calculate movie hash
+               openSub.actionLabel = lang["action_hash"]
                setMessage(openSub.actionLabel..": "..progressBarContent(0))
 
                local item = openSub.getInputItem()
 
                if not item then
-                       setError("Please use this method during playing")
+                       setError(lang["mess_no_input"])
                        return false
                end
 
                openSub.getFileInfo()
-               if openSub.file.protocol ~= "file" then
-                       setError("This method works with local file only (for now)")
-                       return false
-               end
 
-               local path = openSub.file.path
-               if not path then
-                       setError("File not found")
+               if not openSub.file.path then
+                       setError(lang["mess_not_found"])
                        return false
                end
 
-               local file = assert(io.open(path, "rb"))
-               if not file then
-                       setError("File not found")
-                       return false
+               local data_start = ""
+               local data_end = ""
+        local size
+        local chunk_size = 65536
+
+               -- Get data for hash calculation
+               if openSub.file.is_archive then
+                       vlc.msg.dbg("[VLSub] Read hash data from stream")
+
+                       local file = vlc.stream(openSub.file.uri)
+                       local dataTmp1 = ""
+                       local dataTmp2 = ""
+                       size = chunk_size
+
+                       data_start = file:read(chunk_size)
+
+                       while data_end do
+                               size = size + string.len(data_end)
+                               dataTmp1 = dataTmp2
+                               dataTmp2 = data_end
+                               data_end = file:read(chunk_size)
+                               collectgarbage()
+                       end
+                       data_end = string.sub((dataTmp1..dataTmp2), -chunk_size)
+               elseif not file_exist(openSub.file.path)
+               and openSub.file.stat then
+                       vlc.msg.dbg("[VLSub] Read hash data from stream")
+
+                       local file = vlc.stream(openSub.file.uri)
+
+                       if not file then
+                               vlc.msg.dbg("[VLSub] No stream")
+                               return false
+                       end
+
+                       size = openSub.file.stat.size
+                       local decal = size%chunk_size
+
+                       data_start = file:read(chunk_size)
+
+                       -- "Seek" to the end
+                       file:read(decal)
+
+                       for i = 1, math.floor(((size-decal)/chunk_size))-2 do
+                               file:read(chunk_size)
+                       end
+
+                       data_end = file:read(chunk_size)
+
+                       file = nil
+               else
+                       vlc.msg.dbg("[VLSub] Read hash data from file")
+                       local file = io.open( openSub.file.path, "rb")
+                       if not file then
+                               vlc.msg.dbg("[VLSub] No stream")
+                               return false
+                       end
+
+                       data_start = file:read(chunk_size)
+                       size = file:seek("end", -chunk_size) + chunk_size
+                       data_end = file:read(chunk_size)
+                       file = nil
                end
 
-        local lo,hi=0,0
-        for i=1,8192 do
-                local a,b,c,d = file:read(4):byte(1,4)
-                lo = lo + a + b*256 + c*65536 + d*16777216
-                a,b,c,d = file:read(4):byte(1,4)
-                hi = hi + a + b*256 + c*65536 + d*16777216
-                while lo>=4294967296 do
-                        lo = lo-4294967296
-                        hi = hi+1
-                end
-                while hi>=4294967296 do
-                        hi = hi-4294967296
-                end
-        end
-        local size = file:seek("end", -65536) + 65536
-        for i=1,8192 do
-                local a,b,c,d = file:read(4):byte(1,4)
-                lo = lo + a + b*256 + c*65536 + d*16777216
-                a,b,c,d = file:read(4):byte(1,4)
-                hi = hi + a + b*256 + c*65536 + d*16777216
-                while lo>=4294967296 do
-                        lo = lo-4294967296
-                        hi = hi+1
-                end
-                while hi>=4294967296 do
-                        hi = hi-4294967296
-                end
+       -- Hash calculation
+        local lo = size
+        local hi = 0
+        local o,a,b,c,d,e,f,g,h
+        local hash_data = data_start..data_end
+        local max_size = 4294967296
+        local overflow
+
+               for i = 1,  #hash_data, 8 do
+                       a,b,c,d,e,f,g,h = hash_data:byte(i,i+7)
+                       lo = lo + a + b*256 + c*65536 + d*16777216
+                       hi = hi + e + f*256 + g*65536 + h*16777216
+
+                       if lo > max_size then
+                               overflow = math.floor(lo/max_size)
+                               lo = lo-(overflow*max_size)
+                               hi = hi+overflow
+                       end
+
+                       if hi > max_size then
+                               overflow = math.floor(hi/max_size)
+                               hi = hi-(overflow*max_size)
+                       end
         end
-        lo = lo + size
-                while lo>=4294967296 do
-                        lo = lo-4294967296
-                        hi = hi+1
-                end
-                while hi>=4294967296 do
-                        hi = hi-4294967296
-                end
 
                openSub.file.bytesize = size
                openSub.file.hash = string.format("%08x%08x", hi,lo)
-
+               vlc.msg.dbg("[VLSub] Video hash: "..openSub.file.hash)
+               vlc.msg.dbg("[VLSub] Video bytesize: "..size)
+               collectgarbage()
                return true
        end,
-    loadSubtitles = function(url, SubFileName, target)
-        openSub.actionLabel = "Downloading subtitle"
-        setMessage(openSub.actionLabel..": "..progressBarContent(0))
-        local subfileURI = nil
-        local resp = get(url)
-        if resp then
-            local tmpFileName = openSub.file.dir..SubFileName..".zip"
-            subfileURI = "zip://"..make_uri(tmpFileName, true).."!/"..SubFileName
-            local tmpFile = assert(io.open(tmpFileName, "wb"))
-            tmpFile:write(resp)
-            tmpFile:flush()
-            tmpFile:close()
-
-            if target then
-                local stream = vlc.stream(subfileURI)
-                local data = ""
-                local subfile = assert(io.open(target, "w")) -- FIXME: check for file presence before overwrite (maybe ask what to do)
-
-                while data do
-                    if openSub.conf.removeTag then
-                        subfile:write(remove_tag(data).."\n")
-                    else
-                        subfile:write(data.."\n")
-                    end
-                    data = stream:readline()
-                end
-
-                subfile:flush()
-                subfile:close()
-                stream = nil
-                collectgarbage()
-                               subfileURI = make_uri(target, true)
-            end
-                       os.remove(tmpFileName)
-        end
+       checkSession = function()
 
-        if vlc.item or vlc.input.item() then
-                       vlc.msg.dbg("Adding subtitle :" .. subfileURI)
-            vlc.input.add_subtitle(subfileURI)
-            setMessage("Success: Subtitles loaded.")
-        else
-            setError("No current input, unable to add subtitles "..target)
-        end
-    end
-}
-
-function make_uri(str, encode)
-    local windowdrive = string.match(str, "^(%a:/).+$")
-       if encode then
-               local encodedPath = ""
-               for w in string.gmatch(str, "/([^/]+)") do
-                       vlc.msg.dbg(w)
-                       encodedPath = encodedPath.."/"..vlc.strings.encode_uri_component(w)
+               if openSub.session.token == "" then
+                       openSub.request("LogIn")
+               else
+                       openSub.request("NoOperation")
                end
-               str = encodedPath
-       end
-    if windowdrive then
-        return "file:///"..windowdrive..str
-    else
-        return "file://"..str
-    end
-end
-
-function download_selection()
-       local selection = widget.getVal("mainlist")
-       if #selection > 0 and openSub.itemStore then
-               download_subtitles(selection)
        end
-end
+}
 
 function searchHash()
-       openSub.sub.languageid = languages[widget.getVal("language")][2]
-
-       message = widget.get("message")
-       if message.display == "none" then
-               message.display = "block"
-               widget.set_interface(interface)
+       local sel = input_table["language"]:get_value()
+       if sel == 0 then
+               openSub.movie.sublanguageid = 'all'
+       else
+               openSub.movie.sublanguageid = openSub.conf.languages[sel][1]
        end
 
        openSub.getMovieHash()
 
        if openSub.file.hash then
+               openSub.checkSession()
                openSub.request("SearchSubtitlesByHash")
                display_subtitles()
        end
 end
 
 function searchIMBD()
-       openSub.movie.name = trim(widget.getVal("title"))
-       openSub.movie.season = tonumber(widget.getVal("season"))
-       openSub.movie.episode = tonumber(widget.getVal("episode"))
-       openSub.sub.languageid  = languages[widget.getVal("language")][2]
-
-       message = widget.get("message")
-       if message.display == "none" then
-               message.display = "block"
-               widget.set_interface(interface)
+       openSub.movie.title = trim(input_table["title"]:get_text())
+       openSub.movie.seasonNumber = tonumber(input_table["seasonNumber"]:get_text())
+       openSub.movie.episodeNumber = tonumber(input_table["episodeNumber"]:get_text())
+
+       local sel = input_table["language"]:get_value()
+       if sel == 0 then
+               openSub.movie.sublanguageid = 'all'
+       else
+               openSub.movie.sublanguageid = openSub.conf.languages[sel][1]
        end
 
-       if openSub.file.name ~= "" then
+       if openSub.movie.title ~= "" then
+               openSub.checkSession()
                openSub.request("SearchSubtitles")
                display_subtitles()
        end
 end
 
 function display_subtitles()
-       local list = "mainlist"
-       widget.setVal(list)     --~ Reset list
-       if openSub.itemStore then
+       local mainlist = input_table["mainlist"]
+       mainlist:clear()
+
+       if openSub.itemStore == "0" then
+               mainlist:add_value(lang["mess_no_res"], 1)
+               setMessage("<b>"..lang["mess_complete"]..":</b> "..lang["mess_no_res"])
+       elseif openSub.itemStore then
                for i, item in ipairs(openSub.itemStore) do
-                       widget.setVal(list, item.SubFileName.." ["..item.SubLanguageID.."] ("..item.SubSumCD.." CD)")
+                       mainlist:add_value(
+                       item.SubFileName..
+                       " ["..item.SubLanguageID.."]"..
+                       " ("..item.SubSumCD.." CD)", i)
                end
-       else
-               widget.setVal(list, "No result")
+               setMessage("<b>"..lang["mess_complete"]..":</b> "..#(openSub.itemStore).."  "..lang["mess_res"])
        end
 end
 
-function download_subtitles(selection)
-       local list = "mainlist"
-       widget.resetSel(list) -- reset selection
-       local index = selection[1][1]
-       local item = openSub.itemStore[index]
-       local subfileTarget = ""
-
-       if openSub.conf.justgetlink
-       or not (vlc.item or vlc.input.item())
-       or not openSub.file.dir
-       or not openSub.file.name then
-               setMessage("Link : <a href='"..item.ZipDownloadLink.."'>"..item.ZipDownloadLink.."</a>")
-       else
-               subfileTarget = openSub.file.dir..openSub.file.name.."."..item.SubLanguageID.."."..item.SubFormat
-               vlc.msg.dbg("subfileTarget: "..subfileTarget)
-               openSub.loadSubtitles(item.ZipDownloadLink, item.SubFileName, subfileTarget)
+function get_first_sel(list)
+       local selection = list:get_selection()
+       for index, name in pairs(selection) do
+               return index
        end
+       return 0
 end
 
-widget = {
-       stack = {},
-    meta = {},
-       registered_table = {},
-       main_table = {},
-       set_node = function(node, parent)
-               local left = parent.left
-               for k, l in pairs(node) do --parse items
-                       local tmpTop = parent.height
-                       local tmpLeft = left
-                       local ltmpLeft = l.left
-                       local ltmpTop = l.top
-                       local tmphidden = l.hidden
-
-                       l.top = parent.height + parent.top
-                       l.left = left
-                       l.parent = parent
-
-                       if l.display == "none" or parent.hidden then
-                               l.hidden = true
-                       else
-                               l.hidden = false
-                       end
+function download_subtitles()
+       local index = get_first_sel(input_table["mainlist"])
 
-                       if l.type == "div" then --that's a container
-                               l.display = (l.display or "block")
-                               l.height = 1
-                               for _, newNode in ipairs(l.content) do --parse lines
-                                       widget.set_node(newNode, l)
-                                       l.height = l.height+1
-                               end
-                               l.height = l.height - 1
-                               left = left - 1
-                       else --that's an item
-                               l.display = (l.display or "inline")
+       if index == 0 then
+               setMessage(lang["mess_no_selection"])
+               return false
+       end
 
-                               if not l.input then
-                                       tmphidden = true
-                               end
+       openSub.actionLabel = lang["mess_downloading"]
 
-                               if tmphidden and not l.hidden then --~ create
-                                       widget.create(l)
-                               elseif not tmphidden and l.hidden then --~ destroy
-                                       widget.destroy(l)
-                               end
+       display_subtitles() -- reset selection
 
-                               if not l.hidden and (ltmpTop ~= l.top or ltmpLeft ~= l.left) then
-                                       if l.input then --~ destroy
-                                               widget.destroy(l)
-                                       end
-                                       --~ recreate
-                                       widget.create(l)
-                               end
-                       end
+       local item = openSub.itemStore[index]
 
-                       --~  Store reference ID
-                       if l.id and not widget.registered_table[l.id] then
-                               widget.registered_table[l.id] = l
-                       end
+       if openSub.option.downloadBehaviour == 'manual' then
 
-                       if l.display == "block" then
-                               parent.height = parent.height + (l.height or 1)
-                               left = parent.left
-                       elseif l.display == "none" then
-                               parent.height = (tmpTop or parent.height)
-                               left = (tmpLeft or left)
-                       elseif l.display == "inline" then
-                               left = left + (l.width or 1)
-                       end
-               end
-       end,
-       set_interface = function(intf_map)
-               local root = {left = 1, top = 0, height = 0, hidden = false}
-               widget.set_node(intf_map, root)
-               widget.force_refresh()
-       end,
-       force_refresh = function() --~ Hacky
-               if refresh_toggle then
-                       refresh_toggle = false
-                       dlg:set_title(openSub.conf.useragent)
-               else
-                       refresh_toggle = true
-                       dlg:set_title(openSub.conf.useragent.." ")
-               end
-       end,
-       destroy = function(w)
-               dlg:del_widget(w.input)
-               if widget.registered_table[w.id] then
-                       widget.registered_table[w.id] = nil
-               end
-       end,
-       create = function(w)
-               local cur_widget
-               if w.type == "button" then
-                       cur_widget = dlg:add_button(w.value or "", w.callback, w.left, w.top, w.width or 1, w.height or 1)
-               elseif w.type == "label" then
-                       cur_widget = dlg:add_label(w.value or "", w.left, w.top, w.width or 1, w.height or 1)
-               elseif w.type == "html" then
-                       cur_widget = dlg:add_html(w.value or "", w.left, w.top, w.width or 1, w.height or 1)
-               elseif w.type == "text_input" then
-                       cur_widget = dlg:add_text_input(w.value or "", w.left, w.top, w.width or 1, w.height or 1)
-               elseif w.type == "password" then
-                       cur_widget = dlg:add_password(w.value or "", w.left, w.top, w.width or 1, w.height or 1)
-               elseif w.type == "check_box" then
-                       cur_widget = dlg:add_check_box(w.value or "", w.left, w.top, w.width or 1, w.height or 1)
-               elseif w.type == "dropdown" then
-                       cur_widget = dlg:add_dropdown(w.left, w.top, w.width or 1, w.height or 1)
-               elseif w.type == "list" then
-                       cur_widget = dlg:add_list(w.left, w.top, w.width or 1, w.height or 1)
-               elseif w.type == "image" then
+               local link = "<span style='color:#181'>"
+               link = link.."<b>"..lang["mess_dowload_link"]..":</b>"
+               link = link.."</span> &nbsp;"
+               link = link.."</span> &nbsp;<a href='"..item.ZipDownloadLink.."'>"
+               link = link..item.MovieReleaseName.."</a>"
 
+               setMessage(link)
+               return false
+       elseif openSub.option.downloadBehaviour == 'load' then
+               if add_sub("zip://"..item.ZipDownloadLink.."!/"..item.SubFileName) then
+                       setMessage(success_tag(lang["mess_loaded"]))
                end
+               return false
+       end
 
-               if w.type == "dropdown" or w.type == "list" then
-                       if type(w.value) == "table" then
-                               for k, l in ipairs(w.value) do
-                                       if type(l) == "table" then
-                                               cur_widget:add_value(l[1], k)
-                                       else
-                                               cur_widget:add_value(l, k)
-                                       end
-                               end
-                       end
-               end
+       local message = ""
+       local subfileName = openSub.file.name
 
-               if w.type and w.type ~= "div" then
-                       w.input = cur_widget
-               end
-       end,
-       get = function(h)
-                return widget.registered_table[h]
-       end,
-       setVal = function(h, val, index)
-               widget.set_val(widget.registered_table[h], val, index)
-       end,
-       set_val = function(w, val, index)
-               local input = w.input
-               local t = w.type
-               if t == "button" or
-               t == "label" or
-               t == "html" or
-               t == "text_input" or
-               t == "password" then
-                       if type(val) == "string" then
-                               input:set_text(val)
-                               w.value = val
-                       end
-               elseif t == "check_box" then
-                       if type(val) == "bool" then
-                               input:set_checked(val)
-                       else
-                               input:set_text(val)
-                       end
-               elseif t == "dropdown" or t == "list" then
-                       if val and index then
-                               input:add_value(val, index)
-                               w.value[index] = val
-                       elseif val and not index then
-                               if type(val) == "table" then
-                                       for k, l in ipairs(val) do
-                                               input:add_value(l, k)
-                                               table.insert(w.value, l)
-                                       end
-                               else
-                                       input:add_value(val, #w.value+1)
-                                       table.insert(w.value, val)
-                               end
-                       elseif not val and not index then
-                               input:clear()
-                               w.value = nil
-                               w.value = {}
-                       end
-               end
-       end,
-       getVal = function(h, typeval)
-               if not widget.registered_table[h] then print(h) return false end
-               return widget.get_val(widget.registered_table[h], typeval)
-       end,
-       get_val = function(w, typeval)
-               local input = w.input
-               local t = w.type
-
-               if t == "button" or
-                  t == "label" or
-                  t == "html" or
-                  t == "text_input" or
-                  t == "password" then
-                       return input:get_text()
-               elseif t == "check_box" then
-                       if typeval == "checked" then
-                               return input:get_checked()
-                       else
-                               return input:get_text()
-                       end
-               elseif t == "dropdown" then
-                       return input:get_value()
-               elseif t == "list" then
-                       local selection = input:get_selection()
-                       local output = {}
-
-                       for index, name in  pairs(selection)do
-                               table.insert(output, {index, name})
-                       end
-                       return output
-               end
-       end,
-       resetSel = function(h, typeval)
-               local w = widget.registered_table[h]
-               local val = w.value
-               widget.set_val(w)
-               widget.set_val(w, val)
+       if openSub.option.langExt then
+               subfileName = subfileName.."."..item.SubLanguageID
        end
-}
 
-function create_dialog()
-       dlg = vlc.dialog(openSub.conf.useragent)
-       widget.set_interface(interface)
-end
+       subfileName = subfileName.."."..item.SubFormat
+       local tmp_dir
+       local file_target_access = true
 
+       if is_dir(openSub.file.dir) then
+               tmp_dir = openSub.file.dir
+       elseif openSub.conf.dirPath then
+               tmp_dir = openSub.conf.dirPath
 
-function toggle_help()
-       helpMessage = widget.get("helpMessage")
-       if helpMessage.display == "block" then
-               helpMessage.display = "none"
-       elseif helpMessage.display == "none" then
-               helpMessage.display = "block"
+               message = "<br> "..error_tag(lang["mess_save_fail"].." &nbsp;"..
+               "<a href='"..vlc.strings.make_uri(openSub.conf.dirPath).."'>"..
+               lang["mess_click_link"].."</a>")
+       else
+               setError(lang["mess_save_fail"].." &nbsp;"..
+               "<a href='"..item.ZipDownloadLink.."'>"..
+               lang["mess_click_link"].."</a>")
+               return false
        end
 
-       widget.set_interface(interface)
-end
+       local tmpFileURI, tmpFileName = dump_zip(
+               item.ZipDownloadLink,
+               tmp_dir,
+               item.SubFileName)
+
+       vlc.msg.dbg("[VLsub] tmpFileName: "..tmpFileName)
 
+       -- Determine if the path to the video file is accessible for writing
 
-function set_interface()  --~ old
-       local method_index = widget.getVal("method")
-       local method_id = methods[method_index][2]
-       if tmp_method_id then
-               if tmp_method_id == method_id then
+       local target = openSub.file.dir..subfileName
+
+       if not file_touch(target) then
+               if openSub.conf.dirPath then
+                       target =  openSub.conf.dirPath..slash..subfileName
+                       message = "<br> "..error_tag(lang["mess_save_fail"].." &nbsp;"..
+                       "<a href='"..vlc.strings.make_uri(openSub.conf.dirPath).."'>"..
+                       lang["mess_click_link"].."</a>")
+               else
+                       setError(lang["mess_save_fail"].." &nbsp;"..
+                       "<a href='"..item.ZipDownloadLink.."'>"..
+                       lang["mess_click_link"].."</a>")
                        return false
                end
-               widget.get(tmp_method_id).display = "none"
-       else
-               openSub.request("LogIn")
        end
-       tmp_method_id = method_id
-       widget.get(method_id).display = "block"
-       widget.set_interface(interface)
-       setMessage("")
-
-       if method_id == "hash" then
-               searchHash()
-       elseif method_id == "imdb" then
-               if openSub.file.name and not hasAssociatedResult() then
-                       associatedResult()
-                       widget.get("title").input:set_text(openSub.movie.name)
-                       widget.get("season").input:set_text(openSub.movie.seasonNumber)
-                       widget.get("episode").input:set_text(openSub.movie.episodeNumber)
-               end
+
+       vlc.msg.dbg("[VLsub] Subtitles files: "..target)
+
+       -- Unzipped data into file target
+
+       local stream = vlc.stream(tmpFileURI)
+       local data = ""
+       local subfile = io.open(target, "wb")
+
+       while data do
+               subfile:write(data)
+               data = stream:read(65536)
        end
-end
 
-function progressBarContent(pct)
-       local content = "<span style='background-color:#181;color:#181;'>"
-       local accomplished = math.ceil(progressBarSize*pct/100)
-
-       local left = progressBarSize - accomplished
-       content = content .. string.rep ("-", accomplished)
-       content = content .. "</span><span style='background-color:#fff;color:#fff;'>"
-       content = content .. string.rep ("-", left)
-       content = content .. "</span>"
-       return content
-end
+       subfile:flush()
+       subfile:close()
 
-function setError(str)
-       setMessage("<span style='color:#B23'>Error: "..str.."</span>")
-end
+       stream = nil
+       collectgarbage()
 
-function setMessage(str)
-       if widget.get("message") then
-               widget.setVal("message", str)
-               dlg:update()
+       if not os.remove(tmpFileName) then
+               vlc.msg.err("[VLsub] Unable to remove temp: "..tmpFileName)
        end
+
+       subfileURI = vlc.strings.make_uri(target)
+
+       if not subfileURI then
+               subfileURI = make_uri(target, true)
+       end
+
+       -- load subtitles
+       if add_sub(subfileURI) then
+               message = success_tag(lang["mess_loaded"]) .. message
+       end
+
+
+       setMessage(message)
 end
 
---~ Misc utils
+function dump_zip(url, dir, subfileName)
+       -- Dump zipped data in a temporary file
+       setMessage(openSub.actionLabel..": "..progressBarContent(0))
+       local resp = get(url)
 
-function file_exists(name)
-       local f=io.open(name ,"r")
-       if f~=nil then
-               io.close(f)
-               vlc.msg.dbg("File found!" .. name)
-               return true
-       else
-               vlc.msg.dbg("File not found. "..name)
+       if not resp then
+               setError(lang["mess_no_response"])
+               return false
+       end
+
+       local tmpFileName = dir..slash..subfileName..".gz"
+       if not file_touch(tmpFileName) then
                return false
        end
+       local tmpFile = assert(io.open(tmpFileName, "wb"))
+
+       tmpFile:write(resp)
+       tmpFile:flush()
+       tmpFile:close()
+       tmpFile = nil
+       collectgarbage()
+       return "zip://"..make_uri(tmpFileName, true).."!/"..subfileName, tmpFileName
 end
 
-function wait(seconds)
-       local _start = os.time()
-       local _end = _start+seconds
-       while (_end ~= os.time()) do
+function add_sub(subfileURI)
+       if vlc.item or vlc.input.item() then
+               vlc.msg.dbg("[VLsub] Adding subtitle :" .. subfileURI)
+               return vlc.input.add_subtitle(subfileURI)
        end
+       return false
 end
 
-function trim(str)
-    if not str then return "" end
-    return string.gsub(str, "^%s*(.-)%s*$", "%1")
+                                               --[[ Interface helpers]]--
+
+function progressBarContent(pct)
+       local accomplished = math.ceil(openSub.option.progressBarSize*pct/100)
+       local left = openSub.option.progressBarSize - accomplished
+       local content = "<span style='background-color:#181;color:#181;'>"..
+               string.rep ("-", accomplished).."</span>"..
+               "<span style='background-color:#fff;color:#fff;'>"..
+               string.rep ("-", left)..
+               "</span>"
+       return content
 end
 
-function remove_tag(str)
-       return string.gsub(str, "{[^}]+}", "")
+function setMessage(str)
+       if input_table["message"] then
+               input_table["message"]:set_text(str)
+               dlg:update()
+       end
 end
 
---~ Network utils
+function setError(mess)
+       setMessage(error_tag(mess))
+end
+
+function success_tag(str)
+       return "<span style='color:#181'><b>"..
+       lang["mess_success"]..":</b></span> "..str..""
+end
+
+function error_tag(str)
+       return "<span style='color:#B23'><b>"..
+       lang["mess_error"]..":</b></span> "..str..""
+end
+
+                                               --[[ Network utils]]--
 
 function get(url)
        local host, path = parse_url(url)
@@ -916,49 +1642,47 @@ function get(url)
        if status == 200 then
                return response
        else
-               return false
+               return false, status, response
        end
 end
 
 function http_req(host, port, request)
        local fd = vlc.net.connect_tcp(host, port)
-       if fd >= 0 then
-               local pollfds = {}
+       if not fd then return false end
+       local pollfds = {}
 
-               pollfds[fd] = vlc.net.POLLIN
-               vlc.net.send(fd, request)
-               vlc.net.poll(pollfds)
+       pollfds[fd] = vlc.net.POLLIN
+       vlc.net.send(fd, request)
+       vlc.net.poll(pollfds)
 
-               local response = vlc.net.recv(fd, 1024)
-               local headerStr, body = string.match(response, "(.-\r?\n)\r?\n(.*)")
-               local header = parse_header(headerStr)
-               local contentLength = tonumber(header["Content-Length"])
-               local TransferEncoding = header["Transfer-Encoding"]
-               local status = tonumber(header["statuscode"])
-               local bodyLenght = string.len(body)
-               local pct = 0
+       local response = vlc.net.recv(fd, 1024)
+       local headerStr, body = string.match(response, "(.-\r?\n)\r?\n(.*)")
+       local header = parse_header(headerStr)
+       local contentLength = tonumber(header["Content-Length"])
+       local TransferEncoding = header["Transfer-Encoding"]
+       local status = tonumber(header["statuscode"])
+       local bodyLenght = string.len(body)
+       local pct = 0
 
-               if status ~= 200 then return status end
+       --~ if status ~= 200 then return status end
 
-               while contentLength and bodyLenght < contentLength do
-                       vlc.net.poll(pollfds)
-                       response = vlc.net.recv(fd, 1024)
+       while contentLength and bodyLenght < contentLength do
+               vlc.net.poll(pollfds)
+               response = vlc.net.recv(fd, 1024)
 
-                       if response then
-                               body = body..response
-                       else
-                               vlc.net.close(fd)
-                               return false
-                       end
-                       bodyLenght = string.len(body)
-                       pct = bodyLenght / contentLength * 100
-                       setMessage(openSub.actionLabel..": "..progressBarContent(pct))
+               if response then
+                       body = body..response
+               else
+                       vlc.net.close(fd)
+                       return false
                end
-               vlc.net.close(fd)
-
-               return status, body
+               bodyLenght = string.len(body)
+               pct = bodyLenght / contentLength * 100
+               setMessage(openSub.actionLabel..": "..progressBarContent(pct))
        end
-       return ""
+       vlc.net.close(fd)
+
+       return status, body
 end
 
 function parse_header(data)
@@ -976,19 +1700,22 @@ function parse_url(url)
        return  url_parsed["host"], url_parsed["path"], url_parsed["option"]
 end
 
---~ XML utils
+                                               --[[ XML utils]]--
 
 function parse_xml(data)
        local tree = {}
        local stack = {}
        local tmp = {}
        local level = 0
-
+       local op, tag, p, empty, val
        table.insert(stack, tree)
 
-       for op, tag, p, empty, val in string.gmatch(data, "<(%/?)([%w:]+)(.-)(%/?)>[%s\r\n\t]*([^<]*)") do
+       for op, tag, p, empty, val in string.gmatch(
+               data,
+               "[%s\r\n\t]*<(%/?)([%w:_]+)(.-)(%/?)>[%s\r\n\t]*([^<]*)[%s\r\n\t]*"
+       ) do
                if op=="/" then
-                       if level>1 then
+                       if level>0 then
                                level = level - 1
                                table.remove(stack)
                        end
@@ -1025,6 +1752,8 @@ function parse_xml(data)
                        end
                end
        end
+
+       collectgarbage()
        return tree
 end
 
@@ -1034,9 +1763,13 @@ function parse_xmlrpc(data)
        local tmp = {}
        local tmpTag = ""
        local level = 0
+       local op, tag, p, empty, val
        table.insert(stack, tree)
 
-       for op, tag, p, empty, val in string.gmatch(data, "<(%/?)([%w:]+)(.-)(%/?)>[%s\r\n\t]*([^<]*)") do
+       for op, tag, p, empty, val in string.gmatch(
+               data,
+               "<(%/?)([%w:]+)(.-)(%/?)>[%s\r\n\t]*([^<]*)"
+       ) do
                if op=="/" then
                        if tag == "member" or tag == "array" then
                                if level>0  then
@@ -1046,7 +1779,7 @@ function parse_xmlrpc(data)
                        end
                elseif tag == "name" then
                        level = level + 1
-                       if val~=""then tmpTag  = vlc.strings.resolve_xml_special_chars(val) end
+                       if val~= "" then tmpTag  = vlc.strings.resolve_xml_special_chars(val) end
 
                        if type(stack[level][tmpTag]) == "nil" then
                                stack[level][tmpTag] = {}
@@ -1075,6 +1808,7 @@ function parse_xmlrpc(data)
                        stack[level][tmpTag] = vlc.strings.resolve_xml_special_chars(val)
                end
        end
+       collectgarbage()
        return tree
 end
 
@@ -1084,7 +1818,22 @@ function dump_xml(data)
        local dump = ""
 
        local function parse(data, stack)
+               local data_index = {}
+               local k
+               local v
+               local i
+               local tb
+
                for k,v in pairs(data) do
+                       table.insert(data_index, {k, v})
+                       table.sort(data_index, function(a, b)
+                               return a[1] < b[1]
+                       end)
+               end
+
+               for i,tb in pairs(data_index) do
+                       k = tb[1]
+                       v = tb[2]
                        if type(k)=="string" then
                                dump = dump.."\r\n"..string.rep (" ", level).."<"..k..">"
                                table.insert(stack, k)
@@ -1096,9 +1845,11 @@ function dump_xml(data)
                        if type(v)=="table" then
                                parse(v, stack)
                        elseif type(v)=="string" then
-                               dump = dump..vlc.strings.convert_xml_special_chars(v)
+                               dump = dump..(vlc.strings.convert_xml_special_chars(v) or v)
                        elseif type(v)=="number" then
                                dump = dump..v
+                       else
+                               dump = dump..tostring(v)
                        end
 
                        if type(k)=="string" then
@@ -1120,151 +1871,130 @@ function dump_xml(data)
                end
        end
        parse(data, stack)
+       collectgarbage()
        return dump
 end
 
---~ Interface data
-
-languages = {
-       {'All', 'all'},
-       {'Albanian', 'alb'},
-       {'Arabic', 'ara'},
-       {'Armenian', 'arm'},
-       {'Malay', 'may'},
-       {'Bosnian', 'bos'},
-       {'Bulgarian', 'bul'},
-       {'Catalan', 'cat'},
-       {'Basque', 'eus'},
-       {'Chinese (China)', 'chi'},
-       {'Croatian', 'hrv'},
-       {'Czech', 'cze'},
-       {'Danish', 'dan'},
-       {'Dutch', 'dut'},
-       {'English (US)', 'eng'},
-       {'English (UK)', 'bre'},
-       {'Esperanto', 'epo'},
-       {'Estonian', 'est'},
-       {'Finnish', 'fin'},
-       {'French', 'fre'},
-       {'Galician', 'glg'},
-       {'Georgian', 'geo'},
-       {'German', 'ger'},
-       {'Greek', 'ell'},
-       {'Hebrew', 'heb'},
-       {'Hungarian', 'hun'},
-       {'Indonesian', 'ind'},
-       {'Italian', 'ita'},
-       {'Japanese', 'jpn'},
-       {'Kazakh', 'kaz'},
-       {'Korean', 'kor'},
-       {'Latvian', 'lav'},
-       {'Lithuanian', 'lit'},
-       {'Luxembourgish', 'ltz'},
-       {'Macedonian', 'mac'},
-       {'Norwegian', 'nor'},
-       {'Persian', 'per'},
-       {'Polish', 'pol'},
-       {'Portuguese (Portugal)', 'por'},
-       {'Portuguese (Brazil)', 'pob'},
-       {'Romanian', 'rum'},
-       {'Russian', 'rus'},
-       {'Serbian', 'scc'},
-       {'Slovak', 'slo'},
-       {'Slovenian', 'slv'},
-       {'Spanish (Spain)', 'spa'},
-       {'Swedish', 'swe'},
-       {'Thai', 'tha'},
-       {'Turkish', 'tur'},
-       {'Ukrainian', 'ukr'},
-       {'Vietnamese', 'vie'}
-}
+                                               --[[ Misc utils]]--
 
-methods = {
-       {"Video hash", "hash"},
-       {"IMDB ID", "imdb"}
-}
+function make_uri(str, encode)
+    local windowdrive = string.match(str, "^(%a:%).+$")
+       if encode then
+               local encodedPath = ""
+               for w in string.gmatch(str, "/([^/]+)") do
+                       encodedPath = encodedPath.."/"..vlc.strings.encode_uri_component(w)
+               end
+               str = encodedPath
+       end
+    if windowdrive then
+        return "file:///"..windowdrive..str
+    else
+        return "file://"..str
+    end
+end
+
+function file_touch(name) -- test writetability
+       if not name or trim(name) == ""
+       then return false end
+
+       local f=io.open(name ,"w")
+       if f~=nil then
+               io.close(f)
+               return true
+       else
+               return false
+       end
+end
+
+function file_exist(name) -- test readability
+       if not name or trim(name) == ""
+       then return false end
+       local f=io.open(name ,"r")
+       if f~=nil then
+               io.close(f)
+               return true
+       else
+               return false
+       end
+end
+
+function is_dir(path)
+       if not path or trim(path) == ""
+       then return false end
+       -- Remove slash at the or it won't work on Windows
+       path = string.gsub(path, "^(.-)[\\/]?$", "%1")
+       local f, _, code = io.open(path, "rb")
+
+       if f then
+               _, _, code = f:read("*a")
+               f:close()
+               if code == 21 then
+                       return true
+               end
+       elseif code == 13 then
+               return true
+       end
+
+       return false
+end
+
+function list_dir(path)
+       if not path or trim(path) == ""
+       then return false end
+       local dir_list_cmd
+       local list = {}
+       if not is_dir(path) then return false end
+
+       if openSub.conf.os == "win" then
+               dir_list_cmd = io.popen('dir /b "'..path..'"')
+       elseif openSub.conf.os == "lin" then
+               dir_list_cmd = io.popen('ls -1 "'..path..'"')
+       end
+
+       if dir_list_cmd then
+               for filename in dir_list_cmd:lines() do
+                       if string.match(filename, "^[^%s]+.+$") then
+                               table.insert(list, filename)
+                       end
+               end
+               return list
+       else
+               return false
+       end
+end
+
+function mkdir_p(path)
+       if not path or trim(path) == ""
+       then return false end
+       if openSub.conf.os == "win" then
+               os.execute('mkdir "' .. path..'"')
+       elseif openSub.conf.os == "lin" then
+               os.execute("mkdir -p '" .. path.."'")
+       end
+end
+
+function is_window_path(path)
+       return string.match(path, "^(%a:%).+$")
+end
+
+function is_win_safe(path)
+       if not path or trim(path) == ""
+       or not is_window_path(path)
+       then return false end
+       return string.match(path, "^%a?%:?[\\%w%p%s§¤]+$")
+end
+
+function trim(str)
+    if not str then return "" end
+    return string.gsub(str, "^[\r\n%s]*(.-)[\r\n%s]*$", "%1")
+end
+
+function remove_tag(str)
+       return string.gsub(str, "{[^}]+}", "")
+end
+
+function sleep(sec)
+   local t = vlc.misc.mdate()
+   vlc.misc.mwait(t + sec*1000*1000)
+end
 
-interface = {
-       {
-               id = "header",
-               type = "div",
-               content = {
-                       {
-                               { type = "label", value = "Language:" },
-                               { type = "dropdown", value = languages, id = "language" , width = 2 },
-                               { type = "button", value = "Search by hash", callback = searchHash },
-                       }
-               }
-       },
-       {
-               id = "imdb",
-               type = "div",
-               content = {
-                       {
-                               { type = "label", value = "Title:"},
-                               { type = "text_input", value = openSub.movie.name or "", id = "title", width = 2 },
-                               { type = "button", value = "Search by name", callback = searchIMBD }
-                       },{
-                               { type = "label", value = "Season (series):"},
-                               { type = "text_input", value = openSub.movie.seasonNumber or "", id = "season", width = 2 }
-                       },{
-                               { type = "label", value = "Episode (series):"},
-                               { type = "text_input", value = openSub.movie.episodeNumber or "", id = "episode", width = 2 }
-                       },{
-                               { type = "list", width = 4, id = "mainlist" }
-                       }
-               }
-       },
-       {
-               type = "div",
-               content = {
-                       {
-                               { type = "button", value = "Help", callback = toggle_help },
-                               { type = "span", width = 1},
-                               { type = "button", value = "Download", callback = download_selection },
-                               { type = "button", value = "Close", callback = close }
-                       }
-               }
-       },
-       {
-               id = "progressBar",
-               type = "div",
-               content = {
-                       {
-                               { type = "html", width = 4,
-                                       display = "none",
-                                       value = ""..
-                                       " Download subtittles from <a href='http://www.opensubtitles.org/'>opensubtitles.org</a> and display them while watching a video.<br>"..
-                                       " <br>"..
-                                       " <b><u>Usage:</u></b><br>"..
-                                       " <br>"..
-                                       " VLSub is meant to be used while your watching the video, so start it first (if nothing is playing you will get a link to download the subtitles in your browser).<br>"..
-                                       " <br>"..
-                                       " Choose the language for your subtitles and click on the button corresponding to one of the two research method provided by VLSub:<br>"..
-                                       " <br>"..
-                                       " <b>Method 1: Search by hash</b><br>"..
-                                       " It is recommended to try this method first, because it performs a research based on the video file print, so you can find subtitles synchronized with your video.<br>"..
-                                       " <br>"..
-                                       " <b>Method 2: Search by name</b><br>"..
-                                       " If you have no luck with the first method, just check the title is correct before clicking. If you search subtitles for a serie, you can also provide a season and episode number.<br>"..
-                                       " <br>"..
-                                       " <b>Downloading Subtitles</b><br>"..
-                                       " Select one subtitle in the list and click on 'Download'.<br>"..
-                                       " It will be put in the same directory that your video, with the same name (different extension)"..
-                                       " so Vlc will load them automatically the next time you'll start the video.<br>"..
-                                       " <br>"..
-                                       " <b>/!\\ Beware :</b> Existing subtitles are overwrited without asking confirmation, so put them elsewhere if thet're important."
-                                       , id = "helpMessage"
-                               }
-                       },{
-                               {
-                                       type = "label",
-                                       width = 4,
-                                       display = "none",
-                                       value = "  ",
-                                       id = "message"
-                               }
-                       }
-               }
-       }
-}