diff --git a/.gitignore b/.gitignore index ec76ec2..2af8ad6 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ backup + +*.pyc diff --git a/fr-FR.dic b/fr-FR.dic index a9263e4..9cbf465 100755 --- a/fr-FR.dic +++ b/fr-FR.dic @@ -1,3 +1,5 @@ +œ=oe + qq=quelques framasoft=framassofte s'est=sé @@ -6,9 +8,92 @@ c'est=sé /= web=ouèbe est-il=étil -plus=pluse irc=IRC twitter=twitteur forker=forké NAT=nate DHCP=d h c p + + +primtux=primetuxe +linux=linuxe +faisant=feusan +peuvent être=peuvent taitre + +ubuntu=oubountou +inkscape=inksquèipe +tout à fait=toute a fait + +handymenu=handymeunu +live=laïeve +dvd=dévédé +usb= u s b +pdf= p d f +odt= o d t +bne= b n e +Windows=wouine doze +internet=internette +debian=débianne +wine=ouaïne +puzzle=peuzelle +menu=meunu +wiki=ouiki +html= h t m l +css= c s s +abuledu=abulédu +qwant=kouante +ctparental=c t parental +google=gougueulle +firefox=faïrefoxe +lxpanel=l x panel +lightdm=laïte d m +openboard=open borde +gimp=guymme pe +gpl=g p l +admin=adminne +bios=biosse +crm=c r m +dll=d l l +dsi=d s i +erp=e r p +firmware=firmewère +geek=gui ke +hacker=haqueur +hardware=hardwère +ihm=i h m +jit=jite +malware=malwère +p2p=pire tou pire +phishing=fichine gue +ram=rame +boot=boute +download= +upload=eupe lo oude +vm=machine virtuelle + +sac=saque +parc=parque +Marc=marque + +lapin=lapun + +bouche=bou che +puis=pu is + +elles ont=elle zont + +plus=pluse +ne pluse=ne plus + +tu es=tu, ai + +choeur=coeur + +soeur=seure + +l'api=la p i +une api=une a p i + +coq=coque + est = ai + es = ai diff --git a/gSpeech.py b/gSpeech.py index 8a84d23..0cc8bc7 100755 --- a/gSpeech.py +++ b/gSpeech.py @@ -19,6 +19,9 @@ localdir = os.path.abspath(SCRIPT_DIR) + "/locale" gettext.install(APPNAME, localdir) +from speech.debug import is_debug_mode +from speech.dic import replace + ######################### # Application info ICON = os.path.join(SCRIPT_DIR, 'icons', APPNAME + '.svg') @@ -80,7 +83,7 @@ # Main Class class MainApp: """ the main class of the software """ - def __init__(self, config): + def __init__(self): # init app name in notification Notify.init('gSpeech') # define speech language @@ -89,8 +92,6 @@ def __init__(self, config): #~ self.icon = APPNAME + '-' + self.lang self.icon = ICON - self.config = config - if IsAppIndicator == True : self.ind =appindicator.Indicator.new(APPNAME, self.icon, appindicator.IndicatorCategory.APPLICATION_STATUS) self.ind.set_status (appindicator.IndicatorStatus.ACTIVE) @@ -121,14 +122,14 @@ def __init__(self, config): button = Gtk.Button() button.set_image(Gtk.Image.new_from_stock(Gtk.STOCK_EXECUTE,Gtk.IconSize.MENU)) - button.set_label(_("Read clipboard content")) + button.set_label(_(u"Read clipboard content")) button.connect("clicked", self.onExecute) button.add_accelerator("clicked",self.accelgroup , ord('c'), Gdk.ModifierType.SHIFT_MASK, Gtk.AccelFlags.VISIBLE) hbox.pack_start(button, False, False,0) button = Gtk.Button() button.set_image(Gtk.Image.new_from_stock(Gtk.STOCK_EXECUTE, Gtk.IconSize.MENU)) - button.set_label(_("Read selected text")) + button.set_label(_(u"Read selected text")) button.connect("clicked", self.onExecute) button.add_accelerator("clicked",self.accelgroup , ord('x'),Gdk.ModifierType.SHIFT_MASK, Gtk.AccelFlags.VISIBLE) hbox.pack_start(button, False, False,0) @@ -186,32 +187,32 @@ def onRightClick(self, icon=None, event_button=None, event_time=None): menu = Gtk.Menu() # Execute menu item : execute speeching from Desktop clipboard - rmItem = Gtk.MenuItem.new_with_label(_("Read clipboard content")) + rmItem = Gtk.MenuItem.new_with_label(_(u"Read clipboard content")) rmItem.connect('activate', self.onExecute) rmItem.show() menu.append(rmItem) # Execute menu item : execute speeching from X.org clipboard - rmItem = Gtk.MenuItem.new_with_label(_("Read selected text")) + rmItem = Gtk.MenuItem.new_with_label(_(u"Read selected text")) rmItem.connect('activate', self.onExecute) rmItem.show() menu.append(rmItem) # Play item menu - self.MenuPlayPause = Gtk.CheckMenuItem.new_with_label(_("Pause")) + self.MenuPlayPause = Gtk.CheckMenuItem.new_with_label("Pause") self.MenuPlayPause.set_active(False) self.MenuPlayPause.connect('toggled', self.onPlayPause) self.MenuPlayPause.show() menu.append(self.MenuPlayPause) # Stop item menu - rmItem = Gtk.MenuItem.new_with_label(_("Stop")) + rmItem = Gtk.MenuItem.new_with_label("Stop") rmItem.connect('activate', self.onStop) rmItem.show() menu.append(rmItem) # Save item menu - rmItem = Gtk.MenuItem.new_with_label(_("Save")) + rmItem = Gtk.MenuItem.new_with_label("Save") rmItem.connect('activate', self.onSave) rmItem.show() menu.append(rmItem) @@ -221,7 +222,7 @@ def onRightClick(self, icon=None, event_button=None, event_time=None): rmItem.show() menu.append(rmItem) - mediawin = Gtk.MenuItem.new_with_label(_("Multimedia window")) + mediawin = Gtk.MenuItem.new_with_label(_(u"Multimedia window")) mediawin.connect('activate', self.onMediaDialog) mediawin.show() menu.append(mediawin) @@ -264,25 +265,19 @@ def onRightClick(self, icon=None, event_button=None, event_time=None): menu.append(rmItem) ## Reload item menu - item = Gtk.MenuItem.new_with_label(_("Refresh")) + item = Gtk.MenuItem.new_with_label("Refresh") item.connect('activate', self.onReload) item.show() menu.append(item) # About item menu : show About dialog - about = Gtk.MenuItem.new_with_label(_("About")) + about = Gtk.MenuItem.new_with_label("About") about.connect('activate', self.onAbout) about.show() menu.append(about) - # Preferences - options = Gtk.MenuItem.new_with_label(_("Options")) - options.connect('activate', self.onOptions) - options.show() - menu.append(options) - # Quit item menu - item = Gtk.MenuItem.new_with_label(_("Quit")) + item = Gtk.MenuItem.new_with_label("Quit") item.connect('activate', self.destroy) item.show() menu.append(item) @@ -326,10 +321,7 @@ def onLang(self, widget, lng): # show about dialog def onAbout(self, widget): self.aboutdiag = AboutDialog() - - # show options dialog - def onOptions(self, widget): - self.options_dialog = OptionDialog(self.config) + self.aboutdiag # show multimedia control dialog def onMediaDialog(self, widget): @@ -358,42 +350,33 @@ def onLeftClick(self, widget, data=None): # on Execute item function : execute speech def onExecute(self, widget, data=None): - if widget.get_label() == _("Read selected text") : + if widget.get_label() == _(u"Read selected text") : text = Gtk.Clipboard.get(Gdk.SELECTION_PRIMARY).wait_for_text() else : text = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD).wait_for_text() - if text == None: - if self.config.show_notification: - try: - Notify.Notification.new(APPNAME, _("No text selected."), self.icon).show() - except: - pass - else : - if self.config.show_notification: - try: - Notify.Notification.new(APPNAME, _(u"I'm reading the text. One moment please."), self.icon).show() - except: - pass + if text == None : + try: + Notify.Notification.new(APPNAME, _(u"No text selected."), self.icon).show() + except: + pass - #~ text = text.lower() + else : + try: + Notify.Notification.new(APPNAME, _(u"I'm reading the text. One moment please."), self.icon).show() + except: + pass + + CONF_DIR = '.' + if not is_debug_mode(): + CONF_DIR = join(expanduser('~'), '.config/gSpeech') text = text.replace('\"', '') text = text.replace('`', '') text = text.replace('´', '') text = text.replace('-','') - lngDict = CONFIGDIR + '/' + self.lang + '.dic' - - if os.path.exists(lngDict) : - for line in open(lngDict,'r').readlines(): - - bad = line.split('=')[0] - #~ bad = bad.lower() - try : - good = line.split('=')[1] - except : - good = ' ' - text = text.replace(bad, good) + dict_path = CONF_DIR + '/' + self.lang + '.dic' + text = replace(dict_path, text) if len(text) <= 32768: os.system('pico2wave -l %s -w %s \"%s\" ' % ( self.lang, SPEECH, text )) @@ -507,33 +490,12 @@ def __init__(self): dialog.connect("response", lambda self, *f: self.destroy()) dialog.show_all() -class OptionDialog: - """ the options dialog class """ - def __init__(self, config): - dialog = Gtk.Dialog( - APPNAME, - None, - Gtk.DialogFlags.MODAL| Gtk.DialogFlags.DESTROY_WITH_PARENT - ) - dialog.set_name(APPNAME) - dialog.set_border_width(10) - hbox = Gtk.HBox() - notification_check = Gtk.CheckButton(_("Active notification")) - notification_check.set_active(config.show_notification) - notification_check.connect("toggled", self.on_checked, config) - hbox.add(notification_check) - dialog.vbox.pack_start(hbox, False, False, 0) - dialog.show_all() - - def on_checked(self, checkBox, config): - config.show_notification = checkBox.get_active() - config.update() # Audio speech wav file class saving class SaveFile: """ the class to save the speech .wav file """ def __init__(self): - dialog = Gtk.FileChooserDialog(_("Save the speech"), + dialog = Gtk.FileChooserDialog(_(u"Save the speech"), None, Gtk.FileChooserAction.SAVE, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, @@ -579,45 +541,10 @@ def IniRead(configfile, section, key, default): else : return var -class Config: - _path = '' - use_appindicator = True - default_language = '' - section = 'CONFIGURATION' - show_notification = True - - def __init__(self, path): - self._path = path - raw = ConfigParser.ConfigParser() - raw.read(self._path) - if raw.has_option(self.section, 'shownotification'): - self.show_notification = raw.getboolean( - self.section, - 'shownotification' - ) - - def update(self): - raw = ConfigParser.RawConfigParser() - raw.add_section(self.section) - raw.set( - self.section, - 'USEAPPINDICATOR', - self.use_appindicator - ) - raw.set( - self.section, - 'DEFAULTLANGUAGE', - self.default_language - ) - raw.set( - self.section, - 'SHOWNOTIFICATION', - self.show_notification - ) - with open(self._path, 'wb') as configfile: - raw.write(configfile) if __name__ == "__main__": + if is_debug_mode(): + print('DEBUG MODE') # is PID exists? if os.path.isfile(PID): # yes. read it @@ -647,15 +574,15 @@ def update(self): if not os.path.isdir(CONFIGDIR) : os.mkdir(CONFIGDIR, 0775) - CONFIGFILE = os.path.join(CONFIGDIR, 'gspeech.conf') + CONFIGFILE = os.path.join(CONFIGDIR,'gspeech.conf') if not os.path.isfile(CONFIGFILE) : - raw = ConfigParser.RawConfigParser() - raw.add_section('CONFIGURATION') - raw.set('CONFIGURATION', 'USEAPPINDICATOR', 'True') - raw.set('CONFIGURATION', 'DEFAULTLANGUAGE', '') + config = ConfigParser.RawConfigParser() + config.add_section('CONFIGURATION') + config.set('CONFIGURATION', 'USEAPPINDICATOR', 'True') + config.set('CONFIGURATION', 'DEFAULTLANGUAGE', '') #~ config.set('CONFIGURATION', 'SHOWMEDIADIALOG', 'False') with open(CONFIGFILE, 'wb') as configfile: - raw.write(configfile) + config.write(configfile) IsAppIndicator = bool(IniRead(CONFIGFILE, 'CONFIGURATION', 'USEAPPINDICATOR', 'True' )) @@ -675,9 +602,5 @@ def update(self): IsAppIndicator = False - config = Config(CONFIGFILE) - config.use_appindicator = IsAppIndicator - config.default_language = DefaultLang - gSpeech = MainApp(config) + gSpeech = MainApp() gSpeech.main() - diff --git a/gspeech-cli b/gspeech-cli new file mode 100755 index 0000000..64697f7 --- /dev/null +++ b/gspeech-cli @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- + +import sys +import os +from os.path import join, expanduser + +from speech import __version__ +from speech.debug import is_debug_mode +from speech.conf import Conf, LISTLANG +from speech.dic import replace + +if __name__ == "__main__": + if len(sys.argv) <= 1: + print("first argument is mising for the text to read") + exit(0) + text = sys.argv[1] + conf = Conf() + if text in ['--version']: + print('%s version %s' % (conf.app_name, __version__)) + exit(0) + if text in ['--help', '-h']: + print('%s version %s' % (conf.app_name, __version__)) + print('') + print('Usage : %s-cli "[text to read]" [.wav path]...[optionnal lang]') + print('') + print('Common flags:') + print(' -h --help show usage information') + print(' --version show version information') + exit(0) + if len(sys.argv) == 2: + print("a secong argument is missing : .wav path") + exit(0) + path = sys.argv[2] + if len(sys.argv) > 3 and sys.argv[3] in LISTLANG: + conf.lang = sys.argv[3] + + text = text.replace('\"', '') + text = text.replace('`', '') + text = text.replace('´', '') + text = text.replace('-','') + + text = replace(conf.dict_path, text) + if len(text) <= 32768: + cmd = 'pico2wave -l %s -w %s \"%s\" ' % ( conf.lang, path, text ) + if is_debug_mode(): + print(cmd) + os.system(cmd) + + elif os.path.isfile('/usr/bin/sox'): + discours = text.split('\n\n') + cmds = [] + names = [] + text = '' + for idx,paragraph in enumerate(discours): + text += paragraph + if idx == len(discours)-1 or len(text) + len(discours[idx+1]) >= 32767: + filename = conf.cache + 'speech' + str(idx) + '.wav' + cmds.append('pico2wave -l %s -w %s \"%s\" ' % ( conf.lang, filename, text )) + names.append(filename) + text = '' + + nproc = int(.5 * multiprocessing.cpu_count()) + if nproc == 0: + nproc = 1 + multiprocessing.Pool(nproc).map(os.system, cmds) + os.system('sox %s %s' % ( ' '.join(names), path )) + for fichier in names: + os.remove(fichier) diff --git a/speech/__init__.py b/speech/__init__.py new file mode 100644 index 0000000..a655b90 --- /dev/null +++ b/speech/__init__.py @@ -0,0 +1,2 @@ +VERSION = (0, 7, 0) +__version__ = ".".join(map(str, VERSION)) diff --git a/speech/conf.py b/speech/conf.py new file mode 100644 index 0000000..94c537f --- /dev/null +++ b/speech/conf.py @@ -0,0 +1,80 @@ +try: + from configparser import RawConfigParser, SafeConfigParser +except: + from ConfigParser import RawConfigParser, SafeConfigParser + +from os import getenv, environ, mkdir +from os.path import join, isfile, isdir + +from .debug import is_debug_mode + +# Supported SVOX Pico's languages +LISTLANG = ["de-DE", "en-GB", "en-US", "es-ES", "fr-FR", "it-IT"] + + +def ini_read(configfile, section, key, default): + if isfile(configfile): + parser = SafeConfigParser() + parser.read(configfile) + try: + var = parser.get( section , key ) + except: + var = default + else: + var = default + + if var.lower() in ['1', 'yes', 'true', 'on'] : + return True + elif var.lower() in ['0', 'no', 'false', 'off'] : + return False + else : + return var + + +class Conf: + app_name = "gSpeech" + # Temporaries files + cache_path = join(getenv('HOME'), '.cache', app_name) + + def __init__(self, script_dir=None): + self.dir = '.' + if is_debug_mode(): + print('DEBUG MODE') + else: + self.dir = join(expanduser('~'), '.config/gSpeech') + if not isdir(conf.dir): + mkdir(conf.dir, '0775') + + self.path = join(self.dir, 'gspeech.conf') + if not isfile(self.path): + config = RawConfigParser() + config.add_section('CONFIGURATION') + config.set('CONFIGURATION', 'USEAPPINDICATOR', 'True') + config.set('CONFIGURATION', 'DEFAULTLANGUAGE', '') + #~ config.set('CONFIGURATION', 'SHOWMEDIADIALOG', 'False') + with open(self.path, 'wb') as stream: + config.write(stream) + + self.has_app_indicator = bool(ini_read( + self.path, 'CONFIGURATION', 'USEAPPINDICATOR', 'True' ) + ) + + lang = str(ini_read( + self.dir, 'CONFIGURATION', 'DEFAULTLANGUAGE', '' + )) + self.lang = lang[:2] + '-' + lang[3:][:2] + # if SVOX Pico not support this language, find os environment language + if not self.lang in LISTLANG: + self.lang = environ.get('LANG', 'en_US')[:2] + '-' + environ.get('LANG', 'en_US')[3:][:2] + # if SVOX Pico not support this language, use US english + if not self.lang in LISTLANG: + self.lang = "en-US" + + if script_dir: + self.icon = join(script_dir, 'icons', self.app_name + '.svg') + self.lang_icon = join( + script_dir, + 'icons', + self.app_name + '-' + self.lang + '.svg' + ) + self.dict_path = join(self.dir, '%s.dic' % self.lang) diff --git a/speech/debug.py b/speech/debug.py new file mode 100755 index 0000000..b037094 --- /dev/null +++ b/speech/debug.py @@ -0,0 +1,6 @@ +from os.path import isdir + +def is_debug_mode(): + if isdir('.git'): + return True + return False diff --git a/speech/dic.py b/speech/dic.py new file mode 100755 index 0000000..636de46 --- /dev/null +++ b/speech/dic.py @@ -0,0 +1,12 @@ +from os.path import exists + +def replace(dict_path, text): + if not exists(dict_path): + return text + for line in open(dict_path, 'r').readlines(): + bad = line.split('=')[0] + if line.find('=') == -1: + continue + good = line.split('=')[1].replace('\n', '') + text = text.replace(bad, good) + return text