Skip to content

Instantly share code, notes, and snippets.

@keiya
Last active May 3, 2024 09:59
Show Gist options
  • Save keiya/0ff5af32d399ef010c89d48896e57931 to your computer and use it in GitHub Desktop.
Save keiya/0ff5af32d399ef010c89d48896e57931 to your computer and use it in GitHub Desktop.
foobar2000 Accessing UPnP DLNA Example (also Python upnpclient example)

Generate M3U Playlist from foobar2000 UPnP/DLNA Server

I have many songs stored in foobar2000 on Windows, and I wanted to listen to them from other Macs or tablets. foobar2000 mac and foobar2000 mobile seemed suitable for this purpose, but it took a long time to list the songs. So I used this script to create a playlist in advance to save me the trouble.

How to use

  • step 0: create playlist in your foobar2000
  • step 1: install UPnP/DLNA Renderer, Server, Control Point in your foobar2000
    • and make some configuration
  • step 2: install pip packages
    • upnpclient
  • step 3: run python3 upnp.py

ObjectID explained (foobar2000)

+ root : ObjectID = 0 
+--+ Playlists : ObjectID = 0/0 
   +--- Playlist 1 : ObjectID = 0/0/0
   +--- Playlist 2 : ObjectID = 0/0/1
   +--- Playlist 3 : ObjectID = 0/0/2
        ...
   +--- Playlist n : ObjectID = 0/0/n
+--- Media Library : ObjectID = 0/1 
+--- Playback Stream : ObjectID = 0/2

User Agent of this script

Use it for UPnP/DLNA Server configuration (Tools->UPnP->Server->Streaming Profiles in your foobar2000 Library->Configure) python-requests/2.25.1

import upnpclient
import pprint
pp = pprint.PrettyPrinter(indent=4)
import xml.etree.ElementTree as ET
# specify foobar2000 upnp endpoint
d = upnpclient.Device("http://192.168.0.206:12345/DeviceDescription.xml")
s = d.services
def parse_playlists(xml):
# parsing playlists
playlists = []
root = ET.fromstring(xml)
for container in root.findall("./"):
container_attr = container.attrib
title_elm = container.find(".//dc:title", namespaces={'dc': 'http://purl.org/dc/elements/1.1/'})
title = title_elm.text
result = { 'id': container_attr['id'], 'title': title }
playlists.append(dict(result))
return playlists
def parse_songs(xml):
songs = []
root = ET.fromstring(xml)
for item in root.findall("./{urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/}item"):
item_attr = item.attrib
title_elm = item.find(".//dc:title", namespaces={'dc': 'http://purl.org/dc/elements/1.1/'})
creator_elm = item.find(".//dc:creator", namespaces={'dc': 'http://purl.org/dc/elements/1.1/'})
resources = [{"meta": res.attrib, "url": res.text} for res in item.findall(".//{urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/}res")]
result = { 'id': item_attr['id'], 'title': title_elm.text, 'creator': creator_elm.text, 'resources': resources}
songs.append(dict(result))
return songs
# Getting ObjectID of Playlists
# Playlists: 0/0
# Media Library: 0/1
# Playback Stream: 0/2
browsed_playlists = s[0].actions[0](ObjectID="0/0", BrowseFlag="BrowseDirectChildren", Filter="*", StartingIndex=0, RequestedCount=100, SortCriteria="")
playlists = parse_playlists(browsed_playlists['Result'])
pp.pprint(playlists)
# Getting songs in specific playlist
# specify ObjectID, which is a playlist that you want to generate
browsed_playlist = s[0].actions[0](ObjectID="0/0/0", BrowseFlag="BrowseDirectChildren", Filter="*", StartingIndex=0, RequestedCount=100, SortCriteria="")
songs = parse_songs(browsed_playlist['Result'])
pp.pprint(songs)
def duration_to_sec(formatted_duration):
hour, minute, second = formatted_duration.split(":")
print(hour, minute, second)
return round(int(hour) * 3600 + int(minute) * 60 + float(second))
# generating m3u8 playlist file
with open("playlist.m3u8",'w') as fh:
fh.write("#EXTM3U\n")
for song in songs:
duration = duration_to_sec(song['resources'][0]['meta']['duration'])
extinf = "#EXTINF:{},{} - {}".format(duration, song["creator"], song["title"])
line = '\n'.join([extinf, song['resources'][0]['url']])
fh.write(line + "\n")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment