diff --git a/main.go b/main.go
index 429ea2a..d28f64e 100644
--- a/main.go
+++ b/main.go
@@ -278,67 +278,44 @@ func (s *Session) firstNav(ctx context.Context) error {
return nil
}
-// navToEnd waits for the page to be ready to receive scroll key events,
-// 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 advances 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
- chromedp.WaitReady("body", chromedp.ByQuery).Do(ctx)
-
- // Try scrolling to the end of the page.
- // After each scroll attempt we extract the last Photo Page Element (href) from the DOM.
- // We detect we are at the end when two consecutive DOM extractions are identical.
- var previousHref string
+ var prev, active string
+ lastRepeated := 0
for {
- chromedp.KeyEvent(kb.PageDown).Do(ctx)
+ chromedp.KeyEvent(kb.ArrowRight).Do(ctx)
chromedp.KeyEvent(kb.End).Do(ctx)
- time.Sleep(tick) // sleep *before* we establish new state in DOM
+ time.Sleep(tick)
- // Extract last Photo Page Element (href) from DOM.
- lastHrefInDOM, err := lastPhotoInDOM(ctx)
- if err != nil {
- continue // Just ignore this error, continue will retry.
+ if err := chromedp.Evaluate(`document.activeElement.href`, &active).Do(ctx); err != nil {
+ time.Sleep(tick) // this extra sleep is important: after the kb.End, it sometimes takes a while for the active element to be reset
+ continue // ignore this error: no active element, or active element has no href
+ }
+ if active == prev {
+ lastRepeated++
+ } else {
+ lastRepeated = 0
}
- if previousHref == lastHrefInDOM {
+ if *verboseFlag {
+ log.Printf("** navToEnd:activeElt %s %d", active, lastRepeated)
+ }
+ if lastRepeated > 2 {
break
}
- previousHref = lastHrefInDOM
- }
-
- // Now that we have stopped scrolling, select (focus) on the last element
- // The element must be focused, so that navToLast can send "\n" to enter photo detail page
- lastEltSel := fmt.Sprintf(`a[href="%s"]`, previousHref)
- if err := chromedp.Focus(lastEltSel).Do(ctx); err != nil {
- return err
+ prev = active
+ // time.Sleep(tick)
}
-
if *verboseFlag {
- log.Printf("Successfully jumped to the end: %s", previousHref)
+ log.Printf("Successfully jumped to the end: %s", active)
}
- return nil
-}
-// When in the Main/Album Page, the DOM contains elements for all visible images.
-// lastPhotoInDOM simply returns the last such href in document order.
-// The DOM actually contains more images than those that are visible, in a kind of virtual scrolling window
-// In the DOM, but not reflecting exactly the visible photos (actually a superset of the visible elements):
-//
-//
-//
-// We tried to find the actual *oldest* photo by using the aria-label attribute which contains a date for the photo,
-// unfortunately that label is localised for each user's language which makes the date format very hard to parse.
-func lastPhotoInDOM(ctx context.Context) (string, error) {
- sel := `a[href^="./photo/"]` // css selector for all links to images with href prefix "./photo/..."
- var attrs []map[string]string
- if err := chromedp.AttributesAll(sel, &attrs).Do(ctx); err != nil {
- return "", err
- }
- if len(attrs) == 0 {
- return "", fmt.Errorf("lastPhotoInDOM: no elements match")
- }
-
- lastElement := attrs[len(attrs)-1]
- href := lastElement["href"]
- return href, nil
+ return nil
}
// navToLast sends the "\n" event until we detect that an item is loaded as a