//This function is going to be the core of my scanner. func (v *Mp4Video) Inspect(f io.ReadSeeker) (item *core.Item, err error) { item = &core.Item{ Type: core.ItemTypeVideo, } //get the mvhd atom offset mvhd, err := getMp4MvhdAtom(f) if err != nil { return nil, fmt.Errorf("cannot find mvhd: %s", err) } //see if we can find the creation time created, err := getMp4CreationTimeFromMvhdAtom(f) if err != nil { return nil, fmt.Errorf("cannot get creation time: %s", err) //no creation, no dice } item.Created = core.AdjustTime(created) //return to mvhd to get duration f.Seek(mvhd, 0) duration, err := getMp4DurationFromMvhdAtom(f) if err == nil { v.Duration = duration } //getting size means finding the video track detail atom x, y, r, err := getMp4Dimensions(f) if err == nil { v.Width = int(x) v.Height = int(y) v.Orientation = r } latlon, err := getMp4Location(f) if err == nil { item.Location = latlon } //get size by seeking to end. v.Size, _ = f.Seek(0, 2) b, err := json.Marshal(v) if err != nil { return nil, fmt.Errorf("json marshal error: %s", err) } //set the metadata item.Meta = json.RawMessage(b) return }
//This function is going to be the core of my scanner. func (p *JpegPhoto) Inspect(f io.ReadSeeker) (item *core.Item, err error) { item = &core.Item{ Type: core.ItemTypePhoto, Location: &core.LatLon{}, } p.Width = 0 p.Height = 0 p.Orientation = OrientedNormal p.Device = "" cfg, err := jpeg.DecodeConfig(f) if err != nil { return nil, err } p.Width = cfg.Width p.Height = cfg.Height f.Seek(0, 0) //rewind... //get date from exif exifInfo, err := exif.Decode(f) if err != nil { return nil, err //no exif, no photo } var createds []time.Time for _, field := range possibleExifDateTimeFields { tag, err := exifInfo.Get(field) if err != nil { //log.Println("no tag", field) continue } tag_val, err := tag.StringVal() if err != nil { log.Println("wrong type", field) continue } if tag_created, err := time.Parse(ExifDateFormat, tag_val); err == nil { if tag_created == exifDateBug { log.Println("EXIF DATE BUG:", field) continue } createds = append(createds, tag_created) } } if len(createds) == 0 || createds[0].IsZero() { return nil, fmt.Errorf("Could not get date photo taken") } item.Created = core.AdjustTime(createds[0]) //now optional, orientation tag, err := exifInfo.Get(exif.Orientation) if err == nil { o, _ := tag.Int(0) p.Orientation = ExifOrientation(o) //swap height/width if needed. switch p.Orientation { case OrientedMirror270, OrientedNormal270, OrientedNormal90, OrientedMirror90: p.Width, p.Height = p.Height, p.Width } } //Device/Make tag, err = exifInfo.Get(exif.Make) if err == nil && tag.Format() == tiff.StringVal { p.Device, _ = tag.StringVal() } tag, err = exifInfo.Get(exif.Model) if err == nil && tag.Format() == tiff.StringVal { if p.Device == "" { p.Device, _ = tag.StringVal() } else { tag_val, _ := tag.StringVal() p.Device = p.Device + " " + tag_val } } //and GPS location setLocationFromExif(item.Location, exifInfo) //get size of file by seeking to the end. p.Size, _ = f.Seek(0, 2) b, err := json.Marshal(p) if err != nil { return nil, err } //now add meta to item. item.Meta = json.RawMessage(b) return }
func main() { flag.Parse() if *version { fmt.Printf("OPFS %s\n", core.VERSION) return } if *usage { flag.Usage() return } if *dumpConfig { if err := core.WriteDefaultConfigTo(os.Stdout); err != nil { log.Fatal("Dump Config Error:", err) } os.Exit(0) } //make sure we use all available cores runtime.GOMAXPROCS(runtime.NumCPU()) conf, err := os.Open(core.ExpandHome(*config)) configuration := &core.Config{} if err != nil { if os.IsNotExist(err) && *config == defaultConfigLocation { //this ok, probably first run. write config file. if err = core.WriteDefaultConfig(core.ExpandHome(*config)); err != nil { log.Fatal("error creating default config file:", err) } configuration = core.DefaultConfig } else { log.Fatal("Error opening config file:", err) } } else { if err = json.NewDecoder(conf).Decode(configuration); err != nil { log.Fatal("Error reading Config File:", err) } conf.Close() } //now instatiate from config. service, err := core.NewService(configuration) if err != nil { log.Fatal("Error initialising service:", err) } if *importDir != "" { //single import job //work out tags var tags []string if *importTags != "" { tags = strings.Split(*importTags, ",") } log.Println(core.AllMimeTypes()) opts := &core.ImportOptions{ Dir: *importDir, Tags: tags, Time: core.AdjustTime(time.Now()), MimeTypes: core.AllMimeTypes(), DeleteAfterImport: *importDeleteAfter, } errors := service.Import(opts) for err := range errors { log.Println("ImportError:", err) } return } if *storeReindex { errors := service.ReindexFromStore() for err := range errors { log.Println("ImportError:", err) } return } if *daemon { log.Printf("OPFS: Starting API on http://%s/\n", configuration.Api.Listen) var wg sync.WaitGroup wg.Add(1) errs, fini := service.WatchImport(&core.ImportOptions{ MimeTypes: core.AllMimeTypes(), DeleteAfterImport: true, //DANGER DANGER! }) go func() { log.Println("API Fail:", service.ApiListen()) wg.Done() }() go func() { for { select { case err := <-errs: log.Println("Import Watch Error:", err) case <-fini: break } } }() wg.Wait() //now shutdown close(fini) os.Exit(0) } //no option given... flag.Usage() }