Upload 5 files
Browse files- .gitattributes +11 -35
- .gitignore +22 -0
- README.md +59 -0
- requirements.txt +1 -0
- x_downloader.py +91 -0
.gitattributes
CHANGED
|
@@ -1,35 +1,11 @@
|
|
| 1 |
-
|
| 2 |
-
*
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
*.
|
| 6 |
-
*.
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
*.
|
| 10 |
-
*.
|
| 11 |
-
*.
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 1 |
+
# Default behavior
|
| 2 |
+
* text=auto
|
| 3 |
+
|
| 4 |
+
# Python and Markdown
|
| 5 |
+
*.py text eol=lf
|
| 6 |
+
*.md text eol=lf
|
| 7 |
+
|
| 8 |
+
# Hugging Face / GitHub Large File Storage (LFS) rules
|
| 9 |
+
*.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.mkv filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.webm filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.gitignore
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Python artifacts
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
|
| 6 |
+
# Environments
|
| 7 |
+
.env
|
| 8 |
+
.venv
|
| 9 |
+
env/
|
| 10 |
+
venv/
|
| 11 |
+
ENV/
|
| 12 |
+
|
| 13 |
+
# Output videos (prevents accidental commits of large files)
|
| 14 |
+
*.mp4
|
| 15 |
+
*.mkv
|
| 16 |
+
*.webm
|
| 17 |
+
*.part
|
| 18 |
+
*.ytdl
|
| 19 |
+
|
| 20 |
+
# OS generated files
|
| 21 |
+
.DS_Store
|
| 22 |
+
Thumbs.db
|
README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: X Video Downloader
|
| 3 |
+
colorFrom: blue
|
| 4 |
+
colorTo: indigo
|
| 5 |
+
sdk: docker
|
| 6 |
+
pinned: false
|
| 7 |
+
---
|
| 8 |
+
|
| 9 |
+
# X.com Video Downloader
|
| 10 |
+
|
| 11 |
+

|
| 12 |
+

|
| 13 |
+

|
| 14 |
+
|
| 15 |
+
A robust, fast-processing Python tool to download high-quality video content from X.com (Twitter) links.
|
| 16 |
+
|
| 17 |
+
**Author:** algorembrant
|
| 18 |
+
|
| 19 |
+
## Architecture and Flow
|
| 20 |
+
|
| 21 |
+
```mermaid
|
| 22 |
+
sequenceDiagram
|
| 23 |
+
participant User
|
| 24 |
+
participant CLI as x_downloader.py
|
| 25 |
+
participant YTDLP as yt-dlp Core
|
| 26 |
+
participant X as X.com Servers
|
| 27 |
+
participant Local as Local Storage
|
| 28 |
+
|
| 29 |
+
User->>CLI: python x_downloader.py "url"
|
| 30 |
+
CLI->>YTDLP: Pass URL and Configuration
|
| 31 |
+
YTDLP->>X: Request Metadata & Media Streams
|
| 32 |
+
X-->>YTDLP: Return Video Stream & Audio Stream
|
| 33 |
+
YTDLP->>Local: Download Streams
|
| 34 |
+
YTDLP->>Local: Merge via FFmpeg (mp4)
|
| 35 |
+
Local-->>CLI: Success Status
|
| 36 |
+
CLI-->>User: "Download completed successfully."
|
| 37 |
+
|
| 38 |
+
```
|
| 39 |
+
.
|
| 40 |
+
## File Structure
|
| 41 |
+
├── .gitattributes
|
| 42 |
+
├── .gitignore
|
| 43 |
+
├── README.md
|
| 44 |
+
├── requirements.txt
|
| 45 |
+
└── x_downloader.py
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
- 1 Install Python Ensure Python 3.8+ is installed on your system.
|
| 49 |
+
- 2 Install FFmpeg
|
| 50 |
+
- Windows: Download from gyan.dev, extract, and add the bin folder to your System PATH.
|
| 51 |
+
- macOS: brew install ffmpeg
|
| 52 |
+
- Linux (Debian/Ubuntu): sudo apt update && sudo apt install ffmpeg
|
| 53 |
+
- 3 Create Directory Create a folder for your project and navigate into it.
|
| 54 |
+
- 4 Set up Virtual Env python -m venv venv (then activate it: venv\Scripts\activate on Windows or source venv/bin/activate on Mac/Linux).
|
| 55 |
+
- 5 Save Files Save the provided .py, requirements.txt, .gitignore, .gitattributes, and README.md files in your directory.
|
| 56 |
+
- 6 Install Libraries pip install -r requirements.txt
|
| 57 |
+
- 7 Run the Script Execute the script using the commands listed at the top of the Python file.
|
| 58 |
+
|
| 59 |
+
**Note**: FFmpeg is strictly required. X.com often serves video and audio as separate streams to optimize bandwidth; FFmpeg allows the script to rapidly merge them back into a single MP4 file without quality loss.
|
requirements.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
yt-dlp>=2023.11.16
|
x_downloader.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
X.com Video Downloader
|
| 3 |
+
Author: algorembrant
|
| 4 |
+
|
| 5 |
+
Usage Commands:
|
| 6 |
+
1. Basic usage (download to current directory with default title):
|
| 7 |
+
python x_downloader.py "https://x.com/user/status/123456789"
|
| 8 |
+
|
| 9 |
+
2. Specify output directory:
|
| 10 |
+
python x_downloader.py "https://x.com/user/status/123456789" -o "./downloads"
|
| 11 |
+
|
| 12 |
+
3. Specify custom filename (must include extension, e.g., .mp4):
|
| 13 |
+
python x_downloader.py "https://x.com/user/status/123456789" -f "my_video.mp4"
|
| 14 |
+
"""
|
| 15 |
+
|
| 16 |
+
import argparse
|
| 17 |
+
import sys
|
| 18 |
+
import os
|
| 19 |
+
import yt_dlp
|
| 20 |
+
|
| 21 |
+
def download_x_video(url: str, output_dir: str = None, filename: str = None) -> None:
|
| 22 |
+
"""
|
| 23 |
+
Downloads a video from an X.com link using yt-dlp.
|
| 24 |
+
"""
|
| 25 |
+
print(f"Initializing download for: {url}")
|
| 26 |
+
|
| 27 |
+
# Base configuration for optimal processing
|
| 28 |
+
ydl_opts = {
|
| 29 |
+
'format': 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best',
|
| 30 |
+
'merge_output_format': 'mp4',
|
| 31 |
+
'quiet': False,
|
| 32 |
+
'no_warnings': True,
|
| 33 |
+
'updatetime': False,
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
# Handle output paths
|
| 37 |
+
if filename:
|
| 38 |
+
if output_dir:
|
| 39 |
+
ydl_opts['outtmpl'] = os.path.join(output_dir, filename)
|
| 40 |
+
else:
|
| 41 |
+
ydl_opts['outtmpl'] = filename
|
| 42 |
+
else:
|
| 43 |
+
if output_dir:
|
| 44 |
+
ydl_opts['outtmpl'] = os.path.join(output_dir, '%(title)s_%(id)s.%(ext)s')
|
| 45 |
+
else:
|
| 46 |
+
ydl_opts['outtmpl'] = '%(title)s_%(id)s.%(ext)s'
|
| 47 |
+
|
| 48 |
+
try:
|
| 49 |
+
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
| 50 |
+
print("Extracting video metadata and downloading...")
|
| 51 |
+
error_code = ydl.download([url])
|
| 52 |
+
|
| 53 |
+
if error_code == 0:
|
| 54 |
+
print("Download completed successfully.")
|
| 55 |
+
else:
|
| 56 |
+
print(f"Download finished with non-zero exit code: {error_code}")
|
| 57 |
+
sys.exit(1)
|
| 58 |
+
|
| 59 |
+
except yt_dlp.utils.DownloadError as e:
|
| 60 |
+
print(f"Error during download: {str(e)}")
|
| 61 |
+
sys.exit(1)
|
| 62 |
+
except Exception as e:
|
| 63 |
+
print(f"An unexpected error occurred: {str(e)}")
|
| 64 |
+
sys.exit(1)
|
| 65 |
+
|
| 66 |
+
def main():
|
| 67 |
+
parser = argparse.ArgumentParser(description="Download videos from X.com links.")
|
| 68 |
+
parser.add_argument("url", help="The full X.com (or Twitter) status URL.")
|
| 69 |
+
parser.add_argument("-o", "--output-dir", type=str, help="Directory to save the video.")
|
| 70 |
+
parser.add_argument("-f", "--filename", type=str, help="Custom filename (e.g., video.mp4).")
|
| 71 |
+
|
| 72 |
+
args = parser.parse_args()
|
| 73 |
+
|
| 74 |
+
# Validate URL structure loosely
|
| 75 |
+
if "x.com" not in args.url and "twitter.com" not in args.url:
|
| 76 |
+
print("Error: The provided URL does not appear to be a valid X.com or Twitter link.")
|
| 77 |
+
sys.exit(1)
|
| 78 |
+
|
| 79 |
+
# Ensure output directory exists if provided
|
| 80 |
+
if args.output_dir and not os.path.exists(args.output_dir):
|
| 81 |
+
try:
|
| 82 |
+
os.makedirs(args.output_dir)
|
| 83 |
+
print(f"Created output directory: {args.output_dir}")
|
| 84 |
+
except OSError as e:
|
| 85 |
+
print(f"Error creating output directory: {e}")
|
| 86 |
+
sys.exit(1)
|
| 87 |
+
|
| 88 |
+
download_x_video(args.url, args.output_dir, args.filename)
|
| 89 |
+
|
| 90 |
+
if __name__ == "__main__":
|
| 91 |
+
main()
|