izuemon commited on
Commit
34963a0
·
verified ·
1 Parent(s): a8c8026

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +216 -85
app.py CHANGED
@@ -1,107 +1,238 @@
1
- from flask import Flask, request, jsonify, redirect, Response
2
  import os
 
 
 
3
  import requests
4
  import subprocess
5
- import sys
6
-
7
- app = Flask(__name__)
8
-
9
- watcher_process = None
10
-
11
- def start_watcher():
12
- global watcher_process
13
-
14
- if watcher_process is None or watcher_process.poll() is not None:
15
- watcher_process = subprocess.Popen(
16
- [sys.executable, "watcher.py"],
17
- stdout=sys.stdout,
18
- stderr=sys.stderr,
19
- env=os.environ, # 環境変数を引き継ぐ
20
- )
21
- print("watcher.py を起動しました")
22
-
23
-
24
- #----------
25
-
26
- @app.route("/drive.com/files")
27
- def index():
28
- ip = request.remote_addr
29
- print(f"アクセスIP: {ip}")
30
- return redirect("https://drive.google.com/")
31
-
32
- @app.route("/channel-io-managers")
33
- def get_managers():
34
- limit = request.args.get("limit")
35
- since = request.args.get("since")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
- params = {}
38
- if limit:
39
- params["limit"] = limit
40
- if since:
41
- params["since"] = since
42
 
43
- channel_id = request.args.get("channelid", "200605")
44
- url = f"https://desk-api.channel.io/desk/channels/{channel_id}/managers"
 
 
 
 
45
 
46
- headers = {
47
- "accept": "application/json",
48
- "x-account": os.getenv("channeliotokenmain"),
49
- }
50
 
51
- res = requests.get(url, headers=headers, params=params)
 
 
52
 
53
- if res.status_code != 200:
54
- return jsonify({"error": res.text}), res.status_code
55
 
56
- return jsonify(res.json().get("managers", []))
 
 
57
 
58
- @app.route("/cors-proxy", methods=["GET"])
59
- def corsproxy():
60
- url = request.args.get("url")
61
- if not url:
62
- return "url パラメータが必要です", 400
63
 
64
- if not url.startswith(("http://", "https://")):
65
- return "http または https のURLのみ使用できます", 400
 
66
 
67
- resp = requests.get(url, headers=request.headers, timeout=60)
 
 
 
 
68
 
69
- response = Response(resp.content, resp.status_code)
70
- response.headers["Access-Control-Allow-Origin"] = "*"
71
- response.headers["Access-Control-Allow-Headers"] = "*"
72
- response.headers["Access-Control-Allow-Methods"] = "GET, POST, PATCH, OPTIONS"
 
 
73
 
74
- if "Content-Type" in resp.headers:
75
- response.headers["Content-Type"] = resp.headers["Content-Type"]
 
76
 
77
- return response
 
 
 
78
 
79
- @app.route("/cors-proxy", methods=["POST", "PATCH"])
80
- def corsproxy_post():
81
- url = request.args.get("url")
82
- if not url:
83
- return "url パラメータが必要です", 400
84
 
85
- if not url.startswith(("http://", "https://")):
86
- return "http または https のURLのみ使用できます", 400
 
87
 
88
- resp = requests.request(
89
- method=request.method,
90
- url=url,
91
- headers=request.headers,
92
- data=request.data,
93
- timeout=60,
94
- )
95
 
96
- headers = {
97
- "Access-Control-Allow-Origin": "*",
98
- "Access-Control-Allow-Headers": "*",
99
- "Access-Control-Allow-Methods": "GET, POST, PATCH, OPTIONS",
100
- }
101
 
102
- return Response(resp.content, resp.status_code, headers=headers)
103
 
104
  if __name__ == "__main__":
105
- if os.environ.get("WERKZEUG_RUN_MAIN") != "true":
106
- start_watcher()
107
- app.run(debug=True, host="0.0.0.0", port=7860)
 
 
1
  import os
2
+ import re
3
+ import time
4
+ import json
5
  import requests
6
  import subprocess
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"
12
+ POST_URL = GET_URL
13
+
14
+ PARAMS = {
15
+ "sortOrder": "desc",
16
+ "limit": 36,
17
+ "logFolded": "false",
18
+ }
19
+
20
+ X_ACCOUNT = os.getenv("channeliotokenbot2")
21
+ if not X_ACCOUNT:
22
+ raise RuntimeError("環境変数 channeliotokenbot2 が設定されていません")
23
+
24
+ HEADERS_GET = {
25
+ "accept": "application/json",
26
+ "accept-language": "ja",
27
+ "x-account": X_ACCOUNT,
28
+ }
29
+
30
+ HEADERS_POST = {
31
+ "accept": "application/json",
32
+ "accept-language": "ja",
33
+ "content-type": "application/json",
34
+ "x-account": X_ACCOUNT,
35
+ }
36
+
37
+ # ===== ssyoutube =====
38
+ SSYOUTUBE_URL = "https://ssyoutube.online/yt-video-detail/"
39
+
40
+ # ===== tflink =====
41
+ TFLINK_UPLOAD_URL = os.getenv("TFLINK_UPLOAD_URL")
42
+ if not TFLINK_UPLOAD_URL:
43
+ raise RuntimeError("環境変数 TFLINK_UPLOAD_URL が設定されていません")
44
+
45
+ # ===== Utils =====
46
+ def parse_updated_at(value):
47
+ if isinstance(value, (int, float)):
48
+ return datetime.fromtimestamp(value / 1000, tz=timezone.utc)
49
+ elif isinstance(value, str):
50
+ return datetime.fromisoformat(value.replace("Z", "+00:00"))
51
+ return None
52
+
53
+ def extract_youtube_id(text):
54
+ patterns = [
55
+ r"v=([A-Za-z0-9_-]{11})",
56
+ r"youtu\.be/([A-Za-z0-9_-]{11})",
57
+ ]
58
+ for p in patterns:
59
+ m = re.search(p, text)
60
+ if m:
61
+ return m.group(1)
62
+ return None
63
+
64
+ # ===== ssyoutube HTML 解析 =====
65
+ def fetch_download_links(youtube_url):
66
+ res = requests.post(
67
+ SSYOUTUBE_URL,
68
+ data={"videoURL": youtube_url},
69
+ timeout=30,
70
+ headers={
71
+ "User-Agent": "Mozilla/5.0",
72
+ "Referer": "https://ssyoutube.online/",
73
+ }
74
+ )
75
+ res.raise_for_status()
76
+
77
+ soup = BeautifulSoup(res.text, "lxml")
78
+ buttons = soup.select("button[data-url]")
79
+
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 or "audio",
92
+ "has_audio": has_audio,
93
+ })
94
+
95
+ return results
96
+
97
+ # ===== 動画結合 =====
98
+ def merge_video_audio(video_url, audio_url, output_file):
99
+ # ダウンロード
100
+ video_path = "video.mp4"
101
+ audio_path = "audio.m4a"
102
+ with requests.get(video_url, stream=True) as r:
103
+ r.raise_for_status()
104
+ with open(video_path, "wb") as f:
105
+ for chunk in r.iter_content(chunk_size=8192):
106
+ f.write(chunk)
107
+
108
+ with requests.get(audio_url, stream=True) as r:
109
+ r.raise_for_status()
110
+ with open(audio_path, "wb") as f:
111
+ for chunk in r.iter_content(chunk_size=8192):
112
+ f.write(chunk)
113
+
114
+ # ffmpeg で結合
115
+ cmd = [
116
+ "ffmpeg", "-y",
117
+ "-i", video_path,
118
+ "-i", audio_path,
119
+ "-c:v", "copy",
120
+ "-c:a", "aac",
121
+ output_file
122
+ ]
123
+ subprocess.run(cmd, check=True)
124
+
125
+ # 一時ファイル削除
126
+ os.remove(video_path)
127
+ os.remove(audio_path)
128
+
129
+ # ===== tflink アップロード =====
130
+ def upload_to_tflink(file_path):
131
+ with open(file_path, "rb") as f:
132
+ res = requests.post(TFLINK_UPLOAD_URL, files={"file": f})
133
+ res.raise_for_status()
134
+ return res.json().get("url")
135
+
136
+ # ===== Channel.io 送信 =====
137
+ def build_links(items):
138
+ lines = []
139
+ for item in items:
140
+ url = item["url"]
141
+ quality = item["quality"]
142
+ has_audio = item["has_audio"]
143
+
144
+ audio_label = ""
145
+ if has_audio == "false":
146
+ audio_label = "(映像のみ)"
147
+ elif has_audio == "true":
148
+ audio_label = "(音声付き)"
149
+
150
+ line = f'<link type="url" value="{url}"> {quality} {audio_label}</link>'
151
+ lines.append(line)
152
+
153
+ return "\n".join(lines)
154
+
155
+ def send_to_channel(text):
156
+ payload = {
157
+ "requestId": f"desk-web-{int(time.time() * 1000)}",
158
+ "blocks": [{"type": "text", "value": text}],
159
+ "buttons": None,
160
+ "form": None,
161
+ "webPage": None,
162
+ "files": None,
163
+ "customPayload": None
164
+ }
165
 
