v2.4.0 썸네일, 자막 다운로드 기능 추가

API에 playlist 키 추가
썸네일 다운로드 기능 추가
thumbnail API 추가
자막 다운로드 기능 추가
sub API 추가
설정에 경로 선택 버튼 추가
This commit is contained in:
joyfuI
2021-04-28 21:03:47 +09:00
12 changed files with 452 additions and 23 deletions

View File

@@ -10,11 +10,10 @@ SJVA에서 "시스템 → 플러그인 → 플러그인 수동 설치" 칸에
시놀로지 docker 환경에서 테스트했습니다. 시놀로지 docker 환경에서 테스트했습니다.
API를 제공합니다. 다른 플러그인에서 동영상 정보나 다운로드를 요청할 수 있습니다. API를 제공합니다. 다른 플러그인에서 동영상 정보나 다운로드를 요청할 수 있습니다.
다른 플러그인이 멋대로 다운로드를 중지할 수 없도록 다운로드를 요청할 때 임의의 키를 넘겨받습니다. 중지 요청 시 키가 일치해야 요청이 실행됩니다. 다른 플러그인이 멋대로 다운로드를 중지할 수 없도록 다운로드를 요청할 때 임의의 키를 넘겨받습니다. 중지 요청 시 키가 일치해야 요청이 실행됩니다.
과연 이걸로 뭔가를 만드실 분이 계실지...
[youtube-dl](https://github.com/ytdl-org/youtube-dl)의 DMCA 테이크다운 사태 이후, 비슷한 상황을 대비하기 위해 youtube-dl의 포크 프로젝트 [youtube-dlc](https://github.com/blackjack4494/yt-dlc)의 포크 프로젝트(...)인 [yt-dlp](https://github.com/pukkandan/yt-dlp)를 추가했습니다. [youtube-dl](https://github.com/ytdl-org/youtube-dl)의 DMCA 테이크다운 사태 이후, 비슷한 상황을 대비하기 위해 youtube-dl의 포크 프로젝트 [youtube-dlc](https://github.com/blackjack4494/yt-dlc)의 포크 프로젝트(...)인 [yt-dlp](https://github.com/pukkandan/yt-dlp)를 추가했습니다.
설정에서 취향껏 골라서 사용하시면 되며 youtube-dl 외에는 곁다리 지원이라 작동하지 않거나 우선순위에서 밀릴 수 있습니다. 설정에서 취향껏 골라서 사용하시면 되며 youtube-dl가 우선 지원됩니다.
## API ## API
### 공통사항 ### 공통사항
@@ -58,7 +57,7 @@ API를 제공합니다. 다른 플러그인에서 동영상 정보나 다운로
그리고 만약 주소가 플레이리스트라면 `_type` 키에 `"playlist"`라는 값이 들어 있습니다. 이때는 `entries` 키에 리스트가 들어있어 동영상들의 제목과 ID를 확인할 수 있습니다. 그리고 만약 주소가 플레이리스트라면 `_type` 키에 `"playlist"`라는 값이 들어 있습니다. 이때는 `entries` 키에 리스트가 들어있어 동영상들의 제목과 ID를 확인할 수 있습니다.
### /youtube-dl/api/download ### /youtube-dl/api/download
다운로드 준비를 요청하는 API 비디오 다운로드 준비를 요청하는 API
#### Request #### Request
키 | 설명 | 필수 | 타입 키 | 설명 | 필수 | 타입
--- | --- | --- | --- --- | --- | --- | ---
@@ -71,12 +70,78 @@ API를 제공합니다. 다른 플러그인에서 동영상 정보나 다운로
`preferedformat` | 변환할 비디오 포맷. 가능한 포맷은 https://ffmpeg.org/general.html#File-Formats 참고. 미지정 시 변환하지 않음 | X | String `preferedformat` | 변환할 비디오 포맷. 가능한 포맷은 https://ffmpeg.org/general.html#File-Formats 참고. 미지정 시 변환하지 않음 | X | String
`preferredcodec` | 추출할 오디오 코덱. 가능한 값은 `"best"`, `"mp3"`, `"aac"`, `"flac"`, `"m4a"`, `"opus"`, `"vorbis"`, `"wav"`. 미지정 시 추출하지 않음 | X | String `preferredcodec` | 추출할 오디오 코덱. 가능한 값은 `"best"`, `"mp3"`, `"aac"`, `"flac"`, `"m4a"`, `"opus"`, `"vorbis"`, `"wav"`. 미지정 시 추출하지 않음 | X | String
`preferredquality` | 추출한 오디오의 비트레이트. 0 ~ 9 사이의 VBR 퀄리티 값(0에 가까울수록 좋음) 혹은 특정 비트레이트 값. `preferredcodec` 키가 있을 때만 유효. 기본값: `192` | X | Integer `preferredquality` | 추출한 오디오의 비트레이트. 0 ~ 9 사이의 VBR 퀄리티 값(0에 가까울수록 좋음) 혹은 특정 비트레이트 값. `preferredcodec` 키가 있을 때만 유효. 기본값: `192` | X | Integer
`dateafter` | 지정한 날짜 이후에 업로드된 동영상만 다운로드. 미지정 시 모든 동영상 다운로드 | X | String `dateafter` | 지정한 날짜 이후에 업로드된 동영상만 다운로드. 미지정 시 모 다운로드 | X | String
`playlist` | 플레이리스트를 다운로드할 때 범위나 순서 지정. 미지정 시 모두 순서대로 다운로드 | X | String
`archive` | 다운로드한 동영상의 ID를 기록할 파일 경로. 파일이 이미 있으면 이미 다운로드한 동영상은 다운로드 하지 않음. 미지정 시 기록하지 않음 | X | String `archive` | 다운로드한 동영상의 ID를 기록할 파일 경로. 파일이 이미 있으면 이미 다운로드한 동영상은 다운로드 하지 않음. 미지정 시 기록하지 않음 | X | String
`start` | 다운로드 준비 후 바로 다운로드를 시작할지 여부. 기본값: `false` | X | Boolean `start` | 다운로드 준비 후 바로 다운로드를 시작할지 여부. 기본값: `false` | X | Boolean
`cookiefile` | 다운로드 시 필요한 쿠키 파일 경로 | X | String `cookiefile` | 다운로드 시 필요한 쿠키 파일 경로 | X | String
`dateafter` 키에 넣을 수 있는 날짜는 `YYYYMMDD` 또는 `(now|today)[+-][0-9](day|week|month|year)(s)?` 형식의 문자열입니다. `dateafter` 키에 넣을 수 있는 날짜는 `"YYYYMMDD"` 또는 `"(now|today)[+-][0-9](day|week|month|year)(s)?"` 형식의 문자열입니다.
`playlist` 키에 넣을 수 있는 값은 `"1-3,7,10-13"` 형식의 범위 또는 `"reverse"`, `"random"`입니다.
범위를 넣으면 플레이리스트에서 선택한 것만 다운로드합니다.
`"reverse"`를 넣으면 전체 플레이리스트를 역순으로 다운로드합니다.
`"random"`을 넣으면 전체 플레이리스트를 랜덤 순서로 다운로드합니다.
#### Response
키 | 설명 | 타입
--- | --- | ---
`errorCode` | 에러 코드 | Integer
`index` | 동영상 인덱스. 이후 다운로드를 제어할 때 이 값이 필요함 | Integer
### /youtube-dl/api/thumbnail
썸네일 다운로드 준비를 요청하는 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_thumbnails` | 모든 썸네일 다운로드 여부. `false`는 대표 썸네일 하나만 다운로드. 기본값: `false` | X | Boolean
`dateafter` | 지정한 날짜 이후에 업로드된 동영상만 다운로드. 미지정 시 모두 다운로드 | X | String
`playlist` | 플레이리스트를 다운로드할 때 범위나 순서 지정. 미지정 시 모두 순서대로 다운로드 | X | String
`archive` | 다운로드한 동영상의 ID를 기록할 파일 경로. 파일이 이미 있으면 이미 다운로드한 동영상은 다운로드 하지 않음. 미지정 시 기록하지 않음 | X | String
`start` | 다운로드 준비 후 바로 다운로드를 시작할지 여부. 기본값: `false` | X | Boolean
`cookiefile` | 다운로드 시 필요한 쿠키 파일 경로 | X | String
`dateafter` 키에 넣을 수 있는 날짜는 `"YYYYMMDD"` 또는 `"(now|today)[+-][0-9](day|week|month|year)(s)?"` 형식의 문자열입니다.
`playlist` 키에 넣을 수 있는 값은 `"1-3,7,10-13"` 형식의 범위 또는 `"reverse"`, `"random"`입니다.
범위를 넣으면 플레이리스트에서 선택한 것만 다운로드합니다.
`"reverse"`를 넣으면 전체 플레이리스트를 역순으로 다운로드합니다.
`"random"`을 넣으면 전체 플레이리스트를 랜덤 순서로 다운로드합니다.
#### Response
키 | 설명 | 타입
--- | --- | ---
`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
`playlist` | 플레이리스트를 다운로드할 때 범위나 순서 지정. 미지정 시 모두 순서대로 다운로드 | X | String
`archive` | 다운로드한 동영상의 ID를 기록할 파일 경로. 파일이 이미 있으면 이미 다운로드한 동영상은 다운로드 하지 않음. 미지정 시 기록하지 않음 | X | String
`start` | 다운로드 준비 후 바로 다운로드를 시작할지 여부. 기본값: `false` | X | Boolean
`cookiefile` | 다운로드 시 필요한 쿠키 파일 경로 | X | String
`dateafter` 키에 넣을 수 있는 날짜는 `"YYYYMMDD"` 또는 `"(now|today)[+-][0-9](day|week|month|year)(s)?"` 형식의 문자열입니다.
`playlist` 키에 넣을 수 있는 값은 `"1-3,7,10-13"` 형식의 범위 또는 `"reverse"`, `"random"`입니다.
범위를 넣으면 플레이리스트에서 선택한 것만 다운로드합니다.
`"reverse"`를 넣으면 전체 플레이리스트를 역순으로 다운로드합니다.
`"random"`을 넣으면 전체 플레이리스트를 랜덤 순서로 다운로드합니다.
#### Response #### Response
키 | 설명 | 타입 키 | 설명 | 타입
--- | --- | --- --- | --- | ---
@@ -124,15 +189,25 @@ API를 제공합니다. 다른 플러그인에서 동영상 정보나 다운로
--- | --- | --- --- | --- | ---
`errorCode` | 에러 코드 | Integer `errorCode` | 에러 코드 | Integer
`status` | 요청을 받았을 당시의 상태 | Status `status` | 요청을 받았을 당시의 상태 | Status
`type` | 다운로드 타입. `"video" "thumbnail" "subtitle"` | String
`start_time` | 다운로드 시작 시간 | String `start_time` | 다운로드 시작 시간 | String
`end_time` | 다운로드 종료 시간 | String `end_time` | 다운로드 종료 시간 | String
`temp_path` | 임시 폴더 경로 | String `temp_path` | 임시 폴더 경로 | String
`save_path` | 저장 폴더 경로 | String `save_path` | 저장 폴더 경로 | String
`start_time` 키와 `end_time` 키에 들어있는 시간은 "YYYY-MM-DDThh:mm:ss" 형식의 문자열입니다. `start_time` 키와 `end_time` 키에 들어있는 시간은 `"YYYY-MM-DDThh:mm:ss"` 형식의 문자열입니다.
물론 해당 정보가 없으면 null입니다. 물론 해당 정보가 없으면 null입니다.
## Changelog ## Changelog
v2.4.0
* API에 playlist 키 추가
플레이리스트 다운로드 시 범위나 순서를 지정할 수 있습니다.
* 썸네일 다운로드 기능 추가
* thumbnail API 추가
* 자막 다운로드 기능 추가
* sub API 추가
* 설정에 경로 선택 버튼 추가
v2.3.1 v2.3.1
* yt-dlp 패키지명 변경 대응 * yt-dlp 패키지명 변경 대응

View File

@@ -1 +1 @@
{"description": "\uc720\ud29c\ube0c, \ub124\uc774\ubc84TV \ub4f1 \ub3d9\uc601\uc0c1 \uc0ac\uc774\ud2b8\uc5d0\uc11c \ub3d9\uc601\uc0c1 \ub2e4\uc6b4\ub85c\ub4dc", "name": "youtube-dl", "more": "", "version": "2.3.1", "home": "https://github.com/joyfuI/youtube-dl", "category_name": "vod", "developer": "joyfuI"} {"description": "\uc720\ud29c\ube0c, \ub124\uc774\ubc84TV \ub4f1 \ub3d9\uc601\uc0c1 \uc0ac\uc774\ud2b8\uc5d0\uc11c \ub3d9\uc601\uc0c1 \ub2e4\uc6b4\ub85c\ub4dc", "name": "youtube-dl", "more": "", "version": "2.4.0", "home": "https://github.com/joyfuI/youtube-dl", "category_name": "vod", "developer": "joyfuI"}

View File

@@ -109,6 +109,13 @@ class LogicNormal(object):
}) })
if postprocessor: if postprocessor:
opts['postprocessors'] = postprocessor opts['postprocessors'] = postprocessor
if 'playlist' in kwagrs and kwagrs['playlist']:
if kwagrs['playlist'] == 'reverse':
opts['playlistreverse'] = True
elif kwagrs['playlist'] == 'random':
opts['playlistrandom'] = True
else:
opts['playlist_items'] = kwagrs['playlist']
if 'archive' in kwagrs and kwagrs['archive']: if 'archive' in kwagrs and kwagrs['archive']:
opts['download_archive'] = kwagrs['archive'] opts['download_archive'] = kwagrs['archive']
if 'proxy' in kwagrs and kwagrs['proxy']: if 'proxy' in kwagrs and kwagrs['proxy']:
@@ -118,7 +125,94 @@ class LogicNormal(object):
if 'cookiefile' in kwagrs and kwagrs['cookiefile']: if 'cookiefile' in kwagrs and kwagrs['cookiefile']:
opts['cookiefile'] = kwagrs['cookiefile'] opts['cookiefile'] = kwagrs['cookiefile']
dateafter = kwagrs.get('dateafter') dateafter = kwagrs.get('dateafter')
youtube_dl = MyYoutubeDL(plugin, url, filename, temp_path, save_path, opts, dateafter) youtube_dl = MyYoutubeDL(plugin, 'video', 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 thumbnail(**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
}
if 'all_thumbnails' in kwagrs and str(kwagrs['all_thumbnails']).lower() != 'false':
opts['write_all_thumbnails'] = True
else:
opts['writethumbnail'] = True
if 'playlist' in kwagrs and kwagrs['playlist']:
if kwagrs['playlist'] == 'reverse':
opts['playlistreverse'] = True
elif kwagrs['playlist'] == 'random':
opts['playlistrandom'] = True
else:
opts['playlist_items'] = kwagrs['playlist']
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, 'thumbnail', 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 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 'playlist' in kwagrs and kwagrs['playlist']:
if kwagrs['playlist'] == 'reverse':
opts['playlistreverse'] = True
elif kwagrs['playlist'] == 'random':
opts['playlistrandom'] = True
else:
opts['playlist_items'] = kwagrs['playlist']
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') youtube_dl.key = kwagrs.get('key')
LogicNormal.youtube_dl_list.append(youtube_dl) # 리스트 추가 LogicNormal.youtube_dl_list.append(youtube_dl) # 리스트 추가
return youtube_dl return youtube_dl
@@ -140,8 +234,8 @@ class LogicNormal(object):
data['status_str'] = youtube_dl.status.name data['status_str'] = youtube_dl.status.name
data['status_ko'] = str(youtube_dl.status) data['status_ko'] = str(youtube_dl.status)
data['end_time'] = '' data['end_time'] = ''
data['extractor'] = youtube_dl.info_dict['extractor'] if \ data['extractor'] = youtube_dl.type + (
youtube_dl.info_dict['extractor'] is not None else '' ' - ' + youtube_dl.info_dict['extractor'] if youtube_dl.info_dict['extractor'] is not None else '')
data['title'] = youtube_dl.info_dict['title'] if \ data['title'] = youtube_dl.info_dict['title'] if \
youtube_dl.info_dict['title'] is not None else youtube_dl.url youtube_dl.info_dict['title'] is not None else youtube_dl.url
data['uploader'] = youtube_dl.info_dict['uploader'] if youtube_dl.info_dict['uploader'] is not None else '' data['uploader'] = youtube_dl.info_dict['uploader'] if youtube_dl.info_dict['uploader'] is not None else ''

View File

@@ -43,7 +43,8 @@ class MyYoutubeDL(object):
__index = 0 __index = 0
_last_msg = '' _last_msg = ''
def __init__(self, plugin, url, filename, temp_path, save_path=None, opts=None, dateafter=None, datebefore=None): def __init__(self, plugin, type_name, url, filename, temp_path, save_path=None, opts=None, dateafter=None,
datebefore=None):
# from youtube_dl.utils import DateRange # from youtube_dl.utils import DateRange
from .plugin import YOUTUBE_DL_PACKAGE from .plugin import YOUTUBE_DL_PACKAGE
DateRange = __import__('%s.utils' % YOUTUBE_DL_PACKAGE, fromlist=['DateRange']).DateRange DateRange = __import__('%s.utils' % YOUTUBE_DL_PACKAGE, fromlist=['DateRange']).DateRange
@@ -53,6 +54,7 @@ class MyYoutubeDL(object):
if opts is None: if opts is None:
opts = {} opts = {}
self.plugin = plugin self.plugin = plugin
self.type = type_name
self.url = url self.url = url
self.filename = filename self.filename = filename
if not os.path.isdir(temp_path): if not os.path.isdir(temp_path):
@@ -166,6 +168,7 @@ class MyYoutubeDL(object):
# import youtube_dl # import youtube_dl
from .plugin import YOUTUBE_DL_PACKAGE from .plugin import YOUTUBE_DL_PACKAGE
youtube_dl = __import__('%s' % YOUTUBE_DL_PACKAGE) youtube_dl = __import__('%s' % YOUTUBE_DL_PACKAGE)
try: try:
ydl_opts = { ydl_opts = {
'simulate': True, 'simulate': True,

139
plugin.py
View File

@@ -41,13 +41,14 @@ if ModelSetting.get_bool('activate_cors'):
menu = { menu = {
'main': [package_name, 'youtube-dl'], 'main': [package_name, 'youtube-dl'],
'sub': [ 'sub': [
['setting', '설정'], ['download', '다운로드'], ['list', '목록'], ['log', '로그'] ['setting', '설정'], ['download', '다운로드'], ['thumbnail', '썸네일 다운로드'], ['sub', '자막 다운로드'], ['list', '목록'],
['log', '로그']
], ],
'category': 'vod' 'category': 'vod'
} }
plugin_info = { plugin_info = {
'version': '2.3.1', 'version': '2.4.0',
'name': 'youtube-dl', 'name': 'youtube-dl',
'category_name': 'vod', 'category_name': 'vod',
'developer': 'joyfuI', 'developer': 'joyfuI',
@@ -98,6 +99,16 @@ def first_menu(sub):
arg['postprocessor_list'] = LogicNormal.get_postprocessor_list() arg['postprocessor_list'] = LogicNormal.get_postprocessor_list()
return render_template('%s_%s.html' % (package_name, sub), arg=arg) return render_template('%s_%s.html' % (package_name, sub), arg=arg)
elif sub == 'thumbnail':
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 == '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': elif sub == 'list':
return render_template('%s_%s.html' % (package_name, sub), arg=arg) return render_template('%s_%s.html' % (package_name, sub), arg=arg)
@@ -157,6 +168,34 @@ def ajax(sub):
socketio_emit('add', youtube_dl) socketio_emit('add', youtube_dl)
return jsonify([]) return jsonify([])
elif sub == 'thumbnail':
youtube_dl = LogicNormal.thumbnail(plugin=package_name,
url=request.form['url'],
filename=request.form['filename'],
temp_path=ModelSetting.get('temp_path'),
save_path=ModelSetting.get('save_path'),
all_thumbnails=request.form['all_thumbnails'],
proxy=ModelSetting.get('proxy'),
ffmpeg_path=ModelSetting.get('ffmpeg_path'))
youtube_dl.start()
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': elif sub == 'list':
ret = [] ret = []
for i in LogicNormal.youtube_dl_list: for i in LogicNormal.youtube_dl_list:
@@ -208,7 +247,7 @@ def api(sub):
ret['info_dict'] = info_dict ret['info_dict'] = info_dict
return jsonify(ret) return jsonify(ret)
# 다운로드 준비를 요청하는 API # 비디오 다운로드 준비를 요청하는 API
elif sub == 'download': elif sub == 'download':
key = request.values.get('key') key = request.values.get('key')
url = request.values.get('url') url = request.values.get('url')
@@ -219,6 +258,7 @@ def api(sub):
preferredcodec = request.values.get('preferredcodec', None) preferredcodec = request.values.get('preferredcodec', None)
preferredquality = request.values.get('preferredquality', 192) preferredquality = request.values.get('preferredquality', 192)
dateafter = request.values.get('dateafter', None) dateafter = request.values.get('dateafter', None)
playlist = request.values.get('playlist', None)
archive = request.values.get('archive', None) archive = request.values.get('archive', None)
start = request.values.get('start', False) start = request.values.get('start', False)
cookiefile = request.values.get('cookiefile', None) cookiefile = request.values.get('cookiefile', None)
@@ -244,6 +284,97 @@ def api(sub):
preferredcodec=preferredcodec, preferredcodec=preferredcodec,
preferredquality=preferredquality, preferredquality=preferredquality,
dateafter=dateafter, dateafter=dateafter,
playlist=playlist,
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 == 'thumbnail':
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_thumbnails = request.values.get('all_thumbnails', False)
dateafter = request.values.get('dateafter', None)
playlist = request.values.get('playlist', 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.thumbnail(plugin=plugin,
url=url,
filename=filename,
temp_path=ModelSetting.get('temp_path'),
save_path=save_path,
all_thumbnails=all_thumbnails,
dateafter=dateafter,
playlist=playlist,
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 == '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)
playlist = request.values.get('playlist', 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,
playlist=playlist,
archive=archive, archive=archive,
proxy=ModelSetting.get('proxy'), proxy=ModelSetting.get('proxy'),
ffmpeg_path=ModelSetting.get('ffmpeg_path'), ffmpeg_path=ModelSetting.get('ffmpeg_path'),
@@ -306,6 +437,7 @@ def api(sub):
ret = { ret = {
'errorCode': 0, 'errorCode': 0,
'status': None, 'status': None,
'type': None,
'start_time': None, 'start_time': None,
'end_time': None, 'end_time': None,
'temp_path': None, 'temp_path': None,
@@ -320,6 +452,7 @@ def api(sub):
if youtube_dl.key != key: if youtube_dl.key != key:
return LogicNormal.abort(ret, 4) # 키가 일치하지 않음 return LogicNormal.abort(ret, 4) # 키가 일치하지 않음
ret['status'] = youtube_dl.status.name ret['status'] = youtube_dl.status.name
ret['type'] = youtube_dl.type
ret['start_time'] = youtube_dl.start_time.strftime('%Y-%m-%dT%H:%M:%S') if \ ret['start_time'] = youtube_dl.start_time.strftime('%Y-%m-%dT%H:%M:%S') if \
youtube_dl.start_time is not None else None youtube_dl.start_time is not None else None
ret['end_time'] = youtube_dl.end_time.strftime('%Y-%m-%dT%H:%M:%S') if \ ret['end_time'] = youtube_dl.end_time.strftime('%Y-%m-%dT%H:%M:%S') if \

View File

@@ -1,7 +1,12 @@
"use strict"; "use strict";
const ffmpeg_version_btn = document.getElementById('ffmpeg_version_btn');
const ffmpeg_path = document.getElementById('ffmpeg_path'); const ffmpeg_path = document.getElementById('ffmpeg_path');
const ffmpeg_version_btn = document.getElementById('ffmpeg_version_btn');
const ffmpeg_path_btn = document.getElementById('ffmpeg_path_btn');
const temp_path = document.getElementById('temp_path');
const temp_path_btn = document.getElementById('temp_path_btn');
const save_path = document.getElementById('save_path');
const save_path_btn = document.getElementById('save_path_btn');
const modal_title = document.getElementById('modal_title'); const modal_title = document.getElementById('modal_title');
const modal_body = document.getElementById('modal_body'); const modal_body = document.getElementById('modal_body');
@@ -30,3 +35,27 @@ ffmpeg_version_btn.addEventListener('click', (event) => {
notify('버전확인 실패', 'danger'); notify('버전확인 실패', 'danger');
}); });
}); });
// FFmpeg 파일 선택
ffmpeg_path_btn.addEventListener('click', (event) => {
event.preventDefault();
m_select_local_file_modal('실행 파일 선택', '/', false, (result) => {
ffmpeg_path.value = result;
});
});
// 임시 폴더 경로 선택
temp_path_btn.addEventListener('click', (event) => {
event.preventDefault();
m_select_local_file_modal("저장 경로 선택", temp_path.value, true, (result) => {
temp_path.value = result;
});
});
// 저장 폴더 경로 선택
save_path_btn.addEventListener('click', (event) => {
event.preventDefault();
m_select_local_file_modal("저장 경로 선택", save_path.value, true, (result) => {
save_path.value = result;
});
});

31
static/youtube-dl_sub.js Normal file
View File

@@ -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');
});
});

