# Once upon a time I got interested in Magic the Gathering and remembered that one of my favorite youtubers,
# Day[9], had a show about MtG on Geek and Sundry.
# I decided to use my python skills and write a little utility to format youtube playlists for the
# desktop player I use to watch Youtube videos.
# First you set up application access permissions on your Google account if you want to use Google APIs.
# That grants you 'client_secret.json', a file with a few variables you need to get an access token later.
# Our imports:
# interface for google APIs
import google.oauth2.credentials
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
import regex # find the coveted string
import json # save and load access information
import isodate # format video durations
# Settings for the script
CLIENT_SECRETS_FILE = "client_secret.json"
SCOPES = ['https://www.googleapis.com/auth/youtube.readonly']
API_SERVICE_NAME = 'youtube'
API_VERSION = 'v3'
# You need to request an authorization code to get an access and refresh token,
# which you can then save and use pretty much forever.
flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRETS_FILE, SCOPES)
credentials = flow.run_console()
# You are given a url where you authorize the application with a Google account, and you receive the code.
# (I didn't explicitly do it here, since the url itself reveals some security-sensitive account information)
"Please visit this URL to authorize this application: "
"Enter the authorization code: "
# Upon completing that, we get a credentials variable, and we can cherry-pick the most important components.
creds = {
'access_token' : credentials.token,
'refresh_token' : credentials.refresh_token,
'token_uri' : credentials.token_uri,
'client_id' : credentials.client_id,
'client_secret' : credentials.client_secret
}
with open('creds.json', 'w+') as fout :
json.dump(creds, fout)
# Until a few days ago, there were API functions to save credentials data, now we have to do it by hand.
# And this is what usage looks like now, load the file and plot it into the Credentials constructor:
with open('creds.json', 'r') as fin :
creds = json.load(fin)
credentials = google.oauth2.credentials.Credentials(
creds['access_token'],
refresh_token = creds['refresh_token'],
token_uri = creds['token_uri'],
client_id = creds['client_id'],
client_secret = creds['client_secret'])
# And this is our instrument of war. We can now run queries against Youtube Data API.
service = build(API_SERVICE_NAME, API_VERSION, credentials = credentials)
# Test query, and we'll save the channel ID for later:
results = service.channels().list(part='snippet,contentDetails,statistics',
forUsername='geekandsundry').execute()
print('This channel\'s ID is %s. Its title is %s, and it has %s views.' %
(results['items'][0]['id'],
results['items'][0]['snippet']['title'],
results['items'][0]['statistics']['viewCount']))
channel_id = results['items'][0]['id']
# Everything's working, now we get to the interesting parts.
# This is the playlist format of the media player:
# i is integer position, starting with 1
MPCPLAYLIST # first line
"i",type,0
"i",label,"video title"
"i",time,"duration HH:MM:SS"
"i",filename,"video url"
# First we gotta find the playlist we want to convert.
playlist_id = ''
playlist_itemcount = 0
playlist_title = ''
# If we spot Day[9] in the title - that's the one.
re = regex.compile(r'Day\[9\]')
# Here's how we gather all the playlists:
pl = service.playlists().list(part='id, contentDetails, snippet',
channelId=channel_id).execute()
counter = 0
while (True) :
for i in pl['items'] :
counter += 1
title = i['snippet']['title']
id = i['id']
count = i['contentDetails']['itemCount']
print(title)
print(id)
if (re.search(title)) :
print('GOT IT!!')
playlist_id = id
playlist_itemcount = count
playlist_title = title
print('')
# Youtube Data API retrieves items in pages of 5, and we need to make continious queries and "next page" tokens.
if(pl.get('nextPageToken')) :
pl = service.playlists().list(part='id, contentDetails, snippet',
channelId=channel_id,
pageToken=pl['nextPageToken']).execute()
else :
break
print(str(counter) + ' playlists total')
# Got it!
print(playlist_title)
print(playlist_id)
print('Has ' + str(playlist_itemcount) + ' videos')
# Now, in a similar fashion, we'll go through the playlist items, gathering required parameters:
id_prefix = 'https://www.youtube.com/watch?v='
playlist_info = []
playlist_itemlist = service.playlistItems().list(part='contentDetails, id, snippet',
playlistId=playlist_id).execute()
playlist_length = playlist_itemlist['pageInfo']['totalResults']
while(True) :
for i in playlist_itemlist['items'] :
title = i['snippet']['title']
vid_id = i['contentDetails']['videoId']
dur = service.videos().list(part='contentDetails',
id=vid_id).execute()
dur_str = isodate.parse_duration(dur['items'][0]['contentDetails']['duration'])
dur_format = '%H:%M:%S'
dur_str = str(isodate.time_isoformat(dur_str, dur_format))
vid_id = id_prefix + vid_id
#playlist_info.append((title, vid_id, dur_str, position, published))
playlist_info.append((title, vid_id, dur_str))
if(playlist_itemlist.get('nextPageToken')) :
playlist_itemlist = service.playlistItems().list(part='contentDetails, id, snippet',
playlistId=playlist_id,
pageToken=playlist_itemlist['nextPageToken']).execute()
else :
break
# Let's see what it looks like:
playlist_info[:5]
# Looks like the playlist, as it the often the case with youtube playlists, is reversed.
playlist_info.reverse()
# We got everything we need. Time to format.
filename = 'Spellslingers.mpcpl'
counter = 0
with open(filename, 'w') as f :
f.write('MPCPLAYLIST\n')
for info in playlist_info :
counter += 1
title, url, dur = info
f.write('%d,type,0\n' % counter)
f.write('%d,label,%s\n' %(counter, title))
f.write('%d,time,%s\n' %(counter, dur))
f.write('%d,filename,%s\n' %(counter, url))
# And there it is.
with open(filename, 'r') as f:
lines = f.readlines()
for line in lines[:13]:
print(line)
for line in lines[-12:]:
print(line)