This commit is contained in:
KurtBestor 2022-07-21 14:14:09 +09:00
parent ff397445a9
commit 7952f9173e
88 changed files with 1112 additions and 742 deletions

View File

@ -35,7 +35,6 @@
| **4chan** | <https://4chan.org> |
| **AfreecaTV** | <https://afreecatv.com> |
| **ArtStation** | <https://artstation.com> |
| **AsianSister** | <https://asiansister.com> |
| **AsmHentai** | <https://asmhentai.com> |
| **Avgle** | <https://avgle.com> |
| **baraag.net** | <https://baraag.net> |
@ -56,13 +55,11 @@
| **hanime.tv** | <https://hanime.tv> |
| **Hentai Foundry** | <https://hentai-foundry.com> |
| **Hitomi.la** | <https://hitomi.la> |
| **Hiyobi.me** | <https://hiyobi.me> |
| **Imgur** | <https://imgur.com> |
| **Instagram** | <https://instagram.com> |
| **Iwara** | <https://iwara.tv><br><https://ecchi.iwara.tv> |
| **Jmana** | <https://jmana.net> |
| **カクヨム** | <https://kakuyomu.jp> |
| **LHScan** | <https://loveheaven.net> |
| **Likee** | <https://likee.video> |
| **Luscious** | <https://luscious.net> |
| **MyReadingManga** | <https://myreadingmanga.info> |
@ -100,4 +97,4 @@
| **Yande.re** | <https://yande.re> |
| **Youku** | <https://youku.com> |
| **YouTube** | <https://youtube.com> |
| **and more...** | [Supported sites by youtube-dl](http://ytdl-org.github.io/youtube-dl/supportedsites.html) |
| **and more...** | [Supported sites by yt-dlp](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md) |

View File

@ -8,15 +8,14 @@ class Image:
self._url = url
self.url = LazyUrl(ref, self.get, self)
self.filename = '{:04}{}'.format(n, get_ext(url))
@sleep_and_retry
@limits(2, 1)
def get(self, _):
return self._url
@Downloader.register
class Downloader_4chan(Downloader):
type = '4chan'
URLS = [r'regex:boards.(4chan|4channel).org']

View File

@ -21,7 +21,7 @@ class Video(object):
downloader.download(url_thumb, buffer=self.thumb)
@Downloader.register
class Downloader_afreeca(Downloader):
type = 'afreeca'
URLS = ['afreecatv.com']
@ -31,14 +31,14 @@ class Downloader_afreeca(Downloader):
@classmethod
def fix_url(cls, url):
return url.rstrip(' /')
def read(self):
session = Session()
video = get_video(self.url, session, self.cw)
self.urls.append(video.url)
self.setIcon(video.thumb)
self.title = video.title

View File

@ -22,7 +22,7 @@ class Image(object):
return 'Image({})'.format(self.filename)
@Downloader.register
class Downloader_artstation(Downloader):
type = 'artstation'
URLS = ['artstation.com']
@ -30,7 +30,7 @@ class Downloader_artstation(Downloader):
def init(self):
self.url_main = 'https://www.artstation.com/{}'.format(self.id.replace('artstation_', '', 1).replace('', '/'))
if '/artwork/' in self.url or '/projects/' in self.url:
pass#raise NotImplementedError('Single post')
else:
@ -67,7 +67,7 @@ class Downloader_artstation(Downloader):
imgs = get_imgs_page(id_art, self.session, cw=self.cw)
else:
imgs = get_imgs(id, self.title, self.session, type=type, cw=self.cw)
for img in imgs:
self.urls.append(img.url)
@ -135,7 +135,7 @@ def get_id_art(post_url):
def get_id(url, cw=None):
print_ = get_print(cw)
print_ = get_print(cw)
url = url.split('?')[0].split('#')[0]
@ -143,7 +143,7 @@ def get_id(url, cw=None):
id_art = get_id_art(url)
imgs = get_imgs_page(id_art, session=Session(), cw=cw)
return imgs[0].data['user']['username']
if '.artstation.' in url and 'www.artstation.' not in url:
id = url.split('.artstation')[0].split('//')[-1]
type = None
@ -214,10 +214,8 @@ def get_imgs_page(id_art, session, date=None, cw=None):
img = video
else:
img = Image(post_url, date, url, page)
img.data = data#
imgs.append(img)
return imgs

View File

@ -1,67 +0,0 @@
import downloader
from utils import Soup, urljoin, LazyUrl, Downloader, try_n, clean_title
from timee import sleep
import os
import ree as re
@Downloader.register
class Downloader_asiansister(Downloader):
type = 'asiansister'
URLS = ['asiansister.com']
display_name = 'AsianSister'
@try_n(4)
def init(self):
html = downloader.read_html(self.url)
self.soup = Soup(html)
@property
def name(self):
return clean_title(self.soup.find('title').text.replace('- ASIANSISTER.COM', '').strip())
def read(self):
imgs = get_imgs(self.url, self.soup, self.name)
for img in imgs:
if img.type == 'video':
self.single = True
self.urls.append(img.url)
self.title = self.name
class Image(object):
def __init__(self, url, referer, p, type='image'):
self.url = LazyUrl(referer, lambda x: url, self)
ext = os.path.splitext(url.split('?')[0])[1]
self.filename = u'{:04}{}'.format(p, ext)
self.type = type
@try_n(4)
def get_imgs(url, soup=None, name=None):
if soup is None:
html = downloader.read_html(url)
soup = Soup(html)
view = soup.findAll('div', class_='rootContant')[:2][-1]
v = view.find('video')
if v:
img = v.find('source').attrs['src']
img = urljoin(url, img)
img = Image(img, url, 0, 'video')
ext = os.path.splitext(img.url().split('?')[0])[1]
img.filename = u'{}{}'.format(name, ext)
return [img]
imgs = []
for img in view.findAll('img'):
img = img.attrs['dataurl']
img = urljoin(url, img)
img = re.sub('/[a-z]+images/', '/images/', img).replace('_t.', '.')
img = Image(img, url, len(imgs))
imgs.append(img)
return imgs

View File

@ -1,8 +1,10 @@
#coding: utf8
import downloader
import ree as re
from utils import Soup, urljoin, Downloader, join
from utils import Soup, urljoin, Downloader, join, LazyUrl, Session, get_print
import os
from timee import sleep
from translator import tr_
@ -16,15 +18,12 @@ def get_id(url):
return int(re.find('/g/([0-9]+)', url))
@Downloader.register
class Downloader_asmhentai(Downloader):
type = 'asmhentai'
URLS = ['asmhentai.com']
MAX_CORE = 8
display_name = 'AsmHentai'
def init(self):
pass
@classmethod
def fix_url(cls, url):
@ -32,7 +31,8 @@ class Downloader_asmhentai(Downloader):
return 'https://asmhentai.com/g/{}/'.format(id_)
def read(self):
info, imgs = get_imgs(self.url)
self.session = Session()
info = get_info(self.url, self.session, self.cw)
# 1225
artist = join(info['artists'])
@ -42,51 +42,39 @@ class Downloader_asmhentai(Downloader):
series = info['parodies'][0] if info['parodies'] else u'NA'
title = self.format_title(info['category'][0], info['id'], info['title'], artist, group, series, lang)
self.urls += imgs
self.urls += [img.url for img in info['imgs']]
self.title = title
class Image:
def __init__(self, url, referer):
self.url = LazyUrl(referer, lambda _:url, self)
self.filename = os.path.basename(url)
def get_imgs(url):
html = downloader.read_html(url)
def get_info(url, session, cw):
print_ = get_print(cw)
html = downloader.read_html(url, session=session)
soup = Soup(html)
info = get_info(url, soup)
view = soup.find('div', class_='gallery')
imgs = []
for img in view.findAll('div', class_='preview_thumb'):
img = img.find('img').attrs.get('data-src') or img.find('img').attrs.get('src')
img = urljoin(url, img).replace('t.jpg', '.jpg')
imgs.append(img)
return info, imgs
def get_info(url, soup=None):
if soup is None:
html = downloader.read_html(url)
soup = Soup(html)
info = {}
info['id'] = get_id(url)
title = soup.find('h1').text.strip()
info['title'] = title
for tag in soup.findAll('span', class_='tag'):
href = tag.parent.attrs['href']
href = urljoin(url, href).strip('/')
key = href.split('/')[3]
value = href.split('/')[-1]
if key == 'language' and value == 'translated':
continue
if key in info:
info[key].append(value)
else:
@ -95,6 +83,40 @@ def get_info(url, soup=None):
for key in ['artists', 'groups', 'parodies', 'tags', 'characters']:
if key not in info:
info[key] = []
info['imgs'] = []
def read_imgs(soup):
c = 0
for img in soup.findAll('div', class_='preview_thumb'):
img = img.find('img').attrs.get('data-src') or img.find('img').attrs.get('src')
img = urljoin(url, img).replace('t.jpg', '.jpg')
img = Image(img, url)
info['imgs'].append(img)
c += 1
if not c:
raise Exception('no imgs')
read_imgs(soup)
csrf = soup.find('meta', {'name':'csrf-token'})['content']
print_(f'csrf: {csrf}')
t_pages = int(soup.find('input', type='hidden', id='t_pages')['value'])
print_(f't_pages: {t_pages}')
while len(info['imgs']) < t_pages: #4971
print_('imgs: {}'.format(len(info['imgs'])))
sleep(1, cw)
cw.setTitle('{} {} - {} / {}'.format(tr_('읽는 중...'), info['title'], len(info['imgs']), t_pages))
data = {
'_token': csrf,
'id': str(info['id']),
'dir': soup.find('input', type='hidden', id='dir')['value'],
'v_pages': len(info['imgs']),
't_pages': str(t_pages),
'type': '1',
}
r = session.post('https://asmhentai.com/load_thumbs', data=data)
soup_more = Soup(r.text)
read_imgs(soup_more)
return info

View File

@ -12,7 +12,7 @@ import webbrowser
import errors
@Downloader.register
class Downloader_avgle(Downloader):
type = 'avgle'
single = True
@ -29,14 +29,14 @@ class Downloader_avgle(Downloader):
self.urls.append(video.url)
self.setIcon(video.thumb)
self.title = video.title
@try_n(2)
def get_video(url, cw=None):
print_ = get_print(cw)
check_alive(cw)
data = cw.data_
@ -61,7 +61,7 @@ def get_video(url, cw=None):
url_thumb = soup.find('meta', {'property': 'og:image'}).attrs['content']
title = soup.find('meta', {'property': 'og:title'}).attrs['content'].strip()
video = Video(stream, url_thumb, url, title)
return video
@ -76,5 +76,3 @@ class Video(object):
self.title = title
ext = '.mp4'
self.filename = u'{}{}'.format(clean_title(title, n=-len(ext)), ext)

View File

@ -11,12 +11,12 @@ def get_id(url):
return re.find('baraag.net/([^/]+)', url.lower())
@Downloader.register
class Downloader_baraag(Downloader):
type = 'baraag'
URLS = ['baraag.net']
display_name = 'baraag.net'
def init(self):
self.referer = self.url
@ -49,6 +49,3 @@ class Downloader_baraag(Downloader):
self.filenames[img.url] = img.filename
self.title = self.name

View File

@ -8,13 +8,13 @@ import os
from translator import tr_
@Downloader.register
class Downloader_bcy(Downloader):
type = 'bcy'
URLS = ['bcy.net/item/detail/', 'bcy.net/u/']
MAX_CORE = 8
display_name = '半次元'
def init(self):
self.html = downloader.read_html(self.url)
self.info = get_info(self.url, self.html)
@ -49,7 +49,7 @@ def get_ssr_data(html):
def get_imgs(url, html=None, cw=None):
if '/detail/' not in url:
return get_imgs_channel(url, html, cw)
if html is None:
html = downloader.read_html(url)
@ -109,7 +109,7 @@ def get_info(url, html):
info['artist'] = uname.text.strip()
j = get_ssr_data(html)
if '/detail/' in url:
info['uid'] = j['detail']['detail_user']['uid']
info['id'] = j['detail']['post_data']['item_id']
@ -117,17 +117,17 @@ def get_info(url, html):
info['uid'] = j['homeInfo']['uid']
return info
def get_imgs_channel(url, html=None, cw=None):
print_ = get_print(cw)
if html is None:
html = downloader.read_html(url)
info = get_info(url, html)
# Range
max_pid = get_max_range(cw)
ids = set()
imgs = []
for p in range(1000):
@ -170,4 +170,3 @@ def get_imgs_channel(url, html=None, cw=None):
print('over max_pid:', max_pid)
break
return imgs[:max_pid]

View File

@ -11,7 +11,7 @@ import clf2
import errors
@Downloader.register
class Downloader_bdsmlr(Downloader):
type = 'bdsmlr'
URLS = ['bdsmlr.com']
@ -20,7 +20,7 @@ class Downloader_bdsmlr(Downloader):
def init(self):
if u'bdsmlr.com/post/' in self.url:
raise errors.Invalid(tr_(u'개별 다운로드는 지원하지 않습니다: {}').format(self.url))
self.url = 'https://{}.bdsmlr.com'.format(self.id_)
self.session = Session()
clf2.solve(self.url, session=self.session, cw=self.cw)
@ -38,13 +38,13 @@ class Downloader_bdsmlr(Downloader):
def read(self):
info = get_imgs(self.id_, session=self.session, cw=self.cw)
for post in info['posts']:
self.urls.append(post.url)
self.title = u'{} (bdsmlr_{})'.format(clean_title(info['username']), self.id_)
class Post(object):
def __init__(self, url, referer, id, p):
self.id = id
@ -71,7 +71,7 @@ def foo(url, soup, info, reblog=False):
post = Post(mag.attrs['href'], url, id, p)
info['posts'].append(post)
info['c'] += 20 if info['c'] else 5
@try_n(2)
def get_imgs(user_id, session, cw=None):
@ -89,7 +89,7 @@ def get_imgs(user_id, session, cw=None):
username = soup.find('title').text.strip()###
print('username:', username)
info['username'] = username
token = soup.find('meta', {'name': 'csrf-token'}).attrs['content']
print_(u'token: {}'.format(token))
@ -136,9 +136,8 @@ def get_imgs(user_id, session, cw=None):
cw.setTitle(s)
else:
print(s)
if len(info['posts']) > max_pid:
break
return info

View File

@ -29,7 +29,7 @@ RESOLS[80] = '1080p'
RESOLS[64] = '720p'
RESOLS[32] = '480p'
RESOLS[16] = '360p'
class Video(object):
@ -61,7 +61,7 @@ def fix_url(url, cw=None):
return url_new
@Downloader.register
class Downloader_bili(Downloader):
type = 'bili'
URLS = [r'regex:'+_VALID_URL]
@ -158,7 +158,7 @@ def get_resolution_(quality):
def get_videos(url, cw=None, depth=0):
print_ = get_print(cw)
res = get_resolution()
mobj = re.match(_VALID_URL, url)
video_id = mobj.group('id')
anime_id = mobj.group('anime_id')
@ -180,9 +180,9 @@ def get_videos(url, cw=None, depth=0):
print_('cid: {}'.format(cid))
headers = {'Referer': url}
entries = []
RENDITIONS = ['qn={}&quality={}&type='.format(qlt, qlt) for qlt in RESOLS.keys()]# + ['quality=2&type=mp4']
for num, rendition in enumerate(RENDITIONS, start=1):
print('####', num, rendition)
payload = 'appkey=%s&cid=%s&otype=json&%s' % (_APP_KEY, cid, rendition)
@ -209,7 +209,7 @@ def get_videos(url, cw=None, depth=0):
if int(re.find('([0-9]+)p', resolution)) > res:
print_('skip resolution')
continue
for idx, durl in enumerate(video_info['durl']):
# 1343
if idx == 0:
@ -217,19 +217,19 @@ def get_videos(url, cw=None, depth=0):
if size < 1024 * 1024 and depth == 0:
print_('size is too small')
return get_videos(url, cw, depth+1)
formats = [
{'url': durl['url'],
{'url': durl['url'],
'filesize': int_or_none(durl['size'])}]
for backup_url in durl.get('backup_url', []):
formats.append({'url': backup_url,
formats.append({'url': backup_url,
'preference': -2 if 'hd.mp4' in backup_url else -3})
for a_format in formats:
a_format.setdefault('http_headers', {}).update({'Referer': url})
entries.append({'id': '%s_part%s' % (video_id, idx),
'duration': float_or_none(durl.get('length'), 1000),
entries.append({'id': '%s_part%s' % (video_id, idx),
'duration': float_or_none(durl.get('length'), 1000),
'formats': formats})
break
@ -240,7 +240,7 @@ def get_videos(url, cw=None, depth=0):
video = Video(url_video, url, cid, len(videos))
videos.append(video)
info = {'title': clean_title(title),
info = {'title': clean_title(title),
'url_thumb': url_thumb}
return (
videos, info)
@ -252,4 +252,3 @@ def get_pages(html):
data = json.loads(data_raw)
pages = data['videoData']['pages']
return pages

View File

@ -15,8 +15,8 @@ import os
def decode(s, hash):
# generateKey
key = int(hash[:16], 16)
filter = [int((key>>i*8)%256) for i in range(8)][::-1] #
filter = [int((key>>i*8)%256) for i in range(8)][::-1] #
s2 = bytes(x^y for x, y in zip(s, cycle(filter)))
return s2
@ -42,7 +42,7 @@ class Page(object):
self.title = clean_title(title)
@Downloader.register
class Downloader_comicwalker(Downloader):
type = 'comicwalker'
URLS = ['comic-walker.com/contents/detail/', 'comic-walker.jp/contents/detail/']
@ -147,13 +147,12 @@ def get_imgs(url, soup=None, cw=None):
if imgs_already:
imgs += imgs_already
continue
if cw is not None:
if not cw.alive:
return
cw.setTitle(u'{} {} / {} ({} / {})'.format(tr_(u'읽는 중...'), title, page.title, i+1, len(pages)))
imgs += get_imgs_page(page)
return imgs

View File

@ -11,7 +11,7 @@ def get_id(url):
return re.find(r'/view/([0-9a-z]+)', url, err='no id')
@Downloader.register
class Downloader_coub(Downloader):
type = 'coub'
URLS = ['coub.com', r'regex:'+PATTEN_IMAGIZER]
@ -40,7 +40,7 @@ class Downloader_coub(Downloader):
class Video(object):
_url = None
def __init__(self, url, cw=None):
self.url = LazyUrl(url, self.get, self, pp=self.pp)
self.cw = cw
@ -49,7 +49,7 @@ class Video(object):
def get(self, url):
if self._url:
return self._url
ydl = ytdl.YoutubeDL(cw=self.cw)
info = ydl.extract_info(url)
fs = [f for f in info['formats'] if f['ext'] == 'mp4']
@ -57,7 +57,7 @@ class Video(object):
self._url = f['url']
## fs = [f for f in info['formats'] if f['ext'] == 'mp3']
## self.f_audio = sorted(fs, key=lambda f: int(f.get('filesize', 0)))[-1]
self.thumb_url = info['thumbnails'][0]['url']
self.thumb = IO()
downloader.download(self.thumb_url, buffer=self.thumb)

View File

@ -9,7 +9,6 @@ from ratelimit import limits, sleep_and_retry
@Downloader.register
class Downloader_danbooru(Downloader):
type='danbooru'
URLS = ['danbooru.donmai.us']
@ -57,9 +56,9 @@ class Downloader_danbooru(Downloader):
for img in imgs:
self.urls.append(img.url)
self.title = self.name
class Image(object):
def __init__(self, id, url, cw):
@ -77,10 +76,10 @@ class Image(object):
if get_ext(img) == '.zip': #4635
img = soup.find('section', id='content').find('video')['src']
img = urljoin(url, img)
ext = get_ext(img)
self.filename = '{}{}'.format(self.id, ext)
return img
@ -90,8 +89,8 @@ class Image(object):
@limits(2, 1)
def wait(cw):
check_alive(cw)
def setPage(url, page):
# Always use HTTPS
url = url.replace('http://', 'https://')
@ -105,7 +104,7 @@ def setPage(url, page):
url = re.sub('page=[0-9]*', 'page={}'.format(page), url)
else:
url += '&page={}'.format(page)
return url
@ -126,7 +125,7 @@ def get_imgs(url, title=None, range_=None, cw=None):
# Range
max_pid = get_max_range(cw)
if range_ is None:
range_ = range(1, 1001)
print(range_)
@ -156,13 +155,13 @@ def get_imgs(url, title=None, range_=None, cw=None):
if empty_count_global >= 6:
break
for article in articles:
id = article.attrs['data-id']
#url_img = article.attrs['data-file-url'].strip()
url_img = urljoin(url, article.find('a', class_='post-preview-link')['href']) #4160
#print(url_img)
if url_img not in url_imgs:
url_imgs.add(url_img)
@ -171,9 +170,9 @@ def get_imgs(url, title=None, range_=None, cw=None):
if len(imgs) >= max_pid:
break
if cw is not None:
cw.setTitle('{} {} - {}'.format(tr_('읽는 중...'), title, len(imgs)))
i += 1
return imgs[:max_pid]

View File

@ -30,7 +30,7 @@ import requests
import errors
@Downloader.register
class DownloaderDiscordEmoji(Downloader):
type = "discord"

View File

@ -8,7 +8,7 @@ import utils
import ffmpeg
@Downloader.register
class Downloader_etc(Downloader):
type = 'etc'
URLS = []
@ -51,7 +51,7 @@ def int_or_none(s):
def format_(f):
if f is None:
return 'None'
return '{} - {} - {} - {}'.format(f['format'], f['_resolution'], f['_audio'], f['url'])
return 'format:{} - resolution:{} - vbr:{} - audio:{} - url:{}'.format(f['format'], f['_resolution'], f['_vbr'], f['_audio'], f['url'])
def get_video(url, session, cw, ie_key=None):
@ -105,7 +105,8 @@ def _get_video(url, session, cw, ie_key=None, allow_m3u8=True):
fs = []
for i, f in enumerate(formats):
f['_index'] = i
f['_resolution'] = f.get('vbr') or int_or_none(re.find('([0-9]+)p', f['format'], re.IGNORECASE)) or f.get('height') or f.get('width') or int(f.get('vcodec', 'none') != 'none')
f['_resolution'] = int_or_none(re.find('([0-9]+)p', f['format'], re.IGNORECASE)) or f.get('height') or f.get('width') or int(f.get('vcodec', 'none') != 'none')
f['_vbr'] = f.get('vbr') or 0
f['_audio'] = f.get('abr') or f.get('asr') or int(f.get('acodec', 'none') != 'none')
print_(format_(f))
fs.append(f)
@ -128,13 +129,13 @@ def _get_video(url, session, cw, ie_key=None, allow_m3u8=True):
print_('invalid url: {}'.format(f['url']))
return list(fs)[0]#
f_video = filter_f(reversed(sorted(fs, key=lambda f:(f['_resolution'], f['_index']))))
f_video = filter_f(reversed(sorted(fs, key=lambda f:(f['_resolution'], f['_vbr'], f['_index']))))
print_('video0: {}'.format(format_(f_video)))
if f_video['_audio']:
f_audio = None
else:
fs_audio = sorted([f_audio for f_audio in fs if (not f_audio['_resolution'] and f_audio['_audio'])], key=lambda f:(f['_audio'], f['_index']))
fs_audio = sorted([f_audio for f_audio in fs if (not f_audio['_resolution'] and f_audio['_audio'])], key=lambda f:(f['_audio'], f['_vbr'], f['_index']))
if fs_audio:
f_audio = fs_audio[-1]
else:
@ -179,6 +180,7 @@ class Video(object):
self.header = utils.capitalize(get_ie_key(info))
self.session = session
self.referer = referer
self.subs = ytdl.get_subtitles(info)
self.url_thumb = info.get('thumbnail')
self.thumb = BytesIO()
@ -211,4 +213,5 @@ class Video(object):
f = BytesIO()
downloader.download(self.f_audio['url'], buffer=f, referer=self.referer, session=self.session)
ffmpeg.merge(filename, f, cw=self.cw)
utils.pp_subtitle(self, filename, self.cw)
return filename

View File

@ -6,7 +6,7 @@ from io import BytesIO
PATTERN_ID = r'/content/([^/]+)'
@Downloader.register
class Downloader_fc2(Downloader):
type = 'fc2'
single = True
@ -34,7 +34,7 @@ class Downloader_fc2(Downloader):
f = BytesIO()
downloader.download(video.url_thumb, referer=self.url, buffer=f)
self.setIcon(f)
self.title = info['title']

View File

@ -5,7 +5,7 @@ from timee import sleep
from hashlib import md5
@Downloader.register
class Downloader_file(Downloader):
type = 'file'
single = True
@ -35,15 +35,15 @@ class Downloader_file(Downloader):
ext = ''
if not ext:
ext = get_ext(name)
name = os.path.splitext(name)[0]
self.urls.append(self.url)
id_ = md5(self.url.encode('utf8')).hexdigest()[:8]
tail = ' ({}){}'.format(id_, ext)
filename = clean_title(name, n=-len(tail)) + tail
self.filenames[self.url] = filename
self.title = filename

View File

@ -51,12 +51,12 @@ def find_ps(url):
return user, ps
@Downloader.register
class Downloader_flickr(Downloader):
type = 'flickr'
URLS = ['flickr.com']
_name = None
def init(self):
if 'flickr.com' in self.url.lower():
self.url = self.url.replace('http://', 'https://')
@ -94,14 +94,14 @@ def get_imgs(url, title=None, cw=None):
if not flickr_auth.isAuth:
raise Exception('No Auth')
if '/albums/' in url:
user, ps = find_ps(url)
handle = ps
else:
user = flickr_api.Person.findByUrl(url)
handle = user
photos = []
per_page = 500
@ -125,4 +125,3 @@ def get_imgs(url, title=None, cw=None):
imgs.append(img)
return imgs

View File

@ -28,7 +28,7 @@ def get_tags(url):
return id
@Downloader.register
class Downloader_gelbooru(Downloader):
type = 'gelbooru'
URLS = ['gelbooru.com']
@ -79,7 +79,7 @@ class LazyUrl_gelbooru(LazyUrl):
img = Image(data['id'], data['url'])
return img.url
class Image(object):
def __init__(self, id_, url):
self.id_ = id_
@ -134,7 +134,7 @@ def get_imgs(url, title=None, cw=None):
else:
cookies = {'fringeBenefits': 'yup'}
print_('user_id: {}'.format(user_id))
# Range
max_pid = get_max_range(cw)
@ -167,7 +167,7 @@ def get_imgs(url, title=None, cw=None):
if count_no_imgs > 1:
print('break')
break
if len(imgs) >= max_pid:
break
@ -175,5 +175,5 @@ def get_imgs(url, title=None, cw=None):
if not cw.alive:
break
cw.setTitle(u'{} {} - {}'.format(tr_(u'읽는 중...'), title, len(imgs)))
return imgs[:max_pid]

View File

@ -11,7 +11,6 @@ from translator import tr_
@Downloader.register
class Downloader_hameln(Downloader):
type = 'hameln'
URLS = ['syosetu.org']
@ -71,7 +70,7 @@ class Text(object):
f.write(text.encode('utf8'))
f.seek(0)
return f
class Page(object):
def __init__(self, title, url):
@ -114,7 +113,7 @@ def get_pages(url, soup=None):
def read_page(page):
html = read_html(page.url)
soup = Soup(html)
text_top = get_text(soup.find('div', id='maegaki'))
print(text_top.count('\n'))
text_mid = get_text(soup.find('div', id='honbun'))
@ -151,4 +150,3 @@ def get_info(url, soup=None):
sss = get_sss(soup)
info['novel_ex'] = get_text(sss[-2], '')
return info

View File

@ -34,7 +34,7 @@ class Video(object):
return ('Video({})').format(self.id)
@Downloader.register
class Downloader_hanime(Downloader):
type = 'hanime'
URLS = ['hanime.tv/hentai-videos/', 'hanime.tv/videos/']
@ -44,7 +44,7 @@ class Downloader_hanime(Downloader):
def read(self):
video, session = get_video(self.url, cw=self.cw)
self.video = video
self.urls.append(video.url)
self.filenames[video.url] = video.filename
@ -116,10 +116,7 @@ def get_video(url, session=None, cw=None):
stream = sorted(steams_filtered, key=lambda _: _['height'])[-1]
else:
stream = sorted(streams_good, key=lambda _: _['height'])[0]
print_('Final stream:')
print_stream(stream)
return Video(info, stream), session

View File

@ -1,11 +1,12 @@
#coding: utf8
import downloader
from utils import Downloader, Session, Soup, LazyUrl, urljoin, get_ext, clean_title
from utils import Downloader, Session, Soup, LazyUrl, urljoin, get_ext, clean_title, try_n
import ree as re
from translator import tr_
import clf2
from ratelimit import limits, sleep_and_retry
from m3u8_tools import M3u8_stream
from timee import sleep
@ -18,11 +19,14 @@ class Image:
self.session = session
@sleep_and_retry
@limits(2, 1)
@limits(1, 1)
def get(self, referer):
soup = downloader.read_soup(self._url, referer, session=self.session)
div = soup.find('div', id='display_image_detail')
url = urljoin(self._url, div.find('img').parent['href'])
div = soup.find('div', id='display_image_detail') or soup.find('ul', id='detail_list')
parent = div.find('img').parent
while not parent.get('href'):
parent = parent.parent
url = urljoin(self._url, parent['href'])
ext = get_ext(url)
self.filename = '{:04}{}'.format(self._p, ext)
return url, self._url
@ -39,13 +43,14 @@ class Video:
self.url = LazyUrl(referer, lambda _: src, self)
self.filename = '{}{}'.format(clean_title(title), ext)
@Downloader.register
class Downloader_hentaicosplay(Downloader):
type = 'hentaicosplay'
URLS = ['hentai-cosplays.com', 'porn-images-xxx.com']
icon = None
display_name = 'Hentai Cosplay'
MAX_PARALLEL = 1 # must be 1
MAX_CORE = 4
@classmethod
@ -59,13 +64,20 @@ class Downloader_hentaicosplay(Downloader):
def init(self):
self.session = Session()
@try_n(2)
def read(self):
#4961
ua = downloader.random_ua()
self.print_(f'read start ua: {ua}')
downloader.REPLACE_UA[r'hentai-cosplays\.com'] = ua
downloader.REPLACE_UA[r'porn-images-xxx\.com'] = ua
if '/video/' in self.url:
res = clf2.solve(self.url, session=self.session, cw=self.cw)
soup = Soup(res['html'])
title = soup.find('h1', id='post_title').text.strip()
title = (soup.find('h1', id='post_title') or soup.find('div', id='page').find('h2')).text.strip()
self.title = title
view = soup.find('div', id='post')
view = soup.find('div', id='post') or soup.find('div', class_='video-container')
video = view.find('video')
src = video.find('source')['src']
src = urljoin(self.url, src)
@ -73,14 +85,14 @@ class Downloader_hentaicosplay(Downloader):
self.urls.append(video.url)
self.single = True
return
if '/image/' not in self.url:
raise NotImplementedError('Not a post')
res = clf2.solve(self.url, session=self.session, cw=self.cw)
soup = Soup(res['html'])
title = soup.find('h2').text
paginator = soup.find('div', id='paginator')
title = (soup.find('h2') or soup.find('h3')).text
paginator = soup.find('div', id='paginator') or soup.find('div', class_='paginator_area')
pages = [self.url]
for a in paginator.findAll('a'):
href = a.get('href')
@ -89,23 +101,27 @@ class Downloader_hentaicosplay(Downloader):
href = urljoin(self.url, href)
if href not in pages:
pages.append(href)
self.print_(f'pages: {len(pages)}')
imgs = []
for i, page in enumerate(pages):
sleep(2, self.cw)
if page == self.url:
soup_page = soup
else:
soup_page = downloader.read_soup(page, session=self.session)
view = soup_page.find('div', id='post')
view = soup_page.find('div', id='post') or soup_page.find('ul', id='detail_list')
for img in view.findAll('img'):
href = img.parent['href']
href = img.parent.get('href') or img.parent.parent.get('href')
if not href:
continue
href = urljoin(page, href)
img = Image(href, page, len(imgs), self.session)
imgs.append(img)
self.print_(f'imgs: {len(imgs)}')
self.cw.setTitle('{} {} ({} / {})'.format(tr_('읽는 중...'), title, i+1, len(pages)))
for img in imgs:
self.urls.append(img.url)
self.title = clean_title(title)

View File

@ -15,12 +15,12 @@ class Image(object):
def f(_):
html = downloader.read_html(url, session=session)
soup = Soup(html)
box = soup.find('section', id='picBox')
img = box.find('img')
if img is None:
raise Exception('No img')
onclick = img.attrs.get('onclick', '')
if onclick and '.src' in onclick:
print('onclick', onclick)
@ -28,14 +28,14 @@ class Image(object):
else:
img = img.attrs['src']
img = urljoin(url, img)
filename = clean_title(os.path.basename(img.split('?')[0]))
name, ext = os.path.splitext(filename)
# https://www.hentai-foundry.com/pictures/user/DrGraevling/74069/Eversong-Interrogation-pg.-13
if ext.lower() not in ['.bmp', '.png', '.gif', '.jpg', '.jpeg', '.webp', '.webm', '.avi', '.mp4', '.mkv', '.wmv']:
filename = u'{}.jpg'.format(name)
self.filename = filename
return img
self.url = LazyUrl(url, f, self)
@ -47,13 +47,13 @@ def get_username(url):
return username
@Downloader.register
class Downloader_hf(Downloader):
type = 'hf'
URLS = ['hentai-foundry.com']
MAX_CORE = 16
display_name = 'Hentai Foundry'
def init(self):
self.session = enter()
@ -78,7 +78,7 @@ class Downloader_hf(Downloader):
def enter():
print('enter')
session = Session()
r = session.get(URL_ENTER)
# 862
@ -104,10 +104,10 @@ def enter():
})
r = session.post(URL_FILTER, data=data, headers={'Referer': r.url})
print(r)
return session
def get_imgs(username, title, session, cw=None):
url = 'https://www.hentai-foundry.com/pictures/user/{}'.format(username)
@ -153,4 +153,3 @@ def get_imgs(username, title, session, cw=None):
imgs.append(img)
return imgs

View File

@ -1,15 +1,11 @@
# uncompyle6 version 3.5.0
# Python bytecode 2.7 (62211)
# Decompiled from: Python 2.7.16 (v2.7.16:413a49145e, Mar 4 2019, 01:30:55) [MSC v.1500 32 bit (Intel)]
# Embedded file name: imgur_downloader.pyo
# Compiled at: 2019-10-07 05:58:14
import downloader
from utils import Downloader, Soup, try_n, urljoin, get_max_range, clean_title, cut_pair
import ree as re, json, os
from timee import sleep
from translator import tr_
@Downloader.register
class Downloader_imgur(Downloader):
type = 'imgur'
URLS = ['imgur.com']
@ -81,7 +77,7 @@ def get_imgs(url, info=None, cw=None):
imgs_ = info['media']
else: # legacy
imgs_ = [info]
for img in imgs_:
img_url = img.get('url') # new
if not img_url: # legacy
@ -91,7 +87,7 @@ def get_imgs(url, info=None, cw=None):
if img_url in imgs:
continue
imgs.append(img_url)
elif info['type'] == 'r':
urls = set()
for p in range(100):
@ -99,7 +95,7 @@ def get_imgs(url, info=None, cw=None):
print(url_api)
html = downloader.read_html(url_api, referer=url)
soup = Soup(html)
c = 0
for post in soup.findAll('div', class_='post'):
a = post.find('a', class_='image-list-link')
@ -122,10 +118,9 @@ def get_imgs(url, info=None, cw=None):
return []
else:
print(s)
if c == 0:
print('same; break')
break
return imgs

View File

@ -33,7 +33,7 @@ class File(object):
class LazyFile(object):
_url = None
thumb = None
def __init__(self, url, type_, session):
self.url = LazyUrl(url, self.get, self)
self.type = {'videos': 'video', 'images': 'image'}.get(type_) or type_
@ -48,9 +48,9 @@ class LazyFile(object):
self.filename = file.filename
self._url = file.url()
return self._url
@Downloader.register
class Downloader_iwara(Downloader):
type = 'iwara'
URLS = ['iwara.tv']
@ -104,14 +104,14 @@ class Downloader_iwara(Downloader):
if file.type == 'youtube':
raise errors.Invalid('[iwara] Youtube: {}'.format(self.url))
if file.type == 'image':
self.single = False
title = title or file.title
if not self.single:
title = clean_title(title)
self.title = title
if file.thumb is not None:
self.setIcon(file.thumb)
@ -143,11 +143,11 @@ def read_channel(url, type_, session, cw=None):
if p == 0:
title = soup.find('h1', class_='page-title').text
info['title'] = title.replace("'s videos", '').replace("'s images", '').strip()
view = soup.find('div', class_='view-content')
if view is None:
break
urls_new = []
for div in view.findAll('div', class_='views-column'):
href = div.find('a')['href']
@ -175,7 +175,7 @@ def get_files(url, session, multi_post=False, cw=None):
video = content.find('video')
if youtube:
type = 'youtube'
elif video:
elif video and video.attrs.get('poster'): #4901
type = 'video'
else:
type = 'image'
@ -183,8 +183,12 @@ def get_files(url, session, multi_post=False, cw=None):
files = []
if type == 'image':
urls = set()
for img in content.findAll('img'):
img = urljoin(url, img.parent.attrs['href'])
for img in content.findAll(['img', 'video']):
if img.source: #4901
img = img.source['src']
else:
img = img.parent.attrs['href']
img = urljoin(url, img)
if '/files/' not in img:
continue
if img in urls:

View File

@ -30,7 +30,7 @@ class Page(object):
self.id = int(re.find(PATTERN_ID, url))
@Downloader.register
class Downloader_jmana(Downloader):
type = 'jmana'
URLS = ['regex:'+PATTERN_ALL]
@ -55,7 +55,7 @@ class Downloader_jmana(Downloader):
raise Exception('list not found')
self.url = self.fix_url(url)
self._soup = None
for i, page in enumerate(get_pages(self.url, self.session, self.soup)):
if page.id == int(op['value']):
break
@ -106,7 +106,7 @@ def get_title(soup):
def get_artist(soup):
return re.find(r'작가 *: *(.+)', soup.text, default='').strip() or 'N/A'
@try_n(4, sleep=60)
def get_imgs_page(page, referer, session, cw=None):
@ -118,15 +118,15 @@ def get_imgs_page(page, referer, session, cw=None):
print_('inserted: {}'.format(inserted))
inserted = set(int(i) for i in inserted.split(',')) if inserted else set()
soup = Soup(html)
view = soup.find(class_='pdf-wrap')
imgs = []
for i, img in enumerate(child for child in view.children if isinstance(child, bs4.element.Tag)):
src = img.get('data-src') or img.get('src') or ''
if i in inserted:
print_('remove: {}'.format(src))
continue
@ -140,7 +140,7 @@ def get_imgs_page(page, referer, session, cw=None):
if '/notice' in src:
print('notice:', src)
continue
img = Image(src, page, len(imgs))
imgs.append(img)
@ -195,7 +195,7 @@ def get_imgs(url, title, session, soup=None, cw=None):
if imgs_already:
imgs += imgs_already
continue
imgs += get_imgs_page(page, url, session, cw)
if cw is not None:
if not cw.alive:
@ -206,4 +206,3 @@ def get_imgs(url, title, session, soup=None, cw=None):
raise Exception('no imgs')
return imgs

View File

@ -22,9 +22,9 @@ class Image(object):
self.url = LazyUrl('https://page.kakao.com/', lambda _: url, self)
ext = '.jpg'
self.filename = '{}/{:04}{}'.format(clean_title(page.title), p, ext)
@Downloader.register
class Downloader_kakaopage(Downloader):
type = 'kakaopage'
URLS = ['page.kakao.com/home']
@ -56,8 +56,8 @@ class Downloader_kakaopage(Downloader):
self.title = info['title']
def get_id(url):
id_ = re.find('seriesId=([0-9]+)', url, err='No seriesId')
return id_
@ -138,7 +138,7 @@ def get_info(url, session, cw=None):
raise Exception('no pages')
info = {}
html = read_html(url, session=session)
soup = Soup(html)
@ -174,7 +174,7 @@ def get_info(url, session, cw=None):
if imgs_already:
imgs += imgs_already
continue
try:
_imgs = get_imgs_page(page, session)
e_msg = None
@ -184,7 +184,7 @@ def get_info(url, session, cw=None):
print_('{} {}'.format(page.title, len(_imgs)))
if e_msg:
print_(e_msg)
imgs += _imgs
sleep(.2)

View File

@ -5,7 +5,7 @@ from io import BytesIO as IO
from m3u8_tools import M3u8_stream
@Downloader.register
class Downloader_vlive(Downloader):
type = 'kakaotv'
URLS = ['tv.kakao']
@ -31,7 +31,7 @@ class Downloader_vlive(Downloader):
class Video(object):
_url = None
def __init__(self, url, cw=None):
self.url = LazyUrl(url, self.get, self)
self.cw = cw
@ -40,13 +40,13 @@ class Video(object):
def get(self, url):
if self._url:
return self._url
ydl = ytdl.YoutubeDL(cw=self.cw)
info = ydl.extract_info(url)
fs = [f for f in info['formats'] if f['ext'] == 'mp4']
f = sorted(fs, key=lambda f: f['height'])[-1]
self._url = f['url']
self.thumb_url = info['thumbnails'][0]['url']
self.thumb = IO()
downloader.download(self.thumb_url, buffer=self.thumb)

View File

@ -26,7 +26,7 @@ class Page(object):
return f
@Downloader.register
class Downloader_kakuyomu(Downloader):
type = 'kakuyomu'
URLS = ['kakuyomu.jp']
@ -36,13 +36,13 @@ class Downloader_kakuyomu(Downloader):
def init(self):
self.info = get_info(self.url, cw=self.cw)
def read(self):
outdir = get_outdir('kakuyomu')
self.artist = self.info['artist']
title_dir = clean_title(u'[{}] {}'.format(self.artist, self.info['title']))
for page in self.info['pages']:
file = os.path.join(outdir, title_dir, page.filename)
if os.path.isfile(file):
@ -85,7 +85,7 @@ def get_text(page):
{}'''.format(page.title, page.date, story)
return text
def get_info(url, soup=None, cw=None):
print_ = get_print(cw)
@ -117,7 +117,7 @@ def get_info(url, soup=None, cw=None):
intro = intro.text.strip()
desc = u' {}{}'.format(catch, ('\n\n\n'+intro) if intro else '')
info['description'] = desc
pages = []
for a in soup.findAll('a', class_='widget-toc-episode-episodeTitle'):
href = urljoin(url, a.attrs['href'])
@ -129,4 +129,3 @@ def get_info(url, soup=None, cw=None):
info['pages'] = pages
return info

View File

@ -6,7 +6,7 @@ from io import BytesIO
import clf2
@Downloader.register
class Downloader_kissjav(Downloader):
type = 'kissjav'
URLS = ['kissjav.com', 'kissjav.li'] #4835
@ -15,12 +15,12 @@ class Downloader_kissjav(Downloader):
def read(self):
self.session = None#get_session(self.url, cw=self.cw)
video = get_video(self.url, self.session, self.cw)
self.urls.append(video.url)
self.setIcon(video.thumb)
self.enableSegment(1024*1024//2)
self.title = video.title
@ -38,7 +38,7 @@ def get_video(url, session, cw):
f = {'res': res, 'src': src}
fs.append(f)
print_(f)
if not fs:
raise Exception('No source')
@ -77,4 +77,3 @@ def get_session(url, cw=None):
session = Session()
clf2.solve(url, session=session, cw=cw)
return session

View File

@ -46,7 +46,7 @@ def get_soup_session(url, cw=None):
return Soup(res['html']), session
@Downloader.register
class Downloader_lhscan(Downloader):
type = 'lhscan'
URLS = [
@ -103,7 +103,7 @@ class Downloader_lhscan(Downloader):
def get_imgs_page(page, referer, session, cw=None):
print_ = get_print(cw)
print_(page.title)
html = downloader.read_html(page.url, referer, session=session)
if clf2._is_captcha(Soup(html)): #4124
html = clf2.solve(page.url, session, cw)['html']
@ -149,7 +149,7 @@ def get_pages(url, session, soup=None, cw=None):
if soup is None:
html = downloader.read_html(url, session=session)
soup = Soup(html)
tab = soup.find('ul', class_='list-chapters')
pages = []

View File

@ -6,7 +6,7 @@ from io import BytesIO
from translator import tr_
@Downloader.register
class Downloader_likee(Downloader):
type = 'likee'
URLS = ['likee.video']
@ -39,7 +39,7 @@ def get_info(url, session, cw=None):
info = {}
info['videos'] = []
if '/video/' in url:
info['type'] = 'single'
video = Video(url, session)
@ -68,7 +68,7 @@ def get_info(url, session, cw=None):
videos = data['data']['videoList']
if not videos:
break
for data in videos:
url_post = 'https://likee.video/@{}/video/{}'.format(data['likeeId'], data['postId'])
if url_post in urls:
@ -87,7 +87,7 @@ def get_info(url, session, cw=None):
cw.setTitle(msg)
else:
print(msg)
return info
@ -107,7 +107,7 @@ class Video(object):
data = json.loads(r.text)
video = data['data']['videoList'][0]
url_video = video['videoUrl']
self.url_thumb = video['coverUrl']
self.artist = video['nickname']
@ -116,4 +116,3 @@ class Video(object):
self.filename = '{}{}'.format(self.id_, ext)
return url_video

View File

@ -7,6 +7,8 @@ from timee import sleep
from translator import tr_
from io import BytesIO
import json
import clf2
downloader.REPLACE_UA[r'\.luscious\.net'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36'
class Image(object):
@ -32,9 +34,9 @@ class Video(object):
self.url_thumb = url_thumb
self.thumb = BytesIO()
downloader.download(self.url_thumb, buffer=self.thumb)
@Downloader.register
class Downloader_luscious(Downloader):
type = 'luscious'
URLS = ['luscious.net']
@ -45,20 +47,28 @@ class Downloader_luscious(Downloader):
url = url.replace('members.luscious.', 'www.luscious.')
return url
@classmethod
def key_id(cls, url):
return '/'.join(url.split('/')[3:])
def read(self):
for try_ in range(8):
def f(html, browser=None):
soup = Soup(html)
if soup.find('input', type='password'):
browser.show()
return False
browser.hide()
try:
html = downloader.read_html(self.url)
break
except Exception as e:
e_ = e
self.print_error(e)
self.print_('retry...')
else:
raise e_
soup = Soup(html)
get_title(soup)
except:
return False
return True
res = clf2.solve(self.url, f=f, cw=self.cw, show=True)
self.url = res['url']
soup = Soup(res['html'])
title = clean_title(get_title(soup))
self.title = tr_(u'읽는 중... {}').format(title)
if '/videos/' in self.url:
@ -101,17 +111,17 @@ def get_imgs(url, soup=None, cw=None):
title = get_title(soup)
n = get_max_range(cw)
imgs = []
p = 1
while True:
imgs_new = get_imgs_p(url, p)
imgs_new, has_next_page = get_imgs_p(url, p)
if not imgs_new:
break
imgs += imgs_new
update(cw, title, imgs)
p += 1
if len(imgs) >= n:
if len(imgs) >= n or not has_next_page:
break
return imgs[:n]
@ -129,12 +139,12 @@ def get_imgs_p(url, p=1):
img = Image(item, url)
imgs.append(img)
return imgs
return imgs, data['data']['picture']['list']['info']['has_next_page']
def get_video(url, soup):
url_thumb = soup.find('meta', {'property': 'og:image'}).attrs['content']
title = re.find('videos/([^/]+)', url)
video = soup.find('video')
url = video.source.attrs['src']

View File

@ -7,13 +7,13 @@ from translator import tr_
DEFAULT_N_THREAD = 2
@Downloader.register
class Downloader_m3u8(Downloader):
type = 'm3u8'
URLS = ['.m3u8']
single = True
display_name = 'M3U8'
@classmethod
def fix_url(cls, url):
if '://' not in url:
@ -43,7 +43,7 @@ class Video(object):
import selector
@selector.options('m3u8')
def options():
def options(urls):
def f(urls):
n_thread, ok = utils.QInputDialog.getInt(Downloader.mainWindow, tr_('Set number of threads'), tr_('Number of threads?'), value=DEFAULT_N_THREAD, min=1, max=4, step=1)
if not ok:

View File

@ -31,7 +31,7 @@ class Page(object):
self.id = int(re.find(r'/(comic|webtoon)/([0-9]+)', url, err='no id')[1])
@Downloader.register
class Downloader_manatoki(Downloader):
type = 'manatoki'
URLS = [r'regex:(mana|new)toki[0-9]*\.(com|net)']
@ -55,7 +55,7 @@ class Downloader_manatoki(Downloader):
raise Exception('no selected option')
self.session, self.soup, url = get_soup(url)
url_page = self.fix_url(url)
for i, page in enumerate(get_pages(url_page, self.soup)):
if page.id == int(op['value']):
break
@ -89,7 +89,7 @@ class Downloader_manatoki(Downloader):
self.artist = get_artist(self.soup)
imgs = get_imgs(self.url, self.name, self.soup, self.session, self.cw)
for img in imgs:
if isinstance(img, Image):
self.urls.append(img.url)
@ -121,26 +121,53 @@ def get_soup(url, session=None):
return True
res = clf2.solve(url, session=session, f=f)
soup = Soup(res['html'], apply_css=True)
return session, soup, res['url']
def get_pages(url, soup):
def get_pages(url, soup, sub=False):
list = soup.find('ul', class_='list-body')
pages = []
titles = {}
for item in list.findAll('div', 'wr-subject')[::-1]:
for item in list.findAll('div', 'wr-subject'):
for span in item.a.findAll('span'):
span.decompose()
title = item.a.text.strip()
title = utils.fix_dup(title, titles) #4161
href = item.a['href']
href = urljoin(url, href)
page = Page(title, href)
pages.append(page)
pages.append((title, href))
if not pages:
raise Exception('no pages')
return pages
## if sub: #4909
## return pages
## else:
## pg = soup.find('ul', class_='pagination')
## as_ = pg.findAll('a')
## for a in as_:
## href = a.get('href')
## if not href:
## continue
## href = urljoin(url, href)
## for try_ in range(2):
## try:
## session, soup2, href = get_soup(href)
## pages += get_pages(href, soup2, sub=True)
## break
## except Exception as e:
## e_ = e
## print(e)
## else:
## raise e_
titles = {}
pages_ = []
for title, href in pages[::-1]:
title = utils.fix_dup(title, titles) #4161
page = Page(title, href)
pages_.append(page)
return pages_
@page_selector.register('manatoki')
@ -155,7 +182,7 @@ def f(url):
def get_imgs(url, title, soup=None, session=None, cw=None):
print_ = get_print(cw)
if soup is None or session is None:
session, soup, url = get_soup(url, session)
@ -168,7 +195,7 @@ def get_imgs(url, title, soup=None, session=None, cw=None):
if imgs_already:
imgs += imgs_already
continue
imgs_ = get_imgs_page(page, title, url, session, cw)
imgs += imgs_
@ -198,7 +225,7 @@ def get_imgs_page(page, title, referer, session, cw):
if page.title != title_page:
print_('{} -> {}'.format(page.title, title_page))
page.title = title_page
views = soup.findAll('div', class_='view-content')\
+ soup.findAll('div', class_='view-padding')
if not views:
@ -208,7 +235,7 @@ def get_imgs_page(page, title, referer, session, cw):
print_('hash: {}'.format(hash))
if hash is None:
raise Exception('no hash')
imgs = []
for view in views:
if view is None:

View File

@ -30,16 +30,14 @@ class Page(object):
self.soup = soup
@Downloader.register
class Downloader_mrm(Downloader):
type = 'mrm'
URLS = ['myreadingmanga.info']
_soup = None
MAX_CORE = 4
display_name = 'MyReadingManga'
def init(self):
self.session = get_session(self.url, self.cw)
@ -76,7 +74,7 @@ class Downloader_mrm(Downloader):
self.urls.append(img.url)
self.title = self.name
def get_title(soup):
title = soup.find('h1', class_='entry-title').text.strip()
@ -102,7 +100,7 @@ def get_imgs(url, soup=None, session=None, cw=None):
imgs = []
for i, page in enumerate(pages):
s = '{} {} / {} ({} / {})'.format(tr_('읽는 중...'), title, page.title, i+1, len(pages))
if cw:
if not cw.alive:
return
@ -157,7 +155,7 @@ def get_imgs_page(page, session=None, cw=None):
html = read_html(url, session=session, cw=None)
soup = Soup(html)
page.soup = soup
view = soup.find('div', class_='entry-content')
imgs = []
@ -209,4 +207,3 @@ def get_session(url, cw=None):
session = r['session']
return session

View File

@ -30,12 +30,12 @@ def get_id(url):
return username, pid
@Downloader.register
class Downloader_naver(Downloader):
type = 'naver'
URLS = ['blog.naver.', '.blog.me']
display_name = 'Naver Blog'
def init(self):
username, pid = get_id(self.url)
if username is None:
@ -58,7 +58,7 @@ class Downloader_naver(Downloader):
self.urls.append(img.url)
self.title = self.name
class Image(object):
def __init__(self, url, referer, p):
@ -66,14 +66,14 @@ class Image(object):
#3788, #3817
ext = get_ext(url)
self.filename = '{:04}{}'.format(p, ext)
class Video(object):
def __init__(self, url, referer, p):
self.url = LazyUrl(referer, lambda _: url, self)
self.filename = 'video_{}.mp4'.format(p)
def read_page(url, depth=0):
print('read_page', url, depth)
if depth > 10:
@ -108,7 +108,7 @@ def get_imgs(url):
print('view', view is not None)
imgs_ = view.findAll('span', class_='_img') + view.findAll('img')
for img in imgs_:
url = img.attrs.get('src', None)
if url is None:
@ -116,7 +116,7 @@ def get_imgs(url):
if url is None:
print(u'invalid img: {}'.format(url))
continue
if 'ssl.pstatic.net' in url: #
continue
@ -128,7 +128,7 @@ def get_imgs(url):
if 'storep-phinf.pstatic.net' in url: # emoticon
continue
url = url.replace('mblogthumb-phinf', 'blogfiles')
#url = re.sub('\?type=[a-zA-Z0-9]*', '?type=w1@2x', url)
#url = re.sub('\?type=[a-zA-Z0-9]*', '', url)
@ -137,7 +137,7 @@ def get_imgs(url):
if url in urls:
print('### Duplicate:', url)
continue
urls.add(url)
#url = url.split('?type=')[0]
img = Image(url, referer, len(imgs))
@ -170,4 +170,3 @@ def get_imgs(url):
videos.append(video)
return imgs + videos

View File

@ -2,7 +2,7 @@
from utils import Downloader, get_print, urljoin, Soup, get_ext, LazyUrl, clean_title, downloader, re, try_n, errors, json
@Downloader.register
class Downloader_navercafe(Downloader):
type = 'navercafe'
URLS = ['cafe.naver.com']
@ -45,7 +45,7 @@ def get_info(url, cw=None):
info['cafename'] = j['result']['cafe']['url']
info['cafeid'] = clubid
info['id'] = articleid
html_content = j['result']['article']['contentHtml']
soup = Soup(html_content)
@ -75,7 +75,7 @@ def get_info(url, cw=None):
fs = sorted(fs, key=lambda f: f['size'], reverse=True)
video = Image(fs[0]['source'], url_article, len(imgs))
imgs.append(video)
for img in soup.findAll('img'):
img = Image(urljoin(url_article, img['src']), url, len(imgs))
imgs.append(img)

View File

@ -48,7 +48,7 @@ class Page(object):
self.url = url
@Downloader.register
class DownloaderNaverPost(Downloader):
type = "naverpost" # 타입
URLS = ["m.post.naver.com", "post.naver.com"]

View File

@ -33,7 +33,7 @@ class Info(object):
self.artist = artist
@Downloader.register
class Downloader_navertoon(Downloader):
type = 'navertoon'
URLS = ['comic.naver.com']
@ -167,7 +167,7 @@ def get_imgs(page, cw=None):
type_ = re.find('''webtoonType *: *['"](.+?)['"]''', html)
print_('type: {}'.format(type_))
imgs = []
if type_ == 'DEFAULT': # https://m.comic.naver.com/webtoon/detail.nhn?titleId=715772
view = soup.find('div', class_='toon_view_lst')
@ -235,4 +235,3 @@ def get_imgs_all(url, title, cw=None):
break
return imgs

View File

@ -9,13 +9,12 @@ import ytdl
@Downloader.register
class Downloader_navertv(Downloader):
type = 'navertv'
single = True
URLS = ['tv.naver.com']
display_name = 'Naver TV'
def init(self):
if not re.match('https?://.+', self.url, re.IGNORECASE):
self.url = 'https://tv.naver.com/v/{}'.format(self.url)
@ -35,7 +34,7 @@ class Downloader_navertv(Downloader):
class Video(object):
_url = None
def __init__(self, url, cw=None):
self.url = LazyUrl(url, self.get, self)
self.cw = cw
@ -44,7 +43,7 @@ class Video(object):
def get(self, url):
if self._url:
return self._url
ydl = ytdl.YoutubeDL(cw=self.cw)
info = ydl.extract_info(url)
fs = [f for f in info['formats'] if f['protocol'] in ['http', 'https']]
@ -53,7 +52,7 @@ class Video(object):
raise Exception('No MP4 videos')
f = fs[0]
self._url = f['url']
self.thumb_url = info['thumbnails'][0]['url']
self.thumb = IO()
downloader.download(self.thumb_url, buffer=self.thumb)

View File

@ -7,13 +7,13 @@ import os
import json
@Downloader.register
class Downloader_nhentai_com(Downloader):
type = 'nhentai_com'
URLS = [r'regex:https?://nhentai.com']
MAX_CORE = 16
display_name = 'nhentai.com'
def init(self):
self.info = get_info(self.url)
self.url = self.info['url']
@ -56,8 +56,8 @@ class LazyUrl_nhentai_com(LazyUrl):
url = data['url']
img = Image(referer, url, data['p'])
return img.url
class Image(object):
def __init__(self, url_page, url_img, p):
self.p = p
@ -82,7 +82,7 @@ def get_info(url):
info = {}
info['url'] = url
info['id'] = int(data['id'])
info['type'] = data['category']['name']
info['title'] = data['title']
@ -97,7 +97,5 @@ def get_info(url):
img = Image(url, img, len(imgs))
imgs.append(img)
info['imgs'] = imgs
return info

View File

@ -15,13 +15,13 @@ def get_id(url):
return int(re.find('/g/([0-9]+)', url))
@Downloader.register
class Downloader_nhentai(Downloader):
type = 'nhentai'
URLS = ['nhentai.net']
MAX_CORE = 16
display_name = 'nhentai'
def init(self):
self.session = clf2.solve(self.url)['session'] #4541
@ -63,8 +63,8 @@ class LazyUrl_nhentai(LazyUrl):
url = data['url']
img = Image(referer, url, data['p'])
return img.url
class Image(object):
def __init__(self, url_page, url_img, p):
self.p = p
@ -96,7 +96,7 @@ def get_info(id, session):
data = html.split('JSON.parse(')[1].split(');')[0]
gal = json.loads(json.loads(data))
host = 'https://i.nhentai.net'#re.find('''media_url: *['"]([^'"]+)''', html, err='no host')
id = int(gal['id'])
id_media = int(gal['media_id'])
title = gal['title']['english']

View File

@ -27,9 +27,9 @@ class Video(object):
self.username = info['uploader']
self.url = LazyUrl('https://www.nicovideo.jp/watch/{}'.format(self.id), lambda _: info['url'], self, pp=self.pp)
self.cw = cw
self.filename = format_filename(self.title, self.id, self.ext)
self.url_thumb = info['thumbnail_url']
print('thumb:', self.url_thumb)
self.thumb = BytesIO()
@ -60,7 +60,7 @@ def suitable(url):
return get_id(url) is not None
@Downloader.register
class Downloader_nico(Downloader):
type = 'nico'
single = True
@ -94,7 +94,7 @@ class Downloader_nico(Downloader):
else:
username = ''
password = ''
try:
session = login(username, password)
except Exception as e:
@ -133,7 +133,7 @@ def get_video(session, url, format, cw=None):
import selector
@selector.options('nico')
def options():
def options(urls):
return [
{'text': 'MP4 (동영상)', 'format': 'mp4'},
{'text': 'MP3 (음원)', 'format': 'mp3'},

View File

@ -21,13 +21,13 @@ def isLogin(soup):
return False
@Downloader.register
class Downloader_nijie(Downloader):
type = 'nijie'
URLS = ['nijie.info']
MAX_CORE = 4
display_name = 'ニジエ'
def init(self):
if 'members.php' not in self.url and 'members_illust.php' not in self.url:
raise NotImplementedError()
@ -77,7 +77,7 @@ class Image(object):
ext = get_ext(img)
self.filename = '{}_p{}{}'.format(self.id, self.p, ext)
return img
@try_n(8, sleep=10)
def get_imgs_post(id, url):
@ -92,7 +92,7 @@ def get_imgs_post(id, url):
img = Image(id, url, len(imgs), False, url_img)
imgs.append(img)
return imgs
def setPage(url, page):
# Always use HTTPS
@ -142,7 +142,7 @@ def get_imgs(url, title=None, cw=None):
imgs_ = get_imgs_post(id, url_img)#
else:
imgs_ = [Image(id, url_img, 0)]
imgs += imgs_
c += 1
@ -160,5 +160,3 @@ def get_imgs(url, title=None, cw=None):
if len(imgs) >= max_pid or c == 0:
break
return imgs

View File

@ -23,7 +23,7 @@ class Image:
return url
@Downloader.register
class Downloader_nozomi(Downloader):
type = 'nozomi'
URLS = ['nozomi.la']

View File

@ -10,7 +10,7 @@ import errors
class EmbedUrlError(Exception): pass
@Downloader.register
class Downloader_pandoratv(Downloader):
type = 'pandoratv'
URLS = ['pandora.tv']
@ -50,7 +50,7 @@ def extract(name, html, cw=None):
class Video(object):
_url_video = None
def __init__(self, url, format='title', cw=None):
self.url = LazyUrl(url, self.get, self)
self.format = format
@ -68,7 +68,7 @@ class Video(object):
embedUrl = extract('embedUrl', html, cw)
if embedUrl:
raise EmbedUrlError('[pandoratv] EmbedUrl: {}'.format(embedUrl))
uid = extract('strLocalChUserId', html, cw)
pid = extract('nLocalPrgId', html, cw)
fid = extract('strFid', html, cw)
@ -98,13 +98,12 @@ class Video(object):
self._url_video = data['src']
self.title = soup.find('meta', {'property': 'og:description'})['content']
ext = get_ext(self._url_video)
self.filename = format_filename(self.title, pid, ext)
self.url_thumb = soup.find('meta', {'property': 'og:image'})['content']
self.thumb = BytesIO()
downloader.download(self.url_thumb, buffer=self.thumb)
return self._url_video
return self._url_video

View File

@ -7,15 +7,15 @@ from mastodon import get_imgs
import json
@Downloader.register
class Downloader_pawoo(Downloader):
type = 'pawoo'
URLS = ['pawoo.net']
def init(self):
self.url = 'https://pawoo.net/{}'.format(self.id_)
self.referer = self.url
@property
def id_(self):
return re.find('pawoo.net/([^/]+)', self.url.lower(), default=self.url)
@ -41,5 +41,3 @@ class Downloader_pawoo(Downloader):
self.filenames[img.url] = img.filename
self.title = self.name

View File

@ -10,7 +10,7 @@ from m3u8_tools import playlist2stream, M3u8_stream
BASE_URL = 'https://www.pinterest.com'
@Downloader.register
class Downloader_pinter(Downloader):
type = 'pinter'
URLS = ['pinterest.']
@ -82,11 +82,11 @@ def get_info(username, board, api):
class PinterestAPI:
HEADERS = {'Accept': 'application/json, text/javascript, */*, q=0.01',
'Accept-Language': 'en-US,en;q=0.5',
'X-Pinterest-AppState': 'active',
'X-APP-VERSION': 'cb1c7f9',
'X-Requested-With': 'XMLHttpRequest',
HEADERS = {'Accept': 'application/json, text/javascript, */*, q=0.01',
'Accept-Language': 'en-US,en;q=0.5',
'X-Pinterest-AppState': 'active',
'X-APP-VERSION': 'cb1c7f9',
'X-Requested-With': 'XMLHttpRequest',
'Origin': BASE_URL + '/'}
def __init__(self):
@ -228,4 +228,3 @@ def get_username_board(url):
board = board[:-1].strip()
return (username, board)

View File

@ -7,6 +7,7 @@ import page_selector, clf2
from hashlib import md5
from datetime import datetime
import errors
import utils
SALT = 'mAtW1X8SzGS880fsjEXlM73QpS1i4kUMBhyhdaYySk8nWz533nrEunaSplg63fzT'
@ -26,7 +27,7 @@ class Page(object):
self.url = url
@Downloader.register
class Downloader_pixiv_comic(Downloader):
type = 'pixiv_comic'
URLS = ['comic.pixiv.net/works', 'comic.pixiv.net/viewer/']
@ -93,12 +94,14 @@ def get_artist(soup):
if artist:
return artist.text.strip()
else:
artist = re.find(r'"author" *: *(".*?")', soup.html) # 4389
html = soup.html.replace('\\"', utils.esc('"')) #4936
artist = re.find(r'"author" *: *(".*?")', html) # 4389
if artist:
artist = json.loads(artist)
artist = json.loads(artist).replace(utils.esc('"'), '"')
return artist or None
@try_n(2)
def get_pages(soup, url):
pages = []
hrefs = set()
@ -109,6 +112,8 @@ def get_pages(soup, url):
continue
hrefs.add(href)
divs = a.findAll('div', recursive=False)
if len(divs) < 2:
divs = divs[0].findAll('div', recursive=False) #4861
if len(divs) < 2:
continue
right = divs[1]
@ -158,7 +163,7 @@ def get_imgs(url, title, soup=None, session=None, cw=None):
if cw is not None:
if not cw.alive:
return
cw.setTitle((u'{} {} / {} ({} / {})').format(tr_(u'\uc77d\ub294 \uc911...'), title, page.title, i + 1, len(pages)))
cw.setTitle('{} {} / {} ({} / {})'.format(tr_('읽는 중...'), title, page.title, i + 1, len(pages)))
imgs += get_imgs_page(page, session)
return imgs
@ -183,7 +188,7 @@ def get_imgs_page(page, session):
if not pages:
raise Exception('No pages')
imgs = []
for p in pages:
img = p['url']
@ -192,4 +197,3 @@ def get_imgs_page(page, session):
imgs.append(img)
return imgs

View File

@ -28,7 +28,6 @@ for header in ['pixiv_illust', 'pixiv_bmk', 'pixiv_search', 'pixiv_following', '
@Downloader.register
class Downloader_pixiv(Downloader):
type = 'pixiv'
MAX_CORE = 16
@ -49,10 +48,10 @@ class Downloader_pixiv(Downloader):
url = 'https://www.pixiv.net/bookmark_new_illust.php'
elif not re.find(r'^https?://', url) and '.' not in url:
url = 'https://www.pixiv.net/en/users/{}'.format(url)
#3474
url = re.sub(r'(users/[0-9]+)/artworks$', r'\1', url)
url = re.sub(r'[?&]p=[0-9]+$', '', url)
return url.strip('/')
@ -65,6 +64,7 @@ class Downloader_pixiv(Downloader):
## asyncio.set_event_loop(loop)
try:
info = get_info(self.url, self.cw)
self.artist = info.get('artist') #4897
for img in info['imgs']:
self.urls.append(img.url)
self.title = clean_title(info['title'])
@ -108,15 +108,15 @@ class PixivAPI():
if err:
raise PixivAPIError(info.get('message'))
return info['body']
def illust(self, id_):
return self.call('illust/{}'.format(id_))
def pages(self, id_):
return self.call('illust/{}/pages'.format(id_))
def ugoira_meta(self, id_):
return self.call('illust/{}/ugoira_meta'.format(id_))
@ -159,8 +159,8 @@ class PixivAPI():
mode = 'r18' if r18 else 'all'
url = f'follow_latest/illust?p={p}&mode={mode}&lang=en'
return self.call(url)
class Image():
local = False
@ -251,7 +251,7 @@ def tags_matched(tags_illust, tags_add, cw=None):
tags.update((pretty_tag(tag) for tag in tags_add))
if init:
print_('tags_add: {}'.format(tags_add))
tags_illust = set(pretty_tag(tag) for tag in tags_illust)
return (not tags or tags & tags_illust) and tags_ex.isdisjoint(tags_illust)
@ -261,12 +261,12 @@ def get_info(url, cw=None, depth=0, tags_add=None):
api = PixivAPI()
info = {}
imgs = []
ugoira_ext = [None, '.gif', '.webp', '.png'][utils.ui_setting.ugoira_convert.currentIndex()] if utils.ui_setting else None
format_ = compatstr(utils.ui_setting.pixivFormat.currentText()) if utils.ui_setting else 'id_ppage'
max_pid = get_max_range(cw)
if api.illust_id(url): # Single post
id_ = api.illust_id(url)
data = api.illust(id_)
@ -281,7 +281,7 @@ def get_info(url, cw=None, depth=0, tags_add=None):
info['title'] = '{} (pixiv_illust_{})'.format(info['raw_title'], id_)
info['create_date'] = parse_time(data['createDate'])
tags_illust = set(tag['tag'] for tag in data['tags']['tags'])
if tags_matched(tags_illust, tags_add, cw):
if data['illustType'] == 2: # ugoira
data = api.ugoira_meta(id_)
@ -407,12 +407,12 @@ def get_info(url, cw=None, depth=0, tags_add=None):
else:
tag = None
print_('types: {}, tag: {}'.format(types, tag))
id_ = api.user_id(url)
process_user(id_, info, api)
data = api.profile(id_)
info['title'] = '{} (pixiv_{})'.format(info['artist'], info['artist_id'])
ids = []
for type_ in types:
illusts = data[type_]
@ -466,7 +466,7 @@ def process_ids(ids, info, imgs, cw, depth=0, tags_add=None):
@lock
def add_rem(cls, x):
cls.rem += x
def run(self):
while self.alive:
try:

View File

@ -25,25 +25,25 @@ class File(object):
File
'''
def __init__(self, id_, title, url, url_thumb):
def __init__(self, id_, title, url, url_thumb, artist=''):
self.id_ = id_
self.title = clean_title('{}'.format(title))
self.url = url
ext = get_ext(self.url)
if ext.lower() == '.m3u8':
try:
self.url = playlist2stream(self.url, n_thread=4)
except:
self.url = M3u8_stream(self.url, n_thread=4)
self.url_thumb = url_thumb
self.thumb = BytesIO()
downloader.download(self.url_thumb, buffer=self.thumb)
if ext.lower() == '.m3u8':
ext = '.mp4'
self.filename = format_filename(self.title, self.id_, ext)
self.filename = format_filename(self.title, self.id_, ext, artist=artist)
print('filename:', self.filename)
@ -89,7 +89,7 @@ class Video(object):
except: #3511
url = url.replace('pornhub.com', 'pornhubpremium.com')
html = downloader.read_html(url, session=session)
soup = Soup(html)
soup = fix_soup(soup, url, session, cw)
html = soup.html
@ -103,7 +103,7 @@ class Video(object):
print_('GIF')
id_ = url.split('/gif/')[1]
id_ = re.findall('[0-9a-zA-Z]+', id_)[0]
jss = list(gif.children)
for js in jss:
if 'data-mp4' in getattr(js, 'attrs', {}):
@ -139,7 +139,7 @@ class Video(object):
return None
url = url.strip()
return url if re.match(r'^(?:(?:https?|rt(?:m(?:pt?[es]?|fp)|sp[su]?)|mms|ftps?):)?//', url) else None
flashvars = json.loads(re.find(r'var\s+flashvars_\d+\s*=\s*({.+?});', html, err='no flashvars'))
url_thumb = flashvars.get('image_url')
media_definitions = flashvars.get('mediaDefinitions')
@ -245,7 +245,7 @@ class Video(object):
for video_url, height in video_urls_:
if '/video/get_media' in video_url:
print_(video_url)
medias = downloader.read_json(video_url, session=session)
medias = downloader.read_json(video_url, url, session=session)
if isinstance(medias, list):
for media in medias:
if not isinstance(media, dict):
@ -255,9 +255,9 @@ class Video(object):
continue
height = int_or_none(media.get('quality'))
video_urls.append((video_url, height))
continue
video_urls.append((video_url, height))
else:
video_urls.append((video_url, height))
videos = []
for video_url, height in video_urls:
@ -286,8 +286,11 @@ class Video(object):
video = videos[0]
print_('\n[{}p] {} {}'.format(video['height'], video['ext'], video['videoUrl']))
file = File(id_, title, video['videoUrl'].strip(), url_thumb)
#4940
artist = soup.find('div', class_='userInfo').find('div', class_='usernameWrap').text.strip()
file = File(id_, title, video['videoUrl'].strip(), url_thumb, artist)
self._url = file.url
self.title = file.title
self.filename = file.filename
@ -313,7 +316,6 @@ def is_login(session, cw=None, n=2):
@Downloader.register
class Downloader_pornhub(Downloader):
'''
Downloader
@ -323,12 +325,6 @@ class Downloader_pornhub(Downloader):
strip_header = False
URLS = ['pornhub.com', 'pornhubpremium.com', 'pornhubthbh7ap3u.onion']
def init(self):
self.session = Session() # 1791
if 'pornhubpremium.com' in self.url.lower() and\
not is_login(self.session, self.cw):
raise errors.LoginRequired()
@classmethod
def fix_url(cls, url):
if 'pornhub_gif_' in url:
@ -356,9 +352,14 @@ class Downloader_pornhub(Downloader):
raise Exception('no id')
return id_.split('#')[0]
@try_n(2)
def read(self):
cw = self.cw
session = self.session
session = self.session = Session() # 1791
if 'pornhubpremium.com' in self.url.lower() and\
not is_login(session, cw):
raise errors.LoginRequired()
videos = []
tab = ''.join(self.url.replace('pornhubpremium.com', 'pornhub.com', 1).split('?')[0].split('#')[0].split('pornhub.com/')[-1].split('/')[2:3])

View File

@ -28,7 +28,7 @@ def get_tags(url):
return id
@Downloader.register
class Downloader_rule34_xxx(Downloader):
type = 'rule34_xxx'
URLS = ['rule34.xxx']

View File

@ -21,13 +21,13 @@ from ratelimit import limits, sleep_and_retry
from urllib.parse import quote
@Downloader.register
class Downloader_sankaku(Downloader):
type = 'sankaku'
URLS = ['chan.sankakucomplex.com', 'idol.sankakucomplex.com', 'www.sankakucomplex.com']
MAX_CORE = 4
display_name = 'Sankaku Complex'
def init(self):
type = self.url.split('sankakucomplex.com')[0].split('//')[-1].strip('.').split('.')[-1]
if type == '':
@ -174,7 +174,7 @@ class Image(object):
cw = self.cw
d = self.d
print_ = get_print(cw)
for try_ in range(4):
wait(cw)
html = ''
@ -197,7 +197,7 @@ class Image(object):
t_sleep = 5
s = '[Sankaku] failed to read image (id:{}): {}'.format(self.id, e)
print_(s)
sleep(t_sleep, cw)
sleep(t_sleep, cw)
else:
raise Exception('can not find image (id:{})\n{}'.format(self.id, e_msg))
soup = Soup('<p>{}</p>'.format(url))
@ -216,7 +216,7 @@ def setPage(url, page):
url = re.sub(r'page=[0-9]*', 'page={}'.format(page), url)
else:
url += '&page={}'.format(page)
return url
@ -246,7 +246,7 @@ def get_imgs(url, title=None, cw=None, d=None, types=['img', 'gif', 'video'], se
id = get_id(url)
info['imgs'] = [Image(type, id, url, None, cw=cw, d=d)]
return info
# Range
max_pid = get_max_range(cw)
@ -261,7 +261,7 @@ def get_imgs(url, title=None, cw=None, d=None, types=['img', 'gif', 'video'], se
for name in names:
id = os.path.splitext(name)[0]
local_ids[id] = os.path.join(dir, name)
imgs = []
page = 1
url_imgs = set()
@ -287,10 +287,10 @@ def get_imgs(url, title=None, cw=None, d=None, types=['img', 'gif', 'video'], se
url_old = url
soup = Soup(html)
articles = soup.findAll('span', {'class': 'thumb'})
if not articles:
break
for article in articles:
# 1183
tags = article.find('img', class_='preview').attrs['title'].split()
@ -302,7 +302,7 @@ def get_imgs(url, title=None, cw=None, d=None, types=['img', 'gif', 'video'], se
type_ = 'img'
if type_ not in types:
continue
url_img = article.a.attrs['href']
if not url_img.startswith('http'):
url_img = urljoin('https://{}.sankakucomplex.com'.format(type), url_img)
@ -337,7 +337,7 @@ def get_imgs(url, title=None, cw=None, d=None, types=['img', 'gif', 'video'], se
print_(print_error(e)[-1])
#url = setPage(url, page)
break
if cw is not None:
cw.setTitle('{} {} - {}'.format(tr_('읽는 중...'), title, len(imgs)))
else:
@ -347,10 +347,9 @@ def get_imgs(url, title=None, cw=None, d=None, types=['img', 'gif', 'video'], se
raise Exception('no images')
info['imgs'] = imgs
return info
def get_id(url_img):
return re.find('show/([0-9]+)', url_img)

View File

@ -1,14 +1,11 @@
#coding: utf8
import downloader
import json
from io import BytesIO
from utils import Downloader, LazyUrl, get_print, try_n, lock, clean_title
from error_printer import print_error
import os
from timee import sleep
from utils import Downloader, LazyUrl, get_print, try_n, lock, clean_title, get_max_range
import ffmpeg
import ytdl
from m3u8_tools import M3u8_stream
from ratelimit import limits, sleep_and_retry
CLIENT_ID = None
@ -26,41 +23,41 @@ def get_cid(force=False):
class Audio(object):
_url = None
def __init__(self, info, album_art, cw=None):
self.info = info
def __init__(self, url, album_art, cw=None):
self.album_art = album_art
self.cw = cw
self.url = LazyUrl(info['webpage_url'], self.get, self, pp=self.pp)
self.url = LazyUrl(url, self.get, self, pp=self.pp)
@try_n(2)
@sleep_and_retry
@limits(1, 1)
def get(self, url):
print_ = get_print(self.cw)
if self._url:
return self._url
info = self.info
## ydl = ytdl.YoutubeDL()
## info = ydl.extract_info(url)
ydl = ytdl.YoutubeDL()
self.info = info = ydl.extract_info(url)
formats = info['formats']
print(formats)
formats = sorted(formats, key=lambda x: int(x.get('abr', 0)), reverse=True)
url_audio = None
for format in formats:
protocol = format['protocol']
print_(u'{}】 format【{}】 abr【{}'.format(protocol, format['format'], format.get('abr', 0)))
print_('{}】 format【{}】 abr【{}'.format(protocol, format['format'], format.get('abr', 0)))
if not url_audio and protocol in ['http', 'https']:
url_audio = format['url']
if not url_audio:
url_audio = M3u8_stream(formats[0]['url'])
self.album_art = False#
self.username = info['uploader']
self.title = u'{} - {}'.format(self.username, info['title'])
self.filename = u'{}{}'.format(clean_title(self.title, allow_dot=True, n=-4), '.mp3')
self.title = '{} - {}'.format(self.username, info['title'])
self.filename = '{}{}'.format(clean_title(self.title, allow_dot=True, n=-4), '.mp3')
thumb = None
for t in info['thumbnails'][::-1]:
@ -76,7 +73,7 @@ class Audio(object):
print(e)
thumb = None
self.thumb = thumb
self._url = url_audio
return self._url
@ -86,7 +83,7 @@ class Audio(object):
ffmpeg.add_cover(filename, self.thumb, {'artist':self.username, 'title':self.info['title']}, cw=self.cw)
@Downloader.register
class Downloader_soundcloud(Downloader):
type = 'soundcloud'
single = True
@ -94,7 +91,7 @@ class Downloader_soundcloud(Downloader):
#lock = True
audio = None
display_name = 'SoundCloud'
def init(self):
if 'soundcloud.com' in self.url.lower():
self.url = self.url.replace('http://', 'https://')
@ -105,7 +102,7 @@ class Downloader_soundcloud(Downloader):
album_art = self.ui_setting.albumArt.isChecked()
info = get_audios(self.url, self.cw, album_art)
audios = info['audios']
if not audios:
raise Exception('no audios')
@ -139,11 +136,12 @@ def get_audios(url, cw, album_art):
if url.count('/') == 3:
url += '/tracks'
info = {
#'extract_flat': True,
options = {
'extract_flat': True,
'playlistend': get_max_range(cw),
}
ydl = ytdl.YoutubeDL(cw=cw)
ydl = ytdl.YoutubeDL(options, cw=cw)
info = ydl.extract_info(url)
if 'entries' in info:
entries = info['entries']
@ -156,20 +154,19 @@ def get_audios(url, cw, album_art):
break
else:
kind = 'Playlist'
print_(u'kind: {}'.format(kind))
info['title'] = u'[{}] {}'.format(kind.capitalize(), title)
print_('kind: {}'.format(kind))
info['title'] = '[{}] {}'.format(kind.capitalize(), title)
else:
entries = [info]
audios = []
for e in entries:
if '/sets/' in e['webpage_url']:
url = e.get('webpage_url') or e['url']
if '/sets/' in url:
continue
audio = Audio(e, album_art, cw=cw)
audio = Audio(url, album_art, cw=cw)
audios.append(audio)
info['audios'] = audios
return info

View File

@ -32,7 +32,7 @@ class Text(object):
self.url = LazyUrl(url, f, self)
@Downloader.register
class Downloader_syosetu(Downloader):
type = 'syosetu'
URLS = ['syosetu.com']
@ -148,20 +148,20 @@ def get_text(url, subtitle, update, session):
update = u' ' + update
else:
update = ''
story = soup.find('div', id='novel_honbun').text.strip()
p = soup.find('div', id='novel_p')
p = '' if p is None else p.text.strip()
if p:
story = '{}\n\n════════════════════════════════\n\n{}'.format(p, story)
#2888
a = soup.find('div', id='novel_a')
a = '' if a is None else a.text.strip()
if a:
story = '{}\n\n════════════════════════════════\n\n{}'.format(story, a)
text =u'''────────────────────────────────
{}{}
@ -177,5 +177,3 @@ def get_session():
session = Session()
session.cookies.set(name='over18', value='yes', path='/', domain='.syosetu.com')
return session

View File

@ -31,7 +31,7 @@ import requests
from utils import Downloader, Soup
@Downloader.register
class DownloaderTalkOPGG(Downloader):
type = "talkopgg"
URLS = ["talk.op.gg"]

View File

@ -1,7 +1,7 @@
from __future__ import division, print_function, unicode_literals
import downloader
import ree as re
from utils import Soup, LazyUrl, Downloader, try_n, compatstr, get_print, clean_title, Session, get_max_range, format_filename
from utils import Soup, LazyUrl, Downloader, try_n, compatstr, get_print, Session, get_max_range, format_filename, json
from io import BytesIO
import clf2
from translator import tr_
@ -15,14 +15,14 @@ SHOW = True
def is_captcha(soup):
return soup.find('div', class_="verify-wrap") is not None
@Downloader.register
class Downloader_tiktok(Downloader):
type = 'tiktok'
single = True
URLS = ['tiktok.com']
URLS = ['tiktok.com', 'douyin.com']
display_name = 'TikTok'
def init(self):
cw = self.cw
self.session = Session()
@ -37,33 +37,40 @@ class Downloader_tiktok(Downloader):
@classmethod
def fix_url(cls, url):
url = url.split('?')[0].split('#')[0].strip('/')
if 'tiktok.com' not in url.lower():
if '://' not in url:
url = 'https://www.tiktok.com/@{}'.format(url)
return url
def read(self):
format = compatstr(self.ui_setting.youtubeFormat.currentText()).lower().strip()
def parse_video_url(info, item):
if 'tiktok.com' in self.url.lower(): # TikTok
return 'https://www.tiktok.com/@{}/video/{}'.format(info['uid'], item['id'])
else: # Douyin
return 'https://www.douyin.com/video/{}'.format(item['id'])
if re.search(PATTERN_VID, self.url) is None:
info = read_channel(self.url, self.session, self.cw)
items = info['items']
videos = [Video('https://www.tiktok.com/@{}/video/{}'.format(info['uid'], item['id']), self.session, format) for item in items]
videos = [Video(parse_video_url(info, item), self.session, format, self.cw) for item in items]
title = '{} (tiktok_{})'.format(info['nickname'], info['uid'])
video = self.process_playlist(title, videos)
else:
video = Video(self.url, self.session, format)
video = Video(self.url, self.session, format, self.cw)
video.url()
self.urls.append(video.url)
self.title = clean_title(video.title)
self.title = video.title
class Video(object):
_url = None
def __init__(self, url, session, format='title (id)'):
def __init__(self, url, session, format, cw):
self.url = LazyUrl(url, self.get, self)
self.session = session
self.format = format
self.cw = cw
@try_n(2)
def get(self, url):
@ -71,13 +78,14 @@ class Video(object):
return self._url
m = re.search(PATTERN_VID, url)
id = m.group('id')
ext = '.mp4'
self.title = id#
self.filename = format_filename(self.title, id, ext)
ydl = ytdl.YoutubeDL()
ydl = ytdl.YoutubeDL(cw=self.cw)
info = ydl.extract_info(url)
ext = '.mp4'
self.title = info['title']
self.filename = format_filename(self.title, id, ext)
self._url = info['url']
return self._url
@ -97,7 +105,7 @@ def read_channel(url, session, cw=None):
}
max_pid = get_max_range(cw)
def f(html, browser=None):
soup = Soup(html)
if is_captcha(soup):
@ -107,25 +115,38 @@ def read_channel(url, session, cw=None):
elif sd['shown'] and not SHOW:
browser.hide()
sd['shown'] = False
try:
st = soup.find('h2', class_='share-title')
if st is None:
st = soup.find('h2', class_=lambda c: c and 'ShareTitle' in c)
info['uid'] = st.text.strip()
st = soup.find('h1', class_='share-sub-title')
if st is None:
st = soup.find('h1', class_=lambda c: c and 'ShareSubTitle' in c)
info['nickname'] = st.text.strip()
except Exception as e:
print_(print_error(e)[0])
if 'tiktok.com' in url.lower(): # TikTok
try:
st = soup.find('h2', class_='share-title')
if st is None:
st = soup.find('h2', class_=lambda c: c and 'ShareTitle' in c)
info['uid'] = st.text.strip()
st = soup.find('h1', class_='share-sub-title')
if st is None:
st = soup.find('h1', class_=lambda c: c and 'ShareSubTitle' in c)
info['nickname'] = st.text.strip()
except Exception as e:
print_(print_error(e)[0])
else: # Douyin
try:
info['uid'] = re.find(r'''uniqueId%22%3A%22(.+?)%22''', html, err='no uid')
info['nickname'] = json.loads(re.find(r'''"name"\s*:\s*(".+?")''', html, err='no nickname'))
except Exception as e:
print_(print_error(e)[0])
c = 0
ids_now = set()
items = soup.findAll('div', class_='video-feed-item') + soup.findAll('div', class_=lambda c: c and 'DivItemContainer' in c)
for div in items:
a = div.find('a')
if a is None:
continue
href = a['href']
if 'tiktok.com' in url.lower(): # TikTok
items = soup.findAll('div', class_='video-feed-item') + soup.findAll('div', class_=lambda c: c and 'DivItemContainer' in c)
else: # Douyin
items = soup.findAll('a')
for item in items:
if item.name == 'a':
a = item
else:
a = item.find('a')
if a is None:
continue
href = a.get('href')
if not href:
continue
m = re.search(PATTERN_VID, href)
@ -143,10 +164,10 @@ def read_channel(url, session, cw=None):
if len(info['items']) >= max_pid:
info['items'] = info['items'][:max_pid]
return True
browser.runJavaScript('window.scrollTo(0, document.body.scrollHeight);')
sleep(15, cw)
if c or (ids_now and min(ids_now) > min(ids)):
sd['count_empty'] = 0
else:
@ -166,4 +187,3 @@ def read_channel(url, session, cw=None):
raise Exception('no items')
return info

View File

@ -8,14 +8,14 @@ import ree as re
import os
@Downloader.register
class Downloader_tokyomotion(Downloader):
type = 'tokyomotion'
URLS = ['tokyomotion.net']
single = True
_type = None
display_name = 'TOKYO Motion'
def init(self):
html = downloader.read_html(self.url)
self.soup = Soup(html)
@ -67,7 +67,7 @@ def get_video(url, soup=None):
if soup is None:
html = downloader.read_html(url)
soup = Soup(html)
video = soup.find('video', id='vjsplayer').find('source').attrs['src']
url_thumb = soup.find('video', id='vjsplayer').attrs['poster']
title = get_title(soup)

View File

@ -7,16 +7,27 @@ import utils
import filesize as fs
from datetime import datetime
import errors
import ips
torrent = None
TIMEOUT = 600
CACHE_INFO = True
TOO_MANY = 1000
def isInfoHash(s):
if len(s) != 40:
return False
try:
bytes.fromhex(s)
return True
except:
return False
@Downloader.register
class Downloader_torrent(Downloader):
type = 'torrent'
URLS = [r'regex:^magnet:', r'regex:\.torrent$']
URLS = [r'regex:^magnet:', r'regex:\.torrent$', isInfoHash]
single = True
update_filesize = False
_info = None
@ -27,11 +38,14 @@ class Downloader_torrent(Downloader):
_h = None
_dn = None
MAX_PARALLEL = 16
MAX_CORE = 0
skip_convert_imgs = True
_filesize_init = False
_max_speed = None
_anon = False
_proxy = '', '', 0, '', ''
_seeding = False
_virgin = True
@classmethod
def set_max_speed(cls, speed):
@ -58,13 +72,16 @@ class Downloader_torrent(Downloader):
torrent.set_anon(cls._anon)
torrent.set_proxy(*cls._proxy)
@lock
def __init(self):
@classmethod
def _import_torrent(cls):
global torrent
if torrent is None:
import torrent
@lock
def __init(self):
self._import_torrent()
Downloader_torrent.updateSettings()
self.cw.pbar.hide()
@classmethod
def key_id(cls, url):
@ -90,8 +107,9 @@ class Downloader_torrent(Downloader):
return utils.html_unescape(qs['dn'][0])
def read(self):
self.__init()
cw = self.cw
self.cw.pbar.hide()
self.__init()
if cw:
cw._torrent_s = None
title = self.url
@ -126,7 +144,7 @@ class Downloader_torrent(Downloader):
self.print_('Creator: {}'.format(self._info.creator()))
self.print_('Comment: {}'.format(self._info.comment()))
cw.setTotalFileSize(self._info.total_size())
cw.imgs.clear()
cw.dones.clear()
@ -136,7 +154,7 @@ class Downloader_torrent(Downloader):
if not self.single and not os.path.isdir(self.dir): #4698
downloader.makedir_event(self.dir, cw)
cw.pbar.show()
def update_files(self):
@ -146,12 +164,15 @@ class Downloader_torrent(Downloader):
raise Exception('No files')
cw.single = self.single = len(files) <= 1
for file in files:
filename = os.path.join(self.dir, file)
filename = os.path.join(self.dir, file.path)
cw.imgs.append(filename)
def update_pause(self):
cw = self.cw
if cw.pause_lock:
if self._seeding:
cw.pause_lock = False
return
cw.pause_data = {
'type': self.type,
'url': self.url,
@ -165,39 +186,47 @@ class Downloader_torrent(Downloader):
cw.pbar.setFormat('%p%')
cw.setColor('reading')
cw.downloader_pausable = True
self._seeding = False
if cw.paused:
data = cw.pause_data
cw.paused = False
cw.pause_lock = False
self.update_tools_buttons()
self.read()
if self.status == 'stop':
self.stop()
return True
if cw.paused:
pass
else:
cw.dir = self.dir
cw.urls[:] = self.urls
try:
self.read()
if self.status == 'stop':
self.stop()
return True
if cw.paused:
pass
else:
cw.dir = self.dir
cw.urls[:] = self.urls
cw.clearPieces()
self.size = Size()
self.size_upload = Size()
cw.pbar.setMaximum(self._info.total_size())
cw.setColor('reading')
torrent.download(self._info, save_path=self.dir, callback=self.callback, cw=cw)
self.update_progress(self._h, False)
cw.setSpeed(0.0)
cw.setUploadSpeed(0.0)
if not cw.alive:
return
self.update_pause()
if cw.paused:
return True
self.title = self.name
if not self.single:
cw.pbar.setMaximum(len(cw.imgs))
finally:
cw.clearPieces()
self.size = Size()
self.size_upload = Size()
cw.pbar.setMaximum(self._info.total_size())
cw.setColor('downloading')
torrent.download(self._info, save_path=self.dir, callback=self.callback, cw=cw)
self.update_progress(self._h, False)
cw.setSpeed(0.0)
cw.setUploadSpeed(0.0)
if not cw.alive:
return
self.update_pause()
if cw.paused:
return True
self.title = self.name
if not self.single:
cw.pbar.setMaximum(len(cw.imgs))
cw.clearPieces()
self._h = None
try:
cw.set_extra('torrent_progress', torrent.get_file_progress(self._h, self._info, True))
except Exception as e:
cw.remove_extra('torrent_progress')
self.print_error(e)
self._h = None
def _updateIcon(self):
cw = self.cw
@ -211,16 +240,16 @@ class Downloader_torrent(Downloader):
if self._info is None:
return
cw = self.cw
if not cw.imgs: #???
self.print_('???')
self.update_files()
sizes = torrent.get_file_progress(h, self._info, fast)
for i, (file, size) in enumerate(zip(cw.names, sizes)):
if i > 0 and fast:
break#
file = os.path.realpath(file.replace('\\\\?\\', ''))
file = os.path.realpath(file)
if file in cw.dones:
continue
if size[0] == size[1]:
@ -245,7 +274,14 @@ class Downloader_torrent(Downloader):
def _callback(self, h, s, alerts):
self._h = h
cw = self.cw
if self._virgin:
self._virgin = False
try:
ips.get('0.0.0.0')
except Exception as e:
self.print_error(e)
if self._state != s.state_str:
self._state = s.state_str
self.print_('state: {}'.format(s.state_str))
@ -256,19 +292,21 @@ class Downloader_torrent(Downloader):
title = (self._dn or self.url) if self._info is None else self.name
if cw.alive and cw.valid and not cw.pause_lock:
seeding = False
cw._torrent_s = s
fast = len(cw.imgs) > TOO_MANY
self.update_progress(h, fast)
filesize = s.total_done
upload = s.total_upload
if s.state_str in ('downloading', ):
color = 'downloading'
if s.state_str in ('downloading', 'seeding'):
# init filesize
if not self._filesize_init:
self._filesize_prev = filesize
self._filesize_init = True
self.print_('init filesize: {}'.format(fs.size(filesize)))
# download
d_size = filesize - self._filesize_prev
self._filesize_prev = filesize
@ -282,8 +320,10 @@ class Downloader_torrent(Downloader):
if self._info is not None:
cw.pbar.setValue(s.progress * self._info.total_size())
if s.state_str == 'queued':
color = 'reading'
title_ = 'Waiting... {}'.format(title)
elif s.state_str == 'checking files':
color = 'reading'
title_ = 'Checking files... {}'.format(title)
self._filesize_prev = filesize
elif s.state_str == 'downloading':
@ -292,15 +332,51 @@ class Downloader_torrent(Downloader):
cw.setSpeed(self.size.speed)
cw.setUploadSpeed(self.size_upload.speed)
elif s.state_str == 'seeding':
title_ = '{}'.format(title)
cw.setFileSize(filesize)
if not cw.seeding:
return 'abort'
seeding = True
title_ = 'Seeding... {}'.format(title)
cw.setSpeed(self.size_upload.speed)
elif s.state_str == 'reading':
color = 'reading'
title_ = 'Reading... {}'.format(title)
elif s.state_str == 'finished':
return 'abort'
else:
title_ = '{}... {}'.format(s.state_str.capitalize(), title)
cw.setTitle(title_, update_filter=False)
cw.setColor(color)
self._seeding = seeding
else:
self.print_('abort')
if cw:
cw._torrent_s = None
return 'abort'
@utils.actions('torrent')
def actions(cw):
if cw.type != 'torrent':
return
items = [item for item in cw.listWidget().selectedItems() if item.type == 'torrent']
seeding = int(all(item._seeding for item in items)) * 2
if not seeding:
seeding = int(all(item._seeding is False for item in items))
if not seeding:
seeding = 0 if all(item._seeding is None for item in items) else None
if seeding is None:
mix_seeding = any(item._seeding for item in items)
mix_no_seeding = any(item._seeding is False for item in items)
mix_pref = any(item._seeding is None for item in items)
else:
mix_seeding = mix_no_seeding = mix_pref = False
return [
{'icon': 'list', 'text': '파일 목록', 'clicked': cw.showFiles},
{'icon': 'peer', 'text': 'Peers', 'clicked': cw.showPeers},
{'icon': 'tracker', 'text': '트래커 수정', 'clicked': cw.editTrackers},
{'text':'-'},
{'text': '시딩', 'clicked': lambda:cw.setSeedings(True), 'checkable': True, 'checked': seeding==2, 'group': 'seeding', 'mixed': mix_seeding},
{'text': '시딩 하지 않음', 'clicked': lambda:cw.setSeedings(False), 'checkable': True, 'checked': seeding==1, 'group': 'seeding', 'mixed': mix_no_seeding},
{'text': '설정을 따름', 'clicked': lambda:cw.setSeedings(None), 'checkable': True, 'checked': seeding==0, 'group': 'seeding', 'mixed': mix_pref},
]

View File

@ -33,7 +33,7 @@ class Image(object):
return url
@Downloader.register
class Downloader_tumblr(Downloader):
type = 'tumblr'
URLS = ['tumblr.com']
@ -41,7 +41,7 @@ class Downloader_tumblr(Downloader):
def init(self):
if u'tumblr.com/post/' in self.url:
raise errors.Invalid(tr_(u'개별 다운로드는 지원하지 않습니다: {}').format(self.url))
raise errors.Invalid(tr_(u'개별 다운로드는 지원하지 않습니다: {}').format(self.url))
self.session = Session()
@classmethod
@ -52,7 +52,7 @@ class Downloader_tumblr(Downloader):
def read(self):
username = get_id(self.url)
name = get_name(username, self.session)
for img in get_imgs(username, self.session, cw=self.cw):
self.urls.append(img.url)
@ -141,11 +141,11 @@ class Post(object):
def __init__(self, data, url, cw=None):
id_ = data['id']
self.imgs = []
cs = data['content']
for trail in data['trail']:
cs += trail['content']
for c in cs:
if c['type'] in ['image', 'video']:
media = c.get('media')
@ -159,7 +159,7 @@ class Post(object):
continue
else:
raise NotImplementedError(id_, c)
def get_name(username, session):
@ -183,7 +183,7 @@ def get_imgs(username, session, cw=None):
cw.setTitle(s)
else:
print(s)
if len(imgs) > max_pid:
break
@ -205,4 +205,3 @@ def get_id(url):
if url == 'www':
raise Exception('no id')
return url

View File

@ -10,7 +10,7 @@ import errors
import utils
@Downloader.register
class Downloader_twitch(Downloader):
type = 'twitch'
URLS = ['twitch.tv']
@ -35,7 +35,7 @@ class Downloader_twitch(Downloader):
def read(self):
if '/directory/' in self.url.lower():
raise errors.Invalid('[twitch] Directory is unsupported: {}'.format(self.url))
if self.url.count('/') == 3:
if 'www.twitch.tv' in self.url or '//twitch.tv' in self.url:
filter = 'live'
@ -47,7 +47,7 @@ class Downloader_twitch(Downloader):
filter = None
else:
filter = None
if filter is None:
video = Video(self.url, self.cw)
video.url()
@ -64,8 +64,10 @@ class Downloader_twitch(Downloader):
else:
raise NotImplementedError(filter)
self.artist = video.artist
self.setIcon(video.thumb)
@try_n(2)
def get_videos(url, cw=None):
@ -117,7 +119,7 @@ def extract_info(url, cw=None):
raise errors.LoginRequired()
raise
return info
class Video(object):
_url = None
@ -133,11 +135,12 @@ class Video(object):
if self._url:
return self._url
info = extract_info(url, self.cw)
self.artist = info['creator'] #4953
def print_video(video):
#print_(video)#
print_('{}[{}] [{}] [{}] {}'.format('LIVE ', video['format_id'], video.get('height'), video.get('tbr'), video['url']))
videos = [video for video in info['formats'] if video.get('height')]
videos = sorted(videos, key=lambda video:(video.get('height', 0), video.get('tbr', 0)), reverse=True)
@ -152,9 +155,9 @@ class Video(object):
else:
video_best = videos[-1]
print_video(video)
video = video_best['url']
ext = get_ext(video)
self.title = info['title']
id = info['display_id']
@ -166,7 +169,7 @@ class Video(object):
if ext.lower() == '.m3u8':
video = M3u8_stream(video, n_thread=4, alter=alter)
ext = '.mp4'
self.filename = format_filename(self.title, id, ext)
self.filename = format_filename(self.title, id, ext, artist=self.artist)
self.url_thumb = info['thumbnail']
self.thumb = BytesIO()
downloader.download(self.url_thumb, buffer=self.thumb)

View File

@ -35,21 +35,21 @@ class Image(object):
return self._url
@Downloader.register
class Downloader_v2ph(Downloader):
type = 'v2ph'
URLS = ['v2ph.com/album/']
MAX_CORE = 4
MAX_PARALLEL = 1
display_name = 'V2PH'
@classmethod
def fix_url(cls, url):
return url.split('?')[0]
def read(self):
info = get_info(self.url)
for img in get_imgs(self.url, info['title'], self.cw):
self.urls.append(img.url)
@ -68,7 +68,7 @@ def get_info(url):
@try_n(4)
@sleep_and_retry
@limits(1, 5)
def read_soup(url):
def read_soup(url):
return downloader.read_soup(url, user_agent=UA)
@ -91,7 +91,7 @@ def get_imgs(url, title, cw=None):
img = img.attrs['data-src']
img = Image(img, url, len(imgs))
imgs.append(img)
pgn = soup.find('ul', class_='pagination')
ps = [getPage(a.attrs['href']) for a in pgn.findAll('a')] if pgn else []
if not ps or p >= max(ps):
@ -105,5 +105,3 @@ def get_imgs(url, title, cw=None):
print(msg)
return imgs

View File

@ -7,12 +7,11 @@ import ytdl
@Downloader.register
class Downloader_vimeo(Downloader):
type = 'vimeo'
URLS = ['vimeo.com']
single = True
def init(self):
if 'vimeo.com' not in self.url.lower():
self.url = u'https://vimeo.com/{}'.format(self.url)
@ -31,7 +30,7 @@ class Downloader_vimeo(Downloader):
class Video(object):
_url = None
def __init__(self, url, cw=None):
self.url = LazyUrl(url, self.get, self)
self.cw = cw
@ -40,7 +39,7 @@ class Video(object):
def get(self, url):
if self._url:
return self._url
ydl = ytdl.YoutubeDL(cw=self.cw)
info = ydl.extract_info(url)
fs = [f for f in info['formats'] if f['protocol'] in ['http', 'https']]
@ -48,7 +47,7 @@ class Video(object):
if not fs:
raise Exception('No MP4 videos')
f = fs[0]
self.thumb_url = info['thumbnails'][0]['url']
self.thumb = IO()
downloader.download(self.thumb_url, buffer=self.thumb)

View File

@ -7,7 +7,7 @@ from m3u8_tools import M3u8_stream
import os
@Downloader.register
class Downloader_vlive(Downloader):
type = 'vlive'
URLS = ['vlive.tv']
@ -21,21 +21,21 @@ class Downloader_vlive(Downloader):
def read(self):
cw = self.cw
video = get_video(self.url, cw=cw)
self.urls.append(video.url)
self.setIcon(video.thumb)
self.enableSegment()
self.title = clean_title(video.title)
@try_n(4)
def get_video(url, cw=None):
options = {
'noplaylist': True,
}
ydl = ytdl.YoutubeDL(options, cw=cw)
info = ydl.extract_info(url)
@ -51,25 +51,17 @@ def get_video(url, cw=None):
raise Exception('No videos')
f = sorted(fs, key=lambda f:f['quality'])[-1]
subs = {}
for sub, items in info['subtitles'].items():
sub = sub.split('_')[0]
for item in items:
if item['ext'] != 'vtt':
continue
subs[sub] = item['url']
video = Video(f, info, subs, cw)
video = Video(f, info, cw)
return video
class Video(object):
def __init__(self, f, info, subs, cw=None):
def __init__(self, f, info, cw=None):
self.title = title = info['title']
self.id = info['id']
self.url = f['url']
self.subs = subs
self.subs = ytdl.get_subtitles(info)
self.cw = cw
self.thumb = BytesIO()
@ -87,5 +79,3 @@ class Video(object):
def pp(self, filename):
pp_subtitle(self, filename, self.cw)
return filename

View File

@ -11,7 +11,7 @@ from ratelimit import limits, sleep_and_retry
from utils import Downloader, Session, clean_title, get_print, print_error
@Downloader.register
class Downloader_wayback_machine(Downloader):
type = 'waybackmachine'
URLS = ['archive.org', 'web.archive.org']

View File

@ -7,7 +7,6 @@ import ree as re
@Downloader.register
class Downloader_webtoon(Downloader):
type = 'webtoon'
URLS = ['webtoon.com', 'webtoons.com']
@ -34,7 +33,7 @@ class Downloader_webtoon(Downloader):
self.urls.append(img)
self.title = title
class Page(object):
@ -50,7 +49,7 @@ class Image(object):
self.filename = '{}/{:04}{}'.format(clean_title(page.title), p, ext)
self.url = LazyUrl(page.url, lambda _: url, self)
@try_n(2)
def get_imgs(page):

View File

@ -21,7 +21,7 @@ def suitable(url):
return True
@Downloader.register
class Downloader_weibo(Downloader):
type = 'weibo'
URLS = [suitable]
@ -48,10 +48,10 @@ class Downloader_weibo(Downloader):
def read(self):
checkLogin(self.session)
uid, oid, name = get_id(self.url, self.cw)
title = clean_title('{} (weibo_{})'.format(name, uid))
for img in get_imgs(uid, oid, title, self.session, cw=self.cw, d=self, parent=self.mainWindow):
self.urls.append(img.url)
self.filenames[img.url] = img.filename
@ -63,7 +63,7 @@ def checkLogin(session):
c = session.cookies._cookies.get('.weibo.com', {}).get('/',{}).get('SUBP')
if not c or c.is_expired():
raise errors.LoginRequired()
class Album(object):
@ -117,7 +117,7 @@ def get_id(url, cw=None):
def get_imgs(uid, oid, title, session, cw=None, d=None, parent=None):
print_ = get_print(cw)
print_('uid: {}, oid:{}'.format(uid, oid))
max_pid = get_max_range(cw)
@try_n(4)
@ -190,5 +190,3 @@ def get_imgs(uid, oid, title, session, cw=None, d=None, parent=None):
imgs = sorted(imgs, key=lambda img: img.timestamp, reverse=True)
return imgs[:max_pid]

View File

@ -17,8 +17,7 @@ class Image(object):
self.filename = u'{} - {}{}'.format(id, title, ext)
@Downloader.register
class Downloader_wikiart(Downloader):
type = 'wikiart'
URLS = ['wikiart.org']
@ -39,7 +38,7 @@ class Downloader_wikiart(Downloader):
for img in get_imgs(self.url, artist, cw=self.cw):
self.urls.append(img.url)
self.title = clean_title(artist)
@ -103,4 +102,3 @@ def get_artist(userid, soup=None):
soup = Soup(html)
return soup.find('h3').text.strip()

View File

@ -8,7 +8,7 @@ import shutil, ffmpeg, json
from io import BytesIO
@Downloader.register
class Downloader_xhamster(Downloader):
type = 'xhamster'
__name = r'(xhamster|xhwebsite|xhofficial|xhlocal|xhopen|xhtotal|megaxh|xhwide)[0-9]*' #3881, #4332, #4826
@ -38,7 +38,7 @@ class Downloader_xhamster(Downloader):
cw = self.cw
self.enableSegment(1024*1024//2)
thumb = BytesIO()
if '/users/' in self.url:
info = read_channel(self.url, cw)
urls = info['urls']
@ -82,12 +82,12 @@ class Video(object):
fs = self.info['formats']
res = max(get_resolution(), min(f['height'] for f in fs))
fs = [f for f in fs if f['height'] <= res]
video_best = fs[-1]
self._url = video_best['url']
ext = get_ext(self._url)
self.filename = format_filename(self.title, id, ext)
if isinstance(self._url, str) and 'referer=force' in self._url.lower():
self._referer = self._url
else:
@ -110,11 +110,11 @@ def get_info(url):
raise Exception(err.text.strip())
data = get_data(html)
info['title'] = data['videoModel']['title']
info['id'] = data['videoModel']['id']
info['thumbnail'] = data['videoModel']['thumbURL']
fs = []
for res, url_video in data['videoModel']['sources']['mp4'].items():
height = int(re.find('(\d+)p', res))
@ -145,7 +145,7 @@ def read_page(username, p, cw):
raise e_
return items
def read_channel(url, cw=None):
print_ = get_print(cw)
username = url.split('/users/')[1].split('/')[0]
@ -154,7 +154,7 @@ def read_channel(url, cw=None):
soup = downloader.read_soup(url)
title = soup.find('div', class_='user-name').text.strip()
info['title'] = '[Channel] {}'.format(title)
urls = []
urls_set = set()
for p in range(1, 101):
@ -204,7 +204,7 @@ def setPage(url, p):
url += '/{}'.format(p)
return url
def read_gallery(url, cw=None):
print_ = get_print(cw)
@ -225,14 +225,14 @@ def read_gallery(url, cw=None):
print_('p: {}'.format(p))
url = setPage(url, p)
html = downloader.read_html(url)
data = get_data(html)
photos = data['photosGalleryModel']['photos']
if not photos:
print('no photos')
break
for photo in photos:
img = photo['imageURL']
id = photo['id']
@ -243,7 +243,7 @@ def read_gallery(url, cw=None):
ids.add(id)
img = Image(img, id, referer)
imgs.append(img)
info['imgs'] = imgs
return info

View File

@ -30,7 +30,7 @@ def get_id(url):
return url.split('xnxx.com/')[1].split('/')[0]
@Downloader.register
class Downloader_xnxx(Downloader):
type = 'xnxx'
URLS = [r'regex:xnxx[0-9]*\.(com|es)']
@ -46,7 +46,7 @@ class Downloader_xnxx(Downloader):
self.urls.append(video.url)
self.setIcon(video.thumb)
self.title = video.title
def get_video(url):
html = downloader.read_html(url)
@ -65,11 +65,10 @@ def get_video(url):
title = get_title(soup)
url_thumb = soup.find('meta', {'property': 'og:image'}).attrs['content'].strip()
video = Video(video, url, title, url_thumb)
return video
def get_title(soup):
return soup.find('meta', {'property': 'og:title'}).attrs['content'].strip()

View File

@ -57,7 +57,6 @@ class Video(object):
@Downloader.register
class Downloader_xvideo(Downloader):
type = 'xvideo'
URLS = [r'regex:[./]xvideos[0-9]*\.(com|in|es)']
@ -95,7 +94,7 @@ class Downloader_xvideo(Downloader):
video.url()
self.title = video.title
self.urls.append(video.url)
self.setIcon(video.thumb)
@ -118,12 +117,12 @@ def read_channel(url_page, cw=None):
print_(url_api)
r = session.post(url_api)
data = json.loads(r.text)
videos = data.get('videos') #4530
if not videos:
print_('empty')
break
for video in videos:
id_ = video['id']
if id_ in ids:
@ -132,12 +131,12 @@ def read_channel(url_page, cw=None):
ids.add(id_)
info['name'] = video['pn']
urls.append(urljoin(url_page, video['u']))
if len(urls) >= max_pid:
break
n = data['nb_videos']
s = '{} {} - {}'.format(tr_('읽는 중...'), info['name'], len(urls))
if cw:
cw.setTitle(s)

View File

@ -12,7 +12,7 @@ def read_soup(url):
return downloader.read_soup(url)
@Downloader.register
class Downloader_yandere(Downloader):
type = 'yande.re'
URLS = ['yande.re']

View File

@ -6,7 +6,7 @@ from utils import LazyUrl, get_ext, Downloader, format_filename, clean_title
from io import BytesIO
@Downloader.register
class Downloader_youku(Downloader):
type = 'youku'
single = True
@ -21,10 +21,10 @@ class Downloader_youku(Downloader):
self.title = video.title
class Video(object):
_url = None
def __init__(self, url, cw=None):
self.url = LazyUrl(url, self.get, self)
self.cw = cw
@ -32,7 +32,7 @@ class Video(object):
def get(self, url):
if self._url:
return self._url
ydl = ytdl.YoutubeDL(cw=self.cw)
info = ydl.extract_info(url)
@ -57,6 +57,5 @@ class Video(object):
self.filename = format_filename(self.title, info['id'], '.mp4')
self._url = url_video
return self._url
return self._url

View File

@ -9,7 +9,6 @@ import ytdl
@Downloader.register
class Downloader_youporn(Downloader):
type = 'youporn'
single = True
@ -42,7 +41,7 @@ class Video(object):
f = info['formats'][-1]
url_video = f['url']
self.url = LazyUrl(url, lambda _: url_video, self)
self.url_thumb = info['thumbnails'][0]['url']
self.thumb = BytesIO()
downloader.download(self.url_thumb, buffer=self.thumb)

View File

@ -16,13 +16,15 @@ import chardet
import os
from random import randrange
import utils
from translator import tr_
from translator import tr, tr_
from datetime import datetime
import threading
from putils import DIR
def print_streams(streams, cw):
print_ = get_print(cw)
for stream in streams:
print_('{}[{}][{}fps][{}{}][{}] {} [{} / {}] ─ {}'.format('LIVE ' if stream.live else '', stream.resolution, stream.fps, stream.abr_str, '(fixed)' if stream.abr_fixed else '', stream.tbr, stream.subtype, stream.video_codec, stream.audio_codec, stream.format))
print_('')
@ -31,7 +33,7 @@ def print_streams(streams, cw):
class Video(object):
_url = None
vcodec = None
def __init__(self, url, type='video', only_mp4=False, audio_included=False, max_res=None, max_abr=None, cw=None):
self.type = type
self.only_mp4 = only_mp4
@ -41,11 +43,11 @@ class Video(object):
self.cw = cw
self.url = LazyUrl(url, self.get, self, pp=self.pp)
self.exec_queue = cw.exec_queue if cw else None#
def get(self, url, force=False):
if self._url:
return self._url
type = self.type
only_mp4 = self.only_mp4
audio_included = self.audio_included
@ -72,7 +74,7 @@ class Video(object):
streams = yt.streams.all()
print_streams(streams, cw)
#3528
time = datetime.strptime(yt.info['upload_date'], '%Y%m%d')
self.utime = (time-datetime(1970,1,1)).total_seconds()
@ -145,7 +147,7 @@ class Video(object):
#print(foo)
print_('# stream_final {} {} {} {} {} {}fps'.format(stream, stream.format, stream.resolution, stream.subtype, stream.audio_codec, stream.fps))
stream_final = stream
ok = downloader.ok_url(stream_final.url, referer=url) if isinstance(stream_final.url, str) else True
if ok:
break
@ -161,7 +163,7 @@ class Video(object):
## if stream.video_codec and stream_final.video_codec.lower().startswith('av'):
## self.vcodec = 'h264'
self.yt = yt
self.id = yt.video_id
self.stream = stream
@ -236,7 +238,7 @@ class Video(object):
#title = soup.title.text.replace('- YouTube', '').strip()
self.title = title
ext = '.' + self.stream.subtype
self.filename = self.filename0 = format_filename(title, self.id, ext)
self.filename = self.filename0 = format_filename(title, self.id, ext, artist=self.username) #4953
if type == 'audio':
self.filename = f'{uuid()}_audio.tmp' #4776
@ -247,6 +249,21 @@ class Video(object):
print_('Subtype: {}'.format(stream.subtype))
print_('FPS: {}\n'.format(stream.fps))
if self.audio is not None: #5015
def f(audio):
print_('Download audio: {}'.format(audio))
path = os.path.join(DIR, f'{uuid()}_a.tmp')
if cw is not None:
cw.trash_can.append(path)
if constants.FAST:
downloader_v3.download(audio, chunk=1024*1024, n_threads=2, outdir=os.path.dirname(path), fileName=os.path.basename(path), customWidget=cw, overwrite=True)
else:
downloader.download(audio, outdir=os.path.dirname(path), fileName=os.path.basename(path), customWidget=cw, overwrite=True)
self.audio_path = path
print_('audio done')
self.thread_audio = threading.Thread(target=f, args=(self.audio,), daemon=True)
self.thread_audio.start()
return self._url
def pp(self, filename):
@ -257,20 +274,12 @@ class Video(object):
if not os.path.isfile(filename):
print('no file: {}'.format(filename))
return
filename_new = filename
if self.type == 'video' and (self.audio is not None or ext != '.mp4') and not self.stream.live: # UHD or non-mp4
if self.audio is not None: # merge
print_('Download audio: {}'.format(self.audio))
hash = uuid()
path = os.path.join(os.path.dirname(filename), '{}_a.tmp'.format(hash))
if cw is not None:
cw.trash_can.append(path)
if constants.FAST:
downloader_v3.download(self.audio, chunk=1024*1024, n_threads=2, outdir=os.path.dirname(path), fileName=os.path.basename(path), customWidget=cw, overwrite=True)
else:
downloader.download(self.audio, outdir=os.path.dirname(path), fileName=os.path.basename(path), customWidget=cw, overwrite=True)
ext, out = ffmpeg.merge(filename, path, cw=cw, vcodec=self.vcodec)
self.thread_audio.join()
ext, out = ffmpeg.merge(filename, self.audio_path, cw=cw, vcodec=self.vcodec)
#print(out)
name, ext_old = os.path.splitext(filename)
if ext_old.lower() != ext.lower():
@ -294,7 +303,10 @@ class Video(object):
ext = '.mp4' if self.type == 'video' else '.mp3'
filename_new = os.path.join(os.path.dirname(filename_old), os.path.splitext(self.filename0)[0]+ext)
print_(f'rename: {filename_old} -> {filename_new}')
os.rename(filename_old, filename_new)
if filename_old != filename_new:
if os.path.isfile(filename_new):
os.remove(filename_new)
os.rename(filename_old, filename_new)
if self.type == 'audio' and ui_setting.albumArt.isChecked():
try:
@ -309,7 +321,7 @@ class Video(object):
return filename_new
@Downloader.register
class Downloader_youtube(Downloader):
type = 'youtube'
single = True
@ -318,15 +330,22 @@ class Downloader_youtube(Downloader):
lock = True
display_name = 'YouTube'
keep_date = True #3528
__format = {}
def init(self):
ui_setting = self.ui_setting
if self.cw.format:
ext_result = self.cw.format
format = self.cw.format
if format:
if isinstance(format, str):
ext_result = format
elif isinstance(format, dict):
ext_result = format['format']
self.__format = format
else:
raise NotImplementedError(format)
else:
ext_result = compatstr(ui_setting.youtubeCombo_type.currentText()).lower().split()[0]
ext_result = default_option()
self.cw.format = ext_result
if ext_result in ['mp4', 'mkv', '3gp']:
self.yt_type = 'video'
else:
@ -346,16 +365,16 @@ class Downloader_youtube(Downloader):
def key_id(cls, url):
id_ = re.find(r'youtu.be/([0-9A-Za-z-_]{10,})', url) or re.find(r'[?&]v=([0-9A-Za-z-_]{10,})', url)
return id_ or url
def read(self):
ui_setting = self.ui_setting
cw = self.cw
print_ = get_print(cw)
if self.yt_type == 'video':
res = get_resolution()
res = self.__format.get('res', get_resolution())
info = get_videos(self.url, type=self.yt_type, max_res=res, only_mp4=False, audio_included=not True, cw=cw)
else:
abr = get_abr()
abr = self.__format.get('abr', get_abr())
info = get_videos(self.url, type=self.yt_type, max_abr=abr, cw=cw)
videos = info['videos']
@ -384,7 +403,7 @@ class Downloader_youtube(Downloader):
self.title = video.title
if video.stream.live:
self.lock = False
self.artist = video.username
self.setIcon(video.thumb)
@ -395,13 +414,13 @@ def int_(x):
except:
return 0
@try_n(2, sleep=1)
def get_videos(url, type='video', only_mp4=False, audio_included=False, max_res=None, max_abr=None, cw=None):
info = {}
n = get_max_range(cw)
if '/channel/' in url or '/user/' in url or '/c/' in url:
info = read_channel(url, n=n, cw=cw)
info['type'] = 'channel'
@ -435,7 +454,7 @@ def read_playlist(url, n, cw=None):
if '/{}/'.format(header) in url.lower():
username = re.find(r'/{}/([^/\?]+)'.format(header), url, re.IGNORECASE)
url = urljoin(url, '/{}/{}/videos'.format(header, username))
options = {
'extract_flat': True,
'playlistend': n,
@ -463,15 +482,90 @@ def read_playlist(url, n, cw=None):
import selector
@selector.register('youtube')
def select():
from Qt import Qt, QDialog, QFormLayout, QLabel, QComboBox, QWidget, QVBoxLayout, QDialogButtonBox
if utils.ui_setting.askYoutube.isChecked():
value = utils.messageBox(tr_('Youtube format?'), icon=utils.QMessageBox.Question, buttons=[tr_('MP4 (동영상)'), tr_('MP3 (음원)')])
format = ['mp4', 'mp3'][value]
win = QDialog(constants.mainWindow)
win.setWindowTitle('Youtube format')
utils.windows.append(win)
layout = QFormLayout(win)
youtubeCombo_type = QComboBox()
layout.addRow('파일 형식', youtubeCombo_type)
for i in range(utils.ui_setting.youtubeCombo_type.count()):
youtubeCombo_type.addItem(utils.ui_setting.youtubeCombo_type.itemText(i))
youtubeCombo_type.setCurrentIndex(utils.ui_setting.youtubeCombo_type.currentIndex())
youtubeLabel_res = QLabel('해상도')
youtubeCombo_res = QComboBox()
for i in range(utils.ui_setting.youtubeCombo_res.count()):
youtubeCombo_res.addItem(utils.ui_setting.youtubeCombo_res.itemText(i))
youtubeCombo_res.setCurrentIndex(utils.ui_setting.youtubeCombo_res.currentIndex())
youtubeLabel_abr = QLabel('음질')
youtubeCombo_abr = QComboBox()
for i in range(utils.ui_setting.youtubeCombo_abr.count()):
youtubeCombo_abr.addItem(utils.ui_setting.youtubeCombo_abr.itemText(i))
youtubeCombo_abr.setCurrentIndex(utils.ui_setting.youtubeCombo_abr.currentIndex())
aa = QWidget()
a = QVBoxLayout(aa)
a.setContentsMargins(0,0,0,0)
a.addWidget(youtubeLabel_res)
a.addWidget(youtubeLabel_abr)
bb = QWidget()
b = QVBoxLayout(bb)
b.setContentsMargins(0,0,0,0)
b.addWidget(youtubeCombo_res)
b.addWidget(youtubeCombo_abr)
layout.addRow(aa, bb)
def currentIndexChanged(index):
text_type = compatstr(youtubeCombo_type.currentText())
print(text_type)
if tr_('동영상') in text_type:
youtubeLabel_abr.hide()
youtubeCombo_abr.hide()
youtubeLabel_res.show()
youtubeCombo_res.show()
elif tr_('음원') in text_type:
youtubeLabel_res.hide()
youtubeCombo_res.hide()
youtubeLabel_abr.show()
youtubeCombo_abr.show()
youtubeCombo_type.currentIndexChanged.connect(currentIndexChanged)
youtubeCombo_type.currentIndexChanged.emit(youtubeCombo_type.currentIndex())
buttonBox = QDialogButtonBox()
layout.addWidget(buttonBox)
buttonBox.setOrientation(Qt.Horizontal)
buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok)
buttonBox.accepted.connect(win.accept)
buttonBox.rejected.connect(win.reject)
tr(win)
win.setWindowOpacity(constants.opacity_max)
try:
res = win.exec_()
utils.log(f'youtube.select.res: {res}')
if not res:
return selector.Cancel
utils.windows.remove(win)
format = {}
format['format'] = compatstr(youtubeCombo_type.currentText()).lower().split()[0]
format['res'] = get_resolution(compatstr(youtubeCombo_res.currentText()))
format['abr'] = get_abr(compatstr(youtubeCombo_abr.currentText()))
finally:
win.deleteLater()
return format
@selector.options('youtube')
def options():
def options(urls):
return [
{'text': 'MP4 (동영상)', 'format': 'mp4'},
{'text': 'MP3 (음원)', 'format': 'mp3'},
]
@selector.default_option('youtube')
def default_option():
return compatstr(utils.ui_setting.youtubeCombo_type.currentText()).lower().split()[0]

View File

@ -222,17 +222,17 @@ Click thumbnail</li>
<li>데이터를 다시 다운로드할 때마다 서버에서 최신 데이터를 가져옵니다.</li>
</ul>
<h3>스크립트</h3>
<h3>Scripts</h3>
<ul>
<li>메뉴 - 스크립트 가져오기...</li>
<li>파이썬 스크립트를 실행합니다.</li>
<li>Tools - Import script...</li>
<li>Execute python script.</li>
<li>다운로드 스크립트를 직접 만들어 추가하는 등 다양한 작업을 할 수 있습니다.</li>
<li>스크립트 파일 (*.hds) 은 텍스트에디터(메모장 등)로 수정할 수 있습니다.</li>
<li>스크립트 파일들을 프로그램에 드래그 &amp; 드랍해서 실행시킬 수도 있습니다.</li>
<li>실행파일 경로에 scripts 폴더 만들고 스크립트 파일 (*.hds) 넣으면 시작할 때 자동으로 실행합니다.</li>
<li>스크립트 다운로드 :<br>
<li>Download scripts :<br>
<a href="https://github.com/KurtBestor/Hitomi-Downloader/wiki/Scripts">https://github.com/KurtBestor/Hitomi-Downloader/wiki/Scripts</a></li>
<li>스크립트 작성 방법 :<br>
<li>How to write a script :<br>
<a href="https://github.com/KurtBestor/Hitomi-Downloader/wiki/How-to-write-a-script">https://github.com/KurtBestor/Hitomi-Downloader/wiki/How-to-write-a-script</a></li>
</ul>
@ -248,16 +248,24 @@ Click thumbnail</li>
<li>Cookies exported from browser extensions can be loaded. (Netscape HTTP Cookie File)</li>
<li>Using these cookies, Hitomi Downloader can access login-required pages.</li>
<li>Chrome extension: <a href="https://chrome.google.com/webstore/detail/cookiestxt/njabckikapfpffapmjgojcnbfjonfjfg">cookies.txt</a>, <a href="https://chrome.google.com/webstore/detail/get-cookiestxt/bgaddhkoddajcdgocldbbfleckgcbcid">Get cookies.txt</a></li>
<li>Firefox extension: <a href="https://addons.mozilla.org/en-US/firefox/addon/cookies-txt/">cookies.txt</a>
<li>Firefox extension: <a href="https://addons.mozilla.org/en-US/firefox/addon/cookies-txt/">cookies.txt</a></li>
<li>Edge extension: <a href="https://microsoftedge.microsoft.com/addons/detail/get-cookiestxt/helleheikohejgehaknifdkcfcmceeip">Get cookies.txt</a></li>
<li>Expired cookies are shown in gray.</li>
</ul>
<h3>Built-in Web browser</h3>
<ul>
<li>Options - Preferences - Advanced Setting - Built-in Web browser - View</li>
<li>Used to read pages that require JS rendering.</li>
<li>You can use this to update cookies.</li>
</ul>
<h3>Chrome Extension</h3>
<ul>
<li><a href="https://github.com/KurtBestor/Hitomi-Downloader/wiki/Chrome-Extension">Hitomi Downloader</a></li>
<li>기능:<br>
확장프로그램이 필요한 사이트의 다운로드<br>
쿠키 업데이트
<li>Functions:<br>
Download sites that require the extension<br>
Update cookies
</ul>
<h3>Save</h3>
@ -272,7 +280,7 @@ Quit without saving</li>
<h3>Shortcuts</h3>
<ul>
<li>Alt + D : Address bar ↔ toggle task list</li>
<li>Ctrl + 1 ~ 7 : 작업에 태그 표시</li>
<li>Ctrl + 1 ~ 7 : Set tags in tasks</li>
<li>Ctrl + Tab : Show/Hide Toolbar</li>
<li>Ctrl + - / + : Thumbnail size adjustment</li>
<li>Ctrl + Scroll : Thumbnail size adjustment</li>

View File

@ -224,7 +224,7 @@ female:maid, -female:schoolgirl uniform</li>
<h3>스크립트</h3>
<ul>
<li>메뉴 - 스크립트 가져오기...</li>
<li>도구 - 스크립트 가져오기...</li>
<li>파이썬 스크립트를 실행합니다.</li>
<li>다운로드 스크립트를 직접 만들어 추가하는 등 다양한 작업을 할 수 있습니다.</li>
<li>스크립트 파일 (*.hds) 은 텍스트에디터(메모장 등)로 수정할 수 있습니다.</li>
@ -248,10 +248,18 @@ female:maid, -female:schoolgirl uniform</li>
<li>브라우저 확장프로그램에서 추출한 쿠키를 불러올 수 있습니다. (Netscape HTTP Cookie File)</li>
<li>이 쿠키를 이용해서 로그인이 필요한 페이지에 접근할 수 있습니다.</li>
<li>크롬 확장프로그램: <a href="https://chrome.google.com/webstore/detail/cookiestxt/njabckikapfpffapmjgojcnbfjonfjfg">cookies.txt</a>, <a href="https://chrome.google.com/webstore/detail/get-cookiestxt/bgaddhkoddajcdgocldbbfleckgcbcid">Get cookies.txt</a></li>
<li>파이어폭스 확장프로그램: <a href="https://addons.mozilla.org/en-US/firefox/addon/cookies-txt/">cookies.txt</a>
<li>파이어폭스 확장프로그램: <a href="https://addons.mozilla.org/en-US/firefox/addon/cookies-txt/">cookies.txt</a></li>
<li>엣지 확장프로그램: <a href="https://microsoftedge.microsoft.com/addons/detail/get-cookiestxt/helleheikohejgehaknifdkcfcmceeip">Get cookies.txt</a></li>
<li>만료된 쿠키는 회색으로 표시됩니다.</li>
</ul>
<h3>내장 웹브라우저</h3>
<ul>
<li>옵션 - 설정 - 고급 설정 - 내장 웹브라우저 - 보기</li>
<li>JS 렌더가 필요한 사이트를 읽는데 주로 사용됩니다.</li>
<li>쿠키를 업데이트하는 데 사용할 수 있습니다.</li>
</ul>
<h3>크롬 확장 프로그램</h3>
<ul>
<li><a href="https://github.com/KurtBestor/Hitomi-Downloader/wiki/Chrome-Extension">Hitomi Downloader</a></li>

View File

@ -139,6 +139,7 @@
"고급 검색": "Advanced search",
"고정": "Pin",
"고정 해제": "Unpin",
"관리자 권한 없이 실행": "Run without administrator privileges",
"그대로": "Normal",
"그룹": "Groups",
"그룹 (Groups)": "Groups",
@ -158,6 +159,7 @@
"깊은 모델": "Deep model",
"끝내기": "Quit",
"날짜": "Date",
"낮음": "Low",
"내보내기": "Export",
"내보내기 실패": "Failed to export",
"내용 보기": "View script",
@ -165,6 +167,7 @@
"내장 이미지 뷰어": "Built-in image viewer",
"녹화 중...": "Recording...",
"녹화 중지": "Stop recording",
"높음": "High",
"다른 동영상 사이트에도 적용됩니다.": "Applies to other video sites also.",
"다시 시작 (&S)": "Restart (&S)",
"다시 시작 실패; 복구됨": "Failed to retry; Reverted",
@ -178,6 +181,7 @@
"다운로드 완료 후 자동 제거": "Remove automatically after complete download",
"다운로드 일시 정지 / 재개": "Download Pause / Resume",
"다운로드가 완료되면 알림": "Show notification when downloading is finished",
"다운로드하지 않음": "Don't download",
"다운로드한 작품만": "Only downloaded galleries",
"다음 검색시 더 빠르게 검색": "Improve performance the next time",
"다크 모드": "Dark mode",
@ -216,6 +220,7 @@
"로컬 파일 감지": "Detect local files",
"로컬 파일로부터 새 작업 만들기": "Create a new task from the local files",
"를 북마크에서 지웠습니다": "is removed from bookmarks",
"리트윗 포함": "Include retweets",
"릴리즈 노트": "Release note",
"링크 열기": "Open link",
"링크 주소 복사": "Copy link",
@ -246,6 +251,7 @@
"변경할 최소 크기": "Minimum size",
"변환 중...": "Converting...",
"보기": "View",
"보통": "Normal",
"부팅 시 실행": "Start at boot",
"북마크": "Bookmarks",
"북마크 가져오기": "Import Bookmarks",
@ -295,6 +301,7 @@
"설명": "Comment",
"설정": "Preferences",
"설정 (Preferences)": "Preferences",
"설정을 따름": "Follow preferences",
"성공: {}\n실패: {}": "Success: {}\nFailed: {}",
"수동": "Manual",
"수동 업데이트": "Manual",
@ -308,8 +315,12 @@
"스크립트": "Script",
"스크립트 가져오기": "Import script",
"스크립트를 실행하시겠습니까?": "Are you sure you want to run this script?",
"스크립트를 읽는 중 오류가 발생했습니다": "Error occurred while reading the script",
"스토리 읽는 중...": "Reading stories...",
"스토리 포함": "Include stories",
"시딩": "Seeding",
"시딩 중지": "Stop seeding",
"시딩 하지 않음": "No seeding",
"시리즈": "Series",
"시리즈 (Series)": "Series",
"시리즈가 없습니다": "No series",
@ -339,6 +350,7 @@
"업데이트": "Update",
"업데이트 중...": "Updating...",
"업데이트 체크 중...": "Checking for updates...",
"업로드": "Upload",
"연결 프로그램 변경": "Choose the program to use to open",
"열기": "Open",
"예(&Y)": "&Yes",
@ -347,13 +359,14 @@
"올바르지 않은 범위입니다": "Invalid range",
"올바르지 않은 주소입니다": "Invalid URL",
"올바르지 않은 형식의 검색 필터입니다": "Invalid syntax error with filter",
"올바르지 않은 형식의 스크립트입니다": "Invalid format script",
"옵션": "Options",
"옵션 (Options)": "Options",
"완료": "Finished",
"완료된 작업 모두 제거": "Remove all complete tasks",
"완료됨으로 표시": "Mark as complete",
"완전 삭제": "Delete permanently",
"우선순위": "priority",
"우선순위": "Priority",
"움짤": "Ugoira",
"움짤 변환...": "Convert Ugoira...",
"원본": "Original",
@ -376,6 +389,8 @@
"이름 변경": "Rename",
"이름을 얻는 도중 실패했습니다": "Failed to read name",
"이미 다운로드한 작품 제외": "Exclude already downloaded galleries",
"이미 추가한 작업입니다. 다시 다운로드하시겠습니까?": "This task already in the list. Do you want to download it again?",
"이미 추가한 플러그인입니다": "This plugin already in the list",
"이미 포함하는 그룹이 있습니다.": "There is already a group that includes tasks.",
"이미지": "Image",
"이미지 정보 캐시": "Cache image infos",
@ -404,6 +419,7 @@
"작업": "Tasks",
"작업 개수": "Number of tasks",
"작업 수정": "Edit task",
"작업 수정...": "Edit task...",
"작업 왼쪽에서 순서 변경": "Reorder on the left side",
"작업 용량": "File size of tasks",
"작업 정보": "Task info",
@ -455,6 +471,7 @@
"종료": "Quit",
"종료 중...": "Quit...",
"좌우로 끌어서 1 씩 증감\n상하로 끌어서 100 씩 증감": "Drag horizontally: ±1\nDrag vertically: ±100",
"주소": "Address",
"주소 or 갤러리 넘버": "URL or gallery id",
"주소를 입력해주세요": "Please type some URLs",
"중간": "Medium",
@ -465,6 +482,7 @@
"지원하는 사이트:": "Supported sites:",
"지정한 페이지만 다운로드합니다.": "Download selected pages only.",
"직접 다운로드": "Direct download",
"진행도": "Progress",
"참고 작품: {} 개": "Reference: {} galleries",
"참고해주세요: 도움말 - 사용법 (F1) - 쿠키 불러오기": "Please see: Help - How to use (F1) - Load cookies",
"창 보이기 / 숨기기": "Show / Hide windows",
@ -476,6 +494,7 @@
"초기화": "Reset",
"최대 다운로드 속도": "Maximum download speed",
"최대 동시 작업": "Maximum parallel tasks",
"최대 페이지 제한": "Maximum page limit",
"최소화 버튼으로 트레이로 최소화": "Minimize button minimizes to system tray",
"추가한 날짜": "Date added",
"취소": "Cancel",
@ -499,6 +518,7 @@
"크기 조절": "Resize",
"크기 조절...": "Resizing...",
"크롬 확장프로그램 연동에 실패했습니다.": "Chrome extension link failed.",
"클라이언트": "Clilent",
"클래식": "Classic",
"클립보드에서 자동 추가": "Clipboard monitor",
"타입": "Types",
@ -509,6 +529,7 @@
"태그 (Tags)": "Tags",
"태그 :": "Tag :",
"태그 설정": "Tag setting",
"태그 수정": "Edit tags",
"태그 없음": "No tags",
"테마": "Theme",
"토렌트 추가": "Add Torrent",
@ -521,12 +542,13 @@
"트레이로 최소화": "Minimize to system tray",
"트레이에서 알림 보이기": "Show notifications in tray",
"파일": "File",
"파일 목록": "File list",
"파일 삭제": "Delete files",
"파일 삭제 (&X)": "Delete files (&X)",
"파일 수": "Number of files",
"파일 스캔 중": "Scanning files",
"파일 유형 제외": "Exclude file types",
"파일 크기": "File size",
"파일 크기": "Filesize",
"파일 형식": "Format",
"파일 형식 물어보기": "Ask the format",
"파일명": "Filename",
@ -560,6 +582,8 @@
"표지 검색...": "Search cover...",
"프로그램 패스워드": "Program password",
"프록시": "Proxy",
"플러그인": "Plugins",
"플러그인을 추가하시겠습니까?": "Are you sure you want to add this plugin?",
"플레이리스트 파일에 번호 매기기": "Numerate files in playlists",
"플레이리스트 한 폴더에 다운로드": "Download playlist to one folder",
"플로팅 미리보기": "Floating preview",

View File

@ -139,6 +139,7 @@
"고급 검색": "Búsqueda avanzada",
"고정": "Pin",
"고정 해제": "Unpin",
"관리자 권한 없이 실행": "Run without administrator privileges",
"그대로": "Normal",
"그룹": "Grupos",
"그룹 (Groups)": "Grupos",
@ -158,6 +159,7 @@
"깊은 모델": "Deep model",
"끝내기": "Salir",
"날짜": "Fecha",
"낮음": "Low",
"내보내기": "Exportación",
"내보내기 실패": "Error al exportar",
"내용 보기": "Ver el script",
@ -165,6 +167,7 @@
"내장 이미지 뷰어": "Visor de imagen integrado",
"녹화 중...": "Recording...",
"녹화 중지": "Stop recording",
"높음": "High",
"다른 동영상 사이트에도 적용됩니다.": "También se aplica a otros sitios de video.",
"다시 시작 (&S)": "Reiniciar (&S)",
"다시 시작 실패; 복구됨": "Failed to retry; Reverted",
@ -178,6 +181,7 @@
"다운로드 완료 후 자동 제거": "Eliminar automáticamente después de completar la descarga",
"다운로드 일시 정지 / 재개": "Descargar Pausa / Retomar",
"다운로드가 완료되면 알림": "Visualizar notificación cuando se complete la descarga",
"다운로드하지 않음": "Don't download",
"다운로드한 작품만": "Only downloaded galleries",
"다음 검색시 더 빠르게 검색": "Improve performance the next time",
"다크 모드": "Dark mode",
@ -216,6 +220,7 @@
"로컬 파일 감지": "Detect local files",
"로컬 파일로부터 새 작업 만들기": "Create a new task from the local files",
"를 북마크에서 지웠습니다": "se elimina de favoritos",
"리트윗 포함": "Include retweets",
"릴리즈 노트": "Notes de la versió",
"링크 열기": "Abrir enlace",
"링크 주소 복사": "Copia la URL",
@ -246,6 +251,7 @@
"변경할 최소 크기": "Tamaño mínimo",
"변환 중...": "Converting...",
"보기": "Visualizar",
"보통": "Normal",
"부팅 시 실행": "Iniciar automáticamente al inicio",
"북마크": "Favoritos",
"북마크 가져오기": "Importar favoritos",
@ -295,6 +301,7 @@
"설명": "Comentario",
"설정": "Preferencias",
"설정 (Preferences)": "Opciones",
"설정을 따름": "Follow preferences",
"성공: {}\n실패: {}": "Éxito:{}\nFracaso:{}",
"수동": "Manual",
"수동 업데이트": "Manual",
@ -308,8 +315,12 @@
"스크립트": "Guion",
"스크립트 가져오기": "Importar script",
"스크립트를 실행하시겠습니까?": "¿Seguro que quieres ejecutar este script?",
"스크립트를 읽는 중 오류가 발생했습니다": "Error occurred while reading the script",
"스토리 읽는 중...": "Reading stories...",
"스토리 포함": "Include stories",
"시딩": "Seeding",
"시딩 중지": "Stop seeding",
"시딩 하지 않음": "No seeding",
"시리즈": "Serie",
"시리즈 (Series)": "Serie",
"시리즈가 없습니다": "No series",
@ -339,6 +350,7 @@
"업데이트": "Actualizado",
"업데이트 중...": "Actualización...",
"업데이트 체크 중...": "Buscar actualizaciones...",
"업로드": "Upload",
"연결 프로그램 변경": "Elegir el programa a usar para abrir",
"열기": "Open",
"예(&Y)": "&Sí",
@ -347,6 +359,7 @@
"올바르지 않은 범위입니다": "Rango inválido",
"올바르지 않은 주소입니다": "URL no válida",
"올바르지 않은 형식의 검색 필터입니다": "Error de sintaxis no válido con filtro",
"올바르지 않은 형식의 스크립트입니다": "Invalid format script",
"옵션": "Opciones",
"옵션 (Options)": "Opciones",
"완료": "Terminado",
@ -376,6 +389,8 @@
"이름 변경": "Cambiar nombre",
"이름을 얻는 도중 실패했습니다": "No se pudo leer el nombre",
"이미 다운로드한 작품 제외": "Exclude already downloaded galleries",
"이미 추가한 작업입니다. 다시 다운로드하시겠습니까?": "This task already in the list. Do you want to download it again?",
"이미 추가한 플러그인입니다": "This plugin already in the list",
"이미 포함하는 그룹이 있습니다.": "There is already a group that includes tasks.",
"이미지": "Imagen",
"이미지 정보 캐시": "Cache image infos",
@ -404,6 +419,7 @@
"작업": "Tareas",
"작업 개수": "Número de tareas",
"작업 수정": "Edit task",
"작업 수정...": "Edit task...",
"작업 왼쪽에서 순서 변경": "Reorder on the left side",
"작업 용량": "Tamaño de archivo de tarea",
"작업 정보": "Información del trabajo",
@ -455,6 +471,7 @@
"종료": "Salir",
"종료 중...": "Salir...",
"좌우로 끌어서 1 씩 증감\n상하로 끌어서 100 씩 증감": "Deslizar horizontalmente: ±1\nDeslizar verticalmente: ±100",
"주소": "Address",
"주소 or 갤러리 넘버": "URL o ID de la galería",
"주소를 입력해주세요": "Por favor, introduzca algunas URL",
"중간": "Medium",
@ -465,6 +482,7 @@
"지원하는 사이트:": "Sitios soportados:",
"지정한 페이지만 다운로드합니다.": "Descargar solo las páginas seleccionadas.",
"직접 다운로드": "Direct download",
"진행도": "Progress",
"참고 작품: {} 개": "Reference: {} galleries",
"참고해주세요: 도움말 - 사용법 (F1) - 쿠키 불러오기": "Please see: Help - How to use (F1) - Load cookies",
"창 보이기 / 숨기기": "Show / Hide windows",
@ -476,6 +494,7 @@
"초기화": "Reset",
"최대 다운로드 속도": "Velocidad máxima de descarga",
"최대 동시 작업": "Maximum parallel tasks",
"최대 페이지 제한": "Maximum page limit",
"최소화 버튼으로 트레이로 최소화": "El botón Minimizar minimiza en la barra de tareas",
"추가한 날짜": "Fecha de adición",
"취소": "Cancelar",
@ -499,6 +518,7 @@
"크기 조절": "Redimensionar",
"크기 조절...": "Redimensionando...",
"크롬 확장프로그램 연동에 실패했습니다.": "Chrome extension link failed.",
"클라이언트": "Clilent",
"클래식": "Classic",
"클립보드에서 자동 추가": "Portapapeles",
"타입": "Tipos",
@ -509,6 +529,7 @@
"태그 (Tags)": "Tags",
"태그 :": "Tag :",
"태그 설정": "Parámetro Tag",
"태그 수정": "Edit tags",
"태그 없음": "No tags",
"테마": "Theme",
"토렌트 추가": "Add Torrent",
@ -521,6 +542,7 @@
"트레이로 최소화": "Reducir en la barra de tareas",
"트레이에서 알림 보이기": "Visualizar las notificaciones",
"파일": "Archivo",
"파일 목록": "File list",
"파일 삭제": "Eliminar archivos",
"파일 삭제 (&X)": "Eliminar archivos (&X)",
"파일 수": "Número de archivos",
@ -560,6 +582,8 @@
"표지 검색...": "Busque cubierta...",
"프로그램 패스워드": "Program password",
"프록시": "Proxy",
"플러그인": "Plugins",
"플러그인을 추가하시겠습니까?": "Are you sure you want to add this plugin?",
"플레이리스트 파일에 번호 매기기": "Numerate files in playlists",
"플레이리스트 한 폴더에 다운로드": "Download playlist to one folder",
"플로팅 미리보기": "Vista previa flotante",

View File

@ -139,6 +139,7 @@
"고급 검색": "Recherche avancée",
"고정": "Épingler",
"고정 해제": "Désépingler",
"관리자 권한 없이 실행": "Run without administrator privileges",
"그대로": "Normal",
"그룹": "Groupes",
"그룹 (Groups)": "Groupes",
@ -158,6 +159,7 @@
"깊은 모델": "Deep model",
"끝내기": "Quitter",
"날짜": "Date",
"낮음": "Low",
"내보내기": "Exportation",
"내보내기 실패": "Échec de l'exportation",
"내용 보기": "Voir le script",
@ -165,6 +167,7 @@
"내장 이미지 뷰어": "Visionneuse d'images intégrée",
"녹화 중...": "Recording...",
"녹화 중지": "Stop recording",
"높음": "High",
"다른 동영상 사이트에도 적용됩니다.": "S'applique également à d'autres sites vidéos.",
"다시 시작 (&S)": "Redémarrer (&S)",
"다시 시작 실패; 복구됨": "Erreur lors du redémarrage; Retour à l'état précédent",
@ -178,6 +181,7 @@
"다운로드 완료 후 자동 제거": "Supprimer automatiquement après la complétion du téléchargement",
"다운로드 일시 정지 / 재개": "Télécharger Pause / Reprendre",
"다운로드가 완료되면 알림": "Afficher la notification lorsque le téléchargement est terminé",
"다운로드하지 않음": "Don't download",
"다운로드한 작품만": "Télécharger uniquement les galeries",
"다음 검색시 더 빠르게 검색": "Améliore la performance la prochaine fois",
"다크 모드": "Mode sombre",
@ -216,6 +220,7 @@
"로컬 파일 감지": "Detect local files",
"로컬 파일로부터 새 작업 만들기": "Créer une nouvelle tâche depuis les fichiers locaux",
"를 북마크에서 지웠습니다": "est supprimé des favoris",
"리트윗 포함": "Include retweets",
"릴리즈 노트": "Notes de version",
"링크 열기": "Ouvrir le lien",
"링크 주소 복사": "Copier le lien",
@ -246,6 +251,7 @@
"변경할 최소 크기": "Taille minimum",
"변환 중...": "Conversion...",
"보기": "Visualiser",
"보통": "Normal",
"부팅 시 실행": "Démarrer automatiquement au démarrage",
"북마크": "Favoris",
"북마크 가져오기": "Importer des favoris",
@ -295,6 +301,7 @@
"설명": "Commentaire",
"설정": "Paramètres",
"설정 (Preferences)": "Paramètres",
"설정을 따름": "Follow preferences",
"성공: {}\n실패: {}": "Succès : {}\nÉchec : {}",
"수동": "Manuel",
"수동 업데이트": "Manuel",
@ -308,8 +315,12 @@
"스크립트": "Script",
"스크립트 가져오기": "Script d'importation",
"스크립트를 실행하시겠습니까?": "Êtes-vous certain de vouloir exécuter ce script ?",
"스크립트를 읽는 중 오류가 발생했습니다": "Error occurred while reading the script",
"스토리 읽는 중...": "Lecture des stories...",
"스토리 포함": "Inclure les stories",
"시딩": "Seeding",
"시딩 중지": "Stop seeding",
"시딩 하지 않음": "No seeding",
"시리즈": "Séries",
"시리즈 (Series)": "Séries",
"시리즈가 없습니다": "Aucune séries",
@ -339,6 +350,7 @@
"업데이트": "Mise à jour",
"업데이트 중...": "Mise à jour...",
"업데이트 체크 중...": "Vérification des mises à jour...",
"업로드": "Upload",
"연결 프로그램 변경": "Choisir le programme à utiliser pour ouvrir",
"열기": "Open",
"예(&Y)": "&Oui",
@ -347,6 +359,7 @@
"올바르지 않은 범위입니다": "Plage invalide",
"올바르지 않은 주소입니다": "URL invalide",
"올바르지 않은 형식의 검색 필터입니다": "Erreur de syntaxe non valide avec le filtre",
"올바르지 않은 형식의 스크립트입니다": "Invalid format script",
"옵션": "Options",
"옵션 (Options)": "Options",
"완료": "Terminé",
@ -376,6 +389,8 @@
"이름 변경": "Renommer",
"이름을 얻는 도중 실패했습니다": "Impossible de lire le nom",
"이미 다운로드한 작품 제외": "Exclude already downloaded galleries",
"이미 추가한 작업입니다. 다시 다운로드하시겠습니까?": "This task already in the list. Do you want to download it again?",
"이미 추가한 플러그인입니다": "This plugin already in the list",
"이미 포함하는 그룹이 있습니다.": "There is already a group that includes tasks.",
"이미지": "Image",
"이미지 정보 캐시": "Cache image infos",
@ -404,6 +419,7 @@
"작업": "Tâches",
"작업 개수": "Nombre de tâches",
"작업 수정": "Modifier la tâche",
"작업 수정...": "Edit task...",
"작업 왼쪽에서 순서 변경": "Reorder on the left side",
"작업 용량": "Taille des fichiers",
"작업 정보": "Informations de la tâche",
@ -455,6 +471,7 @@
"종료": "Quitter",
"종료 중...": "Quitter...",
"좌우로 끌어서 1 씩 증감\n상하로 끌어서 100 씩 증감": "Glissez horizontalement : ±1\nGlissez verticalement : ±100",
"주소": "Address",
"주소 or 갤러리 넘버": "URL ou ID de la galerie",
"주소를 입력해주세요": "Veuillez saisir quelques URLs",
"중간": "Medium",
@ -465,6 +482,7 @@
"지원하는 사이트:": "Sites pris en charge :",
"지정한 페이지만 다운로드합니다.": "Télécharger les pages sélectionnées uniquement.",
"직접 다운로드": "Direct download",
"진행도": "Progress",
"참고 작품: {} 개": "Reference: {} galleries",
"참고해주세요: 도움말 - 사용법 (F1) - 쿠키 불러오기": "Merci de lire :Aide - Comment utiliser (F1) - Charger les cookies",
"창 보이기 / 숨기기": "Afficher / Cacher la fenêtre",
@ -476,6 +494,7 @@
"초기화": "Reset",
"최대 다운로드 속도": "Vitesse maximale de téléchargement",
"최대 동시 작업": "Maximum parallel tasks",
"최대 페이지 제한": "Maximum page limit",
"최소화 버튼으로 트레이로 최소화": "Le bouton Réduire minimise dans la barre des tâches",
"추가한 날짜": "Date d'ajout",
"취소": "Annuler",
@ -499,6 +518,7 @@
"크기 조절": "Redimensionner",
"크기 조절...": "Redimensionnement...",
"크롬 확장프로그램 연동에 실패했습니다.": "Erreur du lien vers l'extension Chrome.",
"클라이언트": "Clilent",
"클래식": "Classic",
"클립보드에서 자동 추가": "Presse-papiers",
"타입": "Types",
@ -509,6 +529,7 @@
"태그 (Tags)": "Tags",
"태그 :": "Tag :",
"태그 설정": "Paramètres des tags",
"태그 수정": "Edit tags",
"태그 없음": "No tags",
"테마": "Theme",
"토렌트 추가": "Ajout un torrent",
@ -521,6 +542,7 @@
"트레이로 최소화": "Réduire dans la barre des tâches",
"트레이에서 알림 보이기": "Afficher les notifications",
"파일": "Fichier",
"파일 목록": "File list",
"파일 삭제": "Supprimer les fichiers",
"파일 삭제 (&X)": "Supprimer les fichiers (&X)",
"파일 수": "Nombre de fichiers",
@ -560,6 +582,8 @@
"표지 검색...": "Rechercher une couverture...",
"프로그램 패스워드": "Mot de passe du programme",
"프록시": "Proxy",
"플러그인": "Plugins",
"플러그인을 추가하시겠습니까?": "Are you sure you want to add this plugin?",
"플레이리스트 파일에 번호 매기기": "Numerate files in playlists",
"플레이리스트 한 폴더에 다운로드": "Télécharger la playlist dans un dossier",
"플로팅 미리보기": "Aperçu flottant",

View File

@ -139,6 +139,7 @@
"고급 검색": "詳細検索",
"고정": "固定",
"고정 해제": "固定解除",
"관리자 권한 없이 실행": "Run without administrator privileges",
"그대로": "普通",
"그룹": "グループ",
"그룹 (Groups)": "グループ",
@ -158,6 +159,7 @@
"깊은 모델": "深層モデル",
"끝내기": "終了",
"날짜": "日付",
"낮음": "Low",
"내보내기": "エクスポート",
"내보내기 실패": "エクスポートに失敗しました",
"내용 보기": "スクリプトの表示",
@ -165,6 +167,7 @@
"내장 이미지 뷰어": "内蔵の画像ビューア",
"녹화 중...": "レコーディング...",
"녹화 중지": "レコーディングを停止する",
"높음": "High",
"다른 동영상 사이트에도 적용됩니다.": "他の動画サイトにも適用されます。",
"다시 시작 (&S)": "再起動 (&S)",
"다시 시작 실패; 복구됨": "再試行に失敗したため、元に戻しました。",
@ -178,6 +181,7 @@
"다운로드 완료 후 자동 제거": "ダウンロードが完了時に自動削除",
"다운로드 일시 정지 / 재개": "ダウンロード一時停止/再開",
"다운로드가 완료되면 알림": "ダウンロードが終了時に通知を表示する",
"다운로드하지 않음": "Don't download",
"다운로드한 작품만": "ダウンロード済みのギャラリーのみ",
"다음 검색시 더 빠르게 검색": "次回の検索でより早く検索",
"다크 모드": "ダークモード",
@ -216,6 +220,7 @@
"로컬 파일 감지": "ローカルファイルの検出",
"로컬 파일로부터 새 작업 만들기": "ローカルファイルから新しいタスクを作成する",
"를 북마크에서 지웠습니다": "をブックマークから消去しました",
"리트윗 포함": "Include retweets",
"릴리즈 노트": "リリースノート",
"링크 열기": "リンクを開く",
"링크 주소 복사": "リンクをコピー",
@ -246,6 +251,7 @@
"변경할 최소 크기": "最小サイズ",
"변환 중...": "変換...",
"보기": "表示",
"보통": "Normal",
"부팅 시 실행": "起動時に開始",
"북마크": "ブックマーク",
"북마크 가져오기": "ブックマークのインポート",
@ -295,6 +301,7 @@
"설명": "コメント",
"설정": "環境設定",
"설정 (Preferences)": "環境設定",
"설정을 따름": "Follow preferences",
"성공: {}\n실패: {}": "成功: {}\n失敗: {}",
"수동": "マニュアル",
"수동 업데이트": "マニュアル",
@ -308,8 +315,12 @@
"스크립트": "スクリプト",
"스크립트 가져오기": "スクリプトインポート",
"스크립트를 실행하시겠습니까?": "このスクリプトを実行してもよろしいですか?",
"스크립트를 읽는 중 오류가 발생했습니다": "Error occurred while reading the script",
"스토리 읽는 중...": "ストーリーを読む...",
"스토리 포함": "ストーリーを含む",
"시딩": "Seeding",
"시딩 중지": "Stop seeding",
"시딩 하지 않음": "No seeding",
"시리즈": "シリーズ",
"시리즈 (Series)": "シリーズ",
"시리즈가 없습니다": "シリーズなし",
@ -339,6 +350,7 @@
"업데이트": "更新",
"업데이트 중...": "更新中...",
"업데이트 체크 중...": "更新を確認しています...",
"업로드": "Upload",
"연결 프로그램 변경": "開くために使用するプログラムを選択します",
"열기": "開く",
"예(&Y)": "&はい",
@ -347,6 +359,7 @@
"올바르지 않은 범위입니다": "無効な範囲",
"올바르지 않은 주소입니다": "無効なURL",
"올바르지 않은 형식의 검색 필터입니다": "フィルターでの無効な構文エラー",
"올바르지 않은 형식의 스크립트입니다": "Invalid format script",
"옵션": "オプション",
"옵션 (Options)": "オプション",
"완료": "終了したタスク",
@ -376,6 +389,8 @@
"이름 변경": "名前を変更",
"이름을 얻는 도중 실패했습니다": "名前の読み取りに失敗しました",
"이미 다운로드한 작품 제외": "ダウンロード済みのギャラリーを除外する",
"이미 추가한 작업입니다. 다시 다운로드하시겠습니까?": "This task already in the list. Do you want to download it again?",
"이미 추가한 플러그인입니다": "This plugin already in the list",
"이미 포함하는 그룹이 있습니다.": "タスクを含むグループはすでに存在します。",
"이미지": "画像",
"이미지 정보 캐시": "画像情報のキャッシュ",
@ -404,6 +419,7 @@
"작업": "タスク",
"작업 개수": "タスクの数",
"작업 수정": "タスクの編集",
"작업 수정...": "Edit task...",
"작업 왼쪽에서 순서 변경": "左側で並び替え",
"작업 용량": "タスクのファイルサイズ",
"작업 정보": "タスク情報",
@ -455,6 +471,7 @@
"종료": "終了",
"종료 중...": "終了中...",
"좌우로 끌어서 1 씩 증감\n상하로 끌어서 100 씩 증감": "水平方向へのドラッグ: ±1\n垂直方向へのドラッグ: ±100",
"주소": "Address",
"주소 or 갤러리 넘버": "URLまたはギャラリーID",
"주소를 입력해주세요": "URLを入力してください。",
"중간": "中",
@ -465,6 +482,7 @@
"지원하는 사이트:": "対応サイト:",
"지정한 페이지만 다운로드합니다.": "選択したページのみをダウンロード",
"직접 다운로드": "直接ダウンロード",
"진행도": "Progress",
"참고 작품: {} 개": "参照: {} ギャラリー",
"참고해주세요: 도움말 - 사용법 (F1) - 쿠키 불러오기": "ヘルプ - 使い方 (F1) - Cookieの読み込みをご覧ください。",
"창 보이기 / 숨기기": "ウィンドウの表示/非表示",
@ -476,6 +494,7 @@
"초기화": "リセット",
"최대 다운로드 속도": "最大ダウンロード速度",
"최대 동시 작업": "最大並列タスク数",
"최대 페이지 제한": "Maximum page limit",
"최소화 버튼으로 트레이로 최소화": "最小化ボタンでシステムトレイに最小化",
"추가한 날짜": "追加された日付",
"취소": "キャンセル",
@ -499,6 +518,7 @@
"크기 조절": "サイズ変更",
"크기 조절...": "サイズ変更...",
"크롬 확장프로그램 연동에 실패했습니다.": "Chrome拡張機能のリンクに失敗しました。",
"클라이언트": "Clilent",
"클래식": "クラシック",
"클립보드에서 자동 추가": "クリップボードモニター",
"타입": "種類",
@ -509,6 +529,7 @@
"태그 (Tags)": "タグ",
"태그 :": "タグ :",
"태그 설정": "タグの設定",
"태그 수정": "Edit tags",
"태그 없음": "タグなし",
"테마": "テーマ",
"토렌트 추가": "トレントの追加",
@ -521,6 +542,7 @@
"트레이로 최소화": "システムトレイに最小化",
"트레이에서 알림 보이기": "トレイに通知を表示",
"파일": "ファイル",
"파일 목록": "File list",
"파일 삭제": "ファイルの削除",
"파일 삭제 (&X)": "ファイルの削除(&X)",
"파일 수": "ファイルの数",
@ -560,6 +582,8 @@
"표지 검색...": "カバーを検索...",
"프로그램 패스워드": "プログラムのパスワード",
"프록시": "プロキシ",
"플러그인": "Plugins",
"플러그인을 추가하시겠습니까?": "Are you sure you want to add this plugin?",
"플레이리스트 파일에 번호 매기기": "プレイリストファイルに番号を付ける",
"플레이리스트 한 폴더에 다운로드": "プレイリストを1つのフォルダにダウンロード",
"플로팅 미리보기": "フローティングプレビュー",

View File

@ -139,6 +139,7 @@
"고급 검색": "Zaawansowane wyszukiwanie",
"고정": "Przypnij",
"고정 해제": "Odepnij",
"관리자 권한 없이 실행": "Run without administrator privileges",
"그대로": "Normalny",
"그룹": "Grupy",
"그룹 (Groups)": "Grupy",
@ -158,6 +159,7 @@
"깊은 모델": "Deep model",
"끝내기": "Wyjście",
"날짜": "Data",
"낮음": "Low",
"내보내기": "Eksport",
"내보내기 실패": "Nie udało się wyeksportować",
"내용 보기": "Zobacz skrypt",
@ -165,6 +167,7 @@
"내장 이미지 뷰어": "Wbudowana przeglądarka obrazów",
"녹화 중...": "Nagrywanie...",
"녹화 중지": "Zatrzymaj nagrywanie",
"높음": "High",
"다른 동영상 사이트에도 적용됩니다.": "Dotyczy również innych serwisów wideo.",
"다시 시작 (&S)": "Uruchom ponownie (&S)",
"다시 시작 실패; 복구됨": "Nie udało się ponowić próby; Cofnięto",
@ -178,6 +181,7 @@
"다운로드 완료 후 자동 제거": "Usuń automatycznie po zakończeniu pobierania",
"다운로드 일시 정지 / 재개": "Wstrzymaj pobieranie / Wznów",
"다운로드가 완료되면 알림": "Pokaż powiadomienie po zakończeniu pobierania",
"다운로드하지 않음": "Don't download",
"다운로드한 작품만": "Tylko pobrane galerie",
"다음 검색시 더 빠르게 검색": "Popraw wydajność następnym razem",
"다크 모드": "Tryb ciemny",
@ -216,6 +220,7 @@
"로컬 파일 감지": "Wykryj lokalne pliki",
"로컬 파일로부터 새 작업 만들기": "Utwórz nowe zadanie z lokalnych plików",
"를 북마크에서 지웠습니다": "jest usunięty z zakładek",
"리트윗 포함": "Include retweets",
"릴리즈 노트": "Informacja o wydaniu",
"링크 열기": "Otwórz link",
"링크 주소 복사": "Skopiuj link",
@ -246,6 +251,7 @@
"변경할 최소 크기": "Minimalny rozmiar",
"변환 중...": "Konwersja...",
"보기": "Zobacz",
"보통": "Normal",
"부팅 시 실행": "Uruchom od startu systemu",
"북마크": "Zakładki",
"북마크 가져오기": "Importuj zakładki",
@ -295,6 +301,7 @@
"설명": "Komentarz",
"설정": "Preferencje",
"설정 (Preferences)": "Preferencje",
"설정을 따름": "Follow preferences",
"성공: {}\n실패: {}": "Sukces: {}\nNiepowodzenie: {}",
"수동": "Manualne",
"수동 업데이트": "Manualne",
@ -308,8 +315,12 @@
"스크립트": "Skrypt",
"스크립트 가져오기": "Importuj skrypt",
"스크립트를 실행하시겠습니까?": "Czy na pewno chcesz uruchomić ten skrypt?",
"스크립트를 읽는 중 오류가 발생했습니다": "Error occurred while reading the script",
"스토리 읽는 중...": "Czytanie historii...",
"스토리 포함": "Dołącz relacje",
"시딩": "Seeding",
"시딩 중지": "Stop seeding",
"시딩 하지 않음": "No seeding",
"시리즈": "Seria",
"시리즈 (Series)": "Seria",
"시리즈가 없습니다": "Brak serii",
@ -339,6 +350,7 @@
"업데이트": "Aktualizacja",
"업데이트 중...": "Aktualizacja...",
"업데이트 체크 중...": "Sprawdzanie dostępności aktualizacji...",
"업로드": "Upload",
"연결 프로그램 변경": "Wybierz program, którego chcesz użyć do otwarcia",
"열기": "Otwórz",
"예(&Y)": "&Tak",
@ -347,6 +359,7 @@
"올바르지 않은 범위입니다": "Nieprawidłowy zakres",
"올바르지 않은 주소입니다": "Nieprawidłowy link",
"올바르지 않은 형식의 검색 필터입니다": "Nieprawidłowo sformatowany filtr wyszukiwania",
"올바르지 않은 형식의 스크립트입니다": "Invalid format script",
"옵션": "Opcje",
"옵션 (Options)": "Opcje",
"완료": "Gotowe",
@ -376,6 +389,8 @@
"이름 변경": "Zmień nazwę",
"이름을 얻는 도중 실패했습니다": "Nie udało się odczytać nazwy",
"이미 다운로드한 작품 제외": "Wyklucz już pobrane galerie",
"이미 추가한 작업입니다. 다시 다운로드하시겠습니까?": "This task already in the list. Do you want to download it again?",
"이미 추가한 플러그인입니다": "This plugin already in the list",
"이미 포함하는 그룹이 있습니다.": "Istnieje już grupa, która zawiera zadania.",
"이미지": "Obraz",
"이미지 정보 캐시": "Informacje o pamięci podręcznej obrazu",
@ -404,6 +419,7 @@
"작업": "Zadania",
"작업 개수": "Ilość zadań",
"작업 수정": "Edytuj zadanie",
"작업 수정...": "Edit task...",
"작업 왼쪽에서 순서 변경": "Zmień kolejność po lewej stronie",
"작업 용량": "Rozmiar pliku zadań",
"작업 정보": "Informacje o zadaniu",
@ -455,6 +471,7 @@
"종료": "Wyjście",
"종료 중...": "Wychodzenie...",
"좌우로 끌어서 1 씩 증감\n상하로 끌어서 100 씩 증감": "Przeciągnij poziomo: ±1\nPrzeciągnij pionowo: ±100",
"주소": "Address",
"주소 or 갤러리 넘버": "Adres URL albo ID galerii",
"주소를 입력해주세요": "Proszę wpisz jakieś adresy URL",
"중간": "Średnia",
@ -465,6 +482,7 @@
"지원하는 사이트:": "Obsługiwane witryny:",
"지정한 페이지만 다운로드합니다.": "Pobieraj tylko wybrane strony.",
"직접 다운로드": "Bezpośrednie pobieranie",
"진행도": "Progress",
"참고 작품: {} 개": "Odniesienie: {} galerie",
"참고해주세요: 도움말 - 사용법 (F1) - 쿠키 불러오기": "Proszę zobaczyć: Pomoc - Jak używać (F1) - Załaduj pliki cookie",
"창 보이기 / 숨기기": "Pokaż / Ukryj okna",
@ -476,12 +494,13 @@
"초기화": "Resetuj",
"최대 다운로드 속도": "Maksymalna prędkość pobierania",
"최대 동시 작업": "Maksymalna liczba zadań równoległych",
"최대 페이지 제한": "Maximum page limit",
"최소화 버튼으로 트레이로 최소화": "Przycisk minimalizuj minimalizuje do zasobnika systemowego",
"추가한 날짜": "Data dodania",
"취소": "Anuluj",
"캐릭터": "Postacie",
"캐릭터 (Characters)": "Postacie",
"캐릭터 이름": "Imię postaci",
"캐릭터 이름": "Imię postaci",
"캐릭터가 없습니다": "Brak postaci",
"캐릭터명 복사 (&C)": "Kopiuj imię postaci (&C)",
"코멘트": "Komentarz",
@ -499,6 +518,7 @@
"크기 조절": "Zmień rozmiar",
"크기 조절...": "Zmiana rozmiaru...",
"크롬 확장프로그램 연동에 실패했습니다.": "Połączenie z rozszerzeniem Chrome nie powiodło się.",
"클라이언트": "Clilent",
"클래식": "Klasyczna",
"클립보드에서 자동 추가": "Monitor schowka",
"타입": "Typy",
@ -509,6 +529,7 @@
"태그 (Tags)": "Znaczniki",
"태그 :": "Znacznik :",
"태그 설정": "Ustawienia znacznika",
"태그 수정": "Edit tags",
"태그 없음": "Brak znaczników",
"테마": "Motyw",
"토렌트 추가": "Dodaj torrent",
@ -521,6 +542,7 @@
"트레이로 최소화": "Minimalizuj do zasobnika systemowego",
"트레이에서 알림 보이기": "Wyświetl powiadomienia w zasobniku",
"파일": "Plik",
"파일 목록": "File list",
"파일 삭제": "Usuń pliki",
"파일 삭제 (&X)": "Usuń pliki (&X)",
"파일 수": "Liczba plików",
@ -560,6 +582,8 @@
"표지 검색...": "Szukanie okładki...",
"프로그램 패스워드": "Hasło programu",
"프록시": "Proxy",
"플러그인": "Plugins",
"플러그인을 추가하시겠습니까?": "Are you sure you want to add this plugin?",
"플레이리스트 파일에 번호 매기기": "Numeruj pliki w playlistach",
"플레이리스트 한 폴더에 다운로드": "Pobierz playlistę do jednego folderu",
"플로팅 미리보기": "Pływający podgląd",

View File

@ -139,6 +139,7 @@
"고급 검색": "Busca avançada",
"고정": "Fixar",
"고정 해제": "Desfixar",
"관리자 권한 없이 실행": "Run without administrator privileges",
"그대로": "Normal",
"그룹": "Grupos",
"그룹 (Groups)": "Grupos",
@ -158,6 +159,7 @@
"깊은 모델": "Deep model",
"끝내기": "Sair",
"날짜": "Data",
"낮음": "Low",
"내보내기": "Exportar",
"내보내기 실패": "Falha na exportação",
"내용 보기": "Ver script",
@ -165,6 +167,7 @@
"내장 이미지 뷰어": "Visualizador de imagem acoplado",
"녹화 중...": "Recording...",
"녹화 중지": "Parar gravação",
"높음": "High",
"다른 동영상 사이트에도 적용됩니다.": "Isso também se aplica para outros sites de vídeo.",
"다시 시작 (&S)": "Reinicialização em (&S)",
"다시 시작 실패; 복구됨": "Falha ao tentar novamente; Revertido",
@ -178,6 +181,7 @@
"다운로드 완료 후 자동 제거": "Remover automaticamente após completar o download",
"다운로드 일시 정지 / 재개": "Pausar / Resumir Download",
"다운로드가 완료되면 알림": "Exibir notificação ao terminar o download",
"다운로드하지 않음": "Don't download",
"다운로드한 작품만": "Apenas galerias baixadas",
"다음 검색시 더 빠르게 검색": "Melhora desempenho da próxima vez",
"다크 모드": "Modo noturno",
@ -216,6 +220,7 @@
"로컬 파일 감지": "Detect local files",
"로컬 파일로부터 새 작업 만들기": "Criar uma nova tarefa a partir de arquivo local",
"를 북마크에서 지웠습니다": "removido dos favoritos",
"리트윗 포함": "Include retweets",
"릴리즈 노트": "Nota de atualização",
"링크 열기": "Abrir link",
"링크 주소 복사": "Copiar link",
@ -246,6 +251,7 @@
"변경할 최소 크기": "Tamanho mínimo",
"변환 중...": "Convertendo...",
"보기": "Visualizar",
"보통": "Normal",
"부팅 시 실행": "Iniciar ao ligar a máquina",
"북마크": "Favoritos",
"북마크 가져오기": "Importar favoritos",
@ -295,6 +301,7 @@
"설명": "Comentário",
"설정": "Preferências",
"설정 (Preferences)": "Preferências",
"설정을 따름": "Follow preferences",
"성공: {}\n실패: {}": "Sucesso: {}\nFalho: {}",
"수동": "Manual",
"수동 업데이트": "Atualização Manual",
@ -308,8 +315,12 @@
"스크립트": "Script",
"스크립트 가져오기": "Importar script",
"스크립트를 실행하시겠습니까?": "Você tem certeza que você quer executar esse script?",
"스크립트를 읽는 중 오류가 발생했습니다": "Error occurred while reading the script",
"스토리 읽는 중...": "Lendo stories...",
"스토리 포함": "Incluir stories",
"시딩": "Seeding",
"시딩 중지": "Stop seeding",
"시딩 하지 않음": "No seeding",
"시리즈": "Série",
"시리즈 (Series)": "Série",
"시리즈가 없습니다": "Nenhuma série",
@ -339,6 +350,7 @@
"업데이트": "Atualizar",
"업데이트 중...": "Atualizando...",
"업데이트 체크 중...": "Buscando Atualizações...",
"업로드": "Upload",
"연결 프로그램 변경": "Escolha o programa para usar para abrir",
"열기": "Abrir",
"예(&Y)": "&Sim",
@ -347,6 +359,7 @@
"올바르지 않은 범위입니다": "Intervalo inválido",
"올바르지 않은 주소입니다": "URL Inválida",
"올바르지 않은 형식의 검색 필터입니다": "Filtro de pesquisa formatado de forma inválida",
"올바르지 않은 형식의 스크립트입니다": "Invalid format script",
"옵션": "Opções",
"옵션 (Options)": "Opções",
"완료": "Finalizado",
@ -376,6 +389,8 @@
"이름 변경": "Renomear",
"이름을 얻는 도중 실패했습니다": "Falha ao obter o nome",
"이미 다운로드한 작품 제외": "Ignorando os trabalhos que já foram baixados",
"이미 추가한 작업입니다. 다시 다운로드하시겠습니까?": "This task already in the list. Do you want to download it again?",
"이미 추가한 플러그인입니다": "This plugin already in the list",
"이미 포함하는 그룹이 있습니다.": "Já existe um grupo que contém estas tarefas.",
"이미지": "Imagem",
"이미지 정보 캐시": "Cache de informações de imagem",
@ -404,6 +419,7 @@
"작업": "Tarefas",
"작업 개수": "Número de tarefas",
"작업 수정": "Editar tarefa",
"작업 수정...": "Edit task...",
"작업 왼쪽에서 순서 변경": "Reorganizar à esquerda",
"작업 용량": "Tamanho de arquivo das tarefas",
"작업 정보": "Informações da Tarefa",
@ -455,6 +471,7 @@
"종료": "Sair",
"종료 중...": "Sair...",
"좌우로 끌어서 1 씩 증감\n상하로 끌어서 100 씩 증감": "Arraste para a esquerda para diminuir ou para a direita para aumentar em 1\nArraste para cima para aumentar ou para baixo para diminuir em 100",
"주소": "Address",
"주소 or 갤러리 넘버": "URL ou ID de Galeria",
"주소를 입력해주세요": "Por favor insira algumas URLs",
"중간": "Meio",
@ -465,6 +482,7 @@
"지원하는 사이트:": "Sites suportados:",
"지정한 페이지만 다운로드합니다.": "Baixar apenas págians selecionadas.",
"직접 다운로드": "Download Direto",
"진행도": "Progress",
"참고 작품: {} 개": "Referenciar: {} galerias",
"참고해주세요: 도움말 - 사용법 (F1) - 쿠키 불러오기": "Por favor veja: Ajuda - Como Usar (F1) - Carregar Cookies",
"창 보이기 / 숨기기": "Exibir / Ocultar Janelas",
@ -476,6 +494,7 @@
"초기화": "Resetar",
"최대 다운로드 속도": "Velocidade de download máxima",
"최대 동시 작업": "QUantidade máxima de tarefas paralelas",
"최대 페이지 제한": "Maximum page limit",
"최소화 버튼으로 트레이로 최소화": "Botão de minimizar envia para a bandeja do sistema",
"추가한 날짜": "Data adicionado",
"취소": "Cancelar",
@ -499,6 +518,7 @@
"크기 조절": "Redimensionar",
"크기 조절...": "Redimensionando...",
"크롬 확장프로그램 연동에 실패했습니다.": "Vínculo com a extensão do Chrome falhou.",
"클라이언트": "Clilent",
"클래식": "Clássico",
"클립보드에서 자동 추가": "Adicionar automaticamente da área de transferência",
"타입": "Tipos",
@ -509,6 +529,7 @@
"태그 (Tags)": "Tags",
"태그 :": "Tag :",
"태그 설정": "Configurações de Tag",
"태그 수정": "Edit tags",
"태그 없음": "Nenhuma tag",
"테마": "Tema",
"토렌트 추가": "Adicionar Torrent",
@ -521,6 +542,7 @@
"트레이로 최소화": "Minimizar para a bandeja do sistema",
"트레이에서 알림 보이기": "Mostrar notificações na bandeja",
"파일": "Arquivo",
"파일 목록": "File list",
"파일 삭제": "Deletar arquivos",
"파일 삭제 (&X)": "Deletar arquivos (&X)",
"파일 수": "Númeror de arquivos",
@ -560,6 +582,8 @@
"표지 검색...": "Buscar capa...",
"프로그램 패스워드": "Senha do programa",
"프록시": "Proxy",
"플러그인": "Plugins",
"플러그인을 추가하시겠습니까?": "Are you sure you want to add this plugin?",
"플레이리스트 파일에 번호 매기기": "Enumerar arquivos na playlist",
"플레이리스트 한 폴더에 다운로드": "Baixar a playlist para uma pasta",
"플로팅 미리보기": "Prévia flutuante",

View File

@ -139,6 +139,7 @@
"고급 검색": "Tìm kiếm nâng cao",
"고정": "Ghim",
"고정 해제": "Bỏ ghim",
"관리자 권한 없이 실행": "Run without administrator privileges",
"그대로": "Bình thường",
"그룹": "Nhóm",
"그룹 (Groups)": "Nhóm",
@ -158,6 +159,7 @@
"깊은 모델": "Deep model",
"끝내기": "Thoát",
"날짜": "Ngày",
"낮음": "Low",
"내보내기": "Xuất",
"내보내기 실패": "Xuất thất bại",
"내용 보기": "Xem tập lệnh",
@ -165,6 +167,7 @@
"내장 이미지 뷰어": "Trình xem ảnh đính kèm",
"녹화 중...": "Recording...",
"녹화 중지": "Dừng ghi",
"높음": "High",
"다른 동영상 사이트에도 적용됩니다.": "Áp dụng với các trang video khác.",
"다시 시작 (&S)": "Khởi động lại (&S)",
"다시 시작 실패; 복구됨": "Thử lại thất bại; Hoàn trả hiện trạng",
@ -178,6 +181,7 @@
"다운로드 완료 후 자동 제거": "Tự động gỡ bỏ sau khi hoàn tất tải xuống",
"다운로드 일시 정지 / 재개": "Tạm dừng tại xuống / Tiếp tục",
"다운로드가 완료되면 알림": "Thông báo khi tải xuống thành công",
"다운로드하지 않음": "Don't download",
"다운로드한 작품만": "Chỉ tải xuống thư viện",
"다음 검색시 더 빠르게 검색": "Cải thiện hiệu năng trong lần tới",
"다크 모드": "Chế độ tối",
@ -216,6 +220,7 @@
"로컬 파일 감지": "Detect local files",
"로컬 파일로부터 새 작업 만들기": "Tạo nhiệm vụ mới từ tệp cục bộ",
"를 북마크에서 지웠습니다": "Được gỡ bỏ khỏi danh sách yêu thích",
"리트윗 포함": "Include retweets",
"릴리즈 노트": "Ghi chú khi phát hành",
"링크 열기": "Mở link",
"링크 주소 복사": "Sao chép link",
@ -246,6 +251,7 @@
"변경할 최소 크기": "Kích thước tối thiểu",
"변환 중...": "Đang chuyển đổi...",
"보기": "Xem",
"보통": "Normal",
"부팅 시 실행": "Bắt đầu khi máy khởi động",
"북마크": "Danh sách yêu thích",
"북마크 가져오기": "Nhập danh sách yêu thích",
@ -295,6 +301,7 @@
"설명": "Bình luận",
"설정": "Tùy chỉnh",
"설정 (Preferences)": "Tùy chỉnh",
"설정을 따름": "Follow preferences",
"성공: {}\n실패: {}": "Thành công: {}\nThất bại: {}",
"수동": "Thủ công",
"수동 업데이트": "Thủ công",
@ -308,8 +315,12 @@
"스크립트": "Tập lệnh",
"스크립트 가져오기": "Nạp tập lệnh",
"스크립트를 실행하시겠습니까?": "Bạn có chắc bạn muốn chạy tập lệnh này?",
"스크립트를 읽는 중 오류가 발생했습니다": "Error occurred while reading the script",
"스토리 읽는 중...": "Xem stories...",
"스토리 포함": "Bao gồm cả stories",
"시딩": "Seeding",
"시딩 중지": "Stop seeding",
"시딩 하지 않음": "No seeding",
"시리즈": "Series",
"시리즈 (Series)": "Series",
"시리즈가 없습니다": "Không có series",
@ -339,6 +350,7 @@
"업데이트": "Cập nhập",
"업데이트 중...": "Đang cập nhập...",
"업데이트 체크 중...": "Kiểm tra bản cập nhập...",
"업로드": "Upload",
"연결 프로그램 변경": "Chọn một phần mềm sử dụng đê mở",
"열기": "Mở",
"예(&Y)": "&Đúng",
@ -347,6 +359,7 @@
"올바르지 않은 범위입니다": "Phạm vi không hợp lệ",
"올바르지 않은 주소입니다": "Đường dẫn không hợp lệ",
"올바르지 않은 형식의 검색 필터입니다": "Lỗi cú pháp với bộ lọc",
"올바르지 않은 형식의 스크립트입니다": "Invalid format script",
"옵션": "Lựa chọn",
"옵션 (Options)": "Lựa chọn",
"완료": "Hoàn tất",
@ -376,6 +389,8 @@
"이름 변경": "Đổi tên",
"이름을 얻는 도중 실패했습니다": "Đọc tên thất bại",
"이미 다운로드한 작품 제외": "Loại trừ đã tải xuống thư viện",
"이미 추가한 작업입니다. 다시 다운로드하시겠습니까?": "This task already in the list. Do you want to download it again?",
"이미 추가한 플러그인입니다": "This plugin already in the list",
"이미 포함하는 그룹이 있습니다.": "Có một nhóm đã bao gồm nhiệm vụ.",
"이미지": "Ảnh",
"이미지 정보 캐시": "Bộ nhớ đệm thông tin ảnh",
@ -404,6 +419,7 @@
"작업": "Nhiệm vụ",
"작업 개수": "Số lượng nhiệm vụ",
"작업 수정": "Chỉnh sửa nhiệm vụ",
"작업 수정...": "Edit task...",
"작업 왼쪽에서 순서 변경": "Sắp xếp lại phía bên trái",
"작업 용량": "Kích thước tệp nhiệm vụ",
"작업 정보": "Thông tin nhiệm vụ",
@ -455,6 +471,7 @@
"종료": "Thoát",
"종료 중...": "Thoát...",
"좌우로 끌어서 1 씩 증감\n상하로 끌어서 100 씩 증감": "Kéo theo chiều ngang: ±1\nKéo theo chiều dọc: ±100",
"주소": "Address",
"주소 or 갤러리 넘버": "Đường dẫn hoặc ID thư viện",
"주소를 입력해주세요": "Hãy nhập vào vài đường dẫn",
"중간": "Ở giữa",
@ -465,6 +482,7 @@
"지원하는 사이트:": "Các trang hỗ trợ:",
"지정한 페이지만 다운로드합니다.": "Chỉ tải xuống những trang được chọn.",
"직접 다운로드": "Tải xuống trực tiếp",
"진행도": "Progress",
"참고 작품: {} 개": "Tham khảo: {} thư viện",
"참고해주세요: 도움말 - 사용법 (F1) - 쿠키 불러오기": "Hãy xem qua: Hỗ trợ - Hướng dẫn sử dụng (F1) - Tải cookies",
"창 보이기 / 숨기기": "Xem / Ẩn cửa sổ",
@ -476,6 +494,7 @@
"초기화": "Khởi động lại",
"최대 다운로드 속도": "Tốc độ tải xuống tối đa",
"최대 동시 작업": "Tối đa các nhiệm vụ chạy song song",
"최대 페이지 제한": "Maximum page limit",
"최소화 버튼으로 트레이로 최소화": "Nút ẩn sẽ ẩn xuống khay hệ thống",
"추가한 날짜": "Ngày đã được thêm vào",
"취소": "Hủy",
@ -499,6 +518,7 @@
"크기 조절": "Sửa kích thước",
"크기 조절...": "Sửa kích thước...",
"크롬 확장프로그램 연동에 실패했습니다.": "Kết nối tới tiện ích Chrome thất bại.",
"클라이언트": "Clilent",
"클래식": "Cổ điển",
"클립보드에서 자동 추가": "Giám sát bản tạm",
"타입": "Loại",
@ -509,6 +529,7 @@
"태그 (Tags)": "Thẻ",
"태그 :": "Thẻ :",
"태그 설정": "Cài đặt thẻ",
"태그 수정": "Edit tags",
"태그 없음": "Thẻ không tồn tại",
"테마": "Chủ đề",
"토렌트 추가": "Thêm Torrrent",
@ -521,6 +542,7 @@
"트레이로 최소화": "Ẩn xuống khay hệ thống",
"트레이에서 알림 보이기": "Xem thông báo trong khay",
"파일": "Tệp",
"파일 목록": "File list",
"파일 삭제": "Xóa tệp",
"파일 삭제 (&X)": "Xóa các tệp (&X)",
"파일 수": "Số lượng tệp",
@ -560,6 +582,8 @@
"표지 검색...": "Tìm ảnh bìa...",
"프로그램 패스워드": "Mật khẩu phần mềm",
"프록시": "Proxy",
"플러그인": "Plugins",
"플러그인을 추가하시겠습니까?": "Are you sure you want to add this plugin?",
"플레이리스트 파일에 번호 매기기": "Đánh số tệp trong danh sách phát ",
"플레이리스트 한 폴더에 다운로드": "Tải xuống danh sách phát vào một tệp",
"플로팅 미리보기": "Xem trước nổi",

View File

@ -139,6 +139,7 @@
"고급 검색": "高級搜尋",
"고정": "固定",
"고정 해제": "取消固定",
"관리자 권한 없이 실행": "Run without administrator privileges",
"그대로": "正序",
"그룹": "分組",
"그룹 (Groups)": "分組",
@ -158,6 +159,7 @@
"깊은 모델": "深度模型",
"끝내기": "關閉",
"날짜": "日期",
"낮음": "Low",
"내보내기": "匯出",
"내보내기 실패": "匯出失敗",
"내용 보기": "檢視指令碼",
@ -165,6 +167,7 @@
"내장 이미지 뷰어": "內建影像檢視器",
"녹화 중...": "正在錄制中……",
"녹화 중지": "停止錄制",
"높음": "High",
"다른 동영상 사이트에도 적용됩니다.": "也適用於其他影片網站。",
"다시 시작 (&S)": "重新開始(&S)",
"다시 시작 실패; 복구됨": "重試失敗; 已恢復",
@ -178,6 +181,7 @@
"다운로드 완료 후 자동 제거": "完成下載後自動刪除",
"다운로드 일시 정지 / 재개": "下載暫停/恢復",
"다운로드가 완료되면 알림": "下載完成後顯示通知",
"다운로드하지 않음": "Don't download",
"다운로드한 작품만": "僅下載畫廊",
"다음 검색시 더 빠르게 검색": "下一次搜尋時更快搜尋",
"다크 모드": "深色模式",
@ -216,6 +220,7 @@
"로컬 파일 감지": "檢測本地檔案",
"로컬 파일로부터 새 작업 만들기": "從本地檔案建立新任務",
"를 북마크에서 지웠습니다": "已從書簽中刪除",
"리트윗 포함": "Include retweets",
"릴리즈 노트": "版本說明",
"링크 열기": "開啟連結",
"링크 주소 복사": "複製連結位址",
@ -246,6 +251,7 @@
"변경할 최소 크기": "最小尺寸",
"변환 중...": "轉換中……",
"보기": "檢視",
"보통": "Normal",
"부팅 시 실행": "開機自動執行",
"북마크": "書簽",
"북마크 가져오기": "匯入書簽",
@ -295,6 +301,7 @@
"설명": "注釋",
"설정": "首選項",
"설정 (Preferences)": "設定",
"설정을 따름": "Follow preferences",
"성공: {}\n실패: {}": "成功: {}\n失敗: {}",
"수동": "手動",
"수동 업데이트": "手動更新",
@ -308,8 +315,12 @@
"스크립트": "指令碼",
"스크립트 가져오기": "匯入指令碼",
"스크립트를 실행하시겠습니까?": "您確定要執行此指令碼嗎?",
"스크립트를 읽는 중 오류가 발생했습니다": "Error occurred while reading the script",
"스토리 읽는 중...": "閱讀故事……",
"스토리 포함": "包括故事",
"시딩": "Seeding",
"시딩 중지": "Stop seeding",
"시딩 하지 않음": "No seeding",
"시리즈": "系列",
"시리즈 (Series)": "系列",
"시리즈가 없습니다": "沒有系列",
@ -339,6 +350,7 @@
"업데이트": "更新",
"업데이트 중...": "更新……",
"업데이트 체크 중...": "檢查更新……",
"업로드": "Upload",
"연결 프로그램 변경": "選擇用於解壓壓縮包的工具",
"열기": "開啟",
"예(&Y)": "是(&Y)",
@ -347,6 +359,7 @@
"올바르지 않은 범위입니다": "無效的范圍",
"올바르지 않은 주소입니다": "無效的網址",
"올바르지 않은 형식의 검색 필터입니다": "搜尋過濾器格式不正確",
"올바르지 않은 형식의 스크립트입니다": "Invalid format script",
"옵션": "選項",
"옵션 (Options)": "選項",
"완료": "完成",
@ -376,6 +389,8 @@
"이름 변경": "重新命名分組",
"이름을 얻는 도중 실패했습니다": "獲取名稱時失敗",
"이미 다운로드한 작품 제외": "排除已經下載的相簿",
"이미 추가한 작업입니다. 다시 다운로드하시겠습니까?": "This task already in the list. Do you want to download it again?",
"이미 추가한 플러그인입니다": "This plugin already in the list",
"이미 포함하는 그룹이 있습니다.": "已經有一個包含任務的組。",
"이미지": "影象",
"이미지 정보 캐시": "快取影像資訊",
@ -404,6 +419,7 @@
"작업": "任務",
"작업 개수": "完成任務數量",
"작업 수정": "編輯任務",
"작업 수정...": "Edit task...",
"작업 왼쪽에서 순서 변경": "在左側重新排序",
"작업 용량": "完成檔案大小",
"작업 정보": "任務資訊",
@ -455,6 +471,7 @@
"종료": "關閉",
"종료 중...": "Quit……",
"좌우로 끌어서 1 씩 증감\n상하로 끌어서 100 씩 증감": "向左或向右拖動增加或減少: ±1\n向上或向下拖動增加或減少: ±100",
"주소": "Address",
"주소 or 갤러리 넘버": "網址或相簿 ID",
"주소를 입력해주세요": "請輸入一些網址",
"중간": "中等",
@ -465,6 +482,7 @@
"지원하는 사이트:": "支援的網站:",
"지정한 페이지만 다운로드합니다.": "僅下載所選頁面。",
"직접 다운로드": "直接下載",
"진행도": "Progress",
"참고 작품: {} 개": "引用:{} 個畫廊",
"참고해주세요: 도움말 - 사용법 (F1) - 쿠키 불러오기": "請參閱:幫助-手冊 (F1) - 載入 cookie",
"창 보이기 / 숨기기": "顯示/隱藏視窗",
@ -476,6 +494,7 @@
"초기화": "重置",
"최대 다운로드 속도": "下載限速",
"최대 동시 작업": "同時下載的最大任務數",
"최대 페이지 제한": "Maximum page limit",
"최소화 버튼으로 트레이로 최소화": "點選最小化時最小化到托盤",
"추가한 날짜": "新增日期",
"취소": "取消",
@ -499,6 +518,7 @@
"크기 조절": "縮放",
"크기 조절...": "調整大小……",
"크롬 확장프로그램 연동에 실패했습니다.": "Chrome 擴充套件連結失敗。",
"클라이언트": "Clilent",
"클래식": "經典",
"클립보드에서 자동 추가": "監視剪貼簿",
"타입": "型別",
@ -509,6 +529,7 @@
"태그 (Tags)": "標簽",
"태그 :": "標簽:",
"태그 설정": "標簽設定",
"태그 수정": "Edit tags",
"태그 없음": "無標簽",
"테마": "主題",
"토렌트 추가": "開啟種子檔案",
@ -521,6 +542,7 @@
"트레이로 최소화": "最小化到系統托盤",
"트레이에서 알림 보이기": "在托盤中顯示通知",
"파일": "檔案",
"파일 목록": "File list",
"파일 삭제": "刪除檔案",
"파일 삭제 (&X)": "刪除檔案(&X)",
"파일 수": "檔案數",
@ -560,6 +582,8 @@
"표지 검색...": "搜尋封面……",
"프로그램 패스워드": "設定啟動密碼",
"프록시": "代理",
"플러그인": "Plugins",
"플러그인을 추가하시겠습니까?": "Are you sure you want to add this plugin?",
"플레이리스트 파일에 번호 매기기": "計算播放列表中的檔案",
"플레이리스트 한 폴더에 다운로드": "全部下載到一個資料夾",
"플로팅 미리보기": "浮動預覽",
@ -582,4 +606,4 @@
"후원": "捐贈",
"휴지통으로 이동": "移動到回收站"
}
}
}

View File

@ -139,6 +139,7 @@
"고급 검색": "高级搜索",
"고정": "固定",
"고정 해제": "取消固定",
"관리자 권한 없이 실행": "Run without administrator privileges",
"그대로": "正序",
"그룹": "分组",
"그룹 (Groups)": "分组",
@ -158,6 +159,7 @@
"깊은 모델": "深度模型",
"끝내기": "关闭",
"날짜": "日期",
"낮음": "Low",
"내보내기": "导出",
"내보내기 실패": "导出失败",
"내용 보기": "查看脚本",
@ -165,6 +167,7 @@
"내장 이미지 뷰어": "内置图像查看器",
"녹화 중...": "正在录制中...",
"녹화 중지": "停止录制",
"높음": "High",
"다른 동영상 사이트에도 적용됩니다.": "也适用于其他视频网站。",
"다시 시작 (&S)": "重新开始(&S)",
"다시 시작 실패; 복구됨": "重试失败; 已恢复",
@ -178,6 +181,7 @@
"다운로드 완료 후 자동 제거": "完成下载后自动删除",
"다운로드 일시 정지 / 재개": "下载暂停/恢复",
"다운로드가 완료되면 알림": "下载完成后显示通知",
"다운로드하지 않음": "Don't download",
"다운로드한 작품만": "仅下载画廊",
"다음 검색시 더 빠르게 검색": "下一次搜索时更快搜索",
"다크 모드": "深色模式",
@ -216,6 +220,7 @@
"로컬 파일 감지": "检测本地文件",
"로컬 파일로부터 새 작업 만들기": "从本地文件创建新任务",
"를 북마크에서 지웠습니다": "已从书签中删除",
"리트윗 포함": "Include retweets",
"릴리즈 노트": "版本说明",
"링크 열기": "打开链接",
"링크 주소 복사": "复制链接地址",
@ -246,6 +251,7 @@
"변경할 최소 크기": "最小尺寸",
"변환 중...": "转换中...",
"보기": "查看",
"보통": "Normal",
"부팅 시 실행": "开机自动运行",
"북마크": "书签",
"북마크 가져오기": "导入书签",
@ -295,6 +301,7 @@
"설명": "注释",
"설정": "首选项",
"설정 (Preferences)": "设置",
"설정을 따름": "Follow preferences",
"성공: {}\n실패: {}": "成功: {}\n失败: {}",
"수동": "手动",
"수동 업데이트": "手动更新",
@ -308,8 +315,12 @@
"스크립트": "脚本",
"스크립트 가져오기": "导入脚本",
"스크립트를 실행하시겠습니까?": "您确定要运行此脚本吗?",
"스크립트를 읽는 중 오류가 발생했습니다": "Error occurred while reading the script",
"스토리 읽는 중...": "阅读故事...",
"스토리 포함": "包括故事",
"시딩": "Seeding",
"시딩 중지": "Stop seeding",
"시딩 하지 않음": "No seeding",
"시리즈": "系列",
"시리즈 (Series)": "系列",
"시리즈가 없습니다": "没有系列",
@ -339,6 +350,7 @@
"업데이트": "更新",
"업데이트 중...": "更新...",
"업데이트 체크 중...": "检查更新...",
"업로드": "Upload",
"연결 프로그램 변경": "选择用于解压压缩包的工具",
"열기": "打开",
"예(&Y)": "是(&Y)",
@ -347,6 +359,7 @@
"올바르지 않은 범위입니다": "无效的范围",
"올바르지 않은 주소입니다": "无效的网址",
"올바르지 않은 형식의 검색 필터입니다": "搜索过滤器格式不正确",
"올바르지 않은 형식의 스크립트입니다": "Invalid format script",
"옵션": "选项",
"옵션 (Options)": "选项",
"완료": "完成",
@ -376,6 +389,8 @@
"이름 변경": "重命名分组",
"이름을 얻는 도중 실패했습니다": "获取名称时失败",
"이미 다운로드한 작품 제외": "排除已经下载的图库",
"이미 추가한 작업입니다. 다시 다운로드하시겠습니까?": "This task already in the list. Do you want to download it again?",
"이미 추가한 플러그인입니다": "This plugin already in the list",
"이미 포함하는 그룹이 있습니다.": "已经有一个包含任务的组。",
"이미지": "图像",
"이미지 정보 캐시": "缓存图像信息",
@ -404,6 +419,7 @@
"작업": "任务",
"작업 개수": "完成任务数量",
"작업 수정": "编辑任务",
"작업 수정...": "Edit task...",
"작업 왼쪽에서 순서 변경": "在左侧重新排序",
"작업 용량": "完成文件大小",
"작업 정보": "任务信息",
@ -455,6 +471,7 @@
"종료": "关闭",
"종료 중...": "Quit...",
"좌우로 끌어서 1 씩 증감\n상하로 끌어서 100 씩 증감": "向左或向右拖动增加或减少: ±1\n向上或向下拖动增加或减少: ±100",
"주소": "Address",
"주소 or 갤러리 넘버": "网址或图库 ID",
"주소를 입력해주세요": "请输入一些网址",
"중간": "中等",
@ -465,6 +482,7 @@
"지원하는 사이트:": "支持的网站:",
"지정한 페이지만 다운로드합니다.": "仅下载所选页面。",
"직접 다운로드": "直接下载",
"진행도": "Progress",
"참고 작품: {} 개": "引用:{} 个画廊",
"참고해주세요: 도움말 - 사용법 (F1) - 쿠키 불러오기": "请参阅:帮助-手册 (F1) - 加载 cookie",
"창 보이기 / 숨기기": "显示/隐藏窗口",
@ -476,6 +494,7 @@
"초기화": "重置",
"최대 다운로드 속도": "下载限速",
"최대 동시 작업": "同时下载的最大任务数",
"최대 페이지 제한": "Maximum page limit",
"최소화 버튼으로 트레이로 최소화": "点击最小化时最小化到托盘",
"추가한 날짜": "添加日期",
"취소": "取消",
@ -499,6 +518,7 @@
"크기 조절": "缩放",
"크기 조절...": "调整大小...",
"크롬 확장프로그램 연동에 실패했습니다.": "Chrome 扩展链接失败。",
"클라이언트": "Clilent",
"클래식": "经典",
"클립보드에서 자동 추가": "监视剪贴板",
"타입": "类型",
@ -509,6 +529,7 @@
"태그 (Tags)": "标签",
"태그 :": "标签:",
"태그 설정": "标签设置",
"태그 수정": "Edit tags",
"태그 없음": "无标签",
"테마": "主题",
"토렌트 추가": "打开种子文件",
@ -521,6 +542,7 @@
"트레이로 최소화": "最小化到系统托盘",
"트레이에서 알림 보이기": "在托盘中显示通知",
"파일": "文件",
"파일 목록": "File list",
"파일 삭제": "删除文件",
"파일 삭제 (&X)": "删除文件(&X)",
"파일 수": "文件数",
@ -560,6 +582,8 @@
"표지 검색...": "搜索封面...",
"프로그램 패스워드": "设置启动密码",
"프록시": "代理",
"플러그인": "Plugins",
"플러그인을 추가하시겠습니까?": "Are you sure you want to add this plugin?",
"플레이리스트 파일에 번호 매기기": "计算播放列表中的文件",
"플레이리스트 한 폴더에 다운로드": "全部下载到一个文件夹",
"플로팅 미리보기": "浮动预览",