merleau 0.1.0__py3-none-any.whl → 0.2.0__py3-none-any.whl
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.
- merleau/__init__.py +3 -0
- merleau/cli.py +117 -0
- {merleau-0.1.0.dist-info → merleau-0.2.0.dist-info}/METADATA +7 -7
- merleau-0.2.0.dist-info/RECORD +6 -0
- {merleau-0.1.0.dist-info → merleau-0.2.0.dist-info}/WHEEL +1 -2
- merleau-0.2.0.dist-info/entry_points.txt +2 -0
- merleau-0.1.0.dist-info/RECORD +0 -4
- merleau-0.1.0.dist-info/top_level.txt +0 -1
merleau/__init__.py
ADDED
merleau/cli.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Command-line interface for Merleau video analysis."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
|
|
8
|
+
from dotenv import load_dotenv
|
|
9
|
+
from google import genai
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def wait_for_processing(client, file):
|
|
13
|
+
"""Wait for file to finish processing."""
|
|
14
|
+
while file.state.name == "PROCESSING":
|
|
15
|
+
print(".", end="", flush=True)
|
|
16
|
+
time.sleep(2)
|
|
17
|
+
file = client.files.get(name=file.name)
|
|
18
|
+
print()
|
|
19
|
+
return file
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def print_usage(usage):
|
|
23
|
+
"""Print token usage and cost estimation."""
|
|
24
|
+
print("\n--- Usage Information ---")
|
|
25
|
+
print(f"Prompt tokens: {usage.prompt_token_count}")
|
|
26
|
+
print(f"Response tokens: {usage.candidates_token_count}")
|
|
27
|
+
print(f"Total tokens: {usage.total_token_count}")
|
|
28
|
+
|
|
29
|
+
# Gemini 2.5 Flash pricing (as of 2025):
|
|
30
|
+
# Input: $0.15 per 1M tokens (text/image), $0.075 per 1M tokens for video
|
|
31
|
+
# Output: $0.60 per 1M tokens, $3.50 for thinking tokens
|
|
32
|
+
input_cost = (usage.prompt_token_count / 1_000_000) * 0.15
|
|
33
|
+
output_cost = (usage.candidates_token_count / 1_000_000) * 0.60
|
|
34
|
+
total_cost = input_cost + output_cost
|
|
35
|
+
print(f"\nEstimated cost:")
|
|
36
|
+
print(f" Input: ${input_cost:.6f}")
|
|
37
|
+
print(f" Output: ${output_cost:.6f}")
|
|
38
|
+
print(f" Total: ${total_cost:.6f}")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def analyze(video_path, prompt, model, show_cost):
|
|
42
|
+
"""Analyze a video file using Gemini."""
|
|
43
|
+
load_dotenv()
|
|
44
|
+
|
|
45
|
+
api_key = os.getenv("GEMINI_API_KEY")
|
|
46
|
+
if not api_key:
|
|
47
|
+
print("Error: GEMINI_API_KEY not found in environment or .env file", file=sys.stderr)
|
|
48
|
+
sys.exit(1)
|
|
49
|
+
|
|
50
|
+
if not os.path.exists(video_path):
|
|
51
|
+
print(f"Error: Video file not found: {video_path}", file=sys.stderr)
|
|
52
|
+
sys.exit(1)
|
|
53
|
+
|
|
54
|
+
client = genai.Client(api_key=api_key)
|
|
55
|
+
|
|
56
|
+
# Upload video
|
|
57
|
+
print(f"Uploading video: {video_path}")
|
|
58
|
+
myfile = client.files.upload(file=video_path)
|
|
59
|
+
print(f"Upload complete. File URI: {myfile.uri}")
|
|
60
|
+
|
|
61
|
+
# Wait for processing
|
|
62
|
+
print("Waiting for file to be processed...", end="")
|
|
63
|
+
myfile = wait_for_processing(client, myfile)
|
|
64
|
+
|
|
65
|
+
if myfile.state.name == "FAILED":
|
|
66
|
+
print(f"Error: File processing failed", file=sys.stderr)
|
|
67
|
+
sys.exit(1)
|
|
68
|
+
|
|
69
|
+
print(f"File state: {myfile.state.name}")
|
|
70
|
+
|
|
71
|
+
# Generate analysis
|
|
72
|
+
print(f"\nAnalyzing video with {model}...")
|
|
73
|
+
response = client.models.generate_content(
|
|
74
|
+
model=model,
|
|
75
|
+
contents=[myfile, prompt]
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
print("\n--- Video Analysis ---")
|
|
79
|
+
print(response.text)
|
|
80
|
+
|
|
81
|
+
# Show usage if requested
|
|
82
|
+
if show_cost and hasattr(response, 'usage_metadata'):
|
|
83
|
+
print_usage(response.usage_metadata)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def main():
|
|
87
|
+
"""Main entry point for the CLI."""
|
|
88
|
+
parser = argparse.ArgumentParser(
|
|
89
|
+
prog="ponty",
|
|
90
|
+
description="Analyze videos using Google's Gemini API"
|
|
91
|
+
)
|
|
92
|
+
parser.add_argument(
|
|
93
|
+
"video",
|
|
94
|
+
help="Path to the video file to analyze"
|
|
95
|
+
)
|
|
96
|
+
parser.add_argument(
|
|
97
|
+
"-p", "--prompt",
|
|
98
|
+
default="Explain what happens in this video",
|
|
99
|
+
help="Prompt for the analysis (default: 'Explain what happens in this video')"
|
|
100
|
+
)
|
|
101
|
+
parser.add_argument(
|
|
102
|
+
"-m", "--model",
|
|
103
|
+
default="gemini-2.5-flash",
|
|
104
|
+
help="Gemini model to use (default: gemini-2.5-flash)"
|
|
105
|
+
)
|
|
106
|
+
parser.add_argument(
|
|
107
|
+
"--no-cost",
|
|
108
|
+
action="store_true",
|
|
109
|
+
help="Hide usage and cost information"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
args = parser.parse_args()
|
|
113
|
+
analyze(args.video, args.prompt, args.model, show_cost=not args.no_cost)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
if __name__ == "__main__":
|
|
117
|
+
main()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: merleau
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Video analysis using Google's Gemini 2.5 Flash API
|
|
5
|
-
Requires-Python: >=3.10
|
|
6
|
-
Requires-Dist: google-genai
|
|
7
|
-
Requires-Dist: python-dotenv
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: merleau
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Video analysis using Google's Gemini 2.5 Flash API
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Requires-Dist: google-genai
|
|
7
|
+
Requires-Dist: python-dotenv
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
merleau/__init__.py,sha256=y5-8LWAim3ziWojAKzkwChDCV3rFkVzPmJ3m6Lkojgo,81
|
|
2
|
+
merleau/cli.py,sha256=XQz6ncqj4Zl_jLAmiRhhYXdn7kFNM76ReuG0gTvF9KY,3471
|
|
3
|
+
merleau-0.2.0.dist-info/METADATA,sha256=-71rumv5Gjq75t9fwzKSUBl_W-cPm1rLMsUrKlhRimE,192
|
|
4
|
+
merleau-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
5
|
+
merleau-0.2.0.dist-info/entry_points.txt,sha256=pMCcADIqyZbK-lQ5d2iLKIWqNeeY3pAQDHI1IBmtdEQ,43
|
|
6
|
+
merleau-0.2.0.dist-info/RECORD,,
|
merleau-0.1.0.dist-info/RECORD
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
merleau-0.1.0.dist-info/METADATA,sha256=CE2o3opn5MUqcOf47bVQ_QhN4xxIYPzjQ95eigmRSKQ,199
|
|
2
|
-
merleau-0.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
3
|
-
merleau-0.1.0.dist-info/top_level.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
4
|
-
merleau-0.1.0.dist-info/RECORD,,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|