次は、超簡単Media Playerプログラム。


動画の日本語音声を探して、日本語音声だけで再生しようとするだけの、超簡単Media Playerの作成に、チャレンジしてみた。


作成できるかどうかの問い合わせを含めて、12回のやりとりで、

自分の求める超簡単Media Playerができた・・・はずw


作成途中、ファイルを開く、や、ドラッグアンドドロップ、を繰り返すと、暴走状態になってたんだが、直った・・・はずw


フルスクリーンボタンはあるけど、下のボタンなどは、消せません!

べたべたの日本人のための、Windows用の超簡単Media Player、ですw


興味がある方は、


Pythonをダウンロードしてインストール
VS Codeをダウンロードしてインストール

メモ帳で、
install ライブラリ.bat
などのファイル名で、

pip install python-vlc
pip install tkinterdnd2
pause

の内容のバッチファイルを作成して、実行

VS Codeで
MediaPlayer004.pyw
などのファイル名で、次のコードのプログラムを作成します。

プログラム名に「.pyw」の拡張子をつけておいてください。

import os
import tkinter as tk
from tkinter import filedialog

import vlc
from tkinterdnd2 import DND_FILES, TkinterDnD


class MediaPlayer:

    def __init__(self, root):

        self.root = root

        # ========================================================
        # ウィンドウ
        # ========================================================
        self.root.title("Python Media Player")
        self.root.geometry("1100x750")
        self.root.minsize(800, 500)

        # ========================================================
        # 状態
        # ========================================================
        self.current_file = None

        # 動画切替中
        self.switching = False

        # ドロップ処理中
        self.drop_busy = False

        # フルスクリーン
        self.fullscreen = False

        # シークバー操作中
        self.slider_dragging = False

        # 動画世代番号
        self.media_generation = 0

        # 初期音量
        self.initial_volume = 50

        # ========================================================
        # VLC
        # ========================================================
        self.instance = vlc.Instance(
            "--quiet",
            "--no-video-title-show",
            "--no-sub-autodetect-file"
        )

        # Playerは最初に1個だけ作成
        self.player = None

        # ========================================================
        # GUI
        # ========================================================
        self.create_gui()

        # VLC Player作成
        self.create_player()

        # ========================================================
        # キー
        # ========================================================
        self.root.bind(
            "<Escape>",
            self.exit_fullscreen
        )

        self.root.bind(
            "<space>",
            self.toggle_play_pause
        )

        # ========================================================
        # 終了
        # ========================================================
        self.root.protocol(
            "WM_DELETE_WINDOW",
            self.close_app
        )

        # ========================================================
        # 定期更新
        # ========================================================
        self.root.after(
            200,
            self.update_player
        )

    # ============================================================
    # GUI
    # ============================================================
    def create_gui(self):

        # --------------------------------------------------------
        # 動画表示部分
        # --------------------------------------------------------
        self.video_frame = tk.Frame(
            self.root,
            bg="black"
        )

        self.video_frame.pack(
            fill="both",
            expand=True
        )

        # --------------------------------------------------------
        # ドラッグ&ドロップ
        # --------------------------------------------------------
        self.video_frame.drop_target_register(
            DND_FILES
        )

        self.video_frame.dnd_bind(
            "<<Drop>>",
            self.drop_file
        )

        # --------------------------------------------------------
        # 操作エリア
        # --------------------------------------------------------
        control_frame = tk.Frame(
            self.root
        )

        control_frame.pack(
            fill="x",
            padx=5,
            pady=5
        )

        # --------------------------------------------------------
        # ファイルを開く
        # --------------------------------------------------------
        self.open_button = tk.Button(
            control_frame,
            text="ファイルを開く",
            width=12,
            command=self.open_file
        )

        self.open_button.pack(
            side="left",
            padx=3
        )

        # --------------------------------------------------------
        # 再生 / 一時停止
        # --------------------------------------------------------
        self.play_button = tk.Button(
            control_frame,
            text="再生 / 一時停止",
            width=15,
            command=self.toggle_play_pause
        )

        self.play_button.pack(
            side="left",
            padx=3
        )

        # --------------------------------------------------------
        # フルスクリーン
        # --------------------------------------------------------
        self.fullscreen_button = tk.Button(
            control_frame,
            text="フルスクリーン",
            width=12,
            command=self.toggle_fullscreen
        )

        self.fullscreen_button.pack(
            side="left",
            padx=3
        )

        # --------------------------------------------------------
        # 音量
        # --------------------------------------------------------
        tk.Label(
            control_frame,
            text="音量"
        ).pack(
            side="left",
            padx=(15, 3)
        )

        self.volume = tk.Scale(
            control_frame,
            from_=0,
            to=100,
            orient="horizontal",
            length=130,
            showvalue=True,
            resolution=1,
            command=self.change_volume
        )

        self.volume.set(
            self.initial_volume
        )

        self.volume.pack(
            side="left"
        )

        # --------------------------------------------------------
        # シークバー
        # --------------------------------------------------------
        self.position = tk.Scale(
            self.root,
            from_=0,
            to=1000,
            orient="horizontal",
            showvalue=False,
            resolution=1
        )

        self.position.pack(
            fill="x",
            padx=5,
            pady=(0, 5)
        )

        self.position.bind(
            "<ButtonPress-1>",
            self.slider_press
        )

        self.position.bind(
            "<ButtonRelease-1>",
            self.slider_release
        )

        # --------------------------------------------------------
        # ファイル名
        # --------------------------------------------------------
        self.file_label = tk.Label(
            self.root,
            text="ファイルが選択されていません",
            anchor="w"
        )

        self.file_label.pack(
            fill="x",
            padx=8,
            pady=(0, 5)
        )

    # ============================================================
    # VLC Player作成
    # ============================================================
    def create_player(self):

        try:

            self.player = (
                self.instance.media_player_new()
            )

            # Windowsの動画表示先
            self.player.set_hwnd(
                self.video_frame.winfo_id()
            )

            # 音量50%
            self.player.audio_set_volume(
                self.initial_volume
            )

        except Exception as e:

            print(
                "VLC Player作成エラー:",
                e
            )

            self.player = None

    # ============================================================
    # 対応ファイル確認
    # ============================================================
    def is_supported_file(self, filename):

        extensions = (
            ".mp4",
            ".mkv",
            ".avi",
            ".mov",
            ".wmv",
            ".webm",
            ".m4v",
            ".ts"
        )

        return filename.lower().endswith(
            extensions
        )

    # ============================================================
    # 「ファイルを開く」
    # ============================================================
    def open_file(self):

        if self.switching:
            return

        filename = filedialog.askopenfilename(
            title="動画ファイルを選択",
            filetypes=[
                (
                    "動画ファイル",
                    "*.mp4 *.mkv *.avi *.mov *.wmv *.webm *.m4v *.ts"
                ),
                (
                    "すべてのファイル",
                    "*.*"
                )
            ]
        )

        if not filename:
            return

        # ドラッグ&ドロップと同じ処理へ
        self.request_file_change(
            filename
        )

    # ============================================================
    # ドラッグ&ドロップ
    # ============================================================
    def drop_file(self, event):

        # --------------------------------------------------------
        # 二重受付防止
        # --------------------------------------------------------
        if self.drop_busy:
            return

        if self.switching:
            return

        self.drop_busy = True

        try:

            files = self.root.tk.splitlist(
                event.data
            )

            if not files:
                self.drop_busy = False
                return

            # 最初の1ファイルだけ使用
            filename = files[0]

            print()
            print(
                "ドラッグ&ドロップ受付:"
            )
            print(filename)

            # ----------------------------------------------------
            # ここではVLCを操作しない
            # ----------------------------------------------------
            self.root.after(
                50,
                lambda f=filename:
                    self.request_drop_file(f)
            )

        except Exception as e:

            print(
                "ドロップ処理エラー:",
                e
            )

            self.drop_busy = False

    # ============================================================
    # ドロップされたファイル
    # ============================================================
    def request_drop_file(self, filename):

        try:

            self.request_file_change(
                filename
            )

        finally:

            self.drop_busy = False

    # ============================================================
    # ファイル切替共通処理
    #
    # 「ファイルを開く」
    # 「ドラッグ&ドロップ」
    #
    # 両方ともここへ来る
    # ============================================================
    def request_file_change(self, filename):

        # --------------------------------------------------------
        # 切替中なら無視
        # --------------------------------------------------------
        if self.switching:

            print(
                "動画切替中のため要求を無視します。"
            )

            return

        # --------------------------------------------------------
        # ファイル確認
        # --------------------------------------------------------
        if not os.path.isfile(filename):

            print(
                "ファイルが存在しません:"
            )
            print(filename)

            return

        # --------------------------------------------------------
        # 拡張子確認
        # --------------------------------------------------------
        if not self.is_supported_file(filename):

            print(
                "対応していないファイル形式です:"
            )
            print(filename)

            return

        # --------------------------------------------------------
        # 切替ロック
        # --------------------------------------------------------
        self.switching = True

        # --------------------------------------------------------
        # 世代番号更新
        # --------------------------------------------------------
        self.media_generation += 1

        generation = (
            self.media_generation
        )

        print()
        print("==============================")
        print("動画切替開始")
        print(filename)
        print("==============================")

        # --------------------------------------------------------
        # UI
        # --------------------------------------------------------
        self.open_button.config(
            state="disabled"
        )

        self.file_label.config(
            text="切替中: "
            + os.path.basename(filename)
        )

        self.position.set(0)

        # --------------------------------------------------------
        # 少し待ってからMedia切替
        #
        # ドラッグ&ドロップ時のイベント処理を
        # 完全に抜けてからVLCを操作する
        # --------------------------------------------------------
        self.root.after(
            50,
            lambda:
                self.load_media(
                    filename,
                    generation
                )
        )

    # ============================================================
    # Media切替
    #
    # Playerは破棄しない
    # ============================================================
    def load_media(
        self,
        filename,
        generation
    ):

        # 古い要求なら無視
        if generation != self.media_generation:

            return

        if self.player is None:

            print(
                "VLC Playerがありません。"
            )

            self.create_player()

        if self.player is None:

            print(
                "VLC Playerを作成できませんでした。"
            )

            self.finish_switch()

            return

        print(
            "新しいMediaを作成しています..."
        )

        try:

            # ----------------------------------------------------
            # 新しいMedia
            # ----------------------------------------------------
            media = self.instance.media_new(
                filename
            )

            # ----------------------------------------------------
            # 字幕OFF
            # ----------------------------------------------------
            media.add_option(
                ":sub-track=-1"
            )

            media.add_option(
                ":no-sub-autodetect-file"
            )

            # ----------------------------------------------------
            # 日本語音声優先
            # ----------------------------------------------------
            media.add_option(
                ":audio-language=ja,jpn,japanese"
            )

            # ----------------------------------------------------
            # Mediaをセット
            #
            # Player自体は作り直さない
            # ----------------------------------------------------
            self.player.set_media(
                media
            )

            # ----------------------------------------------------
            # 音量50%
            # ----------------------------------------------------
            self.player.audio_set_volume(
                self.initial_volume
            )

            # ----------------------------------------------------
            # ファイル名
            # ----------------------------------------------------
            self.current_file = filename

            self.file_label.config(
                text=os.path.basename(filename)
            )

            print(
                "新しいMediaをセットしました。"
            )

            # ----------------------------------------------------
            # 再生
            # ----------------------------------------------------
            result = self.player.play()

            if result == -1:

                print(
                    "VLC再生開始に失敗しました。"
                )

            else:

                print(
                    "再生開始要求完了"
                )

            # ----------------------------------------------------
            # 字幕OFF
            # ----------------------------------------------------
            try:

                self.player.video_set_spu(-1)

            except Exception:
                pass

            # ----------------------------------------------------
            # 日本語音声確認
            # ----------------------------------------------------
            self.root.after(
                1200,
                lambda:
                    self.configure_audio(
                        generation
                    )
            )

        except Exception as e:

            print()
            print(
                "Media切替エラー:"
            )
            print(e)

            self.file_label.config(
                text="再生できませんでした: "
                + os.path.basename(filename)
            )

        finally:

            self.finish_switch()

    # ============================================================
    # 日本語音声設定
    # ============================================================
    def configure_audio(
        self,
        generation
    ):

        # 古い動画なら無視
        if generation != self.media_generation:
            return

        if self.player is None:
            return

        # --------------------------------------------------------
        # 字幕OFF
        # --------------------------------------------------------
        try:

            self.player.video_set_spu(-1)

        except Exception:
            pass

        # --------------------------------------------------------
        # 音声トラック
        # --------------------------------------------------------
        try:

            tracks = (
                self.player.audio_get_track_description()
            )

            if not tracks:
                return

            print()
            print(
                "音声トラック:"
            )

            japanese_track = None

            for track_id, name in tracks:

                if name is None:
                    name = ""

                name_text = str(name)

                print(
                    "  ID:",
                    track_id,
                    "/",
                    name_text
                )

                lower_name = (
                    name_text.lower()
                )

                if (
                    "japanese" in lower_name
                    or "japan" in lower_name
                    or "日本語" in name_text
                    or "日本" in name_text
                    or "jpn" in lower_name
                ):

                    japanese_track = track_id

            # ----------------------------------------------------
            # 日本語が見つかった場合のみ変更
            # ----------------------------------------------------
            if japanese_track is not None:

                result = (
                    self.player.audio_set_track(
                        japanese_track
                    )
                )

                print(
                    "日本語音声を選択:",
                    japanese_track,
                    "result=",
                    result
                )

            else:

                # 無理に変更しない
                print(
                    "日本語音声を判別できませんでした。"
                )

                print(
                    "現在の音声トラックを使用します。"
                )

        except Exception as e:

            print(
                "音声トラック設定エラー:",
                e
            )

    # ============================================================
    # 切替終了
    # ============================================================
    def finish_switch(self):

        self.switching = False

        try:

            self.open_button.config(
                state="normal"
            )

        except Exception:
            pass

        print(
            "=============================="
        )

        print(
            "動画切替終了"
        )

        print(
            "=============================="
        )

        print()

    # ============================================================
    # 音量変更
    # ============================================================
    def change_volume(
        self,
        value
    ):

        if self.player is None:
            return

        try:

            volume = int(
                float(value)
            )

            self.player.audio_set_volume(
                volume
            )

        except Exception:
            pass

    # ============================================================
    # シークバー押下
    # ============================================================
    def slider_press(
        self,
        event
    ):

        self.slider_dragging = True

    # ============================================================
    # シークバー離す
    # ============================================================
    def slider_release(
        self,
        event
    ):

        self.slider_dragging = False

        if self.player is None:
            return

        if self.switching:
            return

        try:

            value = self.position.get()

            position = (
                value / 1000.0
            )

            if 0.0 <= position <= 1.0:

                self.player.set_position(
                    position
                )

        except Exception as e:

            print(
                "シークエラー:",
                e
            )

    # ============================================================
    # 再生 / 一時停止
    # ============================================================
    def toggle_play_pause(
        self,
        event=None
    ):

        if self.switching:
            return

        if self.player is None:
            return

        try:

            if self.player.is_playing():

                self.player.pause()

            else:

                self.player.play()

        except Exception as e:

            print(
                "再生/一時停止エラー:",
                e
            )

    # ============================================================
    # フルスクリーン
    # ============================================================
    def toggle_fullscreen(self):

        self.fullscreen = (
            not self.fullscreen
        )

        self.root.attributes(
            "-fullscreen",
            self.fullscreen
        )

    # ============================================================
    # Esc
    # ============================================================
    def exit_fullscreen(
        self,
        event=None
    ):

        if self.fullscreen:

            self.fullscreen = False

            self.root.attributes(
                "-fullscreen",
                False
            )

    # ============================================================
    # VLC状態更新
    # ============================================================
    def update_player(self):

        try:

            if (
                self.player is not None
                and not self.slider_dragging
                and not self.switching
            ):

                position = (
                    self.player.get_position()
                )

                if position >= 0:

                    value = int(
                        position * 1000
                    )

                    if value > 1000:
                        value = 1000

                    self.position.set(
                        value
                    )

        except Exception:
            pass

        try:

            self.root.after(
                200,
                self.update_player
            )

        except Exception:
            pass

    # ============================================================
    # 終了
    # ============================================================
    def close_app(self):

        print()
        print(
            "アプリを終了しています..."
        )

        self.switching = True

        player = self.player
        self.player = None

        if player is not None:

            try:

                player.stop()

            except Exception:
                pass

            try:

                player.release()

            except Exception:
                pass

        try:

            self.instance.release()

        except Exception:
            pass

        try:

            self.root.destroy()

        except Exception:
            pass


