feat: include title in watchlist csv export
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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' });
|
||||
|
||||
@@ -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' });
|
||||
|
||||
Reference in New Issue
Block a user