Merge pull request #33 from rayanfer32/master
UI improvements + patch for avoiding alias duplication
This commit is contained in:
commit
9eabc78872
|
@ -7,5 +7,5 @@ tests/
|
|||
logo/
|
||||
app.json
|
||||
Procfile
|
||||
.vscode
|
||||
.vscode/
|
||||
.gitignore
|
|
@ -8,7 +8,8 @@ import os
|
|||
|
||||
try:
|
||||
port = int(os.environ.get("PORT", "8080"))
|
||||
except ValueError:
|
||||
except Exception as e:
|
||||
print(e)
|
||||
port = -1
|
||||
if not 1 <= port <= 65535:
|
||||
print(
|
||||
|
|
|
@ -30,6 +30,8 @@ async def setup_routes(app, handler):
|
|||
web.get(p + r"/{id:\d+}/download", h.download_get),
|
||||
web.head(p + r"/{id:\d+}/download", h.download_head),
|
||||
web.get(p + r"/{id:\d+}/thumbnail", h.thumbnail_get),
|
||||
web.get(p + r"/{id:\d+}/v.mp4", h.download_get),
|
||||
web.head(p + r"/{id:\d+}/v.mp4", h.download_head),
|
||||
]
|
||||
if index_all:
|
||||
# print(await client.get_dialogs())
|
||||
|
|
|
@ -7,6 +7,24 @@
|
|||
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
|
||||
<script src="https://cdn.fluidplayer.com/v3/current/fluidplayer.min.js"></script>
|
||||
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css?family=PT+Sans&display=swap');
|
||||
* {
|
||||
font-family: 'PT Sans';
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
window.onload = () => {
|
||||
// set title as host url
|
||||
const titleA = document.getElementById("title-a")
|
||||
titleA.innerHTML = document.location.host
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<title>
|
||||
{% if title %} {{title}} {% else %} Telegram Index {% endif %}
|
||||
</title>
|
||||
|
@ -15,9 +33,9 @@
|
|||
|
||||
<body>
|
||||
|
||||
<header class="bg-red-600 text-white mb-2 p-4 w-full shadow">
|
||||
<header class="bg-blue-300 text-white mb-2 p-4 w-full shadow">
|
||||
<div class="mx-auto flex justify-between content-center max-w-screen-xl">
|
||||
<a href="/" class="text-left text-xl lg:text-2xl xl:text-3xl"> Telegram Index </a>
|
||||
<a href="/" id="title-a" class="text-left text-xl lg:text-2xl xl:text-3xl"> Telegram index </a>
|
||||
{% if authenticated %} <a href="/logout" class="text-right"> Logout </a> {% else %} {%
|
||||
endif %}
|
||||
</div>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<div class="block p-4 md:flex md:flex-wrap md:justify-items-center w-full text-center break-words">
|
||||
{% for chat in chats %}
|
||||
<a title="{{chat.name}}" href="/{{chat.page_id}}"
|
||||
class="mx-auto flex flex-col justify-items-center w-2/3 min-h-full md:w-2/5 lg:w-1/6 rounded my-2 p-2 shadow-md hover:shadow-lg hover:border hover:border-blue-300 hover:bg-blue-100">
|
||||
class="mx-auto flex flex-col justify-items-center w-2/3 min-h-full md:w-2/5 lg:w-1/6 rounded my-2 p-2 shadow hover:shadow-md hover:border hover:border-blue-300 hover:bg-blue-100">
|
||||
|
||||
<img src="/{{chat.page_id}}/logo?big=1" class="w-full rounded-full ">
|
||||
<div class="p-4 rounded">{{chat.name}}</div>
|
||||
|
|
|
@ -1,59 +1,143 @@
|
|||
{% include 'header.html' %}
|
||||
|
||||
<div class="block md:flex justify-between items-center px-4 text-center border-b-2">
|
||||
<div class="m-2 block md:flex items-center justify-center md:justify-start text-2xl md:text-right font-bold text-blue-500">
|
||||
<img class="mx-auto md:ml-0 md:mr-1 my-2 w-16 h-16 rounded-full bg-black outline-none" src="{{logo}}">
|
||||
<h1> {{name}} </h1>
|
||||
</div>
|
||||
|
||||
<div class="m-2">
|
||||
<form class="">
|
||||
<input class="border rounded-full px-4 py-2 border-indigo-500 placeholder-indigo-500 text-indigo-500 outline-none" type="text" name="search" value="{% if search %}{{search}}{% endif %}" placeholder="search...">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{% if item_list %}
|
||||
|
||||
<div class="mx-auto my-2 w-full px-4">
|
||||
<div class="block md:flex md:flex-wrap md:justify-center text-center break-all">
|
||||
{% for item in item_list %}
|
||||
<a title="{% if item.media %} {{item.mime_type}} | {{item.human_size}} {% else %} Text message {% endif %}" href="{{item.url}}" class="flex flex-col items-center justify-center w-full min-h-full md:w-1/5 lg:w-1/6 rounded my-4 md:mx-1 shadow-md hover:shadow-lg hover:bg-blue-100 hover:border hover:border-blue-200">
|
||||
|
||||
{% if item.media %}
|
||||
<img src="{{item.thumbnail}}" class="w-full rounded">
|
||||
<div class="p-4 rounded">{{item.insight}}</div>
|
||||
{% else %}
|
||||
<div class="p-4 rounded">{{item.insight}}</div>
|
||||
{% endif %}
|
||||
|
||||
</a>
|
||||
{% endfor %}
|
||||
|
||||
{% block javascript %}
|
||||
<script type="text/javascript">
|
||||
{% include "./js/filesaver.min.js" %}
|
||||
{% include "./js/playlist.js" %}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
<div class="block md:flex justify-between items-center px-4 text-center border-b-2">
|
||||
<div
|
||||
class="m-2 block md:flex items-center justify-center md:justify-start text-2xl md:text-right font-bold text-blue-500">
|
||||
<img class="mx-auto md:ml-0 md:mr-1 my-2 w-16 h-16 rounded-full bg-black outline-none" src="{{logo}}">
|
||||
<h1> {{name}} </h1>
|
||||
</div>
|
||||
|
||||
<div class="m-2">
|
||||
<form class="">
|
||||
|
||||
<div class="p-0">
|
||||
<div class=" flex items-center rounded-full shadow">
|
||||
<input class=" rounded-l-full w-full py-1 px-6 text-gray-700 leading-tight focus:outline-none"
|
||||
name="search" id="search" value="{% if search %}{{search}}{% endif %}" type="text"
|
||||
placeholder="Search">
|
||||
<div class="p-1">
|
||||
<button
|
||||
class="bg-blue-500 text-white rounded-full p-2 hover:bg-blue-400 focus:outline-none w-12 h-12 flex items-center justify-center">
|
||||
<div><svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg></div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{% if item_list %}
|
||||
|
||||
<div class="mx-auto my-2 w-full px-4 ">
|
||||
<div class="block md:flex md:flex-wrap md:justify-center text-center break-all ">
|
||||
{% for item in item_list %}
|
||||
|
||||
<!-- Card -->
|
||||
<div title="{% if item.media %} {{item.mime_type}} | {{item.human_size}} {% else %} Text message {% endif %}"
|
||||
href="{{item.url}}"
|
||||
class="text-sm flex flex-col items-center justify-center w-full min-h-full md:w-1/5 lg:w-1/6 rounded my-4 md:mx-1 shadow hover:shadow-md border-solid ">
|
||||
|
||||
<div class="bg-blue-500 rounded text-white my-1 py-0 px-1">{{item.file_id}}</div>
|
||||
|
||||
{% if item.media %}
|
||||
|
||||
<a href="{{item.url}}"><img src="{{item.thumbnail}}" class="w-full rounded shadow-inner"></a>
|
||||
|
||||
|
||||
<!-- Buttons container -->
|
||||
<span class="item-buttons my-1 rounded shadow-inner">
|
||||
|
||||
<div class="p-4 text-dark py-0 px-2 my-1 ">{{item.insight}}</div>
|
||||
|
||||
<!-- Direct file download button -->
|
||||
<a href="{{item.download}}"
|
||||
class=" hover:bg-blue-300 text-gray-900 font-semibold py-1 px-2 rounded inline-flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M15 13l-3 3m0 0l-3-3m3 3V8m0 13a9 9 0 110-18 9 9 0 010 18z" />
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<!-- Kodi/media player supported url ending with v.mp4 -->
|
||||
<a title="v.mp4" href="{{item.vlc}}"
|
||||
class=" hover:bg-blue-300 text-gray-900 font-semibold py-1 px-2 rounded inline-flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<!-- One click single item playlist Download -->
|
||||
<button title="{{item.insight}}.m3u" onclick='singleItemPlaylist("{{item.vlc}}","{{item.insight}}")'
|
||||
class=" hover:bg-blue-300 text-gray-900 font-semibold py-1 px-2 rounded inline-flex items-center">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M3 4h13M3 8h9m-9 4h9m5-4v12m0 0l-4-4m4 4l4-4" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
</span>
|
||||
|
||||
<p class="my-2 text-center font-semibold">
|
||||
{{item_list|length}} items
|
||||
</p>
|
||||
|
||||
<div class="mx-auto my-2 text-center flex text-white content-center justify-center">
|
||||
{% if prev_page %}
|
||||
<a title="Previous page" class="mx-2 p-2 border rounded bg-green-500 hover:border-green-500 hover:text-green-500 hover:bg-white" href="{{prev_page.url}}">Page {{prev_page.no}}</a>
|
||||
{% endif %}
|
||||
<p class="mx-2 p-2 border rounded border-green-500 text-green-500 hover:border-green-500 hover:text-green-500 hover:bg-white">Page {{cur_page}}</p>
|
||||
{% if next_page %}
|
||||
<a title="Next page" class="mx-2 p-2 border rounded bg-green-500 hover:border-green-500 hover:text-green-500 hover:bg-white" href="{{next_page.url}}">Page {{next_page.no}}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
|
||||
<p class="my-4 text-center text-2xl md:text-3xl lg:text-4xl xl:text-5xl">
|
||||
No message to display!
|
||||
</p>
|
||||
|
||||
<a href={{item.url}}>
|
||||
<div class="p-4 rounded shadow-inner rounded text-dark py-0 px-2">{{item.insight}}</div>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% include 'footer.html' %}
|
||||
</div>
|
||||
|
||||
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<p class="my-2 text-center font-semibold">
|
||||
{{item_list|length}} items
|
||||
</p>
|
||||
|
||||
<div class="mx-auto my-2 text-center flex text-white content-center justify-center">
|
||||
{% if prev_page %}
|
||||
<a title="Previous page"
|
||||
class="mx-2 p-2 border rounded bg-green-500 hover:border-green-500 hover:text-green-500 hover:bg-white"
|
||||
href="{{prev_page.url}}">Page {{prev_page.no}}</a>
|
||||
{% endif %}
|
||||
<p
|
||||
class="mx-2 p-2 border rounded border-green-500 text-green-500 hover:border-green-500 hover:text-green-500 hover:bg-white">
|
||||
Page {{cur_page}}</p>
|
||||
{% if next_page %}
|
||||
<a title="Next page"
|
||||
class="mx-2 p-2 border rounded bg-green-500 hover:border-green-500 hover:text-green-500 hover:bg-white"
|
||||
href="{{next_page.url}}">Page {{next_page.no}}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
|
||||
<p class="my-4 text-center text-2xl md:text-3xl lg:text-4xl xl:text-5xl">
|
||||
No message to display!
|
||||
</p>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% include 'footer.html' %}
|
|
@ -0,0 +1,2 @@
|
|||
//# sourceMappingURL=FileSaver.min.js.map
|
||||
(function(a,b){if("function"==typeof define&&define.amd)define([],b);else if("undefined"!=typeof exports)b();else{b(),a.FileSaver={exports:{}}.exports}})(this,function(){"use strict";function b(a,b){return"undefined"==typeof b?b={autoBom:!1}:"object"!=typeof b&&(console.warn("Deprecated: Expected third argument to be a object"),b={autoBom:!b}),b.autoBom&&/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(a.type)?new Blob(["\uFEFF",a],{type:a.type}):a}function c(a,b,c){var d=new XMLHttpRequest;d.open("GET",a),d.responseType="blob",d.onload=function(){g(d.response,b,c)},d.onerror=function(){console.error("could not download file")},d.send()}function d(a){var b=new XMLHttpRequest;b.open("HEAD",a,!1);try{b.send()}catch(a){}return 200<=b.status&&299>=b.status}function e(a){try{a.dispatchEvent(new MouseEvent("click"))}catch(c){var b=document.createEvent("MouseEvents");b.initMouseEvent("click",!0,!0,window,0,0,0,80,20,!1,!1,!1,!1,0,null),a.dispatchEvent(b)}}var f="object"==typeof window&&window.window===window?window:"object"==typeof self&&self.self===self?self:"object"==typeof global&&global.global===global?global:void 0,a=/Macintosh/.test(navigator.userAgent)&&/AppleWebKit/.test(navigator.userAgent)&&!/Safari/.test(navigator.userAgent),g=f.saveAs||("object"!=typeof window||window!==f?function(){}:"download"in HTMLAnchorElement.prototype&&!a?function(b,g,h){var i=f.URL||f.webkitURL,j=document.createElement("a");g=g||b.name||"download",j.download=g,j.rel="noopener","string"==typeof b?(j.href=b,j.origin===location.origin?e(j):d(j.href)?c(b,g,h):e(j,j.target="_blank")):(j.href=i.createObjectURL(b),setTimeout(function(){i.revokeObjectURL(j.href)},4E4),setTimeout(function(){e(j)},0))}:"msSaveOrOpenBlob"in navigator?function(f,g,h){if(g=g||f.name||"download","string"!=typeof f)navigator.msSaveOrOpenBlob(b(f,h),g);else if(d(f))c(f,g,h);else{var i=document.createElement("a");i.href=f,i.target="_blank",setTimeout(function(){e(i)})}}:function(b,d,e,g){if(g=g||open("","_blank"),g&&(g.document.title=g.document.body.innerText="downloading..."),"string"==typeof b)return c(b,d,e);var h="application/octet-stream"===b.type,i=/constructor/i.test(f.HTMLElement)||f.safari,j=/CriOS\/[\d]+/.test(navigator.userAgent);if((j||h&&i||a)&&"undefined"!=typeof FileReader){var k=new FileReader;k.onloadend=function(){var a=k.result;a=j?a:a.replace(/^data:[^;]*;/,"data:attachment/file;"),g?g.location.href=a:location=a,g=null},k.readAsDataURL(b)}else{var l=f.URL||f.webkitURL,m=l.createObjectURL(b);g?g.location=m:location.href=m,g=null,setTimeout(function(){l.revokeObjectURL(m)},4E4)}});f.saveAs=g.saveAs=g,"undefined"!=typeof module&&(module.exports=g)});
|
|
@ -0,0 +1,26 @@
|
|||
function singleItemPlaylist(file,name){
|
||||
let hostUrl = 'http://' + window.location.host
|
||||
let pd = ""
|
||||
pd += '#EXTM3U\n'
|
||||
pd += `#EXTINF: ${name}\n`
|
||||
pd += `${hostUrl}/${file}\n`
|
||||
let blob = new Blob([pd], { endings: "native" });
|
||||
saveAs(blob, `${name}.m3u`);
|
||||
}
|
||||
|
||||
// function createPlaylist(indexSite, id, playlistName = "Playlist", duration = 60) {
|
||||
// let pd = ""
|
||||
// name = playlistName
|
||||
// pd += '#EXTM3U\n'
|
||||
// pd += `#EXTINF: ${duration * 60} | ${name}\n`
|
||||
// pd += `${indexSite}/${id}/v.mp4\n`
|
||||
// return pd
|
||||
// }
|
||||
|
||||
// function playlist(id, name) {
|
||||
// hostUrl = 'https://' + window.location.host
|
||||
// playlistData = createPlaylist(hostUrl, id, name);
|
||||
// let blob = new Blob([playlistData], { endings: "native" });
|
||||
// saveAs(blob, `${name}.m3u`);
|
||||
// }
|
||||
|
|
@ -31,24 +31,23 @@ class Views(
|
|||
):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
self.url_len = SHORT_URL_LEN
|
||||
self.chat_ids = {}
|
||||
|
||||
def generate_alias_id(self, chat):
|
||||
chat_id = chat.id
|
||||
title = chat.title
|
||||
while True:
|
||||
# alias_id = "".join(
|
||||
# [
|
||||
# random.choice(string.ascii_letters + string.digits)
|
||||
# for _ in range(len(str(chat_id)))
|
||||
# ]
|
||||
# )
|
||||
orig_id = f"{title}{chat_id}" # the original id
|
||||
alias_id = base64.urlsafe_b64encode(hashlib.md5(orig_id.encode()).digest())[:SHORT_URL_LEN].decode()
|
||||
|
||||
while True:
|
||||
orig_id = f"{chat_id}" # the original id
|
||||
unique_hash = hashlib.md5(orig_id.encode()).digest()
|
||||
alias_id = base64.urlsafe_b64encode(unique_hash).decode()[:self.url_len]
|
||||
|
||||
if alias_id in self.chat_ids:
|
||||
self.url_len += 1 # increment url_len just incase the hash is already used.
|
||||
continue
|
||||
elif (self.url_len > SHORT_URL_LEN): # reset url_len to initial if hash was unique.
|
||||
self.url_len = SHORT_URL_LEN
|
||||
|
||||
self.chat_ids[alias_id] = {
|
||||
"chat_id": chat_id,
|
||||
|
|
|
@ -59,6 +59,8 @@ class IndexView:
|
|||
insight=get_file_name(m),
|
||||
human_size=get_human_size(m.file.size),
|
||||
url=f"/{alias_id}/{m.id}/view",
|
||||
download=f"{alias_id}/{m.id}/download",
|
||||
vlc = f"{alias_id}/{m.id}/v.mp4",
|
||||
)
|
||||
elif m.message:
|
||||
entry = dict(
|
||||
|
|
|
@ -46,7 +46,12 @@ class ThumbnailView:
|
|||
body = temp.getvalue()
|
||||
else:
|
||||
thumb_pos = int(len(thumbnails) / 2)
|
||||
thumbnail = self.client._get_thumb(thumbnails, thumb_pos)
|
||||
try:
|
||||
thumbnail = self.client._get_thumb(thumbnails, thumb_pos)
|
||||
except Exception as e:
|
||||
logging.debug(e)
|
||||
thumbnail = None
|
||||
|
||||
if not thumbnail or isinstance(thumbnail, types.PhotoSizeEmpty):
|
||||
return web.Response(
|
||||
status=410,
|
||||
|
|
Loading…
Reference in New Issue