360 lines
13 KiB
Python
360 lines
13 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
# @Time : 2022/02/08 3:44 PM
|
|
# @Author : yommi
|
|
# @Site :
|
|
# @File : logic_ohli24
|
|
# @Software: PyCharm
|
|
|
|
import os, sys, traceback, re, json, threading
|
|
from datetime import datetime
|
|
import copy
|
|
# third-party
|
|
import requests
|
|
from lxml import html
|
|
# third-party
|
|
from flask import request, render_template, jsonify
|
|
from sqlalchemy import or_, and_, func, not_, desc
|
|
|
|
# sjva 공용
|
|
from framework import db, scheduler, path_data, socketio
|
|
from framework.util import Util
|
|
from framework.common.util import headers
|
|
from plugin import LogicModuleBase, FfmpegQueueEntity, FfmpegQueue, default_route_socketio
|
|
from tool_base import d
|
|
# 패키지
|
|
from .plugin import P
|
|
|
|
logger = P.logger
|
|
|
|
|
|
#########################################################
|
|
|
|
|
|
class LogicOhli24(LogicModuleBase):
|
|
db_default = {
|
|
'ohli24_db_version': '1',
|
|
'ohli24_url': 'https://ohli24.net',
|
|
'ohli24_download_path': os.path.join(path_data, P.package_name, 'ohli24'),
|
|
'ohli24_auto_make_folder': 'True',
|
|
'ohli24_auto_make_season_folder': 'True',
|
|
'ohli24_finished_insert': u'[완결]',
|
|
'ohli24_max_ffmpeg_process_count': '1',
|
|
'ohli24_order_desc': 'False',
|
|
'ohli24_auto_start': 'False',
|
|
'ohli24_interval': '* 5 * * *',
|
|
'ohli24_auto_mode_all': 'False',
|
|
'ohli24_auto_code_list': 'all',
|
|
'ohli24_current_code': '',
|
|
'ohli24_uncompleted_auto_enqueue': 'False',
|
|
'ohli24_image_url_prefix_series': 'https://www.jetcloud.cc/series/',
|
|
'ohli24_image_url_prefix_episode': 'https://www.jetcloud-list.cc/thumbnail/',
|
|
}
|
|
current_headers = None
|
|
current_data = None
|
|
session = requests.Session()
|
|
headers = {
|
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36',
|
|
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
|
|
'Accept-Language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
|
|
'Referer': ''
|
|
}
|
|
useragent = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, '
|
|
'like Gecko) Chrome/96.0.4664.110 Whale/3.12.129.46 Safari/537.36'}
|
|
|
|
def __init__(self, P):
|
|
super(LogicOhli24, self).__init__(P, 'setting', scheduler_desc='ani365 자동 다운로드')
|
|
self.name = 'ohli24'
|
|
default_route_socketio(P, self)
|
|
|
|
@staticmethod
|
|
def db_init():
|
|
pass
|
|
# try:
|
|
# for key, value in P.Logic.db_default.items():
|
|
# if db.session.query(ModelSetting).filter_by(key=key).count() == 0:
|
|
# db.session.add(ModelSetting(key, value))
|
|
# db.session.commit()
|
|
# except Exception as e:
|
|
# logger.error('Exception:%s', e)
|
|
# logger.error(traceback.format_exc())
|
|
|
|
def process_menu(self, sub, req):
|
|
arg = P.ModelSetting.to_dict()
|
|
arg['sub'] = self.name
|
|
if sub in ['setting', 'queue', 'list', 'request']:
|
|
if sub == 'request' and req.args.get('content_code') is not None:
|
|
arg['ani365_current_code'] = req.args.get('content_code')
|
|
if sub == 'setting':
|
|
job_id = '%s_%s' % (self.P.package_name, self.name)
|
|
arg['scheduler'] = str(scheduler.is_include(job_id))
|
|
arg['is_running'] = str(scheduler.is_running(job_id))
|
|
return render_template(
|
|
'{package_name}_{module_name}_{sub}.html'.format(package_name=P.package_name, module_name=self.name,
|
|
sub=sub), arg=arg)
|
|
return render_template('sample.html', title='%s - %s' % (P.package_name, sub))
|
|
|
|
# @staticmethod
|
|
def process_ajax(self, sub, req):
|
|
try:
|
|
if sub == 'analysis':
|
|
# code = req.form['code']
|
|
code = request.form['code']
|
|
data = []
|
|
# print(code)
|
|
# logger.info("code::: %s", code)
|
|
P.ModelSetting.set('ohli24_current_code', code)
|
|
data = self.get_series_info(code)
|
|
self.current_data = data
|
|
return jsonify({'ret': 'success', 'data': data, 'code': code})
|
|
elif sub == 'add_queue':
|
|
ret = {}
|
|
info = json.loads(request.form['data'])
|
|
ret['ret'] = self.add(info)
|
|
return jsonify(ret)
|
|
pass
|
|
except Exception as e:
|
|
P.logger.error('Exception:%s', e)
|
|
P.logger.error(traceback.format_exc())
|
|
|
|
def get_series_info(self, code):
|
|
try:
|
|
if self.current_data is not None and 'code' in self.current_data and self.current_data['code'] == code:
|
|
return self.current_data
|
|
|
|
if code.startswith('http'):
|
|
code = code.split('c/')[1]
|
|
logger.info(f'code:::: {code}')
|
|
|
|
url = P.ModelSetting.get('ohli24_url') + '/c/' + code
|
|
# self.current_headers = { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)
|
|
# AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/96.0.4664.110 Whale/3.12.129.46 Safari/537.36',
|
|
# 'Referer': url }
|
|
|
|
response_data = LogicOhli24.get_html(url, timeout=10)
|
|
tree = html.fromstring(response_data)
|
|
title = tree.xpath('//div[@class="view-title"]/h1/text()')[0]
|
|
# image = tree.xpath('//div[@class="view-info"]/div[@class="image"]/div/img')[0]['src']
|
|
image = tree.xpath('//div[@class="image"]/div/img/@src')[0]
|
|
des_items = tree.xpath('//div[@class="list"]/p')
|
|
des = {}
|
|
des_key = ['_otit', '_dir', '_pub', '_tag', '_classifi', '_country', '_grade']
|
|
|
|
logger.info('des_items length:: %s', len(des_items))
|
|
for idx, item in enumerate(des_items):
|
|
key = des_key[idx]
|
|
span = item.xpath('.//span//text()')
|
|
logger.info(span)
|
|
des[key] = item.xpath('.//span/text()')[1]
|
|
|
|
logger.info(f'des::>> {des}')
|
|
image = image.replace('..', P.ModelSetting.get('ohli24_url'))
|
|
logger.info('images:: %s', image)
|
|
logger.info('title:: %s', title)
|
|
|
|
ser_description = tree.xpath('//div[@class="view-stocon"]/div[@class="c"]/text()')
|
|
|
|
data = {
|
|
'title': title,
|
|
'image': image,
|
|
'date': '2022.01.11 00:30 (화)',
|
|
'ser_description': ser_description,
|
|
'des': des,
|
|
'episode': [
|
|
{
|
|
'title': '녹을 먹는 비스코 5화',
|
|
'thumbnail': 'https://ohli24.net/data/editor/2201/6ced5f453ef2fe9efb7edfa0e9e12d19_1641871470_4041.jpg',
|
|
'date': '2022-02-08'
|
|
}
|
|
]
|
|
}
|
|
|
|
return data
|
|
# logger.info(response_text)
|
|
|
|
except Exception as e:
|
|
P.logger.error('Exception:%s', e)
|
|
P.logger.error(traceback.format_exc())
|
|
return {'ret': 'exception', 'log': str(e)}
|
|
|
|
@staticmethod
|
|
def plugin_load():
|
|
try:
|
|
logger.debug('%s plugin_load', P.package_name)
|
|
# self.queue = FfmpegQueue(P, P.ModelSetting.get_int('ani365_max_ffmpeg_process_count'))
|
|
# self.current_data = None
|
|
# self.queue.queue_start()
|
|
|
|
except Exception as e:
|
|
logger.error('Exception:%s', e)
|
|
logger.error(traceback.format_exc())
|
|
|
|
@staticmethod
|
|
def plugin_unload():
|
|
try:
|
|
logger.debug('%s plugin_unload', P.package_name)
|
|
scheduler.remove_job('%s_recent' % P.package_name)
|
|
except Exception as e:
|
|
logger.error('Exception:%s', e)
|
|
logger.error(traceback.format_exc())
|
|
|
|
@staticmethod
|
|
def reset_db() -> bool:
|
|
db.session.query(ModelOhli24Item).delete()
|
|
db.session.commit()
|
|
return True
|
|
|
|
@staticmethod
|
|
def get_html(url, referer=None, stream=False, timeout=5):
|
|
try:
|
|
if LogicOhli24.session is None:
|
|
LogicOhli24.session = requests.session()
|
|
|
|
# logger.debug('get_html :%s', url)
|
|
headers['Referer'] = '' if referer is None else referer
|
|
page_content = LogicOhli24.session.get(url, headers=headers, timeout=timeout)
|
|
data = page_content.text
|
|
except Exception as e:
|
|
logger.error('Exception:%s', e)
|
|
logger.error(traceback.format_exc())
|
|
return data
|
|
|
|
|
|
class Ohli24QueueEntity(FfmpegQueueEntity):
|
|
def __init__(self, P, module_logic, info):
|
|
super(Ohli24QueueEntity, self).__init__(P, module_logic, info)
|
|
self.vtt = None
|
|
self.season = 1
|
|
self.content_title = None
|
|
# Todo::: 임시주석처리
|
|
# self.make_episode_info()
|
|
|
|
# episode info
|
|
def make_episode_info(self):
|
|
pass
|
|
|
|
|
|
class ModelOhli24Item(db.Model):
|
|
__tablename__ = '{package_name}_ohli24_item'.format(package_name=P.package_name)
|
|
__table_args__ = {'mysql_collate': 'utf8_general_ci'}
|
|
__bind_key__ = P.package_name
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
created_time = db.Column(db.DateTime)
|
|
completed_time = db.Column(db.DateTime)
|
|
reserved = db.Column(db.JSON)
|
|
content_code = db.Column(db.String)
|
|
season = db.Column(db.Integer)
|
|
episode_no = db.Column(db.Integer)
|
|
title = db.Column(db.String)
|
|
episode_title = db.Column(db.String)
|
|
ani365_va = db.Column(db.String)
|
|
ani365_vi = db.Column(db.String)
|
|
ani365_id = db.Column(db.String)
|
|
quality = db.Column(db.String)
|
|
filepath = db.Column(db.String)
|
|
filename = db.Column(db.String)
|
|
savepath = db.Column(db.String)
|
|
video_url = db.Column(db.String)
|
|
vtt_url = db.Column(db.String)
|
|
thumbnail = db.Column(db.String)
|
|
status = db.Column(db.String)
|
|
ohli24_info = db.Column(db.JSON)
|
|
|
|
def __init__(self):
|
|
self.created_time = datetime.now()
|
|
|
|
def __repr__(self):
|
|
return repr(self.as_dict())
|
|
|
|
def as_dict(self):
|
|
ret = {x.name: getattr(self, x.name) for x in self.__table__.columns}
|
|
ret['created_time'] = self.created_time.strftime('%Y-%m-%d %H:%M:%S')
|
|
ret['completed_time'] = self.completed_time.strftime(
|
|
'%Y-%m-%d %H:%M:%S') if self.completed_time is not None else None
|
|
return ret
|
|
|
|
def save(self):
|
|
db.session.add(self)
|
|
db.session.commit()
|
|
|
|
@classmethod
|
|
def get_by_id(cls, id):
|
|
return db.session.query(cls).filter_by(id=id).first()
|
|
|
|
@classmethod
|
|
def get_by_ani365_id(cls, ani365_id):
|
|
return db.session.query(cls).filter_by(ani365_id=ani365_id).first()
|
|
|
|
@classmethod
|
|
def delete_by_id(cls, id):
|
|
db.session.query(cls).filter_by(id=id).delete()
|
|
db.session.commit()
|
|
return True
|
|
|
|
@classmethod
|
|
def web_list(cls, req):
|
|
ret = {}
|
|
page = int(req.form['page']) if 'page' in req.form else 1
|
|
page_size = 30
|
|
job_id = ''
|
|
search = req.form['search_word'] if 'search_word' in req.form else ''
|
|
option = req.form['option'] if 'option' in req.form else 'all'
|
|
order = req.form['order'] if 'order' in req.form else 'desc'
|
|
query = cls.make_query(search=search, order=order, option=option)
|
|
count = query.count()
|
|
query = query.limit(page_size).offset((page - 1) * page_size)
|
|
lists = query.all()
|
|
ret['list'] = [item.as_dict() for item in lists]
|
|
ret['paging'] = Util.get_paging_info(count, page, page_size)
|
|
return ret
|
|
|
|
@classmethod
|
|
def make_query(cls, search='', order='desc', option='all'):
|
|
query = db.session.query(cls)
|
|
if search is not None and search != '':
|
|
if search.find('|') != -1:
|
|
tmp = search.split('|')
|
|
conditions = []
|
|
for tt in tmp:
|
|
if tt != '':
|
|
conditions.append(cls.filename.like('%' + tt.strip() + '%'))
|
|
query = query.filter(or_(*conditions))
|
|
elif search.find(',') != -1:
|
|
tmp = search.split(',')
|
|
for tt in tmp:
|
|
if tt != '':
|
|
query = query.filter(cls.filename.like('%' + tt.strip() + '%'))
|
|
else:
|
|
query = query.filter(cls.filename.like('%' + search + '%'))
|
|
if option == 'completed':
|
|
query = query.filter(cls.status == 'completed')
|
|
|
|
query = query.order_by(desc(cls.id)) if order == 'desc' else query.order_by(cls.id)
|
|
return query
|
|
|
|
@classmethod
|
|
def get_list_uncompleted(cls):
|
|
return db.session.query(cls).filter(cls.status != 'completed').all()
|
|
|
|
@classmethod
|
|
def append(cls, q):
|
|
item = ModelOhli24Item()
|
|
item.content_code = q['content_code']
|
|
item.season = q['season']
|
|
item.episode_no = q['epi_queue']
|
|
item.title = q['content_title']
|
|
item.episode_title = q['title']
|
|
item.ani365_va = q['va']
|
|
item.ani365_vi = q['_vi']
|
|
item.ani365_id = q['_id']
|
|
item.quality = q['quality']
|
|
item.filepath = q['filepath']
|
|
item.filename = q['filename']
|
|
item.savepath = q['savepath']
|
|
item.video_url = q['url']
|
|
item.vtt_url = q['vtt']
|
|
item.thumbnail = q['thumbnail']
|
|
item.status = 'wait'
|
|
item.ani365_info = q['ani365_info']
|
|
item.save()
|