0

I want to be able to save the name of an .mp3 file every time I open a song with VLC.

Edit: As you guys in the comments mentioned;

  1. I want to open VLC directly in a player window.
  2. I want to save the name of the song that is being played into a text file.

How can I achieve this goal? Thanks in advance.

Jacob Vlijm
  • 83,767
Amir Shabani
  • 239
  • 2
  • 13
  • 2
    You would have to write a script to do what you want and then force every user to use the script rather then launching VLC direct. Your question is otherwise lacking details so .... – Panther Jun 05 '17 at 18:07
  • @bodhi.zazen no I want to launch VLC (or any other program) directly and be able to run a script automatically. I'll add more details to the question. – Amir Shabani Jun 05 '17 at 18:09
  • 1
    Well you would have to either write a script or modify the source code and recompile the apps you want. – Panther Jun 05 '17 at 18:11
  • Is your example valid, meaning: do you want to use the song name as an argument for anything? – Jacob Vlijm Jun 05 '17 at 18:14
  • @JacobVlijm no, I just want to write the name of the song into a text file every time I directly launch VLC. – Amir Shabani Jun 05 '17 at 18:15
  • ...Then do you run vlc in a player window? You might want to narrow the question a bit down, since it is quite broad as it is, but you mean to ask quite specifically. – Jacob Vlijm Jun 05 '17 at 18:17
  • 1
    @JacobVlijm Yes, sorry if it wasn't clear, I'll edit in a sec. – Amir Shabani Jun 05 '17 at 18:19
  • 1
    VLC tracks recent files see and https://superuser.com/questions/287137/does-vlc-media-player-store-the-files-or-its-history-in-a-hidden-location. – Panther Jun 05 '17 at 18:28
  • Hi @AmirA.Shabani, added another option. – Jacob Vlijm Jun 06 '17 at 16:17

3 Answers3

3

You can get the currently playing track name (Address) using:

lsof -p `pidof -s vlc` | grep -o "/.*\.mp3"

If you want to save the name when you double click on a mp3 file to open VLC here is my Idea:

open VLC's desktop file:

sudo nano /usr/share/applications/vlc.desktop

then edit Exec line to make it look like:

Exec=bash -c "{ /usr/bin/vlc --started-from-file %U; }& sleep 1; lsof -p `pidof -s vlc` | grep -o "/.*\.mp3" > /home/user/list"

it's going to save the file name at /home/user/list.

Otherwise I suggest you to create a simple keybinding for sh -c 'lsof -p $(pidof -s vlc) | grep -o "/.*\.mp3"' so whenever you press that keys it saves the currently playing song's name.

We can also create a script and run it at the same time with VLC, and keep it running in the background then withing that script we can check which track is being played right now.

Ravexina
  • 55,668
  • 25
  • 164
  • 183
  • The line:

    lsof -p \pidof -s vlc` | grep -o "/home.*mp3"` gives me this error:

    lsof: WARNING: can't stat() tracefs file system /sys/kernel/debug/tracing Output information may be incomplete.

    – Amir Shabani Jun 05 '17 at 19:00
  • Warning doesn't related to the command syntax, Here:1, Here:2. Just make sure VLC is running and is playing a song then run that command again, I also updated the command, use this one instead the old one. you can test it using sudo too. – Ravexina Jun 05 '17 at 19:10
2

You can use the verbose output of VLC, but it's a bit complicated. You need verbosity level 2 to even get any file names printed at all (for whatever reason), but that logs tons of other things as well, so you need to filter the output.

Here is an example output of verbosity 2, but only including the file names that tell you the path (vlc --verbose 2 file.mp3 | grep "file.mp3"):

[00007f1720000c20] main input debug: Creating an input for preparsing 'file.mp3'
[00007f16d0000c80] main input debug: `file:///path/to/file.mp3' gives access `file' demux `any' path `/path/to/file.mp3'
[00007f16c4000fe0] main input source debug: creating demux: access='file' demux='any' location='/path/to/file.mp3' file='/path/to/file.mp3'
[00007f16c4001690] main stream debug: creating access: file:///path/to/file.mp3
[00007f16c4001690] main stream debug:  (path: /path/to/file.mp3)
[00007f16c4000fe0] main input source debug: attachment of directory-extractor failed for file:///path/to/file.mp3
[00007f16c4000fe0] main input source debug: creating demux: access='file' demux='any' location='/path/to/file.mp3' file='/path/to/file.mp3'
[00007f16d0000c80] main input debug: `file:///path/to/file.mp3' successfully opened

Here is an example command that prints all played file names to the console (you can redirect that into a file with >> file.txt at the end):

vlc --verbose 2 file1.mp3 /path/to/file2.m4a 2>&1 | grep -v "ml.xspf" | grep -oP "\/.*\/\K.*(?=\.[a-zA-Z0-9-_]*\' successfully opened)"

