mirror of
https://github.com/Eggbertx/gochan.git
synced 2025-09-05 11:06:23 -07:00
Add on-demand fingerprinting function available to mods
This commit is contained in:
parent
97a98ce124
commit
96c2c2bffe
3 changed files with 110 additions and 20 deletions
|
@ -2,11 +2,13 @@ package manage
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
|
@ -15,6 +17,7 @@ import (
|
|||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gctemplates"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
"github.com/gochan-org/gochan/pkg/posting/uploads"
|
||||
"github.com/gochan-org/gochan/pkg/server/serverutil"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
@ -671,6 +674,56 @@ func postInfoCallback(_ http.ResponseWriter, request *http.Request, _ *gcsql.Sta
|
|||
return postInfo, nil
|
||||
}
|
||||
|
||||
type fingerprintJSON struct {
|
||||
Filename string `json:"file"`
|
||||
Fingerprint string `json:"fingerprint"`
|
||||
}
|
||||
|
||||
func fingerprintCallback(_ http.ResponseWriter, request *http.Request, _ *gcsql.Staff, _ bool, _ *zerolog.Event, errEv *zerolog.Event) (output interface{}, err error) {
|
||||
postIDstr := request.Form.Get("post")
|
||||
if postIDstr == "" {
|
||||
return "", errors.New("missing 'post' field")
|
||||
}
|
||||
postID, err := strconv.Atoi(postIDstr)
|
||||
if err != nil {
|
||||
errEv.Err(err).Caller().Send()
|
||||
return "", err
|
||||
}
|
||||
const query = `SELECT
|
||||
(SELECT dir from DBPREFIXboards WHERE id = (SELECT thread_id FROM DBPREFIXposts WHERE id = ?))
|
||||
AS board, filename, is_spoilered FROM DBPREFIXfiles WHERE post_id = ? LIMIT 1`
|
||||
var board, filename string
|
||||
var spoiler bool
|
||||
err = gcsql.QueryRowSQL(query, []any{postID, postID}, []any{&board, &filename, &spoiler})
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return "", errors.New("post has no files")
|
||||
} else if err != nil {
|
||||
errEv.Err(err).Caller().Send()
|
||||
return "", err
|
||||
}
|
||||
fpVideoThumbs := config.GetSiteConfig().FingerprintVideoThumbnails
|
||||
if !uploads.IsImage(filename) && !uploads.IsVideo(filename) {
|
||||
return "", fmt.Errorf(
|
||||
"unable to fingerprint file %q (not an image or a video)", filename)
|
||||
} else if uploads.IsVideo(filename) && !fpVideoThumbs {
|
||||
return "", fmt.Errorf(
|
||||
"unable to fingerprint file %q (video thumbnail fingerprinting not enabled)",
|
||||
filename)
|
||||
}
|
||||
docRoot := config.GetSystemCriticalConfig().DocumentRoot
|
||||
filePath := path.Join(docRoot, board, "src", filename)
|
||||
fingerprintHash, err := uploads.FingerprintFile(filePath)
|
||||
if err != nil {
|
||||
errEv.Err(err).Caller().Send()
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fingerprintJSON{
|
||||
Filename: filename,
|
||||
Fingerprint: fingerprintHash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func registerModeratorPages() {
|
||||
actions = append(actions,
|
||||
Action{
|
||||
|
@ -727,5 +780,12 @@ func registerModeratorPages() {
|
|||
JSONoutput: AlwaysJSON,
|
||||
Callback: postInfoCallback,
|
||||
},
|
||||
Action{
|
||||
ID: "fingerprint",
|
||||
Title: "Get image/thumbnail fingerprint",
|
||||
Permissions: ModPerms,
|
||||
JSONoutput: AlwaysJSON,
|
||||
Callback: fingerprintCallback,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -39,6 +39,26 @@ func RegisterUploadHandler(ext string, handler UploadHandler) {
|
|||
uploadHandlers[ext] = handler
|
||||
}
|
||||
|
||||
func IsImage(file string) bool {
|
||||
ext := path.Ext(file)
|
||||
for _, iExt := range ImageExtensions {
|
||||
if ext == iExt {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func IsVideo(file string) bool {
|
||||
ext := path.Ext(file)
|
||||
for _, vExt := range VideoExtensions {
|
||||
if ext == vExt {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func init() {
|
||||
uploadHandlers = make(map[string]UploadHandler)
|
||||
for _, ext := range ImageExtensions {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package uploads
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"net/http"
|
||||
"path"
|
||||
|
||||
"github.com/devedge/imagehash"
|
||||
"github.com/disintegration/imaging"
|
||||
|
@ -12,8 +12,12 @@ import (
|
|||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrVideoThumbFingerprint = errors.New("video thumbnail fingerprinting not enabled")
|
||||
)
|
||||
|
||||
const (
|
||||
defaultFingerprintHashLength = 16
|
||||
defaultFingerprintHashLength = 8
|
||||
)
|
||||
|
||||
type FingerprintSource struct {
|
||||
|
@ -22,11 +26,16 @@ type FingerprintSource struct {
|
|||
Request *http.Request
|
||||
}
|
||||
|
||||
func fingerprintImage(img image.Image, board string) (*gcsql.FileBan, error) {
|
||||
func getHashLength() int {
|
||||
hashLength := config.GetSiteConfig().FingerprintHashLength
|
||||
if hashLength < 1 {
|
||||
hashLength = defaultFingerprintHashLength
|
||||
return defaultFingerprintHashLength
|
||||
}
|
||||
return hashLength
|
||||
}
|
||||
|
||||
func checkImageFingerprintBan(img image.Image, board string) (*gcsql.FileBan, error) {
|
||||
hashLength := getHashLength()
|
||||
ba, err := imagehash.Ahash(img, hashLength)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -50,28 +59,29 @@ func fingerprintImage(img image.Image, board string) (*gcsql.FileBan, error) {
|
|||
return &fileBan, err
|
||||
}
|
||||
|
||||
func fingerprintFile(filePath string, board string) (*gcsql.FileBan, error) {
|
||||
func FingerprintFile(filePath string) (string, error) {
|
||||
img, err := imaging.Open(filePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
fmt.Println("hash length:", getHashLength())
|
||||
ba, err := imagehash.Ahash(img, getHashLength())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%x", ba), nil
|
||||
}
|
||||
|
||||
func checkFileFingerprintBan(filePath string, board string) (*gcsql.FileBan, error) {
|
||||
img, err := imaging.Open(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fingerprintImage(img, board)
|
||||
return checkImageFingerprintBan(img, board)
|
||||
}
|
||||
|
||||
func canFingerprint(filename string) bool {
|
||||
siteCfg := config.GetSiteConfig()
|
||||
ext := path.Ext(filename)
|
||||
for _, iExt := range ImageExtensions {
|
||||
if iExt == ext {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if siteCfg.FingerprintVideoThumbnails {
|
||||
for _, vExt := range VideoExtensions {
|
||||
if vExt == ext {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
return IsImage(filename) || (IsVideo(filename) && siteCfg.FingerprintVideoThumbnails)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue