diff --git a/AppBase.py b/AppBase.py index 0b3b8e4..5917e6e 100644 --- a/AppBase.py +++ b/AppBase.py @@ -33,6 +33,12 @@ def __init__(self): #各種初期設定 self.InitLogger() self.LoadSettings() + # localeは「ja_JP」のような形式。設定ファイルの定義ミスにより「_」ではなく「-」を使っていたため、必要に応じて最初に設定ファイルを書き換える。 + locale_old = self.config["general"]["locale"] + if "-" in locale_old: + locale_new = locale_old.replace("-", "_") + self.log.warn("Fixed locale setting: %s -> %s" % (locale_old, locale_new)) + self.config["general"]["locale"] = locale_new try: if self.config["general"]["locale"]!=None: locale.setlocale(locale.LC_TIME,self.config["general"]["locale"]) diff --git a/DefaultSettings.py b/DefaultSettings.py index 62239ad..d65b403 100644 --- a/DefaultSettings.py +++ b/DefaultSettings.py @@ -10,7 +10,7 @@ def get(): config["general"]={ "language": "ja-JP", "fileVersion": "101", - "locale": "ja-JP", + "locale": "ja_JP", "autoHide": False, "minimizeOnExit": True, } diff --git a/app.py b/app.py index d23ed1f..4d79079 100644 --- a/app.py +++ b/app.py @@ -71,6 +71,8 @@ def initialize(self): # 「ツイキャスの監視を有効化」の設定値を確認 if self.config.getboolean("twitcasting", "enable", True) and self.tc.initialize(): self.tc.start() + from sources import ydl + self.ydl = ydl.YDL() self.hMainView.Show() if self.config.getboolean("general", "autoHide", False): self.hMainView.events.hide() diff --git a/constants.py b/constants.py index 6ae91d5..5e24e86 100644 --- a/constants.py +++ b/constants.py @@ -9,8 +9,8 @@ APP_FULL_NAME = "Universal Live Tracking and Recording App"#アプリケーションの完全な名前 APP_NAME="ULTRA"#アプリケーションの名前 APP_ICON = "ultra.ico" -APP_VERSION="1.6.0" -APP_LAST_RELEASE_DATE="2023-04-29" +APP_VERSION="1.7.0" +APP_LAST_RELEASE_DATE="2023-07-17" APP_COPYRIGHT_YEAR="2021-2023" APP_LICENSE="Apache License 2.0" APP_DEVELOPERS="Kazto Kitabatake, ACT Laboratory" diff --git a/locale/en-us/LC_MESSAGES/messages.mo b/locale/en-us/LC_MESSAGES/messages.mo index 203e282..d24d21f 100644 Binary files a/locale/en-us/LC_MESSAGES/messages.mo and b/locale/en-us/LC_MESSAGES/messages.mo differ diff --git a/locale/en-us/LC_MESSAGES/messages.po b/locale/en-us/LC_MESSAGES/messages.po index f53f138..208af77 100644 --- a/locale/en-us/LC_MESSAGES/messages.po +++ b/locale/en-us/LC_MESSAGES/messages.po @@ -5,19 +5,19 @@ # msgid "" msgstr "" -"Project-Id-Version: TCV\n" +"Project-Id-Version: ULTRA\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-29 23:05+0900\n" -"PO-Revision-Date: 2023-04-29 23:07+0900\n" +"POT-Creation-Date: 2023-07-17 10:32+0900\n" +"PO-Revision-Date: 2023-07-17 10:49+0900\n" "Last-Translator: \n" "Language-Team: \n" "Language: en_US\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 3.2.2\n" +"X-Generator: Poedit 3.3.2\n" -#: app.py:66 menuItemsDic.py:31 +#: app.py:66 menuItemsDic.py:31 menuItemsDic.py:34 msgid "録画形式の設定" msgstr "Recording format settings" @@ -149,26 +149,34 @@ msgid "通知対象ユーザの管理(&M)" msgstr "&Manage Notified Users" #: menuItemsDic.py:32 +msgid "その他のサービス(&yt-dlp)" +msgstr "Other Services(&yt-dlp)" + +#: menuItemsDic.py:33 +msgid "&URLを指定してダウンロード" +msgstr "Download by &URL" + +#: menuItemsDic.py:35 msgid "設定(&S)" msgstr "&Settings" -#: menuItemsDic.py:33 +#: menuItemsDic.py:36 msgid "キーボードショートカットの設定(&K)" msgstr "Shortcut &Key Configuration" -#: menuItemsDic.py:34 +#: menuItemsDic.py:37 msgid "グローバルホットキーの設定(&H)" msgstr "Global &Hot Key Configuration" -#: menuItemsDic.py:35 +#: menuItemsDic.py:38 msgid "Windows起動時の自動起動を有効化(&W)" msgstr "Enable automatic startup on &Windows startup" -#: menuItemsDic.py:36 +#: menuItemsDic.py:39 msgid "最新バージョンを確認(&U)" msgstr "Check For &Updates" -#: menuItemsDic.py:37 +#: menuItemsDic.py:40 msgid "バージョン情報(&V)" msgstr "&Version Info" @@ -177,12 +185,12 @@ msgstr "&Version Info" msgid "配信開始:%s、サービス:%s" msgstr "Broadcasting Started: %s, service: %s" -#: recorder.py:169 recorder.py:171 recorder.py:182 recorder.py:194 -#: recorder.py:198 recorder.py:200 +#: recorder.py:176 recorder.py:178 recorder.py:189 recorder.py:201 +#: recorder.py:205 recorder.py:207 msgid "録画エラー" msgstr "Recording error" -#: recorder.py:169 +#: recorder.py:176 msgid "" "録画の開始に失敗しました。録画の保存先が適切に設定されていることを確認してく" "ださい。定期的に再試行する場合は[はい]、処理を中断する場合は[いいえ]を選択し" @@ -194,41 +202,41 @@ msgstr "" "interrupt the process. You may be able to start recording correctly by " "selecting Yes and changing where the recording is saved." -#: recorder.py:171 recorder.py:182 +#: recorder.py:178 recorder.py:189 #, python-format msgid "%sのライブの録画処理を中断しました。" msgstr "Recording process for %s's broadcasting has been canseled." -#: recorder.py:185 +#: recorder.py:192 msgid "録画開始" msgstr "Start recording" -#: recorder.py:185 recorder.py:212 sources\twitcasting.py:830 +#: recorder.py:192 recorder.py:219 sources\twitcasting.py:830 #: sources\twitcasting.py:842 #, python-format msgid "ユーザ:%(user)s、ムービーID:%(movie)s" msgstr "User: %(user)s, Movie ID:%(movie)s" -#: recorder.py:186 +#: recorder.py:193 msgid "録画中" msgstr "Recording" -#: recorder.py:194 recorder.py:198 +#: recorder.py:201 recorder.py:205 #, python-format msgid "%sのライブを録画中にエラーが発生しました。" msgstr "An error has occured while recording %s's live." -#: recorder.py:194 recorder.py:198 recorder.py:200 +#: recorder.py:201 recorder.py:205 recorder.py:207 #, python-format msgid "詳細:%s" msgstr "Details: %s" -#: recorder.py:200 +#: recorder.py:207 #, python-format msgid "%sのライブを録画中にエラーが発生したため、再度録画を開始します。" msgstr "An error has occured while recording %s's live. retrying..." -#: recorder.py:212 +#: recorder.py:219 msgid "録画終了" msgstr "End of recording" @@ -286,7 +294,7 @@ msgstr "" "Download has successfully finished.\n" "Update will be started when closing the application." -#: sources\twitcasting.py:42 sources\twitcasting.py:1107 views\main.py:369 +#: sources\twitcasting.py:42 sources\twitcasting.py:1107 views\main.py:386 msgid "ツイキャス" msgstr "TwitCasting" @@ -693,6 +701,32 @@ msgid "認証が必要です。ブラウザで操作を完了してください msgstr "" "Authorization is required. Open the browser, and finish the authentication." +#: sources\ydl.py:22 +msgid "その他のサービス(yt-dlp)" +msgstr "Other Services(yt-dlp)" + +#: sources\ydl.py:25 +msgid "動画" +msgstr "video" + +#: sources\ydl.py:26 +msgid "音声のみ" +msgstr "Audio only" + +#: sources\ydl.py:51 +#, python-format +msgid "" +"動画情報の取得に失敗しました。\n" +"詳細:%s" +msgstr "" +"Failed to get video information.\n" +"Details: %s" + +#: sources\ydl.py:61 +#, python-format +msgid "%sのダウンロードは現在サポートされていません。" +msgstr "Downloading %s is not currently supported." + #: views\auth.py:23 msgid "ブラウザでの操作を待っています..." msgstr "waiting for browser..." @@ -746,7 +780,7 @@ msgstr "shortcut" msgid "識別子" msgstr "identifier" -#: views\globalKeyConfig.py:31 views\main.py:278 +#: views\globalKeyConfig.py:31 views\main.py:295 msgid "ショートカットキーの設定" msgstr "Shortcut Key Configuration" @@ -755,7 +789,7 @@ msgstr "Shortcut Key Configuration" #: views\globalKeyConfig.py:120 views\globalKeyConfig.py:128 #: views\globalKeyConfig.py:138 views\globalKeyConfig.py:143 #: views\keyConfig.py:57 views\KeyValueSettingDialogBase.py:333 -#: views\main.py:375 views\main.py:394 +#: views\main.py:392 views\main.py:411 msgid "なし" msgstr "None" @@ -945,23 +979,23 @@ msgstr "" "and key names.\n" "\n" -#: views\main.py:152 +#: views\main.py:160 msgid "ファイル(&F)" msgstr "&File" -#: views\main.py:153 +#: views\main.py:161 msgid "サービス(&S)" msgstr "&Services" -#: views\main.py:154 +#: views\main.py:162 msgid "オプション(&O)" msgstr "&Options" -#: views\main.py:155 +#: views\main.py:163 msgid "ヘルプ(&H)" msgstr "&Help" -#: views\main.py:198 views\main.py:204 views\main.py:399 +#: views\main.py:208 views\main.py:214 views\main.py:416 #: views\settingsDialog.py:167 msgid "" "設定の保存に失敗しました。下記のファイルへのアクセスが可能であることを確認し" @@ -969,54 +1003,58 @@ msgid "" msgstr "" "Failed to save settings. Make sure you can access to the following files:" -#: views\main.py:216 +#: views\main.py:226 views\main.py:282 msgid "URLを入力" msgstr "Enter URL" -#: views\main.py:216 +#: views\main.py:226 msgid "再生ページのURL" msgstr "Playback page URL" -#: views\main.py:223 views\main.py:230 +#: views\main.py:233 views\main.py:240 msgid "ユーザ名を入力" msgstr "Enter user name" -#: views\main.py:223 views\main.py:230 views\tcManageUser.py:20 +#: views\main.py:233 views\main.py:240 views\tcManageUser.py:20 #: views\tcManageUser.py:99 msgid "ユーザ名" msgstr "user name" -#: views\main.py:246 +#: views\main.py:256 msgid "すでに削除されています。" msgstr "It has already been deleted." -#: views\main.py:248 +#: views\main.py:258 msgid "アクセストークンの削除" msgstr "Delete access token" -#: views\main.py:248 +#: views\main.py:258 msgid "" "ツイキャス連携機能を無効化し、アクセストークンを削除します。よろしいですか?" msgstr "" "Disable TwitCasting functions and remove the access token. Are you sure?" -#: views\main.py:255 views\main.py:344 views\main.py:350 +#: views\main.py:265 views\main.py:361 views\main.py:367 msgid "完了" msgstr "Finish" -#: views\main.py:255 +#: views\main.py:265 msgid "アクセストークンを削除しました。" msgstr "Removed the access token." -#: views\main.py:286 +#: views\main.py:282 +msgid "URLの指定" +msgstr "set URL" + +#: views\main.py:303 msgid "グローバルホットキーの設定" msgstr "Global Hot Keys Settings" -#: views\main.py:325 views\main.py:341 +#: views\main.py:342 views\main.py:358 msgid "確認" msgstr "confirmation" -#: views\main.py:325 +#: views\main.py:342 msgid "" "録画処理を実行中です。このまま終了すると、録画は中断されます。終了してもよろ" "しいですか?" @@ -1024,17 +1062,17 @@ msgstr "" "Recording process is running. If you exit, the recording will be " "interrupted. Are you sure?" -#: views\main.py:341 +#: views\main.py:358 msgid "Windows起動時の自動起動はすでに設定されています。設定を解除しますか?" msgstr "" "Automatic startup at Windows startup is already set. Do you want to un-set " "it?" -#: views\main.py:344 +#: views\main.py:361 msgid "Windows起動時の自動起動を無効化しました。" msgstr "Disabled automatic startup at Windows startup." -#: views\main.py:350 +#: views\main.py:367 msgid "Windows起動時の自動起動を設定しました。" msgstr "Set automatic startup at Windows startup." @@ -1379,9 +1417,6 @@ msgstr "close" #~ msgid "Twitterとの通信に失敗しました。詳細:%s" #~ msgstr "Communication with Twitter failed. Details: %s" -#~ msgid "ユーザ情報の取得に失敗しました。詳細:%s" -#~ msgstr "Failed to retrieve user information. Details: %s" - #~ msgid "認証情報の保存に失敗しました。" #~ msgstr "Failed to save credentials." @@ -1410,9 +1445,6 @@ msgstr "close" #~ msgid "入力されたURLが正しくありません。" #~ msgstr "The URL entered is incorrect." -#~ msgid "このスペースはまだ開始されていません。" -#~ msgstr "This space has not yet started." - #~ msgid "非公開アカウント" #~ msgstr "Private account" diff --git a/locale/ja-jp/LC_MESSAGES/messages.po b/locale/ja-jp/LC_MESSAGES/messages.po index 9dcbba5..abef4b3 100644 --- a/locale/ja-jp/LC_MESSAGES/messages.po +++ b/locale/ja-jp/LC_MESSAGES/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ULTRA\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-29 23:05+0900\n" +"POT-Creation-Date: 2023-07-17 10:32+0900\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: app.py:66 menuItemsDic.py:31 +#: app.py:66 menuItemsDic.py:31 menuItemsDic.py:34 msgid "録画形式の設定" msgstr "" @@ -143,26 +143,34 @@ msgid "通知対象ユーザの管理(&M)" msgstr "" #: menuItemsDic.py:32 -msgid "設定(&S)" +msgid "その他のサービス(&yt-dlp)" msgstr "" #: menuItemsDic.py:33 +msgid "&URLを指定してダウンロード" +msgstr "" + +#: menuItemsDic.py:35 +msgid "設定(&S)" +msgstr "" + +#: menuItemsDic.py:36 msgid "キーボードショートカットの設定(&K)" msgstr "" -#: menuItemsDic.py:34 +#: menuItemsDic.py:37 msgid "グローバルホットキーの設定(&H)" msgstr "" -#: menuItemsDic.py:35 +#: menuItemsDic.py:38 msgid "Windows起動時の自動起動を有効化(&W)" msgstr "" -#: menuItemsDic.py:36 +#: menuItemsDic.py:39 msgid "最新バージョンを確認(&U)" msgstr "" -#: menuItemsDic.py:37 +#: menuItemsDic.py:40 msgid "バージョン情報(&V)" msgstr "" @@ -171,12 +179,12 @@ msgstr "" msgid "配信開始:%s、サービス:%s" msgstr "" -#: recorder.py:169 recorder.py:171 recorder.py:182 recorder.py:194 -#: recorder.py:198 recorder.py:200 +#: recorder.py:176 recorder.py:178 recorder.py:189 recorder.py:201 +#: recorder.py:205 recorder.py:207 msgid "録画エラー" msgstr "" -#: recorder.py:169 +#: recorder.py:176 msgid "" "録画の開始に失敗しました。録画の保存先が適切に設定されていることを確認してく" "ださい。定期的に再試行する場合は[はい]、処理を中断する場合は[いいえ]を選択し" @@ -184,41 +192,41 @@ msgid "" "きる場合があります。" msgstr "" -#: recorder.py:171 recorder.py:182 +#: recorder.py:178 recorder.py:189 #, python-format msgid "%sのライブの録画処理を中断しました。" msgstr "" -#: recorder.py:185 +#: recorder.py:192 msgid "録画開始" msgstr "" -#: recorder.py:185 recorder.py:212 sources\twitcasting.py:830 +#: recorder.py:192 recorder.py:219 sources\twitcasting.py:830 #: sources\twitcasting.py:842 #, python-format msgid "ユーザ:%(user)s、ムービーID:%(movie)s" msgstr "" -#: recorder.py:186 +#: recorder.py:193 msgid "録画中" msgstr "" -#: recorder.py:194 recorder.py:198 +#: recorder.py:201 recorder.py:205 #, python-format msgid "%sのライブを録画中にエラーが発生しました。" msgstr "" -#: recorder.py:194 recorder.py:198 recorder.py:200 +#: recorder.py:201 recorder.py:205 recorder.py:207 #, python-format msgid "詳細:%s" msgstr "" -#: recorder.py:200 +#: recorder.py:207 #, python-format msgid "%sのライブを録画中にエラーが発生したため、再度録画を開始します。" msgstr "" -#: recorder.py:212 +#: recorder.py:219 msgid "録画終了" msgstr "" @@ -272,7 +280,7 @@ msgid "" "ソフトウェア終了時に、自動でアップデートされます。" msgstr "" -#: sources\twitcasting.py:42 sources\twitcasting.py:1107 views\main.py:369 +#: sources\twitcasting.py:42 sources\twitcasting.py:1107 views\main.py:386 msgid "ツイキャス" msgstr "" @@ -638,6 +646,30 @@ msgstr "" msgid "認証が必要です。ブラウザで操作を完了してください。" msgstr "" +#: sources\ydl.py:22 +msgid "その他のサービス(yt-dlp)" +msgstr "" + +#: sources\ydl.py:25 +msgid "動画" +msgstr "" + +#: sources\ydl.py:26 +msgid "音声のみ" +msgstr "" + +#: sources\ydl.py:51 +#, python-format +msgid "" +"動画情報の取得に失敗しました。\n" +"詳細:%s" +msgstr "" + +#: sources\ydl.py:61 +#, python-format +msgid "%sのダウンロードは現在サポートされていません。" +msgstr "" + #: views\auth.py:23 msgid "ブラウザでの操作を待っています..." msgstr "" @@ -689,7 +721,7 @@ msgstr "" msgid "識別子" msgstr "" -#: views\globalKeyConfig.py:31 views\main.py:278 +#: views\globalKeyConfig.py:31 views\main.py:295 msgid "ショートカットキーの設定" msgstr "" @@ -698,7 +730,7 @@ msgstr "" #: views\globalKeyConfig.py:120 views\globalKeyConfig.py:128 #: views\globalKeyConfig.py:138 views\globalKeyConfig.py:143 #: views\keyConfig.py:57 views\KeyValueSettingDialogBase.py:333 -#: views\main.py:375 views\main.py:394 +#: views\main.py:392 views\main.py:411 msgid "なし" msgstr "" @@ -877,90 +909,94 @@ msgid "" "\n" msgstr "" -#: views\main.py:152 +#: views\main.py:160 msgid "ファイル(&F)" msgstr "" -#: views\main.py:153 +#: views\main.py:161 msgid "サービス(&S)" msgstr "" -#: views\main.py:154 +#: views\main.py:162 msgid "オプション(&O)" msgstr "" -#: views\main.py:155 +#: views\main.py:163 msgid "ヘルプ(&H)" msgstr "" -#: views\main.py:198 views\main.py:204 views\main.py:399 +#: views\main.py:208 views\main.py:214 views\main.py:416 #: views\settingsDialog.py:167 msgid "" "設定の保存に失敗しました。下記のファイルへのアクセスが可能であることを確認し" "てください。" msgstr "" -#: views\main.py:216 +#: views\main.py:226 views\main.py:282 msgid "URLを入力" msgstr "" -#: views\main.py:216 +#: views\main.py:226 msgid "再生ページのURL" msgstr "" -#: views\main.py:223 views\main.py:230 +#: views\main.py:233 views\main.py:240 msgid "ユーザ名を入力" msgstr "" -#: views\main.py:223 views\main.py:230 views\tcManageUser.py:20 +#: views\main.py:233 views\main.py:240 views\tcManageUser.py:20 #: views\tcManageUser.py:99 msgid "ユーザ名" msgstr "" -#: views\main.py:246 +#: views\main.py:256 msgid "すでに削除されています。" msgstr "" -#: views\main.py:248 +#: views\main.py:258 msgid "アクセストークンの削除" msgstr "" -#: views\main.py:248 +#: views\main.py:258 msgid "" "ツイキャス連携機能を無効化し、アクセストークンを削除します。よろしいですか?" msgstr "" -#: views\main.py:255 views\main.py:344 views\main.py:350 +#: views\main.py:265 views\main.py:361 views\main.py:367 msgid "完了" msgstr "" -#: views\main.py:255 +#: views\main.py:265 msgid "アクセストークンを削除しました。" msgstr "" -#: views\main.py:286 +#: views\main.py:282 +msgid "URLの指定" +msgstr "" + +#: views\main.py:303 msgid "グローバルホットキーの設定" msgstr "" -#: views\main.py:325 views\main.py:341 +#: views\main.py:342 views\main.py:358 msgid "確認" msgstr "" -#: views\main.py:325 +#: views\main.py:342 msgid "" "録画処理を実行中です。このまま終了すると、録画は中断されます。終了してもよろ" "しいですか?" msgstr "" -#: views\main.py:341 +#: views\main.py:358 msgid "Windows起動時の自動起動はすでに設定されています。設定を解除しますか?" msgstr "" -#: views\main.py:344 +#: views\main.py:361 msgid "Windows起動時の自動起動を無効化しました。" msgstr "" -#: views\main.py:350 +#: views\main.py:367 msgid "Windows起動時の自動起動を設定しました。" msgstr "" diff --git a/locale/messages.po b/locale/messages.po index 9dcbba5..abef4b3 100644 --- a/locale/messages.po +++ b/locale/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ULTRA\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-29 23:05+0900\n" +"POT-Creation-Date: 2023-07-17 10:32+0900\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: app.py:66 menuItemsDic.py:31 +#: app.py:66 menuItemsDic.py:31 menuItemsDic.py:34 msgid "録画形式の設定" msgstr "" @@ -143,26 +143,34 @@ msgid "通知対象ユーザの管理(&M)" msgstr "" #: menuItemsDic.py:32 -msgid "設定(&S)" +msgid "その他のサービス(&yt-dlp)" msgstr "" #: menuItemsDic.py:33 +msgid "&URLを指定してダウンロード" +msgstr "" + +#: menuItemsDic.py:35 +msgid "設定(&S)" +msgstr "" + +#: menuItemsDic.py:36 msgid "キーボードショートカットの設定(&K)" msgstr "" -#: menuItemsDic.py:34 +#: menuItemsDic.py:37 msgid "グローバルホットキーの設定(&H)" msgstr "" -#: menuItemsDic.py:35 +#: menuItemsDic.py:38 msgid "Windows起動時の自動起動を有効化(&W)" msgstr "" -#: menuItemsDic.py:36 +#: menuItemsDic.py:39 msgid "最新バージョンを確認(&U)" msgstr "" -#: menuItemsDic.py:37 +#: menuItemsDic.py:40 msgid "バージョン情報(&V)" msgstr "" @@ -171,12 +179,12 @@ msgstr "" msgid "配信開始:%s、サービス:%s" msgstr "" -#: recorder.py:169 recorder.py:171 recorder.py:182 recorder.py:194 -#: recorder.py:198 recorder.py:200 +#: recorder.py:176 recorder.py:178 recorder.py:189 recorder.py:201 +#: recorder.py:205 recorder.py:207 msgid "録画エラー" msgstr "" -#: recorder.py:169 +#: recorder.py:176 msgid "" "録画の開始に失敗しました。録画の保存先が適切に設定されていることを確認してく" "ださい。定期的に再試行する場合は[はい]、処理を中断する場合は[いいえ]を選択し" @@ -184,41 +192,41 @@ msgid "" "きる場合があります。" msgstr "" -#: recorder.py:171 recorder.py:182 +#: recorder.py:178 recorder.py:189 #, python-format msgid "%sのライブの録画処理を中断しました。" msgstr "" -#: recorder.py:185 +#: recorder.py:192 msgid "録画開始" msgstr "" -#: recorder.py:185 recorder.py:212 sources\twitcasting.py:830 +#: recorder.py:192 recorder.py:219 sources\twitcasting.py:830 #: sources\twitcasting.py:842 #, python-format msgid "ユーザ:%(user)s、ムービーID:%(movie)s" msgstr "" -#: recorder.py:186 +#: recorder.py:193 msgid "録画中" msgstr "" -#: recorder.py:194 recorder.py:198 +#: recorder.py:201 recorder.py:205 #, python-format msgid "%sのライブを録画中にエラーが発生しました。" msgstr "" -#: recorder.py:194 recorder.py:198 recorder.py:200 +#: recorder.py:201 recorder.py:205 recorder.py:207 #, python-format msgid "詳細:%s" msgstr "" -#: recorder.py:200 +#: recorder.py:207 #, python-format msgid "%sのライブを録画中にエラーが発生したため、再度録画を開始します。" msgstr "" -#: recorder.py:212 +#: recorder.py:219 msgid "録画終了" msgstr "" @@ -272,7 +280,7 @@ msgid "" "ソフトウェア終了時に、自動でアップデートされます。" msgstr "" -#: sources\twitcasting.py:42 sources\twitcasting.py:1107 views\main.py:369 +#: sources\twitcasting.py:42 sources\twitcasting.py:1107 views\main.py:386 msgid "ツイキャス" msgstr "" @@ -638,6 +646,30 @@ msgstr "" msgid "認証が必要です。ブラウザで操作を完了してください。" msgstr "" +#: sources\ydl.py:22 +msgid "その他のサービス(yt-dlp)" +msgstr "" + +#: sources\ydl.py:25 +msgid "動画" +msgstr "" + +#: sources\ydl.py:26 +msgid "音声のみ" +msgstr "" + +#: sources\ydl.py:51 +#, python-format +msgid "" +"動画情報の取得に失敗しました。\n" +"詳細:%s" +msgstr "" + +#: sources\ydl.py:61 +#, python-format +msgid "%sのダウンロードは現在サポートされていません。" +msgstr "" + #: views\auth.py:23 msgid "ブラウザでの操作を待っています..." msgstr "" @@ -689,7 +721,7 @@ msgstr "" msgid "識別子" msgstr "" -#: views\globalKeyConfig.py:31 views\main.py:278 +#: views\globalKeyConfig.py:31 views\main.py:295 msgid "ショートカットキーの設定" msgstr "" @@ -698,7 +730,7 @@ msgstr "" #: views\globalKeyConfig.py:120 views\globalKeyConfig.py:128 #: views\globalKeyConfig.py:138 views\globalKeyConfig.py:143 #: views\keyConfig.py:57 views\KeyValueSettingDialogBase.py:333 -#: views\main.py:375 views\main.py:394 +#: views\main.py:392 views\main.py:411 msgid "なし" msgstr "" @@ -877,90 +909,94 @@ msgid "" "\n" msgstr "" -#: views\main.py:152 +#: views\main.py:160 msgid "ファイル(&F)" msgstr "" -#: views\main.py:153 +#: views\main.py:161 msgid "サービス(&S)" msgstr "" -#: views\main.py:154 +#: views\main.py:162 msgid "オプション(&O)" msgstr "" -#: views\main.py:155 +#: views\main.py:163 msgid "ヘルプ(&H)" msgstr "" -#: views\main.py:198 views\main.py:204 views\main.py:399 +#: views\main.py:208 views\main.py:214 views\main.py:416 #: views\settingsDialog.py:167 msgid "" "設定の保存に失敗しました。下記のファイルへのアクセスが可能であることを確認し" "てください。" msgstr "" -#: views\main.py:216 +#: views\main.py:226 views\main.py:282 msgid "URLを入力" msgstr "" -#: views\main.py:216 +#: views\main.py:226 msgid "再生ページのURL" msgstr "" -#: views\main.py:223 views\main.py:230 +#: views\main.py:233 views\main.py:240 msgid "ユーザ名を入力" msgstr "" -#: views\main.py:223 views\main.py:230 views\tcManageUser.py:20 +#: views\main.py:233 views\main.py:240 views\tcManageUser.py:20 #: views\tcManageUser.py:99 msgid "ユーザ名" msgstr "" -#: views\main.py:246 +#: views\main.py:256 msgid "すでに削除されています。" msgstr "" -#: views\main.py:248 +#: views\main.py:258 msgid "アクセストークンの削除" msgstr "" -#: views\main.py:248 +#: views\main.py:258 msgid "" "ツイキャス連携機能を無効化し、アクセストークンを削除します。よろしいですか?" msgstr "" -#: views\main.py:255 views\main.py:344 views\main.py:350 +#: views\main.py:265 views\main.py:361 views\main.py:367 msgid "完了" msgstr "" -#: views\main.py:255 +#: views\main.py:265 msgid "アクセストークンを削除しました。" msgstr "" -#: views\main.py:286 +#: views\main.py:282 +msgid "URLの指定" +msgstr "" + +#: views\main.py:303 msgid "グローバルホットキーの設定" msgstr "" -#: views\main.py:325 views\main.py:341 +#: views\main.py:342 views\main.py:358 msgid "確認" msgstr "" -#: views\main.py:325 +#: views\main.py:342 msgid "" "録画処理を実行中です。このまま終了すると、録画は中断されます。終了してもよろ" "しいですか?" msgstr "" -#: views\main.py:341 +#: views\main.py:358 msgid "Windows起動時の自動起動はすでに設定されています。設定を解除しますか?" msgstr "" -#: views\main.py:344 +#: views\main.py:361 msgid "Windows起動時の自動起動を無効化しました。" msgstr "" -#: views\main.py:350 +#: views\main.py:367 msgid "Windows起動時の自動起動を設定しました。" msgstr "" diff --git a/menuItemsDic.py b/menuItemsDic.py index 14d1ffa..6f4998d 100644 --- a/menuItemsDic.py +++ b/menuItemsDic.py @@ -29,6 +29,9 @@ def getValueString(ref_id): "TC_SET_TOKEN": _("アクセストークンを設定(&T)"), "TC_MANAGE_USER": _("通知対象ユーザの管理(&M)") + "...", "TC_FILETYPES": _("録画形式の設定"), + "YDL_SUB": _("その他のサービス(&yt-dlp)"), + "YDL_DOWNLOAD": _("&URLを指定してダウンロード"), + "YDL_FILETYPES": _("録画形式の設定"), "OP_SETTINGS": _("設定(&S)") + "...", "OP_SHORTCUT": _("キーボードショートカットの設定(&K)") + "...", "OP_HOTKEY": _("グローバルホットキーの設定(&H)") + "...", diff --git a/public/history.txt b/public/history.txt index c8a4d8d..fa2a06a 100644 --- a/public/history.txt +++ b/public/history.txt @@ -1,5 +1,8 @@ Universal Live Tracking and Recording App(ULTRA) 更新履歴 +2023/07/17 Version 1.7.0 +1. 「yt-dlp」との連携機能を追加し、YouTubeなどの動画をダウンロードできるようになりました。現状、最新の動画を定期的に取得することはできませんが、再生ページのURLを入力することで、お好きな動画をダウンロードしていただけます。 + 2023/04/29 Version 1.6.0 1. Twitterスペースとの連携機能を廃止しました。Twitter APIの変更により、この機能の継続的な運用が難しくなりました。これまでご愛顧いただき、ありがとうございました。 2. ツイキャス連携機能において、[Twitterでフォローしているユーザを一括追加]を廃止しました。 diff --git a/public/readme.txt b/public/readme.txt index 8fd8a4e..84e5e2e 100644 --- a/public/readme.txt +++ b/public/readme.txt @@ -1,7 +1,7 @@ Universal Live Tracking and Recording App -ULTRA- - バージョン:  ver.1.6.0 - リリース:   2023-04-29 + バージョン:  ver.1.7.0 + リリース:   2023-07-17 開発・配布元: ACT Laboratory (https://actlab.org/) 主要開発者:  北畠一翔   ソフト種別:  オープンソースソフトウェア (GitHubリポジトリ:https://github.com/actlaboratory/ULTRA/) @@ -22,6 +22,8 @@ ULTRAはWindowsの標準的なコントロールのみを使用しており、 現時点では、以下のサービスに対応しています。 今後の更新で、更に対応サービスを追加する予定です。 ・ツイキャス(https://twitcasting.tv/) +・YouTubeなど、「yt-dlp」(https://github.com/yt-dlp/yt-dlp)がサポートしているサービスからの動画のダウンロード +※yt-dlpとの連携機能は、基本的にYouTube動画のダウンロードのために使用することを想定しています。他のサービスの動画もダウンロードを試みますが、すべてのサービスの動画を確実にダウンロードできることを保障するものではありません。 第2章 セットアップ 2.1 インストール @@ -118,6 +120,21 @@ ULTRAをコンピュータから削除する場合は、「ULTRA」フォルダ 6. [過去ライブのダウンロード]と[ログイン状態で録画]では、同じログイン情報を共有しています。そのため、一方でパスワードを入力してログインに成功すると、もう一方の機能を使用する際にパスワードを入力する必要はありません。また、セッション情報は、ULTRAをインストールしたフォルダ内の「data」フォルダにある「twitcasting」サブフォルダ内に、「session.dat」というファイル名で保存されます。このファイルを外部に流出させないよう、十分ご注意ください。 7. ULTRA Version 1.5.2の更新に伴い、[ログイン状態で録画]および[過去ライブのダウンロード]機能でTwitterアカウントにログインする際、ツイキャスのサイトで設定された「ログインパスワード」を使用するようになりました。この機能の使用前に、ツイキャスのサイトでログインパスワードを設定し、それをULTRAに入力する必要があります。なお、旧バージョンで過去にログインしたことがあり、そのセッションが利用可能な場合、手動でセッションを削除したり、何らかの理由でセッションが無効になったりしない限り、特別な操作は不要です。 +4.2 「yt-dlp」対応サービスからの動画ダウンロード機能 +4.2.1 yt-dlpとの連携機能の概要 +本ソフトと一緒にインストールされる「yt-dlp」と連携し、動画をダウンロードする機能です。 +基本的に、YouTube動画のダウンロード機能として使用することを想定しています。 +動画をダウンロードするには、メニューバーの[サービス]→[その他のサービス(yt-dlp)]→[URLを指定してダウンロード]を選択し、再生ページのURLを入力します。 +なお、動画の形式は[動画]と[音声のみ]を選択できますが、具体的なファイル形式(mp4、mp3など)は、対象の動画の種類によって変化します。 +また、保存先フォルダ・ファイル名に関して、ユーザ名の末尾に対象サービス名(例:youtube)を、ファイル名の末尾に動画のIDを、それぞれ追加します。 + +4.2.2 制限事項 +本機能の利用に関して、現状、以下の制限があります。 +・プレイリストやチャンネルなど、複数の動画を含むURLを指定してダウンロードすることができません。必ず、動画のURLを入力してください。 +・動画のダウンロードに当たっては、「yt-dlpで取得した情報を元にffmpegを呼び出す」という方法をとっています。そのため、yt-dlpの対応サービスの内、ダウンロード時に特殊な処理が必要なサービスでは、本ソフトでの動画のダウンロードに失敗する可能性があります。現時点で、「ニコニコ動画」の動画をダウンロードできない事象を確認しています。 +・本ソフトを用いてYouTubeの動画をダウンロードした際、「録画エラー」として何らかのエラーメッセージが表示されることがあります。しかし、ダウンロードに失敗しているのか、成功しているのかは、保存されたファイルを確認しなければ判断できません。そこで、同じ動画を複数回ダウンロードしてしまうことのないよう、録画エラーが発生した場合でも、エラー内容を表示するのみで、直ちに録画処理を終了します(再度録画を開始することはありません)。 +・YouTube動画のアップロード時刻を取得することができません。そのため、「アップロードされた日の0時0分0秒」として処理を行います。また、日時情報をまったく取得できない場合、ダウンロード操作を行った日時を使用します。 + 第5章 メニューリファレンス ここでは、ULTRAの各メニュー項目について解説します。 また、メニューを選択することによって表示される設定画面の内容についても記載しています。 diff --git a/recorder.py b/recorder.py index c73043e..7686d83 100644 --- a/recorder.py +++ b/recorder.py @@ -18,7 +18,7 @@ class Recorder(threading.Thread): - def __init__(self, source, stream, userName, time, movie="", *, header={}, userAgent="Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko", skipExisting=False): + def __init__(self, source, stream, userName, time, movie="", *, header={}, userAgent="Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko", skipExisting=False, ext=None): """コンストラクタ :param source: SourceBaseクラスを継承したオブジェクト。 @@ -38,12 +38,12 @@ def __init__(self, source, stream, userName, time, movie="", *, header={}, userA :param skipExisting: 保存先ファイルが存在する場合、録画処理を中断するかどうか :type skipExisting: bool """ - self.addMovieId = False - if type(time) == int: + # 配信開始日時がとれなかったことを示すフラグ + self.timeIsNone = time is None + if type(time) in (int, float): time = datetime.datetime.fromtimestamp(time) elif time is None: time = datetime.datetime.now() - self.addMovieId = True self.stream = stream self.userName = userName self.time = time @@ -53,6 +53,10 @@ def __init__(self, source, stream, userName, time, movie="", *, header={}, userA self.processHeader(header) self.userAgent = userAgent self.skipExisting = skipExisting + if ext is None: + self.ext = self.source.getFiletype() + else: + self.ext = ext super().__init__(daemon=True) self.log.info("stream URL: %s" % self.stream) @@ -73,10 +77,10 @@ def getOutputFile(self): if globalVars.app.config.getboolean("record", "createSubDir", True): lst.append(self.replaceUnusableChar(globalVars.app.config["record"]["subDirName"])) fname = self.replaceUnusableChar(globalVars.app.config["record"]["fileName"]) - if self.addMovieId: + if self.addMovieId(): fname += "(%s)" % self.movie lst.append(fname) - ext = self.source.getFiletype() + ext = self.ext path = "%s.%s" % ("\\".join(lst), ext) path = self.extractVariable(path) os.makedirs(os.path.dirname(path), exist_ok=True) @@ -144,15 +148,18 @@ def getCommand(self): "-headers", '"%s"' % self.header, ] + if self.userAgent: + cmd += [ + "-user-agent", + '"%s"' % self.userAgent, + ] cmd += [ - "-user-agent", - '"%s"' % self.userAgent, "-i", '"%s"' % self.stream, "-max_muxing_queue_size", "1024", ] - if not self.needEncode(self.source.getFiletype()): + if not self.needEncode(self.ext): cmd += [ "-c", "copy", @@ -229,9 +236,21 @@ def isRecordedByAnotherThread(self): def needEncode(self, ext): from sources.twitcasting import Twitcasting + # ツイキャスは必ずMP4 if isinstance(self.source, Twitcasting) and ext == "mp4": return False return True + + def addMovieId(self): + # 配信開始日時がとれなかった + if self.timeIsNone: + return True + # ydl + from sources.ydl import YDL + if isinstance(self.source, YDL): + return True + # 通常は不要 + return False def getRecordingUsers(self=None): """現在録画中のユーザ名のリストを返す diff --git a/requirements.txt b/requirements.txt index 0774e59..9ef4d3b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,3 +12,5 @@ https://github.com/actlaboratory/named-pipe/archive/v1.0.4.zip https://github.com/actlaboratory/diff_archiver/archive/v1.0.1.zip beautifulsoup4 lxml +yt-dlp==2023.7.6 +pillow==9 diff --git a/sources/ydl.py b/sources/ydl.py new file mode 100644 index 0000000..63dd08c --- /dev/null +++ b/sources/ydl.py @@ -0,0 +1,74 @@ +# yt-dlp module for ULTRA + +import datetime +import json +import logging +import traceback + +import yt_dlp + +import constants +import recorder +import simpleDialog +from sources.base import SourceBase + +# debug +# 0: 何もしない、1:info_jsonを保存 +DEBUG = 0 + + +class YDL(SourceBase): + name = "ydl" + friendlyName = _("その他のサービス(yt-dlp)") + index = 1 + filetypes = { + "b": _("動画"), + "ba": _("音声のみ"), + } + defaultFiletype = "b" + + def __init__(self): + super().__init__() + self.log = logging.getLogger("%s.%s" % (constants.LOG_PREFIX, "sources.ydl")) + + def download(self, url): + # yt-dlpのオプション + options = { + # 詳しいログを出す + "verbose": True, + # ダウンロードするファイル形式 + "format": self.getFiletype(), + # ログ出力 + "logger": self.log, + # プレイリストの各アイテムをダウンロードしないようにする + "extract_flat": "in_playlist", + } + try: + with yt_dlp.YoutubeDL(options) as ydl: + info = ydl.extract_info(url, False) + except Exception as e: + self.log.error(traceback.format_exc()) + simpleDialog.errorDialog(_("動画情報の取得に失敗しました。\n詳細:%s") % e) + return + # debug + if DEBUG: + with open("info_%s.json" % info["id"], "w", encoding="utf-8") as f: + json.dump(info, f, ensure_ascii=False, indent="\t") + # ビデオ以外は現状サポートしていない + _type = info.get("_type", "video") + if _type != "video": + self.log.error("unsupported: %s" % _type) + simpleDialog.errorDialog(_("%sのダウンロードは現在サポートされていません。") % _type) + return + url = info["url"] + user = "%(user)s(%(extractor)s)" % {"user": info.get("uploader", "unknown_user"), "extractor": info.get("extractor", "unknown_service")} + if "timestamp" in info.keys(): + time = info["timestamp"] + elif "upload_date" in info.keys(): + time = datetime.datetime.strptime(info["upload_date"], "%Y%m%d") + else: + time = None + id = info["id"] + headers = info.get("http_headers", {}) + r = recorder.Recorder(self, url, user, time, id, header=headers, userAgent="", ext=info["ext"]) + r.start() diff --git a/version.json b/version.json index 01e61b1..4a58391 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{"version": "1.6.0", "release_date": "2023-04-29"} \ No newline at end of file +{"version": "1.7.0", "release_date": "2023-07-17"} \ No newline at end of file diff --git a/views/main.py b/views/main.py index 3beabd1..9748534 100644 --- a/views/main.py +++ b/views/main.py @@ -106,6 +106,8 @@ def Apply(self,target): self.hServicesMenu.Bind(wx.EVT_MENU_OPEN, self.event.OnMenuOpen) self.hTwitcastingMenu=wx.Menu() self.hTwitcastingFiletypesMenu = wx.Menu() + self.hYDLMenu=wx.Menu() + self.hYDLFiletypesMenu = wx.Menu() self.hOptionMenu = wx.Menu() self.hHelpMenu=wx.Menu() @@ -117,6 +119,7 @@ def Apply(self,target): # サービスメニューの中身 self.RegisterMenuCommand(self.hServicesMenu, "TC_SUB", subMenu=self.hTwitcastingMenu) + self.RegisterMenuCommand(self.hServicesMenu, "YDL_SUB", subMenu=self.hYDLMenu) # ツイキャスメニューの中身 self.RegisterCheckMenuCommand(self.hTwitcastingMenu, "TC_ENABLE") self.RegisterCheckMenuCommand(self.hTwitcastingMenu, "TC_SAVE_COMMENTS") @@ -133,6 +136,11 @@ def Apply(self,target): "TC_MANAGE_USER", ]) self.RegisterMenuCommand(self.hTwitcastingMenu, "TC_FILETYPES", subMenu=self.hTwitcastingFiletypesMenu) + # yt-dlpメニューの中身 + self.RegisterMenuCommand(self.hYDLMenu, [ + "YDL_DOWNLOAD", + ]) + self.RegisterMenuCommand(self.hYDLMenu, "YDL_FILETYPES", subMenu=self.hYDLFiletypesMenu) # オプションメニュー self.RegisterMenuCommand(self.hOptionMenu, [ @@ -162,6 +170,8 @@ def OnMenuOpen(self, event): menu = event.GetMenu() if menu == self.parent.menu.hTwitcastingFiletypesMenu: globalVars.app.tc.getFiletypesMenu(menu) + if menu == self.parent.menu.hYDLFiletypesMenu: + globalVars.app.ydl.getFiletypesMenu(menu) def OnMenuSelect(self,event): """メニュー項目が選択されたときのイベントハンドら。""" @@ -267,6 +277,13 @@ def OnMenuSelect(self,event): globalVars.app.tc.users = d.GetValue() globalVars.app.tc.saveUserList() + # yt-dlp:URLを指定してダウンロード + if selected == menuItemsStore.getRef("YDL_DOWNLOAD"): + d = SimpleInputDialog.Dialog(_("URLを入力"), _("URLの指定")) + d.Initialize() + if d.Show() == wx.ID_CANCEL: return + globalVars.app.ydl.download(d.GetData()) + # 設定 if selected == menuItemsStore.getRef("OP_SETTINGS"): d = settingsDialog.Dialog()