// Tool to write Exif Date to Snapchat Data Archive. // It filters files, takes the date in the name of the file and writes it to EXIF Metadata. // // Author : Zuma // Date : 2025-03 package main import ( "io" "errors" "os" "github.com/tdewolff/argp" "path/filepath" "io/fs" "log" "fmt" "strings" "regexp" exif "github.com/barasher/go-exiftool" ) func listFiles(dir string, ext string) []string { var files []string err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { if !d.IsDir() && filepath.Ext(path) == "." + ext { files = append(files, path) } return nil }) if err != nil { log.Fatal(err) } return files } // https://github.com/barasher/go-exiftool/blob/master/exiftool_sample_test.go func copyFile(src, dest string) (err error) { s, err := os.Open(src) if err != nil { return err } defer s.Close() d, err := os.Create(dest) if err != nil { return err } defer d.Close() _, err = io.Copy(d, s) if err != nil { return err } return nil } func copyFileAndSetMetadata(file string, outputFolder string, exif *exif.Exiftool) { newDirectory := filepath.Join(outputFolder, filepath.Base(filepath.Dir(file))) if _, err := os.Stat(newDirectory); errors.Is(err, os.ErrNotExist) { if err := os.Mkdir(newDirectory, 0755); err != nil { log.Fatalf("Cannot create directory : %s", newDirectory) } } date := filepath.Base(file)[:10] date_fields := strings.Split(date,"-") exif_date := fmt.Sprintf("%s:%s:%s 00:00:00\n", date_fields[0], date_fields[1], date_fields[2]) exportFile := filepath.Join(newDirectory, filepath.Base(file)) copyFile(file, exportFile) exifData := exif.ExtractMetadata(exportFile) exifData[0].SetString("DateTime", exif_date) exifData[0].SetString("DateTimeOriginal", exif_date) exifData[0].SetString("DateTimeDigitized", exif_date) exif.WriteMetadata(exifData) } func set_files_metadata(files []string, outputFolder string) { r, _ := regexp.Compile("[0-9]{4}-[0-9]{2}-[0-9]{2}") et, err := exif.NewExiftool() if err != nil { fmt.Printf("Error when intializing: %v\n", err) return } defer et.Close() for _, file := range files { datename := filepath.Base(file)[:10] if(r.MatchString(datename)) { fileInfos := et.ExtractMetadata(file) MIMEType, err := fileInfos[0].GetString("MIMEType") if err != nil { fmt.Printf("%s\nError when reading MIMEType : %v\n", file, err) continue } if MIMEType == "video/mp4" { HandlerType, err := fileInfos[0].GetString("HandlerType") if err != nil { fmt.Printf("%s\nError when reading HandlerType : %v\n", file, err) continue } if HandlerType == "Audio Track" { continue } copyFileAndSetMetadata(file, outputFolder, et) } else if MIMEType == "image/jpeg" { filename := filepath.Base(file) // Skip thumbnails if len(filename) == 116 || len(filename) == 66 { continue } copyFileAndSetMetadata(file, outputFolder, et) } else { fmt.Printf("Unknown MIMEType : %v\n", MIMEType) } } } } func main() { var inputFolder string var outputFolder string cmd := argp.New("write_metadata, un outil pour mettre la date dans les données EXIF d'une photo") cmd.AddArg(&inputFolder, "inputFolder", "Dossier à scanner") cmd.AddArg(&outputFolder, "outputFolder", "Dossier ou exporter les fichiers édités") cmd.Parse() if(inputFolder == "") { log.Fatal("ERROR : You must specify a folder to scan") } if(outputFolder == "") { log.Fatal("ERROR : You must specify a folder to export output") } jpeg_files := listFiles(inputFolder, "jpeg") jpg_files := listFiles(inputFolder, "jpg") mp4_files := listFiles(inputFolder, "mp4") var all_files []string all_files = append(all_files, jpeg_files[:]...) all_files = append(all_files, jpg_files[:]...) all_files = append(all_files, mp4_files[:]...) fmt.Println(fmt.Sprintf("%d files found.",len(all_files))) set_files_metadata(all_files, outputFolder) }