|
|
|
@ -19,7 +19,6 @@ limitations under the License.
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"context"
|
|
|
|
|
"errors"
|
|
|
|
|
"flag"
|
|
|
|
@ -34,7 +33,6 @@ import (
|
|
|
|
|
"sync"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/chromedp/cdproto/cdp"
|
|
|
|
|
"github.com/chromedp/cdproto/input"
|
|
|
|
|
"github.com/chromedp/cdproto/page"
|
|
|
|
|
"github.com/chromedp/chromedp"
|
|
|
|
@ -263,90 +261,61 @@ func (s *Session) firstNav(ctx context.Context) error {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := navToLast(ctx); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// navToEnd waits for the page to be ready to receive scroll key events, by
|
|
|
|
|
// trying to select an item with the right arrow key, and then scrolls down to the
|
|
|
|
|
// end of the page, i.e. to the oldest items.
|
|
|
|
|
// navToEnd selects the last item in the page
|
|
|
|
|
// by repeatedly advancing the selected item with
|
|
|
|
|
// - kb.ArrowRight (which causes an initial selection, and/or advances it by one)
|
|
|
|
|
// - kb.End which scrolls to the end of the page, and advances the selected item.
|
|
|
|
|
// Note timing is important, because when the kb.End causes significant scrolling,
|
|
|
|
|
// the active element become undefined for a certain time, in that case, we
|
|
|
|
|
// get an error (ignore), sleep, and retry.
|
|
|
|
|
// The termnation criteria is that the selected item (document.activeElement.href)
|
|
|
|
|
// is stable for >2 iterations
|
|
|
|
|
func navToEnd(ctx context.Context) error {
|
|
|
|
|
// wait for page to be loaded, i.e. that we can make an element active by using
|
|
|
|
|
// the right arrow key.
|
|
|
|
|
var prev, active string
|
|
|
|
|
lastRepeated := 0
|
|
|
|
|
for {
|
|
|
|
|
chromedp.KeyEvent(kb.ArrowRight).Do(ctx)
|
|
|
|
|
chromedp.KeyEvent(kb.End).Do(ctx)
|
|
|
|
|
time.Sleep(tick)
|
|
|
|
|
var ids []cdp.NodeID
|
|
|
|
|
if err := chromedp.Run(ctx,
|
|
|
|
|
chromedp.NodeIDs(`document.activeElement`, &ids, chromedp.ByJSPath)); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
|
|
if err := chromedp.Evaluate(`document.activeElement.href`, &active).Do(ctx); err != nil {
|
|
|
|
|
// This extra sleep is important: after the kb.End,
|
|
|
|
|
// it sometimes takes a while for the scrolled page to be in a state
|
|
|
|
|
// which allows the next kb.ArrowRight to take effect and actually select
|
|
|
|
|
// the next element at the new scroll position.
|
|
|
|
|
time.Sleep(tick)
|
|
|
|
|
continue // ignore this error: no active element, or active element has no href
|
|
|
|
|
}
|
|
|
|
|
if len(ids) > 0 {
|
|
|
|
|
if *verboseFlag {
|
|
|
|
|
log.Printf("We are ready, because element %v is selected", ids[0])
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
if active == prev {
|
|
|
|
|
lastRepeated++
|
|
|
|
|
} else {
|
|
|
|
|
lastRepeated = 0
|
|
|
|
|
}
|
|
|
|
|
time.Sleep(tick)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// try jumping to the end of the page. detect we are there and have stopped
|
|
|
|
|
// moving when two consecutive screenshots are identical.
|
|
|
|
|
var previousScr, scr []byte
|
|
|
|
|
for {
|
|
|
|
|
chromedp.KeyEvent(kb.PageDown).Do(ctx)
|
|
|
|
|
chromedp.KeyEvent(kb.End).Do(ctx)
|
|
|
|
|
chromedp.CaptureScreenshot(&scr).Do(ctx)
|
|
|
|
|
if previousScr == nil {
|
|
|
|
|
previousScr = scr
|
|
|
|
|
continue
|
|
|
|
|
if *verboseFlag {
|
|
|
|
|
log.Printf("Active element %s was seen %d times", active, lastRepeated+1)
|
|
|
|
|
}
|
|
|
|
|
if bytes.Equal(previousScr, scr) {
|
|
|
|
|
if lastRepeated > 2 {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
previousScr = scr
|
|
|
|
|
time.Sleep(tick)
|
|
|
|
|
prev = active
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if *verboseFlag {
|
|
|
|
|
log.Printf("Successfully jumped to the end")
|
|
|
|
|
chromedp.KeyEvent("\n").Do(ctx)
|
|
|
|
|
time.Sleep(tick)
|
|
|
|
|
var location string
|
|
|
|
|
if err := chromedp.Location(&location).Do(ctx); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// navToLast sends the "\n" event until we detect that an item is loaded as a
|
|
|
|
|
// new page. It then sends the right arrow key event until we've reached the very
|
|
|
|
|
// last item.
|
|
|
|
|
func navToLast(ctx context.Context) error {
|
|
|
|
|
var location, prevLocation string
|
|
|
|
|
ready := false
|
|
|
|
|
for {
|
|
|
|
|
chromedp.KeyEvent(kb.ArrowRight).Do(ctx)
|
|
|
|
|
time.Sleep(tick)
|
|
|
|
|
if !ready {
|
|
|
|
|
chromedp.KeyEvent("\n").Do(ctx)
|
|
|
|
|
time.Sleep(tick)
|
|
|
|
|
}
|
|
|
|
|
if err := chromedp.Location(&location).Do(ctx); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if !ready {
|
|
|
|
|
if location != "https://photos.google.com/" {
|
|
|
|
|
ready = true
|
|
|
|
|
log.Printf("Nav to the end sequence is started because location is %v", location)
|
|
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if location == prevLocation {
|
|
|
|
|
break
|
|
|
|
|
if active == location {
|
|
|
|
|
if *verboseFlag {
|
|
|
|
|
log.Printf("Successfully jumped to the end: %s", location)
|
|
|
|
|
}
|
|
|
|
|
prevLocation = location
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -579,6 +548,9 @@ func (s *Session) navN(N int) func(context.Context) error {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if location == prevLocation {
|
|
|
|
|
if *verboseFlag {
|
|
|
|
|
log.Printf("Terminating because we stopped advancing: %s", prevLocation)
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
prevLocation = location
|
|
|
|
@ -591,6 +563,9 @@ func (s *Session) navN(N int) func(context.Context) error {
|
|
|
|
|
}
|
|
|
|
|
n++
|
|
|
|
|
if N > 0 && n >= N {
|
|
|
|
|
if *verboseFlag {
|
|
|
|
|
log.Printf("Terminating because desired number of items (%d) was reached", n)
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|