diff --git a/README.md b/README.md index 92be7d5..3be0739 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,31 @@ API를 제공합니다. 다른 플러그인에서 동영상 정보나 다운로 `errorCode` | 에러 코드 | Integer `index` | 동영상 인덱스. 이후 다운로드를 제어할 때 이 값이 필요함 | Integer +### /youtube-dl/api/sub +자막 다운로드 준비를 요청하는 API +#### Request +키 | 설명 | 필수 | 타입 +--- | --- | --- | --- +`plugin` | 플러그인 이름 | O | String +`key` | 임의의 키. 이후 다운로드를 제어할 때 이 키가 필요함 | O | String +`url` | 동영상 주소 | O | String +`filename` | 파일명. 템플릿 규칙은 https://github.com/ytdl-org/youtube-dl/#output-template 참고. 미지정 시 사용자 설정 | X | String +`save_path` | 저장 폴더 경로. 미지정 시 사용자 설정 | X | String +`all_subs` | 모든 자막 다운로드 여부. 기본값: `false` | X | Boolean +`sub_lang` | 자막 언어. 두 자리 국가 코드. 콤마(,)를 구분자로 여러 개 지정 가능. `all_subs` 키가 `false`일 때만 유효. 기본값: `"ko"` | X | String +`auto_sub` | 자동생성 자막 다운로드 여부. 유튜브 전용. 기본값: `false` | X | Boolean +`dateafter` | 지정한 날짜 이후에 업로드된 동영상만 다운로드. 미지정 시 모든 동영상 다운로드 | X | String +`archive` | 다운로드한 동영상의 ID를 기록할 파일 경로. 파일이 이미 있으면 이미 다운로드한 동영상은 다운로드 하지 않음. 미지정 시 기록하지 않음 | X | String +`start` | 다운로드 준비 후 바로 다운로드를 시작할지 여부. 기본값: `false` | X | Boolean +`cookiefile` | 다운로드 시 필요한 쿠키 파일 경로 | X | String + +`dateafter` 키에 넣을 수 있는 날짜는 `YYYYMMDD` 또는 `(now|today)[+-][0-9](day|week|month|year)(s)?` 형식의 문자열입니다. +#### Response +키 | 설명 | 타입 +--- | --- | --- +`errorCode` | 에러 코드 | Integer +`index` | 동영상 인덱스. 이후 다운로드를 제어할 때 이 값이 필요함 | Integer + ### /youtube-dl/api/start 다운로드 시작을 요청하는 API #### Request @@ -147,7 +172,7 @@ API를 제공합니다. 다른 플러그인에서 동영상 정보나 다운로 --- | --- | --- `errorCode` | 에러 코드 | Integer `status` | 요청을 받았을 당시의 상태 | Status -`type` | 다운로드 타입. `"video" "thumbnail"` | String +`type` | 다운로드 타입. `"video" "thumbnail" "subtitle"` | String `start_time` | 다운로드 시작 시간 | String `end_time` | 다운로드 종료 시간 | String `temp_path` | 임시 폴더 경로 | String @@ -160,6 +185,8 @@ API를 제공합니다. 다른 플러그인에서 동영상 정보나 다운로 v2.4.0 * 썸네일 다운로드 기능 추가 * thumbnail API 추가 +* 자막 다운로드 기능 추가 +* sub API 추가 * 설정에 경로 선택 버튼 추가 v2.3.1 diff --git a/logic_normal.py b/logic_normal.py index 334ce91..dc1d0ab 100644 --- a/logic_normal.py +++ b/logic_normal.py @@ -162,6 +162,46 @@ class LogicNormal(object): logger.error(traceback.format_exc()) return None + @staticmethod + def sub(**kwagrs): + try: + logger.debug(kwagrs) + plugin = kwagrs['plugin'] + url = kwagrs['url'] + filename = kwagrs['filename'] + temp_path = kwagrs['temp_path'] + save_path = kwagrs['save_path'] + opts = { + 'skip_download': True + } + sub_lang = map(lambda x: x.strip(), kwagrs['sub_lang'].split(',')) # 문자열을 리스트로 변환 + if 'all_subs' in kwagrs and str(kwagrs['all_subs']).lower() != 'false': + opts['allsubtitles'] = True + else: + opts['subtitleslangs'] = sub_lang + if 'auto_sub' in kwagrs and str(kwagrs['auto_sub']).lower() != 'false': + opts['writeautomaticsub'] = True + else: + opts['writesubtitles'] = True + + if 'archive' in kwagrs and kwagrs['archive']: + opts['download_archive'] = kwagrs['archive'] + if 'proxy' in kwagrs and kwagrs['proxy']: + opts['proxy'] = kwagrs['proxy'] + if 'ffmpeg_path' in kwagrs and kwagrs['ffmpeg_path']: + opts['ffmpeg_location'] = kwagrs['ffmpeg_path'] + if 'cookiefile' in kwagrs and kwagrs['cookiefile']: + opts['cookiefile'] = kwagrs['cookiefile'] + dateafter = kwagrs.get('dateafter') + youtube_dl = MyYoutubeDL(plugin, 'subtitle', url, filename, temp_path, save_path, opts, dateafter) + youtube_dl.key = kwagrs.get('key') + LogicNormal.youtube_dl_list.append(youtube_dl) # 리스트 추가 + return youtube_dl + except Exception as e: + logger.error('Exception:%s', e) + logger.error(traceback.format_exc()) + return None + @staticmethod def get_data(youtube_dl): try: diff --git a/plugin.py b/plugin.py index 8f1e19a..125a100 100644 --- a/plugin.py +++ b/plugin.py @@ -41,7 +41,8 @@ if ModelSetting.get_bool('activate_cors'): menu = { 'main': [package_name, 'youtube-dl'], 'sub': [ - ['setting', '설정'], ['download', '다운로드'], ['thumbnail', '썸네일 다운로드'], ['list', '목록'], ['log', '로그'] + ['setting', '설정'], ['download', '다운로드'], ['thumbnail', '썸네일 다운로드'], ['sub', '자막 다운로드'], ['list', '목록'], + ['log', '로그'] ], 'category': 'vod' } @@ -103,6 +104,11 @@ def first_menu(sub): arg['filename'] = default_filename if default_filename else LogicNormal.get_default_filename() return render_template('%s_%s.html' % (package_name, sub), arg=arg) + elif sub == 'sub': + default_filename = ModelSetting.get('default_filename') + arg['filename'] = default_filename if default_filename else LogicNormal.get_default_filename() + return render_template('%s_%s.html' % (package_name, sub), arg=arg) + elif sub == 'list': return render_template('%s_%s.html' % (package_name, sub), arg=arg) @@ -175,6 +181,21 @@ def ajax(sub): socketio_emit('add', youtube_dl) return jsonify([]) + elif sub == 'sub': + youtube_dl = LogicNormal.sub(plugin=package_name, + url=request.form['url'], + filename=request.form['filename'], + temp_path=ModelSetting.get('temp_path'), + save_path=ModelSetting.get('save_path'), + all_subs=request.form['all_subs'], + sub_lang=request.form['sub_lang'], + auto_sub=request.form['auto_sub'], + proxy=ModelSetting.get('proxy'), + ffmpeg_path=ModelSetting.get('ffmpeg_path')) + youtube_dl.start() + socketio_emit('add', youtube_dl) + return jsonify([]) + elif sub == 'list': ret = [] for i in LogicNormal.youtube_dl_list: @@ -316,6 +337,51 @@ def api(sub): socketio_emit('add', youtube_dl) return jsonify(ret) + # 자막 다운로드 준비를 요청하는 API + elif sub == 'sub': + key = request.values.get('key') + url = request.values.get('url') + filename = request.values.get('filename', ModelSetting.get('default_filename')) + save_path = request.values.get('save_path', ModelSetting.get('save_path')) + all_subs = request.values.get('all_subs', False) + sub_lang = request.values.get('sub_lang', 'ko') + auto_sub = request.values.get('all_subs', False) + dateafter = request.values.get('dateafter', None) + archive = request.values.get('archive', None) + start = request.values.get('start', False) + cookiefile = request.values.get('cookiefile', None) + ret = { + 'errorCode': 0, + 'index': None + } + if None in (key, url): + return LogicNormal.abort(ret, 1) # 필수 요청 변수가 없음 + if not url.startswith('http'): + return LogicNormal.abort(ret, 2) # 잘못된 동영상 주소 + if not filename: + filename = LogicNormal.get_default_filename() + youtube_dl = LogicNormal.sub(plugin=plugin, + url=url, + filename=filename, + temp_path=ModelSetting.get('temp_path'), + save_path=save_path, + all_subs=all_subs, + sub_lang=sub_lang, + auto_sub=auto_sub, + dateafter=dateafter, + archive=archive, + proxy=ModelSetting.get('proxy'), + ffmpeg_path=ModelSetting.get('ffmpeg_path'), + key=key, + cookiefile=cookiefile) + if youtube_dl is None: + return LogicNormal.abort(ret, 10) # 실패 + ret['index'] = youtube_dl.index + if start: + youtube_dl.start() + socketio_emit('add', youtube_dl) + return jsonify(ret) + # 다운로드 시작을 요청하는 API elif sub == 'start': index = request.values.get('index') diff --git a/static/youtube-dl_sub.js b/static/youtube-dl_sub.js new file mode 100644 index 0000000..f5a166d --- /dev/null +++ b/static/youtube-dl_sub.js @@ -0,0 +1,31 @@ +"use strict"; + +const url = document.getElementById('url'); +const download_btn = document.getElementById('download_btn'); + +// 모든 자막 다운로드 +$('#all_subs').change(() => { + use_collapse('all_subs', true); +}); + +// 다운로드 +download_btn.addEventListener('click', (event) => { + event.preventDefault(); + if (!url.value.startsWith('http')) { + notify('URL을 입력하세요.', 'warning'); + return; + } + + fetch(`/${package_name}/ajax/sub`, { + method: 'POST', + cache: 'no-cache', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' + }, + body: get_formdata('#download') + }).then(response => response.json()).then(() => { + notify('분석중..', 'info'); + }).catch(() => { + notify('다운로드 요청 실패', 'danger'); + }); +}); diff --git a/templates/youtube-dl_sub.html b/templates/youtube-dl_sub.html new file mode 100644 index 0000000..8dfb1a4 --- /dev/null +++ b/templates/youtube-dl_sub.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} +{% block content %} + +
+ + + + +{% endblock %}