fix: Project lists once again display the list

Signed-off-by: Skylar "The Cobra" Widulski <cobra@vern.cc>
This commit is contained in:
Skylar "The Cobra" Widulski 2023-06-08 06:06:29 -04:00
parent 8428d81708
commit 30f142ce15
Signed by: cobra
GPG Key ID: 4FD8F812083FF6F9
3 changed files with 121 additions and 51 deletions

135
main.py
View File

@ -3,10 +3,14 @@
from flask import Flask, render_template, request, redirect, Response, stream_with_context
import requests
import re
import json
from math import ceil
from bs4 import BeautifulSoup
from urllib.parse import quote, unquote
from traceback import print_exc
typesense_key = "TUIxY0xkNjdHV09KaFV1dEVxYVRHNGs1QW1sbzlNVVZBaVZKV2VrODc0VT02ZWFYeyJleGNsdWRlX2ZpZWxkcyI6WyJvdXRfb2YiLCJzZWFyY2hfdGltZV9tcyIsInN0ZXBCb2R5Il0sInBlcl9wYWdlIjo2MH0="
def proxy(src):
return "/proxy/?url=" + quote(str(src))
@ -79,7 +83,67 @@ def member_header(header):
return [avatar, title, location, signup, instructables, views, comments, followers, bio]
def category_page(path, name, teachers=False):
def search(path, args, query='*'):
sort_by = "publishDate"
if args.get("sort"):
if args.get("sort").lower() == "views":
sort_by = "views"
elif args.get("sort").lower() == "favorites":
sort_by = "favorites"
elif args.get("sort").lower() == "i made its":
sort_by = "IMadeItCount"
type_ = "featureFlag%3A%3Dtrue"
if args.get("projects"):
if args.get("projects").lower() == "winners":
type_ = "contestFinalist%3A%3Dtrue"
elif args.get("projects").lower() == "all":
type_ = ''
split_path = path.split('/')
category = "category%3A%3D" + split_path[1] if len(split_path) > 1 and split_path[1] != '' and split_path[1] != 'projects' and not split_path[1].startswith("?x") else ''
if not type_ == '':
category = "+%26%26+" + category
channel = "channel%3A%3D" + split_path[2] if len(split_path) > 2 and split_path[2] != '' and split_path[2] != 'projects' and not split_path[2].startswith("?x") else ''
if not type_ == '' or not category == '':
channel = "+%26%26+" + channel
page = 1
if args.get("page"):
page = args.get("page")
search_url = f"https://www.instructables.com/api_proxy/search/collections/projects/documents/search?q={query}&query_by=title%2CstepBody%2CscreenName&page={page}&sort_by={sort_by}%3Adesc&include_fields=title%2CurlString%2CcoverImageUrl%2CscreenName%2Cfavorites%2Cviews%2CprimaryClassification%2CfeatureFlag%2CprizeLevel%2CIMadeItCount&filter_by={type_}{category}{channel}&per_page=60"
search_data = requests.get(search_url, headers={"X-Typesense-API-Key": typesense_key})
if search_data.status_code != 200:
return Response(render_template(str(search_data.status_code) + ".html"), status=search_data.status_code)
json_data = json.loads(search_data.text)
ibles = []
for document in json_data["hits"]:
ible = document["document"]
link = "/" + ible["urlString"] + "/"
img = proxy(ible["coverImageUrl"])
title = ible["title"]
author = ible["screenName"]
author_link = "/member/" + quote(author)
channel = ible["primaryClassification"].split("/")[-1].title()
channel_link = "/" + ible["primaryClassification"]
views = ible["views"]
favorites = ible["favorites"]
imadeits = ible["IMadeItCount"]
ibles.append([link, img, title, author_link, author, channel_link, channel, views, favorites, imadeits])
return (ibles, int(page), ceil(int(json_data["found"]) / 60))
def teachers_page(path, name):
data = requests.get("https://www.instructables.com" + path)
if data.status_code != 200:
return Response(render_template(str(data.status_code) + ".html"), status=data.status_code)
@ -89,7 +153,7 @@ def category_page(path, name, teachers=False):
channels = []
for card in soup.select("div.scrollable-cards-inner div.scrollable-card"):
link = card.a["href"]
img = proxy(card.select(f"a{' noscript' if teachers else ''} img")[0].get("src"))
img = proxy(card.select(f"a noscript img")[0].get("src"))
title = card.select("a img")[0].get("alt")
channels.append([link, title, img])
@ -124,37 +188,30 @@ def category_page(path, name, teachers=False):
contests.append([link, img, title])
return render_template("category.html", data=[name, channels, ibles, contests, path])
return render_template("teachers.html", data=[name, channels, ibles, contests, path])
def project_list(path, head, sort=''):
def project_list(path, head, args=None, realpath=None):
if not realpath:
realpath = path
data = requests.get("https://www.instructables.com" + path)
if data.status_code != 200:
return Response(render_template(str(data.status_code) + ".html"), status=data.status_code)
print(data.text)
head = f"{head + ' ' if head != '' else ''}Projects" + sort
path_ = path.rsplit('/', 1)[0]
head = f"{head + ' ' if head != '' else ''}Projects"
soup = BeautifulSoup(data.text, "html.parser")
searched = search(path, args)
ibles = []
for ible in soup.select("div[class^=cards__] div[class^=ibleCard__]"):
print(ible)
link = ible.select("div a")[0].get("href")
img = proxy(ible.select("div a img")[0].get("src"))
if type(searched) == Response:
return searched
info = ible.select("div div[class^=description__]")[0]
title = info.select("strong a[^=title__]")[0].text
author = info.select("a")[0].get("href")
author_link = info.select("a")[0].text
channel = info.select("a")[1].get("href")
channel_link = info.select("a")[1].text
ibles = searched[0]
page = searched[1]
pages = searched[2]
stats = ible.select("div[class^=footer__] div[class^=stats__]")[0]
views = stats.select("div")[1].text
favorites = stats.select("div")[0].text
return render_template("projects.html", data=[head, ibles, realpath, page, pages], sub=re.sub)
ibles.append([link, img, title, author, author_link, channel, channel_link, views, favorites])
return render_template("projects.html", data=[head, ibles, path_])
app = Flask(__name__, template_folder="templates", static_folder="static")
@ -299,55 +356,43 @@ def route_contests():
@app.route('/<category>/<channel>/projects/')
def route_channel_projects(category, channel):
return project_list(f"/{category}/{channel}/projects/", channel.title())
@app.route('/<category>/<channel>/projects/<sort>/')
def route_channel_projects_sort(category, channel, sort):
return project_list(f"/{category}/{channel}/projects/{sort}", channel.title(), " Sorted by " + sort.title())
return project_list(f"/{category}/{channel}/projects/?x", channel.title(), request.args, request.full_path)
@app.route('/<category>/projects/')
def route_category_projects(category):
return project_list(f"/{category}/projects/", category.title())
@app.route('/<category>/projects/<sort>/')
def route_category_projects_sort(category, sort):
return project_list(f"/{category}/projects/{sort}", category.title(), " Sorted by " + sort.title())
return project_list(f"/{category}/projects/?x", category.title(), request.args, request.full_path)
@app.route('/projects/')
def route_projects():
return project_list("/projects/", '')
@app.route('/projects/<sort>/')
def route_projects_sort(sort):
return project_list(f"/projects/{sort}", '', " Sorted by " + sort.title())
return project_list("/projects/?x", '', request.args, request.full_path)
@app.route('/circuits/')
def route_circuits():
return category_page("/circuits/", "Circuits")
return project_list("/circuits/?x", "Circuits", request.args, request.full_path)
@app.route('/workshop/')
def route_workshop():
return category_page("/workshop/", "Workshop")
return project_list("/workshop/?x", "Workshop", request.args, request.full_path)
@app.route('/craft/')
def route_craft():
return category_page("/craft/", "Craft")
return project_list("/craft/?x", "Craft", request.args, request.full_path)
@app.route('/cooking/')
def route_cooking():
return category_page("/cooking/", "Cooking")
return project_list("/cooking/?x", "Cooking", request.args, request.full_path)
@app.route('/living/')
def route_living():
return category_page("/living/", "Living")
return project_list("/living/?x", "Living", request.args, request.full_path)
@app.route('/outside/')
def route_outside():
return category_page("/outside/", "Outside")
return project_list("/outside/?x", "Outside", request.args, request.full_path)
@app.route('/teachers/')
def route_teachers():
return category_page("/teachers/", "Teachers", True)
return teachers_page("/teachers/?x", "Teachers")
@app.route('/member/<member>/instructables/')
def route_member_instructables(member):

