mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
Bugfix/tachiyomi sync issue (#1002)
* Added extra unit cases for GetContinuePoint. Fixed a bug where if the series was just read chapters, the first chapter wouldn't be returned and would throw an error. * Wrote unit tests for MarkChaptersUntilAsRead for Tachiyomi and fixed a few cases where due to tracking on Tachiyomi, Volumes with a single 0 chapter would get marked as read.
This commit is contained in:
parent
c9bd1d1bfb
commit
567d475e46
@ -1007,6 +1007,282 @@ public class ReaderServiceTests
|
|||||||
Assert.Equal("1", nextChapter.Range);
|
Assert.Equal("1", nextChapter.Range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetContinuePoint_ShouldReturnFirstChapter_WhenAllReadAndAllChapters()
|
||||||
|
{
|
||||||
|
_context.Series.Add(new Series()
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
Library = new Library() {
|
||||||
|
Name = "Test LIb",
|
||||||
|
Type = LibraryType.Manga,
|
||||||
|
},
|
||||||
|
Volumes = new List<Volume>()
|
||||||
|
{
|
||||||
|
EntityFactory.CreateVolume("0", new List<Chapter>()
|
||||||
|
{
|
||||||
|
EntityFactory.CreateChapter("1", false, new List<MangaFile>(), 1),
|
||||||
|
EntityFactory.CreateChapter("2", false, new List<MangaFile>(), 1),
|
||||||
|
EntityFactory.CreateChapter("3", false, new List<MangaFile>(), 1),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_context.AppUser.Add(new AppUser()
|
||||||
|
{
|
||||||
|
UserName = "majora2007"
|
||||||
|
});
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var fileSystem = new MockFileSystem();
|
||||||
|
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), fileSystem);
|
||||||
|
var cs = new CacheService(_logger, _unitOfWork, ds, new MockReadingItemServiceForCacheService(ds));
|
||||||
|
var readerService = new ReaderService(_unitOfWork, Substitute.For<ILogger<ReaderService>>(), ds, cs);
|
||||||
|
|
||||||
|
// Save progress on first volume chapters and 1st of second volume
|
||||||
|
await readerService.SaveReadingProgress(new ProgressDto()
|
||||||
|
{
|
||||||
|
PageNum = 1,
|
||||||
|
ChapterId = 1,
|
||||||
|
SeriesId = 1,
|
||||||
|
VolumeId = 1
|
||||||
|
}, 1);
|
||||||
|
await readerService.SaveReadingProgress(new ProgressDto()
|
||||||
|
{
|
||||||
|
PageNum = 1,
|
||||||
|
ChapterId = 2,
|
||||||
|
SeriesId = 1,
|
||||||
|
VolumeId = 1
|
||||||
|
}, 1);
|
||||||
|
await readerService.SaveReadingProgress(new ProgressDto()
|
||||||
|
{
|
||||||
|
PageNum = 1,
|
||||||
|
ChapterId = 3,
|
||||||
|
SeriesId = 1,
|
||||||
|
VolumeId = 1
|
||||||
|
}, 1);
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
var nextChapter = await readerService.GetContinuePoint(1, 1);
|
||||||
|
|
||||||
|
Assert.Equal("1", nextChapter.Range);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetContinuePoint_ShouldReturnFirstSpecial_WhenAllReadAndAllChapters()
|
||||||
|
{
|
||||||
|
_context.Series.Add(new Series()
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
Library = new Library() {
|
||||||
|
Name = "Test LIb",
|
||||||
|
Type = LibraryType.Manga,
|
||||||
|
},
|
||||||
|
Volumes = new List<Volume>()
|
||||||
|
{
|
||||||
|
EntityFactory.CreateVolume("0", new List<Chapter>()
|
||||||
|
{
|
||||||
|
EntityFactory.CreateChapter("1", false, new List<MangaFile>(), 1),
|
||||||
|
EntityFactory.CreateChapter("2", false, new List<MangaFile>(), 1),
|
||||||
|
EntityFactory.CreateChapter("3", false, new List<MangaFile>(), 1),
|
||||||
|
EntityFactory.CreateChapter("Some Special Title", true, new List<MangaFile>(), 1),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_context.AppUser.Add(new AppUser()
|
||||||
|
{
|
||||||
|
UserName = "majora2007"
|
||||||
|
});
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var fileSystem = new MockFileSystem();
|
||||||
|
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), fileSystem);
|
||||||
|
var cs = new CacheService(_logger, _unitOfWork, ds, new MockReadingItemServiceForCacheService(ds));
|
||||||
|
var readerService = new ReaderService(_unitOfWork, Substitute.For<ILogger<ReaderService>>(), ds, cs);
|
||||||
|
|
||||||
|
// Save progress on first volume chapters and 1st of second volume
|
||||||
|
await readerService.SaveReadingProgress(new ProgressDto()
|
||||||
|
{
|
||||||
|
PageNum = 1,
|
||||||
|
ChapterId = 1,
|
||||||
|
SeriesId = 1,
|
||||||
|
VolumeId = 1
|
||||||
|
}, 1);
|
||||||
|
await readerService.SaveReadingProgress(new ProgressDto()
|
||||||
|
{
|
||||||
|
PageNum = 1,
|
||||||
|
ChapterId = 2,
|
||||||
|
SeriesId = 1,
|
||||||
|
VolumeId = 1
|
||||||
|
}, 1);
|
||||||
|
await readerService.SaveReadingProgress(new ProgressDto()
|
||||||
|
{
|
||||||
|
PageNum = 1,
|
||||||
|
ChapterId = 3,
|
||||||
|
SeriesId = 1,
|
||||||
|
VolumeId = 1
|
||||||
|
}, 1);
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
var nextChapter = await readerService.GetContinuePoint(1, 1);
|
||||||
|
|
||||||
|
Assert.Equal("Some Special Title", nextChapter.Range);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region MarkChaptersUntilAsRead
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MarkChaptersUntilAsRead_ShouldMarkAllChaptersAsRead()
|
||||||
|
{
|
||||||
|
_context.Series.Add(new Series()
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
Library = new Library() {
|
||||||
|
Name = "Test LIb",
|
||||||
|
Type = LibraryType.Manga,
|
||||||
|
},
|
||||||
|
Volumes = new List<Volume>()
|
||||||
|
{
|
||||||
|
EntityFactory.CreateVolume("0", new List<Chapter>()
|
||||||
|
{
|
||||||
|
EntityFactory.CreateChapter("1", false, new List<MangaFile>(), 1),
|
||||||
|
EntityFactory.CreateChapter("2", false, new List<MangaFile>(), 1),
|
||||||
|
EntityFactory.CreateChapter("3", false, new List<MangaFile>(), 1),
|
||||||
|
EntityFactory.CreateChapter("Some Special Title", true, new List<MangaFile>(), 1),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_context.AppUser.Add(new AppUser()
|
||||||
|
{
|
||||||
|
UserName = "majora2007"
|
||||||
|
});
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var fileSystem = new MockFileSystem();
|
||||||
|
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), fileSystem);
|
||||||
|
var cs = new CacheService(_logger, _unitOfWork, ds, new MockReadingItemServiceForCacheService(ds));
|
||||||
|
var readerService = new ReaderService(_unitOfWork, Substitute.For<ILogger<ReaderService>>(), ds, cs);
|
||||||
|
|
||||||
|
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
|
||||||
|
await readerService.MarkChaptersUntilAsRead(user, 1, 5);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
// Validate correct chapters have read status
|
||||||
|
Assert.Equal(1, (await _unitOfWork.AppUserProgressRepository.GetUserProgressAsync(1, 1)).PagesRead);
|
||||||
|
Assert.Equal(1, (await _unitOfWork.AppUserProgressRepository.GetUserProgressAsync(2, 1)).PagesRead);
|
||||||
|
Assert.Equal(1, (await _unitOfWork.AppUserProgressRepository.GetUserProgressAsync(3, 1)).PagesRead);
|
||||||
|
Assert.Null((await _unitOfWork.AppUserProgressRepository.GetUserProgressAsync(4, 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MarkChaptersUntilAsRead_ShouldMarkUptTillChapterNumberAsRead()
|
||||||
|
{
|
||||||
|
_context.Series.Add(new Series()
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
Library = new Library() {
|
||||||
|
Name = "Test LIb",
|
||||||
|
Type = LibraryType.Manga,
|
||||||
|
},
|
||||||
|
Volumes = new List<Volume>()
|
||||||
|
{
|
||||||
|
EntityFactory.CreateVolume("0", new List<Chapter>()
|
||||||
|
{
|
||||||
|
EntityFactory.CreateChapter("1", false, new List<MangaFile>(), 1),
|
||||||
|
EntityFactory.CreateChapter("2", false, new List<MangaFile>(), 1),
|
||||||
|
EntityFactory.CreateChapter("2.5", false, new List<MangaFile>(), 1),
|
||||||
|
EntityFactory.CreateChapter("3", false, new List<MangaFile>(), 1),
|
||||||
|
EntityFactory.CreateChapter("Some Special Title", true, new List<MangaFile>(), 1),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_context.AppUser.Add(new AppUser()
|
||||||
|
{
|
||||||
|
UserName = "majora2007"
|
||||||
|
});
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var fileSystem = new MockFileSystem();
|
||||||
|
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), fileSystem);
|
||||||
|
var cs = new CacheService(_logger, _unitOfWork, ds, new MockReadingItemServiceForCacheService(ds));
|
||||||
|
var readerService = new ReaderService(_unitOfWork, Substitute.For<ILogger<ReaderService>>(), ds, cs);
|
||||||
|
|
||||||
|
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
|
||||||
|
await readerService.MarkChaptersUntilAsRead(user, 1, 2.5f);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
// Validate correct chapters have read status
|
||||||
|
Assert.Equal(1, (await _unitOfWork.AppUserProgressRepository.GetUserProgressAsync(1, 1)).PagesRead);
|
||||||
|
Assert.Equal(1, (await _unitOfWork.AppUserProgressRepository.GetUserProgressAsync(2, 1)).PagesRead);
|
||||||
|
Assert.Equal(1, (await _unitOfWork.AppUserProgressRepository.GetUserProgressAsync(3, 1)).PagesRead);
|
||||||
|
Assert.Null((await _unitOfWork.AppUserProgressRepository.GetUserProgressAsync(4, 1)));
|
||||||
|
Assert.Null((await _unitOfWork.AppUserProgressRepository.GetUserProgressAsync(5, 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MarkChaptersUntilAsRead_ShouldNotReadOnlyVolumesWithChapter0()
|
||||||
|
{
|
||||||
|
_context.Series.Add(new Series()
|
||||||
|
{
|
||||||
|
Name = "Test",
|
||||||
|
Library = new Library() {
|
||||||
|
Name = "Test LIb",
|
||||||
|
Type = LibraryType.Manga,
|
||||||
|
},
|
||||||
|
Volumes = new List<Volume>()
|
||||||
|
{
|
||||||
|
EntityFactory.CreateVolume("1", new List<Chapter>()
|
||||||
|
{
|
||||||
|
EntityFactory.CreateChapter("0", false, new List<MangaFile>(), 1),
|
||||||
|
}),
|
||||||
|
EntityFactory.CreateVolume("2", new List<Chapter>()
|
||||||
|
{
|
||||||
|
EntityFactory.CreateChapter("0", false, new List<MangaFile>(), 1),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_context.AppUser.Add(new AppUser()
|
||||||
|
{
|
||||||
|
UserName = "majora2007"
|
||||||
|
});
|
||||||
|
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var fileSystem = new MockFileSystem();
|
||||||
|
var ds = new DirectoryService(Substitute.For<ILogger<DirectoryService>>(), fileSystem);
|
||||||
|
var cs = new CacheService(_logger, _unitOfWork, ds, new MockReadingItemServiceForCacheService(ds));
|
||||||
|
var readerService = new ReaderService(_unitOfWork, Substitute.For<ILogger<ReaderService>>(), ds, cs);
|
||||||
|
|
||||||
|
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync("majora2007", AppUserIncludes.Progress);
|
||||||
|
await readerService.MarkChaptersUntilAsRead(user, 1, 2);
|
||||||
|
await _context.SaveChangesAsync();
|
||||||
|
|
||||||
|
// Validate correct chapters have read status
|
||||||
|
Assert.False(await _unitOfWork.AppUserProgressRepository.UserHasProgress(LibraryType.Manga, 1));
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
@ -392,12 +392,7 @@ namespace API.Controllers
|
|||||||
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Progress);
|
var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername(), AppUserIncludes.Progress);
|
||||||
user.Progresses ??= new List<AppUserProgress>();
|
user.Progresses ??= new List<AppUserProgress>();
|
||||||
|
|
||||||
var volumes = await _unitOfWork.VolumeRepository.GetVolumesForSeriesAsync(new List<int>() { seriesId }, true);
|
await _readerService.MarkChaptersUntilAsRead(user, seriesId, chapterNumber);
|
||||||
foreach (var volume in volumes.OrderBy(v => v.Number))
|
|
||||||
{
|
|
||||||
var chapters = volume.Chapters.OrderBy(c => float.Parse(c.Number)).Where(c => !c.IsSpecial && Parser.Parser.MaximumNumberFromRange(c.Range) <= chapterNumber);
|
|
||||||
_readerService.MarkChaptersAsRead(user, volume.SeriesId, chapters);
|
|
||||||
}
|
|
||||||
|
|
||||||
_unitOfWork.UserRepository.Update(user);
|
_unitOfWork.UserRepository.Update(user);
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ public interface IReaderService
|
|||||||
Task<int> GetNextChapterIdAsync(int seriesId, int volumeId, int currentChapterId, int userId);
|
Task<int> GetNextChapterIdAsync(int seriesId, int volumeId, int currentChapterId, int userId);
|
||||||
Task<int> GetPrevChapterIdAsync(int seriesId, int volumeId, int currentChapterId, int userId);
|
Task<int> GetPrevChapterIdAsync(int seriesId, int volumeId, int currentChapterId, int userId);
|
||||||
Task<ChapterDto> GetContinuePoint(int seriesId, int userId);
|
Task<ChapterDto> GetContinuePoint(int seriesId, int userId);
|
||||||
|
Task MarkChaptersUntilAsRead(AppUser user, int seriesId, float chapterNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ReaderService : IReaderService
|
public class ReaderService : IReaderService
|
||||||
@ -317,27 +318,22 @@ public class ReaderService : IReaderService
|
|||||||
.OrderBy(c => float.Parse(c.Number))
|
.OrderBy(c => float.Parse(c.Number))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var currentlyReadingChapter = nonSpecialChapters.FirstOrDefault(chapter => chapter.PagesRead < chapter.Pages);
|
var currentlyReadingChapter = nonSpecialChapters.FirstOrDefault(chapter => chapter.PagesRead < chapter.Pages);
|
||||||
|
|
||||||
|
|
||||||
// Check if there are any specials
|
if (currentlyReadingChapter != null) return currentlyReadingChapter;
|
||||||
if (currentlyReadingChapter == null)
|
|
||||||
{
|
|
||||||
var volume = volumes.SingleOrDefault(v => v.Number == 0);
|
|
||||||
if (volume == null) return nonSpecialChapters.First();
|
|
||||||
|
|
||||||
foreach (var chapter in volume.Chapters.OrderBy(c => float.Parse(c.Number)))
|
// Check if there are any specials
|
||||||
{
|
var volume = volumes.SingleOrDefault(v => v.Number == 0);
|
||||||
if (chapter.PagesRead < chapter.Pages)
|
if (volume == null) return nonSpecialChapters.First();
|
||||||
{
|
|
||||||
return chapter;
|
var chapters = volume.Chapters.OrderBy(c => float.Parse(c.Number)).ToList();
|
||||||
}
|
foreach (var chapter in chapters.Where(chapter => chapter.PagesRead < chapter.Pages))
|
||||||
}
|
{
|
||||||
|
return chapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
return currentlyReadingChapter ?? nonSpecialChapters.First();
|
return chapters.First();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -357,5 +353,23 @@ public class ReaderService : IReaderService
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Marks every chapter that is sorted below the passed number as Read. This will not mark any specials as read or Volumes with a single 0 chapter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user"></param>
|
||||||
|
/// <param name="seriesId"></param>
|
||||||
|
/// <param name="chapterNumber"></param>
|
||||||
|
public async Task MarkChaptersUntilAsRead(AppUser user, int seriesId, float chapterNumber)
|
||||||
|
{
|
||||||
|
var volumes = await _unitOfWork.VolumeRepository.GetVolumesForSeriesAsync(new List<int>() { seriesId }, true);
|
||||||
|
foreach (var volume in volumes.OrderBy(v => v.Number))
|
||||||
|
{
|
||||||
|
var chapters = volume.Chapters
|
||||||
|
.OrderBy(c => float.Parse(c.Number))
|
||||||
|
.Where(c => !c.IsSpecial && Parser.Parser.MaximumNumberFromRange(c.Range) <= chapterNumber && Parser.Parser.MaximumNumberFromRange(c.Range) > 0.0);
|
||||||
|
MarkChaptersAsRead(user, volume.SeriesId, chapters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user