talking-snake 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. talking_snake-0.1.0/.github/workflows/ci.yml +118 -0
  2. talking_snake-0.1.0/.github/workflows/publish.yml +74 -0
  3. talking_snake-0.1.0/.gitignore +61 -0
  4. talking_snake-0.1.0/.htmlhintrc +25 -0
  5. talking_snake-0.1.0/.pre-commit-config.yaml +51 -0
  6. talking_snake-0.1.0/.stylelintrc.json +10 -0
  7. talking_snake-0.1.0/Dockerfile +31 -0
  8. talking_snake-0.1.0/LICENSE +21 -0
  9. talking_snake-0.1.0/PKG-INFO +71 -0
  10. talking_snake-0.1.0/README.md +34 -0
  11. talking_snake-0.1.0/README_HF.md +17 -0
  12. talking_snake-0.1.0/eslint.config.js +34 -0
  13. talking_snake-0.1.0/favicon.png +0 -0
  14. talking_snake-0.1.0/landing.png +0 -0
  15. talking_snake-0.1.0/package-lock.json +2509 -0
  16. talking_snake-0.1.0/package.json +20 -0
  17. talking_snake-0.1.0/pyproject.toml +111 -0
  18. talking_snake-0.1.0/rendering.png +0 -0
  19. talking_snake-0.1.0/scripts/generate_sample.py +33 -0
  20. talking_snake-0.1.0/src/talking_snake/__init__.py +3 -0
  21. talking_snake-0.1.0/src/talking_snake/__main__.py +119 -0
  22. talking_snake-0.1.0/src/talking_snake/app.py +935 -0
  23. talking_snake-0.1.0/src/talking_snake/extract.py +489 -0
  24. talking_snake-0.1.0/src/talking_snake/static/app.js +773 -0
  25. talking_snake-0.1.0/src/talking_snake/static/apple-touch-icon.png +0 -0
  26. talking_snake-0.1.0/src/talking_snake/static/favicon.png +0 -0
  27. talking_snake-0.1.0/src/talking_snake/static/icon-192.png +0 -0
  28. talking_snake-0.1.0/src/talking_snake/static/icon-512.png +0 -0
  29. talking_snake-0.1.0/src/talking_snake/static/index.html +142 -0
  30. talking_snake-0.1.0/src/talking_snake/static/manifest.json +36 -0
  31. talking_snake-0.1.0/src/talking_snake/static/sample.wav +0 -0
  32. talking_snake-0.1.0/src/talking_snake/static/styles.css +848 -0
  33. talking_snake-0.1.0/src/talking_snake/static/talking_snake.png +0 -0
  34. talking_snake-0.1.0/src/talking_snake/tts.py +486 -0
  35. talking_snake-0.1.0/talking_snake.png +0 -0
  36. talking_snake-0.1.0/tests/conftest.py +205 -0
  37. talking_snake-0.1.0/tests/test_app.py +200 -0
  38. talking_snake-0.1.0/tests/test_app_streaming.py +266 -0
  39. talking_snake-0.1.0/tests/test_extract.py +227 -0
  40. talking_snake-0.1.0/tests/test_tts.py +250 -0
  41. talking_snake-0.1.0/uv.lock +3381 -0