# =================================================================
# メイン
# =================================================================
if __name__ == "__main__":

    root = TkinterDnD.Tk()

    app = MediaPlayer(
        root
    )

    root.mainloop()


VS Codeで動作確認が出来たら、起動できるようにします。

メモ帳で、
MediaPlayerソフトWin.bat
などのファイル名で、

@echo off
cd /d "%~dp0"
start pythonw.exe "MediaPlayer004.pyw"

というような、起動用のバッチファイルを作ります。
作成した、プログラムファイル名に合わせて、内容を変更してください。

このバッチファイルは、プログラムと同じフォルダにおいてください。
次にバッチファイルを選択して右クリックし、送る、から、デスクトップにショートカットを作成してください。

これで、デスクトップのショートカットでプログラムを起動できるようになったはずです。

うっとおしいバッチファイルの起動画面が、デスクトップに残りませんw


私のパソコンは、プログラム開発機として利用しているので、いろいろ入っていて、足りないものがあってもわかりませんw

他のパソコンでは、気が向いた時しか動作確認しませんし、いつになるかわかりませんw


このプログラムはAI(ChatGPT)を利用して作成したものです。 

どなたでも自由にコピー・改変・再配布していただいて構いません。

 【免責事項】

 本プログラムの利用によって生じた、いかなる損害やトラブルについても開発者は一切の責任を負いません。ご自身の責任においてご利用ください。


てゆうか、こんな機能の無いMediaPlayerプログラムを使おうとする人っているのかな???

コメント

このブログの人気の投稿