Hello community, here is the log from the commit of package python-gTTS for openSUSE:Factory checked in at 2020-04-07 10:30:44 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-gTTS (Old) and /work/SRC/openSUSE:Factory/.python-gTTS.new.3248 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-gTTS" Tue Apr 7 10:30:44 2020 rev:5 rq:791751 version:2.1.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-gTTS/python-gTTS.changes 2019-09-27 14:47:05.064977577 +0200 +++ /work/SRC/openSUSE:Factory/.python-gTTS.new.3248/python-gTTS.changes 2020-04-07 10:32:08.330562485 +0200 @@ -1,0 +2,14 @@ +Mon Apr 6 09:24:58 UTC 2020 - Marketa Calabkova <mcalabk...@suse.com> + +- update to 2.1.1 + * Debug mode now uses a copy of locals() to prevent RuntimeError (`#213 <https://github.com/pndurette/gTTS/issues/213>`_) + * Added the ability to customize the Google Translate URL hostname. + This is useful when ``google.com`` might be blocked within a network but + a local or different Google host (e.g. ``google.cn``) is not + * Pre-generated TTS API request URLs can now be obtained instead of + writing an ``mp3`` file to disk (for example to be used in an + external program) + * New ``--tld`` option to match the new ``gtts`` customizable hostname + * Added Python 3.8 support + +------------------------------------------------------------------- Old: ---- gTTS-2.0.4.tar.gz New: ---- gTTS-2.1.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-gTTS.spec ++++++ --- /var/tmp/diff_new_pack.AYCMHA/_old 2020-04-07 10:32:08.870563188 +0200 +++ /var/tmp/diff_new_pack.AYCMHA/_new 2020-04-07 10:32:08.870563188 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-gTTS # -# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2020 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-gTTS -Version: 2.0.4 +Version: 2.1.1 Release: 0 Summary: Python module to create MP3 files from spoken text via the Google TTS API License: MIT ++++++ gTTS-2.0.4.tar.gz -> gTTS-2.1.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gTTS-2.0.4/CHANGELOG.rst new/gTTS-2.1.1/CHANGELOG.rst --- old/gTTS-2.0.4/CHANGELOG.rst 2019-08-30 02:57:17.000000000 +0200 +++ new/gTTS-2.1.1/CHANGELOG.rst 2020-01-26 04:22:29.000000000 +0100 @@ -9,6 +9,67 @@ .. towncrier release notes start +2.1.1 (2020-01-25) +------------------ + +Bugfixes +~~~~~~~~ + +- Debug mode now uses a copy of locals() to prevent RuntimeError (`#213 <https://github.com/pndurette/gTTS/issues/213>`_) + + +2.1.0 (2020-01-01) +------------------ + +Features +~~~~~~~~ + +- The ``gtts`` module + + - Added the ability to customize the Google Translate URL hostname. + This is useful when ``google.com`` might be blocked within a network but + a local or different Google host (e.g. ``google.cn``) is not + (`#143 <https://github.com/pndurette/gTTS/issues/143>`_, `#203 <https://github.com/pndurette/gTTS/issues/203>`_): + + - New ``gTTS()`` parameter ``tld`` to specify the top-level + domain to use for the Google hostname, i.e ``https://translate.google.<tld>`` + (default: ``com``). + - Languages are also now fetched using the same customized hostname. + + - Pre-generated TTS API request URLs can now be obtained instead of + writing an ``mp3`` file to disk (for example to be used in an + external program): + + - New ``get_urls()`` method returns the list of URLs generated by ``gTTS``, + which can be used in lieu of ``write_to_fp()`` or ``save()``. + +- The ``gtts-cli`` command-line tool + + - New ``--tld`` option to match the new ``gtts`` customizable hostname (`#200 <https://github.com/pndurette/gTTS/issues/200>`_, `#207 <https://github.com/pndurette/gTTS/issues/207>`_) + +- Other + + - Added Python 3.8 support (`#204 <https://github.com/pndurette/gTTS/issues/204>`_) + + +Bugfixes +~~~~~~~~ + +- Changed default word-for-word pre-processor (``('M.', 'Monsieur')``) which would substitute any 'm.' for 'monsieur' (e.g. 'them.' became 'themonsieur') (`#197 <https://github.com/pndurette/gTTS/issues/197>`_) + + +Improved Documentation +~~~~~~~~~~~~~~~~~~~~~~ + +- Added examples for newer features (`#205 <https://github.com/pndurette/gTTS/issues/205>`_, `#207 <https://github.com/pndurette/gTTS/issues/207>`_) + + +Misc +~~~~ + +- `#204 <https://github.com/pndurette/gTTS/issues/204>`_, `#205 <https://github.com/pndurette/gTTS/issues/205>`_, `#207 <https://github.com/pndurette/gTTS/issues/207>`_ + + 2.0.4 (2019-08-29) ------------------ @@ -103,7 +164,7 @@ (e.g. "en-gb", "fr-ca") - New ``gTTS()`` parameter ``lang_check`` to disable language checking. - - ``gTTS()`` now delegates the ``text`` tokenizing to the + - ``gTTS()`` now delegates the ``text`` tokenizing to the API request methods (i.e. ``write_to_fp()``, ``save()``), allowing ``gTTS`` instances to be modified/reused - Rewrote tokenizing and added pre-processing (see below) @@ -114,7 +175,7 @@ - Added new exception ``gTTSError`` raised on API request errors. It attempts to guess what went wrong based on known information - and observed behaviour + and observed behaviour (`#60 <https://github.com/pndurette/gTTS/issues/60>`_, `#106 <https://github.com/pndurette/gTTS/issues/106>`_) - ``gTTS.write_to_fp()`` and ``gTTS.save()`` also raise ``gTTSError`` @@ -175,15 +236,15 @@ ~~~~~~~~ - ``_minimize()``: Fixed an infinite recursion loop that would occur - when a token started with the miminizing delimiter (i.e. a space) + when a token started with the miminizing delimiter (i.e. a space) (`#86 <https://github.com/pndurette/gTTS/issues/86>`_) - ``_minimize()``: Handle the case where a token of more than 100 characters did not contain a space (e.g. in Chinese). - Fixed an issue that fused multiline text together if the total number of characters was less than 100 - Fixed ``gtts-cli`` Unicode errors in Python 2.7 (famous last words) - (`#78 <https://github.com/pndurette/gTTS/issues/78>`_, - `#93 <https://github.com/pndurette/gTTS/issues/93>`_, + (`#78 <https://github.com/pndurette/gTTS/issues/78>`_, + `#93 <https://github.com/pndurette/gTTS/issues/93>`_, `#96 <https://github.com/pndurette/gTTS/issues/96>`_) @@ -203,7 +264,7 @@ - Rewrote all documentation files as reStructuredText - Comprehensive documentation writen for `Sphinx <http://www.sphinx-doc.org>`_, published to http://gtts.readthedocs.io -- Changelog built with `towncrier <https://github.com/hawkowl/towncrier>`_ +- Changelog built with `towncrier <https://github.com/hawkowl/towncrier>`_ Misc ~~~~ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gTTS-2.0.4/LICENSE new/gTTS-2.1.1/LICENSE --- old/gTTS-2.0.4/LICENSE 2019-08-30 02:57:17.000000000 +0200 +++ new/gTTS-2.1.1/LICENSE 2020-01-26 04:22:29.000000000 +0100 @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright © 2014-2018 Pierre Nicolas Durette +Copyright © 2014-2020 Pierre Nicolas Durette Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gTTS-2.0.4/PKG-INFO new/gTTS-2.1.1/PKG-INFO --- old/gTTS-2.0.4/PKG-INFO 2019-08-30 02:57:31.000000000 +0200 +++ new/gTTS-2.1.1/PKG-INFO 2020-01-26 04:22:49.388464500 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: gTTS -Version: 2.0.4 +Version: 2.1.1 Summary: gTTS (Google Text-to-Speech), a Python library and CLI tool to interface with Google Translate text-to-speech API Home-page: https://github.com/pndurette/gTTS Author: Pierre Nicolas Durette @@ -9,16 +9,16 @@ Description: # gTTS **gTTS** (*Google Text-to-Speech*), a Python library and CLI tool to interface with Google Translate's text-to-speech API. - Writes spoken `mp3` data to a file, a file-like object (bytestring) for further audio - manipulation, or `stdout`. <http://gtts.readthedocs.org/> + Write spoken `mp3` data to a file, a file-like object (bytestring) for further audio manipulation, or `stdout`. Or simply pre-generate Google Translate TTS request URLs to feed to an external program. + <http://gtts.readthedocs.org/> [![PyPI version](https://img.shields.io/pypi/v/gTTS.svg)](https://pypi.org/project/gTTS/) [![Python versions](https://img.shields.io/pypi/pyversions/gTTS.svg)](https://pypi.org/project/gTTS/) - [![Build Status](https://travis-ci.org/pndurette/gTTS.svg?branch=master)](https://travis-ci.org/pndurette/gTTS) - [![AppVeyor](https://ci.appveyor.com/api/projects/status/eiuxodugo78kemff/branch/master?svg=true)](https://ci.appveyor.com/project/pndurette/gtts) - [![Coveralls](https://coveralls.io/repos/github/pndurette/gTTS/badge.svg?branch=master)](https://coveralls.io/github/pndurette/gTTS?branch=master) + [![Push workflow](https://github.com/pndurette/gTTS/workflows/Push/badge.svg)](https://github.com/pndurette/gTTS/actions) + [![codecov](https://codecov.io/gh/pndurette/gTTS/branch/master/graph/badge.svg)](https://codecov.io/gh/pndurette/gTTS) [![Commits Since](https://img.shields.io/github/commits-since/pndurette/gTTS/latest.svg)](https://github.com/pndurette/gTTS/commits/) [![PyPi Downloads](http://pepy.tech/badge/gtts)](http://pepy.tech/project/gtts) + [![Buy me a Coffee](https://img.shields.io/badge/buy%20me%20a-coffee-orange)](https://www.buymeacoffee.com/pndurette) ## Features @@ -51,7 +51,7 @@ ### Licence - [The MIT License (MIT)](LICENSE) Copyright © 2014-2018 Pierre Nicolas Durette + [The MIT License (MIT)](LICENSE) Copyright © 2014-2020 Pierre Nicolas Durette Keywords: gtts,text to speech,Google Translate,TTS Platform: UNKNOWN @@ -67,9 +67,10 @@ Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Multimedia :: Sound/Audio :: Speech -Requires-Python: >= 2.7 +Requires-Python: >=2.7 Description-Content-Type: text/markdown Provides-Extra: tests Provides-Extra: docs diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gTTS-2.0.4/README.md new/gTTS-2.1.1/README.md --- old/gTTS-2.0.4/README.md 2019-08-30 02:57:17.000000000 +0200 +++ new/gTTS-2.1.1/README.md 2020-01-26 04:22:29.000000000 +0100 @@ -1,16 +1,16 @@ # gTTS **gTTS** (*Google Text-to-Speech*), a Python library and CLI tool to interface with Google Translate's text-to-speech API. -Writes spoken `mp3` data to a file, a file-like object (bytestring) for further audio -manipulation, or `stdout`. <http://gtts.readthedocs.org/> +Write spoken `mp3` data to a file, a file-like object (bytestring) for further audio manipulation, or `stdout`. Or simply pre-generate Google Translate TTS request URLs to feed to an external program. +<http://gtts.readthedocs.org/> [![PyPI version](https://img.shields.io/pypi/v/gTTS.svg)](https://pypi.org/project/gTTS/) [![Python versions](https://img.shields.io/pypi/pyversions/gTTS.svg)](https://pypi.org/project/gTTS/) -[![Build Status](https://travis-ci.org/pndurette/gTTS.svg?branch=master)](https://travis-ci.org/pndurette/gTTS) -[![AppVeyor](https://ci.appveyor.com/api/projects/status/eiuxodugo78kemff/branch/master?svg=true)](https://ci.appveyor.com/project/pndurette/gtts) -[![Coveralls](https://coveralls.io/repos/github/pndurette/gTTS/badge.svg?branch=master)](https://coveralls.io/github/pndurette/gTTS?branch=master) +[![Push workflow](https://github.com/pndurette/gTTS/workflows/Push/badge.svg)](https://github.com/pndurette/gTTS/actions) +[![codecov](https://codecov.io/gh/pndurette/gTTS/branch/master/graph/badge.svg)](https://codecov.io/gh/pndurette/gTTS) [![Commits Since](https://img.shields.io/github/commits-since/pndurette/gTTS/latest.svg)](https://github.com/pndurette/gTTS/commits/) [![PyPi Downloads](http://pepy.tech/badge/gtts)](http://pepy.tech/project/gtts) +[![Buy me a Coffee](https://img.shields.io/badge/buy%20me%20a-coffee-orange)](https://www.buymeacoffee.com/pndurette) ## Features @@ -43,4 +43,4 @@ ### Licence -[The MIT License (MIT)](LICENSE) Copyright © 2014-2018 Pierre Nicolas Durette +[The MIT License (MIT)](LICENSE) Copyright © 2014-2020 Pierre Nicolas Durette diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gTTS-2.0.4/gTTS.egg-info/PKG-INFO new/gTTS-2.1.1/gTTS.egg-info/PKG-INFO --- old/gTTS-2.0.4/gTTS.egg-info/PKG-INFO 2019-08-30 02:57:31.000000000 +0200 +++ new/gTTS-2.1.1/gTTS.egg-info/PKG-INFO 2020-01-26 04:22:49.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: gTTS -Version: 2.0.4 +Version: 2.1.1 Summary: gTTS (Google Text-to-Speech), a Python library and CLI tool to interface with Google Translate text-to-speech API Home-page: https://github.com/pndurette/gTTS Author: Pierre Nicolas Durette @@ -9,16 +9,16 @@ Description: # gTTS **gTTS** (*Google Text-to-Speech*), a Python library and CLI tool to interface with Google Translate's text-to-speech API. - Writes spoken `mp3` data to a file, a file-like object (bytestring) for further audio - manipulation, or `stdout`. <http://gtts.readthedocs.org/> + Write spoken `mp3` data to a file, a file-like object (bytestring) for further audio manipulation, or `stdout`. Or simply pre-generate Google Translate TTS request URLs to feed to an external program. + <http://gtts.readthedocs.org/> [![PyPI version](https://img.shields.io/pypi/v/gTTS.svg)](https://pypi.org/project/gTTS/) [![Python versions](https://img.shields.io/pypi/pyversions/gTTS.svg)](https://pypi.org/project/gTTS/) - [![Build Status](https://travis-ci.org/pndurette/gTTS.svg?branch=master)](https://travis-ci.org/pndurette/gTTS) - [![AppVeyor](https://ci.appveyor.com/api/projects/status/eiuxodugo78kemff/branch/master?svg=true)](https://ci.appveyor.com/project/pndurette/gtts) - [![Coveralls](https://coveralls.io/repos/github/pndurette/gTTS/badge.svg?branch=master)](https://coveralls.io/github/pndurette/gTTS?branch=master) + [![Push workflow](https://github.com/pndurette/gTTS/workflows/Push/badge.svg)](https://github.com/pndurette/gTTS/actions) + [![codecov](https://codecov.io/gh/pndurette/gTTS/branch/master/graph/badge.svg)](https://codecov.io/gh/pndurette/gTTS) [![Commits Since](https://img.shields.io/github/commits-since/pndurette/gTTS/latest.svg)](https://github.com/pndurette/gTTS/commits/) [![PyPi Downloads](http://pepy.tech/badge/gtts)](http://pepy.tech/project/gtts) + [![Buy me a Coffee](https://img.shields.io/badge/buy%20me%20a-coffee-orange)](https://www.buymeacoffee.com/pndurette) ## Features @@ -51,7 +51,7 @@ ### Licence - [The MIT License (MIT)](LICENSE) Copyright © 2014-2018 Pierre Nicolas Durette + [The MIT License (MIT)](LICENSE) Copyright © 2014-2020 Pierre Nicolas Durette Keywords: gtts,text to speech,Google Translate,TTS Platform: UNKNOWN @@ -67,9 +67,10 @@ Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Multimedia :: Sound/Audio :: Speech -Requires-Python: >= 2.7 +Requires-Python: >=2.7 Description-Content-Type: text/markdown Provides-Extra: tests Provides-Extra: docs diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gTTS-2.0.4/gTTS.egg-info/SOURCES.txt new/gTTS-2.1.1/gTTS.egg-info/SOURCES.txt --- old/gTTS-2.0.4/gTTS.egg-info/SOURCES.txt 2019-08-30 02:57:31.000000000 +0200 +++ new/gTTS-2.1.1/gTTS.egg-info/SOURCES.txt 2020-01-26 04:22:49.000000000 +0100 @@ -3,6 +3,7 @@ LICENSE MANIFEST.in README.md +pyproject.toml pytest.ini setup.cfg setup.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gTTS-2.0.4/gTTS.egg-info/requires.txt new/gTTS-2.1.1/gTTS.egg-info/requires.txt --- old/gTTS-2.0.4/gTTS.egg-info/requires.txt 2019-08-30 02:57:31.000000000 +0200 +++ new/gTTS-2.1.1/gTTS.egg-info/requires.txt 2020-01-26 04:22:49.000000000 +0100 @@ -17,3 +17,4 @@ flake8 testfixtures mock +six diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gTTS-2.0.4/gtts/cli.py new/gTTS-2.1.1/gtts/cli.py --- old/gTTS-2.0.4/gtts/cli.py 2019-08-30 02:57:17.000000000 +0200 +++ new/gTTS-2.1.1/gtts/cli.py 2020-01-26 04:22:29.000000000 +0100 @@ -60,12 +60,14 @@ def validate_lang(ctx, param, lang): """Validation callback for the <lang> option. Ensures <lang> is a supported language unless the <nocheck> flag is set + Uses <tld> to fetch languages from other domains """ if ctx.params['nocheck']: return lang try: - if lang not in tts_langs(): + tld = ctx.params['tld'] + if lang not in tts_langs(tld): raise click.UsageError( "'%s' not in list of supported languages.\n" "Use --all to list languages or " @@ -88,8 +90,16 @@ """ if not value or ctx.resilient_parsing: return + + try: + tld = ctx.params['tld'] + except KeyError: + # Either --tld was used after --all or not at all + # Default to the 'com' tld + tld = 'com' + try: - langs = tts_langs() + langs = tts_langs(tld) langs_str_list = sorted("{}: {}".format(k, langs[k]) for k in langs) click.echo(' ' + '\n '.join(langs_str_list)) except RuntimeError as e: # pragma: no cover @@ -137,6 +147,14 @@ callback=validate_lang, help="IETF language tag. Language to speak in. List documented tags with --all.") @click.option( + '-t', + '--tld', + metavar='<tld>', + default='com', + show_default=True, + is_eager=True, # Prioritize <tld> to ensure it gets set before <lang> + help="Top-level domain for the Google host, i.e https://translate.google.<tld>") +@click.option( '--nocheck', default=False, is_flag=True, @@ -149,7 +167,8 @@ is_eager=True, expose_value=False, callback=print_languages, - help="Print all documented available IETF language tags and exit.") + help="Print all documented available IETF language tags and exit. " + "Use --tld beforehand to use an alternate domain") @click.option( '--debug', default=False, @@ -159,7 +178,7 @@ callback=set_debug, help="Show debug information.") @click.version_option(version=__version__) -def tts_cli(text, file, output, slow, lang, nocheck): +def tts_cli(text, file, output, slow, tld, lang, nocheck): """ Read <text> to mp3 format using Google Translate's Text-to-Speech API (set <text> or --file <file> to - for standard input) """ @@ -189,6 +208,7 @@ text=text, lang=lang, slow=slow, + tld=tld, lang_check=not nocheck) tts.write_to_fp(output) except (ValueError, AssertionError) as e: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gTTS-2.0.4/gtts/lang.py new/gTTS-2.1.1/gtts/lang.py --- old/gTTS-2.0.4/gtts/lang.py 2019-08-30 02:57:17.000000000 +0200 +++ new/gTTS-2.1.1/gtts/lang.py 2020-01-26 04:22:29.000000000 +0100 @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +from gtts.utils import _translate_url from bs4 import BeautifulSoup import requests import logging @@ -6,23 +7,25 @@ __all__ = ['tts_langs'] -URL_BASE = 'https://translate.google.com' -JS_FILE = 'translate_m.js' - # Logger log = logging.getLogger(__name__) log.addHandler(logging.NullHandler()) -def tts_langs(): +def tts_langs(tld="com"): """Languages Google Text-to-Speech supports. + Args: + tld (string): Top-level domain for the Google Translate host + to fetch languages from. i.e `https://translate.google.<tld>`. + Default is ``com``. + Returns: dict: A dictionnary of the type `{ '<lang>': '<name>'}` - Where `<lang>` is an IETF language tag such as `en` or `pt-br`, - and `<name>` is the full English name of the language, such as - `English` or `Portuguese (Brazil)`. + Where `<lang>` is an IETF language tag such as `en` or `pt-br`, + and `<name>` is the full English name of the language, such as + `English` or `Portuguese (Brazil)`. The dictionnary returned combines languages from two origins: @@ -33,32 +36,48 @@ """ try: langs = dict() - langs.update(_fetch_langs()) + log.debug("Fetching with '{}' tld".format(tld)) + langs.update(_fetch_langs(tld)) langs.update(_extra_langs()) - log.debug("langs: %s", langs) + log.debug("langs: {}".format(langs)) return langs except Exception as e: - raise RuntimeError("Unable to get language list: %s" % str(e)) + raise RuntimeError("Unable to get language list: {}".format(str(e))) -def _fetch_langs(): +def _fetch_langs(tld="com"): """Fetch (scrape) languages from Google Translate. Google Translate loads a JavaScript Array of 'languages codes' that can be spoken. We intersect this list with all the languages Google Translate provides to get the ones that support text-to-speech. + Args: + tld (string): Top-level domain for the Google Translate host + to fetch languages from. i.e `https://translate.google.<tld>`. + The language names obtained will be in a language locale of the TLD + (e.g. ``tld=fr`` will retrieve the French names of the languages). + Default is ``com``. + Returns: dict: A dictionnary of languages from Google Translate """ + URL_BASE = _translate_url(tld) + + # The JavaScript file to look for is either: + # * translate_m.js or + # * translate_m_<lang-code>.js + # e.g. translate_m_fr.js or translate_m_zh-CN.js + JS_FILE = r'translate_m(|_\S*)\.js' + # Load HTML page = requests.get(URL_BASE) soup = BeautifulSoup(page.content, 'html.parser') # JavaScript URL # The <script src=''> path can change, but not the file. - # Ex: /zyx/abc/20180211/desktop_module_main.js + # Ex: /zyx/abc/20180211/translate_m.js js_path = soup.find(src=re.compile(JS_FILE))['src'] js_url = "{}/{}".format(URL_BASE, js_path) @@ -89,9 +108,9 @@ Returns: dict: A dictionnary of extra languages manually defined. - Variations of the ones fetched by `_fetch_langs`, - observed to provide different dialects or accents or - just simply accepted by the Google Translate Text-to-Speech API. + Variations of the ones fetched by `_fetch_langs`, + observed to provide different dialects or accents or + just simply accepted by the Google Translate Text-to-Speech API. """ return { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gTTS-2.0.4/gtts/tests/test_cli.py new/gTTS-2.1.1/gtts/tests/test_cli.py --- old/gTTS-2.0.4/gtts/tests/test_cli.py 2019-08-30 02:57:17.000000000 +0200 +++ new/gTTS-2.1.1/gtts/tests/test_cli.py 2020-01-26 04:22:29.000000000 +0100 @@ -25,7 +25,7 @@ return CliRunner().invoke(tts_cli, args + ['--debug'], input) -# %% <text> tests +# <text> tests def test_text_no_text_or_file(): """One of <test> (arg) and <file> <opt> should be set""" result = runner_debug([]) @@ -55,9 +55,8 @@ assert "No text to speak" in result.output assert result.exit_code != 0 -# %% <file> tests - +# <file> tests def test_file_not_exists(): """<file> should exist""" result = runner_debug(['--file', 'notexist.txt', 'test']) @@ -65,9 +64,8 @@ assert "No such file or directory" in result.output assert result.exit_code != 0 -# %% <all> tests - +# <all> tests def test_all(): """Option <all> should return a list of languages""" result = runner(['--all']) @@ -78,9 +76,18 @@ assert re.match(r"^(?:\s{2}(\w{2}|\w{2}-\w{2}): .+\n?)+$", result.output) assert result.exit_code == 0 -# %% <lang> tests + +def test_all_tld(): + """Option <all> should return a list of languages""" + result = runner(['--tld', 'it', '--all']) + + # Top-level domain set to 'it', language outputs should be Italian + + assert "en: Inglese" in result.output + assert result.exit_code == 0 +# <lang> tests def test_lang_not_valid(): """Invalid <lang> should display an error""" result = runner(['--lang', 'xx', 'test']) @@ -101,25 +108,25 @@ assert "Probable cause: Unsupported language 'xx'" in result.output assert result.exit_code != 0 -# %% Param set tests +# Param set tests def test_params_set(): """Options should set gTTS instance arguments (read from debug log)""" with LogCapture() as lc: - result = runner_debug(['--lang', 'fr', '--slow', '--nocheck', 'test']) + result = runner_debug(['--lang', 'fr', '--tld', 'es', '--slow', '--nocheck', 'test']) log = str(lc) assert 'lang: fr' in log + assert 'tld: es' in log assert 'lang_check: False' in log assert 'slow: True' in log assert 'text: test' in log assert result.exit_code == 0 -# %% Test all input methods - +# Test all input methods pwd = os.path.dirname(__file__) # Text for stdin ('-' for <text> or <file>) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gTTS-2.0.4/gtts/tests/test_lang.py new/gTTS-2.1.1/gtts/tests/test_lang.py --- old/gTTS-2.0.4/gtts/tests/test_lang.py 2019-08-30 02:57:17.000000000 +0200 +++ new/gTTS-2.1.1/gtts/tests/test_lang.py 2020-01-26 04:22:29.000000000 +0100 @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- import pytest -from mock import patch - from gtts.lang import tts_langs, _fetch_langs, _extra_langs @@ -25,11 +23,10 @@ assert len(all_langs) == len(scraped_langs) + len(extra_langs) -@patch("gtts.lang.URL_BASE", "http://abc.def.hij.dghj") def test_fetch_langs_exception(): """Raise RuntimeError on language fetch exception""" with pytest.raises(RuntimeError): - tts_langs() + tts_langs(tld="invalid") if __name__ == '__main__': diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gTTS-2.0.4/gtts/tests/test_tts.py new/gTTS-2.1.1/gtts/tests/test_tts.py --- old/gTTS-2.0.4/gtts/tests/test_tts.py 2019-08-30 02:57:17.000000000 +0200 +++ new/gTTS-2.1.1/gtts/tests/test_tts.py 2020-01-26 04:22:29.000000000 +0100 @@ -2,6 +2,7 @@ import os import pytest from mock import Mock +from six.moves import urllib from gtts.tts import gTTS, gTTSError from gtts.lang import _fetch_langs, _extra_langs @@ -45,7 +46,7 @@ for slow in (False, True): filename = tmp_path / 'test_{}_.mp3'.format(lang) # Create gTTS and save - tts = gTTS(text, lang, slow=slow) + tts = gTTS(text=text, lang=lang, slow=slow) tts.save(filename) # Check if files created is > 2k @@ -77,7 +78,7 @@ tts.save(filename) -# %%Test write_to_fp()/save() cases not covered elsewhere in this file +# Test write_to_fp()/save() cases not covered elsewhere in this file def test_bad_fp_type(): """Raise TypeError if fp is not a file-like object (no .write())""" @@ -98,6 +99,21 @@ assert filename.stat().st_size > 2000 +def test_get_urls(): + """Get request URLs list""" + + tts = gTTS(text='test', tld='com', lang='en-uk') + urls = tts.get_urls() + + # Check the url + r = urllib.parse.urlparse(urls[0]) + assert r.scheme == 'https' + assert r.netloc == 'translate.google.com' + assert r.path == '/translate_tts' + assert 'test' in urllib.parse.parse_qs(r.query)['q'] + assert 'en-uk' in urllib.parse.parse_qs(r.query)['tl'] + + def test_msg(): """Test gTTsError internal exception handling Set exception message successfully""" @@ -111,6 +127,15 @@ def test_infer_msg(): """Infer message sucessfully based on context""" + # Without response: + + # Bad TLD + ttsTLD = Mock(tld='invalid') + errorTLD = gTTSError(tts=ttsTLD) + assert errorTLD.msg == "Failed to connect. Probable cause: Host 'https://translate.google.invalid/' is not reachable" + + # With response: + # 403 tts403 = Mock() response403 = Mock(status_code=403, reason='aaa') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gTTS-2.0.4/gtts/tests/test_utils.py new/gTTS-2.1.1/gtts/tests/test_utils.py --- old/gTTS-2.0.4/gtts/tests/test_utils.py 2019-08-30 02:57:17.000000000 +0200 +++ new/gTTS-2.1.1/gtts/tests/test_utils.py 2020-01-26 04:22:29.000000000 +0100 @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- import pytest -from gtts.utils import _minimize, _len, _clean_tokens +from gtts.utils import _minimize, _len, _clean_tokens, _translate_url delim = ' ' Lmax = 10 @@ -52,5 +52,11 @@ assert _clean_tokens(_in) == _out +def test_translate_url(): + _in = {"tld": "qwerty", "path": "asdf"} + _out = "https://translate.google.qwerty/asdf" + assert _translate_url(**_in) == _out + + if __name__ == '__main__': pytest.main(['-x', __file__]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gTTS-2.0.4/gtts/tokenizer/symbols.py new/gTTS-2.1.1/gtts/tokenizer/symbols.py --- old/gTTS-2.0.4/gtts/tokenizer/symbols.py 2019-08-30 02:57:17.000000000 +0200 +++ new/gTTS-2.1.1/gtts/tokenizer/symbols.py 2020-01-26 04:22:29.000000000 +0100 @@ -6,7 +6,7 @@ 'prof', 'sr', 'st'] SUB_PAIRS = [ - ('M.', 'Monsieur') + ('Esq.', 'Esquire') ] ALL_PUNC = u"?!?!.,¡()[]¿…‥،;:—。,、:\n" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gTTS-2.0.4/gtts/tts.py new/gTTS-2.1.1/gtts/tts.py --- old/gTTS-2.0.4/gtts/tts.py 2019-08-30 02:57:17.000000000 +0200 +++ new/gTTS-2.1.1/gtts/tts.py 2020-01-26 04:22:29.000000000 +0100 @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from gtts.tokenizer import pre_processors, Tokenizer, tokenizer_cases -from gtts.utils import _minimize, _len, _clean_tokens +from gtts.utils import _minimize, _len, _clean_tokens, _translate_url from gtts.lang import tts_langs from gtts_token import gtts_token @@ -33,12 +33,19 @@ Args: text (string): The text to be read. + tld (string): Top-level domain for the Google Translate host, + i.e `https://translate.google.<tld>`. This is useful + when ``google.com`` might be blocked within a network but + a local or different Google host (e.g. ``google.cn``) is not. + Default is ``com``. lang (string, optional): The language (IETF language tag) to - read the text in. Defaults to 'en'. + read the text in. Default is ``en``. slow (bool, optional): Reads text more slowly. Defaults to ``False``. lang_check (bool, optional): Strictly enforce an existing ``lang``, to catch a language error early. If set to ``True``, a ``ValueError`` is raised if ``lang`` doesn't exist. + Setting ``lang_check`` to ``False`` skips Web requests + (to validate language) and therefore speeds up instanciation. Default is ``True``. pre_processor_funcs (list): A list of zero or more functions that are called to transform (pre-process) text before tokenizing. Those @@ -74,7 +81,6 @@ """ GOOGLE_TTS_MAX_CHARS = 100 # Max characters the Google TTS API takes at a time - GOOGLE_TTS_URL = "https://translate.google.com/translate_tts" GOOGLE_TTS_HEADERS = { "Referer": "http://translate.google.com/", "User-Agent": @@ -86,6 +92,7 @@ def __init__( self, text, + tld='com', lang='en', slow=False, lang_check=True, @@ -104,7 +111,7 @@ ): # Debug - for k, v in locals().items(): + for k, v in dict(locals()).items(): if k == 'self': continue log.debug("%s: %s", k, v) @@ -113,10 +120,13 @@ assert text, 'No text to speak' self.text = text + # Translate URL top-level domain + self.tld = tld + # Language if lang_check: try: - langs = tts_langs() + langs = tts_langs(self.tld) if lang.lower() not in langs: raise ValueError("Language not supported: %s" % lang) except RuntimeError as e: @@ -164,25 +174,20 @@ min_tokens += _minimize(t, ' ', self.GOOGLE_TTS_MAX_CHARS) return min_tokens - def write_to_fp(self, fp): - """Do the TTS API request and write bytes to a file-like object. - - Args: - fp (file object): Any file-like object to write the ``mp3`` to. - - Raises: - :class:`gTTSError`: When there's an error with the API request. - TypeError: When ``fp`` is not a file-like object that takes bytes. + def _prepare_requests(self): + """Created the TTS API the request(s) without sending them. + Returns: + list: ``requests.PreparedRequests_``. <https://2.python-requests.org/en/master/api/#requests.PreparedRequest>`_``. """ - # When disabling ssl verify in requests (for proxies and firewalls), - # urllib3 prints an insecure warning on stdout. We disable that. - urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + # TTS API URL + translate_url = _translate_url(tld=self.tld, path="translate_tts") text_parts = self._tokenize(self.text) log.debug("text_parts: %i", len(text_parts)) assert text_parts, 'No text to send to TTS API' + prepared_requests = [] for idx, part in enumerate(text_parts): try: # Calculate token @@ -205,25 +210,66 @@ log.debug("payload-%i: %s", idx, payload) - try: - # Request - r = requests.get(self.GOOGLE_TTS_URL, + # Request + r = requests.Request(method='GET', + url=translate_url, params=payload, - headers=self.GOOGLE_TTS_HEADERS, - proxies=urllib.request.getproxies(), - verify=False) + headers=self.GOOGLE_TTS_HEADERS) + + # Prepare request + prepared_requests.append(r.prepare()) + + return prepared_requests + + def get_urls(self): + """Get TTS API request URL(s) that would be sent to the TTS API. + + Returns: + list: A list of TTS API request URLs to make. + + This is particularly useful to get the list of URLs generated + by ``gTTS`` but not yet fullfilled, + for example to be used by an external program. + """ + return [pr.url for pr in self._prepare_requests()] + + def write_to_fp(self, fp): + """Do the TTS API request(s) and write bytes to a file-like object. + + Args: + fp (file object): Any file-like object to write the ``mp3`` to. + + Raises: + :class:`gTTSError`: When there's an error with the API request. + TypeError: When ``fp`` is not a file-like object that takes bytes. + + """ + # When disabling ssl verify in requests (for proxies and firewalls), + # urllib3 prints an insecure warning on stdout. We disable that. + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + + prepared_requests = self._prepare_requests() + for idx, pr in enumerate(prepared_requests): + try: + with requests.Session() as s: + # Send request + r = s.send(request=pr, + proxies=urllib.request.getproxies(), + verify=False) log.debug("headers-%i: %s", idx, r.request.headers) log.debug("url-%i: %s", idx, r.request.url) log.debug("status-%i: %s", idx, r.status_code) r.raise_for_status() - except requests.exceptions.HTTPError: + except requests.exceptions.HTTPError as e: # pragma: no cover # Request successful, bad response + log.debug(str(e)) raise gTTSError(tts=self, response=r) except requests.exceptions.RequestException as e: # pragma: no cover # Request failed - raise gTTSError(str(e)) + log.debug(str(e)) + raise gTTSError(tts=self) try: # Write @@ -258,29 +304,39 @@ self.rsp = kwargs.pop('response', None) if msg: self.msg = msg - elif self.tts is not None and self.rsp is not None: + elif self.tts is not None: self.msg = self.infer_msg(self.tts, self.rsp) else: self.msg = None super(gTTSError, self).__init__(self.msg) - def infer_msg(self, tts, rsp): + def infer_msg(self, tts, rsp=None): """Attempt to guess what went wrong by using known information (e.g. http response) and observed behaviour """ - # rsp should be <requests.Response> - # http://docs.python-requests.org/en/master/api/ - status = rsp.status_code - reason = rsp.reason - cause = "Unknown" - if status == 403: - cause = "Bad token or upstream API changes" - elif status == 404 and not tts.lang_check: - cause = "Unsupported language '%s'" % self.tts.lang - elif status >= 500: - cause = "Uptream API error. Try again later." - return "%i (%s) from TTS API. Probable cause: %s" % ( - status, reason, cause) + if rsp is None: + premise = "Failed to connect" + + if tts.tld != 'com': + host = _translate_url(tld=tts.tld) + cause = "Host '{}' is not reachable".format(host) + + else: + # rsp should be <requests.Response> + # http://docs.python-requests.org/en/master/api/ + status = rsp.status_code + reason = rsp.reason + + premise = "{:d} ({}) from TTS API".format(status, reason) + + if status == 403: + cause = "Bad token or upstream API changes" + elif status == 404 and not tts.lang_check: + cause = "Unsupported language '%s'" % self.tts.lang + elif status >= 500: + cause = "Uptream API error. Try again later." + + return "{}. Probable cause: {}".format(premise, cause) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gTTS-2.0.4/gtts/utils.py new/gTTS-2.1.1/gtts/utils.py --- old/gTTS-2.0.4/gtts/utils.py 2019-08-30 02:57:17.000000000 +0200 +++ new/gTTS-2.1.1/gtts/utils.py 2020-01-26 04:22:29.000000000 +0100 @@ -23,12 +23,13 @@ Returns: list: the minimized string in tokens - Every chunk size will be at minimum `the_string[0:idx]` where `idx` - is the highest index of `delim` found in `the_string`; and at maximum - `the_string[0:max_size]` if no `delim` was found in `the_string`. - In the latter case, the split will occur at `the_string[max_size]` + Every chunk size will be at minimum ``the_string[0:idx]`` where ``idx`` + is the highest index of ``delim`` found in ``the_string``; and at maximum + ``the_string[0:max_size]`` if no ``delim`` was found in ``the_string``. + In the latter case, the split will occur at ``the_string[max_size]`` which can be any character. The function runs itself again on the rest of - `the_string` (`the_string[idx:]`) until no chunk is larger than `max_size`. + ``the_string`` (``the_string[idx:]``) until no chunk is larger than + ``max_size``. """ # Remove `delim` from start of `the_string` @@ -54,14 +55,14 @@ def _len(text): - """Same as `len(text)` for a string but that decodes - `text` first in Python 2.x + """Same as ``len(text)`` for a string but that decodes + ``text`` first in Python 2.x Args: - text (string): string to get the size of. + text (string): String to get the size of. Returns: - int: the size of the string. + int: The size of the string. """ try: # Python 2 @@ -75,11 +76,27 @@ """Clean a list of strings Args: - tokens (list): a list of strings (tokens) to clean. + tokens (list): A list of strings (tokens) to clean. Returns: - list: stripped strings `tokens` without the original elements + list: Stripped strings ``tokens`` without the original elements that only consisted of whitespace and/or punctuation characters. """ return [t.strip() for t in tokens if not _ALL_PUNC_OR_SPACE.match(t)] + + +def _translate_url(tld="com", path=""): + """Generates a Google Translate URL + + Args: + tld (string): Top-level domain for the Google Translate host, + i.e ``https://translate.google.<tld>``. Default is ``com``. + path: (string): A path to append to the Google Translate host, + i.e ``https://translate.google.com/<path>``. Default is ``""``. + + Returns: + string: A Google Translate URL `https://translate.google.<tld>/path` + """ + _GOOGLE_TTS_URL = "https://translate.google.{}/{}" + return _GOOGLE_TTS_URL.format(tld, path) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gTTS-2.0.4/gtts/version.py new/gTTS-2.1.1/gtts/version.py --- old/gTTS-2.0.4/gtts/version.py 2019-08-30 02:57:17.000000000 +0200 +++ new/gTTS-2.1.1/gtts/version.py 2020-01-26 04:22:29.000000000 +0100 @@ -1 +1 @@ -__version__ = '2.0.4' +__version__ = '2.1.1' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gTTS-2.0.4/pyproject.toml new/gTTS-2.1.1/pyproject.toml --- old/gTTS-2.0.4/pyproject.toml 1970-01-01 01:00:00.000000000 +0100 +++ new/gTTS-2.1.1/pyproject.toml 2020-01-26 04:22:29.000000000 +0100 @@ -0,0 +1,7 @@ +[tool.towncrier] +package = "gtts" +filename = "CHANGELOG.rst" +directory = "news/" +underlines = ["-", "~", "_"] +title_format = "{version} ({project_date})" +issue_format = "`#{issue} <https://github.com/pndurette/gTTS/issues/{issue}>`_" \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/gTTS-2.0.4/setup.cfg new/gTTS-2.1.1/setup.cfg --- old/gTTS-2.0.4/setup.cfg 2019-08-30 02:57:31.000000000 +0200 +++ new/gTTS-2.1.1/setup.cfg 2020-01-26 04:22:49.392464600 +0100 @@ -23,6 +23,7 @@ Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 Topic :: Software Development :: Libraries Topic :: Multimedia :: Sound/Audio :: Speech license_file = LICENSE @@ -33,7 +34,6 @@ python_requires = >= 2.7 setup_requires = setuptools >= 38.6 - pip >= 10 twine >= 1.11 include_package_data = True packages = find: @@ -51,6 +51,7 @@ flake8 testfixtures mock + six docs = sphinx sphinx-autobuild ++++++ remove-pip-requirement.patch ++++++ --- /var/tmp/diff_new_pack.AYCMHA/_old 2020-04-07 10:32:08.954563298 +0200 +++ /var/tmp/diff_new_pack.AYCMHA/_new 2020-04-07 10:32:08.954563298 +0200 @@ -1,12 +1,11 @@ -Index: gTTS-2.0.4/setup.cfg +Index: gTTS-2.1.1/setup.cfg =================================================================== ---- gTTS-2.0.4.orig/setup.cfg -+++ gTTS-2.0.4/setup.cfg -@@ -33,8 +33,6 @@ long_description_content_type = text/mar +--- gTTS-2.1.1.orig/setup.cfg ++++ gTTS-2.1.1/setup.cfg +@@ -34,7 +34,6 @@ long_description_content_type = text/mar python_requires = >= 2.7 setup_requires = setuptools >= 38.6 -- pip >= 10 - twine >= 1.11 include_package_data = True packages = find: