Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
.DS_Store
/config.toml
/storage/
/memmoro/output/
/memmoro/materials/
/.idea/
/app/services/__pycache__
/app/__pycache__/
Expand Down
44 changes: 37 additions & 7 deletions app/services/voice.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,20 @@
import edge_tts
import requests
from edge_tts import SubMaker, submaker
from edge_tts.submaker import mktimestamp
from loguru import logger
from moviepy.video.tools import subtitles

try:
from edge_tts.submaker import mktimestamp
except ImportError:
import math

def mktimestamp(time_unit: float) -> str:
hour = math.floor(time_unit / 10**7 / 3600)
minute = math.floor((time_unit / 10**7 / 60) % 60)
seconds = (time_unit / 10**7) % 60
return f"{hour:02d}:{minute:02d}:{seconds:06.3f}"

from app.config import config
from app.utils import utils

Expand Down Expand Up @@ -1116,6 +1126,23 @@ def convert_rate_to_percent(rate: float) -> str:
return f"{percent}%"


def _append_tts_boundary(sub_maker: SubMaker, chunk: dict) -> None:
if hasattr(sub_maker, "create_sub"):
sub_maker.create_sub((chunk["offset"], chunk["duration"]), chunk["text"])
return

if not hasattr(sub_maker, "offset"):
sub_maker.offset = []
if not hasattr(sub_maker, "subs"):
sub_maker.subs = []

sub_maker.offset.append((chunk["offset"], chunk["offset"] + chunk["duration"]))
sub_maker.subs.append(chunk["text"])

if hasattr(sub_maker, "feed"):
sub_maker.feed(chunk)


def azure_tts_v1(
text: str, voice_name: str, voice_rate: float, voice_file: str
) -> Union[SubMaker, None]:
Expand All @@ -1133,15 +1160,18 @@ async def _do() -> SubMaker:
async for chunk in communicate.stream():
if chunk["type"] == "audio":
file.write(chunk["data"])
elif chunk["type"] == "WordBoundary":
sub_maker.create_sub(
(chunk["offset"], chunk["duration"]), chunk["text"]
)
elif chunk["type"] in ("WordBoundary", "SentenceBoundary"):
_append_tts_boundary(sub_maker, chunk)
return sub_maker

sub_maker = asyncio.run(_do())
if not sub_maker or not sub_maker.subs:
logger.warning("failed, sub_maker is None or sub_maker.subs is None")
if not sub_maker:
logger.warning("failed, sub_maker is None")
continue

has_subs = bool(getattr(sub_maker, "subs", None) or getattr(sub_maker, "cues", None))
if not has_subs:
logger.warning("failed, sub_maker has no subtitle cues")
continue

logger.info(f"completed, output file: {voice_file}")
Expand Down
67 changes: 67 additions & 0 deletions memmoro/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Memmoro YouTube Shorts ads

English 9:16 ad package for [Memmoro](https://pexch.lat/) built on MoneyPrinterTurbo.

## Contents

- `ads.json` — five fixed EN scripts with Pexels search terms
- `preset.json` — Shorts defaults (voice, subtitles, aspect ratio)
- `generate.py` — CLI wrapper around `app.services.task.start`
- `output/` — generated MP4 files (gitignored)

## Requirements

- Python deps from repo root: `pip install -r requirements.txt`
- `ffmpeg` on PATH
- Internet for `edge-tts` (voice synthesis)
- **Recommended:** `pexels_api_keys` in `config.toml` for stock footage
- **Fallback:** if no Pexels key is configured, the CLI uses local PNG materials from `memmoro/materials/` (seeded from `test/resources/`)

## Setup

```bash
cp config.example.toml config.toml
# Add at least one Pexels API key under [app].pexels_api_keys
```

LLM keys are **not** required when using the fixed scripts in `ads.json`.

## Usage

```bash
python memmoro/generate.py --list
python memmoro/generate.py --ad who-is-talking
python memmoro/generate.py --demo
python memmoro/generate.py --all
```

Demo set (used for PR artifacts):

1. `who-is-talking`
2. `caregiving`
3. `privacy-on-device`

Outputs:

- `memmoro/output/<ad-id>/memmoro-<ad-id>.mp4`
- `/opt/cursor/artifacts/memmoro-shorts/` (when available)

## Messaging guardrails

Scripts intentionally avoid:

- medical device / diagnosis / treatment claims
- guaranteed accuracy
- transcription positioning

They emphasize on-device processing and speaker identification.

## Ad concepts

| ID | Angle |
|----|-------|
| `who-is-talking` | Social awkwardness, parties, calls |
| `caregiving` | Families supporting loved ones |
| `deaf-hoh` | Accessibility without face recognition |
| `meetings` | Work calls and colleagues |
| `privacy-on-device` | No cloud voice upload |
78 changes: 78 additions & 0 deletions memmoro/ads.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
{
"product": "Memmoro",
"language": "en",
"platform": "youtube-shorts",
"disclaimer": "Not a medical device. Not transcription. Voice processing stays on your device.",
"ads": [
{
"id": "who-is-talking",
"title": "Who is talking?",
"subject": "Memmoro — real-time voice identification for social moments",
"cta": "Memmoro — on the App Store",
"script": "That awkward pause when someone talks and you cannot place the voice? Memmoro shows their name on screen — in real time. Register friends and family once. When they speak, you see who it is. Not transcription. Not a medical device. Just less guessing at parties, calls, and everyday moments. Memmoro — on-device voice identification. Try it on the App Store.",
"terms": [
"friends talking at party",
"phone video call conversation",
"social gathering people talking",
"person speaking microphone",
"family dinner conversation"
]
},
{
"id": "caregiving",
"title": "Caregiving support",
"subject": "Memmoro — voice identification for families and caregivers",
"cta": "Memmoro — on the App Store",
"script": "When a loved one cannot always remember who is speaking, every conversation can feel harder. Memmoro registers people by voice and shows their name on screen when they talk. Optional photo. Companion mode for a clear full-screen display. Everything runs on the phone — no voice upload to the cloud. Not a medical device. Not transcription. A small tool that can make daily moments easier. Memmoro — on the App Store.",
"terms": [
"elderly care family support",
"caregiver helping senior",
"family visiting home",
"grandparent with family",
"warm living room conversation"
]
},
{
"id": "deaf-hoh",
"title": "Accessibility for Deaf and HoH",
"subject": "Memmoro — see who is speaking without relying on face recognition",
"cta": "Memmoro — on the App Store",
"script": "You hear someone speak — but who is it? Face recognition fails in bad light and at odd angles. Memmoro goes the other way: voice. Register people once, and their name appears when they speak. On-device processing. No voice data sent to a server. Not transcription — speaker identification. Built for accessibility, caregiving, and real life. Memmoro — on the App Store.",
"terms": [
"deaf community communication",
"person using smartphone accessibility",
"sign language interpreter meeting",
"inclusive workplace conversation",
"listening with headphones"
]
},
{
"id": "meetings",
"title": "Meetings and colleagues",
"subject": "Memmoro — know who is speaking in meetings and calls",
"cta": "Memmoro — on the App Store",
"script": "New colleague on the call. Side conversation in a meeting. Someone walks in mid-discussion. Memmoro registers voices and shows names when people speak — so you spend less time guessing and more time listening. Works on your phone. Processing stays on-device. No cloud voice upload. Not transcription. Memmoro — on the App Store.",
"terms": [
"business meeting office",
"video conference laptop",
"team standup meeting",
"colleagues talking office",
"workplace presentation"
]
},
{
"id": "privacy-on-device",
"title": "Privacy on device",
"subject": "Memmoro — 100% on-device voice identification",
"cta": "Memmoro — on the App Store",
"script": "Your voice is personal. Memmoro processes everything on your iPhone — no voice upload, no cloud server. Register friends, family, or colleagues. See their name when they speak. Real-time speaker identification. Not transcription. Not a medical device. Privacy-first by design. Memmoro — on the App Store.",
"terms": [
"smartphone privacy security",
"person holding iphone",
"data protection technology",
"secure mobile app",
"home office phone"
]
}
]
}
Loading