2018-02-03 16:02:28 +08:00
package cmd
import (
"fmt"
2018-02-17 22:49:16 +08:00
"html/template"
2018-02-03 16:02:28 +08:00
"strconv"
"strings"
"sync"
"time"
2018-03-02 20:29:26 +08:00
"github.com/RadhiFadlillah/go-readability"
"github.com/RadhiFadlillah/shiori/model"
"github.com/spf13/cobra"
2018-02-03 16:02:28 +08:00
)
var (
updateCmd = & cobra . Command {
Use : "update [indices]" ,
2018-03-02 20:29:26 +08:00
Short : "Update the saved bookmarks" ,
2018-02-03 16:02:28 +08:00
Long : "Update fields of an existing bookmark. " +
"Accepts space-separated list of indices (e.g. 5 6 23 4 110 45), hyphenated range (e.g. 100-200) or both (e.g. 1-3 7 9). " +
"If no arguments, ALL bookmarks will be updated. Update works differently depending on the flags:\n" +
"- If indices are passed without any flags (--url, --title, --tag and --excerpt), read the URLs from DB and update titles from web.\n" +
"- If --url is passed (and --title is omitted), update the title from web using the URL. While using this flag, update only accept EXACTLY one index.\n" +
"While updating bookmark's tags, you can use - to remove tag (e.g. -nature to remove nature tag from this bookmark)." ,
Run : func ( cmd * cobra . Command , args [ ] string ) {
// Read flags
url , _ := cmd . Flags ( ) . GetString ( "url" )
title , _ := cmd . Flags ( ) . GetString ( "title" )
excerpt , _ := cmd . Flags ( ) . GetString ( "excerpt" )
tags , _ := cmd . Flags ( ) . GetStringSlice ( "tags" )
offline , _ := cmd . Flags ( ) . GetBool ( "offline" )
skipConfirmation , _ := cmd . Flags ( ) . GetBool ( "yes" )
// Check if --url flag is used
if url != "" {
if len ( args ) != 1 {
cError . Println ( "Update only accepts one index while using --url flag" )
return
}
idx , err := strconv . Atoi ( args [ 0 ] )
if err != nil || idx < - 1 {
cError . Println ( "Index is not valid" )
return
}
}
// If no arguments, confirm to user
if len ( args ) == 0 && ! skipConfirmation {
confirmUpdate := ""
fmt . Print ( "Update ALL bookmarks? (y/n): " )
fmt . Scanln ( & confirmUpdate )
if confirmUpdate != "y" {
fmt . Println ( "No bookmarks updated" )
return
}
}
2018-02-13 17:14:08 +08:00
// Update bookmarks
bookmarks , err := updateBookmarks ( args , url , title , excerpt , tags , offline )
2018-02-03 16:02:28 +08:00
if err != nil {
cError . Println ( err )
return
}
2018-02-13 17:14:08 +08:00
printBookmark ( bookmarks ... )
} ,
}
)
2018-02-03 16:02:28 +08:00
2018-02-13 17:14:08 +08:00
func init ( ) {
updateCmd . Flags ( ) . StringP ( "url" , "u" , "" , "New URL for this bookmark." )
updateCmd . Flags ( ) . StringP ( "title" , "i" , "" , "New title for this bookmark." )
updateCmd . Flags ( ) . StringP ( "excerpt" , "e" , "" , "New excerpt for this bookmark." )
updateCmd . Flags ( ) . StringSliceP ( "tags" , "t" , [ ] string { } , "Comma-separated tags for this bookmark." )
updateCmd . Flags ( ) . BoolP ( "offline" , "o" , false , "Update bookmark without fetching data from internet." )
updateCmd . Flags ( ) . BoolP ( "yes" , "y" , false , "Skip confirmation prompt and update ALL bookmarks" )
rootCmd . AddCommand ( updateCmd )
}
2018-02-03 16:02:28 +08:00
2018-02-13 17:14:08 +08:00
func updateBookmarks ( indices [ ] string , url , title , excerpt string , tags [ ] string , offline bool ) ( [ ] model . Bookmark , error ) {
2018-02-17 13:30:08 +08:00
mutex := sync . Mutex { }
2018-02-13 17:14:08 +08:00
// Read bookmarks from database
2018-02-22 21:45:43 +08:00
bookmarks , err := DB . GetBookmarks ( true , indices ... )
2018-02-13 17:14:08 +08:00
if err != nil {
return [ ] model . Bookmark { } , err
}
2018-02-03 16:02:28 +08:00
2018-02-13 17:14:08 +08:00
if len ( bookmarks ) == 0 {
return [ ] model . Bookmark { } , fmt . Errorf ( "No matching index found" )
}
2018-02-03 16:02:28 +08:00
2018-02-13 17:14:08 +08:00
if url != "" && len ( bookmarks ) == 1 {
bookmarks [ 0 ] . URL = url
}
2018-02-03 16:02:28 +08:00
2018-02-13 17:14:08 +08:00
// If not offline, fetch articles from internet
if ! offline {
waitGroup := sync . WaitGroup { }
for i , book := range bookmarks {
2018-02-17 13:30:08 +08:00
waitGroup . Add ( 1 )
2018-02-13 17:14:08 +08:00
go func ( pos int , book model . Bookmark ) {
defer waitGroup . Done ( )
article , err := readability . Parse ( book . URL , 10 * time . Second )
if err == nil {
book . Title = article . Meta . Title
book . ImageURL = article . Meta . Image
book . Excerpt = article . Meta . Excerpt
book . Author = article . Meta . Author
book . MinReadTime = article . Meta . MinReadTime
book . MaxReadTime = article . Meta . MaxReadTime
book . Content = article . Content
2018-02-17 22:49:16 +08:00
book . HTML = template . HTML ( article . RawContent )
2018-02-13 17:14:08 +08:00
mutex . Lock ( )
bookmarks [ pos ] = book
mutex . Unlock ( )
2018-02-03 16:02:28 +08:00
}
2018-02-13 17:14:08 +08:00
} ( i , book )
}
2018-02-03 16:02:28 +08:00
2018-02-13 17:14:08 +08:00
waitGroup . Wait ( )
}
2018-02-03 16:02:28 +08:00
2018-02-13 17:14:08 +08:00
// Map the tags to be deleted
addedTags := make ( map [ string ] struct { } )
deletedTags := make ( map [ string ] struct { } )
for _ , tag := range tags {
tag = strings . ToLower ( tag )
tag = strings . TrimSpace ( tag )
if strings . HasPrefix ( tag , "-" ) {
tag = strings . TrimPrefix ( tag , "-" )
deletedTags [ tag ] = struct { } { }
} else {
addedTags [ tag ] = struct { } { }
}
}
2018-02-03 16:02:28 +08:00
2018-02-13 17:14:08 +08:00
// Set default title, excerpt and tags
for i := range bookmarks {
if title != "" {
bookmarks [ i ] . Title = title
}
if excerpt != "" {
bookmarks [ i ] . Excerpt = excerpt
}
tempAddedTags := make ( map [ string ] struct { } )
for key , value := range addedTags {
tempAddedTags [ key ] = value
}
newTags := [ ] model . Tag { }
for _ , tag := range bookmarks [ i ] . Tags {
if _ , isDeleted := deletedTags [ tag . Name ] ; isDeleted {
tag . Deleted = true
}
2018-02-03 16:02:28 +08:00
2018-02-13 17:14:08 +08:00
if _ , alreadyExist := addedTags [ tag . Name ] ; alreadyExist {
delete ( tempAddedTags , tag . Name )
}
2018-02-03 16:02:28 +08:00
2018-02-13 17:14:08 +08:00
newTags = append ( newTags , tag )
}
2018-02-03 16:02:28 +08:00
2018-02-13 17:14:08 +08:00
for tag := range tempAddedTags {
newTags = append ( newTags , model . Tag { Name : tag } )
}
2018-02-03 16:02:28 +08:00
2018-02-13 17:14:08 +08:00
bookmarks [ i ] . Tags = newTags
}
2018-02-03 16:02:28 +08:00
2018-02-26 18:00:14 +08:00
result , err := DB . UpdateBookmarks ( bookmarks )
2018-02-13 17:14:08 +08:00
if err != nil {
return [ ] model . Bookmark { } , fmt . Errorf ( "Failed to update bookmarks: %v" , err )
2018-02-03 16:02:28 +08:00
}
2018-02-26 18:00:14 +08:00
return result , nil
2018-02-03 16:02:28 +08:00
}