Download voobly mods from browser

If you inspect a Voobly mods page (such as Jurassic 2 for Age of Empires II: The Conquerors) you can see that the “Download” button is a custom protocol.

<form action="voobly://client.voobly.com:17600/?service=Package&amp;uid=0&amp;session=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&amp;arg=voobly.com**gamemods*13*Jurassic[space]2" method="post">

You can inspect what your system is currently configured to use for a custom protocol of x-scheme-handler/voobly:

$ xdg-mime query default x-scheme-handler/voobly
Voobly.desktop

And if you need to manually set it, you can do so with:

xdg-mime default Voobly.desktop x-scheme-handler/voobly

Interestingly enough, I did not need to fully qualify the path to Voobly.desktop. I suspect that is because it is in one of the XDG_DATA_DIRS default locations which is the ~/.local/share/applications/
My Voobly.desktop actually calls a wrapper shell script, which I augmented to pass the parameters to voobly.exe.

$ cat ~/.local/share/applications/Voobly.desktop
[Desktop Entry]
Name=Voobly
Exec=/home/bgstack15/.wine/voobly.sh %u
Type=Application
StartupNotify=true
Path=/home/bgstack15/.wine/dosdevices/c:/Program Files/Voobly
Icon=/home/bgstack15/.local/share/icons/voobly.png
StartupWMClass=voobly.exe
Comment=Play Age of Empires 2 online
Terminal=false
Categories=Game;StrategyGame;
MimeType=x-scheme-handler/voobly;
StartupWMClass=voobly.exe

The shell script voobly.sh has that $@ (all parameters), which will pass any of those values from the %u from the Desktop file.

#!/bin/sh
env WINEPREFIX="/home/bgstack15/.wine" /usr/bin/wine C:\\Program\ Files\\Voobly\\voobly.exe $@

Side note: The STAGING_WRITECOPY=1 environment variable is not necessary for wine with versions starting approximately 2020. I cannot pinpoint the exact time or version, unfortunately. The Wine release notes make passing references to copy-on-write or similar for versions 4.4, 4.2, and 2.18 but they don’t seem definitive enough to match what I recall. But for Devuan Ceres I haven’t needed the winehq apt repo for a long time now. (And Fedora has always used the staging version of Wine.)

Or you could just copy extant Voobly mods from one installed system to another underneath directory ~/.wine/drive_c/Program\ Files/Microsoft\ Games/Age\ of\ Empires\ II/Voobly\ Mods/AOC/. There are several directories in there, particularly Local Mods.

$ ls -al ~/.wine/drive_c/Program\ Files/Microsoft\ Games/Age\ of\ Empires\ II/Voobly\ Mods/AOC/Local\ Mods/
total 16
drwxrwxr-x. 3 bgstack15 bgstack15 4096 Jan 24  2020 Short Walls/
drwxrwxr-x. 3 bgstack15 bgstack15 4096 Jan 29  2020 Small Trees/
drwx------. 4 bgstack15 bgstack15 4096 Oct  6  2019 Spectator Dashboard/
drwx------. 4 bgstack15 bgstack15 4096 Oct  6  2019 Spectator Overlay/

Happy downloading mods!

Screenshot of Voobly mods web page

References

