Repository: Diaoul/subliminal
Pull request #1156 (2024-07-15 22:02:39)

Fix running coverage tests with tox
Coverage was not working properly.

I also updated some tests that were failing when run in parallel (running `pytest` with the "-n auto" option), for the `tvdb` refiner, because they needed to login.

Minor fix: cleaning up after running `doctest`

github-actions[bot]: ## Coverage report


<img title="Coverage for the whole project went from 83.46% to 84.69%" src="https://img.shields.io/badge/Coverage%20evolution-83%25%20%3E%2084%25-brightgreen.svg"> <img title="100% of the statement lines added by this PR are covered" src="https://img.shields.io/badge/PR%20Coverage-100%25-brightgreen.svg"><details><summary>Click to see where and how coverage changed</summary><table><thead>
  <tr><th>File</th><th>Statements</th><th>Missing</th><th>Coverage</th><th>Coverage<br>(new stmts)</th><th>Lines missing</th></tr>
</thead>
<tbody><tr>
<td colspan="6">&nbsp;&nbsp;<b>subliminal</b></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-b73dd018789b4e8ef8fe85251d8627fa5059ff39e2586b50d2e4d0659d1d8267">__init__.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-b73dd018789b4e8ef8fe85251d8627fa5059ff39e2586b50d2e4d0659d1d8267"><img title="This PR doesn't change the number of statements in subliminal/__init__.py, which is 18." src="https://img.shields.io/badge/18-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-b73dd018789b4e8ef8fe85251d8627fa5059ff39e2586b50d2e4d0659d1d8267"><img title="This PR doesn't change the number of statements missing coverage in subliminal/__init__.py, which is 0." src="https://img.shields.io/badge/0-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-b73dd018789b4e8ef8fe85251d8627fa5059ff39e2586b50d2e4d0659d1d8267"><img title="This PR doesn't change the coverage rate in subliminal/__init__.py, which is 100% (18/18)." src="https://img.shields.io/badge/100%25-%2818/18%20%3E%2018/18%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-b73dd018789b4e8ef8fe85251d8627fa5059ff39e2586b50d2e4d0659d1d8267"><img title="This PR does not seem to add statements to subliminal/__init__.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-3448ac609f63c11ccc7b5eb968c6b5e56ad3e023027667b5eef5e100e664f36b">cache.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-3448ac609f63c11ccc7b5eb968c6b5e56ad3e023027667b5eef5e100e664f36b"><img title="This PR doesn't change the number of statements in subliminal/cache.py, which is 14." src="https://img.shields.io/badge/14-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-3448ac609f63c11ccc7b5eb968c6b5e56ad3e023027667b5eef5e100e664f36b"><img title="This PR doesn't change the number of statements missing coverage in subliminal/cache.py, which is 0." src="https://img.shields.io/badge/0-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-3448ac609f63c11ccc7b5eb968c6b5e56ad3e023027667b5eef5e100e664f36b"><img title="This PR doesn't change the coverage rate in subliminal/cache.py, which is 100% (14/14)." src="https://img.shields.io/badge/100%25-%2814/14%20%3E%2014/14%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-3448ac609f63c11ccc7b5eb968c6b5e56ad3e023027667b5eef5e100e664f36b"><img title="This PR does not seem to add statements to subliminal/cache.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-ec42c577c17cada8e2ea702b34701c438825ad899ba137dffa51501401736c71">core.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-ec42c577c17cada8e2ea702b34701c438825ad899ba137dffa51501401736c71"><img title="This PR doesn't change the number of statements in subliminal/core.py, which is 334." src="https://img.shields.io/badge/334-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-ec42c577c17cada8e2ea702b34701c438825ad899ba137dffa51501401736c71"><img title="This PR doesn't change the number of statements missing coverage in subliminal/core.py, which is 39." src="https://img.shields.io/badge/39-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-ec42c577c17cada8e2ea702b34701c438825ad899ba137dffa51501401736c71"><img title="This PR doesn't change the coverage rate in subliminal/core.py, which is 88.32% (295/334)." src="https://img.shields.io/badge/88%25-%28295/334%20%3E%20295/334%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-ec42c577c17cada8e2ea702b34701c438825ad899ba137dffa51501401736c71"><img title="This PR does not seem to add statements to subliminal/core.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-a12703295ef7ea33e0e9e645465b11c21c04b4370e8b20bb18f017455f843fae">exceptions.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-a12703295ef7ea33e0e9e645465b11c21c04b4370e8b20bb18f017455f843fae"><img title="This PR doesn't change the number of statements in subliminal/exceptions.py, which is 14." src="https://img.shields.io/badge/14-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-a12703295ef7ea33e0e9e645465b11c21c04b4370e8b20bb18f017455f843fae"><img title="This PR doesn't change the number of statements missing coverage in subliminal/exceptions.py, which is 0." src="https://img.shields.io/badge/0-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-a12703295ef7ea33e0e9e645465b11c21c04b4370e8b20bb18f017455f843fae"><img title="This PR doesn't change the coverage rate in subliminal/exceptions.py, which is 100% (14/14)." src="https://img.shields.io/badge/100%25-%2814/14%20%3E%2014/14%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-a12703295ef7ea33e0e9e645465b11c21c04b4370e8b20bb18f017455f843fae"><img title="This PR does not seem to add statements to subliminal/exceptions.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-8691c0109531330587ddfb0735f589734e650377a8f140ed1c8889eabe970156">extensions.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-8691c0109531330587ddfb0735f589734e650377a8f140ed1c8889eabe970156"><img title="This PR doesn't change the number of statements in subliminal/extensions.py, which is 60." src="https://img.shields.io/badge/60-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-8691c0109531330587ddfb0735f589734e650377a8f140ed1c8889eabe970156"><img title="This PR doesn't change the number of statements missing coverage in subliminal/extensions.py, which is 11." src="https://img.shields.io/badge/11-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-8691c0109531330587ddfb0735f589734e650377a8f140ed1c8889eabe970156"><img title="This PR doesn't change the coverage rate in subliminal/extensions.py, which is 81.66% (49/60)." src="https://img.shields.io/badge/81%25-%2849/60%20%3E%2049/60%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-8691c0109531330587ddfb0735f589734e650377a8f140ed1c8889eabe970156"><img title="This PR does not seem to add statements to subliminal/extensions.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-05acc97a63b03d5d3f0a2f95c60c8f01e6a580c85499998d18cdd734bf483474">matches.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-05acc97a63b03d5d3f0a2f95c60c8f01e6a580c85499998d18cdd734bf483474"><img title="This PR doesn't change the number of statements in subliminal/matches.py, which is 57." src="https://img.shields.io/badge/57-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-05acc97a63b03d5d3f0a2f95c60c8f01e6a580c85499998d18cdd734bf483474"><img title="This PR doesn't change the number of statements missing coverage in subliminal/matches.py, which is 3." src="https://img.shields.io/badge/3-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-05acc97a63b03d5d3f0a2f95c60c8f01e6a580c85499998d18cdd734bf483474"><img title="This PR doesn't change the coverage rate in subliminal/matches.py, which is 94.73% (54/57)." src="https://img.shields.io/badge/94%25-%2854/57%20%3E%2054/57%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-05acc97a63b03d5d3f0a2f95c60c8f01e6a580c85499998d18cdd734bf483474"><img title="This PR does not seem to add statements to subliminal/matches.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-af7ce8eb89954393f7b074419655b57336c009531566a5a2ffc8a15c3196b9c6">score.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-af7ce8eb89954393f7b074419655b57336c009531566a5a2ffc8a15c3196b9c6"><img title="This PR doesn't change the number of statements in subliminal/score.py, which is 84." src="https://img.shields.io/badge/84-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-af7ce8eb89954393f7b074419655b57336c009531566a5a2ffc8a15c3196b9c6"><img title="This PR doesn't change the number of statements missing coverage in subliminal/score.py, which is 18." src="https://img.shields.io/badge/18-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-af7ce8eb89954393f7b074419655b57336c009531566a5a2ffc8a15c3196b9c6"><img title="This PR doesn't change the coverage rate in subliminal/score.py, which is 78.57% (66/84)." src="https://img.shields.io/badge/78%25-%2866/84%20%3E%2066/84%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-af7ce8eb89954393f7b074419655b57336c009531566a5a2ffc8a15c3196b9c6"><img title="This PR does not seem to add statements to subliminal/score.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td colspan="6">&nbsp;&nbsp;<b>subliminal/converters</b></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-1748b23b56ff6cfca72749bb88d0b2fe2e2b41a79e24ce5a64999c4ed3981760">__init__.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-1748b23b56ff6cfca72749bb88d0b2fe2e2b41a79e24ce5a64999c4ed3981760"><img title="This PR doesn't change the number of statements in subliminal/converters/__init__.py, which is 1." src="https://img.shields.io/badge/1-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-1748b23b56ff6cfca72749bb88d0b2fe2e2b41a79e24ce5a64999c4ed3981760"><img title="This PR doesn't change the number of statements missing coverage in subliminal/converters/__init__.py, which is 0." src="https://img.shields.io/badge/0-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-1748b23b56ff6cfca72749bb88d0b2fe2e2b41a79e24ce5a64999c4ed3981760"><img title="This PR doesn't change the coverage rate in subliminal/converters/__init__.py, which is 100% (1/1)." src="https://img.shields.io/badge/100%25-%281/1%20%3E%201/1%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-1748b23b56ff6cfca72749bb88d0b2fe2e2b41a79e24ce5a64999c4ed3981760"><img title="This PR does not seem to add statements to subliminal/converters/__init__.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-8b98454653594731a4121305829c89346713acb3c1adbd7c0296017087c45478">addic7ed.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-8b98454653594731a4121305829c89346713acb3c1adbd7c0296017087c45478"><img title="This PR doesn't change the number of statements in subliminal/converters/addic7ed.py, which is 21." src="https://img.shields.io/badge/21-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-8b98454653594731a4121305829c89346713acb3c1adbd7c0296017087c45478"><img title="This PR doesn't change the number of statements missing coverage in subliminal/converters/addic7ed.py, which is 0." src="https://img.shields.io/badge/0-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-8b98454653594731a4121305829c89346713acb3c1adbd7c0296017087c45478"><img title="This PR doesn't change the coverage rate in subliminal/converters/addic7ed.py, which is 100% (21/21)." src="https://img.shields.io/badge/100%25-%2821/21%20%3E%2021/21%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-8b98454653594731a4121305829c89346713acb3c1adbd7c0296017087c45478"><img title="This PR does not seem to add statements to subliminal/converters/addic7ed.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-fd64f630ecf8ae0b46ba0d09f494e7034a8cdffe8b16155697f4265230249a3f">opensubtitlescom.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-fd64f630ecf8ae0b46ba0d09f494e7034a8cdffe8b16155697f4265230249a3f"><img title="This PR doesn't change the number of statements in subliminal/converters/opensubtitlescom.py, which is 20." src="https://img.shields.io/badge/20-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-fd64f630ecf8ae0b46ba0d09f494e7034a8cdffe8b16155697f4265230249a3f"><img title="This PR doesn't change the number of statements missing coverage in subliminal/converters/opensubtitlescom.py, which is 1." src="https://img.shields.io/badge/1-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-fd64f630ecf8ae0b46ba0d09f494e7034a8cdffe8b16155697f4265230249a3f"><img title="This PR doesn't change the coverage rate in subliminal/converters/opensubtitlescom.py, which is 95% (19/20)." src="https://img.shields.io/badge/95%25-%2819/20%20%3E%2019/20%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-fd64f630ecf8ae0b46ba0d09f494e7034a8cdffe8b16155697f4265230249a3f"><img title="This PR does not seem to add statements to subliminal/converters/opensubtitlescom.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-199b9cef91014282bc57f1987e087e72c899260fcb4a89b31262018195945dc0">tvsubtitles.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-199b9cef91014282bc57f1987e087e72c899260fcb4a89b31262018195945dc0"><img title="This PR doesn't change the number of statements in subliminal/converters/tvsubtitles.py, which is 19." src="https://img.shields.io/badge/19-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-199b9cef91014282bc57f1987e087e72c899260fcb4a89b31262018195945dc0"><img title="This PR doesn't change the number of statements missing coverage in subliminal/converters/tvsubtitles.py, which is 0." src="https://img.shields.io/badge/0-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-199b9cef91014282bc57f1987e087e72c899260fcb4a89b31262018195945dc0"><img title="This PR doesn't change the coverage rate in subliminal/converters/tvsubtitles.py, which is 100% (19/19)." src="https://img.shields.io/badge/100%25-%2819/19%20%3E%2019/19%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-199b9cef91014282bc57f1987e087e72c899260fcb4a89b31262018195945dc0"><img title="This PR does not seem to add statements to subliminal/converters/tvsubtitles.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td colspan="6">&nbsp;&nbsp;<b>subliminal/providers</b></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-27188bda17c190035d4321774831a4f501fa48ed6195a4305e55ceee0b7145b8">__init__.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-27188bda17c190035d4321774831a4f501fa48ed6195a4305e55ceee0b7145b8"><img title="This PR doesn't change the number of statements in subliminal/providers/__init__.py, which is 74." src="https://img.shields.io/badge/74-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-27188bda17c190035d4321774831a4f501fa48ed6195a4305e55ceee0b7145b8"><img title="This PR doesn't change the number of statements missing coverage in subliminal/providers/__init__.py, which is 1." src="https://img.shields.io/badge/1-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-27188bda17c190035d4321774831a4f501fa48ed6195a4305e55ceee0b7145b8"><img title="This PR doesn't change the coverage rate in subliminal/providers/__init__.py, which is 98.64% (73/74)." src="https://img.shields.io/badge/98%25-%2873/74%20%3E%2073/74%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-27188bda17c190035d4321774831a4f501fa48ed6195a4305e55ceee0b7145b8"><img title="This PR does not seem to add statements to subliminal/providers/__init__.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-f141a3afa0ba7a3efe79543e07bdf6a0605538b026467c1efd2a39f47356ec0e">addic7ed.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-f141a3afa0ba7a3efe79543e07bdf6a0605538b026467c1efd2a39f47356ec0e"><img title="This PR doesn't change the number of statements in subliminal/providers/addic7ed.py, which is 302." src="https://img.shields.io/badge/302-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-f141a3afa0ba7a3efe79543e07bdf6a0605538b026467c1efd2a39f47356ec0e"><img title="This PR doesn't change the number of statements missing coverage in subliminal/providers/addic7ed.py, which is 103." src="https://img.shields.io/badge/103-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-f141a3afa0ba7a3efe79543e07bdf6a0605538b026467c1efd2a39f47356ec0e"><img title="This PR doesn't change the coverage rate in subliminal/providers/addic7ed.py, which is 65.89% (199/302)." src="https://img.shields.io/badge/65%25-%28199/302%20%3E%20199/302%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-f141a3afa0ba7a3efe79543e07bdf6a0605538b026467c1efd2a39f47356ec0e"><img title="This PR does not seem to add statements to subliminal/providers/addic7ed.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-7bd9ab5864f01e29fd607323707c0114caa96347df540e0926ce3e4f664ee0d8">gestdown.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-7bd9ab5864f01e29fd607323707c0114caa96347df540e0926ce3e4f664ee0d8"><img title="This PR doesn't change the number of statements in subliminal/providers/gestdown.py, which is 184." src="https://img.shields.io/badge/184-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-7bd9ab5864f01e29fd607323707c0114caa96347df540e0926ce3e4f664ee0d8"><img title="This PR doesn't change the number of statements missing coverage in subliminal/providers/gestdown.py, which is 31." src="https://img.shields.io/badge/31-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-7bd9ab5864f01e29fd607323707c0114caa96347df540e0926ce3e4f664ee0d8"><img title="This PR doesn't change the coverage rate in subliminal/providers/gestdown.py, which is 83.15% (153/184)." src="https://img.shields.io/badge/83%25-%28153/184%20%3E%20153/184%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-7bd9ab5864f01e29fd607323707c0114caa96347df540e0926ce3e4f664ee0d8"><img title="This PR does not seem to add statements to subliminal/providers/gestdown.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-e31edb2fe13483cba21eaa3c27b3e05bfc269238644e194d0010255eaa1dae53">napiprojekt.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-e31edb2fe13483cba21eaa3c27b3e05bfc269238644e194d0010255eaa1dae53"><img title="This PR doesn't change the number of statements in subliminal/providers/napiprojekt.py, which is 85." src="https://img.shields.io/badge/85-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-e31edb2fe13483cba21eaa3c27b3e05bfc269238644e194d0010255eaa1dae53"><img title="This PR doesn't change the number of statements missing coverage in subliminal/providers/napiprojekt.py, which is 8." src="https://img.shields.io/badge/8-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-e31edb2fe13483cba21eaa3c27b3e05bfc269238644e194d0010255eaa1dae53"><img title="This PR doesn't change the coverage rate in subliminal/providers/napiprojekt.py, which is 90.58% (77/85)." src="https://img.shields.io/badge/90%25-%2877/85%20%3E%2077/85%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-e31edb2fe13483cba21eaa3c27b3e05bfc269238644e194d0010255eaa1dae53"><img title="This PR does not seem to add statements to subliminal/providers/napiprojekt.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-96a3f6029b74b809eab30b339cd6c66d0821fe44c7fd5481635a1315b29919bb">opensubtitles.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-96a3f6029b74b809eab30b339cd6c66d0821fe44c7fd5481635a1315b29919bb"><img title="This PR doesn't change the number of statements in subliminal/providers/opensubtitles.py, which is 219." src="https://img.shields.io/badge/219-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-96a3f6029b74b809eab30b339cd6c66d0821fe44c7fd5481635a1315b29919bb"><img title="This PR doesn't change the number of statements missing coverage in subliminal/providers/opensubtitles.py, which is 20." src="https://img.shields.io/badge/20-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-96a3f6029b74b809eab30b339cd6c66d0821fe44c7fd5481635a1315b29919bb"><img title="This PR doesn't change the coverage rate in subliminal/providers/opensubtitles.py, which is 90.86% (199/219)." src="https://img.shields.io/badge/90%25-%28199/219%20%3E%20199/219%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-96a3f6029b74b809eab30b339cd6c66d0821fe44c7fd5481635a1315b29919bb"><img title="This PR does not seem to add statements to subliminal/providers/opensubtitles.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-0cde4f41be2242117fa48aa152cd6e1a618ecdb8f3d5a58a6d93a4750754c6e7">opensubtitlescom.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-0cde4f41be2242117fa48aa152cd6e1a618ecdb8f3d5a58a6d93a4750754c6e7"><img title="This PR doesn't change the number of statements in subliminal/providers/opensubtitlescom.py, which is 406." src="https://img.shields.io/badge/406-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-0cde4f41be2242117fa48aa152cd6e1a618ecdb8f3d5a58a6d93a4750754c6e7"><img title="This PR doesn't change the number of statements missing coverage in subliminal/providers/opensubtitlescom.py, which is 58." src="https://img.shields.io/badge/58-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-0cde4f41be2242117fa48aa152cd6e1a618ecdb8f3d5a58a6d93a4750754c6e7"><img title="This PR doesn't change the coverage rate in subliminal/providers/opensubtitlescom.py, which is 85.71% (348/406)." src="https://img.shields.io/badge/85%25-%28348/406%20%3E%20348/406%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-0cde4f41be2242117fa48aa152cd6e1a618ecdb8f3d5a58a6d93a4750754c6e7"><img title="This PR does not seem to add statements to subliminal/providers/opensubtitlescom.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-c2f819aacadb93d2d7a738fd8775325ed3db9695886d8ad2c176001093d1f0fa">podnapisi.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-c2f819aacadb93d2d7a738fd8775325ed3db9695886d8ad2c176001093d1f0fa"><img title="This PR doesn't change the number of statements in subliminal/providers/podnapisi.py, which is 127." src="https://img.shields.io/badge/127-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-c2f819aacadb93d2d7a738fd8775325ed3db9695886d8ad2c176001093d1f0fa"><img title="This PR doesn't change the number of statements missing coverage in subliminal/providers/podnapisi.py, which is 14." src="https://img.shields.io/badge/14-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-c2f819aacadb93d2d7a738fd8775325ed3db9695886d8ad2c176001093d1f0fa"><img title="This PR doesn't change the coverage rate in subliminal/providers/podnapisi.py, which is 88.97% (113/127)." src="https://img.shields.io/badge/88%25-%28113/127%20%3E%20113/127%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-c2f819aacadb93d2d7a738fd8775325ed3db9695886d8ad2c176001093d1f0fa"><img title="This PR does not seem to add statements to subliminal/providers/podnapisi.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-a87521a232b09117c0a23c2ee0715569851444e1f2b89118223f24a7315058f2">tvsubtitles.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-a87521a232b09117c0a23c2ee0715569851444e1f2b89118223f24a7315058f2"><img title="This PR doesn't change the number of statements in subliminal/providers/tvsubtitles.py, which is 166." src="https://img.shields.io/badge/166-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-a87521a232b09117c0a23c2ee0715569851444e1f2b89118223f24a7315058f2"><img title="This PR doesn't change the number of statements missing coverage in subliminal/providers/tvsubtitles.py, which is 15." src="https://img.shields.io/badge/15-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-a87521a232b09117c0a23c2ee0715569851444e1f2b89118223f24a7315058f2"><img title="This PR doesn't change the coverage rate in subliminal/providers/tvsubtitles.py, which is 90.96% (151/166)." src="https://img.shields.io/badge/90%25-%28151/166%20%3E%20151/166%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-a87521a232b09117c0a23c2ee0715569851444e1f2b89118223f24a7315058f2"><img title="This PR does not seem to add statements to subliminal/providers/tvsubtitles.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td colspan="6">&nbsp;&nbsp;<b>subliminal/refiners</b></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-fcb081342f742f72b82fb53209b9056620bd37895756c633461b9068a66c8428">__init__.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-fcb081342f742f72b82fb53209b9056620bd37895756c633461b9068a66c8428"><img title="This PR doesn't change the number of statements in subliminal/refiners/__init__.py, which is 0." src="https://img.shields.io/badge/0-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-fcb081342f742f72b82fb53209b9056620bd37895756c633461b9068a66c8428"><img title="This PR doesn't change the number of statements missing coverage in subliminal/refiners/__init__.py, which is 0." src="https://img.shields.io/badge/0-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-fcb081342f742f72b82fb53209b9056620bd37895756c633461b9068a66c8428"><img title="This PR doesn't change the coverage rate in subliminal/refiners/__init__.py, which is 100% (0/0)." src="https://img.shields.io/badge/100%25-%280/0%20%3E%200/0%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-fcb081342f742f72b82fb53209b9056620bd37895756c633461b9068a66c8428"><img title="This PR does not seem to add statements to subliminal/refiners/__init__.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-8bab5e962f0c947c1db90473a10c8b7c7605ee00465a6818662794b9b8fbda43">hash.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-8bab5e962f0c947c1db90473a10c8b7c7605ee00465a6818662794b9b8fbda43"><img title="This PR removes 21 from the number of statements in subliminal/refiners/hash.py, taking it from 74 to 53." src="https://img.shields.io/badge/53-%28--21%29-49c2ee.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-8bab5e962f0c947c1db90473a10c8b7c7605ee00465a6818662794b9b8fbda43"><img title="This PR removes 11 from the number of statements missing coverage in subliminal/refiners/hash.py, taking it from 32 to 21." src="https://img.shields.io/badge/21-%28--11%29-brightgreen.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-8bab5e962f0c947c1db90473a10c8b7c7605ee00465a6818662794b9b8fbda43"><img title="This PR adds 3.62 percentage points to the coverage rate in subliminal/refiners/hash.py, taking it from 56.75% (42/74) to 60.37% (32/53)." src="https://img.shields.io/badge/60%25-%2842/74%20%3E%2032/53%29-brightgreen.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-8bab5e962f0c947c1db90473a10c8b7c7605ee00465a6818662794b9b8fbda43"><img title="In this PR, 2 new statements are added to subliminal/refiners/hash.py, 2 of which are covered (100%)." src="https://img.shields.io/badge/100%25-%282/2%29-brightgreen.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-14760de934852b23e169bf3be8345f83b48e99ccf117a2936752d48fc7c63ea9">metadata.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-14760de934852b23e169bf3be8345f83b48e99ccf117a2936752d48fc7c63ea9"><img title="This PR doesn't change the number of statements in subliminal/refiners/metadata.py, which is 68." src="https://img.shields.io/badge/68-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-14760de934852b23e169bf3be8345f83b48e99ccf117a2936752d48fc7c63ea9"><img title="This PR doesn't change the number of statements missing coverage in subliminal/refiners/metadata.py, which is 28." src="https://img.shields.io/badge/28-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-14760de934852b23e169bf3be8345f83b48e99ccf117a2936752d48fc7c63ea9"><img title="This PR doesn't change the coverage rate in subliminal/refiners/metadata.py, which is 58.82% (40/68)." src="https://img.shields.io/badge/58%25-%2840/68%20%3E%2040/68%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-14760de934852b23e169bf3be8345f83b48e99ccf117a2936752d48fc7c63ea9"><img title="This PR does not seem to add statements to subliminal/refiners/metadata.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-d19ca0cbe0efd3725cc2c7cb5501575fe1329e8ad88c38ae7cdaa6ce7c21aa91">omdb.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-d19ca0cbe0efd3725cc2c7cb5501575fe1329e8ad88c38ae7cdaa6ce7c21aa91"><img title="This PR doesn't change the number of statements in subliminal/refiners/omdb.py, which is 167." src="https://img.shields.io/badge/167-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-d19ca0cbe0efd3725cc2c7cb5501575fe1329e8ad88c38ae7cdaa6ce7c21aa91"><img title="This PR doesn't change the number of statements missing coverage in subliminal/refiners/omdb.py, which is 34." src="https://img.shields.io/badge/34-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-d19ca0cbe0efd3725cc2c7cb5501575fe1329e8ad88c38ae7cdaa6ce7c21aa91"><img title="This PR doesn't change the coverage rate in subliminal/refiners/omdb.py, which is 79.64% (133/167)." src="https://img.shields.io/badge/79%25-%28133/167%20%3E%20133/167%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-d19ca0cbe0efd3725cc2c7cb5501575fe1329e8ad88c38ae7cdaa6ce7c21aa91"><img title="This PR does not seem to add statements to subliminal/refiners/omdb.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-2285d7fa0ea5e1f2e998429a94b983c96d3c14fa2184caca8e60d609257aef8c">tmdb.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-2285d7fa0ea5e1f2e998429a94b983c96d3c14fa2184caca8e60d609257aef8c"><img title="This PR doesn't change the number of statements in subliminal/refiners/tmdb.py, which is 164." src="https://img.shields.io/badge/164-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-2285d7fa0ea5e1f2e998429a94b983c96d3c14fa2184caca8e60d609257aef8c"><img title="This PR doesn't change the number of statements missing coverage in subliminal/refiners/tmdb.py, which is 27." src="https://img.shields.io/badge/27-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-2285d7fa0ea5e1f2e998429a94b983c96d3c14fa2184caca8e60d609257aef8c"><img title="This PR doesn't change the coverage rate in subliminal/refiners/tmdb.py, which is 83.53% (137/164)." src="https://img.shields.io/badge/83%25-%28137/164%20%3E%20137/164%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-2285d7fa0ea5e1f2e998429a94b983c96d3c14fa2184caca8e60d609257aef8c"><img title="This PR does not seem to add statements to subliminal/refiners/tmdb.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td><tr>
<td>&nbsp;&nbsp;<a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-3f8a3133c798926ee998f3123661b9fd28743b8ab002f9f93e80d7b19b7d4088">tvdb.py</a></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-3f8a3133c798926ee998f3123661b9fd28743b8ab002f9f93e80d7b19b7d4088"><img title="This PR doesn't change the number of statements in subliminal/refiners/tvdb.py, which is 225." src="https://img.shields.io/badge/225-%28%C2%B10%29-5d89ba.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-3f8a3133c798926ee998f3123661b9fd28743b8ab002f9f93e80d7b19b7d4088"><img title="This PR doesn't change the number of statements missing coverage in subliminal/refiners/tvdb.py, which is 27." src="https://img.shields.io/badge/27-%28%C2%B10%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-3f8a3133c798926ee998f3123661b9fd28743b8ab002f9f93e80d7b19b7d4088"><img title="This PR doesn't change the coverage rate in subliminal/refiners/tvdb.py, which is 88% (198/225)." src="https://img.shields.io/badge/88%25-%28198/225%20%3E%20198/225%29-lightgrey.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-3f8a3133c798926ee998f3123661b9fd28743b8ab002f9f93e80d7b19b7d4088"><img title="This PR does not seem to add statements to subliminal/refiners/tvdb.py." src="https://img.shields.io/badge/N/A-grey.svg"></a></td><td></td></tbody>
<tfoot>
<tr>
<td><b>Project Total</b></td>

<td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-4b0bf2efa3367c0072ac2bf1e234e703dc46b47aaa4fe9d3b01737b1a15752b1"><img title="This PR removes 61 from the number of statements in the whole project, taking it from 3387 to 3326." src="https://img.shields.io/badge/3.3k-%28--61%29-49c2ee.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-4b0bf2efa3367c0072ac2bf1e234e703dc46b47aaa4fe9d3b01737b1a15752b1"><img title="This PR removes 51 from the number of statements missing coverage in the whole project, taking it from 560 to 509." src="https://img.shields.io/badge/509-%28--51%29-brightgreen.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-4b0bf2efa3367c0072ac2bf1e234e703dc46b47aaa4fe9d3b01737b1a15752b1"><img title="This PR adds 1.23 percentage points to the coverage rate in the whole project, taking it from 83.46% (2827/3387) to 84.69% (2817/3326)." src="https://img.shields.io/badge/84%25-%282.8k/3.4k%20%3E%202.8k/3.3k%29-brightgreen.svg"></a></td><td align="center"><a href="https://github.com/Diaoul/subliminal/pull/1156/files#diff-4b0bf2efa3367c0072ac2bf1e234e703dc46b47aaa4fe9d3b01737b1a15752b1"><img title="In this PR, 2 new statements are added to the whole project, 2 of which are covered (100%)." src="https://img.shields.io/badge/100%25-%282/2%29-brightgreen.svg"></a></td><td>&nbsp;</td>
</tr>
</tfoot>
</table>

_The report is truncated to 25 files out of 28. To see the full report, please visit the workflow summary page._



<sub>

This report was generated by [python-coverage-comment-action](https://github.com/py-cov-action/python-coverage-comment-action)

</sub>
</details>



<!-- This comment was produced by python-coverage-comment-action -->

Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e76db70..f776cf2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -82,7 +82,7 @@ Change into the newly created directory and after activating a virtual environme
 ```console
 $ cd subliminal
 $ python -m pip install --upgrade pip wheel  # PLEASE don't skip this step
-$ python -m pip install -e '.[dev]'
+$ python -m pip install -e '.[docs,test,dev]'  # you can omit `docs` if you are not planning to build docs
 ```
 
 At this point,
diff --git a/changelog.d/1157.doc.rst b/changelog.d/1157.doc.rst
new file mode 100644
index 0000000..047d10f
--- /dev/null
+++ b/changelog.d/1157.doc.rst
@@ -0,0 +1,1 @@
+Add installing docs and test dependencies to contributing docs
diff --git a/docs/user/usage.rst b/docs/user/usage.rst
index d531598..cfaeab3 100644
--- a/docs/user/usage.rst
+++ b/docs/user/usage.rst
@@ -162,3 +162,18 @@ We got ourselves a nice subtitle, now we can save it on the file system using :f
     [<PodnapisiSubtitle 'ZtAW' [hu]>]
     >>> 'The.Big.Bang.Theory.S05E18.HDTV.x264-LOL.hu.srt' in os.listdir()
     True
+
+
+    .. testcleanup::
+
+        import os
+
+        downloaded = [
+            'The.Big.Bang.Theory.S05E18.HDTV.x264-LOL.hu.srt',
+            'The.Big.Bang.Theory.S05E18.HDTV.x264-LOL.en.srt',
+            'The.Big.Bang.Theory.S05E18.HDTV.x264-LOL.srt',
+        ]
+        # Remove downloaded files
+        for f in downloaded:
+            if os.path.isfile(f):
+                os.remove(f)
diff --git a/pyproject.toml b/pyproject.toml
index 3d2613f..0c347f4 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -62,10 +62,8 @@ docs = [
 ]
 test = [
     "mypy",
-    "lxml",
     "pytest>=6.0",
     "pytest-cov",
-    "pytest-flakes",
     "pytest-xdist",
     "sympy",
     "vcrpy>=1.6.1",
@@ -131,6 +129,41 @@ version = {attr = "subliminal.__version__"}
 readme = {file = ["README.rst", "HISTORY.rst"]}
 
 
+# https://docs.pytest.org/en/6.2.x/customize.html
+[tool.pytest.ini_options]
+minversion = "6.0"
+testpaths = ["tests"]
+addopts = "--import-mode=importlib --doctest-glob='*.rst'"
+markers = [
+    "integration",
+    "converter",
+]
+doctest_optionflags = ["NORMALIZE_WHITESPACE", "IGNORE_EXCEPTION_DETAIL"]
+
+# https://coverage.readthedocs.io/en/latest/config.html
+[tool.coverage.report]
+exclude_also = [
+    "pragma: no.cover",
+    "if TYPE_CHECKING:",
+    "@overload",
+    "except ImportError",
+    "\\.\\.\\.",
+    "raise NotImplementedError()",
+    "if __name__ == .__main__.:",
+]
+show_missing = true
+skip_covered = true
+fail_under = 80
+omit = [
+    "subliminal/cli.py",
+]
+
+[tool.coverage.run]
+source = ["subliminal"]
+branch = true
+relative_files = true
+
+
 # https://docs.astral.sh/ruff/
 [tool.ruff]
 line-length = 120
@@ -185,37 +218,6 @@ ignore = [
 docstring-code-format = true
 quote-style = "single"
 
-# https://docs.pytest.org/en/6.2.x/customize.html
-[tool.pytest.ini_options]
-minversion = "6.0"
-testpaths = ["tests"]
-addopts = "--doctest-glob='*.rst'"
-markers = [
-    "integration",
-    "converter",
-]
-doctest_optionflags = ["NORMALIZE_WHITESPACE", "IGNORE_EXCEPTION_DETAIL"]
-
-# https://coverage.readthedocs.io/en/latest/config.html
-[tool.coverage.report]
-exclude_also = [
-    "pragma: no.cover",
-    "if TYPE_CHECKING:",
-    "@overload",
-    "except ImportError",
-    "\\.\\.\\.",
-    "raise NotImplementedError()",
-    "if __name__ == .__main__.:",
-]
-show_missing = true
-skip_covered = true
-fail_under = 80
-omit = ["subliminal/cli.py"]
-
-[tool.coverage.run]
-source = ["subliminal"]
-relative_files = true
-
 
 # https://towncrier.readthedocs.io/en/stable/tutorial.html
 [tool.towncrier]
diff --git a/subliminal/converters/shooter.py b/subliminal/converters/shooter.py
deleted file mode 100644
index 974f0a0..0000000
--- a/subliminal/converters/shooter.py
+++ /dev/null
@@ -1,37 +0,0 @@
-"""Language converter for Shooter."""
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from babelfish import LanguageReverseConverter  # type: ignore[import-untyped]
-
-from subliminal.exceptions import ConfigurationError
-
-if TYPE_CHECKING:
-    from . import LanguageTuple
-
-
-class ShooterConverter(LanguageReverseConverter):
-    """Language converter for Shooter."""
-
-    def __init__(self) -> None:
-        self.from_shooter: dict[str, LanguageTuple] = {'chn': ('zho',), 'eng': ('eng',)}
-        self.to_shooter: dict[LanguageTuple, str] = {v: k for k, v in self.from_shooter.items()}
-        self.codes = set(self.from_shooter.keys())
-
-    def convert(self, alpha3: str, country: str | None = None, script: str | None = None) -> str:
-        """Convert an alpha3 language code with an alpha2 country code and a script code into a custom code."""
-        if (alpha3,) in self.to_shooter:
-            return self.to_shooter[(alpha3,)]
-
-        msg = f'Unsupported language code for shooter: {(alpha3, country, script)}'
-        raise ConfigurationError(msg)
-
-    def reverse(self, code: str) -> LanguageTuple:
-        """Reverse a custom code into alpha3, country and script code."""
-        if code in self.from_shooter:
-            return self.from_shooter[code]
-
-        msg = f'Unsupported language code for shooter: {code}'
-        raise ConfigurationError(msg)
diff --git a/subliminal/converters/thesubdb.py b/subliminal/converters/thesubdb.py
deleted file mode 100644
index 2c2b61f..0000000
--- a/subliminal/converters/thesubdb.py
+++ /dev/null
@@ -1,50 +0,0 @@
-"""Language converter for TheSubDB."""
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-from babelfish import LanguageReverseConverter  # type: ignore[import-untyped]
-
-from subliminal.exceptions import ConfigurationError
-
-if TYPE_CHECKING:
-    from . import LanguageTuple
-
-
-class TheSubDBConverter(LanguageReverseConverter):
-    """Language converter for TheSubDB."""
-
-    def __init__(self) -> None:
-        self.from_thesubdb: dict[str, LanguageTuple] = {
-            'en': ('eng',),
-            'es': ('spa',),
-            'fr': ('fra',),
-            'it': ('ita',),
-            'nl': ('nld',),
-            'pl': ('pol',),
-            'pt': ('por', 'BR'),
-            'ro': ('ron',),
-            'sv': ('swe',),
-            'tr': ('tur',),
-        }
-        self.to_thesubdb: dict[LanguageTuple, str] = {v: k for k, v in self.from_thesubdb.items()}
-        self.codes = set(self.from_thesubdb.keys())
-
-    def convert(self, alpha3: str, country: str | None = None, script: str | None = None) -> str:
-        """Convert an alpha3 language code with an alpha2 country code and a script code into a custom code."""
-        if (alpha3, country) in self.to_thesubdb:
-            return self.to_thesubdb[(alpha3, country)]
-        if (alpha3,) in self.to_thesubdb:
-            return self.to_thesubdb[(alpha3,)]
-
-        msg = f'Unsupported language code for shooter: {(alpha3, country, script)}'
-        raise ConfigurationError(msg)
-
-    def reverse(self, code: str) -> LanguageTuple:
-        """Reverse a custom code into alpha3, country and script code."""
-        if code in self.from_thesubdb:
-            return self.from_thesubdb[code]
-
-        msg = f'Unsupported language code for thesubdb: {code}'
-        raise ConfigurationError(msg)
diff --git a/subliminal/refiners/hash.py b/subliminal/refiners/hash.py
index 31357ed..a992b55 100644
--- a/subliminal/refiners/hash.py
+++ b/subliminal/refiners/hash.py
@@ -53,7 +53,7 @@ def hash_opensubtitles(video_path: str | os.PathLike) -> str | None:
     return f'{filehash:016x}'
 
 
-def hash_thesubdb(video_path: str | os.PathLike) -> str | None:
+def hash_thesubdb(video_path: str | os.PathLike) -> str | None:  # pragma: no cover
     """Compute a hash using TheSubDB's algorithm.
 
     :param str video_path: path of the video.
@@ -86,7 +86,7 @@ def hash_napiprojekt(video_path: str | os.PathLike) -> str | None:
     return hashlib.md5(data).hexdigest()  # noqa: S324
 
 
-def hash_shooter(video_path: str | os.PathLike) -> str | None:
+def hash_shooter(video_path: str | os.PathLike) -> str | None:  # pragma: no cover
     """Compute a hash using Shooter's algorithm.
 
     :param string video_path: path of the video
diff --git a/tox.ini b/tox.ini
index b6497b5..70222da 100644
--- a/tox.ini
+++ b/tox.ini
@@ -2,32 +2,43 @@
 min_version = 4
 env_list =
     pre-commit,
-    py3{9,10,11,12}-tests,
-    changelog,
-    pre-commit,
+    mypy,
+    py3{9,12}-tests,
+    coverage,
     docs,
-    docs-linkcheck,
+    changelog,
+
 
 [testenv:.pkg]
 pass_env = SETUPTOOLS_SCM_PRETEND_VERSION
 
+
 [testenv]
 package = wheel
 wheel_build_env = .pkg
-extras =
-    tests: test
-    coverage: test
-    mypy: test
-commands =
-    tests: pytest {posargs:-n auto}
-    coverage: pytest --cov --cov-report term {posargs:-n auto}
-    mypy: mypy --install-types --non-interactive subliminal tests
+extras = test
+
+
+[testenv:tests]
+commands = pytest {posargs:-n auto}
+
+
+[testenv:mypy]
+commands = mypy --install-types --non-interactive {posargs:subliminal tests}
+
+
+[testenv:coverage]
+usedevelop = true
+deps = coverage[toml]>=7
+set_env =
+    COVERAGE_PROCESS_START = pyproject.toml
+commands = pytest --cov --cov-report=term-missing:skip-covered {posargs:-n auto}
 
 
 [testenv:pre-commit]
 description =
     run pre-commit-defined linters under `{basepython}`
-skip_install = True
+skip_install = true
 basepython = python3
 deps = pre-commit>=2.9.3
 commands = pre-commit run --all-files --show-diff-on-failure {posargs:}
@@ -37,18 +48,17 @@ setenv =
 
 
 [testenv:docs]
+package = wheel
+wheel_build_env = .pkg
 # Keep base_python in-sync with check.yaml/docs and .readthedocs.yaml.
 base_python = py312
 extras = docs
+allowlist_externals = sphinx-build
 commands =
     sphinx-build -n -T -W --keep-going -b html -d {envtmpdir}/doctrees docs docs/_build/html
     sphinx-build -n -T -W --keep-going -b doctest -d {envtmpdir}/doctrees docs docs/_build/html
+    sphinx-build -n -T -W --keep-going -b linkcheck -d {envtmpdir}/doctrees docs docs/_build/html
 
-[testenv:docs-linkcheck]
-package = editable
-base_python = {[testenv:docs]base_python}
-extras = {[testenv:docs]extras}
-commands = sphinx-build -W --keep-going -b linkcheck -d {envtmpdir}/doctrees docs docs/_build/html
 
 [testenv:changelog]
 extras = docs


Test patch:
diff --git a/tests/cassettes/tvdb/test_refine_ambiguous.yaml b/tests/cassettes/tvdb/test_refine_ambiguous.yaml
index b28cfd4..17d1d4b 100644
--- a/tests/cassettes/tvdb/test_refine_ambiguous.yaml
+++ b/tests/cassettes/tvdb/test_refine_ambiguous.yaml
@@ -1,4 +1,52 @@
 interactions:
+- request:
+    body: '{"apikey": "5EC930FB90DA1ADA", "username": null, "password": null}'
+    headers:
+      Accept:
+      - '*/*'
+      Accept-Encoding:
+      - gzip, deflate, br, zstd
+      Accept-Language:
+      - en
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '66'
+      Content-Type:
+      - application/json
+      User-Agent:
+      - Subliminal/2.1
+    method: POST
+    uri: https://api.thetvdb.com/login
+  response:
+    body:
+      string: '{"token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTY5OTMyMTQsImlkIjoic3VibGltaW5hbCIsIm9yaWdfaWF0IjoxNzE2Mzg4NDE0fQ.dJAo_FBEcSnkDtgNp-g_mZjEyYoW1MHhgAb-IWuwL3OIhiI3Ga0QAn0MbhX6o88BtmVsoHsWoRD1dFYGeNyO4AK8JUOYV4zwUBpZzxr2dBCd02cEDvbOnA9yLaYmhpUAyN86p3hWKLKlLTBrctBQD9obkMY8pIFoeHNS0SfkQD6PMnjsHdqtpjab0IoH2cFpgm45HhAq78XhEOPBAX45WjlsP-wXrF4H49nzAL_bFButDyS_cCYdYcqaTv4J65OyDJ0VQpA4aUp4T-_6WjwnZ2d0WtcPSaXGX0cDiGFSQMptR7atojVlRJD30ZaZU_wrAfpWGcaoip22xeEx_D0DJg"}'
+    headers:
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '470'
+      Content-Type:
+      - application/json; charset=utf-8
+      Date:
+      - Wed, 22 May 2024 14:33:34 GMT
+      Vary:
+      - Accept-Language
+      Via:
+      - 1.1 2b1fd1e1421ca124eaa002817c6c475a.cloudfront.net (CloudFront)
+      X-Amz-Cf-Id:
+      - ZRlGvJMw9l8m8F_5_ZcUkEpo4bbvjXOwoVTOWqy_WubSgl5jBugmFQ==
+      X-Amz-Cf-Pop:
+      - LIS50-P1
+      X-Cache:
+      - Miss from cloudfront
+      X-Powered-By:
+      - Thundar!
+      X-Thetvdb-Api-Version:
+      - 3.0.0
+    status:
+      code: 200
+      message: OK
 - request:
     body: null
     headers:
diff --git a/tests/cassettes/tvdb/test_refine_ambiguous_2.yaml b/tests/cassettes/tvdb/test_refine_ambiguous_2.yaml
index 3ffdb73..62b9b1e 100644
--- a/tests/cassettes/tvdb/test_refine_ambiguous_2.yaml
+++ b/tests/cassettes/tvdb/test_refine_ambiguous_2.yaml
@@ -1,4 +1,52 @@
 interactions:
+- request:
+    body: '{"apikey": "5EC930FB90DA1ADA", "username": null, "password": null}'
+    headers:
+      Accept:
+      - '*/*'
+      Accept-Encoding:
+      - gzip, deflate, br, zstd
+      Accept-Language:
+      - en
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '66'
+      Content-Type:
+      - application/json
+      User-Agent:
+      - Subliminal/2.1
+    method: POST
+    uri: https://api.thetvdb.com/login
+  response:
+    body:
+      string: '{"token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTY5OTMyMTQsImlkIjoic3VibGltaW5hbCIsIm9yaWdfaWF0IjoxNzE2Mzg4NDE0fQ.dJAo_FBEcSnkDtgNp-g_mZjEyYoW1MHhgAb-IWuwL3OIhiI3Ga0QAn0MbhX6o88BtmVsoHsWoRD1dFYGeNyO4AK8JUOYV4zwUBpZzxr2dBCd02cEDvbOnA9yLaYmhpUAyN86p3hWKLKlLTBrctBQD9obkMY8pIFoeHNS0SfkQD6PMnjsHdqtpjab0IoH2cFpgm45HhAq78XhEOPBAX45WjlsP-wXrF4H49nzAL_bFButDyS_cCYdYcqaTv4J65OyDJ0VQpA4aUp4T-_6WjwnZ2d0WtcPSaXGX0cDiGFSQMptR7atojVlRJD30ZaZU_wrAfpWGcaoip22xeEx_D0DJg"}'
+    headers:
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '470'
+      Content-Type:
+      - application/json; charset=utf-8
+      Date:
+      - Wed, 22 May 2024 14:33:34 GMT
+      Vary:
+      - Accept-Language
+      Via:
+      - 1.1 2b1fd1e1421ca124eaa002817c6c475a.cloudfront.net (CloudFront)
+      X-Amz-Cf-Id:
+      - ZRlGvJMw9l8m8F_5_ZcUkEpo4bbvjXOwoVTOWqy_WubSgl5jBugmFQ==
+      X-Amz-Cf-Pop:
+      - LIS50-P1
+      X-Cache:
+      - Miss from cloudfront
+      X-Powered-By:
+      - Thundar!
+      X-Thetvdb-Api-Version:
+      - 3.0.0
+    status:
+      code: 200
+      message: OK
 - request:
     body: null
     headers:
diff --git a/tests/cassettes/tvdb/test_refine_episode_alternative_series.yaml b/tests/cassettes/tvdb/test_refine_episode_alternative_series.yaml
index 5e88a43..0623e37 100644
--- a/tests/cassettes/tvdb/test_refine_episode_alternative_series.yaml
+++ b/tests/cassettes/tvdb/test_refine_episode_alternative_series.yaml
@@ -1,4 +1,52 @@
 interactions:
+- request:
+    body: '{"apikey": "5EC930FB90DA1ADA", "username": null, "password": null}'
+    headers:
+      Accept:
+      - '*/*'
+      Accept-Encoding:
+      - gzip, deflate, br, zstd
+      Accept-Language:
+      - en
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '66'
+      Content-Type:
+      - application/json
+      User-Agent:
+      - Subliminal/2.1
+    method: POST
+    uri: https://api.thetvdb.com/login
+  response:
+    body:
+      string: '{"token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTY5OTMyMTQsImlkIjoic3VibGltaW5hbCIsIm9yaWdfaWF0IjoxNzE2Mzg4NDE0fQ.dJAo_FBEcSnkDtgNp-g_mZjEyYoW1MHhgAb-IWuwL3OIhiI3Ga0QAn0MbhX6o88BtmVsoHsWoRD1dFYGeNyO4AK8JUOYV4zwUBpZzxr2dBCd02cEDvbOnA9yLaYmhpUAyN86p3hWKLKlLTBrctBQD9obkMY8pIFoeHNS0SfkQD6PMnjsHdqtpjab0IoH2cFpgm45HhAq78XhEOPBAX45WjlsP-wXrF4H49nzAL_bFButDyS_cCYdYcqaTv4J65OyDJ0VQpA4aUp4T-_6WjwnZ2d0WtcPSaXGX0cDiGFSQMptR7atojVlRJD30ZaZU_wrAfpWGcaoip22xeEx_D0DJg"}'
+    headers:
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '470'
+      Content-Type:
+      - application/json; charset=utf-8
+      Date:
+      - Wed, 22 May 2024 14:33:34 GMT
+      Vary:
+      - Accept-Language
+      Via:
+      - 1.1 2b1fd1e1421ca124eaa002817c6c475a.cloudfront.net (CloudFront)
+      X-Amz-Cf-Id:
+      - ZRlGvJMw9l8m8F_5_ZcUkEpo4bbvjXOwoVTOWqy_WubSgl5jBugmFQ==
+      X-Amz-Cf-Pop:
+      - LIS50-P1
+      X-Cache:
+      - Miss from cloudfront
+      X-Powered-By:
+      - Thundar!
+      X-Thetvdb-Api-Version:
+      - 3.0.0
+    status:
+      code: 200
+      message: OK
 - request:
     body: null
     headers:
diff --git a/tests/cassettes/tvdb/test_refine_episode_no_year.yaml b/tests/cassettes/tvdb/test_refine_episode_no_year.yaml
index b3f8117..867782d 100644
--- a/tests/cassettes/tvdb/test_refine_episode_no_year.yaml
+++ b/tests/cassettes/tvdb/test_refine_episode_no_year.yaml
@@ -1,4 +1,52 @@
 interactions:
+- request:
+    body: '{"apikey": "5EC930FB90DA1ADA", "username": null, "password": null}'
+    headers:
+      Accept:
+      - '*/*'
+      Accept-Encoding:
+      - gzip, deflate, br, zstd
+      Accept-Language:
+      - en
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '66'
+      Content-Type:
+      - application/json
+      User-Agent:
+      - Subliminal/2.1
+    method: POST
+    uri: https://api.thetvdb.com/login
+  response:
+    body:
+      string: '{"token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTY5OTMyMTQsImlkIjoic3VibGltaW5hbCIsIm9yaWdfaWF0IjoxNzE2Mzg4NDE0fQ.dJAo_FBEcSnkDtgNp-g_mZjEyYoW1MHhgAb-IWuwL3OIhiI3Ga0QAn0MbhX6o88BtmVsoHsWoRD1dFYGeNyO4AK8JUOYV4zwUBpZzxr2dBCd02cEDvbOnA9yLaYmhpUAyN86p3hWKLKlLTBrctBQD9obkMY8pIFoeHNS0SfkQD6PMnjsHdqtpjab0IoH2cFpgm45HhAq78XhEOPBAX45WjlsP-wXrF4H49nzAL_bFButDyS_cCYdYcqaTv4J65OyDJ0VQpA4aUp4T-_6WjwnZ2d0WtcPSaXGX0cDiGFSQMptR7atojVlRJD30ZaZU_wrAfpWGcaoip22xeEx_D0DJg"}'
+    headers:
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '470'
+      Content-Type:
+      - application/json; charset=utf-8
+      Date:
+      - Wed, 22 May 2024 14:33:34 GMT
+      Vary:
+      - Accept-Language
+      Via:
+      - 1.1 2b1fd1e1421ca124eaa002817c6c475a.cloudfront.net (CloudFront)
+      X-Amz-Cf-Id:
+      - ZRlGvJMw9l8m8F_5_ZcUkEpo4bbvjXOwoVTOWqy_WubSgl5jBugmFQ==
+      X-Amz-Cf-Pop:
+      - LIS50-P1
+      X-Cache:
+      - Miss from cloudfront
+      X-Powered-By:
+      - Thundar!
+      X-Thetvdb-Api-Version:
+      - 3.0.0
+    status:
+      code: 200
+      message: OK
 - request:
     body: null
     headers:
diff --git a/tests/cassettes/tvdb/test_refine_episode_partial.yaml b/tests/cassettes/tvdb/test_refine_episode_partial.yaml
index 762e764..b845195 100644
--- a/tests/cassettes/tvdb/test_refine_episode_partial.yaml
+++ b/tests/cassettes/tvdb/test_refine_episode_partial.yaml
@@ -1,4 +1,52 @@
 interactions:
+- request:
+    body: '{"apikey": "5EC930FB90DA1ADA", "username": null, "password": null}'
+    headers:
+      Accept:
+      - '*/*'
+      Accept-Encoding:
+      - gzip, deflate, br, zstd
+      Accept-Language:
+      - en
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '66'
+      Content-Type:
+      - application/json
+      User-Agent:
+      - Subliminal/2.1
+    method: POST
+    uri: https://api.thetvdb.com/login
+  response:
+    body:
+      string: '{"token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTY5OTMyMTQsImlkIjoic3VibGltaW5hbCIsIm9yaWdfaWF0IjoxNzE2Mzg4NDE0fQ.dJAo_FBEcSnkDtgNp-g_mZjEyYoW1MHhgAb-IWuwL3OIhiI3Ga0QAn0MbhX6o88BtmVsoHsWoRD1dFYGeNyO4AK8JUOYV4zwUBpZzxr2dBCd02cEDvbOnA9yLaYmhpUAyN86p3hWKLKlLTBrctBQD9obkMY8pIFoeHNS0SfkQD6PMnjsHdqtpjab0IoH2cFpgm45HhAq78XhEOPBAX45WjlsP-wXrF4H49nzAL_bFButDyS_cCYdYcqaTv4J65OyDJ0VQpA4aUp4T-_6WjwnZ2d0WtcPSaXGX0cDiGFSQMptR7atojVlRJD30ZaZU_wrAfpWGcaoip22xeEx_D0DJg"}'
+    headers:
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '470'
+      Content-Type:
+      - application/json; charset=utf-8
+      Date:
+      - Wed, 22 May 2024 14:33:34 GMT
+      Vary:
+      - Accept-Language
+      Via:
+      - 1.1 2b1fd1e1421ca124eaa002817c6c475a.cloudfront.net (CloudFront)
+      X-Amz-Cf-Id:
+      - ZRlGvJMw9l8m8F_5_ZcUkEpo4bbvjXOwoVTOWqy_WubSgl5jBugmFQ==
+      X-Amz-Cf-Pop:
+      - LIS50-P1
+      X-Cache:
+      - Miss from cloudfront
+      X-Powered-By:
+      - Thundar!
+      X-Thetvdb-Api-Version:
+      - 3.0.0
+    status:
+      code: 200
+      message: OK
 - request:
     body: null
     headers:
diff --git a/tests/cassettes/tvdb/test_refine_episode_with_comma.yaml b/tests/cassettes/tvdb/test_refine_episode_with_comma.yaml
index b9c2c93..cdde370 100644
--- a/tests/cassettes/tvdb/test_refine_episode_with_comma.yaml
+++ b/tests/cassettes/tvdb/test_refine_episode_with_comma.yaml
@@ -1,4 +1,52 @@
 interactions:
+- request:
+    body: '{"apikey": "5EC930FB90DA1ADA", "username": null, "password": null}'
+    headers:
+      Accept:
+      - '*/*'
+      Accept-Encoding:
+      - gzip, deflate, br, zstd
+      Accept-Language:
+      - en
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '66'
+      Content-Type:
+      - application/json
+      User-Agent:
+      - Subliminal/2.1
+    method: POST
+    uri: https://api.thetvdb.com/login
+  response:
+    body:
+      string: '{"token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTY5OTMyMTQsImlkIjoic3VibGltaW5hbCIsIm9yaWdfaWF0IjoxNzE2Mzg4NDE0fQ.dJAo_FBEcSnkDtgNp-g_mZjEyYoW1MHhgAb-IWuwL3OIhiI3Ga0QAn0MbhX6o88BtmVsoHsWoRD1dFYGeNyO4AK8JUOYV4zwUBpZzxr2dBCd02cEDvbOnA9yLaYmhpUAyN86p3hWKLKlLTBrctBQD9obkMY8pIFoeHNS0SfkQD6PMnjsHdqtpjab0IoH2cFpgm45HhAq78XhEOPBAX45WjlsP-wXrF4H49nzAL_bFButDyS_cCYdYcqaTv4J65OyDJ0VQpA4aUp4T-_6WjwnZ2d0WtcPSaXGX0cDiGFSQMptR7atojVlRJD30ZaZU_wrAfpWGcaoip22xeEx_D0DJg"}'
+    headers:
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '470'
+      Content-Type:
+      - application/json; charset=utf-8
+      Date:
+      - Wed, 22 May 2024 14:33:34 GMT
+      Vary:
+      - Accept-Language
+      Via:
+      - 1.1 2b1fd1e1421ca124eaa002817c6c475a.cloudfront.net (CloudFront)
+      X-Amz-Cf-Id:
+      - ZRlGvJMw9l8m8F_5_ZcUkEpo4bbvjXOwoVTOWqy_WubSgl5jBugmFQ==
+      X-Amz-Cf-Pop:
+      - LIS50-P1
+      X-Cache:
+      - Miss from cloudfront
+      X-Powered-By:
+      - Thundar!
+      X-Thetvdb-Api-Version:
+      - 3.0.0
+    status:
+      code: 200
+      message: OK
 - request:
     body: null
     headers:
diff --git a/tests/cassettes/tvdb/test_refine_episode_with_country.yaml b/tests/cassettes/tvdb/test_refine_episode_with_country.yaml
index 6cad582..4d32151 100644
--- a/tests/cassettes/tvdb/test_refine_episode_with_country.yaml
+++ b/tests/cassettes/tvdb/test_refine_episode_with_country.yaml
@@ -1,4 +1,52 @@
 interactions:
+- request:
+    body: '{"apikey": "5EC930FB90DA1ADA", "username": null, "password": null}'
+    headers:
+      Accept:
+      - '*/*'
+      Accept-Encoding:
+      - gzip, deflate, br, zstd
+      Accept-Language:
+      - en
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '66'
+      Content-Type:
+      - application/json
+      User-Agent:
+      - Subliminal/2.1
+    method: POST
+    uri: https://api.thetvdb.com/login
+  response:
+    body:
+      string: '{"token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTY5OTMyMTQsImlkIjoic3VibGltaW5hbCIsIm9yaWdfaWF0IjoxNzE2Mzg4NDE0fQ.dJAo_FBEcSnkDtgNp-g_mZjEyYoW1MHhgAb-IWuwL3OIhiI3Ga0QAn0MbhX6o88BtmVsoHsWoRD1dFYGeNyO4AK8JUOYV4zwUBpZzxr2dBCd02cEDvbOnA9yLaYmhpUAyN86p3hWKLKlLTBrctBQD9obkMY8pIFoeHNS0SfkQD6PMnjsHdqtpjab0IoH2cFpgm45HhAq78XhEOPBAX45WjlsP-wXrF4H49nzAL_bFButDyS_cCYdYcqaTv4J65OyDJ0VQpA4aUp4T-_6WjwnZ2d0WtcPSaXGX0cDiGFSQMptR7atojVlRJD30ZaZU_wrAfpWGcaoip22xeEx_D0DJg"}'
+    headers:
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '470'
+      Content-Type:
+      - application/json; charset=utf-8
+      Date:
+      - Wed, 22 May 2024 14:33:34 GMT
+      Vary:
+      - Accept-Language
+      Via:
+      - 1.1 2b1fd1e1421ca124eaa002817c6c475a.cloudfront.net (CloudFront)
+      X-Amz-Cf-Id:
+      - ZRlGvJMw9l8m8F_5_ZcUkEpo4bbvjXOwoVTOWqy_WubSgl5jBugmFQ==
+      X-Amz-Cf-Pop:
+      - LIS50-P1
+      X-Cache:
+      - Miss from cloudfront
+      X-Powered-By:
+      - Thundar!
+      X-Thetvdb-Api-Version:
+      - 3.0.0
+    status:
+      code: 200
+      message: OK
 - request:
     body: null
     headers:
diff --git a/tests/cassettes/tvdb/test_refine_episode_with_country_hoc_us.yaml b/tests/cassettes/tvdb/test_refine_episode_with_country_hoc_us.yaml
index 9a9cbbf..7c34435 100644
--- a/tests/cassettes/tvdb/test_refine_episode_with_country_hoc_us.yaml
+++ b/tests/cassettes/tvdb/test_refine_episode_with_country_hoc_us.yaml
@@ -1,4 +1,52 @@
 interactions:
+- request:
+    body: '{"apikey": "5EC930FB90DA1ADA", "username": null, "password": null}'
+    headers:
+      Accept:
+      - '*/*'
+      Accept-Encoding:
+      - gzip, deflate, br, zstd
+      Accept-Language:
+      - en
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '66'
+      Content-Type:
+      - application/json
+      User-Agent:
+      - Subliminal/2.1
+    method: POST
+    uri: https://api.thetvdb.com/login
+  response:
+    body:
+      string: '{"token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTY5OTMyMTQsImlkIjoic3VibGltaW5hbCIsIm9yaWdfaWF0IjoxNzE2Mzg4NDE0fQ.dJAo_FBEcSnkDtgNp-g_mZjEyYoW1MHhgAb-IWuwL3OIhiI3Ga0QAn0MbhX6o88BtmVsoHsWoRD1dFYGeNyO4AK8JUOYV4zwUBpZzxr2dBCd02cEDvbOnA9yLaYmhpUAyN86p3hWKLKlLTBrctBQD9obkMY8pIFoeHNS0SfkQD6PMnjsHdqtpjab0IoH2cFpgm45HhAq78XhEOPBAX45WjlsP-wXrF4H49nzAL_bFButDyS_cCYdYcqaTv4J65OyDJ0VQpA4aUp4T-_6WjwnZ2d0WtcPSaXGX0cDiGFSQMptR7atojVlRJD30ZaZU_wrAfpWGcaoip22xeEx_D0DJg"}'
+    headers:
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '470'
+      Content-Type:
+      - application/json; charset=utf-8
+      Date:
+      - Wed, 22 May 2024 14:33:34 GMT
+      Vary:
+      - Accept-Language
+      Via:
+      - 1.1 2b1fd1e1421ca124eaa002817c6c475a.cloudfront.net (CloudFront)
+      X-Amz-Cf-Id:
+      - ZRlGvJMw9l8m8F_5_ZcUkEpo4bbvjXOwoVTOWqy_WubSgl5jBugmFQ==
+      X-Amz-Cf-Pop:
+      - LIS50-P1
+      X-Cache:
+      - Miss from cloudfront
+      X-Powered-By:
+      - Thundar!
+      X-Thetvdb-Api-Version:
+      - 3.0.0
+    status:
+      code: 200
+      message: OK
 - request:
     body: null
     headers:
diff --git a/tests/cassettes/tvdb/test_refine_episode_year.yaml b/tests/cassettes/tvdb/test_refine_episode_year.yaml
index 8ec6f38..a0aad18 100644
--- a/tests/cassettes/tvdb/test_refine_episode_year.yaml
+++ b/tests/cassettes/tvdb/test_refine_episode_year.yaml
@@ -1,4 +1,52 @@
 interactions:
+- request:
+    body: '{"apikey": "5EC930FB90DA1ADA", "username": null, "password": null}'
+    headers:
+      Accept:
+      - '*/*'
+      Accept-Encoding:
+      - gzip, deflate, br, zstd
+      Accept-Language:
+      - en
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '66'
+      Content-Type:
+      - application/json
+      User-Agent:
+      - Subliminal/2.1
+    method: POST
+    uri: https://api.thetvdb.com/login
+  response:
+    body:
+      string: '{"token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTY5OTMyMTQsImlkIjoic3VibGltaW5hbCIsIm9yaWdfaWF0IjoxNzE2Mzg4NDE0fQ.dJAo_FBEcSnkDtgNp-g_mZjEyYoW1MHhgAb-IWuwL3OIhiI3Ga0QAn0MbhX6o88BtmVsoHsWoRD1dFYGeNyO4AK8JUOYV4zwUBpZzxr2dBCd02cEDvbOnA9yLaYmhpUAyN86p3hWKLKlLTBrctBQD9obkMY8pIFoeHNS0SfkQD6PMnjsHdqtpjab0IoH2cFpgm45HhAq78XhEOPBAX45WjlsP-wXrF4H49nzAL_bFButDyS_cCYdYcqaTv4J65OyDJ0VQpA4aUp4T-_6WjwnZ2d0WtcPSaXGX0cDiGFSQMptR7atojVlRJD30ZaZU_wrAfpWGcaoip22xeEx_D0DJg"}'
+    headers:
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '470'
+      Content-Type:
+      - application/json; charset=utf-8
+      Date:
+      - Wed, 22 May 2024 14:33:34 GMT
+      Vary:
+      - Accept-Language
+      Via:
+      - 1.1 2b1fd1e1421ca124eaa002817c6c475a.cloudfront.net (CloudFront)
+      X-Amz-Cf-Id:
+      - ZRlGvJMw9l8m8F_5_ZcUkEpo4bbvjXOwoVTOWqy_WubSgl5jBugmFQ==
+      X-Amz-Cf-Pop:
+      - LIS50-P1
+      X-Cache:
+      - Miss from cloudfront
+      X-Powered-By:
+      - Thundar!
+      X-Thetvdb-Api-Version:
+      - 3.0.0
+    status:
+      code: 200
+      message: OK
 - request:
     body: null
     headers:
diff --git a/tests/test_cache.py b/tests/test_cache.py
index 83b50ac..286480f 100644
--- a/tests/test_cache.py
+++ b/tests/test_cache.py
@@ -14,7 +14,7 @@ region_dogpile.configure = Mock()  # type: ignore[method-assign]
 str_object = 'The Simpsons-S12E09-HOMЯ'
 bytes_object = b'The Simpsons-S12E09-HOM\xd0\xaf'
 namespace = 'namespace'
-expected_key = 'test_cache:fn|namespace|The Simpsons-S12E09-HOMЯ'  # Key is expected as native string
+expected_key = 'tests.test_cache:fn|namespace|The Simpsons-S12E09-HOMЯ'  # Key is expected as native string
 
 
 def fn():
@@ -29,7 +29,7 @@ def test_dogpile_cache_key_generator_unicode_string():
 
 def test_dogpile_cache_key_generator_byte_string():
     key = region_dogpile.function_key_generator(namespace, fn)(bytes_object)
-    assert key == 'test_cache:fn|namespace|' + str(b'The Simpsons-S12E09-HOM\xd0\xaf')
+    assert key == 'tests.test_cache:fn|namespace|' + str(b'The Simpsons-S12E09-HOM\xd0\xaf')
     assert key != expected_key  # Key is not as expected
     assert isinstance(key, str)