Fix duplicate video embed when youtube_url is empty string

- Add .trim() checks to all video source conditions
- Prevents rendering empty youtube_url as valid video
- Fixes double embed card display issue
- Update sidebar icon check to use optional chaining with trim

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
dwindown
2025-12-30 21:11:35 +07:00
parent 94aca1edec
commit a9ad84eb23
6 changed files with 34797 additions and 7 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,196 @@
# E-Course Curriculum: Cara Jual Jasa via Online
## How to Sell Services Online
**Source Video**: Live Zoom - Diskusi Cara Jual Jasa via Online
**Duration**: 2 hours 30 minutes
**Speaker**: Dwindi Ramadhana
**Language**: Indonesian
---
## Course Overview
This comprehensive course teaches you how to successfully sell services online, with a focus on web development and plugin installation services. Learn from real-world examples, case studies, and practical strategies for building a sustainable service business.
---
## Course Structure
### Chapter 1: Pengenalan & Temukan Pasar [00:00:00 - 00:15:00]
- [00:04:40 - 00:08:20] Pembukaan & Setup Diskusi
- Pengenalan topik cara jual jasa via online
- Format diskusi kasual dan interaktif
- Ruang lingkup: jasa pembuatan website & instalasi plugin
- [00:08:20 - 00:15:00] Cara Menemukan Market Anda
- Pentingnya bergabung dengan komunitas (Telegram, grup Facebook, forum)
- Kisah Abdurrahman bin Auf mencari pasar terlebih dahulu
- Mengamati pola komunitas dan masalah umum
- Jangan takut untuk bergabung dan mengamati
### Chapter 2: Discovery Masalah & Eksplorasi [00:15:00 - 00:30:00]
- [00:15:00 - 00:21:30] Identifikasi Masalah yang Sering Muncul
- Menemukan masalah yang sering disebutkan di komunitas
- Berpindah dari komunitas ke chat pribadi (Japri)
- Membangun jejaring melalui manfaat timbal balik
- Fase eksplorasi: trial and error di situs demo
- [00:21:30 - 00:30:00] Dasar Personal Branding
- Melakukan hal-hal bermanfaat untuk orang lain
- Pamerkan pengetahuan Anda (jangan disembunyikan)
- Memahami funnel AIDA (Awareness, Interest, Desire, Action)
- Cold market (grup) vs Warm market (chat pribadi) vs Hot market (klien)
### Chapter 3: Akuisisi & Manajemen Klien [00:30:00 - 00:45:00]
- [00:30:00 - 00:35:00] Psikologi Klien
- Aturan Klien: Semua orang ingin mengeluh
- Semua orang berpikir masalah mereka adalah yang terpenting
- Semua orang ingin didengar
- Empat kunci: Simak, Rekap, Offer, Deal
- [00:35:00 - 00:45:00] Strategi Harga
- Fokus pada effort Anda, bukan budget klien
- Studi kasus: Penetapan harga Elementor Pro
- Memahami nilai vs harga komoditas
- Contoh: Menjual lisensi Elementor Pro dengan harga Rp1.000/tahun
### Chapter 4: Deliver Layanan & Relasi Klien [00:45:00 - 01:00:00]
- [00:45:00 - 00:50:00] Prioritas Pelayanan Klien
- Prioritas 1: Respon cepat (jadilah responsif)
- Prioritas 2: Selesaikan proyek
- Prioritas 3: Eksplorasi hal baru
- Pentingnya keberadaan dan ketersediaan
- [00:50:00 - 01:00:00] Keberlanjutan Bisnis
- Mengapa harga rendah bermasalah (undercutting)
- Dua risiko utama: persaingan dan komoditisasi skill
- Transisi dari layanan dasar ke solusi spesialis
### Chapter 5: Sesi Tanya Jawab - Bagian 1 [01:00:00 - 01:15:00]
- [01:00:00 - 01:07:00] Implementasi Funnel
- Cara menerapkan AIDA dalam praktik
- Personal branding melalui partisipasi komunitas
- Menghadapi keberadaan komersial vs membantu di komunitas
- [01:07:00 - 01:15:00] Strategi Pemasaran
- Pemasaran afiliasi (model komisi 30%)
- Perbandingan pemasaran organik vs berbayar
- Contoh nyata keberhasilan afiliasi
### Chapter 6: Harga Lanjutan & Negosiasi [01:15:00 - 01:30:00]
- [01:15:00 - 01:22:00] Pendekatan Berorientasi Solusi
- Fokus pada solusi, bukan hanya menjual
- Studi kasus: Kustomisasi JetEngine
- Kapan merekomendasikan alternatif vs jasa Anda
- [01:22:00 - 01:30:00] Psikologi Harga
- Hindari harga terperinci (contoh 8 poin vs 4 poin)
- Memahami keberatan klien
- Jelas tentang ruang lingkup sebelum memberi harga
### Chapter 7: Manajemen Proyek & Kontrak [01:30:00 - 01:45:00]
- [01:30:00 - 01:37:00] Kejujuran dalam Relasi Klien
- Bersikap langsung tentang apa yang mungkin
- Contoh: Masalah kompatibilitas tema
- Menghindari janji yang berlebihan
- [01:37:00 - 01:45:00] Esensial Kontrak
- Kapan menggunakan kontrak (proyek besar)
- Format kontrak sederhana (1-2 halaman)
- Syarat dan ketentuan yang jelas
- Kebijakan uang muka
### Chapter 8: Pengembangan Diri & Etos Kerja [01:45:00 - 02:00:00]
- [01:45:00 - 01:53:00] Membangun Skill Anda
- Mindset workaholic vs kerja berkelanjutan
- Belajar dari rasa ingin tahu (contoh PSP)
- Eksplorasi dan pembelajaran terus-menerus
- [01:53:00 - 02:00:00] Alur Kerja Manajemen Proyek
- Strategi alokasi waktu
- Mengganggu saat menangani proyek
- Menyeimbangkan banyak klien
### Chapter 9: Sesi Tanya Jawab - Bagian 2 [02:00:00 - 02:15:00]
- [02:00:00 - 02:07:00] Lisensi Plugin & Etika
- Plugin GPL dari vendor (Envato, dll)
- Risiko plugin nulled/bajakan
- Mengunduh dari sumber resmi
- Edukasi klien tentang lisensi
- [02:07:00 - 02:15:00] Funnel AIDA Lebih Dalam
- Bagaimana eksplorasi memenuhi funnel
- Menciptakan kesadaran melalui kebermanfaatan
- Memindahkan prospek melalui tahapan
### Chapter 10: Tanya Jawab Final & Alat [02:15:00 - 02:30:18]
- [02:15:00 - 02:22:00] Pertanyaan Umum Layanan
- Etika pemasaran (hindari spam)
- Follow-up tanpa mengganggu
- Perjalanan belajar teknis
- [02:22:00 - 02:30:00] Alat yang Direkomendasikan
- Lingkungan pengembangan lokal (LocalWP)
- Rekomendasi hosting
- Sumber belajar
- Penutup dan perpisahan
---
## Key Takeaways
### Prinsip Utama
1. **Komunitas Dulu**: Bergabunglah dengan komunitas sebelum mencoba menjual apa pun
2. **Fokus pada Masalah**: Identifikasi masalah yang sering disebutkan
3. **Eksplorasi & Belajar**: Gunakan situs demo untuk trial and error
4. **Bangun Kepercayaan**: Bagikan pengetahuan dengan gratis, jangan sembunyikan
5. **Pahami Psikologi**: Klien ingin didengar dan dipahami
6. **Harga Berbasis Nilai**: Harga berdasarkan effort dan nilai Anda, bukan budget klien
7. **Jadilah Responsif**: Waktu respon cepat sangat krusial
8. **Berorientasi Solusi**: Fokus pada memecahkan masalah, bukan hanya menjual jasa
9. **Kejujuran Menang**: Bersikap langsung tentang keterbatasan dan kemungkinan
10. **Pembelajaran Terus-menerus**: Selalu eksplorasi alat dan teknik baru
### Studi Kasus Utama
- Penjualan Elementor Pro (100+ domain)
- Negosiasi harga dengan klien
- Membangun personal branding di komunitas
- Transisi dari freelancer ke pemilik produk
---
## Sumber Tambahan
### Tools yang Disebutkan
- LocalWP (local development)
- Elementor Pro
- JetEngine
- Sejoli Shortcode
### Komunitas yang Direkomendasikan
- Grup WordPress Indonesia
- Grup Facebook seputar web development
- Forum Telegram untuk diskusi teknis
---
## Catatan untuk Kursus
Kurikulum ini dirancang untuk memecah video 2.5 jam menjadi pelajaran yang mudah dicerna. Setiap chapter berfokus pada topik tertentu dengan durasi 15 menit, ideal untuk pembelajaran online. Materi mencakup contoh nyata, studi kasus, dan strategi praktis yang dapat langsung diterapkan.
---
**Dokumen ini dibuat berdasarkan transcript video asli**
**Total Durasi Video**: 2 jam 30 menit 18 detik
**Jumlah Chapter**: 10
**Jumlah Pelajaran**: 20

