Skip to content

Instantly share code, notes, and snippets.

@jadsongmatos
Created July 16, 2025 23:12
Show Gist options
  • Save jadsongmatos/79abce0bdec55438b7efa7ef1466e99b to your computer and use it in GitHub Desktop.
Save jadsongmatos/79abce0bdec55438b7efa7ef1466e99b to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import csv\n",
"import json\n",
"import time\n",
"import requests\n",
"from collections import defaultdict"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"id": "7NAIsQC5ScHe"
},
"outputs": [],
"source": [
"# Configurações\n",
"PIPED_API = \"https://piapi.ggtyler.dev\" # API Piped\n",
"TIMEOUT = (1000/9) # segundos de timeout para a requisição 111,111111111\n",
"DELAY = (1/3) # segundos de espera entre requisições\n",
"TARGET_TYPES = [\"music_songs\", \"music_videos\", \"music_albums\", \"stream\", \"videos\"]\n",
"\n",
"def load_progress(path):\n",
" \"\"\"Carrega playlists já processadas (para retomar o trabalho).\"\"\"\n",
" if not os.path.exists(path):\n",
" return defaultdict(list)\n",
" with open(path, encoding='utf-8') as f:\n",
" data = json.load(f)\n",
" pl = defaultdict(list)\n",
" for item in data.get(\"playlists\", []):\n",
" pl[item[\"name\"]] = item[\"videos\"]\n",
" return pl\n",
"\n",
"\n",
"def save_progress(playlists, path):\n",
" \"\"\"Salva playlists no formato Piped imediatamente.\"\"\"\n",
" piped_format = {\n",
" \"format\": \"Piped\",\n",
" \"version\": 1,\n",
" \"playlists\": [\n",
" {\n",
" \"name\": name,\n",
" \"type\": \"playlist\",\n",
" \"visibility\": \"private\",\n",
" \"videos\": videos\n",
" }\n",
" for name, videos in playlists.items()\n",
" ]\n",
" }\n",
" # Grava em arquivo\n",
" with open(path, 'w', encoding='utf-8') as f:\n",
" json.dump(piped_format, f, ensure_ascii=False, indent=2)\n",
"\n",
"\n",
"def search_video(track_name, artist_name):\n",
" \"\"\"Busca um vídeo no YouTube via Piped API.\"\"\"\n",
" query = f\"{track_name} {artist_name}\"\n",
" params = {\"q\": query, \"filter\": \"music_songs\"}\n",
" try:\n",
" resp = requests.get(f\"{PIPED_API}/search\", params=params, timeout=TIMEOUT)\n",
" resp.raise_for_status()\n",
" items = resp.json().get(\"items\", [])\n",
" # seleciona pela ordem de preferência de tipo\n",
" for t in TARGET_TYPES:\n",
" for item in items:\n",
" if item.get(\"type\") == t:\n",
" return item.get(\"url\")\n",
" return None\n",
" except Exception as e:\n",
" print(f\"❌ Erro ao buscar «{query}»: {e}\")\n",
" return None\n",
"\n",
"\n",
"def convert_csv_to_piped_format(csv_path, output_path):\n",
" playlists = load_progress(output_path)\n",
"\n",
" with open(csv_path, newline='', encoding='utf-8') as csvfile:\n",
" reader = csv.DictReader(csvfile)\n",
" rows = list(reader)\n",
"\n",
" for idx, row in enumerate(rows, start=1):\n",
" playlist_name = row.get('Playlist name') or \"Liked videos\"\n",
" track_name = row['Track name'].strip()\n",
" artist_name = row['Artist name'].strip()\n",
"\n",
" # Se já houver esse vídeo, pula\n",
" if any(track_name in url for url in playlists[playlist_name]):\n",
" print(f\"⏭️ Já processado ({idx}/{len(rows)}): {track_name}\")\n",
" continue\n",
"\n",
" print(f\"🔎 Buscando {idx}/{len(rows)}: {track_name} – {artist_name}\")\n",
" url = search_video(track_name, artist_name)\n",
" if url:\n",
" full_url = f\"https://youtube.com{url}\"\n",
" playlists[playlist_name].append(full_url)\n",
" # salva progresso imediatamente\n",
" save_progress(playlists, output_path)\n",
" print(f\"✅ Salvo: {full_url}\")\n",
" else:\n",
" print(f\"⚠️ Não encontrado: {track_name}\")\n",
"\n",
" time.sleep(DELAY)\n",
"\n",
" print(\"🏁 Conversão concluída.\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "sVcOdeD7SiWg",
"outputId": "084f8279-80d1-4d74-8d49-b7e07724c79b"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"🔄 Iniciando conversão do CSV para formato Piped...\n",
"🔎 Buscando 1/4920: A Arena do Vazio | O Filme | - Minecraft Em busca da casa automática #329 – \n"
]
}
],
"source": [
"print(\"🔄 Iniciando conversão do CSV para formato Piped...\")\n",
"convert_csv_to_piped_format(\"seu_arquivo.csv\", \"playlists_piped.json\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"display_name": "base",
"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.12.2"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment