@clawhub-nicowu07-59d9256d97
Fetch weekly sale details from Australian supermarkets (Woolworths and Coles). Use when the user wants to check current specials, compare prices, or get sale...
---
name: supermarket-sales
description: Fetch weekly sale details from Australian supermarkets (Woolworths and Coles). Use when the user wants to check current specials, compare prices, or get sale information from Woolworths or Coles.
---
# Supermarket Sales Fetcher
Fetch weekly specials and sale details from Woolworths and Coles Australia.
## Overview
This skill retrieves current supermarket specials from Australia's two major grocery chains:
- **Coles** - Direct fetch via Puppeteer browser automation
- **Woolworths** - Via trusted third-party aggregators (official site blocks automated access)
Works well for Melbourne metro area (postcode 3000-3008).
## Installation
```bash
# Clone and install dependencies
npm install
```
This will install:
- puppeteer (^22.0.0) - Browser automation
Requires: Node.js 18+, npm
## Usage
### Common Commands
| Task | Command |
|------|--------|
| Fetch all deals | `npm start` or `node scripts/fetch_daily.js` |
| Coles only | `npm run coles` or `node scripts/fetch_with_puppeteer.js` |
| Bash (basic) | `bash scripts/fetch_sales.sh` |
### Alternative: Web Fetch
For Woolworths deals without running code:
```
web_fetch: { "url": "https://www.catalogueau.com/sales/?stores=Woolworths" }
web_fetch: { "url": "https://currentspecials.com.au/woolworths-weekly-catalogue/" }
```
### Alternative: Search
```
web_search_plus: { "query": "Woolworths Coles specials this week Melbourne" }
```
## How It Works
### Coles
Uses Puppeteer to launch a headless browser, navigate to coles.com.au/on-special, and extract product cards with prices and savings.
### Woolworths
Uses trusted aggregators (catalogueau.com, currentspecials.com.au) since woolworths.com.au blocks automated access.
## Output
Sample output:
```
=== COLES SPECIALS - 11 Apr 2026 ===
| # | Product | Price | Deal |
|---|---|---|
| 1 | Coles Strawberries 250g | $4.00 | Was $16/kg |
| 2 | Staminade Powder 585g | $8.00 | Save $3.50 |
```
## Sources
| Store | Method | URL |
|-------|-------|-----|
| Coles | Puppeteer | coles.com.au/on-special |
| Woolworths | Aggregator | catalogueau.com/woolworths/ |
| Both | Search | web_search_plus |
## Troubleshooting
### Puppeteer not found
Install Chromium:
```bash
npx puppeteer browsers install chrome
```
### No deals found
- Site may have changed selectors - check for updates
- Try alternative methods (web_fetch or search)
### Errors
Check Node.js version: `node --version` (need 18+)
FILE:package.json
{
"name": "supermarket-sales",
"version": "1.1.0",
"description": "Fetch weekly sale details from Australian supermarkets (Woolworths and Coles)",
"main": "scripts/fetch_daily.js",
"scripts": {
"start": "node scripts/fetch_daily.js",
"coles": "node scripts/fetch_with_puppeteer.js"
},
"dependencies": {
"puppeteer": "^22.0.0"
},
"engines": {
"node": ">=18.0.0"
},
"keywords": ["australian", "supermarkets", "coles", "woolworths", "deals", "shopping"],
"license": "MIT",
"homepage": "https://clawhub.com/s/skills/supermarket-sales",
"repository": {
"type": "git",
"url": "https://github.com/openclaw/skills"
}
}
FILE:scripts/fetch_daily.js
#!/usr/bin/env node
/**
* Daily Supermarket Sales Fetcher
* Fetches Coles and Woolworths specials for the week
* Run: node fetch_daily.js
*/
const puppeteer = require('puppeteer');
const COLES_URL = 'https://www.coles.com.au/on-special';
const WOOLWORTHS_CATALOGUE = 'https://www.catalogueau.com/woolworths/';
const WOOLWORTHS_SPECIALS = 'https://www.catalogueau.com/sales/?stores=Woolworths';
async function fetchColes() {
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage']
});
const page = await browser.newPage();
await page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36');
await page.goto(COLES_URL, { waitUntil: 'networkidle2', timeout: 30000 });
await new Promise(r => setTimeout(r, 5000));
const deals = await page.evaluate(() => {
const items = [];
const cards = document.querySelectorAll('article, .product-tile, [data-testid*="product"], .product-card');
cards.forEach(card => {
const text = card.innerText.replace(/\n/g, ' ').trim();
if (text.includes('$') && text.length > 10 && text.length < 200) {
items.push(text.substring(0, 150));
}
});
return items.slice(0, 20);
});
await browser.close();
return deals;
}
function printHeader(store) {
const date = new Date().toLocaleDateString('en-AU', { day: 'numeric', month: 'short', year: 'numeric' });
console.log(`\n=== store.toUpperCase() SPECIALS - date ===`);
console.log(`Source: 'catalogueau.com'\n`);
}
async function main() {
console.log('🛒 Fetching Daily Supermarket Specials...\n');
// Coles
try {
printHeader('Coles');
const colesDeals = await fetchColes();
if (colesDeals.length > 0) {
console.log('| # | Product | Price | Deal |');
console.log('|---|---|---|---|');
colesDeals.slice(0, 10).forEach((deal, i) => {
// Clean up and format
const clean = deal.replace(/\s+/g, ' ').substring(0, 100);
console.log(`| i+1 | clean.split('$')[0].substring(0, 30) || '-' | $clean.match(/\$[\d.]+/)?.[0]?.replace('$','') || '-' | clean.match(/Save \$[\d.]+|Was \$[\d.]+/)?.[0] || '-' |`);
});
} else {
console.log('No deals found via browser. Try web_fetch instead.');
}
} catch(e) {
console.log('Coles error:', e.message);
}
// Woolworths (from aggregator)
console.log('\n=== WOOLWORTHS SPECIALS ===');
console.log('Source: catalogueau.com');
console.log('\nUse: web_fetch https://www.catalogueau.com/sales/?stores=Woolworths');
console.log('Or search: Woolworths half price specials this week');
console.log('\n✅ Done! Edit the skill or cron job for daily automation.');
}
main().catch(console.error);
FILE:scripts/fetch_sales.sh
#!/bin/bash
#
# Fetch supermarket specials using curl
# Usage: ./fetch_sales.sh [woolworths|coles|both]
STORE="-both"
USER_AGENT="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
fetch_woolworths() {
echo "=== Woolworths Specials ==="
echo "Date: $(date +%Y-%m-%d)"
echo "URL: https://www.woolworths.com.au/shop/catalogue"
echo ""
# Try to fetch the page
HTML=$(curl -s -L "https://www.woolworths.com.au/shop/catalogue" \
-H "User-Agent: $USER_AGENT" \
-H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" \
-H "Accept-Language: en-US,en;q=0.5" \
--max-time 30 2>/dev/null)
if [ -z "$HTML" ]; then
echo "Error: Could not fetch Woolworths page"
echo "Note: This site requires JavaScript. Try visiting manually:"
echo "https://www.woolworths.com.au/shop/catalogue"
return 1
fi
# Check if we got meaningful content
TITLE=$(echo "$HTML" | grep -o '<title>[^<]*</title>' | sed 's/<[^\u003e]*>//g')
echo "Page title: $TITLE"
echo "Content length: #HTML characters"
echo ""
# Try to extract prices
echo "Sample prices found:"
echo "$HTML" | grep -oE '\$[0-9]+\.?[0-9]*' | head -10
echo ""
echo "Note: Full extraction may require JavaScript rendering."
echo "Consider using browser automation for complete results."
}
fetch_coles() {
echo "=== Coles Specials ==="
echo "Date: $(date +%Y-%m-%d)"
echo "URL: https://www.coles.com.au/on-special"
echo ""
# Try to fetch the page
HTML=$(curl -s -L "https://www.coles.com.au/on-special" \
-H "User-Agent: $USER_AGENT" \
-H "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" \
-H "Accept-Language: en-US,en;q=0.5" \
--max-time 30 2>/dev/null)
if [ -z "$HTML" ]; then
echo "Error: Could not fetch Coles page"
echo "Note: This site requires JavaScript. Try visiting manually:"
echo "https://www.coles.com.au/on-special"
return 1
fi
# Check if we got meaningful content
TITLE=$(echo "$HTML" | grep -o '<title>[^<]*</title>' | sed 's/<[^\u003e]*>//g')
echo "Page title: $TITLE"
echo "Content length: #HTML characters"
echo ""
# Try to extract prices
echo "Sample prices found:"
echo "$HTML" | grep -oE '\$[0-9]+\.?[0-9]*' | head -10
echo ""
echo "Note: Full extraction may require JavaScript rendering."
echo "Consider using browser automation for complete results."
}
# Main execution
case "$STORE" in
woolworths)
fetch_woolworths
;;
coles)
fetch_coles
;;
both)
fetch_woolworths
echo ""
echo "========================================"
echo ""
fetch_coles
;;
*)
echo "Usage: $0 [woolworths|coles|both]"
exit 1
;;
esac
FILE:scripts/fetch_with_puppeteer.js
#!/usr/bin/env node
/**
* Fetch supermarket specials using Puppeteer browser automation
* Usage: node fetch_with_puppeteer.js [woolworths|coles|both]
*/
const puppeteer = require('puppeteer');
async function fetchWoolworths() {
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage']
});
const page = await browser.newPage();
try {
await page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
await page.goto('https://www.woolworths.com.au/shop/catalogue', {
waitUntil: 'networkidle2',
timeout: 30000
});
await new Promise(r => setTimeout(r, 5000));
const products = await page.evaluate(() => {
const items = [];
const cards = document.querySelectorAll('article, .product-tile, [data-testid*="product"], .product-card');
cards.forEach(card => {
const text = card.innerText.replace(/\n/g, ' ').trim();
if (text.includes('$') && text.length > 10 && text.length < 200) {
items.push(text.substring(0, 150));
}
});
return items.slice(0, 20);
});
return { specials: products, count: products.length };
} catch (e) {
return { error: e.message };
} finally {
await browser.close();
}
}
async function fetchColes() {
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage']
});
const page = await browser.newPage();
try {
await page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
await page.goto('https://www.coles.com.au/on-special', {
waitUntil: 'networkidle2',
timeout: 30000
});
await new Promise(r => setTimeout(r, 5000));
const products = await page.evaluate(() => {
const items = [];
const cards = document.querySelectorAll('article, .product-tile, [data-testid*="product"], .product-card');
cards.forEach(card => {
const text = card.innerText.replace(/\n/g, ' ').trim();
if (text.includes('$') && text.length > 10 && text.length < 200) {
items.push(text.substring(0, 150));
}
});
return items.slice(0, 20);
});
return { specials: products, count: products.length };
} catch (e) {
return { error: e.message };
} finally {
await browser.close();
}
}
async function main() {
const args = process.argv.slice(2);
const which = args[0] || 'both';
let woolworths = null;
let coles = null;
if (which === 'woolworths' || which === 'both') {
console.log('Fetching Woolworths...');
woolworths = await fetchWoolworths();
}
if (which === 'coles' || which === 'both') {
console.log('Fetching Coles...');
coles = await fetchColes();
}
console.log(JSON.stringify({ woolworths, coles }, null, 2));
}
main().catch(console.error);Professional currency exchange rate analysis and forecasting for any currency pair. Provides technical analysis, trend prediction, and market insights using...
---
name: currency-forecast-pro
description: Professional currency exchange rate analysis and forecasting for any currency pair. Provides technical analysis, trend prediction, and market insights using real-time data.
metadata:
openclaw:
emoji: "💹"
requires:
env: []
---
# Currency Forecast Pro
A professional-grade currency analysis tool that provides comprehensive exchange rate forecasting for any currency pair.
## Features
- **Universal Currency Support**: Works with any currency pair (USD/EUR, GBP/JPY, AUD/CNY, etc.)
- **Technical Analysis**: Moving averages (7/14/30-day), trend lines, support/resistance levels
- **Predictive Modeling**: Short-term (7-day) and medium-term (30-day) forecasts
- **Market Context**: Real-time market research and economic factors
- **Risk Assessment**: Volatility analysis and confidence intervals
## Usage
### Basic Forecast
```
Forecast USD/EUR exchange rate
```
### Specific Timeframe
```
Analyze GBP/JPY with 60 days of historical data
```
### Compare Multiple Pairs
```
Compare USD/CNY and EUR/USD forecasts
```
## Supported Currencies
All major currencies supported by Frankfurter API:
- **Major**: USD, EUR, GBP, JPY, CHF, CAD, AUD, NZD
- **Asian**: CNY, HKD, SGD, KRW, INR, THB, MYR, IDR
- **European**: SEK, NOK, DKK, PLN, CZK, HUF, RON
- **Others**: MXN, BRL, ZAR, TRY, RUB, AED, SAR
## Output Format
```
💹 Currency Forecast: USD/EUR
📊 Technical Analysis
Current: 0.9234
MA7: 0.9210 | MA14: 0.9185 | MA30: 0.9150
Trend: Upward 📈 (slope: +0.00012)
Support: 0.9100 | Resistance: 0.9350
Volatility: 0.45% daily
📰 Market Context
• Fed Policy: Hawkish stance
• ECB: Dovish outlook
• US Data: Strong employment
🔮 Forecasts
7-day: 0.9250 ~ 0.9300 (+0.2% ~ +0.7%)
30-day: 0.9300 ~ 0.9450 (+0.7% ~ +2.3%)
Confidence: High
⚠️ Risk Factors
- Upcoming Fed meeting
- EU inflation data
- Geopolitical tensions
```
## Data Sources
- **Frankfurter API**: ECB reference rates, historical data
- **Web Search**: Current market conditions and news
- **Technical Indicators**: Calculated locally using statistical methods
## Technical Details
### Indicators Calculated
1. **Moving Averages**: Simple MA for 7, 14, and 30-day periods
2. **Trend Analysis**: Linear regression slope over 30 days
3. **Support/Resistance**: Min/Max of recent 20-day range
4. **Volatility**: Average absolute daily change percentage
5. **Forecast**: Linear projection with volatility bands
### Forecast Methodology
- Short-term (7-day): Linear trend projection
- Medium-term (30-day): Trend + seasonal adjustment
- Confidence levels based on recent volatility
## Installation
No additional dependencies required. Uses:
- `exec` for API calls
- `web_search` for market research
## Examples
### Example 1: Major Pair
```
User: Forecast EUR/USD
Agent: [Comprehensive analysis with 75-day data]
```
### Example 2: Asian Currencies
```
User: Analyze USD/CNY and USD/JPY
Agent: [Comparative analysis of both pairs]
```
### Example 3: Commodity Currencies
```
User: What's the outlook for AUD and CAD against USD?
Agent: [Analysis considering commodity price factors]
```
## Version History
- **1.0.0**: Initial release with full technical analysis
## License
MIT-0 - Free to use, modify, and redistribute
## Author
Created by clawclaw for OpenClaw
FILE:_meta.json
{
"name": "currency-forecast-pro",
"version": "1.0.0",
"description": "Professional currency exchange rate analysis and forecasting for any currency pair",
"author": "clawclaw",
"license": "MIT-0",
"tags": ["finance", "currency", "forecast", "exchange-rate", "trading", "forex", "analysis"],
"homepage": "https://clawhub.com/skills/currency-forecast-pro"
}
FILE:scripts/analyze.py
#!/usr/bin/env python3
"""
Currency Forecast Pro - Technical Analysis Engine
Performs comprehensive technical analysis on any currency pair
"""
import json
import sys
import math
from datetime import datetime, timedelta
def fetch_exchange_data(base, target, days=75):
"""Fetch historical exchange rate data from Frankfurter API"""
import urllib.request
end_date = datetime.now().strftime('%Y-%m-%d')
start_date = (datetime.now() - timedelta(days=days)).strftime('%Y-%m-%d')
url = f"https://api.frankfurter.app/{start_date}..{end_date}?from={base}&to={target}"
try:
with urllib.request.urlopen(url, timeout=10) as response:
data = json.loads(response.read().decode())
return data
except Exception as e:
return {"error": str(e)}
def calculate_ma(values, period):
"""Calculate simple moving average"""
if len(values) < period:
return sum(values) / len(values) if values else 0
return sum(values[-period:]) / period
def calculate_trend_slope(values, period=30):
"""Calculate linear regression slope"""
n = min(period, len(values))
if n < 2:
return 0
x = list(range(n))
y = values[-n:]
x_mean = sum(x) / n
y_mean = sum(y) / n
numerator = sum((x[i] - x_mean) * (y[i] - y_mean) for i in range(n))
denominator = sum((x[i] - x_mean) ** 2 for i in range(n))
return numerator / denominator if denominator != 0 else 0
def calculate_volatility(values):
"""Calculate average daily volatility as percentage"""
if len(values) < 2:
return 0
daily_changes = [abs(values[i] - values[i-1]) for i in range(1, len(values))]
avg_change = sum(daily_changes) / len(daily_changes)
current = values[-1]
return (avg_change / current) * 100 if current != 0 else 0
def analyze_currency_pair(base, target, days=75):
"""Perform complete technical analysis on a currency pair"""
# Fetch data
data = fetch_exchange_data(base, target, days)
if "error" in data:
return {"error": data["error"]}
rates = data.get("rates", {})
if not rates:
return {"error": "No data available for this currency pair"}
dates = sorted(rates.keys())
values = [rates[d][target] for d in dates]
n = len(values)
current = values[-1]
# Calculate indicators
ma7 = calculate_ma(values, 7)
ma14 = calculate_ma(values, 14)
ma30 = calculate_ma(values, 30)
slope = calculate_trend_slope(values, 30)
volatility = calculate_volatility(values)
# Support and resistance (20-day)
recent = values[-20:] if len(values) >= 20 else values
support = min(recent)
resistance = max(recent)
# Forecasts
forecast_7d = current + (slope * 7)
forecast_30d = current + (slope * 30)
# Volatility bands
vol_factor = volatility / 100
forecast_7d_low = forecast_7d * (1 - vol_factor * 2)
forecast_7d_high = forecast_7d * (1 + vol_factor * 2)
forecast_30d_low = forecast_30d * (1 - vol_factor * 3)
forecast_30d_high = forecast_30d * (1 + vol_factor * 3)
return {
"base": base,
"target": target,
"current_rate": round(current, 4),
"data_points": n,
"date_range": f"{dates[0]} to {dates[-1]}",
"indicators": {
"ma7": round(ma7, 4),
"ma14": round(ma14, 4),
"ma30": round(ma30, 4),
"trend_slope": round(slope, 6),
"trend_direction": "upward" if slope > 0 else "downward" if slope < 0 else "flat",
"volatility_percent": round(volatility, 2),
"support": round(support, 4),
"resistance": round(resistance, 4)
},
"forecasts": {
"7_day": {
"predicted": round(forecast_7d, 4),
"range_low": round(forecast_7d_low, 4),
"range_high": round(forecast_7d_high, 4),
"change_percent": round(((forecast_7d - current) / current) * 100, 2)
},
"30_day": {
"predicted": round(forecast_30d, 4),
"range_low": round(forecast_30d_low, 4),
"range_high": round(forecast_30d_high, 4),
"change_percent": round(((forecast_30d - current) / current) * 100, 2)
}
}
}
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description="Currency Forecast Pro Analysis")
parser.add_argument("--base", required=True, help="Base currency code (e.g., USD)")
parser.add_argument("--target", required=True, help="Target currency code (e.g., EUR)")
parser.add_argument("--days", type=int, default=75, help="Number of days of historical data")
args = parser.parse_args()
result = analyze_currency_pair(args.base.upper(), args.target.upper(), args.days)
print(json.dumps(result, indent=2))
Professional currency exchange rate analysis and forecasting tool. Provides technical analysis, market research, and predictive insights for currency pairs l...
---
name: currency-forecast
description: Professional currency exchange rate analysis and forecasting tool. Provides technical analysis, market research, and predictive insights for currency pairs like AUD/CNY, USD/CNY, etc.
metadata:
openclaw:
emoji: "💱"
requires:
env: []
---
# Currency Forecast Skill
Professional currency exchange rate analysis and forecasting using technical indicators, market research, and predictive modeling.
## Features
- **Technical Analysis**: Moving averages, trend lines, support/resistance levels
- **Market Research**: Real-time market factors and news analysis
- **Predictive Modeling**: Short, medium, and long-term forecasts
- **Multi-Currency Support**: Works with any currency pair supported by Frankfurter API
## Usage
### Basic Analysis
```
Analyze AUD/CNY exchange rate
```
### With Specific Parameters
```
Forecast USD/EUR with 30-day historical data
```
### Custom Threshold Alert
```
Check if AUD/CNY is below 4.82 and provide analysis
```
## Supported Currency Pairs
Any pair supported by Frankfurter API:
- AUD/CNY, AUD/USD, AUD/EUR
- USD/CNY, USD/EUR, USD/JPY
- EUR/CNY, EUR/USD
- And more...
## Data Sources
- **Frankfurter API**: Historical exchange rate data
- **Web Search**: Current market conditions and news
- **Technical Indicators**: Calculated locally
## Output Format
The skill generates a comprehensive report including:
1. Technical Analysis (MA, trends, volatility)
2. Fundamental Analysis (market factors)
3. Professional Forecasts (short/medium/long-term)
4. Trading Recommendations
## Example Output
```
## 📊 AUD/CNY Technical Analysis
Current Rate: 4.8583
7-day MA: 4.8826 (below)
Trend: Downward slope -0.000175
Support: 4.8376 | Resistance: 4.9100
## Market Factors
- RBA Rate: 4.10% (hawkish)
- PBOC Policy: Loose (3.0%)
- Iron Ore: $95/ton (rebounding)
## Forecast
Short-term: 4.82 ~ 4.92
Medium-term: 4.80 ~ 4.95
Long-term: Target 5.36 by end 2026
```
## Installation
No additional setup required. Uses built-in tools:
- `exec` for API calls
- `web_search` for market research
## Version
1.0.0
FILE:_meta.json
{
"name": "currency-forecast",
"version": "1.0.0",
"description": "Professional currency exchange rate analysis and forecasting tool",
"author": "clawclaw",
"tags": ["finance", "currency", "forecast", "exchange-rate", "trading"]
}
FILE:scripts/analyze.py
#!/usr/bin/env python3
"""
Currency Forecast Analysis Script
Performs technical analysis on exchange rate data
"""
import json
import sys
import math
from datetime import datetime, timedelta
def analyze_currency(base="AUD", target="CNY", days=75):
"""Analyze currency pair and return technical indicators"""
# This would normally fetch from API
# For now, return structure
return {
"base": base,
"target": target,
"current": None,
"ma7": None,
"ma14": None,
"ma30": None,
"trend": None,
"support": None,
"resistance": None
}
def calculate_ma(values, period):
"""Calculate moving average"""
if len(values) < period:
return sum(values) / len(values)
return sum(values[-period:]) / period
def calculate_trend(values, period=30):
"""Calculate linear regression slope"""
n = min(period, len(values))
x = list(range(n))
y = values[-n:]
x_mean = sum(x) / n
y_mean = sum(y) / n
numerator = sum((x[i]-x_mean)*(y[i]-y_mean) for i in range(n))
denominator = sum((x[i]-x_mean)**2 for i in range(n))
return numerator / denominator if denominator != 0 else 0
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--base", default="AUD")
parser.add_argument("--target", default="CNY")
parser.add_argument("--days", type=int, default=75)
args = parser.parse_args()
result = analyze_currency(args.base, args.target, args.days)
print(json.dumps(result, indent=2))
Detect YouTube Music links as MV (music video) or song (audio). Use when user shares YouTube Music links (music.youtube.com/watch?v=...) and you need to clas...
---
name: youtube-music-mv-detector
description: Detect YouTube Music links as MV (music video) or song (audio). Use when user shares YouTube Music links (music.youtube.com/watch?v=...) and you need to classify them as MV or audio track.
---
# YouTube Music MV Detector
## Detection Method
Use YouTube's free **oEmbed API** (no API key needed):
```
https://www.youtube.com/oembed?url={youtube_music_url}&format=json
```
## Classification Rules
After fetching oEmbed data, check:
### 🎬 MV (Music Video) - if ANY true:
1. Author does NOT contain " - Topic"
2. Title contains "(Official Music Video)", "(Music Video)", "MV", "(Official Video)"
### 🎵 Song (Audio) - if ANY true:
1. Author contains " - Topic" (YouTube Topic channels = audio tracks)
2. Title is just the song name without video indicators
## Examples
| URL | Title | Author | Result |
|-----|-------|--------|--------|
| music.youtube.com/watch?v=1rvqA8rMTu8 | Iceage - Star (Official Music Video) | Iceage | 🎬 MV |
| music.youtube.com/watch?v=Mh5Y8vusknE | Star | Iceage - Topic | 🎵 Song |
| music.youtube.com/watch?v=wtJcLWeY114 | amazarashi...ED | amazarashi Official YouTube Channel | 🎬 MV |
| music.youtube.com/watch?v=HPMwDxi9-e0 | Kisetsu Wa Tsugitsugi Shindeiku | amazarashi - Topic | 🎵 Song |
## Workflow
1. Extract video ID from YouTube Music URL
2. Call oEmbed API: `https://www.youtube.com/oembed?url=...&format=json`
3. Parse `title` and `author_name` from JSON response
4. Apply classification rules above
5. Return result with title and classification
## Notes
- YouTube Music URLs may include extra params like `&list=...` or `&si=...` - strip these before calling oEmbed
- oEmbed is free and doesn't require authentication
- This works for both regular YouTube and YouTube Music links