Skip to content

Instantly share code, notes, and snippets.

@Dynom
Created January 25, 2020 19:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Dynom/dfe491a20b9b6d8f48dd2b1b25c147a0 to your computer and use it in GitHub Desktop.
Save Dynom/dfe491a20b9b6d8f48dd2b1b25c147a0 to your computer and use it in GitHub Desktop.
Flickr data recovery script
package main
import (
"encoding/json"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"time"
)
/*
1. Visit https://www.flickr.com/account/
2. Request your data, when the are available, download them to the same directory on a local drive
3. Extract all zip files to the same directory (both photo and JSON files)
4. Run this script to:
- Rename files back to their original name
- Set the correct file modified times
*/
func main() {
var dir = "."
var loc = time.UTC
var dryRun = false
logger := log.New(os.Stdout, "", log.LstdFlags)
files, err := ioutil.ReadDir(dir)
if err != nil {
panic(err)
}
var jsonFiles = make([]string, 0, 128)
var photoFiles = make(map[string]string, 128)
for _, file := range files {
if file.IsDir() {
continue
}
if strings.HasPrefix(file.Name(), "photo_") && strings.HasSuffix(file.Name(), ".json") {
jsonFiles = append(jsonFiles, file.Name())
continue
}
// If it's a file, matching Flickr's expected structure
if id, ok := getIDFromFileName(file.Name()); ok {
if _, ok := photoFiles[id]; ok {
logger.Printf("Muiltiple ID's are possible! %q", file.Name())
}
photoFiles[id] = file.Name()
continue
}
}
for _, fileName := range jsonFiles {
contents, err := ioutil.ReadFile(filepath.Join(dir, fileName))
if err != nil {
panic(err)
}
var details FlickrDEPhoto
if err := json.Unmarshal(contents, &details); err != nil {
panic(err)
}
photoFileName, ok := photoFiles[details.ID]
if !ok {
logger.Printf("Have a match in file %q, but didn't find a photo with id %q", fileName, details.ID)
continue
}
var ext string
if ext = filepath.Ext(photoFileName); ext == "" || ext == "." || ext[0] != '.' {
logger.Printf("Missing extension for file %q", photoFileName)
continue
}
logger.Printf("Processing %q", photoFileName)
source := filepath.Join(dir, photoFileName)
destination := filepath.Join(dir, details.Name+ext)
logger.Printf("==> Renaming to: %q", destination)
if !dryRun {
if err := os.Rename(source, destination); err != nil {
logger.Printf("==> Failed to rename %q to %q, %s", source, destination, err)
}
}
originalCTime, err := time.ParseInLocation("2006-01-02 15:04:05", details.DateTaken, loc)
logger.Printf("==> Setting time to: %q", originalCTime)
if !dryRun {
err = os.Chtimes(destination, originalCTime, originalCTime)
if err != nil {
logger.Printf("==> Failed to set time %q on file %q, %s", originalCTime, destination, err)
}
}
}
}
func getIDFromFileName(fileName string) (string, bool) {
endIndex := strings.LastIndex(fileName, ".")
if endIndex <= 0 {
return "", false
}
// images typically have the "_o.jpg" suffix
if strings.HasSuffix(fileName[:endIndex], "_o") {
endIndex = endIndex - 2
}
// The identifier is prefixed with the "_" separator
startIndex := strings.LastIndex(fileName[:endIndex], "_")
if startIndex < 0 {
return "", false
}
for _, c := range fileName[startIndex+1 : endIndex] {
if '0' <= c && c <= '9' {
continue
}
return "", false
}
return fileName[startIndex+1 : endIndex], true
}
type FlickrDEPhoto struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
DateTaken string `json:"date_taken"`
Rotation int `json:"rotation"`
Original string `json:"original"`
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment