Knowledge Base

Preserving for the future: Shell scripts, AoC, and more

xdg-mime command for window managers

I don't use a desktop environment for most of my desktop systems, I use the fluxbox (my blog posts) window manager. It hasn't changed since before I started using it, and probably won't ever. I think it's mostly dead, but that also means it won't ever change! I did start packaging the latest (probable) upstream (github) along with a patch I wrote for myself though.

So, because I don't use a desktop environment, and I had forgotten I could use my package of mime_types_editor or anything similar, I had a small problem when I try to use the "open directory" function beside a downloaded item in my web browser. So this command solved that for me:

xdg-mime default xfe.desktop inode/directory

And also to help myautomount tray icon menu:

xdg-mime default xfe.desktop inode/mount-point
Update 2023-11-08

For dvds, particularly with myautomount:

xdg-mime default vlc.desktop x-scheme-handler/dvd

Which adds to your mimeapps.list:

[Default Applications]
x-scheme-handler/dvd=vlc.desktop

Wikipedia: use usable layout

Wikipedia updated its layout, and it now makes less sense than before. To use the sensible design it has been for over a decade, you can add this url parameter: ?useskin=vector.

Alternatively, you can use the Firefox extension Classic mode for Wikipedia, or just suck it up and press the multiple buttons it takes to widen the new layout, but that still hides the table of contents. The old view is the only way to get that classic TOC object.

Reference

  1. How to restore Wikipedia's old design - gHacks Tech News
  2. Classic mode for Wikipedia Firefox extension

Why I stopped using Palemoon

Summary

I stopped using Palemoon web browser this last year, 2022. It was a decent browser for a while but eventually slid into irrelevancy, and this is ignoring the toxicity of the community. It started out as a more sane Firefox 20 or so, and ended up as an obsolete Firefox 20. (Numbers are generalizations and useful for imparting approximate understanding and humor only.)

And yes, I'm out of technical topics which is why it's time for a chat about old news. Plus, somebody asked and I didn't want to drown the individual so the best place is here.

Main reasons

The primary reason I stopped using Palemoon web browser is because it stopped working on enough websites that I had to always keep a second browser open. It also suffered from instability for quite a few months and it would freeze the whole system for a few seconds at a time. I never submitted a bug because I didn't know how to document or bring it up. Plus, I always compile it myself and upstream always claimed you're on your own for that situation.

Google Voice used to use a dedicated plugin that you could use to place voice calls. When Google Voice switched to WebRTC, palemoon stopped being usable for that, and that is a primary use of that for me. Yes, it's absurd to use a web browser for everything under the sun. But yes, it's still absurd that this one browser doesn't keep up with the rest it wants people to use it as an alternative to.

Secondary reasons

The upstream development community used to be extremely toxic. It's now only slightly toxic, with the removal of the most malicious person I've ever interacted with on the Internet (including multiplayer video game players). I could make do by mostly avoiding the offending developer, but because he held such high status until his malevolent confrontations with the other upstream devs which lead to alleged vandalism of the palemoon servers, I couldn't actually hide his posts on the forum from my view.

This person also called me offensive terms because I stopped maintaining the Fedora-ized build of Palemoon. I had stopped using Fedora GNU/Linux as a daily driver and could no longer validate the built assets, so I didn't want to advertise things that I built but couldn't dogfood. Thinking through that process didn't occur to the most offensive developer, but that's OK. He missed things all the time and the world keeps going around him.

I was banned from the upstream forum for a few days once for calling out his toxicity and supporting some newb that had asked a stereotypically-noobie question. In normal places that wouldn't get you in trouble, but the upstream devs have their own, misanthropic opinions about things, as previously discussed.

Only when the chief developer was personally affected by the toxicity and malevolent behavior of the most offensive developer did he tone down his stupidity and give a mild apology.

Debian Unstable, for which I'm too utterly dependent because I target Devuan Unstable, does not have a solution for the ancient python2 build dependency of the palemoon application. Debian Unstable just removed python2, and folks smarter than me haven't solved this one [in public] yet. I suppose it would be too much to ask to get upstream to use python3. If I wrote this article in a few more month's time without any changes to the situation, this right here would be reason #1.

Minor reasons

I swear that once my Palemoon crashed and it merged the history of a private browsing window with my permanent browsing history. Of course there's the smallest likelihood that it wasn't actually a private browsing window, but that is a very small chance.

The silliness with shuffling the upstream scm links rapidly between 4 releases' worth of time didn't help, but I'm guilty of brief experimentations too. I just try really, really hard to avoid doing it to production.

I'm not even mad about the gtk2-is-primary thing; I think gtk2 is quite fine. Unfortunately, I don't run the world and too many places are starting to drop gtk2.

Extensions

Thankfully, I never cared about noscript. I didn't really care about Adblock Plus or whatever that Palemoon officially recommended, and uBlock Origin [legacy] worked well enough and I didn't tell anyone I used it.

Final thoughts

I use LibreWolf and Firefox now. I need to get back into Waterfox Classic.

A new project I found: Metube

Metube is a web frontent for yt-dlp that makes it convenient to download youtube videos.

I set it up for myself recently, for when I don't have a terminal handy. I use a reverse-proxy so it can be on https for when I use the bookmarklet.

Alternatives

I actually tested only a few alternatives, which are indicated in the above list.

Bookmarklets

Upstream provides sample bookmarklets you can save as a browser bookmark. When visiting a video page, you can select this bookmarklet and it will send it to the app.

<a href='javascript:(function(){xhr=new XMLHttpRequest();xhr.open("POST","https://server3.example.com/metube/add");xhr.send(JSON.stringify({"url":document.location.href,"quality":"best"}));xhr.onload=function(){if(xhr.status==200){alert("Sent to metube!")}else{alert("Send to metube failed. Check the javascript console for clues.")}}})();'>Save to Jellyfin</a>

Photoshop CS4 in Wine

PhotoShop CS4 works in Wine after you install a few components.

env WINEARCH=win64 WINEPREFIX=~/.wine-photoshop winetricks gdiplus corefonts

Installing it was a black box that I cannot yet confirm how I accomplished it.

Wine error: Couldn't get desktop folder

I copied a wine prefix to a different user, and was then unable to start my application. I struggled to get the app to work, and started backtracking to see if I could get more info. I learned that even explorer.exe was not working in Wine. I don't control that at all! That's a built-in component of Wine. So if that doesn't work, I've really messed something up!

WINEPREFIX=/home/bgstack15/.wine WINEARCH=win64 wine explorer.exe

When explorer.exe doesn't work in wine, you KNOW you have a problem! Here is the output of the error message.

Unhandled exception: page fault on read access to 0x00000000 in 32-bit code (0x0040be65).
Register dump:
 CS:0023 SS:002b DS:002b ES:002b FS:006b GS:0063
 EIP:0040be65 ESP:0078f7a0 EBP:0078fe68 EFLAGS:00010206(  R- --  I   - -P- )
 EAX:80070057 EBX:008a38c0 ECX:00000000 EDX:7149b720
 ESI:0089c980 EDI:0078f854
Stack dump:
0x0078f7a0:  0089cc48 00000000 00000000 0089ca88
0x0078f7b0:  00000000 00000000 00000280 00000018
0x0078f7c0:  0001005c 00000000 00400000 00000000
0x0078f7d0:  00000064 00891cc2 000000c4 00000000
0x0078f7e0:  7bc27600 688df3d0 00000000 00000000
0x0078f7f0:  008a1ffc 008a2004 01322014 0078f820
Backtrace:
=>0 0x0040be65 IShellFolder_Release+0x9(This=<internal error>) [Y:\include\shobjidl.h:1707] in explorer (0x0078fe68)
  1 0x0040be65 make_explorer_window+0x82a(params=<internal error>) [Y:\programs\explorer\explorer.c:372] in explorer (0x0078fe68)
  2 0x0040be65 wWinMain+0x995(hinstance=<couldn't compute location>, previnstance=<couldn't compute location>, cmdline=<couldn't compute location>, cmdshow=<couldn't compute location>) [Y:\programs\explorer\explorer.c:927] in explorer (0x0078fe68)
  3 0x00410568 wmain+0xa8(argc=<couldn't compute location>, argv=<couldn't compute location>) [Y:\dlls\msvcrt\crt_wwinmain.c:55] in explorer (0x0078fef8)
  4 0x004104b8 wmainCRTStartup+0x68() [Y:\dlls\msvcrt\crt_wmain.c:60] in explorer (0x0078ff30)
  5 0x7b6293e0 in kernel32 (+0x293e0) (0x0078ff48)
  6 0x7bc5ca37 in ntdll (+0x5ca37) (0x0078ff5c)
  7 0x7bc5d258 in ntdll (+0x5d258) (0x0078ffec)
