Spaces:
Paused
Paused
Upload 40 files
Browse files- .env +10 -0
- .gitattributes +36 -35
- Dockerfile +24 -0
- README.md +11 -11
- bot/__init__.py +15 -0
- bot/__main__.py +31 -0
- bot/__pycache__/__init__.cpython-311.pyc +0 -0
- bot/__pycache__/__main__.cpython-311.pyc +0 -0
- bot/__pycache__/config.cpython-311.pyc +0 -0
- bot/config.py +58 -0
- bot/data.json +1 -0
- bot/modules/__pycache__/decorators.cpython-311.pyc +0 -0
- bot/modules/__pycache__/static.cpython-311.pyc +0 -0
- bot/modules/__pycache__/telegram.cpython-311.pyc +0 -0
- bot/modules/decorators.py +38 -0
- bot/modules/static.py +68 -0
- bot/modules/telegram.py +68 -0
- bot/plugins/__pycache__/callback.cpython-311.pyc +0 -0
- bot/plugins/__pycache__/commands.cpython-311.pyc +0 -0
- bot/plugins/__pycache__/deeplinks.cpython-311.pyc +0 -0
- bot/plugins/__pycache__/files.cpython-311.pyc +0 -0
- bot/plugins/__pycache__/upload.cpython-311.pyc +0 -0
- bot/plugins/callback.py +78 -0
- bot/plugins/commands.py +135 -0
- bot/plugins/data.json +1 -0
- bot/plugins/deeplinks.py +22 -0
- bot/plugins/files.py +110 -0
- bot/plugins/upload.py +193 -0
- bot/server/__init__.py +31 -0
- bot/server/__pycache__/__init__.cpython-311.pyc +0 -0
- bot/server/__pycache__/error.cpython-311.pyc +0 -0
- bot/server/__pycache__/main.cpython-311.pyc +0 -0
- bot/server/error.py +31 -0
- bot/server/main.py +88 -0
- bot/server/templates/link.png +0 -0
- bot/server/templates/mx.png +0 -0
- bot/server/templates/player.html +781 -0
- bot/server/templates/share.png +0 -0
- bot/server/templates/vlc.png +3 -0
- requirements.txt +5 -0
.env
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
BASE_URL=https://privateone-telethon-upload.hf.space
|
| 2 |
+
BIND_ADDRESS=0.0.0.0
|
| 3 |
+
OWNER_ID=708296135
|
| 4 |
+
PORT=7860
|
| 5 |
+
SECRET_CODE_LENGTH=100
|
| 6 |
+
TELEGRAM_API_HASH=371f110baf9d09b9b0bce9766f9a606b
|
| 7 |
+
TELEGRAM_API_ID=4667470
|
| 8 |
+
TELEGRAM_BOT_TOKEN=1857136393:AAFRXz8OGqIH-ga-qb0rM22pwYeVNPmkS00
|
| 9 |
+
TELEGRAM_BOT_USERNAME=@MFBlendersBot
|
| 10 |
+
TELEGRAM_CHANNEL_ID=-1001498849913
|
.gitattributes
CHANGED
|
@@ -1,35 +1,36 @@
|
|
| 1 |
-
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
-
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
bot/server/templates/vlc.png filter=lfs diff=lfs merge=lfs -text
|
Dockerfile
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
FROM python:3.11.1
|
| 3 |
+
RUN useradd -ms /bin/bash admin
|
| 4 |
+
|
| 5 |
+
WORKDIR /app
|
| 6 |
+
COPY . /app
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
COPY requirements.txt ./requirements.txt
|
| 10 |
+
RUN apt-get update
|
| 11 |
+
|
| 12 |
+
#RUN python -m venv venv # Create virtual environment
|
| 13 |
+
#RUN /app/venv/bin/activate &&
|
| 14 |
+
RUN pip install --upgrade pip && pip install --no-cache-dir --upgrade -r requirements.txt
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
#RUN touch /app/event-log.txt && chmod 664 /app/event-log.txt
|
| 18 |
+
RUN chown -R admin:admin /app
|
| 19 |
+
RUN chmod 755 /app
|
| 20 |
+
#EXPOSE 7860
|
| 21 |
+
USER admin
|
| 22 |
+
#CMD ["docker","run","-p","7860:8080"]
|
| 23 |
+
#CMD ["uvicorn","bot.server.main:bp","--host","0.0.0.0","--port","7860"]
|
| 24 |
+
ENTRYPOINT ["python","-m","bot"]
|
README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
| 1 |
-
---
|
| 2 |
-
title: Telethon Upload
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
-
sdk: docker
|
| 7 |
-
pinned: false
|
| 8 |
-
license:
|
| 9 |
-
---
|
| 10 |
-
|
| 11 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Telethon Upload
|
| 3 |
+
emoji: 🐠
|
| 4 |
+
colorFrom: red
|
| 5 |
+
colorTo: gray
|
| 6 |
+
sdk: docker
|
| 7 |
+
pinned: false
|
| 8 |
+
license: lgpl
|
| 9 |
+
---
|
| 10 |
+
|
| 11 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
bot/__init__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from telethon import TelegramClient
|
| 2 |
+
from logging import getLogger
|
| 3 |
+
from logging.config import dictConfig
|
| 4 |
+
from .config import Telegram, LOGGER_CONFIG_JSON
|
| 5 |
+
|
| 6 |
+
dictConfig(LOGGER_CONFIG_JSON)
|
| 7 |
+
|
| 8 |
+
version = 1.5
|
| 9 |
+
logger = getLogger('bot')
|
| 10 |
+
|
| 11 |
+
TelegramBot = TelegramClient(
|
| 12 |
+
session='bot',
|
| 13 |
+
api_id=Telegram.API_ID,
|
| 14 |
+
api_hash=Telegram.API_HASH
|
| 15 |
+
)
|
bot/__main__.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from importlib import import_module
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
from bot import TelegramBot, logger
|
| 4 |
+
from bot.config import Telegram
|
| 5 |
+
from bot.server import server
|
| 6 |
+
|
| 7 |
+
from telethon import types
|
| 8 |
+
def load_plugins():
|
| 9 |
+
count = 0
|
| 10 |
+
for path in Path('bot/plugins').rglob('*.py'):
|
| 11 |
+
import_module(f'bot.plugins.{path.stem}')
|
| 12 |
+
count += 1
|
| 13 |
+
logger.info(f'Loaded {count} {"plugins" if count > 1 else "plugin"}.')
|
| 14 |
+
|
| 15 |
+
if __name__ == '__main__':
|
| 16 |
+
#def main():
|
| 17 |
+
try:
|
| 18 |
+
logger.info('initializing...')
|
| 19 |
+
TelegramBot.loop.create_task(server.serve())
|
| 20 |
+
TelegramBot.start(bot_token=Telegram.BOT_TOKEN)
|
| 21 |
+
logger.info('Telegram client is now started.')
|
| 22 |
+
logger.info('Loading bot plugins...')
|
| 23 |
+
load_plugins()
|
| 24 |
+
logger.info('Bot is now ready!')
|
| 25 |
+
#logger.info(TelegramBot(types.PhoneConnectionWebrtc.to_dict(TelegramBot)))
|
| 26 |
+
TelegramBot.run_until_disconnected()
|
| 27 |
+
except KeyboardInterrupt:
|
| 28 |
+
#pass
|
| 29 |
+
TelegramBot.disconnect()
|
| 30 |
+
logging.info('----------------------- Service Stopped -----------------------')
|
| 31 |
+
|
bot/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (707 Bytes). View file
|
|
|
bot/__pycache__/__main__.cpython-311.pyc
ADDED
|
Binary file (2.1 kB). View file
|
|
|
bot/__pycache__/config.cpython-311.pyc
ADDED
|
Binary file (2.52 kB). View file
|
|
|
bot/config.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from os import environ as env
|
| 2 |
+
import json
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class Telegram:
|
| 6 |
+
|
| 7 |
+
API_ID = int(env.get("TELEGRAM_API_ID"))
|
| 8 |
+
API_HASH = env.get("TELEGRAM_API_HASH")
|
| 9 |
+
OWNER_ID = int(env.get("OWNER_ID"))
|
| 10 |
+
ALLOWED_USER_IDS = json.load(open('/app/bot/data.json'))
|
| 11 |
+
BOT_USERNAME = env.get("TELEGRAM_BOT_USERNAME")
|
| 12 |
+
BOT_TOKEN = env.get("TELEGRAM_BOT_TOKEN")
|
| 13 |
+
CHANNEL_ID = int(env.get("TELEGRAM_CHANNEL_ID"))
|
| 14 |
+
SECRET_CODE_LENGTH = int(env.get("SECRET_CODE_LENGTH"))
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class Server:
|
| 19 |
+
BASE_URL = env.get("BASE_URL")
|
| 20 |
+
BIND_ADDRESS = env.get("BIND_ADDRESS", "0.0.0.0")
|
| 21 |
+
PORT = int(env.get("PORT", 7860))
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
# LOGGING CONFIGURATION
|
| 25 |
+
LOGGER_CONFIG_JSON = {
|
| 26 |
+
'version': 1,
|
| 27 |
+
'formatters': {
|
| 28 |
+
'default': {
|
| 29 |
+
'format': '[%(asctime)s][%(name)s][%(levelname)s] -> %(message)s',
|
| 30 |
+
'datefmt': '%d/%m/%Y %H:%M:%S'
|
| 31 |
+
},
|
| 32 |
+
},
|
| 33 |
+
'handlers': {
|
| 34 |
+
'file_handler': {
|
| 35 |
+
'class': 'logging.FileHandler',
|
| 36 |
+
'filename': 'event-log.txt',
|
| 37 |
+
'formatter': 'default'
|
| 38 |
+
},
|
| 39 |
+
'stream_handler': {
|
| 40 |
+
'class': 'logging.StreamHandler',
|
| 41 |
+
'formatter': 'default'
|
| 42 |
+
}
|
| 43 |
+
},
|
| 44 |
+
'loggers': {
|
| 45 |
+
'uvicorn': {
|
| 46 |
+
'level': 'INFO',
|
| 47 |
+
'handlers': ['file_handler', 'stream_handler']
|
| 48 |
+
},
|
| 49 |
+
'uvicorn.error': {
|
| 50 |
+
'level': 'WARNING',
|
| 51 |
+
'handlers': ['file_handler', 'stream_handler']
|
| 52 |
+
},
|
| 53 |
+
'bot': {
|
| 54 |
+
'level': 'INFO',
|
| 55 |
+
'handlers': ['file_handler', 'stream_handler']
|
| 56 |
+
}
|
| 57 |
+
}
|
| 58 |
+
}
|
bot/data.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"my_array": [708296135]}
|
bot/modules/__pycache__/decorators.cpython-311.pyc
ADDED
|
Binary file (1.61 kB). View file
|
|
|
bot/modules/__pycache__/static.cpython-311.pyc
ADDED
|
Binary file (1.43 kB). View file
|
|
|
bot/modules/__pycache__/telegram.cpython-311.pyc
ADDED
|
Binary file (2.96 kB). View file
|
|
|
bot/modules/decorators.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from telethon.events import NewMessage, CallbackQuery
|
| 2 |
+
from telethon.tl.custom import Message
|
| 3 |
+
from typing import Callable
|
| 4 |
+
from functools import wraps
|
| 5 |
+
from bot.config import Telegram
|
| 6 |
+
from bot.modules.static import *
|
| 7 |
+
def byte_to_gb_mb(size: int) -> str:
|
| 8 |
+
if not size:
|
| 9 |
+
return ""
|
| 10 |
+
power = 2 ** 10
|
| 11 |
+
number = 0
|
| 12 |
+
dict_power_n = {
|
| 13 |
+
0: " ",
|
| 14 |
+
1: "K",
|
| 15 |
+
2: "M",
|
| 16 |
+
3: "G",
|
| 17 |
+
4: "T",
|
| 18 |
+
5: "P"
|
| 19 |
+
}
|
| 20 |
+
while size > power:
|
| 21 |
+
size /= power
|
| 22 |
+
number += 1
|
| 23 |
+
return str(round(size, 2)) + " " + dict_power_n[number] + 'B'
|
| 24 |
+
def verify_user(private: bool = False):
|
| 25 |
+
|
| 26 |
+
def decorator(func: Callable):
|
| 27 |
+
@wraps(func)
|
| 28 |
+
async def wrapper(update: NewMessage.Event | CallbackQuery.Event):
|
| 29 |
+
if private and not update.is_private:
|
| 30 |
+
return
|
| 31 |
+
|
| 32 |
+
chat_id = str(update.chat_id)
|
| 33 |
+
|
| 34 |
+
if not Telegram.ALLOWED_USER_IDS or chat_id in Telegram.ALLOWED_USER_IDS:
|
| 35 |
+
return await func(update)
|
| 36 |
+
|
| 37 |
+
return wrapper
|
| 38 |
+
return decorator
|
bot/modules/static.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
WelcomeText = \
|
| 2 |
+
"""
|
| 3 |
+
Hi **%(first_name)s**, send me a file or add me as an admin to any channel to instantly generate file links.
|
| 4 |
+
|
| 5 |
+
Add me to your channel to instantly generate links for any downloadable media. Once received, I will automatically attach appropriate buttons to the post containing the URL. If you want me to ignore a given post, you can insert `#pass` in the post.
|
| 6 |
+
|
| 7 |
+
- /start to get this message.
|
| 8 |
+
- /info to get user info.
|
| 9 |
+
- /log to get bot logs. (admin only!)
|
| 10 |
+
"""
|
| 11 |
+
|
| 12 |
+
UserInfoText = \
|
| 13 |
+
"""
|
| 14 |
+
**First Name:**
|
| 15 |
+
`{sender.first_name}`
|
| 16 |
+
|
| 17 |
+
**Last Name:**
|
| 18 |
+
`{sender.last_name}`
|
| 19 |
+
|
| 20 |
+
**User ID:**
|
| 21 |
+
`{sender.id}`
|
| 22 |
+
|
| 23 |
+
**Username:**
|
| 24 |
+
`@{sender.username}`
|
| 25 |
+
"""
|
| 26 |
+
|
| 27 |
+
FileLinksText = \
|
| 28 |
+
"""
|
| 29 |
+
**Download Link:**
|
| 30 |
+
`%(dl_link)s`
|
| 31 |
+
**Telegram File:**
|
| 32 |
+
`%(tg_link)s`
|
| 33 |
+
"""
|
| 34 |
+
|
| 35 |
+
MediaLinksText = \
|
| 36 |
+
"""
|
| 37 |
+
**Download Link:**
|
| 38 |
+
`%(dl_link)s`
|
| 39 |
+
**Stream Link:**
|
| 40 |
+
`%(stream_link)s`
|
| 41 |
+
**Telegram File:**
|
| 42 |
+
`%(tg_link)s`
|
| 43 |
+
"""
|
| 44 |
+
|
| 45 |
+
InvalidQueryText = \
|
| 46 |
+
"""
|
| 47 |
+
Query data mismatched.
|
| 48 |
+
"""
|
| 49 |
+
|
| 50 |
+
MessageNotExist = \
|
| 51 |
+
"""
|
| 52 |
+
File revoked or not exist.
|
| 53 |
+
"""
|
| 54 |
+
|
| 55 |
+
LinkRevokedText = \
|
| 56 |
+
"""
|
| 57 |
+
The link has been revoked. It may take some time for the changes to take effect.
|
| 58 |
+
"""
|
| 59 |
+
|
| 60 |
+
InvalidPayloadText = \
|
| 61 |
+
"""
|
| 62 |
+
Invalid payload.
|
| 63 |
+
"""
|
| 64 |
+
|
| 65 |
+
MediaTypeNotSupportedText = \
|
| 66 |
+
"""
|
| 67 |
+
Sorry, this media type is not supported.
|
| 68 |
+
"""
|
bot/modules/telegram.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from telethon.events import NewMessage
|
| 2 |
+
from telethon.tl.custom import Message
|
| 3 |
+
from datetime import datetime
|
| 4 |
+
from mimetypes import guess_type
|
| 5 |
+
from bot import TelegramBot
|
| 6 |
+
from bot.config import Telegram
|
| 7 |
+
from bot.server.error import abort
|
| 8 |
+
|
| 9 |
+
async def get_message(message_id: int) -> Message | None:
|
| 10 |
+
message = None
|
| 11 |
+
|
| 12 |
+
try:
|
| 13 |
+
message = await TelegramBot.get_messages(Telegram.CHANNEL_ID, ids=message_id)
|
| 14 |
+
except Exception:
|
| 15 |
+
pass
|
| 16 |
+
|
| 17 |
+
return message
|
| 18 |
+
|
| 19 |
+
async def send_message(message:Message, send_to:int = Telegram.CHANNEL_ID) -> Message:
|
| 20 |
+
message.forward
|
| 21 |
+
return await TelegramBot.send_message(
|
| 22 |
+
entity=send_to,
|
| 23 |
+
message=message
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
def filter_files(update: NewMessage.Event | Message):
|
| 27 |
+
return bool(
|
| 28 |
+
(
|
| 29 |
+
update.document
|
| 30 |
+
or update.photo
|
| 31 |
+
or update.video
|
| 32 |
+
or update.video_note
|
| 33 |
+
or update.audio
|
| 34 |
+
or update.gif
|
| 35 |
+
)
|
| 36 |
+
and not update.sticker
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
def get_file_properties(message: Message):
|
| 40 |
+
file_name = message.file.name
|
| 41 |
+
file_size = message.file.size or 0
|
| 42 |
+
mime_type = message.file.mime_type
|
| 43 |
+
|
| 44 |
+
if not file_name:
|
| 45 |
+
attributes = {
|
| 46 |
+
'video': 'mp4',
|
| 47 |
+
'audio': 'mp3',
|
| 48 |
+
'voice': 'ogg',
|
| 49 |
+
'photo': 'jpg',
|
| 50 |
+
'video_note': 'mp4'
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
for attribute in attributes:
|
| 54 |
+
media = getattr(message, attribute, None)
|
| 55 |
+
if media:
|
| 56 |
+
file_type, file_format = attribute, attributes[attribute]
|
| 57 |
+
break
|
| 58 |
+
|
| 59 |
+
if not media:
|
| 60 |
+
abort(400, 'Invalid media type.')
|
| 61 |
+
|
| 62 |
+
date = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
| 63 |
+
file_name = f'{file_type}-{date}.{file_format}'
|
| 64 |
+
|
| 65 |
+
if not mime_type:
|
| 66 |
+
mime_type = guess_type(file_name)[0] or 'application/octet-stream'
|
| 67 |
+
|
| 68 |
+
return file_name, file_size, mime_type
|
bot/plugins/__pycache__/callback.cpython-311.pyc
ADDED
|
Binary file (4.43 kB). View file
|
|
|
bot/plugins/__pycache__/commands.cpython-311.pyc
ADDED
|
Binary file (5.63 kB). View file
|
|
|
bot/plugins/__pycache__/deeplinks.cpython-311.pyc
ADDED
|
Binary file (1.78 kB). View file
|
|
|
bot/plugins/__pycache__/files.cpython-311.pyc
ADDED
|
Binary file (5.76 kB). View file
|
|
|
bot/plugins/__pycache__/upload.cpython-311.pyc
ADDED
|
Binary file (5.25 kB). View file
|
|
|
bot/plugins/callback.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from telethon.events import CallbackQuery
|
| 2 |
+
from bot import TelegramBot
|
| 3 |
+
from bot.modules.decorators import verify_user,byte_to_gb_mb
|
| 4 |
+
from bot.modules.static import *
|
| 5 |
+
from bot.modules.telegram import get_message, get_file_properties
|
| 6 |
+
from bot.config import Telegram, Server
|
| 7 |
+
|
| 8 |
+
'''async def upload_file_in_chunks(TelegramBot, chat_id, file_path, chunk_size_kb):
|
| 9 |
+
file_id = None
|
| 10 |
+
total_parts = math.ceil(file_size / (chunk_size_kb * 1024))
|
| 11 |
+
current_part = 1
|
| 12 |
+
with open(file_path, "rb") as f:
|
| 13 |
+
while True:
|
| 14 |
+
data = f.read(chunk_size_kb * 1024)
|
| 15 |
+
if not data:
|
| 16 |
+
break
|
| 17 |
+
# Upload the current chunk
|
| 18 |
+
file_part = await client.upload_file_part(file_id, current_part, data)
|
| 19 |
+
if not file_id:
|
| 20 |
+
file_id = file_part.file_id
|
| 21 |
+
current_part += 1
|
| 22 |
+
# Show progress if desired
|
| 23 |
+
progress = current_part / total_parts * 100
|
| 24 |
+
print(f"Uploaded {progress:.2f}%")
|
| 25 |
+
# Complete the upload
|
| 26 |
+
complete_file = await client.upload_file_completed(file_id, total_parts)
|
| 27 |
+
# Send the uploaded file to the chat
|
| 28 |
+
await client.send_file(chat_id, complete_file)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
'''
|
| 32 |
+
|
| 33 |
+
@TelegramBot.on(CallbackQuery(pattern=r'^rm_'))
|
| 34 |
+
#@verify_user(private=True)
|
| 35 |
+
async def delete_file(event: CallbackQuery.Event):
|
| 36 |
+
query_data = event.query.data.decode().split('_')
|
| 37 |
+
|
| 38 |
+
if len(query_data) != 3:
|
| 39 |
+
return await event.answer(InvalidQueryText, alert=True)
|
| 40 |
+
|
| 41 |
+
message = await get_message(int(query_data[1]))
|
| 42 |
+
|
| 43 |
+
if not message:
|
| 44 |
+
return await event.answer(MessageNotExist, alert=True)
|
| 45 |
+
|
| 46 |
+
await message.delete()
|
| 47 |
+
|
| 48 |
+
return await event.answer(LinkRevokedText, alert=True)
|
| 49 |
+
|
| 50 |
+
@TelegramBot.on(CallbackQuery(pattern=r'^up_'))
|
| 51 |
+
#@verify_user(private=True)
|
| 52 |
+
async def up_file(event:CallbackQuery.Event):
|
| 53 |
+
query_data = event.query.data.decode().split('_')
|
| 54 |
+
|
| 55 |
+
if len(query_data) != 5:
|
| 56 |
+
return await event.answer(InvalidQueryText, alert=True)
|
| 57 |
+
message_id=int(query_data[1])
|
| 58 |
+
event_id=int(query_data[3])
|
| 59 |
+
event_peer=int(query_data[4])
|
| 60 |
+
message = await get_message(int(query_data[1]))
|
| 61 |
+
main_event = await TelegramBot.get_messages(event_peer, ids=event_id)
|
| 62 |
+
|
| 63 |
+
#print(event_id,event_peer)
|
| 64 |
+
#print(main_event)
|
| 65 |
+
code = str(query_data[2])
|
| 66 |
+
def callback(current, total,event_peer,event_id):
|
| 67 |
+
print('Uploaded', current, 'out of', total,'bytes: {:.2%}'.format(current / total))
|
| 68 |
+
TelegramBot.edit_message(event_peer, event_id, f'Uploaded, {current}, out of, {total}')
|
| 69 |
+
if not message or code != message.raw_text:
|
| 70 |
+
return await event.answer(MessageNotExist, alert=True)
|
| 71 |
+
else:
|
| 72 |
+
dl_link = f'{Server.BASE_URL}/dl/{message_id}?code={code}'
|
| 73 |
+
file_name, file_size, mime_type = get_file_properties(main_event)
|
| 74 |
+
await main_event.reply(
|
| 75 |
+
message= f'**File name:{file_name} \nFile size:{byte_to_gb_mb(file_size)} \nID:{message_id} \nAccess Code:{code} \n*If you want to upload with different name please send name with reply of the message \n reply with n for original file name ***',
|
| 76 |
+
parse_mode="Markdown"
|
| 77 |
+
)
|
| 78 |
+
|
bot/plugins/commands.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from telethon import Button
|
| 2 |
+
from telethon import functions, types,utils,helpers,custom
|
| 3 |
+
from telethon.events import NewMessage
|
| 4 |
+
from telethon.tl.custom.message import Message
|
| 5 |
+
from bot import TelegramBot
|
| 6 |
+
from bot.config import Telegram
|
| 7 |
+
from bot.modules.static import *
|
| 8 |
+
from bot.modules.decorators import verify_user
|
| 9 |
+
from bot.plugins.upload import upload_chunk
|
| 10 |
+
from bot.modules.telegram import get_message, get_file_properties
|
| 11 |
+
import json
|
| 12 |
+
|
| 13 |
+
async def refresh():
|
| 14 |
+
with open("/app/bot/data.json", "r") as infile:
|
| 15 |
+
Telegram.ALLOWED_USER_IDS = json.load(infile)
|
| 16 |
+
# Add a value
|
| 17 |
+
async def add_id(id):
|
| 18 |
+
await refresh()
|
| 19 |
+
if id in Telegram.ALLOWED_USER_IDS["my_array"]:
|
| 20 |
+
await TelegramBot.send_message(id, "You are already exists")
|
| 21 |
+
|
| 22 |
+
else:
|
| 23 |
+
Telegram.ALLOWED_USER_IDS["my_array"].append(id)
|
| 24 |
+
await TelegramBot.send_message(id, "Now you are in whitelist !!")
|
| 25 |
+
with open("/app/bot/data.json", "w") as outfile:
|
| 26 |
+
json.dump(Telegram.ALLOWED_USER_IDS, outfile)
|
| 27 |
+
await refresh()
|
| 28 |
+
|
| 29 |
+
#data["my_array"].append("new_value")
|
| 30 |
+
|
| 31 |
+
# Remove a value (index 2 in this example)
|
| 32 |
+
async def del_id(id):
|
| 33 |
+
await refresh()
|
| 34 |
+
for i in range(len(Telegram.ALLOWED_USER_IDS["my_array"])):
|
| 35 |
+
if Telegram.ALLOWED_USER_IDS["my_array"][i] == id:
|
| 36 |
+
Telegram.ALLOWED_USER_IDS["my_array"].pop(i)
|
| 37 |
+
await TelegramBot.send_message(id, "You are removed from whitelist")
|
| 38 |
+
break
|
| 39 |
+
with open("/app/bot/data.json", "w") as outfile:
|
| 40 |
+
json.dump(Telegram.ALLOWED_USER_IDS, outfile)
|
| 41 |
+
await refresh()
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
@TelegramBot.on(NewMessage(incoming=True))
|
| 48 |
+
#@verify_user(private=True)
|
| 49 |
+
async def reply_message(event: NewMessage.Event | Message):
|
| 50 |
+
if event.message.reply_to:
|
| 51 |
+
#print('this is reply message', event)
|
| 52 |
+
message=await TelegramBot.get_messages(event.message.peer_id.user_id, ids=event.message.reply_to.reply_to_msg_id)
|
| 53 |
+
event_message=str(event.message.message)
|
| 54 |
+
message_text=message.message
|
| 55 |
+
parts = message_text.split("\n")
|
| 56 |
+
filename = str(parts[0].split(":")[1].strip()) # Remove leading/trailing spaces
|
| 57 |
+
#size = int(parts[1].split(":")[1].strip())
|
| 58 |
+
message_id = int(parts[2].split(":")[1].strip())
|
| 59 |
+
code = str(parts[3].split(":")[1].strip())
|
| 60 |
+
file = await get_message(message_id=int(message_id))
|
| 61 |
+
file_name, file_size, mime_type = get_file_properties(file)
|
| 62 |
+
|
| 63 |
+
#print(filename,size,message_id,code)
|
| 64 |
+
result,new_name=await upload_chunk(message_id,code,event.message.id,event_message,event.message.peer_id.user_id,event.message.reply_to.reply_to_msg_id)
|
| 65 |
+
attributes, mime_type = utils.get_attributes(
|
| 66 |
+
new_name,
|
| 67 |
+
mime_type=mime_type,
|
| 68 |
+
attributes=None,
|
| 69 |
+
force_document=True,
|
| 70 |
+
voice_note=None,
|
| 71 |
+
video_note=None,
|
| 72 |
+
supports_streaming=True,
|
| 73 |
+
thumb=None
|
| 74 |
+
)
|
| 75 |
+
#await TelegramBot.send_file(event.message.peer_id.user_id, file=result,force_document=True)
|
| 76 |
+
await TelegramBot(functions.messages.SendMediaRequest(
|
| 77 |
+
peer=event.message.peer_id.user_id,
|
| 78 |
+
media=types.InputMediaUploadedDocument(
|
| 79 |
+
file=result,
|
| 80 |
+
mime_type=mime_type,
|
| 81 |
+
attributes=attributes,
|
| 82 |
+
|
| 83 |
+
),
|
| 84 |
+
message='File Uploaded successfully !'
|
| 85 |
+
))
|
| 86 |
+
#print(result.stringify())
|
| 87 |
+
|
| 88 |
+
#print(result)
|
| 89 |
+
'''if event_message.lower() =='n':
|
| 90 |
+
|
| 91 |
+
else:
|
| 92 |
+
message_text=message.message
|
| 93 |
+
parts = message_text.split("\n")
|
| 94 |
+
filename = event_message # Remove leading/trailing spaces
|
| 95 |
+
size = int(parts[1].split(":")[1].strip())
|
| 96 |
+
message_id = int(parts[2].split(":")[1].strip())
|
| 97 |
+
code = str(parts[3].split(":")[1].strip())
|
| 98 |
+
print(filename,size,message_id,code)
|
| 99 |
+
upload_chunk(filename,message_id,code,event.message.id)'''
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
@TelegramBot.on(NewMessage(incoming=True, pattern=r'^/start$'))
|
| 108 |
+
#@verify_user(private=True)
|
| 109 |
+
async def welcome(event: NewMessage.Event | Message):
|
| 110 |
+
await event.reply(
|
| 111 |
+
message=WelcomeText % {'first_name': event.sender.first_name},
|
| 112 |
+
buttons=[
|
| 113 |
+
[
|
| 114 |
+
Button.url('Add to Channel', f'https://t.me/{Telegram.BOT_USERNAME}?startchannel&admin=post_messages+edit_messages+delete_messages')
|
| 115 |
+
]
|
| 116 |
+
]
|
| 117 |
+
)
|
| 118 |
+
|
| 119 |
+
@TelegramBot.on(NewMessage(incoming=True, pattern=r'^/info$'))
|
| 120 |
+
#@verify_user(private=True)
|
| 121 |
+
async def user_info(event: Message):
|
| 122 |
+
await event.reply(UserInfoText.format(sender=event.sender))
|
| 123 |
+
|
| 124 |
+
@TelegramBot.on(NewMessage(chats=Telegram.OWNER_ID, incoming=True, pattern=r'^/log$'))
|
| 125 |
+
async def send_log(event: NewMessage.Event | Message):
|
| 126 |
+
await event.reply(file='event-log.txt')
|
| 127 |
+
@TelegramBot.on(NewMessage(incoming=True, pattern=r'^/in$'))
|
| 128 |
+
async def add_user(event: NewMessage.Event | Message):
|
| 129 |
+
await add_id(event.sender.id)
|
| 130 |
+
|
| 131 |
+
@TelegramBot.on(NewMessage(incoming=True, pattern=r'^/out$'))
|
| 132 |
+
async def add_user(event: NewMessage.Event | Message):
|
| 133 |
+
await del_id(event.sender.id)
|
| 134 |
+
|
| 135 |
+
|
bot/plugins/data.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"my_array": [708296135]}
|
bot/plugins/deeplinks.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from telethon.events import NewMessage
|
| 2 |
+
from telethon.tl.custom import Message
|
| 3 |
+
from bot import TelegramBot
|
| 4 |
+
from bot.modules.decorators import verify_user
|
| 5 |
+
from bot.modules.telegram import get_message, send_message
|
| 6 |
+
from bot.modules.static import *
|
| 7 |
+
|
| 8 |
+
@TelegramBot.on(NewMessage(incoming=True, pattern=r'^/start file_'))
|
| 9 |
+
#@verify_user(private=True)
|
| 10 |
+
async def send_file(event: NewMessage.Event | Message):
|
| 11 |
+
payload = event.raw_text.split()[-1].split('_')
|
| 12 |
+
|
| 13 |
+
if len(payload) != 3:
|
| 14 |
+
return await event.reply(InvalidPayloadText)
|
| 15 |
+
|
| 16 |
+
message = await get_message(int(payload[1]))
|
| 17 |
+
|
| 18 |
+
if not message:
|
| 19 |
+
return await event.reply(MessageNotExist)
|
| 20 |
+
|
| 21 |
+
message.raw_text = ''
|
| 22 |
+
await send_message(message, send_to=event.chat_id)
|
bot/plugins/files.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from telethon import Button
|
| 2 |
+
from telethon.events import NewMessage
|
| 3 |
+
from telethon.errors import MessageAuthorRequiredError, MessageNotModifiedError, MessageIdInvalidError
|
| 4 |
+
from telethon.tl.custom import Message
|
| 5 |
+
from secrets import token_hex
|
| 6 |
+
from bot import TelegramBot
|
| 7 |
+
from bot.config import Telegram, Server
|
| 8 |
+
from bot.modules.decorators import verify_user
|
| 9 |
+
from bot.modules.telegram import send_message, filter_files
|
| 10 |
+
from bot.modules.static import *
|
| 11 |
+
|
| 12 |
+
@TelegramBot.on(NewMessage(incoming=True, func=filter_files))
|
| 13 |
+
#@verify_user(private=True)
|
| 14 |
+
async def user_file_handler(event: NewMessage.Event | Message):
|
| 15 |
+
secret_code = token_hex(Telegram.SECRET_CODE_LENGTH)
|
| 16 |
+
event.message.text = f'`{secret_code}`'
|
| 17 |
+
message = await send_message(event.message)
|
| 18 |
+
message_id = message.id
|
| 19 |
+
#print(event)
|
| 20 |
+
|
| 21 |
+
event_id=event.message.id
|
| 22 |
+
event_peer=event.message.peer_id.user_id
|
| 23 |
+
dl_link = f'{Server.BASE_URL}/dl/{message_id}?code={secret_code}'
|
| 24 |
+
tg_link = f'{Server.BASE_URL}/file/{message_id}?code={secret_code}'
|
| 25 |
+
deep_link = f'https://t.me/{Telegram.BOT_USERNAME}?start=file_{message_id}_{secret_code}'
|
| 26 |
+
|
| 27 |
+
if (event.document and 'video' in event.document.mime_type) or event.video:
|
| 28 |
+
stream_link = f'{Server.BASE_URL}/stream/{message_id}?code={secret_code}'
|
| 29 |
+
await event.reply(
|
| 30 |
+
message= MediaLinksText % {'dl_link': dl_link, 'tg_link': tg_link, 'tg_link': tg_link, 'stream_link': stream_link},
|
| 31 |
+
buttons=[
|
| 32 |
+
[
|
| 33 |
+
Button.url('Download', dl_link),
|
| 34 |
+
Button.url('Stream', stream_link)
|
| 35 |
+
],
|
| 36 |
+
[
|
| 37 |
+
Button.inline('Upload', f'up_{message_id}_{secret_code}_{event_id}_{event_peer}')
|
| 38 |
+
],
|
| 39 |
+
[
|
| 40 |
+
Button.url('Get File', deep_link),
|
| 41 |
+
Button.inline('Revoke', f'rm_{message_id}_{secret_code}')
|
| 42 |
+
]
|
| 43 |
+
]
|
| 44 |
+
)
|
| 45 |
+
else:
|
| 46 |
+
await event.reply(
|
| 47 |
+
message=FileLinksText % {'dl_link': dl_link, 'tg_link': tg_link},
|
| 48 |
+
buttons=[
|
| 49 |
+
[
|
| 50 |
+
Button.url('Download', dl_link),
|
| 51 |
+
Button.url('Get File', deep_link)
|
| 52 |
+
],
|
| 53 |
+
[
|
| 54 |
+
Button.inline('Upload', f'up_{message_id}_{secret_code}_{event_id}_{event_peer}')
|
| 55 |
+
],
|
| 56 |
+
[
|
| 57 |
+
Button.inline('Revoke', f'rm_{message_id}_{secret_code}')
|
| 58 |
+
]
|
| 59 |
+
]
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
@TelegramBot.on(NewMessage(incoming=True, func=filter_files, forwards=False))
|
| 63 |
+
#@verify_user()
|
| 64 |
+
async def channel_file_handler(event: NewMessage.Event | Message):
|
| 65 |
+
secret_code = token_hex(Telegram.SECRET_CODE_LENGTH)
|
| 66 |
+
event.message.text = f"`{secret_code}`"
|
| 67 |
+
message = await send_message(event.message)
|
| 68 |
+
message_id = message.id
|
| 69 |
+
event_id=event.message.id
|
| 70 |
+
event_peer=event.message.peer_id.user_id
|
| 71 |
+
#print(event)
|
| 72 |
+
#print(event.message.id,event.message.peer_id.user_id)
|
| 73 |
+
dl_link = f"{Server.BASE_URL}/dl/{message_id}?code={secret_code}"
|
| 74 |
+
tg_link = f"{Server.BASE_URL}/file/{message_id}?code={secret_code}"
|
| 75 |
+
|
| 76 |
+
if (event.document and "video" in event.document.mime_type) or event.video:
|
| 77 |
+
stream_link = f"{Server.BASE_URL}/stream/{message_id}?code={secret_code}"
|
| 78 |
+
|
| 79 |
+
try:
|
| 80 |
+
await event.edit(
|
| 81 |
+
buttons=[
|
| 82 |
+
[Button.url("Download", dl_link), Button.url("Stream", stream_link)],
|
| 83 |
+
[
|
| 84 |
+
Button.inline('Upload', f'up_{message_id}_{secret_code}_{event_id}_{event_peer}')
|
| 85 |
+
],
|
| 86 |
+
[Button.url("Get File", tg_link)],
|
| 87 |
+
]
|
| 88 |
+
)
|
| 89 |
+
except (
|
| 90 |
+
MessageAuthorRequiredError,
|
| 91 |
+
MessageIdInvalidError,
|
| 92 |
+
MessageNotModifiedError,
|
| 93 |
+
):
|
| 94 |
+
pass
|
| 95 |
+
else:
|
| 96 |
+
try:
|
| 97 |
+
await event.edit(
|
| 98 |
+
buttons=[
|
| 99 |
+
[Button.url("Download", dl_link), Button.url("Get File", tg_link)],
|
| 100 |
+
[
|
| 101 |
+
Button.inline('Upload', f'up_{message_id}_{secret_code}')
|
| 102 |
+
],
|
| 103 |
+
]
|
| 104 |
+
)
|
| 105 |
+
except (
|
| 106 |
+
MessageAuthorRequiredError,
|
| 107 |
+
MessageIdInvalidError,
|
| 108 |
+
MessageNotModifiedError,
|
| 109 |
+
):
|
| 110 |
+
pass
|
bot/plugins/upload.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import hashlib
|
| 2 |
+
import io
|
| 3 |
+
import requests
|
| 4 |
+
from bot import TelegramBot
|
| 5 |
+
from bot.modules.decorators import verify_user,byte_to_gb_mb
|
| 6 |
+
from telethon import functions, types,utils,helpers,custom,errors
|
| 7 |
+
from bot.config import Telegram, Server
|
| 8 |
+
from math import ceil, floor
|
| 9 |
+
from bot.modules.telegram import get_message, get_file_properties
|
| 10 |
+
MIN_CHUNK_SIZE = 4096
|
| 11 |
+
async def callback(current,current_part,part_size,part_count,total,event_peer,event_id,file_name):
|
| 12 |
+
c=int(30*current_part//part_count)
|
| 13 |
+
progress_bar = '#' * c + '-' * (30 - c)
|
| 14 |
+
progress_text = f'**File name:{file_name} \nFile size:{byte_to_gb_mb(total)} \nUploading: {int(100*current_part//part_count)}%\n[{progress_bar}]'
|
| 15 |
+
print(current_part,part_count,part_size,'Uploaded', current, 'out of', total,'bytes: {:.2%}'.format(current / total))
|
| 16 |
+
try:
|
| 17 |
+
await TelegramBot.edit_message(event_peer, event_id, progress_text)
|
| 18 |
+
|
| 19 |
+
except errors.rpcerrorlist.MessageNotModifiedError as e:
|
| 20 |
+
print(f"Error: {e}")
|
| 21 |
+
except Exception as e: # Catch other potential errors
|
| 22 |
+
print(f"An unexpected error occurred: {e}")
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
async def upload_chunk(message_id,code,event_id,event_message,peer_id,mesage_update_id):
|
| 26 |
+
file_ = await get_message(message_id=int(message_id))
|
| 27 |
+
file_name, file_size, mime_type = get_file_properties(file_)
|
| 28 |
+
info = utils._get_file_info(file_)
|
| 29 |
+
dc_id = info.dc_id
|
| 30 |
+
|
| 31 |
+
file_size = info.size
|
| 32 |
+
|
| 33 |
+
file = info.location
|
| 34 |
+
|
| 35 |
+
part_size_kb = utils.get_appropriated_part_size(file_size)
|
| 36 |
+
|
| 37 |
+
part_size = int(part_size_kb * 1024)
|
| 38 |
+
if part_size % MIN_CHUNK_SIZE != 0:
|
| 39 |
+
raise ValueError(
|
| 40 |
+
'The part size must be evenly divisible by 4096.')
|
| 41 |
+
|
| 42 |
+
if part_size_kb > 512:
|
| 43 |
+
raise ValueError('The part size must be less or equal to 512KB')
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
if part_size % 1024 != 0:
|
| 47 |
+
raise ValueError(
|
| 48 |
+
'The part size must be evenly divisible by 1024')
|
| 49 |
+
|
| 50 |
+
# Set a default file name if None was specified
|
| 51 |
+
file_id = helpers.generate_random_long()
|
| 52 |
+
|
| 53 |
+
if event_message.lower() =='n':
|
| 54 |
+
filename=file_name
|
| 55 |
+
else:
|
| 56 |
+
ext = utils._get_extension(file_name)
|
| 57 |
+
filename = event_message+ext
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
is_big = file_size > 10 * 1024 * 1024
|
| 61 |
+
hash_md5 = hashlib.md5()
|
| 62 |
+
|
| 63 |
+
part_count = (file_size + part_size - 1) // part_size
|
| 64 |
+
until_bytes = file_size - 1
|
| 65 |
+
from_bytes=0
|
| 66 |
+
until_bytes = min(until_bytes, file_size - 1)
|
| 67 |
+
offset = from_bytes - (from_bytes % part_size)
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
pos =0
|
| 71 |
+
part_index=0
|
| 72 |
+
#for part_index in range(part_count):
|
| 73 |
+
|
| 74 |
+
async for chunk in TelegramBot.iter_download(file, offset=offset, chunk_size=part_size, stride=part_size, file_size=file_size):
|
| 75 |
+
|
| 76 |
+
#limit = part_size
|
| 77 |
+
|
| 78 |
+
if type(chunk)!="<class 'bytes'>":
|
| 79 |
+
chunk=bytes(chunk)
|
| 80 |
+
#chunk = await TelegramBot(functions.upload.GetFileRequest(
|
| 81 |
+
#precise=True, location=file, offset=pos, limit=limit
|
| 82 |
+
# ))
|
| 83 |
+
if not chunk:
|
| 84 |
+
|
| 85 |
+
break
|
| 86 |
+
|
| 87 |
+
elif part_index >= part_count:
|
| 88 |
+
|
| 89 |
+
break
|
| 90 |
+
else:
|
| 91 |
+
|
| 92 |
+
if not is_big:
|
| 93 |
+
# Bit odd that MD5 is only needed for small files and not
|
| 94 |
+
# big ones with more chance for corruption, but that's
|
| 95 |
+
# what Telegram wants.
|
| 96 |
+
hash_md5.update(chunk)
|
| 97 |
+
|
| 98 |
+
# The SavePartRequest is different depending on whether
|
| 99 |
+
# the file is too large or not (over or less than 10MB)
|
| 100 |
+
if is_big:
|
| 101 |
+
await TelegramBot(functions.upload.SaveBigFilePartRequest(
|
| 102 |
+
file_id, part_index, part_count, chunk))
|
| 103 |
+
else:
|
| 104 |
+
await TelegramBot(functions.upload.SaveFilePartRequest(
|
| 105 |
+
file_id, part_index, chunk))
|
| 106 |
+
|
| 107 |
+
#yield chunk
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
pos += len(chunk)
|
| 113 |
+
part_index += 1
|
| 114 |
+
await callback(pos,part_index,part_size,part_count,file_size,peer_id,mesage_update_id,filename)
|
| 115 |
+
if is_big:
|
| 116 |
+
return types.InputFileBig(file_id, part_count, file_name),filename
|
| 117 |
+
else:
|
| 118 |
+
return custom.InputSizedFile(
|
| 119 |
+
file_id, part_count, file_name, md5=hash_md5, size=file_size
|
| 120 |
+
),filename
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
''' if not chunk:
|
| 124 |
+
break
|
| 125 |
+
elif part_count == 1:
|
| 126 |
+
if not is_big:
|
| 127 |
+
# Bit odd that MD5 is only needed for small files and not
|
| 128 |
+
# big ones with more chance for corruption, but that's
|
| 129 |
+
# what Telegram wants.
|
| 130 |
+
hash_md5.update(chunk[first_part_cut:last_part_cut])
|
| 131 |
+
if is_big:
|
| 132 |
+
request = functions.upload.SaveBigFilePartRequest(file_id=file_id,file_part=current_part,file_total_parts=part_count,bytes=chunk[first_part_cut:last_part_cut])
|
| 133 |
+
else:
|
| 134 |
+
request = functions.upload.SaveFilePartRequest(file_id=file_id,file_part=current_part,bytes=chunk[first_part_cut:last_part_cut])
|
| 135 |
+
#yield chunk[first_part_cut:last_part_cut]
|
| 136 |
+
elif current_part == 0:
|
| 137 |
+
if not is_big:
|
| 138 |
+
# Bit odd that MD5 is only needed for small files and not
|
| 139 |
+
# big ones with more chance for corruption, but that's
|
| 140 |
+
# what Telegram wants.
|
| 141 |
+
hash_md5.update(chunk[first_part_cut:])
|
| 142 |
+
if is_big:
|
| 143 |
+
request = functions.upload.SaveBigFilePartRequest(
|
| 144 |
+
file_id=file_id,file_part=current_part,file_total_parts=part_count,bytes=chunk[first_part_cut:])
|
| 145 |
+
#file_id, current_part, part_count, chunk[first_part_cut:])
|
| 146 |
+
else:
|
| 147 |
+
request = functions.upload.SaveFilePartRequest(
|
| 148 |
+
file_id=file_id,file_part=current_part,bytes=chunk[first_part_cut:])
|
| 149 |
+
#yield chunk[first_part_cut:]
|
| 150 |
+
elif current_part == part_count-1:
|
| 151 |
+
if not is_big:
|
| 152 |
+
# Bit odd that MD5 is only needed for small files and not
|
| 153 |
+
# big ones with more chance for corruption, but that's
|
| 154 |
+
# what Telegram wants.
|
| 155 |
+
hash_md5.update(chunk[:last_part_cut])
|
| 156 |
+
if is_big:
|
| 157 |
+
request = functions.upload.SaveBigFilePartRequest(
|
| 158 |
+
file_id=file_id,file_part=current_part,file_total_parts=part_count,bytes=chunk[:last_part_cut])
|
| 159 |
+
#file_id, current_part, part_count, chunk[:last_part_cut])
|
| 160 |
+
else:
|
| 161 |
+
request = functions.upload.SaveFilePartRequest(
|
| 162 |
+
file_id=file_id,file_part=current_part,bytes=chunk[:last_part_cut])
|
| 163 |
+
|
| 164 |
+
#yield chunk[:last_part_cut]
|
| 165 |
+
else:
|
| 166 |
+
if not is_big:
|
| 167 |
+
# Bit odd that MD5 is only needed for small files and not
|
| 168 |
+
# big ones with more chance for corruption, but that's
|
| 169 |
+
# what Telegram wants.
|
| 170 |
+
hash_md5.update(chunk)
|
| 171 |
+
if is_big:
|
| 172 |
+
request = functions.upload.SaveBigFilePartRequest(
|
| 173 |
+
file_id=file_id,file_part=current_part,file_total_parts=part_count,bytes=chunk)
|
| 174 |
+
#file_id, current_part, part_count, chunk)
|
| 175 |
+
else:
|
| 176 |
+
request = functions.upload.SaveFilePartRequest(
|
| 177 |
+
file_id=file_id,file_part=current_part,bytes=chunk)
|
| 178 |
+
#file_id, current_part, chunk)
|
| 179 |
+
#yield chunk
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
current_part += 1
|
| 184 |
+
pos +=len(chunk)
|
| 185 |
+
|
| 186 |
+
await callback(pos,current_part,part_size,part_count,file_size,peer_id,mesage_update_id)
|
| 187 |
+
|
| 188 |
+
if is_big:
|
| 189 |
+
return types.InputFileBig(file_id, part_count, filename)
|
| 190 |
+
else:
|
| 191 |
+
return custom.InputSizedFile(
|
| 192 |
+
file_id, part_count, filename, md5=hash_md5, size=file_size
|
| 193 |
+
)'''
|
bot/server/__init__.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from quart import Quart
|
| 2 |
+
from uvicorn import Server as UvicornServer, Config
|
| 3 |
+
from logging import getLogger
|
| 4 |
+
from bot.config import Server, LOGGER_CONFIG_JSON
|
| 5 |
+
|
| 6 |
+
from . import main, error
|
| 7 |
+
|
| 8 |
+
logger = getLogger('uvicorn')
|
| 9 |
+
instance = Quart(__name__)
|
| 10 |
+
instance.config['RESPONSE_TIMEOUT'] = None
|
| 11 |
+
|
| 12 |
+
@instance.before_serving
|
| 13 |
+
async def before_serve():
|
| 14 |
+
logger.info('Web server is started!')
|
| 15 |
+
logger.info(f'Server running on {Server.BIND_ADDRESS}:{Server.PORT}')
|
| 16 |
+
|
| 17 |
+
instance.register_blueprint(main.bp)
|
| 18 |
+
|
| 19 |
+
instance.register_error_handler(400, error.invalid_request)
|
| 20 |
+
instance.register_error_handler(404, error.not_found)
|
| 21 |
+
instance.register_error_handler(405, error.invalid_method)
|
| 22 |
+
instance.register_error_handler(error.HTTPError, error.http_error)
|
| 23 |
+
|
| 24 |
+
server = UvicornServer (
|
| 25 |
+
Config (
|
| 26 |
+
app=instance,
|
| 27 |
+
host=Server.BIND_ADDRESS,
|
| 28 |
+
port=Server.PORT,
|
| 29 |
+
log_config=LOGGER_CONFIG_JSON
|
| 30 |
+
)
|
| 31 |
+
)
|
bot/server/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (1.88 kB). View file
|
|
|
bot/server/__pycache__/error.cpython-311.pyc
ADDED
|
Binary file (2.18 kB). View file
|
|
|
bot/server/__pycache__/main.cpython-311.pyc
ADDED
|
Binary file (5.24 kB). View file
|
|
|
bot/server/error.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class HTTPError(Exception):
|
| 2 |
+
status_code:int = None
|
| 3 |
+
description:str = None
|
| 4 |
+
def __init__(self, status_code, description):
|
| 5 |
+
self.status_code = status_code
|
| 6 |
+
self.description = description
|
| 7 |
+
super().__init__(self.status_code, self.description)
|
| 8 |
+
|
| 9 |
+
error_messages = {
|
| 10 |
+
400: 'Invalid request.',
|
| 11 |
+
401: 'File code is required to download the file.',
|
| 12 |
+
403: 'Invalid file code.',
|
| 13 |
+
404: 'File not found.',
|
| 14 |
+
500: 'Internal server error.'
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
async def invalid_request(_):
|
| 18 |
+
return 'Invalid request.', 400
|
| 19 |
+
|
| 20 |
+
async def not_found(_):
|
| 21 |
+
return 'Resource not found.', 404
|
| 22 |
+
|
| 23 |
+
async def invalid_method(_):
|
| 24 |
+
return 'Invalid request method.', 405
|
| 25 |
+
|
| 26 |
+
async def http_error(error: HTTPError):
|
| 27 |
+
error_message = error_messages.get(error.status_code)
|
| 28 |
+
return error.description or error_message, error.status_code
|
| 29 |
+
|
| 30 |
+
def abort(status_code: int = 500, description: str = None):
|
| 31 |
+
raise HTTPError(status_code, description)
|
bot/server/main.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from quart import Blueprint, Response, request, render_template, redirect
|
| 2 |
+
from .error import abort
|
| 3 |
+
from bot import TelegramBot
|
| 4 |
+
from bot.modules.decorators import byte_to_gb_mb
|
| 5 |
+
from bot.config import Telegram, Server
|
| 6 |
+
from math import ceil, floor
|
| 7 |
+
from bot.modules.telegram import get_message, get_file_properties
|
| 8 |
+
|
| 9 |
+
bp = Blueprint('main', __name__)
|
| 10 |
+
|
| 11 |
+
@bp.route('/')
|
| 12 |
+
async def home():
|
| 13 |
+
return "Welcome!!"
|
| 14 |
+
#return redirect(f'https://t.me/{Telegram.BOT_USERNAME}')
|
| 15 |
+
|
| 16 |
+
@bp.route('/dl/<int:file_id>')
|
| 17 |
+
async def transmit_file(file_id):
|
| 18 |
+
file = await get_message(message_id=int(file_id)) or abort(404)
|
| 19 |
+
code = request.args.get('code') or abort(401)
|
| 20 |
+
range_header = request.headers.get('Range', 0)
|
| 21 |
+
|
| 22 |
+
if code != file.raw_text:
|
| 23 |
+
abort(403)
|
| 24 |
+
|
| 25 |
+
file_name, file_size, mime_type = get_file_properties(file)
|
| 26 |
+
|
| 27 |
+
if range_header:
|
| 28 |
+
from_bytes, until_bytes = range_header.replace("bytes=", "").split("-")
|
| 29 |
+
from_bytes = int(from_bytes)
|
| 30 |
+
until_bytes = int(until_bytes) if until_bytes else file_size - 1
|
| 31 |
+
else:
|
| 32 |
+
from_bytes = 0
|
| 33 |
+
until_bytes = file_size - 1
|
| 34 |
+
|
| 35 |
+
if (until_bytes > file_size) or (from_bytes < 0) or (until_bytes < from_bytes):
|
| 36 |
+
abort(416, 'Invalid range.')
|
| 37 |
+
|
| 38 |
+
chunk_size = 1024 * 1024
|
| 39 |
+
until_bytes = min(until_bytes, file_size - 1)
|
| 40 |
+
|
| 41 |
+
offset = from_bytes - (from_bytes % chunk_size)
|
| 42 |
+
first_part_cut = from_bytes - offset
|
| 43 |
+
last_part_cut = until_bytes % chunk_size + 1
|
| 44 |
+
|
| 45 |
+
req_length = until_bytes - from_bytes + 1
|
| 46 |
+
part_count = ceil(until_bytes / chunk_size) - floor(offset / chunk_size)
|
| 47 |
+
|
| 48 |
+
headers = {
|
| 49 |
+
"Content-Type": f"{mime_type}",
|
| 50 |
+
"Content-Range": f"bytes {from_bytes}-{until_bytes}/{file_size}",
|
| 51 |
+
"Content-Length": str(req_length),
|
| 52 |
+
"Content-Disposition": f'attachment; filename="{file_name}"',
|
| 53 |
+
"Accept-Ranges": "bytes",
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
async def file_generator():
|
| 57 |
+
current_part = 1
|
| 58 |
+
async for chunk in TelegramBot.iter_download(file, offset=offset, chunk_size=chunk_size, stride=chunk_size, file_size=file_size):
|
| 59 |
+
if not chunk:
|
| 60 |
+
break
|
| 61 |
+
elif part_count == 1:
|
| 62 |
+
yield chunk[first_part_cut:last_part_cut]
|
| 63 |
+
elif current_part == 1:
|
| 64 |
+
yield chunk[first_part_cut:]
|
| 65 |
+
elif current_part == part_count:
|
| 66 |
+
yield chunk[:last_part_cut]
|
| 67 |
+
else:
|
| 68 |
+
yield chunk
|
| 69 |
+
|
| 70 |
+
current_part += 1
|
| 71 |
+
|
| 72 |
+
if current_part > part_count:
|
| 73 |
+
break
|
| 74 |
+
|
| 75 |
+
return Response(file_generator(), headers=headers, status=206 if range_header else 200)
|
| 76 |
+
|
| 77 |
+
@bp.route('/stream/<int:file_id>')
|
| 78 |
+
async def stream_file(file_id):
|
| 79 |
+
code = request.args.get('code') or abort(401)
|
| 80 |
+
file = await get_message(message_id=int(file_id)) or abort(404)
|
| 81 |
+
file_name, file_size, mime_type = get_file_properties(file)
|
| 82 |
+
return await render_template('player.html', mediaLink=f'{Server.BASE_URL}/dl/{file_id}?code={code}',file_name=file_name,file_size=byte_to_gb_mb(file_size))
|
| 83 |
+
|
| 84 |
+
@bp.route('/file/<int:file_id>')
|
| 85 |
+
async def file_deeplink(file_id):
|
| 86 |
+
code = request.args.get('code') or abort(401)
|
| 87 |
+
|
| 88 |
+
return redirect(f'https://t.me/{Telegram.BOT_USERNAME}?start=file_{file_id}_{code}')
|
bot/server/templates/link.png
ADDED
|
bot/server/templates/mx.png
ADDED
|
bot/server/templates/player.html
ADDED
|
@@ -0,0 +1,781 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<title>Play | {{file_name}}</title>
|
| 5 |
+
|
| 6 |
+
<meta charset="UTF-8">
|
| 7 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 8 |
+
<meta http-equiv="X-Frame-Options" content="deny">
|
| 9 |
+
|
| 10 |
+
<link rel="stylesheet" href="https://cdn.plyr.io/3.7.8/plyr.css" />
|
| 11 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
| 12 |
+
|
| 13 |
+
<script src="https://cdn.plyr.io/3.7.8/plyr.polyfilled.js"></script>
|
| 14 |
+
|
| 15 |
+
<style>
|
| 16 |
+
html, body {
|
| 17 |
+
height: 100%;
|
| 18 |
+
width: 100%;
|
| 19 |
+
background-image: url(https://i.postimg.cc/T2mcBGtv/pxfuel-2.jpg);
|
| 20 |
+
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
#stream-media {
|
| 24 |
+
height: 100%;
|
| 25 |
+
width: 100%;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
#error-message {
|
| 29 |
+
color: red;
|
| 30 |
+
font-size: 24px;
|
| 31 |
+
text-align: center;
|
| 32 |
+
margin-top: 20px;
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
.plyr__video-wrapper .plyr-download-button{
|
| 36 |
+
position: absolute;
|
| 37 |
+
top: 10px;
|
| 38 |
+
left: 10px;
|
| 39 |
+
width: 30px;
|
| 40 |
+
height: 30px;
|
| 41 |
+
background-color: rgba(0, 0, 0, 0.7);
|
| 42 |
+
border-radius: 50%;
|
| 43 |
+
text-align: center;
|
| 44 |
+
line-height: 30px;
|
| 45 |
+
color: white;
|
| 46 |
+
z-index: 10;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
.plyr__volume {
|
| 50 |
+
max-width: initial;
|
| 51 |
+
min-width: initial;
|
| 52 |
+
width: auto;
|
| 53 |
+
position: relative;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
.plyr__video-wrapper .plyr-share-button{
|
| 58 |
+
position: absolute;
|
| 59 |
+
top: 50px;
|
| 60 |
+
left: 10px;
|
| 61 |
+
width: 30px;
|
| 62 |
+
height: 30px;
|
| 63 |
+
background-color: rgba(0, 0, 0, 0.7);
|
| 64 |
+
border-radius: 50%;
|
| 65 |
+
text-align: center;
|
| 66 |
+
line-height: 30px;
|
| 67 |
+
color: white;
|
| 68 |
+
z-index: 10;
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
.plyr__video-wrapper .plyr-download-button:hover,
|
| 72 |
+
.plyr__video-wrapper .plyr-share-button:hover{
|
| 73 |
+
background-color: rgba(255, 255, 255, 0.7);
|
| 74 |
+
color: black;
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
.plyr__video-wrapper .plyr-download-button:before {
|
| 78 |
+
font-family: "Font Awesome 5 Free";
|
| 79 |
+
content: "\f019";
|
| 80 |
+
font-weight: bold;
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
.plyr__video-wrapper .plyr-share-button:before {
|
| 84 |
+
font-family: "Font Awesome 5 Free";
|
| 85 |
+
content: "\f064";
|
| 86 |
+
font-weight: bold;
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
.plyr, .plyr__video-wrapper, .plyr__video-embed iframe {
|
| 90 |
+
height: auto;
|
| 91 |
+
width:100vw;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
:root {
|
| 95 |
+
--mycol: #ff003c;
|
| 96 |
+
--gcol: linear-gradient(to right, red, blue);
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
span {
|
| 100 |
+
color: var(--mycol);
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
* {
|
| 104 |
+
margin: 0;
|
| 105 |
+
padding: 0;
|
| 106 |
+
box-sizing: border-box;
|
| 107 |
+
color: white;
|
| 108 |
+
font-family: 'Josefin Sans', sans-serif;
|
| 109 |
+
scroll-behavior: smooth;
|
| 110 |
+
text-transform: capitalize;
|
| 111 |
+
}
|
| 112 |
+
|
| 113 |
+
*::-webkit-scrollbar {
|
| 114 |
+
display: none;
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
a {
|
| 122 |
+
text-decoration: none;
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
.file-name {
|
| 126 |
+
overflow: hidden;
|
| 127 |
+
margin-top: 20px;
|
| 128 |
+
width: 100vw;
|
| 129 |
+
height: auto;
|
| 130 |
+
padding: 20px;
|
| 131 |
+
border-radius: 5px;
|
| 132 |
+
background: linear-gradient(to bottom, rgba(52, 52, 52, 0.301), rgba(153, 153, 153, 0.075));
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
.file-name h4 {
|
| 136 |
+
color: var(--mycol);
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
.downloadBtn {
|
| 140 |
+
padding: 30px 0;
|
| 141 |
+
display: flex;
|
| 142 |
+
align-items: center;
|
| 143 |
+
justify-content: center;
|
| 144 |
+
/* flex-direction: column; */
|
| 145 |
+
flex-wrap: wrap;
|
| 146 |
+
gap: 20px;
|
| 147 |
+
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
.downloadBtn button {
|
| 151 |
+
opacity: 1;
|
| 152 |
+
border-radius: 6px;
|
| 153 |
+
background: linear-gradient(to bottom, rgba(247, 247, 247, 0.187), rgba(240, 240, 240, 0.105));
|
| 154 |
+
height: 50px;
|
| 155 |
+
width: 300px;
|
| 156 |
+
display: flex;
|
| 157 |
+
align-items: center;
|
| 158 |
+
font-weight: 700;
|
| 159 |
+
gap: 10px;
|
| 160 |
+
padding-left: 50px;
|
| 161 |
+
border: none;
|
| 162 |
+
text-transform: uppercase;
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
.downloadBtn img {
|
| 166 |
+
height: 20px;
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
/* about sec edit */
|
| 174 |
+
.abt {
|
| 175 |
+
|
| 176 |
+
overflow: hidden;
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
.about {
|
| 180 |
+
/* width: 100%; */
|
| 181 |
+
width: 100vw;
|
| 182 |
+
background-size: cover;
|
| 183 |
+
backdrop-filter: blur(1px);
|
| 184 |
+
height: 100vh;
|
| 185 |
+
position: relative;
|
| 186 |
+
/* overflow: hidden; */
|
| 187 |
+
transition: all 0.4s ease;
|
| 188 |
+
/* display: none; */
|
| 189 |
+
overflow: hidden;
|
| 190 |
+
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
.about::-webkit-scrollbar {
|
| 194 |
+
display: none;
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
.about-nav {
|
| 198 |
+
/* height: 50px; */
|
| 199 |
+
width: 100%;
|
| 200 |
+
align-items: center;
|
| 201 |
+
justify-content: space-evenly;
|
| 202 |
+
backdrop-filter: blur(1px);
|
| 203 |
+
position: fixed;
|
| 204 |
+
left: 0%;
|
| 205 |
+
z-index: 200;
|
| 206 |
+
font-size: 14px;
|
| 207 |
+
top: 12%;
|
| 208 |
+
display: none;
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
.about-nav a {
|
| 212 |
+
position: relative;
|
| 213 |
+
margin: 0 7px;
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
.about-nav a::after {
|
| 217 |
+
content: "";
|
| 218 |
+
position: absolute;
|
| 219 |
+
height: 2px;
|
| 220 |
+
width: 0%;
|
| 221 |
+
background-color: var(--mycol);
|
| 222 |
+
bottom: -5px;
|
| 223 |
+
left: 1px;
|
| 224 |
+
border-radius: 30px;
|
| 225 |
+
transition: all 0.4s ease;
|
| 226 |
+
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
.about-nav a.active::after {
|
| 230 |
+
width: 90%;
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
.about-dets {
|
| 234 |
+
height: 100vh;
|
| 235 |
+
width: 400vw;
|
| 236 |
+
position: absolute;
|
| 237 |
+
display: flex;
|
| 238 |
+
align-items: center;
|
| 239 |
+
overflow: hidden;
|
| 240 |
+
transition: all 0.4s ease;
|
| 241 |
+
}
|
| 242 |
+
|
| 243 |
+
.abt-sec {
|
| 244 |
+
height: 100%;
|
| 245 |
+
width: 100vw;
|
| 246 |
+
display: flex;
|
| 247 |
+
flex-direction: column;
|
| 248 |
+
align-items: center;
|
| 249 |
+
/* justify-content: center; */
|
| 250 |
+
z-index: 100;
|
| 251 |
+
gap: 10%;
|
| 252 |
+
padding-top: 130px;
|
| 253 |
+
backdrop-filter: blur(5px);
|
| 254 |
+
overflow: hidden;
|
| 255 |
+
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
#sec4 {
|
| 259 |
+
/* transform: translateY(60px); */
|
| 260 |
+
gap: 22px;
|
| 261 |
+
padding-left: 20px;
|
| 262 |
+
padding-right: 20px;
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
#sec4 h1 {
|
| 266 |
+
margin-bottom: 30px;
|
| 267 |
+
}
|
| 268 |
+
|
| 269 |
+
.abt-sec h1 {
|
| 270 |
+
text-align: center;
|
| 271 |
+
transform: translateY(20px);
|
| 272 |
+
font-weight: 700;
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
+
.abt-sec p {
|
| 276 |
+
text-align: center;
|
| 277 |
+
font-weight: 700;
|
| 278 |
+
text-transform: capitalize;
|
| 279 |
+
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
.links {
|
| 285 |
+
display: flex;
|
| 286 |
+
flex-direction: column;
|
| 287 |
+
align-items: center;
|
| 288 |
+
gap: 10px;
|
| 289 |
+
}
|
| 290 |
+
|
| 291 |
+
.links button {
|
| 292 |
+
width: 350px;
|
| 293 |
+
height: 50px;
|
| 294 |
+
border-radius: 6px;
|
| 295 |
+
border: none;
|
| 296 |
+
background: linear-gradient(to top, rgba(46, 46, 46, 0.365), rgba(46, 46, 46, 0.443));
|
| 297 |
+
font-size: 15px;
|
| 298 |
+
font-weight: 550;
|
| 299 |
+
transition: all 0.4s ease;
|
| 300 |
+
/* opacity: 0; */
|
| 301 |
+
}
|
| 302 |
+
|
| 303 |
+
.links a {
|
| 304 |
+
opacity: 0;
|
| 305 |
+
}
|
| 306 |
+
|
| 307 |
+
button {
|
| 308 |
+
transition: all 1s ease;
|
| 309 |
+
/* opacity: 0; */
|
| 310 |
+
/* animation: linksBtnAn 2s ease 0.3s infinite, strtLoad 4s ease 0.5s forwards; */
|
| 311 |
+
position: relative;
|
| 312 |
+
overflow: hidden;
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
.links button::before,
|
| 316 |
+
.downloadBtn button::before {
|
| 317 |
+
opacity: 0;
|
| 318 |
+
left: -10px;
|
| 319 |
+
top: -20px;
|
| 320 |
+
content: "";
|
| 321 |
+
width: 20%;
|
| 322 |
+
height: 200%;
|
| 323 |
+
position: absolute;
|
| 324 |
+
background-color: #ffffff18;
|
| 325 |
+
box-shadow: 2px 2px 30px 30px #ffffff18;
|
| 326 |
+
rotate: 20deg;
|
| 327 |
+
animation: var(--beforestyl);
|
| 328 |
+
filter: contrast(100);
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
.links button:hover {
|
| 332 |
+
scale: 1.05;
|
| 333 |
+
}
|
| 334 |
+
|
| 335 |
+
button:active {
|
| 336 |
+
scale: 0.7;
|
| 337 |
+
}
|
| 338 |
+
|
| 339 |
+
.box {
|
| 340 |
+
margin-bottom: 60px;
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
.box h3 {
|
| 344 |
+
margin-bottom: 15px;
|
| 345 |
+
position: relative;
|
| 346 |
+
}
|
| 347 |
+
|
| 348 |
+
.box a {
|
| 349 |
+
margin-top: 15px;
|
| 350 |
+
}
|
| 351 |
+
|
| 352 |
+
.box p {
|
| 353 |
+
margin-top: 5px;
|
| 354 |
+
}
|
| 355 |
+
|
| 356 |
+
.box h3::after {
|
| 357 |
+
content: "";
|
| 358 |
+
position: absolute;
|
| 359 |
+
height: 1.5px;
|
| 360 |
+
width: 90%;
|
| 361 |
+
background-color: var(--mycol);
|
| 362 |
+
bottom: -3px;
|
| 363 |
+
left: 0%;
|
| 364 |
+
border-radius: 30px;
|
| 365 |
+
transition: all 0.4s ease;
|
| 366 |
+
}
|
| 367 |
+
|
| 368 |
+
/* middle part */
|
| 369 |
+
.mid {
|
| 370 |
+
font-size: 40px;
|
| 371 |
+
font-weight: 600;
|
| 372 |
+
text-align: center;
|
| 373 |
+
padding: 10%;
|
| 374 |
+
}
|
| 375 |
+
|
| 376 |
+
.last-text {
|
| 377 |
+
text-align: center;
|
| 378 |
+
padding: 10%;
|
| 379 |
+
}
|
| 380 |
+
|
| 381 |
+
.movie-cont {
|
| 382 |
+
height: fit-content;
|
| 383 |
+
width: 100vw;
|
| 384 |
+
display: flex;
|
| 385 |
+
flex-direction: column;
|
| 386 |
+
align-items: center;
|
| 387 |
+
gap: 30px;
|
| 388 |
+
}
|
| 389 |
+
|
| 390 |
+
.movieSug {
|
| 391 |
+
height: 500px;
|
| 392 |
+
width: 95vw;
|
| 393 |
+
border-radius: 2%;
|
| 394 |
+
position: relative;
|
| 395 |
+
display: flex;
|
| 396 |
+
align-items: center;
|
| 397 |
+
justify-content: space-around;
|
| 398 |
+
/* background-image: url(https://image.tmdb.org/t/p/w1280//8Gxv8gSFCU0XGDykEGv7zR1n2ua.jpg); */
|
| 399 |
+
background-size: cover;
|
| 400 |
+
background-position: center;
|
| 401 |
+
overflow: hidden;
|
| 402 |
+
|
| 403 |
+
}
|
| 404 |
+
|
| 405 |
+
.movieSug::before {
|
| 406 |
+
content: '';
|
| 407 |
+
height: 110%;
|
| 408 |
+
width: 110%;
|
| 409 |
+
position: absolute;
|
| 410 |
+
background: linear-gradient(to right, black 20%, rgba(0, 0, 0, 0.322) 75%, rgba(0, 0, 0, 0));
|
| 411 |
+
top: 0%;
|
| 412 |
+
left: -10px;
|
| 413 |
+
}
|
| 414 |
+
|
| 415 |
+
.movieDets {
|
| 416 |
+
padding: 5%;
|
| 417 |
+
height: 100%;
|
| 418 |
+
width: 45vw;
|
| 419 |
+
display: flex;
|
| 420 |
+
flex-direction: column;
|
| 421 |
+
text-align: start;
|
| 422 |
+
justify-content: center;
|
| 423 |
+
gap: 10px;
|
| 424 |
+
z-index: 100;
|
| 425 |
+
}
|
| 426 |
+
|
| 427 |
+
.movieDets h3:first-child {
|
| 428 |
+
font-size: 32px;
|
| 429 |
+
font-weight: 600;
|
| 430 |
+
}
|
| 431 |
+
|
| 432 |
+
.movieimg {
|
| 433 |
+
/* height: 100%; */
|
| 434 |
+
width: 45vw;
|
| 435 |
+
/* background-color: #100206; */
|
| 436 |
+
display: flex;
|
| 437 |
+
justify-content: center;
|
| 438 |
+
align-items: center;
|
| 439 |
+
gap: 10px;
|
| 440 |
+
}
|
| 441 |
+
|
| 442 |
+
.movieimg img {
|
| 443 |
+
max-height: 400px;
|
| 444 |
+
box-shadow: 2px 2px 100px 70px rgba(0, 0, 0, 0.566);
|
| 445 |
+
}
|
| 446 |
+
|
| 447 |
+
.movieDets span {
|
| 448 |
+
font-weight: 700;
|
| 449 |
+
font-size: 20px;
|
| 450 |
+
}
|
| 451 |
+
|
| 452 |
+
.movieDets h4:nth-child(2) {
|
| 453 |
+
margin-top: 20px;
|
| 454 |
+
font-size: 23px;
|
| 455 |
+
font-weight: 700;
|
| 456 |
+
}
|
| 457 |
+
|
| 458 |
+
.movieDets h4:nth-child(3) {
|
| 459 |
+
|
| 460 |
+
font-size: 15px;
|
| 461 |
+
font-weight: 700;
|
| 462 |
+
}
|
| 463 |
+
|
| 464 |
+
.movieStsBar {
|
| 465 |
+
display: none;
|
| 466 |
+
}
|
| 467 |
+
|
| 468 |
+
.ranMovBtn {
|
| 469 |
+
|
| 470 |
+
border-radius: 6px;
|
| 471 |
+
background: linear-gradient(to bottom, rgba(247, 247, 247, 0.187), rgba(240, 240, 240, 0.105));
|
| 472 |
+
height: 50px;
|
| 473 |
+
width: 300px;
|
| 474 |
+
display: flex;
|
| 475 |
+
align-items: center;
|
| 476 |
+
font-weight: 700;
|
| 477 |
+
gap: 10px;
|
| 478 |
+
padding-left: 50px;
|
| 479 |
+
border: none;
|
| 480 |
+
}
|
| 481 |
+
|
| 482 |
+
.copyright {
|
| 483 |
+
margin: 30px;
|
| 484 |
+
gap: 10px;
|
| 485 |
+
display: flex;
|
| 486 |
+
flex-direction: column;
|
| 487 |
+
align-items: center;
|
| 488 |
+
|
| 489 |
+
}
|
| 490 |
+
|
| 491 |
+
.copyright h5 {
|
| 492 |
+
font-size: 13px;
|
| 493 |
+
}
|
| 494 |
+
|
| 495 |
+
@keyframes linksBtnAn {
|
| 496 |
+
|
| 497 |
+
from {
|
| 498 |
+
scale: 1;
|
| 499 |
+
}
|
| 500 |
+
|
| 501 |
+
50% {
|
| 502 |
+
scale: 1.02;
|
| 503 |
+
}
|
| 504 |
+
|
| 505 |
+
to {
|
| 506 |
+
scale: 1;
|
| 507 |
+
}
|
| 508 |
+
}
|
| 509 |
+
|
| 510 |
+
@keyframes strtLoad {
|
| 511 |
+
from {
|
| 512 |
+
opacity: 0;
|
| 513 |
+
transform: translateX(-200px);
|
| 514 |
+
}
|
| 515 |
+
|
| 516 |
+
to {
|
| 517 |
+
opacity: 1;
|
| 518 |
+
transform: translateX(0px);
|
| 519 |
+
}
|
| 520 |
+
|
| 521 |
+
}
|
| 522 |
+
|
| 523 |
+
@keyframes button_shine {
|
| 524 |
+
from {
|
| 525 |
+
opacity: .9;
|
| 526 |
+
left: -100px;
|
| 527 |
+
}
|
| 528 |
+
|
| 529 |
+
to {
|
| 530 |
+
opacity: 1;
|
| 531 |
+
left: 400px;
|
| 532 |
+
}
|
| 533 |
+
}
|
| 534 |
+
|
| 535 |
+
@media (max-width : 1029px) {
|
| 536 |
+
|
| 537 |
+
.movieDets h3:first-child {
|
| 538 |
+
font-size: 25px;
|
| 539 |
+
}
|
| 540 |
+
}
|
| 541 |
+
|
| 542 |
+
@media (max-width : 992px) {
|
| 543 |
+
.movieSug {
|
| 544 |
+
scale: 0.9;
|
| 545 |
+
}
|
| 546 |
+
|
| 547 |
+
.movie-cont {
|
| 548 |
+
scale: 0.9;
|
| 549 |
+
}
|
| 550 |
+
|
| 551 |
+
}
|
| 552 |
+
|
| 553 |
+
@media (max-width : 800px) {
|
| 554 |
+
.movie-cont {
|
| 555 |
+
scale: 0.9;
|
| 556 |
+
}
|
| 557 |
+
|
| 558 |
+
.movieSug {
|
| 559 |
+
height: 400px;
|
| 560 |
+
}
|
| 561 |
+
|
| 562 |
+
.movieimg img {
|
| 563 |
+
max-height: 300px;
|
| 564 |
+
|
| 565 |
+
}
|
| 566 |
+
|
| 567 |
+
.movieDets {
|
| 568 |
+
scale: 0.8;
|
| 569 |
+
}
|
| 570 |
+
|
| 571 |
+
}
|
| 572 |
+
|
| 573 |
+
@media (max-width : 637px) {
|
| 574 |
+
.movieSug {
|
| 575 |
+
background-position: right;
|
| 576 |
+
padding: 5%;
|
| 577 |
+
scale: 1;
|
| 578 |
+
width: 90vw;
|
| 579 |
+
}
|
| 580 |
+
|
| 581 |
+
.movie-cont {
|
| 582 |
+
width: 90vw;
|
| 583 |
+
padding: 5%;
|
| 584 |
+
scale: 1;
|
| 585 |
+
}
|
| 586 |
+
|
| 587 |
+
.movieDets {
|
| 588 |
+
display: none;
|
| 589 |
+
}
|
| 590 |
+
|
| 591 |
+
.movieStsBar {
|
| 592 |
+
display: block;
|
| 593 |
+
border-radius: 6%;
|
| 594 |
+
}
|
| 595 |
+
|
| 596 |
+
.movieDets-mini {
|
| 597 |
+
position: absolute;
|
| 598 |
+
display: flex;
|
| 599 |
+
flex-direction: column;
|
| 600 |
+
text-align: start;
|
| 601 |
+
width: 80%;
|
| 602 |
+
left: 50px;
|
| 603 |
+
transform: translate(-35px, -120px);
|
| 604 |
+
|
| 605 |
+
}
|
| 606 |
+
|
| 607 |
+
.movieSug::before {
|
| 608 |
+
content: '';
|
| 609 |
+
height: 100%;
|
| 610 |
+
width: 100%;
|
| 611 |
+
position: absolute;
|
| 612 |
+
background: linear-gradient(to top, black 20%, rgba(0, 0, 0, 0.322) 75%, rgba(0, 0, 0, 0));
|
| 613 |
+
top: 10%;
|
| 614 |
+
left: 0px;
|
| 615 |
+
}
|
| 616 |
+
|
| 617 |
+
.movieimg img {
|
| 618 |
+
transform: translate(75px, -40px);
|
| 619 |
+
|
| 620 |
+
}
|
| 621 |
+
}
|
| 622 |
+
</style>
|
| 623 |
+
</head>
|
| 624 |
+
|
| 625 |
+
<body>
|
| 626 |
+
|
| 627 |
+
|
| 628 |
+
<video id="stream-media" controls preload="auto">
|
| 629 |
+
<source src="" type="">
|
| 630 |
+
<p class="vjs-no-js">
|
| 631 |
+
To view this video please enable JavaScript, and consider upgrading to a web browser that supports HTML5 video
|
| 632 |
+
</p>
|
| 633 |
+
</video>
|
| 634 |
+
<div class="file-name">
|
| 635 |
+
<h4 style="display: inline;">File Name: </h4>
|
| 636 |
+
<p style="display: inline;" id="myDiv">{{file_name}}</p><br>
|
| 637 |
+
<h4 style="display: inline;">File Size: </h4>
|
| 638 |
+
<p style="display: inline;">{{file_size}}</p>
|
| 639 |
+
</div>
|
| 640 |
+
<div class="downloadBtn">
|
| 641 |
+
<button class="magnet" onclick="copyStreamLink()">
|
| 642 |
+
<img src="https://i.ibb.co/CM4Y586/link.png" alt="Copy Link">copy link
|
| 643 |
+
</button>
|
| 644 |
+
<button class="magnet share" onclick="link_share()">
|
| 645 |
+
<img src="https://i.ibb.co/vx5D2g5/share.png" alt="">share link
|
| 646 |
+
</button>
|
| 647 |
+
<button class="magnet" onclick="vlc_player()">
|
| 648 |
+
<img src="https://i.ibb.co/px6fQs1/vlc.png" alt="">watch in VLC PLAYER
|
| 649 |
+
</button>
|
| 650 |
+
<button class="magnet" onclick="mx_player()">
|
| 651 |
+
<img src="https://i.ibb.co/41WvtQ3/mx.png" alt="">watch in MX PLAYER
|
| 652 |
+
</button>
|
| 653 |
+
|
| 654 |
+
|
| 655 |
+
</div>
|
| 656 |
+
<div id="error-message"></div>
|
| 657 |
+
|
| 658 |
+
|
| 659 |
+
<script>
|
| 660 |
+
var player = new Plyr('#stream-media', {
|
| 661 |
+
controls:['play-large', 'rewind', 'play', 'fast-forward', 'progress', 'current-time', 'mute', 'settings','download', 'pip', 'fullscreen'],
|
| 662 |
+
settings:['speed','loop'],
|
| 663 |
+
speed:{selected:1,options:[0.25,0.5,0.75,1,1.25,1.5,1.75,2]},
|
| 664 |
+
seek: 10,
|
| 665 |
+
keyboard: { focused: true, global: true },
|
| 666 |
+
});
|
| 667 |
+
|
| 668 |
+
var mediaLink = "{{ mediaLink }}";
|
| 669 |
+
|
| 670 |
+
if (mediaLink) {
|
| 671 |
+
document.querySelector('#stream-media source').setAttribute('src', mediaLink);
|
| 672 |
+
player.restart();
|
| 673 |
+
|
| 674 |
+
/*var downloadButton = document.createElement('div');
|
| 675 |
+
downloadButton.className = 'plyr-download-button';
|
| 676 |
+
|
| 677 |
+
downloadButton.onclick = function() {
|
| 678 |
+
event.stopPropagation();
|
| 679 |
+
var link = document.createElement('a');
|
| 680 |
+
link.href = mediaLink;
|
| 681 |
+
document.body.appendChild(link);
|
| 682 |
+
link.click();
|
| 683 |
+
document.body.removeChild(link);
|
| 684 |
+
};
|
| 685 |
+
|
| 686 |
+
player.elements.container.querySelector('.plyr__video-wrapper').appendChild(downloadButton);
|
| 687 |
+
|
| 688 |
+
var shareButton = document.createElement('div');
|
| 689 |
+
shareButton.className = 'plyr-share-button';
|
| 690 |
+
|
| 691 |
+
shareButton.onclick = function() {
|
| 692 |
+
event.stopPropagation();
|
| 693 |
+
if (navigator.share) {
|
| 694 |
+
navigator.share({
|
| 695 |
+
title: "Play",
|
| 696 |
+
url: window.location.href
|
| 697 |
+
});
|
| 698 |
+
}
|
| 699 |
+
};
|
| 700 |
+
|
| 701 |
+
player.elements.container.querySelector('.plyr__video-wrapper').appendChild(shareButton);
|
| 702 |
+
*/
|
| 703 |
+
|
| 704 |
+
} else {
|
| 705 |
+
document.getElementById('error-message').textContent = 'Error: Media URL not provided';
|
| 706 |
+
}
|
| 707 |
+
function link_share() {
|
| 708 |
+
if (navigator.share) {
|
| 709 |
+
navigator.share({
|
| 710 |
+
title: mediaLink,
|
| 711 |
+
url: window.location.href
|
| 712 |
+
});
|
| 713 |
+
}
|
| 714 |
+
|
| 715 |
+
}
|
| 716 |
+
function vlc_player() {
|
| 717 |
+
|
| 718 |
+
const openstreamlink = mediaLink;
|
| 719 |
+
const openVlc = `vlc://${openstreamlink}`;
|
| 720 |
+
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
|
| 721 |
+
// Try opening VLC first, then MX Player if VLC is unavailable
|
| 722 |
+
window.location.href = openVlc;
|
| 723 |
+
|
| 724 |
+
} else {
|
| 725 |
+
alert("This feature is currently only supported on Android devices.");
|
| 726 |
+
}
|
| 727 |
+
//window.location.href = openVlc;
|
| 728 |
+
}
|
| 729 |
+
|
| 730 |
+
function mx_player() {
|
| 731 |
+
const openstreamlink = mediaLink;
|
| 732 |
+
const openMx = `intent:${openstreamlink}#Intent;package=com.mxtech.videoplayer.ad;end`;
|
| 733 |
+
if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
|
| 734 |
+
// Try opening VLC first, then MX Player if VLC is unavailable
|
| 735 |
+
window.location.href = openMx;;
|
| 736 |
+
|
| 737 |
+
} else {
|
| 738 |
+
alert("This feature is currently only supported on Android devices.");
|
| 739 |
+
}
|
| 740 |
+
|
| 741 |
+
}
|
| 742 |
+
function copyStreamLink() {
|
| 743 |
+
const linkToCopy = mediaLink.toLowerCase();
|
| 744 |
+
|
| 745 |
+
if (!navigator.clipboard) {
|
| 746 |
+
navigator.clipboard = {
|
| 747 |
+
writeText: function(text) {
|
| 748 |
+
return new Promise((resolve, reject) => {
|
| 749 |
+
try {
|
| 750 |
+
const textArea = document.createElement("textarea");
|
| 751 |
+
textArea.value = text;
|
| 752 |
+
document.body.appendChild(textArea);
|
| 753 |
+
textArea.focus();
|
| 754 |
+
textArea.select();
|
| 755 |
+
document.execCommand('copy');
|
| 756 |
+
document.body.removeChild(textArea);
|
| 757 |
+
resolve();
|
| 758 |
+
} catch (err) {
|
| 759 |
+
reject(err);
|
| 760 |
+
}
|
| 761 |
+
});
|
| 762 |
+
}
|
| 763 |
+
};
|
| 764 |
+
}
|
| 765 |
+
|
| 766 |
+
navigator.clipboard.writeText(linkToCopy)
|
| 767 |
+
.then(() => {
|
| 768 |
+
console.log('Stream link copied to clipboard!');
|
| 769 |
+
alert('Stream link copied successfully!');
|
| 770 |
+
})
|
| 771 |
+
.catch(err => {
|
| 772 |
+
console.error('Failed to copy link: ', err);
|
| 773 |
+
alert('Failed to copy link. Please try manually.');
|
| 774 |
+
});
|
| 775 |
+
}
|
| 776 |
+
|
| 777 |
+
|
| 778 |
+
</script>
|
| 779 |
+
|
| 780 |
+
</body>
|
| 781 |
+
</html>
|
bot/server/templates/share.png
ADDED
|
bot/server/templates/vlc.png
ADDED
|
Git LFS Details
|
requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
telethon
|
| 2 |
+
cryptg
|
| 3 |
+
quart
|
| 4 |
+
uvicorn
|
| 5 |
+
requests
|