{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "d31d8850",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "os.environ[\"CUDA_VISIBLE_DEVICES\"] = \"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "7cd4c0dd",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Warning : `load_model` does not return WordVectorModel or SupervisedModel any more, but a `FastText` object which is very similar.\n"
     ]
    }
   ],
   "source": [
    "import tqdm\n",
    "import math\n",
    "import torch\n",
    "import random\n",
    "import funcy\n",
    "import copy\n",
    "import gc\n",
    "import re\n",
    "import json\n",
    "import tempfile\n",
    "import collections\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "import fasttext\n",
    "from joblib import Parallel, delayed\n",
    "\n",
    "from suno_utils.audio import Audio\n",
    "from suno_utils.utils.text import write_jsonl, read_jsonl, write_json, read_json, normalize_whitespace\n",
    "from suno_utils.utils.s3 import read_from_s3, check_s3_file_exists, open_from_s3\n",
    "from suno_utils.utils.tokenizers import tokenize\n",
    "from suno_utils.harvest.youtube.constants.text_lang import BASE_TO_FASTTEXT_REMAP\n",
    "\n",
    "LANG_ID_MODEL_FP = \"s3://suno-data/georg/trained_models/chirp_v1/lid.176.bin\"\n",
    "text_lang_model = read_from_s3(LANG_ID_MODEL_FP, read_f=fasttext.load_model)\n",
    "\n",
    "def _get_text_lang(text):\n",
    "    \"\"\"get probability of input language for text\"\"\"\n",
    "    text = text.replace(\"’\", \"'\").lower()\n",
    "    text = re.sub(r\"\\[.+?\\]\", \" \", text)\n",
    "    text = normalize_whitespace(text)\n",
    "    out = text_lang_model.predict(text, k=1)\n",
    "    lang_str = out[0][0]\n",
    "    p_lang = out[1][0]\n",
    "    lang = lang_str.split(\"__\")[-1]\n",
    "    lang = BASE_TO_FASTTEXT_REMAP.get(lang, lang)\n",
    "#     if p_lang >= 0.8:\n",
    "#         return lang\n",
    "    return p_lang, lang\n",
    "\n",
    "\n",
    "TMP_DIR = os.path.join(os.getcwd(), \"tmp\")\n",
    "os.makedirs(TMP_DIR, exist_ok=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9e30f743",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cb688ac7",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "d9d50793",
   "metadata": {},
   "source": [
    "## genius_hq"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "8f428b8f",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n"
     ]
    }
   ],
   "source": [
    "from Bio import pairwise2\n",
    "\n",
    "from suno_utils.utils.metrics import get_cer\n",
    "from suno_utils.tasks.hoot import parse_lyrics, legacy_parse_lyrics, EMBEDDING_RATE as HOOT_EMBEDDING_RATE\n",
    "from suno_utils.utils.lyrics import remove_speakers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "54a969c0",
   "metadata": {},
   "outputs": [],
   "source": [
    "# base_metas = read_from_s3(\"s3://suno-data/datasets/bundles/v1/genius_hq/metas.jsonl\", read_f=read_jsonl)\n",
    "# raw_lyrics_map = {m[\"id\"]: m[\"lyrics\"] for m in base_metas}\n",
    "# del base_metas\n",
    "# gc.collect();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "2cd505d5",
   "metadata": {},
   "outputs": [],
   "source": [
    "base_metas = read_from_s3(\"s3://suno-data/datasets/bundles/v1/genius_hq/metas_plus.jsonl\", read_f=read_jsonl)\n",
    "# TODO: original IDs are already unique here, but did we resolve correctly by highest genius views?\n",
    "#   could be a way to filter translations that don't contain the word 'translation'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "6ac295a0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2090009 clips\n",
      "858539 filtered clips\n",
      "52575 hours\n"
     ]
    }
   ],
   "source": [
    "print(len(base_metas), \"clips\")\n",
    "metas = []\n",
    "for m in base_metas:\n",
    "    if (\n",
    "        m[\"genius_views\"] < 20 or \n",
    "        m[\"youtube_views\"] < 100 or \n",
    "        m[\"lang\"] is None or \n",
    "        m[\"lang\"].lower()[:2] != \"en\" or \n",
    "        \"translation\" in m[\"genius_slug\"].lower() or\n",
    "        m[\"duration_s\"] < 2*60 or\n",
    "        m[\"duration_s\"] > 8*60 or\n",
    "        len(m[\"lyrics\"]) < 512 or\n",
    "        len(m[\"lyrics\"]) > 6144 or\n",
    "        _get_text_lang(m[\"lyrics\"])[1] != \"en\"\n",
    "    ):\n",
    "        continue\n",
    "    new_m = {\n",
    "        \"id\": m[\"id\"],\n",
    "        \"lyrics\": m[\"lyrics\"].strip(),\n",
    "        \"duration_s\": m[\"duration_s\"],\n",
    "        \"lang\": m[\"lang\"],\n",
    "        \"audio_filepath\": m[\"audio_filepath\"],\n",
    "        \"original_id\": m[\"original_id\"],\n",
    "        \"genius_slug\": m[\"genius_slug\"],\n",
    "    }\n",
    "    metas.append(new_m)\n",
    "print(len(metas), \"filtered clips\")\n",
    "print(round(sum(m[\"duration_s\"] for m in metas)/60/60), \"hours\")\n",
    "metas_map = {m[\"id\"]: m for m in metas}\n",
    "# 2090009 clips\n",
    "# 858539 filtered clips\n",
    "# 52575 hours of english"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "29cb5d64",
   "metadata": {},
   "outputs": [],
   "source": [
    "del base_metas, metas\n",
    "gc.collect();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1cc806d",
   "metadata": {},
   "source": [
    "#### do alignment (will be cache in TMP_DIR)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "d350830c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# we need tokenzier to decode greedy hoot preds\n",
    "#  (use a dict instead of model to allow multiprocessing)\n",
    "with open(\"/home/georg/models/hoot_vocab.json\") as f:\n",
    "    vocab = json.load(f)\n",
    "inv_vocab = {v: k for k, v in vocab.items()}\n",
    "def decode_token_ids(token_ids):\n",
    "    prev_idx = None\n",
    "    tokens = []\n",
    "    for idx in token_ids:\n",
    "        idx = int(idx)\n",
    "        if idx == prev_idx:\n",
    "            continue\n",
    "        if idx != len(vocab):\n",
    "            tokens.append(inv_vocab[idx])\n",
    "        prev_idx = idx\n",
    "    return \"\".join(tokens).replace(\"▁\", \" \").strip()\n",
    "\n",
    "MIN_SEGMENT_DURATION_S = 5 # TODO: should this be larger??\n",
    "MAX_SEGMENT_DURATION_S = 2*60\n",
    "MAX_PAD_BOUNDARY_S = 8\n",
    "MAX_PAD_S = 2\n",
    "EARLY_VERSE_TERMINATE_CHANCE = 0.9"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "a273ecbd",
   "metadata": {},
   "outputs": [],
   "source": [
    "def _norm_lyrics_to_plain(text):\n",
    "    text = text.lower()\n",
    "    text = re.sub(r\"\\[.*?\\]\", \" \", text)\n",
    "    text = re.sub(r\"[^a-z\\'\\s]\", \" \", text)\n",
    "    return normalize_whitespace(text)\n",
    "\n",
    "def _get_alignable_tokens(text):\n",
    "    # replace all metatags so we don't tokenize within\n",
    "    tag_repl_map = {}\n",
    "    safe_text = \"\"\n",
    "    offs = 0\n",
    "    for m in re.finditer(r\"\\[.*?\\]\", text):\n",
    "        if m.start() - offs > 0:\n",
    "            safe_text += text[offs:m.start()]\n",
    "        uid = str(random.randint(9999999, 99999999))\n",
    "        safe_text += uid\n",
    "        tag_repl_map[uid] = m.group()\n",
    "        offs = m.end()\n",
    "    if len(text) - offs > 0:\n",
    "        safe_text += text[offs:]\n",
    "    # get tokens that we want to align\n",
    "    safe_words = [s for s in re.split(\"([A-Za-z\\']+)\", safe_text) if len(s) > 0]\n",
    "    words_to_align = [\n",
    "        (n, word.lower()) \n",
    "        for n, word in enumerate(safe_words) \n",
    "        if re.match(r\"^[a-z\\']+$\", word.lower())\n",
    "    ]\n",
    "    # add back in metatags\n",
    "    words = []\n",
    "    for safe_word in safe_words:\n",
    "        word = safe_word\n",
    "        for k, v in tag_repl_map.items():\n",
    "            if k in safe_word:\n",
    "                word = word.replace(k, v)\n",
    "        words.append(word)\n",
    "    return words, words_to_align\n",
    "\n",
    "def _assign_word_timings(words_to_align, aligned_words):\n",
    "    flat_words_a = [w for _, w in words_to_align]\n",
    "    flat_words_b = [m[\"text\"] for m in aligned_words]\n",
    "    assert(len(set(flat_words_a) & set(flat_words_b)) >= 0.75 * len(set(flat_words_a) | set(flat_words_b)))\n",
    "    alignment = pairwise2.align.globalxx(\n",
    "        flat_words_a, flat_words_b, gap_char=[\"*\"]\n",
    "    )[0]\n",
    "    assert(np.mean([int(e != \"*\") for e in alignment.seqA]) >= 0.75)\n",
    "    assert(np.mean([int(e != \"*\") for e in alignment.seqB]) >= 0.75)\n",
    "    # assign align info to tokens in ground truth\n",
    "    flat_words_a_timed = []\n",
    "    idx_a = 0\n",
    "    idx_b = 0\n",
    "    for a, b in zip(alignment.seqA, alignment.seqB):\n",
    "        if a != \"*\":\n",
    "            start_s = None\n",
    "            end_s = None\n",
    "            p_align = None\n",
    "            assert(a == words_to_align[idx_a][1])\n",
    "            idx = words_to_align[idx_a][0]\n",
    "            if b != \"*\":\n",
    "                start_s = aligned_words[idx_b][\"start_s\"]\n",
    "                end_s = aligned_words[idx_b][\"end_s\"]\n",
    "                p_align = aligned_words[idx_b][\"p_align\"]\n",
    "            flat_words_a_timed.append({\n",
    "                \"word\": a,\n",
    "                \"start_s\": round(start_s, 2),\n",
    "                \"end_s\": round(end_s, 2),\n",
    "                \"p_align\": round(p_align, 3),\n",
    "                \"orig_idx\": idx,\n",
    "            })\n",
    "            idx_a += 1\n",
    "        if b != \"*\":\n",
    "            idx_b += 1\n",
    "    return flat_words_a_timed\n",
    "\n",
    "def _merge_metatags_whitespace(all_words_timed):\n",
    "    # generally forward merge cause of \"[Chorus]\\nFirst line.\"\n",
    "    #   but also maybe backward with first part \")\\n\\n[Verse]\\n\"\n",
    "    merged_words_timed = []\n",
    "    n = 0\n",
    "    while True:\n",
    "        if n == len(all_words_timed):\n",
    "            break\n",
    "        if \"success\" not in all_words_timed[n]:\n",
    "            # we have whitespace so lets see if we can merge a prefix to previous\n",
    "            word = all_words_timed[n][\"word\"]\n",
    "            if \"\\n\" in word and word.index(\"\\n\") > 0 and n > 0 and merged_words_timed[-1].get(\"success\", False):\n",
    "                merged_words_timed[-1][\"word\"] += word[:word.index(\"\\n\")]\n",
    "                word = word[word.index(\"\\n\"):]\n",
    "            elif n == len(all_words_timed) - 1:\n",
    "                merged_words_timed[-1][\"word\"] += word\n",
    "                n += 1\n",
    "                continue\n",
    "            # lets see if we can merge forward  \n",
    "            if len(all_words_timed)-1 > n and all_words_timed[n+1].get(\"success\", False):\n",
    "                # merge forward\n",
    "                new_m = {\n",
    "                    \"word\": word + all_words_timed[n+1][\"word\"],\n",
    "                    \"success\": all_words_timed[n+1][\"success\"],\n",
    "                    \"start_s\": all_words_timed[n+1][\"start_s\"],\n",
    "                    \"end_s\": all_words_timed[n+1][\"end_s\"],\n",
    "                    \"p_align\": all_words_timed[n+1][\"p_align\"],\n",
    "                }\n",
    "                merged_words_timed.append(new_m)\n",
    "                n += 2\n",
    "                continue\n",
    "            else:\n",
    "                merged_words_timed.append({\"word\": word})\n",
    "                n += 1\n",
    "                continue\n",
    "        else:\n",
    "            merged_words_timed.append(copy.deepcopy(all_words_timed[n]))\n",
    "            n += 1\n",
    "            continue\n",
    "    return merged_words_timed\n",
    "\n",
    "# pad words\n",
    "def _pad_words(merged_words_timed, duration_s):\n",
    "    # calc max duration\n",
    "    duration_s = max(duration_s, max([m[\"end_s\"] for m in merged_words_timed if m.get(\"success\", False)]))\n",
    "    # make copy so we can modify in place\n",
    "    padded_words_timed = copy.deepcopy(merged_words_timed)\n",
    "    for n, m in enumerate(padded_words_timed):\n",
    "        if not m.get(\"success\", False):\n",
    "            continue\n",
    "        # do left padding\n",
    "        if n == 0:\n",
    "            if m[\"start_s\"] <= MAX_PAD_BOUNDARY_S:\n",
    "                m[\"start_s\"] = 0\n",
    "            else:\n",
    "                m[\"start_s\"] = m[\"start_s\"] - MAX_PAD_S\n",
    "        else:\n",
    "            mm = merged_words_timed[n-1]\n",
    "            if mm.get(\"success\", False):\n",
    "                if m[\"start_s\"] - mm[\"end_s\"] > 0.01:\n",
    "                    m[\"start_s\"] -= min(MAX_PAD_S, (m[\"start_s\"] - mm[\"end_s\"]) / 2)\n",
    "        m[\"start_s\"] = max(0, m[\"start_s\"])\n",
    "        m[\"start_s\"] = min(duration_s, m[\"start_s\"])\n",
    "        # do right padding     \n",
    "        if n == len(merged_words_timed) - 1:\n",
    "            if duration_s - m[\"end_s\"] <= MAX_PAD_BOUNDARY_S:\n",
    "                m[\"end_s\"] = duration_s\n",
    "            else:\n",
    "                m[\"end_s\"] = m[\"end_s\"] + MAX_PAD_S\n",
    "        else:\n",
    "            mm = merged_words_timed[n+1]\n",
    "            if mm.get(\"success\", False):\n",
    "                if mm[\"start_s\"] - m[\"end_s\"] > 0.01:\n",
    "                    m[\"end_s\"] += min(MAX_PAD_S, (mm[\"start_s\"] - m[\"end_s\"]) / 2)\n",
    "        m[\"end_s\"] = max(0, m[\"end_s\"])\n",
    "        m[\"end_s\"] = min(duration_s, m[\"end_s\"])\n",
    "        m[\"start_s\"] = round(m[\"start_s\"], 2)\n",
    "        m[\"end_s\"] = round(m[\"end_s\"], 2)\n",
    "    return padded_words_timed\n",
    "\n",
    "def _merge_into_lines(padded_words_timed):\n",
    "    # first reconsitute lines so we don't break mid-line\n",
    "    lines = []\n",
    "    tmp_line = []\n",
    "    for m in padded_words_timed:\n",
    "        if \"\\n\" in m[\"word\"] and len(tmp_line) > 0:\n",
    "            lines.append(tmp_line)\n",
    "            tmp_line = []\n",
    "        tmp_line.append(m)\n",
    "    if len(tmp_line) > 0:\n",
    "        lines.append(tmp_line)\n",
    "    # simplify and classify as good breakpoints\n",
    "    annotated_lines = []\n",
    "    for line in lines:\n",
    "        assert(len(line) > 0)\n",
    "        text = \"\".join(m[\"word\"] for m in line)\n",
    "        start_s = None\n",
    "        end_s = None\n",
    "        l = [m[\"start_s\"] for m in line[:2] if \"start_s\" in m and m[\"start_s\"] is not None]\n",
    "        if len(l) > 0:\n",
    "            start_s = l[0]\n",
    "        l = [m[\"end_s\"] for m in line[-2:] if \"end_s\" in m and m[\"end_s\"] is not None]\n",
    "        if len(l) > 0:\n",
    "            end_s = l[-1]\n",
    "        success = (\n",
    "            start_s is not None and \n",
    "            end_s is not None and\n",
    "            np.mean([m.get(\"success\", False) for m in line]) >= 0.75\n",
    "            # do mean of success > 0.75\n",
    "        )\n",
    "        new_m = {\n",
    "            \"text\": text,\n",
    "            \"success\": success,\n",
    "        }\n",
    "        if success:\n",
    "            new_m[\"start_s\"] = start_s\n",
    "            new_m[\"end_s\"] = end_s\n",
    "        annotated_lines.append(new_m)\n",
    "    return annotated_lines\n",
    "\n",
    "def _collect_valid_segment_from_lines(annotated_lines):\n",
    "    # group lines into segments\n",
    "    valid_break_lines = [(n, line) for n, line in enumerate(annotated_lines) if line[\"success\"]]    \n",
    "    segments = []\n",
    "    tmp_segment = []\n",
    "    for idx, line in valid_break_lines:\n",
    "        \n",
    "        try:\n",
    "            # check if we wanna evict\n",
    "            if (\n",
    "                len(tmp_segment) > 0 and \n",
    "                (\n",
    "                    (\n",
    "                        (\n",
    "                            re.match(r\"^\\s*\\n\\s*\\n\\s*\", line[\"text\"]) or\n",
    "                            re.match(r\"^\\s*\\[.*\\]\\s*$\", line[\"text\"])\n",
    "                        ) and \n",
    "                        random.random() >= EARLY_VERSE_TERMINATE_CHANCE and\n",
    "                        len(normalize_whitespace(line[\"text\"])) >= 25\n",
    "                    ) or \n",
    "                    line[\"end_s\"] - tmp_segment[0][-1][\"start_s\"] > MAX_SEGMENT_DURATION_S\n",
    "                )\n",
    "            ):\n",
    "                segments.append(tmp_segment)\n",
    "                tmp_segment = []\n",
    "        except:\n",
    "            import pdb; pdb.set_trace()\n",
    "            \n",
    "            \n",
    "        tmp_segment.append((idx, line))\n",
    "    if len(tmp_segment) > 0:\n",
    "        segments.append(tmp_segment)\n",
    "    # splice back in non-success lines\n",
    "    all_segments = []\n",
    "    offs = 0\n",
    "    for seg in segments:\n",
    "        first_idx = seg[0][0]\n",
    "        last_idx = seg[-1][0]\n",
    "        if first_idx > offs:\n",
    "            all_segments.append(annotated_lines[offs:first_idx])\n",
    "        all_segments.append(annotated_lines[first_idx:last_idx+1])\n",
    "        offs = last_idx+1\n",
    "    last_idx = segments[-1][-1][0] + 1\n",
    "    if last_idx < len(annotated_lines):\n",
    "        all_segments.append(annotated_lines[last_idx:len(annotated_lines)])\n",
    "    # merge and filter segment lines\n",
    "    # TODO: ignoring instrumental stuff here (add as empty string in future)\n",
    "    merged_segments = []\n",
    "    for lines in all_segments:\n",
    "        assert(len(lines) > 0)\n",
    "        text = \"\".join(m[\"text\"] for m in lines)\n",
    "        start_s = lines[0].get(\"start_s\")\n",
    "        end_s = lines[-1].get(\"end_s\")\n",
    "        success = (\n",
    "            start_s is not None and \n",
    "            end_s is not None and\n",
    "            np.mean([m[\"success\"] for m in lines]) >= 0.75\n",
    "            # do mean of success > 0.75\n",
    "        )\n",
    "        if not success or end_s - start_s > MAX_SEGMENT_DURATION_S or end_s - start_s < MIN_SEGMENT_DURATION_S:\n",
    "            continue\n",
    "        merged_segments.append({\n",
    "            \"text\": text,\n",
    "            \"start_s\": start_s,\n",
    "            \"end_s\": end_s,\n",
    "        })\n",
    "    return merged_segments\n",
    "\n",
    "def _verify_durations(segments, duration_s):\n",
    "    assert(\n",
    "        duration_s + 0.01 > \n",
    "        np.sum([e[\"end_s\"] - e[\"start_s\"] for e in segments if e.get(\"start_s\") is not None])\n",
    "    )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "3cd6dcbc",
   "metadata": {},
   "outputs": [],
   "source": [
    "# arr[0]: probs.argmax(axis=-1),  # decoded ID\n",
    "# arr[1]: probs.max(axis=-1),  # prob decoded ID\n",
    "# arr[2]: probs[:,-1],  # prob blank\n",
    "\n",
    "def _get_aligned_lyrics(align_metas, align_archive, silent=False):\n",
    "    aligned_lyrics_map = {}\n",
    "    tot_duration_s = 0\n",
    "    tot_duration_silence_s = 0\n",
    "    for m in tqdm.tqdm(align_metas, disable=silent):\n",
    "        arr = align_archive[m[\"id\"]]\n",
    "        if m[\"id\"] not in metas_map:\n",
    "            continue\n",
    "        meta = metas_map[m[\"id\"]]\n",
    "        if meta[\"lang\"][:2].lower() != \"en\":\n",
    "            continue\n",
    "        if \"alignment\" not in m:\n",
    "            continue\n",
    "        aligned_words = m[\"alignment\"]\n",
    "        if len(aligned_words) <= 50:\n",
    "            continue\n",
    "        duration_s = round(arr.shape[-1] / HOOT_EMBEDDING_RATE, 2)\n",
    "        if duration_s < 30 or duration_s > 10*60 or np.abs(meta[\"duration_s\"] - duration_s) > 5:\n",
    "            continue\n",
    "        # check language and alignment quality\n",
    "        p_silence = round(arr[2].mean(), 3)\n",
    "        weights = 1 - arr[2]\n",
    "        p_en = round((arr[1]*weights).sum() / weights.sum(), 3)\n",
    "        p_align = np.mean([mm[\"p_align\"] for mm in aligned_words])\n",
    "        if p_en < 0.65 or p_align < 0.1:\n",
    "            continue\n",
    "        true_text_norm = _norm_lyrics_to_plain(meta[\"lyrics\"])\n",
    "        greedy_preds = decode_token_ids(arr[0])\n",
    "        cer_val = round(get_cer(true_text_norm, greedy_preds), 3)\n",
    "        if cer_val > 0.75:\n",
    "            continue\n",
    "        # do alignment\n",
    "        all_words, words_to_align = _get_alignable_tokens(meta[\"lyrics\"])\n",
    "        try:\n",
    "            timed_words = _assign_word_timings(words_to_align, aligned_words)\n",
    "        except:\n",
    "            # sometimes error on np.mean with None\n",
    "            continue\n",
    "        # add into full words\n",
    "        timed_words_map = {m[\"orig_idx\"]: m for m in timed_words}\n",
    "        all_words_timed = []\n",
    "        for n, w in enumerate(all_words):\n",
    "            if n not in timed_words_map:\n",
    "                all_words_timed.append({\"word\": w})\n",
    "                continue\n",
    "            assert(w.lower() == timed_words_map[n][\"word\"]) \n",
    "            new_m = {\n",
    "                \"word\": w,\n",
    "                \"success\": timed_words_map[n][\"start_s\"] is not None,\n",
    "                \"start_s\": timed_words_map[n][\"start_s\"],\n",
    "                \"end_s\": timed_words_map[n][\"end_s\"],\n",
    "                \"p_align\": timed_words_map[n][\"p_align\"],\n",
    "            }\n",
    "            all_words_timed.append(new_m)\n",
    "        # merge meta tags forward for timing (eg [Verse 1])\n",
    "        merged_words_timed = _merge_metatags_whitespace(all_words_timed)\n",
    "        try:\n",
    "            _verify_durations(merged_words_timed, duration_s)\n",
    "        except:\n",
    "            # this shouldn't happen but is super rare so fine for noq\n",
    "            continue\n",
    "        # pad words if possible\n",
    "        padded_words_timed = _pad_words(merged_words_timed, duration_s)\n",
    "        try:\n",
    "            _verify_durations(padded_words_timed, duration_s)\n",
    "        except:\n",
    "            # this shouldn't happen but is super rare so fine for noq\n",
    "            continue\n",
    "        # merge into lines\n",
    "        annotated_lines = _merge_into_lines(padded_words_timed)\n",
    "        try:\n",
    "            _verify_durations(annotated_lines, duration_s)\n",
    "        except:\n",
    "            # this shouldn't happen but is super rare so fine for noq\n",
    "            continue\n",
    "        # merge into segments and filer\n",
    "        valid_segments = _collect_valid_segment_from_lines(annotated_lines)\n",
    "        try:\n",
    "            _verify_durations(valid_segments, duration_s)\n",
    "        except:\n",
    "            # this shouldn't happen but is super rare so fine for noq\n",
    "            continue\n",
    "        # if less than hald the string aligned maybe skip as well\n",
    "        retained_text = \"\".join(m[\"text\"] for m in valid_segments)\n",
    "        if len(retained_text) / len(meta[\"lyrics\"]) < 0.5:\n",
    "            continue\n",
    "        assert(all(\n",
    "            MIN_SEGMENT_DURATION_S - 0.1 <= m[\"end_s\"] - m[\"start_s\"] <= MAX_SEGMENT_DURATION_S + 0.1 \n",
    "            for m in valid_segments\n",
    "        ))\n",
    "        aligned_lyrics_map[m[\"id\"]] = valid_segments\n",
    "    return aligned_lyrics_map\n",
    "\n",
    "def collect_lyrics(work_item):\n",
    "    archive_nr = work_item\n",
    "    base_url = \"s3://suno-data/datasets/bundles/v1/genius_hq\"\n",
    "    align_metas = read_from_s3(\n",
    "        os.path.join(base_url, \"hoot_logits\", \"metas\", f\"part_{archive_nr}.jsonl\"), read_f=read_jsonl\n",
    "    )\n",
    "    align_archive = read_from_s3(\n",
    "        os.path.join(base_url, \"hoot_logits\", f\"part_{archive_nr}.npz\"), read_f=np.load\n",
    "    )\n",
    "    aligned_lyrics_map = _get_aligned_lyrics(align_metas, align_archive, silent=True)\n",
    "    return [(k, v) for k, v in aligned_lyrics_map.items()]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "04a716c4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "253/1000, retained\n"
     ]
    }
   ],
   "source": [
    "# test output\n",
    "out = collect_lyrics(0)\n",
    "print(f\"{len(out)}/1000, retained\")\n",
    "# 255/1000, retained\n",
    "# should be ~500k songs retained\n",
    "# should be ~30k hours max (less cause segments)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "93991c09",
   "metadata": {},
   "outputs": [],
   "source": [
    "# # plot duration hist and duration retained\n",
    "# l = []\n",
    "# for _, m in out:\n",
    "#     for mm in m:\n",
    "#         l.append((mm[\"end_s\"] - mm[\"start_s\"]))\n",
    "# print(round(np.sum(l)/60/60, 1), \"hours retained\")\n",
    "# pd.Series(l).hist(bins=50);\n",
    "# # 12.9 hours retained"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "5f106b58",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# # listen to some\n",
    "# k, segments = random.choice(out)\n",
    "# meta = metas_map[k]\n",
    "# audio = Audio.from_s3(meta[\"audio_filepath\"])\n",
    "# print(f\"{meta['id']}\")\n",
    "# print(meta[\"genius_slug\"])\n",
    "# print(\"-\"*10)\n",
    "# for s in segments:\n",
    "#     print(s[\"text\"])\n",
    "#     audio.get_segment(s[\"start_s\"], s[\"end_s\"]).play()\n",
    "#     print(\"-\"*10)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "6f809ee6",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "  0%|                                                                        | 0/44 [00:00<?, ?it/s]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "  9%|█████▌                                                       | 4/44 [20:59<3:24:26, 306.67s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/joblib/externals/loky/process_executor.py:752: UserWarning: A worker stopped while some jobs were given to the executor. This can be caused by a too short worker timeout or by a memory leak.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 11%|██████▉                                                      | 5/44 [26:05<3:19:18, 306.62s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 14%|████████▎                                                    | 6/44 [31:03<3:12:14, 303.55s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 16%|█████████▋                                                   | 7/44 [35:46<3:02:57, 296.68s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 23%|█████████████▋                                              | 10/44 [50:25<2:45:21, 291.82s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 25%|███████████████                                             | 11/44 [54:30<2:32:38, 277.54s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 27%|████████████████▎                                           | 12/44 [58:52<2:25:23, 272.61s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 30%|█████████████████▏                                        | 13/44 [1:03:18<2:19:52, 270.74s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 32%|██████████████████▍                                       | 14/44 [1:08:04<2:17:35, 275.19s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 36%|█████████████████████                                     | 16/44 [1:17:55<2:14:34, 288.37s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 39%|██████████████████████▍                                   | 17/44 [1:23:53<2:19:07, 309.17s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 41%|███████████████████████▋                                  | 18/44 [1:29:39<2:18:44, 320.16s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      " 43%|█████████████████████████                                 | 19/44 [1:35:25<2:16:42, 328.11s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 48%|███████████████████████████▋                              | 21/44 [1:47:17<2:11:22, 342.71s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 50%|█████████████████████████████                             | 22/44 [1:52:43<2:03:50, 337.73s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 52%|██████████████████████████████▎                           | 23/44 [1:58:44<2:00:40, 344.77s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 55%|███████████████████████████████▋                          | 24/44 [2:04:24<1:54:26, 343.34s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 57%|████████████████████████████████▉                         | 25/44 [2:10:05<1:48:25, 342.38s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 61%|███████████████████████████████████▌                      | 27/44 [2:21:09<1:35:51, 338.30s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 66%|██████████████████████████████████████▏                   | 29/44 [2:32:39<1:25:36, 342.41s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 68%|███████████████████████████████████████▌                  | 30/44 [2:37:45<1:17:20, 331.48s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 70%|████████████████████████████████████████▊                 | 31/44 [2:43:37<1:13:08, 337.61s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 75%|███████████████████████████████████████████▌              | 33/44 [2:55:02<1:02:25, 340.46s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 80%|███████████████████████████████████████████████▋            | 35/44 [3:06:48<52:12, 348.02s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 82%|█████████████████████████████████████████████████           | 36/44 [3:12:16<45:35, 341.95s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 84%|██████████████████████████████████████████████████▍         | 37/44 [3:17:47<39:29, 338.56s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 86%|███████████████████████████████████████████████████▊        | 38/44 [3:23:22<33:46, 337.67s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 91%|██████████████████████████████████████████████████████▌     | 40/44 [3:34:20<22:12, 333.12s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      " 93%|███████████████████████████████████████████████████████▉    | 41/44 [3:39:52<16:38, 332.78s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      " 95%|█████████████████████████████████████████████████████████▎  | 42/44 [3:45:32<11:09, 334.89s/it]/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "/home/georg/anaconda3/envs/ml10/lib/python3.10/site-packages/Bio/pairwise2.py:278: BiopythonDeprecationWarning: Bio.pairwise2 has been deprecated, and we intend to remove it in a future release of Biopython. As an alternative, please consider using Bio.Align.PairwiseAligner as a replacement, and contact the Biopython developers if you still need the Bio.pairwise2 module.\n",
      "  warnings.warn(\n",
      "100%|████████████████████████████████████████████████████████████| 44/44 [3:50:56<00:00, 314.92s/it]\n"
     ]
    }
   ],
   "source": [
    "# get hoot alignment info (total genius should take ~4h)\n",
    "n_total = 2151\n",
    "n_jobs = 10\n",
    "step_size = 50\n",
    "n_steps = int(np.ceil(n_total/step_size))\n",
    "for n_step in tqdm.tqdm(range(n_steps), total=n_steps):\n",
    "    min_idx = n_step*step_size\n",
    "    max_idx = np.min([(n_step+1)*step_size, n_total])\n",
    "    aligned_lyrics = Parallel(n_jobs=n_jobs)(delayed(collect_lyrics)(n) for n in range(min_idx, max_idx))\n",
    "    flat_aligned_lyrics = []\n",
    "    for e in aligned_lyrics:\n",
    "        flat_aligned_lyrics.extend(e)\n",
    "    write_jsonl(\n",
    "        flat_aligned_lyrics, \n",
    "        os.path.join(TMP_DIR, \"genius_hq_alignments.jsonl\"), \n",
    "        do_append=bool(n_step!=0),\n",
    "    )\n",
    "    del flat_aligned_lyrics, aligned_lyrics\n",
    "    gc.collect();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "f70d25a0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "685557 tmp/genius_hq_alignments.jsonl\r\n"
     ]
    }
   ],
   "source": [
    "!wc -l tmp/genius_hq_alignments.jsonl"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8c43d1c8",
   "metadata": {},
   "source": [
    "#### Verify some stats after saving cache file"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "23000343",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "  5%|██▋                                                    | 34277/685557 [00:49<15:32, 698.21it/s]"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "35903.2 hours\n",
      "0.0 hours silence\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "# get summary (sample) stats\n",
    "with open(os.path.join(TMP_DIR, \"genius_hq_alignments.jsonl\")) as f:\n",
    "    n_tot_rows = sum(1 for _ in f)\n",
    "    \n",
    "test_tokenize = True\n",
    "n_max_sample = n_tot_rows  \n",
    "if test_tokenize:\n",
    "    n_max_sample = int(round(n_max_sample/20))\n",
    "sample_data = []\n",
    "tot_duration_s = 0\n",
    "tot_duration_silence_s = 0\n",
    "n_tokens_list = []\n",
    "unique_ids = set()\n",
    "n = 0\n",
    "with open(os.path.join(TMP_DIR, \"genius_hq_alignments.jsonl\")) as f:\n",
    "    for line in tqdm.tqdm(f, total=n_tot_rows):\n",
    "        line = line.strip()\n",
    "        if len(line) == 0:\n",
    "            continue\n",
    "        k, v = json.loads(line)\n",
    "        if k not in metas_map:\n",
    "            continue\n",
    "        if k in unique_ids:\n",
    "            continue\n",
    "        unique_ids.add(k)\n",
    "        for e in v:\n",
    "            if e[\"text\"] != \"\":\n",
    "                tot_duration_s += e[\"end_s\"] - e[\"start_s\"]\n",
    "            if e[\"text\"] == \"\":\n",
    "                tot_duration_silence_s += e[\"end_s\"] - e[\"start_s\"]\n",
    "            if test_tokenize:\n",
    "                n_tokens_list.append(len(tokenize(e[\"text\"], max_tokens=512*8)))\n",
    "        if n % 500 == 0:\n",
    "            sample_data.append((k, v))\n",
    "        n += 1\n",
    "        if n == n_max_sample:\n",
    "            break\n",
    "# print(int(len(unique_ids)*n_tot_rows/n_max_sample), \"items\")\n",
    "print(round(tot_duration_s*n_tot_rows/n_max_sample/60/60,1), \"hours\")\n",
    "print(round(tot_duration_silence_s*n_tot_rows/n_max_sample/60/60,1), \"hours silence\")\n",
    "## last round\n",
    "# 748206 items\n",
    "# 34517.4 hours\n",
    "# 7617.4 hours silence\n",
    "## this round (approx)\n",
    "# 687017 items\n",
    "# 32635.1 hours\n",
    "# 0.0 hours silence\n",
    "## this round (approx)\n",
    "# 685557 items\n",
    "# 35829.3 hours\n",
    "# 0.0 hours silence"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "8cdd3fd1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# max is prob around 1000\n",
    "pd.Series(n_tokens_list).hist(bins=50);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f1611437",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "71969894",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "75bdde26",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "80cc74c5",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