View File

@@ -0,0 +1,26 @@
"use strict";
const url = document.getElementById('url');
const download_btn = document.getElementById('download_btn');
// 다운로드
download_btn.addEventListener('click', (event) => {
event.preventDefault();
if (!url.value.startsWith('http')) {
notify('URL을 입력하세요.', 'warning');
return;
}
fetch(`/${package_name}/ajax/thumbnail`, {
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');
});
});

View File

@@ -11,13 +11,13 @@
<table class="table table-sm tableRowHover"> <table class="table table-sm tableRowHover">
<thead> <thead>
<tr> <tr>
<th style="width: 5%">IDX</th> <th style="width: 4%">IDX</th>
<th style="width: 8%">Plugin</th> <th style="width: 8%">Plugin</th>
<th style="width: 10%">시작시간</th> <th style="width: 10%">시작시간</th>
<th style="width: 10%">타입</th> <th style="width: 14%">타입</th>
<th style="width: 28%">제목</th> <th style="width: 29%">제목</th>
<th style="width: 8%">상태</th> <th style="width: 8%">상태</th>
<th style="width: 15%">진행률</th> <th style="width: 11%">진행률</th>
<th style="width: 8%">진행시간</th> <th style="width: 8%">진행시간</th>
<th style="width: 8%">Action</th> <th style="width: 8%">Action</th>
</tr> </tr>

