[46] add letterboxd filters

pull/811/head
meisnate12 3 years ago
parent 4928a25b71
commit 534eda26dc

@ -1 +1 @@
1.16.2-develop45 1.16.2-develop46

@ -35,3 +35,21 @@ collections:
collection_order: custom collection_order: custom
sync_mode: sync sync_mode: sync
``` ```
You can add 3 different filters directly to this builder.
| Filter Attribute | Description |
|:-----------------|:---------------------------------------------------------------------------------------------------|
| `rating` | **Description:** Search for the specified rating range<br>**Values:** range of int i.e. `80-100` |
| `year` | **Description:** Search for the specified year range<br>**Values:** range of int i.e. `1990-1999` |
| `note` | **Description:** Search for the specified value in the note<br>**Values:** Any String |
```yaml
collections:
Vultures 101 Best Movie Endings From the 90s:
letterboxd_list_details:
url: https://letterboxd.com/brianformo/list/vultures-101-best-movie-endings/
year: 1990-1999
collection_order: custom
sync_mode: sync
```

@ -1084,11 +1084,11 @@ class CollectionBuilder:
def _letterboxd(self, method_name, method_data): def _letterboxd(self, method_name, method_data):
if method_name.startswith("letterboxd_list"): if method_name.startswith("letterboxd_list"):
letterboxd_lists = self.config.Letterboxd.validate_letterboxd_lists(method_data, self.language) letterboxd_lists = self.config.Letterboxd.validate_letterboxd_lists(self.Type, method_data, self.language)
for letterboxd_list in letterboxd_lists: for letterboxd_list in letterboxd_lists:
self.builders.append(("letterboxd_list", letterboxd_list)) self.builders.append(("letterboxd_list", letterboxd_list))
if method_name.endswith("_details"): if method_name.endswith("_details"):
self.summaries[method_name] = self.config.Letterboxd.get_list_description(letterboxd_lists[0], self.language) self.summaries[method_name] = self.config.Letterboxd.get_list_description(letterboxd_lists[0]["url"], self.language)
def _mal(self, method_name, method_data): def _mal(self, method_name, method_data):
if method_name == "mal_id": if method_name == "mal_id":
@ -2575,7 +2575,7 @@ class CollectionBuilder:
items = self.library.get_filter_items(search_data[2]) items = self.library.get_filter_items(search_data[2])
previous = None previous = None
for i, item in enumerate(items, 0): for i, item in enumerate(items, 0):
if len(self.items) <= i or item.ratingKey != self.items[i]: if len(self.items) <= i or item.ratingKey != self.items[i].ratingKey:
text = f"after {util.item_title(previous)}" if previous else "to the beginning" text = f"after {util.item_title(previous)}" if previous else "to the beginning"
logger.info(f"Moving {util.item_title(item)} {text}") logger.info(f"Moving {util.item_title(item)} {text}")
self.library.moveItem(self.obj, item, previous) self.library.moveItem(self.obj, item, previous)

@ -1,4 +1,4 @@
import time import re, time
from modules import util from modules import util
from modules.util import Failed from modules.util import Failed
@ -20,6 +20,16 @@ class Letterboxd:
for letterboxd_id in letterboxd_ids: for letterboxd_id in letterboxd_ids:
slugs = response.xpath(f"//div[@data-film-id='{letterboxd_id}']/@data-film-slug") slugs = response.xpath(f"//div[@data-film-id='{letterboxd_id}']/@data-film-slug")
items.append((letterboxd_id, slugs[0])) items.append((letterboxd_id, slugs[0]))
slugs = response.xpath(f"//div[@data-film-id='{letterboxd_id}']/@data-film-slug")
notes = response.xpath(f"//div[@data-film-id='{letterboxd_id}']/parent::li/div[@class='film-detail-content']/div/p/text()")
ratings = response.xpath(f"//div[@data-film-id='{letterboxd_id}']/parent::li/div[@class='film-detail-content']//span[contains(@class, 'rating')]/@class")
years = response.xpath(f"//div[@data-film-id='{letterboxd_id}']/parent::li/div[@class='film-detail-content']/h2/small/a/text()")
rating = None
if ratings:
match = re.search("rated-(\\d+)", ratings[0])
if match:
rating = int(match.group(1))
items.append((letterboxd_id, slugs[0], int(years[0]) if years else None, notes[0] if notes else None, rating))
next_url = response.xpath("//a[@class='next']/@href") next_url = response.xpath("//a[@class='next']/@href")
if len(next_url) > 0: if len(next_url) > 0:
time.sleep(2) time.sleep(2)
@ -44,27 +54,50 @@ class Letterboxd:
descriptions = response.xpath("//meta[@property='og:description']/@content") descriptions = response.xpath("//meta[@property='og:description']/@content")
return descriptions[0] if len(descriptions) > 0 and len(descriptions[0]) > 0 else None return descriptions[0] if len(descriptions) > 0 and len(descriptions[0]) > 0 else None
def validate_letterboxd_lists(self, letterboxd_lists, language): def validate_letterboxd_lists(self, err_type, letterboxd_lists, language):
valid_lists = [] valid_lists = []
for letterboxd_list in util.get_list(letterboxd_lists, split=False): for letterboxd_dict in util.get_list(letterboxd_lists, split=False):
list_url = letterboxd_list.strip() if not isinstance(letterboxd_dict, dict):
if not list_url.startswith(base_url): letterboxd_dict = {"url": letterboxd_dict}
raise Failed(f"Letterboxd Error: {list_url} must begin with: {base_url}") dict_methods = {dm.lower(): dm for dm in letterboxd_dict}
elif len(self._parse_list(list_url, language)) > 0: final = {
valid_lists.append(list_url) "url": util.parse(err_type, "url", letterboxd_dict, methods=dict_methods, parent="letterboxd_list").strip(),
else: "note": util.parse(err_type, "note", letterboxd_dict, methods=dict_methods, parent="letterboxd_list") if "note" in dict_methods else None,
raise Failed(f"Letterboxd Error: {list_url} failed to parse") "rating": util.parse(err_type, "rating", letterboxd_dict, methods=dict_methods, datatype="int", parent="letterboxd_list", maximum=100, range_split="-") if "rating" in dict_methods else None,
"year": util.parse(err_type, "year", letterboxd_dict, methods=dict_methods, datatype="int", parent="letterboxd_list", minimum=1000, maximum=3000, range_split="-") if "year" in dict_methods else None
}
if not final["url"].startswith(base_url):
raise Failed(f"{err_type} Error: {final['url']} must begin with: {base_url}")
elif not self._parse_list(final["url"], language):
raise Failed(f"{err_type} Error: {final['url']} failed to parse")
valid_lists.append(final)
return valid_lists return valid_lists
def get_tmdb_ids(self, method, data, language): def get_tmdb_ids(self, method, data, language):
if method == "letterboxd_list": if method == "letterboxd_list":
logger.info(f"Processing Letterboxd List: {data}") logger.info(f"Processing Letterboxd List: {data}")
items = self._parse_list(data, language) items = self._parse_list(data["url"], language)
total_items = len(items) total_items = len(items)
if total_items > 0: if total_items > 0:
ids = [] ids = []
filtered_ids = []
for i, item in enumerate(items, 1): for i, item in enumerate(items, 1):
letterboxd_id, slug = item letterboxd_id, slug, year, note, rating = item
filtered = False
if data["year"]:
start_year, end_year = data["year"].split("-")
if not year or int(end_year) < year or year < int(start_year):
filtered = True
if data["rating"]:
start_rating, end_rating = data["rating"].split("-")
if not rating or int(end_rating) < rating or rating < int(start_rating):
filtered = True
if data["note"]:
if not note or data["note"] not in note:
filtered = True
if filtered:
filtered_ids.append(slug)
continue
logger.ghost(f"Finding TMDb ID {i}/{total_items}") logger.ghost(f"Finding TMDb ID {i}/{total_items}")
tmdb_id = None tmdb_id = None
expired = None expired = None
@ -80,6 +113,8 @@ class Letterboxd:
self.config.Cache.update_letterboxd_map(expired, letterboxd_id, tmdb_id) self.config.Cache.update_letterboxd_map(expired, letterboxd_id, tmdb_id)
ids.append((tmdb_id, "tmdb")) ids.append((tmdb_id, "tmdb"))
logger.info(f"Processed {total_items} TMDb IDs") logger.info(f"Processed {total_items} TMDb IDs")
if filtered_ids:
logger.info(f"Filtered: {filtered_ids}")
return ids return ids
else: else:
raise Failed(f"Letterboxd Error: No List Items found in {data}") raise Failed(f"Letterboxd Error: No List Items found in {data}")

@ -521,8 +521,11 @@ class MetadataFile(DataFile):
"name": template_name, "name": template_name,
"value": other_keys, "value": other_keys,
auto_type: other_keys, auto_type: other_keys,
"key_name": str(map_name), "key": str(map_name) "key_name": other_name, "key": "other"
} }
for k, v in template_variables.items():
if "other" in v:
template_call[k] = v["other"]
col = {"template": template_call, "label": str(map_name)} col = {"template": template_call, "label": str(map_name)}
if test: if test:
col["test"] = True col["test"] = True

@ -860,7 +860,7 @@ class Plex(Library):
self.query_data(getattr(obj, f"remove{attr_call}"), _remove) self.query_data(getattr(obj, f"remove{attr_call}"), _remove)
display += f"-{', -'.join(_remove)}" display += f"-{', -'.join(_remove)}"
final = f"{obj.title[:25]:<25} | {attr_display} | {display}" if display else display final = f"{obj.title[:25]:<25} | {attr_display} | {display}" if display else display
if do_print: if do_print and final:
logger.info(final) logger.info(final)
return final return final

Loading…
Cancel
Save