import com.flickr4java.flickr.Flickr; import com.flickr4java.flickr.FlickrException; import com.flickr4java.flickr.REST; import com.flickr4java.flickr.RequestContext; // import com.flickr4java.flickr.Transport; import com.flickr4java.flickr.auth.Auth; import com.flickr4java.flickr.auth.AuthInterface; import com.flickr4java.flickr.auth.Permission; import com.flickr4java.flickr.people.PeopleInterface; import com.flickr4java.flickr.people.User; import com.flickr4java.flickr.photos.Photo; import com.flickr4java.flickr.photos.PhotoList; import com.flickr4java.flickr.photos.PhotosInterface; // import com.flickr4java.flickr.photos.Size; import com.flickr4java.flickr.photosets.Photoset; import com.flickr4java.flickr.photosets.Photosets; import com.flickr4java.flickr.photosets.PhotosetsInterface; // import com.flickr4java.flickr.util.IOUtilities; import com.flickr4java.flickr.prefs.PrefsInterface; // import com.flickr4java.flickr.photos.PhotosInterface; import com.flickr4java.flickr.uploader.UploadMetaData; import com.flickr4java.flickr.uploader.Uploader; import com.flickr4java.flickr.util.AuthStore; import com.flickr4java.flickr.util.FileAuthStore; import org.apache.log4j.Logger; import org.scribe.model.Token; import org.scribe.model.Verifier; import org.xml.sax.SAXException; // import java.io.BufferedInputStream; import java.io.File; import java.io.FilenameFilter; // import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; // import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Properties; // import java.util.Map; import java.util.Scanner; // import java.io.ByteArrayOutputStream; //import java.io.File; // import java.io.FileInputStream; // import java.io.IOException; // import java.io.InputStream; import java.util.Set; // import com.flickr4java.flickr.tags.Tag; /** * A simple program to upload photos to a set. It checks for files already uploaded assuming the title is not changed so that it can be rerun if partial upload * is done. It uses the tag field to store the filename as OrigFileName to be used while downloading if the title has been changed. If setup.properties is not * available, pass the apiKey and secret as arguments to the program. * * This sample also uses the AuthStore interface, so users will only be asked to authorize on the first run. * * Please NOTE that this needs Java 7 to work. Java 7 was released on July 28, 2011 and soon Java 6 may not be supported anymore ( Jul 2014). * * @author Keyur Parikh */ public class UploadPhoto { private static final Logger logger = Logger.getLogger(UploadPhoto.class); private String nsid; private String username; // private final String sharedSecret; private final Flickr flickr; private AuthStore authStore; public boolean flickrDebug = false; private boolean setOrigFilenameTag = true; private boolean replaceSpaces = false; private int privacy = -1; HashMap<String, Photoset> allSetsMap = new HashMap<String, Photoset>(); HashMap<String, ArrayList<String>> setNameToId = new HashMap<String, ArrayList<String>>(); public static final SimpleDateFormat smp = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss a"); public UploadPhoto(String apiKey, String nsid, String sharedSecret, File authsDir, String username) throws FlickrException { flickr = new Flickr(apiKey, sharedSecret, new REST()); this.username = username; this.nsid = nsid; // this.sharedSecret = sharedSecret; if (authsDir != null) { this.authStore = new FileAuthStore(authsDir); } // If one of them is not filled in, find and populate it. if (username == null || username.equals("")) setUserName(); if (nsid == null || nsid.equals("")) setNsid(); } private void setUserName() throws FlickrException { if (nsid != null && !nsid.equals("")) { Auth auth = null; if (authStore != null) { auth = authStore.retrieve(nsid); if (auth != null) { username = auth.getUser().getUsername(); } } // For this to work: REST.java or PeopleInterface needs to change to pass apiKey // as the parameter to the call which is not authenticated. if (auth == null) { // Get nsid using flickr.people.findByUsername PeopleInterface peopleInterf = flickr.getPeopleInterface(); User u = peopleInterf.getInfo(nsid); if (u != null) { username = u.getUsername(); } } } } /** * Check local saved copy first ??. If Auth by username is available, then we will not need to make the API call. * * @throws FlickrException */ private void setNsid() throws FlickrException { if (username != null && !username.equals("")) { Auth auth = null; if (authStore != null) { auth = authStore.retrieve(username); // assuming FileAuthStore is enhanced else need to // keep in user-level files. if (auth != null) { nsid = auth.getUser().getId(); } } if (auth != null) return; Auth[] allAuths = authStore.retrieveAll(); for (int i = 0; i < allAuths.length; i++) { if (username.equals(allAuths[i].getUser().getUsername())) { nsid = allAuths[i].getUser().getId(); return; } } // For this to work: REST.java or PeopleInterface needs to change to pass apiKey // as the parameter to the call which is not authenticated. // Get nsid using flickr.people.findByUsername PeopleInterface peopleInterf = flickr.getPeopleInterface(); User u = peopleInterf.findByUsername(username); if (u != null) { nsid = u.getId(); } } } private void authorize() throws IOException, SAXException, FlickrException { AuthInterface authInterface = flickr.getAuthInterface(); Token accessToken = authInterface.getRequestToken(); // Try with DELETE permission. At least need write permission for upload and add-to-set. String url = authInterface.getAuthorizationUrl(accessToken, Permission.DELETE); System.out.println("Follow this URL to authorise yourself on Flickr"); System.out.println(url); System.out.println("Paste in the token it gives you:"); System.out.print(">>"); Scanner scanner = new Scanner(System.in); String tokenKey = scanner.nextLine(); Token requestToken = authInterface.getAccessToken(accessToken, new Verifier(tokenKey)); Auth auth = authInterface.checkToken(requestToken); RequestContext.getRequestContext().setAuth(auth); this.authStore.store(auth); scanner.close(); System.out.println("Thanks. You probably will not have to do this every time. Auth saved for user: " + auth.getUser().getUsername() + " nsid is: " + auth.getUser().getId()); System.out.println(" AuthToken: " + auth.getToken() + " tokenSecret: " + auth.getTokenSecret()); } /** * If the Authtoken was already created in a separate program but not saved to file. * * @param authToken * @param tokenSecret * @param username * @return * @throws IOException */ private Auth constructAuth(String authToken, String tokenSecret, String username) throws IOException { Auth auth = new Auth(); auth.setToken(authToken); auth.setTokenSecret(tokenSecret); // Prompt to ask what permission is needed: read, update or delete. auth.setPermission(Permission.fromString("delete")); User user = new User(); // Later change the following 3. Either ask user to pass on command line or read // from saved file. user.setId(nsid); user.setUsername((username)); user.setRealName(""); auth.setUser(user); this.authStore.store(auth); return auth; } public void setAuth(String authToken, String username, String tokenSecret) throws IOException, SAXException, FlickrException { RequestContext rc = RequestContext.getRequestContext(); Auth auth = null; if (authToken != null && !authToken.equals("") && tokenSecret != null && !tokenSecret.equals("")) { auth = constructAuth(authToken, tokenSecret, username); rc.setAuth(auth); } else { if (this.authStore != null) { auth = this.authStore.retrieve(this.nsid); if (auth == null) { this.authorize(); } else { rc.setAuth(auth); } } } } public int getPrivacy() throws Exception { PrefsInterface prefi = flickr.getPrefsInterface(); privacy = prefi.getPrivacy(); return (privacy); } private String makeSafeFilename(String input) { byte[] fname = input.getBytes(); byte[] bad = new byte[] { '\\', '/', '"', '*' }; byte replace = '_'; for (int i = 0; i < fname.length; i++) { for (byte element : bad) { if (fname[i] == element) { fname[i] = replace; } } if (replaceSpaces && fname[i] == ' ') fname[i] = '_'; } return new String(fname); } public String uploadfile(String filename, String inpTitle) throws Exception { String photoId; RequestContext rc = RequestContext.getRequestContext(); if (this.authStore != null) { Auth auth = this.authStore.retrieve(this.nsid); if (auth == null) { this.authorize(); } else { rc.setAuth(auth); } } // PhotosetsInterface pi = flickr.getPhotosetsInterface(); // PhotosInterface photoInt = flickr.getPhotosInterface(); // Map<String, Collection> allPhotos = new HashMap<String, Collection>(); /** * 1 : Public 2 : Friends only 3 : Family only 4 : Friends and Family 5 : Private **/ if (privacy == -1) getPrivacy(); UploadMetaData metaData = new UploadMetaData(); if (privacy == 1) metaData.setPublicFlag(true); if (privacy == 2 || privacy == 4) metaData.setFriendFlag(true); if (privacy == 3 || privacy == 4) metaData.setFamilyFlag(true); if (basefilename == null || basefilename.equals("")) basefilename = filename; // "image.jpg"; String title = basefilename; boolean setMimeType = true; // change during testing. Doesn't seem to be supported at this time in flickr. if (setMimeType) { if (basefilename.lastIndexOf('.') > 0) { title = basefilename.substring(0, basefilename.lastIndexOf('.')); String suffix = basefilename.substring(basefilename.lastIndexOf('.') + 1); // Set Mime Type if known. // Later use a mime-type properties file or a hash table of all known photo and video types // allowed by flickr. if (suffix.equalsIgnoreCase("png")) { metaData.setFilemimetype("image/png"); } else if (suffix.equalsIgnoreCase("mpg") || suffix.equalsIgnoreCase("mpeg")) { metaData.setFilemimetype("video/mpeg"); } else if (suffix.equalsIgnoreCase("mov")) { metaData.setFilemimetype("video/quicktime"); } } } logger.debug(" File : " + filename); logger.debug(" basefilename : " + basefilename); if (inpTitle != null && !inpTitle.equals("")) { title = inpTitle; logger.debug(" title : " + inpTitle); metaData.setTitle(title); } // flickr defaults the title field from file name. // UploadMeta is using String not Tag class. // Tags are getting mangled by yahoo stripping off the = , '.' and many other punctuation characters // and converting to lower case: use the raw tag field to find the real value for checking and // for download. if (setOrigFilenameTag) { List<String> tags = new ArrayList<String>(); String tmp = basefilename; basefilename = makeSafeFilename(basefilename); tags.add("OrigFileName='" + basefilename + "'"); metaData.setTags(tags); if (!tmp.equals(basefilename)) { System.out.println(" File : " + basefilename + " contains special characters. stored as " + basefilename + " in tag field"); } } // File imageFile = new File(filename); // InputStream in = null; Uploader uploader = flickr.getUploader(); // ByteArrayOutputStream out = null; try { // in = new FileInputStream(imageFile); // out = new ByteArrayOutputStream(); // int b = -1; /** * while ((b = in.read()) != -1) { out.write((byte) b); } **/ /** * byte[] buf = new byte[1024]; while ((b = in.read(buf)) != -1) { // fos.write(read); out.write(buf, 0, b); } **/ metaData.setFilename(basefilename); // check correct handling of escaped value File f = new File(filename); photoId = uploader.upload(f, metaData); logger.debug(" File : " + filename + " uploaded: photoId = " + photoId); } finally { } return (photoId); } public void getPhotosetsInfo() { PhotosetsInterface pi = flickr.getPhotosetsInterface(); try { int setsPage = 1; while (true) { Photosets photosets = pi.getList(nsid, 500, setsPage, null); Collection<Photoset> setsColl = photosets.getPhotosets(); Iterator<Photoset> setsIter = setsColl.iterator(); while (setsIter.hasNext()) { Photoset set = setsIter.next(); allSetsMap.put(set.getId(), set); // 2 or more sets can in theory have the same name. !!! ArrayList<String> setIdarr = setNameToId.get(set.getTitle()); if (setIdarr == null) { setIdarr = new ArrayList<String>(); setIdarr.add(new String(set.getId())); setNameToId.put(set.getTitle(), setIdarr); } else { setIdarr.add(new String(set.getId())); } } if (setsColl.size() < 500) { break; } setsPage++; } logger.debug(" Sets retrieved: " + allSetsMap.size()); // all_sets_retrieved = true; // Print dups if any. Set<String> keys = setNameToId.keySet(); Iterator<String> iter = keys.iterator(); while (iter.hasNext()) { String name = iter.next(); ArrayList<String> setIdarr = setNameToId.get(name); if (setIdarr != null && setIdarr.size() > 1) { System.out.println("There is more than 1 set with this name : " + setNameToId.get(name)); for (int j = 0; j < setIdarr.size(); j++) { System.out.println(" id: " + setIdarr.get(j)); } } } } catch (FlickrException e) { e.printStackTrace(); } } private String setid = null; private String basefilename = null; private final PhotoList<Photo> photos = new PhotoList<Photo>(); private final HashMap<String, Photo> filePhotos = new HashMap<String, Photo>(); private static void Usage() { System.out.println("Usage: java " + UploadPhoto.class.getName() + " [ -n nsid | -u username ] -s setName { File../Directories}"); System.out.println(" Must pass either -u username or -n nsid "); System.out.println(" Must pass -s followed by set-name(albums) followed by file/directories."); System.out .println("apiKey and shared secret must be available as apiKey and secret via setup.properties or passed as -apiKey key -secret shared-secret"); System.exit(1); } /** * @return the setOrigFilenameTag */ public boolean isSetorigfilenametag() { return setOrigFilenameTag; } /** * @param setOrigFilenameTag * the setOrigFilenameTag to set */ public void setSetorigfilenametag(boolean setOrigFilenameTag) { this.setOrigFilenameTag = setOrigFilenameTag; } public static void main(String[] args) throws Exception { String apiKey = null; // args[0]; String sharedSecret = null; // args[1]; Properties properties = new Properties(); InputStream in = null; try { in = UploadPhoto.class.getResourceAsStream("/setup.properties"); if (in != null) { properties.load(in); apiKey = properties.getProperty("apiKey"); sharedSecret = properties.getProperty("secret"); if (apiKey != null && sharedSecret != null) logger.debug("Found setup.properties in classpath and set apiKey and shared secret"); } } catch (Exception e) { e.printStackTrace(); } finally { if (in != null) in.close(); } if (args.length < 5) { Usage(); System.exit(1); } ArrayList<String> uploadfileArgs = new ArrayList<String>(); ArrayList<String> optionArgs = new ArrayList<String>(); // Flickr.debugRequest = true; // keep it false else entire file will be in stdout. // Flickr.debugStream = true; String authsDirStr = System.getProperty("user.home") + File.separatorChar + ".flickrAuth"; String nsid = null; String username = null; String accessToken = null; // Optional entry. String tokenSecret = null; // Optional entry. String setName = null; boolean settagname = true; // Default to true to add tag while uploading. int i = 0; /*** * for(i = 0; i < args.length; i++) { System.out.println("args[" + i + "] " + args[i]); } **/ for (i = 0; i < args.length; i++) { switch (args[i]) { case "-n": if (i < args.length) nsid = args[++i]; break; case "-u": if (i < args.length) username = args[++i]; break; case "-apiKey": if (i < args.length) apiKey = args[++i]; break; case "-secret": if (i < args.length) sharedSecret = args[++i]; break; case "-notags": if (i < args.length) settagname = false; break; case "-a": if (i < args.length) accessToken = args[++i]; break; case "-t": if (i < args.length) tokenSecret = args[++i]; break; case "-s": if (i < args.length) setName = args[++i]; break; case "-option": if (i < args.length) optionArgs.add(args[++i]); break; default: if (setName != null) uploadfileArgs.add(args[i]); else { Usage(); System.exit(1); } } } if (apiKey == null || sharedSecret == null || (username == null && nsid == null) || (setName == null) || (uploadfileArgs.size() == 0)) { Usage(); System.exit(1); } UploadPhoto bf = new UploadPhoto(apiKey, nsid, sharedSecret, new File(authsDirStr), username); for (i = 0; i < optionArgs.size(); i++) { bf.addOption(optionArgs.get(i)); } bf.setSetorigfilenametag(settagname); bf.setAuth(accessToken, username, tokenSecret); if (!bf.canUpload()) System.exit(1); bf.getPrivacy(); bf.getPhotosetsInfo(); if (setName != null && !setName.equals("")) { bf.getSetPhotos(setName); } // String photoid; for (i = 0; i < uploadfileArgs.size(); i++) { String filename = uploadfileArgs.get(i); File f = new File(filename); if (f.isDirectory()) { String[] filelist = f.list(new UploadFilenameFilter()); logger.debug("Processing directory : " + uploadfileArgs.get(i)); for (int j = 0; j < filelist.length; j++) { bf.processFileArg(uploadfileArgs.get(i) + File.separatorChar + filelist[j], setName); } } else { bf.processFileArg(filename, setName); } } } private static final String[] photoSuffixes = { "jpg", "jpeg", "png", "gif", "bmp", "tif", "tiff" }; private static final String[] videoSuffixes = { "3gp", "3gp", "avi", "mov", "mp4", "mpg", "mpeg", "wmv", "ogg", "ogv", "m2v" }; static class UploadFilenameFilter implements FilenameFilter { // Following suffixes from flickr upload page. An App should have this configurable, // for videos and photos separately. @Override public boolean accept(File dir, String name) { if (isValidSuffix(name)) return true; else return false; } } private static boolean isValidSuffix(String basefilename) { if (basefilename.lastIndexOf('.') <= 0) { return false; } String suffix = basefilename.substring(basefilename.lastIndexOf('.') + 1).toLowerCase(); for (int i = 0; i < photoSuffixes.length; i++) { if (photoSuffixes[i].equals(suffix)) return true; } for (int i = 0; i < videoSuffixes.length; i++) { if (videoSuffixes[i].equals(suffix)) return true; } logger.debug(basefilename + " does not have a valid suffix, skipped."); return false; } private void processFileArg(String filename, String setName) throws Exception { String photoid; if (filename.equals("")) { System.out.println("filename must be entered for uploadfile "); return; } if (filename.lastIndexOf(File.separatorChar) > 0) basefilename = filename.substring(filename.lastIndexOf(File.separatorChar) + 1, filename.length()); else basefilename = filename; boolean fileUploaded = checkIfLoaded(filename); if (!fileUploaded) { if (!isValidSuffix(basefilename)) { System.out.println(" File: " + basefilename + " is not a supported filetype for flickr (invalid suffix)"); return; } File f = new File(filename); if (!f.exists() || !f.canRead()) { System.out.println(" File: " + filename + " cannot be processed, does not exist or is unreadable."); return; } logger.debug("Calling uploadfile for filename : " + filename); logger.info("Upload of " + filename + " started at: " + smp.format(new Date()) + "\n"); photoid = uploadfile(filename, null); // Add to Set. Create set if it does not exist. if (photoid != null) { addPhotoToSet(photoid, setName); } logger.info("Upload of " + filename + " finished at: " + smp.format(new Date()) + "\n"); } else { logger.info(" File: " + filename + " has already been loaded on " + getUploadedTime(filename)); } } private void addOption(String opt) { switch (opt) { case "replaceSpaces": replaceSpaces = true; break; case "notags": setSetorigfilenametag(false); break; default: // Not supported at this time. System.out.println("Option: " + opt + " is not supported at this time"); } } private boolean canUpload() { RequestContext rc = RequestContext.getRequestContext(); Auth auth = null; auth = rc.getAuth(); if (auth == null) { System.out.println(" Cannot upload, there is no authorization information."); return false; } Permission perm = auth.getPermission(); if ((perm.getType() == Permission.WRITE_TYPE) || (perm.getType() == Permission.DELETE_TYPE)) return true; else { System.out.println(" Cannot upload, You need write or delete permission, you have : " + perm.toString()); return false; } } /** * The assumption here is that for a given set only unique file-names will be loaded and the title field can be used. Later change to use the tags field ( * OrigFileName) and strip off the suffix. * * @param filename * @return */ private boolean checkIfLoaded(String filename) { String title; if (basefilename.lastIndexOf('.') > 0) title = basefilename.substring(0, basefilename.lastIndexOf('.')); else return false; if (filePhotos.containsKey(title)) return true; return false; } private String getUploadedTime(String filename) { String title = ""; if (basefilename.lastIndexOf('.') > 0) title = basefilename.substring(0, basefilename.lastIndexOf('.')); if (filePhotos.containsKey(title)) { Photo p = filePhotos.get(title); if (p.getDatePosted() != null) { return (smp.format(p.getDatePosted())); } } return ""; } private void getSetPhotos(String setName) throws FlickrException { // Check if this is an existing set. If it is get all the photo list to avoid reloading already // loaded photos. ArrayList<String> setIdarr; setIdarr = setNameToId.get(setName); if (setIdarr != null) { setid = setIdarr.get(0); PhotosetsInterface pi = flickr.getPhotosetsInterface(); Set<String> extras = new HashSet<String>(); /** * A comma-delimited list of extra information to fetch for each returned record. Currently supported fields are: license, date_upload, date_taken, * owner_name, icon_server, original_format, last_update, geo, tags, machine_tags, o_dims, views, media, path_alias, url_sq, url_t, url_s, url_m, * url_o */ extras.add("date_upload"); extras.add("original_format"); extras.add("media"); // extras.add("url_o"); extras.add("tags"); int setPage = 1; while (true) { PhotoList<Photo> tmpSet = pi.getPhotos(setid, extras, Flickr.PRIVACY_LEVEL_NO_FILTER, 500, setPage); int tmpSetSize = tmpSet.size(); photos.addAll(tmpSet); if (tmpSetSize < 500) { break; } setPage++; } for (int i = 0; i < photos.size(); i++) { filePhotos.put(photos.get(i).getTitle(), photos.get(i)); } if (flickrDebug) { logger.debug("Set title: " + setName + " id: " + setid + " found"); logger.debug(" Photos in Set already loaded: " + photos.size()); } } } public void addPhotoToSet(String photoid, String setName) throws Exception { ArrayList<String> setIdarr; // all_set_maps. PhotosetsInterface psetsInterface = flickr.getPhotosetsInterface(); Photoset set = null; if (setid == null) { // In case it is a new photo-set. setIdarr = setNameToId.get(setName); if (setIdarr == null) { // setIdarr should be null since we checked it getSetPhotos. // Create the new set. // set the setid . String description = ""; set = psetsInterface.create(setName, description, photoid); setid = set.getId(); setIdarr = new ArrayList<String>(); setIdarr.add(new String(setid)); setNameToId.put(setName, setIdarr); allSetsMap.put(set.getId(), set); } } else { set = allSetsMap.get(setid); psetsInterface.addPhoto(setid, photoid); } // Add to photos . // Add Photo to existing set. PhotosInterface photoInt = flickr.getPhotosInterface(); Photo p = photoInt.getPhoto(photoid); if (p != null) { photos.add(p); String title; if (basefilename.lastIndexOf('.') > 0) title = basefilename.substring(0, basefilename.lastIndexOf('.')); else title = p.getTitle(); filePhotos.put(title, p); } } }