2020年5月4日月曜日

超A&G+ 録画方法 〜サーバー変更について〜

超A&G+のスマートな録画方法を考える。
 文化放送のインターネットラジオ、超A&G+を録画するうえで注意しなければいけないのはRTMPの配信サーバーの変更が時々行われることである。この番組を録画できるツールはRadikool、AnGe4、らじれこなど複数あるが、これらのソフトは配信サーバー変更のたびにアップデートを待たないと録画できない。そこで、超A&G+の配信サーバー変更を自動的に検知して録画する方法がないか探してみた。

配信サーバーのアドレス

配信サーバーのアドレスは
rtmp://fms-base1.mitene.ad.jp/agqr/aandgXX
で、末尾のXXが時々変化する。いろいろな解説サイトを見ても、このアドレスの取得方法が書かれておらず、「○○に変更してください」的なものばかりで根本的な解決策にはならない。

さらにいろいろ調べていると、どうやらこのアドレスに配信サーバーのリストがあるようなので、録画前ここから配信サーバーのアドレスを取得して使用するスプリクトを作成すればおそらく正常に録画できるはずである。
配信サーバーのアドレスが有効かどうかを調べる方法は、「超A&G+の放送URLが有効か確認するスクリプト」を参考にさせていただいた。

スクリプト

辛うじて使えるpythonでいろいろ苦戦しながら書いてみた。
まず予約管理はつくるのがめんどくさいのでWindowsのタスクスケジューラーに丸投げすることにして、予約を登録するだけのスクリプトと実際に録画時に実行するスクリプトの2つに分けた。本当は一つにできるのかもしれないけど。

agdump

サーバーを取得してrtmpdumperの引数に入れるだけ。
#-*- coding: utf-8 -*-
import requests
import re
import os
import subprocess
import shutil
import argparse
import datetime
import configparser
from pathlib import Path
from time import sleep
from datetime import datetime as dt
from datetime import timedelta
from plyer import notification
headers_dic = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36"}
listurl='http://www.uniqueradio.jp/agplayerf/getfmsListHD.php'

#引数
parser = argparse.ArgumentParser()
parser.add_argument('B', help='録画時間(ss)')
parser.add_argument('OUT', help='ファイル名')
parser.add_argument('-cd', '--CD', help='作業ディレクトリ')
args = parser.parse_args()

out = args.OUT
b = args.B
cd = args.CD


now = dt.now()
d = now.strftime('%Y%m%d%H%M')

#パス
try:
 dir = Path(cd)
except:
 dir = Path.cwd()
ffmpegpath = dir / 'exe' / 'ffmpeg.exe'
dumppath = dir / 'exe' / 'rtmpdump.exe'
inipath = dir / 'setting.ini'

if not inipath.exists():
 r_dir = dir / 'rec' / out

config_ini = configparser.ConfigParser()
config_ini.read(str(inipath), encoding='utf-8')


try:
 r_dir = Path(config_ini['Dir']['SaveDir']) / out
except KeyError:
 r_dir = dir / 'rec' / out

#ディレクトリ準備
r_dir.mkdir(exist_ok=True,parents=True)


def svchk():
 #サーバーチェック
 rq = requests.get(listurl)
 if rq.status_code != 200:
  err = 1
 else:
  src = str(rq.content).replace(r'\n',"")
  src = src.replace(r'\r',"")
  src = src.replace(r'\t',"")
  serverlist = re.findall(r'(.+?)<\⁄serverinfo>',src)
  serverlist = [s for s in serverlist if not 'true' in s]
  rtmplist = []
  for server in serverlist:
   s = re.findall(r'.+?(rtmp.+?)<\⁄server>', server)
   app = re.findall(r'(.+?)<\⁄app>', server)
   stream = re.findall(r'(.+?)<\⁄stream>', server)
   url = '{}/{}/{}'.format(s[0],app[0],stream[0])
   rtmplist.append(url)
  #rtmpdump -r url --live -B dur(sec) -o filename.flv
  for rtmp in rtmplist:
   chk = dir / 'chk.flv'
   cmd = str(dumppath) + ' -r ' + rtmplist[0] + ' --live -B 1 -o ' +str(chk)
   serverchk = subprocess.Popen(cmd,shell=True)
   serverchk.communicate()
   if chk.stat().st_size != 0:
    url = rtmplist[0]
    err = 0
    chk.unlink()
    break
   else:
    url = None
    err = 1
 
 if err == 1:
  url = None
 return url
 



rtmpurl = svchk()
if not rtmpurl:
 notification.notify(
  title='AGdump',
  message='サーバーエラーです',
 )
flv = str(r_dir) +'\\' + ('{0}_{1}').format(d,out) + '.flv'
mp4 = str(r_dir) +'\\' + ('{0}_{1}').format(d,out) + '.mp4'
cmd = ('{0} -r "{1}" --live -B {2} -o "{3}"').format(str(dumppath),rtmpurl,b,flv)
rec = subprocess.Popen(cmd,shell=True)
rec.communicate()
cmd = ('{0} -i "{1}" -y -c copy "{2}"').format(str(ffmpegpath),flv,mp4)
conv = subprocess.Popen(cmd,shell=True)
conv.communicate()
Path(flv).unlink()


