mirror of
https://codeberg.org/Freedium-cfd/web.git
synced 2026-03-11 09:04:37 +00:00
UI: fixes
This commit is contained in:
parent
1fe36ab574
commit
fab920caaf
7 changed files with 173 additions and 173 deletions
|
|
@ -47,7 +47,6 @@ class MediumParser:
|
|||
logger.debug("...maybe it's URL. Let's checkout...")
|
||||
return await cls.from_url(unknown, cache=cache, timeout=timeout, host_address=host_address, auth_cookies=auth_cookies)
|
||||
|
||||
|
||||
@classmethod
|
||||
async def from_url(cls, url: str, cache: "SQLiteCacheBackend", timeout: int, host_address: str, auth_cookies: str = None) -> "MediumParser":
|
||||
sanitized_url = correct_url(url)
|
||||
|
|
@ -171,7 +170,7 @@ class MediumParser:
|
|||
async def _parse_and_render_content_html_post(self, content: dict, title: str, subtitle: str, preview_image_id: str, highlights: list, tags: list) -> tuple[list, str, str]:
|
||||
paragraphs = content["bodyModel"]["paragraphs"]
|
||||
tags_list = [tag["displayTitle"] for tag in tags]
|
||||
out_paragraphs = []
|
||||
out_paragraphs: list[str] = []
|
||||
current_pos = 0
|
||||
|
||||
def parse_paragraph_text(text: str, markups: list, is_code: bool = False) -> str:
|
||||
|
|
@ -240,7 +239,7 @@ class MediumParser:
|
|||
if highlight_paragraph["text"] != text_formater.get_text():
|
||||
logger.warning("Highlighted text and paragraph text are not the same! Skip...")
|
||||
break
|
||||
quote_markup_template = '<mark style="background-color: rgb(200 227 200);">{{ text }}</mark>'
|
||||
quote_markup_template = '<mark class="bg-emerald-300">{{ text }}</mark>'
|
||||
text_formater.set_template(
|
||||
highlight["startOffset"],
|
||||
highlight["endOffset"],
|
||||
|
|
@ -271,7 +270,7 @@ class MediumParser:
|
|||
out_paragraphs.append(header_template_rendered)
|
||||
elif paragraph["type"] == "IMG":
|
||||
image_template = jinja_env.from_string(
|
||||
'<div class="mt-7"><img alt="{{ paragraph.metadata.alt }}" style="margin: auto;" class="pt-5 lazy" role="presentation" data-src="https://miro.medium.com/v2/resize:fit:700/{{ paragraph.metadata.id }}"></div>'
|
||||
'<div class="mt-7"><img alt="{{ paragraph.metadata.alt }}" class="pt-5 lazy m-auto" role="presentation" data-src="https://miro.medium.com/v2/resize:fit:700/{{ paragraph.metadata.id }}"></div>'
|
||||
)
|
||||
image_caption_template = jinja_env.from_string("<figcaption class='mt-3 text-sm text-center text-gray-500 dark:text-gray-200'>{{ text }}</figcaption>")
|
||||
if paragraph["layout"] == "OUTSET_ROW":
|
||||
|
|
@ -355,14 +354,15 @@ class MediumParser:
|
|||
|
||||
current_pos = _tmp_current_pos - 1
|
||||
elif paragraph["type"] == "PRE":
|
||||
pre_template = jinja_env.from_string('<pre class="p-4 mt-7 bg-gray-100 dark:bg-gray-900 flex flex-col justify-center">{{code_block}}</pre>')
|
||||
code_block_template = jinja_env.from_string('<code class="overflow-x-auto mt-1 {{ code_css_class }} bg-gray-100 dark:bg-gray-900">{{ text }}</code>')
|
||||
pre_template = jinja_env.from_string('<pre class="mt-7 flex flex-col justify-center border dark:border-gray-700">{{code_block}}</pre>')
|
||||
code_block_template = jinja_env.from_string('<code class="p-2 bg-gray-100 dark:bg-gray-900 overflow-x-auto {{ code_css_class }}">{{ text }}</code>')
|
||||
|
||||
code_css_class = []
|
||||
if paragraph["codeBlockMetadata"] and paragraph["codeBlockMetadata"]["lang"] is not None:
|
||||
code_css_class.append(f'language-{paragraph["codeBlockMetadata"]["lang"]}')
|
||||
else:
|
||||
code_css_class.append("nohighlight")
|
||||
# code_css_class.append("auto")
|
||||
|
||||
code_list = []
|
||||
_tmp_current_pos = current_pos
|
||||
|
|
@ -382,7 +382,9 @@ class MediumParser:
|
|||
out_paragraphs.append(pre_template_rendered)
|
||||
current_pos = _tmp_current_pos - 1
|
||||
elif paragraph["type"] == "BQ":
|
||||
bq_template = jinja_env.from_string('<blockquote class="px-5 pt-3 pb-3 mt-5 shadow-lf"><p style="font-style: italic;">{{ text }}</p></blockquote>')
|
||||
bq_template = jinja_env.from_string(
|
||||
'<blockquote style="box-shadow: inset 3px 0 0 0 rgb(209 207 239 / var(--tw-bg-opacity));" class="px-5 pt-3 pb-3 mt-5"><p class="font-italic">{{ text }}</p></blockquote>'
|
||||
)
|
||||
bq_template_rendered = await bq_template.render_async(text=text_formater.get_text())
|
||||
logger.trace(bq_template_rendered)
|
||||
out_paragraphs.append(bq_template_rendered)
|
||||
|
|
@ -394,9 +396,7 @@ class MediumParser:
|
|||
elif paragraph["type"] == "MIXTAPE_EMBED":
|
||||
# TODO: redirect all Medium embeding articles to Fredium
|
||||
embed_template = jinja_env.from_string(
|
||||
"""
|
||||
<div class="border border-gray-300 p-2 mt-7 items-center overflow-hidden"><a rel="noopener follow" href="{{ url }}" target="_blank"> <div class="flex flex-row justify-between p-2 overflow-hidden"><div class="flex flex-col justify-center p-2"><h2 class="text-black dark:text-gray-100 text-base font-bold">{{ embed_title }}</h2><div class="mt-2 block"><h3 class="text-grey-darker text-sm">{{ embed_description }}</h3></div><div class="mt-5" style=""><p class="text-grey-darker text-xs">{{ embed_site }}</p></div></div><div class="relative flex flew-row h-40 w-60"><div class="lazy absolute inset-0 bg-cover bg-center" data-bg="https://miro.medium.com/v2/resize:fit:320/{{ paragraph.mixtapeMetadata.thumbnailImageId }}"></div></div></div> </a></div>
|
||||
"""
|
||||
'<div class="border border-gray-300 p-2 mt-7 items-center overflow-hidden"><a rel="noopener follow" href="{{ url }}" target="_blank"> <div class="flex flex-row justify-between p-2 overflow-hidden"><div class="flex flex-col justify-center p-2"><h2 class="text-black dark:text-gray-100 text-base font-bold">{{ embed_title }}</h2><div class="mt-2 block"><h3 class="text-grey-darker text-sm">{{ embed_description }}</h3></div><div class="mt-5"><p class="text-grey-darker text-xs">{{ embed_site }}</p></div></div><div class="relative flex flew-row h-40 w-60"><div class="lazy absolute inset-0 bg-cover bg-center" data-bg="https://miro.medium.com/v2/resize:fit:320/{{ paragraph.mixtapeMetadata.thumbnailImageId }}"></div></div></div> </a></div>'
|
||||
)
|
||||
if paragraph.get("mixtapeMetadata") is not None:
|
||||
url = paragraph["mixtapeMetadata"]["href"]
|
||||
|
|
@ -437,7 +437,7 @@ class MediumParser:
|
|||
out_paragraphs.append(embed_template_rendered)
|
||||
elif paragraph["type"] == "IFRAME":
|
||||
iframe_template = jinja_env.from_string(
|
||||
'<div class="mt-7"><iframe class="lazy w-full h-full" data-src="{{ host_address }}/render_iframe/{{ iframe_id }}" allowfullscreen="" frameborder="0" scrolling="no"></iframe></div>'
|
||||
'<div class="mt-7"><iframe class="lazy w-full" data-src="{{ host_address }}/render_iframe/{{ iframe_id }}" allowfullscreen="" frameborder="0" scrolling="no"></iframe></div>'
|
||||
)
|
||||
iframe_template_rendered = await iframe_template.render_async(host_address=self.host_address, iframe_id=paragraph["iframe"]["mediaResource"]["id"])
|
||||
out_paragraphs.append(iframe_template_rendered)
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ KNOWN_MEDIUM_DOMAINS = (
|
|||
"towardsdatascience.com",
|
||||
"thetaoist.online",
|
||||
"devopsquare.com",
|
||||
"www.laceydearie.com",
|
||||
"laceydearie.com",
|
||||
"bettermarketing.pub",
|
||||
"itnext.io",
|
||||
"eand.co",
|
||||
|
|
@ -72,23 +72,20 @@ NOT_MEDIUM_DOMAINS = (
|
|||
"yandex.ru",
|
||||
"yandex.kz",
|
||||
"youtube.com",
|
||||
"www.nytimes.com",
|
||||
"nytimes.com",
|
||||
"wsj.com",
|
||||
"www.forbes.com",
|
||||
"www.wsj.com",
|
||||
"reddit.com",
|
||||
"elpais.com",
|
||||
"forbes.com",
|
||||
"bloomberg.com",
|
||||
"www.lesechos.fr",
|
||||
"www.otz.de",
|
||||
"www.businessinsider.com",
|
||||
"lesechos.fr",
|
||||
"otz.de",
|
||||
"businessinsider.com",
|
||||
"buff.ly",
|
||||
"www.delish.com",
|
||||
"www.economist.com",
|
||||
"www.wired.com",
|
||||
"www.rollingstone.com",
|
||||
"delish.com",
|
||||
"economist.com",
|
||||
"wired.com",
|
||||
"rollingstone.com",
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -138,7 +135,7 @@ def get_unix_ms() -> int:
|
|||
return milliseconds_since_epoch
|
||||
|
||||
|
||||
def unquerify_url(url):
|
||||
def unquerify_url(url: str) -> str:
|
||||
"""
|
||||
Sanitizes a URL by removing all query parameters.
|
||||
|
||||
|
|
@ -157,6 +154,13 @@ def unquerify_url(url):
|
|||
return sanitized_url.removesuffix("/")
|
||||
|
||||
|
||||
@lru_cache(maxsize=500)
|
||||
def un_wwwify(url: str):
|
||||
if url.startswith("www."):
|
||||
return url.removeprefix("www.")
|
||||
return url
|
||||
|
||||
|
||||
def correct_url(url: str) -> str:
|
||||
# Workaround for Safari bug. We don't known by what condition this happens, but sometimes we get
|
||||
# some broken URL, for example like "", and all of them based on user-agent comes from Safari browser engine,
|
||||
|
|
@ -247,12 +251,13 @@ async def resolve_medium_short_link(short_url_id: str, timeout: int = 5) -> str:
|
|||
async def resolve_medium_url(url: str, timeout: int = 5) -> str:
|
||||
logger.debug(f"Trying resolve {url=}, with {timeout=}")
|
||||
parsed_url = urlparse(url)
|
||||
parsed_netloc = un_wwwify(parsed_url.netloc)
|
||||
|
||||
if parsed_url.path.startswith("/p/"):
|
||||
logger.debug("URL is Medium 'mobile' link")
|
||||
post_id = parsed_url.path.rsplit("/p/")[1]
|
||||
|
||||
elif parsed_url.netloc == "l.facebook.com" and parsed_url.path.startswith("/l.php"):
|
||||
elif parsed_netloc == "l.facebook.com" and parsed_url.path.startswith("/l.php"):
|
||||
logger.debug("URL seems like is Facebook redirect (tracking) link")
|
||||
|
||||
parsed_query = parse_qs(parsed_url.query)
|
||||
|
|
@ -263,7 +268,7 @@ async def resolve_medium_url(url: str, timeout: int = 5) -> str:
|
|||
logger.debug("...but we get fucked up...")
|
||||
return False
|
||||
|
||||
elif parsed_url.netloc == "webcache.googleusercontent.com" and parsed_url.path.startswith("/search"):
|
||||
elif parsed_netloc == "webcache.googleusercontent.com" and parsed_url.path.startswith("/search"):
|
||||
logger.debug("URL seems like is Google Web Archive page link")
|
||||
|
||||
parsed_query = parse_qs(parsed_url.query)
|
||||
|
|
@ -274,7 +279,7 @@ async def resolve_medium_url(url: str, timeout: int = 5) -> str:
|
|||
logger.debug("...but we get fucked up...")
|
||||
return False
|
||||
|
||||
elif parsed_url.netloc == "www.google.com" and parsed_url.path.startswith("/url"):
|
||||
elif parsed_netloc == "google.com" and parsed_url.path.startswith("/url"):
|
||||
logger.debug("URL seems like is Google redirect (tracking) link")
|
||||
|
||||
parsed_query = parse_qs(parsed_url.query)
|
||||
|
|
@ -290,7 +295,7 @@ async def resolve_medium_url(url: str, timeout: int = 5) -> str:
|
|||
logger.debug("...but we get fucked up...")
|
||||
return False
|
||||
|
||||
elif parsed_url.netloc == "12ft.io":
|
||||
elif parsed_netloc == "12ft.io":
|
||||
logger.debug("URL seems like is from our partner named 12ft.io")
|
||||
|
||||
parsed_query = parse_qs(parsed_url.query)
|
||||
|
|
@ -314,7 +319,7 @@ async def resolve_medium_url(url: str, timeout: int = 5) -> str:
|
|||
logger.debug("...but we get fucked up...")
|
||||
return False
|
||||
|
||||
elif parsed_url.netloc == "link.medium.com":
|
||||
elif parsed_netloc == "link.medium.com":
|
||||
logger.debug("URL seems like is Medium short (SHORT) redirect (tracking) link. Make resolve them...")
|
||||
short_url_id = parsed_url.path.removeprefix("/")
|
||||
post_url = await resolve_medium_short_link(short_url_id, timeout)
|
||||
|
|
@ -337,13 +342,16 @@ async def resolve_medium_url_old(url: str, timeout: int = 5) -> str:
|
|||
retry_client = RetryClient(client_session=session, raise_for_status=False, retry_options=retry_options)
|
||||
request = await retry_client.get(url, timeout=timeout)
|
||||
response = await request.text()
|
||||
|
||||
soup = BeautifulSoup(response, "html.parser")
|
||||
type_meta_tag = soup.head.find("meta", property="og:type")
|
||||
if not type_meta_tag or type_meta_tag.get("content") != "article":
|
||||
return False
|
||||
|
||||
url_meta_tag = soup.head.find("meta", property="al:android:url")
|
||||
if not url_meta_tag or not url_meta_tag.get("content"):
|
||||
return False
|
||||
|
||||
parsed_url = urlparse(url_meta_tag["content"])
|
||||
path = parsed_url.path.strip("/")
|
||||
parsed_value = path.split("/")[-1]
|
||||
|
|
@ -394,16 +402,17 @@ async def is_valid_medium_url(url: str) -> bool:
|
|||
"""
|
||||
domain = get_fld(url)
|
||||
parsed_url = urlparse(url)
|
||||
domain_netloc = un_wwwify(parsed_url.netloc)
|
||||
|
||||
# TODO: http://freedium.cfd/https://www.google.com.vn/url?sa=i&url=https%3A%2F%2Fmedium.com%2F%40dugguRK%2Fabout-android-hardware-abstraction-layer-hal-5d191dafeb2c&psig=AOvVaw17KP0U_haPMmhAByeMTxSg&ust=1711354113283000&source=images&cd=vfe&opi=89978449&ved=0CBQQjhxqFwoTCMCM_oG5jIUDFQAAAAAdAAAAABAa
|
||||
|
||||
if domain in ["12ft.io", "google.com", "facebook.com", "googleusercontent.com"]:
|
||||
return True
|
||||
|
||||
if domain in NOT_MEDIUM_DOMAINS or parsed_url.netloc in NOT_MEDIUM_DOMAINS:
|
||||
if domain in NOT_MEDIUM_DOMAINS or domain_netloc in NOT_MEDIUM_DOMAINS:
|
||||
raise exceptions.NotValidMediumURL("100% not valid Medium URL")
|
||||
|
||||
if domain in KNOWN_MEDIUM_DOMAINS or parsed_url.netloc in KNOWN_MEDIUM_CUSTOM_DOMAINS:
|
||||
if domain in KNOWN_MEDIUM_DOMAINS or domain_netloc in KNOWN_MEDIUM_CUSTOM_DOMAINS:
|
||||
return True
|
||||
|
||||
logger.warning(f"url '{url}' wasn't detected in known Medium domains")
|
||||
|
|
@ -411,7 +420,7 @@ async def is_valid_medium_url(url: str) -> bool:
|
|||
# XXX: Unfourtunately, for now we don't know ALL Medium's domains, so we need resolve links
|
||||
resolve_result = bool(await resolve_medium_url(url))
|
||||
|
||||
# send_message(f"We found that {domain=}, {parsed_url.netloc=} is not listed in out known Medium database.\nURL: {url}")
|
||||
# send_message(f"We found that {domain=}, {domain_netloc=} is not listed in out known Medium database.\nURL: {url}")
|
||||
|
||||
return resolve_result
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ services:
|
|||
# - ./.env:/app/.env
|
||||
# - ./server/user_data/logs:/app/server/user_data/logs
|
||||
- .:/app
|
||||
- ./core/medium_parser/:/app/medium_parser
|
||||
develop:
|
||||
watch:
|
||||
- action: rebuild
|
||||
|
|
@ -65,23 +66,23 @@ services:
|
|||
restart: always
|
||||
mem_limit: 2g
|
||||
|
||||
dragonfly:
|
||||
image: 'docker.dragonflydb.io/dragonflydb/dragonfly'
|
||||
ulimits:
|
||||
memlock: -1
|
||||
expose:
|
||||
- 6379
|
||||
networks:
|
||||
- web_network
|
||||
volumes:
|
||||
- dragonflydata:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 30s
|
||||
start_period: 20s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
restart: always
|
||||
# dragonfly:
|
||||
# image: 'docker.dragonflydb.io/dragonflydb/dragonfly'
|
||||
# ulimits:
|
||||
# memlock: -1
|
||||
# expose:
|
||||
# - 6379
|
||||
# networks:
|
||||
# - web_network
|
||||
# volumes:
|
||||
# - dragonflydata:/data
|
||||
# healthcheck:
|
||||
# test: ["CMD", "redis-cli", "ping"]
|
||||
# interval: 30s
|
||||
# start_period: 20s
|
||||
# timeout: 10s
|
||||
# retries: 3
|
||||
# restart: always
|
||||
|
||||
autoheal:
|
||||
restart: always
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ from fastapi import Response
|
|||
from server import config
|
||||
from server.utils.logger_trace import trace
|
||||
|
||||
IFRAME_HEADERS = {"Access-Control-Allow-Origin": "*", "X-Frame-Options": "SAMEORIGIN"}
|
||||
from bs4 import BeautifulSoup, Comment
|
||||
|
||||
IFRAME_HEADERS = {"Access-Control-Allow-Origin": "*", "X-Frame-Options": "SAMEORIGIN"}
|
||||
|
||||
@trace
|
||||
async def iframe_proxy(iframe_id):
|
||||
|
|
@ -17,8 +18,15 @@ async def iframe_proxy(iframe_id):
|
|||
headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36"},
|
||||
)
|
||||
request_content = await request.text()
|
||||
request_content = request_content.replace("document.domain = document.domain", "console.log('[FREEDIUM] iframe workaround')")
|
||||
return Response(content=request_content, media_type="text/html", headers=IFRAME_HEADERS)
|
||||
request_content = request_content.replace("document.domain = document.domain", 'console.log("[FREEDIUM] iframe workaround started")')
|
||||
|
||||
request_content_soup = BeautifulSoup(request_content, "html.parser")
|
||||
iframe_hack_script = '<script src="https://cdn.jsdelivr.net/npm/@iframe-resizer/child"></script>'
|
||||
new_script_tag = BeautifulSoup(iframe_hack_script, 'html.parser').script
|
||||
|
||||
request_content_soup.head.append(new_script_tag)
|
||||
|
||||
return Response(content=request_content_soup.prettify(), media_type="text/html", headers=IFRAME_HEADERS)
|
||||
|
||||
|
||||
@trace
|
||||
|
|
@ -31,4 +39,4 @@ async def miro_proxy(miro_data: str):
|
|||
)
|
||||
request_content = await request.read()
|
||||
content_type = request.headers["Content-Type"]
|
||||
return Response(content=request_content, media_type=content_type)
|
||||
return Response(content=request_content, media_type=content_type)
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@
|
|||
{% if creator %}<meta name="author" content="{{ creator.name }}" />{% endif %}
|
||||
<meta name="description" content="{{ description or 'Your paywall breakthrough for Medium!' }}" />
|
||||
<meta name="keywords" content="medium, paywall, medium.com, paywall breakthrough" />
|
||||
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script src="https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio"></script>
|
||||
<!--<script src="https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio"></script>-->
|
||||
<link href="https://glyph.medium.com/css/unbound.css" rel="stylesheet">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||
|
|
@ -18,77 +19,13 @@
|
|||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#00aba9">
|
||||
<meta name="msapplication-TileColor" content="#00aba9">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/@highlightjs/cdn-assets@11.8.0/styles/atom-one-dark.min.css">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/@iframe-resizer/parent"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vanilla-lazyload@17.8.4/dist/lazyload.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/lightense-images@1.0.17/dist/lightense.min.js"></script>
|
||||
<script>
|
||||
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
document.documentElement.classList.add('dark');
|
||||
//document.getElementById('darkIcon').classList.remove('hidden');
|
||||
//document.getElementById('lightIcon').classList.add('hidden')
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
//document.getElementById('lightIcon').classList.remove('hidden');
|
||||
//document.getElementById('darkIcon').classList.add('hidden');
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.overflow-hidden {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.shadow-lf {
|
||||
box-shadow: inset 3px 0 0 0 rgb(209 207 239 / var(--tw-bg-opacity));
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.notification-container {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
padding: 2%;
|
||||
max-height: 95vh; /* Set a maximum height for the container */
|
||||
overflow-y: auto; /* Enable vertical scrolling */
|
||||
}
|
||||
|
||||
.notification-card {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
window._resizeIframe = function (iframeData)
|
||||
{
|
||||
iframeData.iframe.height = iframeData.height
|
||||
_resizeIframeWidth()
|
||||
}
|
||||
|
||||
function _resizeIframeWidth(){ var element = document.querySelector(".main-content");
|
||||
var width = element.offsetWidth;
|
||||
|
||||
iframes = document.getElementsByTagName("iframe");
|
||||
for (var i = 0; i < iframes.length; i++) {
|
||||
iframes[i].width = width
|
||||
}
|
||||
|
||||
window.onresize = _resizeIframeWidth
|
||||
}
|
||||
</script>
|
||||
<!--
|
||||
<script>
|
||||
window.onload = function() {
|
||||
window.parent.postMessage({
|
||||
type: "URL_UPDATE",
|
||||
url: window.location.href
|
||||
}, "*");
|
||||
}
|
||||
</script>
|
||||
-->
|
||||
<script>iframeResize({ license: 'GPLv3' })</script>
|
||||
</head>
|
||||
<div class="fixed bottom-4 left-4" style="z-index: 999999;">
|
||||
<button id="openProblemModal"
|
||||
|
|
@ -111,8 +48,8 @@
|
|||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="notification-container" style="z-index: 999999;">
|
||||
<div class="notification-card dark:bg-gray-800 bg-white">
|
||||
<div class="notification-container fixed top-5 p-2 max-h-[95vh] overflow-y-auto hidden" style="z-index: 999999;">
|
||||
<div class="bg-white border border-gray-300 shadow-sm p-5 rounded-md text-center dark:bg-gray-800 bg-white">
|
||||
<p class="text-2xl pb-5 text-black dark:text-white">Bad news</p>
|
||||
<p class="pb-3 text-black dark:text-white">
|
||||
We regret to inform you that our account on BuyMeACoffee has been suspended due to a violation of their terms of service. This was an unexpected development, and we are currently addressing the matter with utmost priority.
|
||||
|
|
@ -203,7 +140,7 @@ Warm regards, The Freedium Team
|
|||
class="modal hidden fixed inset-0 w-full h-full flex items-center justify-center overflow-y-auto bg-black bg-opacity-50"
|
||||
style="z-index: 999999">
|
||||
<div class="modal-container w-11/12 md:max-w-xl mx-auto rounded shadow-lg max-h-screen">
|
||||
<div class="modal-content bg-white dark:bg-gray-800 dark:text-white my-8 py-4 text-left px-6">
|
||||
<div class="modal-content bg-white dark:bg-gray-800 text-black dark:text-white my-8 py-4 text-left px-6">
|
||||
<h1 class="text-3xl font-bold">Reporting a Problem</h1>
|
||||
<div class="mt-3">
|
||||
<p>
|
||||
|
|
@ -238,35 +175,59 @@ Warm regards, The Freedium Team
|
|||
darkMode: 'class',
|
||||
}
|
||||
|
||||
function navigateToOrigin() {
|
||||
window.location.href = window.location.origin;
|
||||
}
|
||||
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
// document.documentElement.classList.add('dark');
|
||||
document.getElementById('darkIcon').classList.remove('hidden');
|
||||
document.getElementById('lightIcon').classList.add('hidden')
|
||||
} else {
|
||||
// document.documentElement.classList.remove('dark')
|
||||
document.getElementById('lightIcon').classList.remove('hidden');
|
||||
document.getElementById('darkIcon').classList.add('hidden');
|
||||
function changeTheme(themeName) {
|
||||
// Source: https://stackoverflow.com/questions/59257368/how-to-dynamically-change-the-theme-using-highlight-js
|
||||
console.log(`Applying theme: ${themeName}`);
|
||||
const existingLink = document.querySelector('link[href*="highlight.js"]');
|
||||
if (existingLink) {
|
||||
existingLink.remove();
|
||||
}
|
||||
const link = document.createElement("link");
|
||||
link.rel = "stylesheet";
|
||||
link.href = `https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/${themeName}.min.css`;
|
||||
document.head.appendChild(link);
|
||||
document.querySelector("span").textContent = themeName;
|
||||
}
|
||||
|
||||
function navigateToOrigin() {
|
||||
window.location.href = window.location.origin;
|
||||
}
|
||||
|
||||
function updateThemeIcons() {
|
||||
const isDarkMode = localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
document.getElementById('darkIcon').classList.toggle('hidden', !isDarkMode);
|
||||
document.getElementById('lightIcon').classList.toggle('hidden', isDarkMode);
|
||||
}
|
||||
|
||||
document.getElementById('darkModeToggle').addEventListener('click', function() {
|
||||
updateThemeIcons();
|
||||
|
||||
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
document.documentElement.classList.remove('dark');
|
||||
document.getElementById('darkIcon').classList.add('hidden');
|
||||
document.getElementById('lightIcon').classList.remove('hidden')
|
||||
document.documentElement.style.cssText = "--lightense-backdrop: white;";
|
||||
localStorage.setItem("theme", "light")
|
||||
} else {
|
||||
document.documentElement.classList.add('dark')
|
||||
document.getElementById('lightIcon').classList.add('hidden');
|
||||
document.getElementById('darkIcon').classList.remove('hidden');
|
||||
document.documentElement.style.cssText = "--lightense-backdrop: black;";
|
||||
localStorage.setItem("theme", "dark")
|
||||
}
|
||||
});
|
||||
document.getElementById('darkModeToggle').addEventListener('click', function () {
|
||||
const isDarkMode = localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
if (isDarkMode) {
|
||||
document.documentElement.classList.remove('dark');
|
||||
document.documentElement.style.cssText = "--lightense-backdrop: white;";
|
||||
localStorage.setItem("theme", "light");
|
||||
changeTheme("a11y-light");
|
||||
} else {
|
||||
document.documentElement.classList.add('dark');
|
||||
document.documentElement.style.cssText = "--lightense-backdrop: black;";
|
||||
localStorage.setItem("theme", "dark");
|
||||
changeTheme("androidstudio");
|
||||
}
|
||||
updateThemeIcons();
|
||||
})
|
||||
|
||||
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
document.documentElement.classList.add('dark');
|
||||
//document.getElementById('darkIcon').classList.remove('hidden');
|
||||
//document.getElementById('lightIcon').classList.add('hidden')
|
||||
changeTheme("androidstudio");
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
//document.getElementById('lightIcon').classList.remove('hidden');
|
||||
//document.getElementById('darkIcon').classList.add('hidden');
|
||||
changeTheme("a11y-light");
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
const openModalButton = document.getElementById('openProblemModal');
|
||||
|
|
@ -277,19 +238,19 @@ if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.match
|
|||
const body = document.querySelector('body');
|
||||
|
||||
openModalButton.addEventListener('click', () => {
|
||||
body.classList.add('overflow-hidden'); // Prevent scrolling on the body
|
||||
body.classList.add('!overflow-hidden'); // Prevent scrolling on the body
|
||||
modal.classList.remove('hidden');
|
||||
});
|
||||
|
||||
closeModalButton.addEventListener('click', () => {
|
||||
body.classList.remove('overflow-hidden'); // Re-enable scrolling on the body
|
||||
body.classList.remove('!overflow-hidden'); // Re-enable scrolling on the body
|
||||
modal.classList.add('hidden');
|
||||
});
|
||||
|
||||
modal.addEventListener('click', (e) => {
|
||||
if (e.target === modal) {
|
||||
modal.classList.add('hidden');
|
||||
body.classList.remove('overflow-hidden');
|
||||
body.classList.remove('!overflow-hidden');
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -360,22 +321,41 @@ document.addEventListener('scroll', function () {
|
|||
Lightense('img:not(.no-lightense)');
|
||||
}, false);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
<script>
|
||||
var lazyLoadInstance = new LazyLoad({
|
||||
callback_loaded: function(element) {
|
||||
Lightense(element);
|
||||
},
|
||||
callback_error: (img) => {
|
||||
console.log(img);
|
||||
if (img.hasAttribute("data-src")) {
|
||||
if (img.attributes["data-src"].value.startsWith("https://miro.medium.com/v2/")) {
|
||||
img.setAttribute("src", img.attributes["data-src"].value.replace("https://miro.medium.com/v2/", "{{HOST_ADDRESS}}/@miro/v2/" ));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
console.log(element);
|
||||
console.log(element.tagName);
|
||||
switch (element.tagName) {
|
||||
case "IMG":
|
||||
console.log(`${element} is image, wrapping into lightense`);
|
||||
Lightense(element);
|
||||
break;
|
||||
case "IFRAME":
|
||||
const resizeIframe = () => {
|
||||
console.log(`${element} is iframe, wrapping script`);
|
||||
let iframeHeight = element.contentWindow.document.body.scrollHeight;
|
||||
if (iframeHeight == 150) {
|
||||
iframeHeight = 500;
|
||||
}
|
||||
element.style.height = iframeHeight + 'px';
|
||||
};
|
||||
window.addEventListener('resize', resizeIframe);
|
||||
setInterval(resizeIframe, 4500);
|
||||
resizeIframe();
|
||||
break;
|
||||
}
|
||||
},
|
||||
callback_error: (element) => {
|
||||
console.log(element);
|
||||
if (element.tagName === "IMG" && element.hasAttribute("data-src")) {
|
||||
const srcAttribute = element.attributes["data-src"].value;
|
||||
if (srcAttribute.startsWith("https://miro.medium.com/v2/")) {
|
||||
element.setAttribute("src", srcAttribute.replace("https://miro.medium.com/v2/", "{{HOST_ADDRESS}}/@miro/v2/"));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
function navigateToOrigin() {
|
||||
|
|
@ -392,14 +372,14 @@ document.addEventListener('scroll', function () {
|
|||
function showNotification() {
|
||||
if (!localStorage.getItem(notificationFlagString)) {
|
||||
notificationContainer.style.display = 'block';
|
||||
body.classList.add('overflow-hidden');
|
||||
body.classList.add('!overflow-hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function hideNotification() {
|
||||
localStorage.setItem(notificationFlagString, 'false');
|
||||
notificationContainer.style.display = 'none';
|
||||
body.classList.remove('overflow-hidden');
|
||||
body.classList.remove('!overflow-hidden');
|
||||
}
|
||||
|
||||
// Close button functionality
|
||||
|
|
@ -410,3 +390,6 @@ document.addEventListener('scroll', function () {
|
|||
showNotification();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -90,9 +90,9 @@ document.addEventListener('DOMContentLoaded', (event) => {
|
|||
hljs.highlightAll();
|
||||
|
||||
document.querySelectorAll('pre code').forEach((el) => {
|
||||
code = el.textContent;
|
||||
el = el.parentElement;
|
||||
el.innerHTML = '<button class="hljs-copy p-1 bg-gray-300 dark:bg-black">Copy</button>' + el.innerHTML; // append copy button
|
||||
code = el.textContent;
|
||||
el = el.parentElement;
|
||||
el.innerHTML = '<button class="hljs-copy p-1 bg-gray-300 dark:bg-zinc-800">Copy</button>' + el.innerHTML; // append copy button
|
||||
el.getElementsByClassName('hljs-copy')[0].contentCopy = code;
|
||||
el.getElementsByClassName('hljs-copy')[0].addEventListener("click", function () {
|
||||
this.innerText = 'Copying..';
|
||||
|
|
|
|||
|
|
@ -30,10 +30,9 @@ def enable_maintenance_mode():
|
|||
|
||||
def do_maintenance(sleep_time: int = 60 * 60 * 24):
|
||||
while True:
|
||||
sleep(sleep_time)
|
||||
try:
|
||||
enable_maintenance_mode()
|
||||
except Exception as e:
|
||||
logger.error(f"Error enabling maintenance mode: {e}")
|
||||
send_message(f"Error enabling maintenance mode: {e}")
|
||||
finally:
|
||||
sleep(sleep_time)
|
||||
|
|
|
|||
Loading…
Reference in a new issue