├── .idea └── inspectionProfiles │ └── profiles_settings.xml ├── Params.py ├── README.md ├── __init__.py ├── download ├── AutoDownLoader.py ├── DownloadThread.py ├── TranslateThread.py ├── Translator.py ├── __init__.py ├── douyin.py ├── douyin_v2.py ├── tiktok.py ├── xigua.js ├── xigua.py └── youtube.py ├── editor ├── AutoEditor.py ├── EditorThread.py ├── MergeEditor.py ├── SingleEditor.py ├── VideoProcess.py └── __init__.py ├── main.py ├── requirements.txt ├── resource ├── __init__.py ├── double_page.py └── word_dict.txt ├── ui ├── AccountDialog.py ├── ComboxDialog.py ├── LoadingDialog.py ├── TextCellDialog.py ├── __init__.py ├── add_account.py ├── add_account.ui ├── combox_cell_change.py ├── combox_cell_change.ui ├── double_page.py ├── double_page.ui ├── loading.py ├── loading.ui ├── loading_cat.gif ├── running.gif ├── shortVideo.ui ├── text_cell_change.py ├── text_cell_change.ui └── working.gif └── upload ├── AdsChrome.py ├── AutoUpload.py ├── ChromeDriver.py ├── Constant.py ├── HubChromeDriver.py ├── TikTokUploader.py ├── YouTubeUploader.py ├── YouTubeUploaderLong.py ├── YouTubeUploaderShort.py ├── __init__.py └── conf.json /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /Params.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Params: 4 | download_keys = ["save_path", "home_page_url", "translate_to_english", "web"] 5 | 6 | editor_common_keys = ["background_music", "water_logo", "background_pic", "background_pic_rate", "volume", "is_music_covered"] 7 | 8 | single_editor_keys = ["single_ready_videos", "single_save_path", "input_start_x", "input_start_y", "input_end_x", "input_end_y", "front_cut_dur", "end_cut_dur"] 9 | 10 | merge_editor_keys = ["merge_ready_videos", "merge_save_path", "merge_type", "total_duration", "merge_video_duration", "merge_top10_path", "material_num"] 11 | 12 | upload_keys = ["add_finished"] 13 | 14 | account_info = ["account", "web", "title", "description", "caption", "title_tags", "tags", "video_path", "video_type", "use_file_title", "finger_web"] 15 | 16 | bat_info = ["account", "web", "meta", "video_path", "video_type", "use_file_title", "finger_web"] 17 | 18 | file_name_black_char = ['"', '*', '<', '>', '?', '\\', '|', '/', ':', ':'] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # homework 2 | 3 | 该项目是用脚本实现部分视频网站视频内容的自动化下载、剪辑以及上传,其中界面是用PyQT做的。 4 | 5 | 使用的浏览器驱动是undetected_chromedriver,可以跳过tiktok的机器人检查 6 | 使用的浏览器是91,版本:Google_Chrome_(64bit)_v91.0.4472.77 7 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlamingJay/homework/168f205345857c56f285ad6a77dadaef64337897/__init__.py -------------------------------------------------------------------------------- /download/AutoDownLoader.py: -------------------------------------------------------------------------------- 1 | from abc import abstractmethod, ABCMeta 2 | 3 | class AutoDownLoader(metaclass=ABCMeta): 4 | @abstractmethod 5 | def parseUrl(self, home_page_url): 6 | pass 7 | 8 | @abstractmethod 9 | def download(self, video_url, video_name, save_path): 10 | pass -------------------------------------------------------------------------------- /download/DownloadThread.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import QThread, pyqtSignal 2 | 3 | from download.douyin_v2 import DouyinDownloader 4 | from download.youtube import YoutubeDownloader 5 | from download.xigua import XiguaDownloader 6 | import os 7 | 8 | 9 | class DownloadThread(QThread): 10 | # 通过类成员对象定义信号对象 11 | _download_signal = pyqtSignal(int) 12 | 13 | def __init__(self, web, home_page_url, save_path): 14 | super(DownloadThread, self).__init__() 15 | self.website = web 16 | self.autoloader = self.__create_downloader__() 17 | self.target_link = home_page_url 18 | self.save_path = save_path 19 | 20 | def __create_downloader__(self): 21 | web = self.website 22 | if web == "douyin": 23 | # 创建下载器 24 | return DouyinDownloader() 25 | elif web == 'youtube': 26 | return YoutubeDownloader() 27 | elif web == 'xigua': 28 | return XiguaDownloader() 29 | else: 30 | return None 31 | 32 | def __del__(self): 33 | self.wait() 34 | 35 | def run(self): 36 | ''' 37 | 创建下载对象 38 | :return: 39 | ''' 40 | if self.target_link is None: 41 | return 42 | 43 | # 解析 44 | self._download_signal.emit(-1) 45 | nickname, parsed_urls_names = self.autoloader.parseUrl(self.target_link) 46 | self._download_signal.emit(-2) 47 | 48 | video_count = len(parsed_urls_names) 49 | # 保存路径 50 | nickname_dir = os.path.join(self.save_path, nickname) 51 | if not os.path.exists(nickname_dir): 52 | os.makedirs(nickname_dir) 53 | # 下载 54 | for i in range(video_count): 55 | item = parsed_urls_names[i] 56 | video_url = item[0] 57 | video_name = item[1] 58 | self.autoloader.download(video_url, video_name, nickname_dir) 59 | self._download_signal.emit(int((i+1) / video_count * 100)) -------------------------------------------------------------------------------- /download/TranslateThread.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import QThread, pyqtSignal 2 | import os 3 | import re 4 | import time 5 | from download.Translator import baiduAPI_translate 6 | 7 | 8 | class TranslateThread(QThread): 9 | _translate_signal = pyqtSignal(int) 10 | 11 | def __init__(self, file_path): 12 | super(TranslateThread, self).__init__() 13 | self.file_path = file_path 14 | 15 | def __del__(self): 16 | self.wait() 17 | 18 | def run(self): 19 | if self.file_path == "": 20 | return 21 | 22 | file_path = self.file_path 23 | files = os.listdir(file_path) 24 | count = len(files) 25 | for i, file in enumerate(files): 26 | try: 27 | time.sleep(1) 28 | res = baiduAPI_translate(file[:-4].replace(" ", ""), "zh", "en") 29 | new_file = re.sub('[^\u4e00-\u9fa5^a-z^A-Z^0-9\.\_ ]', '', res) 30 | new_file = new_file[:min(len(new_file), 100)] 31 | os.rename(file_path + os.sep + file, file_path + os.sep + new_file + ".mp4") 32 | self._translate_signal.emit(int((i+1) / count * 100)) 33 | except Exception: 34 | print("error translate") 35 | finally: 36 | pass -------------------------------------------------------------------------------- /download/Translator.py: -------------------------------------------------------------------------------- 1 | import re 2 | import hashlib 3 | import random 4 | import requests 5 | from importlib import resources 6 | 7 | 8 | def word_replace(text, word_dict): 9 | text = re.sub('[^\u4e00-\u9fa5^a-z^A-Z^0-9\.\_ ]', '', text) 10 | for key in word_dict.keys(): 11 | if key in text: 12 | text.replace(key, word_dict[key]) 13 | 14 | return text 15 | 16 | 17 | def load_word_dict(): 18 | word_dict = dict() 19 | with resources.open_text("resource", "word_dict.txt") as f: 20 | for line in f.readlines(): 21 | token = line.split("|") 22 | word_dict[token[0]] = token[1] 23 | return word_dict 24 | 25 | 26 | def baiduAPI_translate(query_str, from_lang, to_lang): 27 | ''' 28 | 传入待翻译的字符串和目标语言类型,请求 apiURL,自动检测传入的语言类型获得翻译结果 29 | :param query_str: 待翻译的字符串 30 | :param from_lang: 当前语言类型 31 | :param to_lang: 目标语言类型 32 | :return: 翻译结果字典 33 | ''' 34 | apiURL = 'http://api.fanyi.baidu.com/api/trans/vip/translate' 35 | appID = '20220621001253515' 36 | secretKey = 'TbZ9GHaoAsbKcZDLz8oT' 37 | # 生成随机的 salt 值 38 | salt = str(random.randint(32768, 65536)) 39 | # 准备计算 sign 值需要的字符串 40 | pre_sign = appID + query_str + salt + secretKey 41 | # 计算 md5 生成 sign 42 | sign = hashlib.md5(pre_sign.encode()).hexdigest() 43 | # 请求 apiURL 所有需要的参数 44 | params = { 45 | 'q': query_str, 46 | 'from': from_lang, 47 | 'to': to_lang, 48 | 'appid': appID, 49 | 'salt': salt, 50 | 'sign': sign 51 | } 52 | try: 53 | # 直接将 params 和 apiURL 一起传入 requests.get() 函数 54 | response = requests.get(apiURL, params=params) 55 | # 获取返回的 json 数据 56 | result_dict = response.json() 57 | # 得到的结果正常则 return 58 | if 'trans_result' in result_dict: 59 | res = result_dict['trans_result'][0]['dst'] 60 | # 过滤 61 | res = re.sub(r'[\\/:*?<>?]', r'../qt', res)[0: min(len(res), 250)] 62 | return res 63 | else: 64 | return query_str 65 | except Exception as e: 66 | return query_str 67 | -------------------------------------------------------------------------------- /download/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlamingJay/homework/168f205345857c56f285ad6a77dadaef64337897/download/__init__.py -------------------------------------------------------------------------------- /download/douyin.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import time 4 | 5 | import requests 6 | from abc import ABC 7 | from contextlib import closing 8 | 9 | from download.AutoDownLoader import AutoDownLoader 10 | from download import Translator 11 | 12 | 13 | class DouyinDownloader(AutoDownLoader, ABC): 14 | def __init__(self): 15 | self.headers = { 16 | 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 17 | 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8', 18 | 'pragma': 'no-cache', 19 | 'cache-control': 'no-cache', 20 | 'upgrade-insecure-requests': '1', 21 | 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1', 22 | } 23 | 24 | def parseUrl(self, home_page_url, translate_to_english): 25 | ''' 26 | 解析主页链接,返回所有的视频链接列表 27 | :param home_page_url: 28 | :return: 29 | ''' 30 | 31 | parsed_urls_names = [] 32 | 33 | requests.packages.urllib3.disable_warnings() 34 | session = requests.session() 35 | res = session.get(url=home_page_url, headers=self.headers, verify=False) 36 | sec_uid = re.findall(r'sec_uid=(\w+-\w+-\w+|\w+-\w+|\w+)', res.url) 37 | # 获取视频数量总数 用户名 38 | sum_url = 'https://www.iesdouyin.com/web/api/v2/user/info/?sec_uid={0}'.format(sec_uid[0]) 39 | se = session.get(sum_url) 40 | sm_count = re.findall('"aweme_count":(\w+)', se.text) 41 | # 用户名 42 | nickname = re.findall('"nickname":"(.*?)"', se.text) 43 | # Translator配置 44 | word_dict = Translator.load_word_dict() 45 | 46 | # 页面 47 | max_cursor = 0 48 | index = 0 49 | while True: 50 | while True: 51 | if max_cursor == 0: 52 | sec_id_url = "https://www.iesdouyin.com/web/api/v2/aweme/post/?sec_uid={0}&count=21&max_cursor=0&aid=1128&_signature=dF8skQAAK0iTKNSXi9av.XRfLI&dytk=".format( 53 | sec_uid[0]) 54 | else: 55 | sec_id_url = "https://www.iesdouyin.com/web/api/v2/aweme/post/?sec_uid={0}&count=21&max_cursor={1}&aid=1128&_signature=dF8skQAAK0iTKNSXi9av.XRfLI&dytk=".format( 56 | sec_uid[0], max_cursor) 57 | try: 58 | sec_respone = session.get(url=sec_id_url, headers=self.headers, verify=False) 59 | comment = sec_respone.json() 60 | except: 61 | break 62 | 63 | if len(comment['aweme_list']) == 0: 64 | continue 65 | else: 66 | break 67 | # 下一页下标 68 | max_cursor = comment['max_cursor'] 69 | for s in comment['aweme_list']: 70 | index += 1 71 | 72 | if 'video' in s.keys(): 73 | if 'play_addr_lowbr' in s['video'].keys(): 74 | if 'url_list' in s['video']['play_addr_lowbr'].keys(): 75 | # 无水印视频链接地址 76 | video_url = s['video']['play_addr_lowbr']['url_list'][0] 77 | # 视频名称 78 | video_name = re.sub(r'[\/:*?"<>|\n]', '', s['desc']) 79 | if translate_to_english: 80 | try: 81 | time.sleep(1) 82 | video_name = Translator.word_replace(video_name, word_dict) 83 | video_name = Translator.baiduAPI_translate(query_str=video_name, from_lang='zh', 84 | to_lang='en') 85 | 86 | except: 87 | video_name = video_name 88 | parsed_urls_names.append([video_url, video_name]) 89 | 90 | if int(index) >= int(sm_count[0]): 91 | break 92 | 93 | return nickname[0], parsed_urls_names 94 | 95 | def download(self, video_url, video_name, save_path): 96 | ''' 97 | 下载单个视频 98 | :param video_name: 99 | :param video_url: 100 | :param save_path: 101 | :param translate_to_english: 102 | :return: 103 | ''' 104 | 105 | size = 0 106 | with closing(requests.get(video_url, headers=self.headers, stream=True)) as response: 107 | chunk_size = 1024 108 | if response.status_code == 200: 109 | while os.path.exists(video_name + '.mp4'): 110 | video_name += "_" 111 | 112 | video_name = video_name + '.mp4' 113 | file_path = save_path + os.sep + video_name 114 | with open(file_path, 'wb') as file: 115 | for data in response.iter_content(chunk_size=chunk_size): 116 | file.write(data) 117 | size += len(data) 118 | file.flush() -------------------------------------------------------------------------------- /download/douyin_v2.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import json 4 | import time 5 | 6 | import requests 7 | from abc import ABC 8 | from contextlib import closing 9 | from urllib import parse 10 | 11 | from download.AutoDownLoader import AutoDownLoader 12 | 13 | 14 | class DouyinDownloader(AutoDownLoader, ABC): 15 | def __init__(self): 16 | self.headers = { 17 | 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 18 | 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8', 19 | 'pragma': 'no-cache', 20 | 'cache-control': 'no-cache', 21 | 'upgrade-insecure-requests': '1', 22 | 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1', 23 | } 24 | 25 | def parseUrl(self, home_page_url): 26 | ''' 27 | 解析主页链接,返回所有的视频链接列表 28 | :param home_page_url: 29 | :return: 30 | ''' 31 | 32 | parsed_urls_names = [] 33 | 34 | requests.packages.urllib3.disable_warnings() 35 | session = requests.session() 36 | res = session.get(url=home_page_url, headers=self.headers, verify=False) 37 | sec_uid = re.findall(r'sec_uid=(\w+-\w+-\w+|\w+-\w+|\w+)', res.url) 38 | # 获取视频数量总数 用户名 39 | sum_url = 'https://www.iesdouyin.com/web/api/v2/user/info/?sec_uid={0}'.format(sec_uid[0]) 40 | se = session.get(sum_url) 41 | # 用户名 42 | nicknames = re.findall('"nickname":"(.*?)"', se.text) 43 | 44 | # 标题正则修改 45 | rstr = r"[\/\\\:;\*#¥%$!@^……&()\?\"\<\>\|\n\t]" 46 | 47 | try: 48 | url = "https://www.iesdouyin.com/web/api/v2/aweme/post/?sec_uid={0}&count=21&max_cursor=0&aid=1128&_signature=R6Ub1QAAJ-gQklOOeJfpTEelG8&dytk=".format( 49 | sec_uid[0]) 50 | r = requests.get(url=url, headers=self.headers, stream=True) 51 | 52 | # todo:返回码判断 53 | data_json = json.loads(r.text) 54 | has_more = data_json['has_more'] 55 | max_cursor = data_json['max_cursor'] 56 | for i in range(len(data_json['aweme_list'])): 57 | video_url_json = data_json['aweme_list'][i]['video'] 58 | target_key = "" 59 | if 'play_addr_lowbr' in video_url_json.keys(): 60 | target_key = 'play_addr_lowbr' 61 | elif 'play_addr' in video_url_json.keys(): 62 | target_key = 'play_addr' 63 | else: 64 | continue 65 | video_url = data_json['aweme_list'][i]['video'][target_key]['url_list'][0] 66 | 67 | video_name = re.sub(rstr, '', data_json['aweme_list'][i]['desc']) 68 | 69 | parsed_urls_names.append([video_url, video_name]) 70 | while has_more == True: 71 | url_parsed = parse.urlparse(url) 72 | bits = list(url_parsed) 73 | qs = parse.parse_qs(bits[4]) 74 | qs['max_cursor'] = max_cursor 75 | bits[4] = parse.urlencode(qs, True) 76 | 77 | # 重新拼接整个url,只要hasmore是否为true,则反复访问作者主页链接,直到成功返回这个为false 78 | url_new = parse.urlunparse(bits) 79 | r = requests.get(url=url_new, headers=self.headers, stream=True) 80 | data_json = json.loads(r.text) 81 | has_more = data_json['has_more'] # 重置hasmore直到返回为false则退出循环 82 | max_cursor = data_json['max_cursor'] # 每次重置这个页数,继续替换url中下一页页码进行访问 83 | for i in range(len(data_json['aweme_list'])): 84 | video_url_json = data_json['aweme_list'][i]['video'] 85 | target_key = "" 86 | if 'play_addr_lowbr' in video_url_json.keys(): 87 | target_key = 'play_addr_lowbr' 88 | elif 'play_addr' in video_url_json.keys(): 89 | target_key = 'play_addr' 90 | else: 91 | continue 92 | video_url = data_json['aweme_list'][i]['video'][target_key]['url_list'][0] 93 | video_name = re.sub(rstr, '', data_json['aweme_list'][i]['desc']) 94 | 95 | parsed_urls_names.append([video_url, video_name]) 96 | except: 97 | pass 98 | 99 | nickname = str(time.time()) 100 | if len(nicknames) != 0: 101 | nickname = nicknames[0] 102 | 103 | 104 | return nickname, parsed_urls_names 105 | 106 | def download(self, video_url, video_name, save_path): 107 | ''' 108 | 下载单个视频 109 | :param video_name: 110 | :param video_url: 111 | :param save_path: 112 | :param translate_to_english: 113 | :return: 114 | ''' 115 | 116 | with closing(requests.get(video_url, headers=self.headers, stream=True)) as response: 117 | chunk_size = 102400 118 | video_name = video_name[:min(len(video_name), 100)] 119 | if response.status_code == 200: 120 | i = 0 121 | while os.path.exists(save_path + os.sep + video_name + '.mp4'): 122 | video_name = video_name.replace("_" + str(i), "") 123 | i += 1 124 | video_name += ("_" + str(i)) 125 | 126 | file_path = save_path + os.sep + video_name + '.mp4' 127 | with open(file_path, 'wb') as file: 128 | for data in response.iter_content(chunk_size=chunk_size): 129 | file.write(data) 130 | -------------------------------------------------------------------------------- /download/tiktok.py: -------------------------------------------------------------------------------- 1 | from abc import ABC 2 | from argparse import ArgumentParser 3 | import os 4 | from contextlib import closing 5 | from urllib.parse import parse_qsl, urlparse 6 | import requests 7 | 8 | from download.AutoDownLoader import AutoDownLoader 9 | 10 | 11 | class TiktokDownloader(AutoDownLoader, ABC): 12 | def __init__(self): 13 | self.headers = { 14 | 'Connection': 'keep-alive', 15 | 'Pragma': 'no-cache', 16 | 'Cache-Control': 'no-cache', 17 | 'DNT': '1', 18 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36', 19 | 'Accept': '*/*', 20 | 'Sec-Fetch-Site': 'same-site', 21 | 'Sec-Fetch-Mode': 'no-cors', 22 | 'Sec-Fetch-Dest': 'video', 23 | 'Referer': 'https://www.tiktok.com/', 24 | 'Accept-Language': 'en-US,en;q=0.9,bs;q=0.8,sr;q=0.7,hr;q=0.6', 25 | 'sec-gpc': '1', 26 | 'Range': 'bytes=0-', 27 | 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 28 | 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8', 29 | 'cache-control': 'no-cache', 30 | 'upgrade-insecure-requests': '1', 31 | } 32 | 33 | def parseUrl(self, home_page_url): 34 | response = requests.get(home_page_url, headers=self.headers) 35 | urls = response.text.split('"playAddr":"')[1].split('"')[0].replace(r'\u0026', '&') 36 | return urls 37 | 38 | def download(self, video_url, video_name, save_path): 39 | ''' 40 | 下载单个视频 41 | :param video_name: 42 | :param video_url: 43 | :param save_path: 44 | :param translate_to_english: 45 | :return: 46 | ''' 47 | 48 | with closing(requests.get(video_url, headers=self.headers, stream=True)) as response: 49 | chunk_size = 102400 50 | video_name = video_name[:min(len(video_name), 100)] 51 | if response.status_code == 200: 52 | i = 0 53 | while os.path.exists(save_path + os.sep + video_name + '.mp4'): 54 | video_name = video_name.replace("_" + str(i), "") 55 | i += 1 56 | video_name += ("_" + str(i)) 57 | 58 | file_path = save_path + os.sep + video_name + '.mp4' 59 | with open(file_path, 'wb') as file: 60 | for data in response.iter_content(chunk_size=chunk_size): 61 | file.write(data) 62 | 63 | 64 | if __name__ == "__main__": 65 | url = 'https://www.tiktok.com/@andcarli/video/7136971648669895941?is_from_webapp=1&sender_device=pc&web_id=7136917778469045762' 66 | down = TiktokDownloader() 67 | xx = down.parseUrl(url) 68 | down.download(xx, "aa", "E:/") 69 | -------------------------------------------------------------------------------- /download/xigua.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import math 3 | import os 4 | import re 5 | import json 6 | from abc import ABC 7 | from contextlib import closing 8 | 9 | import requests 10 | import execjs 11 | from download.AutoDownLoader import AutoDownLoader 12 | 13 | 14 | class XiguaDownloader(AutoDownLoader, ABC): 15 | def __init__(self): 16 | self.headers = { 17 | 'cookie': 'MONITOR_WEB_ID=7150929952647857694; _tea_utm_cache_1300=undefined; ttcid=9139c6381f264f9a86cc469db71f199175; tt_scid=4CzxxBHxx1AS02YjYeBt02rYO-ND2OakT3radlgp0hWaCoexwq21s0IZG33gjiqbf15e; ttwid=1|kVHm9rjcb53HgfkrmPeje_kkgLE6eOnYRBXy5glhBVA|1665033558|5ec3f1ac64e101e0691fa45db8970d6fe745a507bc503c7c00fdc60c801bf70d; ixigua-a-s=1; support_webp=true; support_avif=true; msToken=AnKS6IXLYfOLuSzU2Ybe284UDs7A5ajpoJbPLgUPWSJ0rK_giD5a-zPDZMCsgCfB7sbgHGN9Aw-ZIjvUVd4hJYDnn4uss9eCEd5t40Zo8fIxw1qsH-KdVbqBKHaIEQ==; __ac_nonce=0633fe01c009f6114a905; __ac_signature=_02B4Z6wo00f0110WMLwAAIDA7efcdOXABpddNjQAALSFZOdX4FOlsjRQXj1O.RlxQhDlP0KodZ2TWD50YUSs02uWj.Nb4iAemr.M8ZFKWXbND2yJ810BpXynPCtpl-5gi.2OeKmVnx7GGTshcd; __ac_referer=__ac_blank', 18 | 'referer': 'https://www.ixigua.com/', 19 | 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36' 20 | } 21 | 22 | def parseUrl(self, home_page_url): 23 | home_resp = requests.get(home_page_url, headers=self.headers) 24 | home_resp.encoding = 'utf-8' 25 | home_html = home_resp.text 26 | 27 | # 获取主页视频总数量,主页昵称 28 | video_total_count = re.findall('"video_total_count":"(.*?)"', home_html)[0] 29 | 30 | nickname = re.findall('(.*)的个人主页 - 西瓜视频', home_html)[0] 31 | user_id = home_page_url.split("/")[4] 32 | 33 | # 反爬出签名 34 | path = os.path.join(os.path.dirname(__file__), "xigua.js") 35 | jscode = execjs.compile(open(path).read()) 36 | 37 | epoch = int(math.floor(int(video_total_count) / 30)) 38 | group_ids = [] 39 | nonce = home_resp.cookies.get("__ac_nonce") 40 | ctx = jscode.call("getSign", nonce, home_page_url) 41 | signature = ctx 42 | for i in range(epoch): 43 | # 真正的存储视频的地址 44 | url = 'https://www.ixigua.com/api/videov2/author/new_video_list?to_user_id={0}&offset={1}&limit=30&_signature={2}'\ 45 | .format(user_id, i * 30, signature) 46 | video_html = requests.get(url, headers=self.headers) 47 | video_html.encoding = 'utf-8' 48 | json_video = video_html.text 49 | json_video_data = json.loads(json_video) 50 | video_list = json_video_data['data']['videoList'] 51 | 52 | # 得到所有视频的id 53 | group_ids += [item['group_id'] for item in video_list] 54 | 55 | # 真正的存储视频的地址 56 | url = 'https://www.ixigua.com/api/videov2/author/new_video_list?to_user_id={0}&offset={1}&limit={2}&_signature={3}' \ 57 | .format(user_id, epoch * 30, int(video_total_count) - epoch * 30, signature) 58 | video_html = requests.get(url, headers=self.headers) 59 | video_html.encoding = 'utf-8' 60 | json_video = video_html.text 61 | json_video_data = json.loads(json_video) 62 | video_list = json_video_data['data']['videoList'] 63 | 64 | # 得到所有视频的id 65 | group_ids += [item['group_id'] for item in video_list] 66 | 67 | parsed_urls_names = [] 68 | # 标题正则修改 69 | rstr = r"[\/\\\:;\*#¥%$!@^……&()\?\"\<\>\|\n\t]" 70 | for id in set(group_ids): 71 | # 视频下载地址(伪装) 72 | url = 'https://www.ixigua.com/{}'.format(id) 73 | 74 | resp = requests.get(url, headers=self.headers) 75 | resp.encoding = 'utf-8' 76 | 77 | res_html = resp.text 78 | json_str = re.findall('window._SSR_HYDRATED_DATA=(.*?)', res_html)[0] 79 | json_str = json_str.replace('undefined', 'null') 80 | json_data = json.loads(json_str) 81 | video_name = re.sub(rstr, "", json_data['anyVideo']['gidInformation']['packerData']['video']['title']) 82 | video_list = json_data['anyVideo']['gidInformation']['packerData']['video']['videoResource']['normal']['video_list'] 83 | for i in ['5', '4', '3', '2', '1']: 84 | video_n = 'video_' + i 85 | if video_n in video_list.keys(): 86 | main_url = video_list[video_n]['main_url'] 87 | break 88 | 89 | video_url = base64.b64decode(main_url).decode() 90 | print("{0}:{1}".format(video_name, video_url)) 91 | parsed_urls_names.append([video_url, video_name]) 92 | 93 | return nickname, parsed_urls_names 94 | 95 | def download(self, video_url, video_name, save_path): 96 | with closing(requests.get(video_url, headers=self.headers, stream=True)) as response: 97 | chunk_size = 102400 98 | video_name = video_name[:min(len(video_name), 100)] 99 | if response.status_code == 200: 100 | i = 0 101 | while os.path.exists(save_path + os.sep + video_name + '.mp4'): 102 | video_name = video_name.replace("_" + str(i), "") 103 | i += 1 104 | video_name += ("_" + str(i)) 105 | 106 | file_path = save_path + os.sep + video_name + '.mp4' 107 | with open(file_path, 'wb') as file: 108 | for data in response.iter_content(chunk_size=chunk_size): 109 | file.write(data) 110 | -------------------------------------------------------------------------------- /download/youtube.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pytube import YouTube 3 | from abc import ABC 4 | import re 5 | import urllib.request 6 | import urllib.error 7 | 8 | from download.AutoDownLoader import AutoDownLoader 9 | 10 | 11 | class YoutubeDownloader(AutoDownLoader, ABC): 12 | def __init__(self): 13 | pass 14 | 15 | def parseUrl(self, home_page_url): 16 | parsed_urls_names = [] 17 | 18 | # 先对home_page_url解析 19 | try: 20 | yTUBE = urllib.request.urlopen(home_page_url).read() 21 | yTUBE = str(yTUBE, "utf-8") 22 | except: 23 | return 24 | 25 | namePatt = re.compile(r'"channelId":.*?"title":"(.*?)"') 26 | longVideoPatt = re.compile(r'"url":"/watch\?v=(.*?)"') 27 | shortVideoPatt = re.compile(r'"url":"/shorts/(.*?)"') 28 | 29 | long_res = longVideoPatt.findall(yTUBE) 30 | short_res = shortVideoPatt.findall(yTUBE) 31 | name_res = namePatt.findall(yTUBE) 32 | nickname = name_res[0] 33 | 34 | for item in long_res: 35 | parsed_urls_names.append(["https://www.youtube.com/watch?v=" + item, ""]) 36 | for item in short_res: 37 | parsed_urls_names.append(["https://www.youtube.com/shorts/" + item, ""]) 38 | 39 | return nickname, parsed_urls_names 40 | 41 | def download(self, video_url, video_name, save_path): 42 | try: 43 | yt = YouTube(video_url) 44 | except Exception as e: 45 | return 46 | try: 47 | video = yt.streams.filter(progressive=True, file_extension="mp4").first() 48 | except Exception: 49 | video = sorted(yt.filter("mp4"), key=lambda video: int(video.resolution[:-1]), reverse=True)[0] 50 | 51 | try: 52 | video_name = yt.title 53 | 54 | # 判断重名、长度等 55 | video_name = video_name[:min(len(video_name), 100)] 56 | i = 0 57 | while os.path.exists(save_path + os.sep + video_name + '.mp4'): 58 | video_name = video_name.replace("_" + str(i), "") 59 | i += 1 60 | video_name += ("_" + str(i)) 61 | 62 | video.download(save_path, video_name) 63 | except OSError: 64 | return -------------------------------------------------------------------------------- /editor/AutoEditor.py: -------------------------------------------------------------------------------- 1 | from abc import abstractmethod, ABCMeta 2 | 3 | class AutoEditor(metaclass=ABCMeta): 4 | @abstractmethod 5 | def videos_edit(self, video_list): 6 | pass -------------------------------------------------------------------------------- /editor/EditorThread.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtCore import QThread, pyqtSignal 2 | 3 | from ui.LoadingDialog import LoadingDialog 4 | from editor.MergeEditor import MergeEditor 5 | from editor.SingleEditor import SingleEditor 6 | 7 | 8 | class EditorThread(QThread): 9 | _signal = pyqtSignal(int) 10 | 11 | def __init__(self, editor_type, source_videos_list, background_pic, background_pic_rate, background_music, volume, is_covered_music, water_logo, save_path, editor_params_dict): 12 | super(EditorThread, self).__init__() 13 | self.editor_type = editor_type 14 | self.source_videos_list = source_videos_list 15 | self.background_pic = background_pic 16 | self.background_pic_rate = background_pic_rate 17 | self.background_audio = background_music 18 | self.volume = volume 19 | self.original_autio_off = is_covered_music 20 | self.water_logo = water_logo 21 | self.output_path = save_path 22 | self.editor_params_dict = editor_params_dict 23 | 24 | self.editor = self.__create_editor__() 25 | 26 | def __create_editor__(self): 27 | if self.editor_type == "single": 28 | return SingleEditor(self.background_pic, self.background_pic_rate, self.background_audio, self.volume, self.original_autio_off, self.water_logo, self.output_path, 29 | self.editor_params_dict["input_start_x"], self.editor_params_dict["input_start_y"], 30 | self.editor_params_dict["input_end_x"], self.editor_params_dict["input_end_y"], 31 | self.editor_params_dict["front_cut_dur"], self.editor_params_dict["end_cut_dur"]) 32 | elif self.editor_type == "merge": 33 | return MergeEditor(self.background_pic, self.background_pic_rate, self.background_audio, self.volume, self.original_autio_off, self.water_logo, self.output_path) 34 | 35 | def __del__(self): 36 | self.wait() 37 | 38 | def run(self) -> None: 39 | if self.editor_type == "single": 40 | self.editor.videos_edit(self.source_videos_list) 41 | else: 42 | for videos in self.source_videos_list: 43 | self.editor.videos_edit(videos) 44 | 45 | def stop(self): 46 | self.terminate() -------------------------------------------------------------------------------- /editor/MergeEditor.py: -------------------------------------------------------------------------------- 1 | from moviepy.video.io.VideoFileClip import VideoFileClip 2 | from moviepy.editor import concatenate_videoclips, afx, AudioFileClip, ImageClip, CompositeVideoClip, CompositeAudioClip 3 | import datetime 4 | from editor.AutoEditor import AutoEditor 5 | import os 6 | import sys 7 | 8 | class MergeEditor(AutoEditor): 9 | def __init__(self, background_pic=None, background_pic_rate=None, background_music=None, volume=None, is_covered_music=None, water_logo=None, 10 | save_path=None): 11 | super(MergeEditor, self).__init__() 12 | self.background_pic = background_pic 13 | self.background_pic_rate = 1.0 if background_pic_rate is None else float(background_pic_rate) 14 | self.background_audio = background_music 15 | self.volume = 0 if volume is None else int(volume) 16 | self.original_autio_off = False if is_covered_music is None else is_covered_music 17 | self.water_logo = water_logo 18 | self.output_path = save_path 19 | 20 | 21 | def videos_edit(self, video_list): 22 | ''' 23 | 视频合集制作 24 | :param video_list: 要合成的单个视频列表 25 | :return: 26 | ''' 27 | videos = [] 28 | for item in video_list: 29 | videos.append(VideoFileClip(item)) 30 | 31 | all_videos = concatenate_videoclips(videos) 32 | all_videos = all_videos.set_position('center') 33 | 34 | # 背景图 35 | if self.background_pic is not None: 36 | background_clip = ImageClip(self.background_pic) 37 | background_clip = background_clip.set_pos('center').set_duration(all_videos.duration) 38 | back_size = background_clip.size 39 | 40 | if self.water_logo is not None: 41 | water_clip = ImageClip(self.water_logo) 42 | water_clip = water_clip.set_pos('center').set_duration(all_videos.duration) 43 | 44 | if (self.background_pic is not None) and (self.water_logo is not None): 45 | # 视频适配背景 46 | if back_size[0] > back_size[1]: 47 | new_height = 1080 * self.background_pic_rate 48 | new_width = new_height * all_videos.size[0] / all_videos.size[1] 49 | background_clip = background_clip.resize((1920, 1080)) 50 | else: 51 | new_width = 1080 * self.background_pic_rate 52 | new_height = new_width * all_videos.size[1] / all_videos.size[0] 53 | background_clip = background_clip.resize((1080, 1920)) 54 | all_videos = all_videos.resize((new_width, new_height)) 55 | all_videos = all_videos.set_position('center') 56 | # 叠层 57 | all_videos = CompositeVideoClip([background_clip, all_videos, water_clip]) 58 | elif self.background_pic is not None: 59 | if back_size[0] > back_size[1]: 60 | new_height = 1080 * self.background_pic_rate 61 | new_width = new_height * all_videos.size[0] / all_videos.size[1] 62 | background_clip = background_clip.resize((1920, 1080)) 63 | else: 64 | new_width = 1080 * self.background_pic_rate 65 | new_height = new_width * all_videos.size[1] / all_videos.size[0] 66 | background_clip = background_clip.resize((1080, 1920)) 67 | all_videos = all_videos.resize((new_width, new_height)) 68 | all_videos = all_videos.set_position('center') 69 | all_videos = CompositeVideoClip([background_clip, all_videos]) 70 | elif self.water_logo is not None: 71 | all_videos = CompositeVideoClip([all_videos, water_clip]) 72 | 73 | # 背景乐 74 | if self.background_audio is not None: 75 | # 要添加的音频 76 | audio_clip = AudioFileClip(self.background_audio) 77 | audio = afx.audio_loop(audio_clip, duration=all_videos.duration) 78 | audio = audio.volumex(int(self.volume) / 100) 79 | 80 | if self.original_autio_off: 81 | all_videos = all_videos.without_audio() 82 | all_videos = all_videos.set_audio(audio) 83 | else: 84 | # 原来的音频 85 | video_audio_clip = all_videos.audio 86 | all_videos = all_videos.set_audio(CompositeAudioClip([video_audio_clip, audio])) 87 | 88 | # 写入 89 | cur_time = datetime.date.today() 90 | if self.output_path is not None: 91 | save_path = self.output_path + os.sep + str(cur_time) + "_1.mp4" 92 | i = 0 93 | while os.path.exists(save_path): 94 | i += 1 95 | save_path = save_path[:-6] + "_" + str(i) + ".mp4" 96 | all_videos.write_videofile(save_path) 97 | else: 98 | all_videos.write_videofile(os.path.dirname(sys.executable) + os.sep + str(cur_time) + ".mp4") -------------------------------------------------------------------------------- /editor/SingleEditor.py: -------------------------------------------------------------------------------- 1 | from editor.VideoProcess import single_process 2 | from editor.AutoEditor import AutoEditor 3 | import multiprocessing 4 | 5 | 6 | class SingleEditor(AutoEditor): 7 | def __init__(self, background_pic=None, background_pic_rate=None, background_music=None, volume=None, is_covered_music=None, water_logo=None, 8 | save_path=None, crop_x_start=0, crop_y_start=0, crop_x_end=0, crop_y_end=0, front_cut_dur=0, end_cut_dur=0): 9 | super(SingleEditor, self).__init__() 10 | self.background_pic = background_pic 11 | self.background_pic_rate = 1.0 if background_pic_rate is None else float(background_pic_rate) 12 | self.background_audio = background_music 13 | self.volume = 0 if volume is None else int(volume) 14 | self.original_autio_off = False if is_covered_music is None else is_covered_music 15 | self.water_logo = water_logo 16 | self.output_path = save_path 17 | self.crop_x_start = 0 if crop_x_start is None else int(crop_x_start) 18 | self.crop_y_start = 0 if crop_y_start is None else int(crop_y_start) 19 | self.crop_x_end = 0 if crop_x_end is None else int(crop_x_end) 20 | self.crop_y_end = 0 if crop_y_end is None else int(crop_y_end) 21 | self.front_cut_dur = 0 if front_cut_dur is None else int(front_cut_dur) 22 | self.end_cut_dur = 0 if end_cut_dur is None else int(end_cut_dur) 23 | 24 | def videos_edit(self, video_list): 25 | ''' 26 | :param video_list: 最终要进行处理的视频 27 | :return: 28 | ''' 29 | videos = [] 30 | names = [] 31 | 32 | if video_list is None or len(video_list) < 1: 33 | return 34 | for file in video_list: 35 | videos.append(file) 36 | names.append(file.split("/")[-1]) 37 | 38 | count = len(videos) 39 | muisic = [self.background_audio] * count 40 | pic = [self.background_pic] * count 41 | pic_rate = [self.background_pic_rate] * count 42 | save_path = [self.output_path] * count 43 | crop_x_start = [self.crop_x_start] * count 44 | crop_y_start = [self.crop_y_start] * count 45 | crop_x_end = [self.crop_x_end] * count 46 | crop_y_end = [self.crop_y_end] * count 47 | water_logo = [self.water_logo] * count 48 | front_cut = [self.front_cut_dur] * count 49 | end_cut = [self.end_cut_dur] * count 50 | 51 | if self.volume is not None: 52 | volume = [int(self.volume)] * count 53 | else: 54 | volume = [0] * count 55 | 56 | audio_off = [self.original_autio_off] * count 57 | 58 | # 对每一个视频都做同样的操作 59 | with multiprocessing.Pool(multiprocessing.cpu_count()) as pool: 60 | pool.map(single_process, 61 | zip(videos, names, muisic, volume, audio_off, pic, pic_rate, save_path, crop_x_start, crop_y_start, 62 | crop_x_end, crop_y_end, water_logo, front_cut, end_cut)) 63 | -------------------------------------------------------------------------------- /editor/VideoProcess.py: -------------------------------------------------------------------------------- 1 | from moviepy.video.io.VideoFileClip import VideoFileClip 2 | from moviepy.editor import afx, AudioFileClip, ImageClip, CompositeVideoClip, CompositeAudioClip 3 | import moviepy.video.fx.all as vfx 4 | import os, sys 5 | 6 | 7 | def single_process(args): 8 | video_file, name, background_music, volume, audio_off, background_pic, background_pic_rate, \ 9 | save_path, crop_x_start, crop_y_start, crop_x_end, crop_y_end, logo, front, end = args 10 | 11 | video = VideoFileClip(video_file) 12 | video = video.subclip(front, video.duration - end) 13 | 14 | # 进行裁剪 15 | if (crop_x_end - crop_x_start > 0) and (crop_y_end - crop_y_start > 0): 16 | video = (video.fx(vfx.crop, crop_x_start, crop_y_start, crop_x_end, crop_y_end)) 17 | duration = video.duration 18 | video = video.set_position('center') 19 | 20 | # 加背景乐 21 | if background_music is not None: 22 | # 要添加的音频 23 | audio_clip = AudioFileClip(background_music) 24 | audio = afx.audio_loop(audio_clip, duration=duration) 25 | audio = audio.volumex(volume / 100) 26 | 27 | if audio_off: 28 | video = video.without_audio() 29 | video = video.set_audio(audio) 30 | else: 31 | # 原来的音频 32 | video_audio_clip = video.audio 33 | video = video.set_audio(CompositeAudioClip([video_audio_clip, audio])) 34 | 35 | # 加背景图 36 | if background_pic is not None: 37 | background_clip = ImageClip(background_pic) 38 | background_clip = background_clip.set_pos('center').set_duration(duration) 39 | back_size = background_clip.size 40 | 41 | # 加水印 42 | if logo is not None: 43 | water_clip = ImageClip(logo) 44 | # todo: logo的位置 45 | water_clip = water_clip.set_pos('center').set_duration(duration) 46 | 47 | if (background_pic is not None) and (logo is not None): 48 | # 视频适配背景 49 | if back_size[0] > back_size[1]: 50 | new_height = 1080 * background_pic_rate 51 | new_width = new_height * video.size[0] / video.size[1] 52 | background_clip = background_clip.resize((1920, 1080)) 53 | else: 54 | new_width = 1080 * background_pic_rate 55 | new_height = new_width * video.size[1] / video.size[0] 56 | background_clip = background_clip.resize((1080, 1920)) 57 | video = video.resize((new_width, new_height)) 58 | video = video.set_position('center') 59 | # 叠层 60 | video = CompositeVideoClip([background_clip, video, water_clip]) 61 | elif background_pic is not None: 62 | if back_size[0] > back_size[1]: 63 | new_height = 1080 * background_pic_rate 64 | new_width = new_height * video.size[0] / video.size[1] 65 | background_clip = background_clip.resize((1920, 1080)) 66 | else: 67 | new_width = 1080 * background_pic_rate 68 | new_height = new_width * video.size[1] / video.size[0] 69 | background_clip = background_clip.resize((1080, 1920)) 70 | video = video.resize((new_width, new_height)) 71 | video = video.set_position('center') 72 | # 叠层 73 | video = CompositeVideoClip([background_clip, video]) 74 | 75 | elif logo is not None: 76 | video = CompositeVideoClip([video, water_clip]) 77 | 78 | if save_path is not None: 79 | video.write_videofile(save_path + os.sep + name) 80 | else: 81 | video.write_videofile(os.path.dirname(sys.executable) + os.sep + name) 82 | -------------------------------------------------------------------------------- /editor/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlamingJay/homework/168f205345857c56f285ad6a77dadaef64337897/editor/__init__.py -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | moviepy==1.0.3 2 | PyQt5==5.15.7 3 | pytube==12.1.0 4 | pytube3==9.6.4 5 | requests==2.27.1 6 | selenium==4.3.0 7 | tldextract==3.3.0 8 | undetected_chromedriver==3.1.5.post4 9 | -------------------------------------------------------------------------------- /resource/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlamingJay/homework/168f205345857c56f285ad6a77dadaef64337897/resource/__init__.py -------------------------------------------------------------------------------- /resource/double_page.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '.\double_page.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.15.7 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | -------------------------------------------------------------------------------- /resource/word_dict.txt: -------------------------------------------------------------------------------- 1 | 解压|释放压力 -------------------------------------------------------------------------------- /ui/AccountDialog.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QDialog, QMessageBox 2 | from PyQt5.QtCore import pyqtSignal 3 | from ui.add_account import Ui_Dialog 4 | 5 | from Params import Params 6 | 7 | import json 8 | import os 9 | 10 | 11 | class AccountDialog(QDialog, Ui_Dialog): 12 | _end_signal = pyqtSignal(dict) 13 | 14 | def __init__(self, parent=None): 15 | super(AccountDialog, self).__init__(parent) 16 | self.setupUi(self) 17 | self.accounts = self.__load_local_accounts() 18 | self.finished_btn.clicked.connect(self.__finished) 19 | 20 | def __load_local_accounts(self): 21 | ''' 22 | 加载本地的账号文件 23 | :return: 24 | ''' 25 | account_path = os.path.abspath(os.path.join(os.getcwd(), "resource", "account_conf.json")) 26 | if not os.path.exists(account_path): 27 | return dict() 28 | 29 | with open(account_path, mode="r", encoding='utf-8') as meta_json: 30 | # 在table中进行展示 31 | meta_dict = json.load(meta_json) 32 | 33 | return meta_dict 34 | 35 | def __finished(self): 36 | ''' 37 | 添加账号完毕,参数传回主页面,进行更新和保存 38 | :return: 39 | ''' 40 | res = dict() 41 | res["account"] = self.account.text() 42 | res["web"] = self.web.currentText() 43 | res["title"] = self.title.text() 44 | res["caption"] = self.caption.text() 45 | res["description"] = self.description.text() 46 | res["tags"] = self.tags.text() 47 | res["title_tags"] = self.title_tags.text() 48 | res["video_path"] = self.video_path.text() 49 | res["video_type"] = self.type.currentText() 50 | res["use_file_title"] = self.user_file_title.currentText() 51 | 52 | success = True 53 | # 没有account 54 | if "account" not in res.keys() or "web" not in res.keys() or res["account"] == "": 55 | QMessageBox.warning(self, "提示", "需要填写账号&web", QMessageBox.Yes) 56 | success = False 57 | elif any(char in res['account'] and char for char in Params.file_name_black_char): 58 | QMessageBox.warning(self, "提示", "账号名中不要包含特殊字符*|等", QMessageBox.Yes) 59 | success = False 60 | elif len(res["title_tags"]) > 50: 61 | QMessageBox.warning(self, "提示", "youtube标题tags长度超过50") 62 | success = False 63 | elif (len(res["title"]) + len(res["title_tags"]) ) > 100: 64 | QMessageBox.warning(self, "提示", "youtube标题长度超过100, 标题:%d, 标题tags:%d" % (len(res["title"]), len(res["title_tags"]))) 65 | success = False 66 | elif len(res["description"]) > 5000: 67 | QMessageBox.warning(self, "提示", "youtube描述长度超过5000") 68 | success = False 69 | elif len(res["tags"]) > 500: 70 | QMessageBox.warning(self, "提示", "youtube的tags长度超过5000") 71 | success = False 72 | else: 73 | # 不希望被覆盖 74 | if res["account"] in self.accounts.keys(): 75 | is_covered = QMessageBox.warning(self, "提示", "该账号已存在,是否覆盖", QMessageBox.No | QMessageBox.Yes) 76 | if is_covered == QMessageBox.No: 77 | success = False 78 | 79 | if success: 80 | self._end_signal.emit(res) 81 | self.close() 82 | -------------------------------------------------------------------------------- /ui/ComboxDialog.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QDialog 2 | from PyQt5.QtCore import pyqtSignal 3 | from ui.combox_cell_change import Ui_Dialog 4 | 5 | 6 | class ComboxDialog(QDialog, Ui_Dialog): 7 | _combox_cell_signal = pyqtSignal(str) 8 | 9 | def __init__(self, mode=""): 10 | super(ComboxDialog, self).__init__() 11 | self.setupUi(self) 12 | self.__fill_combox(mode) 13 | self.change_finished_btn.clicked.connect(self.__change_finished) 14 | 15 | def __fill_combox(self, mode): 16 | if mode == "web": 17 | self.combox_cell.addItems(['youtube', 'tiktok']) 18 | elif mode == "video_type": 19 | self.combox_cell.addItems(['short', 'long']) 20 | elif mode == "use_file_title": 21 | self.combox_cell.addItems(['true', 'false']) 22 | 23 | def __change_finished(self): 24 | cur_text = self.combox_cell.currentText() 25 | if cur_text == "": 26 | pass 27 | else: 28 | self._combox_cell_signal.emit(cur_text) 29 | self.close() 30 | -------------------------------------------------------------------------------- /ui/LoadingDialog.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QDialog, QLabel, QPushButton 2 | from PyQt5.QtCore import Qt 3 | from PyQt5.QtGui import QMovie 4 | from PyQt5 import QtCore 5 | import os 6 | 7 | 8 | class LoadingDialog(QDialog): 9 | def __init__(self, mode): 10 | super(LoadingDialog, self).__init__() 11 | self.setFixedSize(290, 290) 12 | self.setWindowFlags(Qt.CustomizeWindowHint | Qt.FramelessWindowHint) 13 | self.setAttribute(Qt.WA_TranslucentBackground) 14 | self.setAutoFillBackground(True) 15 | 16 | self.close_btn = QPushButton("关闭提示", self) 17 | self.close_btn.setGeometry(QtCore.QRect(80, 250, 80, 40)) 18 | self.close_btn.setStyleSheet("background-color: rgb(85, 170, 0);") 19 | self.close_btn.clicked.connect(self.__stop_show) 20 | 21 | gif = os.path.abspath(os.path.join(os.getcwd(), 'ui')) 22 | if mode == "loading": 23 | gif = os.path.join(gif, 'loading_cat.gif') 24 | elif mode == "running": 25 | gif = os.path.join(gif, 'loading.gif') 26 | elif mode == "merge_editor": 27 | gif = os.path.join(gif, 'working.gif') 28 | elif mode == "single_editor": 29 | gif = os.path.join(gif, 'working.gif') 30 | self.movie_label = QLabel(self) 31 | self.movie_label.setGeometry(QtCore.QRect(0, 0, 240, 240)) 32 | self.movie_label.setStyleSheet("background:transparent") 33 | self.movie = QMovie(gif) 34 | self.movie_label.setMovie(self.movie) 35 | 36 | self.movie.start() 37 | self.show() 38 | 39 | 40 | 41 | def __stop_show(self): 42 | self.close() -------------------------------------------------------------------------------- /ui/TextCellDialog.py: -------------------------------------------------------------------------------- 1 | from PyQt5.QtWidgets import QDialog 2 | from PyQt5.QtCore import pyqtSignal 3 | from ui.text_cell_change import Ui_Dialog 4 | 5 | 6 | class TableCellDialog(QDialog, Ui_Dialog): 7 | _text_cell_signal = pyqtSignal(str) 8 | 9 | def __init__(self, cell_content=""): 10 | super(TableCellDialog, self).__init__() 11 | self.setupUi(self) 12 | self.cell_content.setPlainText(cell_content) 13 | self.change_finished_btn.clicked.connect(self.__change_finished) 14 | 15 | def __change_finished(self): 16 | cur_text = self.cell_content.toPlainText() 17 | if cur_text == "": 18 | pass 19 | else: 20 | self._text_cell_signal.emit(cur_text) 21 | self.close() 22 | -------------------------------------------------------------------------------- /ui/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlamingJay/homework/168f205345857c56f285ad6a77dadaef64337897/ui/__init__.py -------------------------------------------------------------------------------- /ui/add_account.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '.\add_account.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.9.2 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | class Ui_Dialog(object): 12 | def setupUi(self, Dialog): 13 | Dialog.setObjectName("Dialog") 14 | Dialog.resize(578, 615) 15 | self.groupBox = QtWidgets.QGroupBox(Dialog) 16 | self.groupBox.setGeometry(QtCore.QRect(50, 70, 461, 491)) 17 | self.groupBox.setObjectName("groupBox") 18 | self.label_account = QtWidgets.QLabel(self.groupBox) 19 | self.label_account.setGeometry(QtCore.QRect(20, 40, 101, 21)) 20 | font = QtGui.QFont() 21 | font.setPointSize(10) 22 | self.label_account.setFont(font) 23 | self.label_account.setStyleSheet("") 24 | self.label_account.setAlignment(QtCore.Qt.AlignCenter) 25 | self.label_account.setObjectName("label_account") 26 | self.label_web = QtWidgets.QLabel(self.groupBox) 27 | self.label_web.setGeometry(QtCore.QRect(20, 80, 101, 21)) 28 | font = QtGui.QFont() 29 | font.setPointSize(10) 30 | self.label_web.setFont(font) 31 | self.label_web.setAlignment(QtCore.Qt.AlignCenter) 32 | self.label_web.setObjectName("label_web") 33 | self.label_desc = QtWidgets.QLabel(self.groupBox) 34 | self.label_desc.setGeometry(QtCore.QRect(20, 160, 101, 21)) 35 | font = QtGui.QFont() 36 | font.setPointSize(10) 37 | self.label_desc.setFont(font) 38 | self.label_desc.setAlignment(QtCore.Qt.AlignCenter) 39 | self.label_desc.setObjectName("label_desc") 40 | self.label_caption = QtWidgets.QLabel(self.groupBox) 41 | self.label_caption.setGeometry(QtCore.QRect(20, 200, 101, 21)) 42 | font = QtGui.QFont() 43 | font.setPointSize(10) 44 | self.label_caption.setFont(font) 45 | self.label_caption.setAlignment(QtCore.Qt.AlignCenter) 46 | self.label_caption.setObjectName("label_caption") 47 | self.label_title_tags = QtWidgets.QLabel(self.groupBox) 48 | self.label_title_tags.setGeometry(QtCore.QRect(20, 240, 101, 21)) 49 | font = QtGui.QFont() 50 | font.setPointSize(10) 51 | self.label_title_tags.setFont(font) 52 | self.label_title_tags.setAlignment(QtCore.Qt.AlignCenter) 53 | self.label_title_tags.setObjectName("label_title_tags") 54 | self.label_tags = QtWidgets.QLabel(self.groupBox) 55 | self.label_tags.setGeometry(QtCore.QRect(20, 280, 101, 21)) 56 | font = QtGui.QFont() 57 | font.setPointSize(10) 58 | self.label_tags.setFont(font) 59 | self.label_tags.setAlignment(QtCore.Qt.AlignCenter) 60 | self.label_tags.setObjectName("label_tags") 61 | self.label_title = QtWidgets.QLabel(self.groupBox) 62 | self.label_title.setGeometry(QtCore.QRect(20, 120, 101, 21)) 63 | font = QtGui.QFont() 64 | font.setPointSize(10) 65 | self.label_title.setFont(font) 66 | self.label_title.setAlignment(QtCore.Qt.AlignCenter) 67 | self.label_title.setObjectName("label_title") 68 | self.label_type = QtWidgets.QLabel(self.groupBox) 69 | self.label_type.setGeometry(QtCore.QRect(20, 360, 101, 21)) 70 | font = QtGui.QFont() 71 | font.setPointSize(10) 72 | self.label_type.setFont(font) 73 | self.label_type.setAlignment(QtCore.Qt.AlignCenter) 74 | self.label_type.setObjectName("label_type") 75 | self.account = QtWidgets.QLineEdit(self.groupBox) 76 | self.account.setGeometry(QtCore.QRect(140, 40, 271, 20)) 77 | font = QtGui.QFont() 78 | font.setPointSize(10) 79 | self.account.setFont(font) 80 | self.account.setObjectName("account") 81 | self.web = QtWidgets.QComboBox(self.groupBox) 82 | self.web.setGeometry(QtCore.QRect(140, 80, 271, 22)) 83 | font = QtGui.QFont() 84 | font.setPointSize(10) 85 | self.web.setFont(font) 86 | self.web.setObjectName("web") 87 | self.web.addItem("") 88 | self.web.addItem("") 89 | self.title = QtWidgets.QLineEdit(self.groupBox) 90 | self.title.setGeometry(QtCore.QRect(140, 120, 271, 20)) 91 | font = QtGui.QFont() 92 | font.setPointSize(10) 93 | self.title.setFont(font) 94 | self.title.setObjectName("title") 95 | self.description = QtWidgets.QLineEdit(self.groupBox) 96 | self.description.setGeometry(QtCore.QRect(140, 160, 271, 20)) 97 | font = QtGui.QFont() 98 | font.setPointSize(10) 99 | self.description.setFont(font) 100 | self.description.setObjectName("description") 101 | self.caption = QtWidgets.QLineEdit(self.groupBox) 102 | self.caption.setGeometry(QtCore.QRect(140, 200, 271, 20)) 103 | font = QtGui.QFont() 104 | font.setPointSize(10) 105 | self.caption.setFont(font) 106 | self.caption.setObjectName("caption") 107 | self.title_tags = QtWidgets.QLineEdit(self.groupBox) 108 | self.title_tags.setGeometry(QtCore.QRect(140, 240, 271, 20)) 109 | font = QtGui.QFont() 110 | font.setPointSize(10) 111 | self.title_tags.setFont(font) 112 | self.title_tags.setObjectName("title_tags") 113 | self.tags = QtWidgets.QLineEdit(self.groupBox) 114 | self.tags.setGeometry(QtCore.QRect(140, 280, 271, 20)) 115 | font = QtGui.QFont() 116 | font.setPointSize(10) 117 | self.tags.setFont(font) 118 | self.tags.setObjectName("tags") 119 | self.type = QtWidgets.QComboBox(self.groupBox) 120 | self.type.setGeometry(QtCore.QRect(140, 360, 271, 22)) 121 | font = QtGui.QFont() 122 | font.setPointSize(10) 123 | self.type.setFont(font) 124 | self.type.setObjectName("type") 125 | self.type.addItem("") 126 | self.type.addItem("") 127 | self.finished_btn = QtWidgets.QPushButton(self.groupBox) 128 | self.finished_btn.setGeometry(QtCore.QRect(170, 450, 75, 23)) 129 | font = QtGui.QFont() 130 | font.setPointSize(10) 131 | self.finished_btn.setFont(font) 132 | self.finished_btn.setStyleSheet("background-color: rgb(85, 170, 0);") 133 | self.finished_btn.setObjectName("finished_btn") 134 | self.user_file_title = QtWidgets.QComboBox(self.groupBox) 135 | self.user_file_title.setGeometry(QtCore.QRect(140, 400, 271, 22)) 136 | font = QtGui.QFont() 137 | font.setPointSize(10) 138 | self.user_file_title.setFont(font) 139 | self.user_file_title.setObjectName("user_file_title") 140 | self.user_file_title.addItem("") 141 | self.user_file_title.addItem("") 142 | self.label_user_file_title = QtWidgets.QLabel(self.groupBox) 143 | self.label_user_file_title.setGeometry(QtCore.QRect(20, 400, 101, 21)) 144 | font = QtGui.QFont() 145 | font.setPointSize(10) 146 | self.label_user_file_title.setFont(font) 147 | self.label_user_file_title.setAlignment(QtCore.Qt.AlignCenter) 148 | self.label_user_file_title.setObjectName("label_user_file_title") 149 | self.video_path = QtWidgets.QLineEdit(self.groupBox) 150 | self.video_path.setGeometry(QtCore.QRect(140, 320, 271, 20)) 151 | font = QtGui.QFont() 152 | font.setPointSize(10) 153 | self.video_path.setFont(font) 154 | self.video_path.setObjectName("video_path") 155 | self.label_video_path = QtWidgets.QLabel(self.groupBox) 156 | self.label_video_path.setGeometry(QtCore.QRect(20, 320, 101, 21)) 157 | font = QtGui.QFont() 158 | font.setPointSize(10) 159 | self.label_video_path.setFont(font) 160 | self.label_video_path.setAlignment(QtCore.Qt.AlignCenter) 161 | self.label_video_path.setObjectName("label_video_path") 162 | 163 | self.retranslateUi(Dialog) 164 | QtCore.QMetaObject.connectSlotsByName(Dialog) 165 | 166 | def retranslateUi(self, Dialog): 167 | _translate = QtCore.QCoreApplication.translate 168 | Dialog.setWindowTitle(_translate("Dialog", "Dialog")) 169 | self.groupBox.setTitle(_translate("Dialog", "添加账号")) 170 | self.label_account.setText(_translate("Dialog", "

