initial commit
This commit is contained in:
BIN
database/wcx.db
Normal file
BIN
database/wcx.db
Normal file
Binary file not shown.
1009
import/wcx_site_index.json
Normal file
1009
import/wcx_site_index.json
Normal file
File diff suppressed because it is too large
Load Diff
191
scripts/import_site.py
Executable file
191
scripts/import_site.py
Executable file
@ -0,0 +1,191 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import json
|
||||||
|
import sqlite3
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
JSON_FILE = Path("/storage/disk1/WCX/import/wcx_site_index.json")
|
||||||
|
DATABASE_FILE = Path("/storage/disk1/WCX/database/wcx.db")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_duration(value: str | None) -> int | None:
|
||||||
|
"""Convert mm:ss or hh:mm:ss to seconds."""
|
||||||
|
if not value:
|
||||||
|
return None
|
||||||
|
|
||||||
|
parts = value.split(":")
|
||||||
|
|
||||||
|
try:
|
||||||
|
numbers = [int(part) for part in parts]
|
||||||
|
except ValueError as error:
|
||||||
|
raise ValueError(f"Ogiltig speltid: {value!r}") from error
|
||||||
|
|
||||||
|
if len(numbers) == 2:
|
||||||
|
minutes, seconds = numbers
|
||||||
|
hours = 0
|
||||||
|
elif len(numbers) == 3:
|
||||||
|
hours, minutes, seconds = numbers
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Ogiltigt duration-format: {value!r}")
|
||||||
|
|
||||||
|
if hours < 0 or minutes < 0 or seconds < 0:
|
||||||
|
raise ValueError(f"Speltiden får inte vara negativ: {value!r}")
|
||||||
|
|
||||||
|
if seconds >= 60:
|
||||||
|
raise ValueError(f"Ogiltigt sekundvärde i speltid: {value!r}")
|
||||||
|
|
||||||
|
if len(numbers) == 3 and minutes >= 60:
|
||||||
|
raise ValueError(f"Ogiltigt minutvärde i speltid: {value!r}")
|
||||||
|
|
||||||
|
return hours * 3600 + minutes * 60 + seconds
|
||||||
|
|
||||||
|
|
||||||
|
def get_site_updated(movie: dict[str, Any]) -> str | None:
|
||||||
|
"""
|
||||||
|
Read the site's update date.
|
||||||
|
|
||||||
|
Accept both 'update' and 'updated', although the site's normal field
|
||||||
|
is expected to be named 'update'.
|
||||||
|
"""
|
||||||
|
return movie.get("update") or movie.get("updated")
|
||||||
|
|
||||||
|
|
||||||
|
def validate_movie(movie: Any, index: int) -> dict[str, Any]:
|
||||||
|
if not isinstance(movie, dict):
|
||||||
|
raise ValueError(f"Post {index} är inte ett JSON-objekt.")
|
||||||
|
|
||||||
|
for required_field in ("id", "titel"):
|
||||||
|
value = movie.get(required_field)
|
||||||
|
|
||||||
|
if not isinstance(value, str) or not value.strip():
|
||||||
|
raise ValueError(
|
||||||
|
f"Post {index} saknar ett giltigt fält: {required_field}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return movie
|
||||||
|
|
||||||
|
|
||||||
|
def insert_movie(
|
||||||
|
connection: sqlite3.Connection,
|
||||||
|
movie: dict[str, Any],
|
||||||
|
site_updated: str | None,
|
||||||
|
) -> None:
|
||||||
|
connection.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO movie (
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
duration_seconds,
|
||||||
|
web_url,
|
||||||
|
thumbnail,
|
||||||
|
published,
|
||||||
|
updated
|
||||||
|
)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
movie["id"],
|
||||||
|
movie["titel"],
|
||||||
|
parse_duration(movie.get("duration")),
|
||||||
|
movie.get("details"),
|
||||||
|
movie.get("thumb"),
|
||||||
|
movie.get("published"),
|
||||||
|
site_updated,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def update_movie(
|
||||||
|
connection: sqlite3.Connection,
|
||||||
|
movie: dict[str, Any],
|
||||||
|
site_updated: str,
|
||||||
|
) -> None:
|
||||||
|
connection.execute(
|
||||||
|
"""
|
||||||
|
UPDATE movie
|
||||||
|
SET
|
||||||
|
name = ?,
|
||||||
|
duration_seconds = ?,
|
||||||
|
web_url = ?,
|
||||||
|
thumbnail = ?,
|
||||||
|
published = ?,
|
||||||
|
updated = ?,
|
||||||
|
modified_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE id = ?
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
movie["titel"],
|
||||||
|
parse_duration(movie.get("duration")),
|
||||||
|
movie.get("details"),
|
||||||
|
movie.get("thumb"),
|
||||||
|
movie.get("published"),
|
||||||
|
site_updated,
|
||||||
|
movie["id"],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
if not JSON_FILE.is_file():
|
||||||
|
raise FileNotFoundError(f"JSON-filen finns inte: {JSON_FILE}")
|
||||||
|
|
||||||
|
if not DATABASE_FILE.is_file():
|
||||||
|
raise FileNotFoundError(f"Databasen finns inte: {DATABASE_FILE}")
|
||||||
|
|
||||||
|
with JSON_FILE.open("r", encoding="utf-8") as file:
|
||||||
|
movies = json.load(file)
|
||||||
|
|
||||||
|
if not isinstance(movies, list):
|
||||||
|
raise ValueError("JSON-filen måste innehålla en array.")
|
||||||
|
|
||||||
|
inserted = 0
|
||||||
|
updated = 0
|
||||||
|
unchanged = 0
|
||||||
|
|
||||||
|
with sqlite3.connect(DATABASE_FILE) as connection:
|
||||||
|
for index, raw_movie in enumerate(movies, start=1):
|
||||||
|
movie = validate_movie(raw_movie, index)
|
||||||
|
movie_id = movie["id"]
|
||||||
|
site_updated = get_site_updated(movie)
|
||||||
|
|
||||||
|
existing = connection.execute(
|
||||||
|
"""
|
||||||
|
SELECT updated
|
||||||
|
FROM movie
|
||||||
|
WHERE id = ?
|
||||||
|
""",
|
||||||
|
(movie_id,),
|
||||||
|
).fetchone()
|
||||||
|
|
||||||
|
if existing is None:
|
||||||
|
insert_movie(connection, movie, site_updated)
|
||||||
|
inserted += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
database_updated = existing[0]
|
||||||
|
|
||||||
|
should_update = (
|
||||||
|
site_updated is not None
|
||||||
|
and (
|
||||||
|
database_updated is None
|
||||||
|
or site_updated > database_updated
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if should_update:
|
||||||
|
update_movie(connection, movie, site_updated)
|
||||||
|
updated += 1
|
||||||
|
else:
|
||||||
|
unchanged += 1
|
||||||
|
|
||||||
|
print(f"Poster i JSON: {len(movies)}")
|
||||||
|
print(f"Tillagda: {inserted}")
|
||||||
|
print(f"Uppdaterade: {updated}")
|
||||||
|
print(f"Oförändrade: {unchanged}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
||||||
23
scripts/schema.sql
Normal file
23
scripts/schema.sql
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS movie (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
aka TEXT,
|
||||||
|
nationality TEXT,
|
||||||
|
age INTEGER,
|
||||||
|
shoot_location TEXT,
|
||||||
|
shoot_date TEXT,
|
||||||
|
duration_seconds INTEGER,
|
||||||
|
description TEXT,
|
||||||
|
rating REAL,
|
||||||
|
web_url TEXT,
|
||||||
|
thumbnail TEXT,
|
||||||
|
published TEXT,
|
||||||
|
updated TEXT,
|
||||||
|
|
||||||
|
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
modified_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CHECK (age IS NULL OR age >= 0),
|
||||||
|
CHECK (rating IS NULL OR rating BETWEEN 0 AND 10),
|
||||||
|
CHECK (duration_seconds IS NULL OR duration_seconds >= 0)
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user