diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..38389e3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.pyo +*.pyc \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..f42bb1d --- /dev/null +++ b/__init__.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# @Time : 2022/02/08 2:55 PM +# @Author : yommi +# @Site : +# @File : __init__ +# @Software: PyCharm +from .plugin import P +blueprint = P.blueprint +menu = P.menu +plugin_load = P.logic.plugin_load +plugin_unload = P.logic.plugin_unload +plugin_info = P.plugin_info diff --git a/logic_linkkf.py b/logic_linkkf.py new file mode 100644 index 0000000..374028d --- /dev/null +++ b/logic_linkkf.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Time : 2022/02/08 3:44 PM +# @Author : yommi +# @Site : +# @File : logic_linkkf +# @Software: PyCharm +import os, sys, traceback, re, json, threading +from datetime import datetime +import copy +# third-party +import requests +# 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 + + +class LogicLinkkf(LogicModuleBase): + def __init__(self, P): + super(LogicLinkkf, self).__init__(P, 'setting', scheduler_desc='linkkf 자동 다운로드') + self.name = 'linkkf' + default_route_socketio(P, self) + + 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)) + + pass diff --git a/logic_ohli24.py b/logic_ohli24.py new file mode 100644 index 0000000..af190a7 --- /dev/null +++ b/logic_ohli24.py @@ -0,0 +1,272 @@ +#!/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 +# 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 + + 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()) + + @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 + + +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 + 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() diff --git a/plugin.py b/plugin.py new file mode 100644 index 0000000..39359c9 --- /dev/null +++ b/plugin.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# @Time : 2022/02/08 2:57 PM +# @Author : yommi +# @Site : +# @File : plugin +# @Software: PyCharm + +import os, traceback +# third-party +from flask import Blueprint +# sjva 공용 +from framework.logger import get_logger +from framework import app, path_data +from framework.util import Util +from plugin import get_model_setting, Logic, default_route, PluginUtil + + +####################################################################### + +class P(object): + package_name = __name__.split('.')[0] + logger = get_logger(package_name) + blueprint = Blueprint(package_name, package_name, url_prefix='/%s' % package_name, + template_folder=os.path.join(os.path.dirname(__file__), 'templates')) + menu = { + 'main': [package_name, u'애니 다운로드'], + 'sub': [ + ['ohli24', u'ohli24'], ['linkkf', u'LINKKF'], ['log', u'로그'] + ], + 'category': 'vod', + 'sub2': { + 'ohli24': [ + ['setting', u'설정'], ['request', u'요청'], ['queue', u'큐'], ['list', u'목록'] + ], + 'linkkf': [ + ['setting', u'설정'], ['request', u'요청'], ['queue', u'큐'], ['list', u'목록'] + ], + } + } + plugin_info = { + 'version': '0.1.1.0', + 'name': 'anime_downloader', + 'category_name': 'vod', + 'icon': '', + 'developer': 'soju6jan && projectdx', + 'description': u'비디오 다운로드', + 'home': 'http://yommi.duckdns.org:20080/projectdx/anime-downloader', + 'more': '', + } + ModelSetting = get_model_setting(package_name, logger) + logic = None + module_list = None + home_module = 'ohli24' + + +# 초기화 함수 +def initialize(): + try: + app.config['SQLALCHEMY_BINDS'][P.package_name] = 'sqlite:///%s' % ( + os.path.join(path_data, 'db', '{package_name}.db'.format(package_name=P.package_name))) + PluginUtil.make_info_json(P.plugin_info, __file__) + from .logic_ohli24 import LogicOhli24 + from .logic_linkkf import LogicLinkkf + # P.module_list = [LogicOhli24(P), LogicLinkkf(P)] + P.module_list = [LogicOhli24(P)] + P.logic = Logic(P) + default_route(P) + except Exception as e: + P.logger.error('Exception:%s', e) + P.logger.error(traceback.format_exc()) + + +initialize() diff --git a/templates/anime_downloader_ohli24_list.html b/templates/anime_downloader_ohli24_list.html new file mode 100644 index 0000000..566549b --- /dev/null +++ b/templates/anime_downloader_ohli24_list.html @@ -0,0 +1,10 @@ + + +
+ +