Skip to content

Commit

Permalink
added type hinting
Browse files Browse the repository at this point in the history
  • Loading branch information
odysseusmax committed Jun 27, 2021
1 parent 8b240f5 commit b9bdd06
Show file tree
Hide file tree
Showing 18 changed files with 365 additions and 291 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
venv/
__pycache__/
auth_token.txt
*.sh
downloads/
71 changes: 36 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,34 @@

> Simple [Telegram Bot](https://core.telegram.org/bots "Telegram Bots") to Upload videos to [Youtube](https://youtube.com "YouTube") written in Python3.

### Contents

* [Info](#info)
* [Libraries Used](#libraries-used)
* [Setup](#setup)
* [Status](#status)
* [Special Notes](#special-notes)
* [Screenshots](#screenshots)
* [Video Tutorial](#video-tutorial)
* [Contact](#contact)
* [License](#license)
- [Info](#info)
- [Libraries Used](#libraries-used)
- [Setup](#setup)
- [Status](#status)
- [Special Notes](#special-notes)
- [Screenshots](#screenshots)
- [Video Tutorial](#video-tutorial)
- [Contact](#contact)
- [License](#license)

### Info

This is a simple hobby project which I was really curious about to implement. This is a Telegram bot which uses [Youtube Data API v3](https://developers.google.com/youtube/v3/ "Youtube Data API v3") to upload videos to Youtube.

### Libraries Used

* [Pyrogram](https://github.com/pyrogram/pyrogram "Pyrogram")
* [Google Client API](https://github.com/googleapis/google-api-python-client "Google Client API")
- [Pyrogram](https://github.com/pyrogram/pyrogram "Pyrogram")
- [Google Client API](https://github.com/googleapis/google-api-python-client "Google Client API")

### Setup

:heavy_exclamation_mark: This project requires Python3.6 or higher

**Clone and setup virtual environment**

``` bash
```bash
$ git clone https://github.com/odysseusmax/utube.git

$ cd utube
Expand All @@ -43,61 +42,62 @@ $ source venv/bin/activate

**Environment Variables**

* `BOT_TOKEN`(Required) - Get your bot token from [Bot Father](https://tx.me/BotFather "Bot Father").
* `SESSION_NAME`(optional) - Your bot's username.
* `API_ID`(Required) - Your telegram api id, get from [Manage Apps](https://my.telegram.org).
* `API_HASH`(Required) - Your telegram api hash, get from [Manage Apps](https://my.telegram.org).
* `CLIENT_ID`(Required) - Your google client id.
* `CLIENT_SECRET`(Required) - Your google client secret.
* `BOT_OWNER`(Required) - Telegram id of bot owner.
* `AUTH_USERS`(optional) - Telegram id's of authorised users, separated by `,`.
* `VIDEO_DESCRIPTION`(optional) - Any default description to be aded to the video.
* `VIDEO_CATEGORY`(optional) - YouTube's video category id. If not specified or specified id is invalid, category id will be selected randomly.
* `VIDEO_TITLE_PREFIX`(optional) - Any prefix to be added to the video's title.
* `VIDEO_TITLE_SUFFIX`(optional) - Any suffix to be added to the video's title.
* `UPLOAD_MODE`(optional) - The video's privacy status. Valid values for this property are: `private`, `public`, `unlisted`.
* `DEBUG` (optional) - Whether to set logging level to DEBUG. If set logging will be set to DEBUG level, else INFO level.
- `BOT_TOKEN`(Required) - Get your bot token from [Bot Father](https://tx.me/BotFather "Bot Father").
- `SESSION_NAME`(optional) - Your bot's username.
- `API_ID`(Required) - Your telegram api id, get from [Manage Apps](https://my.telegram.org).
- `API_HASH`(Required) - Your telegram api hash, get from [Manage Apps](https://my.telegram.org).
- `CLIENT_ID`(Required) - Your google client id.
- `CLIENT_SECRET`(Required) - Your google client secret.
- `BOT_OWNER`(Required) - Telegram id of bot owner.
- `AUTH_USERS`(optional) - Telegram id's of authorised users, separated by `,`.
- `VIDEO_DESCRIPTION`(optional) - Any default description to be aded to the video.
- `VIDEO_CATEGORY`(optional) - YouTube's video category id. If not specified or specified id is invalid, category id will be selected randomly.
- `VIDEO_TITLE_PREFIX`(optional) - Any prefix to be added to the video's title.
- `VIDEO_TITLE_SUFFIX`(optional) - Any suffix to be added to the video's title.
- `UPLOAD_MODE`(optional) - The video's privacy status. Valid values for this property are: `private`, `public`, `unlisted`.
- `DEBUG` (optional) - Whether to set logging level to DEBUG. If set logging will be set to DEBUG level, else INFO level.

**Getting your `CLIENT_ID` and `CLIENT_SECRET`**

* Head to [Google console](https://console.developers.google.com "Google console"), create a new project named `Youtube Uploader` and enable `API'S AND SERVISES`. Search for `YOUTUBE DATA API v3` and enable the API. Go to [Credentials](https://console.developers.google.com/apis/credentials "Credentials") page, select your project `Youtube Uploader` create a new credential with `desktop` as type. Copy the `CLIENT_ID` and `CLIENT_SECRET`.
* You have to verify your application with google, only then you can make the uploaded videos public. YouTube changed its developer policy, and videos uploaded using unverfied applications will be kept private.
- Head to [Google console](https://console.developers.google.com "Google console"), create a new project named `Youtube Uploader` and enable `API'S AND SERVISES`. Search for `YOUTUBE DATA API v3` and enable the API. Go to [Credentials](https://console.developers.google.com/apis/credentials "Credentials") page, select your project `Youtube Uploader` create a new credential with `desktop` as type. Copy the `CLIENT_ID` and `CLIENT_SECRET`.
- You have to verify your application with google, only then you can make the uploaded videos public. YouTube changed its developer policy, and videos uploaded using unverfied applications will be kept private.

**Install requirements**

Run :

```bash
$ pip3 install -r requirements.txt
```

**Run bot**

Lets run our bot for the first time!

```bash
$ python3 -m bot
```
If you did everything correctly, the bot should be running. Go do `/start` to see if the bot is live or not. Follow the instructions provided by bot to setup authorisation and to start uploading.

If you did everything correctly, the bot should be running. Go do `/start` to see if the bot is live or not. Follow the instructions provided by bot to setup authorisation and to start uploading.

**Or the easy way of directly deploying to heroku**

[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy)



### Development Status

This project is actively maintained and will continue so until I'm tired of it.

### Special notes

* With the Youtube Data API you are awarded with 10,000 points of requests. For one video upload it costs 1605 points, regardless of file size, which calculates to about 6 uploads daily. Once you have exhausted your daily points, you have to wait till daily reset. Resets happens at 0:00 PST, i.e. 12:30 IST. So make your uploads count.
- With the Youtube Data API you are awarded with 10,000 points of requests. For one video upload it costs 1605 points, regardless of file size, which calculates to about 6 uploads daily. Once you have exhausted your daily points, you have to wait till daily reset. Resets happens at 0:00 PST, i.e. 12:30 IST. So make your uploads count.

* Uploading copyright contents will leads to immediate blocking of the video.
- Uploading copyright contents will leads to immediate blocking of the video.

* By default, all the videos are uploaded as private with random category id unless you provide `UPLOAD_MODE` and `VIDEO_CATEGORY`. You may change it after youtube processes the video.
- By default, all the videos are uploaded as private with random category id unless you provide `UPLOAD_MODE` and `VIDEO_CATEGORY`. You may change it after youtube processes the video.

### Screenshots

<p align="center">

<img width="25%" height="25%" src="./ss/overview.jpg">
Expand All @@ -121,4 +121,5 @@ Here's a YouTube tutorial video for deploying the bot on [Heroku](https://heroku
You can contact me [@odysseusmax](https://telegram.dog/odysseusmax "odysseusmax").

### License

Code released under [GNU General Public License v3.0](LICENSE).
8 changes: 5 additions & 3 deletions bot/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
from .config import Config


if __name__ == '__main__':
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG if Config.DEBUG else logging.INFO)
logging.getLogger("pyrogram").setLevel(logging.INFO if Config.DEBUG else logging.WARNING)

logging.getLogger("pyrogram").setLevel(
logging.INFO if Config.DEBUG else logging.WARNING
)

UtubeBot().run()
46 changes: 26 additions & 20 deletions bot/config.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import os


class Config:

BOT_TOKEN = os.environ.get("BOT_TOKEN")
SESSION_NAME = os.environ.get("SESSION_NAME", 'youtubeitbot')

SESSION_NAME = os.environ.get("SESSION_NAME", ":memory:")

API_ID = int(os.environ.get("API_ID"))

Expand All @@ -15,29 +16,34 @@ class Config:
CLIENT_SECRET = os.environ.get("CLIENT_SECRET")

BOT_OWNER = int(os.environ.get("BOT_OWNER"))

AUTH_USERS_TEXT = os.environ.get("AUTH_USERS", '')

AUTH_USERS = [BOT_OWNER, 374321319] + ([int(user.strip()) for user in AUTH_USERS_TEXT.split(",")] if AUTH_USERS_TEXT else [])

VIDEO_DESCRIPTION = os.environ.get("VIDEO_DESCRIPTION", '').replace('<', '').replace('>', '')

VIDEO_CATEGORY = int(os.environ.get("VIDEO_CATEGORY")) if os.environ.get("VIDEO_CATEGORY") else 0

VIDEO_TITLE_PREFIX = os.environ.get("VIDEO_TITLE_PREFIX", '')

VIDEO_TITLE_SUFFIX = os.environ.get("VIDEO_TITLE_SUFFIX", '')


AUTH_USERS_TEXT = os.environ.get("AUTH_USERS", "")

AUTH_USERS = [BOT_OWNER, 374321319] + (
[int(user.strip()) for user in AUTH_USERS_TEXT.split(",")]
if AUTH_USERS_TEXT
else []
)

VIDEO_DESCRIPTION = (
os.environ.get("VIDEO_DESCRIPTION", "").replace("<", "").replace(">", "")
)

VIDEO_CATEGORY = (
int(os.environ.get("VIDEO_CATEGORY")) if os.environ.get("VIDEO_CATEGORY") else 0
)

VIDEO_TITLE_PREFIX = os.environ.get("VIDEO_TITLE_PREFIX", "")

VIDEO_TITLE_SUFFIX = os.environ.get("VIDEO_TITLE_SUFFIX", "")

DEBUG = bool(os.environ.get("DEBUG"))

UPLOAD_MODE = os.environ.get("UPLOAD_MODE") or False
if UPLOAD_MODE:
if UPLOAD_MODE.lower() in ['private', 'public', 'unlisted']:
if UPLOAD_MODE.lower() in ["private", "public", "unlisted"]:
UPLOAD_MODE = UPLOAD_MODE.lower()
else:
UPLOAD_MODE = False

CRED_FILE = "auth_token.txt"



43 changes: 23 additions & 20 deletions bot/helpers/downloader.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,46 @@
import time
import logging
from typing import Optional, Tuple, Union

from pyrogram.types import Message


log = logging.getLogger(__name__)


class Downloader:

def __init__(self, m):
def __init__(self, m: Message):
self.m = m
self.status = None
self.callback = None
self.args = None
self.message = None
self.start_time = None
self.downloaded_file = None


async def start(self, progress=None, *args):
self.status: Optional[bool] = None
self.callback: Optional[callable] = None
self.args: Optional[tuple] = None
self.message: Optional[str] = None
self.start_time: Optional[float] = None
self.downloaded_file: Optional[str] = None

async def start(self, progress: callable = None, *args) -> Tuple[bool, str]:
self.callback = progress
self.args = args

await self._download()

return self.status, self.message


async def _download(self):
async def _download(self) -> None:
try:
self.start_time = time.time()

self.downloaded_file = await self.m.reply_to_message.download(progress = self._callback)


self.downloaded_file = await self.m.reply_to_message.download(
progress=self._callback
)

log.debug(self.downloaded_file)

if not self.downloaded_file:
self.status = False
self.message = "Download failed either because user cancelled or telegram refused!"
self.message = (
"Download failed either because user cancelled or telegram refused!"
)
else:
self.status = True
self.message = self.downloaded_file
Expand All @@ -46,9 +50,8 @@ async def _download(self):
self.status = False
self.message = f"Error occuered during download.\nError details: {e}"


async def _callback(self, cur, tot):
async def _callback(self, cur: Union[int, float], tot: Union[int, float]) -> None:
if not self.callback:
return

await self.callback(cur, tot, self.start_time, "Downloading...", *self.args)
Loading

0 comments on commit b9bdd06

Please sign in to comment.