account

")) 171 | self.label_web.setText(_translate("Dialog", "

web

")) 172 | self.label_desc.setText(_translate("Dialog", "

youtube_desc

")) 173 | self.label_caption.setText(_translate("Dialog", "

tiktok_caption

")) 174 | self.label_title_tags.setText(_translate("Dialog", "

title_tags

")) 175 | self.label_tags.setText(_translate("Dialog", "

youtube_tags

")) 176 | self.label_title.setText(_translate("Dialog", "

youtube_title

")) 177 | self.label_type.setText(_translate("Dialog", "

type

")) 178 | self.web.setItemText(0, _translate("Dialog", "youtube")) 179 | self.web.setItemText(1, _translate("Dialog", "douyin")) 180 | self.type.setItemText(0, _translate("Dialog", "short")) 181 | self.type.setItemText(1, _translate("Dialog", "long")) 182 | self.finished_btn.setText(_translate("Dialog", "完成")) 183 | self.user_file_title.setItemText(0, _translate("Dialog", "true")) 184 | self.user_file_title.setItemText(1, _translate("Dialog", "false")) 185 | self.label_user_file_title.setText(_translate("Dialog", "

use_file_title

")) 186 | self.label_video_path.setText(_translate("Dialog", "

video_path

")) 187 | 188 | -------------------------------------------------------------------------------- /ui/add_account.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 578 10 | 615 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 50 20 | 70 21 | 461 22 | 491 23 | 24 | 25 | 26 | 添加账号 27 | 28 | 29 | 30 | 31 | 20 32 | 40 33 | 101 34 | 21 35 | 36 | 37 | 38 | 39 | 10 40 | 41 | 42 | 43 | 44 | 45 | 46 | <html><head/><body><p><span style=" font-size:10pt;">account</span></p></body></html> 47 | 48 | 49 | Qt::AlignCenter 50 | 51 | 52 | 53 | 54 | 55 | 20 56 | 80 57 | 101 58 | 21 59 | 60 | 61 | 62 | 63 | 10 64 | 65 | 66 | 67 | <html><head/><body><p><span style=" font-size:10pt;">web</span></p></body></html> 68 | 69 | 70 | Qt::AlignCenter 71 | 72 | 73 | 74 | 75 | 76 | 20 77 | 160 78 | 101 79 | 21 80 | 81 | 82 | 83 | 84 | 10 85 | 86 | 87 | 88 | <html><head/><body><p>youtube_desc</p></body></html> 89 | 90 | 91 | Qt::AlignCenter 92 | 93 | 94 | 95 | 96 | 97 | 20 98 | 200 99 | 101 100 | 21 101 | 102 | 103 | 104 | 105 | 10 106 | 107 | 108 | 109 | <html><head/><body><p>tiktok_caption</p></body></html> 110 | 111 | 112 | Qt::AlignCenter 113 | 114 | 115 | 116 | 117 | 118 | 20 119 | 240 120 | 101 121 | 21 122 | 123 | 124 | 125 | 126 | 10 127 | 128 | 129 | 130 | <html><head/><body><p><span style=" font-size:10pt;">title_tags</span></p></body></html> 131 | 132 | 133 | Qt::AlignCenter 134 | 135 | 136 | 137 | 138 | 139 | 20 140 | 280 141 | 101 142 | 21 143 | 144 | 145 | 146 | 147 | 10 148 | 149 | 150 | 151 | <html><head/><body><p>youtube_tags</p></body></html> 152 | 153 | 154 | Qt::AlignCenter 155 | 156 | 157 | 158 | 159 | 160 | 20 161 | 120 162 | 101 163 | 21 164 | 165 | 166 | 167 | 168 | 10 169 | 170 | 171 | 172 | <html><head/><body><p>youtube_title</p></body></html> 173 | 174 | 175 | Qt::AlignCenter 176 | 177 | 178 | 179 | 180 | 181 | 20 182 | 360 183 | 101 184 | 21 185 | 186 | 187 | 188 | 189 | 10 190 | 191 | 192 | 193 | <html><head/><body><p><span style=" font-size:10pt;">type</span></p></body></html> 194 | 195 | 196 | Qt::AlignCenter 197 | 198 | 199 | 200 | 201 | 202 | 140 203 | 40 204 | 271 205 | 20 206 | 207 | 208 | 209 | 210 | 10 211 | 212 | 213 | 214 | 215 | 216 | 217 | 140 218 | 80 219 | 271 220 | 22 221 | 222 | 223 | 224 | 225 | 10 226 | 227 | 228 | 229 | 230 | youtube 231 | 232 | 233 | 234 | 235 | douyin 236 | 237 | 238 | 239 | 240 | 241 | 242 | 140 243 | 120 244 | 271 245 | 20 246 | 247 | 248 | 249 | 250 | 10 251 | 252 | 253 | 254 | 255 | 256 | 257 | 140 258 | 160 259 | 271 260 | 20 261 | 262 | 263 | 264 | 265 | 10 266 | 267 | 268 | 269 | 270 | 271 | 272 | 140 273 | 200 274 | 271 275 | 20 276 | 277 | 278 | 279 | 280 | 10 281 | 282 | 283 | 284 | 285 | 286 | 287 | 140 288 | 240 289 | 271 290 | 20 291 | 292 | 293 | 294 | 295 | 10 296 | 297 | 298 | 299 | 300 | 301 | 302 | 140 303 | 280 304 | 271 305 | 20 306 | 307 | 308 | 309 | 310 | 10 311 | 312 | 313 | 314 | 315 | 316 | 317 | 140 318 | 360 319 | 271 320 | 22 321 | 322 | 323 | 324 | 325 | 10 326 | 327 | 328 | 329 | 330 | short 331 | 332 | 333 | 334 | 335 | long 336 | 337 | 338 | 339 | 340 | 341 | 342 | 170 343 | 450 344 | 75 345 | 23 346 | 347 | 348 | 349 | 350 | 10 351 | 352 | 353 | 354 | background-color: rgb(85, 170, 0); 355 | 356 | 357 | 完成 358 | 359 | 360 | 361 | 362 | 363 | 140 364 | 400 365 | 271 366 | 22 367 | 368 | 369 | 370 | 371 | 10 372 | 373 | 374 | 375 | 376 | true 377 | 378 | 379 | 380 | 381 | false 382 | 383 | 384 | 385 | 386 | 387 | 388 | 20 389 | 400 390 | 101 391 | 21 392 | 393 | 394 | 395 | 396 | 10 397 | 398 | 399 | 400 | <html><head/><body><p><span style=" font-size:10pt;">use_file_title</span></p></body></html> 401 | 402 | 403 | Qt::AlignCenter 404 | 405 | 406 | 407 | 408 | 409 | 140 410 | 320 411 | 271 412 | 20 413 | 414 | 415 | 416 | 417 | 10 418 | 419 | 420 | 421 | 422 | 423 | 424 | 20 425 | 320 426 | 101 427 | 21 428 | 429 | 430 | 431 | 432 | 10 433 | 434 | 435 | 436 | <html><head/><body><p>video_path</p></body></html> 437 | 438 | 439 | Qt::AlignCenter 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | -------------------------------------------------------------------------------- /ui/combox_cell_change.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '.\combox_cell_change.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.9.2 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | class Ui_Dialog(object): 12 | def setupUi(self, Dialog): 13 | Dialog.setObjectName("Dialog") 14 | Dialog.resize(334, 130) 15 | self.combox_cell = QtWidgets.QComboBox(Dialog) 16 | self.combox_cell.setGeometry(QtCore.QRect(20, 20, 291, 41)) 17 | self.combox_cell.setObjectName("combox_cell") 18 | self.change_finished_btn = QtWidgets.QPushButton(Dialog) 19 | self.change_finished_btn.setGeometry(QtCore.QRect(120, 80, 75, 31)) 20 | self.change_finished_btn.setStyleSheet("background-color: rgb(85, 170, 0);") 21 | self.change_finished_btn.setObjectName("change_finished_btn") 22 | 23 | self.retranslateUi(Dialog) 24 | QtCore.QMetaObject.connectSlotsByName(Dialog) 25 | 26 | def retranslateUi(self, Dialog): 27 | _translate = QtCore.QCoreApplication.translate 28 | Dialog.setWindowTitle(_translate("Dialog", "Dialog")) 29 | self.change_finished_btn.setText(_translate("Dialog", "修改完成")) 30 | 31 | -------------------------------------------------------------------------------- /ui/combox_cell_change.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 334 10 | 130 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 20 20 | 20 21 | 291 22 | 41 23 | 24 | 25 | 26 | 27 | 28 | 29 | 120 30 | 80 31 | 75 32 | 31 33 | 34 | 35 | 36 | background-color: rgb(85, 170, 0); 37 | 38 | 39 | 修改完成 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ui/double_page.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '.\double_page.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.15.7 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PyQt5 import QtCore, QtGui, QtWidgets 12 | 13 | 14 | class Ui_MainWindow(object): 15 | def setupUi(self, MainWindow): 16 | MainWindow.setObjectName("MainWindow") 17 | MainWindow.resize(1173, 682) 18 | self.centralwidget = QtWidgets.QWidget(MainWindow) 19 | self.centralwidget.setObjectName("centralwidget") 20 | self.tabWidget = QtWidgets.QTabWidget(self.centralwidget) 21 | self.tabWidget.setGeometry(QtCore.QRect(20, 60, 1141, 581)) 22 | font = QtGui.QFont() 23 | font.setPointSize(12) 24 | self.tabWidget.setFont(font) 25 | self.tabWidget.setIconSize(QtCore.QSize(16, 16)) 26 | self.tabWidget.setObjectName("tabWidget") 27 | self.video_page = QtWidgets.QWidget() 28 | self.video_page.setObjectName("video_page") 29 | self.group_box_download = QtWidgets.QGroupBox(self.video_page) 30 | self.group_box_download.setGeometry(QtCore.QRect(0, 20, 1131, 121)) 31 | self.group_box_download.setMinimumSize(QtCore.QSize(0, 0)) 32 | self.group_box_download.setSizeIncrement(QtCore.QSize(0, 0)) 33 | self.group_box_download.setStyleSheet("border-color: rgb(255, 0, 0);") 34 | self.group_box_download.setObjectName("group_box_download") 35 | self.pick_web = QtWidgets.QComboBox(self.group_box_download) 36 | self.pick_web.setGeometry(QtCore.QRect(100, 30, 291, 31)) 37 | self.pick_web.setObjectName("pick_web") 38 | self.pick_web.addItem("") 39 | self.pick_web.addItem("") 40 | self.pick_web.addItem("") 41 | self.input_home_url = QtWidgets.QLineEdit(self.group_box_download) 42 | self.input_home_url.setGeometry(QtCore.QRect(680, 30, 291, 31)) 43 | self.input_home_url.setObjectName("input_home_url") 44 | self.label_web = QtWidgets.QLabel(self.group_box_download) 45 | self.label_web.setGeometry(QtCore.QRect(20, 40, 71, 21)) 46 | font = QtGui.QFont() 47 | font.setPointSize(10) 48 | font.setKerning(True) 49 | self.label_web.setFont(font) 50 | self.label_web.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) 51 | self.label_web.setObjectName("label_web") 52 | self.label_url = QtWidgets.QLabel(self.group_box_download) 53 | self.label_url.setGeometry(QtCore.QRect(590, 40, 54, 12)) 54 | font = QtGui.QFont() 55 | font.setPointSize(10) 56 | self.label_url.setFont(font) 57 | self.label_url.setObjectName("label_url") 58 | self.label_save_path_download = QtWidgets.QLabel(self.group_box_download) 59 | self.label_save_path_download.setGeometry(QtCore.QRect(20, 90, 54, 12)) 60 | self.label_save_path_download.setObjectName("label_save_path_download") 61 | self.web_confirm_btn = QtWidgets.QPushButton(self.group_box_download) 62 | self.web_confirm_btn.setGeometry(QtCore.QRect(440, 30, 75, 31)) 63 | self.web_confirm_btn.setObjectName("web_confirm_btn") 64 | self.download_save_btn = QtWidgets.QPushButton(self.group_box_download) 65 | self.download_save_btn.setGeometry(QtCore.QRect(440, 80, 75, 31)) 66 | self.download_save_btn.setObjectName("download_save_btn") 67 | self.download_btn = QtWidgets.QPushButton(self.group_box_download) 68 | self.download_btn.setGeometry(QtCore.QRect(590, 80, 61, 31)) 69 | self.download_btn.setObjectName("download_btn") 70 | self.download_save_display = QtWidgets.QTextBrowser(self.group_box_download) 71 | self.download_save_display.setGeometry(QtCore.QRect(100, 80, 291, 31)) 72 | self.download_save_display.setObjectName("download_save_display") 73 | self.download_progress_bar = QtWidgets.QProgressBar(self.group_box_download) 74 | self.download_progress_bar.setGeometry(QtCore.QRect(680, 80, 291, 31)) 75 | font = QtGui.QFont() 76 | font.setPointSize(15) 77 | self.download_progress_bar.setFont(font) 78 | self.download_progress_bar.setStyleSheet("QProgressBar{border: 1px solid grey}") 79 | self.download_progress_bar.setMaximum(100) 80 | self.download_progress_bar.setProperty("value", 0) 81 | self.download_progress_bar.setAlignment(QtCore.Qt.AlignCenter) 82 | self.download_progress_bar.setObjectName("download_progress_bar") 83 | self.end_download_btn = QtWidgets.QPushButton(self.group_box_download) 84 | self.end_download_btn.setGeometry(QtCore.QRect(1000, 80, 71, 31)) 85 | self.end_download_btn.setObjectName("end_download_btn") 86 | self.group_box_editor = QtWidgets.QGroupBox(self.video_page) 87 | self.group_box_editor.setGeometry(QtCore.QRect(0, 170, 1131, 391)) 88 | font = QtGui.QFont() 89 | font.setPointSize(9) 90 | self.group_box_editor.setFont(font) 91 | self.group_box_editor.setObjectName("group_box_editor") 92 | self.groupBox_4 = QtWidgets.QGroupBox(self.group_box_editor) 93 | self.groupBox_4.setGeometry(QtCore.QRect(10, 30, 1111, 61)) 94 | self.groupBox_4.setObjectName("groupBox_4") 95 | self.select_background_pic_btn = QtWidgets.QPushButton(self.groupBox_4) 96 | self.select_background_pic_btn.setGeometry(QtCore.QRect(10, 20, 81, 31)) 97 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) 98 | sizePolicy.setHorizontalStretch(0) 99 | sizePolicy.setVerticalStretch(0) 100 | sizePolicy.setHeightForWidth(self.select_background_pic_btn.sizePolicy().hasHeightForWidth()) 101 | self.select_background_pic_btn.setSizePolicy(sizePolicy) 102 | self.select_background_pic_btn.setObjectName("select_background_pic_btn") 103 | self.select_background_music_btn = QtWidgets.QPushButton(self.groupBox_4) 104 | self.select_background_music_btn.setGeometry(QtCore.QRect(369, 20, 81, 31)) 105 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) 106 | sizePolicy.setHorizontalStretch(0) 107 | sizePolicy.setVerticalStretch(0) 108 | sizePolicy.setHeightForWidth(self.select_background_music_btn.sizePolicy().hasHeightForWidth()) 109 | self.select_background_music_btn.setSizePolicy(sizePolicy) 110 | self.select_background_music_btn.setObjectName("select_background_music_btn") 111 | self.select_water_logo_btn = QtWidgets.QPushButton(self.groupBox_4) 112 | self.select_water_logo_btn.setGeometry(QtCore.QRect(820, 20, 81, 31)) 113 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) 114 | sizePolicy.setHorizontalStretch(0) 115 | sizePolicy.setVerticalStretch(0) 116 | sizePolicy.setHeightForWidth(self.select_water_logo_btn.sizePolicy().hasHeightForWidth()) 117 | self.select_water_logo_btn.setSizePolicy(sizePolicy) 118 | self.select_water_logo_btn.setObjectName("select_water_logo_btn") 119 | self.label_volume = QtWidgets.QLabel(self.groupBox_4) 120 | self.label_volume.setGeometry(QtCore.QRect(649, 20, 36, 31)) 121 | self.label_volume.setObjectName("label_volume") 122 | self.input_volume = QtWidgets.QLineEdit(self.groupBox_4) 123 | self.input_volume.setGeometry(QtCore.QRect(689, 20, 31, 31)) 124 | self.input_volume.setText("") 125 | self.input_volume.setObjectName("input_volume") 126 | self.is_music_covered = QtWidgets.QCheckBox(self.groupBox_4) 127 | self.is_music_covered.setGeometry(QtCore.QRect(730, 20, 71, 31)) 128 | self.is_music_covered.setObjectName("is_music_covered") 129 | self.water_logo_display = QtWidgets.QTextBrowser(self.groupBox_4) 130 | self.water_logo_display.setGeometry(QtCore.QRect(910, 20, 161, 31)) 131 | self.water_logo_display.setObjectName("water_logo_display") 132 | self.background_pic_path_display = QtWidgets.QTextBrowser(self.groupBox_4) 133 | self.background_pic_path_display.setGeometry(QtCore.QRect(100, 20, 151, 31)) 134 | self.background_pic_path_display.setObjectName("background_pic_path_display") 135 | self.background_music_path_display = QtWidgets.QTextBrowser(self.groupBox_4) 136 | self.background_music_path_display.setGeometry(QtCore.QRect(459, 20, 181, 31)) 137 | self.background_music_path_display.setObjectName("background_music_path_display") 138 | self.label_background_pic = QtWidgets.QLabel(self.groupBox_4) 139 | self.label_background_pic.setGeometry(QtCore.QRect(260, 20, 31, 31)) 140 | self.label_background_pic.setObjectName("label_background_pic") 141 | self.input_background_pic_rate = QtWidgets.QLineEdit(self.groupBox_4) 142 | self.input_background_pic_rate.setGeometry(QtCore.QRect(300, 20, 51, 31)) 143 | self.input_background_pic_rate.setText("") 144 | self.input_background_pic_rate.setObjectName("input_background_pic_rate") 145 | self.groupBox_5 = QtWidgets.QGroupBox(self.group_box_editor) 146 | self.groupBox_5.setGeometry(QtCore.QRect(10, 100, 561, 281)) 147 | self.groupBox_5.setObjectName("groupBox_5") 148 | self.select_single_source_path_btn = QtWidgets.QPushButton(self.groupBox_5) 149 | self.select_single_source_path_btn.setGeometry(QtCore.QRect(20, 60, 75, 31)) 150 | self.select_single_source_path_btn.setObjectName("select_single_source_path_btn") 151 | self.select_save_path_single_btn = QtWidgets.QPushButton(self.groupBox_5) 152 | self.select_save_path_single_btn.setGeometry(QtCore.QRect(20, 240, 75, 31)) 153 | self.select_save_path_single_btn.setObjectName("select_save_path_single_btn") 154 | self.run_single_btn = QtWidgets.QPushButton(self.groupBox_5) 155 | self.run_single_btn.setGeometry(QtCore.QRect(380, 240, 131, 31)) 156 | self.run_single_btn.setObjectName("run_single_btn") 157 | self.single_video_list = QtWidgets.QListWidget(self.groupBox_5) 158 | self.single_video_list.setGeometry(QtCore.QRect(20, 110, 491, 121)) 159 | self.single_video_list.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) 160 | self.single_video_list.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove) 161 | self.single_video_list.setAlternatingRowColors(True) 162 | self.single_video_list.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) 163 | self.single_video_list.setObjectName("single_video_list") 164 | self.add_single_btn = QtWidgets.QPushButton(self.groupBox_5) 165 | self.add_single_btn.setGeometry(QtCore.QRect(510, 110, 31, 23)) 166 | self.add_single_btn.setObjectName("add_single_btn") 167 | self.delete_single_btn = QtWidgets.QPushButton(self.groupBox_5) 168 | self.delete_single_btn.setGeometry(QtCore.QRect(510, 140, 31, 23)) 169 | self.delete_single_btn.setObjectName("delete_single_btn") 170 | self.label_video_count = QtWidgets.QLabel(self.groupBox_5) 171 | self.label_video_count.setGeometry(QtCore.QRect(380, 70, 54, 12)) 172 | self.label_video_count.setObjectName("label_video_count") 173 | self.editor_single_source_path_display = QtWidgets.QTextBrowser(self.groupBox_5) 174 | self.editor_single_source_path_display.setGeometry(QtCore.QRect(100, 60, 241, 31)) 175 | self.editor_single_source_path_display.setObjectName("editor_single_source_path_display") 176 | self.video_count_display = QtWidgets.QTextBrowser(self.groupBox_5) 177 | self.video_count_display.setGeometry(QtCore.QRect(430, 60, 81, 31)) 178 | self.video_count_display.setObjectName("video_count_display") 179 | self.label_end = QtWidgets.QLabel(self.groupBox_5) 180 | self.label_end.setGeometry(QtCore.QRect(100, 30, 41, 21)) 181 | self.label_end.setObjectName("label_end") 182 | self.editor_single_save_path_display = QtWidgets.QTextBrowser(self.groupBox_5) 183 | self.editor_single_save_path_display.setGeometry(QtCore.QRect(110, 240, 231, 31)) 184 | self.editor_single_save_path_display.setObjectName("editor_single_save_path_display") 185 | self.label_end_x = QtWidgets.QLabel(self.groupBox_5) 186 | self.label_end_x.setGeometry(QtCore.QRect(350, 31, 41, 21)) 187 | self.label_end_x.setObjectName("label_end_x") 188 | self.input_end_x = QtWidgets.QLineEdit(self.groupBox_5) 189 | self.input_end_x.setGeometry(QtCore.QRect(397, 31, 31, 20)) 190 | self.input_end_x.setObjectName("input_end_x") 191 | self.input_end_y = QtWidgets.QLineEdit(self.groupBox_5) 192 | self.input_end_y.setGeometry(QtCore.QRect(487, 31, 31, 20)) 193 | self.input_end_y.setObjectName("input_end_y") 194 | self.label_end_y = QtWidgets.QLabel(self.groupBox_5) 195 | self.label_end_y.setGeometry(QtCore.QRect(440, 31, 41, 21)) 196 | self.label_end_y.setObjectName("label_end_y") 197 | self.input_start_y = QtWidgets.QLineEdit(self.groupBox_5) 198 | self.input_start_y.setGeometry(QtCore.QRect(307, 31, 31, 20)) 199 | self.input_start_y.setObjectName("input_start_y") 200 | self.label_start_y = QtWidgets.QLabel(self.groupBox_5) 201 | self.label_start_y.setGeometry(QtCore.QRect(270, 31, 41, 21)) 202 | self.label_start_y.setObjectName("label_start_y") 203 | self.label_start_x = QtWidgets.QLabel(self.groupBox_5) 204 | self.label_start_x.setGeometry(QtCore.QRect(179, 30, 41, 21)) 205 | self.label_start_x.setObjectName("label_start_x") 206 | self.input_start_x = QtWidgets.QLineEdit(self.groupBox_5) 207 | self.input_start_x.setGeometry(QtCore.QRect(220, 31, 31, 20)) 208 | self.input_start_x.setObjectName("input_start_x") 209 | self.input_end_second = QtWidgets.QLineEdit(self.groupBox_5) 210 | self.input_end_second.setGeometry(QtCore.QRect(140, 30, 31, 20)) 211 | self.input_end_second.setText("") 212 | self.input_end_second.setObjectName("input_end_second") 213 | self.input_front_second = QtWidgets.QLineEdit(self.groupBox_5) 214 | self.input_front_second.setGeometry(QtCore.QRect(60, 32, 31, 20)) 215 | self.input_front_second.setText("") 216 | self.input_front_second.setObjectName("input_front_second") 217 | self.label_front = QtWidgets.QLabel(self.groupBox_5) 218 | self.label_front.setGeometry(QtCore.QRect(22, 32, 31, 21)) 219 | self.label_front.setObjectName("label_front") 220 | self.groupBox_6 = QtWidgets.QGroupBox(self.group_box_editor) 221 | self.groupBox_6.setGeometry(QtCore.QRect(580, 100, 541, 281)) 222 | self.groupBox_6.setObjectName("groupBox_6") 223 | self.normal_merge_btn = QtWidgets.QRadioButton(self.groupBox_6) 224 | self.normal_merge_btn.setGeometry(QtCore.QRect(100, 30, 89, 21)) 225 | self.normal_merge_btn.setObjectName("normal_merge_btn") 226 | self.merge_type_btn_group = QtWidgets.QButtonGroup(MainWindow) 227 | self.merge_type_btn_group.setObjectName("merge_type_btn_group") 228 | self.merge_type_btn_group.addButton(self.normal_merge_btn) 229 | self.label_merge_videos = QtWidgets.QLabel(self.groupBox_6) 230 | self.label_merge_videos.setGeometry(QtCore.QRect(10, 30, 54, 21)) 231 | self.label_merge_videos.setObjectName("label_merge_videos") 232 | self.top10_merge_btn = QtWidgets.QRadioButton(self.groupBox_6) 233 | self.top10_merge_btn.setGeometry(QtCore.QRect(210, 30, 71, 21)) 234 | self.top10_merge_btn.setObjectName("top10_merge_btn") 235 | self.merge_type_btn_group.addButton(self.top10_merge_btn) 236 | self.select_merge_source_path_btn = QtWidgets.QPushButton(self.groupBox_6) 237 | self.select_merge_source_path_btn.setGeometry(QtCore.QRect(10, 60, 75, 31)) 238 | self.select_merge_source_path_btn.setObjectName("select_merge_source_path_btn") 239 | self.select_save_path_merge_btn = QtWidgets.QPushButton(self.groupBox_6) 240 | self.select_save_path_merge_btn.setGeometry(QtCore.QRect(10, 240, 75, 31)) 241 | self.select_save_path_merge_btn.setObjectName("select_save_path_merge_btn") 242 | self.run_merge_btn = QtWidgets.QPushButton(self.groupBox_6) 243 | self.run_merge_btn.setGeometry(QtCore.QRect(380, 240, 121, 31)) 244 | self.run_merge_btn.setObjectName("run_merge_btn") 245 | self.delete_merge_btn = QtWidgets.QPushButton(self.groupBox_6) 246 | self.delete_merge_btn.setGeometry(QtCore.QRect(500, 140, 31, 23)) 247 | self.delete_merge_btn.setObjectName("delete_merge_btn") 248 | self.add_merge_btn = QtWidgets.QPushButton(self.groupBox_6) 249 | self.add_merge_btn.setGeometry(QtCore.QRect(500, 110, 31, 23)) 250 | self.add_merge_btn.setObjectName("add_merge_btn") 251 | self.merge_video_list = QtWidgets.QListWidget(self.groupBox_6) 252 | self.merge_video_list.setGeometry(QtCore.QRect(10, 111, 491, 121)) 253 | self.merge_video_list.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) 254 | self.merge_video_list.setDragEnabled(True) 255 | self.merge_video_list.setDragDropOverwriteMode(False) 256 | self.merge_video_list.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove) 257 | self.merge_video_list.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) 258 | self.merge_video_list.setViewMode(QtWidgets.QListView.ListMode) 259 | self.merge_video_list.setObjectName("merge_video_list") 260 | self.editor_merge_source_path_display = QtWidgets.QTextBrowser(self.groupBox_6) 261 | self.editor_merge_source_path_display.setGeometry(QtCore.QRect(100, 60, 231, 31)) 262 | self.editor_merge_source_path_display.setObjectName("editor_merge_source_path_display") 263 | self.editor_merge_save_path_display = QtWidgets.QTextBrowser(self.groupBox_6) 264 | self.editor_merge_save_path_display.setGeometry(QtCore.QRect(100, 240, 231, 31)) 265 | self.editor_merge_save_path_display.setObjectName("editor_merge_save_path_display") 266 | self.top_10_status = QtWidgets.QLabel(self.groupBox_6) 267 | self.top_10_status.setGeometry(QtCore.QRect(280, 30, 211, 21)) 268 | self.top_10_status.setText("") 269 | self.top_10_status.setObjectName("top_10_status") 270 | self.meterial_num_btn = QtWidgets.QPushButton(self.groupBox_6) 271 | self.meterial_num_btn.setGeometry(QtCore.QRect(370, 60, 71, 31)) 272 | self.meterial_num_btn.setObjectName("meterial_num_btn") 273 | self.material_num_display = QtWidgets.QTextBrowser(self.groupBox_6) 274 | self.material_num_display.setGeometry(QtCore.QRect(450, 60, 51, 31)) 275 | font = QtGui.QFont() 276 | font.setPointSize(12) 277 | self.material_num_display.setFont(font) 278 | self.material_num_display.setObjectName("material_num_display") 279 | self.shuffle_order_btn = QtWidgets.QRadioButton(self.groupBox_6) 280 | self.shuffle_order_btn.setGeometry(QtCore.QRect(290, 30, 89, 21)) 281 | self.shuffle_order_btn.setObjectName("shuffle_order_btn") 282 | self.tabWidget.addTab(self.video_page, "") 283 | self.account_page = QtWidgets.QWidget() 284 | self.account_page.setObjectName("account_page") 285 | self.group_box_upload = QtWidgets.QGroupBox(self.account_page) 286 | self.group_box_upload.setGeometry(QtCore.QRect(0, 10, 1131, 561)) 287 | self.group_box_upload.setTitle("") 288 | self.group_box_upload.setObjectName("group_box_upload") 289 | self.account_table = QtWidgets.QTableWidget(self.group_box_upload) 290 | self.account_table.setGeometry(QtCore.QRect(10, 20, 1081, 521)) 291 | sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) 292 | sizePolicy.setHorizontalStretch(0) 293 | sizePolicy.setVerticalStretch(0) 294 | sizePolicy.setHeightForWidth(self.account_table.sizePolicy().hasHeightForWidth()) 295 | self.account_table.setSizePolicy(sizePolicy) 296 | self.account_table.setStyleSheet("alternate-background-color: rgb(85, 170, 0); ") 297 | self.account_table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) 298 | self.account_table.setAlternatingRowColors(True) 299 | self.account_table.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerItem) 300 | self.account_table.setShowGrid(True) 301 | self.account_table.setObjectName("account_table") 302 | self.account_table.setColumnCount(11) 303 | self.account_table.setRowCount(0) 304 | item = QtWidgets.QTableWidgetItem() 305 | self.account_table.setHorizontalHeaderItem(0, item) 306 | item = QtWidgets.QTableWidgetItem() 307 | self.account_table.setHorizontalHeaderItem(1, item) 308 | item = QtWidgets.QTableWidgetItem() 309 | self.account_table.setHorizontalHeaderItem(2, item) 310 | item = QtWidgets.QTableWidgetItem() 311 | self.account_table.setHorizontalHeaderItem(3, item) 312 | item = QtWidgets.QTableWidgetItem() 313 | self.account_table.setHorizontalHeaderItem(4, item) 314 | item = QtWidgets.QTableWidgetItem() 315 | self.account_table.setHorizontalHeaderItem(5, item) 316 | item = QtWidgets.QTableWidgetItem() 317 | self.account_table.setHorizontalHeaderItem(6, item) 318 | item = QtWidgets.QTableWidgetItem() 319 | self.account_table.setHorizontalHeaderItem(7, item) 320 | item = QtWidgets.QTableWidgetItem() 321 | self.account_table.setHorizontalHeaderItem(8, item) 322 | item = QtWidgets.QTableWidgetItem() 323 | self.account_table.setHorizontalHeaderItem(9, item) 324 | item = QtWidgets.QTableWidgetItem() 325 | self.account_table.setHorizontalHeaderItem(10, item) 326 | self.account_table.horizontalHeader().setCascadingSectionResizes(False) 327 | self.account_table.horizontalHeader().setHighlightSections(False) 328 | self.account_table.horizontalHeader().setSortIndicatorShown(False) 329 | self.account_table.horizontalHeader().setStretchLastSection(True) 330 | self.add_account_btn = QtWidgets.QPushButton(self.group_box_upload) 331 | self.add_account_btn.setGeometry(QtCore.QRect(1090, 20, 31, 23)) 332 | self.add_account_btn.setObjectName("add_account_btn") 333 | self.delete_account_btn = QtWidgets.QPushButton(self.group_box_upload) 334 | self.delete_account_btn.setGeometry(QtCore.QRect(1090, 50, 31, 23)) 335 | self.delete_account_btn.setObjectName("delete_account_btn") 336 | self.tabWidget.addTab(self.account_page, "") 337 | self.tab = QtWidgets.QWidget() 338 | self.tab.setObjectName("tab") 339 | self.groupBox = QtWidgets.QGroupBox(self.tab) 340 | self.groupBox.setGeometry(QtCore.QRect(30, 30, 391, 171)) 341 | self.groupBox.setObjectName("groupBox") 342 | self.select_translate_dir_btn = QtWidgets.QPushButton(self.groupBox) 343 | self.select_translate_dir_btn.setGeometry(QtCore.QRect(20, 50, 91, 31)) 344 | font = QtGui.QFont() 345 | font.setPointSize(12) 346 | self.select_translate_dir_btn.setFont(font) 347 | self.select_translate_dir_btn.setObjectName("select_translate_dir_btn") 348 | self.run_translate_btn = QtWidgets.QPushButton(self.groupBox) 349 | self.run_translate_btn.setGeometry(QtCore.QRect(20, 100, 91, 31)) 350 | font = QtGui.QFont() 351 | font.setPointSize(12) 352 | self.run_translate_btn.setFont(font) 353 | self.run_translate_btn.setObjectName("run_translate_btn") 354 | self.translate_path = QtWidgets.QTextBrowser(self.groupBox) 355 | self.translate_path.setGeometry(QtCore.QRect(120, 50, 251, 31)) 356 | font = QtGui.QFont() 357 | font.setPointSize(12) 358 | self.translate_path.setFont(font) 359 | self.translate_path.setObjectName("translate_path") 360 | self.translate_progress_bar = QtWidgets.QProgressBar(self.groupBox) 361 | self.translate_progress_bar.setGeometry(QtCore.QRect(120, 100, 251, 31)) 362 | font = QtGui.QFont() 363 | font.setPointSize(12) 364 | self.translate_progress_bar.setFont(font) 365 | self.translate_progress_bar.setProperty("value", 0) 366 | self.translate_progress_bar.setAlignment(QtCore.Qt.AlignCenter) 367 | self.translate_progress_bar.setObjectName("translate_progress_bar") 368 | self.tabWidget.addTab(self.tab, "") 369 | self.label = QtWidgets.QLabel(self.centralwidget) 370 | self.label.setGeometry(QtCore.QRect(10, 10, 1141, 61)) 371 | font = QtGui.QFont() 372 | font.setPointSize(40) 373 | self.label.setFont(font) 374 | self.label.setAlignment(QtCore.Qt.AlignCenter) 375 | self.label.setObjectName("label") 376 | MainWindow.setCentralWidget(self.centralwidget) 377 | self.statusbar = QtWidgets.QStatusBar(MainWindow) 378 | self.statusbar.setObjectName("statusbar") 379 | MainWindow.setStatusBar(self.statusbar) 380 | 381 | self.retranslateUi(MainWindow) 382 | self.tabWidget.setCurrentIndex(0) 383 | QtCore.QMetaObject.connectSlotsByName(MainWindow) 384 | 385 | def retranslateUi(self, MainWindow): 386 | _translate = QtCore.QCoreApplication.translate 387 | MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) 388 | self.group_box_download.setTitle(_translate("MainWindow", "视频下载")) 389 | self.pick_web.setItemText(0, _translate("MainWindow", "douyin")) 390 | self.pick_web.setItemText(1, _translate("MainWindow", "youtube")) 391 | self.pick_web.setItemText(2, _translate("MainWindow", "xigua")) 392 | self.label_web.setText(_translate("MainWindow", "视频网站:")) 393 | self.label_url.setText(_translate("MainWindow", "主页链接:")) 394 | self.label_save_path_download.setText(_translate("MainWindow", "保存目录:")) 395 | self.web_confirm_btn.setText(_translate("MainWindow", "确认选择")) 396 | self.download_save_btn.setText(_translate("MainWindow", "路径选择")) 397 | self.download_btn.setText(_translate("MainWindow", "开始下载")) 398 | self.download_progress_bar.setFormat(_translate("MainWindow", "%p%")) 399 | self.end_download_btn.setText(_translate("MainWindow", "结束下载")) 400 | self.group_box_editor.setTitle(_translate("MainWindow", "视频编辑")) 401 | self.groupBox_4.setTitle(_translate("MainWindow", "常规设置")) 402 | self.select_background_pic_btn.setText(_translate("MainWindow", "背景图")) 403 | self.select_background_music_btn.setText(_translate("MainWindow", "背景乐")) 404 | self.select_water_logo_btn.setText(_translate("MainWindow", "水印")) 405 | self.label_volume.setText(_translate("MainWindow", "音量:")) 406 | self.is_music_covered.setText(_translate("MainWindow", "是否覆盖")) 407 | self.label_background_pic.setText(_translate("MainWindow", "比例:")) 408 | self.groupBox_5.setTitle(_translate("MainWindow", "单视频编辑")) 409 | self.select_single_source_path_btn.setText(_translate("MainWindow", "原视频目录")) 410 | self.select_save_path_single_btn.setText(_translate("MainWindow", "保存目录")) 411 | self.run_single_btn.setText(_translate("MainWindow", "开始剪辑")) 412 | self.add_single_btn.setText(_translate("MainWindow", "+")) 413 | self.delete_single_btn.setText(_translate("MainWindow", "-")) 414 | self.label_video_count.setText(_translate("MainWindow", "总数量:")) 415 | self.label_end.setText(_translate("MainWindow", "片尾")) 416 | self.label_end_x.setText(_translate("MainWindow", "终点x")) 417 | self.label_end_y.setText(_translate("MainWindow", "终点y")) 418 | self.label_start_y.setText(_translate("MainWindow", "起点y")) 419 | self.label_start_x.setText(_translate("MainWindow", "起点x")) 420 | self.label_front.setText(_translate("MainWindow", "片头")) 421 | self.groupBox_6.setTitle(_translate("MainWindow", "合集")) 422 | self.normal_merge_btn.setText(_translate("MainWindow", "常规合集")) 423 | self.label_merge_videos.setText(_translate("MainWindow", "合集类型")) 424 | self.top10_merge_btn.setText(_translate("MainWindow", "top10")) 425 | self.select_merge_source_path_btn.setText(_translate("MainWindow", "原视频目录")) 426 | self.select_save_path_merge_btn.setText(_translate("MainWindow", "保存目录")) 427 | self.run_merge_btn.setText(_translate("MainWindow", "开始剪辑")) 428 | self.delete_merge_btn.setText(_translate("MainWindow", "-")) 429 | self.add_merge_btn.setText(_translate("MainWindow", "+")) 430 | self.meterial_num_btn.setText(_translate("MainWindow", "素材数量")) 431 | self.material_num_display.setHtml(_translate("MainWindow", "\n" 432 | "\n" 435 | "


