Compare commits
8 Commits
5d58428cd9
...
master
Author | SHA1 | Date | |
---|---|---|---|
668e497ae0 | |||
3c522c6459 | |||
2a89badcb6 | |||
4084f836cc | |||
9e9a1e19d5 | |||
7e68eb982b | |||
1a4e78567e | |||
8bd3b26900 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
|||||||
youtube.key
|
youtube.key
|
||||||
|
/playlists/
|
||||||
|
|
||||||
# ---> Python
|
# ---> Python
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
|
21
README.md
21
README.md
@ -1,2 +1,23 @@
|
|||||||
# yt-unlist-saver
|
# yt-unlist-saver
|
||||||
|
|
||||||
|
This program will download all unlisted videos and descriptions in a given YouTube playlist that were uploaded on or before 2017.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Clone the repository.
|
||||||
|
2. Install youtube-dl Python library
|
||||||
|
3. Get a YouTube API key following the instructions here [YouTube Data API Overview](https://developers.google.com/youtube/registering_an_application)
|
||||||
|
4. Save the API key to a file called `youtube.key`
|
||||||
|
```sh
|
||||||
|
pip install youtube_dl
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Go to the desired playlist in your browser.
|
||||||
|
2. On the address bar you will see an URL like the following: https://www.youtube.com/watch?v=swRkBEDmaqk&list=PLRmPOFgS7WQFiijsHm-nAlpVAZaNXYRkx
|
||||||
|
3. The playlist ID is after the `list=` On the playlist above it would be `PLRmPOFgS7WQFiijsHm-nAlpVAZaNXYRkx`
|
||||||
|
4. Run the following command to download the playlist.
|
||||||
|
```sh
|
||||||
|
python yt-unlist-saver.py [Playlist ID]
|
||||||
|
```
|
||||||
|
@ -19,9 +19,13 @@ class YouTubeAPI(object):
|
|||||||
def getVideo(self, part, id):
|
def getVideo(self, part, id):
|
||||||
return self.getRequest('videos?part={}&id={}'.format(part, id))
|
return self.getRequest('videos?part={}&id={}'.format(part, id))
|
||||||
|
|
||||||
def getVideoInfo(self, tag):
|
def getVideoInfo(self, id):
|
||||||
snippet = self.getVideo('snippet', tag)
|
snippet = self.getVideo('snippet', id)
|
||||||
status = self.getVideo('status', tag)
|
status = self.getVideo('status', id)
|
||||||
|
|
||||||
|
# Check for private videos
|
||||||
|
if len(snippet['items']) == 0:
|
||||||
|
return '', '', '', '', '', '', 'private', id
|
||||||
snippet = snippet['items'][0]['snippet']
|
snippet = snippet['items'][0]['snippet']
|
||||||
status = status['items'][0]['status']
|
status = status['items'][0]['status']
|
||||||
title = snippet['title']
|
title = snippet['title']
|
||||||
@ -34,7 +38,7 @@ class YouTubeAPI(object):
|
|||||||
except:
|
except:
|
||||||
tags = ''
|
tags = ''
|
||||||
privacy = status['privacyStatus']
|
privacy = status['privacyStatus']
|
||||||
return title, date, channel, channelId, description, tags, privacy
|
return title, date, channel, channelId, description, tags, privacy, id
|
||||||
|
|
||||||
def getPlaylist(self, id, pageToken=''):
|
def getPlaylist(self, id, pageToken=''):
|
||||||
if(pageToken):
|
if(pageToken):
|
||||||
|
@ -1,22 +1,42 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from youtube_api import YouTubeAPI
|
from youtube_api import YouTubeAPI
|
||||||
import sys
|
import sys, os
|
||||||
import youtube_dl
|
import youtube_dl
|
||||||
|
|
||||||
# Read YouTube API Key.
|
# Read YouTube API Key.
|
||||||
def getYouTubeAPIKey():
|
def getYouTubeAPIKey():
|
||||||
return open('youtube.key', 'r').read()
|
return open('youtube.key', 'r').read()
|
||||||
|
|
||||||
|
def getTitle(info):
|
||||||
|
return info[0]
|
||||||
|
|
||||||
def getUploadTime(info):
|
def getUploadTime(info):
|
||||||
return info[1]
|
return info[1]
|
||||||
|
|
||||||
def getYear(info):
|
def getYear(info):
|
||||||
return int(getUploadTime(info)[:4])
|
return int(getUploadTime(info)[:4])
|
||||||
|
|
||||||
|
def getChannel(info):
|
||||||
|
return info[2]
|
||||||
|
|
||||||
|
def getChannelId(info):
|
||||||
|
return info[3]
|
||||||
|
|
||||||
|
def getDescription(info):
|
||||||
|
return info[4]
|
||||||
|
|
||||||
|
def getTags(info):
|
||||||
|
if isinstance(info[5], list):
|
||||||
|
return '[' + ','.join(info[5]) + ']'
|
||||||
|
return info[5]
|
||||||
|
|
||||||
def getPrivacy(info):
|
def getPrivacy(info):
|
||||||
return info[6]
|
return info[6]
|
||||||
|
|
||||||
|
def getId(info):
|
||||||
|
return info[7]
|
||||||
|
|
||||||
# Print out information about a video.
|
# Print out information about a video.
|
||||||
def printVideoInfo(info):
|
def printVideoInfo(info):
|
||||||
title, date, channel, channelId, description, tags, privacy = info
|
title, date, channel, channelId, description, tags, privacy = info
|
||||||
@ -27,11 +47,45 @@ def printVideoInfo(info):
|
|||||||
print('Tags: ', tags)
|
print('Tags: ', tags)
|
||||||
print('Privacy: ' + privacy)
|
print('Privacy: ' + privacy)
|
||||||
|
|
||||||
|
playlistId = sys.argv[1]
|
||||||
|
|
||||||
|
# Create download directory
|
||||||
|
downloadDir = 'playlists'
|
||||||
|
playlistDir = downloadDir+'/'+playlistId
|
||||||
|
if not os.path.exists(downloadDir):
|
||||||
|
os.mkdir(downloadDir)
|
||||||
|
if not os.path.exists(playlistDir):
|
||||||
|
os.mkdir(playlistDir)
|
||||||
|
|
||||||
api = YouTubeAPI(getYouTubeAPIKey())
|
api = YouTubeAPI(getYouTubeAPIKey())
|
||||||
videos = api.getPlaylistVideos(sys.argv[1])
|
videos = api.getPlaylistVideos(playlistId)
|
||||||
|
|
||||||
videoInfo = [api.getVideoInfo(video['snippet']['resourceId']['videoId']) for video in videos]
|
videoInfo = [api.getVideoInfo(video['snippet']['resourceId']['videoId']) for video in videos]
|
||||||
filteredVideos = [video for video in videoInfo if getPrivacy(video) == 'unlisted' and getYear(video) <= 2017]
|
filteredVideos = [video for video in videoInfo if getPrivacy(video) == 'unlisted' and getYear(video) <= 2017]
|
||||||
|
|
||||||
|
ydl_opts = {
|
||||||
|
'outtmpl': '{}/%(uploader)s-%(title)s-%(id)s.%(ext)s'.format(playlistDir)
|
||||||
|
}
|
||||||
|
|
||||||
for v in filteredVideos:
|
for v in filteredVideos:
|
||||||
print(getYear(v))
|
|
||||||
|
# Save metadata to file
|
||||||
|
descriptionFile = '{}/{}-{}-{}.txt'.format(playlistDir,
|
||||||
|
getChannel(v),
|
||||||
|
getTitle(v).replace('/', ''),
|
||||||
|
getId(v))
|
||||||
|
if not os.path.exists(descriptionFile):
|
||||||
|
description = open(descriptionFile, 'w')
|
||||||
|
description.write('Title: ' + getTitle(v) + '\n')
|
||||||
|
description.write('Uploaded: ' + getUploadTime(v) + '\n')
|
||||||
|
description.write('Channel: ' + getChannel(v) + '\n')
|
||||||
|
description.write('Channel ID: ' + getChannelId(v) + '\n')
|
||||||
|
description.write('Description: ' + getDescription(v) + '\n')
|
||||||
|
description.write('Tags: ' + getTags(v) + '\n')
|
||||||
|
description.write('Video ID: ' + getId(v) + '\n')
|
||||||
|
description.close()
|
||||||
|
|
||||||
|
videoURL = 'https://www.youtube.com/watch?v='+getId(v)
|
||||||
|
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
|
||||||
|
ydl.download([videoURL])
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user