├── uploaders ├── __init__.py ├── tiktok.py ├── facebook.py ├── instagram.py ├── youtube.py └── twitter.py ├── scraping_manager ├── __init__.py └── automate.py ├── spreadsheet_manager ├── __init__.py ├── google_ss.py └── xlsx.py ├── logo.png ├── requirements.txt ├── globals.py ├── .gitignore ├── LICENSE ├── download.py ├── config.py ├── __main__.py └── README.md /uploaders/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scraping_manager/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /spreadsheet_manager/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darideveloper/video-post/HEAD/logo.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | selenium==3.141.0 2 | webdriver-manager==3.4.1 3 | requests==2.25.1 4 | moviepy==1.0.3 5 | gspread==4.0.1 6 | oauth2client==4.1.3 7 | -------------------------------------------------------------------------------- /globals.py: -------------------------------------------------------------------------------- 1 | global scraper 2 | global current_folder 3 | global videos_path 4 | global download_folder 5 | 6 | scraper = None 7 | current_folder = "" 8 | videos_path = "" 9 | download_folder = "" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *__pycache__ 3 | *.json 4 | *temp.* 5 | temp.* 6 | *.log* 7 | *.log* 8 | *.zip 9 | *.png 10 | chrome_data 11 | downloads 12 | done 13 | videos.txt 14 | venv 15 | videos.xlsx 16 | *.mp4 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Dari Developer 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. -------------------------------------------------------------------------------- /uploaders/tiktok.py: -------------------------------------------------------------------------------- 1 | import time 2 | import globals 3 | 4 | def upload (file_path:str, title:str, description:str, tags:list): 5 | """ Upload video to tiktok """ 6 | 7 | print ("\tUploading video to Tiktok...") 8 | 9 | # Open page 10 | globals.scraper.set_page ("https://www.tiktok.com/upload?lang=en") 11 | time.sleep (5) 12 | 13 | # Add id to frame 14 | iframe_selector = "iframe" 15 | iframe = globals.scraper.get_elem (iframe_selector) 16 | globals.scraper.driver.execute_script("arguments[0].setAttribute('id', 'iframe');", iframe) 17 | time.sleep (2) 18 | globals.scraper.refresh_selenium () 19 | 20 | # Swicth to internal frame 21 | globals.scraper.switch_to_frame ("iframe") 22 | time.sleep (2) 23 | 24 | # Upload file 25 | selector_input = 'input[accept="video/*"]' 26 | globals.scraper.send_data (selector_input, file_path) 27 | time.sleep (10) 28 | 29 | # Video title and description 30 | selector_details = 'div[aria-autocomplete="list"][role="combobox"]' 31 | tag_text = "" 32 | for tag in tags: 33 | tag_text += f" #{tag}" 34 | text_formated = f" - {description} - {tag_text}" 35 | globals.scraper.send_data (selector_details, text_formated) 36 | time.sleep (1) 37 | 38 | # Post video 39 | selector_post = 'button.tiktok-btn-pc.tiktok-btn-pc-primary' 40 | globals.scraper.click_js (selector_post) 41 | time.sleep (15) -------------------------------------------------------------------------------- /uploaders/facebook.py: -------------------------------------------------------------------------------- 1 | import time 2 | import globals 3 | 4 | def upload (facebook_page, file_path:str, title:str, description:str, tags:list): 5 | """ Upload video to facebook page """ 6 | 7 | print ("\tUploading video to Facebook Page...") 8 | 9 | # Open page 10 | globals.scraper.set_page (facebook_page) 11 | time.sleep (5) 12 | 13 | # Start new post 14 | selector_new_post = 'div[aria-label="Create post"]' 15 | globals.scraper.click (selector_new_post) 16 | globals.scraper.refresh_selenium () 17 | time.sleep (2) 18 | 19 | 20 | # Select to upload photo or video 21 | selector_photo_video = '[aria-label="Photo/Video"][role="button"]' 22 | globals.scraper.click (selector_photo_video) 23 | time.sleep (2) 24 | 25 | # Upload file 26 | selector_input = 'input[accept="image/*,image/heif,image/heic,video/*,video/mp4,video/x-m4v,video/x-matroska,.mkv"]' 27 | globals.scraper.send_data (selector_input, file_path) 28 | time.sleep (10) 29 | globals.scraper.refresh_selenium () 30 | 31 | # Video title and description 32 | selector_details = '.k4urcfbm.l9j0dhe7.datstx6m.rq0escxv div[role="textbox"][contenteditable="true"]' 33 | tag_text = "" 34 | for tag in tags: 35 | tag_text += f"\n#{tag}" 36 | text_formated = f"{title}\n\n{description}\n{tag_text}" 37 | globals.scraper.click_js (selector_details) 38 | globals.scraper.send_data (selector_details, text_formated) 39 | time.sleep (1) 40 | globals.scraper.refresh_selenium () 41 | 42 | # Post video 43 | selector_post = 'input[type="submit"]' 44 | globals.scraper.click_js (selector_post) 45 | time.sleep (15) -------------------------------------------------------------------------------- /download.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import globals 4 | import requests 5 | 6 | def tiktok (tiktok_url:str, title:str): 7 | """Download video from tictok and save it in downloads folder 8 | 9 | Args: 10 | globals.scraper (object): web scraping instance 11 | tiktok_url (str): tiktok link 12 | """ 13 | 14 | print ("\tDownloading video...") 15 | 16 | # Open browser and go to snaptik 17 | time.sleep (5) 18 | globals.scraper.switch_to_tab (0) 19 | globals.scraper.set_page ("https://snaptik.app/en") 20 | 21 | # Paste link and start download 22 | selector_input = "#url" 23 | selector_submit = "#submiturl" 24 | globals.scraper.send_data(selector_input, tiktok_url) 25 | globals.scraper.click (selector_submit) 26 | 27 | # Wait for video load 28 | download_selector = 'a.abutton.is-success:nth-child(1)' 29 | 30 | try: 31 | globals.scraper.wait_load(download_selector, time_out=60) 32 | except: 33 | print ("Error to download, video omitted") 34 | 35 | # Close browser and end function 36 | globals.scraper.kill() 37 | return None 38 | 39 | # get file link and extension 40 | downlod_link = globals.scraper.get_attrib (download_selector, "href") 41 | separator = downlod_link.rfind (".") 42 | extension = downlod_link[separator+1:] 43 | 44 | # Download file 45 | file_path = os.path.join (os.path.dirname (__file__), "downloads", f"{title}.{extension}") 46 | mp4 (downlod_link, file_path) 47 | return file_path 48 | 49 | def mp4 (url:str, file_path:str): 50 | """ Download mp4 video from link """ 51 | 52 | res = requests.get (url) 53 | res.raise_for_status() 54 | with open (file_path, "wb") as file: 55 | for chunk in res.iter_content (chunk_size=8000): 56 | file.write (chunk) 57 | time.sleep (5) 58 | -------------------------------------------------------------------------------- /uploaders/instagram.py: -------------------------------------------------------------------------------- 1 | import time 2 | import globals 3 | from selenium.webdriver.common.keys import Keys 4 | 5 | def upload (file_path:str, title:str, description:str, tags:list): 6 | """ Upload video to instagram reels """ 7 | 8 | print ("\tUploading video to Instagram Reels...") 9 | 10 | # Open page 11 | instagram_url = "https://www.instagram.com/" 12 | globals.scraper.set_page (instagram_url) 13 | globals.scraper.set_page (instagram_url) 14 | globals.scraper.send_data ("body", Keys.CONTROL + Keys.SHIFT + "r") 15 | time.sleep (5) 16 | 17 | # Open reels 18 | selector_new = "nav.NXc7H.jLuN9 .J5g42 > .XrOey:nth-child(3)" 19 | globals.scraper.click (selector_new) 20 | globals.scraper.refresh_selenium () 21 | selector_reel = '.CreationPopup.CreationPopup_show > [data-id="reel"]' 22 | globals.scraper.click (selector_reel) 23 | globals.scraper.refresh_selenium () 24 | 25 | # Upload file 26 | selector_input = 'input[accept="video/mp4,video/quicktime"]' 27 | globals.scraper.send_data (selector_input, file_path) 28 | time.sleep (10) 29 | 30 | # Go to video details 31 | selector_continue = ".qF0y9.Igw0E.IwRSH.eGOV_._4EzTm.XfCBB.g6RW6" 32 | globals.scraper.click (selector_continue) 33 | time.sleep (2) 34 | globals.scraper.refresh_selenium () 35 | globals.scraper.click (selector_continue) 36 | time.sleep (2) 37 | globals.scraper.refresh_selenium () 38 | 39 | # Video title and description 40 | selector_details = 'textarea[aria-label="Write a caption..."]' 41 | tag_text = "" 42 | for tag in tags: 43 | tag_text += f"\n#{tag}" 44 | text_formated = f"{title}\n\n{description}\n{tag_text}" 45 | time.sleep (1) 46 | globals.scraper.send_data (selector_details, text_formated) 47 | 48 | # Share video 49 | selector_share = ".qF0y9.Igw0E.IwRSH.eGOV_._4EzTm.XfCBB.g6RW6" 50 | globals.scraper.click (selector_share) 51 | time.sleep (15) 52 | -------------------------------------------------------------------------------- /spreadsheet_manager/google_ss.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # Conect to google spreadsheets 3 | import os 4 | import gspread 5 | import time 6 | import sys 7 | from oauth2client.service_account import ServiceAccountCredentials 8 | 9 | class SS_manager (): 10 | """ Class to conect to google shets and upload data""" 11 | 12 | def __init__ (self, google_sheet_link, creds_path, sheet_name=None): 13 | """ Construtor of the class""" 14 | 15 | # Read credentials 16 | if not os.path.isfile (creds_path): 17 | raise FileNotFoundError ("The credential file path is not correct") 18 | 19 | scope = ['https://spreadsheets.google.com/feeds', 'https://www.googleapis.com/auth/drive'] 20 | creds = ServiceAccountCredentials.from_json_keyfile_name(creds_path, scope) 21 | client = gspread.authorize(creds) 22 | 23 | # Conect to google sheet 24 | sheet = client.open_by_url(google_sheet_link) 25 | 26 | # Set the sheet 1 as worksheet 27 | if sheet_name: 28 | self.worksheet = sheet.worksheet(sheet_name) 29 | else: 30 | self.worksheet = sheet.sheet1 31 | 32 | def write_cell (self, value, row=1, column=1): 33 | """ Write data in specific cell 34 | """ 35 | self.worksheet.update_cell(row, column, value) 36 | 37 | def write_data (self, data, row=1, column=1): 38 | """ Write list of data in the worksheet""" 39 | 40 | # check if data exist 41 | if not data: 42 | print ("THERE IS NO NEW INFORMATION TO WRITE IN THE FILE.") 43 | else: 44 | print ("Writing information on spreadsheet...") 45 | 46 | # Loop for each row of data 47 | for row_data in data: 48 | 49 | # Set the position of the next row. Omit the header 50 | row_index = data.index(row_data) + row 51 | 52 | for cell in row_data: 53 | column_index = row_data.index (cell) + column 54 | 55 | # Write data in gss 56 | print (cell, row_index, column_index) 57 | self.write_cell (cell, row_index, column_index) 58 | 59 | 60 | def get_data (self): 61 | """ Read all records of the sheet""" 62 | 63 | records = self.worksheet.get_all_records() 64 | return records -------------------------------------------------------------------------------- /uploaders/youtube.py: -------------------------------------------------------------------------------- 1 | import time 2 | import globals 3 | 4 | def upload (file_path:str, title:str, description:str, tags:list): 5 | """ Upload video to youtube shorts """ 6 | 7 | print ("\tUploading video to Youtube Shorts...") 8 | 9 | # Open page 10 | youtube_url = "https://studio.youtube.com/" 11 | globals.scraper.set_page (youtube_url) 12 | 13 | # Ignore browser warning 14 | selector_go_shorts = "body > div > div.buttons > a.button.text-button.black-secondary:only-child" 15 | try: 16 | globals.scraper.click (selector_go_shorts) 17 | except: 18 | pass 19 | globals.scraper.refresh_selenium() 20 | 21 | # Dimiss button 22 | selector_continue = "#dismiss-button" 23 | try: 24 | globals.scraper.click (selector_continue) 25 | except: 26 | pass 27 | globals.scraper.refresh_selenium() 28 | 29 | # Open upload 30 | selector_upload = "#upload-button" 31 | selector_upload_icon = "#upload-icon" 32 | try: 33 | globals.scraper.click_js (selector_upload) 34 | except: 35 | globals.scraper.click_js (selector_upload_icon) 36 | globals.scraper.refresh_selenium() 37 | 38 | # Upload file 39 | selector_input = 'input[type="file"]' 40 | globals.scraper.send_data (selector_input, file_path) 41 | globals.scraper.refresh_selenium() 42 | time.sleep (10) 43 | 44 | # Video title 45 | # selector_description = ".input-container.title #textbox" 46 | # globals.scraper.send_data (selector_description, title) 47 | 48 | # Video description 49 | selector_description = ".input-container.description #textbox" 50 | globals.scraper.send_data (selector_description, description) 51 | 52 | # No child content 53 | selector_no_child = "tp-yt-paper-radio-button:nth-child(2)" 54 | globals.scraper.click (selector_no_child) 55 | 56 | # Open more details 57 | selector_more = "#toggle-button" 58 | globals.scraper.click (selector_more) 59 | time.sleep (3) 60 | globals.scraper.refresh_selenium() 61 | 62 | # Tags 63 | selector_tags = "#tags-container #text-input" 64 | for tag in tags: 65 | globals.scraper.send_data (selector_tags, f"{tag}\n") 66 | 67 | # Next pages 68 | selector_next = "#next-button" 69 | globals.scraper.click (selector_next) 70 | globals.scraper.refresh_selenium() 71 | globals.scraper.click (selector_next) 72 | globals.scraper.refresh_selenium() 73 | globals.scraper.click (selector_next) 74 | globals.scraper.refresh_selenium() 75 | 76 | # Public type 77 | selector_public = 'tp-yt-paper-radio-button[name="PUBLIC"]' 78 | globals.scraper.click (selector_public) 79 | 80 | # Publish video 81 | selector_publish = "#done-button" 82 | globals.scraper.click (selector_publish) 83 | time.sleep (15) 84 | -------------------------------------------------------------------------------- /uploaders/twitter.py: -------------------------------------------------------------------------------- 1 | import time 2 | import download 3 | import globals 4 | from selenium.webdriver.common.keys import Keys 5 | 6 | def convert (file_path:str): 7 | """ Upload video to twitter """ 8 | 9 | print ("\tConverting video for twitter...") 10 | 11 | # Open converter page 12 | converter_url = "https://servicios-web.online-convert.com/es/convertir-para-twitter" 13 | globals.scraper.set_page (converter_url) 14 | 15 | # Upload video 16 | selector_input = "#fileUploadInput" 17 | globals.scraper.send_data (selector_input, file_path) 18 | 19 | # Start conversion 20 | selector_start = "button.btn.btn-lg.submit-btn.mb-0" 21 | last_url = globals.scraper.driver.current_url 22 | while True: 23 | globals.scraper.click (selector_start) 24 | time.sleep (2) 25 | current_url = globals.scraper.driver.current_url 26 | if current_url != last_url: 27 | break 28 | 29 | # Get download link 30 | while True: 31 | time.sleep (2) 32 | selector_download = 'a.btn.btn-large.btn-download[title="Descargar tu archivo"]' 33 | downlod_link = globals.scraper.get_attrib (selector_download, "href") 34 | if downlod_link and downlod_link != 'https://www.online-convert.com/es': 35 | time.sleep (10) 36 | downlod_link = globals.scraper.get_attrib (selector_download, "href") 37 | break 38 | else: 39 | continue 40 | 41 | # Download file 42 | file_converted = file_path.replace(".mp4", " for twitter.mp4") 43 | download.mp4 (downlod_link, file_converted) 44 | return file_converted 45 | 46 | 47 | def upload (file_path:str, title:str, description:str, tags:list): 48 | 49 | print ("\tUploading video to Twitter...") 50 | 51 | # Open page 52 | twitter_url = "https://twitter.com/home" 53 | globals.scraper.set_page (twitter_url) 54 | time.sleep (5) 55 | globals.scraper.refresh_selenium () 56 | 57 | # Upload file 58 | selector_input = 'input[accept="image/jpeg,image/png,image/webp,image/gif,video/mp4,video/quicktime,video/webm"]' 59 | globals.scraper.send_data (selector_input, file_path) 60 | time.sleep (10) 61 | 62 | # Video title and description 63 | selector_details = 'label[data-testid="tweetTextarea_0_label"] div[role="textbox"]' 64 | tag_text = "" 65 | for tag in tags: 66 | tag_text += f"\n#{tag}" 67 | text_formated = f"{title}\n\n{description}\n{tag_text}" 68 | globals.scraper.send_data (selector_details, text_formated) 69 | 70 | # Post tweet 71 | selector_share = 'div[data-testid="tweetButtonInline"]' 72 | globals.scraper.click_js (selector_share) 73 | 74 | # Wait to update video 75 | selector_placeholder = ".public-DraftEditorPlaceholder-inner" 76 | while True: 77 | time.sleep (2) 78 | place_holder = globals.scraper.get_text (selector_placeholder) 79 | if place_holder: 80 | break 81 | else: 82 | continue 83 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | current_file = os.path.basename(__file__) 5 | current_folder = os.path.dirname(__file__) 6 | default_config_path = os.path.join(current_folder, "config.json") 7 | 8 | class Config (): 9 | def __init__ (self, config_path=default_config_path, utf8=False): 10 | """Contructor of class 11 | 12 | Args: 13 | config_path (str/path, optional): Json file for process credentials. Defaults to config.json file. 14 | utf8 (bool, optional): Read or write data in utf8 format. Defaults to False. 15 | """ 16 | self.config_path=config_path 17 | self.utf8=utf8 18 | 19 | config_exist = os.path.isfile(self.config_path) 20 | if not config_exist: 21 | print (f"NOT FILE {self.config_path}") 22 | 23 | def get (self, credential=""): 24 | """ 25 | Get specific credential from config file 26 | """ 27 | 28 | # Read credentials file 29 | if self.utf8: 30 | config_file = open(self.config_path, "r", encoding='utf-8') 31 | else: 32 | config_file = open(self.config_path, "r") 33 | 34 | # Get specific credential 35 | try: 36 | config_data = json.loads(config_file.read()) 37 | return (config_data[credential]) 38 | except Exception as err: 39 | # print (err) 40 | return "" 41 | 42 | # Close file 43 | config_file.close() 44 | 45 | def get_all (self): 46 | """ 47 | return all crdentials from file 48 | """ 49 | 50 | # Read credentials file 51 | if self.utf8: 52 | config_file = open(self.config_path, "r", encoding='utf-8') 53 | else: 54 | config_file = open(self.config_path, "r") 55 | 56 | # Get specific credential 57 | try: 58 | config_data = json.loads(config_file.read()) 59 | return (config_data) 60 | except Exception as err: 61 | print (err) 62 | return "" 63 | 64 | # Close file 65 | config_file.close() 66 | 67 | def create_config (self, credentials, rewrite=False): 68 | """ 69 | Create a config file with default credentials 70 | """ 71 | 72 | if rewrite: 73 | open_mode = "w" 74 | else: 75 | open_mode = "a" 76 | 77 | with open (self.config_path, open_mode) as config_file: 78 | config_file.write(json.dumps(credentials)) 79 | 80 | 81 | def update (self, credential="", value=""): 82 | """ 83 | Update specific credential in config file 84 | """ 85 | 86 | with open (self.config_path, "r") as config_file: 87 | config_data = json.loads(config_file.read()) 88 | config_data[credential] = value 89 | 90 | with open (self.config_path, "w") as config_file: 91 | config_file.write(json.dumps(config_data)) 92 | 93 | def update_all (self, credentials, values): 94 | """ 95 | Update credentials 96 | """ 97 | 98 | for cred_config, cred_gui in credentials.items(): 99 | 100 | new_credential = values[cred_gui] 101 | self.update (cred_config, new_credential) -------------------------------------------------------------------------------- /spreadsheet_manager/xlsx.py: -------------------------------------------------------------------------------- 1 | import openpyxl 2 | from openpyxl.utils import get_column_letter 3 | from openpyxl.styles import Font 4 | 5 | class SS_manager (): 6 | """Manage local spread sheets 7 | """ 8 | 9 | def __init__(self, file_name): 10 | 11 | self.file_name = file_name 12 | self.wb = openpyxl.load_workbook(self.file_name) 13 | self.current_sheet = None 14 | 15 | def get_sheets (self): 16 | """ Return the list of sheets in the current document 17 | """ 18 | 19 | return self.wb.sheetnames 20 | 21 | 22 | def clean_workbook (self): 23 | """ Delete all sheets in current workbook 24 | """ 25 | 26 | for sheet in self.wb.sheetnames: 27 | sheet_obj = self.wb[sheet] 28 | self.wb.remove(sheet_obj) 29 | 30 | def create_get_sheet (self, sheet_name): 31 | """ Create a new sheet with specifici name, and set it as 32 | current sheet in class 33 | """ 34 | 35 | self.wb.create_sheet(sheet_name) 36 | self.set_sheet(sheet_name) 37 | 38 | def set_sheet (self, sheet_name): 39 | 40 | self.current_sheet = self.wb[sheet_name] 41 | 42 | def save (self): 43 | """Save current workbook 44 | """ 45 | 46 | self.wb.save(self.file_name) 47 | 48 | def write_cell (self, value="", row=1, column=1): 49 | """ Write data in specific cell 50 | """ 51 | 52 | self.current_sheet.cell (row, column).value = value 53 | 54 | def write_data (self, data=[], start_row=1, start_column=1): 55 | """ Write data list starting in specific cell 56 | """ 57 | 58 | current_row = start_row 59 | current_column = start_column 60 | 61 | for row in data: 62 | 63 | for cell_value in row: 64 | 65 | cell_obj = self.current_sheet.cell (current_row, current_column) 66 | cell_obj.value = cell_value 67 | 68 | current_column += 1 69 | 70 | current_column = start_column 71 | current_row += 1 72 | 73 | def auto_width (self): 74 | """ Set corect width to each coumn in the current sheet 75 | """ 76 | 77 | for col in self.current_sheet.columns: 78 | max_length = 0 79 | column = col[0].column_letter # Get the column name 80 | for cell in col: 81 | try: # Necessary to avoid error on empty cells 82 | if len(str(cell.value)) > max_length: 83 | max_length = len(str(cell.value)) 84 | except: 85 | pass 86 | adjusted_width = (max_length + 2) * 1.2 87 | self.current_sheet.column_dimensions[column].width = adjusted_width 88 | 89 | def format_range (self, start_cell=(1,1), end_cell=(1,1), italic=False, 90 | bold=False, font_size=8): 91 | 92 | # Create font style 93 | formated_font = Font(size=font_size, italic=italic, bold=bold) 94 | 95 | # Apply style 96 | current_row = start_cell[0] 97 | current_column = start_cell[1] 98 | 99 | for row in range(start_cell[0], end_cell[0] + 1): 100 | 101 | for cell_value in range(start_cell[1], end_cell[1] + 1): 102 | 103 | cell_obj = self.current_sheet.cell (current_row, current_column) 104 | cell_obj.font = formated_font 105 | 106 | current_column += 1 107 | 108 | current_column = 1 109 | current_row += 1 110 | 111 | def get_data (self): 112 | """ Get all data from the current page """ 113 | 114 | rows = self.current_sheet.max_row 115 | columns = self.current_sheet.max_column 116 | 117 | data = [] 118 | for row in range(1, rows + 1): 119 | 120 | row_data = [] 121 | for column in range(1, columns + 1): 122 | cell_data = self.current_sheet.cell (row, column).value 123 | row_data.append (cell_data) 124 | 125 | data.append (row_data) 126 | 127 | return data -------------------------------------------------------------------------------- /__main__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import time 4 | import shutil 5 | import globals 6 | import datetime 7 | import download 8 | from uploaders import instagram, twitter, youtube, facebook, tiktok 9 | from config import Config 10 | from moviepy.editor import VideoFileClip 11 | from spreadsheet_manager.google_ss import SS_manager 12 | from scraping_manager.automate import Web_scraping 13 | 14 | # Get credentials 15 | credentials = Config() 16 | chrome_folder = credentials.get ("chrome_folder") 17 | facebook_page = credentials.get ("facebook_page") 18 | api_key = credentials.get ("api_key") 19 | sheet_url = credentials.get ("sheet_url") 20 | upload_instagram = credentials.get ("instagram") 21 | upload_facebook = credentials.get ("facebook") 22 | upload_twitter = credentials.get ("twitter") 23 | upload_youtube = credentials.get ("youtube") 24 | upload_tiktok = credentials.get ("tiktok") 25 | 26 | # Global variables 27 | globals.current_folder = os.path.dirname (__file__) 28 | globals.download_folder = os.path.join (globals.current_folder, "downloads") 29 | globals.chrome_folder = chrome_folder 30 | 31 | 32 | def start_scraper (): 33 | """ Start selenium with user settings and save as global variable 34 | """ 35 | 36 | 37 | # Start browser for install extensions 38 | globals.scraper = Web_scraping (headless=False, 39 | download_folder=globals.download_folder, 40 | chrome_folder=globals.chrome_folder) 41 | 42 | def get_video_duration (file_path:str): 43 | """Get the duration in seconds from specific video 44 | 45 | Args: 46 | file_path (str): path of the video 47 | 48 | Returns: 49 | float: duation in seconds 50 | """ 51 | clip = VideoFileClip(file_path) 52 | clip.close() 53 | return clip.duration 54 | 55 | def main (): 56 | """ 57 | Download videos from tiktok and post in: 58 | * facebook page 59 | * youtube shorts 60 | * instagram reels 61 | * twitter 62 | * tiktok 63 | """ 64 | 65 | # Get data from file 66 | print ("connecting with google sheet...") 67 | ss = SS_manager(sheet_url, api_key) 68 | videos_data = ss.get_data() 69 | 70 | # Main loop for each video 71 | output_data = [[]] 72 | for row in videos_data: 73 | 74 | # Get data from row 75 | date_time_text = row["date time"] 76 | video_url_name = row["url or name"] 77 | title = row["title"] 78 | description = row["description"] 79 | tags_text = row["tags"] 80 | processed = row["processed"] 81 | uploaded_instagram = row["uploaded instagram"] 82 | uploaded_facebook = row["uploaded facebook"] 83 | uploaded_twitter = row["uploaded twitter"] 84 | uploaded_youtube = row["uploaded youtube"] 85 | uploaded_tiktok = row["uploaded tiktok"] 86 | 87 | # Tags to list 88 | tags = tags_text.split(",") 89 | 90 | # Format date time 91 | date_time = datetime.datetime.strptime(date_time_text, "%m/%d/%Y %H:%M") 92 | 93 | # Validate video link 94 | if not video_url_name: 95 | break 96 | else: 97 | 98 | print (f"\nCurrent video: {title}") 99 | 100 | # Time validation 101 | now = datetime.datetime.now() 102 | if now > date_time: 103 | print (f"\tVideo skipped. The current time ({now}) is greater than the publication time ({date_time}).") 104 | output_data.append ([]) 105 | continue 106 | 107 | # Validate video processed 108 | if processed.lower().strip() == "yes": 109 | print ("\tVideo omitted, already processed") 110 | output_data.append ([]) 111 | continue 112 | else: 113 | 114 | start_scraper () 115 | 116 | # Wait time 117 | wait_time = date_time - now 118 | print (f"\tWaiting for the time: {date_time}...") 119 | time.sleep (wait_time.total_seconds()) 120 | 121 | # Default values for output uploaded in spreadsheet 122 | uploaded_instagram = "no" 123 | uploaded_facebook = "no" 124 | uploaded_twitter = "no" 125 | uploaded_youtube = "no" 126 | uploaded_tiktok = "no" 127 | processed = "yes" 128 | 129 | # Download video 130 | if "www.tiktok.com" in video_url_name: 131 | # Download tiktok video 132 | file_path = download.tiktok (video_url_name, title) 133 | else: 134 | # Validate video path 135 | file_path = os.path.join (globals.current_folder, "downloads", video_url_name) 136 | if not os.path.isfile (file_path): 137 | raise FileNotFoundError (file_path) 138 | 139 | duration = get_video_duration (file_path) 140 | 141 | # Validate duration for youtube and instagram 142 | if duration <= 60: 143 | # Upload video to youtube 144 | if upload_youtube: 145 | youtube.upload (file_path, title, description, tags) 146 | uploaded_youtube = "yes" 147 | 148 | # Upload video to instagram 149 | if upload_instagram: 150 | instagram.upload (file_path, title, description, tags) 151 | uploaded_instagram = "yes" 152 | 153 | # Upload video to tiktok 154 | if upload_tiktok: 155 | tiktok.upload (file_path, title, description, tags) 156 | uploaded_tiktok = "yes" 157 | else: 158 | print ("\tYoutube, Instagram and Tiktok: video skipped (60 sec it's max time for this pages)") 159 | 160 | # Validate duration for twitter 161 | if duration <= 140: 162 | if upload_twitter: 163 | # Convert video 164 | file_converted = twitter.convert (file_path) 165 | globals.scraper.kill () 166 | start_scraper () 167 | 168 | # Upload video to twitter 169 | twitter.upload (file_converted, title, description, tags) 170 | uploaded_twitter = "yes" 171 | 172 | # Move twitter file to done folder 173 | shutil.move (file_converted, file_converted.replace("downloads", "done")) 174 | 175 | else: 176 | print ("\tTwitter: video skipped (2:20 min it's max time for twitter)") 177 | 178 | # Post in faebook page without time validation 179 | if upload_facebook: 180 | facebook.upload (facebook_page, file_path, title, description, tags) 181 | uploaded_facebook = "yes" 182 | 183 | # End browser 184 | globals.scraper.kill() 185 | 186 | # Move file to done folder 187 | os.replace (file_path, file_path.replace("downloads", "done")) 188 | 189 | # Add row to output data 190 | output_data.append ([ 191 | date_time_text, 192 | video_url_name, 193 | title, 194 | description, 195 | tags_text, 196 | processed, 197 | uploaded_instagram, 198 | uploaded_facebook, 199 | uploaded_twitter, 200 | uploaded_youtube, 201 | uploaded_tiktok 202 | ]) 203 | 204 | # End browser 205 | try: 206 | globals.scraper.kill() 207 | except: 208 | pass 209 | 210 | # Update data in sheet 211 | ss.worksheet.update (output_data) 212 | print ("Done") 213 | 214 | 215 | if __name__ == "__main__": 216 | main() -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
16 |
17 | # Video Post
18 |
19 | Download videos from tiktok and post in:
20 | * facebook page
21 | * youtube shorts
22 | * instagram reels
23 | * twitter
24 | * tiktok
25 |
26 | Project type: **client**
27 |
28 |