m3u-filter

m3u-filter is a simple application which can filter, rename and map entries out of a playlist in EXTM3U format. M3U format is supported by most media and iptv players as a playlist. If you have a playlist which contains unwanted entries, you can create filter which include or discard entries based on the header information of the playlist entries, you can rename entries or map entries based on regular expressions. Currently, filter and rename operations support group, name and title fields.

You can run m3u-filter as command line application to update your playlists (manually or as cron job), or you can run it in server mode and open the web-ui to see the contents of the playlist, filter/search content and save the filtered groups as a new playlist.

m3u-filter can process multiple inputs and can create multiple files from this input files trough target definitions. You can define multiple targets for filtering if you want to create multiple playlists from a big playlist.

Starting in server mode for Web-UI

If you want to see the contents of a playlist, you can simply start with the -s (--server) argument. Other arguments are ignored. A server is started. You can open a browser to view the Web-UI. According to your configuration, use the printed url on console. The UI allows you to download a list. You can download the list with the Save Button. The downloaded list only contains non-selected entries.

1. config.yml

For running in cli mode, you need to define a config.yml file which can be next to the executable or provided with the -c cli argument. It contains the filter, rename and mapping definitions.

Top level entries in the config files are:

  • threads optional
  • api
  • working_dir
  • templates optional
  • sources

1.1. threads

If you are running on a cpu which has multiple cores, you can set for example threads: 2 to run two threads. Don't use too many threads, you should consider max of cpu cores * 2. Default is 0.

1.2. api

api contains the server-mode settings. To run m3u-filter in server-mode you need to start it with the -scli argument.

  • api: {host: localhost, port: 8901, web_root: ./web}

1.3. working_dir

working_dir is the directory where file are written which are given with relative paths.

  • working_dir: ./data

With this configuration, you should create a data directory next to the executable.

1.4 templates

If you have a lot of repeats in you regexps, you can use templates to make your regexps cleaner. You can reference other templates in templates with !name!.

templates:
  - {name: delimiter, value: '[\s_-]*' }
  - {name: quality, value: '(?i)(?P<quality>HD|LQ|4K|UHD)?'}

With this definition you can use delimiter and quality in your regexp's surrounded with ! like.

^.*TF1!delimiter!Series?!delimiter!Films?(!delimiter!!quality!)\s*$

This will replace all occurrences of !delimiter! and !quality! in the regexp string.

1.5. sources

sources is a sequence of source definitions, which have two top level entries:

  • input
  • targets

1.5.1 input

Has for entries, persist, url, prefix, suffix.

