2025-01-23, 09:18 PM
(This post was last modified: 2025-01-23, 09:23 PM by johnson. Edited 5 times in total.)
For anyone else who does a search, I wrote my own little python script. This gets you 90% of the way and the last 10% is manual. All steps are below.
1. Export playlist from VLC in
2. Run the script below in the terminal
3. In
1. Export playlist from VLC in
xspf
format. Name it input.xspf
2. Run the script below in the terminal
$ python3 convert_xspf_to_xml.py
. It will create a file called output.xml
3. In
output.xml, manually change the url encoded characters to spaces, apostrophes, etc. Do a search for % (the percent sign). Use "Find & Replace All" to do replace these out quickly.
4. Manually delete the text
file: // Again, "Find & Replace All" does this in one shot
5. Create a Playlist in Jellyfin with one song in the JF app/UI. Give your playlist a title.
6. Navigate to
/var/lib/jellyfin/data/playlists/ and find your playlist folder, in that folder there will be a
playlist.xml file. Open it and add in the extra songs from your
output.xml file. Then save it.
7. Rescan metadata in Jellyfin.
8. Profit.
Code:import xml.etree.ElementTree as ET
def parse_xspf(file_path):
tree = ET.parse(file_path)
root = tree.getroot()
# Define the namespace dictionary
ns = {'xspf': 'http://xspf.org/ns/0/', 'vlc': 'http://www.videolan.org/vlc/playlist/ns/0/'}
playlist_items = []
# Iterate through each track in the trackList and extract relevant information
for track in root.find('xspf:trackList', ns):
location = track.find('xspf:location', ns).text
playlist_item = ET.Element("PlaylistItem")
path_elem = ET.SubElement(playlist_item, "Path")
path_elem.text = location
playlist_items.append(playlist_item)
return playlist_items
def generate_xml(output_path, playlist_items):
root_element = ET.Element("Item")
# Add static elements inside the Item element
added = ET.SubElement(root_element, "Added")
lock_data = ET.SubElement(root_element, "LockData")
local_title = ET.SubElement(root_element, "LocalTitle")
running_time = ET.SubElement(root_element, "RunningTime")
owner_user_id = ET.SubElement(root_element, "OwnerUserId")
# Add empty elements
added.text = ""
lock_data.text = ""
local_title.text = ""
running_time.text = ""
owner_user_id.text = ""
shares = ET.SubElement(root_element, "Shares")
playlist_media_type = ET.SubElement(root_element, "PlaylistMediaType")
playlist_media_type.text = "Audio"
# Create PlaylistItems element and append PlaylistItem elements
playlist_items_elem = ET.SubElement(root_element, "PlaylistItems")
for item in playlist_items:
playlist_items_elem.append(item)
tree = ET.ElementTree(root_element)
tree.write(output_path, encoding='utf-8', xml_declaration=True)
def main():
input_file = 'input.xspf' # Path to the XSPF file
output_file = 'output.xml' # Desired path for the output XML file
playlist_items = parse_xspf(input_file)
generate_xml(output_file, playlist_items)
if __name__ == "__main__":
main()
Save & run this as:
convert_xspf_to_xml.py