tiktokautouploader 2.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ /.DS_Store
2
+ /build
3
+ /dist
4
+ /tiktokautouploader.egg-info
@@ -0,0 +1,130 @@
1
+ # autotiktokuploader Documentation
2
+
3
+ This document provides detailed information about the parameters and usage of the `upload_tiktok` function in the **AutoTikTokUploader** library. The function is designed to automate the process of uploading or scheduling videos to TikTok with additional features such as adding TikTok sounds, hashtags, and conducting copyright checks.
4
+
5
+ ### ❗ Key Sections:
6
+
7
+ - **Parameter Explanations**: Provides detailed descriptions of each parameter, including the valid options and their effects.
8
+ - **Initialization Info**: Details instances that occur during first run of function
9
+ - **Important Notes**: Highlights important account recommendations and limitations related to TikTok accounts and scheduling.
10
+ - **Supported Captchas**: Showcases the Captcha's the code is able to solve
11
+ - **Runtime**: Provides an estimate of how much runtime is added by different parameters
12
+ - **Example Usage**: Demonstrates a practical example of how to use the function.
13
+
14
+ ## πŸ“œ Function: `upload_tiktok`
15
+
16
+ ### Parameters
17
+
18
+ - **`video`** (str)
19
+ - The input path for your video file that you want to upload to TikTok.
20
+
21
+ - **`description`** (str)
22
+ - The description text that will accompany the video when uploaded. hashtags included in description will NOT work, must be included in `hashtags` parameter
23
+
24
+ - **`hashtags`** (list of str, optional, default: None)
25
+ - An array of hashtag strings (e.g., `['#example', '#fun']`) to be added to the video description.
26
+
27
+ - **`sound_name`** (str, optional, default: None)
28
+ - The name of the TikTok sound that you want to use for the video. This sound will be applied during the upload. Defaults to None
29
+
30
+ - **`sound_aud_vol`** (str, optional, default: `'mix'`)
31
+ - Determines the volume mix between the TikTok sound and the original video audio. Accepts one of the following options:
32
+ - `'mix'`: The TikTok sound and original audio will have a 50/50 split.
33
+ - `'background'`: The original audio will be louder, and the TikTok sound will be faintly heard in the background.
34
+ - `'main'`: The TikTok sound will be louder, and the original audio will be faintly heard in the background.
35
+
36
+ - **`schedule`** (str, optional, default: None)
37
+ - The time you want the video to be uploaded. The format should be `HH:MM`, and the minute (`MM`) must be a multiple of 5. The scheduled time must be at least 15 minutes later than the current local time (unless scheduling for a different day). The time should be in your local time zone.
38
+
39
+ - **`day`** (int, optional, default: None) (requires time in `schedule` also)
40
+ - If you want to schedule the video for a different day, this parameter specifies the day of the current month on which to upload the video. i.e: If current day is Sept 3rd, day=5 will upload video on Sept 5th
41
+ - NOTE: You will also need to specify time of upload in `schedule` parameter or else `day` won't work
42
+
43
+ **Important**:
44
+ - You can only schedule a maximum of 10 days in advance.
45
+ - If scheduling for the next month, you can only schedule within the first 5 days of the next month (as long as they are also within 10 days of the current date). i.e: If current day is Sept 30th, day=5 will upload on Oct 5th, 6 WILL NOT WORK.
46
+
47
+ - **`copyrightcheck`** (bool, optional, default: `False`)
48
+ - If set to `True`, the function will conduct a copyright check on TikTok before uploading. If the check fails, the video will be saved as a draft, and the code execution will stop.
49
+
50
+ - **`suppressprint`** (bool, optional, default: `True`)
51
+ - Suppresses print messages that indicate the progress of the video upload. It is recommended to set this to `False` when first running the code to see progress and ensure everything works correctly.
52
+
53
+ ### πŸ“ IMPORTANT Notes
54
+ - **`suppressprint`** (bool, optional, default: `False`)
55
+ - When `True`, uppresses print messages that indicate the progress of the video upload. It is recommended to set this to `False` when first running the code to see progress and ensure everything works correctly.
56
+
57
+
58
+ ### πŸ› οΈ Initialization Info
59
+
60
+ - **During FIRST RUN:**
61
+
62
+ - You will be asked to log-in to TikTok, your cookies from your log-in will then be stored in a file `TK_cookes.json`. If you wish to change the account you want to post to, just delete the cookies file and you will be prompted to log in again.
63
+
64
+ - Javascript dependencies will be automatically downloaded, once downloaded it will not attempt to download it again unless the files get deleted.
65
+
66
+ - Runtime might be a 20-30 seconds longer than usual, this is due to libraries being built and runtime should return to normal after first run
67
+
68
+
69
+ ### πŸ“ Important Notes
70
+
71
+ - **TikTok Account Recommendations**:
72
+ - It is recommended to have a TikTok account with at least a few weeks of cookies built up for the best results.
73
+ - Your TikTok account MUST have the ability to save drafts; otherwise, the code will not work correctly. If your account does not have the ability to save drafts, you will be prompted with a warning message during execution
74
+ - **VERY IMPORTANT: TikTok Account Recommendations**:
75
+ - It is recommended to have a TikTok account with at least a few weeks of history built up for the best results.
76
+ - Your TikTok account MUST have the ability to save drafts; otherwise, the code may not work correctly.
77
+
78
+ - **Scheduling Limitations**:
79
+ - The function allows scheduling up to 10 days in advance.
80
+ - If you need to schedule a video for the next month, the video can only be uploaded within the first 5 days of that month (as long as these days are within 10 days from the current date).
81
+
82
+ ## β›” Supported Captchas:
83
+
84
+ - **Captcha solver currently works perfectly for Captcha's of type:**
85
+ <p align="center">
86
+ <img src="READMEimage/CaptchaImage1.jpg" alt="" width="200"/>
87
+ </p>
88
+
89
+ <p align="center">
90
+ <img src="READMEimage/CaptchaImage2.jpg" alt="" width="200"/>
91
+ </p>
92
+
93
+ ## πŸ•°οΈ Runtime:
94
+ **Total runtime depends on how long TikTok takes to upload your video to their servers, usually it should take anywhere between 1-5 minutes, however, here are approximations on how much runtime is added by each parameter**
95
+
96
+ - **Captcha's:** 3 - 5 secs
97
+ - **Adding Sound:** 3 - 4 secs
98
+ - **Scheduling:** 1 - 3 secs
99
+ - **Copyright Check:** 4 - 7 secs (In rare cases it can be longer due to large file size)
100
+
101
+ - **NOTE:** When running for the FIRST TIME ONLY, it may take an extra 20 - 30 seconds at the beginning for the code to start running as libraries are being built
102
+
103
+
104
+ ## Example Usage
105
+
106
+ Here’s a basic example of how to use the `upload_tiktok` function:
107
+
108
+ ```python
109
+ from autotiktokuploader import upload_tiktok
110
+
111
+ upload_tiktok(
112
+ video='path/to/your/video.mp4',
113
+ description='Check out my latest video!',
114
+ hashtags=['#fun', '#viral'],
115
+ sound_name='popular_sound',
116
+ sound_aud_vol='mix',
117
+ schedule='15:00',
118
+ day=5,
119
+ copyrightcheck=True,
120
+ suppressprint=False
121
+ )
122
+ ```
123
+
124
+ For more details, please feel free to contact me at haziqmk123@gmail.com or on LinkedIn (on my github profile)
125
+
126
+ ### Key Sections:
127
+
128
+ - **Parameter Explanations**: Provides detailed descriptions of each parameter, including the valid options and their effects.
129
+ - **Other Notes**: Highlights recommendations and limitations related to TikTok accounts and scheduling.
130
+ - **Example Usage**: Demonstrates a practical example of how to use the function.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [2024] [HAZIQ KHALID]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.3
2
+ Name: tiktokautouploader
3
+ Version: 2.7
4
+ Summary: Upload or schedule videos to TikTok with TikTok sounds and hashtags that work.
5
+ Project-URL: Homepage, https://github.com/haziq-exe/TikTokAutoUploader
6
+ Author-email: HAZIQ KHALID <haziqmk123@gmail.com>
7
+ License-File: LICENSE.md
8
+ Keywords: autoupload,tiktok,tiktokautoupload
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.8
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Requires-Dist: pillow>=8.0.0
17
+ Requires-Dist: playwright>=1.0.0
18
+ Requires-Dist: requests>=2.0.0
19
+ Requires-Dist: scikit-learn>=0.24.0
@@ -0,0 +1,118 @@
1
+ <div align="center">
2
+ <h1>tiktokautouploader</h1>
3
+ </div>
4
+
5
+
6
+ ### AUTOMATE TIKTOK UPLOADS. USE TRENDING SOUNDS πŸ”Š, AUTOSOLVES CAPTCHAS 🧠, ADD WORKING HASHTAGS πŸ’―, SCHEDULE UPLOADS πŸ—“οΈ AND MORE 🎁
7
+
8
+ [![PyPI version](https://img.shields.io/pypi/v/tiktokautouploader.svg)](https://pypi.org/project/tiktokautouploader/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+
10
+
11
+ <p align="center">
12
+ <img src="READMEimage/Image.png" alt="" width="400"/>
13
+ </p>
14
+
15
+ ## πŸš€ Features
16
+
17
+ - **πŸ” Bypass/Auto Solve Captchas:** No more manual captcha solving; fully automated process!
18
+ - **🎡 Use TikTok Sounds:** Seamlessly add popular TikTok sounds to your videos.
19
+ - **πŸ—“ Schedule Uploads:** Upload videos at specific times or upto 10 days in advance with our scheduling feature.
20
+ - **πŸ” Copyright Check:** Ensure your video is safe from copyright claims before uploading.
21
+ - **🏷 Add Working Hashtags:** Increase your reach by adding effective hashtags that actually work.
22
+
23
+
24
+ ## πŸ“¦ Installation
25
+
26
+ 1. **Python Installation:** Install the package using `pip`:
27
+
28
+ ```bash
29
+ pip install tiktokautouploader
30
+ ```
31
+
32
+ ---
33
+
34
+
35
+ ## βš™οΈ Pre-requisites
36
+
37
+ 1. **Node.js:** You must have Node.js installed on your system, as some parts of this package rely on JavaScript code. If you don't have Node.js installed, you can download it from [nodejs.org](https://nodejs.org/).
38
+
39
+ - **Note:** The necessary JavaScript dependencies (`playwright`,`playwright-extra`, `puppeteer-extra-plugin-stealth`) will be automatically installed the first time you run the function, so you don't need to install them manually. Make sure that `npm` (Node.js package manager) is available in your system's PATH.
40
+
41
+ 2. **Browser Binaries:** You'll need to install the necessary browser binaries for `playwright`.
42
+
43
+ To do so, just run the following command AFTER installing the package:
44
+
45
+ ```bash
46
+ playwright install
47
+ ```
48
+
49
+ ## πŸ“ Quick-Start
50
+
51
+ Here's how to upload a video to TikTok with hashtags using `tiktokautouploader`:
52
+
53
+ NOTE: It is highly recommended you read DOCUMENTATION.md before using the library.
54
+
55
+ The first time you run the code, you will be prompted to log-in, this will only occur the first time the function is used. Check documentation for more info.
56
+
57
+ ```python
58
+ from tiktokautouploader import upload_tiktok
59
+
60
+ video_path = 'path/to/your/video.mp4'
61
+ description = 'Check out my latest TikTok video!'
62
+ hashtags = ['#fun', '#viral']
63
+
64
+ upload_tiktok(video=video_path, description=description, hashtags=hashtags)
65
+
66
+ ```
67
+
68
+ ### Upload with TikTok Sound
69
+
70
+ ```python
71
+ upload_tiktok(video=video_path, description=description, sound_name='trending_sound')
72
+ ```
73
+
74
+ PLEASE READ DOCUMENTATION FOR MORE INFO.
75
+
76
+ ### Schedule an Upload
77
+
78
+ ```python
79
+ upload_tiktok(video=video_path, description=description, schedule='03:10', day=11)
80
+ ```
81
+
82
+ PLEASE READ DOCUMENTATION FOR MORE INFO
83
+
84
+ ### Perform Copyright Check Before Uploading
85
+
86
+ ```python
87
+ upload_tiktok(video=video_path, description=description, hashtags=hashtags, copyrightcheck=True)
88
+ ```
89
+
90
+ ## 🎯 Why Choose `autotiktokuploader`?
91
+
92
+ - **No more captchas:** Fully automated uploads without interruptions, If captchas do show up, no worries, they will be solved. (read documentation for more info)
93
+ - **Maximize your reach:** Add popular sounds and effective hashtags that work to boost visibility.
94
+ - **Stay compliant:** Built-in copyright checks to avoid unforeseen takedowns.
95
+ - **Convenient scheduling:** Post at the right time, even when you're away.
96
+
97
+ ## πŸ›  Dependencies
98
+
99
+ This library requires the following dependencies:
100
+
101
+ - `playwright`
102
+ - `requests`
103
+ - `Pillow`
104
+ - `transformers`
105
+ - `torch`
106
+ - `scikit-learn`
107
+ - `inference`
108
+
109
+ These will be automatically installed when you install the package.
110
+
111
+ ## πŸ‘€ Author
112
+
113
+ Created by **Haziq Khalid**. Feel free to reach out at [haziqmk123@gmail.com](mailto:haziqmk123@gmail.com) or my LinkedIn.
114
+
115
+ ## πŸ“„ License
116
+
117
+ This project is licensed under the MIT License. See the [LICENSE](LICENSE.md) file for details.
118
+ ```
@@ -0,0 +1,61 @@
1
+ [build-system]
2
+ requires = ["hatchling", "setupwheel"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "tiktokautouploader"
7
+ version = "2.7"
8
+ description = "Upload or schedule videos to TikTok with TikTok sounds and hashtags that work."
9
+ long-description = "file: README.md"
10
+ long-description-content-type = "text/markdown"
11
+ keywords = ["tiktok", "autoupload", "tiktokautoupload"]
12
+ authors = [
13
+ { name = "HAZIQ KHALID", email = "haziqmk123@gmail.com" }
14
+ ]
15
+ urls = { "Homepage" = "https://github.com/haziq-exe/TikTokAutoUploader" }
16
+ classifiers = [
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.8",
19
+ "Programming Language :: Python :: 3.9",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "License :: OSI Approved :: MIT License",
23
+ "Operating System :: OS Independent",
24
+ ]
25
+ python-requires = ">=3.6"
26
+
27
+ dependencies = [
28
+ "playwright>=1.0.0",
29
+ "requests>=2.0.0",
30
+ "Pillow>=8.0.0",
31
+ "scikit-learn>=0.24.0",
32
+ ]
33
+
34
+ [tool.hatch.build]
35
+ tags = ["wheel"]
36
+
37
+ [tool.hatch.metadata]
38
+ name = "tiktokautouploader"
39
+ version = "2.7"
40
+ description = "Upload or schedule videos to TikTok with TikTok sounds and hashtags that work."
41
+ long_description = "file: README.md"
42
+ long_description_content_type = "text/markdown"
43
+ keywords = ["tiktok", "autoupload", "tiktokautoupload"]
44
+ authors = [
45
+ { name = "HAZIQ KHALID", email = "haziqmk123@gmail.com" }
46
+ ]
47
+ urls = { "Homepage" = "https://github.com/haziq-exe/TikTokAutoUploader" }
48
+ classifiers = [
49
+ "Programming Language :: Python :: 3",
50
+ "Programming Language :: Python :: 3.8",
51
+ "Programming Language :: Python :: 3.9",
52
+ "Programming Language :: Python :: 3.10",
53
+ "Programming Language :: Python :: 3.11",
54
+ "License :: OSI Approved :: MIT License",
55
+ "Operating System :: OS Independent",
56
+ ]
57
+ python_requires = ">=3.6"
58
+
59
+ [tool.hatch.envs]
60
+ packages = ["autotiktokuploader"]
61
+ include = ["autotiktokuploader/__init__.py", "autotiktokuploader/function.py", "autotiktokuploader/Js_assets/login.js"]
@@ -0,0 +1,43 @@
1
+ const { chromium } = require('playwright-extra')
2
+
3
+ // Load the stealth plugin and use defaults (all tricks to hide playwright usage)
4
+ // Note: playwright-extra is compatible with most puppeteer-extra plugins
5
+ const stealth = require('puppeteer-extra-plugin-stealth')()
6
+
7
+ // Add the plugin to playwright (any number of plugins can be added)
8
+ chromium.use(stealth)
9
+
10
+ // Define a delay function
11
+ function delay(time) {
12
+ return new Promise(resolve => setTimeout(resolve, time));
13
+ }
14
+
15
+ // Define a function to check for redirects
16
+ async function checkForRedirect(page) {
17
+ const currentUrl = page.url();
18
+ const pattern = /^https:\/\/www\.tiktok\.com\/foryou/;
19
+ return pattern.test(currentUrl);
20
+ }
21
+
22
+ (async () => {
23
+ let redirected = false;
24
+ const browser = await chromium.launch({ headless: false });
25
+ const page = await browser.newPage();
26
+ await page.goto('https://www.tiktok.com/login');
27
+
28
+ // Loop until redirect is detected
29
+ while (!redirected) {
30
+ redirected = await checkForRedirect(page);
31
+ if (!redirected) {
32
+ await delay(1000);
33
+ }
34
+ }
35
+
36
+ delay(2000)
37
+ const cookies = await page.context().cookies();
38
+ const fs = require('fs');
39
+ fs.writeFileSync('TK_cookies.json', JSON.stringify(cookies, null, 2));
40
+
41
+ // Close the browser
42
+ await browser.close();
43
+ })();
@@ -0,0 +1,3 @@
1
+ from .function import upload_tiktok
2
+
3
+ __all__ = ['upload_tiktok']
@@ -0,0 +1,559 @@
1
+ from playwright.sync_api import sync_playwright
2
+ import json
3
+ import time
4
+ import inference
5
+ import subprocess
6
+ import pkg_resources
7
+ import requests
8
+ from PIL import Image
9
+ import sys
10
+ import os
11
+ import warnings
12
+ warnings.filterwarnings("ignore")
13
+
14
+ def no_draft_warning():
15
+ print("SAVE AS DRAFT BUTTON NOT FOUND; CODE WILL CONTINUE RUNNING BUT VIDEO IS UNLIKELY TO UPLOAD; BEING ABLE TO SAVE AS DRAFT IS ESSENTIAL TO BEING ABLE TO EDIT VIDEOS AND POST, PLEASE BUILD UP ENOUGH ACCOUNT HISTORY TO BE ABLE TO SAVE DRAFTS ")
16
+
17
+ def login_warning():
18
+ print("NO COOKIES FILE FOUND, PLEASE LOG-IN WHEN PROMPTED")
19
+
20
+ def save_cookies(cookies):
21
+ with open('TK_cookies.json', 'w') as file:
22
+ json.dump(cookies, file, indent=4)
23
+
24
+ def run_javascript():
25
+ js_file_path = pkg_resources.resource_filename(__name__, 'Js_assets/login.js')
26
+ result = subprocess.run(['node', js_file_path], capture_output=True, text=True)
27
+ return result.stdout, result.stderr
28
+
29
+ def install_js_dependencies():
30
+ js_dir = pkg_resources.resource_filename(__name__, 'Js_assets')
31
+ node_modules_path = os.path.join(js_dir, 'node_modules')
32
+
33
+ if not os.path.exists(node_modules_path):
34
+ print("JavaScript dependencies not found. Installing...")
35
+ subprocess.run(['npm', 'install', 'playwright', 'playwright-extra', 'puppeteer-extra-plugin-stealth', '--silent'], cwd=js_dir, check=True)
36
+ else:
37
+ print("found dependencies")
38
+ time.sleep(0.1)
39
+
40
+
41
+ def read_cookies(cookies_path):
42
+ cookie_read = False
43
+ try:
44
+ with open(cookies_path , 'r') as cookiefile:
45
+ cookies = json.load(cookiefile)
46
+
47
+ for cookie in cookies:
48
+ if cookie.get('sameSite') not in ['Strict', 'Lax', 'None']:
49
+ cookie['sameSite'] = 'Lax'
50
+
51
+ cookie_read = True
52
+ except:
53
+ sys.exit("ERROR: CANT READ COOKIES FILE")
54
+
55
+ return cookies, cookie_read
56
+
57
+ def detect_redirect(page):
58
+ redirect_detected = False
59
+
60
+ def on_response(response):
61
+ nonlocal redirect_detected
62
+ if response.request.redirected_from:
63
+ redirect_detected = True
64
+
65
+ page.on('response', on_response)
66
+
67
+ return redirect_detected
68
+
69
+ def understood_Qs(question):
70
+ understood_terms = {
71
+ 'touchdowns' : 'football',
72
+ 'orange and round': 'basketball',
73
+ 'used in hoops': 'basketball',
74
+ 'has strings': 'guitar',
75
+ 'oval and inflatable': 'football',
76
+ 'strumming': 'guitar',
77
+ 'bounces on courts': 'basketball',
78
+ 'musical instrument': 'guitar',
79
+ 'laces': 'football',
80
+ 'bands': 'guitar',
81
+ 'leather': 'football'
82
+ }
83
+
84
+ for key in understood_terms.keys():
85
+ if key in question:
86
+ item = understood_terms.get(key)
87
+ return item
88
+
89
+ return 'N.A'
90
+
91
+
92
+
93
+ def get_image_src(page):
94
+ image_url = page.get_attribute("img#captcha-verify-image", "src")
95
+ return image_url
96
+
97
+ def download_image(image_url):
98
+ response = requests.get(image_url)
99
+ image_path = "captcha_image.jpg"
100
+ with open(image_path, "wb") as f:
101
+ f.write(response.content)
102
+ return image_path
103
+
104
+
105
+ def run_inference_on_image_tougher(image_path, object):
106
+
107
+ found_proper = False
108
+ model = inference.get_model("captcha-2-6ehbe/1")
109
+ results = model.infer(image=image_path)
110
+
111
+ class_names = []
112
+ bounding_boxes = []
113
+ for obj in results[0].predictions:
114
+ class_names.append(obj.class_name)
115
+ bounding_boxes.append({
116
+ "x": obj.x,
117
+ "y": obj.y,
118
+ "width": obj.width,
119
+ "height": obj.height
120
+ })
121
+
122
+ bounding_box = []
123
+ class_to_click = object
124
+ for i, classes in enumerate(class_names):
125
+ if classes == class_to_click:
126
+ bounding_box.append(bounding_boxes[i])
127
+
128
+ if len(bounding_box) == 2:
129
+ found_proper = True
130
+
131
+ found_proper = True
132
+
133
+ return bounding_box, found_proper
134
+
135
+ def run_inference_on_image(image_path):
136
+
137
+ model = inference.get_model("tk-3nwi9/2")
138
+ results = model.infer(image=image_path)
139
+
140
+ class_names = []
141
+ bounding_boxes = []
142
+ for obj in results[0].predictions:
143
+ class_names.append(obj.class_name)
144
+ bounding_boxes.append({
145
+ "x": obj.x,
146
+ "y": obj.y,
147
+ "width": obj.width,
148
+ "height": obj.height
149
+ })
150
+
151
+ already_written = []
152
+ bounding_box = []
153
+ class_to_click = []
154
+ for i, classes in enumerate(class_names):
155
+ if classes in already_written:
156
+ class_to_click.append(classes)
157
+ bounding_box.append(bounding_boxes[i])
158
+ index = already_written.index(classes)
159
+ bounding_box.append(bounding_boxes[index])
160
+
161
+ already_written.append(classes)
162
+
163
+ found = False
164
+
165
+ if len(class_to_click) == 1:
166
+ found = True
167
+
168
+ return bounding_box, found
169
+
170
+ def convert_to_webpage_coordinates(bounding_boxes, image_x, image_y, image_height_web, image_width_web, image_height_real, image_width_real):
171
+
172
+ webpage_coordinates = []
173
+ for box in bounding_boxes:
174
+ x_box = box['x']
175
+ y_box = box['y']
176
+ rel_x = (x_box * image_width_web) / image_width_real
177
+ rel_y = (y_box * image_height_web) / image_height_real
178
+ x_cord = image_x + rel_x
179
+ y_cord = image_y + rel_y
180
+
181
+ webpage_coordinates.append((x_cord, y_cord))
182
+
183
+ return webpage_coordinates
184
+
185
+ def click_on_objects(page, object_coords):
186
+ for (x, y) in object_coords:
187
+ page.mouse.click(x, y)
188
+ time.sleep(0.5)
189
+
190
+
191
+
192
+ def upload_tiktok(video, description, hashtags=None, sound_name=None, sound_aud_vol='mix', schedule=None, day=None, copyrightcheck=False, suppressprint=False):
193
+
194
+ """
195
+ UPLOADS VIDEO TO TIKTOK
196
+ ------------------------------------------------------------------------------------------------------------------------------------------------c
197
+ video (str) -> path to video to upload
198
+ description (str) -> description for video
199
+ hashtags (str)(array) -> hashtags for video
200
+ cookies_path (str) -> path to tik tok cookies .json file
201
+ sound_name (str) -> name of tik tok sound to use for video
202
+ sound_aud_vol (str) -> volume of tik tok sound, 'main', 'mix' or 'background', check documentation for more info -> https://github.com/haziq-exe/TikTokAutoUploader
203
+ schedule (str) -> format HH:MM, your local time to upload video
204
+ day (int) -> day to schedule video for, check documentation for more info -> https://github.com/haziq-exe/TikTokAutoUploader
205
+ copyrightcheck (bool) -> include copyright check or not; CODE FAILS IF FAIL COPYRIGHT CHECK
206
+ suppressprint (bool) -> True means function doesnt print anything to provide updates on progress
207
+ --------------------------------------------------------------------------------------------------------------------------------------------
208
+ """
209
+
210
+ retries = 0
211
+ cookie_read = False
212
+
213
+ if os.path.exists('TK_cookies.json'):
214
+ cookies, cookie_read = read_cookies(cookies_path='TK_cookies.json')
215
+
216
+ if cookie_read == False:
217
+ install_js_dependencies()
218
+ login_warning()
219
+ run_javascript()
220
+
221
+ cookies, cookie_read = read_cookies("TK_cookies.json")
222
+ if cookie_read == False:
223
+ sys.exit("ERROR READING COOKIES")
224
+
225
+
226
+ with sync_playwright() as p:
227
+ browser = p.chromium.launch(headless=True)
228
+ context = browser.new_context()
229
+ context.add_cookies(cookies)
230
+ page = context.new_page()
231
+ url = 'https://www.tiktok.com/tiktokstudio/upload?from=upload&lang=en'
232
+
233
+ while retries < 2:
234
+ try:
235
+ page.goto(url, timeout=30000)
236
+ except:
237
+ retries +=1
238
+ time.sleep(5)
239
+ if retries == 2:
240
+ sys.exit("ERROR: TIK TOK PAGE FAILED TO LOAD, try again.")
241
+ else:
242
+ break
243
+
244
+ detected = False
245
+ captcha = False
246
+ while detected == False:
247
+ if page.locator('.upload-text-container').is_visible():
248
+ detected = True
249
+ else:
250
+ if page.locator('div.VerifyBar___StyledDiv-sc-12zaxoy-0.hRJhHT').is_visible():
251
+ detected = True
252
+ captcha = True
253
+ else:
254
+ time.sleep(0.1)
255
+
256
+ if captcha == True:
257
+ print("CAPTCHA FOUND")
258
+ image = get_image_src(page)
259
+ if image:
260
+ if suppressprint == False:
261
+ print("CAPTCHA DETECTED, Attempting to solve")
262
+ solved = False
263
+ attempts = 0
264
+ while solved == False:
265
+ attempts += 1
266
+ question = page.locator('div.VerifyBar___StyledDiv-sc-12zaxoy-0.hRJhHT').text_content()
267
+ if 'Select 2 objects that are the same' in question:
268
+ found = False
269
+ while found == False:
270
+ page.click('span.secsdk_captcha_refresh--text')
271
+ time.sleep(0.5)
272
+ image = get_image_src(page)
273
+ img_path = download_image(image)
274
+ b_box, found = run_inference_on_image(image_path=img_path)
275
+
276
+ with Image.open(img_path) as img:
277
+ image_size = img.size
278
+
279
+ image = page.locator('#captcha-verify-image')
280
+ image.wait_for()
281
+ box = image.bounding_box()
282
+ image_x = box['x']
283
+ image_y = box['y']
284
+ image_height_web = box['height']
285
+ image_width_web = box['width']
286
+ image_width_real, image_height_real = image_size
287
+
288
+ webpage_coords = convert_to_webpage_coordinates(b_box, image_x, image_y, image_height_web, image_width_web, image_height_real, image_width_real)
289
+ click_on_objects(page, webpage_coords)
290
+ time.sleep(0.5)
291
+ if attempts > 5:
292
+ sys.exit("FAILED TO SOLVE CAPTCHA")
293
+ try:
294
+ page.wait_for_selector('.upload-text-container', timeout=10000)
295
+ os.remove('captcha_image.jpg')
296
+ if suppressprint == False:
297
+ print("Captcha Solved")
298
+ solved = True
299
+ except:
300
+ continue
301
+ else:
302
+ found_prop = False
303
+ while found_prop == False:
304
+ page.click('span.secsdk_captcha_refresh--text')
305
+ time.sleep(0.5)
306
+ question = page.locator('div.VerifyBar___StyledDiv-sc-12zaxoy-0.hRJhHT').text_content()
307
+ objectclick = understood_Qs(question)
308
+ while objectclick == 'N.A':
309
+ page.click('span.secsdk_captcha_refresh--text')
310
+ page.wait_for_selector('div.VerifyBar___StyledDiv-sc-12zaxoy-0.hRJhHT')
311
+ time.sleep(2)
312
+ question = page.locator('div.VerifyBar___StyledDiv-sc-12zaxoy-0.hRJhHT').text_content()
313
+ objectclick = understood_Qs(question)
314
+ image = get_image_src(page)
315
+ img_path = download_image(image)
316
+ b_box, found_prop = run_inference_on_image_tougher(image_path=img_path, object=objectclick)
317
+
318
+ with Image.open(img_path) as img:
319
+ image_size = img.size
320
+
321
+ image = page.locator('#captcha-verify-image')
322
+ image.wait_for()
323
+ box = image.bounding_box()
324
+ image_x = box['x']
325
+ image_y = box['y']
326
+ image_height_web = box['height']
327
+ image_width_web = box['width']
328
+ image_width_real, image_height_real = image_size
329
+
330
+ webpage_coords = convert_to_webpage_coordinates(b_box, image_x, image_y, image_height_web, image_width_web, image_height_real, image_width_real)
331
+
332
+
333
+ click_on_objects(page, webpage_coords)
334
+ time.sleep(0.5)
335
+ page.click("div.verify-captcha-submit-button")
336
+ if attempts > 5:
337
+ sys.exit("FAILED TO SOLVE CAPTCHA")
338
+ try:
339
+ page.wait_for_selector('.upload-text-container', timeout=10000)
340
+ solved = True
341
+ os.remove('captcha_image.jpg')
342
+ if suppressprint == False:
343
+ print("Captcha Solved")
344
+ except:
345
+ continue
346
+
347
+
348
+
349
+ try:
350
+ page.set_input_files('input[type="file"][accept="video/*"]', f'{video}')
351
+ except:
352
+ sys.exit("ERROR: FAILED TO INPUT FILE. Possible Issues: Wifi too slow, file directory wrong, or check documentation to see if captcha is solvable")
353
+
354
+ page.wait_for_selector('div[data-contents="true"]')
355
+ page.click('div[data-contents="true"]')
356
+ if suppressprint == False:
357
+ print("done inputting File, waiting for tiktok to load onto their server, this may take a couple of minutes, depending on your video length")
358
+ time.sleep(0.5)
359
+ if description == None:
360
+ sys.exit("ERROR: PLEASE INCLUDE A DESCRIPTION")
361
+
362
+ for _ in range(len(video) + 2):
363
+ page.keyboard.press("Backspace")
364
+ page.keyboard.press("Delete")
365
+
366
+ time.sleep(0.5)
367
+
368
+ page.keyboard.type(description)
369
+
370
+ for _ in range(3):
371
+ page.keyboard.press("Enter")
372
+
373
+ if hashtags != None:
374
+ for hashtag in hashtags:
375
+ if hashtag[0] != '#':
376
+ hashtag = "#" + hashtag
377
+
378
+ page.keyboard.type(hashtag)
379
+ time.sleep(0.5)
380
+ try:
381
+ page.click(f'span.hash-tag-topic:has-text("{hashtag}")', timeout=1000)
382
+ except:
383
+ try:
384
+ page.click('span.hash-tag-topic', timeout=1000)
385
+ except:
386
+ page.keyboard.press("Backspace")
387
+ try:
388
+ page.click('span.hash-tag-topic', timeout=1000)
389
+ except:
390
+ if suppressprint == False:
391
+ print(f"Tik tok hashtag not working for {hashtag}, moving onto next")
392
+ for _ in range(len(hashtag)):
393
+ page.keyboard.press("Backspace")
394
+
395
+ if suppressprint == False:
396
+ print("Description and Hashtags added")
397
+
398
+ try:
399
+ page.wait_for_function("document.querySelector('.info-progress-num').textContent.trim() === '100%'", timeout=12000000)
400
+ except:
401
+ sys.exit("ERROR: TIK TOK TOOK TOO LONG TO UPLOAD YOUR FILE (>20min). Try again, if issue persists then try a lower file size or different wifi connection")
402
+
403
+ time.sleep(0.5)
404
+ if suppressprint == False:
405
+ print("Tik tok done loading file onto servers")
406
+ try:
407
+ page.click('button.TUXButton.TUXButton--default.TUXButton--large.TUXButton--secondary:has-text("Save draft")', timeout=10000)
408
+ page.wait_for_selector("path[d='M37.37 4.85a4.01 4.01 0 0 0-.99-.79 3 3 0 0 0-2.72 0c-.45.23-.81.6-1 .79a9 9 0 0 1-.04.05l-19.3 19.3c-1.64 1.63-2.53 2.52-3.35 3.47a36 36 0 0 0-4.32 6.16c-.6 1.1-1.14 2.24-2.11 4.33l-.3.6c-.4.75-.84 1.61-.8 2.43a2.5 2.5 0 0 0 2.37 2.36c.82.05 1.68-.4 2.44-.79l.59-.3c2.09-.97 3.23-1.5 4.33-2.11a36 36 0 0 0 6.16-4.32c.95-.82 1.84-1.71 3.47-3.34l19.3-19.3.05-.06a3 3 0 0 0 .78-3.71c-.22-.45-.6-.81-.78-1l-.02-.02-.03-.03-3.67-3.67a8.7 8.7 0 0 1-.06-.05ZM16.2 26.97 35.02 8.15l2.83 2.83L19.03 29.8c-1.7 1.7-2.5 2.5-3.33 3.21a32 32 0 0 1-7.65 4.93 32 32 0 0 1 4.93-7.65c.73-.82 1.51-1.61 3.22-3.32Z']")
409
+ page.click("path[d='M37.37 4.85a4.01 4.01 0 0 0-.99-.79 3 3 0 0 0-2.72 0c-.45.23-.81.6-1 .79a9 9 0 0 1-.04.05l-19.3 19.3c-1.64 1.63-2.53 2.52-3.35 3.47a36 36 0 0 0-4.32 6.16c-.6 1.1-1.14 2.24-2.11 4.33l-.3.6c-.4.75-.84 1.61-.8 2.43a2.5 2.5 0 0 0 2.37 2.36c.82.05 1.68-.4 2.44-.79l.59-.3c2.09-.97 3.23-1.5 4.33-2.11a36 36 0 0 0 6.16-4.32c.95-.82 1.84-1.71 3.47-3.34l19.3-19.3.05-.06a3 3 0 0 0 .78-3.71c-.22-.45-.6-.81-.78-1l-.02-.02-.03-.03-3.67-3.67a8.7 8.7 0 0 1-.06-.05ZM16.2 26.97 35.02 8.15l2.83 2.83L19.03 29.8c-1.7 1.7-2.5 2.5-3.33 3.21a32 32 0 0 1-7.65 4.93 32 32 0 0 1 4.93-7.65c.73-.82 1.51-1.61 3.22-3.32Z']")
410
+ page.wait_for_selector('div[data-contents="true"]')
411
+ page.wait_for_function("document.querySelector('.info-progress-num').textContent.trim() === '100%'", timeout=3000000)
412
+ time.sleep(0.5)
413
+ except:
414
+ no_draft_warning()
415
+
416
+ if sound_name != None:
417
+ page.click("div.TUXButton-label:has-text('Edit video')")
418
+ page.wait_for_selector("input.search-bar-input")
419
+ page.fill(f"input.search-bar-input", f"{sound_name}")
420
+ time.sleep(0.5)
421
+ page.click("div.TUXButton-label:has-text('Search')")
422
+ try:
423
+ page.wait_for_selector('div.music-card-container')
424
+ page.click("div.music-card-container")
425
+ page.wait_for_selector("div.TUXButton-label:has-text('Use')")
426
+ page.click("div.TUXButton-label:has-text('Use')")
427
+ except:
428
+ sys.exit("ERROR: SOUND NOT FOUND, VIDEO SAVED AS DRAFT")
429
+ try:
430
+ page.wait_for_selector('img[src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMSAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTAgNy41MDE2QzAgNi42NzMxNyAwLjY3MTU3MyA2LjAwMTYgMS41IDYuMDAxNkgzLjU3NzA5QzMuODY4MDUgNi4wMDE2IDQuMTQ0NTggNS44NzQ4OCA0LjMzNDU1IDUuNjU0NDlMOC43NDI1NSAwLjU0MDUyQzkuMzQ3OCAtMC4xNjE2NjggMTAuNSAwLjI2NjM3NCAxMC41IDEuMTkzNDFWMTguOTY3MkMxMC41IDE5Ljg3NDUgOS4zODg5NCAyMC4zMTI5IDguNzY5NDIgMTkuNjVMNC4zMzE3OSAxNC45MDIxQzQuMTQyNjkgMTQuNjk5OCAzLjg3ODE2IDE0LjU4NDkgMy42MDEyMiAxNC41ODQ5SDEuNUMwLjY3MTU3MyAxNC41ODQ5IDAgMTMuOTEzNCAwIDEzLjA4NDlWNy41MDE2Wk01Ljg0OTQ1IDYuOTYwMjdDNS4yNzk1NiA3LjYyMTQzIDQuNDQ5OTcgOC4wMDE2IDMuNTc3MDkgOC4wMDE2SDJWMTIuNTg0OUgzLjYwMTIyQzQuNDMyMDMgMTIuNTg0OSA1LjIyNTY0IDEyLjkyOTUgNS43OTI5NSAxMy41MzY0TDguNSAxNi40MzI4VjMuODg1MjJMNS44NDk0NSA2Ljk2MDI3WiIgZmlsbD0iIzE2MTgyMyIgZmlsbC1vcGFjaXR5PSIwLjYiLz4KPHBhdGggZD0iTTEzLjUxNSA3LjE5MTE5QzEzLjM0MjQgNi45NzU1OSAxMy4zMzk5IDYuNjYwNTYgMTMuNTM1MiA2LjQ2NTNMMTQuMjQyMyA1Ljc1ODE5QzE0LjQzNzYgNS41NjI5MyAxNC43NTU4IDUuNTYxNzUgMTQuOTM1NiA1Ljc3MTM2QzE2Ljk5NTkgOC4xNzM2MiAxNi45OTU5IDExLjgyOCAxNC45MzU2IDE0LjIzMDNDMTQuNzU1OCAxNC40Mzk5IDE0LjQzNzYgMTQuNDM4NyAxNC4yNDIzIDE0LjI0MzVMMTMuNTM1MiAxMy41MzY0QzEzLjMzOTkgMTMuMzQxMSAxMy4zNDI0IDEzLjAyNjEgMTMuNTE1IDEyLjgxMDVDMTQuODEzIDExLjE4ODUgMTQuODEzIDguODEzMTIgMTMuNTE1IDcuMTkxMTlaIiBmaWxsPSIjMTYxODIzIiBmaWxsLW9wYWNpdHk9IjAuNiIvPgo8cGF0aCBkPSJNMTYuNzE3MiAxNi43MTgzQzE2LjUyMTkgMTYuNTIzMSAxNi41MjMxIDE2LjIwNzQgMTYuNzA3MiAxNi4wMDE3QzE5LjcyNTcgMTIuNjMgMTkuNzI1NyA3LjM3MTY4IDE2LjcwNzIgNC4wMDAwMUMxNi41MjMxIDMuNzk0MjcgMTYuNTIxOSAzLjQ3ODU4IDE2LjcxNzIgMy4yODMzMkwxNy40MjQzIDIuNTc2MjFDMTcuNjE5NSAyLjM4MDk1IDE3LjkzNyAyLjM4MDIgMTguMTIzMyAyLjU4NDA4QzIxLjkwOTkgNi43MjkyNiAyMS45MDk5IDEzLjI3MjQgMTguMTIzMyAxNy40MTc2QzE3LjkzNyAxNy42MjE1IDE3LjYxOTUgMTcuNjIwNyAxNy40MjQzIDE3LjQyNTVMMTYuNzE3MiAxNi43MTgzWiIgZmlsbD0iIzE2MTgyMyIgZmlsbC1vcGFjaXR5PSIwLjYiLz4KPC9zdmc+Cg=="]')
431
+ page.click('img[src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMSAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTAgNy41MDE2QzAgNi42NzMxNyAwLjY3MTU3MyA2LjAwMTYgMS41IDYuMDAxNkgzLjU3NzA5QzMuODY4MDUgNi4wMDE2IDQuMTQ0NTggNS44NzQ4OCA0LjMzNDU1IDUuNjU0NDlMOC43NDI1NSAwLjU0MDUyQzkuMzQ3OCAtMC4xNjE2NjggMTAuNSAwLjI2NjM3NCAxMC41IDEuMTkzNDFWMTguOTY3MkMxMC41IDE5Ljg3NDUgOS4zODg5NCAyMC4zMTI5IDguNzY5NDIgMTkuNjVMNC4zMzE3OSAxNC45MDIxQzQuMTQyNjkgMTQuNjk5OCAzLjg3ODE2IDE0LjU4NDkgMy42MDEyMiAxNC41ODQ5SDEuNUMwLjY3MTU3MyAxNC41ODQ5IDAgMTMuOTEzNCAwIDEzLjA4NDlWNy41MDE2Wk01Ljg0OTQ1IDYuOTYwMjdDNS4yNzk1NiA3LjYyMTQzIDQuNDQ5OTcgOC4wMDE2IDMuNTc3MDkgOC4wMDE2SDJWMTIuNTg0OUgzLjYwMTIyQzQuNDMyMDMgMTIuNTg0OSA1LjIyNTY0IDEyLjkyOTUgNS43OTI5NSAxMy41MzY0TDguNSAxNi40MzI4VjMuODg1MjJMNS44NDk0NSA2Ljk2MDI3WiIgZmlsbD0iIzE2MTgyMyIgZmlsbC1vcGFjaXR5PSIwLjYiLz4KPHBhdGggZD0iTTEzLjUxNSA3LjE5MTE5QzEzLjM0MjQgNi45NzU1OSAxMy4zMzk5IDYuNjYwNTYgMTMuNTM1MiA2LjQ2NTNMMTQuMjQyMyA1Ljc1ODE5QzE0LjQzNzYgNS41NjI5MyAxNC43NTU4IDUuNTYxNzUgMTQuOTM1NiA1Ljc3MTM2QzE2Ljk5NTkgOC4xNzM2MiAxNi45OTU5IDExLjgyOCAxNC45MzU2IDE0LjIzMDNDMTQuNzU1OCAxNC40Mzk5IDE0LjQzNzYgMTQuNDM4NyAxNC4yNDIzIDE0LjI0MzVMMTMuNTM1MiAxMy41MzY0QzEzLjMzOTkgMTMuMzQxMSAxMy4zNDI0IDEzLjAyNjEgMTMuNTE1IDEyLjgxMDVDMTQuODEzIDExLjE4ODUgMTQuODEzIDguODEzMTIgMTMuNTE1IDcuMTkxMTlaIiBmaWxsPSIjMTYxODIzIiBmaWxsLW9wYWNpdHk9IjAuNiIvPgo8cGF0aCBkPSJNMTYuNzE3MiAxNi43MTgzQzE2LjUyMTkgMTYuNTIzMSAxNi41MjMxIDE2LjIwNzQgMTYuNzA3MiAxNi4wMDE3QzE5LjcyNTcgMTIuNjMgMTkuNzI1NyA3LjM3MTY4IDE2LjcwNzIgNC4wMDAwMUMxNi41MjMxIDMuNzk0MjcgMTYuNTIxOSAzLjQ3ODU4IDE2LjcxNzIgMy4yODMzMkwxNy40MjQzIDIuNTc2MjFDMTcuNjE5NSAyLjM4MDk1IDE3LjkzNyAyLjM4MDIgMTguMTIzMyAyLjU4NDA4QzIxLjkwOTkgNi43MjkyNiAyMS45MDk5IDEzLjI3MjQgMTguMTIzMyAxNy40MTc2QzE3LjkzNyAxNy42MjE1IDE3LjYxOTUgMTcuNjIwNyAxNy40MjQzIDE3LjQyNTVMMTYuNzE3MiAxNi43MTgzWiIgZmlsbD0iIzE2MTgyMyIgZmlsbC1vcGFjaXR5PSIwLjYiLz4KPC9zdmc+Cg=="]')
432
+ time.sleep(0.5)
433
+ sliders = page.locator("input.scaleInput")
434
+
435
+ if sound_aud_vol == 'background':
436
+ slider1 = sliders.nth(0)
437
+ bounding_box1 = slider1.bounding_box()
438
+ if bounding_box1:
439
+ x1 = bounding_box1["x"] + (bounding_box1["width"] * 0.95)
440
+ y1 = bounding_box1["y"] + bounding_box1["height"] / 2
441
+ page.mouse.click(x1, y1)
442
+
443
+ slider2 = sliders.nth(1)
444
+ bounding_box2 = slider2.bounding_box()
445
+ if bounding_box2:
446
+ x2 = bounding_box2["x"] + (bounding_box2["width"] * 0.097)
447
+ y2 = bounding_box2["y"] + bounding_box2["height"] / 2
448
+ page.mouse.click(x2, y2)
449
+
450
+ if sound_aud_vol == 'main':
451
+ slider1 = sliders.nth(0)
452
+ bounding_box1 = slider1.bounding_box()
453
+ if bounding_box1:
454
+ x1 = bounding_box1["x"] + (bounding_box1["width"] * 0.097)
455
+ y1 = bounding_box1["y"] + bounding_box1["height"] / 2
456
+ page.mouse.click(x1, y1)
457
+ slider2 = sliders.nth(1)
458
+ bounding_box2 = slider2.bounding_box()
459
+ if bounding_box2:
460
+ x2 = bounding_box2["x"] + (bounding_box2["width"] * 0.95)
461
+ y2 = bounding_box2["y"] + bounding_box2["height"] / 2
462
+ page.mouse.click(x2, y2)
463
+ except:
464
+ sys.exit("ERROR ADJUSTING SOUND VOLUME, VIDEO SAVED AS DRAFT")
465
+
466
+ page.wait_for_selector("div.TUXButton-label:has-text('Save edit')")
467
+ page.click("div.TUXButton-label:has-text('Save edit')")
468
+ if suppressprint == False:
469
+ print("Added sound")
470
+
471
+ page.wait_for_selector('div[data-contents="true"]')
472
+ if schedule != None:
473
+ try:
474
+ hour = schedule[0:2]
475
+ minute = schedule[3:]
476
+ if (int(minute) % 5) != 0:
477
+ sys.exit("MINUTE FORMAT ERROR: PLEASE MAKE SURE MINUTE YOU SCHEDULE AT IS A MULTIPLE OF 5 UNTIL 60 (i.e: 40), VIDEO SAVED AS DRAFT")
478
+
479
+ except:
480
+ sys.exit("SCHEDULE TIME ERROR: PLEASE MAKE SURE YOUR SCHEDULE TIME IS A STRING THAT FOLLOWS THE 24H FORMAT 'HH:MM', VIDEO SAVED AS DRAFT")
481
+
482
+ page.locator('div.TUXRadioStandalone.TUXRadioStandalone--medium').nth(1).click()
483
+ time.sleep(0.5)
484
+ if day != None:
485
+ page.locator('div.TUXTextInputCore-trailingIconWrapper').nth(1).click()
486
+ time.sleep(0.5)
487
+ try:
488
+ page.locator(f'span.day.valid:has-text("{day}")').click()
489
+ except:
490
+ sys.exit("SCHEDULE DAY ERROR: ERROR WITH SCHEDULED DAY, read documentation for more information on format of day")
491
+ try:
492
+ page.locator('div.TUXTextInputCore-trailingIconWrapper').nth(0).click()
493
+ time.sleep(0.5)
494
+ page.locator(f'.tiktok-timepicker-option-text:has-text("{hour}")').nth(0).scroll_into_view_if_needed()
495
+ page.locator(f'.tiktok-timepicker-option-text:has-text("{hour}")').nth(0).click()
496
+ time.sleep(0.5)
497
+ page.locator('div.TUXTextInputCore-trailingIconWrapper').nth(0).click()
498
+ time.sleep(0.5)
499
+ page.locator(f'.tiktok-timepicker-option-text.tiktok-timepicker-right:has-text("{minute}")').nth(0).scroll_into_view_if_needed()
500
+ time.sleep(0.5)
501
+ page.locator(f'.tiktok-timepicker-option-text.tiktok-timepicker-right:has-text("{minute}")').nth(0).click()
502
+
503
+ if suppressprint == False:
504
+ print("Done scheduling video")
505
+
506
+ except:
507
+ sys.exit("SCHEDULING ERROR: VIDEO SAVED AS DRAFT")
508
+
509
+
510
+ if copyrightcheck == True:
511
+ page.locator(".TUXSwitch-input").nth(0).click()
512
+ while copyrightcheck == True:
513
+ time.sleep(0.5)
514
+ if page.locator("span", has_text="No issues detected.").is_visible():
515
+ if suppressprint == False:
516
+ print("Copyright check complete")
517
+ break
518
+ if page.locator("span", has_text="Copyright issues detected.").is_visible():
519
+ sys.exit("COPYRIGHT CHECK FAILED: VIDEO SAVED AS DRAFT, COPYRIGHT AUDIO DETECTED FROM TIKTOK")
520
+
521
+
522
+ try:
523
+ if schedule == None:
524
+ page.click('button.TUXButton.TUXButton--default.TUXButton--large.TUXButton--primary:has-text("Post")', timeout=10000)
525
+ uploaded = False
526
+ checks = 0
527
+ while uploaded == False:
528
+ if page.locator(':has-text("Leaving the page does not interrupt")').nth(0).is_visible():
529
+ print("I see it")
530
+ time.sleep(1)
531
+ break
532
+ time.sleep(0.2)
533
+ checks += 1
534
+ if checks > 100:
535
+ time.sleep(10)
536
+ if checks == 150:
537
+ break
538
+ else:
539
+ page.click('button.TUXButton.TUXButton--default.TUXButton--large.TUXButton--primary:has-text("Schedule")', timeout=10000)
540
+ uploaded = False
541
+ checks = 0
542
+ while uploaded == False:
543
+ if page.locator(':has-text("Leaving the page does not interrupt")').nth(0).is_visible():
544
+ time.sleep(1)
545
+ break
546
+ time.sleep(0.2)
547
+ checks += 1
548
+ if checks > 100:
549
+ time.sleep(10)
550
+ if checks == 150:
551
+ break
552
+ if suppressprint == False:
553
+ print("Done uploading video, NOTE: it may take a minute or two to show on TikTok")
554
+ except:
555
+ time.sleep(5)
556
+ sys.exit("ERROR UPLOADING: VIDEO HAS SAVED AS DRAFT BUT CANT UPLOAD")
557
+ time.sleep(1)
558
+
559
+ page.close()