shephouse-yt 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ShepHouse
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,65 @@
1
+ Metadata-Version: 2.4
2
+ Name: shephouse-yt
3
+ Version: 0.1.0
4
+ Summary: youtube-transcript-api-style client that works from cloud servers, backed by an Apify Actor
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/shephouse-actors/shephouse-yt
7
+ Project-URL: Apify Actor, https://apify.com/shephouse/youtube-transcript-scraper-bulk
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: requests>=2.25
12
+ Dynamic: license-file
13
+
14
+ # shephouse-yt: YouTube transcripts from cloud servers (youtube-transcript-api-style client)
15
+
16
+ > **Disclosure:** ShepHouse is an AI-operated software studio. An AI agent wrote and maintains this repo, under a human owner. This client calls **our own paid Apify Actor**, [shephouse/youtube-transcript-scraper-bulk](https://apify.com/shephouse/youtube-transcript-scraper-bulk).
17
+
18
+ The `youtube-transcript-api` library is often blocked (`RequestBlocked` / `IpBlocked`) when you run it on AWS, GCP, Azure or other hosts. This small client keeps a familiar interface and fetches transcripts through the Apify Actor above, which uses residential proxies.
19
+
20
+ ## Cost (please read)
21
+ - **$0.005 per delivered transcript** (pay-per-event on Apify). Failed videos (no captions, unavailable, invalid ID) are **not charged**. There is no start fee.
22
+ - Runs bill to **your own Apify account**. Apify's free monthly platform credit can cover small volumes. Plan limits apply; see the Actor page.
23
+ - Prices can change. The Actor's Pricing tab is the source of truth.
24
+ - Running `youtube-transcript-api` on your own machine is free and often works fine locally. You only need this if you're blocked.
25
+
26
+ ## Install
27
+ ```bash
28
+ pip install git+https://github.com/shephouse-actors/shephouse-yt
29
+ export APIFY_TOKEN=... # Apify Console → Settings → API & Integrations
30
+ ```
31
+
32
+ ## Try it in 60 seconds
33
+ 1. Create a free Apify account and copy your API token: Apify Console → Settings → API & Integrations.
34
+ 2. Run:
35
+ ```bash
36
+ pip install git+https://github.com/shephouse-actors/shephouse-yt
37
+ export APIFY_TOKEN=apify_api_...
38
+ python -m shephouse_yt https://www.youtube.com/watch?v=PXbVn-f_wuM # add --json or --lang de,en
39
+ ```
40
+ For RAG chunking with timestamps, see [examples/rag_chunks.py](examples/rag_chunks.py).
41
+
42
+ ## Use
43
+ ```python
44
+ from shephouse_yt import YouTubeTranscriptApi, TranscriptsDisabled
45
+
46
+ api = YouTubeTranscriptApi() # proxy_config / cookies kwargs are accepted and ignored
47
+ t = api.fetch("PXbVn-f_wuM", languages=["de", "en"])
48
+ print(t.language_code, t.is_generated, t[0].text, t[0].start, t[0].duration)
49
+ raw = t.to_raw_data() # [{"text", "start", "duration"}, ...]
50
+
51
+ many = api.fetch_many(["PXbVn-f_wuM", "AAAAAAAAAAA"]) # one Actor run for the batch
52
+ for vid, r in many.items():
53
+ print(vid, "error" if isinstance(r, Exception) else len(r))
54
+ ```
55
+ Exceptions: `TranscriptsDisabled`, `NoTranscriptFound`, `VideoUnavailable` (all subclass `CouldNotRetrieveTranscript`).
56
+
57
+ ## What is not supported
58
+ - `list()` / `translate()`.
59
+ - Formatters. For SRT/VTT/chapters/token counts, call the Actor directly.
60
+ - Each call is an Apify run, so expect several seconds to tens of seconds of latency. Batch with `fetch_many`.
61
+
62
+ ## Tests
63
+ `pip install pytest requests && pytest -q` (offline; the HTTP call is mocked).
64
+
65
+ Not affiliated with YouTube/Google or with the youtube-transcript-api project. MIT licensed.
@@ -0,0 +1,52 @@
1
+ # shephouse-yt: YouTube transcripts from cloud servers (youtube-transcript-api-style client)
2
+
3
+ > **Disclosure:** ShepHouse is an AI-operated software studio. An AI agent wrote and maintains this repo, under a human owner. This client calls **our own paid Apify Actor**, [shephouse/youtube-transcript-scraper-bulk](https://apify.com/shephouse/youtube-transcript-scraper-bulk).
4
+
5
+ The `youtube-transcript-api` library is often blocked (`RequestBlocked` / `IpBlocked`) when you run it on AWS, GCP, Azure or other hosts. This small client keeps a familiar interface and fetches transcripts through the Apify Actor above, which uses residential proxies.
6
+
7
+ ## Cost (please read)
8
+ - **$0.005 per delivered transcript** (pay-per-event on Apify). Failed videos (no captions, unavailable, invalid ID) are **not charged**. There is no start fee.
9
+ - Runs bill to **your own Apify account**. Apify's free monthly platform credit can cover small volumes. Plan limits apply; see the Actor page.
10
+ - Prices can change. The Actor's Pricing tab is the source of truth.
11
+ - Running `youtube-transcript-api` on your own machine is free and often works fine locally. You only need this if you're blocked.
12
+
13
+ ## Install
14
+ ```bash
15
+ pip install git+https://github.com/shephouse-actors/shephouse-yt
16
+ export APIFY_TOKEN=... # Apify Console → Settings → API & Integrations
17
+ ```
18
+
19
+ ## Try it in 60 seconds
20
+ 1. Create a free Apify account and copy your API token: Apify Console → Settings → API & Integrations.
21
+ 2. Run:
22
+ ```bash
23
+ pip install git+https://github.com/shephouse-actors/shephouse-yt
24
+ export APIFY_TOKEN=apify_api_...
25
+ python -m shephouse_yt https://www.youtube.com/watch?v=PXbVn-f_wuM # add --json or --lang de,en
26
+ ```
27
+ For RAG chunking with timestamps, see [examples/rag_chunks.py](examples/rag_chunks.py).
28
+
29
+ ## Use
30
+ ```python
31
+ from shephouse_yt import YouTubeTranscriptApi, TranscriptsDisabled
32
+
33
+ api = YouTubeTranscriptApi() # proxy_config / cookies kwargs are accepted and ignored
34
+ t = api.fetch("PXbVn-f_wuM", languages=["de", "en"])
35
+ print(t.language_code, t.is_generated, t[0].text, t[0].start, t[0].duration)
36
+ raw = t.to_raw_data() # [{"text", "start", "duration"}, ...]
37
+
38
+ many = api.fetch_many(["PXbVn-f_wuM", "AAAAAAAAAAA"]) # one Actor run for the batch
39
+ for vid, r in many.items():
40
+ print(vid, "error" if isinstance(r, Exception) else len(r))
41
+ ```
42
+ Exceptions: `TranscriptsDisabled`, `NoTranscriptFound`, `VideoUnavailable` (all subclass `CouldNotRetrieveTranscript`).
43
+
44
+ ## What is not supported
45
+ - `list()` / `translate()`.
46
+ - Formatters. For SRT/VTT/chapters/token counts, call the Actor directly.
47
+ - Each call is an Apify run, so expect several seconds to tens of seconds of latency. Batch with `fetch_many`.
48
+
49
+ ## Tests
50
+ `pip install pytest requests && pytest -q` (offline; the HTTP call is mocked).
51
+
52
+ Not affiliated with YouTube/Google or with the youtube-transcript-api project. MIT licensed.
@@ -0,0 +1,14 @@
1
+ [project]
2
+ name = "shephouse-yt"
3
+ version = "0.1.0"
4
+ description = "youtube-transcript-api-style client that works from cloud servers, backed by an Apify Actor"
5
+ readme = "README.md"
6
+ license = {text = "MIT"}
7
+ requires-python = ">=3.8"
8
+ dependencies = ["requests>=2.25"]
9
+ [build-system]
10
+ requires = ["setuptools>=61"]
11
+ build-backend = "setuptools.build_meta"
12
+ [project.urls]
13
+ Homepage = "https://github.com/shephouse-actors/shephouse-yt"
14
+ "Apify Actor" = "https://apify.com/shephouse/youtube-transcript-scraper-bulk"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,45 @@
1
+ """Drop-in subset of youtube-transcript-api that works from cloud servers.
2
+ Backed by the Apify Actor shephouse/youtube-transcript-scraper-bulk ($0.005 per delivered transcript; no charge for failures).
3
+ Usage: from shephouse_yt import YouTubeTranscriptApi; YouTubeTranscriptApi().fetch("VIDEO_ID", languages=["de","en"])
4
+ """
5
+ import os, requests
6
+ ACTOR = "shephouse~youtube-transcript-scraper-bulk"
7
+ class CouldNotRetrieveTranscript(Exception):
8
+ def __init__(self, video_id, code, msg=""): super().__init__(f"{video_id}: {code} {msg}".strip()); self.video_id, self.code = video_id, code
9
+ class NoTranscriptFound(CouldNotRetrieveTranscript): pass
10
+ class TranscriptsDisabled(CouldNotRetrieveTranscript): pass
11
+ class VideoUnavailable(CouldNotRetrieveTranscript): pass
12
+ _MAP = {"NO_CAPTIONS": TranscriptsDisabled, "EMPTY_CAPTIONS": TranscriptsDisabled, "NO_MATCHING_CAPTIONS": NoTranscriptFound, "VIDEO_UNAVAILABLE": VideoUnavailable, "VIDEO_UNPLAYABLE": VideoUnavailable, "LOGIN_REQUIRED": VideoUnavailable}
13
+ class FetchedTranscriptSnippet:
14
+ __slots__ = ("text", "start", "duration")
15
+ def __init__(self, text, start, duration): self.text, self.start, self.duration = text, start, duration
16
+ def __repr__(self): return f"FetchedTranscriptSnippet(text={self.text!r}, start={self.start}, duration={self.duration})"
17
+ class FetchedTranscript(list):
18
+ def __init__(self, item):
19
+ super().__init__(FetchedTranscriptSnippet(s["text"], s["start"], s.get("dur", 0.0)) for s in item.get("segments") or [])
20
+ self.video_id, self.language, self.language_code = item["videoId"], item.get("languageName"), item.get("language")
21
+ self.is_generated, self.raw = bool(item.get("isAutoGenerated")), item
22
+ def to_raw_data(self): return [{"text": s.text, "start": s.start, "duration": s.duration} for s in self]
23
+ class YouTubeTranscriptApi:
24
+ def __init__(self, apify_token=None, timeout=300, **_ignored): # proxy_config/cookies accepted and ignored
25
+ self.token = apify_token or os.environ.get("APIFY_TOKEN")
26
+ if not self.token: raise ValueError("Set APIFY_TOKEN or pass apify_token=")
27
+ self.timeout = timeout
28
+ def _run(self, ids, languages):
29
+ inp = {"startUrls": [{"url": f"https://www.youtube.com/watch?v={v}"} for v in ids], "languages": list(languages), "outputFormats": ["segments"], "maxTranscripts": len(ids)}
30
+ r = requests.post(f"https://api.apify.com/v2/acts/{ACTOR}/run-sync-get-dataset-items", params={"timeout": self.timeout},
31
+ headers={"Authorization": f"Bearer {self.token}"}, json=inp, timeout=self.timeout + 30)
32
+ r.raise_for_status(); return {i.get("videoId"): i for i in r.json()}
33
+ def fetch_many(self, video_ids, languages=("en",)):
34
+ got = self._run(video_ids, languages); out = {}
35
+ for v in video_ids:
36
+ i = got.get(v)
37
+ if i and i.get("status") == "OK": out[v] = FetchedTranscript(i)
38
+ else:
39
+ code = (i or {}).get("errorCode") or (i or {}).get("status") or "NOT_RETURNED"
40
+ out[v] = _MAP.get(code, CouldNotRetrieveTranscript)(v, code, (i or {}).get("errorMessage", ""))
41
+ return out
42
+ def fetch(self, video_id, languages=("en",), preserve_formatting=False):
43
+ r = self.fetch_many([video_id], languages)[video_id]
44
+ if isinstance(r, Exception): raise r
45
+ return r
@@ -0,0 +1,19 @@
1
+ """CLI: python -m shephouse_yt VIDEO_ID_OR_URL [...] [--lang en,de] [--json]
2
+ Needs APIFY_TOKEN. Cost: $0.005 per delivered transcript on your Apify account; failures are free."""
3
+ import argparse, json, re, sys
4
+ from . import YouTubeTranscriptApi
5
+ def vid(s):
6
+ m = re.search(r"(?:v=|youtu\.be/|shorts/|embed/)([A-Za-z0-9_-]{11})", s)
7
+ return m.group(1) if m else s
8
+ def main(argv=None):
9
+ p = argparse.ArgumentParser(prog="python -m shephouse_yt", description=__doc__)
10
+ p.add_argument("videos", nargs="+"); p.add_argument("--lang", default="en"); p.add_argument("--json", action="store_true")
11
+ a = p.parse_args(argv); ids = [vid(v) for v in a.videos]
12
+ res = YouTubeTranscriptApi().fetch_many(ids, languages=a.lang.split(","))
13
+ ok = 0
14
+ for v, r in res.items():
15
+ if isinstance(r, Exception): print(f"# {v}: {type(r).__name__}: {r}", file=sys.stderr); continue
16
+ ok += 1
17
+ print(json.dumps({"videoId": v, "language": r.language_code, "snippets": r.to_raw_data()}) if a.json else f"# {v} ({r.language_code})\n" + " ".join(s.text for s in r))
18
+ return 0 if ok else 1
19
+ if __name__ == "__main__": sys.exit(main())
@@ -0,0 +1,65 @@
1
+ Metadata-Version: 2.4
2
+ Name: shephouse-yt
3
+ Version: 0.1.0
4
+ Summary: youtube-transcript-api-style client that works from cloud servers, backed by an Apify Actor
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/shephouse-actors/shephouse-yt
7
+ Project-URL: Apify Actor, https://apify.com/shephouse/youtube-transcript-scraper-bulk
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: requests>=2.25
12
+ Dynamic: license-file
13
+
14
+ # shephouse-yt: YouTube transcripts from cloud servers (youtube-transcript-api-style client)
15
+
16
+ > **Disclosure:** ShepHouse is an AI-operated software studio. An AI agent wrote and maintains this repo, under a human owner. This client calls **our own paid Apify Actor**, [shephouse/youtube-transcript-scraper-bulk](https://apify.com/shephouse/youtube-transcript-scraper-bulk).
17
+
18
+ The `youtube-transcript-api` library is often blocked (`RequestBlocked` / `IpBlocked`) when you run it on AWS, GCP, Azure or other hosts. This small client keeps a familiar interface and fetches transcripts through the Apify Actor above, which uses residential proxies.
19
+
20
+ ## Cost (please read)
21
+ - **$0.005 per delivered transcript** (pay-per-event on Apify). Failed videos (no captions, unavailable, invalid ID) are **not charged**. There is no start fee.
22
+ - Runs bill to **your own Apify account**. Apify's free monthly platform credit can cover small volumes. Plan limits apply; see the Actor page.
23
+ - Prices can change. The Actor's Pricing tab is the source of truth.
24
+ - Running `youtube-transcript-api` on your own machine is free and often works fine locally. You only need this if you're blocked.
25
+
26
+ ## Install
27
+ ```bash
28
+ pip install git+https://github.com/shephouse-actors/shephouse-yt
29
+ export APIFY_TOKEN=... # Apify Console → Settings → API & Integrations
30
+ ```
31
+
32
+ ## Try it in 60 seconds
33
+ 1. Create a free Apify account and copy your API token: Apify Console → Settings → API & Integrations.
34
+ 2. Run:
35
+ ```bash
36
+ pip install git+https://github.com/shephouse-actors/shephouse-yt
37
+ export APIFY_TOKEN=apify_api_...
38
+ python -m shephouse_yt https://www.youtube.com/watch?v=PXbVn-f_wuM # add --json or --lang de,en
39
+ ```
40
+ For RAG chunking with timestamps, see [examples/rag_chunks.py](examples/rag_chunks.py).
41
+
42
+ ## Use
43
+ ```python
44
+ from shephouse_yt import YouTubeTranscriptApi, TranscriptsDisabled
45
+
46
+ api = YouTubeTranscriptApi() # proxy_config / cookies kwargs are accepted and ignored
47
+ t = api.fetch("PXbVn-f_wuM", languages=["de", "en"])
48
+ print(t.language_code, t.is_generated, t[0].text, t[0].start, t[0].duration)
49
+ raw = t.to_raw_data() # [{"text", "start", "duration"}, ...]
50
+
51
+ many = api.fetch_many(["PXbVn-f_wuM", "AAAAAAAAAAA"]) # one Actor run for the batch
52
+ for vid, r in many.items():
53
+ print(vid, "error" if isinstance(r, Exception) else len(r))
54
+ ```
55
+ Exceptions: `TranscriptsDisabled`, `NoTranscriptFound`, `VideoUnavailable` (all subclass `CouldNotRetrieveTranscript`).
56
+
57
+ ## What is not supported
58
+ - `list()` / `translate()`.
59
+ - Formatters. For SRT/VTT/chapters/token counts, call the Actor directly.
60
+ - Each call is an Apify run, so expect several seconds to tens of seconds of latency. Batch with `fetch_many`.
61
+
62
+ ## Tests
63
+ `pip install pytest requests && pytest -q` (offline; the HTTP call is mocked).
64
+
65
+ Not affiliated with YouTube/Google or with the youtube-transcript-api project. MIT licensed.
@@ -0,0 +1,11 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ shephouse_yt/__init__.py
5
+ shephouse_yt/__main__.py
6
+ shephouse_yt.egg-info/PKG-INFO
7
+ shephouse_yt.egg-info/SOURCES.txt
8
+ shephouse_yt.egg-info/dependency_links.txt
9
+ shephouse_yt.egg-info/requires.txt
10
+ shephouse_yt.egg-info/top_level.txt
11
+ tests/test_client.py
@@ -0,0 +1 @@
1
+ requests>=2.25
@@ -0,0 +1 @@
1
+ shephouse_yt
@@ -0,0 +1,36 @@
1
+ import pytest, shephouse_yt as s
2
+ OK = {"videoId": "PXbVn-f_wuM", "status": "OK", "language": "en", "languageName": "English", "isAutoGenerated": False,
3
+ "segments": [{"start": 1.12, "dur": 3.92, "text": "Hey"}, {"start": 5.0, "dur": 1.0, "text": "there"}]}
4
+ class R:
5
+ def __init__(self, d): self.d = d
6
+ def raise_for_status(self): pass
7
+ def json(self): return self.d
8
+ @pytest.fixture
9
+ def api(monkeypatch):
10
+ calls = []
11
+ def post(url, params=None, headers=None, json=None, timeout=None):
12
+ calls.append((url, headers, json))
13
+ return R([OK, {"videoId": "AAAAAAAAAAA", "status": "FAILED", "errorCode": "VIDEO_UNAVAILABLE", "errorMessage": "x"},
14
+ {"videoId": "nocaptions1", "status": "FAILED", "errorCode": "NO_CAPTIONS"}])
15
+ monkeypatch.setattr(s.requests, "post", post)
16
+ a = s.YouTubeTranscriptApi(apify_token="t", proxy_config=object()); a._calls = calls; return a
17
+ def test_fetch_ok(api):
18
+ t = api.fetch("PXbVn-f_wuM")
19
+ assert len(t) == 2 and t[0].text == "Hey" and t.to_raw_data()[0] == {"text": "Hey", "start": 1.12, "duration": 3.92}
20
+ assert t.language_code == "en" and t.is_generated is False
21
+ url, headers, body = api._calls[0]
22
+ assert "shephouse~youtube-transcript-scraper-bulk" in url and headers["Authorization"] == "Bearer t" and body["maxTranscripts"] == 1
23
+ def test_errors_mapped(api):
24
+ m = api.fetch_many(["AAAAAAAAAAA", "nocaptions1", "missing0000"])
25
+ assert isinstance(m["AAAAAAAAAAA"], s.VideoUnavailable) and isinstance(m["nocaptions1"], s.TranscriptsDisabled)
26
+ assert isinstance(m["missing0000"], s.CouldNotRetrieveTranscript)
27
+ with pytest.raises(s.VideoUnavailable): api.fetch("AAAAAAAAAAA")
28
+ def test_token_required(monkeypatch):
29
+ monkeypatch.delenv("APIFY_TOKEN", raising=False)
30
+ with pytest.raises(ValueError): s.YouTubeTranscriptApi()
31
+ def test_cli(api, monkeypatch, capsys):
32
+ import shephouse_yt.__main__ as cli
33
+ monkeypatch.setattr(cli, "YouTubeTranscriptApi", lambda: api)
34
+ assert cli.vid("https://youtu.be/PXbVn-f_wuM?t=3") == "PXbVn-f_wuM"
35
+ assert cli.main(["https://www.youtube.com/watch?v=PXbVn-f_wuM"]) == 0
36
+ assert "Hey there" in capsys.readouterr().out