轻量级mp3播放器

MP3播放器

效果
在这里插入图片描述

import threading
import traceback
import os
from ctypes import windll, c_buffer
import random
import time

'''
Inspired by (but not copied from) Michael Gundlach <gundlach@gmail.com>'s mp3play:
    https://github.com/michaelgundlach/mp3play
在上位大神的基础上修改了其源码,并封装成类
'''

#_mci里有个!!异常捕捉,调试时必须开启
class _mci:
    def __init__(self):
        self.w32mci = windll.winmm.mciSendStringA
        self.w32mcierror = windll.winmm.mciGetErrorStringA

    def send(self, command):
        buffer = c_buffer(255)
        errorcode = self.w32mci(str(command).encode(), buffer, 254, 0)
        if errorcode:
            return errorcode, self.get_error(errorcode)
        else:
            return errorcode, buffer.value

    def get_error(self, error):
        error = int(error)
        buffer = c_buffer(255)
        self.w32mcierror(error, buffer, 254)
        return buffer.value

    def directsend(self, txt):
        (err, buf) = self.send(txt)
        if err != 0:
            pass
            #!!异常捕捉 print('Error %s for "%s": %s' % (str(err), txt, buf))
        return (err, buf)


class AudioClip(object):
    def __init__(self, filename):
        filename = filename.replace('/', '\\')
        self.filename = filename
        self._alias = "mp3-1"
        self._mci = _mci()

        self._mci.directsend(r'open "%s" alias %s' % (filename, self._alias))
        self._mci.directsend('set %s time format milliseconds' % self._alias)

        err, buf = self._mci.directsend('status %s length' % self._alias)
        self._length_ms = int(buf)

    def volume(self, level):
        """Sets the volume between 0 and 100."""
        self._mci.directsend('setaudio %s volume to %d' %
                             (self._alias, level * 10))

    def start(self,filename):
        filename = filename.replace('/', '\\')
        self._mci.directsend(r'open "%s" alias %s' % (filename, self._alias))
        self._mci.directsend('set %s time format milliseconds' % self._alias)
        err, buf = self._mci.directsend('status %s length' % self._alias)
        self._length_ms = int(buf)

    def play(self, start_ms=None, end_ms=None):
        start_ms = 0 if not start_ms else start_ms
        end_ms = self._length_ms if not end_ms else end_ms
        err, buf = self._mci.directsend('play %s from %d to %d'
                                        % (self._alias, start_ms, end_ms))
        return err, buf

    def isplaying(self):
        return self._mode() == 'playing'

    def _mode(self):

        # self._mci.directsend('pause %s' % self._alias)
        # self._mci.directsend('resume %s' % self._alias)
        err, buf = self._mci.directsend('status %s mode' % self._alias)
        return buf


    def pause(self):
        self._mci.directsend('pause %s' % self._alias)

    def resume(self):
        self._mci.directsend('resume %s' % self._alias)

    def ispaused(self):
        return self._mode() == 'paused'

    def stop(self):
        self._mci.directsend('close %s' % self._alias)
        # self._mci.directsend('stop %s' % self._alias)
        # self._mci.directsend('seek %s to start' % self._alias)

    def milliseconds(self):
        return self._length_ms

    # TODO: this closes the file even if we're still playing.
    # no good.  detect isplaying(), and don't die till then!
    def __del__(self):
        self._mci.directsend('close %s' % self._alias)


