import axios from 'axios';
import fs from 'fs';
import path from 'path';
import os from 'os';
import crypto from 'crypto';
import { setWallpaper } from 'wallpaper';
import readline from 'readline';
import { SocksProxyAgent } from 'socks-proxy-agent';
const UNSPLASH_API_URL = '
https://api.unsplash.com/photos/random';const ACCESS_KEY = 'Z_GQ0Jd3V27mew6lRc1MB-1ojS0e9s9W7B5FPN6XoZc'; // Replace with your Unsplash access key
const INTERVAL = 5 * 60 * 1000; // 5 minutes
const DOWNLOAD_TIMEOUT = 60 * 1000; // 1 minute
const THREAD_COUNT = 4; // Number of threads for parallel download
const RETRY_LIMIT = 3; // Retry limit for failed downloads
const PROGRESS_TIMEOUT = 10 * 1000; // 10 seconds timeout for progress check
const MIN_SPEED = 10; // Minimum speed in KB/s to consider as stalled
// SOCKS5 Proxy setup
const proxyUrl = 'socks5h://127.0.0.1:1080'; // Replace with your SOCKS5 proxy server address and port
const agent = new SocksProxyAgent(proxyUrl);
// Keywords list
const originalKeywords = [
'mountain', 'africa', 'middle east', 'snow', 'travel',
'Caribbean', 'Hubble Space Telescope', 'roman',
'Soviets', 'Aegean Sea',
'Bahrain', 'Eritrea', 'Iran', 'Iraq', 'Israel',
'Jordan', 'Kuwait', 'Lebanon', 'Oman', 'Qatar',
'Saudi Arabia', 'Syria', 'United Arab Emirates', 'Yemen',
// Europe
'Albania', 'Andorra', 'Austria', 'Belarus', 'Belgium',
'Bosnia and Herzegovina', 'Bulgaria', 'Iceland', 'Ireland',
'Italy', 'Kosovo', 'Latvia', 'Lithuania', 'Luxembourg',
'Malta', 'Monaco', 'Moldova', 'Norway', 'Netherlands',
'Portugal', 'Romania', 'Russia', 'San Marino', 'Serbia',
'Cyprus', 'Slovakia', 'Slovenia', 'Spain', 'Switzerland',
'Ukraine', 'United Kingdom', 'Vatican City',
// Asia
'Afghanistan', 'United Arab Emirates', 'Armenia', 'China',
'Georgia', 'India', 'Indonesia', 'Iran', 'Iraq',
'Israel', 'Japan', 'Jordan', 'Kazakhstan', 'South Korea',
'Kuwait', 'Kyrgyzstan', 'Lebanon', 'Maldives', 'Mongolia',
'Myanmar', 'Nepal', 'Macau', 'Malaysia', 'Pakistan',
'Philippines', 'Russia', 'Saudi Arabia', 'Singapore',
'Sri Lanka', 'Syria', 'Tajikistan', 'Thailand', 'Timor-Leste',
'Turkey', 'Turkmenistan', 'Uzbekistan', 'Yemen',
// Caribbean and South America
'Antigua and Barbuda', 'Bahamas', 'Barbados', 'Bolivia',
'Colombia', 'Cuba', 'Dominica', 'Dominican Republic',
'Grenada', 'Guyana', 'Haiti', 'Honduras', 'Jamaica',
'Nicaragua', 'Paraguay', 'Peru', 'Saint Kitts and Nevis',
'Saint Lucia', 'Saint Vincent and the Grenadines', 'Suriname',
'Trinidad and Tobago', 'Venezuela'
];
// Create a copy of the original keywords to keep track of unused ones
let unusedKeywords = [...originalKeywords];
// Function to generate a random hash
function generateHash(length = 8) {
return crypto.randomBytes(length).toString('hex');
}
// Function to get the path for temporary file
function getTempFilePath(partIndex) {
const tempDir = os.tmpdir();
return path.join(tempDir, `wallpaper_part${partIndex}.tmp`);
}
// Function to get a random keyword
function getRandomKeyword() {
if (unusedKeywords.length === 0) {
unusedKeywords = [...originalKeywords]; // Reset the list when all keywords have been used
console.log('All keywords used, resetting keyword list.');
}
const randomIndex = Math.floor(Math.random() * unusedKeywords.length);
const keyword = unusedKeywords[randomIndex];
unusedKeywords.splice(randomIndex, 1); // Remove the keyword from the list
return keyword;
}
async function downloadPart(photoUrl, start, end, partIndex, totalLength, downloadedParts, startTime) {
let retryCount = 0;
let lastProgress = 0;
let progressTimer;
const tempFilePath = getTempFilePath(partIndex);
// Read previous progress
if (fs.existsSync(tempFilePath)) {
downloadedParts[partIndex] = fs.statSync(tempFilePath).size;
}
while (retryCount < RETRY_LIMIT) {
try {
let currentStart = start + downloadedParts[partIndex];
const response = await axios.get(photoUrl, {
headers: {
'Range': `bytes=${currentStart}-${end}`,
},
responseType: 'arraybuffer',
httpAgent: agent, // Apply the SOCKS5 agent
httpsAgent: agent, // Apply the SOCKS5 agent
onDownloadProgress: (progressEvent) => {
const newBytes = progressEvent.loaded - downloadedParts[partIndex];
downloadedParts[partIndex] += newBytes;
updateProgress(downloadedParts, totalLength, startTime);
const elapsedTime = (Date.now() - startTime) / 1000; // in seconds
const speed = (newBytes / 1024 / elapsedTime).toFixed(2); // in KB/s
// Reset progress timer on new data
clearTimeout(progressTimer);
progressTimer = setTimeout(() => {
if (downloadedParts[partIndex] <= lastProgress || speed < MIN_SPEED) {
console.log(`\nPart ${partIndex + 1} is stalled or too slow, retrying...`);
retryCount++;
if (retryCount >= RETRY_LIMIT) {
throw new Error(`Failed to download part ${partIndex + 1} after ${RETRY_LIMIT} attempts.`);
}
lastProgress = downloadedParts[partIndex];
downloadPart(photoUrl, start, end, partIndex, totalLength, downloadedParts, startTime);
}
}, PROGRESS_TIMEOUT);
}
});
// Append data to temp file
fs.writeFileSync(tempFilePath, response.data, { flag: 'a' });
clearTimeout(progressTimer);
break; // Exit the loop if download was successful
} catch (error) {
console.error(`Part ${partIndex + 1} failed, retrying... (${retryCount}/${RETRY_LIMIT})`);
if (retryCount >= RETRY_LIMIT) {
throw new Error(`Failed to download part ${partIndex + 1} after ${RETRY_LIMIT} attempts.`);
}
}
}
}
async function downloadRandomPhoto() {
console.log('Attempting to download a new wallpaper...');
try {
const keyword = getRandomKeyword(); // Select a random keyword
console.log(`Using keyword: ${keyword}`);
const response = await axios.get(UNSPLASH_API_URL, {
headers: { Authorization: `Client-ID ${ACCESS_KEY}` },
params: {
query: keyword,
},
httpAgent: agent, // Apply the SOCKS5 agent
httpsAgent: agent, // Apply the SOCKS5 agent
});
console.log('Received response from Unsplash API.');
const photoUrl = response.data.urls.full;
console.log(`Photo URL: ${photoUrl}`);
const headResponse = await axios.head(photoUrl, { httpAgent: agent, httpsAgent: agent });
const totalLength = parseInt(headResponse.headers['content-length'], 10);
const tempDir = os.tmpdir();
const hash = generateHash();
const fileName = path.join(tempDir, `wallpaper_${hash}.jpg`);
console.log(`Total size: ${totalLength} bytes`);
console.log(`Saving photo to: ${fileName}`);
const partSize = Math.ceil(totalLength / THREAD_COUNT);
const downloadedParts = new Array(THREAD_COUNT).fill(0);
let startTime = Date.now();
const promises = [];
for (let i = 0; i < THREAD_COUNT; i++) {
const start = i * partSize;
const end = Math.min(start + partSize - 1, totalLength - 1);
console.log(`Downloading part ${i + 1}: bytes ${start}-${end}`);
const promise = downloadPart(photoUrl, start, end, i, totalLength, downloadedParts, startTime);
promises.push(promise);
}
await Promise.all(promises);
// Combine all parts into a single file
const writeStream = fs.createWriteStream(fileName);
for (let i = 0; i < THREAD_COUNT; i++) {
const tempFilePath = getTempFilePath(i);
const data = fs.readFileSync(tempFilePath);
writeStream.write(data);
fs.unlinkSync(tempFilePath); // Delete part file after merging
}
writeStream.end();
console.log('\nPhoto download complete. Setting wallpaper...');
setWallpaper(fileName).then(() => {
console.log('Wallpaper updated successfully!');
}).catch(err => {
console.error('Failed to set wallpaper:', err);
});
} catch (error) {
console.error('Error in downloadRandomPhoto function:', error);
}
}
function updateProgress(downloadedParts, totalLength, startTime) {
const downloadedLength = downloadedParts.reduce((acc, val) => acc + val, 0);
const percent = ((downloadedLength / totalLength) * 100).toFixed(2);
const elapsedTime = (Date.now() - startTime) / 1000; // in seconds
const speed = (downloadedLength / 1024 / elapsedTime).toFixed(2); // in KB/s
readline.clearLine(process.stdout, 0);
readline.cursorTo(process.stdout, 0);
process.stdout.write(`Download progress: ${percent}% | Speed: ${speed} KB/s`);
}
downloadRandomPhoto();
setInterval(() => {
console.log('Starting new wallpaper update cycle...');
downloadRandomPhoto();
}, INTERVAL);