Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

creating smart playlists #551

Closed
cburgard opened this issue Aug 12, 2020 · 6 comments · Fixed by #763
Closed

creating smart playlists #551

cburgard opened this issue Aug 12, 2020 · 6 comments · Fixed by #763
Labels

Comments

@cburgard
Copy link

I'm currently in the process of migrating my old XBMC-based playlists into Plex. However, the majority of my playlists are "smart" playlists, which xbmc saves as xml files of the following format:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<smartplaylist type="songs">
    <name>Cthulhu</name>
    <match>one</match>
    <rule field="artist" operator="is">Nox Arcana</rule>
    <rule field="artist" operator="contains">Musica Cthulhiana</rule>
    <order direction="ascending">random</order>
</smartplaylist>

Looking at the interactive interface to create smart playlists in plex, it seems that there's pretty much a 1:1 mapping of the xml file to the plex smart playlist definition.

However, trying to use the smart playlist feature with plexapi to batch-convert my playlists, I have a bunch of questions:

  • the creation requests a kwargs argument that should map to the categories. However, it's unclear how for example the operators (is, contains, ...) are supposed to be passed over
  • the playlists created with this method are broken, that is, they just contain the entire library, and clicking on the "Edit Filters" button in the GUI doesn't actually do anything, which also makes it hard to debug/understand what exactly is going wrong here

It would be nice if the SmartPlaylist feature could be bugfixed or at the very least documented a bit better to make clear how the operators are supposed to be passed along, and to throw specific error messages when an illegal request has been made that would lead to a broken playlist.

@blacktwin
Copy link
Collaborator

Operators can be found here.

the playlists created with this method are broken

What method are you using? Do you have a code snippet?

@cburgard
Copy link
Author

cburgard commented Aug 12, 2020

Thanks for the link for the operators. Here's a snippet of the code I'm using.

def parse_xsp(xsp):
    import xml.etree.ElementTree as ET
    tree = ET.parse(xsp)
    root = tree.getroot()

    order = root.find("order")
    if order is not None and order.text == "random":
        random = True
    else:
        random = False
        
    return {
        "name": root.find("name").text,
        "match": root.find("match").text,
        "random": random,
        "rules": [ {"text":rule.text, "field":rule.get("field"), "operator":str(rule.get("operator")).strip()} for rule in root.findall("rule") ]
    }

def main(args):
    from plexapi.myplex import MyPlexAccount
    from plexapi.playlist import Playlist
    account = MyPlexAccount(args.account, args.password)
    plex = account.resource(args.server).connect()

    if args.upload:
        for infile in args.input:
            if infile.endswith(".xsp"):
                try:
                    xsp = parse_xsp(infile)
                    name = xsp["name"]
                    for playlist in plex.playlists():
                        if playlist.title == name:
                            print("deleting "+name)
                            playlist.delete()
                    print("attempting to create "+name)
                    playlist = Playlist.create(server=plex,title=name,section=plex.library.section("Music"),smart=True,kwargs={ rule["field"]:rule["text"] for rule in xsp["rules"]})
                except RuntimeError as err:
                    print("error in "+infile+": "+err.message)
                    continue
            else:
                print("file format not yet supported: "+infile)    

if __name__ == "__main__":
    from argparse import ArgumentParser
    parser = ArgumentParser(description="helper script to sync playlists with Plex")
    parser.add_argument("path",type=str,help="path to the music folder")
    parser.add_argument("--upload",dest="upload",action="store_true",help="upload playlists to Plex")
    parser.add_argument("--download",dest="download",action="store_true",help="download playlists missing on the local disk")
    parser.add_argument("--output",dest="output",default=".",type=str,help="folder to contain the downloaded playlists")
    parser.add_argument("--input",dest="input",nargs="+",default=[],help="playlists to be uploaded")
    parser.add_argument("--server",type=str,required=True,default=None,help="Plex server to talk to")
    parser.add_argument("--account",type=str,required=False,default=None,help="Plex account name. If not given, you will be asked to enter it.")
    parser.add_argument("--password",type=str,required=False,default=None,help="Plex account password. If not given, you will be asked to enter it.")
    args = parser.parse_args()

    if not args.account:
        args.account = input("Plex Account: ")
    if not args.password:
        import getpass
        args.password = getpass.getpass("Password for " + args.account + ": ")
    
    main(args)

@blacktwin
Copy link
Collaborator

Based on the XML from XBMC I'd say that the operators are not 1:1. You'd probably need to form your kwarg outside of the Playlst.create() method to make the changes to operators to work with plexapi.

@cburgard
Copy link
Author

cburgard commented Aug 13, 2020

Yes, that's true. I've tried it with some simple replacements, now, but I still get the same behavior: "Broken" smart playlists are created that simply contain my entire music library and where clicking on the "Edit Filters" button doesn't do anything. Here's the call:

Playlist.create(server=plex,title=name,section=plexmusic,smart=True,kwargs=filters)

Where filters in this example prints to {'artist__exact': 'Nox Arcana', 'artist__contains': 'Musica Cthulhiana'}.

Am I doing something stupid here?

One thing I seem to be missing out on is how to do the boolean operation between the filters - how do I decide if I want the "and" or the "or" of all the filters that I give? (But this seems not to be the main issue at the moment, because getting this wrong would lead to the playlist being empty in this case, and not the playlist being my entire library).

@blacktwin
Copy link
Collaborator

blacktwin commented Aug 13, 2020

No, it looks like this feature (creating a smart Playlist) is broken. We'll have to look into it.

@cburgard
Copy link
Author

Great, thanks for confirming! If there's anything I can do to help you debug this, please let me know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants