pkkid / python-plexapi

Python bindings for the Plex API.
BSD 3-Clause "New" or "Revised" License
1.11k stars 198 forks source link

Media Part - Accessible / Exists / Available #385

Closed Animosity022 closed 4 years ago

Animosity022 commented 4 years ago

I can see in the XML, the media part with the location for the file name will show if it's available or not available.

A nice user on reddit detailed what I was looking for and typed up what needed to be done to get access to the attribute in the API:

https://www.reddit.com/r/PleX/comments/davwpo/pythonplexapi_unavailable_status_for_a_location/

This was the message:

I could be wrong, but from what I can see they python plex api currently doesn't have the "accessable" item under a Media objects Parts in the attributes as something that can be queried. You could request that be added. It If you want to add it yourself you can add self.accessible = data.attrib.get('accessible') to the media.py file around line 107. Once that is added you can then get that information by looking at the parts of the movie object. something like video.reload() for m in video.media: for p in m.parts: print (p.accessible) If you look at the xml info you'll notice plex has it setup to have that information for each part of each media object. ie, if you have the multiple copies of a movie, and each copy is made up of multiple files(part 1, part 2...) than this will show you that information for each part for each movie.

This is the screenshot as well:

image
blacktwin commented 4 years ago

While the suggestion feels like it should work, it doesn't. For some reason the accessible and exits attribs are not getting pulled in with the object's xml _data. Looks like ?checkFiles=1 needs to be added to the initial url string.

blacktwin commented 4 years ago

This issue can be approached a couple of ways. Either edit the base.py to include the ?checkFiles=1 call and include self.accessible and self.exists to the media.py or only make the media.py edits and have users reload the video object with the _details_key in order to see the added exists and accessible values:

movie = plex.fetchItem(107063)
movie = plex.fetchItem(movie._details_key)
print(movie.media[0].parts[0].exists)

And lastly, the solution I've provided in the referenced PR.

pkkid commented 4 years ago

I don't have a good answer on which to choose.

Animosity022 commented 4 years ago

Let me share my use case as I mainly use Plex on Google Drive so it's all cloud storage for majority of my media. I do not have Empty Trash on as if there was an issue with the mount, it would automatically delete everything and scanning in media is time consuming.

The way my system works is that I have a local staging disk and a rclone mount that is under a single mergerfs mount. Any time something gets upgraded, it would delete a version on the cloud side (most likely) and copy a new version locally. I move from local to cloud over night behind the scenes.

That being said, when I delete something or upgrade, I have a trash canned item sitting around. I can mainly empty the trash, but rather do something a bit more elegant.

I was trying to replace this sql with an API friendly method:

conn = sqlite3.connect('/var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Plug-in Support/Databases/com.plexapp.plugins.library.db')
c = conn.cursor()
deleted_metadata = \
c.execute('SELECT count(*) FROM metadata_items WHERE deleted_at IS NOT NULL').fetchone()[0]
deleted_media_parts = \
c.execute('SELECT count(*) FROM media_parts WHERE deleted_at IS NOT NULL').fetchone()[0]
conn.close()

deleted_count = int(deleted_metadata) + int(deleted_media_parts)

So I run that every so often and clean up the trash via the API methods as that's cleaner than doing curls or something else.

Hope that gives the use case.

blacktwin commented 4 years ago

@animosity22 This PR would check the video object to see if it exists and is accessible a long with providing the file path as there may be multiple files.

If you want to test this PR you could use the following:

for movie in plex.library.section("Movies").all():
    for part in movie.exists():
        print(part)

The output would look like this: (True, True, "F:\\media\\Movies\\The 'Burbs (1989)\\The 'Burbs (1989).mkv") (exists, accessible, file_path)

amazingr4b commented 4 years ago

After adding the one line to the media.py I can then get the accessible field. For example, I have one file for "Child's Play." I added debug prints for media id, part id, part file, and the accessible to my moviedetails function, which normally just gets a movie object and spits out various details like rating and summary.

When the file is in my movies directory:

C:\Users\rob\Desktop\v5\Posted>python myplextv_alt.py moviedetails "Child's Play
"
Media ID: 473539
Part ID: 478761
File Name: X:\Shared Videos\Movies\Childs Play (2019).m4v
Accessible: 1
...

If I move the file out of my movies directory and then run the same action:

C:\Users\rob\Desktop\v5\Posted>python myplextv_alt.py moviedetails "Child's Play "

Media ID: 473539 Part ID: 478761 File Name: X:\Shared Videos\Movies\Childs Play (2019).m4v Accessible: 0 ...

Hellowlol commented 4 years ago

I havnt tested this, but i think we just need to add the attribute it should reload is the value is none. Has this been tested?

Arg, we subclass plexobject. Anyway imo we should just require a reload it check this.

blacktwin commented 4 years ago

When I tested just adding the attribute I did not get anything. Attribute didn't display. Not until after adding the ?checkFiles=1 to the queried string did the attributes populate and become visible. I could be running an older version. Once I get back to a computer will reinstall and test.

Hellowlol commented 4 years ago

We need to add the attribute first. After that’s done you just reload the item.

blacktwin commented 4 years ago

Ok, fresh reinstall. Adding attributes and the following produces the expected response.

movie = plex.fetchItem(107063)
print(movie.media[0].parts[0].exists) # None
movie.reload()
print(movie.media[0].parts[0].exists) # 1

Will update the PR to add the attributes and update the exists() function.

blacktwin commented 4 years ago

@animosity22 Looks like this can be closed. Please use the above code to confirm.

Animosity022 commented 4 years ago

Thanks @blacktwin - Looks good!