0x0040be65 wWinMain+0x995 [Y:\programs\explorer\explorer.c:927] in explorer: movl   0x0(%ecx),%eax
Unable to access file 'Y:\programs\explorer\explorer.c'
Modules:
Module  Address         Debug info  Name (60 modules)
PE  00400000-0048f000   Dwarf-4         explorer
PE  00b40000-00d5f000   Deferred        rpcrt4
PE  016a0000-01b2c000   Deferred        ole32
PE  01b30000-01bb6000   Deferred        winex11
PE  01cb0000-020ec000   Deferred        actxprxy
PE  62500000-628ff000   Deferred        oleaut32
PE  63080000-630aa000   Deferred        zlib1
PE  64a80000-64ad8000   Deferred        win32u
PE  65680000-65900000   Deferred        msvcrt
PE  66080000-66180000   Deferred        shlwapi
PE  66640000-6665c000   Deferred        version
PE  667c0000-66809000   Deferred        shcore
PE  67500000-67552000   Deferred        imm32
PE  67bc0000-6805e000   Deferred        comctl32
PE  684c0000-68610000   Deferred        combase
PE  68880000-68d46000   Deferred        user32
PE  69840000-69938000   Deferred        advapi32
PE  6aac0000-6ada1000   Deferred        ucrtbase
PE  6bbc0000-6bc59000   Deferred        sechost
PE  6bc80000-6bcec000   Deferred        explorerframe
PE  6da80000-6dc9c000   Deferred        gdi32
PE  70d80000-70e23000   Deferred        uxtheme
PE  71400000-720ab000   Deferred        shell32
PE  7b000000-7b51a000   Deferred        kernelbase
PE  7b600000-7b757000   Dwarf-4         kernel32
PE  7bc00000-7beba000   Dwarf-4         ntdll
ELF 7d000000-7d005000   Deferred        <wine-loader>
ELF 7f416000-7f41e000   Deferred        libxfixes.so.3
ELF 7f41e000-7f42b000   Deferred        libxcursor.so.1
ELF 7f42b000-7f440000   Deferred        libxi.so.6
ELF 7f440000-7f445000   Deferred        libxcomposite.so.1
ELF 7f445000-7f454000   Deferred        libxrandr.so.2
ELF 7f454000-7f462000   Deferred        libxrender.so.1
ELF 7f462000-7f469000   Deferred        libxxf86vm.so.1
ELF 7f469000-7f46e000   Deferred        libxinerama.so.1
ELF 7f46e000-7f47d000   Deferred        libmd.so.0
ELF 7f47d000-7f494000   Deferred        libbsd.so.0
ELF 7f494000-7f49b000   Deferred        libxdmcp.so.6
ELF 7f49b000-7f4a0000   Deferred        libxau.so.6
ELF 7f4a0000-7f4ce000   Deferred        libxcb.so.1
ELF 7f4ce000-7f61f000   Deferred        libx11.so.6
ELF 7f61f000-7f635000   Deferred        libxext.so.6
ELF 7f64d000-7f6d7000   Deferred        winex11.so
ELF 7f6f7000-7f723000   Deferred        libexpat.so.1
ELF 7f723000-7f772000   Deferred        libfontconfig.so.1
ELF 7f772000-7f795000   Deferred        libbrotlicommon.so.1
ELF 7f795000-7f7b2000   Deferred        libz.so.1
ELF 7f7b2000-7f881000   Deferred        libfreetype.so.6
ELF 7f881000-7f986000   Deferred        libm.so.6
ELF 7fe0e000-7fe18000   Deferred        libuuid.so.1
ELF 7fe18000-7fe55000   Deferred        libpng16.so.16
ELF 7fe6d000-7ffe0000   Deferred        win32u.so
ELF f7c00000-f7e28000   Deferred        libc.so.6
ELF f7e2f000-f7ede000   Deferred        ntdll.so
ELF f7ee0000-f7ee5000   Deferred        libpthread.so.0
ELF f7ee5000-f7eea000   Deferred        libdl.so.2
ELF f7eea000-f7ef4000   Deferred        libgtk3-nocsd.so.0
ELF f7ef7000-f7f05000   Deferred        libbrotlidec.so.1
ELF f7f05000-f7f0a000   Dwarf           libwine.so.1
ELF f7f14000-f7f49000   Deferred        ld-linux.so.2
Threads:
process  tid      prio    name (all IDs are in hex)
00000020 start.exe
    00000024    0     
