Compare commits

...

5 Commits

6 changed files with 290 additions and 85 deletions

View File

@ -1,46 +1,4 @@
[
{
"publishers": [
"PAN MACMILLAN"
],
"physical_format": "paperback",
"title": "Permanent Record",
"covers": [
10118259
],
"isbn_13": "9781529035667",
"full_title": "Permanent Record",
"isbn_10": "152903566X",
"publish_date": "Sep 14, 2019",
"authors": [
{
"id": "OL7618561A",
"name": "Edward Snowden"
}
],
"work": {
"id": "OL20080323W",
"title": "Permanent Record",
"subjects": [
"Snowden, edward j., 1983-",
"Government information",
"Whistle blowing",
"Leaks (Disclosure of information)",
"Officials and employees",
"United States",
"United States. National Security Agency",
"Biography",
"nyt:combined-print-and-e-book-nonfiction=2019-10-06",
"New York Times bestseller",
"New York Times reviewed",
"Electronic surveillance"
]
},
"ol_id": "OL28181327M",
"date_added": "2019-09-14",
"date_started": "2024-02-20",
"added_by_id": "9781529035667"
},
{
"publishers": [
"O'Reilly Media"
@ -175,4 +133,4 @@
"date_added": "2024-01-02",
"date_started": "2023-12-25"
}
]
]

View File

