├── .DS_Store ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── BotTuber.png ├── CODE_OF_CONDUCT.md ├── Credits ├── Google.py ├── LICENSE ├── README.md ├── botTuber.py ├── config.py ├── instaloader_test.py ├── instalooter_test.py ├── intro.mp4 ├── make_compilation.py ├── outro.mp4 ├── requirements.txt ├── scrape_videos.py ├── setup_google.py └── upload_ytvid.py /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sam5epi0l/BotTuber/098d3c74bd610f39c6e53c663bcd8e395cb3ecb4/.DS_Store -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /BotTuber.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sam5epi0l/BotTuber/098d3c74bd610f39c6e53c663bcd8e395cb3ecb4/BotTuber.png -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | . 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /Credits: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Nathan Ang 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. 22 | -------------------------------------------------------------------------------- /Google.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | import os 3 | from google_auth_oauthlib.flow import Flow, InstalledAppFlow 4 | from googleapiclient.discovery import build 5 | from googleapiclient.http import MediaFileUpload, MediaIoBaseDownload 6 | from google.auth.transport.requests import Request 7 | 8 | def Create_Service(client_secret_file, api_name, api_version, *scopes): 9 | CLIENT_SECRET_FILE = client_secret_file 10 | API_SERVICE_NAME = api_name 11 | API_VERSION = api_version 12 | SCOPES = [scope for scope in scopes[0]] 13 | print(SCOPES) 14 | 15 | cred = None 16 | 17 | pickle_file = f'token_{API_SERVICE_NAME}_{API_VERSION}.pickle' 18 | # print(pickle_file) # Do not change 19 | 20 | if os.path.exists(pickle_file): 21 | with open(pickle_file, 'rb') as token: 22 | cred = pickle.load(token) 23 | 24 | if not cred or not cred.valid: 25 | if cred and cred.expired and cred.refresh_token: 26 | cred.refresh(Request()) 27 | else: 28 | flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRET_FILE, SCOPES) 29 | cred = flow.run_local_server() 30 | 31 | with open(pickle_file, 'wb') as token: 32 | pickle.dump(cred, token) 33 | 34 | try: 35 | service = build(API_SERVICE_NAME, API_VERSION, credentials=cred) 36 | print(API_SERVICE_NAME, 'service created successfully') 37 | return service 38 | except Exception as e: 39 | print('Unable to connect.') 40 | print(e) 41 | return None 42 | 43 | def convert_to_RFC_datetime(year=1900, month=1, day=1, hour=0, minute=0): 44 | dt = datetime.datetime(year, month, day, hour, minute, 0).isoformat() + 'Z' 45 | return dt 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 pradeep jhuriya 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #### Graphql error in instalooter not been resolved yet! 2 | 3 | # Fully Automated Youtube Channel 4 | 5 | ``` 6 | ░█▀▀█ █▀▀█ ▀▀█▀▀ ▀▀█▀▀ █──█ █▀▀▄ █▀▀ █▀▀█ 7 | ░█▀▀▄ █──█ ──█── ─░█── █──█ █▀▀▄ █▀▀ █▄▄▀ 8 | ░█▄▄█ ▀▀▀▀ ──▀── ─░█── ─▀▀▀ ▀▀▀─ ▀▀▀ ▀─▀▀ 9 | 🄵🅄🄻🄻 🅈🄾🅄🅃🅄🄱🄴 🄲🄷🄰🄽🄽🄴🄻 🄰🅄🅃🄾🄼🄰🅃🄸🄾🄽 🅂🅄🄸🅃🄴 10 | ``` 11 | 12 | [![GitHub issues](https://img.shields.io/github/issues/sam5epi0l/BotTuber?color=lightgreeen&style=for-the-badge)](https://github.com/sam5epi0l/BotTuber/issues) 13 | [![GitHub forks](https://img.shields.io/github/forks/sam5epi0l/BotTuber?color=brightgreen&style=for-the-badge)](https://github.com/sam5epi0l/BotTuber/network) 14 | [![GitHub stars](https://img.shields.io/github/stars/sam5epi0l/BotTuber?color=blue&style=for-the-badge)](https://github.com/sam5epi0l/BotTuber/stargazers) 15 | [![GitHub license](https://img.shields.io/github/license/sam5epi0l/BotTuber?color=lightgrey&style=for-the-badge)](https://github.com/sam5epi0l/BotTuber/blob/master/LICENSE) 16 | git status 17 | BotTuber LOGO 18 | [![Twitter](https://img.shields.io/twitter/url?style=social&url=https%3A%2F%2Ftwitter.com%2Fsam5epi0l?style=for-the-badge)](https://twitter.com/intent/tweet?text=Wow:&url=https%3A%2F%2Fgithub.com%2Fsam5epi0l%2FBotTuber) 19 | [![YouTube](https://img.shields.io/badge/videoTutorial-BotTuber-red)](https://youtu.be/BbPErvcqXyw) 20 | 21 | 22 | 23 | Code to run a fully automated youtube that can scrape content, edit a compilation, and upload to youtube daily. 24 | 25 | 26 | 27 | 28 | 29 | 30 | # Features😶‍🌫️ 31 | 32 | 1. Interactive Auto/Manual mode 33 | 2. Makes Compilation (Intro & Outro) 34 | 3. Auto Title, Description & Tags 35 | 4. Auto TimeStamps & Credits (Username & Caption from Video) 36 | 5. Edit description.txt when in manual mode 37 | 6. Add Watermark to final Video 38 | 39 | # Quick Start🐿️ 40 | 41 | git clone https://github.com/sam5epi0l/BotTuber.git 42 | cd BotTuber 43 | # add instagram credentials in config.py 44 | # add YouTube API v3 credentials to googleAPI.json (check instructions) 45 | pip3 install -r requirements.txt 46 | python3 botTuber.py 47 | 48 | # Usage📄 49 | 50 | python3 botTuber.py -i # interactive mode 51 | python3 botTuber.py -a # Full automation 52 | python3 botTuber.py -m # manual mode 53 | python3 botTuber.py -h # help menu 54 | 55 | # Instructions✅ 56 | 57 | 1. [Download](https://github.com/sam5epi0l/BotTuber.git) the Github Repository 58 | 59 | 2. Download and install [Python3](https://www.python.org/downloads/) and [pip](https://pip.pypa.io/en/stable/installing/) if necessary. 60 | 61 | 3. Install libraries with `pip3 install -r requirements.txt` or `python3 -m pip install -r requirements.txt` . 62 | 63 | 4. Get setup and create a Project with the Youtube API: https://developers.google.com/youtube/v3/quickstart/python 64 | Be sure to follow it carefully, as it won't work if you don't do this part right. 65 | Download your OATH file and name it as "googleAPI.json" in your project folder. 66 | 67 | 6. Create an instagram account and follow accounts you want to scrape from 68 | 69 | 7. Open config.py in a text editor and fill in instagram credentials 70 | 71 | - Note that you can edit variables inside botTuber.py in a text editor and things such as MAX_CLIP_LENGTH, Title, Description, Tags, etc.. 72 | 73 | 8. In terminal/cmd, run `instalooter login` and `instaloader --login {YOUR_USERNAME}`. Follow the instructions to login. 74 | 75 | 9. Run `python3 botTuber.py` in your computer terminal (terminal or cmd). You have to sign in to your Youtube Account through the link the script will give you. It's going to ask you: "Please visit this URL to authorize this application:..." so you copy that link, paste it in your browser, and then sign into your Google account. Then paste the authentication code you get back into your terminal. It will then say "Starting Scraping" and sign into your instagram account. 76 | 77 | 10. Type "A" to run automated script or "M" to manually decide what to do at each step 78 | 79 | 11. Enjoy your fully automated youtube channel! :) Note that for uploading public videos, you have to complete an audit for the Youtube API. See the note in the [Google Documentation](https://developers.google.com/youtube/v3/docs/videos/insert). Without this, you can only post private videos, but they approve everyone. Have fun! 80 | 81 | ## Contributors ✨ 82 | 83 | Thanks goes to these wonderful people who have improved the code and documentation to help this project grow. : 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 |

