#!/usr/bin/env python3 """ Process stems_captions data and append to existing metadata files Following Victor's pattern from add_extreme.ipynb """ import os import json import random from tqdm import tqdm # ============================================================================= # CONFIGURATION CONSTANTS - Modify these as needed # ============================================================================= # Input data file INPUT_STEMS_CAPTIONS_FILE = "/home/vibert/shared/stems_captions_c686c20e.jsonl" # Output directories (using ~/tmp for intermediate processing) OUTPUT_DIR = "/home/vibert/tmp/stems_captions_output" METADATA_DIR = "/home/vibert/tmp/new_metadata" # Source metadata files (v3 -> v5) - will need to be copied from restricted dirs SOURCE_TRAIN_FILE = "/app2/suno/data/auk_v0/metas_v3_tr.jsonl" SOURCE_VAL_FILE = "/app2/suno/data/auk_v0/metas_v3_val.jsonl" # Target metadata files (v5) - will be created in ~/tmp TARGET_TRAIN_FILE = f"{METADATA_DIR}/metas_v5_tr.jsonl" TARGET_VAL_FILE = f"{METADATA_DIR}/metas_v5_val.jsonl" # Output split files OUTPUT_TRAIN_FILE = f"{OUTPUT_DIR}/train.jsonl" OUTPUT_VAL_FILE = f"{OUTPUT_DIR}/val.jsonl" # Split configuration TRAIN_VAL_SPLIT_RATIO = 0.95 # 95% train, 5% validation RANDOM_SEED = 42 # Processing options REQUIRE_BOTH_STEMS_AND_CAPTIONS = False # Only process records with both stems and stems_captions # ============================================================================= # MAIN PROCESSING FUNCTIONS # ============================================================================= def load_stems_captions_data(input_file): """Load and filter stems_captions data""" print(f"Loading stems_captions data from: {input_file}") if not os.path.exists(input_file): raise FileNotFoundError(f"Input file not found: {input_file}") records = [] total_count = 0 filtered_count = 0 with open(input_file, "r") as f: for line in tqdm(f, desc="Loading records"): total_count += 1 try: record = json.loads(line.strip()) # Filter records based on requirements has_stems = record.get("stems") and len(record["stems"]) > 0 has_stems_captions = record.get("stems_captions") and len(record["stems_captions"]) > 0 if REQUIRE_BOTH_STEMS_AND_CAPTIONS: if has_stems and has_stems_captions: records.append(record) filtered_count += 1 else: records.append(record) filtered_count += 1 except json.JSONDecodeError as e: print(f"Warning: Skipping malformed JSON line: {e}") continue print(f"Loaded {filtered_count} records out of {total_count} total records") if REQUIRE_BOTH_STEMS_AND_CAPTIONS: print(f"Filtered for records with both stems and stems_captions") return records def split_train_val(records, split_ratio=TRAIN_VAL_SPLIT_RATIO, random_seed=RANDOM_SEED): """Split records into train and validation sets""" print(f"Splitting {len(records)} records with ratio {split_ratio:.2f}") # Shuffle with fixed seed for reproducibility random.seed(random_seed) shuffled_records = records.copy() random.shuffle(shuffled_records) # Calculate split index split_index = int(len(shuffled_records) * split_ratio) train_records = shuffled_records[:split_index] val_records = shuffled_records[split_index:] print(f"Split into {len(train_records)} train and {len(val_records)} val records") return train_records, val_records def save_split_files(train_records, val_records, output_dir): """Save train and val splits to output directory""" print(f"Saving split files to: {output_dir}") # Create output directory os.makedirs(output_dir, exist_ok=True) # Save train file train_file = f"{output_dir}/train.jsonl" with open(train_file, "w") as f: for record in tqdm(train_records, desc="Writing train file"): f.write(json.dumps(record) + "\n") print(f"Saved {len(train_records)} train records to: {train_file}") # Save val file val_file = f"{output_dir}/val.jsonl" with open(val_file, "w") as f: for record in tqdm(val_records, desc="Writing val file"): f.write(json.dumps(record) + "\n") print(f"Saved {len(val_records)} val records to: {val_file}") return train_file, val_file def copy_existing_metadata_files(): """Copy v3 metadata files to v5 (following Victor's pattern)""" print("Copying existing v3 metadata files to v5...") # Check source files exist if not os.path.exists(SOURCE_TRAIN_FILE): raise FileNotFoundError(f"Source train file not found: {SOURCE_TRAIN_FILE}") if not os.path.exists(SOURCE_VAL_FILE): raise FileNotFoundError(f"Source val file not found: {SOURCE_VAL_FILE}") # Copy files import shutil shutil.copy2(SOURCE_TRAIN_FILE, TARGET_TRAIN_FILE) shutil.copy2(SOURCE_VAL_FILE, TARGET_VAL_FILE) print(f"Copied {SOURCE_TRAIN_FILE} -> {TARGET_TRAIN_FILE}") print(f"Copied {SOURCE_VAL_FILE} -> {TARGET_VAL_FILE}") def append_to_metadata_files(train_records, val_records): """Append new records to existing v5 metadata files""" print("Appending stems_captions records to v5 metadata files...") # Append to train file with open(TARGET_TRAIN_FILE, "a") as f: for record in tqdm(train_records, desc="Appending to train file"): f.write(json.dumps(record) + "\n") print(f"Appended {len(train_records)} records to: {TARGET_TRAIN_FILE}") # Append to val file with open(TARGET_VAL_FILE, "a") as f: for record in tqdm(val_records, desc="Appending to val file"): f.write(json.dumps(record) + "\n") print(f"Appended {len(val_records)} records to: {TARGET_VAL_FILE}") def print_summary_stats(records, train_records, val_records): """Print summary statistics""" print("\n" + "=" * 60) print("PROCESSING SUMMARY") print("=" * 60) # Overall stats print(f"Total input records: {len(records)}") print(f"Train records: {len(train_records)}") print(f"Val records: {len(val_records)}") print(f"Train/Val ratio: {len(train_records)/(len(train_records)+len(val_records)):.3f}") # Stems and captions stats records_with_stems = sum(1 for r in records if r.get("stems")) records_with_captions = sum(1 for r in records if r.get("stems_captions")) records_with_both = sum(1 for r in records if r.get("stems") and r.get("stems_captions")) print(f"\nContent statistics:") print(f"Records with stems: {records_with_stems}") print(f"Records with stems_captions: {records_with_captions}") print(f"Records with both: {records_with_both}") # Sample record info if records: sample_record = records[0] print(f"\nSample record ID: {sample_record.get('id', 'N/A')}") if sample_record.get("stems"): print(f"Sample stems: {list(sample_record['stems'].keys())}") if sample_record.get("stems_captions"): print(f"Sample stems_captions keys: {list(sample_record['stems_captions'].keys())}") print("=" * 60) def main(): """Main processing function""" print("šŸŽµ Stems Captions Processing Script") print("Following Victor's pattern from add_extreme.ipynb") print(f"Input: {INPUT_STEMS_CAPTIONS_FILE}") print(f"Output dir: {OUTPUT_DIR}") print(f"Metadata: v3 -> v5") print() try: # Step 1: Load stems_captions data records = load_stems_captions_data(INPUT_STEMS_CAPTIONS_FILE) if not records: print("āŒ No records to process!") return # Step 2: Split into train/val train_records, val_records = split_train_val(records) # Step 3: Save split files save_split_files(train_records, val_records, OUTPUT_DIR) # Step 4: Copy existing metadata files (v3 -> v5) copy_existing_metadata_files() # Step 5: Append to metadata files append_to_metadata_files(train_records, val_records) # Step 6: Print summary print_summary_stats(records, train_records, val_records) print("\nāœ… Processing completed successfully!") print(f"šŸ“ Split files: {OUTPUT_DIR}/") print(f"šŸ“ Updated metadata: {TARGET_TRAIN_FILE}, {TARGET_VAL_FILE}") except Exception as e: print(f"āŒ Error during processing: {e}") raise if __name__ == "__main__": main()