nix

configuration files that power my machines
Log | Files | Refs | README | LICENSE

parse-playlist (3845B)


      1 #!/usr/bin/env python3
      2 
      3 import os
      4 import re
      5 import sys
      6 from pathlib import Path
      7 
      8 def parse_playlist(playlist_path):
      9     """
     10     Parse M3U/M3U8 playlist file to extract artist and song information.
     11     Outputs in format: Artist Name - Song Name
     12     """
     13     songs = []
     14 
     15     # Enhanced patterns for music files
     16     patterns = [
     17         # Artist - Song.mp3
     18         r'^(?P<artist>.+?)\s*-\s*(?P<song>.+?)\.[^.]+$',
     19         # Song by Artist.mp3
     20         r'^(?P<song>.+?)\s+by\s+(?P<artist>.+?)\.[^.]+$',
     21         # Artist_-_Song.mp3 or Artist_-_Song.m4a etc
     22         r'^(?P<artist>.+?)_-_(?P<song>.+?)\.[^.]+$',
     23         # ArtistName--SongName.mp3
     24         r'^(?P<artist>.+?)--(?P<song>.+?)\.[^.]+$'
     25     ]
     26 
     27     try:
     28         with open(playlist_path, 'r', encoding='utf-8') as f:
     29             current_title = None
     30 
     31             for line in f:
     32                 line = line.strip()
     33 
     34                 # Handle extended M3U directives
     35                 if line.startswith('#EXTINF:'):
     36                     # Try to extract title from EXTINF line
     37                     parts = line.split(',', 1)
     38                     if len(parts) > 1:
     39                         current_title = parts[1]
     40                     continue
     41                 elif line.startswith('#'):
     42                     continue
     43                 elif not line:
     44                     continue
     45 
     46                 # Get just the filename without the path and extension
     47                 filename = os.path.basename(line)
     48                 name_without_ext = os.path.splitext(filename)[0]
     49 
     50                 # First try to use EXTINF title if available
     51                 if current_title:
     52                     if ' - ' in current_title:
     53                         artist, song = current_title.split(' - ', 1)
     54                         songs.append({
     55                             'artist': artist.strip(),
     56                             'song': song.strip()
     57                         })
     58                         current_title = None
     59                         continue
     60 
     61                 # If no EXTINF or couldn't parse it, try filename patterns
     62                 song_info = None
     63 
     64                 # Try each pattern until we find a match
     65                 for pattern in patterns:
     66                     match = re.match(pattern, filename)
     67                     if match:
     68                         song_info = {
     69                             'artist': match.group('artist').strip(),
     70                             'song': match.group('song').strip()
     71                         }
     72                         break
     73 
     74                 # If no pattern matched, try to split on the first hyphen if it exists
     75                 if not song_info and ' - ' in name_without_ext:
     76                     parts = name_without_ext.split(' - ', 1)
     77                     if len(parts) == 2:
     78                         song_info = {
     79                             'artist': parts[0].strip(),
     80                             'song': parts[1].strip()
     81                         }
     82 
     83                 # If still no match, use the whole filename as the song name
     84                 if not song_info:
     85                     song_info = {
     86                         'artist': 'Unknown',
     87                         'song': name_without_ext.strip()
     88                     }
     89 
     90                 songs.append(song_info)
     91 
     92         return songs
     93 
     94     except Exception as e:
     95         print(f"Error reading playlist: {e}")
     96         return []
     97 
     98 def main():
     99     # Check if playlist file was provided as argument
    100     if len(sys.argv) != 2:
    101         print("Usage: parse-playlist playlist_file.m3u8")
    102         return
    103 
    104     playlist_path = sys.argv[1]
    105 
    106     if not os.path.exists(playlist_path):
    107         print(f"Playlist file '{playlist_path}' not found.")
    108         return
    109 
    110     songs = parse_playlist(playlist_path)
    111 
    112     # Print in requested format
    113     for song in songs:
    114         print(f"{song['artist']} - {song['song']}")
    115 
    116 if __name__ == "__main__":
    117     main()