ncDocConverter/internal/ncworker/office.go

207 lines
5.7 KiB
Go
Raw Normal View History

2023-01-02 10:06:13 +00:00
package ncworker
import (
"fmt"
2023-01-02 10:44:59 +00:00
"io"
2023-01-02 10:06:13 +00:00
"net/http"
"path/filepath"
2023-01-02 10:44:59 +00:00
"strings"
"sync"
2023-01-02 10:06:13 +00:00
"time"
"rpjosh.de/ncDocConverter/internal/models"
"rpjosh.de/ncDocConverter/internal/nextcloud"
"rpjosh.de/ncDocConverter/pkg/logger"
"rpjosh.de/ncDocConverter/pkg/utils"
)
type convertJob struct {
job *models.NcConvertJob
ncUser *models.NextcloudUser
}
2023-01-02 10:44:59 +00:00
type convertQueu struct {
source nextcloud.NcFile
destination string
2023-01-02 10:06:13 +00:00
}
func NewNcJob(job *models.NcConvertJob, ncUser *models.NextcloudUser) *convertJob {
convJob := &convertJob{
job: job,
ncUser: ncUser,
}
return convJob
}
func (job *convertJob) ExecuteJob() {
// Get existing directory contents
2023-01-02 10:44:59 +00:00
sourceFolder, err := nextcloud.SearchInDirectory(
2023-01-02 10:06:13 +00:00
job.ncUser,
job.job.SourceDir,
[]string{
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/msword",
},
)
if err != nil {
logger.Error("Failed to get files in source directory '%s': %s", job.job.SourceDir, err)
return
}
2023-01-02 10:44:59 +00:00
destinationFolder, err := nextcloud.SearchInDirectory(
2023-01-02 10:06:13 +00:00
job.ncUser,
job.job.DestinationDir,
[]string{
"application/pdf",
},
)
if err != nil {
logger.Error("Failed to get files in destination directory '%s': %s", job.job.DestinationDir, err)
return
}
2023-01-02 10:44:59 +00:00
// Store all files in a map
prefix := "/remote.php/dav/files/" + job.ncUser.Username + "/"
sourceMap := nextcloud.ParseSearchResult(sourceFolder, prefix, job.job.SourceDir)
destinationMap := nextcloud.ParseSearchResult(destinationFolder, prefix, job.job.DestinationDir)
2023-01-02 10:06:13 +00:00
2023-01-02 10:44:59 +00:00
// check which files should be converted
var filesToConvert []convertQueu
var directorys []string
2023-01-02 10:06:13 +00:00
for index, source := range sourceMap {
2023-01-02 10:44:59 +00:00
// Check if the file exists in the destination map
2023-01-02 10:06:13 +00:00
if dest, exists := destinationMap[index]; exists {
2023-01-02 10:44:59 +00:00
// Compare timestamp and size
if dest.LastModified.Before(source.LastModified) {
filesToConvert = append(filesToConvert, convertQueu{source: source, destination: dest.Path})
2023-01-02 10:06:13 +00:00
}
delete(destinationMap, index)
} else {
2023-01-02 10:44:59 +00:00
// the directory could not be existing -> check for existance
destinationDir := job.getDestinationDir(source.Path)
appendIfNotExists(&directorys, destinationDir[0:strings.LastIndex(destinationDir, "/")+1])
filesToConvert = append(filesToConvert, convertQueu{source: source, destination: destinationDir})
2023-01-02 10:06:13 +00:00
delete(destinationMap, index)
}
}
2023-01-02 10:44:59 +00:00
var wg sync.WaitGroup
2023-01-02 10:06:13 +00:00
// Delete the files which are not available anymore
2023-01-02 10:44:59 +00:00
wg.Add(len(destinationMap))
2023-01-02 10:06:13 +00:00
for _, dest := range destinationMap {
2023-01-02 10:44:59 +00:00
go func(file *nextcloud.NcFile) {
err := nextcloud.DeleteFile(job.ncUser, dest.Path)
if err != nil {
logger.Error(utils.FirstCharToUppercase(err.Error()))
}
wg.Done()
}(&dest)
2023-01-02 10:06:13 +00:00
}
2023-01-02 10:44:59 +00:00
wg.Wait()
// Create required directorys
wg.Add(len(directorys))
for _, dest := range directorys {
go func(path string) {
nextcloud.CreateFoldersRecursively(job.ncUser, path)
wg.Done()
}(dest)
}
wg.Wait()
// Convert the files
wg.Add(len(filesToConvert))
for _, file := range filesToConvert {
logger.Info("Path: %s", file.source.Path)
go func(cvt convertQueu) {
job.convertFile(cvt.source.Path, cvt.source.Fileid, cvt.destination)
wg.Done()
}(file)
}
wg.Wait()
logger.Info("Finished Nextcloud job \"%s\": %d documents converted", job.job.JobName, len(filesToConvert))
}
2023-01-02 10:06:13 +00:00
2023-01-02 10:44:59 +00:00
// Appends the directory to the array if it isn't contained
// by another element already
func appendIfNotExists(dirs *[]string, directory string) {
directoryLength := len(directory)
for i, currentDir := range *dirs {
currentLength := len(currentDir)
// the existing directory is already referenced in the current
if directoryLength > currentLength && directory[0:currentLength] == currentDir {
(*dirs)[i] = directory
continue
} else if directoryLength <= currentLength && currentDir[0:directoryLength] == directory {
continue
}
}
*dirs = append(*dirs, directory)
2023-01-02 10:06:13 +00:00
}
func (job *convertJob) getDestinationDir(sourceFile string) string {
sourceFile = sourceFile[len(job.job.SourceDir):]
var extension = filepath.Ext(sourceFile)
var name = sourceFile[0 : len(sourceFile)-len(extension)]
return job.job.DestinationDir + name + ".pdf"
}
// Converts the source file to the destination file utilizing the onlyoffice convert api
func (job *convertJob) convertFile(sourceFile string, sourceid int, destinationFile string) {
2023-01-02 10:44:59 +00:00
logger.Debug("Converting %s (%d) to %s", sourceFile, sourceid, destinationFile)
2023-01-02 10:06:13 +00:00
client := http.Client{Timeout: 10 * time.Second}
req, err := http.NewRequest(http.MethodGet, job.ncUser.NextcloudBaseUrl+"/apps/onlyoffice/downloadas", nil)
if err != nil {
logger.Error("%s", err)
}
req.SetBasicAuth(job.ncUser.Username, job.ncUser.Password)
q := req.URL.Query()
q.Add("fileId", fmt.Sprint(sourceid))
q.Add("toExtension", "pdf")
req.URL.RawQuery = q.Encode()
res, err := client.Do(req)
if err != nil {
2023-01-02 10:44:59 +00:00
logger.Error("Failed to access the convert api: %s", err)
return
2023-01-02 10:06:13 +00:00
}
defer res.Body.Close()
2023-01-02 10:44:59 +00:00
if res.StatusCode != 200 {
body, _ := io.ReadAll(res.Body)
logger.Error("Failed to access the convert api (#%d). Do you have OnlyOffice installed?: %s", res.StatusCode, body)
return
}
2023-01-02 10:06:13 +00:00
uploadClient := http.Client{Timeout: 10 * time.Second}
uploadReq, err := http.NewRequest(http.MethodPut, job.ncUser.NextcloudBaseUrl+"/remote.php/dav/files/"+job.ncUser.Username+"/"+destinationFile, res.Body)
if err != nil {
logger.Error("%s", err)
}
uploadReq.SetBasicAuth(job.ncUser.Username, job.ncUser.Password)
uploadReq.Header.Set("Content-Type", "application/binary")
res, err = uploadClient.Do(uploadReq)
if err != nil {
logger.Error("%s", err)
}
if res.StatusCode != 204 && res.StatusCode != 201 {
logger.Error("Failed to create file %s (#%d)", destinationFile, res.StatusCode)
}
// Status Code 201
res.Body.Close()
}