From 7b5884c005996c25a1702e721dec946e02a56534 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Thu, 3 Feb 2022 14:21:41 +0900 Subject: [PATCH 01/32] Revert "Revert "Fix twitter service"" This reverts commit 1949de805654eca6d049102d7a300abb93d56704. --- twitterService.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/twitterService.py b/twitterService.py index 3080034..f257eb1 100644 --- a/twitterService.py +++ b/twitterService.py @@ -62,14 +62,14 @@ def getFollowList(token,target): try: user = twitterApi.get_user(screen_name=target) friendsCount = user.friends_count - friends = tweepy.Cursor(twitterApi.friends,screen_name=target,include_user_entities=False,skip_status=True,count=200).items() + friends = tweepy.Cursor(twitterApi.get_friends,screen_name=target,include_user_entities=False,skip_status=True,count=200).items() for friend in friends: ret.append(friend.screen_name) return ret - except tweepy.error.RateLimitError: + except tweepy.TooManyRequests: log.error("rateLimitError") return ret - except tweepy.error.TweepError as e: + except tweepy.TweepyException as e: log.error(e) log.error("%s" %(e.response)) simpleDialog.errorDialog(_("Twitterからフォローリストを取得できませんでした。指定したユーザが存在しないか、フォローしていない非公開アカウントである可能性があります。")) From e31f48ada786d65d9ea0c1ba07caca2aa1a534b2 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Thu, 3 Feb 2022 17:03:47 +0900 Subject: [PATCH 02/32] Revert "hide Twitter Spaces-related functionarity" This reverts commit 12b406cbcb92fec009010e507e7d82762ab074b4. --- .gitignore | 1 + app.py | 3 + constants.py | 1 + errorCodes.py | 4 + menuItemsDic.py | 2 + notificationHandler.py | 1 - sources/spaces.py | 176 +++++++++++++++++++++++++++++++++++++++++ views/main.py | 17 ++++ 8 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 sources/spaces.py diff --git a/.gitignore b/.gitignore index 0d32ec7..24827c7 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ users.dat output received.txt *.po~ +spaces_metadata_dumps diff --git a/app.py b/app.py index 7b648c3..800b87b 100644 --- a/app.py +++ b/app.py @@ -51,6 +51,9 @@ def initialize(self): # 「ツイキャスの監視を有効化」の設定値を確認 if self.config.getboolean("twitcasting", "enable", True) and self.tc.initialize(): self.tc.start() + from sources import spaces + self.spaces = spaces.Spaces() + self.spaces.start() self.hMainView.Show() if self.config.getboolean("general", "autoHide", False): self.hMainView.events.hide() diff --git a/constants.py b/constants.py index f641787..2aaff18 100644 --- a/constants.py +++ b/constants.py @@ -58,6 +58,7 @@ TWITTER_V1_KEY = "zAphCyhNj7ElkI5J1aXqH4Gcw" TWITTER_V1_SECRET = "Ioq2aSTDMThRopTD7LH7uA0eYzKYJgtwNe9j4vkDOOTNQoOWh4" TWITTER_PORT = 9339 +TWITTER_BEARER = "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA" # その他 diff --git a/errorCodes.py b/errorCodes.py index ea1cba7..2e7dcb9 100644 --- a/errorCodes.py +++ b/errorCodes.py @@ -13,4 +13,8 @@ UPDATER_BAD_PARAM = 400# パラメーターが不正 UPDATER_NOT_FOUND = 404# アプリケーションが存在しない RECORD_SKIPPED = 1000 +SPACE_ENDED = 2000 +CONNECTION_ERROR = 2001 +INVALID_RECEIVED = 2002 +INVALID_URL = 2003 UNKNOWN=99999 diff --git a/menuItemsDic.py b/menuItemsDic.py index e73ddf8..391b88e 100644 --- a/menuItemsDic.py +++ b/menuItemsDic.py @@ -26,6 +26,8 @@ def getValueString(ref_id): "TC_REMOVE_TOKEN": _("アクセストークンを削除"), "TC_SET_TOKEN": _("アクセストークンを設定(&T)"), "TC_MANAGE_USER": _("通知対象ユーザの管理(&M)") + "...", + "SPACES_SUB": _("Twitter スペース(&S)"), + "SPACES_URL_REC": _("スペースの&URLを指定して録画"), "OP_SETTINGS": _("設定(&S)") + "...", "OP_SHORTCUT": _("キーボードショートカットの設定(&K)") + "...", "OP_HOTKEY": _("グローバルホットキーの設定(&H)") + "...", diff --git a/notificationHandler.py b/notificationHandler.py index 8c3d2d2..94ccd14 100644 --- a/notificationHandler.py +++ b/notificationHandler.py @@ -49,7 +49,6 @@ def notify(self, source, userName, link, stream, time, config=None, movie=""): if self.baloon: b = wx.adv.NotificationMessage(constants.APP_NAME, _("配信開始:%s") %(userName)) b.Show() - b.Close() if self.sound: fxPlayer.playFx(self.soundFile) if self.openBrowser: diff --git a/sources/spaces.py b/sources/spaces.py new file mode 100644 index 0000000..ce6237e --- /dev/null +++ b/sources/spaces.py @@ -0,0 +1,176 @@ +# twitter spaces module for ULTRA + +import json +import re +import requests +import traceback + +from logging import getLogger + +import constants +import errorCodes +import globalVars +import recorder +import simpleDialog +from sources.base import SourceBase + +class Spaces(SourceBase): + name = "TwitterSpaces" + friendlyName = _("Twitter スペース") + index = 1 + + def __init__(self): + super().__init__() + self.log = getLogger("%s.%s" %(constants.LOG_PREFIX, "sources.spaces")) + self.setStatus(_("準備完了")) + self.guestToken: str + self.initialized = 0 + + def initialize(self): + result = self.getGuestToken() + if result != errorCodes.OK: + self.showError(result) + return False + self.initialized = 1 + return super().initialize() + + def getGuestToken(self): + headers = { + "authorization": constants.TWITTER_BEARER + } + try: + response = requests.post("https://api.twitter.com/1.1/guest/activate.json", headers=headers) + except Exception as e: + self.log.error(traceback.format_exc()) + return errorCodes.CONNECTION_ERROR + try: + response = response.json() + guestToken = response["guest_token"] + except Exception as e: + self.log.error(traceback.format_exc()) + return errorCodes.INVALID_RECEIVED + self.log.debug("guest_token: " + guestToken) + self.guestToken = guestToken + return errorCodes.OK + + def run(self): + if self.initialized == 0 and not self.initialize(): + return + + def recFromUrl(self, url): + spaceId = self.getSpaceIdFromUrl(url) + if spaceId is None: + self.log.error("Space ID not found: " + url) + return errorCodes.INVALID_URL + metadata = self.getMetadata(spaceId) + if type(metadata) == int: + self.showError(metadata) + return + if metadata.isEnded(): + self.log.debug("is ended: " + metadata) + return errorCodes.SPACE_ENDED + mediaKey = metadata.getMediaKey() + if mediaKey == errorCodes.INVALID_RECEIVED: + self.log.error("Media key not found: " + metadata) + self.showError(errorCodes.INVALID_RECEIVED) + return + location = self.getMediaLocation(mediaKey) + if type(location) == int: + self.showError(location) + return + r = recorder.Recorder(self, location, metadata.getUserName(), metadata.getStartedTime(), metadata.getSpaceId()) + r.start() + + def getSpaceIdFromUrl(self, url): + ret = re.search(r"(?<=spaces/)\w*", url) + if not ret: + return None + return ret.group(0) + + def getMetadata(self, spaceId): + params = { + "variables": json.dumps({ + "id": spaceId, + "isMetatagsQuery": False, + "withSuperFollowsUserFields": True, + "withUserResults": True, + "withBirdwatchPivots": False, + "withReactionsMetadata": False, + "withReactionsPerspective": False, + "withSuperFollowsTweetFields": True, + "withReplays": True, + "withScheduledSpaces": True + }) + } + headers = { + "authorization": constants.TWITTER_BEARER, + "x-guest-token": self.guestToken, + } + try: + response = requests.get("https://twitter.com/i/api/graphql/jyQ0_DEMZHeoluCgHJ-U5Q/AudioSpaceById", params=params, headers=headers) + except Exception as e: + self.log.error(traceback.format_exc()) + return errorCodes.CONNECTION_ERROR + try: + metadata = response.json() + except Exception as e: + self.log.error(traceback.format_exc()) + return errorCodes.INVALID_RECEIVED + return Metadata(metadata) + + def getMediaLocation(self, mediaKey): + headers = { + "authorization": constants.TWITTER_BEARER, + "cookie": "auth_token=" + } + try: + response = requests.get("https://twitter.com/i/api/1.1/live_video_stream/status/" + mediaKey, headers=headers) + except Exception as e: + self.log.error(traceback.format_exc()) + return errorCodes.CONNECTION_ERROR + try: + data = response.json() + return data["source"]["location"] + except Exception as e: + self.log.error(traceback.format_exc()) + return errorCodes.INVALID_RECEIVED + + def showError(self, code): + if code == errorCodes.CONNECTION_ERROR: + simpleDialog.errorDialog(_("Twitterとの接続に失敗しました。インターネット接続に問題がない場合は、しばらくたってから再度お試しください。この問題が再度発生する場合は、開発者までお問い合わせください。")) + elif code == errorCodes.INVALID_RECEIVED: + simpleDialog.errorDialog(_("Twitterからの応答が不正です。開発者までご連絡ください。")) + +class Metadata: + # デバッグ用に、メタデータをファイルに書き出す + debug = False + def __init__(self, metadata): + self._metadata = metadata + if self.debug and not globalVars.app.GetFrozenStatus(): + import datetime + import os + if not os.path.exists("spaces_metadata_dumps"): + os.mkdir("spaces_metadata_dumps") + with open(os.path.join("spaces_metadata_dumps", datetime.datetime.now().strftime("%Y%m%d_%H%M%S.txt")), "w", encoding="utf-8") as f: + json.dump(self._metadata, f, ensure_ascii=False, indent="\t") + + def getMediaKey(self): + try: + return self._metadata["data"]["audioSpace"]["metadata"]["media_key"] + except KeyError as e: + return errorCodes.INVALID_RECEIVED + + def getUserName(self): + return self._metadata["data"]["audioSpace"]["metadata"]["creator_results"]["result"]["legacy"]["screen_name"] + + def getStartedTime(self): + return int(self._metadata["data"]["audioSpace"]["metadata"]["started_at"] / 1000) + + def getSpaceId(self): + return self._metadata["data"]["audioSpace"]["metadata"]["rest_id"] + + def isEnded(self): + return self._metadata["data"]["audioSpace"]["metadata"]["state"] == "Ended" + + def __str__(self): + return json.dumps(self._metadata, ensure_ascii=False, indent=None) diff --git a/views/main.py b/views/main.py index 46438a9..170d34d 100644 --- a/views/main.py +++ b/views/main.py @@ -100,6 +100,7 @@ def Apply(self,target): self.hFileMenu=wx.Menu() self.hServicesMenu = wx.Menu() self.hTwitcastingMenu=wx.Menu() + self.hSpacesMenu=wx.Menu() self.hOptionMenu = wx.Menu() self.hHelpMenu=wx.Menu() @@ -111,6 +112,7 @@ def Apply(self,target): # サービスメニューの中身 self.RegisterMenuCommand(self.hServicesMenu, "TC_SUB", subMenu=self.hTwitcastingMenu) + self.RegisterMenuCommand(self.hServicesMenu, "SPACES_SUB", subMenu=self.hSpacesMenu) # ツイキャスメニューの中身 self.RegisterCheckMenuCommand(self.hTwitcastingMenu, "TC_ENABLE") self.RegisterCheckMenuCommand(self.hTwitcastingMenu, "TC_SAVE_COMMENTS") @@ -124,6 +126,10 @@ def Apply(self,target): "TC_SET_TOKEN", "TC_MANAGE_USER", ]) + # スペースメニューの中身 + self.RegisterMenuCommand(self.hSpacesMenu, [ + "SPACES_URL_REC", + ]) # オプションメニュー self.RegisterMenuCommand(self.hOptionMenu, [ @@ -245,6 +251,17 @@ def OnMenuSelect(self,event): globalVars.app.tc.users = d.GetValue() globalVars.app.tc.saveUserList() + # スペース:URLを指定して録画 + if selected == menuItemsStore.getRef("SPACES_URL_REC"): + d = SimpleInputDialog.Dialog(_("URLを入力"), _("スペースのURL")) + d.Initialize() + if d.Show() == wx.ID_CANCEL: return + ret = globalVars.app.spaces.recFromUrl(d.GetData()) + if ret == errorCodes.SPACE_ENDED: + errorDialog(_("このスペースは既に終了しています。")) + elif ret == errorCodes.INVALID_URL: + errorDialog(_("入力されたURLが正しくありません。")) + # 設定 if selected == menuItemsStore.getRef("OP_SETTINGS"): d = settingsDialog.Dialog() From 8a9bdfa5d4788b26f64080d19a76b86d97cd993b Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Thu, 3 Feb 2022 18:48:15 +0900 Subject: [PATCH 03/32] =?UTF-8?q?=E9=87=8D=E8=A4=87=E9=8C=B2=E7=94=BB?= =?UTF-8?q?=E5=9B=9E=E9=81=BF=E3=81=AE=E4=BB=95=E7=B5=84=E3=81=BF=E3=82=92?= =?UTF-8?q?=E6=94=B9=E5=96=84=20fixed=20#28?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- recorder.py | 43 ++++++++++++++++++++++++------------------ sources/twitcasting.py | 8 ++++---- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/recorder.py b/recorder.py index ba0dd76..eeb090e 100644 --- a/recorder.py +++ b/recorder.py @@ -11,13 +11,14 @@ import globalVars import os from logging import getLogger - + # debug # 0:何もしない、1:ffmpegのログをカレントディレクトリに保存 DEBUG = 0 + 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): """コンストラクタ :param source: SourceBaseクラスを継承したオブジェクト。 @@ -36,41 +37,41 @@ def __init__(self, source, stream, userName, time, movie="",*,header="",userAgen :type userAgent: str :param skipExisting: 保存先ファイルが存在する場合、録画処理を中断するかどうか :type skipExisting: bool - """ + """ if type(time) == int: time = datetime.datetime.fromtimestamp(time) self.stream = stream self.userName = userName self.time = time - self.log = getLogger("%s.%s" %(constants.LOG_PREFIX, "recorder")) + self.log = getLogger("%s.%s" % (constants.LOG_PREFIX, "recorder")) self.source = source self.movie = movie - self.header=header + self.header = header self.userAgent = userAgent self.skipExisting = skipExisting super().__init__(daemon=True) - self.log.info("stream URL: %s" %self.stream) + self.log.info("stream URL: %s" % self.stream) def getOutputFile(self): """設定値を元に、出力ファイルのパスを取得 - """ + """ lst = [] lst.append(self.replaceUnusableChar(globalVars.app.config["record"]["dir"])) if globalVars.app.config.getboolean("record", "createSubDir", True): lst.append(self.replaceUnusableChar(globalVars.app.config["record"]["subDirName"])) lst.append(self.replaceUnusableChar(globalVars.app.config["record"]["fileName"])) ext = globalVars.app.config.getstring("record", "extension", "ts", constants.SUPPORTED_FILETYPE) - path = "%s.%s" %("\\".join(lst), ext) + path = "%s.%s" % ("\\".join(lst), ext) path = self.extractVariable(path) os.makedirs(os.path.dirname(path), exist_ok=True) path = os.path.abspath(path) if os.path.exists(path) and not self.skipExisting: count = 1 base = os.path.splitext(path)[0] - tmp = "%s (%i).%s" %(base, count, ext) + tmp = "%s (%i).%s" % (base, count, ext) while os.path.exists(tmp): count += 1 - tmp = "%s (%i).%s" %(base, count, ext) + tmp = "%s (%i).%s" % (base, count, ext) path = tmp self.path = path return path @@ -144,7 +145,7 @@ def run(self): except IOError: d = simpleDialog.yesNoDialog(_("録画エラー"), _("録画の開始に失敗しました。録画の保存先が適切に設定されていることを確認してください。定期的に再試行する場合は[はい]、処理を中断する場合は[いいえ]を選択してください。[はい]を選択して録画の保存先を変更することで、正しく録画を開始できる場合があります。")) if d == wx.ID_NO: - globalVars.app.hMainView.addLog(_("録画エラー"), _("%sのライブの録画処理を中断しました。") %self.userName) + globalVars.app.hMainView.addLog(_("録画エラー"), _("%sのライブの録画処理を中断しました。") % self.userName) return max = 30 for i in range(max): @@ -152,17 +153,17 @@ def run(self): cmd = self.getCommand() break except IOError: - self.log.info("#%i failed." %i) + self.log.info("#%i failed." % i) sleep(30) if i + 1 == max: - globalVars.app.hMainView.addLog(_("録画エラー"), _("%sのライブの録画処理を中断しました。") %self.userName) + globalVars.app.hMainView.addLog(_("録画エラー"), _("%sのライブの録画処理を中断しました。") % self.userName) return self.source.onRecord(self.path, self.movie) - globalVars.app.hMainView.addLog(_("録画開始"), _("ユーザ:%(user)s、ムービーID:%(movie)s") %{"user": self.userName, "movie": self.movie}, self.source.friendlyName) + globalVars.app.hMainView.addLog(_("録画開始"), _("ユーザ:%(user)s、ムービーID:%(movie)s") % {"user": self.userName, "movie": self.movie}, self.source.friendlyName) globalVars.app.tb.setAlternateText(_("録画中")) self.log.debug("command: " + " ".join(cmd)) result = subprocess.run(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, shell=True, encoding="utf-8") - self.log.info("saved: %s" %self.path) + self.log.info("saved: %s" % self.path) while len(result.stdout) > 0: self.log.info("FFMPEG returned some errors.\n" + result.stdout) if not self.source.onRecordError(self.movie): @@ -171,19 +172,19 @@ def run(self): if "404 Not Found" in result.stdout: self.log.info("not found") break - globalVars.app.hMainView.addLog(_("録画エラー"), (_("%sのライブを録画中にエラーが発生したため、再度録画を開始します。") %self.userName) + (_("詳細:%s") %result.stdout), self.source.friendlyName) + globalVars.app.hMainView.addLog(_("録画エラー"), (_("%sのライブを録画中にエラーが発生したため、再度録画を開始します。") % self.userName) + (_("詳細:%s") % result.stdout), self.source.friendlyName) sleep(15) cmd = self.getCommand() self.source.onRecord(self.path, self.movie) result = subprocess.run(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, shell=True, encoding="utf-8") - self.log.info("saved: %s" %self.path) + self.log.info("saved: %s" % self.path) if not self.source.onRecordError(self.movie): self.log.info("End of recording") break if "404 Not Found" in result.stdout: self.log.info("not found") break - globalVars.app.hMainView.addLog(_("録画終了"), _("ユーザ:%(user)s、ムービーID:%(movie)s") %{"user": self.userName, "movie": self.movie}, self.source.friendlyName) + globalVars.app.hMainView.addLog(_("録画終了"), _("ユーザ:%(user)s、ムービーID:%(movie)s") % {"user": self.userName, "movie": self.movie}, self.source.friendlyName) if getRecordingUsers(self) == []: globalVars.app.tb.setAlternateText() @@ -195,6 +196,11 @@ def getTargetUser(self): """ return self.userName + def isRecordedByAnotherThread(self): + for i in getActiveObj(self): + if i.stream == self.stream: + return True + return False def getRecordingUsers(self=None): """現在録画中のユーザ名のリストを返す @@ -205,6 +211,7 @@ def getRecordingUsers(self=None): ret.append(i.getTargetUser()) return ret + def getActiveObj(self=None): """現在動作中のレコーダーオブジェクトのリストを返す """ diff --git a/sources/twitcasting.py b/sources/twitcasting.py index 3c68ae8..271ce37 100644 --- a/sources/twitcasting.py +++ b/sources/twitcasting.py @@ -608,18 +608,18 @@ def record(self, userName): if userInfo == None: return if userInfo["user"]["id"] in self.users.keys(): - if userInfo["user"]["screen_id"] not in recorder.getRecordingUsers() and userInfo["user"]["is_live"]: + if userInfo["user"]["is_live"]: movie = self.getCurrentLive(userInfo["user"]["screen_id"]) if movie == None: return r = recorder.Recorder(self, movie["movie"]["hls_url"], movie["broadcaster"]["screen_id"], movie["movie"]["created"], movie["movie"]["id"]) + if r.isRecordedByAnotherThread(): + simpleDialog.errorDialog(_("このユーザのライブはすでに録画中です。")) + return r.start() return simpleDialog.errorDialog(_("このユーザはすでに登録されています。")) return - if userInfo["user"]["screen_id"] in recorder.getRecordingUsers(): - simpleDialog.errorDialog(_("このユーザのライブはすでに録画中です。")) - return self.users[userInfo["user"]["id"]] = { "user": userInfo["user"]["screen_id"], "name": userInfo["user"]["name"], From f22cd29ad1a1346f57903ca62b93496e83254928 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Fri, 4 Feb 2022 11:54:02 +0900 Subject: [PATCH 04/32] update requirements --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index deb53cf..3e05060 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,4 +10,4 @@ https://github.com/actlaboratory/implicitGrantManager/archive/v1.3.0.zip https://github.com/actlaboratory/soundPlayer/archive/0.3.1.zip https://github.com/actlaboratory/named-pipe/archive/v1.0.4.zip https://github.com/actlaboratory/diff_archiver/archive/v1.0.1.zip -https://github.com/actlaboratory/twitter-authorization/archive/refs/tags/1.0.2.zip +https://github.com/actlaboratory/twitter-authorization/archive/refs/tags/1.0.3.zip From 7fd718045af39226faefdde298b4c7847dbd1022 Mon Sep 17 00:00:00 2001 From: kitabatake1013 <62418605+kitabatake1013@users.noreply.github.com> Date: Wed, 9 Feb 2022 20:43:46 +0900 Subject: [PATCH 05/32] =?UTF-8?q?spaces=E5=AF=BE=E5=BF=9C=20(#30)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DefaultSettings.py | 3 + app.py | 3 +- constants.py | 14 +- data/spaces/.dummy | 0 errorCodes.py | 26 +- menuItemsDic.py | 5 + public/data/spaces/.dummy | 0 requirements.txt | 2 +- sources/base.py | 11 + sources/spaces.py | 534 +++++++++++++++++++++++++++++++++- sources/twitcasting.py | 3 +- twitterService.py | 4 +- views/chooseTwitterAccount.py | 35 +++ views/main.py | 42 +++ views/spacesManageUser.py | 164 +++++++++++ views/spacesTokenManager.py | 138 +++++++++ 16 files changed, 957 insertions(+), 27 deletions(-) create mode 100644 data/spaces/.dummy create mode 100644 public/data/spaces/.dummy create mode 100644 views/chooseTwitterAccount.py create mode 100644 views/spacesManageUser.py create mode 100644 views/spacesTokenManager.py diff --git a/DefaultSettings.py b/DefaultSettings.py index e6344d8..aaed8b9 100644 --- a/DefaultSettings.py +++ b/DefaultSettings.py @@ -36,6 +36,9 @@ def get(): "enable": False, "saveComments": False, } + config["spaces"] = { + "enable": False, + } config["record"] = { "dir": "output\\%source%", "createSubDir": True, diff --git a/app.py b/app.py index 800b87b..5cae1ea 100644 --- a/app.py +++ b/app.py @@ -53,7 +53,8 @@ def initialize(self): self.tc.start() from sources import spaces self.spaces = spaces.Spaces() - self.spaces.start() + if self.config.getboolean("spaces", "enable", False) and self.spaces.initialize(): + self.spaces.start() self.hMainView.Show() if self.config.getboolean("general", "autoHide", False): self.hMainView.events.hide() diff --git a/constants.py b/constants.py index 2aaff18..9c9a6ed 100644 --- a/constants.py +++ b/constants.py @@ -26,9 +26,11 @@ SETTING_FILE_NAME="data\\settings.ini" KEYMAP_FILE_NAME="data\\keymap.ini" TC_USER_DATA = os.path.abspath("data\\twitcasting\\users.dat") +SPACES_USER_DATA = os.path.abspath("data\\spaces\\users.dat") FFMPEG_PATH = os.path.abspath("bin\\ffmpeg.exe") # 各サービスのアカウントデータの格納場所 AC_TWITCASTING = os.path.abspath("data\\twitcasting\\account.bin") +AC_SPACES = os.path.abspath("data\\spaces\\account.bin") #フォントの設定可能サイズ範囲 FONT_MIN_SIZE=5 FONT_MAX_SIZE=35 @@ -55,10 +57,18 @@ TC_URL = "https://apiv2.twitcasting.tv/oauth2/authorize" TC_PORT = 9339 # Twitter関係 -TWITTER_V1_KEY = "zAphCyhNj7ElkI5J1aXqH4Gcw" -TWITTER_V1_SECRET = "Ioq2aSTDMThRopTD7LH7uA0eYzKYJgtwNe9j4vkDOOTNQoOWh4" +TWITTER_CLIENT_ID = "bDVKekZERkMtNXNVMlFoWnNONWY6MTpjaQ" +TWITTER_CONSUMER_KEY = "zAphCyhNj7ElkI5J1aXqH4Gcw" +TWITTER_CONSUMER_SECRET = "Ioq2aSTDMThRopTD7LH7uA0eYzKYJgtwNe9j4vkDOOTNQoOWh4" TWITTER_PORT = 9339 TWITTER_BEARER = "Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA" +TWITTER_SCOPE = [ + "tweet.read", + "users.read", + "follows.read", + "offline.access", + "space.read", +] # その他 diff --git a/data/spaces/.dummy b/data/spaces/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/errorCodes.py b/errorCodes.py index 2e7dcb9..464630e 100644 --- a/errorCodes.py +++ b/errorCodes.py @@ -1,20 +1,22 @@ # -*- coding: utf-8 -*- -#error codes +# error codes -OK=0 #成功(エラーなし) -NOT_SUPPORTED=1 #サポートされていない呼び出し -FILE_NOT_FOUND=2 -PARSING_FAILED=3 -ACCESS_DENIED=4 +OK = 0 # 成功(エラーなし) +NOT_SUPPORTED = 1 # サポートされていない呼び出し +FILE_NOT_FOUND = 2 +PARSING_FAILED = 3 +ACCESS_DENIED = 4 CONNECT_TIMEOUT = 12 -UPDATER_NEED_UPDATE = 200# アップデートが必要 -UPDATER_LATEST = 204# アップデートが無い +UPDATER_NEED_UPDATE = 200 # アップデートが必要 +UPDATER_LATEST = 204 # アップデートが無い UPDATER_VISIT_SITE = 205 -UPDATER_BAD_PARAM = 400# パラメーターが不正 -UPDATER_NOT_FOUND = 404# アプリケーションが存在しない +UPDATER_BAD_PARAM = 400 # パラメーターが不正 +UPDATER_NOT_FOUND = 404 # アプリケーションが存在しない RECORD_SKIPPED = 1000 SPACE_ENDED = 2000 CONNECTION_ERROR = 2001 INVALID_RECEIVED = 2002 -INVALID_URL = 2003 -UNKNOWN=99999 +SPACE_NOT_STARTED = 2003 +INVALID_URL = 2004 +SHOULD_EXIT = 2005 +UNKNOWN = 99999 diff --git a/menuItemsDic.py b/menuItemsDic.py index 391b88e..8f7d94f 100644 --- a/menuItemsDic.py +++ b/menuItemsDic.py @@ -27,7 +27,12 @@ def getValueString(ref_id): "TC_SET_TOKEN": _("アクセストークンを設定(&T)"), "TC_MANAGE_USER": _("通知対象ユーザの管理(&M)") + "...", "SPACES_SUB": _("Twitter スペース(&S)"), + "SPACES_ENABLE": _("Twitter スペースとの連携機能を有効化(&E)"), + "SPACES_ADD_FOLLOWING": _("フォロー中のユーザをすべて追加(&F)") + "...", + "SPACES_UPDATE_USER": _("ユーザ情報の更新(&I)"), "SPACES_URL_REC": _("スペースの&URLを指定して録画"), + "SPACES_TOKEN_MANAGER": _("連携アカウントの管理(&A)") + "...", + "SPACES_MANAGE_USER": _("通知対象ユーザの管理(&M)") + "...", "OP_SETTINGS": _("設定(&S)") + "...", "OP_SHORTCUT": _("キーボードショートカットの設定(&K)") + "...", "OP_HOTKEY": _("グローバルホットキーの設定(&H)") + "...", diff --git a/public/data/spaces/.dummy b/public/data/spaces/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/requirements.txt b/requirements.txt index 3e05060..a5558fe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,4 +10,4 @@ https://github.com/actlaboratory/implicitGrantManager/archive/v1.3.0.zip https://github.com/actlaboratory/soundPlayer/archive/0.3.1.zip https://github.com/actlaboratory/named-pipe/archive/v1.0.4.zip https://github.com/actlaboratory/diff_archiver/archive/v1.0.1.zip -https://github.com/actlaboratory/twitter-authorization/archive/refs/tags/1.0.3.zip +https://github.com/actlaboratory/twitter-authorization/archive/refs/tags/2.0.1.zip diff --git a/sources/base.py b/sources/base.py index 2729b3d..a643195 100644 --- a/sources/base.py +++ b/sources/base.py @@ -60,3 +60,14 @@ def onRecordError(self, movie): :rtype: bool """ return False + + def getActiveSourceCount(self, includeSelf=False): + count = 0 + t = threading.enumerate() + for i in t: + if isinstance(i, SourceBase): + if i != self: + count += 1 + elif includeSelf: + count += 1 + return count diff --git a/sources/spaces.py b/sources/spaces.py index ce6237e..3535748 100644 --- a/sources/spaces.py +++ b/sources/spaces.py @@ -1,9 +1,17 @@ # twitter spaces module for ULTRA import json +import os +import pickle import re import requests +import sys +import time import traceback +import tweepy +import twitterAuthorization +import webbrowser +import wx from logging import getLogger @@ -12,28 +20,109 @@ import globalVars import recorder import simpleDialog -from sources.base import SourceBase +import sources.base +import views.auth -class Spaces(SourceBase): - name = "TwitterSpaces" + +interval = 15 + + +class Spaces(sources.base.SourceBase): + name = "Spaces" friendlyName = _("Twitter スペース") index = 1 def __init__(self): super().__init__() - self.log = getLogger("%s.%s" %(constants.LOG_PREFIX, "sources.spaces")) - self.setStatus(_("準備完了")) + self.log = getLogger("%s.%s" % (constants.LOG_PREFIX, "sources.spaces")) + self.setStatus(_("未接続")) self.guestToken: str self.initialized = 0 + self.users = UserList() + self.running = False + self.shouldExit = False + self.notified = [] + self.enableMenu(False) + self.tokenManager = TokenManager() + self._tokenManagerShown = False def initialize(self): + if self.initialized == 1: + self.initThread() result = self.getGuestToken() if result != errorCodes.OK: self.showError(result) return False + if not os.path.exists(constants.AC_SPACES): + d = simpleDialog.yesNoDialog(_("Twitterアカウントの連携"), _("Twitterスペースを使用する前に、使用するTwitterアカウントを設定する必要があります。今すぐ設定画面を開きますか?")) + if d == wx.ID_NO: + return False + if self.openTokenManager() == errorCodes.SHOULD_EXIT: + return False + if not self.tokenManager.load() or not self.tokenManager.hasDefaultAccount(): + d = simpleDialog.yesNoDialog(_("Twitterアカウントの連携"), _("Twitterアカウント情報の読み込みに失敗しました。再度アカウントの連携を行ってください。今すぐ設定画面を開きますか?")) + if d == wx.ID_NO: + return False + if self.openTokenManager() == errorCodes.SHOULD_EXIT: + return False self.initialized = 1 + self.enableMenu(True) return super().initialize() + def openTokenManager(self): + from views import spacesTokenManager + d = spacesTokenManager.Dialog() + d.Initialize() + self._tokenManagerShown = True + d.Show() + self._tokenManagerShown = False + if d.shouldExit(): + self.shouldExit = True + return errorCodes.SHOULD_EXIT + + def addFollowingUsers(self): + from views import chooseTwitterAccount + d = chooseTwitterAccount.Dialog(self.tokenManager) + d.Initialize() + if d.Show() == wx.ID_CANCEL: + return + user = d.GetData() + users = self.getFollowingUsers(user, user) + count = 0 + for i in users: + if str(i.id) in self.users.getUserIds(): + continue + count += 1 + self.users.addUser(str(i.id), i.username, i.name, i.protected) + simpleDialog.dialog(_("完了"), _("フォロー中のユーザの追加が完了しました。追加件数:%d") % count) + + def getFollowingUsers(self, user, account): + self.log.debug("Getting follow list...") + ret = [] + pagination = "" + while True: + try: + client = self.tokenManager.getClient(account) + if pagination: + response = client.get_users_following(user, user_fields="protected", max_results=1000, pagination_token=pagination) + else: + response = client.get_users_following(user, user_fields="protected", max_results=1000) + except tweepy.Unauthorized as e: + self.showTokenError() + return [] + except Exception as e: + self.log.error(traceback.format_exc()) + simpleDialog.errorDialog(_("フォロー中のユーザの取得に失敗しました。詳細:%s") % e) + return [] + self.log.debug(response) + if response.data: + ret += response.data + meta = response.meta + if "next_token" not in meta.keys(): + break + pagination = meta["next_token"] + return ret + def getGuestToken(self): headers = { "authorization": constants.TWITTER_BEARER @@ -56,6 +145,136 @@ def getGuestToken(self): def run(self): if self.initialized == 0 and not self.initialize(): return + self.running = True + globalVars.app.hMainView.addLog(_("接続完了"), _("スペースの監視を開始しました。"), self.friendlyName) + globalVars.app.hMainView.menu.CheckMenu("SPACES_ENABLE", True) + globalVars.app.hMainView.menu.EnableMenu("HIDE") + self.setStatus(_("接続済み")) + self.enableMenu(True) + self.log.debug("shouldExit: %s" % self.shouldExit) + while not self.shouldExit: + self._process() + time.sleep(interval) + wx.YieldIfNeeded() + + def updateUser(self): + self.log.debug("Updating user info...") + ids = self.users.getUserIds() + for i in self.splitIds(ids): + try: + result = self.tokenManager.getClient().get_users(ids=i, user_fields="protected") + except tweepy.Unauthorized as e: + self.showTokenError() + return + except Exception as e: + self.log.error(traceback.format_exc()) + simpleDialog.errorDialog(_("ユーザ情報の更新に失敗しました。詳細:%s") % e) + return + if result.errors: + simpleDialog.errorDialog(_("ユーザ情報の更新に失敗しました。詳細:%s") % result.errors) + return + data = result.data + if data: + for j in data: + self._updateUserInfo(j) + simpleDialog.dialog(_("完了"), _("ユーザ情報の更新が完了しました。")) + + def _process(self): + self.log.debug("shouldExit: %s" % self.shouldExit) + self.log.debug("Checking for space status...") + users = self.users.getUserIds() + for i in self.splitIds(users): + self.checkSpaceStatus(i) + protected = self.users.getProtectedUsers() + if not protected: + return + self.log.debug("Checking for protected accounts") + self.log.debug("protected users: %d" % len(protected)) + accounts = self.tokenManager.getOtherAccount() + self.log.debug("Other accounts: %d" % len(accounts)) + for i in accounts: + self.log.debug("Checking by account %s" % i) + for j in self.splitIds(protected): + self.checkSpaceStatus(j, i) + + def enableMenu(self, mode): + spaces = ( + "SPACES_ADD_FOLLOWING", + "SPACES_URL_REC", + "SPACES_UPDATE_USER", + "SPACES_TOKEN_MANAGER", + "SPACES_MANAGE_USER", + ) + for i in spaces: + globalVars.app.hMainView.menu.EnableMenu(i, mode) + + def exit(self): + self.shouldExit = True + self.running = False + if self.getActiveSourceCount() == 0: + globalVars.app.hMainView.menu.EnableMenu("HIDE", False) + globalVars.app.hMainView.addLog(_("切断"), _("Twitterとの接続を切断しました。"), self.friendlyName) + globalVars.app.hMainView.menu.CheckMenu("SPACES_ENABLE", False) + self.setStatus(_("未接続")) + self.enableMenu(False) + + def checkSpaceStatus(self, users, account=None): + if account is None: + account = self.tokenManager.getDefaultAccount() + self.log.debug("Checking spaces status...") + user_ids = ",".join(users) + expansions = [ + "creator_id", + ] + space_fields = [ + "creator_id", + ] + user_fields = [ + "username", + "protected", + "name", + ] + try: + ret = self.tokenManager.getClient(account).get_spaces(user_ids=user_ids, expansions=expansions, space_fields=space_fields, user_fields=user_fields) + except tweepy.Unauthorized as e: + self.showTokenError() + return + except Exception as e: + self.log.error(traceback.format_exc()) + if not self._tokenManagerShown: + globalVars.app.hMainView.addLog(_("Twitter エラー"), _("Twitterとの通信中にエラーが発生しました。詳細:%s") % e, self.friendlyName) + return + self.log.debug(ret) + if ret.data: + for d in ret.data: + u = [i for i in ret.includes["users"] if i.id == int(d.creator_id)][0] + self._updateUserInfo(u) + if d.id in self.notified: + continue + metadata = self.getMetadata(d.id) + if metadata.isRunning(): + globalVars.app.notificationHandler.notify(self, u.username, "https://twitter.com/i/spaces/%s" % d.id, self.getMediaLocation(metadata.getMediaKey()), metadata.getStartedTime(), self.users.getConfig(str(u.id)), d.id) + self.notified.append(d.id) + + def _updateUserInfo(self, u): + prev = self.users.getUserData(str(u.id)) + if u.username != prev["user"]: + self.users.setAttribute(str(u.id), "user", u.username) + if u.name != prev["name"]: + self.users.setAttribute(str(u.id), "name", u.name) + if u.protected != prev["protected"]: + self.users.setAttribute(str(u.id), "protected", u.protected) + + def splitIds(self, ids, count=100): + ids = list(ids) + ret = [] + for i in range(0, len(ids), count): + ret.append(ids[i:i+count]) + return ret + + def onRecordError(self, movie): + metadata = self.getMetadata(movie) + return metadata.isRunning() def recFromUrl(self, url): spaceId = self.getSpaceIdFromUrl(url) @@ -66,9 +285,13 @@ def recFromUrl(self, url): if type(metadata) == int: self.showError(metadata) return - if metadata.isEnded(): - self.log.debug("is ended: " + metadata) - return errorCodes.SPACE_ENDED + if not metadata.isRunning(): + if metadata.isEnded(): + self.log.debug("is ended: " + metadata) + return errorCodes.SPACE_ENDED + if metadata.isNotStarted(): + self.log.debug("is not started: " + metadata) + return errorCodes.SPACE_NOT_STARTED mediaKey = metadata.getMediaKey() if mediaKey == errorCodes.INVALID_RECEIVED: self.log.error("Media key not found: " + metadata) @@ -141,9 +364,41 @@ def showError(self, code): elif code == errorCodes.INVALID_RECEIVED: simpleDialog.errorDialog(_("Twitterからの応答が不正です。開発者までご連絡ください。")) + def showTokenError(self): + self.log.error("unauthorized") + if self._tokenManagerShown: + return + d = simpleDialog.yesNoDialog(_("Twitterアカウントの連携"), _("Twitterアカウントの認証情報が正しくありません。再度アカウントの連携を行ってください。今すぐ設定画面を開きますか?")) + if d == wx.ID_NO: + return + wx.CallAfter(self.openTokenManager) + if self.shouldExit: + self.exit() + + def getUser(self, user, showNotFound=True): + try: + ret = self.tokenManager.getClient().get_user(username=user, user_fields="protected", user_auth=True) + self.log.debug(ret) + except tweepy.Unauthorized as e: + self.showTokenError() + return + except Exception as e: + self.log.error(traceback.format_exc()) + if showNotFound: + simpleDialog.errorDialog(_("Twitterとの通信に失敗しました。詳細:%s") % e) + return + if ret.errors: + self.log.error(ret) + if showNotFound: + simpleDialog.errorDialog(_("ユーザ情報の取得に失敗しました。詳細:%s") % ret.errors[0]["detail"]) + return + return ret.data + + class Metadata: # デバッグ用に、メタデータをファイルに書き出す debug = False + def __init__(self, metadata): self._metadata = metadata if self.debug and not globalVars.app.GetFrozenStatus(): @@ -163,6 +418,9 @@ def getMediaKey(self): def getUserName(self): return self._metadata["data"]["audioSpace"]["metadata"]["creator_results"]["result"]["legacy"]["screen_name"] + def getUserId(self): + return self._metadata["data"]["audioSpace"]["metadata"]["creator_results"]["result"]["rest_id"] + def getStartedTime(self): return int(self._metadata["data"]["audioSpace"]["metadata"]["started_at"] / 1000) @@ -172,5 +430,265 @@ def getSpaceId(self): def isEnded(self): return self._metadata["data"]["audioSpace"]["metadata"]["state"] == "Ended" + def isRunning(self): + return self._metadata["data"]["audioSpace"]["metadata"]["state"] == "Running" + + def isNotStarted(self): + return self._metadata["data"]["audioSpace"]["metadata"]["state"] == "NotStarted" + def __str__(self): return json.dumps(self._metadata, ensure_ascii=False, indent=None) + + +class TokenManager: + def __init__(self): + self._file = constants.AC_SPACES + self._data = {} + self.log = getLogger("%s.%s" % (constants.LOG_PREFIX, "sources.spaces.tokenManager")) + + def _getManager(self): + return twitterAuthorization.TwitterAuthorization2(constants.TWITTER_CLIENT_ID, constants.TWITTER_PORT, constants.TWITTER_SCOPE) + + def addUser(self): + token = self._getToken() + if token is None: + return False + self.save() + return True + + def deleteUser(self, user): + try: + del self._data[user] + except Exception as e: + self.log.error(traceback.format_exc()) + return False + self.save() + return True + + def load(self): + try: + with open(self._file, "rb") as f: + self._data = pickle.load(f) + except Exception as e: + self.log.error(traceback.format_exc()) + return False + return True + + def save(self): + try: + with open(self._file, "wb") as f: + pickle.dump(self._data, f) + except Exception as e: + self.log.error(traceback.format_exc()) + simpleDialog.errorDialog(_("認証情報の保存に失敗しました。")) + return False + return True + + def _getToken(self): + manager = self._getManager() + l = "ja" + try: + l = globalVars.app.config["general"]["language"].split("_")[0].lower() + except: + pass # end うまく読めなかったら ja を採用 + # end except + manager.setMessage( + lang=l, + success=_("認証に成功しました。このウィンドウを閉じて、アプリケーションに戻ってください。"), + failed=_("認証に失敗しました。もう一度お試しください。"), + transfer=_("しばらくしても画面が切り替わらない場合は、別のブラウザでお試しください。") + ) + webbrowser.open(manager.getUrl()) + d = views.auth.waitingDialog() + d.Initialize(_("Twitterアカウント認証")) + d.Show(False) + self.log.debug("start authorization") + while True: + time.sleep(0.01) + wx.YieldIfNeeded() + if manager.getToken(): + self.log.debug("accepted") + d.Destroy() + break + if d.canceled == 1 or manager.getToken() == "": + self.log.debug("canceled") + simpleDialog.dialog(_("処理結果"), _("キャンセルされました。")) + manager.shutdown() + d.Destroy() + return + self.log.debug("waiting for browser operation...") + user = self._getUser(manager) + token = manager.getData() + self._data[user["id"]] = { + "user": user, + "token": token, + "default": False, + } + manager.shutdown() + return manager.getToken() + + def getClient(self, user=None): + if user is None: + user = self.getDefaultAccount() + manager = self._getManager() + manager.setData(self._data[user]["token"]) + client = manager.getClient() + if client and (self._data[user]["token"] != manager.getData()): + self._data[user]["token"] = manager.getData() + self.save() + manager.shutdown() + return client + + def _getUser(self, manager): + self.log.debug("Checking for authenticated user...") + client = manager.getClient() + if not client: + self.log.error("This token is not available") + return + try: + me = client.get_me() + except Exception as e: + self.log.error(traceback.format_exc()) + return + if me.errors: + self.log.error(me.error) + return + self.log.info("Authenticated user: %s" % me.data) + ret = { + "id": me.data.id, + "username": me.data.username, + "name": me.data.name, + } + return ret + + def setDefaultAccount(self, user): + for i in self._data: + if i == user: + self._data[i]["default"] = True + else: + self._data[i]["default"] = False + self.save() + + def hasDefaultAccount(self): + for i in self._data: + if self._data[i]["default"]: + return True + return False + + def isDefaultAccount(self, user): + return self._data[user]["default"] + + def getDefaultAccount(self): + for i in self._data: + if self._data[i]["default"]: + return i + + def getOtherAccount(self): + ret = [] + for i in self._data: + if not self._data[i]["default"]: + ret.append(i) + return ret + + def getData(self): + return self._data + + def getAccountCount(self): + return len(self._data) + + +class UserList: + def __init__(self): + self._file = constants.SPACES_USER_DATA + self._data = {} + self.log = getLogger("%s.%s" % (constants.LOG_PREFIX, "sources.spaces.userList")) + self.load() + + def load(self): + try: + with open(self._file, "r", encoding="utf-8") as f: + self._data = json.load(f) + self.log.debug("loaded: " + self._file) + except Exception as e: + self.log.error(traceback.format_exc()) + + def save(self): + if hasattr(sys, "frozen"): + indent = None + else: + indent = "\t" + try: + with open(self._file, "w", encoding="utf-8") as f: + json.dump(self._data, f, ensure_ascii=False, indent=indent) + self.log.debug("saved: " + self._file) + except Exception as e: + self.log.error(traceback.format_exc()) + simpleDialog.errorDialog(_("ユーザ情報の保存に失敗しました。")) + + def hasSpecificSetting(self, user): + return self._data[user]["specific"] + + def getConfig(self, user): + items = ( + "baloon", + "record", + "openBrowser", + "sound", + ) + config = {} + if self.hasSpecificSetting(user): + for i in items: + config[i] = self._data[user][i] + config["soundFile"] = self._data[user]["soundFile"] + else: + for i in items: + config[i] = globalVars.app.config.getboolean("notification", i, False) + config["soundFile"] = globalVars.app.config["notification"]["soundFile"] + return config + + def getData(self): + return self._data + + def setData(self, data): + self._data = data + + def getUserIds(self): + return self._data.keys() + + def getProtectedUsers(self): + return [i for i in self._data.keys() if self._data[i]["protected"]] + + def setAttribute(self, user, attribute, newValue): + assert type(user) == str + oldValue = self._data[user][attribute] + self.log.debug("%s changed: %s -> %s" % (attribute, oldValue, newValue)) + self._data[user][attribute] = newValue + field = "" + if attribute == "name": + field = _("名前") + elif attribute == "user": + field = _("ユーザ名") + if field: + globalVars.app.hMainView.addLog( + _("%(field)s変更") % {"field": field}, + _("「%(old)s」→「%(new)s」") %{"old": oldValue, "new": newValue}, + Spaces.friendlyName + ) + self.save() + + def getUserData(self, user): + return self._data[user] + + def addUser(self, id, user, name, protected): + self._data[id] = { + "user": user, + "name": name, + "specific": False, + "baloon": globalVars.app.config.getboolean("notification", "baloon", True), + "record": globalVars.app.config.getboolean("notification", "record", True), + "openBrowser": globalVars.app.config.getboolean("notification", "openBrowser", False), + "sound": globalVars.app.config.getboolean("notification", "sound", False), + "soundFile": globalVars.app.config["notification"]["soundFile"], + "protected": protected, + } + self.save() diff --git a/sources/twitcasting.py b/sources/twitcasting.py index 271ce37..6b8599a 100644 --- a/sources/twitcasting.py +++ b/sources/twitcasting.py @@ -209,7 +209,8 @@ def onClose(self): """ソケット通信が切断された """ self.running = False - globalVars.app.hMainView.menu.EnableMenu("HIDE", False) + if self.getActiveSourceCount() == 0: + globalVars.app.hMainView.menu.EnableMenu("HIDE", False) globalVars.app.hMainView.addLog(_("切断"), _("ツイキャスとの接続を切断しました。"), self.friendlyName) self.setStatus(_("未接続")) self.enableMenu(False) diff --git a/twitterService.py b/twitterService.py index f257eb1..b425204 100644 --- a/twitterService.py +++ b/twitterService.py @@ -20,7 +20,7 @@ def getToken(): - manager = twitterAuthorization.TwitterAuthorization(constants.TWITTER_V1_KEY, constants.TWITTER_V1_SECRET, constants.TWITTER_PORT) + manager = twitterAuthorization.TwitterAuthorization(constants.TWITTER_CONSUMER_KEY, constants.TWITTER_CONSUMER_SECRET, constants.TWITTER_PORT) l="ja" try: l=globalVars.app.config["general"]["language"].split("_")[0].lower() @@ -51,7 +51,7 @@ def getToken(): return manager.getToken() def getFollowList(token,target): - auth = tweepy.OAuthHandler(constants.TWITTER_V1_KEY, constants.TWITTER_V1_SECRET) + auth = tweepy.OAuth1UserHandler(constants.TWITTER_CONSUMER_KEY, constants.TWITTER_CONSUMER_SECRET) auth.set_access_token(*token) try: twitterApi = tweepy.API(auth,proxy=os.environ['HTTPS_PROXY']) diff --git a/views/chooseTwitterAccount.py b/views/chooseTwitterAccount.py new file mode 100644 index 0000000..39e59fd --- /dev/null +++ b/views/chooseTwitterAccount.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# choose account dialog + +import wx +import globalVars +import views.ViewCreator +from logging import getLogger +from views.baseDialog import * + + +class Dialog(BaseDialog): + def __init__(self, tokenManager): + super().__init__("chooseTwitterAccountDialog") + self.tokenManager = tokenManager + self.data = {} + data = tokenManager.getData() + for i in data: + self.data[data[i]["user"]["username"]] = i + + def Initialize(self): + self.log.debug("created") + super().Initialize(self.app.hMainView.hFrame, _("Twitterアカウントを選択")) + self.InstallControls() + return True + + def InstallControls(self): + """いろんなwidgetを設置する。""" + self.creator = views.ViewCreator.ViewCreator(self.viewMode, self.panel, self.sizer, wx.VERTICAL, 20, style=wx.ALL | wx.EXPAND, margin=20) + self.combobox, tmp = self.creator.combobox(_("アカウント"), list(self.data.keys()), state=0) + self.creator = views.ViewCreator.ViewCreator(self.viewMode, self.panel, self.sizer, wx.HORIZONTAL, 20, "", wx.ALIGN_RIGHT | wx.ALL, margin=20) + self.bOk = self.creator.okbutton(_("OK")) + self.bCancel = self.creator.cancelbutton(_("キャンセル"), None) + + def GetData(self): + return self.data[self.combobox.GetValue()] diff --git a/views/main.py b/views/main.py index 170d34d..53d5759 100644 --- a/views/main.py +++ b/views/main.py @@ -23,6 +23,7 @@ from views import globalKeyConfig from views import SimpleInputDialog from views import tcManageUser +from views import spacesManageUser from views import settingsDialog from views import versionDialog @@ -127,8 +128,13 @@ def Apply(self,target): "TC_MANAGE_USER", ]) # スペースメニューの中身 + self.RegisterCheckMenuCommand(self.hSpacesMenu, "SPACES_ENABLE") self.RegisterMenuCommand(self.hSpacesMenu, [ + "SPACES_ADD_FOLLOWING", + "SPACES_UPDATE_USER", "SPACES_URL_REC", + "SPACES_TOKEN_MANAGER", + "SPACES_MANAGE_USER", ]) # オプションメニュー @@ -251,6 +257,27 @@ def OnMenuSelect(self,event): globalVars.app.tc.users = d.GetValue() globalVars.app.tc.saveUserList() + # スペース連携の有効化 + if selected == menuItemsStore.getRef("SPACES_ENABLE"): + if event.IsChecked(): + if not globalVars.app.spaces.initialize(): + self.parent.menu.CheckMenu("SPACES_ENABLE", False) + return + globalVars.app.spaces.start() + else: + globalVars.app.spaces.exit() + globalVars.app.config["spaces"]["enable"] = event.IsChecked() + if globalVars.app.config.write() != errorCodes.OK: + errorDialog(_("設定の保存に失敗しました。下記のファイルへのアクセスが可能であることを確認してください。") + "\n" + os.path.abspath(constants.SETTING_FILE_NAME)) + + # スペース:フォロー中のユーザを追加 + if selected == menuItemsStore.getRef("SPACES_ADD_FOLLOWING"): + globalVars.app.spaces.addFollowingUsers() + + # スペース:URLを指定して録画 + if selected == menuItemsStore.getRef("SPACES_UPDATE_USER"): + globalVars.app.spaces.updateUser() + # スペース:URLを指定して録画 if selected == menuItemsStore.getRef("SPACES_URL_REC"): d = SimpleInputDialog.Dialog(_("URLを入力"), _("スペースのURL")) @@ -261,6 +288,21 @@ def OnMenuSelect(self,event): errorDialog(_("このスペースは既に終了しています。")) elif ret == errorCodes.INVALID_URL: errorDialog(_("入力されたURLが正しくありません。")) + elif ret == errorCodes.SPACE_NOT_STARTED: + errorDialog(_("このスペースはまだ開始されていません。")) + + # スペース:トークンの管理 + if selected == menuItemsStore.getRef("SPACES_TOKEN_MANAGER"): + globalVars.app.spaces.openTokenManager() + + # スペース:ユーザの管理 + if selected == menuItemsStore.getRef("SPACES_MANAGE_USER"): + d = spacesManageUser.Dialog() + d.Initialize() + if d.Show() == wx.ID_CANCEL: + return + globalVars.app.spaces.users.setData(d.GetValue()) + globalVars.app.spaces.users.save() # 設定 if selected == menuItemsStore.getRef("OP_SETTINGS"): diff --git a/views/spacesManageUser.py b/views/spacesManageUser.py new file mode 100644 index 0000000..c02823b --- /dev/null +++ b/views/spacesManageUser.py @@ -0,0 +1,164 @@ +# -*- coding: utf-8 -*- +# Spaces:ユーザーの管理 +# Copyright (C) 2020 yamahubuki +# Note: All comments except these top lines will be written in Japanese. + +import views.KeyValueSettingDialogBase +import wx +import constants +import globalVars +import simpleDialog + +SPECIFIC_INDEX = 3 +SOUND_INDEX = 7 + +class Dialog(views.KeyValueSettingDialogBase.KeyValueSettingDialogBase): + def __init__(self): + columnInfo = [ + (_("ユーザID"), 0, 300), + (_("ユーザ名"), 0, 300), + (_("名前"), 0, 200), + (_("専用設定"), 0, 100), + (_("バルーン通知"), 0, 100), + (_("録画"), 0, 100), + (_("ブラウザで開く"), 0, 100), + (_("サウンドを再生"), 0, 100), + (_("再生するサウンド"), 0, 200), + (_("非公開アカウント"), 0, 200), + ] + user = {} + name = {} + specific = {} + baloon = {} + record = {} + openBrowser = {} + sound = {} + soundFile = {} + protected = {} + for k, v in globalVars.app.spaces.users.getData().items(): + user[k] = v["user"] + name[k] = v["name"] + specific[k] = v["specific"] + if v["specific"]: + baloon[k] = v["baloon"] + record[k] = v["record"] + openBrowser[k] = v["openBrowser"] + sound[k] = v["sound"] + soundFile[k] = v["soundFile"] + else: + baloon[k] = globalVars.app.config.getboolean("notification", "baloon", True) + record[k] = globalVars.app.config.getboolean("notification", "record", True) + openBrowser[k] = globalVars.app.config.getboolean("notification", "openBrowser", False) + sound[k] = globalVars.app.config.getboolean("notification", "sound", False) + soundFile[k] = globalVars.app.config["notification"]["soundFile"] + protected[k] = v["protected"] + super().__init__("spacesManageUser", SettingDialog, columnInfo, user, name, specific, baloon, record, openBrowser, sound, soundFile, protected) + for i in range(len(columnInfo)): + self.SetCheckResultValueString(i, _("有効"), _("無効")) + + def Initialize(self): + super().Initialize(self.app.hMainView.hFrame,_("ユーザの管理")) + self.hListCtrl.SetColumnsOrder([1,2,3,4,5,6,7,8]) + return + + def GetValue(self): + data = super().GetValue() + ret = {} + for i in data[0]: + ret[i] = { + "user": data[0][i], + "name": data[1][i], + "specific": data[2][i], + "baloon": data[3][i], + "record": data[4][i], + "openBrowser": data[5][i], + "sound": data[6][i], + "soundFile": data[7][i], + "protected": data[8][i] == "True", + } + return ret + +class SettingDialog(views.KeyValueSettingDialogBase.SettingDialogBase): + """設定内容を入力するダイアログ""" + + def __init__(self, parent, key="", user="", name="", specific=False, baloon=None, record=None, openBrowser=None, sound=None, soundFile=None, protected=None): + if baloon == None: + baloon = globalVars.app.config.getboolean("notification", "baloon", True) + if record == None: + record = globalVars.app.config.getboolean("notification", "record", True) + if openBrowser == None: + openBrowser = globalVars.app.config.getboolean("notification", "openBrowser", False) + if sound == None: + sound = globalVars.app.config.getboolean("notification", "sound", False) + if soundFile == None: + soundFile = globalVars.app.config["notification"]["soundFile"] + if protected is None: + protected = "" + super().__init__( + parent, + [ + (_("ユーザID"), None), + (_("ユーザ名"), user == ""), + (_("名前"), None), + ("", _("専用設定を使用")), + ("", _("バルーン通知")), + ("", _("録画")), + ("", _("ブラウザで開く")), + ("", _("サウンドを再生")), + (_("再生するサウンド"), True), + (_("保護されたユーザ"), None), + ], + [None] * 8 + [(_("参照"), self.browse)] + [None], + key, user, name, specific, baloon, record, openBrowser, sound, soundFile, protected + ) + + def Initialize(self): + return super().Initialize(_("通知設定")) + + def OkButtonEvent(self, event): + return self.Validation(event) + + def Validation(self, event): + if self.edits[1].GetValue() == "": + simpleDialog.errorDialog(_("ユーザ名が入力されていません。")) + return + if self.edits[0].GetValue() != "": + event.Skip() + return + if self.edits[1].GetValue()[0] == "@": + self.edits[1].SetValue(self.edits[1].GetValue()[1:]) + user = globalVars.app.spaces.getUser(self.edits[1].GetValue()) + if user == None: + return + self.edits[0].SetValue(str(user.id)) + self.edits[2].SetValue(user.name) + self.edits[9].SetValue(str(user.protected)) + event.Skip() + + def InstallControls(self): + super().InstallControls() + self.edits[SPECIFIC_INDEX].Bind(wx.EVT_CHECKBOX, self.onSpecifyChanged) + self.edits[SOUND_INDEX].Bind (wx.EVT_CHECKBOX, self.onSoundChanged) + self.onSpecifyChanged() + + def onSpecifyChanged(self, event=None): + state = self.edits[SPECIFIC_INDEX].GetValue() + for i in range(SPECIFIC_INDEX + 1, len(self.edits)): + self.edits[i].GetParent().Enable(state) + self.buttonObjects[SOUND_INDEX + 1].Enable(state) + if state: + self.onSoundChanged() + + def onSoundChanged(self, event=None): + state = self.edits[SOUND_INDEX].GetValue() + for i in range(SOUND_INDEX + 1, len(self.edits)): + self.edits[i].GetParent().Enable(state) + self.buttonObjects[SOUND_INDEX + 1].Enable(state) + + def browse(self, event): + target = self.edits[SOUND_INDEX + 1] + dialog = wx.FileDialog(self.wnd, _("効果音ファイルを選択"), wildcard=_("音声ファイル(.wav/.mp3/.ogg)") + "|*.wav;*.mp3;*.ogg", style=wx.FD_OPEN) + result = dialog.ShowModal() + if result == wx.ID_CANCEL: + return + target.SetValue(dialog.GetPath()) diff --git a/views/spacesTokenManager.py b/views/spacesTokenManager.py new file mode 100644 index 0000000..b7c46d9 --- /dev/null +++ b/views/spacesTokenManager.py @@ -0,0 +1,138 @@ +# -*- coding: utf-8 -*- +# Twitterアカウントの管理 + +import os +import wx +from logging import getLogger + +from .baseDialog import * +import globalVars +import views.ViewCreator +import simpleDialog + + +class Dialog(BaseDialog): + + def __init__(self): + super().__init__("spacesTokenManager") + self._shouldExit = False + + def Initialize(self): + self.log.debug("created") + self.app = globalVars.app + super().Initialize(self.app.hMainView.hFrame, _("Twitterアカウントの管理")) + self.InstallControls() + return True + + def InstallControls(self): + """いろんなwidgetを設置する。""" + + # 情報の表示 + self.creator = views.ViewCreator.ViewCreator(self.viewMode, self.panel, self.sizer, wx.VERTICAL, 20, style=wx.EXPAND | wx.ALL) + self.hListCtrl, self.hListStatic = self.creator.virtualListCtrl(_("アカウント"), None, wx.LC_REPORT | wx.BORDER_RAISED, (800, 300), wx.ALL | wx.ALIGN_CENTER_HORIZONTAL) + self.hListCtrl.Bind(wx.EVT_LIST_ITEM_SELECTED, self.ItemSelected) + self.hListCtrl.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.ItemSelected) + self.hListCtrl.AppendColumn(_("ユーザ名"), format=wx.LIST_FORMAT_LEFT, width=250) + self.hListCtrl.AppendColumn(_("名前"),format=wx.LIST_FORMAT_LEFT,width=250) + self.hListCtrl.AppendColumn(_("規定のアカウント"), format=wx.LIST_FORMAT_LEFT, width=110) + self.hListCtrl.AppendColumn(_("ID"), format=wx.LIST_FORMAT_LEFT, width=110) + self.hListCtrl.SetColumnsOrder([0, 1, 2]) + + # 処理ボタン + self.creator = views.ViewCreator.ViewCreator(self.viewMode, self.panel, self.creator.GetSizer(), wx.HORIZONTAL, 20, "", wx.ALIGN_RIGHT) + g1 = views.ViewCreator.ViewCreator(self.viewMode, self.panel, self.creator.GetSizer(), wx.VERTICAL, 20, "") + g1r1 = views.ViewCreator.ViewCreator(self.viewMode, self.panel, g1.GetSizer(), wx.HORIZONTAL, 20, "", wx.EXPAND) + g1r2 = views.ViewCreator.ViewCreator(self.viewMode, self.panel, g1.GetSizer(), wx.HORIZONTAL, 20, "", wx.EXPAND) + self.addButton = g1r1.button(_("追加(&A)"), self.add, proportion=1) + self.deleteButton = g1r1.button(_("削除(&D)"), self.delete, proportion=1) + self.deleteButton.Enable(False) + self.setDefaultButton = g1r2.button(_("規定のアカウントに設定(&F)"), self.setDefault) + self.setDefaultButton.Enable(False) + + g2 = views.ViewCreator.ViewCreator(self.viewMode, self.panel, self.creator.GetSizer(), wx.VERTICAL, 0) + self.bClose = self.creator.closebutton(_("閉じる"), self.close) + self.refreshList() + + def refreshList(self): + cursor = self.hListCtrl.GetFocusedItem() + self.hListCtrl.DeleteAllItems() + data = globalVars.app.spaces.tokenManager.getData() + for i in data: + if data[i]["default"]: + state = _("設定中") + else: + state = "" + self.hListCtrl.Append([ + data[i]["user"]["username"], + data[i]["user"]["name"], + state, + str(i), + ]) + if cursor >= 0: + try: + self.hListCtrl.Focus(cursor) + self.hListCtrl.Select(cursor) + except: + pass + else: + self.ItemSelected(None) + + def ItemSelected(self, event): + self.deleteButton.Enable(self.hListCtrl.GetFocusedItem() >= 0) + self.setDefaultButton.Enable(self.hListCtrl.GetFocusedItem() >= 0 and not globalVars.app.spaces.tokenManager.isDefaultAccount(int(self.hListCtrl.GetItemText(self.hListCtrl.GetFocusedItem(), 3)))) + + def add(self, event): + q = simpleDialog.yesNoDialog(_("アカウントの追加"), _("ブラウザを開いてアカウントの認証作業を行います。よろしいですか?")) + if q == wx.ID_NO: + return + self.wnd.Enable(False) + globalVars.app.spaces.tokenManager.addUser() + self.wnd.Enable(True) + self.refreshList() + self.hListCtrl.SetFocus() + + def setDefault(self, event): + key = int(self.hListCtrl.GetItemText(self.hListCtrl.GetFocusedItem(), 3)) + globalVars.app.spaces.tokenManager.setDefaultAccount(key) + self.refreshList() + self.hListCtrl.SetFocus() + + def delete(self, event): + idx = self.hListCtrl.GetFocusedItem() + key = int(self.hListCtrl.GetItemText(self.hListCtrl.GetFocusedItem(), 3)) + self.hListCtrl.DeleteItem(idx) + globalVars.app.spaces.tokenManager.deleteUser(key) + self.setDefaultButton.Enable(False) + self.deleteButton.Enable(False) + self.hListCtrl.SetFocus() + if self.hListCtrl.GetItemCount() == 0: + d = simpleDialog.yesNoDialog(_("確認"), _("Twitterアカウントの情報が設定されていません。Twitterとの連携を停止しますか?")) + if d == wx.ID_YES: + globalVars.app.spaces.exit() + if os.path.exists(constants.AC_SPACES): + os.remove(constants.AC_SPACES) + self._shouldExit = True + self.wnd.Destroy() + + def close(self, event): + if self.hListCtrl.GetItemCount() == 0: + d = simpleDialog.yesNoDialog(_("確認"), _("Twitterアカウントの情報が設定されていません。Twitterとの連携を停止しますか?")) + if d == wx.ID_YES: + globalVars.app.spaces.exit() + if os.path.exists(constants.AC_SPACES): + os.remove(constants.AC_SPACES) + self._shouldExit = True + event.Skip() + else: + return + result = globalVars.app.spaces.tokenManager.hasDefaultAccount() + if not result and self.hListCtrl.GetItemCount() > 0: + simpleDialog.errorDialog(_("規定のアカウントが設定されていません。")) + return + self.wnd.Destroy() + + def OnClose(self, event): + self.close(event) + + def shouldExit(self): + return self._shouldExit From 0a45b449b4c6ed40220c6f19cbe9fc59d2296da3 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Wed, 9 Feb 2022 20:56:03 +0900 Subject: [PATCH 06/32] fix token management process --- sources/spaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/spaces.py b/sources/spaces.py index 3535748..26792ee 100644 --- a/sources/spaces.py +++ b/sources/spaces.py @@ -533,7 +533,7 @@ def getClient(self, user=None): manager = self._getManager() manager.setData(self._data[user]["token"]) client = manager.getClient() - if client and (self._data[user]["token"] != manager.getData()): + if client: self._data[user]["token"] = manager.getData() self.save() manager.shutdown() From 8d4b5167fc0856839340dc8e7beca8a13a8de651 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Wed, 9 Feb 2022 21:58:47 +0900 Subject: [PATCH 07/32] change interval --- sources/spaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/spaces.py b/sources/spaces.py index 26792ee..8f0251c 100644 --- a/sources/spaces.py +++ b/sources/spaces.py @@ -24,7 +24,7 @@ import views.auth -interval = 15 +interval = 40 class Spaces(sources.base.SourceBase): From d986a0d103547d14f4b82dd8be6f2a11a83ac893 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Wed, 9 Feb 2022 22:17:30 +0900 Subject: [PATCH 08/32] add log output --- sources/spaces.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sources/spaces.py b/sources/spaces.py index 8f0251c..b446221 100644 --- a/sources/spaces.py +++ b/sources/spaces.py @@ -475,6 +475,7 @@ def load(self): return True def save(self): + self.log.debug("Saving token data") try: with open(self._file, "wb") as f: pickle.dump(self._data, f) @@ -482,6 +483,7 @@ def save(self): self.log.error(traceback.format_exc()) simpleDialog.errorDialog(_("認証情報の保存に失敗しました。")) return False + self.log.debug("saved: %s" % self._file) return True def _getToken(self): From 25bfa4f3fd6ad458e9657224806dafbeeb808274 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Thu, 10 Feb 2022 09:55:51 +0900 Subject: [PATCH 09/32] Fix addLog --- sources/spaces.py | 9 +++++---- sources/twitcasting.py | 40 ++++++++++++++++++++-------------------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/sources/spaces.py b/sources/spaces.py index b446221..465046d 100644 --- a/sources/spaces.py +++ b/sources/spaces.py @@ -146,7 +146,7 @@ def run(self): if self.initialized == 0 and not self.initialize(): return self.running = True - globalVars.app.hMainView.addLog(_("接続完了"), _("スペースの監視を開始しました。"), self.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("接続完了"), _("スペースの監視を開始しました。"), self.friendlyName) globalVars.app.hMainView.menu.CheckMenu("SPACES_ENABLE", True) globalVars.app.hMainView.menu.EnableMenu("HIDE") self.setStatus(_("接続済み")) @@ -213,7 +213,7 @@ def exit(self): self.running = False if self.getActiveSourceCount() == 0: globalVars.app.hMainView.menu.EnableMenu("HIDE", False) - globalVars.app.hMainView.addLog(_("切断"), _("Twitterとの接続を切断しました。"), self.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("切断"), _("Twitterとの接続を切断しました。"), self.friendlyName) globalVars.app.hMainView.menu.CheckMenu("SPACES_ENABLE", False) self.setStatus(_("未接続")) self.enableMenu(False) @@ -242,7 +242,7 @@ def checkSpaceStatus(self, users, account=None): except Exception as e: self.log.error(traceback.format_exc()) if not self._tokenManagerShown: - globalVars.app.hMainView.addLog(_("Twitter エラー"), _("Twitterとの通信中にエラーが発生しました。詳細:%s") % e, self.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("Twitter エラー"), _("Twitterとの通信中にエラーが発生しました。詳細:%s") % e, self.friendlyName) return self.log.debug(ret) if ret.data: @@ -671,7 +671,8 @@ def setAttribute(self, user, attribute, newValue): elif attribute == "user": field = _("ユーザ名") if field: - globalVars.app.hMainView.addLog( + wx.CallAfter( + globalVars.app.hMainView.addLog, _("%(field)s変更") % {"field": field}, _("「%(old)s」→「%(new)s」") %{"old": oldValue, "new": newValue}, Spaces.friendlyName diff --git a/sources/twitcasting.py b/sources/twitcasting.py index 6b8599a..e4133c7 100644 --- a/sources/twitcasting.py +++ b/sources/twitcasting.py @@ -136,14 +136,14 @@ def onMessage(self, text): continue userId = i["broadcaster"]["id"] if userId in self.users.keys(): - globalVars.app.hMainView.addLog(_("配信開始"), i["broadcaster"]["screen_id"], self.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("配信開始"), i["broadcaster"]["screen_id"], self.friendlyName) globalVars.app.notificationHandler.notify(self, i["broadcaster"]["screen_id"], i["movie"]["link"], i["movie"]["hls_url"], i["movie"]["created"], self.getConfig(userId), i["movie"]["id"]) self.updateUserInfo(userId, i["broadcaster"]["screen_id"], i["broadcaster"]["name"]) rm = [] for i in self.users: if "remove" in self.users[i].keys() and (self.users[i]["remove"] - time.time()) < 0: rm.append(i) - globalVars.app.hMainView.addLog(_("録画対象の削除"), _("%sのライブを、録画対象から削除しました。") %self.users[i]["user"]) + wx.CallAfter(globalVars.app.hMainView.addLog, _("録画対象の削除"), _("%sのライブを、録画対象から削除しました。") %self.users[i]["user"]) b = wx.adv.NotificationMessage(constants.APP_NAME, _("%sのライブを、録画対象から削除しました。") %self.users[i]["user"]) b.Show() b.Close() @@ -171,10 +171,10 @@ def updateUserInfo(self, id, user, name): with tlock: self.loadUserList() if user != self.users[id]["user"]: - globalVars.app.hMainView.addLog(_("ユーザ名変更"), _("「%(old)s」→「%(new)s」") %{"old": self.users[id]["user"], "new": user}, self.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("ユーザ名変更"), _("「%(old)s」→「%(new)s」") %{"old": self.users[id]["user"], "new": user}, self.friendlyName) self.users[id]["user"] = user if name != self.users[id]["name"]: - globalVars.app.hMainView.addLog(_("名前変更"), _("「%(old)s」→「%(new)s」") %{"old": self.users[id]["name"], "new": name}, self.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("名前変更"), _("「%(old)s」→「%(new)s」") %{"old": self.users[id]["name"], "new": name}, self.friendlyName) self.users[id]["name"] = name self.saveUserList() @@ -190,7 +190,7 @@ def onError(self, error): if type(error) in (ConnectionResetError, websocket._exceptions.WebSocketAddressException): self.shouldExit = True self.socket.close() - globalVars.app.hMainView.addLog(_("切断"), _("インターネット接続が切断されました。再試行します。"), self.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("切断"), _("インターネット接続が切断されました。再試行します。"), self.friendlyName) self.setStatus(_("接続試行中")) self.initSocket() self.socket.run_forever() @@ -199,7 +199,7 @@ def onOpen(self): """ソケット通信が始まった """ self.running = True - globalVars.app.hMainView.addLog(_("接続完了"), _("新着ライブの監視を開始しました。"), self.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("接続完了"), _("新着ライブの監視を開始しました。"), self.friendlyName) globalVars.app.hMainView.menu.CheckMenu("TC_ENABLE", True) globalVars.app.hMainView.menu.EnableMenu("HIDE") self.setStatus(_("接続済み")) @@ -211,12 +211,12 @@ def onClose(self): self.running = False if self.getActiveSourceCount() == 0: globalVars.app.hMainView.menu.EnableMenu("HIDE", False) - globalVars.app.hMainView.addLog(_("切断"), _("ツイキャスとの接続を切断しました。"), self.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("切断"), _("ツイキャスとの接続を切断しました。"), self.friendlyName) self.setStatus(_("未接続")) self.enableMenu(False) globalVars.app.hMainView.menu.CheckMenu("TC_ENABLE", False) if not self.shouldExit: - globalVars.app.hMainView.addLog(_("再接続"), _("ツイキャスとの接続が切断されたため、再度接続します。"), self.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("再接続"), _("ツイキャスとの接続が切断されたため、再度接続します。"), self.friendlyName) self.log.debug("Connection does not closed by user.") self.initSocket() self.socket.run_forever() @@ -633,7 +633,7 @@ def record(self, userName): "remove": (datetime.datetime.now() + datetime.timedelta(hours=10)).timestamp(), } self.saveUserList() - globalVars.app.hMainView.addLog(_("ユーザ名を指定して録画"), _("%sを、録画対象として追加しました。この登録は一定時間経過後に自動で削除されます。") %userInfo["user"]["screen_id"]) + wx.CallAfter(globalVars.app.hMainView.addLog, _("ユーザ名を指定して録画"), _("%sを、録画対象として追加しました。この登録は一定時間経過後に自動で削除されます。") %userInfo["user"]["screen_id"]) if userInfo["user"]["is_live"]: movie = self.getCurrentLive(userName) if movie == None: @@ -700,7 +700,7 @@ def run(self): self.log.info("Failed to get movie info(ID:%s" %self.movie) return self.userName = movieInfo["broadcaster"]["screen_id"] - globalVars.app.hMainView.addLog(_("コメント保存開始"), _("ユーザ:%(user)s、ムービーID:%(movie)s") %{"user": self.userName, "movie": self.movie}, self.tc.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("コメント保存開始"), _("ユーザ:%(user)s、ムービーID:%(movie)s") %{"user": self.userName, "movie": self.movie}, self.tc.friendlyName) self.saveComment() while self.isLive(): if self.hasError == 1: @@ -712,7 +712,7 @@ def run(self): if len(self.comments) > 0: self.lastCommentId = self.comments[0]["id"] self.saveComment() - globalVars.app.hMainView.addLog(_("コメント保存終了"), _("ユーザ:%(user)s、ムービーID:%(movie)s") %{"user": self.userName, "movie": self.movie}, self.tc.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("コメント保存終了"), _("ユーザ:%(user)s、ムービーID:%(movie)s") %{"user": self.userName, "movie": self.movie}, self.tc.friendlyName) def saveComment(self): """コメントをファイルに保存する @@ -819,15 +819,15 @@ def __init__(self, tc): def run(self): globalVars.app.hMainView.menu.EnableMenu("TC_MANAGE_USER", False) - globalVars.app.hMainView.addLog(_("ユーザ情報の更新"), _("ユーザ情報の更新を開始します。"), self.tc.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("ユーザ情報の更新"), _("ユーザ情報の更新を開始します。"), self.tc.friendlyName) for i in self.users: if i in self.tc.users: - globalVars.app.hMainView.addLog(_("ユーザ情報の更新"), _("%sの情報を取得しています。") %self.tc.users[i]["user"], self.tc.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("ユーザ情報の更新"), _("%sの情報を取得しています。") %self.tc.users[i]["user"], self.tc.friendlyName) userInfo = self.tc.getUserInfo(i, False) if userInfo: self.tc.updateUserInfo(i, userInfo["user"]["screen_id"], userInfo["user"]["name"]) time.sleep(60) - globalVars.app.hMainView.addLog(_("ユーザ情報の更新"), _("ユーザ情報の更新が終了しました。"), self.tc.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("ユーザ情報の更新"), _("ユーザ情報の更新が終了しました。"), self.tc.friendlyName) globalVars.app.hMainView.menu.EnableMenu("TC_MANAGE_USER", True) class TwitterHelper(threading.Thread): @@ -843,7 +843,7 @@ def showLog(self, message): :param message: メッセージ本文 :type message: str """ - globalVars.app.hMainView.addLog(_("一括追加"), message, self.tc.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("一括追加"), message, self.tc.friendlyName) def run(self): globalVars.app.hMainView.menu.EnableMenu("TC_MANAGE_USER", False) @@ -919,19 +919,19 @@ def getMovies(self, slice_id=""): return dict["movies"] def run(self): - globalVars.app.hMainView.addLog(_("一括録画"), _("ライブ一覧を取得しています。"), self.tc.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("一括録画"), _("ライブ一覧を取得しています。"), self.tc.friendlyName) movies = [i for i in self.getAllMovies() if i["is_recorded"] and (not i["is_protected"])] movies.reverse() - globalVars.app.hMainView.addLog(_("一括録画"), _("処理を開始します。対象ライブ数:%i") % len(movies), self.tc.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("一括録画"), _("処理を開始します。対象ライブ数:%i") % len(movies), self.tc.friendlyName) count = 0 index = 1 for i in movies: - globalVars.app.hMainView.addLog(_("一括録画"), _("処理中(%(index)i/%(total)i)") % {"index": index, "total": len(movies)}, self.tc.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("一括録画"), _("処理中(%(index)i/%(total)i)") % {"index": index, "total": len(movies)}, self.tc.friendlyName) result = self.tc.downloadArchive(i["link"], join=True, skipExisting=True) index += 1 time.sleep(5) if result == errorCodes.RECORD_SKIPPED: - globalVars.app.hMainView.addLog(_("一括録画"), _("ファイルが既に存在するため、録画をスキップします。"), self.tc.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("一括録画"), _("ファイルが既に存在するため、録画をスキップします。"), self.tc.friendlyName) continue count += 1 - globalVars.app.hMainView.addLog(_("一括録画"), _("完了。%i件録画しました。") % count, self.tc.friendlyName) + wx.CallAfter(globalVars.app.hMainView.addLog, _("一括録画"), _("完了。%i件録画しました。") % count, self.tc.friendlyName) From 6f341048f9753fec86b3ca843f8836bc031d0ac5 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Thu, 10 Feb 2022 09:57:03 +0900 Subject: [PATCH 10/32] Fix error handling of updateUser --- sources/spaces.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/sources/spaces.py b/sources/spaces.py index 465046d..90dcd3a 100644 --- a/sources/spaces.py +++ b/sources/spaces.py @@ -159,6 +159,7 @@ def run(self): def updateUser(self): self.log.debug("Updating user info...") + notFound = [] ids = self.users.getUserIds() for i in self.splitIds(ids): try: @@ -171,12 +172,21 @@ def updateUser(self): simpleDialog.errorDialog(_("ユーザ情報の更新に失敗しました。詳細:%s") % e) return if result.errors: - simpleDialog.errorDialog(_("ユーザ情報の更新に失敗しました。詳細:%s") % result.errors) - return + for err in result.errors: + if err["title"] == "Not Found Error": + notFound.append(err["value"]) + continue + simpleDialog.errorDialog(_("ユーザ情報の更新に失敗しました。詳細:%s") % err["detail"]) + return data = result.data if data: for j in data: self._updateUserInfo(j) + if notFound: + usernames = [] + for i in notFound: + usernames.append(self.users.getData()[i]["user"]) + simpleDialog.errorDialog(_("以下のユーザの情報を取得できませんでした。") + "\n" + "\n".join(usernames)) simpleDialog.dialog(_("完了"), _("ユーザ情報の更新が完了しました。")) def _process(self): From f75313d7b371ff16f2cdb8c265c0c8e4eaf0b349 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Thu, 10 Feb 2022 11:40:46 +0900 Subject: [PATCH 11/32] Fix error of invalid url --- sources/spaces.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sources/spaces.py b/sources/spaces.py index 90dcd3a..e106d43 100644 --- a/sources/spaces.py +++ b/sources/spaces.py @@ -292,6 +292,8 @@ def recFromUrl(self, url): self.log.error("Space ID not found: " + url) return errorCodes.INVALID_URL metadata = self.getMetadata(spaceId) + if metadata == errorCodes.INVALID_URL: + return errorCodes.INVALID_URL if type(metadata) == int: self.showError(metadata) return @@ -349,6 +351,8 @@ def getMetadata(self, spaceId): except Exception as e: self.log.error(traceback.format_exc()) return errorCodes.INVALID_RECEIVED + if not metadata["data"]["audioSpace"]: + return errorCodes.INVALID_URL return Metadata(metadata) def getMediaLocation(self, mediaKey): From 33393e3335d5a37d635f185866ab20f3781679be Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Thu, 10 Feb 2022 11:44:45 +0900 Subject: [PATCH 12/32] fix typo --- sources/spaces.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sources/spaces.py b/sources/spaces.py index e106d43..ac674e3 100644 --- a/sources/spaces.py +++ b/sources/spaces.py @@ -299,10 +299,10 @@ def recFromUrl(self, url): return if not metadata.isRunning(): if metadata.isEnded(): - self.log.debug("is ended: " + metadata) + self.log.debug("is ended: %s" % metadata) return errorCodes.SPACE_ENDED if metadata.isNotStarted(): - self.log.debug("is not started: " + metadata) + self.log.debug("is not started: %s" % metadata) return errorCodes.SPACE_NOT_STARTED mediaKey = metadata.getMediaKey() if mediaKey == errorCodes.INVALID_RECEIVED: From f631a73953b1b690f7e5e652b4613b5494c94e60 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Thu, 10 Feb 2022 12:36:05 +0900 Subject: [PATCH 13/32] fix interval --- sources/spaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/spaces.py b/sources/spaces.py index ac674e3..d484b45 100644 --- a/sources/spaces.py +++ b/sources/spaces.py @@ -24,7 +24,7 @@ import views.auth -interval = 40 +interval = 45 class Spaces(sources.base.SourceBase): From d48257a1748aba36b5a080ee3371a7289f734347 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Thu, 10 Feb 2022 12:56:34 +0900 Subject: [PATCH 14/32] update readme --- public/readme.txt | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/public/readme.txt b/public/readme.txt index 2a589cf..e68d750 100644 --- a/public/readme.txt +++ b/public/readme.txt @@ -109,6 +109,54 @@ ULTRAをコンピュータから削除する場合は、「ULTRA」フォルダ 2. ツイキャスには、「グループ限定配信」や「プライベート配信」など、特定のユーザにしか閲覧できない特別な配信があります。本ソフトの通知機能は、これらの配信には対応していません。ただし、プライベート配信の録画が公開されている場合、「合い言葉」を入力することで、録画をダウンロードすることは可能です。 3. ULTRAに設定したアクセストークンの情報は、ULTRAをインストールしたフォルダ内の「data」フォルダにある「twitcasting」サブフォルダ内に、「account.bin」というファイル名で保存されます。このファイルを外部に流出させないよう、十分ご注意ください。また、複数のコンピュータでULTRAを使用する場合、1台のコンピュータで認証作業を行った後、account.binを別のコンピュータにコピーしてください。 +4.2 Twitter スペース +4.2.1 Twitter スペースとの連携機能の概要 +Twitterが提供する音声コミュニケーション機能である「スペース(Spaces)」との連携機能です。 + +4.2.2 利用方法 +Twitter スペースとの連携機能を使用するには、メニューバーの[サービス]→[Twitter スペース]→[Twitter スペースとの連携機能を有効化]を選択します。 +初めてこの機能を使用する場合は、Twitterアカウントの設定を促すメッセージが表示されます。 +[はい]を選択すると、[Twitterアカウントの管理]ダイアログが表示されます。 + +Twitterアカウントを登録するには、[追加]ボタンを押します。 +ブラウザが起動し、アカウントへのアクセスを許可するかどうかを確認するメッセージが表示されます。 +内容を確認し、[アプリにアクセスを許可]ボタンを押すと、アカウントが本ソフトに登録されます。 +複数のアカウントを登録したい場合は、それぞれのアカウントに対して上記の操作を行ってください。 + +すべてのアカウントを登録したら、いずれか1つのアカウントを「規定のアカウント」に設定する必要があります。 +アカウントを選択し、[規定のアカウントに設定]ボタンをおしてください。 +[閉じる]ボタンを押すと、ダイアログを閉じてメイン画面に戻ります。 + +通常、Twitterと本ソフトの通信は、規定のアカウントを用いて行われます。 +しかし、非公開アカウントが開催しているスペースの情報は、登録しているすべてのアカウントで取得します。 +これは、規定のアカウントがフォローしていない非公開アカウントが通知対象に含まれ、その非公開アカウントを別のアカウントがフォローしている場合に、開催中のスペースの情報を得られるようにするためです。 + +通知対象のユーザを設定するには、メニューバーの[サービス]→[Twitter スペース]→[通知対象ユーザの管理]を選択します。 +新しいユーザを追加するには、[追加]ボタンを押します。 +[ユーザ名]に、英数字のユーザ名(例:act_laboratory)を入力します。 +デフォルトの通知方法を使用せず、ユーザ専用の通知方法を設定するには、[専用設定を使用]を選択します。 +[OK]ボタンを押すと、入力したユーザ名を元にユーザ情報が取得されます。該当するユーザが見つからない場合は、エラーメッセージが表示されます。 +[現在の登録内容]でユーザを選択して[変更]ボタンを押すと、選択したユーザに対する通知方法を変更できます。ただし、ユーザ名は変更できません。 +全ての設定が終了したら、[OK]ボタンを押して設定を保存します。 + +4.2.3 メニュー項目の詳細 +メニューバーの[サービス]→[Twitter スペース]には、以下の項目があります。 +・Twitter スペースとの連携機能を有効化:Twitter スペースとの連携機能を使用するかどうかを切り替えます。 +・フォロー中のユーザをすべて追加:ULTRAに登録したTwitterアカウントがフォローしているすべてのユーザを、通知対象に追加します。アカウントを選択して[OK]ボタンを押すと、ユーザの検索と追加が行われます。終了すると、今回の操作で追加されたユーザの数が標示されます。 +・ユーザ情報の更新:通知対象として登録しているユーザの情報(ユーザ名、名前、公開/非公開アカウントのいずれか)を最新の情報に更新します。 +・スペースのURLを指定して録画:スペースのURL(例:https://twitter.com/i/spaces/xxxxx)を入力し、手動で録画を開始します。 +・連携アカウントの管理:本ソフトで使用するTwitterアカウントの追加/削除、規定のアカウントの変更などができます。 +・通知対象ユーザの管理:通知対象として登録するユーザを設定します。 + +4.2.4 利用に当たっての注意事項 +1. Twitter スペースでやりとりされる情報は、音声のみです。録画ファイルの形式は、「音声のみ(MP3)」を使用することをお勧めします。現在のところ、サービスごとにファイル形式を設定することができないため、大変お手数ですが、本機能を使用する前に、必要に応じてファイル形式の設定を行ってください。なお、「動画(TS)」や「動画(MP4)」に設定されていても、録画機能は問題なく使用できます。 +2. ULTRAに設定したアクセストークンの情報は、ULTRAをインストールしたフォルダ内の「data」フォルダにある「twitcasting」サブフォルダ内に、「account.bin」というファイル名で保存されます。このファイルを外部に流出させないよう、十分ご注意ください。 +3. 録画機能は、Twitterが公式に提供しているものではないため、将来的に利用できなくなる可能性があります。 +4. Twitterには、アカウントごとのAPI呼び出し回数に制限があります。本ソフトでは、通知対象ユーザが最大1500人登録されることを想定し、制限を超過しないように設計しています。 +5. 通知対象ユーザが非公開アカウントかどうかは、以下のタイミングで取得した情報に基づいて判断しています。 +・[通知対象ユーザの管理]で、新しくユーザが追加されたとき +・そのユーザがスペースを開催し、それを本ソフトが検知したとき +従って、既に通知対象として登録されており、スペースを開催したことのないユーザが公開/非公開を変更した場合、本ソフトが管理している状態と実際の状態が一致しないことがあります。この問題を防止するため、定期的に[ユーザ情報の更新]を実行することをお勧めします。 第5章 メニューリファレンス ここでは、ULTRAの各メニュー項目について解説します。 From f04818710ce48b7192736a2d58da70206d1f4d44 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Thu, 10 Feb 2022 12:58:03 +0900 Subject: [PATCH 15/32] updated history.txt --- public/history.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/public/history.txt b/public/history.txt index 4fed678..98a5861 100644 --- a/public/history.txt +++ b/public/history.txt @@ -1,5 +1,8 @@ Universal Live Tracking and Recording App(ULTRA) 更新履歴 +2022/02/11 Version 1.3.0 +1. Twitter スペースとの連携機能を追加しました。 + 2022/02/02 Version 1.2.1 1. ツイキャス連携機能が無効になっているとき、[過去ライブの一括ダウンロード]を実行できないように修正しました。 2. [Twitterでフォローしているユーザを一括追加]が正しく動作しなくなった問題を修正しました。 From 8ff4d652366b71ba50db3f067c6c2320e0a4127b Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Thu, 10 Feb 2022 13:04:26 +0900 Subject: [PATCH 16/32] fix menu blocking --- public/history.txt | 1 + sources/twitcasting.py | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/public/history.txt b/public/history.txt index 98a5861..7b0c9a3 100644 --- a/public/history.txt +++ b/public/history.txt @@ -2,6 +2,7 @@ Universal Live Tracking and Recording App(ULTRA) 更新履歴 2022/02/11 Version 1.3.0 1. Twitter スペースとの連携機能を追加しました。 +2. ツイキャス連携機能が無効になっているとき、[Twitterでフォローしているユーザを一括追加]を実行できないように修正しました。 2022/02/02 Version 1.2.1 1. ツイキャス連携機能が無効になっているとき、[過去ライブの一括ダウンロード]を実行できないように修正しました。 diff --git a/sources/twitcasting.py b/sources/twitcasting.py index e4133c7..f6a20b5 100644 --- a/sources/twitcasting.py +++ b/sources/twitcasting.py @@ -106,7 +106,16 @@ def checkTokenExpires(self, startup=False): pickle.dump(d, f) def enableMenu(self, mode): - tc = ("TC_SAVE_COMMENTS", "TC_UPDATE_USER", "TC_RECORD_ARCHIVE", "TC_RECORD_ALL", "TC_RECORD_USER", "TC_SET_TOKEN", "TC_MANAGE_USER") + tc = ( + "TC_SAVE_COMMENTS", + "TC_UPDATE_USER", + "TC_ADD_TW", + "TC_RECORD_ARCHIVE", + "TC_RECORD_ALL", + "TC_RECORD_USER", + "TC_SET_TOKEN", + "TC_MANAGE_USER" + ) for i in tc: globalVars.app.hMainView.menu.EnableMenu(i, mode) From 0f8dd24e7b316228a0207320e905c683ab92d9e9 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Thu, 10 Feb 2022 13:18:18 +0900 Subject: [PATCH 17/32] fix unusable log --- sources/spaces.py | 2 ++ views/spacesTokenManager.py | 6 ------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/sources/spaces.py b/sources/spaces.py index d484b45..a13a148 100644 --- a/sources/spaces.py +++ b/sources/spaces.py @@ -78,6 +78,8 @@ def openTokenManager(self): self._tokenManagerShown = False if d.shouldExit(): self.shouldExit = True + if self.running: + self.exit() return errorCodes.SHOULD_EXIT def addFollowingUsers(self): diff --git a/views/spacesTokenManager.py b/views/spacesTokenManager.py index b7c46d9..b7439db 100644 --- a/views/spacesTokenManager.py +++ b/views/spacesTokenManager.py @@ -108,9 +108,6 @@ def delete(self, event): if self.hListCtrl.GetItemCount() == 0: d = simpleDialog.yesNoDialog(_("確認"), _("Twitterアカウントの情報が設定されていません。Twitterとの連携を停止しますか?")) if d == wx.ID_YES: - globalVars.app.spaces.exit() - if os.path.exists(constants.AC_SPACES): - os.remove(constants.AC_SPACES) self._shouldExit = True self.wnd.Destroy() @@ -118,9 +115,6 @@ def close(self, event): if self.hListCtrl.GetItemCount() == 0: d = simpleDialog.yesNoDialog(_("確認"), _("Twitterアカウントの情報が設定されていません。Twitterとの連携を停止しますか?")) if d == wx.ID_YES: - globalVars.app.spaces.exit() - if os.path.exists(constants.AC_SPACES): - os.remove(constants.AC_SPACES) self._shouldExit = True event.Skip() else: From 3e1a5e4afcc0dd3cd2c02ee5bdb95c92715880c4 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Thu, 10 Feb 2022 15:28:16 +0900 Subject: [PATCH 18/32] fix typo --- sources/spaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/spaces.py b/sources/spaces.py index a13a148..6ce6aba 100644 --- a/sources/spaces.py +++ b/sources/spaces.py @@ -54,7 +54,7 @@ def initialize(self): self.showError(result) return False if not os.path.exists(constants.AC_SPACES): - d = simpleDialog.yesNoDialog(_("Twitterアカウントの連携"), _("Twitterスペースを使用する前に、使用するTwitterアカウントを設定する必要があります。今すぐ設定画面を開きますか?")) + d = simpleDialog.yesNoDialog(_("Twitterアカウントの連携"), _("Twitter スペースを使用する前に、使用するTwitterアカウントを設定する必要があります。今すぐ設定画面を開きますか?")) if d == wx.ID_NO: return False if self.openTokenManager() == errorCodes.SHOULD_EXIT: From 4b44f6fb4a3a32825ccf02a06e89a960b148ab9a Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Thu, 10 Feb 2022 16:26:40 +0900 Subject: [PATCH 19/32] update translation --- locale/en-us/LC_MESSAGES/messages.mo | Bin 24994 -> 30578 bytes locale/en-us/LC_MESSAGES/messages.po | 661 ++++++++++++++++++--------- locale/ja-jp/LC_MESSAGES/messages.po | 637 +++++++++++++++++--------- locale/messages.po | 637 +++++++++++++++++--------- 4 files changed, 1303 insertions(+), 632 deletions(-) diff --git a/locale/en-us/LC_MESSAGES/messages.mo b/locale/en-us/LC_MESSAGES/messages.mo index db5762b3330b15f9b3214d9dcb7022ee1b7ed4bd..fde736d80407ced819328c0eccad968075f097e2 100644 GIT binary patch delta 9931 zcmciId2|$2zQFMt2>TKU5JDhOY#}kBNrE7tfML@hdsqd8Cfy+oO?T+-5CNY}2N2mF zNU_Q;3K2{cTlP&IWyEFV%{UH<+q1L4=%}DPml=ln{<Dp5&qAjBR43H_z0!iU}Qa|{G`8bD!EEsYN=EnUdAYVI992-I14A?C)gT$ zwo9fp6o7@k;f>-EEcn1;4;#yx2~j_bXLLQ))07{ctueL9VIq za4$A#Z=4@7{R>L|k0?F1qk~cn@dVoNE$oI}J1W%&r{X=h8FTSG%JtrKQ%Byf93-fu zT8+}O12`PtKs!cvHhSn$lotOUd*eT`JNBYKA4Dfg{vnk6zQw7Sz_>VZ2~woGf^{O4 zibK)F7@t5LD7UFrX5GJYn5`K20K=r-(x58)Wh#~mnL-JE_C(~xJA}2gXpU^u!{R zf=^)+d>iHYix`7fOzWl_=j}{;nhy7qkTJ0#!=aXAG`@_|6K|rl(19FC-yd@gjQgB1Yo~W{_yb7U)Bk zuv&rA(q~bI=n%$W1XFx4#^P{%1Zli_0n;Ot`V8qj)sz`3KM=u5^iY=l3e zw4@emrvbJ=DX2Y4g|bk3AP1#~yeI|kM(L>|DEEDY^1`q2UX15MhIkUn9P(hQtp7FP zgi@~}?^ah(3QlH#<;F=U6_|%ou_CnNQDgzAw$%P%%*56BEJ_8_`x*8}SzW_Wp3g#A zHQ8w6{b~=1R#=x@u>-cn`%r2;8KtXz$UD`m$d9_thqSQo{l*+{q1^u{N{>8+QqDeX zjb||(zeM&Z)gpubmzwq>AvGL~kK-_`kME!q@FB{5*Gy{+Fj^9Yjmd9^qtSv=&LWii zm!VW-y_vt?%s+@y{^>mj6s4fgPzv|~YvT>`y!Hc1 zZQ;BrI`IHLiLvZXOK>;Jevpu9G$_@yFG@K>QC8Jxl%e%cB|&ei$1oqypv?XrLydy6 zQ8uPI*bX;heLRd(;A<#d{1!?Ner2BjWS+NX=F0PlC_OP6<-SGORM!7$5>n7kyn%ah zZUhc9#x!<>QISNH7N(>0&=F)<)q5xfHXUi$7VB}IY}x~*tNWuoHxXquxG+K1{|XWt z$vB8okq1Yyv~VQSyJ|R7o0_T(C@YPW8H&)qZv~YeB{ryRNMPdpjPcXiQpTx&F--Xg;@e_?M?2eCeJ`82@yoplb zQe+g=H<*HbCmAo8g-M)m!B+S?Y=fVo^u#Zd=>KREv8=H;OvYX~1ZC3ru^Sd)d%S?{ z@E4Q<;wBsS^+H)aR@87AcEloNRMopEFK$CCbFeq^qjpW9|079U=d2Fam})GK`WVA` z3^v2A7>gNb$A?iW@+vy<3zYkYKVr1hip@D+i2ZOa%Fw-!(!kGA=31tf_|!$v3t#-I#IJhsLzD3f;}N(07WJi1US>|aGfUc3wI z<6+a+P=@ARlrjArdt*bZasLpM9-D&lLK{kjTqxxfm=>C@Hr;Hx2jh6ZDj^}W`y5I^ z7f{CTOKgI*XBf6bX?Y?}L<`E$EyD)53ZE_xhg8LmUAz%ES3y(lgH2)kn`_QzNzh|KnhSdJc)xipEHCvzYV zrGksFGw)aLk&uGFM`_8=C>5zS)0p**P+HavrHAgrjyMFRz*#62UVzeLYf!^on2N7q zOT3CQ2kPg_&PN&Om&8yK7MzP3?n1foHSB;NV;#JX1F@FFXi+AznW^c>3R6Lpo{E}f z^h`X;bBQSBrD7UpVlP}ci~g4uyh4U_^&8j*FQCkcYbX_roNaV%vgvG;S$qKN;W?E1 z|AaEzzeFkT2lKqH)7UZNP$Pc;*2Zin{V!cRiwuTO6`<7k6O;l?eO{ussiC?wynrjwA_xfrFu zttb_E9;HG-^ZX>Tl+{_hACu-9d;2WRnO|g zBb3$f4c3DJiHXEZ#9=~?#>8L3DWj_%CVkO7AB*=B z!_0F*=8qir5s_3(Lz(^aiRnb;A@kvuL&kp~vE9s#;$enA{F|<1{YV^=1ji!z;JU0^ zIo1<0&TYtBhi8a`L>J-)QAX4t{z%9%!a#k6*=E|mjD#$=%A=W?kR{TIc!H2$NFE{5 zh>r<5Y7!g6DdQsY)QO9TqERYXAu7UzFK;rjK3TWcucsX4r%LR_wq-PpQm1Q~_|7ND+ zxZc}Le~nA4#-EdanioFBKM*asc?H(Q3Vf7^Bw7$rL?faG*W~Cyyi9B%CJ=I*CK3rd z@fgwCyqBc=tnFjCJ|oZ6tSC-qb>!V$2o+B7(|p4d4wGG zxPAl^!e_=u4s5D+UP8LLob&$h@fn|1VhHgC@f1;cL{Q-GIeE*>$j80JTw)g?M=>#j z7)i9^emTA+RuX@b6OL$u@Mm{3-6FwplrjM%ez;F0{rX`1(uZU zdZldblcB(iR8mh#ZmkcAZQCUj(51Y}JIkI}U3PqL#g^k0>vx*%3O}VUjlExgH@Rc8 z$`%D^cFE;MPnVwAtv5}NZFcGTV8tsheS7lh&fdz}>LqveZ&!K$Z9NbuDPO#_Y}Y~k zN_w1sYp75UEh?J)ywfP+(0cuqzbUK$lYM|HCNWQR&Nz zM#)=#tJX}3z|ry*JAUJOWn*YtaVT)+(&>|Y%AjQbKQt=%W~50ST8^ZJ3##8O0!)x;x)4{l$m@p(b zzU$)=eK*R^!$L1ST)C2$$eg)j$c=eedTLwwbH&^lo?T0HXG*8w2NqvM?VGi2P;F)D z{?u0bh}2e*i3yoWoW#`%*IA#Fnxf~Xrs>h?EpIk1=uaI`BRaI0O^0poFw50ka-YR^ z(vPH{h}nLr_=rs7LMHaks{3?z_jj5ueXeZ9)~aiCtA4$0+hDJ|f3B6fcTIQ@N^WbC zy7U_tWQBJ%SNLz<#7rnz9=G8NObALNi$aHty^rM#MlWE6Ec#5tNJEHw%op>Z0QTKx86Dy`b!yYo68qQFjROn z{Aqr9=w~w$qc86amhE}TSXiY;*{|2>H`25i_Qsu|!T=lTEtj@+Zr`G8*>U==Y$+RU ziShl!@an^2WA(pf^wviXh^e36H7!+3>)9v0hwdJ*w^nycswGWtJ#bi*`K_j9=UQDe z?Y5w8V5=JbYTs*vJs$obG`C4DE6T|eV8sCe# z+HKDbu;;ivc5S{pU(5H}tFC&b)D+3?;&f^*yWeipd~VHa_i6cgnziaaYj(Cf-{q5Q zJZ{kjxx+;jSdBb)p53MSa_#b<&*7Ts)x6mrJ9#d5L4P%t3m&^S&+X#PIUe_%s$v~p zDxU53c*VCu8(r*k-3GN$QBf>w>?Q(JDjyC039ZtK=qGjfUYfihejXRCn zOGPs250A_4OVNCu`I>d6)#1{dR`M#J((I4s**y-sE89-Zr6OK$zTLPpMauJ8{n_CL zOK~>)JiC)tdMs)ZU6^Z~N1nrb^R?BRWl;m&bLQl`9NE7z9+h<;WcSeuZy$|s&<}KsO5L7FGI(e!(wQ{T+E{1%LQ3cxPa{J72wP+LE z`A%DS1f7oAw4V|8x%0FFw`Vq^SNS4;`1h;+YPi*9oq2Q4nB{w=C+>VJ<8HINd=9JA zt63SDTgTVR3~)MJv*q0$t+J-!;kk2C`Ty;#8Yab5&hiXnetK1w-N}r(bLOfc;W;Bs zb-Jy#ss}5pP_47V6HzKU_gVV9xKaXX#v0$EJA%_n1yTWvOGqulQePZt?c;}IUV zXe09H%%CWDj>e4+S*S9hZ7RcN)2wVLJlNQLGQoH(IA}~%#IQ`J7?;D_MtCOJ>^ata zr%#*VaTiGCn7LX;zAxA9aV!YW;G46<;mws%lojf5<=gw~W3sM||E)&eGOX2GI4;k| zZea98wVPD+`~*E~Y0g%J6VbD87zMn ztD|a-_R%fl7d9T{ar>CSW~b{#mA@?;8FeBluzSsE3w)pp`V}nRE)X9$daak2^r>WpY}+zno*-X zj(IXvW)1bZ)@E!B_Uv3OYrcz@dL0XxFvgc<7GF{}ZHT-((J~=pd^gMRQQf;)Mn3RB zQnd}h;bN*|<59w@GeCWtBV)ByxR5nK=#FccS5K(kSCBS2A66x1@8XSro*X0K&Z zE73+J%f_DCqL$iNF4dTAPK#J-*`{VWqSe^%&pmuTozLgb_niCQz4vVQ0#`1&HofKY zo^BTSxS?Dot;vQUW3pV-o9L)91DhK&69?fed>uz)e5f(4a5=`{Cj1*7!>)J6cS~?wk8ynF+i-mX@JK;z! zl8Vc)J08GdtVVsFPjB1dT8zNm7|#98hg61g;72UNRHlKRnT^;DFX2Fp=ByrAiaK72 zDaR5f)J-8H~L4Dqi`B#VfV>8^0OrqI?3HUx{;qN#H2XYZFgEMbXk)LCI ztj1f=&4TGdVW{m8i@H${?2LJ+rC5#H1M9E@9zu=uqBV#Q*$&2ox=uILKr&;Pe@*S( z9GHR!P%{zN-nsB(452+6b%ABbzA@{qFI$gV&seV^gEYahPKT3`)i5Jb9iEMI@xEB* zzdn^}4yb4DIA`R|P*W6*Ik*8A;ZLZ&QNl2jaW87bm-tbJt>T>-d)Qim>i8~XpPFN+ z0iDAvyy~T*5%pz(l3d1&LQTy>9i0>RVQ< z3e|zvQA>9M^}Q>o`*`c{Q)|@?bs(D`I&m>-PwYV5;5atHKd=ZJbCAg}Wqn4^i7pKD+sF96D zomYlD!DcOTZSx}X;FvS04h3{|Iue3f$`;r_&;Jutbb$)g7Y|t9L-qJG494#<7q6p6 zl*LOx-ye>lI01Ehjy=8r8_-^9k3WVysb)QD#^1z7+~1s{q6?fyX4m|NEVqg0HKrTn zS&LB}nuGe@GSm(Cphmjiw$Gqu>RT+upY8Gd6z6)ySkqDTs8ft_GP#r#wJi+D$YO}UYb-tg7`d%jLyj0bipEiXt!6QI`{?Nf)}wFy8Ai5 z6+=-=nS)HKnU4|ZeTB+oDj%ap-n+lk!%3))mE#gzh>Osb>3nZ7Y6&V)-#dcC@Fd1# z8+xf39EP#D5L@C_Y>fwz4tULJD(dO)cq=v;Xbk(##GyJ=fSqs^w!wY&_-Txy9W;o2 zfC<A{tCt($i#mvFR1mb3FsONthl`szMLQUOq496-g!fK4f@w}Bva1rWz zKcGf*6PshpAc)Mr0p5XLRVGl;5=_N-T!=iv=6Tc! z=P(Fwpl;}9e$}BysPjXu9%~0{H)~(i(hk88oNT=(oB7uWS8`w!u0gHM1=LjigpKez zGD)Uxj=0^mzKc!pGO9zydxtY6 zq3EFs-vAo@(T$wF%O?Yjr1yNU;%eJ=LMs# z=MAHhLZuU`XT^96-h+C4R-rmliD`Jk8uE8%WH}hj@iEvGr=ZTCZ`*4yhIR#d@F;5Y zevizM*ZfRHyZ`^F5f!izbb)E83wlwTW-V%}H=#zd1GVO_p)Pm|wJEDm9lC(pOV?0K z@jL4J^+!A3Z;G*c{==zgif+edn1f+hfExL|sI{xW=6D#jrk`Obokz4#yHoB zLftS0)q!*zfJ0FoejMxQ`QJdL=J`flcsuHO-i;dh2dFjv8r7j_7FHb|it0cf>ISn= zn{p9qW|pG%%4XDc-bS7GDe6Aw(W@I@vIna1F4}J1lj&H5Jb7jdhU5P*5}S;3M%D?n zB;BzkW?*|9hgrBBwNxju1%8Pg@G7dKZN@YI>Uqp~XUe;vMwEr>SUzeGOtqgcKrMj} z)uC;usXU04ctoGOc=u0mHuH*!&Pbm`#%3x|9r**}Ff^a}*VLrvJ169#dX|ss`DE1R zW!MUrpc|h>7ygGlNmTk0-Ye!?H7MK2KWb{uf4m#0ceU+W-qiZHAu)tU*o-6rgomN# zf4NfBh#w~}kxS&S<-fL}S-eQ*+cs}%^CC$jONmCU&8?z6q@umxoym{)$!W5S+)q>< zAU$hp&X4`Jo{TTsdUsrH>;FXU{=XJYvDW)(GL3X2CrKZo^0l6Sm2&bRDI)(OwI!X( zZjwNHlY5CCp-7^#*}+udAzQEIsI7cvU5SH94>H^y8?E~;;Njl@_bxYkt_7Ii1B#Epi#UzoO zA#CuP|7?R$WjX0zQ*(YpzCry1+ZKruz?_umI}$R=it=YqDV>qf zFMoDXswXKiDJdbbb3$U0Cn-6#bC(W@JrWar3v*gFs6CdHtC6$QI|?@jvEcV%+8uU&DB ze`9f_%Qt`Oy}rO{Tm46;tqAn5pBd@$)hqkr(egF^t>sMvLi)}roKot^o>4N-(|%}9 Oa;)#?xe)=`S>{jg39}&p diff --git a/locale/en-us/LC_MESSAGES/messages.po b/locale/en-us/LC_MESSAGES/messages.po index 98032c7..51e3665 100644 --- a/locale/en-us/LC_MESSAGES/messages.po +++ b/locale/en-us/LC_MESSAGES/messages.po @@ -7,17 +7,17 @@ msgid "" msgstr "" "Project-Id-Version: TCV\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-01-02 23:14+0900\n" -"PO-Revision-Date: 2022-01-02 23:22+0900\n" +"POT-Creation-Date: 2022-02-10 15:28+0900\n" +"PO-Revision-Date: 2022-02-10 16:25+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 1.8.13\n" +"X-Generator: Poedit 3.0.1\n" -#: AppBase.py:45 +#: AppBase.py:48 msgid "" "ログ機能の初期化に失敗しました。下記のファイルへのアクセスが可能であることを" "確認してください。" @@ -25,6 +25,18 @@ msgstr "" "Failed to initialize logger. Make sure you have permission to access to the " "following file:" +#: AppBase.py:57 +msgid "音声エンジンエラー" +msgstr "Speech Synthesizer Error" + +#: AppBase.py:57 +msgid "" +"音声読み上げ機能の初期化に失敗したため、読み上げ機能を使用できません。出力先" +"の変更をお試しください。" +msgstr "" +"Failed to initialize speech function. You cannot use speech. Please try to " +"change speech output." + #: keymapHandlerBase.py:696 keymapHandlerBase.py:726 #, python-format msgid "%s は存在しないキーです。" @@ -111,31 +123,55 @@ msgstr "Delete access token" msgid "アクセストークンを設定(&T)" msgstr "Set up access &token" -#: menuItemsDic.py:28 +#: menuItemsDic.py:28 menuItemsDic.py:35 msgid "通知対象ユーザの管理(&M)" msgstr "&Manage Notified Users" #: menuItemsDic.py:29 +msgid "Twitter スペース(&S)" +msgstr "Twitter &Spaces" + +#: menuItemsDic.py:30 +msgid "Twitter スペースとの連携機能を有効化(&E)" +msgstr "&Enable integration with Twitter Spaces" + +#: menuItemsDic.py:31 +msgid "フォロー中のユーザをすべて追加(&F)" +msgstr "Add all users you &follow" + +#: menuItemsDic.py:32 +msgid "ユーザ情報の更新(&I)" +msgstr "Update user &information" + +#: menuItemsDic.py:33 +msgid "スペースの&URLを指定して録画" +msgstr "Record By &URL" + +#: menuItemsDic.py:34 +msgid "連携アカウントの管理(&A)" +msgstr "Manage Linked &Accounts" + +#: menuItemsDic.py:36 msgid "設定(&S)" msgstr "&Settings" -#: menuItemsDic.py:30 +#: menuItemsDic.py:37 msgid "キーボードショートカットの設定(&K)" msgstr "Shortcut &Key Configuration" -#: menuItemsDic.py:31 +#: menuItemsDic.py:38 msgid "グローバルホットキーの設定(&H)" msgstr "Global &Hot Key Configuration" -#: menuItemsDic.py:32 +#: menuItemsDic.py:39 msgid "Windows起動時の自動起動を有効化(&W)" msgstr "Enable automatic startup on &Windows startup" -#: menuItemsDic.py:33 +#: menuItemsDic.py:40 msgid "最新バージョンを確認(&U)" msgstr "Check For &Updates" -#: menuItemsDic.py:34 +#: menuItemsDic.py:41 msgid "バージョン情報(&V)" msgstr "&Version Info" @@ -144,11 +180,11 @@ msgstr "&Version Info" msgid "配信開始:%s" msgstr "Broadcasting Started: %s" -#: recorder.py:145 recorder.py:147 recorder.py:158 recorder.py:174 +#: recorder.py:146 recorder.py:148 recorder.py:159 recorder.py:175 msgid "録画エラー" msgstr "Recording error" -#: recorder.py:145 +#: recorder.py:146 msgid "" "録画の開始に失敗しました。録画の保存先が適切に設定されていることを確認してく" "ださい。定期的に再試行する場合は[はい]、処理を中断する場合は[いいえ]を選択し" @@ -160,66 +196,67 @@ msgstr "" "interrupt the process. You may be able to start recording correctly by " "selecting Yes and changing where the recording is saved." -#: recorder.py:147 recorder.py:158 +#: recorder.py:148 recorder.py:159 #, python-format msgid "%sのライブの録画処理を中断しました。" msgstr "Recording process for %s's broadcasting has been canseled." -#: recorder.py:161 +#: recorder.py:162 msgid "録画開始" msgstr "Start recording" -#: recorder.py:161 recorder.py:186 sources\twitcasting.py:702 -#: sources\twitcasting.py:714 +#: recorder.py:162 recorder.py:187 sources\twitcasting.py:712 +#: sources\twitcasting.py:724 #, python-format msgid "ユーザ:%(user)s、ムービーID:%(movie)s" msgstr "User: %(user)s, Movie ID:%(movie)s" -#: recorder.py:162 +#: recorder.py:163 msgid "録画中" msgstr "Recording" -#: recorder.py:174 +#: recorder.py:175 #, python-format msgid "%sのライブを録画中にエラーが発生したため、再度録画を開始します。" msgstr "An error has occured while recording %s's live. retrying..." -#: recorder.py:174 +#: recorder.py:175 #, python-format msgid "詳細:%s" msgstr "Details: %s" -#: recorder.py:186 +#: recorder.py:187 msgid "録画終了" msgstr "End of recording" -#: twitterService.py:32 sources\twitcasting.py:262 +#: twitterService.py:32 sources\spaces.py:515 sources\twitcasting.py:272 msgid "" "認証に成功しました。このウィンドウを閉じて、アプリケーションに戻ってくださ" "い。" msgstr "" "Authorization successful. Close this window and go back to the application." -#: twitterService.py:33 sources\twitcasting.py:263 +#: twitterService.py:33 sources\spaces.py:516 sources\twitcasting.py:273 msgid "認証に失敗しました。もう一度お試しください。" msgstr "Authorization failed. Please try again." -#: twitterService.py:34 sources\twitcasting.py:264 +#: twitterService.py:34 sources\spaces.py:517 sources\twitcasting.py:274 msgid "" "しばらくしても画面が切り替わらない場合は、別のブラウザでお試しください。" msgstr "" "If the screen does not change after a while, open this page in another " "browser." -#: twitterService.py:38 +#: twitterService.py:38 sources\spaces.py:521 msgid "Twitterアカウント認証" msgstr "Twitter Account authentication" -#: twitterService.py:47 sources\twitcasting.py:277 sources\twitcasting.py:289 +#: twitterService.py:47 sources\spaces.py:533 sources\twitcasting.py:287 +#: sources\twitcasting.py:299 msgid "処理結果" msgstr "result" -#: twitterService.py:47 sources\twitcasting.py:277 +#: twitterService.py:47 sources\spaces.py:533 sources\twitcasting.py:287 msgid "キャンセルされました。" msgstr "Canceled." @@ -293,14 +330,159 @@ msgstr "" "Download has successfully finished.\n" "Update will be started when closing the application." -#: sources\twitcasting.py:42 -msgid "ツイキャス" -msgstr "TwitCasting" +#: sources\spaces.py:32 +msgid "Twitter スペース" +msgstr "Twitter Spaces" -#: sources\twitcasting.py:54 sources\twitcasting.py:214 +#: sources\spaces.py:38 sources\spaces.py:230 sources\twitcasting.py:54 +#: sources\twitcasting.py:224 msgid "未接続" msgstr "Disconnected" +#: sources\spaces.py:57 sources\spaces.py:63 sources\spaces.py:387 +msgid "Twitterアカウントの連携" +msgstr "Link Twitter Accounts" + +#: sources\spaces.py:57 +msgid "" +"Twitter スペースを使用する前に、使用するTwitterアカウントを設定する必要があり" +"ます。今すぐ設定画面を開きますか?" +msgstr "" +"Before you use Twitter space, you'll need to set up a Twitter account to " +"use. Do you want to open the settings screen now?" + +#: sources\spaces.py:63 +msgid "" +"Twitterアカウント情報の読み込みに失敗しました。再度アカウントの連携を行ってく" +"ださい。今すぐ設定画面を開きますか?" +msgstr "" +"Failed to load Twitter account information. Please link your account again. " +"Do you want to open the settings screen now?" + +#: sources\spaces.py:99 sources\spaces.py:192 views\main.py:245 +#: views\main.py:382 views\main.py:388 +msgid "完了" +msgstr "Finish" + +#: sources\spaces.py:99 +#, python-format +msgid "フォロー中のユーザの追加が完了しました。追加件数:%d" +msgstr "You have added users you are following. Number of additions: %d" + +#: sources\spaces.py:117 +#, python-format +msgid "フォロー中のユーザの取得に失敗しました。詳細:%s" +msgstr "Failed to retrieve user being followed. Details: %s" + +#: sources\spaces.py:151 sources\twitcasting.py:211 +msgid "接続完了" +msgstr "Connection completed" + +#: sources\spaces.py:151 +msgid "スペースの監視を開始しました。" +msgstr "You have started monitoring Spaces." + +#: sources\spaces.py:154 sources\twitcasting.py:214 +msgid "接続済み" +msgstr "Connected" + +#: sources\spaces.py:174 sources\spaces.py:181 +#, python-format +msgid "ユーザ情報の更新に失敗しました。詳細:%s" +msgstr "Failed to update user information. Details: %s" + +#: sources\spaces.py:191 +msgid "以下のユーザの情報を取得できませんでした。" +msgstr "The following users could not obtain information:" + +#: sources\spaces.py:192 +msgid "ユーザ情報の更新が完了しました。" +msgstr "The user information has been updated." + +#: sources\spaces.py:228 sources\twitcasting.py:202 sources\twitcasting.py:223 +msgid "切断" +msgstr "disconnected" + +#: sources\spaces.py:228 +msgid "Twitterとの接続を切断しました。" +msgstr "You have disconnected from Twitter." + +#: sources\spaces.py:257 +msgid "Twitter エラー" +msgstr "Twitter Error" + +#: sources\spaces.py:257 +#, python-format +msgid "Twitterとの通信中にエラーが発生しました。詳細:%s" +msgstr "An error occurred while communicating with Twitter. Details: %s" + +#: sources\spaces.py:379 +msgid "" +"Twitterとの接続に失敗しました。インターネット接続に問題がない場合は、しばらく" +"たってから再度お試しください。この問題が再度発生する場合は、開発者までお問い" +"合わせください。" +msgstr "" +"Connection with Twitter failed. If you're connected to the Internet, try " +"again later. If you experience this issue again, contact to the developer." + +#: sources\spaces.py:381 +msgid "Twitterからの応答が不正です。開発者までご連絡ください。" +msgstr "The response from Twitter is incorrect. Please contact the developer." + +#: sources\spaces.py:387 +msgid "" +"Twitterアカウントの認証情報が正しくありません。再度アカウントの連携を行ってく" +"ださい。今すぐ設定画面を開きますか?" +msgstr "" +"Your Twitter account credentials are incorrect. Please link your account " +"again. Do you want to open the settings screen now?" + +#: sources\spaces.py:404 +#, python-format +msgid "Twitterとの通信に失敗しました。詳細:%s" +msgstr "Communication with Twitter failed. Details: %s" + +#: sources\spaces.py:409 +#, python-format +msgid "ユーザ情報の取得に失敗しました。詳細:%s" +msgstr "Failed to retrieve user information. Details: %s" + +#: sources\spaces.py:500 +msgid "認証情報の保存に失敗しました。" +msgstr "Failed to save credentials." + +#: sources\spaces.py:644 sources\twitcasting.py:333 +msgid "ユーザ情報の保存に失敗しました。" +msgstr "Failed to save user information." + +#: sources\spaces.py:686 views\globalKeyConfig.py:20 +#: views\globalKeyConfig.py:74 views\spacesManageUser.py:20 +#: views\spacesManageUser.py:102 views\spacesTokenManager.py:36 +#: views\tcManageUser.py:21 views\tcManageUser.py:100 +msgid "名前" +msgstr "name" + +#: sources\spaces.py:688 views\main.py:221 views\main.py:228 +#: views\spacesManageUser.py:19 views\spacesManageUser.py:101 +#: views\spacesTokenManager.py:35 views\tcManageUser.py:20 +#: views\tcManageUser.py:99 +msgid "ユーザ名" +msgstr "user name" + +#: sources\spaces.py:692 +#, python-format +msgid "%(field)s変更" +msgstr "%(field)s changed" + +#: sources\spaces.py:693 sources\twitcasting.py:183 sources\twitcasting.py:186 +#, python-format +msgid "「%(old)s」→「%(new)s」" +msgstr "%(old)s -> %(new)s" + +#: sources\twitcasting.py:42 +msgid "ツイキャス" +msgstr "TwitCasting" + #: sources\twitcasting.py:89 msgid "アクセストークンの有効期限が切れています" msgstr "Access token has expired" @@ -331,69 +513,52 @@ msgstr "" "The access token is about to expire. To continue using the software, you " "need to set the access token again." -#: sources\twitcasting.py:139 +#: sources\twitcasting.py:148 msgid "配信開始" msgstr "broadcasting started" -#: sources\twitcasting.py:146 +#: sources\twitcasting.py:155 msgid "録画対象の削除" msgstr "Delete recording target" -#: sources\twitcasting.py:146 sources\twitcasting.py:147 +#: sources\twitcasting.py:155 sources\twitcasting.py:156 #, python-format msgid "%sのライブを、録画対象から削除しました。" msgstr "%s has been deleted from recording target." -#: sources\twitcasting.py:174 +#: sources\twitcasting.py:183 msgid "ユーザ名変更" msgstr "Username changed" -#: sources\twitcasting.py:174 sources\twitcasting.py:177 -#, python-format -msgid "「%(old)s」→「%(new)s」" -msgstr "%(old)s -> %(new)s" - -#: sources\twitcasting.py:177 +#: sources\twitcasting.py:186 msgid "名前変更" msgstr "name changed" -#: sources\twitcasting.py:193 sources\twitcasting.py:213 -msgid "切断" -msgstr "disconnected" - -#: sources\twitcasting.py:193 +#: sources\twitcasting.py:202 msgid "インターネット接続が切断されました。再試行します。" msgstr "Your Internet connection has been disconnected. Trying again." -#: sources\twitcasting.py:194 +#: sources\twitcasting.py:203 msgid "接続試行中" msgstr "Attempting to connect" -#: sources\twitcasting.py:202 -msgid "接続完了" -msgstr "Connection completed" - -#: sources\twitcasting.py:202 +#: sources\twitcasting.py:211 msgid "新着ライブの監視を開始しました。" msgstr "We have started monitoring new lives." -#: sources\twitcasting.py:205 -msgid "接続済み" -msgstr "Connected" - -#: sources\twitcasting.py:213 +#: sources\twitcasting.py:223 msgid "ツイキャスとの接続を切断しました。" msgstr "Disconnected from TwitCasting." -#: sources\twitcasting.py:218 +#: sources\twitcasting.py:228 msgid "再接続" msgstr "reconnect" -#: sources\twitcasting.py:218 +#: sources\twitcasting.py:228 msgid "ツイキャスとの接続が切断されたため、再度接続します。" msgstr "Connection to twitcasting has been deleted. Retrying." -#: sources\twitcasting.py:245 +#: sources\twitcasting.py:255 msgid "" "インターネット接続に失敗しました。現在ツイキャスとの連携機能を使用できませ" "ん。" @@ -401,35 +566,31 @@ msgstr "" "Internet connection failed. The integration function with TwitCasting is not " "available at this time." -#: sources\twitcasting.py:268 +#: sources\twitcasting.py:278 views\spacesTokenManager.py:85 msgid "アカウントの追加" msgstr "add account" -#: sources\twitcasting.py:289 +#: sources\twitcasting.py:299 msgid "認証が完了しました。" msgstr "Authorization successful." -#: sources\twitcasting.py:323 -msgid "ユーザ情報の保存に失敗しました。" -msgstr "Failed to save user information." - -#: sources\twitcasting.py:352 +#: sources\twitcasting.py:362 msgid "指定したユーザが見つかりません。" msgstr "The specified user could not be found." -#: sources\twitcasting.py:464 +#: sources\twitcasting.py:474 msgid "合い言葉の入力" msgstr "Enter password" -#: sources\twitcasting.py:464 +#: sources\twitcasting.py:474 msgid "合い言葉" msgstr "password" -#: sources\twitcasting.py:547 +#: sources\twitcasting.py:557 msgid "アクセストークンが見つかりません" msgstr "Access token not found" -#: sources\twitcasting.py:547 +#: sources\twitcasting.py:557 msgid "" "利用可能なアクセストークンが見つかりません。ブラウザを起動し、認証作業を行い" "ますか?" @@ -437,7 +598,7 @@ msgstr "" "No available access tokens found. Do you want to start your browser and " "perform authentication work?" -#: sources\twitcasting.py:557 +#: sources\twitcasting.py:567 msgid "" "録画に失敗しました。録画が公開されていること、入力したURLに誤りがないことを確" "認してください。合い言葉を入力した場合は、入力した合い言葉に誤りがないことを" @@ -447,7 +608,7 @@ msgstr "" "you entered is correct. If you type password, make sure that the follower " "you typed is correct." -#: sources\twitcasting.py:566 +#: sources\twitcasting.py:576 msgid "" "ツイキャスAPIの実行回数が上限に達しました。しばらくたってから、再度実行してく" "ださい。" @@ -455,37 +616,37 @@ msgstr "" "The maximum number of runs of the TwitCasting API has been reached. Please " "try again after a while." -#: sources\twitcasting.py:568 +#: sources\twitcasting.py:578 msgid "" "ツイキャスAPIが500エラーを返しました。しばらく待ってから、再度接続してくださ" "い。" msgstr "" "TwitCasting API returned a 500 error. Wait a while and try to connect again." -#: sources\twitcasting.py:570 +#: sources\twitcasting.py:580 msgid "現在ツイキャスとの連携機能を使用できません。開発者に連絡してください。" msgstr "You cannot use TwitCasting functions. Please contact the developer." -#: sources\twitcasting.py:583 +#: sources\twitcasting.py:593 #, python-format msgid "ツイキャスAPIとの通信中にエラーが発生しました。詳細:%s" msgstr "" "An error has occured during the communication between TwitCasting. Details: " "%s" -#: sources\twitcasting.py:618 -msgid "このユーザはすでに登録されています。" -msgstr "This user is already registered." - -#: sources\twitcasting.py:621 +#: sources\twitcasting.py:627 msgid "このユーザのライブはすでに録画中です。" msgstr "This user's live is already recorded." -#: sources\twitcasting.py:635 +#: sources\twitcasting.py:631 +msgid "このユーザはすでに登録されています。" +msgstr "This user is already registered." + +#: sources\twitcasting.py:645 msgid "ユーザ名を指定して録画" msgstr "Record with username" -#: sources\twitcasting.py:635 +#: sources\twitcasting.py:645 #, python-format msgid "" "%sを、録画対象として追加しました。この登録は一定時間経過後に自動で削除されま" @@ -494,11 +655,11 @@ msgstr "" "%s added a recording target. This registration is automatically deleted " "after a certain amount of time." -#: sources\twitcasting.py:660 +#: sources\twitcasting.py:670 msgid "対象ユーザの指定" msgstr "specify users" -#: sources\twitcasting.py:660 +#: sources\twitcasting.py:670 msgid "" "フォロー中のユーザを取得するアカウントの@からはじまるアカウント名を入力してく" "ださい。\n" @@ -512,74 +673,74 @@ msgstr "" "Or you can specify a private account that the account you used to " "authenticate follows." -#: sources\twitcasting.py:702 +#: sources\twitcasting.py:712 msgid "コメント保存開始" msgstr "Start saving comments" -#: sources\twitcasting.py:714 +#: sources\twitcasting.py:724 msgid "コメント保存終了" msgstr "End of comment save" -#: sources\twitcasting.py:821 sources\twitcasting.py:824 -#: sources\twitcasting.py:829 +#: sources\twitcasting.py:831 sources\twitcasting.py:834 +#: sources\twitcasting.py:839 msgid "ユーザ情報の更新" msgstr "Update user information" -#: sources\twitcasting.py:821 +#: sources\twitcasting.py:831 msgid "ユーザ情報の更新を開始します。" msgstr "Start updating user information." -#: sources\twitcasting.py:824 +#: sources\twitcasting.py:834 #, python-format msgid "%sの情報を取得しています。" msgstr "getting the information of %s" -#: sources\twitcasting.py:829 +#: sources\twitcasting.py:839 msgid "ユーザ情報の更新が終了しました。" msgstr "The user information has been updated." -#: sources\twitcasting.py:845 +#: sources\twitcasting.py:855 msgid "一括追加" msgstr "Add in a lump" -#: sources\twitcasting.py:849 +#: sources\twitcasting.py:859 msgid "処理を開始します。" msgstr "Operation Started" -#: sources\twitcasting.py:869 +#: sources\twitcasting.py:879 #, python-format msgid "%sを追加しました。" msgstr "Added %s." -#: sources\twitcasting.py:876 +#: sources\twitcasting.py:886 msgid "処理が終了しました。" msgstr "Operation finished." -#: sources\twitcasting.py:921 sources\twitcasting.py:924 -#: sources\twitcasting.py:928 sources\twitcasting.py:933 -#: sources\twitcasting.py:936 +#: sources\twitcasting.py:931 sources\twitcasting.py:934 +#: sources\twitcasting.py:938 sources\twitcasting.py:943 +#: sources\twitcasting.py:946 msgid "一括録画" msgstr "Record All" -#: sources\twitcasting.py:921 +#: sources\twitcasting.py:931 msgid "ライブ一覧を取得しています。" msgstr "Retrieving movie list." -#: sources\twitcasting.py:924 +#: sources\twitcasting.py:934 #, python-format msgid "処理を開始します。対象ライブ数:%i" msgstr "Operation started. Number of movies: %i" -#: sources\twitcasting.py:928 +#: sources\twitcasting.py:938 #, python-format msgid "処理中(%(index)i/%(total)i)" msgstr "processing (%(index)i/%(total)i)" -#: sources\twitcasting.py:933 +#: sources\twitcasting.py:943 msgid "ファイルが既に存在するため、録画をスキップします。" msgstr "Recording will be skipped because the file exists." -#: sources\twitcasting.py:936 +#: sources\twitcasting.py:946 #, python-format msgid "完了。%i件録画しました。" msgstr "Operation finished. Downloaded %i movies." @@ -588,9 +749,10 @@ msgstr "Operation finished. Downloaded %i movies." msgid "ブラウザでの操作を待っています..." msgstr "waiting for browser..." -#: views\auth.py:24 views\KeyValueSettingDialogBase.py:81 -#: views\KeyValueSettingDialogBase.py:262 views\settingsDialog.py:96 -#: views\SimpleInputDialog.py:38 views\updateDialog.py:45 +#: views\auth.py:24 views\chooseTwitterAccount.py:32 +#: views\KeyValueSettingDialogBase.py:84 views\KeyValueSettingDialogBase.py:275 +#: views\settingsDialog.py:98 views\SimpleInputDialog.py:40 +#: views\updateDialog.py:45 msgid "キャンセル" msgstr "cancel" @@ -609,12 +771,25 @@ msgstr "" "combinations and key names.\n" #: views\base.py:117 views\fontManager.py:25 views\fontManager.py:50 -#: views\globalKeyConfig.py:61 views\KeyValueSettingDialogBase.py:282 -#: views\KeyValueSettingDialogBase.py:333 views\main.py:89 +#: views\globalKeyConfig.py:61 views\KeyValueSettingDialogBase.py:297 +#: views\KeyValueSettingDialogBase.py:348 views\main.py:90 #: views\updateDialog.py:66 msgid "エラー" msgstr "error" +#: views\chooseTwitterAccount.py:22 +msgid "Twitterアカウントを選択" +msgstr "Select a Twitter account" + +#: views\chooseTwitterAccount.py:29 views\spacesTokenManager.py:32 +msgid "アカウント" +msgstr "account" + +#: views\chooseTwitterAccount.py:31 views\KeyValueSettingDialogBase.py:83 +#: views\KeyValueSettingDialogBase.py:274 views\SimpleInputDialog.py:39 +msgid "OK" +msgstr "OK" + #: views\fontManager.py:25 msgid "" "設定されているフォント情報が不正です。デフォルトのフォントを適用します。" @@ -624,11 +799,6 @@ msgstr "Font configuration is invalid. Default settings will be applied." msgid "原因不明のエラーにより、フォントの変更に失敗しました。" msgstr "Font configuration has failed." -#: views\globalKeyConfig.py:20 views\globalKeyConfig.py:74 -#: views\tcManageUser.py:21 views\tcManageUser.py:100 -msgid "名前" -msgstr "name" - #: views\globalKeyConfig.py:21 msgid "ショートカット" msgstr "shortcut" @@ -637,7 +807,7 @@ msgstr "shortcut" msgid "識別子" msgstr "identifier" -#: views\globalKeyConfig.py:31 views\main.py:252 +#: views\globalKeyConfig.py:31 views\main.py:315 msgid "ショートカットキーの設定" msgstr "Shortcut Key Configuration" @@ -645,8 +815,8 @@ msgstr "Shortcut Key Configuration" #: views\globalKeyConfig.py:102 views\globalKeyConfig.py:107 #: views\globalKeyConfig.py:120 views\globalKeyConfig.py:128 #: views\globalKeyConfig.py:138 views\globalKeyConfig.py:143 -#: views\keyConfig.py:57 views\KeyValueSettingDialogBase.py:318 -#: views\main.py:344 views\main.py:363 +#: views\keyConfig.py:57 views\KeyValueSettingDialogBase.py:333 +#: views\main.py:408 views\main.py:427 msgid "なし" msgstr "None" @@ -681,7 +851,7 @@ msgstr "shortcut 4" msgid "ショートカット5" msgstr "shortcut 5" -#: views\globalKeyConfig.py:75 views\settingsDialog.py:39 +#: views\globalKeyConfig.py:75 views\settingsDialog.py:41 msgid "設定" msgstr "settings" @@ -722,56 +892,51 @@ msgstr "" msgid "設定解除" msgstr "remove registered configuration" -#: views\KeyValueSettingDialogBase.py:51 +#: views\KeyValueSettingDialogBase.py:54 msgid "現在の登録内容" msgstr "Current Settings" -#: views\KeyValueSettingDialogBase.py:72 +#: views\KeyValueSettingDialogBase.py:75 views\spacesTokenManager.py:46 msgid "追加(&A)" msgstr "&Add " -#: views\KeyValueSettingDialogBase.py:73 +#: views\KeyValueSettingDialogBase.py:76 msgid "変更(&M)" msgstr "&Modify" -#: views\KeyValueSettingDialogBase.py:75 +#: views\KeyValueSettingDialogBase.py:78 views\spacesTokenManager.py:47 msgid "削除(&D)" msgstr "&Delete" -#: views\KeyValueSettingDialogBase.py:80 views\KeyValueSettingDialogBase.py:261 -#: views\SimpleInputDialog.py:37 -msgid "OK" -msgstr "OK" - -#: views\KeyValueSettingDialogBase.py:122 -#: views\KeyValueSettingDialogBase.py:152 +#: views\KeyValueSettingDialogBase.py:125 +#: views\KeyValueSettingDialogBase.py:155 #, python-format msgid "この%sは既に登録されています。登録を上書きしますか?" msgstr "This %s is already registered. Do you wish to overwrite?" -#: views\KeyValueSettingDialogBase.py:122 -#: views\KeyValueSettingDialogBase.py:152 +#: views\KeyValueSettingDialogBase.py:125 +#: views\KeyValueSettingDialogBase.py:155 msgid "上書き確認" msgstr "Overwriting Confirmation" -#: views\KeyValueSettingDialogBase.py:282 +#: views\KeyValueSettingDialogBase.py:297 #, python-format msgid "%sを空白や半角の=を含む値に設定することはできません。" msgstr "%s cannot include space and equals sign." -#: views\KeyValueSettingDialogBase.py:322 +#: views\KeyValueSettingDialogBase.py:337 #, python-format msgid "%(v1)sと%(v2)sに同じショートカットキー%(key)sが設定されています。\n" msgstr "%(v1)s and %(v2)s have same shortcut key %(key)s.\n" -#: views\KeyValueSettingDialogBase.py:325 +#: views\KeyValueSettingDialogBase.py:340 #, python-format msgid "" "%(v1)s、%(v2)sなど計%(count)d箇所に同じショートカットキー%(key)sが設定されて" "います。\n" msgstr "%(key)s is registered at %(count)i items, %(v1)s, %(v2)s and more.\n" -#: views\KeyValueSettingDialogBase.py:329 +#: views\KeyValueSettingDialogBase.py:344 #, python-format msgid "" "設定されたショートカット%sが認識できません。お手数ですが、このエラーメッセー" @@ -780,7 +945,7 @@ msgstr "" "Configured shortcut %s cannot be recognized. Please contact the developer " "for further assistance.\n" -#: views\KeyValueSettingDialogBase.py:331 +#: views\KeyValueSettingDialogBase.py:346 #, python-format msgid "" "%(command)sに設定されたショートカットキー%(key)sは、別の場所で利用されていま" @@ -788,44 +953,44 @@ msgid "" msgstr "" "The key combination %(key)s, configured as %(command)s, is reserved.\\n\n" -#: views\main.py:47 +#: views\main.py:48 msgid "動作履歴" msgstr "operation History" -#: views\main.py:48 +#: views\main.py:49 msgid "タイトル" msgstr "title" -#: views\main.py:49 +#: views\main.py:50 msgid "詳細" msgstr "Details" -#: views\main.py:50 views\main.py:55 +#: views\main.py:51 views\main.py:56 msgid "サービス" msgstr "Service" -#: views\main.py:51 +#: views\main.py:52 msgid "日時" msgstr "Date" -#: views\main.py:54 +#: views\main.py:55 msgid "動作状況" msgstr "Operating status" -#: views\main.py:56 +#: views\main.py:57 msgid "状態" msgstr "Condition" -#: views\main.py:58 +#: views\main.py:59 msgid "準備完了" msgstr "Ready" -#: views\main.py:58 +#: views\main.py:59 #, python-format msgid "%sを起動しました。" msgstr "%s launched." -#: views\main.py:86 +#: views\main.py:87 msgid "" "で設定されたホットキーが正しくありません。キーの重複、存在しないキー名の指" "定、使用できないキーパターンの指定などが考えられます。以下のキーの設定内容を" @@ -836,70 +1001,86 @@ msgstr "" "and key names.\n" "\n" -#: views\main.py:143 +#: views\main.py:155 msgid "ファイル(&F)" msgstr "&File" -#: views\main.py:144 +#: views\main.py:156 msgid "サービス(&S)" msgstr "&Services" -#: views\main.py:145 +#: views\main.py:157 msgid "オプション(&O)" msgstr "&Options" -#: views\main.py:146 +#: views\main.py:158 msgid "ヘルプ(&H)" msgstr "&Help" -#: views\main.py:198 +#: views\main.py:196 views\main.py:202 views\main.py:271 views\main.py:432 +#: views\settingsDialog.py:174 +msgid "" +"設定の保存に失敗しました。下記のファイルへのアクセスが可能であることを確認し" +"てください。" +msgstr "" +"Failed to save settings. Make sure you can access to the following files:" + +#: views\main.py:214 views\main.py:283 msgid "URLを入力" msgstr "Enter URL" -#: views\main.py:198 +#: views\main.py:214 msgid "再生ページのURL" msgstr "Playback page URL" -#: views\main.py:205 views\main.py:212 +#: views\main.py:221 views\main.py:228 msgid "ユーザ名を入力" msgstr "Enter user name" -#: views\main.py:205 views\main.py:212 views\tcManageUser.py:20 -#: views\tcManageUser.py:99 -msgid "ユーザ名" -msgstr "user name" - -#: views\main.py:220 +#: views\main.py:236 msgid "すでに削除されています。" msgstr "It has already been deleted." -#: views\main.py:222 +#: views\main.py:238 msgid "アクセストークンの削除" msgstr "Delete access token" -#: views\main.py:222 +#: views\main.py:238 msgid "" "ツイキャス連携機能を無効化し、アクセストークンを削除します。よろしいですか?" msgstr "" "Disable TwitCasting functions and remove the access token. Are you sure?" -#: views\main.py:229 views\main.py:318 views\main.py:324 -msgid "完了" -msgstr "Finish" - -#: views\main.py:229 +#: views\main.py:245 msgid "アクセストークンを削除しました。" msgstr "Removed the access token." -#: views\main.py:260 +#: views\main.py:283 +msgid "スペースのURL" +msgstr "Space URL" + +#: views\main.py:288 +msgid "このスペースは既に終了しています。" +msgstr "This space has already ended." + +#: views\main.py:290 +msgid "入力されたURLが正しくありません。" +msgstr "The URL entered is incorrect." + +#: views\main.py:292 +msgid "このスペースはまだ開始されていません。" +msgstr "This space has not yet started." + +#: views\main.py:323 msgid "グローバルホットキーの設定" msgstr "Global Hot Keys Settings" -#: views\main.py:299 views\main.py:315 +#: views\main.py:363 views\main.py:379 views\spacesTokenManager.py:109 +#: views\spacesTokenManager.py:116 msgid "確認" msgstr "confirmation" -#: views\main.py:299 +#: views\main.py:363 msgid "" "録画処理を実行中です。このまま終了すると、録画は中断されます。終了してもよろ" "しいですか?" @@ -907,182 +1088,238 @@ msgstr "" "Recording process is running. If you exit, the recording will be " "interrupted. Are you sure?" -#: views\main.py:315 +#: views\main.py:379 msgid "Windows起動時の自動起動はすでに設定されています。設定を解除しますか?" msgstr "" "Automatic startup at Windows startup is already set. Do you want to un-set " "it?" -#: views\main.py:318 +#: views\main.py:382 msgid "Windows起動時の自動起動を無効化しました。" msgstr "Disabled automatic startup at Windows startup." -#: views\main.py:324 +#: views\main.py:388 msgid "Windows起動時の自動起動を設定しました。" msgstr "Set automatic startup at Windows startup." -#: views\settingsDialog.py:23 +#: views\settingsDialog.py:25 msgid "標準" msgstr "normal" -#: views\settingsDialog.py:24 +#: views\settingsDialog.py:26 msgid "ダーク" msgstr "Dark" -#: views\settingsDialog.py:28 +#: views\settingsDialog.py:30 msgid "動画(TS)" msgstr "Video (TS)" -#: views\settingsDialog.py:29 +#: views\settingsDialog.py:31 msgid "動画(MP4)" msgstr "Video (MP4)" -#: views\settingsDialog.py:30 +#: views\settingsDialog.py:32 msgid "音声のみ(MP3)" msgstr "Audio Only (MP3)" -#: views\settingsDialog.py:50 +#: views\settingsDialog.py:52 msgid "カテゴリ選択" msgstr "category" -#: views\settingsDialog.py:53 +#: views\settingsDialog.py:55 msgid "一般" msgstr "general" -#: views\settingsDialog.py:54 +#: views\settingsDialog.py:56 msgid "起動時にウィンドウを隠す(&H)" msgstr "&Hide window at startup" -#: views\settingsDialog.py:55 +#: views\settingsDialog.py:57 msgid "終了時にタスクトレイに最小化(&M)" msgstr "&Minimize to task tray at exit" -#: views\settingsDialog.py:58 +#: views\settingsDialog.py:60 msgid "表示/言語" msgstr "Display/Language" -#: views\settingsDialog.py:59 +#: views\settingsDialog.py:61 msgid "言語(&L)" msgstr "&Language" -#: views\settingsDialog.py:60 +#: views\settingsDialog.py:62 msgid "画面表示モード(&D)" msgstr "&Display mode" -#: views\settingsDialog.py:63 +#: views\settingsDialog.py:65 msgid "通知" msgstr "notification" -#: views\settingsDialog.py:64 views\tcManageUser.py:23 +#: views\settingsDialog.py:66 views\spacesManageUser.py:22 +#: views\spacesManageUser.py:104 views\tcManageUser.py:23 #: views\tcManageUser.py:102 msgid "バルーン通知" msgstr "Balloon notification" -#: views\settingsDialog.py:65 views\settingsDialog.py:74 +#: views\settingsDialog.py:67 views\settingsDialog.py:76 +#: views\spacesManageUser.py:23 views\spacesManageUser.py:105 #: views\tcManageUser.py:24 views\tcManageUser.py:103 msgid "録画" msgstr "Record" -#: views\settingsDialog.py:66 views\tcManageUser.py:25 +#: views\settingsDialog.py:68 views\spacesManageUser.py:24 +#: views\spacesManageUser.py:106 views\tcManageUser.py:25 #: views\tcManageUser.py:104 msgid "ブラウザで開く" msgstr "Open in browser" -#: views\settingsDialog.py:67 views\tcManageUser.py:26 +#: views\settingsDialog.py:69 views\spacesManageUser.py:25 +#: views\spacesManageUser.py:107 views\tcManageUser.py:26 #: views\tcManageUser.py:105 msgid "サウンドを再生" msgstr "Play sound" -#: views\settingsDialog.py:69 views\tcManageUser.py:27 +#: views\settingsDialog.py:71 views\spacesManageUser.py:26 +#: views\spacesManageUser.py:108 views\tcManageUser.py:27 #: views\tcManageUser.py:106 msgid "再生するサウンド" msgstr "Sounds to play" -#: views\settingsDialog.py:71 views\settingsDialog.py:78 -#: views\tcManageUser.py:108 +#: views\settingsDialog.py:73 views\settingsDialog.py:80 +#: views\spacesManageUser.py:111 views\tcManageUser.py:108 msgid "参照" msgstr "browse" -#: views\settingsDialog.py:76 +#: views\settingsDialog.py:78 msgid "保存先フォルダ(&F)" msgstr "Destination &folder" -#: views\settingsDialog.py:80 +#: views\settingsDialog.py:82 msgid "ユーザごとにサブフォルダを作成(&S)" msgstr "Create &sub folders for each user" -#: views\settingsDialog.py:81 +#: views\settingsDialog.py:83 msgid "保存ファイル名(&N)" msgstr "Save file &name" -#: views\settingsDialog.py:83 +#: views\settingsDialog.py:85 msgid "ファイル形式(&T)" msgstr "File &Type" -#: views\settingsDialog.py:86 +#: views\settingsDialog.py:88 msgid "ネットワーク" msgstr "network" -#: views\settingsDialog.py:87 +#: views\settingsDialog.py:89 msgid "起動時に更新を確認(&U)" msgstr "Automatically Check for &Updates on startup" -#: views\settingsDialog.py:88 +#: views\settingsDialog.py:90 msgid "プロキシサーバーの情報を手動で設定する(&M)" msgstr "&Manually configure proxy information" -#: views\settingsDialog.py:89 +#: views\settingsDialog.py:91 msgid "サーバーURL" msgstr "server URL" -#: views\settingsDialog.py:91 +#: views\settingsDialog.py:93 msgid "ポート番号" msgstr "port number" -#: views\settingsDialog.py:175 views\tcManageUser.py:154 +#: views\settingsDialog.py:178 views\spacesManageUser.py:160 +#: views\tcManageUser.py:154 msgid "効果音ファイルを選択" msgstr "select a sound file" -#: views\settingsDialog.py:175 views\tcManageUser.py:154 +#: views\settingsDialog.py:178 views\spacesManageUser.py:160 +#: views\tcManageUser.py:154 msgid "音声ファイル(.wav/.mp3/.ogg)" msgstr "Sound Files(.WAV/.MP3/.OGG)" -#: views\settingsDialog.py:183 +#: views\settingsDialog.py:186 msgid "保存先フォルダを選択" msgstr "Select the folder to save to" +#: views\spacesManageUser.py:18 views\spacesManageUser.py:100 #: views\tcManageUser.py:19 views\tcManageUser.py:98 msgid "ユーザID" msgstr "User ID" -#: views\tcManageUser.py:22 +#: views\spacesManageUser.py:21 views\tcManageUser.py:22 msgid "専用設定" msgstr "Dedicated settings" -#: views\tcManageUser.py:58 +#: views\spacesManageUser.py:27 +msgid "非公開アカウント" +msgstr "Private account" + +#: views\spacesManageUser.py:57 views\tcManageUser.py:58 msgid "有効" msgstr "Enabled" -#: views\tcManageUser.py:58 +#: views\spacesManageUser.py:57 views\tcManageUser.py:58 msgid "無効" msgstr "Disabled" -#: views\tcManageUser.py:61 +#: views\spacesManageUser.py:60 views\tcManageUser.py:61 msgid "ユーザの管理" msgstr "Manage users" -#: views\tcManageUser.py:101 +#: views\spacesManageUser.py:103 views\tcManageUser.py:101 msgid "専用設定を使用" msgstr "Use dedicated settings" -#: views\tcManageUser.py:113 +#: views\spacesManageUser.py:109 +msgid "保護されたユーザ" +msgstr "Protected user" + +#: views\spacesManageUser.py:116 views\tcManageUser.py:113 msgid "通知設定" msgstr "Notification settings" -#: views\tcManageUser.py:120 +#: views\spacesManageUser.py:123 views\tcManageUser.py:120 msgid "ユーザ名が入力されていません。" msgstr "The user name has not been entered." +#: views\spacesTokenManager.py:23 +msgid "Twitterアカウントの管理" +msgstr "Manage Twitter accounts" + +#: views\spacesTokenManager.py:37 +msgid "規定のアカウント" +msgstr "Default account" + +#: views\spacesTokenManager.py:38 +msgid "ID" +msgstr "ID" + +#: views\spacesTokenManager.py:49 +msgid "規定のアカウントに設定(&F)" +msgstr "Set to de&fault account" + +#: views\spacesTokenManager.py:53 views\versionDialog.py:49 +msgid "閉じる" +msgstr "close" + +#: views\spacesTokenManager.py:62 +msgid "設定中" +msgstr "set" + +#: views\spacesTokenManager.py:85 +msgid "ブラウザを開いてアカウントの認証作業を行います。よろしいですか?" +msgstr "" +"I will open your default browser for Authorization. Do you wish to continue?" + +#: views\spacesTokenManager.py:109 views\spacesTokenManager.py:116 +msgid "" +"Twitterアカウントの情報が設定されていません。Twitterとの連携を停止しますか?" +msgstr "" +"Your Twitter account information is not set up. Would you like to stop " +"working with Twitter?" + +#: views\spacesTokenManager.py:124 +msgid "規定のアカウントが設定されていません。" +msgstr "Default account is not configured." + #: views\updateDialog.py:23 #, python-format msgid "アップデート - %s" @@ -1148,9 +1385,5 @@ msgid "" "ライセンス/著作権情報については、同梱の license.txt を参照してください。" msgstr "See the included license.txt for licensing / copyright information." -#: views\versionDialog.py:49 -msgid "閉じる" -msgstr "close" - #~ msgid "Twitterでフォローしているユーザを一括追加" #~ msgstr "Add Users From Twitter" diff --git a/locale/ja-jp/LC_MESSAGES/messages.po b/locale/ja-jp/LC_MESSAGES/messages.po index 11788b6..c950974 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: 2022-01-02 23:14+0900\n" +"POT-Creation-Date: 2022-02-10 15:28+0900\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,12 +17,22 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: AppBase.py:45 +#: AppBase.py:48 msgid "" "ログ機能の初期化に失敗しました。下記のファイルへのアクセスが可能であることを" "確認してください。" msgstr "" +#: AppBase.py:57 +msgid "音声エンジンエラー" +msgstr "" + +#: AppBase.py:57 +msgid "" +"音声読み上げ機能の初期化に失敗したため、読み上げ機能を使用できません。出力先" +"の変更をお試しください。" +msgstr "" + #: keymapHandlerBase.py:696 keymapHandlerBase.py:726 #, python-format msgid "%s は存在しないキーです。" @@ -109,31 +119,55 @@ msgstr "" msgid "アクセストークンを設定(&T)" msgstr "" -#: menuItemsDic.py:28 +#: menuItemsDic.py:28 menuItemsDic.py:35 msgid "通知対象ユーザの管理(&M)" msgstr "" #: menuItemsDic.py:29 -msgid "設定(&S)" +msgid "Twitter スペース(&S)" msgstr "" #: menuItemsDic.py:30 -msgid "キーボードショートカットの設定(&K)" +msgid "Twitter スペースとの連携機能を有効化(&E)" msgstr "" #: menuItemsDic.py:31 -msgid "グローバルホットキーの設定(&H)" +msgid "フォロー中のユーザをすべて追加(&F)" msgstr "" #: menuItemsDic.py:32 -msgid "Windows起動時の自動起動を有効化(&W)" +msgid "ユーザ情報の更新(&I)" msgstr "" #: menuItemsDic.py:33 -msgid "最新バージョンを確認(&U)" +msgid "スペースの&URLを指定して録画" msgstr "" #: menuItemsDic.py:34 +msgid "連携アカウントの管理(&A)" +msgstr "" + +#: menuItemsDic.py:36 +msgid "設定(&S)" +msgstr "" + +#: menuItemsDic.py:37 +msgid "キーボードショートカットの設定(&K)" +msgstr "" + +#: menuItemsDic.py:38 +msgid "グローバルホットキーの設定(&H)" +msgstr "" + +#: menuItemsDic.py:39 +msgid "Windows起動時の自動起動を有効化(&W)" +msgstr "" + +#: menuItemsDic.py:40 +msgid "最新バージョンを確認(&U)" +msgstr "" + +#: menuItemsDic.py:41 msgid "バージョン情報(&V)" msgstr "" @@ -142,11 +176,11 @@ msgstr "" msgid "配信開始:%s" msgstr "" -#: recorder.py:145 recorder.py:147 recorder.py:158 recorder.py:174 +#: recorder.py:146 recorder.py:148 recorder.py:159 recorder.py:175 msgid "録画エラー" msgstr "" -#: recorder.py:145 +#: recorder.py:146 msgid "" "録画の開始に失敗しました。録画の保存先が適切に設定されていることを確認してく" "ださい。定期的に再試行する場合は[はい]、処理を中断する場合は[いいえ]を選択し" @@ -154,63 +188,64 @@ msgid "" "きる場合があります。" msgstr "" -#: recorder.py:147 recorder.py:158 +#: recorder.py:148 recorder.py:159 #, python-format msgid "%sのライブの録画処理を中断しました。" msgstr "" -#: recorder.py:161 +#: recorder.py:162 msgid "録画開始" msgstr "" -#: recorder.py:161 recorder.py:186 sources\twitcasting.py:702 -#: sources\twitcasting.py:714 +#: recorder.py:162 recorder.py:187 sources\twitcasting.py:712 +#: sources\twitcasting.py:724 #, python-format msgid "ユーザ:%(user)s、ムービーID:%(movie)s" msgstr "" -#: recorder.py:162 +#: recorder.py:163 msgid "録画中" msgstr "" -#: recorder.py:174 +#: recorder.py:175 #, python-format msgid "%sのライブを録画中にエラーが発生したため、再度録画を開始します。" msgstr "" -#: recorder.py:174 +#: recorder.py:175 #, python-format msgid "詳細:%s" msgstr "" -#: recorder.py:186 +#: recorder.py:187 msgid "録画終了" msgstr "" -#: twitterService.py:32 sources\twitcasting.py:262 +#: twitterService.py:32 sources\spaces.py:515 sources\twitcasting.py:272 msgid "" "認証に成功しました。このウィンドウを閉じて、アプリケーションに戻ってくださ" "い。" msgstr "" -#: twitterService.py:33 sources\twitcasting.py:263 +#: twitterService.py:33 sources\spaces.py:516 sources\twitcasting.py:273 msgid "認証に失敗しました。もう一度お試しください。" msgstr "" -#: twitterService.py:34 sources\twitcasting.py:264 +#: twitterService.py:34 sources\spaces.py:517 sources\twitcasting.py:274 msgid "" "しばらくしても画面が切り替わらない場合は、別のブラウザでお試しください。" msgstr "" -#: twitterService.py:38 +#: twitterService.py:38 sources\spaces.py:521 msgid "Twitterアカウント認証" msgstr "" -#: twitterService.py:47 sources\twitcasting.py:277 sources\twitcasting.py:289 +#: twitterService.py:47 sources\spaces.py:533 sources\twitcasting.py:287 +#: sources\twitcasting.py:299 msgid "処理結果" msgstr "" -#: twitterService.py:47 sources\twitcasting.py:277 +#: twitterService.py:47 sources\spaces.py:533 sources\twitcasting.py:287 msgid "キャンセルされました。" msgstr "" @@ -276,14 +311,151 @@ msgid "" "ソフトウェア終了時に、自動でアップデートされます。" msgstr "" -#: sources\twitcasting.py:42 -msgid "ツイキャス" +#: sources\spaces.py:32 +msgid "Twitter スペース" msgstr "" -#: sources\twitcasting.py:54 sources\twitcasting.py:214 +#: sources\spaces.py:38 sources\spaces.py:230 sources\twitcasting.py:54 +#: sources\twitcasting.py:224 msgid "未接続" msgstr "" +#: sources\spaces.py:57 sources\spaces.py:63 sources\spaces.py:387 +msgid "Twitterアカウントの連携" +msgstr "" + +#: sources\spaces.py:57 +msgid "" +"Twitter スペースを使用する前に、使用するTwitterアカウントを設定する必要があり" +"ます。今すぐ設定画面を開きますか?" +msgstr "" + +#: sources\spaces.py:63 +msgid "" +"Twitterアカウント情報の読み込みに失敗しました。再度アカウントの連携を行ってく" +"ださい。今すぐ設定画面を開きますか?" +msgstr "" + +#: sources\spaces.py:99 sources\spaces.py:192 views\main.py:245 +#: views\main.py:382 views\main.py:388 +msgid "完了" +msgstr "" + +#: sources\spaces.py:99 +#, python-format +msgid "フォロー中のユーザの追加が完了しました。追加件数:%d" +msgstr "" + +#: sources\spaces.py:117 +#, python-format +msgid "フォロー中のユーザの取得に失敗しました。詳細:%s" +msgstr "" + +#: sources\spaces.py:151 sources\twitcasting.py:211 +msgid "接続完了" +msgstr "" + +#: sources\spaces.py:151 +msgid "スペースの監視を開始しました。" +msgstr "" + +#: sources\spaces.py:154 sources\twitcasting.py:214 +msgid "接続済み" +msgstr "" + +#: sources\spaces.py:174 sources\spaces.py:181 +#, python-format +msgid "ユーザ情報の更新に失敗しました。詳細:%s" +msgstr "" + +#: sources\spaces.py:191 +msgid "以下のユーザの情報を取得できませんでした。" +msgstr "" + +#: sources\spaces.py:192 +msgid "ユーザ情報の更新が完了しました。" +msgstr "" + +#: sources\spaces.py:228 sources\twitcasting.py:202 sources\twitcasting.py:223 +msgid "切断" +msgstr "" + +#: sources\spaces.py:228 +msgid "Twitterとの接続を切断しました。" +msgstr "" + +#: sources\spaces.py:257 +msgid "Twitter エラー" +msgstr "" + +#: sources\spaces.py:257 +#, python-format +msgid "Twitterとの通信中にエラーが発生しました。詳細:%s" +msgstr "" + +#: sources\spaces.py:379 +msgid "" +"Twitterとの接続に失敗しました。インターネット接続に問題がない場合は、しばらく" +"たってから再度お試しください。この問題が再度発生する場合は、開発者までお問い" +"合わせください。" +msgstr "" + +#: sources\spaces.py:381 +msgid "Twitterからの応答が不正です。開発者までご連絡ください。" +msgstr "" + +#: sources\spaces.py:387 +msgid "" +"Twitterアカウントの認証情報が正しくありません。再度アカウントの連携を行ってく" +"ださい。今すぐ設定画面を開きますか?" +msgstr "" + +#: sources\spaces.py:404 +#, python-format +msgid "Twitterとの通信に失敗しました。詳細:%s" +msgstr "" + +#: sources\spaces.py:409 +#, python-format +msgid "ユーザ情報の取得に失敗しました。詳細:%s" +msgstr "" + +#: sources\spaces.py:500 +msgid "認証情報の保存に失敗しました。" +msgstr "" + +#: sources\spaces.py:644 sources\twitcasting.py:333 +msgid "ユーザ情報の保存に失敗しました。" +msgstr "" + +#: sources\spaces.py:686 views\globalKeyConfig.py:20 +#: views\globalKeyConfig.py:74 views\spacesManageUser.py:20 +#: views\spacesManageUser.py:102 views\spacesTokenManager.py:36 +#: views\tcManageUser.py:21 views\tcManageUser.py:100 +msgid "名前" +msgstr "" + +#: sources\spaces.py:688 views\main.py:221 views\main.py:228 +#: views\spacesManageUser.py:19 views\spacesManageUser.py:101 +#: views\spacesTokenManager.py:35 views\tcManageUser.py:20 +#: views\tcManageUser.py:99 +msgid "ユーザ名" +msgstr "" + +#: sources\spaces.py:692 +#, python-format +msgid "%(field)s変更" +msgstr "" + +#: sources\spaces.py:693 sources\twitcasting.py:183 sources\twitcasting.py:186 +#, python-format +msgid "「%(old)s」→「%(new)s」" +msgstr "" + +#: sources\twitcasting.py:42 +msgid "ツイキャス" +msgstr "" + #: sources\twitcasting.py:89 msgid "アクセストークンの有効期限が切れています" msgstr "" @@ -309,160 +481,139 @@ msgid "" "セストークンを再度設定する必要があります。" msgstr "" -#: sources\twitcasting.py:139 +#: sources\twitcasting.py:148 msgid "配信開始" msgstr "" -#: sources\twitcasting.py:146 +#: sources\twitcasting.py:155 msgid "録画対象の削除" msgstr "" -#: sources\twitcasting.py:146 sources\twitcasting.py:147 +#: sources\twitcasting.py:155 sources\twitcasting.py:156 #, python-format msgid "%sのライブを、録画対象から削除しました。" msgstr "" -#: sources\twitcasting.py:174 +#: sources\twitcasting.py:183 msgid "ユーザ名変更" msgstr "" -#: sources\twitcasting.py:174 sources\twitcasting.py:177 -#, python-format -msgid "「%(old)s」→「%(new)s」" -msgstr "" - -#: sources\twitcasting.py:177 +#: sources\twitcasting.py:186 msgid "名前変更" msgstr "" -#: sources\twitcasting.py:193 sources\twitcasting.py:213 -msgid "切断" -msgstr "" - -#: sources\twitcasting.py:193 +#: sources\twitcasting.py:202 msgid "インターネット接続が切断されました。再試行します。" msgstr "" -#: sources\twitcasting.py:194 +#: sources\twitcasting.py:203 msgid "接続試行中" msgstr "" -#: sources\twitcasting.py:202 -msgid "接続完了" -msgstr "" - -#: sources\twitcasting.py:202 +#: sources\twitcasting.py:211 msgid "新着ライブの監視を開始しました。" msgstr "" -#: sources\twitcasting.py:205 -msgid "接続済み" -msgstr "" - -#: sources\twitcasting.py:213 +#: sources\twitcasting.py:223 msgid "ツイキャスとの接続を切断しました。" msgstr "" -#: sources\twitcasting.py:218 +#: sources\twitcasting.py:228 msgid "再接続" msgstr "" -#: sources\twitcasting.py:218 +#: sources\twitcasting.py:228 msgid "ツイキャスとの接続が切断されたため、再度接続します。" msgstr "" -#: sources\twitcasting.py:245 +#: sources\twitcasting.py:255 msgid "" "インターネット接続に失敗しました。現在ツイキャスとの連携機能を使用できませ" "ん。" msgstr "" -#: sources\twitcasting.py:268 +#: sources\twitcasting.py:278 views\spacesTokenManager.py:85 msgid "アカウントの追加" msgstr "" -#: sources\twitcasting.py:289 +#: sources\twitcasting.py:299 msgid "認証が完了しました。" msgstr "" -#: sources\twitcasting.py:323 -msgid "ユーザ情報の保存に失敗しました。" -msgstr "" - -#: sources\twitcasting.py:352 +#: sources\twitcasting.py:362 msgid "指定したユーザが見つかりません。" msgstr "" -#: sources\twitcasting.py:464 +#: sources\twitcasting.py:474 msgid "合い言葉の入力" msgstr "" -#: sources\twitcasting.py:464 +#: sources\twitcasting.py:474 msgid "合い言葉" msgstr "" -#: sources\twitcasting.py:547 +#: sources\twitcasting.py:557 msgid "アクセストークンが見つかりません" msgstr "" -#: sources\twitcasting.py:547 +#: sources\twitcasting.py:557 msgid "" "利用可能なアクセストークンが見つかりません。ブラウザを起動し、認証作業を行い" "ますか?" msgstr "" -#: sources\twitcasting.py:557 +#: sources\twitcasting.py:567 msgid "" "録画に失敗しました。録画が公開されていること、入力したURLに誤りがないことを確" "認してください。合い言葉を入力した場合は、入力した合い言葉に誤りがないことを" "確認してください。" msgstr "" -#: sources\twitcasting.py:566 +#: sources\twitcasting.py:576 msgid "" "ツイキャスAPIの実行回数が上限に達しました。しばらくたってから、再度実行してく" "ださい。" msgstr "" -#: sources\twitcasting.py:568 +#: sources\twitcasting.py:578 msgid "" "ツイキャスAPIが500エラーを返しました。しばらく待ってから、再度接続してくださ" "い。" msgstr "" -#: sources\twitcasting.py:570 +#: sources\twitcasting.py:580 msgid "現在ツイキャスとの連携機能を使用できません。開発者に連絡してください。" msgstr "" -#: sources\twitcasting.py:583 +#: sources\twitcasting.py:593 #, python-format msgid "ツイキャスAPIとの通信中にエラーが発生しました。詳細:%s" msgstr "" -#: sources\twitcasting.py:618 -msgid "このユーザはすでに登録されています。" +#: sources\twitcasting.py:627 +msgid "このユーザのライブはすでに録画中です。" msgstr "" -#: sources\twitcasting.py:621 -msgid "このユーザのライブはすでに録画中です。" +#: sources\twitcasting.py:631 +msgid "このユーザはすでに登録されています。" msgstr "" -#: sources\twitcasting.py:635 +#: sources\twitcasting.py:645 msgid "ユーザ名を指定して録画" msgstr "" -#: sources\twitcasting.py:635 +#: sources\twitcasting.py:645 #, python-format msgid "" "%sを、録画対象として追加しました。この登録は一定時間経過後に自動で削除されま" "す。" msgstr "" -#: sources\twitcasting.py:660 +#: sources\twitcasting.py:670 msgid "対象ユーザの指定" msgstr "" -#: sources\twitcasting.py:660 +#: sources\twitcasting.py:670 msgid "" "フォロー中のユーザを取得するアカウントの@からはじまるアカウント名を入力してく" "ださい。\n" @@ -471,74 +622,74 @@ msgid "" "す。" msgstr "" -#: sources\twitcasting.py:702 +#: sources\twitcasting.py:712 msgid "コメント保存開始" msgstr "" -#: sources\twitcasting.py:714 +#: sources\twitcasting.py:724 msgid "コメント保存終了" msgstr "" -#: sources\twitcasting.py:821 sources\twitcasting.py:824 -#: sources\twitcasting.py:829 +#: sources\twitcasting.py:831 sources\twitcasting.py:834 +#: sources\twitcasting.py:839 msgid "ユーザ情報の更新" msgstr "" -#: sources\twitcasting.py:821 +#: sources\twitcasting.py:831 msgid "ユーザ情報の更新を開始します。" msgstr "" -#: sources\twitcasting.py:824 +#: sources\twitcasting.py:834 #, python-format msgid "%sの情報を取得しています。" msgstr "" -#: sources\twitcasting.py:829 +#: sources\twitcasting.py:839 msgid "ユーザ情報の更新が終了しました。" msgstr "" -#: sources\twitcasting.py:845 +#: sources\twitcasting.py:855 msgid "一括追加" msgstr "" -#: sources\twitcasting.py:849 +#: sources\twitcasting.py:859 msgid "処理を開始します。" msgstr "" -#: sources\twitcasting.py:869 +#: sources\twitcasting.py:879 #, python-format msgid "%sを追加しました。" msgstr "" -#: sources\twitcasting.py:876 +#: sources\twitcasting.py:886 msgid "処理が終了しました。" msgstr "" -#: sources\twitcasting.py:921 sources\twitcasting.py:924 -#: sources\twitcasting.py:928 sources\twitcasting.py:933 -#: sources\twitcasting.py:936 +#: sources\twitcasting.py:931 sources\twitcasting.py:934 +#: sources\twitcasting.py:938 sources\twitcasting.py:943 +#: sources\twitcasting.py:946 msgid "一括録画" msgstr "" -#: sources\twitcasting.py:921 +#: sources\twitcasting.py:931 msgid "ライブ一覧を取得しています。" msgstr "" -#: sources\twitcasting.py:924 +#: sources\twitcasting.py:934 #, python-format msgid "処理を開始します。対象ライブ数:%i" msgstr "" -#: sources\twitcasting.py:928 +#: sources\twitcasting.py:938 #, python-format msgid "処理中(%(index)i/%(total)i)" msgstr "" -#: sources\twitcasting.py:933 +#: sources\twitcasting.py:943 msgid "ファイルが既に存在するため、録画をスキップします。" msgstr "" -#: sources\twitcasting.py:936 +#: sources\twitcasting.py:946 #, python-format msgid "完了。%i件録画しました。" msgstr "" @@ -547,9 +698,10 @@ msgstr "" msgid "ブラウザでの操作を待っています..." msgstr "" -#: views\auth.py:24 views\KeyValueSettingDialogBase.py:81 -#: views\KeyValueSettingDialogBase.py:262 views\settingsDialog.py:96 -#: views\SimpleInputDialog.py:38 views\updateDialog.py:45 +#: views\auth.py:24 views\chooseTwitterAccount.py:32 +#: views\KeyValueSettingDialogBase.py:84 views\KeyValueSettingDialogBase.py:275 +#: views\settingsDialog.py:98 views\SimpleInputDialog.py:40 +#: views\updateDialog.py:45 msgid "キャンセル" msgstr "" @@ -566,12 +718,25 @@ msgid "" msgstr "" #: views\base.py:117 views\fontManager.py:25 views\fontManager.py:50 -#: views\globalKeyConfig.py:61 views\KeyValueSettingDialogBase.py:282 -#: views\KeyValueSettingDialogBase.py:333 views\main.py:89 +#: views\globalKeyConfig.py:61 views\KeyValueSettingDialogBase.py:297 +#: views\KeyValueSettingDialogBase.py:348 views\main.py:90 #: views\updateDialog.py:66 msgid "エラー" msgstr "" +#: views\chooseTwitterAccount.py:22 +msgid "Twitterアカウントを選択" +msgstr "" + +#: views\chooseTwitterAccount.py:29 views\spacesTokenManager.py:32 +msgid "アカウント" +msgstr "" + +#: views\chooseTwitterAccount.py:31 views\KeyValueSettingDialogBase.py:83 +#: views\KeyValueSettingDialogBase.py:274 views\SimpleInputDialog.py:39 +msgid "OK" +msgstr "" + #: views\fontManager.py:25 msgid "" "設定されているフォント情報が不正です。デフォルトのフォントを適用します。" @@ -581,11 +746,6 @@ msgstr "" msgid "原因不明のエラーにより、フォントの変更に失敗しました。" msgstr "" -#: views\globalKeyConfig.py:20 views\globalKeyConfig.py:74 -#: views\tcManageUser.py:21 views\tcManageUser.py:100 -msgid "名前" -msgstr "" - #: views\globalKeyConfig.py:21 msgid "ショートカット" msgstr "" @@ -594,7 +754,7 @@ msgstr "" msgid "識別子" msgstr "" -#: views\globalKeyConfig.py:31 views\main.py:252 +#: views\globalKeyConfig.py:31 views\main.py:315 msgid "ショートカットキーの設定" msgstr "" @@ -602,8 +762,8 @@ msgstr "" #: views\globalKeyConfig.py:102 views\globalKeyConfig.py:107 #: views\globalKeyConfig.py:120 views\globalKeyConfig.py:128 #: views\globalKeyConfig.py:138 views\globalKeyConfig.py:143 -#: views\keyConfig.py:57 views\KeyValueSettingDialogBase.py:318 -#: views\main.py:344 views\main.py:363 +#: views\keyConfig.py:57 views\KeyValueSettingDialogBase.py:333 +#: views\main.py:408 views\main.py:427 msgid "なし" msgstr "" @@ -635,7 +795,7 @@ msgstr "" msgid "ショートカット5" msgstr "" -#: views\globalKeyConfig.py:75 views\settingsDialog.py:39 +#: views\globalKeyConfig.py:75 views\settingsDialog.py:41 msgid "設定" msgstr "" @@ -674,107 +834,102 @@ msgstr "" msgid "設定解除" msgstr "" -#: views\KeyValueSettingDialogBase.py:51 +#: views\KeyValueSettingDialogBase.py:54 msgid "現在の登録内容" msgstr "" -#: views\KeyValueSettingDialogBase.py:72 +#: views\KeyValueSettingDialogBase.py:75 views\spacesTokenManager.py:46 msgid "追加(&A)" msgstr "" -#: views\KeyValueSettingDialogBase.py:73 +#: views\KeyValueSettingDialogBase.py:76 msgid "変更(&M)" msgstr "" -#: views\KeyValueSettingDialogBase.py:75 +#: views\KeyValueSettingDialogBase.py:78 views\spacesTokenManager.py:47 msgid "削除(&D)" msgstr "" -#: views\KeyValueSettingDialogBase.py:80 views\KeyValueSettingDialogBase.py:261 -#: views\SimpleInputDialog.py:37 -msgid "OK" -msgstr "" - -#: views\KeyValueSettingDialogBase.py:122 -#: views\KeyValueSettingDialogBase.py:152 +#: views\KeyValueSettingDialogBase.py:125 +#: views\KeyValueSettingDialogBase.py:155 #, python-format msgid "この%sは既に登録されています。登録を上書きしますか?" msgstr "" -#: views\KeyValueSettingDialogBase.py:122 -#: views\KeyValueSettingDialogBase.py:152 +#: views\KeyValueSettingDialogBase.py:125 +#: views\KeyValueSettingDialogBase.py:155 msgid "上書き確認" msgstr "" -#: views\KeyValueSettingDialogBase.py:282 +#: views\KeyValueSettingDialogBase.py:297 #, python-format msgid "%sを空白や半角の=を含む値に設定することはできません。" msgstr "" -#: views\KeyValueSettingDialogBase.py:322 +#: views\KeyValueSettingDialogBase.py:337 #, python-format msgid "%(v1)sと%(v2)sに同じショートカットキー%(key)sが設定されています。\n" msgstr "" -#: views\KeyValueSettingDialogBase.py:325 +#: views\KeyValueSettingDialogBase.py:340 #, python-format msgid "" "%(v1)s、%(v2)sなど計%(count)d箇所に同じショートカットキー%(key)sが設定されて" "います。\n" msgstr "" -#: views\KeyValueSettingDialogBase.py:329 +#: views\KeyValueSettingDialogBase.py:344 #, python-format msgid "" "設定されたショートカット%sが認識できません。お手数ですが、このエラーメッセー" "ジを作者へご連絡ください。\n" msgstr "" -#: views\KeyValueSettingDialogBase.py:331 +#: views\KeyValueSettingDialogBase.py:346 #, python-format msgid "" "%(command)sに設定されたショートカットキー%(key)sは、別の場所で利用されていま" "す。\n" msgstr "" -#: views\main.py:47 +#: views\main.py:48 msgid "動作履歴" msgstr "" -#: views\main.py:48 +#: views\main.py:49 msgid "タイトル" msgstr "" -#: views\main.py:49 +#: views\main.py:50 msgid "詳細" msgstr "" -#: views\main.py:50 views\main.py:55 +#: views\main.py:51 views\main.py:56 msgid "サービス" msgstr "" -#: views\main.py:51 +#: views\main.py:52 msgid "日時" msgstr "" -#: views\main.py:54 +#: views\main.py:55 msgid "動作状況" msgstr "" -#: views\main.py:56 +#: views\main.py:57 msgid "状態" msgstr "" -#: views\main.py:58 +#: views\main.py:59 msgid "準備完了" msgstr "" -#: views\main.py:58 +#: views\main.py:59 #, python-format msgid "%sを起動しました。" msgstr "" -#: views\main.py:86 +#: views\main.py:87 msgid "" "で設定されたホットキーが正しくありません。キーの重複、存在しないキー名の指" "定、使用できないキーパターンの指定などが考えられます。以下のキーの設定内容を" @@ -782,248 +937,316 @@ msgid "" "\n" msgstr "" -#: views\main.py:143 +#: views\main.py:155 msgid "ファイル(&F)" msgstr "" -#: views\main.py:144 +#: views\main.py:156 msgid "サービス(&S)" msgstr "" -#: views\main.py:145 +#: views\main.py:157 msgid "オプション(&O)" msgstr "" -#: views\main.py:146 +#: views\main.py:158 msgid "ヘルプ(&H)" msgstr "" -#: views\main.py:198 +#: views\main.py:196 views\main.py:202 views\main.py:271 views\main.py:432 +#: views\settingsDialog.py:174 +msgid "" +"設定の保存に失敗しました。下記のファイルへのアクセスが可能であることを確認し" +"てください。" +msgstr "" + +#: views\main.py:214 views\main.py:283 msgid "URLを入力" msgstr "" -#: views\main.py:198 +#: views\main.py:214 msgid "再生ページのURL" msgstr "" -#: views\main.py:205 views\main.py:212 +#: views\main.py:221 views\main.py:228 msgid "ユーザ名を入力" msgstr "" -#: views\main.py:205 views\main.py:212 views\tcManageUser.py:20 -#: views\tcManageUser.py:99 -msgid "ユーザ名" -msgstr "" - -#: views\main.py:220 +#: views\main.py:236 msgid "すでに削除されています。" msgstr "" -#: views\main.py:222 +#: views\main.py:238 msgid "アクセストークンの削除" msgstr "" -#: views\main.py:222 +#: views\main.py:238 msgid "" "ツイキャス連携機能を無効化し、アクセストークンを削除します。よろしいですか?" msgstr "" -#: views\main.py:229 views\main.py:318 views\main.py:324 -msgid "完了" +#: views\main.py:245 +msgid "アクセストークンを削除しました。" msgstr "" -#: views\main.py:229 -msgid "アクセストークンを削除しました。" +#: views\main.py:283 +msgid "スペースのURL" +msgstr "" + +#: views\main.py:288 +msgid "このスペースは既に終了しています。" +msgstr "" + +#: views\main.py:290 +msgid "入力されたURLが正しくありません。" msgstr "" -#: views\main.py:260 +#: views\main.py:292 +msgid "このスペースはまだ開始されていません。" +msgstr "" + +#: views\main.py:323 msgid "グローバルホットキーの設定" msgstr "" -#: views\main.py:299 views\main.py:315 +#: views\main.py:363 views\main.py:379 views\spacesTokenManager.py:109 +#: views\spacesTokenManager.py:116 msgid "確認" msgstr "" -#: views\main.py:299 +#: views\main.py:363 msgid "" "録画処理を実行中です。このまま終了すると、録画は中断されます。終了してもよろ" "しいですか?" msgstr "" -#: views\main.py:315 +#: views\main.py:379 msgid "Windows起動時の自動起動はすでに設定されています。設定を解除しますか?" msgstr "" -#: views\main.py:318 +#: views\main.py:382 msgid "Windows起動時の自動起動を無効化しました。" msgstr "" -#: views\main.py:324 +#: views\main.py:388 msgid "Windows起動時の自動起動を設定しました。" msgstr "" -#: views\settingsDialog.py:23 +#: views\settingsDialog.py:25 msgid "標準" msgstr "" -#: views\settingsDialog.py:24 +#: views\settingsDialog.py:26 msgid "ダーク" msgstr "" -#: views\settingsDialog.py:28 +#: views\settingsDialog.py:30 msgid "動画(TS)" msgstr "" -#: views\settingsDialog.py:29 +#: views\settingsDialog.py:31 msgid "動画(MP4)" msgstr "" -#: views\settingsDialog.py:30 +#: views\settingsDialog.py:32 msgid "音声のみ(MP3)" msgstr "" -#: views\settingsDialog.py:50 +#: views\settingsDialog.py:52 msgid "カテゴリ選択" msgstr "" -#: views\settingsDialog.py:53 +#: views\settingsDialog.py:55 msgid "一般" msgstr "" -#: views\settingsDialog.py:54 +#: views\settingsDialog.py:56 msgid "起動時にウィンドウを隠す(&H)" msgstr "" -#: views\settingsDialog.py:55 +#: views\settingsDialog.py:57 msgid "終了時にタスクトレイに最小化(&M)" msgstr "" -#: views\settingsDialog.py:58 +#: views\settingsDialog.py:60 msgid "表示/言語" msgstr "" -#: views\settingsDialog.py:59 +#: views\settingsDialog.py:61 msgid "言語(&L)" msgstr "" -#: views\settingsDialog.py:60 +#: views\settingsDialog.py:62 msgid "画面表示モード(&D)" msgstr "" -#: views\settingsDialog.py:63 +#: views\settingsDialog.py:65 msgid "通知" msgstr "" -#: views\settingsDialog.py:64 views\tcManageUser.py:23 +#: views\settingsDialog.py:66 views\spacesManageUser.py:22 +#: views\spacesManageUser.py:104 views\tcManageUser.py:23 #: views\tcManageUser.py:102 msgid "バルーン通知" msgstr "" -#: views\settingsDialog.py:65 views\settingsDialog.py:74 +#: views\settingsDialog.py:67 views\settingsDialog.py:76 +#: views\spacesManageUser.py:23 views\spacesManageUser.py:105 #: views\tcManageUser.py:24 views\tcManageUser.py:103 msgid "録画" msgstr "" -#: views\settingsDialog.py:66 views\tcManageUser.py:25 +#: views\settingsDialog.py:68 views\spacesManageUser.py:24 +#: views\spacesManageUser.py:106 views\tcManageUser.py:25 #: views\tcManageUser.py:104 msgid "ブラウザで開く" msgstr "" -#: views\settingsDialog.py:67 views\tcManageUser.py:26 +#: views\settingsDialog.py:69 views\spacesManageUser.py:25 +#: views\spacesManageUser.py:107 views\tcManageUser.py:26 #: views\tcManageUser.py:105 msgid "サウンドを再生" msgstr "" -#: views\settingsDialog.py:69 views\tcManageUser.py:27 +#: views\settingsDialog.py:71 views\spacesManageUser.py:26 +#: views\spacesManageUser.py:108 views\tcManageUser.py:27 #: views\tcManageUser.py:106 msgid "再生するサウンド" msgstr "" -#: views\settingsDialog.py:71 views\settingsDialog.py:78 -#: views\tcManageUser.py:108 +#: views\settingsDialog.py:73 views\settingsDialog.py:80 +#: views\spacesManageUser.py:111 views\tcManageUser.py:108 msgid "参照" msgstr "" -#: views\settingsDialog.py:76 +#: views\settingsDialog.py:78 msgid "保存先フォルダ(&F)" msgstr "" -#: views\settingsDialog.py:80 +#: views\settingsDialog.py:82 msgid "ユーザごとにサブフォルダを作成(&S)" msgstr "" -#: views\settingsDialog.py:81 +#: views\settingsDialog.py:83 msgid "保存ファイル名(&N)" msgstr "" -#: views\settingsDialog.py:83 +#: views\settingsDialog.py:85 msgid "ファイル形式(&T)" msgstr "" -#: views\settingsDialog.py:86 +#: views\settingsDialog.py:88 msgid "ネットワーク" msgstr "" -#: views\settingsDialog.py:87 +#: views\settingsDialog.py:89 msgid "起動時に更新を確認(&U)" msgstr "" -#: views\settingsDialog.py:88 +#: views\settingsDialog.py:90 msgid "プロキシサーバーの情報を手動で設定する(&M)" msgstr "" -#: views\settingsDialog.py:89 +#: views\settingsDialog.py:91 msgid "サーバーURL" msgstr "" -#: views\settingsDialog.py:91 +#: views\settingsDialog.py:93 msgid "ポート番号" msgstr "" -#: views\settingsDialog.py:175 views\tcManageUser.py:154 +#: views\settingsDialog.py:178 views\spacesManageUser.py:160 +#: views\tcManageUser.py:154 msgid "効果音ファイルを選択" msgstr "" -#: views\settingsDialog.py:175 views\tcManageUser.py:154 +#: views\settingsDialog.py:178 views\spacesManageUser.py:160 +#: views\tcManageUser.py:154 msgid "音声ファイル(.wav/.mp3/.ogg)" msgstr "" -#: views\settingsDialog.py:183 +#: views\settingsDialog.py:186 msgid "保存先フォルダを選択" msgstr "" +#: views\spacesManageUser.py:18 views\spacesManageUser.py:100 #: views\tcManageUser.py:19 views\tcManageUser.py:98 msgid "ユーザID" msgstr "" -#: views\tcManageUser.py:22 +#: views\spacesManageUser.py:21 views\tcManageUser.py:22 msgid "専用設定" msgstr "" -#: views\tcManageUser.py:58 +#: views\spacesManageUser.py:27 +msgid "非公開アカウント" +msgstr "" + +#: views\spacesManageUser.py:57 views\tcManageUser.py:58 msgid "有効" msgstr "" -#: views\tcManageUser.py:58 +#: views\spacesManageUser.py:57 views\tcManageUser.py:58 msgid "無効" msgstr "" -#: views\tcManageUser.py:61 +#: views\spacesManageUser.py:60 views\tcManageUser.py:61 msgid "ユーザの管理" msgstr "" -#: views\tcManageUser.py:101 +#: views\spacesManageUser.py:103 views\tcManageUser.py:101 msgid "専用設定を使用" msgstr "" -#: views\tcManageUser.py:113 +#: views\spacesManageUser.py:109 +msgid "保護されたユーザ" +msgstr "" + +#: views\spacesManageUser.py:116 views\tcManageUser.py:113 msgid "通知設定" msgstr "" -#: views\tcManageUser.py:120 +#: views\spacesManageUser.py:123 views\tcManageUser.py:120 msgid "ユーザ名が入力されていません。" msgstr "" +#: views\spacesTokenManager.py:23 +msgid "Twitterアカウントの管理" +msgstr "" + +#: views\spacesTokenManager.py:37 +msgid "規定のアカウント" +msgstr "" + +#: views\spacesTokenManager.py:38 +msgid "ID" +msgstr "" + +#: views\spacesTokenManager.py:49 +msgid "規定のアカウントに設定(&F)" +msgstr "" + +#: views\spacesTokenManager.py:53 views\versionDialog.py:49 +msgid "閉じる" +msgstr "" + +#: views\spacesTokenManager.py:62 +msgid "設定中" +msgstr "" + +#: views\spacesTokenManager.py:85 +msgid "ブラウザを開いてアカウントの認証作業を行います。よろしいですか?" +msgstr "" + +#: views\spacesTokenManager.py:109 views\spacesTokenManager.py:116 +msgid "" +"Twitterアカウントの情報が設定されていません。Twitterとの連携を停止しますか?" +msgstr "" + +#: views\spacesTokenManager.py:124 +msgid "規定のアカウントが設定されていません。" +msgstr "" + #: views\updateDialog.py:23 #, python-format msgid "アップデート - %s" @@ -1088,7 +1311,3 @@ msgstr "" msgid "" "ライセンス/著作権情報については、同梱の license.txt を参照してください。" msgstr "" - -#: views\versionDialog.py:49 -msgid "閉じる" -msgstr "" diff --git a/locale/messages.po b/locale/messages.po index 11788b6..c950974 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: 2022-01-02 23:14+0900\n" +"POT-Creation-Date: 2022-02-10 15:28+0900\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,12 +17,22 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: AppBase.py:45 +#: AppBase.py:48 msgid "" "ログ機能の初期化に失敗しました。下記のファイルへのアクセスが可能であることを" "確認してください。" msgstr "" +#: AppBase.py:57 +msgid "音声エンジンエラー" +msgstr "" + +#: AppBase.py:57 +msgid "" +"音声読み上げ機能の初期化に失敗したため、読み上げ機能を使用できません。出力先" +"の変更をお試しください。" +msgstr "" + #: keymapHandlerBase.py:696 keymapHandlerBase.py:726 #, python-format msgid "%s は存在しないキーです。" @@ -109,31 +119,55 @@ msgstr "" msgid "アクセストークンを設定(&T)" msgstr "" -#: menuItemsDic.py:28 +#: menuItemsDic.py:28 menuItemsDic.py:35 msgid "通知対象ユーザの管理(&M)" msgstr "" #: menuItemsDic.py:29 -msgid "設定(&S)" +msgid "Twitter スペース(&S)" msgstr "" #: menuItemsDic.py:30 -msgid "キーボードショートカットの設定(&K)" +msgid "Twitter スペースとの連携機能を有効化(&E)" msgstr "" #: menuItemsDic.py:31 -msgid "グローバルホットキーの設定(&H)" +msgid "フォロー中のユーザをすべて追加(&F)" msgstr "" #: menuItemsDic.py:32 -msgid "Windows起動時の自動起動を有効化(&W)" +msgid "ユーザ情報の更新(&I)" msgstr "" #: menuItemsDic.py:33 -msgid "最新バージョンを確認(&U)" +msgid "スペースの&URLを指定して録画" msgstr "" #: menuItemsDic.py:34 +msgid "連携アカウントの管理(&A)" +msgstr "" + +#: menuItemsDic.py:36 +msgid "設定(&S)" +msgstr "" + +#: menuItemsDic.py:37 +msgid "キーボードショートカットの設定(&K)" +msgstr "" + +#: menuItemsDic.py:38 +msgid "グローバルホットキーの設定(&H)" +msgstr "" + +#: menuItemsDic.py:39 +msgid "Windows起動時の自動起動を有効化(&W)" +msgstr "" + +#: menuItemsDic.py:40 +msgid "最新バージョンを確認(&U)" +msgstr "" + +#: menuItemsDic.py:41 msgid "バージョン情報(&V)" msgstr "" @@ -142,11 +176,11 @@ msgstr "" msgid "配信開始:%s" msgstr "" -#: recorder.py:145 recorder.py:147 recorder.py:158 recorder.py:174 +#: recorder.py:146 recorder.py:148 recorder.py:159 recorder.py:175 msgid "録画エラー" msgstr "" -#: recorder.py:145 +#: recorder.py:146 msgid "" "録画の開始に失敗しました。録画の保存先が適切に設定されていることを確認してく" "ださい。定期的に再試行する場合は[はい]、処理を中断する場合は[いいえ]を選択し" @@ -154,63 +188,64 @@ msgid "" "きる場合があります。" msgstr "" -#: recorder.py:147 recorder.py:158 +#: recorder.py:148 recorder.py:159 #, python-format msgid "%sのライブの録画処理を中断しました。" msgstr "" -#: recorder.py:161 +#: recorder.py:162 msgid "録画開始" msgstr "" -#: recorder.py:161 recorder.py:186 sources\twitcasting.py:702 -#: sources\twitcasting.py:714 +#: recorder.py:162 recorder.py:187 sources\twitcasting.py:712 +#: sources\twitcasting.py:724 #, python-format msgid "ユーザ:%(user)s、ムービーID:%(movie)s" msgstr "" -#: recorder.py:162 +#: recorder.py:163 msgid "録画中" msgstr "" -#: recorder.py:174 +#: recorder.py:175 #, python-format msgid "%sのライブを録画中にエラーが発生したため、再度録画を開始します。" msgstr "" -#: recorder.py:174 +#: recorder.py:175 #, python-format msgid "詳細:%s" msgstr "" -#: recorder.py:186 +#: recorder.py:187 msgid "録画終了" msgstr "" -#: twitterService.py:32 sources\twitcasting.py:262 +#: twitterService.py:32 sources\spaces.py:515 sources\twitcasting.py:272 msgid "" "認証に成功しました。このウィンドウを閉じて、アプリケーションに戻ってくださ" "い。" msgstr "" -#: twitterService.py:33 sources\twitcasting.py:263 +#: twitterService.py:33 sources\spaces.py:516 sources\twitcasting.py:273 msgid "認証に失敗しました。もう一度お試しください。" msgstr "" -#: twitterService.py:34 sources\twitcasting.py:264 +#: twitterService.py:34 sources\spaces.py:517 sources\twitcasting.py:274 msgid "" "しばらくしても画面が切り替わらない場合は、別のブラウザでお試しください。" msgstr "" -#: twitterService.py:38 +#: twitterService.py:38 sources\spaces.py:521 msgid "Twitterアカウント認証" msgstr "" -#: twitterService.py:47 sources\twitcasting.py:277 sources\twitcasting.py:289 +#: twitterService.py:47 sources\spaces.py:533 sources\twitcasting.py:287 +#: sources\twitcasting.py:299 msgid "処理結果" msgstr "" -#: twitterService.py:47 sources\twitcasting.py:277 +#: twitterService.py:47 sources\spaces.py:533 sources\twitcasting.py:287 msgid "キャンセルされました。" msgstr "" @@ -276,14 +311,151 @@ msgid "" "ソフトウェア終了時に、自動でアップデートされます。" msgstr "" -#: sources\twitcasting.py:42 -msgid "ツイキャス" +#: sources\spaces.py:32 +msgid "Twitter スペース" msgstr "" -#: sources\twitcasting.py:54 sources\twitcasting.py:214 +#: sources\spaces.py:38 sources\spaces.py:230 sources\twitcasting.py:54 +#: sources\twitcasting.py:224 msgid "未接続" msgstr "" +#: sources\spaces.py:57 sources\spaces.py:63 sources\spaces.py:387 +msgid "Twitterアカウントの連携" +msgstr "" + +#: sources\spaces.py:57 +msgid "" +"Twitter スペースを使用する前に、使用するTwitterアカウントを設定する必要があり" +"ます。今すぐ設定画面を開きますか?" +msgstr "" + +#: sources\spaces.py:63 +msgid "" +"Twitterアカウント情報の読み込みに失敗しました。再度アカウントの連携を行ってく" +"ださい。今すぐ設定画面を開きますか?" +msgstr "" + +#: sources\spaces.py:99 sources\spaces.py:192 views\main.py:245 +#: views\main.py:382 views\main.py:388 +msgid "完了" +msgstr "" + +#: sources\spaces.py:99 +#, python-format +msgid "フォロー中のユーザの追加が完了しました。追加件数:%d" +msgstr "" + +#: sources\spaces.py:117 +#, python-format +msgid "フォロー中のユーザの取得に失敗しました。詳細:%s" +msgstr "" + +#: sources\spaces.py:151 sources\twitcasting.py:211 +msgid "接続完了" +msgstr "" + +#: sources\spaces.py:151 +msgid "スペースの監視を開始しました。" +msgstr "" + +#: sources\spaces.py:154 sources\twitcasting.py:214 +msgid "接続済み" +msgstr "" + +#: sources\spaces.py:174 sources\spaces.py:181 +#, python-format +msgid "ユーザ情報の更新に失敗しました。詳細:%s" +msgstr "" + +#: sources\spaces.py:191 +msgid "以下のユーザの情報を取得できませんでした。" +msgstr "" + +#: sources\spaces.py:192 +msgid "ユーザ情報の更新が完了しました。" +msgstr "" + +#: sources\spaces.py:228 sources\twitcasting.py:202 sources\twitcasting.py:223 +msgid "切断" +msgstr "" + +#: sources\spaces.py:228 +msgid "Twitterとの接続を切断しました。" +msgstr "" + +#: sources\spaces.py:257 +msgid "Twitter エラー" +msgstr "" + +#: sources\spaces.py:257 +#, python-format +msgid "Twitterとの通信中にエラーが発生しました。詳細:%s" +msgstr "" + +#: sources\spaces.py:379 +msgid "" +"Twitterとの接続に失敗しました。インターネット接続に問題がない場合は、しばらく" +"たってから再度お試しください。この問題が再度発生する場合は、開発者までお問い" +"合わせください。" +msgstr "" + +#: sources\spaces.py:381 +msgid "Twitterからの応答が不正です。開発者までご連絡ください。" +msgstr "" + +#: sources\spaces.py:387 +msgid "" +"Twitterアカウントの認証情報が正しくありません。再度アカウントの連携を行ってく" +"ださい。今すぐ設定画面を開きますか?" +msgstr "" + +#: sources\spaces.py:404 +#, python-format +msgid "Twitterとの通信に失敗しました。詳細:%s" +msgstr "" + +#: sources\spaces.py:409 +#, python-format +msgid "ユーザ情報の取得に失敗しました。詳細:%s" +msgstr "" + +#: sources\spaces.py:500 +msgid "認証情報の保存に失敗しました。" +msgstr "" + +#: sources\spaces.py:644 sources\twitcasting.py:333 +msgid "ユーザ情報の保存に失敗しました。" +msgstr "" + +#: sources\spaces.py:686 views\globalKeyConfig.py:20 +#: views\globalKeyConfig.py:74 views\spacesManageUser.py:20 +#: views\spacesManageUser.py:102 views\spacesTokenManager.py:36 +#: views\tcManageUser.py:21 views\tcManageUser.py:100 +msgid "名前" +msgstr "" + +#: sources\spaces.py:688 views\main.py:221 views\main.py:228 +#: views\spacesManageUser.py:19 views\spacesManageUser.py:101 +#: views\spacesTokenManager.py:35 views\tcManageUser.py:20 +#: views\tcManageUser.py:99 +msgid "ユーザ名" +msgstr "" + +#: sources\spaces.py:692 +#, python-format +msgid "%(field)s変更" +msgstr "" + +#: sources\spaces.py:693 sources\twitcasting.py:183 sources\twitcasting.py:186 +#, python-format +msgid "「%(old)s」→「%(new)s」" +msgstr "" + +#: sources\twitcasting.py:42 +msgid "ツイキャス" +msgstr "" + #: sources\twitcasting.py:89 msgid "アクセストークンの有効期限が切れています" msgstr "" @@ -309,160 +481,139 @@ msgid "" "セストークンを再度設定する必要があります。" msgstr "" -#: sources\twitcasting.py:139 +#: sources\twitcasting.py:148 msgid "配信開始" msgstr "" -#: sources\twitcasting.py:146 +#: sources\twitcasting.py:155 msgid "録画対象の削除" msgstr "" -#: sources\twitcasting.py:146 sources\twitcasting.py:147 +#: sources\twitcasting.py:155 sources\twitcasting.py:156 #, python-format msgid "%sのライブを、録画対象から削除しました。" msgstr "" -#: sources\twitcasting.py:174 +#: sources\twitcasting.py:183 msgid "ユーザ名変更" msgstr "" -#: sources\twitcasting.py:174 sources\twitcasting.py:177 -#, python-format -msgid "「%(old)s」→「%(new)s」" -msgstr "" - -#: sources\twitcasting.py:177 +#: sources\twitcasting.py:186 msgid "名前変更" msgstr "" -#: sources\twitcasting.py:193 sources\twitcasting.py:213 -msgid "切断" -msgstr "" - -#: sources\twitcasting.py:193 +#: sources\twitcasting.py:202 msgid "インターネット接続が切断されました。再試行します。" msgstr "" -#: sources\twitcasting.py:194 +#: sources\twitcasting.py:203 msgid "接続試行中" msgstr "" -#: sources\twitcasting.py:202 -msgid "接続完了" -msgstr "" - -#: sources\twitcasting.py:202 +#: sources\twitcasting.py:211 msgid "新着ライブの監視を開始しました。" msgstr "" -#: sources\twitcasting.py:205 -msgid "接続済み" -msgstr "" - -#: sources\twitcasting.py:213 +#: sources\twitcasting.py:223 msgid "ツイキャスとの接続を切断しました。" msgstr "" -#: sources\twitcasting.py:218 +#: sources\twitcasting.py:228 msgid "再接続" msgstr "" -#: sources\twitcasting.py:218 +#: sources\twitcasting.py:228 msgid "ツイキャスとの接続が切断されたため、再度接続します。" msgstr "" -#: sources\twitcasting.py:245 +#: sources\twitcasting.py:255 msgid "" "インターネット接続に失敗しました。現在ツイキャスとの連携機能を使用できませ" "ん。" msgstr "" -#: sources\twitcasting.py:268 +#: sources\twitcasting.py:278 views\spacesTokenManager.py:85 msgid "アカウントの追加" msgstr "" -#: sources\twitcasting.py:289 +#: sources\twitcasting.py:299 msgid "認証が完了しました。" msgstr "" -#: sources\twitcasting.py:323 -msgid "ユーザ情報の保存に失敗しました。" -msgstr "" - -#: sources\twitcasting.py:352 +#: sources\twitcasting.py:362 msgid "指定したユーザが見つかりません。" msgstr "" -#: sources\twitcasting.py:464 +#: sources\twitcasting.py:474 msgid "合い言葉の入力" msgstr "" -#: sources\twitcasting.py:464 +#: sources\twitcasting.py:474 msgid "合い言葉" msgstr "" -#: sources\twitcasting.py:547 +#: sources\twitcasting.py:557 msgid "アクセストークンが見つかりません" msgstr "" -#: sources\twitcasting.py:547 +#: sources\twitcasting.py:557 msgid "" "利用可能なアクセストークンが見つかりません。ブラウザを起動し、認証作業を行い" "ますか?" msgstr "" -#: sources\twitcasting.py:557 +#: sources\twitcasting.py:567 msgid "" "録画に失敗しました。録画が公開されていること、入力したURLに誤りがないことを確" "認してください。合い言葉を入力した場合は、入力した合い言葉に誤りがないことを" "確認してください。" msgstr "" -#: sources\twitcasting.py:566 +#: sources\twitcasting.py:576 msgid "" "ツイキャスAPIの実行回数が上限に達しました。しばらくたってから、再度実行してく" "ださい。" msgstr "" -#: sources\twitcasting.py:568 +#: sources\twitcasting.py:578 msgid "" "ツイキャスAPIが500エラーを返しました。しばらく待ってから、再度接続してくださ" "い。" msgstr "" -#: sources\twitcasting.py:570 +#: sources\twitcasting.py:580 msgid "現在ツイキャスとの連携機能を使用できません。開発者に連絡してください。" msgstr "" -#: sources\twitcasting.py:583 +#: sources\twitcasting.py:593 #, python-format msgid "ツイキャスAPIとの通信中にエラーが発生しました。詳細:%s" msgstr "" -#: sources\twitcasting.py:618 -msgid "このユーザはすでに登録されています。" +#: sources\twitcasting.py:627 +msgid "このユーザのライブはすでに録画中です。" msgstr "" -#: sources\twitcasting.py:621 -msgid "このユーザのライブはすでに録画中です。" +#: sources\twitcasting.py:631 +msgid "このユーザはすでに登録されています。" msgstr "" -#: sources\twitcasting.py:635 +#: sources\twitcasting.py:645 msgid "ユーザ名を指定して録画" msgstr "" -#: sources\twitcasting.py:635 +#: sources\twitcasting.py:645 #, python-format msgid "" "%sを、録画対象として追加しました。この登録は一定時間経過後に自動で削除されま" "す。" msgstr "" -#: sources\twitcasting.py:660 +#: sources\twitcasting.py:670 msgid "対象ユーザの指定" msgstr "" -#: sources\twitcasting.py:660 +#: sources\twitcasting.py:670 msgid "" "フォロー中のユーザを取得するアカウントの@からはじまるアカウント名を入力してく" "ださい。\n" @@ -471,74 +622,74 @@ msgid "" "す。" msgstr "" -#: sources\twitcasting.py:702 +#: sources\twitcasting.py:712 msgid "コメント保存開始" msgstr "" -#: sources\twitcasting.py:714 +#: sources\twitcasting.py:724 msgid "コメント保存終了" msgstr "" -#: sources\twitcasting.py:821 sources\twitcasting.py:824 -#: sources\twitcasting.py:829 +#: sources\twitcasting.py:831 sources\twitcasting.py:834 +#: sources\twitcasting.py:839 msgid "ユーザ情報の更新" msgstr "" -#: sources\twitcasting.py:821 +#: sources\twitcasting.py:831 msgid "ユーザ情報の更新を開始します。" msgstr "" -#: sources\twitcasting.py:824 +#: sources\twitcasting.py:834 #, python-format msgid "%sの情報を取得しています。" msgstr "" -#: sources\twitcasting.py:829 +#: sources\twitcasting.py:839 msgid "ユーザ情報の更新が終了しました。" msgstr "" -#: sources\twitcasting.py:845 +#: sources\twitcasting.py:855 msgid "一括追加" msgstr "" -#: sources\twitcasting.py:849 +#: sources\twitcasting.py:859 msgid "処理を開始します。" msgstr "" -#: sources\twitcasting.py:869 +#: sources\twitcasting.py:879 #, python-format msgid "%sを追加しました。" msgstr "" -#: sources\twitcasting.py:876 +#: sources\twitcasting.py:886 msgid "処理が終了しました。" msgstr "" -#: sources\twitcasting.py:921 sources\twitcasting.py:924 -#: sources\twitcasting.py:928 sources\twitcasting.py:933 -#: sources\twitcasting.py:936 +#: sources\twitcasting.py:931 sources\twitcasting.py:934 +#: sources\twitcasting.py:938 sources\twitcasting.py:943 +#: sources\twitcasting.py:946 msgid "一括録画" msgstr "" -#: sources\twitcasting.py:921 +#: sources\twitcasting.py:931 msgid "ライブ一覧を取得しています。" msgstr "" -#: sources\twitcasting.py:924 +#: sources\twitcasting.py:934 #, python-format msgid "処理を開始します。対象ライブ数:%i" msgstr "" -#: sources\twitcasting.py:928 +#: sources\twitcasting.py:938 #, python-format msgid "処理中(%(index)i/%(total)i)" msgstr "" -#: sources\twitcasting.py:933 +#: sources\twitcasting.py:943 msgid "ファイルが既に存在するため、録画をスキップします。" msgstr "" -#: sources\twitcasting.py:936 +#: sources\twitcasting.py:946 #, python-format msgid "完了。%i件録画しました。" msgstr "" @@ -547,9 +698,10 @@ msgstr "" msgid "ブラウザでの操作を待っています..." msgstr "" -#: views\auth.py:24 views\KeyValueSettingDialogBase.py:81 -#: views\KeyValueSettingDialogBase.py:262 views\settingsDialog.py:96 -#: views\SimpleInputDialog.py:38 views\updateDialog.py:45 +#: views\auth.py:24 views\chooseTwitterAccount.py:32 +#: views\KeyValueSettingDialogBase.py:84 views\KeyValueSettingDialogBase.py:275 +#: views\settingsDialog.py:98 views\SimpleInputDialog.py:40 +#: views\updateDialog.py:45 msgid "キャンセル" msgstr "" @@ -566,12 +718,25 @@ msgid "" msgstr "" #: views\base.py:117 views\fontManager.py:25 views\fontManager.py:50 -#: views\globalKeyConfig.py:61 views\KeyValueSettingDialogBase.py:282 -#: views\KeyValueSettingDialogBase.py:333 views\main.py:89 +#: views\globalKeyConfig.py:61 views\KeyValueSettingDialogBase.py:297 +#: views\KeyValueSettingDialogBase.py:348 views\main.py:90 #: views\updateDialog.py:66 msgid "エラー" msgstr "" +#: views\chooseTwitterAccount.py:22 +msgid "Twitterアカウントを選択" +msgstr "" + +#: views\chooseTwitterAccount.py:29 views\spacesTokenManager.py:32 +msgid "アカウント" +msgstr "" + +#: views\chooseTwitterAccount.py:31 views\KeyValueSettingDialogBase.py:83 +#: views\KeyValueSettingDialogBase.py:274 views\SimpleInputDialog.py:39 +msgid "OK" +msgstr "" + #: views\fontManager.py:25 msgid "" "設定されているフォント情報が不正です。デフォルトのフォントを適用します。" @@ -581,11 +746,6 @@ msgstr "" msgid "原因不明のエラーにより、フォントの変更に失敗しました。" msgstr "" -#: views\globalKeyConfig.py:20 views\globalKeyConfig.py:74 -#: views\tcManageUser.py:21 views\tcManageUser.py:100 -msgid "名前" -msgstr "" - #: views\globalKeyConfig.py:21 msgid "ショートカット" msgstr "" @@ -594,7 +754,7 @@ msgstr "" msgid "識別子" msgstr "" -#: views\globalKeyConfig.py:31 views\main.py:252 +#: views\globalKeyConfig.py:31 views\main.py:315 msgid "ショートカットキーの設定" msgstr "" @@ -602,8 +762,8 @@ msgstr "" #: views\globalKeyConfig.py:102 views\globalKeyConfig.py:107 #: views\globalKeyConfig.py:120 views\globalKeyConfig.py:128 #: views\globalKeyConfig.py:138 views\globalKeyConfig.py:143 -#: views\keyConfig.py:57 views\KeyValueSettingDialogBase.py:318 -#: views\main.py:344 views\main.py:363 +#: views\keyConfig.py:57 views\KeyValueSettingDialogBase.py:333 +#: views\main.py:408 views\main.py:427 msgid "なし" msgstr "" @@ -635,7 +795,7 @@ msgstr "" msgid "ショートカット5" msgstr "" -#: views\globalKeyConfig.py:75 views\settingsDialog.py:39 +#: views\globalKeyConfig.py:75 views\settingsDialog.py:41 msgid "設定" msgstr "" @@ -674,107 +834,102 @@ msgstr "" msgid "設定解除" msgstr "" -#: views\KeyValueSettingDialogBase.py:51 +#: views\KeyValueSettingDialogBase.py:54 msgid "現在の登録内容" msgstr "" -#: views\KeyValueSettingDialogBase.py:72 +#: views\KeyValueSettingDialogBase.py:75 views\spacesTokenManager.py:46 msgid "追加(&A)" msgstr "" -#: views\KeyValueSettingDialogBase.py:73 +#: views\KeyValueSettingDialogBase.py:76 msgid "変更(&M)" msgstr "" -#: views\KeyValueSettingDialogBase.py:75 +#: views\KeyValueSettingDialogBase.py:78 views\spacesTokenManager.py:47 msgid "削除(&D)" msgstr "" -#: views\KeyValueSettingDialogBase.py:80 views\KeyValueSettingDialogBase.py:261 -#: views\SimpleInputDialog.py:37 -msgid "OK" -msgstr "" - -#: views\KeyValueSettingDialogBase.py:122 -#: views\KeyValueSettingDialogBase.py:152 +#: views\KeyValueSettingDialogBase.py:125 +#: views\KeyValueSettingDialogBase.py:155 #, python-format msgid "この%sは既に登録されています。登録を上書きしますか?" msgstr "" -#: views\KeyValueSettingDialogBase.py:122 -#: views\KeyValueSettingDialogBase.py:152 +#: views\KeyValueSettingDialogBase.py:125 +#: views\KeyValueSettingDialogBase.py:155 msgid "上書き確認" msgstr "" -#: views\KeyValueSettingDialogBase.py:282 +#: views\KeyValueSettingDialogBase.py:297 #, python-format msgid "%sを空白や半角の=を含む値に設定することはできません。" msgstr "" -#: views\KeyValueSettingDialogBase.py:322 +#: views\KeyValueSettingDialogBase.py:337 #, python-format msgid "%(v1)sと%(v2)sに同じショートカットキー%(key)sが設定されています。\n" msgstr "" -#: views\KeyValueSettingDialogBase.py:325 +#: views\KeyValueSettingDialogBase.py:340 #, python-format msgid "" "%(v1)s、%(v2)sなど計%(count)d箇所に同じショートカットキー%(key)sが設定されて" "います。\n" msgstr "" -#: views\KeyValueSettingDialogBase.py:329 +#: views\KeyValueSettingDialogBase.py:344 #, python-format msgid "" "設定されたショートカット%sが認識できません。お手数ですが、このエラーメッセー" "ジを作者へご連絡ください。\n" msgstr "" -#: views\KeyValueSettingDialogBase.py:331 +#: views\KeyValueSettingDialogBase.py:346 #, python-format msgid "" "%(command)sに設定されたショートカットキー%(key)sは、別の場所で利用されていま" "す。\n" msgstr "" -#: views\main.py:47 +#: views\main.py:48 msgid "動作履歴" msgstr "" -#: views\main.py:48 +#: views\main.py:49 msgid "タイトル" msgstr "" -#: views\main.py:49 +#: views\main.py:50 msgid "詳細" msgstr "" -#: views\main.py:50 views\main.py:55 +#: views\main.py:51 views\main.py:56 msgid "サービス" msgstr "" -#: views\main.py:51 +#: views\main.py:52 msgid "日時" msgstr "" -#: views\main.py:54 +#: views\main.py:55 msgid "動作状況" msgstr "" -#: views\main.py:56 +#: views\main.py:57 msgid "状態" msgstr "" -#: views\main.py:58 +#: views\main.py:59 msgid "準備完了" msgstr "" -#: views\main.py:58 +#: views\main.py:59 #, python-format msgid "%sを起動しました。" msgstr "" -#: views\main.py:86 +#: views\main.py:87 msgid "" "で設定されたホットキーが正しくありません。キーの重複、存在しないキー名の指" "定、使用できないキーパターンの指定などが考えられます。以下のキーの設定内容を" @@ -782,248 +937,316 @@ msgid "" "\n" msgstr "" -#: views\main.py:143 +#: views\main.py:155 msgid "ファイル(&F)" msgstr "" -#: views\main.py:144 +#: views\main.py:156 msgid "サービス(&S)" msgstr "" -#: views\main.py:145 +#: views\main.py:157 msgid "オプション(&O)" msgstr "" -#: views\main.py:146 +#: views\main.py:158 msgid "ヘルプ(&H)" msgstr "" -#: views\main.py:198 +#: views\main.py:196 views\main.py:202 views\main.py:271 views\main.py:432 +#: views\settingsDialog.py:174 +msgid "" +"設定の保存に失敗しました。下記のファイルへのアクセスが可能であることを確認し" +"てください。" +msgstr "" + +#: views\main.py:214 views\main.py:283 msgid "URLを入力" msgstr "" -#: views\main.py:198 +#: views\main.py:214 msgid "再生ページのURL" msgstr "" -#: views\main.py:205 views\main.py:212 +#: views\main.py:221 views\main.py:228 msgid "ユーザ名を入力" msgstr "" -#: views\main.py:205 views\main.py:212 views\tcManageUser.py:20 -#: views\tcManageUser.py:99 -msgid "ユーザ名" -msgstr "" - -#: views\main.py:220 +#: views\main.py:236 msgid "すでに削除されています。" msgstr "" -#: views\main.py:222 +#: views\main.py:238 msgid "アクセストークンの削除" msgstr "" -#: views\main.py:222 +#: views\main.py:238 msgid "" "ツイキャス連携機能を無効化し、アクセストークンを削除します。よろしいですか?" msgstr "" -#: views\main.py:229 views\main.py:318 views\main.py:324 -msgid "完了" +#: views\main.py:245 +msgid "アクセストークンを削除しました。" msgstr "" -#: views\main.py:229 -msgid "アクセストークンを削除しました。" +#: views\main.py:283 +msgid "スペースのURL" +msgstr "" + +#: views\main.py:288 +msgid "このスペースは既に終了しています。" +msgstr "" + +#: views\main.py:290 +msgid "入力されたURLが正しくありません。" msgstr "" -#: views\main.py:260 +#: views\main.py:292 +msgid "このスペースはまだ開始されていません。" +msgstr "" + +#: views\main.py:323 msgid "グローバルホットキーの設定" msgstr "" -#: views\main.py:299 views\main.py:315 +#: views\main.py:363 views\main.py:379 views\spacesTokenManager.py:109 +#: views\spacesTokenManager.py:116 msgid "確認" msgstr "" -#: views\main.py:299 +#: views\main.py:363 msgid "" "録画処理を実行中です。このまま終了すると、録画は中断されます。終了してもよろ" "しいですか?" msgstr "" -#: views\main.py:315 +#: views\main.py:379 msgid "Windows起動時の自動起動はすでに設定されています。設定を解除しますか?" msgstr "" -#: views\main.py:318 +#: views\main.py:382 msgid "Windows起動時の自動起動を無効化しました。" msgstr "" -#: views\main.py:324 +#: views\main.py:388 msgid "Windows起動時の自動起動を設定しました。" msgstr "" -#: views\settingsDialog.py:23 +#: views\settingsDialog.py:25 msgid "標準" msgstr "" -#: views\settingsDialog.py:24 +#: views\settingsDialog.py:26 msgid "ダーク" msgstr "" -#: views\settingsDialog.py:28 +#: views\settingsDialog.py:30 msgid "動画(TS)" msgstr "" -#: views\settingsDialog.py:29 +#: views\settingsDialog.py:31 msgid "動画(MP4)" msgstr "" -#: views\settingsDialog.py:30 +#: views\settingsDialog.py:32 msgid "音声のみ(MP3)" msgstr "" -#: views\settingsDialog.py:50 +#: views\settingsDialog.py:52 msgid "カテゴリ選択" msgstr "" -#: views\settingsDialog.py:53 +#: views\settingsDialog.py:55 msgid "一般" msgstr "" -#: views\settingsDialog.py:54 +#: views\settingsDialog.py:56 msgid "起動時にウィンドウを隠す(&H)" msgstr "" -#: views\settingsDialog.py:55 +#: views\settingsDialog.py:57 msgid "終了時にタスクトレイに最小化(&M)" msgstr "" -#: views\settingsDialog.py:58 +#: views\settingsDialog.py:60 msgid "表示/言語" msgstr "" -#: views\settingsDialog.py:59 +#: views\settingsDialog.py:61 msgid "言語(&L)" msgstr "" -#: views\settingsDialog.py:60 +#: views\settingsDialog.py:62 msgid "画面表示モード(&D)" msgstr "" -#: views\settingsDialog.py:63 +#: views\settingsDialog.py:65 msgid "通知" msgstr "" -#: views\settingsDialog.py:64 views\tcManageUser.py:23 +#: views\settingsDialog.py:66 views\spacesManageUser.py:22 +#: views\spacesManageUser.py:104 views\tcManageUser.py:23 #: views\tcManageUser.py:102 msgid "バルーン通知" msgstr "" -#: views\settingsDialog.py:65 views\settingsDialog.py:74 +#: views\settingsDialog.py:67 views\settingsDialog.py:76 +#: views\spacesManageUser.py:23 views\spacesManageUser.py:105 #: views\tcManageUser.py:24 views\tcManageUser.py:103 msgid "録画" msgstr "" -#: views\settingsDialog.py:66 views\tcManageUser.py:25 +#: views\settingsDialog.py:68 views\spacesManageUser.py:24 +#: views\spacesManageUser.py:106 views\tcManageUser.py:25 #: views\tcManageUser.py:104 msgid "ブラウザで開く" msgstr "" -#: views\settingsDialog.py:67 views\tcManageUser.py:26 +#: views\settingsDialog.py:69 views\spacesManageUser.py:25 +#: views\spacesManageUser.py:107 views\tcManageUser.py:26 #: views\tcManageUser.py:105 msgid "サウンドを再生" msgstr "" -#: views\settingsDialog.py:69 views\tcManageUser.py:27 +#: views\settingsDialog.py:71 views\spacesManageUser.py:26 +#: views\spacesManageUser.py:108 views\tcManageUser.py:27 #: views\tcManageUser.py:106 msgid "再生するサウンド" msgstr "" -#: views\settingsDialog.py:71 views\settingsDialog.py:78 -#: views\tcManageUser.py:108 +#: views\settingsDialog.py:73 views\settingsDialog.py:80 +#: views\spacesManageUser.py:111 views\tcManageUser.py:108 msgid "参照" msgstr "" -#: views\settingsDialog.py:76 +#: views\settingsDialog.py:78 msgid "保存先フォルダ(&F)" msgstr "" -#: views\settingsDialog.py:80 +#: views\settingsDialog.py:82 msgid "ユーザごとにサブフォルダを作成(&S)" msgstr "" -#: views\settingsDialog.py:81 +#: views\settingsDialog.py:83 msgid "保存ファイル名(&N)" msgstr "" -#: views\settingsDialog.py:83 +#: views\settingsDialog.py:85 msgid "ファイル形式(&T)" msgstr "" -#: views\settingsDialog.py:86 +#: views\settingsDialog.py:88 msgid "ネットワーク" msgstr "" -#: views\settingsDialog.py:87 +#: views\settingsDialog.py:89 msgid "起動時に更新を確認(&U)" msgstr "" -#: views\settingsDialog.py:88 +#: views\settingsDialog.py:90 msgid "プロキシサーバーの情報を手動で設定する(&M)" msgstr "" -#: views\settingsDialog.py:89 +#: views\settingsDialog.py:91 msgid "サーバーURL" msgstr "" -#: views\settingsDialog.py:91 +#: views\settingsDialog.py:93 msgid "ポート番号" msgstr "" -#: views\settingsDialog.py:175 views\tcManageUser.py:154 +#: views\settingsDialog.py:178 views\spacesManageUser.py:160 +#: views\tcManageUser.py:154 msgid "効果音ファイルを選択" msgstr "" -#: views\settingsDialog.py:175 views\tcManageUser.py:154 +#: views\settingsDialog.py:178 views\spacesManageUser.py:160 +#: views\tcManageUser.py:154 msgid "音声ファイル(.wav/.mp3/.ogg)" msgstr "" -#: views\settingsDialog.py:183 +#: views\settingsDialog.py:186 msgid "保存先フォルダを選択" msgstr "" +#: views\spacesManageUser.py:18 views\spacesManageUser.py:100 #: views\tcManageUser.py:19 views\tcManageUser.py:98 msgid "ユーザID" msgstr "" -#: views\tcManageUser.py:22 +#: views\spacesManageUser.py:21 views\tcManageUser.py:22 msgid "専用設定" msgstr "" -#: views\tcManageUser.py:58 +#: views\spacesManageUser.py:27 +msgid "非公開アカウント" +msgstr "" + +#: views\spacesManageUser.py:57 views\tcManageUser.py:58 msgid "有効" msgstr "" -#: views\tcManageUser.py:58 +#: views\spacesManageUser.py:57 views\tcManageUser.py:58 msgid "無効" msgstr "" -#: views\tcManageUser.py:61 +#: views\spacesManageUser.py:60 views\tcManageUser.py:61 msgid "ユーザの管理" msgstr "" -#: views\tcManageUser.py:101 +#: views\spacesManageUser.py:103 views\tcManageUser.py:101 msgid "専用設定を使用" msgstr "" -#: views\tcManageUser.py:113 +#: views\spacesManageUser.py:109 +msgid "保護されたユーザ" +msgstr "" + +#: views\spacesManageUser.py:116 views\tcManageUser.py:113 msgid "通知設定" msgstr "" -#: views\tcManageUser.py:120 +#: views\spacesManageUser.py:123 views\tcManageUser.py:120 msgid "ユーザ名が入力されていません。" msgstr "" +#: views\spacesTokenManager.py:23 +msgid "Twitterアカウントの管理" +msgstr "" + +#: views\spacesTokenManager.py:37 +msgid "規定のアカウント" +msgstr "" + +#: views\spacesTokenManager.py:38 +msgid "ID" +msgstr "" + +#: views\spacesTokenManager.py:49 +msgid "規定のアカウントに設定(&F)" +msgstr "" + +#: views\spacesTokenManager.py:53 views\versionDialog.py:49 +msgid "閉じる" +msgstr "" + +#: views\spacesTokenManager.py:62 +msgid "設定中" +msgstr "" + +#: views\spacesTokenManager.py:85 +msgid "ブラウザを開いてアカウントの認証作業を行います。よろしいですか?" +msgstr "" + +#: views\spacesTokenManager.py:109 views\spacesTokenManager.py:116 +msgid "" +"Twitterアカウントの情報が設定されていません。Twitterとの連携を停止しますか?" +msgstr "" + +#: views\spacesTokenManager.py:124 +msgid "規定のアカウントが設定されていません。" +msgstr "" + #: views\updateDialog.py:23 #, python-format msgid "アップデート - %s" @@ -1088,7 +1311,3 @@ msgstr "" msgid "" "ライセンス/著作権情報については、同梱の license.txt を参照してください。" msgstr "" - -#: views\versionDialog.py:49 -msgid "閉じる" -msgstr "" From 67e12f2ec4daeed7a5758852b4a78d608c4889f6 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Thu, 10 Feb 2022 19:00:48 +0900 Subject: [PATCH 20/32] add keymap entry --- defaultKeymap.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/defaultKeymap.py b/defaultKeymap.py index df1d514..3c919b5 100644 --- a/defaultKeymap.py +++ b/defaultKeymap.py @@ -10,6 +10,11 @@ "TC_RECORD_ARCHIVE": "", "TC_RECORD_USER": "", "TC_MANAGE_USER": "", + "SPACES_ENABLE": "", + "SPACES_UPDATE_USER": "", + "SPACES_URL_REC": "", + "SPACES_TOKEN_MANAGER": "", + "SPACES_MANAGE_USER": "", "OP_SETTINGS": "", "HELP_UPDATE":"", "HELP_VERSIONINFO":"", @@ -19,5 +24,5 @@ "SHOW":"", "EXIT": "", "TC_ENABLE": "", - + "SPACES_ENABLE": "", } From b8d9f19cf62a5587cd9a98e303499661f56ec10e Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Thu, 10 Feb 2022 21:33:36 +0900 Subject: [PATCH 21/32] display service name on shortcut/hotkey settings --- public/history.txt | 1 + views/main.py | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/public/history.txt b/public/history.txt index 7b0c9a3..d052743 100644 --- a/public/history.txt +++ b/public/history.txt @@ -3,6 +3,7 @@ Universal Live Tracking and Recording App(ULTRA) 更新履歴 2022/02/11 Version 1.3.0 1. Twitter スペースとの連携機能を追加しました。 2. ツイキャス連携機能が無効になっているとき、[Twitterでフォローしているユーザを一括追加]を実行できないように修正しました。 +3. キーボードショートカットおよびグローバルホットキーの設定において、各サービスに固有の機能には、サービス名を前置して標示するようになりました。 2022/02/02 Version 1.2.1 1. ツイキャス連携機能が無効になっているとき、[過去ライブの一括ダウンロード]を実行できないように修正しました。 diff --git a/views/main.py b/views/main.py index 53d5759..421ef09 100644 --- a/views/main.py +++ b/views/main.py @@ -402,6 +402,13 @@ def setKeymap(self, identifier,ttl, keymap=None,filter=None): menuData={} for refName in defaultKeymap.defaultKeymap[identifier.upper()].keys(): title=menuItemsDic.getValueString(refName) + prefix = "" + if refName.startswith("TC_"): + prefix = _("ツイキャス") + elif refName.startswith("SPACES_"): + prefix = _("Twitter スペース") + if prefix: + title = prefix + ":" + title if refName in keys: keyData[title]=keys[refName] else: From 2fcc2a971355488731b9e47ea7f85e52f463d4d6 Mon Sep 17 00:00:00 2001 From: yamahubuki Date: Thu, 10 Feb 2022 22:36:14 +0900 Subject: [PATCH 22/32] =?UTF-8?q?=E7=94=BB=E9=9D=A2=E3=83=AC=E3=82=A4?= =?UTF-8?q?=E3=82=A2=E3=82=A6=E3=83=88=E8=AA=BF=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- views/spacesTokenManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/spacesTokenManager.py b/views/spacesTokenManager.py index b7439db..0d537cd 100644 --- a/views/spacesTokenManager.py +++ b/views/spacesTokenManager.py @@ -34,7 +34,7 @@ def InstallControls(self): self.hListCtrl.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.ItemSelected) self.hListCtrl.AppendColumn(_("ユーザ名"), format=wx.LIST_FORMAT_LEFT, width=250) self.hListCtrl.AppendColumn(_("名前"),format=wx.LIST_FORMAT_LEFT,width=250) - self.hListCtrl.AppendColumn(_("規定のアカウント"), format=wx.LIST_FORMAT_LEFT, width=110) + self.hListCtrl.AppendColumn(_("規定のアカウント"), format=wx.LIST_FORMAT_LEFT, width=250) self.hListCtrl.AppendColumn(_("ID"), format=wx.LIST_FORMAT_LEFT, width=110) self.hListCtrl.SetColumnsOrder([0, 1, 2]) From 04287bd8b6bc8b2282370c9789379ab0f532853f Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Thu, 10 Feb 2022 23:41:32 +0900 Subject: [PATCH 23/32] update readme --- public/readme.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/readme.txt b/public/readme.txt index e68d750..05e4e81 100644 --- a/public/readme.txt +++ b/public/readme.txt @@ -10,7 +10,6 @@ 第1章 はじめに 1.1 Universal Live Tracking and Recording App(ULTRA)の概要 Universal Live Tracking and Recording Appは、インターネット上に公開されている動画配信サイト等の動画をダウンロードしたり、特定のユーザが配信を始めた際に通知したりできるソフトウェアです。 -Ver.1.0.0のリリース時点では、ライブ配信サイト「ツイキャス」に対応しています。 今後の更新で、対応サービスを追加する予定です。 1.2 動作環境について @@ -23,6 +22,7 @@ ULTRAはWindowsの標準的なコントロールのみを使用しており、 現時点では、以下のサービスに対応しています。 今後の更新で、更に対応サービスを追加する予定です。 ・ツイキャス(https://twitcasting.tv/) +・Twitter スペース 第2章 セットアップ 2.1 インストール @@ -152,7 +152,7 @@ Twitterアカウントを登録するには、[追加]ボタンを押します 1. Twitter スペースでやりとりされる情報は、音声のみです。録画ファイルの形式は、「音声のみ(MP3)」を使用することをお勧めします。現在のところ、サービスごとにファイル形式を設定することができないため、大変お手数ですが、本機能を使用する前に、必要に応じてファイル形式の設定を行ってください。なお、「動画(TS)」や「動画(MP4)」に設定されていても、録画機能は問題なく使用できます。 2. ULTRAに設定したアクセストークンの情報は、ULTRAをインストールしたフォルダ内の「data」フォルダにある「twitcasting」サブフォルダ内に、「account.bin」というファイル名で保存されます。このファイルを外部に流出させないよう、十分ご注意ください。 3. 録画機能は、Twitterが公式に提供しているものではないため、将来的に利用できなくなる可能性があります。 -4. Twitterには、アカウントごとのAPI呼び出し回数に制限があります。本ソフトでは、通知対象ユーザが最大1500人登録されることを想定し、制限を超過しないように設計しています。 +4. Twitterには、アカウントごとのAPI呼び出し回数に制限があります。本ソフトでは、通知対象ユーザが最大1500人登録されることを想定し、単体で使用したときに制限を超過しないように設計しています。他のTwitter関連アプリと一緒に本ソフトを使用する場合、制限を超過してしまう可能性があります。 5. 通知対象ユーザが非公開アカウントかどうかは、以下のタイミングで取得した情報に基づいて判断しています。 ・[通知対象ユーザの管理]で、新しくユーザが追加されたとき ・そのユーザがスペースを開催し、それを本ソフトが検知したとき From 5df4cc3f8450c92c7adc090b8bb85b15eac3fdcf Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Thu, 10 Feb 2022 23:44:13 +0900 Subject: [PATCH 24/32] =?UTF-8?q?Revert=20"=E9=85=8D=E4=BF=A1=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E3=82=92=E3=81=99=E3=81=90=E3=81=AB=E6=B6=88=E3=81=95?= =?UTF-8?q?=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB=E5=A4=89=E6=9B=B4?= =?UTF-8?q?=EF=BC=88=E3=83=86=E3=82=B9=E3=83=88=EF=BC=89"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 4b381e02e76c4ff0da069f7110d3d4bf63bda648. --- notificationHandler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/notificationHandler.py b/notificationHandler.py index 94ccd14..8c3d2d2 100644 --- a/notificationHandler.py +++ b/notificationHandler.py @@ -49,6 +49,7 @@ def notify(self, source, userName, link, stream, time, config=None, movie=""): if self.baloon: b = wx.adv.NotificationMessage(constants.APP_NAME, _("配信開始:%s") %(userName)) b.Show() + b.Close() if self.sound: fxPlayer.playFx(self.soundFile) if self.openBrowser: From 886b794ca1980cee54607944239e593c00f38b87 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Thu, 10 Feb 2022 23:58:45 +0900 Subject: [PATCH 25/32] fix Show function --- public/history.txt | 1 + views/main.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/public/history.txt b/public/history.txt index d052743..b0ff16b 100644 --- a/public/history.txt +++ b/public/history.txt @@ -4,6 +4,7 @@ Universal Live Tracking and Recording App(ULTRA) 更新履歴 1. Twitter スペースとの連携機能を追加しました。 2. ツイキャス連携機能が無効になっているとき、[Twitterでフォローしているユーザを一括追加]を実行できないように修正しました。 3. キーボードショートカットおよびグローバルホットキーの設定において、各サービスに固有の機能には、サービス名を前置して標示するようになりました。 +4. グローバルホットキーの[ウィンドウを表示]を実行したとき、ULTRAのウィンドウ上で最後にフォーカスがあった項目に自動でフォーカスを移動するようにしました。 2022/02/02 Version 1.2.1 1. ツイキャス連携機能が無効になっているとき、[過去ライブの一括ダウンロード]を実行できないように修正しました。 diff --git a/views/main.py b/views/main.py index 421ef09..bea8870 100644 --- a/views/main.py +++ b/views/main.py @@ -342,7 +342,7 @@ def OnMenuSelect(self,event): def OnExit(self, event): if event.CanVeto(): # Alt+F4が押された - if globalVars.app.config.getboolean("general", "minimizeOnExit", True) and globalVars.app.tc.running: + if globalVars.app.config.getboolean("general", "minimizeOnExit", True) and (globalVars.app.tc.running or globalVars.app.spaces.running): self.hide() else: super().OnExit(event) @@ -356,7 +356,7 @@ def hide(self): def show(self): self.parent.hFrame.Show() - self.parent.hFrame.SetFocus() + self.parent.hPanel.SetFocus() def exitWithConfirmation(self): if getRecordingUsers() != []: From b5492e2b8860d1860c340412719903d66d9c83b5 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Fri, 11 Feb 2022 00:21:02 +0900 Subject: [PATCH 26/32] update readme --- public/readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/readme.txt b/public/readme.txt index 05e4e81..6091c20 100644 --- a/public/readme.txt +++ b/public/readme.txt @@ -156,7 +156,7 @@ Twitterアカウントを登録するには、[追加]ボタンを押します 5. 通知対象ユーザが非公開アカウントかどうかは、以下のタイミングで取得した情報に基づいて判断しています。 ・[通知対象ユーザの管理]で、新しくユーザが追加されたとき ・そのユーザがスペースを開催し、それを本ソフトが検知したとき -従って、既に通知対象として登録されており、スペースを開催したことのないユーザが公開/非公開を変更した場合、本ソフトが管理している状態と実際の状態が一致しないことがあります。この問題を防止するため、定期的に[ユーザ情報の更新]を実行することをお勧めします。 +従って、ユーザが公開/非公開を変更しても、本ソフトでスペースの開催を検知しない限り最新の状況を確認できず、本ソフトが管理している状態と実際の状態が一致しないことがあります。この問題を防止するため、定期的に[ユーザ情報の更新]を実行することをお勧めします。 第5章 メニューリファレンス ここでは、ULTRAの各メニュー項目について解説します。 From c873952bfbb77c0743255965c60a5f4d84f98b0b Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Fri, 11 Feb 2022 00:22:00 +0900 Subject: [PATCH 27/32] fix TypeError --- views/spacesManageUser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/spacesManageUser.py b/views/spacesManageUser.py index c02823b..76b04bf 100644 --- a/views/spacesManageUser.py +++ b/views/spacesManageUser.py @@ -109,7 +109,7 @@ def __init__(self, parent, key="", user="", name="", specific=False, baloon=None (_("保護されたユーザ"), None), ], [None] * 8 + [(_("参照"), self.browse)] + [None], - key, user, name, specific, baloon, record, openBrowser, sound, soundFile, protected + key, user, name, specific, baloon, record, openBrowser, sound, soundFile, str(protected) ) def Initialize(self): From 7724e451c0c327a3868fb94dc52542e2b06fbc6d Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Fri, 11 Feb 2022 00:30:06 +0900 Subject: [PATCH 28/32] show source name on baloon notification --- notificationHandler.py | 2 +- public/history.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/notificationHandler.py b/notificationHandler.py index 8c3d2d2..4c83fce 100644 --- a/notificationHandler.py +++ b/notificationHandler.py @@ -47,7 +47,7 @@ def notify(self, source, userName, link, stream, time, config=None, movie=""): """ self.loadSettings(config) if self.baloon: - b = wx.adv.NotificationMessage(constants.APP_NAME, _("配信開始:%s") %(userName)) + b = wx.adv.NotificationMessage(constants.APP_NAME, _("配信開始:%s、サービス:%s") %(userName, source.friendlyName)) b.Show() b.Close() if self.sound: diff --git a/public/history.txt b/public/history.txt index b0ff16b..c1a2912 100644 --- a/public/history.txt +++ b/public/history.txt @@ -5,6 +5,7 @@ Universal Live Tracking and Recording App(ULTRA) 更新履歴 2. ツイキャス連携機能が無効になっているとき、[Twitterでフォローしているユーザを一括追加]を実行できないように修正しました。 3. キーボードショートカットおよびグローバルホットキーの設定において、各サービスに固有の機能には、サービス名を前置して標示するようになりました。 4. グローバルホットキーの[ウィンドウを表示]を実行したとき、ULTRAのウィンドウ上で最後にフォーカスがあった項目に自動でフォーカスを移動するようにしました。 +5. バルーン通知において、通知の送信元のサービス名を標示するように変更しました。 2022/02/02 Version 1.2.1 1. ツイキャス連携機能が無効になっているとき、[過去ライブの一括ダウンロード]を実行できないように修正しました。 From d6a7bfd1db57cee53f6250ccce2fd3542cf915d3 Mon Sep 17 00:00:00 2001 From: yamahubuki Date: Fri, 11 Feb 2022 00:41:59 +0900 Subject: [PATCH 29/32] fix wx Error --- views/spacesTokenManager.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/views/spacesTokenManager.py b/views/spacesTokenManager.py index 0d537cd..f42624d 100644 --- a/views/spacesTokenManager.py +++ b/views/spacesTokenManager.py @@ -50,7 +50,7 @@ def InstallControls(self): self.setDefaultButton.Enable(False) g2 = views.ViewCreator.ViewCreator(self.viewMode, self.panel, self.creator.GetSizer(), wx.VERTICAL, 0) - self.bClose = self.creator.closebutton(_("閉じる"), self.close) + self.bClose = self.creator.closebutton(_("閉じる")) self.refreshList() def refreshList(self): @@ -111,7 +111,7 @@ def delete(self, event): self._shouldExit = True self.wnd.Destroy() - def close(self, event): + def OnClose(self, event): if self.hListCtrl.GetItemCount() == 0: d = simpleDialog.yesNoDialog(_("確認"), _("Twitterアカウントの情報が設定されていません。Twitterとの連携を停止しますか?")) if d == wx.ID_YES: @@ -123,10 +123,6 @@ def close(self, event): if not result and self.hListCtrl.GetItemCount() > 0: simpleDialog.errorDialog(_("規定のアカウントが設定されていません。")) return - self.wnd.Destroy() - - def OnClose(self, event): - self.close(event) def shouldExit(self): return self._shouldExit From c5840c7a39ac36409d7024229175f0cc4d15bbf1 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Fri, 11 Feb 2022 00:46:24 +0900 Subject: [PATCH 30/32] update translation --- locale/en-us/LC_MESSAGES/messages.mo | Bin 30578 -> 30611 bytes locale/en-us/LC_MESSAGES/messages.po | 16 ++++++++-------- locale/ja-jp/LC_MESSAGES/messages.po | 12 ++++++------ locale/messages.po | 12 ++++++------ 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/locale/en-us/LC_MESSAGES/messages.mo b/locale/en-us/LC_MESSAGES/messages.mo index fde736d80407ced819328c0eccad968075f097e2..6a03334a7e4e2dbdaaf577d18417700e6bd9390c 100644 GIT binary patch delta 2527 zcmXZe4NTQl7{~E*LEhy3B?Cmb$WY$KYYr0ZnwaDuMF}Mmk}fZisDOa38MnY{&6zD4 zt=SxnqP5{_U0SKMwwBXsnXRy7W7FMMxgl6I$J&yey;;p;=p#}F>4r{W?>jFpaR)bZOnh@#tLlOVN5iBhO>7vCKDP z4A$X#+=p2>hNbu;uEOvdW7;sg#$!wdgCQ1lf=N{5-=H7EV!@)>+xP@Fpv2p zrs6E7V8oNw2Dzx`s!+Yui%vX+I_V@*RdXHn;(SioijN^*9y7uBWh_KD7!!mmFcgz< z1!iF^K8*2Li!FEn70A!nf&ZYMYihJk+KX|_PvS%P9;$Rc)BzVTkoTLMr>qwip;GO_ z4OolKIE3nrMcaVA#sn~r!C;KHO+__t7OEr#xDrb-8h4`((2lEc0AoE2Mi}VD6Sy3| zwVlEk=GRd*UBCy?*V-#80S%z`Ic_^_`>yS{?Puso zcL$>|@oCF!)XB@R4mY7ncM4Uq5mcteu?o+knljMMRmNJrtvD-{vs`BBV4 zov0U8yXR3geFJq3$4~)GU?HAEo%9wK;Uc;)hXzr%ci`{%GOCyM(|PKFW2gY%Y$gBc z3~sOxfI)56Ny1TqBw#A0qE1$h%1||?VLfWYepJA(p)xj#PMpAeoI+jez-O!;NX9kH zi#_(B5p`c*LMKk3nq>-C;~WNIbi4Hln1DJ_1M;C}dXO7tE}}A(_Nk4mk})`KeDml%RGsOSGcb^9IEe!(5qJPFq_ z&qXKh!a&{s!wkY$=ts(Dj-w*JjoM%lwPDzPE49fO$~+I3V=1bcwjdjt77WG?R6xDB z43A+LzKYs^81LQx(+o0Mcpt;@2h_EjL8bUMs#ZYV+3j0ep`YIE{h2|EmvL&6kOaJP);T3F-r66RN2iu^bPg0`j5)o<;@mC+Y=v zQOz9MX=NrF)kFEHeQGcgo6(~m^e|9&zJ=QGBUAuipaQyR ?+Dzj{+|Xrxw)f*! z=9keQrfe}MU}1rbv@g=secB8K^7`;9ChMFRBhu9S%IZsHuGZC$=s-u zwxdeahswYS)Imm3fuBe9&ey2tuA)kJ3sow=9`diI$>`yq3(Uhf2Y*&kDVpoGGI1Am zy@LC!7c^rQ^P?DrAE2K992MXt)B!Hr>(@}v-9%l(e^CdGjXKQVat3+mhp&5YM!Ow; zp$_l1*a!zJ-p06%#gV=jUi1xo>KmN&4G#GR&W^ra84y(9%+Ft6^niDyx-~5%$9brw OyRY5d;`Qt4^#32~MO+a8 delta 2502 zcmXZee@xX?7{~E*@$y4{BM4W18z~Tlh@;R_Bnm}PFd|XVKnxQJ6*Nkl-=;2`(^k5r|>^Kjk`QpWlRix-x_0PVh$d{LY$2sVHpl%B*s-+(^2#DT#L|0 zzXbJv^_YSk)y6UNX&hnTRXmG2$jTaHrm-4V;Oez3j<29L?88F5?vAHFVoW^!0-S{p z;SyYjcVQ3SkDp;0-omYzRO=8G4X4(gydQP)4=@Nn$0)pr(fA$4;237(4cv=Kj~eqZ z_TWJr!Ts1+XV<@sC+K@0GbRgrF&{5DG)ib($41Os$71**K8Jo(%F^m>3K!us^s6u( zzeXK+9JvKEfp_E5^>%|c%%y)4=irx^idRvYaHeR?qT$e;+Qxmrz&tEh@k<3}Jucd)#iAfl75AF2z#Z zf)1*>$6bHNVEX@J7)hKn(>+0HLVWvmgk;TF_^I#BC$yB=^o=GyD}COXMXe2<2zdkD4A2&%t`kM_t`v)Ri4YWvUme@GVqPPGSp|@~cSWn2wiQ$iGrC#lTARk#9xRjJmsR zs5{+{dWJoy1NgBJPop9o#YH%dCFr9-=3qVkjGd@jT2JMv26mzj_Y1DJ*7s0G_l2i}Xy*m2B6KNjEs>RC^sY9RbcbNhoY%W)g4 zFrPo{!c6p|CJx{o_#=kmG?roTHXBg|@?mD0kQZhGs7%GQ+03M&{x1i$UI8w|3M@uv z9}Put4wdS!Fcn8oHSs6TNAGr<+C10osH=MiXW|fQ{`aVAzlK`x54RuIZa*=TF_ZBJ zF+}gbg+?R;ZOHYRZq$jdpcWWME%-Mowc$_MD@ws|`q`*rDnu4EYcUM#Q3q;9FYd%h z+=E*G0N#H8M`+|S@G?f>r>JK)gesaVs0`dh-Fb3{z3XMD$ZJv0cO$Bnx=K;)fqI^Go!oy8jU5bBVlOJ1&E9%IgA!%Q{~GFmeW(E5b;r-6=6!?OcoY@rWGsy)8qwYM3&)PYuW>D&AacGY nKH3u$;R$R?SbksN{mOs5p#_->?_E+@6gXGYnia_2-9F=gddp4~ diff --git a/locale/en-us/LC_MESSAGES/messages.po b/locale/en-us/LC_MESSAGES/messages.po index 51e3665..fefab8e 100644 --- a/locale/en-us/LC_MESSAGES/messages.po +++ b/locale/en-us/LC_MESSAGES/messages.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: TCV\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-02-10 15:28+0900\n" -"PO-Revision-Date: 2022-02-10 16:25+0900\n" +"POT-Creation-Date: 2022-02-11 00:30+0900\n" +"PO-Revision-Date: 2022-02-11 00:34+0900\n" "Last-Translator: \n" "Language-Team: \n" "Language: en_US\n" @@ -177,8 +177,8 @@ msgstr "&Version Info" #: notificationHandler.py:50 #, python-format -msgid "配信開始:%s" -msgstr "Broadcasting Started: %s" +msgid "配信開始:%s、サービス:%s" +msgstr "Broadcasting Started: %s, service: %s" #: recorder.py:146 recorder.py:148 recorder.py:159 recorder.py:175 msgid "録画エラー" @@ -330,7 +330,7 @@ msgstr "" "Download has successfully finished.\n" "Update will be started when closing the application." -#: sources\spaces.py:32 +#: sources\spaces.py:32 views\main.py:409 msgid "Twitter スペース" msgstr "Twitter Spaces" @@ -479,7 +479,7 @@ msgstr "%(field)s changed" msgid "「%(old)s」→「%(new)s」" msgstr "%(old)s -> %(new)s" -#: sources\twitcasting.py:42 +#: sources\twitcasting.py:42 views\main.py:407 msgid "ツイキャス" msgstr "TwitCasting" @@ -816,7 +816,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:408 views\main.py:427 +#: views\main.py:415 views\main.py:434 msgid "なし" msgstr "None" @@ -1017,7 +1017,7 @@ msgstr "&Options" msgid "ヘルプ(&H)" msgstr "&Help" -#: views\main.py:196 views\main.py:202 views\main.py:271 views\main.py:432 +#: views\main.py:196 views\main.py:202 views\main.py:271 views\main.py:439 #: views\settingsDialog.py:174 msgid "" "設定の保存に失敗しました。下記のファイルへのアクセスが可能であることを確認し" diff --git a/locale/ja-jp/LC_MESSAGES/messages.po b/locale/ja-jp/LC_MESSAGES/messages.po index c950974..10415a8 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: 2022-02-10 15:28+0900\n" +"POT-Creation-Date: 2022-02-11 00:30+0900\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -173,7 +173,7 @@ msgstr "" #: notificationHandler.py:50 #, python-format -msgid "配信開始:%s" +msgid "配信開始:%s、サービス:%s" msgstr "" #: recorder.py:146 recorder.py:148 recorder.py:159 recorder.py:175 @@ -311,7 +311,7 @@ msgid "" "ソフトウェア終了時に、自動でアップデートされます。" msgstr "" -#: sources\spaces.py:32 +#: sources\spaces.py:32 views\main.py:409 msgid "Twitter スペース" msgstr "" @@ -452,7 +452,7 @@ msgstr "" msgid "「%(old)s」→「%(new)s」" msgstr "" -#: sources\twitcasting.py:42 +#: sources\twitcasting.py:42 views\main.py:407 msgid "ツイキャス" msgstr "" @@ -763,7 +763,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:408 views\main.py:427 +#: views\main.py:415 views\main.py:434 msgid "なし" msgstr "" @@ -953,7 +953,7 @@ msgstr "" msgid "ヘルプ(&H)" msgstr "" -#: views\main.py:196 views\main.py:202 views\main.py:271 views\main.py:432 +#: views\main.py:196 views\main.py:202 views\main.py:271 views\main.py:439 #: views\settingsDialog.py:174 msgid "" "設定の保存に失敗しました。下記のファイルへのアクセスが可能であることを確認し" diff --git a/locale/messages.po b/locale/messages.po index c950974..10415a8 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: 2022-02-10 15:28+0900\n" +"POT-Creation-Date: 2022-02-11 00:30+0900\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -173,7 +173,7 @@ msgstr "" #: notificationHandler.py:50 #, python-format -msgid "配信開始:%s" +msgid "配信開始:%s、サービス:%s" msgstr "" #: recorder.py:146 recorder.py:148 recorder.py:159 recorder.py:175 @@ -311,7 +311,7 @@ msgid "" "ソフトウェア終了時に、自動でアップデートされます。" msgstr "" -#: sources\spaces.py:32 +#: sources\spaces.py:32 views\main.py:409 msgid "Twitter スペース" msgstr "" @@ -452,7 +452,7 @@ msgstr "" msgid "「%(old)s」→「%(new)s」" msgstr "" -#: sources\twitcasting.py:42 +#: sources\twitcasting.py:42 views\main.py:407 msgid "ツイキャス" msgstr "" @@ -763,7 +763,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:408 views\main.py:427 +#: views\main.py:415 views\main.py:434 msgid "なし" msgstr "" @@ -953,7 +953,7 @@ msgstr "" msgid "ヘルプ(&H)" msgstr "" -#: views\main.py:196 views\main.py:202 views\main.py:271 views\main.py:432 +#: views\main.py:196 views\main.py:202 views\main.py:271 views\main.py:439 #: views\settingsDialog.py:174 msgid "" "設定の保存に失敗しました。下記のファイルへのアクセスが可能であることを確認し" From cda2db5fcf131fa166c5e2225e5341c6fc7f9598 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Fri, 11 Feb 2022 01:24:50 +0900 Subject: [PATCH 31/32] fix ManageTokenDialog --- views/spacesTokenManager.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/views/spacesTokenManager.py b/views/spacesTokenManager.py index f42624d..914a68c 100644 --- a/views/spacesTokenManager.py +++ b/views/spacesTokenManager.py @@ -50,7 +50,7 @@ def InstallControls(self): self.setDefaultButton.Enable(False) g2 = views.ViewCreator.ViewCreator(self.viewMode, self.panel, self.creator.GetSizer(), wx.VERTICAL, 0) - self.bClose = self.creator.closebutton(_("閉じる")) + self.bClose = self.creator.closebutton(_("閉じる"), self.onCloseButton) self.refreshList() def refreshList(self): @@ -105,13 +105,8 @@ def delete(self, event): self.setDefaultButton.Enable(False) self.deleteButton.Enable(False) self.hListCtrl.SetFocus() - if self.hListCtrl.GetItemCount() == 0: - d = simpleDialog.yesNoDialog(_("確認"), _("Twitterアカウントの情報が設定されていません。Twitterとの連携を停止しますか?")) - if d == wx.ID_YES: - self._shouldExit = True - self.wnd.Destroy() - def OnClose(self, event): + def onCloseButton(self, event): if self.hListCtrl.GetItemCount() == 0: d = simpleDialog.yesNoDialog(_("確認"), _("Twitterアカウントの情報が設定されていません。Twitterとの連携を停止しますか?")) if d == wx.ID_YES: @@ -123,6 +118,7 @@ def OnClose(self, event): if not result and self.hListCtrl.GetItemCount() > 0: simpleDialog.errorDialog(_("規定のアカウントが設定されていません。")) return + event.Skip() def shouldExit(self): return self._shouldExit From a304414c1de7f12119e595dbdd0465e5ff5985a5 Mon Sep 17 00:00:00 2001 From: Kazto Kitabatake Date: Fri, 11 Feb 2022 01:58:05 +0900 Subject: [PATCH 32/32] bump up to 1.3.0 --- constants.py | 4 ++-- public/readme.txt | 4 ++-- version.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/constants.py b/constants.py index 9c9a6ed..daba756 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.2.1" -APP_LAST_RELEASE_DATE="2022-02-02" +APP_VERSION="1.3.0" +APP_LAST_RELEASE_DATE="2022-02-11" APP_COPYRIGHT_YEAR="2021" APP_LICENSE="Apache License 2.0" APP_DEVELOPERS="Kazto Kitabatake, ACT Laboratory" diff --git a/public/readme.txt b/public/readme.txt index 6091c20..4e111a2 100644 --- a/public/readme.txt +++ b/public/readme.txt @@ -1,7 +1,7 @@ Universal Live Tracking and Recording App -ULTRA- - バージョン:  ver.1.2.1 - リリース:   2022-02-02 + バージョン:  ver.1.3.0 + リリース:   2022-02-11 開発・配布元: ACT Laboratory (https://actlab.org/) 主要開発者:  北畠一翔   ソフト種別:  オープンソースソフトウェア (GitHubリポジトリ:https://github.com/actlaboratory/ULTRA/) diff --git a/version.json b/version.json index 3e841c8..8096acd 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{"version": "1.2.1", "release_date": "2022-02-02"} \ No newline at end of file +{"version": "1.3.0", "release_date": "2022-02-11"} \ No newline at end of file