/
mikochiku_alarm.py
245 lines (205 loc) · 9.02 KB
/
mikochiku_alarm.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#!/usr/bin/env python3
import sys
import os
import webbrowser
import pygame.mixer
import json
import settings
import config_tab
import release_notice
import log_viewer
import logger
import vparser
import platform
from PyQt5.QtWidgets import (QWidget, QCheckBox, QPushButton,
QLabel, QListWidget, QMessageBox)
from PyQt5.QtGui import QIcon, QPixmap, QCloseEvent
from PyQt5.QtCore import Qt, QTimer, QCoreApplication
from httpreq import HttpRequest
from tasktray import TrayWidget
import toast
from singleapp import QtSingleApplication
PY3 = sys.version_info[0] == 3
if PY3:
from urllib.parse import urlencode
from queue import Queue
else:
from Queue import Queue
from urllib import urlencode
log = logger.get_logger(__name__)
class MikochikuAlarm(QWidget):
def __init__(self, parent=None):
super(MikochikuAlarm, self).__init__(parent)
self.search_ch_id = settings.CHID
self.old_video_id_list = []
self.request = HttpRequest()
# メンバー一覧のjsonを取得し、memberに格納
with open("./res/channel/hololive.json", encoding="UTF-8") as file:
self.member = json.load(file)
self.initUI()
# 起動直後にチャンネルIDを調べる
self.check_live()
# 古いログファイルを削除しログファイルの数を一定個数以下にする。(既定値:5個)
logger.remove_old_log()
def initUI(self):
self.timer = QTimer(self)
self.timer.timeout.connect(self.check_live)
self.timer.setInterval(40000)
self.timer.start()
sakura_miko = QLabel(self)
sakura_miko.setPixmap(QPixmap(resource_path(settings.ICON)))
sakura_miko.move(65, 70)
self.alarm_cb = QCheckBox(self.localized_text("alarm"), self)
self.alarm_cb.toggle()
self.webbrowser_cb = QCheckBox(self.localized_text("webbrowser"), self)
self.webbrowser_cb.toggle()
self.alarm_state = "waiting"
self.alarm_stop = QPushButton(self.localized_text("waiting"), self)
self.alarm_stop.clicked[bool].connect(self.stop_alarm)
self.config_btn = QPushButton("config", self)
self.config_btn.clicked.connect(self.config_dialog)
self.dialogs = list()
# setGeometry
self.alarm_cb .setGeometry( 10, 10, 250, 20)
self.webbrowser_cb.setGeometry( 10, 30, 250, 20)
self.alarm_stop .setGeometry( 80, 80, 80, 25)
self.config_btn .setGeometry(195, 120, 60, 25)
main_width = 260
main_height = 150
self.setGeometry(300, 300, main_width, main_height)
self.setFixedSize(main_width, main_height)
self.setWindowTitle(self.localized_text("title"))
# メンバー名をlistWidgetに格納
self.listWidget = QListWidget(self)
for v in self.member:
self.listWidget.addItem(v['name'])
self.listWidget.move(30, 200)
self.listWidget.itemClicked.connect(self.set_target_channel)
# v 更新通知 / ログ出力 タブ表示用
# self.notice_dialog()
# self.log_viewer_dialog()
# タスクトレイ周り
self.tray = TrayWidget(self)
self.tray.show()
self.show()
def config_dialog(self):
config = config_tab.ConfigTab(self)
self.dialogs.append(config)
def notice_dialog(self):
notice = release_notice.ReleaseNotice(self)
self.dialogs.append(notice)
def log_viewer_dialog(self):
log_out = log_viewer.LogViewer(self)
self.dialogs.append(log_out)
def set_target_channel(self, qmode8ndex):
# 要素番号使うのでcurrentRow()に変更
member = self.member[self.listWidget.currentRow()]
self.search_ch_id = member['channel_id']
def check_live(self):
videos = []
should_open_browser = self.webbrowser_cb.checkState()
buff_video_id_set = self.get_live_video_id(self.search_ch_id)
for getting_video_id in buff_video_id_set.keys():
if getting_video_id in self.old_video_id_list:
continue
videos.append(
{'vid': getting_video_id,
'title': buff_video_id_set[getting_video_id]})
self.old_video_id_list.append(getting_video_id)
if len(self.old_video_id_list) > 30:
self.old_video_id_list.pop(0)
log.info(''.join([self.localized_text("started"), ' -[', getting_video_id, '] ', buff_video_id_set[getting_video_id]]))
log.debug(f"buff_video_id_set: {[id for id in buff_video_id_set.keys()]}")
log.debug(f"self.old_video_id_list {self.old_video_id_list}")
self.alarm_stop.click()
self.alarm_state = "stop"
self.alarm_stop.setText(self.localized_text("stop"))
if should_open_browser:
webbrowser.open(
"https://www.youtube.com/watch?v=" + getting_video_id)
if self.alarm_cb.checkState():
self.alarm_sound()
if len(videos) > 0:
t = toast.Toast(self, videos, should_open_browser)
QTimer.singleShot(10000, t.close)
def stop_alarm(self):
pygame.mixer.music.stop()
self.alarm_stop.setEnabled(True)
self.alarm_state = "waiting"
self.alarm_stop.setText(self.localized_text("waiting"))
def alarm_sound(self):
# loop = 1
# if self.loop_cb.checkState():
loop_count = 5
pygame.mixer.music.play(loop_count)
pygame.mixer.music.play(loop_count)
def get_live_video_id(self, search_ch_id):
try:
self.request = HttpRequest()
source = vparser.get_source_json(self.request, search_ch_id)
video_ids = vparser.extract_video_ids(source)
return video_ids
except vparser.InvalidChannelIDException:
# チャンネルページが見つからない場合
# TODO: アラートダイアログをポップアウトさせたい
log.error(f'{search_ch_id} は、存在しないチャンネルです。')
except Exception as e:
log.error('不明なエラーが発生しました')
log.error(f'{type(e)}:{str(e)}')
return {}
def load_locale_json(self): # from json file
path = "./res/lang/locale.json"
with open(path, mode='r') as file:
dict_json = json.load(file)
return dict_json["locale"]
def localized_text(self, content):
path = "./res/lang/" + self.load_locale_json() + ".json"
with open(path, encoding="UTF-8") as file:
dict_json = json.load(file)
return dict_json[content]
def update_ui_language(self):
self.setWindowTitle(self.localized_text("title"))
self.webbrowser_cb.setText(self.localized_text("webbrowser"))
self.alarm_cb.setText(self.localized_text("alarm"))
self.alarm_stop.setText(self.localized_text(self.alarm_state))
def changeEvent(self, event):
# メインウィジェットの最小化ボタンを押したら非表示にする。
if self.windowState() & Qt.WindowMinimized:
# TODO: config内の「最小化時にタスクトレイの格納」にチェックが入っているか否かの
# 判定をここで行う。チェックが入っている場合はhide()を実行。
self.hide()
def closeEvent(self, event: QCloseEvent):
# main()にてapp.setQuitOnLastWindowClosed(False)を設定しているため
# そのままだとcloseEventがスルーされXボタンを押しても終了できない。
# この関数でcloseEventを捕捉して終了させる。
QCoreApplication.quit()
def resource_path(relative):
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS, relative)
return os.path.join(relative)
def main():
log.info("---App start---")
log.debug(f"platform: {sys.platform} / python ver: {platform.python_version()}")
pygame.mixer.init()
alarm_path = "./res/alarm.mp3"
if os.path.exists(alarm_path):
pygame.mixer.music.load(alarm_path)
# 同じ場所からの二重起動を防ぐ
appGuid = os.getcwd()
app = QtSingleApplication(appGuid, sys.argv)
# トレイ格納時に強制終了するのを防ぐためsetQuitOnLastWindowClosed(False)を設定。
app.setQuitOnLastWindowClosed(False)
icon = QIcon(resource_path(settings.ICON))
# 二重起動チェック
if app.isRunning():
msg = QMessageBox(icon=icon)
msg.setWindowTitle("Caution!")
msg.setText("mikochiku_alarmは既に起動中です。")
msg.exec_()
# 通常起動
else:
app.setWindowIcon(icon)
mk = MikochikuAlarm()
sys.exit(app.exec_())
if __name__ == '__main__':
main()