Manually fix volume in mp3 file

I am shamelessly ripping off a superuser answer: How can I normalize audio using ffmpeg?.

Option 3: Manually normalizing audio with ffmpeg

In ffmpeg you can use the volume filter to change the volume of a track. Make sure you download a recent version of the program.

This guide is for peak normalization, meaning that it will make the loudest part in the file sit at 0 dB instead of something lower. There is also RMS-based normalization which tries to make the average loudness the same across multiple files. To do that, do not try to push the maximum volume to 0 dB, but the mean volume to the dB level of choice (e.g. -26 dB).

Find out the gain to apply

First you need to analyze the audio stream for the maximum volume to see if normalizing would even pay off:

ffmpeg -i video.avi -af "volumedetect" -vn -sn -dn -f null /dev/null

Replace /dev/null with NUL on Windows.
The -vn, -sn, and -dn arguments instruct ffmpeg to ignore non-audio streams during this analysis. This drastically speeds up the analysis.

This will output something like the following:

[Parsed_volumedetect_0 @ 0x7f8ba1c121a0] mean_volume: -16.0 dB
[Parsed_volumedetect_0 @ 0x7f8ba1c121a0] max_volume: -5.0 dB
[Parsed_volumedetect_0 @ 0x7f8ba1c121a0] histogram_0db: 87861

As you can see, our maximum volume is -5.0 dB, so we can apply 5 dB gain. If you get a value of 0 dB, then you don’t need to normalize the audio.

Apply the volume filter:

Now we apply the volume filter to an audio file. Note that applying the filter means we will have to re-encode the audio stream. What codec you want for audio depends on the original format, of course. Here are some examples:

  • Plain audio file: Just encode the file with whatever encoder you need:
    ffmpeg -i input.wav -af "volume=5dB" output.mp3

    Your options are very broad, of course.

ldapsearch find disabled users in Active Directory

If you want to find the disabled users in your AD environment, you can use a specific filter. Additionally, due to the number of records returned, I had to turn on paging (pr = some arbitrarily high value) so I could actually retrieve more than just the first 1000 entries.

echo '' | ldapsearch -E 'pr=4500' -z max -b 'dc=prod1,dc=example,dc=com' -s 'sub' -x -D 'CN=B Stack15,OU=Users,DC=prod1,DC=example,DC=com' -W -H 'ldaps://' '(&(objectCategory=person)(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=2))' dn

The userAccountControl item in the search filter stores various useful information. The := operator is a bitmask.



  2. Found from web search string “userAccountControl:1.2.840.113556.1.4.803”

Compile Pale Moon 28 on Fedora 27


Pale Moon 28 was released on August 16, 2018. I package it myself on Fedora because I don’t see it in the fedora repositories, and plus I like the experience of assembling packages myself. For a basic compile (not in an rpm), you can follow these instructions.

Install dependencies

Install the whole set of packages listed on the Pale Moon site (reference 2) or CentOS7

sudo dnf -y install gtk2-devel dbus-glib-devel autoconf213 yasm mesa-libGL-devel alsa-lib-devel libXt-devel zlib-devel openssl-devel sqlite-devel bzip2-devel pulseaudio-libs-devel
sudo dnf -y groupinstall 'Development Tools'

Install the dependencies I found.

sudo dnf -y install GConf2-devel notification-daemon

Use autoconf 2.13

Pale Moon depends on autoconf 2.13. Thankfully, it’s in the Fedora repos, but changing the main autoconf link to point to this specific version will save a bunch of headache later. Be aware that this step exactly as shown will change your system’s default autoconf. I’m sure this is a crude way to do it, but aren’t build systems throwaway systems nowadays?

autoconfver="$( autoconf --version 2>/dev/null | awk 'NR==1 {print $NF*100;} END {print "0";}' | head -n1 )"
test ${autoconfver} -ne 213 &&amp test ${autoconfver} -gt 0 && sudo mv /usr/bin/autoconf /usr/bin/autoconf-${autoconver} 2>/dev/null ; sudo ln -sf autoconf-2.13 /usr/bin/autoconf

Fetch source

Pale Moon likes to compile in ~/pmsrc. Don’t change it. It just makes it easier.

mkdir ~/pmsrc ~/pmbuild
cd ~/pmsrc
git clone .

Prepare to compile

Use the recommended .mozconfig from the Pale Moon site (reference 2)

touch "${tf}"
cat <<'EOFMOZCONFIG' > "${tf}"
mk_add_options AUTOCLOBBER=1
mk_add_options MOZ_OBJDIR=/home/$USER/pmbuild/
ac_add_options --enable-application=palemoon
ac_add_options --enable-optimize="-O2"
# Please see for restrictions when using the official branding.
ac_add_options --enable-official-branding
ac_add_options --enable-default-toolkit=cairo-gtk2
ac_add_options --enable-jemalloc
ac_add_options --enable-strip
ac_add_options --with-pthreads
ac_add_options --disable-tests
ac_add_options --disable-eme
ac_add_options --disable-parental-controls
ac_add_options --disable-accessibility
ac_add_options --disable-webrtc
ac_add_options --disable-gamepad
ac_add_options --disable-necko-wifi
ac_add_options --disable-updater
ac_add_options --x-libraries=/usr/lib


These instructions include saving the output to a log file, but that’s not necessary.

