--[[ extension VLC "traduction" Copyright © 2010 Christophe Durville (Polyglotte http://polyglotte.tuxfamily.org/) Thanks to Ale5000 (http://ale5000.altervista.org/vlc/extensions/subtitles-mod.lua) and billig (http://linguae.stalikez.info) for code's inspiration ; and Ghislain for the tests... Auteur : Christophe Durville This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. --]] --[[ Changelog 0.1.5 : The dictionary format "ling" is now supported. 0.1.4 : subtitle file's directory does not need any more to be hard coded. Known issues : * OSD duration is too short. * Only uncompressed dict dictionaries are supported. 0.1.2 : debug. 0.1.1 : modif esthétique du fichier : rassemblement des variables paramètres. 0.1.0 : beta version. Known issues : * OSD duration is too short. * subtitle file's directory has to be hard-coded. * Only uncompressed dict dictionaries are supported. ]]-- --************************************************************************************************************************************** ---- PARAMÈTRES---PARAMETERS -- chemins à modifier pour attraper le bon dico -- attention il faut un fichier *.ling ou *.dict (sametypesequence = m), non compressé (pas de .dz). possibilité de trouver des dictionnaires sur http://polyglotte.tuxfamily.org/doku.php?id=donnees:dicos_bilingues et http://stardict.sourceforge.net/Dictionaries.php --path to modify to load the desired dictionary (*.ifo file or *.ling file) : it must have as neighbour a dict and idx file ; caution : dict.dz files are not yet recognized, you have to uncompress it. you can find such dictionaries here : http://stardict.sourceforge.net/Dictionaries.php chemindico = "/home/cdurpp/.stardict/dic/italien_italien-francais.ling/italien-francais.ling" --- langues du dico ---languages of the dictionary langue1 = "italien" langue2 = "français" -- nom des fichiers dans lesquels peuvent être ajoutés mot recherché ou entréesdéfinition (si ils n'existent pas, ils sont créés) + nom du répertoire dans lequel ils se trouvent (paramètres optionnels) -- names of the files where are saved the last definition (for future training with anki for example) or words (missing words to complete the dictionary for example...) . Optional parameters. nomanki = "memo-italien-francais.txt" nomfuturdico = "dico-italien-francais.txt" cheminecriture = "/home/cdurpp/multimedia/anki" --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --parmètres de localisation (pour avoir les menus dans la langue souhaitée) : par défaut c'est le français. --Localization parameters (default = french): Menusuivant = "Suivant" --to go to the next word of the subtitle Menuprecedent = "Précédent" --to go to the preceding word of the subtitle Menuecrireladefinition = "Écrire la définition" --write the definition (in a file) Menuecrirelemotrecherche = "Écrire le mot recherché" --write the searched word (in a file) Menuaproposdico = "À propos du dictionnaire" --about the dictionary... Menuaproposextension = "À propos de l'extension" --about the extension... --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --Vous n'avez pas à toucher aux paramètres suivants a priori. --You do not have to modify these following parameters a priori. --liste des caractères qui "séparent" les mots : peut être modifié suivant les langues (et les sous-titres) listesep={" ","%.",",",";",":","!","?","'",")","%(","-",'"',"\n","\t","\r",">","<","/","\\", " "} --**************************************************************************************************************************************** -- variables globales nomextension = "Dico" numversion = "0.1.5" soustitre = nil tableaust = {} tabledico = {} tempsref = nil motapproche = nil definition = nil mot = nil --fonctions pour hotkeys bindings = { [2228224] --[[ --> ]] = "avancer", [2162688] --[[ <-- ]] = "reculer", [2359296] --[[ flèche bas ]] = "ecrireanki", [2293760] --[[ flèche haut ]] = "ecrirefuturdico" } function avancer() local temps = recuperetemps() vlc.msg.info("temps="..temps) if temps ~= tempsref then tempsref = temps soustitre = recuperesoustitre(temps,tableaust) if soustitre == nil then vlc.osd.message("Pas de sous-titre !") numlexeme = 0 tempsref = 0 return false else nbmot = #soustitre vlc.msg.info(nbmot) numlexeme=1 end elseif numlexeme==nbmot then numlexeme=1 else numlexeme=numlexeme+1 end mot,motapproche,definition = recava(soustitre,numlexeme,mot,tabledico,chainedict) return true end function reculer() local temps = recuperetemps() vlc.msg.info("temps="..temps) if temps ~= tempsref then tempsref = temps soustitre = recuperesoustitre(temps,tableaust) nbmot = #soustitre if soustitre == nil then vlc.osd.message("Pas de sous-titre !") numlexeme = 0 tempsref = 0 return false else numlexeme=nbmot end elseif numlexeme==1 then numlexeme=nbmot else numlexeme=numlexeme-1 end mot,motapproche,definition = recava(soustitre,numlexeme,mot,tabledico,chainedict) return true end function recava(soustitre,numlexeme,mot,tabledico,chainedict) if soustitre == nil or #soustitre == 0 then afficher(" Pas de mot...") else mot = soustitre[numlexeme] vlc.msg.info(" Recherche de la définition...") definition, motapproche = rechercheentree(mot,tabledico,chainedict) vlc.msg.info(" Fin de la recherche.") if definition == nil then afficher(mot.." x ".."pas de définition") elseif motapproche == nil then afficher(mot.." :\n"..definition) motapproche = mot else vlc.msg.info(mot.." ~~>"..motapproche) afficher(mot.." ~~> "..motapproche.." :\n"..definition) --motapproche = mot end end return mot,motapproche,definition end function ecrireanki() local f = io.open(cheminecriture.."/"..nomanki, "a") if f == nil then vlc.msg.info("Pas de fichier texte (anki) de nom : "..cheminecriture.."/"..nomanki ) return false end if motapproche ~= nil and definition ~= nil then local def = string.gsub(definition,"\n","\\n") vlc.msg.info("écrit : "..motapproche.."\t"..def) f:write(motapproche.."\t"..def.."\n") else vlc.msg.info("Pas de définition à écrire." ) end f:close() return true end function ecrirefuturdico() local f = io.open(cheminecriture.."/"..nomfuturdico, "a") if f == nil then vlc.msg.info("Pas de fichier texte (futur dico) de nom : "..cheminecriture.."/"..nomfuturdico ) return false end if mot ~= nil then vlc.msg.info("écrit : "..mot) f:write(mot.."\n") else vlc.msg.info("Pas d'entrée à écrire." ) end f:close() return true end function action(func,delta) return { func = func, delta = delta or 0, last = 0, times = 0 } end actions = { ["avancer"] = action(avancer), ["reculer"] = action(reculer), ["ecrireanki"] = action(ecrireanki), ["ecrirefuturdico"] = action(ecrirefuturdico) } action = nil queue = {} function action_trigger( action ) vlc.msg.info("action_trigger:",tostring(action)) local a = actions[action] if a then a.func() else vlc.msg.err("Key `"..key.."' points to unknown action `"..bindings[key].."'.") end end function key_press( var, old, new, data ) local key = new vlc.msg.info("key_press:",tostring(key)) if bindings[key] then action_trigger(bindings[key]) else vlc.msg.err("Key `"..key.."' isn't bound to any action.") end end function get_title(str) local item = vlc.item or vlc.input.item() if not item then return "" end local metas = item:metas() if metas["title"] then return metas["title"] else local filename = string.gsub(item:name(), "^(.+)%.%w+$", "%1") --return trim(filename or item:name()) return filename or item:name() end end -- Remove leading and trailing spaces function trim(str) if not str then return "" end return string.gsub(str, "^%s*(.-)%s*$", "%1") end --------------------------------------------------- function descriptor() return { title = nomextension.." "..langue1.."-"..langue2; version = numversion; author = "hector"; url = 'http://polyglotte.tuxfamily.org'; description = "Extension permettant la traduction d'un mot d'un sous-titre"; shortdesc = ""; capabilities = {"menu"; "input-listener"--[[; "meta-listener"]]}; } end function menu() return {Menusuivant, Menuprecedent, Menuecrireladefinition, Menuecrirelemotrecherche, Menuaproposdico, Menuaproposextension} end function activer() vlc.msg.info( "Chargement de l'extension "..nomextension.." "..numversion.." ("..langue1.."-"..langue2..")" ) -- récupérer le nom du sous-titre, et le sous-titre nomfichier=get_title() cheminfilm = vlc.input.item():uri() vlc.msg.info( cheminfilm ) cheminsoustitre=string.gsub(string.gsub(cheminfilm, "^(.+)%.%w+$", "%1"),"file://","")..".srt" -- if nomsoustitresrt existe then ... vlc.msg.info( cheminsoustitre ) -- récupérer les : num / temps de début/fin et chaînes et mettre dans un tableau 2D vlc.msg.info( "récupération du sous-titre sous forme de tableau..." ) tableaust = lecturesrt(cheminsoustitre) if tableaust == false then vlc.msg.info( "fin") deactivate() return false end vlc.msg.info( "fin de récupération du sous-titre." ) -- charger le dico vlc.msg.info( "Chargement du dico..." ) tabledico, chainedict = chargementdico(chemindico) if tabledico == false then vlc.msg.info( "fin") deactivate() return false elseif tabledico == nil then vlc.msg.info("problème quelque part : table des entrées vide...") deactivate() return false elseif chainedict == nil then vlc.msg.info("problème quelque part : chaîne des définitions vide...") deactivate() return false end vlc.msg.info( "Fin de chargement du dico." ) vlc.var.add_callback( vlc.object.libvlc(), "key-pressed", key_press ) return true end function effacer() -- effacer l'OSD -- désaffecter la variable sous-titre soustitre = nil return true end function afficher(chaine) local longueurlignemax = 40 local nblignemax = 4 local nbligne = 1 local id local id0 = 1 local chaine2 = nil local taillechaine = string.len(chaine) local i = 0 local j = 0 local compteur = 0 while i < taillechaine do i = i + 1 if string.sub(chaine,i,i) == '\n' then nbligne = nbligne + 1 compteur = -1 if nbligne >= nblignemax + 1 then break end elseif compteur >= longueurlignemax then compteur = 0 nbligne = nbligne + 1 if nbligne >= nblignemax + 1 then break end chaine2 = chaine2..'\n' end --vlc.msg.info(string.byte(chaine,i)) if string.byte(chaine,i) <= 192 then -- 1 octet if chaine2 == nil then chaine2 = string.sub(chaine,i,i) else chaine2 = chaine2..string.sub(chaine,i,i) end elseif string.byte(chaine,i) <= 224 then -- 2 octets if chaine2 == nil then chaine2 = string.sub(chaine,i,i+1) else chaine2 = chaine2..string.sub(chaine,i,i+1) end i = i +1 elseif string.byte(chaine,i) <= 240 then -- 3 octets if chaine2 == nil then chaine2 = string.sub(chaine,i,i+2) else chaine2 = chaine2..string.sub(chaine,i,i+2) end i = i + 2 else -- 4 octets if chaine2 == nil then chaine2 = string.sub(chaine,i,i+3) else chaine2 = chaine2..string.sub(chaine,i,i+3) end i = i + 3 end compteur = compteur + 1 end --vlc.msg.info("#chaine="..string.len(chaine)) --vlc.msg.info("#chaine2="..string.len(chaine2)) --vlc.msg.info(chaine) --vlc.msg.info(chaine2) vlc.osd.message(chaine2) return true end function recuperesoustitre(temps,tableau) vlc.msg.info(#tableau) for i=1,#tableau do if tableau[i][1]>temps then return nil elseif tableau[i][1] <= temps and tableau[i][2] >= temps then return tableau[i][3] end end return nil end function lecturesrt(cheminsoustitre) vlc.msg.info(cheminsoustitre) local f = io.open(cheminsoustitre, "r") if f == nil then vlc.msg.info("Pas de fichier de sous-titres de chemin : "..cheminsoustitre ) return false end local chainest = f:read("*all") f:close() local ligne = nil local k=0 local tableau={} local a local drapeau local soustitre local p1 = 1 local p2 vlc.msg.info("lecturesrt1") while 1 do --vlc.msg.info("k="..k) p2 = string.find(chainest,'\n',p1) --vlc.msg.info(p2) if p2 == nil then --vlc.msg.info("break") break end ligne = string.sub(chainest,p1,p2-1) p1 = p2+1 if ligne ~= nil then a=string.find(ligne, "%-%-%>") end if a == nil then if tonumber(ligne)~=nil then elseif ligne == nil or string.len(ligne) <= 1 then -- ligne vide (caractère de fin de ligne) if drapeau == 1 then if soustitre ~= nil then listesoustitre = chaine2liste(soustitre) end if listesoustitre ~= nil then --vlc.msg.info("st"..k.."="..listesoustitre[1]) tableau[k] = {tempsdebut,tempsfin,listesoustitre} end soustitre = nil drapeau = 0 end else -- ligne de sous-titre if soustitre == nil then soustitre = string.lower(ligne) else soustitre = soustitre.." "..string.lower(ligne) end end else -- ligne de temps --vlc.msg.info("temps") tempsdebut, tempsfin = lignesrt2temps(ligne) k = k + 1 drapeau = 1 end end if soustitre ~= nil then listesoustitre = chaine2liste(soustitre) end if listesoustitre ~= nil then tableau[k] = {tempsdebut,tempsfin,listesoustitre} end return tableau end function chargementdico(chemindico) if string.sub(chemindico,-5) == ".ling" then -- dico ling tabledico, chainedict =chargementdicoling(chemindico) elseif string.sub(chemindico,-4) == ".ifo" then --dico stardict/m tabledico, chainedict =chargementdicostardictm(chemindico) end return tabledico, chainedict end function chargementdicoling(chemindico) local entree local i=0 vlc.msg.info("La lecture des dictionnaires ling n'est pas encore possible.") f = io.open(chemindico, "r") if f == nil then vlc.msg.info("Pas de fichier ling (dico) de nom : "..chemindico) return false end local chaine = f:read("*all") f:close() local offsetblocentree = str32tonum(string.sub(chaine, 23,26)) local tailleblocentree = str32tonum(string.sub(chaine, 27,30)) local blocentree = string.sub(chaine, offsetblocentree+1, offsetblocentree+tailleblocentree) local offsetblocCN = str32tonum(string.sub(chaine, 39,42)) local tailleblocCN = str32tonum(string.sub(chaine, 43,46)) local blocCN = string.sub(chaine, offsetblocCN+1, offsetblocCN+tailleblocCN) local offsetblocNotice = str32tonum(string.sub(chaine, 47,50)) local tailleblocNotice = str32tonum(string.sub(chaine, 51,54)) local blocNotice = string.sub(chaine, offsetblocNotice+1, offsetblocNotice+tailleblocNotice) local p1 = 1 local p2 = 0 while (1) do p2 = string.find(blocentree,'\0',p1) if p2 == nil then vlc.msg.info("break") break end i = i + 1 entree = string.sub(blocentree,p1,p2-1) -- entrée offsetnotice = str32tonum(string.sub(blocCN,(i-1)*8+1, i*8+4)) taillenotice = str32tonum(string.sub(blocCN,(i-1)*8+5, (i-1)*8+8)) definition = string.sub(blocNotice, offsetnotice+1, offsetnotice+taillenotice) q = string.find(definition,'\0') definition = string.sub(definition,1,q-1) tabledico[i] = {string.lower(entree),offsetnotice,taillenotice} p1 = p2 + 1 end table.sort(tabledico, function(a,b) return a[1] tableaudico[#tableaudico][1])and(i==#tableaudico))then local h = 0 for j = -card,card do if i + j > 1 and i + j < #tableaudico then h = h + 1 ind[h] = i+j tab[h] = tableaudico[i+j][1] end end distance = levenshteinL(mot, tab) distmini, indice = mini(distance) k = ind[indice] break end end for h = 1, #ind do vlc.msg.info( ind[h].." : "..tab[h].." ==> "..distance[h] ) end decalage = tableaudico[k][2] long = tableaudico[k][3] motapproche = tableaudico[k][1] definition = string.sub(chainedict, decalage+1, decalage + long) vlc.msg.info( mot.."~~>"..motapproche.."-->"..definition ) return definition, motapproche end function mini(tableau) local k = {} local indice local j = 0 local min = 100000 local minind = 100000 local milieu = math.floor(#tableau/2) for i = 1 , #tableau do if tableau[i] < min then min = tableau[i] k={} j = 1 k[j] = i elseif tableau[i] == min then j=j+1 k[j] = i end end for i = 1 , #k do if math.abs(k[i] - milieu) 1 then i = i + 1 mmot[i] = a end curseur = value + 1 end return mmot end function aproposextension() afficher("Extension VLC"..nomextension.." "..numversion.."\nProjet Polyglotte :\n http://polyglotte.tuxfamily.org") return true end function aproposdico() afficher("Dico...") return true end -- Function triggered when a element from the menu is selected function trigger_menu(id) if(id == 1) then avancer() elseif(id == 2) then reculer() elseif(id == 3) then ecrireanki() elseif(id == 4) then ecrirefuturdico() elseif(id == 5) then aproposdico() elseif(id == 6) then aproposextension() end return false end -- Function triggered when the extension is activated function activate() activer() return true end -- Function triggered when the dialog is closed function close() soustitre = nil tableau = nil return true end -- Function triggered when the extension is deactivated function deactivate() -- Clean up vlc.var.del_callback( vlc.object.libvlc(), "key-pressed", key_press ) return true end