YanzzMiku / migrate.js
Yanzz Agent
Auto Init GitHub Repo
fdb23d5
Raw
History Blame Contribute Delete
8.93 kB
// Script Migrasi Anime JSON -> Supabase & GitHub Releases
// Menggunakan GitHub Releases sebagai "Hosting Video" gratis.
const { createClient } = require('@supabase/supabase-js');
const { Octokit } = require('octokit');
const fs = require('fs');
const path = require('path');
const axios = require('axios');
const { execSync } = require('child_process');
const mime = require('mime-types');
// --- HARDCODED KEYS (GITHUB) ---
const SUPABASE_URL = "https://ajeukqjcqweuofclpdjo.supabase.co";
const SUPABASE_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImFqZXVrcWpjcXdldW9mY2xwZGpvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzA5MTA5NjMsImV4cCI6MjA4NjQ4Njk2M30.DfSpKC2u3u3MKynG9gkZsG_a4M5_AtQbs9QI1OYpwZQ";
// --- GITHUB CONFIG (WAJIB DIGANTI) ---
const GH_TOKEN = "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"; // Token GitHub Tuan
const GH_OWNER = "yanzzv520-cyber"; // Username Pemilik Repo
const GH_REPO = "mikunime"; // Nama Repo
// Init
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
const octokit = new Octokit({ auth: GH_TOKEN });
// Config Clone
const PROJEK_REPO_URL = "https://github.com/Yanzz15/Projek.git";
const TEMP_DIR = "./temp_projek";
const JSON_DIR = path.join(TEMP_DIR, "anime_json");
async function main() {
console.log("πŸš€ Memulai Migrasi Anime (GITHUB RELEASES)...");
// 1. Cek & Init Repo GitHub (Wajib ada file biar bisa Release)
try {
await octokit.request('GET /repos/{owner}/{repo}/contents/{path}', {
owner: GH_OWNER,
repo: GH_REPO,
path: 'README.md'
});
console.log("βœ… Repo GitHub sudah siap (ada README.md).");
} catch (err) {
if (err.status === 404) {
console.log("⚠️ Repo GitHub kosong! Membuat README.md...");
try {
await octokit.request('PUT /repos/{owner}/{repo}/contents/{path}', {
owner: GH_OWNER,
repo: GH_REPO,
path: 'README.md',
message: 'Initial commit for Anime Vault',
content: Buffer.from('# Anime Vault\n\nTempat penyimpanan video anime.').toString('base64')
});
console.log("βœ… Berhasil inisialisasi Repo GitHub!");
} catch (createErr) {
console.error("❌ Gagal Init Repo:", createErr.message);
process.exit(1);
}
} else {
console.error("❌ Gagal Cek Repo GitHub:", err.message);
}
}
// 2. Clone Repo Projek (Sumber Data)
if (!fs.existsSync(TEMP_DIR)) {
console.log("πŸ“₯ Cloning Repo Projek...");
try { execSync(`git clone ${PROJEK_REPO_URL} ${TEMP_DIR}`); }
catch (e) { console.error("❌ Gagal Clone:", e.message); process.exit(1); }
}
// 2. Baca Semua Anime
const files = fs.readdirSync(JSON_DIR).filter(f => f.endsWith('.json'));
console.log(`πŸ“‘ Total Anime: ${files.length}`);
// 3. Loop Anime
for (const file of files) {
try {
const rawData = fs.readFileSync(path.join(JSON_DIR, file), 'utf-8');
const animeData = JSON.parse(rawData);
const cleanTitle = cleanMetadata(animeData.title);
const cleanSlug = cleanMetadataSlug(animeData.slug);
console.log(`
🎬 [${cleanTitle}] (${cleanSlug})`);
// Cek DB dulu (Skip kalau udah ada)
const { data: existingAnime } = await supabase
.from('animes')
.select('id')
.eq('slug', cleanSlug)
.maybeSingle();
if (existingAnime) {
console.log(`⏭️ Anime sudah ada di DB, skip.`);
continue;
}
// --- BUAT RELEASE BARU PER ANIME (Atau pakai release 'all-videos' biar simple?) ---
// Karena GitHub Release limit 2GB/asset, tapi 1 release bisa banyak asset.
// Biar rapi, kita buat 1 Release = 1 Anime. Tag = Slug Anime.
let releaseData;
try {
// Cek apakah release tag sudah ada?
releaseData = await octokit.request('GET /repos/{owner}/{repo}/releases/tags/{tag}', {
owner: GH_OWNER,
repo: GH_REPO,
tag: cleanSlug
});
console.log(`🏷️ Release '${cleanSlug}' sudah ada, pakai yang lama.`);
releaseData = releaseData.data;
} catch (err) {
if (err.status === 404) {
// Buat Release Baru
console.log(`✨ Membuat Release Baru: ${cleanSlug}`);
const newRelease = await octokit.request('POST /repos/{owner}/{repo}/releases', {
owner: GH_OWNER,
repo: GH_REPO,
tag_name: cleanSlug,
name: cleanTitle,
body: `Video streaming untuk anime ${cleanTitle}`,
draft: false,
prerelease: false
});
releaseData = newRelease.data;
} else {
console.error(`❌ Gagal Cek Release: ${err.message}`);
continue; // Skip anime ini kalau error GitHub
}
}
// --- UPLOAD VIDEO KE RELEASE ---
let allEpisodes = [];
const episodes = animeData.episodes || [];
for (const ep of episodes) {
console.log(`πŸ“Ή Processing Ep ${ep.episode}...`);
let epMetadata = {
number: ep.episode,
title: cleanMetadata(ep.title),
streams: { "480p": null, "720p": null }
};
// Proses Upload (480p & 720p)
if (ep.streams?.["480p"]) {
epMetadata.streams["480p"] = await uploadToGithub(ep.streams["480p"], releaseData, `ep${ep.episode}-480p.mp4`);
}
if (ep.streams?.["720p"]) {
epMetadata.streams["720p"] = await uploadToGithub(ep.streams["720p"], releaseData, `ep${ep.episode}-720p.mp4`);
}
allEpisodes.push(epMetadata);
}
// --- SIMPAN KE SUPABASE ---
const { error: insertError } = await supabase
.from('animes')
.insert({
slug: cleanSlug,
title: cleanTitle,
poster: animeData.poster,
synopsis: animeData.synopsis,
metadata: { original_url: animeData.url },
episodes: allEpisodes
});
if (insertError) console.error(`❌ Gagal Insert DB: ${insertError.message}`);
else console.log(`πŸ’Ύ Anime & Episodes tersimpan di DB!`);
} catch (err) {
console.error(`❌ Error File ${file}:`, err.message);
}
}
console.log("πŸŽ‰ SELESAI SEMUA!");
}
// --- FUNGSI BANTUAN ---
function cleanMetadata(str) {
if (!str) return "";
return str.replace(/Subtitle Indonesia/gi, '').replace(/Sub Indo/gi, '').replace(/\(END\)/gi, '').trim();
}
function cleanMetadataSlug(str) {
if (!str) return "";
return str.replace(/-subtitle-indonesia/gi, '').replace(/-sub-indo/gi, '').replace(/-end-/gi, '-').replace(/-$/gi, '').trim();
}
async function uploadToGithub(url, release, filename) {
// Cek apakah asset sudah ada di release ini? (Biar gak double upload)
const existingAsset = release.assets?.find(a => a.name === filename);
if (existingAsset) {
// console.log(`⏩ Asset ${filename} sudah ada.`);
return existingAsset.browser_download_url;
}
try {
// Download Video ke Memory
const response = await axios({
method: 'get',
url: url,
responseType: 'arraybuffer',
timeout: 60000,
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Referer': 'https://x3.sokuja.uk/'
}
});
// Upload ke GitHub Release
// console.log(`πŸ“€ Uploading ${filename} (${response.data.byteLength} bytes)...`);
const upload = await octokit.request('POST ' + release.upload_url, {
headers: {
'content-type': 'video/mp4',
'content-length': response.data.byteLength
},
name: filename,
data: response.data
});
return upload.data.browser_download_url;
} catch (error) {
console.error(`⚠️ Gagal Upload GitHub ${filename}: ${error.message}`);
return null;
}
}
main();