🏠 返回首頁 

Greasy Fork is available in English.

FB Video Saver

Un script pour facebook.com permettant de télécharger des vidéos ou des collections de vidéos.


Installer ce script?
// ==UserScript==
// @name        FB Video Saver
// @match       https://www.facebook.com/*
// @grant       GM_registerMenuCommand
// @version     1.0
// @author      Macxzew
// @description Un script pour facebook.com permettant de télécharger des vidéos ou des collections de vidéos.
// @license     MIT
// @namespace https://greasyfork.org/users/1425005
// ==/UserScript==
(async () => {
'use strict';
const processedLinks = new Set();
const processedVideos = new Set();
let videoDetails = null;
const addUIButton = () => {
setTimeout(() => {
const button = document.createElement('button');
button.textContent = 'Télécharger';
Object.assign(button.style, {
position: 'fixed',
top: '1%',
right: '20%',
backgroundColor: '#007bff',
color: 'white',
padding: '10px 20px',
border: 'none',
borderRadius: '5px',
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
cursor: 'pointer',
zIndex: '9999',
});
button.addEventListener('click', () => {
const url = window.location.href;
if (url.includes('/saved/')) {
processSavedVideos();
} else if (url.includes('/reel/') || url.includes('/videos/') || url.includes('/watch/')) {
downloadVisibleVideo();
} else {
alert('Aucune action disponible pour cette page.');
}
});
document.body.appendChild(button);
}, 2500);
};
const createNotification = (message) => {
const notification = document.createElement('div');
notification.textContent = message;
Object.assign(notification.style, {
position: 'fixed',
top: '20px',
right: '20px',
backgroundColor: 'rgba(0, 128, 0, 0.9)',
color: 'white',
padding: '10px 20px',
borderRadius: '5px',
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
fontSize: '14px',
zIndex: '9999',
animation: 'fadeout 3s forwards',
});
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
}, 3000);
const style = document.createElement('style');
style.textContent = `
@keyframes fadeout {
0% { opacity: 1; }
80% { opacity: 1; }
100% { opacity: 0; }
}
`;
document.head.appendChild(style);
};
const collectVideoDetails = async () => {
const url = window.location.href;
if (!url.includes('/reel/') && !url.includes('/videos/') && !url.includes('/watch/')) {
console.log('Cette page ne contient pas de contenu vidéo pertinent.');
return;
}
videoDetails = null;
const video = document.querySelector('video');
if (!video) return console.error('Aucune vidéo visible n\'a été trouvée.');
const reactPropsKey = Object.keys(video.parentElement).find(key => key.startsWith('__reactProps'));
const videoFBID = video.parentElement[reactPropsKey]?.children?.props?.videoFBID;
if (!videoFBID) return console.error('Impossible de récupérer l\'identifiant de la vidéo.');
const requestBody = new URLSearchParams({
doc_id: '5279476072161634',
variables: JSON.stringify({ videoID: videoFBID }),
fb_dtsg: require('DTSGInitialData').token,
});
const response = await fetch('https://www.facebook.com/api/graphql/', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: requestBody.toString(),
});
const data = JSON.parse((await response.text()).split('\n')[0])?.data?.video;
const videoURL = data?.playable_url_quality_hd || data?.playable_url;
if (!videoURL) return console.error('Impossible de récupérer l\'URL de la vidéo.');
videoDetails = { url: videoURL, ready: true };
};
const downloadVisibleVideo = async () => {
if (!videoDetails || !videoDetails.ready) return false;
const blob = await (await fetch(videoDetails.url)).blob();
const anchor = document.createElement('a');
anchor.href = URL.createObjectURL(blob);
anchor.download = 'video.mp4';
document.body.appendChild(anchor);
anchor.click();
anchor.remove();
createNotification('La vidéo a été téléchargée avec succès.');
return true;
};
const processSavedVideos = async () => {
const url = window.location.href;
if (!url.includes('/saved/')) return;
let skipNext = false;
const getVideoLinks = () => {
return Array.from(document.querySelectorAll('a'))
.map(a => a.href)
.filter(href => (href.includes('/watch/') || href.includes('/reel/') || href.includes('/videos/')))
.filter(href => !processedLinks.has(href)); // Filtrer les liens déjà traités
};
const refreshPageContent = async () => {
let lastHeight = document.body.scrollHeight, timer;
while (true) {
window.scrollTo(0, document.body.scrollHeight);
await new Promise(resolve => {
clearTimeout(timer);
timer = setTimeout(resolve, 5000);
});
const currentHeight = document.body.scrollHeight;
if (currentHeight === lastHeight) break;
lastHeight = currentHeight;
}
};
await refreshPageContent(); // Charger tous les liens visibles au départ
let links = getVideoLinks();
for (const link of links) {
if (processedLinks.has(link)) continue; // Éviter les doublons
if (skipNext) {
console.log(`Lien sauté : ${link}`);
skipNext = false; // Réinitialiser après avoir sauté un lien
continue;
}
const newTab = window.open(link, '_blank');
if (!newTab) continue;
try {
await new Promise(resolve => {
const interval = setInterval(() => {
if (newTab.document.readyState === 'complete') {
clearInterval(interval);
resolve();
}
}, 500);
});
if (processedLinks.has(link)) {
console.log(`Lien déjà téléchargé : ${link}`);
newTab.close();
continue;
}
const success = await downloadVideoFromDocument(newTab.document);
if (success) {
processedLinks.add(link); // Ajouter à la liste des liens traités
// Vérifier le type de lien pour décider de sauter ou non
if (link.includes('/reel/') || link.includes('/watch/')) {
console.log(`Lien de type "reel" ou "watch" détecté, saut du prochain lien.`);
skipNext = true;
}
}
newTab.close();
} catch (error) {
console.error(`Erreur lors du traitement du lien : ${link}`, error);
newTab.close();
}
// Réactualiser la liste des liens après chaque téléchargement
console.log('Réactualisation des liens visibles...');
await refreshPageContent();
links = getVideoLinks();
}
// Effacer les liens traités de la liste
processedLinks.clear();
console.log('Liste des liens traités effacée.');
// Actualiser la page principale "saved"
console.log('Actualisation de la page principale...');
window.location.reload();
};
const downloadVideoFromDocument = async (doc) => {
const video = doc.querySelector('video');
if (!video) return console.error('Aucune vidéo visible n\'a été trouvée.'), false;
const reactPropsKey = Object.keys(video.parentElement).find(key => key.startsWith('__reactProps'));
const videoFBID = video.parentElement[reactPropsKey]?.children?.props?.videoFBID;
if (!videoFBID) return console.error('Impossible de récupérer l\'identifiant de la vidéo.'), false;
const requestBody = new URLSearchParams({
doc_id: '5279476072161634',
variables: JSON.stringify({ videoID: videoFBID }),
fb_dtsg: require('DTSGInitialData').token,
});
const response = await fetch('https://www.facebook.com/api/graphql/', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: requestBody.toString(),
});
const data = JSON.parse((await response.text()).split('\n')[0])?.data?.video;
const videoURL = data?.playable_url_quality_hd || data?.playable_url;
if (!videoURL || processedVideos.has(videoURL)) return console.error('Impossible de récupérer l\'URL de la vidéo.'), false;
processedVideos.add(videoURL);
const blob = await (await fetch(videoURL)).blob();
const anchor = document.createElement('a');
anchor.href = URL.createObjectURL(blob);
anchor.download = 'video.mp4';
doc.body.appendChild(anchor);
anchor.click();
anchor.remove();
return true;
};
window.addEventListener('load', () => {
collectVideoDetails();
addUIButton();
});
window.addEventListener('popstate', collectVideoDetails);
((history) => {
const originalPushState = history.pushState;
const originalReplaceState = history.replaceState;
history.pushState = function (...args) {
const r###lt = originalPushState.apply(this, args);
window.dispatchEvent(new Event('pushstate'));
return r###lt;
};
history.replaceState = function (...args) {
const r###lt = originalReplaceState.apply(this, args);
window.dispatchEvent(new Event('replacestate'));
return r###lt;
};
})(window.history);
const previousUrl = { value: window.location.href };
const checkUrlChange = () => {
const currentUrl = window.location.href;
if (currentUrl.includes('/saved/') && !previousUrl.value.includes('/saved/')) {
console.log('L\'URL a changé vers "saved". Actualisation de la page...');
window.location.reload();
}
previousUrl.value = currentUrl;
};
window.addEventListener('pushstate', () => {
collectVideoDetails();
checkUrlChange();
});
window.addEventListener('replacestate', () => {
collectVideoDetails();
checkUrlChange();
});
window.addEventListener('popstate', () => {
collectVideoDetails();
checkUrlChange();
});
setInterval(checkUrlChange, 500); // Vérifier les changements d'URL régulièrement
GM_registerMenuCommand('Download Video', () => {
const url = window.location.href;
if (!url.includes('/saved/') && !url.includes('/videos/') && !url.includes('/watch/')) {
alert('Aucune action disponible pour cette page.');
} else {
downloadVisibleVideo();
}
});
GM_registerMenuCommand('Process Saved Videos', processSavedVideos);
})();