├── .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 |
4 |
5 |
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 |
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 | }
--------------------------------------------------------------------------------