Spaces:
Paused
Paused
- lib/fb.js +2 -2
- lib/ig.js +1 -1
- lib/kuaishou.js +1 -1
- lib/qqm.js +1 -1
- lib/skrep.js +1 -1
- lib/snapchat.js +1 -1
- lib/soundcloud.js +1 -1
- lib/spotify.js +1 -1
- lib/twt.js +1 -1
- lib/ytdl.js +33 -56
lib/fb.js
CHANGED
|
@@ -79,10 +79,10 @@ async function fbdl(url) {
|
|
| 79 |
return { status: 200, title: "", thumbnail, video }
|
| 80 |
} catch (e) {
|
| 81 |
if (!(e instanceof SyntaxError)) throw e
|
| 82 |
-
return { status:
|
| 83 |
}
|
| 84 |
} catch (error) {
|
| 85 |
-
return { status:
|
| 86 |
}
|
| 87 |
}
|
| 88 |
|
|
|
|
| 79 |
return { status: 200, title: "", thumbnail, video }
|
| 80 |
} catch (e) {
|
| 81 |
if (!(e instanceof SyntaxError)) throw e
|
| 82 |
+
return { status: 500, error: true, message: eval(js.split(" = ")[1]) }
|
| 83 |
}
|
| 84 |
} catch (error) {
|
| 85 |
+
return { status: 500, error: true, message: "Failed to fetch data" }
|
| 86 |
}
|
| 87 |
}
|
| 88 |
|
lib/ig.js
CHANGED
|
@@ -64,7 +64,7 @@ async function igdl(url) {
|
|
| 64 |
return res;
|
| 65 |
} catch (err) {
|
| 66 |
console.error("[ERROR IG]\n\n", e);
|
| 67 |
-
return { status:
|
| 68 |
}
|
| 69 |
}
|
| 70 |
module.exports = igdl;
|
|
|
|
| 64 |
return res;
|
| 65 |
} catch (err) {
|
| 66 |
console.error("[ERROR IG]\n\n", e);
|
| 67 |
+
return { status: 500, message: err.message};
|
| 68 |
}
|
| 69 |
}
|
| 70 |
module.exports = igdl;
|
lib/kuaishou.js
CHANGED
|
@@ -105,7 +105,7 @@ async function validasi(url, maxRetries = 3) {
|
|
| 105 |
console.log("Gagal, mencoba ulang...");
|
| 106 |
}
|
| 107 |
console.error("Scraping gagal setelah 3 kali percobaan.");
|
| 108 |
-
return { status:
|
| 109 |
}
|
| 110 |
|
| 111 |
module.exports = validasi;
|
|
|
|
| 105 |
console.log("Gagal, mencoba ulang...");
|
| 106 |
}
|
| 107 |
console.error("Scraping gagal setelah 3 kali percobaan.");
|
| 108 |
+
return { status: 500, message: "eror"};
|
| 109 |
}
|
| 110 |
|
| 111 |
module.exports = validasi;
|
lib/qqm.js
CHANGED
|
@@ -80,7 +80,7 @@ async function getInfo(url) {
|
|
| 80 |
}))
|
| 81 |
});
|
| 82 |
} catch (e) {
|
| 83 |
-
reject({ status:
|
| 84 |
}
|
| 85 |
});
|
| 86 |
});
|
|
|
|
| 80 |
}))
|
| 81 |
});
|
| 82 |
} catch (e) {
|
| 83 |
+
reject({ status: 500, message: e.message});
|
| 84 |
}
|
| 85 |
});
|
| 86 |
});
|
lib/skrep.js
CHANGED
|
@@ -59,7 +59,7 @@ async function ttt(link) {
|
|
| 59 |
*/
|
| 60 |
return res;
|
| 61 |
} catch (e) {
|
| 62 |
-
return { status:
|
| 63 |
}
|
| 64 |
}
|
| 65 |
|
|
|
|
| 59 |
*/
|
| 60 |
return res;
|
| 61 |
} catch (e) {
|
| 62 |
+
return { status: 500, message: e.message}
|
| 63 |
}
|
| 64 |
}
|
| 65 |
|
lib/snapchat.js
CHANGED
|
@@ -41,7 +41,7 @@ async function spotlight(url) {
|
|
| 41 |
console.log("Tidak ditemukan data JSON yang sesuai.");
|
| 42 |
}
|
| 43 |
} catch (error) {
|
| 44 |
-
return { status:
|
| 45 |
console.error("Gagal mengambil data:", error.message);
|
| 46 |
}
|
| 47 |
}
|
|
|
|
| 41 |
console.log("Tidak ditemukan data JSON yang sesuai.");
|
| 42 |
}
|
| 43 |
} catch (error) {
|
| 44 |
+
return { status: 500, message: error.message}
|
| 45 |
console.error("Gagal mengambil data:", error.message);
|
| 46 |
}
|
| 47 |
}
|
lib/soundcloud.js
CHANGED
|
@@ -139,7 +139,7 @@ async function soundcloud(query, type) {
|
|
| 139 |
return resd
|
| 140 |
}
|
| 141 |
} catch (e) {
|
| 142 |
-
return { status:
|
| 143 |
}
|
| 144 |
}
|
| 145 |
|
|
|
|
| 139 |
return resd
|
| 140 |
}
|
| 141 |
} catch (e) {
|
| 142 |
+
return { status: 500, message: e.message}
|
| 143 |
}
|
| 144 |
}
|
| 145 |
|
lib/spotify.js
CHANGED
|
@@ -94,7 +94,7 @@ async function spotifyTrack(trackUrl) {
|
|
| 94 |
console.log(res)
|
| 95 |
return res
|
| 96 |
} catch (e) {
|
| 97 |
-
return { status:
|
| 98 |
}
|
| 99 |
}
|
| 100 |
|
|
|
|
| 94 |
console.log(res)
|
| 95 |
return res
|
| 96 |
} catch (e) {
|
| 97 |
+
return { status: 500, message: e.message}
|
| 98 |
}
|
| 99 |
}
|
| 100 |
|
lib/twt.js
CHANGED
|
@@ -44,7 +44,7 @@ async function getTwitterVideoInfo(url) {
|
|
| 44 |
|
| 45 |
resolve(res);
|
| 46 |
} catch (parseError) {
|
| 47 |
-
|
| 48 |
}
|
| 49 |
});
|
| 50 |
});
|
|
|
|
| 44 |
|
| 45 |
resolve(res);
|
| 46 |
} catch (parseError) {
|
| 47 |
+
return { status: 500, message: e.message}
|
| 48 |
}
|
| 49 |
});
|
| 50 |
});
|
lib/ytdl.js
CHANGED
|
@@ -1,20 +1,18 @@
|
|
| 1 |
const { spawn } = require("child_process");
|
| 2 |
const path = require("path");
|
| 3 |
-
const fs = require("fs");
|
| 4 |
|
| 5 |
-
const
|
| 6 |
-
const
|
| 7 |
-
const cookiesPath = path.join(__dirname, "./cookies/yt.txt");
|
| 8 |
|
| 9 |
function runYtDlp(args) {
|
| 10 |
return new Promise((resolve, reject) => {
|
| 11 |
const process = spawn(ytdlp, args, { stdio: ["ignore", "pipe", "pipe"] });
|
| 12 |
let stdout = "";
|
| 13 |
let stderr = "";
|
| 14 |
-
|
| 15 |
process.stdout.on("data", (data) => (stdout += data.toString()));
|
| 16 |
process.stderr.on("data", (data) => (stderr += data.toString()));
|
| 17 |
-
|
| 18 |
process.on("close", (code) => {
|
| 19 |
if (code === 0) resolve(stdout.trim());
|
| 20 |
else reject(new Error(stderr.trim() || `yt-dlp exited with code ${code}`));
|
|
@@ -22,63 +20,42 @@ function runYtDlp(args) {
|
|
| 22 |
});
|
| 23 |
}
|
| 24 |
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
try {
|
| 27 |
const result = await runYtDlp(["-j", "--cookies", cookiesPath, url]);
|
| 28 |
const info = JSON.parse(result);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
return {
|
| 30 |
-
|
| 31 |
-
title: info.title,
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
subscribers: info.channel_follower_count,
|
| 45 |
-
verified: !!info.channel_is_verified,
|
| 46 |
-
},
|
| 47 |
-
videos: [...new Set(info.formats.filter(v => v.video_ext === "mp4").map(v => v.height + "p"))],
|
| 48 |
};
|
| 49 |
} catch (err) {
|
| 50 |
throw new Error(`Failed to get video info: ${err.message}`);
|
| 51 |
}
|
| 52 |
}
|
| 53 |
|
| 54 |
-
|
| 55 |
-
const ts = Date.now();
|
| 56 |
-
const outputFile = `${tmp}/${ts}.mp4`;
|
| 57 |
-
try {
|
| 58 |
-
await runYtDlp(["-f", `ba[ext=m4a]+bv[height=${quality.slice(0, -1)}]`, "--merge-output-format", "mp4", "-o", outputFile, url]);
|
| 59 |
-
const result = fs.readFileSync(outputFile);
|
| 60 |
-
fs.unlinkSync(outputFile);
|
| 61 |
-
return result;
|
| 62 |
-
} catch (err) {
|
| 63 |
-
throw new Error(`Failed to download video: ${err.message}`);
|
| 64 |
-
}
|
| 65 |
-
}
|
| 66 |
-
|
| 67 |
-
async function getAudio(url) {
|
| 68 |
-
const ts = Date.now();
|
| 69 |
-
const outputFile = `${tmp}/${ts}.mp3`;
|
| 70 |
-
try {
|
| 71 |
-
await runYtDlp(["-f", "ba", "-o", outputFile, url]);
|
| 72 |
-
const result = fs.readFileSync(outputFile);
|
| 73 |
-
fs.unlinkSync(outputFile);
|
| 74 |
-
return result;
|
| 75 |
-
} catch (err) {
|
| 76 |
-
throw new Error(`Failed to download audio: ${err.message}`);
|
| 77 |
-
}
|
| 78 |
-
}
|
| 79 |
-
|
| 80 |
-
module.exports = {
|
| 81 |
-
getInfo,
|
| 82 |
-
getAudio,
|
| 83 |
-
getVideo,
|
| 84 |
-
};
|
|
|
|
| 1 |
const { spawn } = require("child_process");
|
| 2 |
const path = require("path");
|
|
|
|
| 3 |
|
| 4 |
+
const ytdlp = "yt-dlp"; // Asumsi yt-dlp sudah terinstall global di Docker
|
| 5 |
+
const cookiesPath = path.join(__dirname, "./cookies/twt.txt");
|
|
|
|
| 6 |
|
| 7 |
function runYtDlp(args) {
|
| 8 |
return new Promise((resolve, reject) => {
|
| 9 |
const process = spawn(ytdlp, args, { stdio: ["ignore", "pipe", "pipe"] });
|
| 10 |
let stdout = "";
|
| 11 |
let stderr = "";
|
| 12 |
+
|
| 13 |
process.stdout.on("data", (data) => (stdout += data.toString()));
|
| 14 |
process.stderr.on("data", (data) => (stderr += data.toString()));
|
| 15 |
+
|
| 16 |
process.on("close", (code) => {
|
| 17 |
if (code === 0) resolve(stdout.trim());
|
| 18 |
else reject(new Error(stderr.trim() || `yt-dlp exited with code ${code}`));
|
|
|
|
| 20 |
});
|
| 21 |
}
|
| 22 |
|
| 23 |
+
/**
|
| 24 |
+
* Mengambil metadata video Twitter menggunakan yt-dlp.
|
| 25 |
+
* @param {string} url - URL tweet yang berisi video.
|
| 26 |
+
* @returns {Promise<Object>} - Metadata video dalam format JSON.
|
| 27 |
+
*/
|
| 28 |
+
async function getTwitterVideoInfo(url) {
|
| 29 |
try {
|
| 30 |
const result = await runYtDlp(["-j", "--cookies", cookiesPath, url]);
|
| 31 |
const info = JSON.parse(result);
|
| 32 |
+
|
| 33 |
+
// Pastikan `formats` ada, jika tidak buat array kosong
|
| 34 |
+
const videoFormats = (info.formats || []).filter(
|
| 35 |
+
(f) => (f.video_ext !== "none" && f.audio_ext !== "none") || f.url.includes(".mp4")
|
| 36 |
+
);
|
| 37 |
+
|
| 38 |
+
const audioFormats = (info.formats || []).filter((f) => f.video_ext === "none" && f.audio_ext !== "none");
|
| 39 |
+
|
| 40 |
return {
|
| 41 |
+
status: 200,
|
| 42 |
+
title: info.title || "No Title",
|
| 43 |
+
description: info.description || "",
|
| 44 |
+
thumbnail: info.thumbnail || "",
|
| 45 |
+
video: videoFormats.map((v) => ({
|
| 46 |
+
format_id: v.format_id,
|
| 47 |
+
resolution: v.resolution || `${v.width}x${v.height}`,
|
| 48 |
+
url: v.url,
|
| 49 |
+
})),
|
| 50 |
+
audio: audioFormats.map((a) => ({
|
| 51 |
+
format_id: a.format_id,
|
| 52 |
+
bitrate: a.abr,
|
| 53 |
+
url: a.url,
|
| 54 |
+
})),
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
};
|
| 56 |
} catch (err) {
|
| 57 |
throw new Error(`Failed to get video info: ${err.message}`);
|
| 58 |
}
|
| 59 |
}
|
| 60 |
|
| 61 |
+
module.exports = getTwitterVideoInfo;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|