From c2025cfdf07a56859c447a4eac7e1546f4a168ae Mon Sep 17 00:00:00 2001 From: Biswakalyan Bhuyan Date: Fri, 28 Jun 2024 12:31:31 +0530 Subject: arranged dir --- .gitignore | 1 + client/package-lock.json | 42 +++++++++++++ client/package.json | 1 + client/src/App.js | 102 +++++++++++++++++++++++--------- client/src/Influencers.css | 22 +++++++ client/src/Influencers.js | 102 ++++++++++++++++++++++++++++++++ download_script.py | 25 -------- package-lock.json | 13 +++++ package.json | 1 + server.js | 102 -------------------------------- server/download_script.py | 25 ++++++++ server/server.js | 143 +++++++++++++++++++++++++++++++++++++++++++++ 12 files changed, 425 insertions(+), 154 deletions(-) create mode 100644 client/src/Influencers.css create mode 100644 client/src/Influencers.js delete mode 100644 download_script.py delete mode 100644 server.js create mode 100644 server/download_script.py create mode 100644 server/server.js diff --git a/.gitignore b/.gitignore index 1a0b59b..494c48a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules downloads credentials.json influencers.json +.env diff --git a/client/package-lock.json b/client/package-lock.json index a6aacf5..a6fa0c1 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -14,6 +14,7 @@ "axios": "^1.7.2", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-router-dom": "^6.24.0", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" } @@ -3562,6 +3563,15 @@ } } }, + "node_modules/@remix-run/router": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.17.0.tgz", + "integrity": "sha512-2D6XaHEVvkCn682XBnipbJjgZUU7xjLtA4dGJRBVUKpEaDYOZMENZoZjAOSb7qirxt5RupjzZxz4fK2FO+EFPw==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -16288,6 +16298,38 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.24.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.24.0.tgz", + "integrity": "sha512-sQrgJ5bXk7vbcC4BxQxeNa5UmboFm35we1AFK0VvQaz9g0LzxEIuLOhHIoZ8rnu9BO21ishGeL9no1WB76W/eg==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.17.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.24.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.24.0.tgz", + "integrity": "sha512-960sKuau6/yEwS8e+NVEidYQb1hNjAYM327gjEyXlc6r3Skf2vtwuJ2l7lssdegD2YjoKG5l8MsVyeTDlVeY8g==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.17.0", + "react-router": "6.24.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", diff --git a/client/package.json b/client/package.json index b660797..70e70ca 100644 --- a/client/package.json +++ b/client/package.json @@ -9,6 +9,7 @@ "axios": "^1.7.2", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-router-dom": "^6.24.0", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" }, diff --git a/client/src/App.js b/client/src/App.js index eaeca2c..2d19281 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -1,9 +1,13 @@ import React, { useEffect, useState } from 'react'; +import { BrowserRouter as Router, Route, Link, Routes } from 'react-router-dom'; import axios from 'axios'; import './App.css'; +import Influencers from './Influencers'; function App() { const [downloadedFiles, setDownloadedFiles] = useState([]); + const [isAuthenticated, setIsAuthenticated] = useState(false); + const [adminPassword, setAdminPassword] = useState(''); useEffect(() => { fetchDownloadedFiles(); @@ -22,35 +26,79 @@ function App() { } }; + const handleLogin = (password) => { + setAdminPassword(password); + setIsAuthenticated(true); + }; + return ( -
-
-

Insta Local

-
- {downloadedFiles.length === 0 ? ( -

No content available

- ) : ( - downloadedFiles.map((file, index) => { - const fileUrl = `http://localhost:5001/static/${file}`; - console.log('Fetching file:', fileUrl); // Log the file URL - return ( -
- {file.endsWith('.mp4') ? ( - - ) : ( - {file} - )} -
- ); - }) - )} -
-
-
+ +
+
+

Insta Local

