legacylisten
legacylisten
is a simple CLI audio player I wrote because no
existing one fulfilled my needs. The main feature is that you can
change how often a song is played (legacylisten
is always on
shuffle-all), but there are some other even odder features.
How it works
legacylisten
creates a list of all songs in
~/.zvavybir/legacylisten/data
1 and sub directories together with
their associated so-called "playing likelihood"2 and volume (the
standard values are 10 and 10% respectively). Then it will choose a
song at random with the probability proportional to it's playing
likelihood and plays it, unless you request something different.
The volume is adjustable on a per-song basis and is saved. Although simple (ridiculously trivial indeed) to implement, there is no way to change the global volume, since I figured that this is better left to the operating system. What a audio player can do very good is recognizing which song is playing and acting according to it. The intended use of that feature is to adjust the volume of very quiet songs once and than the user doesn't have to be bothered ever again.
Another quite obscure feature is that you can not only pause/quit
immediately, but also only on the end of the current song, but the
strangest one is that legacylisten
will sever all connection to the
disk if the *NIX signal SIGUSR1
arrives and only starts reading
again when SIGUSR2
arrives. SIGUSR1
doesn't interrupt an already
playing song since songs are buffered3.
Commands
Commands are how legacylisten
is controlled and consist always out
of a single character. Originally these were the first letter of the
command name, but since this caused rather strange names (like f
–
"fainter" – to decrease the volume), I settled to just number them
through alphabetically.
To execute a command, just type it's letter (but remember that
terminals are usually line-buffered, meaning that until you press
enter legacylisten
won't see – and react to – your input).
The following commands exist:
?
: Shows a list of all command with a help message (essentially this very one).4a
: Increases playing likelihood of the current song by 1.b
: Decreases playing likelihood of the current song by 1.c
: Quitslegacylisten
and saves the songs likelihoods and volumes to~/.zvavybir/legacylisten/songs.csv
.d
: Pauses playing.e
: Resumes playing after pausing withd
orl
(doesn't overwriteSIGUSR1
though).f
: Skips song.g
: Increases permanently the volume of the current song by 1% (but not above 100%).h
: Decreases permanently the volume of the current song by 1% (but not below 0%).i
: Shows how long the song is already playing and – if available – how long it will take in total.j
: Switches between playing and pausing.k
: Quitslegacylisten
as soon as the current song has finished playing (takes precedence overl
).l
: Likek
, but just pauses instead of quitting.m
: Shows the metadata in the song's id3 tag (the length usually has to be queried byi
since it's rarely saved in the id3 tag).n
: Opens the cover image of the song in your preferred image viewer (it usesmimeopen
which is AFAIK not available on MS Windows, so this won't work there). If the song has no cover it opens~/.zvavybir/legacylisten/default.png
(doesn't need to be an PNG file) instead. For the fallback image I use (and made, so it's quite bad) see here.o
: Stops all repeating (but if the current songs is an repetition it's not ended immediately; if you want that also skip withf
).p
: Repeats the current song once.q
: Repeats the current song forever.r
: Skips to the beginning of the current song or – if it already is at the beginning – to the previous one. You can go as many songs back as you want (or more precisely how many there are). All played songs are saved (but only in one run oflegacylisten
, if you restart it the history is lost) and if you went back the next song is the same as previously followed on that song.
Low memory handler
As already briefly pointed out previously, especially older versions
of legacylisten
had an horrendous memory footprint, which rendered
my system unusable for a few seconds a couple times. Under Linux such
problems are usually handled by the OOM killer (which ends the process
with least importance and largest memory consumption), but it turns
out that the Chromium web browser (the free software variant of Google
Chrome), which out of other reasons I'm forced to use every once in a
while, is even worse than my crimes. Instead of doing something
sensible, I added a routine to legacylisten
that watches the amount
of free memory and terminates itself when it falls under some certain
(configurable) threshold (a GiB currently).
This uses currently a wrong notion of "free ram" (it counts memory used for disk caching as used although it's not; see this famous site for more), so it triggers unnecessarily. Although this is better than the reverse, the low memory handler is off as default because of that.
This uses the *NIX function sysconf(3)
, so it won't work on outdated
platforms.
Configuration file
legacylisten
can be configured by the
~/.zvavybir/legacylisten/conffile.csv
file. If an option can't be
parsed it's just silently ignored, so be careful. Every option has an
own line (with mandatory newline at the end, even for the last line
and under MS Windows) and every part of it has to be comma-separated.
As an example, this is my configuration file:
data_dir,/media/my_user_name/external_harddrive/legacylisten
ignore_ram,false
lang,esperanto
repeat_bonus,2
There are currently six possible options:
data_dir
: If you have your music collection somewhere else (like me on an external hard drive or in~/Music
) you can use this option to change the directorylegacylisten
will search. The~/
notation is not usable in the configuration file, even under *NIX systems.minimum_ram
: The threshold for the low memory handler in bytes.ignore_ram
: Disables the low memory handler (possible values aretrue
andfalse
). If this is set (currently the default)minimum_ram
is ignored.lang
:legacylisten
supports basic internationalization and this is the option to activate it. There are currently four possible values for this option:english
: Sets the language to English (this is the default).german
ordeutsch
: Sets the language to German.esperanto
: Sets the language to Esperanto.custom
: If you have a translation file, but it's not included in the official sources (maybe because you're still working on finishing it, you just want to try something out or you are forbidden by legal reasons to publish it underlegacylisten
's license) this option enables you to still use it. This option requires two further values, the path to the translation file and the language ID. As an example, if English weren't included already you could use such an option to circumvent that:
The path has no requirements about filename or file extension, but the language identifier has to be correct.lang,custom,/path/to/file/translation.fl,en-US,
repeat_bonus
: Repeating a song is usually a sign that the song is good and should be played more often, but it's very easy to forget to increase it's playing likelihood, so this option does this automatically. Everytime a song is repeated it's increased by the configured value. You can also set a negative value.enable_dbus
: Enables the dbus module. D-Bus/MPRIS is responsible for integratinglegacylisten
into your system nicely. This is not necessary to getlegacylisten
working. The default value isfalse
.
Plugin interface
In case there is no metadata tag in the song, you can use the plugin
interface to tell legacylisten
the song's title and artist. Every
plugin is a shell script (or executable if you prefer) in the
~/.zvavybir/legacylisten/parser
directory (or sub directories
thereof) and gets the song's file name (without new line character) as
input (on stdin). If the file name could be parsed it has to output
the song's title and artist (delimited by zero bytes and optionally a
trailing zero byte) on stdout. If no zero bytes are in the output the
parsing is treated as having failed and will be ignored.
Installing
The simplest way to install legacylisten
is with
rustup and Cargo. After installing rustup as
indicated on it's website, issue to following command to install
legacylisten
itself:
cargo install legacylisten
Contributing
As every software legacylisten
too always can be improved. While
I'm trying to get it usable alone, I don't have unlimited time and
especially not always the best ideas. If you can help with that or on
some other way (like with a feature request, an additional language or
documentation improvements) please help.
I assume that unless stated otherwise every contribution follows the necessary license.
License
Though unusual for a rust program, legacylisten
is released under
the GNU General Public License version 3 or (at your option) any later
version.
For more see LICENSE.md.
Although not intended (even to the
contrary) legacylisten
should
be quite portable (~/
refers to the user's home directory – in
legacylisten
even under MS Windows).
Or "likelihood" for short.
This is of course quite bad on the memory footprint, but it's the best I could manage so far (at least it's a whole magnitude better than the worst implementation I had). If you have an better idea, please contribute!
This command is a bit special since it's handled differently
internally. You can see this on the one hand directly by it's
special name (only non-letter one) and on the other hand (when you
run legacylisten
) that while usually commands are executed
strictly in order this one is run before all others specified on
the same line.
legacylisten
legacylisten
ist ein einfacher CLI Musikspieler, den ich geschrieben
habe, da kein existierender meinen Bedürfnissen entsprach. Die
Hauptbesonderheit ist, dass man ändern kann, wie oft ein Lied
abgespielt wird (legacylisten
ist immer auf Shuffle-all), aber es
gibt auch einige noch komischere Funktionen.
Funktionsweise
legacylisten
erzeugt rekursiv eine Liste aller Lieder in
~/.zvavybir/legacylisten/data
1 zusammen mit ihrer
"Spielwahrscheinlichkeit" und Lautstärke (die Standardwerte sind 10
und 10% respektive). Dann wählt es ein zufälliges Lied mit der
Wahrscheinlichkeit proportional zu seiner Spielwahrscheinlichkeit aus
und spielt es bis der Nutzer etwas anderes sagt.
Die Lautstärke ist pro Lied anpassbar und wird gespeichert. Obwohl es einfach wäre (lächerlich trivial sogar) zu implementieren, gibt es keine Möglichkeit die globale Lautstärke zu verändern, da ich finde, dass man dies besser dem Betriebssystem überlässt. Worin sich ein Musikspieler hervortun kann, ist zu wissen, welches Lied gespielt wird und darauf zu reagieren. Die Idee hinter dieser Funktion ist die Lautstärke von sehr leisen Liedern einmal zu erhöhen und sich dann nicht mehr darum kümmern zu müssen.
Eine andere ziemlich komische Funktion ist das man nicht nur sofort
stoppen/beenden kann, sondern auch wenn das aktuelle Lied zu Ende ist.
Das Komischste ist aber, dass legacylisten
alle Verbindungen zur
Festplatte trennt, wenn das *NIX Signal SIGUSR1
gefangen wird und
nur nach SIGUSR2
wieder Verbindungen aufnimmt. SIGUSR1
unterbricht allerdings kein bereits spielendes Lied, da Lieder immer
gepuffert werden2.
Befehle
Befehle sind der Weg auf dem legacylisten
bedient wird und bestehen
immer aus einem einzigen Zeichen. Ursprünglich waren sie immer der
erste Buchstabe des Befehlsnamen, da dies aber in sehr komische Namen
resultierte (z.b. f
– "fainter" schwächer – um die Lautstärke
zu verringern), habe ich mich entschieden sie einfach alphabetisch
durch zu nummerieren.
Um ein Befehl auszuführen, tippe einfach sein Buchstaben ein (aber
denke daran, dass Terminals üblicherweise Zeilen-gepuffert sind,
d.h. legacylisten
sieht – und reagiert auf – die Eingabe nur nachdem
Enter gedrückt wurde).
Es gibt die folgenden Befehle:
?
: Zeigt eine Liste aller Befehle mit Erklärung (mehr oder weniger diese Liste).3a
: Erhöht die Spielwahrscheinlichkeit des aktuellen Lieds um 1.b
: Verringert die Spielwahrscheinlichkeit des aktuellen Lieds um 1.c
: Beendetlegacylisten
und speichert die Spielwahrscheinlichkeiten und Lautstärken in~/.zvavybir/legacylisten/songs.csv
.d
: Stoppt das Abspielen.e
: Setzt das Abspielen wieder fort nachdem es mitd
oderl
angehalten wurde (überschreibtSIGUSR1
allerdings nicht).f
: Überspringt das Lied.g
: Erhöht die Lautstärke des aktuellen Lieds permanent um 1% (allerdings nicht auf mehr als 100%).h
: Verringert die Lautstärke des aktuellen Lieds permanent um 1% (allerdings nicht auf weniger als 0%).i
: Zeigt an wie lange das Lied schon spielt und – wenn verfügbar – wie lange es insgesamt brauchen wird.j
: Wechselt zwischen Abspielen und Stoppen.k
: Beendetlegacylisten
sobald das aktuelle Lied fertig ist (k
nimmt Vorrang zul
).l
: Wiek
, stoppt aber nur anstatt zu beenden.m
: Zeigt die Metadaten im id3-Tag des Lieds (die Länge muss üblicherweise miti
nachgefragt werden, da sie nur selten im id3-Tag gespeichert wird).n
: Öffnet das Cover in dem eingestellten Bildbetrachter (diese Funktion nutztmimeopen
was so viel ich weiß unter MS Windows nicht verfügbar ist). Wenn das Lied kein Cover hat wird~/.zvavybir/legacylisten/default.png
stattdessen geöffnet. Für das Bild, das ich nutze (und gemacht habe, also ziemlich schlecht ist) siehe hier.o
: Wiederholung des Liedes anhalten (wenn allerdings das Lied als Wiederholung gespielt wird, wird nicht sofort beendet; wenn das gewünscht ist, überspringe auch noch mitf
).p
: Wiederholt das aktuelle Lied einmal.q
: Wiederholt das aktuelle Lied ewig.r
: Springt zum Anfang des aktuellen Lieds oder – wenn es bereits am Anfang ist – zum vorherigen. Man kann so viele Lieder zurückgehen wie man will (oder genauer gesagt so viele es gibt). Alle gespielten Lieder werden gespeichert (allerdings nur in einem Aufruf vonlegacylisten
, wenn es neu gestartet wird ist die Geschichte verloren) und wenn man zurückgeht, wird man auf dieses Lied wieder das Gleiche folgen sehen wie davor.
Low memory handler
Wie schon vorher kurz angeschnitten, hatten insbesondere ältere
Versionen von leacylisten
einen schlimmen RAM-Fußabdruck, was mein
System ein paar Mal für ein paar Sekunden lahmgelegt hat. Unter Linux
gibt es für so einen Fall eigentlich den OOM Killer (welcher bei
Bedarf den unwichtigsten, aber verschwenderischsten Prozess beendet),
es stellt sich allerdings heraus, dass der Chromium Webbrowser (die
Freie-Software Variante von Google Chrome), welchen ich manchmal
gezwungener Maßen einsetzte, sogar noch schlimmer als meine Verbrechen
ist. Anstatt etwas Vernünftiges zu machen, habe ich stattdessen eine
Routine zu legacylisten
hinzugefügt, die die Menge an freiem RAM
überwacht und sich selbst beendet, wenn es unter ein (einstellbares)
Limit fällt (ein GiB aktuell).
Es wird aktuell eine falsche Definition von "freiem RAM" genutzt (Festplattencaching wird als verwendet gezählt, obwohl es nicht ist; siehe diese berühmte Seite für mehr), weswegen es unnötigerweise anschlägt. Obwohl das besser ist als umgekehrt, ist aus diesem Grund es aktuell standardmäßig aus.
Dies nutzt die *NIX Funktion sysconf(3)
, wird also auf veralteten
Plattformen nicht funktionieren.
Konfigurationsdatei
legacylisten
kann mittels ~/.zvavybir/legacylisten/conffile.csv
konfiguriert werden. Wenn eine Option nicht geparst werden kann, wird
sie still verworfen, pass also auf. Jede Option hat eine eigene Zeile
(mit verpflichtendem Newline-Zeichen am Ende, sogar für die letzte
Zeile und unter MS Windows) und jeder Teil ist mit Kommata getrennt.
Als ein Beispiel, hier ist meine Konfigurationsdatei:
data_dir,/media/my_user_name/external_harddrive/legacylisten
ignore_ram,false
lang,esperanto
repeat_bonus,2
Es gibt aktuell sechs mögliche Optionen:
data_dir
: Wenn deine Musiksammlung woanders ist (z.b. wie bei mir auf einer externen Festplatte oder in~/Musik
), kann diese Option genutzt werden, um das Verzeichnis, dasslegacylisten
durchsucht, zu ändern. Die~/
Notation ist sogar unter *NIX Systemen nicht in der Konfigurationsdatei verwendbar.minimum_ram
: Das Limit für den low memory handler in Bytes.ignore_ram
: Deaktiviert den low memory handler (mögliche Werte sindtrue
wahr undfalse
falsch). Wenn diese Option gesetzt ist (aktuell Standard) wirdminimum_ram
ignoriert.lang
:legacylisten
unterstützt grundlegende Internationalisierung und dies ist die Option, um es zu aktivieren. Es sind aktuell vier Werte zugelassen:english
: Stellt die Sprache auf Englisch (das ist der Standard).german
oderdeutsch
: Stellt die Sprache auf Deutsch.esperanto
: Stellt die Sprache auf Esperanto.custom
: Wenn man eine Übersetzungsdatei hat, sie aber nicht in dem offiziellen Quellcode aufgenommen ist (vielleicht weil sie noch im Entstehen begriffen ist, man nur kurz was ausprobieren will oder weil aus legalen Gründen nicht möglich ist sie unterlegacylisten
s license zu veröffentlichen), ermöglicht diese Option, sie trotzdem zu nutzen. Diese Option braucht zwei weitere Werte, den Pfad zur Datei und die Sprach-ID. Als Beispiel, wäre Englisch nicht bereits unterstützt, könnte man es so umgehen:
Der Pfad hat keine Anforderungen über Dateiname oder Dateiendung, die Sprach-ID muss aber korrekt sein.lang,custom,/pfad/zur/übersetzung.fl,en-US,
repeat_bonus
: Ein Lied zu wiederholen ist normalerweise ein Zeichen dafür, dass das Lied gut ist und öfters gespielt werden sollte. Allerdings ist es sehr einfach zu vergessen seine Spielwahrscheinlichkeit zu erhöhen, daher kann diese Option es automatisch machen. Jedes mal wenn ein Lied wiederholt wird, wird sie um den angegebenen Wert erhöht. Der Wert kann auch negativ sein.enable_dbus
: Aktiviert das D-Bus Modul. D-Bus/MPRIS ist dafür verantwortlichlegacylisten
schön in das System zu integrieren. Diese Option ist nicht notwendig umlegacylisten
zu nutzen. Der Standardwert istfalse
falsch.
Pluginschnittstelle
Wenn es im Lied kein Metadaten-Tag gibt, kann man die
Pluginschnittstelle nutzen, um legacylisten
den Titel und Künstler
des Liedes mitzuteilen. Jedes Plugin ist ein Shellskript (oder eine
Binärdatei) im ~/.zvavybir/legacylisten/parser
Verzeichnis (oder
Unterverzeichnisse davon) und bekommt den Dateinamen des Liedes (ohne
Newline-Zeichen) als Eingabe (auf stdin). Wenn der Dateiname geparst
werden konnte, muss es den Titel und Künstler (getrennt bei einem
Nullbyte und mit optionalem Nullbyte am Ende) auf stdout ausgeben.
Wenn keine Nullbytes in der Ausgabe sind wird das Parsen als
fehlgeschlagen behandelt und ignoriert.
Installieren
Der einfachste Weg legacylisten
zu installieren ist mit
rustup und Cargo. Sobald rustup wie auf seiner
Webseite angegeben installiert ist, kann legacylisten
selber mit dem
folgenden Befehl installiert werden:
cargo install legacylisten
Mithelfen
Wie jedes Programm auch legacylisten
kann immer verbessert werden.
Obwohl ich auch alleine versuche, es nutzbar zu machen, habe ich nicht
unbegrenzt Zeit und insbesondere nicht immer die besten Ideen. Wenn
du damit oder mit etwas anderes (wie ein Featurevorschlag, einer
weiteren Sprache oder Dokumentationsverbesserungen) helfen kannst,
helfe bitte mit!
Ich nehme an, dass solange nichts anderes angegeben ist, alle Beiträge unter der notwendigen Lizenz stehen.
Lizenz
Obwohl unüblich für ein Rust Programm, steht legacylisten
unter der
GNU General Public License Version 3 oder (deiner Wahl nach) jeder
späteren.
Für mehr siehe LICENSE.md.
Obwohl es nicht beabsichtigt war (sogar im
Gegenteil), sollte legacylisten
mehr oder weniger portabel sein (~/
steht für das
Benutzerverzeichnis – in legacylisten
sogar unter MS Windows).
Das ist natürlich ziemlich schlecht für den Arbeitsspeicherfußabdruck, aber es ist das beste was ich bisher machen konnte (zu mindestens ist es eine ganze Größenordnung besser als die schlimmste Implementation, die ich hatte). Wenn du eine bessere Idee hast, bitte helfe mit!
Dieser Befehl ist ein bisschen speziell, da er intern anderes
verarbeitet wird. Man kann das zum einen an dem speziellen Namen
(einziger Befehl ohne Buchstabe) sehen, andererseits (während man
legacylisten
ausführt) daran, dass obwohl Befehl üblicherweise
streng in der angegebenen Reihenfolge ausgeführt werden, dieser
vor allen anderen auf der selben Zeile ausgeführt wird.