")) 436 | self.shuffle_order_btn.setText(_translate("MainWindow", "乱序")) 437 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.video_page), _translate("MainWindow", "视频编辑")) 438 | item = self.account_table.horizontalHeaderItem(0) 439 | item.setText(_translate("MainWindow", "account")) 440 | item = self.account_table.horizontalHeaderItem(1) 441 | item.setText(_translate("MainWindow", "web")) 442 | item = self.account_table.horizontalHeaderItem(2) 443 | item.setText(_translate("MainWindow", "title")) 444 | item = self.account_table.horizontalHeaderItem(3) 445 | item.setText(_translate("MainWindow", "description")) 446 | item = self.account_table.horizontalHeaderItem(4) 447 | item.setText(_translate("MainWindow", "caption")) 448 | item = self.account_table.horizontalHeaderItem(5) 449 | item.setText(_translate("MainWindow", "title_tags")) 450 | item = self.account_table.horizontalHeaderItem(6) 451 | item.setText(_translate("MainWindow", "tags")) 452 | item = self.account_table.horizontalHeaderItem(7) 453 | item.setText(_translate("MainWindow", "video_path")) 454 | item = self.account_table.horizontalHeaderItem(8) 455 | item.setText(_translate("MainWindow", "video_type")) 456 | item = self.account_table.horizontalHeaderItem(9) 457 | item.setText(_translate("MainWindow", "user_file_title")) 458 | item = self.account_table.horizontalHeaderItem(10) 459 | item.setText(_translate("MainWindow", "finger_web")) 460 | self.add_account_btn.setText(_translate("MainWindow", "+")) 461 | self.delete_account_btn.setText(_translate("MainWindow", "-")) 462 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.account_page), _translate("MainWindow", "账号管理")) 463 | self.groupBox.setTitle(_translate("MainWindow", "标题翻译")) 464 | self.select_translate_dir_btn.setText(_translate("MainWindow", "选择目录")) 465 | self.run_translate_btn.setText(_translate("MainWindow", "开始翻译")) 466 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "其他工具")) 467 | self.label.setText(_translate("MainWindow", "

欢迎杜总使用搬运工

")) 468 | -------------------------------------------------------------------------------- /ui/loading.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '.\loading.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.9.2 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | class Ui_Dialog(object): 12 | def setupUi(self, Dialog): 13 | Dialog.setObjectName("Dialog") 14 | Dialog.resize(1130, 753) 15 | self.movie_label = QtWidgets.QLabel(Dialog) 16 | self.movie_label.setGeometry(QtCore.QRect(0, 0, 1101, 741)) 17 | self.movie_label.setStyleSheet("QLable{background:transparent}") 18 | self.movie_label.setText("") 19 | self.movie_label.setObjectName("movie_label") 20 | 21 | self.retranslateUi(Dialog) 22 | QtCore.QMetaObject.connectSlotsByName(Dialog) 23 | 24 | def retranslateUi(self, Dialog): 25 | _translate = QtCore.QCoreApplication.translate 26 | Dialog.setWindowTitle(_translate("Dialog", "Dialog")) 27 | 28 | -------------------------------------------------------------------------------- /ui/loading.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1130 10 | 753 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 0 20 | 0 21 | 1101 22 | 741 23 | 24 | 25 | 26 | QLable{background:transparent} 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /ui/loading_cat.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlamingJay/homework/168f205345857c56f285ad6a77dadaef64337897/ui/loading_cat.gif -------------------------------------------------------------------------------- /ui/running.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlamingJay/homework/168f205345857c56f285ad6a77dadaef64337897/ui/running.gif -------------------------------------------------------------------------------- /ui/shortVideo.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1167 10 | 886 11 | 12 | 13 | 14 | 15 | 10 16 | 17 | 18 | 19 | MainWindow 20 | 21 | 22 | 23 | 24 | 25 | 20 26 | 670 27 | 1131 28 | 161 29 | 30 | 31 | 32 | 账号和上传管理 33 | 34 | 35 | 36 | 37 | 10 38 | 20 39 | 1071 40 | 131 41 | 42 | 43 | 44 | 45 | 0 46 | 0 47 | 48 | 49 | 50 | color:rgb(0, 0, 0) 51 | 52 | 53 | QAbstractItemView::ScrollPerItem 54 | 55 | 56 | false 57 | 58 | 59 | false 60 | 61 | 62 | false 63 | 64 | 65 | true 66 | 67 | 68 | 69 | account 70 | 71 | 72 | 73 | 74 | web 75 | 76 | 77 | 78 | 79 | title 80 | 81 | 82 | 83 | 84 | description 85 | 86 | 87 | 88 | 89 | caption 90 | 91 | 92 | 93 | 94 | title_tags 95 | 96 | 97 | 98 | 99 | tags 100 | 101 | 102 | 103 | 104 | video_path 105 | 106 | 107 | 108 | 109 | video_type 110 | 111 | 112 | 113 | 114 | user_file_title 115 | 116 | 117 | 118 | 119 | 120 | 121 | 1080 122 | 20 123 | 31 124 | 23 125 | 126 | 127 | 128 | + 129 | 130 | 131 | 132 | 133 | 134 | 1080 135 | 50 136 | 31 137 | 23 138 | 139 | 140 | 141 | - 142 | 143 | 144 | 145 | 146 | 147 | 148 | 20 149 | 110 150 | 1131 151 | 121 152 | 153 | 154 | 155 | 156 | 0 157 | 0 158 | 159 | 160 | 161 | 162 | 0 163 | 0 164 | 165 | 166 | 167 | border-color: rgb(255, 0, 0); 168 | 169 | 170 | 视频下载 171 | 172 | 173 | 174 | 175 | 100 176 | 30 177 | 291 178 | 31 179 | 180 | 181 | 182 | 183 | douyin 184 | 185 | 186 | 187 | 188 | youtube 189 | 190 | 191 | 192 | 193 | 194 | 195 | 680 196 | 30 197 | 291 198 | 31 199 | 200 | 201 | 202 | 203 | 204 | 205 | 20 206 | 40 207 | 71 208 | 21 209 | 210 | 211 | 212 | 213 | 10 214 | true 215 | 216 | 217 | 218 | 视频网站: 219 | 220 | 221 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 222 | 223 | 224 | 225 | 226 | 227 | 590 228 | 40 229 | 54 230 | 12 231 | 232 | 233 | 234 | 235 | 10 236 | 237 | 238 | 239 | 主页链接: 240 | 241 | 242 | 243 | 244 | 245 | 20 246 | 90 247 | 54 248 | 12 249 | 250 | 251 | 252 | 保存目录: 253 | 254 | 255 | 256 | 257 | 258 | 1000 259 | 40 260 | 71 261 | 16 262 | 263 | 264 | 265 | 266 | 10 267 | 268 | 269 | 270 | 英文标题 271 | 272 | 273 | 274 | 275 | 276 | 440 277 | 30 278 | 75 279 | 31 280 | 281 | 282 | 283 | 确认选择 284 | 285 | 286 | 287 | 288 | 289 | 440 290 | 80 291 | 75 292 | 31 293 | 294 | 295 | 296 | 路径选择 297 | 298 | 299 | 300 | 301 | 302 | 590 303 | 80 304 | 61 305 | 31 306 | 307 | 308 | 309 | 开始下载 310 | 311 | 312 | 313 | 314 | 315 | 100 316 | 80 317 | 291 318 | 31 319 | 320 | 321 | 322 | 323 | 324 | 325 | 680 326 | 80 327 | 291 328 | 31 329 | 330 | 331 | 332 | 333 | 15 334 | 335 | 336 | 337 | 0 338 | 339 | 340 | Qt::AlignCenter 341 | 342 | 343 | 344 | 345 | 346 | 1000 347 | 80 348 | 71 349 | 31 350 | 351 | 352 | 353 | 结束下载 354 | 355 | 356 | 357 | 358 | 359 | 360 | 20 361 | 260 362 | 1131 363 | 391 364 | 365 | 366 | 367 | 视频编辑 368 | 369 | 370 | 371 | 372 | 10 373 | 30 374 | 1111 375 | 61 376 | 377 | 378 | 379 | 常规设置 380 | 381 | 382 | 383 | 384 | 10 385 | 20 386 | 81 387 | 31 388 | 389 | 390 | 391 | 392 | 0 393 | 0 394 | 395 | 396 | 397 | 背景图 398 | 399 | 400 | 401 | 402 | 403 | 290 404 | 20 405 | 81 406 | 31 407 | 408 | 409 | 410 | 411 | 0 412 | 0 413 | 414 | 415 | 416 | 背景乐 417 | 418 | 419 | 420 | 421 | 422 | 740 423 | 20 424 | 81 425 | 31 426 | 427 | 428 | 429 | 430 | 0 431 | 0 432 | 433 | 434 | 435 | 水印 436 | 437 | 438 | 439 | 440 | 441 | 569 442 | 28 443 | 36 444 | 16 445 | 446 | 447 | 448 | 音量: 449 | 450 | 451 | 452 | 453 | 454 | 611 455 | 28 456 | 31 457 | 20 458 | 459 | 460 | 461 | 50 462 | 463 | 464 | 465 | 466 | 467 | 650 468 | 30 469 | 71 470 | 16 471 | 472 | 473 | 474 | 是否覆盖 475 | 476 | 477 | 478 | 479 | 480 | 830 481 | 20 482 | 231 483 | 31 484 | 485 | 486 | 487 | 488 | 489 | 490 | 100 491 | 20 492 | 171 493 | 31 494 | 495 | 496 | 497 | 498 | 499 | 500 | 380 501 | 20 502 | 181 503 | 31 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 10 512 | 100 513 | 561 514 | 281 515 | 516 | 517 | 518 | 单视频编辑 519 | 520 | 521 | 522 | 523 | 20 524 | 60 525 | 75 526 | 31 527 | 528 | 529 | 530 | 原视频目录 531 | 532 | 533 | 534 | 535 | 536 | 20 537 | 240 538 | 75 539 | 31 540 | 541 | 542 | 543 | 保存目录 544 | 545 | 546 | 547 | 548 | 549 | 370 550 | 240 551 | 141 552 | 31 553 | 554 | 555 | 556 | 开始剪辑 557 | 558 | 559 | 560 | 561 | 562 | 20 563 | 110 564 | 491 565 | 121 566 | 567 | 568 | 569 | 570 | 571 | 572 | 510 573 | 110 574 | 31 575 | 23 576 | 577 | 578 | 579 | + 580 | 581 | 582 | 583 | 584 | 585 | 510 586 | 140 587 | 31 588 | 23 589 | 590 | 591 | 592 | - 593 | 594 | 595 | 596 | 597 | 598 | 380 599 | 70 600 | 54 601 | 12 602 | 603 | 604 | 605 | 总数量: 606 | 607 | 608 | 609 | 610 | 611 | 100 612 | 60 613 | 256 614 | 31 615 | 616 | 617 | 618 | 619 | 620 | 621 | 430 622 | 60 623 | 81 624 | 31 625 | 626 | 627 | 628 | 629 | 630 | 631 | 100 632 | 30 633 | 41 634 | 21 635 | 636 | 637 | 638 | 片尾 639 | 640 | 641 | 642 | 643 | 644 | 110 645 | 240 646 | 241 647 | 31 648 | 649 | 650 | 651 | 652 | 653 | 654 | 350 655 | 31 656 | 41 657 | 21 658 | 659 | 660 | 661 | 终点x 662 | 663 | 664 | 665 | 666 | 667 | 397 668 | 31 669 | 31 670 | 20 671 | 672 | 673 | 674 | 675 | 676 | 677 | 487 678 | 31 679 | 31 680 | 20 681 | 682 | 683 | 684 | 685 | 686 | 687 | 440 688 | 31 689 | 41 690 | 21 691 | 692 | 693 | 694 | 终点y 695 | 696 | 697 | 698 | 699 | 700 | 307 701 | 31 702 | 31 703 | 20 704 | 705 | 706 | 707 | 708 | 709 | 710 | 270 711 | 31 712 | 41 713 | 21 714 | 715 | 716 | 717 | 起点y 718 | 719 | 720 | 721 | 722 | 723 | 179 724 | 30 725 | 41 726 | 21 727 | 728 | 729 | 730 | 起点x 731 | 732 | 733 | 734 | 735 | 736 | 220 737 | 31 738 | 31 739 | 20 740 | 741 | 742 | 743 | 744 | 745 | 746 | 140 747 | 30 748 | 31 749 | 20 750 | 751 | 752 | 753 | 0 754 | 755 | 756 | 757 | 758 | 759 | 60 760 | 32 761 | 31 762 | 20 763 | 764 | 765 | 766 | 0 767 | 768 | 769 | 770 | 771 | 772 | 22 773 | 32 774 | 31 775 | 21 776 | 777 | 778 | 779 | 片头 780 | 781 | 782 | 783 | 784 | 785 | 786 | 580 787 | 100 788 | 541 789 | 281 790 | 791 | 792 | 793 | 合集 794 | 795 | 796 | 797 | 798 | 100 799 | 30 800 | 89 801 | 21 802 | 803 | 804 | 805 | 常规合集 806 | 807 | 808 | merge_type_btn_group 809 | 810 | 811 | 812 | 813 | 814 | 10 815 | 30 816 | 54 817 | 21 818 | 819 | 820 | 821 | 合集类型 822 | 823 | 824 | 825 | 826 | 827 | 210 828 | 30 829 | 71 830 | 21 831 | 832 | 833 | 834 | top10 835 | 836 | 837 | merge_type_btn_group 838 | 839 | 840 | 841 | 842 | 843 | 10 844 | 60 845 | 75 846 | 31 847 | 848 | 849 | 850 | 原视频目录 851 | 852 | 853 | 854 | 855 | 856 | 360 857 | 61 858 | 54 859 | 31 860 | 861 | 862 | 863 | 总时长: 864 | 865 | 866 | 867 | 868 | 869 | 10 870 | 240 871 | 75 872 | 31 873 | 874 | 875 | 876 | 保存目录 877 | 878 | 879 | 880 | 881 | 882 | 360 883 | 240 884 | 141 885 | 31 886 | 887 | 888 | 889 | 开始剪辑 890 | 891 | 892 | 893 | 894 | 895 | 500 896 | 140 897 | 31 898 | 23 899 | 900 | 901 | 902 | - 903 | 904 | 905 | 906 | 907 | 908 | 500 909 | 110 910 | 31 911 | 23 912 | 913 | 914 | 915 | + 916 | 917 | 918 | 919 | 920 | 921 | 10 922 | 111 923 | 491 924 | 121 925 | 926 | 927 | 928 | 929 | 930 | 931 | 100 932 | 60 933 | 231 934 | 31 935 | 936 | 937 | 938 | 939 | 940 | 941 | 410 942 | 60 943 | 81 944 | 31 945 | 946 | 947 | 948 | 949 | 950 | 951 | 100 952 | 240 953 | 231 954 | 31 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 20 964 | 20 965 | 1121 966 | 61 967 | 968 | 969 | 970 | 971 | 40 972 | 973 | 974 | 975 | <html><head/><body><p><span style=" font-size:36pt; color:#00aa00;">欢迎使用搬运工</span></p></body></html> 976 | 977 | 978 | Qt::AlignCenter 979 | 980 | 981 | 982 | 983 | 984 | 985 | 0 986 | 0 987 | 1167 988 | 25 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | true 1000 | 1001 | 1002 | 1003 | 1004 | -------------------------------------------------------------------------------- /ui/text_cell_change.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file '.\text_cell_change.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.9.2 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | class Ui_Dialog(object): 12 | def setupUi(self, Dialog): 13 | Dialog.setObjectName("Dialog") 14 | Dialog.setWindowModality(QtCore.Qt.NonModal) 15 | Dialog.resize(481, 306) 16 | self.change_finished_btn = QtWidgets.QPushButton(Dialog) 17 | self.change_finished_btn.setGeometry(QtCore.QRect(200, 250, 75, 31)) 18 | self.change_finished_btn.setStyleSheet("background-color: rgb(85, 170, 0);") 19 | self.change_finished_btn.setObjectName("change_finished_btn") 20 | self.cell_content = QtWidgets.QPlainTextEdit(Dialog) 21 | self.cell_content.setGeometry(QtCore.QRect(20, 20, 441, 221)) 22 | self.cell_content.setObjectName("cell_content") 23 | 24 | self.retranslateUi(Dialog) 25 | QtCore.QMetaObject.connectSlotsByName(Dialog) 26 | 27 | def retranslateUi(self, Dialog): 28 | _translate = QtCore.QCoreApplication.translate 29 | Dialog.setWindowTitle(_translate("Dialog", "Dialog")) 30 | self.change_finished_btn.setText(_translate("Dialog", "修改完成")) 31 | 32 | -------------------------------------------------------------------------------- /ui/text_cell_change.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | Qt::NonModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 481 13 | 306 14 | 15 | 16 | 17 | Dialog 18 | 19 | 20 | 21 | 22 | 200 23 | 250 24 | 75 25 | 31 26 | 27 | 28 | 29 | background-color: rgb(85, 170, 0); 30 | 31 | 32 | 修改完成 33 | 34 | 35 | 36 | 37 | 38 | 20 39 | 20 40 | 441 41 | 221 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /ui/working.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlamingJay/homework/168f205345857c56f285ad6a77dadaef64337897/ui/working.gif -------------------------------------------------------------------------------- /upload/AdsChrome.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import requests 4 | import undetected_chromedriver.v2 as uc 5 | import tldextract 6 | import ssl 7 | import pickle, os, time 8 | RANDOM_USERAGENT = 'random' 9 | from selenium import webdriver 10 | from selenium.webdriver.chrome.options import Options 11 | ssl._create_default_https_context = ssl._create_unverified_context 12 | 13 | class AdsChromeDriver(object): 14 | ''' 15 | 自定义驱动 16 | ''' 17 | def __init__(self, 18 | cookies_folder_path: str, 19 | extensions_folder_path: str, 20 | ads_id: str, 21 | host: str = None, 22 | port: int = None, 23 | private: bool = False, 24 | full_screen: bool = False, 25 | headless: bool = False, 26 | language: str = 'en-us', 27 | manual_set_timezone: bool = False, 28 | user_agent: str = None, 29 | load_proxy_checker_website: bool = False 30 | ): 31 | self.cookies_folder_path = cookies_folder_path 32 | open_url = "http://localhost:50325/api/v1/browser/start?user_id=" + ads_id 33 | resp = requests.get(open_url).json() 34 | if resp["code"] != 0: 35 | print(resp["msg"]) 36 | print("please check ads_id") 37 | sys.exit() 38 | chrome_driver = resp["data"]["webdriver"] 39 | chrome_options = Options() 40 | chrome_options.add_experimental_option("debuggerAddress", resp["data"]["ws"]["selenium"]) 41 | self.driver = webdriver.Chrome(chrome_driver, options=chrome_options) 42 | if full_screen: 43 | self.driver.fullscreen_window() 44 | 45 | try: 46 | change_timezone_id = None 47 | for (dirpath, _, filenames) in os.walk(extensions_folder_path): 48 | for filename in filenames: 49 | if filename.endswith('.xpi') or filename.endswith('.zip'): 50 | addon_id = self.driver.install_addon(os.path.join(dirpath, filename), temporary=False) 51 | 52 | if 'change_timezone' in filename: 53 | change_timezone_id = addon_id 54 | 55 | while len(self.driver.window_handles) > 1: 56 | time.sleep(0.5) 57 | self.driver.switch_to.window(self.driver.window_handles[-1]) 58 | self.driver.close() 59 | 60 | self.driver.switch_to.window(self.driver.window_handles[0]) 61 | 62 | if change_timezone_id is not None and manual_set_timezone: 63 | if host is not None and port is not None: 64 | self.open_new_tab('https://whatismyipaddress.com/') 65 | time.sleep(0.25) 66 | 67 | self.open_new_tab('https://www.google.com/search?client=firefox-b-d&q=my+timezone') 68 | time.sleep(0.25) 69 | 70 | self.driver.switch_to.window(self.driver.window_handles[0]) 71 | 72 | input('\n\n\nSet timezone.\n\nPress ENTER, when finished. ') 73 | 74 | while len(self.driver.window_handles) > 1: 75 | time.sleep(0.5) 76 | self.driver.switch_to.window(self.driver.window_handles[-1]) 77 | self.driver.close() 78 | 79 | self.driver.switch_to.window(self.driver.window_handles[0]) 80 | elif load_proxy_checker_website and host is not None and port is not None: 81 | self.driver.get('https://whatismyipaddress.com/') 82 | except: 83 | while len(self.driver.window_handles) > 1: 84 | time.sleep(0.5) 85 | self.driver.switch_to.window(self.driver.window_handles[-1]) 86 | self.driver.close() 87 | 88 | def get(self, url: str) -> bool: 89 | clean_current = self.driver.current_url.replace('https://', '').replace('www.', '').strip('/') 90 | clean_new = url.replace('https://', '').replace('www.', '').strip('/') 91 | 92 | if clean_current == clean_new: 93 | return False 94 | 95 | self.driver.get(url) 96 | 97 | return True 98 | 99 | def switch_to_frame(self, address): 100 | ''' 101 | 切换iframe 102 | :param index: 103 | :return: 104 | ''' 105 | address_iframe = self.find_element_by_xpath(address) 106 | self.driver.switch_to.frame(address_iframe) 107 | 108 | def switch_to_default(self): 109 | ''' 110 | 切换回主文档 111 | :return: 112 | ''' 113 | self.driver.switch_to.default_content() 114 | 115 | def find_element_by_xpath(self, xpath): 116 | ''' 117 | 通过XPath定位元素 118 | :param xpath: 119 | :return: 120 | ''' 121 | return self.driver.find_element_by_xpath(xpath) 122 | 123 | def find_element_by_name(self, name): 124 | ''' 125 | 根据name定位元素 126 | :param name: 127 | :return: 128 | ''' 129 | return self.driver.find_element_by_name(name) 130 | 131 | def find_element_by_id(self, id): 132 | ''' 133 | 通过ID定位元素 134 | :param xpath: 135 | :return: 136 | ''' 137 | return self.driver.find_element_by_id(id) 138 | 139 | def find_elements_by_id(self, id): 140 | ''' 141 | 通过ID定位元素 142 | :param xpath: 143 | :return: 144 | ''' 145 | return self.driver.find_elements_by_id(id) 146 | 147 | def find_element_by_class_name(self, class_name): 148 | ''' 149 | 通过class_name定位元素 150 | :param xpath: 151 | :return: 152 | ''' 153 | return self.driver.find_element_by_class_name(class_name) 154 | 155 | def has_cookies_for_current_website(self, account: str = "", create_folder_if_not_exists: bool = True) -> bool: 156 | return os.path.exists( 157 | self.__cookies_path(account, 158 | create_folder_if_not_exists=create_folder_if_not_exists 159 | ) 160 | ) 161 | 162 | def __cookies_path(self, account: str = "", create_folder_if_not_exists: bool = True) -> str: 163 | url_comps = tldextract.extract(self.driver.current_url) 164 | formatted_url = url_comps.domain + '.' + url_comps.suffix + '.' + account 165 | 166 | return os.path.join( 167 | self.cookies_folder_path, 168 | formatted_url + '.pkl' 169 | ) 170 | 171 | def save_cookies(self, account) -> None: 172 | pickle.dump( 173 | self.driver.get_cookies(), 174 | open(self.__cookies_path(account), "wb") 175 | ) 176 | 177 | def load_cookies(self, account) -> None: 178 | ''' 179 | 加载cookies 180 | :return: 181 | ''' 182 | if not self.has_cookies_for_current_website(account): 183 | self.save_cookies(account) 184 | return 185 | 186 | cookies = pickle.load(open(self.__cookies_path(account), "rb")) 187 | 188 | for cookie in cookies: 189 | self.driver.add_cookie(cookie) 190 | 191 | def refresh(self) -> None: 192 | self.driver.refresh() 193 | 194 | def quit(self): 195 | self.driver.quit() -------------------------------------------------------------------------------- /upload/AutoUpload.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | import json 3 | import os 4 | import argparse 5 | from YouTubeUploaderShort import YouTubeUploaderShort 6 | from YouTubeUploaderLong import YouTubeUploaderLong 7 | from TikTokUploader import TiktokUploader 8 | 9 | 10 | def pick_video(hist_path, target_path): 11 | ''' 12 | 选择一个未上传过的视频进行上传 13 | :param path: 14 | :return: 15 | ''' 16 | history = [] 17 | if not os.path.exists(hist_path): 18 | file = open(hist_path, "w", encoding="utf-8") 19 | file.close() 20 | with open(hist_path, "r", encoding="utf-8") as f: 21 | for line in f.readlines(): 22 | history.append(line.strip()) 23 | 24 | # 随机上传 25 | files = get_file_list(target_path) 26 | target = "" 27 | for file in files: 28 | print(file) 29 | if file not in history: 30 | target = file 31 | break 32 | 33 | return target 34 | 35 | 36 | def add_hist(path, name): 37 | ''' 38 | 将当前上传的视频标记为已上传 39 | :param path: 40 | :param name: 41 | :return: 42 | ''' 43 | with open(path, 'a', encoding="utf-8") as f: 44 | f.write(name + "\n") 45 | 46 | 47 | def get_file_list(file_path): 48 | ''' 49 | 按照文件最后修改时间进行升序排序 50 | ''' 51 | dir_list = os.listdir(file_path) 52 | if not dir_list: 53 | return 54 | else: 55 | dir_list = sorted(filter(lambda item: 'mp4' in item, dir_list), key=lambda x: os.path.getmtime(os.path.join(file_path, x))) 56 | return dir_list 57 | 58 | 59 | if __name__ == "__main__": 60 | parser = argparse.ArgumentParser() 61 | 62 | parser.add_argument("--root", help='account', required=True) 63 | 64 | parser.add_argument("--account", help='account', required=True) 65 | 66 | parser.add_argument("--web", help='web', required=True) 67 | 68 | parser.add_argument("--meta", help='meta', required=True) 69 | 70 | parser.add_argument("--video_path", help='Path to the video file', required=True) 71 | 72 | parser.add_argument("--video_type", help="video_type", required=True) 73 | 74 | parser.add_argument("--use_file_title", help="use_file_title", required=True) 75 | 76 | parser.add_argument("--finger_web", help="use_file_title", required=True) 77 | 78 | args = parser.parse_args() 79 | 80 | # 解析参数 81 | accounts = dict() 82 | root = os.path.abspath(os.path.join(args.root, "..")) 83 | with open(root + os.sep + "resource" + os.sep + args.meta) as f: 84 | accounts = json.load(f) 85 | 86 | title = accounts[args.account]["title"] 87 | caption = accounts[args.account]["caption"] 88 | description = accounts[args.account]["description"] 89 | tags = accounts[args.account]["tags"] 90 | title_tags = accounts[args.account]["title_tags"] 91 | resources = root + os.sep + "resource" 92 | 93 | if args.web == "youtube": 94 | hist_path = resources + os.sep + "_".join([args.web, args.account, args.video_type]) + ".txt" 95 | target = pick_video(hist_path, args.video_path) 96 | if target is not "": 97 | if args.video_type == "short": 98 | print("short") 99 | add_hist(hist_path, target) 100 | uploader = YouTubeUploaderShort(pkl_path=resources, 101 | account=args.account, 102 | video_path=args.video_path + os.sep + target, 103 | title=title, 104 | caption=caption, 105 | description=description, 106 | tags=tags, 107 | title_tags=title_tags, 108 | use_file_title=args.use_file_title, 109 | finger_web=args.finger_web 110 | ) 111 | uploader.upload() 112 | elif args.video_type == "long": 113 | print("long") 114 | add_hist(hist_path, target) 115 | uploader = YouTubeUploaderLong(pkl_path=resources, 116 | account=args.account, 117 | video_path=args.video_path + os.sep + target, 118 | title=title, 119 | caption=caption, 120 | description=description, 121 | tags=tags, 122 | title_tags=title_tags, 123 | use_file_title=args.use_file_title, 124 | finger_web=args.finger_web 125 | ) 126 | print("ready to upload") 127 | uploader.upload() 128 | elif args.web == "tiktok": 129 | hist_path = resources + os.sep + "tiktok_" + args.account + ".txt" 130 | target = pick_video(hist_path, args.video_path) 131 | 132 | if target is not "": 133 | print("tiktok") 134 | add_hist(hist_path, target) 135 | uploader = TiktokUploader(pkl_path=resources, 136 | account=args.account, 137 | video_path=args.video_path + os.sep + target, 138 | title=title, 139 | caption=caption, 140 | description=description, 141 | tags=tags, 142 | title_tags=title_tags, 143 | use_file_title=args.use_file_title, 144 | finger_web=args.finger_web 145 | ) 146 | uploader.upload() 147 | -------------------------------------------------------------------------------- /upload/ChromeDriver.py: -------------------------------------------------------------------------------- 1 | import undetected_chromedriver.v2 as uc 2 | import tldextract 3 | import ssl 4 | import pickle, os, time 5 | RANDOM_USERAGENT = 'random' 6 | 7 | ssl._create_default_https_context = ssl._create_unverified_context 8 | class ChromeDriver(object): 9 | ''' 10 | 自定义驱动 11 | ''' 12 | def __init__(self, 13 | cookies_folder_path: str, 14 | extensions_folder_path: str, 15 | host: str = None, 16 | port: int = None, 17 | private: bool = False, 18 | full_screen: bool = False, 19 | headless: bool = False, 20 | language: str = 'en-us', 21 | manual_set_timezone: bool = False, 22 | user_agent: str = None, 23 | load_proxy_checker_website: bool = False 24 | ): 25 | self.cookies_folder_path = cookies_folder_path 26 | options = uc.ChromeOptions() 27 | # options.add_argument("--headless") 28 | self.driver = uc.Chrome(version_main=91, options=options) 29 | if full_screen: 30 | self.driver.fullscreen_window() 31 | 32 | try: 33 | change_timezone_id = None 34 | for (dirpath, _, filenames) in os.walk(extensions_folder_path): 35 | for filename in filenames: 36 | if filename.endswith('.xpi') or filename.endswith('.zip'): 37 | addon_id = self.driver.install_addon(os.path.join(dirpath, filename), temporary=False) 38 | 39 | if 'change_timezone' in filename: 40 | change_timezone_id = addon_id 41 | 42 | while len(self.driver.window_handles) > 1: 43 | time.sleep(0.5) 44 | self.driver.switch_to.window(self.driver.window_handles[-1]) 45 | self.driver.close() 46 | 47 | self.driver.switch_to.window(self.driver.window_handles[0]) 48 | 49 | if change_timezone_id is not None and manual_set_timezone: 50 | if host is not None and port is not None: 51 | self.open_new_tab('https://whatismyipaddress.com/') 52 | time.sleep(0.25) 53 | 54 | self.open_new_tab('https://www.google.com/search?client=firefox-b-d&q=my+timezone') 55 | time.sleep(0.25) 56 | 57 | self.driver.switch_to.window(self.driver.window_handles[0]) 58 | 59 | input('\n\n\nSet timezone.\n\nPress ENTER, when finished. ') 60 | 61 | while len(self.driver.window_handles) > 1: 62 | time.sleep(0.5) 63 | self.driver.switch_to.window(self.driver.window_handles[-1]) 64 | self.driver.close() 65 | 66 | self.driver.switch_to.window(self.driver.window_handles[0]) 67 | elif load_proxy_checker_website and host is not None and port is not None: 68 | self.driver.get('https://whatismyipaddress.com/') 69 | except: 70 | while len(self.driver.window_handles) > 1: 71 | time.sleep(0.5) 72 | self.driver.switch_to.window(self.driver.window_handles[-1]) 73 | self.driver.close() 74 | 75 | def get(self, url: str) -> bool: 76 | clean_current = self.driver.current_url.replace('https://', '').replace('www.', '').strip('/') 77 | clean_new = url.replace('https://', '').replace('www.', '').strip('/') 78 | 79 | if clean_current == clean_new: 80 | return False 81 | 82 | self.driver.get(url) 83 | 84 | return True 85 | 86 | def switch_to_frame(self, address): 87 | ''' 88 | 切换iframe 89 | :param index: 90 | :return: 91 | ''' 92 | address_iframe = self.find_element_by_xpath(address) 93 | self.driver.switch_to.frame(address_iframe) 94 | 95 | def switch_to_default(self): 96 | ''' 97 | 切换回主文档 98 | :return: 99 | ''' 100 | self.driver.switch_to.default_content() 101 | 102 | def find_element_by_xpath(self, xpath): 103 | ''' 104 | 通过XPath定位元素 105 | :param xpath: 106 | :return: 107 | ''' 108 | return self.driver.find_element("xpath", xpath) 109 | # return self.driver.find_element_by_xpath(xpath) 110 | 111 | def find_element_by_name(self, name): 112 | ''' 113 | 根据name定位元素 114 | :param name: 115 | :return: 116 | ''' 117 | return self.driver.find_element("name", name) 118 | # return self.driver.find_element_by_name(name) 119 | 120 | def find_element_by_id(self, id): 121 | ''' 122 | 通过ID定位元素 123 | :param xpath: 124 | :return: 125 | ''' 126 | return self.driver.find_element("id", id) 127 | # return self.driver.find_element_by_id(id) 128 | 129 | def find_elements_by_id(self, id): 130 | ''' 131 | 通过ID定位元素 132 | :param xpath: 133 | :return: 134 | ''' 135 | return self.driver.find_elements("id", id) 136 | # return self.driver.find_elements_by_id(id) 137 | 138 | def find_element_by_class_name(self, class_name): 139 | ''' 140 | 通过class_name定位元素 141 | :param xpath: 142 | :return: 143 | ''' 144 | return self.driver.find_element("class_name", id) 145 | # return self.driver.find_element_by_class_name(class_name) 146 | 147 | def has_cookies_for_current_website(self, account: str = "", create_folder_if_not_exists: bool = True) -> bool: 148 | return os.path.exists( 149 | self.__cookies_path(account, 150 | create_folder_if_not_exists=create_folder_if_not_exists 151 | ) 152 | ) 153 | 154 | def __cookies_path(self, account: str = "", create_folder_if_not_exists: bool = True) -> str: 155 | url_comps = tldextract.extract(self.driver.current_url) 156 | formatted_url = url_comps.domain + '.' + url_comps.suffix + '.' + account 157 | 158 | return os.path.join( 159 | self.cookies_folder_path, 160 | formatted_url + '.pkl' 161 | ) 162 | 163 | def save_cookies(self, account) -> None: 164 | pickle.dump( 165 | self.driver.get_cookies(), 166 | open(self.__cookies_path(account), "wb") 167 | ) 168 | 169 | def load_cookies(self, account) -> None: 170 | ''' 171 | 加载cookies 172 | :return: 173 | ''' 174 | if not self.has_cookies_for_current_website(account): 175 | self.save_cookies(account) 176 | return 177 | 178 | cookies = pickle.load(open(self.__cookies_path(account), "rb")) 179 | 180 | for cookie in cookies: 181 | self.driver.add_cookie(cookie) 182 | 183 | def refresh(self) -> None: 184 | self.driver.refresh() 185 | 186 | def quit(self): 187 | self.driver.close() 188 | self.driver.quit() -------------------------------------------------------------------------------- /upload/Constant.py: -------------------------------------------------------------------------------- 1 | class YOUTUBE_CONSTANT: 2 | """A class for storing constants for YoutubeUploader class""" 3 | YOUTUBE_URL = 'https://www.youtube.com' 4 | YOUTUBE_STUDIO_URL = 'https://studio.youtube.com' 5 | YOUTUBE_UPLOAD_URL = 'https://www.youtube.com/upload' 6 | 7 | # 时间相关 8 | USER_WAITING_TIME = 3 9 | CONFIRM_TIME = 5 10 | LOAD_TIME = 10 11 | SHORT_WAIT_TIME = 20 12 | LONG_WAIT_TIME = 60 13 | LONG_UPLOAD_TIME = 600 14 | 15 | # xpath相关 16 | VIDEO_TITLE = 'title' 17 | VIDEO_DESCRIPTION = 'description' 18 | VIDEO_TAGS = 'tags' 19 | DESCRIPTION_CONTAINER = '/html/body/ytcp-uploads-dialog/tp-yt-paper-dialog/div/ytcp-animatable[1]/' \ 20 | 'ytcp-uploads-details/div/ytcp-uploads-basics/ytcp-mention-textbox[2]' 21 | 22 | PICTURE_BUTTON = 'file-loader' 23 | 24 | TEXTBOX = 'textbox' 25 | TEXT_INPUT = 'text-input' 26 | RADIO_LABEL = 'radioLabel' 27 | 28 | STATUS_CONTAINER = '/html/body/ytcp-uploads-dialog/tp-yt-paper-dialog/div/ytcp-animatable[2]/' \ 29 | 'div/div[1]/ytcp-video-upload-progress/span' 30 | NOT_MADE_FOR_KIDS_LABEL = 'radioLabel' 31 | 32 | # Thanks to romka777 33 | MORE_BUTTON = '/html/body/ytcp-uploads-dialog/tp-yt-paper-dialog/div/ytcp-animatable[1]/ytcp-video-metadata-editor/div/div/ytcp-button/div' 34 | TAGS_INPUT_CONTAINER = '/html/body/ytcp-uploads-dialog/tp-yt-paper-dialog/div/ytcp-animatable[1]/ytcp-video-metadata-editor/div/ytcp-video-metadata-editor-advanced/div[3]/ytcp-form-input-container/div[1]/div[2]/ytcp-free-text-chip-bar/ytcp-chip-bar/div' 35 | 36 | TAGS_INPUT = 'text-input' 37 | TOOGLE_BUTN = "toggle-button" 38 | NEXT_BUTTON = 'next-button' 39 | PUBLIC_BUTTON = 'PUBLIC' 40 | VIDEO_URL_CONTAINER = "//span[@class='video-url-fadeable style-scope ytcp-video-info']" 41 | VIDEO_URL_ELEMENT = "//a[@class='style-scope ytcp-video-info']" 42 | HREF = 'href' 43 | UPLOADED = 'Uploading' 44 | ERROR_CONTAINER = '//*[@id="error-message"]' 45 | VIDEO_NOT_FOUND_ERROR = 'Could not find video_id' 46 | DONE_BUTTON = 'done-button' 47 | INPUT_FILE_VIDEO = "//input[@type='file']" 48 | INPUT_FILE_THUMBNAIL = "//input[@id='file-loader']" 49 | 50 | class TIKTOK_CONSTANT: 51 | TIKTOK_URL = 'https://www.tiktok.com' 52 | TIKTOK_UPLOAD_URL = 'https://www.tiktok.com/upload/?lang=en' 53 | USER_WAITING_TIME = 2 54 | CHECK_TIME = 15 55 | POST_TIME = 15 56 | WAIT_TIME = 10 57 | LOAD_TIME = 5 58 | 59 | Caption = "//*[contains(@class, 'notranslate public-DraftEditor-content')]" 60 | IFRAME = '//*[@id="main"]/div[2]/div/iframe' 61 | INPUT_FILE_VIDEO = '//*[@id="root"]/div/div/div/div/div[2]/div[1]/div/input' 62 | 63 | ALLOW_CHECK = "tiktok-switch__switch-wrapper" 64 | 65 | POST_BUTTON = '//*[@id="root"]/div/div/div/div/div[2]/div[2]/div[7]/div[2]/button' 66 | PREVIEW = '/html/body/div[1]/div/div/div/div/div[2]/div[1]' 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /upload/HubChromeDriver.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from selenium.webdriver.chrome.service import Service 4 | import tldextract 5 | import socket 6 | import ssl 7 | import pickle, os, time 8 | import json 9 | import uuid 10 | RANDOM_USERAGENT = 'random' 11 | from selenium import webdriver 12 | import traceback 13 | ssl._create_default_https_context = ssl._create_unverified_context 14 | 15 | class HubChromeDriver(object): 16 | ''' 17 | 自定义驱动 18 | ''' 19 | def __init__(self, 20 | cookies_folder_path: str, 21 | extensions_folder_path: str, 22 | container_name: str, 23 | host: str = None, 24 | port: int = None, 25 | private: bool = False, 26 | full_screen: bool = False, 27 | headless: bool = False, 28 | language: str = 'en-us', 29 | manual_set_timezone: bool = False, 30 | user_agent: str = None, 31 | load_proxy_checker_website: bool = False 32 | ): 33 | self.cookies_folder_path = cookies_folder_path 34 | # 端口 35 | socket_port = 22558 36 | group_name = '18946079633的团队' 37 | api_key = '12b1d1760bc0b886a5d40e87c75e94922398eb8095c21e16b4498d562aca39693cbbbb1378d32bc6e4ea7341238612dc' 38 | # 调用参数 39 | data1 = { 40 | 'userInfo': '{\'apiKey\': \'%s\', \'loginGroupName\': \'%s\'}' % (api_key, group_name), 41 | 'action': 'startBrowserByName', 42 | 'browserName': container_name, 43 | 'isHeadless': 0, 44 | 'requestId': str(uuid.uuid4()), 45 | 'isWebDriverReadOnlyMode': 0 46 | } 47 | 48 | print('初始化参数...') 49 | # 发送请求 50 | r = self.send_socket(data1, socket_port) 51 | if r.get('statusCode') == 0: 52 | print('正在启动环境...') 53 | store_port = r.get('debuggingPort') 54 | else: 55 | print('环境启动失败') 56 | exit() 57 | 58 | options = webdriver.ChromeOptions() 59 | options.add_argument('--disable-gpu') 60 | options.add_experimental_option("debuggerAddress", '127.0.0.1:' + str(store_port)) 61 | driver_path = 'D:/Chrome/chromedriver.exe' 62 | s = Service(driver_path) 63 | 64 | self.driver = webdriver.Chrome(service=s, options=options) 65 | 66 | # 初始化WebDriver 67 | print('初始化WebDriver...') 68 | # 打开环境检测页 69 | self.driver.get('chrome-extension://%s/index.html' % r.get('backgroundPluginId')) 70 | print('正在检测环境安全...') 71 | wait_s = 1 72 | # 检测环境是否安全,然后运行自己的逻辑 73 | while True: 74 | if wait_s >= 30: 75 | print('===>环境安全检测等待时间超过30秒,退出后续操作') 76 | break 77 | data2 = { 78 | 'userInfo': '{\'apiKey\': \'%s\'}' % api_key, 79 | 'action': 'getIsEnterStore', 80 | 'containerName': container_name, 81 | 'requestId': str(uuid.uuid4()), 82 | } 83 | # 发送请求 84 | r = self.send_socket(data2, socket_port) 85 | if r.get('isEnterStore'): 86 | print('环境检测通过') 87 | # 环境检测通过后,可执行自己的脚本逻辑 88 | self.driver.execute_script('window.open(\'https://www.baidu.com/\');') 89 | break 90 | time.sleep(1) 91 | wait_s += 1 92 | 93 | if full_screen: 94 | self.driver.fullscreen_window() 95 | 96 | try: 97 | change_timezone_id = None 98 | for (dirpath, _, filenames) in os.walk(extensions_folder_path): 99 | for filename in filenames: 100 | if filename.endswith('.xpi') or filename.endswith('.zip'): 101 | addon_id = self.driver.install_addon(os.path.join(dirpath, filename), temporary=False) 102 | 103 | if 'change_timezone' in filename: 104 | change_timezone_id = addon_id 105 | 106 | while len(self.driver.window_handles) > 1: 107 | time.sleep(0.5) 108 | self.driver.switch_to.window(self.driver.window_handles[-1]) 109 | self.driver.close() 110 | 111 | self.driver.switch_to.window(self.driver.window_handles[0]) 112 | 113 | if change_timezone_id is not None and manual_set_timezone: 114 | if host is not None and port is not None: 115 | self.open_new_tab('https://whatismyipaddress.com/') 116 | time.sleep(0.25) 117 | 118 | self.open_new_tab('https://www.google.com/search?client=firefox-b-d&q=my+timezone') 119 | time.sleep(0.25) 120 | 121 | self.driver.switch_to.window(self.driver.window_handles[0]) 122 | 123 | input('\n\n\nSet timezone.\n\nPress ENTER, when finished. ') 124 | 125 | while len(self.driver.window_handles) > 1: 126 | time.sleep(0.5) 127 | self.driver.switch_to.window(self.driver.window_handles[-1]) 128 | self.driver.close() 129 | 130 | self.driver.switch_to.window(self.driver.window_handles[0]) 131 | elif load_proxy_checker_website and host is not None and port is not None: 132 | self.driver.get('https://whatismyipaddress.com/') 133 | except: 134 | while len(self.driver.window_handles) > 1: 135 | time.sleep(0.5) 136 | self.driver.switch_to.window(self.driver.window_handles[-1]) 137 | self.driver.close() 138 | 139 | def send_socket(self, params, socket_port): 140 | try: 141 | client = socket.socket() 142 | client.settimeout(300) # 超时时间 143 | client.connect(('127.0.0.1', socket_port)) 144 | # 参数后需拼接上 + b'\r\n',不可删除 145 | s = json.dumps(params).encode('utf-8') + b'\r\n' 146 | client.sendall(s) 147 | rec = client.recv(1024).decode() 148 | client.shutdown(2) 149 | # 返回值后面同样存在 '\r\n',需删除后方可正常转为json 150 | return json.loads(str(rec).replace('\r\n', '')) 151 | except Exception as err: 152 | print(traceback.print_exc()) 153 | print(err) 154 | 155 | def get(self, url: str) -> bool: 156 | clean_current = self.driver.current_url.replace('https://', '').replace('www.', '').strip('/') 157 | clean_new = url.replace('https://', '').replace('www.', '').strip('/') 158 | 159 | if clean_current == clean_new: 160 | return False 161 | 162 | self.driver.get(url) 163 | 164 | return True 165 | 166 | def switch_to_frame(self, address): 167 | ''' 168 | 切换iframe 169 | :param index: 170 | :return: 171 | ''' 172 | address_iframe = self.find_element_by_xpath(address) 173 | self.driver.switch_to.frame(address_iframe) 174 | 175 | def switch_to_default(self): 176 | ''' 177 | 切换回主文档 178 | :return: 179 | ''' 180 | self.driver.switch_to.default_content() 181 | 182 | def find_element_by_xpath(self, xpath): 183 | ''' 184 | 通过XPath定位元素 185 | :param xpath: 186 | :return: 187 | ''' 188 | return self.driver.find_element_by_xpath(xpath) 189 | 190 | def find_element_by_name(self, name): 191 | ''' 192 | 根据name定位元素 193 | :param name: 194 | :return: 195 | ''' 196 | return self.driver.find_element_by_name(name) 197 | 198 | def find_element_by_id(self, id): 199 | ''' 200 | 通过ID定位元素 201 | :param xpath: 202 | :return: 203 | ''' 204 | return self.driver.find_element_by_id(id) 205 | 206 | def find_elements_by_id(self, id): 207 | ''' 208 | 通过ID定位元素 209 | :param xpath: 210 | :return: 211 | ''' 212 | return self.driver.find_elements_by_id(id) 213 | 214 | def find_element_by_class_name(self, class_name): 215 | ''' 216 | 通过class_name定位元素 217 | :param xpath: 218 | :return: 219 | ''' 220 | return self.driver.find_element_by_class_name(class_name) 221 | 222 | def has_cookies_for_current_website(self, account: str = "", create_folder_if_not_exists: bool = True) -> bool: 223 | return os.path.exists( 224 | self.__cookies_path(account, 225 | create_folder_if_not_exists=create_folder_if_not_exists 226 | ) 227 | ) 228 | 229 | def __cookies_path(self, account: str = "", create_folder_if_not_exists: bool = True) -> str: 230 | url_comps = tldextract.extract(self.driver.current_url) 231 | formatted_url = url_comps.domain + '.' + url_comps.suffix + '.' + account 232 | 233 | return os.path.join( 234 | self.cookies_folder_path, 235 | formatted_url + '.pkl' 236 | ) 237 | 238 | def save_cookies(self, account) -> None: 239 | pickle.dump( 240 | self.driver.get_cookies(), 241 | open(self.__cookies_path(account), "wb") 242 | ) 243 | 244 | def load_cookies(self, account) -> None: 245 | ''' 246 | 加载cookies 247 | :return: 248 | ''' 249 | if not self.has_cookies_for_current_website(account): 250 | self.save_cookies(account) 251 | return 252 | 253 | cookies = pickle.load(open(self.__cookies_path(account), "rb")) 254 | 255 | for cookie in cookies: 256 | self.driver.add_cookie(cookie) 257 | 258 | def refresh(self) -> None: 259 | self.driver.refresh() 260 | 261 | def quit(self): 262 | self.driver.close() 263 | self.driver.quit() -------------------------------------------------------------------------------- /upload/TikTokUploader.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | from typing import DefaultDict, Optional 3 | from selenium.webdriver.common.action_chains import ActionChains 4 | from selenium.webdriver.common.keys import Keys 5 | from selenium.webdriver.support.ui import WebDriverWait 6 | from selenium.webdriver.support import expected_conditions as EC 7 | from selenium.webdriver.common.by import By 8 | import time 9 | from collections import defaultdict 10 | import json 11 | from pathlib import Path 12 | import logging 13 | from Constant import TIKTOK_CONSTANT 14 | from ChromeDriver import ChromeDriver 15 | from AdsChrome import AdsChromeDriver 16 | logging.basicConfig() 17 | 18 | 19 | class TiktokUploader: 20 | def __init__(self, pkl_path: str, account: str, video_path: str, title: str, caption: str, description: str, tags: str, title_tags: str, use_file_title:str, finger_web: str) -> None: 21 | self.account = account 22 | self.video_path = video_path 23 | self.title = title 24 | self.caption = caption 25 | self.description = description 26 | self.tags = tags 27 | self.title_tags = title_tags 28 | self.use_file_title = use_file_title == "true" 29 | current_working_dir = pkl_path 30 | if finger_web == "": 31 | self.browser = ChromeDriver(current_working_dir, current_working_dir) 32 | else: 33 | self.browser = AdsChromeDriver(current_working_dir, current_working_dir, finger_web) 34 | self.logger = logging.getLogger(__name__) 35 | self.logger.setLevel(logging.DEBUG) 36 | 37 | def upload(self): 38 | try: 39 | self.logger.info("step 1: logging....") 40 | self.__login() 41 | self.logger.info("step 2: ready to upload....") 42 | self.__upload() 43 | except Exception as e: 44 | print(e) 45 | self.__quit() 46 | raise 47 | 48 | def __login(self): 49 | self.browser.driver.get(TIKTOK_CONSTANT.TIKTOK_URL) 50 | time.sleep(TIKTOK_CONSTANT.USER_WAITING_TIME) 51 | if self.browser.has_cookies_for_current_website(self.account): 52 | self.browser.load_cookies(self.account) 53 | time.sleep(TIKTOK_CONSTANT.USER_WAITING_TIME) 54 | self.browser.refresh() 55 | else: 56 | self.logger.info('Please sign in and then press enter') 57 | input() 58 | self.browser.get(TIKTOK_CONSTANT.TIKTOK_URL) 59 | time.sleep(TIKTOK_CONSTANT.USER_WAITING_TIME) 60 | self.browser.save_cookies(self.account) 61 | 62 | def __quit(self): 63 | self.browser.quit() 64 | 65 | def __upload(self) -> (bool, Optional[str]): 66 | # 要上传视频的路径 67 | absolute_video_path = self.video_path 68 | 69 | # 打开相应的网页 70 | self.logger.info("step 3: open the tiktok website....") 71 | self.browser.get(TIKTOK_CONSTANT.TIKTOK_UPLOAD_URL) 72 | time.sleep(TIKTOK_CONSTANT.LOAD_TIME) 73 | 74 | # 切换到iframe中 75 | self.browser.switch_to_frame(TIKTOK_CONSTANT.IFRAME) 76 | time.sleep(TIKTOK_CONSTANT.USER_WAITING_TIME) 77 | 78 | # 上传视频 79 | self.logger.info("step 4: uploading the video....") 80 | self.browser.find_element_by_xpath(TIKTOK_CONSTANT.INPUT_FILE_VIDEO)\ 81 | .send_keys(absolute_video_path) 82 | self.browser.driver.implicitly_wait(15) 83 | 84 | self.logger.debug('Attached video {}'.format(self.video_path)) 85 | time.sleep(TIKTOK_CONSTANT.WAIT_TIME) 86 | 87 | # 填写Caption 88 | self.logger.info("step 5: fill in the caption....") 89 | self.browser.find_element_by_xpath(TIKTOK_CONSTANT.Caption).send_keys(Keys.CONTROL, 'a') 90 | self.browser.find_element_by_xpath(TIKTOK_CONSTANT.Caption).send_keys(Keys.DELETE) 91 | 92 | caption = self.browser.find_element_by_xpath(TIKTOK_CONSTANT.Caption) 93 | self.browser.driver.implicitly_wait(10) 94 | ActionChains(self.browser.driver).move_to_element(caption).click(caption).perform() 95 | time.sleep(1) 96 | if self.use_file_title: 97 | pass 98 | else: 99 | ActionChains(self.browser.driver).send_keys(self.caption).perform() 100 | 101 | tags = self.tags.split("#") 102 | for tag in tags: 103 | ActionChains(self.browser.driver).send_keys("#" + tag.strip()).perform() 104 | time.sleep(2) 105 | ActionChains(self.browser.driver).send_keys(Keys.RETURN).perform() 106 | time.sleep(1) 107 | time.sleep(5) 108 | 109 | self.browser.driver.execute_script("window.scrollTo(150, 300);") 110 | time.sleep(5) 111 | 112 | # 允许审查 113 | self.logger.info("step 6: check the copyright....") 114 | check = self.browser.find_element_by_class_name(TIKTOK_CONSTANT.ALLOW_CHECK) 115 | ActionChains(self.browser.driver).move_to_element(check).click(check).perform() 116 | time.sleep(TIKTOK_CONSTANT.CHECK_TIME) 117 | 118 | # 发布 119 | self.logger.info("step 7: post the video....") 120 | post = WebDriverWait(self.browser.driver, 100).until( 121 | EC.visibility_of_element_located( 122 | (By.XPATH, '//*[@id="root"]/div/div/div/div/div[2]/div[2]/div[7]/div[2]/button'))) 123 | post.click() 124 | 125 | time.sleep(TIKTOK_CONSTANT.WAIT_TIME) 126 | self.logger.info("step 8: finished....") 127 | self.browser.quit() 128 | 129 | def __write_field(self, xpath, dic): 130 | self.browser.find_element_by_xpath(xpath).click() 131 | self.browser.find_element_by_xpath(xpath).clear() 132 | time.sleep(TIKTOK_CONSTANT.USER_WAITING_TIME) 133 | 134 | self.browser.find_element_by_xpath(xpath).send_keys(dic['caption']) 135 | time.sleep(TIKTOK_CONSTANT.USER_WAITING_TIME) 136 | 137 | # # todo: 将@和#的部分也做出来 138 | # tags = dic['tags'].split(",") 139 | # self.browser.find_element_by_xpath(xpath).send_keys(" ") 140 | # if len(tags) > 0: 141 | # for tag in tags: 142 | # self.browser.find_element_by_xpath(xpath).send_keys("#" + tag) 143 | # self.browser.find_element_by_xpath(xpath).send_keys(Keys.ENTER) 144 | # time.sleep(TIKTOK_CONSTANT.USER_WAITING_TIME) 145 | 146 | 147 | if __name__ == "__main__": 148 | # parser = argparse.ArgumentParser() 149 | # parser.add_argument("--video", 150 | # help='Path to the video file', 151 | # required=True) 152 | # parser.add_argument("-t", 153 | # "--thumbnail", 154 | # help='Path to the thumbnail image', ) 155 | # parser.add_argument("--meta", help='Path to the JSON file with metadata') 156 | # args = parser.parse_args() 157 | # uploader = TiktokUploader(args.video, args.meta, args.thumbnail) 158 | uploader = TiktokUploader("jie", "new bag.mp4", "conf.json", None) 159 | uploader.upload() 160 | -------------------------------------------------------------------------------- /upload/YouTubeUploader.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | from typing import DefaultDict, Optional 3 | from selenium.webdriver.common.action_chains import ActionChains 4 | import time 5 | from collections import defaultdict 6 | import json 7 | from pathlib import Path 8 | import logging 9 | from Constant import YOUTUBE_CONSTANT 10 | from ChromeDriver import ChromeDriver 11 | logging.basicConfig() 12 | 13 | 14 | def load_metadata(metadata_json_path: Optional[str] = None) -> DefaultDict[str, str]: 15 | if metadata_json_path is None: 16 | return defaultdict(str) 17 | with open(metadata_json_path, encoding='utf-8') as metadata_json_file: 18 | return defaultdict(str, json.load(metadata_json_file)) 19 | 20 | 21 | class YouTubeUploader: 22 | def __init__(self, root_path: str, account: str, video_path: str, metadata_json_path: Optional[str] = None, thumbnail_path: Optional[str] = None) -> None: 23 | self.account = account 24 | self.video_path = video_path 25 | self.thumbnail_path = thumbnail_path 26 | self.metadata_dict = load_metadata(metadata_json_path) 27 | current_working_dir = root_path 28 | self.browser = ChromeDriver(current_working_dir, current_working_dir) 29 | self.logger = logging.getLogger(__name__) 30 | self.logger.setLevel(logging.DEBUG) 31 | 32 | def upload(self): 33 | try: 34 | self.logger.info("step 1: logging....") 35 | self.__login() 36 | self.logger.info("step 2: ready to upload....") 37 | self.__upload() 38 | except Exception as e: 39 | print(e) 40 | self.__quit() 41 | raise 42 | 43 | def __login(self): 44 | self.browser.driver.get(YOUTUBE_CONSTANT.YOUTUBE_URL) 45 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 46 | if self.browser.has_cookies_for_current_website(self.account): 47 | self.browser.load_cookies(self.account) 48 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 49 | self.browser.refresh() 50 | else: 51 | self.logger.info('Please sign in and then press enter') 52 | input() 53 | self.browser.get(YOUTUBE_CONSTANT.YOUTUBE_URL) 54 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 55 | self.browser.save_cookies(self.account) 56 | 57 | def __quit(self): 58 | self.browser.quit() 59 | 60 | def __upload(self) -> (bool, Optional[str]): 61 | # 要上传视频的路径 62 | absolute_video_path = self.video_path 63 | 64 | # 打开相应的网页 65 | self.logger.info("step 3: open the YOUTUBE website....") 66 | self.browser.get(YOUTUBE_CONSTANT.YOUTUBE_UPLOAD_URL) 67 | time.sleep(YOUTUBE_CONSTANT.LOAD_TIME) 68 | 69 | # 上传视频 70 | self.logger.info("step 4: uploading the video....") 71 | self.browser.find_element_by_xpath(YOUTUBE_CONSTANT.INPUT_FILE_VIDEO)\ 72 | .send_keys(absolute_video_path) 73 | self.browser.driver.implicitly_wait(15) 74 | 75 | self.logger.debug('Attached video {}'.format(self.video_path)) 76 | time.sleep(YOUTUBE_CONSTANT.WAIT_TIME) 77 | 78 | # 填写Caption 79 | self.logger.info("step 5: fill in the title and desc....") 80 | title = self.browser.find_element_by_id(YOUTUBE_CONSTANT.TEXTBOX) 81 | title.clear() 82 | self.browser.driver.implicitly_wait(10) 83 | ActionChains(self.browser.driver).move_to_element(title).click(title).send_keys(self.metadata_dict[self.account + '_title']).perform() 84 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 85 | 86 | desc = self.browser.find_elements_by_id(YOUTUBE_CONSTANT.TEXTBOX)[1] 87 | desc.clear() 88 | self.browser.driver.implicitly_wait(10) 89 | ActionChains(self.browser.driver).move_to_element(desc).click(desc).send_keys(self.metadata_dict[self.account + '_description']).perform() 90 | 91 | self.browser.driver.execute_script("window.scrollTo(150, 300);") 92 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 93 | 94 | # 儿童 95 | self.browser.find_elements_by_id(YOUTUBE_CONSTANT.NOT_MADE_FOR_KIDS_LABEL)[1].click() 96 | time.sleep(YOUTUBE_CONSTANT.LOAD_TIME) 97 | 98 | # 展开加入tag 99 | self.browser.find_element_by_id(YOUTUBE_CONSTANT.TOOGLE_BUTN).click() 100 | self.browser.driver.execute_script("window.scrollTo(150, 800);") 101 | tags = self.browser.find_elements_by_id(YOUTUBE_CONSTANT.TAGS_INPUT)[1] 102 | tags.clear() 103 | self.browser.driver.implicitly_wait(10) 104 | ActionChains(self.browser.driver).move_to_element(tags).click(tags).send_keys(self.metadata_dict[self.account + '_tags']).perform() 105 | time.sleep(YOUTUBE_CONSTANT.WAIT_TIME) 106 | 107 | # # 允许审查 108 | self.browser.find_element_by_id(YOUTUBE_CONSTANT.NEXT_BUTTON).click() 109 | self.logger.debug('Clicked {} one'.format(YOUTUBE_CONSTANT.NEXT_BUTTON)) 110 | time.sleep(YOUTUBE_CONSTANT.WAIT_TIME) 111 | 112 | self.browser.find_element_by_id(YOUTUBE_CONSTANT.NEXT_BUTTON).click() 113 | self.logger.debug('Clicked {} two'.format(YOUTUBE_CONSTANT.NEXT_BUTTON)) 114 | time.sleep(YOUTUBE_CONSTANT.WAIT_TIME) 115 | 116 | self.browser.find_element_by_id(YOUTUBE_CONSTANT.NEXT_BUTTON).click() 117 | self.logger.debug('Clicked {} three'.format(YOUTUBE_CONSTANT.NEXT_BUTTON)) 118 | time.sleep(YOUTUBE_CONSTANT.WAIT_TIME) 119 | 120 | # 公开发布 121 | self.browser.find_element_by_name(YOUTUBE_CONSTANT.PUBLIC_BUTTON).click() 122 | self.logger.debug('Made the video {}'.format(YOUTUBE_CONSTANT.PUBLIC_BUTTON)) 123 | time.sleep(YOUTUBE_CONSTANT.LOAD_TIME) 124 | 125 | done_button = self.browser.find_element_by_id(YOUTUBE_CONSTANT.DONE_BUTTON) 126 | done_button.click() 127 | self.logger.info("step 8: finished....") 128 | time.sleep(YOUTUBE_CONSTANT.WAIT_TIME) 129 | 130 | self.browser.quit() 131 | 132 | def __write_field(self, xpath, dic): 133 | self.browser.find_element_by_xpath(xpath).click() 134 | self.browser.find_element_by_xpath(xpath).clear() 135 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 136 | 137 | self.browser.find_element_by_xpath(xpath).send_keys(dic['caption']) 138 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 139 | 140 | 141 | if __name__ == "__main__": 142 | uploader = YouTubeUploader("jie", "new bag.mp4", "conf.json", None) 143 | uploader.upload() 144 | -------------------------------------------------------------------------------- /upload/YouTubeUploaderLong.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | from typing import DefaultDict, Optional 3 | from selenium.webdriver.common.action_chains import ActionChains 4 | from collections import defaultdict 5 | import json 6 | import logging, os, time 7 | from Constant import YOUTUBE_CONSTANT 8 | from ChromeDriver import ChromeDriver 9 | from HubChromeDriver import HubChromeDriver 10 | logging.basicConfig() 11 | 12 | 13 | class YouTubeUploaderLong: 14 | def __init__(self, pkl_path: str, account: str, video_path: str, title: str, caption: str, description: str, tags: str, title_tags: str, use_file_title: str, finger_web: str) -> None: 15 | self.account = account 16 | self.video_path = video_path 17 | self.title = title 18 | self.caption = caption 19 | self.description = description 20 | self.tags = tags 21 | self.title_tags = title_tags 22 | self.use_file_title = use_file_title == "true" 23 | current_working_dir = pkl_path 24 | if finger_web == "": 25 | self.browser = ChromeDriver(current_working_dir, current_working_dir) 26 | else: 27 | self.browser = HubChromeDriver(current_working_dir, current_working_dir, finger_web) 28 | self.logger = logging.getLogger(__name__) 29 | self.logger.setLevel(logging.DEBUG) 30 | 31 | def upload(self): 32 | try: 33 | self.logger.info("step 1: logging....") 34 | self.__login() 35 | self.logger.info("step 2: ready to upload....") 36 | self.__upload() 37 | except Exception as e: 38 | print(e) 39 | self.__quit() 40 | raise 41 | 42 | def __login(self): 43 | self.browser.driver.get(YOUTUBE_CONSTANT.YOUTUBE_URL) 44 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 45 | if self.browser.has_cookies_for_current_website(self.account): 46 | self.browser.load_cookies(self.account) 47 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 48 | self.browser.refresh() 49 | else: 50 | self.logger.info('Please sign in and then press enter') 51 | input() 52 | self.browser.get(YOUTUBE_CONSTANT.YOUTUBE_URL) 53 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 54 | self.browser.save_cookies(self.account) 55 | 56 | def __quit(self): 57 | self.browser.quit() 58 | 59 | def __upload(self) -> (bool, Optional[str]): 60 | # 要上传视频的路径 61 | absolute_video_path = self.video_path 62 | file_name = self.video_path.split("\\")[-1][:-4] 63 | 64 | # 打开相应的网页 65 | self.logger.info("step 3: open the YOUTUBE website....") 66 | self.browser.get(YOUTUBE_CONSTANT.YOUTUBE_UPLOAD_URL) 67 | time.sleep(YOUTUBE_CONSTANT.LOAD_TIME) 68 | 69 | # 上传视频 70 | self.logger.info("step 4: uploading the video....") 71 | self.browser.find_element_by_xpath(YOUTUBE_CONSTANT.INPUT_FILE_VIDEO)\ 72 | .send_keys(absolute_video_path) 73 | 74 | self.logger.debug('Attached video {}'.format(self.video_path)) 75 | time.sleep(YOUTUBE_CONSTANT.LONG_UPLOAD_TIME) 76 | 77 | # 填写title/desc 78 | self.logger.info("step 5: fill in the title and desc....") 79 | title_ele = self.browser.find_element_by_id(YOUTUBE_CONSTANT.TEXTBOX) 80 | if self.use_file_title: 81 | upload_title = " ".join([file_name, self.title_tags]) 82 | ActionChains(self.browser.driver).move_to_element(title_ele).click(title_ele) 83 | title_ele.clear() 84 | self.browser.driver.implicitly_wait(5) 85 | ActionChains(self.browser.driver).move_to_element(title_ele).click(title_ele).send_keys(upload_title[:min(len(upload_title), 95)]).perform() 86 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 87 | else: 88 | upload_title = " ".join([self.title, self.title_tags]) 89 | ActionChains(self.browser.driver).move_to_element(title_ele).click(title_ele) 90 | title_ele.clear() 91 | self.browser.driver.implicitly_wait(5) 92 | ActionChains(self.browser.driver).move_to_element(title_ele).click(title_ele).send_keys(upload_title[:min(len(upload_title), 95)]).perform() 93 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 94 | 95 | desc_ele = self.browser.find_elements_by_id(YOUTUBE_CONSTANT.TEXTBOX)[1] 96 | ActionChains(self.browser.driver).move_to_element(desc_ele).click(desc_ele) 97 | desc_ele.clear() 98 | self.browser.driver.implicitly_wait(5) 99 | 100 | ActionChains(self.browser.driver).move_to_element(desc_ele).click(desc_ele).send_keys(self.description).perform() 101 | 102 | # 选择缩略图 103 | pic_path = absolute_video_path.replace(r".mp4", r".jpg") 104 | if os.path.exists(pic_path): 105 | self.logger.info("step 5: fill in the cover of video....") 106 | self.browser.find_element_by_id(YOUTUBE_CONSTANT.PICTURE_BUTTON).send_keys(pic_path) 107 | self.browser.driver.implicitly_wait(5) 108 | 109 | self.browser.driver.execute_script("window.scrollTo(150, 900);") 110 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 111 | 112 | # 儿童 113 | self.browser.find_elements_by_id(YOUTUBE_CONSTANT.NOT_MADE_FOR_KIDS_LABEL)[1].click() 114 | time.sleep(YOUTUBE_CONSTANT.LOAD_TIME) 115 | 116 | # 展开加入tag 117 | self.browser.driver.execute_script("window.scrollTo(250, 900);") 118 | self.browser.find_element_by_id(YOUTUBE_CONSTANT.TOOGLE_BUTN).click() 119 | tags = self.browser.find_elements_by_id(YOUTUBE_CONSTANT.TAGS_INPUT)[1] 120 | tags.clear() 121 | self.browser.driver.implicitly_wait(10) 122 | ActionChains(self.browser.driver).move_to_element(tags).click(tags).send_keys(self.tags).perform() 123 | time.sleep(YOUTUBE_CONSTANT.LONG_WAIT_TIME) 124 | 125 | # # 允许审查 126 | self.browser.find_element_by_id(YOUTUBE_CONSTANT.NEXT_BUTTON).click() 127 | self.logger.debug('Clicked {} one'.format(YOUTUBE_CONSTANT.NEXT_BUTTON)) 128 | time.sleep(YOUTUBE_CONSTANT.LONG_WAIT_TIME) 129 | 130 | self.browser.find_element_by_id(YOUTUBE_CONSTANT.NEXT_BUTTON).click() 131 | self.logger.debug('Clicked {} two'.format(YOUTUBE_CONSTANT.NEXT_BUTTON)) 132 | time.sleep(YOUTUBE_CONSTANT.LONG_WAIT_TIME) 133 | 134 | self.browser.find_element_by_id(YOUTUBE_CONSTANT.NEXT_BUTTON).click() 135 | self.logger.debug('Clicked {} three'.format(YOUTUBE_CONSTANT.NEXT_BUTTON)) 136 | time.sleep(YOUTUBE_CONSTANT.LONG_WAIT_TIME) 137 | 138 | # 公开发布 139 | self.browser.find_element_by_name(YOUTUBE_CONSTANT.PUBLIC_BUTTON).click() 140 | self.logger.debug('Made the video {}'.format(YOUTUBE_CONSTANT.PUBLIC_BUTTON)) 141 | time.sleep(YOUTUBE_CONSTANT.LOAD_TIME) 142 | 143 | done_button = self.browser.find_element_by_id(YOUTUBE_CONSTANT.DONE_BUTTON) 144 | done_button.click() 145 | self.logger.info("step 8: finished....") 146 | time.sleep(YOUTUBE_CONSTANT.LONG_WAIT_TIME) 147 | 148 | self.browser.quit() 149 | 150 | def __write_field(self, xpath, dic): 151 | self.browser.find_element_by_xpath(xpath).click() 152 | self.browser.find_element_by_xpath(xpath).clear() 153 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 154 | 155 | self.browser.find_element_by_xpath(xpath).send_keys(dic['caption']) 156 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 157 | 158 | 159 | if __name__ == "__main__": 160 | uploader = YouTubeUploaderLong("jie", "new bag.mp4", "conf.json", None) 161 | uploader.upload() 162 | -------------------------------------------------------------------------------- /upload/YouTubeUploaderShort.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | import os.path 3 | from typing import DefaultDict, Optional 4 | from selenium.webdriver.common.action_chains import ActionChains 5 | import time 6 | from collections import defaultdict 7 | import json 8 | import logging 9 | from Constant import YOUTUBE_CONSTANT 10 | from HubChromeDriver import HubChromeDriver 11 | from ChromeDriver import ChromeDriver 12 | logging.basicConfig() 13 | 14 | 15 | class YouTubeUploaderShort: 16 | def __init__(self, pkl_path: str, account: str, video_path: str, title: str, caption: str, description: str, tags: str, title_tags: str, use_file_title:str, finger_web:str) -> None: 17 | self.account = account 18 | self.video_path = video_path 19 | self.title = title 20 | self.caption = caption 21 | self.description = description 22 | self.tags = tags 23 | self.title_tags = title_tags 24 | self.use_file_title = use_file_title == "true" 25 | current_working_dir = pkl_path 26 | if finger_web == "": 27 | self.browser = ChromeDriver(current_working_dir, current_working_dir) 28 | else: 29 | self.browser = HubChromeDriver(current_working_dir, current_working_dir, finger_web) 30 | self.logger = logging.getLogger(__name__) 31 | self.logger.setLevel(logging.DEBUG) 32 | 33 | def upload(self): 34 | try: 35 | self.logger.info("step 1: logging....") 36 | self.__login() 37 | self.logger.info("step 2: ready to upload....") 38 | self.__upload() 39 | except Exception as e: 40 | print(e) 41 | self.__quit() 42 | raise 43 | 44 | def __login(self): 45 | self.browser.driver.get(YOUTUBE_CONSTANT.YOUTUBE_URL) 46 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 47 | if self.browser.has_cookies_for_current_website(self.account): 48 | self.browser.load_cookies(self.account) 49 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 50 | self.browser.refresh() 51 | else: 52 | self.logger.info('Please sign in and then press enter') 53 | input() 54 | self.browser.get(YOUTUBE_CONSTANT.YOUTUBE_URL) 55 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 56 | self.browser.save_cookies(self.account) 57 | 58 | def __quit(self): 59 | self.browser.quit() 60 | 61 | def __upload(self) -> (bool, Optional[str]): 62 | # 要上传视频的路径 63 | absolute_video_path = self.video_path 64 | file_name = self.video_path.split("\\")[-1][:-4] 65 | 66 | # 打开相应的网页 67 | self.logger.info("step 3: open the YOUTUBE website....") 68 | self.browser.get(YOUTUBE_CONSTANT.YOUTUBE_UPLOAD_URL) 69 | time.sleep(YOUTUBE_CONSTANT.LOAD_TIME) 70 | 71 | # 上传视频 72 | self.logger.info("step 4: uploading the video....") 73 | self.browser.find_element_by_xpath(YOUTUBE_CONSTANT.INPUT_FILE_VIDEO)\ 74 | .send_keys(absolute_video_path) 75 | self.browser.driver.implicitly_wait(15) 76 | 77 | self.logger.debug('Attached video {}'.format(self.video_path)) 78 | time.sleep(YOUTUBE_CONSTANT.SHORT_WAIT_TIME) 79 | time.sleep(YOUTUBE_CONSTANT.SHORT_WAIT_TIME) 80 | 81 | # 填写标题+title_tag、描述 82 | self.logger.info("step 5: fill in the title and desc....") 83 | title_ele = self.browser.find_element_by_id(YOUTUBE_CONSTANT.TEXTBOX) 84 | if self.use_file_title: 85 | upload_title = " ".join([file_name, self.title_tags]) 86 | ActionChains(self.browser.driver).move_to_element(title_ele).click(title_ele) 87 | title_ele.clear() 88 | self.browser.driver.implicitly_wait(5) 89 | ActionChains(self.browser.driver).move_to_element(title_ele).click(title_ele).send_keys(upload_title[:min(len(upload_title), 95)]).perform() 90 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 91 | else: 92 | upload_title = " ".join([self.title, self.title_tags]) 93 | ActionChains(self.browser.driver).move_to_element(title_ele).click(title_ele) 94 | title_ele.clear() 95 | self.browser.driver.implicitly_wait(5) 96 | ActionChains(self.browser.driver).move_to_element(title_ele).click(title_ele).send_keys(upload_title[:min(len(upload_title), 95)]).perform() 97 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 98 | 99 | desc_ele = self.browser.find_elements_by_id(YOUTUBE_CONSTANT.TEXTBOX)[1] 100 | ActionChains(self.browser.driver).move_to_element(desc_ele).click(desc_ele) 101 | self.browser.driver.implicitly_wait(5) 102 | ActionChains(self.browser.driver).move_to_element(desc_ele).click(desc_ele).send_keys(self.description).perform() 103 | desc_ele.clear() 104 | self.browser.driver.implicitly_wait(5) 105 | 106 | # 选择缩略图 107 | pic_path = absolute_video_path.replace(r".mp4", r".jpg") 108 | if os.path.exists(pic_path): 109 | self.logger.info("step 5: fill in the cover of video....") 110 | self.browser.find_element_by_id(YOUTUBE_CONSTANT.PICTURE_BUTTON).send_keys(pic_path) 111 | self.browser.driver.implicitly_wait(5) 112 | 113 | self.browser.driver.execute_script("window.scrollTo(150, 900);") 114 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 115 | 116 | # 儿童 117 | self.browser.find_elements_by_id(YOUTUBE_CONSTANT.NOT_MADE_FOR_KIDS_LABEL)[1].click() 118 | time.sleep(YOUTUBE_CONSTANT.CONFIRM_TIME) 119 | 120 | # 展开加入tag 121 | self.browser.driver.execute_script("window.scrollTo(250, 900);") 122 | self.browser.find_element_by_id(YOUTUBE_CONSTANT.TOOGLE_BUTN).click() 123 | tags = self.browser.find_elements_by_id(YOUTUBE_CONSTANT.TAGS_INPUT)[1] 124 | tags.clear() 125 | self.browser.driver.implicitly_wait(10) 126 | ActionChains(self.browser.driver).move_to_element(tags).click(tags).send_keys(self.tags).perform() 127 | time.sleep(YOUTUBE_CONSTANT.CONFIRM_TIME) 128 | 129 | # # 允许审查 130 | self.browser.find_element_by_id(YOUTUBE_CONSTANT.NEXT_BUTTON).click() 131 | self.logger.debug('Clicked {} one'.format(YOUTUBE_CONSTANT.NEXT_BUTTON)) 132 | time.sleep(YOUTUBE_CONSTANT.SHORT_WAIT_TIME) 133 | 134 | self.browser.find_element_by_id(YOUTUBE_CONSTANT.NEXT_BUTTON).click() 135 | self.logger.debug('Clicked {} two'.format(YOUTUBE_CONSTANT.NEXT_BUTTON)) 136 | time.sleep(YOUTUBE_CONSTANT.SHORT_WAIT_TIME) 137 | 138 | self.browser.find_element_by_id(YOUTUBE_CONSTANT.NEXT_BUTTON).click() 139 | self.logger.debug('Clicked {} three'.format(YOUTUBE_CONSTANT.NEXT_BUTTON)) 140 | time.sleep(YOUTUBE_CONSTANT.SHORT_WAIT_TIME) 141 | 142 | # 公开发布 143 | self.browser.find_element_by_name(YOUTUBE_CONSTANT.PUBLIC_BUTTON).click() 144 | self.logger.debug('Made the video {}'.format(YOUTUBE_CONSTANT.PUBLIC_BUTTON)) 145 | time.sleep(YOUTUBE_CONSTANT.LOAD_TIME) 146 | 147 | done_button = self.browser.find_element_by_id(YOUTUBE_CONSTANT.DONE_BUTTON) 148 | done_button.click() 149 | self.logger.info("step 8: finished....") 150 | time.sleep(YOUTUBE_CONSTANT.SHORT_WAIT_TIME) 151 | 152 | self.browser.quit() 153 | 154 | 155 | def __write_field(self, xpath, dic): 156 | self.browser.find_element_by_xpath(xpath).click() 157 | self.browser.find_element_by_xpath(xpath).clear() 158 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 159 | 160 | self.browser.find_element_by_xpath(xpath).send_keys(dic['caption']) 161 | time.sleep(YOUTUBE_CONSTANT.USER_WAITING_TIME) 162 | 163 | 164 | if __name__ == "__main__": 165 | uploader = YouTubeUploaderShort("jie", "new bag.mp4", "conf.json", None) 166 | uploader.upload() 167 | -------------------------------------------------------------------------------- /upload/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FlamingJay/homework/168f205345857c56f285ad6a77dadaef64337897/upload/__init__.py -------------------------------------------------------------------------------- /upload/conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "jie_caption": "this is TikTok caption #gift #fpy", 3 | "jie_title": "this is Youtube title", 4 | "jie_description": "this is TikTok desc", 5 | "jie_tags": "animal,funny," 6 | } --------------------------------------------------------------------------------