View File

@@ -4,9 +4,9 @@
<form id="setting"> <form id="setting">
{{ macros.setting_radio('youtube_dl_package', 'youtube-dl', arg['package_list'], value=arg['youtube_dl_package'], desc='사용할 youtube-dl 패키지를 선택합니다. 설정 저장 후 재시작이 필요합니다.') }} {{ macros.setting_radio('youtube_dl_package', 'youtube-dl', arg['package_list'], value=arg['youtube_dl_package'], desc='사용할 youtube-dl 패키지를 선택합니다. 설정 저장 후 재시작이 필요합니다.') }}
{{ macros.setting_input_text('youtube_dl_version', 'youtube-dl 버전', value=arg['youtube_dl_version'], disabled=True) }} {{ macros.setting_input_text('youtube_dl_version', 'youtube-dl 버전', value=arg['youtube_dl_version'], disabled=True) }}
{{ macros.setting_input_text_and_buttons('ffmpeg_path', 'FFmpeg 경로', [['ffmpeg_version_btn', '버전확인']], value=arg['ffmpeg_path'], placeholder='ffmpeg', desc='SJVA에 내장된 버전 말고 원하는 버전을 사용할 수 있습니다.') }} {{ macros.setting_input_text_and_buttons('ffmpeg_path', 'FFmpeg 경로', [['ffmpeg_version_btn', '버전확인'], ['ffmpeg_path_btn', '파일 선택']], value=arg['ffmpeg_path'], placeholder='ffmpeg', desc='SJVA에 내장된 버전 말고 원하는 버전을 사용할 수 있습니다.') }}
{{ macros.setting_input_text('temp_path', '임시 폴더', value=arg['temp_path'], desc='다운로드 파일이 임시로 저장될 폴더입니다.') }} {{ macros.setting_input_text_and_buttons('temp_path', '임시 폴더', [['temp_path_btn', '경로 선택']], value=arg['temp_path'], desc='다운로드 파일이 임시로 저장될 폴더입니다.') }}
{{ macros.setting_input_text('save_path', '저장 폴더', value=arg['save_path'], desc='정상적으로 완료된 파일이 이동할 폴더입니다.') }} {{ macros.setting_input_text_and_buttons('save_path', '저장 폴더', [['save_path_btn', '경로 선택']], value=arg['save_path'], desc='정상적으로 완료된 파일이 이동할 폴더입니다.') }}
{{ macros.setting_input_text('default_filename', '기본 파일명', value=arg['default_filename'], placeholder=arg['DEFAULT_FILENAME'], desc='템플릿 규칙은 https://github.com/ytdl-org/youtube-dl/#output-template 참고') }} {{ macros.setting_input_text('default_filename', '기본 파일명', value=arg['default_filename'], placeholder=arg['DEFAULT_FILENAME'], desc='템플릿 규칙은 https://github.com/ytdl-org/youtube-dl/#output-template 참고') }}
{{ macros.setting_input_text('proxy', '프록시', value=arg['proxy'], desc=['HTTP/HTTPS/SOCKS를 지원합니다. 예) socks5://127.0.0.1:1080/', '빈칸으로 두면 프록시를 사용하지 않습니다.']) }} {{ macros.setting_input_text('proxy', '프록시', value=arg['proxy'], desc=['HTTP/HTTPS/SOCKS를 지원합니다. 예) socks5://127.0.0.1:1080/', '빈칸으로 두면 프록시를 사용하지 않습니다.']) }}
{{ macros.setting_checkbox('activate_cors', 'CORS 허용', value=arg['activate_cors'], desc='API로의 크로스 도메인 요청을 허용합니다. 설정 저장 후 재시작이 필요합니다.') }} {{ macros.setting_checkbox('activate_cors', 'CORS 허용', value=arg['activate_cors'], desc='API로의 크로스 도메인 요청을 허용합니다. 설정 저장 후 재시작이 필요합니다.') }}

