I spend 3 hours per day in commute. And while generally it’s tempting to be on my laptop, or staring at my phone, it’s not healthy for your eyes. (And you get headaches etc).

Solution consists of noise-cancelling headphones and audio files to listen. Regular music and audio books are handled by iTunes, that’s not an issue. But I want to have a separate application for lectures that I find on youtube or elsewhere, or stand-up commedians, meditation and breathing exercises.

VLC iPhone App to the rescue

Good things about VLC mobile:

  • it can play while phone is locked (unlike Youtube app)
  • it can organize content by directories
  • it can display embedded APIC attached picture of mp3 files (for easier navigation)

Some bad quirks of the VLC mobile:

  • directories are determined by album names in ID3V2,
  • not all mp3 have ID3V2 tags set properly, you have to set it yourself,
  • it will cache background image of mp3 files,
  • you might need to reinstall the VLC app if it does not recognize background images.

With that being said my solution consists 1 python helper script and a following directory structure with all mp3s:

./tag.py
./Comedian 1/skit 1.mp3
./Comedian 1/skit 2.mp4
./Comedian 2/background.jpg
./Comedian 2/skit 1.mp3
./Lecture 1/background.jpg
./Lecture 1/Lesson 1.mp3
./Lecture 1/Lesson 2.mp3

I made a helper script to

  • convert any mp4s or webms to mp3s
  • set album name, song title, track name, so that VLC content resembles the directory
  • set the background image of the mp3 of background.jpg

Big warnings:

  • This is destructive operation, and your files can lose original ID3V2 tags, only use this on duplicate of files (keep the backup)
  • background.jpg should have dimensions 436x436 (smaller dimensions don’t work)

How to copy?

Use iTunes file sharing while iPhone is connected to laptop with lightning cable.

tag.py

#!/usr/local/bin/python
 
import os
import sys
import subprocess
import re
import mutagen, mutagen.id3
 
D = '/Users/emir/Commute Audios/'
 
for d, dirs, files in os.walk(D):
    videos = [f for f in files if f.endswith('.mp4') or f.endswith('.webm')]
    if d:
        d += '/'
    for video in videos:
        mp3 = video.rsplit('.', 1)[0]+'.mp3'
        if os.path.exists(d + mp3):
            print('Consider deleting: ' + d + video)
            continue
        subprocess.check_call([
            "ffmpeg",
            "-i", d + video,
            "-c:a", "libmp3lame",
            "-q:a", "4",
            d + mp3,
        ])
 
 
def check_and_apply(filename, edits, APIC=None, PRIV=None):
    id3 = mutagen.id3.ID3(filename)
    mutation = False
    for name, value in edits.items():
        if name not in ['TIT2', 'TRCK', 'TPE1', 'TALB']:
            raise Exception("not implemented")
 
        cur_frame = None
        for frame in id3.values():
            cur_name = repr(frame)[0:4]
            if cur_name == name:
                cur_frame = frame
                break
 
        if cur_frame is None:
            cur_frame = mutagen.id3.Frames[name](encoding=3, text='')
            id3.add(cur_frame)
 
        cur_value = cur_frame.text[0]
        if value != cur_value:
            print('Diff', name, value, cur_value)
            cur_frame.text[0] = value
            mutation = True
 
    if APIC is not None:
        with open(APIC) as f:
            apic_data = f.read()
        existing_apic_data = None
        for frame in id3.values():
            cur_name = repr(frame)[0:4]
            if 'APIC' == cur_name:
                existing_apic_data = frame.data
                break
 
        if apic_data != existing_apic_data:
            print('Diff APIC', filename)
            id3.delall('APIC')
            id3.add(mutagen.id3.APIC(
                encoding=mutagen.id3.Encoding.LATIN1,
                mime='image/jpeg',
                type=mutagen.id3.PictureType.OTHER,
                desc='',
                data=apic_data,
            ))
            mutation = True
 
    if mutation:
        id3.save(filename)
 
 
for d, dirs, files in os.walk(D):
    if '/.git/' in d:
        continue
    mp3s = sorted([f for f in files if f.endswith('.mp3')])
    assert d.startswith(D)
    album_name = d[len(D):].replace('/', '_').replace('"', '_')
    if not album_name:
        continue
 
    for i, mp3 in enumerate(mp3s):
        track_num = str(i + 1)  # .zfill(3)
        artist = album_name
        title = mp3
 
        check_and_apply(
            d  + '/' + mp3,
            {
                "TALB": album_name,
                "TPE1": artist,
                "TIT2": title,
                "TRCK": '{}/{}'.format(track_num, len(mp3s)),
            },
            APIC=(d+'/background.jpg') if os.path.exists(d + '/background.jpg') else None,
        )

mp3 audio traveling commute commuting vlc mobile id3v2 apic