mirror of
https://github.com/meisnate12/Plex-Meta-Manager
synced 2024-11-24 05:23:05 +00:00
[1] Add Letterboxd Dynamic Collections (#2098)
This commit is contained in:
parent
de2e1852c5
commit
e4c5ef60ab
5 changed files with 107 additions and 31 deletions
20
CHANGELOG
20
CHANGELOG
|
@ -1,32 +1,14 @@
|
|||
# Requirements Update (requirements will need to be reinstalled)
|
||||
Updated PlexAPI requirement to 4.15.13
|
||||
Update lxml requirement to 5.2.2
|
||||
Update requests requirement to 2.32.3
|
||||
Update schedule requirement to 1.2.2
|
||||
Update setuptools requirement to 70.0.0
|
||||
|
||||
# Removed Features
|
||||
|
||||
# New Features
|
||||
Checks requirement versions to print a message if one needs to be updated
|
||||
Added the `mass_added_at_update` operation to mass set the Added At field of Movies and Shows.
|
||||
Add automated Anime Aggregations for AniDB matching
|
||||
Added `total_runtime` as a special text overlay variable.
|
||||
Added `top_tamil`, `top_telugu`, `top_malayalam`, `trending_india`, `trending_tamil`, and `trending_telugu` as options for `imdb_chart`
|
||||
Adds the `sort_by` attribute to `imdb_list`
|
||||
Added [`letterboxd_user_lists`](https://kometa.wiki/en/latest/files/dynamic_types/#letterboxd-user-lists) Dynamic Collection Type
|
||||
|
||||
# Updates
|
||||
Changed the `overlay_artwork_filetype` Setting to accept `webp_lossy` and `webp_lossless` while the old attribute `webp` will be treated as `webp_lossy`.
|
||||
|
||||
# Defaults
|
||||
Added Letterboxd Default [Collections](https://kometa.wiki/en/latest/defaults/chart/letterboxd/) and [Ribbon](https://kometa.wiki/en/latest/defaults/overlays/ribbon/)
|
||||
|
||||
# Bug Fixes
|
||||
Fixes #2034 `anilist_userlist` `score` attribute wasn't being validated correctly
|
||||
Fixes #1367 Error when trying to symlink the logs folder
|
||||
Fixes #2028 TMDb IDs were being ignored on the report
|
||||
Fixes a bug when parsing a comma-separated string of ints
|
||||
Fixes `imdb_chart` only getting 25 results
|
||||
Fixes `imdb_list` not returning items
|
||||
|
||||
Various other Minor Fixes
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
2.0.2
|
||||
2.0.2-build1
|
||||
|
|
|
@ -222,6 +222,64 @@ requirements of creating the collection.
|
|||
ending: latest
|
||||
```
|
||||
|
||||
??? blank "`letterboxd_user_lists` - Collections based on the Lists of Letterboxd Users.<a class="headerlink" href="#letterboxd-user-lists" title="Permanent link">¶</a>"
|
||||
|
||||
<div id="letterboxd-user-lists" />Creates collections for each of the Letterboxd lists that the user has created.
|
||||
|
||||
<hr style="margin: 0px;">
|
||||
|
||||
**`type` Value:** `letterboxd_user_lists`
|
||||
|
||||
**`data` Value:** [Dictionary](../kometa/yaml.md#dictionaries) of Attributes
|
||||
|
||||
??? blank "`username` - Determines the Usernames to scan for lists.<a class="headerlink" href="#letterboxd-user-lists-username" title="Permanent link">¶</a>"
|
||||
|
||||
<div id="letterboxd-user-lists-username" />This determines which Usernames are scanned.
|
||||
|
||||
**Allowed Values:** Username or list of Usernames
|
||||
|
||||
??? blank "`sort_by` - Determines the sort that the lists are returned.<a class="headerlink" href="#letterboxd-user-lists-sort-by" title="Permanent link">¶</a>"
|
||||
|
||||
<div id="letterboxd-user-lists-sort-by" />Determines the sort that the lists are returned.
|
||||
|
||||
**Allowed Values:** `updated`, `name`, `popularity`, `newest`, `oldest`
|
||||
|
||||
**Default:** `updated`
|
||||
|
||||
??? blank "`limit` - Determines the number of lists to create collections for.<a class="headerlink" href="#letterboxd-user-lists-limit" title="Permanent link">¶</a>"
|
||||
|
||||
<div id="letterboxd-user-lists-limit" />Determines the number of lists to create collections for. (`0` is all lists)
|
||||
|
||||
**Allowed Values:** Number 0 or greater
|
||||
|
||||
**Default:** `0`
|
||||
|
||||
**Valid Library Types:** Movies
|
||||
|
||||
**Key Values:** Letterboxd List URL
|
||||
|
||||
**Key Name Value:** Letterboxd List Title
|
||||
|
||||
**Default `title_format`:** `<<key_name>>`
|
||||
|
||||
??? tip "Default Template (click to expand)"
|
||||
|
||||
```yaml
|
||||
default_template:
|
||||
letterboxd_list_details: <<value>>
|
||||
```
|
||||
|
||||
???+ example "Example"
|
||||
|
||||
```yaml
|
||||
dynamic_collections:
|
||||
Letterboxd User Lists: # This name is the mapping name
|
||||
type: letterboxd_user_lists
|
||||
data:
|
||||
username: thebigpictures
|
||||
limit: 5
|
||||
```
|
||||
|
||||
??? blank "`trakt_user_lists` - Collections based on Trakt Lists by users.<a class="headerlink" href="#trakt-user-lists" title="Permanent link">¶</a>"
|
||||
|
||||
<div id="trakt-user-lists" />Creates collections for each of the Trakt lists for the specified users. Use `me` to
|
||||
|
|
|
@ -4,19 +4,30 @@ from modules.util import Failed
|
|||
|
||||
logger = util.logger
|
||||
|
||||
sort_options = {
|
||||
"name": "by/name/",
|
||||
"popularity": "by/popular/",
|
||||
"newest": "by/newest/",
|
||||
"oldest": "by/oldest/",
|
||||
"updated": ""
|
||||
}
|
||||
builders = ["letterboxd_list", "letterboxd_list_details"]
|
||||
base_url = "https://letterboxd.com"
|
||||
|
||||
class Letterboxd:
|
||||
def __init__(self, requests, cache):
|
||||
def __init__(self, requests, cache=None):
|
||||
self.requests = requests
|
||||
self.cache = cache
|
||||
|
||||
def _request(self, url, language, xpath=None):
|
||||
logger.trace(f"URL: {url}")
|
||||
response = self.requests.get_html(url, language=language)
|
||||
return response.xpath(xpath) if xpath else response
|
||||
|
||||
def _parse_page(self, list_url, language):
|
||||
if "ajax" not in list_url:
|
||||
list_url = list_url.replace("https://letterboxd.com/films", "https://letterboxd.com/films/ajax")
|
||||
logger.trace(f"URL: {list_url}")
|
||||
response = self.requests.get_html(list_url, language=language)
|
||||
response = self._request(list_url, language)
|
||||
letterboxd_ids = response.xpath("//li[contains(@class, 'poster-container') or contains(@class, 'film-detail')]/div/@data-film-id")
|
||||
items = []
|
||||
for letterboxd_id in letterboxd_ids:
|
||||
|
@ -44,19 +55,25 @@ class Letterboxd:
|
|||
return items
|
||||
|
||||
def _tmdb(self, letterboxd_url, language):
|
||||
logger.trace(f"URL: {letterboxd_url}")
|
||||
response = self.requests.get_html(letterboxd_url, language=language)
|
||||
ids = response.xpath("//a[@data-track-action='TMDb']/@href")
|
||||
ids = self._request(letterboxd_url, language, "//a[@data-track-action='TMDb']/@href")
|
||||
if len(ids) > 0 and ids[0]:
|
||||
if "themoviedb.org/movie" in ids[0]:
|
||||
return util.regex_first_int(ids[0], "TMDb Movie ID")
|
||||
raise Failed(f"Letterboxd Error: TMDb Movie ID not found in {ids[0]}")
|
||||
raise Failed(f"Letterboxd Error: TMDb Movie ID not found at {letterboxd_url}")
|
||||
|
||||
def get_user_lists(self, username, sort, language):
|
||||
next_page = [f"/{username}/lists/{sort_options[sort]}"]
|
||||
lists = []
|
||||
while next_page:
|
||||
response = self._request(f"{base_url}{next_page[0]}", language)
|
||||
sections = response.xpath("//div[@class='film-list-summary']/h2/a")
|
||||
lists.extend([(f"{base_url}{s.xpath('@href')[0]}", s.xpath("text()")[0]) for s in sections])
|
||||
next_page = response.xpath("//div[@class='pagination']/div/a[@class='next']/@href")
|
||||
return lists
|
||||
|
||||
def get_list_description(self, list_url, language):
|
||||
logger.trace(f"URL: {list_url}")
|
||||
response = self.requests.get_html(list_url, language=language)
|
||||
descriptions = response.xpath("//meta[@name='description']/@content")
|
||||
descriptions = self._request(f"{list_url}", language, xpath="//meta[@name='description']/@content")
|
||||
if len(descriptions) > 0 and len(descriptions[0]) > 0 and "About this list: " in descriptions[0]:
|
||||
return str(descriptions[0]).split("About this list: ")[1]
|
||||
return None
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import math, operator, os, re
|
||||
from datetime import datetime
|
||||
from modules import plex, ergast, util
|
||||
from modules import plex, ergast, util, letterboxd
|
||||
from modules.request import quote
|
||||
from modules.util import Failed, NotScheduled
|
||||
from plexapi.exceptions import NotFound, BadRequest
|
||||
|
@ -13,7 +13,7 @@ ms_auto = [
|
|||
"trakt_liked_lists", "trakt_people_list", "subtitle_language", "audio_language", "resolution", "decade", "imdb_awards"
|
||||
]
|
||||
auto = {
|
||||
"Movie": ["tmdb_collection", "edition", "country", "director", "producer", "writer"] + all_auto + ms_auto,
|
||||
"Movie": ["tmdb_collection", "edition", "country", "director", "producer", "writer", "letterboxd_user_lists"] + all_auto + ms_auto,
|
||||
"Show": ["network", "origin_country", "episode_year"] + all_auto + ms_auto,
|
||||
"Artist": ["mood", "style", "country", "album_genre", "album_mood", "album_style", "track_mood"] + all_auto,
|
||||
"Video": ["country", "content_rating"] + all_auto
|
||||
|
@ -33,6 +33,7 @@ default_templates = {
|
|||
"tmdb_collection": {"tmdb_collection_details": "<<value>>", "minimum_items": 2},
|
||||
"trakt_user_lists": {"trakt_list_details": "<<value>>"},
|
||||
"trakt_liked_lists": {"trakt_list_details": "<<value>>"},
|
||||
"letterboxd_user_lists": {"letterboxd_list_details": "<<value>>"},
|
||||
"tmdb_popular_people": {"tmdb_person": "<<value>>", "plex_search": {"all": {"actor": "tmdb"}}},
|
||||
"trakt_people_list": {"tmdb_person": "<<value>>", "plex_search": {"all": {"actor": "tmdb"}}}
|
||||
}
|
||||
|
@ -1096,6 +1097,24 @@ class MetadataFile(DataFile):
|
|||
auto_list[k] = v
|
||||
elif auto_type == "trakt_liked_lists":
|
||||
_check_dict(self.config.Trakt.all_liked_lists())
|
||||
elif auto_type == "letterboxd_user_lists":
|
||||
dynamic_data = util.parse("Config", "data", dynamic, parent=map_name, methods=methods, datatype="dict")
|
||||
if "data" in self.temp_vars:
|
||||
temp_data = util.parse("Config", "data", self.temp_vars["data"], datatype="dict")
|
||||
for k, v in temp_data.items():
|
||||
dynamic_data[k] = v
|
||||
letter_methods = {am.lower(): am for am in dynamic_data}
|
||||
users = util.parse("Config", "username", dynamic_data, parent=f"{map_name} data", methods=letter_methods, datatype="strlist")
|
||||
sort = util.parse("Config", "sort_by", dynamic_data, parent=f"{map_name} data", methods=letter_methods, options=letterboxd.sort_options, default="updated")
|
||||
limit = util.parse("Config", "limit", dynamic_data, parent=f"{map_name} data", methods=letter_methods, datatype="int", minimum=0, default=0)
|
||||
final = {}
|
||||
for user in users:
|
||||
out = self.config.Letterboxd.get_user_lists(user, sort, self.language)
|
||||
if limit != 0:
|
||||
out = out[:limit]
|
||||
for url, name in out:
|
||||
final[url] = name
|
||||
_check_dict(final)
|
||||
elif auto_type == "tmdb_popular_people":
|
||||
if "data" in self.temp_vars:
|
||||
dynamic_data = util.parse("Config", "data", self.temp_vars["data"], datatype="int", minimum=1)
|
||||
|
|
Loading…
Reference in a new issue