package main import ( "fmt" "forever-files/db" "forever-files/partitioner" "forever-files/types" "github.com/dustin/go-humanize" "github.com/urfave/cli/v2" "math" ) func plan() *cli.Command { return &cli.Command{ Name: "plan", Aliases: []string{"p"}, Usage: "Reads the database and plans the partitions for the backup, stores the partitions in the database", Flags: []cli.Flag{ &cli.StringFlag{ Name: "targetSize", Usage: "The target size for each partition, valid options are DVD-SL, DVD-DL, BD-SL, BD-DL, BD-TL, BD-QL\nor you can provide any specific size e.g. 4.6GB, 8.1GB, 25GB, 50GB, 100GB, 128GB or 5MB!", Aliases: []string{"s"}, Value: "DVD-SL", }, &cli.BoolFlag{ Name: "verbose", Usage: "Print the files in each partition", Aliases: []string{"v"}, Value: false, }, &cli.BoolFlag{ Name: "left-pack", Usage: "Use the left-pack algorithm to calculate partitions", Aliases: []string{"l"}, Value: false, }, &cli.BoolFlag{ Name: "reset", Usage: "Reset the database before planning partitions", Aliases: []string{"r"}, Value: false, }, }, Action: func(c *cli.Context) error { store, err := db.NewDB(types.AppName) if err != nil { panic(fmt.Errorf("error creating db: %w", err)) } defer store.Close() err = store.Migrate() if err != nil { panic(fmt.Errorf("error migrating db: %w", err)) } if c.Bool("reset") { err = store.RemovePartitionAssignment() if err != nil { return fmt.Errorf("error resetting partitions in the db: %w", err) } } size := int64(4600000000) // size of a single layer DVD targetSize := c.String("targetSize") switch targetSize { case "DVD-SL": size = 4600000000 case "DVD-DL": size = 8100000000 case "BD-SL": size = 25000000000 case "BD-DL": size = 50000000000 case "BD-TL": size = 100000000000 case "BD-QL": size = 128000000000 default: // try to parse the size from human-readable format usize, err := humanize.ParseBytes(targetSize) if err != nil { fmt.Printf("invalid target size: %v\n", err) fmt.Println("valid options are DVD-SL, DVD-DL, BD-SL, BD-DL, BD-TL, BD-QL") fmt.Println("or you can provide any specific size e.g. 4.6GB, 8.1GB, 25GB, 50GB, 100GB, 128GB or 5MB!") size = 4600000000 } if usize > math.MaxInt64 { fmt.Println("size is too large") size = 4600000000 } size = int64(usize) } fmt.Printf("Target Size: %v\n", humanize.Bytes(uint64(size))) fmt.Println("Calculating partitions...") var partitions [][]types.FileMetadata if !c.Bool("left-pack") { partitions, err = partitioner.CalculatePartitions(store, size) if err != nil { return fmt.Errorf("error calculating partitions: %w", err) } } if c.Bool("left-pack") { partitions, err = partitioner.CalculatePartitionsLeftPack(store, size) if err != nil { return fmt.Errorf("error calculating partitions: %w", err) } } for i, partition := range partitions { partSize := int64(0) if c.Bool("verbose") { fmt.Printf("Partition %v:\n", i) } for _, file := range partition { if c.Bool("verbose") { fmt.Printf("%v/%v %v\n", file.Path, file.Name, humanize.Bytes(uint64(file.Size))) } partSize += file.Size // save the planned partitions file.PartitionId = fmt.Sprintf("%d", i) err = store.StoreFilePartition(file) if err != nil { return fmt.Errorf("error storing file's partition: %w", err) } } fmt.Printf("Partition %v Size: %v files %v\n", i, len(partition), humanize.Bytes(uint64(partSize))) } return nil }, } }