Generates and manages static html of mpd currently playing music.

git clone git://watertao.xyz/programs/mpdhh.git

mpdhh.sh (18070B)


      1 #!/bin/sh
      2 
      3 # Important customization:
      4 mpdhh_bin=~/progs/mpdhh
      5 proj_bin="${HOME}/proj/example.com"
      6 publish_sh="publish.sh"
      7 
      8 # All else, modify to your liking
      9 artists="artists"
     10 tracks="tracks"
     11 index_md_name="_index.html"
     12 music_bin_proj_local="content/music"
     13 music_bin="${proj_bin}/${music_bin_proj_local}"
     14 db_bin="${music_bin}/db"
     15 historia_bin="${music_bin}/historia"
     16 db_index_file="${historia_bin}/db.index"
     17 rss_index_name="rss.index"
     18 rss_index="${music_bin}/${rss_index_name}"
     19 
     20 analysis_bin="${music_bin}/analysis"
     21 analysis_artists_bin="${analysis_bin}/${artists}"
     22 analysis_tracks_bin="${analysis_bin}/${tracks}"
     23 analysis_tracks_db="${analysis_tracks_bin}/${tracks}.db"
     24 analysis_artists_db="${analysis_artists_bin}/${artists}.db"
     25 analysis_tracks_html="${analysis_tracks_bin}/${index_md_name}"
     26 analysis_artists_html="${analysis_artists_bin}/${index_md_name}"
     27 analysis_tracks_byartist_html="${analysis_tracks_bin}/by-artist/${index_md_name}"
     28 analysis_tracks_bytrack_html="${analysis_tracks_bin}/by-track/${index_md_name}"
     29 analysis_tracks_bycount_html="${analysis_tracks_bin}/by-play-count/${index_md_name}"
     30 analysis_tracks_byinitial_html="${analysis_tracks_bin}/by-initial/${index_md_name}"
     31 analysis_tracks_byrecent_html="${analysis_tracks_bin}/by-recent/${index_md_name}"
     32 analysis_artists_byartist_html="${analysis_artists_bin}/by-artist/${index_md_name}"
     33 analysis_artists_bycount_html="${analysis_artists_bin}/by-play-count/${index_md_name}"
     34 analysis_artists_byinitial_html="${analysis_artists_bin}/by-initial/${index_md_name}"
     35 analysis_artists_byrecent_html="${analysis_artists_bin}/by-recent/${index_md_name}"
     36 th_playlist="Time::Title::Album::Artist"
     37 th_analysis_tracks="Title::Album::Artist::Initial::Recent::+"
     38 th_analysis_artists="Artist::Initial::Recent::+"
     39 analysis_byinitial_title="By Initial"
     40 analysis_byrecent_title="By Recent"
     41 analysis_byartist_title="By Artist"
     42 analysis_bytrack_title="By Track"
     43 analysis_bycount_title="By Play Count"
     44 analysis_tracks_title="Tracks"
     45 analysis_artists_title="Artists"
     46 
     47 commit_msg="dojo music update"
     48 music_description="The sweet sounds from my pinephone"
     49 rss_title="Sonus"
     50 
     51 # Set rss_data_file to data file to force hugo to parse data file for rss stream
     52 # for currently playing playlist page. This is unset in awk for historia page creation
     53 rss_data_file="${rss_index_name}"
     54 
     55 awk_prog_1="${mpdhh_bin}/a_write_html.awk"
     56 awk_prog_2="${mpdhh_bin}/a_filter_track_data.awk"
     57 playlist_html="${mpdhh_bin}/h_playlist_tmpl.md"
     58 playlist_empty_html="${mpdhh_bin}/h_playlist_empty_tmpl.md"
     59 analysis_tracks_tmpl="${mpdhh_bin}/h_analysis_${tracks}_tmpl.md"
     60 analysis_artists_tmpl="${mpdhh_bin}/h_analysis_${artists}_tmpl.md"
     61 
     62 page_header_html='Recently Played<span class="delimiter"> > </span>'
     63 dash="-"
     64 
     65 # Track meta (tab delim (important))
     66 formatstr='[%title%|%name%]	[%album%]	[%artist%|%albumartist%|%composer%]'
     67 
     68 # 4 modes:
     69 #  0 > default - update data [ & site ]
     70 #  1 > stdout current track
     71 #  2 > update historia only
     72 #  3 > update analysis only
     73 #  4 > build bin tree
     74 mode="${1:-0}"
     75 
     76 [ -z "$@" ] && tput civis
     77 cleanup(){ tput cnorm; exit 0; }
     78 trap cleanup SIGINT
     79 
     80 update_historia(){
     81 
     82 	# Manage historia files
     83 	[ ! -d "$historia_bin" ] && printf "Historia bin no existo, create first, then try again.\n" && exit 0
     84 
     85 	while IFS= read -r date_day; do
     86 
     87 		printf "Historia: today: %s, index date: %s\n" "$today_day" "$date_day"
     88 		# If today's date, skip as it is not yet history
     89 		[ "$today_day" = "$date_day" ] && continue
     90 
     91 		# if exists, stop updating through historia
     92 		[ -d "${historia_bin}/${date_day}" ] && break;
     93 		
     94 		# Get date_day date vars
     95 		get_date_vars "$date_day"
     96 
     97 		# Update Historia index html date
     98 		sed -i -e "s#^\(date: \).*\$#\1${today_date}#" "${historia_bin}/${index_md_name}"
     99 
    100 		# Write date_day historia
    101 		write_playlist_html "$date_day" "historia" "$played_day_h" "$played_day_h"
    102 
    103 	done <"$db_index_file"
    104 }
    105 
    106 update_db_index_file(){
    107 
    108 	[ -z "$1" ] && return 1
    109 
    110 	# Add db file to historia index file
    111 	# Sed will not inject anything before line 1
    112 	# if there is no line 1 (empty/new file)
    113 	# so in this case just throw the line in there
    114 	if [ ! -f "$db_index_file" ]
    115 	then
    116 		printf "%s\n" "$1" > "$db_index_file"
    117 
    118 	elif [ -s "$db_index_file" ]
    119 	then
    120 		! grep "$1" "$db_index_file" && \
    121 			sed -i '1i '"$1"'
    122 			' "$db_index_file"
    123 	fi
    124 	sort -r -o "$db_index_file" "$db_index_file"
    125 }
    126 
    127 update_rss(){
    128 
    129 	# Build rss data line with iso
    130 	# date format for hugo parsing
    131 
    132 	[ ! -f "$rss_index" ] && \
    133 		printf "%s\n" "$track_data" > "$rss_index" || \
    134 		sed -i '1i '"$track_data"'
    135 		' "${rss_index}"
    136 }
    137 
    138 get_matching_line_from_db(){
    139 
    140 	# $1 track_data
    141 	# $2 db file
    142 	# $3 track data fields
    143 	regex=; track_match=
    144 	a="${3:-"2,3,4"}"
    145 
    146 	if [ -z "$1" ] || [ -z "$2" ]; then return 1; fi
    147 
    148 	# Define grep pattern from track data
    149 	# - cut > remove the time by cutting/using fields 2,3,4 only
    150 	# - sed > escape special pattern matching characters from track data
    151 	regex="$(printf "%s" "$1" | cut -f "$a" | sed -e 's#\([][]\)#\\\1#g')"
    152 	
    153 	# Find line no of first matching track
    154 	# - grep > run pattern as regex (-e flag) else '-' in pattern
    155 	#   string gets interpreted as param flag
    156 	track_match=$(grep -ns -m1 -e "$regex" "$2" | cut -d: -f1)
    157 
    158 	# Can't test on empty variable(if no matches)...
    159 	# so make it 0 if need be
    160 	track_match="${track_match:-0}"
    161 }
    162 
    163 get_matching_line_from_analysis_db(){
    164 
    165 	# $1 track_data
    166 	# $2 db file
    167 	# $3 track data fields
    168 
    169 	# Get line number from db using awk
    170 	track_match="$(awk -vtd="$1" -vf="$3" '
    171 	BEGIN{
    172 		FS=OFS="\t"
    173 		falen=split(f,fa,",")
    174 		split(td,tda,FS)
    175 	}
    176 	{
    177 		for (i=1;i<=falen;i++){
    178 			if ($i != tda[fa[i]]) {
    179 				# On any field that does not match,
    180 				# exit loop, go to next line
    181 				next
    182 			}
    183 		}
    184 		# match made, print line number then exit
    185 		print NR
    186 		exit
    187 	}' "$2" )"
    188 
    189 	track_match="${track_match:-0}"
    190 }
    191 
    192 update_csv(){
    193 
    194 	# First check for existing bin
    195 	[ ! -d "$db_bin" ] && \
    196 		printf "%s\nDirectory does not exist. Exiting.\n" "$db_bin" && exit 0
    197 
    198 	pl_file="${db_bin}/${played_day}.db"
    199 
    200 	# if playlist.db no existo,
    201 	# if track data not empty, 
    202 	#   - create pl and add to it
    203 	# update historia regardless
    204 
    205 	if [ ! -f "$pl_file" ]; then
    206 		if [ -n "$track_data" ]; then
    207 			printf "%s\n" "$track_data" > "$pl_file"
    208 			update_rss
    209 			update_db_index_file "$played_day"
    210 		fi
    211 
    212 		update_historia
    213 
    214 		# reset date vars to today if track_data
    215 		[ -n "$track_data" ] && get_date_vars && return 0 || return 1
    216 
    217 	# if playlist file not empty,
    218 	# if track data not empty
    219 	#   - insert new track
    220 	# else if track data empty
    221 	#   - do nothing but return 1
    222 	elif [ -s "$pl_file" ]; then
    223 
    224 		[ -z "$track_data" ] && return 1
    225 
    226 		get_matching_line_from_db "$track_data" "$pl_file"
    227 
    228 		# If track already last track posted (line 1)
    229 		#   - do not add to csv to mitigate redundancy
    230 		[ "$track_match" -eq 1 ] && return 1
    231 
    232 		update_rss
    233 		sed -i '1i '"$track_data"'
    234 		' "$pl_file"
    235 
    236 		return 0
    237 	fi
    238 }
    239 
    240 __update_analysis_sort(){
    241 
    242 	# $1 - db_file
    243 	# $2 - track data fields
    244 
    245 	# Count sort fields, need this for trim off sort fields post sort
    246 	field_count=$(printf "%s" "$2"| awk 'BEGIN{FS=","}{print NF}')
    247 
    248 	if [ ! -f "$1" ]; then
    249 		# scan music db omnia, freshly populating tracks db
    250 		printf "Db file:\n%s\ndoes not exist.\nCreating with fresh sonus db sort...\n" "$1"
    251 
    252 		# Sort all tracks played, remove any single space or single dash lines
    253 		#cut -f "$2" "${db_bin}"/*.db | sort -u | sed '/^[- \t]\+$\|^$/d' > "$1"
    254 
    255 		# First awk entire playlist db into a tmpfile
    256 		tmpfile=$(mktemp)
    257 
    258 		#cat "$db_bin"/*db | \
    259 		awk -v f="$2" '
    260 			BEGIN {
    261 				OFS=FS="\t"
    262 				falen=split(f,fa,",")
    263 			}
    264 			{
    265 				for (i=1; i<=falen; i++) {
    266 					printf("%s%s", $fa[i], OFS)
    267 				}
    268 				print $0
    269 
    270 			}' "$db_bin"/*db | \
    271 			sort -n | \
    272 			cut -f$((field_count + 1))- \
    273 			> "$tmpfile"
    274 
    275 		# Now sort the tmp file into the final db file using awk
    276 		awk -v f="$2" '
    277 			BEGIN {
    278 				OFS = FS = "\t"
    279 				split( f, fa, ",")
    280 			}
    281 			{
    282 				curr = ""
    283 				for (i=1; i<=length(fa); i++) {
    284 					curr = curr $fa[i] OFS
    285 				}
    286 				if (tolower(curr) == tolower(prev)) {
    287 					pcount++
    288 					lastdate = $1
    289 				} else {
    290 					if (NR > 0)  {
    291 						print prev firstdate, lastdate, pcount
    292 					}
    293 					firstdate = lastdate = $1
    294 					pcount = 1
    295 				}
    296 				prev = curr
    297 			}
    298 			END {
    299 				print prev firstdate, lastdate, pcount
    300 
    301 			}' "$tmpfile" > "$1"
    302 
    303 		# Cleanup
    304 		rm -v "$tmpfile"
    305 
    306 		printf "Done.\n"
    307 	else
    308 		# Only add files to analysis if on mode 0 i.e. while adding to playlist db files too
    309 		# as the entire purpose of anaylsis is to analyze what we've played, recorded, and 
    310 		# published to the site and related db files
    311 		[ "$mode" -ne 0 ] && printf "Not mode 0. exiting.\n" && return 0
    312 
    313 		# Set/check for existing data in db
    314 		! get_matching_line_from_analysis_db "$track_data" "$1" "$2" \
    315 			&& printf "Exiting on from failed get_matching_line.\n" && exit 0
    316 
    317 
    318 		# If track not in db, add it
    319 		if [ "$track_match" -ne 0 ]
    320 		then
    321 			printf "Updating %s..." "${1##*/}"
    322 
    323 			# Get new count
    324 			newcount="$(sed -n "${track_match}p" "$1" | cut -f "$((field_count+3))" )"
    325 			newcount=$((newcount+1))
    326 			sed -i "${track_match}s/\([0-9]\{4\}[-0-9T:]\{21\}\)\t\([0-9]\+\)$/${played_date}\t${newcount}/" $1
    327 			printf "done.\n"
    328 		else
    329 			# Track no exist, add it, then sort
    330 			printf "%s\n" "Adding to ${1##*/}..."
    331 			a="$(printf "%s" "$track_data" | cut -f "$2")"
    332 			if printf "%s" "$a" | grep -q -v "^[- ]$"
    333 			then
    334 				printf "%s\t%s\t%s\t%s\n" "$a" "$played_date" "$played_date" "1" >> "$1"
    335 				sort -n -o "$1" "$1"
    336 			fi
    337 			printf "done.\n"
    338 		fi
    339 
    340 	fi
    341 }
    342 
    343 update_analysis(){
    344 	# First check for existing bin
    345 	if [ ! -d "$analysis_bin" ] || [ ! -d "$analysis_tracks_bin" ] || [ ! -d "$analysis_artists_bin" ]; then
    346 		printf "Analysis bins do not exist. Exiting.\n" && exit 0
    347 	fi
    348 
    349 	# First - update tracks db
    350 	__update_analysis_sort "$analysis_tracks_db" "2,3,4"
    351 
    352 	# Second - update artists db
    353 	__update_analysis_sort "$analysis_artists_db" "4"
    354 
    355 	# Write artists index files
    356 	write_analysis_html "$artists" "4,1" "$th_analysis_artists"
    357 	# Write tracks index files
    358 	write_analysis_html "$tracks" "6,1,2,3" "$th_analysis_tracks"
    359 
    360 	# Write tracks sortby's
    361 	# 1|track 2|album 3|artist 4|firstdate 5|lastdate 6|playcount
    362 	__update_analysis_sortby "$tracks" "3,2" "" "$analysis_byartist_title" "$analysis_tracks_byartist_html"
    363 	__update_analysis_sortby "$tracks" "1" "" "$analysis_bytrack_title" "$analysis_tracks_bytrack_html"
    364 	__update_analysis_sortby "$tracks" "6" "-r" "$analysis_bycount_title" "$analysis_tracks_bycount_html"
    365 	__update_analysis_sortby "$tracks" "4" "-r" "$analysis_byinitial_title" "$analysis_tracks_byinitial_html" "4,1,2,3"
    366 	__update_analysis_sortby "$tracks" "5" "-r" "$analysis_byrecent_title" "$analysis_tracks_byrecent_html" "5,1,2,3"
    367 	__update_analysis_sortby "$artists" "1" "" "$analysis_byartist_title" "$analysis_artists_byartist_html"
    368 	__update_analysis_sortby "$artists" "4" "-r" "$analysis_bycount_title" "$analysis_artists_bycount_html"
    369 	__update_analysis_sortby "$artists" "2" "-r" "$analysis_byinitial_title" "$analysis_artists_byinitial_html" "2,1"
    370 	__update_analysis_sortby "$artists" "3" "-r" "$analysis_byrecent_title" "$analysis_artists_byrecent_html" "3,1"
    371 
    372 }
    373 
    374 __update_analysis_sortby(){
    375 
    376 	# $1 - artists or tracks
    377 	# $2 - fields to sort by
    378 	# $3 - sort flags (for reverse sorting)
    379 	# $4 - sortby title
    380 	# $5 - sortby html file
    381 	
    382 	if [ "$1" = "$artists" ]; then
    383 		db_file="$analysis_artists_db"
    384 		th_values="$th_analysis_artists"
    385 		print_fields="${6:-"4,1"}"
    386 
    387 	elif [ "$1" = "$tracks" ]; then
    388 		db_file="$analysis_tracks_db"
    389 		th_values="$th_analysis_tracks"
    390 		print_fields="${6:-"6,1,2,3"}"
    391 	fi
    392 
    393 	field_count=$(printf "%s" "$2" | awk 'BEGIN{FS=","}{print NF}')
    394 	tmpfile="$(mktemp)"
    395 	
    396 	# Add sorting fields to front for.. well sorting of course
    397 	# Then sort, then remove the sorting fields
    398 	awk -vf="$2" '
    399 
    400 	BEGIN{ OFS=FS="\t"; fl=split(f,fa,",") }
    401 	{ 
    402 		for (i=1; i<=fl; i++){
    403 			printf("%s%s", $fa[i], OFS)
    404 		}
    405 		print $0
    406 	}
    407 
    408 	' \
    409 	"$db_file" | \
    410 	sort -n ${3} | \
    411 	cut -f$((field_count + 1))- \
    412 	> "$tmpfile"
    413 
    414 	write_analysis_html "$1" "$print_fields" "$th_values" "$tmpfile" "$4" "$5"
    415 
    416 	rm -v "$tmpfile"
    417 }
    418 
    419 write_playlist_html(){
    420 	# $1 = played_day
    421 	# $2 = page menu
    422 	# $3 = menu title
    423 	# $4 = page title
    424 	#
    425 	# get date vars if date param ($1)
    426 	# set else use date vars set when
    427 	# new track change for current
    428 	# playlist (see while loop)
    429 	#
    430 	# date param will be set for 
    431 	# historia pages, where menu ($2) and
    432 	# title ($3) params will also be passed
    433 	if [ -n "$1" ]; then
    434 		mkdir -p "${historia_bin}/${played_day}" || exit 0
    435 		md_file="${historia_bin}/${played_day}/${index_md_name}"
    436 	else
    437 		md_file="${music_bin}/${index_md_name}"
    438 
    439 		# udpate date vars if historia updated html files (dates
    440 		# will be historical, but we need todays)
    441 		[ ! "$today_date" = "$played_date" ] && get_date_vars
    442 	fi
    443 
    444 	datafile="${db_bin}/${played_day}.db"
    445 	awk_htmltmpl="$playlist_html"
    446 
    447 	[ ! -f "$datafile" ] && \
    448 		datafile="" && \
    449 		awk_htmltmpl="$playlist_empty_html"
    450 
    451 	printf "Awking html for %s..." "$played_day"
    452 
    453 	# generate html with awk
    454 	awk \
    455 		-F "\t" \
    456 		-v today_date="$today_date" \
    457 		-v today_day="$today_day" \
    458 		-v played_date="$played_date" \
    459 		-v played_day="$played_day" \
    460 		-v played_day_h="$played_day_h" \
    461 		-v page_menu="${2:-"main"}" \
    462 		-v page_menutitle="${3:-"Music"}" \
    463 		-v page_menuidentifier="${3:-"Music"}" \
    464 		-v page_title="${4:-"Music"}" \
    465 		-v page_header_html="$page_header_html" \
    466 		-v track_data="$track_data" \
    467 		-v rss_data_file="$rss_data_file" \
    468 		-v description="$music_description" \
    469 		-v rss_title="$rss_title" \
    470 		-v html_type="playlist" \
    471 		-v pfields="" \
    472 		-v th_values="" \
    473 		-v tally="$(wc -l "$datafile" 2>/dev/null | cut -d" " -f1)" \
    474 		-f $awk_prog_1 \
    475 		"$awk_htmltmpl" "$datafile" > "$md_file"
    476 
    477 	printf "done.\n"
    478 }
    479 write_analysis_html(){
    480 
    481 	# $1 tracks or artists
    482 	# $2 field list (comma delim'd)
    483 	# $3 [ table th values ( delim "::" ) ]
    484 	# $4 [ database file ]
    485 	# $5 [ title ]
    486 	# $6 [ output html file ]
    487 
    488 	if [ "$1" = "$tracks" ]; then
    489 		awk_datafile="${4:-"$analysis_tracks_db"}"
    490 		awk_htmltmpl="$analysis_tracks_tmpl"
    491 		awk_html_file="${6:-"$analysis_tracks_html"}"
    492 		awk_title="${5:-"$analysis_tracks_title"}"
    493 	elif [ "$1" = "$artists" ]; then
    494 		awk_datafile="${4:-"$analysis_artists_db"}"
    495 		awk_htmltmpl="$analysis_artists_tmpl"
    496 		awk_html_file="${6:-"$analysis_artists_html"}"
    497 		awk_title="${5:-"$analysis_artists_title"}"
    498 	fi
    499 
    500 	awk_menuidentifier="${1}"
    501 
    502 	printf "Awking html for %s %s..." "$1" "$awktitle"
    503 
    504 	# generate html with awk
    505 	awk \
    506 		-F "\t" \
    507 		-v today_date="$today_date" \
    508 		-v today_day="$today_day" \
    509 		-v played_date="$played_date" \
    510 		-v played_day="$played_day" \
    511 		-v played_day_h="$played_day_h" \
    512 		-v page_menu="analysis" \
    513 		-v page_menutitle="${awk_title}" \
    514 		-v page_menuidentifier="$awk_menuidentifier" \
    515 		-v page_title="${awk_title}" \
    516 		-v page_header_html="$page_header_html" \
    517 		-v track_data="$track_data" \
    518 		-v rss_data_file="$rss_data_file" \
    519 		-v description="$music_description" \
    520 		-v rss_title="$rss_title" \
    521 		-v html_type="analysis" \
    522 		-v pfields="$2" \
    523 		-v th_values="$3" \
    524 		-v tally="$(<"${awk_datafile}" wc -l)" \
    525 		-f $awk_prog_1 \
    526 		"${awk_htmltmpl}" "${awk_datafile}" > "${awk_html_file}"
    527 	printf "done.\n"
    528 }
    529 
    530 publish_site(){
    531 
    532 	# Run simple shell script that
    533 	# 1. Updates hugo (hugo cmd)
    534 	# 2. git add .
    535 	# 3. git commit -m "message"
    536 	# 4. git push
    537 
    538 	[ "$MPDHH_PUBLISH" = "n" ] && printf "Not publishing.\n" && return
    539 
    540 	# Must change to hugo project bin to run hugo command (in publish.sh)
    541 	cur_bin="$(pwd)"
    542 	cd "$proj_bin"
    543 	[ -f ./"${publish_sh}" ] && sh ./"${publish_sh}" "${commit_msg}"
    544 	cd "$cur_bin"
    545 }
    546 
    547 get_track_data(){
    548 	#mpc current -f "$formatstr" 2>/dev/null | grep -v '^[a-zA-Z]*:\s*$'
    549 	mpc current -f "$formatstr" 2>/dev/null | \
    550 	awk \
    551 		-F"\t" \
    552 		-v filename="$(mpc current -f '%file%' 2>/dev/null | awk -F "/" '{print $NF}')" \
    553 		-v played_date="$played_date" \
    554 		-v d="$dash" \
    555 		-f $awk_prog_2
    556 }
    557 
    558 get_date_vars(){
    559 	today_date="$(date -Iseconds)"
    560 	today_day="$(date --date="$today_date" +"%Y%m%d")"
    561 	played_date="${1:-"$today_date"}"
    562 	played_day="$(date --date="$played_date" +"%Y%m%d")"
    563 	played_day_h="$(date --date="$played_date" +"%Y %m %d | %A")"
    564 }
    565 
    566 site_git_merge(){
    567 	printf "Git fetch && merge...\n"
    568 	cur_bin="$(pwd)"
    569 	cd "$proj_bin" && \
    570 	git fetch && git merge
    571 	cd "$cur_bin"
    572 }
    573 
    574 bin_struct(){
    575 	# Make the bins
    576 	[ ! -d "$proj_bin" ] && printf "Please create proj bin, then continue. Exiting.\n" && exit 0
    577 	__bin_struct "$music_bin"
    578 	__bin_struct "$db_bin"
    579 	__bin_struct "$historia_bin"
    580 	__bin_struct "$analysis_bin"
    581 	__bin_struct "$analysis_artists_bin"
    582 	__bin_struct "${analysis_artists_bycount_html%/*}"
    583 	__bin_struct "${analysis_artists_byartist_html%/*}"
    584 	__bin_struct "${analysis_artists_byinitial_html%/*}"
    585 	__bin_struct "${analysis_artists_byrecent_html%/*}"
    586 	__bin_struct "$analysis_tracks_bin"
    587 	__bin_struct "${analysis_tracks_bytrack_html%/*}"
    588 	__bin_struct "${analysis_tracks_byartist_html%/*}"
    589 	__bin_struct "${analysis_tracks_bycount_html%/*}"
    590 	__bin_struct "${analysis_tracks_byinitial_html%/*}"
    591 	__bin_struct "${analysis_tracks_byrecent_html%/*}"
    592 }
    593 
    594 __bin_struct(){
    595 	[ ! -d "$1" ] && mkdir -v "$1" || printf "Bin already exists: %s\n" "$1"
    596 }
    597 
    598 while true; do
    599 	clear
    600 
    601 	get_date_vars
    602 	track_data="$(get_track_data)"
    603 
    604 	if [ "$mode" = "0" ]; then
    605 		# merge with site repo first
    606 		site_git_merge
    607 		# Update playlists csvs
    608 		# If new track then update
    609 		# analysis db/html files
    610 		update_csv && update_analysis
    611 		# Write playlist html files
    612 		write_playlist_html
    613 		# Publish site
    614 		publish_site
    615 
    616 	elif [ "$mode" = "1" ]; then
    617 		printf "%s\n" "$track_data"
    618 		break;
    619 
    620 	elif [ "$mode" = "2" ]; then
    621 		ls -1d "$historia_bin"/????????/ | xargs -I{} rm -rv "{}"
    622 		update_historia && printf "Done.\n"; exit 0
    623 
    624 	elif [ "$mode" = "3" ]; then
    625 		[ ! "$MPDHH_PUBLISH" = "n" ] && site_git_merge
    626 		# Refresh the analysis db
    627 		[ -f "$analysis_artists_db" ] && rm -v "$analysis_artists_db"
    628 		[ -f "$analysis_tracks_db" ] && rm -v "$analysis_tracks_db"
    629 		update_analysis && publish_site && printf "Done.\n"; exit 0
    630 
    631 	elif [ "$mode" = "4" ]; then
    632 		bin_struct
    633 		exit 0
    634 	fi
    635 
    636 	>/dev/null mpc idle player sticker
    637 done