├── .DS_Store
├── .gitignore
├── EveryDay
├── .DS_Store
├── AboutAI.py
├── AboutEveryDay.py
├── AboutJoke.py
├── AboutTime.py
├── AboutWeather.py
├── raw
│ └── city.json
└── utils
│ ├── .DS_Store
│ ├── chat_utils.py
│ └── date_utils.py
├── LICENSE
├── Python执行JS总结
├── .idea
│ ├── exec_js_summary.iml
│ ├── misc.xml
│ ├── modules.xml
│ └── workspace.xml
├── js2py_demo.py
├── js_code.py
├── node_demo.py
├── norm.js
├── norm1.js
├── py_exec_js_demo.py
└── py_v8_demo.py
├── README.md
├── android
├── .DS_Store
├── appium_demo
│ ├── config_nantian.py
│ └── nantian_keyboard.py
├── delete_build.py
├── replace_apk_resource
│ ├── fileutils.py
│ ├── readme.md
│ ├── replace_apk_resource.py
│ └── ziputils.py
└── replace_apk_resource_pro
│ ├── .DS_Store
│ ├── apktool.jar
│ ├── file_utils.py
│ ├── jarsigner
│ ├── logo_white.png
│ ├── readme.md
│ └── replace_source.py
├── raw
└── qr.jpeg
├── video_auto
├── .DS_Store
├── 二次剪辑
│ ├── .DS_Store
│ ├── .idea
│ │ ├── misc.xml
│ │ ├── modules.xml
│ │ ├── vcs.xml
│ │ ├── workspace.xml
│ │ └── 视频剪辑.iml
│ ├── fonts
│ │ ├── .DS_Store
│ │ └── STHeiti Medium.ttc
│ ├── source
│ │ └── .DS_Store
│ ├── util
│ │ ├── .DS_Store
│ │ ├── file_utils.py
│ │ ├── img_utils.py
│ │ └── video_utils.py
│ └── 二次创作.py
├── 制作GIF视频
│ ├── .DS_Store
│ ├── gif_make_video.py
│ ├── gifs
│ │ └── .DS_Store
│ └── utils
│ │ ├── file_utils.py
│ │ ├── image_utils.py
│ │ ├── math_utils.py
│ │ └── video_utils.py
└── 卡点视频
│ ├── .DS_Store
│ ├── images
│ └── .DS_Store
│ ├── readme.md
│ ├── utils
│ ├── .DS_Store
│ ├── file_utils.py
│ └── image_utils.py
│ └── video_cut_cv2.py
├── 一键升级
├── .DS_Store
├── auto_rar.au3
└── auto_rar.exe
├── 发送邮件
├── .idea
│ ├── misc.xml
│ ├── modules.xml
│ ├── vcs.xml
│ ├── workspace.xml
│ └── 发送邮件.iml
├── attachments
│ └── report.png
├── email_by_smtplib.py
├── email_by_yagmail.py
└── email_by_zmail.py
├── 图片视频九宫格
├── .idea
│ ├── inspectionProfiles
│ │ └── Project_Default.xml
│ ├── misc.xml
│ ├── modules.xml
│ ├── vcs.xml
│ ├── workspace.xml
│ └── 图片视频九宫格.iml
├── ninecell_image.py
├── ninecell_video.py
├── output
│ └── result.mp4
├── raw
│ ├── pic.jpg
│ └── 至今无人超越的3个动作.mp4
└── utils
│ ├── file_utils.py
│ ├── math_utils.py
│ └── video_utils.py
├── 截图快捷翻译
├── .idea
│ ├── misc.xml
│ ├── modules.xml
│ ├── vcs.xml
│ ├── workspace.xml
│ └── 截图快捷翻译.iml
├── main.py
└── trans.py
├── 批处理脚本
├── del_all_build.bat
├── del_system_gc.bat
├── documents_classifying.bat
├── raw
│ └── Bat_To_Exe_Converter.exe
├── run_python_loop.bat
└── 提交代码
│ └── 提交代码.bat
├── 批量生成手机号码
├── .DS_Store
├── generate_area_phone_util.py
└── utils
│ ├── .DS_Store
│ └── addr_utils.py
├── 朋友圈防折叠
├── .DS_Store
├── NoCollapseInput
│ ├── .gitignore
│ ├── .idea
│ │ ├── codeStyles
│ │ │ └── Project.xml
│ │ ├── gradle.xml
│ │ ├── misc.xml
│ │ └── runConfigurations.xml
│ ├── .project
│ ├── .settings
│ │ └── org.eclipse.buildship.core.prefs
│ ├── .vscode
│ │ └── settings.json
│ ├── app
│ │ ├── .classpath
│ │ ├── .gitignore
│ │ ├── .project
│ │ ├── .settings
│ │ │ └── org.eclipse.buildship.core.prefs
│ │ ├── build.gradle
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ ├── androidTest
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── xingag
│ │ │ │ └── preventrubbishapp
│ │ │ │ └── ExampleInstrumentedTest.java
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ │ └── com
│ │ │ │ │ └── xingag
│ │ │ │ │ ├── preventrubbishapp
│ │ │ │ │ ├── App.java
│ │ │ │ │ └── MainActivity.java
│ │ │ │ │ ├── service
│ │ │ │ │ ├── BaseService.java
│ │ │ │ │ └── PreventService.java
│ │ │ │ │ └── utils
│ │ │ │ │ ├── AccessibilityUtil.java
│ │ │ │ │ └── AppUtil.java
│ │ │ └── res
│ │ │ │ ├── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ │ │ ├── drawable
│ │ │ │ ├── btn_bg.xml
│ │ │ │ └── ic_launcher_background.xml
│ │ │ │ ├── layout
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── dialog_bottom_alertmsg.xml
│ │ │ │ ├── item_app_select.xml
│ │ │ │ └── title_bar.xml
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_back.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_sure.png
│ │ │ │ ├── values
│ │ │ │ ├── colors.xml
│ │ │ │ ├── strings.xml
│ │ │ │ └── styles.xml
│ │ │ │ └── xml
│ │ │ │ └── accessibility_settings.xml
│ │ │ └── test
│ │ │ └── java
│ │ │ └── com
│ │ │ └── xingag
│ │ │ └── preventrubbishapp
│ │ │ └── ExampleUnitTest.java
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ ├── readme.md
│ └── settings.gradle
└── 防折叠服务.apk
├── 视频特殊处理.py
└── 音频处理
└── 音频尾部处理.py
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 | .pytest_cache/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 | local_settings.py
57 | db.sqlite3
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # Environments
85 | .env
86 | .venv
87 | env/
88 | venv/
89 | ENV/
90 | env.bak/
91 | venv.bak/
92 |
93 | # Spyder project settings
94 | .spyderproject
95 | .spyproject
96 |
97 | # Rope project settings
98 | .ropeproject
99 |
100 | # mkdocs documentation
101 | /site
102 |
103 | # mypy
104 | .mypy_cache/
105 | .DS_Store
106 |
--------------------------------------------------------------------------------
/EveryDay/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/EveryDay/.DS_Store
--------------------------------------------------------------------------------
/EveryDay/AboutAI.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: About_AI.py
12 | @time: 4/18/19 19:55
13 | @description:百度AI语音合成
14 | """
15 |
16 | # 依赖:pip3 install baidu-aip
17 |
18 | from aip import AipSpeech
19 |
20 | """ 你的 APPID AK SK """
21 | APP_ID = '你的APP_ID'
22 | API_KEY = '你的API_KEY'
23 | SECRET_KEY = '你的SECRET_KEY'
24 |
25 | client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)
26 |
27 |
28 | def gene_mp3(content, filename):
29 | # 百度声音
30 | result = client.synthesis(content, 'zh', 1, {
31 | 'vol': 7,
32 | 'spd': 4,
33 | 'per': 4,
34 |
35 | })
36 |
37 | # 识别正确返回语音二进制 错误则返回dict 参照下面错误码
38 | if not isinstance(result, dict):
39 | with open('%s.mp3' % filename, 'wb') as f:
40 | f.write(result)
41 |
--------------------------------------------------------------------------------
/EveryDay/AboutEveryDay.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: About_EveryDay.py
12 | @time: 4/18/19 19:50
13 | @description:每天运行
14 | """
15 |
16 | from AboutTime import *
17 | from AboutWeather import *
18 | from AboutAI import *
19 | from AboutJoke import *
20 | import itchat
21 | from utils.chat_utils import *
22 | from utils.date_utils import *
23 | import schedule
24 |
25 |
26 | # 依赖: pip3 install schedule
27 |
28 |
29 | class EveryDay(object):
30 | def __init__(self):
31 | self.time_start = ''
32 | self.time_end = ''
33 | self.weather = ''
34 |
35 | # mp3名称
36 | self.file_name = 'everyday_%s' % get_today_ymd()
37 |
38 | def start(self):
39 | self.time_start, self.time_end = get_time()
40 | self.weather = get_weather()
41 |
42 | self.__run()
43 |
44 | def __run(self):
45 | # 最后要发送的内容
46 | result_word = self.time_start + "\n\n" + self.weather + "\n" + self.time_end
47 | print(result_word)
48 |
49 | # 语音内容
50 | result_mp3 = (
51 | self.time_start + "。\n\n" + self.weather + "\n" + self.time_end + "你们工作吧,我要休息了!我们明天再见!拜拜~").replace(
52 | ',', '。')
53 |
54 | gene_mp3(result_mp3, self.file_name)
55 |
56 | # self.send_msg(result_word)
57 |
58 | self.send_word('微信上某位联系人', result_word)
59 |
60 | self.send_file('微信上某位联系人')
61 |
62 | def send_msg(self, result_word):
63 | # ===================================================
64 | target_peoples = [
65 | '微信上某位联系人',
66 | '微信上某位联系人2'
67 | ]
68 |
69 | for target in target_peoples:
70 | send_word_to_person(target, result_word)
71 | send_file_to_person(target, '%s.mp3' % self.file_name)
72 |
73 | print('单聊信息发送完成')
74 |
75 | # ===================================================
76 | target_group_names = [
77 | '微信上某个群聊1',
78 | '微信上某个群聊2',
79 | '微信上某个群聊3',
80 | '微信上某个群聊4'
81 | ]
82 |
83 | for target2 in target_group_names:
84 | send_word_to_group(target2, result_word)
85 | send_file_to_group(target2, '%s.mp3' % self.file_name)
86 |
87 | print('全部内容发送完成!')
88 |
89 | def send_word(self, target_name, result_word):
90 | """
91 | 发送文字
92 | :param result_word:
93 | :return:
94 | """
95 | send_word_to_person(target_name, result_word)
96 |
97 | def send_file(self, target_name):
98 | """
99 | 发送文件
100 | :return:
101 | """
102 | send_file_to_person(target_name, '%s.mp3' % self.file_name)
103 |
104 |
105 | if __name__ == '__main__':
106 | every_day = EveryDay()
107 |
108 | # 准备调用itchat发送图片
109 | itchat.auto_login(hotReload=True)
110 | # itchat.auto_login(hotReload=True,enableCmdQR=2)
111 |
112 | # 每天早上8:00执行
113 | # 发送Norm信息【一天一次】
114 | schedule.every().day.at("08:00").do(every_day.start)
115 |
116 | # 每隔3分钟执行一次
117 | # schedule.every(3).minutes.do(every_day.start)
118 |
119 | # 分段发几个笑话【时间段:整点发送】
120 | target_name = '微信发送的对象'
121 | schedule.every().day.at("09:30").do(every_day.send_word, target_name, gene_joke(0))
122 | schedule.every().day.at("10:30").do(every_day.send_word, target_name, gene_joke(1))
123 | schedule.every().day.at("11:30").do(every_day.send_word, target_name, gene_joke(2))
124 | schedule.every().day.at("12:30").do(every_day.send_word, target_name, gene_joke(3))
125 | schedule.every().day.at("13:30").do(every_day.send_word, target_name, gene_joke(4))
126 | schedule.every().day.at("14:30").do(every_day.send_word, target_name, gene_joke(5))
127 | schedule.every().day.at("15:30").do(every_day.send_word, target_name, gene_joke(6))
128 | schedule.every().day.at("16:30").do(every_day.send_word, target_name, gene_joke(7))
129 | schedule.every().day.at("17:30").do(every_day.send_word, target_name, gene_joke(8))
130 | schedule.every().day.at("18:30").do(every_day.send_word, target_name, gene_joke(9))
131 | schedule.every().day.at("19:30").do(every_day.send_word, target_name, gene_joke(10))
132 | schedule.every().day.at("20:30").do(every_day.send_word, target_name, gene_joke(11))
133 | schedule.every().day.at("21:30").do(every_day.send_word, target_name, gene_joke(12))
134 | schedule.every().day.at("22:30").do(every_day.send_word, target_name, gene_joke(13))
135 | schedule.every().day.at("23:30").do(every_day.send_word, target_name, gene_joke(14))
136 |
137 | while True:
138 | schedule.run_pending()
139 | time.sleep(1)
140 |
--------------------------------------------------------------------------------
/EveryDay/AboutJoke.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: AboutJoke.py
12 | @time: 4/19/19 16:30
13 | @description:TODO
14 | """
15 |
16 | import requests
17 | from random import randint
18 | import re
19 | import schedule
20 | import time
21 |
22 |
23 | def gene_joke(index):
24 | joke_url = 'http://api.laifudao.com/open/xiaohua.json'
25 |
26 | resp = requests.get(joke_url)
27 |
28 | resp_content = resp.json()
29 |
30 | # 每天20个笑话
31 | # index = randint(0, len(resp_content))
32 |
33 | joke_random = resp_content[index]
34 |
35 | # 标题
36 | joke_title = joke_random['title']
37 |
38 | # 笑话
39 | joke_content = re.sub('/*
', r'\n', joke_random['content'])
40 |
41 | # print(joke_content)
42 |
43 | return "***某某童鞋,你家老公给你准备了笑话一则***\n"+joke_content.strip()
44 |
45 |
--------------------------------------------------------------------------------
/EveryDay/AboutTime.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: AboutTime.py
12 | @time: 4/18/19 19:07
13 | @description:关于时间
14 | """
15 |
16 | import time
17 | import calendar
18 |
19 | week = ['日', '一', '二', '三', '四', '五', '六']
20 |
21 |
22 | def get_time():
23 | """
24 | 获取时间
25 | :return:
26 | """
27 | # time.strftime("%F-%u-%j")
28 | # F:年月日;u:星期几;j:一年第几天
29 |
30 | # 时间数据
31 | # [2019, 4, 18, 4, 108]
32 | time_datas = [int(i) for i in time.strftime("%F-%u-%j").split('-')]
33 |
34 | # 年
35 | year = time_datas[0]
36 | # 月
37 | month = time_datas[1]
38 | # 日
39 | day = time_datas[2]
40 | # 星期几
41 | week_d = week[time_datas[3]]
42 | # 一年中的第几天
43 | time_d = time_datas[4]
44 |
45 | # 判断是否是闰年;闰年:366天,平年:365
46 | if calendar.isleap(year):
47 | percent = round(time_d * 100 / 366, 2)
48 | else:
49 | percent = round(time_d * 100 / 365, 2)
50 |
51 | # time_content = '今天是:%d 年 %d 月 %d 日,星期%s\n报告主人!今年 %.2f%% 时间已流逝。' % (year, month, day, week_d, percent)
52 | time_content = ('各位帅哥美女们,大家早上好!\n\n今天是:%d 年 %d 月 %d 日,星期%s~' % (year, month, day, week_d),
53 | '\n\n报告主人!报告主人!报告主人!\n\n%d 年,岁月已陪伴主人走过:%.2f%%。' % (year, percent) + "余下时光,请主人们温柔以待哦!")
54 | return time_content
55 |
56 | # print(get_time())
57 |
--------------------------------------------------------------------------------
/EveryDay/AboutWeather.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: AboutWeather.py
12 | @time: 4/18/19 19:32
13 | @description:天气
14 | """
15 |
16 | import requests
17 |
18 |
19 | def get_weather():
20 | url = 'https://www.tianqiapi.com/api/?version=v1&cityid=101280601'
21 |
22 | result = requests.get(url).json().get('data')[0]
23 |
24 | city = '深圳天气:'
25 |
26 | # 天气
27 | wea = result.get('wea')
28 |
29 | # 空气质量
30 | air_level = result.get('air_level')
31 |
32 | # 最高温度
33 | tem_high = result.get('tem1')
34 |
35 | # 最低温度
36 | tem_low = result.get('tem2')
37 |
38 | result = city + str(wea) + ",最低温度:" + str(tem_low) + ",最高温度:" + str(tem_high) + ",空气质量:" + str(air_level) + "。"
39 |
40 | return result
41 |
--------------------------------------------------------------------------------
/EveryDay/utils/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/EveryDay/utils/.DS_Store
--------------------------------------------------------------------------------
/EveryDay/utils/chat_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: chat_utils.py
12 | @time: 4/18/19 20:21
13 | @description:itchat 工具类
14 | """
15 | import itchat
16 |
17 |
18 | # 注意:默认获取到的群聊:保存在通讯录中的群聊
19 |
20 |
21 | def send_word_helper(content):
22 | """
23 | 向文件传输助手发送文字
24 | :param content:
25 | :return:
26 | """
27 | itchat.send(content, toUserName='filehelper')
28 |
29 |
30 | def send_word_to_person(name, content):
31 | """
32 | 发送消息给某个人
33 | :param name:
34 | :param content:
35 | :return:
36 | """
37 | users = itchat.search_friends(name)
38 | userName = users[0]['UserName']
39 | itchat.send(content, toUserName=userName)
40 |
41 |
42 | def send_file_to_person(name, filename):
43 | """
44 | 发送文件给某个人
45 | :param name:
46 | :param filename:
47 | :return:
48 | """
49 | users = itchat.search_friends(name)
50 | userName = users[0]['UserName']
51 | itchat.send_file(filename, toUserName=userName)
52 |
53 |
54 | def send_word_to_group(group_name, word):
55 | """
56 | 群聊中发送文字
57 | :param group_name:
58 | :param content:
59 | :return:
60 | """
61 | # 获取所有群聊
62 | # 显示所有的群聊信息,默认是返回保存到通讯录中的群聊
63 | itchat.dump_login_status()
64 |
65 | target_rooms = itchat.search_chatrooms(name=group_name)
66 |
67 | if target_rooms and len(target_rooms) > 0:
68 | target_rooms[0].send_msg(word)
69 | else:
70 | print('【发送文字】抱歉,不存在这个群聊:%s' % group_name)
71 |
72 |
73 | def send_file_to_group(group_name, file_name):
74 | """
75 | 发送文件到群聊
76 | :param group_name:
77 | :param file:
78 | :return:
79 | """
80 | itchat.dump_login_status()
81 |
82 | target_rooms = itchat.search_chatrooms(name=group_name)
83 |
84 | if target_rooms and len(target_rooms) > 0:
85 | target_rooms[0].send_file(file_name)
86 | else:
87 | print('【发送文件】抱歉,不存在这个群聊:%s' % group_name)
88 |
--------------------------------------------------------------------------------
/EveryDay/utils/date_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: date_utils.py
12 | @time: 4/16/19 23:59
13 | @description:日期工具类
14 | """
15 |
16 | import datetime
17 |
18 |
19 | def get_today():
20 | # 获取今天的日期
21 | #
22 | today = datetime.date.today()
23 | return today
24 |
25 |
26 | def get_today_day():
27 | """
28 | 获取年月日
29 | :return: int类型
30 | """
31 | # datetime.datetime.now().year
32 | # datetime.datetime.now().month
33 | return datetime.datetime.now().day
34 |
35 |
36 | def get_today_ymd():
37 | """
38 | 获取年月日
39 | :return: int类型
40 | """
41 | year = datetime.datetime.now().year
42 | month = datetime.datetime.now().month
43 | day = datetime.datetime.now().day
44 |
45 | return "%d_%d_%d" % (year, month, day)
46 |
47 |
48 | def get_current_time():
49 | # 当前时间
50 | current_time = datetime.datetime.now()
51 | # 时
52 | current_hour = current_time.hour
53 | # 分
54 | current_minute = current_time.minute
55 | # 秒
56 | current_second = current_time.second
57 |
58 | # print('当前时间:%d:%d:%d' % (current_hour, current_minute, current_second))
59 |
60 | return current_hour, current_minute, current_second
61 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/Python执行JS总结/.idea/exec_js_summary.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Python执行JS总结/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Python执行JS总结/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Python执行JS总结/js2py_demo.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: js2py_demo.py
12 | @time: 2020-07-22 17:49
13 | @description:js2py
14 | """
15 |
16 | # 依赖
17 | # pip3 install js2py
18 |
19 | import js2py
20 |
21 | from js_code import *
22 |
23 |
24 | def test_simple():
25 | """
26 | 简单
27 | :return:
28 | """
29 | # 将js代码转为python
30 | add = js2py.eval_js(js_simple())
31 |
32 | # 当做python函数调用
33 | result = add(1, 2)
34 |
35 | print(result)
36 |
37 |
38 | def test_js_from_file():
39 | """
40 | 从文件中读取js进行执行
41 | :return:
42 | """
43 |
44 | # 从文件中读取js代码
45 | js_content = js_from_file('./norm.js')
46 |
47 | # 使用获取上下js2py生成一个上下文环境
48 | context = js2py.EvalJs()
49 |
50 | # 执行整段JS代码
51 | context.execute(js_content)
52 |
53 | # 使用context调用具体的函数
54 | result = context.add(1, 2)
55 |
56 | print(result)
57 |
58 |
59 | if __name__ == '__main__':
60 | # test_simple()
61 | test_js_from_file()
62 |
--------------------------------------------------------------------------------
/Python执行JS总结/js_code.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: js_code.py
12 | @time: 2020-07-22 13:30
13 | @description:待执行的JS代码
14 | """
15 |
16 |
17 | def js_simple():
18 | """
19 | 返回简单的js代码
20 | :return:
21 | """
22 | return """
23 | function add(num1 , num2){
24 | return num1 + num2;
25 | }
26 | """
27 |
28 |
29 | def js_from_file(file_name):
30 | """
31 | 读取js文件
32 | :return:
33 | """
34 | with open(file_name, 'r', encoding='UTF-8') as file:
35 | result = file.read()
36 |
37 | return result
38 |
--------------------------------------------------------------------------------
/Python执行JS总结/node_demo.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: node_demo.py
12 | @time: 2020-07-22 22:19
13 | @description:使用node命令调用js
14 | """
15 |
16 | import os
17 |
18 |
19 | def func1():
20 | # 组成调用js的命令
21 | cmd = 'node -e "require(\\"%s\\").init(%s,%s)"' % ('./norm1', 3, 5)
22 |
23 | pipeline = os.popen(cmd)
24 |
25 | # 读取结果
26 | result = pipeline.read()
27 |
28 | print('结果是:', result)
29 |
30 |
31 | if __name__ == '__main__':
32 | func1()
33 |
--------------------------------------------------------------------------------
/Python执行JS总结/norm.js:
--------------------------------------------------------------------------------
1 | //计算两个数的和
2 | function add(num1, num2) {
3 | return num1 + num2;
4 | }
5 |
--------------------------------------------------------------------------------
/Python执行JS总结/norm1.js:
--------------------------------------------------------------------------------
1 | //计算两个数的和
2 | function add(num1, num2) {
3 | return num1 + num2;
4 | }
5 |
6 | //新增一个导出函数(node方式)
7 | module.exports.init = function (arg1, arg2) {
8 | //调用函数
9 | console.log(add(arg1, arg2));
10 | };
--------------------------------------------------------------------------------
/Python执行JS总结/py_exec_js_demo.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: py_exec_js_demo.py
12 | @time: 2020-07-22 13:24
13 | @description:使用PyExecJS调用JS代码
14 | """
15 |
16 | # 依赖
17 | # pip3 install PyExecJS
18 |
19 | import execjs
20 |
21 | from js_code import *
22 |
23 | # 1、简单的JS字符串
24 | # 编译并加载 js 文件内容,方便执行里面的方法
25 | context = execjs.compile(js_simple())
26 |
27 | # 使用call()函数调用js内部函数
28 | result = context.call("add", 2, 3)
29 |
30 | print(result)
31 |
32 | # 2、js文件
33 | # 编译加载js字符串
34 | context1 = execjs.compile(js_from_file('./norm.js'))
35 |
36 | # 调用js代码中的add()方法,参数为2和3
37 | result1 = context1.call("add", 2, 3)
38 |
39 | print(result1)
40 |
--------------------------------------------------------------------------------
/Python执行JS总结/py_v8_demo.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: py_v8_demo.py
12 | @time: 2020-07-22 14:53
13 | @description:PyV8调用JS
14 | """
15 |
16 | import PyV8
17 | from js_code import js_from_file
18 |
19 | with PyV8.JSContext() as ctx:
20 | ctx.eval(js_from_file('./norm.js'))
21 |
22 | # 调用js函数,指定参数
23 | ctx.locals.add(1, 2)
24 |
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tools Python
2 |
3 | ## 详细教程
4 |
5 | 如果想查看详细教程,请关注微信公众号:**AirPython**
6 |
7 |
8 |
9 | 
10 |
11 |
12 |
13 | ## 文章列表
14 |
15 | * [制作截图快捷翻译](./截图快捷翻译)
16 | * [朋友圈防折叠](./朋友圈防折叠/)
17 | * [批量生成手机号码](./批量生成手机号码)
18 | * [短视频二次创作](./video_auto/二次剪辑)
19 | * [使用 Python 制作 GIF 动画视频](./video_auto/制作GIF视频/)
20 | * [使用 Python 制作一个卡点视频](./video_auto/卡点视频/readme.md)
21 | * [替换 `apk` 的资源并重新打包](./android/replace_apk_resource/readme.md)
22 | * [替换 `apk` 的资源并重新打包全解析](./android/replace_apk_resource_pro/readme.md)
23 | * [`appium` 配合压力测试 `app` 应用](./android/appium_demo/)
24 | * [每天固定时间发送天气、笑话给微信个人和群,并部署到云服务器](./EveryDay/)
25 | * [视频特殊处理,保证唯一性](./视频特殊处理.py)
26 | * [发送邮件的几种方式](./发送邮件)
27 | * [Python 调用 JS 的 4 种方式](./Python执行JS总结)
28 | * [批处理脚本](./批处理脚本)
29 | * [生成九宫格视频+生成九宫格朋友圈图片](./图片视频九宫格/)
30 |
31 |
--------------------------------------------------------------------------------
/android/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/android/.DS_Store
--------------------------------------------------------------------------------
/android/appium_demo/config_nantian.py:
--------------------------------------------------------------------------------
1 | # !/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: config.py
12 | @time: 1/14/19 22:20
13 | @description:配置文件
14 | """
15 |
16 | import os
17 |
18 | PLATFORM = 'Android'
19 |
20 | DEVICE_NAME = 'BP8910_S'
21 |
22 | DRIVER = 'automationName2'
23 |
24 | # APP包名
25 | APP_PACKAGE = 'com.nantian.home'
26 |
27 | # 入口类名
28 | APP_ACTIVITY = 'com.van.view.MainActivity'
29 |
30 | ANDROID_VERSION = '5.1.1'
31 |
32 | # 权限授予
33 | AUTO_GRANT_PERMISSIONS = True
34 |
35 | # ========================================
36 |
37 | # Appium地址
38 | DRIVER_SERVER = 'http://localhost:4723/wd/hub'
39 |
40 | # 等待元素加载时间
41 | TIMEOUT = 60
42 |
43 |
44 | # ===========================================
45 |
46 |
47 |
--------------------------------------------------------------------------------
/android/appium_demo/nantian_keyboard.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: nantian_keyboard.py
12 | @time: 1/18/19 10:05
13 | @description:设备App测试密码键盘
14 | """
15 |
16 | from config_nantian import *
17 | from appium import webdriver
18 | from selenium.webdriver.support.ui import WebDriverWait
19 | from selenium.webdriver.common.by import By
20 | from selenium.webdriver.support import expected_conditions as EC
21 | import time
22 | from element_utils import *
23 | from random import randint
24 |
25 |
26 | class Keyboard(object):
27 |
28 | def __init__(self):
29 | self.caps = {
30 | 'automationName': DRIVER,
31 | 'platformName': PLATFORM,
32 | 'deviceName': DEVICE_NAME,
33 | 'appPackage': APP_PACKAGE,
34 | 'appActivity': APP_ACTIVITY,
35 | 'platformVersion': ANDROID_VERSION,
36 | 'autoGrantPermissions': AUTO_GRANT_PERMISSIONS,
37 | 'unicodeKeyboard': True,
38 | 'resetKeyboard': True
39 | }
40 | self.driver = webdriver.Remote(DRIVER_SERVER, self.caps)
41 | self.wait = WebDriverWait(self.driver, TIMEOUT)
42 |
43 | def login_and_web(self):
44 | """
45 | 输入密码和信息交互
46 | :return:
47 | """
48 | while True:
49 | keyboard_input_element = is_element_exist(self.driver, 'com.nantian.home:id/etPassword')
50 | web_sure_element = is_element_exist(self.driver, 'com.nantian.home:id/web_btnOK')
51 | web_cancel_element = is_element_exist(self.driver, 'com.nantian.home:id/btnCancel')
52 |
53 | # 输入框
54 | if keyboard_input_element:
55 | print('找到元素,准备输入密码')
56 | keyboard_input_element.send_keys('123456')
57 | # Web页面
58 | elif web_sure_element:
59 | print('找到元素,准备点击确认按钮,或者取消按钮')
60 | web_sure_element.click() if randint(0, 1) == 0 else web_cancel_element.click()
61 | else:
62 | print('没有找到元素,继续')
63 | time.sleep(1)
64 |
65 | def run(self):
66 | self.login_and_web()
67 |
68 |
69 | if __name__ == '__main__':
70 | keyboard = Keyboard()
71 | keyboard.run()
72 |
--------------------------------------------------------------------------------
/android/delete_build.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: delete_build.py
12 | @time: 11/14/18 16:33
13 | @description:删除build文件夹
14 | """
15 |
16 | import os
17 |
18 | # 待删除的目录
19 | path = './'
20 |
21 |
22 | def get_file_name(file_full_path):
23 | """
24 | :param file_full_path: 文件的完整路径
25 | :return: 上层父目录、文件名【带后缀】、文件名【不带后缀】、文件后缀
26 | """
27 | # 上层父目录、文件名【带后缀】
28 | (filepath, tempfilename) = os.path.split(file_full_path)
29 |
30 | # 文件名【不带后缀】,文件名
31 | (filename, extension) = os.path.splitext(tempfilename)
32 |
33 | return (filepath, tempfilename, filename, extension)
34 |
35 |
36 | def remove_dir(dir_path):
37 | print("删除文件夹的目录是:"+dir_path)
38 |
39 | # 如果是空文件夹
40 | if not os.listdir(dir_path):
41 | os.removedirs(dir_path)
42 |
43 | for root, dirs, files in os.walk(dir_path, topdown=False):
44 | for name in files:
45 | os.remove(os.path.join(root, name))
46 | for name in dirs:
47 | os.rmdir(os.path.join(root, name))
48 |
49 |
50 | def traverse_files(path):
51 | """
52 | 遍历
53 | :param path:
54 | :return:
55 | """
56 | for item in os.scandir(path):
57 |
58 | file_name = get_file_name(item.path)[2]
59 | # 判断是文件夹还是文件
60 | if item.is_dir():
61 | # print(item.path)
62 | # 删除build文件夹
63 | if file_name == 'build':
64 | remove_dir(item.path)
65 | else:
66 | traverse_files(item.path)
67 |
68 |
69 | traverse_files(path)
70 |
--------------------------------------------------------------------------------
/android/replace_apk_resource/fileutils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: fileutils.py
12 | @time: 2018/9/13 17:44
13 | @description:文件工具
14 | """
15 | import os
16 |
17 |
18 | def rename_file_suffix(name, suffix):
19 | """
20 | 更改文件的后缀名
21 | :param name: 源文件名称
22 | :param suffix: 新的后缀名
23 | :return: 新的文件名称
24 | """
25 | base = os.path.splitext(name)[0]
26 | apk_name = base + suffix
27 | os.rename(name, apk_name)
28 | return apk_name
29 |
--------------------------------------------------------------------------------
/android/replace_apk_resource/readme.md:
--------------------------------------------------------------------------------
1 | # 利用 `Python` 去更换 `apk` 资源
2 |
3 | ### 思路
4 |
5 | 1. 解压 `apk` 包。
6 | 2. 删除源 `apk` 的签名文件夹 - `META_INF/`
7 | 3. 把需要更新的资源文件替换进去,然后压缩成新的 `apk` 包
8 | 4. 利用签名文件 `*.keystore` ,对上面的 `apk` 进行重新签名
9 | 5. 利用 `sdk` 中的 `zipalign` 工具进行对齐
10 | 6. 使用 `jarsigner` 对产生的 `apk` 进行验证
11 |
12 |
13 |
14 |
15 | ### 注意
16 |
17 | * 保证签名文件、`apk` 在脚本文件同目录下
18 |
19 | * 更新资源的时候,必须指定需要被替换的绝对路径
20 |
21 | * 生成压缩文件 `*.apk` 的时候,必须将脚本 `ziputils.py` 添加到压缩根目录执行脚本
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/android/replace_apk_resource/replace_apk_resource.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 |
3 | """
4 | @version: v1.0
5 | @author: xag
6 | @license: Apache Licence
7 | @contact: xinganguo@gmail.com
8 | @site: http://www.xingag.top
9 | @software: PyCharm
10 | @file: ReplaceApkResource.py
11 | @time: 2018/9/13 12:24
12 | @description:利用python替换apk的资源后重新打包
13 | """
14 |
15 | import shutil
16 | import os
17 | import zipfile
18 | import fileutils
19 |
20 | # 设置签名信息
21 | keystore_path = "**.keystore"
22 | keystore_pass = "**"
23 | keystore_alias = "**"
24 | keystore_alias_pass = "**"
25 |
26 | # 脚本根目录
27 | root = os.getcwd()
28 |
29 | # 创建一个临时目录
30 | tmp_path = root + "/tmp/"
31 |
32 | # 传入apk的名称,并更改后缀名为zip
33 | apk_name_temp = input('请输入apk的名称:')
34 |
35 | apk_name = fileutils.rename_file_suffix(apk_name_temp, 'zip')
36 |
37 | # 保证tmp文件夹目录存在
38 | if not os.path.exists(tmp_path):
39 | os.system("mkdir tmp")
40 |
41 | # copy apk文件到临时文件夹内
42 | shutil.copyfile(apk_name, tmp_path + apk_name)
43 |
44 | print(tmp_path + apk_name)
45 |
46 | tmp_path_all = tmp_path + apk_name
47 |
48 | # 获取压缩包路径
49 | azip = zipfile.ZipFile(tmp_path_all)
50 |
51 |
52 | # 解压zip文件【到指定目录下】
53 | azip.extractall(tmp_path)
54 |
55 | # 注意:此处需要关闭对tmp_path的引用
56 | azip.close()
57 |
58 | # 删除签名信息和源zip【apk】
59 | if os.path.exists(tmp_path + 'META-INF/'):
60 | shutil.rmtree(tmp_path + 'META-INF')
61 |
62 | if os.path.exists(tmp_path_all):
63 | os.remove(tmp_path_all)
64 |
65 | # 更新资源【图片替换到目标文件当中去】
66 | shutil.copyfile('ic_logo.png', tmp_path + "res\drawable-mdpi-v4\ic_logo.png")
67 |
68 |
69 | # 移动python文件到tmp目录下,然后显示执行这条Python脚本
70 | shutil.copyfile('ziputils.py', r'./tmp/ziputils.py')
71 |
72 | # 切换到./tmp/目录下,执行压缩文件为
73 | os.chdir(tmp_path)
74 | os.system('python ziputils.py')
75 |
76 | # 切换到根目录下
77 | os.chdir(root)
78 |
79 | # 利用keystore文件进行签名
80 | os.system(
81 | "jarsigner -verbose -digestalg SHA1 -sigalg MD5withRSA -keystore " + keystore_path + " -storepass " + keystore_pass + " -signedjar release-sign.apk release-nosign.apk " + keystore_alias + " -keypass " + keystore_alias_pass)
82 |
83 | # 压缩对齐【利用zipalign去对齐应用apk】
84 | os.system(
85 | "D:/Android/sdk/build-tools/28.0.1/zipalign -v 4 release-sign.apk release.apk")
86 |
87 | # 删除临时文件和多余的apk文件
88 | shutil.rmtree(tmp_path)
89 | os.remove('release-nosign.apk')
90 | os.remove('release-sign.apk')
91 |
92 | # 验证签名
93 | os.system("jarsigner -verify -certs release.apk")
94 |
95 | print('恭喜,终于成功啦!')
96 |
--------------------------------------------------------------------------------
/android/replace_apk_resource/ziputils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: ziputils.py
12 | @time: 2018/9/13 14:54
13 | @description:zip 压缩工具
14 | """
15 | import os
16 | import zipfile
17 | from os.path import isfile, isdir, join
18 |
19 |
20 | def dfs_get_zip_file(input_path, result):
21 | files = os.listdir(input_path)
22 | for file in files:
23 | # 排除掉py文件
24 | if file == 'ziputils.py':
25 | continue
26 | if os.path.isdir(input_path + '/' + file):
27 | dfs_get_zip_file(input_path + '/' + file, result)
28 | else:
29 | result.append(input_path + '/' + file)
30 |
31 |
32 | def zip_path(input_path, output_file_full):
33 | """
34 |
35 | :param input_path: 待压缩的文件夹
36 | :param output_file_full: 压缩的完整目录,包含文件名
37 | :return:
38 | """
39 | f = zipfile.ZipFile(output_file_full, 'w', zipfile.ZIP_DEFLATED)
40 | filelists = []
41 | dfs_get_zip_file(input_path, filelists)
42 | for file in filelists:
43 | # 写入到zip目录下
44 | f.write(file)
45 | f.close()
46 | return output_file_full
47 |
48 |
49 | if __name__ == '__main__':
50 |
51 | # 根目录下打包所有文件
52 | zip_path(r"./", r'../release-nosign.apk')
53 |
--------------------------------------------------------------------------------
/android/replace_apk_resource_pro/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/android/replace_apk_resource_pro/.DS_Store
--------------------------------------------------------------------------------
/android/replace_apk_resource_pro/apktool.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/android/replace_apk_resource_pro/apktool.jar
--------------------------------------------------------------------------------
/android/replace_apk_resource_pro/file_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: file_utils.py
12 | @time: 4/25/19 11:17
13 | @description:文件管理器
14 | """
15 |
16 | import os
17 | import shutil
18 |
19 |
20 | def get_current_folder_file(file_type):
21 | """
22 | 通过文件类型,获取到当前目录下的文件 filename = get_current_folder_file('apk')
23 | :param file_type:
24 | :return:
25 | """
26 | # 当前目录下所有的文件
27 | all_files = os.listdir('.')
28 |
29 | # 满足要求的文件列表
30 | target_files = []
31 | for file in all_files:
32 | if file.split('.')[-1] == file_type:
33 | target_files.append(file)
34 |
35 | # 返回第一项
36 | return target_files[0] if len(target_files) > 0 else ""
37 |
38 |
39 | def rename_current_file(file_type, file_name):
40 | """
41 | 重命令文件夹
42 | :param file_type:文件类型
43 | :param file_name:准备替换的名称
44 | :return:
45 | """
46 | file = get_current_folder_file(file_type)
47 |
48 | # 重命令操作
49 | os.rename(os.path.join('./', file), os.path.join('./', file_name))
50 |
51 |
52 | def replace_file(file_from_path, file_to_path):
53 | """
54 | 替换文件
55 | :param file_from_path:待替换的文件
56 | :param file_to_path:待被替换的文件
57 | :return:
58 | """
59 | shutil.copy(file_from_path, file_to_path)
60 |
--------------------------------------------------------------------------------
/android/replace_apk_resource_pro/jarsigner:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/android/replace_apk_resource_pro/jarsigner
--------------------------------------------------------------------------------
/android/replace_apk_resource_pro/logo_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/android/replace_apk_resource_pro/logo_white.png
--------------------------------------------------------------------------------
/android/replace_apk_resource_pro/readme.md:
--------------------------------------------------------------------------------
1 | # 替换 APK 里面的资源
2 |
3 | ## 思路
4 |
5 | 1. 使用 `apktool.jar` 解压 `apk`
6 | 2. 替换资源
7 | 3. 利用 `apktool.jar` 重新打包
8 | 4. 使用 `jarsigner` 对 `apk` 进行重新签名
9 | 5. 打包成 `exe` 可执行文件
10 |
11 |
12 |
13 | ## 解压
14 |
15 | ```
16 | def __unzip_apk(self):
17 | """
18 | 解压当前目录下的apk文件
19 | :return:
20 | """
21 | # 文件名称,包含后缀名
22 | file_name = get_current_folder_file('apk')
23 |
24 | # 文件名称,不包含后缀名
25 | file_name_pre = file_name.split('.')[0]
26 |
27 | os.system('java -jar apktool.jar d %s' % file_name)
28 |
29 | print('第1步:解压成功~')
30 |
31 | return file_name_pre
32 | ```
33 |
34 |
35 |
36 | ## 替换资源
37 |
38 | ```
39 | def __replace_source(self, file_name_pre):
40 | """
41 | 替换资源
42 | @:param file_name_pre 文件夹的名称
43 | :return:
44 | """
45 | print('生成文件夹的名字是:%s' % file_name_pre)
46 |
47 | # 重命令当前目录下的文件
48 | rename_current_file("png", self.file_name)
49 |
50 | # 待替换的完成路径是
51 | logo_file_path = './%s/res/drawable-mdpi/logo_white.png' % file_name_pre
52 |
53 | # 开始替换文件
54 | replace_file('./%s' % self.file_name, logo_file_path)
55 |
56 | print('第2步:替换资源图片成功~')
57 | ```
58 |
59 |
60 |
61 | ## 重新打包
62 |
63 | ```
64 | def __rezip_apk(self, folder_name):
65 | """
66 | 重新打包成apk
67 | @:param folder_name 文件夹的名称 source
68 | :return:
69 | """
70 |
71 | # 重新打包成apk
72 | os.system('java -jar apktool.jar b %s -o %s' % (folder_name, self.target_apk_name))
73 |
74 | # 删除临时文件夹
75 | shutil.rmtree('./%s/' % folder_name)
76 |
77 | print('第3步:重新打包成功~')
78 | ```
79 |
80 |
81 |
82 | ## 重新签名
83 |
84 | ```
85 | def __re_sign(self):
86 | """
87 | 重新签名
88 | :return:
89 | """
90 | # 重新签名
91 | cmd = 'jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore **.keystore -storepass ** %s **' % self.target_apk_name
92 | p = Popen(cmd, stdout=PIPE, stdin=PIPE, stderr=STDOUT, shell=True)
93 |
94 | # 输入参数
95 | p.communicate(input=b'nantian')
96 | print('第4步:重新签名成功~')
97 | ```
98 |
99 |
100 |
101 | ## 可执行文件
102 |
103 | 使用 [auto-py-to-exe](https://github.com/brentvollebregt/auto-py-to-exe) 生成可执行文件。
104 |
105 |
--------------------------------------------------------------------------------
/android/replace_apk_resource_pro/replace_source.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: replace_source.py
12 | @time: 4/25/19 10:46
13 | @description:替换apk的资源文件
14 | """
15 |
16 | from file_utils import *
17 | import os
18 | from subprocess import Popen, PIPE, STDOUT
19 |
20 |
21 | class ReplaceApkSource(object):
22 | def __init__(self):
23 | self.file_name = 'logo_white.png'
24 |
25 | # 目标apk的名称
26 | self.target_apk_name = 'new.apk'
27 |
28 | def start(self):
29 | # 1.使用apktool.jar解压apk
30 | file_name_pre = self.__unzip_apk()
31 |
32 | # 2.替换资源
33 | self.__replace_source(file_name_pre)
34 |
35 | # 3.重新打包
36 | self.__rezip_apk(file_name_pre)
37 |
38 | # 4.再次签名
39 | self.__re_sign()
40 |
41 | def __unzip_apk(self):
42 | """
43 | 解压当前目录下的apk文件
44 | :return:
45 | """
46 | # 文件名称,包含后缀名
47 | file_name = get_current_folder_file('apk')
48 |
49 | # 文件名称,不包含后缀名
50 | file_name_pre = file_name.split('.')[0]
51 |
52 | os.system('java -jar apktool.jar d %s' % file_name)
53 |
54 | print('第1步:解压成功~')
55 |
56 | return file_name_pre
57 |
58 | def __replace_source(self, file_name_pre):
59 | """
60 | 替换资源
61 | @:param file_name_pre 文件夹的名称
62 | :return:
63 | """
64 | print('生成文件夹的名字是:%s' % file_name_pre)
65 |
66 | # 重命令当前目录下的文件
67 | rename_current_file("png", self.file_name)
68 |
69 | # 待替换的完成路径是
70 | logo_file_path = './%s/res/drawable-mdpi/logo_white.png' % file_name_pre
71 |
72 | # 开始替换文件
73 | replace_file('./%s' % self.file_name, logo_file_path)
74 |
75 | print('第2步:替换资源图片成功~')
76 |
77 | def __rezip_apk(self, folder_name):
78 | """
79 | 重新打包成apk
80 | @:param folder_name 文件夹的名称 source
81 | :return:
82 | """
83 |
84 | # 重新打包成apk
85 | os.system('java -jar apktool.jar b %s -o %s' % (folder_name, self.target_apk_name))
86 |
87 | # 删除临时文件夹
88 | shutil.rmtree('./%s/' % folder_name)
89 |
90 | print('第3步:重新打包成功~')
91 |
92 | def __re_sign(self):
93 | """
94 | 重新签名
95 | :return:
96 | """
97 | # 重新签名
98 | cmd = 'jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore **.keystore -storepass ** %s **' % self.target_apk_name
99 | p = Popen(cmd, stdout=PIPE, stdin=PIPE, stderr=STDOUT, shell=True)
100 |
101 | # 输入参数
102 | p.communicate(input=b'nantian')
103 | print('第4步:重新签名成功~')
104 |
105 |
106 | if __name__ == '__main__':
107 | replace_apk_source = ReplaceApkSource()
108 | replace_apk_source.start()
109 |
110 | print('恭喜!完成操作~')
111 |
--------------------------------------------------------------------------------
/raw/qr.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/raw/qr.jpeg
--------------------------------------------------------------------------------
/video_auto/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/video_auto/.DS_Store
--------------------------------------------------------------------------------
/video_auto/二次剪辑/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/video_auto/二次剪辑/.DS_Store
--------------------------------------------------------------------------------
/video_auto/二次剪辑/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/video_auto/二次剪辑/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/video_auto/二次剪辑/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/video_auto/二次剪辑/.idea/视频剪辑.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/video_auto/二次剪辑/fonts/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/video_auto/二次剪辑/fonts/.DS_Store
--------------------------------------------------------------------------------
/video_auto/二次剪辑/fonts/STHeiti Medium.ttc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/video_auto/二次剪辑/fonts/STHeiti Medium.ttc
--------------------------------------------------------------------------------
/video_auto/二次剪辑/source/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/video_auto/二次剪辑/source/.DS_Store
--------------------------------------------------------------------------------
/video_auto/二次剪辑/util/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/video_auto/二次剪辑/util/.DS_Store
--------------------------------------------------------------------------------
/video_auto/二次剪辑/util/file_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: file_utils.py
12 | @time: 2019-12-29 11:30
13 | @description:TODO
14 | """
15 |
16 | import os
17 |
18 |
19 | def del_temp_file(path):
20 | """
21 | 删除目录下的临时文件
22 | :param path:
23 | :return:
24 | """
25 | # 删除临时文件
26 | g = os.walk(path)
27 |
28 | for path, dir_list, file_list in g:
29 | print(path)
30 | for file_name in file_list:
31 | print(file_name)
32 | if file_name.startswith('temp'):
33 | os.remove(path + file_name)
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/video_auto/二次剪辑/util/img_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: img_utils.py
12 | @time: 2019-12-25 14:23
13 | @description:图片工具类
14 | """
15 | import cv2
16 | from moviepy.video.VideoClip import ImageClip
17 | from moviepy.editor import VideoFileClip
18 |
19 |
20 | def one_pic_to_video(image_path, output_video_path, fps, time):
21 | """
22 | 一张图片合成视频
23 | one_pic_to_video('./../source/1.jpeg', './../source/output.mp4', 25, 10)
24 | :param path: 图片文件路径
25 | :param output_video_path:合成视频的路径
26 | :param fps:帧率
27 | :param time:时长
28 | :return:
29 | """
30 |
31 | image_clip = ImageClip(image_path)
32 | img_width, img_height = image_clip.w, image_clip.h
33 |
34 | # 总共的帧数
35 | frame_num = (int)(fps * time)
36 |
37 | img_size = (int(img_width), int(img_height))
38 |
39 | fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
40 |
41 | video = cv2.VideoWriter(output_video_path, fourcc, fps, img_size)
42 |
43 | for index in range(frame_num):
44 | frame = cv2.imread(image_path)
45 | # 直接缩放到指定大小
46 | frame_suitable = cv2.resize(frame, (img_size[0], img_size[1]), interpolation=cv2.INTER_CUBIC)
47 |
48 | # 把图片写进视频
49 | # 重复写入多少次
50 | video.write(frame_suitable)
51 |
52 | # 释放资源
53 | video.release()
54 |
55 | return VideoFileClip(output_video_path)
56 |
--------------------------------------------------------------------------------
/video_auto/二次剪辑/util/video_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: video_utils.py
12 | @time: 2019-12-26 21:58
13 | @description:视频工具类
14 | """
15 |
16 | from moviepy.editor import *
17 |
18 |
19 | def time_to_hms(seconds_time):
20 | """
21 | 时间转为时分秒
22 | :param seconds_time: 秒数
23 | :return:
24 | """
25 | m, s = divmod(seconds_time, 60)
26 | h, m = divmod(m, 60)
27 | return "%02d:%02d:%02d" % (h, m, s)
28 |
29 |
30 | def synthetic_video(video1_clip, video2_clip2):
31 | """
32 | 合成两段视频,生成视频的宽高以第一段视频为准
33 | :param video1_clip:
34 | :param video2_clip2:
35 | :return:
36 | """
37 | # 最后生成视频的宽、高
38 | width, height = video1_clip.w, video1_clip.h
39 |
40 | # 第二段视频的实际宽、高
41 | video_width, video_height = video2_clip2.w, video2_clip2.h
42 |
43 | # 最第二段视频进行缩放
44 | video_clip1 = video2_clip2.resize((width, width * video_height / video_width))
45 |
46 | # 合成视频的路径
47 | synthetic_video_clip = CompositeVideoClip([video1_clip, video_clip1.set_pos("center")])
48 |
49 | synthetic_video_clip.write_videofile(
50 | './source/temp_synthetic_video.mp4')
51 |
52 | return synthetic_video_clip
53 |
54 |
55 | def video_with_text(synthetic_video_clip, desc_text_clip):
56 | """
57 | 视频中加入文字信息
58 | :param synthetic_video_clip:
59 | :param param:
60 | :return:
61 | """
62 |
63 | video_with_text_clip = CompositeVideoClip([synthetic_video_clip, desc_text_clip.set_start(0)])
64 | video_with_text_clip.write_videofile(
65 | './source/temp_video_with_text.mp4')
66 |
67 | return video_with_text_clip
68 |
69 | # video_with_text_clip.write_videofile(
70 | # './source/temp_video_with_text.mp4')
71 |
72 |
73 | def get_frame_from_video(video_name, frame_time, img_path):
74 | """
75 | 获取视频某个时间的帧图片,保存在本地
76 | :param video_name: 视频路径
77 | :param frame_time: 截取帧的时间位置(s)
78 | :param img_path:生成图片的完整路径
79 | :return:
80 | """
81 | # 秒转为时、分、秒
82 | time_pre = time_to_hms(frame_time)
83 |
84 | os.system('ffmpeg -ss %s -i %s -frames:v 1 %s' % (time_pre, video_name, img_path))
85 |
86 |
87 | def generate_text_clip(text_content, font_params, duration):
88 | """
89 | 产生字幕
90 | :return:
91 | """
92 | # 显示位置
93 | position = font_params.get('position')
94 | position_text = 'top' if position == 0 else 'bottom'
95 |
96 | return TextClip(text_content, font='./fonts/STHeiti Medium.ttc',
97 | fontsize=font_params.get('size'), kerning=font_params.get('kerning'),
98 | color=font_params.get('color')).set_position(("center", 150)).set_duration(duration)
99 |
100 |
--------------------------------------------------------------------------------
/video_auto/二次剪辑/二次创作.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: video_rework.py
12 | @time: 2019-12-25 15:47
13 | @description:抖音视频二次创作
14 | """
15 |
16 | from moviepy.video import fx
17 |
18 | from util.img_utils import *
19 | from util.video_utils import *
20 | from util.file_utils import *
21 |
22 |
23 | class Video_Re(object):
24 |
25 | def __init__(self, video_raw_path, desc):
26 | # 原始素材
27 | self.video_raw_path = video_raw_path
28 |
29 | # 描述信息
30 | self.desc = desc
31 |
32 | # 视频和音频
33 | self.video_clip = None
34 | self.audio = None
35 |
36 | # 视频的宽、高
37 | self.video_width = 0
38 | self.video_height = 0
39 |
40 | # 视频的默认帧率、时长
41 | self.fps = 25
42 | self.time = 10
43 |
44 | # 文字字体属性,包含:字体大小、字体颜色、字体位置,一般是上(0)下(1)
45 | self.font_params = {
46 | 'size': 50,
47 | 'color': 'red',
48 | 'kerning': 5,
49 | 'position': 0
50 | }
51 |
52 | def run(self):
53 | # 1、获取视频的属性数据,并裁掉抖音最后加入的素材数据(抖音记录美好生活,一般为3s)
54 | self.__pre()
55 |
56 | # 2、获取视频的某一帧,去PS量取视频的左上角、右下角坐标
57 | position1, position2 = self.__get_frame(3)
58 |
59 | # 3、对原视频进行一次剪辑,重新产生一个横向的视频
60 | croped_video_clip = self.video_crop(position1, position2, './source/temp_source_croped.mp4')
61 |
62 | print(f'视频宽为:{self.video_width},高为:{self.video_height},帧率:{self.fps},时长:{self.time}')
63 |
64 | # 4、一张图片组成背景视频,和视频同帧率、同时长
65 | image_video_clip = one_pic_to_video('./source/1.jpeg', './source/temp_img_video.mp4', self.fps, self.time)
66 |
67 | # 5、合成两段视频
68 | synthetic_video_clip = synthetic_video(image_video_clip, croped_video_clip)
69 |
70 | # 6、生成描述信息
71 | desc_text_clip = generate_text_clip(self.desc, self.font_params, synthetic_video_clip.duration)
72 |
73 | # 7、视频加入描述信息
74 | video_with_text_clip = video_with_text(synthetic_video_clip, desc_text_clip)
75 |
76 | # 8、视频加入音频,并删除临时文件
77 | self.video_with_audio(video_with_text_clip)
78 |
79 | def video_crop(self, position1, position2, croped_video_path):
80 | """
81 | 视频裁剪
82 | :return:
83 | """
84 | # 裁剪的坐标,包含左上角x轴和y轴;右下角x轴和y轴
85 | clip2 = fx.all.crop(self.video_clip, x1=position1[0], y1=position1[1], x2=position2[0], y2=position2[1])
86 |
87 | # 保存文件
88 | clip2.write_videofile(croped_video_path)
89 |
90 | # 时长
91 | self.time = clip2.duration
92 |
93 | return clip2
94 |
95 | def __pre(self):
96 | """
97 | 准备工作
98 | :return:
99 | """
100 | self.video_raw_clip = VideoFileClip(self.video_raw_path)
101 |
102 | # 视频宽、高
103 | self.video_width, self.video_height = self.video_raw_clip.w, self.video_raw_clip.h
104 |
105 | self.fps = self.video_raw_clip.fps
106 |
107 | # 分离出音频
108 | self.audio = self.video_raw_clip.audio.subclip(0, self.video_raw_clip.duration - 4)
109 |
110 | # 裁剪尾部的视频素材
111 | temp_video_clip = self.video_raw_clip.subclip(0, self.video_raw_clip.duration - 4)
112 |
113 | # 生成新的视频,并保存到本地
114 | temp_video_clip.set_audio(self.audio)
115 |
116 | video_path = './source/temp_source_video.mp4'
117 |
118 | temp_video_clip.write_videofile(video_path, codec='libx264',
119 | audio_codec='aac',
120 | temp_audiofile='temp-audio.m4a',
121 | remove_temp=True)
122 |
123 | # 重新初始化VideoFileClip,便于后面剪辑
124 | self.video_clip = VideoFileClip(video_path)
125 |
126 | def __get_frame(self, time):
127 | """
128 | 获取视频的某一帧
129 | :param time:
130 | :return:
131 | """
132 | # get_frame_from_video(self.path_video_source, time, './output.jpg')
133 |
134 | # 获取要截取视频的左上坐标、右下坐标(328 631)
135 | # position1 = (0, 328)
136 | # position2 = (self.video_width, 631)
137 |
138 | # 注意:坐标值必须为偶数,不然会裁剪失败
139 | position1 = (0, 328)
140 | position2 = (self.video_width, 630)
141 |
142 | return position1, position2
143 |
144 | def video_with_audio(self, video_with_text_clip):
145 | """
146 | 视频合成音频,并删除临时文件
147 | :return:
148 | """
149 | # 设置视频音频,并写入到文件中去
150 | video_with_text_clip.set_audio(self.audio).write_videofile("output.mp4",
151 | codec='libx264',
152 | audio_codec='aac',
153 | temp_audiofile='temp-audio.m4a',
154 | remove_temp=True
155 | )
156 | # 删除所有的临时文件
157 | del_temp_file("./source/")
158 |
159 |
160 | if __name__ == '__main__':
161 | # 原视频文件
162 | video_source = './source/source.mp4'
163 | desc = '我是描述信息'
164 |
165 | video_re = Video_Re(video_source, desc)
166 | video_re.run()
167 |
--------------------------------------------------------------------------------
/video_auto/制作GIF视频/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/video_auto/制作GIF视频/.DS_Store
--------------------------------------------------------------------------------
/video_auto/制作GIF视频/gif_make_video.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: gif_make_video.py
12 | @time: 5/26/19 22:22
13 | @description:抓取一些gif,转为视频
14 | """
15 |
16 | from moviepy.editor import *
17 | import cv2
18 | from utils.image_utils import *
19 | import math
20 |
21 |
22 | class Gif_TO_VIDEO(object):
23 | def __init__(self, gifs_path, bgm_path):
24 | # gif目录
25 | self.gifs_path = gifs_path
26 |
27 | # BGM目录
28 | self.bgm_path = bgm_path
29 |
30 | # 视频帧率
31 | self.fps = 10
32 |
33 | self.img_size = (int(1280), int(720))
34 |
35 | # gif文件临时目录
36 | self.temp_gif_path = './gif_temp/'
37 |
38 | # 视频文件临时目录
39 | self.temp_video_path = './video_temp/'
40 |
41 | # 合成的视频文件完整目录
42 | self.video_output_temp = './video_temp/target_temp.mp4'
43 |
44 | # 最后生成的视频文件目录
45 | self.video_output = './target.mp4'
46 |
47 | def run(self):
48 | # 1.获取所有gif的文件
49 | gifs_path = self.__get_gifs()
50 |
51 | # 2.遍历文件,分别把每个gif转成一个视频
52 | videos_output = []
53 | for gif_path in gifs_path:
54 | print('要处理的gif文件完整路径是:%s' % gif_path)
55 |
56 | # 获取文件名,不带后缀
57 | filename = get_filePath_fileName_all(gif_path)[2]
58 |
59 | # 输出的文件名
60 | video_name = self.temp_video_path + filename + ".mp4"
61 |
62 | self.__gif_to_video(gif_path, video_name)
63 |
64 | # 所有输入视频片段的完整路径
65 | videos_output.append(video_name)
66 |
67 | # 清空gif文件夹
68 | clean_a_folder(self.temp_gif_path)
69 |
70 | # 3.遍历视频目录,合并成一个视频
71 | self.compound_a_video(videos_output)
72 | print('合成单个视频成功~')
73 |
74 | # 4.合并音视频
75 | self.__add_bgm_to_video()
76 | print('恭喜,合成一条包含BGM的视频成功!!!')
77 |
78 | # 5.清空临时视频文件夹
79 | clean_a_folder(self.temp_video_path)
80 |
81 | def __get_gifs(self):
82 | """
83 | 获取所有gif列表
84 | :return:
85 | """
86 |
87 | list_temp = list(map(lambda x: self.gifs_path + x, os.listdir(self.gifs_path)))
88 |
89 | # 排序
90 | list_temp.sort()
91 |
92 | print('排序之后的gif文件列表数据为:')
93 | print(list_temp)
94 |
95 | # 过滤一次,保证获取到的是gif图片
96 | return list(filter(lambda gif_path: gif_path.endswith('.gif'), list_temp))
97 |
98 | def __gif_to_video(self, gif_path, video_name):
99 | """
100 | gif转为视频
101 | :param gif_path:源gif的完整路径
102 | :param video_name:视频输出的完整目录
103 | :return:
104 | """
105 | # 1.把gif图片转为帧图片
106 | get_gif_frames(gif_path, self.temp_gif_path)
107 |
108 | # 2.把帧图片转为固定分辨率的图片
109 | file_paths_pre = os.listdir(self.temp_gif_path)
110 |
111 | file_paths = list(filter(lambda gif_path: gif_path.endswith('.png'), file_paths_pre))
112 |
113 | for file_path in file_paths:
114 | resize_image(self.temp_gif_path + file_path, self.img_size)
115 | print('图片分辨率批量修改成功!')
116 |
117 | # 3.把图片合并成一个视频
118 | # 注意:如果直接对图片采用固定的帧率处理,会导致最后生成的视频不流畅
119 | source_frame_rate, source_duration = get_gif_info(gif_path)
120 |
121 | print('源gif的帧率为:%f,时长为:%fs' % (source_frame_rate, source_duration))
122 |
123 | # 4.获取当前视频应该播放的时长
124 | duration = source_duration / (self.fps / source_frame_rate)
125 |
126 | # print('现有视频,帧率设置为:%d,时长应该设置为:%f' % (self.fps, duration))
127 |
128 | pics_to_video(self.temp_gif_path, video_name, self.fps, duration)
129 |
130 | print('合成视频素材:%s成功' % video_name)
131 |
132 | def compound_a_video(self, videos_path):
133 | """
134 | 合成一个视频
135 | :param videos_output:视频集合的完整目录
136 | :return:
137 | """
138 | # 定义一个数组
139 | L = []
140 |
141 | for video_path in videos_path:
142 | # 载入视频
143 | video = VideoFileClip(video_path)
144 | # 添加到数组
145 | L.append(video)
146 |
147 | # 拼接视频
148 | final_clip = concatenate_videoclips(L)
149 |
150 | # 生成目标视频文件
151 | final_clip.to_videofile(self.video_output_temp, fps=self.fps, remove_temp=False)
152 |
153 | def __add_bgm_to_video(self):
154 | """
155 | 针对合成的视频,新增BGM
156 | :return:
157 | """
158 | # 1.音频文件
159 | audioclip = AudioFileClip(self.bgm_path)
160 |
161 | # 2.视频文件
162 | videoclip = VideoFileClip(self.video_output_temp)
163 |
164 | # 3.获取视频和音频的时长
165 | video_time = videoclip.duration
166 | audio_time = audioclip.duration
167 |
168 | print('视频时长:%f,音频时长:%f' % (video_time, audio_time))
169 |
170 | # 4.对视频或者音频进行裁剪
171 | if video_time > audio_time:
172 | # 视频时长>音频时长,对视频进行截取
173 | videoclip_new = videoclip.subclip(0, audio_time)
174 | audioclip_new = audioclip
175 | else:
176 | # 音频时长>视频时长,对音频进行截取
177 | videoclip_new = videoclip
178 | audioclip_new = audioclip.subclip(0, video_time)
179 |
180 | # 5.视频中加入音频
181 | video_with_new_audio = videoclip_new.set_audio(audioclip_new)
182 |
183 | # 6.写入到新的视频文件中
184 | video_with_new_audio.write_videofile("mp4_with_audio.mp4",
185 | codec='libx264',
186 | audio_codec='aac',
187 | temp_audiofile='temp-audio.m4a',
188 | remove_temp=True
189 | )
190 |
191 |
192 | if __name__ == '__main__':
193 | # gif目录
194 | gif_path = './gifs/'
195 |
196 | # BGM目录
197 | bgm_path = './backup/dancy.mp3'
198 |
199 | gif_to_video = Gif_TO_VIDEO(gif_path, bgm_path)
200 |
201 | # 把所有gif转换为一个视频
202 | gif_to_video.run()
203 |
--------------------------------------------------------------------------------
/video_auto/制作GIF视频/gifs/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/video_auto/制作GIF视频/gifs/.DS_Store
--------------------------------------------------------------------------------
/video_auto/制作GIF视频/utils/file_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: file_utils.py
12 | @time: 5/17/19 10:15
13 | @description:文件操作工具类
14 | """
15 | import os
16 | import shutil
17 |
18 |
19 | def rename(str):
20 | os.rename(str, './../temp_new1.aac')
21 |
22 |
23 | def get_temp_path(file_path, temp_name):
24 | """
25 | 获取同一级目录下临时文件的完整路径
26 | :param str:
27 | :return:
28 | """
29 | filepath, filename_with_extension, filename_without_extension, extension = get_filePath_fileName_all(file_path)
30 |
31 | return filepath + "/" + temp_name + extension
32 |
33 |
34 | def get_filePath_fileName_all(filename):
35 | """
36 | 获取文件的路径、文件名【带后缀】、文件名【不带后缀】、后缀名
37 | :param filename:
38 | :return:
39 | """
40 | (filepath, filename_with_extension) = os.path.split(filename)
41 | (filename_without_extension, extension) = os.path.splitext(filename_with_extension)
42 |
43 | return filepath, filename_with_extension, filename_without_extension, extension
44 |
45 |
46 | def create_a_folder(path):
47 | """
48 | 创建一个文件夹
49 | :param path:
50 | :return:
51 | """
52 | temp_folder = os.path.exists(path)
53 | if not temp_folder:
54 | os.makedirs(path)
55 |
56 |
57 | def clean_a_folder(path):
58 | """
59 | 清空某个目录下的所有文件
60 | :param path:
61 | :return:
62 | """
63 | shutil.rmtree(path)
64 | os.mkdir(path)
65 |
66 |
67 | def time_convert(size):
68 | M, H = 60, 60 ** 2
69 | if size < M:
70 | return str(size) + u'秒'
71 | if size < H:
72 | return u'%s分钟%s秒' % (int(size / M), int(size % M))
73 | else:
74 | hour = int(size / H)
75 | mine = int(size % H / M)
76 | second = int(size % H % M)
77 | tim_srt = u'%s小时%s分钟%s秒' % (hour, mine, second)
78 | return tim_srt
79 |
--------------------------------------------------------------------------------
/video_auto/制作GIF视频/utils/image_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: image_utils.py
12 | @time: 5/17/19 13:03
13 | @description:图像工具类
14 | """
15 | from PIL import Image
16 | import os
17 | from utils.file_utils import *
18 | from utils.video_utils import *
19 | from imgpy import Img
20 | import json
21 | import re
22 |
23 | # pip3 install imgpy
24 |
25 |
26 | def analyseImage(path):
27 | """
28 | 分析图片
29 | :param path:
30 | :return:
31 | """
32 | im = Image.open(path)
33 | results = {
34 | 'size': im.size,
35 | 'mode': 'full',
36 | }
37 | try:
38 | while True:
39 | if im.tile:
40 | tile = im.tile[0]
41 | update_region = tile[1]
42 | update_region_dimensions = update_region[2:]
43 | if update_region_dimensions != im.size:
44 | results['mode'] = 'partial'
45 | break
46 | im.seek(im.tell() + 1)
47 | except EOFError:
48 | pass
49 | return results
50 |
51 |
52 | def get_gif_frames(gif_path, temp_path):
53 | """
54 | 获取一段GIf图片下的所有静态帧
55 | get_gif_frames('./../gifs/3.gif', './../gif_temp/')
56 | :param gif_path:
57 | :return:
58 | """
59 |
60 | # 分析gif图片
61 | mode = analyseImage(gif_path)['mode']
62 |
63 | im = Image.open(gif_path)
64 |
65 | i = 1
66 | p = im.getpalette()
67 | last_frame = im.convert('RGBA')
68 |
69 | try:
70 | while True:
71 | # print("saving %s (%s) frame %d, %s %s" % (gif_path, mode, i, im.size, im.tile))
72 |
73 | '''
74 | If the GIF uses local colour tables, each frame will have its own palette.
75 | If not, we need to apply the global palette to the new frame.
76 | '''
77 | if not im.getpalette():
78 | im.putpalette(p)
79 |
80 | new_frame = Image.new('RGBA', im.size)
81 |
82 | '''
83 | Is this file a "partial"-mode GIF where frames update a region of a different size to the entire image?
84 | If so, we need to construct the new frame by pasting it on top of the preceding frames.
85 | '''
86 | if mode == 'partial':
87 | new_frame.paste(last_frame)
88 |
89 | new_frame.paste(im, (0, 0), im.convert('RGBA'))
90 | new_frame.save(temp_path + '/%s-%d.png' % (''.join(os.path.basename(gif_path).split('.')[:-1]), i), 'PNG')
91 |
92 | i += 1
93 | last_frame = new_frame
94 | im.seek(im.tell() + 1)
95 | except EOFError:
96 | # print('产生EOFError!!!')
97 | pass
98 |
99 |
100 | def get_gif_info(gif_path):
101 | """
102 | 获取gif文件的详细信息
103 | 每一个gif的帧率不一样,有的<10fps;有的>10fps
104 | :param gif_path:
105 | :return:
106 | """
107 | with Img(fp=gif_path) as im:
108 | # 1.有多少帧
109 | frame_count = im.frame_count
110 |
111 | # 2.帧列表-PIL.Image.Image
112 | # print(im.frames)
113 |
114 | # 3.未知
115 | # print(im.exif)
116 |
117 | # 4.GIF
118 | # print(im.format)
119 |
120 | # 5.图片信息
121 | # {'version': b'GIF89a', 'background': 31, 'duration': 70, 'extension': (b'NETSCAPE2.0', 795), 'loop': 0}
122 | duration_pre = im.info.get('duration')
123 |
124 | # 根据规律,除以7位实际的播放时长
125 | duration = duration_pre / 7
126 |
127 | # 6.color palette
128 | # print(im.mode_desc)
129 |
130 | # print((frame_count, duration))
131 |
132 | # 返回帧率和时长
133 | return (frame_count / duration), duration
--------------------------------------------------------------------------------
/video_auto/制作GIF视频/utils/math_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: math_utils.py
12 | @time: 5/29/19 21:01
13 | @description:数学公式工具类
14 | """
15 |
16 | import re
17 |
18 | re_digits = re.compile(r'(\d+)')
19 |
20 |
21 | def emb_numbers(s):
22 | pieces = re_digits.split(s)
23 | pieces[1::2] = map(int, pieces[1::2])
24 | return pieces
25 |
26 |
27 | # 根据字符串中的数字排序,如f10应该在f2后面
28 | def sort_strings_with_emb_numbers(alist):
29 | """
30 | DSU排序
31 | :param alist:
32 | :return:
33 | """
34 | aux = [(emb_numbers(s), s) for s in alist]
35 | aux.sort()
36 | return [s for __, s in aux]
37 |
38 |
39 | def sort_strings_with_emb_numbers2(alist):
40 | """
41 | 内置DSU排序
42 | :param alist:
43 | :return:
44 | """
45 | return sorted(alist, key=emb_numbers)
46 |
--------------------------------------------------------------------------------
/video_auto/制作GIF视频/utils/video_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: video_utils.py
12 | @time: 5/28/19 21:08
13 | @description:视频工具类
14 | """
15 |
16 | from moviepy.editor import *
17 | from utils.math_utils import *
18 |
19 |
20 | def pics_to_video(pics_path, output_path, fps, duration):
21 | """
22 | 图片转为视频
23 | pics_to_video('./../gif_temp/', './../video_temp/temp1.mp4', 20)
24 | :param pics_path:
25 | :param output_path:
26 | :return:
27 | """
28 | image_paths = list(map(lambda x: pics_path + x, os.listdir(pics_path)))
29 |
30 | # 注意:这里必须进行一次排序,保证所有帧的顺序是一致
31 | image_paths = sort_strings_with_emb_numbers(image_paths)
32 |
33 | # 过滤掉非图片
34 | image_paths = list(filter(lambda image_path: image_path.endswith('.png'), image_paths))
35 |
36 | # 图片剪辑类
37 | clip = ImageSequenceClip(image_paths,
38 | fps=fps)
39 |
40 | # 写成视频之前,需要把gif都转成同一个分辨率
41 | clip.write_videofile(output_path)
42 |
--------------------------------------------------------------------------------
/video_auto/卡点视频/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/video_auto/卡点视频/.DS_Store
--------------------------------------------------------------------------------
/video_auto/卡点视频/images/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/video_auto/卡点视频/images/.DS_Store
--------------------------------------------------------------------------------
/video_auto/卡点视频/readme.md:
--------------------------------------------------------------------------------
1 | # 使用 Python 制作卡点视频
2 |
3 | ## 准备
4 |
5 | 1. 一段视频
6 | 2. 一段卡点的 BGM
7 | 3. 多张图片
8 |
9 |
10 |
11 | ## 依赖
12 |
13 | ```
14 | pip3 install opencv-python
15 |
16 | pip3 install ffmpeg
17 | ```
18 |
19 |
20 |
21 | ## 步骤
22 |
23 | 1. 剪辑视频的某部分 2s
24 | 2. 16 张静态图片生成一段视频 8s
25 | 3. 上面两段视频合成一段视频
26 | 4. 添加水印
27 | 5. 剪辑背景音乐,保证是视频时长一致
28 | 6. 合并视频和背景音乐,生成一个卡点视频
--------------------------------------------------------------------------------
/video_auto/卡点视频/utils/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/video_auto/卡点视频/utils/.DS_Store
--------------------------------------------------------------------------------
/video_auto/卡点视频/utils/file_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: file_utils.py
12 | @time: 5/17/19 10:15
13 | @description:TODO
14 | """
15 | import os
16 |
17 |
18 | def rename(str):
19 | os.rename(str, './../temp_new1.aac')
20 |
21 |
22 | def get_temp_path(file_path, temp_name):
23 | """
24 | 获取同一级目录下临时文件的完整路径
25 | :param str:
26 | :return:
27 | """
28 | filepath, filename_with_extension, filename_without_extension, extension = get_filePath_fileName_all(file_path)
29 |
30 | return filepath + "/" + temp_name + extension
31 |
32 |
33 | def get_filePath_fileName_all(filename):
34 | """
35 | 获取文件的路径、文件名【带后缀】、文件名【不带后缀】、后缀名
36 | :param filename:
37 | :return:
38 | """
39 | (filepath, filename_with_extension) = os.path.split(filename)
40 | (filename_without_extension, extension) = os.path.splitext(filename_with_extension)
41 |
42 | return filepath, filename_with_extension, filename_without_extension, extension
43 |
--------------------------------------------------------------------------------
/video_auto/卡点视频/utils/image_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: image_utils.py
12 | @time: 5/17/19 13:03
13 | @description:图像工具类
14 | """
15 | from PIL import Image
16 |
17 |
18 | def resize_image(target_image_path, target_size):
19 | """
20 | 调整图片大小,缺失的部分用黑色填充
21 | :param target_image_path: 图片路径
22 | :param target_size: 分辨率大小
23 | :return:
24 | """
25 | image = Image.open(target_image_path)
26 |
27 | iw, ih = image.size # 原始图像的尺寸
28 | w, h = target_size # 目标图像的尺寸
29 | scale = min(w / iw, h / ih) # 转换的最小比例
30 |
31 | # 保证长或宽,至少一个符合目标图像的尺寸
32 | nw = int(iw * scale)
33 | nh = int(ih * scale)
34 |
35 | image = image.resize((nw, nh), Image.BICUBIC) # 缩小图像
36 | # image.show()
37 |
38 | new_image = Image.new('RGB', target_size, (0, 0, 0, 0)) # 生成黑色图像
39 | # // 为整数除法,计算图像的位置
40 | new_image.paste(image, ((w - nw) // 2, (h - nh) // 2)) # 将图像填充为中间图像,两侧为灰色的样式
41 | # new_image.show()
42 |
43 | # 覆盖原图片
44 | new_image.save(target_image_path)
45 |
--------------------------------------------------------------------------------
/video_auto/卡点视频/video_cut_cv2.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: video_cut.py
12 | @time: 5/16/19 17:18
13 | @description:Python 生成一段10s的卡点视频
14 | """
15 |
16 | import cv2
17 | import numpy as np
18 | import time
19 | import os
20 | import math
21 | from utils.file_utils import *
22 | from utils.image_utils import *
23 | from PIL import Image
24 |
25 | # 最后影片的分辨率片,根据视频来设置,默认是1920*1080
26 | img_size = (int(1920), int(1080))
27 |
28 |
29 | def cut_film(soure_filename, output_filename, start_time, peroid):
30 | """
31 | 裁剪视频
32 | :param start_time: 开始时间【s】
33 | :param peroid: 持续时间【s】
34 | :param soure_filename 源视频
35 | :param output_filename 输出视频
36 | :return:
37 | """
38 | global img_size
39 |
40 | # 数据源
41 | videoCapture = cv2.VideoCapture(soure_filename)
42 |
43 | if videoCapture.isOpened():
44 | print('open success')
45 | else:
46 | print('open fail')
47 |
48 | fps = videoCapture.get(cv2.CAP_PROP_FPS)
49 |
50 | # 获取大小(1920*1080)
51 | img_size = (int(videoCapture.get(cv2.CAP_PROP_FRAME_WIDTH)),
52 | int(videoCapture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
53 |
54 | video_writer = cv2.VideoWriter(output_filename, cv2.VideoWriter_fourcc('X', 'V', 'I', 'D'), fps, img_size)
55 |
56 | i = 0
57 |
58 | # 开始帧和结束帧
59 | start_frame = fps * start_time
60 | end_frame = start_frame + peroid * fps
61 |
62 | print(type(start_frame))
63 | print('即将裁剪的开始帧为:%f,结束帧为:%f' % (start_frame, end_frame))
64 |
65 | while True:
66 | success, frame = videoCapture.read() # 循环读取下一帧
67 |
68 | if success:
69 | i += 1
70 | if start_frame <= i <= end_frame:
71 | # 将截取到的画面写入“新视频”
72 | video_writer.write(frame)
73 | else:
74 | print('end')
75 | break
76 |
77 | videoCapture.release()
78 |
79 | return fps
80 |
81 |
82 | def compound_film(film_names, video_output_path):
83 | """
84 | 合并视频
85 | :param film_names:
86 | :return:
87 | """
88 | print('一共要合成%d段视频' % len(film_names))
89 | print(film_names)
90 |
91 | # 通过第一个视频,获取帧率、宽、高
92 | videoCaptureNorm = cv2.VideoCapture(film_names[0])
93 |
94 | # 帧率、宽、高
95 | fps = videoCaptureNorm.get(cv2.CAP_PROP_FPS)
96 | width = (int(videoCaptureNorm.get(cv2.CAP_PROP_FRAME_WIDTH)))
97 | height = (int(videoCaptureNorm.get(cv2.CAP_PROP_FRAME_HEIGHT)))
98 |
99 | # =====================================================================
100 | # 待写入对象,写入到result.mp4文件中
101 | videoWriter = cv2.VideoWriter(video_output_path, cv2.VideoWriter_fourcc('m', 'p', '4', 'v'), fps, (width, height))
102 |
103 | for index, film_name in enumerate(film_names):
104 | print('视频名称:%s' % film_name)
105 | videoCapture = cv2.VideoCapture(film_name)
106 |
107 | # 循环写入数据
108 | while True:
109 | success, frame = videoCapture.read()
110 |
111 | # 视频必须保证分辨率一致,才能合并
112 | # frame_suitable = cv2.resizeWindow(frame, (width, height), interpolation=cv2.INTER_CUBIC)
113 | videoWriter.write(frame)
114 | if success:
115 | continue
116 | else:
117 | break
118 |
119 | videoCapture.release()
120 | print('第%d个视频合成完成~' % (index + 1))
121 |
122 | videoWriter.release()
123 | videoCaptureNorm.release()
124 |
125 |
126 | def compound_pic_special(images_path, output_video_path, fps):
127 | """
128 | 图片合成视频【卡点视频】
129 | :param path: 图片文件路径
130 | :param output_video_path:合成视频的路径
131 | :param size:
132 | :return:
133 | """
134 |
135 | # 获取该目录下的所有文件名
136 | filelist = os.listdir(images_path)
137 |
138 | print('一共有%d张图片' % len(filelist))
139 |
140 | fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
141 |
142 | video = cv2.VideoWriter(output_video_path, fourcc, fps, img_size)
143 |
144 | total_count = math.ceil(fps / 2)
145 | print('每张图片都需要重复写入%d帧' % total_count)
146 |
147 | for item in filelist:
148 | if item.endswith('.jpg'): # 判断图片后缀是否是.jpg
149 | image_path = images_path + '/' + item
150 |
151 | # 缩放图片到合适的分辨率,并覆盖源文件
152 | resize_image(image_path, img_size)
153 |
154 | frame = cv2.imread(image_path)
155 |
156 | # 直接缩放到指定大小
157 | frame_suitable = cv2.resize(frame, (img_size[0], img_size[1]), interpolation=cv2.INTER_CUBIC)
158 |
159 | # 把图片写进视频
160 | # 重复写入多少次
161 | count = 0
162 | while count < total_count:
163 | video.write(frame_suitable)
164 | count += 1
165 | else:
166 | print('名称为:%s,文件格式不对,过滤掉~' % item)
167 | # 释放资源
168 | video.release()
169 |
170 |
171 | def compound_bgm(video_path, bgm_path):
172 | """
173 | 通过视频、BGM 合成一段视频
174 | :param video_path: 视频路径
175 | :param bgm_path: BGM路径
176 | :return:
177 | """
178 | # $ ffmpeg -i 2_003_014.mp4 -vn -y -acodec copy 3.aac
179 | # 1.提前temp.mp4这个视频的BGM,文件结果为:temp.aac
180 | # os.system('ffmpeg -i temp.mp4 -vn -y -acodec copy temp.aac')
181 |
182 | # 2.获取视频的长度
183 | cap = cv2.VideoCapture(video_path)
184 | # 帧率
185 | fps = cap.get(cv2.CAP_PROP_FPS)
186 | # 总帧数
187 | frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
188 | # 总时长-秒,这里做取整操作 【浮点类型】
189 | time_count = math.floor(frame_count / fps)
190 |
191 | print('帧率:%f,总帧数:%d' % (fps, frame_count))
192 | print(time_count)
193 |
194 | # 3.截取音频
195 | # 为了简单,这里一般不会超过一分钟
196 | bgm_temp_path = get_temp_path(bgm_path, 'temp_new')
197 | os.system('ffmpeg -i %s -ss 00:00:00 -t 00:00:%d -acodec copy %s' % (bgm_path, time_count, bgm_temp_path))
198 |
199 | # 3.1 删除源音频并重命令当前文件
200 | os.remove(bgm_path)
201 | os.rename(bgm_temp_path, bgm_path)
202 |
203 | # 4.视频、音频合二为一
204 | video_temp_path = get_temp_path(video_path, 'temp')
205 | os.system('ffmpeg -i %s -i %s -vcodec copy -acodec copy %s' % (video_path, bgm_path, video_temp_path))
206 | os.remove(video_path)
207 | os.rename(video_temp_path, video_path)
208 |
209 | print('音视频合成完成~')
210 |
211 |
212 | def add_water_mask(video_path, mask_word):
213 | """
214 | 给视频增加水印
215 | :param video_part3: 视频源
216 | :param mask_word: 水印文字
217 | :return:
218 | """
219 | cap = cv2.VideoCapture(video_path)
220 | fps = cap.get(cv2.CAP_PROP_FPS)
221 |
222 | # 保证帧率不变
223 | fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
224 | video_temp_path = get_temp_path(video_path, 'temp')
225 | video_writer = cv2.VideoWriter(video_temp_path, fourcc, fps, img_size)
226 |
227 | ret, frame = cap.read()
228 |
229 | while ret:
230 | # 文字在图中的坐标(注意:这里的坐标原点是图片左上角)
231 | x, y = img_size[0] - 200, img_size[1] - 50
232 |
233 | cv2.putText(img=frame, text=mask_word,
234 | org=(x, y), fontFace=cv2.FONT_HERSHEY_COMPLEX_SMALL,
235 | fontScale=1, color=(255, 255, 255))
236 |
237 | video_writer.write(frame)
238 | ret, frame = cap.read()
239 |
240 | # 删除源文件,并重命名临时文件
241 | os.remove(video_path)
242 | os.rename(video_temp_path, video_path)
243 |
244 | print('水印添加完成~')
245 | video_writer.release()
246 | cap.release()
247 |
248 |
249 | def make_sticke_point_video(source_film_path, source_image_path, source_bgm_path):
250 | """
251 | 制作卡点视频
252 | :return:
253 | """
254 | # 说明:一共10s
255 | # 素材:一段2s的视频,然后准备16张图片,每张图片显示0.5s
256 |
257 | # 1.从一段视频中剪辑前2s的视频
258 | # 也可以指定开始帧和持续时间
259 | # 生成的第一段视频2s
260 | video_part1 = './part1.mp4'
261 | fps = cut_film(source_film_path, video_part1, 3, 2)
262 | print('第一段视频剪辑完成~')
263 | print('第一段视频的帧率是:%f' % fps)
264 |
265 | # 2.把所有图片合成一个视频
266 | # 分析:一张图片0.5s,一共16张图片,一共8s;1张图片1帧,fps:2帧/s
267 | # fps:2 1秒2帧
268 | # 注意:由于最后合成的一段视频帧率为同一个值,这里设置需要一张图片写入30帧,即30次
269 | video_part2 = './part2.mp4'
270 | compound_pic_special(source_image_path, video_part2, fps)
271 | print('第二段视频合成完成~')
272 |
273 | # 3.把两段视频拼接在一起
274 | video_part3 = './output.mp4'
275 | compound_film([video_part1, video_part2], video_part3)
276 | print('上面两段视频合并完成~')
277 |
278 | # 4.给视频加入水印
279 | add_water_mask(video_part3, '@xingag')
280 |
281 | # 5.加入背景音乐
282 | compound_bgm(video_part3, source_bgm_path)
283 |
284 |
285 | if __name__ == '__main__':
286 | # 制作卡点视频
287 | source_film_path = './1.mp4'
288 | source_image_path = './images'
289 | source_bgm_path = './bgm.aac'
290 | make_sticke_point_video(source_film_path, source_image_path, source_bgm_path)
291 |
--------------------------------------------------------------------------------
/一键升级/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/一键升级/.DS_Store
--------------------------------------------------------------------------------
/一键升级/auto_rar.au3:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | ; 注意:对于非标准控件,没法利用Autoit的拿到控件的属性
4 |
5 | ; 选中窗口
6 | ;$handle = WinGetHandle("一键升级","")
7 | $handle = WinGetHandle("source","")
8 |
9 | WinActivate($handle)
10 |
11 | ;全选
12 | Send("^a")
13 |
14 |
15 | ;右键
16 | MouseClick("right")
17 |
18 | ; 点击A,相当于选择[添加到压缩文件]
19 | Send('A')
20 |
21 |
22 | $handle = WinGetHandle("压缩文件名和参数","")
23 | WinActivate($handle)
24 |
25 | Sleep(1000)
26 |
27 | ; 压缩选中自解压文件
28 | ControlClick("压缩文件名和参数","","Button9")
29 |
30 |
31 | Sleep(1000)
32 |
33 |
34 | ;跳到高级Tab
35 | Send("^{TAB}")
36 |
37 | Sleep(1000)
38 |
39 | ;点击自解压选项
40 | ControlClick("压缩文件名和参数","","Button11")
41 |
42 | Sleep(1000)
43 |
44 | ;跳到设置Tab
45 | Send("^{TAB}")
46 |
47 | Sleep(1000)
48 |
49 | ;提取后运行的程序
50 | ControlSetText("高级自解压选项","","Edit1","DGUpdata.exe")
51 |
52 | Sleep(1000)
53 |
54 | ;等待退出
55 | ControlClick("高级自解压选项","","Button2")
56 |
57 | Sleep(1000)
58 |
59 | ;跳到模式Tab
60 | Send("^{TAB}")
61 |
62 | Sleep(1000)
63 |
64 | ;临时文件夹及隐藏
65 | ControlClick("高级自解压选项","","Button2")
66 |
67 | Sleep(1000)
68 |
69 | ControlClick("高级自解压选项","","Button6")
70 |
71 | Sleep(1000)
72 |
73 |
74 | ; 调到文本和图标Tab,执行3次Tab键
75 | Send("^{TAB 3}")
76 |
77 | Sleep(1000)
78 |
79 | ;点击浏览按钮
80 | ControlClick("高级自解压选项","","Button4")
81 |
82 | ;选择文件 update.ico
83 | ControlFocus("选择图标","","Edit1")
84 |
85 |
86 | Sleep(1000)
87 |
88 | ControlSetText("选择图标","","Edit1","update.ico")
89 |
90 | Sleep(1000)
91 |
92 | ; 确定选择文件
93 | ControlClick("选择图标","","Button1")
94 |
95 | ;调到许可Tab
96 | Sleep(1000)
97 | Send("^{TAB}")
98 |
99 |
100 | ;模拟点击回车键,执行压缩操作
101 | Send("{ENTER}")
102 |
103 | Send("^{TAB}")
104 |
105 | Send("{ENTER}")
106 |
107 | ; 点击确定
108 | ; 注意:由于【确定按钮】的ClassNameNN经常会变化,没有办法用下面的方法执行
109 | ; ControlClick("高级自解压选项","","Button7")
110 |
111 |
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/一键升级/auto_rar.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/一键升级/auto_rar.exe
--------------------------------------------------------------------------------
/发送邮件/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/发送邮件/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/发送邮件/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/发送邮件/.idea/发送邮件.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/发送邮件/attachments/report.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/发送邮件/attachments/report.png
--------------------------------------------------------------------------------
/发送邮件/email_by_smtplib.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: email_by_smtplib.py
12 | @time: 2020-07-09 21:30
13 | @description:smtplib发送邮件
14 | """
15 |
16 | import os
17 | import smtplib
18 | from email.mime.multipart import MIMEMultipart
19 | from email.mime.text import MIMEText
20 |
21 |
22 | class SmtplibObject(object):
23 |
24 | def __init__(self):
25 | # 初始化
26 | self.smtp = smtplib.SMTP()
27 | # 连接邮箱服务器地址
28 | self.smtp.connect('smtp.126.com')
29 |
30 | # 加入主题和附件,邮件体
31 | self.email_body = MIMEMultipart('mixed')
32 |
33 | # 发件人地址及授权码
34 | self.email_from_username = '**@126.com'
35 | self.email_from_password = '授权码'
36 |
37 | def send_email(self, email_to_list, email_title, email_content, attchment_path, files):
38 | """
39 | 发送邮件
40 | :return:
41 | """
42 | # 组成邮件体
43 | self.generate_email_body(email_to_list, email_title, email_content, attchment_path, files)
44 |
45 | # 登录邮箱
46 | # 参数为账号和密码(授权码)
47 | self.smtp.login(self.email_from_username, self.email_from_password)
48 |
49 | # 发送邮件
50 | # 注意:此处必须同时指定发件人与收件人,否则会当作垃圾邮件处理掉
51 | self.smtp.sendmail(self.email_from_username, email_to_list, self.email_body.as_string())
52 |
53 | def generate_email_body(self, email_to_list, email_title, email_content, attchment_path, files):
54 | """
55 | 组成邮件体
56 | :param email_to_list:收件人列表
57 | :param email_title:邮件标题
58 | :param email_content:邮件正文内容
59 | :param attchment_path:附件的路径
60 | :param files:附件文件名列表
61 | :return:
62 | """
63 | self.email_body['Subject'] = email_title
64 | self.email_body['From'] = self.email_from_username
65 | self.email_body['To'] = ",".join(email_to_list)
66 |
67 | for file in files:
68 | file_path = attchment_path + '/' + file
69 | if os.path.isfile(file_path):
70 | # 构建一个附件对象
71 | att = MIMEText(open(file_path, 'rb').read(), 'base64', 'utf-8')
72 | att["Content-Type"] = 'application/octet-stream'
73 | att.add_header("Content-Disposition", "attachment", filename=("gbk", "", file))
74 | self.email_body.attach(att)
75 |
76 | text_plain = MIMEText(email_content, 'plain', 'utf-8')
77 | self.email_body.attach(text_plain)
78 |
79 | def exit(self):
80 | """
81 | 退出服务
82 | :return:
83 | """
84 | self.smtp.quit()
85 |
86 |
87 | if __name__ == '__main__':
88 | # 收件人列表
89 | email_to_list = ['**@qq.com']
90 | email_title = "测试报告"
91 | email_content = '这是测试报告具体内容'
92 | # 附件路径
93 | attchment_path = './attachments/'
94 | # 附件文件列表
95 | attchment_files = ['report.png', 'config.json']
96 |
97 | # 发送邮件
98 | smtplib_object = SmtplibObject()
99 | smtplib_object.send_email(email_to_list, email_title, email_content, attchment_path, attchment_files)
100 |
101 | # 退出
102 | smtplib_object.exit()
103 |
--------------------------------------------------------------------------------
/发送邮件/email_by_yagmail.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: email_by_yagmail.py
12 | @time: 2020-07-09 21:07
13 | @description:yagmail发送/接受邮件
14 | """
15 |
16 | # 依赖:pip3 install yagmail
17 | # 公众号:AirPython
18 |
19 | import yagmail
20 |
21 | # 连接服务器
22 | # 用户名、授权码、服务器地址
23 | yag_server = yagmail.SMTP(user='**@126.com', password='授权码', host='smtp.126.com')
24 |
25 | # 发送邮件
26 | # 发送对象列表
27 | email_to = ['**@qq.com', ]
28 | email_title = '测试报告'
29 | email_content = "这是测试报告的具体内容"
30 | # 附件列表
31 | email_attachments = ['./attachments/report.png', ]
32 | yag_server.send(email_to, email_title, email_content, email_attachments)
33 |
34 | # 关闭连接
35 | yag_server.close()
36 |
--------------------------------------------------------------------------------
/发送邮件/email_by_zmail.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: email_by_zmail.py
12 | @time: 2020-07-09 19:57
13 | @description:zmail发送邮件
14 | """
15 |
16 | # 依赖
17 | # pip3 install zmail
18 | # 公众号:AirPython
19 |
20 | import zmail
21 |
22 |
23 | class ZMailObject(object):
24 |
25 | def __init__(self):
26 | # 邮箱账号
27 | self.username = '**@126.com'
28 |
29 | # 邮箱授权码
30 | self.authorization_code = '授权码'
31 |
32 | # 构建一个邮箱服务对象
33 | self.server = zmail.server(self.username, self.authorization_code)
34 |
35 | def send_email(self, mail_to, mail_body):
36 | """
37 | 发送邮件
38 | :param mail_to 发送对象
39 | :param mail: 发送主题、内容及附件
40 | :return:
41 | """
42 |
43 | if self.__check_pop_enable() and self.__check_smtp_enable():
44 | self.server.send_mail(mail_to, mail_body)
45 | else:
46 | pass
47 |
48 | def receive_email(self):
49 | """
50 | 接受邮件
51 | :return:
52 | """
53 | try:
54 | # 接受邮件
55 | last_mail = self.server.get_latest()
56 | # last_mail = self.server.get_mail(2)
57 | # zmail.show(last_mail)
58 | for k, v in last_mail.items():
59 | print(k, v)
60 | except Exception as e:
61 | # 收件箱为空,则会报错
62 | print('接受异常异常')
63 |
64 | def __check_smtp_enable(self):
65 | """
66 | 检查smtp是否正常
67 | :return:
68 | """
69 | return self.server.smtp_able()
70 |
71 | def __check_pop_enable(self):
72 | """
73 | 检查pop功能是否正常
74 | :return:
75 | """
76 | return self.server.pop_able()
77 |
78 |
79 | if __name__ == '__main__':
80 | zmail_obj = ZMailObject()
81 |
82 | # 发送内容及附件
83 | mail_to = '**@qq.com'
84 |
85 | # 邮件主体
86 | mail_body = {
87 | 'subject': '测试报告',
88 | 'content_text': '这是一个测试报告', # 纯文本或者HTML内容
89 | 'attachments': ['./attachments/report.png'],
90 | }
91 |
92 | # 发送邮件
93 | zmail_obj.send_email(mail_to, mail_body)
94 |
95 | # 接受邮件
96 | # zmail_obj.receive_email()
97 |
--------------------------------------------------------------------------------
/图片视频九宫格/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/图片视频九宫格/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/图片视频九宫格/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/图片视频九宫格/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/图片视频九宫格/.idea/图片视频九宫格.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/图片视频九宫格/ninecell_image.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: ninecell_image.py
12 | @time: 2020-08-28 22:20
13 | @description:九宫格图片朋友圈
14 | """
15 |
16 | from PIL import Image
17 |
18 |
19 | class ImageObj(object):
20 | def __init__(self):
21 | pass
22 |
23 | def start(self, file_path):
24 | # 1、打开图片
25 | image = Image.open(file_path)
26 |
27 | # 2、将图片填充为正方形,组成:原图+百底+正方形
28 | image = self.__fill_image(image)
29 |
30 | # 保存图片
31 | image.save('temp.jpg')
32 |
33 | # 3、裁剪合成后图片,为9张图片
34 | nine_images = self.__cut_image(image)
35 |
36 | # 4、保存9张图片到本地
37 | self.save_images(nine_images)
38 |
39 | def __cut_image(self, image):
40 | """
41 | 把一张图片裁剪成9张图片的坐标值,然后进行裁剪成小图片
42 | :return:
43 | """
44 | width, height = image.size
45 |
46 | # 6000
47 | print('原图的宽/高分别为:', width, height)
48 |
49 | # 2000
50 | # 每张图片的宽度
51 | item_width = int(width / 3)
52 |
53 | print('裁剪后的宽度为:', item_width)
54 |
55 | box_list = []
56 |
57 | for i in range(0, 3):
58 | for j in range(0, 3):
59 | # 坐标值分别是:左、上、右、底
60 | box = (j * item_width, i * item_width, (j + 1) * item_width, (i + 1) * item_width)
61 | print(box)
62 | box_list.append(box)
63 |
64 | # 裁剪图片
65 | image_list = [image.crop(box) for box in box_list]
66 |
67 | return image_list
68 |
69 | def __fill_image(self, image):
70 | """
71 | 将图片填充为正方形
72 | :param image:
73 | :return:
74 | """
75 | width, height = image.size
76 |
77 | # 长和宽较大值,作为新图片的宽高
78 | new_image_length = width if width > height else height
79 |
80 | # 生成新图片[纯白底]
81 | new_image = Image.new(image.mode, (new_image_length, new_image_length), color='white')
82 |
83 | # 将之前的图粘贴在新图上,居中
84 | # 如果原图宽大于高(横图),则填充图片的竖直维度
85 | if width > height:
86 | # (x,y)二元组表示粘贴上图相对下图的起始位置
87 | new_image.paste(image, (0, int((new_image_length - height) / 2)))
88 | else:
89 | # 如果原图宽小于高(竖图),则填充图片的水平纬度
90 | new_image.paste(image, (int((new_image_length - width) / 2), 0))
91 | return new_image
92 |
93 | def save_images(self, nine_images):
94 | """
95 | 保存图片
96 | :param nine_images:
97 | :return:
98 | """
99 | index = 1
100 | for image in nine_images:
101 | image.save('result/' + str(index) + '.jpg')
102 | index += 1
103 |
104 |
105 | if __name__ == '__main__':
106 | # 图片路径
107 | image_file_path = './raw/pic.jpg'
108 | image_obj = ImageObj()
109 | image_obj.start(image_file_path)
110 |
--------------------------------------------------------------------------------
/图片视频九宫格/ninecell_video.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: ninecell_video.py
12 | @time: 2020-08-28 23:16
13 | @description:九宫格视频朋友圈
14 | """
15 |
16 | from PIL import Image
17 |
18 | from utils.video_utils import *
19 |
20 |
21 | class VideoObj(object):
22 | def __init__(self, file_path):
23 | self.video_raw_clip = VideoFileClip(file_path)
24 |
25 | # 宽、高
26 | self.video_width, self.video_height = self.video_raw_clip.w, self.video_raw_clip.h
27 |
28 | # 帧率
29 | self.fps = self.video_raw_clip.fps
30 |
31 | # 视频时长
32 | self.during = self.video_raw_clip.duration
33 |
34 | # 白色间隙宽度,用于分割图片
35 | self.item_space = 10
36 |
37 | # 视频原路径
38 | self.path_video_raw = file_path
39 |
40 | # 临时文件夹,用于保存处理后的帧图片
41 | self.path_temp = './temp/'
42 |
43 | # 输出文件夹
44 | self.path_output = './output/'
45 |
46 | # 音频文件保存路径
47 | self.path_bgm = self.path_temp + 'bg.wav'
48 |
49 | # 临时保存的视频文件
50 | self.path_video_temp = self.path_temp + 'temp.mp4'
51 |
52 | # 最终输出的视频文件
53 | self.path_video_output = self.path_output + 'result.mp4'
54 |
55 | def pre(self):
56 | """
57 | 准备工作
58 | :return:
59 | """
60 | # 新建临时文件夹和输出文件夹
61 | mkdir_folder(self.path_temp)
62 | mkdir_folder(self.path_output)
63 |
64 | def start(self):
65 |
66 | # 1、准备工作
67 | self.pre()
68 |
69 | print('视频宽/高为:', self.video_width, '/', self.video_height, '帧率/时长:', self.fps, '/', self.during)
70 |
71 | # 2、获取音频文件保存到本地
72 | get_audio_from_video(self.video_raw_clip, self.path_bgm)
73 |
74 | # 3、保存所有的帧图片到临时文件夹
75 | self.save_frames()
76 |
77 | # 4、将一篮子图片重新合成视频
78 | pics_to_video(self.path_temp, self.path_video_temp, self.fps)
79 |
80 | # 5、视频加入BGM
81 | video_with_audio(self.path_video_temp, self.path_bgm, self.path_video_output)
82 |
83 | # 6、删除临时文件
84 | self.teardown()
85 |
86 | def save_frames(self):
87 | """
88 | 保存视频的所有帧,并做处理
89 | :return:
90 | """
91 | i = 1
92 | for frame in self.video_raw_clip.iter_frames():
93 | image = Image.fromarray(frame)
94 |
95 | # 视频帧图片保存的临时路径(完整路径)
96 | frame_file_complete_path = self.path_temp + "%04d.jpg" % i
97 |
98 | # 对图片进一步处理,加入白色间隔距离
99 | self.handle_frame(image, frame_file_complete_path)
100 |
101 | i += 1
102 |
103 | def handle_frame(self, image, frame_file_complete_path):
104 | """
105 | 对帧图片进一步的处理
106 | 包含,裁剪图片、新建扩张画布、粘贴裁剪图片、保存新图片到本地
107 | :return:
108 | """
109 | # 1、剪成9张图片,计算每张图片的宽、高
110 | item_width = int(self.video_width / 3)
111 | item_height = int(self.video_height / 3)
112 |
113 | # 2、新的宽、高
114 | item_width_new = self.video_width + self.item_space * 2
115 | item_height_new = self.video_height + self.item_space * 2
116 |
117 | # 3、重新建一个画布背景
118 | new_image = Image.new(image.mode, (item_width_new, item_height_new),
119 | color='white')
120 |
121 | # 4、裁剪图片,然后粘贴到新的画布中去
122 | # i:横向、j:纵向
123 | for i in range(0, 3):
124 | for j in range(0, 3):
125 | # 裁剪区域
126 | box = (j * item_width, i * item_height, (j + 1) * item_width, (i + 1) * item_height)
127 |
128 | # 坐标,左、顶部、右、底
129 | # print(box)
130 |
131 | # 根据区域,裁剪图片
132 | crop_image = image.crop(box)
133 |
134 | # 横向、纵向第2块和第3块,要加上偏移距离
135 | x = 0 if j == 0 else (item_width + self.item_space) * j
136 | y = 0 if i == 0 else (item_height + self.item_space) * i
137 |
138 | # 将9张图片,按照上面计算的坐标值,粘贴到背景中去
139 | new_image.paste(crop_image, (int(x), int(y)))
140 |
141 | # 保存图片到本地
142 | new_image.save(frame_file_complete_path)
143 |
144 | def teardown(self):
145 | """
146 | 最后处理
147 | :return:
148 | """
149 | # 删除临时文件
150 | remove_folder(self.path_temp)
151 |
152 |
153 | if __name__ == '__main__':
154 | # 待处理的文件
155 | file_path = './raw/至今无人超越的3个动作.mp4'
156 | video_obj = VideoObj(file_path)
157 |
158 | # 处理
159 | video_obj.start()
160 |
--------------------------------------------------------------------------------
/图片视频九宫格/output/result.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/图片视频九宫格/output/result.mp4
--------------------------------------------------------------------------------
/图片视频九宫格/raw/pic.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/图片视频九宫格/raw/pic.jpg
--------------------------------------------------------------------------------
/图片视频九宫格/raw/至今无人超越的3个动作.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/图片视频九宫格/raw/至今无人超越的3个动作.mp4
--------------------------------------------------------------------------------
/图片视频九宫格/utils/file_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: file_utils.py
12 | @time: 2020-02-01 23:05
13 | @description:TODO
14 | """
15 |
16 | import os
17 | import shutil
18 |
19 |
20 | def get_file_path_and_name(filename):
21 | """
22 | 获取文件的路径、文件名(不含后缀),后缀名
23 | :param filename:
24 | :return:
25 | """
26 | (filepath, tempfilename) = os.path.split(filename)
27 | (shotname, extension) = os.path.splitext(tempfilename)
28 | return filepath, shotname, extension
29 |
30 |
31 | def rename_file(path, filename):
32 | """
33 | 更改文件名(处理文件名中包含空格的情况)
34 | :param path:
35 | :param filename:
36 | :return:
37 | """
38 | os.rename(os.path.join(path, filename), os.path.join(path, filename.replace(' ', '_')))
39 |
40 |
41 | def mkdir_folder(file_path):
42 | """
43 | 创建一个文件夹,如果不存在就创建;否则不做处理
44 | :param file_path:
45 | :return:
46 | """
47 | if os.path.exists(file_path):
48 | return
49 |
50 | os.mkdir(file_path)
51 |
52 |
53 | def remove_folder(file_path):
54 | """
55 | 删除文件夹
56 | :param file_path:
57 | :return:
58 | """
59 | shutil.rmtree(file_path)
60 |
--------------------------------------------------------------------------------
/图片视频九宫格/utils/math_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: math_utils.py
12 | @time: 2020-08-29 10:14
13 | @description:TODO
14 | """
15 |
16 |
17 | import re
18 |
19 | re_digits = re.compile(r'(\d+)')
20 |
21 |
22 | def emb_numbers(s):
23 | pieces = re_digits.split(s)
24 | pieces[1::2] = map(int, pieces[1::2])
25 | return pieces
26 |
27 |
28 | def sort_strings_with_emb_numbers(alist):
29 | """
30 | DSU排序
31 | :param alist:
32 | :return:
33 | """
34 | aux = [(emb_numbers(s), s) for s in alist]
35 | aux.sort()
36 | return [s for __, s in aux]
37 |
38 |
39 | def sort_strings_with_emb_numbers2(alist):
40 | """
41 | 内置DSU排序
42 | :param alist:
43 | :return:
44 | """
45 | return sorted(alist, key=emb_numbers)
--------------------------------------------------------------------------------
/图片视频九宫格/utils/video_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: video_utils.py
12 | @time: 2020-02-01 22:32
13 | @description:公众号:AirPython
14 | """
15 |
16 | import random
17 |
18 | from moviepy.editor import *
19 |
20 | from utils.file_utils import *
21 | from utils.math_utils import *
22 |
23 |
24 | def gene_random():
25 | """
26 | 生成随机数字
27 | :return:
28 | """
29 | str = ""
30 | for i in range(6):
31 | ch = chr(random.randrange(ord('0'), ord('9') + 1))
32 | str += ch
33 | return str
34 |
35 |
36 | def get_audio_from_video(video_raw_clip, output_path):
37 | """
38 | 从视频中提取音频
39 | :param video_raw_clip: 视频Clip对象
40 | :param output_path: 输出音频文件完整路径
41 | :return:
42 | """
43 | audio = video_raw_clip.audio
44 | audio.write_audiofile(output_path)
45 |
46 | return output_path
47 |
48 |
49 | def mov2mp4(video_path, output_path):
50 | """
51 | mov格式转为mp4
52 | 注意:原视频文件名不能包含空格
53 | :param video_path:
54 | :param output_path:
55 | :return:
56 | """
57 | filename = gene_random()
58 | try:
59 | filename = get_file_path_and_name(video_path)[1]
60 | except Exception as e:
61 | pass
62 |
63 | output_path = output_path + filename + ".mp4"
64 |
65 | # 格式转换
66 | os.system('ffmpeg -i %s -qscale 0 %s' % (video_path, output_path))
67 |
68 |
69 | def pics_to_video(pics_path, output_path, fps):
70 | """
71 | 图片转为视频
72 | pics_to_video('./../gif_temp/', './../video_temp/temp1.mp4', 20)
73 | :param pics_path:
74 | :param output_path:
75 | :return:
76 | """
77 | image_paths = list(map(lambda x: pics_path + x, os.listdir(pics_path)))
78 |
79 | # 注意:这里必须进行一次排序,保证所有帧的顺序是一致
80 | image_paths = sort_strings_with_emb_numbers(image_paths)
81 |
82 | # 过滤掉非图片
83 | image_paths = list(filter(lambda image_path: image_path.endswith('.jpg'), image_paths))
84 |
85 | # 图片剪辑类
86 | clip = ImageSequenceClip(image_paths,
87 | fps=fps)
88 |
89 | clip.write_videofile(output_path)
90 |
91 |
92 | def video_with_other_audio(path_video_raw, path_bgm_raw, output):
93 | """
94 | 视频合成音频(混合音效)
95 | :return:
96 | """
97 | videoclip = VideoFileClip(path_video_raw)
98 | audioclip = AudioFileClip(path_bgm_raw)
99 |
100 | new_audioclip = CompositeAudioClip([videoclip.audio, audioclip])
101 | videoclip.audio = new_audioclip
102 | videoclip.write_videofile(output)
103 |
104 |
105 | def video_with_audio(path_video_raw, path_bgm_raw, output):
106 | """
107 | 视频合成音频
108 | :return:
109 | """
110 | videoclip = VideoFileClip(path_video_raw)
111 | audioclip = AudioFileClip(path_bgm_raw)
112 |
113 | # 设置视频音频,并写入到文件中去
114 | videoclip.set_audio(audioclip).write_videofile(output,
115 | codec='libx264',
116 | audio_codec='aac',
117 | temp_audiofile='temp-audio.m4a',
118 | remove_temp=True
119 | )
120 |
--------------------------------------------------------------------------------
/截图快捷翻译/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/截图快捷翻译/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/截图快捷翻译/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/截图快捷翻译/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 | 1584494973510
169 |
170 |
171 | 1584494973510
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
--------------------------------------------------------------------------------
/截图快捷翻译/.idea/截图快捷翻译.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/截图快捷翻译/main.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: main.py
12 | @time: 2020-03-18 09:29
13 | @description:TODO
14 | """
15 |
16 | import tkinter.messagebox
17 | from tkinter import *
18 |
19 | import pytesseract
20 | from PIL import Image
21 | from PIL import ImageGrab
22 | from googletrans import Translator
23 |
24 |
25 | def get_clip_image():
26 | """
27 | 从剪切板获取图片,保存到本地
28 | :return:
29 | """
30 | image_result = None
31 | img = ImageGrab.grabclipboard()
32 | if img and isinstance(img, Image.Image):
33 | print(img.size)
34 | print(img.mode)
35 | image_result = './temp.png'
36 | img.save(image_result)
37 | return image_result
38 |
39 |
40 | def image_ocr(image_path):
41 | """
42 | 识别图像中的英文
43 | :return:
44 | """
45 | # 英文:lang='eng'
46 | # 中文:lang='chi_sim'
47 | return pytesseract.image_to_string(Image.open(image_path), lang='eng')
48 |
49 |
50 | def trans_eng(content_eng):
51 | """
52 | 英文-中文
53 | :param content:
54 | :return:
55 | """
56 | translator = Translator(service_urls=['translate.google.cn'])
57 | return translator.translate(content_eng, src='en', dest='zh-cn').text
58 |
59 |
60 | image_path = get_clip_image()
61 |
62 | if image_path:
63 | # 获取文本
64 | content_eng = image_ocr(image_path).replace("\r", " ").replace("\n", " ")
65 |
66 | # 翻译
67 | if content_eng:
68 | content_chinese = trans_eng(content_eng)
69 | print(content_chinese)
70 |
71 | # 实现主窗口隐藏
72 | root = Tk()
73 | root.withdraw()
74 | tkinter.messagebox.showinfo('翻译结果', content_chinese)
--------------------------------------------------------------------------------
/截图快捷翻译/trans.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: trans.py
12 | @time: 2020-03-18 21:55
13 | @description:截屏快速翻译
14 | """
15 |
16 | import tkinter.messagebox
17 | from tkinter import *
18 |
19 | import pytesseract
20 | from PIL import Image
21 | from PIL import ImageGrab
22 | from googletrans import Translator
23 |
24 | # 1、从剪切板获取图片,保存到本地
25 | img = ImageGrab.grabclipboard()
26 | if img and isinstance(img, Image.Image):
27 | image_result = './temp.png'
28 | img.save(image_result)
29 |
30 | # 2、OCR识别
31 | content_eng = pytesseract.image_to_string(Image.open(image_result), lang='eng')
32 |
33 | # 3、翻译
34 | translator = Translator(service_urls=['translate.google.cn'])
35 |
36 | content_chinese = translator.translate(content_eng, src='en', dest='zh-cn').text
37 |
38 | # 4、显示
39 | root = Tk()
40 | root.withdraw()
41 | tkinter.messagebox.showinfo('翻译结果', content_chinese)
42 |
--------------------------------------------------------------------------------
/批处理脚本/del_all_build.bat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/批处理脚本/del_all_build.bat
--------------------------------------------------------------------------------
/批处理脚本/del_system_gc.bat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/批处理脚本/del_system_gc.bat
--------------------------------------------------------------------------------
/批处理脚本/documents_classifying.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | :: 当前目录下的文件进行分类,放到不同的文件夹内
3 | :: md 用于创建文件夹
4 | :: %%~xi将%%i解开扩展名,也就是取%%i的扩展名部分
5 | for %%i in (*) do (md %%~xi
6 | move *%%~xi %%~xi)
7 |
8 | pause
--------------------------------------------------------------------------------
/批处理脚本/raw/Bat_To_Exe_Converter.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/批处理脚本/raw/Bat_To_Exe_Converter.exe
--------------------------------------------------------------------------------
/批处理脚本/run_python_loop.bat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/批处理脚本/run_python_loop.bat
--------------------------------------------------------------------------------
/批处理脚本/提交代码/提交代码.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | title �ύ����
3 | echo �ύ���룬����
4 |
5 | :: ����״̬
6 | git status
7 |
8 | :: set���ȴ����룬��ֵ������msg
9 | set /p commit_msg=�����ύע�ͣ�
10 |
11 | :: �ύ�����4������
12 | git add .
13 | git commit -m %commit_msg%
14 | git pull
15 | git push
16 |
17 | echo �ύ�ɹ�
18 | pause
19 |
20 |
21 |
--------------------------------------------------------------------------------
/批量生成手机号码/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/批量生成手机号码/.DS_Store
--------------------------------------------------------------------------------
/批量生成手机号码/generate_area_phone_util.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: generate_area_phone_util.py
12 | @time: 2020-01-05 20:16
13 | @description:生成区域的电话号码
14 | """
15 |
16 | from utils.addr_utils import *
17 |
18 | def generate_phones(num, areas):
19 | """
20 | 生成随机号码
21 | :param num:数目
22 | :param areas: 区域
23 | :return:
24 | """
25 |
26 | headers = {
27 | 'authority': 'api.uukit.com',
28 | 'pragma': 'no-cache',
29 | 'cache-control': 'no-cache',
30 | 'accept': 'enote_app/json, text/javascript, */*; q=0.01',
31 | 'origin': 'https://uutool.cn',
32 | 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36',
33 | 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
34 | 'sec-fetch-site': 'cross-site',
35 | 'sec-fetch-mode': 'cors',
36 | 'referer': 'https://uutool.cn/phone-generate/',
37 | 'accept-encoding': 'gzip, deflate, br',
38 | 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
39 | }
40 |
41 | data = {
42 | 'phone_num': num,
43 | 'area': areas,
44 | 'segment': '133,153,189,180,181,177,173,139,138,137,136,135,134,159,158,157,150,151,152,147,188,187,182,183,184,178,130,131,132,156,155,186,185,145,176'
45 | }
46 |
47 | response = requests.post('https://api.uukit.com/phone/generate_batch', headers=headers, data=data)
48 |
49 | phones = json.loads(response.text).get('data').get('rows')
50 |
51 | return phones
52 |
53 |
54 | if __name__ == '__main__':
55 | # 手机号码个数
56 | num = 100
57 |
58 | # 全国所有城市名称和id编号
59 | citys = get_all_citys()
60 |
61 | city_name = input('请输入手机归属地:')
62 |
63 | if city_name not in citys.keys():
64 | city_name = '北京'
65 |
66 | # 获取城市id
67 | city_id = citys.get(city_name)
68 |
69 | # 请输入要获取手机号码的归属地
70 | phones = generate_phones(num, city_id)
71 | print(phones)
72 |
--------------------------------------------------------------------------------
/批量生成手机号码/utils/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/批量生成手机号码/utils/.DS_Store
--------------------------------------------------------------------------------
/批量生成手机号码/utils/addr_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: addr_utils.py
12 | @time: 2020-01-06 22:24
13 | @description:TODO
14 | """
15 |
16 | import json
17 | import re
18 |
19 | import requests
20 |
21 |
22 | # 所有的id和城市名称数据
23 |
24 | def get_all_citys():
25 | """
26 | 获取所有的城市数据
27 | :return:
28 | """
29 |
30 | headers = {
31 | 'authority': 'uutool.cn',
32 | 'pragma': 'no-cache',
33 | 'cache-control': 'no-cache',
34 | 'upgrade-insecure-requests': '1',
35 | 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36',
36 | 'sec-fetch-user': '?1',
37 | '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',
38 | 'sec-fetch-site': 'none',
39 | 'sec-fetch-mode': 'navigate',
40 | 'accept-encoding': 'gzip, deflate, br',
41 | 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
42 | 'cookie': 'UM_distinctid=16f759fe6bd24b-0322efd0d180d8-1d376b5b-1aeaa0-16f759fe6beb69; CNZZDATA1275106188=191793625-1578225029-https%253A%252F%252Fwww.google.com%252F%7C1578316721',
43 | }
44 |
45 | resp = requests.get('https://uutool.cn/phone-generate/', headers=headers).text
46 |
47 | re_rule = r'areaArr:(.+?)segmentArr:'
48 |
49 | # 匹配换行符
50 | result_data = re.findall(re_rule, resp, re.S)[0].strip()[:-1]
51 |
52 | result = json.loads(result_data)
53 |
54 | # 获取所有的省份
55 | provices = result.keys()
56 |
57 | # 所有的城市
58 | citys = {}
59 |
60 | for provice in provices:
61 | current_citys = result.get(provice)
62 | # citys.extend(current_citys)
63 | for item in current_citys:
64 | citys[item.get('name')] = item.get('id')
65 |
66 | return citys
67 |
68 | # get_all_citys()
69 |
--------------------------------------------------------------------------------
/朋友圈防折叠/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/朋友圈防折叠/.DS_Store
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | .DS_Store
16 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | xmlns:android
11 |
12 | ^$
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | xmlns:.*
22 |
23 | ^$
24 |
25 |
26 | BY_NAME
27 |
28 |
29 |
30 |
31 |
32 |
33 | .*:id
34 |
35 | http://schemas.android.com/apk/res/android
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | .*:name
45 |
46 | http://schemas.android.com/apk/res/android
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | name
56 |
57 | ^$
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | style
67 |
68 | ^$
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | .*
78 |
79 | ^$
80 |
81 |
82 | BY_NAME
83 |
84 |
85 |
86 |
87 |
88 |
89 | .*
90 |
91 | http://schemas.android.com/apk/res/android
92 |
93 |
94 | ANDROID_ATTRIBUTE_ORDER
95 |
96 |
97 |
98 |
99 |
100 |
101 | .*
102 |
103 | .*
104 |
105 |
106 | BY_NAME
107 |
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | NoCollapseInput
4 | Project NoCollapseInput created by Buildship.
5 |
6 |
7 |
8 |
9 | org.eclipse.buildship.core.gradleprojectbuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.buildship.core.gradleprojectnature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/.settings/org.eclipse.buildship.core.prefs:
--------------------------------------------------------------------------------
1 | connection.project.dir=
2 | eclipse.preferences.version=1
3 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "java.configuration.updateBuildConfiguration": "automatic"
3 | }
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | app
4 | Project app created by Buildship.
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | org.eclipse.buildship.core.gradleprojectbuilder
15 |
16 |
17 |
18 |
19 |
20 | org.eclipse.jdt.core.javanature
21 | org.eclipse.buildship.core.gradleprojectnature
22 |
23 |
24 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/.settings/org.eclipse.buildship.core.prefs:
--------------------------------------------------------------------------------
1 | connection.project.dir=..
2 | eclipse.preferences.version=1
3 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | defaultConfig {
6 | applicationId "com.xingag.preventrubbishapp"
7 | minSdkVersion 21
8 | targetSdkVersion 28
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
12 | javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
13 | }
14 |
15 | buildTypes {
16 |
17 | debug {
18 | minifyEnabled false
19 | }
20 |
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
24 | }
25 | }
26 | compileOptions {
27 | sourceCompatibility = 1.8
28 | targetCompatibility = 1.8
29 | }
30 |
31 | }
32 |
33 |
34 | dependencies {
35 | implementation fileTree(dir: 'libs', include: ['*.jar'])
36 | implementation 'androidx.appcompat:appcompat:1.0.2'
37 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
38 | testImplementation 'junit:junit:4.12'
39 | androidTestImplementation 'androidx.test.ext:junit:1.1.0'
40 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
41 | implementation 'com.wuhenzhizao:titlebar:1.1.4'
42 | implementation 'com.google.android.material:material:1.0.0'
43 | implementation 'com.google.code.gson:gson:2.8.6'
44 | }
45 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/androidTest/java/com/xingag/preventrubbishapp/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.xingag.preventrubbishapp;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest
20 | {
21 | @Test
22 | public void useAppContext()
23 | {
24 | // Context of the app under test.
25 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
26 |
27 | assertEquals("com.xingag.preventrubbishapp", appContext.getPackageName());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
23 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
37 |
38 |
39 |
40 |
41 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/java/com/xingag/preventrubbishapp/App.java:
--------------------------------------------------------------------------------
1 | package com.xingag.preventrubbishapp;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 |
6 | public class App extends Application
7 | {
8 | private static App instance = null;
9 |
10 | public static int phone_type = 0;
11 |
12 | public static App getInstance()
13 | {
14 | return instance;
15 | }
16 |
17 | @Override
18 | public void onCreate()
19 | {
20 | super.onCreate();
21 | instance = this;
22 | phone_type = BuildConfig.phone_type;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/java/com/xingag/preventrubbishapp/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.xingag.preventrubbishapp;
2 |
3 | import android.os.Bundle;
4 | import android.view.View;
5 | import android.widget.Button;
6 | import android.widget.CheckBox;
7 | import android.widget.CompoundButton;
8 | import android.widget.TextView;
9 |
10 | import androidx.appcompat.app.ActionBar;
11 | import androidx.appcompat.app.AppCompatActivity;
12 |
13 | import com.xingag.utils.AccessibilityUtil;
14 |
15 | public class MainActivity extends AppCompatActivity implements View.OnClickListener
16 | {
17 |
18 | //包名和服务名称
19 | private static final String PACKAGE_NAME = "com.xingag.preventrubbishapp";
20 | private static final String SERVICE_NAME = "com.xingag.service.PreventService";
21 |
22 | private AccessibilityUtil accessibilityUtil = null;
23 |
24 | //状态
25 | private TextView status_tv = null;
26 |
27 | private Button open_service_btn = null;
28 |
29 | @Override
30 | protected void onCreate(Bundle savedInstanceState)
31 | {
32 | super.onCreate(savedInstanceState);
33 | setContentView(R.layout.activity_main);
34 |
35 | status_tv = findViewById(R.id.status_tv);
36 | open_service_btn = findViewById(R.id.open_service_btn);
37 | open_service_btn.setOnClickListener(this);
38 |
39 | accessibilityUtil = new AccessibilityUtil(MainActivity.this,
40 | PACKAGE_NAME, SERVICE_NAME);
41 |
42 | //设置标题
43 | ActionBar actionBar = getSupportActionBar();
44 | if (actionBar != null)
45 | {
46 | actionBar.setTitle("不折叠服务");
47 | }
48 |
49 | //判断状态,如果没有开启,就跳到设置界面
50 | openAccessibilityMet();
51 | }
52 |
53 | public void openAccessibilityMet()
54 | {
55 | //服务状态
56 | boolean status = AccessibilityUtil.isAccessibilitySettingsOn(this, SERVICE_NAME);
57 | if (!status)
58 | {
59 | accessibilityUtil.openSettingDialog();
60 | }
61 | }
62 |
63 |
64 | @Override
65 | protected void onResume()
66 | {
67 | super.onResume();
68 | //判断服务状态
69 | boolean status = AccessibilityUtil.isAccessibilitySettingsOn(this, SERVICE_NAME);
70 | status_tv.setText(status ? "开启" : "关闭");
71 | }
72 |
73 | @Override
74 | public void onClick(View view)
75 | {
76 | if (view.getId() == R.id.open_service_btn)
77 | {
78 | accessibilityUtil.openSettingPage();
79 | }
80 | }
81 | }
82 |
83 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/java/com/xingag/service/BaseService.java:
--------------------------------------------------------------------------------
1 | package com.xingag.service;
2 |
3 | import android.accessibilityservice.AccessibilityService;
4 | import android.accessibilityservice.AccessibilityServiceInfo;
5 | import android.annotation.SuppressLint;
6 | import android.annotation.TargetApi;
7 | import android.content.ClipData;
8 | import android.content.ClipboardManager;
9 | import android.content.Context;
10 | import android.os.Build;
11 | import android.os.Bundle;
12 | import android.text.TextUtils;
13 | import android.util.Log;
14 | import android.view.accessibility.AccessibilityEvent;
15 | import android.view.accessibility.AccessibilityManager;
16 | import android.view.accessibility.AccessibilityNodeInfo;
17 |
18 |
19 | import java.util.List;
20 |
21 | /***
22 | * 无障碍服务的基类
23 | */
24 |
25 |
26 | public class BaseService extends AccessibilityService
27 | {
28 |
29 | @SuppressLint("StaticFieldLeak")
30 | private static BaseService mInstance = null;
31 |
32 |
33 | public static BaseService getInstance()
34 | {
35 | if (mInstance == null)
36 | {
37 | mInstance = new BaseService();
38 | }
39 | return mInstance;
40 | }
41 |
42 |
43 | @Override
44 | public void onAccessibilityEvent(AccessibilityEvent event)
45 | {
46 |
47 | }
48 |
49 | @Override
50 | public void onInterrupt()
51 | {
52 |
53 | }
54 |
55 |
56 | /**
57 | * 模拟输入
58 | *
59 | * @param nodeInfo nodeInfo
60 | * @param text text
61 | */
62 | public void inputText(AccessibilityNodeInfo nodeInfo, String text)
63 | {
64 | ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
65 |
66 | Log.d("xag", "字符个数为:" + text.length());
67 | for (int i = 0; i < text.length(); i++)
68 | {
69 | String temp = text.substring(0, i + 1);
70 | Log.e("xag", "输入一次,输入内容是:" + temp);
71 |
72 | //输入内容
73 | Bundle arguments = new Bundle();
74 | arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, temp);
75 | nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
76 |
77 | //下面可以控制输入的速度,这里设置间隔为0.1s
78 | try
79 | {
80 | Thread.sleep(100);
81 | } catch (InterruptedException e)
82 | {
83 | e.printStackTrace();
84 | }
85 | }
86 | }
87 |
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/java/com/xingag/service/PreventService.java:
--------------------------------------------------------------------------------
1 | package com.xingag.service;
2 |
3 | import android.accessibilityservice.AccessibilityService;
4 | import android.annotation.TargetApi;
5 | import android.content.Intent;
6 | import android.os.Build;
7 | import android.text.TextUtils;
8 | import android.util.Log;
9 | import android.view.accessibility.AccessibilityEvent;
10 | import android.view.accessibility.AccessibilityNodeInfo;
11 | import android.widget.Toast;
12 |
13 | import com.xingag.utils.AppUtil;
14 |
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | public class PreventService extends BaseService
19 | {
20 | //所有的节点
21 | public static List nodes = new ArrayList<>();
22 |
23 | private static final String CLASS_NAME_SNS_UPLOAD = "com.tencent.mm.plugin.sns.ui.SnsUploadUI";
24 |
25 | /**
26 | * 连接服务成功后回调该方法
27 | */
28 | @Override
29 | protected void onServiceConnected()
30 | {
31 | super.onServiceConnected();
32 | //缓存中获取数据
33 | Toast.makeText(PreventService.this, "连接服务成功",
34 | Toast.LENGTH_SHORT).show();
35 |
36 | stopSelf();
37 | }
38 |
39 | @Override
40 | public int onStartCommand(Intent intent, int flags, int startId)
41 | {
42 | return super.onStartCommand(intent, flags, startId);
43 | }
44 |
45 | @Override
46 | public void onAccessibilityEvent(AccessibilityEvent event)
47 | {
48 | //如果是朋友圈发布界面
49 | String className = event.getClassName().toString();
50 |
51 | Log.d("xag", "className:" + className);
52 |
53 | if (TextUtils.equals(CLASS_NAME_SNS_UPLOAD, className))
54 | {
55 | Log.e("xag", "朋友圈发布界面");
56 |
57 | //获取剪切板的内容
58 | String content = AppUtil.getClipBoardContent(getApplicationContext());
59 |
60 | if (!TextUtils.isEmpty(content))
61 | {
62 | Log.d("xag", "剪切板内容是:" + content);
63 | inputContent(event, content);
64 | } else
65 | {
66 | Log.d("xag", "剪切板内容为空");
67 | }
68 | }
69 |
70 | }
71 |
72 |
73 | /***
74 | * 回复消息
75 | * @param event
76 | */
77 | private void inputContent(AccessibilityEvent event, String content)
78 | {
79 | //获取输入框元素
80 | //方式一
81 | // AccessibilityNodeInfo chat_edit = findViewByID("com.tencent.mm:id/d41");
82 |
83 | //方式二
84 | nodes.clear();
85 | AppUtil.getAllNodes(getRootInActiveWindow());
86 | AccessibilityNodeInfo chat_edit = findNodeInfoByClassName("android.widget.EditText");
87 |
88 | //把文本输入到输入框内
89 | inputText(chat_edit, content);
90 | }
91 |
92 |
93 | /***
94 | * 通过类名查找元素
95 | * @param className
96 | * @return
97 | */
98 | public AccessibilityNodeInfo findNodeInfoByClassName(String className)
99 | {
100 | for (int i = 0; i < nodes.size(); i++)
101 | {
102 | //当前节点元素及classname
103 | AccessibilityNodeInfo currentNode = nodes.get(i);
104 | String tagClassName = currentNode.getClassName().toString();
105 |
106 | Log.d("xag", "tagClassName:" + tagClassName);
107 | if (TextUtils.equals(tagClassName, className))
108 | {
109 | Log.d("xag", "元素找到了");
110 | return currentNode;
111 | }
112 | }
113 | Log.d("xag", "元素没找到");
114 | return null;
115 | }
116 |
117 |
118 | @Override
119 | public void onInterrupt()
120 | {
121 | Log.d("xag", "无障碍服务被打断");
122 | }
123 |
124 |
125 | @Override
126 | public void onDestroy()
127 | {
128 | super.onDestroy();
129 | Log.d("xag", "无障碍服务销毁");
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/java/com/xingag/utils/AccessibilityUtil.java:
--------------------------------------------------------------------------------
1 | package com.xingag.utils;
2 |
3 | import android.app.ActivityManager;
4 | import android.app.AlertDialog;
5 | import android.content.ComponentName;
6 | import android.content.Context;
7 | import android.content.DialogInterface;
8 | import android.content.Intent;
9 | import android.provider.Settings;
10 | import android.text.TextUtils;
11 |
12 | import com.xingag.preventrubbishapp.R;
13 |
14 | import java.util.List;
15 |
16 | public class AccessibilityUtil
17 | {
18 | private Context mContext;
19 | private String locPackageName;
20 | private String accessibilityClassAbsolutePath;
21 |
22 | /**
23 | * 构造方法
24 | *
25 | * @param mContext
26 | * @param accessibilityClassAbsolutePath
27 | */
28 | public AccessibilityUtil(Context mContext, String locPackageName,
29 | String accessibilityClassAbsolutePath
30 | )
31 | {
32 | this.mContext = mContext;
33 | this.locPackageName = locPackageName;
34 | this.accessibilityClassAbsolutePath = accessibilityClassAbsolutePath;
35 | }
36 |
37 |
38 | /**
39 | * 判断是否有辅助功能权限
40 | *
41 | * @return 是否开启服务
42 | */
43 | public static boolean isAccessibilitySettingsOn(Context context, String className)
44 | {
45 | if (context == null)
46 | {
47 | return false;
48 | }
49 | ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
50 | // 获取正在运行的服务列表
51 | List runningServices =
52 | activityManager.getRunningServices(300);
53 | for (int i = 0; i < runningServices.size(); i++)
54 | {
55 | ComponentName service = runningServices.get(i).service;
56 | if (service.getClassName().equals(className))
57 | {
58 | return true;
59 | }
60 | }
61 | return false;
62 | }
63 |
64 | public boolean isAccessibilitySettingsOn()
65 | {
66 | boolean isAccessibilityOn = false;
67 | try
68 | {
69 | int accessibilityEnabled = Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED);
70 | TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');
71 | if (accessibilityEnabled == 1)
72 | {
73 | String settingValue = Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
74 | if (settingValue != null)
75 | {
76 | TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
77 | splitter.setString(settingValue);
78 | while (splitter.hasNext())
79 | {
80 | String accessabilityService = splitter.next();
81 | //辅助服务关键路径,格式如"com.corget/com.corget.service.UnAccessibilityService"
82 | String service = locPackageName + "/" + accessibilityClassAbsolutePath;
83 | if (accessabilityService.equalsIgnoreCase(service))
84 | {
85 | isAccessibilityOn = true;
86 | }
87 | }
88 | }
89 | }
90 | } catch (Settings.SettingNotFoundException e)
91 | {
92 | e.printStackTrace();
93 | }
94 | return isAccessibilityOn;
95 | }
96 |
97 |
98 | public void openSettingDialog()
99 | {
100 | AlertDialog.Builder mDialogBuilder = new AlertDialog.Builder(mContext);
101 |
102 | mDialogBuilder.setIcon(R.mipmap.ic_launcher);
103 | mDialogBuilder.setTitle("警告");
104 | mDialogBuilder.setMessage("防折叠服务没有开启,请先开启服务");
105 | mDialogBuilder.setCancelable(false);
106 | mDialogBuilder.setPositiveButton("Sure",
107 | new DialogInterface.OnClickListener()
108 | {
109 | @Override
110 | public void onClick(DialogInterface dialog, int which)
111 | {
112 | dialog.dismiss();
113 | openSettingPage();
114 | }
115 | });
116 | mDialogBuilder.setNegativeButton("Cancel",
117 | new DialogInterface.OnClickListener()
118 | {
119 | @Override
120 | public void onClick(DialogInterface dialog, int which)
121 | {
122 | dialog.dismiss();
123 | }
124 | });
125 | mDialogBuilder.create().show();
126 | }
127 |
128 | /***
129 | * 打开设置界面
130 | */
131 | public void openSettingPage()
132 | {
133 | Intent startIntent = new Intent(
134 | Settings.ACTION_ACCESSIBILITY_SETTINGS);
135 | mContext.startActivity(startIntent);
136 | mContext.sendBroadcast(new Intent()
137 | .setAction("com.xingag.setting.dialog"));
138 | }
139 |
140 |
141 | }
142 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/java/com/xingag/utils/AppUtil.java:
--------------------------------------------------------------------------------
1 | package com.xingag.utils;
2 |
3 | import android.content.ClipData;
4 | import android.content.ClipboardManager;
5 | import android.content.Context;
6 | import android.util.Log;
7 | import android.view.accessibility.AccessibilityNodeInfo;
8 |
9 | import com.xingag.service.PreventService;
10 |
11 | import static android.content.Context.CLIPBOARD_SERVICE;
12 |
13 |
14 | public class AppUtil
15 | {
16 |
17 | /***
18 | * 获取剪切板上的内容
19 | * @return
20 | */
21 | public static String getClipBoardContent(Context context)
22 | {
23 | ClipboardManager cm = (ClipboardManager) context.getSystemService(CLIPBOARD_SERVICE);
24 | ClipData cd2 = cm.getPrimaryClip();
25 | String result = "";
26 | try
27 | {
28 | result = cd2.getItemAt(0).getText().toString();
29 | } catch (Exception e)
30 | {
31 | //pass
32 | Log.d("xag", "产生异常了。。。。");
33 | }
34 | return result;
35 | }
36 |
37 | /***
38 | * 获取当前页面所有的元素
39 | * @param node
40 | */
41 | public static void getAllNodes(AccessibilityNodeInfo node)
42 | {
43 | int count = node.getChildCount();
44 |
45 | if (count == 0)
46 | {
47 | PreventService.nodes.add(node);
48 | } else
49 | {
50 | for (int i = 0; i < count; i++)
51 | {
52 | getAllNodes(node.getChild(i));
53 | }
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/res/drawable/btn_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
18 |
19 |
20 |
27 |
28 |
29 |
38 |
39 |
40 |
41 |
48 |
49 |
58 |
59 |
60 |
61 |
62 |
71 |
72 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/res/layout/dialog_bottom_alertmsg.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
15 |
22 |
33 |
34 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/res/layout/item_app_select.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
16 |
21 |
22 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/res/layout/title_bar.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/res/mipmap-xhdpi/ic_back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/朋友圈防折叠/NoCollapseInput/app/src/main/res/mipmap-xhdpi/ic_back.png
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/朋友圈防折叠/NoCollapseInput/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/res/mipmap-xhdpi/ic_sure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/朋友圈防折叠/NoCollapseInput/app/src/main/res/mipmap-xhdpi/ic_sure.png
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | 防折叠
3 | 防止折叠服务\n1、防止微信朋友圈粘贴的内容被折叠\n2、操作前,请先开启服务\n3、如果失效,请重启服务
4 | 1、防止微信朋友圈粘贴的内容被折叠\n2、操作前,请先开启服务\n3、如果失效,请重启服务
5 |
6 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/main/res/xml/accessibility_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/app/src/test/java/com/xingag/preventrubbishapp/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.xingag.preventrubbishapp;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest
13 | {
14 | @Test
15 | public void addition_isCorrect()
16 | {
17 | assertEquals(4, 2 + 2);
18 | }
19 | }
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | google()
6 | jcenter()
7 |
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.5.3'
11 |
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | jcenter()
21 | maven { url 'https://jitpack.io' }
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
29 |
30 | ext {
31 |
32 | compileSdkVersion = 28
33 |
34 | buildToolsVersion = "28.0.2"
35 |
36 | minSdkVersion = 21
37 |
38 | targetSdkVersion = 21
39 |
40 | versionCode = 100
41 |
42 | versionName = "1.0.0"
43 | }
44 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
21 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/朋友圈防折叠/NoCollapseInput/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Jan 14 10:52:15 CST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/readme.md:
--------------------------------------------------------------------------------
1 | # 帮助
2 | 防折叠服务
3 |
--------------------------------------------------------------------------------
/朋友圈防折叠/NoCollapseInput/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | rootProject.name='NoCollapseInput'
3 |
--------------------------------------------------------------------------------
/朋友圈防折叠/防折叠服务.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingag/tools_python/f01d315c46a619828d02ed9327f4264ba3a382d8/朋友圈防折叠/防折叠服务.apk
--------------------------------------------------------------------------------
/视频特殊处理.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: file_utils.py
12 | @time: 2020-06-15 09:35
13 | @description:公众号:AirPython
14 | """
15 |
16 | import hashlib
17 |
18 | from moviepy.editor import *
19 |
20 |
21 | def get_file_md5(file_path):
22 | """
23 | 获取文件的MD5值
24 | :param file_path:
25 | :return:
26 | """
27 | with open(file_path, 'rb') as file:
28 | temp_md5 = hashlib.md5()
29 | temp_md5.update(file.read())
30 | hash_code = str(temp_md5.hexdigest()).lower()
31 | return hash_code
32 |
33 |
34 | def modify_file_md5(file_path):
35 | """
36 | 修改文件的md5值
37 | :param file_path:
38 | :return:
39 | """
40 | with open(file_path, 'a') as file:
41 | file.write("####&&&&")
42 |
43 |
44 | def get_file_md5_2(file_path):
45 | """
46 | 分段读取,获取文件的md5值
47 | :param file_path:
48 | :return:
49 | """
50 | with open(file_path, 'rb') as file:
51 | md5_obj = hashlib.md5()
52 | while True:
53 | buffer = file.read(8096)
54 | if not buffer:
55 | break
56 | md5_obj.update(buffer)
57 | hash_code = md5_obj.hexdigest()
58 | md5 = str(hash_code).lower()
59 | return md5
60 |
61 |
62 | def handle_frame(image_frame):
63 | """
64 | 处理图片帧
65 | :param image_frame:图片帧
66 | :return:
67 | """
68 | image_frame_result = image_frame * 1.2
69 | # 如果颜色值超过255,直接设置为255
70 | image_frame_result[image_frame_result > 255] = 255
71 | return image_frame_result
72 |
73 |
74 | def increase_video_brightness(file_path):
75 | """
76 | 增加视频整体亮度
77 | :param file_path:源视频路径
78 | :return:
79 | """
80 | video = VideoFileClip(file_path)
81 | result = video.fl_image(handle_frame)
82 |
83 | file_path_new = "/Users/xingag/Desktop/new.mp4"
84 | result.write_videofile(file_path_new)
85 |
86 |
87 | def increase_video_brightness2(file_path):
88 | """
89 | 增加视频整体亮度2
90 | :param file_path:源视频路径
91 | :return:
92 | """
93 | # 调整系数值
94 | coefficient_value = 1.2
95 |
96 | video = VideoFileClip(file_path)
97 | file_path_new = "/Users/xingag/Desktop/new.mp4"
98 | video.fx(vfx.colorx, coefficient_value).write_videofile(file_path_new)
99 |
100 |
101 | def decrease_video_brightness(file_path):
102 | """
103 | 降低亮度
104 | :param file_path:
105 | :return:
106 | """
107 | # 调整系数值
108 | coefficient_value = 0.8
109 |
110 | video = VideoFileClip(file_path)
111 | file_path_new = "/Users/xingag/Desktop/new.mp4"
112 | video.fx(vfx.colorx, coefficient_value).write_videofile(file_path_new)
113 |
114 |
115 | def change_video_bhd(file_path):
116 | """
117 | 黑白处理
118 | :param file_path:
119 | :return:
120 | """
121 | video = VideoFileClip(file_path)
122 | file_path_new = "/Users/xingag/Desktop/new.mp4"
123 | video.fx(vfx.blackwhite).write_videofile(file_path_new)
124 |
125 |
126 | def change_video_todo(file_path):
127 | """
128 | 先获取图片帧,单张进行处理,然后合成
129 | :param file_path:
130 | :return:
131 | """
132 | pass
133 |
134 |
135 | if __name__ == "__main__":
136 | file_path = r'/Users/xingag/Desktop/1.mp4'
137 | # print(get_file_md5_2(file_path))
138 | # modify_file_md5(file_path)
139 | # print(get_file_md5_2(file_path))
140 |
141 | # 2、增加亮度
142 | # increase_video_brightness(file_path)
143 | # increase_video_brightness2(file_path)
144 |
145 | # 降低亮度
146 | # decrease_video_brightness(file_path)
147 |
148 | # 3、饱和度
149 | change_video_bhd(file_path)
150 |
151 |
152 |
153 | # 注意:更多复杂的操作可以参考之前的文章
--------------------------------------------------------------------------------
/音频处理/音频尾部处理.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # encoding: utf-8
3 |
4 | """
5 | @version: v1.0
6 | @author: xag
7 | @license: Apache Licence
8 | @contact: xinganguo@gmail.com
9 | @site: http://www.xingag.top
10 | @software: PyCharm
11 | @file: 尾部处理.py
12 | @time: 2021-01-02 09:59
13 | @description:TODO
14 | """
15 |
16 | from pydub import AudioSegment
17 | from pydub.playback import play
18 |
19 | video_path = './raw.mp4'
20 |
21 |
22 | def speed_change(sound, speed=1.0):
23 | """
24 | 改变音频的速度
25 | 参考:https://www.thinbug.com/q/51434897
26 | :param sound:
27 | :param speed:
28 | :return:
29 | """
30 | sound_with_altered_frame_rate = sound._spawn(sound.raw_data, overrides={
31 | "frame_rate": int(sound.frame_rate * speed)
32 | })
33 | return sound_with_altered_frame_rate.set_frame_rate(sound.frame_rate)
34 |
35 |
36 | # 注意:加载视频不需要指定format
37 | audio_sgement = AudioSegment.from_file(video_path)
38 |
39 | # 截取尾部内容
40 | audio_end = audio_sgement[70 * 1000:70 * 1000 + 3000]
41 |
42 | # 变慢速度,具体根据视频速度去调整
43 | audio_end2 = speed_change(audio_end, 0.55)
44 |
45 | # 合并两段音频
46 | audio_result = audio_end + audio_end2
47 |
48 | # 尾部淡出处理
49 | audio_result.fade_out(1000)
50 |
51 | # 实时播放,方便调试
52 | # play(audio_result)
53 |
54 | # 视频导出
55 | audio_result.export("result.wav", format='wav')
56 |
57 |
58 |
--------------------------------------------------------------------------------