1.YouTube視聴時間制限デスクトップアプリ
overall
・YouTubeを1日1時間までしか見れないようにしたい
・とりあえずWindows版だけでOK
・1時間を超えるとHostsファイルを編集してDNSを偽装し24時間経つまで見れないようにする
・Hostsファイルを編集するには管理者権限でアプリを起動させる必要がある
・Chrome拡張機能だとEdgeとかで見れてしまうのでデスクトップアプリにしたい
・アプリを起動しなくてもWindows起動時に自動起動するようにしたい
・視聴時間を監視するのでバックグラウンド機能が必要
※欲を言えば毎日のYouTube視聴時間を可視化したい
※欲を言えば、YouTubeを開くたびに最前面に問題が表示されて、その問題を正解しないとYouTubeが見れないようにしたい(勉強広告というアプリ名にする)
select programming language
Custom Tkinterでは「ブラウザで開いているタブのタイトルやURL」を取得できなかった → Chrome拡張機能に変更
| Custom Tkinter (python) | Electron (node.js) | Chrome拡張機能(Edge拡張機能) | |
| 権限管理 | pythonのosやsubprocessで簡単 | node.jsではsudo-prompt等の外部モジュールが必要 | 不要? |
| Hosts操作 | with open(…)など簡単 | node.jsでfsで編集できるが管理者権限がネック | 不要 |
| 実行サイズ | 数MB | 数百MB(Electronは重い) | 不明 |
| タスクスケジューリング | scheduleやthreading.Timerで簡単 | 非同期処理で複雑 | 不明 |
| YouTube視聴時間の計測 | chrome.exeやmsedge.exeのウィンドウタイトルにYouTubeが含まれているかを定期チェック | 左に同じ | 正確に計測可能 |
失敗(CustomTkinter)
pip install customtkinter psutil
pip install pywin32 # win32guiを使う場合import customtkinter as ctk
import os
import sys
import time
import datetime
import threading
import json
import winreg
import psutil
import win32gui
# =====
# 設定
# (使用時間データの保存先は %APPDATA% = C:\Users\ユーザ名\AppData\Roaming)
# (os.getenv()は環境変数Keyがあればその値を返す関数)
# =====
HOSTS_PATH = r"C:\Windows\System32\drivers\etc\hosts"
DOMAINS = ["youtube.com", "www.youtube.com", "m.youtube.com"]
USAGE_FILE = os.path.join(os.getenv("APPDATA"), "youtube_limiter.json")
DAILY_LIMIT = 3600
CHECK_INTERVAL = 5
usage_seconds = 0
last_active = None
stop_thread = False
# =====
# PC起動時に自動でこのアプリを起動させる
# =====
def add_to_startup():
exe_path = sys.executable
key_path = r"Software\Microsoft\Windows\CurrentVersion\Run"
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_path, 0, winreg.KEY_SET_VALUE) as key:
winreg.SetValueEx(key, "YouTubeLimiter", 0, winreg.REG_SZ, exe_path)
print("スタートアップ登録済み")
# =====
# 制限発動(hostsファイル編集)
# =====
def block_youtube():
with open(HOSTS_PATH, "r+", encoding="utf-8") as f:
content = f.read()
for domain in DOMAINS:
if domain not in content:
f.write(f"\n127.0.0.1 {domain}")
print("YouTubeをブロック中")
# =====
# 制限解除
# (書き込み専用で開くとファイルが空になる)
# =====
def unblock_youtube():
with open(HOSTS_PATH, "r", encoding="utf-8") as f:
lines = f.readlines()
with open(HOSTS_PATH, "w", encoding="utf-8") as f:
for line in lines:
if not any(domain in line for domain in DOMAINS):
f.write(line)
print("YouTubeブロック解除")
# =====
# 使用時間データの読み込み
# =====
def load_usage():
if not os.path.exists(USAGE_FILE):
return {"date": str(datetime.date.today()), "seconds": 0}
with open(USAGE_FILE, "r") as f:
return json.load(f)
# =====
# 使用時間データの保存
# =====
def save_usage(data):
with open(USAGE_FILE, "w") as f:
json.dump(data, f)
# =====
# 使用時間データのリセット
# =====
def reset_daily_usage():
data = {"date": str(datetime.date.today()), "seconds": 0}
save_usage(data)
unblock_youtube()
print("使用時間をリセット")
# =====
# 【失敗】YouTubeを視聴しているか検出
# psutilではブラウザで開いているURLを取得できなかった
# =====
def is_youtube_active():
for proc in psutil.process_iter(['name', 'cmdline']):
try:
name = (proc.info['name'] or "").lower()
cmdline_list = proc.info.get('cmdline', [])
if not isinstance(cmdline_list, (list, tuple)):
continue # None など iterable でない場合スキップ
cmdline = " ".join(cmdline_list).lower()
if any(browser in name for browser in ["brave", "chrome", "msedge", "firefox"]):
if "youtube.com" in cmdline:
print(f"✅ YouTube検出: {name} ({cmdline})")
return True
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
continue
return False
# =====
# 【失敗】YouTubeを視聴しているか検出
# win32guiではブラウザのタブ名を取得できなかった
# =====
# def is_youtube_active():
# def callback(hwnd, titles):
# if win32gui.IsWindowVisible(hwnd):
# title = win32gui.GetWindowText(hwnd)
# if title:
# print(title)
# if "YouTube" in title:
# titles.append(title)
# titles = []
# win32gui.EnumWindows(callback, titles)
# return len(titles) > 0
# =====
# YouTube視聴時間の測定
# (psutilでChromeなどのプロセス一覧を調査し、コマンドラインに"youtube.com"を含むか確認)
# (ブラウザでYouTubeを開いているだけで加算されるため、実際に再生していなくてもカウントされる)
# =====
def monitor_time():
global usage_seconds, last_active
data = load_usage()
usage_seconds = data["seconds"]
while not stop_thread:
today = str(datetime.date.today())
if data["date"] != today:
reset_daily_usage()
data = load_usage()
usage_seconds = 0
if is_youtube_active():
usage_seconds += CHECK_INTERVAL
data["seconds"] = usage_seconds
save_usage(data)
if usage_seconds >= DAILY_LIMIT:
block_youtube()
else:
unblock_youtube()
time.sleep(CHECK_INTERVAL)
# =====
# GUI
# =====
app = ctk.CTk()
app.title("制限アプリ")
app.geometry("300x200")
label = ctk.CTkLabel(app, text="YouTube使用時間: 0秒 / 3600秒")
label.pack(pady=20)
def update_label():
label.configure(text=f"YouTube使用時間: {usage_seconds}秒 / 3600秒")
app.after(1000, update_label)
def on_close():
global stop_thread
stop_thread = True
app.destroy()
reset_btn = ctk.CTkButton(app, text="リセット", command=reset_daily_usage)
reset_btn.pack(pady=10)
add_to_startup()
threading.Thread(target=monitor_time, daemon=True).start()
update_label()
# WM_DELETE_WINDOWはユーザーがアプリウィンドウの閉じるボタンを押すと発生するイベント
app.protocol("WM_DELETE_WINDOW", on_close)
app.mainloop()pyinstaller
pyinstaller --noconsole --onefile --uac-admin main.pypyinstallerでビルド時に管理者権限要求を付与します
–uac-admin によってアプリ実行時に「このアプリがデバイスに変更を加えることを許可しますか?」というUACが自動で表示されます
BACK