View File

@ -11,11 +11,15 @@
{% include "header.html" %}
<center>
<h1>{{ data[0] }}</h1>
<span><a href="{{ data[2] }}/">Featured</a></span>
<span><a href="{{ data[2] }}/recent/">Recent</a></span>
<span><a href="{{ data[2] }}/popular/">Popular</a></span>
<span><a href="{{ data[2] }}/views/">Views</a></span>
<span><a href="{{ data[2] }}/winners/">Winners</a></span>
<span><a href="{{ sub("[&?]projects=[^&]*", '', data[2]) }}&projects=all">All</a></span>
<span><a href="{{ sub("[&?]projects=[^&]*", '', data[2]) }}&projects=featured">Featured</a></span>
<span><a href="{{ sub("[&?]projects=[^&]*", '', data[2]) }}&projects=winners">Winners</a></span>
<br>
<span>Sort by:</span>
<span><a href="{{ sub("[&?]sort=[^&]*", '', data[2]) }}&sort=Newest">Date</a></span>
<span><a href="{{ sub("[&?]sort=[^&]*", '', data[2]) }}&sort=Views">Views</a></span>
<span><a href="{{ sub("[&?]sort=[^&]*", '', data[2]) }}&sort=Favorites">Favorites</a></span>
<span><a href="{{ sub("[&?]sort=[^&]*", '', data[2]) }}&sort=I+Made+Its">I Made Its</a></span>
<br>
<div style="max-width:90%;">
@ -26,10 +30,31 @@
<p>{{ ible[2] }}</p>
</a>
<p>by <a href="{{ ible[3] }}">{{ ible[4] }}</a> in <a href="{{ ible[5] }}">{{ ible[6] }}</a></p>
<p>{{ ible[7] }} Views, {{ ible[8] }} Favorites</p>
<p>{{ ible[7] }} Views, {{ ible[8] }} Favorites, {{ ible[9] }} I Made Its</p>
</div>
{% endfor %}
</div>
<ul class="pagination">
{% if data[3] == 1 %}
<li class="arrow arrow-prev disabled"><a>Previous</a></li>
{% else %}
<li class="arrow arrow-prev"><a href="{{ data[2] }}&page={{ data[3] - 1 }}">Previous</a></li>
{% endif %}
{% for page in range(1, data[4] + 1) %}
{% if ((data[3] - page < 3) and (data[3] - page > -3)) or (page == 1 or page == data[4]) %}
{% if page != data[3] %}
<li><a href="{{ data[2] }}&page={{ page }}">{{ page }}</a></li>
{% else %}
<li class="active"><a>{{ page }}</a></li>
{% endif %}
{% endif %}
{% endfor %}
{% if data[3] == data[4] %}
<li class="arrow arrow-next disabled"><a>Next</a></li>
{% else %}
<li class="arrow arrow-next"><a href="{{ data[2] }}&page={{ data[3] + 1 }}">Next</a></li>
{% endif %}
</ul>
</center>
{% include "footer.html" %}
</body>