Compare commits
7 Commits
4ec369c18e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
277ef128ac | ||
|
|
61d721b514 | ||
|
|
2e2b10890c | ||
|
|
4051b0f054 | ||
|
|
c710a9bdd5 | ||
|
|
b5b6135cc3 | ||
|
|
f962f16308 |
5
.env
5
.env
@@ -1,6 +1,7 @@
|
||||
# --- TELEGRAM BOT ---
|
||||
BOT_TOKEN=8747085830:AAHsOTdZbK40daE1Lxmjvw5CcKHY_A7sucI
|
||||
AUTHORIZED_USER_ID=951506682
|
||||
ADMIN_ID=951506682
|
||||
AUTHORIZED_USER_IDS=1121430909
|
||||
|
||||
# --- POSTGRESQL ---
|
||||
# Gunakan 'localhost' jika aplikasi jalan langsung di VPS (tanpa Docker),
|
||||
@@ -15,7 +16,7 @@ DB_NAME=default_db
|
||||
# --- MINIO S3 ---
|
||||
# Catatan: Pastikan port 9000 (API MinIO) bisa diakses, bukan cuma port 9001 (Console)
|
||||
# Endpoint tidak perlu memakai awalan https:// atau http://
|
||||
MINIO_ENDPOINT=localhost:9000
|
||||
MINIO_ENDPOINT=127.0.0.1:9000
|
||||
MINIO_ACCESS_KEY=admin_ando
|
||||
MINIO_SECRET_KEY=PasswordSuperKuat123!
|
||||
# SECURE diset ke True karena lalu lintasnya dilewatkan melalui NPM yang menggunakan HTTPS (Let's Encrypt).
|
||||
|
||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
downloads
|
||||
Binary file not shown.
314
bot.py
314
bot.py
@@ -1,164 +1,272 @@
|
||||
import asyncio
|
||||
import os
|
||||
import time
|
||||
import logging
|
||||
from aiogram import Bot, Dispatcher, types, BaseMiddleware
|
||||
import re
|
||||
from aiogram import Bot, Dispatcher, types, BaseMiddleware, F
|
||||
from aiogram.filters import Command
|
||||
from aiogram.types import FSInputFile, InlineKeyboardMarkup, InlineKeyboardButton
|
||||
from aiogram.types import FSInputFile, InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
|
||||
from aiogram.client.default import DefaultBotProperties
|
||||
from aiogram.client.session.aiohttp import AiohttpSession # <-- TAMBAH INI
|
||||
from aiogram.client.session.aiohttp import AiohttpSession
|
||||
from dotenv import load_dotenv
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
|
||||
# Import modul lokal yang sudah kita buat
|
||||
from yt_engine import process_youtube_request
|
||||
from yt_engine import process_youtube_request, get_recommendations, search_youtube_list
|
||||
from db_manager import db
|
||||
from s3_manager import upload_audio, download_audio, delete_audio
|
||||
|
||||
# Setup Logging
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
load_dotenv()
|
||||
BOT_TOKEN = os.getenv("BOT_TOKEN")
|
||||
# Pastikan ini mengambil angka ID Telegram kamu
|
||||
AUTHORIZED_USER_ID = int(os.getenv("AUTHORIZED_USER_ID", 0))
|
||||
|
||||
# --- INISIALISASI BOT ---
|
||||
# Timeout diatur 300 detik (5 menit) agar Telegram tidak memutus koneksi saat mengirim file MP3 yang besar
|
||||
session = AiohttpSession(timeout=300)
|
||||
# --- KONFIGURASI ADMIN & USERS ---
|
||||
ADMIN_ID = int(os.getenv("ADMIN_ID", 0))
|
||||
auth_users_str = os.getenv("AUTHORIZED_USER_IDS", "")
|
||||
AUTHORIZED_USER_IDS = {int(uid.strip()) for uid in auth_users_str.split(",") if uid.strip().isdigit()}
|
||||
|
||||
bot = Bot(
|
||||
token=BOT_TOKEN,
|
||||
session=session,
|
||||
default=DefaultBotProperties(parse_mode=None)
|
||||
)
|
||||
# Pastikan Admin selalu masuk whitelist
|
||||
if ADMIN_ID != 0:
|
||||
AUTHORIZED_USER_IDS.add(ADMIN_ID)
|
||||
|
||||
# Penyimpanan sementara user yang sedang menunggu persetujuan (agar admin tidak di-spam)
|
||||
pending_users = set()
|
||||
|
||||
session = AiohttpSession(timeout=300)
|
||||
bot = Bot(token=BOT_TOKEN, session=session, default=DefaultBotProperties(parse_mode=None))
|
||||
dp = Dispatcher()
|
||||
# --- MIDDLEWARE / SECURITY CHECK ---
|
||||
music_queue = asyncio.Queue()
|
||||
user_searches = {}
|
||||
|
||||
async def queue_worker():
|
||||
while True:
|
||||
query, chat_id, msg_id = await music_queue.get()
|
||||
try:
|
||||
await process_music_request(query, chat_id, msg_id)
|
||||
except Exception as e:
|
||||
logger.error(f"Worker Error: {e}")
|
||||
finally:
|
||||
music_queue.task_done()
|
||||
|
||||
# --- SATPAM BOT DENGAN FITUR NOTIFIKASI ADMIN ---
|
||||
class SecurityMiddleware(BaseMiddleware):
|
||||
async def __call__(self, handler, event: types.Message, data: dict):
|
||||
# Memblokir semua orang kecuali Kamu (Single-User Mode)
|
||||
if event.from_user.id != AUTHORIZED_USER_ID:
|
||||
logger.warning(f"Akses ditolak untuk User ID: {event.from_user.id}")
|
||||
return # Abaikan pesan
|
||||
async def __call__(self, handler, event: types.Message | CallbackQuery, data: dict):
|
||||
user_id = event.from_user.id
|
||||
|
||||
if user_id not in AUTHORIZED_USER_IDS:
|
||||
# Jika user baru dan belum ada di antrean pending
|
||||
if user_id not in pending_users and ADMIN_ID != 0:
|
||||
pending_users.add(user_id)
|
||||
username = event.from_user.username or event.from_user.first_name or "Unknown"
|
||||
|
||||
# 1. Kirim pesan ke Admin dengan tombol
|
||||
kb = InlineKeyboardMarkup(inline_keyboard=[
|
||||
[
|
||||
InlineKeyboardButton(text="✅ Izinkan", callback_data=f"approve_{user_id}"),
|
||||
InlineKeyboardButton(text="❌ Tolak", callback_data=f"reject_{user_id}")
|
||||
]
|
||||
])
|
||||
try:
|
||||
await bot.send_message(
|
||||
ADMIN_ID,
|
||||
f"🔔 **IZIN AKSES BARU!**\n\n👤 Nama: `{username}`\n🆔 ID: `{user_id}`\n\n_Apakah kamu mengizinkan orang ini menggunakan bot musikmu?_",
|
||||
reply_markup=kb, parse_mode="Markdown"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Gagal mengirim notif ke admin: {e}")
|
||||
|
||||
# 2. Kirim pesan ke User asing
|
||||
if isinstance(event, types.Message):
|
||||
await event.answer("🔒 **Akses Terkunci.**\nBot ini bersifat private. Permintaan izin telah dikirim ke Admin. Harap tunggu persetujuan.", parse_mode="Markdown")
|
||||
elif isinstance(event, types.CallbackQuery):
|
||||
await event.answer("Akses terkunci! Permintaan izin dikirim ke Admin.", show_alert=True)
|
||||
|
||||
# Abaikan semua perintah dari user asing ini
|
||||
return
|
||||
|
||||
return await handler(event, data)
|
||||
|
||||
# Daftarkan Satpam ke sistem
|
||||
dp.message.middleware(SecurityMiddleware())
|
||||
dp.callback_query.middleware(SecurityMiddleware())
|
||||
|
||||
# --- FUNGSI UPDATE .ENV OTOMATIS ---
|
||||
def add_user_to_env(new_id):
|
||||
try:
|
||||
with open(".env", "r") as f:
|
||||
lines = f.readlines()
|
||||
with open(".env", "w") as f:
|
||||
for line in lines:
|
||||
if line.startswith("AUTHORIZED_USER_IDS="):
|
||||
clean_line = line.strip().rstrip(',')
|
||||
line = f"{clean_line},{new_id}\n"
|
||||
f.write(line)
|
||||
logger.info(f"User {new_id} berhasil ditambahkan permanen ke .env")
|
||||
except Exception as e:
|
||||
logger.error(f"Gagal update .env: {e}")
|
||||
|
||||
# --- HANDLER TOMBOL ADMIN (APPROVE / REJECT) ---
|
||||
@dp.callback_query(F.data.startswith("approve_") | F.data.startswith("reject_"))
|
||||
async def handle_admin_action(callback: CallbackQuery):
|
||||
if callback.from_user.id != ADMIN_ID:
|
||||
return await callback.answer("Kamu bukan Admin!", show_alert=True)
|
||||
|
||||
action, target_id_str = callback.data.split("_")
|
||||
target_id = int(target_id_str)
|
||||
|
||||
if target_id in pending_users:
|
||||
pending_users.remove(target_id)
|
||||
|
||||
await callback.message.edit_reply_markup(reply_markup=None)
|
||||
|
||||
if action == "approve":
|
||||
AUTHORIZED_USER_IDS.add(target_id)
|
||||
add_user_to_env(target_id)
|
||||
await callback.message.edit_text(f"✅ User `{target_id}` telah **DISETUJUI** dan ditambahkan permanen ke sistem.", parse_mode="Markdown")
|
||||
try:
|
||||
await bot.send_message(target_id, "🎉 **AKSES DISETUJUI!**\nAdmin telah memberikan izin. Silakan ketik /start atau /play <judul> untuk mulai mencari lagu.", parse_mode="Markdown")
|
||||
except: pass
|
||||
else:
|
||||
await callback.message.edit_text(f"❌ User `{target_id}` telah **DITOLAK**.", parse_mode="Markdown")
|
||||
try:
|
||||
await bot.send_message(target_id, "❌ **AKSES DITOLAK.**\nAdmin tidak memberikan izin untuk menggunakan bot ini.", parse_mode="Markdown")
|
||||
except: pass
|
||||
|
||||
def extract_video_id(query):
|
||||
match = re.search(r"(?:v=|\/)([0-9A-Za-z_-]{11}).*", query)
|
||||
return match.group(1) if match else None
|
||||
|
||||
def get_search_keyboard(chat_id, page=0):
|
||||
data = user_searches.get(chat_id)
|
||||
if not data: return None
|
||||
results = data['results']
|
||||
start_idx = page * 10
|
||||
end_idx = start_idx + 10
|
||||
page_results = results[start_idx:end_idx]
|
||||
|
||||
kb = []
|
||||
for rec in page_results:
|
||||
display_text = f"🎵 {rec['title']} | {rec.get('uploader', 'YT')}"
|
||||
short_text = display_text[:55] + "..." if len(display_text) > 55 else display_text
|
||||
kb.append([InlineKeyboardButton(text=short_text, callback_data=f"play_{rec['id']}")])
|
||||
|
||||
nav_buttons = []
|
||||
if page > 0: nav_buttons.append(InlineKeyboardButton(text="⬅️ Prev", callback_data=f"page_{page-1}"))
|
||||
if end_idx < len(results): nav_buttons.append(InlineKeyboardButton(text="Next ➡️", callback_data=f"page_{page+1}"))
|
||||
if nav_buttons: kb.append(nav_buttons)
|
||||
|
||||
return InlineKeyboardMarkup(inline_keyboard=kb)
|
||||
|
||||
# --- COMMAND HANDLERS ---
|
||||
@dp.message(Command("start"))
|
||||
async def cmd_start(message: types.Message):
|
||||
await message.answer(
|
||||
"🎧 **Kantor-Bypass Music Bot Ready!**\n\n"
|
||||
"Kirim judul lagu atau link YouTube dengan format:\n"
|
||||
"`/play <judul/link>`\n\n"
|
||||
"Contoh: `/play Dewa 19 Kangen`"
|
||||
)
|
||||
await message.answer("🎧 **Music Bot VIP Ready!**\nKirim format: `/play <judul lagu>`", parse_mode="Markdown")
|
||||
|
||||
@dp.message(Command("play"))
|
||||
async def cmd_play(message: types.Message):
|
||||
query = message.text.replace("/play", "").strip()
|
||||
if not query:
|
||||
return await message.answer("⚠️ Masukkan judul lagu. Contoh: `/play Nadin Amizah`")
|
||||
|
||||
# Kirim status loading
|
||||
status_msg = await message.answer("🔍 *Mencari dan memproses...*")
|
||||
if not query: return await message.answer("⚠️ Masukkan judul lagu.")
|
||||
|
||||
# Gunakan asyncio.create_task agar tidak nge-block queue bot (bisa antre banyak lagu)
|
||||
asyncio.create_task(process_music_request(query, message.chat.id, status_msg.message_id))
|
||||
status_msg = await message.answer("🔍 *Mencari daftar lagu...*", parse_mode="Markdown")
|
||||
results = await search_youtube_list(query, max_results=30)
|
||||
|
||||
if not results: return await status_msg.edit_text("❌ Lagu tidak ditemukan.")
|
||||
|
||||
user_searches[message.chat.id] = {
|
||||
'header': f"🔎 Hasil pencarian: **{query}**",
|
||||
'results': results
|
||||
}
|
||||
|
||||
kb = get_search_keyboard(message.chat.id, page=0)
|
||||
await status_msg.edit_text(text=f"{user_searches[message.chat.id]['header']}\n_Halaman 1_", reply_markup=kb, parse_mode="Markdown")
|
||||
|
||||
@dp.callback_query(F.data.startswith("page_"))
|
||||
async def handle_page_click(callback: CallbackQuery):
|
||||
chat_id = callback.message.chat.id
|
||||
if chat_id not in user_searches:
|
||||
return await callback.answer("Sesi kadaluarsa. Ketik /play lagi.", show_alert=True)
|
||||
|
||||
page = int(callback.data.split("_")[1])
|
||||
kb = get_search_keyboard(chat_id, page)
|
||||
|
||||
if kb:
|
||||
header = user_searches[chat_id]['header']
|
||||
await callback.message.edit_text(text=f"{header}\n_Halaman {page+1}_", reply_markup=kb, parse_mode="Markdown")
|
||||
await callback.answer()
|
||||
|
||||
@dp.callback_query(F.data.startswith("play_"))
|
||||
async def handle_suggestion_click(callback: CallbackQuery):
|
||||
video_id = callback.data.split("_", 1)[1]
|
||||
await callback.message.edit_reply_markup(reply_markup=None)
|
||||
status_msg = await bot.send_message(callback.message.chat.id, "⏳ Mengunduh lagu pilihanmu...")
|
||||
query_url = f"https://www.youtube.com/watch?v={video_id}"
|
||||
await music_queue.put((query_url, callback.message.chat.id, status_msg.message_id))
|
||||
await callback.answer("Siapp! Diproses...")
|
||||
|
||||
# --- CORE LOGIC: PROCESS MUSIC ---
|
||||
async def process_music_request(query: str, chat_id: int, status_msg_id: int):
|
||||
local_file = None # Mencegah error 'UnboundLocalError' jika gagal di awal
|
||||
local_file = None
|
||||
try:
|
||||
# 1. Download dari YouTube
|
||||
yt_result = await process_youtube_request(query)
|
||||
video_id = extract_video_id(query)
|
||||
cached_data = None
|
||||
|
||||
if yt_result["status"] == "error":
|
||||
return await bot.edit_message_text(f"❌ Gagal: {yt_result['message']}", chat_id, status_msg_id)
|
||||
|
||||
video_id = yt_result["video_id"]
|
||||
title = yt_result["title"]
|
||||
local_file = yt_result["file_path"]
|
||||
s3_object_name = f"{video_id}.mp3"
|
||||
|
||||
# 2. Cek Cache di Database
|
||||
cached_data = await db.get_cache(video_id)
|
||||
if video_id:
|
||||
cached_data = await db.get_cache(video_id)
|
||||
if cached_data:
|
||||
title = cached_data.get('title', 'Lagu')
|
||||
s3_object_name = cached_data.get('s3_object_key', f"{video_id}.m4a")
|
||||
ext = s3_object_name.split('.')[-1]
|
||||
local_file = f"downloads/cache_{video_id}.{ext}"
|
||||
await bot.edit_message_text("⚡ Mengambil dari Cache...", chat_id, status_msg_id)
|
||||
await download_audio(s3_object_name, local_file)
|
||||
|
||||
if cached_data:
|
||||
await bot.edit_message_text("⚡ *Mengambil dari Cache S3...*", chat_id, status_msg_id)
|
||||
# Download dari S3 ke local temporary
|
||||
local_file = f"downloads/cache_{video_id}.mp3"
|
||||
await download_audio(s3_object_name, local_file)
|
||||
else:
|
||||
await bot.edit_message_text("☁️ *Mengunggah ke S3 Storage...*", chat_id, status_msg_id)
|
||||
# Upload file baru ke S3
|
||||
if not cached_data:
|
||||
await bot.edit_message_text("🔍 Mengunduh dari YouTube...", chat_id, status_msg_id)
|
||||
yt_result = await process_youtube_request(query)
|
||||
if yt_result["status"] == "error":
|
||||
return await bot.edit_message_text(f"❌ Gagal: {yt_result['message']}", chat_id, status_msg_id)
|
||||
video_id = yt_result["video_id"]
|
||||
title = yt_result["title"]
|
||||
local_file = yt_result["file_path"]
|
||||
s3_object_name = f"{video_id}.{yt_result['ext']}"
|
||||
await bot.edit_message_text("☁️ Menyimpan ke Server...", chat_id, status_msg_id)
|
||||
if local_file and os.path.exists(local_file):
|
||||
await upload_audio(local_file, s3_object_name)
|
||||
await db.save_cache(video_id, title, s3_object_name)
|
||||
|
||||
# 3. Kirim Audio ke Telegram
|
||||
await bot.edit_message_text("📤 *Mengirim audio ke Telegram... (Mungkin butuh waktu)*", chat_id, status_msg_id)
|
||||
|
||||
await bot.edit_message_text("📤 Mengirim ke Telegram...", chat_id, status_msg_id)
|
||||
if local_file and os.path.exists(local_file):
|
||||
audio = FSInputFile(local_file)
|
||||
await bot.send_audio(chat_id=chat_id, audio=audio, title=title, performer="Music Bot", request_timeout=300)
|
||||
if status_msg_id: await bot.delete_message(chat_id, status_msg_id)
|
||||
|
||||
# Opsi: Tombol pencarian selanjutnya
|
||||
kb = [[InlineKeyboardButton(text="🎵 Putar Lagu Acak Lainnya", switch_inline_query_current_chat="")]]
|
||||
reply_markup = InlineKeyboardMarkup(inline_keyboard=kb)
|
||||
|
||||
# Ekstra Pengaman Timeout di Request Level
|
||||
await bot.send_audio(
|
||||
chat_id=chat_id,
|
||||
audio=audio,
|
||||
title=title,
|
||||
performer="Music Bot",
|
||||
reply_markup=reply_markup,
|
||||
request_timeout=300
|
||||
)
|
||||
|
||||
# Hapus pesan status yang muter-muter
|
||||
await bot.delete_message(chat_id, status_msg_id)
|
||||
else:
|
||||
await bot.edit_message_text("❌ File audio tidak ditemukan di server lokal.", chat_id, status_msg_id)
|
||||
# --- TAMPILKAN 30 REKOMENDASI DENGAN PAGING ---
|
||||
recs = await get_recommendations(video_id)
|
||||
if recs:
|
||||
user_searches[chat_id] = {
|
||||
'header': f"💡 Rekomendasi lanjutan dari: **{title}**",
|
||||
'results': recs
|
||||
}
|
||||
kb = get_search_keyboard(chat_id, page=0)
|
||||
if kb:
|
||||
await bot.send_message(chat_id, text=f"{user_searches[chat_id]['header']}\n_Halaman 1_", reply_markup=kb, parse_mode="Markdown")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing music: {e}")
|
||||
try:
|
||||
await bot.edit_message_text(f"❌ Terjadi kesalahan internal: {e}", chat_id, status_msg_id)
|
||||
except:
|
||||
pass # Abaikan jika pesan sudah terhapus
|
||||
|
||||
logger.error(f"Error: {e}")
|
||||
finally:
|
||||
# 4. Hapus file temporary di VPS (Hemat Disk!)
|
||||
if local_file and os.path.exists(local_file):
|
||||
os.remove(local_file)
|
||||
if local_file and os.path.exists(local_file): os.remove(local_file)
|
||||
|
||||
# --- BACKGROUND JOB: AUTO CLEANUP (7 HARI) ---
|
||||
async def cleanup_expired_cache():
|
||||
logger.info("🧹 Menjalankan tugas pembersihan cache otomatis...")
|
||||
expired_items = await db.get_expired_cache(days=7)
|
||||
|
||||
for item in expired_items:
|
||||
# Hapus dari S3
|
||||
await delete_audio(item['s3_object_key'])
|
||||
# Hapus dari Database
|
||||
await db.delete_cache(item['youtube_id'])
|
||||
logger.info(f"🗑️ Dihapus: {item['title']} (Usia > 7 Hari)")
|
||||
|
||||
# --- MAIN LOOP ---
|
||||
async def main():
|
||||
# Konek ke database
|
||||
await db.connect()
|
||||
|
||||
# Jalankan Scheduler untuk bersih-bersih tiap jam 3 pagi
|
||||
asyncio.create_task(queue_worker())
|
||||
scheduler = AsyncIOScheduler()
|
||||
scheduler.add_job(cleanup_expired_cache, 'cron', hour=3, minute=0)
|
||||
scheduler.start()
|
||||
|
||||
logger.info("🚀 Bot Music Started!")
|
||||
|
||||
# Hapus webhook lama (jika ada) dan mulai polling
|
||||
logger.info("🚀 Bot Music V4 (Admin Approval & Super Paging) Berjalan!")
|
||||
await bot.delete_webhook(drop_pending_updates=True)
|
||||
await dp.start_polling(bot)
|
||||
|
||||
|
||||
43
cookies.txt
43
cookies.txt
@@ -1,25 +1,26 @@
|
||||
# Netscape HTTP Cookie File
|
||||
# This file is generated by yt-dlp. Do not edit.
|
||||
|
||||
.youtube.com TRUE / TRUE 2147483647 __Secure-1PAPISID CABFFJNnCe9Ocqz0/ANX4rXmqpbZzZy8y1
|
||||
.youtube.com TRUE / TRUE 2147483647 __Secure-1PSID g.a0008AhqPWcg3-Syf1o_JEl3LCHzWFKn_n3Y8jgh3RnwDW83PtpvDZ6ZjTqI52V0CZWm40gj1wACgYKAYYSARESFQHGX2MigEiR2_gNFFV-QFRM2YthnxoVAUF8yKp0aiEfXAKcPOTZdsLVsdG00076
|
||||
.youtube.com TRUE / TRUE 1805947740 __Secure-1PSIDCC AKEyXzUORFVg_0ZBDrcdDFVznCjghYk0kFgXPgu6_nGbfqQb8JQApHGnBqt7NkD7fgCea-TTmg
|
||||
.youtube.com TRUE / TRUE 2147483647 __Secure-1PSIDRTS sidts-CjcBWhotCb-4c6IuQXRvmRVYyon3cb4LfaOkQmhdlWz2FTjey8tLFTyGymAQr1fbHPACgLGOXKsXEAA
|
||||
.youtube.com TRUE / TRUE 2147483647 __Secure-1PSIDTS sidts-CjUBWhotCXH5m0kFaAC1lhqf8kaotcZDxFxOty6XBxNsYxySai6J15Fi8BHklGtKOGZKSNYrLxAA
|
||||
.youtube.com TRUE / TRUE 1837483738 __Secure-3PAPISID CABFFJNnCe9Ocqz0/ANX4rXmqpbZzZy8y1
|
||||
.youtube.com TRUE / TRUE 1837483738 __Secure-3PSID g.a0008AhqPWcg3-Syf1o_JEl3LCHzWFKn_n3Y8jgh3RnwDW83PtpvC14W2htdab0jQBsnf1YkqgACgYKAdcSARESFQHGX2MiTK5ZxIBltpFTHCsEt_xiuxoVAUF8yKrG5FgxNyKwsIayRODxp8Fo0076
|
||||
.youtube.com TRUE / TRUE 1805947740 __Secure-3PSIDCC AKEyXzVld0GspLJe8l6JS4cfa4h4ElnWgBoAB5kiHdSs0gCerB-quDuuc4JEn63Voja8hi9ZSQ
|
||||
.youtube.com TRUE / TRUE 2147483647 __Secure-3PSIDRTS sidts-CjcBWhotCb-4c6IuQXRvmRVYyon3cb4LfaOkQmhdlWz2FTjey8tLFTyGymAQr1fbHPACgLGOXKsXEAA
|
||||
.youtube.com TRUE / TRUE 2147483647 __Secure-3PSIDTS sidts-CjUBWhotCXH5m0kFaAC1lhqf8kaotcZDxFxOty6XBxNsYxySai6J15Fi8BHklGtKOGZKSNYrLxAA
|
||||
.youtube.com TRUE / TRUE 2147483647 __Secure-BUCKET CJsE
|
||||
.youtube.com TRUE / TRUE 2147483647 __Secure-ROLLOUT_TOKEN CPuykLucktiDWBCKzZ6okLmLAximj_KpgbqTAw%3D%3D
|
||||
.youtube.com TRUE / TRUE 2147483647 _gcl_au 1.1.804233229.1768921729
|
||||
.youtube.com TRUE / TRUE 2147483647 AEC AaJma5u8FgHY34zVcINRn3aV7kclTK8WLQNF5t6Vx4CV-lCCTfNWLQLU_A
|
||||
.youtube.com TRUE / TRUE 2147483647 APC AfxxVi7ddIpw9dyE7CafpiBRx2fLbINQXwCWfbOjJujb7m4E7VjYkg
|
||||
.youtube.com TRUE / TRUE 2147483647 APISID PbWG-XrqTeJJFiPn/ACQ0hTzgL5lCMuKP5
|
||||
.youtube.com TRUE / TRUE 2147483647 __Secure-1PAPISID MfDYI460uMwbNWQX/AyiVY9j7HpzgoY-0w
|
||||
.youtube.com TRUE / TRUE 2147483647 __Secure-1PSID g.a0008QhqPaEYewnBjrrVkAfyYUX4qZBvGlP0AAKy4GHNUANHIhILSu3fanNWkdZCLU-xY2lVkgACgYKAZkSARESFQHGX2Miw30Iyoiest2hLNRbwo7xrBoVAUF8yKoPd2FfK6VDhHMeP0AeTiHq0076
|
||||
.youtube.com TRUE / TRUE 1806115265 __Secure-1PSIDCC AKEyXzWNkpsXnpDnuLA3W_IHPrWB2-z9uWItNPEWogkNMH6RR3EmgVtXL8guFpgBVBPTATfXbg
|
||||
.youtube.com TRUE / TRUE 2147483647 __Secure-1PSIDRTS sidts-CjYBWhotCVbg4zL386MEd1xPDnIrXetv9dZJvT7F0t8IruwIx6ZlfvbmmOFnYYLIOBUQlwwdCggQAA
|
||||
.youtube.com TRUE / TRUE 2147483647 __Secure-1PSIDTS sidts-CjYBWhotCVbg4zL386MEd1xPDnIrXetv9dZJvT7F0t8IruwIx6ZlfvbmmOFnYYLIOBUQlwwdCggQAA
|
||||
.youtube.com TRUE / TRUE 2147483647 __Secure-3PAPISID MfDYI460uMwbNWQX/AyiVY9j7HpzgoY-0w
|
||||
.youtube.com TRUE / TRUE 2147483647 __Secure-3PSID g.a0008QhqPaEYewnBjrrVkAfyYUX4qZBvGlP0AAKy4GHNUANHIhILfnT7eNSgF_8dabeD7hB4KQACgYKARISARESFQHGX2MiQZ9X8qjH9mx4cMVrf6sfSRoVAUF8yKqWhqmWybq0wXdDiyiB4j2A0076
|
||||
.youtube.com TRUE / TRUE 1806115265 __Secure-3PSIDCC AKEyXzUcbRIRHbwhE3LWL4wDgXuVGsXcWgERJXuPBpaDFfSANgOgmmYKjsM9laeZYKVJojrQUyc
|
||||
.youtube.com TRUE / TRUE 2147483647 __Secure-3PSIDRTS sidts-CjYBWhotCVbg4zL386MEd1xPDnIrXetv9dZJvT7F0t8IruwIx6ZlfvbmmOFnYYLIOBUQlwwdCggQAA
|
||||
.youtube.com TRUE / TRUE 2147483647 __Secure-3PSIDTS sidts-CjYBWhotCVbg4zL386MEd1xPDnIrXetv9dZJvT7F0t8IruwIx6ZlfvbmmOFnYYLIOBUQlwwdCggQAA
|
||||
.youtube.com TRUE / TRUE 2147483647 __Secure-BUCKET CPYF
|
||||
.youtube.com TRUE / TRUE 2147483647 __Secure-ROLLOUT_TOKEN CMbM-7XNqZzV5QEQmYbS_rO8kwMY_rSr_fW-kwM%3D
|
||||
.youtube.com TRUE / TRUE 2147483647 AEC AaJma5uOowvEPsKVftwErbdidI19zMVl5C9-xa4RyR78tyxULUO9ytInv40
|
||||
.youtube.com TRUE / TRUE 2147483647 APC AfxxVi5bzd9v-P9K_jo26VgiosIxzE813GYPLYyC5gmUMYjiaXg36w
|
||||
.youtube.com TRUE / TRUE 2147483647 APISID OnSMY049lYtHikQy/A5cj2ru6POklKZoFC
|
||||
.youtube.com TRUE / TRUE 2147483647 GPS 1
|
||||
.youtube.com TRUE / TRUE 2147483647 HSID AvGoSoPSULrXuoRT9
|
||||
.youtube.com TRUE / TRUE 2147483647 IDE AHWqTUmyGzw548Vz7XpBhz59fxCsE3RWJDZPUh3zrwecOjSQ1O8bujmlLDEeiEqCdE8
|
||||
.youtube.com TRUE / TRUE 2147483647 LOGIN_INFO AFmmF2swQwIfXTYVZVbXCVUR4NikS0IeA8v1bJ89WfHBNfr5ipEBGQIgK6RN_6Ubyj7GXQEy9AxZ9tofqAHiELNQ9NP0xRUrZL0:QUQ3MjNmejU3NEZORFNHTk5MM1BBam9mTm9sTGlLVXRSVk1qa3ExU0dkRlFHMjZMSUNwZlZrRFRmcUIxNHZ4S1p5WG5FbkEzSnNhQ3NITVhxSW5NVVBoc3BaV1N1OTFBYXd4cFdMZ2NNcXEwMFY4UEZsVmZLckNWMGtOVUFyRW5DUEdtLU9KX3pyMGZDWUdZdWhzejlIUWwtOUlyOEdNUnN3
|
||||
.youtube.com TRUE / FALSE 0 PREF hl=en&tz=UTC
|
||||
.youtube.com TRUE / TRUE 0 SOCS CAI
|
||||
.youtube.com TRUE / TRUE 0 YSC a2WtW-hP4Ck
|
||||
.youtube.com TRUE / TRUE 1789963740 VISITOR_INFO1_LIVE BIF7sNwBpLI
|
||||
.youtube.com TRUE / TRUE 1789963740 VISITOR_PRIVACY_METADATA CgJJRBIEGgAgFA%3D%3D
|
||||
.youtube.com TRUE / TRUE 1837483740 LOGIN_INFO AFmmF2swRgIhAMFCn3UGB5h-2Wf8Rnv4LIWp0LiVwf6ibiPVQhmFufMQAiEA-q7C22E801epMI4Lk54Xb9uNUHWVKkjNLHJ-auj0p7c:QUQ3MjNmelp0RkxSamFrTTZnOVBLelN1ZjVpeVRBWC1TTUc1d0NPUm9PXzNPQmx0dUNEdjBDOEF0cGsxc3p1SWhwMWtIZDdyMjRxYVFKZWRIWnkwMkxzLWVnakVCdFlxcVpFclc3N3VfNmpIWWh2aUd2eUxydzJMekZ6eFo4MzZUMDJROVRtTHZXdjFyZGlqV3dWVFZnUTlTWWJvbTZHRHRR
|
||||
.youtube.com TRUE / TRUE 0 YSC YlhQVvcChl0
|
||||
.youtube.com TRUE / TRUE 1790131265 VISITOR_INFO1_LIVE yYevOjATcc8
|
||||
.youtube.com TRUE / TRUE 1790131265 VISITOR_PRIVACY_METADATA CgJJRBIEGgAgTw%3D%3D
|
||||
|
||||
BIN
downloads/7SqNVv98e8Q.mp3
Normal file
BIN
downloads/7SqNVv98e8Q.mp3
Normal file
Binary file not shown.
BIN
downloads/grp6FCnioMM.mp3
Normal file
BIN
downloads/grp6FCnioMM.mp3
Normal file
Binary file not shown.
BIN
downloads/gvunApwKIiY.mp3
Normal file
BIN
downloads/gvunApwKIiY.mp3
Normal file
Binary file not shown.
BIN
downloads/gvunApwKIiY.webm
Normal file
BIN
downloads/gvunApwKIiY.webm
Normal file
Binary file not shown.
@@ -1,30 +1,30 @@
|
||||
__Secure-1PAPISID CABFFJNnCe9Ocqz0/ANX4rXmqpbZzZy8y1 .google.com / 2027-04-29T01:16:45.016Z 51 ✓ High
|
||||
__Secure-1PAPISID CABFFJNnCe9Ocqz0/ANX4rXmqpbZzZy8y1 .google.co.id / 2027-04-29T01:16:47.771Z 51 ✓ High
|
||||
__Secure-1PAPISID CABFFJNnCe9Ocqz0/ANX4rXmqpbZzZy8y1 .youtube.com / 2027-04-29T01:16:46.387Z 51 ✓ High
|
||||
__Secure-1PSID g.a0008AhqPRgOQo5F8wOanRJB1db-thi1yhD1Xwlfzu9ppmi4kdMvzqGjBiiYDCNImYvcK_KXVgACgYKAXwSARESFQHGX2Mir1uyxrt2SQU3jrmD0yIthBoVAUF8yKq28ua4eVfoLokWn8CKTPoa0076 .youtube.com / 2027-04-29T01:16:46.387Z 167 ✓ ✓ High
|
||||
__Secure-1PSID g.a0008AhqPRgOQo5F8wOanRJB1db-thi1yhD1Xwlfzu9ppmi4kdMvzqGjBiiYDCNImYvcK_KXVgACgYKAXwSARESFQHGX2Mir1uyxrt2SQU3jrmD0yIthBoVAUF8yKq28ua4eVfoLokWn8CKTPoa0076 .google.co.id / 2027-04-29T01:16:47.771Z 167 ✓ ✓ High
|
||||
__Secure-1PSID g.a0008AhqPWcg3-Syf1o_JEl3LCHzWFKn_n3Y8jgh3RnwDW83PtpvDZ6ZjTqI52V0CZWm40gj1wACgYKAYYSARESFQHGX2MigEiR2_gNFFV-QFRM2YthnxoVAUF8yKp0aiEfXAKcPOTZdsLVsdG00076 .google.com / 2027-04-29T01:16:45.579Z 167 ✓ ✓ High
|
||||
__Secure-1PSIDCC AKEyXzXv0PIbX1eVJDr7Vttvo3tExsBC8rMs2vpVgrxNe00DfRJOHTiRJlKZ3pEvy00-Eayfsg .youtube.com / 2027-03-25T02:41:49.761Z 90 ✓ ✓ High
|
||||
__Secure-1PSIDCC AKEyXzXvPBT_qPOKMMyJTGDcKgJyzeumUh2o_XM_-pzE2LOejAK6BTqjxXwrYtvVmBkzOcnh1Q .google.com / 2027-03-25T02:42:51.868Z 90 ✓ ✓ High
|
||||
__Secure-1PSIDRTS sidts-CjcBWhotCb-4c6IuQXRvmRVYyon3cb4LfaOkQmhdlWz2FTjey8tLFTyGymAQr1fbHPACgLGOXKsXEAA .google.com / 2026-03-25T02:52:18.056Z 102 ✓ ✓ High
|
||||
__Secure-1PSIDTS sidts-CjcBWhotCb-4c6IuQXRvmRVYyon3cb4LfaOkQmhdlWz2FTjey8tLFTyGymAQr1fbHPACgLGOXKsXEAA .google.com / 2027-03-25T02:42:18.056Z 101 ✓ ✓ High
|
||||
__Secure-1PSIDTS sidts-CjUBWhotCXH5m0kFaAC1lhqf8kaotcZDxFxOty6XBxNsYxySai6J15Fi8BHklGtKOGZKSNYrLxAA .youtube.com / 2027-03-25T01:16:46.387Z 98 ✓ ✓ High
|
||||
__Secure-3PAPISID CABFFJNnCe9Ocqz0/ANX4rXmqpbZzZy8y1 .google.co.id / 2027-04-29T01:16:47.771Z 51 ✓ None High
|
||||
__Secure-3PAPISID CABFFJNnCe9Ocqz0/ANX4rXmqpbZzZy8y1 .google.com / 2027-04-29T01:16:45.016Z 51 ✓ None High
|
||||
__Secure-3PAPISID CABFFJNnCe9Ocqz0/ANX4rXmqpbZzZy8y1 .youtube.com / 2027-04-29T01:16:46.387Z 51 ✓ None High
|
||||
__Secure-3PSID g.a0008AhqPRgOQo5F8wOanRJB1db-thi1yhD1Xwlfzu9ppmi4kdMvHGjo5dodXzHfz189Lq0cJAACgYKAYASARESFQHGX2MiLX3h-qgYtLWxidyIqDdTXxoVAUF8yKogpTMEeE4KkrIFdEdSM-w20076 .youtube.com / 2027-04-29T01:16:46.387Z 167 ✓ ✓ None High
|
||||
__Secure-3PSID g.a0008AhqPWcg3-Syf1o_JEl3LCHzWFKn_n3Y8jgh3RnwDW83PtpvC14W2htdab0jQBsnf1YkqgACgYKAdcSARESFQHGX2MiTK5ZxIBltpFTHCsEt_xiuxoVAUF8yKrG5FgxNyKwsIayRODxp8Fo0076 .google.com / 2027-04-29T01:16:45.579Z 167 ✓ ✓ None High
|
||||
__Secure-3PSID g.a0008AhqPRgOQo5F8wOanRJB1db-thi1yhD1Xwlfzu9ppmi4kdMvHGjo5dodXzHfz189Lq0cJAACgYKAYASARESFQHGX2MiLX3h-qgYtLWxidyIqDdTXxoVAUF8yKogpTMEeE4KkrIFdEdSM-w20076 .google.co.id / 2027-04-29T01:16:47.771Z 167 ✓ ✓ None High
|
||||
__Secure-3PSIDCC AKEyXzVz48drvwGgN1b2XAKs81pl9H2mM7nJHYNIIEbJC9fqGsjq6GA_F_67AqpwXAWM8EzMxg .youtube.com / 2027-03-25T02:41:49.761Z 90 ✓ ✓ None High
|
||||
__Secure-3PSIDCC AKEyXzXWcQoWLgU8-WQAC97HQEgo_Xv1BD5ov5jpXypXRQBggzdmNsu9hamArLQJDmo-Kvyy0A .google.com / 2027-03-25T02:42:51.868Z 90 ✓ ✓ None High
|
||||
__Secure-3PSIDRTS sidts-CjcBWhotCb-4c6IuQXRvmRVYyon3cb4LfaOkQmhdlWz2FTjey8tLFTyGymAQr1fbHPACgLGOXKsXEAA .google.com / 2026-03-25T02:52:18.057Z 102 ✓ ✓ None High
|
||||
__Secure-3PSIDTS sidts-CjcBWhotCb-4c6IuQXRvmRVYyon3cb4LfaOkQmhdlWz2FTjey8tLFTyGymAQr1fbHPACgLGOXKsXEAA .google.com / 2027-03-25T02:42:18.056Z 101 ✓ ✓ None High
|
||||
__Secure-3PSIDTS sidts-CjUBWhotCXH5m0kFaAC1lhqf8kaotcZDxFxOty6XBxNsYxySai6J15Fi8BHklGtKOGZKSNYrLxAA .youtube.com / 2027-03-25T01:16:46.387Z 98 ✓ ✓ None High
|
||||
__Secure-BUCKET CJsE .google.com / 2026-06-09T10:41:45.999Z 19 ✓ ✓ Medium
|
||||
__Secure-ROLLOUT_TOKEN CPuykLucktiDWBCKzZ6okLmLAximj_KpgbqTAw%3D%3D .youtube.com / 2026-09-21T02:32:45.104Z 66 ✓ ✓ None https://youtube.com Medium
|
||||
_gcl_au 1.1.804233229.1768921729 .youtube.com / 2026-04-20T15:08:49.000Z 31 Medium
|
||||
AEC AaJma5u8FgHY34zVcINRn3aV7kclTK8WLQNF5t6Vx4CV-lCCTfNWLQLU_A .google.com / 2026-04-04T03:15:45.043Z 61 ✓ ✓ Lax Medium
|
||||
APC AfxxVi7ddIpw9dyE7CafpiBRx2fLbINQXwCWfbOjJujb7m4E7VjYkg .doubleclick.net / 2026-05-26T06:48:31.384Z 57 ✓ None https://youtube.com ✓ Medium
|
||||
APISID PbWG-XrqTeJJFiPn/ACQ0hTzgL5lCMuKP5 .google.co.id / 2027-04-29T01:16:47.770Z 40 High
|
||||
APISID PbWG-XrqTeJJFiPn/ACQ0hTzgL5lCMuKP5 .youtube.com / 2027-04-29T01:16:46.387Z 40 High
|
||||
APISID PbWG-XrqTeJJFiPn/ACQ0hTzgL5lCMuKP5 .google.com / 2027-04-29T01:16:45.016Z 40 High
|
||||
__Secure-1PAPISID MfDYI460uMwbNWQX/AyiVY9j7HpzgoY-0w .google.co.id / 2027-05-01T01:26:56.717Z 51 ✓ High
|
||||
__Secure-1PAPISID MfDYI460uMwbNWQX/AyiVY9j7HpzgoY-0w .google.com / 2027-05-01T01:26:55.647Z 51 ✓ High
|
||||
__Secure-1PSID g.a0008QhqPejw13d8Bas4NdqKxoWCDD00TsMPF7Knc2ottj33ssYPm7cjYs1E8yUJXQ9IZsAengACgYKATgSARESFQHGX2MijdbKOp-mlWEL91PAMktjThoVAUF8yKpXrlV2MNU0xOiEf0WSNWup0076 .google.co.id / 2027-05-01T01:26:56.717Z 167 ✓ ✓ High
|
||||
__Secure-1PSID g.a0008QhqPaEYewnBjrrVkAfyYUX4qZBvGlP0AAKy4GHNUANHIhILSu3fanNWkdZCLU-xY2lVkgACgYKAZkSARESFQHGX2Miw30Iyoiest2hLNRbwo7xrBoVAUF8yKoPd2FfK6VDhHMeP0AeTiHq0076 .google.com / 2027-05-01T01:26:56.205Z 167 ✓ ✓ High
|
||||
__Secure-1PSIDCC AKEyXzUAwHyvaa8__7v4HjDYX86BY3ua4rZyVIzG0ja6gavUQP4WL3HLSUvuIcsltx_jqSk9xA .google.com / 2027-03-27T02:39:37.490Z 90 ✓ ✓ High
|
||||
__Secure-1PSIDRTS sidts-CjYBWhotCVbg4zL386MEd1xPDnIrXetv9dZJvT7F0t8IruwIx6ZlfvbmmOFnYYLIOBUQlwwdCggQAA .google.com / 2026-03-27T02:46:46.685Z 101 ✓ ✓ High
|
||||
__Secure-1PSIDTS sidts-CjQBWhotCR30ArqZTdTYPA9IaKLjiqxyQo3pFqyW0ahH99kAISY8Xx7eYW3SKziORlnuTslIEAA .youtube.com / 2027-03-27T02:39:38.397Z 97 ✓ ✓ High
|
||||
__Secure-1PSIDTS sidts-CjYBWhotCVbg4zL386MEd1xPDnIrXetv9dZJvT7F0t8IruwIx6ZlfvbmmOFnYYLIOBUQlwwdCggQAA .google.com / 2027-03-27T02:36:46.684Z 100 ✓ ✓ High
|
||||
__Secure-3PAPISID MfDYI460uMwbNWQX/AyiVY9j7HpzgoY-0w .youtube.com / 2027-05-01T02:39:38.398Z 51 ✓ None High
|
||||
__Secure-3PAPISID MfDYI460uMwbNWQX/AyiVY9j7HpzgoY-0w .google.com / 2027-05-01T01:26:55.647Z 51 ✓ None High
|
||||
__Secure-3PAPISID MfDYI460uMwbNWQX/AyiVY9j7HpzgoY-0w .google.co.id / 2027-05-01T01:26:56.717Z 51 ✓ None High
|
||||
__Secure-3PSID g.a0008QhqPejw13d8Bas4NdqKxoWCDD00TsMPF7Knc2ottj33ssYPtyc-n5f0oEWAodP032RkiQACgYKAVQSARESFQHGX2Miyfngp-pxu1zO6LsyiRhYLxoVAUF8yKqQ_Mt_VPGw8gmF1lmeF0Mz0076 .google.co.id / 2027-05-01T01:26:56.717Z 167 ✓ ✓ None High
|
||||
__Secure-3PSID g.a0008QhqPaEYewnBjrrVkAfyYUX4qZBvGlP0AAKy4GHNUANHIhILfnT7eNSgF_8dabeD7hB4KQACgYKARISARESFQHGX2MiQZ9X8qjH9mx4cMVrf6sfSRoVAUF8yKqWhqmWybq0wXdDiyiB4j2A0076 .google.com / 2027-05-01T01:26:56.205Z 167 ✓ ✓ None High
|
||||
__Secure-3PSID g.a0008QhqPaEYewnBjrrVkAfyYUX4qZBvGlP0AAKy4GHNUANHIhILfnT7eNSgF_8dabeD7hB4KQACgYKARISARESFQHGX2MiQZ9X8qjH9mx4cMVrf6sfSRoVAUF8yKqWhqmWybq0wXdDiyiB4j2A0076 .youtube.com / 2027-05-01T02:39:38.398Z 167 ✓ ✓ None High
|
||||
__Secure-3PSIDCC AKEyXzVeeDHS1lWGDCFuEFfj0Is6t0rrf7sdFyFo1MtTGdCCU8zHIEdbgvsv1lHyu7SApJif0AA .youtube.com / 2027-03-27T02:39:45.367Z 91 ✓ ✓ None High
|
||||
__Secure-3PSIDCC AKEyXzUChCCGljD9bILJOGLEfDcrdJaAiA7APxnV2QrTAW9glKnDQ0nbEYkOf-jR_dCUXkO7QKM .google.com / 2027-03-27T02:39:38.133Z 91 ✓ ✓ None High
|
||||
__Secure-3PSIDRTS sidts-CjYBWhotCVbg4zL386MEd1xPDnIrXetv9dZJvT7F0t8IruwIx6ZlfvbmmOFnYYLIOBUQlwwdCggQAA .google.com / 2026-03-27T02:46:46.685Z 101 ✓ ✓ None High
|
||||
__Secure-3PSIDTS sidts-CjQBWhotCR30ArqZTdTYPA9IaKLjiqxyQo3pFqyW0ahH99kAISY8Xx7eYW3SKziORlnuTslIEAA .youtube.com / 2027-03-27T02:39:38.398Z 97 ✓ ✓ None High
|
||||
__Secure-3PSIDTS sidts-CjYBWhotCVbg4zL386MEd1xPDnIrXetv9dZJvT7F0t8IruwIx6ZlfvbmmOFnYYLIOBUQlwwdCggQAA .google.com / 2027-03-27T02:36:46.685Z 100 ✓ ✓ None High
|
||||
__Secure-BUCKET CPYF .google.com / 2026-09-22T01:33:55.289Z 19 ✓ ✓ Medium
|
||||
__Secure-ROLLOUT_TOKEN CMbM-7XNqZzV5QEQmYbS_rO8kwMY_rSr_fW-kwM%3D .youtube.com / 2026-09-23T01:25:16.915Z 64 ✓ ✓ None https://youtube.com Medium
|
||||
AEC AaJma5uOowvEPsKVftwErbdidI19zMVl5C9-xa4RyR78tyxULUO9ytInv40 .google.com / 2026-09-22T01:50:18.899Z 62 ✓ ✓ Lax Medium
|
||||
APC AfxxVi5bzd9v-P9K_jo26VgiosIxzE813GYPLYyC5gmUMYjiaXg36w .doubleclick.net / 2026-09-22T01:24:54.796Z 57 ✓ None https://youtube.com ✓ Medium
|
||||
APISID OnSMY049lYtHikQy/A5cj2ru6POklKZoFC .google.com / 2027-05-01T01:26:55.647Z 40 High
|
||||
APISID OnSMY049lYtHikQy/A5cj2ru6POklKZoFC .google.co.id / 2027-05-01T01:26:56.717Z 40 High
|
||||
GPS 1 .youtube.com / 2026-03-27T03:09:36.573Z 4 ✓ ✓ Medium
|
||||
HSID AOTQRxoGPcLgaWqhk .google.com / 2027-05-01T01:26:55.647Z 21 ✓ High
|
||||
HSID AvGoSoPSULrXuoRT9 .google.co.id / 2027-05-01T01:26:56.717Z 21 ✓ High
|
||||
IDE AHWqTUmyGzw548Vz7XpBhz59fxCsE3RWJDZPUh3zrwecOjSQ1O8bujmlLDEeiEqCdE8 .doubleclick.net / 2027-04-30T02:35:52.982Z 70 ✓ ✓ None Medium
|
||||
LOGIN_INFO AFmmF2swQwIfXTYVZVbXCVUR4NikS0IeA8v1bJ89WfHBNfr5ipEBGQIgK6RN_6Ubyj7GXQEy9AxZ9tofqAHiELNQ9NP0xRUrZL0:QUQ3MjNmejU3NEZORFNHTk5MM1BBam9mTm9sTGlLVXRSVk1qa3ExU0dkRlFHMjZMSUNwZlZrRFRmcUIxNHZ4S1p5WG5FbkEzSnNhQ3NITVhxSW5NVVBoc3BaV1N1OTFBYXd4cFdMZ2NNcXEwMFY4UEZsVmZLckNWMGtOVUFyRW5DUEdtLU9KX3pyMGZDWUdZdWhzejlIUWwtOUlyOEdNUnN3 .youtube.com / 2027-05-01T02:39:38.516Z 326 ✓ ✓ None Medium
|
||||
|
||||
131
yt_engine.py
131
yt_engine.py
@@ -2,107 +2,134 @@ import yt_dlp
|
||||
import os
|
||||
import asyncio
|
||||
|
||||
# Buat folder penampungan sementara jika belum ada
|
||||
DOWNLOAD_DIR = "downloads"
|
||||
os.makedirs(DOWNLOAD_DIR, exist_ok=True)
|
||||
|
||||
def generate_netscape_cookies():
|
||||
"""
|
||||
Mengubah data tabel cookie (copy-paste dari tab Application browser)
|
||||
menjadi format baku Netscape HTTP Cookie agar bisa dibaca yt-dlp.
|
||||
"""
|
||||
if not os.path.exists("raw_cookie.txt"):
|
||||
print("⚠️ raw_cookie.txt tidak ditemukan. Lanjut tanpa cookies.")
|
||||
return False
|
||||
|
||||
try:
|
||||
with open("raw_cookie.txt", "r", encoding="utf-8") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
with open("cookies.txt", "w", encoding="utf-8") as f:
|
||||
f.write("# Netscape HTTP Cookie File\n")
|
||||
f.write("# This is a generated file! Do not edit.\n\n")
|
||||
|
||||
for line in lines:
|
||||
# Pisahkan kolom berdasarkan tombol Tab (\t) bawaan copy-paste tabel
|
||||
parts = line.split('\t')
|
||||
|
||||
# Pastikan baris tersebut valid (minimal punya Nama dan Value)
|
||||
if len(parts) >= 2:
|
||||
name = parts[0].strip()
|
||||
value = parts[1].strip()
|
||||
|
||||
# Abaikan jika yang ter-copy adalah Header tabelnya ("Name", "Value")
|
||||
if name.lower() == "name" or not name:
|
||||
continue
|
||||
|
||||
# Format baku Netscape: domain, subdomains, path, secure, expiration, name, value
|
||||
# Kita paksa domainnya .youtube.com agar yt-dlp bisa membacanya
|
||||
name, value = parts[0].strip(), parts[1].strip()
|
||||
if name.lower() == "name" or not name: continue
|
||||
f.write(f".youtube.com\tTRUE\t/\tTRUE\t2147483647\t{name}\t{value}\n")
|
||||
|
||||
print("✅ Cookies berhasil di-generate dari raw_cookie.txt")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"❌ Error saat memproses cookies: {e}")
|
||||
print(f"Error parsing cookies: {e}")
|
||||
return False
|
||||
|
||||
def fetch_recommendations_sync(video_id: str, limit: int = 30):
|
||||
ydl_opts_recs = {
|
||||
'quiet': True,
|
||||
'extract_flat': True,
|
||||
'playlistend': limit + 2,
|
||||
'extractor_args': {'youtube': ['player_client=android,ios']},
|
||||
}
|
||||
if os.path.exists("cookies.txt"):
|
||||
ydl_opts_recs['cookiefile'] = 'cookies.txt'
|
||||
try:
|
||||
with yt_dlp.YoutubeDL(ydl_opts_recs) as ydl:
|
||||
mix_url = f"https://www.youtube.com/watch?v={video_id}&list=RD{video_id}"
|
||||
info = ydl.extract_info(mix_url, download=False)
|
||||
recs = []
|
||||
if 'entries' in info:
|
||||
for entry in info['entries'][1:]:
|
||||
if entry and entry.get('id') and entry.get('title'):
|
||||
recs.append({'id': entry['id'], 'title': entry['title']})
|
||||
return recs
|
||||
except Exception as e:
|
||||
return []
|
||||
|
||||
async def get_recommendations(video_id: str):
|
||||
return await asyncio.to_thread(fetch_recommendations_sync, video_id)
|
||||
|
||||
def search_and_download(query_or_url: str):
|
||||
"""
|
||||
Fungsi utama untuk mencari dan mengunduh audio dari YouTube.
|
||||
Berjalan secara sinkronus.
|
||||
"""
|
||||
# 1. Konversi cookie setiap kali mau download
|
||||
has_cookies = generate_netscape_cookies()
|
||||
|
||||
# 2. Pengaturan yt-dlp
|
||||
ydl_opts = {
|
||||
'format': 'bestaudio/best/m4a/mp3',
|
||||
# ❌ FFMPEG POSTPROCESSORS DIHAPUS ❌
|
||||
# Langsung tembak format asli m4a (AAC) agar CPU tidak tersiksa
|
||||
'format': 'bestaudio[ext=m4a]/bestaudio/best',
|
||||
'outtmpl': f'{DOWNLOAD_DIR}/%(id)s.%(ext)s',
|
||||
'max_filesize': 45000000, # Batas aman Telegram 45MB
|
||||
'postprocessors': [{
|
||||
'key': 'FFmpegExtractAudio',
|
||||
'preferredcodec': 'mp3',
|
||||
'preferredquality': '128',
|
||||
}],
|
||||
'max_filesize': 45000000,
|
||||
'quiet': True,
|
||||
'noplaylist': True,
|
||||
'default_search': 'ytsearch1',
|
||||
'js_runtimes': {'node': {}},
|
||||
# 'extractor_args': {'youtube': ['player_client=android,ios']},
|
||||
'extractor_args': {'youtube': ['player_client=android,ios']},
|
||||
'js_runtimes': {'node': {}},
|
||||
}
|
||||
|
||||
# 3. Sisipkan cookie jika berhasil digenerate
|
||||
if has_cookies and os.path.exists("cookies.txt"):
|
||||
ydl_opts['cookiefile'] = 'cookies.txt'
|
||||
|
||||
try:
|
||||
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
||||
# Mulai proses ekstraksi & download
|
||||
info = ydl.extract_info(query_or_url, download=True)
|
||||
|
||||
# Jika hasil berupa playlist/pencarian, ambil item pertama
|
||||
if 'entries' in info:
|
||||
info = info['entries'][0]
|
||||
|
||||
video_id = info.get('id')
|
||||
title = info.get('title')
|
||||
duration = info.get('duration')
|
||||
|
||||
file_path = f"{DOWNLOAD_DIR}/{video_id}.mp3"
|
||||
# Ambil ekstensi aslinya (kemungkinan besar m4a)
|
||||
ext = info.get('ext', 'm4a')
|
||||
file_path = f"{DOWNLOAD_DIR}/{video_id}.{ext}"
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"video_id": video_id,
|
||||
"title": title,
|
||||
"duration": duration,
|
||||
"file_path": file_path if os.path.exists(file_path) else None
|
||||
"file_path": file_path if os.path.exists(file_path) else None,
|
||||
"ext": ext
|
||||
}
|
||||
except Exception as e:
|
||||
return {"status": "error", "message": str(e)}
|
||||
|
||||
async def process_youtube_request(query: str):
|
||||
"""
|
||||
Bungkus (Wrapper) Async agar bot Telegram tidak nge-hang
|
||||
saat menunggu proses download selesai.
|
||||
"""
|
||||
return await asyncio.to_thread(search_and_download, query)
|
||||
|
||||
|
||||
# ==========================================
|
||||
# FITUR TAMBAHAN: PENCARIAN LIST LAGU
|
||||
# ==========================================
|
||||
def fetch_search_results_sync(query: str, max_results: int = 30):
|
||||
"""Mengambil daftar hasil pencarian tanpa mendownload audionya"""
|
||||
ydl_opts_search = {
|
||||
'quiet': True,
|
||||
'extract_flat': True, # Mengambil metadata saja biar super cepat
|
||||
# KITA HAPUS extractor_args (player_client) di sini karena bikin search error
|
||||
}
|
||||
|
||||
if os.path.exists("cookies.txt"):
|
||||
ydl_opts_search['cookiefile'] = 'cookies.txt'
|
||||
|
||||
try:
|
||||
with yt_dlp.YoutubeDL(ydl_opts_search) as ydl:
|
||||
# Memaksa yt-dlp menggunakan mode pencarian (ytsearch) secara eksplisit
|
||||
search_query = f"ytsearch{max_results}:{query}"
|
||||
info = ydl.extract_info(search_query, download=False)
|
||||
|
||||
results = []
|
||||
if 'entries' in info:
|
||||
for entry in info['entries']:
|
||||
if entry and entry.get('id') and entry.get('title'):
|
||||
# Terkadang di hasil pencarian nama key-nya 'channel', bukan 'uploader'
|
||||
uploader_name = entry.get('uploader') or entry.get('channel') or 'Unknown'
|
||||
results.append({
|
||||
'id': entry['id'],
|
||||
'title': entry['title'],
|
||||
'uploader': uploader_name
|
||||
})
|
||||
return results
|
||||
except Exception as e:
|
||||
print(f"Search error: {e}")
|
||||
return []
|
||||
|
||||
async def search_youtube_list(query: str, max_results: int = 30):
|
||||
return await asyncio.to_thread(fetch_search_results_sync, query, max_results)
|
||||
|
||||
Reference in New Issue
Block a user