feat: include title in watchlist csv export

This commit is contained in:
2026-05-07 00:06:18 +02:00
parent 5ee16a19ef
commit 43de6544fe
3 changed files with 27 additions and 12 deletions

View File

@@ -210,23 +210,36 @@ func (s *Service) ImportWatchlist(ctx context.Context, userID string, r io.Reade
}
for i, record := range records {
if len(record) < 4 {
log.Printf("skipping row %d: insufficient columns", i+2) // i+2 because i is 0-indexed record after header
// New format: anime_id,title,status,current_episode,current_time_seconds
// Old format: anime_id,status,current_episode,current_time_seconds
var animeIDStr, status, episodeStr, timeStr string
if len(record) >= 5 {
animeIDStr = record[0]
status = record[2]
episodeStr = record[3]
timeStr = record[4]
} else if len(record) >= 4 {
animeIDStr = record[0]
status = record[1]
episodeStr = record[2]
timeStr = record[3]
} else {
log.Printf("skipping row %d: insufficient columns", i+2)
continue
}
animeID, err := strconv.ParseInt(record[0], 10, 64)
animeID, err := strconv.ParseInt(animeIDStr, 10, 64)
if err != nil {
return fmt.Errorf("row %d: invalid anime id: %w", i+2, err)
}
status := record[1]
if _, ok := validStatuses[status]; !ok {
status = "plan_to_watch"
}
currentEpisode, _ := strconv.ParseInt(record[2], 10, 64)
currentTimeSeconds, _ := strconv.ParseFloat(record[3], 64)
currentEpisode, _ := strconv.ParseInt(episodeStr, 10, 64)
currentTimeSeconds, _ := strconv.ParseFloat(timeStr, 64)
if err := s.ensureAnimeExists(ctx, animeID); err != nil {
return fmt.Errorf("row %d: failed to ensure anime: %w", i+2, err)

View File

@@ -59,7 +59,7 @@
<div id="watchlist-items" class="grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4 2xl:grid-cols-6">
{{range $.AllEntries}}
<div class="watchlist-item flex w-full flex-col gap-2" data-status="{{.Status}}" data-updated-at="{{.UpdatedAt.Unix}}" data-episode="{{.CurrentEpisode.Int64}}" data-time="{{.CurrentTimeSeconds}}">
<div class="watchlist-item flex w-full flex-col gap-2" data-status="{{.Status}}" data-updated-at="{{.UpdatedAt.Unix}}" data-episode="{{.CurrentEpisode.Int64}}" data-time="{{.CurrentTimeSeconds}}" data-title="{{.DisplayTitle}}">
<div class="group relative flex aspect-2/3 w-full flex-col overflow-hidden bg-white/5 after:absolute after:inset-0 after:bg-black/80 after:opacity-0 hover:after:opacity-100 after:transition-opacity">
<a href="/anime/{{.AnimeID}}" class="absolute inset-0 z-10"></a>
<img src="{{.ImageUrl}}" alt="{{.DisplayTitle}}" class="h-full w-full object-cover" loading="lazy" />
@@ -154,13 +154,14 @@
return;
}
let csv = 'anime_id,status,current_episode,current_time_seconds\n';
let csv = 'anime_id,title,status,current_episode,current_time_seconds\n';
items.forEach(function(item) {
const animeId = item.querySelector('a').href.split('/').pop();
const title = (item.dataset.title || '').replace(/"/g, '""');
const status = item.dataset.status || 'plan_to_watch';
const episode = item.dataset.episode || '0';
const time = item.dataset.time || '0';
csv += `${animeId},${status},${episode},${time}\n`;
csv += `${animeId},"${title}",${status},${episode},${time}\n`;
});
const blob = new Blob([csv], { type: 'text/csv' });

View File

@@ -62,7 +62,7 @@
<div class="watchlist-section" data-status="{{$status}}">
<div class="grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-4 2xl:grid-cols-6">
{{range $entries}}
<div class="watchlist-item flex w-full flex-col gap-2" data-status="{{.Status}}" data-updated-at="{{.UpdatedAt.Unix}}" data-episode="{{.CurrentEpisode.Int64}}" data-time="{{.CurrentTimeSeconds}}">
<div class="watchlist-item flex w-full flex-col gap-2" data-status="{{.Status}}" data-updated-at="{{.UpdatedAt.Unix}}" data-episode="{{.CurrentEpisode.Int64}}" data-time="{{.CurrentTimeSeconds}}" data-title="{{.DisplayTitle}}">
<div class="group relative flex aspect-2/3 w-full flex-col overflow-hidden bg-white/5 after:absolute after:inset-0 after:bg-black/80 after:opacity-0 hover:after:opacity-100 after:transition-opacity">
<a href="/anime/{{.AnimeID}}" class="absolute inset-0"></a>
<img src="{{.ImageUrl}}" alt="{{.DisplayTitle}}" class="h-full w-full object-cover" loading="lazy" />
@@ -155,13 +155,14 @@
return;
}
let csv = 'anime_id,status,current_episode,current_time_seconds\n';
let csv = 'anime_id,title,status,current_episode,current_time_seconds\n';
items.forEach(function(item) {
const animeId = item.querySelector('a').href.split('/').pop();
const title = (item.dataset.title || '').replace(/"/g, '""');
const status = item.dataset.status || 'plan_to_watch';
const episode = item.dataset.episode || '0';
const time = item.dataset.time || '0';
csv += `${animeId},${status},${episode},${time}\n`;
csv += `${animeId},"${title}",${status},${episode},${time}\n`;
});
const blob = new Blob([csv], { type: 'text/csv' });