26 Commits

Author SHA1 Message Date
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
19 changed files with 368 additions and 315 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/

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

View File

@@ -1,58 +1,60 @@
# Facebook-Downloader
A program for downloading Facebook videos
# Facebook Downloader
A program for downloading videos from facebook, given a video url
# Installation
**1. Clone the project**
**Install from PyPI**
```
git clone https://github.com/rly0nheart/Facebook-Downloader.git
pip install facebook-downloader
```
**2. Move to Facebook-Downloader directory**
### Note
> You will need to have the FireFox browser installed on your pc
>> The program is dependent on selenium, so in order to run it, you will have to download and properly setup geckodriver (setup instructions available below)
# Geckodriver setup
## Linux
**1. Go to the geckodriver [releases page](https://github.com/mozilla/geckodriver/releases/). Find the latest version of the driver for your platform and download it**
**2. Extract the downloaded file**
```
cd Facebook-Downloader
tar -xvzf geckodriver*
```
**3. Add geckodriver to your system path**
```
export PATH=$PATH:/path/to/downloaded/geckodriver
```
**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*
```
pip install -r requirements.txt
```
### Note
> If you encounter issues with the above commands, then you should run them as root
## Windows
**1. Go to the geckodriver [releases page](https://github.com/mozilla/geckodriver/releases/). Find the geckodriver.exe binary for your platform and download it**
**2. Move the downloaded executable to** *C:\Users\yourusername\AppData\Local\Programs\Python\Python310*
### Note
> The numbers on the directory 'Python310' will depend on the version of Python you have
## Mac OS
* [Set up Selenium & GeckoDriver (Mac)](https://medium.com/dropout-analytics/selenium-and-geckodriver-on-mac-b411dbfe61bc)
# Usage
```
python downloader.py <facebook-url>
```
> *Alternatively, you could grant execution permission to the downloader and run it as shown below*
**1. Grant execution permission**
```
chmod +x downloader.py
```
**2. Run downloader**
```
./downloader.py <facebook-url>
```
## Example
```
python downloader.py https://www.facebook.com/PageName/videos/VideoID
facebook_downloader <video-url>
```
## Note
> Upon run, the downloader will first check for updates. If found, users will be prompted to download the updates
> The url format should be as follows; https://www.facebook.com/PageName/videos/VideoID
# 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

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

View File

@@ -0,0 +1,94 @@
import os
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
class FacebookDownloader:
def __init__(self):
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='download file as audio', action='store_true')
parser.add_argument('-o', '--output', help='output filename')
self.args = parser.parse_args()
option = webdriver.FirefoxOptions()
option.add_argument('--headless')
self.driver = webdriver.Firefox(options=option)
self.program_version_number = "2022.1.2.0"
self.downloading_url = "https://getfvid.com"
self.update_check_endpoint = "https://api.github.com/repos/rly0nheart/facebook-downloader/releases/latest"
def notice(self):
notice_msg = f"""
facebook-downloader {self.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_updates(self):
self.notice()
response = requests.get(self.update_check_endpoint).json()
if response['tag_name'] == self.program_version_number:
"""Ignore if the program is up to date"""
pass
else:
print(f"[!] A new release is available ({response['tag_name']}). Run 'pip install --upgrade facebook-downloader' to get the updates.")
def download_type(self):
"""
The elements change according to what file type will be downloaded
So, we pass an option to specify what file type we want, by default the file is an HD video
"""
"""
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"
"""
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):
directory_list = [os.path.join('downloads', 'videos'), os.path.join('downloads', 'audio')]
for directory in directory_list:
os.makedirs(directory, exist_ok=True)
def download_video(self):
self.path_finder()
self.check_updates()
self.driver.get(self.downloading_url) # Opening getfvid.com, a website that downloads facebook videos
url_entry_field = self.driver.find_element(By.NAME, "url") # Find the url entry field
url_entry_field.send_keys(self.args.url) # write facebook url in the entry field
url_entry_field.send_keys(Keys.ENTER) # press enter
print('Please standby (20 seconds)...')
self.driver.refresh
download_btn = WebDriverWait(self.driver, 20).until(expected_conditions.presence_of_element_located((By.XPATH, self.download_type()))) # 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(os.path.join('downloads', 'videos', f'{self.args.output}.mp4'), 'wb') as file:
for chunk in tqdm(response.iter_content(chunk_size=8192), desc=f'Downloading: {self.args.output}.mp4'):
file.write(chunk)
print(f'Downloaded: {file.name}')
self.driver.close()

View File

@@ -0,0 +1,12 @@
from facebook_downloader.downloader import FacebookDownloader
def main():
try:
start = FacebookDownloader()
start.download_video()
except KeyboardInterrupt:
print('Process interrupted with Ctrl+C.')
except Exception as e:
print('An error occured:', e)

Binary file not shown.

View File

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

31
setup.py Normal file
View File

@@ -0,0 +1,31 @@
import setuptools
with open('README.md', 'r', encoding='utf-8') as file:
long_description = file.read()
setuptools.setup(
name='facebook-downloader',
version='2022.1.2.0',
author='Richard Mwewa',
author_email='rly0nheart@duck.com',
packages=['facebook_downloader'],
description='Facebook video downloader',
long_description=long_description,
long_description_content_type='text/markdown',
url='https://github.com/rly0nheart/facebook-downloader',
license='GNU General Public License v3 (GPLv3)',
install_requires=['requests', 'selenium', 'tqdm'],
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Information Technology',
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
'Operating System :: OS Independent',
'Natural Language :: English',
'Programming Language :: Python :: 3'
],
entry_points={
'console_scripts': [
'facebook_downloader=facebook_downloader.main:main',
]
},
)

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')