Skip to content

feat: implement episode offset #673

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: 3.2-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ sqlmodel==0.0.8
sse-starlette==1.6.5
semver==3.0.1
openai==0.28.1
torrentool==1.2.0
15 changes: 15 additions & 0 deletions backend/src/module/checker/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
from pathlib import Path

import requests
from sqlalchemy import inspect

from module.conf import VERSION, settings
from module.downloader import DownloadClient
from module.models import Config
from module.update import version_check
from module.database import Database

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -83,3 +85,16 @@ def check_img_cache() -> bool:
else:
img_path.mkdir()
return False

@staticmethod
def check_torrent_hash() -> bool:
with Database() as db:
inspector = inspect(db.engine)
columns = inspector.get_columns("torrent")
if any(column["name"] == "hash" for column in columns):
for torrent in db.torrent.search_all():
if torrent.hash is None and torrent.bangumi_id:
return False
return True
else:
return False
14 changes: 13 additions & 1 deletion backend/src/module/core/program.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@

from module.conf import VERSION, settings
from module.models import ResponseModel
from module.update import data_migration, first_run, from_30_to_31, start_up, cache_image
from module.update import (
data_migration,
first_run,
from_30_to_31,
start_up,
cache_image,
torrent_migration,
)

from .sub_thread import RenameThread, RSSThread

Expand Down Expand Up @@ -49,6 +56,11 @@ def startup(self):
if not self.img_cache:
logger.info("[Core] No image cache exists, create image cache.")
cache_image()
if not self.torrent_hash:
logger.info(
"[Core] The hash field of the torrent table does not exist or its value is empty, get torrent hash."
)
torrent_migration()
self.start()

def start(self):
Expand Down
4 changes: 4 additions & 0 deletions backend/src/module/core/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,7 @@ def database(self):
@property
def img_cache(self):
return self.check_img_cache()

@property
def torrent_hash(self):
return self.check_torrent_hash()
7 changes: 7 additions & 0 deletions backend/src/module/database/bangumi.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def add(self, data: Bangumi):
statement = select(Bangumi).where(Bangumi.title_raw == data.title_raw)
bangumi = self.session.exec(statement).first()
if bangumi:
data.id = bangumi.id
return False
self.session.add(data)
self.session.commit()
Expand Down Expand Up @@ -171,3 +172,9 @@ def disable_rule(self, _id: int):
def search_rss(self, rss_link: str) -> list[Bangumi]:
statement = select(Bangumi).where(func.instr(rss_link, Bangumi.rss_link) > 0)
return self.session.exec(statement).all()

def get_offset(self, _id: int) -> int:
offset = self.session.exec(
select(Bangumi.offset).where(Bangumi.id == _id)
).first()
return 0 if offset is None else offset
18 changes: 17 additions & 1 deletion backend/src/module/database/torrent.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
from typing import Optional

from sqlmodel import Session, select
from sqlmodel import Session, select, and_, desc

from module.models import Torrent

Expand Down Expand Up @@ -55,3 +56,18 @@ def check_new(self, torrents_list: list[Torrent]) -> list[Torrent]:
if torrent.url not in old_urls:
new_torrents.append(torrent)
return new_torrents

def get_bangumi_id(self, torrent_hash: str) -> Optional[int]:
return self.session.exec(
select(Torrent.bangumi_id)
.where(and_(Torrent.hash == torrent_hash, Torrent.bangumi_id.isnot(None)))
.order_by(desc(Torrent.id))
).first()

