331 lines
6.7 KiB
Go
331 lines
6.7 KiB
Go
/*
|
|
* Copyright (c) 2013-2021 Utkan Güngördü <utkan@freeconsole.org>
|
|
* Copyright (c) 2021-2024 Piotr Grabowski
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package gomicsv
|
|
|
|
import (
|
|
"errors"
|
|
"log"
|
|
"math/rand"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/fauu/gomicsv/archive"
|
|
"github.com/fauu/gomicsv/imgdiff"
|
|
)
|
|
|
|
func (app *App) randomPage() {
|
|
if !app.archiveIsLoaded() {
|
|
return
|
|
}
|
|
if app.S.Archive.Len() == nil {
|
|
return
|
|
}
|
|
|
|
app.setPage(rand.Int() % *app.S.Archive.Len())
|
|
}
|
|
|
|
func (app *App) previousPage() {
|
|
if !app.archiveIsLoaded() {
|
|
if app.Config.Seamless {
|
|
app.previousArchive()
|
|
}
|
|
return
|
|
}
|
|
|
|
if app.Config.Random && app.S.Archive.Len() != nil {
|
|
app.randomPage()
|
|
return
|
|
}
|
|
|
|
n := 1
|
|
if app.Config.DoublePage && app.S.ArchivePos > 1 {
|
|
n = 2
|
|
}
|
|
|
|
if app.Config.Seamless && app.S.ArchivePos+1 <= n {
|
|
app.previousArchive()
|
|
return
|
|
}
|
|
|
|
app.setPage(app.S.ArchivePos - n)
|
|
|
|
if app.Config.DoublePage &&
|
|
app.shouldForceSinglePage() &&
|
|
app.S.Archive.Len() != nil &&
|
|
*app.S.Archive.Len()-app.S.ArchivePos > 1 {
|
|
|
|
app.nextPage()
|
|
}
|
|
}
|
|
|
|
func (app *App) nextPage() {
|
|
if !app.archiveIsLoaded() {
|
|
if app.Config.Seamless {
|
|
app.nextArchive()
|
|
}
|
|
return
|
|
}
|
|
|
|
if app.Config.Random && app.S.Archive.Len() != nil {
|
|
app.randomPage()
|
|
return
|
|
}
|
|
|
|
n := 1
|
|
if app.Config.DoublePage &&
|
|
!app.shouldForceSinglePage() &&
|
|
app.S.Archive.Len() != nil &&
|
|
*app.S.Archive.Len() > app.S.ArchivePos+2 {
|
|
|
|
n = 2
|
|
}
|
|
|
|
if app.Config.Seamless &&
|
|
app.S.Archive.Len() != nil &&
|
|
*app.S.Archive.Len()-app.S.ArchivePos <= n {
|
|
|
|
app.nextArchive()
|
|
return
|
|
}
|
|
|
|
app.setPage(app.S.ArchivePos + n)
|
|
}
|
|
|
|
func (app *App) firstPage() {
|
|
if !app.archiveIsLoaded() {
|
|
return
|
|
}
|
|
app.setPage(0)
|
|
}
|
|
|
|
func (app *App) lastPage() {
|
|
if !app.archiveIsLoaded() {
|
|
return
|
|
}
|
|
|
|
if app.S.Archive.Len() == nil {
|
|
return
|
|
}
|
|
|
|
offset := -1
|
|
if app.Config.DoublePage && *app.S.Archive.Len() >= 2 {
|
|
offset = -2
|
|
}
|
|
app.setPage(*app.S.Archive.Len() + offset)
|
|
}
|
|
|
|
func (app *App) imageHash(n int) (imgdiff.Hash, bool) {
|
|
if hash, ok := app.S.ImageHashes[n]; ok {
|
|
return hash, true
|
|
}
|
|
|
|
pixbuf, err := app.S.Archive.Load(n, app.Config.EmbeddedOrientation, 0)
|
|
if err != nil {
|
|
app.showError(err.Error())
|
|
return 0, false
|
|
}
|
|
|
|
return imgdiff.DHash(pixbuf), true
|
|
}
|
|
|
|
func (app *App) skipForward() {
|
|
app.setPage(app.S.ArchivePos + app.Config.NSkip)
|
|
}
|
|
|
|
func (app *App) skipBackward() {
|
|
app.setPage(app.S.ArchivePos - app.Config.NSkip)
|
|
}
|
|
|
|
func (app *App) nextScene() {
|
|
if !app.archiveIsLoaded() {
|
|
return
|
|
}
|
|
|
|
if app.S.Archive.Len() == nil {
|
|
return
|
|
}
|
|
|
|
if app.S.PixbufL == nil {
|
|
return
|
|
}
|
|
hash := imgdiff.DHash(app.S.PixbufL)
|
|
|
|
dn := app.Config.SceneScanSkip
|
|
if *app.S.Archive.Len()-1-app.S.ArchivePos <= dn {
|
|
dn = 1
|
|
}
|
|
|
|
for n := app.S.ArchivePos + 1; n < *app.S.Archive.Len(); n += dn {
|
|
h, ok := app.imageHash(n)
|
|
if !ok {
|
|
return
|
|
}
|
|
distance := float32(imgdiff.Distance(hash, h)) / 64
|
|
|
|
if distance > app.Config.ImageDiffThres {
|
|
if dn == 1 || n == app.S.ArchivePos+1 {
|
|
app.doSetPage(n)
|
|
return
|
|
}
|
|
|
|
// Did we go too fast?
|
|
for l := n - 1; l >= app.S.ArchivePos+1; l-- {
|
|
h, ok := app.imageHash(l)
|
|
if !ok {
|
|
return
|
|
}
|
|
d := float32(imgdiff.Distance(hash, h)) / 64
|
|
if d <= app.Config.ImageDiffThres {
|
|
app.doSetPage(l + 1)
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (app *App) previousScene() {
|
|
if !app.archiveIsLoaded() {
|
|
return
|
|
}
|
|
|
|
if app.S.PixbufL == nil {
|
|
return
|
|
}
|
|
hash := imgdiff.DHash(app.S.PixbufL)
|
|
|
|
dn := app.Config.SceneScanSkip
|
|
if app.S.ArchivePos <= dn {
|
|
dn = 1
|
|
}
|
|
|
|
for n := app.S.ArchivePos - 1; n >= 0; n -= dn {
|
|
h, ok := app.imageHash(n)
|
|
if !ok {
|
|
return
|
|
}
|
|
distance := float32(imgdiff.Distance(hash, h)) / 64
|
|
|
|
if distance > app.Config.ImageDiffThres {
|
|
if dn == 1 || n == app.S.ArchivePos-1 {
|
|
app.doSetPage(n)
|
|
return
|
|
}
|
|
|
|
// Did we go too fast?
|
|
for l := n + 1; l <= app.S.ArchivePos-1; l++ {
|
|
h, ok := app.imageHash(l)
|
|
if !ok {
|
|
return
|
|
}
|
|
d := float32(imgdiff.Distance(hash, h)) / 64
|
|
if d <= app.Config.ImageDiffThres {
|
|
app.doSetPage(l - 1)
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO(fau): Distinguish a failiure from the "no next archive" condition and inform the user accordingly
|
|
func (app *App) nextArchive() bool {
|
|
newName, err := app.archiveNameRelativeToCurrent(1)
|
|
if err != nil {
|
|
log.Printf("Error getting next archive: %v", err)
|
|
return false
|
|
}
|
|
|
|
app.loadArchiveFromPath(newName)
|
|
return true
|
|
}
|
|
|
|
// TODO(fau): Distinguish a failiure from the "no previous archive" condition and inform the user accordingly
|
|
func (app *App) previousArchive() bool {
|
|
newName, err := app.archiveNameRelativeToCurrent(-1)
|
|
if err != nil {
|
|
log.Printf("Error getting previous archive: %v", err)
|
|
return false
|
|
}
|
|
|
|
app.loadArchiveFromPath(newName)
|
|
app.lastPage()
|
|
return true
|
|
}
|
|
|
|
// currentArchiveIdx determines the index of the current archive in the directory. We need to do
|
|
// this every time, since the filesystem is mutable
|
|
func (app *App) currentArchiveIdx() (idx int, err error) {
|
|
dir, name := filepath.Split(app.S.ArchivePath)
|
|
if dir == "" {
|
|
dir, err = os.Getwd()
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
arNames, err := archive.ListInDirectory(dir)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
idx = -1
|
|
for i := 0; i < len(arNames); i++ {
|
|
if arNames[i] == name {
|
|
idx = i
|
|
}
|
|
}
|
|
if idx == -1 {
|
|
return 0, errors.New("could not find the current archive in the current directory. Deleted, perhaps?")
|
|
}
|
|
return
|
|
}
|
|
|
|
// archiveNameRelativeToCurrent gets the name of the archive in the current directory whose
|
|
// relative position with regards to the current archive is equal to relIdx
|
|
// TODO(utkan): Use inotify to avoid obtaining list from the scratch all the time
|
|
func (app *App) archiveNameRelativeToCurrent(relIdx int) (newName string, err error) {
|
|
dir, _ := filepath.Split(app.S.ArchivePath)
|
|
if dir == "" {
|
|
dir, err = os.Getwd()
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
arNames, err := archive.ListInDirectory(dir)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
currIdx, err := app.currentArchiveIdx()
|
|
if err != nil {
|
|
return "", nil
|
|
}
|
|
|
|
idx := currIdx + relIdx
|
|
if idx < 0 || idx >= len(arNames) {
|
|
err = errors.New("no more archives in the directory")
|
|
return
|
|
}
|
|
|
|
newName = filepath.Join(dir, arNames[idx])
|
|
return
|
|
}
|