class MyMusic():
    def __init__(self,music_set):
        self.music_thread = None  # 音乐线程,第三只手,用于检测状态,支持播放完后切换歌曲,检测用户输入,切换功能
        self.mymusic = None
        self.music_set = music_set  # 音乐播放列表
        self.music_set_del = set()  # 已播放
        self.type = "random"  # 用户选择的播放类型 random,normal
        self.mp3id = "" # mp3的ID,用于windll指令
        self.state = None # mp3id音乐的当前状态
        self.musicname = None # 将要播放的音乐名,实际上是这个音乐文件的路径
        self.is_over = False # 音乐是否完毕,主线程里如果发现是true,可以提示是否再次打开线程
        # mp3id音乐的用户操作None代表无操作,next代表切换歌曲指令,stop代表暂停
        self.useraction = None


    def start(self):
        # 获取要播放的音乐文件名
        self.get_music_filename()
        # 创建对象播放音乐
        self.mymusic = AudioClip(self.musicname)
        self.mymusic.play()
        self.get_state()

        # 启动线程
        self.music_thread = threading.Thread(target=self.__music_thread)  # ,args=(self.music_set)
        self.music_thread.start()

    #-----------  主线程方法  ------------
    # 由于只有主线程可以调用windll的指令,所以把用户的键盘输入放入到子线程中去了
    def __music_thread(self):
        #音乐线程,第三只手,用于检测状态,支持播放完后切换歌曲,检测用户输入,切换功能
        while True:
            music.useraction = input()

    def get_music_filename(self):
        # 通过type判断播放类型,拿出要播放的文件名!!
        if self.type == "random":
            musicname = random.choice(list(self.music_set - self.music_set_del))  # 删除已播放的
        else:
            musicname = list(self.music_set - self.music_set_del)[0]
        self.music_set_del.add(musicname)  # 存入已播放列表
        self.musicname = musicname
        print("正在播放 :" + musicname[musicname.rfind("\\") + 1:])

    def get_state(self):
        # 获取mp3id的状态
        self.state = self.mymusic._mode()
        return self.mymusic._mode()

    def play(self):
        self.mymusic._alias = "mp3-2"
        self.mymusic = AudioClip(self.musicname)
        self.mymusic.play()

    def stop(self):
        self.mymusic.stop()

    def resume(self):
        self.mymusic.resume()

    def pause(self):
        self.mymusic.pause()

    def next(self):
        if self.music_set - self.music_set_del:
            self.stop()
            self.get_music_filename()
            self.play()
        else:
            print("已是最后一首了")

    def replay(self):
        self.stop()
        # self.get_music_filename()
        self.play()

    def change_type(self):
        if str(self.type) == "normal":
            self.type = "random"
        else:
            self.type = "normal"


if __name__ == "__main__":
    try:
        print("--------自制MP3音乐播放器---------")
        print("说明:")
        print("    指定文件夹路径: 直接 Enter 默认播放music文件夹下的音乐")
        print("操作指南:")
        print("    输入p可暂停")
        print("    输入r可继续")
        print("    输入n可切换下一首歌曲")
        print("    输入c可切换播放模式")
        print("    输入RP可重新播放该曲")
        print("    关闭点击右上角叉叉")

        print("-------------Enjoy------------")
        file_path = ""
        # file_path = input("请输入文件路径,或回车播放music下的音乐:")

        if file_path == "":
            file_path = os.getcwd()+"\\music\\"

        # 检查文件中是否存在mp3文件并存入集合
        myset = set()
        for root, dirs, files in os.walk(file_path):
            # print(root)
            if root == file_path:
                for f in files:
                    if f.endswith(".mp3"):
                        myset.add(file_path+f)
                break
        if len(myset) == 0:
            print(file_path+"里没有找到mp3文件 T。T")


        # music = MyMusic({"F:/pywork/fight/fight_music3/music/Fade-AlanWalker.mp3",
        # "F:/pywork/fight/fight_music3/music/NobodyCanSaveMe-LinkinPark.mp3"})

        while True:
            music = MyMusic(myset)
            print("当前播放模式:" + music.type)
            print("播放文件目录:" + file_path)
            music.start()
            while True:
                music.state = music.get_state() # 检测状态
                time.sleep(1)

                # 最后一首歌执行完毕跳出循环,结束线程,用户无法再输入
                if (not music.music_set - music.music_set_del) and music.state == b'stopped':
                    print("音乐播放完毕")
                    break

                # 播放完一首后自动播放下一首
                if music.state == b'stopped' and music.useraction == None:
                    music.stop()
                    # 选择下一个要播放的文件
                    music.get_music_filename()
                    # 播放
                    music.play()
                    continue

                # 指令!!
                if music.useraction != None:
                    if music.useraction.upper() == "P":
                        music.pause()
                    elif music.useraction.upper() == "R":
                        music.resume()
                    elif music.useraction.upper() == "N":
                        music.next()
                    elif music.useraction.upper() == "C":
                        music.change_type()
                        print("当前播放模式:"+ music.type)
                    elif music.useraction.upper() == "RP":
                        music.replay()


                music.useraction = None # 清空指令防止重复运行
                time.sleep(1)

            if input("是否要重新播放?y/n") == "y":
                continue
            else:
                break



    except SystemExit:
        pass
    except:
        traceback.print_exc()

        input()











  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值