1st
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/venv/
|
||||
/*.lnk
|
BIN
ic/128.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
BIN
ic/32.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
ic/48.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
ic/64.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
384
jp_win.py
Normal file
|
@ -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()
|
477
jp_win_new.py
Normal file
|
@ -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()
|
6
lib/config.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"dl_path": "E:\\YTPMV\\source\\bgm",
|
||||
"convert": "1",
|
||||
"encoder": "h264_nvenc",
|
||||
"bitrate_add": "1.5"
|
||||
}
|
BIN
lib/error64.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
lib/info32.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
lib/info64.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
lib/logo.ico
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
lib/logo.png
Normal file
After Width: | Height: | Size: 5 KiB |
BIN
lib/logo16.png
Normal file
After Width: | Height: | Size: 507 B |
BIN
lib/logo32.png
Normal file
After Width: | Height: | Size: 1,021 B |
BIN
lib/setting_32.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
lib/thm.png
Normal file
After Width: | Height: | Size: 71 KiB |
BIN
lib/wod_icon.ico
Normal file
After Width: | Height: | Size: 104 KiB |
6
requirements.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
customtkinter
|
||||
pillow
|
||||
winotify
|
||||
ffmpeg-python
|
||||
pyperclip
|
||||
yt-dlp
|
16
rminv.ini
Normal file
|
@ -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"
|
15
rminv.py
Normal file
|
@ -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'
|