下載mp3音樂

      在〈下載mp3音樂〉中尚無留言

本專案以wxPython寫成, 屬較舊的版本, 且也有些bug, 請改用新版PyQt5寫成的版本
MP3下載-PyQt5

方式

本程式先連線到youtube搜尋歌曲影片, 再把網址copy 到 youtube to mp4的網站, 進行轉碼後下載

程式執行的畫面如下

music_1

安裝套件

pip install pywin32 selenium wxPython

UI下載

/files/mp3.fbp

代碼

import wx
import wx.xrc
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import threading, time
import os
import win32api
import win32file


class MainFrame(wx.Frame):
    def __init__(self, parent):
        wx.Frame.__init__(self, parent, id=wx.ID_ANY, title=u"MP3 Terminator", pos=wx.DefaultPosition,
                          size=wx.Size(943, 518), style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL)
        self.SetSizeHints(wx.DefaultSize, wx.DefaultSize)
        self.SetBackgroundColour(wx.Colour(105, 179, 252))
        bSizer1 = wx.BoxSizer(wx.VERTICAL)
        bSizer8 = wx.BoxSizer(wx.HORIZONTAL)
        self.m_panel3 = wx.Panel(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL)
        self.m_panel3.SetBackgroundColour(wx.Colour(255, 255, 128))
        bSizer8.Add(self.m_panel3, 1, wx.EXPAND, 5)
        self.m_staticText3 = wx.StaticText(self, wx.ID_ANY, u"MP3 Terminator", wx.DefaultPosition, wx.DefaultSize,
                                           wx.ALIGN_CENTRE)
        self.m_staticText3.Wrap(-1)
        self.m_staticText3.SetFont(wx.Font(16, 70, 93, 90, False, wx.EmptyString))
        self.m_staticText3.SetForegroundColour(wx.Colour(224, 57, 16))
        self.m_staticText3.SetBackgroundColour(wx.Colour(255, 255, 128))
        bSizer8.Add(self.m_staticText3, 1, wx.EXPAND, 5)
        self.m_staticText6 = wx.StaticText(self, wx.ID_ANY, u"Authorized : Thomas Wu(Ver 1.0.3)", wx.DefaultPosition,
                                           wx.DefaultSize, wx.ALIGN_RIGHT)
        self.m_staticText6.Wrap(-1)
        self.m_staticText6.SetFont(wx.Font(10, 70, 93, 90, False, wx.EmptyString))
        self.m_staticText6.SetForegroundColour(wx.Colour(0, 0, 255))
        self.m_staticText6.SetBackgroundColour(wx.Colour(255, 255, 128))
        bSizer8.Add(self.m_staticText6, 1, wx.EXPAND, 5)
        bSizer1.Add(bSizer8, 0, wx.EXPAND, 5)
        bSizer2 = wx.BoxSizer(wx.HORIZONTAL)
        self.m_staticText1 = wx.StaticText(self, wx.ID_ANY, u"歌名/歌手", wx.DefaultPosition, wx.DefaultSize, 0)
        self.m_staticText1.Wrap(-1)
        bSizer2.Add(self.m_staticText1, 0, wx.ALIGN_CENTER | wx.ALL, 5)
        self.txtSong = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(170, -1), 0)
        bSizer2.Add(self.txtSong, 0, wx.ALIGN_CENTER | wx.ALL, 5)
        self.btnSearch = wx.Button(self, wx.ID_ANY, u"搜尋(&S)", wx.DefaultPosition, wx.DefaultSize, 0)
        bSizer2.Add(self.btnSearch, 0, wx.ALIGN_CENTER | wx.ALL, 5)
        bSizer1.Add(bSizer2, 0, wx.ALIGN_CENTER, 5)
        listBoxChoices = []
        self.listBox = wx.CheckListBox(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, listBoxChoices, 0)
        self.listBox.SetForegroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_3DLIGHT))
        self.listBox.SetBackgroundColour(wx.Colour(210, 210, 255))
        bSizer1.Add(self.listBox, 1, wx.EXPAND, 5)
        bSizer5 = wx.BoxSizer(wx.HORIZONTAL)
        self.m_panel1 = wx.Panel(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL)
        bSizer5.Add(self.m_panel1, 1, wx.EXPAND | wx.ALL, 5)
        bSizer7 = wx.BoxSizer(wx.VERTICAL)
        self.btnDownload = wx.Button(self, wx.ID_ANY, u"下載(&D)", wx.DefaultPosition, wx.Size(150, -1), 0)
        bSizer7.Add(self.btnDownload, 1, wx.ALIGN_CENTER | wx.ALL, 5)
        bSizer5.Add(bSizer7, 1, wx.EXPAND, 5)
        bSizer6 = wx.BoxSizer(wx.HORIZONTAL)
        self.m_panel2 = wx.Panel(self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.TAB_TRAVERSAL)
        bSizer6.Add(self.m_panel2, 1, wx.EXPAND | wx.ALL, 5)
        self.lblPath = wx.StaticText(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(200, -1),
                                     wx.ALIGN_RIGHT)
        self.lblPath.Wrap(-1)
        bSizer6.Add(self.lblPath, 0, wx.ALIGN_CENTER, 5)
        self.btnPath = wx.Button(self, wx.ID_ANY, u"儲存目錄(&P)", wx.DefaultPosition, wx.Size(90, -1), 0)
        bSizer6.Add(self.btnPath, 0, wx.ALL | wx.RIGHT, 5)
        bSizer5.Add(bSizer6, 1, 0, 5)
        bSizer1.Add(bSizer5, 0, wx.EXPAND, 5)
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.lblStatus = wx.StaticText(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(400, -1),
                                       wx.ALIGN_CENTRE)
        self.lblStatus.Wrap(-1)
        self.lblStatus.SetFont(wx.Font(14, 70, 93, 90, False, wx.EmptyString))
        self.lblStatus.SetForegroundColour(wx.Colour(0, 0, 255))
        self.lblStatus.SetBackgroundColour(wx.Colour(255, 128, 255))
        sizer.Add(self.lblStatus, 0, wx.EXPAND, 5)
        bSizer1.Add(sizer, 0, wx.EXPAND, 5)
        self.SetSizer(bSizer1)
        self.Layout()
        self.Centre(wx.BOTH)

        self.Init()

    def __del__(self):
        pass

    def Init(self):
        # 綁定按鈕事件
        self.Bind(wx.EVT_BUTTON, self.BtnSearchClick, self.btnSearch)
        self.Bind(wx.EVT_BUTTON, self.BtnDownloadClick, self.btnDownload)
        self.Bind(wx.EVT_BUTTON, self.BtnPathClick, self.btnPath)
        self.Bind(wx.EVT_CLOSE, self.OnClose)

        # 設定快速鍵
        entries = [wx.AcceleratorEntry() for i in range(3)]
        entries[0].Set(wx.ACCEL_ALT, ord('S'), self.btnSearch.GetId())
        entries[1].Set(wx.ACCEL_ALT, ord('D'), self.btnDownload.GetId())
        entries[2].Set(wx.ACCEL_ALT, ord('P'), self.btnPath.GetId())
        table = wx.AcceleratorTable(entries)
        self.SetAcceleratorTable(table)

        # 選取硬碟
        disk = []
        for i in win32api.GetLogicalDriveStrings().split('\000'):
            if win32file.GetDriveType(i) == 3:
                disk.append(i[:-2])
        self.lblPath.SetLabelText('{0}:\mp3_tmp'.format(disk[len(disk) - 1]))
        if not os.path.isdir(self.lblPath.GetLabelText()):
            os.mkdir(self.lblPath.GetLabelText())

        # 載入Chromedriver
        options = Options()
        options.add_argument('--headless')
        options.add_argument('--disable-gpu')
        options.add_experimental_option('excludeSwitches', ['enable-logging'])
        self.chromePath = "{0}\chromedriver.exe".format(os.getcwd())
        self.web = webdriver.Chrome(executable_path=self.chromePath, options=options)

    # 設定下載目錄
    def BtnPathClick(self, event):
        dlg = wx.DirDialog(self, "Choose a directory:", style=wx.DD_DEFAULT_STYLE)
        if dlg.ShowModal() == wx.ID_OK:
            self.lblPath.SetLabelText(dlg.GetPath())
        dlg.Destroy()

    def BtnSearchClick(self, event):
        self.listBox.Clear()
        self.song = self.txtSong.GetValue()
        if self.song == '':
            wx.MessageBox(u'請輸入歌名')
            return
        self.lblStatus.SetLabelText('Searching....')
        self.btnDownload.Enable(False)
        self.btnSearch.Enable(False)
        self.btnPath.Enable(False)
        self.Layout()
        t = threading.Thread(target=self.SearchMp3)
        t.start()

    def BtnDownloadClick(self, event):
        self.path = self.lblPath.GetLabelText()
        if self.path == '':
            wx.MessageBox(u'請選擇路徑')
            return
        self.btnDownload.Enable(False)
        self.btnSearch.Enable(False)
        self.btnPath.Enable(False)
        t = threading.Thread(target=self.DownloadMp3, args=(self.listBox.GetCheckedStrings(),))
        t.start()

    def SearchMp3(self):
        url = "https://www.youtube.com/results?search_query={0}".format(self.song)
        self.web.get(url)
        tags = self.web.find_elements_by_tag_name('a')
        links = {}
        for tag in tags:
            href = tag.get_attribute('href')
            if 'watch' in str(href):
                title = tag.get_attribute('title')
                if title == '':
                    try:
                        title = tag.find_element_by_id('video-title').get_attribute('title')
                    except:
                        pass
                if title != '':
                    links[href] = '{0} url={1}'.format(title, href)
        if len(links) == 0: return
        wx.CallAfter(self.AddList, links)

    def AddList(self, links):
        for key in links.keys():
            self.listBox.Append(links[key])
        self.lblStatus.SetLabelText('')
        self.btnSearch.Enable(True)
        self.btnDownload.Enable(True)
        self.btnPath.Enable(True)
        self.Layout()

    def DownloadMp3(self, items):
        # 開啟chromedriver允許下載設定
        self.web.command_executor._commands["send_command"] = ("POST", '/session/$sessionId/chromium/send_command')
        params = {'cmd': 'Page.setDownloadBehavior', 'params': {'behavior': 'allow', 'downloadPath': self.path}}
        for itme in items:
            title = itme.split('url=')[0]
            wx.CallAfter(self.DrawUI, u'正在下載 : {0}'.format(title))
            self.web.get('https://www.youtubeto.com/zh/')
            txt = self.web.find_element_by_id('url')
            txt.send_keys(itme.split('url=')[1])
            btn = self.web.find_element_by_id('DownloadMP3_text')
            btn.click()
            command_result = self.web.execute("send_command", params)
            time.sleep(3)
        while self.web.page_source.find('allow-same-origin') == -1:
            time.sleep(0.1)
        wx.CallAfter(self.DrawUI, u"下載完成")
        wx.CallAfter(self.DownloadFinished)

    def DrawUI(self, s):
        self.lblStatus.SetLabel(s)
        self.Layout()

    def DownloadFinished(self):
        # 取消 CheckListBox選取
        for i in range(self.listBox.GetCount()):
            self.listBox.Check(i, check=False)

        self.btnDownload.Enable(True)
        self.btnSearch.Enable(True)
        self.btnPath.Enable(True)
        self.Layout()

    def OnClose(self, event):
        self.web.quit()
        app.ExitMainLoop()

