tiktokautouploader 4.0__py2.py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,40 @@
1
+ const { chromium } = require('playwright-extra')
2
+
3
+
4
+ const stealth = require('puppeteer-extra-plugin-stealth')()
5
+
6
+ chromium.use(stealth)
7
+
8
+
9
+ function sleep(time) {
10
+ return new Promise(resolve => setTimeout(resolve, time));
11
+ }
12
+
13
+
14
+ async function checkForRedirect(page) {
15
+ const currentUrl = page.url();
16
+ const pattern = /^https:\/\/www\.tiktok\.com\/foryou/;
17
+ return pattern.test(currentUrl);
18
+ }
19
+
20
+ (async () => {
21
+ let redirected = false;
22
+ const browser = await chromium.launch({ headless: false });
23
+ const page = await browser.newPage();
24
+ await page.goto('https://www.tiktok.com/login');
25
+
26
+ while (!redirected) {
27
+ redirected = await checkForRedirect(page);
28
+ if (!redirected) {
29
+ await sleep(1000);
30
+ }
31
+ }
32
+
33
+ sleep(2000)
34
+ const cookies = await page.context().cookies();
35
+ const fs = require('fs');
36
+ fs.writeFileSync('TK_cookies.json', JSON.stringify(cookies, null, 2));
37
+
38
+
39
+ await browser.close();
40
+ })();
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "tiktokautouploader-assets",
3
+ "version": "1.0.0",
4
+ "scripts": {
5
+ "postinstall": "npx playwright install chromium"
6
+ },
7
+ "dependencies": {
8
+ "playwright": "^1.48.2",
9
+ "playwright-extra": "^4.3.6",
10
+ "puppeteer-extra-plugin-stealth": "^2.11.2"
11
+ }
12
+ }
@@ -0,0 +1,3 @@
1
+ from .function import upload_tiktok
2
+
3
+ __all__ = ['upload_tiktok']
@@ -0,0 +1,857 @@
1
+ from playwright.sync_api import sync_playwright
2
+ import json
3
+ import time
4
+ import subprocess
5
+ from inference_sdk import InferenceHTTPClient
6
+ import pkg_resources
7
+ import requests
8
+ from PIL import Image
9
+ import sys
10
+ import os
11
+ import warnings
12
+ warnings.simplefilter("ignore")
13
+
14
+
15
+ def check_for_updates():
16
+ current_version = pkg_resources.get_distribution("tiktokautouploader").version
17
+ response = requests.get("https://pypi.org/pypi/tiktokautouploader/json")
18
+
19
+ if response.status_code == 200:
20
+ latest_version = response.json()["info"]["version"]
21
+ if current_version != latest_version:
22
+ print(f"WARNING: You are using version {current_version} of tiktokautouploader, "
23
+ f"PLEASE UPDATE TO LATEST VERSION {latest_version} FOR BEST EXPERIENCE.")
24
+
25
+ def login_warning(accountname):
26
+ print(f"NO COOKIES FILE FOUND FOR ACCOUNT {accountname}, PLEASE LOG-IN TO {accountname} WHEN PROMPTED")
27
+
28
+ def save_cookies(cookies):
29
+ with open('TK_cookies.json', 'w') as file:
30
+ json.dump(cookies, file, indent=4)
31
+
32
+ def check_expiry(accountname):
33
+ with open(f'TK_cookies_{accountname}.json', 'r') as file:
34
+ cookies = json.load(file)
35
+
36
+ current_time = int(time.time())
37
+ cookies_expire = []
38
+ expired = False
39
+ for cookie in cookies:
40
+ if cookie['name'] in ['sessionid', 'sid_tt', 'sessionid_ss', 'passport_auth_status']:
41
+ expiry = cookie.get('expires')
42
+ cookies_expire.append(expiry < current_time)
43
+
44
+ if all(cookies_expire):
45
+ expired = True
46
+
47
+ return expired
48
+
49
+ def run_javascript():
50
+ js_file_path = pkg_resources.resource_filename(__name__, 'Js_assets/login.js')
51
+ result = subprocess.run(['node', js_file_path], capture_output=True, text=True)
52
+ return result
53
+
54
+ def install_js_dependencies():
55
+ js_dir = pkg_resources.resource_filename(__name__, 'Js_assets')
56
+ node_modules_path = os.path.join(js_dir, 'node_modules')
57
+
58
+ if not os.path.exists(node_modules_path):
59
+ print("JavaScript dependencies not found. Installing...")
60
+ try:
61
+ subprocess.run(['npm', 'install', '--silent'], cwd=js_dir, check=True)
62
+ except subprocess.CalledProcessError as e:
63
+ print("An error occurred during npm installation.")
64
+ print(f"Error details: {e}")
65
+ else:
66
+ time.sleep(0.1)
67
+
68
+
69
+ def read_cookies(cookies_path):
70
+ cookie_read = False
71
+ try:
72
+ with open(cookies_path , 'r') as cookiefile:
73
+ cookies = json.load(cookiefile)
74
+
75
+ for cookie in cookies:
76
+ if cookie.get('sameSite') not in ['Strict', 'Lax', 'None']:
77
+ cookie['sameSite'] = 'Lax'
78
+
79
+ cookie_read = True
80
+ except:
81
+ sys.exit("ERROR: CANT READ COOKIES FILE")
82
+
83
+ return cookies, cookie_read
84
+
85
+ def detect_redirect(page):
86
+ redirect_detected = False
87
+
88
+ def on_response(response):
89
+ nonlocal redirect_detected
90
+ if response.request.redirected_from:
91
+ redirect_detected = True
92
+
93
+ page.on('response', on_response)
94
+
95
+ return redirect_detected
96
+
97
+ def understood_Qs(question):
98
+ understood_terms = {
99
+ 'touchdowns' : 'football',
100
+ 'orange and round': 'basketball',
101
+ 'used in hoops': 'basketball',
102
+ 'has strings': 'guitar',
103
+ 'oval and inflatable': 'football',
104
+ 'strumming': 'guitar',
105
+ 'bounces': 'basketball',
106
+ 'musical instrument': 'guitar',
107
+ 'laces': 'football',
108
+ 'bands': 'guitar',
109
+ 'leather': 'football',
110
+ 'leaves':'tree',
111
+ 'pages': 'book',
112
+ 'throwing': 'football',
113
+ 'tossed in a spiral': 'football',
114
+ 'spiky crown': 'pineapple',
115
+ 'pigskin': 'football',
116
+ 'photography': 'camera',
117
+ 'lens': 'camera',
118
+ 'grow': 'tree',
119
+ 'captures images': 'camera',
120
+ 'keeps doctors': 'apple',
121
+ 'crown': 'pineapple',
122
+ 'driven': 'car',
123
+ }
124
+
125
+ for key in understood_terms.keys():
126
+ if key in question:
127
+ item = understood_terms.get(key)
128
+ return item
129
+
130
+ return 'N.A'
131
+
132
+
133
+
134
+ def get_image_src(page):
135
+ image_url = page.get_attribute("img#captcha-verify-image", "src")
136
+ return image_url
137
+
138
+ def download_image(image_url):
139
+ response = requests.get(image_url)
140
+ image_path = "captcha_image.jpg"
141
+ with open(image_path, "wb") as f:
142
+ f.write(response.content)
143
+ return image_path
144
+
145
+ def run_inference_on_image_tougher(image_path, object):
146
+
147
+ k = 'n|KIeDZnRZiJ};iVHz;R'
148
+ rk = ''.join(chr((ord(c) - 3) % 256) for c in k)
149
+
150
+ CLIENT = InferenceHTTPClient(
151
+ api_url="https://detect.roboflow.com",
152
+ api_key=f"{rk}"
153
+ )
154
+
155
+ results = CLIENT.infer(image_path, model_id="captcha-2-6ehbe/2")
156
+
157
+ class_names = []
158
+ bounding_boxes = []
159
+ for obj in results['predictions']:
160
+ class_names.append(obj['class'])
161
+ bounding_boxes.append({
162
+ "x": obj['x'],
163
+ "y": obj['y'],
164
+ "width": obj['width'],
165
+ "height": obj['height']
166
+ })
167
+
168
+ bounding_box = []
169
+ class_to_click = object
170
+ for i, classes in enumerate(class_names):
171
+ if classes == class_to_click:
172
+ bounding_box.append(bounding_boxes[i])
173
+
174
+
175
+
176
+ return bounding_box
177
+
178
+ def run_inference_on_image(image_path):
179
+
180
+ k = 'n|KIeDZnRZiJ};iVHz;R'
181
+ rk = ''.join(chr((ord(c) - 3) % 256) for c in k)
182
+
183
+ CLIENT = InferenceHTTPClient(
184
+ api_url="https://detect.roboflow.com",
185
+ api_key=f"{rk}"
186
+ )
187
+
188
+ results = CLIENT.infer(image_path, model_id="tk-3nwi9/2")
189
+
190
+ class_names = []
191
+ bounding_boxes = []
192
+ for obj in results['predictions']:
193
+ class_names.append(obj['class'])
194
+ bounding_boxes.append({
195
+ "x": obj['x'],
196
+ "y": obj['y'],
197
+ "width": obj['width'],
198
+ "height": obj['height']
199
+ })
200
+
201
+ already_written = []
202
+ bounding_box = []
203
+ class_to_click = []
204
+ for i, detected_class in enumerate(class_names):
205
+ if detected_class in already_written:
206
+ class_to_click.append(detected_class)
207
+ bounding_box.append(bounding_boxes[i])
208
+ index = already_written.index(detected_class)
209
+ bounding_box.append(bounding_boxes[index])
210
+
211
+ already_written.append(detected_class)
212
+
213
+ found = False
214
+ if len(class_to_click) == 1:
215
+ found = True
216
+
217
+ return bounding_box, found
218
+
219
+ def convert_to_webpage_coordinates(bounding_boxes, image_x, image_y, image_height_web, image_width_web, image_height_real, image_width_real):
220
+
221
+ webpage_coordinates = []
222
+ for box in bounding_boxes:
223
+ x_box = box['x']
224
+ y_box = box['y']
225
+ rel_x = (x_box * image_width_web) / image_width_real
226
+ rel_y = (y_box * image_height_web) / image_height_real
227
+ x_cord = image_x + rel_x
228
+ y_cord = image_y + rel_y
229
+
230
+ webpage_coordinates.append((x_cord, y_cord))
231
+
232
+ return webpage_coordinates
233
+
234
+ def click_on_objects(page, object_coords):
235
+ for (x, y) in object_coords:
236
+ page.mouse.click(x, y)
237
+ time.sleep(0.5)
238
+
239
+
240
+
241
+ def upload_tiktok(video, description, accountname, hashtags=None, sound_name=None, sound_aud_vol='mix', schedule=None, day=None, copyrightcheck=False, suppressprint=False, headless=True, stealth=False):
242
+
243
+ """
244
+ UPLOADS VIDEO TO TIKTOK
245
+ ------------------------------------------------------------------------------------------------------------------------------------------------c
246
+ video (str) -> path to video to upload
247
+ description (str) -> description for video
248
+ accountname (str) -> account to upload on
249
+ hashtags (str)(array) -> hashtags for video
250
+ sound_name (str) -> name of tik tok sound to use for video
251
+ sound_aud_vol (str) -> volume of tik tok sound, 'main', 'mix' or 'background', check documentation for more info -> https://github.com/haziq-exe/TikTokAutoUploader
252
+ schedule (str) -> format HH:MM, your local time to upload video
253
+ day (int) -> day to schedule video for, check documentation for more info -> https://github.com/haziq-exe/TikTokAutoUploader
254
+ copyrightcheck (bool) -> include copyright check or not; CODE FAILS IF FAIL COPYRIGHT CHECK
255
+ suppressprint (bool) -> True means function doesnt print anything to provide updates on progress
256
+ headless (bool) -> run in headless mode or not
257
+ stealth (bool) -> will wait second(s) before each operation
258
+ --------------------------------------------------------------------------------------------------------------------------------------------
259
+ """
260
+ try:
261
+ check_for_updates()
262
+ except:
263
+ time.sleep(0.1)
264
+
265
+ retries = 0
266
+ cookie_read = False
267
+ oldQ = 'N.A'
268
+
269
+ if accountname == None:
270
+ sys.exit("PLEASE ENTER NAME OF ACCOUNT TO POST ON, READ DOCUMENTATION FOR MORE INFO")
271
+
272
+ if os.path.exists(f'TK_cookies_{accountname}.json'):
273
+ cookies, cookie_read = read_cookies(cookies_path=f'TK_cookies_{accountname}.json')
274
+ expired = check_expiry(accountname=accountname)
275
+ if expired == True:
276
+ os.remove(f'TK_cookies_{accountname}.json')
277
+ print(f"COOKIES EXPIRED FOR ACCOUNT {accountname}, PLEASE LOG-IN AGAIN")
278
+ cookie_read = False
279
+
280
+ if cookie_read == False:
281
+ install_js_dependencies()
282
+ login_warning(accountname=accountname)
283
+ result = run_javascript()
284
+ os.rename('TK_cookies.json', f'TK_cookies_{accountname}.json')
285
+
286
+ cookies, cookie_read = read_cookies(f"TK_cookies_{accountname}.json")
287
+ if cookie_read == False:
288
+ sys.exit("ERROR READING COOKIES")
289
+
290
+
291
+ with sync_playwright() as p:
292
+
293
+ browser = p.firefox.launch(headless=headless)
294
+ context = browser.new_context()
295
+ context.add_cookies(cookies)
296
+ page = context.new_page()
297
+ url = 'https://www.tiktok.com/tiktokstudio/upload?from=upload&lang=en'
298
+
299
+ if suppressprint == False:
300
+ print(f"Uploading to account '{accountname}'")
301
+
302
+ while retries < 2:
303
+ try:
304
+ page.goto(url, timeout=30000)
305
+ except:
306
+ retries +=1
307
+ time.sleep(5)
308
+ if retries == 2:
309
+ sys.exit("ERROR: TIK TOK PAGE FAILED TO LOAD, try again.")
310
+ else:
311
+ break
312
+
313
+ detected = False
314
+ captcha = False
315
+ while detected == False:
316
+ if page.locator('.upload-text-container').is_visible():
317
+ detected = True
318
+ else:
319
+ if page.locator('div.VerifyBar___StyledDiv-sc-12zaxoy-0.hRJhHT').is_visible():
320
+ detected = True
321
+ captcha = True
322
+ else:
323
+ time.sleep(0.1)
324
+
325
+ if captcha == True:
326
+ image = get_image_src(page)
327
+ if image:
328
+ if suppressprint == False:
329
+ print("CAPTCHA DETECTED, Attempting to solve")
330
+ solved = False
331
+ attempts = 0
332
+ question = page.locator('div.VerifyBar___StyledDiv-sc-12zaxoy-0.hRJhHT').text_content()
333
+ while solved == False:
334
+ attempts += 1
335
+ start_time = time.time()
336
+ while question == oldQ:
337
+ question = page.locator('div.VerifyBar___StyledDiv-sc-12zaxoy-0.hRJhHT').text_content()
338
+ if time.time() - start_time > 2:
339
+ break
340
+ if 'Select 2 objects that are the same' in question or 'Select two objects that are the same' in question:
341
+ found = False
342
+ while found == False:
343
+ page.click('span.secsdk_captcha_refresh--text')
344
+ image = get_image_src(page)
345
+ img_path = download_image(image)
346
+ b_box, found = run_inference_on_image(image_path=img_path)
347
+
348
+ with Image.open(img_path) as img:
349
+ image_size = img.size
350
+
351
+ imageweb = page.locator('#captcha-verify-image')
352
+ imageweb.wait_for()
353
+ box = imageweb.bounding_box()
354
+ image_x = box['x']
355
+ image_y = box['y']
356
+ image_height_web = box['height']
357
+ image_width_web = box['width']
358
+ image_width_real, image_height_real = image_size
359
+
360
+ webpage_coords = convert_to_webpage_coordinates(b_box, image_x, image_y, image_height_web, image_width_web, image_height_real, image_width_real)
361
+ if not webpage_coords:
362
+ webpage_coords.append((image_x + 50, image_y + 50))
363
+ click_on_objects(page, webpage_coords)
364
+ page.click("div.verify-captcha-submit-button")
365
+ time.sleep(0.5)
366
+ if attempts > 5:
367
+ sys.exit("FAILED TO SOLVE CAPTCHA")
368
+ while showedup == False:
369
+ if page.locator("div.captcha_verify_message.captcha_verify_message-pass").is_visible():
370
+ solved = True
371
+ showedup = True
372
+ os.remove('captcha_image.jpg')
373
+ if page.locator("div.captcha_verify_message.captcha_verify_message-fail").is_visible():
374
+ showedup = True
375
+ oldQ = question
376
+ page.click('span.secsdk_captcha_refresh--text')
377
+ else:
378
+ objectclick = understood_Qs(question)
379
+ while objectclick == 'N.A':
380
+ oldQ = question
381
+ page.click('span.secsdk_captcha_refresh--text')
382
+ start_time = time.time()
383
+ runs = 0
384
+ while question == oldQ:
385
+ runs += 1
386
+ question = page.locator('div.VerifyBar___StyledDiv-sc-12zaxoy-0.hRJhHT').text_content()
387
+ if runs > 1:
388
+ time.sleep(1)
389
+ if time.time() - start_time > 2:
390
+ break
391
+ objectclick = understood_Qs(question)
392
+ image = get_image_src(page)
393
+ img_path = download_image(image)
394
+ b_box = run_inference_on_image_tougher(image_path=img_path, object=objectclick)
395
+
396
+ with Image.open(img_path) as img:
397
+ image_size = img.size
398
+
399
+ imageweb = page.locator('#captcha-verify-image')
400
+ imageweb.wait_for()
401
+ box = imageweb.bounding_box()
402
+ image_x = box['x']
403
+ image_y = box['y']
404
+ image_height_web = box['height']
405
+ image_width_web = box['width']
406
+ image_width_real, image_height_real = image_size
407
+
408
+ webpage_coords = convert_to_webpage_coordinates(b_box, image_x, image_y, image_height_web, image_width_web, image_height_real, image_width_real)
409
+ if not webpage_coords:
410
+ webpage_coords.append((image_x + 50, image_y + 50))
411
+ click_on_objects(page, webpage_coords)
412
+ page.click("div.verify-captcha-submit-button")
413
+ time.sleep(1)
414
+ if attempts > 20:
415
+ sys.exit("FAILED TO SOLVE CAPTCHA")
416
+ showedup = False
417
+ while showedup == False:
418
+ if page.locator("div.captcha_verify_message.captcha_verify_message-pass").is_visible():
419
+ solved = True
420
+ showedup = True
421
+ os.remove('captcha_image.jpg')
422
+ if suppressprint == False:
423
+ print("CAPTCHA SOLVED")
424
+ if page.locator("div.captcha_verify_message.captcha_verify_message-fail").is_visible():
425
+ showedup = True
426
+ oldQ = question
427
+ page.click('span.secsdk_captcha_refresh--text')
428
+
429
+
430
+ try:
431
+ page.set_input_files('input[type="file"][accept="video/*"]', f'{video}')
432
+ except:
433
+ sys.exit("ERROR: FAILED TO INPUT FILE. Possible Issues: Wifi too slow, file directory wrong, or check documentation to see if captcha is solvable")
434
+ page.wait_for_selector('div[data-contents="true"]')
435
+ page.click('div[data-contents="true"]')
436
+ if suppressprint == False:
437
+ print("Entered File, waiting for tiktok to load onto their server, this may take a couple of minutes, depending on your video length")
438
+ time.sleep(0.5)
439
+ if description == None:
440
+ sys.exit("ERROR: PLEASE INCLUDE A DESCRIPTION")
441
+
442
+ for _ in range(len(video) + 2):
443
+ page.keyboard.press("Backspace")
444
+ page.keyboard.press("Delete")
445
+
446
+ time.sleep(0.5)
447
+
448
+ page.keyboard.type(description)
449
+
450
+ for _ in range(3):
451
+ page.keyboard.press("Enter")
452
+
453
+ if hashtags != None:
454
+ for hashtag in hashtags:
455
+ if hashtag[0] != '#':
456
+ hashtag = "#" + hashtag
457
+
458
+ page.keyboard.type(hashtag)
459
+ time.sleep(0.5)
460
+ try:
461
+ if stealth == True:
462
+ time.sleep(2)
463
+ page.click(f'span.hash-tag-topic:has-text("{hashtag}")', timeout=1000)
464
+ except:
465
+ try:
466
+ page.click('span.hash-tag-topic', timeout=1000)
467
+ except:
468
+ page.keyboard.press("Backspace")
469
+ try:
470
+ page.click('span.hash-tag-topic', timeout=1000)
471
+ except:
472
+ if suppressprint == False:
473
+ print(f"Tik tok hashtag not working for {hashtag}, moving onto next")
474
+ for _ in range(len(hashtag)):
475
+ page.keyboard.press("Backspace")
476
+
477
+ if suppressprint == False:
478
+ print("Description and Hashtags added")
479
+
480
+ try:
481
+ page.wait_for_selector('button:has-text("Post")[aria-disabled="false"]', timeout=12000000)
482
+ except:
483
+ 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")
484
+
485
+ time.sleep(0.2)
486
+ if suppressprint == False:
487
+ print("Tik tok done loading file onto servers")
488
+
489
+ if (schedule == None) and (day != None):
490
+ sys.exit("ERROR: CANT SCHEDULE FOR ANOTHER DAY USING 'day' WITHOUT ALSO INCLUDING TIME OF UPLOAD WITH 'schedule'; PLEASE ALSO INCLUDE TIME WITH 'schedule' PARAMETER")
491
+
492
+ if schedule != None:
493
+ try:
494
+ hour = schedule[0:2]
495
+ minute = schedule[3:]
496
+ if (int(minute) % 5) != 0:
497
+ 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")
498
+
499
+ except:
500
+ 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")
501
+
502
+ page.locator('label:has-text("Schedule")').click()
503
+ if stealth==True:
504
+ time.sleep(2)
505
+ visible = False
506
+ while visible == False:
507
+ if page.locator('button:has-text("Allow")').nth(0).is_visible():
508
+ if stealth == True:
509
+ time.sleep(1)
510
+ page.locator('button:has-text("Allow")').nth(0).click()
511
+ visible = True
512
+ time.sleep(0.1)
513
+ else:
514
+ if page.locator('div.TUXTextInputCore-trailingIconWrapper').nth(1).is_visible():
515
+ visible = True
516
+ time.sleep(0.1)
517
+ if day != None:
518
+ if stealth==True:
519
+ time.sleep(1)
520
+ page.locator('div.TUXTextInputCore-leadingIconWrapper:has(svg > path[d="M15 3a1 1 0 0 0-1 1v3h-1.4c-3.36 0-5.04 0-6.32.65a6 6 0 0 0-2.63 2.63C3 11.56 3 13.24 3 16.6v16.8c0 3.36 0 5.04.65 6.32a6 6 0 0 0 2.63 2.63c1.28.65 2.96.65 6.32.65h22.8c3.36 0 5.04 0 6.32-.65a6 6 0 0 0 2.63-2.63c.65-1.28.65-2.96.65-6.32V16.6c0-3.36 0-5.04-.65-6.32a6 6 0 0 0-2.63-2.63C40.44 7 38.76 7 35.4 7H34V4a1 1 0 0 0-1-1h-2a1 1 0 0 0-1 1v3H18V4a1 1 0 0 0-1-1h-2Zm-2.4 8H14v3a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1v-3h12v3a1 1 0 0 0 1 1h2a1 1 0 0 0 1-1v-3h1.4c1.75 0 2.82 0 3.62.07a5.11 5.11 0 0 1 .86.14h.03a2 2 0 0 1 .88.91 5.11 5.11 0 0 1 .14.86c.07.8.07 1.87.07 3.62v1.9H7v-1.9c0-1.75 0-2.82.07-3.62a5.12 5.12 0 0 1 .14-.86v-.03a2 2 0 0 1 .88-.87l.03-.01a5.11 5.11 0 0 1 .86-.14c.8-.07 1.87-.07 3.62-.07ZM7 22.5h34v10.9c0 1.75 0 2.82-.07 3.62a5.11 5.11 0 0 1-.14.86v.03a2 2 0 0 1-.88.87l-.03.01a5.11 5.11 0 0 1-.86.14c-.8.07-1.87.07-3.62.07H12.6c-1.75 0-2.82 0-3.62-.07a5.11 5.11 0 0 1-.89-.15 2 2 0 0 1-.87-.87l-.01-.03a5.12 5.12 0 0 1-.14-.86C7 36.22 7 35.15 7 33.4V22.5Z"])').click()
521
+ time.sleep(0.2)
522
+ try:
523
+ if stealth==True:
524
+ time.sleep(1)
525
+ page.locator(f'span.day.valid:text-is("{day}")').click()
526
+ except:
527
+ sys.exit("SCHEDULE DAY ERROR: ERROR WITH SCHEDULED DAY, read documentation for more information on format of day")
528
+ try:
529
+ time.sleep(0.2)
530
+ page.locator('div.TUXTextInputCore-leadingIconWrapper:has(svg > path[d="M24 2a22 22 0 1 0 0 44 22 22 0 0 0 0-44ZM6 24a18 18 0 1 1 36 0 18 18 0 0 1-36 0Z"])').click()
531
+ time.sleep(0.2)
532
+ page.locator(f'.tiktok-timepicker-option-text.tiktok-timepicker-right:text-is("{minute}")').scroll_into_view_if_needed()
533
+ time.sleep(0.2)
534
+ if stealth==True:
535
+ time.sleep(2)
536
+ page.locator(f'.tiktok-timepicker-option-text.tiktok-timepicker-right:text-is("{minute}")').click()
537
+ time.sleep(0.2)
538
+ if page.locator("div.tiktok-timepicker-time-picker-container").is_visible():
539
+ time.sleep(0.1)
540
+ else:
541
+ page.locator('div.TUXTextInputCore-leadingIconWrapper:has(svg > path[d="M24 2a22 22 0 1 0 0 44 22 22 0 0 0 0-44ZM6 24a18 18 0 1 1 36 0 18 18 0 0 1-36 0Z"])').click()
542
+ page.locator(f'.tiktok-timepicker-option-text.tiktok-timepicker-left:text-is("{hour}")').scroll_into_view_if_needed()
543
+ if stealth == True:
544
+ time.sleep(2)
545
+ page.locator(f'.tiktok-timepicker-option-text.tiktok-timepicker-left:text-is("{hour}")').click()
546
+ time.sleep(1)
547
+
548
+ if suppressprint == False:
549
+ print("Done scheduling video")
550
+
551
+ except:
552
+ sys.exit("SCHEDULING ERROR: VIDEO SAVED AS DRAFT")
553
+
554
+ sound_fail = False
555
+ if sound_name != None:
556
+ try:
557
+ if stealth == True:
558
+ time.sleep(2)
559
+ page.click("div.TUXButton-label:has-text('Edit video')")
560
+ except:
561
+ sound_fail = True
562
+ if sound_fail == False:
563
+ page.wait_for_selector("input.search-bar-input")
564
+ page.fill(f"input.search-bar-input", f"{sound_name}")
565
+ time.sleep(0.2)
566
+ if stealth == True:
567
+ time.sleep(2)
568
+ page.click("div.TUXButton-label:has-text('Search')")
569
+ try:
570
+ page.wait_for_selector('div.music-card-container')
571
+ if stealth == True:
572
+ time.sleep(0.5)
573
+ page.click("div.music-card-container")
574
+ page.wait_for_selector("div.TUXButton-label:has-text('Use')")
575
+ if stealth == True:
576
+ time.sleep(1)
577
+ page.click("div.TUXButton-label:has-text('Use')")
578
+ except:
579
+ sys.exit(f"ERROR: SOUND '{sound_name}' NOT FOUND")
580
+ try:
581
+ page.wait_for_selector('img[src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMSAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTAgNy41MDE2QzAgNi42NzMxNyAwLjY3MTU3MyA2LjAwMTYgMS41IDYuMDAxNkgzLjU3NzA5QzMuODY4MDUgNi4wMDE2IDQuMTQ0NTggNS44NzQ4OCA0LjMzNDU1IDUuNjU0NDlMOC43NDI1NSAwLjU0MDUyQzkuMzQ3OCAtMC4xNjE2NjggMTAuNSAwLjI2NjM3NCAxMC41IDEuMTkzNDFWMTguOTY3MkMxMC41IDE5Ljg3NDUgOS4zODg5NCAyMC4zMTI5IDguNzY5NDIgMTkuNjVMNC4zMzE3OSAxNC45MDIxQzQuMTQyNjkgMTQuNjk5OCAzLjg3ODE2IDE0LjU4NDkgMy42MDEyMiAxNC41ODQ5SDEuNUMwLjY3MTU3MyAxNC41ODQ5IDAgMTMuOTEzNCAwIDEzLjA4NDlWNy41MDE2Wk01Ljg0OTQ1IDYuOTYwMjdDNS4yNzk1NiA3LjYyMTQzIDQuNDQ5OTcgOC4wMDE2IDMuNTc3MDkgOC4wMDE2SDJWMTIuNTg0OUgzLjYwMTIyQzQuNDMyMDMgMTIuNTg0OSA1LjIyNTY0IDEyLjkyOTUgNS43OTI5NSAxMy41MzY0TDguNSAxNi40MzI4VjMuODg1MjJMNS44NDk0NSA2Ljk2MDI3WiIgZmlsbD0iIzE2MTgyMyIgZmlsbC1vcGFjaXR5PSIwLjYiLz4KPHBhdGggZD0iTTEzLjUxNSA3LjE5MTE5QzEzLjM0MjQgNi45NzU1OSAxMy4zMzk5IDYuNjYwNTYgMTMuNTM1MiA2LjQ2NTNMMTQuMjQyMyA1Ljc1ODE5QzE0LjQzNzYgNS41NjI5MyAxNC43NTU4IDUuNTYxNzUgMTQuOTM1NiA1Ljc3MTM2QzE2Ljk5NTkgOC4xNzM2MiAxNi45OTU5IDExLjgyOCAxNC45MzU2IDE0LjIzMDNDMTQuNzU1OCAxNC40Mzk5IDE0LjQzNzYgMTQuNDM4NyAxNC4yNDIzIDE0LjI0MzVMMTMuNTM1MiAxMy41MzY0QzEzLjMzOTkgMTMuMzQxMSAxMy4zNDI0IDEzLjAyNjEgMTMuNTE1IDEyLjgxMDVDMTQuODEzIDExLjE4ODUgMTQuODEzIDguODEzMTIgMTMuNTE1IDcuMTkxMTlaIiBmaWxsPSIjMTYxODIzIiBmaWxsLW9wYWNpdHk9IjAuNiIvPgo8cGF0aCBkPSJNMTYuNzE3MiAxNi43MTgzQzE2LjUyMTkgMTYuNTIzMSAxNi41MjMxIDE2LjIwNzQgMTYuNzA3MiAxNi4wMDE3QzE5LjcyNTcgMTIuNjMgMTkuNzI1NyA3LjM3MTY4IDE2LjcwNzIgNC4wMDAwMUMxNi41MjMxIDMuNzk0MjcgMTYuNTIxOSAzLjQ3ODU4IDE2LjcxNzIgMy4yODMzMkwxNy40MjQzIDIuNTc2MjFDMTcuNjE5NSAyLjM4MDk1IDE3LjkzNyAyLjM4MDIgMTguMTIzMyAyLjU4NDA4QzIxLjkwOTkgNi43MjkyNiAyMS45MDk5IDEzLjI3MjQgMTguMTIzMyAxNy40MTc2QzE3LjkzNyAxNy42MjE1IDE3LjYxOTUgMTcuNjIwNyAxNy40MjQzIDE3LjQyNTVMMTYuNzE3MiAxNi43MTgzWiIgZmlsbD0iIzE2MTgyMyIgZmlsbC1vcGFjaXR5PSIwLjYiLz4KPC9zdmc+Cg=="]')
582
+ if stealth == True:
583
+ time.sleep(1)
584
+ page.click('img[src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMSAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTAgNy41MDE2QzAgNi42NzMxNyAwLjY3MTU3MyA2LjAwMTYgMS41IDYuMDAxNkgzLjU3NzA5QzMuODY4MDUgNi4wMDE2IDQuMTQ0NTggNS44NzQ4OCA0LjMzNDU1IDUuNjU0NDlMOC43NDI1NSAwLjU0MDUyQzkuMzQ3OCAtMC4xNjE2NjggMTAuNSAwLjI2NjM3NCAxMC41IDEuMTkzNDFWMTguOTY3MkMxMC41IDE5Ljg3NDUgOS4zODg5NCAyMC4zMTI5IDguNzY5NDIgMTkuNjVMNC4zMzE3OSAxNC45MDIxQzQuMTQyNjkgMTQuNjk5OCAzLjg3ODE2IDE0LjU4NDkgMy42MDEyMiAxNC41ODQ5SDEuNUMwLjY3MTU3MyAxNC41ODQ5IDAgMTMuOTEzNCAwIDEzLjA4NDlWNy41MDE2Wk01Ljg0OTQ1IDYuOTYwMjdDNS4yNzk1NiA3LjYyMTQzIDQuNDQ5OTcgOC4wMDE2IDMuNTc3MDkgOC4wMDE2SDJWMTIuNTg0OUgzLjYwMTIyQzQuNDMyMDMgMTIuNTg0OSA1LjIyNTY0IDEyLjkyOTUgNS43OTI5NSAxMy41MzY0TDguNSAxNi40MzI4VjMuODg1MjJMNS44NDk0NSA2Ljk2MDI3WiIgZmlsbD0iIzE2MTgyMyIgZmlsbC1vcGFjaXR5PSIwLjYiLz4KPHBhdGggZD0iTTEzLjUxNSA3LjE5MTE5QzEzLjM0MjQgNi45NzU1OSAxMy4zMzk5IDYuNjYwNTYgMTMuNTM1MiA2LjQ2NTNMMTQuMjQyMyA1Ljc1ODE5QzE0LjQzNzYgNS41NjI5MyAxNC43NTU4IDUuNTYxNzUgMTQuOTM1NiA1Ljc3MTM2QzE2Ljk5NTkgOC4xNzM2MiAxNi45OTU5IDExLjgyOCAxNC45MzU2IDE0LjIzMDNDMTQuNzU1OCAxNC40Mzk5IDE0LjQzNzYgMTQuNDM4NyAxNC4yNDIzIDE0LjI0MzVMMTMuNTM1MiAxMy41MzY0QzEzLjMzOTkgMTMuMzQxMSAxMy4zNDI0IDEzLjAyNjEgMTMuNTE1IDEyLjgxMDVDMTQuODEzIDExLjE4ODUgMTQuODEzIDguODEzMTIgMTMuNTE1IDcuMTkxMTlaIiBmaWxsPSIjMTYxODIzIiBmaWxsLW9wYWNpdHk9IjAuNiIvPgo8cGF0aCBkPSJNMTYuNzE3MiAxNi43MTgzQzE2LjUyMTkgMTYuNTIzMSAxNi41MjMxIDE2LjIwNzQgMTYuNzA3MiAxNi4wMDE3QzE5LjcyNTcgMTIuNjMgMTkuNzI1NyA3LjM3MTY4IDE2LjcwNzIgNC4wMDAwMUMxNi41MjMxIDMuNzk0MjcgMTYuNTIxOSAzLjQ3ODU4IDE2LjcxNzIgMy4yODMzMkwxNy40MjQzIDIuNTc2MjFDMTcuNjE5NSAyLjM4MDk1IDE3LjkzNyAyLjM4MDIgMTguMTIzMyAyLjU4NDA4QzIxLjkwOTkgNi43MjkyNiAyMS45MDk5IDEzLjI3MjQgMTguMTIzMyAxNy40MTc2QzE3LjkzNyAxNy42MjE1IDE3LjYxOTUgMTcuNjIwNyAxNy40MjQzIDE3LjQyNTVMMTYuNzE3MiAxNi43MTgzWiIgZmlsbD0iIzE2MTgyMyIgZmlsbC1vcGFjaXR5PSIwLjYiLz4KPC9zdmc+Cg=="]')
585
+ time.sleep(0.5)
586
+ sliders = page.locator("input.scaleInput")
587
+
588
+ if sound_aud_vol == 'background':
589
+ slider1 = sliders.nth(0)
590
+ bounding_box1 = slider1.bounding_box()
591
+ if bounding_box1:
592
+ x1 = bounding_box1["x"] + (bounding_box1["width"] * 0.92)
593
+ y1 = bounding_box1["y"] + bounding_box1["height"] / 2
594
+ if stealth == True:
595
+ time.sleep(1)
596
+ page.mouse.click(x1, y1)
597
+
598
+ slider2 = sliders.nth(1)
599
+ bounding_box2 = slider2.bounding_box()
600
+ if bounding_box2:
601
+ x2 = bounding_box2["x"] + (bounding_box2["width"] * 0.097)
602
+ y2 = bounding_box2["y"] + bounding_box2["height"] / 2
603
+ if stealth == True:
604
+ time.sleep(1)
605
+ page.mouse.click(x2, y2)
606
+
607
+ if sound_aud_vol == 'main':
608
+ slider1 = sliders.nth(0)
609
+ bounding_box1 = slider1.bounding_box()
610
+ if bounding_box1:
611
+ x1 = bounding_box1["x"] + (bounding_box1["width"] * 0.092)
612
+ y1 = bounding_box1["y"] + bounding_box1["height"] / 2
613
+ if stealth == True:
614
+ time.sleep(1)
615
+ page.mouse.click(x1, y1)
616
+ slider2 = sliders.nth(1)
617
+ bounding_box2 = slider2.bounding_box()
618
+ if bounding_box2:
619
+ x2 = bounding_box2["x"] + (bounding_box2["width"] * 0.92)
620
+ y2 = bounding_box2["y"] + bounding_box2["height"] / 2
621
+ if stealth == True:
622
+ time.sleep(1)
623
+ page.mouse.click(x2, y2)
624
+ except:
625
+ sys.exit("ERROR ADJUSTING SOUND VOLUME: please try again.")
626
+
627
+ page.wait_for_selector("div.TUXButton-label:has-text('Save edit')")
628
+ if stealth == True:
629
+ time.sleep(1)
630
+ page.click("div.TUXButton-label:has-text('Save edit')")
631
+ if suppressprint == False:
632
+ print("Added sound")
633
+
634
+ if sound_fail == False:
635
+ page.wait_for_selector('div[data-contents="true"]')
636
+
637
+ if copyrightcheck == True:
638
+ if stealth == True:
639
+ time.sleep(1)
640
+ page.locator('div[data-e2e="copyright_container"] span[data-part="thumb"]').click()
641
+ while copyrightcheck == True:
642
+ time.sleep(0.2)
643
+ if page.locator("span" ,has_text="No issues detected.").is_visible():
644
+ if suppressprint == False:
645
+ print("Copyright check complete")
646
+ break
647
+ if page.locator("span", has_text="Copyright issues detected.").is_visible():
648
+ sys.exit("COPYRIGHT CHECK FAILED: VIDEO SAVED AS DRAFT, COPYRIGHT AUDIO DETECTED FROM TIKTOK")
649
+
650
+
651
+ try:
652
+ if schedule == None:
653
+ if stealth == True:
654
+ time.sleep(1)
655
+ page.click('button:has-text("Post")', timeout=10000)
656
+ uploaded = False
657
+ checks = 0
658
+ while uploaded == False:
659
+ if page.locator(':has-text("Leaving the page does not interrupt")').nth(0).is_visible():
660
+ time.sleep(0.1)
661
+ break
662
+ time.sleep(0.2)
663
+ checks += 1
664
+ if checks == 25:
665
+ break
666
+ else:
667
+ if stealth == True:
668
+ time.sleep(1)
669
+ page.click('button:has-text("Schedule")', timeout=10000)
670
+ uploaded = False
671
+ checks = 0
672
+ while uploaded == False:
673
+ if page.locator(':has-text("Leaving the page does not interrupt")').nth(0).is_visible():
674
+ time.sleep(0.2)
675
+ break
676
+ time.sleep(0.2)
677
+ checks += 1
678
+ if checks == 25:
679
+ break
680
+ if suppressprint == False:
681
+ print("Done uploading video, NOTE: it may take a minute or two to show on TikTok")
682
+ except:
683
+ time.sleep(2)
684
+ sys.exit("POSSIBLE ERROR UPLOADING: Cannot confirm if uploaded successfully, Please check account in a minute or two to confirm.")
685
+ time.sleep(1)
686
+
687
+ page.close()
688
+ else:
689
+ try:
690
+ if stealth == True:
691
+ time.sleep(1)
692
+ page.click('button:has-text("Save draft")', timeout=10000)
693
+ except:
694
+ sys.exit("SAVE AS DRAFT BUTTON NOT FOUND; Please try account that has ability to save as draft")
695
+
696
+ time.sleep(0.5)
697
+ page.close()
698
+
699
+ browser = p.chromium.launch(headless=headless)
700
+
701
+ context = browser.new_context()
702
+ context.add_cookies(cookies)
703
+ page = context.new_page()
704
+ url2 = 'https://www.tiktok.com/tiktokstudio/content?tab=draft'
705
+
706
+ while retries < 2:
707
+ try:
708
+ page.goto(url2, timeout=30000)
709
+ except:
710
+ retries +=1
711
+ time.sleep(5)
712
+ if retries == 2:
713
+ sys.exit("ERROR: TIK TOK PAGE FAILED TO LOAD, try again.")
714
+ else:
715
+ break
716
+
717
+ try:
718
+ 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']")
719
+ if stealth == True:
720
+ time.sleep(1)
721
+ 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']")
722
+ page.wait_for_selector('div[data-contents="true"]')
723
+ time.sleep(0.2)
724
+ except:
725
+ sys.exit("ERROR ADDING SOUND: Video saved as draft")
726
+
727
+ if sound_name != None:
728
+ if stealth == True:
729
+ time.sleep(1)
730
+ page.click("div.TUXButton-label:has-text('Edit video')")
731
+ page.wait_for_selector("input.search-bar-input")
732
+ page.fill(f"input.search-bar-input", f"{sound_name}")
733
+ time.sleep(0.2)
734
+ if stealth == True:
735
+ time.sleep(1)
736
+ page.click("div.TUXButton-label:has-text('Search')")
737
+ try:
738
+ page.wait_for_selector('div.music-card-container')
739
+ if stealth == True:
740
+ time.sleep(1)
741
+ page.click("div.music-card-container")
742
+ page.wait_for_selector("div.TUXButton-label:has-text('Use')")
743
+ if stealth == True:
744
+ time.sleep(1)
745
+ page.click("div.TUXButton-label:has-text('Use')")
746
+ except:
747
+ sys.exit(f"ERROR: SOUND '{sound_name}' NOT FOUND")
748
+ try:
749
+ page.wait_for_selector('img[src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMSAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTAgNy41MDE2QzAgNi42NzMxNyAwLjY3MTU3MyA2LjAwMTYgMS41IDYuMDAxNkgzLjU3NzA5QzMuODY4MDUgNi4wMDE2IDQuMTQ0NTggNS44NzQ4OCA0LjMzNDU1IDUuNjU0NDlMOC43NDI1NSAwLjU0MDUyQzkuMzQ3OCAtMC4xNjE2NjggMTAuNSAwLjI2NjM3NCAxMC41IDEuMTkzNDFWMTguOTY3MkMxMC41IDE5Ljg3NDUgOS4zODg5NCAyMC4zMTI5IDguNzY5NDIgMTkuNjVMNC4zMzE3OSAxNC45MDIxQzQuMTQyNjkgMTQuNjk5OCAzLjg3ODE2IDE0LjU4NDkgMy42MDEyMiAxNC41ODQ5SDEuNUMwLjY3MTU3MyAxNC41ODQ5IDAgMTMuOTEzNCAwIDEzLjA4NDlWNy41MDE2Wk01Ljg0OTQ1IDYuOTYwMjdDNS4yNzk1NiA3LjYyMTQzIDQuNDQ5OTcgOC4wMDE2IDMuNTc3MDkgOC4wMDE2SDJWMTIuNTg0OUgzLjYwMTIyQzQuNDMyMDMgMTIuNTg0OSA1LjIyNTY0IDEyLjkyOTUgNS43OTI5NSAxMy41MzY0TDguNSAxNi40MzI4VjMuODg1MjJMNS44NDk0NSA2Ljk2MDI3WiIgZmlsbD0iIzE2MTgyMyIgZmlsbC1vcGFjaXR5PSIwLjYiLz4KPHBhdGggZD0iTTEzLjUxNSA3LjE5MTE5QzEzLjM0MjQgNi45NzU1OSAxMy4zMzk5IDYuNjYwNTYgMTMuNTM1MiA2LjQ2NTNMMTQuMjQyMyA1Ljc1ODE5QzE0LjQzNzYgNS41NjI5MyAxNC43NTU4IDUuNTYxNzUgMTQuOTM1NiA1Ljc3MTM2QzE2Ljk5NTkgOC4xNzM2MiAxNi45OTU5IDExLjgyOCAxNC45MzU2IDE0LjIzMDNDMTQuNzU1OCAxNC40Mzk5IDE0LjQzNzYgMTQuNDM4NyAxNC4yNDIzIDE0LjI0MzVMMTMuNTM1MiAxMy41MzY0QzEzLjMzOTkgMTMuMzQxMSAxMy4zNDI0IDEzLjAyNjEgMTMuNTE1IDEyLjgxMDVDMTQuODEzIDExLjE4ODUgMTQuODEzIDguODEzMTIgMTMuNTE1IDcuMTkxMTlaIiBmaWxsPSIjMTYxODIzIiBmaWxsLW9wYWNpdHk9IjAuNiIvPgo8cGF0aCBkPSJNMTYuNzE3MiAxNi43MTgzQzE2LjUyMTkgMTYuNTIzMSAxNi41MjMxIDE2LjIwNzQgMTYuNzA3MiAxNi4wMDE3QzE5LjcyNTcgMTIuNjMgMTkuNzI1NyA3LjM3MTY4IDE2LjcwNzIgNC4wMDAwMUMxNi41MjMxIDMuNzk0MjcgMTYuNTIxOSAzLjQ3ODU4IDE2LjcxNzIgMy4yODMzMkwxNy40MjQzIDIuNTc2MjFDMTcuNjE5NSAyLjM4MDk1IDE3LjkzNyAyLjM4MDIgMTguMTIzMyAyLjU4NDA4QzIxLjkwOTkgNi43MjkyNiAyMS45MDk5IDEzLjI3MjQgMTguMTIzMyAxNy40MTc2QzE3LjkzNyAxNy42MjE1IDE3LjYxOTUgMTcuNjIwNyAxNy40MjQzIDE3LjQyNTVMMTYuNzE3MiAxNi43MTgzWiIgZmlsbD0iIzE2MTgyMyIgZmlsbC1vcGFjaXR5PSIwLjYiLz4KPC9zdmc+Cg=="]')
750
+ if stealth == True:
751
+ time.sleep(1)
752
+ page.click('img[src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjEiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMSAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTAgNy41MDE2QzAgNi42NzMxNyAwLjY3MTU3MyA2LjAwMTYgMS41IDYuMDAxNkgzLjU3NzA5QzMuODY4MDUgNi4wMDE2IDQuMTQ0NTggNS44NzQ4OCA0LjMzNDU1IDUuNjU0NDlMOC43NDI1NSAwLjU0MDUyQzkuMzQ3OCAtMC4xNjE2NjggMTAuNSAwLjI2NjM3NCAxMC41IDEuMTkzNDFWMTguOTY3MkMxMC41IDE5Ljg3NDUgOS4zODg5NCAyMC4zMTI5IDguNzY5NDIgMTkuNjVMNC4zMzE3OSAxNC45MDIxQzQuMTQyNjkgMTQuNjk5OCAzLjg3ODE2IDE0LjU4NDkgMy42MDEyMiAxNC41ODQ5SDEuNUMwLjY3MTU3MyAxNC41ODQ5IDAgMTMuOTEzNCAwIDEzLjA4NDlWNy41MDE2Wk01Ljg0OTQ1IDYuOTYwMjdDNS4yNzk1NiA3LjYyMTQzIDQuNDQ5OTcgOC4wMDE2IDMuNTc3MDkgOC4wMDE2SDJWMTIuNTg0OUgzLjYwMTIyQzQuNDMyMDMgMTIuNTg0OSA1LjIyNTY0IDEyLjkyOTUgNS43OTI5NSAxMy41MzY0TDguNSAxNi40MzI4VjMuODg1MjJMNS44NDk0NSA2Ljk2MDI3WiIgZmlsbD0iIzE2MTgyMyIgZmlsbC1vcGFjaXR5PSIwLjYiLz4KPHBhdGggZD0iTTEzLjUxNSA3LjE5MTE5QzEzLjM0MjQgNi45NzU1OSAxMy4zMzk5IDYuNjYwNTYgMTMuNTM1MiA2LjQ2NTNMMTQuMjQyMyA1Ljc1ODE5QzE0LjQzNzYgNS41NjI5MyAxNC43NTU4IDUuNTYxNzUgMTQuOTM1NiA1Ljc3MTM2QzE2Ljk5NTkgOC4xNzM2MiAxNi45OTU5IDExLjgyOCAxNC45MzU2IDE0LjIzMDNDMTQuNzU1OCAxNC40Mzk5IDE0LjQzNzYgMTQuNDM4NyAxNC4yNDIzIDE0LjI0MzVMMTMuNTM1MiAxMy41MzY0QzEzLjMzOTkgMTMuMzQxMSAxMy4zNDI0IDEzLjAyNjEgMTMuNTE1IDEyLjgxMDVDMTQuODEzIDExLjE4ODUgMTQuODEzIDguODEzMTIgMTMuNTE1IDcuMTkxMTlaIiBmaWxsPSIjMTYxODIzIiBmaWxsLW9wYWNpdHk9IjAuNiIvPgo8cGF0aCBkPSJNMTYuNzE3MiAxNi43MTgzQzE2LjUyMTkgMTYuNTIzMSAxNi41MjMxIDE2LjIwNzQgMTYuNzA3MiAxNi4wMDE3QzE5LjcyNTcgMTIuNjMgMTkuNzI1NyA3LjM3MTY4IDE2LjcwNzIgNC4wMDAwMUMxNi41MjMxIDMuNzk0MjcgMTYuNTIxOSAzLjQ3ODU4IDE2LjcxNzIgMy4yODMzMkwxNy40MjQzIDIuNTc2MjFDMTcuNjE5NSAyLjM4MDk1IDE3LjkzNyAyLjM4MDIgMTguMTIzMyAyLjU4NDA4QzIxLjkwOTkgNi43MjkyNiAyMS45MDk5IDEzLjI3MjQgMTguMTIzMyAxNy40MTc2QzE3LjkzNyAxNy42MjE1IDE3LjYxOTUgMTcuNjIwNyAxNy40MjQzIDE3LjQyNTVMMTYuNzE3MiAxNi43MTgzWiIgZmlsbD0iIzE2MTgyMyIgZmlsbC1vcGFjaXR5PSIwLjYiLz4KPC9zdmc+Cg=="]')
753
+ time.sleep(0.5)
754
+ sliders = page.locator("input.scaleInput")
755
+
756
+ if sound_aud_vol == 'background':
757
+ slider1 = sliders.nth(0)
758
+ bounding_box1 = slider1.bounding_box()
759
+ if bounding_box1:
760
+ x1 = bounding_box1["x"] + (bounding_box1["width"] * 0.92)
761
+ y1 = bounding_box1["y"] + bounding_box1["height"] / 2
762
+ if stealth == True:
763
+ time.sleep(1)
764
+ page.mouse.click(x1, y1)
765
+
766
+ slider2 = sliders.nth(1)
767
+ bounding_box2 = slider2.bounding_box()
768
+ if bounding_box2:
769
+ x2 = bounding_box2["x"] + (bounding_box2["width"] * 0.097)
770
+ y2 = bounding_box2["y"] + bounding_box2["height"] / 2
771
+ if stealth == True:
772
+ time.sleep(1)
773
+ page.mouse.click(x2, y2)
774
+
775
+ if sound_aud_vol == 'main':
776
+ slider1 = sliders.nth(0)
777
+ bounding_box1 = slider1.bounding_box()
778
+ if bounding_box1:
779
+ x1 = bounding_box1["x"] + (bounding_box1["width"] * 0.092)
780
+ y1 = bounding_box1["y"] + bounding_box1["height"] / 2
781
+ if stealth == True:
782
+ time.sleep(1)
783
+ page.mouse.click(x1, y1)
784
+ slider2 = sliders.nth(1)
785
+ bounding_box2 = slider2.bounding_box()
786
+ if bounding_box2:
787
+ x2 = bounding_box2["x"] + (bounding_box2["width"] * 0.92)
788
+ y2 = bounding_box2["y"] + bounding_box2["height"] / 2
789
+ if stealth == True:
790
+ time.sleep(1)
791
+ page.mouse.click(x2, y2)
792
+ except:
793
+ sys.exit("ERROR ADJUSTING SOUND VOLUME: please try again.")
794
+
795
+ page.wait_for_selector("div.TUXButton-label:has-text('Save edit')")
796
+ if stealth == True:
797
+ time.sleep(1)
798
+ page.click("div.TUXButton-label:has-text('Save edit')")
799
+ if suppressprint == False:
800
+ print("Added sound")
801
+
802
+
803
+ page.wait_for_selector('div[data-contents="true"]')
804
+
805
+ if copyrightcheck == True:
806
+ if stealth == True:
807
+ time.sleep(1)
808
+ page.locator('div[data-e2e="copyright_container"] span[data-part="thumb"]').click()
809
+ while copyrightcheck == True:
810
+ time.sleep(0.2)
811
+ if page.locator("span", has_text="No issues detected.").is_visible():
812
+ if suppressprint == False:
813
+ print("Copyright check complete")
814
+ break
815
+ if page.locator('span', has_text="Copyright issues detected.").is_visible():
816
+ sys.exit("COPYRIGHT CHECK FAILED: VIDEO SAVED AS DRAFT, COPYRIGHT AUDIO DETECTED FROM TIKTOK")
817
+
818
+
819
+ try:
820
+ if schedule == None:
821
+ if stealth == True:
822
+ time.sleep(1)
823
+ page.click('button:has-text("Post")', timeout=10000)
824
+ uploaded = False
825
+ checks = 0
826
+ while uploaded == False:
827
+ if page.locator(':has-text("Leaving the page does not interrupt")').nth(0).is_visible():
828
+ time.sleep(0.2)
829
+ break
830
+ time.sleep(0.2)
831
+ checks += 1
832
+ if checks == 25:
833
+ break
834
+ else:
835
+ if stealth == True:
836
+ time.sleep(1)
837
+ page.click('button:has-text("Schedule")', timeout=10000)
838
+ uploaded = False
839
+ checks = 0
840
+ while uploaded == False:
841
+ if page.locator(':has-text("Leaving the page does not interrupt")').nth(0).is_visible():
842
+ time.sleep(0.1)
843
+ break
844
+ time.sleep(0.2)
845
+ checks += 1
846
+ if checks == 25:
847
+ break
848
+ if suppressprint == False:
849
+ print("Done uploading video, NOTE: it may take a minute or two to show on TikTok")
850
+ except:
851
+ time.sleep(2)
852
+ sys.exit("POSSIBLE ERROR UPLOADING: Cannot confirm if uploaded successfully, Please check account in a minute or two to confirm.")
853
+ time.sleep(1)
854
+
855
+ page.close()
856
+
857
+ return "Completed"
@@ -0,0 +1,152 @@
1
+ Metadata-Version: 2.4
2
+ Name: tiktokautouploader
3
+ Version: 4.0
4
+ Summary: Upload or schedule videos to TikTok with viral TikTok sounds and hashtags.
5
+ Project-URL: Homepage, https://github.com/haziq-exe/TikTokAutoUploader
6
+ Author-email: HAZIQ <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: inference>=0.18.1
17
+ Requires-Dist: pillow>=8.0.0
18
+ Requires-Dist: playwright>=1.0.0
19
+ Requires-Dist: requests>=2.0.0
20
+ Description-Content-Type: text/markdown
21
+
22
+ <div align="center">
23
+ <h1>tiktokautouploader</h1>
24
+ </div>
25
+
26
+
27
+ ### AUTOMATE TIKTOK UPLOADS 🤖. USE TRENDING SOUNDS 🔊, ADD WORKING HASHTAGS 💯, SCHEDULE UPLOADS 🗓️, AUTOSOLVES CAPTCHAS 🧠, AND MORE 🎁
28
+
29
+ [![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)
30
+
31
+ <p align="center">
32
+ <img src="READMEimage/READMEGIF.gif" alt="" width="900"/>
33
+ </p>
34
+
35
+ ## 🚀 Features
36
+
37
+ - **🔐 Bypass/Auto Solve Captchas:** No need to worry about any captchas interrupting the process, they'll be solved!
38
+ - **🎵 Use TikTok Sounds:** Go viral by seamlessly adding popular TikTok sounds to your videos.
39
+ - **🗓 Schedule Uploads:** Upload videos at specific times or upto 10 days in advance with our scheduling feature.
40
+ - **🔍 Copyright Check:** Ensure your video is safe from copyright claims before uploading.
41
+ - **🏷 Add Working Hashtags:** Increase your reach by adding effective hashtags that actually work.
42
+ - **⏰ Cutdown on upload time:** Upload to TikTok with way less hassle and much more speed using our library.
43
+ - **📝 Upload to different accounts:** Stay organized and on top of multiple different accounts with our multi-account functionality.
44
+
45
+ ⭐️ If you like this project please feel free to star it, Thank you.
46
+
47
+ ## 📦 Installation
48
+
49
+ 1. **Python Installation:** Install the package using `pip`:
50
+
51
+ ```bash
52
+ pip install tiktokautouploader
53
+ ```
54
+
55
+ **NOTE:** IF YOU HAVE INSTALLED BEFORE, PLEASE MAKE SURE YOU UPGRADE TO THE LATEST VERSION.
56
+
57
+ ---
58
+
59
+ ## ⚙️ Pre-requisites
60
+
61
+ 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/).
62
+
63
+ - **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.
64
+
65
+
66
+ 2. **Browser Binaries:** If you don't have them already, you'll need to install the browser binaries for `playwright`. This library uses chromium and firefox.
67
+
68
+ to do so, just run the following command AFTER installing the package:
69
+
70
+ ```bash
71
+ python -m playwright install
72
+ ```
73
+
74
+ **NOTE:** If you want to add sounds to your TikTok, you MUST have the ability to save drafts. If you don't want to add sounds then you don't need this feature.
75
+
76
+
77
+ ## 📝 Quick-Start
78
+
79
+ **NOTE:** It is highly recommended you read DOCUMENTATION.md before using the library.
80
+ The first time you run the code for an account, you will be prompted to log-in, this will only occur the first time the function is used for the account. Check documentation for more info.
81
+
82
+ ## Upload with hashtags
83
+
84
+ ```python
85
+ from tiktokautouploader import upload_tiktok
86
+
87
+ video_path = 'path/to/your/video.mp4'
88
+ description = 'Check out my latest TikTok video!'
89
+ accountname = 'mytiktokaccount'
90
+ hashtags = ['#fun', '#viral']
91
+
92
+ upload_tiktok(video=video_path, description=description, accountname=accountname, hashtags=hashtags)
93
+
94
+ ```
95
+
96
+ ### Upload with TikTok Sound
97
+
98
+ ```python
99
+ upload_tiktok(video=video_path, description=description, accountname=accountname, sound_name='trending_sound', sound_aud_vol='main')
100
+ ```
101
+
102
+ PLEASE READ DOCUMENTATION FOR MORE INFO.
103
+
104
+ ### Schedule an Upload
105
+
106
+ ```python
107
+ upload_tiktok(video=video_path, description=description, accountname=accountname, schedule='03:10', day=11)
108
+ ```
109
+
110
+ PLEASE READ DOCUMENTATION FOR MORE INFO
111
+
112
+ ### Perform Copyright Check Before Uploading
113
+
114
+ ```python
115
+ upload_tiktok(video=video_path, description=description, accountname=accountname, hashtags=hashtags, copyrightcheck=True)
116
+ ```
117
+
118
+ ## 🎯 Why Choose `autotiktokuploader`?
119
+
120
+ - **No more captchas:** Fully automated uploads without interruptions, If captchas do show up, no worries, they will be solved. (read documentation for more info)
121
+ - **Maximize your reach:** Add popular sounds and effective hashtags that work to boost visibility and go viral!
122
+ - **Stay compliant:** Built-in copyright checks to avoid unforeseen takedowns.
123
+ - **Convenient scheduling:** Post at the right time, even when you're away!
124
+ - **Much faster than manually uploading:** Drastically reduce the time you need to upload videos, just click one button and be done much quicker!
125
+ - **Upload to different account:** Stay on top of all your TikTok accounts with our multi-account functionality!
126
+
127
+ ## 🛠 Dependencies
128
+
129
+ This library requires the following dependencies:
130
+
131
+ - `playwright`
132
+ - `requests`
133
+ - `Pillow`
134
+ - `scikit-learn`
135
+ - `inference`
136
+
137
+ These will be automatically installed when you install the package.
138
+
139
+ ## 👤 Author
140
+
141
+ Created by **Haziq**. Feel free to reach out at [haziqmk123@gmail.com](mailto:haziqmk123@gmail.com)
142
+
143
+ ## 📄 License
144
+
145
+ This project is licensed under the MIT License. See the [LICENSE](LICENSE.md) file for details.
146
+
147
+ ## 📮 Related Projects
148
+
149
+ If you liked this project please check out my use case showcase project that generates TikToks and uploads them using 'tiktokautouploader'
150
+
151
+ [TikTokGenerator](https://github.com/haziq-exe/TikTokGenerator)
152
+
@@ -0,0 +1,8 @@
1
+ tiktokautouploader/__init__.py,sha256=u7OWCK_u68ST8dfrkSF4Yw44CJOzV9NXI6ASRuoDfmE,64
2
+ tiktokautouploader/function.py,sha256=fKS8bm6yzvP9QXO_Y8gAcZufOIhA_G2WsSwM_xH0KmM,46918
3
+ tiktokautouploader/Js_assets/login.js,sha256=SLhtPYo8ZfTRUnbR7Xqp084lSuAOqIWUxi75FlFH3vs,966
4
+ tiktokautouploader/Js_assets/package.json,sha256=aNbwSOpEpDUlplSuXugHVeN11riVwV4ofVkRPwHzbLs,273
5
+ tiktokautouploader-4.0.dist-info/METADATA,sha256=7C6hDX0w_QKgybBMx756B3BqzuVQL1rCeSUP7dsWe9I,6125
6
+ tiktokautouploader-4.0.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
7
+ tiktokautouploader-4.0.dist-info/licenses/LICENSE.md,sha256=hYds_VJIpnS5gC73WhuWk2IY_e9BWjuEJthQCb9ThyU,1073
8
+ tiktokautouploader-4.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py2-none-any
5
+ Tag: py3-none-any
@@ -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.