Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ put book.pdf /books
- `--force`: Completely replace an existing document (removes all annotations and metadata)
- `--content-only`: Replace only the PDF content while preserving annotations and metadata
- `--coverpage=<0|1>`: Set coverpage (0 to disable, 1 to set first page as cover)
- `--currentpage=<N>`: Set the current page, 1-indexed (PDF only)
- `--contrast=<fullpage|off|adaptive>`: Set the contrast filter (PDF and epub only)

Examples:

Expand All @@ -203,14 +205,17 @@ put --content-only document.pdf
# Upload with coverpage set to first page
put --coverpage=1 document.pdf

# Upload and open to page 5 with full-page contrast
put --currentpage=5 --contrast=fullpage document.pdf

# Replace PDF content in specific directory
put --content-only document.pdf /target-directory

# Upload to specific directory with force
put --force document.pdf /reports
```

**Note**: `--force` and `--content-only` are mutually exclusive. The `--coverpage` flag can be combined with either. If the target document doesn't exist, all flags will create a new document.
**Note**: `--force` and `--content-only` are mutually exclusive. The `--coverpage`, `--currentpage`, and `--contrast` flags can be combined with either. If the target document doesn't exist, all flags will create a new document.

## Recursively upload directories and files

Expand Down
2 changes: 1 addition & 1 deletion api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type ApiCtx interface {
Filetree() *filetree.FileTreeCtx
FetchDocument(docId, dstPath string) error
CreateDir(parentId, name string, notify bool) (*model.Document, error)
UploadDocument(parentId string, sourceDocPath string, notify bool, coverpage *int) (*model.Document, error)
UploadDocument(parentId string, sourceDocPath string, notify bool, coverpage *int, currentPage *int, pageCount *int, contrastFilter *string) (*model.Document, error)
ReplaceDocumentFile(docId, sourceDocPath string, notify bool) error
MoveEntry(src, dstDir *model.Node, name string) (*model.Node, error)
DeleteEntry(node *model.Node, recursive, notify bool) error
Expand Down
8 changes: 4 additions & 4 deletions api/sync15/apictx.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,13 @@ func (ctx *ApiCtx) CreateDir(parentId, name string, notify bool) (*model.Documen
return nil, err
}
id := uuid.New().String()
objectName, filePath, err := archive.CreateMetadata(id, name, parentId, model.DirectoryType, tmpDir)
objectName, filePath, err := archive.CreateMetadata(id, name, parentId, model.DirectoryType, tmpDir, nil)
if err != nil {
return nil, err
}
files.AddMap(objectName, filePath, archive.MetadataExt)

objectName, filePath, err = archive.CreateContent(id, "", tmpDir, nil, nil)
objectName, filePath, err = archive.CreateContent(id, "", tmpDir, nil, nil, nil, nil, nil)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -335,7 +335,7 @@ func (ctx *ApiCtx) MoveEntry(src, dstDir *model.Node, name string) (*model.Node,
}

// UploadDocument uploads a local document given by sourceDocPath under the parentId directory
func (ctx *ApiCtx) UploadDocument(parentId string, sourceDocPath string, notify bool, coverpage *int) (*model.Document, error) {
func (ctx *ApiCtx) UploadDocument(parentId string, sourceDocPath string, notify bool, coverpage *int, currentPage *int, pageCount *int, contrastFilter *string) (*model.Document, error) {
//TODO: overwrite file
name, ext := util.DocPathToName(sourceDocPath)

Expand All @@ -356,7 +356,7 @@ func (ctx *ApiCtx) UploadDocument(parentId string, sourceDocPath string, notify

defer os.RemoveAll(tmpDir)

docFiles, id, err := archive.Prepare(name, parentId, sourceDocPath, ext, tmpDir, coverpage)
docFiles, id, err := archive.Prepare(name, parentId, sourceDocPath, ext, tmpDir, coverpage, currentPage, pageCount, contrastFilter)
if err != nil {
return nil, err
}
Expand Down
8 changes: 4 additions & 4 deletions archive/blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (d *DocumentFiles) AddMap(name, filepath string, filetype RmExt) {
}

// Prepare prepares a file for uploading (creates needed temp files or unpacks a zip)
func Prepare(name, parentId, sourceDocPath, ext, tmpDir string, coverpage *int) (files *DocumentFiles, id string, err error) {
func Prepare(name, parentId, sourceDocPath, ext, tmpDir string, coverpage *int, currentPage *int, pageCount *int, contrastFilter *string) (files *DocumentFiles, id string, err error) {
files = &DocumentFiles{}
if ext == util.ZIP || ext == util.RMDOC {
var metadataPath string
Expand All @@ -60,7 +60,7 @@ func Prepare(name, parentId, sourceDocPath, ext, tmpDir string, coverpage *int)
}
if metadataPath == "" {
log.Warning.Println("missing metadata, creating...", name)
objectName, filePath, err1 := CreateMetadata(id, name, parentId, model.DocumentType, tmpDir)
objectName, filePath, err1 := CreateMetadata(id, name, parentId, model.DocumentType, tmpDir, currentPage)
if err1 != nil {
err = err1
return
Expand All @@ -84,14 +84,14 @@ func Prepare(name, parentId, sourceDocPath, ext, tmpDir string, coverpage *int)
pageIds = []string{pageId}
}
files.AddMap(objectName, sourceDocPath, RmExt(doctype))
objectName, filePath, err1 := CreateMetadata(id, name, parentId, model.DocumentType, tmpDir)
objectName, filePath, err1 := CreateMetadata(id, name, parentId, model.DocumentType, tmpDir, currentPage)
if err1 != nil {
err = err1
return
}
files.AddMap(objectName, filePath, MetadataExt)

objectName, filePath, err = CreateContent(id, doctype, tmpDir, pageIds, coverpage)
objectName, filePath, err = CreateContent(id, doctype, tmpDir, pageIds, coverpage, currentPage, pageCount, contrastFilter)
if err != nil {
return
}
Expand Down
3 changes: 2 additions & 1 deletion archive/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ type Content struct {
DocumentTags []Tag `json:"tags"`
RedirectionMap []int `json:"redirectionPageMap"`
TextScale float64 `json:"textScale"`
CoverPageNumber *int `json:"coverPageNumber,omitempty"`
CoverPageNumber *int `json:"coverPageNumber,omitempty"`
ViewBackgroundFilter *string `json:"viewBackgroundFilter,omitempty"`

Transform *Transform `json:"-"`
}
Expand Down
24 changes: 17 additions & 7 deletions archive/zipdoc.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ func CreateZipDocument(id, srcPath string) (zipPath string, err error) {
return
}

c, err := createZipContent(fileType, pages, nil)
c, err := createZipContent(fileType, pages, nil, nil, nil, nil)
if err != nil {
return
}
Expand Down Expand Up @@ -179,7 +179,7 @@ func CreateZipDirectory(id string) (string, error) {
return tmp.Name(), nil
}

func createZipContent(ext string, pageIDs []string, coverpage *int) (string, error) {
func createZipContent(ext string, pageIDs []string, coverpage *int, currentPage *int, pageCount *int, contrastFilter *string) (string, error) {
c := Content{
DummyDocument: false,
ExtraMetadata: ExtraMetadata{
Expand All @@ -204,8 +204,15 @@ func createZipContent(ext string, pageIDs []string, coverpage *int) (string, err
M32: 0,
M33: 1,
},
Pages: pageIDs,
CoverPageNumber: coverpage,
Pages: pageIDs,
CoverPageNumber: coverpage,
ViewBackgroundFilter: contrastFilter,
}
if currentPage != nil {
c.LastOpenedPage = *currentPage
}
if pageCount != nil {
c.PageCount = *pageCount
}

cstring, err := json.Marshal(c)
Expand All @@ -218,13 +225,13 @@ func createZipContent(ext string, pageIDs []string, coverpage *int) (string, err
return string(cstring), nil
}

func CreateContent(id, ext, fpath string, pageIds []string, coverpage *int) (fileName, filePath string, err error) {
func CreateContent(id, ext, fpath string, pageIds []string, coverpage *int, currentPage *int, pageCount *int, contrastFilter *string) (fileName, filePath string, err error) {
fileName = id + "." + string(ContentExt)
filePath = path.Join(fpath, fileName)
content := "{}"

if ext != "" {
content, err = createZipContent(ext, pageIds, coverpage)
content, err = createZipContent(ext, pageIds, coverpage, currentPage, pageCount, contrastFilter)
if err != nil {
return
}
Expand All @@ -240,7 +247,7 @@ func UnixTimestamp() string {
return tf
}

func CreateMetadata(id, name, parent, colType, fpath string) (fileName string, filePath string, err error) {
func CreateMetadata(id, name, parent, colType, fpath string, currentPage *int) (fileName string, filePath string, err error) {
fileName = id + "." + string(MetadataExt)
filePath = path.Join(fpath, fileName)
meta := MetadataFile{
Expand All @@ -251,6 +258,9 @@ func CreateMetadata(id, name, parent, colType, fpath string) (fileName string, f
Synced: true,
LastModified: UnixTimestamp(),
}
if currentPage != nil {
meta.LastOpenedPage = *currentPage
}

c, err := json.Marshal(meta)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion shell/mput.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ func putFilesAndDirs(pCtx *ShellCtxt, pC *ishell.Context, localDir string, depth
pC.Printf("uploading: [%s]...", name)

fullName := path.Join(localDir, name)
doc, err := pCtx.api.UploadDocument(pCtx.node.Id(), fullName, false, nil)
doc, err := pCtx.api.UploadDocument(pCtx.node.Id(), fullName, false, nil, nil, nil, nil)

if err != nil {
pC.Err(fmt.Errorf("failed to upload file '%s', %v", name, err))
Expand Down
69 changes: 66 additions & 3 deletions shell/put.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ package shell
import (
"errors"
"fmt"
"os"
"strconv"

"github.com/abiosoft/ishell"
"github.com/juruen/rmapi/util"
"github.com/ogier/pflag"
pdf "github.com/unidoc/unipdf/v3/model"
)

func putCmd(ctx *ShellCtxt) *ishell.Cmd {
Expand All @@ -29,6 +32,8 @@ func putCmd(ctx *ShellCtxt) *ishell.Cmd {
force := flags.Bool("force", false, "Overwrite existing file (recreates document)")
contentOnly := flags.Bool("content-only", false, "Overwrite existing file (recreates document)")
coverpage := flags.String("coverpage", "", "Set coverpage (0 to disable, 1 to set first page as cover)")
currentpageStr := flags.String("currentpage", "", "Set current page (1-indexed)")
contrast := flags.String("contrast", "", "Set contrast filter (fullpage, off, adaptive)")

if !processFlagSet(flags, longHelp, c.Args, c) {
return
Expand Down Expand Up @@ -61,6 +66,64 @@ func putCmd(ctx *ShellCtxt) *ishell.Cmd {
}
}

_, srcExt := util.DocPathToName(args[0])

var currentPageFlag *int
var pageCountFlag *int
if *currentpageStr != "" {
if srcExt != "pdf" {
c.Err(errors.New("--currentpage is only supported for PDF files"))
return
}
val, err := strconv.Atoi(*currentpageStr)
if err != nil || val < 1 {
c.Err(errors.New("--currentpage must be a positive integer"))
return
}

f, err := os.Open(args[0])
if err != nil {
c.Err(fmt.Errorf("failed to open file: %v", err))
return
}
reader, err := pdf.NewPdfReader(f)
f.Close()
if err != nil {
c.Err(fmt.Errorf("failed to read PDF: %v", err))
return
}
numPages, err := reader.GetNumPages()
if err != nil {
c.Err(fmt.Errorf("failed to get page count: %v", err))
return
}
if val > numPages {
c.Err(fmt.Errorf("--currentpage %d exceeds page count (%d)", val, numPages))
return
}
pageCountFlag = &numPages

val-- // convert 1-indexed to 0-indexed
currentPageFlag = &val
}

var contrastFlag *string
if *contrast != "" {
if srcExt != "pdf" && srcExt != "epub" {
c.Err(errors.New("--contrast is only supported for PDF and epub files"))
return
}
switch *contrast {
case "fullpage", "off":
contrastFlag = contrast
case "adaptive":
// leave nil to omit field
default:
c.Err(errors.New("--contrast must be fullpage, off, or adaptive"))
return
}
}

srcName := args[0]

// Handle --content-only mode (replace PDF content)
Expand Down Expand Up @@ -90,7 +153,7 @@ func putCmd(ctx *ShellCtxt) *ishell.Cmd {
// Document doesn't exist, create new one
c.Printf("uploading: [%s]...", srcName)
dstDir := node.Id()
document, err := ctx.api.UploadDocument(dstDir, srcName, true, coverpageFlag)
document, err := ctx.api.UploadDocument(dstDir, srcName, true, coverpageFlag, currentPageFlag, pageCountFlag, contrastFlag)
if err != nil {
c.Err(fmt.Errorf("failed to upload file [%s]: %v", srcName, err))
return
Expand Down Expand Up @@ -152,7 +215,7 @@ func putCmd(ctx *ShellCtxt) *ishell.Cmd {

// Upload new document
dstDir := node.Id()
document, err := ctx.api.UploadDocument(dstDir, srcName, true, coverpageFlag)
document, err := ctx.api.UploadDocument(dstDir, srcName, true, coverpageFlag, currentPageFlag, pageCountFlag, contrastFlag)
if err != nil {
c.Err(fmt.Errorf("failed to upload replacement file [%s]: %v", srcName, err))
return
Expand All @@ -166,7 +229,7 @@ func putCmd(ctx *ShellCtxt) *ishell.Cmd {
// File doesn't exist, upload new document
c.Printf("uploading: [%s]...", srcName)
dstDir := node.Id()
document, err := ctx.api.UploadDocument(dstDir, srcName, true, coverpageFlag)
document, err := ctx.api.UploadDocument(dstDir, srcName, true, coverpageFlag, currentPageFlag, pageCountFlag, contrastFlag)

if err != nil {
c.Err(fmt.Errorf("failed to upload file [%s] %v", srcName, err))
Expand Down
Loading