From db051fcf34338e31748b1a197878bb6bd33d84a0 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Thu, 10 Oct 2019 21:14:16 +0200 Subject: [PATCH 01/39] 2.0-dev cleanup --- .editorconfig | 34 - .gitignore | 56 -- .gitmodules | 6 - README.md | 9 + config.defaults.ini | 34 - configs/demo.ini | 15 - configs/kadsen.ini | 9 - configs/nsfw.ini | 15 - configs/pr0.ini | 9 - configs/sfw.ini | 14 - crawler/__init__.py | 442 ------------- crawler/fourchan.py | 59 -- crawler/giphy.py | 52 -- crawler/instagram.py | 65 -- crawler/ninegag.py | 78 --- crawler/pr0gramm.py | 105 --- crawler/reddit.py | 55 -- crawler/soupio.py | 63 -- nichtparasoup.py | 402 ------------ requirements.txt | 9 - templates.py | 374 ----------- templates_raw/README.md | 26 - templates_raw/_bundler | 1 - templates_raw/_foreign/normalize.css | 1 - templates_raw/build.sh | 34 - templates_raw/root/.editorconfig | 36 -- templates_raw/root/README.md | 26 - templates_raw/root/build.sh | 10 - templates_raw/root/css/feel.css | 11 - templates_raw/root/css/look.css | 200 ------ templates_raw/root/css/look_bossMode.css | 7 - templates_raw/root/css/look_buttons.css | 43 -- templates_raw/root/css/look_hotkeys.css | 60 -- templates_raw/root/css/look_stateSwitch.css | 24 - templates_raw/root/css/normalize.css | 1 - templates_raw/root/css/sourceIcons.css | 43 -- templates_raw/root/flush | 1 - templates_raw/root/get | 7 - templates_raw/root/js/feel.js | 485 -------------- templates_raw/root/js/feel_hotkeys.js | 122 ---- templates_raw/root/js/helper.js | 71 --- templates_raw/root/js/look_maxSizer.js | 116 ---- templates_raw/root/js/look_stateSwitch.js | 25 - templates_raw/root/js/snowstorm.js | 668 -------------------- templates_raw/root/reset | 1 - templates_raw/root/root.html | 163 ----- templates_raw/root/test/get_local | 7 - templates_raw/root/test/get_remote | 7 - templates_raw/root/test/testimg.png | Bin 1761 -> 0 bytes tests/configs/README.md | 10 - tests/configs/factors.ini | 11 - tests/configs/fourchan.ini | 10 - tests/configs/giphy.ini | 9 - tests/configs/instagram.ini | 10 - tests/configs/ninegag.ini | 9 - tests/configs/pr0gramm.ini | 9 - tests/configs/reddit.ini | 9 - tests/configs/soup.ini | 9 - tests/logs/.gitignore | 3 - 59 files changed, 9 insertions(+), 4181 deletions(-) delete mode 100644 .editorconfig delete mode 100644 .gitignore delete mode 100644 .gitmodules delete mode 100644 config.defaults.ini delete mode 100644 configs/demo.ini delete mode 100644 configs/kadsen.ini delete mode 100644 configs/nsfw.ini delete mode 100644 configs/pr0.ini delete mode 100644 configs/sfw.ini delete mode 100644 crawler/__init__.py delete mode 100644 crawler/fourchan.py delete mode 100644 crawler/giphy.py delete mode 100644 crawler/instagram.py delete mode 100644 crawler/ninegag.py delete mode 100644 crawler/pr0gramm.py delete mode 100644 crawler/reddit.py delete mode 100644 crawler/soupio.py delete mode 100755 nichtparasoup.py delete mode 100644 requirements.txt delete mode 100644 templates.py delete mode 100644 templates_raw/README.md delete mode 160000 templates_raw/_bundler delete mode 160000 templates_raw/_foreign/normalize.css delete mode 100755 templates_raw/build.sh delete mode 100644 templates_raw/root/.editorconfig delete mode 100644 templates_raw/root/README.md delete mode 100755 templates_raw/root/build.sh delete mode 100644 templates_raw/root/css/feel.css delete mode 100644 templates_raw/root/css/look.css delete mode 100644 templates_raw/root/css/look_bossMode.css delete mode 100644 templates_raw/root/css/look_buttons.css delete mode 100644 templates_raw/root/css/look_hotkeys.css delete mode 100644 templates_raw/root/css/look_stateSwitch.css delete mode 120000 templates_raw/root/css/normalize.css delete mode 100644 templates_raw/root/css/sourceIcons.css delete mode 100644 templates_raw/root/flush delete mode 100644 templates_raw/root/get delete mode 100644 templates_raw/root/js/feel.js delete mode 100644 templates_raw/root/js/feel_hotkeys.js delete mode 100644 templates_raw/root/js/helper.js delete mode 100644 templates_raw/root/js/look_maxSizer.js delete mode 100644 templates_raw/root/js/look_stateSwitch.js delete mode 100644 templates_raw/root/js/snowstorm.js delete mode 100644 templates_raw/root/reset delete mode 100644 templates_raw/root/root.html delete mode 100644 templates_raw/root/test/get_local delete mode 100644 templates_raw/root/test/get_remote delete mode 100644 templates_raw/root/test/testimg.png delete mode 100644 tests/configs/README.md delete mode 100644 tests/configs/factors.ini delete mode 100644 tests/configs/fourchan.ini delete mode 100644 tests/configs/giphy.ini delete mode 100644 tests/configs/instagram.ini delete mode 100644 tests/configs/ninegag.ini delete mode 100644 tests/configs/pr0gramm.ini delete mode 100644 tests/configs/reddit.ini delete mode 100644 tests/configs/soup.ini delete mode 100644 tests/logs/.gitignore diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index abe2f6ad..00000000 --- a/.editorconfig +++ /dev/null @@ -1,34 +0,0 @@ -root = true - -[*] -end_of_line = LF - -[*.py] -indent_style = space -indent_size = 4 -tab_width = 4 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.css] -indent_style = space -indent_size = 2 -tab_width = 2 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.js] -indent_style = space -indent_size = 2 -tab_width = 2 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.ini] -trim_trailing_whitespace = true -insert_final_newline = true - -[*.md] -indent_style = space -indent_size = 3 -trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore deleted file mode 100644 index abbacd5a..00000000 --- a/.gitignore +++ /dev/null @@ -1,56 +0,0 @@ - -## ignore our own log files -*.log -logs/* - -## ignore our own custom config -./config.ini - - - -## ignore python runtime files - based on https://github.com/github/gitignore/blob/master/Python.gitignore - -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.cache -nosetests.xml -coverage.xml - -# Translations -*.mo -*.pot - -# Django stuff: -*.log - -# Sphinx documentation -docs/_build/ diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 8cbf57b4..00000000 --- a/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "templates_raw/_bundler"] - path = templates_raw/_bundler - url = https://github.com/jkowalleck/HTML_bundler.git -[submodule "templates_raw/_foreign/normalize.css"] - path = templates_raw/_foreign/normalize.css - url = https://github.com/necolas/normalize.css.git diff --git a/README.md b/README.md index 22bf244a..9b24fc8f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,14 @@ # nichtparasoup +--- + +:construction: THIS FILE IS OUTDATED +:construction: DUE TO A REWRITE + +--- + + + nichtparasoup is a hackspaces entertainment system. It randomly displays images/gifs from [giphy](https://giphy.com), diff --git a/config.defaults.ini b/config.defaults.ini deleted file mode 100644 index c7883c90..00000000 --- a/config.defaults.ini +++ /dev/null @@ -1,34 +0,0 @@ -;; DEFAULT config for nichtparasoup -;; see README file for more - -;; DO -;; NOT -;; TOUCH - -[General] -Port: 5000 -IP: 0.0.0.0 -Useragent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit/600.1.25 (KHTML, like Gecko) Version/8.0 Safari/600.1.25 - -[Cache] -Images_min_limit: 15 - -[Logging] -;; possible destinations: file, syslog -Destination: file -File: nichtparasoup.log -;; verbosity levels: CRITICAL, ERROR, WARNING, INFO, DEBUG, NOTSET -Verbosity: ERROR - -[Sites] -;; no sites default configured. -;; more examples: see the configs dir -;; more examples: see the tests/configs dir -;; just an example: -; SoupIO: everyone -; Pr0gramm: new,top -; Reddit: nsfw,gifs,pics,nsfw_gifs,aww,aww_gifs,reactiongifs,wtf,FoodPorn,cats,StarWars -; NineGag: geeky,wtf,girl,hot,trending -; Instagram: cats,animals,pornhub,nerdy_gaming_art,nature,wtf -; Fourchan: b,sci - diff --git a/configs/demo.ini b/configs/demo.ini deleted file mode 100644 index d53e096d..00000000 --- a/configs/demo.ini +++ /dev/null @@ -1,15 +0,0 @@ -[General] -Port: 5006 - -[Logging] -Destination: syslog -Verbosity: WARNING - -[Sites] -SoupIO: false -Pr0gramm: false -Reddit: gifs*6.0,pics*4.0,aww_gifs,FoodPorn*1.0,cats,EarthPorn -NineGag: false -Instagram: false -Fourchan: false -Giphy: false diff --git a/configs/kadsen.ini b/configs/kadsen.ini deleted file mode 100644 index c0c3bb31..00000000 --- a/configs/kadsen.ini +++ /dev/null @@ -1,9 +0,0 @@ -[General] -Port: 5003 - -[Logging] -Destination: syslog -Verbosity: WARNING - -[Sites] -Reddit: aww_gifs,aww,cats diff --git a/configs/nsfw.ini b/configs/nsfw.ini deleted file mode 100644 index 65aeb57d..00000000 --- a/configs/nsfw.ini +++ /dev/null @@ -1,15 +0,0 @@ -[General] -Port: 5002 - -[Logging] -Destination: Syslog -Verbosity: WARNING - -[Sites] -SoupIO: everyone*5.0 -Pr0gramm: top*9.0,new*0.5 -Reddit: nsfw,gifs,pics,nsfw_gifs,aww_gifs*0.3,reactiongifs,wtf*3.0 -NineGag: false -Instagram: false -Fourchan: b*6.0 -Giphy: false diff --git a/configs/pr0.ini b/configs/pr0.ini deleted file mode 100644 index dd2b7b1a..00000000 --- a/configs/pr0.ini +++ /dev/null @@ -1,9 +0,0 @@ -[General] -Port: 5004 - -[Logging] -Destination: syslog -Verbosity: WARNING - -[Sites] -Pr0gramm: top*9.0,new*0.8 diff --git a/configs/sfw.ini b/configs/sfw.ini deleted file mode 100644 index eb5da651..00000000 --- a/configs/sfw.ini +++ /dev/null @@ -1,14 +0,0 @@ -[General] -Port: 5000 - -[Logging] -Destination: File -File: /tmp/foo.log -Verbosity: debug - -[Sites] -SoupIO: false -Pr0gramm: false -Reddit: gifs*9.0,pics,aww_gifs,mildlyinteresting*3.0,reactiongifs,Oddlysatisfying,Spaceflightporn,Therewasanattempt*5.0,HistoryMemes*3.0,Unexpected,Whatcouldgowrong,Weird,photoshopbattles,NichtDerPostillon,Nonononoyes,earthporn*0.5,Bettereveryloop,assholedesign,machineporn,Shittyfoodporn,Instant_regret,Mildlyinfuriating,somememes,Crappydesign*5.0,Accidentalwesanderson,engineeringporn,hmmm*2.0,mechanical_gifs,cableporn,shittylifehacks*3.0,perfecttiming,AnimalPorn,CatGifs,ChildrenFallingOver,funny,geek,IdiotsInCars,interestingasfuck,itsaunixsystem*5.0,MadeMeSmile,insanepeoplefacebook,PeopleFuckingDying*3.0,spaceflightporn,woahdude*4.0,spaceporn*2.0,neckbeardnests,techsupportgore,perfectloops,wasletztepreis -Instagram: false -Fourchan: false diff --git a/crawler/__init__.py b/crawler/__init__.py deleted file mode 100644 index 76411ff9..00000000 --- a/crawler/__init__.py +++ /dev/null @@ -1,442 +0,0 @@ - -__all__ = ['Crawler', 'CrawlerError', 'ImageData'] - - -import sys -import random -import time -import re - -try: - from urllib.request import Request, urlopen # py3 -except ImportError: - from urllib2 import Request, urlopen # py2 - -try: - from urllib.request import URLError as HTTPError # py3 -except ImportError: - from urllib2 import HTTPError # py2 - -try: - from urllib.parse import urljoin # py3 -except ImportError: - from urlparse import urljoin # py2 - -from bs4 import BeautifulSoup - - -class Crawler(object): - """ - abstract class Crawler - """ - - ## class constants - - __C_timeout_ = 'timeout' - __C_headers_ = 'headers' - __C_resetDelay_ = 'resetDelay' - - __refresh_uri_RE = re.compile(r'^(?:\d*;)?url=(.+)$', flags=re.IGNORECASE) - - __image_RE = re.compile(r'.*\.(?:jpeg|jpg|png|gif)(?:\?.*)?(?:#.*)?$', flags=re.IGNORECASE) - - ## class vars - - __configuration = { - __C_timeout_: 10, # needs to be greater 0 - __C_headers_: {}, # additional headers - __C_resetDelay_: 10800 # value in seconds - } - - __blacklist = [] - __images = {} - - __factors = None - __logger = None - - ## properties - - __crawlingStarted = 0.0 - - ## config methods - - @classmethod - def configure(cls, config=None, key=None, value=None): - if isinstance(config, dict): - cls.__configuration += config - elif key and value: - cls.__configuration[key] = value - - @classmethod - def __config_setter_and_getter(cls, key, value=None): - if value is None: - return cls.__configuration[key] - cls.configure(key=key, value=value) - - ## wellknown config accessors - - @classmethod - def request_headers(cls, value=None): - return cls.__config_setter_and_getter(cls.__C_headers_, value) - - @classmethod - def request_timeout(cls, value=None): - return cls.__config_setter_and_getter(cls.__C_timeout_, value) - - @classmethod - def reset_delay(cls, value=None): - return cls.__config_setter_and_getter(cls.__C_resetDelay_, value) - - ## basic document fetcher - - @classmethod - def __html_find_meta_refresh(cls, document): - """ - :type document: BeautifulSoup - :rtype: str | None - """ - refresh_uri = None - - # - meta_refresh = document.find("meta", {"http-equiv": "refresh", "content": cls.__refresh_uri_RE}) - if meta_refresh: - # @fixme html-decode ! - refresh_uri = cls.__refresh_uri_RE.match(meta_refresh["content"]).group(1) - - return refresh_uri - - @staticmethod - def __html_find_base(document): - """ - :type document: BeautifulSoup - :rtype: str | None - """ - base_uri = None - - # - base_tag = document.find("base", {"href": True}) - if base_tag: - # @fixme html-decode ! - base_uri = base_tag["href"] - - return base_uri - - @classmethod - def _fetch_remote(cls, uri, depth_indicator=1): - """ - return remote document and actual remote uri - :type uri: str - :type depth_indicator: int - :rtype: (document: str | None, uri) - """ - - cls._log("debug", "fetch remote(%d): %s" % (depth_indicator, uri)) - request = Request(uri, headers=cls.request_headers()) - try: - response = urlopen(request, timeout=cls.request_timeout()) - except HTTPError: - cls._log("warn", "Could not fetch: %s" % uri) - return None - - if not response: - return None - - uri = response.geturl() - - charset = 'utf8' - try: - charset = response.info().get_param('charset', charset) # py3 - except AttributeError: - pass - - return response.read().decode(charset), uri - - @classmethod - def _fetch_remote_html(cls, uri, follow_meta_refresh=True, follow_meta_refresh_max=5, bs4features="html5lib"): - """ - returns remote HTML document, actual remote uri and base uri - :type uri: str - :type follow_meta_refresh: bool - :type follow_meta_refresh_max: int - :rtype: ( document: BeautifulSoup | None , base: str, uri: str ) - """ - - document = None - follow_meta_refresh_depth = 1 - - while True: - (response, uri) = cls._fetch_remote(uri, follow_meta_refresh_depth) - - if not response: - break - - document = BeautifulSoup(response, features=bs4features) - - if not follow_meta_refresh: - break - - refresh_uri = cls.__html_find_meta_refresh(document) - if not refresh_uri: - break - refresh_uri = urljoin(uri, refresh_uri) - - if refresh_uri == uri: - break - - cls._log("debug", "fetch remote HTML(%d): %s meta-refreshes to %s" % - (follow_meta_refresh_depth, uri, refresh_uri)) - uri = refresh_uri - - follow_meta_refresh_depth += 1 - if follow_meta_refresh_depth >= follow_meta_refresh_max: - break - - if not document: - cls._log("debug", "fetch remote HTML: %s is empty" % uri) - - base = uri - - doc_base = cls.__html_find_base(document) - if doc_base: - base = urljoin(base, doc_base) - - return document, base, uri - - ## general functions - - @classmethod - def __blacklist_clear(cls): - cls.__blacklist = [] # be aware: list.clean() is not available in py2 - - @classmethod - def _blacklist(cls, uri): - cls.__blacklist.append(uri) - - @classmethod - def _is_blacklisted(cls, uri): - return uri in cls.__blacklist - - @classmethod - def _is_image(cls, uri): - return cls.__image_RE.match(uri) is not None - - @classmethod - def __images_clear(cls): - cls.__images = {} # be aware: dict.clean() is not available in py2 - - @classmethod - def __add_image(cls, uri, crawler, site, source, more): - """ - :type uri: str - :type crawler: str - :type site: str - :type source: str|None - :type more: dict - :rtype: bool - """ - if not cls._is_image(uri): - # self._log("info", uri + " is no image ") - return False - - if cls._is_blacklisted(uri): - return False - - if crawler not in cls.__images: - cls.__images[crawler] = {} - - if site not in cls.__images[crawler]: - cls.__images[crawler][site] = [] - - cls._blacklist(uri) # add it to the blacklist to detect duplicates - cls.__images[crawler][site].append(ImageData(uri, source, more)) - cls._log("debug", "added %s-%s-%s: %s" % (crawler, site, source, uri)) - return True - - @classmethod - def get_image(cls): - """ - :rtype: dict - """ - images = cls.__images - factors = cls.__factors - if images: - # return image respecting factors: - # Summing up the Factors till reaching a random number - - max = 0 - for crawler in images: - for site in images[crawler]: - factor = 1 - if crawler in factors and site in factors[crawler]: - factor = factors[crawler][site] - - max += factor - - rnd = random.uniform(0, max) - - count = 0 - for crawler in images: - for site in images[crawler]: - factor = 1 - if crawler in factors and site in factors[crawler]: - factor = factors[crawler][site] - count += factor - if count <= rnd: - continue - - image = random.choice(images[crawler][site]) - if not image: - continue - - images[crawler][site].remove(image) - cls._log("debug", "delivered %s-%s: %s - remaining: %d" - % (crawler, site, image.uri, len(images[crawler][site]))) - - result = image.asDict() - result['crawler'] = crawler - result['site'] = site - return result - return None - - @classmethod - def set_logger(cls, logger): - cls.__logger = logger - - @classmethod - def set_factors(cls, factors): - cls.__factors = factors - - @classmethod - def _log(cls, log_type, message): - if cls.__logger: - getattr(cls.__logger, log_type)(message) - - @classmethod - def _debug(cls): - return "" % (cls.__configuration, cls.info()) - - @classmethod - def info(cls): - images = cls.__images - blacklist = cls.__blacklist - - info = { - "blacklist": len(blacklist), - "blacklist_size": sys.getsizeof(blacklist, 0), - "images_per_site": {} - } - - images_count = 0 - images_size = 0 - for crawler in images: - for site in images[crawler]: - site_image_count = len(images[crawler][site]) - images_count += site_image_count - images_size += sys.getsizeof(images[crawler][site]) - info["images_per_site"][crawler + "_" + site] = site_image_count - - info["images_size"] = images_size - info["images"] = images_count - - return info - - @classmethod - def show_imagelist(cls): - imagelist = [] - for crawler in cls.__images: - for site in cls.__images[crawler]: - for imageData in cls.__images[crawler][site]: - imagelist.append(imageData.uri) - return imagelist - - @classmethod - def show_blacklist(cls): - return cls.__blacklist - - @classmethod - def flush(cls): - cls.__images_clear() - cls._log("info", "flushed. cache is now empty") - - @classmethod - def reset(cls): - cls.__images_clear() - cls.__blacklist_clear() - cls._log("info", "reset. cache and blacklist are now empty") - - def crawl(self): - now = time.time() - if not self.__crawlingStarted: - self.__crawlingStarted = now - elif self.__crawlingStarted <= now - self.__class__.reset_delay(): - self.__class__._log("debug", "instance %s starts at front" % (repr(self))) - self.__crawlingStarted = now - self._restart_at_front() - - self.__class__._log("debug", "instance %s starts crawling" % (repr(self))) - try: - self._crawl() - except TypeError: - # Catch empty returns from crawling (returns nonetype) - pass - except CrawlerError as e: - self.__class__._log("exception", "crawler error: %s" % (repr(e))) - except: - e = sys.exc_info()[0] - self.__class__._log("exception", "unexpected crawler error: %s" % (repr(e))) - - def _add_image(self, uri, site, source=None, **kwargs): - """ - :type uri: str - :type site: str - :type source: str|None - :type kwargs: dict - :rtype: bool - """ - return self.__class__.__add_image(uri, self.__class__.__name__, site, source, kwargs) - - ## abstract functions - - def __init__(self): - # call every abstract method to be sure it is there - self._restart_at_front() - self._crawl() - raise NotImplementedError("Should have implemented this") - - def _crawl(self): - raise NotImplementedError("Should have implemented this") - - def _restart_at_front(self): - raise NotImplementedError("Should have implemented this") - - -class CrawlerError(Exception): - pass - - -class ImageData(object): - """ class def: image data """ - - uri = "" - source = None - more = {} - - def __init__(self, uri, source=None, more={}): - """ - :type uri: str - :type source: str|None - :type more: dict - """ - self.uri = uri - self.source = source - self.more = more - - def asDict(self): - """ - :rtype: dict - """ - return { - "uri": self.uri , - "source": self.source , - "more": self.more , - } diff --git a/crawler/fourchan.py b/crawler/fourchan.py deleted file mode 100644 index e5818eed..00000000 --- a/crawler/fourchan.py +++ /dev/null @@ -1,59 +0,0 @@ - - -try: - from urllib.parse import urljoin # py3 -except ImportError: - from urlparse import urljoin # py2 - - -from . import Crawler, CrawlerError - - -class Fourchan(Crawler): - """ Fourchan image provider """ - - __uri = "" - __next = "" - __site = "" - - @staticmethod - def __build_uri(uri): - return uri - - def _restart_at_front(self): - self.__next = self.__uri - - def __init__(self, uri, site): - self.__site = site - self.__uri = self.__class__.__build_uri(uri) - self._restart_at_front() - - def _crawl(self): - uri = self.__next - if not uri: - return - - self.__class__._log("debug", "%s crawls url: %s" % (self.__class__.__name__, uri)) - - (page, base, _) = self.__class__._fetch_remote_html(uri) - if not page: - self.__class__._log("debug", "%s crawled EMPTY url: %s" % (self.__class__.__name__, uri)) - return - - # get more content ("scroll down") - # to know what page to parse next - # update new last URI when we're not on first run - _buttons = page.find_all("a", {"class": "button", "href": True}) - if _buttons: - self.__next = urljoin(base, _buttons[-1]["href"]) - else: - self.__class__._log("debug", "%s found no `next` on url: %s" % (self.__class__.__name__, uri)) - self.__next = "" - - images_added = 0 - for con in page.find_all("a", {"class": "fileThumb", "href": True}): - if self._add_image(urljoin(base, con["href"]), self.__site): - images_added += 1 - - if not images_added: - self.__class__._log("debug", "%s found no images on url: %s" % (self.__class__.__name__, uri)) diff --git a/crawler/giphy.py b/crawler/giphy.py deleted file mode 100644 index 5c0b9c59..00000000 --- a/crawler/giphy.py +++ /dev/null @@ -1,52 +0,0 @@ - - -import json - -from . import Crawler, CrawlerError - - -class Giphy(Crawler): - """ class def: a crawler for Giphy """ - - __uri = "" - __next = 0 - __site = "" - - __limit = 50 - - __api_key = "dc6zaTOxFJmzC" - - @classmethod - def _build_uri(cls, uri): - return uri + "&api_key=" + cls.__api_key + "&limit=" + str(cls.__limit) - - def _restart_at_front(self): - self.__next = 0 - - def __init__(self, uri, site): - self.__site = site - self.__uri = self.__class__._build_uri(uri) - self._restart_at_front() - - def _crawl(self): - uri = self.__uri + "&offset=" + str(self.__next) - self.__class__._log("debug", "%s crawls url: %s" % (self.__class__.__name__, uri)) - - (remote, uri) = self.__class__._fetch_remote(uri) - if not remote: - self.__class__._log("debug", "%s crawled EMPTY url: %s" % (self.__class__.__name__, uri)) - return - - data = json.loads(remote) - - self.__next += self.__limit - - images_added = 0 - for child in data['data']: - image = child['images']['original']['url'] - if image: - if self._add_image(image, self.__site): - images_added += 1 - - if not images_added: - self.__class__._log("debug", "%s found no images on url: %s" % (self.__class__.__name__, uri)) diff --git a/crawler/instagram.py b/crawler/instagram.py deleted file mode 100644 index e5454883..00000000 --- a/crawler/instagram.py +++ /dev/null @@ -1,65 +0,0 @@ - - -try: - from urllib.parse import urljoin # py3 -except ImportError: - from urlparse import urljoin # py2 - -import json - - -from . import Crawler, CrawlerError - - -class Instagram(Crawler): - """ instagram image provider """ - - __uri = "" - __last = "" - __site = "" - - ## class methods - - @staticmethod - def __build_uri(uri): - return urljoin(uri+"/", "./media/") - - ## instance methods - - def _restart_at_front(self): - self.__last = "" - - def __init__(self, uri, site): - self.__site = site - self.__uri = self.__class__.__build_uri(uri) - self._restart_at_front() - - def _crawl(self): - uri = urljoin(self.__uri, "?max_id="+self.__last) - self.__class__._log("debug", "%s crawls url: %s" % (self.__class__.__name__, uri)) - - (remote, uri) = self.__class__._fetch_remote(uri) - if not remote: - self.__class__._log("debug", "%s crawled EMPTY url: %s" % (self.__class__.__name__, uri)) - return - - data = json.loads(remote) - if "status" not in data or data["status"] != "ok": - raise CrawlerError() - - images_added = 0 - for item_image in [item for item in data['items'] if item["type"] == "image"]: - if 'id' in item_image: - self.__last = item_image['id'] - - if 'images' in item_image \ - and 'standard_resolution' in item_image['images']\ - and 'url' in item_image['images']['standard_resolution']: - image = item_image['images']['standard_resolution']['url'] - if image: - if self._add_image(image, self.__site): - images_added += 1 - - if not images_added: - self.__class__._log("debug", "%s found no images on url: %s" % (self.__class__.__name__, uri)) - diff --git a/crawler/ninegag.py b/crawler/ninegag.py deleted file mode 100644 index 32033e5f..00000000 --- a/crawler/ninegag.py +++ /dev/null @@ -1,78 +0,0 @@ - -try: - from urllib.parse import urljoin # py3 -except ImportError: - from urlparse import urljoin # py2 - -import re - - -from . import Crawler, CrawlerError - - - -class NineGag(Crawler): - """ 9gag image provider """ - - __uri = "" - __next = "" - __site = "" - - __RE_post_container = re.compile(r'badge-post-container') - - @staticmethod - def __build_uri(uri): - return uri - - def _restart_at_front(self): - self.__next = self.__uri - - def __init__(self, uri, site): - self.__site = site - self.__uri = self.__class__.__build_uri(uri) - self._restart_at_front() - - def _crawl(self): - uri = self.__next - self.__class__._log("debug", "%s crawls url: %s" % (self.__class__.__name__, uri)) - - (page, base, _) = self.__class__._fetch_remote_html(uri) - if not page: - self.__class__._log("debug", "%s crawled EMPTY url: %s" % (self.__class__.__name__, uri)) - return - - # get more content ("scroll down") - # to know what page to parse next - # update new last URI when we're not on first run - _more = page.find("div", {"class": "loading"}) - _next = None - if _more: - _more = _more.find("a", {"class": "btn badge-load-more-post", "href": True}) - if _more: - _next = urljoin(base, _more["href"]) - if _next: - self.__next = _next - else: - self.__class__._log("debug", "%s found no `next` on url: %s" % (self.__class__.__name__, uri)) - - - # for every found imageContainer - # add img-src to map if not blacklisted - images_added = 0 - for con in page.find_all("div", {"class": self.__class__.__RE_post_container}): - image = None - - image_inline = con.find("div", {"class": "badge-animated-container-animated", "data-image": True}) - if image_inline: - image = image_inline['data-image'] - - image_src = con.find('img', {"class": "badge-item-img", "src": True}) - if image_src: - image = image_src['src'] - - if image: - if self._add_image(urljoin(base, image), self.__site): - images_added += 1 - - if not images_added: - self.__class__._log("debug", "%s found no images on url: %s" % (self.__class__.__name__, uri)) diff --git a/crawler/pr0gramm.py b/crawler/pr0gramm.py deleted file mode 100644 index 03387588..00000000 --- a/crawler/pr0gramm.py +++ /dev/null @@ -1,105 +0,0 @@ - -import re, json - -try: - from urllib.parse import urljoin # py3 -except ImportError: - from urlparse import urljoin # py2 - -from . import Crawler, CrawlerError - - -class Pr0gramm(Crawler): - """ pr0gramm.com image provider""" - - ## class constants - - ## properties - - __uri = "" - __next = "" - __site = "" - __image_base_url = "https://img.pr0gramm.com/" - __api_base_url = "" - - __filter = re.compile(r'^/static/[\d]+') - __filterNextPage = "" - - ## functions - - """ - JSON From API - { - atEnd: - atStart: - error: ? - items: [ - ... - { - id: - promoted: - up: - down: - created: - image: z.B.: 2016/02/18/047b2e356d059074.jpg - thumb: - fullsize: - source: - flags: - user: - mark: - } - ... - ] - ts: - cache: - rt: - qc: - } - """ - - @staticmethod - def __build_uri(uri): - return uri - - def _restart_at_front(self): - self.__next = self.__uri - - def __init__(self, uri, site): - self.__site = site - self.__uri = self.__class__.__build_uri(uri) - self.__api_base_url = self.__uri - self.__filterNextPage = re.compile(r'^/static/' + re.escape(self.__site) + r'/[\d]+') - self._restart_at_front() - - def _crawl(self): - uri = self.__next - self.__class__._log("debug", "%s crawls url: %s" % (self.__class__.__name__, uri)) - - - (remote, uri) = self.__class__._fetch_remote(uri) - if not remote: - self.__class__._log("debug", "%s crawled EMPTY url: %s" % (self.__class__.__name__, uri)) - return - - data = json.loads(remote) - - last_id = 0 - images_added = 0 - for item in data["items"]: - - if item["promoted"] == 0: - continue - - if item["image"].lower().endswith(".webm"): - continue - - last_id = item["id"] - - if self._add_image(urljoin(self.__image_base_url, item["image"]), self.__site): - images_added += 1 - - - self.__next = self.__api_base_url + "?older=" + str(last_id) - - self.__class__._log("debug", "%s added %s images on url: %s" % (self.__class__.__name__, images_added, uri)) diff --git a/crawler/reddit.py b/crawler/reddit.py deleted file mode 100644 index 596495f6..00000000 --- a/crawler/reddit.py +++ /dev/null @@ -1,55 +0,0 @@ - - -try: - from urllib.parse import urljoin # py3 -except ImportError: - from urlparse import urljoin # py2 - -import json - -from . import Crawler, CrawlerError - - -class Reddit(Crawler): - """ class def: a crawler for Reddit image threads """ - - __uri = "" - __next = "" - __site = "" - - @staticmethod - def __build_uri(uri): - return uri + ".json" - - def _restart_at_front(self): - self.__next = "" - - def __init__(self, uri, site): - self.__site = site - self.__uri = self.__class__.__build_uri(uri) - self._restart_at_front() - - def _crawl(self): - uri = urljoin(self.__uri, "?after="+self.__next) - self.__class__._log("debug", "%s crawls url: %s" % (self.__class__.__name__, uri)) - - (remote, uri) = self.__class__._fetch_remote(uri) - if not remote: - self.__class__._log("debug", "%s crawled EMPTY url: %s" % (self.__class__.__name__, uri)) - return - - data = json.loads(remote) - - self.__next = data['data']['after'] - - images_added = 0 - for child in data['data']['children']: - image = child['data']['url'] - if image: - thread_uri = urljoin(self.__uri, child['data']['permalink']) - self.__class__._log("debug", thread_uri) - if self._add_image(image, self.__site, thread_uri): - images_added += 1 - - if not images_added: - self.__class__._log("debug", "%s found no images on url: %s" % (self.__class__.__name__, uri)) diff --git a/crawler/soupio.py b/crawler/soupio.py deleted file mode 100644 index 2d15e2eb..00000000 --- a/crawler/soupio.py +++ /dev/null @@ -1,63 +0,0 @@ - - -try: - from urllib.parse import urljoin # py3 -except ImportError: - from urlparse import urljoin # py2 - -from . import Crawler, CrawlerError - - -class SoupIO(Crawler): - """ soup.io image provider """ - - __uri = "" - __next = "" - __site = "" - - @staticmethod - def __build_uri(uri): - return urljoin(uri, "?type=image") - - def _restart_at_front(self): - self.__next = self.__uri - - def __init__(self, uri, site): - self.__site = site - self.__uri = self.__class__.__build_uri(uri) - self._restart_at_front() - - def _crawl(self): - uri = urljoin(self.__uri, self.__next) - self.__class__._log("debug", "%s crawls url: %s" % (self.__class__.__name__, uri)) - - (page, base, _) = self.__class__._fetch_remote_html(uri) - if not page: - self.__class__._log("debug", "%s crawled EMPTY url: %s" % (self.__class__.__name__, uri)) - return - - # get more content ("scroll down") - # to know what page to parse next - # update new last URI when we're not on first run - _next = None - _more = page.find("div", {"id": "more_loading"}) - if _more: - _more = _more.find("a", {"href": True}) - if _more: - _next = urljoin(base, _more["href"]) - if _next: - self.__next = _next - else: - self.__class__._log("debug", "%s found no `next` on url: %s" % (self.__class__.__name__, uri)) - - # for every found imageContainer - # add img-src to map if not blacklisted - images_added = 0 - for con in page.find_all("div", {"class": "imagecontainer"}): - image = con.find('img', {"src": True}) - if image: - if self._add_image(urljoin(base, image['src']), self.__site): - images_added += 1 - - if not images_added: - self.__class__._log("debug", "%s found no images on url: %s" % (self.__class__.__name__, uri)) diff --git a/nichtparasoup.py b/nichtparasoup.py deleted file mode 100755 index 39d06804..00000000 --- a/nichtparasoup.py +++ /dev/null @@ -1,402 +0,0 @@ -#!/usr/bin/env python - -### import libraries -from crawler.giphy import Giphy -from crawler.fourchan import Fourchan -from crawler.instagram import Instagram -from crawler.ninegag import NineGag -from crawler.pr0gramm import Pr0gramm -from crawler.soupio import SoupIO -from crawler.reddit import Reddit -from os import path -import math -import random -import logging -import logging.handlers -import time -import threading -import argparse -import json - -try: - from configparser import RawConfigParser # py3 -except ImportError: - from ConfigParser import RawConfigParser # py2 - -try: - from urllib.parse import quote_plus as url_quote_plus # py3 -except ImportError: - from urllib import quote_plus as url_quote_plus # py2 - -from werkzeug.wrappers import Request, Response -from werkzeug.routing import Map, Rule -from werkzeug.exceptions import HTTPException, NotFound -from werkzeug.serving import run_simple - - -## import templates -import templates as tmpl - -## import crawler -from crawler import Crawler - -_file_path = path.dirname(path.realpath(__file__)) - -# argument parser -arg_parser = argparse.ArgumentParser() -arg_parser.add_argument('-c', '--config-file', metavar='', - type=argparse.FileType('r'), - required=True, - help='a file path to the config ini', - dest="config_file") -args = arg_parser.parse_args() - -# configuration -# init config parser -config = RawConfigParser() - -# read defaults -config.read(path.join(_file_path, 'config.defaults.ini')) -try: - config.read_file(args.config_file) # py3 -except AttributeError: - config.readfp(args.config_file) # py2 -args.config_file.close() - -# get actual config items -nps_port = config.getint("General", "Port") -nps_bindip = config.get("General", "IP") - -min_cache_imgs_before_refill = config.getint("Cache", "Images_min_limit") -user_agent = config.get("General", "Useragent") - -# crawler logging config -logverbosity = config.get("Logging", "Verbosity") -logger = logging.getLogger("nichtparasoup") - -if config.get("Logging", "Destination").lower() == 'syslog': - hdlr = logging.handlers.SysLogHandler() -else: - hdlr = logging.FileHandler(config.get("Logging", "File")) - hdlr.setFormatter(logging.Formatter( - '%(asctime)s %(levelname)s %(message)s')) - -logger.addHandler(hdlr) -logger.setLevel(logverbosity.upper()) - -# werkzeug logging config -log = logging.getLogger('werkzeug') -log.setLevel(logging.CRITICAL) - -try: - urlpath = config.get("General", "Urlpath") -except: - urlpath = '' - -factor_separator = "*" -call_flush_timeout = 10 # value in seconds -call_flush_last = time.time() - call_flush_timeout - -call_reset_timeout = 10 # value in seconds -call_reset_last = time.time() - call_reset_timeout - -Crawler.request_headers({'User-Agent': user_agent}) -Crawler.set_logger(logger) - -# config the crawlers - - -def get_crawlers(configuration, section): - """ - parse the config section for crawlers - * does recognize (by name) known and implemented crawlers only - * a robust config reading and more freedom for users - - :param configuration: RawConfigParser - :param section: string - :return: crawler, factors - """ - crawlers = {} - factors = {} - - for crawler_class in Crawler.__subclasses__(): - crawler_class_name = crawler_class.__name__ - if not configuration.has_option(section, crawler_class_name): - continue # skip crawler if not configured - - crawler_config = configuration.get(section, crawler_class_name) - if not crawler_config or crawler_config.lower() == "false": - continue # skip crawler if not configured or disabled - - crawler_uris = {} - - # mimic old behaviours for bool values - if crawler_config.lower() == "true": - if crawler_class == SoupIO: - crawler_config = "everyone" - - crawler_sites_and_factors = [site_stripped for site_stripped in [site.strip() for site in crawler_config.split(",")] # trim sites - if site_stripped] # filter stripped list for valid values - - if not crawler_sites_and_factors: - continue # skip crawler if no valid sites configured - - crawler_sites = [] - factors[crawler_class_name] = {} - - # Separate Site and Factor - for factorPair in crawler_sites_and_factors: - if factor_separator not in factorPair: - # No Factor configured - crawler_sites.append(url_quote_plus(factorPair)) - continue - - factorPair_parts = [factorPairPart.strip( - ) for factorPairPart in factorPair.split(factor_separator)] - - if not factorPair_parts or not len(factorPair_parts) == 2: - continue - - site = url_quote_plus(factorPair_parts[0]) - factor = float(factorPair_parts[1]) - - crawler_sites.append(site) - - if site not in factors[crawler_class_name] and 0 < factor <= 10: - factors[crawler_class_name][site] = factor - - logger.info("found configured Crawler: %s = %s Factors: %s" % ( - crawler_class_name, repr(crawler_sites), repr(factors[crawler_class_name]))) - - if crawler_class == Reddit: - crawler_uris = {site: "https://www.reddit.com/r/%s" % - site for site in crawler_sites} - elif crawler_class == NineGag: - crawler_uris = {site: "https://9gag.com/%s" % - site for site in crawler_sites} - elif crawler_class == Pr0gramm: - crawler_uris = {crawler_sites[0] : "https://pr0gramm.com/api/items/get"} - elif crawler_class == SoupIO: - crawler_uris = {site: ("http://www.soup.io/%s" if site in ["everyone"] # public site - else "http://%s.soup.io") % site # user site - for site in crawler_sites} - elif crawler_class == Instagram: - crawler_uris = {site: "https://instagram.com/%s" % - site for site in crawler_sites} - elif crawler_class == Fourchan: - crawler_uris = {site: "https://boards.4chan.org/%s/" % - site for site in crawler_sites} - elif crawler_class == Giphy: - crawler_uris = { - site: "https://api.giphy.com/v1/gifs/search?q=%s" % site for site in crawler_sites} - - if crawler_class_name not in crawlers: - crawlers[crawler_class_name] = {} - - crawlers[crawler_class_name] = {site: crawler_class( - crawler_uris[site], site) for site in crawler_uris} - - return crawlers, factors - - -(sources, factors) = get_crawlers(config, "Sites") -if not sources: - raise Exception("no sources configured") -if factors: - Crawler.set_factors(factors) - - -# wrapper function for cache filling -def cache_fill_loop(): - global sources - while True: # fill cache up to min_cache_imgs per site - - info = Crawler.info() - for crawler in sources: - for site in sources[crawler]: - key = crawler + "_" + site - - if key not in info["images_per_site"] or info["images_per_site"][key] < min_cache_imgs_before_refill: - try: - sources[crawler][site].crawl() - info = Crawler.info() - except Exception as e: - logger.error("Error in crawler %s - %s: %s" % - (crawler, site, e)) - break - - # sleep for non-invasive threading ;) - time.sleep(1.337) - - -# return image data from map -def cache_get(): - return Crawler.get_image() - -# get status of cache - - -def cache_status_dict(): - info = Crawler.info() - return { - "crawler": Crawler.info(), - "factors": factors, - "min_cache_imgs_before_refill": min_cache_imgs_before_refill, - } - -# print status of cache - - -def cache_status_text(): - status = cache_status_dict() - info = status['crawler'] - - bar_reps = 5 - bar_repr_refill = status['min_cache_imgs_before_refill'] / bar_reps - - msg = "images cached: %d (%d bytes) - already crawled: %d (%d bytes)" % \ - (info["images"], info["images_size"], - info["blacklist"], info["blacklist_size"]) - logger.info(msg) - - for crawler in sources: - for site in sources[crawler]: - key = crawler + "_" + site - if key in info["images_per_site"]: - - factor = 1 - if crawler in factors and site in factors[crawler]: - factor = factors[crawler][site] - - count = info["images_per_site"][key] - - bar = "|" - for i in range(0, int(math.ceil(count / bar_reps))): - if i < bar_repr_refill: - bar += "#" - else: - bar += "*" - - sitestats = ("%15s - %-15s with factor %4.1f: %2d Images " + - bar) % (crawler, site, factor, count) - logger.info(sitestats) - msg += "\r\n" + sitestats - return msg - -# print imagelist - - -def show_imagelist(): - return "\n".join(Crawler.show_imagelist()) - - -# print blacklist -def show_blacklist(): - return "\n".join(Crawler.show_blacklist()) - - -# flush blacklist -def flush(): - global call_flush_last - time_since_last_call = time.time() - call_flush_last - if time_since_last_call >= call_flush_timeout: - Crawler.flush() - call_flush_last = time.time() - time_since_last_call = 0 - return "%i000" % (call_flush_timeout - time_since_last_call) - - -# reset the crawler -def reset(): - global call_reset_last - time_since_last_call = time.time() - call_reset_last - if time_since_last_call >= call_reset_timeout: - Crawler.reset() - call_reset_last = time.time() - time_since_last_call = 0 - return "%i000" % (call_reset_timeout - time_since_last_call) - - -# werkzeug webserver -# class with mapping to cache_* functions above -class NichtParasoup(object): - # init webserver with routing - def __init__(self): - self.url_map = Map([ - Rule(urlpath + '/', endpoint='root'), - Rule(urlpath + '/status', endpoint='cache_status'), - Rule(urlpath + '/get', endpoint='cache_get'), - Rule(urlpath + '/imagelist', endpoint='show_imagelist'), - Rule(urlpath + '/blacklist', endpoint='show_blacklist'), - Rule(urlpath + '/flush', endpoint='flush'), - Rule(urlpath + '/reset', endpoint='reset'), - ]) - - # proxy call to the wsgi_app - def __call__(self, environ, start_response): - return self.wsgi_app(environ, start_response) - - # calculate the request and use the defined map to route - def dispatch_request(self, request): - adapter = self.url_map.bind_to_environ(request.environ) - try: - endpoint, values = adapter.match() - return getattr(self, 'on_' + endpoint)(request, **values) - except HTTPException as e: - return e - - # the wsgi app itself - def wsgi_app(self, environ, start_response): - request = Request(environ) - response = self.dispatch_request(request) - return response(environ, start_response) - - # start page with js and scroll - def on_root(self, request): - return Response(tmpl.root, mimetype='text/html') - - # map function for print the status - def on_cache_status(self, request): - if request.values.get('t') == 'json': - return Response(json.dumps(cache_status_dict()), mimetype='application/json') - return Response(cache_status_text()) - - # map function for getting an image url - def on_cache_get(self, request): - return Response(json.dumps(cache_get()), mimetype='application/json') - - # map function for showing blacklist - def on_show_blacklist(self, request): - return Response(show_blacklist()) - - # map function for showing imagelist - def on_show_imagelist(self, request): - return Response(show_imagelist()) - - # map function for flushing (deleting everything in cache) - def on_flush(self, request): - return Response(flush()) - - # map function for resetting (deleting everything in cache and blacklist) - def on_reset(self, request): - return Response(reset()) - - -# runtime -# main function how to run -# on start-up, fill the cache and get up the webserver -if __name__ == "__main__": - try: - # start the cache filler tread - cache_fill_thread = threading.Thread(target=cache_fill_loop) - cache_fill_thread.daemon = True - cache_fill_thread.start() - except (KeyboardInterrupt, SystemExit): - # end the cache filler thread properly - min_cache_imgs_before_refill - 1 - - # give the cache_fill some time in advance - time.sleep(1.337) - - # start webserver after a bit of delay - run_simple(nps_bindip, nps_port, NichtParasoup(), use_debugger=False) diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 6b7496c4..00000000 --- a/requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -### the webserver toolkit -Werkzeug - -### basic HTML/XML toolkit -beautifulsoup4 - -### advanced html/xml parser & tree builders -html5lib -lxml diff --git a/templates.py b/templates.py deleted file mode 100644 index ca49d09e..00000000 --- a/templates.py +++ /dev/null @@ -1,374 +0,0 @@ - -root = """ - -nichtparasoup - Hackspace Entertainment System - - - - -
- -  - -
- 
esc
boss mode
-
space
play/pause
+
faster -
-
slower
j
next image
-
k
next-to-last image
hot keys
-""" diff --git a/templates_raw/README.md b/templates_raw/README.md deleted file mode 100644 index 0dc89ebe..00000000 --- a/templates_raw/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# template builder for UI - -This is just the template builder - not needed to run __nichtparasoup__. - -## idea - -To have proper IDE support when it comes to writing the UI, the thing should be written in several raw files and be -yuiCompressed and put together at the end. -Furthermore it would be great to be able to develop the UI without running __nichtparasoup__. - -## requirements - -```sh -git submodule update --init --recursive -``` - -for further information: see the `README` of the submodules - -## how to use - -just run the `build.sh` so the `templates.py` of __nichtparasoup__ will be built automatically - -## attention: symlinks - -some files are symlinked - so be sure your environment supports symlinks, otherwise the outcome may be broken. -this is the case on most windows systems. diff --git a/templates_raw/_bundler b/templates_raw/_bundler deleted file mode 160000 index 5acc19fa..00000000 --- a/templates_raw/_bundler +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5acc19fa3c2803be416a7941acb6f9fa65aa81ee diff --git a/templates_raw/_foreign/normalize.css b/templates_raw/_foreign/normalize.css deleted file mode 160000 index d8313280..00000000 --- a/templates_raw/_foreign/normalize.css +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d83132802e8bc10ece0c0afbe88fce83674ec252 diff --git a/templates_raw/build.sh b/templates_raw/build.sh deleted file mode 100755 index aabd2f2c..00000000 --- a/templates_raw/build.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/sh - - -oldDir=$(pwd) - -cd $(dirname $0) - - -###################### - -target="../templates.py" - -###################### - -printf '\n' > $target - -for builder in */build.sh -do - echo "running $builder ... " - - printf $(basename $(dirname $builder)) >> $target - printf ' = """' >> $target - - $builder 1>> $target - - printf '"""' >> $target - -done - -printf '\n' >> $target - -##################### - -cd $oldDir diff --git a/templates_raw/root/.editorconfig b/templates_raw/root/.editorconfig deleted file mode 100644 index f059efd6..00000000 --- a/templates_raw/root/.editorconfig +++ /dev/null @@ -1,36 +0,0 @@ -root = true - -[*] -end_of_line = LF - -[flush,reset,get,get_local,get_remote] -end_of_line = LF -trim_trailing_whitespace = false -insert_final_newline = false - -[*.html] -indent_style = space -indent_size = 2 -tab_width = 2 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.css] -indent_style = tab -indent_size = 2 -tab_width = 2 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.js] -indent_style = tab -indent_size = 2 -tab_width = 2 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.md] -indent_style = space -indent_size = 3 -trim_trailing_whitespace = false -insert_final_newline = true diff --git a/templates_raw/root/README.md b/templates_raw/root/README.md deleted file mode 100644 index b0e24219..00000000 --- a/templates_raw/root/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# ROOT LAYOUT dev env - -~ this is a local runnable UI builder environment that needs no web server and no internet connection (just `file://`). -Ideal for development on long train rides ;-) - -~ since this development may require XHttpRequests (= AJAX) you want to use a web browser that supports such requests via `file://` - like FireFox - or start a local web server via -`python -m SimpleHTTPServer `. - -## how to develop - -The file `root.html` is the main UI file for _nichtparasoup_. -It may include any known HTML, JS and CSS you need. - -The UI file(s) will be bundled. For details see the `build.sh`. -Therefore some specialties are needed to know: - -* the HTML tag `striponbuild` will be removed on build and may be used for debug/develop purposes -* each line of CSS or JS that contains the string `@striponbuild` is stripped on build, so this may be used for development purposes also - -/!\ attention: -the `normalize.css` is symlinked. symlinks may not work properly on windows systems. - -## how to test - -The ready made UI will normally do a AJAX request to `./get` which should return the URI of an image. -For testing purposes and local dev, a file `test/get_local` was prepared which will respond a locally stored image. Since this Image will have the same Size every time, there is also a file `test/get_remote` which used to respond a random (resolution/content) image. I guess you know how to handle them. diff --git a/templates_raw/root/build.sh b/templates_raw/root/build.sh deleted file mode 100755 index bd858395..00000000 --- a/templates_raw/root/build.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -dir=$(dirname $0) - -bundler="$dir/../_bundler//bundler.py --compress --strip-comments \ ---strip-tags striponbuild \ ---strip-markers @striponbuild" - -$bundler -i $dir/root.html - diff --git a/templates_raw/root/css/feel.css b/templates_raw/root/css/feel.css deleted file mode 100644 index ec21b5e0..00000000 --- a/templates_raw/root/css/feel.css +++ /dev/null @@ -1,11 +0,0 @@ -/* remember for development: lines that include the string "@stripOnBuild" will be stripped on build ;-) */ - -input[type="range"] { - position: relative; - top: 5px; - direction: rtl; -} - - - - diff --git a/templates_raw/root/css/look.css b/templates_raw/root/css/look.css deleted file mode 100644 index b612160b..00000000 --- a/templates_raw/root/css/look.css +++ /dev/null @@ -1,200 +0,0 @@ -/* remember for development: lines that include the string "@striponbuild" will be stripped on build ;-) */ - -* { cursor: default; } -a { cursor: pointer; } - -html { - direction: ltr; - text-align: left; - overflow: scroll; - overflow-x: hidden; -} - - -html, body { - color: #ccc; - background-color: black; -} - -header { - z-index: 99999; - display: block; - position: fixed; - top: 0ex; - left: 0em; - right: 0em; - height: 5ex; - line-height: 5ex; - text-align: left; - padding: 0ex 1em; - color: #ccc; - background-color: transparent; - cursor: default; - white-space: nowrap; - background-color: rgba(255, 255, 0, 0.5); /* @stripOnBuild */ -} - -header #burger { - color: #eee; - position: fixed; - top: inherit; - right: inherit; - margin-right: 1em; - text-shadow: - -1px -1px 0px black, - 1px -1px 0px black, - -1px 1px 0px black, - 1px 1px 0px black; -} - -header #controls { - text-align: center; - padding-left: 3em; - padding-right: 3em; - position: absolute; - top: -7ex; /* neg. height */ - left: 0em; - right: 0em; - background-color: #111; - background-color: rgba(23, 23, 23, 0.9); - border-bottom: 1px solid #808080; - transition: all 0.6s ease-in-out 0s; -} - -header #controls #serverControls { - margin: 0ex 2em; -} - -header #controls #serverControls button { - margin-right: 0.5em; -} - -header #controls label { - text-align: left; - margin-right: 1em; - margin-left: 1em; -} - -header:hover { - color: white; -} - -header:hover #burger { - font-weight: bold; -} - -header:hover #controls , -#controls.forceShow { - top: 0ex; -} - -#controls.forceShow { - transition: none; -} - -footer { - z-index: 99999; - text-align: center; - position: fixed; - right: 0em; - bottom: 0ex; - left: 0em; - background-color: transparent; - color: #ccc; - font-size: smaller; - height: 4ex; - background-color: rgba(255, 255, 0, 0.5); /* @stripOnBuild */ -} - -#wall article { - position: relative; - margin: 1ex 1ex; - border: 1px solid #999; - display: inline; - display: inline-block; - float: left; -} - -#wall article img { - display: none; -} - -#wall article { - animation: scaleIn linear 0.5s; - -webkit-animation: scaleIn linear 0.5s; - -moz-animation: scaleIn linear 0.5s; - -o-animation: scaleIn linear 0.5s; - -ms-animation: scaleIn linear 0.5s; - - transform-origin: top left; - -webkit-transform-origin: top left; - -moz-transform-origin: top left; - -o-transform-origin: top left; - -ms-transform-origin: top left; -} - - -@keyframes scaleIn { - from { transform: scale(0); } - to { transform: scale(1); } -} - -@-webkit-keyframes scaleIn { - from { -webkit-transform: scale(0); } - to { -webkit-transform: scale(1); } -} - -@-moz-keyframes scaleIn { - from { -moz-transform: scale(0); } - to { -moz-transform: scale(1); } -} - -@-o-keyframes scaleIn { - from { -o-transform: scale(0); } - to { -o-transform: scale(1); } -} - -@-ms-keyframes scaleIn { - from { -ms-transform: scale(0); } - to { -ms-transform: scale(1); } -} - -#wall article img { - display: block; /* needed for alignment of the image to its container box ... */ -} - -#wall article section[role="source"] { - display: block; - position: absolute; - bottom: 0ex; - right: 0em; - line-height: 2.5ex; - min-height: 2.5ex; - min-width: 2.5ex; - background-size: 2.5ex 2.5ex; - background-repeat: no-repeat; - background-position: right bottom; - background-color: transparent; - color: white; - opacity: 0.5; - text-align: initial; /* fall back to native */ -} - -#wall article section[role="source"] a { - display: none; - word-wrap: break-word; - text-decoration: none; - color: inherit; - margin: 0.1ex 0.1em; -} - -#wall article section[role="source"]:hover { - opacity: 1; - background-color: #606060; - background-color: rgba(100, 100, 100, 0.7); - left: 0em; -} - -#wall article section[role="source"]:hover a { - display: block; -} diff --git a/templates_raw/root/css/look_bossMode.css b/templates_raw/root/css/look_bossMode.css deleted file mode 100644 index f20bfd6a..00000000 --- a/templates_raw/root/css/look_bossMode.css +++ /dev/null @@ -1,7 +0,0 @@ -/* remember for development: lines that include the string "@stripOnBuild" will be stripped on build ;-) */ - -/* @TODO write a good looking bos mode that is not totally suspicious */ - -html.boss * { - visibility: hidden; -} diff --git a/templates_raw/root/css/look_buttons.css b/templates_raw/root/css/look_buttons.css deleted file mode 100644 index 784cfc79..00000000 --- a/templates_raw/root/css/look_buttons.css +++ /dev/null @@ -1,43 +0,0 @@ -button , -.button { - min-height: 2ex; - min-width: 2.2ex; - vertical-align: middle; - background-color: #111; - color: #eee; - border: 1px solid #ccc; - border-radius: 0.5ex; - - display: inline; - display: inline-block; - padding: 0.2ex 0.4ex; - - line-height: 2.8ex; -} - - -button:hover , button:active , button:focus , -a.button:hover , a.button:active , a.button:focus , -label.button:hover , label.button:active , label.button:focus { - outline: 0 -} - - -button:hover , button:active -a.button:hover , a.button:active -label.button:hover , label.button:active { - /* invert colors */ - background-color: #ccc; - color: #111; - cursor: pointer; -} - - - -button[disabled] , -.button[disabled] { - opacity: 0.4; - filter: alpha(opacity=40); /* For IE8 and earlier */ - - cursor: default !important; -} diff --git a/templates_raw/root/css/look_hotkeys.css b/templates_raw/root/css/look_hotkeys.css deleted file mode 100644 index 8541f973..00000000 --- a/templates_raw/root/css/look_hotkeys.css +++ /dev/null @@ -1,60 +0,0 @@ - -/* remember for development: lines that include the string "@stripOnBuild" will be stripped on build ;-) */ - -#keys { - position: absolute; - right: 1em; - bottom: 1.2ex; -} - -#hotkeys { - padding: 0.5ex 1em; - line-height: 3ex; - - position: absolute; - left: 0em; - bottom: -5ex; /* neg. height */ - right: 0em; - background-color: #111; - background-color: rgba(23, 23, 23, 0.9); - border-top: 1px solid #808080; - transition: all 0.6s ease-in-out 0s; - - display: inline; - display: inline-block; - margin: 0ex 0em; - list-style: none; -} - -footer:hover #hotkeys , -#hotkeys.forceShow { - bottom: 0ex; -} - -#hotkeys.forceShow { - transition: none; -} - -#hotkeys dd , -#hotkeys dt , -.hotkey { - display: inline; - display: inline-block; - padding: 0.2ex 0.4ex; - margin: 0ex 0em; -} - -#hotkeys dd.active { - background-color: white; - color: black; -} - -#hotkeys dt { - margin-right: 1em; -} - - - - - - diff --git a/templates_raw/root/css/look_stateSwitch.css b/templates_raw/root/css/look_stateSwitch.css deleted file mode 100644 index 2eba86b1..00000000 --- a/templates_raw/root/css/look_stateSwitch.css +++ /dev/null @@ -1,24 +0,0 @@ - -/* remember for development: lines that include the string "@stripOnBuild" will be stripped on build ;-) */ - -.stateSwitchContainer > input { - display: none; -} - -.stateSwitchContainer:after { - display: inline-block; - width: 1.5em; -} - -.stateSwitchContainer.state_0:after { - content: 'off'; -} - -.stateSwitchContainer.state_1:after { - content: 'on'; -} - -.stateSwitchContainer { - cursor: pointer; -} - diff --git a/templates_raw/root/css/normalize.css b/templates_raw/root/css/normalize.css deleted file mode 120000 index 976d81f2..00000000 --- a/templates_raw/root/css/normalize.css +++ /dev/null @@ -1 +0,0 @@ -../../_foreign/normalize.css/normalize.css \ No newline at end of file diff --git a/templates_raw/root/css/sourceIcons.css b/templates_raw/root/css/sourceIcons.css deleted file mode 100644 index 64ece209..00000000 --- a/templates_raw/root/css/sourceIcons.css +++ /dev/null @@ -1,43 +0,0 @@ -/* here are the source favicons defined */ - - -/* resolution is as given, no transformation needed */ -/* used http://www.base64-image.de/ */ - -/* naming conventions: lowercase the names of the crawler classes */ - -#wall article section[role="source"] { /* fallback */ - background-image: url(data:image/png;base64,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); -} - -#wall article section[role="source"][data-crawler='pr0gramm'] { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAGcUlEQVR42u2d229UVRTG5x/gjXfSekONxruRqYAjYIqoBARBG5AoCqEmKCISAzrerUFQEZQoRVAeUCJGIWKUiDZixEA702mn7XSmnd7vl+llOrflWQckTVCknXXaWXO+lXwvfThp9vebfc7ea629HY5RUeKcOsXjzHV7nDmlnrzciCGCskIR01PDW/bY8W9RNmOaq8yZU4fBym6xx+z1ReYbhKQwQDaR4fUFCHhKwC/fnjOB+TowaHgVA2LXmSDX7Tj/wYfBsOeroNSBr317rw4cGAR7CwAAAAwCAIAAAAQAIAAAAQAIAEAAAAIAEACAAAAEACAAAAEACABAAAACABAAGIPqtxRS/6lfKN7dQclolIZqKqj1sx1UuSgPBmQ1ALOupu6jX9F/RaK/j4IbVsGEbAWgedfb9H+RSiSo4Z3NMCLbAKh46E5KDg/R5Ubbvg9hRjYBUL9lHY01uo99bb42YEoWAMAfeeOJ/j9/o/K5N8AY7QDwlD7eGKqpxApB/yugkNKJWGc71axeCIO0AuBf5qJ0IxkdNkGCSUqXgX2//pg2BKlUilo+eRdGaQSg6tE5lIrHSSJ4QwkrBIVbwc073ySpiJw5Rb78m2CatlxA+LXnjJkgJgJBtD5I/qWzYZy2bGBwfQElBvpFIIj3dlNg7VKYpy0dXFVwH420NIpAkByJUti9HgZqqweoeOB2Giw/K/ZdwDuOMFFZQYjXNZ16TxwVg6D7+BHy3jMdZmqrCGr7/CMxCAbKTpNv/i0wVFtJWPj15ykZG5FZITSEyL/cBVO11QTWFi6jeF+PCASJ/l7jecthrLaiUM4dRMNBEQh4zyH8xkaYq60q2Jd/s7Hb94fYd0Hbvp0wWFtZuHf2NZcsJB1r8GrD67oWRmvrC2j5uMjMBErEYGWZWaMIs5U1hnBdIdcESESsrZmqVy2A4do6g7gqKNbVIbN9PDRIoReehOnaWsMqF+fRcK1fZoWQTFLT9ldgvLbewPJ5N5rtZVLRcWgvee6+AgCoag6deSV1Ht4vt0I4+QNWCBq7g5t2uM2WMpEVQvkZ8t1/KwDQptDGJygxOCBUZVRLFQ/eAQC0qXrlfHN5JxHDAb9tU8qqD4jgDZ4hv0emuKT4AwCgUd57r6O+kp9Fag3tWHqeHUfE8Arhmy/ShiDw9GIAoFnpdiPx9jMA0ChjU6dt/660Z4DghscBgDbxLqHENwBvE3PVMgBQJK4BHA7ViKwC+n4/gVWAJvEJY4mITLcRl5FVr8wHAFrUsrvInLKlws4ZQlUAcPKm56fvxIznX37jtq3IBWhQ5SKnsevnFTOfC03QZKoEgMCah8Uqg85lAc9SxcK7UA+gAYCGtzaJdQ9xdH1/yKw+hvmZDsCsq4zKnWLB932cmt57GaZrAMBsEjldImZ+vKcLLWNaAOCDJKKNdWLm8/H0XFgKsxUAENq02qj0iQh2Bh0j75zrYbQGAPg8QKluIH5O66fbYbAGAKQ3d7hWsG7zGpirAQDpzZ2RpjBVrciHsRoACKxdYt4jJHqQpM1LvNUAIL2503n4gLlvAEMzHQDDJMnuHjOZU/QSjNQAgG/BbaIngMS7Oymw7hGRJpP2g3syUm0HdlPVY3P1A8C9+SOtTXKbO9U+sc2dsVxuNRnRe/K4bgDqtz4jOsjmsS9GL4DU/5fpwfco6QSAK3UFD4M0L4/Ys038/wQAFj2469uDgps7EQq9+JQl/ycAsKhaV6pmjxNDnCCyagAAgAUP5UsgRTZ3jJQwp4atHAAAYMFDY+0tAse4FJs9f1Z/pAIA6U4d4+bPtE7z4ssgJvCoVwAgneBZMnP8lbrGzDHRF0YCAAuWf+M54HHA89ekHNUCACx4KJs5pmTOkS8nrVJ30Fea0QBYfei1JQBww8XlVPfw+54zg5PdWs5dwZmoibgFxbKNID5z55Lve+OAJ1wQneXZwOCzK8zr4Eef68dJoda975vZQRhgk3Qw3xRWVTAPFzuhIggCABAAgAAABAAgAAABAAgAQAAAAgAQAIAAAAQAIAAAAQAIAEAAAAIAEACAAAAEACAAAAEACABAwgBEMBC2VcThceaUYiBsKsN7A4BcNwbDrgDkuh0lzqlTypw5dRgQe4k9Z+8dHGUzprmM6SCFgbHN1J9izx2jg/+AmcAev/yLzP8neEowvwnOfRhidZBFX/ump4a3F6b98/E374R7V338CnIAAAAASUVORK5CYII=); -} - -#wall article section[role="source"][data-crawler='reddit'] { - background-image: url(data:image/x-icon;base64,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); -} - -#wall article section[role="source"][data-crawler='imgur'] { - background-image: url(data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAABMLAAATCwAAAAAAAAAAAAArKysSKysroisrK+YrKyvtKysr7SsrK+0rKyvtKysr7SsrK+0rKyvtKysr7SsrK+0rKyvtKysr5isrK6IrKysSKysroysrK/8rKyv/Kysr/ysrK/8rKyv/Kysr/ysqKv8rKir/Kysr/ysrK/8rKyv/Kysr/ysrK/8rKyv/KysroysrK+wrKyv/Kysr/ysrK/8rKyv/KyYo/yskJ/8sKyv/LCor/yskJ/8rJyj/Kysr/ysrK/8rKyv/Kysr/ysrK+wrKyvuKysr/ysrK/8rKyv/KyUn/yxGPP8rfF3/KZNr/ymRav8rd1r/LD84/yslJ/8rKyv/Kysr/ysrK/8rKyvuKysr7SsrK/8rKyv/KyUn/yxgTf8qrnz/KLR//yixff8osX7/KLWA/yqqev8tVEX/KyUn/ysrK/8rKyv/Kysr7SsrK+0rKyv/KyUo/y1KP/8tsoD/LLF//yyuff8srn3/LK59/yyuff8ss4D/Lap7/yw9N/8rJyj/Kysr/ysrK+0rKyvtKysr/yslJ/8whmb/MbqG/zGxgf8xsYH/MbGB/zGxgf8xsYH/MbGB/zG7h/8wdVv/KyMm/ysrK/8rKyvtKysr7SspKv8sLi3/NqB4/ze5iP83tYb/N7WG/ze1hv83tYb/N7WG/ze1hv83u4r/NZNw/ysnKf8rKiv/Kysr7SsrK+0rKSr/LC4t/zykff8/vY7/P7mL/z+5i/8/uYv/P7mL/z+5i/8/uYv/QMCP/zqWc/8rJyj/Kyor/ysrK+0rKyvtKysr/yokJ/8+jG//SceX/0e9kf9HvZH/R72R/0e9kf9HvZH/R72Q/0nImP86e2L/KSMl/ysrK/8rKyvtKysr7SsrK/8qJSf/M01D/1DFmf9Rxpr/UMGX/1DCl/9Qwpf/UMGX/1HHm/9OvJP/Lz85/yonKP8rKyv/Kysr7SsrK+0rKyv/Kysr/ykjJv87aFf/WMed/1vQpP9ay6H/Wsyh/1vQpf9VwZj/N1lM/ykkJv8rKyv/Kysr/ysrK+0rKyvuKysr/ysrK/8rKyv/KSMl/zRKQv9JjHP/UqmJ/1Knh/9GhW7/MUI8/ykjJv8rKyv/Kysr/ysrK/8rKyvuKysr7CsrK/8rKyv/Kysr/ysrK/8pJSf/KCMl/yoqKv8qKSn/KCIl/yomJ/8rKyv/Kysr/ysrK/8rKyv/Kysr7CsrK6QrKyv/Kysr/ysrK/8rKyv/Kysr/ysrK/8rKir/Kyoq/ysrK/8rKyv/Kysr/ysrK/8rKyv/Kysr/ysrK6QrKysSKysroisrK+YrKyvtKysr7SsrK+0rKyvtKysr7SsrK+0rKyvtKysr7SsrK+0rKyvtKysr5isrK6IrKysSAAAgRgAAaWwAADYgAABkZQAAMjYAAHRlAABpbgAANSwAAGVuAABuZQAAdGUAAFBSAABFUwAAUl8AAFZFAAA2AA==); -} - -#wall article section[role="source"][data-crawler='soupio'] { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAATVJREFUeNpiZMACzsnqGgCpADThDUaPL19AV8uIReN8IDZgwA5ABiQiG8SIpDkBqpkYADJkAdwAqM3n0VWx8PEycOnqMHy7fIXhz6fP6NKGIJewQDkoNnNpazLI9DYxcGupw8W+XrvJcMsjFFkZSI8hI7rtIFu1Tu1hYObihGv8cvwsA4+lMboBYFewoIc2yMkwzSDwcetehrcLlzI8a/yMLSwCmAiFllRpFthFUvUVWOUxDPh09DjD+z2HUMRALhJPjmIQiYkgbAAIPEjKYXiQWcbw6807FHE2ZQWsBmxAFuCztmQQz81g+HbxMsPLvhkoir/sOYiufwMsHZyHpT5QFKpvX4lh0+OqNoY3S1agpEpgOjDEmpBAhgiE+MNVvp27mOHnk6dYExJ1kjJVMhO52RkgwABgs3aktxfFvgAAAABJRU5ErkJggg==); -} - -#wall article section[role="source"][data-crawler='ninegag'] { - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJcAAACXCAMAAAAvQTlLAAAAZlBMVEUAAAD///+zs7O2trZ5eXn8/Pz4+Pjg4OCcnJzd3d1hYWGoqKgsLCxlZWXExMTQ0NBtbW3o6OhbW1sbGxvx8fEICAiRkZExMTHKyspRUVGIiIgnJyeioqJycnJWVlY9PT1ISEgUFBR9OlkuAAADwUlEQVR4nO2b6ZKiMBCAA8gliAeHyDGu7/+SC+ooR9IdZ3NM1fb3Fym/mrSd7k6GMYIgCIIgCIIgCIIgCEIzW29rW4FD7gVO4LW2NRbcosAZCaKbbZUph6PzzfFgW+bFqXam1L8jzFIvcOYEXmpbipXV0mrEryyH2S7kWI2EO4tWX7XAamT/ZUvL5S3hNMysWEWw1d0sKk1bbWPUaiQ2G2anTMpqJDsZs7ol+BJOFtPNjViVG/8DqzuNgWx2EWUsiFB3zvgDZSyI/UWnVlL8UMtxikSbFXcrfBPUyPNKSzbDlnDYd7726EeUW3nwV8aPcvCApNtMcZhFSGBF34V9G8EfLDp1UrceDhx/vkGv6sQ5wUZVnkX+BKs9ENs7Vf3JwNA69pw3Jn0IB1X1D+QV8Uv5tjLg5Yq+wM/EfWzuCbdR3V5IQyYshnR7hbzQerMTZVnt6+hkV+FLqTgq9XsNv3m+2bkD3jHxe3SODeeNxkieSKAvGcJsGf5bpHRU5dU2SD2fTZNYi/Yk6vrKHGlh/e78/GQq0Vaq7Hexzix8hFkjU/2r7cN3yG4cD3WhXLOrej6ALJGfSTZwyucWN6S0kkTDPOXy0z5tiqvea0hOYMq058VKLJutWPYGeryGzc/9ZEJRbDaGvIbFlJ8zJWdmzmuo4OXGJ/dO1qQXYx0+q3huAma9WCuu4O8E3bNRNOyFZDPvVTEa92LlQbSY4WQQYd5roOKZzYtYK17suspmwasas+m1Oo7J/iye2/Iawuy9aXIOrKx5sdeArIg4z2x6sfPYzbln3iOrXkM2cwVDSsteQn6rV/9LvQ4LLzvnpWt2Zrz6jPurE3JdzsG0eN2HzZX8PZx0Pc3W4FVmj6ZD+rZLz2mflHu1k4lTLHPbhT/JV+01n7ZJXEMQtOdqvdZHUn4EnajkkajMVumVcociwDUE4GRNnZd4jLvnX0M4QSeRyrygMW7A21Xg8WKmSgtp+5t5mOUN/HG/UuTFLsj5cDhdTGwarfQG1gn5svr75Bo74lZ9/RBbnOf5MHaRQXAs+C+U2Ky8SZFA9DVdb91iYQY/jvXdIO3lBuE8uOdIysihczKI5LOa7QfITukn+LWJ+3ySV/jeHE1d5oOvISytNoasGLc4FtFpD6wZZ2SI+cD/sEVRQY9ks4EYvjCgiw0cZnozFkTbAYuZ2Lzs3tZ8M7+2/Z8e3GNcjVuhPKswM5mxINr5pmk1sOZcX5umD1zZscGzArKUsSCawimsZSyItDK/6RAEQRAEQRAEQRAEQfx3/AVIvysz/+uSlwAAAABJRU5ErkJggg=='); -} - -#wall article section[role="source"][data-crawler='instagram'] { - background-image: url('data:image/png;base64,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'); -} - -#wall article section[role="source"][data-crawler='fourchan'] { - background-image: url('data:image/png;base64,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'); -} - -#wall article section[role="source"][data-crawler='giphy'] { - background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADkAAAA5CAIAAAADehTSAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozQUREQ0NFRUE2OTQxMUUyQkYxQ0E0NzQyMzFDQUUxNSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozQUREQ0NFRkE2OTQxMUUyQkYxQ0E0NzQyMzFDQUUxNSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjNBRERDQ0VDQTY5NDExRTJCRjFDQTQ3NDIzMUNBRTE1IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjNBRERDQ0VEQTY5NDExRTJCRjFDQTQ3NDIzMUNBRTE1Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+hIGO9wAAAptJREFUeNrsWTtuFEEQrTdM5nATZ8hyQrAn4A5IRuwhiJG4AxGnIGMDI0IO4MCBA1IL5Axy24ktnmeqP9PeXaGpdjdopR57dzzt2ZrXr199uhaLxUL25Ohkf46GtWFtWBvWhrVh7eff+uvqAzldAhjeSeoZ1P+Ng/R36fh4vn13LsPfOrr8cnp7f18dq4OAAMED0XFgQDzCZTob8ZPRDydD/0YDmHBT3yI0pCihE/JM62u81t+bXFJtvHp2HjHnFnoE5niN7DoxeD4pDvVw3483Kzfj4epo/bkW1vcXv6EPHDB8ev1x/gd/rlYMKhonI+GnqgYyHpAoVyQ6HiprQNdXHnvPXOUg4kMUkxz0/TN0f4TXd3fl9QpkcsIkFIRp8/vJiTN3tF4X1oAPPnkqoOwIWkaDnZFUicHIHOkQsgNCBoFtjbpsgszioaR+5VRhmnWf81SjZHfK8eXhWxoNGXMBnf3MyLXD21BTA4AUOaCmaJl3lyXVInjHaA2LB1jqLE36pahdLl+IqxkgZ99q5FhSJCd1/SUXVvKtMl61NfcqvkUAxfRqt2HJW+oNBUOBT1yzzZl8qyBKn2pNqdBUEzrjLKMBF7Esxmx1lg9Zpdg1JsHOymsxoNFQPV5JKRNeGaJKjbxVuB6wG7LF1ydtDba0at0l9lYmGNfu6XHAuEz/rx7wYmUVXouGq5zaImMfW6Z8ydBSlxEByqC1LxHmf8d5/PVqWjtuJN6NlOGacFN1ltwTkpXuYBzgy1fPy/uWbw5w6lHRb0SSDZ/WONHL04X2HQa3Ew5eyhq5AAGHO0norgamkjIkUsqwA0S4xReW9P3Yar7F2Fbf4mljULvFgGsa+0aGxHYh84putO/kG9aGtWFtWBvWPcL6IMAAUeQiiCnMC5IAAAAASUVORK5CYII='); -} diff --git a/templates_raw/root/flush b/templates_raw/root/flush deleted file mode 100644 index 8da85a22..00000000 --- a/templates_raw/root/flush +++ /dev/null @@ -1 +0,0 @@ -3000 \ No newline at end of file diff --git a/templates_raw/root/get b/templates_raw/root/get deleted file mode 100644 index 29d80455..00000000 --- a/templates_raw/root/get +++ /dev/null @@ -1,7 +0,0 @@ -{ - "uri": "./test/testimg.png", - "crawler": "noCrawler", - "site": "get_local", - "source": "http://nicht.parasoup.de/demo/", - "more": {} -} \ No newline at end of file diff --git a/templates_raw/root/js/feel.js b/templates_raw/root/js/feel.js deleted file mode 100644 index 488131cd..00000000 --- a/templates_raw/root/js/feel.js +++ /dev/null @@ -1,485 +0,0 @@ -; /* remember for development: lines that include the string "@stripOnBuild" will be stripped on build ;-) */ - -(function(np, window, undefined) -{ "use strict"; - - var log = window.helperFuncs.log; - log("feel started"); // @stripOnBuild - - var addEvent = window.helperFuncs.addEvent - , fireEvent = window.helperFuncs.fireEvent - ; - - /* - * put everything public/protected in a single-use object called "np" - * every thing else is private ... - */ - - { // some helper variables and shortcuts - var document = window.document - , localStorage = window.helperFuncs.storageFactory() - ; - } - - { // some helper functions - var bitset = { // BitSet helper functions - "gen": function (bit) - { - var r = 1 << bit; - // log("BS gen", bit ,"->", r); // @stripOnBuild - return r; - } - , "check": function (int, bit) - { - var tar = this.gen(bit) - , r = ( (int & tar) == tar ); - // log("BS check", int, bit, " against:", tar, "->", r); // @stripOnBuild - return r; - } - , "set": function (int, bit) - { - var r = int | this.gen(bit); - // log("BS set", int, bit, "->", r); - return r; - } - , "unset": function (int, bit) - { - var r = this.check(int, bit) ? int ^ this.gen(bit) : int; - // log("BS unset", int, bit, "->", r); - return r; - } - }; - } - - np.constants = { // a bunch of public constants - stateBS : { // BitSet positions for states - init : 0 , // init play blocker - manual : 1 , // manual play/pause - boss : 2 , // boss mode - not implemented yet - presented : 3 , // tab is presented - active : 4 , // window is presented - scroll : 5 , // window is scrolled away from top - gallery : 6 // image is clicked for gallery-view - } - }; - - np.__bossMode_className = " boss"; - np.__bossMode_className_RE = new RegExp(np.__bossMode_className, "g"); - - np._imageTarget = undefined; - - np._imageFadeInTime = 1000; - - np._images = []; - np._imagesMax = 50; - - np._state = bitset.set(0, np.constants.stateBS.init); - - np._fetchRequest = new XMLHttpRequest(); - np._serverResetRequest = new XMLHttpRequest(); - np._serverFlushRequest = new XMLHttpRequest(); - - - np._options = { - interval : 10 - , playInBackground : false - }; - - np.__intervall = 0; - - np._setTimer = function(){ - - log("Timer refresh called"); // @stripOnBuild - - if(np.__intervall) - { - window.clearInterval(np.__intervall); - log("Timer Cleared"); // @stripOnBuild - } - - if(this._state == 0) { - np.__intervall = window.setInterval(function () { - np._fetch(); - }, np._options.interval * 1000); - log("Timer Set"); // @stripOnBuild - } - }; - - addEvent(np._fetchRequest, "readystatechange", function () - { - var req = this, imageData = undefined; - log("XHR onreadystatechange", req.readyState); // @stripOnBuild - if ( req.readyState == 4 && req.status == 200) - { - try - { - imageData = JSON.parse(req.responseText); - } - catch (e) - { - log("JSON.parse error", e, req.responseText); // @stripOnBuild - } - if (imageData) - { - np._pushImage(imageData); - } - } - }); - - np.__controllableRequestReadystatechange = function () - { - var req = this; - log("XHR onreadystatechange", req.readyState); // @stripOnBuild - if ( req.readyState == 4 && req.status == 200 ) - { - var controlElement = req.controlElement; - if ( controlElement ) - { - var sleep = parseFloat(req.responseText); - if (isNaN(sleep)) { - sleep = 500; // half a second should be enough as a default ... - } - window.setTimeout(function () - { - controlElement.disabled = false; - controlElement.setAttribute("disabled", null); - controlElement.removeAttribute("disabled"); - }, sleep); - } - } - }; - - addEvent(np._serverResetRequest, "readystatechange", np.__controllableRequestReadystatechange); - addEvent(np._serverFlushRequest, "readystatechange", np.__controllableRequestReadystatechange); - - np._fetch = function () - { - var req = this._fetchRequest; - var r_rs = req.readyState; - if ( r_rs == 4 || r_rs == 0 ) - { - log("triggered fetch"); // @stripOnBuild - req.open("GET", "./get", true); - req.send(); - } - }; - - np._mkImage = function (imageData, onReady) - { - log("mkImage", imageData); // @stripOnBuild - if (! imageData.uri) { return; } - - var imageDoc = document.createElement("img"); - addEvent(imageDoc, "load", function () - { - log("loaded", this.src); // @stripOnBuild - /* structure looks like : - - */ - - var imageBox = document.createElement("article"); - imageBox.appendChild(imageDoc); - - var srcSpan = imageBox.appendChild(document.createElement("section")); - srcSpan.setAttribute("role", "source"); - - // not all browsers support dataset property - so use setAttribute function - srcSpan.setAttribute("data-crawler", imageData.crawler.toLowerCase()); // naming conventions: lowercase the names of the crawler classes - srcSpan.setAttribute("data-site", imageData.site); - if ( imageData.source ) - { // this one is optional - srcSpan.setAttribute("data-source", imageData.source); - } - - var srcA = srcSpan.appendChild(document.createElement("a")); - srcA.href = srcA.innerHTML = srcA.innerText = imageData.source || this.src; - - if ( typeof onReady == "function" ) - { - onReady(imageBox); - } - }); - imageDoc.src = imageData.uri; - }; - - np._pushImage = function (imageData) - { - if ( this._imageTarget ) - { - log("add image", imageData); // @stripOnBuild - this._mkImage(imageData, function (image) - { - var add = ( np._state == 0 ); - if ( add ) - { - np._images.push(image); - np._imageTarget.insertBefore(image, np._imageTarget.firstChild); - log("added image", image); // @stripOnBuild - if ( np._images.length > np._imagesMax ) - { - np._popImage(); - } - } - else // @stripOnBuild - { // @stripOnBuild - log("image not loaded, since _state != 0", np._state); // @stripOnBuild - } // @stripOnBuild - }); - } - else // @stripOnBuild - { // @stripOnBuild - log("! image not added", "no target", this._imageTarget); // @stripOnBuild - } // @stripOnBuild - }; - - np._popImage = function () - { - var image = this._images.shift(); - if ( image && image.parentNode ) - { - image.parentNode.removeChild(image); - } - log("popped an image ... if there was one ... "); // @stripOnBuild - }; - - np._optionsStorage = { - target : "np_store" - , save : function () - { - log("options saved"); - localStorage.setItem(this.target, JSON.stringify(np._options)); - } - , load : function () - { - var lo = localStorage.getItem(this.target); - if ( lo ) - { - try - { - lo = JSON.parse(lo); - log("options loaded"); // @stripOnBuild - np._options = lo; - } - catch ( ex ) - { - log("ERROR:", ex); // @stripOnBuild - } - } - else // @stripOnBuild - { // @stripOnBuild - log("options load failed"); // @stripOnBuild - } // @stripOnBuild - } - }; - - np.serverReset = function (controlElement) - { - var req = this._serverResetRequest; - var r_rs = req.readyState; - if ( r_rs == 4 || r_rs == 0 ) - { - log("triggered serverReset"); // @stripOnBuild - req.controlElement = controlElement; - controlElement.disabled = true; - controlElement.setAttribute("disabled", "disabled"); - req.open("GET", "./reset", true); - req.send(); - } - }; - - np.serverFlush = function (controlElement) - { - var req = this._serverFlushRequest; - var r_rs = req.readyState; - if ( r_rs == 4 || r_rs == 0 ) - { - log("triggered serverFlush"); // @stripOnBuild - req.controlElement = controlElement; - controlElement.disabled = true; - controlElement.setAttribute("disabled", "disabled"); - req.open("GET", "./flush", true); - req.send(); - } - }; - - np.setInterval = function (interval) - { - log("set interval", interval); // @stripOnBuild - this._options.interval = interval; - this._optionsStorage.save(); - np._setTimer(); - }; - - np.getInterval = function () - { - return this._options.interval; - }; - - np.setState = function (which, status) - { - var oldState0 = ( this._state == 0 ); - this._state = bitset[ status ? "set" : "unset" ](this._state, which); - log("setState", which, status, "->", this._state); // @stripOnBuild - var state0 = ( this._state == 0 ); - - // not sure it this is the right place for this ... but i had to put it somewhere ... - if ( which == this.constants.stateBS.boss ) - { - var rootElem = document.documentElement; - if ( status ) - { - rootElem.className += this.__bossMode_className; - try - { - window.blur(); - } - catch ( ex ) - { - // nothing to do ... . - } - } - else - { - rootElem.className = rootElem.className.replace(this.__bossMode_className_RE, ""); - } - } - - if ( state0 != oldState0 ) - { - // state changed - this._setTimer(); - - if (this._state == 0) - { - log("! continue"); // @stripOnBuild - this._fetch(); - } - else - { - log("! halt"); // @stripOnBuild - this._fetchRequest.abort(); - } - } - }; - - np.getState = function (which) - { - return bitset.check(this._state, which); - }; - - np.__inited = false; - np.init = function (imageTargetID, imageFadeInTime) - { - if ( this.__inited ) - { - log("init ran already"); // @stripOnBuild - return false; - } - - this.__inited = true; - log("init started"); // @stripOnBuild - - this._imageTarget = document.getElementById(imageTargetID); - this._imageTarget.appendChild(document.createTextNode("")); // to prevent issues with insertBefore() - log("imageTarget:", this._imageTarget); // @stripOnBuild - - this._imageFadeInTime = imageFadeInTime; - log("imageFadeInTime", this._imageFadeInTime); // @stripOnBuild - - this._optionsStorage.load(); - - - addEvent(window, "scroll", function () - { - log("scroll detected", "offset:", this.pageYOffset); // @stripOnBuild - np.setState(np.constants.stateBS.scroll, this.pageYOffset > 0 ); - }); - this.setState(this.constants.stateBS.scroll, window.pageYOffset > 0 ); - - - var c_background = document.getElementById("c_background"); - c_background.checked = this._options.playInBackground; - fireEvent(c_background, "change"); - addEvent(c_background, "change", function () - { - np._options.playInBackground = this.checked; - np._optionsStorage.save(); - log("playInBackground", np._options.playInBackground); // @stripOnBuild - }); - - - /* blur and focus event is broken some time - can not reproduce :-( */ - addEvent(window, "blur", function () - { - log("!# window blur"); // @stripOnBuild - if ( ! np._options.playInBackground ) - { - np.setState(np.constants.stateBS.active, true); - } - }); - addEvent(window, "focus", function () - { - log("!# window active"); // @stripOnBuild - np.setState(np.constants.stateBS.active, false); - }); - - if ( document.hidden != undefined ) - { - // document. // scroll to top - addEvent(document, "visibilitychange", function () - { - log("-- visibility change detected", "hidden:", this.hidden); // @stripOnBuild - np.setState(np.constants.stateBS.presented, this.hidden); - }); - this.setState(this.constants.stateBS.presented, document.hidden); - } - - var c_speed = document.getElementById("c_speed"); - c_speed.value = this.getInterval(); - addEvent(c_speed, "change", function () - { - np.setInterval(this.value); - log("-- interval changed to", "this.value:", this.value); // @stripOnBuild - }); - - var c_state = document.getElementById("c_state"); - c_state.checked = ! this.getState(this.constants.stateBS.manual); - addEvent(c_state, "change", function () - { - np.setState(np.constants.stateBS.manual, !this.checked); - }); - - - var sc_reset = document.getElementById("sc_reset"); - addEvent(sc_reset, "click", function () - { - var controlElement = this; - if ( ! controlElement.disabled ) - { - np.serverReset(controlElement); - } - }); - - var sc_flush = document.getElementById("sc_flush"); - addEvent(sc_flush, "click", function () - { - var controlElement = this; - if ( ! controlElement.disabled ) - { - np.serverFlush(controlElement); - } - }); - - this.setState(this.constants.stateBS.init, false); - log("init ended"); // @stripOnBuild - }; - - - - log("feel done"); // @stripOnBuild -})(window.nichtparasoup={}, window); diff --git a/templates_raw/root/js/feel_hotkeys.js b/templates_raw/root/js/feel_hotkeys.js deleted file mode 100644 index b3036a34..00000000 --- a/templates_raw/root/js/feel_hotkeys.js +++ /dev/null @@ -1,122 +0,0 @@ -; /* remember for development: lines that include the string "@stripOnBuild" will be stripped on build ;-) */ - - -/* hot keys coming soon. - -idea: -* right(39) := make interval slower -* left(37) := make interval faster -* space(32) := toggle play/pause -* escape(27) := toggle boss mode -> stop all loading, make all content invisible and show some productive instead ;-) - - -if a hot key is pressed, show a badge or something for notification .... -maybe its enough to just toggle the
or something? - -hot keys need to be marked in the