Explanation:

  • vlc … file1.mp3 /path/to/file2.m4a opens VLC and plays those two tracks. You can also set it to random or whatever on the command line, there are MANY options.
  • --verbose 2 activates the verbose output. You could also put this into a file with --file-logging --log-verbose 2 --log-file file.log, but the rest of my command also works with the command line, so this works better.
  • 2>&1 | redirects the error output (and the standard output) to the next command.
  • grep -v "ml.xspf" filters out anything containing "ml.xspf", because for some reason VLC opens that file (in my case located in ~/.local/share/vlc) the same way as a regular file. It's not visible anywhere in the GUI, but it looks the same as a regular played file in the log.
  • | grep -oP only lets lines through that match a certain Perl-style regex and only displays the matched part of that regex.
  • Now the complicated part: "\/.*\/\K.*(?=\.[a-zA-Z0-9-_]*\' successfully opened)" is a regular expression that matches lines like the last one in the log example above and only outputs the file name without the ending (remove \.[a-zA-Z0-9-_]* if you want to keep it) and path (change \/.*\/ to file\:\/\/ if you want to keep it).
    Explanation for that regex:
    • \/.*\/ matches anything between two slashes (/) and the slashes themselves. That could for example be "/path/to/".
    • \K excludes everything before it from the match, meaning that it has to be there to have any match at all, but it's not in the result.
    • .* just matches anything (except newlines etc.).
    • (?=…) is a "lookahead", which does essentially the opposite of \K, it excludes everything after it (or in this case inside the brackets) from the match, but it still needs to be there to have a match.
    • \. matches a period (.).
    • [a-zA-Z0-9-_]* matches all letters, numbers, - and _ an arbitrary number of times, which should hopefully cover all file endings.
    • \' successfully opened matches the end of the line from the log example.
  • I picked the "successfully opened" line instead of some more easily parseable ones, because it seems to confirm that no errors have happened, so for example if you dumped your entire home folder into it, it would not output the files that it couldn't play because they're not media files.

A special difficulty in this problem was that many commands cannot be used. For example basename doesn't work with StdIn, sed only executes once the input ends and so on. VLC has interfaces that are intended for controlling and monitoring it from the outside, but they all seem to be very complicated to use. The simplest one seems to be the HTTP interface, but that cannot be used without a password, which makes using it programmatically much harder.

2

Automatically log all your played songs, with a time stamp

Alternatively, without changing anything to your .desktop file or having to manually log your songs, you can run the background script below. It will create a log file, named played_songs, in your home directory:

enter image description here

Advantages are:

  • Fully automatically logs all your played songs
  • No need to change your .desktopfile, which means it also works when you start VLC from the command line.

Disadvantages are:

  • Although I couldn't measure or see any additional processor load, theoretically it adds some activity. This is practically none though, even on stone-age boxes.
  • From time to (long) time, you will need to "empty" or remove the log file, since (currently) it has no limit in size. This can be easily fixed though if you like.

The script

#!/usr/bin/env python3
import subprocess
import os
import time

home = os.environ["HOME"]
log = os.path.join(home, "played_songs")
def_name = "VLC media player"

def get(cmd):
    try:
        return subprocess.check_output(cmd).decode("utf-8").strip()
    except subprocess.CalledProcessError:
        pass

song1 = None

while True:
    time.sleep(2)
    pid = get(["pgrep", "vlc"])
    if not pid:
        song = None
    else:
        # only do anything at all if vlc runs
        wins = get(["wmctrl", "-lp"])
        if wins:
            wins = wins.splitlines()
            try:
                # check if any of the windows belongs to vlc
                # this will not if it is closed in the meantime
                match = [w.split()[0] for w in wins if pid in w][0]
            except IndexError:
                pass
            else:
                # ...if so, retrieve the song name, only log if there is a new song
                song2 = get(["xdotool", "getwindowname", match])
                if all([song2, song2 != song1, song2 != def_name]):
                    open(log, "a+").write(
                        time.strftime("%H:%M:%S_%d-%m-%Y")+"\n   "+\
                        song2.replace(def_name, "").rstrip(" - ")+"\n"
                        )
                song1 = song2

How to use

  1. The script needs both xdotool and wmctrl:

    sudo apt-get install xdotool wmctrl
    
  2. Copy the script into an empty file, save it as log_vlc.py

  3. Test- run it from a terminal by the command:

    python3 /path/to/log_vlc.py
    

    Open any song in VLC in either way (cli, gui), the log file should be created and show the logged songs.

  4. If all works fine, add ity to Startup Applications: Dash > Startup Applications > Add. Add the command:

    python3 /path/to/log_vlc.py
    

Explanation

Once per two seconds, the script:

  • checks if VLC is running at all, does nothing if not

    pid = get(["pgrep", "vlc"])
    if pid:
        # only do anything at all if vlc runs
    
  • if VLC is running, it finds its window and parses out the song title

    wins = get(["wmctrl", "-lp"])
    if wins:
        wins = wins.splitlines()
        try:
            # check if any of the windows belongs to vlc
            # this will not be the case if it is closed in the meantime
            match = [w.split()[0] for w in wins if pid in w][0]
        except IndexError:
            pass
        else:
            # ...if so, retrieve the song name, only log if there is a new song
            song2 = get(["xdotool", "getwindowname", match])
    
  • (only) if the song title changes, obviously a new song is played, then the song name is logged into the log file, time stamped.

            if all([song2, song2 != song1, song2 != def_name]):
                open(log, "a+").write(
                    time.strftime("%H:%M:%S_%d-%m-%Y")+"\n   "+\
                    song2.replace(def_name, "").rstrip(" - ")+"\n"
                    )
                print(song2)
            song1 = song2
    
Jacob Vlijm
  • 83,767