diff --git a/.gitignore b/.gitignore
index 06647ff..d2c5a66 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,5 @@ __pycache__
.coverage
*.egg-info
sample
-.tox
\ No newline at end of file
+.tox
+*.sqlite
\ No newline at end of file
diff --git a/static/css/yoink.css b/static/css/yoink.css
new file mode 100644
index 0000000..2eabf48
--- /dev/null
+++ b/static/css/yoink.css
@@ -0,0 +1,3 @@
+* {
+ box-sizing: border-box;
+}
\ No newline at end of file
diff --git a/static/js/yoink.js b/static/js/yoink.js
new file mode 100644
index 0000000..e69de29
diff --git a/templates/404.html b/templates/404.html
new file mode 100644
index 0000000..1445757
--- /dev/null
+++ b/templates/404.html
@@ -0,0 +1,14 @@
+{% extends "base.html" %}
+
+{% block meta %}
+{{ super() }}
+
+{% endblock %}
+
+{% block title %}Testing{% endblock %}
+
+{% block page_content %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/500.html b/templates/500.html
new file mode 100644
index 0000000..e0bd7b8
--- /dev/null
+++ b/templates/500.html
@@ -0,0 +1,14 @@
+{% extends "base.html" %}
+
+{% block meta %}
+{{ super() }}
+
+{% endblock %}
+
+{% block title %}Testing{% endblock %}
+
+{% block page_content %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/base.html b/templates/base.html
new file mode 100644
index 0000000..48cb4a8
--- /dev/null
+++ b/templates/base.html
@@ -0,0 +1,61 @@
+
+
+
+ {% block meta %}
+
+
+
+ {% endblock %}
+ {% block title %}Y!oink Web App{% endblock %}
+ {% block styles %}
+
+
+ {% endblock %}
+ {% block scripts %}
+
+ {{ moment.include_moment() }}
+ {% endblock %}
+
+
+
+ {% block navbar %}
+
+ {% endblock %}
+
+ {% block content %}
+
+ {% for message in get_flashed_messages() %}
+
+
+ {{message}}
+
+ {% endfor %}
+ {% block page_content %}
+ {% endblock %}
+
+ {% endblock %}
+
+
+
+
\ No newline at end of file
diff --git a/templates/index.html b/templates/index.html
new file mode 100644
index 0000000..d25fca7
--- /dev/null
+++ b/templates/index.html
@@ -0,0 +1,31 @@
+{% extends "base.html" %}
+{% import 'macros/library.html' as library %}
+
+{% block meta %}
+{{ super() }}
+
+{% endblock %}
+
+{% block title %}Testing{% endblock %}
+
+{% block page_content %}
+
+
+
+
+{{ library.latest_downloads(latest) }}
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/macros/comments.html b/templates/macros/comments.html
new file mode 100644
index 0000000..35dddb0
--- /dev/null
+++ b/templates/macros/comments.html
@@ -0,0 +1,3 @@
+{% macro render_comment(comment) %}
+ {{ comment }}
+{% endmacro %}
diff --git a/templates/macros/library.html b/templates/macros/library.html
new file mode 100644
index 0000000..1b29b91
--- /dev/null
+++ b/templates/macros/library.html
@@ -0,0 +1,38 @@
+
+{% macro latest_downloads(comics) %}
+ {% if comics %}
+
+
Latest Downloads
+ {% for comic in comics %}
+
+
+
![{{ comic['title'] }}]({{ url_for('download_file', filename=comic['cover']) }})
+
+
{{ comic['title'] }}
+
{{ comic['title'] }}
+
+
+
+
+ {% endfor %}
+
+ {% else %}
+ Empty Comic Library
+ {% endif %}
+{% endmacro %}
+
+
+{% macro all_downloads(comics) %}
+ {% if comics %}
+ {% for comic in comics %}
+
+ {{ comic['title'] }}
+
+
+ {% endfor %}
+ {% else %}
+ Empty Comic Library
+ {% endif %}
+{% endmacro %}
\ No newline at end of file
diff --git a/web.py b/web.py
new file mode 100644
index 0000000..e60c867
--- /dev/null
+++ b/web.py
@@ -0,0 +1,141 @@
+import os
+import threading
+from flask import Flask, render_template, url_for, request, flash, make_response, redirect, send_from_directory
+from flask_moment import Moment
+from flask_sqlalchemy import SQLAlchemy
+from flask_wtf import FlaskForm
+from wtforms import StringField, SubmitField, BooleanField
+from wtforms.validators import DataRequired
+
+from yoink.config import config
+from yoink.comic import Comic
+
+
+class DownloadForm(FlaskForm):
+ url = StringField('Comic URL', validators=[DataRequired()])
+ series = BooleanField('Series? ')
+ download = SubmitField('Download')
+
+app = Flask(__name__)
+moment = Moment(app)
+app.config['SECRET_KEY'] = 'snapekilleddumpledork'
+app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{os.path.join(config.app_root, "data.sqlite")}'
+app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
+db = SQLAlchemy(app)
+
+
+
+class Role(db.Model):
+ __tablename__ = 'roles'
+ id = db.Column(db.Integer, primary_key=True)
+ name = db.Column(db.String(64), unique=True)
+ users = db.relationship('User', backref='role', lazy='dynamic')
+
+ def __repr__(self):
+ return f''
+
+
+class User(db.Model):
+ __tablename__ = 'users'
+ id = db.Column(db.Integer, primary_key=True)
+ username = db.Column(db.String(64), unique=True, index=True)
+ role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
+
+ def __repr__(self): return f''
+
+class ComicMeta(db.Model):
+ __tablename__ = 'comicmeta'
+ id = db.Column(db.Integer, primary_key=True)
+ title = db.Column(db.String(256), unique=True, index=True)
+ issue = db.Column(db.Integer, nullable=True, index=True)
+ category = db.Column(db.String(128), index=True, nullable=True)
+ previous_issue = db.Column(db.String(256), nullable=True)
+ next_issue = db.Column(db.String(256), nullable=True)
+ cover_path = db.Column(db.String(256))
+ archive_path = db.Column(db.String(256))
+
+
+@app.errorhandler(404)
+def not_found(e):
+ return render_template('404.html'), 404
+
+@app.errorhandler(500)
+def server_error(e):
+ return render_template('500.html'), 500
+
+@app.route('/uploads/')
+def download_file(filename):
+ return send_from_directory(os.path.join(config.library_path, 'comics'), filename, as_attachment=True)
+
+
+def get_cover_path(comic):
+ return [image for image in os.listdir(os.path.join(config.library_path, 'comics', comic.title)) if image.endswith('000.jpg')][0]
+
+
+def get_archive_path(comic):
+ return [image for image in os.listdir(os.path.join(config.library_path, 'comics', comic.title)) if image.endswith('.cbr')][0]
+
+
+def get_comic_library_meta():
+ comic_meta = []
+
+ for comic in ComicMeta.query.all():
+ comic_meta.append({
+ 'cover': comic.cover_path,
+ 'title': comic.title,
+ 'archive': comic.archive_path
+ })
+
+ return comic_meta
+
+
+@app.route('/', methods=['post','get'])
+def index():
+ url = None
+ series = False
+ form = DownloadForm()
+ latest = get_comic_library_meta()
+
+ if form.validate_on_submit():
+ url = form.url.data.strip()
+ series = form.series.data
+
+ comic = Comic(url)
+ comic_meta = ComicMeta.query.filter_by(title=comic.title).first()
+
+ comic.archiver.download()
+ comic.archiver.generate_archive()
+
+
+ if comic_meta is None:
+ comic_meta = ComicMeta()
+ comic_meta.title = comic.title
+ comic_meta.category = comic.category
+ comic_meta.issue = comic.issue_number
+ comic_meta.next_issue = comic.next
+ comic_meta.previous_issue = comic.prev
+ comic_meta.cover_path = os.path.join(comic.title, get_cover_path(comic))
+ comic_meta.archive_path = os.path.join(comic.title, get_archive_path(comic))
+
+ db.session.add(comic_meta)
+ db.session.commit()
+ else:
+ flash(f'Comic {comic.title} exists')
+
+ latest = get_comic_library_meta()
+ form.url.data = ''
+
+ return render_template('index.html', form=form, url=url, series=series, latest=latest), 200
+
+
+ if form.series.data:
+ print('Download the whole damn lot')
+
+ flash(f'{comic.title} downloaded to {os.path.join(config.library_path, "comics/" + comic.title)}')
+
+ latest = get_comic_library_meta()
+ comic.archiver.cleanup_worktree()
+ form.url.data = ''
+
+ return render_template('index.html', form=form, url=url, series=series, latest=latest), 200
+
\ No newline at end of file
diff --git a/yoink/comic.py b/yoink/comic.py
index 866f4e3..b1e52a3 100644
--- a/yoink/comic.py
+++ b/yoink/comic.py
@@ -42,7 +42,7 @@ class Comic(Scrapable):
if len(comics) > 0:
return comics
-
+
@property
def filelist(self) -> list:
comics = self.__parse_soup()
diff --git a/yoink/tests/test_basic.py b/yoink/tests/test_basic.py
index 27c710e..798402a 100644
--- a/yoink/tests/test_basic.py
+++ b/yoink/tests/test_basic.py
@@ -15,7 +15,6 @@ class BasicTestCase(unittest.TestCase):
self.test_comic = 'http://readallcomics.com/static-season-one-6-2022/'
self.test_comic_b = 'http://readallcomics.com/captain-marvel-vs-rogue-2021-part-1/'
self.comic = Comic(self.test_comic)
- self.archiver = ComicArchiver(self.comic)
self.remove_queue = []
self.expected_title = 'Static Season One 6 (2022)'
self.expected_title_b = 'Captain Marvel vs. Rogue (2021 – Part 1)'
@@ -46,16 +45,16 @@ class BasicTestCase(unittest.TestCase):
self.assertEqual(len(os.listdir(os.path.join(library_path, 'comics'))), 0)
def test_004_comic_folder_created_and_populated(self):
- self.archiver.download()
+ self.comic.archiver.download()
self.assertTrue(os.path.exists(os.path.join(library_path, f'comics/{self.comic.title}')))
self.assertGreater(len(os.listdir(os.path.join(library_path, f'comics/{self.comic.title}'))), 0)
def test_005_comic_archive_generated(self):
- self.archiver.generate_archive()
+ self.comic.archiver.generate_archive()
self.assertTrue(os.path.exists(os.path.join(library_path, f'comics/{self.comic.title}/{self.comic.title}.cbr')))
def test_006_folder_cleaned_after_archive_generation(self):
- self.archiver.cleanup_worktree()
+ self.comic.archiver.cleanup_worktree()
self.assertLessEqual(len(os.listdir(os.path.join(library_path, f'comics/{self.comic.title}'))), 3)
def test_007_comic_instance_has_archiver(self):
diff --git a/yoink/yoink.json b/yoink/yoink.json
index 49e1b47..0e74ba1 100644
--- a/yoink/yoink.json
+++ b/yoink/yoink.json
@@ -11,7 +11,7 @@
{
"name": "readallcomics",
"url": "http://readallcomics.com",
- "search": {
+ "filters": {
"default": {
"element": "div",
"class": "separator",
@@ -34,7 +34,7 @@
{
"name": "dragonballsupermanga",
"url": "https://www.dragonballsupermanga.net",
- "search": {
+ "filters": {
"dbsuper": {
"element": "meta",
"class": null,
@@ -47,7 +47,7 @@
{
"name": "mangadex",
"url": "https://www.mangadex.tv",
- "search": {
+ "filters": {
"mangadex": {
"element": "img",
"class": null,