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.
- shephouse_yt-0.1.0/LICENSE +21 -0
- shephouse_yt-0.1.0/PKG-INFO +65 -0
- shephouse_yt-0.1.0/README.md +52 -0
- shephouse_yt-0.1.0/pyproject.toml +14 -0
- shephouse_yt-0.1.0/setup.cfg +4 -0
- shephouse_yt-0.1.0/shephouse_yt/__init__.py +45 -0
- shephouse_yt-0.1.0/shephouse_yt/__main__.py +19 -0
- shephouse_yt-0.1.0/shephouse_yt.egg-info/PKG-INFO +65 -0
- shephouse_yt-0.1.0/shephouse_yt.egg-info/SOURCES.txt +11 -0
- shephouse_yt-0.1.0/shephouse_yt.egg-info/dependency_links.txt +1 -0
- shephouse_yt-0.1.0/shephouse_yt.egg-info/requires.txt +1 -0
- shephouse_yt-0.1.0/shephouse_yt.egg-info/top_level.txt +1 -0
- shephouse_yt-0.1.0/tests/test_client.py +36 -0
|
@@ -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,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
|
+
|
|
@@ -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
|