AwesomeTTS / awesometts-anki-addon

AwesomeTTS text-to-speech add-on for Anki
GNU General Public License v3.0
483 stars 100 forks source link

Suggestion - don't regenerate mp3 when you have a cached version of that text made by a different preset in a group. #265

Open xavier-taylor opened 2 years ago

xavier-taylor commented 2 years ago

I have some large language learning decks with lots of example sentences.

I use the preset/group feature to randomly use one of several API voices (some azure, some google etc).

To save on API calls and storage space etc, I don't want to randomly select a new voice when I am hearing a sentence I have heard before. It is enough that I get variety of voices between sentences. I don't need to accumulate 7 versions of the same sentence.

I hacked together something that implements this locally, all in router.py. The changes are mainly just adding a chain of 'from_preset' parameters then adding some custom logic in _path_cache()

The result is that for a given text input (ie a sentence/word in your language), if you have a group of presets set up, whichever one you first use for that text input you will cache and always use.

`

in group()

                     want_human=want_human, note=note, from_preset=True)

in call()

def call(self, svc_id, text, options, callbacks, want_human=False, note=None, async_variable=True, from_preset=False): ... path = self._validate_path(svc_id, text, options, from_preset)

in _validate_path()

def _validate_path(self, svc_id, text, options, from_preset=False):

... path = self._path_cache(svc_id, text, options, from_preset)

in _path_cache

def _path_cache(self, svc_id, text, options, from_preset=False):

... if not from_preset: hash_input = '/'.join([ text, svc_id, ';'.join( '='.join([ key, value if isinstance(value, str) else str(value), ]) for key, value in sorted(options.items()) ) ]) else: hash_input = '/'.join([ 'from-preset', text ]) ... if not from_preset: return os.path.join( self._cache_dir, '.'.join([ '-'.join([ svc_id, hex_digest[:8], hex_digest[8:16], hex_digest[16:24], hex_digest[24:32], hex_digest[32:], ]), 'mp3', ]), ) else: return os.path.join( self._cache_dir, '.'.join([ '-'.join([ 'from_preset', hex_digest[:8], hex_digest[8:16], hex_digest[16:24], hex_digest[24:32], hex_digest[32:], ]), 'mp3', ]), )

`

I can tell already that this isn't a full solution for the entire codebase - for one thing this behaviour would probably want to be configurable rather than hardcoded in, for another the filename prefix would ideally be the name of the group itself.

I don't have the time to write up a pull request at the moment - just putting this code here in case anyone else wants to do something similar, or if the idea seems like a good feature to the maintainers.

Thanks for your work on this tool, absolute godsend.