Skip to content

Commit

Permalink
Updated to v1.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
elmoiv authored Aug 25, 2020
1 parent 33d95bd commit 5fd3094
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 50 deletions.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![Python version](https://img.shields.io/badge/python-3.x-brightgreen.svg)](https://pypi.org/project/redvid/)
[![Current Release](https://img.shields.io/github/v/release/elmoiv/redvid.svg)](https://github.com/elmoiv/redvid/releases)

### Smart downloader for Reddit hosted videos
### Smart downloader for Reddit *hosted* videos

## Features
* Download local hosted videos with audio.
Expand Down Expand Up @@ -68,17 +68,22 @@ Here are a few sample tests:

* [Video only](https://github.com/elmoiv/redvid/tree/master/tests/test1.py)
* [Video with audio](https://github.com/elmoiv/redvid/tree/master/tests/test2.py)
* [**[NEW]** Get best quality according to given size (Automated)](https://github.com/elmoiv/redvid/tree/master/tests/test12.py)
* [Choose PATH](https://github.com/elmoiv/redvid/tree/master/tests/test3.py)
* [Auto-detect maximum quality](https://github.com/elmoiv/redvid/tree/master/tests/test4.py)
* [Auto-detect minimum quality](https://github.com/elmoiv/redvid/tree/master/tests/test5.py)
* [Skip file check and overwrite](https://github.com/elmoiv/redvid/tree/master/tests/test6.py)
* [Silent download (No logging)](https://github.com/elmoiv/redvid/tree/master/tests/test7.py)
* [Set maximum video size](https://github.com/elmoiv/redvid/tree/master/tests/test8.py)
* [Set maximum video size (different technique)](https://github.com/elmoiv/redvid/tree/master/tests/test9.py)
* [Set maximum video size (different technique)](https://github.com/elmoiv/redvid/tree/master/tests/test9.py)
* [Set maximum video duration](https://github.com/elmoiv/redvid/tree/master/tests/test10.py)
* [Set maximum video duration (different technique)](https://github.com/elmoiv/redvid/tree/master/tests/test11.py)
* [Set maximum video duration (different technique)](https://github.com/elmoiv/redvid/tree/master/tests/test11.py)

## Changelog
### v1.1.0
* redvid can now decide best quality according to given size.
* Added support for old reddit videos.
* Fixed bug where video qualities list can't be parsed.
### v1.0.9:
* Added the ability to disable logging.
* Maximum video size can be set.
Expand All @@ -95,4 +100,4 @@ Here are a few sample tests:
* Adjusted printed text and progress bars.

## Contributing
Please contribute! If you want to fix a bug, suggest improvements, or add new features to the project, just [open an issue](https://github.com/elmoiv/redvid/issues) or send me a pull request.
Please contribute! If you want to fix a bug, suggest improvements, or add new features to the project, just [open an issue](https://github.com/elmoiv/redvid/issues) or send me a pull request.
4 changes: 2 additions & 2 deletions redvid/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
__author__ = 'Khaled H. El-Morshedy'
__url__ = 'https://github.com/elmoiv/redvid'
__description__ = 'Smart downloader for Reddit hosted videos'
__license__ = 'MIT'
__version__ = '1.0.9'
__license__ = 'GPL-v3.0'
__version__ = '1.1.0'
118 changes: 82 additions & 36 deletions redvid/redvid.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,32 @@ def __init__(
min_q=False,
max_d=1e1000,
max_s=1e1000,
auto_max=False,
log=True,
proxies={}
):
self.path, self.url, self.proxies = path, url, proxies
self.max, self.min, self.log = max_q, min_q, log
self.max_d, self.max_s = max_d, max_s
self.auto_max = auto_max

self.sizes_error = False
self.page = None
self.overwrite = False
self.ischeck = False
self.UNQ = None
self.r_url = None
self.videos = []
self.video, self.audio, self.file_name = '', '', ''
self.duration, self.size = 0, 0

def setup(self):
"""
Checks PATH and URL for any errors
"""
self.ischeck = False
self.is_catch_errors = False

if not self.log:
if not self.max and not self.min:
self.max = True
Expand Down Expand Up @@ -73,43 +83,31 @@ def scrape(self):
"""
Gets direct video and audio (if exists) urls
"""
UNQ = getUNQ(self.page)
if not UNQ:
self.UNQ = getUNQ(self.page)

if not self.UNQ:
Clean(self.path)
raise BaseException('No video in this post')

self.r_url = 'https://v.redd.it/' + self.UNQ + '/'

# Getting Qualities and audio (if exists) from mpd file
mpd = self.get(
UNQ + 'DASHPlaylist.mpd',
self.r_url + 'DASHPlaylist.mpd',
_proxies=self.proxies
)
# v1.0.8: Fix new Reddit mechanism
VQS, AQS = mpdParse(mpd.text)

if [VQS, AQS] == [0, 0]:
raise BaseException('Qualities not found!')

self.videos = VQS

# Check for Audio
if AQS:
self.audio = UNQ + j(AQS[0])

# Fix - It was creating recursive directories like temp/temp/temp/temp when VQS was equals to [].
if VQS == []:
Clean(self.path)
raise BaseException('Malformed quality.')
self.audio = self.r_url + j(AQS)

# Select Quality
if self.max:
quality = j(VQS[0])
elif self.min:
quality = j(VQS[-1])
else:
quality = j(UserSelect(VQS))

self.video = UNQ + quality
self.file_name = '{}{}-{}.mp4'.format(
self.path,
UNQ.split('/')[-2],
quality
# v1.0.8: fix file name dups
).replace('.mp4' * 2, '.mp4')

def get_video(self):
"""
Expand Down Expand Up @@ -144,32 +142,79 @@ def get_and_mux(self):

# Moving video file without using shutil
os.rename('av.mp4', self.file_name)

# Clean Temp folder
Clean(self.path)

# v1.0.9: get size and duration
def check(self):
"""
Scrapes video and metadata (Duration and size)
"""
self.ischeck = True

lprint(self.log, '>> Connecting...')
self.setup()

lprint(self.log, '>> Scraping...')
self.scrape()

quality = None

# Select Quality
self.sizes_error = False

# v1.1.0: Get quality according to max size
if self.auto_max:
lprint(self.log, '>> Getting quality according to max size.')
sizes = getSizes(
self.r_url,
self.head,
self.proxies,
self.videos
)

# Loop through qualities and compare each size to max_s
for v, s in sorted(sizes, key=lambda i: i[1])[::-1]:
if s <= self.max_s:
quality = j(v)
break

# Store error if no quality is <= max_s
if not quality:
lprint(self.log, '>>>> All qualities are > {} bytes' \
.format(self.max_s))
self.sizes_error = True
return
else:
if self.max:
quality = j(self.videos[0])
elif self.min:
quality = j(self.videos[-1])
else:
quality = j(UserSelect(self.videos))

self.video = self.r_url + quality

self.file_name = '{}{}-{}.mp4'.format(
self.path,
self.UNQ,
quality
# v1.0.8: fix file name dups
).replace('.mp4' * 2, '.mp4')

self.duration = getDuration(self.page)
self.size = int(
self.head(
if not self.auto_max:
self.size = int(
self.head(
self.video,
_proxies=self.proxies
).headers['Content-Length']
)


self.ischeck = True

def download(self):
"""
Automatic usage of the class
Returns:
self.file_name (str): PATH of the downloaded video
0: Size exceeds maximum
Expand All @@ -178,14 +223,16 @@ def download(self):
"""
if not self.ischeck:
self.check()
self.ischeck = False

if self.size > self.max_s:
if self.size > self.max_s or self.sizes_error:
lprint(self.log, '>> Size > {} bytes'.format(self.max_s))
return 0

if self.duration > self.max_d:
lprint(self.log, '>> Duration > {}s'.format(self.max_d))
return 1
if self.duration:
if self.duration > self.max_d:
lprint(self.log, '>> Duration > {}s'.format(self.max_d))
return 1

if self.overwrite and ope(self.file_name):
os.remove(self.file_name)
Expand All @@ -204,5 +251,4 @@ def download(self):
Clean(self.path)
lprint(self.log, '>> Done')

self.ischeck = False
return self.file_name
return self.file_name
40 changes: 33 additions & 7 deletions redvid/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,52 @@ def toJsonUrl(url):
return url + '.json'

def getUNQ(page):
regex = r'https://v\.redd\.it/[a-zA-Z0-9]+'
regex = r'/([a-zA-Z0-9]+)/DASHPlaylist.mpd'
Match = re.findall(regex, page.text)
UNQ = None
if Match:
UNQ = Match[0]
UNQ += '/' if UNQ[-1] != '/' else UNQ
return UNQ

# v1.0.9: getting duration
def getDuration(page):
dur = re.findall(r'"duration": (\d+)', page.text)
return int(dur[0]) if dur else None

# v1.1.0: looping through qualities to get all sizes
def getSizes(u, h, p, vs):
sizes = []
for v in vs:
sizes.append(
(v, int(h(
u + j(v),
_proxies=p
).headers['Content-Length']))
)
return sizes

def mpdParse(mpd):
# v1.0.8: Fix for new reddit mechanism
tag_vid = r'<BaseURL>(DASH_)?(\d+)(\.mp4)?</BaseURL>'
tag_aud = r'<BaseURL>(DASH_)?(audio)(\.mp4)?</BaseURL>'
lst_vid = re.findall(tag_vid, mpd)
yield sorted(lst_vid, key=lambda a: int(a[1]))[::-1]
yield re.findall(tag_aud, mpd)
tags = r'<BaseURL>(DASH_)?(.*?)(\.mp4)?</BaseURL>'
re_tags = re.findall(tags, mpd)

# Filter audio tag
tag_aud = None
for n, tag in enumerate(re_tags):
if 'audio' in tag:
tag_aud = re_tags.pop(n)
break

if not re_tags:
return 0, 0

# Allow getting qualities with old reddit method
try:
yield sorted(re_tags, key=lambda a: int(a[1]))[::-1]
except:
yield sorted(re_tags, key=lambda a: a[1])[::-1]

yield tag_aud

def UserSelect(lst):
print('\nQualities available:')
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

setuptools.setup(
name="redvid",
version="1.0.9",
version="1.1.0",
author="elmoiv",
author_email="[email protected]",
description="Smart downloader for Reddit hosted videos",
Expand Down
10 changes: 10 additions & 0 deletions tests/test12.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from redvid import Downloader

reddit = Downloader()
reddit.auto_max = True

# redvid will find the quality with the size
# that does not exceed max_s (3 MB)
reddit.max_s = 3 * (1 << 20)
reddit.url = 'https://www.reddit.com/r/Unexpected/comments/9n8mmz/_/'
reddit.download()

0 comments on commit 5fd3094

Please sign in to comment.