def delete_by_bangumi_id(self, bangumi_id: int):
statement = select(Torrent).where(Torrent.bangumi_id == bangumi_id)
torrents = self.session.exec(statement).all()
for torrent in torrents:
logger.debug(f"[Database] Delete torrent name: {torrent.name}.")
self.session.delete(torrent)
self.session.commit()
4 changes: 4 additions & 0 deletions backend/src/module/database/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ def get_user(self, username):
def auth_user(self, user: User):
statement = select(User).where(User.username == user.username)
result = self.session.exec(statement).first()
if not user.password:
return ResponseModel(
status_code=401, status=False, msg_en="Incorrect password format", msg_zh="密码格式不正确"
)
if not result:
return ResponseModel(
status_code=401, status=False, msg_en="User not found", msg_zh="用户不存在"
Expand Down
21 changes: 18 additions & 3 deletions backend/src/module/downloader/download_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from module.conf import settings
from module.models import Bangumi, Torrent
from module.network import RequestContent
from module.utils import torrent_hash

from .path import TorrentPath

Expand Down Expand Up @@ -120,21 +121,35 @@ def add_torrent(self, torrent: Torrent | list, bangumi: Bangumi) -> bool:
with RequestContent() as req:
if isinstance(torrent, list):
if len(torrent) == 0:
logger.debug(f"[Downloader] No torrent found: {bangumi.official_title}")
logger.debug(
f"[Downloader] No torrent found: {bangumi.official_title}"
)
return False
if "magnet" in torrent[0].url:
torrent_url = [t.url for t in torrent]
torrent_url = []
for t in torrent:
torrent_url.append(t.url)
t.hash = torrent_hash.from_magnet(t.url)
torrent_file = None
else:
torrent_file = [req.get_content(t.url) for t in torrent]
torrent_file = []
for t in torrent:
content = req.get_content(t.url)
torrent_file.append(content)
t.hash = torrent_hash.from_torrent(content)
torrent_url = None
for t in torrent:
t.bangumi_id = bangumi.id
else:
if "magnet" in torrent.url:
torrent_url = torrent.url
torrent.hash = torrent_hash.from_magnet(torrent.url)
torrent_file = None
else:
torrent_file = req.get_content(torrent.url)
torrent.hash = torrent_hash.from_torrent(torrent_file)
torrent_url = None
torrent.bangumi_id = bangumi.id
if self.client.add_torrents(
torrent_urls=torrent_url,
torrent_files=torrent_file,
Expand Down
2 changes: 1 addition & 1 deletion backend/src/module/manager/collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ def subscribe_season(data: Bangumi):
engine.add_rss(
rss_link=data.rss_link, name=data.official_title, aggregate=False
)
result = engine.download_bangumi(data)
engine.bangumi.add(data)
result = engine.download_bangumi(data)
return result


Expand Down
71 changes: 44 additions & 27 deletions backend/src/module/manager/renamer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from module.downloader import DownloadClient
from module.models import EpisodeFile, Notification, SubtitleFile
from module.parser import TitleParser
from module.database import Database

logger = logging.getLogger(__name__)

Expand All @@ -25,9 +26,13 @@ def print_result(torrent_count, rename_count):

@staticmethod
def gen_path(
file_info: EpisodeFile | SubtitleFile, bangumi_name: str, method: str
file_info: EpisodeFile | SubtitleFile,
bangumi_name: str,
method: str,
offset: int,
) -> str:
season = f"0{file_info.season}" if file_info.season < 10 else file_info.season
file_info.episode += offset
episode = (
f"0{file_info.episode}" if file_info.episode < 10 else file_info.episode
)
Expand All @@ -49,22 +54,23 @@ def gen_path(
return file_info.media_path

def rename_file(
self,
torrent_name: str,
media_path: str,
bangumi_name: str,
method: str,
season: int,
_hash: str,
**kwargs,
self,
torrent_name: str,
media_path: str,
bangumi_name: str,
method: str,
season: int,
_hash: str,
offset: int,
**kwargs,
):
ep = self._parser.torrent_parser(
torrent_name=torrent_name,
torrent_path=media_path,
season=season,
)
if ep:
new_path = self.gen_path(ep, bangumi_name, method=method)
new_path = self.gen_path(ep, bangumi_name, method=method, offset=offset)
if media_path != new_path:
if new_path not in self.check_pool.keys():
if self.rename_torrent_file(
Expand All @@ -82,13 +88,14 @@ def rename_file(
return None

def rename_collection(
self,
media_list: list[str],
bangumi_name: str,
season: int,
method: str,
_hash: str,
**kwargs,
self,
media_list: list[str],
bangumi_name: str,
season: int,
method: str,
_hash: str,
offset: int,
**kwargs,
):
for media_path in media_list:
if self.is_ep(media_path):
Expand All @@ -97,7 +104,9 @@ def rename_collection(
season=season,
)
if ep:
new_path = self.gen_path(ep, bangumi_name, method=method)
new_path = self.gen_path(
ep, bangumi_name, method=method, offset=offset
)
if media_path != new_path:
renamed = self.rename_torrent_file(
_hash=_hash, old_path=media_path, new_path=new_path
Expand All @@ -110,14 +119,15 @@ def rename_collection(
break

def rename_subtitles(
self,
subtitle_list: list[str],
torrent_name: str,
bangumi_name: str,
season: int,
method: str,
_hash,
**kwargs,
self,
subtitle_list: list[str],
torrent_name: str,
bangumi_name: str,
season: int,
method: str,
_hash: str,
offset: int,
**kwargs,
):
method = "subtitle_" + method
for subtitle_path in subtitle_list:
Expand All @@ -128,7 +138,9 @@ def rename_subtitles(
file_type="subtitle",
)
if sub:
new_path = self.gen_path(sub, bangumi_name, method=method)
new_path = self.gen_path(
sub, bangumi_name, method=method, offset=offset
)
if subtitle_path != new_path:
renamed = self.rename_torrent_file(
_hash=_hash, old_path=subtitle_path, new_path=new_path
Expand All @@ -151,7 +163,12 @@ def rename(self) -> list[Notification]:
"method": rename_method,
"season": season,
"_hash": info.hash,
"offset": 0,
}
with Database() as db:
bangumi_id = db.torrent.get_bangumi_id(info.hash)
if bangumi_id:
kwargs["offset"] = db.bangumi.get_offset(bangumi_id)
# Rename single media file
if len(media_list) == 1:
notify_info = self.rename_file(media_path=media_list[0], **kwargs)
Expand Down
1 change: 1 addition & 0 deletions backend/src/module/manager/torrent.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def delete_rule(self, _id: int | str, file: bool = False):
with DownloadClient() as client:
self.rss.delete(data.official_title)
self.bangumi.delete_one(int(_id))
self.torrent.delete_by_bangumi_id(int(_id))
if file:
torrent_message = self.delete_torrents(data, client)
logger.info(f"[Manager] Delete rule for {data.official_title}")
Expand Down
1 change: 1 addition & 0 deletions backend/src/module/models/torrent.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class Torrent(SQLModel, table=True):
url: str = Field("https://example.com/torrent", alias="url")
homepage: Optional[str] = Field(None, alias="homepage")
downloaded: bool = Field(False, alias="downloaded")
hash: Optional[str] = Field(None, alias="hash")


class TorrentUpdate(SQLModel):
Expand Down
2 changes: 1 addition & 1 deletion backend/src/module/update/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .cross_version import from_30_to_31, cache_image
from .data_migration import data_migration
from .data_migration import data_migration, torrent_migration
from .startup import first_run, start_up
from .version_check import version_check
34 changes: 32 additions & 2 deletions backend/src/module/update/data_migration.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import logging

from module.conf import LEGACY_DATA_PATH
from module.models import Bangumi
from module.models import Bangumi, Torrent
from module.rss import RSSEngine
from module.utils import json_config
from module.utils import json_config, torrent_hash
from module.network import RequestContent

logger = logging.getLogger(__name__)


def data_migration():
Expand All @@ -22,3 +27,28 @@ def data_migration():
def database_migration():
with RSSEngine() as engine:
engine.migrate()


def torrent_migration():
with RSSEngine() as db, RequestContent() as req:
engine = db.engine
torrents = db.exec("SELECT * FROM torrent").all()
torrents = [dict(torrent) for torrent in torrents]
for torrent in torrents:
torrent["refer_id"] = torrent["bangumi_id"]
if torrent.get("hash") or torrent.get("bangumi_id") is None:
continue
logger.debug(f"Get {torrent['name']} Hash")
url = torrent["url"]
if url.startswith("magnet"):
info_hash = torrent_hash.from_magnet(url)
else:
content = req.get_content(url)
info_hash = torrent_hash.from_torrent(content)
torrent["hash"] = info_hash
readd_torrents = [Torrent(**torrent) for torrent in torrents]
Torrent.__table__.drop(engine)
Torrent.__table__.create(engine)
db.commit()
db.torrent.add_all(readd_torrents)
db.commit()
Loading