206 lines
5.7 KiB
Go
206 lines
5.7 KiB
Go
package ncworker
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"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
|
|
}
|
|
|
|
type convertQueu struct {
|
|
source nextcloud.NcFile
|
|
destination string
|
|
}
|
|
|
|
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
|
|
sourceFolder, err := nextcloud.SearchInDirectory(
|
|
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
|
|
}
|
|
|
|
destinationFolder, err := nextcloud.SearchInDirectory(
|
|
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
|
|
}
|
|
|
|
// 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)
|
|
|
|
// check which files should be converted
|
|
var filesToConvert []convertQueu
|
|
var directorys []string
|
|
|
|
for index, source := range sourceMap {
|
|
// Check if the file exists in the destination map
|
|
if dest, exists := destinationMap[index]; exists {
|
|
// Compare timestamp and size
|
|
if dest.LastModified.Before(source.LastModified) {
|
|
filesToConvert = append(filesToConvert, convertQueu{source: source, destination: dest.Path})
|
|
}
|
|
delete(destinationMap, index)
|
|
} else {
|
|
// 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})
|
|
|
|
delete(destinationMap, index)
|
|
}
|
|
}
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
// Delete the files which are not available anymore
|
|
wg.Add(len(destinationMap))
|
|
for _, dest := range destinationMap {
|
|
go func(file *nextcloud.NcFile) {
|
|
err := nextcloud.DeleteFile(job.ncUser, file.Path)
|
|
if err != nil {
|
|
logger.Error(utils.FirstCharToUppercase(err.Error()))
|
|
}
|
|
wg.Done()
|
|
}(&dest)
|
|
}
|
|
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 {
|
|
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))
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
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) {
|
|
logger.Debug("Converting %s (%d) to %s", sourceFile, sourceid, destinationFile)
|
|
|
|
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 {
|
|
logger.Error("Failed to access the convert api: %s", err)
|
|
return
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
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
|
|
}
|
|
|
|
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()
|
|
}
|