Film Maker

Programming Snapshot – YouTube Metadata

Article from Issue 209/2018
Author(s):

Instead of manually editing the metadata of YouTube movies, video craftsman Mike Schilli dips into YouTube’s API spell book and lets a script automatically do the work.

If one day someone should invite me onto a TV talk show, in which I could brag about my best productivity tricks, I would say this: The most effective way forward is to be able to make quick changes at any time, which you can revert later on – just as quickly and almost without loss – if they turn out to be duds.

Software development with Git is a good example of this: Git makes it possible to boldly add new functions to programs in huge leaps or to rearrange the code on a large scale. If the whole thing turns out to be a crazy idea after a while, the developer simply reverts everything in just a second without anyone noticing what pipe dreams they were chasing in the meantime.

That's why I religiously put everything I produce under version control. My blog posts, my articles, my programs: The version control system lets me revert to yesterday's status at any time should the need arise, and for any reason.

But up to now, I have not been able to get the procedure to work for my YouTube videos. For example, if I change the title or even just one tag of one of my internationally-respected, quality movies (Figure 1), I can't simply tell YouTube later on: Today, all I've done is produce nonsense; please undo everything and make it look like yesterday.

Figure 1: Mike Schilli as a pancake tossing chef on YouTube.

Metadata with Version Control

