From 4455b6902a21888a3741e4fc9f5a97b6c9673d0c Mon Sep 17 00:00:00 2001 From: William Kaiser Date: Sat, 18 Feb 2023 16:14:21 -0500 Subject: [PATCH] V0.1.0 --- .gitignore | 3 +++ README.md | 38 +++++++++++++++++++++++++++------ pyproject.toml | 8 +++---- src/tiktok_uploader/__main__.py | 2 +- src/tiktok_uploader/auth.py | 8 +++---- src/tiktok_uploader/browsers.py | 21 ++++++++++++------ src/tiktok_uploader/cli.py | 2 +- src/tiktok_uploader/upload.py | 4 ++++ test.py | 4 ++-- 9 files changed, 64 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index b6e4761..39f226e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Customized items +test.py + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/README.md b/README.md index e0c65ff..6b6ded0 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,28 @@ -# TikTok Uploader +# ⬆️ TikTok Uploader + +![Downloads](https://img.shields.io/github/downloads/wkaisertexas/tiktok-uploader/total) + A selenium-based uploader for TikTok Videos ## Usage While Tik Tok is pretty good about blocking bots (giving "too many requests" with FireFox's default installation"), simply copying your session tokens is enough to bypass this restriction and be able to upload your videos. +```bash +$ tiktok_uploader -v video.mp4 -d "this is my description" -c cookies.txt +``` + +```python +upload_video('video.mp4', description='this is my description', cookies='cookies.txt') # single video + +# Multiple Videos +videos = [ + {'path': 'video.mp4', 'description': 'this is my description'}, + {'path': 'video2.mp4', 'description': 'this is also my description'} +] +auth = AuthBackend(cookies='cookies.txt') +upload_videos(videos=videos, auth=auth) +``` ### Uploading videos This library revolves around the 'upload_video' function which takes in a list of videos which have **filenames** and **descriptions** and are passed as follows: @@ -33,7 +51,9 @@ Finally, pass the saved file path to `upload_videos`. upload_videos(cookies='cookies.txt') ``` -> Optionally, if you would like to pass your own cookies you may do as an array of dictionaries with keys `name`, `value`, `domain`, `path` and `expiry` though there really is no need +> Optionally, if you would like to pass your own cookies you may do as an array of dictionaries with keys `name`, `value`, `domain`, `path` and `expiry` + +> The login script does have a `login_accounts` function, but this gets detected by the #### Login-based Authentication @@ -42,9 +62,7 @@ upload_videos(cookies='cookies.txt') To use password authentication, pass your **username** and **password** as keyword arguments to `upload_videos`. ```python -upload_videos(username='mytiktokusername', password='*******') -# or -upload_videos(login_info=('mytiktokusername', '*********') +upload_video(username='mytiktokusername', password='*******') ``` > As a side note, try to avoid keeping passwords as values in your code. You can read more about why [here](https://medium.com/twodigits/keep-passwords-out-of-source-code-why-and-how-e84f9004815a). @@ -68,10 +86,16 @@ options = Options() options.add_argument('start-maximized') -upload_videos(browser_agent=options) +upload_videos(options=options) ``` -> Note: Options are Browser specific +> Note: Make sure to use the right selenium options for your browser + +### Headless Browsers + +**Headless browsers do not work at this time** + +> If more experienced in Webscraping, I would really appreciate helping make this work. [undetected-chromedriver](https://github.com/ultrafunkamsterdam/undetected-chromedriver) was already tried and did not work ## Installation diff --git a/pyproject.toml b/pyproject.toml index 4e794c9..4badc42 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [project] name = "tiktok_uploader" -version = "0.0.1" +version = "0.1.0" authors = [ { name="William Kaiser", email="wkaisertexas@gmail.com" }, ] -description = "An automatic TikTok video uploader w/ CLI" +description = "An automatic TikTok video uploader w/ CLI. Uses cookies from your browser to manage authentication and upload your videos automatically." readme = "README.md" requires-python = ">=3.11" keywords = [ @@ -33,8 +33,8 @@ test = [ "pytest", ] -# [tool.hatch.envs.test.matrix] -# python = ["38", "39"] +[tool.hatch.envs.test.matrix] +python = ["38", "39"] [project.scripts] tiktok_uploader = "tiktok_uploader.cli:cli" diff --git a/src/tiktok_uploader/__main__.py b/src/tiktok_uploader/__main__.py index 26fc7e4..0850f13 100644 --- a/src/tiktok_uploader/__main__.py +++ b/src/tiktok_uploader/__main__.py @@ -6,7 +6,7 @@ def main(): """ Entry point for TikTok-Uploader, makes a call to CLI """ - cli.main() + cli.cli() if __name__ == '__main__': main() diff --git a/src/tiktok_uploader/auth.py b/src/tiktok_uploader/auth.py index f6d0c13..7ee39c6 100644 --- a/src/tiktok_uploader/auth.py +++ b/src/tiktok_uploader/auth.py @@ -14,9 +14,9 @@ class AuthBackend: username: str password: str - cookies: dict + cookies: list - def __init__(self, username=None, password=None, cookies=[], cookies_path=None): + def __init__(self, username: str = '', password: str = '', cookies=[], cookies_path=None): """ Creates the authenticaiton backend @@ -84,7 +84,7 @@ def get_cookies(self, path: str) -> dict: ] -def login_accounts(driver=None, accounts=[(None, None)]) -> list: +def login_accounts(driver=None, accounts=[(None, None)], *args, **kwargs) -> list: """ Authenticates the accounts using the browser backend and saves the required credentials @@ -92,7 +92,7 @@ def login_accounts(driver=None, accounts=[(None, None)]) -> list: - driver -> the webdriver to use - accounts -> a list of tuples of the form (username, password) """ - driver = driver or get_browser(headless=False) + driver = driver or get_browser(headless=False, *args, **kwargs) cookies = {} for account in accounts: diff --git a/src/tiktok_uploader/browsers.py b/src/tiktok_uploader/browsers.py index 9b25e15..21e3f9e 100644 --- a/src/tiktok_uploader/browsers.py +++ b/src/tiktok_uploader/browsers.py @@ -22,16 +22,15 @@ from tiktok_uploader import config - -def get_browser(name: str = 'chrome', *args, **kwargs) -> webdriver: +def get_browser(name: str = 'chrome', options=None, *args, **kwargs) -> webdriver: """ Gets a browser based on the name with the ability to pass in additional arguments """ # get the web driver for the browser - driver_to_use = get_driver(name=name) + driver_to_use = get_driver(name=name, *args, **kwargs) # gets the options for the browser - options = get_default_options(name=name, *args, **kwargs) + options = options or get_default_options(name=name, *args, **kwargs) # combines them together into a completed driver service = get_service(name=name) @@ -45,7 +44,7 @@ def get_browser(name: str = 'chrome', *args, **kwargs) -> webdriver: return driver -def get_driver(name: str = 'chrome') -> webdriver: +def get_driver(name: str = 'chrome', *args, **kwargs) -> webdriver: """ Gets the web driver function for the browser """ @@ -96,11 +95,19 @@ def get_default_options(name: str, *args, **kwargs): raise Exception(f'{name} is not a supported browser') -def chrome_defaults(headless: bool = False, *args, **kwargs) -> ChromeOptions: +def chrome_defaults(headless: bool = False, undetectable=False, *args, **kwargs) -> ChromeOptions: """ Creates Chrome with Options """ - options = ChromeOptions() + + if undetectable: + options = uc.ChromeOptions() + if headless: + options.headless = True + options.add_argument( '--headless' ) + return options # TODO: Maybe customize the undetectable chromedriver + else: + options = ChromeOptions() # default options diff --git a/src/tiktok_uploader/cli.py b/src/tiktok_uploader/cli.py index 63e0d47..0f3fd20 100644 --- a/src/tiktok_uploader/cli.py +++ b/src/tiktok_uploader/cli.py @@ -109,7 +109,7 @@ def get_auth_args(): return parser.parse_args() -def validate_auth_args(args: dict): +def validate_auth_args(args): """ Preforms validation on each input given """ diff --git a/src/tiktok_uploader/upload.py b/src/tiktok_uploader/upload.py index 2a6ef0c..4be09a5 100644 --- a/src/tiktok_uploader/upload.py +++ b/src/tiktok_uploader/upload.py @@ -91,12 +91,16 @@ def upload_videos(videos: list = None, auth: AuthBackend = None, browser='chrome print(e) if i == n-1: # adds if the last retry failed.append(video) + + if on_complete: # calls the user-specified on-complete function + on_complete(video) if config['quit_on_end']: driver.quit() return failed + def complete_upload_form(driver, path: str, description: str, *args, **kwargs) -> None: """ Actually uploades each video diff --git a/test.py b/test.py index 88af176..e54e7c0 100644 --- a/test.py +++ b/test.py @@ -4,8 +4,8 @@ if __name__ == '__main__': # Uploads a video to TikTok - # upload_video('../../Desktop/video.mp4', description='this is a story about why I don', cookies='../../Desktop/cookies.txt', headless=True) + upload_video('../../Desktop/video.mp4', description='this is a story about why I don', cookies='../../Desktop/cookies.txt', headless=True) # tests cookies for accounts -> Works except I get too many requests error (really sad) # tiktok_uploader.auth.login_accounts(accounts=[('server314159@gmail.com', 'asdfse12323dfsd!')]) - system('tiktok_uploader -v ../../Desktop/video.mp4 -d "this is a story about why I don\'t like tiktok" -c ../../Desktop/cookies.txt') \ No newline at end of file + # system('tiktok_uploader -v ../../Desktop/video.mp4 -d "this is a story about why I don\'t like tiktok" -c ../../Desktop/cookies.txt') \ No newline at end of file