00000038 services.exe
    0000003c    0     
    00000040    0     wine_rpcrt4_server
    0000004c    0     wine_rpcrt4_io
    00000050    0     wine_threadpool_worker
    00000064    0     wine_rpcrt4_io
    00000084    0     wine_rpcrt4_io
    00000090    0     wine_rpcrt4_io
    000000b0    0     wine_rpcrt4_io
    000000d8    0     wine_threadpool_worker
    000000dc    0     wine_threadpool_worker
    000000e0    0     wine_threadpool_waitqueue
    00000114    0     wine_rpcrt4_io
00000044 svchost.exe
    00000048    0     
    00000054    0     
    00000058    0     wine_sechost_service
0000005c winedevice.exe
    00000060    0     
    00000068    0     
    0000006c    0     wine_sechost_service
    00000070    0     
    00000074    0     
    00000078    0     
    000000e4    0     
0000007c winedevice.exe
    00000080    0     
    00000088    0     
    0000008c    0     wine_sechost_service
    00000094    0     
    00000098    0     
    0000009c    0     
    000000a0    0     
    000000a4    0     
000000a8 plugplay.exe
    000000ac    0     
    000000b4    0     
    000000b8    0     wine_sechost_service
    000000bc    0     wine_rpcrt4_server
000000e8 conhost.exe
    000000ec    0     
000000f0 (D) C:\windows\syswow64\explorer.exe
    000000f4    0 <== 
    00000138    0     wine_threadpool_worker
    00000154    0     
000000f8 explorer.exe
    000000fc    0     
    00000100    0     
    00000104    0     wine_rpcrt4_server
    00000134    0     wine_rpcrt4_io
    0000013c    0     wine_threadpool_worker
    00000140    0     wine_rpcrt4_io
    00000144    0     wine_rpcrt4_io
    00000148    0     wine_rpcrt4_io
0000010c rpcss.exe
    00000110    0     
    00000118    0     
    0000011c    0     wine_sechost_service
    00000120    0     wine_rpcrt4_server
    00000124    0     wine_rpcrt4_server
    00000128    0     wine_rpcrt4_io
    0000012c    0     wine_threadpool_worker
    00000130    0     wine_rpcrt4_io
System information:
    Wine build: wine-8.0-rc1 (Debian 8.0~rc1~repack-1)
    Platform: i386 (WOW64)
    Version: Windows 7
    Host system: Linux
    Host version: 6.1.0-1-amd64

I finally noticed this little message on the command line:

00fc:err:wineboot:ProcessStartupItems Couldn't get desktop folder.

Who would have thought that this would be significant? When I bothered to read it, I checked:

[bgstack15@pcb-006|/home/bgstack15/.wine/drive_c/users]$ ll bgstack15/
total 4
drwxr-xr-x 4 bgstack15 bgstack15 4096 Jan 10 21:00 AppData/

When most "C:\users\bgstack15\" directories resemble:

$ ls -l ~/.wine/drive_c/users/bgstack15
total 4
drwxr-xr-x 4 bgstack15 bgstack15 4096 Jan 10 21:00 AppData/
lrwxrwxrwx 1 bgstack15 bgstack15   21 Jan 10 21:10 Desktop -> /home/bgstack15/Desktop/
lrwxrwxrwx 1 bgstack15 bgstack15   23 Jan 10 21:10 Documents -> /home/bgstack15/Documents/
lrwxrwxrwx 1 bgstack15 bgstack15   23 Jan 10 21:10 Downloads -> /home/bgstack15/Downloads/
lrwxrwxrwx 1 bgstack15 bgstack15   19 Jan 10 21:10 Music -> /home/bgstack15/Music/
lrwxrwxrwx 1 bgstack15 bgstack15   22 Jan 10 21:10 Pictures -> /home/bgstack15/Pictures/
lrwxrwxrwx 1 bgstack15 bgstack15   20 Jan 10 21:10 Videos -> /home/bgstack15/Videos/

So I fixed this weird wine error with:

cd ~/.wine/drive_c/users/bgstack15 ; for word in Desktop Documents Downloads Music Pictures Videos ; do ln -s ~/"${word}" . ; done

Which means that I made the symlinks that go to those real directories, and then Wine would work! This happened because the wineprefix came from a different user in Linux, so even the drive C user directory was different, and somehow it just didn't bother making a fully fleshed-out user directory there.

Devuan updates for January 2023 and FreeIPA

As of January 10, the latest monthly OS updates has introduced a problem only for new installs. Package freeipa-client depends on a package, python3-ipalib-hbac, that has a hard dependency on python3<3.11.

