Python video player

이상학·2021년 11월 6일
0

Toyproejct

목록 보기
2/2

자사 서비스 영상을 USB에 담아 고객에게 전달하려 한다. 이때, 영상자체를 재생해줄 플레이어를 만들어 exe파일로 만들어 준다면 영상전달효과가 더 극대화 될 것이란 생각에 자사 맞춤형 플레이어를 만들기로 했다.

cutomer flow
1. 영상 구매
2. USB 수령
3. USB속 EXE파일(영상 플레이어) 실행
4. 영상감상

DEV condition
1. 고객은 영상파일에 대한 직접적인 접근을 하지 못해야한다.
2. 그러면서도 직접적으로 영상파일을 재생할수있어야한다
--> 영상파일을 줘야하는데 파일로의 직접적 접근은 막아야한다

  1. 오프라인 환경에서도 재생가능해야한다
    --> 인터넷 끊겨도 재생가능

1,2번에 대한 solution으로

  • S3에 업로드한 파일을 QUrl로 online video streaming 한다.
  • 파일에 lock을 걸어놓는다.
  • IPFS 으로 파일을 분산저장시킨다음 플레이어로 분산된 미디어 조각 모은후 플레이

3번에 대한 solution

  • online video streaming
  • IPFS

최종 결론은 고객이 exe파일을 실행시키면 exe파일 Content속 미디어 파일조각이 Exe파일을 실행한 컴퓨터속에 (/var/usr/...의 디렉토리)분산저장되고, 플레이 버튼을 누르면 특정 해쉬값을 기준으로 미디어 파일조각을 모아 재생한다.

파이썬 GUI를 만들기 쉽게 도와주는 대표적인 Library엔 Tkinter, pyqt5가 있다. 개인적으로 Zepli, Figma Plugin이 많아 확장성이 더 넓은 Tkinter를 선호한다. 반면 pyqt5는 관련 플러그인도 적고 가볍다. 빠르게 외부 디자인을 신경쓰지 않았기에 이번 토이프로젝트에선 pyqt5를 선택했다.
PYQT5 Project

Qt is set of cross-platform C++ libraries that implement high-level APIs for accessing many aspects of modern desktop and mobile systems. These include location and positioning services, multimedia, NFC and Bluetooth connectivity, a Chromium based web browser, as well as traditional UI development.
PyQt5 is a comprehensive set of Python bindings for Qt v5. It is implemented as more than 35 extension modules and enables Python to be used as an alternative application development language to C++ on all supported platforms including iOS and Android.
PyQt5 may also be embedded in C++ based applications to allow users of those applications to configure or enhance the functionality of those applications.
from PyQt5.QtCore import QDir, Qt, QUrl
from PyQt5.QtMultimedia import QMediaContent, QMediaPlayer
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtWidgets import (QApplication, QFileDialog, QHBoxLayout, QLabel,
        QPushButton, QSizePolicy, QSlider, QStyle, QVBoxLayout, QWidget, QMessageBox)
from PyQt5.QtWidgets import QMainWindow,QWidget, QPushButton, QAction
from PyQt5.QtGui import QIcon
import sys,os
from PyQt5.QtCore import QSize
from PyQt5.QtGui import QMovie, QPainter, QPixmap
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QWidget

