# olpc-video.py : written in Python : www.python.org # Google App Engine approach to YouTube Search, Player, and Playlists # a separate player optimizes access and lowers load times for pages # also allows a separate XO users' community and social data set # datetime, users, and db are currently unused, but will be useful for community and social data #from datetime import datetime #from google.appengine.api import users #from google.appengine.ext import db import cgi from google.appengine.ext import webapp from google.appengine.ext.webapp.util import run_wsgi_app from google.appengine.api.urlfetch import fetch, GET # MainPage at /olpc-video/ class MainPage(webapp.RequestHandler): def get(self): self.response.out.write(''' Video for XO Laptop

Videos for the XO Laptop

Special:

''') # Explaining how to get Adobe Flash working on the laptop class Flash(webapp.RequestHandler): def get(self): self.response.out.write(''' Flash for XO

Adobe Flash for XO Laptop

You will need Adobe Flash to play YouTube videos on the XO laptop

Instructions on OLPC Wiki ''') # Explaining why olpc-video is better than using YouTube.com on the XO class Separate(webapp.RequestHandler): def get(self): self.response.out.write(''' Why the XO Player?

Why have a separate player for the XO?

''') # Player is where videos are displayed class Player(webapp.RequestHandler): def get(self): # print JavaScript (probably should be in a play.js or other file) watchVid = cgi.escape(self.request.get('v')) self.response.out.write(''' Video for XO Laptop

Videos for the XO Laptop

''') # add &welcome=true to a player link to display this close-able About: banner if(self.request.get('welcome') == 'true'): self.response.out.write("
\n\n
About:
"); #print HTML for player and icons. The Powered by YouTube image is requested for all uses of the YouTube API self.response.out.write('''
Sorry, you need to install Adobe Flash's player.
For XO Laptops Powered by YouTube.com
''') # Search page - power to filter and mark results as needed class Search(webapp.RequestHandler): def get(self): term = cgi.escape(self.request.get("term")) offset = self.request.get('offset') # see a sample API feed: http://gdata.youtube.com/feeds/api/videos?q=Pittsburgh&max-results=30&safeSearch=strict&format=5&v=2 if(offset != ''): VidResults = fetch('http://gdata.youtube.com/feeds/api/videos?q=' + term.replace(" ",",") + '&start-index=' + offset + '&max-results=30&safeSearch=strict&format=5&v=2', payload=None, method=GET, headers={}, allow_truncated=False, follow_redirects=True) else: VidResults = fetch('http://gdata.youtube.com/feeds/api/videos?q=' + term.replace(" ",",") + '&max-results=30&safeSearch=strict&format=5&v=2', payload=None, method=GET, headers={}, allow_truncated=False, follow_redirects=True) vidData = VidResults.content processedResults = "" # check if no videos were found if(vidData.find("totalResults>0There were no results on YouTube.

" else: # col == 0: left column of results ; col == 1 : right column of results col = 0 while(vidData.find(" -1): # extract video title title = vidData[vidData.find(' 60): # convert seconds to mm:ss seconds = duration % 60 if(seconds > 9): duration = str((duration - seconds) / 60) + ":" + str(seconds) else: duration = str((duration - seconds) / 60) + ":0" + str(seconds) else: if(duration > 9): duration = "0:" + str(duration) else: duration = "0:0" + str(duration) # extract YouTube video ID player = vidData[vidData.find('") : len(player) ] player = player[0 : player.find("'/>")] # extract video thumbnail (small pic) thumbnail = vidData[vidData.find('" processedResults = processedResults + '' + title + '
Length: ' + duration + '' if(col == 1): processedResults = processedResults + "" col = 0 else: col = 1 # filter follow-up: repeats search on two more pages looking for playable videos if(processedResults.find('img') == -1): if(offset >= 60): processedResults = "

No popular videos for your query (" + term + ") could run in the player.

UNDER CONSTRUCTION: My XO laptop has an odd audio echo on some videos, and I am experimenting with ways to keep these from appearing in search. I intend to test this and find a workable solution.

" else: offset = offset + 30 self.redirect('/olpc-video/search?term=' + term + '&offset=' + str(offset)) # print page with results self.response.out.write(''' Search Results from YouTube

Videos for the XO Laptop

''' + processedResults + '''

Powered by YouTube.com
''') # Library section from OLPCMagongo's YouTube playlists # TODO: check for more than 25 playlists, work out social data for library videos class Library(webapp.RequestHandler): def get(self): # "shelf" is used instead of "playlist" - these videos are not played as a playlist libraryShelf = self.request.get('shelf') offset = self.request.get('offset') if(libraryShelf == ''): # no shelf selected; requesting the library homepage shelfList = fetch('http://gdata.youtube.com/feeds/api/users/olpcmagongo/playlists', payload=None, method=GET, headers={}, allow_truncated=False, follow_redirects=True) shelfData = shelfList.content shelfData = shelfData[shelfData.find('') : len(shelfData)] self.response.out.write(''' OLPC Video Library

OLPC Video Library

