commit af10d7ad17d0d2e6d7d64a9d6110fa6afda269e3
Author: Jeff <dev@watertao.xyz>
Date: Sun, 24 Mar 2024 22:08:32 -0700
Initial commit
Diffstat:
8 files changed, 965 insertions(+), 0 deletions(-)
diff --git a/README b/README
@@ -0,0 +1,67 @@
+mpdhh - mpd history html
+
+mpc's current track from mpd, generates playlist, manages history and
+analyses. Static html file generation. With every track change, db files
+are modified or generated if need be, and html is rendered.
+
+By default the html is rendered as hugo content files. This can be
+modified by editing the *tmpl.md files.
+
+Requirements:
+mpd
+mpc
+posix compliant shell tools: awk/sed/grep/sort
+
+hugo or full html markup needed
+
+note: watertao.xyz is hugo project bin. Doesn't have to be hugo.
+Can be whatever.
+
+This program assumes the following tree:
+(run mpdhh.sh 4 to create the tree)
+
+project/
+----content/
+-------- music/
+------------ _index.[html|md]
+------------ analysis/
+---------------- _index.[html|md]
+---------------- tracks/
+-------------------- _index.[html/md]
+-------------------- tracks.db
+---------------- artists/
+-------------------- _index.[html|md]
+-------------------- artists.db
+------------ db/
+---------------- [date].db
+------------ historia/
+---------------- _index.[html|md]
+---------------- db.index
+---------------- [date_bin]/
+-------------------- _index.[html|md]
+
+Important note on hugo
+This has been designed for portability thus it does not need hugo. As
+such, I've had to keep a menu bit out of this program. If using hugo,
+you'll need the following menus injected in the hugo config for
+"Analysis" menus to work:
+
+menus:
+ analysis:
+ - name: Artists
+ pageRef: /music/analysis/artists
+ - name: Tracks
+ pageRef: /music/analysis/tracks
+ artists:
+ - name: Artist
+ pageRef: /music/analysis/artists/by-artist
+ - name: Play Count
+ pageRef: /music/analysis/artists/by-play-count
+ tracks:
+ - name: Artist
+ pageRef: /music/analysis/tracks/by-artist
+ - name: Track
+ pageRef: /music/analysis/tracks/by-track
+ - name: Play Count
+ pageRef: /music/analysis/tracks/by-play-count
+
diff --git a/a_filter_track_data.awk b/a_filter_track_data.awk
@@ -0,0 +1,49 @@
+#!/bin/awk
+
+# Sanitize Field
+function sf(s) {
+
+ p="";r=""
+
+ # analyze chars, create whitepace
+ # as needed to break long words
+ # apart
+ while (s){
+ c=substr(s,1,1)
+
+ # Space after case change
+ if (p ~ /[a-z]/){
+ if (c ~ /[A-Z]/){
+ r = r " "
+ }
+
+ # Space after colons
+ }else if (p == ":") {
+ if (c ~ /[a-zA-Z0-9]/){
+ r = r " "
+ }
+ }
+ r = r c
+ s=substr(s,2)
+ p=c
+ }
+ gsub( /[_.#]+/, " ",r) # rm underscores
+ gsub(/[-/////\\]+/," & ",r) # pad [-/] with space
+ gsub(/[ ]{2,}/," ",r) # squeeze spaces
+ gsub(/^ - $/,"-",r) # remove spaces from empty fields
+ return r
+}
+{
+ # If metadata is emtpy, set track field to filename
+ if ($0 == "\t\t") {
+ $1 = filename
+ }
+
+ # If value is empty, replace with dash
+ for (i=1; i<=NF; i++){
+ gsub(/^$/, d, $i)
+ }
+
+ OFS="\t"
+ print played_date, sf($1), sf($2), sf($3)
+}
diff --git a/a_write_html.awk b/a_write_html.awk
@@ -0,0 +1,144 @@
+#!/bin/awk
+
+# Add slash escapes to protect hugo
+# front matter yaml values from awk
+# Ampersands need to be escaped, else
+# gsub substitutes amp with matched term
+function hf(s){
+ gsub(/[&]/,"\\\\&",s)
+ return s
+}
+function df(s){
+ gsub (/[-T]/," ",$s)
+ $s = substr($s, 1, 16)
+}
+
+BEGIN {
+ gsub(/[ ,]/, "", page_menuidentifier)
+ page_menu_id = tolower(page_menuidentifier)
+
+ if (html_type == "playlist") {
+
+ if (! track_data) {
+ page_header=page_summary=page_summary_title= "Nothing Recently Played"
+ syndicate = 1
+ }
+ else if (today_day == played_day) {
+
+ page_header = page_header_html played_day_h
+
+ # Sanitize tab delimited track data for
+ # rss xml title and description tags
+ split(hf(track_data), a, "\t")
+ track_data = sprintf("%s<br><br>Track: %s<br>Album: %s<br>Artist: %s",
+ a[1], a[2], a[3], a[4])
+
+ page_summary = "Most recently played:<br><br>" track_data
+ page_summary_title = sprintf("Recently Played %s > %s, %s, %s",
+ $a[1], a[2], a[3], a[4])
+ syndicate = 1
+
+ }else{
+ # Page header and rss tags for historia pages
+ # Many nulled vars here to force default rss behavior
+ # that is otherwise specialized for music's
+ # main currently playing playlist page
+ page_summary = sprintf("%s playlist has been archived to Sonus Historia.", played_day_h)
+ page_summary_title = ""
+ page_header = ""
+ syndicate = 0
+ description = ""
+ rss_title = ""
+ rss_data_file = ""
+ }
+
+ } else if ( html_type == "analysis" ) {
+ page_summary_title = ""
+ page_summary = ""
+ }
+
+ # Use a default for music playlist tables
+ if ( !th_values ) { th_values = "Time::Title::Album::Artist" }
+ if ( !pfields ) { pfields = "1,2,3,4" }
+
+ # Now split into arrays
+ split( th_values, th_values_a, "::" )
+ pfields_a_l = split( pfields, pfields_a, "," )
+}
+{
+ # Track file inputs w/ x
+ if ( FNR == 1) { x++ }
+
+ # First input file, hugo archetype, sub values
+ if ( x==1 ) {
+ gsub(/%%header%%/, page_header)
+ gsub(/%%menu%%/, page_menu)
+ gsub(/%%menutitle%%/, page_menutitle)
+ gsub(/%%title%%/, page_title)
+ gsub(/%%date%%/, today_date)
+ gsub(/%%day%%/, played_day_h)
+ gsub(/%%menuidentifier%%/, page_menuidentifier)
+ gsub(/%%pagemenuid%%/, page_menu_id)
+ gsub(/%%pagesummary%%/, page_summary)
+ gsub(/%%pagesummarytitle%%/, page_summary_title)
+ gsub(/%%syndicate%%/, syndicate)
+ gsub(/%%rssdata%%/, rss_data_file)
+ gsub(/%%description%%/, description)
+ gsub(/%%rsstitle%%/, rss_title)
+ gsub(/%%tally%%/, tally)
+
+ # Check for foot of body (begins after opening tr tag)
+ # Then store lines from rest of archetype file to print
+ # after data file parsing.
+ if ( is_foot == 1 ) {
+
+ # store rest in html_footer
+ html_footer = html_footer $0 ORS
+ next
+ } else {
+ if ( /%%table%%/ ) {
+ is_foot = 1
+ next
+ }
+ }
+ print
+ }
+
+ # Now db data (input file 2)
+ # Fairly self explanitory
+ if ( x==2 ){
+ if ( FNR == 1 ) {
+ printf("<table id=\"%s\">\n", html_type)
+ printf("<tr>")
+ #<tr><th>+</th><th>Title</th><th>Album</th><th>Artist</th></tr>
+ for (i=1;i<=pfields_a_l;i++){
+ printf("<th>%s</th>", th_values_a[pfields_a[i]])
+ }
+ printf("</tr>\n")
+ printf("<tr class=\"spacer\"><td colspan=\"%s\"> </td></tr>\n", pfields_a_l)
+
+ }
+
+ # gsub(/[0-9]{4}-[0-9]{2}-[0-9]{2}T([0-9]{2}:[0-9]{2}).*/,"&",$1)
+ printf("<tr>")
+
+ # extract time from date in playlist db
+ if (html_type == "playlist") { $1 = substr($1, 12, 5) }
+
+ for(i=1;i<=pfields_a_l;i++){
+
+ # Format date for analysis sections
+ if (page_menu_id == "tracks") { if (pfields_a[i] == 4 || pfields_a[i] == 5) { df( pfields_a[i] ) }}
+ if (page_menu_id == "artists") { if (pfields_a[i] == 2 || pfields_a[i] == 3) { df( pfields_a[i] ) }}
+
+ printf("<td>%s</td>", $pfields_a[i])
+ }
+
+ printf("</tr>\n")
+ }
+}
+END{
+ # print body footer from archetype (input file 1)
+ printf("</table>\n")
+ print html_footer
+}
diff --git a/h_analysis_artists_tmpl.md b/h_analysis_artists_tmpl.md
@@ -0,0 +1,13 @@
+---
+layout: single
+title: %%title%%
+description: %%description%%
+pagemenuid: %%pagemenuid%%
+date: %%date%%
+draft: false
+
+---
+<p>Tally: %%tally%%</p>
+
+%%table%%
+
diff --git a/h_analysis_tracks_tmpl.md b/h_analysis_tracks_tmpl.md
@@ -0,0 +1,13 @@
+---
+layout: single
+title: %%title%%
+description: %%description%%
+pagemenuid: %%pagemenuid%%
+date: %%date%%
+draft: false
+
+---
+<p>Tally: %%tally%%</p>
+
+%%table%%
+
diff --git a/h_playlist_empty_tmpl.md b/h_playlist_empty_tmpl.md
@@ -0,0 +1,20 @@
+---
+layout: single
+title: %%title%%
+description: %%description%%
+syndicate: %%syndicate%%
+rss_data: %%rssdata%%
+rss_title: %%rsstitle%%
+summary: >
+ %%pagesummary%%
+summarytitle: >-
+ %%pagesummarytitle%%
+pagemenuid: %%pagemenuid%%
+date: %%date%%
+draft: false
+menus:
+ %%menu%%:
+ identifier: %%menuidentifier%%
+ name: %%menutitle%%
+---
+<h2>%%header%%</h2>
diff --git a/h_playlist_tmpl.md b/h_playlist_tmpl.md
@@ -0,0 +1,23 @@
+---
+layout: single
+title: %%title%%
+description: %%description%%
+syndicate: %%syndicate%%
+rss_data: %%rssdata%%
+rss_title: %%rsstitle%%
+summary: >
+ %%pagesummary%%
+summarytitle: >-
+ %%pagesummarytitle%%
+pagemenuid: %%pagemenuid%%
+date: %%date%%
+draft: false
+menus:
+ %%menu%%:
+ identifier: %%menuidentifier%%
+ name: %%menutitle%%
+---
+<h2>%%header%%</h2>
+
+%%table%%
+
diff --git a/mpdhh.sh b/mpdhh.sh
@@ -0,0 +1,636 @@
+#!/bin/sh
+
+# Important customization:
+mpdhh_bin=~/progs/mpdhh
+hugo_proj="watertao.xyz"
+work_bin="${HOME}/w/proj"
+publish_sh="publish.sh"
+
+# All else, modify to your liking
+artists="artists"
+tracks="tracks"
+index_md_name="_index.html"
+music_bin_proj_local="content/music"
+music_bin="${work_bin}/${hugo_proj}/${music_bin_proj_local}"
+db_bin="${music_bin}/db"
+historia_bin="${music_bin}/historia"
+db_index_file="${historia_bin}/db.index"
+rss_index_name="rss.index"
+rss_index="${music_bin}/${rss_index_name}"
+
+analysis_bin="${music_bin}/analysis"
+analysis_artists_bin="${analysis_bin}/${artists}"
+analysis_tracks_bin="${analysis_bin}/${tracks}"
+analysis_tracks_db="${analysis_tracks_bin}/${tracks}.db"
+analysis_artists_db="${analysis_artists_bin}/${artists}.db"
+analysis_tracks_html="${analysis_tracks_bin}/${index_md_name}"
+analysis_artists_html="${analysis_artists_bin}/${index_md_name}"
+analysis_tracks_byartist_html="${analysis_tracks_bin}/by-artist/${index_md_name}"
+analysis_tracks_bytrack_html="${analysis_tracks_bin}/by-track/${index_md_name}"
+analysis_tracks_bycount_html="${analysis_tracks_bin}/by-play-count/${index_md_name}"
+analysis_tracks_byinitial_html="${analysis_tracks_bin}/by-initial/${index_md_name}"
+analysis_tracks_byrecent_html="${analysis_tracks_bin}/by-recent/${index_md_name}"
+analysis_artists_byartist_html="${analysis_artists_bin}/by-artist/${index_md_name}"
+analysis_artists_bycount_html="${analysis_artists_bin}/by-play-count/${index_md_name}"
+analysis_artists_byinitial_html="${analysis_artists_bin}/by-initial/${index_md_name}"
+analysis_artists_byrecent_html="${analysis_artists_bin}/by-recent/${index_md_name}"
+th_playlist="Time::Title::Album::Artist"
+th_analysis_tracks="Title::Album::Artist::Initial::Recent::+"
+th_analysis_artists="Artist::Initial::Recent::+"
+analysis_byinitial_title="By Initial"
+analysis_byrecent_title="By Recent"
+analysis_byartist_title="By Artist"
+analysis_bytrack_title="By Track"
+analysis_bycount_title="By Play Count"
+analysis_tracks_title="Tracks"
+analysis_artists_title="Artists"
+
+commit_msg="dojo music update"
+music_description="The sweet sounds from my pinephone"
+rss_title="Sonus"
+
+# Set rss_data_file to data file to force hugo to parse data file for rss stream
+# for currently playing playlist page. This is unset in awk for historia page creation
+rss_data_file="${music_bin_proj_local}/${rss_index_name}"
+
+awk_prog_1="${mpdhh_bin}/a_write_html.awk"
+awk_prog_2="${mpdhh_bin}/a_filter_track_data.awk"
+playlist_html="${mpdhh_bin}/h_playlist_tmpl.md"
+playlist_empty_html="${mpdhh_bin}/h_playlist_empty_tmpl.md"
+analysis_tracks_tmpl="${mpdhh_bin}/h_analysis_${tracks}_tmpl.md"
+analysis_artists_tmpl="${mpdhh_bin}/h_analysis_${artists}_tmpl.md"
+
+page_header_html='Recently Played<span class="delimiter"> > </span>'
+dash="-"
+
+# Track meta (tab delim (important))
+formatstr='[%title%|%name%] [%album%] [%artist%|%albumartist%|%composer%]'
+
+# 4 modes:
+# 0 > default - update data [ & site ]
+# 1 > stdout current track
+# 2 > update historia only
+# 3 > update analysis only
+# 4 > build bin tree
+mode="${1:-0}"
+
+[ -z "$@" ] && tput civis
+cleanup(){ tput cnorm; exit 0; }
+trap cleanup SIGINT
+
+update_historia(){
+
+ # Manage historia files
+ [ ! -d "$historia_bin" ] && printf "Historia bin no existo, create first, then try again.\n" && exit 0
+
+ while IFS= read -r date_day; do
+
+ printf "Historia: today: %s, index date: %s\n" "$today_day" "$date_day"
+ # If today's date, skip as it is not yet history
+ [ "$today_day" = "$date_day" ] && continue
+
+ # if exists, stop updating through historia
+ [ -d "${historia_bin}/${date_day}" ] && break;
+
+ # Get date_day date vars
+ get_date_vars "$date_day"
+
+ # Update Historia index html date
+ sed -i -e "s#^\(date: \).*\$#\1${today_date}#" "${historia_bin}/${index_md_name}"
+
+ # Write date_day historia
+ write_playlist_html "$date_day" "historia" "$played_day_h" "$played_day_h"
+
+ done <"$db_index_file"
+}
+
+update_db_index_file(){
+
+ [ -z "$1" ] && return 1
+
+ # Add db file to historia index file
+ # Sed will not inject anything before line 1
+ # if there is no line 1 (empty/new file)
+ # so in this case just throw the line in there
+ if [ ! -f "$db_index_file" ]; then
+ printf "%s\n" "$1" > "$db_index_file"
+
+ elif [ "$(<"$db_index_file" wc -l)" -gt 0 ]; then
+
+ ! grep "$1" "$db_index_file" && \
+ sed -i '1i '"$1"'
+ ' "$db_index_file"
+ fi
+ sort -r -o "$db_index_file" "$db_index_file"
+}
+
+update_rss(){
+
+ # Build rss data line with iso
+ # date format for hugo parsing
+
+ [ ! -f "$rss_index" ] && \
+ printf "%s\n" "$track_data" > "$rss_index" || \
+ sed -i '1i '"$track_data"'
+ ' "${rss_index}"
+}
+
+get_matching_line_from_db(){
+
+ # $1 track_data
+ # $2 db file
+ # $3 track data fields
+ regex=; track_match=
+ a="${3:-"2,3,4"}"
+
+ if [ -z "$1" ] || [ -z "$2" ]; then return 1; fi
+
+ # Define grep pattern from track data
+ # - cut > remove the time by cutting/using fields 2,3,4 only
+ # - sed > escape special pattern matching characters from track data
+ regex="$(printf "%s" "$1" | cut -f "$a" | sed -e 's#\([][]\)#\\\1#g')"
+
+ # Find line no of first matching track
+ # - grep > run pattern as regex (-e flag) else '-' in pattern
+ # string gets interpreted as param flag
+ track_match=$(grep -ns -m1 -e "$regex" "$2" | cut -d: -f1)
+
+ # Can't test on empty variable(if no matches)...
+ # so make it 0 if need be
+ track_match="${track_match:-0}"
+}
+
+get_matching_line_from_analysis_db(){
+
+ # $1 track_data
+ # $2 db file
+ # $3 track data fields
+
+ # Get line number from db using awk
+ track_match="$(awk -vtd="$1" -vf="$3" '
+ BEGIN{
+ FS=OFS="\t"
+ falen=split(f,fa,",")
+ split(td,tda,FS)
+ }
+ {
+ for (i=1;i<=falen;i++){
+ if ($i != tda[fa[i]]) {
+ # On any field that does not match,
+ # exit loop, go to next line
+ next
+ }
+ }
+ # match made, print line number then exit
+ print NR
+ exit
+ }' "$2" )"
+
+ track_match="${track_match:-0}"
+}
+
+update_csv(){
+
+ # First check for existing bin
+ [ ! -d "$db_bin" ] && \
+ printf "%s\nDirectory does not exist. Exiting.\n" "$db_bin" && exit 0
+
+ pl_file="${db_bin}/${played_day}.db"
+
+ # if playlist.db no existo,
+ # if track data not empty,
+ # - create pl and add to it
+ # update historia regardless
+
+ if [ ! -f "$pl_file" ]; then
+ if [ -n "$track_data" ]; then
+ printf "%s\n" "$track_data" > "$pl_file"
+ update_rss
+ update_db_index_file "$played_day"
+ fi
+
+ update_historia
+
+ [ -n "$track_data" ] && return 0 || return 1
+
+ # if playlist file not empty,
+ # if track data not empty
+ # - insert new track
+ # else if track data empty
+ # - do nothing but return 1
+ elif [ "$(<"$pl_file" wc -l)" -gt 0 ]; then
+
+ [ -z "$track_data" ] && return 1
+
+ get_matching_line_from_db "$track_data" "$pl_file"
+
+ # If track already last track posted (line 1)
+ # - do not add to csv to mitigate redundancy
+ [ "$track_match" -eq 1 ] && return 1
+
+ update_rss
+ sed -i '1i '"$track_data"'
+ ' "$pl_file"
+
+ return 0
+ fi
+}
+
+__update_analysis_sort(){
+
+ # $1 - db_file
+ # $2 - track data fields
+
+ # Count sort fields, need this for trim off sort fields post sort
+ field_count=$(printf "%s" "$2"| awk 'BEGIN{FS=","}{print NF}')
+
+ if [ ! -f "$1" ]; then
+ # scan music db omnia, freshly populating tracks db
+ printf "Db file:\n%s\ndoes not exist.\nCreating with fresh sonus db sort...\n" "$1"
+
+ # Sort all tracks played, remove any single space or single dash lines
+ #cut -f "$2" "${db_bin}"/*.db | sort -u | sed '/^[- \t]\+$\|^$/d' > "$1"
+
+ # First awk entire playlist db into a tmpfile
+ tmpfile=$(mktemp)
+
+ #cat "$db_bin"/*db | \
+ awk -v f="$2" '
+ BEGIN {
+ OFS=FS="\t"
+ falen=split(f,fa,",")
+ }
+ {
+ for (i=1; i<=falen; i++) {
+ printf("%s%s", $fa[i], OFS)
+ }
+ print $0
+
+ }' "$db_bin"/*db | \
+ sort -n | \
+ cut -f$((field_count + 1))- \
+ > "$tmpfile"
+
+ # Now sort the tmp file into the final db file using awk
+ awk -v f="$2" '
+ BEGIN {
+ OFS = FS = "\t"
+ split( f, fa, ",")
+ }
+ {
+ curr = ""
+ for (i=1; i<=length(fa); i++) {
+ curr = curr $fa[i] OFS
+ }
+ if (tolower(curr) == tolower(prev)) {
+ pcount++
+ lastdate = $1
+ } else {
+ if (NR > 0) {
+ print prev firstdate, lastdate, pcount
+ }
+ firstdate = lastdate = $1
+ pcount = 1
+ }
+ prev = curr
+ }
+ END {
+ print prev firstdate, lastdate, pcount
+
+ }' "$tmpfile" > "$1"
+
+ # Cleanup
+ rm -v "$tmpfile"
+
+ printf "Done.\n"
+ else
+ # Only add files to analysis if on mode 0 i.e. while adding to playlist db files too
+ # as the entire purpose of anaylsis is to analyze what we've played, recorded, and
+ # published to the site and related db files
+ [ "$mode" -ne 0 ] && printf "Not mode 0. exiting.\n" && return 0
+
+ # Set/check for existing data in db
+ ! get_matching_line_from_analysis_db "$track_data" "$1" "$2" \
+ && printf "Exiting on from failed get_matching_line.\n" && exit 0
+
+
+ # If track not in db, add it
+ if [ "$track_match" -ne 0 ]; then
+ printf "Updating %s..." "${1##*/}"
+
+ # Get new count
+ newcount="$(sed -n "${track_match}p" "$1" | cut -f "$((field_count+3))" )"
+ newcount=$((newcount+1))
+ sed -i "${track_match}s/\([0-9]\{4\}[-0-9T:]\{21\}\)\t\([0-9]\+\)$/$played_date\t$newcount/" $1
+ printf "done.\n"
+ else
+ # Track no exist, add it, then sort
+ printf "Adding to %s..." "${1##*/}"
+ a="$(printf "%s" "$track_data" | cut -f "$2")"
+ regex="^[- ]$"
+ if [[ ! "$a" =~ $regex ]]; then
+
+ printf "%s\t%s\t%s\t%s\n" "$a" "$played_date" "$played_date" "1" >> "$1"
+ sort -n -o "$1" "$1"
+ fi
+ printf "done.\n"
+ fi
+
+ fi
+}
+
+update_analysis(){
+ # First check for existing bin
+ if [ ! -d "$analysis_bin" ] || [ ! -d "$analysis_tracks_bin" ] || [ ! -d "$analysis_artists_bin" ]; then
+ printf "Analysis bins do not exist. Exiting.\n" && exit 0
+ fi
+
+ # First - update tracks db
+ __update_analysis_sort "$analysis_tracks_db" "2,3,4"
+
+ # Second - update artists db
+ __update_analysis_sort "$analysis_artists_db" "4"
+
+ # Write artists index files
+ write_analysis_html "$artists" "4,1" "$th_analysis_artists"
+ # Write tracks index files
+ write_analysis_html "$tracks" "6,1,2,3" "$th_analysis_tracks"
+
+ # Write tracks sortby's
+ # 1|track 2|album 3|artist 4|firstdate 5|lastdate 6|playcount
+ __update_analysis_sortby "$tracks" "3,2" "" "$analysis_byartist_title" "$analysis_tracks_byartist_html"
+ __update_analysis_sortby "$tracks" "1" "" "$analysis_bytrack_title" "$analysis_tracks_bytrack_html"
+ __update_analysis_sortby "$tracks" "6" "-r" "$analysis_bycount_title" "$analysis_tracks_bycount_html"
+ __update_analysis_sortby "$tracks" "4" "-r" "$analysis_byinitial_title" "$analysis_tracks_byinitial_html" "4,1,2,3"
+ __update_analysis_sortby "$tracks" "5" "-r" "$analysis_byrecent_title" "$analysis_tracks_byrecent_html" "5,1,2,3"
+ __update_analysis_sortby "$artists" "1" "" "$analysis_byartist_title" "$analysis_artists_byartist_html"
+ __update_analysis_sortby "$artists" "4" "-r" "$analysis_bycount_title" "$analysis_artists_bycount_html"
+ __update_analysis_sortby "$artists" "2" "-r" "$analysis_byinitial_title" "$analysis_artists_byinitial_html" "2,1"
+ __update_analysis_sortby "$artists" "3" "-r" "$analysis_byrecent_title" "$analysis_artists_byrecent_html" "3,1"
+
+}
+
+__update_analysis_sortby(){
+
+ # $1 - artists or tracks
+ # $2 - fields to sort by
+ # $3 - sort flags (for reverse sorting)
+ # $4 - sortby title
+ # $5 - sortby html file
+
+ if [ "$1" = "$artists" ]; then
+ db_file="$analysis_artists_db"
+ th_values="$th_analysis_artists"
+ print_fields="${6:-"4,1"}"
+
+ elif [ "$1" = "$tracks" ]; then
+ db_file="$analysis_tracks_db"
+ th_values="$th_analysis_tracks"
+ print_fields="${6:-"6,1,2,3"}"
+ fi
+
+ field_count=$(printf "%s" "$2" | awk 'BEGIN{FS=","}{print NF}')
+ tmpfile="$(mktemp)"
+
+ # Add sorting fields to front for.. well sorting of course
+ # Then sort, then remove the sorting fields
+ awk -vf="$2" '
+
+ BEGIN{ OFS=FS="\t"; fl=split(f,fa,",") }
+ {
+ for (i=1; i<=fl; i++){
+ printf("%s%s", $fa[i], OFS)
+ }
+ print $0
+ }
+
+ ' \
+ "$db_file" | \
+ sort -n ${3} | \
+ cut -f$((field_count + 1))- \
+ > "$tmpfile"
+
+ write_analysis_html "$1" "$print_fields" "$th_values" "$tmpfile" "$4" "$5"
+
+ rm -v "$tmpfile"
+}
+
+write_playlist_html(){
+ # $1 = played_day
+ # $2 = page menu
+ # $3 = menu title
+ # $4 = page title
+ #
+ # get date vars if date param ($1)
+ # set else use date vars set when
+ # new track change for current
+ # playlist (see while loop)
+ #
+ # date param will be set for
+ # historia pages, where menu ($2) and
+ # title ($3) params will also be passed
+ if [ -n "$1" ]; then
+ mkdir -p "${historia_bin}/${played_day}" || exit 0
+ md_file="${historia_bin}/${played_day}/${index_md_name}"
+ else
+ md_file="${music_bin}/${index_md_name}"
+
+ # udpate date vars if historia updated html files (dates
+ # will be historical, but we need todays)
+ [ ! "$today_date" = "$played_date" ] && get_date_vars
+ fi
+
+ datafile="${db_bin}/${played_day}.db"
+ awk_htmltmpl="$playlist_html"
+
+ [ ! -f "$datafile" ] && \
+ datafile="" && \
+ awk_htmltmpl="$playlist_empty_html"
+
+ printf "Awking html for %s..." "$played_day"
+
+ # generate html with awk
+ awk \
+ -F "\t" \
+ -v today_date="$today_date" \
+ -v today_day="$today_day" \
+ -v played_date="$played_date" \
+ -v played_day="$played_day" \
+ -v played_day_h="$played_day_h" \
+ -v page_menu="${2:-"main"}" \
+ -v page_menutitle="${3:-"Music"}" \
+ -v page_menuidentifier="${3:-"Music"}" \
+ -v page_title="${4:-"Music"}" \
+ -v page_header_html="$page_header_html" \
+ -v track_data="$track_data" \
+ -v rss_data_file="$rss_data_file" \
+ -v description="$music_description" \
+ -v rss_title="$rss_title" \
+ -v html_type="playlist" \
+ -v pfields="" \
+ -v th_values="" \
+ -v tally="$(wc -l "$datafile" 2>/dev/null | cut -d" " -f1)" \
+ -f $awk_prog_1 \
+ "$awk_htmltmpl" "$datafile" > "$md_file"
+
+ printf "done.\n"
+}
+write_analysis_html(){
+
+ # $1 tracks or artists
+ # $2 field list (comma delim'd)
+ # $3 [ table th values ( delim "::" ) ]
+ # $4 [ database file ]
+ # $5 [ title ]
+ # $6 [ output html file ]
+
+ if [ "$1" = "$tracks" ]; then
+ awk_datafile="${4:-"$analysis_tracks_db"}"
+ awk_htmltmpl="$analysis_tracks_tmpl"
+ awk_html_file="${6:-"$analysis_tracks_html"}"
+ awk_title="${5:-"$analysis_tracks_title"}"
+ elif [ "$1" = "$artists" ]; then
+ awk_datafile="${4:-"$analysis_artists_db"}"
+ awk_htmltmpl="$analysis_artists_tmpl"
+ awk_html_file="${6:-"$analysis_artists_html"}"
+ awk_title="${5:-"$analysis_artists_title"}"
+ fi
+
+ awk_menuidentifier="${1}"
+
+ printf "Awking html for %s %s..." "$1" "$awktitle"
+
+ # generate html with awk
+ awk \
+ -F "\t" \
+ -v today_date="$today_date" \
+ -v today_day="$today_day" \
+ -v played_date="$played_date" \
+ -v played_day="$played_day" \
+ -v played_day_h="$played_day_h" \
+ -v page_menu="analysis" \
+ -v page_menutitle="${awk_title}" \
+ -v page_menuidentifier="$awk_menuidentifier" \
+ -v page_title="${awk_title}" \
+ -v page_header_html="$page_header_html" \
+ -v track_data="$track_data" \
+ -v rss_data_file="$rss_data_file" \
+ -v description="$music_description" \
+ -v rss_title="$rss_title" \
+ -v html_type="analysis" \
+ -v pfields="$2" \
+ -v th_values="$3" \
+ -v tally="$(<"${awk_datafile}" wc -l)" \
+ -f $awk_prog_1 \
+ "${awk_htmltmpl}" "${awk_datafile}" > "${awk_html_file}"
+ printf "done.\n"
+}
+
+publish_site(){
+
+ # Run simple shell script that
+ # 1. Updates hugo (hugo cmd)
+ # 2. git add .
+ # 3. git commit -m "message"
+ # 4. git push
+
+ [ "$MPDHH_PUBLISH" = "n" ] && printf "Not publishing.\n" && return
+
+ # Must change to hugo project bin to run hugo command (in publish.sh)
+ cur_bin="$(pwd)"
+ cd "${work_bin}/${hugo_proj}"
+ [ -f ./"${publish_sh}" ] && sh ./"${publish_sh}" "${commit_msg}"
+ cd "$cur_bin"
+}
+
+get_track_data(){
+ #mpc current -f "$formatstr" 2>/dev/null | grep -v '^[a-zA-Z]*:\s*$'
+ mpc current -f "$formatstr" 2>/dev/null | \
+ awk \
+ -F"\t" \
+ -v filename="$(mpc current -f '%file%' 2>/dev/null | awk -F "/" '{print $NF}')" \
+ -v played_date="$played_date" \
+ -v d="$dash" \
+ -f $awk_prog_2
+}
+
+get_date_vars(){
+ today_date="$(date -Iseconds)"
+ today_day="$(date --date="$today_date" +"%Y%m%d")"
+ played_date="${1:-"$today_date"}"
+ played_day="$(date --date="$played_date" +"%Y%m%d")"
+ played_day_h="$(date --date="$played_date" +"%Y %m %d | %A")"
+}
+
+site_git_merge(){
+ printf "Git fetch && merge...\n"
+ cur_bin="$(pwd)"
+ cd "${work_bin}/${hugo_proj}" && \
+ git fetch && git merge
+ cd "$cur_bin"
+}
+
+bin_struct(){
+ # Make the bins
+ [ ! -d "$work_bin" ] && printf "Please create proj bin, then continue. Exiting.\n" && exit 0
+ __bin_struct "$music_bin"
+ __bin_struct "$db_bin"
+ __bin_struct "$historia_bin"
+ __bin_struct "$analysis_bin"
+ __bin_struct "$analysis_artists_bin"
+ __bin_struct "${analysis_artists_bycount_html%/*}"
+ __bin_struct "${analysis_artists_byartist_html%/*}"
+ __bin_struct "${analysis_artists_byinitial_html%/*}"
+ __bin_struct "${analysis_artists_byrecent_html%/*}"
+ __bin_struct "$analysis_tracks_bin"
+ __bin_struct "${analysis_tracks_bytrack_html%/*}"
+ __bin_struct "${analysis_tracks_byartist_html%/*}"
+ __bin_struct "${analysis_tracks_bycount_html%/*}"
+ __bin_struct "${analysis_tracks_byinitial_html%/*}"
+ __bin_struct "${analysis_tracks_byrecent_html%/*}"
+}
+
+__bin_struct(){
+ [ ! -d "$1" ] && mkdir -v "$1" || printf "Bin already exists: %s\n" "$1"
+}
+
+while true; do
+ clear
+
+ get_date_vars
+ track_data="$(get_track_data)"
+
+ if [ "$mode" = "0" ]; then
+ # merge with site repo first
+ site_git_merge
+ # Update playlists csvs
+ # If new track then update
+ # analysis db/html files
+ update_csv && update_analysis
+ # Write playlist html files
+ write_playlist_html
+ # Publish site
+ publish_site
+
+ elif [ "$mode" = "1" ]; then
+ printf "%s\n" "$track_data"
+ break;
+
+ elif [ "$mode" = "2" ]; then
+ ls -1d "$historia_bin"/????????/ | xargs -I{} rm -rv "{}"
+ update_historia && printf "Done.\n"; exit 0
+
+ elif [ "$mode" = "3" ]; then
+ [ ! "$MPDHH_PUBLISH" = "n" ] && site_git_merge
+ # Refresh the analysis db
+ [ -f "$analysis_artists_db" ] && rm -v "$analysis_artists_db"
+ [ -f "$analysis_tracks_db" ] && rm -v "$analysis_tracks_db"
+ update_analysis && publish_site && printf "Done.\n"; exit 0
+
+ elif [ "$mode" = "4" ]; then
+ bin_struct
+ exit 0
+ fi
+
+ >/dev/null mpc idle player sticker
+done