mkdir ~/log
cd ~/pmsrc
{ time ./mach build && time ./mach package ; } | tee -a ~/log/pmsrc.$( date "+%F-%H%M%S" ).log
echo done



  3. Compiling Pale Moon web browser on Fedora (published 2018-02-09)

ansible control hosts entries

Somebody should make a module of this. In the meantime, here’s a simple hosts entries task in ansible.

- name: hosts entries
    path: "{{ etc_hosts_file | default('/etc/hosts') }}"
    regexp: '^\s*{{ item.ip }}.*'
    line: "{{ item.ip }}   {{ item.hostnames | sort() | join(' ') }}"
    backup: yes
  - "{{ hosts_entries }}"

Use a list like so:

- { ip: '', hostnames: ['',''] }
- { ip: '', hostnames: [''] }

Playbook that Converts Local to AD Users

If you like removing local users in favor of the domain users, check out how to do that in shell at my post Convert Local to AD Users.

If you want to do it at scale, you can wrap it with a bit of ansible. Check out the full thing with syntax highlighting on gitlab:

# Only use one thisuser at a time, for the fail/changed logic to work correctly!
# usage: ansible-playbook -l targethost1 /etc/ansible/books/stable/cladu.yml -v -e 'thisuser=joneill'

- name: book that runs cladu
  hosts: all
  become: yes
  become_user: root
  become_method: sudo

  - name: copy in rpm
      src: /etc/ansible/files/bgscripts-core-1.3-9.noarch.rpm
      dest: /tmp/
      mode: 0644

  - shell: rpm -U --nodeps /tmp/bgscripts-core-1.3-9.noarch.rpm
      warn: no
    register: this_rpm
    - 'not ("is already installed" in this_rpm.stdout or "is already installed" in this_rpm.stderr or this_rpm.rc == 0)'
    - 'not ("is already installed" in this_rpm.stdout or "is already installed" in this_rpm.stderr)'

  - shell: /usr/share/bgscripts/work/ -r -g '{{ thisuser }}'
      warn: no
    register: this_shell
    - 'not ("Skipped" in this_shell.stdout or "Failed" in this_shell.stdout)'
    - '"Failed" in this_shell.stdout'

Playbook that resets user password

As an admin, it’s my job to reset user passwords who still use local accounts. I’m working on converting users to domain accounts, but in the mean time, here’s my little book.
Check it out with context highlighting at

# Dependencies: from bgscripts-core, installed on ansible server
- name: book that resets password for thisuser
  hosts: all
  become: yes
  become_user: root
  become_method: sudo

  - name: generate pw hash
    shell: /usr/share/bgscripts/py/ "{{ thispw | default('TEMP_PASSWORD_HERE') }}"
    register: thispw
    delegate_to: localhost
    changed_when: false
    run_once: true

  - user:
      name: "{{ thisuser }}"
      password: "{{ thispw.stdout }}"

  - shell: passwd -e "{{ thisuser }}"
      warn: no

Playbook that runs du

Here’s a playbook I wrote for my Ops team. Apparently they’re impressed by the simplest things.

# Filename: du.yml
# Author: bgstack15
# Startdate: 2018-07-24
# Title: Book that Runs du
# Purpose: Run du with elevated privileges, primarily for auditing /home
# History:
# Usage:
#    In ansible tower.
#    Variables:
#       path=/home   What location to check
#       max_depth=1  How many directories to read in to
#       tail=5       How many to show
# Reference:
# Improve:
# Documentation:

- name: book that runs du
  hosts: all
  become: yes
  become_user: root
  become_method: sudo

  - name: du
    shell: /usr/bin/du -xBM --max-depth "{{ max_depth | default('1') }}" "{{ path | default('/home') }}" | sort -n | tail -n "{{ tail | default('5') }}" | sed -r -e 's/[[:space:]]+/ /g;'
      warn: no
    register: du

  - debug:
      msg: "{{ du.stdout_lines }}"

Find certs that will expire soon

I wrote a new utility to discover ssl certificates that will need to be replaced soon.
You can go view at gitlab. It does need bgscripts as a dependency, like most of my complex scripts.

The flow of the script is pretty basic.

   # Find all cert files
   all_certs="$( find_all_certs "${FEC_FIND_METHOD}" "${FEC_EXCLUDE_DIRS}" "${FEC_FILE_FILTER}" )"
   debuglev 7 && { echo "===== ALL CERTS FOUND that match file filter and outside excluded dirs" ; echo "${all_certs}" ; } 1>&2

   # Limit list to those that will expire within the limit
   dangerous_certs="$( echo "${all_certs}" | limit_certs "${FEC_DATE}" )"
   debuglev 6 && { echo "===== CERTS that will expire soon" ; echo "${dangerous_certs}" ; } 1>&2

   # Notify
   printf "%s\n" "---- CERTS -----"
   display_certs "${dangerous_certs}" 0

I have not added verbosity flags, because picking which attributes to read and display for different verbosity levels (or, displaying arbitrary fields based on a parameter) would be hard. And I didn’t fully-feature it because it really is just an academic project. Turns out I don’t need this tool in my environment.

Frankly, the coolest thing about this script is that the find_all_certs function can use different ways to discover the files: find, or locate, or read standard input. Providing a mechanism for common field and exclude syntax, that can be munged to the right formats for the different ways to search, was the most interesting task of this script. Parsing openssl x509 output is just bog-standard awk logic.