71 Commits

Author SHA1 Message Date
Richard Mwewa
14071bd5ba Merge pull request #9 from bellingcat/dev-1
Update uninstall.sh
2023-10-12 15:19:09 +02:00
Richard Mwewa
3ae6d79d1a Update uninstall.sh 2023-10-12 15:18:46 +02:00
Richard Mwewa
f9d8caef01 Merge pull request #8 from bellingcat/dev
Dev
2023-10-12 14:33:32 +02:00
Richard Mwewa
106c754b2f Merge pull request #7 from bellingcat/dev-1
Dev 1
2023-10-12 14:32:16 +02:00
Richard Mwewa
05c71f36e3 Update downloader.py 2023-10-12 14:31:47 +02:00
Richard Mwewa
70c15338df Merge pull request #6 from bellingcat/dev
Dev
2023-10-12 14:28:08 +02:00
Richard Mwewa
7cb7adc351 Update README.md 2023-10-12 14:27:30 +02:00
Richard Mwewa
3b9d04e333 Update README.md 2023-10-12 14:26:47 +02:00
Richard Mwewa
bb01ea0909 Merge pull request #5 from bellingcat/dev
Dev
2023-10-12 14:11:38 +02:00
Richard Mwewa
118aecd3af Merge pull request #4 from bellingcat/dev-1
Dev 1
2023-10-12 14:10:25 +02:00
Richard Mwewa
84da1c4c8d update 2023-10-12 14:09:05 +02:00
Richard Mwewa
f58a599bdb Update README.md 2023-10-12 13:53:46 +02:00
Richard Mwewa
1e5d166200 Added setup scripts. 2023-10-12 06:57:32 +02:00
Richard Mwewa
79b21427b5 Merge pull request #3 from rly0nheart/rly0nheart-patch-1
Update README.md
2023-07-11 01:59:54 +02:00
Richard Mwewa
4b713b5e46 Update README.md 2023-07-11 01:58:39 +02:00
Richard Mwewa
3a226fb497 Merge pull request #2 from rly0nheart/dev
Dev
2023-07-10 01:50:24 +02:00
Richard Mwewa
d8db916f87 Update Dockerfile 2023-07-10 01:47:59 +02:00
Richard Mwewa
ceb51e6bc1 Update downloader.py 2023-07-10 01:45:36 +02:00
Richard Mwewa
3ecaef799d Update main.py 2023-07-10 01:43:21 +02:00
Richard Mwewa
f9d12e25d4 Update downloader.py 2023-07-10 01:42:46 +02:00
Richard Mwewa
af302995b7 Update and rename setup.py to pyproject.toml
Update and rename setup.py to pyproject.toml
2023-07-10 01:41:36 +02:00
Richard Mwewa
ddd14c151d Update README.md 2023-01-02 05:14:30 +02:00
Richard Mwewa
18213172aa Switched to semantic versioning 2023-01-02 05:11:48 +02:00
Richard Mwewa
99dc9e3ea7 Update README.md 2023-01-02 05:03:04 +02:00
Richard Mwewa
1126a72a9f Update README.md 2023-01-02 04:57:08 +02:00
Richard Mwewa
de660b30f4 Update README.md 2023-01-02 04:56:22 +02:00
Richard Mwewa
07bf9a0220 Update main.py 2023-01-02 04:49:33 +02:00
Richard Mwewa
0a8454fcf9 Switched to semantic versioning 2023-01-02 04:32:51 +02:00
Richard Mwewa
8abc59474b Update main.py 2023-01-02 04:13:52 +02:00
Richard Mwewa
885676d826 Update downloader.py 2023-01-02 04:12:24 +02:00
Richard Mwewa
59d8a30d85 Update LICENSE 2023-01-02 04:06:54 +02:00
Richard Mwewa
fa12e7e477 Update Dockerfile 2022-12-02 23:15:58 +02:00
Richard Mwewa
d38eb6aefe Update Dockerfile 2022-12-02 23:06:09 +02:00
Richard Mwewa
eda82c051d Update Dockerfile 2022-11-27 02:40:29 +02:00
Richard Mwewa
e1c15ddb0f Update README.md 2022-11-03 01:32:31 +02:00
Richard Mwewa
47844da5b8 Merge pull request #1 from rly0nheart/dev
Create Dockerfile
2022-11-03 01:25:26 +02:00
Richard Mwewa
aebb6f0e65 Update README.md 2022-10-31 21:36:03 +02:00
Richard Mwewa
37e028c769 Create Dockerfile 2022-10-31 19:17:02 +02:00
Richard Mwewa
a65a1f65d5 Update README.md 2022-10-21 23:28:18 +02:00
Richard Mwewa
864f80921e Update setup.py 2022-10-21 23:23:42 +02:00
Richard Mwewa
b1760aad56 Update downloader.py 2022-10-21 23:23:39 +02:00
Richard Mwewa
52df5b63ae Update downloader.py 2022-10-21 23:22:30 +02:00
Richard Mwewa
8097d273bd Create python-publish.yml 2022-10-21 20:26:29 +02:00
Richard Mwewa
1f008f71fc Update downloader.py 2022-10-20 21:49:17 +02:00
Richard Mwewa
d9c4666db9 Create codeql.yml 2022-10-20 21:47:39 +02:00
Richard Mwewa
de55a6a803 Delete codeql.yml 2022-10-20 21:43:14 +02:00
Richard Mwewa
066ab0e824 Update README.md 2022-10-20 21:39:43 +02:00
Richard Mwewa
8195f4a363 Update downloader.py 2022-10-20 19:29:18 +02:00
Richard Mwewa
576e3f6bdb update setup.py and downloader.py 2022-10-08 11:01:24 +02:00
Richard Mwewa
9ad3deb9f8 Create codeql.yml 2022-10-08 10:59:42 +02:00
Richard Mwewa
8fb3ae15f9 Merge branch 'master' of https://github.com/rly0nheart/Facebook-Downloader 2022-10-08 10:58:34 +02:00
Richard Mwewa
6ad5e5f512 updates 2022-10-08 10:58:18 +02:00
Richard Mwewa
1df1d5c600 Create dependabot.yml 2022-10-08 10:56:56 +02:00
Richard Mwewa
c5fa0be4d1 Delete python-app.yml 2022-10-08 10:55:13 +02:00
Richard Mwewa
35b69544f1 Update README.md 2022-10-08 10:54:29 +02:00
Richard Mwewa
2e7269fe12 Merge branch 'master' of https://github.com/rly0nheart/Facebook-Downloader 2022-10-08 10:50:19 +02:00
Richard Mwewa
a5149c3048 Merge branch 'master' of https://github.com/rly0nheart/Facebook-Downloader 2022-10-08 10:48:46 +02:00
Richard Mwewa
f86b007aab Create LICENSE 2022-10-08 10:48:08 +02:00
Richard Mwewa
7f9ab313a8 Create README.md 2022-10-08 10:45:58 +02:00
Richard Mwewa
751f4bba62 Refactored for pypi 2022-10-08 10:44:14 +02:00
Richard Mwewa
a00d78b114 Delete test_download_video.py 2022-10-08 10:39:00 +02:00
Richard Mwewa
163fe48e88 Delete requirements.txt 2022-10-08 10:38:51 +02:00
Richard Mwewa
3f246bd5b9 Delete geckodriver.exe 2022-10-08 10:38:41 +02:00
Richard Mwewa
569881cf4b Delete README.md 2022-10-08 10:38:32 +02:00
Richard Mwewa
4031c206f8 Delete LICENSE 2022-10-08 10:38:19 +02:00
Richard Mwewa
e3b94b0871 Delete .gitignore 2022-10-08 10:38:08 +02:00
Richard Mwewa
875bae98b7 Delete downloader.py 2022-10-08 10:37:57 +02:00
Richard Mwewa
550610cbc1 Delete downloads directory 2022-10-08 10:37:37 +02:00
Richard Mwewa
2bad245ae7 Merge branch 'master' of https://github.com/rly0nheart/Facebook-Downloader 2022-10-03 16:20:42 +02:00
Richard Mwewa
cec70c0600 Update downloader.py 2022-10-03 16:20:31 +02:00
Richard Mwewa
7cf8a2f74c Update downloader.py 2022-10-03 16:08:23 +02:00
24 changed files with 523 additions and 314 deletions