+ +
+ + + {downloadedFiles.length === 0 ? ( +

No content available

+ ) : ( + downloadedFiles.map((file, index) => { + const fileUrl = `http://localhost:5001/static/${file}`; + return ( +
+ {file.endsWith('.mp4') ? ( + + ) : ( + {file} + )} +
+ ); + }) + )} +
+ } /> + + ) : ( + + ) + } /> + + +
); } +const Login = ({ onLogin }) => { + const [password, setPassword] = useState(''); + + const handleSubmit = (event) => { + event.preventDefault(); + onLogin(password); + }; + + return ( +
+ + +
+ ); +}; + export default App; diff --git a/client/src/Influencers.css b/client/src/Influencers.css new file mode 100644 index 0000000..443762e --- /dev/null +++ b/client/src/Influencers.css @@ -0,0 +1,22 @@ +.influencers { + padding: 20px; +} + +.password-input, .add-profile, .export-import { + margin-bottom: 20px; +} + +.profile-list ul { + list-style-type: none; + padding: 0; +} + +.profile-list li { + display: flex; + justify-content: space-between; + margin-bottom: 10px; +} + +button { + margin-left: 10px; +} diff --git a/client/src/Influencers.js b/client/src/Influencers.js new file mode 100644 index 0000000..a72d654 --- /dev/null +++ b/client/src/Influencers.js @@ -0,0 +1,102 @@ +import React, { useState, useEffect, useCallback } from 'react'; +import axios from 'axios'; + +function Influencers({ adminPassword }) { + const [profiles, setProfiles] = useState([]); + const [newProfile, setNewProfile] = useState(''); + + const fetchProfiles = useCallback(async () => { + try { + const response = await axios.get('http://localhost:5001/profiles', { + headers: { Authorization: `Bearer ${adminPassword}` } + }); + console.log('Fetched profiles:', response.data); + setProfiles(response.data); + } catch (error) { + console.error('Error fetching profiles:', error); + } + }, [adminPassword]); + + useEffect(() => { + fetchProfiles(); + }, [fetchProfiles]); + + const addProfile = async () => { + try { + await axios.post( + 'http://localhost:5001/profiles', + { username: newProfile }, + { headers: { Authorization: `Bearer ${adminPassword}` } } + ); + setNewProfile(''); + fetchProfiles(); + } catch (error) { + console.error('Error adding profile:', error); + } + }; + + const handleJsonUpload = async (event) => { + const file = event.target.files[0]; + if (file) { + const formData = new FormData(); + formData.append('file', file); + + try { + await axios.post('http://localhost:5001/upload', formData, { + headers: { + 'Content-Type': 'multipart/form-data', + Authorization: `Bearer ${adminPassword}` + } + }); + fetchProfiles(); + } catch (error) { + console.error('Error uploading JSON file:', error); + } + } + }; + + const exportJson = async () => { + try { + const response = await axios.get('http://localhost:5001/export', { + headers: { Authorization: `Bearer ${adminPassword}` }, + responseType: 'blob' + }); + const url = window.URL.createObjectURL(new Blob([response.data])); + const link = document.createElement('a'); + link.href = url; + link.setAttribute('download', 'influencers.json'); + document.body.appendChild(link); + link.click(); + } catch (error) { + console.error('Error exporting JSON file:', error); + } + }; + + return ( +
+

Add Influencers

+ setNewProfile(e.target.value)} + placeholder="Enter Instagram username" + /> + +

Current Influencers

+ +

Import/Export Influencers

+ + +
+ ); +} + +export default Influencers; diff --git a/download_script.py b/download_script.py deleted file mode 100644 index a9d07c4..0000000 --- a/download_script.py +++ /dev/null @@ -1,25 +0,0 @@ - -import instaloader -import os - -L = instaloader.Instaloader() -L.login('xtester2', 'teTeD7QojvhOdvEGa50u6FFJo') -PROFILES = ["aao.remote.ho.jaye"] -BASE_DIR = "/home/genos/imp/work/project/insta/downloads" - -os.makedirs(BASE_DIR, exist_ok=True) - -def download_posts(profile): - profile_dir = os.path.join(BASE_DIR, profile) - os.makedirs(profile_dir, exist_ok=True) - L.dirname_pattern = profile_dir - posts = instaloader.Profile.from_username(L.context, profile).get_posts() - for post in posts: - if post.typename == 'GraphVideo': # Only download videos - L.download_post(post, target=profile) - elif post.typename == 'GraphImage': # Only download images - L.download_post(post, target=profile) - -for profile in PROFILES: - download_posts(profile) - \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1e51c34..773fac2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "axios": "^1.7.2", "cors": "^2.8.5", + "dotenv": "^16.4.5", "express": "^4.19.2", "instaloader": "^1.0.1", "node-cron": "^3.0.3" @@ -219,6 +220,18 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", diff --git a/package.json b/package.json index 9353b5e..b7e2b30 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dependencies": { "axios": "^1.7.2", "cors": "^2.8.5", + "dotenv": "^16.4.5", "express": "^4.19.2", "instaloader": "^1.0.1", "node-cron": "^3.0.3" diff --git a/server.js b/server.js deleted file mode 100644 index 5727d34..0000000 --- a/server.js +++ /dev/null @@ -1,102 +0,0 @@ -const express = require('express'); -const { exec } = require('child_process'); -const path = require('path'); -const cors = require('cors'); -const fs = require('fs'); -const cron = require('node-cron'); -const app = express(); -const port = 5001; - -app.use(express.json()); -app.use(cors()); - -function escapePythonString(str) { - return str.replace(/\\/g, '\\\\') - .replace(/'/g, "\\'") - .replace(/\n/g, '\\n') - .replace(/\t/g, '\\t'); -} - -// Read credentials from JSON file -const credentials = JSON.parse(fs.readFileSync(path.join(__dirname, 'credentials.json'), 'utf8')); -const escapedUsername = escapePythonString(credentials.username); -const escapedPassword = escapePythonString(credentials.password); - -const baseDir = path.join(__dirname, 'downloads'); - -function downloadContent() { - const influencers = JSON.parse(fs.readFileSync(path.join(__dirname, 'influencers.json'), 'utf8')).profiles; - - const script = ` -import instaloader -import os - -L = instaloader.Instaloader() -L.login('${escapedUsername}', '${escapedPassword}') -PROFILES = ${JSON.stringify(influencers)} -BASE_DIR = "${baseDir.replace(/\\/g, '/')}" - -os.makedirs(BASE_DIR, exist_ok=True) - -def download_posts(profile): - profile_dir = os.path.join(BASE_DIR, profile) - os.makedirs(profile_dir, exist_ok=True) - L.dirname_pattern = profile_dir - posts = instaloader.Profile.from_username(L.context, profile).get_posts() - for post in posts: - if post.typename == 'GraphVideo': # Only download videos - L.download_post(post, target=profile) - elif post.typename == 'GraphImage': # Only download images - L.download_post(post, target=profile) - -for profile in PROFILES: - download_posts(profile) - `; - - const scriptPath = path.join(__dirname, 'download_script.py'); - fs.writeFileSync(scriptPath, script); - - exec(`python3 ${scriptPath}`, (error, stdout, stderr) => { - if (error) { - console.error(`Error: ${error.message}`); - } - if (stderr) { - console.error(`Stderr: ${stderr}`); - } - console.log('Download complete'); - }); -} - -// Schedule the download task to run every hour -cron.schedule('0 * * * *', downloadContent); - -// Run the download task immediately on server start -downloadContent(); - -// Endpoint to list downloaded files -app.get('/downloads', (req, res) => { - fs.readdir(baseDir, (err, profiles) => { - if (err) { - return res.status(500).json({ error: err.message }); - } - const allowedExtensions = ['.mp4', '.jpg', '.jpeg']; - const fileList = profiles.flatMap(profileDir => { - const profilePath = path.join(baseDir, profileDir); - return fs.readdirSync(profilePath) - .filter(file => allowedExtensions.includes(path.extname(file).toLowerCase())) - .map(file => { - const filePath = path.join(profileDir, file); - console.log('Serving file:', filePath); // Log the file path - return filePath; - }); - }); - res.json(fileList); - }); -}); - -// Serve downloaded files statically -app.use('/static', express.static(path.join(__dirname, 'downloads'))); - -app.listen(port, () => { - console.log(`Server running at http://localhost:${port}`); -}); diff --git a/server/download_script.py b/server/download_script.py new file mode 100644 index 0000000..d6e03bc --- /dev/null +++ b/server/download_script.py @@ -0,0 +1,25 @@ + +import instaloader +import os + +L = instaloader.Instaloader() +L.login('xtester2', 'teTeD7QojvhOdvEGa50u6FFJo') +PROFILES = ["aao.remote.ho.jaye"] +BASE_DIR = "/home/genos/imp/work/project/insta/server/downloads" + +os.makedirs(BASE_DIR, exist_ok=True) + +def download_posts(profile): + profile_dir = os.path.join(BASE_DIR, profile) + os.makedirs(profile_dir, exist_ok=True) + L.dirname_pattern = profile_dir + posts = instaloader.Profile.from_username(L.context, profile).get_posts() + for post in posts: + if post.typename == 'GraphVideo': # Only download videos + L.download_post(post, target=profile) + elif post.typename == 'GraphImage': # Only download images + L.download_post(post, target=profile) + +for profile in PROFILES: + download_posts(profile) + \ No newline at end of file diff --git a/server/server.js b/server/server.js new file mode 100644 index 0000000..b795846 --- /dev/null +++ b/server/server.js @@ -0,0 +1,143 @@ +require('dotenv').config(); // Add this line at the top +const express = require('express'); +const { exec } = require('child_process'); +const path = require('path'); +const cors = require('cors'); +const fs = require('fs'); +const cron = require('node-cron'); +const bodyParser = require('body-parser'); +const app = express(); +const port = 5001; + +const adminPassword = process.env.ADMIN_PASSWORD; // Use environment variable + +app.use(express.json()); +app.use(cors()); +app.use(bodyParser.json()); + +const baseDir = path.join(__dirname, 'downloads'); + +function escapePythonString(str) { + return str.replace(/\\/g, '\\\\') + .replace(/'/g, "\\'") + .replace(/\n/g, '\\n') + .replace(/\t/g, '\\t'); +} + +// Read credentials from JSON file +const credentials = JSON.parse(fs.readFileSync(path.join(__dirname, 'credentials.json'), 'utf8')); +const escapedUsername = escapePythonString(credentials.username); +const escapedPassword = escapePythonString(credentials.password); + +function downloadContent() { + const influencers = JSON.parse(fs.readFileSync(path.join(__dirname, 'influencers.json'), 'utf8')).profiles; + + const script = ` +import instaloader +import os + +L = instaloader.Instaloader() +L.login('${escapedUsername}', '${escapedPassword}') +PROFILES = ${JSON.stringify(influencers)} +BASE_DIR = "${baseDir.replace(/\\/g, '/')}" + +os.makedirs(BASE_DIR, exist_ok=True) + +def download_posts(profile): + profile_dir = os.path.join(BASE_DIR, profile) + os.makedirs(profile_dir, exist_ok=True) + L.dirname_pattern = profile_dir + posts = instaloader.Profile.from_username(L.context, profile).get_posts() + for post in posts: + if post.typename == 'GraphVideo': # Only download videos + L.download_post(post, target=profile) + elif post.typename == 'GraphImage': # Only download images + L.download_post(post, target=profile) + +for profile in PROFILES: + download_posts(profile) + `; + + const scriptPath = path.join(__dirname, 'download_script.py'); + fs.writeFileSync(scriptPath, script); + + exec(`python3 ${scriptPath}`, (error, stdout, stderr) => { + if (error) { + console.error(`Error: ${error.message}`); + } + if (stderr) { + console.error(`Stderr: ${stderr}`); + } + console.log('Download complete'); + }); +} + +// Schedule the download task to run every hour +cron.schedule('0 * * * *', downloadContent); + +// Run the download task immediately on server start +downloadContent(); + +// Endpoint to list downloaded files +app.get('/downloads', (req, res) => { + fs.readdir(baseDir, (err, profiles) => { + if (err) { + return res.status(500).json({ error: err.message }); + } + const allowedExtensions = ['.mp4']; + const fileList = profiles.flatMap(profileDir => { + const profilePath = path.join(baseDir, profileDir); + return fs.readdirSync(profilePath) + .filter(file => allowedExtensions.includes(path.extname(file).toLowerCase())) + .map(file => { + const filePath = path.join(profileDir, file); + console.log('Serving file:', filePath); // Log the file path + return filePath; + }); + }); + res.json(fileList); + }); +}); + +// Serve downloaded files statically +app.use('/static', express.static(path.join(__dirname, 'downloads'))); + +// Authentication middleware +const authenticate = (req, res, next) => { + const password = req.headers['x-admin-password']; + if (password === adminPassword) { + next(); + } else { + res.status(401).json({ error: 'Unauthorized' }); + } +}; + +// Endpoint to get influencers +app.get('/influencers', authenticate, (req, res) => { + const influencers = JSON.parse(fs.readFileSync(path.join(__dirname, 'influencers.json'), 'utf8')).profiles; + res.json(influencers); +}); + +// Endpoint to update influencers +app.post('/influencers', authenticate, (req, res) => { + const { profiles } = req.body; + fs.writeFileSync(path.join(__dirname, 'influencers.json'), JSON.stringify({ profiles }, null, 2)); + res.status(200).json({ message: 'Influencers updated successfully' }); +}); + +// Endpoint to export influencers JSON +app.get('/export-influencers', authenticate, (req, res) => { + const influencers = JSON.parse(fs.readFileSync(path.join(__dirname, 'influencers.json'), 'utf8')); + res.json(influencers); +}); + +// Endpoint to import influencers JSON +app.post('/import-influencers', authenticate, (req, res) => { + const { profiles } = req.body; + fs.writeFileSync(path.join(__dirname, 'influencers.json'), JSON.stringify({ profiles }, null, 2)); + res.status(200).json({ message: 'Influencers imported successfully' }); +}); + +app.listen(port, () => { + console.log(`Server running at http://localhost:${port}`); +}); -- cgit v1.2.3-59-g8ed1b