Skip to content

Commit a558427

Browse files
authored
Merge pull request #569 from HarryS/main
Add PlexSync plugin
2 parents f45bb60 + 75bacb6 commit a558427

File tree

5 files changed

+247
-0
lines changed

5 files changed

+247
-0
lines changed

plugins/PlexSync/PlexSync.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import stashapi.log as log
2+
import re
3+
from stashapi.stashapp import StashInterface
4+
import sys
5+
import json
6+
import unidecode
7+
import requests
8+
9+
10+
def processScene(scene):
11+
tags = []
12+
13+
if settings["skipUnorganized"] and not scene['organized']: # don't process scenes that aren't marked organized.
14+
log.info("Not organized, bye!")
15+
sys.exit()
16+
17+
18+
if settings["tagStashIDs"]:
19+
if not scene["stash_ids"]: # Tagging empty Stash ID
20+
tags.append(int(settings["tagID_emptystashid"]))
21+
22+
for stashbox in scene["stash_ids"]: # Add all the stashbox ID tags
23+
url = stashbox['endpoint']
24+
if (url == "https://stashdb.org/graphql"):
25+
tags.append(int(settings["tagID_stashdb"]))
26+
27+
if (url == "https://pmvstash.org/graphql"):
28+
tags.append(int(settings["tagID_pmvstash"]))
29+
30+
if (url == "https://theporndb.net/graphql"):
31+
tags.append(int(settings["tagID_porndb"]))
32+
33+
if (url == "https://fansdb.cc/graphql"):
34+
tags.append(int(settings["tagID_fansdb"]))
35+
36+
if (url == "https://javstash.org/graphql"):
37+
tags.append(int(settings["tagID_javstash"]))
38+
39+
if (len(tags) != 0):
40+
for x in scene['tags']:
41+
if (int(x['id']) in tags):
42+
log.debug(f"We already have the tag {x['id']} from {tags}")
43+
tags.remove(int(x['id']))
44+
45+
if tags: # Just a double check that we still have pending tags to add
46+
log.info(f"Adding the stashbox tags {tags} to scene {scene['title']}")
47+
stash.update_scenes({"ids": scene["id"], "tag_ids": {"mode": "ADD", "ids": tags}})
48+
49+
50+
if settings["cleanTitles"]:
51+
if scene['title']: # Lots of title fixing. It's messy, but works good to remove weird characters and other improvements.
52+
new_title = unidecode.unidecode(scene['title'], errors='ignore') # NB!!: Cyrillic and such gets changed to "normal" characters.
53+
new_title = re.sub('^( | |@|\')+', "", new_title)
54+
new_title = re.sub(' +$', "", new_title)
55+
new_title = re.sub('^!+', "", new_title)
56+
new_title = re.sub("^\\?+", "", new_title)
57+
new_title = re.sub("^\\*+", "", new_title)
58+
new_title = re.sub('^The ("|\')', 'The ', new_title)
59+
new_title = re.sub('^A ("|\')', 'A ', new_title)
60+
new_title = re.sub(' (Wmv|Mp4|Mov|Qt|HD)$', '', new_title)
61+
new_title = re.sub('^[^A-Za-z0-9]', '', new_title)
62+
new_title = re.sub('^( )', '', new_title)
63+
if not (new_title == scene['title']):
64+
log.info(f"New title: {new_title} from {scene['title']}")
65+
if (new_title == ""):
66+
new_title = "ZZZ Invalid Title"
67+
stash.update_scenes({"ids": scene["id"], "title": new_title})
68+
69+
70+
if scene["urls"]: # If scene contains Plex URL, refresh Plex metadata
71+
for urls in reversed(scene["urls"]): # todo: remove reversed(); this was a quickfix for when a scene somehow got two plex urls - the most recent url is most likely the most "valid"
72+
if ("/library/metadata/" in urls):
73+
plexurl = urls.replace("plex", "https://" + settings["plexHost"] + ":" + settings["plexPort"])
74+
plexurl += "/refresh?X-Plex-Token=" + settings["plexToken"]
75+
log.info(f"Refreshing scene on Plex via {plexurl}")
76+
try:
77+
requests.packages.urllib3.disable_warnings() # TLS errors are irrelevant when I'm on localhost...
78+
r = requests.put(plexurl, verify=False)
79+
r.raise_for_status()
80+
except requests.exceptions.HTTPError as e:
81+
log.error(f"Plex API failed: {e.response.text}")
82+
83+
return
84+
85+
86+
json_input = json.loads(sys.stdin.read())
87+
FRAGMENT_SERVER = json_input["server_connection"]
88+
stash = StashInterface(FRAGMENT_SERVER)
89+
90+
config = stash.get_configuration()
91+
settings = {
92+
"plexToken": "YOUR_PLEX_TOKEN_HERE",
93+
"plexHost": "localhost",
94+
"plexPort": "32400",
95+
"tagStashIDs": False,
96+
"skipUnorganized": True,
97+
"cleanTitles": True,
98+
"tagID_emptystashid": "1",
99+
"tagID_stashdb": "2",
100+
"tagID_pmvstash": "3",
101+
"tagID_porndb": "4",
102+
"tagID_fansdb": "5",
103+
"tagID_javstash": "6"
104+
}
105+
if "PlexSync" in config["plugins"]:
106+
settings.update(config["plugins"]["PlexSync"])
107+
108+
if "hookContext" in json_input["args"]:
109+
id = json_input["args"]["hookContext"]["id"]
110+
if (
111+
json_input["args"]["hookContext"]["type"] == "Scene.Update.Post"
112+
or "Scene.Create.Post"
113+
):
114+
#log.info(f"We run with {json_input}") # Uncomment for debugging what's sent to Stash
115+
exit = False
116+
request_tags = []
117+
stashbox_tags = []
118+
stashbox_tags.append(frozenset(
119+
{
120+
int(settings["tagID_emptystashid"]),
121+
int(settings["tagID_stashdb"]),
122+
int(settings["tagID_pmvstash"]),
123+
int(settings["tagID_porndb"]),
124+
int(settings["tagID_fansdb"]),
125+
int(settings["tagID_javstash"])
126+
}))
127+
128+
if "inputFields" in json_input["args"]["hookContext"]:
129+
if "tag_ids" in json_input["args"]["hookContext"]["inputFields"]:
130+
request_tags = json_input["args"]["hookContext"]["input"]["tag_ids"]
131+
if set(request_tags) & set(stashbox_tags): # prevent rerunning tag addition, when the update is simply adding the tags
132+
log.info("Already have correct tags.")
133+
exit = True
134+
else:
135+
exit = False
136+
137+
if "urls" in json_input["args"]["hookContext"]["inputFields"]:
138+
if len(json_input["args"]["hookContext"]["inputFields"]) == 2: # Hacky fix; Plex agent sends only two fields, so in this case we won't update metadata there again. Stash UI etc usually sends all fields in update, so no worries.
139+
log.info("Got new Plex URL, will not refresh.")
140+
exit = True
141+
else:
142+
exit = False
143+
144+
if exit == True:
145+
log.info("Nothing to do, exiting.")
146+
sys.exit()
147+
148+
scene = stash.find_scene(id)
149+
processScene(scene)

plugins/PlexSync/PlexSync.yml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: Plex Sync
2+
description: Refresh Plex metadata when scene updated in Stash. Requires the StashPlexAgent.bundle agent.
3+
version: 0.1
4+
url: https://github.com/stashapp/CommunityScripts/
5+
exec:
6+
- python3
7+
- "{pluginDir}/PlexSync.py"
8+
interface: raw
9+
settings:
10+
plexToken:
11+
displayName: Plex server token
12+
description: The X-Plex-Token for your connected Plex server.
13+
type: STRING
14+
plexHost:
15+
displayName: Plex server address
16+
description: Address to your Plex server; only hostname (do NOT add "https" or similar). "localhost" should work if Stash is on the same machine.
17+
type: STRING
18+
plexPort:
19+
displayName: Plex server port
20+
description: Port to your Plex server. Usually "32400"
21+
type: STRING
22+
tagStashIDs:
23+
displayName: Set tags on scenes based on their Stashbox source. Suggested default False.
24+
description: If enabled, you MUST manually create each tag, and set all the following tagID configs.
25+
type: BOOLEAN
26+
skipUnorganized:
27+
displayName: Do not process scenes that are not marked as "organized". Suggested default True.
28+
type: BOOLEAN
29+
cleanTitles:
30+
displayName: Convert away non-ASCII characters in titles. Suggested default True.
31+
description: This is useful especially for "new Plex experience", but it will remove non-ASCII characters such as Cyrillic script.
32+
type: BOOLEAN
33+
tagID_emptystashid:
34+
displayName: Tag ID for scenes not containing any Stashbox URL
35+
type: STRING
36+
tagID_stashdb:
37+
displayName: Tag ID for scenes from StashDB
38+
type: STRING
39+
tagID_pmvstash:
40+
displayName: Tag ID for scenes from PMVStash
41+
type: STRING
42+
tagID_porndb:
43+
displayName: Tag ID for scenes from PornDB
44+
type: STRING
45+
tagID_fansdb:
46+
displayName: Tag ID for scenes from FansDB
47+
type: STRING
48+
tagID_javstash:
49+
displayName: Tag ID for scenes from JAVStash
50+
type: STRING
51+
52+
hooks:
53+
- name: Scene update
54+
description: Run tasks for PlexSync
55+
triggeredBy:
56+
- Scene.Update.Post