@@ -0,0 +1,118 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ lint:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - name: Install uv
16
+ uses: astral-sh/setup-uv@v5
17
+ with:
18
+ version: "latest"
19
+
20
+ - name: Set up Python
21
+ run: uv python install 3.12
22
+
23
+ - name: Install dependencies
24
+ run: uv sync --extra dev
25
+
26
+ - name: Run ruff check
27
+ run: uv run ruff check src tests
28
+
29
+ - name: Run ruff format check
30
+ run: uv run ruff format --check src tests
31
+
32
+ lint-frontend:
33
+ runs-on: ubuntu-latest
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+
37
+ - name: Set up Node.js
38
+ uses: actions/setup-node@v4
39
+ with:
40
+ node-version: "20"
41
+ cache: "npm"
42
+
43
+ - name: Install dependencies
44
+ run: npm ci
45
+
46
+ - name: Lint HTML
47
+ run: npm run lint:html
48
+
49
+ - name: Lint CSS
50
+ run: npm run lint:css
51
+
52
+ - name: Lint JavaScript
53
+ run: npm run lint:js
54
+
55
+ test:
56
+ runs-on: ubuntu-latest
57
+ strategy:
58
+ matrix:
59
+ python-version: ["3.11", "3.12"]
60
+ steps:
61
+ - uses: actions/checkout@v4
62
+
63
+ - name: Install uv
64
+ uses: astral-sh/setup-uv@v5
65
+ with:
66
+ version: "latest"
67
+
68
+ - name: Set up Python ${{ matrix.python-version }}
69
+ run: uv python install ${{ matrix.python-version }}
70
+
71
+ - name: Install dependencies
72
+ run: uv sync --extra dev
73
+
74
+ - name: Run tests with coverage
75
+ run: |
76
+ uv run pytest \
77
+ --cov=talking_snake \
78
+ --cov-report=xml \
79
+ --cov-report=term-missing \
80
+ --cov-fail-under=50
81
+
82
+ - name: Upload coverage to Codecov
83
+ if: matrix.python-version == '3.12'
84
+ uses: codecov/codecov-action@v5
85
+ with:
86
+ token: ${{ secrets.CODECOV_TOKEN }}
87
+ files: ./coverage.xml
88
+ fail_ci_if_error: false
89
+
90
+ typecheck:
91
+ runs-on: ubuntu-latest
92
+ steps:
93
+ - uses: actions/checkout@v4
94
+
95
+ - name: Install uv
96
+ uses: astral-sh/setup-uv@v5
97
+ with:
98
+ version: "latest"
99
+
100
+ - name: Set up Python
101
+ run: uv python install 3.12
102
+
103
+ - name: Install dependencies
104
+ run: uv sync --extra dev
105
+
106
+ - name: Run mypy
107
+ run: uv run mypy src --ignore-missing-imports
108
+
109
+ links:
110
+ runs-on: ubuntu-latest
111
+ steps:
112
+ - uses: actions/checkout@v4
113
+
114
+ - name: Check links in markdown files
115
+ uses: lycheeverse/lychee-action@v2
116
+ with:
117
+ args: --verbose --no-progress '**/*.md'
118
+ fail: true
@@ -0,0 +1,74 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+ inputs:
8
+ publish_target:
9
+ description: 'Publish target'
10
+ required: true
11
+ default: 'testpypi'
12
+ type: choice
13
+ options:
14
+ - testpypi
15
+ - pypi
16
+
17
+ jobs:
18
+ build:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+
23
+ - name: Install uv
24
+ uses: astral-sh/setup-uv@v5
25
+ with:
26
+ version: "latest"
27
+
28
+ - name: Set up Python
29
+ run: uv python install 3.12
30
+
31
+ - name: Build package
32
+ run: uv build
33
+
34
+ - name: Upload artifacts
35
+ uses: actions/upload-artifact@v4
36
+ with:
37
+ name: dist
38
+ path: dist/
39
+
40
+ publish-testpypi:
41
+ needs: build
42
+ if: github.event_name == 'workflow_dispatch' && github.event.inputs.publish_target == 'testpypi'
43
+ runs-on: ubuntu-latest
44
+ environment: testpypi
45
+ permissions:
46
+ id-token: write
47
+ steps:
48
+ - name: Download artifacts
49
+ uses: actions/download-artifact@v4
50
+ with:
51
+ name: dist
52
+ path: dist/
53
+
54
+ - name: Publish to TestPyPI
55
+ uses: pypa/gh-action-pypi-publish@release/v1
56
+ with:
57
+ repository-url: https://test.pypi.org/legacy/
58
+
59
+ publish-pypi:
60
+ needs: build
61
+ if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.publish_target == 'pypi')
62
+ runs-on: ubuntu-latest
63
+ environment: pypi
64
+ permissions:
65
+ id-token: write
66
+ steps:
67
+ - name: Download artifacts
68
+ uses: actions/download-artifact@v4
69
+ with:
70
+ name: dist
71
+ path: dist/
72
+
73
+ - name: Publish to PyPI
74
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,61 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ .venv/
25
+ venv/
26
+ ENV/
27
+
28
+ # IDE
29
+ .idea/
30
+ .vscode/
31
+ *.swp
32
+ *.swo
33
+ *~
34
+
35
+ # Testing
36
+ .pytest_cache/
37
+ .coverage
38
+ htmlcov/
39
+ .tox/
40
+ .nox/
41
+ coverage.xml
42
+ *.cover
43
+
44
+ # mypy
45
+ .mypy_cache/
46
+ .dmypy.json
47
+ dmypy.json
48
+
49
+ # ruff
50
+ .ruff_cache/
51
+
52
+ # Node.js (frontend linting)
53
+ node_modules/
54
+
55
+ # OS
56
+ .DS_Store
57
+ Thumbs.db
58
+
59
+ # Project specific
60
+ *.wav
61
+ !src/talking_snake/static/sample.wav
@@ -0,0 +1,25 @@
1
+ {
2
+ "tagname-lowercase": true,
3
+ "attr-lowercase": true,
4
+ "attr-value-double-quotes": true,
5
+ "attr-value-not-empty": false,
6
+ "attr-no-duplication": true,
7
+ "doctype-first": true,
8
+ "tag-pair": true,
9
+ "empty-tag-not-self-closed": true,
10
+ "spec-char-escape": true,
11
+ "id-unique": true,
12
+ "src-not-empty": true,
13
+ "title-require": true,
14
+ "alt-require": true,
15
+ "doctype-html5": true,
16
+ "id-class-value": false,
17
+ "style-disabled": true,
18
+ "inline-style-disabled": true,
19
+ "inline-script-disabled": true,
20
+ "space-tab-mixed-disabled": "space",
21
+ "id-class-ad-disabled": true,
22
+ "href-abs-or-rel": false,
23
+ "attr-unsafe-chars": true,
24
+ "head-script-disabled": false
25
+ }
@@ -0,0 +1,51 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v5.0.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: check-yaml
8
+ - id: check-toml
9
+ - id: check-added-large-files
10
+ args: ['--maxkb=1000']
11
+ - id: check-merge-conflict
12
+
13
+ - repo: https://github.com/astral-sh/ruff-pre-commit
14
+ rev: v0.8.6
15
+ hooks:
16
+ - id: ruff
17
+ args: [--fix, --exit-non-zero-on-fix]
18
+ - id: ruff-format
19
+
20
+ - repo: https://github.com/pre-commit/mirrors-mypy
21
+ rev: v1.14.1
22
+ hooks:
23
+ - id: mypy
24
+ additional_dependencies:
25
+ - types-requests
26
+ - pydantic
27
+ args: [--ignore-missing-imports]
28
+ files: ^src/
29
+
30
+ - repo: local
31
+ hooks:
32
+ - id: htmlhint
33
+ name: htmlhint
34
+ entry: npx htmlhint
35
+ language: system
36
+ files: \.html$
37
+ types: [file]
38
+
39
+ - id: stylelint
40
+ name: stylelint
41
+ entry: npx stylelint
42
+ language: system
43
+ files: \.css$
44
+ types: [file]
45
+
46
+ - id: eslint
47
+ name: eslint
48
+ entry: npx eslint
49
+ language: system
50
+ files: \.js$
51
+ types: [file]
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": ["stylelint-config-standard"],
3
+ "rules": {
4
+ "selector-class-pattern": null,
5
+ "custom-property-pattern": null,
6
+ "no-descending-specificity": null,
7
+ "color-function-notation": "legacy",
8
+ "alpha-value-notation": "number"
9
+ }
10
+ }
@@ -0,0 +1,31 @@
1
+ FROM python:3.12-slim
2
+
3
+ # Install system dependencies
4
+ RUN apt-get update && apt-get install -y \
5
+ sox \
6
+ libsox-dev \
7
+ git \
8
+ && rm -rf /var/lib/apt/lists/*
9
+
10
+ # Install uv
11
+ RUN pip install uv
12
+
13
+ # Create non-root user for HF Spaces
14
+ RUN useradd -m -u 1000 user
15
+ USER user
16
+ ENV HOME=/home/user \
17
+ PATH=/home/user/.local/bin:$PATH
18
+
19
+ WORKDIR $HOME/app
20
+
21
+ # Copy project files
22
+ COPY --chown=user . .
23
+
24
+ # Install dependencies
25
+ RUN uv sync --no-dev
26
+
27
+ # Expose port 7860 (HF Spaces default)
28
+ EXPOSE 7860
29
+
30
+ # Run the app
31
+ CMD ["uv", "run", "talking-snake", "--host", "0.0.0.0", "--port", "7860"]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Luca Cappelletti
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: talking-snake
3
+ Version: 0.1.0
4
+ Summary: Just a talking snake that reads PDFs and web pages aloud.
5
+ Author: Luca
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Keywords: audiobook,listening,pdf,speech,text-to-speech,tts
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: End Users/Desktop
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
16
+ Requires-Python: >=3.11
17
+ Requires-Dist: fastapi>=0.115.0
18
+ Requires-Dist: httpx>=0.27.0
19
+ Requires-Dist: jinja2>=3.1.4
20
+ Requires-Dist: pdfminer-six>=20260107
21
+ Requires-Dist: python-multipart>=0.0.12
22
+ Requires-Dist: qwen-tts>=0.1.1
23
+ Requires-Dist: torch>=2.5.0
24
+ Requires-Dist: trafilatura>=2.0.0
25
+ Requires-Dist: uvicorn[standard]>=0.32.0
26
+ Provides-Extra: dev
27
+ Requires-Dist: httpx>=0.27.0; extra == 'dev'
28
+ Requires-Dist: mypy>=1.14.0; extra == 'dev'
29
+ Requires-Dist: pre-commit>=4.0.0; extra == 'dev'
30
+ Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
31
+ Requires-Dist: pytest-cov>=6.0.0; extra == 'dev'
32
+ Requires-Dist: pytest>=8.3.0; extra == 'dev'
33
+ Requires-Dist: ruff>=0.8.0; extra == 'dev'
34
+ Provides-Extra: fast
35
+ Requires-Dist: flash-attn>=2.5.0; extra == 'fast'
36
+ Description-Content-Type: text/markdown
37
+
38
+ # Talking Snake
39
+
40
+ <img src="https://raw.githubusercontent.com/LucaCappelletti94/talking-snake/main/talking_snake.png" alt="Talking Snake" width="400">
41
+
42
+ [![CI](https://github.com/LucaCappelletti94/talking-snake/actions/workflows/ci.yml/badge.svg)](https://github.com/LucaCappelletti94/talking-snake/actions/workflows/ci.yml)
43
+ [![codecov](https://codecov.io/gh/LucaCappelletti94/talking-snake/branch/main/graph/badge.svg)](https://codecov.io/gh/LucaCappelletti94/talking-snake)
44
+
45
+ PDF and web page to speech using [Qwen3-TTS](https://huggingface.co/Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice). Upload a document or URL, get it read aloud with 9 natural voices across English, Chinese, Japanese, and Korean. Audio streams progressively while generation continues.
46
+
47
+ ## Deploy Your Own
48
+
49
+ [![Deploy on Hugging Face Spaces](https://huggingface.co/datasets/huggingface/badges/resolve/main/deploy-on-spaces-lg.svg)](https://huggingface.co/spaces/LucaCappelletti94/talking-snake?duplicate=true)
50
+
51
+ Click the button above to deploy your own GPU-powered instance. You'll be prompted to create a Hugging Face account and select hardware (L4 or A100 recommended for speed, ~$0.80-$4/hr).
52
+
53
+ ## Run Locally
54
+
55
+ Requires Python 3.11+, NVIDIA GPU (~6GB VRAM), and [SoX](https://sourceforge.net/projects/sox/) (`apt install sox libsox-dev`).
56
+
57
+ ```bash
58
+ uv sync && uv run talking-snake --port 8888 # Open http://localhost:8888
59
+ ```
60
+
61
+
62
+ [▶️ Listen to a sample](https://github.com/LucaCappelletti94/talking-snake/raw/main/src/talking_snake/static/sample.wav)
63
+
64
+ The website looks like this:
65
+
66
+ <img src="https://raw.githubusercontent.com/LucaCappelletti94/talking-snake/main/landing.png" alt="Upload interface" width="400">
67
+ <img src="https://raw.githubusercontent.com/LucaCappelletti94/talking-snake/main/rendering.png" alt="Audio playback with progress" width="400">
68
+
69
+ ## License
70
+
71
+ This project is licensed under the [MIT License](LICENSE). Dependencies and third-party components (e.g., Qwen3-TTS, SoX) are subject to their own licenses.
@@ -0,0 +1,34 @@
1
+ # Talking Snake
2
+
3
+ <img src="https://raw.githubusercontent.com/LucaCappelletti94/talking-snake/main/talking_snake.png" alt="Talking Snake" width="400">
4
+
5
+ [![CI](https://github.com/LucaCappelletti94/talking-snake/actions/workflows/ci.yml/badge.svg)](https://github.com/LucaCappelletti94/talking-snake/actions/workflows/ci.yml)
6
+ [![codecov](https://codecov.io/gh/LucaCappelletti94/talking-snake/branch/main/graph/badge.svg)](https://codecov.io/gh/LucaCappelletti94/talking-snake)
7
+
8
+ PDF and web page to speech using [Qwen3-TTS](https://huggingface.co/Qwen/Qwen3-TTS-12Hz-1.7B-CustomVoice). Upload a document or URL, get it read aloud with 9 natural voices across English, Chinese, Japanese, and Korean. Audio streams progressively while generation continues.
9
+
10
+ ## Deploy Your Own
11
+
12
+ [![Deploy on Hugging Face Spaces](https://huggingface.co/datasets/huggingface/badges/resolve/main/deploy-on-spaces-lg.svg)](https://huggingface.co/spaces/LucaCappelletti94/talking-snake?duplicate=true)
13
+
14
+ Click the button above to deploy your own GPU-powered instance. You'll be prompted to create a Hugging Face account and select hardware (L4 or A100 recommended for speed, ~$0.80-$4/hr).
15
+
16
+ ## Run Locally
17
+
18
+ Requires Python 3.11+, NVIDIA GPU (~6GB VRAM), and [SoX](https://sourceforge.net/projects/sox/) (`apt install sox libsox-dev`).
19
+
20
+ ```bash
21
+ uv sync && uv run talking-snake --port 8888 # Open http://localhost:8888
22
+ ```
23
+
24
+
25
+ [▶️ Listen to a sample](https://github.com/LucaCappelletti94/talking-snake/raw/main/src/talking_snake/static/sample.wav)
26
+
27
+ The website looks like this:
28
+
29
+ <img src="https://raw.githubusercontent.com/LucaCappelletti94/talking-snake/main/landing.png" alt="Upload interface" width="400">
30
+ <img src="https://raw.githubusercontent.com/LucaCappelletti94/talking-snake/main/rendering.png" alt="Audio playback with progress" width="400">
31
+
32
+ ## License
33
+
34
+ This project is licensed under the [MIT License](LICENSE). Dependencies and third-party components (e.g., Qwen3-TTS, SoX) are subject to their own licenses.
@@ -0,0 +1,17 @@
1
+ ---
2
+ title: Talking Snake
3
+ emoji: 🐍
4
+ colorFrom: green
5
+ colorTo: purple
6
+ sdk: docker
7
+ pinned: false
8
+ license: mit
9
+ app_port: 7860
10
+ suggested_hardware: l4x1
11
+ ---
12
+
13
+ # Talking Snake
14
+
15
+ PDF and web page to speech using Qwen3-TTS.
16
+
17
+ Click "Duplicate this Space" to deploy your own instance (L4 or A100 recommended for speed).
@@ -0,0 +1,34 @@
1
+ import js from "@eslint/js";
2
+
3
+ export default [
4
+ js.configs.recommended,
5
+ {
6
+ files: ["**/*.js"],
7
+ ignores: ["eslint.config.js"],
8
+ languageOptions: {
9
+ ecmaVersion: 2022,
10
+ sourceType: "script",
11
+ globals: {
12
+ document: "readonly",
13
+ window: "readonly",
14
+ console: "readonly",
15
+ fetch: "readonly",
16
+ URL: "readonly",
17
+ Blob: "readonly",
18
+ FormData: "readonly",
19
+ TextDecoder: "readonly",
20
+ AbortController: "readonly",
21
+ EventSource: "readonly",
22
+ setTimeout: "readonly",
23
+ },
24
+ },
25
+ rules: {
26
+ "no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
27
+ "no-console": "off",
28
+ "prefer-const": "error",
29
+ "no-var": "error",
30
+ eqeqeq: ["error", "always"],
31
+ curly: ["error", "all"],
32
+ },
33
+ },
34
+ ];
Binary file
Binary file