Weblinks

  1. xdg – Create a custom URL Protocol Handler – Unix & Linux Stack Exchange
  2. Associate steam protocol (steam://) with linux steam client | Vivaldi Forum
  3. How do I associate a protocol with a program? / Newbie Corner / Arch Linux Forums
  4. Ripped directly from How to download mods from browser (Linux)

Shell: find duplicate files and replace most with symlinks

This is probably not the best solution, but it’s the one I wanted to work with. I intended to reduce disk space of 3 similar projects. Here is my shell script to find duplicate files (by md5sum) and replace any secondary file with a symlink to the relative path of the original file.

#!/usr/bin/env sh
# File: set-symlinks.sh
# License: CC-BY-SA 3.0
# Author: bgstack15
# Startdate: 2020-02-07 14:03
# Title: Script that Replaces Duplicate Files with Symlinks
# Purpose:
# History:
# Usage:
# Reference:
#    https://stackoverflow.com/questions/2564634/convert-absolute-path-into-relative-path-given-a-current-directory-using-bash
# Improve:
# Dependencies:
#    coreutils >= 8.23

INDIR=~/src/project

#results="$( find "${INDIR}" ! -type d ! -type l -exec md5sum {} + | sort )"
find "${INDIR}" ! -type d ! -type l -exec md5sum {} + | sort | \
   awk '{a[$1]=a[$1]":"$2} END {for (i in a){print a[i]}}' | sed -r -e 's/^://;' | \
   while IFS=':' read main child1 child2 child3 child4 ;
   do
      x=0
      while test $x -lt 4 ;
      do
         x=$(( x + 1 ))
         eval thischild="\${child$x}"
         if test -n "${thischild}" ;
         then
            linkname="$( realpath --relative-to "$( dirname "${thischild}" )" "${main}" 2>/dev/null )"
            test -n "${DEBUG}" && echo "ln -sf ${linkname} ${thischild}" 1>&2
            test -z "${DRYRUN}" && ln -sf "${linkname}" "${thischild}"
         fi
      done
   done

I was going to do this task for myself by hand, but then a quick investigation showed 73 files that were duplicates. Because of the small size of the project, I decided to just run it in shell and not revert to Python. I don’t need efficiency; I just need to run it once, really.

The tricky bits are in the very front of the logic. The awk associate array builds a list of all filenames that correspond with an md5sum. Then, stripping out the leading colon (separator), I pipe the output to a while for easy variable naming. And then loop a few times (hard-coded to 4) and if item number X exists, get the relative path to the main file, and force create the symlink.

Monitor owner and permissions changes

A user on the Fedora forum asked for assistance monitoring owner and permissions changes to files. I whipped up a general solution in shell.

It uses a compressed database to store the last run, and will show the changes of the requested attributes of each file.

Here’s some of the business logic.

   # not empty
   test -n "${CO_DEBUG}" && echo "Comparing ${CO_INPUT} to database ${CO_OUTPUT}"

   # learn current status
   scan_dir "${CO_INPUT}" > "${CO_TMPFILE}"

   # compare to database
   zcat "${CO_OUTPUT}" | diff -W300 --suppress-common-lines -y "-" "${CO_TMPFILE}"

   # replace database
   cat "${CO_TMPFILE}" | gzip > "${CO_OUTPUT}"

And the scan function is pretty simple. Just change what stat outputs if you want to monitor different file characteristics.

scan_dir() {
   # call: scan_dir "${CO_INPUT}"
   # output: listing of hash, owner+perm hash for each file
   local td="${1}"

   find "${td}" -exec stat -L -c '%u,%U,%g,%G,%a,%n' {} + 2>/dev/null | sort -t ',' -k6
}

The script stores its compressed databases in /var/cache/check-owners/, and it will make files named based on the base directory it scans, so /home would be db file /var/cache/check-owners/co.home.db.gz.

You could write a cron entry to call this once a day on a particular directory and email the output to you. A poor man’s AIDE, if you will.

Convert m4a to mp3 while preserving audio quality

If you have a set of m4a files and want to automatically convert them to mp3 so you can tag them the right way, use a snippet I wrote.

See the code in its proper formatting at https://gitlab.com/bgstack15/former-gists/blob/master/to-mp3/m4a-to-mp3.sh.

#!/bin/sh
# reference: https://superuser.com/questions/704493/ffmpeg-convert-m4a-files-to-mp3-without-significant-loss-of-information-quali/704535#704535

logfile="/mnt/bgstack15/log/m4a-to-mp3.$( date -u "+%FT%H%M%SZ" ).log"

func() {
for word in "$@" ;
do
   echo "Entering item ${word}";
   outdir="${word}/mp3" ; mkdir "${outdir}" || exit 1 ;
   find "${word}" -type f \( -regex '.*M4A' -o -regex '.*m4a' \) | while IFS='\0' read infile ;
   do
      test -f "${infile}" && echo "Found file: \"${infile}\"" || echo "INVALID! ${infile}"
      outfile="$( echo "${infile}" | sed -r -e "s/\.m4a/\.mp3/i" )"
      echo  ffmpeg -i \"${infile}\" -codec:v copy -codec:a libmp3lame -q:a 2 \"${outfile}\"
      yes | ffmpeg -i "${infile}" -codec:v copy -codec:a libmp3lame -q:a 2 -y "${outfile}" ; test -n "${outdir}" && /bin/mv -f "${outfile}" "${outdir}/" ;
      sleep 2 ;
   done
done
}


time func "$@" | tee -a "${logfile}"

Set word-read permissions on python libs

In an environment where the default umask is 0077 or similar, the pip utility for installing python libraries can set up new files that cannot be read by all users.

I dropped this script into my ~/bin dir so I can enforce other-read permissions easily on all the python libs. I wrote this script after hours and hours of troubleshooting python libs just to find out it’s an old-school permissions issue.


#!/bin/sh
# File: /usr/local/bin/set-readable-python-libs
worldreadpythonlibs_version="2019-05-31a"
for word in /usr/{local/,}lib{,64}/python* ;
do
find ${word} ! -perm -o+rX -exec chmod g+rX,o+rX {} + 2>/dev/null
done

 


#!/bin/sh
worldreadpythonlibs_version="2018-04-06a"
for word in /usr/lib{,64}/python2.7/site-packages ;
do
find ${word} -exec chmod g+rX,o+rX {} \;
done

Enforce ansible ownership of ansible files

In a team environment, using ansible as a common user requires a few considerations. One is the ownership and access of the files. Here is a script I placed in my ~/bin directory so I can correct accessibility of all the files


#!/bin/sh
ansibleown_version="2018-04-04a"
tu=ansible
tg="$( id -ng "${tu}" )"
for word in $@ ;
do
# set group accessible
find ${word} -exec chown "${tu}:${tg}" {} \; -exec chmod g+rwX {} \;
# set setgid and sticky bits
find ${word} -type d -exec chmod g+s,o+t {} \;
done

view raw

ansible-own.sh

hosted with ❤ by GitHub

Insert filename in Libreoffice Calc spreadsheet ods file in Linux

In LibreOffice Writer, you can use the “Insert Field” tool to easily insert the filename into the document. In Calc, it’s a little different, but still possible. You can access the raw filename and sheet with CELL(“filename”). To make it pretty, use a longer formula.

=LEFT(REPLACE(LEFT(CELL("filename"),FIND("'",CELL("filename"),2)),1,9,"/"),LEN(REPLACE(LEFT(CELL("filename"),FIND("'",CELL("filename"),2)),1,9,"/"))-1)

References

  1. https://listarchives.libreoffice.org/global/users/msg24734.html
  2. search libreoffice calc insert filename

Grep odt file

Overview

In the GNU/Linux world, you spend a lot of time on the command line. Searching a text file is a piece of cake:
grep -iE "expression" file1

You might even use a gui, and inside that gui you might even use an open-source office suite for all those times that plain text isn’t enough. But what about when you want to search one of those odt files you vaguely remember is some form of xml?

Easy. You use unoconv or odt2txt (look those up in your package manager) and then grep the outputted file. Or you can use the –stdout option.

unoconv -f txt foo.odt

unoconv -f txt --stdout foo.odt | grep -iE "Joe Schmoe"

History

I first started tackling this problem by figuring out how to access the xml inside. I learned an odt file is zipped, but a tar xf didn’t help. Turns out it was some other compression, that unzip manages.

I also had to actually learn the tiniest bit of perl, as regular GNU grep (and I inferred sed) doesn’t do non-greedy wildcard matching.

So I got this super-complicated one-liner going before I decided to try a different approach and discovered the unoconv and odt2txt applications.

time unzip -p foo.odt content.xml | sed -e 's/\([^n]\)>\n(.*)<\/\1>/\2/;s/<text:h.*?>(.*)<\/text:h>/\1/;' -e 's/<style:(font-face|text-properties).*\/>//g;' | sed -e "s/'/\'/g;s/"/\"/g;s/<text:.*break\/>//g;"

 

References

Weblinks

  1. Unzipping an odt file https://ubuntuforums.org/showthread.php?t=899179&s=3aa7c303c4a5655e039600c4082d7a2c&p=5653494#post5653494
  2. Perl non-greedy wildcard matching http://stackoverflow.com/a/1103177/3569534