Skip to content

Commit 4fa8223

Browse files
committed
save
1 parent 373756f commit 4fa8223

6 files changed

Lines changed: 175 additions & 41 deletions

File tree

README.md

Lines changed: 53 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,11 @@
33

44
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
55

6-
This file will become your README and also the index of your
7-
documentation.
8-
9-
## Developer Guide
10-
11-
If you are new to using `nbdev` here are some useful pointers to get you
12-
started.
13-
14-
### Install hamel in Development mode
15-
16-
``` sh
17-
# make sure hamel package is installed in development mode
18-
$ pip install -e .
19-
20-
# make changes under nbs/ directory
21-
# ...
22-
23-
# compile to have changes apply to hamel
24-
$ nbdev_prepare
25-
```
6+
A collection of personal utilities for various tasks, including: -
7+
Working with Google’s Gemini API for text generation and media
8+
analysis - Processing YouTube videos and **local MP4 files** for
9+
transcripts and chapter generation - Creating annotated blog posts from
10+
technical talks
2611

2712
## Usage
2813

@@ -35,33 +20,66 @@ Install latest from the GitHub
3520
$ pip install git+https://github.com/hamelsmu/hamel.git
3621
```
3722

38-
or from [conda](https://anaconda.org/hamelsmu/hamel)
39-
40-
``` sh
41-
$ conda install -c hamelsmu hamel
42-
```
43-
4423
or from [pypi](https://pypi.org/project/hamel/)
4524

4625
``` sh
4726
$ pip install hamel
4827
```
4928

29+
**Note**: For MP4 video transcription, you’ll also need: - FFmpeg:
30+
`brew install ffmpeg` (macOS) or `apt-get install ffmpeg` (Ubuntu) - The
31+
installation will include openai-whisper automatically
32+
5033
### Documentation
5134

5235
Documentation can be found hosted on this GitHub
5336
[repository](https://github.com/hamelsmu/hamel)’s
54-
[pages](https://hamelsmu.github.io/hamel/). Additionally you can find
55-
package manager specific guidelines on
56-
[conda](https://anaconda.org/hamelsmu/hamel) and
57-
[pypi](https://pypi.org/project/hamel/) respectively.
37+
[pages](https://hamelsmu.github.io/hamel/).
5838

59-
## How to use
39+
## Available Utilities
6040

61-
Fill me in please! Don’t forget code examples:
41+
### Gemini API (`hamel.gem`)
6242

63-
``` python
64-
1+1
43+
Simple interface for Google’s Gemini API supporting: - Text generation -
44+
PDF and image analysis
45+
- YouTube video analysis - **Local MP4 video analysis**
46+
47+
See the [gem module documentation](gem.html) for details.
48+
49+
### YouTube & Video Utilities (`hamel.yt`)
50+
51+
Tools for working with video content: - Fetch transcripts from YouTube
52+
videos - **Transcribe local MP4 files using OpenAI Whisper** - Generate
53+
chapter timestamps for YouTube videos or **local MP4 files**
54+
55+
See the [yt module documentation](yt.html) for details.
56+
57+
### Zoom Utilities (`hamel.zoom`)
58+
59+
Tools for downloading Zoom meeting transcripts: - Download transcripts
60+
by meeting ID - Search recordings by topic - Parallel downloads for
61+
multiple meetings
62+
63+
**Setup**: Requires Zoom Server-to-Server OAuth app with
64+
`recording:read:admin` scope activated in [Zoom
65+
marketplace](https://marketplace.zoom.us/). Click **Develop** in the
66+
top-right corner, then **Build App**, select **Server-to-Server OAuth**,
67+
and fill in the app information.
68+
69+
Create `.env` with:
70+
71+
``` bash
72+
ZOOM_CLIENT_ID=your_client_id
73+
ZOOM_CLIENT_SECRET=your_client_secret
74+
ZOOM_ACCOUNT_ID=your_account_id
6575
```
6676

67-
2
77+
See the [zoom module documentation](zoom.html) for details.
78+
79+
### Writing Utilities (`hamel.writing`)
80+
81+
Tools for content creation: - Convert PDFs to images - Generate
82+
annotated blog posts from talks (supports **YouTube or MP4 videos**) -
83+
Gather context from web pages
84+
85+
See the [writing module documentation](writing.html) for details.

hamel/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.0.23"
1+
__version__ = "0.0.24"

hamel/_modidx.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@
2727
'hamel.zoom.get_zoom_token': ('zoom.html#get_zoom_token', 'hamel/zoom.py'),
2828
'hamel.zoom.list_recordings': ('zoom.html#list_recordings', 'hamel/zoom.py'),
2929
'hamel.zoom.main': ('zoom.html#main', 'hamel/zoom.py'),
30-
'hamel.zoom.make_filename': ('zoom.html#make_filename', 'hamel/zoom.py')}}}
30+
'hamel.zoom.make_filename': ('zoom.html#make_filename', 'hamel/zoom.py')},
31+
'hamel.zoom_cli': {}}}