View File

@@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block content %}
<form id="download">
{{ macros.setting_input_text('url', 'URL', placeholder='http:// 주소', desc='유튜브, 네이버TV 등 동영상 주소') }}
{{ macros.setting_input_text('filename', '파일명', value=arg['filename'], desc='템플릿 규칙은 https://github.com/ytdl-org/youtube-dl/#output-template 참고') }}
{{ macros.setting_checkbox('all_subs', '모든 자막 다운로드', value='False') }}
<div id="all_subs_div" class="collapse show">
{{ macros.setting_input_text('sub_lang', '자막 언어', value='ko', desc=['두 자리 국가 코드', '콤마(,)를 구분자로 여러 개 지정 가능']) }}
</div>
{{ macros.setting_checkbox('auto_sub', '자동생성 자막 다운로드', value='False', desc='유튜브 전용') }}
{{ macros.setting_button([['download_btn', '다운로드']]) }}
</form>
<script>
"use strict";
const package_name = '{{ arg["package_name"] }}';
</script>
<script src="{{ url_for('.static', filename='%s.js' % arg['template_name']) }}"></script>
{% endblock %}

View File

@@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block content %}
<form id="download">
{{ macros.setting_input_text('url', 'URL', placeholder='http:// 주소', desc='유튜브, 네이버TV 등 동영상 주소') }}
{{ macros.setting_input_text('filename', '파일명', value=arg['filename'], desc='템플릿 규칙은 https://github.com/ytdl-org/youtube-dl/#output-template 참고') }}
{{ macros.setting_checkbox('all_thumbnails', '모든 썸네일 다운로드', value='False') }}
{{ macros.setting_button([['download_btn', '다운로드']]) }}
</form>
<script>
"use strict";
const package_name = '{{ arg["package_name"] }}';
</script>
<script src="{{ url_for('.static', filename='%s.js' % arg['template_name']) }}"></script>
{% endblock %}