166
+ res = requests.post(POST_URL, headers=HEADERS_POST, data=json.dumps(payload), timeout=30)
167
+ res.raise_for_status()
 
 
 
168
 
169
+ # ===== Main =====
170
+ def main():
171
+ while True:
172
+ try:
173
+ res = requests.get(GET_URL, headers=HEADERS_GET, params=PARAMS, timeout=30)
174
+ res.raise_for_status()
175
 
176
+ messages = res.json().get("messages", [])
177
+ latest_msg = None
178
+ latest_time = None
 
179
 
180
+ for msg in messages:
181
+ plain_text = msg.get("plainText")
182
+ updated_at = msg.get("updatedAt")
183
 
184
+ if not plain_text or updated_at is None:
185
+ continue
186
 
187
+ t = parse_updated_at(updated_at)
188
+ if not t:
189
+ continue
190
 
191
+ if latest_time is None or t > latest_time:
192
+ latest_time = t
193
+ latest_msg = msg
 
 
194
 
195
+ if not latest_msg:
196
+ time.sleep(10)
197
+ continue
198
 
199
+ text = latest_msg["plainText"]
200
+ youtube_id = extract_youtube_id(text)
201
+ if not youtube_id:
202
+ time.sleep(10)
203
+ continue
204
 
205
+ youtube_url = f"https://www.youtube.com/watch?v={youtube_id}"
206
+ items = fetch_download_links(youtube_url)
207
+ if not items:
208
+ print("ダウンロードリンクが取得できませんでした")
209
+ time.sleep(10)
210
+ continue
211
 
212
+ # 最も高画質の動画と音声を選択
213
+ video_item = max((i for i in items if i['has_audio']=='false'), key=lambda x: int(x['quality'].replace('p','') if x['quality']!='audio' else 0))
214
+ audio_item = next((i for i in items if i['has_audio']=='true' and i['quality']=='audio'), None)
215
 
216
+ if not video_item or not audio_item:
217
+ print("動画または音声が不足しています")
218
+ time.sleep(10)
219
+ continue
220
 
221
+ output_file = "merged.mp4"
222
+ merge_video_audio(video_item['url'], audio_item['url'], output_file)
 
 
 
223
 
224
+ # tflink アップロード
225
+ tflink_url = upload_to_tflink(output_file)
226
+ os.remove(output_file)
227
 
228
+ message_text = build_links(items) + f"\n結合ファイル: {tflink_url}"
229
+ send_to_channel(message_text)
230
+ print("送信完了")
 
 
 
 
231
 
232
+ except Exception as e:
233
+ print("エラー:", e)
 
 
 
234
 
235
+ time.sleep(15)
236
 
237
  if __name__ == "__main__":
238
+ main()