if __name__ == '__main__':
    app = wx.App()
    frame = MainFrame(None)
    frame.Show()
    app.MainLoop()

新版

待解決 — 儲存位置, 需按一下youtubeto網站需再按一下 mp3

    def Init(self):
        options = Options()
        #options.add_argument('--headless')
        options.add_argument('--disable-gpu')
        options.add_experimental_option('excludeSwitches', ['enable-logging'])
        self.browser = webdriver.Chrome('chromedriver.exe', options=options)
        self.lblPath.SetLabelText("c:/tmp_mp3")
        self.btnSearch.Bind(wx.EVT_BUTTON, self.btnSearch_Click)
        self.btnDownload.Bind(wx.EVT_BUTTON, self.btnDownload_Click)
    def btnSearch_Click(self, event):
        self.listBox.Clear()
        self.song=self.txtSong.GetValue()
        if self.song=='':
            wx.MessageBox('歌曲名稱不可為空白')
            return
        self.btnSearch.Enable(False)
        self.btnDownload.Enable(False)
        self.btnPath.Enable(False)
        self.lblStatus.SetLabelText("搜詢中.....")
        self.Layout()
        t=threading.Thread(target=self.Search)
        t.start()
    def Search(self):
        url = "https://www.youtube.com/results?search_query={0}".format(self.song)
        self.browser.get(url)
        tags = self.browser.find_elements_by_tag_name('a')
        links = {}
        for tag in tags:
            href = tag.get_attribute('href')
            if 'watch' in str(href):
                title=tag.get_attribute('title')
                if title=='':
                    try:
                        title=tag.find_element_by_tag_id('video-title').get_attribute('title')
                    except:
                        pass
                if title!='':
                    links[href]='{0} url={1}'.format(title, href)
        if len(links) == 0: return
        wx.CallAfter(self.AddList, links)
    def AddList(self, links):
        for key in links.keys():
            self.listBox.Append(links[key])
        self.lblStatus.SetLabelText('')
        self.btnSearch.Enable(True)
        self.btnDownload.Enable(True)
        self.btnPath.Enable(True)
        self.Layout()
    def btnDownload_Click(self, event):
        self.path = self.lblPath.GetLabelText()
        if self.path == '':
            wx.MessageBox(u'請選擇路徑')
            return
        self.btnDownload.Enable(False)
        self.btnSearch.Enable(False)
        self.btnPath.Enable(False)
        t = threading.Thread(target=self.DownloadMp3, args=(self.listBox.GetCheckedStrings(),))
        t.start()
    def DownloadMp3(self, items):
        #允許Chrome 可以下載檔案
        self.browser.command_executor._commands["send_command"] = ("POST", '/session/$sessionId/chromium/send_command')
        params = {'cmd': 'Page.setDownloadBehavior', 'params': {'behavior': 'allow', 'downloadPath': self.path}}
        for item in items:
            title=item.split(' url=')[0]
            url=item.split(' url=')[1].replace('youtube', 'youtubeto')
            wx.CallAfter(self.DrawUi, title)
            self.browser.get(url)
        wx.CallAfter(self.DownloadFinished)
    def DrawUi(self, msg):
        self.lblStatus.SetLabelText('{0}下載中...'.format(msg))
    def DownloadFinished(self):
        self.lblStatus.SetLabelText('下載完成')
        self.btnSearch.Enable(True)
        self.btnDownload.Enable(True)
        self.btnPath.Enable(True)
app=wx.App()
frame=MainFrame(None)
frame.Show()
app.MainLoop()

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *