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:
commit
d269a9992e
4 changed files with 66 additions and 35 deletions
15
README.md
15
README.md
|
@ -1,16 +1,23 @@
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<img src="static/img/logo.png">
|
<img src="static/img/logo.png">
|
||||||
<h1>Indestructables</h1>
|
<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>.
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
<a href="https://matrix.to/#/#indestructables:fedora.im">snowcatridge10's Matrix Room</a>
|
<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>
|
</div>
|
||||||
|
|
||||||
# Instances
|
# Instances
|
||||||
|
|
||||||
| URL | Provided by | Country | Comments |
|
| URL | Provided by | Country | Comments |
|
||||||
|------------------------------------------------------------------------------------|-------------------------------------------|---------|----------|
|
| ---------------------------------------------------------------------------------- | ----------------------------------------- | ------- | -------- |
|
||||||
| [https://indestructables.private.coffee/](https://indestructables.private.coffee/) | [Private.coffee](https://private.coffee/) | Austria | |
|
| [https://indestructables.private.coffee/](https://indestructables.private.coffee/) | [Private.coffee](https://private.coffee/) | Austria | |
|
||||||
|
|
||||||
# Run your own instance
|
# 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
|
1. Clone the repository
|
||||||
2. Run `python3 main.py`
|
2. Run `python3 main.py`
|
||||||
3. Connect to http://localhost:8002
|
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
60
main.py
|
@ -8,8 +8,10 @@ from flask import (
|
||||||
Response,
|
Response,
|
||||||
stream_with_context,
|
stream_with_context,
|
||||||
)
|
)
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from urllib.parse import quote, unquote
|
from urllib.parse import quote, unquote
|
||||||
from traceback import print_exc
|
from traceback import print_exc
|
||||||
|
@ -18,6 +20,8 @@ from playwright.sync_api import sync_playwright
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
|
from werkzeug.exceptions import BadRequest, abort, InternalServerError, NotFound
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
global_ibles = {}
|
global_ibles = {}
|
||||||
|
@ -258,9 +262,7 @@ def member_header(header):
|
||||||
def category_page(path, name, teachers=False):
|
def category_page(path, name, teachers=False):
|
||||||
data = requests.get("https://www.instructables.com" + path)
|
data = requests.get("https://www.instructables.com" + path)
|
||||||
if data.status_code != 200:
|
if data.status_code != 200:
|
||||||
return Response(
|
abort(data.status_code)
|
||||||
render_template(str(data.status_code) + ".html"), status=data.status_code
|
|
||||||
)
|
|
||||||
|
|
||||||
soup = BeautifulSoup(data.text, "html.parser")
|
soup = BeautifulSoup(data.text, "html.parser")
|
||||||
|
|
||||||
|
@ -417,9 +419,7 @@ def project_list(path, head, sort=""):
|
||||||
def route_sitemap():
|
def route_sitemap():
|
||||||
data = requests.get(f"https://www.instructables.com/sitemap/")
|
data = requests.get(f"https://www.instructables.com/sitemap/")
|
||||||
if data.status_code != 200:
|
if data.status_code != 200:
|
||||||
return Response(
|
abort(data.status_code)
|
||||||
render_template(str(data.status_code) + ".html"), status=data.status_code
|
|
||||||
)
|
|
||||||
|
|
||||||
soup = BeautifulSoup(data.text, "html.parser")
|
soup = BeautifulSoup(data.text, "html.parser")
|
||||||
|
|
||||||
|
@ -446,9 +446,7 @@ def route_contest_archive():
|
||||||
page = request.args.get("page")
|
page = request.args.get("page")
|
||||||
data = requests.get(f"https://www.instructables.com/contest/archive/?page={page}")
|
data = requests.get(f"https://www.instructables.com/contest/archive/?page={page}")
|
||||||
if data.status_code != 200:
|
if data.status_code != 200:
|
||||||
return Response(
|
abort(data.status_code)
|
||||||
render_template(str(data.status_code) + ".html"), status=data.status_code
|
|
||||||
)
|
|
||||||
|
|
||||||
soup = BeautifulSoup(data.text, "html.parser")
|
soup = BeautifulSoup(data.text, "html.parser")
|
||||||
|
|
||||||
|
@ -485,9 +483,7 @@ def route_contest_archive():
|
||||||
def route_contest(contest):
|
def route_contest(contest):
|
||||||
data = requests.get(f"https://www.instructables.com/contest/{contest}/")
|
data = requests.get(f"https://www.instructables.com/contest/{contest}/")
|
||||||
if data.status_code != 200:
|
if data.status_code != 200:
|
||||||
return Response(
|
abort(data.status_code)
|
||||||
render_template(str(data.status_code) + ".html"), status=data.status_code
|
|
||||||
)
|
|
||||||
|
|
||||||
soup = BeautifulSoup(data.text, "html.parser")
|
soup = BeautifulSoup(data.text, "html.parser")
|
||||||
|
|
||||||
|
@ -541,9 +537,7 @@ def route_contest(contest):
|
||||||
def route_contests():
|
def route_contests():
|
||||||
data = requests.get("https://www.instructables.com/contest/")
|
data = requests.get("https://www.instructables.com/contest/")
|
||||||
if data.status_code != 200:
|
if data.status_code != 200:
|
||||||
return Response(
|
abort(data.status_code)
|
||||||
render_template(str(data.status_code) + ".html"), status=data.status_code
|
|
||||||
)
|
|
||||||
|
|
||||||
soup = BeautifulSoup(data.text, "html.parser")
|
soup = BeautifulSoup(data.text, "html.parser")
|
||||||
|
|
||||||
|
@ -668,9 +662,7 @@ def route_sitemap_circuits(category, subcategory):
|
||||||
def route_member_instructables(member):
|
def route_member_instructables(member):
|
||||||
data = requests.get(f"https://www.instructables.com/member/{member}/instructables")
|
data = requests.get(f"https://www.instructables.com/member/{member}/instructables")
|
||||||
if data.status_code != 200:
|
if data.status_code != 200:
|
||||||
return Response(
|
abort(data.status_code)
|
||||||
render_template(str(data.status_code) + ".html"), status=data.status_code
|
|
||||||
)
|
|
||||||
|
|
||||||
soup = BeautifulSoup(data.text, "html.parser")
|
soup = BeautifulSoup(data.text, "html.parser")
|
||||||
|
|
||||||
|
@ -709,9 +701,7 @@ def route_member(member):
|
||||||
f"https://www.instructables.com/member/{member}/", headers=headers
|
f"https://www.instructables.com/member/{member}/", headers=headers
|
||||||
)
|
)
|
||||||
if data.status_code != 200:
|
if data.status_code != 200:
|
||||||
return Response(
|
abort(data.status_code)
|
||||||
render_template(str(data.status_code) + ".html"), status=data.status_code
|
|
||||||
)
|
|
||||||
|
|
||||||
soup = BeautifulSoup(data.text, "html.parser")
|
soup = BeautifulSoup(data.text, "html.parser")
|
||||||
|
|
||||||
|
@ -765,9 +755,7 @@ def route_member(member):
|
||||||
def route_article(article):
|
def route_article(article):
|
||||||
data = requests.get(f"https://www.instructables.com/{article}/")
|
data = requests.get(f"https://www.instructables.com/{article}/")
|
||||||
if data.status_code != 200:
|
if data.status_code != 200:
|
||||||
return Response(
|
abort(data.status_code)
|
||||||
render_template(str(data.status_code) + ".html"), status=data.status_code
|
|
||||||
)
|
|
||||||
|
|
||||||
soup = BeautifulSoup(data.text, "html.parser")
|
soup = BeautifulSoup(data.text, "html.parser")
|
||||||
|
|
||||||
|
@ -948,7 +936,7 @@ def route_article(article):
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
print_exc()
|
print_exc()
|
||||||
return Response(render_template("404.html"), status=404)
|
raise InternalServerError()
|
||||||
|
|
||||||
|
|
||||||
@app.route("/<category>/<channel>/")
|
@app.route("/<category>/<channel>/")
|
||||||
|
@ -964,16 +952,14 @@ def route_channel_redirect(category, channel):
|
||||||
):
|
):
|
||||||
return redirect(f"/{category}/{channel}/projects/", 307)
|
return redirect(f"/{category}/{channel}/projects/", 307)
|
||||||
else:
|
else:
|
||||||
return Response(render_template("404.html"), status=404)
|
raise NotFound()
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
def route_explore():
|
def route_explore():
|
||||||
data = requests.get("https://www.instructables.com/")
|
data = requests.get("https://www.instructables.com/")
|
||||||
if data.status_code != 200:
|
if data.status_code != 200:
|
||||||
return Response(
|
abort(data.status_code)
|
||||||
render_template(str(data.status_code) + ".html"), status=data.status_code
|
|
||||||
)
|
|
||||||
|
|
||||||
soup = BeautifulSoup(data.text, "html.parser")
|
soup = BeautifulSoup(data.text, "html.parser")
|
||||||
|
|
||||||
|
@ -1011,9 +997,9 @@ def route_proxy():
|
||||||
data = requests.get(unquote(url))
|
data = requests.get(unquote(url))
|
||||||
return Response(data.content, content_type=data.headers["content-type"])
|
return Response(data.content, content_type=data.headers["content-type"])
|
||||||
else:
|
else:
|
||||||
return Response(render_template("400.html"), status=400)
|
raise BadRequest()
|
||||||
else:
|
else:
|
||||||
return Response(render_template("400.html"), status=400)
|
raise BadRequest()
|
||||||
|
|
||||||
@app.route("/privacypolicy/")
|
@app.route("/privacypolicy/")
|
||||||
def privacypolicy():
|
def privacypolicy():
|
||||||
|
@ -1023,5 +1009,17 @@ def privacypolicy():
|
||||||
def not_found(e):
|
def not_found(e):
|
||||||
return render_template("404.html")
|
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__':
|
if __name__ == '__main__':
|
||||||
app.run(port=args.port, host=args.listen_host, debug=debugmode)
|
app.run(port=args.port, host=args.listen_host, debug=debugmode)
|
||||||
|
|
21
templates/500.html
Normal file
21
templates/500.html
Normal 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>
|
|
@ -2,7 +2,8 @@
|
||||||
<hr>
|
<hr>
|
||||||
<center>
|
<center>
|
||||||
<p><a href="https://kumig.it/PrivateCoffee/indestructables">Modified Source Code (AGPLv3)</a></p>
|
<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>
|
<p><a href="/privacypolicy">View privacy policy.</a></p>
|
||||||
</center>
|
</center>
|
||||||
</footer>
|
</footer>
|
Loading…
Reference in a new issue