hamel/zoom_cli.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
"""CLI for Zoom transcript downloads."""
2+
3+
from pathlib import Path
4+
from dotenv import load_dotenv
5+
from fastcore.parallel import parallel
6+
import httpx
7+
import typer
8+
from hamel.zoom import get_zoom_token, list_recordings, download_transcript, make_filename
9+
10+
load_dotenv()
11+
12+
app = typer.Typer()
13+
14+
@app.command()
15+
def zoom(
16+
meeting_id: str = typer.Argument(None, help="Meeting ID to download"),
17+
search: str = typer.Option(None, "--search", "-s", help="Filter by text in topic"),
18+
days: int = typer.Option(45, "--days", "-d", help="Number of days to look back"),
19+
output: Path = typer.Option(None, "--output", "-o", help="Output file or directory"),
20+
):
21+
"""Download Zoom meeting transcripts.
22+
23+
Examples:
24+
zoom 123456789 # Print to stdout (pipe to other tools)
25+
zoom 123456789 -o file.vtt # Download to file
26+
zoom -s "Jason" # Search, select one or 'a' for all
27+
zoom -s "" # List all meetings
28+
"""
29+
try:
30+
token = get_zoom_token()
31+
32+
# Direct meeting ID download
33+
if meeting_id:
34+
transcript = download_transcript(meeting_id, token)
35+
if not transcript:
36+
print("No transcript available.")
37+
raise typer.Exit(1)
38+
39+
if output:
40+
output.parent.mkdir(parents=True, exist_ok=True)
41+
output.write_text(transcript, encoding="utf-8")
42+
print(f"Saved to {output}")
43+
else:
44+
print(transcript)
45+
return
46+
47+
# Search and download
48+
meetings = list_recordings(days)
49+
50+
# Filter by topic
51+
if search is not None:
52+
query = search.lower()
53+
meetings = [m for m in meetings if query in m.get('topic', '').lower()]
54+
55+
if not meetings:
56+
print("No meetings found.")
57+
return
58+
59+
# Show list
60+
print(f"\nFound {len(meetings)} meeting(s):\n")
61+
for idx, meeting in enumerate(meetings, 1):
62+
date = meeting['start_time'].split('T')[0]
63+
print(f"{idx:2}. {date} | {meeting['id']} | {meeting.get('topic', '')}")
64+
65+
# Prompt for selection
66+
choice = typer.prompt("\nEnter number (or 'a' for all)")
67+
68+
# Prompt for output directory
69+
outdir = Path(typer.prompt("Save to directory", default="."))
70+
if outdir != Path("."):
71+
outdir.mkdir(parents=True, exist_ok=True)
72+
73+
# Download all
74+
if choice.lower() == "a":
75+
print(f"\nDownloading {len(meetings)} transcript(s)...")
76+
77+
def download_one(meeting):
78+
transcript = download_transcript(str(meeting['id']), token)
79+
if transcript:
80+
filepath = outdir / make_filename(meeting)
81+
filepath.write_text(transcript, encoding="utf-8")
82+
print(f"✓ {filepath.name}")
83+
else:
84+
print(f"✗ No transcript: {meeting.get('topic', '')[:50]}")
85+
86+
parallel(download_one, meetings, threadpool=True, n_workers=8)
87+
return
88+
89+
# Download one
90+
try:
91+
idx = int(choice)
92+
except ValueError:
93+
print("Invalid input.")
94+
raise typer.Exit(1)
95+
96+
if idx < 1 or idx > len(meetings):
97+
print("Invalid selection.")
98+
raise typer.Exit(1)
99+
100+
meeting = meetings[idx - 1]
101+
transcript = download_transcript(str(meeting['id']), token)
102+
if not transcript:
103+
print("No transcript available.")
104+
raise typer.Exit(1)
105+
106+
filepath = outdir / make_filename(meeting)
107+
filepath.write_text(transcript, encoding="utf-8")
108+
print(f"Saved to {filepath}")
109+
110+
except (httpx.HTTPError, httpx.RequestError, KeyError) as e:
111+
print(f"Error: {e}")
112+
raise typer.Exit(1)
113+
114+
def main():
115+
app()

