pookz@stme commited on
Commit
bd47096
·
1 Parent(s): 34d738a

1. add cli support.

Browse files

2. change LOCAL_CHROME_PATH to conf file
3. change douyin gen cookie url

README.MD CHANGED
@@ -65,9 +65,56 @@ meta_file 内容(content):
65
  这位勇敢的男子为了心爱之人每天坚守 🥺❤️‍🩹
66
  #坚持不懈 #爱情执着 #奋斗使者 #短视频
67
  ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  ---
69
 
70
- ### 2. 抖音
 
 
71
  <img src="media/show/pdf3.gif" alt="douyin show" width="500"/>
72
 
73
  使用playwright模拟浏览器行为
@@ -104,7 +151,7 @@ generate_schedule_time_next_day 默认从第二天开始(此举为避免选择
104
 
105
  ---
106
 
107
- ### 3. 视频号
108
  使用playwright模拟浏览器行为
109
  1. 准备视频目录结构
110
  2. cookie获取:get_tencent_cookie.py 扫码登录
@@ -126,7 +173,7 @@ generate_schedule_time_next_day 默认从第二天开始(此举为避免选择
126
  ---
127
 
128
 
129
- ### 4. 小红书
130
  该实现,借助ReaJason的[xhs](https://github.com/ReaJason/xhs),再次感谢。
131
 
132
  1. 目录结构同上
@@ -166,7 +213,7 @@ https://github.com/requireCool/stealth.min.js
166
 
167
  ---
168
 
169
- ### 5. bilibili
170
  该实现,借助biliup的[biliup-rs](https://github.com/biliup/biliup-rs),再次感谢。
171
  1. 准备视频目录结构
172
  2. cookie获取:`biliup.exe -u account.json login` 选项你喜欢的登录方式
@@ -184,7 +231,7 @@ bilibili cookie 长期有效(至少我运行2年以来是这样的)
184
 
185
  ---
186
 
187
- ### 6. tiktok
188
  使用playwright模拟浏览器行为(Simulating Browser Behavior with playwright)
189
  1. 准备视频目录结构(Prepare the video directory structure)
190
  2. cookie获取(generate your cookie):get_tk_cookie.py
 
65
  这位勇敢的男子为了心爱之人每天坚守 🥺❤️‍🩹
66
  #坚持不懈 #爱情执着 #奋斗使者 #短视频
67
  ```
68
+
69
+ ### Usage
70
+ 1. 设置conf 文件中的 `LOCAL_CHROME_PATH`(在douyin 或者视频号可能出现chromium 不兼容的各种问题,建议设置本地的chrome)
71
+ 2. 这里分割出来3条路
72
+ - 可自行研究源码,免费、任意 穿插在自己的项目中
73
+ - 可参考下面的各个平台的使用指南,`examples`文件夹中有各种示例代码
74
+ - 使用cli 简易使用(支持tiktok douyin 视频号)
75
+
76
+ #### cli 用法
77
+ ```python
78
+ python cli_main.py <platform> <account_name> <action: upload, login> [options]
79
+ ```
80
+ 查看详细的参数说明使用:
81
+ ```python
82
+ python cli_main.py -h
83
+ ```
84
+ ```python
85
+ usage: cli_main.py [-h] platform account_name action ...
86
+
87
+ Upload video to multiple social-media.
88
+
89
+ positional arguments:
90
+ platform Choose social-media platform: douyin tencent tiktok
91
+ account_name Account name for the platform: xiaoA
92
+ action Choose action
93
+ upload upload operation
94
+ login login operation
95
+ watch watch operation
96
+
97
+ options:
98
+ -h, --help show this help message and exit
99
+
100
+ ```
101
+ 示例
102
+ ```python
103
+ python cli_main.py douyin test login
104
+ douyin平台,账号名为test,动作为login
105
+
106
+ python douyin test upload "C:\Users\duperdog\Videos\2023-11-07_05-27-44 - 这位少女如梦中仙... .mp4" -pt 0
107
+ douyin平台, 账号名为test, 动作为upload, 视频文件(需对应的meta文件,详见上), 发布方式(pt):0 立即发布
108
+
109
+ python douyin test upload "C:\Users\superdog\Videos\2023-11-07_05-27-44 - 这位少女如梦中仙... .mp4" -pt 1 -t "2024-6-14 12:00"
110
+ douyin平台, 账号名为test, 动作为upload, 视频文件, 发布方式(pt):1 定时发布, 发布时间(t): 2024-6-14 12:00
111
+ ```
112
+
113
  ---
114
 
115
+ ## 各平台详细说明
116
+
117
+ ### 1. 抖音
118
  <img src="media/show/pdf3.gif" alt="douyin show" width="500"/>
119
 
120
  使用playwright模拟浏览器行为
 
151
 
152
  ---
153
 
154
+ ### 2. 视频号
155
  使用playwright模拟浏览器行为
156
  1. 准备视频目录结构
157
  2. cookie获取:get_tencent_cookie.py 扫码登录
 
173
  ---
174
 
175
 
176
+ ### 3. 小红书
177
  该实现,借助ReaJason的[xhs](https://github.com/ReaJason/xhs),再次感谢。
178
 
179
  1. 目录结构同上
 
213
 
214
  ---
215
 
216
+ ### 4. bilibili
217
  该实现,借助biliup的[biliup-rs](https://github.com/biliup/biliup-rs),再次感谢。
218
  1. 准备视频目录结构
219
  2. cookie获取:`biliup.exe -u account.json login` 选项你喜欢的登录方式
 
231
 
232
  ---
233
 
234
+ ### 5. tiktok
235
  使用playwright模拟浏览器行为(Simulating Browser Behavior with playwright)
236
  1. 准备视频目录结构(Prepare the video directory structure)
237
  2. cookie获取(generate your cookie):get_tk_cookie.py
cli_main.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+ import asyncio
3
+ from datetime import datetime
4
+ from os.path import exists
5
+ from pathlib import Path
6
+
7
+ from conf import BASE_DIR
8
+ from douyin_uploader.main import douyin_setup, DouYinVideo
9
+ from tencent_uploader.main import weixin_setup, TencentVideo
10
+ from tk_uploader.main import tiktok_setup, TiktokVideo
11
+ from utils.base_social_media import get_supported_social_media, get_cli_action, SOCIAL_MEDIA_DOUYIN, \
12
+ SOCIAL_MEDIA_TENCENT, SOCIAL_MEDIA_TIKTOK
13
+ from utils.constant import TencentZoneTypes
14
+ from utils.files_times import get_title_and_hashtags
15
+
16
+
17
+ def parse_schedule(schedule_raw):
18
+ if schedule_raw:
19
+ schedule = datetime.strptime(schedule_raw, '%Y-%m-%d %H:%M')
20
+ else:
21
+ schedule = None
22
+ return schedule
23
+
24
+
25
+ async def main():
26
+ # 主解析器
27
+ parser = argparse.ArgumentParser(description="Upload video to multiple social-media.")
28
+ parser.add_argument("platform", metavar='platform', choices=get_supported_social_media(), help="Choose social-media platform: douyin tencent tiktok")
29
+
30
+ parser.add_argument("account_name", type=str, help="Account name for the platform: xiaoA")
31
+ subparsers = parser.add_subparsers(dest="action", metavar='action', help="Choose action", required=True)
32
+
33
+ actions = get_cli_action()
34
+ for action in actions:
35
+ action_parser = subparsers.add_parser(action, help=f'{action} operation')
36
+ if action == 'login':
37
+ # Login 不需要额外参数
38
+ continue
39
+ elif action == 'upload':
40
+ action_parser.add_argument("video_file", help="Path to the Video file")
41
+ action_parser.add_argument("-pt", "--publish_type", type=int, choices=[0, 1],
42
+ help="0 for immediate, 1 for scheduled", default=0)
43
+ action_parser.add_argument('-t', '--schedule', help='Schedule UTC time in %Y-%m-%d %H:%M format')
44
+
45
+ # 解析命令行参数
46
+ args = parser.parse_args()
47
+ # 参数校验
48
+ if args.action == 'upload':
49
+ if not exists(args.video_file):
50
+ raise FileNotFoundError(f'Could not find the video file at {args["video_file"]}')
51
+ if args.publish_type == 1 and not args.schedule:
52
+ parser.error("The schedule must must be specified for scheduled publishing.")
53
+
54
+ account_file = Path(BASE_DIR / "cookies" / f"{args.platform}_{args.account_name}.json")
55
+ account_file.parent.mkdir(exist_ok=True)
56
+
57
+ # 根据 action 处理不同的逻辑
58
+ if args.action == 'login':
59
+ print(f"Logging in with account {args.account_name} on platform {args.platform}")
60
+ if args.platform == SOCIAL_MEDIA_DOUYIN:
61
+ await douyin_setup(str(account_file), handle=True)
62
+ elif args.platform == SOCIAL_MEDIA_TIKTOK:
63
+ await tiktok_setup(str(account_file), handle=True)
64
+ elif args.platform == SOCIAL_MEDIA_TENCENT:
65
+ await weixin_setup(str(account_file), handle=True)
66
+ elif args.action == 'upload':
67
+ title, tags = get_title_and_hashtags(args.video_file)
68
+ video_file = args.video_file
69
+
70
+ if args.publish_type == 0:
71
+ print("Uploading immediately...")
72
+ publish_date = 0
73
+ else:
74
+ print("Scheduling videos...")
75
+ publish_date = parse_schedule(args.schedule)
76
+
77
+ if args.platform == SOCIAL_MEDIA_DOUYIN:
78
+ await douyin_setup(account_file, handle=False)
79
+ app = DouYinVideo(title, video_file, tags, publish_date, account_file)
80
+ elif args.platform == SOCIAL_MEDIA_TIKTOK:
81
+ await tiktok_setup(account_file, handle=True)
82
+ app = TiktokVideo(title, video_file, tags, publish_date, account_file)
83
+ elif args.platform == SOCIAL_MEDIA_TENCENT:
84
+ await weixin_setup(account_file, handle=True)
85
+ category = TencentZoneTypes.LIFESTYLE.value # 标记原创需要否则不需要传
86
+ app = TencentVideo(title, video_file, tags, publish_date, account_file, category)
87
+
88
+ await app.main()
89
+
90
+
91
+ if __name__ == "__main__":
92
+ asyncio.run(main())
conf.py CHANGED
@@ -1,4 +1,5 @@
1
  from pathlib import Path
2
 
3
  BASE_DIR = Path(__file__).parent.resolve()
4
- XHS_SERVER = "http://127.0.0.1:11901"
 
 
1
  from pathlib import Path
2
 
3
  BASE_DIR = Path(__file__).parent.resolve()
4
+ XHS_SERVER = "http://127.0.0.1:11901"
5
+ LOCAL_CHROME_PATH = "" # change me necessary! for example C:/Program Files/Google/Chrome/Application/chrome.exe
douyin_uploader/main.py CHANGED
@@ -6,6 +6,8 @@ from playwright.async_api import Playwright, async_playwright
6
  import os
7
  import asyncio
8
 
 
 
9
 
10
  async def cookie_auth(account_file):
11
  async with async_playwright() as playwright:
@@ -45,7 +47,7 @@ async def douyin_cookie_gen(account_file):
45
  context = await browser.new_context() # Pass any options
46
  # Pause the page, and start recording manually.
47
  page = await context.new_page()
48
- await page.goto("https://www.douyin.com/")
49
  await page.pause()
50
  # 点击调试器的继续,保存cookie
51
  await context.storage_state(path=account_file)
@@ -59,7 +61,7 @@ class DouYinVideo(object):
59
  self.publish_date = publish_date
60
  self.account_file = account_file
61
  self.date_format = '%Y年%m月%d日 %H:%M'
62
- self.local_executable_path = "" # change me
63
 
64
  async def set_schedule_time_douyin(self, page, publish_date):
65
  # 选择包含特定文本内容的 label 元素
@@ -163,7 +165,8 @@ class DouYinVideo(object):
163
  await page.keyboard.press("Control+KeyA")
164
  await page.keyboard.press("Delete")
165
  await page.keyboard.type("杭州市")
166
- await asyncio.sleep(1)
 
167
  await page.locator('div[role="listbox"] [role="option"]').first.click()
168
 
169
  # 頭條/西瓜
 
6
  import os
7
  import asyncio
8
 
9
+ from conf import LOCAL_CHROME_PATH
10
+
11
 
12
  async def cookie_auth(account_file):
13
  async with async_playwright() as playwright:
 
47
  context = await browser.new_context() # Pass any options
48
  # Pause the page, and start recording manually.
49
  page = await context.new_page()
50
+ await page.goto("https://creator.douyin.com/")
51
  await page.pause()
52
  # 点击调试器的继续,保存cookie
53
  await context.storage_state(path=account_file)
 
61
  self.publish_date = publish_date
62
  self.account_file = account_file
63
  self.date_format = '%Y年%m月%d日 %H:%M'
64
+ self.local_executable_path = LOCAL_CHROME_PATH
65
 
66
  async def set_schedule_time_douyin(self, page, publish_date):
67
  # 选择包含特定文本内容的 label 元素
 
165
  await page.keyboard.press("Control+KeyA")
166
  await page.keyboard.press("Delete")
167
  await page.keyboard.type("杭州市")
168
+ # await asyncio.sleep(1)
169
+ await page.wait_for_timeout(1000)
170
  await page.locator('div[role="listbox"] [role="option"]').first.click()
171
 
172
  # 頭條/西瓜
tencent_uploader/main.py CHANGED
@@ -6,6 +6,7 @@ from playwright.async_api import Playwright, async_playwright
6
  import os
7
  import asyncio
8
 
 
9
  from utils.files_times import get_absolute_path
10
 
11
 
@@ -83,7 +84,7 @@ class TencentVideo(object):
83
  self.publish_date = publish_date
84
  self.account_file = account_file
85
  self.category = category
86
- self.local_executable_path = "" # change me necessary!
87
 
88
  async def set_schedule_time_tencent(self, page, publish_date):
89
  print("click schedule")
 
6
  import os
7
  import asyncio
8
 
9
+ from conf import LOCAL_CHROME_PATH
10
  from utils.files_times import get_absolute_path
11
 
12
 
 
84
  self.publish_date = publish_date
85
  self.account_file = account_file
86
  self.category = category
87
+ self.local_executable_path = LOCAL_CHROME_PATH
88
 
89
  async def set_schedule_time_tencent(self, page, publish_date):
90
  print("click schedule")
utils/base_social_media.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List
2
+
3
+ SOCIAL_MEDIA_DOUYIN = "douyin"
4
+ SOCIAL_MEDIA_TENCENT = "tencent"
5
+ SOCIAL_MEDIA_TIKTOK = "tiktok"
6
+ SOCIAL_MEDIA_BILIBILI = "bilibili"
7
+
8
+
9
+ def get_supported_social_media() -> List[str]:
10
+ return [SOCIAL_MEDIA_DOUYIN, SOCIAL_MEDIA_TENCENT, SOCIAL_MEDIA_TIKTOK]
11
+
12
+
13
+ def get_cli_action() -> List[str]:
14
+ return ["upload", "login", "watch"]