dramabox.mjs
javascript•Created 5 Des 2025, 07:09•260 views
Short drama, directly using dramaboxdb api server.
#utility#video#downloader
javascript
0 lines
/***
@ Base: https://dramabox.web.id/
@ Author: Shannz
@ Desc: Short drama, directly using dramaboxdb api server
***/
import axios from 'axios';
import * as cheerio from 'cheerio';
const CONFIG = {
BASE_URL: 'https://dramabox.web.id',
HEADERS: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
};
const request = async (url) => {
try {
const response = await axios.get(url, { headers: CONFIG.HEADERS });
return cheerio.load(response.data);
} catch (error) {
throw new Error(`Network Error: ${error.message}`);
}
};
const resolveUrl = (link) => {
if (link && !link.startsWith('http')) {
return `${CONFIG.BASE_URL}/${link.replace(/^\//, '')}`;
}
return link;
};
const getBookIdFromUrl = (urlStr) => {
try {
const match = urlStr.match(/\/watch\/(\d+)/);
if (match) return match[1];
const urlObj = new URL(urlStr);
return urlObj.searchParams.get('bookId');
} catch (e) {
return null;
}
};
export const dramabox = {
home: async () => {
const $ = await request(`${CONFIG.BASE_URL}/in`);
const latest = [];
$('.drama-grid .drama-card').each((_, el) => {
const link = resolveUrl($(el).find('.watch-button').attr('href'));
const episodes = $(el).find('.drama-meta span[itemprop="numberOfEpisodes"]').text().replace(/[^0-9]/g, '');
latest.push({
title: $(el).find('.drama-title').text().trim(),
book_id: getBookIdFromUrl(link),
image: $(el).find('.drama-image img').attr('src') || $(el).find('.drama-image img').attr('data-src'),
episodes: episodes
});
});
const trending = [];
$('.sidebar-widget .rank-list .rank-item').each((_, el) => {
const link = resolveUrl($(el).attr('href'));
const episodes = $(el).find('.rank-meta span').text().replace(/[^0-9]/g, '');
trending.push({
rank: $(el).find('.rank-number').text().trim(),
title: $(el).find('.rank-title').text().trim(),
book_id: getBookIdFromUrl(link),
image: $(el).find('.rank-image img').attr('src') || $(el).find('.rank-image img').attr('data-src'),
episodes: episodes
});
});
return { latest, trending };
},
search: async (query) => {
const targetUrl = `${CONFIG.BASE_URL}/search.php?lang=in&q=${encodeURIComponent(query)}`;
const $ = await request(targetUrl);
const results = [];
$('.drama-grid .drama-card').each((_, el) => {
const link = resolveUrl($(el).find('.watch-button').attr('href'));
const viewsRaw = $(el).find('.drama-meta span').first().text().trim();
results.push({
title: $(el).find('.drama-title').text().trim(),
book_id: getBookIdFromUrl(link),
views: viewsRaw,
image: $(el).find('.drama-image img').attr('src') || $(el).find('.drama-image img').attr('data-src')
});
});
return results;
},
detail: async (bookId) => {
if (!bookId) throw new Error("Book ID is required");
const targetUrl = `${CONFIG.BASE_URL}/watch/${bookId}`;
const $ = await request(targetUrl);
const fullTitle = $('.video-title').text().trim();
const cleanTitle = fullTitle.split('- Episode')[0].trim();
const episodes = [];
$('.episodes-grid .episode-btn').each((_, el) => {
episodes.push({
episode: parseInt($(el).text().trim()),
id: $(el).attr('data-episode')
});
});
const followersRaw = $('.video-meta span').first().text().trim();
const totalEpRaw = $('span[itemprop="numberOfEpisodes"]').text().replace(/[^0-9]/g, '');
return {
book_id: bookId,
title: cleanTitle,
description: $('.video-description').text().trim(),
thumbnail: $('meta[itemprop="thumbnailUrl"]').attr('content'),
upload_date: $('meta[itemprop="uploadDate"]').attr('content'),
stats: {
followers: followersRaw,
total_episodes: totalEpRaw,
},
episode_list: episodes
};
},
stream: async (bookId, episode) => {
if (!bookId || episode === undefined || episode === null) {
throw new Error("Book ID and Episode are required");
}
const epPath = episode == 0 ? '' : `/ep-${episode}`;
const targetUrl = `${CONFIG.BASE_URL}/watch/${bookId}${epPath}`;
const $ = await request(targetUrl);
const videoUrls = [];
const rawHtml = $.html();
const qualitiesRegex = /const\s+initialQualities\s*=\s*(\[.*?\]);/s;
const match = rawHtml.match(qualitiesRegex);
if (match && match[1]) {
try {
const qualitiesData = JSON.parse(match[1]);
qualitiesData.forEach(item => {
if (item.quality && item.videoPath) {
videoUrls.push({
quality: `${item.quality}p`,
url: item.videoPath
});
}
});
} catch (error) {
console.error("Gagal parsing JSON kualitas, lanjut ke metode fallback.");
}
}
if (videoUrls.length === 0) {
$('#qualityMenu .quality-option').each((_, el) => {
const quality = $(el).attr('data-quality');
const url = $(el).attr('data-url');
if (quality && url) {
videoUrls.push({
quality: `${quality}p`,
url: url
});
}
});
}
if (videoUrls.length === 0) {
let fallbackUrl = $('#mainVideo source').attr('src') ||
$('#mainVideo').attr('data-hls-url') ||
$('#mainVideo').attr('src');
if (fallbackUrl) {
videoUrls.push({
quality: 'default',
url: fallbackUrl
});
}
}
return {
book_id: bookId,
episode: episode,
videos: videoUrls
};
}
};
/*
(async () => {
// Get Home
console.log("Fetching Home...");
const home = await dramabox.home();
console.log("Result Home:", home);
// Search Drama
console.log("Searching Drama...");
const search = await dramabox.search('ceo');
console.log("Result Search:", search);
// Detail Drama
console.log("Detail Drama...");
const detail = await dramabox.detail(41000103051);
console.log("Result Detail:", detail);
// Get Stream Drama
console.log("Fetching Stream Episode...");
const stream = await dramabox.stream(41000103051, 1)
console.log("Result Stream Episode:", stream);
})();
*/