72 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
d734cba88c Update downloader.py 2022-10-03 16:11:06 +02:00
Richard Mwewa
7cf8a2f74c Update downloader.py 2022-10-03 16:08:23 +02:00
24 changed files with 523 additions and 311 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,97 +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
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 = driver.find_element(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."