# -*- coding: utf-8 -*-
import subprocess
import re
import time
import threading
import time
from datetime import datetime
from .utils import str_to_time, time_to_str
from .api import BiliBiliVoice
from .storage import Storage
from .ui import Ui
from .mpv import MPV
from . import logger
log = logger.getLogger(__name__)
[文档]class MyMPV(MPV):
"""
根据mpv基类,构造自己需要的mpv功能
例如获取当前的播放进度
.. code-block:: python
def on_property_time_pos(self, position=None):
if position is None:
return
self.process_location = int(position)
return
例如构造播放功能
.. code-block:: python
def play(self):
self.set_property("pause", False)
.. note:: 提示
具体需要获取哪些属性和方法,可以参考mpv的官方文档
https://mpv.io/manual/stable/
"""
#-------------------------------------------------------------------------
# Initialization.
#-------------------------------------------------------------------------
# The mpv process and the communication code run in their own thread
# context. This results in the callback methods below being run in that
# thread as well.
def __init__(self, path):
# Pass a window id to embed mpv into that window. Change debug to True
# to see the json communication.
super().__init__(window_id=None, debug=False)
self.process_location = 0
self.remaining = 0
self.command("loadfile", path, "append")
self.set_property("playlist-pos", 0)
self.loaded = threading.Event()
self.loaded.wait()
#-------------------------------------------------------------------------
# Callbacks
#-------------------------------------------------------------------------
# The MPV base class automagically registers event callback methods
# if they are specially named: "file-loaded" -> on_file_loaded().
[文档] def on_file_loaded(self):
self.loaded.set()
# The same applies to property change events:
# "time-pos" -> on_property_time_pos().
[文档] def on_property_time_pos(self, position=None):
"""
返回时间,单位:秒
:return: 时间,单位:秒
"""
if position is None:
return
self.process_location = int(position)
return
[文档] def on_property_length(self, length=None):
if length is None:
return
[文档] def on_property_time_remaining(self, remaining=None):
if remaining is None:
return
self.remaining = int(remaining)
return remaining
#-------------------------------------------------------------------------
# Commands
#-------------------------------------------------------------------------
# Many commands must be implemented by changing properties.
[文档] def new_play(self, url):
self.command("loadfile", url, "replace")
self.set_property("playlist-pos", 0)
[文档] def add_music_list(self, url):
self.command("loadfile", url, 'append-play')
[文档] def play(self):
self.set_property("pause", False)
[文档] def pause(self):
self.set_property("pause", True)
[文档] def seek(self, position):
self.command("seek", position, "absolute")
[文档] def resume(self):
self.set_property("pause", False)
[文档] def stop(self):
self.command("stop")
[文档]class Player(object):
"""
音乐播放播放的类
"""
def __init__(self):
self.ui = Ui()
self.bilibilivoice = BiliBiliVoice()
self.playing_flag = False
self.pause_flag = False
self.player = None
self.total_flag = False # 进度条是否可以判定为结束的标志
self.change_flag = False # 切歌标志
self.now_time = ''
self.total_time = ''
self.play_thread = None
self.data_list = None
self.playing_index = 0 # 正在播放的音乐在播放池中的位置
self.process_length = 0 # 总的播放时间
self.process_location = 0 # 当前播放进度
self.playing_id = 0 # 正在播放的音乐的av号
self.storage = Storage()
self.info = self.storage.database['player_info']
self.songs = self.storage.database['songs']
[文档] def play(self, av_number):
"""
启动播放器
:param av_number: 播放的B站av_id
"""
self.playing_flag = True
self.playing_id = av_number
play_url = 'https://www.bilibili.com/video/av{av_number}'.format(av_number=av_number)
def runInThread():
self.player = MyMPV(play_url)
self.player.play()
while True:
if self.playing_flag is False:
break
process_location = self.process_length
self.process_location = int(self.player.process_location)
self.remaining = int(self.player.remaining)
self.now_time = time_to_str(self.process_location)
if not self.total_flag:
self.process_length = self.bilibilivoice.get_play_total_time(self.playing_id)
if self.process_length is not 0:
self.total_flag = True
# 此时判定为该音乐播放完成
if abs(self.process_length - self.process_location) < 2:
self.total_flag = False
self.change_flag = True
self.total_time = time_to_str(self.process_length)
if self.process_location == self.process_length and self.process_length is not 0:
self.playing_flag = False
if not self.pause_flag and self.process_length == process_location:
a = datetime.now()
while process_location == self.process_location:
self.process_location = int(self.player.process_location)
b = datetime.now()
if (b-a).seconds > 20:
log.debug('changing song or playing song die, av number:' + str(self.playing_id))
self.change_flag = True
break
self.play_thread = threading.Thread(target=runInThread, args=())
self.play_thread.setDaemon(True)
self.play_thread.start()
self.ui.build_loading()
# TODO 切换新的歌曲时,location进度条临时性没有归零
[文档] def new_play(self, av_number):
"""
切换新的播放歌曲
:param av_number: 播放的B站av_id
"""
self.playing_id = av_number
self.process_location = 0
self.now_time = time_to_str(self.process_location)
play_url = 'https://www.bilibili.com/video/av{av_number}'.format(av_number=av_number)
self.total_flag = False
self.player.new_play(play_url)
[文档] def add_music_list(self, av_number):
"""
加入歌曲到播放列表
:param av_number: 播放的B站av_id
"""
play_url = 'https://www.bilibili.com/video/av{av_number}'.format(av_number=av_number)
self.player.add_music_list(play_url)
[文档] def play_and_pause(self):
"""
播放或者暂停播放
:meth:`pause`
:meth:`resume`
"""
if not self.pause_flag:
self.pause()
else:
self.resume()
[文档] def stop_music(self):
"""
退出播放
"""
if self.playing_flag and self.player:
self.playing_flag = False
try:
self.player.close()
except IOError as e:
log.error(e)
[文档] def quit(self):
"""
退出播放
"""
if self.playing_flag and self.player:
self.playing_flag = False
try:
self.player.close()
except IOError as e:
log.error(e)
[文档] def pause(self):
"""
暂停播放 如果当前没有正在播放音乐,或者播放类为空则直接跳过
"""
if not self.playing_flag and not self.player:
return
self.pause_flag = True
try:
self.player.pause()
except IOError as e:
log.error(e)
return
[文档] def resume(self):
"""
继续播放
"""
self.pause_flag = False
try:
self.player.resume()
except IOError as e:
log.error(e)
return
if __name__ == '__main__':
player = Player()
player.play('https://www.bilibili.com/video/av16860566/')
time.sleep(10)
player.stop()