diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..42b415e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/venv/ +/*.lnk \ No newline at end of file diff --git a/ic/128.png b/ic/128.png new file mode 100644 index 0000000..bfa41dc Binary files /dev/null and b/ic/128.png differ diff --git a/ic/32.png b/ic/32.png new file mode 100644 index 0000000..ea23b04 Binary files /dev/null and b/ic/32.png differ diff --git a/ic/48.png b/ic/48.png new file mode 100644 index 0000000..1843ee5 Binary files /dev/null and b/ic/48.png differ diff --git a/ic/64.png b/ic/64.png new file mode 100644 index 0000000..ec8041f Binary files /dev/null and b/ic/64.png differ diff --git a/jp_win.py b/jp_win.py new file mode 100644 index 0000000..aea1b74 --- /dev/null +++ b/jp_win.py @@ -0,0 +1,384 @@ +import tkinter as tk +import customtkinter +import os +from PIL import Image +from winotify import Notification, audio +from yt_dlp import YoutubeDL +import pyperclip +import threading +from tkinter import messagebox +import sys + +version = "0.1" +builddate = "2023/9/14" + +lib_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "lib") +cpurl = pyperclip.paste() +thm_conf = os.path.isfile("./lib/thm.png") + + +class App(customtkinter.CTk): + def __init__(self): + super().__init__() + + self.title("which_online_video") + self.iconbitmap(r".\lib\logo.ico") + self.geometry("640x360") + self.resizable(height=False, width=False) + + self.grid_rowconfigure(0, weight=1) + self.grid_columnconfigure(1, weight=1) + + self.logo_icon = customtkinter.CTkImage(Image.open( + os.path.join(lib_path, "logo32.png")), size=(32, 32)) + self.info_icon = customtkinter.CTkImage(Image.open( + os.path.join(lib_path, "info32.png")), size=(32, 32)) + + if thm_conf: + pass + else: + thm_dummy = Image.new("L", (640, 360)) + thm_dummy.save("./lib/thm.png") + + self.thm_preview = customtkinter.CTkImage(Image.open( + os.path.join(lib_path, "thm.png")), size=(160, 90)) + + self.title_font = customtkinter.CTkFont( + family="meiryo", size=16, weight="bold") + self.info_font = customtkinter.CTkFont(family="meiryo", size=14) + self.ui_font = customtkinter.CTkFont(family="meiryo", size=12) + self.button_font = customtkinter.CTkFont( + family="meiryo", size=14, weight="bold") + + self.frame = customtkinter.CTkFrame(self, corner_radius=0) + self.frame.grid(row=0, column=0, sticky="nsew") + self.frame.grid_rowconfigure(4, weight=1) + + self.dl_frame_button = customtkinter.CTkButton(self.frame, width=32, height=36, border_spacing=4, image=self.logo_icon, + text="", fg_color="transparent", hover_color=("gray70", "gray30"), command=self.frame_select_dl) + self.dl_frame_button.grid(row=0, column=0, sticky="ew") + + self.info_button = customtkinter.CTkButton(self.frame, width=32, height=36, border_spacing=4, image=self.info_icon, + text="", fg_color="transparent", hover_color=("gray70", "gray30"), command=self.app_info) + self.info_button.grid(row=1, column=0, sticky="ew") + + # dl_frame + self.dl_frame = customtkinter.CTkFrame(self, fg_color="transparent") + self.dl_frame.grid(row=0, column=1) + self.dl_frame.grid_rowconfigure(0, weight=1) + self.dl_frame.grid_columnconfigure(0, weight=1) + + self.dl_info_frame = customtkinter.CTkFrame( + self.dl_frame, fg_color="transparent") + self.dl_info_frame.grid(row=0, column=0, padx=2, pady=2) + self.dl_info_frame.grid_columnconfigure(0, weight=2) + self.dl_info_frame.grid_columnconfigure(1, weight=1) + + self.dl_info_label_frame = customtkinter.CTkFrame( + self.dl_info_frame, fg_color="transparent") + self.dl_info_label_frame.grid(row=0, column=0, padx=8, pady=0) + + self.dl_video_title = customtkinter.CTkLabel( + self.dl_info_label_frame, text="URLを入力してください...", width=360, font=self.title_font, wraplength=360, anchor=tk.W) + self.dl_video_title.grid(row=0, column=0, padx=2, pady=4) + + self.dl_creator_title = customtkinter.CTkLabel( + self.dl_info_label_frame, text="クリップボードの内容が自動的にコピーされています。", width=360, font=self.info_font, wraplength=360, anchor=tk.W) + self.dl_creator_title.grid(row=1, column=0, padx=2, pady=4) + + self.dl_video_thm = customtkinter.CTkLabel( + self.dl_info_frame, image=self.thm_preview, text="", anchor=tk.NE) + self.dl_video_thm.grid(row=0, column=1, padx=8, pady=0) + + self.dl_input_frame = customtkinter.CTkFrame( + self.dl_frame, fg_color="transparent") + self.dl_input_frame.grid(row=1, column=0, padx=16, pady=2) + self.dl_input_frame.grid_rowconfigure(0, weight=1) + self.dl_input_frame.grid_columnconfigure(0, weight=1) + + self.dl_url_textbox = customtkinter.CTkEntry( + self.dl_input_frame, placeholder_text="URLを入力", width=428, font=self.ui_font) + self.dl_url_textbox.grid(row=0, column=0, padx=8, pady=8) + self.dl_url_textbox.insert(0, cpurl) + + self.dl_path_textbox = customtkinter.CTkEntry( + self.dl_input_frame, placeholder_text="保存先を入力...", width=428, font=self.ui_font) + self.dl_path_textbox.grid(row=1, column=0, padx=8, pady=8) + + self.dl_path_paste_button = customtkinter.CTkButton( + self.dl_input_frame, text="URLを貼り付け", command=self.dl_path_paste, font=self.button_font) + self.dl_path_paste_button.grid(row=0, column=1, padx=8, pady=8) + + self.dl_path_button = customtkinter.CTkButton( + self.dl_input_frame, text="保存先を選択", command=self.dl_path_select, font=self.button_font) + self.dl_path_button.grid(row=1, column=1, padx=8, pady=8) + + self.pathtxt_open = open('./lib/path.txt', 'r', encoding='UTF-8') + self.dl_path_textbox.insert(0, self.pathtxt_open.read().rstrip('\n')) + self.pathtxt_open.close() + + self.dl_control_frame = customtkinter.CTkFrame( + self.dl_frame, fg_color="transparent") + self.dl_control_frame.grid(row=2, column=0, padx=16, pady=6) + self.dl_control_frame.grid_rowconfigure(0, weight=1) + self.dl_control_frame.grid_columnconfigure(0, weight=1) + + self.dl_cookie_switch = customtkinter.CTkSwitch( + self.dl_control_frame, text="Cookieの取得", font=self.ui_font) + self.dl_cookie_switch.grid(row=0, column=0, padx=4, pady=6) + + self.dl_quality_text = customtkinter.CTkLabel( + self.dl_control_frame, text="Cookie取得元", font=self.ui_font) + self.dl_quality_text.grid(row=0, column=1, padx=2, pady=6) + + self.dl_cookie_select = customtkinter.CTkComboBox(self.dl_control_frame, width=140, values=[ + "firefox", "chrome", "edge", "vivaldi", "opera", "chromium", "safari"], font=self.ui_font, dropdown_font=self.ui_font) + self.dl_cookie_select.grid(row=0, column=2, padx=4, pady=6) + + self.dl_audio_switch = customtkinter.CTkSwitch( + self.dl_control_frame, text="音声を出力", width=120, font=self.ui_font) + self.dl_audio_switch.grid(row=1, column=1, padx=4, pady=6) + + self.dl_video_switch = customtkinter.CTkSwitch( + self.dl_control_frame, text="動画を出力", width=120, font=self.ui_font) + self.dl_video_switch.grid(row=1, column=0, padx=4, pady=6) + self.dl_video_switch.select() + + self.dl_special_dir_switch = customtkinter.CTkSwitch( + self.dl_control_frame, text="SpeDir", width=120, font=self.ui_font) + self.dl_special_dir_switch.grid(row=2, column=0, padx=4, pady=6) + + self.dl_acodec_select = customtkinter.CTkComboBox(self.dl_control_frame, width=140, values=[ + "bestaudio", "opus", "vorbis", "aac", "mp3"], font=self.ui_font, dropdown_font=self.ui_font) + self.dl_acodec_select.grid(row=1, column=2, padx=4, pady=6) + + self.dl_start_button = customtkinter.CTkButton( + self.dl_control_frame, text="ダウンロード", command=self.dl_start_thread, font=self.button_font) + self.dl_start_button.grid(row=2, column=3, padx=4, pady=6) + + self.select_frame_name("dl_frame") + + def select_frame_name(self, name): + self.dl_frame_button.configure( + fg_color=("gray75", "gray25") if name == "dl_frame" else "transparent") + + if name == "dl_frame": + self.dl_frame.grid(row=0, column=1, sticky="nsew") + else: + self.dl_frame.grid_forget() + + def frame_select_dl(self): + self.select_frame_name("dl_frame") + + def dl_path_select(self): + dl_path = tk.filedialog.askdirectory( + title="保存場所を選択...", initialdir="%HOMEPATH%/Videos") + self.dl_path_textbox.delete(0, tk.END) + self.dl_path_textbox.insert(0, dl_path) + pathtxt = open('./lib/path.txt', 'w', encoding='UTF-8') + pathtxt.write(dl_path) + pathtxt.close() + + def dl_path_paste(self): + cpurl = pyperclip.paste() + self.dl_url_textbox.delete(0, tk.END) + self.dl_url_textbox.insert(0, cpurl) + + # ダウンロード + + def dl_start_thread(self): + dl_thread = threading.Thread(target=self.dl_start) + dl_thread.start() + + def dl_start(self): + if self.dl_video_switch.get() == 0 and self.dl_audio_switch.get() == 0: + messagebox.showerror("ダウンロードエラー", "動画、音声のどちらをダウンロードするかを選択してください。") + sys.exit() + + if self.dl_url_textbox.get() == "": + messagebox.showerror( + "ダウンロードエラー", "URLが入力されていません。\n有効なURLを入力してから、もう一度お試しください。") + sys.exit() + + if self.dl_cookie_switch.get() == 1: + cookie_warning = Notification( + app_id="which_online_video", + title="Cookieの取得は実装されていません", + msg="Cookieの取得はされず、そのまま動画をダウンロードします。", + icon=lib_path + r"\info64.png" + ) + cookie_warning.set_audio(audio.Default, loop=False) + cookie_warning.show() + + self.dl_start_button.configure( + text="ダウンロード中...", state="disabled", fg_color="gray") + + if self.dl_video_switch.get() == 1: + dl_option = { + "outtmpl": self.dl_path_textbox.get() + r"\%(title)s.%(ext)s", + "format": "bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4", + "vcodec": "h264", + "updatetime": "now" + } + + if self.dl_special_dir_switch.get() == 1: + dl_option["outtmpl"] = self.dl_path_textbox.get( + ) + r'\video\source\%(title)s.%(ext)s' + + if thm_conf: + os.remove("./lib/thm.png") + + dlp = YoutubeDL(dl_option) + video_info = dlp.extract_info( + self.dl_url_textbox.get(), download=False) + dlp_thm = YoutubeDL({"outtmpl": "./lib/thm.png"}) + video_thm = video_info.get("thumbnail", "") + video_title = video_info.get("title", "タイトルなし") + video_uploader = video_info.get("uploader", "タイトルなし") + dlp_thm.download([video_thm]) + self.thm_preview = customtkinter.CTkImage(Image.open( + os.path.join(lib_path, "thm.png")), size=(160, 90)) + self.dl_video_thm.configure(image=self.thm_preview) + self.dl_video_title.configure(text=video_title) + self.dl_creator_title.configure(text=video_uploader) + + dling_winotify_notificaiton = Notification( + app_id="which_online_video", + title="ダウンロード中...", + msg=f"{video_title}", + icon=lib_path + r"\thm.png" + ) + dling_winotify_notificaiton.set_audio(audio.Default, loop=False) + dling_winotify_notificaiton.show() + + dlp.download([self.dl_url_textbox.get()]) + + if self.dl_audio_switch.get() == 0: + dl_winotify_notificaiton = Notification( + app_id="which_online_video", + title="動画のダウンロードが完了しました。", + msg=f"{video_title}", + icon=lib_path + r"\thm.png" + ) + + dl_winotify_notificaiton.add_actions( + label="ファイルを開く", + launch=self.dl_path_textbox.get() + fr"\{video_title}.mp4" + ) + + dl_winotify_notificaiton.add_actions( + label="保存先を開く", + launch=self.dl_path_textbox.get() + ) + + dl_winotify_notificaiton.set_audio(audio.Reminder, loop=False) + dl_winotify_notificaiton.show() + dl_option.clear() + self.dl_start_button.configure( + text="ダウンロード", state="normal", fg_color="#3b8ed0") + + else: + dl_option.clear() + + if self.dl_audio_switch.get() == 1: + self.dl_audio_start() + + def dl_audio_start(self): + + dl_audio_option = { + 'outtmpl': self.dl_path_textbox.get() + r'\%(title)s.%(ext)s', + 'format': 'bestaudio[ext=m4a]' + } + + if self.dl_acodec_select.get() == "opus" or self.dl_acodec_select.get() == "vorbis": + dl_audio_option['format'] = 'ogg' + + elif self.dl_acodec_select.get() == "aac": + dl_audio_option['format'] = 'm4a' + + elif self.dl_acodec_select.get() == "mp3": + dl_audio_option['format'] = 'bestaudio[ext=mp3]' + + if self.dl_special_dir_switch.get() == 1: + dl_audio_option["outtmpl"] = self.dl_path_textbox.get( + ) + r'\audio\source\%(title)s.%(ext)s' + + dlp_audio = YoutubeDL(dl_audio_option) + video_info = dlp_audio.extract_info( + self.dl_url_textbox.get(), download=False) + video_title = video_info.get("title", "タイトルなし") + dlp_audio.download([self.dl_url_textbox.get()]) + + self.dl_start_button.configure( + text="ダウンロード", state="normal", fg_color="#3b8ed0") + + if self.dl_video_switch.get() == 1 and self.dl_audio_switch.get() == 1: + dl_winotify_notificaiton = Notification( + app_id="which_online_video", + title="動画と音声のダウンロードが完了しました。", + msg=f"{video_title}", + icon=lib_path + r"\thm.png" + ) + else: + dl_winotify_notificaiton = Notification( + app_id="which_online_video", + title="音声のダウンロードが完了しました。", + msg=f"{video_title}", + icon=lib_path + r"\thm.png" + ) + + if self.dl_special_dir_switch.get() == 1: + if self.dl_video_switch.get() == 1: + dl_winotify_notificaiton.add_actions( + label="動画ファイルを開く", + launch=self.dl_path_textbox.get( + ) + fr"\video\source\{video_title}.mp4" + ) + + dl_winotify_notificaiton.add_actions( + label="音声ファイルを開く", + launch=self.dl_path_textbox.get( + ) + fr"\audio\source\{video_title}.m4a" + ) + + dl_winotify_notificaiton.add_actions( + label="保存先を開く", + launch=self.dl_path_textbox.get() + ) + else: + if self.dl_video_switch.get() == 1: + dl_winotify_notificaiton.add_actions( + label="動画ファイルを開く", + launch=self.dl_path_textbox.get() + fr"\{video_title}.mp4" + ) + + dl_winotify_notificaiton.add_actions( + label="音声ファイルを開く", + launch="'" + self.dl_path_textbox.get() + + fr"\{video_title}.m4a'" + ) + + dl_winotify_notificaiton.add_actions( + label="保存先を開く", + launch=self.dl_path_textbox.get() + ) + + dl_winotify_notificaiton.set_audio(audio.Reminder, loop=False) + dl_winotify_notificaiton.show() + + dl_audio_option.clear() + + def app_info(self): + print(version) + info_notification = Notification( + app_id="which_online_video", + title="バージョン情報", + msg=f"which_online_video バージョン {version} \nビルド日時 {builddate}", + icon=lib_path + r"\logo.png" + ) + info_notification.show() + + +if __name__ == "__main__": + app = App() + app.mainloop() diff --git a/jp_win_new.py b/jp_win_new.py new file mode 100644 index 0000000..6415119 --- /dev/null +++ b/jp_win_new.py @@ -0,0 +1,477 @@ +import tkinter as tk +from tkinter import messagebox +import customtkinter +import os +from PIL import Image +from winotify import Notification, audio +from yt_dlp import YoutubeDL +import ffmpeg +import pyperclip +import threading +import sys +import json +import re + +version = "0.2" +builddate = "2024/2/18" +appname = "which_online_video" + +lib_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "lib") +thm_conf = os.path.isfile(lib_path + "\\thm.png") + +cpurl = pyperclip.paste() + +class Download(): + def info(self, url): + try: + ytdl = YoutubeDL() + info = ytdl.extract_info(url, download=False) + self.title = info.get("title", "タイトルなし") + thm_url = info.get("thumbnail", "") + if thm_conf: + os.remove(lib_path + "\\thm.png") + thm = YoutubeDL({"outtmpl": lib_path + "\\thm.png"}) + thm.download([thm_url]) + self.url = url + self.thm = lib_path + "\\thm.png" + self.creator = info.get("uploader", "タイトルなし") + self.title_re = re.sub(r'[\\/:*?"<>|]+', "", self.title) + return {"video_title": self.title, "video_title_re": self.title_re, "video_creator": self.creator, "thm_path": self.thm} + except: + messagebox.showerror("ダウンロードエラー", "入力されたURLにはアクセスできませんでした。\nURLが有効なものであるか確認の上、再度お試しください。") + return "url_error" + + + def video(self, url, dir_path, video_title, video_title_re, quality): + dl_option = { + "outtmpl": dir_path + "\\" + video_title_re + ".mp4", + "format": "bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4", + } + dlp = YoutubeDL(dl_option) + print("動画をダウンロードしています") + Notify().downloading(video_title, "video") + try: + dlp.download([url]) + print("成功") + return {"file_name": video_title_re + ".mp4", "file_path": dir_path + "\\" + video_title_re + ".mp4", "dir_path": dir_path, "video_title": video_title, "video_title_re": video_title_re} + except: + print("失敗") + return "download_error" + + def audio(self, url, dir_path, video_title, video_title_re): + dl_option = { + 'outtmpl': dir_path + "\\" + video_title_re + ".m4a", + 'format': 'bestaudio[ext=m4a]' + } + dlp = YoutubeDL(dl_option) + print("音声をダウンロードしています") + Notify().downloading(video_title, "audio") + try: + dlp.download([url]) + print("成功") + return {"file_name": url + ".m4a", "file_path": dir_path + "\\" + video_title_re + ".m4a", "dir_path": dir_path, "video_title": video_title, "video_title_re": video_title_re} + except: + print("失敗") + return "download_error" + +class Convert(): + def h264(self, file_path, dir_path, video_title, encoder, bitrate_add): + dl_video_info = ffmpeg.probe(file_path) + bitrate = float(dl_video_info["streams"][0]["bit_rate"]) + vcodec = dl_video_info["streams"][0]["codec_name"] + if vcodec == "vp9": + dl_video_converted_path = dir_path + "\\" + video_title + "_h264.mp4" + try: + stream = ffmpeg.input(file_path) + stream_convert = ffmpeg.output(stream, dl_video_converted_path, vcodec=encoder, video_bitrate=int(bitrate + bitrate * (bitrate_add - 1)), audio_bitrate="320k") + stream_convert.run() + return {"file_path": dl_video_converted_path} + except: + return "convert_error" + else: + return {"file_path": file_path} + + + +class Notify(): + def downloading(self, video_title, mode): + if mode == "video": + self.dl_notify = Notification( + app_id=appname, + title="動画をダウンロード中...", + msg=video_title, + icon=lib_path + r"\thm.png" + ) + elif mode == "audio": + self.dl_notify = Notification( + app_id=appname, + title="音声をダウンロード中...", + msg=video_title, + icon=lib_path + r"\thm.png" + ) + + self.dl_notify.set_audio(audio.Default, loop=False) + self.dl_notify.show() + + def download_success(self, video_title, file_path, dir_path): + self.dl_success_notify = Notification( + app_id=appname, + title="ダウンロードが完了しました", + msg=video_title, + icon=lib_path + r"\thm.png" + ) + self.dl_success_notify.add_actions( + label="ファイルを開く", + launch=file_path + ) + self.dl_success_notify.add_actions( + label="保存先を開く", + launch=dir_path + ) + + self.dl_success_notify.set_audio(audio.Reminder, loop=False) + self.dl_success_notify.show() + + def info(self): + self.info_notification = Notification( + app_id=appname, + title="バージョン情報", + msg=f"{appname} バージョン {version}", + icon=lib_path + r"\logo.png" + ) + self.info_notification.show() + + + + +class App(customtkinter.CTk): + def __init__(self): + super().__init__() + + self.config_file = lib_path + "\\config.json" + if not os.path.isfile(self.config_file): + dummylist = { + "dl_path": "", + "convert": "1", + "encoder": "h264", + "bitrate_add": "1.0" + } + + with open(self.config_file, "w") as list_write: + json.dump(dummylist, list_write, indent=4) + + with open(self.config_file, "r") as list_read: + self.config = json.load(list_read) + + self.title(appname) + self.iconbitmap(r".\lib\logo.ico") + self.geometry("640x360") + self.resizable(height=False, width=False) + self.grid_rowconfigure(0, weight=1) + self.grid_columnconfigure(1, weight=1) + + self.logo_icon = customtkinter.CTkImage(Image.open( + os.path.join(lib_path, "logo32.png")), size=(32, 32)) + self.info_icon = customtkinter.CTkImage(Image.open( + os.path.join(lib_path, "info32.png")), size=(32, 32)) + self.setting_icon = customtkinter.CTkImage(Image.open( + os.path.join(lib_path, "setting_32.png")), size=(32, 32)) + + if thm_conf: + pass + else: + thm_dummy = Image.new("L", (640, 360)) + thm_dummy.save(lib_path + "\\thm.png") + + self.thm_preview = customtkinter.CTkImage(Image.open( + os.path.join(lib_path, "thm.png")), size=(160, 90)) + + self.title_font = customtkinter.CTkFont( + family="meiryo", size=16, weight="bold") + self.info_font = customtkinter.CTkFont(family="meiryo", size=14) + self.ui_font = customtkinter.CTkFont(family="meiryo", size=12) + self.button_font = customtkinter.CTkFont( + family="meiryo", size=14, weight="bold") + + self.frame = customtkinter.CTkFrame(self, corner_radius=0) + self.frame.grid(row=0, column=0, sticky="nsew") + self.frame.grid_rowconfigure(4, weight=1) + + self.dl_frame_button = customtkinter.CTkButton(self.frame, width=32, height=36, border_spacing=4, image=self.logo_icon, + text="", fg_color="transparent", hover_color=("gray70", "gray30"), command=self.frame_select_dl) + self.dl_frame_button.grid(row=0, column=0, sticky="ew") + + self.info_button = customtkinter.CTkButton(self.frame, width=32, height=36, border_spacing=4, image=self.info_icon, + text="", fg_color="transparent", hover_color=("gray70", "gray30"), command=self.app_info) + self.info_button.grid(row=2, column=0, sticky="ew") + + self.setting_frame_button = customtkinter.CTkButton(self.frame, width=32, height=36, border_spacing=4, image=self.setting_icon, text="", fg_color="transparent", hover_color=("gray70", "gray30"), command=self.frame_select_setting) + self.setting_frame_button.grid(row=1, column=0, sticky="ew") + + # dl_frame + self.dl_frame = customtkinter.CTkFrame(self, fg_color="transparent") + self.dl_frame.grid(row=0, column=1) + self.dl_frame.grid_rowconfigure(0, weight=1) + self.dl_frame.grid_columnconfigure(0, weight=1) + + self.dl_info_frame = customtkinter.CTkFrame( + self.dl_frame, fg_color="transparent") + self.dl_info_frame.grid(row=0, column=0, padx=2, pady=2) + self.dl_info_frame.grid_columnconfigure(0, weight=2) + self.dl_info_frame.grid_columnconfigure(1, weight=1) + + self.dl_info_label_frame = customtkinter.CTkFrame( + self.dl_info_frame, fg_color="transparent") + self.dl_info_label_frame.grid(row=0, column=0, padx=8, pady=0) + + self.dl_video_title = customtkinter.CTkLabel( + self.dl_info_label_frame, text="URLを入力してください...", width=360, font=self.title_font, wraplength=360, anchor=tk.W) + self.dl_video_title.grid(row=0, column=0, padx=2, pady=4) + + self.dl_creator_title = customtkinter.CTkLabel( + self.dl_info_label_frame, text="クリップボードの内容が自動的にコピーされています。", width=360, font=self.info_font, wraplength=360, anchor=tk.W) + self.dl_creator_title.grid(row=1, column=0, padx=2, pady=4) + + self.dl_video_thm = customtkinter.CTkLabel( + self.dl_info_frame, image=self.thm_preview, text="", anchor=tk.NE) + self.dl_video_thm.grid(row=0, column=1, padx=8, pady=0) + + self.dl_input_frame = customtkinter.CTkFrame( + self.dl_frame, fg_color="transparent") + self.dl_input_frame.grid(row=1, column=0, padx=16, pady=2) + self.dl_input_frame.grid_rowconfigure(0, weight=1) + self.dl_input_frame.grid_columnconfigure(0, weight=1) + + self.dl_url_textbox = customtkinter.CTkEntry( + self.dl_input_frame, placeholder_text="URLを入力", width=428, font=self.ui_font) + self.dl_url_textbox.grid(row=0, column=0, padx=8, pady=8) + self.dl_url_textbox.insert(0, cpurl) + + self.dl_path_textbox = customtkinter.CTkEntry( + self.dl_input_frame, placeholder_text="保存先を入力...", width=428, font=self.ui_font) + self.dl_path_textbox.grid(row=1, column=0, padx=8, pady=8) + + self.dl_path_paste_button = customtkinter.CTkButton( + self.dl_input_frame, text="URLを貼り付け", command=self.dl_path_paste, font=self.button_font) + self.dl_path_paste_button.grid(row=0, column=1, padx=8, pady=8) + + self.dl_path_button = customtkinter.CTkButton( + self.dl_input_frame, text="保存先を選択", command=self.dl_path_select, font=self.button_font) + self.dl_path_button.grid(row=1, column=1, padx=8, pady=8) + + self.dl_path_textbox.insert(0, self.config["dl_path"].rstrip('\n')) + + self.dl_control_frame = customtkinter.CTkFrame( + self.dl_frame, fg_color="transparent") + self.dl_control_frame.grid(row=2, column=0, padx=16, pady=6) + self.dl_control_frame.grid_rowconfigure(0, weight=1) + self.dl_control_frame.grid_columnconfigure(0, weight=1) + + self.dl_audio_switch = customtkinter.CTkSwitch( + self.dl_control_frame, text="音声を出力", width=120, font=self.ui_font) + self.dl_audio_switch.grid(row=1, column=1, padx=4, pady=6) + + self.dl_video_switch = customtkinter.CTkSwitch( + self.dl_control_frame, text="動画を出力", width=120, font=self.ui_font) + self.dl_video_switch.grid(row=1, column=0, padx=4, pady=6) + self.dl_video_switch.select() + + self.dl_special_dir_switch = customtkinter.CTkSwitch( + self.dl_control_frame, text="SpeDir", width=120, font=self.ui_font) + self.dl_special_dir_switch.grid(row=2, column=0, padx=4, pady=6) + + self.dl_acodec_select = customtkinter.CTkComboBox(self.dl_control_frame, width=140, values=[ + "bestaudio", "opus", "vorbis", "aac", "mp3"], font=self.ui_font, dropdown_font=self.ui_font) + self.dl_acodec_select.grid(row=1, column=2, padx=4, pady=6) + + self.dl_start_button = customtkinter.CTkButton( + self.dl_control_frame, text="ダウンロード", command=self.dl_start_thread, font=self.button_font) + self.dl_start_button.grid(row=2, column=3, padx=4, pady=6) + + + + # setting_frame + self.setting_frame = customtkinter.CTkFrame(self, fg_color="transparent") + self.setting_frame.grid_rowconfigure(0, weight=1) + self.setting_frame.grid_columnconfigure(0, weight=1) + + self.setting_video_frame = customtkinter.CTkFrame(self.setting_frame, fg_color="transparent") + self.setting_video_frame.grid(row=0, column=0) + self.setting_video_title = customtkinter.CTkLabel(self.setting_video_frame, text="動画", width=400, font=self.title_font, wraplength=400, anchor=tk.W) + self.setting_video_title.grid(row=0, column=0) + self.setting_video_convert_title = customtkinter.CTkLabel(self.setting_video_frame, text="H264に変換する", width=400, font=self.info_font, wraplength=400, anchor=tk.W) + self.setting_video_convert_title.grid(row=1, column=0) + self.setting_video_convert_detail = customtkinter.CTkLabel(self.setting_video_frame, text="動画のコーデックがH264以外の場合、自動的に変換します。", width=400, font=self.ui_font, wraplength=400, anchor=tk.W) + self.setting_video_convert_detail.grid(row=2, column=0) + self.setting_video_convert_switch = customtkinter.CTkSwitch(self.setting_video_frame, text="", width=120, font=self.ui_font) + self.setting_video_convert_switch.grid(row=1, column=1) + self.setting_video_encoder_title = customtkinter.CTkLabel(self.setting_video_frame, text="エンコーダーの選択", width=400, font=self.info_font, wraplength=400, anchor=tk.W) + self.setting_video_encoder_title.grid(row=3, column=0) + self.setting_video_encoder_detail = customtkinter.CTkLabel(self.setting_video_frame, text="変換時に使用するエンコーダーを選択します。", width=400, font=self.ui_font, wraplength=400, anchor=tk.W) + self.setting_video_encoder_detail.grid(row=4, column=0) + self.setting_video_encoder_combobox = customtkinter.CTkComboBox(self.setting_video_frame, width=140, values=["h264", "h264_qsv", "h264_nvenc", "libx264"], font=self.ui_font, dropdown_font=self.ui_font) + self.setting_video_encoder_combobox.grid(row=3, column=1) + self.setting_video_bitrate_title = customtkinter.CTkLabel(self.setting_video_frame, text="ビットレートの上乗せ", width=400, font=self.info_font, wraplength=400, anchor=tk.W) + self.setting_video_bitrate_title.grid(row=5, column=0) + self.setting_video_bitrate_detail = customtkinter.CTkLabel(self.setting_video_frame, text="H264に変換後、元動画の画質を維持するためにビットレートを上げます。", width=400, font=self.ui_font, wraplength=400, anchor=tk.W) + self.setting_video_bitrate_detail.grid(row=6, column=0) + self.setting_video_bitrate_combobox = customtkinter.CTkComboBox(self.setting_video_frame, width=140, values=["1", "1.25", "1.5", "2"], font=self.ui_font, dropdown_font=self.ui_font) + self.setting_video_bitrate_combobox.grid(row=5, column=1) + + + if self.config["convert"] == "1": + self.setting_video_convert_switch.select() + else: + self.setting_video_convert_switch.deselect() + + self.setting_video_encoder_combobox.set(self.config["encoder"]) + self.setting_video_bitrate_combobox.set(self.config["bitrate_add"]) + + self.select_frame_name("dl_frame") + + + def select_frame_name(self, name): + self.dl_frame_button.configure( + fg_color=("gray75", "gray25") if name == "dl_frame" else "transparent") + self.setting_frame_button.configure( + fg_color=("gray75", "gray25") if name == "setting_frame" else "transparent") + + if name == "dl_frame": + self.dl_frame.grid(row=0, column=1, sticky="nsew") + else: + self.dl_frame.grid_forget() + + if name == "setting_frame": + self.setting_frame.grid(row=0, column=1, sticky="nsew") + else: + self.setting_frame.grid_forget() + + def frame_select_dl(self): + self.select_frame_name("dl_frame") + + def frame_select_setting(self): + self.select_frame_name("setting_frame") + + def dl_path_select(self): + dl_path = tk.filedialog.askdirectory( + title="保存場所を選択...", initialdir="%HOMEPATH%/Videos") + dl_path_win = dl_path.replace("/", "\\") + self.dl_path_textbox.delete(0, tk.END) + self.dl_path_textbox.insert(0, dl_path_win) + self.config["dl_path"] = dl_path_win + + def dl_path_paste(self): + cpurl = pyperclip.paste() + self.dl_url_textbox.delete(0, tk.END) + self.dl_url_textbox.insert(0, cpurl) + + + + # ダウンロード + + def dl_start_thread(self): + if self.dl_video_switch.get() == 0 and self.dl_audio_switch.get() == 0: + messagebox.showerror("ダウンロードエラー", "動画、音声のどちらをダウンロードするかを選択してください。") + sys.exit() + + if self.dl_url_textbox.get() == "": + messagebox.showerror( + "ダウンロードエラー", "URLが入力されていません。\n有効なURLを入力してから、もう一度お試しください。") + sys.exit() + dl_thread = threading.Thread(target=self.dl_start) + dl_thread.start() + + def dl_start(self): + error = 0 + self.dl_start_button.configure( + text="ダウンロード中...", state="disabled", fg_color="gray") + + if self.dl_special_dir_switch.get() == 1: + video_path = self.dl_path_textbox.get() + "\\" + "video" + "\\" + "source" + audio_path = self.dl_path_textbox.get() + "\\" + "audio" + "\\" + "source" + else: + video_path = self.dl_path_textbox.get() + audio_path = self.dl_path_textbox.get() + + self.ytdl = Download().info(self.dl_url_textbox.get()) + if self.ytdl == "url_error": + error = 1 + else: + self.thm_preview = customtkinter.CTkImage(Image.open(os.path.join(lib_path, "thm.png")), size=(160, 90)) + self.dl_video_thm.configure(image=self.thm_preview) + self.dl_video_title.configure(text=self.ytdl["video_title"]) + self.dl_creator_title.configure(text=self.ytdl["video_creator"]) + + if self.dl_video_switch.get() == 1: + print(self.ytdl["video_title_re"]) + if os.path.isfile(video_path + "\\" + self.ytdl["video_title_re"] + ".mp4") or os.path.isfile(video_path + "\\" + self.ytdl["video_title_re"] + "_h264.mp4"): + video_file_num_check = 0 + video_file_num = 1 + while video_file_num_check == 0: + if os.path.isfile(video_path + "\\" + self.ytdl["video_title_re"] + "_" + str(video_file_num) + ".mp4") or os.path.isfile(video_path + "\\" + self.ytdl["video_title_re"] + "_" + str(video_file_num) + "_h264.mp4"): + video_file_num = video_file_num + 1 + else: + video_file_num_check = 1 + self.video_dl = Download().video(self.dl_url_textbox.get(), video_path, self.ytdl["video_title"], self.ytdl["video_title_re"] + "_" + str(video_file_num), "kayoko") + else: + self.video_dl = Download().video(self.dl_url_textbox.get(), video_path, self.ytdl["video_title"], self.ytdl["video_title_re"], "kayoko") + + if self.video_dl == "download_error": + messagebox.showerror("ダウンロードエラー", "ダウンロードの段階でエラーが発生しました。動画はダウンロードされません。") + error = 1 + else: + if self.setting_video_convert_switch.get() == 1: + self.video_convert = Convert().h264(self.video_dl["file_path"], self.video_dl["dir_path"], self.video_dl["video_title_re"], self.setting_video_encoder_combobox.get(), float(self.setting_video_bitrate_combobox.get())) + if self.video_convert == "convert_error": + messagebox.showerror("ダウンロードエラー", "コーデックの変換の段階でエラーが発生しました。動画は変換されずにダウンロードされます。") + error = 1 + else: + self.file_path = self.video_convert["file_path"] + else: + self.file_path = self.video_dl["file_path"] + + if self.dl_audio_switch.get() == 1: + self.ytdl = Download().info(self.dl_url_textbox.get()) + if self.ytdl == "url_error": + error = 1 + else: + if os.path.isfile(audio_path + "\\" + self.ytdl["video_title_re"] + ".m4a"): + audio_file_num_check = 0 + audio_file_num = 1 + while audio_file_num_check == 0: + if os.path.isfile(audio_path + "\\" + self.ytdl["video_title_re"] + "_" + str(audio_file_num) + ".m4a"): + audio_file_num = audio_file_num + 1 + else: + audio_file_num_check = 1 + self.audio_dl = Download().audio(self.dl_url_textbox.get(), audio_path, self.ytdl["video_title"], self.ytdl["video_title_re"] + "_" + str(audio_file_num)) + else: + self.audio_dl = Download().audio(self.dl_url_textbox.get(), audio_path, self.ytdl["video_title"], self.ytdl["video_title_re"]) + if self.audio_dl == "download_error": + messagebox.showerror("ダウンロードエラー", "ダウンロードの段階でエラーが発生しました。音声はダウンロードされません。") + else: + if self.dl_video_switch.get() == 0: + self.file_path = self.audio_dl["file_path"] + + if error == 0: + Notify().download_success(self.ytdl["video_title"], self.file_path, self.dl_path_textbox.get()) + + self.dl_start_button.configure( + text="ダウンロード", state="normal", fg_color="#3b8ed0") + + def app_info(self): + Notify().info() + + def handler_close(self): + self.config["dl_path"] = self.dl_path_textbox.get() + self.config["convert"] = str(self.setting_video_convert_switch.get()) + self.config["encoder"] = self.setting_video_encoder_combobox.get() + self.config["bitrate_add"] = self.setting_video_bitrate_combobox.get() + with open(self.config_file, "w") as list_write: + json.dump(self.config, list_write, indent=4) + self.destroy() + + +if __name__ == "__main__": + app = App() + app.protocol("WM_DELETE_WINDOW", app.handler_close) + app.mainloop() diff --git a/lib/config.json b/lib/config.json new file mode 100644 index 0000000..b96a28a --- /dev/null +++ b/lib/config.json @@ -0,0 +1,6 @@ +{ + "dl_path": "E:\\YTPMV\\source\\bgm", + "convert": "1", + "encoder": "h264_nvenc", + "bitrate_add": "1.5" +} \ No newline at end of file diff --git a/lib/error64.png b/lib/error64.png new file mode 100644 index 0000000..416b073 Binary files /dev/null and b/lib/error64.png differ diff --git a/lib/info32.png b/lib/info32.png new file mode 100644 index 0000000..2805359 Binary files /dev/null and b/lib/info32.png differ diff --git a/lib/info64.png b/lib/info64.png new file mode 100644 index 0000000..3b3bd85 Binary files /dev/null and b/lib/info64.png differ diff --git a/lib/logo.ico b/lib/logo.ico new file mode 100644 index 0000000..03bd701 Binary files /dev/null and b/lib/logo.ico differ diff --git a/lib/logo.png b/lib/logo.png new file mode 100644 index 0000000..208926a Binary files /dev/null and b/lib/logo.png differ diff --git a/lib/logo16.png b/lib/logo16.png new file mode 100644 index 0000000..0b9ded5 Binary files /dev/null and b/lib/logo16.png differ diff --git a/lib/logo32.png b/lib/logo32.png new file mode 100644 index 0000000..f92f742 Binary files /dev/null and b/lib/logo32.png differ diff --git a/lib/setting_32.png b/lib/setting_32.png new file mode 100644 index 0000000..89fdc78 Binary files /dev/null and b/lib/setting_32.png differ diff --git a/lib/thm.png b/lib/thm.png new file mode 100644 index 0000000..138c8bf Binary files /dev/null and b/lib/thm.png differ diff --git a/lib/wod_icon.ico b/lib/wod_icon.ico new file mode 100644 index 0000000..52a974b Binary files /dev/null and b/lib/wod_icon.ico differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0d2753d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +customtkinter +pillow +winotify +ffmpeg-python +pyperclip +yt-dlp \ No newline at end of file diff --git a/rminv.ini b/rminv.ini new file mode 100644 index 0000000..d28cb94 --- /dev/null +++ b/rminv.ini @@ -0,0 +1,16 @@ +import re +import string + +def remove_invalid_chars(text): + # Windowsで無効な文字のリスト + invalid_chars = re.escape(''.join([chr(i) for i in range(32)] + [ord(char) for char in '/\\:*?"<>|'])) + + # 無効な文字を空白に置換 + cleaned_text = re.sub(r'[' + invalid_chars + ']', '', text) + + return cleaned_text + +# 使用例 +file_name = "My.File$Name%^.txt" +cleaned_file_name = remove_invalid_chars(file_name) +print(cleaned_file_name) # 出力: "My.FileName.txt" \ No newline at end of file diff --git a/rminv.py b/rminv.py new file mode 100644 index 0000000..c8fc139 --- /dev/null +++ b/rminv.py @@ -0,0 +1,15 @@ +import re + +def remove_invalid_characters(input_string): + # 置換対象の無効な文字を正規表現で指定 + pattern = r'[\\/:*?"<>|]+' + # 置換文字列は空文字列に設定 + replacement = '' + # 無効な文字を削除 + cleaned_string = re.sub(pattern, replacement, input_string) + return cleaned_string + +# 例: 無効な文字を含む文字列 +original_string = '\\gbahsd:njs?<>|"asd/as*' +cleaned_result = remove_invalid_characters(original_string) +print(cleaned_result) # 出力: 'gbahsdnjsasdas'