commit - 7da8ef9eb7516961a7acaedbe448cde8a6e66380
commit + 906e3938298d8ab91936cc7800fd7f0e8cfd7f52
blob - dd5eb25c031c1b4f1578aa089476ced1fc81d51a
blob + facdbe6734a9193f10c74f61f62ebae8fb2b0c59
--- README.md
+++ README.md
## Keys
```text
-m months w weeks l labels
-a artists s search r random play
+d dailies m months w weeks
+l labels a artists s search
+r random play
p play release q queue release
pa play buffer qa queue buffer
nn next page pp prev page b back
x exit
```
+Chronological views show the newest entries first. The dailies view shows one
+effective source per date after applying the daily, fallback, and session
+precedence rules. Days, weeks, and months with no counted releases are omitted.
+
Enter a row number to open it. Commands such as `:month 2026-08`,
`:day 2026-08-11`, `:label Warp`, `:artist Autechre`, `:release Label/Artist/Release`, and
`:search Autechre`
blob - 72778315b5c77ca53c9db9d0b0f203f3b95d0211
blob + b1b0d3dc2027753401c52f08c856ed8549983ffd
--- doc/mcol.1
+++ doc/mcol.1
-.TH MCOL 1 "2026-08-23" "mcol" "User Commands"
+.TH MCOL 1 "2026-08-26" "mcol" "User Commands"
.SH NAME
mcol \- browse an MPD release archive
.SH SYNOPSIS
Print usage.
.SH KEYS
.TP
+.B d
+Show effective daily sources, newest first. There is one row per date after
+applying the daily, fallback, and session precedence rules.
+.TP
.BR m ,
.BR w ,
.BR l ,
.B a
Show months, weeks, labels, or artists.
+.PP
+Chronological views and addition-date lists are shown newest first. Releases
+within a label or artist view are ordered by their latest known daily addition;
+entries known only from weekly sources follow dated releases.
+Days, weeks, and months with no counted release additions are omitted.
.TP
.B s
Search artists, labels, releases, and paths.
blob - 48c89f1ba7d2310515cb27fa3f798c06fe4257fd
blob + 39864a15674a715bec0f17379fb8d6e7f5781c4b
--- src/mcol/core.sls
+++ src/mcol/core.sls
(vector->list (hashtable-keys table)))
(define (sorted-keys table)
(sort string<? (table-keys table)))
+ (define (reverse-sorted-keys table)
+ (sort string>? (table-keys table)))
(define (unique-entries entries)
(let ([seen (make-string-table)])
(filter (lambda (item)
(sources-of-kind database 'daily))
table))
(define (canonical-days canonical)
- (map (lambda (date) (file-summary (hashtable-ref canonical date #f))) (sorted-keys canonical)))
+ (filter (lambda (day) (> (field day 'additions) 0))
+ (map (lambda (date) (file-summary (hashtable-ref canonical date #f)))
+ (reverse-sorted-keys canonical))))
(define (dated-catalog canonical database)
(append (fold-left (lambda (entries date)
(append entries
'observedDays)
(max 1 expected))))
10.0))))))
- (sorted-keys table))))
+ (reverse-sorted-keys table))))
(define (label-summary canonical)
(let ([counts (make-string-table)])
(for-each
(map (lambda (key) (object (cons 'name key) (cons 'count (hashtable-ref counts key 0))))
(table-keys counts)))))
(define (weekly-summary database days)
- (map (lambda (item)
- (let* ([row (file-summary item)]
- [daily-total (fold-left (lambda (sum day)
- (if (and (string<=? (source-period-start item)
- (field day
- 'date))
- (string<=? (field day
- 'date)
- (source-period-end item)))
- (+ sum
- (field day
- 'additions))
- sum))
- 0
- days)])
- (append row (object (cons 'dailyAdditions daily-total)))))
- (sources-of-kind database 'weekly)))
+ (filter (lambda (week) (> (field week 'additions) 0))
+ (sort (lambda (left right)
+ (string>? (field left 'periodStart) (field right 'periodStart)))
+ (map (lambda (item)
+ (let* ([row (file-summary item)]
+ [daily-total (fold-left (lambda (sum day)
+ (if (and (string<=? (source-period-start item)
+ (field day
+ 'date))
+ (string<=? (field day
+ 'date)
+ (source-period-end item)))
+ (+ sum
+ (field day
+ 'additions))
+ sum))
+ 0
+ days)])
+ (append row (object (cons 'dailyAdditions daily-total)))))
+ (sources-of-kind database 'weekly)))))
(define (summarize database)
(let* ([canonical (canonical-sources database)]
[days (canonical-days canonical)]
blob - 9a40b996cbc13bbaaaee79c3852e729732b11e17
blob + 00d8a6fcb98ec3f6a3e827342da62bfc5bd52fcc
--- src/mcol/tui.sls
+++ src/mcol/tui.sls
(define (dates-for path)
(let ([dates (hashtable-ref dates-by-path path #f)])
(if dates
- (sort string<? (vector->list (hashtable-keys dates)))
+ (sort string>? (vector->list (hashtable-keys dates)))
'())))
+ (define (items-newest-first items)
+ (map cdr
+ (sort (lambda (left right)
+ (let ([left-date (car left)]
+ [right-date (car right)]
+ [left-item (cdr left)]
+ [right-item (cdr right)])
+ (cond
+ [(and left-date right-date)
+ (or (string>? left-date right-date)
+ (and (string=? left-date right-date)
+ (string-ci<? (field left-item 'path)
+ (field right-item 'path))))]
+ [left-date #t]
+ [right-date #f]
+ [else (string-ci<? (field left-item 'path)
+ (field right-item 'path))])))
+ (map (lambda (item)
+ (let ([dates (dates-for (field item 'path))])
+ (cons (and (pair? dates) (car dates)) item)))
+ items))))
(define (find-day date)
(find (lambda (day)
(string=? date
'date)))))
month-days))))
+ (define (view-dailies)
+ (values (list (format "~a effective daily sources" (length days)))
+ (map (lambda (day)
+ (link (format "~a ~5a additions ~a"
+ (field day
+ 'date)
+ (field day
+ 'additions)
+ (field day
+ 'source))
+ (lambda ()
+ (open 'day
+ (field day
+ 'date)))))
+ days)))
+
(define (view-day)
(let ([day (find-day value)])
(if (not day)
(equal? value
(field item
'label))))]
- [items (unique-by-path (append (filter matches? catalog) (filter matches? events)))]
+ [items (items-newest-first
+ (unique-by-path (append (filter matches? catalog)
+ (filter matches? events))))]
[artists (unique-strings (map (lambda (item)
(field item
'artist))
visible))))
(define (view-artist)
- (let ([items (unique-by-path (filter (lambda (item)
- (equal? value
- (field item
- 'artist)))
- (append catalog events)))])
+ (let ([items (items-newest-first
+ (unique-by-path (filter (lambda (item)
+ (equal? value
+ (field item
+ 'artist)))
+ (append catalog events))))])
(values (list (format "~a releases" (length items)))
(map (lambda (item)
(release-link (item-line item
(define (view-help)
(values (list "number open row nn/pp next/prev page"
- "b back m months"
+ "b back m months d dailies"
"w weeks l labels"
"a artists s search r random play"
"p [N...] play release(s) q [N...] queue release(s)"
(case screen
[(months) (view-months)]
[(month) (view-month)]
+ [(dailies) (view-dailies)]
[(day) (view-day)]
[(weeks) (view-weeks)]
[(week) (view-week)]
(display (muted " release database // all sources"))
(newline)
(display
- (muted "[m]onths [w]eeks [l]abels [a]rtists [s]earch [p]lay [q]ueue [r]random [?]help [x]exit"))
+ (muted "[m]onths [d]ailies [w]eeks [l]abels [a]rtists [s]earch [p]lay [q]ueue [r]random [?]help [x]exit"))
(newline)
(rule)
(display (muted (string-append "~/" (crumbs))))
[(string=? input "?") (open 'help)]
[(string=? input "b") (back)]
[(string=? input "m") (root 'months)]
+ [(string=? input "d") (root 'dailies)]
[(string=? input "w") (root 'weeks)]
[(string=? input "l") (root 'labels)]
[(string=? input "a") (root 'artists)]
blob - 6cf5db2f0f80ac83a7a8d40781efa3c9ed6a0393
blob + c7d3e10f7d41880ae6f0249229d9b36d3cfd8257
--- test/test.ss
+++ test/test.ss
(write-lines root "2026/may/dailies/2026-05-20.txt" '("Future/Album"))
(write-lines root "2026/dailies/stdin_playlist_20260521_120000.txt" '("Session/One"))
(write-lines root "2026/dailies/stdin_playlist_20260521_130000.txt" '("Session/Two"))
+ (write-lines root "2026/may/dailies/2026-05-22.txt" '(""))
+ (write-lines root "2026/june/dailies/2026-06-01.txt" '(""))
+ (write-lines root "2026/may/2_05_2026.txt" '(""))
(write-lines root "2026/may/not-a-week.txt" '("Malformed/Source"))
(let* ([database (scan-database root)]
[report (summarize database)]
(field current
'single))
"no undated catalog-only records")
- (check (= 3 (length days)) "all dated sources scanned")
+ (check (= 3 (length days)) "empty daily sources omitted")
+ (check (= 1 (length (field report 'weeks))) "empty weekly sources omitted")
+ (check (equal? (map (lambda (month) (field month 'period))
+ (field report 'months))
+ '("2026-05"))
+ "empty months omitted")
+ (check (equal? (map (lambda (day) (field day 'date)) days)
+ '("2026-05-21" "2026-05-20" "2026-05-02"))
+ "days newest first")
(check (string=? "2026-05-20"
(field (cadr days)
'date))
"later date retained")
(check (= 2
- (field (car days)
+ (field (caddr days)
'additions))
"parents excluded")
- (check (string=? (field (car days)
+ (check (string=? (field (caddr days)
'source)
"2026/may/dailies/2026-05-02.txt")
"month-local source precedence")
(check (= 2
- (length (field (car days)
+ (length (field (caddr days)
'items)))
"day exposes selectable releases")
(check (= 5
'catalog)))
"catalog is derived from dated records")
(check (= 2
- (field (caddr days)
+ (field (car days)
'additions))
"same-day session sources merged")
(check (string=? "2 session playlists"
- (field (caddr days)
+ (field (car days)
'source))
"session fallback source shown"))
(check (guard (condition (else #t)) (scan-database root #t) #f)
(check (= 6 (length (field report 'days))) "demo days")
(check (= 3 (length (field report 'months))) "demo months")
(check (= 2 (length (field report 'weeks))) "demo weeks")
- (check (= 9 (length (field report 'labels))) "demo labels")))
+ (check (= 9 (length (field report 'labels))) "demo labels")
+ (check (equal? (map (lambda (day) (field day 'date)) (field report 'days))
+ '("2026-08-21" "2026-08-20" "2026-08-19"
+ "2026-08-18" "2025-08-12" "2024-12-05"))
+ "demo days newest first")
+ (check (equal? (map (lambda (month) (field month 'period)) (field report 'months))
+ '("2026-08" "2025-08" "2024-12"))
+ "demo months newest first")
+ (check (equal? (map (lambda (week) (field week 'periodStart)) (field report 'weeks))
+ '("2026-08-01" "2025-08-01"))
+ "demo weeks newest first")))
(define (tui-test)
;; remove-at removes a single element at the given index