input: { persist: ./playlist_{}.m3u, url: http://myserver.net/playlist.m3u, prefix: {field: title, value: '#!# ' }, suffix: {field: title, value: ' +-+' } }

  • persist is optional, you can skip or leave it blank to avoid persisting the input file. The {} in the filename is filled with the current timestamp.
  • url is the download url or a local filename of the input-source.
  • prefix is optional, it is applied to the given field with the given value
  • suffix is optional, it is applied to the given field with the given value

prefix and suffix is appended after all processing is done, but before sort. They have 2 fields:

  • field can be name , group, title
  • value a static text

1.5.2 targets

Has the following top level entries:

  • filename mandatory
  • sort optional
  • output optional default is m3u
  • processing_order optional default is frm
  • options optional
  • filter mandatory,
  • rename optional
  • mapping optional

1.5.2.1 filename

Is the filename for the resulting playlist.

1.5.2.2 sort

Has one top level attribute order which can be set to ascor desc.

1.5.2.3 output

There are two types of targets m3u and strm. If the attribute is not specified m3u is created by default. You can set options for each output type.

strm output has additional options underscore_whitespace, cleanup and kodi_style.

1.5.2.4 processing_order

The processing order (Filter, Rename and Map) can be configured for each target with: processing_order: frm (valid values are: frm, fmr, rfm, rmf, mfr, mrf. default is frm)

1.5.2.5 options

  • ignore_logo true or false
  • underscore_whitespace true or false
  • cleanup true or false
  • kodi_style true or false

underscore_whitespace, cleanup and kodi_style are only valid for strm output.

  • ingore_log logo attributes are ignored to avoid caching logo files on devices.
  • underscore_whitespace replaces all whitespaces with _ in the path.
  • cleanup deletes the directory given at filename.
  • kodi_style tries to rename filename with kodi style.

1.5.2.6 filter

The filter is a string with a filter statement. The filter can have UnaryExpression NOT, BinaryExpression AND OR, and Comparison (Group|Title|Name|Url) ~ "regexp". Filter fields are Group, Title, Name and Url. Example filter: ((Group ~ "^DE.*") AND (NOT Title ~ ".*Shopping.*")) OR (Group ~ "^AU.*")

The regular expression syntax is similar to Perl-style regular expressions, but lacks a few features like look around and backreferences.

1.5.2.7 rename

Has 3 top level entries.

  • field can be group, title, name or url.
  • new_name can contain capture groups variables addressed with $1,$2,...

rename supports capture groups. Each group can be addressed with $1, $2 .. in the new_name attribute.

This could be used for players which do not observe the order and sort themselves.

rename:
  - { field: group,  pattern: ^DE(.*),  new_name: 1. DE$1 }

In the above example each entry starting with DE will be prefixed with 1..

(Please be aware of the processing order. If you first map, you should match the mapped entries!)

1.5.2.8 mapping

mapping: <list of mapping id's> The mappings are defined in a file mapping.yml. The filename can be given as -m argument.

Example config file

threads: 4
working_dir: ./data
api:
  host: localhost
  port: 8901
  web_root: ./web
input:
  url: http://myserver.net/playlist.m3u
  persist: ./playlist_{}.m3u
targets:
  - filename: playlist_1.m3u
    processing_order: frm
    options:
      ignore_logo: true
    sort:
      order: asc
    filter: Group ~ "^DE\s.*" OR Group ~ "^AU\s.*" 
    rename:
      - field: group
        pattern: ^DE(.*)
        new_name: 1. DE$1
  - filename: playlist_strm
    output: strm
    options:
      ignore_logo: true
      underscore_whitespace: false
      kodi_style: true
      cleanup: true
    sort:
      order: asc
    filter: Group ~ "^DE\s.*" OR Group ~ "^AU\s.*"
    mapping:
       - France
    rename:
      - field: group
        pattern: ^DE(.*)
        new_name: 1. DE$1

2. mapping.yml

Has the root item mappings which has the following top level entries:

  • templates optional
  • tags optional
  • mapping mandatory

2.1 templates

If you have a lot of repeats in you regexps, you can use templates to make your regexps cleaner. You can reference other templates in templates with !name!;

templates:
  - {name: delimiter, value: '[\s_-]*' }
  - {name: quality, value: '(?i)(?P<quality>HD|LQ|4K|UHD)?'}

With this definition you can use delimiter and quality in your regexp's surrounded with ! like.

^.*TF1!delimiter!Series?!delimiter!Films?(!delimiter!!quality!)\s*$

This will replace all occurrences of !delimiter! and !quality! in the regexp string.

2.2 tags

Has the following top level entries:

  • name: unique name of the tag.
  • captures: List of captured variable names like quality. The names should be equal to the regexp capture names.
  • concat: if you have more than one captures defined this is the join string between them
  • suffix: suffix for the tag
  • prefix: prefix for the tag

2.3 mapping

Has the following top level entries:

  • id mandatory
  • match_as_ascii optional default is false
  • mapper mandatory

2.3.1 id

Is referenced in the config.yml, should be a unique identifier

2.3.2 match_as_ascii

If you have non ascii characters in you playlist and want to write regexp without considering chars like é and use e instead, set this option to true. unidecode is used to convert the text.

2.3.3 mapper

Has the following top level entries:

  • pattern
  • attributes
  • suffix
  • prefix
  • assignments

2.3.4.1 pattern

The pattern is a string with a statement (@see filter statements). The pattern can have UnaryExpression NOT, BinaryExpression AND OR, and Comparison (Group|Title|Name|Url) ~ "regexp". Filter fields are Group, Title, Name and Url. Example filter: ((Group ~ "^DE.*") AND (NOT Title ~ ".*Shopping.*")) OR (Group ~ "^AU.*")

The regular expression syntax is similar to Perl-style regular expressions, but lacks a few features like look around and backreferences.

2.3.4.2 attributes

Attributes is a map of key value pairs. Valid keys are:

  • id
  • chno
  • name
  • group
  • title
  • logo
  • logo_small
  • parent_code
  • audio_track
  • time_shift
  • rec
  • source

If the regexps matches, the given fields will be set to the new value

2.3.4.3 suffix

Suffix is a map of key value pairs. Valid keys are

  • name
  • group
  • title

The special text <tag:tag_name> is used to append the tag if not empty. Example:

  suffix:
     name: '<tag:quality>'
     title: '-=[<tag:group>]=-'

In this example there must be 2 tag definitions quality and group.

If the regexps matches, the given fields will be appended to field value

2.3.4.4 prefix

Suffix is a map of key value pairs. Valid keys are

  • name
  • group
  • title

The special text <tag:tag_name> is used to append the tag if not empty Example:

  suffix:
     name: '<tag:quality>'
     title: '-=[<tag:group>]=-'

In this example there must be 2 tag definitions quality and group.

If the regexps matches, the given fields will be prefixed to field value

2.3.4.5 assignments

Attributes is a map of key value pairs. Valid keys and values are:

  • id
  • chno
  • name
  • group
  • title
  • logo
  • logo_small
  • parent_code
  • audio_track
  • time_shift
  • rec
  • source

Example configuration is:

assignments:
   title: name

This configuration sets title property to the value of name.

2.5 Example mapping.yml file.

mappings:
    templates:
      - name: delimiter
        value: '[\s_-]*'
      - name: quality
        value: '(?i)(?P<quality>HD|LQ|4K|UHD)?'
    tags:
      - name: quality
        captures:
          - quality
        concat: '|'
        prefix: ' [ '
        suffix: ' ]'
    mapping:
      - id: France
        match_as_ascii: true
        mapper:
          - pattern: 'Name ~ "^TF1$"'
            attributes:
              name: TF1
              id: TF1.fr,
              chno: '1',
              logo: https://upload.wikimedia.org/wikipedia/commons/thumb/3/3c/TF1_logo_2013.svg/320px-TF1_logo_2013.svg.png
            suffix:
              title: '<tag:quality>'
              group: '|FR|TNT'
            assignments:
              title: name
          - pattern: 'Name ~ "^TF1!delimiter!!quality!*Series[_ ]*Films$"'
            attributes:
              name: TF1 Series Films,
              id: TF1SeriesFilms.fr,
              chno: '20',
              logo: https://upload.wikimedia.org/wikipedia/commons/thumb/3/3c/TF1_logo_2013.svg/320px-TF1_logo_2013.svg.png,
            suffix:
              group: '|FR|TNT'

3. Compilation

Static binary for docker

Install prerequisites

rustup update
sudo apt-get install pkg-config musl-tools libssl-dev
rustup target add x86_64-unknown-linux-musl

Build statically linked binary

cargo build --target x86_64-unknown-linux-musl --release

Dockerize

Dockerfile

FROM scratch
COPY ./m3u-filter /
COPY ./config.yml /
COPY ./web /web
WORKDIR /
CMD ["./m3u-filter", "-s", "-c", "./config.yml"]

Image

docker build -t m3u-filter .

docker-compose.yml

version: '3'
services:
  m3u-filter:
    container_name: m3u-filter
    image: m3u-filter:latest
    working_dir: /
    volumes:
      - ./data:/data
    ports:
      - "8901:8901"
    restart: unless-stopped

The image should be around 15MB.

m3u-filter$ docker images
REPOSITORY                             TAG       IMAGE ID       CREATED        SIZE
m3u-filter                             latest    c59e1edb9e56   1 day ago     14.6MB

Cross compile for windows on linux

If you want to compile this project on linux for windows, you need to do the following steps.

Install mingw packages for your distribution

For ubuntu type:

sudo apt-get install gcc-mingw-w64

Install mingw support for rust

rustup target add x86_64-pc-windows-gnu
rustup toolchain install stable-x86_64-pc-windows-gnu

Compile it with:

cargo build --release --target x86_64-pc-windows-gnu

4. The EXTM3U format is an extension of the M3U format.

m3u has become almost a standard for the formation of playlists of media players and media devices.

A file in the EXTM3U format is a text file with the extension m3u or m3u8.

An example of the contents of the file in the EXTM3U format

#EXTM3U
#EXTINF:-1 tvg-name="Channel 1" tvg-logo="http://site.domain/channel1_logo.png" group-title="Group 1",Channel 1
http://site.domain/channel1
#EXTINF:-1 tvg-name="Channel 2" tvg-logo="http://site.domain/channel2_logo.png"  group-title="Group 2",Channel 2
http://site.domain/channel2
#EXTINF:-1 tvg-name="Channel 3" tvg-logo="http://site.domain/channel3_logo.png"  group-title="Group 2",Channel 3
http://site.domain/channel3
S
Description
Flexible IPTV playlist processor & proxy in Rust 🦀 - supports M3U, Xtream Codes, HDHomeRun, Plex, Emby & Jellyfin. Filtering, merging, scheduling & web delivery.
Readme MIT
139 MiB
v3.3.0
Latest
2026-04-02 14:05:15 +02:00
Languages
Rust 96.8%
SCSS 2.5%
Shell 0.5%
Dockerfile 0.1%