kopia lustrzana https://github.com/corrscope/corrscope
rodzic
b29e5e9252
commit
fd24ab6b0d
|
@ -8,11 +8,13 @@ from PyInstaller.building.datastruct import TOC
|
|||
block_cipher = None
|
||||
|
||||
|
||||
dir = "corrscope/gui"
|
||||
includes = glob.glob("corrscope/gui/*.ui")
|
||||
assert includes
|
||||
def keep(dir, wildcard):
|
||||
includes = glob.glob(f"{dir}/{wildcard}")
|
||||
assert includes
|
||||
return [(include, dir) for include in includes]
|
||||
|
||||
datas = [(include, dir) for include in includes]
|
||||
|
||||
datas = keep("corrscope/gui", "*.ui") + keep("corrscope/path", "*")
|
||||
|
||||
a = Analysis(
|
||||
["corrscope\\__main__.py"],
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import datetime
|
||||
import sys
|
||||
from itertools import count
|
||||
from pathlib import Path
|
||||
from typing import Optional, List, Tuple, Union, Iterator
|
||||
|
@ -8,6 +9,7 @@ import click
|
|||
from corrscope import __version__
|
||||
from corrscope.channel import ChannelConfig
|
||||
from corrscope.config import yaml
|
||||
from corrscope.ffmpeg_path import MissingFFmpegError
|
||||
from corrscope.outputs import IOutputConfig, FFplayOutputConfig, FFmpegOutputConfig
|
||||
from corrscope.corrscope import default_config, CorrScope, Config, Arguments
|
||||
|
||||
|
@ -211,7 +213,11 @@ def main(
|
|||
profile_dump_name = get_profile_dump_name(first_song_name)
|
||||
cProfile.runctx('command()', globals(), locals(), profile_dump_name)
|
||||
else:
|
||||
command()
|
||||
try:
|
||||
command()
|
||||
except MissingFFmpegError as e:
|
||||
# Tell user how to install ffmpeg (__str__).
|
||||
print(e, file=sys.stderr)
|
||||
|
||||
|
||||
def get_profile_dump_name(prefix: str) -> str:
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
import os
|
||||
import platform
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from corrscope.config import CorrError
|
||||
|
||||
# Add app-specific ffmpeg path.
|
||||
|
||||
path_dir = str(Path(__file__).parent / "path")
|
||||
os.environ["PATH"] += os.pathsep + path_dir
|
||||
# Editing sys.path doesn't work.
|
||||
# https://bugs.python.org/issue8557 is relevant but may be outdated.
|
||||
|
||||
|
||||
# Unused
|
||||
# def ffmpeg_exists():
|
||||
# return shutil.which("ffmpeg") is not None
|
||||
|
||||
|
||||
def get_ffmpeg_url() -> str:
|
||||
# is_python_64 = sys.maxsize > 2 ** 32
|
||||
is_os_64 = platform.machine().endswith("64")
|
||||
|
||||
def url(os_ver):
|
||||
return f"https://ffmpeg.zeranoe.com/builds/{os_ver}/shared/ffmpeg-latest-{os_ver}-shared.zip"
|
||||
|
||||
if sys.platform == "win32" and is_os_64:
|
||||
return url("win64")
|
||||
elif sys.platform == "win32" and not is_os_64:
|
||||
return url("win32")
|
||||
elif sys.platform == "darwin" and is_os_64:
|
||||
return url("macos64")
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
class MissingFFmpegError(CorrError):
|
||||
ffmpeg_url = get_ffmpeg_url()
|
||||
can_download = bool(ffmpeg_url)
|
||||
|
||||
message = f'FFmpeg must be in PATH or "{path_dir}" in order to use corrscope.\n'
|
||||
|
||||
if can_download:
|
||||
message += f"Download ffmpeg from {ffmpeg_url}."
|
||||
else:
|
||||
message += "Cannot download FFmpeg for your platform."
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
|
@ -15,8 +15,10 @@ from PyQt5.QtWidgets import QShortcut
|
|||
|
||||
from corrscope import __version__ # variable
|
||||
from corrscope import cli # module wtf?
|
||||
from corrscope import ffmpeg_path
|
||||
from corrscope.channel import ChannelConfig
|
||||
from corrscope.config import CorrError, copy_config, yaml
|
||||
from corrscope.corrscope import CorrScope, Config, Arguments, default_config
|
||||
from corrscope.gui.data_bind import (
|
||||
PresentationModel,
|
||||
map_gui,
|
||||
|
@ -32,7 +34,6 @@ from corrscope.gui.util import (
|
|||
TracebackDialog,
|
||||
)
|
||||
from corrscope.outputs import IOutputConfig, FFplayOutputConfig, FFmpegOutputConfig
|
||||
from corrscope.corrscope import CorrScope, Config, Arguments, default_config
|
||||
from corrscope.triggers import CorrelationTriggerConfig, ITriggerConfig
|
||||
from corrscope.util import obj_name
|
||||
|
||||
|
@ -336,15 +337,19 @@ class MainWindow(qw.QMainWindow):
|
|||
|
||||
cfg = copy_config(self.model.cfg)
|
||||
t = self.corr_thread.obj = CorrThread(cfg, arg)
|
||||
t.error.connect(self.on_play_thread_error)
|
||||
t.finished.connect(self.on_play_thread_finished)
|
||||
t.error.connect(self.on_play_thread_error)
|
||||
t.ffmpeg_missing.connect(self.on_play_thread_ffmpeg_missing)
|
||||
t.start()
|
||||
|
||||
def on_play_thread_finished(self):
|
||||
self.corr_thread.set(None)
|
||||
|
||||
def on_play_thread_error(self, stack_trace: str):
|
||||
TracebackDialog(self).showMessage(stack_trace)
|
||||
|
||||
def on_play_thread_finished(self):
|
||||
self.corr_thread.set(None)
|
||||
def on_play_thread_ffmpeg_missing(self):
|
||||
DownloadFFmpegActivity(self)
|
||||
|
||||
def _get_args(self, outputs: List[IOutputConfig]):
|
||||
arg = Arguments(cfg_dir=self.cfg_dir, outputs=outputs)
|
||||
|
@ -401,14 +406,24 @@ class CorrThread(qc.QThread):
|
|||
arg = self.arg
|
||||
try:
|
||||
CorrScope(cfg, arg).play()
|
||||
except Exception:
|
||||
|
||||
except ffmpeg_path.MissingFFmpegError:
|
||||
arg.on_end()
|
||||
stack_trace = traceback.format_exc()
|
||||
self.ffmpeg_missing.emit()
|
||||
|
||||
except Exception as e:
|
||||
arg.on_end()
|
||||
if isinstance(e, CorrError):
|
||||
stack_trace = traceback.format_exc(limit=0)
|
||||
else:
|
||||
stack_trace = traceback.format_exc()
|
||||
self.error.emit(stack_trace)
|
||||
|
||||
else:
|
||||
arg.on_end()
|
||||
|
||||
error = qc.pyqtSignal(str)
|
||||
ffmpeg_missing = qc.pyqtSignal()
|
||||
|
||||
|
||||
class CorrProgressDialog(qw.QProgressDialog):
|
||||
|
@ -626,7 +641,6 @@ class ChannelModel(qc.QAbstractTableModel):
|
|||
Column("trigger_width", int, None, "Trigger Width ×"),
|
||||
Column("render_width", int, None, "Render Width ×"),
|
||||
Column("line_color", str, None, "Line Color"),
|
||||
# TODO move from table view to sidebar QDataWidgetMapper?
|
||||
Column("trigger__edge_strength", float, None),
|
||||
Column("trigger__responsiveness", float, None),
|
||||
Column("trigger__buffer_falloff", float, None),
|
||||
|
@ -799,3 +813,32 @@ class ChannelModel(qc.QAbstractTableModel):
|
|||
|
||||
|
||||
nope = qc.QVariant()
|
||||
|
||||
|
||||
class DownloadFFmpegActivity:
|
||||
title = "Missing FFmpeg"
|
||||
|
||||
ffmpeg_url = ffmpeg_path.get_ffmpeg_url()
|
||||
can_download = bool(ffmpeg_url)
|
||||
|
||||
path_uri = qc.QUrl.fromLocalFile(ffmpeg_path.path_dir).toString()
|
||||
|
||||
required = (
|
||||
f"FFmpeg must be in PATH or "
|
||||
f'<a href="{path_uri}">corrscope folder</a> in order to use corrscope.<br>'
|
||||
)
|
||||
|
||||
ffmpeg_template = required + (
|
||||
f'Download ffmpeg from <a href="{ffmpeg_url}">{ffmpeg_url}</a>.'
|
||||
)
|
||||
fail_template = required + "Cannot download FFmpeg for your platform."
|
||||
|
||||
def __init__(self, window: qw.QWidget):
|
||||
"""Prompt the user to download and install ffmpeg."""
|
||||
Msg = qw.QMessageBox
|
||||
|
||||
if not self.can_download:
|
||||
Msg.information(window, self.title, self.fail_template, Msg.Ok)
|
||||
return
|
||||
|
||||
Msg.information(window, self.title, self.ffmpeg_template, Msg.Ok)
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
# https://ffmpeg.org/ffplay.html
|
||||
import numpy as np
|
||||
import shlex
|
||||
import subprocess
|
||||
from abc import ABC, abstractmethod
|
||||
from os.path import abspath
|
||||
from typing import TYPE_CHECKING, Type, List, Union, Optional
|
||||
|
||||
import numpy as np
|
||||
|
||||
from corrscope.config import register_config
|
||||
from corrscope.ffmpeg_path import MissingFFmpegError
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from corrscope.corrscope import Config
|
||||
|
@ -95,12 +96,16 @@ class _FFmpegProcess:
|
|||
self.templates.append(cfg.audio_template) # audio
|
||||
|
||||
def popen(self, extra_args, bufsize, **kwargs) -> subprocess.Popen:
|
||||
return subprocess.Popen(
|
||||
self._generate_args() + extra_args,
|
||||
stdin=subprocess.PIPE,
|
||||
bufsize=bufsize,
|
||||
**kwargs,
|
||||
)
|
||||
"""Raises FileNotFoundError if FFmpeg missing"""
|
||||
try:
|
||||
args = self._generate_args() + extra_args
|
||||
return subprocess.Popen(
|
||||
args, stdin=subprocess.PIPE, bufsize=bufsize, **kwargs
|
||||
)
|
||||
except FileNotFoundError as e:
|
||||
raise MissingFFmpegError(
|
||||
# FIXME REMOVE f'Class {obj_name(self)}: program {args[0]} is missing'
|
||||
)
|
||||
|
||||
def _generate_args(self) -> List[str]:
|
||||
return [arg for template in self.templates for arg in shlex.split(template)]
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
*
|
||||
!*.txt
|
||||
!.gitignore
|
Ładowanie…
Reference in New Issue