I created this snippet to "split" my full-album videos into its individual songs without the need of actually splitting the video file.

I personally created this snippet because I enjoy reading all musics from a directory in shuffle mode and stumbling upon a full album kinda ruins the point.

# -*- coding: utf-8 -*-

__author__ = 'JeromeJ'
__website__ = 'http://www.olissea.com/'

# Match example for this regex:
#   32. Random song title - 1:33:05
DEFAULT_PATTERN = '^[0-9]+\.\s*(?P<SONG_TITLE>.*?)\s*-\s*(?P<START_TIME>[0-9]+\:[0-9]+(?:\:[0-9]+)?)'

SONG_TITLE = '{ALBUM_TITLE}: {SONG_TITLE}'  # What the song will be titled

import pathlib
import re

import pdb

print('Please put a .txt file containing the songs\' list next to the album')
print('with the same name as the album file.')
print('The playlist will be saved in current directory.')
print('The links in the playlist are relative, not absolute.')

# TODO: Idea: Allow to drag-and-drop the file on the script to run it.
#   -> Doesn't seem to work (Tried on W7 only so far)
filename = input('Path to album? ')

txt_file = str(pathlib.Path(filename).with_suffix('.txt'))

# Saves the playlist in the current directory with the same name as the album
playlist_file = pathlib.Path(filename).stem + '.m3u8'

pattern = input('Pattern for the songs\' list? (Default: {})\n'.format(DEFAULT_PATTERN)) or DEFAULT_PATTERN
song_title = input('Song title? (Default: {}) '.format(SONG_TITLE)) or SONG_TITLE
album_title = input('Album title? (Default: {}) '.format(ALBUM_TITLE)) or ALBUM_TITLE

playlist_entries = []

with open(txt_file, mode='r', encoding='utf-8') as song_list:
    for i, line in enumerate(song_list):
        r = re.search(pattern, line)

        # pdb.set_trace()

        start_time = r.group('START_TIME').split(':')
        start_time = \
            int(start_time[-1]) \
            + int(start_time[-2])*60 \
            + (int(start_time[-3])*60*60 if len(start_time) > 2 else 0)

#EXTINF:0, - {album_title}: {song_title}
                stop_time='{stop_time}',  # To be set by the next iteration

        if i:
            playlist_entries[i-1] = playlist_entries[i-1].format(stop_time=start_time)

# Up until the end of the file
playlist_entries[-1] = playlist_entries[-1].format(stop_time=0)

with open(playlist_file, 'w', encoding='utf-8') as f:

input('Done. Press Enter to leave.')

Any advice/opinion about how to improve this code in its Python-ness is welcome!

What 'could' be improved? What is wrong or advised against?

EDIT: As I've been suggested I could/should turn this into a lib. For my excuse this is "just a snippet".

Also I want to keep it as KISS as possible.

Example: The script turns this:

01. Once Upon a Time - 00:00
02. Start Menu - 01:28
03. Your Best Friend - 02:00

Into this:

#EXTINF:0, - Undertale: Once Upon a Time
..\Full albums\Undertale OST.mp4

#EXTINF:0, - Undertale: Start Menu
..\Full albums\Undertale OST.mp4

#EXTINF:0, - Undertale: Your Best Friend
..\Full albums\Undertale OST.mp4
  • \$\begingroup\$ Looking at your TODO, here is a hint \$\endgroup\$ Feb 4, 2016 at 13:04
  • \$\begingroup\$ @MathiasEttinger Oh great! When I first tried to drag and drop on the script, it didn't launch at all. I just tried it again and it works now! Now I could do it! Nice! \$\endgroup\$
    – jeromej
    Feb 4, 2016 at 15:33

1 Answer 1


Instead of using print() you can add a newline character \n at the end of the previous printed string:

print('with the same name as the album file.\n')
print('The playlist will be saved in current directory.')

When you ask for a filename, you don't do any input validation. What if they enter an empty string? What if their path isn't real? I'm not familar with pathlib, and also not sure what you'd want to do. You could create an infinite loop that will only break when the user enters a valid/existing path. Alternatively you could end the program if an invalid path appears. You definitely shouldn't ignore errors or leave it to Python to raise whatever errors it sees fit. You should make explicit choices about what happens.

When you're appending the string to playlist_entries, it's quite hard to read. You would do better to define an ENTRY_TEMPLATE string at the top of the file which you just use and format when appending.

Python truthiness can be very useful, but in this case if i is liable to confuse people. I would suggest either an explicit statement like i != 0 or a comment.

    if i:  # Not the first entry
  • \$\begingroup\$ Is it bad to do print()? Is it better to do it as you proposed? If so, why? Thanks. (About path validation, what could go wrong? It is "just a snippet" after all. I do not mind having Python crashing on bad input because it is just a small script, otherwise, totally agree with you) \$\endgroup\$
    – jeromej
    Feb 4, 2016 at 17:05
  • \$\begingroup\$ It's slightly wasteful because you call print twice. Personally I also find it looks better to use escape characters. Both reasons are minor though. Input validation is a good habit even for small scripts, as letting generic input in can lead to misuse or bad data, both of which are harder to spot. In this case, it would mostly lead to bad data or unclear crashes, but both are worth avoiding. \$\endgroup\$ Feb 4, 2016 at 17:51
  • \$\begingroup\$ What kind of path validation would you do/advise? Pathlib, afaik, already ensures the 'validity' of the path but allows relative path (using .. and such) which I need anyway. (But I'm not currently catching any error it'd raise but I should) \$\endgroup\$
    – jeromej
    Feb 5, 2016 at 18:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.