nbs/index.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
{
6161
"cell_type": "markdown",
6262
"metadata": {},
63-
"source": "### Gemini API (`hamel.gem`)\n\nSimple interface for Google's Gemini API supporting:\n- Text generation\n- PDF and image analysis \n- YouTube video analysis\n- **Local MP4 video analysis**\n\nSee the [gem module documentation](gem.html) for details.\n\n### YouTube & Video Utilities (`hamel.yt`)\n\nTools for working with video content:\n- Fetch transcripts from YouTube videos\n- **Transcribe local MP4 files using OpenAI Whisper**\n- Generate chapter timestamps for YouTube videos or **local MP4 files**\n\nSee the [yt module documentation](yt.html) for details.\n\n### Writing Utilities (`hamel.writing`)\n\nTools for content creation:\n- Convert PDFs to images\n- Generate annotated blog posts from talks (supports **YouTube or MP4 videos**)\n- Gather context from web pages\n\nSee the [writing module documentation](writing.html) for details."
63+
"source": "### Gemini API (`hamel.gem`)\n\nSimple interface for Google's Gemini API supporting:\n- Text generation\n- PDF and image analysis \n- YouTube video analysis\n- **Local MP4 video analysis**\n\nSee the [gem module documentation](gem.html) for details.\n\n### YouTube & Video Utilities (`hamel.yt`)\n\nTools for working with video content:\n- Fetch transcripts from YouTube videos\n- **Transcribe local MP4 files using OpenAI Whisper**\n- Generate chapter timestamps for YouTube videos or **local MP4 files**\n\nSee the [yt module documentation](yt.html) for details.\n\n### Zoom Utilities (`hamel.zoom`)\n\nTools for downloading Zoom meeting transcripts:\n- Download transcripts by meeting ID\n- Search recordings by topic\n- Parallel downloads for multiple meetings\n\n**Setup**: Requires Zoom Server-to-Server OAuth app with `recording:read:admin` scope activated in [Zoom marketplace](https://marketplace.zoom.us/). Click **Develop** in the top-right corner, then **Build App**, select **Server-to-Server OAuth**, and fill in the app information.\n\nCreate `.env` with:\n```bash\nZOOM_CLIENT_ID=your_client_id\nZOOM_CLIENT_SECRET=your_client_secret\nZOOM_ACCOUNT_ID=your_account_id\n```\n\nSee the [zoom module documentation](zoom.html) for details.\n\n### Writing Utilities (`hamel.writing`)\n\nTools for content creation:\n- Convert PDFs to images\n- Generate annotated blog posts from talks (supports **YouTube or MP4 videos**)\n- Gather context from web pages\n\nSee the [writing module documentation](writing.html) for details."
6464
}
6565
],
6666
"metadata": {

settings.ini

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[DEFAULT]
22
repo = hamel
33
lib_name = hamel
4-
version = 0.0.23
4+
version = 0.0.24
55
min_python = 3.9
66
license = apache2
77
black_formatting = False
@@ -27,8 +27,8 @@ keywords = nbdev jupyter notebook python
2727
language = English
2828
status = 3
2929
user = hamelsmu
30-
requirements = typer youtube_transcript_api httpx fastcore google-genai openai-whisper fastprogress
31-
console_scripts = ai-gem=hamel.gem_cli:main
30+
requirements = typer youtube_transcript_api httpx fastcore google-genai openai-whisper fastprogress python-dotenv
31+
console_scripts = ai-gem=hamel.gem_cli:main zoom=hamel.zoom_cli:main
3232
readme_nb = index.ipynb
3333
allowed_metadata_keys =
3434
allowed_cell_metadata_keys =

0 commit comments

Comments
 (0)