@ -1,5 +1,48 @@
[
{
{
"publishers": [
"PAN MACMILLAN"
],
"physical_format": "paperback",
"title": "Permanent Record",
"covers": [
10118259
],
"isbn_13": "9781529035667",
"full_title": "Permanent Record",
"isbn_10": "152903566X",
"publish_date": "Sep 14, 2019",
"authors": [
{
"id": "OL7618561A",
"name": "Edward Snowden"
}
],
"work": {
"id": "OL20080323W",
"title": "Permanent Record",
"subjects": [
"Snowden, edward j., 1983-",
"Government information",
"Whistle blowing",
"Leaks (Disclosure of information)",
"Officials and employees",
"United States",
"United States. National Security Agency",
"Biography",
"nyt:combined-print-and-e-book-nonfiction=2019-10-06",
"New York Times bestseller",
"New York Times reviewed",
"Electronic surveillance"
]
},
"ol_id": "OL28181327M",
"date_added": "2019-09-14",
"date_started": "2024-02-20",
"date_finished": "2024-03-11",
"added_by_id": "9781529035667"
},
{
"description": {
"type": "/type/text",
"value": "\"Most of us have been educated from birth to compete, judge, demand, and diagnose--to think and communicate in terms of what is 'right' and 'wrong' with people. At best, communicating and thinking this way can create misunderstanding and frustration. At its worst, it can lead to anger, depression, and even emotional or physical violence. [This book] uses stories, role-plays, and real-world examples to introduce the Nonviolent Communication (NVC) process. Far more than simple techniques, you'll learn to transform the thinking, language, and moralistic judgments that prevent the quality of relationships you've always wanted. Start to more easily resolve conflicts, get what you want without demands, hear the needs of others, strengthen your personal and professional relationships, and live your fullest potential. With Nonviolent Communication, you'll learn to: significantly improve your relationships with family, friends, and co-workers; stay peaceful in the face of judgment, criticism, and anger; speak, think, and listen in ways that inspire compassion and understanding; break patterns of thinking that lead to stress, depression, guilt, and shame; discover common ground with anyone, anytime, anywhere.\"--Book cover."

View File

@ -1,10 +1,34 @@
[
{
"ol_id": "OL23279299W",
"title": "Soldier box",
"authors": [
{
"ol_id": "OL8830255A",
"name": "Joe Glenton"
}
],
"subjects": [
"Afghan War, 2001-",
"Desertions",
"Military deserters",
"Biography",
"Conscientious objectors",
"Soldiers",
"Political activity",
"British Participation",
"Personal narratives",
"Afghan war, 2001-2021"
],
"date_added": "2024-03-06"
},
{
"ol_id": "OL25856221W",
"title": "Johnny Viable",
"subjects": [
"Art"
]
],
"date_added": "2024-02-25"
},
{
"ol_id": "OL27719403W",
@ -14,7 +38,8 @@
"Ecology",
"Forest ecology",
"Wald\u00f6kosystem"
]
],
"date_added": "2024-02-15"
},
{
"ol_id": "OL25815646W",
@ -30,7 +55,8 @@
"Sens et sensations",
"Physiologie",
"nyt:paperback-nonfiction=2023-09-17"
]
],
"date_added": "2024-02-15"
},
{
"ol_id": "OL21164374W",

View File

@ -1,4 +1,101 @@
[
{
"belongs_to_collection": null,
"genres": [
{
"id": 878,
"name": "Science Fiction"
},
{
"id": 10749,
"name": "Romance"
},
{
"id": 35,
"name": "Comedy"
}
],
"imdb_id": "tt14230458",
"overview": "Brought back to life by an unorthodox scientist, a young woman runs off with a debauched lawyer on a whirlwind adventure across the continents. Free from the prejudices of her times, she grows steadfast in her purpose to stand for equality and liberation.",
"poster_path": "/kCGlIMHnOm8JPXq3rXM6c5wMxcT.jpg",
"production_countries": [
{
"iso_3166_1": "IE",
"name": "Ireland"
},
{
"iso_3166_1": "GB",
"name": "United Kingdom"
},
{
"iso_3166_1": "US",
"name": "United States of America"
}
],
"release_date": "2023-12-07",
"spoken_languages": [
{
"english_name": "English",
"iso_639_1": "en",
"name": "English"
},
{
"english_name": "French",
"iso_639_1": "fr",
"name": "Fran\u00e7ais"
},
{
"english_name": "Portuguese",
"iso_639_1": "pt",
"name": "Portugu\u00eas"
}
],
"title": "Poor Things",
"tmdb_id": 792307,
"date_added": "2024-03-12",
"date_finished": "2024-03-09",
"added_by_id": "792307"
},
{
"belongs_to_collection": {
"id": 726871,
"name": "Dune Collection",
"poster_path": "/wcVafar6Efk3YgFvh8oZQ4yHL6H.jpg",
"backdrop_path": "/ygVSGv86R0BTOKJIb8RQ1sFxs4q.jpg"
},
"genres": [
{
"id": 878,
"name": "Science Fiction"
},
{
"id": 12,
"name": "Adventure"
}
],
"imdb_id": "tt15239678",
"overview": "Follow the mythic journey of Paul Atreides as he unites with Chani and the Fremen while on a path of revenge against the conspirators who destroyed his family. Facing a choice between the love of his life and the fate of the known universe, Paul endeavors to prevent a terrible future only he can foresee.",
"poster_path": "/8b8R8l88Qje9dn9OE8PY05Nxl1X.jpg",
"production_countries": [
{
"iso_3166_1": "US",
"name": "United States of America"
}
],
"release_date": "2024-02-27",
"spoken_languages": [
{
"english_name": "English",
"iso_639_1": "en",
"name": "English"
}
],
"title": "Dune: Part Two",
"tmdb_id": 693134,
"date_added": "2024-03-12",
"date_finished": "2024-03-09",
"added_by_id": "693134"
},
{
"belongs_to_collection": null,
"genres": [
@ -16158,4 +16255,4 @@
"date_finished": null,
"is_repeat": false
}
]
]

View File

@ -1,4 +1,35 @@
[
{
"belongs_to_collection": null,
"genres": [
{
"id": 99,
"name": "Documentary"
}
],
"imdb_id": "tt0318202",
"original_language": "fr",
"original_title": "\u00catre et avoir",
"overview": "The documentary's title translates as \"to be and to have\", the two auxiliary verbs in the French language. It is about a primary school in the commune of Saint-\u00c9tienne-sur-Usson, Puy-de-D\u00f4me, France, the population of which is just over 200. The school has one small class of mixed ages (from four to twelve years), with a dedicated teacher, Georges Lopez, who shows patience and respect for the children as we follow their story through a single school year.",
"poster_path": "/mqB5QPNCmbF8DgsZqmyGQAGktUE.jpg",
"production_countries": [
{
"iso_3166_1": "FR",
"name": "France"
}
],
"release_date": "2002-08-28",
"spoken_languages": [
{
"english_name": "French",
"iso_639_1": "fr",
"name": "Fran\u00e7ais"
}
],
"title": "To Be and to Have",
"tmdb_id": 5168,
"date_added": "2024-03-06"
},
{
"belongs_to_collection": null,
"genres": [

View File

@ -54,17 +54,28 @@ if "" == TMDB_API_KEY:
logger.error("TMDB API key not found")
def return_if_exists(item_id, media_type, log) -> dict | None:
def return_if_exists(item_id: str, media_type: str, log: str) -> dict | None:
"""Returns an item if it exists in the requested log"""
logger.info(f"Checking for '{item_id}' in '{log}'")
with open(f"./data/{media_type}/{log}.json", "r", encoding="utf-8") as log_file:
log_items = json.load(log_file)
id_key = "id"
if "books" == media_type:
if re.search("OL[0-9]+[MW]", item_id) is not None:
id_key = "ol_id"
elif re.search("[0-9]{13}", item_id) is not None:
id_key = "isbn_13"
elif re.search("[0-9]{10}", item_id) is not None:
id_key = "isbn_10"
else:
raise Exception("Invalid ID for book")
existing_items = [
log_item
for log_item in log_items
if "id" in log_item and log_item["id"] == item_id
if id_key in log_item and log_item[id_key] == item_id
]
if len(existing_items) > 0:
logger.info(f"Found item in '{log}'")
@ -72,18 +83,38 @@ def return_if_exists(item_id, media_type, log) -> dict | None:
logger.info(f"'{item_id}' not found in '{log}'")
def delete_existing(item_id, media_type, log) -> None:
def delete_existing(item_id: str, media_type: str, log: str) -> None:
"""Deletes an item from a log if it matches the ID"""
logger.info(f"Deleting '{item_id}' from '{log}'")
with open(f"./data/{media_type}/{log}.json", "r", encoding="utf-8") as log_file:
log_items = json.load(log_file)
id_key = "id"
if "books" == media_type:
if re.search("OL[0-9]+[MW]", item_id) is not None:
id_key = "ol_id"
elif re.search("[0-9]{13}", item_id) is not None:
id_key = "isbn_13"
elif re.search("[0-9]{10}", item_id) is not None:
id_key = "isbn_10"
else:
raise Exception("Invalid ID for book")
elif media_type in ["films", "tv-episodes"]:
if re.search("tt[0-9]+", item_id) is not None:
id_key = "isbn_id"
elif re.search("[0-9]+", item_id) is not None:
id_key = "tmdb_id"
else:
raise Exception("Invalid ID for film")
old_len = len(log_items)
log_items = [
log_item
for log_item in log_items
if "id" not in log_item or ("id" in log_item and log_item["id"] != item_id)
if id_key not in log_item
or (id_key in log_item and log_item[id_key] != item_id)
]
if len(log_items) < (old_len - 1):
raise Exception("More than one deletion made, discarding…")
@ -93,7 +124,9 @@ def delete_existing(item_id, media_type, log) -> None:
logger.info(f"'{item_id}' deleted from '{log}'")
def check_for_existing(item_id, media_type, log) -> dict[dict, str]:
def check_for_existing(
item_id, media_type, log
) -> tuple[dict[dict, str] | None, str | None]:
"""
Check for an existing item in the current log, and pull the
`date_added` etc. and mark it as a repeat if so.
@ -127,14 +160,14 @@ def check_for_existing(item_id, media_type, log) -> dict[dict, str]:
return None, None
def add_item_to_log(item_id, media_type, log) -> None:
def add_item_to_log(item_id: str, media_type: str, log: str) -> None:
"""Add a film, book, TV series or TV episode to a log"""
logger.info(f"Processing {item_id}")
item = None
item: dict | None = None
log_to_delete = None
if "tv-episodes" != media_type and ("books" != media_type and "wishlist" != log):
if media_type not in ["tv-episodes", "books"]:
item, log_to_delete = check_for_existing(item_id, media_type, log)
if item is None:
@ -142,10 +175,21 @@ def add_item_to_log(item_id, media_type, log) -> None:
if item is None:
raise Exception("No item found")
if "books" == media_type and "wishlist" != log:
item, log_to_delete = check_for_existing(item['work']['ol_id'], media_type, log)
if item is None:
item, log_to_delete = check_for_existing(item['ol_id'], media_type, log)
if "books" == media_type:
new_item, log_to_delete = check_for_existing(
item["work"]["ol_id"], media_type, log
)
if new_item is None:
new_item, log_to_delete = check_for_existing(item["ol_id"], media_type, log)
if new_item is None:
new_item, log_to_delete = check_for_existing(
item["isbn_13"], media_type, log
)
if new_item is None:
new_item, log_to_delete = check_for_existing(
item["isbn_10"], media_type, log
)
item = new_item if new_item is not None else item
if log in ["log", "current"]:
if "date_started" not in item and media_type in ["books", "tv-series", "games"]:
@ -202,7 +246,7 @@ def add_item_to_log(item_id, media_type, log) -> None:
delete_existing(item_id, media_type, log_to_delete)
def import_by_id(import_id, media_type, log) -> dict:
def import_by_id(import_id, media_type, log) -> dict | None:
"""Import from the appropriate API by unique ID"""
if media_type in ["films", "tv-series"]:
@ -230,8 +274,12 @@ def import_from_tmdb_by_external_id(external_id, media_type) -> dict:
response = requests.get(
api_url,
headers={"Authorization": f"Bearer {TMDB_API_KEY}"},
params={"external_source": "imdb_id" if re.search("tt[0-9]+", external_id) else "tvdb_id"},
timeout=15
params={
"external_source": (
"imdb_id" if re.search("tt[0-9]+", external_id) else "tvdb_id"
)
},
timeout=15,
)
# Process the response
@ -254,7 +302,7 @@ def import_from_tmdb_by_external_id(external_id, media_type) -> dict:
key = "movie_results"
response_data = json.loads(response.text)[key][0]
if response_data == None:
if response_data is None:
raise Exception(f"Nothing found for TVDB ID {external_id}!")
# Modify the returned result to add additional data
@ -289,7 +337,7 @@ def import_from_tmdb_by_id(tmdb_id, media_type) -> dict:
return cleanup_result(response_data, media_type)
def import_from_openlibrary_by_isbn(isbn, media_type) -> dict:
def import_from_openlibrary_by_isbn(isbn, media_type) -> dict | None:
"""Retrieve a film, TV show or TV episode from TMDB using an IMDB ID"""
logging.info(f"Importing '{isbn}'")
@ -337,18 +385,19 @@ def import_from_openlibrary_by_isbn(isbn, media_type) -> dict:
return cleanup_result(item, media_type)
def import_from_openlibrary_by_ol_key(key) -> dict:
def import_from_openlibrary_by_ol_key(key) -> dict | None:
"""Retrieves an item (author or work, NOT edition) from OpenLibrary using an OL key"""
if (len(key.split("/")) == 1):
if len(key.split("/")) == 1:
key = f"/works/{key}"
logger.info(f"Retrieving {key}")
_, mode, ol_id = key.split("/")
cached_authors = []
if "authors" == mode:
with open(
f"./scripts/caching/authors.json", "r", encoding="utf-8"
"./scripts/caching/authors.json", "r", encoding="utf-8"
) as authors_cache:
cached_authors = json.load(authors_cache)
@ -396,7 +445,7 @@ def import_from_openlibrary_by_ol_key(key) -> dict:
logger.info(f"Caching author '{author['name']}'")
cached_authors.append(author)
with open(
f"./scripts/caching/authors.json", "w", encoding="utf-8"
"./scripts/caching/authors.json", "w", encoding="utf-8"
) as authors_cache:
json.dump(cached_authors, authors_cache, indent=4)
logger.info(f"Author '{author['name']}' cached!")
@ -408,7 +457,9 @@ def import_from_openlibrary_by_ol_key(key) -> dict:
if "authors" in item:
for author in item["authors"]:
work["authors"].append(import_from_openlibrary_by_ol_key(author["author"]["key"]))
work["authors"].append(
import_from_openlibrary_by_ol_key(author["author"]["key"])
)
for result_key in ["first_publish_date", "subjects"]:
if result_key in item:
@ -429,7 +480,7 @@ def cleanup_result(item, media_type) -> dict:
for field_name in [
"adult", # TMDB
"backdrop_path", # TMDB
"budget", # TMDB
"budget", # TMDB
"copyright_date", # OpenLibrary
"classifications", # OpenLibrary
"created", # OpenLibrary
@ -437,7 +488,7 @@ def cleanup_result(item, media_type) -> dict:
"episode_type", # TMDB
"first_sentence", # OpenLibrary
"genre_ids", # TMDB
"homepage", # TMDB
"homepage", # TMDB
"identifiers", # OpenLibrary
"media_type", # TMDB
"last_modified", # OpenLibrary
@ -452,16 +503,16 @@ def cleanup_result(item, media_type) -> dict:
"physical_dimensions", # OpenLibrary
"popularity", # TMDB
"production_code", # TMDB
"production_companies", # TMDB
"publish_places", # OpenLibrary
"revenue", # TMDB
"production_companies", # TMDB
"publish_places", # OpenLibrary
"revenue", # TMDB
"revision", # OpenLibrary
"runtime", # TMDB
"source_records", # OpenLibrary
"status", # TMDB
"status", # TMDB
"still_path", # TMDB
"table_of_contents", # OpenLibrary
"tagline", # TMDB
"tagline", # TMDB
"type", # OpenLibrary
"uri_descriptions", # OpenLibrary
"url", # OpenLibrary
@ -487,8 +538,8 @@ def cleanup_result(item, media_type) -> dict:
del item[f"original_{title_key}"], item["original_language"]
if "tv-episodes" == media_type:
item['series'] = { 'tmdb_id': item['show_id'] }
del item['show_id']
item["series"] = {"tmdb_id": item["show_id"]}
del item["show_id"]
if "books" == media_type:
_, _, item["ol_id"] = item["key"].split("/")
@ -515,7 +566,7 @@ def cleanup_result(item, media_type) -> dict:
f"translation_of '{item['translation_of']}' \
is different to work title '{item['work']['title']}'"
)
if 'y' != input("Accept change? [y|n]: "):
if "y" != input("Accept change? [y|n]: "):
raise Exception(
f"translation_of '{item['translation_of']}' \
is different to work title '{item['work']['title']}'"
@ -546,8 +597,8 @@ def main() -> None:
try:
item_id = ""
log = ""
if "films" == media_type:
log = ""
while log not in ["log", "wishlist"]:
log = input("Enter log to update [log|wishlist]: ")
@ -555,7 +606,6 @@ def main() -> None:
item_id = input("Enter TMDB ID: ")
elif "books" == media_type:
log = ""
while log not in ["log", "current", "wishlist"]:
log = input("Enter log to update [log|current|wishlist]: ")
@ -566,19 +616,19 @@ def main() -> None:
item_id = "".join(re.findall(r"\d+", input("Enter ISBN: ")))
elif "tv-episodes" == media_type:
log = "log"
while re.search("(tt)?[0-9]+", item_id) is None:
item_id = input("Enter TVDB or IMDB ID: ")
elif "tv-series" == media_type:
log = ""
while log not in ["log", "current", "wishlist"]:
log = input("Enter log to update [log|current|wishlist]: ")
while re.search("[0-9]+", item_id) is None:
item_id = input("Enter TMDB ID: ")
add_item_to_log(re.search("(OL|tt)?[0-9]+[WMA]?", item_id)[0], media_type, log)
item_id_parsed = re.search("(OL|tt)?[0-9]+[WMA]?", item_id)
if item_id_parsed is not None:
add_item_to_log(item_id_parsed[0], media_type, log)
except Exception:
logger.exception("Exception occurred")