File size: 4,793 Bytes
ece4c92
 
 
 
1a7be53
6a31a3b
ece4c92
 
 
 
 
 
 
1a7be53
ece4c92
 
1a7be53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ffe37d1
1a7be53
 
 
ffe37d1
 
1a7be53
 
 
ffe37d1
1a7be53
 
 
 
ece4c92
6a31a3b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ece4c92
 
 
ffe37d1
ece4c92
 
 
ffe37d1
ece4c92
1a7be53
 
ece4c92
 
1a7be53
 
 
 
 
ece4c92
 
1a7be53
6a31a3b
 
 
 
 
 
 
ece4c92
1a7be53
6a31a3b
1a7be53
ece4c92
6a31a3b
c6e5396
 
 
6a31a3b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ece4c92
 
 
ffe37d1
ece4c92
 
 
 
ffe37d1
ece4c92
 
 
 
ffe37d1
ece4c92
 
 
 
c6e5396
ece4c92
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
const express = require("express");
const axios = require("axios");
const fs = require("fs");
const path = require("path");
const https = require("https");
const { exec } = require("child_process"); // For FFmpeg execution

const app = express();
const PORT = 7860;
const DOWNLOAD_DIR = path.join(__dirname, "downloads");

// Ensure the downloads directory exists
if (!fs.existsSync(DOWNLOAD_DIR)) {
    fs.mkdirSync(DOWNLOAD_DIR, { recursive: true });
}

// Axios instance to ignore SSL certificate verification
const axiosInstance = axios.create({
    httpsAgent: new https.Agent({ rejectUnauthorized: false })
});

// Function to extract filename from URL or headers
const getFileName = (fileUrl, headers) => {
    let filename = path.basename(new URL(fileUrl).pathname);

    if (headers["content-disposition"]) {
        const match = headers["content-disposition"].match(/filename="(.+)"/);
        if (match) {
            filename = match[1];
        }
    }

    return filename.replace(/[<>:"/\\|?*]+/g, ""); // Remove invalid characters
};

// Function to delete file after a delay (default: 10 minutes)
const scheduleFileDeletion = (filePath, delay = 600000) => {
    setTimeout(() => {
        fs.unlink(filePath, (err) => {
            if (!err) {
                console.log(`βœ… Deleted file: ${filePath}`);
            }
        });
    }, delay);
};

// Function to convert MKV to MP4 using FFmpeg
const convertToMp4 = (inputPath, outputPath) => {
    return new Promise((resolve, reject) => {
        const command = `ffmpeg -i "${inputPath}" -c:v copy -c:a aac -strict experimental "${outputPath}"`;
        exec(command, (error, stdout, stderr) => {
            if (error) {
                console.error(`❌ FFmpeg error: ${error.message}`);
                reject(error);
            } else {
                console.log(`βœ… Conversion complete: ${outputPath}`);
                resolve(outputPath);
            }
        });
    });
};

// API Route to download, convert, and serve .mp4 files
app.get("/download", async (req, res) => {
    const fileUrl = req.query.url;
    if (!fileUrl) {
        return res.status(400).json({ error: "❌ Missing URL parameter" });
    }

    try {
        console.log(`⬇️ Downloading: ${fileUrl}`);

        // Request file with forced download handling
        const response = await axiosInstance({
            url: fileUrl,
            method: "GET",
            responseType: "stream",
            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",
                "Accept": "*/*"
            }
        });

        // Extract correct filename
        const originalFilename = getFileName(fileUrl, response.headers);
        const originalFilePath = path.join(DOWNLOAD_DIR, originalFilename);

        // Determine new filename if conversion is needed
        const isMkv = originalFilename.toLowerCase().endsWith(".mkv");
        const mp4Filename = isMkv ? originalFilename.replace(/\.mkv$/, ".mp4") : originalFilename;
        const mp4FilePath = path.join(DOWNLOAD_DIR, mp4Filename);

        // Pipe response stream to file
        const writer = fs.createWriteStream(originalFilePath);
        response.data.pipe(writer);

        writer.on("finish", async () => {
            // Dynamically get the host URL from request headers
            const hostUrl = `${req.protocol}://${req.get("host")}`;
            
            if (isMkv) {
                // Convert MKV to MP4
                try {
                    await convertToMp4(originalFilePath, mp4FilePath);
                    fs.unlinkSync(originalFilePath); // Remove original MKV after conversion
                } catch (error) {
                    return res.status(500).json({ error: "❌ Conversion failed" });
                }
            }

            const servedUrl = `${hostUrl}/files/${mp4Filename}`;
            scheduleFileDeletion(mp4FilePath); // Auto-delete after 10 minutes

            console.log(`βœ… Download & conversion complete: ${servedUrl}`);
            return res.json({ message: "Download & conversion complete", fileUrl: servedUrl });
        });

        writer.on("error", (err) => {
            console.error("❌ Error writing file:", err);
            return res.status(500).json({ error: "Error saving file" });
        });

    } catch (error) {
        console.error("❌ Download error:", error.message);
        return res.status(500).json({ error: "Failed to download file" });
    }
});

// Serve files from downloads directory (publicly accessible)
app.use("/files", express.static(DOWNLOAD_DIR));

// Start the server
app.listen(PORT, () => {
    console.log(`πŸš€ Server running on port ${PORT}`);
});