diff --git a/README.md b/README.md index 94e9d56..d93a459 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,8 @@ A URL management tool that helps you organize and access your links efficiently. 3. Build and start services: ```bash - ./manage-docker.sh build - ./manage-docker.sh start + ./docker.sh build + ./docker.sh start ``` The application will be available at `http://localhost:8000` diff --git a/data/db.sqlite3 b/data/db.sqlite3 index 85a1464..a8bc1c8 100644 Binary files a/data/db.sqlite3 and b/data/db.sqlite3 differ diff --git a/links/migrations/0016_migrate_existing_screenshots.py b/links/migrations/0016_migrate_existing_screenshots.py new file mode 100644 index 0000000..b0626e9 --- /dev/null +++ b/links/migrations/0016_migrate_existing_screenshots.py @@ -0,0 +1,31 @@ +from django.db import migrations + +def migrate_screenshots(apps, schema_editor): + Page = apps.get_model('links', 'Page') + Screenshot = apps.get_model('links', 'Screenshot') + + for page in Page.objects.all(): + if page.screenshot_path: + Screenshot.objects.create( + page=page, + path=page.screenshot_path, + status='completed' + ) + +def reverse_migrate(apps, schema_editor): + Page = apps.get_model('links', 'Page') + Screenshot = apps.get_model('links', 'Screenshot') + + for screenshot in Screenshot.objects.filter(status='completed'): + screenshot.page.screenshot_path = screenshot.path + screenshot.page.save() + +class Migration(migrations.Migration): + + dependencies = [ + ('links', '0015_create_screenshot_model'), + ] + + operations = [ + migrations.RunPython(migrate_screenshots, reverse_migrate), + ] diff --git a/links/migrations/0017_remove_old_screenshot_fields.py b/links/migrations/0017_remove_old_screenshot_fields.py new file mode 100644 index 0000000..5453962 --- /dev/null +++ b/links/migrations/0017_remove_old_screenshot_fields.py @@ -0,0 +1,14 @@ +from django.db import migrations + +class Migration(migrations.Migration): + + dependencies = [ + ('links', '0016_migrate_existing_screenshots'), + ] + + operations = [ + migrations.RemoveField( + model_name='page', + name='screenshot_path', + ), + ] diff --git a/links/migrations/0018_page_screenshot_path.py b/links/migrations/0018_page_screenshot_path.py new file mode 100644 index 0000000..77ffd2b --- /dev/null +++ b/links/migrations/0018_page_screenshot_path.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.9 on 2024-11-13 11:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('links', '0017_remove_old_screenshot_fields'), + ] + + operations = [ + migrations.AddField( + model_name='page', + name='screenshot_path', + field=models.CharField(blank=True, max_length=255), + ), + ] diff --git a/links/models.py b/links/models.py index db3bbcf..3ea34e2 100644 --- a/links/models.py +++ b/links/models.py @@ -177,10 +177,14 @@ class Page(models.Model): self.retry_count < 3 def get_screenshot_url(self): - if self.screenshot_path: - return f'/media/{self.screenshot_path}' + latest_screenshot = self.screenshots.first() + if latest_screenshot and latest_screenshot.path: + return os.path.join(settings.MEDIA_URL, latest_screenshot.path) return None + def get_latest_screenshot(self): + return self.screenshots.first() + class Screenshot(models.Model): class Status(models.TextChoices): PENDING = 'pending', _('Pending') diff --git a/links/page_views.py b/links/page_views.py index 7785baf..3593547 100644 --- a/links/page_views.py +++ b/links/page_views.py @@ -222,6 +222,40 @@ class PageViewSet(viewsets.ModelViewSet): 'error': str(e) }, status=400) + @action(detail=False, methods=['delete'], url_path='screenshots/(?P[^/.]+)') + def delete_screenshot(self, request, screenshot_id=None): + """Delete a specific screenshot""" + try: + screenshot = Screenshot.objects.get(id=screenshot_id) + + # Optional: Add permission check here + # if screenshot.page.user != request.user: + # return Response(status=status.HTTP_403_FORBIDDEN) + + # Delete the actual file + if screenshot.path: + file_path = os.path.join(settings.MEDIA_ROOT, screenshot.path) + try: + os.remove(file_path) + except OSError: + pass # File doesn't exist or can't be deleted + + # Delete the database record + screenshot.delete() + + return Response(status=status.HTTP_204_NO_CONTENT) + + except Screenshot.DoesNotExist: + return Response( + {"error": "Screenshot not found"}, + status=status.HTTP_404_NOT_FOUND + ) + except Exception as e: + return Response( + {"error": str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR + ) + class ScreenshotGalleryView(TemplateView): template_name = 'links/screenshot_gallery.html' diff --git a/links/tasks.py b/links/tasks.py index 4c2ce3d..af7d6c4 100644 --- a/links/tasks.py +++ b/links/tasks.py @@ -213,7 +213,7 @@ def capture_screenshot(self, page_id, screenshot_id): @shared_task(bind=True, max_retries=3) def process_page(self, page_id): """Task to process a page and extract its metadata""" - from .models import Page + from .models import Page, Screenshot try: page = Page.objects.get(id=page_id) @@ -250,12 +250,18 @@ def process_page(self, page_id): if description: page.summary = description[:500] - # Trigger screenshot capture if needed - if not page.screenshot_path: - capture_screenshot.delay(page.id) + # Create a new screenshot record and trigger capture + screenshot = Screenshot.objects.create( + page=page, + status=Screenshot.Status.PENDING + ) + + # Trigger screenshot capture with the new screenshot ID + capture_screenshot.delay(page.id, screenshot.id) page.process_status = Page.ProcessStatus.COMPLETED page.save() + logger.info(f"Successfully processed page {page_id}") except Exception as exc: diff --git a/links/templates/links/page_detail.html b/links/templates/links/page_detail.html index bb247f6..72381ee 100644 --- a/links/templates/links/page_detail.html +++ b/links/templates/links/page_detail.html @@ -5,6 +5,19 @@ {% block content %}
+ + +
@@ -113,9 +126,11 @@

