// New creates and returns a new *Spooler. func New(dbPath, destination, errorPath string, noop bool) (*Spooler, error) { sp := new(Spooler) sp.noop = noop if !file.Exists(destination) { if _, err := os.Create(destination); err != nil { return nil, err } } sp.Destination = destination if !file.Exists(errorPath) { if _, err := os.Create(errorPath); err != nil { return nil, err } } sp.ErrorPath = errorPath if sp.noop { sp.database = db.NewNoopDb() } else { database, err := db.NewMapFileDb(dbPath) if err != nil { return nil, err } sp.database = database } sp.closed = false return sp, nil }
// load deserializes the md5 json database from the given filePath. func load(path string) (map[string][]string, error) { var db map[string][]string if file.Exists(path) { json_bytes, err := ioutil.ReadFile(path) if err != nil { return db, err } err = json.Unmarshal(json_bytes, &db) if err != nil { return db, err } } else { db = make(map[string][]string) } return db, nil }
// Spool copies the photo given by filename to the correct directory with the correct name. func (sp *Spooler) Spool(filename string) error { if sp.closed { return fmt.Errorf("This Spooler is already closed.") } // calculate an md5 sum for the file hash := getHash(filename) // get the Time from the DateTimeOriginal exif tag dateTime, err := getDateTime(filename) if err != nil { log.Printf("Could not read the DateTimeOriginal tag. %v", err) log.Printf("Moving %s to %s.\n", filename, sp.ErrorPath) if sp.noop { log.Println("DRY RUN Skipping move file.") } else if mverr := file.MoveTo(sp.ErrorPath, filename); mverr != nil { log.Fatal(mverr) } return err } // check if the hash already exists in the db if sp.database.ContainsKey(hash) { msg := "A db entry already exists for " + filename + "." errorName := filepath.Join(sp.ErrorPath, strings.Join([]string{filepath.Base(filename), "DUPLICATE"}, ".")) log.Printf("Mv(%s, %s)\n", errorName, filename) if sp.noop { log.Println("DRY RUN Skipping move file.") } else { err := file.Mv(errorName, filename) if err != nil { return err } } return errors.New(msg) } // calculate the path to copy the file to, including a new file name newPath := sp.getDestination(filename, sp.Destination, dateTime) // ensure the new path doesn't already exist for file.Exists(newPath) { dateTime = dateTime.Add(1 * time.Second) newPath = sp.getDestination(filename, sp.Destination, dateTime) } log.Printf("Mv(%s, %s)\n", newPath, filename) if file.Exists(newPath) { fields := strings.Split(filepath.Base(filename), ".") errorName := filepath.Join(sp.ErrorPath, strings.Join([]string{fields[0], filepath.Base(newPath)}, "::")) if sp.noop { log.Println("DRY RUN Skipping move file.") } else { err := file.Mv(errorName, filename) if err != nil { log.Println(err) return err } } msg := "A file with that named " + newPath + " already exists at the destination. Moving to " + errorName log.Println(msg) // TODO This logging sucks. // TODO send an e-mail. return errors.New(msg) } // move the file to its new home if sp.noop { log.Println("DRY RUN Skipping move file.") } else if err := file.Mv(newPath, filename); err != nil { log.Println(err) return err } // add an entry to the hashmap db if err := sp.database.Insert(newPath, hash); err != nil { return err // TODO plain errors suck... } return nil }