Skip to content

Commit 5cdc66c

Browse files
committed
Switched to using youtube video trailers instead of the native plex trailers for consistency with other UMC-compatible integrations
1 parent cdd6c21 commit 5cdc66c

File tree

3 files changed

+49
-32
lines changed

3 files changed

+49
-32
lines changed

custom_components/plex_recently_added/manifest.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
"iot_class": "local_polling",
1010
"issue_tracker": "https://github.com/custom-components/sensor.plex_recently_added/issues",
1111
"requirements": [],
12-
"version": "0.4.7"
12+
"version": "0.4.8"
1313
}

custom_components/plex_recently_added/plex_api.py

+13-31
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from homeassistant.core import HomeAssistant
77
from .const import DEFAULT_PARSE_DICT, USER_AGENT, ACCEPTS
88
from .parser import parse_data, parse_library
9-
9+
from .tmdb_api import get_tmdb_trailer_url
1010

1111
def check_headers(response):
1212
if 'text/xml' not in response.headers.get('Content-Type', '') and 'application/xml' not in response.headers.get('Content-Type', ''):
@@ -38,33 +38,6 @@ def __init__(
3838
self._section_libraries = section_libraries
3939
self._exclude_keywords = exclude_keywords
4040
self._images_base_url = f'/{name.lower() + "_" if len(name) > 0 else ""}plex_recently_added'
41-
42-
async def get_trailer_url(self, item_key):
43-
extras_url = f'http{self._ssl}://{self._host}:{self._port}/library/metadata/{item_key}/extras?X-Plex-Token={self._token}'
44-
try:
45-
extras_res = await self._hass.async_add_executor_job(
46-
requests.get,
47-
extras_url,
48-
{
49-
"headers": {
50-
"User-agent": USER_AGENT,
51-
"Accept": ACCEPTS,
52-
},
53-
"timeout": 10
54-
}
55-
)
56-
check_headers(extras_res)
57-
root = ElementTree.fromstring(extras_res.text)
58-
59-
for video in root.findall(".//Video"):
60-
if video.get("type") == "clip" and video.get("subtype") == "trailer":
61-
part = video.find(".//Part")
62-
if part is not None and part.get("key"):
63-
return f'http{self._ssl}://{self._host}:{self._port}{part.get("key")}&X-Plex-Token={self._token}'
64-
65-
except Exception as e:
66-
print(f"Error fetching trailer: {str(e)}")
67-
return None
6841

6942
async def update(self):
7043
info_url = 'http{0}://{1}:{2}'.format(
@@ -147,7 +120,7 @@ async def update(self):
147120

148121
# Fetch trailer URLs for each item
149122
for item in parsed_libs:
150-
item['trailer'] = await self.get_trailer_url(item['ratingKey'])
123+
item['trailer'] = await get_tmdb_trailer_url(self._hass, item['title'], library['type'])
151124

152125
if library["type"] not in data['all']:
153126
data['all'][library["type"]] = []
@@ -156,14 +129,23 @@ async def update(self):
156129

157130
data_out = {}
158131
for k in data.keys():
159-
data_out[k] = {'data': [DEFAULT_PARSE_DICT] + parse_data(data[k], self._max, info_url, self._token, identifier, k, self._images_base_url, k == "all")}
132+
parsed_data = parse_data(data[k], self._max, info_url, self._token, identifier, k, self._images_base_url, k == "all")
133+
134+
# Ensure trailer URLs are correctly set for the "all" sensor
135+
if k == "all":
136+
for item in parsed_data:
137+
if item.get('trailer') is None:
138+
item_type = 'movie' if item.get('episode') == '' else 'show'
139+
item['trailer'] = await get_tmdb_trailer_url(self._hass, item['title'], item_type)
140+
141+
data_out[k] = {'data': [DEFAULT_PARSE_DICT] + parsed_data}
160142

161143
return {
162144
"data": {**data_out},
163145
"online": True,
164146
"libraries": libs
165147
}
166-
148+
167149

168150
class FailedToLogin(Exception):
169151
"Raised when the Plex user fail to Log-in"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import aiohttp
2+
3+
TMDB_API_KEY = '1f7708bb9a218ab891a5d438b1b63992'
4+
TMDB_SEARCH_URL = 'https://api.themoviedb.org/3/search/{media_type}?api_key={api_key}&query={query}'
5+
TMDB_DETAILS_URL = 'https://api.themoviedb.org/3/{media_type}/{tmdb_id}?api_key={api_key}&append_to_response=videos'
6+
7+
async def get_tmdb_trailer_url(hass, title, media_type):
8+
if media_type == 'show':
9+
media_type = 'tv'
10+
elif media_type == 'movie':
11+
media_type = 'movie'
12+
else:
13+
return None
14+
15+
async with aiohttp.ClientSession() as session:
16+
# Search for the movie or TV show
17+
search_url = TMDB_SEARCH_URL.format(media_type=media_type, api_key=TMDB_API_KEY, query=title)
18+
async with session.get(search_url) as response:
19+
search_data = await response.json()
20+
if not search_data.get('results'):
21+
return None
22+
23+
tmdb_id = search_data['results'][0]['id']
24+
25+
# Get details including videos
26+
details_url = TMDB_DETAILS_URL.format(media_type=media_type, tmdb_id=tmdb_id, api_key=TMDB_API_KEY)
27+
async with session.get(details_url) as response:
28+
details_data = await response.json()
29+
30+
videos = details_data.get('videos', {}).get('results', [])
31+
for video in videos:
32+
if video['type'] == 'Trailer' and video['site'] == 'YouTube':
33+
return f'https://www.youtube.com/watch?v={video["key"]}'
34+
35+
return None

0 commit comments

Comments
 (0)