class VideoPlayer(QMainWindow):
  try:
      a = sys._MEIPASS
  except AttributeError:
      a = os.getcwd()
  path = os.path.join(a)
  limit=10
  def __init__(self):
      super().__init__()
      self.setWindowTitle("ONLY FOR 김금운") 
      self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.VideoSurface)
      videoWidget = QVideoWidget()
      self.playButton = QPushButton()
      #self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
      self.playButton.setIcon(QIcon(self.path+'/lock/p.ico'))
      self.playButton.clicked.connect(self.play)

      self.positionSlider = QSlider(Qt.Horizontal)
      self.positionSlider.setRange(0, 0)
      self.positionSlider.sliderMoved.connect(self.setPosition)

      self.error = QLabel()
      self.error.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum)

      openButton = QPushButton("영상받아오기")   
      openButton.setToolTip("Open Video File")
      openButton.setStatusTip("Open Video File")
      openButton.setFixedHeight(24)
      openButton.clicked.connect(self.openFile)

      # self.movie = QMovie("a.gif")
      # self.movie.frameChanged.connect(self.repaint)
      # self.movie.start()
      # self.movie.setScaledSize(QSize(100,100))
      self.msg = QMessageBox()
      self.msg.setWindowTitle("플레이플리즈 영상 안내")

      # Create a widget for window contents
      wid = QWidget(self)
      self.setCentralWidget(wid)

      # Create layouts to place inside widget
      controlLayout = QHBoxLayout()
      controlLayout.setContentsMargins(0, 0, 0, 0)
      controlLayout.addWidget(self.playButton)
      controlLayout.addWidget(self.positionSlider)


      layout = QVBoxLayout()
      layout.addWidget(videoWidget)
      layout.addLayout(controlLayout)
      layout.addWidget(self.error)
      layout.addWidget(openButton)
       # Set widget to contain window contents
      wid.setLayout(layout)

      self.mediaPlayer.setVideoOutput(videoWidget)
      self.mediaPlayer.stateChanged.connect(self.mediaStateChanged)
      self.mediaPlayer.positionChanged.connect(self.positionChanged)
      self.mediaPlayer.durationChanged.connect(self.durationChanged)
      self.mediaPlayer.error.connect(self.handleError)
  # def paintEvent(self, event):
  #     currentFrame = self.movie.currentPixmap()
  #     frameRect = currentFrame.rect()
  #     frameRect.moveCenter(self.rect().center())
  #     if frameRect.intersects(event.rect()):
  #         painter = QPainter(self)
  #         painter.drawPixmap(frameRect.left(), frameRect.top(), currentFrame)


  def exitCall(self):
      sys.exit(app.exec_())

  def openFile(self):
      try:
          wd = sys._MEIPASS
      except AttributeError:
          wd = os.getcwd()
      file_path = os.path.join(wd)
      # self.mediaPlayer.setMedia(
      #         QMediaContent(QUrl("https://playplz.s3.ap-northeast-2.amazonaws.com/gift.mp4")) )
      # current_path = os.path.sep.join(sys.argv[0].split(os.path.sep)[:-1])
      current_path=file_path
      path='file:///'+current_path+'/lock/gift_brand.mp4'
      self.mediaPlayer.setMedia(
              QMediaContent(QUrl(path)))                
      # self.msg.setText(file_path)
      # self.msg.exec_()
      if self.limit>0:
          self.limit-=1
          self.msg.setText("재생가능 횟수 {0}회 남았습니다".format(self.limit))
          self.msg.exec_()
          self.playButton.setEnabled(True)
      else:
          self.msg.setText("재생가능 횟수를 모두 쓰셨습니다. 아래 주소로 이동해 추가 결제 부탁드립니다")
          self.msg.exec_()            
  def play(self):
      if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
          self.mediaPlayer.pause()
      else:
          self.mediaPlayer.play()



  def mediaStateChanged(self, state):
      if self.mediaPlayer.state() == QMediaPlayer.PlayingState:
          self.playButton.setIcon(
                  self.style().standardIcon(QStyle.SP_MediaPause))
      else:
          self.playButton.setIcon(QIcon(self.path+'/lock/p.ico'))

  def positionChanged(self, position):
      self.positionSlider.setValue(position)

  def durationChanged(self, duration):
      self.positionSlider.setRange(0, duration)

  def setPosition(self, position):
      self.mediaPlayer.setPosition(position)

  def handleError(self):
      self.playButton.setEnabled(False)
      self.error.setText("Error: " + self.mediaPlayer.errorString())


  app = QApplication(sys.argv)
  videoplayer = VideoPlayer()
  videoplayer.resize(640, 480)
  videoplayer.show()
  sys.exit(app.exec_())

MAC os는 icns파일 사용
window os는 ico파일 사용
pyinstaller --onefile --noconsole --icon=playplz.icns main.py
으로 main.spec생성후

# -*- mode: python ; coding: utf-8 -*-


block_cipher = None

added_files = [
    ( './lock', './lock' ) 
]

a = Analysis(['main.py'],
             pathex=['/Users/sanghak/Desktop/test1'],
             binaries=[],
             datas=added_files,
             hiddenimports=[],
             hookspath=[],
             hooksconfig={},
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)

exe = EXE(pyz,
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,  
          [],
          name='PLAYPLZ PLAYER',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          upx_exclude=[],
          runtime_tmpdir=None,
          console=False,
          disable_windowed_traceback=False,
          target_arch=None,
          codesign_identity=None,
          entitlements_file=None , icon='playplz.icns')
app = BUNDLE(exe,
             name='PLAYPLZ PLAYER.app',
             icon='playplz.icns',
             bundle_identifier=None)

 
 
 

pyinstaller main.spec 으로 마무리

결과물

profile
기억의 지배장

0개의 댓글