initial commit

This commit is contained in:
Kevin F 2021-10-03 00:56:33 +00:00
commit f895fe5348
15 changed files with 250 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
venv/*
helloworld/__pycache__/*
testdata/*
.vscode/*

7
CHANGELOG.md Normal file
View File

@ -0,0 +1,7 @@
# Changelog
This project uses Semantic Versioning
## 0.0.0
Initial release

2
Makefile Normal file
View File

@ -0,0 +1,2 @@
test:
./run_tests.sh

66
imgmine/__init__.py Normal file
View File

@ -0,0 +1,66 @@
from gevent import monkey
from gevent import sleep
monkey.patch_all()
from threading import Thread
from os import remove, mkdir, path
from shutil import rmtree
from glob import glob
from uuid import uuid4
from bottle import route, run
from bottle import static_file
from bottle import SimpleTemplate
from .get import get
from .config import IMAGE_CACHE, SINGLE_IMAGE_DELETE_AFTER_SECS, ALBUM_DELETE_AFTER_SECS, template_dir
def album(id):
req_id = str(uuid4())
req = IMAGE_CACHE
get("/a/" + id, req)
imgs = glob(req + "*")
for c, img in enumerate(imgs):
imgs[c] = img.replace(IMAGE_CACHE, '/')
with open(f'{template_dir}gallery.html', 'r') as img_view:
tpl = SimpleTemplate(img_view)
return tpl.render(imgs=imgs)
@route('/')
@route('')
def home():
return static_file("index.html", root=template_dir)
@route('/static/<file>')
def static(file=''):
return static_file(file, root=template_dir)
@route('/gallery/<id>')
def gallery(id=''):
return album(id)
@route('/a/<id>')
def gallery(id=''):
return album(id)
@route('/<img>')
def hello(img=''):
if not path.exists(IMAGE_CACHE + img):
get(img, IMAGE_CACHE)
return static_file(img, root=IMAGE_CACHE)
def start_server():
try:
rmtree(IMAGE_CACHE)
except FileNotFoundError:
pass
mkdir(IMAGE_CACHE)
run(server='gevent')

Binary file not shown.

Binary file not shown.

Binary file not shown.

4
imgmine/config.py Normal file
View File

@ -0,0 +1,4 @@
IMAGE_CACHE = '/tmp/imgmine-imgur-images/'
SINGLE_IMAGE_DELETE_AFTER_SECS = 60
ALBUM_DELETE_AFTER_SECS = 120
template_dir = 'imgmine/web/'

64
imgmine/get.py Normal file
View File

@ -0,0 +1,64 @@
import sys
from shutil import rmtree
from os import remove
from threading import Thread
import requests
import bs4
from gevent import sleep
from .config import SINGLE_IMAGE_DELETE_AFTER_SECS, ALBUM_DELETE_AFTER_SECS
def delete_file(path):
sleep(SINGLE_IMAGE_DELETE_AFTER_SECS)
print('Erasing', path)
try:
remove(path)
except FileNotFoundError:
pass
def error(msg):
sys.stderr.write(msg + "\n")
sys.stderr.flush()
def get(url: str, write_dir: str, delete=True):
if not url.startswith('https://imgur.com/'):
url = 'https://imgur.com/' + url
found_url = ''
print('it not album', url)
album = False
if "gallery" in url:
url = url.replace("gallery", "a")
print('it album')
if "/a/" in url:
print('it album')
album = True
if not url.endswith("blog"):
url += "/layout/blog"
if not album:
print('getting img', url)
url = 'https://i.imgur.com/' + url.rsplit('/', 1)[-1].replace('jpeg', '').replace('jpg', '')
with open(f'{write_dir}/{url[-12:]}', 'wb') as img:
img.write(requests.get(url).content)
if delete:
Thread(target=delete_file, args=[f"{write_dir}/{url[-12:]}"]).start()
else:
print('Detecting album/gallery images', url)
soup = bs4.BeautifulSoup(requests.get(url).text, 'html.parser')
for count, el in enumerate(soup.select('.post-image meta[itemprop="contentUrl"]'), start=1):
try:
found_url = "https:" + el['content']
except KeyError:
error("Could not obtain url for detected image")
continue
print(f"Downloading image {count}: {found_url}")
print("Writing image", f"{write_dir}{found_url[-11:]}")
with open(f"{write_dir}{found_url[-11:]}", "wb") as f:
f.write(requests.get(found_url).content)
if delete:
Thread(target=delete_file, args=[f"{write_dir}{found_url[-11:]}"]).start()

25
imgmine/web/gallery.html Normal file
View File

@ -0,0 +1,25 @@
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charself="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>imgmine - minimal & private imgur proxy</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>📷</text></svg>">
<link rel="stylesheet" href="/static/theme.css">
</head>
<body>
% for img in imgs:
<img src="{{img}}" loading="lazy">
<br>
% end
<footer>
<small>
This website does not claim ownership of any media.
<br>This service simply acts as a proxy to Imgur.com and does not store images aside from a temporary cache.
<br>Abusive images should be reported to imgur. This website does not create new images/comments.
</small>
</footer>
</body>
</html>

41
imgmine/web/index.html Normal file
View File

@ -0,0 +1,41 @@
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charself="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>imgmine - minimal & private imgur proxy</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>📷</text></svg>">
<link rel="stylesheet" href="/static/theme.css">
</head>
<body>
<h1>
imgmine
</h1>
<hr>
<p>
A minimalist read-only Imgur proxy insipired by software like <a href="https://invidio.us/">Invidious</a>, <a href="https://nitter.net/">Nitter</a>, and <a href="https://bibliogram.art/">Bibliogram</a>.
</p>
<p>
This project was started because <a href="https://chaoswebs.net/">Kevin Froman</a> got fed up with Imgur breaking with Tor because of cow-dung JS.
</p>
<h2>Feature roadmap</h2>
<p>Features are in order of priority of most to least</p>
<ul>
<li><input type="checkbox" checked> Image proxying</li>
<li><input type="checkbox" checked> Gallery/album proxying</li>
<li><input type="checkbox"> Proper order of gallery posts</li>
<li><input type="checkbox"> Image description and author info</li>
<li><input type="checkbox"> GIFs/videos</li>
<li><input type="checkbox"> Comments... maybe</li>
</ul>
<footer>
<small>
This website does not claim ownership of any media.
<br>This service simply acts as a proxy to Imgur.com and does not store images aside from a temporary cache.
<br>Abusive images should be reported to imgur. This website does not create new images/comments.
</small>
</footer>
</body>
</html>

14
imgmine/web/theme.css Normal file
View File

@ -0,0 +1,14 @@
body{
background-color: rgba(8, 6, 37, 0.781);
color: white;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin-left: 5%;
}
a, a:visited {
color: white;
}
ul{
list-style-type: none;
}

3
run.py Normal file
View File

@ -0,0 +1,3 @@
import imgmine
imgmine.start_server()

13
run_tests.sh Executable file
View File

@ -0,0 +1,13 @@
#!/bin/bash
ran=0
SECONDS=0 ;
close () {
exit 10;
}
for f in tests/*.py; do
python3 "$f" || close # if needed
let "ran++"
done
echo "ran $ran test files successfully in $SECONDS seconds"
rm -f *.dat

7
tests/test.py Normal file
View File

@ -0,0 +1,7 @@
import unittest
class TestBasic(unittest.TestCase):
def test_basic(self):
self.assertTrue(True)
unittest.main()