Flickr

  • Devastating critique of the damage Yahoo! has done to Web culture. "All I can say, looking back, is that when history takes a look at the lives of Jerry Yang and David Filo, this is what it will probably say: 'Two graduate students, intrigued by a growing wealth of material on the Internet, built a huge fucking lobster trap, absorbed as much of human history and creativity as they could, and destroyed all of it.' Great work, guys."
  • Interesting thinking about the current state of weblogs. Will all blog-like activity be consumed by Facebook, or will new tools emerge to help with privacy? And how do private blogs mix with public tools like Newsreaders? Complicated questions to answer.
  • Nelson has a good roundup of the issues surrounding the Wikileaks story.
  • "Whatever restrictions we eventually end up enacting, we need to keep Wikileaks alive today, while we work through the process democracies always go through to react to change. If it’s OK for a democracy to just decide to run someone off the internet for doing something they wouldn’t prosecute a newspaper for doing, the idea of an internet that further democratizes the public sphere will have taken a mortal blow."
  • "If you host your content on a commercial provider or on a social network, there are different points at which you can be cut off." The Wikileaks case is pointing out a weakness in the completely libertarian web ideal.
  • The case for Instagram. I must be a photography snob. I can not see the appeal of a community based solely on heavily-filtered photos. 
  • Nice sanity check in the mobile Web App vs. Native App debate. Often a Web App will do.
  • "A naval officer told the present writer that he had often, when on deck, been both amused and surprised at the accuracy with which some of these girls used this form of signalling out of pure fun." People have always found ways to communicate over distances.
  • Bloglines is shutting down and people are leaving Google Reader in droves. [via pkedrosky on Twitter via my social netw...OH, I see what's happening.]
  • Turn any Flickr RSS feed into The Big Picture by resizing the photos from Small to Large. I have my issues with Yahoo Pipes and their agressive scraping, but I have to admit this does make viewing Flickr photos with Google Reader fun.
  • "I do wonder, however, whether my son will someday feel that his privacy is being violated, or more likely, be embarrassed about the site." I struggle with this issue too and it's why I don't post very much in public about my son. [via Daddy Types]
  • "Google is to be forced to release the records of every video watched on YouTube, including user names and web addresses, to entertainment company Viacom after a US federal court ruling." Copyright trumps privacy. [via MeFi]
  • "Perhaps the right answer is to excise the links from the old posts and to add a note explaining why the links were removed." Rafe brings up a good reason why you might need to alter archives and how you might handle it in a way that doesn't break links.
  • welcome, Ollie!
    filed under: life
  • Good start at an iPhone-friendly interface for Flickr. Needs links to larger photos, though.
    filed under: flickr, iphone, photography, mobile
  • Access your Mac desktop remotely with your iPhone. wow!
    filed under: mac, mobile, software, iphone

OTFG Housekeeping

This afternoon I threw all of the code I've written for my photoblog into one directory: onfocus photos code. I realized all of the SQL stuff is spread across several text files, the code is scattered across a dozen posts, and I just need a place to dump stuff as I update the site. I'm going to try to keep this directory updated as I go. I should probably use subversion, and offer it all as a .zip file, but it's really not at a public consumption stage yet.

I spruced up the standard Apache indexes with Silk Icons by Mark James. Thanks!

OTFG: Syndication, Exif Data, and Tags

Now that I'm off the photo-hosting grid and have my local photoblog up and running, I've been working on some of the extra features. This week I set up an RSS feed and JavaScript syndication, grabbed Exif data from my photos, and set up a way to browse photos by tag. I won't go into great detail about my thinking on these—I'll just share my code and describe how I set up each one briefly.
Syndication
The RSS feed was fairly easy to put together. Here's the code that generates the feed: rss.php. I added a few extra constants to the ini.inc file that's included on every page because they were needed in the feed: APP_TITLE, APP_DESCRIPTION, BASE_URL, PHOTO_URL, and PHOTOGRAPHER_NAME. I also went back through each file in the application and substituted these constants where I'd hard-coded the values. (This will make it easier to change these variables globally in the future, and it makes the code more reusable.) And I went with full-sized images in the feed. Here's the live feed if you'd like to check it out.

One of Flickr's nice features that I was using before my move was their JavaScript badges. With a few lines of CSS and a single line of JavaScript, you can include a strip of photos on any website. I was using a vertical strip of five photos (check "now seeing" in the right sidebar on the front page of this site for an example). I decided to throw this together for my local photoblog, and here's the file that generates the JavaScript that generates the strip: js.php. Nothing too tricky going on here, just a document.write of some HTML that displays the smaller-sized thumbnails of recent photos.

I store both rss.php and js.php outside of my public web directory. I run them every hour, piping the output to a couple of files in the public web directory. To accomplish this with Windows Task Scheduler, I have a simple .bat file that looks like this:

C:\path\to\php\php-win.exe C:\scripts\js.php > C:\web\site\include.js
C:\path\to\php\php-win.exe C:\scripts\rss.php > C:\web\site\rss.xml


So I just run this batch file every hour and that way the feed and JavaScript file are cached and won't be re-built each time someone requests it.

I set up an .htaccess entry for the feed so it has a nicer URL: RewriteRule ^feed/?$ rss.xml [L]. I'm the only one consuming the JavaScript, so that URL can be as ugly as it wants to be.
Exif Data
Most (if not all) digital cameras embed some information about the state of the camera within the photo files themselves using a format called EXIF. Flickr made great use of this data with a special page that displays quite a bit of the Exif data available in uploaded photos. (For example, More detail about bandon beach.) The Exif data gives you a quick look at your shutter speed, aperture, focal length, and a few other settings. I think it helps to look at Exif data frequently so I can remind myself which settings worked or didn't work for any particular type of photo.

Because the Exif data is embedded directly in the original photographs, I could just grab this info on-the-fly to display it on the site. (PHP 5 has a few Exif functions that make snagging this data fairly easy.) But I'm guessing down the road I'd like to do more with this data, such as grouping all photos by a particular camera together, or grouping all photos taken with my 50mm lens together. More control means storing everything in the database, and I put together a table called exif and a generic function to grab/store the data.

Here's the SQL to build the table: otfg_tables_6.txt, and here's the include file with the exif-grabbing function: addExif.inc. The function addExif() accepts a PhotoID and database connection, finds the original file for that photo, and extracts and saves the Exif data. I went back and added this function to the uploading files (Step 10: Adding Photos), and whipped up a quick script to add Exif data for existing photos: setExif.php.

After running this script I had a table full of Exif data, and I found out that 490 of my 840 photos had Exif data. Unfortunately my cell phone strips Exif data from photos before it sends them via email, which means my cell phone pictures (a large percentage) don't have any Exif data to extract. One improvement to this process that I need to write soon is setting the DateTaken value in the photos table based on the Exif DateTimeOriginal value. That way I can display both the time the photo was posted, and the time the photo was actually taken.

I'm displaying the Exif data on the photo detail page, as a single line of text. For example, the photo bandon beach shows the following Exif line below the photo:

otfg exif

This Exif line lists the camera make and model, the shutter speed, the aperture, and the focal length. I'll probably add ISO and exposure bias eventually because I like to see that data too, but I thought these were the basics.
Tags
I've also added some features around tagging. Each tag listed on the photo detail page is now linked to a tag page where viewers can see all photos with that particular tag. One example is the tag page for architecture—a tag I've used frequently. Here's the code for this page: tag.php. As viewers click photos from the tag page, they're able to stay within that tag context. So all of the back/next controls on the photo detail page reflect the viewer's most recent choice. The photo detail URL is also under the /tag directory to reflect the different context. (I've set up my robots.txt to ask robots to ignore the /tag directory so there aren't multiple locations for photo detail pages.)

Today I put together a standard tag cloud so I can visualize how I'm using tags. As usual, the larger the tag, the more frequently it's used, with red tags the most-used. Here's the code for this: tags.php.

All of this tagwork required some significant changes to photo.php and editing.js. Both of these updated scripts are getting more complex by the hour. (Beyond the tag stuff, I also improved some other JavaScript editing functions so simple HTML won't wreak havoc in captions.) I also added a few more entries to the .htaccess file to handle this new tag-space:

RewriteRule ^tag/(.*?)/(\d{1,2})/$ tag.php?tag=$1&page=$2 [L]
RewriteRule ^tag/(.*?)/(\d{4}/\d{2}/.*)$ photo.php?p=$2&tag=$1 [L]
RewriteRule ^tag/(.*?)/$ tag.php?tag=$1 [L]


This just cleans up the URLs for tag pages, and paging through photos there. As you can see, photo stubs under /tag/ are just routed back to photo.php along with the tag itself. (This is also some good foreshadowing for how I'll probably handle galleries since these are basically just tag-galleries.)

All in all, going off the grid is going well. I still have a long to-do list, but I feel like I crossed some of the big features and fixes off my list this week.

OTFG Step 11: Displaying and Editing Photos

Good morning, off the grid fans! I know it's been a while—I've been working on my new photoblog behind the scenes. I'm at a point where I'm not sure it helps anyone to share my code, but who knows? Last time I talked about how I'm getting photos into my system, and today I'll explain how I'm editing photos that are already in the system.
Photo URLs
The first thing I needed to do was provide a permanent home for every photo that finds its way into my database. The easiest method would be using the internal photo ID in the URL somehow, so I'd end up with a URL for any photo like http://photos.onfocus.com/459918/. But as I mentioned when designing the photo file locations (Thinking about Photo URLs), I want to include a bit more information about the photo in the URL. So I went with a pattern similar to the files: http://photos.onfocus.com/[year]/[month]/[title]. As with files, the title is stripped of any non-URL-approved characters, and whitespace is replaced with a dash.

I also wanted to keep photo IDs internal, and not use them anywhere within the public-facing application. So I thought the URL pattern of [year]/[month]/[title] would be a good way to uniquely identify a photo within the system as long as there were no duplicates. To make this happen I set up a field in the database called Stub (varchar(50)) that holds the entire string: [year]/[month]/[title]. If two photos have the same title within the same year and month, I simply increment the stub like this: [year]/[month]/[title]-1, [year]/[month]/[title]-2, etc. This way the permalink can be used not only to see the photo on the Web, but also to identify the photo within the application.

I wrote a quick script to add URL stubs to all of the existing photos: addStubs.php. And I retrofitted all of the uploading scripts from the last step to write a URL stub as a photo is added.

With the virtual space for photos set to go, I needed to give them a permanent home. I set up a script called photo.php that shows one photo at a time, along with a bunch of details about that photo. By passing a URL stub into the script like this http://photos.onfocus.com/photo.php?p=[photo stub], the page knows which photo to display. With a little .htaccess magic: RewriteRule ^(\d{4}/\d{2}/.*)$ photo.php?p=$1 [L] the nicely formatted URLs are a reality: for example, bandon beach.
Photo Detail Page
So the photo detail page accepts an incoming URL stub, looks up info about that photo in the database, and arranges things nicely on the page. Here's what a photo detail page looks like in my system today:

otfg photo detail

The main bits are the title, photo itself, caption, time the photo was posted, and a list of tags associated with the photo. This is the public view. If you have the right credentials (set up in Step 9: Authentication), you see a bit more on the page, and you have a few more options. As an administrator, directly below the caption is a row of administrative buttons:

otfg admin buttons

Here's what each button does:
  • Sets the public/private status of the photo.
  • Toggles the caption editing form.
  • Rotates the photo 90 degrees clockwise.
  • Completely removes the photo, its thumbnails, and all associated info.
The other thing an admin can do is edit the title, caption, and tags in place by clicking on any of these things (like Flickr). It looks like this:

otfg editing title

All of this editing is accomplished with a series of files. The Ajax package Prototype handles some of the work in the background. And the rest of the interface stuff is in a Javascript file that's included on the page for an administrator: editing.js. The functions in this file post to several PHP scripts that return simple text information: The only function that requires a page refresh is completely deleting a photo, which is ok with me because the photo detail page is about to be history anyway and I've got to go somewhere. The administrative buttons all have a JavaScript confirmation dialog before they execute, so I can't accidentally delete a photo when I want to rotate it.

And here's the beast of a script that pulls everything together: photo.php. This is fairly complex, especially because the public design and administrative functions live together in the same page.
Photo Home Page
With the photo detail page set, I set up a place to introduce people to my photos: the home page. Again, it's very Flickrish, with two columns of photos and a list of pages at the bottom. The only difference is that the latest photo is at its largest size at the top. I haven't built any editing into this page yet, so any updates have to happen on photo detail pages. And here's the code that powers the front page: home.php. I set up an .htaccess rule for this page too, to help with paging: ^home/(\d{1,2})/?$ home.php?page=$1 [L]. That way, as you page through the photos, you'll get friendly URLs like this: http://photos.onfocus.com/home/2.

I think this step gives me a functioning system I can use to publish photos. There are definitely more features I need to build: browsing by tag, extracting EXIF data, an RSS feed, galleries/sets, commenting, and mapping photos with coordinates. And I have some issues I need to work out. (Anyone know how to force a cache refresh on photos after rotation beyond adding some random numbers to the file name?) But I think all of the bare essentials for sharing photos with the world are working now. woohoo! You can compare my local photostream to my Flickr photostream to see how similar they are.

By the way, the little administrative and tag icons are all modified from a set of tiny icons by Timothy Groves: Ho!Ho!Ho!. Thanks!

OTFG Step 10: Adding Photos

I'm still working on my project to host my own photos that I'm calling going off the grid, and I'm sharing the code I write as I go. I was hoping to be done by the end of February, but I think I'm still a few steps away from a functioning system. Last time I set up a method for authenticating myself, and the next step was figuring out how to get new photos into the filesystem and database.

Flickr has myriad ways to get photos into their system. They have a bunch of client software that can upload photos in batches, in addition to a standard web form and uploading via email. I've played around with all of these, but I only used two regularly: the web form and uploading via email. So that's what I put together for my local system.
Web Upload
For the forms, I basically duplicated Flickr's 3-step upload process. I choose the files, upload the photos, then title and tag them. The big difference here is there aren't any privacy controls. I'm assuming that every photo I add into the system will be public, so I'm not concerned about setting privacy when I upload. (That's always something I can add later.) I reused code from Step 8 to write thumbnails for the new photos, and just needed to write a few scripts to handle uploading and updating the database. Here's the set of files I'm using to upload photos:
  • upload.php - sets the basic uploading form.
  • upload-action.php - uploads the files, writes thumbnails, and then writes a form for each photo for adding title, description, and tags.
  • upload-final.php - updates the database with the new titles, descriptions, and tags, and sets the photos as public.
And outside of my public web directory, I have a couple of files with some helper functions that are included:
  • addPhoto.inc - adds an incoming photo to the database and returns its photoID.
  • writeThumbs.inc - writes all of the standard thumbnail sizes (and resizes the original, if necessary) for a given photoID.
Each of the public files are only going to be used by me, so there's an identity check at the top of each script. If the current user isn't logged in as an admin, the script boots the user to the home page. That's not too friendly, but I'll know what's happening immediately since this is my system.

Another difference I should point out is that I have to go through all steps to publish the photos. If I upload three photos, but don't add tags and titles in Step 2, the photos won't be public. In Flickr web uploading, you can skip the form for adding titles, tags, and descriptions and the photos will still be live. I decided to make that last step mandatory, even if it's just hitting the submit button again. I think forcing myself to think about titles helps my publishing process.
Email Upload
Sending photos by email is crucial for me because I like publishing cell-phone pictures while I'm out and about. When I set up my first moblog several years ago, I whipped up a filter for XMail (my mail server) to process attachments from any incoming message to a specific address. This time around I wanted something more generic, so I settled on a script that checks a specific email address via POP every 15 minutes, and handles any new messages with attachments.

And here's the script: check-mail.php. I keep it outside the public web directory, and run it every 15 minutes with Windows Task Scheduler. (You could use cron on *nix systems.)

If you're going to try this out, you'll need to add your own mail server details to the top of the script. The MAIL_TAGS constant is set in ini.inc, and is simply a set of tags to use for any photo that comes in via email. (a la Flickr.) I use mopho and cameraphone for any photo that comes in this route. Same with TEMP_DIR, this should be set to a full path to a directory for temporary files.

I think it's important to use a brand new email address that's hard to guess, and is only used for this purpose. And you shouldn't ever share the address. The address is almost like a password, so I treat mine accordingly. And if you can use an email address at a private domain (instead of gmail, hotmail, yahoo, etc.) I think that would be better. You don't want random spammers to be able to post pictures of pills or casinos to your photoblog. (In fact, I think I'll go back and add a sender whitelist to this script for my own piece of mind.) This script is set up for a POP account on standard ports, so you might need to check the PHP IMAP documentation for different setups. I've also only tested this with my phone (a Sony Ericsson S710a) and other phones might attach photos in a different way.

Now that photos can find their way into the system, I need a way to edit photo details. That's up next.
« Older posts  /  Newer posts »