{% trans "Screenshots" %}

-
@@ -167,31 +182,54 @@

{% trans "Previous Screenshots" %}

{% for screenshot in previous_screenshots %} -
- - {% if screenshot.path %} - Screenshot {{ forloop.counter }} - {% else %} - Pending screenshot {{ forloop.counter }} - {% endif %} - -
- - {{ screenshot.get_status_display }} - -
-
- - {{ screenshot.created_at|date:"Y-m-d H:i" }} - +
+ +
+ + + {% if screenshot.path %} + Screenshot {{ forloop.counter }} + {% else %} + Pending screenshot {{ forloop.counter }} + {% endif %} + + + +
+
+ +
+
+ + +
+
+ + {{ screenshot.get_status_display }} + + + + {{ screenshot.created_at|date:"Y-m-d H:i" }} + + +
+
{% endfor %} @@ -255,5 +293,26 @@ function getCookie(name) { } return cookieValue; } + +function deleteScreenshot(screenshotId) { + if (confirm('{% trans "Are you sure you want to delete this screenshot?" %}')) { + fetch(`/api/pages/screenshots/${screenshotId}`, { + method: 'DELETE', + headers: { + 'X-CSRFToken': getCookie('csrftoken') + } + }) + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + location.reload(); + }) + .catch(error => { + console.error('Error:', error); + alert('Failed to delete screenshot'); + }); + } +} {% endblock %} diff --git a/links/templates/links/page_list.html b/links/templates/links/page_list.html index ee1c333..24d03b6 100644 --- a/links/templates/links/page_list.html +++ b/links/templates/links/page_list.html @@ -16,20 +16,22 @@ {% for page in pages %}
  • - +