github.com/soulteary/pocket-bookcase@v0.0.0-20240428065142-0b5a9a0fc98a/internal/cmd/add.go (about) 1 package cmd 2 3 import ( 4 "fmt" 5 "os" 6 "strings" 7 8 "github.com/soulteary/pocket-bookcase/internal/core" 9 "github.com/soulteary/pocket-bookcase/internal/model" 10 "github.com/spf13/cobra" 11 ) 12 13 func addCmd() *cobra.Command { 14 cmd := &cobra.Command{ 15 Use: "add url", 16 Short: "Bookmark the specified URL", 17 Args: cobra.ExactArgs(1), 18 Run: addHandler, 19 } 20 21 cmd.Flags().StringP("title", "i", "", "Custom title for this bookmark") 22 cmd.Flags().StringP("excerpt", "e", "", "Custom excerpt for this bookmark") 23 cmd.Flags().StringSliceP("tags", "t", []string{}, "Comma-separated tags for this bookmark") 24 cmd.Flags().BoolP("offline", "o", false, "Save bookmark without fetching data from internet") 25 cmd.Flags().BoolP("no-archival", "a", false, "Save bookmark without creating offline archive") 26 cmd.Flags().Bool("log-archival", false, "Log the archival process") 27 28 return cmd 29 } 30 31 func addHandler(cmd *cobra.Command, args []string) { 32 cfg, deps := initShiori(cmd.Context(), cmd) 33 34 // Read flag and arguments 35 url := args[0] 36 title, _ := cmd.Flags().GetString("title") 37 excerpt, _ := cmd.Flags().GetString("excerpt") 38 tags, _ := cmd.Flags().GetStringSlice("tags") 39 offline, _ := cmd.Flags().GetBool("offline") 40 noArchival, _ := cmd.Flags().GetBool("no-archival") 41 logArchival, _ := cmd.Flags().GetBool("log-archival") 42 43 // Normalize input 44 title = validateTitle(title, "") 45 excerpt = normalizeSpace(excerpt) 46 47 // Create bookmark item 48 book := model.BookmarkDTO{ 49 URL: url, 50 Title: title, 51 Excerpt: excerpt, 52 CreateArchive: !noArchival, 53 } 54 55 // Set bookmark tags 56 book.Tags = make([]model.Tag, len(tags)) 57 for i, tag := range tags { 58 book.Tags[i].Name = strings.TrimSpace(tag) 59 } 60 61 // Clean up bookmark URL 62 var err error 63 book.URL, err = core.RemoveUTMParams(book.URL) 64 if err != nil { 65 cError.Printf("Failed to clean URL: %v\n", err) 66 os.Exit(1) 67 } 68 69 // Make sure bookmark's title not empty 70 if book.Title == "" { 71 book.Title = book.URL 72 } 73 74 // Save bookmark to database 75 books, err := deps.Database.SaveBookmarks(cmd.Context(), true, book) 76 if err != nil { 77 cError.Printf("Failed to save bookmark: %v\n", err) 78 os.Exit(1) 79 } 80 81 book = books[0] 82 83 // If it's not offline mode, fetch data from internet. 84 if !offline { 85 cInfo.Println("Downloading article...") 86 87 var isFatalErr bool 88 content, contentType, err := core.DownloadBookmark(book.URL) 89 if err != nil { 90 cError.Printf("Failed to download: %v\n", err) 91 } 92 93 if err == nil && content != nil { 94 request := core.ProcessRequest{ 95 DataDir: cfg.Storage.DataDir, 96 Bookmark: book, 97 Content: content, 98 ContentType: contentType, 99 LogArchival: logArchival, 100 KeepTitle: title != "", 101 KeepExcerpt: excerpt != "", 102 } 103 104 book, isFatalErr, err = core.ProcessBookmark(deps, request) 105 content.Close() 106 107 if err != nil { 108 cError.Printf("Failed: %v\n", err) 109 } 110 111 if isFatalErr { 112 os.Exit(1) 113 } 114 } 115 116 // Save bookmark to database 117 _, err = deps.Database.SaveBookmarks(cmd.Context(), false, book) 118 if err != nil { 119 cError.Printf("Failed to save bookmark with content: %v\n", err) 120 os.Exit(1) 121 } 122 } 123 124 // Print added bookmark 125 fmt.Println() 126 printBookmarks(book) 127 }