sam-sepiol

🖋

nathan Ang

🖋
94 | 95 | 96 | 97 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind are welcome! 98 | 99 | ## Socials 100 | 101 | * [YouTube_Channel](https://youtube.com/c/pwnos) 102 | * [Buy Me A Coffee](https://www.buymeacoffee.com/sam5epi0l) 103 | * [Patreon](https://www.patreon.com/pwnOS) 104 | * [LinkedIn](https://linkedin.com/in/sam-sepi0l/) 105 | * [Quora](https://pwnos.quora.com/) 106 | * [GitHub](https://github.com/sam5epi0l) 107 | * [TryHackMe](https://tryhackme.com/signup?referrer=sam.sepiol) 108 | * [Reddit](https://www.reddit.com/r/pwn05/) 109 | -------------------------------------------------------------------------------- /botTuber.py: -------------------------------------------------------------------------------- 1 | from scrape_videos import scrapeVideos 2 | from make_compilation import makeCompilation 3 | from upload_ytvid import uploadYtvid 4 | import schedule 5 | import time 6 | import datetime 7 | import os 8 | import shutil 9 | import googleapiclient.errors 10 | from googleapiclient.discovery import build #pip install google-api-python-client 11 | from google_auth_oauthlib.flow import InstalledAppFlow #pip install google-auth-oauthlib 12 | from google.auth.transport.requests import Request 13 | from google.oauth2.credentials import Credentials 14 | import config 15 | import json 16 | import sys 17 | from pyffmpeg import FFmpeg 18 | 19 | 20 | print(""" 21 | ▒█▀▀█ █▀▀█ ▀▀█▀▀ ▀▀█▀▀ █░░█ █▀▀▄ █▀▀ █▀▀█ 22 | ▒█▀▀▄ █░░█ ░░█░░ ░▒█░░ █░░█ █▀▀▄ █▀▀ █▄▄▀ 23 | ▒█▄▄█ ▀▀▀▀ ░░▀░░ ░▒█░░ ░▀▀▀ ▀▀▀░ ▀▀▀ ▀░▀▀ 24 | 🄵🅄🄻🄻 🅈🄾🅄🅃🅄🄱🄴 🄲🄷🄰🄽🄽🄴🄻 🄰🅄🅃🄾🄼🄰🅃🄸🄾🄽 🅂🅄🄸🅃🄴 25 | 26 | YouTube Channel - https://youtube.com/c/pwnos 27 | GitHub - https://github.com/sam5epi0l/BotTuber 28 | LinkedIn - https://linkedin.com/in/sam-sepi0l/ 29 | Twitter - https://twitter.com/sam5epi0l 30 | Quora - https://pwnos.quora.com/ 31 | Patreon - https://www.patreon.com/pwnOS 32 | Buymeacoffee - https://www.buymeacoffee.com/sam5epi0l 33 | """) 34 | 35 | if sys.argv[-1].upper() == "-A": 36 | mode = "A" 37 | elif sys.argv[-1].upper() == "-I": 38 | mode = input("[Q] Automated or Manual A/M:").upper() 39 | elif sys.argv[-1].upper() == "-M": 40 | mode = "M" 41 | else: 42 | print(""" 43 | Code to run a fully automated youtube that can scrape content, edit a compilation, and upload to youtube daily. \ 44 | 45 | Quick Start 46 | git clone https://github.com/sam5epi0l/BotTuber.git 47 | cd BotTuber 48 | # add instagram credentials in config.py 49 | # add YouTube API v3 credentials to googleAPI.json (check instructions) 50 | pip3 install -r requirements.txt 51 | python3 botTuber.py 52 | 53 | USAGE: python3 botTuber.py [OPTIONS] 54 | 55 | OPTIONS - 56 | python3 botTuber.py -i Interactive Mode 57 | python3 botTuber.py -a Full Automation 58 | python3 botTuber.py -m Manual Mode 59 | python3 botTuber.py -h Help Menu 60 | """) 61 | exit() 62 | 63 | 64 | num_to_month = { 65 | 1: "Jan", 66 | 2: "Feb", 67 | 3: "Mar", 68 | 4: "Apr", 69 | 5: "May", 70 | 6: "June", 71 | 7: "July", 72 | 8: "Aug", 73 | 9: "Sept", 74 | 10: "Oct", 75 | 11: "Nov", 76 | 12: "Dec" 77 | } 78 | now = datetime.datetime.now() 79 | 80 | # USER VARIABLES FILL THESE OUT (fill out username and password in config.py) 81 | 82 | IG_USERNAME = config.IG_USERNAME 83 | IG_PASSWORD = config.IG_PASSWORD 84 | print("[i] ", IG_USERNAME) 85 | print("[i] ", IG_PASSWORD) 86 | 87 | # Commit - Automating Title with HashTags 88 | if mode == "M": 89 | title = input("[Q] Type video title in 100 C or leave Blank to Use Default Title:").strip() 90 | description = input("[Q] Type video description headers or leave blank to use Default Headers:").strip() 91 | tags = input("[Q] Add some tags to default tag list or use default tags:").strip() 92 | if title == "": 93 | title = "TRY NOT TO LAUGH | BEST Dank video #memes #" + str(now.day) 94 | elif mode == "A": 95 | title = "TRY NOT TO LAUGH | BEST Dank video #memes #" + str(now.day) 96 | description = "" 97 | tags = "" 98 | 99 | videoDirectory = "./DankMemes_" + num_to_month[now.month].upper() + "_" + str(now.year) + "_V" + str(now.day) + "/" 100 | outputFile = "./" + num_to_month[now.month].upper() + "_" + str(now.year) + "_v" + str(now.day) + ".mp4" 101 | os.system(f"touch {videoDirectory}description.txt") #make description file 102 | 103 | INTRO_VID = 'intro.mp4' # SET AS '' IF YOU DONT HAVE ONE 104 | OUTRO_VID = 'outro.mp4' 105 | WATER_MARK = 'BotTuber.png' 106 | TOTAL_VID_LENGTH = 13*60 107 | MAX_CLIP_LENGTH = 19 108 | MIN_CLIP_LENGTH = 5 109 | DAILY_SCHEDULED_TIME = "20:00" 110 | TOKEN_NAME = "token.json" # Don't change 111 | 112 | # Setup Google 113 | SCOPES = ["https://www.googleapis.com/auth/youtube.upload"] 114 | os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1" 115 | client_secrets_file = "googleAPI.json" 116 | 117 | def routine(title, description, tags): 118 | 119 | # Handle GoogleAPI oauthStuff 120 | print("[+] Handling GoogleAPI") 121 | creds = None 122 | # The file token1.json stores the user's access and refresh tokens, and is 123 | # created automatically when the authorization flow completes for the first 124 | # time. 125 | if os.path.exists(TOKEN_NAME): 126 | creds = Credentials.from_authorized_user_file(TOKEN_NAME, SCOPES) 127 | # If there are no (valid) credentials available, let the user log in. 128 | if not creds or not creds.valid: 129 | if creds and creds.expired and creds.refresh_token: 130 | creds.refresh(Request()) 131 | else: 132 | flow = InstalledAppFlow.from_client_secrets_file( 133 | client_secrets_file, SCOPES) 134 | creds = flow.run_console() 135 | # Save the credentials for the next run 136 | with open(TOKEN_NAME, 'w') as token: 137 | token.write(creds.to_json()) 138 | 139 | googleAPI = build('youtube', 'v3', credentials=creds) 140 | 141 | now = datetime.datetime.now() 142 | print("[+] ", now.year, now.month, now.day, now.hour, now.minute, now.second) 143 | 144 | print("[+] ", outputFile) 145 | 146 | if not os.path.exists(videoDirectory): 147 | os.makedirs(videoDirectory) 148 | 149 | # Step 1: Scrape Videos 150 | print("[+] Scraping Videos...") 151 | scrapeVideos(username = IG_USERNAME, 152 | password = IG_PASSWORD, 153 | output_folder = videoDirectory, 154 | modeAM = mode, 155 | days=1) 156 | print("[+] Scraped Videos!") 157 | 158 | os.system(f"touch {videoDirectory}description.txt") #make description file 159 | # intro into description 160 | description += """Enjoy some of the funniest videos on the internet! 161 | Why spend hours searching for funny videos that make you laugh when you can get some of the best memes here! 162 | 163 | This Video is made using BotTuber tool which scrape videos from Instagram, make compilation and upload to YouTube with Auto Title, Description & Tags with creadits to instagram account owners 164 | 165 | Try it YourSelf - GitHub Repositry - https://github.com/sam5epi0l/BotTuber 166 | 167 | ▒█▀▀█ █▀▀█ ▀▀█▀▀ ▀▀█▀▀ █░░█ █▀▀▄ █▀▀ █▀▀█ 168 | ▒█▀▀▄ █░░█ ░░█░░ ░▒█░░ █░░█ █▀▀▄ █▀▀ █▄▄▀ 169 | ▒█▄▄█ ▀▀▀▀ ░░▀░░ ░▒█░░ ░▀▀▀ ▀▀▀░ ▀▀▀ ▀░▀▀ 170 | 🄵🅄🄻🄻 🅈🄾🅄🅃🅄🄱🄴 🄲🄷🄰🄽🄽🄴🄻 🄰🅄🅃🄾🄼🄰🅃🄸🄾🄽 🅂🅄🄸🅃🄴 171 | 172 | YouTube Channel - https://youtube.com/c/pwnos 173 | GitHub - https://github.com/sam5epi0l/ 174 | LinkedIn - https://linkedin.com/in/sam-sepi0l/ 175 | Twitter - https://twitter.com/sam5epi0l 176 | Quora - https://pwnos.quora.com/ 177 | Patreon - https://www.patreon.com/pwnOS 178 | Buymeacoffee - https://www.buymeacoffee.com/sam5epi0l 179 | medium - https://medium.com/@sam5epi0l 180 | reddit - https://reddit.com/pwnOS 181 | 182 | In this video, I show you the best Dank Video Memes on the internet😁😂😂. 183 | Links To Sources & Credit to owners⬇️: 184 | 185 | """ 186 | 187 | with open(f"{videoDirectory}description.txt", 'a+', encoding="utf-8") as dfile: 188 | dfile.write(description) 189 | 190 | # Step 2: Make Compilation 191 | 192 | if mode == "M": 193 | makeCompilation_or_not = input(f"[Q] Use existing compilation i.e, {outputFile}(y/N)").strip() 194 | if makeCompilation_or_not.lower() == "y": 195 | print(f"[+] using existing File {outputFile}") 196 | else: 197 | print("[+] Making Compilation...") 198 | makeCompilation(path = videoDirectory, 199 | introName = INTRO_VID, 200 | outroName = OUTRO_VID, 201 | wmark = WATER_MARK, 202 | totalVidLength = TOTAL_VID_LENGTH, 203 | maxClipLength = MAX_CLIP_LENGTH, 204 | minClipLength = MIN_CLIP_LENGTH, 205 | outputFile = outputFile, 206 | videoDirectory = videoDirectory, 207 | description_meta= "") 208 | print("[+] Made Compilation!") 209 | else: 210 | print("[+] Making Compilation...") 211 | makeCompilation(path = videoDirectory, 212 | introName = INTRO_VID, 213 | outroName = OUTRO_VID, 214 | wmark = WATER_MARK, 215 | totalVidLength = TOTAL_VID_LENGTH, 216 | maxClipLength = MAX_CLIP_LENGTH, 217 | minClipLength = MIN_CLIP_LENGTH, 218 | outputFile = outputFile, 219 | videoDirectory = videoDirectory, 220 | description_meta= "") 221 | print("[+] Made Compilation!") 222 | 223 | # added video metaData(profile, video_url, Caption) within make_compilation script 224 | #description += make_compilation.description_meta 225 | 226 | description += """ 227 | Welcome to my Channel, where I search for the best trending videos, or videos people have forgotten about, and put them all in one video. I upload 2-3 times a week to keep video quality high. I always ask for permission to share videos that I find! 228 | If you enjoyed this video, watch my other videos as well 229 | 230 | 231 | subscribe today - https://youtube.com/c/pwnOS 232 | ►►►Follow me! 233 | GitHub - https://github.com/sam5epi0l/ 234 | LinkedIn - https://linkedin.com/in/sam-sepi0l/ 235 | Twitter - https://twitter.com/sam5epi0l 236 | Quora - https://pwnos.quora.com/ 237 | medium - https://medium.com/@sam5epi0l 238 | reddit - https://reddit.com/pwnOS 239 | new video every day :) 240 | 241 | #️⃣clips featured are used with permission from original creators 242 | 243 | """ 244 | 245 | #disclaimer 246 | description += "\n----------------------------------------------------------------------------------------------------------------" 247 | 248 | # tags 249 | description += """\n 250 | In this video you will watch Extremely Funny memes, dankest, funny af, offensive memes, vine videos, meme compilation, dank meme compilation, Funny videos, Memes, Unexpected videos, Reddit Memes, Perfectly Cut Screams Memes, Watch People Die Inside Memes, videos I found on reddit, Try not to laugh videos, Totally Random, Cursed Memes, Cute and Funny Animals, Cute and Funny Dogs, Cute and Funny Cats, Funny Vines, Anime Memes, Cartoon Memes, Fails Memes, SpongeBob Memes, Spiderman Memes, Super Mario Memes, Dwayne "The Rock" Johnson Memes, Gaming Memes, Among Us "Sus" Memes, Disney Memes, Nintendo Memes, Wii Sports Memes, Mickey Mouse Memes, Star Wars Memes, Adventure Time Memes, Twitch Streamer Memes, Family Guy Memes, GTA Memes, Bowling Memes, Soccer Football Memes, Fortnite Memes, Music Memes, Avengers Memes, Michael Jackson Memes, Pokemon Memes, Windows Error Memes, Thanos Memes, Zoom Memes, Winnie The Pooh Memes, McDonald's Memes, Monkey Memes, Twitter Memes, Will Smith Memes, School Memes, Halloween Memes, Phineas and Ferb Memes, Mom Memes, Holy Memes, Amazon Echo Memes, Gorillaz Memes 251 | 252 | Memes that are approved by school 253 | Memes that will finally bring you happiness 254 | You laugh, you 💀 255 | Memes that are teacher approved 256 | Memes that will finally bring you happines 257 | memes that 😂 258 | UNUSUAL MEMES COMPILATION 259 | The Best Of The Internet 260 | """ 261 | description += "\n\nCopyright Disclaimer, Under Section 107 of the Copyright Act 1976, allowance is made for 'fair use' for purposes such as criticism, comment, news reporting, teaching, scholarship, and research. Fair use is a use permitted by copyright statute that might otherwise be infringing. Non-profit, educational or personal use tips the balance in favor of fair use.\n\n" 262 | 263 | # Hashtags 264 | description += "#memes #dankmemes #compilation #funny #funnyvideos #BotTuber \n\n" 265 | description += "#memes #dankmemes #compilation #funny #funnyvideos #shorts #TikTok #randomvideos #BotTuber " 266 | 267 | with open(f"{videoDirectory}description.txt", 'a+', encoding="utf-8") as dfile: 268 | dfile.write(description) 269 | 270 | with open(f"{videoDirectory}description.txt", 'r', encoding="utf-8") as rfile: 271 | description = rfile.read() 272 | print("[+]", description) 273 | 274 | tags += "Extremely Funny memes, dankest, funny af, offensive memes, vine videos, meme compilation, dank meme compilation, Funny videos, Memes, Unexpected videos, Reddit Memes" 275 | 276 | # Step 3: Upload to Youtube 277 | 278 | def upload_to_youtube(): 279 | print("[+] Uploading to Youtube...") 280 | uploadYtvid(VIDEO_FILE_NAME=outputFile, 281 | title=title, 282 | description=description, 283 | modeAM=mode, 284 | googleAPI=googleAPI) 285 | print("[+] Uploaded To Youtube!") 286 | 287 | if mode =="A": 288 | upload_to_youtube() 289 | print(f"[+] tags used: {tags}") 290 | elif mode =="M": 291 | proceed_to_upload = input("[Q] Upload to YouTube Y/n:") 292 | if proceed_to_upload != "n": 293 | upload_to_youtube() 294 | print(f"[+] tags used: {tags}") 295 | else: 296 | print("[+] Video not uploaded to YouTube") 297 | 298 | # Step 4: Cleanup 299 | def cleanup(): 300 | print("[-] Removing temp files!") 301 | # Delete all files made: 302 | # Folder videoDirectory 303 | shutil.rmtree(videoDirectory, ignore_errors=True) 304 | # remove instalooter cache to remove error 305 | instalooterDirectory = "~/.cache/instalooter" 306 | shutil.rmtree(instalooterDirectory, ignore_errors=True) 307 | # File outputFile 308 | try: 309 | os.remove(outputFile) 310 | except OSError as e: ## if failed, report it back to the user ## 311 | print ("[E] Error: %s - %s." % (e.filename, e.strerror)) 312 | print("[i] Removed temp files!") 313 | 314 | if mode == "A": 315 | cleanup() 316 | elif mode == "M": 317 | keep_files = input("[Q] Do you wanna keep temp files?(Y/n)").strip() 318 | if keep_files == "n": 319 | cleanup() 320 | else: 321 | print("[-] files are not deleted") 322 | 323 | def attemptRoutine(): 324 | while(1): 325 | try: 326 | routine(title, description, tags) 327 | break 328 | except OSError as err: 329 | print("[e] Routine Failed on " + "OS error: {0}".format(err)) 330 | time.sleep(60*60) 331 | 332 | #attemptRoutine() 333 | schedule.every().day.at(DAILY_SCHEDULED_TIME).do(attemptRoutine) 334 | 335 | attemptRoutine() 336 | while True: 337 | schedule.run_pending() 338 | time.sleep(60) # wait one min 339 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | IG_USERNAME = "example_username" 2 | IG_PASSWORD = "example_password" -------------------------------------------------------------------------------- /instaloader_test.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | import instaloader 3 | 4 | # Do not change 5 | # instaloader downloads some posts under the hashtag urbanphotography 6 | 7 | L = instaloader.Instaloader() 8 | 9 | posts = instaloader.Hashtag.from_name(L.context, "urbanphotography").get_posts() 10 | 11 | SINCE = datetime(2020, 5, 10) # further from today, inclusive 12 | UNTIL = datetime(2020, 5, 11) # closer to today, not inclusive 13 | 14 | k = 0 # initiate k 15 | #k_list = [] # uncomment this to tune k 16 | 17 | for post in posts: 18 | postdate = post.date 19 | 20 | if postdate > UNTIL: 21 | continue 22 | elif postdate <= SINCE: 23 | k += 1 24 | if k == 50: 25 | break 26 | else: 27 | continue 28 | else: 29 | L.download_post(post, "#urbanphotography") 30 | # if you want to tune k, uncomment below to get your k max 31 | #k_list.append(k) 32 | k = 0 # set k to 0 33 | -------------------------------------------------------------------------------- /instalooter_test.py: -------------------------------------------------------------------------------- 1 | from instalooter.looters import ProfileLooter 2 | import datetime 3 | import dateutil.relativedelta 4 | 5 | # instalooter_test downloads videos posted by daquan in the last month 6 | 7 | # Instanciate 8 | looter = ProfileLooter("daquan", videos_only=True, template="{id}-{username}-{width}-{height}") 9 | looter.login("", "") 10 | 11 | today = datetime.date.today() 12 | thismonth = (today, today - dateutil.relativedelta.relativedelta(days=28)) 13 | 14 | looter.download('./Memes_December_4', media_count=50, timeframe=thismonth) -------------------------------------------------------------------------------- /intro.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sam5epi0l/BotTuber/098d3c74bd610f39c6e53c663bcd8e395cb3ecb4/intro.mp4 -------------------------------------------------------------------------------- /make_compilation.py: -------------------------------------------------------------------------------- 1 | from moviepy.editor import VideoFileClip, concatenate_videoclips 2 | from moviepy.video.fx.resize import resize 3 | import os 4 | from os.path import isfile, join 5 | import random 6 | import shutil 7 | from collections import defaultdict 8 | import json 9 | from pyffmpeg import FFmpeg 10 | 11 | 12 | ff = FFmpeg() 13 | VideoFileClip.resize = resize 14 | 15 | def extractAcc(filepath): 16 | try: 17 | s = filepath.split("/")[-1].split("-") 18 | acc = "-".join(s[1:(2+(len(s) - 4))]) 19 | return acc 20 | except: 21 | return "" 22 | 23 | 24 | # generateTimeRange converts float seconds to a range of form @MM:SS 25 | def generateTimeRange(duration, clipDuration): 26 | preHour = int(duration / 60) 27 | preMin = int(duration % 60) 28 | preTime = str(preHour // 10) + str(preHour % 10) + ":" + str(preMin // 10) + str(preMin % 10) 29 | 30 | duration += clipDuration 31 | postHour = int(duration / 60) 32 | postMin = int(duration % 60) 33 | postTime = str(postHour // 10) + str(postHour % 10) + ":" + str(postMin // 10) + str(postMin % 10) 34 | 35 | #return "@" + preTime + " - " + "@" + postTime 36 | return "@" + preTime 37 | 38 | 39 | # makeCompilation takes videos in a folder and creates a compilation with max length totalVidLength 40 | def makeCompilation(path = "./", 41 | introName = '', 42 | outroName = '', 43 | wmark = '', 44 | totalVidLength = 10, 45 | maxClipLength = 20, 46 | minClipLength = 5, 47 | outputFile = "output.mp4", 48 | video_source_meta = {}, 49 | videoDirectory = "", 50 | description_meta = "", 51 | modeAM = "A"): 52 | 53 | downVideos = [] 54 | seenLengths = defaultdict(list) 55 | #totalLength = 0 56 | duration = 0 57 | videos = [] 58 | 59 | # Add intro video if included 60 | if introName != '': 61 | introVid = VideoFileClip("./" + introName) 62 | videos.append(introVid) 63 | timeStamp = generateTimeRange(duration, introVid.duration) 64 | duration += introVid.duration 65 | 66 | for fileName in os.listdir(path): 67 | filePath = join(path, fileName) 68 | 69 | if isfile(filePath) and fileName.endswith(".mp4"): 70 | 71 | if os.stat(filePath).st_size < 5000: 72 | continue 73 | 74 | # Destination path 75 | print("[i] ", filePath) 76 | clip = VideoFileClip(filePath) 77 | clip = clip.resize(width=1920) 78 | clip = clip.resize(height=1080) 79 | duration = clip.duration 80 | print("[i] " + fileName + " " + str(duration) + " Added") 81 | 82 | # add_video in min&max range or ignore errors 83 | def add_video(duration): 84 | downVideos.append(clip) 85 | seenLengths[duration].append(fileName) 86 | duration += clip.duration 87 | 88 | 89 | print("[i] ", duration, seenLengths, downVideos) 90 | 91 | if modeAM == "A": 92 | add_video(duration) 93 | elif modeAM == "M": 94 | if duration <= maxClipLength and duration >= minClipLength: 95 | add_video(duration) 96 | else: 97 | ignore_error = input("[Q] Do you want to ignore Errors in min max Total Video Length?(Y/n)").strip() 98 | if ignore_error != "n": 99 | pass 100 | else: 101 | add_video(duration) 102 | 103 | #Add automated description 104 | 105 | for k in range(len(os.listdir(path))): 106 | 107 | fileNameJ = fileName.split(".mp4") 108 | fileNameJSON = ''.join(fileNameJ) + ".json" 109 | 110 | acc = extractAcc(clip.filename) 111 | 112 | timeStamp = generateTimeRange((duration - clip.duration), clip.duration) 113 | video_source_meta[f"TimeStamps{k}"] = timeStamp + " : @" + acc + "\n" 114 | 115 | video_source_meta[f"profile{k}"] = "Instagram profile:" + " instagram.com/" + acc +'\n' 116 | 117 | #extract url & other information about video 118 | 119 | f = open(f"{videoDirectory}{fileNameJSON}", "r") 120 | json_d = json.loads(f.read()) 121 | f.close() 122 | 123 | video_source_meta[f"vido_url{k}"] = "Video URL:" + "instagram.com/tv/" + json_d["shortcode"] + '\n' 124 | video_source_meta[f"Caption{k}"] = json_d["edge_media_to_caption"]["edges"][0]["node"]["text"] + '\n' 125 | 126 | description_meta = video_source_meta[f"TimeStamps{k}"] + video_source_meta[f"profile{k}"] + video_source_meta[f"vido_url{k}"] + video_source_meta[f"Caption{k}"] + '\n\n' 127 | 128 | print("[i] ", description_meta) 129 | 130 | with open(f"{videoDirectory}description.txt", 'a', encoding="utf-8") as dfile: 131 | dfile.write(description_meta) 132 | 133 | print("[i] Total Length: " + str(duration)) 134 | 135 | # Create videos 136 | for clip in downVideos: 137 | #duration += clip.duration 138 | videos.append(clip) 139 | 140 | if duration >= totalVidLength: 141 | # Just make one video 142 | break 143 | 144 | # Add outro vid 145 | if outroName != '': 146 | outroVid = VideoFileClip("./" + outroName) 147 | videos.append(outroVid) 148 | 149 | # Used Moviepy 150 | finalClip = concatenate_videoclips(videos, method="compose") 151 | 152 | audio_path = "/tmp/temoaudiofile.m4a" 153 | 154 | # Create compilation 155 | finalClip.write_videofile(outputFile, threads=8, temp_audiofile=audio_path, remove_temp=True, codec="libx264", audio_codec="aac") 156 | def watrmrk(): 157 | print("[i] Adding Watermark") 158 | os.rename(outputFile, f"{outputFile}.tmp") 159 | ff.options(f"-i {outputFile}.tmp -i {wmark} -filter_complex overlay=1500:10 {outputFile}") 160 | os.remove(f"{outputFile}.tmp") 161 | print("[i] Watermark added") 162 | if modeAM == "M": 163 | add_waterMark = input(f"[Q] Do you want to add watermark {wmark}(Y/n):").strip() 164 | if add_waterMark.lower() == "n": 165 | print("[i] No watermark added") 166 | else: 167 | watrmrk() 168 | else: 169 | watrmrk() 170 | 171 | 172 | if __name__ == "__main__": 173 | makeCompilation(path = "/home/kali/Documents/YOUTUBE/AutomatedChannel/Videos/Memes/", 174 | introName = "intro_vid.mp4", 175 | outroName = 'outro.mp4', 176 | wmark = 'BotTuber.png', 177 | totalVidLength = 10*60, 178 | maxClipLength = 20, 179 | outputFile = "outputseq.mp4", 180 | video_source_meta = {}, 181 | videoDirectory = "", 182 | description_meta = "", 183 | modeAM = "A") 184 | -------------------------------------------------------------------------------- /outro.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sam5epi0l/BotTuber/098d3c74bd610f39c6e53c663bcd8e395cb3ecb4/outro.mp4 -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | google_api_python_client==2.36.0 2 | google_auth_oauthlib==0.4.6 3 | instaloader==4.5.5 4 | instalooter==2.4.4 5 | moviepy==1.0.3 6 | protobuf==3.14.0 7 | python_dateutil==2.8.1 8 | schedule==0.6.0 9 | pyffmpeg==2.1.0 10 | -------------------------------------------------------------------------------- /scrape_videos.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import dateutil.relativedelta 3 | from instalooter.looters import InstaLooter, ProfileLooter 4 | import instaloader 5 | from instalooter.cli.login import login 6 | 7 | # scrape_videos.py scrapes all the videos from pages we are following 8 | def scrapeVideos(username = "", 9 | password = "", 10 | output_folder = "", 11 | modeAM = "", 12 | days = 1): 13 | 14 | print("Starting Scraping") 15 | 16 | L = instaloader.Instaloader() 17 | 18 | # Login or load session for loader 19 | L.login(username, password) 20 | profile = instaloader.Profile.from_username(L.context, username) 21 | following = profile.get_followees() 22 | print(following) 23 | 24 | today = datetime.date.today() 25 | timeframe = (today, today - dateutil.relativedelta.relativedelta(days=days)) 26 | 27 | for profile in following: 28 | acc = profile.username 29 | # Scrap videos from this account or not 30 | 31 | def scrape_videos_fn(): 32 | looter = ProfileLooter(acc, videos_only=True, dump_json=True, template="{id}-{username}-{width}-{height}") 33 | if not looter.logged_in(): 34 | looter.login(username, password) 35 | print("[+]Scraping From Account: " + acc) 36 | 37 | try: 38 | # videos downloaded 39 | numDowloaded = looter.download(output_folder, media_count=30, timeframe=timeframe) 40 | print("[+]Downloaded " + str(numDowloaded) + " videos successfully") 41 | print("") 42 | 43 | except Exception as e: 44 | # error Occcured 45 | print("[+]Skipped acc " + acc + "because of") 46 | print(e) 47 | 48 | if modeAM == "M": 49 | scrap_or_Skip_video = input(f"[Q]Do you want to scrape from {acc}'s profile?(Y/n/q):").strip().lower() 50 | if scrap_or_Skip_video == "n": 51 | print("[+]Skiped Scraping") 52 | continue 53 | if scrap_or_Skip_video == "q": 54 | print("[+]Exit Scrapping Loop") 55 | break 56 | else: 57 | scrape_videos_fn() 58 | elif modeAM == "A": 59 | scrape_videos_fn() 60 | 61 | 62 | if __name__ == "__main__": 63 | scrapeVideos(username = "chewymemes_v3", 64 | password = "", 65 | modeAM = "A", 66 | output_folder = "./Memes_December_4") 67 | -------------------------------------------------------------------------------- /setup_google.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from Google import Create_Service 3 | from googleapiclient.http import MediaFileUpload 4 | 5 | # Leave Unchanged 6 | CLIENT_SECRET_FILE = 'secret.json' 7 | API_NAME = 'youtube' 8 | API_VERSION = 'v3' 9 | SCOPES = ['https://www.googleapis.com/auth/youtube.upload'] 10 | 11 | # setup_google.py allows user to log into 12 | service = Create_Service(CLIENT_SECRET_FILE, API_NAME, API_VERSION, SCOPES) 13 | -------------------------------------------------------------------------------- /upload_ytvid.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from googleapiclient.http import MediaFileUpload 3 | 4 | def uploadYtvid(VIDEO_FILE_NAME='', 5 | title='Intro Video!', 6 | description=':) ', 7 | tags=[], 8 | modeAM = "A", 9 | googleAPI=None): 10 | 11 | now = datetime.datetime.now() 12 | upload_date_time = datetime.datetime(now.year, now.month, now.day, now.hour, now.minute, int(now.second)).isoformat() + '.000Z' 13 | 14 | request_body = { 15 | 'snippet': { 16 | 'categoryId': 23, 17 | 'title': title, 18 | 'description': description, 19 | 'tags': tags 20 | }, 21 | 'status': { 22 | 'privacyStatus': 'public', 23 | 'selfDeclaredMadeForKids': False, 24 | }, 25 | 'notifySubscribers': False 26 | } 27 | 28 | mediaFile = MediaFileUpload(VIDEO_FILE_NAME, chunksize=-1, resumable=True) 29 | 30 | response_upload = googleAPI.videos().insert( 31 | part='snippet,status', 32 | body=request_body, 33 | media_body=mediaFile 34 | ).execute() 35 | 36 | def thumbnail_upload(): 37 | googleAPI.thumbnails().set( 38 | videoId=response_upload.get('id'), 39 | media_body=MediaFileUpload('thumbnail.png') 40 | ).execute() 41 | 42 | if modeAM == "A": 43 | thumbnail_upload() 44 | elif modeAM == "M": 45 | wanna_thum = input("[Q] Do you wanna upload thumbnail.png?(y/N):").strip() 46 | if wanna_thum.lower() == "y": 47 | thumbnail_upload() 48 | else: 49 | print("[i] No Custom thumbnail uploaded.") 50 | 51 | print("Upload Successful!") 52 | 53 | if __name__ == "__main__": 54 | uploadYtvid(VIDEO_FILE_NAME='./intro_vid.mp4') 55 | --------------------------------------------------------------------------------