38
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Richard Mwewa (Telegram)
url: https://t.me/rly0nheart/
about: Please ask and answer questions here.
- name: Richard Mwewa (Twitter)
url: https://m.twitter.com/rly0nheart/
about: Please report security vulnerabilities or bugs here.

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

13
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
# Enable version updates for pip
- package-ecosystem: "pip"
directory: "/" # Location of package manifests
# Check the tqdm registry for updates every day (weekdays)
schedule:
interval: "daily"

74
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,74 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "master" ]
schedule:
- cron: '16 14 * * 6'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"

View File

@@ -1,39 +0,0 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: Python application
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
pytest test_download_video.py

39
.github/workflows/python-publish.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
# This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
name: Upload Python Package
on:
release:
types: [published]
permissions:
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build
- name: Build package
run: python -m build
- name: Publish package
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}

129
.gitignore vendored
View File

@@ -1,129 +0,0 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/

15
Dockerfile Normal file
View File

@@ -0,0 +1,15 @@
FROM python:latest
WORKDIR /app
COPY . .
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl firefox-esr \
&& rm -fr /var/lib/apt/lists/* \
&& curl -L https://github.com/mozilla/geckodriver/releases/download/v0.32.0/geckodriver-v0.33.0-linux64.tar.gz | tar xz -C /usr/local/bin \
&& apt-get purge -y ca-certificates curl
RUN pip install .
ENTRYPOINT ["facebook_downloader"]

View File

@@ -631,7 +631,7 @@ to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Facebook video downloader
facebook video downloader
Copyright (C) 2022 Richard Mwewa
This program is free software: you can redistribute it and/or modify
@@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Facebook-Downloader Copyright (C) 2022 Richard Mwewa
Facebook-Downloader Copyright (C) 2023 Richard Mwewa
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.

View File

@@ -1,62 +1,71 @@
# Facebook-Downloader
A program for downloading Facebook videos
# Facebook Downloader
A program for downloading videos from Facebook, given a video url
[![Upload Python Package](https://github.com/rly0nheart/facebook-downloader/actions/workflows/python-publish.yml/badge.svg)](https://github.com/rly0nheart/facebook-downloader/actions/workflows/python-publish.yml)
[![CodeQL](https://github.com/rly0nheart/facebook-downloader/actions/workflows/codeql.yml/badge.svg)](https://github.com/rly0nheart/facebook-downloader/actions/workflows/codeql.yml)
# Installation
**1. Clone the project**
## Install from PyPI
```
git clone https://github.com/rly0nheart/Facebook-Downloader.git
pip install facebook-downloader
```
> You will need to have the FireFox browser installed and geckodriver properly set up.
## Building from source
**1.** Clone the repository
```
git clone https://github.com/bellingcat/facebook-downloader
```
**2. Move to Facebook-Downloader directory**
**2.** Navigate to the cloned repository
```
cd Facebook-Downloader
cd facebook-downloader
```
**3. Install dependencies**
## Note
> *This will install tqdm, selenium, and requests*
> > *You will need to have Firefox installed to run the program*
> > > *For user convenience, the program will come with a geckodriver.exe binary*
### Building the Docker container
```
pip install -r requirements.txt
docker build --tty my-facebook-downloader .
```
### Building the facebook-downloader package
#### Linux
Find the `install.sh` script and run it
```
./install.sh
```
> This assumes the script was already made executable with the `chmod +x uninstall.sh` command.
#### Windows
**1.** Navigate to the facebook-downloader directory
Find the `install.ps1` script and run it
```
.\install.ps1
```
> The installation scripts will download and setup geckodriver, then install **facebook-downloader**.
# Usage
## Package
```
python downloader.py <facebook-url>
facebook_downloader <video-url>
```
> *Alternatively, you could grant execution permission to the downloader and run it as shown below*
**1. Grant execution permission**
## Docker
```
chmod +x downloader.py
docker run --tty --volume $PWD/downloads:/app/downloads my-facebook-downloader <facebook_video_url>
```
**2. Run downloader**
```
./downloader.py <facebook-url>
```
## Example
```
python downloader.py https://www.facebook.com/PageName/videos/VideoID
```
## Note
> Upon run, the downloader will first check for updates. If found, users will be prompted to download the updates
# Optional Arguments
| Flag | Description |
|---------|:-----------:|
| *-A/--audio* | download audio only (coming soon) |
| *-o/--output* | output filename |
| *-v/--version* | show program's version number and exit |
| *-a/--audio* | download as audio |
| *-o/--output* | output file name |
# Donations
If you would like to donate, you could Buy A Coffee for the developer using the button below
<a href="https://www.buymeacoffee.com/189381184" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a>
<a href="https://www.buymeacoffee.com/_rly0nheart"><img src="https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=_rly0nheart&button_colour=40DCA5&font_colour=ffffff&font_family=Comic&outline_colour=000000&coffee_colour=FFDD00" /></a>
Your support will be much appreciated!

View File

@@ -1,100 +0,0 @@
import time
import logging
import argparse
import requests
from tqdm import tqdm
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
option = webdriver.FirefoxOptions()
option.add_argument('--headless')
driver = webdriver.Firefox(options=option)
program_version_number = "2022.1.0.0"
downloading_url = "https://getfvid.com"
update_check_endpoint = "https://api.github.com/repos/rly0nheart/Facebook-Downloader/releases/latest"
def notice():
notice_msg = f"""
Facebook-Downloader {program_version_number} Copyright (C) 2022 Richard Mwewa
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
"""
print(notice_msg)
def check_and_get_updates():
notice()
response = requests.get(update_check_endpoint).json()
if response['tag_name'] == program_version_number:
"""Ignore if the program is up to date"""
pass
else:
update_prompt = input(f"[?] A new release is available ({response['tag_name']}). Would you like to install it? (y/n) ")
if update_prompt.lower() == "y":
files_to_update = ['downloader.py', 'geckodriver.exe', 'README.md', 'requirements.txt']
for file in tqdm(files_to_update, desc=f'Updating'):
data = requests.get(f'https://raw.githubusercontent.com/rly0nheart/Facebook-Downloader/master/{file}')
with open(file, "wb") as f:
f.write(data.content)
f.close()
print("Updated: Re-run program.")
else:
pass
def download_video(url, output):
driver.get(downloading_url) # Opening getfvid.com, a website that downloads facebook videos
url_entry_field = driver.find_element(By.NAME, "url") # Find the url entry field
url_entry_field.send_keys(url) # write facebook url in the entry field
url_entry_field.send_keys(Keys.ENTER) # press enter
print('Please standby (20 seconds)...')
time.sleep(20) # Sleep for at least 20 seconds to wait for the next page to load
driver.refresh
"""
HD: "/html/body/div[2]/div/div/div[1]/div/div[2]/div/div[3]/p[1]/a"
SD: "/html/body/div[2]/div/div/div[1]/div/div[2]/div/div[3]/p[2]/a"
Audio: "/html/body/div[2]/div/div/div[1]/div/div[2]/div/div[3]/p[3]/a"
"""
download_btn = WebDriverWait(driver, 20).until(expected_conditions.presence_of_element_located((By.XPATH, '/html/body/div[2]/div/div/div[1]/div/div[2]/div/div[3]/p[1]/a'))) # Find the download button (this clicks the first button which returns a video in hd)
download_url = download_btn.get_attribute('href')
with requests.get(download_url, stream=True) as response:
response.raise_for_status()
with open(f'downloads/{output}.mp4', 'wb') as file:
for chunk in tqdm(response.iter_content(chunk_size=8192), desc=f'Downloading: {output}.mp4'):
file.write(chunk)
print(f'Downloaded: {file.name}')
driver.close()
parser = argparse.ArgumentParser(description='Facebook-Downloader — by Richard Mwewa')
parser.add_argument('url', help='facebook video url (eg. https://www.facebook.com/PageName/videos/VideoID')
parser.add_argument('-A', '--audio', help=argparse.SUPPRESS, action='store_true')
parser.add_argument('-o', '--output', help='output filename')
parser.add_argument('-v', '--version', version='2022.1.0.0', action='version')
args = parser.parse_args()
url = args.url
output = args.output
if __name__ == "__main__":
try:
check_and_get_updates()
download_video(url, output)
except KeyboardInterrupt:
print('Process interrupted with Ctrl+C.')
except Exception as e:
print('An error occured:', e)

View File

@@ -1 +0,0 @@

View File

@@ -0,0 +1,2 @@
__author__ = "Richard Mwewa"
__version__ = "1.4.0"

View File

@@ -0,0 +1,141 @@
import os
import requests
import argparse
from tqdm import tqdm
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
from . import __version__, __author__
class FacebookDownloader:
def __init__(self):
self.__base_url = "https://getfvid.com"
self.__update_check_endpoint = "https://api.github.com/repos/rly0nheart/facebook-downloader/releases/latest"
self.__home_directory = os.path.expanduser("~")
self.__downloads_directory = os.path.join(self.__home_directory, "facebook-downloader")
__option = webdriver.FirefoxOptions()
__option.add_argument('--headless')
self.__driver = webdriver.Firefox(options=__option)
parser = argparse.ArgumentParser(description=f'facebook-downloader — by {__author__}',
epilog='Facebook video downloader.')
parser.add_argument('url', help='facebook video url')
parser.add_argument('-a', '--audio', help='download file as audio', action='store_true')
parser.add_argument('-o', '--output', help='output filename', default="")
parser.add_argument('-v', '--version', action='version', version=__version__)
self.__args = parser.parse_args()
@staticmethod
def __format_output_filename(user_defined_name) -> str:
"""
Formats the output file's name.
:param user_defined_name: User-defined name for the file.
:return: Formatted/Reconstructed name of the file.
"""
from datetime import datetime
dt_now = datetime.now()
if os.name == "nt":
output_name = dt_now.strftime(f"{user_defined_name}_%d-%m-%Y %I-%M-%S%p-facebook-downloader.mp4")
else:
output_name = dt_now.strftime(f"{user_defined_name}_%d-%m-%Y %I:%M:%S%p-facebook-downloader.mp4")
return output_name
def notice(self) -> str:
"""
Returns the program's license notice and current version.
:return: License notice.
:rtype: str
"""
return f"""
facebook-downloader v{__version__} Copyright (C) 2022-2023 Richard Mwewa
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
"""
def check_updates(self):
"""
Checks if the program's version tag matches the tag of the latest release on GitHub.
If the tags match, assume the program is up-to-date.
"""
with requests.get(self.__update_check_endpoint) as response:
remote_version = response.json().get('tag_name')
if remote_version != __version__:
print(f"* A new release is available -> facebook-downloader v{remote_version}.\n"
f"* Run 'pip install --upgrade facebook-downloader' to get the updates.\n")
def __get_download_type_element(self) -> str:
"""
Gets the web element according to the specified command-line arguments.
ELements
--------
- HD: /html/body/div[2]/div/div/div[1]/div/div[2]/div/div[3]/p[1]/a
- SD: /html/body/div[2]/div/div/div[1]/div/div[2]/div/div[3]/p[2]/a
- Audio: /html/body/div[2]/div/div/div[1]/div/div[2]/div/div[3]/p[3]/a
:return: Web element
"""
if self.__args.audio:
download_type_element = "/html/body/div[2]/div/div/div[1]/div/div[2]/div/div[3]/p[3]/a"
else:
download_type_element = "/html/body/div[2]/div/div/div[1]/div/div[2]/div/div[3]/p[1]/a"
return download_type_element
def path_finder(self) -> None:
"""
Creates the facebook-videos directory.
:return: None
"""
# Construct and create the directory if it doesn't already exist
os.makedirs(os.path.join(self.__home_directory, "facebook-downloader"), exist_ok=True)
def download_video(self):
"""
Opens https://getfvid.com with selenium and uses the specified facebook video link as a query.
"""
# Open the base url.
self.__driver.get(self.__base_url)
# Locate the facebook video url entry field.
url_entry_field = self.__driver.find_element(By.NAME, "url")
# Write the given facebook video url in the entry field.
url_entry_field.send_keys(self.__args.url)
# Press ENTER.
url_entry_field.send_keys(Keys.ENTER)
print("* Loading web resources... Please wait.")
# Find the download button (this clicks the first button which returns a video in hd).
download_btn = WebDriverWait(self.__driver, 20).until(
expected_conditions.presence_of_element_located((By.XPATH,
self.__get_download_type_element())))
# Get the video download url from the download button.
download_url = download_btn.get_attribute('href')
# Open the download url and stream the content to a file.
with requests.get(download_url, stream=True) as response:
response.raise_for_status()
with open(os.path.join(self.__downloads_directory,
self.__format_output_filename(self.__args.output)), 'wb') as file:
for chunk in tqdm(response.iter_content(chunk_size=8192),
desc=f"* Downloading: {file.name}"):
file.write(chunk)
print(f"* Downloaded: {file.name}")
# Close driver.
self.__driver.close()

View File

@@ -0,0 +1,25 @@
from facebook_downloader.downloader import FacebookDownloader
def start_downloader():
try:
# Initialise the FaceBookDownloader instance.
program = FacebookDownloader()
# Create directory where downloaded videos will be stored.
program.path_finder()
# Print program's license notice.
print(program.notice())
# Check for latest releases.
program.check_updates()
# Start video download.
program.download_video()
except KeyboardInterrupt:
print("Process interrupted with Ctrl+C.")
except Exception as e:
print(f"An error occurred: {e}")

Binary file not shown.

27
install.ps1 Normal file
View File

@@ -0,0 +1,27 @@
# Define URL for GeckoDriver
$geckoURL = "https://github.com/mozilla/geckodriver/releases/download/v0.33.0/geckodriver-v0.33.0-win64.zip"
# Define target directories for installation
$geckoDir = "$env:USERPROFILE\facebook-downloader\GeckoDriver"
# Function to download a file
function DownloadFile([string]$url, [string]$path) {
Invoke-WebRequest -Uri $url -OutFile $path
}
# Check if GeckoDriver directory exists, if not create and download
if (-Not (Test-Path $geckoDir)) {
New-Item -Path $geckoDir -ItemType Directory
Write-Host "Downloading GeckoDriver..."
DownloadFile $geckoURL "$geckoDir\geckodriver.zip"
# Unzipping the GeckoDriver
Expand-Archive -Path "$geckoDir\geckodriver.zip" -DestinationPath $geckoDir
Remove-Item "$geckoDir\geckodriver.zip"
}
# Add the geckodriver directory to PATH
[Environment]::SetEnvironmentVariable("PATH", [Environment]::GetEnvironmentVariable("PATH", [EnvironmentVariableTarget]::User) + ";$geckoDir", [EnvironmentVariableTarget]::User)
pip install .
Write-Host "Setup complete."

9
install.sh Normal file
View File

@@ -0,0 +1,9 @@
#!/bin/bash
# Download geckodriver .tar.gz file and pipe it to 'tar' to extract the geckodriver binary directly into /usr/bin.
curl -L https://github.com/mozilla/geckodriver/releases/download/v0.33.0/geckodriver-v0.33.0-linux64.tar.gz | \
tar xz -C /usr/bin
# Install Python packages defined in the current directory's setup.py/pyproject.toml file. (pyproject.toml in this case)
pip3 install .
echo "Setup complete."

33
pyproject.toml Normal file
View File

@@ -0,0 +1,33 @@
[build-system]
requires = ["setuptools", "setuptools-scm"]
build-backend = "setuptools.build_meta"
[project]
name = "facebook-downloader"
version = "1.4.0"
description = "A Facebook video downloader."
readme = "README.md"
requires-python = ">=3.9"
license = {file = "LICENSE"}
dependencies = ["tqdm", "requests", "selenium"]
keywords = ["searchcode", "search-engine", "codesearch", "api-wrapper"]
authors = [{name = "Richard Mwewa", email = "rly0nheart@duck.com"}]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Programming Language :: Python :: 3",
"Intended Audience :: Information Technology",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
"Operating System :: OS Independent",
"Natural Language :: English",
]
[tool.setuptools]
packages = ["facebook_downloader"]
[project.urls]
homepage = "https://pypi.org/project/facebook-downloader"
documentation = "https://github.com/bellingcat/facebook-downloader/wiki"
repository = "https://github.com/bellingcat/facebook-downloader"
[project.scripts]
facebook_downloader = "facebook_downloader.main:start_downloader"

View File

@@ -1,3 +0,0 @@
tqdm
selenium
requests

View File

@@ -1,6 +0,0 @@
from downloader import download_video
def test_download_video():
# I find this video very interesting, enjoy! ;)
url = 'https://www.facebook.com/VICE/videos/663211078474482'
download_video(url, output='test_video_making-a-weed-smoothie')

26
uninstall.ps1 Normal file
View File

@@ -0,0 +1,26 @@
# Define target directory for removal
$geckoDir = "$env:USERPROFILE\facebook-downloader\GeckoDriver"
# Function to remove directory
function RemoveDir([string]$dirPath) {
if (Test-Path $dirPath) {
Remove-Item -Path $dirPath -Recurse -Force
Write-Host "Removed directory: $dirPath"
} else {
Write-Host "Directory $dirPath does not exist."
}
}
# Remove GeckoDriver directory
RemoveDir $geckoDir
# Remove the geckodriver directory from PATH
$pathEnv = [Environment]::GetEnvironmentVariable("PATH", [EnvironmentVariableTarget]::User)
$newPath = ($pathEnv -split ";" | Where-Object { $_ -ne $geckoDir }) -join ";"
[Environment]::SetEnvironmentVariable("PATH", $newPath, [EnvironmentVariableTarget]::User)
Write-Host "Removed GeckoDriver directory from PATH."
# Uninstall facebook-downloader Python package
pip uninstall facebook-downloader -y
Write-Host "Cleanup complete."

8
uninstall.sh Normal file
View File

@@ -0,0 +1,8 @@
#!/bin/bash
# Remove the geckodriver binary from /usr/bin
sudo rm /usr/bin/geckodriver -v
# Uninstall facebook-downloader
pip3 uninstall facebook-downloader -y -v
echo "Cleanup complete."