Contact Us

Creating an m3u playlist with relative paths using Bash, Powershell or Python

Reading Time: 4 min

Tags:  Programming

Most of the media player software lack the option to create playlists with relative paths. Absolute paths make it difficult to move and organize media folders without breaking playlists. Let us see how to create playlists with relative paths using Bash, Powershell, and Python.

I am not sure how many media players support relative paths in a playlist. The three which I tested: VLC, Media Player Classic, and mpv seem to support it.

M3u does not have a format specification, it is more of a de-facto standard. A playlist that works in one player might not work in another player. One major limitation of m3u is that many players expect m3u files to have ASCII encoding.

A note for mpv users

If you are an mpv user like me, you would know that mpv’s support for m3u playlists is limited to very basic playback support. If you want to use playlists with mpv, do take a look at the mpv plugin Mpv-Playlistmanager. It allows you to create and manage playlists from inside mpv. It is a must-have plugin for mpv users in my opinion.

Using PowerShell

In the PowerShell console, CD into the directory containing the media files and run the following command.

1gci -recurse | where {$_.extension -eq '.mp4' -Or $_.extension -eq '.mp3'} | Resolve-Path -Relative | Out-File -Encoding UTF8 -FilePath '.\playlist.m3u'

It creates a playlist named playlist.m3u in the current directory. The playlist recursively includes all mp3 files and mp4 files in the current directory. Adjust the file type filter and the name of the playlist as required. If you are not interested in relative paths and just want to create playlists from the command line, try this instead.

1gci -recurse | where {$_.extension -eq '.mp4' -Or $_.extension -eq '.mp3'} | select -ExpandProperty FUllName | Out-File -Encoding UTF8 -FilePath '.\playlist.m3u'

The select -ExpandProperty FullName option is for getting rid of the property name header.

Using Bash

CD into the media directory and type the following command:

1find . -type f \( -name "*.mp4" -o -name "*.aac" -o -name "*.mp3" \) -fprint playlist.m3u

Add more extensions as required in the format -o -name “*.” . 

Since adding a lot of extensions can make the command very long, here is a regex variant:

1find . -type f -iregex ".*\.\(mp4\|mp3\|avi\|m4v\|aac\|mkv\|mov\|webm\|wmv\|flv\)" -fprint playlist.m3u

If you like to sort the playlist by name, use the following command:

1find . -type f -iregex ".*\.\(mp4\|mp3\|avi\|m4v\|aac\|mkv\|mov\|webm\|wmv\|flv\)" | sort > playlist.m3u

Using Python

One option which I would have liked my PowerShell command to have is a natural sort option. Natural Sort is a term which is only beginning to gain acceptance. In short, it means sorting roughly the same way Windows Explorer or Mac Finder sorts. Please read my article about  Natural Sort, if you like to know more. 

I was hoping that PowerShell will have built-in support for natural sort, given that Windows Explorer uses natural sort. But there is no natural-sort support in PowerShell. So I wrote a Python script for creating naturally-sorted playlists. Python does not have built-in support for natural sorting either. So I used the Natsort library.

 1""" Creates an m3u playlist with relative paths inside.
 2    Playlist is created inside the dir supplied.
 3    All media files inside the root dir are sorted using 'natural' sort.
 4"""
 5
 6import argparse
 7from os import walk
 8from os import path
 9from natsort import natsorted
10
11
12def get_file_list(root_dir_path):
13    media_files = []
14    for (dirpath, _dirnames, filenames) in walk(root_dir_path):
15        for file in filenames:
16            extension = path.splitext(file)[1]
17            if extension in ['.mp4', '.mp3', '.mkv', '.aac']:
18                media_files.append(path.join(dirpath, file))
19    return media_files
20
21def create_playlist(playlist_path, files):
22    with open(playlist_path, 'w', encoding='utf-8') as p:
23        for f in files:
24            print(f, file=p)
25
26def parse_arguments():
27    """ Parses the arguments
28        directory is required argument
29        playlist name defaults to playlist.m3u
30        sort_by_files defaults to False"""
31    parser = argparse.ArgumentParser()
32    parser.add_argument(
33        "-p", "--play-list", default='playlist.m3u', help="playlist name")
34    parser.add_argument(
35        "-d", "--directory", required=True, help="root directory")
36    parser.add_argument(
37        '--sort-by-files',
38        dest='sort_by_files',
39        action='store_true',
40        help='sorts list by file name')
41    parser.add_argument(
42        '--no-sort-by-files',
43        dest='sort_by_files',
44        action='store_false',
45        help='disables file name based sort')
46    parser.set_defaults(sort_by_files=False)
47    return parser.parse_args()
48
49def main():
50    """ Entry point function """
51    args = parse_arguments()
52    file_list = get_file_list(args.directory)
53    if args.sort_by_files:
54        file_list = natsorted(file_list, path.basename)
55    else:
56        file_list = natsorted(file_list)
57    relative_paths = [path.relpath(p, args.directory) for p in file_list]
58    print(*relative_paths, sep='\n')
59    create_playlist(path.join(args.directory, args.play_list), relative_paths)
60
61if __name__ == '__main__':
62    main()

You can run the script like so:

1python create_m3u_playlist.py -d "C:\Users\admin\Music" -p my_playlist.m3u
Parameters Description
-d The directory containing media files.
-p Playlist name; do not specify the full path.
–sort-by-files Boolean switch; no arguments needed. Sorts playlist by filename; the default is sort by relative path.
-h Show help

The playlist is created in the directory specified by the -d parameter. By default, the playlist is sorted by full path. To sort by file name only, use the –sort-by-files switch without arguments.

Share