Initial checking of picasa project.
[picasa.git] / mirror_picasa.py
1 #!/usr/local/bin/python
2
3 # Copyright (c) 2015 Scott Gasch
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 #     http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 import sys
18 import traceback
19 import os
20 from threading import Thread
21 import time
22 import gdata_oauth
23 from datetime import datetime
24 import sets
25 import gdata_oauth
26 import gdata.photos, gdata.photos.service
27 import gdata
28 import secrets
29
30 class mirror_picasaweb:
31     """A program to mirror my picasaweb photos and make a local webpage out of it."""
32
33     album_whitelist = sets.ImmutableSet([
34         '1-Wire Project',
35         'Alex 6.0..8.0 years old',
36         'Barn',
37         'Bangkok and Phuket, 2003',
38         'Blue Angels... Seafair',
39         'Dunn Gardens',
40         'East Coast, 2011',
41         'East Coast, 2013',
42         'Friends',
43         'Gasches',
44         'Gasch Wedding',
45         'Hiking and Ohme Gardens',
46         'Hiking',
47         'Kiosk Project',
48         'Krakow 2009',
49         'NJ 2015',
50         'Oahu 2010'
51         'Ocean Shores 2009',
52         'Ohme Gardens',
53         'Olympic Sculpture Park',
54         'Paintings',
55         'Puerto Vallarta',
56         'Photos from posts',
57         'Random',
58         'SFO 2014',
59         'Soccer',
60         'Skiing with Alex',
61         'Tuscany 2008',
62         'Yosemite 2010',
63         'Zoo',
64     ])
65
66     def __init__(self):
67         self.client = oauth.photos_service()
68         self.album = None
69         self.photo = None
70
71     def write_album_header(self, f, album):
72         f.write("""
73 <HEAD>
74 <TITLE>%s</TITLE>
75 <BODY>
76 <H2><A HREF="/scott/photos">Album List</A> | <A HREF="%s">%s</A></H2>
77 """ % (album.title.text,
78        album.link[0].href,
79        album.title.text))
80         if album.location.text is not None:
81             f.write("<B>Location:</B> %s <BR>\n" % album.location.text)
82         if album.summary.text is not None:
83             f.write("<B>Summary:</B> %s <BR>\n" % album.summary.text)
84         if album.updated.text is not None:
85             f.write("<B>Updated:</B> %s <BR>\n" % album.updated.text)
86         f.write("""
87 <HR>
88 <TABLE STYLE="float:left; margin:0 5px 20px 0;" WIDTH=100%%>""")
89
90     def add_photo_to_album(self, f, photo, url, number):
91         descr = photo.summary.text
92         camera = photo.exif.model.text if photo.exif.model else 'unknown'
93         if photo.timestamp:
94             ts = photo.timestamp.datetime().strftime('%d %b %Y')
95         else:
96             ts = 'unknown'
97         if photo.commentCount and photo.commentCount > 0:
98             comments = self.client.GetFeed(
99                 '/data/feed/api/user/default/albumid/%s/photoid/%s?kind=comment' % (
100                     self.album.gphoto_id.text, photo.gphoto_id.text))
101         else:
102             comments = None
103         thumbnail = photo.media.thumbnail[1].url
104
105         if number % 2 == 0:
106             f.write('<TR>')
107         f.write("""
108 <TD ALIGN="center" WIDTH=33%%>
109   <TABLE>
110     <TR>
111     <TD ALIGN="center">
112       <CENTER>
113       <A HREF="%s">
114         <DIV CLASS="img-shadow"><IMG SRC="%s" ALT="%s"></DIV>
115       </A>
116       </CENTER>
117     </TD>
118     </TR>
119     <TR>
120     <TD>
121       <DIV><CENTER>
122         <B STYLE="font-size: 60%%; color: #406b38">%s</B> <BR>
123         <B>Date</B>: %s <BR>
124         <B>Camera</B>: %s <BR>""" % (
125             url,
126             thumbnail,
127             descr,
128             descr,
129             ts,
130             camera))
131         if comments is not None:
132             for comment in comments.entry:
133                 print '%s <BR>' % comment.content.text
134         f.write("""
135       </CENTER></DIV>
136     </TD>
137   </TABLE>
138 </TD>""")
139         if number % 2 == 1:
140             f.write('</TR>')
141
142     def write_album_footer(self, f):
143         f.write("""
144 </TABLE>
145 <CENTER>
146 <FONT style="font-size:60%;color:#406b38">
147   Are you interested in <A HREF="/svn/picasa/trunk">how this page was made</A>?
148 </FONT>
149 </CENTER>
150 </BODY>
151 </HTML>""")
152
153     def write_index_header(self, f):
154         f.write("""
155 <HEAD>
156 <TITLE>Scott's Photo Albums</TITLE>
157 </HEAD>
158 <BODY>
159 <TABLE WIDTH=100%%>
160   <TR>
161     <TD WIDTH=50%%>
162       <H2><A HREF="/scott/photos">Album List</A></H2>
163     </TD>
164     <TD></TD>
165   </TR>""")
166
167     def add_album_to_index(self, f, album, count):
168         thumbnail = album.media.thumbnail[0].url
169         if count % 2 == 0:
170             f.write('<TR>\n')
171         f.write("""
172 <TD>
173   <TABLE>
174     <TR>
175     <TD WIDTH=200>
176       <CENTER>
177       <A HREF="%s.html">
178         <IMG ALIGN=CENTER WIDTH=160 HEIGHT=160 SRC="%s">
179       </A>
180       </CENTER>
181     </TD>
182     <TD>
183       <B>Title</B>: %s <BR>
184       <B>Subtitle</B>: %s <BR>
185       <B>Date</B>: %s <BR>
186       <B>%s</B> photo(s) <BR>
187       <B>%d</B> Mb""" % (
188           album.name.text,
189           thumbnail,
190           album.title.text,
191           album.summary.text,
192           album.published.text,
193           album.numphotos.text,
194           int(album.bytesUsed) / (1024 * 1024)))
195         f.write("""
196     </TD>
197     </TR>
198   </TABLE>
199 </TD>""")
200         if count % 2 == 1:
201             f.write('</TR>\n')
202
203     def write_index_footer(self, f):
204         f.write("""
205 </TABLE>
206 </BODY>""")
207
208     def run(self):
209         attempts = 0
210         while attempts < 3:
211             attempts += 1
212             try:
213                 self.run_internal()
214                 return
215             except (gdata.service.RequestError,
216                     gdata.photos.service.GooglePhotosException):
217                 print "******** TRYING TO REFRESH PHOTOS CLIENT *********"
218                 oauth.refresh_token()
219                 self.client = oauth.photos_service()
220         print "Tried 3x, giving up."
221
222     def run_internal(self):
223         albums = self.client.GetUserFeed().entry
224         album_count = 0
225         index = open('./index.html', 'w')
226         self.write_index_header(index)
227         for album in albums:
228             if album.title.text not in mirror_picasaweb.album_whitelist:
229                 print '--> Skipping "%s" (%s)' % (album.title.text, album.access.text)
230                 continue
231             print '--> Doing "%s" (%s)' % (album.title.text, album.access.text)
232             self.album = album
233             self.add_album_to_index(index, album, album_count)
234             filename = './%s.html' % album.name.text
235             detail = open(filename, 'w')
236             self.write_album_header(detail, album)
237             photos = self.client.GetFeed(
238                 '/data/feed/api/user/%s/albumid/%s?kind=photo&imgmax=1024u' %
239                 (secrets.account, album.gphoto_id.text))
240             photo_count = 0
241             for photo in photos.entry:
242                 resolution = 0
243                 for x in photo.media.content:
244                     if "video" in x.type and int(x.height) > resolution:
245                         url = x.url
246                         resolution = int(x.height)
247                     elif resolution == 0:
248                         url = x.url
249                         resolution = int(x.height)
250                 self.photo = photo
251                 self.add_photo_to_album(detail, photo, url, photo_count)
252                 photo_count += 1
253             self.write_album_footer(detail)
254             detail.close()
255             album_count += 1
256         self.write_index_footer(index)
257         index.close()
258
259 if __name__ == "__main__":
260     oauth = gdata_oauth.OAuth(secrets.client_id,
261                               secrets.client_secret)
262     if not oauth.has_token():
263         user_code = oauth.get_user_code()
264         print '------------------------------------------------------------'
265         print 'Go to %s and enter the code "%s" (no quotes, case-sensitive)' % (
266             oauth.verification_url, user_code)
267         oauth.get_new_token()
268     x = mirror_picasaweb()
269     x.run()