179
analyze_final.py Normal file
View File

@@ -0,0 +1,179 @@
#!/usr/bin/env python3
"""
Analyze video transcript to identify topics and create chapter divisions.
"""
import json
import re
from datetime import timedelta
def seconds_to_timestamp(seconds):
"""Convert seconds to readable timestamp."""
total_seconds = int(float(seconds))
hours, remainder = divmod(total_seconds, 3600)
minutes, seconds = divmod(remainder, 60)
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
def load_transcript(file_path):
"""Load JSON transcript file."""
with open(file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
return data
def extract_segments(data):
"""Extract transcript segments with timestamps."""
segments = []
for track in data[0]['tracks']:
if 'transcript' in track:
for item in track['transcript']:
start = float(item.get('start', 0))
dur = float(item.get('dur', 0))
text = item.get('text', '').strip()
if text and text != '\n':
segments.append({
'start': start,
'end': start + dur,
'text': text
})
# Sort by start time
segments.sort(key=lambda x: x['start'])
return segments
def extract_keywords(text):
"""Extract key topics from text."""
keywords = {
'Market & Community': ['market', 'pasar', 'grup', 'komunitas', 'telegram', 'facebook', 'forum'],
'Problem Finding': ['masalah', 'problem', 'kesulitan', 'permasalahan', 'error', 'bermasalah'],
'Exploration': ['explor', 'coba', 'trial', 'nyoba', 'eksplor', 'explore'],
'Personal Branding': ['branding', 'personal branding', 'show off', 'image', 'eksistensi'],
'AIDA/Funnel': ['aida', 'awareness', 'interest', 'desire', 'action', 'funel', 'funnel'],
'Trust': ['trust', 'percaya', 'kepercayaan'],
'Clients': ['klien', 'client', 'pelanggan', 'customer'],
'Pricing': ['harga', 'price', 'bayar', 'budget', 'rp', 'juta', 'ribu', 'dibayar'],
'Negotiation': ['tawar', 'negosiasi', 'deal'],
'Services': ['jasa', 'service', 'website', 'plugin', 'elementor', 'instal'],
'Cold/Warm/Hot Market': ['cold market', 'warm market', 'hot market', 'dingin', 'hangat'],
'Network': ['network', 'jaringan', 'koneksi', 'hubungan'],
'Sharing': ['sharing', 'share', 'bagi'],
'Products': ['produk', 'product', 'template'],
'Japri': ['japri', 'private', 'chat pribadi'],
}
found = []
text_lower = text.lower()
for topic, kw_list in keywords.items():
count = sum(1 for kw in kw_list if kw.lower() in text_lower)
if count > 0:
found.append((topic, count))
return sorted(found, key=lambda x: x[1], reverse=True)
def analyze_video():
"""Analyze the video transcript."""
file_path = "/Users/dwindown/CascadeProjects/MeetDwindiCom/access-hub/Live Zoom - Diskusi Cara Jual Jasa via Online.json"
print("="*80)
print("VIDEO TRANSCRIPT ANALYSIS")
print("Cara Jual Jasa via Online (How to Sell Services Online)")
print("="*80)
print()
data = load_transcript(file_path)
segments = extract_segments(data)
print(f"Total segments: {len(segments)}")
if not segments:
print("No segments found!")
return
total_duration = segments[-1]['end']
print(f"Total duration: {seconds_to_timestamp(total_duration)} ({total_duration/60:.1f} minutes)\n")
# Create time-based groups every 5 minutes
print("="*80)
print("CONTENT BREAKDOWN BY 5-MINUTE INTERVALS")
print("="*80)
print()
window = 300 # 5 minutes
current_time = 0
section_num = 1
while current_time < total_duration:
window_end = min(current_time + window, total_duration)
window_segments = [s for s in segments
if current_time <= s['start'] < window_end]
if window_segments:
# Combine text
combined_text = ' '.join([s['text'] for s in window_segments])
# Extract keywords
keywords = extract_keywords(combined_text)
print(f"Section {section_num}: {seconds_to_timestamp(current_time)} - {seconds_to_timestamp(window_end)}")
print("-" * 80)
# Show first 400 characters as preview
preview = combined_text[:400]
print(f"Content: {preview}...")
print()
if keywords:
print("Key topics detected:")
for topic, count in keywords[:7]:
print(f"{topic}: {count} mentions")
else:
print("Key topics: (transition/break section)")
print()
print()
section_num += 1
current_time = window_end
# Now create suggested chapters based on content analysis
print("\n")
print("="*80)
print("SUGGESTED CHAPTER STRUCTURE")
print("="*80)
print()
# Create larger 15-minute groups for chapter suggestions
chapter_window = 900 # 15 minutes
current_time = 0
chapter_num = 1
while current_time < total_duration:
chapter_end = min(current_time + chapter_window, total_duration)
chapter_segments = [s for s in segments
if current_time <= s['start'] < chapter_end]
if chapter_segments:
combined_text = ' '.join([s['text'] for s in chapter_segments])
keywords = extract_keywords(combined_text)
# Get top 3 keywords for chapter title
main_topics = [kw[0] for kw in keywords[:3]]
print(f"Chapter {chapter_num}: {seconds_to_timestamp(current_time)} - {seconds_to_timestamp(chapter_end)}")
print(f"Main topics: {', '.join(main_topics)}")
# Show first 300 chars
preview = combined_text[:300].replace('\n', ' ')
print(f"Preview: {preview}...")
print()
print()
chapter_num += 1
current_time = chapter_end
if __name__ == "__main__":
analyze_video()

167
analyze_transcript.py Normal file
View File

@@ -0,0 +1,167 @@
#!/usr/bin/env python3
"""
Analyze video transcript to identify topics and create chapter divisions.
"""
import json
import re
from datetime import timedelta
def seconds_to_timestamp(seconds):
"""Convert seconds to readable timestamp."""
td = timedelta(seconds=float(seconds))
hours, remainder = divmod(td.seconds, 3600)
minutes, seconds = divmod(remainder, 60)
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
def load_transcript(file_path):
"""Load JSON transcript file."""
with open(file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
return data
def extract_text_with_timestamps(data):
"""Extract text segments with timestamps."""
segments = []
for entry in data:
if 'events' in entry:
for event in entry['events']:
if 'segs' in event:
for seg in event['segs']:
if 'utf8' in seg:
segments.append({
'start': float(event.get('tStartMs', 0)) / 1000,
'text': seg['utf8']
})
return segments
def clean_text(text):
"""Clean transcript text."""
# Remove extra whitespace
text = ' '.join(text.split())
return text
def identify_keywords(text):
"""Identify important keywords in Indonesian business context."""
keywords = {
'market': ['market', 'pasar', 'grup', 'komunitas', 'community'],
'problem': ['masalah', 'problem', 'kesulitan', 'error', 'gagal'],
'branding': ['branding', 'personal branding', 'image', 'citra'],
'funnel': ['funel', 'funnel', 'awareness', 'desire', 'action'],
'client': ['klien', 'client', 'pelanggan', 'customer'],
'price': ['harga', 'price', 'bayar', 'paid', 'invoice'],
'negotiation': ['tawar', 'negosiasi', 'deal'],
'service': ['jasa', 'service', 'website', 'plugin'],
'exploration': ['explore', 'eksplor', 'coba', 'trial'],
'network': ['network', 'jaringan', 'koneksi'],
'sharing': ['sharing', 'share', 'bagi'],
'product': ['produk', 'product', 'template', 'plugin'],
'trust': ['trust', 'percaya', 'kepercayaan'],
'sales': ['jual', 'sales', 'closing'],
}
return keywords
def analyze_structure():
"""Analyze the transcript structure."""
file_path = "/Users/dwindown/CascadeProjects/MeetDwindiCom/access-hub/Live Zoom - Diskusi Cara Jual Jasa via Online.json"
print("Loading transcript...")
data = load_transcript(file_path)
print("Extracting segments...")
segments = extract_text_with_timestamps(data)
print(f"\nTotal segments: {len(segments)}")
# Get total duration
if segments:
total_duration = segments[-1]['start']
print(f"Total duration: {seconds_to_timestamp(total_duration)} ({total_duration/60:.1f} minutes)")
# Sample segments at different intervals
print("\n=== SAMPLING SEGMENTS AT KEY INTERVALS ===\n")
sample_points = [0, 300, 600, 900, 1200, 1500, 1800, 2100, 2400, 2700, 3000,
3300, 3600, 3900, 4200, 4500, 4800, 5100, 5400, 5700, 6000,
6300, 6600, 6900, 7200, 7500, 7800, 8100, 8400, 8700, 9000]
for i, target_time in enumerate(sample_points):
# Find closest segment
closest = None
min_diff = float('inf')
for seg in segments:
diff = abs(seg['start'] - target_time)
if diff < min_diff:
min_diff = diff
closest = seg
if closest and min_diff < 60: # Within 1 minute
text = clean_text(closest['text'])
if len(text) > 50: # Only meaningful segments
print(f"[{seconds_to_timestamp(closest['start'])}]")
print(f"{text[:200]}...")
print()
# Look for transition phrases
print("\n=== LOOKING FOR TRANSITION PHRASES ===\n")
transition_phrases = [
'oke',
'jadi',
'nah',
'kemudian',
'selanjutnya',
'setelah itu',
'sekarang',
'selanjutnya',
'lanjut',
'terus',
'setelah',
'nah sekarang',
'oke jadi',
'jadi sekarang',
'nah kalau',
]
# Look for sections mentioning main topics
print("\n=== SEARCHING FOR TOPIC MENTIONS ===\n")
topic_keywords = {
'Market/Community': ['market', 'pasar', 'grup', 'komunitas', 'community', 'telegram', 'facebook'],
'Problem Finding': ['masalah', 'problem', 'kesulitan', 'permasalahan'],
'Personal Branding': ['branding', 'personal branding', 'show off'],
'AIDA Funnel': ['aida', 'awareness', 'interest', 'desire', 'action', 'funel', 'funnel'],
'Getting Clients': ['klien', 'client', 'calon klien'],
'Pricing/Payment': ['harga', 'bayar', 'budget', 'invoice', 'price'],
'Negotiation': ['tawar', 'negosiasi', 'deal'],
'Trust Building': ['trust', 'percaya', 'kepercayaan'],
'Services/Products': ['jasa', 'service', 'produk', 'elementor', 'plugin', 'website'],
'Cold/Warm/Hot Market': ['cold market', 'warm market', 'hot market'],
}
# Find segments grouped by time periods
print("\n=== CONTENT BY TIME PERIODS (every 10 minutes) ===\n")
period = 600 # 10 minutes in seconds
current_period = 0
while current_period < total_duration:
period_end = current_period + period
period_segments = [s for s in segments if current_period <= s['start'] < period_end]
if period_segments:
# Combine text from this period
period_text = ' '.join([clean_text(s['text']) for s in period_segments[:20]]) # First 20 segments
period_text = period_text[:500] # First 500 chars
print(f"\n{'='*70}")
print(f"PERIOD: {seconds_to_timestamp(current_period)} - {seconds_to_timestamp(period_end)}")
print(f"{'='*70}")
print(period_text)
print()
current_period = period_end
if __name__ == "__main__":
analyze_structure()

162
analyze_transcript2.py Normal file
View File

@@ -0,0 +1,162 @@
#!/usr/bin/env python3
"""
Analyze video transcript to identify topics and create chapter divisions.
"""
import json
import re
from datetime import timedelta
def seconds_to_timestamp(seconds):
"""Convert seconds to readable timestamp."""
td = timedelta(seconds=float(seconds))
total_seconds = int(td.total_seconds())
hours, remainder = divmod(total_seconds, 3600)
minutes, seconds = divmod(remainder, 60)
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
def load_transcript(file_path):
"""Load JSON transcript file."""
with open(file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
return data
def extract_transcript_segments(data):
"""Extract all transcript segments with timestamps."""
segments = []
# The structure has a 'tracks' key
if 'tracks' in data[0]:
for track in data[0]['tracks']:
if track['kind'] == 'asr': # Automatic Speech Recognition
for event in track['events']:
start_time = event.get('tStartMs', 0) / 1000
duration = event.get('dDurationMs', 0) / 1000
# Extract text from segments
text_parts = []
if 'segs' in event:
for seg in event['segs']:
if 'utf8' in seg:
text_parts.append(seg['utf8'])
text = ' '.join(text_parts)
if text.strip():
segments.append({
'start': start_time,
'end': start_time + duration,
'text': text
})
return segments
def group_by_time_window(segments, window_seconds=600):
"""Group segments into time windows for analysis."""
groups = []
current_time = 0
while current_time < segments[-1]['end']:
window_end = current_time + window_seconds
window_segments = [s for s in segments
if current_time <= s['start'] < window_end]
if window_segments:
combined_text = ' '.join([s['text'] for s in window_segments])
groups.append({
'start': current_time,
'end': window_end,
'segments': window_segments,
'text': combined_text
})
current_time = window_end
return groups
def extract_keywords(text):
"""Extract key topics from text."""
keywords = {
'Market & Community': ['market', 'pasar', 'grup', 'komunitas', 'telegram', 'facebook', 'forum'],
'Problem Finding': ['masalah', 'problem', 'kesulitan', 'permasalahan', 'error'],
'Exploration': ['explor', 'coba', 'trial', 'nyoba', 'eksplor'],
'Personal Branding': ['branding', 'personal branding', 'show off', 'image'],
'AIDA/Funnel': ['aida', 'awareness', 'interest', 'desire', 'action', 'funel', 'funnel'],
'Trust': ['trust', 'percaya', 'kepercayaan'],
'Clients': ['klien', 'client', 'pelanggan', 'customer'],
'Pricing': ['harga', 'price', 'bayar', 'budget', 'rp', 'juta', 'ribu'],
'Negotiation': ['tawar', 'negosiasi', 'deal'],
'Services': ['jasa', 'service', 'website', 'plugin', 'elementor', 'instal'],
'Cold/Warm/Hot Market': ['cold', 'warm', 'hot', 'dingin', 'hangat'],
'Network': ['network', 'jaringan', 'koneksi', 'hubungan'],
'Sharing': ['sharing', 'share', 'bagi'],
'Products': ['produk', 'product', 'template'],
}
found = []
text_lower = text.lower()
for topic, kw_list in keywords.items():
count = sum(1 for kw in kw_list if kw.lower() in text_lower)
if count > 0:
found.append((topic, count))
return sorted(found, key=lambda x: x[1], reverse=True)
def identify_main_topics():
"""Identify main topics throughout the video."""
file_path = "/Users/dwindown/CascadeProjects/MeetDwindiCom/access-hub/Live Zoom - Diskusi Cara Jual Jasa via Online.json"
print("Loading transcript...")
data = load_transcript(file_path)
print("Extracting segments...")
segments = extract_transcript_segments(data)
print(f"Total segments: {len(segments)}")
if not segments:
print("No segments found!")
return
total_duration = segments[-1]['end']
print(f"Total duration: {seconds_to_timestamp(total_duration)} ({total_duration/60:.1f} minutes)")
print("\n" + "="*80)
print("ANALYZING CONTENT IN 10-MINUTE INTERVALS")
print("="*80 + "\n")
# Group by 10-minute windows
groups = group_by_time_window(segments, window_seconds=600)
for i, group in enumerate(groups, 1):
print(f"\n{'='*80}")
print(f"SECTION {i}: {seconds_to_timestamp(group['start'])} - {seconds_to_timestamp(group['end'])}")
print(f"{'='*80}")
# Get first 500 chars for preview
preview = group['text'][:500]
print(f"\nContent Preview:\n{preview}...")
# Extract keywords
keywords = extract_keywords(group['text'])
if keywords:
print(f"\nMain Topics:")
for topic, count in keywords[:5]:
print(f" - {topic}: {count} mentions")
print("\n" + "="*80)
print("DETAILED BREAKDOWN (5-minute intervals for first hour)")
print("="*80 + "\n")
# More detailed for first hour
detailed_groups = group_by_time_window(segments[:int(len(segments)*0.4)], window_seconds=300)
for i, group in enumerate(detailed_groups, 1):
print(f"\n--- {seconds_to_timestamp(group['start'])} - {seconds_to_timestamp(group['end'])} ---")
# Get text summary
text_summary = group['text'][:300]
print(f"{text_summary}...")
if __name__ == "__main__":
identify_main_topics()

View File

@@ -247,13 +247,13 @@ export default function Bootcamp() {
// Get video based on product's active source // Get video based on product's active source
const getVideoSource = () => { const getVideoSource = () => {
if (activeSource === 'youtube') { if (activeSource === 'youtube') {
if (lesson.youtube_url) { if (lesson.youtube_url && lesson.youtube_url.trim()) {
return { return {
type: 'youtube', type: 'youtube',
url: lesson.youtube_url, url: lesson.youtube_url,
embedUrl: getYouTubeEmbedUrl(lesson.youtube_url) embedUrl: getYouTubeEmbedUrl(lesson.youtube_url)
}; };
} else if (lesson.video_url) { } else if (lesson.video_url && lesson.video_url.trim()) {
// Fallback to old video_url for backward compatibility // Fallback to old video_url for backward compatibility
return { return {
type: 'youtube', type: 'youtube',
@@ -262,24 +262,24 @@ export default function Bootcamp() {
}; };
} else { } else {
// Fallback to embed if YouTube not available // Fallback to embed if YouTube not available
return lesson.embed_code ? { return lesson.embed_code && lesson.embed_code.trim() ? {
type: 'embed', type: 'embed',
html: lesson.embed_code html: lesson.embed_code
} : null; } : null;
} }
} else { } else {
if (lesson.embed_code) { if (lesson.embed_code && lesson.embed_code.trim()) {
return { return {
type: 'embed', type: 'embed',
html: lesson.embed_code html: lesson.embed_code
}; };
} else { } else {
// Fallback to YouTube if embed not available // Fallback to YouTube if embed not available
return lesson.youtube_url ? { return lesson.youtube_url && lesson.youtube_url.trim() ? {
type: 'youtube', type: 'youtube',
url: lesson.youtube_url, url: lesson.youtube_url,
embedUrl: getYouTubeEmbedUrl(lesson.youtube_url) embedUrl: getYouTubeEmbedUrl(lesson.youtube_url)
} : lesson.video_url ? { } : lesson.video_url && lesson.video_url.trim() ? {
type: 'youtube', type: 'youtube',
url: lesson.video_url, url: lesson.video_url,
embedUrl: getYouTubeEmbedUrl(lesson.video_url) embedUrl: getYouTubeEmbedUrl(lesson.video_url)
@@ -360,7 +360,7 @@ export default function Bootcamp() {
> >
{isCompleted ? ( {isCompleted ? (
<Check className="w-4 h-4 shrink-0 text-accent" /> <Check className="w-4 h-4 shrink-0 text-accent" />
) : lesson.video_url || lesson.youtube_url || lesson.embed_code ? ( ) : (lesson.video_url?.trim() || lesson.youtube_url?.trim() || lesson.embed_code?.trim()) ? (
<Play className="w-4 h-4 shrink-0" /> <Play className="w-4 h-4 shrink-0" />
) : ( ) : (
<BookOpen className="w-4 h-4 shrink-0" /> <BookOpen className="w-4 h-4 shrink-0" />