Merge branch '3-so-there-s-another-upstream' into 'main'

License/Upstream link fixes

Closes #3

See merge request PrivateCoffee/indestructables!1
This commit is contained in:
Kumi 2023-07-19 06:28:04 +00:00
commit d269a9992e
4 changed files with 66 additions and 35 deletions

View file

@ -1,16 +1,23 @@
<div align="center">
<img src="static/img/logo.png">
<h1>Indestructables</h1>
An open source alternative front-end to Instructables. This is a fork of <a href="https://codeberg.org/indestructables/indestructables">snowcatridge10's Indestructables</a> to use Playwright instead of Selenium.
An open source alternative front-end to Instructables. This is a fork of <a href="https://codeberg.org/indestructables/indestructables">snowcatridge10's Indestructables</a> to use Playwright instead of Selenium, which itself is a fork of <a href="https://git.vern.cc/cobra/Destructables">Cobra's Destructables</a>.
<a href="https://matrix.to/#/#indestructables:fedora.im">snowcatridge10's Matrix Room</a>
<ul>
<li>
<a href="https://matrix.to/#/#indestructables:fedora.im">snowcatridge10's Matrix Room</a>
</li>
<li>
<a href="https://mto.vern.cc/#/%23cobra-frontends:vern.cc">Cobra's Matrix Room</a>
</li>
</ul>
</div>
# Instances
| URL | Provided by | Country | Comments |
|------------------------------------------------------------------------------------|-------------------------------------------|---------|----------|
| ---------------------------------------------------------------------------------- | ----------------------------------------- | ------- | -------- |
| [https://indestructables.private.coffee/](https://indestructables.private.coffee/) | [Private.coffee](https://private.coffee/) | Austria | |
# Run your own instance
@ -36,3 +43,7 @@ Furthermore, you need to install the Chromium binary used by Playwright. You can
1. Clone the repository
2. Run `python3 main.py`
3. Connect to http://localhost:8002
## License
This project, as well as the two projects it is based on, are licensed under the GNU Affero General Public License v3. See the LICENSE file for more information.

60
main.py
View file

@ -8,8 +8,10 @@ from flask import (
Response,
stream_with_context,
)
import requests
import re
from bs4 import BeautifulSoup
from urllib.parse import quote, unquote
from traceback import print_exc
@ -18,6 +20,8 @@ from playwright.sync_api import sync_playwright
from urllib.parse import urljoin
from argparse import ArgumentParser
from werkzeug.exceptions import BadRequest, abort, InternalServerError, NotFound
import os
global_ibles = {}
@ -258,9 +262,7 @@ def member_header(header):
def category_page(path, name, teachers=False):
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
)
abort(data.status_code)
soup = BeautifulSoup(data.text, "html.parser")
@ -417,9 +419,7 @@ def project_list(path, head, sort=""):
def route_sitemap():
data = requests.get(f"https://www.instructables.com/sitemap/")
if data.status_code != 200:
return Response(
render_template(str(data.status_code) + ".html"), status=data.status_code
)
abort(data.status_code)
soup = BeautifulSoup(data.text, "html.parser")
@ -446,9 +446,7 @@ def route_contest_archive():
page = request.args.get("page")
data = requests.get(f"https://www.instructables.com/contest/archive/?page={page}")
if data.status_code != 200:
return Response(
render_template(str(data.status_code) + ".html"), status=data.status_code
)
abort(data.status_code)
soup = BeautifulSoup(data.text, "html.parser")
@ -485,9 +483,7 @@ def route_contest_archive():
def route_contest(contest):
data = requests.get(f"https://www.instructables.com/contest/{contest}/")
if data.status_code != 200:
return Response(
render_template(str(data.status_code) + ".html"), status=data.status_code
)
abort(data.status_code)
soup = BeautifulSoup(data.text, "html.parser")
@ -541,9 +537,7 @@ def route_contest(contest):
def route_contests():
data = requests.get("https://www.instructables.com/contest/")
if data.status_code != 200:
return Response(
render_template(str(data.status_code) + ".html"), status=data.status_code
)
abort(data.status_code)
soup = BeautifulSoup(data.text, "html.parser")
@ -668,9 +662,7 @@ def route_sitemap_circuits(category, subcategory):
def route_member_instructables(member):
data = requests.get(f"https://www.instructables.com/member/{member}/instructables")
if data.status_code != 200:
return Response(
render_template(str(data.status_code) + ".html"), status=data.status_code
)
abort(data.status_code)
soup = BeautifulSoup(data.text, "html.parser")
@ -709,9 +701,7 @@ def route_member(member):
f"https://www.instructables.com/member/{member}/", headers=headers
)
if data.status_code != 200:
return Response(
render_template(str(data.status_code) + ".html"), status=data.status_code
)
abort(data.status_code)
soup = BeautifulSoup(data.text, "html.parser")
@ -765,9 +755,7 @@ def route_member(member):
def route_article(article):
data = requests.get(f"https://www.instructables.com/{article}/")
if data.status_code != 200:
return Response(
render_template(str(data.status_code) + ".html"), status=data.status_code
)
abort(data.status_code)
soup = BeautifulSoup(data.text, "html.parser")
@ -948,7 +936,7 @@ def route_article(article):
except Exception:
print_exc()
return Response(render_template("404.html"), status=404)
raise InternalServerError()
@app.route("/<category>/<channel>/")
@ -964,16 +952,14 @@ def route_channel_redirect(category, channel):
):
return redirect(f"/{category}/{channel}/projects/", 307)
else:
return Response(render_template("404.html"), status=404)
raise NotFound()
@app.route("/")
def route_explore():
data = requests.get("https://www.instructables.com/")
if data.status_code != 200:
return Response(
render_template(str(data.status_code) + ".html"), status=data.status_code
)
abort(data.status_code)
soup = BeautifulSoup(data.text, "html.parser")
@ -1011,9 +997,9 @@ def route_proxy():
data = requests.get(unquote(url))
return Response(data.content, content_type=data.headers["content-type"])
else:
return Response(render_template("400.html"), status=400)
raise BadRequest()
else:
return Response(render_template("400.html"), status=400)
raise BadRequest()
@app.route("/privacypolicy/")
def privacypolicy():
@ -1023,5 +1009,17 @@ def privacypolicy():
def not_found(e):
return render_template("404.html")
@app.errorhandler(400)
def bad_request(e):
return render_template("400.html")
@app.errorhandler(429)
def too_many_requests(e):
return render_template("429.html")
@app.errorhandler(500)
def internal_server_error(e):
return render_template("500.html")
if __name__ == '__main__':
app.run(port=args.port, host=args.listen_host, debug=debugmode)

21
templates/500.html Normal file
View file

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<title>500 - Indestructables</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
{% include "style.html" %}
<link rel="icon" type="image/png" href="{{ url_for('static', filename='img/favicon.png') }}">
</head>
<body>
{% include "header.html" %}
<center>
<h1 style="font-size:10em;line-height:0em;">500</h1>
<p>An internal server error has occurred. Please try again later, or notify the administrator if this keeps happening.</p>
</center>
{% include "footer.html" %}
</body>
</html>

View file

@ -2,7 +2,8 @@
<hr>
<center>
<p><a href="https://kumig.it/PrivateCoffee/indestructables">Modified Source Code (AGPLv3)</a></p>
<p><a href="https://codeberg.org/indestructables/indestructables">Original Source Code (AGPLv3)</a></p>
<p><a href="https://codeberg.org/indestructables/indestructables">Original Indestructables Source Code (AGPLv3)</a></p>
<p><a href="https://git.vern.cc/cobra/Destructables">Original Destructables Source Code (AGPLv3)</a></p>
<p><a href="/privacypolicy">View privacy policy.</a></p>
</center>
</footer>