getscript 0.12.0__tar.gz → 0.14.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 (37) hide show
  1. getscript-0.14.0/PKG-INFO +194 -0
  2. getscript-0.14.0/README.md +166 -0
  3. {getscript-0.12.0 → getscript-0.14.0}/getscript/__init__.py +1 -1
  4. {getscript-0.12.0 → getscript-0.14.0}/getscript/cli.py +62 -81
  5. {getscript-0.12.0 → getscript-0.14.0}/getscript/completions.py +10 -14
  6. {getscript-0.12.0 → getscript-0.14.0}/getscript/config.py +0 -6
  7. {getscript-0.12.0 → getscript-0.14.0}/getscript/detect.py +4 -23
  8. {getscript-0.12.0 → getscript-0.14.0}/getscript/search.py +1 -38
  9. {getscript-0.12.0 → getscript-0.14.0}/getscript/upload.py +1 -19
  10. getscript-0.14.0/getscript.egg-info/PKG-INFO +194 -0
  11. {getscript-0.12.0 → getscript-0.14.0}/getscript.egg-info/SOURCES.txt +1 -3
  12. getscript-0.14.0/getscript.egg-info/requires.txt +1 -0
  13. {getscript-0.12.0 → getscript-0.14.0}/pyproject.toml +3 -5
  14. {getscript-0.12.0 → getscript-0.14.0}/tests/test_detect.py +6 -32
  15. {getscript-0.12.0 → getscript-0.14.0}/tests/test_integration.py +68 -9
  16. {getscript-0.12.0 → getscript-0.14.0}/tests/test_output.py +9 -9
  17. getscript-0.14.0/tests/test_search.py +65 -0
  18. {getscript-0.12.0 → getscript-0.14.0}/tests/test_upload.py +16 -65
  19. getscript-0.12.0/PKG-INFO +0 -125
  20. getscript-0.12.0/README.md +0 -95
  21. getscript-0.12.0/getscript/youtube.py +0 -58
  22. getscript-0.12.0/getscript.egg-info/PKG-INFO +0 -125
  23. getscript-0.12.0/getscript.egg-info/requires.txt +0 -3
  24. getscript-0.12.0/tests/test_search.py +0 -125
  25. getscript-0.12.0/tests/test_youtube.py +0 -109
  26. {getscript-0.12.0 → getscript-0.14.0}/LICENSE +0 -0
  27. {getscript-0.12.0 → getscript-0.14.0}/getscript/apple.py +0 -0
  28. {getscript-0.12.0 → getscript-0.14.0}/getscript/output.py +0 -0
  29. {getscript-0.12.0 → getscript-0.14.0}/getscript/picker.py +0 -0
  30. {getscript-0.12.0 → getscript-0.14.0}/getscript/progress.py +0 -0
  31. {getscript-0.12.0 → getscript-0.14.0}/getscript.egg-info/dependency_links.txt +0 -0
  32. {getscript-0.12.0 → getscript-0.14.0}/getscript.egg-info/entry_points.txt +0 -0
  33. {getscript-0.12.0 → getscript-0.14.0}/getscript.egg-info/top_level.txt +0 -0
  34. {getscript-0.12.0 → getscript-0.14.0}/setup.cfg +0 -0
  35. {getscript-0.12.0 → getscript-0.14.0}/tests/test_apple.py +0 -0
  36. {getscript-0.12.0 → getscript-0.14.0}/tests/test_config.py +0 -0
  37. {getscript-0.12.0 → getscript-0.14.0}/tests/test_picker.py +0 -0
@@ -0,0 +1,194 @@
1
+ Metadata-Version: 2.4
2
+ Name: getscript
3
+ Version: 0.14.0
4
+ Summary: Fast, Unix-friendly CLI for fetching transcripts from Apple Podcasts
5
+ Author: Voxly
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/outerbanks73/cli-tools
8
+ Project-URL: Documentation, https://voxlytranscribes.com/docs/getscript
9
+ Project-URL: Repository, https://github.com/outerbanks73/cli-tools
10
+ Project-URL: Issues, https://github.com/outerbanks73/cli-tools/issues
11
+ Keywords: transcript,podcast,apple-podcasts,cli
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Multimedia :: Sound/Audio
22
+ Classifier: Topic :: Utilities
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: defusedxml>=0.7.1
27
+ Dynamic: license-file
28
+
29
+ # getscript
30
+
31
+ A fast, Unix-friendly CLI for fetching transcripts from Apple Podcasts. You don't need to transcribe much of anything nowadays
32
+ because Apple is transcribing everything for us. 'getscript' lets you use the transcripts in a more human / text friendly manner.
33
+ The idea behind getscript is to put the transcripts to use in a meaningful way.
34
+
35
+ For example - let's say you're curious about AI Slop as a podcast topic:
36
+
37
+ $ getscript --search "AI Slop" --list
38
+ <removing list of 10 podcasts and their podcast id's>
39
+ $ getscript 1000730374732 | claude -p "Summarize the top 5 Points"
40
+
41
+ Of course you can also export to TTML, JSON, markdown and run from cron if you want to schedule things.
42
+
43
+ [![PyPI](https://img.shields.io/pypi/v/getscript)](https://pypi.org/project/getscript/)
44
+ [![CI](https://github.com/outerbanks73/cli-tools/actions/workflows/test.yml/badge.svg)](https://github.com/outerbanks73/cli-tools/actions/workflows/test.yml)
45
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
46
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org)
47
+
48
+ ## Install
49
+
50
+ ```bash
51
+ pip install getscript
52
+ ```
53
+
54
+ Or via Homebrew:
55
+
56
+ ```bash
57
+ brew install outerbanks73/tap/getscript
58
+ ```
59
+
60
+ Requires Python 3.10+. Apple Podcasts transcripts require macOS 15.5+ with Xcode CLI tools.
61
+
62
+ ## Quick Start
63
+
64
+ ```bash
65
+ # Fetch from an episode URL
66
+ getscript "https://podcasts.apple.com/us/podcast/the-daily/id1200361736?i=1000753754819"
67
+
68
+ # Fetch from a bare episode ID
69
+ getscript 1000753754819
70
+
71
+ # Raw TTML XML output
72
+ getscript 1000753754819 --ttml
73
+
74
+ # Search Apple Podcasts interactively (requires fzf)
75
+ getscript --search "artificial intelligence"
76
+ ```
77
+
78
+ ### Output Formats
79
+
80
+ Output formats are mutually exclusive (`--json`, `--ttml`, `--markdown`). The `--timestamps` flag can be combined with any format.
81
+
82
+ ```bash
83
+ # JSON piped to jq
84
+ getscript EPISODE_ID --json | jq '.segments[].text'
85
+
86
+ # Markdown with timestamps
87
+ getscript EPISODE_ID --markdown --timestamps > notes.md
88
+
89
+ # Write to file
90
+ getscript EPISODE_ID -o transcript.txt
91
+ ```
92
+
93
+ ### Search
94
+
95
+ ```bash
96
+ # Search Apple Podcasts (requires fzf)
97
+ getscript --search "climate change"
98
+
99
+ # List results without fzf
100
+ getscript --search "topic" --list --limit 20
101
+ ```
102
+
103
+ ### Stdin & Batch Processing
104
+
105
+ ```bash
106
+ # Read episode ID from stdin
107
+ echo "1000753754819" | getscript -
108
+
109
+ # Batch process a list of IDs
110
+ cat ids.txt | xargs -n1 getscript --no-upload --quiet
111
+
112
+ # Disable upload via environment variable
113
+ GETSCRIPT_UPLOAD=0 getscript EPISODE_ID
114
+
115
+ # Silent file output for scripting
116
+ getscript EPISODE_ID --quiet --no-upload -o out.txt
117
+ ```
118
+
119
+ ### Shared Transcript Library
120
+
121
+ Fetched transcripts are automatically submitted to [voxlytranscribes.com](https://voxlytranscribes.com) for indexing and enrichment. To disable:
122
+
123
+ ```bash
124
+ getscript EPISODE_ID --no-upload
125
+ GETSCRIPT_UPLOAD=0 getscript EPISODE_ID
126
+ ```
127
+
128
+ ### Shell Completions
129
+
130
+ ```bash
131
+ getscript --completions bash >> ~/.bashrc
132
+ getscript --completions zsh >> ~/.zshrc
133
+ getscript --completions fish > ~/.config/fish/completions/getscript.fish
134
+ ```
135
+
136
+ ## Configuration
137
+
138
+ Config file: `~/.config/getscript/config.json`
139
+
140
+ | Key | Type | Default | Description |
141
+ |-----|------|---------|-------------|
142
+ | `output_format` | string | `text` | Default format: `text`, `json`, `markdown`, `ttml` |
143
+ | `timestamps` | bool | `false` | Include timestamps in text output |
144
+ | `search_limit` | int | `10` | Number of search results |
145
+ | `no_upload` | bool | `false` | Disable shared library submissions |
146
+ | `quiet` | bool | `false` | Suppress progress and upload status messages |
147
+
148
+ Environment variables:
149
+
150
+ | Variable | Description |
151
+ |----------|-------------|
152
+ | `GETSCRIPT_UPLOAD` | Set to `0` to disable submissions |
153
+ | `NO_COLOR` | Disable colored output |
154
+
155
+ Precedence: config file < environment variables < CLI flags.
156
+
157
+ See [examples/config.example.md](examples/config.example.md) for a full annotated reference.
158
+
159
+ ## How It Works
160
+
161
+ Compiles a small Obj-C helper that calls Apple's private AMSMescal framework (FairPlay DRM) to generate a bearer token. That token authenticates requests to the AMP API, which returns TTML transcripts. The token is cached at `~/.cache/getscript/apple_token` for 30 days. This is the only open-source tool that can fetch Apple Podcasts transcripts programmatically.
162
+
163
+ ## Shared Transcript Library
164
+
165
+ Every fetch contributes to the shared library at [voxlytranscribes.com](https://voxlytranscribes.com). Submissions go through a quarantine pipeline: server-side re-fetch, content hash verification, and provenance tracking before promotion to the canonical library. See the [technical docs](https://voxlytranscribes.com/docs/getscript) for details.
166
+
167
+ ## Exit Codes
168
+
169
+ | Code | Meaning |
170
+ |------|---------|
171
+ | `0` | Success |
172
+ | `1` | Runtime error (network, auth, missing transcript) |
173
+ | `2` | Usage error (bad arguments, unrecognized URL) |
174
+ | `130` | Interrupted (Ctrl-C) |
175
+
176
+ ## Testing
177
+
178
+ 87 tests across 9 modules. CI matrix: Python 3.10–3.13 on Ubuntu and macOS.
179
+
180
+ ```bash
181
+ pytest -v
182
+ ```
183
+
184
+ ## Contributing
185
+
186
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
187
+
188
+ ## Changelog
189
+
190
+ See [CHANGELOG.md](CHANGELOG.md).
191
+
192
+ ## License
193
+
194
+ MIT
@@ -0,0 +1,166 @@
1
+ # getscript
2
+
3
+ A fast, Unix-friendly CLI for fetching transcripts from Apple Podcasts. You don't need to transcribe much of anything nowadays
4
+ because Apple is transcribing everything for us. 'getscript' lets you use the transcripts in a more human / text friendly manner.
5
+ The idea behind getscript is to put the transcripts to use in a meaningful way.
6
+
7
+ For example - let's say you're curious about AI Slop as a podcast topic:
8
+
9
+ $ getscript --search "AI Slop" --list
10
+ <removing list of 10 podcasts and their podcast id's>
11
+ $ getscript 1000730374732 | claude -p "Summarize the top 5 Points"
12
+
13
+ Of course you can also export to TTML, JSON, markdown and run from cron if you want to schedule things.
14
+
15
+ [![PyPI](https://img.shields.io/pypi/v/getscript)](https://pypi.org/project/getscript/)
16
+ [![CI](https://github.com/outerbanks73/cli-tools/actions/workflows/test.yml/badge.svg)](https://github.com/outerbanks73/cli-tools/actions/workflows/test.yml)
17
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
18
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)](https://www.python.org)
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ pip install getscript
24
+ ```
25
+
26
+ Or via Homebrew:
27
+
28
+ ```bash
29
+ brew install outerbanks73/tap/getscript
30
+ ```
31
+
32
+ Requires Python 3.10+. Apple Podcasts transcripts require macOS 15.5+ with Xcode CLI tools.
33
+
34
+ ## Quick Start
35
+
36
+ ```bash
37
+ # Fetch from an episode URL
38
+ getscript "https://podcasts.apple.com/us/podcast/the-daily/id1200361736?i=1000753754819"
39
+
40
+ # Fetch from a bare episode ID
41
+ getscript 1000753754819
42
+
43
+ # Raw TTML XML output
44
+ getscript 1000753754819 --ttml
45
+
46
+ # Search Apple Podcasts interactively (requires fzf)
47
+ getscript --search "artificial intelligence"
48
+ ```
49
+
50
+ ### Output Formats
51
+
52
+ Output formats are mutually exclusive (`--json`, `--ttml`, `--markdown`). The `--timestamps` flag can be combined with any format.
53
+
54
+ ```bash
55
+ # JSON piped to jq
56
+ getscript EPISODE_ID --json | jq '.segments[].text'
57
+
58
+ # Markdown with timestamps
59
+ getscript EPISODE_ID --markdown --timestamps > notes.md
60
+
61
+ # Write to file
62
+ getscript EPISODE_ID -o transcript.txt
63
+ ```
64
+
65
+ ### Search
66
+
67
+ ```bash
68
+ # Search Apple Podcasts (requires fzf)
69
+ getscript --search "climate change"
70
+
71
+ # List results without fzf
72
+ getscript --search "topic" --list --limit 20
73
+ ```
74
+
75
+ ### Stdin & Batch Processing
76
+
77
+ ```bash
78
+ # Read episode ID from stdin
79
+ echo "1000753754819" | getscript -
80
+
81
+ # Batch process a list of IDs
82
+ cat ids.txt | xargs -n1 getscript --no-upload --quiet
83
+
84
+ # Disable upload via environment variable
85
+ GETSCRIPT_UPLOAD=0 getscript EPISODE_ID
86
+
87
+ # Silent file output for scripting
88
+ getscript EPISODE_ID --quiet --no-upload -o out.txt
89
+ ```
90
+
91
+ ### Shared Transcript Library
92
+
93
+ Fetched transcripts are automatically submitted to [voxlytranscribes.com](https://voxlytranscribes.com) for indexing and enrichment. To disable:
94
+
95
+ ```bash
96
+ getscript EPISODE_ID --no-upload
97
+ GETSCRIPT_UPLOAD=0 getscript EPISODE_ID
98
+ ```
99
+
100
+ ### Shell Completions
101
+
102
+ ```bash
103
+ getscript --completions bash >> ~/.bashrc
104
+ getscript --completions zsh >> ~/.zshrc
105
+ getscript --completions fish > ~/.config/fish/completions/getscript.fish
106
+ ```
107
+
108
+ ## Configuration
109
+
110
+ Config file: `~/.config/getscript/config.json`
111
+
112
+ | Key | Type | Default | Description |
113
+ |-----|------|---------|-------------|
114
+ | `output_format` | string | `text` | Default format: `text`, `json`, `markdown`, `ttml` |
115
+ | `timestamps` | bool | `false` | Include timestamps in text output |
116
+ | `search_limit` | int | `10` | Number of search results |
117
+ | `no_upload` | bool | `false` | Disable shared library submissions |
118
+ | `quiet` | bool | `false` | Suppress progress and upload status messages |
119
+
120
+ Environment variables:
121
+
122
+ | Variable | Description |
123
+ |----------|-------------|
124
+ | `GETSCRIPT_UPLOAD` | Set to `0` to disable submissions |
125
+ | `NO_COLOR` | Disable colored output |
126
+
127
+ Precedence: config file < environment variables < CLI flags.
128
+
129
+ See [examples/config.example.md](examples/config.example.md) for a full annotated reference.
130
+
131
+ ## How It Works
132
+
133
+ Compiles a small Obj-C helper that calls Apple's private AMSMescal framework (FairPlay DRM) to generate a bearer token. That token authenticates requests to the AMP API, which returns TTML transcripts. The token is cached at `~/.cache/getscript/apple_token` for 30 days. This is the only open-source tool that can fetch Apple Podcasts transcripts programmatically.
134
+
135
+ ## Shared Transcript Library
136
+
137
+ Every fetch contributes to the shared library at [voxlytranscribes.com](https://voxlytranscribes.com). Submissions go through a quarantine pipeline: server-side re-fetch, content hash verification, and provenance tracking before promotion to the canonical library. See the [technical docs](https://voxlytranscribes.com/docs/getscript) for details.
138
+
139
+ ## Exit Codes
140
+
141
+ | Code | Meaning |
142
+ |------|---------|
143
+ | `0` | Success |
144
+ | `1` | Runtime error (network, auth, missing transcript) |
145
+ | `2` | Usage error (bad arguments, unrecognized URL) |
146
+ | `130` | Interrupted (Ctrl-C) |
147
+
148
+ ## Testing
149
+
150
+ 87 tests across 9 modules. CI matrix: Python 3.10–3.13 on Ubuntu and macOS.
151
+
152
+ ```bash
153
+ pytest -v
154
+ ```
155
+
156
+ ## Contributing
157
+
158
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
159
+
160
+ ## Changelog
161
+
162
+ See [CHANGELOG.md](CHANGELOG.md).
163
+
164
+ ## License
165
+
166
+ MIT
@@ -3,4 +3,4 @@ from importlib.metadata import version, PackageNotFoundError
3
3
  try:
4
4
  __version__ = version("getscript")
5
5
  except PackageNotFoundError:
6
- __version__ = "0.12.0" # fallback for editable installs without metadata
6
+ __version__ = "0.14.0" # fallback for editable installs without metadata
@@ -12,41 +12,45 @@ from getscript.progress import Progress
12
12
 
13
13
  EXAMPLES = """\
14
14
  examples:
15
- getscript "https://youtube.com/watch?v=dQw4w9WgXcQ"
16
- getscript "https://youtu.be/dQw4w9WgXcQ" --timestamps
17
- getscript "https://youtube.com/watch?v=dQw4w9WgXcQ" --json | jq .
18
- getscript "https://youtube.com/watch?v=dQw4w9WgXcQ" --markdown > notes.md
19
15
  getscript 1000753754819 # Apple episode ID
20
16
  getscript 1000753754819 --ttml # raw TTML XML
17
+ getscript 1000753754819 --timestamps # include timestamps
21
18
  getscript "https://podcasts.apple.com/...?i=12345"
22
- getscript "https://youtube.com/watch?v=..." -o transcript.txt
23
- getscript --search "my favorite YouTuber" # search YouTube, pick via fzf
24
- getscript --search "my favorite podcaster" --apple # search Apple Podcasts
19
+ getscript EPISODE_ID --json | jq . # JSON piped to jq
20
+ getscript EPISODE_ID --markdown > notes.md
21
+ getscript EPISODE_ID -o transcript.txt
22
+ getscript EPISODE_ID --no-upload # skip shared library indexing
23
+ getscript --search "artificial intelligence" # search Apple Podcasts, pick via fzf
25
24
  getscript --search "topic" --list # print results, no fzf
26
25
  getscript --search "topic" --limit 20 # control result count
27
- getscript VIDEO_ID --proxy socks5://127.0.0.1:1080 # use proxy for YouTube
28
- getscript VIDEO_ID --cookies ~/cookies.txt # use browser cookies
29
- getscript VIDEO_ID --no-upload # skip shared library indexing
30
- getscript --completions zsh >> ~/.zshrc"""
26
+ getscript --completions zsh >> ~/.zshrc
27
+
28
+ batch & scripting:
29
+ echo "1000753754819" | getscript - # read ID from stdin
30
+ cat ids.txt | xargs -n1 getscript --no-upload --quiet # batch process, no noise
31
+ GETSCRIPT_UPLOAD=0 getscript EPISODE_ID # env var to skip upload
32
+ getscript EPISODE_ID --quiet --no-upload -o out.txt # silent file output
33
+
34
+ exit codes:
35
+ 0 success
36
+ 1 runtime error (network, auth, missing transcript)
37
+ 2 usage error (bad arguments, unrecognized URL)
38
+ 130 interrupted (Ctrl-C)"""
31
39
 
32
40
 
33
41
  def build_parser() -> argparse.ArgumentParser:
34
42
  parser = argparse.ArgumentParser(
35
43
  prog="getscript",
36
- description="Fetch transcripts from YouTube and Apple Podcasts.",
44
+ description="Fetch transcripts from Apple Podcasts.",
37
45
  epilog=EXAMPLES,
38
46
  formatter_class=argparse.RawDescriptionHelpFormatter,
39
47
  )
40
- parser.add_argument("input", nargs="?", help="URL or ID to fetch transcript for")
48
+ parser.add_argument("input", nargs="?", help="Apple Podcasts URL or episode ID")
41
49
  parser.add_argument(
42
50
  "-o", "--output", metavar="FILE", help="write output to file"
43
51
  )
44
52
  parser.add_argument(
45
- "--search", metavar="QUERY", help="search for content by topic or creator"
46
- )
47
- parser.add_argument(
48
- "--apple", action="store_true", default=False,
49
- help="search Apple Podcasts instead of YouTube",
53
+ "--search", metavar="QUERY", help="search Apple Podcasts by topic or creator"
50
54
  )
51
55
  parser.add_argument(
52
56
  "--limit", type=int, default=None, metavar="N",
@@ -56,25 +60,18 @@ def build_parser() -> argparse.ArgumentParser:
56
60
  "--list", action="store_true", default=False,
57
61
  help="print search results without interactive selection",
58
62
  )
59
- parser.add_argument(
63
+ fmt_group = parser.add_mutually_exclusive_group()
64
+ fmt_group.add_argument(
60
65
  "--json", action="store_true", default=None, help="structured JSON output"
61
66
  )
62
- parser.add_argument(
63
- "--ttml", action="store_true", default=None, help="raw TTML XML (Apple only)"
67
+ fmt_group.add_argument(
68
+ "--ttml", action="store_true", default=None, help="raw TTML XML output"
64
69
  )
65
- parser.add_argument(
66
- "--timestamps", action="store_true", default=None, help="include timestamps"
67
- )
68
- parser.add_argument(
70
+ fmt_group.add_argument(
69
71
  "--markdown", action="store_true", default=None, help="Markdown output"
70
72
  )
71
73
  parser.add_argument(
72
- "--proxy", metavar="URL", default=None,
73
- help="proxy URL for YouTube requests (e.g. socks5://host:port)",
74
- )
75
- parser.add_argument(
76
- "--cookies", metavar="FILE", default=None,
77
- help="Netscape cookie file for YouTube auth (e.g. cookies.txt)",
74
+ "--timestamps", action="store_true", default=None, help="include timestamps"
78
75
  )
79
76
  parser.add_argument(
80
77
  "--no-upload", action="store_true", default=None,
@@ -84,7 +81,7 @@ def build_parser() -> argparse.ArgumentParser:
84
81
  "--no-color", action="store_true", default=None, help="disable colors"
85
82
  )
86
83
  parser.add_argument(
87
- "--quiet", action="store_true", default=None, help="suppress progress output"
84
+ "--quiet", action="store_true", default=None, help="suppress progress and upload status messages"
88
85
  )
89
86
  parser.add_argument(
90
87
  "--verbose", action="store_true", default=None, help="show detailed errors"
@@ -104,14 +101,14 @@ def build_parser() -> argparse.ArgumentParser:
104
101
  def _handle_search(args, config) -> int:
105
102
  """Handle --search mode: search, select, then fetch transcript."""
106
103
  from getscript.picker import format_list, pick_result
107
- from getscript.search import search_apple, search_youtube
104
+ from getscript.search import search_apple
108
105
 
109
106
  verbose = config.get("verbose", False)
110
107
  quiet = config.get("quiet", False)
111
108
  limit = args.limit or config.get("search_limit", 10)
112
109
 
113
110
  # Apple transcript fetch requires macOS — warn before searching unless --list
114
- if args.apple and not args.list:
111
+ if not args.list:
115
112
  if sys.platform != "darwin":
116
113
  print(
117
114
  "Apple Podcasts transcripts require macOS 15.5+ with Xcode CLI tools.\n"
@@ -123,22 +120,8 @@ def _handle_search(args, config) -> int:
123
120
  progress = Progress(quiet=quiet)
124
121
 
125
122
  try:
126
- if args.apple:
127
- progress.update("Searching Apple Podcasts...")
128
- results = search_apple(args.search, limit=limit)
129
- else:
130
- api_key = config.get("youtube_api_key")
131
- if not api_key:
132
- print(
133
- "YouTube API key required for --search.\n"
134
- "Set GETSCRIPT_YOUTUBE_API_KEY env var or add "
135
- '"youtube_api_key" to ~/.config/getscript/config.json\n'
136
- "Get a key: https://console.cloud.google.com/apis/credentials",
137
- file=sys.stderr,
138
- )
139
- return 1
140
- progress.update("Searching YouTube...")
141
- results = search_youtube(args.search, api_key, limit=limit)
123
+ progress.update("Searching Apple Podcasts...")
124
+ results = search_apple(args.search, limit=limit)
142
125
 
143
126
  progress.done()
144
127
 
@@ -166,7 +149,7 @@ def _handle_search(args, config) -> int:
166
149
  except KeyboardInterrupt:
167
150
  progress.done()
168
151
  print("\nInterrupted.", file=sys.stderr)
169
- return 1
152
+ return 130
170
153
  except Exception as e:
171
154
  progress.done()
172
155
  if verbose:
@@ -207,34 +190,24 @@ def _fetch_transcript(args, config) -> int:
207
190
  progress.update("Detecting source...")
208
191
  source, source_id = detect_source(args.input)
209
192
 
210
- ttml_raw = None
193
+ from getscript.apple import fetch_ttml, get_bearer_token, ttml_to_segments
211
194
 
212
- if source == "youtube":
213
- progress.update("Fetching YouTube transcript...")
214
- from getscript.youtube import fetch_transcript
195
+ cache_dir = get_cache_dir()
215
196
 
216
- segments = fetch_transcript(source_id, config)
197
+ progress.update("Authenticating with Apple...")
198
+ token = get_bearer_token(cache_dir)
199
+ if not token:
217
200
  progress.done()
201
+ print(
202
+ "Failed to get Apple bearer token. Requires macOS 15.5+.",
203
+ file=sys.stderr,
204
+ )
205
+ return 1
218
206
 
219
- elif source == "apple":
220
- from getscript.apple import fetch_ttml, get_bearer_token, ttml_to_segments
221
-
222
- cache_dir = get_cache_dir()
223
-
224
- progress.update("Authenticating with Apple...")
225
- token = get_bearer_token(cache_dir)
226
- if not token:
227
- progress.done()
228
- print(
229
- "Failed to get Apple bearer token. Requires macOS 15.5+.",
230
- file=sys.stderr,
231
- )
232
- return 1
233
-
234
- progress.update("Fetching Apple Podcasts transcript...")
235
- ttml_raw = fetch_ttml(source_id, token)
236
- segments = ttml_to_segments(ttml_raw)
237
- progress.done()
207
+ progress.update("Fetching Apple Podcasts transcript...")
208
+ ttml_raw = fetch_ttml(source_id, token)
209
+ segments = ttml_to_segments(ttml_raw)
210
+ progress.done()
238
211
 
239
212
  # Format output
240
213
  result = format_output(
@@ -256,11 +229,9 @@ def _fetch_transcript(args, config) -> int:
256
229
 
257
230
  # Upload to shared library (on by default, disable with --no-upload)
258
231
  if not config.get("no_upload"):
259
- from getscript.upload import fetch_title, upload_transcript
232
+ from getscript.upload import upload_transcript
260
233
 
261
234
  title = getattr(args, "_title", None)
262
- if not title:
263
- title = fetch_title(source, source_id)
264
235
  resp = upload_transcript(source, source_id, segments, title, config)
265
236
  if resp and not quiet:
266
237
  status = resp.get("status", "unknown")
@@ -284,7 +255,7 @@ def _fetch_transcript(args, config) -> int:
284
255
  except KeyboardInterrupt:
285
256
  progress.done()
286
257
  print("\nInterrupted.", file=sys.stderr)
287
- return 1
258
+ return 130
288
259
  except Exception as e:
289
260
  progress.done()
290
261
  if verbose:
@@ -305,6 +276,18 @@ def main(argv: list[str] | None = None) -> int:
305
276
  print(generate_completions(args.completions))
306
277
  return 0
307
278
 
279
+ # Read from stdin if "-" is given
280
+ if args.input == "-":
281
+ if sys.stdin.isatty():
282
+ print("Error: stdin is a terminal. Pipe a URL/ID or use a positional argument.",
283
+ file=sys.stderr)
284
+ return 2
285
+ line = sys.stdin.readline().strip()
286
+ if not line:
287
+ print("Error: no input received on stdin.", file=sys.stderr)
288
+ return 2
289
+ args.input = line
290
+
308
291
  # No input provided and no search
309
292
  if not args.input and not args.search:
310
293
  parser.print_help(sys.stderr)
@@ -326,8 +309,6 @@ def main(argv: list[str] | None = None) -> int:
326
309
  "no_color": args.no_color,
327
310
  "quiet": args.quiet,
328
311
  "verbose": args.verbose,
329
- "proxy": args.proxy,
330
- "cookie_file": args.cookies,
331
312
  "no_upload": args.no_upload,
332
313
  }
333
314
  config = merge_config(file_config, cli_flags)
@@ -20,7 +20,7 @@ _getscript() {
20
20
  local cur opts
21
21
  COMPREPLY=()
22
22
  cur="${COMP_WORDS[COMP_CWORD]}"
23
- opts="--search --apple --limit --list --json --ttml --timestamps --markdown --proxy --cookies --no-color --quiet --verbose -o --output --completions -h --help --version"
23
+ opts="--search --limit --list --json --ttml --timestamps --markdown --no-upload --no-color --quiet --verbose -o --output --completions -h --help --version"
24
24
 
25
25
  if [[ ${cur} == -* ]]; then
26
26
  COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
@@ -36,19 +36,17 @@ def _zsh() -> str:
36
36
 
37
37
  _getscript() {
38
38
  _arguments \\
39
- '1:url or id:' \\
40
- '--search[Search for content by topic or creator]:query:' \\
41
- '--apple[Search Apple Podcasts instead of YouTube]' \\
39
+ '1:Apple Podcasts URL or episode ID:' \\
40
+ '--search[Search Apple Podcasts by topic or creator]:query:' \\
42
41
  '--limit[Number of search results]:count:' \\
43
42
  '--list[Print search results without interactive selection]' \\
44
43
  '--json[Output as JSON]' \\
45
- '--ttml[Output raw TTML XML (Apple only)]' \\
44
+ '--ttml[Output raw TTML XML]' \\
46
45
  '--timestamps[Include timestamps]' \\
47
46
  '--markdown[Output as Markdown]' \\
48
- '--proxy[Proxy URL for YouTube requests]:url:' \\
49
- '--cookies[Netscape cookie file for YouTube auth]:cookie file:_files' \\
47
+ '--no-upload[Disable shared library submission]' \\
50
48
  '--no-color[Disable colors]' \\
51
- '--quiet[Suppress progress output]' \\
49
+ '--quiet[Suppress progress and upload status messages]' \\
52
50
  '--verbose[Show detailed errors]' \\
53
51
  {-o,--output}'[Write to file]:output file:_files' \\
54
52
  '--completions[Generate shell completions]:shell:(bash zsh fish)' \\
@@ -62,18 +60,16 @@ _getscript "$@"
62
60
 
63
61
  def _fish() -> str:
64
62
  return """\
65
- complete -c getscript -l search -d 'Search for content by topic or creator' -r
66
- complete -c getscript -l apple -d 'Search Apple Podcasts instead of YouTube'
63
+ complete -c getscript -l search -d 'Search Apple Podcasts by topic or creator' -r
67
64
  complete -c getscript -l limit -d 'Number of search results' -r
68
65
  complete -c getscript -l list -d 'Print search results without interactive selection'
69
66
  complete -c getscript -l json -d 'Output as JSON'
70
- complete -c getscript -l ttml -d 'Output raw TTML XML (Apple only)'
67
+ complete -c getscript -l ttml -d 'Output raw TTML XML'
71
68
  complete -c getscript -l timestamps -d 'Include timestamps'
72
69
  complete -c getscript -l markdown -d 'Output as Markdown'
73
- complete -c getscript -l proxy -d 'Proxy URL for YouTube requests' -r
74
- complete -c getscript -l cookies -d 'Netscape cookie file for YouTube auth' -r -F
70
+ complete -c getscript -l no-upload -d 'Disable shared library submission'
75
71
  complete -c getscript -l no-color -d 'Disable colors'
76
- complete -c getscript -l quiet -d 'Suppress progress output'
72
+ complete -c getscript -l quiet -d 'Suppress progress and upload status messages'
77
73
  complete -c getscript -l verbose -d 'Show detailed errors'
78
74
  complete -c getscript -s o -l output -d 'Write to file' -r -F
79
75
  complete -c getscript -l completions -d 'Generate shell completions' -r -fa 'bash zsh fish'