mirror of
https://github.com/stashapp/CommunityScripts.git
synced 2026-04-12 09:52:34 -05:00
Initial commit of Plex metadata agent to pull Stash data (#15)
Full bundle is included, put into "3rd party' directory to segment from Stash based scripts
This commit is contained in:
192
3rd party/StashPlexAgent.bundle/Contents/Code/__init__.py
Normal file
192
3rd party/StashPlexAgent.bundle/Contents/Code/__init__.py
Normal file
@@ -0,0 +1,192 @@
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import string
|
||||
import thread
|
||||
import threading
|
||||
import copy
|
||||
import json
|
||||
import dateutil.parser as dateparser
|
||||
from urllib2 import HTTPError
|
||||
from urllib2 import quote
|
||||
from datetime import datetime
|
||||
from lxml import etree
|
||||
|
||||
# preferences
|
||||
preference = Prefs
|
||||
DEBUG = preference['debug']
|
||||
if DEBUG:
|
||||
Log('Agent debug logging is enabled!')
|
||||
else:
|
||||
Log('Agent debug logging is disabled!')
|
||||
|
||||
def ValidatePrefs():
|
||||
pass
|
||||
|
||||
|
||||
def Start():
|
||||
Log("Stash metadata agent started")
|
||||
HTTP.Headers['Accept'] = 'application/json'
|
||||
HTTP.CacheTime = 0.1
|
||||
ValidatePrefs()
|
||||
|
||||
|
||||
def HttpReq(url, authenticate=True, retry=True):
|
||||
Log("Requesting: %s" % url)
|
||||
api_string = ''
|
||||
if Prefs['APIKey']:
|
||||
api_string = '&apikey=%s' % Prefs['APIKey']
|
||||
|
||||
if Prefs['UseHTTPS']:
|
||||
connectstring = 'https://%s:%s/graphql?query=%s%s'
|
||||
else:
|
||||
connectstring = 'http://%s:%s/graphql?query=%s%s'
|
||||
try:
|
||||
connecttoken = connectstring % (Prefs['Hostname'], Prefs['Port'], url, api_string)
|
||||
Log(connecttoken)
|
||||
return JSON.ObjectFromString(
|
||||
HTTP.Request(connecttoken).content)
|
||||
except Exception, e:
|
||||
if not retry:
|
||||
raise e
|
||||
return HttpReq(url, authenticate, False)
|
||||
|
||||
class StashPlexAgent(Agent.Movies):
|
||||
name = 'Stash Plex Agent'
|
||||
languages = [Locale.Language.English]
|
||||
primary_provider = True
|
||||
accepts_from = [
|
||||
'com.plexapp.agents.xbmcnfo'
|
||||
]
|
||||
|
||||
def search(self, results, media, lang):
|
||||
DEBUG = Prefs['debug']
|
||||
file_query = r"""query{findScenes(scene_filter:{path:{value:"\"<FILENAME>\"",modifier:INCLUDES}}){scenes{id,title,date,studio{id,name}}}}"""
|
||||
mediaFile = media.items[0].parts[0].file
|
||||
filename = String.Unquote(mediaFile).encode('utf8', 'ignore')
|
||||
filename = os.path.splitext(os.path.basename(filename))[0]
|
||||
if filename:
|
||||
filename = str(quote(filename.encode('UTF-8')))
|
||||
query = file_query.replace("<FILENAME>", filename)
|
||||
request = HttpReq(query)
|
||||
if DEBUG:
|
||||
Log(request)
|
||||
movie_data = request['data']['findScenes']['scenes']
|
||||
score = 100 if len(movie_data) == 1 else 85
|
||||
for scene in movie_data:
|
||||
if scene['date']:
|
||||
title = scene['title'] + ' - ' + scene['date']
|
||||
else:
|
||||
title = scene['title']
|
||||
Log("Title Found: " + title + " Score: " + str(score) + " ID:" + scene['id'])
|
||||
results.Append(MetadataSearchResult(id = str(scene['id']), name = title, score = int(score), lang = lang))
|
||||
|
||||
|
||||
def update(self, metadata, media, lang, force=False):
|
||||
DEBUG = Prefs['debug']
|
||||
Log("update(%s)" % metadata.id)
|
||||
mid = metadata.id
|
||||
id_query = "query{findScene(id:%s){path,id,title,details,url,date,rating,paths{screenshot,stream}studio{id,name,image_path,parent_studio{id,name,details}}tags{id,name}performers{name,image_path}movies{movie{name}}}}"
|
||||
data = HttpReq(id_query % mid)
|
||||
data = data['data']['findScene']
|
||||
metadata.collections.clear()
|
||||
|
||||
if data['date']:
|
||||
try:
|
||||
Log("Trying to parse:" + data["date"])
|
||||
date=dateparser().parse(data["date"])
|
||||
except Exception as ex:
|
||||
Log(ex)
|
||||
date=None
|
||||
pass
|
||||
# Set the date and year if found.
|
||||
if date is not None:
|
||||
metadata.originally_available_at = date
|
||||
metadata.year = date.year
|
||||
|
||||
# Get the title
|
||||
if data['title']:
|
||||
metadata.title = data["title"]
|
||||
|
||||
# Get the Studio
|
||||
if not data["studio"] is None:
|
||||
metadata.studio = data["studio"]["name"]
|
||||
|
||||
# Get the rating
|
||||
if not data["rating"] is None:
|
||||
metadata.rating = float(data["rating"]) * 2
|
||||
if Prefs["CreateRatingTags"]:
|
||||
if int(data["rating"]) > 0:
|
||||
rating = str(int(data["rating"]))
|
||||
ratingstring = "Rating: " + rating + " Stars"
|
||||
try:
|
||||
metadata.collections.add(ratingstring)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Set the summary
|
||||
if data['details']:
|
||||
summary=data["details"].replace("\n"," ").replace("\r", "").replace("\t","")
|
||||
metadata.summary = summary
|
||||
|
||||
# Set series and add to collections
|
||||
if Prefs["CreateCollectionTags"]:
|
||||
if not data["studio"] is None:
|
||||
site="Site: " + data["studio"]["name"]
|
||||
try:
|
||||
metadata.collections.add(site)
|
||||
except:
|
||||
pass
|
||||
if not data["studio"]["parent_studio"] is None:
|
||||
site="Studio: " + data["studio"]["parent_studio"]["name"]
|
||||
else:
|
||||
site="Studio: " + data["studio"]["name"]
|
||||
try:
|
||||
metadata.collections.add(site)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Add the genres
|
||||
metadata.genres.clear()
|
||||
if Prefs["IgnoreTags"]:
|
||||
ignore_tags = Prefs["IgnoreTags"].split(",")
|
||||
ignore_tags = list(map(lambda x: x.strip(), ignore_tags))
|
||||
try:
|
||||
if data["tags"]:
|
||||
genres = data["tags"]
|
||||
for genre in genres:
|
||||
if not genre["id"] in ignore_tags and "ambiguous" not in genre["name"].lower():
|
||||
metadata.genres.add(genre["name"])
|
||||
except:
|
||||
pass
|
||||
|
||||
# Add the performers
|
||||
metadata.roles.clear()
|
||||
# Create and populate role with actor's name
|
||||
try:
|
||||
if data["performers"]:
|
||||
api_string = ""
|
||||
if Prefs['APIKey']:
|
||||
api_string = '&apikey=%s' % Prefs['APIKey']
|
||||
models=data["performers"]
|
||||
for model in models:
|
||||
if DEBUG:
|
||||
Log("Pulling Model: " + model["name"] + " With Image: " + model["image_path"])
|
||||
role = metadata.roles.new()
|
||||
role.name=model["name"]
|
||||
role.photo=model["image_path"] + api_string
|
||||
except:
|
||||
pass
|
||||
|
||||
# Add posters and fan art.
|
||||
if not data["paths"]["screenshot"] is None:
|
||||
api_string = ""
|
||||
if Prefs['APIKey']:
|
||||
api_string = '&apikey=%s' % Prefs['APIKey']
|
||||
try:
|
||||
thumb = HTTP.Request(data["paths"]["screenshot"] + api_string)
|
||||
metadata.posters[data["paths"]["screenshot"] + api_string] = Proxy.Preview(thumb)
|
||||
metadata.art[data["paths"]["screenshot"] + api_string] = Proxy.Preview(thumb)
|
||||
except:
|
||||
pass
|
||||
|
||||
50
3rd party/StashPlexAgent.bundle/Contents/DefaultPrefs.json
Normal file
50
3rd party/StashPlexAgent.bundle/Contents/DefaultPrefs.json
Normal file
@@ -0,0 +1,50 @@
|
||||
[
|
||||
{
|
||||
"id": "Hostname",
|
||||
"label": "The host for Stash",
|
||||
"type": "text",
|
||||
"default": "127.0.0.1"
|
||||
},
|
||||
{
|
||||
"id": "Port",
|
||||
"label": "The port for Stash",
|
||||
"type": "text",
|
||||
"default": "9999"
|
||||
},
|
||||
{
|
||||
"id": "UseHTTPS",
|
||||
"label": "Use HTTPS instead of HTTP to connect",
|
||||
"type": "bool",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"id": "APIKey",
|
||||
"label": "The API Key for Stash if Authentication is enabled",
|
||||
"type": "text",
|
||||
"default": ""
|
||||
},
|
||||
{
|
||||
"id": "IgnoreTags",
|
||||
"label": "Stash Tag ID numbers to ignore (comma separated, 0 to disable)",
|
||||
"type": "text",
|
||||
"default": "1,2"
|
||||
},
|
||||
{
|
||||
"id": "CreateCollectionTags",
|
||||
"label": "Auto create Plex Collection tags for Site and Studio",
|
||||
"type": "bool",
|
||||
"default": true
|
||||
},
|
||||
{
|
||||
"id": "CreateRatingTags",
|
||||
"label": "Auto create Plex Collection tags for Stash star rating",
|
||||
"type": "bool",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"id": "debug",
|
||||
"label": "Use debug logging",
|
||||
"type": "bool",
|
||||
"default": false
|
||||
},
|
||||
]
|
||||
28
3rd party/StashPlexAgent.bundle/Contents/Info.plist
Normal file
28
3rd party/StashPlexAgent.bundle/Contents/Info.plist
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>StashPlexAgent</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.plexapp.agents.stashplexagent</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>PlexFrameworkVersion</key>
|
||||
<string>2</string>
|
||||
<key>PlexPluginClass</key>
|
||||
<string>Agent</string>
|
||||
<key>PlexPluginMode</key>
|
||||
<string>AlwaysOn</string>
|
||||
<key>PlexPluginCodePolicy</key>
|
||||
<string>Elevated</string>
|
||||
</dict>
|
||||
</plist>
|
||||
18
3rd party/StashPlexAgent.bundle/README.md
Normal file
18
3rd party/StashPlexAgent.bundle/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# StashPlexAgent.bundle
|
||||
A very simplistic Plex agent to pull metadata from Stash.
|
||||
|
||||
Scenes are matched based on filename (without path or extension) against the Stash "Path", so files must be scanned into Stash with their current filename.
|
||||
|
||||
Preferences are set under the plugin, or in the library definition (if you set it as the primary agent for the library). I'm using "Video Files Scanner" with it.
|
||||
|
||||
By default it will create Plex "Site: <STUDIO>" and "Studio: <STUDIO PARENT>" collection tags, but this can be disabled in preferences. (If the studio doesn't have a parent then the primary studio will be used instead and be in both tags)
|
||||
|
||||
Also Stash "Tags" are placed into Plex "Genres"
|
||||
|
||||
You can also set tag numbers to ignore on import, I've left mine in as an example. You probably want to change these unless your "temporary" tags miraculously line up with mine. (Also initially you might need to try saving a couple of times. Plex seems to not want to initally keep changes in this field for some reason)
|
||||
|
||||
For installing just download the bundle and put it into your "\PlexMediaServer\Plex Media Server\Plug-ins" folder. (The entire bundle as a directory... "\StashPlexAgent.bundle")
|
||||
|
||||
I guarantee there will be problems. When they pop up feel free to get with me (@Darklyter) on either the TPDB or Stash Discord channels.
|
||||
|
||||
Also this agent only handles scenes currently. I haven't played with movies in Stash yet, but can take a look if there is interest. Currently the Plex ADE agent handles that for me.
|
||||
Reference in New Issue
Block a user