tiktokautouploader 2.7__py2.py3-none-any.whl
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.
- tiktokautouploader/Js_assets/login.js +43 -0
- tiktokautouploader/__init__.py +3 -0
- tiktokautouploader/function.py +559 -0
- tiktokautouploader-2.7.dist-info/METADATA +19 -0
- tiktokautouploader-2.7.dist-info/RECORD +7 -0
- tiktokautouploader-2.7.dist-info/WHEEL +5 -0
- tiktokautouploader-2.7.dist-info/licenses/LICENSE.md +21 -0
@@ -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,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()
|
@@ -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,7 @@
|
|
1
|
+
tiktokautouploader/__init__.py,sha256=u7OWCK_u68ST8dfrkSF4Yw44CJOzV9NXI6ASRuoDfmE,64
|
2
|
+
tiktokautouploader/function.py,sha256=3S18jcePZCEDSQ2RQNs27Z3Xbp3QW9vyu1aNtbSAHsE,28340
|
3
|
+
tiktokautouploader/Js_assets/login.js,sha256=Qk_zUzRPeIN0ZnQVDtzahDNqIUJbIBbKoKw8GWW6et4,1318
|
4
|
+
tiktokautouploader-2.7.dist-info/METADATA,sha256=dQtuZ0yRpDNo1X_X6tjm2iNyCPmF7pRNQXZH1YgS1gs,816
|
5
|
+
tiktokautouploader-2.7.dist-info/WHEEL,sha256=fl6v0VwpzfGBVsGtkAkhILUlJxROXbA3HvRL6Fe3140,105
|
6
|
+
tiktokautouploader-2.7.dist-info/licenses/LICENSE.md,sha256=hYds_VJIpnS5gC73WhuWk2IY_e9BWjuEJthQCb9ThyU,1073
|
7
|
+
tiktokautouploader-2.7.dist-info/RECORD,,
|
@@ -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.
|