tv-recorder 0.0.0.dev2__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.
@@ -0,0 +1,94 @@
1
+ ---
2
+ name: tv-recorder-add-channel
3
+ description: Add or repair a channel entry in a Python tv-recorder project's defaults.yaml. Use when the user asks to add a public TV/news/live channel, discover or stabilize an HLS m3u8 URL, choose between direct URL/API/browser discovery strategies, configure video/audio selection, or run smoke tests for a tv-recorder channel.
4
+ ---
5
+
6
+ # TV Recorder Add Channel
7
+
8
+ ## Workflow
9
+
10
+ Use this skill when editing `src/tv_recorder/defaults.yaml` for a `tv-recorder` project.
11
+
12
+ 1. Read `defaults.yaml`, `config.py`, `stream_finder.py`, and `recorder.py` before editing. Preserve the existing schema and local style.
13
+ 2. Read the requested channel URL or channel list. If a companion file such as `chaines.md` exists, inspect the relevant lines.
14
+ 3. Prefer the least fragile working strategy:
15
+ - Stable direct HLS URL or public API returning HLS.
16
+ - Browser discovery with Playwright when the URL is tokenized, embedded, or only emitted after consent/play clicks.
17
+ - Alternate public HLS only when the official site is geoblocked, stale, DRM-protected, or unusable from the current location.
18
+ 4. Validate candidates with a real short ffmpeg capture, not only HTTP status or dry-run. A master manifest can return `200` while every child variant fails.
19
+ 5. Patch `defaults.yaml` only after a candidate records successfully.
20
+ 6. Run one focused smoke test for the new/changed channel. Use 1 minute unless the user asks otherwise.
21
+ 7. Report the source key, strategy used, test result, output path, and any caveat such as geoblocking or third-party alternate feed.
22
+
23
+ ## Discovery
24
+
25
+ Use these options in order, stopping once one gives a reliable recording:
26
+
27
+ - **Direct manifest/API**: Check the live page source, network calls, JSON APIs, and public stream indexes for `.m3u8`. Configure `stream_request_urls` and `steps: []`.
28
+ - **Browser discovery**: Configure `start_url`, `stream_url_pattern`, `steps: [{action: wait_for_stream}]`, and add reject patterns for ads, analytics, VOD, or redirector manifests.
29
+ - **JSON response extraction**: Configure `stream_response_url_patterns` and `stream_response_json_keys` when a validation/API endpoint returns the stream URL.
30
+ - **Separate audio HLS**: If the master manifest uses `#EXT-X-MEDIA` audio groups, set `recording.hls.separate_audio: true`, `video.direct_variant: false`, and configure `audio.language`.
31
+
32
+ Read `references/channel-recipes.md` for concrete recipes and failure patterns before repairing a difficult channel.
33
+
34
+ ## Validation
35
+
36
+ For each new or repaired channel:
37
+
38
+ ```powershell
39
+ .\.venv\Scripts\tv-recorder.exe <source-key> now 1m --output-dir recordings\smoke-<stamp> --timeout-ms 60000
40
+ ```
41
+
42
+ If validating several channels, cap parallelism. Previous project smoke tests used 3 or 5 concurrent recordings.
43
+
44
+ After recording, inspect the file with the bundled ffmpeg:
45
+
46
+ ```powershell
47
+ @'
48
+ import subprocess, imageio_ffmpeg
49
+ from pathlib import Path
50
+ ffmpeg = imageio_ffmpeg.get_ffmpeg_exe()
51
+ path = Path(r"<recorded-file.mp4>")
52
+ subprocess.run([ffmpeg, "-hide_banner", "-i", str(path)])
53
+ '@ | .\.venv\Scripts\python.exe -
54
+ ```
55
+
56
+ A successful entry should produce a playable MP4 with nonzero size, about the requested duration, and expected video/audio streams.
57
+
58
+ ## Defaults YAML Pattern
59
+
60
+ Use stable, lowercase source keys. Keep display names human-readable.
61
+
62
+ ```yaml
63
+ source-key:
64
+ display_name: Channel Name
65
+ start_url: "https://example.com/live"
66
+ stream_url_pattern: "\\.m3u8(\\?|$)"
67
+ stream_request_urls:
68
+ - "https://example.com/live/master.m3u8"
69
+ stream_url_reject_patterns:
70
+ - "dai\\.google\\.com"
71
+ output_extension: "mp4"
72
+ recording:
73
+ video:
74
+ height: 720
75
+ audio:
76
+ language: "eng"
77
+ steps: []
78
+ user_agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125 Safari/537.36"
79
+ ```
80
+
81
+ For browser discovery, omit `stream_request_urls` and use:
82
+
83
+ ```yaml
84
+ steps:
85
+ - action: wait_for_stream
86
+ ```
87
+
88
+ ## Guardrails
89
+
90
+ - Do not trust a candidate because `Invoke-WebRequest` or `context.request.get()` returns `200`; run ffmpeg for a few seconds.
91
+ - Reject ad manifests such as `dai.google.com` and Dailymotion redirectors when they do not record.
92
+ - Prefer official sources, but accept that a stable direct alternate can be as reasonable as browsing a fragile page.
93
+ - If DRM or HLS SAMPLE-AES appears, do not try to bypass protection. Mark it as unsupported.
94
+ - Keep changes scoped to channel config unless the project lacks a capability needed by multiple channels.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "TV Recorder Add Channel"
3
+ short_description: "Add and validate channels in tv-recorder defaults.yaml."
4
+ default_prompt: "Add a new tv-recorder channel entry to defaults.yaml and validate it with a short recording smoke test."
@@ -0,0 +1,105 @@
1
+ # Channel Recipes
2
+
3
+ Use this reference when a channel is not obvious from `defaults.yaml`.
4
+
5
+ ## Direct HLS
6
+
7
+ Use `stream_request_urls` with `steps: []` when a manifest records directly. This is fastest and most stable when the URL is not tokenized per session.
8
+
9
+ Validate with ffmpeg, because a master manifest can be alive while its variants return `400`, `403`, or `404`.
10
+
11
+ Examples observed in this project:
12
+ - Global News regional feeds: direct Corus `.isml/.m3u8` manifests.
13
+ - CPAC: direct master with separate audio tracks.
14
+ - NHK: old master returned `200` but children failed; `https://masterpl.hls.nhkworld.jp/hls/w/live/smarttv.m3u8` recorded successfully.
15
+
16
+ ## API-Returned Stream
17
+
18
+ Use `stream_response_url_patterns` and `stream_response_json_keys` when a public endpoint returns a stream URL. Keep browser steps empty if the API is enough.
19
+
20
+ Example pattern:
21
+
22
+ ```yaml
23
+ stream_request_urls:
24
+ - "https://service.example/api/live?output=json"
25
+ stream_response_url_patterns:
26
+ - "service\\.example/api/live"
27
+ stream_response_json_keys:
28
+ - "url"
29
+ steps: []
30
+ ```
31
+
32
+ ## Browser Discovery
33
+
34
+ Use browser discovery when:
35
+ - the manifest is tokenized per session,
36
+ - a consent or play action is required,
37
+ - the source is embedded in an iframe/player,
38
+ - the direct URL expires quickly.
39
+
40
+ Start with:
41
+
42
+ ```yaml
43
+ steps:
44
+ - action: wait_for_stream
45
+ ```
46
+
47
+ The project's auto-start logic tries common consent and play controls. Add explicit steps only when required.
48
+
49
+ ## Reject Patterns
50
+
51
+ Add `stream_url_reject_patterns` for candidates that are detected but not recordable or not the desired program:
52
+
53
+ - `dai\\.google\\.com` for ad-insertion manifests.
54
+ - `originpath=/linear/hls` when a provider exposes an ad/linear wrapper instead of the clean live stream.
55
+ - `dmxleo\\.dailymotion\\.com` for Dailymotion manifest wrappers that are not final media.
56
+ - `cdndirector\\.dailymotion\\.com` when ffmpeg receives `403`; prefer the final `live.*.dmcdn.net/...m3u8` variant if discovered.
57
+
58
+ ## Video and Audio Selection
59
+
60
+ Set `recording.video.height` when the stream has multiple variants. Use `direct_variant: false` when ffmpeg should receive the master manifest, especially when separate audio groups are involved.
61
+
62
+ For master manifests with separate audio:
63
+
64
+ ```yaml
65
+ recording:
66
+ hls:
67
+ separate_audio: true
68
+ video:
69
+ height: 720
70
+ direct_variant: false
71
+ audio:
72
+ language: "en"
73
+ ```
74
+
75
+ For French audio, observed language codes may be `fre`, `fra`, or `fr`; use what ffmpeg or the HLS manifest reports.
76
+
77
+ When audio-description tracks exist, use `reject_comments` if ffmpeg metadata exposes the descriptive track:
78
+
79
+ ```yaml
80
+ audio:
81
+ language: "fre"
82
+ comment: "Français"
83
+ reject_comments:
84
+ - "audio_dv"
85
+ ```
86
+
87
+ ## Failure Meanings
88
+
89
+ - `403 Forbidden` on the manifest or variants: likely geoblocking, missing headers, expired token, or a redirector that should be rejected.
90
+ - `Output file does not contain any stream`: master was reachable but variants failed or were empty.
91
+ - `SAMPLE-AES` or `KEYFORMAT`: protected HLS; do not attempt to bypass.
92
+ - A small partial file with nonzero size and rc=1: often a late stream interruption, timestamp issue, or too-short capture; retest before changing config.
93
+ - A live endpoint returning `false`, `is_on_air: false`, or no player data can mean the channel is event-based rather than broken.
94
+ - YouTube embeds can expose no HLS and return `EMBEDDER_IDENTITY_DENIED`; do not treat that as a normal HLS channel unless the project intentionally adds a YouTube resolver.
95
+ - Do not replace a channel with a different but similarly named feed just to make a smoke test pass. Keep `TV5MONDE Europe` as Europe, `UK Parliament` as UK Parliament, etc., unless the user explicitly accepts a substitute.
96
+
97
+ ## Smoke Testing
98
+
99
+ Use 1-minute tests for confidence:
100
+
101
+ ```powershell
102
+ .\.venv\Scripts\tv-recorder.exe <source-key> now 1m --output-dir recordings\channel-smoke --timeout-ms 60000
103
+ ```
104
+
105
+ For many channels, use a small worker pool. This project previously used 3 or 5 simultaneous recordings. Treat repeated failures across 3 attempts as real configuration problems; treat one failure followed by successes as a possible transient.
@@ -0,0 +1,33 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+ id-token: write
10
+
11
+ jobs:
12
+ publish:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - name: Check out repository
16
+ uses: actions/checkout@v4
17
+
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.12"
22
+
23
+ - name: Validate release version
24
+ run: python scripts/check_version.py --tag "${{ github.event.release.tag_name }}"
25
+
26
+ - name: Install build tools
27
+ run: python -m pip install build
28
+
29
+ - name: Build package
30
+ run: python -m build
31
+
32
+ - name: Publish package
33
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,7 @@
1
+ .venv/
2
+ recordings/
3
+ dist/
4
+ __pycache__/
5
+ *.py[cod]
6
+ .pytest_cache/
7
+ *.egg-info/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 tv-recorder contributors
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,186 @@
1
+ Metadata-Version: 2.4
2
+ Name: tv-recorder
3
+ Version: 0.0.0.dev2
4
+ Summary: Command-line recorder for public live TV streams.
5
+ Project-URL: Homepage, https://github.com/onclefranck/tv-recorder
6
+ Project-URL: Repository, https://github.com/onclefranck/tv-recorder
7
+ Project-URL: Issues, https://github.com/onclefranck/tv-recorder/issues
8
+ Author: tv-recorder contributors
9
+ License: MIT License
10
+
11
+ Copyright (c) 2026 tv-recorder contributors
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+ License-File: LICENSE
31
+ Keywords: cli,ffmpeg,hls,playwright,recorder,tv
32
+ Classifier: Development Status :: 3 - Alpha
33
+ Classifier: Environment :: Console
34
+ Classifier: Intended Audience :: End Users/Desktop
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Operating System :: OS Independent
37
+ Classifier: Programming Language :: Python :: 3
38
+ Classifier: Programming Language :: Python :: 3.11
39
+ Classifier: Programming Language :: Python :: 3.12
40
+ Classifier: Programming Language :: Python :: 3.13
41
+ Classifier: Topic :: Multimedia :: Video
42
+ Classifier: Topic :: Utilities
43
+ Requires-Python: >=3.11
44
+ Requires-Dist: imageio-ffmpeg>=0.6.0
45
+ Requires-Dist: playwright>=1.44
46
+ Requires-Dist: pytimeparse2>=1.7.1
47
+ Requires-Dist: pyyaml>=6.0.1
48
+ Provides-Extra: dev
49
+ Requires-Dist: build>=1.2; extra == 'dev'
50
+ Requires-Dist: pytest>=8.0; extra == 'dev'
51
+ Requires-Dist: twine>=5.0; extra == 'dev'
52
+ Description-Content-Type: text/markdown
53
+
54
+ # tv-recorder
55
+
56
+ Command-line recorder for public live TV streams.
57
+
58
+ ## Local Installation
59
+
60
+ ```powershell
61
+ python -m pip install -e .
62
+ ```
63
+
64
+ Chromium is installed automatically on first use if Playwright does not already have it.
65
+
66
+ The recorder uses the `ffmpeg` binary provided by `imageio-ffmpeg`.
67
+
68
+ ## Usage
69
+
70
+ ```powershell
71
+ tv-recorder radio-canada.ca now 2h
72
+ ```
73
+
74
+ The general form is:
75
+
76
+ ```text
77
+ tv-recorder SOURCE START DURATION
78
+ ```
79
+
80
+ Examples:
81
+
82
+ ```powershell
83
+ tv-recorder radio-canada.ca now 30m
84
+ tv-recorder radio-canada.ca 2026-05-24T20:00:00 2h --output-dir recordings
85
+ tv-recorder tvaplus.ca now 30m
86
+ tv-recorder telequebec.tv now 30m
87
+ tv-recorder cpac-english now 30m
88
+ tv-recorder cpac-francais now 30m
89
+ tv-recorder canal-assemblee-nationale now 30m
90
+ tv-recorder globalnews-national now 30m
91
+ tv-recorder globalnews-montreal now 30m
92
+ tv-recorder radio-canada.ca now 10m --headful
93
+ tv-recorder radio-canada.ca now 10m --dry-run
94
+ tv-recorder radio-canada.ca now 10m --debug
95
+ ```
96
+
97
+ `START` accepts `now` or a local ISO date. `DURATION` accepts values such as `90s`, `30m`, `2h`, or `01:30:00`.
98
+
99
+ By default, the CLI runs at `--info` level and prints the effective recording URL or inputs. During recording, a small activity indicator moves when ffmpeg emits progress. Use `--debug` to show discovery details, step results, and ffmpeg output.
100
+
101
+ ## Configuration YAML
102
+
103
+ The package includes a default configuration. To replace it, provide your own YAML file:
104
+
105
+ ```powershell
106
+ tv-recorder radio-canada.ca now 2h --config my-file.yaml
107
+ ```
108
+
109
+ Each source can define:
110
+
111
+ - `start_url`: page to open with Playwright.
112
+ - `stream_url_pattern`: regular expression used to identify stream URLs.
113
+ - `stream_request_urls`: optional JSON endpoints to call before browser steps.
114
+ - `stream_response_url_patterns`: response URL patterns whose JSON can contain stream URLs.
115
+ - `stream_response_json_keys`: JSON keys whose values should be treated as stream URLs.
116
+ - `stream_url_reject_patterns`: stream URL patterns to ignore.
117
+ - `recording`: video/audio track selection.
118
+ - `steps`: browser interaction recipe before stream detection.
119
+ - `output_extension`: output file extension.
120
+ - `user_agent`: optional user-agent for browser and recorder requests.
121
+
122
+ Track selection:
123
+
124
+ ```yaml
125
+ recording:
126
+ video:
127
+ height: 720
128
+ audio:
129
+ language: "fre"
130
+ reject_comments:
131
+ - "audio_dv"
132
+ ```
133
+
134
+ If no video track matches the requested height, the highest available resolution is used. For audio, rejected comments are filtered first, then language and comment preferences are used to choose the main track.
135
+
136
+ Supported steps:
137
+
138
+ ```yaml
139
+ steps:
140
+ - action: wait_for_stream
141
+ - action: wait_for_selector
142
+ selector: "button.vjs-big-play-button"
143
+ - action: click_by_text
144
+ text: "EN DIRECT"
145
+ - action: click_by_selector
146
+ selector: "button:has-text('Play')"
147
+ fallback:
148
+ action: click_by_text
149
+ text: "Play"
150
+ required: true
151
+ timeout_ms: 30000
152
+ ```
153
+
154
+ ## Notes
155
+
156
+ The recorder uses browser automation to discover streams when a channel does not expose a stable feed URL, then records the selected audio/video tracks without re-encoding. This version starts the recording at the requested time in the current process. A real system scheduler can be layered on top of the same command later.
157
+
158
+ ## Publishing
159
+
160
+ Install packaging tools:
161
+
162
+ ```powershell
163
+ python -m pip install -e ".[dev]"
164
+ ```
165
+
166
+ Build and check the package:
167
+
168
+ ```powershell
169
+ python scripts/check_version.py --tag v0.1.0
170
+ python -m build
171
+ python -m twine check dist/*
172
+ ```
173
+
174
+ Publish to TestPyPI first:
175
+
176
+ ```powershell
177
+ python -m twine upload --repository testpypi dist/*
178
+ ```
179
+
180
+ Publish to PyPI:
181
+
182
+ ```powershell
183
+ python -m twine upload dist/*
184
+ ```
185
+
186
+ Before publishing, update `version` in `pyproject.toml`, then rebuild from a clean `dist` directory. The package `__version__` is resolved from the package metadata generated from `pyproject.toml`. The GitHub release tag must match the version in `pyproject.toml`; a leading `v` is accepted, so `v0.1.0` matches `0.1.0`.
@@ -0,0 +1,133 @@
1
+ # tv-recorder
2
+
3
+ Command-line recorder for public live TV streams.
4
+
5
+ ## Local Installation
6
+
7
+ ```powershell
8
+ python -m pip install -e .
9
+ ```
10
+
11
+ Chromium is installed automatically on first use if Playwright does not already have it.
12
+
13
+ The recorder uses the `ffmpeg` binary provided by `imageio-ffmpeg`.
14
+
15
+ ## Usage
16
+
17
+ ```powershell
18
+ tv-recorder radio-canada.ca now 2h
19
+ ```
20
+
21
+ The general form is:
22
+
23
+ ```text
24
+ tv-recorder SOURCE START DURATION
25
+ ```
26
+
27
+ Examples:
28
+
29
+ ```powershell
30
+ tv-recorder radio-canada.ca now 30m
31
+ tv-recorder radio-canada.ca 2026-05-24T20:00:00 2h --output-dir recordings
32
+ tv-recorder tvaplus.ca now 30m
33
+ tv-recorder telequebec.tv now 30m
34
+ tv-recorder cpac-english now 30m
35
+ tv-recorder cpac-francais now 30m
36
+ tv-recorder canal-assemblee-nationale now 30m
37
+ tv-recorder globalnews-national now 30m
38
+ tv-recorder globalnews-montreal now 30m
39
+ tv-recorder radio-canada.ca now 10m --headful
40
+ tv-recorder radio-canada.ca now 10m --dry-run
41
+ tv-recorder radio-canada.ca now 10m --debug
42
+ ```
43
+
44
+ `START` accepts `now` or a local ISO date. `DURATION` accepts values such as `90s`, `30m`, `2h`, or `01:30:00`.
45
+
46
+ By default, the CLI runs at `--info` level and prints the effective recording URL or inputs. During recording, a small activity indicator moves when ffmpeg emits progress. Use `--debug` to show discovery details, step results, and ffmpeg output.
47
+
48
+ ## Configuration YAML
49
+
50
+ The package includes a default configuration. To replace it, provide your own YAML file:
51
+
52
+ ```powershell
53
+ tv-recorder radio-canada.ca now 2h --config my-file.yaml
54
+ ```
55
+
56
+ Each source can define:
57
+
58
+ - `start_url`: page to open with Playwright.
59
+ - `stream_url_pattern`: regular expression used to identify stream URLs.
60
+ - `stream_request_urls`: optional JSON endpoints to call before browser steps.
61
+ - `stream_response_url_patterns`: response URL patterns whose JSON can contain stream URLs.
62
+ - `stream_response_json_keys`: JSON keys whose values should be treated as stream URLs.
63
+ - `stream_url_reject_patterns`: stream URL patterns to ignore.
64
+ - `recording`: video/audio track selection.
65
+ - `steps`: browser interaction recipe before stream detection.
66
+ - `output_extension`: output file extension.
67
+ - `user_agent`: optional user-agent for browser and recorder requests.
68
+
69
+ Track selection:
70
+
71
+ ```yaml
72
+ recording:
73
+ video:
74
+ height: 720
75
+ audio:
76
+ language: "fre"
77
+ reject_comments:
78
+ - "audio_dv"
79
+ ```
80
+
81
+ If no video track matches the requested height, the highest available resolution is used. For audio, rejected comments are filtered first, then language and comment preferences are used to choose the main track.
82
+
83
+ Supported steps:
84
+
85
+ ```yaml
86
+ steps:
87
+ - action: wait_for_stream
88
+ - action: wait_for_selector
89
+ selector: "button.vjs-big-play-button"
90
+ - action: click_by_text
91
+ text: "EN DIRECT"
92
+ - action: click_by_selector
93
+ selector: "button:has-text('Play')"
94
+ fallback:
95
+ action: click_by_text
96
+ text: "Play"
97
+ required: true
98
+ timeout_ms: 30000
99
+ ```
100
+
101
+ ## Notes
102
+
103
+ The recorder uses browser automation to discover streams when a channel does not expose a stable feed URL, then records the selected audio/video tracks without re-encoding. This version starts the recording at the requested time in the current process. A real system scheduler can be layered on top of the same command later.
104
+
105
+ ## Publishing
106
+
107
+ Install packaging tools:
108
+
109
+ ```powershell
110
+ python -m pip install -e ".[dev]"
111
+ ```
112
+
113
+ Build and check the package:
114
+
115
+ ```powershell
116
+ python scripts/check_version.py --tag v0.1.0
117
+ python -m build
118
+ python -m twine check dist/*
119
+ ```
120
+
121
+ Publish to TestPyPI first:
122
+
123
+ ```powershell
124
+ python -m twine upload --repository testpypi dist/*
125
+ ```
126
+
127
+ Publish to PyPI:
128
+
129
+ ```powershell
130
+ python -m twine upload dist/*
131
+ ```
132
+
133
+ Before publishing, update `version` in `pyproject.toml`, then rebuild from a clean `dist` directory. The package `__version__` is resolved from the package metadata generated from `pyproject.toml`. The GitHub release tag must match the version in `pyproject.toml`; a leading `v` is accepted, so `v0.1.0` matches `0.1.0`.
@@ -0,0 +1,58 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.24"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "tv-recorder"
7
+ version = "0.0.0.dev2"
8
+ description = "Command-line recorder for public live TV streams."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { file = "LICENSE" }
12
+ authors = [
13
+ { name = "tv-recorder contributors" },
14
+ ]
15
+ keywords = ["cli", "ffmpeg", "hls", "playwright", "recorder", "tv"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Environment :: Console",
19
+ "Intended Audience :: End Users/Desktop",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Operating System :: OS Independent",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Topic :: Multimedia :: Video",
27
+ "Topic :: Utilities",
28
+ ]
29
+ dependencies = [
30
+ "imageio-ffmpeg>=0.6.0",
31
+ "playwright>=1.44",
32
+ "pytimeparse2>=1.7.1",
33
+ "PyYAML>=6.0.1",
34
+ ]
35
+
36
+ [project.optional-dependencies]
37
+ dev = [
38
+ "build>=1.2",
39
+ "pytest>=8.0",
40
+ "twine>=5.0",
41
+ ]
42
+
43
+ [project.scripts]
44
+ tv-recorder = "tv_recorder.cli:main"
45
+
46
+ [project.urls]
47
+ Homepage = "https://github.com/onclefranck/tv-recorder"
48
+ Repository = "https://github.com/onclefranck/tv-recorder"
49
+ Issues = "https://github.com/onclefranck/tv-recorder/issues"
50
+
51
+ [tool.hatch.build.targets.wheel]
52
+ packages = ["src/tv_recorder"]
53
+
54
+ [tool.hatch.build.targets.wheel.force-include]
55
+ "src/tv_recorder/defaults.yaml" = "tv_recorder/defaults.yaml"
56
+
57
+ [tool.pytest.ini_options]
58
+ testpaths = ["tests"]