''') processedResults = "" while(shelfData.find('') != -1): # extract information about each playlist shelfTitle = shelfData[shelfData.find("")] shelfDescription = shelfData[shelfData.find("<content type=") + 21 : shelfData.find("</content>")] shelfID = shelfData[shelfData.find("<yt:playlistId>") + 15 : len(shelfData)] shelfData = shelfID[shelfID.find("</yt:playlistId>") : len(shelfID)] shelfID = shelfID[0 : shelfID.find("</yt:playlistId>")] # write the playlist information processedResults = processedResults + '<div><a href="/olpc-video/library?shelf=' + shelfID + '&offset=1"><h4>' + shelfTitle + '</h4></a>' + shelfDescription + '<hr/></div>' self.response.out.write(processedResults) else: # specific shelf requested - load this playlist's information if(offset == ''): vidList = fetch('http://gdata.youtube.com/feeds/api/playlists/' + cgi.escape(libraryShelf) + '?max-results=30', payload=None, method=GET, headers={}, allow_truncated=False, follow_redirects=True) else: vidList = fetch('http://gdata.youtube.com/feeds/api/playlists/' + cgi.escape(libraryShelf) + '?max-results=30&start-index=' + offset, payload=None, method=GET, headers={}, allow_truncated=False, follow_redirects=True) vidData = vidList.content shelfTitle = vidData[vidData.find('<title type=') + 19 : vidData.find('')] self.response.out.write(''' OLPC Video Library - ''' + shelfTitle + '''

Videos: ''' + shelfTitle + '''

← Library Page
''') vidData = vidData[vidData.find('') : len(vidData)] # col == 0: left column of results ; col == 1 : right column of results col = 0 vidCount = 0 processedResults = "" # extract information about each video while(vidData.find('') != -1): # extract video title title = vidData[vidData.find('') - 1]) if(duration > 60): # convert seconds to mm:ss seconds = duration % 60 if(seconds > 9): duration = str((duration - seconds) / 60) + ":" + str(seconds) else: duration = str((duration - seconds) / 60) + ":0" + str(seconds) else: if(duration > 9): duration = "0:" + str(duration) else: duration = "0:0" + str(duration) # extract YouTube video ID player = vidData[vidData.find('") : len(player) ] player = player[0 : player.find("'/>")] # infer video thumbnail (small pic) thumbnail = "http://i.ytimg.com/vi/" + player[player.find('?v=') + 3 : len(player)] + "/2.jpg" # print into the left or right column if(col == 0): processedResults = processedResults + "" processedResults = processedResults + '' + title + '
Length: ' + duration + '
' vidCount = vidCount + 1 if(col == 1): processedResults = processedResults + "" col = 0 else: col = 1 # have information, now print if(vidCount % 2 == 1): self.response.out.write("" + processedResults + "
") else: self.response.out.write("" + processedResults + "
") if(vidCount == 30): self.response.out.write('''
''') # print YouTube API image and close page self.response.out.write('''
Powered by YouTube.com
''') # TODO: make special ClassShare class ClassShare(webapp.RequestHandler): def get(self): self.response.out.write(''' ''') # NTV Kenya section - shows most recent videos from a Kenyan news channel # TODO: add social data class NTVKenya(webapp.RequestHandler): def get(self): offset = self.request.get('offset') if(offset == ''): vidList = fetch('http://gdata.youtube.com/feeds/api/users/ntvkenya/uploads?orderby=published&max-results=30', payload=None, method=GET, headers={}, allow_truncated=False, follow_redirects=True) else: vidList = fetch('http://gdata.youtube.com/feeds/api/users/ntvkenya/uploads?orderby=published&max-results=30&start-index=' + offset, payload=None, method=GET, headers={}, allow_truncated=False, follow_redirects=True) vidData = vidList.content self.response.out.write(''' NTV Kenya Videos

News from NTV Kenya

← Newest
''') vidData = vidData[vidData.find('') : len(vidData)] # col == 0: left column of results ; col == 1 : right column of results col = 0 vidCount = 0 processedResults = "" # extract information about each video while(vidData.find('') != -1): # extract video title title = vidData[vidData.find('') - 1]) if(duration > 60): # convert seconds to mm:ss seconds = duration % 60 if(seconds > 9): duration = str((duration - seconds) / 60) + ":" + str(seconds) else: duration = str((duration - seconds) / 60) + ":0" + str(seconds) else: if(duration > 9): duration = "0:" + str(duration) else: duration = "0:0" + str(duration) # extract YouTube video ID player = vidData[vidData.find('") : len(player) ] player = player[0 : player.find("'/>")] # infer video thumbnail (small pic) thumbnail = "http://i.ytimg.com/vi/" + player[player.find('?v=') + 3 : len(player)] + "/2.jpg" # print into the left or right column if(col == 0): processedResults = processedResults + "" processedResults = processedResults + '' + title + '
Length: ' + duration + '
' vidCount = vidCount + 1 if(col == 1): processedResults = processedResults + "" col = 0 else: col = 1 # have information, now print if(vidCount % 2 == 1): self.response.out.write("" + processedResults + "
") else: self.response.out.write("" + processedResults + "
") # print the "Find More" link self.response.out.write('''
''') # print YouTube API image and close self.response.out.write('''
Powered by YouTube.com
''') # core URL-sorting and other details application = webapp.WSGIApplication( [('/olpc-video/search.*', Search), ('/olpc-video/player.*', Player), ('/olpc-video/library.*', Library), ('/olpc-video/class.*', ClassShare), ('/olpc-video/NTV-Kenya', NTVKenya), ('/olpc-video/separate', Separate), ('/olpc-video/flash', Flash), ('/olpc-video.*', MainPage)], debug=True) def main(): run_wsgi_app(application) if __name__ == "__main__": main()