textflowkit 0.1.5__tar.gz → 0.1.6__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.
- textflowkit-0.1.6/.dockerignore +54 -0
- textflowkit-0.1.6/.env.example +20 -0
- textflowkit-0.1.6/.github/workflows/publish-pypi.yml +211 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/.gitignore +2 -0
- textflowkit-0.1.6/Dockerfile +115 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/LEGAL.md +12 -3
- {textflowkit-0.1.5 → textflowkit-0.1.6}/PKG-INFO +81 -18
- {textflowkit-0.1.5 → textflowkit-0.1.6}/README.md +76 -17
- textflowkit-0.1.6/compose.yaml +111 -0
- textflowkit-0.1.6/docs/404.html +111 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/docs/adapters.md +242 -13
- {textflowkit-0.1.5 → textflowkit-0.1.6}/docs/index.html +15 -9
- {textflowkit-0.1.5 → textflowkit-0.1.6}/docs/install.md +79 -4
- textflowkit-0.1.6/docs/release-checklist.md +278 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/docs/roadmap.md +32 -4
- {textflowkit-0.1.5 → textflowkit-0.1.6}/docs/site-deployment.md +22 -0
- textflowkit-0.1.6/docs/user-manual.md +218 -0
- textflowkit-0.1.6/pyproject.toml +119 -0
- textflowkit-0.1.6/scripts/fetch_published_fonts.py +481 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/scripts/smoke_live_youtube.py +80 -1
- textflowkit-0.1.6/scripts/verify_readme_release.py +219 -0
- textflowkit-0.1.6/scripts/verify_release_ci.py +171 -0
- textflowkit-0.1.6/scripts/verify_release_versions.py +145 -0
- textflowkit-0.1.6/scripts/write_release_manifest.py +236 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/__init__.py +1 -1
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/adapters/http_server.py +124 -17
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/adapters/mcp_server.py +194 -16
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/cli.py +99 -10
- textflowkit-0.1.6/src/textflowkit/core/batch.py +152 -0
- textflowkit-0.1.6/src/textflowkit/core/bind.py +321 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/core/checkpoint.py +75 -10
- textflowkit-0.1.6/src/textflowkit/core/engine.py +343 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/core/executor.py +10 -14
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/core/model.py +30 -10
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/core/pipeline.py +65 -4
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/core/runner.py +36 -9
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/core/service.py +33 -1
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/core/submission.py +60 -3
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/core/translate.py +21 -5
- textflowkit-0.1.6/src/textflowkit/render/__init__.py +571 -0
- textflowkit-0.1.6/src/textflowkit/render/_cue_layout.py +352 -0
- textflowkit-0.1.6/src/textflowkit/render/_cue_text.py +74 -0
- textflowkit-0.1.6/src/textflowkit/render/_rename_excl.py +163 -0
- textflowkit-0.1.6/src/textflowkit/render/_rename_noreplace.py +163 -0
- textflowkit-0.1.6/src/textflowkit/render/srt.py +31 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/render/txt.py +10 -1
- textflowkit-0.1.6/src/textflowkit/render/vtt.py +34 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/sources/acquire.py +116 -2
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/sources/detect.py +16 -8
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_acquire.py +178 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_adapters.py +134 -21
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_batch.py +34 -19
- textflowkit-0.1.6/tests/test_batch_resume_shared.py +213 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_bind.py +48 -0
- textflowkit-0.1.6/tests/test_checkpoint_single_copy.py +517 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_cli_resume_batch.py +18 -0
- textflowkit-0.1.6/tests/test_cli_stdout_format.py +169 -0
- textflowkit-0.1.6/tests/test_container_example.py +756 -0
- textflowkit-0.1.6/tests/test_cue_layout.py +427 -0
- textflowkit-0.1.6/tests/test_diarize_example_flags.py +140 -0
- textflowkit-0.1.6/tests/test_diarize_token_docs.py +181 -0
- textflowkit-0.1.6/tests/test_done_output_integrity.py +611 -0
- textflowkit-0.1.6/tests/test_empty_formats_resume.py +135 -0
- textflowkit-0.1.6/tests/test_engine_adapters.py +538 -0
- textflowkit-0.1.6/tests/test_engine_faster.py +470 -0
- textflowkit-0.1.6/tests/test_engine_quiet.py +129 -0
- textflowkit-0.1.6/tests/test_export_file_mode.py +226 -0
- textflowkit-0.1.6/tests/test_faster_whisper_docs.py +108 -0
- textflowkit-0.1.6/tests/test_fetch_published_fonts.py +779 -0
- textflowkit-0.1.6/tests/test_font_notices.py +280 -0
- textflowkit-0.1.6/tests/test_http_host_boundary.py +279 -0
- textflowkit-0.1.6/tests/test_http_trusted_proxy.py +333 -0
- textflowkit-0.1.6/tests/test_job_progress.py +237 -0
- textflowkit-0.1.6/tests/test_landing_commands.py +216 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_landing_page.py +3 -1
- textflowkit-0.1.6/tests/test_legal_summary.py +134 -0
- textflowkit-0.1.6/tests/test_linux_no_hardlink.py +740 -0
- textflowkit-0.1.6/tests/test_live_smoke_script.py +250 -0
- textflowkit-0.1.6/tests/test_macos_no_hardlink.py +619 -0
- textflowkit-0.1.6/tests/test_mcp_http_boundary.py +457 -0
- textflowkit-0.1.6/tests/test_mcp_tool_descriptions.py +294 -0
- textflowkit-0.1.6/tests/test_mcp_work_root.py +193 -0
- textflowkit-0.1.6/tests/test_model.py +150 -0
- textflowkit-0.1.6/tests/test_partial_output_resume.py +273 -0
- textflowkit-0.1.6/tests/test_partial_pdf_resume.py +362 -0
- textflowkit-0.1.6/tests/test_production_browser_cookies.py +228 -0
- textflowkit-0.1.6/tests/test_publish_ci_gate.py +276 -0
- textflowkit-0.1.6/tests/test_publish_fonts_wiring.py +351 -0
- textflowkit-0.1.6/tests/test_publish_manifest_wiring.py +158 -0
- textflowkit-0.1.6/tests/test_publish_workflow_gate.py +118 -0
- textflowkit-0.1.6/tests/test_readme_layout.py +219 -0
- textflowkit-0.1.6/tests/test_readme_release_guard.py +393 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_release_boundaries.py +140 -3
- textflowkit-0.1.6/tests/test_release_manifest.py +420 -0
- textflowkit-0.1.6/tests/test_release_surfaces.py +209 -0
- textflowkit-0.1.6/tests/test_render.py +253 -0
- textflowkit-0.1.6/tests/test_sdist_allowlist.py +237 -0
- textflowkit-0.1.6/tests/test_service_profile.py +390 -0
- textflowkit-0.1.6/tests/test_site_not_found.py +162 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_sources.py +84 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_submission.py +8 -3
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_translate.py +71 -0
- textflowkit-0.1.6/tests/test_verify_release_versions.py +225 -0
- textflowkit-0.1.6/tests/test_windows_no_hardlink.py +319 -0
- textflowkit-0.1.5/.github/workflows/publish-pypi.yml +0 -114
- textflowkit-0.1.5/docs/release-checklist.md +0 -86
- textflowkit-0.1.5/pyproject.toml +0 -68
- textflowkit-0.1.5/src/textflowkit/core/batch.py +0 -186
- textflowkit-0.1.5/src/textflowkit/core/bind.py +0 -66
- textflowkit-0.1.5/src/textflowkit/core/engine.py +0 -135
- textflowkit-0.1.5/src/textflowkit/render/__init__.py +0 -206
- textflowkit-0.1.5/src/textflowkit/render/srt.py +0 -22
- textflowkit-0.1.5/src/textflowkit/render/vtt.py +0 -20
- textflowkit-0.1.5/tests/test_live_smoke_script.py +0 -106
- textflowkit-0.1.5/tests/test_model.py +0 -57
- textflowkit-0.1.5/tests/test_render.py +0 -99
- textflowkit-0.1.5/tests/test_service_profile.py +0 -206
- {textflowkit-0.1.5 → textflowkit-0.1.6}/.gitattributes +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/.github/dependabot.yml +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/.github/workflows/ci.yml +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/.pre-commit-config.yaml +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/CONTRIBUTING.md +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/LICENSE +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/SECURITY.md +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/docs/.nojekyll +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/docs/sources.md +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/scripts/smoke_installed_wheel.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/adapters/__init__.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/assets/README.md +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/assets/selftest-speech.wav +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/core/__init__.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/core/cancel.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/core/diarize.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/core/jobs.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/core/paths.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/core/retrieval.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/core/sqlite_store.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/core/timeutil.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/render/docx.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/render/markdown.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/render/pdf.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/sources/__init__.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/src/textflowkit/sources/scratch.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/__init__.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/ollama_stub.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_checkpoint.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_cli.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_diarize.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_engine_cache.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_executor.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_export.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_jobs.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_paths.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_pipeline_integrity.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_retrieval.py +0 -0
- {textflowkit-0.1.5 → textflowkit-0.1.6}/tests/test_stdio_protocol.py +0 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# What the container image build must not receive.
|
|
2
|
+
#
|
|
3
|
+
# The image is built from this repository root, so anything not named here lands
|
|
4
|
+
# in the build context. Only local state and secrets are named: the licence, the
|
|
5
|
+
# source, the docs, and the packaging metadata stay in, because the wheel build
|
|
6
|
+
# reads them (`license-files` and `readme` in pyproject.toml) and because a
|
|
7
|
+
# context that quietly dropped them would produce an image that builds but is not
|
|
8
|
+
# this project.
|
|
9
|
+
|
|
10
|
+
# Version control, environments, caches, build output.
|
|
11
|
+
.git
|
|
12
|
+
.venv
|
|
13
|
+
venv
|
|
14
|
+
__pycache__
|
|
15
|
+
*.py[cod]
|
|
16
|
+
*.egg-info
|
|
17
|
+
.pytest_cache
|
|
18
|
+
.ruff_cache
|
|
19
|
+
.mypy_cache
|
|
20
|
+
build
|
|
21
|
+
dist
|
|
22
|
+
|
|
23
|
+
# Local work products and media, as in .gitignore. The one exception below is
|
|
24
|
+
# load-bearing: pyproject.toml's `artifacts` packs `src/textflowkit/assets/*.wav`
|
|
25
|
+
# into the wheel, so a media filter without the negation would ship an image
|
|
26
|
+
# whose asset directory is silently empty.
|
|
27
|
+
work
|
|
28
|
+
out
|
|
29
|
+
outputs
|
|
30
|
+
*.wav
|
|
31
|
+
!src/textflowkit/assets/selftest-speech.wav
|
|
32
|
+
*.mp3
|
|
33
|
+
*.m4a
|
|
34
|
+
*.mp4
|
|
35
|
+
*.webm
|
|
36
|
+
*.srt
|
|
37
|
+
*.vtt
|
|
38
|
+
|
|
39
|
+
# Secrets and local configuration. The production token comes from the operator's
|
|
40
|
+
# environment at run time, never from the image.
|
|
41
|
+
.env
|
|
42
|
+
.env.*
|
|
43
|
+
*.local.toml
|
|
44
|
+
|
|
45
|
+
# Host-side media for the Compose example, if the operator placed it in-tree.
|
|
46
|
+
# Anchored: only the repository-root `data/` the Compose file mounts, not any
|
|
47
|
+
# fixture directory a package might legitimately ship.
|
|
48
|
+
/data
|
|
49
|
+
|
|
50
|
+
# OS and editor debris.
|
|
51
|
+
.DS_Store
|
|
52
|
+
Thumbs.db
|
|
53
|
+
.vscode
|
|
54
|
+
.idea
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Template for the Compose example. Copy it to `.env`, which is git-ignored:
|
|
2
|
+
#
|
|
3
|
+
# cp .env.example .env
|
|
4
|
+
#
|
|
5
|
+
# There is no default token, and Compose will not start without one. Generate a
|
|
6
|
+
# value and paste it in:
|
|
7
|
+
#
|
|
8
|
+
# openssl rand -hex 32
|
|
9
|
+
#
|
|
10
|
+
# An environment variable keeps the token out of the command line, so it does not
|
|
11
|
+
# appear in `ps`. That is all it buys. Docker records the value in the container's
|
|
12
|
+
# configuration, where `docker inspect` and `docker compose config` render it for
|
|
13
|
+
# anyone who can reach the daemon; inside the container it is readable through
|
|
14
|
+
# `/proc/<pid>/environ`; and here it sits in plain text in `.env`, which git
|
|
15
|
+
# ignores but does not encrypt. Restrict access to the daemon, this Compose file,
|
|
16
|
+
# and this env file, and do not mistake one shared token for secret-store
|
|
17
|
+
# isolation - a real secret store is a separate decision this example does not
|
|
18
|
+
# make for you. Keep the value out of the repository and out of any image layer.
|
|
19
|
+
|
|
20
|
+
TEXTFLOWKIT_API_TOKEN=
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
name: Publish PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ['v*']
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
if: github.repository == 'scottconverse/textflowkit'
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
# Actions read is needed to verify that this exact commit passed main CI.
|
|
15
|
+
# It is granted to this job only: no upload job can read run results.
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
actions: read
|
|
19
|
+
outputs:
|
|
20
|
+
# The upload jobs act on this: only a new fonts version may be uploaded.
|
|
21
|
+
fonts_mode: ${{ steps.fonts_release.outputs.fonts_mode }}
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
24
|
+
with:
|
|
25
|
+
persist-credentials: false
|
|
26
|
+
fetch-depth: 0
|
|
27
|
+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
28
|
+
with:
|
|
29
|
+
python-version: '3.12'
|
|
30
|
+
- run: python -m pip install packaging
|
|
31
|
+
- name: Verify release tag and package versions
|
|
32
|
+
env:
|
|
33
|
+
RELEASE_TAG: ${{ github.ref_name }}
|
|
34
|
+
run: |
|
|
35
|
+
python scripts/verify_release_versions.py
|
|
36
|
+
git merge-base --is-ancestor "$GITHUB_SHA" origin/main || {
|
|
37
|
+
echo 'Release tag is not merged into main' >&2
|
|
38
|
+
exit 1
|
|
39
|
+
}
|
|
40
|
+
# A distribution's long description is built from the README at this commit
|
|
41
|
+
# and frozen on upload, so a README that still names an older tag publishes
|
|
42
|
+
# a permanently wrong version claim - the v0.1.5 mistake. This runs before
|
|
43
|
+
# anything is built or uploaded.
|
|
44
|
+
- name: Require the README to name the release being published
|
|
45
|
+
env:
|
|
46
|
+
RELEASE_TAG: ${{ github.ref_name }}
|
|
47
|
+
run: python scripts/verify_readme_release.py
|
|
48
|
+
- name: Require successful CI for this exact commit
|
|
49
|
+
env:
|
|
50
|
+
GITHUB_TOKEN: ${{ github.token }}
|
|
51
|
+
run: python scripts/verify_release_ci.py
|
|
52
|
+
# The fonts package is versioned on its own contract: when the version this
|
|
53
|
+
# commit declares is already on PyPI, the release reuses those published
|
|
54
|
+
# files instead of rebuilding them, and the fonts upload job is skipped.
|
|
55
|
+
# The script decides, downloads and verifies, and stops the release on
|
|
56
|
+
# anything it cannot verify. It runs before the build so a reuse can never
|
|
57
|
+
# be overwritten by a local build, and before any upload.
|
|
58
|
+
- name: Resolve the fonts release
|
|
59
|
+
id: fonts_release
|
|
60
|
+
run: |
|
|
61
|
+
python scripts/fetch_published_fonts.py \
|
|
62
|
+
--package-dir packages/textflowkit-fonts \
|
|
63
|
+
--out-dir dist/fonts \
|
|
64
|
+
--github-output "$GITHUB_OUTPUT"
|
|
65
|
+
- name: Build distributions
|
|
66
|
+
env:
|
|
67
|
+
FONTS_MODE: ${{ steps.fonts_release.outputs.fonts_mode }}
|
|
68
|
+
run: |
|
|
69
|
+
python -m pip install --upgrade build packaging
|
|
70
|
+
python -m build --sdist --wheel --outdir dist/main .
|
|
71
|
+
case "$FONTS_MODE" in
|
|
72
|
+
new)
|
|
73
|
+
# The declared fonts version is not on PyPI, so this release
|
|
74
|
+
# publishes it.
|
|
75
|
+
python -m build --sdist --wheel --outdir dist/fonts packages/textflowkit-fonts
|
|
76
|
+
;;
|
|
77
|
+
reuse)
|
|
78
|
+
# dist/fonts holds the published files, fetched and verified
|
|
79
|
+
# against PyPI above. Rebuilding here would replace them with
|
|
80
|
+
# different bytes and break the hashes the manifest publishes.
|
|
81
|
+
echo "reusing the textflowkit-fonts files fetched from PyPI"
|
|
82
|
+
;;
|
|
83
|
+
*)
|
|
84
|
+
echo "no fonts mode was resolved; refusing to build" >&2
|
|
85
|
+
exit 1
|
|
86
|
+
;;
|
|
87
|
+
esac
|
|
88
|
+
python -m pip install twine
|
|
89
|
+
python -m twine check dist/main/* dist/fonts/*
|
|
90
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
91
|
+
with:
|
|
92
|
+
name: distributions
|
|
93
|
+
path: dist/
|
|
94
|
+
if-no-files-found: error
|
|
95
|
+
|
|
96
|
+
publish-fonts:
|
|
97
|
+
needs: build
|
|
98
|
+
# Only a new fonts version is uploaded. A reused one is already on PyPI, and
|
|
99
|
+
# PyPI rejects a duplicate filename; this workflow deliberately does not
|
|
100
|
+
# skip such an upload, so the job must not run at all.
|
|
101
|
+
if: needs.build.outputs.fonts_mode == 'new'
|
|
102
|
+
runs-on: ubuntu-latest
|
|
103
|
+
environment: pypi
|
|
104
|
+
permissions:
|
|
105
|
+
id-token: write
|
|
106
|
+
steps:
|
|
107
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
108
|
+
with:
|
|
109
|
+
name: distributions
|
|
110
|
+
path: dist/
|
|
111
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
112
|
+
with:
|
|
113
|
+
packages-dir: dist/fonts/
|
|
114
|
+
|
|
115
|
+
publish-main:
|
|
116
|
+
# The core upload needs the build as well as the fonts job. On a reuse the
|
|
117
|
+
# fonts job is *skipped*, and GitHub skips a job whose `needs:` job was
|
|
118
|
+
# skipped unless the job's `if` uses a status check function - `cancelled()`
|
|
119
|
+
# below. That is why the condition is written out instead of left implicit:
|
|
120
|
+
# an implicit `success()` would lose to the skip propagation and the reuse
|
|
121
|
+
# path would upload nothing and publish no release.
|
|
122
|
+
#
|
|
123
|
+
# `always()` would also defeat the skip propagation, but it runs the job in
|
|
124
|
+
# a cancelled run too, so it is not used.
|
|
125
|
+
#
|
|
126
|
+
# Core may continue past the fonts job only when that job uploaded a new
|
|
127
|
+
# fonts version or was skipped because the release reuses published files. A
|
|
128
|
+
# failed or cancelled fonts upload, a failed build, a cancelled run, and a
|
|
129
|
+
# skipped fonts job that was not a reuse all leave core unpublished, so a
|
|
130
|
+
# core release cannot advertise a fonts version that is not on PyPI.
|
|
131
|
+
needs: [build, publish-fonts]
|
|
132
|
+
if: >-
|
|
133
|
+
${{ !cancelled()
|
|
134
|
+
&& needs.build.result == 'success'
|
|
135
|
+
&& (needs.publish-fonts.result == 'success'
|
|
136
|
+
|| (needs.publish-fonts.result == 'skipped'
|
|
137
|
+
&& needs.build.outputs.fonts_mode == 'reuse')) }}
|
|
138
|
+
runs-on: ubuntu-latest
|
|
139
|
+
environment: pypi
|
|
140
|
+
permissions:
|
|
141
|
+
id-token: write
|
|
142
|
+
steps:
|
|
143
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
144
|
+
with:
|
|
145
|
+
name: distributions
|
|
146
|
+
path: dist/
|
|
147
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
148
|
+
with:
|
|
149
|
+
packages-dir: dist/main/
|
|
150
|
+
|
|
151
|
+
publish-github-release:
|
|
152
|
+
# Deliberately no `if:` here. The implicit `success()` plus the default skip
|
|
153
|
+
# propagation is the gate that matters: a core upload that failed or was
|
|
154
|
+
# skipped for any reason skips this job too, so a failed release cannot end
|
|
155
|
+
# in a public GitHub release. Adding a status check function here would
|
|
156
|
+
# weaken that, not strengthen it.
|
|
157
|
+
needs: publish-main
|
|
158
|
+
runs-on: ubuntu-latest
|
|
159
|
+
permissions:
|
|
160
|
+
contents: write
|
|
161
|
+
steps:
|
|
162
|
+
# The manifest step runs this repository's own script, so the tagged
|
|
163
|
+
# commit must be present. Its bytes are the ones already required to have
|
|
164
|
+
# passed CI on main, and `gh` takes the token from the environment, not
|
|
165
|
+
# from git credentials.
|
|
166
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
167
|
+
with:
|
|
168
|
+
persist-credentials: false
|
|
169
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
170
|
+
with:
|
|
171
|
+
name: distributions
|
|
172
|
+
path: dist/
|
|
173
|
+
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
|
|
174
|
+
with:
|
|
175
|
+
python-version: '3.12'
|
|
176
|
+
# The manifest pins each project's own version. Core is the tag; fonts is
|
|
177
|
+
# the version the tagged commit declares, which is the version of the
|
|
178
|
+
# files in dist/fonts whether they were built or fetched.
|
|
179
|
+
- name: Read the declared fonts version
|
|
180
|
+
id: fonts_version
|
|
181
|
+
run: |
|
|
182
|
+
python - <<'PY' >> "$GITHUB_OUTPUT"
|
|
183
|
+
import tomllib
|
|
184
|
+
from pathlib import Path
|
|
185
|
+
|
|
186
|
+
project = tomllib.loads(
|
|
187
|
+
Path('packages/textflowkit-fonts/pyproject.toml').read_text(encoding='utf-8')
|
|
188
|
+
)['project']
|
|
189
|
+
print(f"version={project['version']}")
|
|
190
|
+
PY
|
|
191
|
+
- name: Hash the downloaded distributions
|
|
192
|
+
env:
|
|
193
|
+
RELEASE_TAG: ${{ github.ref_name }}
|
|
194
|
+
FONTS_VERSION: ${{ steps.fonts_version.outputs.version }}
|
|
195
|
+
run: |
|
|
196
|
+
python scripts/write_release_manifest.py \
|
|
197
|
+
--output SHA256SUMS --version "$RELEASE_TAG" --fonts-version "$FONTS_VERSION" \
|
|
198
|
+
dist/main/* dist/fonts/*
|
|
199
|
+
- name: Create draft with the exact PyPI distributions
|
|
200
|
+
env:
|
|
201
|
+
GH_TOKEN: ${{ github.token }}
|
|
202
|
+
RELEASE_TAG: ${{ github.ref_name }}
|
|
203
|
+
run: |
|
|
204
|
+
gh release create "$RELEASE_TAG" dist/main/* dist/fonts/* SHA256SUMS \
|
|
205
|
+
--repo "$GITHUB_REPOSITORY" --verify-tag --draft \
|
|
206
|
+
--title "$RELEASE_TAG" --generate-notes
|
|
207
|
+
- name: Publish complete GitHub release
|
|
208
|
+
env:
|
|
209
|
+
GH_TOKEN: ${{ github.token }}
|
|
210
|
+
RELEASE_TAG: ${{ github.ref_name }}
|
|
211
|
+
run: gh release edit "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" --draft=false
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
#
|
|
3
|
+
# Optional Linux deployment surface for the JSON HTTP adapter. The product stays
|
|
4
|
+
# native Windows; nothing here is needed for a local install, and the Compose
|
|
5
|
+
# file beside this one is the supported way to run it.
|
|
6
|
+
#
|
|
7
|
+
# What this image does NOT provide, and cannot:
|
|
8
|
+
#
|
|
9
|
+
# * A public service. `compose.yaml` publishes the host port to 127.0.0.1 only,
|
|
10
|
+
# so the container is reachable from its own host and nowhere else.
|
|
11
|
+
# Terminating TLS, authenticating callers beyond the Bearer token, and
|
|
12
|
+
# enforcing an independent rate and egress policy belong to a trusted gateway
|
|
13
|
+
# in front of it. The built-in limiter is per process, not a distributed
|
|
14
|
+
# quota.
|
|
15
|
+
# * A working URL-job path on its own. Production URL acquisition requires an
|
|
16
|
+
# SSRF-filtering TEXTFLOWKIT_EGRESS_PROXY and refuses to run without one, so
|
|
17
|
+
# URL jobs fail closed until an operator supplies a proxy that blocks
|
|
18
|
+
# private/loopback destinations and DNS rebinding. No proxy is bundled. Local
|
|
19
|
+
# files and /health need no egress.
|
|
20
|
+
# * More than one process. The durable SQLite store has one owning process; a
|
|
21
|
+
# second one sharing TEXTFLOWKIT_DB would reap the first one's live jobs.
|
|
22
|
+
#
|
|
23
|
+
# The token is deliberately not here. The server fails at startup with a named
|
|
24
|
+
# error when TEXTFLOWKIT_API_TOKEN is missing, which is better than an
|
|
25
|
+
# image-embedded default that every puller would share.
|
|
26
|
+
|
|
27
|
+
# Pinned to a Python the CI matrix covers (3.10-3.13, linted on 3.12) and to a
|
|
28
|
+
# named Debian release rather than to `latest`, which would move under a rebuild.
|
|
29
|
+
# Not a digest pin: resolving a digest needs a registry lookup that could not be
|
|
30
|
+
# performed where this was written, and an invented one would be worse than an
|
|
31
|
+
# honest tag.
|
|
32
|
+
FROM python:3.12-slim-bookworm
|
|
33
|
+
|
|
34
|
+
# ffmpeg is required in every profile: decode, and the ffprobe duration bound the
|
|
35
|
+
# production profile enforces before a full decode.
|
|
36
|
+
# ca-certificates is the trust store URL acquisition needs.
|
|
37
|
+
# libstdc++6 is installed unconditionally so the Node binary copied below has a
|
|
38
|
+
# C++ runtime to link against whatever the slim base happens to carry; the gate
|
|
39
|
+
# below would fail the build if that binary could not run at all.
|
|
40
|
+
RUN set -eux; \
|
|
41
|
+
apt-get update; \
|
|
42
|
+
apt-get install -y --no-install-recommends ca-certificates ffmpeg libstdc++6; \
|
|
43
|
+
rm -rf /var/lib/apt/lists/*
|
|
44
|
+
|
|
45
|
+
# The JavaScript runtime yt-dlp uses to solve some YouTube player challenges.
|
|
46
|
+
# It comes from the official Node image rather than from the base distribution,
|
|
47
|
+
# because the installed yt-dlp requires Node >= 22 (yt_dlp/utils/_jsruntime.py,
|
|
48
|
+
# `NodeJsRuntime.MIN_SUPPORTED_VERSION`) and the version the base distribution
|
|
49
|
+
# carries was not measured here. Taking the runtime from a tag that names the
|
|
50
|
+
# major version does not by itself prove the floor either, which is why the gate
|
|
51
|
+
# below runs the binary and fails the build rather than trusting a tag name.
|
|
52
|
+
COPY --from=node:22-bookworm-slim /usr/local/bin/node /usr/local/bin/node
|
|
53
|
+
|
|
54
|
+
# yt-dlp's Node floor, taken from the installed package and re-checked against it
|
|
55
|
+
# by tests/test_container_example.py, so a yt-dlp upgrade that raises the floor
|
|
56
|
+
# fails the suite rather than only the build.
|
|
57
|
+
ARG NODE_MIN_MAJOR=22
|
|
58
|
+
RUN set -eux; \
|
|
59
|
+
node --version; \
|
|
60
|
+
node -e "const v = parseInt(process.versions.node, 10); if (v < ${NODE_MIN_MAJOR}) { console.error('node ' + process.versions.node + ' is below the yt-dlp Node floor of ${NODE_MIN_MAJOR}.0.0 (yt_dlp/utils/_jsruntime.py); use a newer node image, or raise NODE_MIN_MAJOR deliberately'); process.exit(1) }"; \
|
|
61
|
+
ffmpeg -version | head -n 1; \
|
|
62
|
+
ffprobe -version | head -n 1
|
|
63
|
+
|
|
64
|
+
# Nonroot runtime. The uid and gid are fixed so the Compose file can name them
|
|
65
|
+
# and the volumes below initialize with the right ownership.
|
|
66
|
+
RUN set -eux; \
|
|
67
|
+
groupadd --gid 10001 textflowkit; \
|
|
68
|
+
useradd --uid 10001 --gid 10001 --create-home --shell /usr/sbin/nologin textflowkit; \
|
|
69
|
+
install -d -o textflowkit -g textflowkit \
|
|
70
|
+
/srv/textflowkit \
|
|
71
|
+
/srv/textflowkit/input \
|
|
72
|
+
/srv/textflowkit/output \
|
|
73
|
+
/srv/textflowkit/work \
|
|
74
|
+
/srv/textflowkit/state \
|
|
75
|
+
/home/textflowkit/.cache
|
|
76
|
+
|
|
77
|
+
WORKDIR /app
|
|
78
|
+
COPY pyproject.toml README.md LICENSE ./
|
|
79
|
+
COPY src ./src
|
|
80
|
+
# The base dependencies are yt-dlp and openai-whisper, so this image is large:
|
|
81
|
+
# openai-whisper pulls a torch build. That is the product's own requirement, not
|
|
82
|
+
# something this file adds.
|
|
83
|
+
RUN python -m pip install --no-cache-dir --upgrade pip \
|
|
84
|
+
&& python -m pip install --no-cache-dir ".[http]"
|
|
85
|
+
|
|
86
|
+
# Non-secret defaults only. The roots are the directories created above; Compose
|
|
87
|
+
# repeats them so the volumes it mounts land on the paths the service reads.
|
|
88
|
+
# Concurrency stays at the documented default of 1: the store has one owner.
|
|
89
|
+
ENV PYTHONUNBUFFERED=1 \
|
|
90
|
+
PYTHONDONTWRITEBYTECODE=1 \
|
|
91
|
+
PIP_NO_CACHE_DIR=1 \
|
|
92
|
+
TEXTFLOWKIT_PROFILE=production \
|
|
93
|
+
TEXTFLOWKIT_INPUT_ROOT=/srv/textflowkit/input \
|
|
94
|
+
TEXTFLOWKIT_OUTPUT_ROOT=/srv/textflowkit/output \
|
|
95
|
+
TEXTFLOWKIT_WORK_ROOT=/srv/textflowkit/work \
|
|
96
|
+
TEXTFLOWKIT_DB=/srv/textflowkit/state/jobs.db \
|
|
97
|
+
TEXTFLOWKIT_MAX_CONCURRENCY=1
|
|
98
|
+
|
|
99
|
+
USER 10001:10001
|
|
100
|
+
|
|
101
|
+
# Liveness only, and it has to carry the Bearer token: the production middleware
|
|
102
|
+
# covers /health like every other route. The token is read from the process
|
|
103
|
+
# environment, never passed as an argument, so `ps` cannot show it. That is not
|
|
104
|
+
# secrecy: Docker records an environment variable in the container's
|
|
105
|
+
# configuration (`docker inspect`), and `/proc/<pid>/environ` exposes it inside
|
|
106
|
+
# the container to anything running as the same user. A missing token makes this
|
|
107
|
+
# probe fail rather than pass - the same fail-closed direction as the server.
|
|
108
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 \
|
|
109
|
+
CMD ["python", "-c", "import os, urllib.request; request = urllib.request.Request('http://127.0.0.1:8767/health', headers={'Authorization': 'Bearer ' + os.environ['TEXTFLOWKIT_API_TOKEN']}); body = urllib.request.urlopen(request, timeout=3).read(); assert b'status' in body, body"]
|
|
110
|
+
|
|
111
|
+
# 0.0.0.0 inside the container, so the published host port reaches the server;
|
|
112
|
+
# `--allow-remote` is the explicit opt-in the adapter requires for a bind beyond
|
|
113
|
+
# loopback, and the host side of that publication stays loopback-only in Compose.
|
|
114
|
+
EXPOSE 8767
|
|
115
|
+
CMD ["textflowkit-http", "--host", "0.0.0.0", "--port", "8767", "--allow-remote"]
|
|
@@ -7,9 +7,18 @@ This document is plain-language guidance, not legal advice.
|
|
|
7
7
|
textflowkit is distributed under the **Apache License 2.0**, which includes:
|
|
8
8
|
|
|
9
9
|
- **§7 — Disclaimer of Warranty:** the work is provided on an "AS IS" BASIS,
|
|
10
|
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied —
|
|
11
|
+
unless required by applicable law or agreed to in writing.
|
|
12
|
+
- **§8 — Limitation of Liability:** in no event shall any contributor be liable
|
|
13
|
+
for damages arising from use of the work — unless required by applicable law
|
|
14
|
+
(deliberate and grossly negligent acts are the example the license gives) or
|
|
15
|
+
agreed to in writing.
|
|
16
|
+
|
|
17
|
+
Neither of those is an absolute rule: the license sets aside the disclaimer and
|
|
18
|
+
the liability limit where applicable law requires otherwise, or where a separate
|
|
19
|
+
written agreement says otherwise. This section is a plain-language summary; the
|
|
20
|
+
[`LICENSE`](LICENSE) file is the controlling text and is what you should read for
|
|
21
|
+
the terms that apply to you.
|
|
13
22
|
|
|
14
23
|
The project ships no warranty of accuracy. **Transcripts produced by speech-to-text
|
|
15
24
|
models contain errors.** Do not rely on output for legal, medical, safety-critical,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: textflowkit
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: Cross-platform media transcription toolkit: URL or file in, timestamped transcripts and subtitle files out.
|
|
5
5
|
Project-URL: Homepage, https://www.textflowkit.org/
|
|
6
6
|
Project-URL: Issues, https://github.com/scottconverse/textflowkit/issues
|
|
@@ -33,6 +33,8 @@ Requires-Dist: reportlab>=4.0; extra == 'all'
|
|
|
33
33
|
Requires-Dist: textflowkit-fonts<0.2,>=0.1.5; extra == 'all'
|
|
34
34
|
Requires-Dist: uvicorn>=0.30; extra == 'all'
|
|
35
35
|
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
37
|
+
Requires-Dist: hatchling>=1.25; extra == 'dev'
|
|
36
38
|
Requires-Dist: pypdf>=5; extra == 'dev'
|
|
37
39
|
Requires-Dist: pytest>=8; extra == 'dev'
|
|
38
40
|
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
@@ -43,6 +45,8 @@ Provides-Extra: export
|
|
|
43
45
|
Requires-Dist: python-docx>=1.1; extra == 'export'
|
|
44
46
|
Requires-Dist: reportlab>=4.0; extra == 'export'
|
|
45
47
|
Requires-Dist: textflowkit-fonts<0.2,>=0.1.5; extra == 'export'
|
|
48
|
+
Provides-Extra: faster-whisper
|
|
49
|
+
Requires-Dist: faster-whisper<2,>=1.2.1; extra == 'faster-whisper'
|
|
46
50
|
Provides-Extra: http
|
|
47
51
|
Requires-Dist: fastapi>=0.115; extra == 'http'
|
|
48
52
|
Requires-Dist: pydantic>=2.7; extra == 'http'
|
|
@@ -57,7 +61,10 @@ Cross-platform media transcription toolkit. **One core, one CLI, thin adapters.*
|
|
|
57
61
|
|
|
58
62
|
[Project landing page](https://www.textflowkit.org/) ·
|
|
59
63
|
[PyPI package](https://pypi.org/project/textflowkit/) ·
|
|
60
|
-
[GitHub releases](https://github.com/scottconverse/textflowkit/releases)
|
|
64
|
+
[GitHub releases](https://github.com/scottconverse/textflowkit/releases) ·
|
|
65
|
+
[User manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md)
|
|
66
|
+
|
|
67
|
+
**Current release: [v0.1.6](https://github.com/scottconverse/textflowkit/releases/tag/v0.1.6).**
|
|
61
68
|
|
|
62
69
|
The [static site deployment](https://github.com/scottconverse/textflowkit/blob/main/docs/site-deployment.md) is hosted on Cloudflare
|
|
63
70
|
Pages. GitHub remains the source and CI host; the website does not run the
|
|
@@ -69,16 +76,31 @@ products, AI harnesses, and agents.
|
|
|
69
76
|
|
|
70
77
|
---
|
|
71
78
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
## Quickstart
|
|
80
|
+
|
|
81
|
+
Requires **Python ≥ 3.10** and **ffmpeg** on `PATH`.
|
|
82
|
+
|
|
83
|
+
Windows (PowerShell or CMD):
|
|
84
|
+
|
|
85
|
+
```powershell
|
|
86
|
+
python -m pip install textflowkit
|
|
87
|
+
textflowkit doctor
|
|
88
|
+
textflowkit transcribe .\meeting.mp4 --formats srt,txt --output-dir .\out
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
macOS or Linux:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
python -m pip install textflowkit
|
|
95
|
+
textflowkit doctor
|
|
96
|
+
textflowkit transcribe ./meeting.mp4 --formats srt,txt --output-dir ./out
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`doctor` prints the Python, ffmpeg, yt-dlp, JavaScript-runtime, and
|
|
100
|
+
optional-extra versions this install will use. The `transcribe` example writes
|
|
101
|
+
the SRT and TXT files into `out\` and prints the path of each one; give it a
|
|
102
|
+
supported URL instead of a path to fetch remote media. See **Install** below for
|
|
103
|
+
the extras, and **Usage** for language, translation, speaker labels, and resume.
|
|
82
104
|
|
|
83
105
|
---
|
|
84
106
|
|
|
@@ -99,6 +121,8 @@ URL or file ─► detect platform ─► acquire media ─► ffmpeg
|
|
|
99
121
|
TXT SRT VTT JSON Markdown
|
|
100
122
|
```
|
|
101
123
|
|
|
124
|
+
DOCX and PDF are available through the optional `export` extra.
|
|
125
|
+
|
|
102
126
|
## Architecture
|
|
103
127
|
|
|
104
128
|
The design principle is **one engine, three doors**. Everything of substance lives in
|
|
@@ -108,7 +132,7 @@ the core; the interfaces are thin.
|
|
|
108
132
|
|---|---|---|
|
|
109
133
|
| **Core** | `src/textflowkit/core` | Canonical transcript model, pipeline orchestration |
|
|
110
134
|
| **Sources** | `src/textflowkit/sources` | Per-platform URL normalization + media acquisition |
|
|
111
|
-
| **Renderers** | `src/textflowkit/render` | TXT / SRT / VTT / JSON / Markdown output |
|
|
135
|
+
| **Renderers** | `src/textflowkit/render` | TXT / SRT / VTT / JSON / Markdown / DOCX / PDF output |
|
|
112
136
|
| **CLI** | `src/textflowkit/cli.py` | Reference interface (subprocess-friendly) |
|
|
113
137
|
| **MCP** | `src/textflowkit/adapters/mcp_server.py` | stdio + Streamable HTTP, for AI harnesses |
|
|
114
138
|
| **HTTP** | `src/textflowkit/adapters/http_server.py` | JSON API, for software products and web frontends |
|
|
@@ -192,7 +216,11 @@ to preserve it.
|
|
|
192
216
|
|
|
193
217
|
The same version's wheel and source archive are also on the
|
|
194
218
|
[GitHub release page](https://github.com/scottconverse/textflowkit/releases/latest).
|
|
195
|
-
|
|
219
|
+
Starting with the next release, every release published by this project's release
|
|
220
|
+
workflow carries a `SHA256SUMS` asset listing the SHA-256 hash of each wheel and
|
|
221
|
+
source archive it contains, so you can check a download with
|
|
222
|
+
`sha256sum -c SHA256SUMS`. Releases published before that change, v0.1.5
|
|
223
|
+
included, have no such asset. For editable source development, see
|
|
196
224
|
[CONTRIBUTING.md](https://github.com/scottconverse/textflowkit/blob/main/CONTRIBUTING.md).
|
|
197
225
|
|
|
198
226
|
## Usage
|
|
@@ -278,6 +306,16 @@ Bearer token, explicit roots, durable SQLite jobs, and request/rate/media/output
|
|
|
278
306
|
limits; URL input additionally requires an SSRF-filtering egress proxy. See
|
|
279
307
|
[adapter deployment details](https://github.com/scottconverse/textflowkit/blob/main/docs/adapters.md#developer-mode-and-production-profile).
|
|
280
308
|
|
|
309
|
+
A `Dockerfile` and a `compose.yaml` for that profile sit at the repository root:
|
|
310
|
+
ffmpeg and a JavaScript runtime in the image, a nonroot runtime user, the host
|
|
311
|
+
port published to loopback only, and a token the operator supplies (there is no
|
|
312
|
+
default). This is a Linux deployment **example**, not a published service - it
|
|
313
|
+
ships no TLS gateway and no egress proxy, so URL jobs fail closed until you
|
|
314
|
+
provide one. The image has not been built or started anywhere: only static
|
|
315
|
+
contract checks cover it, and running an actual build is deliberately out of
|
|
316
|
+
scope here, so no image is claimed to build or start. See
|
|
317
|
+
[the container example](https://github.com/scottconverse/textflowkit/blob/main/docs/adapters.md#container-example-dockerfile-and-compose).
|
|
318
|
+
|
|
281
319
|
## Durable, bounded, cancellable
|
|
282
320
|
|
|
283
321
|
```bash
|
|
@@ -297,12 +335,37 @@ web frontend share the job contract without blocking a request.
|
|
|
297
335
|
|
|
298
336
|
## Status
|
|
299
337
|
|
|
300
|
-
**v0.1.
|
|
301
|
-
coverage
|
|
338
|
+
**v0.1.6 release.** Core, CLI, MCP, and HTTP have automated
|
|
339
|
+
coverage. This release is the post-v0.1.5 review repair set: security hardening
|
|
340
|
+
for media acquisition and the HTTP and MCP adapters, safer subtitle wrapping and
|
|
341
|
+
output-file publication, job and checkpoint storage corrections, an opt-in
|
|
342
|
+
faster-whisper engine, a container example, and fail-closed release guards for
|
|
343
|
+
tagged versions, README claims, and exact-commit CI.
|
|
344
|
+
The v0.1.5 release added a speech-bearing self-test, full-media duration,
|
|
345
|
+
retained word timings, optional PDF fonts, and tokenless PyPI publishing, and its
|
|
346
|
+
live evidence is the only live evidence recorded on this page.
|
|
347
|
+
Windows-native ROCm and a local Windows YouTube run on the v0.1.5 release commit were verified.
|
|
348
|
+
That run's receipt records the clip, timestamps, and hashes; the checklist's
|
|
349
|
+
recognized-speech assertion was added after it, so treat it as shape evidence for
|
|
350
|
+
that commit rather than proof of what was said.
|
|
302
351
|
The GitHub-hosted YouTube attempt was blocked by a bot challenge, so hosted
|
|
303
|
-
live transcription is not verified.
|
|
352
|
+
live transcription is not verified. No v0.1.6 tag, upload, or live run is verified here.
|
|
304
353
|
This does not imply that all 13 platforms or every harness workflow has been
|
|
305
|
-
tested end to end. See [
|
|
354
|
+
tested end to end. See the [user manual](https://github.com/scottconverse/textflowkit/blob/main/docs/user-manual.md)
|
|
355
|
+
and [roadmap](https://github.com/scottconverse/textflowkit/blob/main/docs/roadmap.md).
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
> ## ⚠️ NO WARRANTY — AS IS
|
|
360
|
+
>
|
|
361
|
+
> **This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND**, express or
|
|
362
|
+
> implied, including but not limited to the warranties of MERCHANTABILITY, FITNESS
|
|
363
|
+
> FOR A PARTICULAR PURPOSE, and NONINFRINGEMENT. See [LICENSE](https://github.com/scottconverse/textflowkit/blob/main/LICENSE) (Apache-2.0,
|
|
364
|
+
> §7–8) for the full disclaimer and limitation of liability.
|
|
365
|
+
>
|
|
366
|
+
> **You are responsible for what you transcribe.** textflowkit can fetch media from
|
|
367
|
+
> third-party platforms. Copyright, terms-of-service, and privacy obligations for any
|
|
368
|
+
> media you choose to process are **yours alone**. See [LEGAL.md](https://github.com/scottconverse/textflowkit/blob/main/LEGAL.md).
|
|
306
369
|
|
|
307
370
|
## License
|
|
308
371
|
|