agregister

録画開始時刻、録画時間、ファイル名(タスク名)を引数にしてタスクスケジューラーに登録する。
#-*- coding: utf-8 -*-
import requests
import re
import os
import subprocess
import shutil
import zipfile
import datetime
from pathlib import Path
from time import sleep
from datetime import datetime as dt
from datetime import timedelta

#変数
dir = Path.cwd()
r_dir = dir / 'rec'
e_dir = dir / 'exe'
agdumppath = dir / 'agdump.exe'
ffmpegpath = dir / 'exe' / 'ffmpeg.exe'
dumppath = dir / 'exe' / 'rtmpdump.exe'


def install():
 if not os.path.isfile(ffmpegpath):
  txt = requests.get(r'https://ffmpeg.zeranoe.com/builds/').text
  ver = re.search(r'<input type="radio" name="v" value="(.+?)" checked>', txt).group(1)
  latest = r'https://ffmpeg.zeranoe.com/builds/win64/static/ffmpeg-' + ver + r'-win64-static.zip'
  dir_zip = dir / 'ffmpeg.zip'
  f = open(dir_zip, 'wb')
  f.write(requests.get(latest).content)
  f.close()
  exe = r'ffmpeg-' + ver + '-win64-static/bin/ffmpeg.exe'
  zip_f = zipfile.ZipFile(str(dir_zip))
  zip_f.extract(exe, str(dir))
  zip_f.close()
  exe = r'\ffmpeg-' + ver + '-win64-static\\bin\\ffmpeg.exe'
  shutil.copyfile(str(dir) + exe, str(dir / 'exe') + r'\ffmpeg.exe')
  os.remove(dir_zip)
  shutil.rmtree(str(dir) + r'\ffmpeg-' + ver + '-win64-static')
 if not os.path.isfile(dumppath):
  latest = r'https://rtmpdump.mplayerhq.hu/download/rtmpdump-2.4-git-010913-windows.zip'
  dir_zip = dir / 'rtmp.zip'
  f = open(dir_zip, 'wb')
  f.write(requests.get(latest).content)
  f.close()
  exe = r'rtmpdump.exe'
  zip_f = zipfile.ZipFile(str(dir_zip))
  zip_f.extract(exe, str(dir))
  zip_f.close()
  exe = r'\rtmpdump.exe'
  shutil.copyfile(str(dir) + exe, str(dir / 'exe') + r'\rtmpdump.exe')
  dir_zip.unlink()
  rtm = dir / 'rtmpdump.exe'
  rtm.unlink()




def ngchr(str): #禁則文字
 dic={'\¥': '¥', '/': '/', ':': ':', '*': '*', '?': '?', '!': '!', '¥"': '”', '<': '<', '>': '>','|': '|'}
 table='\\/:*?!"<>|' 
 for ch in table:
  if ch in str:
   rm = dic.pop(ch)
   str = str.replace(ch,rm)
 return str

#ディレクトリ準備
e_dir.mkdir(exist_ok=True)

install() #ffmpegとrtmpdumperのインストール

print('超A&G+録画予約ツール') 
mode = input('Mode? N=新規,D=削除 (N or D):>')



if mode == 'D':
 key = input('[削除用]番組名?:>')
 key = ngchr(key)
 cmd = ('schtasks /delete /tn "{0}"').format(key)
 delete = subprocess.Popen(cmd)
 delete.communicate()

elif mode == 'N':
 pgdata = plget()
 key = input('[登録用]番組名?:>') 
 dur = input('放送時間を入力してください(mm):>')
 fa = input('放送開始時刻を入力してください(YYYYMMDDhhmm):>')

 dur = int(dur) * 60 + 60
 ft = dt.strptime(fa,'%Y%m%d%H%M') - timedelta(minutes=1)
 sd = ft.strftime('%Y/%m/%d')
 t = ft.strftime('%H:%M')
 yobi = ['MON','TUE','WED','THU','FRI','SAT','SUN']
 d = yobi[ft.weekday()]
 key=ngword(key)
 
 rtmpc =  ('\'{0}\' {1} \'{2}\' -cd \'{3}\'').format(str(agdumppath),dur,key,str(dir))
 cmd = ('schtasks /create /tn "{0}" /tr "{1}" /sc weekly /d "{2}" /sd "{3}" /st "{4}"').format(key,rtmpc,d,sd,t)
 #print(key)
 #input()
 regi = subprocess.Popen(cmd)
 regi.communicate()
else:
 print('"N" or "D"で入力して下さい。')
print('3秒後に終了します。')
sleep(3)

一通り確認できたのでpyinstallerでexe化して運用する。
多分ところどころおかしいかもしれないが、初心者なので勘弁。

まとめ

このツールを作ってからサーバー変更があったという話は聞かないのでちゃんと動くかは不明。一応現状録画は成功しているので、多分問題なく動くはず。
※2020/06/21追記
やっぱり色々間違ってた(サーバー関連は問題なさそう)ので使いたい人は適宜直してくださいm(__)m

0 件のコメント:

コメントを投稿