And of course I wanted to set up a system with Devuan Ceres at that exact day. It has since been fixed, with the release of src:sssd_2.8.1-2 today.

I had to set up an apt repo connection to snapshot.debian.org, in file /etc/apt/sources.list.d/snapshot.list.

deb [check-valid-until=no] http://snapshot.debian.org/archive/debian/20221215T151605Z/ unstable main

Then run:

sudo apt-get install python3=3.10.6-3 python3-minimal=3.10.6-3 libpython3-stdlib=3.10.6-3

The FreeIPA client install process also needs this:

sudo apt-get install python3-cryptography=3.4.8-2

To get around this error on the ipa-client-install invocation.

AttributeError: module 'cryptography.utils' has no attribute 'register_interface'. Did you mean: 'verify_interface'?

Journald use real syslog

I have a few systems that use systemd. I feel that it is a fine service manager, but the whole systemd project keeps subsuming other functions and getting more bloated, and also chooses terrible default values! Don't give me any lip about how it's a function of the distro's packaging that makes systemd's defaults bad: I'm talking about CentOS here, so THE systemd distro. Its package defaults are systemd defaults.

To make sure you actually have logs that last longer than a reboot, you need to change from the default. Make sure rsyslog is installed, and then modify journald configs.

Inside file /etc/systemd/journald.conf:

[Journal]
Storage=persistent

Also mkdir /var/log/journal. And then systemctl restart systemd-journald because they love their name so much you need it add it in front of many things. As if there's some other possible "journald" that people could possibly use.

So after this manual configuration, you can actually investigate what happened before your system shut down. Thanks a lot, [censored].

References

  1. https://www.redhat.com/sysadmin/store-linux-system-journals

My Vaultwarden backup process

I have decided to turn my pre-production-worthy instance of Vaultwarden to a production-worthy one. That means I needed backups.

So I wrote my own quick-and-dirty shell script to handle that for me. The first part backups up the sqlite database in the proper fashion. The main script collects that bup database file, and the other relevant files for Vaultwarden, and tarballs them to the correct location!

files/2023/01/listings/bup-vw-db.sh (Source)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/sh
# File: vm4:/home/vaultwarden/bup-vw-db.sh
# Location: vm4
# Author: bgstack15
# Startdate: 2023-01-02-2 16:54
# Title: Bup Vaultwarden database
# Project: bup-vw
# Purpose: Backup Vaultwarden database
# History:
# Usage: called by bup-vw.sh
# Reference:
#    https://github.com/dani-garcia/vaultwarden/wiki/General-%28not-docker%29
#    https://stackoverflow.com/questions/25675314/how-to-backup-sqlite-database/25684912#25684912
# Improve:
# Dependencies:
#    sqlite3
#    must run as root!
# Documentation: see bup-vw.sh
INDB=/home/vaultwarden/vw/bitwarden/db.sqlite3
test -z "${LOCALOUTDIR}" && LOCALOUTDIR=/home/vaultwarden/bup
test -z "${OUTFILE}" && OUTFILE="${LOCALOUTDIR}/vw.$( date "+%F" ).sq3"
echo ".backup ${OUTFILE}" | sqlite3 "${INDB}"
# the main script will tarball it with the relevant files
#gzip "${OUTFILE}"
chown vaultwarden:vaultwarden "${OUTFILE}"
echo "${OUTFILE}"
files/2023/01/listings/bup-vw.sh (Source)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#!/bin/sh
# File: vm4:/home/vaultwarden/bup-vw.sh
# Location: vm4
# Author: bgstack15
# Startdate: 2023-01-02-2 16:54
# Title: Bup Vaultwarden
# Project: bup-vw
# Purpose: Backup Vaultwarden contents
# History:
# Usage: called by cron: 70_vaultwarden_cron
# Reference:
#    https://github.com/dani-garcia/vaultwarden/wiki/General-%28not-docker%29
#    photorprism/bup-pp-db.sh
# Improve:
# Dependencies:
#    sudo access for user vaultwardern to run bup-vw-db.sh, /etc/sudoers.d/70_vaultwarden_bup_sudo
# Documentation:
#    README-vw-bup.md

workdir="$( dirname "$( readlink -f "${0}" 2>/dev/null )" 2>/dev/null || echo "${PWD}" )"
#echo "workdir=${workdir}"
test -z "${CONFFILE}" && CONFFILE="${workdir}/bup-vw.conf"
test -e "${CONFFILE}" && . "${CONFFILE}"
test -z "${LOGFILE}" && LOGFILE="/mnt/public/Support/Systems/vm4/var/log/vaultwarden/bup-vw.$( date "+%F" ).log"

