Guest User

vlsub for streaming

a guest
Feb 13th, 2016
146
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 61.25 KB | None | 0 0
  1. --[[
  2. VLSub Extension for VLC media player 1.1 and 2.0
  3. Copyright 2013 Guillaume Le Maout
  4.  
  5. Authors:  Guillaume Le Maout
  6. Contact:
  7. http://addons.videolan.org/messages/?action=newmessage&username=exebetche
  8. Bug report: http://addons.videolan.org/content/show.php/?content=148752
  9.  
  10. This program is free software; you can redistribute it and/or modify
  11. it under the terms of the GNU General Public License as published by
  12. the Free Software Foundation; either version 2 of the License, or
  13. (at your option) any later version.
  14.  
  15. This program is distributed in the hope that it will be useful,
  16. but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18. GNU General Public License for more details.
  19.  
  20. You should have received a copy of the GNU General Public License
  21. along with this program; if not, write to the Free Software
  22. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  23. --]]
  24.  
  25.  
  26.             --[[ Global var ]]--
  27.  
  28. -- You can set here your default language by replacing nil with
  29. -- your language code (see below).Example:
  30. -- language = "fre",
  31. -- language = "ger",
  32. -- language = "eng",
  33. -- ...
  34.  
  35. local options = {
  36.   language = "tur",
  37.   downloadBehaviour = 'save',
  38.   langExt = false,
  39.   removeTag = false,
  40.   showMediaInformation = true,
  41.   progressBarSize = 80,
  42.   intLang = 'eng',
  43.   translations_avail = {
  44.     eng = 'English',
  45.     cze = 'Czech',
  46.     dan = 'Danish',
  47.     dut = 'Nederlands',
  48.     fre = 'Français',
  49.     ell = 'Greek',
  50.     baq = 'Basque',
  51.     pob = 'Brazilian Portuguese',
  52.     por = 'Portuguese (Portugal)',
  53.     rum = 'Romanian',
  54.     slo = 'Slovak',
  55.     spa = 'Spanish',
  56.     swe = 'Swedish',
  57.     ukr = 'Ukrainian',
  58.     hun = 'Hungarian'
  59.   },
  60.   translation = {
  61.     int_all = 'All',
  62.     int_descr = 'Download subtitles from OpenSubtitles.org',
  63.     int_research = 'Research',
  64.     int_config = 'Config',
  65.     int_configuration = 'Configuration',
  66.     int_help = 'Help',
  67.     int_search_hash = 'Search by hash',
  68.     int_search_name = 'Search by name',
  69.     int_title = 'Title',
  70.     int_season = 'Season (series)',
  71.     int_episode = 'Episode (series)',
  72.     int_show_help = 'Show help',
  73.     int_show_conf = 'Show config',
  74.     int_dowload_sel = 'Download selection',
  75.     int_close = 'Close',
  76.     int_ok = 'Ok',
  77.     int_save = 'Save',
  78.     int_cancel = 'Cancel',
  79.     int_bool_true = 'Yes',
  80.     int_bool_false = 'No',
  81.     int_search_transl = 'Search translations',
  82.     int_searching_transl = 'Searching translations ...',
  83.     int_int_lang = 'Interface language',
  84.     int_default_lang = 'Subtitles language',
  85.     int_dowload_behav = 'What to do with subtitles',
  86.     int_dowload_save = 'Load and save',
  87.     int_dowload_load = 'Load only',
  88.     int_dowload_manual =  'Manual download',
  89.     int_display_code = 'Display language code in file name',
  90.     int_remove_tag = 'Remove tags',
  91.     int_vlsub_work_dir = 'VLSub working directory',
  92.     int_os_username = 'Username',
  93.     int_os_password = 'Password',
  94.     int_help_mess =[[
  95.       Download subtitles from
  96.       <a href='http://www.opensubtitles.org/'>
  97.       opensubtitles.org
  98.       </a> and display them while watching a video.<br>
  99.       <br>
  100.       <b><u>Usage:</u></b><br>
  101.       <br>
  102.       Start your video. If you use Vlsub witout playing a video
  103.       you will get a link to download the subtitles in your browser
  104.       but the subtitles won't be saved and loaded automatically.<br>
  105.      <br>
  106.      Choose the language for your subtitles and click on the
  107.      button corresponding to one of the two research methods
  108.      provided by VLSub:<br>
  109.      <br>
  110.      <b>Method 1: Search by hash</b><br>
  111.      It is recommended to try this method first, because it
  112.      performs a research based on the video file print, so you
  113.      can find subtitles synchronized with your video.<br>
  114.      <br>
  115.      <b>Method 2: Search by name</b><br>
  116.      If you have no luck with the first method, just check the
  117.      title is correct before clicking. If you search subtitles
  118.      for a series, you can also provide a season and episode
  119.      number.<br>
  120.      <br>
  121.      <b>Downloading Subtitles</b><br>
  122.      Select one subtitle in the list and click on 'Download'.<br>
  123.      It will be put in the same directory that your video, with
  124.      the same name (different extension)
  125.      so VLC will load them automatically the next time you'll
  126.       start the video.<br>
  127.       <br>
  128.       <b>/!\\ Beware :</b> Existing subtitles are overwritten
  129.       without asking confirmation, so put them elsewhere if
  130.       they're important.<br>
  131.      <br>
  132.      Find more VLC extensions at
  133.      <a href='http://addons.videolan.org'>addons.videolan.org</a>.
  134.      ]],
  135.    int_no_support_mess = [[
  136.      <strong>VLSub is not working with Vlc 2.1.x on
  137.      any platform</strong>
  138.      because the lua "net" module needed to interact
  139.      with opensubtitles has been
  140.      removed in this release for the extensions.
  141.      <br>
  142.      <strong>Works with Vlc 2.2 on mac and linux.</strong>
  143.      <br>
  144.      <strong>On windows you have to install an older version
  145.      of Vlc (2.0.8 for example)</strong>
  146.      to use Vlsub:
  147.      <br>
  148.      <a target="_blank" rel="nofollow"
  149.      href="http://download.videolan.org/pub/videolan/vlc/2.0.8/">
  150.      http://download.videolan.org/pub/videolan/vlc/2.0.8/</a><br>
  151.    ]],
  152.  
  153.    action_login = 'Logging in',
  154.    action_logout = 'Logging out',
  155.    action_noop = 'Checking session',
  156.    action_search = 'Searching subtitles',
  157.    action_hash = 'Calculating movie hash',
  158.    
  159.    mess_success = 'Success',
  160.    mess_error = 'Error',
  161.    mess_no_response = 'Server not responding',
  162.    mess_unauthorized = 'Request unauthorized',
  163.    mess_expired = 'Session expired, retrying',
  164.    mess_overloaded = 'Server overloaded, please retry later',
  165.    mess_no_input = 'Please use this method during playing',
  166.    mess_not_local = 'This method works with local file only (for now)',
  167.    mess_not_found = 'File not found',
  168.    mess_not_found2 = 'File not found (illegal character?)',
  169.    mess_no_selection = 'No subtitles selected',
  170.    mess_save_fail = 'Unable to save subtitles',
  171.    mess_click_link = 'Click here to open the file',
  172.    mess_complete = 'Research complete',
  173.    mess_no_res = 'No result',
  174.    mess_res = 'result(s)',
  175.    mess_loaded = 'Subtitles loaded',
  176.    mess_not_load = 'Unable to load subtitles',
  177.    mess_downloading = 'Downloading subtitle',
  178.    mess_dowload_link = 'Download link',
  179.    mess_err_conf_access ='Can\'t find a suitable path to save'..
  180.       'config, please set it manually',
  181.     mess_err_wrong_path ='the path contains illegal character, '..
  182.       'please correct it'
  183.   }
  184. }
  185.  
  186. local languages = {
  187.   {'alb', 'Albanian'},
  188.   {'ara', 'Arabic'},
  189.   {'arm', 'Armenian'},
  190.   {'baq', 'Basque'},
  191.   {'ben', 'Bengali'},
  192.   {'bos', 'Bosnian'},
  193.   {'bre', 'Breton'},
  194.   {'bul', 'Bulgarian'},
  195.   {'bur', 'Burmese'},
  196.   {'cat', 'Catalan'},
  197.   {'chi', 'Chinese'},
  198.   {'hrv', 'Croatian'},
  199.   {'cze', 'Czech'},
  200.   {'dan', 'Danish'},
  201.   {'dut', 'Dutch'},
  202.   {'eng', 'English'},
  203.   {'epo', 'Esperanto'},
  204.   {'est', 'Estonian'},
  205.   {'fin', 'Finnish'},
  206.   {'fre', 'French'},
  207.   {'glg', 'Galician'},
  208.   {'geo', 'Georgian'},
  209.   {'ger', 'German'},
  210.   {'ell', 'Greek'},
  211.   {'heb', 'Hebrew'},
  212.   {'hin', 'Hindi'},
  213.   {'hun', 'Hungarian'},
  214.   {'ice', 'Icelandic'},
  215.   {'ind', 'Indonesian'},
  216.   {'ita', 'Italian'},
  217.   {'jpn', 'Japanese'},
  218.   {'kaz', 'Kazakh'},
  219.   {'khm', 'Khmer'},
  220.   {'kor', 'Korean'},
  221.   {'lav', 'Latvian'},
  222.   {'lit', 'Lithuanian'},
  223.   {'ltz', 'Luxembourgish'},
  224.   {'mac', 'Macedonian'},
  225.   {'may', 'Malay'},
  226.   {'mal', 'Malayalam'},
  227.   {'mon', 'Mongolian'},
  228.   {'nor', 'Norwegian'},
  229.   {'oci', 'Occitan'},
  230.   {'per', 'Persian'},
  231.   {'pol', 'Polish'},
  232.   {'por', 'Portuguese'},
  233.   {'pob', 'Brazilian Portuguese'},
  234.   {'rum', 'Romanian'},
  235.   {'rus', 'Russian'},
  236.   {'scc', 'Serbian'},
  237.   {'sin', 'Sinhalese'},
  238.   {'slo', 'Slovak'},
  239.   {'slv', 'Slovenian'},
  240.   {'spa', 'Spanish'},
  241.   {'swa', 'Swahili'},
  242.   {'swe', 'Swedish'},
  243.   {'syr', 'Syriac'},
  244.   {'tgl', 'Tagalog'},
  245.   {'tel', 'Telugu'},
  246.   {'tha', 'Thai'},
  247.   {'tur', 'Turkish'},
  248.   {'ukr', 'Ukrainian'},
  249.   {'urd', 'Urdu'},
  250.   {'vie', 'Vietnamese'}
  251. }
  252.  
  253. -- Languages code conversion table: iso-639-1 to iso-639-3
  254. -- See https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
  255. local lang_os_to_iso = {
  256.   sq = "alb",
  257.   ar = "ara",
  258.   hy = "arm",
  259.   eu = "baq",
  260.   bn = "ben",
  261.   bs = "bos",
  262.   br = "bre",
  263.   bg = "bul",
  264.   my = "bur",
  265.   ca = "cat",
  266.   zh = "chi",
  267.   hr = "hrv",
  268.   cs = "cze",
  269.   da = "dan",
  270.   nl = "dut",
  271.   en = "eng",
  272.   eo = "epo",
  273.   et = "est",
  274.   fi = "fin",
  275.   fr = "fre",
  276.   gl = "glg",
  277.   ka = "geo",
  278.   de = "ger",
  279.   el = "ell",
  280.   he = "heb",
  281.   hi = "hin",
  282.   hu = "hun",
  283.   is = "ice",
  284.   id = "ind",
  285.   it = "ita",
  286.   ja = "jpn",
  287.   kk = "kaz",
  288.   km = "khm",
  289.   ko = "kor",
  290.   lv = "lav",
  291.   lt = "lit",
  292.   lb = "ltz",
  293.   mk = "mac",
  294.   ms = "may",
  295.   ml = "mal",
  296.   mn = "mon",
  297.   no = "nor",
  298.   oc = "oci",
  299.   fa = "per",
  300.   pl = "pol",
  301.   pt = "por",
  302.   po = "pob",
  303.   ro = "rum",
  304.   ru = "rus",
  305.   sr = "scc",
  306.   si = "sin",
  307.   sk = "slo",
  308.   sl = "slv",
  309.   es = "spa",
  310.   sw = "swa",
  311.   sv = "swe",
  312.   tl = "tgl",
  313.   te = "tel",
  314.   th = "tha",
  315.   tr = "tur",
  316.   uk = "ukr",
  317.   ur = "urd",
  318.   vi = "vie"
  319. }
  320.  
  321. local dlg = nil
  322. local input_table = {} -- General widget id reference
  323. local select_conf = {} -- Drop down widget / option table association
  324.  
  325.             --[[ VLC extension stuff ]]--
  326.  
  327. function descriptor()
  328.   return {
  329.     title = "VLsub 0.9.13",
  330.     version = "0.9.13",
  331.     author = "exebetche",
  332.     url = 'http://www.opensubtitles.org/',
  333.     shortdesc = "VLsub";
  334.     description = options.translation.int_descr,
  335.     capabilities = {"menu", "input-listener" }
  336.   }
  337. end
  338.  
  339. function activate()
  340.   vlc.msg.dbg("[VLsub] Welcome")
  341.  
  342.   if not check_config() then
  343.     vlc.msg.err("[VLsub] Unsupported VLC version")
  344.     return false
  345.   end
  346.    
  347. --  if vlc.input.item() then
  348. --    openSub.getFileInfo()
  349. --    openSub.getMovieInfo()
  350. --  end
  351.  
  352.   show_main()
  353. end
  354.  
  355. function close()
  356.   vlc.deactivate()
  357. end
  358.  
  359. function deactivate()
  360.   vlc.msg.dbg("[VLsub] Bye bye!")
  361.   if dlg then
  362.     dlg:hide()
  363.   end
  364.  
  365.   if openSub.session.token and openSub.session.token ~= "" then
  366.     openSub.request("LogOut")
  367.   end
  368. end
  369.  
  370. function menu()
  371.   return {    
  372.     lang.int_research,
  373.     lang.int_config,
  374.     lang.int_help
  375.   }
  376. end
  377.  
  378. function meta_changed()
  379.   return false
  380. end
  381.  
  382. function input_changed()
  383.   collectgarbage()
  384.   set_interface_main()
  385.   collectgarbage()
  386. end
  387.  
  388.             --[[ Interface data ]]--
  389.  
  390. function interface_main()
  391.   dlg:add_label(lang["int_default_lang"]..':', 1, 1, 1, 1)
  392.   input_table['language'] =  dlg:add_dropdown(2, 1, 2, 1)
  393.   dlg:add_button(lang["int_search_hash"],
  394.     searchHash, 4, 1, 1, 1)
  395.  
  396.   dlg:add_label(lang["int_title"]..':', 1, 2, 1, 1)
  397.   input_table['title'] = dlg:add_text_input(
  398.     openSub.movie.title or "", 2, 2, 2, 1)
  399.   dlg:add_button(lang["int_search_name"],
  400.     searchIMBD, 4, 2, 1, 1)
  401.   dlg:add_label(lang["int_season"]..':', 1, 3, 1, 1)
  402.   input_table['seasonNumber'] = dlg:add_text_input(
  403.     openSub.movie.seasonNumber or "", 2, 3, 2, 1)
  404.   dlg:add_label(lang["int_episode"]..':', 1, 4, 1, 1)
  405.   input_table['episodeNumber'] = dlg:add_text_input(
  406.     openSub.movie.episodeNumber or "", 2, 4, 2, 1)
  407.   input_table['mainlist'] = dlg:add_list(1, 5, 4, 1)
  408.   input_table['message'] = nil
  409.   input_table['message'] = dlg:add_label(' ', 1, 6, 4, 1)
  410.   dlg:add_button(
  411.     lang["int_show_help"], show_help, 1, 7, 1, 1)
  412.   dlg:add_button(
  413.     '   '..lang["int_show_conf"]..'   ', show_conf, 2, 7, 1, 1)
  414.   dlg:add_button(
  415.     lang["int_dowload_sel"], download_subtitles, 3, 7, 1, 1)
  416.   dlg:add_button(
  417.     lang["int_close"], deactivate, 4, 7, 1, 1)
  418.  
  419.   assoc_select_conf(
  420.     'language',
  421.     'language',
  422.     openSub.conf.languages,
  423.     2,
  424.     lang["int_all"])
  425.    
  426.   display_subtitles()
  427. end
  428.  
  429. function set_interface_main()
  430.   -- Update movie title and co. if video input change
  431.   if not type(input_table['title']) == 'userdata' then return false end
  432.  
  433.   openSub.getFileInfo()
  434.   openSub.getMovieInfo()
  435.  
  436.   input_table['title']:set_text(
  437.     openSub.movie.title or "")
  438.   input_table['episodeNumber']:set_text(
  439.     openSub.movie.episodeNumber or "")
  440.   input_table['seasonNumber']:set_text(
  441.     openSub.movie.seasonNumber or "")
  442. end
  443.  
  444. function interface_config()
  445.   input_table['intLangLab'] = dlg:add_label(
  446.     lang["int_int_lang"]..':', 1, 1, 1, 1)
  447.   input_table['intLangBut'] = dlg:add_button(
  448.     lang["int_search_transl"],
  449.     get_available_translations, 2, 1, 1, 1)
  450.   input_table['intLang'] = dlg:add_dropdown(3, 1, 1, 1)
  451.   dlg:add_label(
  452.     lang["int_default_lang"]..':', 1, 2, 2, 1)
  453.   input_table['default_language'] = dlg:add_dropdown(3, 2, 1, 1)   
  454.   dlg:add_label(
  455.     lang["int_dowload_behav"]..':', 1, 3, 2, 1)
  456.   input_table['downloadBehaviour'] = dlg:add_dropdown(3, 3, 1, 1)
  457.   dlg:add_label(
  458.     lang["int_display_code"]..':', 1, 4, 0, 1)
  459.   input_table['langExt'] = dlg:add_dropdown(3, 4, 1, 1)
  460.   dlg:add_label(
  461.     lang["int_remove_tag"]..':', 1, 5, 0, 1)
  462.   input_table['removeTag'] = dlg:add_dropdown(3, 5, 1, 1)
  463.    
  464.   if openSub.conf.dirPath then
  465.     if openSub.conf.os == "win" then
  466.       dlg:add_label(
  467.         "<a href='file:///"..openSub.conf.dirPath.."'>"..
  468.         lang["int_vlsub_work_dir"].."</a>", 1, 6, 2, 1)
  469.     else
  470.       dlg:add_label(
  471.         "<a href='"..openSub.conf.dirPath.."'>"..
  472.         lang["int_vlsub_work_dir"].."</a>", 1, 6, 2, 1)
  473.     end
  474.   else
  475.     dlg :add_label(
  476.       lang["int_vlsub_work_dir"], 1, 6, 2, 1)
  477.   end
  478.  
  479.   input_table['dir_path'] = dlg:add_text_input(
  480.     openSub.conf.dirPath, 2, 6, 2, 1)
  481.  
  482.   dlg:add_label(
  483.     lang["int_os_username"]..':', 1, 7, 0, 1)
  484.   input_table['os_username'] = dlg:add_text_input(
  485.     type(openSub.option.os_username) == "string"
  486.     and openSub.option.os_username or "", 2, 7, 2, 1)
  487.   dlg:add_label(
  488.     lang["int_os_password"]..':', 1, 8, 0, 1)
  489.   input_table['os_password'] = dlg:add_text_input(
  490.     type(openSub.option.os_password) == "string"
  491.     and openSub.option.os_password or "", 2, 8, 2, 1)
  492.        
  493.   input_table['message'] = nil
  494.   input_table['message'] = dlg:add_label(' ', 1, 9, 3, 1)
  495.  
  496.   dlg:add_button(
  497.     lang["int_cancel"],
  498.     show_main, 2, 10, 1, 1)
  499.   dlg:add_button(
  500.     lang["int_save"],
  501.     apply_config, 3, 10, 1, 1)
  502.  
  503.   input_table['langExt']:add_value(
  504.     lang["int_bool_"..tostring(openSub.option.langExt)], 1)
  505.   input_table['langExt']:add_value(
  506.     lang["int_bool_"..tostring(not openSub.option.langExt)], 2)
  507.   input_table['removeTag']:add_value(
  508.     lang["int_bool_"..tostring(openSub.option.removeTag)], 1)
  509.   input_table['removeTag']:add_value(
  510.     lang["int_bool_"..tostring(not openSub.option.removeTag)], 2)
  511.  
  512.   assoc_select_conf(
  513.     'intLang',
  514.     'intLang',
  515.     openSub.conf.translations_avail,
  516.     2)
  517.   assoc_select_conf(
  518.     'default_language',
  519.     'language',
  520.     openSub.conf.languages,
  521.     2,
  522.     lang["int_all"])
  523.   assoc_select_conf(
  524.     'downloadBehaviour',
  525.     'downloadBehaviour',
  526.     openSub.conf.downloadBehaviours,
  527.     1)
  528. end
  529.  
  530. function interface_help()
  531.   local help_html = lang["int_help_mess"]
  532.    
  533.   input_table['help'] = dlg:add_html(
  534.     help_html, 1, 1, 4, 1)
  535.   dlg:add_label(
  536.     string.rep ("&nbsp;", 100), 1, 2, 3, 1)
  537.   dlg:add_button(
  538.     lang["int_ok"], show_main, 4, 2, 1, 1)
  539. end
  540.  
  541. function interface_no_support()
  542.   local no_support_html = lang["int_no_support_mess"]
  543.    
  544.   input_table['no_support'] = dlg:add_html(
  545.     no_support_html, 1, 1, 4, 1)
  546.   dlg:add_label(
  547.     string.rep ("&nbsp;", 100), 1, 2, 3, 1)
  548. end
  549.  
  550. function trigger_menu(dlg_id)
  551.   if dlg_id == 1 then
  552.     close_dlg()
  553.     dlg = vlc.dialog(
  554.       openSub.conf.useragent)
  555.     interface_main()
  556.   elseif dlg_id == 2 then
  557.     close_dlg()
  558.     dlg = vlc.dialog(
  559.       openSub.conf.useragent..': '..lang["int_configuration"])
  560.     interface_config()
  561.   elseif dlg_id == 3 then
  562.     close_dlg()
  563.     dlg = vlc.dialog(
  564.       openSub.conf.useragent..': '..lang["int_help"])
  565.     interface_help()
  566.   end
  567.   collectgarbage() --~ !important  
  568. end
  569.  
  570. function show_main()
  571.   trigger_menu(1)
  572. end
  573.  
  574. function show_conf()
  575.   trigger_menu(2)
  576. end
  577.  
  578. function show_help()
  579.   trigger_menu(3)
  580. end
  581.  
  582. function close_dlg()
  583.   vlc.msg.dbg("[VLSub] Closing dialog")
  584.  
  585.   if dlg ~= nil then
  586.     --~ dlg:delete() -- Throw an error
  587.     dlg:hide()
  588.   end
  589.  
  590.   dlg = nil
  591.   input_table = nil
  592.   input_table = {}
  593.   collectgarbage() --~ !important  
  594. end
  595.  
  596.             --[[ Drop down / config association]]--
  597.  
  598. function assoc_select_conf(select_id, option, conf, ind, default)
  599. -- Helper for i/o interaction between drop down and option list
  600.   select_conf[select_id] = {
  601.     cf = conf,
  602.     opt  = option,
  603.     dflt = default,
  604.     ind = ind
  605.   }
  606.   set_default_option(select_id)
  607.   display_select(select_id)
  608. end
  609.  
  610. function set_default_option(select_id)
  611. -- Put the selected option of a list in first place of the associated table
  612.   local opt = select_conf[select_id].opt
  613.   local cfg = select_conf[select_id].cf
  614.   local ind = select_conf[select_id].ind
  615.   if openSub.option[opt] then
  616.     table.sort(cfg, function(a, b)
  617.       if a[1] == openSub.option[opt] then
  618.         return true
  619.       elseif b[1] == openSub.option[opt] then
  620.         return false
  621.       else
  622.         return a[ind] < b[ind]
  623.       end
  624.     end)
  625.   end
  626. end
  627.  
  628. function display_select(select_id)
  629. -- Display the drop down values with an optional default value at the top
  630.   local conf = select_conf[select_id].cf
  631.   local opt = select_conf[select_id].opt
  632.   local option = openSub.option[opt]
  633.   local default = select_conf[select_id].dflt
  634.   local default_isset = false
  635.    
  636.   if not default then
  637.     default_isset = true
  638.   end
  639.  
  640.   for k, l in ipairs(conf) do
  641.     if default_isset then
  642.       input_table[select_id]:add_value(l[2], k)
  643.     else
  644.       if option then
  645.         input_table[select_id]:add_value(l[2], k)
  646.         input_table[select_id]:add_value(default, 0)
  647.       else
  648.         input_table[select_id]:add_value(default, 0)
  649.         input_table[select_id]:add_value(l[2], k)
  650.       end
  651.       default_isset = true
  652.     end
  653.   end
  654. end
  655.  
  656.             --[[ Config & interface localization]]--
  657.  
  658. function check_config()
  659.   -- Make a copy of english translation to use it as default
  660.   -- in case some element aren't translated in other translations
  661.   eng_translation = {}
  662.   for k, v in pairs(openSub.option.translation) do
  663.     eng_translation[k] = v
  664.   end
  665.  
  666.   -- Get available translation full name from code
  667.   trsl_names = {}
  668.   for i, lg in ipairs(languages) do
  669.     trsl_names[lg[1]] = lg[2]
  670.   end
  671.  
  672.   if is_window_path(vlc.config.datadir()) then
  673.     openSub.conf.os = "win"
  674.     slash = "\\"
  675.   else
  676.     openSub.conf.os = "lin"
  677.     slash = "/"
  678.   end
  679.  
  680.   local path_generic = {"lua", "extensions", "userdata", "vlsub"}
  681.   local dirPath = slash..table.concat(path_generic, slash)
  682.   local filePath    = slash.."vlsub_conf.xml"
  683.   local config_saved = false
  684.   sub_dir = slash.."vlsub_subtitles"
  685.  
  686.   -- Check if config file path is stored in vlc config
  687.   local other_dirs = {}
  688.  
  689.   for path in
  690.   vlc.config.get("sub-autodetect-path"):gmatch("[^,]+") do
  691.     if path:match(".*"..sub_dir.."$") then
  692.       openSub.conf.dirPath = path:gsub(
  693.         "%s*(.*)"..sub_dir.."%s*$", "%1")
  694.       config_saved = true
  695.     end
  696.     table.insert(other_dirs, path)
  697.   end
  698.  
  699.   -- if not stored in vlc config
  700.   -- try to find a suitable config file path
  701.  
  702.   if openSub.conf.dirPath then
  703.     if not is_dir(openSub.conf.dirPath) and
  704.     (openSub.conf.os == "lin"  or
  705.     is_win_safe(openSub.conf.dirPath)) then
  706.       mkdir_p(openSub.conf.dirPath)
  707.     end
  708.   else
  709.     local userdatadir = vlc.config.userdatadir()
  710.     local datadir = vlc.config.datadir()
  711.    
  712.     -- check if the config already exist
  713.     if file_exist(userdatadir..dirPath..filePath) then
  714.       -- in vlc.config.userdatadir()
  715.       openSub.conf.dirPath = userdatadir..dirPath
  716.       config_saved = true
  717.     elseif file_exist(datadir..dirPath..filePath) then
  718.       -- in vlc.config.datadir()
  719.       openSub.conf.dirPath = datadir..dirPath
  720.       config_saved = true
  721.     else
  722.       -- if not found determine an accessible path
  723.       local extension_path = slash..path_generic[1]
  724.         ..slash..path_generic[2]
  725.      
  726.       -- use the same folder as the extension if accessible
  727.       if is_dir(userdatadir..extension_path)
  728.       and file_touch(userdatadir..dirPath..filePath) then
  729.           openSub.conf.dirPath = userdatadir..dirPath
  730.       elseif file_touch(datadir..dirPath..filePath) then
  731.         openSub.conf.dirPath = datadir..dirPath
  732.       end
  733.      
  734.       -- try to create working dir in user folder
  735.       if not openSub.conf.dirPath
  736.       and is_dir(userdatadir) then
  737.         if not is_dir(userdatadir..dirPath) then
  738.           mkdir_p(userdatadir..dirPath)
  739.         end
  740.         if is_dir(userdatadir..dirPath) and
  741.         file_touch(userdatadir..dirPath..filePath) then
  742.           openSub.conf.dirPath = userdatadir..dirPath
  743.         end
  744.       end
  745.      
  746.       -- try to create working dir in vlc folder   
  747.       if not openSub.conf.dirPath and
  748.       is_dir(datadir) then
  749.         if not is_dir(datadir..dirPath) then
  750.           mkdir_p(datadir..dirPath)
  751.         end
  752.         if file_touch(datadir..dirPath..filePath) then
  753.           openSub.conf.dirPath = datadir..dirPath
  754.         end
  755.       end
  756.     end
  757.   end
  758.  
  759.   if openSub.conf.dirPath then
  760.     vlc.msg.dbg("[VLSub] Working directory: " ..
  761.       (openSub.conf.dirPath or "not found"))
  762.    
  763.     openSub.conf.filePath = openSub.conf.dirPath..filePath
  764.     openSub.conf.localePath = openSub.conf.dirPath..slash.."locale"
  765.    
  766.     if config_saved
  767.     and file_exist(openSub.conf.filePath) then
  768.       vlc.msg.dbg(
  769.         "[VLSub] Loading config file: "..openSub.conf.filePath)
  770.       load_config()
  771.     else
  772.       vlc.msg.dbg("[VLSub] No config file")
  773.       getenv_lang()
  774.       config_saved = save_config()
  775.       if not config_saved then
  776.         vlc.msg.dbg("[VLSub] Unable to save config")
  777.       end
  778.     end
  779.    
  780.     -- Check presence of a translation file
  781.     -- in "%vlsub_directory%/locale"
  782.     -- Add translation files to available translation list
  783.     local file_list = list_dir(openSub.conf.localePath)
  784.     local translations_avail = openSub.conf.translations_avail
  785.    
  786.     if file_list then
  787.       for i, file_name in ipairs(file_list) do
  788.         local lg =  string.gsub(
  789.           file_name,
  790.           "^(%w%w%w).xml$",
  791.           "%1")
  792.         if lg
  793.         and not openSub.option.translations_avail[lg] then
  794.           table.insert(translations_avail, {
  795.             lg,
  796.             trsl_names[lg]
  797.           })
  798.         end
  799.       end
  800.     end
  801.    
  802.     -- Load selected translation from file
  803.     if openSub.option.intLang ~= "eng"
  804.     and not openSub.conf.translated
  805.     then
  806.       local transl_file_path = openSub.conf.localePath..
  807.         slash..openSub.option.intLang..".xml"
  808.       if file_exist(transl_file_path) then
  809.         vlc.msg.dbg(
  810.           "[VLSub] Loading translation from file: "..
  811.           transl_file_path)
  812.         load_transl(transl_file_path)
  813.       end
  814.     end
  815.   else
  816.     vlc.msg.dbg("[VLSub] Unable to find a suitable path"..
  817.       "to save config, please set it manually")
  818.   end
  819.  
  820.   lang = nil
  821.   lang = options.translation -- just a short cut
  822.  
  823.   if not vlc.net or not vlc.net.poll then
  824.     dlg = vlc.dialog(
  825.       openSub.conf.useragent..': '..lang["mess_error"])
  826.     interface_no_support()
  827.     dlg:show()
  828.     return false
  829.   end
  830.  
  831.   SetDownloadBehaviours()
  832.   if not openSub.conf.dirPath then
  833.     setError(lang["mess_err_conf_access"])
  834.   end
  835.    
  836.   -- Set table list of available translations from assoc. array
  837.   -- so it is sortable
  838.  
  839.   for k, l in pairs(openSub.option.translations_avail) do      
  840.     if k == openSub.option.int_research then
  841.       table.insert(openSub.conf.translations_avail, 1, {k, l})
  842.     else
  843.       table.insert(openSub.conf.translations_avail, {k, l})
  844.     end
  845.   end
  846.   collectgarbage()
  847.   return true
  848. end
  849.  
  850. function load_config()
  851. -- Overwrite default conf with loaded conf
  852.   local tmpFile = io.open(openSub.conf.filePath, "rb")
  853.   if not tmpFile then return false end
  854.   local resp = tmpFile:read("*all")
  855.   tmpFile:flush()
  856.   tmpFile:close()
  857.   local option = parse_xml(resp)
  858.  
  859.   for key, value in pairs(option) do
  860.     if type(value) == "table" then
  861.       if key == "translation" then
  862.         openSub.conf.translated = true
  863.         for k, v in pairs(value) do
  864.           openSub.option.translation[k] = v
  865.         end
  866.       else
  867.         openSub.option[key] = value
  868.       end
  869.     else
  870.       if value == "true" then
  871.         openSub.option[key] = true
  872.       elseif value == "false" then
  873.         openSub.option[key] = false
  874.       else
  875.         openSub.option[key] = value
  876.       end
  877.     end
  878.   end
  879.   collectgarbage()
  880. end
  881.  
  882. function load_transl(path)
  883. -- Overwrite default conf with loaded conf
  884.   local tmpFile = assert(io.open(path, "rb"))
  885.   local resp = tmpFile:read("*all")
  886.   tmpFile:flush()
  887.   tmpFile:close()
  888.   openSub.option.translation = nil
  889.  
  890.   openSub.option.translation = parse_xml(resp) 
  891.   collectgarbage()
  892. end
  893.  
  894. function apply_translation()
  895. -- Overwrite default conf with loaded conf
  896.   for k, v in pairs(eng_translation) do
  897.     if not openSub.option.translation[k] then
  898.       openSub.option.translation[k] = eng_translation[k]
  899.     end
  900.   end
  901. end
  902.  
  903. function getenv_lang()
  904. -- Retrieve the user OS language
  905.   local os_lang = os.getenv("LANG")
  906.  
  907.   if os_lang then -- unix, mac
  908.     os_lang = string.sub(os_lang, 0, 2)
  909.     if type(lang_os_to_iso[os_lang]) then
  910.       openSub.option.language = lang_os_to_iso[os_lang]
  911.     end
  912.   else -- Windows
  913.     local lang_w = string.match(
  914.       os.setlocale("", "collate"),
  915.       "^[^_]+")
  916.     for i, v in ipairs(openSub.conf.languages) do
  917.      if v[2] == lang_w then
  918.       openSub.option.language = v[1]
  919.      end
  920.     end
  921.   end
  922. end
  923.  
  924. function apply_config()
  925. -- Apply user config selection to local config
  926.   local lg_sel = input_table['intLang']:get_value()
  927.   local sel_val
  928.   local opt
  929.   local sel_cf
  930.  
  931.   if lg_sel and lg_sel ~= 1
  932.   and openSub.conf.translations_avail[lg_sel] then
  933.     local lg = openSub.conf.translations_avail[lg_sel][1]
  934.     set_translation(lg)
  935.     SetDownloadBehaviours()
  936.   end
  937.  
  938.   for select_id, v in pairs(select_conf) do
  939.     if input_table[select_id]
  940.     and select_conf[select_id] then
  941.       sel_val = input_table[select_id]:get_value()
  942.       sel_cf = select_conf[select_id]
  943.       opt = sel_cf.opt
  944.      
  945.       if sel_val == 0 then
  946.         openSub.option[opt] = nil
  947.       else
  948.         openSub.option[opt] = sel_cf.cf[sel_val][1]
  949.       end
  950.      
  951.       set_default_option(select_id)
  952.     end
  953.   end
  954.  
  955.  
  956.   openSub.option.os_username = input_table['os_username']:get_text()
  957.   openSub.option.os_password = input_table['os_password']:get_text()
  958.  
  959.   if input_table["langExt"]:get_value() == 2 then
  960.     openSub.option.langExt = not openSub.option.langExt
  961.   end
  962.  
  963.   if input_table["removeTag"]:get_value() == 2 then
  964.     openSub.option.removeTag = not openSub.option.removeTag
  965.   end
  966.  
  967.   -- Set a custom working directory
  968.   local dir_path = input_table['dir_path']:get_text()
  969.   local dir_path_err = false
  970.   if trim(dir_path) == "" then dir_path = nil end
  971.  
  972.   if dir_path ~= openSub.conf.dirPath then
  973.     if openSub.conf.os == "lin"
  974.     or is_win_safe(dir_path)
  975.     or not dir_path then
  976.       local other_dirs = {}
  977.    
  978.       for path in
  979.       vlc.config.get(
  980.         "sub-autodetect-path"):gmatch("[^,]+"
  981.       ) do
  982.         path = trim(path)
  983.         if path ~= (openSub.conf.dirPath or "")..sub_dir then
  984.           table.insert(other_dirs, path)
  985.         end
  986.       end
  987.       openSub.conf.dirPath = dir_path
  988.       if dir_path then
  989.         table.insert(other_dirs,
  990.         string.gsub(dir_path, "^(.-)[\\/]?$", "%1")..sub_dir)
  991.        
  992.         if not is_dir(dir_path) then
  993.           mkdir_p(dir_path)
  994.         end
  995.        
  996.         openSub.conf.filePath = openSub.conf.dirPath..
  997.           slash.."vlsub_conf.xml"
  998.         openSub.conf.localePath = openSub.conf.dirPath..
  999.           slash.."locale"
  1000.       else
  1001.         openSub.conf.filePath = nil
  1002.         openSub.conf.localePath = nil
  1003.       end
  1004.       vlc.config.set(
  1005.         "sub-autodetect-path",
  1006.         table.concat(other_dirs, ", "))
  1007.     else
  1008.       dir_path_err = true
  1009.       setError(lang["mess_err_wrong_path"]..
  1010.         "<br><b>"..
  1011.         string.gsub(
  1012.           dir_path,
  1013.           "[^%:%w%p%s§¤]+",
  1014.           "<span style='color:#B23'>%1</span>"
  1015.         )..
  1016.         "</b>")
  1017.     end
  1018.   end
  1019.  
  1020.   if openSub.conf.dirPath and
  1021.   not dir_path_err then
  1022.     local config_saved = save_config()
  1023.     trigger_menu(1)
  1024.     if not config_saved then
  1025.       setError(lang["mess_err_conf_access"])
  1026.     end
  1027.   else
  1028.     setError(lang["mess_err_conf_access"])
  1029.   end
  1030. end
  1031.  
  1032. function save_config()
  1033. -- Dump local config into config file
  1034.   if openSub.conf.dirPath
  1035.   and openSub.conf.filePath then
  1036.     vlc.msg.dbg(
  1037.       "[VLSub] Saving config file:  "..
  1038.       openSub.conf.filePath)
  1039.    
  1040.     if file_touch(openSub.conf.filePath) then
  1041.       local tmpFile = assert(
  1042.         io.open(openSub.conf.filePath, "wb"))
  1043.       local resp = dump_xml(openSub.option)
  1044.       tmpFile:write(resp)
  1045.       tmpFile:flush()
  1046.       tmpFile:close()
  1047.       tmpFile = nil
  1048.     else
  1049.       return false
  1050.     end
  1051.     collectgarbage()
  1052.     return true
  1053.   else
  1054.     vlc.msg.dbg("[VLSub] Unable fount a suitable path "..
  1055.       "to save config, please set it manually")
  1056.     setError(lang["mess_err_conf_access"])
  1057.     return false
  1058.   end
  1059. end
  1060.  
  1061. function SetDownloadBehaviours()
  1062.   openSub.conf.downloadBehaviours = 'save'
  1063.   openSub.conf.downloadBehaviours = {
  1064.   --  {'save', lang["int_dowload_save"]}--,
  1065.  --   {'manual', lang["int_dowload_manual"]}
  1066.   }
  1067. end
  1068.  
  1069. function get_available_translations()
  1070. -- Get all available translation files from the internet
  1071. -- (drop previous direct download from github repo
  1072. -- causing error  with github https CA certficate on OS X an XP)
  1073. -- https://github.com/exebetche/vlsub/tree/master/locale
  1074.  
  1075.   local translations_url = "http://addons.videolan.org/CONTENT/"..
  1076.     "content-files/148752-vlsub_translations.xml"
  1077.  
  1078.   if input_table['intLangBut']:get_text() == lang["int_search_transl"]
  1079.   then
  1080.     openSub.actionLabel = lang["int_searching_transl"]
  1081.    
  1082.     local translations_content, lol = get(translations_url)
  1083.     local translations_avail = openSub.option.translations_avail
  1084.     all_trsl = parse_xml(translations_content)
  1085.     local lg, trsl
  1086.    
  1087.     for lg, trsl in pairs(all_trsl) do
  1088.       if lg ~= options.intLang[1]
  1089.       and not translations_avail[lg] then
  1090.         translations_avail[lg] = trsl_names[lg] or ""
  1091.         table.insert(openSub.conf.translations_avail, {
  1092.           lg,
  1093.           trsl_names[lg]
  1094.         })
  1095.         input_table['intLang']:add_value(
  1096.           trsl_names[lg],
  1097.           #openSub.conf.translations_avail)
  1098.       end
  1099.     end
  1100.    
  1101.     setMessage(success_tag(lang["mess_complete"]))
  1102.     collectgarbage()
  1103.   end
  1104. end
  1105.  
  1106. function set_translation(lg)
  1107.   openSub.option.translation = nil
  1108.   openSub.option.translation = {}
  1109.  
  1110.   if lg == 'eng' then
  1111.     for k, v in pairs(eng_translation) do
  1112.       openSub.option.translation[k] = v
  1113.     end
  1114.   else
  1115.     -- If translation file exists in /locale directory load it
  1116.     if openSub.conf.localePath
  1117.     and file_exist(openSub.conf.localePath..
  1118.       slash..lg..".xml") then
  1119.       local transl_file_path = openSub.conf.localePath..
  1120.       slash..lg..".xml"
  1121.       vlc.msg.dbg("[VLSub] Loading translation from file: "..
  1122.         transl_file_path)
  1123.       load_transl(transl_file_path)
  1124.       apply_translation()
  1125.     else
  1126.     -- Load translation file from internet
  1127.       if not all_trsl then
  1128.         get_available_translations()
  1129.       end
  1130.  
  1131.       if not all_trsl or not all_trsl[lg] then
  1132.         vlc.msg.dbg("[VLSub] Error, translation not found")
  1133.         return false
  1134.       end
  1135.       openSub.option.translation = all_trsl[lg]
  1136.       apply_translation()
  1137.       all_trsl = nil
  1138.     end
  1139.   end
  1140.  
  1141.   lang = nil
  1142.   lang = openSub.option.translation
  1143.   collectgarbage()
  1144. end
  1145.  
  1146.             --[[ Core ]]--
  1147.  
  1148. openSub = {
  1149.   itemStore = nil,
  1150.   actionLabel = "",
  1151.   conf = {
  1152.     url = "http://api.opensubtitles.org/xml-rpc",
  1153.     path = nil,
  1154.     userAgentHTTP = "VLSub",
  1155.     useragent = "VLSub 0.9",
  1156.     translations_avail = {},
  1157.     downloadBehaviours = nil,
  1158.     languages = languages
  1159.   },
  1160.   option = options,
  1161.   session = {
  1162.     loginTime = 0,
  1163.     token = ""
  1164.   },
  1165.   file = {
  1166.     hasInput = true,
  1167.     uri = nil,
  1168.     ext = nil,
  1169.     name = nil,
  1170.     path = nil,
  1171.     protocol = nil,
  1172.     cleanName = nil,
  1173.     dir = 'bug',
  1174.     hash = nil,
  1175.     bytesize = nil,
  1176.     fps = nil,
  1177.     timems = nil,
  1178.     frames = nil
  1179.   },
  1180.   movie = {
  1181.     title = "",
  1182.     seasonNumber = "",
  1183.     episodeNumber = "",
  1184.     sublanguageid = ""
  1185.   },
  1186.   request = function(methodName)
  1187.     local params = openSub.methods[methodName].params()
  1188.     local reqTable = openSub.getMethodBase(methodName, params)
  1189.     local request = "<?xml version='1.0'?>"..dump_xml(reqTable)
  1190.     local host, path = parse_url(openSub.conf.url)     
  1191.     local header = {
  1192.       "POST "..path.." HTTP/1.1",
  1193.       "Host: "..host,
  1194.       "User-Agent: "..openSub.conf.userAgentHTTP,
  1195.       "Content-Type: text/xml",
  1196.       "Content-Length: "..string.len(request),
  1197.       "",
  1198.       ""
  1199.     }
  1200.     request = table.concat(header, "\r\n")..request
  1201.    
  1202.     local response
  1203.     local status, responseStr = http_req(host, 80, request)
  1204.    
  1205.     if status == 200 then
  1206.       response = parse_xmlrpc(responseStr)
  1207.      
  1208.       if response then
  1209.         if response.status == "200 OK" then
  1210.           return openSub.methods[methodName]
  1211.             .callback(response)
  1212.         elseif response.status == "406 No session" then
  1213.           openSub.request("LogIn")
  1214.         elseif response then
  1215.           setError("code '"..
  1216.             response.status..
  1217.             "' ("..status..")")
  1218.           return false
  1219.         end
  1220.       else
  1221.         setError("Server not responding")
  1222.         return false
  1223.       end
  1224.     elseif status == 401 then
  1225.       setError("Request unauthorized")
  1226.       response = parse_xmlrpc(responseStr)
  1227.       if openSub.session.token ~= response.token then
  1228.         setMessage("Session expired, retrying")
  1229.         openSub.session.token = response.token
  1230.         openSub.request(methodName)
  1231.       end
  1232.       return false
  1233.     elseif status == 503 then
  1234.       setError("Server overloaded, please retry later")
  1235.       return false
  1236.     end
  1237.    
  1238.   end,
  1239.   getMethodBase = function(methodName, param)
  1240.     if openSub.methods[methodName].methodName then
  1241.       methodName = openSub.methods[methodName].methodName
  1242.     end
  1243.    
  1244.     local request = {
  1245.      methodCall={
  1246.       methodName=methodName,
  1247.       params={ param=param }}}
  1248.    
  1249.     return request
  1250.   end,
  1251.   methods = {
  1252.     LogIn = {
  1253.       params = function()
  1254.         openSub.actionLabel = lang["action_login"]
  1255.         return {
  1256.           { value={ string=openSub.option.os_username } },
  1257.           { value={ string=openSub.option.os_password } },
  1258.           { value={ string=openSub.movie.sublanguageid } },
  1259.           { value={ string=openSub.conf.useragent } }
  1260.         }
  1261.       end,
  1262.       callback = function(resp)
  1263.         openSub.session.token = resp.token
  1264.         openSub.session.loginTime = os.time()
  1265.         return true
  1266.       end
  1267.     },
  1268.     LogOut = {
  1269.       params = function()
  1270.         openSub.actionLabel = lang["action_logout"]
  1271.         return {
  1272.           { value={ string=openSub.session.token } }
  1273.         }
  1274.       end,
  1275.       callback = function()
  1276.         return true
  1277.       end
  1278.     },
  1279.     NoOperation = {
  1280.       params = function()
  1281.         openSub.actionLabel = lang["action_noop"]
  1282.         return {
  1283.           { value={ string=openSub.session.token } }
  1284.         }
  1285.       end,
  1286.       callback = function(resp)
  1287.         return true
  1288.       end
  1289.     },
  1290.     SearchSubtitlesByHash = {
  1291.       methodName = "SearchSubtitles",
  1292.       params = function()
  1293.         openSub.actionLabel = lang["action_search"]
  1294.         setMessage(openSub.actionLabel..": "..
  1295.           progressBarContent(0))
  1296.        
  1297.         return {
  1298.           { value={ string=openSub.session.token } },
  1299.           { value={
  1300.             array={
  1301.              data={
  1302.               value={
  1303.                struct={
  1304.                 member={
  1305.                  { name="sublanguageid", value={
  1306.                   string=openSub.movie.sublanguageid }
  1307.                   },
  1308.                  { name="moviehash", value={
  1309.                   string=openSub.file.hash } },
  1310.                  { name="moviebytesize", value={
  1311.                   double=openSub.file.bytesize } }
  1312.                   }}}}}}}
  1313.         }
  1314.       end,
  1315.       callback = function(resp)
  1316.         openSub.itemStore = resp.data
  1317.       end
  1318.     },
  1319.     SearchSubtitles = {
  1320.       methodName = "SearchSubtitles",
  1321.       params = function()
  1322.         openSub.actionLabel = lang["action_search"]
  1323.         setMessage(openSub.actionLabel..": "..
  1324.           progressBarContent(0))
  1325.                
  1326.         local member = {
  1327.              { name="sublanguageid", value={
  1328.               string=openSub.movie.sublanguageid } },
  1329.              { name="query", value={
  1330.               string=openSub.movie.title } } }
  1331.              
  1332.        
  1333.         if openSub.movie.seasonNumber ~= nil then
  1334.           table.insert(member, { name="season", value={
  1335.             string=openSub.movie.seasonNumber } })
  1336.         end
  1337.        
  1338.         if openSub.movie.episodeNumber ~= nil then
  1339.           table.insert(member, { name="episode", value={
  1340.             string=openSub.movie.episodeNumber } })
  1341.         end
  1342.        
  1343.         return {
  1344.           { value={ string=openSub.session.token } },
  1345.           { value={
  1346.             array={
  1347.              data={
  1348.               value={
  1349.                struct={
  1350.                 member=member
  1351.                   }}}}}}
  1352.         }
  1353.       end,
  1354.       callback = function(resp)
  1355.         openSub.itemStore = resp.data
  1356.       end
  1357.     },
  1358.     SearchSubtitles2 = {
  1359.       methodName = "SearchSubtitles",
  1360.       params = function()
  1361.         openSub.actionLabel = lang["action_search"]
  1362.         setMessage(openSub.actionLabel..": "..
  1363.           progressBarContent(0))
  1364.                
  1365.         local member = {
  1366.              { name="sublanguageid", value={
  1367.               string=openSub.movie.sublanguageid } },
  1368.              { name="tag", value={
  1369.               string=openSub.file.completeName } } }
  1370.        
  1371.         return {
  1372.           { value={ string=openSub.session.token } },
  1373.           { value={
  1374.             array={
  1375.              data={
  1376.               value={
  1377.                struct={
  1378.                 member=member
  1379.                   }}}}}}
  1380.         }
  1381.       end,
  1382.       callback = function(resp)
  1383.         openSub.itemStore = resp.data
  1384.       end
  1385.     }
  1386.   },
  1387.   getInputItem = function()
  1388.     return vlc.item or vlc.input.item()
  1389.   end,
  1390.   getFileInfo = function()
  1391.   -- Get video file path, name, extension from input uri
  1392.     local item = openSub.getInputItem()
  1393.     local file = openSub.file
  1394.     if not item then
  1395.       file.hasInput = true;
  1396.       file.cleanName = "tmp";
  1397.       file.protocol = "tmp";
  1398.       file.path = "/tmp";
  1399.       file.ext = ".mp4";
  1400.       file.uri = "tmp";
  1401.     else
  1402.       vlc.msg.dbg("[VLSub] Video URI: "..item:uri())
  1403.       local parsed_uri = vlc.net.url_parse(item:uri())
  1404.       file.uri = item:uri()
  1405.       file.protocol = parsed_uri["protocol"]
  1406.       file.path = parsed_uri["path"]
  1407.      
  1408.     -- Corrections
  1409.      
  1410.       -- For windows
  1411.       file.path = string.match(file.path, "^/(%a:/.+)$") or file.path
  1412.      
  1413.       -- For file in archive
  1414.       local archive_path, name_in_archive = string.match(
  1415.         file.path, '^([^!]+)!/([^!/]*)$')
  1416.  
  1417.       if archive_path and archive_path ~= "" then
  1418.         file.path = string.gsub(
  1419.           archive_path,
  1420.           '\063',
  1421.           '%%')
  1422.         file.path = vlc.strings.decode_uri(file.path)
  1423.         file.completeName = string.gsub(
  1424.           name_in_archive,
  1425.           '\063',
  1426.           '%%')
  1427.         file.completeName = vlc.strings.decode_uri(
  1428.           file.completeName)
  1429.         file.is_archive = true
  1430.       else -- "classic" input
  1431.         file.path = vlc.strings.decode_uri(file.path)
  1432.         file.dir = 'bug'
  1433.     file.completeName = ""
  1434.          
  1435.        
  1436.         local file_stat = vlc.net.stat(file.path)
  1437.         if file_stat
  1438.         then
  1439.           file.stat = file_stat
  1440.         end
  1441.        
  1442.         file.is_archive = false
  1443.       end
  1444.      
  1445.       file.name, file.ext = string.match(
  1446.         file.completeName,
  1447.         '^([^/]-)%.?([^%.]*)$')
  1448.      
  1449.       if file.ext == "part" then
  1450.         file.name, file.ext = string.match(
  1451.           file.name,
  1452.           '^([^/]+)%.([^%.]+)$')
  1453.       end
  1454.      
  1455.       file.hasInput = true;
  1456.       file.cleanName = string.gsub(
  1457.         file.name,
  1458.         "[%._]", " ")
  1459.       vlc.msg.dbg("[VLSub] file info "..(dump_xml(file)))
  1460.     end
  1461.     collectgarbage()
  1462.   end,
  1463.   getMovieInfo = function()
  1464.   -- Clean video file name and check for season/episode pattern in title
  1465.     if not openSub.file.name then
  1466.       openSub.movie.title = ""
  1467.       openSub.movie.seasonNumber = ""
  1468.       openSub.movie.episodeNumber = ""
  1469.       return false
  1470.     end
  1471.    
  1472.     local showName, seasonNumber, episodeNumber = string.match(
  1473.       openSub.file.cleanName,
  1474.       "(.+)[sS](%d%d)[eE](%d%d).*")
  1475.  
  1476.     if not showName then
  1477.       showName, seasonNumber, episodeNumber = string.match(
  1478.       openSub.file.cleanName,
  1479.       "(.+)(%d)[xX](%d%d).*")
  1480.     end
  1481.    
  1482.     if showName then
  1483.       openSub.movie.title = showName
  1484.       openSub.movie.seasonNumber = seasonNumber
  1485.       openSub.movie.episodeNumber = episodeNumber
  1486.     else
  1487.       openSub.movie.title = openSub.file.cleanName
  1488.       openSub.movie.seasonNumber = ""
  1489.       openSub.movie.episodeNumber = ""
  1490.     end
  1491.     collectgarbage()
  1492.   end,
  1493.   getMovieHash = function()
  1494.   -- Calculate movie hash
  1495.     openSub.actionLabel = lang["action_hash"]
  1496.     setMessage(openSub.actionLabel..": "..
  1497.       progressBarContent(0))
  1498.    
  1499.     local item = openSub.getInputItem()
  1500.    
  1501.     if not item then
  1502.       setError(lang["mess_no_input"])
  1503.       return false
  1504.     end
  1505.    
  1506.     openSub.getFileInfo()
  1507.      
  1508.     if not openSub.file.path then
  1509.       setError(lang["mess_not_found"])
  1510.       return false
  1511.     end
  1512.    
  1513.     local data_start = ""
  1514.     local data_end = ""
  1515.     local size
  1516.     local chunk_size = 65536
  1517.        
  1518.     -- Get data for hash calculation
  1519.     if openSub.file.is_archive then
  1520.       vlc.msg.dbg("[VLSub] Read hash data from stream")
  1521.    
  1522.       local file = vlc.stream(openSub.file.uri)
  1523.       local dataTmp1 = ""
  1524.       local dataTmp2 = ""
  1525.       size = chunk_size
  1526.      
  1527.       data_start = file:read(chunk_size)
  1528.      
  1529.       while data_end do
  1530.         size = size + string.len(data_end)
  1531.         dataTmp1 = dataTmp2
  1532.         dataTmp2 = data_end
  1533.         data_end = file:read(chunk_size)
  1534.         collectgarbage()
  1535.       end
  1536.       data_end = string.sub((dataTmp1..dataTmp2), -chunk_size)
  1537.     elseif not file_exist(openSub.file.path)
  1538.     and openSub.file.stat then
  1539.       vlc.msg.dbg("[VLSub] Read hash data from stream")
  1540.      
  1541.       local file = vlc.stream(openSub.file.uri)
  1542.      
  1543.       if not file then
  1544.         vlc.msg.dbg("[VLSub] No stream")
  1545.         return false
  1546.       end
  1547.      
  1548.       size = openSub.file.stat.size
  1549.       local decal = size%chunk_size
  1550.      
  1551.       data_start = file:read(chunk_size)
  1552.      
  1553.       -- "Seek" to the end
  1554.       file:read(decal)
  1555.      
  1556.       for i = 1, math.floor(((size-decal)/chunk_size))-2 do
  1557.         file:read(chunk_size)
  1558.       end
  1559.      
  1560.       data_end = file:read(chunk_size)
  1561.        
  1562.       file = nil
  1563.     else
  1564.       vlc.msg.dbg("[VLSub] Read hash data from file")
  1565.       local file = io.open( openSub.file.path, "rb")
  1566.       if not file then
  1567.         vlc.msg.dbg("[VLSub] No stream")
  1568.         return false
  1569.       end
  1570.      
  1571.       data_start = file:read(chunk_size)
  1572.       size = file:seek("end", -chunk_size) + chunk_size
  1573.       data_end = file:read(chunk_size)
  1574.       file = nil
  1575.     end
  1576.    
  1577.   -- Hash calculation
  1578.     local lo = size
  1579.     local hi = 0
  1580.     local o,a,b,c,d,e,f,g,h
  1581.     local hash_data = data_start..data_end
  1582.     local max_size = 4294967296
  1583.     local overflow
  1584.    
  1585.     for i = 1,  #hash_data, 8 do
  1586.       a,b,c,d,e,f,g,h = hash_data:byte(i,i+7)
  1587.       lo = lo + a + b*256 + c*65536 + d*16777216
  1588.       hi = hi + e + f*256 + g*65536 + h*16777216
  1589.      
  1590.       if lo > max_size then
  1591.         overflow = math.floor(lo/max_size)
  1592.         lo = lo-(overflow*max_size)
  1593.         hi = hi+overflow
  1594.       end
  1595.      
  1596.       if hi > max_size then
  1597.         overflow = math.floor(hi/max_size)
  1598.         hi = hi-(overflow*max_size)
  1599.       end
  1600.     end
  1601.    
  1602.     openSub.file.bytesize = size
  1603.     openSub.file.hash = string.format("%08x%08x", hi,lo)
  1604.     vlc.msg.dbg("[VLSub] Video hash: "..openSub.file.hash)
  1605.     vlc.msg.dbg("[VLSub] Video bytesize: "..size)
  1606.     collectgarbage()
  1607.     return true
  1608.   end,
  1609.   checkSession = function()
  1610.    
  1611.     if openSub.session.token == "" then
  1612.       openSub.request("LogIn")
  1613.     else
  1614.       openSub.request("NoOperation")
  1615.     end
  1616.   end
  1617. }
  1618.  
  1619. function searchHash()
  1620.   local sel = input_table["language"]:get_value()
  1621.   if sel == 0 then
  1622.     openSub.movie.sublanguageid = 'all'
  1623.   else
  1624.     openSub.movie.sublanguageid = openSub.conf.languages[sel][1]
  1625.   end
  1626.  
  1627.   openSub.getMovieHash()
  1628.  
  1629.   if openSub.file.hash then
  1630.     openSub.checkSession()
  1631.     openSub.request("SearchSubtitlesByHash")
  1632.     display_subtitles()
  1633.   end
  1634. end
  1635.  
  1636. function searchIMBD()
  1637.   openSub.movie.title = trim(input_table["title"]:get_text())
  1638.   openSub.movie.seasonNumber = tonumber(
  1639.     input_table["seasonNumber"]:get_text())
  1640.   openSub.movie.episodeNumber = tonumber(
  1641.     input_table["episodeNumber"]:get_text())
  1642.  
  1643.   local sel = input_table["language"]:get_value()
  1644.   if sel == 0 then
  1645.     openSub.movie.sublanguageid = 'all'
  1646.   else
  1647.     openSub.movie.sublanguageid = openSub.conf.languages[sel][1]
  1648.   end
  1649.  
  1650.   if openSub.movie.title ~= "" then
  1651.     openSub.checkSession()
  1652.     openSub.request("SearchSubtitles")
  1653.     display_subtitles()
  1654.   end
  1655. end
  1656.  
  1657. function display_subtitles()
  1658.   local mainlist = input_table["mainlist"]
  1659.   mainlist:clear()
  1660.  
  1661.   if openSub.itemStore == "0" then
  1662.     mainlist:add_value(lang["mess_no_res"], 1)
  1663.     setMessage("<b>"..lang["mess_complete"]..":</b> "..
  1664.       lang["mess_no_res"])
  1665.   elseif openSub.itemStore then
  1666.     for i, item in ipairs(openSub.itemStore) do
  1667.       mainlist:add_value(
  1668.       (item.SubFileName or "???")..
  1669.       " ["..(item.SubLanguageID or "?").."]"..
  1670.       " ("..(item.SubSumCD or "?").." CD)", i)
  1671.     end
  1672.     setMessage("<b>"..lang["mess_complete"]..":</b> "..
  1673.       #(openSub.itemStore).."  "..lang["mess_res"])
  1674.   end
  1675. end
  1676.  
  1677. function get_first_sel(list)
  1678.   local selection = list:get_selection()
  1679.   for index, name in pairs(selection) do
  1680.     return index
  1681.   end
  1682.   return 0
  1683. end
  1684.  
  1685. function download_subtitles()
  1686.   local index = get_first_sel(input_table["mainlist"])
  1687.  
  1688.   if index == 0 then
  1689.     setMessage(lang["mess_no_selection"])
  1690.     return false
  1691.   end
  1692.  
  1693.   openSub.actionLabel = lang["mess_downloading"]
  1694.  
  1695.   local item = openSub.itemStore[index]
  1696.  
  1697.   if openSub.option.downloadBehaviour == 'manual'
  1698.   or not openSub.file.hasInput then
  1699.     local link = "<span style='color:#181'>"
  1700.     link = link.."<b>"..lang["mess_dowload_link"]..":</b>"
  1701.     link = link.."</span> &nbsp;"
  1702.     link = link.."</span> &nbsp;<a href='"..
  1703.       item.ZipDownloadLink.."'>"
  1704.     link = link..item.MovieReleaseName.."</a>"
  1705.    
  1706.     setMessage(link)
  1707.     return false
  1708.   end
  1709.  
  1710.   local message = ""
  1711.   local subfileName = openSub.file.name or ""
  1712.  
  1713.   if openSub.option.langExt then
  1714.     subfileName = subfileName.."."..item.SubLanguageID
  1715.   end
  1716.  
  1717.   subfileName = subfileName.."."..item.SubFormat
  1718.   local tmp_dir
  1719.   local file_target_access = true
  1720.  
  1721.   if is_dir(openSub.file.dir) then
  1722.     tmp_dir = openSub.file.dir
  1723.   elseif openSub.conf.dirPath then
  1724.     tmp_dir = openSub.conf.dirPath
  1725.    
  1726.     message = "<br>"..error_tag(lang["mess_save_fail"].." &nbsp;"..
  1727.     "<a href='"..vlc.strings.make_uri(openSub.conf.dirPath).."'>"..
  1728.     lang["mess_click_link"].."</a>")
  1729.   else
  1730.     setError(lang["mess_save_fail"].." &nbsp;"..
  1731.     "<a href='"..item.ZipDownloadLink.."'>"..
  1732.     lang["mess_click_link"].."</a>")
  1733.     return false
  1734.   end
  1735.  
  1736.   local tmpFileURI, tmpFileName = dump_zip(
  1737.     item.ZipDownloadLink,
  1738.     tmp_dir,
  1739.     item.SubFileName)
  1740.  
  1741.   vlc.msg.dbg("[VLsub] tmpFileName: "..tmpFileName)
  1742.  
  1743.   -- Determine if the path to the video file is accessible for writing
  1744.  
  1745.   local target = openSub.file.dir..subfileName
  1746.  
  1747.   if not file_touch(target) then
  1748.     if openSub.conf.dirPath then
  1749.       target =  openSub.conf.dirPath..slash..subfileName
  1750.       message = "<br>"..
  1751.         error_tag(lang["mess_save_fail"].." &nbsp;"..
  1752.         "<a href='"..vlc.strings.make_uri(
  1753.           openSub.conf.dirPath).."'>"..
  1754.           lang["mess_click_link"].."</a>")
  1755.     else
  1756.       setError(lang["mess_save_fail"].." &nbsp;"..
  1757.       "<a href='"..item.ZipDownloadLink.."'>"..
  1758.       lang["mess_click_link"].."</a>")
  1759.       return false
  1760.     end
  1761.   end
  1762.  
  1763.   vlc.msg.dbg("[VLsub] Subtitles files: "..target)
  1764.  
  1765.   -- Unzipped data into file target
  1766.    
  1767.   local stream = vlc.stream(tmpFileURI)
  1768.   local data = ""
  1769.   local subfile = io.open(target, "wb")
  1770.  
  1771.   while data do
  1772.     subfile:write(data)
  1773.     data = stream:read(65536)
  1774.   end
  1775.  
  1776.   subfile:flush()
  1777.   subfile:close()
  1778.  
  1779.   stream = nil
  1780.   collectgarbage()
  1781.  
  1782.   if not os.remove(tmpFileName) then
  1783.     vlc.msg.err("[VLsub] Unable to remove temp: "..tmpFileName)
  1784.   end
  1785.    
  1786.   -- load subtitles
  1787.   if add_sub(target) then
  1788.     message = success_tag(lang["mess_loaded"]) .. message
  1789.   else
  1790.     message = error_tag(lang["mess_not_load"]) .. message
  1791.   end
  1792.  
  1793.   setMessage(message)
  1794. end
  1795.  
  1796. function dump_zip(url, dir, subfileName)
  1797.   -- Dump zipped data in a temporary file
  1798.   setMessage(openSub.actionLabel..": "..progressBarContent(0))
  1799.   local resp = get(url)
  1800.  
  1801.   if not resp then
  1802.     setError(lang["mess_no_response"])
  1803.     return false
  1804.   end
  1805.  
  1806.   local tmpFileName = dir..subfileName..".gz"
  1807.   if not file_touch(tmpFileName) then
  1808.     return false
  1809.   end
  1810.   local tmpFile = assert(io.open(tmpFileName, "wb"))
  1811.  
  1812.   tmpFile:write(resp)
  1813.   tmpFile:flush()
  1814.   tmpFile:close()
  1815.   tmpFile = nil
  1816.   collectgarbage()
  1817.   return "zip://"..make_uri(tmpFileName)
  1818.     .."!/"..subfileName, tmpFileName
  1819. end
  1820.  
  1821. function add_sub(subPath)
  1822.   if vlc.item or vlc.input.item() then
  1823.     subPath = decode_uri(subPath)
  1824.     vlc.msg.dbg("[VLsub] Adding subtitle :" .. subPath)
  1825.     return vlc.input.add_subtitle(subPath)
  1826.   end
  1827.   return false
  1828. end
  1829.  
  1830.             --[[ Interface helpers]]--
  1831.  
  1832. function progressBarContent(pct)
  1833.   local accomplished = math.ceil(
  1834.     openSub.option.progressBarSize*pct/100)
  1835.   local left = openSub.option.progressBarSize - accomplished
  1836.   local content = "<span style='background-color:#181;color:#181;'>"..
  1837.     string.rep ("-", accomplished).."</span>"..
  1838.     "<span style='background-color:#fff;color:#fff;'>"..
  1839.     string.rep ("-", left)..
  1840.     "</span>"
  1841.   return content
  1842. end
  1843.  
  1844. function setMessage(str)
  1845.   if input_table["message"] then
  1846.     input_table["message"]:set_text(str)
  1847.     dlg:update()
  1848.   end
  1849. end
  1850.  
  1851. function setError(mess)
  1852.   setMessage(error_tag(mess))
  1853. end
  1854.  
  1855. function success_tag(str)
  1856.   return "<span style='color:#181'><b>"..
  1857.   lang["mess_success"]..":</b></span> "..str..""
  1858. end
  1859.  
  1860. function error_tag(str)
  1861.   return "<span style='color:#B23'><b>"..
  1862.   lang["mess_error"]..":</b></span> "..str..""
  1863. end
  1864.  
  1865.             --[[ Network utils]]--
  1866.  
  1867. function get(url)
  1868.   local host, path = parse_url(url)
  1869.   local header = {
  1870.     "GET "..path.." HTTP/1.1",
  1871.     "Host: "..host,
  1872.     "User-Agent: "..openSub.conf.userAgentHTTP,
  1873.     "",
  1874.     ""
  1875.   }
  1876.   local request = table.concat(header, "\r\n")
  1877.    
  1878.   local response
  1879.   local status, response = http_req(host, 80, request)
  1880.  
  1881.   if status == 200 then
  1882.     return response
  1883.   else
  1884.     return false, status, response
  1885.   end
  1886. end
  1887.  
  1888. function http_req(host, port, request)
  1889.   local fd = vlc.net.connect_tcp(host, port)
  1890.   if not fd then return false end
  1891.   local pollfds = {}
  1892.  
  1893.   pollfds[fd] = vlc.net.POLLIN
  1894.   vlc.net.send(fd, request)
  1895.   vlc.net.poll(pollfds)
  1896.  
  1897.   local chunk = vlc.net.recv(fd, 2048)
  1898.   local response = ""
  1899.   local headerStr, header, body
  1900.   local contentLength, status
  1901.   local pct = 0
  1902.  
  1903.   --~ vlc.msg.err(headerStr)
  1904.  
  1905.   while chunk do
  1906.     response = response..chunk
  1907.   --~ vlc.msg.err("response", response)
  1908.     if not header then
  1909.         headerStr, body = response:match("(.-\r?\n)\r?\n(.*)")
  1910.         if headerStr then
  1911.             --~ vlc.msg.err(headerStr)
  1912.             response = body
  1913.             header = parse_header(headerStr)
  1914.             contentLength = tonumber(header["Content-Length"])
  1915.             status = tonumber(header["statuscode"])
  1916.         end
  1917.     end
  1918.  
  1919.     if contentLength then
  1920.         bodyLenght = #response
  1921.         pct = bodyLenght / contentLength * 100
  1922.         setMessage(openSub.actionLabel..": "..progressBarContent(pct))
  1923.       if bodyLenght >= contentLength then
  1924.         break
  1925.       end
  1926.     end
  1927.  
  1928.     vlc.net.poll(pollfds)
  1929.     chunk = vlc.net.recv(fd, 1024)
  1930.   end
  1931.  
  1932.   vlc.net.close(fd)
  1933.  
  1934.   if status == 301
  1935.   and header["Location"] then
  1936.     local host, path = parse_url(trim(header["Location"]))
  1937.     request = request
  1938.     :gsub("^([^%s]+ )([^%s]+)", "%1"..path)
  1939.     :gsub("(Host: )([^\n]*)", "%1"..host)
  1940.    
  1941.     return http_req(host, port, request)
  1942.   end
  1943.  
  1944.   return status, response
  1945. end
  1946.  
  1947. function parse_header(data)
  1948.   local header = {}
  1949.  
  1950.   for name, s, val in string.gmatch(
  1951.     data,
  1952.     "([^%s:]+)(:?)%s([^\n]+)\r?\n")
  1953.   do
  1954.     if s == "" then
  1955.     header['statuscode'] = tonumber(string.sub(val, 1 , 3))
  1956.     else
  1957.       header[name] = val
  1958.     end
  1959.   end
  1960.   return header
  1961. end
  1962.  
  1963. function parse_url(url)
  1964.   local url_parsed = vlc.net.url_parse(url)
  1965.   return  url_parsed["host"],
  1966.     url_parsed["path"],
  1967.     url_parsed["option"]
  1968. end
  1969.  
  1970.             --[[ XML utils]]--
  1971.  
  1972. function parse_xml(data)
  1973.   local tree = {}
  1974.   local stack = {}
  1975.   local tmp = {}
  1976.   local level = 0
  1977.   local op, tag, p, empty, val
  1978.   table.insert(stack, tree)
  1979.   local resolve_xml =  vlc.strings.resolve_xml_special_chars
  1980.  
  1981.   for op, tag, p, empty, val in string.gmatch(
  1982.     data,
  1983.     "[%s\r\n\t]*<(%/?)([%w:_]+)(.-)(%/?)>"..
  1984.     "[%s\r\n\t]*([^<]*)[%s\r\n\t]*"
  1985.   ) do
  1986.     if op=="/" then
  1987.       if level>0 then
  1988.         level = level - 1
  1989.         table.remove(stack)
  1990.       end
  1991.     else
  1992.       level = level + 1
  1993.       if val == "" then
  1994.         if type(stack[level][tag]) == "nil" then
  1995.           stack[level][tag] = {}
  1996.           table.insert(stack, stack[level][tag])
  1997.         else
  1998.           if type(stack[level][tag][1]) == "nil" then
  1999.             tmp = nil
  2000.             tmp = stack[level][tag]
  2001.             stack[level][tag] = nil
  2002.             stack[level][tag] = {}
  2003.             table.insert(stack[level][tag], tmp)
  2004.           end
  2005.           tmp = nil
  2006.           tmp = {}
  2007.           table.insert(stack[level][tag], tmp)
  2008.           table.insert(stack, tmp)
  2009.         end
  2010.       else
  2011.         if type(stack[level][tag]) == "nil" then
  2012.           stack[level][tag] = {}
  2013.         end
  2014.         stack[level][tag] = resolve_xml(val)
  2015.         table.insert(stack,  {})
  2016.       end
  2017.       if empty ~= "" then
  2018.         stack[level][tag] = ""
  2019.         level = level - 1
  2020.         table.remove(stack)
  2021.       end
  2022.     end
  2023.   end
  2024.  
  2025.   collectgarbage()
  2026.   return tree
  2027. end
  2028.  
  2029. function parse_xmlrpc(xmlText)
  2030.     local stack = {}
  2031.     local tree = {}
  2032.     local tmp, name = nil, nil
  2033.     table.insert(stack, tree)
  2034.     local FromXmlString =  vlc.strings.resolve_xml_special_chars
  2035.    
  2036.     local data_handle = {
  2037.         int = function(v) return tonumber(v) end,
  2038.         i4 = function(v) return tonumber(v) end,
  2039.         double = function(v) return tonumber(v) end,
  2040.         boolean = function(v) return tostring(v) end,
  2041.         base64 = function(v) return tostring(v) end, -- FIXME
  2042.         ["string"] = function(v) return FromXmlString(v) end
  2043.     }
  2044.    
  2045.    for c, label, empty, value
  2046.    in xmlText:gmatch("<(%/?)([%w_:]+)(%/?)>([^<]*)") do
  2047.    
  2048.         if c == ""
  2049.         then -- start tag
  2050.             if label == "struct"
  2051.             or label == "array" then
  2052.                 tmp = nil
  2053.                 tmp = {}
  2054.                 if name then
  2055.                     stack[#stack][name] = tmp
  2056.                 else
  2057.                     table.insert(stack[#stack], tmp)
  2058.                 end
  2059.                 table.insert(stack, tmp)
  2060.                 name = nil
  2061.             elseif label == "name" then
  2062.                 name = value
  2063.             elseif data_handle[label] then
  2064.                 if name then
  2065.                     stack[#stack][name] = data_handle[label](value)
  2066.                 else
  2067.                     table.insert(stack[#stack],
  2068.                     data_handle[label](value))
  2069.                 end
  2070.                 name = nil
  2071.             end
  2072.             if empty == "/"  -- empty tag
  2073.             and #stack>0
  2074.             and (label == "struct"
  2075.             or label == "array")
  2076.             then
  2077.                 table.remove(stack)
  2078.             end
  2079.         else -- end tag
  2080.             if #stack>0
  2081.             and (label == "struct"
  2082.             or label == "array")then
  2083.                 table.remove(stack)
  2084.             end
  2085.         end
  2086.     end
  2087.    
  2088.     return tree[1]
  2089. end
  2090.  
  2091. function dump_xml(data)
  2092.   local level = 0
  2093.   local stack = {}
  2094.   local dump = ""
  2095.   local convert_xml = vlc.strings.convert_xml_special_chars
  2096.  
  2097.   local function parse(data, stack)
  2098.     local data_index = {}
  2099.     local k
  2100.     local v
  2101.     local i
  2102.     local tb
  2103.    
  2104.     for k,v in pairs(data) do
  2105.       table.insert(data_index, {k, v})
  2106.       table.sort(data_index, function(a, b)
  2107.         return a[1] < b[1]
  2108.       end)
  2109.     end
  2110.    
  2111.     for i,tb in pairs(data_index) do
  2112.       k = tb[1]
  2113.       v = tb[2]
  2114.       if type(k)=="string" then
  2115.         dump = dump.."\r\n"..string.rep(
  2116.           " ",
  2117.           level)..
  2118.           "<"..k..">"  
  2119.         table.insert(stack, k)
  2120.         level = level + 1
  2121.       elseif type(k)=="number" and k ~= 1 then
  2122.         dump = dump.."\r\n"..string.rep(
  2123.           " ",
  2124.           level-1)..
  2125.           "<"..stack[level]..">"
  2126.       end
  2127.      
  2128.       if type(v)=="table" then
  2129.         parse(v, stack)
  2130.       elseif type(v)=="string" then
  2131.         dump = dump..(convert_xml(v) or v)
  2132.       elseif type(v)=="number" then
  2133.         dump = dump..v
  2134.       else
  2135.         dump = dump..tostring(v)
  2136.       end
  2137.      
  2138.       if type(k)=="string" then
  2139.         if type(v)=="table" then
  2140.           dump = dump.."\r\n"..string.rep(
  2141.             " ",
  2142.             level-1)..
  2143.             "</"..k..">"
  2144.         else
  2145.           dump = dump.."</"..k..">"
  2146.         end
  2147.         table.remove(stack)
  2148.         level = level - 1
  2149.        
  2150.       elseif type(k)=="number" and k ~= #data then
  2151.         if type(v)=="table" then
  2152.           dump = dump.."\r\n"..string.rep(
  2153.             " ",
  2154.             level-1)..
  2155.             "</"..stack[level]..">"
  2156.         else
  2157.           dump = dump.."</"..stack[level]..">"
  2158.         end
  2159.       end
  2160.     end
  2161.   end
  2162.   parse(data, stack)
  2163.   collectgarbage()
  2164.   return dump
  2165. end
  2166.  
  2167.             --[[ Misc utils]]--
  2168.  
  2169. function make_uri(str)
  2170.   str = str:gsub("\\", "/")
  2171.   local windowdrive = string.match(str, "^(%a:).+$")
  2172.   local encode_uri = vlc.strings.encode_uri_component
  2173.   local encodedPath = ""
  2174.   for w in string.gmatch(str, "/([^/]+)") do
  2175.     encodedPath = encodedPath.."/"..encode_uri(w)
  2176.   end
  2177.    
  2178.   if windowdrive then
  2179.     return "file:///"..windowdrive..encodedPath
  2180.   else
  2181.     return "file://"..encodedPath
  2182.   end
  2183. end
  2184.  
  2185. function file_touch(name) -- test write ability
  2186.   if not name or trim(name) == ""
  2187.   then return false end
  2188.  
  2189.   local f=io.open(name ,"w")
  2190.   if f~=nil then
  2191.     io.close(f)
  2192.     return true
  2193.   else
  2194.     return false
  2195.   end
  2196. end
  2197.  
  2198. function file_exist(name) -- test readability
  2199.   if not name or trim(name) == ""
  2200.   then return false end
  2201.   local f=io.open(name ,"r")
  2202.   if f~=nil then
  2203.     io.close(f)
  2204.     return true
  2205.   else
  2206.     return false
  2207.   end
  2208. end
  2209.  
  2210. function is_dir(path)
  2211.   if not path or trim(path) == ""
  2212.   then return false end
  2213.   -- Remove slash at the end or it won't work on Windows
  2214.   path = string.gsub(path, "^(.-)[\\/]?$", "%1")
  2215.   local f, _, code = io.open(path, "rb")
  2216.  
  2217.   if f then
  2218.     _, _, code = f:read("*a")
  2219.     f:close()
  2220.     if code == 21 then
  2221.       return true
  2222.     end
  2223.   elseif code == 13 then
  2224.     return true
  2225.   end
  2226.  
  2227.   return false
  2228. end
  2229.  
  2230. function list_dir(path)
  2231.   if not path or trim(path) == ""
  2232.   then return false end
  2233.   local dir_list_cmd
  2234.   local list = {}
  2235.   if not is_dir(path) then return false end
  2236.  
  2237.   if openSub.conf.os == "win" then
  2238.     dir_list_cmd = io.popen('dir /b "'..path..'"')
  2239.   elseif openSub.conf.os == "lin" then
  2240.     dir_list_cmd = io.popen('ls -1 "'..path..'"')
  2241.   end
  2242.  
  2243.   if dir_list_cmd then
  2244.     for filename in dir_list_cmd:lines() do
  2245.       if string.match(filename, "^[^%s]+.+$") then
  2246.         table.insert(list, filename)
  2247.       end
  2248.     end
  2249.     return list
  2250.   else
  2251.     return false
  2252.   end
  2253. end
  2254.  
  2255. function mkdir_p(path)
  2256.   if not path or trim(path) == ""
  2257.   then return false end
  2258.   if openSub.conf.os == "win" then
  2259.     os.execute('mkdir "' .. path..'"')
  2260.   elseif openSub.conf.os == "lin" then
  2261.     os.execute("mkdir -p '" .. path.."'")
  2262.   end
  2263. end
  2264.  
  2265. function decode_uri(str)
  2266.   return str:gsub("/", slash)
  2267. end
  2268.  
  2269. function is_window_path(path)
  2270.   return string.match(path, "^(%a:.+)$")
  2271. end
  2272.  
  2273. function is_win_safe(path)
  2274.   if not path or trim(path) == ""
  2275.   or not is_window_path(path)
  2276.   then return false end
  2277.   return string.match(path, "^%a?%:?[\\%w%p%s§¤]+$")
  2278. end
  2279.    
  2280. function trim(str)
  2281.   if not str then return "" end
  2282.   return string.gsub(str, "^[\r\n%s]*(.-)[\r\n%s]*$", "%1")
  2283. end
  2284.  
  2285. function remove_tag(str)
  2286.   return string.gsub(str, "{[^}]+}", "")
  2287. end
Add Comment
Please, Sign In to add comment