Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -38,9 +38,10 @@ HEADERS_POST = {
|
|
| 38 |
SSYOUTUBE_URL = "https://ssyoutube.online/yt-video-detail/"
|
| 39 |
|
| 40 |
# ===== tflink =====
|
| 41 |
-
TFLINK_UPLOAD_URL =
|
| 42 |
-
|
| 43 |
-
|
|
|
|
| 44 |
|
| 45 |
# ===== Utils =====
|
| 46 |
def parse_updated_at(value):
|
|
@@ -94,68 +95,45 @@ def fetch_download_links(youtube_url):
|
|
| 94 |
|
| 95 |
return results
|
| 96 |
|
| 97 |
-
# =====
|
| 98 |
-
def
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 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 |
-
|
| 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(
|
| 131 |
-
with open(
|
| 132 |
-
res = requests.post(
|
| 133 |
-
|
| 134 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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": [
|
|
|
|
|
|
|
| 159 |
"buttons": None,
|
| 160 |
"form": None,
|
| 161 |
"webPage": None,
|
|
@@ -163,7 +141,12 @@ def send_to_channel(text):
|
|
| 163 |
"customPayload": None
|
| 164 |
}
|
| 165 |
|
| 166 |
-
res = requests.post(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
res.raise_for_status()
|
| 168 |
|
| 169 |
# ===== Main =====
|
|
@@ -204,28 +187,32 @@ def main():
|
|
| 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 |
-
|
| 214 |
-
audio_item = next((i for i in items if i['has_audio']=='true' and i['quality']=='audio'), None)
|
| 215 |
|
| 216 |
-
if not
|
| 217 |
-
print("
|
| 218 |
time.sleep(10)
|
| 219 |
continue
|
| 220 |
|
| 221 |
-
|
| 222 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
|
| 224 |
-
# tflink アップロード
|
| 225 |
tflink_url = upload_to_tflink(output_file)
|
| 226 |
-
os.remove(output_file)
|
| 227 |
|
| 228 |
-
message_text =
|
| 229 |
send_to_channel(message_text)
|
| 230 |
print("送信完了")
|
| 231 |
|
|
@@ -235,4 +222,4 @@ def main():
|
|
| 235 |
time.sleep(15)
|
| 236 |
|
| 237 |
if __name__ == "__main__":
|
| 238 |
-
main()
|
|
|
|
| 38 |
SSYOUTUBE_URL = "https://ssyoutube.online/yt-video-detail/"
|
| 39 |
|
| 40 |
# ===== tflink =====
|
| 41 |
+
TFLINK_UPLOAD_URL = "https://tflink.example/upload" # ここを実際のURLに置き換え
|
| 42 |
+
TFLINK_API_KEY = os.getenv("TFLINK_API_KEY")
|
| 43 |
+
if not TFLINK_API_KEY:
|
| 44 |
+
raise RuntimeError("環境変数 TFLINK_API_KEY が設定されていません")
|
| 45 |
|
| 46 |
# ===== Utils =====
|
| 47 |
def parse_updated_at(value):
|
|
|
|
| 95 |
|
| 96 |
return results
|
| 97 |
|
| 98 |
+
# ===== ファイル操作 =====
|
| 99 |
+
def download_file(url, filename):
|
| 100 |
+
res = requests.get(url, stream=True)
|
| 101 |
+
res.raise_for_status()
|
| 102 |
+
with open(filename, "wb") as f:
|
| 103 |
+
for chunk in res.iter_content(chunk_size=8192):
|
| 104 |
+
f.write(chunk)
|
| 105 |
+
|
| 106 |
+
# ===== ffmpeg 結合 =====
|
| 107 |
+
def merge_video_audio(video_path, audio_path, output_path):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
cmd = [
|
| 109 |
+
"ffmpeg", "-y",
|
| 110 |
+
"-i", video_path,
|
| 111 |
+
"-i", audio_path,
|
| 112 |
+
"-c:v", "copy",
|
| 113 |
+
"-c:a", "aac",
|
| 114 |
+
output_path
|
| 115 |
]
|
| 116 |
subprocess.run(cmd, check=True)
|
| 117 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
# ===== tflink アップロード =====
|
| 119 |
+
def upload_to_tflink(filepath):
|
| 120 |
+
with open(filepath, "rb") as f:
|
| 121 |
+
res = requests.post(
|
| 122 |
+
TFLINK_UPLOAD_URL,
|
| 123 |
+
headers={"Authorization": f"Bearer {TFLINK_API_KEY}"},
|
| 124 |
+
files={"file": f},
|
| 125 |
+
)
|
| 126 |
+
res.raise_for_status()
|
| 127 |
+
data = res.json()
|
| 128 |
+
return data.get("url")
|
| 129 |
|
| 130 |
# ===== Channel.io 送信 =====
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
def send_to_channel(text):
|
| 132 |
payload = {
|
| 133 |
"requestId": f"desk-web-{int(time.time() * 1000)}",
|
| 134 |
+
"blocks": [
|
| 135 |
+
{"type": "text", "value": text}
|
| 136 |
+
],
|
| 137 |
"buttons": None,
|
| 138 |
"form": None,
|
| 139 |
"webPage": None,
|
|
|
|
| 141 |
"customPayload": None
|
| 142 |
}
|
| 143 |
|
| 144 |
+
res = requests.post(
|
| 145 |
+
POST_URL,
|
| 146 |
+
headers=HEADERS_POST,
|
| 147 |
+
data=json.dumps(payload),
|
| 148 |
+
timeout=30
|
| 149 |
+
)
|
| 150 |
res.raise_for_status()
|
| 151 |
|
| 152 |
# ===== Main =====
|
|
|
|
| 187 |
|
| 188 |
youtube_url = f"https://www.youtube.com/watch?v={youtube_id}"
|
| 189 |
items = fetch_download_links(youtube_url)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
|
| 191 |
+
video_items = [i for i in items if i['has_audio'] == 'false']
|
| 192 |
+
audio_items = [i for i in items if i['has_audio'] == 'true' and i['quality'] == 'audio']
|
|
|
|
| 193 |
|
| 194 |
+
if not video_items or not audio_items:
|
| 195 |
+
print("必要な動画/音声が取得できませんでした")
|
| 196 |
time.sleep(10)
|
| 197 |
continue
|
| 198 |
|
| 199 |
+
# 高画質の動画を選択
|
| 200 |
+
video_items.sort(key=lambda x: int(re.sub(r'[^0-9]', '', x['quality'])), reverse=True)
|
| 201 |
+
video_url = video_items[0]['url']
|
| 202 |
+
audio_url = audio_items[0]['url']
|
| 203 |
+
|
| 204 |
+
video_file = "video.mp4"
|
| 205 |
+
audio_file = "audio.m4a"
|
| 206 |
+
output_file = "output.mp4"
|
| 207 |
+
|
| 208 |
+
download_file(video_url, video_file)
|
| 209 |
+
download_file(audio_url, audio_file)
|
| 210 |
+
|
| 211 |
+
merge_video_audio(video_file, audio_file, output_file)
|
| 212 |
|
|
|
|
| 213 |
tflink_url = upload_to_tflink(output_file)
|
|
|
|
| 214 |
|
| 215 |
+
message_text = f"結合済み動画はこちら: {tflink_url}"
|
| 216 |
send_to_channel(message_text)
|
| 217 |
print("送信完了")
|
| 218 |
|
|
|
|
| 222 |
time.sleep(15)
|
| 223 |
|
| 224 |
if __name__ == "__main__":
|
| 225 |
+
main()
|