_return() {
   return ${1}
}

main() {
   export OUTDIR=/mnt/public/Support/Systems/vm4/vw/vaultwarden
   export LOCALOUTDIR=/home/vaultwarden/bup
   export OUTFILE="${LOCALOUTDIR}/vw.$( date "+%F" ).sq3"
   export OUTTARBALL="${OUTDIR}/vw.$( date "+%F" ).tgz"
   # fail early if network mount is not there
   if ! test -w "$( dirname "${OUTFILE}" )" ;
   then
      echo "Fatal! Unable to write to directory for ${OUTTARBALL}: Aborted."
      exit 1
   fi
   # run the bup-vw-db.sh script, collect other things, make a tarball
   generated_file="$( sudo /home/vaultwarden/bup-vw-db.sh )"
   if ! test -f "${generated_file}" ;
   then
      echo "Fatal! Unable to find exported database file ${OUTFILE}: Aborted."
      exit 1
   fi
   relative_generated_file="bup/$( basename "${generated_file}" )"
   tar -zcf "${OUTTARBALL}" -C /home/vaultwarden vw/docker-compose.yml vw/.env vw/bitwarden "${relative_generated_file}"
   find "${OUTTARBALL}"
}

# Determine if this script was dot-sourced
sourced=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
  case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
elif [ -n "$KSH_VERSION" ]; then
  [ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ] && sourced=1
elif [ -n "$BASH_VERSION" ]; then
  (return 0 2>/dev/null) && sourced=1 
else # All other shells: examine $0 for known shell binary filenames
  # Detects `sh` and `dash`; add additional shell filenames as needed.
  case ${0##*/} in sh|dash) sourced=1;; esac
fi

# So, if not dot-sourced, and this is run by cron, add logging
if test $sourced -eq 0;
then
   if echo " ${@} " | grep -q cron ;
   then 
      main 2>&1 | plecho | tee -a "${LOGFILE}"
      response=$?
      printf '\n' | tee -a "${LOGFILE}"
   else
      main
      response=$?
   fi
fi
_return ${response}

Cron entry

30 06  *  *  *  vaultwarden  /home/vaultwarden/bup-vw.sh cron 1>/dev/null 2>&1

I don't have any rotation processes set up yet, so I need to check in a few months and built a retention plan.

References

  1. https://github.com/dani-garcia/vaultwarden/wiki/General-%28not-docker%29

Split single mp3 of whole CD

I had a single mp3 recording of an entire CD that I wished to split into the individual tracks. Thankfully, people smarter than me have already solved this problem on the Internet.

The first order of business was to find the track times. Thankfully, musicbrainz.org delivered TOC info. It looks like this but I wasn't able to figure how to download a cddb-style TOC file, but that's OK. I used Firefox's CTRL+click-and-drag functionality to select just a column of the start times of the tracks, and pasted them in a file.

I ended up having to subtract lots of seconds, because my recording was somehow skewed. I also remember that ffmpeg can only be so precise with seconds (due to how compression works?) so that affected things to. So my track times ended up a little different than the TOC I used.

File ./times

00:02:21
00:04:32
00:08:32
00:12:25
00:15:12
00:19:14
00:22:53
00:25:52
00:29:58
00:35:03
00:39:27
00:42:48
00:46:54
00:48:54
00:53:33
00:58:17
01:02:44

Notice how I had to add the end time of the last track! Armed with these times, I used a small script from Unix.SE, of course.

split.sh (Source)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/bin/bash
# Adapted from https://unix.stackexchange.com/questions/659789/automating-the-splitting-of-a-large-mp3-file-with-ffmpeg-into-multiple-files-in
x="00:00:00"
z=0
filename=$(basename -- "$2")
ext="${filename##*.}"
filename="${filename%.*}"
initcmd="ffmpeg  -nostdin -hide_banner -loglevel error -i $2"
while read y ; do
   initcmd+=" -ss $x -to $y -c copy $filename$z.$ext"
   let "z=z+1"
   x=$y 
done < $1
${initcmd}

Then I ran the script:

./split.sh ./times album.mp3

I had to subtract lots of seconds, smeared across the whole track. I suspect the whole recording was the tiniest bit fast, but I also remember that ffmpeg plays fast and loose with relative time specifications.

But now, I had an mp3 file for each track! Then I used puddletag and its "Tag Sources" window to load the musicbrainz information for these songs.