From 7aac9d4fa396d37ce2f3ff2b2f44033d4c785085 Mon Sep 17 00:00:00 2001 From: nichuanfang Date: Wed, 11 Oct 2023 00:22:47 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20=E5=AE=8C=E6=88=90=E4=BA=86?= =?UTF-8?q?=E9=9F=B3=E4=B9=90=E4=B8=8B=E8=BD=BD=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/release_pypi.yml | 1 + README.md | 12 ++- mtk/__main__.py | 147 ++++++++++++++++++++++++++++- mtk/mp3_util.py | 98 +++++++++++++++++++ mtk/requirements.txt | 0 requirements.txt | 4 + setup.py | 4 +- 7 files changed, 259 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/release_pypi.yml create mode 100644 mtk/mp3_util.py delete mode 100644 mtk/requirements.txt create mode 100644 requirements.txt diff --git a/.github/workflows/release_pypi.yml b/.github/workflows/release_pypi.yml new file mode 100644 index 0000000..740d1f1 --- /dev/null +++ b/.github/workflows/release_pypi.yml @@ -0,0 +1 @@ +# 推送到pypi diff --git a/README.md b/README.md index 4272eff..10422da 100644 --- a/README.md +++ b/README.md @@ -11,15 +11,21 @@ pip install -U music-tool-kit - 音乐下载 ```bash -mtk download 网址 输出 [封面url] +mtk 网址 [输出] [封面url] ``` -> tips: 输出格式为 歌曲名[-歌手] 输出歌曲格式为 mp3, 封面 url 可选 +> tips: 输出格式为 歌曲名[-歌手] 输出歌曲格式为 mp3 可选, 封面 url 也可选 - 音乐剪辑 ```bash -mtk clip 输入的文件 开始时间 结束时间 +mtk -clip 输入的文件 开始时间 结束时间 ``` > tips: 时间格式为: 00:00:00 + +- 音乐伴奏提取 + +```bash +mtk -extract 输入的文件 输出的文件 +``` diff --git a/mtk/__main__.py b/mtk/__main__.py index 686a762..6d68f12 100644 --- a/mtk/__main__.py +++ b/mtk/__main__.py @@ -1,2 +1,145 @@ -def main(): - print('hello world') \ No newline at end of file +import sys +import requests +from mtk.mp3_util import MP3 +from yt_dlp import YoutubeDL + +# 提取yt_dlp信息 +def extract_info(url): + ydl = YoutubeDL() + info = ydl.extract_info(url, download=False) + return info + +def download(url:str,title:str|None=None,cover_url:str|None=None): + """下载mp3格式的音乐 + Args: + url (str): 歌曲网址 + name (str): 歌曲[-歌手] + cover_url (str, optional): 封面url. Defaults to None. + """ + if title != None: + outtmpl = f'{title}.%(ext)s' + else: + outtmpl = '%(title)s.%(ext)s' + + ydl_opts = { + 'quiet': True, + 'no_color': True, + 'format': 'bestaudio/best', + 'outtmpl': outtmpl, + 'postprocessors': [{ + 'key': 'FFmpegExtractAudio', + 'preferredcodec': 'mp3', + 'preferredquality': 0 + }], + } + with YoutubeDL(ydl_opts) as ydl: + ydl.download([url]) + + if title != None: + mp3 = MP3(f'{title}.mp3') + if title.find('-') != -1: + song = title.split('-')[0] + artist = title.split('-')[1] + mp3.add_title(song) + mp3.add_artist(artist) + else: + mp3.add_title(title) + if cover_url != None: + mp3.add_cover(cover_url) + mp3.save() + else: + info = extract_info(url) + title = info['title'] + mp3 = MP3(f'{title}.mp3') + mp3.add_title(title) + if cover_url != None: + mp3.add_cover(cover_url) + else: + thumbnail = info['thumbnail'] + mp3.add_cover(thumbnail) + mp3.save() + + print('下载完成!') + +def clip(path:str,start:str,end:str): + """剪辑音乐 + + Args: + path (str): 歌曲文件路径 + start (str): 开始时间(格式 00:00:00) + end (str): 结束时间(格式 00:00:00) + """ + pass + +def main(args=None): + if args == None: + args = sys.argv[1:] + # 校验args + if len(args) == 0: + print('configuration:\n\n' + '---------------------------------------------\n'+ + '下载: mtk url [title] [cover_url]\n'+ + '剪辑: mtk --clip path start end\n' + '---------------------------------------------\n' + ) + return + flag = args[0] + if flag == '--clip': + path = args[1] + start = args[2] + end = args[3] + clip(path,start,end) + elif flag == '-extract': + pass + else: + # 默认下载 + # 判断flag是否是网址 + if flag.startswith(('http://','https://')): + url = flag + if len(args) == 1: + download(url,None,None) + elif len(args) == 2: + title_or_url = args[1] + if title_or_url.startswith(('http://','https://')): + download(url,None,title_or_url) + else: + download(url,title_or_url,None) + elif (len(args) == 3): + if args[1].startswith(('http://','https://')): + print('歌曲名称不合法!') + return + if not args[2].startswith(('http://','https://')): + print('封面url不合法!') + return + download(url,args[1],args[2]) + else: + print('非法参数!') + else: + print('请输入合法的网址!') + return + +if __name__ == '__main__': + # add_cover('out.mp3','https://yt3.googleusercontent.com/ytc/APkrFKYi81RwDYPJx9n1cZzI3jT3nQv1PmB0QPlNk2Ruhw=s900-c-k-c0x00ffffff-no-rj') + # add_title('out.mp3','test_title') + # add_artist('out.mp3','test_artist') + + # mp3 = MP3('out.mp3') + # mp3.add_cover('https://yt3.googleusercontent.com/ytc/APkrFKYi81RwDYPJx9n1cZzI3jT3nQv1PmB0QPlNk2Ruhw=s900-c-k-c0x00ffffff-no-rj') + # mp3.add_title('test_title') + # mp3.add_artist('test_artist') + # mp3.add_album('test_album') + # mp3.save() + + + # info = extract_info('https://www.youtube.com/watch?v=zq-lIBwhWLk') + # # 获取缩略图url + # thumbnail = info['thumbnail'] + # # 获取标题 + # title = info['title'] + + + # download('https://youtu.be/xsk1SLmf9a0?si=zj8z06UGwLAKEMOj','Handbook - See The World','https://i2.cdn.turner.com/cnn/2008/WORLD/asiapcf/09/03/ta.jaychou/art.jaychou.jpg') + + pass + + \ No newline at end of file diff --git a/mtk/mp3_util.py b/mtk/mp3_util.py new file mode 100644 index 0000000..815e0c5 --- /dev/null +++ b/mtk/mp3_util.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +import requests +import os +from mutagen.id3 import ID3, APIC, TIT2, TPE1, TALB + +def get_image_bytes(image_url): + # 发送HTTP请求,获取图片内容 + response = requests.get(image_url) + + # 将图片内容转换为字节数据 + image_bytes = response.content + + return image_bytes + +# webp转jpg +def webp2jpg(webp_path,jpg_path): + from PIL import Image + im = Image.open(webp_path).convert('RGB') + im.save(jpg_path, 'jpeg') + # 删除webp文件 + os.remove(webp_path) + # 返回jpg文件字节码 + with open(jpg_path,'rb') as f: + return f.read() + +class MP3: + + def __init__(self,mp3path:str) -> None: + """mp3对象 + + Args: + mp3path (str): mp3文件路径 + """ + try: + self.mp3path = mp3path + self.songFile = ID3(mp3path) + except: + print('mp3路径不存在!') + return + + # 给mp3文件添加封面 + def add_cover(self,cover_url): + mp3_name = self.mp3path.split('.')[0] + cover_extension = cover_url.split('.')[-1] + + response = requests.get(cover_url) + if response.status_code != 200: + print('封面url不合法!') + return + image_data = response.content + if cover_extension == 'webp': + with open(f'{mp3_name}.{cover_extension}','wb') as f: + f.write(response.content) + image_data = webp2jpg(f'{mp3_name}.{cover_extension}',f'{mp3_name}.jpg') + os.remove(f'{mp3_name}.jpg') + + # 插入封面 + self.songFile['APIC'] = APIC( + encoding=0, + mime='image/jpg', + type=3, + desc=u'Cover', + data=image_data + ) + print(f'封面{cover_url}添加完成') + + # 给mp3文件添加歌名 + def add_title(self,title): + # 插入歌名 + self.songFile['TIT2'] = TIT2( + encoding=3, + text=title + ) + print(f'歌名{title}添加完成') + + # 给mp3文件添加歌手 + def add_artist(self,artist): + # 插入歌手 + self.songFile['TPE1'] = TPE1( + encoding=3, + text=artist + ) + print(f'歌手{artist}添加完成') + + # 给mp3文件添加专辑 + def add_album(self,album): + # 插入专辑 + self.songFile['TALB'] = TALB( + encoding=3, + text=album + ) + print(f'专辑{album}添加完成') + + def save(self): + # 保存 + self.songFile.save() + print('保存完成') + \ No newline at end of file diff --git a/mtk/requirements.txt b/mtk/requirements.txt deleted file mode 100644 index e69de29..0000000 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..510fcd5 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +yt-dlp>=2023.10.7 +ffmpeg>=1.4 +requests>=2.26.0 +mutagen>=1.47.0 \ No newline at end of file diff --git a/setup.py b/setup.py index e4061a2..92690e3 100644 --- a/setup.py +++ b/setup.py @@ -3,12 +3,12 @@ with open('README.md', 'r', encoding='utf-8') as f: readme = f.read() -with open('mtk/requirements.txt', 'r', encoding='utf-8') as f: +with open('requirements.txt', 'r', encoding='utf-8') as f: requirements = f.read() setup( name='music-tool-kit', - version='0.0.3', + version='0.0.4', description='A tool kit for music download and clip', long_description_content_type='text/markdown', long_description=readme,