plugins/PlexSync/README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Stash Plugin updating your Plex metadata automatically
2+
3+
This plugin solves the problem of "I have many files in my Plex, but they don't get any of the changes I do in Stash, and doing a `refresh all metadata` takes too much time".
4+
5+
With this, Stash behaves as the main source for all your Stash scenes in Plex, and it keeps Plex in sync with changes done via Stash.
6+
7+
8+
9+
# Install
10+
11+
Install the plugin in Stash first, and then install the updated [Stash-Plex-Agent](https://github.com/Darklyter/StashPlexAgent.bundle) on your Plex server.
12+
13+
## Stash side
14+
15+
1. Install plugin via the CommunityScripts repository.
16+
17+
2. Go to the install directory and install requirements: `python3 -m pip install -r requirements.txt -t .` -- or, install the required packages globally `python3 -m pip install stashapi unidecode requests`
18+
19+
3. Configure the plugin in Stash UI.
20+
21+
## Plex side
22+
23+
Do this *after* making sure the Stash side is complete.
24+
25+
1. After installing the newest version of this agent, make sure that `AddPlexURL` is enabled ("Adds the Plex media ID to the scene in Stash; allows Stash to update Plex metadata.")
26+
27+
2. Refresh all metadata in Plex for the libraries using this agent.
28+
29+
Now, you should see scenes being updated in Stash, adding this URL to the scenes: `plex/library/metadata/12345` (12345 being the metadata ID of the scene in Plex)
30+
31+
# Usage
32+
33+
Update your scenes in Stash like normal, and these scenes will be automatically refreshed in Plex. 🎉
34+
35+
# Warnings
36+
- If you have the "clean titles" option enabled in plugin, all titles are processed with `unidecode`. Basically, all non-ASCII characters in your titles will be converted; Cyrillic script for example will be taken away.
37+
38+
- This plugin connects to your Plex via TLS, but it ignores cert errors. But this is not really a problem, as your Stash is most likely on the same host as your Plex...

plugins/PlexSync/TODO.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Automatic support for getting/creating tag IDs. And/or have them more dynamically name-based.

plugins/PlexSync/requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
stashapp-tools
2+
unidecode
3+
requests

0 commit comments

Comments
 (0)