Therefore, I want to entrust the metadata of my YouTube existence to a local YAML file (Listing 1) and version it with Git. A script then regularly checks the entries in the file, reads the ID of each movie from the YAML data, and checks on YouTube whether everything is set up according to the file (i.e., whether the title in the file matches the video page's metadata). In the event of differences, the script adapts the online YouTube data to the YAML data. Listing 1 [1] defines a long list of videos with their IDs and titles. It could easily be extended with tags, a thumbnail image, a release date, or other metadata.

Listing 1

videos.yaml

1 videos:
2   - id: _3i5yVoTvCs
3     title: "How to flip German pancakes"
4   - id: brPfE66FC24
5     title: "Tivo Stream Cooling Fan Replacement"
6   - id: 2qxXhW7RxsY
7     title: "Rio Portable Beach Shelter Assembly Instructions"

At the User's Service

But before the script is allowed to access or change the user data, Google has to grant the new application access to the account holder's data, because Joe Public is not allowed to mess around with my videos. Google obtains the user's consent in Listing 2 using the Google API [2] to guide the user's browser to a Google page on which the user logs in and then confirms that the application actually should have access (Figure 2).

Listing 2

youtube-sync

01 #!/usr/bin/python
02 import httplib2
03 import os
04 import sys
05 import yaml
06
07 from apiclient.discovery import build
08 from apiclient.errors import HttpError
09 from oauth2client.client import \
10   flow_from_clientsecrets
11 from oauth2client.file import Storage
12 from oauth2client.tools import \
13   argparser, run_flow
14
15 CLIENT_SECRETS_FILE = "client-secrets.json"
16 YOUTUBE_READ_WRITE_SCOPE = \
17   "https://www.googleapis.com/auth/youtube"
18 YOUTUBE_API_SERVICE_NAME = "youtube"
19 YOUTUBE_API_VERSION      = "v3"
20
21 def get_authenticated_service(args):
22   flow = flow_from_clientsecrets(
23     CLIENT_SECRETS_FILE,
24     scope=YOUTUBE_READ_WRITE_SCOPE)
25
26   storage = Storage("oauth2.json");
27   credentials = storage.get()
28
29   if credentials is None or \
30      credentials.invalid:
31     credentials = \
32             run_flow(flow, storage, args)
33
34   return build(YOUTUBE_API_SERVICE_NAME,
35     YOUTUBE_API_VERSION,
36     http=credentials.authorize(
37         httplib2.Http()))
38
39 def video_update(youtube, id, title):
40   response = youtube.videos().list(
41     id=id, part='snippet').execute()
42
43   if not response["items"]:
44     print("Video '%s' was not found." % id)
45     sys.exit(1)
46
47   snippet = response["items"][0]["snippet"]
48
49   if snippet['title'] == title:
50     print("%s: Unchanged" % id)
51     return
52
53   snippet['title'] = title
54
55   try:
56     youtube.videos().update(
57       part='snippet',
58       body=dict(
59          snippet=snippet, id=id)).execute()
60   except HttpError, e:
61     print("HTTP error %d: %s" % \
62             (e.resp.status, e.content))
63   else:
64     print("Updated OK")
65
66 if __name__ == "__main__":
67   args = argparser.parse_args()
68   youtube = get_authenticated_service(args)
69
70   stream = open("videos.yaml", "r")
71   all = yaml.load(stream)
72   for video in all['videos']:
73     video_update(youtube, video['id'],
74             video['title'])
Figure 2: The first time the script is called, it opens a browser that asks for the user's consent.

To do this, API jockeys need to create a new project on the Google Cloud Platform Console [2] (Figure 3). Then navigate to Create credentials (Figure 4) and select OAuth client ID [3] (not API key, that's for project management only). Since it is a desktop program and not a web application, you have to select Other in the selection menu for the application type. The strings then produced by Google for the Client ID and Client Secret (Figure 5) need to be entered into a JSON file as shown in Listing 3.

Listing 3

client-secrets.json

1 {
2   "installed": {
3     "client_id": "XXX",
4     "client_secret": "YYY",
5     "redirect_uris": ["http://localhost", "urn:ietf:wg:oauth:2.0:oob"],
6     "auth_uri": "https://accounts.google.com/o/oauth2/auth",
7     "token_uri": "https://accounts.google.com/o/oauth2/token"
8   }
9 }
Figure 3: Creating a new project on the Google Cloud Platform Console.
Figure 4: Google produces the client ID and key.
Figure 5: Active API keys on Google Console.

After the script's first run, when the user has successfully granted permission in the browser, the browser branches to a page with a The authentication flow has completed message. The script drops an OAuth 2 access token into the oauth2.json file, thus granting access to the user's data for future calls without the user having to agree again.

This works until the access token expires. The corresponding expiration date is also noted in the JSON file. The file also contains a refresh token that the script can use to request a new token after the current access token expires. This practically works infinitely – unless the user goes to the Google Console (Figure 5) and revokes access for the client, in which case Google pulls the plug.

The get_authenticated_service() function in line 21 of Listing 2 defines YOUTUBE_READ_WRITE_SCOPE as the access scope, thus requesting read and write access. The interaction with the browser and the underlying OAuth 2 token dance is nicely abstracted by the Google API SDK; the script only calls the flow_from_clientsecrets() and run_flow() functions from the two oauth2client.client and oauth2client.tools packages. This works equally well in the browser on both Linux and Mac OS.

Line 70 reads the YAML file with the locally stored video metadata; line 71 iterates over all movies located there. For each of them, it calls the video_update() function defined in line 39, which first retrieves the online movie's metadata by its ID using youtube.videos().list(). Data retrieved is limited to the snippet area, which YouTube uses to designate the title, tags, description, and a few other fields.

Line 49 retrieves the movie title from this metadata and compares it with the local version. If the two titles do not match, line 56 triggers the update() method in a try block. The function expects the originally retrieved metadata along with the new, modified title as parameters. If an error occurs during transfer, line 61 prints the HTTP error message; otherwise line 64 reports Updated OK – and the metadata on YouTube now exactly matches the metadata stored locally in the version control system.

Installation and Outlook

The SDK needed to run the script is available as a Python package from the standard repository and can be installed via pip:

pip install --user --upgrade google-api-python-client

After the script's first run – with the browser window popping up at the beginning to obtain the user's consent – it produces the following output with the YAML file in Listing 1:

<$> ./youtube-sync
_3i5yVoTvCs: Unchanged
brPfE66FC24: Unchanged
2qxXhW7RxsY: Unchanged

Since all title strings in the retrieved YouTube metadata match the YAML data, the script does not make any changes. However, if a title text changes in the YAML file, the script fires it off to YouTube, which refreshes the movie's metadata. The script confirms such an action with Updated OK.

The script can be extended as needed, so that it can also upload videos from the hard disk to YouTube if required and automatically synchronize the local status of your movie collection with the publicly accessible movies on YouTube in each run. If you version your local collection and its metadata with a version control system, you can jump back and forth in time, making changes, and reverting if an idea turns out to be less than brilliant.

The Author

Mike Schilli works as a software engineer in the San Francisco Bay area, California. Each month in his column, which has been running since 1997, he researches practical applications of various programming languages. If you go to mailto:mschilli@perlmeister.com he will gladly answer any questions.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • YouTube Players

    YouTube offers more than just funny kitten movies; you will also find more than 60 million music videos. With a native YouTube client for Linux, you can use this online jukebox as conveniently as your local music collection.

  • Perl: YouTube Statistics

    Hobby YouTuber Mike Schilli is interested in whether his videos go viral. What better way to check skyrocketing viewer numbers than letting a Perl script analyze the daily trends and watch for unexpected upswings?

  • Downloading Web Video

    With the right tools, you can store YouTube movies on your hard disk and view them when Internet access is unsatisfactory or unavailable.

  • Legal Video Downloads: YouTube goes Offline

    Until now, watching YouTube videos offline has been an awkward business, possible only with Firefox plugins or similar contraptions. Now, with the help of Creative Commons licenses, this could soon change.

  • Google Launches New Channel On YouTube

    Ellen Ko of Google's Open Source Team announces the creation of a new Google YouTube channel, googleOSPO, created to house all the Google and open source related videos in one place.

comments powered by Disqus

Direct Download

Read full article as PDF:

Price $2.95

News