izuemon commited on
Commit
677e8ae
·
verified ·
1 Parent(s): 4e00570

Update watcher.py

Browse files
Files changed (1) hide show
  1. watcher.py +47 -98
watcher.py CHANGED
@@ -2,10 +2,11 @@ import os
2
  import re
3
  import time
4
  import json
5
- import subprocess
6
  import requests
7
  from datetime import datetime, timezone
8
  from bs4 import BeautifulSoup
 
 
9
 
10
  # ===== Channel.io 設定 =====
11
  GET_URL = "https://desk-api.channel.io/desk/channels/200605/groups/519217/messages"
@@ -19,7 +20,7 @@ PARAMS = {
19
 
20
  X_ACCOUNT = os.getenv("channeliotokenbot2")
21
  if not X_ACCOUNT:
22
- raise RuntimeError("環境変数 channeliotokenokenbot2 が設定されていません")
23
 
24
  HEADERS_GET = {
25
  "accept": "application/json",
@@ -37,11 +38,6 @@ HEADERS_POST = {
37
  # ===== ssyoutube =====
38
  SSYOUTUBE_URL = "https://ssyoutube.online/yt-video-detail/"
39
 
40
- # ===== tfLink クライアント =====
41
- from tflink import TFLinkClient
42
-
43
- tf_client = TFLinkClient() # 匿名アップロード
44
-
45
  # ===== Utils =====
46
  def parse_updated_at(value):
47
  if isinstance(value, (int, float)):
@@ -80,82 +76,41 @@ def fetch_download_links(youtube_url):
80
  results = []
81
  for btn in buttons:
82
  url = btn.get("data-url")
83
- quality = btn.get("data-quality")
84
- has_audio = btn.get("data-has-audio")
85
 
86
  if not url:
87
  continue
88
 
89
  results.append({
90
  "url": url,
91
- "quality": quality,
92
  "has_audio": has_audio,
93
  })
94
 
95
  return results
96
 
97
- # ===== 動画と音声の選別 =====
98
- def choose_best_streams(items):
99
- video_only = []
100
- audio_only = []
101
-
102
- for item in items:
103
- url = item["url"]
104
- quality = item["quality"] or ""
105
- has_audio = item["has_audio"]
106
-
107
- if url.endswith(".m4a") or "audio" in quality.lower():
108
- audio_only.append(item)
109
- else:
110
- video_only.append(item)
111
-
112
- if not video_only:
113
- video = None
114
- else:
115
- video = sorted(video_only,
116
- key=lambda x: int(re.sub(r"[^\d]", "", x["quality"] or "0")),
117
- reverse=True)[0]
118
-
119
- if not audio_only:
120
- audio = None
121
- else:
122
- audio = sorted(audio_only,
123
- key=lambda x: int(re.sub(r"[^\d]", "", x["quality"] or "0")),
124
- reverse=True)[0]
125
-
126
- return video, audio
127
-
128
-
129
- # ===== 結合 =====
130
- def merge_video_audio(video_url, audio_url, out_file):
131
- cmd = [
132
- "ffmpeg", "-y",
133
- "-i", video_url,
134
- "-i", audio_url,
135
- "-c", "copy",
136
- out_file,
137
- ]
138
- result = subprocess.run(cmd, capture_output=True)
139
- if result.returncode != 0:
140
- print("FFmpeg merge error:", result.stderr.decode())
141
- return False
142
- return True
143
 
144
- # ===== tfLink アップロード =====
145
  def upload_to_tflink(file_path):
146
- res = tf_client.upload(file_path)
147
- return res.download_link
148
-
149
- def build_links(items, upload_link):
150
- lines = []
151
- for item in items:
152
- url = item["url"]
153
- quality = item["quality"]
154
- line = f'<link type="url" value="{url}"> {quality}</link>'
155
- lines.append(line)
156
-
157
- lines.append(f"結合ファイルダウンロード: {upload_link}")
158
- return "\n".join(lines)
159
 
160
  def send_to_channel(text):
161
  payload = {
@@ -181,7 +136,6 @@ def send_to_channel(text):
181
  )
182
  res.raise_for_status()
183
 
184
- # ===== Main =====
185
  def main():
186
  while True:
187
  try:
@@ -226,41 +180,36 @@ def main():
226
 
227
  items = fetch_download_links(youtube_url)
228
  if not items:
 
229
  time.sleep(10)
230
  continue
231
 
232
- video_stream, audio_stream = choose_best_streams(items)
233
- if not video_stream or not audio_stream:
234
- print("映像または音声ストリームが足りません")
235
- time.sleep(10)
236
- continue
237
 
238
- # ダウンロードして結合
239
- temp_video = "video.mp4"
240
- temp_audio = "audio.mp4"
241
- merged = "merged_output.mp4"
242
-
243
- # ダウンロード
244
- with requests.get(video_stream["url"], stream=True) as r:
245
- with open(temp_video, "wb") as f:
246
- for chunk in r.iter_content(chunk_size=8192):
247
- f.write(chunk)
248
- with requests.get(audio_stream["url"], stream=True) as r:
249
- with open(temp_audio, "wb") as f:
250
- for chunk in r.iter_content(chunk_size=8192):
251
- f.write(chunk)
252
-
253
- if not merge_video_audio(temp_video, temp_audio, merged):
254
- print("結合失敗")
255
  time.sleep(10)
256
  continue
257
 
258
- # tfLink アップロード
259
- upload_link = upload_to_tflink(merged)
 
 
 
 
 
 
 
 
 
 
 
260
 
261
- message_text = build_links(items, upload_link)
262
  send_to_channel(message_text)
263
- print("送信完了")
264
 
265
  except Exception as e:
266
  print("エラー:", e)
@@ -268,4 +217,4 @@ def main():
268
  time.sleep(15)
269
 
270
  if __name__ == "__main__":
271
- main()
 
2
  import re
3
  import time
4
  import json
 
5
  import requests
6
  from datetime import datetime, timezone
7
  from bs4 import BeautifulSoup
8
+ import ffmpeg
9
+ from tflink import TFLinkClient
10
 
11
  # ===== Channel.io 設定 =====
12
  GET_URL = "https://desk-api.channel.io/desk/channels/200605/groups/519217/messages"
 
20
 
21
  X_ACCOUNT = os.getenv("channeliotokenbot2")
22
  if not X_ACCOUNT:
23
+ raise RuntimeError("環境変数 channeliotokenbot2 が設定されていません")
24
 
25
  HEADERS_GET = {
26
  "accept": "application/json",
 
38
  # ===== ssyoutube =====
39
  SSYOUTUBE_URL = "https://ssyoutube.online/yt-video-detail/"
40
 
 
 
 
 
 
41
  # ===== Utils =====
42
  def parse_updated_at(value):
43
  if isinstance(value, (int, float)):
 
76
  results = []
77
  for btn in buttons:
78
  url = btn.get("data-url")
79
+ quality = btn.get("data-quality") # 例: 1080p / 720p / None
80
+ has_audio = btn.get("data-has-audio") # "true" / "false" / None
81
 
82
  if not url:
83
  continue
84
 
85
  results.append({
86
  "url": url,
87
+ "quality": quality or "audio",
88
  "has_audio": has_audio,
89
  })
90
 
91
  return results
92
 
93
+ def download_file(url, filename):
94
+ with requests.get(url, stream=True) as r:
95
+ r.raise_for_status()
96
+ with open(filename, 'wb') as f:
97
+ for chunk in r.iter_content(chunk_size=8192):
98
+ f.write(chunk)
99
+ return filename
100
+
101
+ def merge_video_audio(video_file, audio_file, output_file):
102
+ (
103
+ ffmpeg
104
+ .input(video_file)
105
+ .output(audio_file, output_file, vcodec='copy', acodec='aac', strict='experimental')
106
+ .run(overwrite_output=True)
107
+ )
108
+ return output_file
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
 
 
110
  def upload_to_tflink(file_path):
111
+ client = TFLinkClient()
112
+ result = client.upload(file_path)
113
+ return result.download_link
 
 
 
 
 
 
 
 
 
 
114
 
115
  def send_to_channel(text):
116
  payload = {
 
136
  )
137
  res.raise_for_status()
138
 
 
139
  def main():
140
  while True:
141
  try:
 
180
 
181
  items = fetch_download_links(youtube_url)
182
  if not items:
183
+ print("ダウンロードリンクが取得できませんでした")
184
  time.sleep(10)
185
  continue
186
 
187
+ # ===== 最も高画質の動画と音声を選択 =====
188
+ video_items = [i for i in items if i["has_audio"] == "false"]
189
+ audio_items = [i for i in items if i["has_audio"] == "true" and i["quality"] == "audio"]
 
 
190
 
191
+ if not video_items or not audio_items:
192
+ print("動画または音声が見つかりません")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  time.sleep(10)
194
  continue
195
 
196
+ # 解像度順にソートして最初を取得
197
+ video_items.sort(key=lambda x: int(re.sub(r"\D", "", x["quality"] or "0")), reverse=True)
198
+ video_url = video_items[0]["url"]
199
+ audio_url = audio_items[0]["url"]
200
+
201
+ video_file = download_file(video_url, "video.mp4")
202
+ audio_file = download_file(audio_url, "audio.m4a")
203
+ output_file = "merged.mp4"
204
+
205
+ merge_video_audio(video_file, audio_file, output_file)
206
+
207
+ # tflink にアップロード
208
+ tflink_url = upload_to_tflink(output_file)
209
 
210
+ message_text = f"結合動画のダウンロード: {tflink_url}"
211
  send_to_channel(message_text)
212
+ print("送信完了:", tflink_url)
213
 
214
  except Exception as e:
215
  print("エラー:", e)
 
217
  time.sleep(15)
218
 
219
  if __name__ == "__main__":
220
+ main()