Skip to content

Commit

Permalink
optimize: subject episode attachment matching operate. (#581)
Browse files Browse the repository at this point in the history
* feat: 当条目同步窗口只有一个第三方的时候,自动选择该第三方 #580

* feat: 在条目新增时,检测到剧集没有条目,自动创建个默认的剧集条目 #579

* feat: 当条目没有剧集的时候,批量绑定按钮加个禁用和提示 #578

* docs: update CHANGELOG.MD

* feat: 条目被移除时,条目的封面附件未被移除

* fix: 小说条目绑定资源提示非视频媒体 #576
feat: 剧集批量附件绑定,支持文件名更多格式:` 01 ` `[01]` `EP01` `-01-` `_01_`

* docs: update CHANGELOG.MD

* build: change version to 0.12.2
  • Loading branch information
chivehao committed Jun 16, 2024
1 parent 8d417af commit 5b67bee
Show file tree
Hide file tree
Showing 15 changed files with 208 additions and 10 deletions.
4 changes: 2 additions & 2 deletions BUILD.MD
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ commit之前,用checkstyle检查下代码,确认没有问题后再commit。

## 本地开发

`IkarosApplication`的运行配置里,将`Active profiles` 配置成:`dev,win,local`
`IkarosApplication`的运行配置里,将`Active profiles` 配置成:`dev,win-dev,local`

这里的`local`请先确保您已经进行了上面的`application-local.yaml.example`实例文件复制移动重命名

Expand Down Expand Up @@ -120,7 +120,7 @@ java -jar ./ikaros-server.jar
在打包文件所在目录,`Windwos` 运行,
需要加上额外参数`--spring.profiles.active=win`
```shell
java -jar ikaros-server.jar --spring.profiles.active=win
java -jar ikaros-server.jar --spring.profiles.active=win
```

# 提交PR后
Expand Down
8 changes: 7 additions & 1 deletion CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@

更新日志文档,版本顺序从新到旧,最新版本在最前(上)面。

# 0.13.0
# 0.12.2

## 问题修复

- 条目番组计划平台的收藏进度无法同步 #4
- 条目被移除时,条目的封面附件未被移除
- 小说条目绑定资源提示非视频媒体 #576

## 优化

- 优化开发文档和本地开发配置
- 当条目同步窗口只有一个第三方的时候,自动选择该第三方 #580
- 在条目新增时,检测到剧集没有条目,自动创建个默认的剧集条目 #579
- 当条目没有剧集的时候,批量绑定按钮加个禁用和提示 #578
- 优化条目剧集的附件绑定操作 #577 ,支持文件名更多格式:` 01 ` `[01]` `EP01` `-01-` `_01_`

# 0.12.1

Expand Down
17 changes: 17 additions & 0 deletions api/src/main/java/run/ikaros/api/core/subject/Episode.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,21 @@ public class Episode {
private EpisodeGroup group;

private List<EpisodeResource> resources;

/**
* Create a default episode instance.
*/
public static Episode defaultEpisode(Long subjectId) {
Episode episode = new Episode();
episode.setAirTime(LocalDateTime.now());
episode.setSequence(1);
episode.setGroup(EpisodeGroup.MAIN);
episode.setDescription("Default episode description");
episode.setName("Default episode name");
episode.setNameCn("默认的剧集名称");
if (subjectId != null) {
episode.setSubjectId(subjectId);
}
return episode;
}
}
6 changes: 6 additions & 0 deletions api/src/main/java/run/ikaros/api/infra/utils/FileUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,12 @@ private static void deleteFileAndChild(File parentFile) throws IOException {
if (parentFile == null) {
return;
}

if (parentFile.isFile()) {
Files.deleteIfExists(parentFile.toPath());
log.debug("Delete file in path: {}", parentFile.toPath());
}

if (parentFile.listFiles() == null) {
return;
}
Expand Down
2 changes: 2 additions & 0 deletions api/src/main/java/run/ikaros/api/infra/utils/RegexConst.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public interface RegexConst {
String FILE_NAME_TAG_EPISODE_SEQUENCE_WITH_BRACKETS = "\\[[0-9]{1,2}\\]";
String FILE_NAME_EPISODE_SEQUENCE_WITH_BLANK = "\\s{1}[0-9]{1,2}\\s{1}";
String FILE_NAME_EPISODE_SEQUENCE_WITH_INTEGRALLY_AND_BLANK = "\\s{1}EP[0-9]{1,2}\\s{1}";
String FILE_NAME_EPISODE_SEQUENCE_WITH_HORIZONTAL = "\\-[0-9]{1,2}\\-";
String FILE_NAME_EPISODE_SEQUENCE_WITH_UNDERLINE = "\\_[0-9]{1,2}\\_";
String FILE_NAME_TAG = "\\[[^\\[^\\]]+\\]";
String BRACKETS = "\\([^\\(^\\)]+\\)";
String FILE_POSTFIX = "\\.[A-Za-z0-9_-]+$";
Expand Down
64 changes: 63 additions & 1 deletion api/src/main/java/run/ikaros/api/infra/utils/RegexUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,60 @@ public static Long getFileNameBlankEpSeq(@Nonnull final String fileName) {
.orElse(null);
}

/**
* Get episode seq by file name, such as: xxxx-04-xxxx.mp4 => 04 .
*/
@Nonnull
public static Long getFileNameHorizontalEpSeq(@Nonnull final String fileName) {
AssertUtils.notBlank(fileName, "fileName");
Set<String> strSet = new HashSet<>();

Matcher matcher =
Pattern.compile(RegexConst.FILE_NAME_EPISODE_SEQUENCE_WITH_HORIZONTAL)
.matcher(fileName);
while (matcher.find()) {
strSet.add(matcher.group());
}

if (strSet.isEmpty()) {
throw new RegexMatchingException(
"file name episode seq matching exception , file name: "
+ fileName);
}

return strSet.stream().findFirst()
.map(str -> str.replace("-", ""))
.map(Long::parseLong)
.orElse(null);
}

/**
* Get episode seq by file name, such as: xxxx_04_xxxx.mp4 => 04 .
*/
@Nonnull
public static Long getFileNameUnderlineEpSeq(@Nonnull final String fileName) {
AssertUtils.notBlank(fileName, "fileName");
Set<String> strSet = new HashSet<>();

Matcher matcher =
Pattern.compile(RegexConst.FILE_NAME_EPISODE_SEQUENCE_WITH_UNDERLINE)
.matcher(fileName);
while (matcher.find()) {
strSet.add(matcher.group());
}

if (strSet.isEmpty()) {
throw new RegexMatchingException(
"file name episode seq matching exception , file name: "
+ fileName);
}

return strSet.stream().findFirst()
.map(str -> str.replace("_", ""))
.map(Long::parseLong)
.orElse(null);
}

/**
* Get episode seq by file name and ep integrally, such as: xxxx EP04 xxxx.mp4 => 04 .
*/
Expand Down Expand Up @@ -252,7 +306,15 @@ public static Long parseEpisodeSeqByFileName(String fileName) {
try {
seq = getEpFileNameIntegrallySeq(fileName);
} catch (RegexMatchingException e3) {
log.warn("parse episode seq fail by file name: {}.", fileName, e3);
try {
seq = getFileNameHorizontalEpSeq(fileName);
} catch (RegexMatchingException e4) {
try {
seq = getFileNameUnderlineEpSeq(fileName);
} catch (RegexMatchingException e5) {
log.warn("parse episode seq fail by file name: {}.", fileName, e5);
}
}
}
}
}
Expand Down
34 changes: 34 additions & 0 deletions api/src/test/java/run/ikaros/api/infra/utils/RegexConstTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,38 @@ void testFileNameEpSeqWithBlank() {

Assertions.assertThat(strSet).isNotEmpty();
}

@Test
void testFileNameEpSeqWithHorizontal() {
String fileName =
"[ANi] Reign of the Seven Spellblades "
+ "- 七魔剑支配天下 -03- [1080P][Baha][WEB-DL][AAC AVC][CHT][MP4]";
Set<String> strSet = new HashSet<>();

Matcher matcher =
Pattern.compile(RegexConst.FILE_NAME_EPISODE_SEQUENCE_WITH_HORIZONTAL)
.matcher(fileName);
while (matcher.find()) {
strSet.add(matcher.group());
}

Assertions.assertThat(strSet).isNotEmpty();
}

@Test
void testFileNameEpSeqWithUnderline() {
String fileName =
"[ANi] Reign of the Seven Spellblades "
+ "- 七魔剑支配天下 _03_ [1080P][Baha][WEB-DL][AAC AVC][CHT][MP4]";
Set<String> strSet = new HashSet<>();

Matcher matcher =
Pattern.compile(RegexConst.FILE_NAME_EPISODE_SEQUENCE_WITH_UNDERLINE)
.matcher(fileName);
while (matcher.find()) {
strSet.add(matcher.group());
}

Assertions.assertThat(strSet).isNotEmpty();
}
}
1 change: 1 addition & 0 deletions config/server/resource/application-local.yaml.example
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ logging:
io.r2dbc: INFO
run.ikaros.server: DEBUG
run.ikaros.plugin: DEBUG
run.ikaros.api: DEBUG
org.pf4j: INFO
org.hibernate.SQL: INFO
org.hibernate.type.descriptor.sql.BasicBinder: INFO
Expand Down
12 changes: 12 additions & 0 deletions console/src/modules/content/subject/SubjectDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ const fetchSubjectById = async () => {
subject.value = await subjectStore.fetchSubjectById(
subject.value.id as number
);
if (!subject.value.episodes || subject.value.episodes.length === 0) {
batchMatchingSubjectButtonDisable.value = true;
deleteMatchingSubjectButtonDisable.value = true;
} else {
batchMatchingSubjectButtonDisable.value = false;
deleteMatchingSubjectButtonDisable.value = false;
}
}
};
Expand Down Expand Up @@ -369,6 +376,8 @@ const fetchDatas = async () => {
const bindMasterIsEpisodeFlag = ref(false);
const batchMatchingSubjectButtonLoading = ref(false);
const batchMatchingSubjectButtonDisable = ref(false);
const deleteMatchingSubjectButtonDisable = ref(false);
const batchMatchingEpisodeButtonLoading = ref(false);
const attachmentMultiSelectDialogVisible = ref(false);
const onCloseWithAttachments = async (attachments: Attachment[]) => {
Expand Down Expand Up @@ -832,6 +841,8 @@ onMounted(fetchDatas);
<el-button
plain
:loading="batchMatchingSubjectButtonLoading"
:disabled="batchMatchingSubjectButtonDisable"

@click="
() => {
attachmentMultiSelectDialogVisible = true;
Expand All @@ -855,6 +866,7 @@ onMounted(fetchDatas);
<el-button
plain
type="danger"
:disabled="deleteMatchingSubjectButtonDisable"
:loading="batchCancenMatchingSubjectButtonLoading"
>
{{
Expand Down
3 changes: 3 additions & 0 deletions console/src/modules/content/subject/SubjectSyncDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ onMounted(() => {
] as unknown as string;
subjectPlatformArr.value.push(subjectPlatform);
});
if (subjectPlatformArr.value.length == 1) {
subjectSync.value.platform = subjectPlatformArr.value[0]
}
});
</script>

Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version=0.13.0
version=0.12.2
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package run.ikaros.server.core.attachment.listener;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import run.ikaros.api.infra.utils.FileUtils;
import run.ikaros.server.core.subject.event.SubjectRemoveEvent;
import run.ikaros.server.store.entity.SubjectEntity;
import run.ikaros.server.store.repository.AttachmentRepository;

@Slf4j
@Component
public class AttachmentSubjectCoverChangeListener {
private final AttachmentRepository attachmentRepository;

public AttachmentSubjectCoverChangeListener(
AttachmentRepository attachmentRepository) {
this.attachmentRepository = attachmentRepository;
}

/**
* Construct.
*/
@EventListener(SubjectRemoveEvent.class)
public Mono<Void> onSubjectRemove(SubjectRemoveEvent event) {
SubjectEntity subjectEntity = event.getEntity();
Long subjectId = subjectEntity.getId();
String cover = subjectEntity.getCover();
if (Objects.isNull(subjectId) || subjectId < 0 || StringUtils.isBlank(cover)) {
return Mono.empty();
}
return attachmentRepository.findByUrl(cover)
.map(attachmentEntity -> {
String fsPath = attachmentEntity.getFsPath();
if (StringUtils.isBlank(fsPath) || fsPath.startsWith("http")) {
return attachmentEntity;
}
try {
FileUtils.deletePathAndContentIfExists(Path.of(fsPath));
log.debug("Delete subject cover url success in fs path for fsPath[{}].",
fsPath);
} catch (IOException e) {
log.error("Delete subject cover url failed in fs path for fsPath[{}].",
fsPath, e);
}
return attachmentEntity;
})
.flatMap(attachmentRepository::delete);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import run.ikaros.api.core.attachment.exception.AttachmentRefMatchingException;
import run.ikaros.api.infra.exception.RegexMatchingException;
import run.ikaros.api.infra.exception.subject.EpisodeNotFoundException;
import run.ikaros.api.infra.utils.FileUtils;
import run.ikaros.api.infra.utils.RegexUtils;
import run.ikaros.api.store.enums.AttachmentReferenceType;
import run.ikaros.api.store.enums.EpisodeGroup;
Expand Down Expand Up @@ -105,9 +104,6 @@ public Mono<Void> matchingAttachmentsAndSubjectEpisodes(Long subjectId, Long[] a
.flatMap(attId -> attachmentRepository.findById(attId)
.switchIfEmpty(Mono.error(new AttachmentNotFoundException(
"Check fail, current attachment not found for id=" + attId))))
.filter(entity -> FileUtils.isVideo(entity.getUrl()))
.switchIfEmpty(Mono.error(new AttachmentRefMatchingException(
"Matching fail, current attachment is not a video.")))
.flatMap(entity -> getSeqMono(entity.getName())
.flatMap(seq -> episodeRepository.findBySubjectIdAndGroupAndSequence(subjectId,
EpisodeGroup.MAIN, seq)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ public synchronized Mono<Subject> create(Subject subject) {
.map(sub -> Objects.isNull(subject.getEpisodes())
? new ArrayList<Episode>() : subject.getEpisodes())
.filter(Objects::nonNull)
.switchIfEmpty(Mono.just(new ArrayList<>()))
.filter(episodes -> !episodes.isEmpty())
.switchIfEmpty(Mono.just(List.of(Episode.defaultEpisode(subject.getId()))))
.flatMapMany(episodes -> Flux.fromStream(episodes.stream()))
.flatMap(episode -> copyProperties(episode, new EpisodeEntity()))
.map(entity -> entity.setSubjectId(subjectId.get()))
Expand Down
3 changes: 3 additions & 0 deletions server/src/main/resources/application-win-dev.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
spring:
r2dbc:
url: r2dbc:h2:file:///~/ikaros-dev/database/ikaros?MODE=MySQL&DB_CLOSE_ON_EXIT=FALSE

0 comments on commit 5b67bee

Please sign in to comment.