diff --git a/README.md b/README.md
index 344b38d..7206175 100644
--- a/README.md
+++ b/README.md
@@ -81,6 +81,11 @@
## ๐ ๋ณ๊ฒฝ ์ด๋ ฅ (Changelog)
+### v0.6.25 (2026-01-09)
+- **์๊ฐ ์
๋ฐ์ดํธ ๊ธฐ๋ฅ ์ถ๊ฐ**: ๋ชจ๋ ์ค์ ํ์ด์ง (Ohli24, Anilife, Linkkf)์์ "์
๋ฐ์ดํธ" ๋ฒํผ ํด๋ฆญ์ผ๋ก Git Pull ๋ฐ ํ๋ฌ๊ทธ์ธ ํซ ๋ฆฌ๋ก๋ ์ง์
+- **๋ฒ์ ์ฒดํฌ API**: GitHub์์ ์ต์ ๋ฒ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ ์
๋ฐ์ดํธ ์๋ฆผ ํ์ (1์๊ฐ ์บ์ฑ)
+- **๊ณตํต ๋ฒ ์ด์ค ํตํฉ**: `AnimeModuleBase`์ `get_update_info`, `reload_plugin` ๋ฉ์๋ ์ถ๊ฐ๋ก ๋ชจ๋ ๋ชจ๋์์ ์๋ ์ฌ์ฉ ๊ฐ๋ฅ
+
### v0.6.24 (2026-01-08)
- **Ohli24 GDM ์ฐ๋ ๋ฒ๊ทธ ์์ **:
- **์ธ๋ค์ผ ๋๋ฝ ํด๊ฒฐ**: GDM ์์ ์ `image` ํค๋ฅผ `thumbnail`๋ก ์ฌ๋ฐ๋ฅด๊ฒ ๋งคํํ์ฌ ๋ชฉ๋ก์์ ์ด๋ฏธ์ง๊ฐ ๋ณด์ด๋๋ก ์์ .
diff --git a/info.yaml b/info.yaml
index 736c659..20e6716 100644
--- a/info.yaml
+++ b/info.yaml
@@ -1,5 +1,5 @@
title: "์ ๋ ๋ค์ด๋ก๋"
-version: 0.6.23
+version: 0.6.25
package_name: "anime_downloader"
developer: "projectdx"
description: "anime downloader"
diff --git a/mod_base.py b/mod_base.py
index 9e84fbf..c03bcc2 100644
--- a/mod_base.py
+++ b/mod_base.py
@@ -5,6 +5,10 @@ import os, traceback, time, json
from datetime import datetime
class AnimeModuleBase(PluginModuleBase):
+ # ์
๋ฐ์ดํธ ์ฒดํฌ ์บ์ฑ (ํด๋์ค ๋ ๋ฒจ)
+ _last_update_check = 0
+ _latest_version = None
+
def __init__(self, P, setup_default=None, **kwargs):
super(AnimeModuleBase, self).__init__(P, **kwargs)
self.P = P # Ensure P is available via self.P
@@ -131,6 +135,37 @@ class AnimeModuleBase(PluginModuleBase):
arg3 = request.form.get('arg3') or request.args.get('arg3')
return self.process_command(command, arg1, arg2, arg3, req)
+ elif sub == 'self_update':
+ # ์๊ฐ ์
๋ฐ์ดํธ (Git Pull) ๋ฐ ๋ชจ๋ ๋ฆฌ๋ก๋
+ try:
+ import subprocess
+ plugin_path = os.path.dirname(os.path.dirname(__file__)) if '__file__' in dir() else os.path.dirname(__file__)
+ # ์ค์ ํ๋ฌ๊ทธ์ธ ๋ฃจํธ ๋๋ ํ ๋ฆฌ
+ plugin_path = os.path.dirname(__file__)
+ self.P.logger.info(f"์ ๋ ๋ค์ด๋ก๋ ์๊ฐ ์
๋ฐ์ดํธ ์์: {plugin_path}")
+
+ cmd = ['git', '-C', plugin_path, 'pull']
+ process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
+ stdout, stderr = process.communicate()
+
+ if process.returncode != 0:
+ raise Exception(f"Git pull ์คํจ: {stderr}")
+
+ self.P.logger.info(f"Git pull ๊ฒฐ๊ณผ: {stdout}")
+
+ # ๋ชจ๋ ๋ฆฌ๋ก๋
+ self.reload_plugin()
+
+ return jsonify({'ret': 'success', 'msg': f"์
๋ฐ์ดํธ ๋ฐ ๋ฆฌ๋ก๋ ์๋ฃ!
{stdout}", 'data': stdout})
+ except Exception as e:
+ self.P.logger.error(f"์๊ฐ ์
๋ฐ์ดํธ ์ค ์ค๋ฅ: {str(e)}")
+ self.P.logger.error(traceback.format_exc())
+ return jsonify({'ret': 'danger', 'msg': f"์
๋ฐ์ดํธ ์คํจ: {str(e)}"})
+
+ elif sub == 'check_update':
+ force = req.form.get('force') == 'true'
+ return jsonify({'ret': 'success', 'data': self.get_update_info(force=force)})
+
return jsonify({'ret': 'fail', 'log': f"Unknown sub: {sub}"})
except Exception as e:
@@ -198,3 +233,92 @@ class AnimeModuleBase(PluginModuleBase):
except Exception as e:
return {'ret': 'fail', 'msg': str(e)}
+ def get_update_info(self, force=False):
+ """GitHub์์ ์ต์ ๋ฒ์ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ (์บ์ฑ ํ์ฉ)"""
+ import requests
+ now = time.time()
+
+ # ์ค์ ๋ก์ปฌ ํ์ผ์์ ํ์ฌ ๋ฒ์ ์ฝ๊ธฐ
+ current_version = self.P.plugin_info.get('version', '0.0.0')
+ try:
+ info_path = os.path.join(os.path.dirname(__file__), 'info.yaml')
+ if os.path.exists(info_path):
+ import yaml
+ with open(info_path, 'r', encoding='utf-8') as f:
+ local_info = yaml.safe_load(f)
+ current_version = str(local_info.get('version', current_version))
+ except: pass
+
+ # 1์๊ฐ๋ง๋ค ์ฒดํฌ (force=True๋ฉด ์ฆ์)
+ if not force and AnimeModuleBase._latest_version and (now - AnimeModuleBase._last_update_check < 3600):
+ return {
+ 'current': current_version,
+ 'latest': AnimeModuleBase._latest_version,
+ 'has_update': self._is_newer(AnimeModuleBase._latest_version, current_version)
+ }
+
+ try:
+ url = "https://raw.githubusercontent.com/projectdx75/anime_downloader/master/info.yaml"
+ res = requests.get(url, timeout=5)
+ if res.status_code == 200:
+ import yaml
+ data = yaml.safe_load(res.text)
+ AnimeModuleBase._latest_version = str(data.get('version', ''))
+ AnimeModuleBase._last_update_check = now
+
+ return {
+ 'current': current_version,
+ 'latest': AnimeModuleBase._latest_version,
+ 'has_update': self._is_newer(AnimeModuleBase._latest_version, current_version)
+ }
+ except Exception as e:
+ self.P.logger.error(f"Update check failed: {e}")
+
+ return {
+ 'current': current_version,
+ 'latest': AnimeModuleBase._latest_version or current_version,
+ 'has_update': False
+ }
+
+ def _is_newer(self, latest, current):
+ """๋ฒ์ ๋น๊ต (0.7.8 vs 0.7.7)"""
+ if not latest or not current: return False
+ try:
+ l_parts = [int(p) for p in latest.split('.')]
+ c_parts = [int(p) for p in current.split('.')]
+ return l_parts > c_parts
+ except:
+ return latest != current
+
+ def reload_plugin(self):
+ """ํ๋ฌ๊ทธ์ธ ๋ชจ๋ ํซ ๋ฆฌ๋ก๋"""
+ import sys
+ import importlib
+
+ try:
+ package_name = self.P.package_name
+ self.P.logger.info(f"ํ๋ฌ๊ทธ์ธ ๋ฆฌ๋ก๋ ์์: {package_name}")
+
+ # ๊ด๋ จ ๋ชจ๋ ์ฐพ๊ธฐ ๋ฐ ๋ฆฌ๋ก๋
+ modules_to_reload = []
+ for module_name in list(sys.modules.keys()):
+ if module_name.startswith(package_name):
+ modules_to_reload.append(module_name)
+
+ # ์์กด์ฑ ์ญ์์ผ๋ก ์ ๋ ฌ (๊น์ ๋ชจ๋ ๋จผ์ )
+ modules_to_reload.sort(key=lambda x: x.count('.'), reverse=True)
+
+ for module_name in modules_to_reload:
+ try:
+ module = sys.modules[module_name]
+ importlib.reload(module)
+ self.P.logger.debug(f"Reloaded: {module_name}")
+ except Exception as e:
+ self.P.logger.warning(f"Failed to reload {module_name}: {e}")
+
+ self.P.logger.info(f"ํ๋ฌ๊ทธ์ธ ๋ชจ๋ [{package_name}] ๋ฆฌ๋ก๋ ์๋ฃ")
+ return True
+ except Exception as e:
+ self.P.logger.error(f"๋ชจ๋ ๋ฆฌ๋ก๋ ์ค ์คํจ: {str(e)}")
+ self.P.logger.error(traceback.format_exc())
+ return False
diff --git a/templates/anime_downloader_anilife_setting.html b/templates/anime_downloader_anilife_setting.html
index fa1a901..fc07c16 100644
--- a/templates/anime_downloader_anilife_setting.html
+++ b/templates/anime_downloader_anilife_setting.html
@@ -8,7 +8,12 @@