2018-08-14 07:45:17 -07:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
2018-09-28 14:44:35 -07:00
|
|
|
import os
|
2015-05-11 21:22:27 -07:00
|
|
|
import opml
|
|
|
|
import feedparser
|
|
|
|
import youtube_dl
|
2018-02-07 08:43:37 -08:00
|
|
|
import sys
|
2018-09-28 14:41:33 -07:00
|
|
|
from pathlib import Path
|
2018-09-28 14:44:35 -07:00
|
|
|
import argparse
|
2015-05-11 21:22:27 -07:00
|
|
|
from time import time, mktime, strptime
|
|
|
|
from datetime import datetime
|
2018-09-28 14:44:35 -07:00
|
|
|
from dateutil.relativedelta import relativedelta
|
|
|
|
|
2018-10-08 04:39:45 -07:00
|
|
|
if sys.version_info.major < 3 and sys.version_info.minor < 6:
|
|
|
|
raise Exception('Must be using Python 3.6 or greater')
|
|
|
|
|
2018-09-28 14:44:35 -07:00
|
|
|
if __name__ == '__main__':
|
|
|
|
|
2018-09-28 14:44:56 -07:00
|
|
|
parser = argparse.ArgumentParser('Download YouTube subscriptions.')
|
2018-10-08 04:40:23 -07:00
|
|
|
parser.add_argument('--save-directory', '-o',
|
2018-09-28 14:44:56 -07:00
|
|
|
dest='output',
|
|
|
|
default=None,
|
|
|
|
help='The directory to which to save the videos.')
|
|
|
|
parser.add_argument('--retain', '-c',
|
|
|
|
dest='retain',
|
|
|
|
default=None,
|
|
|
|
help='Retain videos up to the given number of days since today.')
|
2018-10-08 04:40:23 -07:00
|
|
|
parser.add_argument('--since', '-s',
|
|
|
|
dest='since',
|
|
|
|
default=None,
|
|
|
|
help='Only download videos newer than the given number of days.')
|
2019-11-07 06:45:49 -08:00
|
|
|
parser.add_argument('--config-directory', '-f',
|
|
|
|
dest='config',
|
|
|
|
default=None,
|
|
|
|
help='The directory to which config is saved.')
|
2018-09-28 14:44:56 -07:00
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
# The current run time.
|
2018-10-08 04:41:15 -07:00
|
|
|
script_time = time()
|
2018-09-28 14:44:56 -07:00
|
|
|
|
2019-11-07 06:45:49 -08:00
|
|
|
subsPath = 'subs.xml'
|
|
|
|
if args.config is not None:
|
|
|
|
subsPath = f'{args.config}/{subsPath}'
|
|
|
|
outlines = opml.parse(subsPath)
|
2018-10-08 04:41:15 -07:00
|
|
|
|
2018-09-28 14:44:56 -07:00
|
|
|
if args.output is not None:
|
2018-10-08 04:41:38 -07:00
|
|
|
args.output = Path(args.output).absolute()
|
|
|
|
os.chdir(args.output)
|
2019-11-07 06:45:49 -08:00
|
|
|
else:
|
|
|
|
print('Must specify an ouput directory with -o')
|
2018-09-28 14:44:56 -07:00
|
|
|
|
2019-11-07 06:45:49 -08:00
|
|
|
lastPath = 'last.txt'
|
|
|
|
if args.config is not None:
|
|
|
|
lastPath = f'{args.config}/{lastPath}'
|
|
|
|
|
|
|
|
if not Path(lastPath).exists():
|
|
|
|
with open(lastPath, 'w') as f:
|
2018-09-28 14:44:56 -07:00
|
|
|
f.write(str(time()))
|
|
|
|
print('Initialized a last.txt file with current timestamp.')
|
2015-05-11 21:22:27 -07:00
|
|
|
else:
|
2019-11-07 06:45:49 -08:00
|
|
|
with open(lastPath, 'r') as f:
|
2018-09-28 14:44:56 -07:00
|
|
|
# The last run time.
|
2018-10-08 04:41:15 -07:00
|
|
|
threshold_time = datetime.utcfromtimestamp(float(f.read()))
|
|
|
|
|
2018-10-08 04:40:23 -07:00
|
|
|
# Overrule the time from which to download video if we've been asked to
|
|
|
|
# keep videos since a certain number of days ago.
|
|
|
|
if args.since is not None:
|
|
|
|
threshold_time = datetime.fromtimestamp(script_time) - relativedelta(days=float(args.since))
|
2018-09-28 14:44:56 -07:00
|
|
|
|
|
|
|
if args.retain is not None:
|
|
|
|
# Find the videos in this directory which are older than the time
|
|
|
|
# stamp since the last run and remove them.
|
2018-10-08 04:42:28 -07:00
|
|
|
keep_time = datetime.fromtimestamp(script_time) - relativedelta(days=float(args.retain))
|
|
|
|
for video in Path('.').glob('**/*.*'):
|
|
|
|
if 'last.txt' in str(video):
|
|
|
|
continue
|
2018-10-08 04:41:15 -07:00
|
|
|
modified_time = datetime.utcfromtimestamp(os.path.getmtime(video))
|
|
|
|
if modified_time < keep_time:
|
2018-09-28 14:44:56 -07:00
|
|
|
print(f'Removing {str(video)}.')
|
2018-09-28 14:55:22 -07:00
|
|
|
video.unlink()
|
2019-11-07 06:45:49 -08:00
|
|
|
|
2018-10-08 04:41:15 -07:00
|
|
|
urls = [outline.xmlUrl for outline in outlines[0]]
|
2019-11-07 06:45:49 -08:00
|
|
|
|
2018-09-28 14:44:56 -07:00
|
|
|
videos = []
|
|
|
|
for i, url in enumerate(urls):
|
|
|
|
print(f'Parsing through channel {i + 1} of {len(urls)}', end='\r')
|
|
|
|
feed = feedparser.parse(url)
|
|
|
|
for item in feed['items']:
|
2018-10-08 04:42:01 -07:00
|
|
|
video_time = datetime.fromtimestamp(mktime(item['published_parsed']))
|
|
|
|
if video_time > threshold_time:
|
2018-09-28 14:44:56 -07:00
|
|
|
videos.append(item['link'])
|
2018-10-08 04:42:15 -07:00
|
|
|
|
|
|
|
print(' ' * 100, end='\r')
|
2018-09-28 14:44:56 -07:00
|
|
|
if len(videos) == 0:
|
|
|
|
print('Sorry, no new video found')
|
2019-11-07 06:45:49 -08:00
|
|
|
quit()
|
2018-09-28 14:44:56 -07:00
|
|
|
else:
|
|
|
|
print(f'{len(videos)} new videos found')
|
2019-11-07 06:45:49 -08:00
|
|
|
|
2018-10-08 04:41:38 -07:00
|
|
|
ydl_opts = {'ignoreerrors': True, 'quiet': True, 'outtmpl': (args.output / Path('%(uploader)s', '%(title)s.%(ext)s')).as_posix()}
|
2019-11-07 06:45:49 -08:00
|
|
|
|
2018-09-28 14:44:56 -07:00
|
|
|
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
|
|
|
|
ydl.download(videos)
|
2019-11-07 06:45:49 -08:00
|
|
|
|
|
|
|
with open(lastPath, 'w') as f:
|
2018-10-08 04:41:15 -07:00
|
|
|
f.write(str(script_time))
|