2024-04-14 21:43:35 -04:00
|
|
|
(ql:quickload '(:cl-yaml :alexandria :cl-ppcre))
|
2024-04-14 21:21:53 -04:00
|
|
|
(defpackage :hsmusic
|
2024-04-14 21:43:35 -04:00
|
|
|
(:use :common-lisp :alexandria))
|
2024-04-14 21:21:53 -04:00
|
|
|
(in-package :hsmusic)
|
|
|
|
|
|
|
|
(defparameter tracks
|
2024-04-14 23:05:48 -04:00
|
|
|
(loop for path in (directory #p"hsmusic-data/album/*.yaml")
|
2024-04-14 21:21:53 -04:00
|
|
|
appending (loop for entry in (yaml:parse path :multi-document-p t)
|
|
|
|
when (and (not (symbolp entry)) (gethash "Track" entry))
|
|
|
|
collect entry)))
|
|
|
|
|
|
|
|
; terrible, idc
|
|
|
|
; lossy! (not surprising)
|
2024-04-14 23:05:48 -04:00
|
|
|
; upcase for nice interning
|
2024-04-15 00:23:22 -04:00
|
|
|
(defun track->href (track)
|
|
|
|
(or (gethash "Directory" track)
|
|
|
|
(let ((r (copy-array (gethash "Track" track))))
|
|
|
|
(setf r (ppcre:regex-replace-all " " r "-"))
|
|
|
|
(setf r (ppcre:regex-replace-all "&" r "and"))
|
|
|
|
(setf r (ppcre:regex-replace-all "[^a-zA-Z0-9-]" r ""))
|
|
|
|
(setf r (ppcre:regex-replace-all "-{2,}" r ""))
|
|
|
|
(setf r (ppcre:regex-replace-all "^-+|-+$" r ""))
|
|
|
|
(nstring-downcase r))))
|
2024-04-14 21:21:53 -04:00
|
|
|
|
2024-04-15 00:23:22 -04:00
|
|
|
(defun track->url (track)
|
|
|
|
(format nil "https://hsmusic.wiki/track/~a" (track->href track)))
|
2024-04-14 23:05:48 -04:00
|
|
|
|
2024-04-15 00:23:22 -04:00
|
|
|
; let's just store the href because that's unambiguous and links are desirable anyway
|
2024-04-14 23:05:48 -04:00
|
|
|
|
2024-04-15 00:23:22 -04:00
|
|
|
; string name -> href
|
|
|
|
; so we don't have to research over and over
|
|
|
|
; there's ambiguity here! so consult only when not given a track:!
|
|
|
|
(defparameter hrefs (make-hash-table :test 'equal))
|
|
|
|
|
|
|
|
(loop for track in tracks
|
|
|
|
do (setf (gethash (gethash "Track" track) hrefs) (track->href track)))
|
2024-04-14 21:21:53 -04:00
|
|
|
|
2024-04-14 23:05:48 -04:00
|
|
|
; a -> b <-> a references b
|
|
|
|
; screw it! let's use symbols. why not? (LKFDSJKFDJ)
|
|
|
|
; it's a finite set it's FINE
|
|
|
|
(defparameter -> (make-hash-table))
|
|
|
|
(defparameter <- (make-hash-table))
|
2024-04-14 21:21:53 -04:00
|
|
|
|
2024-04-14 21:43:35 -04:00
|
|
|
(loop for track in tracks
|
2024-04-14 23:05:48 -04:00
|
|
|
do (let ((from-name (intern (track-normalized-name track) :keyword)))
|
2024-04-14 21:43:35 -04:00
|
|
|
(loop for ref in (append (gethash "Referenced Tracks" track) (gethash "Sampled Tracks" track))
|
2024-04-14 23:05:48 -04:00
|
|
|
do (let ((to-name (intern (normalize-name ref) :keyword)))
|
|
|
|
; bodge
|
|
|
|
(unless (eq from-name to-name)
|
|
|
|
(unionf (gethash from-name ->) (list to-name))
|
|
|
|
(unionf (gethash to-name <-) (list from-name)))))))
|
|
|
|
|
|
|
|
(defun tree (src from depth)
|
|
|
|
(let ((succs (gethash from src)))
|
|
|
|
(if (and (plusp depth) (consp succs))
|
|
|
|
(list from (mapcar (lambda (x) (tree src x (- depth 1))) succs))
|
|
|
|
from)))
|
|
|
|
|
|
|
|
(defun ->tree (from depth)
|
|
|
|
(tree -> from depth))
|
|
|
|
|
|
|
|
(defun <-tree (from depth)
|
|
|
|
(tree <- from depth))
|
|
|
|
|
|
|
|
(defun pp-tree (tree &key (stream t) (indent 0) (indent-increment 2))
|
|
|
|
(let ((ind (make-string indent :initial-element #\ )))
|
|
|
|
(if (symbolp tree)
|
|
|
|
(format stream "~a~a~%" ind tree)
|
|
|
|
(destructuring-bind (from tos) tree
|
|
|
|
(format stream "~a~a~%" ind from)
|
|
|
|
(loop for to in tos
|
|
|
|
do (pp-tree to :stream stream
|
|
|
|
:indent (+ indent indent-increment)
|
|
|
|
:indent-increment indent-increment))))))
|