I wrote a Python script to do so. I can’t share the complete script because it’s part of a big Python library that does various things with Plex. All is done with the Web-URLs.
My files are usually renamed like:
Movie (yyyy) {imdb-12345678} {edition-editionname} - part1.mkv
With this script I rename them to the following and patch the edition as empty and unlocked. Notice the different brace styles:
Movie (yyyy) {imdb-12345678} [edition-editionname] - part1.mkv
Just the relevant part. Use at your own risk:
library_key = findLibrary("movie", args.library)
if library_key:
if args.ratingkey:
data = getUrl(f"/library/sections/{library_key}/all?id={args.ratingkey}")
elif args.edition:
data = getUrl(f"/library/sections/{library_key}/all?editionTitle={args.edition}")
elif args.file:
data = getUrl(f"/library/sections/{library_key}/all?file={args.file}")
elif args.recentlyadded:
data = getUrl(f"/library/sections/{library_key}/recentlyAdded")
elif args.title:
data = getUrl(f"/library/sections/{library_key}/all?title={args.title}")
elif args.year:
data = getUrl(f"/library/sections/{library_key}/all?year={args.year}")
else:
data = getUrl(f"/library/sections/{library_key}/all")
if data:
media_container = ET.ElementTree(ET.fromstring(data.text))
for video in media_container.findall("Video"):
movies += 1
video_editiontitle = video.get("editionTitle")
if video_editiontitle:
video_key = video.get("key")
data = getUrl(f"{video_key}")
if data:
media_container2 = ET.ElementTree(ET.fromstring(data.text))
for video2 in media_container2.findall("Video"):
video2_editiontitle_locked = None
for video2_field in video2.findall("Field"):
video2_field_locked = video2_field.get("locked")
video2_field_name = video2_field.get("name")
if video2_field_name == FIELDNAMES["Edition"]:
if video2_field_locked == "1":
video2_editiontitle_locked = True
break
video2_ratingkey = video2.get("ratingKey")
video2_title = video2.get("title")
video2_year = video2.get("year")
video2_media = video2.find("Media")
found = None
for video2_media_part in video2_media.findall("Part"):
video2_media_part_file = video2_media_part.get("file")
file_path, file_filename, file_extension = split_filename(video2_media_part_file)
path_old = os.path.basename(os.path.normpath(file_path))
imdbid = ""
tmdbid = ""
tvdbid = ""
for guid in video2.findall("Guid"):
guid_id = guid.get("id")
if "imdb://" in guid_id:
imdbid = " {imdb-" + guid_id.replace("imdb://", "") + "}"
if imdbid in video2_media_part_file:
break
elif "tmdb://" in guid_id:
tmdbid = " {tmdb-" + guid_id.replace("tmdb://", "") + "}"
if tmdbid in video2_media_part_file:
break
elif "tvdb://" in guid_id:
tvdbid = " {tvdb-" + guid_id.replace("tvdb://", "") + "}"
if tvdbid in video2_media_part_file:
break
id = ""
if imdbid:
id = imdbid
elif tmdbid:
id = tmdbid
elif tvdbid:
id = tvdbid
edition = ""
if video_editiontitle:
edition = " [edition-" + video_editiontitle + "]"
if not edition and "[edition-" in file_filename:
match_edition_unofficial = pattern_edition_unofficial.match(file_filename)
if match_edition_unofficial:
edition = " [edition-" + match_edition_unofficial[2] + "]"
part = ""
if " - part" in file_filename:
match_part = pattern_part.match(file_filename)
if match_part:
part = " - part" + match_part[1]
year = ""
if video2_year:
year = f" ({video2_year})"
title_new = processUmlauts(video2_title)
movies_verarbeitet += 1
filename_new = f"{title_new}{year}{id}{edition}{part}.{file_extension}"
if os.path.isfile(video2_media_part_file) and file_filename + "." + file_extension != filename_new:
if not args.dryrun:
os.rename(video2_media_part_file, f"{file_path}/{filename_new}")
found = True
if video2_editiontitle_locked:
params = {
"id": video2_ratingkey,
"includeExternalMedia": 1,
"type": SECTIONTYPES["movie"],
FIELDNAMES["Edition"]+".locked": 0,
}
if not args.dryrun:
data = requests.put(f"{plex_url}/library/sections/{library_key}/all", params=params, headers=plex_headers)
params = {
"id": video2_ratingkey,
"includeExternalMedia": 1,
"type": SECTIONTYPES["movie"],
FIELDNAMES["Edition"]+".value": "",
}
if not args.dryrun:
data = requests.put(f"{plex_url}/library/sections/{library_key}/all", params=params, headers=plex_headers)
Some additional stuff:
FIELDNAMES = {
#
"Edition": "editionTitle",
#
}
SECTIONTYPES = {
#
"movie": 1,
#
}
pattern_edition = re.compile("^(.*)\{edition-(.*)\}(.*)")
pattern_edition_unofficial = re.compile("^(.*)\[edition-(.*)\](.*)")
pattern_part = re.compile(".* part([0-9]*).*")
def split_foldername(folder):
foldername = ""
path = ""
index_path = folder.rfind("/")
if index_path:
foldername = folder[index_path + 1:]
path = folder[:index_path]
return path, foldername
def split_filename(file):
extension = ""
filename = ""
path = ""
path, filename = split_foldername(file)
if filename:
index_extension = filename.rfind(".")
if index_extension:
extension = filename[index_extension + 1:]
filename = filename[:index_extension]
return path, filename, extension
def getUrl(url, use_headers = True):
if use_headers:
return requests.get(f"{plex_url}{url}", headers=plex_headers)
else:
return requests.get(f"{plex_url}{url}")
plex_url = "http://***.***.***.***:32400"
plex_username = "***@***.***"
plex_password = "********"
plex_client_name = "A"
plex_client_version = "1"
plex_client_id = hashlib.sha512("{} {}".format(plex_client_name, plex_client_version).encode()).hexdigest()
plex_base64string = base64.b64encode("{}:{}".format(plex_username, plex_password).encode())
plex_headers = {
"Authorization": "Basic {}".format(plex_base64string.decode("ascii")),
"X-Plex-Client-Identifier": plex_client_id,
"X-Plex-Product": plex_client_name,
"X-Plex-Version": plex_client_version,
}
def processUmlauts(text):
text = text.replace("ä", "ae")
text = text.replace("ö", "oe")
text = text.replace("ü", "ue")
text = text.replace("Ä", "Ae")
text = text.replace("Ö", "Oe")
text = text.replace("Ü", "Ue")
text = text.replace("ß", "ss")
text = text.replace("&", " Und ")
text = text.replace("+", " Plus ")
text = text.replace("$", "s")
text = text.replace(":", " ")
text = text.replace("-", " ")
text = text.replace("_", " ")
text = text.replace("²", " 2")
text = text.replace("³", " 3")
text = text.replace("½", " 1 2")
text = text.replace("/", " ")
text = unidecode(text)
text = re.sub("[^a-zA-Z0-9 ]+", "", text)
text = re.sub("\s+", " ", text)
text = text.title()
return text.strip()
def findLibrary(library_type, library_name):
data = getUrl("/library/sections")
if data:
media_container = ET.ElementTree(ET.fromstring(data.text))
for directory in media_container.findall("Directory"):
directory_key = directory.get("key")
directory_title = directory.get("title")
directory_type = directory.get("type")
if directory_type == library_type and directory_title == library_name:
return directory_key
return None