structables 0.4.3__tar.gz → 0.4.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. {structables-0.4.3 → structables-0.4.4}/.forgejo/workflows/docker.yml +7 -1
  2. {structables-0.4.3 → structables-0.4.4}/PKG-INFO +1 -1
  3. {structables-0.4.3 → structables-0.4.4}/pyproject.toml +1 -1
  4. {structables-0.4.3 → structables-0.4.4}/src/structables/routes/main.py +95 -46
  5. {structables-0.4.3 → structables-0.4.4}/.dockerignore +0 -0
  6. {structables-0.4.3 → structables-0.4.4}/.env.example +0 -0
  7. {structables-0.4.3 → structables-0.4.4}/.forgejo/workflows/release.yml +0 -0
  8. {structables-0.4.3 → structables-0.4.4}/.gitignore +0 -0
  9. {structables-0.4.3 → structables-0.4.4}/Dockerfile +0 -0
  10. {structables-0.4.3 → structables-0.4.4}/LICENSE +0 -0
  11. {structables-0.4.3 → structables-0.4.4}/README.md +0 -0
  12. {structables-0.4.3 → structables-0.4.4}/cronjob.txt +0 -0
  13. {structables-0.4.3 → structables-0.4.4}/docker-compose-example-dev.yml +0 -0
  14. {structables-0.4.3 → structables-0.4.4}/docker-compose-example.yml +0 -0
  15. {structables-0.4.3 → structables-0.4.4}/entrypoint.sh +0 -0
  16. {structables-0.4.3 → structables-0.4.4}/instances.json +0 -0
  17. {structables-0.4.3 → structables-0.4.4}/renovate.json +0 -0
  18. {structables-0.4.3 → structables-0.4.4}/requirements-dev.txt +0 -0
  19. {structables-0.4.3 → structables-0.4.4}/src/structables/__init__.py +0 -0
  20. {structables-0.4.3 → structables-0.4.4}/src/structables/config.py +0 -0
  21. {structables-0.4.3 → structables-0.4.4}/src/structables/main.py +0 -0
  22. {structables-0.4.3 → structables-0.4.4}/src/structables/routes/__init__.py +0 -0
  23. {structables-0.4.3 → structables-0.4.4}/src/structables/routes/category.py +0 -0
  24. {structables-0.4.3 → structables-0.4.4}/src/structables/routes/contest.py +0 -0
  25. {structables-0.4.3 → structables-0.4.4}/src/structables/routes/member.py +0 -0
  26. {structables-0.4.3 → structables-0.4.4}/src/structables/routes/proxy.py +0 -0
  27. {structables-0.4.3 → structables-0.4.4}/src/structables/static/css/iframe.css +0 -0
  28. {structables-0.4.3 → structables-0.4.4}/src/structables/static/css/style.css +0 -0
  29. {structables-0.4.3 → structables-0.4.4}/src/structables/static/img/logo.png +0 -0
  30. {structables-0.4.3 → structables-0.4.4}/src/structables/static/img/logo_lg.png +0 -0
  31. {structables-0.4.3 → structables-0.4.4}/src/structables/static/img/magnifying-glass-solid.svg +0 -0
  32. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/400.html +0 -0
  33. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/404.html +0 -0
  34. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/429.html +0 -0
  35. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/500.html +0 -0
  36. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/archives.html +0 -0
  37. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/article-review.html +0 -0
  38. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/article.html +0 -0
  39. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/base.html +0 -0
  40. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/category.html +0 -0
  41. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/collection.html +0 -0
  42. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/contest.html +0 -0
  43. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/contests.html +0 -0
  44. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/footer.html +0 -0
  45. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/header.html +0 -0
  46. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/iframe.html +0 -0
  47. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/index.html +0 -0
  48. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/member-instructables.html +0 -0
  49. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/member.html +0 -0
  50. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/privacypolicy.html +0 -0
  51. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/projects.html +0 -0
  52. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/sitemap.html +0 -0
  53. {structables-0.4.3 → structables-0.4.4}/src/structables/templates/style.html +0 -0
  54. {structables-0.4.3 → structables-0.4.4}/src/structables/utils/data.py +0 -0
  55. {structables-0.4.3 → structables-0.4.4}/src/structables/utils/helpers.py +0 -0
@@ -11,8 +11,14 @@ jobs:
11
11
  name: Docker Build and Push to Docker Hub
12
12
  runs-on: docker-privileged
13
13
  container:
14
- image: catthehacker/ubuntu:act-latest
14
+ image: node:22-bookworm
15
+ options: >-
16
+ --privileged
15
17
  steps:
18
+ - name: Install dependencies
19
+ run: |
20
+ apt update
21
+ apt install -y docker.io
16
22
  - name: Set up QEMU
17
23
  uses: docker/setup-qemu-action@v3
18
24
  - name: Set up Docker Buildx
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: structables
3
- Version: 0.4.3
3
+ Version: 0.4.4
4
4
  Summary: A simple frontend for Instructables
5
5
  Project-URL: Homepage, https://git.private.coffee/privatecoffee/structables
6
6
  Project-URL: Bug Tracker, https://git.private.coffee/privatecoffee/structables/issues
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "structables"
7
- version = "0.4.3"
7
+ version = "0.4.4"
8
8
  authors = [
9
9
  { name="Private.coffee Team", email="support@private.coffee" },
10
10
  ]
@@ -23,60 +23,109 @@ def init_main_routes(app):
23
23
  @app.route("/")
24
24
  def route_explore():
25
25
  logger.debug("Rendering explore page")
26
-
27
- # Check if data needs to be updated
28
26
  maybe_update_data(app)
29
27
 
30
28
  try:
31
- logger.debug("Fetching data from instructables.com")
29
+ # Extract API endpoints from the JSON context data
30
+ logger.debug("Fetching data from instructables.com API")
32
31
  data = urlopen("https://www.instructables.com/")
33
- except HTTPError as e:
34
- logger.error(f"HTTP error fetching explore page: {e.code}")
35
- abort(e.code)
32
+ soup = BeautifulSoup(data.read().decode(), "html.parser")
33
+
34
+ # Try to extract JSON data from script tag
35
+ page_context = None
36
+ script_tag = soup.select("#js-page-context")
37
+ if script_tag:
38
+ try:
39
+ page_context = json.loads(script_tag[0].string)
40
+ logger.debug("Successfully extracted page context JSON")
41
+ except Exception as e:
42
+ logger.error(f"Error parsing page context JSON: {str(e)}")
43
+
44
+ # Use the API endpoints to fetch category data
45
+ categories = [
46
+ "circuits",
47
+ "workshop",
48
+ "design",
49
+ "craft",
50
+ "cooking",
51
+ "living",
52
+ "outside",
53
+ "teachers",
54
+ ]
55
+ sections = []
56
+
57
+ for category in categories:
58
+ try:
59
+ # Use the appropriate API endpoint for each category
60
+ api_url = f"https://www.instructables.com/json-api/getHomepageFeatured?limit=5&category={category}"
61
+ logger.debug(
62
+ f"Fetching data for category {category} from {api_url}"
63
+ )
36
64
 
37
- soup = BeautifulSoup(data.read().decode(), "html.parser")
65
+ category_data = urlopen(api_url)
66
+ category_json = json.loads(category_data.read().decode())
67
+
68
+ # Process category items
69
+ category_items = []
70
+ for item in category_json["items"]:
71
+ category_items.append(
72
+ {
73
+ "title": item["title"],
74
+ "link": item["url"],
75
+ "img": (
76
+ proxy(item["downloadUrl"])
77
+ if "downloadUrl" in item
78
+ else ""
79
+ ),
80
+ "author": item["author"] if "author" in item else "",
81
+ "channel": (
82
+ item["classifications"][0]["channels"][0]["title"]
83
+ if "classifications" in item
84
+ and item["classifications"]
85
+ else ""
86
+ ),
87
+ "channel_link": (
88
+ f"/{item['classifications'][0]['name']}/{item['classifications'][0]['channels'][0]['name']}/"
89
+ if "classifications" in item
90
+ and item["classifications"]
91
+ else ""
92
+ ),
93
+ }
94
+ )
95
+
96
+ sections.append(
97
+ (category.capitalize(), f"/{category}", category_items)
98
+ )
99
+ logger.debug(
100
+ f"Processed {len(category_items)} items for category {category}"
101
+ )
38
102
 
39
- explore = soup.select(".home-content-explore-wrap")[0]
103
+ except Exception as e:
104
+ logger.error(f"Error processing category {category}: {str(e)}")
105
+ raise
106
+ sections.append((category.capitalize(), f"/{category}", []))
40
107
 
41
- title = explore.select("h2")[0].text
42
- logger.debug(f"Explore page title: {title}")
108
+ return render_template("index.html", title="Explore", sections=sections)
43
109
 
44
- logger.debug("Parsing category sections")
45
- circuits = explore_lists(
46
- explore.select(".home-content-explore-category-circuits")[0]
47
- )
48
- workshop = explore_lists(
49
- explore.select(".home-content-explore-category-workshop")[0]
50
- )
51
- craft = explore_lists(explore.select(".home-content-explore-category-craft")[0])
52
- cooking = explore_lists(
53
- explore.select(".home-content-explore-category-cooking")[0]
54
- )
55
- living = explore_lists(
56
- explore.select(".home-content-explore-category-living")[0]
57
- )
58
- outside = explore_lists(
59
- explore.select(".home-content-explore-category-outside")[0]
60
- )
61
- teachers = explore_lists(
62
- explore.select(".home-content-explore-category-teachers")[0]
63
- )
64
-
65
- logger.debug("Rendering explore page template")
110
+ except Exception as e:
111
+ logger.error(f"Error rendering explore page: {str(e)}")
112
+ print_exc()
66
113
 
67
- return render_template(
68
- "index.html",
69
- title=title,
70
- sections=[
71
- ("Circuits", "/circuits", circuits),
72
- ("Workshop", "/workshop", workshop),
73
- ("Craft", "/craft", craft),
74
- ("Cooking", "/cooking", cooking),
75
- ("Living", "/living", living),
76
- ("Outside", "/outside", outside),
77
- ("Teachers", "/teachers", teachers),
78
- ],
79
- )
114
+ # Fallback to a static version if API fails
115
+ return render_template(
116
+ "index.html",
117
+ title="Explore",
118
+ sections=[
119
+ ("Circuits", "/circuits", []),
120
+ ("Workshop", "/workshop", []),
121
+ ("Craft", "/craft", []),
122
+ ("Cooking", "/cooking", []),
123
+ ("Living", "/living", []),
124
+ ("Outside", "/outside", []),
125
+ ("Teachers", "/teachers", []),
126
+ ],
127
+ message="Instructables.com recently redesigned their site. We're experiencing issues with our scraper.",
128
+ )
80
129
 
81
130
  @app.route("/sitemap/")
82
131
  @app.route("/sitemap/<path:path>")
@@ -341,7 +390,7 @@ def init_main_routes(app):
341
390
  enumerate=enumerate,
342
391
  )
343
392
  else:
344
- ## Collections
393
+ # Collections
345
394
  logger.debug("Article is a collection")
346
395
  thumbnails = []
347
396
  for thumbnail in data["instructables"]:
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes