diff --git a/.gitignore b/.gitignore index 5adcf824f..98c8ecfac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ *.pyc -htdocs/media/logo/* +media/* logs/* *.mo mygpo/settings_prod.py @@ -23,3 +23,9 @@ venv* # envdirs envs + +# Jupyter Notebooks +notebooks/ + +.cache +.pytest_cache \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 20cd66e8a..3b7c25855 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: python python: - - "3.5" - "3.6" - "3.6-dev" - "3.7-dev" @@ -19,7 +18,7 @@ before_script: - psql -c 'create database mygpo_test;' -U postgres script: - coverage run --branch --source=mygpo ./manage.py test + - pytest --cov=mygpo/ --cov-branch after_script: - coveralls @@ -28,3 +27,11 @@ env: - DATABASE_URL="postgres://postgres@localhost/mygpo_test" sudo: false + +# temporarily allow failures on 3.7 +# see https://github.com/chtd/psycopg2cffi/issues/95 +matrix: + allow_failures: + - python: "3.7-dev" + - python: "nightly" + diff --git a/Procfile b/Procfile index cbbcbbb6e..4dd801905 100644 --- a/Procfile +++ b/Procfile @@ -1,2 +1,3 @@ -web: gunicorn mygpo.wsgi:application -c gunicorn.conf.py -beat: python manage.py celery beat -S django --pidfile /var/run/mygpo/celerybeat.pid +web: gunicorn mygpo.wsgi:application -c conf/gunicorn.conf.py +beat: celery -A mygpo beat --pidfile /tmp/celerybeat.pid -S django +celery: celery -A mygpo worker --concurrency=3 -l info -Ofair diff --git a/gunicorn.conf.py b/conf/gunicorn.conf.py similarity index 100% rename from gunicorn.conf.py rename to conf/gunicorn.conf.py diff --git a/conftest.py b/conftest.py new file mode 100644 index 000000000..61629a16d --- /dev/null +++ b/conftest.py @@ -0,0 +1,9 @@ +import pytest + + +@pytest.fixture(autouse=True) +def enable_db_access_for_all_tests(db): + """ Enable DB access for all tests + + http://pytest-django.readthedocs.io/en/latest/faq.html#how-can-i-give-database-access-to-all-my-tests-without-the-django-db-marker """ + pass diff --git a/doc/api/reference/devices.rst b/doc/api/reference/devices.rst index 10d0e29d2..2afdab02a 100644 --- a/doc/api/reference/devices.rst +++ b/doc/api/reference/devices.rst @@ -40,7 +40,7 @@ Update Device Data List Devices ------------ -.. http:post:: /api/2/devices/(username).json +.. http:get:: /api/2/devices/(username).json :synopsis: list the user's devices * Requires HTTP authentication diff --git a/doc/dev/configuration.rst b/doc/dev/configuration.rst index 797c99810..27380d6c2 100644 --- a/doc/dev/configuration.rst +++ b/doc/dev/configuration.rst @@ -95,3 +95,8 @@ Social Login * ``GOOGLE_CLIENT_ID`` - Google Client ID * ``GOOGLE_CLIENT_SECRET`` - Google Client Secret + + +API +--- +* ``MAX_EPISODE_ACTIONS`` - maximum number of episode actions that the API will return in one `GET` request. diff --git a/doc/dev/index.rst b/doc/dev/index.rst index 56a42433f..0dacb0456 100644 --- a/doc/dev/index.rst +++ b/doc/dev/index.rst @@ -7,7 +7,7 @@ gpodder.net The sourcecode of the webservice gpodder.net is released as open source under the `AGPLv3 `_ and `hosted at GitHub `_. Bugs can be reported at the `gPodder -Bugtracker `_. +Bugtracker `_. Integrating Clients ------------------- @@ -30,3 +30,4 @@ Contents postgres-setup libraries configuration + jupyter-notebook diff --git a/doc/dev/installation.rst b/doc/dev/installation.rst index 0cbe2b31b..4ec2219ca 100644 --- a/doc/dev/installation.rst +++ b/doc/dev/installation.rst @@ -43,20 +43,7 @@ Now install additional dependencies locally: pip install -r requirements-test.txt # for running tests -That's it for the setup. Now to initialize the DB: - -First run the commands from :ref:`db-setup`. Then - -.. code-block:: bash - - cd mygpo - python manage.py migrate - -..and here we go: - -.. code-block:: bash - - python manage.py runserver +That's it for the setup. Configuration @@ -72,9 +59,35 @@ For a development configuration you will probably want to use the following echo postgres://mygpo:mygpo@localhost/mygpo > envs/local/DATABASE_URL echo True > envs/local/DEBUG +On an Debian/Ubuntu based system, you can perform this configuration with + +.. code-block:: bash + + make dev-config + See :ref:`configuration` for further information. +Database Initialization +----------------------- + +Now to initialize the DB: + +First run the commands from :ref:`db-setup`. Then + +.. code-block:: bash + + cd mygpo + envdir envs/local python manage.py migrate + +..and here we go: + +.. code-block:: bash + + envdir envs/local python manage.py runserver + + + Accessing the dev server from other devices ------------------------------------------- @@ -84,7 +97,7 @@ runserver command of manage.py, like this: .. code-block:: bash - python manage.py runserver 0.0.0.0:8000 + envdir envs/local python manage.py runserver 0.0.0.0:8000 Beware, though, that this will expose the web service to your all networks that your machine is connected to. Apply common sense and ideally use only @@ -101,22 +114,22 @@ commands regularly on your development machine: .. code-block:: bash - python manage.py update-categories - python manage.py update-toplist - python manage.py update-episode-toplist + envdir envs/local python manage.py update-categories + envdir envs/local python manage.py update-toplist + envdir envs/local python manage.py update-episode-toplist - python manage.py feed-downloader - python manage.py feed-downloader [...] - python manage.py feed-downloader --max - python manage.py feed-downloader --random --max - python manage.py feed-downloader --toplist --max - python manage.py feed-downloader --update-new --max + envdir envs/local python manage.py feed-downloader + envdir envs/local python manage.py feed-downloader [...] + envdir envs/local python manage.py feed-downloader --max + envdir envs/local python manage.py feed-downloader --random --max + envdir envs/local python manage.py feed-downloader --toplist --max + envdir envs/local python manage.py feed-downloader --update-new --max or to only do a dry run (this won't do any web requests for feeds): .. code-block:: bash - python manage.py feed-downloader --list-only [other parameters] + envdir envs/local python manage.py feed-downloader --list-only [other parameters] Maintaining publisher relationships with user accounts @@ -127,7 +140,7 @@ To set a user as publisher for a given feed URL, use: .. code-block:: bash cd mygpo - python manage.py make-publisher [...] + envdir envs/local python manage.py make-publisher [...] Web-Server @@ -138,7 +151,7 @@ directory with .. code-block:: bash - python manage.py runserver + envdir envs/local python manage.py runserver If you want to run a production server, check out `Deploying Django `_. diff --git a/doc/dev/jupyter-notebook.rst b/doc/dev/jupyter-notebook.rst new file mode 100644 index 000000000..9ad2e5033 --- /dev/null +++ b/doc/dev/jupyter-notebook.rst @@ -0,0 +1,28 @@ +.. _jupyter-notebook: + +Jupyter Notebook +================ + +You can use `Jupyter Notebooks `_ during development for +exploring data and prototyping methods. + +To do so, follow these steps + +* Make sure you have all requirements from ``requirements-dev.txt`` installed. + +* Run ``make notebook``, which will start the notebook and open it in the + browser . + +* Navigate to the directory ``notebooks`` (listed in `.gitignore`) and create + a new notebook. + +* Use the following code in the first cell to setup your environment + + .. code-block:: python + + MYPROJECT = '/path/to/mygpo' + import os, sys + sys.path.insert(0, MYPROJECT) + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "local_settings.py") + import django + django.setup() diff --git a/htdocs/media/16x16/emblem-default.png b/htdocs/media/16x16/emblem-default.png deleted file mode 100644 index fb4362c1b..000000000 Binary files a/htdocs/media/16x16/emblem-default.png and /dev/null differ diff --git a/htdocs/media/16x16/server.png b/htdocs/media/16x16/server.png deleted file mode 100644 index 82c9b4bef..000000000 Binary files a/htdocs/media/16x16/server.png and /dev/null differ diff --git a/htdocs/media/24x24/opml-icon.png b/htdocs/media/24x24/opml-icon.png deleted file mode 100644 index 6ea4a3e12..000000000 Binary files a/htdocs/media/24x24/opml-icon.png and /dev/null differ diff --git a/htdocs/media/32x32/audio-x-generic.png b/htdocs/media/32x32/audio-x-generic.png deleted file mode 100644 index d66563f14..000000000 Binary files a/htdocs/media/32x32/audio-x-generic.png and /dev/null differ diff --git a/htdocs/media/32x32/computer.png b/htdocs/media/32x32/computer.png deleted file mode 100644 index 367e4ece5..000000000 Binary files a/htdocs/media/32x32/computer.png and /dev/null differ diff --git a/htdocs/media/32x32/server.png b/htdocs/media/32x32/server.png deleted file mode 100644 index 4f863f99c..000000000 Binary files a/htdocs/media/32x32/server.png and /dev/null differ diff --git a/htdocs/media/FontAwesome.ttf b/htdocs/media/FontAwesome.ttf deleted file mode 100644 index 793a922a0..000000000 Binary files a/htdocs/media/FontAwesome.ttf and /dev/null differ diff --git a/htdocs/media/charts-new.png b/htdocs/media/charts-new.png deleted file mode 100644 index 3b8a8859d..000000000 Binary files a/htdocs/media/charts-new.png and /dev/null differ diff --git a/htdocs/media/dialog-error.png b/htdocs/media/dialog-error.png deleted file mode 100644 index e71bba3bc..000000000 Binary files a/htdocs/media/dialog-error.png and /dev/null differ diff --git a/htdocs/media/emblem-default.png b/htdocs/media/emblem-default.png deleted file mode 100644 index 05bff579b..000000000 Binary files a/htdocs/media/emblem-default.png and /dev/null differ diff --git a/htdocs/media/img/glyphicons-halflings-white.png b/htdocs/media/img/glyphicons-halflings-white.png deleted file mode 100644 index 3bf6484a2..000000000 Binary files a/htdocs/media/img/glyphicons-halflings-white.png and /dev/null differ diff --git a/htdocs/media/img/glyphicons-halflings.png b/htdocs/media/img/glyphicons-halflings.png deleted file mode 100644 index a99699932..000000000 Binary files a/htdocs/media/img/glyphicons-halflings.png and /dev/null differ diff --git a/htdocs/media/info.png b/htdocs/media/info.png deleted file mode 100644 index c3c07e15d..000000000 Binary files a/htdocs/media/info.png and /dev/null differ diff --git a/htdocs/media/mygpo18.png b/htdocs/media/mygpo18.png deleted file mode 100644 index 0928894ca..000000000 Binary files a/htdocs/media/mygpo18.png and /dev/null differ diff --git a/makefile b/makefile index da055b289..e9bf59b45 100644 --- a/makefile +++ b/makefile @@ -4,15 +4,28 @@ help: @echo 'make test run tests and show coverage report' @echo 'make clean clean up files' -test: - envdir envs/dev/ python -Wd -m coverage run ./manage.py test - coverage report +dev-config: + mkdir -p envs/local + echo django.core.mail.backends.console.EmailBackend > envs/local/EMAIL_BACKEND + echo secret > envs/local/SECRET_KEY + echo postgres://mygpo:mygpo@localhost/mygpo > envs/local/DATABASE_URL + echo True > envs/local/DEBUG + +test: envs/test/MEDIA_ROOT + # assume defined media root directory, empty before running tests + rm -rf $(shell cat envs/test/MEDIA_ROOT) + mkdir -p $(shell cat envs/test/MEDIA_ROOT) + envdir envs/dev/ pytest --cov=mygpo/ --cov-branch + coverage report --show-missing update-po: envdir envs/dev/ python manage.py makemessages \ - --ignore=doc/* --ignore=envs/* --ignore=htdocs/* --ignore=venv/* \ - --ignore=res/* --ignore=tools/* --ignore=mygpo/*/migrations/* + --ignore=doc/* --ignore=envs/* --ignore=media/* --ignore=venv/* \ + --ignore=res/* --ignore=tools/* --ignore=mygpo/*/migrations/* \ + --ignore=static/* +notebook: + envdir envs/dev/ python manage.py shell_plus --notebook clean: git clean -fX diff --git a/mygpo/administration/templates/admin/hostinfo.html b/mygpo/administration/templates/admin/hostinfo.html index e09c45ea3..ad273f682 100644 --- a/mygpo/administration/templates/admin/hostinfo.html +++ b/mygpo/administration/templates/admin/hostinfo.html @@ -1,5 +1,6 @@ {% extends "base.html" %} {% load i18n %} +{% load time %} {% load podcasts %} {% load menu %} @@ -62,6 +63,15 @@

{% trans "Host Information" %}

{{ num_index_outdated }} + + + + {% trans "Average podcast update duration" %} + + + {{ avg_podcast_update_duration.total_seconds|format_duration}} + + {% trans "Scheduled Celery Tasks" %} {{ num_celery_tasks }} diff --git a/mygpo/administration/templates/admin/overview.html b/mygpo/administration/templates/admin/overview.html index 1c542533b..4fea5e4ba 100644 --- a/mygpo/administration/templates/admin/overview.html +++ b/mygpo/administration/templates/admin/overview.html @@ -20,6 +20,7 @@

{% trans "Admin Area" %}

  • {% trans "User-Agent Stats" %}
  • {% trans "General Stats" %} ({% trans "JSON" %})
  • {% trans "Activate User" %}
  • +
  • {% trans "Resend Activation Email" %}
  • {% trans "Assign Publisher Permissions" %}
  • diff --git a/mygpo/administration/templates/admin/resend-acivation.html b/mygpo/administration/templates/admin/resend-acivation.html new file mode 100644 index 000000000..054508139 --- /dev/null +++ b/mygpo/administration/templates/admin/resend-acivation.html @@ -0,0 +1,41 @@ +{% extends "base.html" %} +{% load i18n %} +{% load podcasts %} + +{% load menu %} +{% block mainmenu %}{{ "/admin/"|main_menu }}{% endblock %} +{% block sectionmenu %}{{ "/admin/"|section_menu:"Admin" }}{% endblock %} + +{% block title %}{% trans "Activate User" %}{% endblock %} + +{% block header %} +

    {% trans "Resend Activation email" %}

    +{% endblock %} + +{% block content %} +
    + {% csrf_token %} + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    + +
    +
    + +
    +
    +
    + +{% endblock %} + diff --git a/mygpo/administration/tests.py b/mygpo/administration/tests.py index a8124dc74..3a181c868 100644 --- a/mygpo/administration/tests.py +++ b/mygpo/administration/tests.py @@ -24,22 +24,22 @@ class SimpleTest(TestCase): def test_merge(self): - p1 = Podcast.objects.get_or_create_for_url('http://example.com/podcast1.rss') - p2 = Podcast.objects.get_or_create_for_url('http://example.com/podcast2.rss') + p1 = Podcast.objects.get_or_create_for_url('http://example.com/podcast1.rss').object + p2 = Podcast.objects.get_or_create_for_url('http://example.com/podcast2.rss').object - e1 = Episode.objects.get_or_create_for_url(p1, 'http://example.com/podcast1/e1.mp3') + e1 = Episode.objects.get_or_create_for_url(p1, 'http://example.com/podcast1/e1.mp3').object e1.title = 'Episode 1' e1.save() - e2 = Episode.objects.get_or_create_for_url(p2, 'http://example.com/podcast1/e2.mp3') + e2 = Episode.objects.get_or_create_for_url(p2, 'http://example.com/podcast1/e2.mp3').object e2.title = 'Episode 2' e2.save() - e3 = Episode.objects.get_or_create_for_url(p2, 'http://example.com/podcast2/e2.mp3') + e3 = Episode.objects.get_or_create_for_url(p2, 'http://example.com/podcast2/e2.mp3').object e3.title = 'Episode 3' e3.save() - e4 = Episode.objects.get_or_create_for_url(p2, 'http://example.com/podcast2/e3.mp3') + e4 = Episode.objects.get_or_create_for_url(p2, 'http://example.com/podcast2/e3.mp3').object e4.title = 'Episode 4' e4.save() diff --git a/mygpo/administration/urls.py b/mygpo/administration/urls.py index d9fbd58f6..36a241ac7 100644 --- a/mygpo/administration/urls.py +++ b/mygpo/administration/urls.py @@ -1,66 +1,70 @@ -from django.conf.urls import url +from django.urls import path from . import views urlpatterns = [ - url(r'^$', + path('', views.Overview.as_view(), name='admin-overview'), - url(r'^hostinfo$', + path('hostinfo', views.HostInfo.as_view(), name='admin-hostinfo'), - url(r'^merge/$', + path('merge/', views.MergeSelect.as_view(), name='admin-merge'), - url(r'^merge/verify$', + path('merge/verify', views.MergeVerify.as_view(), name='admin-merge-verify'), - url(r'^merge/process$', + path('merge/process', views.MergeProcess.as_view(), name='admin-merge-process'), - url(r'^merge/status/(?P[^/]+)$', + path('merge/status/', views.MergeStatus.as_view(), name='admin-merge-status'), - url(r'^clients$', + path('clients', views.ClientStatsView.as_view(), name='clients'), - url(r'^clients\.json$', + path('clients.json', views.ClientStatsJsonView.as_view(), name='clients-json'), - url(r'^clients/user_agents$', + path('clients/user_agents', views.UserAgentStatsView.as_view(), name='useragents'), - url(r'^stats$', + path('stats', views.StatsView.as_view(), name='stats'), - url(r'^stats\.json$', + path('stats.json', views.StatsJsonView.as_view(), name='stats-json'), - url(r'^activate-user/$', + path('activate-user', views.ActivateUserView.as_view(), name='admin-activate-user'), - url(r'^make-publisher/input$', + path('resend-activation-email', + views.ResendActivationEmail.as_view(), + name='admin-resend-activation'), + + path('make-publisher/input', views.MakePublisherInput.as_view(), name='admin-make-publisher-input'), - url(r'^make-publisher/process$', + path('make-publisher/process', views.MakePublisher.as_view(), name='admin-make-publisher'), - url(r'^make-publisher/result$', + path('make-publisher/result', views.MakePublisher.as_view(), name='admin-make-publisher-result'), diff --git a/mygpo/administration/views.py b/mygpo/administration/views.py index 0c9b5c689..d5b87b22d 100644 --- a/mygpo/administration/views.py +++ b/mygpo/administration/views.py @@ -4,7 +4,10 @@ from collections import Counter from datetime import datetime +import redis + import django +from django.db.models import Avg from django.shortcuts import render from django.contrib import messages from django.urls import reverse @@ -26,6 +29,7 @@ from mygpo.administration.clients import UserAgentStats, ClientStats from mygpo.administration.tasks import merge_podcasts from mygpo.utils import get_git_head +from mygpo.data.models import PodcastUpdateResult from mygpo.users.models import UserProxy from mygpo.publisher.models import PublishedPodcast from mygpo.api.httpresponse import JsonResponse @@ -57,15 +61,9 @@ def get(self, request): hostname = socket.gethostname() django_version = django.VERSION - i = celery.control.inspect() - scheduled = i.scheduled() - if not scheduled: - num_celery_tasks = None - else: - num_celery_tasks = sum(len(node) for node in scheduled.values()) - feed_queue_status = self._get_feed_queue_status() num_index_outdated = self._get_num_outdated_search_index() + avg_podcast_update_duration = self._get_avg_podcast_update_duration() return self.render_to_response({ 'git_commit': commit, @@ -73,11 +71,26 @@ def get(self, request): 'base_dir': base_dir, 'hostname': hostname, 'django_version': django_version, - 'num_celery_tasks': num_celery_tasks, + 'num_celery_tasks': self._get_waiting_celery_tasks(), + 'avg_podcast_update_duration': avg_podcast_update_duration, 'feed_queue_status': feed_queue_status, 'num_index_outdated': num_index_outdated, }) + def _get_waiting_celery_tasks(self): + con = celery.broker_connection() + + args = {'host': con.hostname} + if con.port: + args['port'] = con.port + + r = redis.StrictRedis(**args) + return r.llen('celery') + + def _get_avg_podcast_update_duration(self): + queryset = PodcastUpdateResult.objects.filter(successful=True) + return queryset.aggregate(avg_duration=Avg('duration'))['avg_duration'] + def _get_feed_queue_status(self): now = datetime.utcnow() next_podcast = Podcast.objects.all().order_by_next_update().first() @@ -317,7 +330,7 @@ def post(self, request): return HttpResponseRedirect(reverse('admin-activate-user')) try: - user = UserProxy.objects.by_username_or_email(username, email) + user = UserProxy.objects.all().by_username_or_email(username, email) except UserProxy.DoesNotExist: messages.error(request, _('No user found')) return HttpResponseRedirect(reverse('admin-activate-user')) @@ -329,6 +342,42 @@ def post(self, request): return HttpResponseRedirect(reverse('admin-activate-user')) +class ResendActivationEmail(AdminView): + """ Resends the users activation email """ + + template_name = 'admin/resend-acivation.html' + + def get(self, request): + return self.render_to_response({}) + + def post(self, request): + + username = request.POST.get('username') + email = request.POST.get('email') + + if not (username or email): + messages.error(request, + _('Provide either username or email address')) + return HttpResponseRedirect(reverse('admin-resend-activation')) + + try: + user = UserProxy.objects.all().by_username_or_email(username, email) + except UserProxy.DoesNotExist: + messages.error(request, _('No user found')) + return HttpResponseRedirect(reverse('admin-resend-activation')) + + if user.is_active: + messages.success(request, 'User {username} is already activated') + + else: + #send_activation_email(user, request) + messages.success(request, + _('Email for {username} ({email}) resent'.format( + username=user.username, email=user.email))) + + return HttpResponseRedirect(reverse('admin-resend-activation')) + + class MakePublisherInput(AdminView): """ Get all information necessary for making someone publisher """ diff --git a/mygpo/api/advanced/__init__.py b/mygpo/api/advanced/__init__.py index 9f3cddc90..26bb1fae9 100644 --- a/mygpo/api/advanced/__init__.py +++ b/mygpo/api/advanced/__init__.py @@ -144,6 +144,9 @@ def get_episode_changes(user, podcast, device, since, until, aggregated, version history = EpisodeHistoryEntry.objects.filter(user=user, timestamp__lt=until) + # return the earlier entries first + history = history.order_by('timestamp') + if since: history = history.filter(timestamp__gte=since) @@ -156,12 +159,24 @@ def get_episode_changes(user, podcast, device, since, until, aggregated, version if version == 1: history = map(convert_position, history) + # Limit number of returned episode actions + max_actions = dsettings.MAX_EPISODE_ACTIONS + history = history[:max_actions] + + # evaluate query and turn into list, for negative indexing + history = list(history) + actions = [episode_action_json(a, user) for a in history] if aggregated: actions = list(dict( (a['episode'], a) for a in actions ).values()) - return {'actions': actions, 'timestamp': get_timestamp(until)} + if history: + ts = get_timestamp(history[-1].timestamp) + else: + ts = get_timestamp(until) + + return {'actions': actions, 'timestamp': ts} def episode_action_json(history, user): @@ -200,8 +215,8 @@ def update_episodes(user, actions, now, ua_string): if not episode_url: continue - podcast = Podcast.objects.get_or_create_for_url(podcast_url) - episode = Episode.objects.get_or_create_for_url(podcast, episode_url) + podcast = Podcast.objects.get_or_create_for_url(podcast_url).object + episode = Episode.objects.get_or_create_for_url(podcast, episode_url).object # parse_episode_action returns a EpisodeHistoryEntry obj history = parse_episode_action(action, user, update_urls, now, @@ -249,7 +264,7 @@ def parse_episode_action(action, user, update_urls, now, ua_string): # instead of "POST" requests for uploading device settings @allowed_methods(['POST', 'PUT']) @cors_origin() -def device(request, username, device_uid): +def device(request, username, device_uid, version=None): d = get_device(request.user, device_uid, request.META.get('HTTP_USER_AGENT', '')) @@ -295,7 +310,7 @@ def valid_episodeaction(type): @never_cache @allowed_methods(['GET']) @cors_origin() -def devices(request, username): +def devices(request, username, version=None): user = request.user clients = user.client_set.filter(deleted=False) client_data = [get_client_data(user, client) for client in clients] diff --git a/mygpo/api/advanced/episode.py b/mygpo/api/advanced/episode.py index 2b12704f2..2741945f2 100644 --- a/mygpo/api/advanced/episode.py +++ b/mygpo/api/advanced/episode.py @@ -54,8 +54,8 @@ def get(self, request, username): def update_chapters(self, req, user): """ Add / remove chapters according to the client's request """ - podcast = Podcast.objects.get_or_create_for_url(podcast_url) - episode = Episode.objects.get_or_create_for_url(podcast, episode_url) + podcast = Podcast.objects.get_or_create_for_url(podcast_url).object + episode = Episode.objects.get_or_create_for_url(podcast, episode_url).object # add chapters for chapter_data in req.get('chapters_add', []): diff --git a/mygpo/api/advanced/lists.py b/mygpo/api/advanced/lists.py index cb586945c..85ac72cf2 100644 --- a/mygpo/api/advanced/lists.py +++ b/mygpo/api/advanced/lists.py @@ -61,7 +61,7 @@ def create(request, username, format): return HttpResponse('List already exists', status=409) urls = parse_subscription(request.body.decode('utf-8'), format) - podcasts = [Podcast.objects.get_or_create_for_url(url) for url in urls] + podcasts = [Podcast.objects.get_or_create_for_url(url).object for url in urls] for podcast in podcasts: plist.add_entry(podcast) @@ -148,7 +148,7 @@ def get_list(request, plist, owner, format): def update_list(request, plist, owner, format): """ Replaces the podcasts in the list and returns 204 No Content """ urls = parse_subscription(request.body.decode('utf-8'), format) - podcasts = [Podcast.objects.get_or_create_for_url(url) for url in urls] + podcasts = [Podcast.objects.get_or_create_for_url(url).object for url in urls] plist.set_entries(podcasts) return HttpResponse(status=204) diff --git a/mygpo/api/basic_auth.py b/mygpo/api/basic_auth.py index ac76baa94..77308fcab 100644 --- a/mygpo/api/basic_auth.py +++ b/mygpo/api/basic_auth.py @@ -1,4 +1,5 @@ import base64 +import binascii from functools import wraps from django.http import HttpResponse, HttpResponseBadRequest @@ -46,7 +47,7 @@ def view_or_basicauth(view, request, test_func, realm = "", *args, **kwargs): .decode('utf-8')\ .split(':', 1) - except UnicodeDecodeError as e: + except (UnicodeDecodeError, binascii.Error) as e: return HttpResponseBadRequest( 'Could not decode credentials: {msg}'.format(msg=str(e))) diff --git a/mygpo/api/legacy.py b/mygpo/api/legacy.py index f7aee4126..d00da2416 100644 --- a/mygpo/api/legacy.py +++ b/mygpo/api/legacy.py @@ -55,11 +55,11 @@ def upload(request): rem = list(set(rem)) for n in new: - p = Podcast.objects.get_or_create_for_url(n) + p = Podcast.objects.get_or_create_for_url(n).object subscribe(p, user, dev) for r in rem: - p = Podcast.objects.get_or_create_for_url(r) + p = Podcast.objects.get_or_create_for_url(r).object unsubscribe(p, user, dev) return HttpResponse('@SUCCESS', content_type='text/plain') diff --git a/mygpo/api/simple.py b/mygpo/api/simple.py index 00970e35a..d53846e6c 100644 --- a/mygpo/api/simple.py +++ b/mygpo/api/simple.py @@ -138,7 +138,7 @@ def default_get_podcast(p): if any(x not in ALLOWED_FUNCNAME for x in jsonp_padding): return HttpResponseBadRequest('JSONP padding can only contain the characters %(char)s' % {'char': ALLOWED_FUNCNAME}) - objs = map(json_map, obj_list) + objs = list(map(json_map, obj_list)) return JsonResponse(objs, jsonp_padding=jsonp_padding) elif format == 'xml': @@ -176,6 +176,9 @@ def parse_subscription(raw_post_data, format): end = raw_post_data.find(']') + 1 urls = json.loads(raw_post_data[begin:end]) + if not isinstance(urls, list): + raise ValueError('A list of feed URLs was expected') + else: return [] @@ -200,7 +203,7 @@ def set_subscriptions(urls, user, device_uid, user_agent): unsubscribe(podcast, user, device) for url in new: - podcast = Podcast.objects.get_or_create_for_url(url) + podcast = Podcast.objects.get_or_create_for_url(url).object subscribe(podcast, user, device, url) # Only an empty response is a successful response diff --git a/mygpo/api/subscriptions.py b/mygpo/api/subscriptions.py index 5f38984f5..50ac7cf92 100644 --- a/mygpo/api/subscriptions.py +++ b/mygpo/api/subscriptions.py @@ -81,7 +81,7 @@ def update_subscriptions(self, user, device, add, remove): rem_s = filter(lambda x: x not in add_s, rem_s) for add_url in add_s: - podcast = Podcast.objects.get_or_create_for_url(add_url) + podcast = Podcast.objects.get_or_create_for_url(add_url).object subscribe(podcast, user, device, add_url) remove_podcasts = Podcast.objects.filter(urls__url__in=rem_s) diff --git a/mygpo/api/tests.py b/mygpo/api/tests.py index 86504a59d..7f093f471 100644 --- a/mygpo/api/tests.py +++ b/mygpo/api/tests.py @@ -1,16 +1,21 @@ import json +import uuid import copy import unittest +from datetime import datetime, timedelta from urllib.parse import urlencode from django.test.client import Client from django.test import TestCase from django.urls import reverse from django.contrib.auth import get_user_model +from django.test.utils import override_settings from mygpo.podcasts.models import Podcast, Episode from mygpo.api.advanced import episodes +from mygpo.history.models import EpisodeHistoryEntry from mygpo.test import create_auth_string, anon_request +from mygpo.utils import get_timestamp class AdvancedAPITests(unittest.TestCase): @@ -168,14 +173,14 @@ def setUp(self): defaults = { 'title': 'My Podcast', }, - ) + ).object self.episode = Episode.objects.get_or_create_for_url( self.podcast, 'http://example.com/directory-podcast/1.mp3', defaults = { 'title': 'My Episode', }, - ) + ).object self.client = Client() def test_episode_info(self): @@ -186,3 +191,104 @@ def test_episode_info(self): resp = self.client.get(url) self.assertEqual(resp.status_code, 200) + + +class EpisodeActionTests(TestCase): + + def setUp(self): + self.podcast = Podcast.objects.get_or_create_for_url( + 'http://example.com/directory-podcast.xml', + defaults = { + 'title': 'My Podcast', + }, + ).object + self.episode = Episode.objects.get_or_create_for_url( + self.podcast, + 'http://example.com/directory-podcast/1.mp3', + defaults = { + 'title': 'My Episode', + }, + ).object + User = get_user_model() + self.password = 'asdf' + self.username = 'adv-api-user' + self.user = User(username=self.username, email='user@example.com') + self.user.set_password(self.password) + self.user.save() + self.user.is_active = True + self.client = Client() + self.extra = { + 'HTTP_AUTHORIZATION': create_auth_string(self.username, + self.password) + } + + def tearDown(self): + self.episode.delete() + self.podcast.delete() + self.user.delete() + + @override_settings(MAX_EPISODE_ACTIONS=10) + def test_limit_actions(self): + """ Test that max MAX_EPISODE_ACTIONS episodes are returned """ + + timestamps = [] + t = datetime.utcnow() + for n in range(15): + timestamp = t - timedelta(seconds=n) + EpisodeHistoryEntry.objects.create( + timestamp = timestamp, + episode = self.episode, + user = self.user, + action = EpisodeHistoryEntry.DOWNLOAD, + ) + timestamps.append(timestamp) + + url = reverse(episodes, kwargs={ + 'version': '2', + 'username': self.user.username, + }) + response = self.client.get(url, {'since': '0'}, **self.extra) + self.assertEqual(response.status_code, 200, response.content) + response_obj = json.loads(response.content.decode('utf-8')) + actions = response_obj['actions'] + + # 10 actions should be returned + self.assertEqual(len(actions), 10) + + timestamps = sorted(timestamps) + + # the first 10 actions, according to their timestamp should be returned + for action, timestamp in zip(actions, timestamps): + self.assertEqual(timestamp.isoformat(), action['timestamp']) + + # the `timestamp` field in the response should be the timestamp of the + # last returned action + self.assertEqual( + get_timestamp(timestamps[9]), + response_obj['timestamp'] + ) + + + def test_no_actions(self): + """ Test when there are no actions to return """ + + t1 = get_timestamp(datetime.utcnow()) + + url = reverse(episodes, kwargs={ + 'version': '2', + 'username': self.user.username, + }) + response = self.client.get(url, {'since': '0'}, **self.extra) + self.assertEqual(response.status_code, 200, response.content) + response_obj = json.loads(response.content.decode('utf-8')) + actions = response_obj['actions'] + + # 10 actions should be returned + self.assertEqual(len(actions), 0) + + returned = response_obj['timestamp'] + t2 = get_timestamp(datetime.utcnow()) + # the `timestamp` field in the response should be the timestamp of the + # last returned action + self.assertGreaterEqual(returned, t1) + self.assertGreaterEqual(t2, returned) diff --git a/mygpo/api/urls.py b/mygpo/api/urls.py index 9776d0db1..456d87cd3 100644 --- a/mygpo/api/urls.py +++ b/mygpo/api/urls.py @@ -1,107 +1,111 @@ -from django.conf.urls import url +from django.urls import path, register_converter from . import legacy, simple, advanced, advanced, subscriptions from .advanced import auth, lists, sync, updates, episode, settings +from mygpo.users import converters +from mygpo.usersettings.converters import ScopeConverter + +register_converter(converters.ClientUIDConverter, 'client-uid') +register_converter(converters.UsernameConverter, 'username') +register_converter(ScopeConverter, 'scope') urlpatterns = [ - url(r'^upload$', + path('upload', legacy.upload), - url(r'^getlist$', + path('getlist', legacy.getlist), - url(r'^toplist.opml$', + path('toplist.opml', simple.toplist, kwargs={'count': 50, 'format': 'opml'}), - url(r'^subscriptions/(?P[\w.+-]+)/' - '(?P[\w.-]+)\.(?P\w+)', + path('subscriptions//' + '.', simple.subscriptions), - url(r'^subscriptions/(?P[\w.+-]+)\.(?P\w+)', + path('subscriptions/.', simple.all_subscriptions, name='api-all-subscriptions'), - url(r'^toplist/(?P\d+)\.(?P\w+)', + path('toplist/.', simple.toplist, name='toplist-opml'), - url(r'^search\.(?P\w+)', + path('search.', simple.search), - url(r'^suggestions/(?P\d+)\.(?P\w+)', + path('suggestions/.', simple.suggestions, name='suggestions-opml'), - url(r'^toplist\.(?P\w+)$', + path('toplist.', simple.toplist, kwargs={'count': 50}), - url(r'^gpodder-examples\.(?P\w+)$', + path('gpodder-examples.', simple.example_podcasts, name='example-opml'), - url(r'^api/(?P[12])/subscriptions/(?P[\w.+-]+)/' - '(?P[\w.-]+)\.json', + path('api//subscriptions//' + '.json', subscriptions.SubscriptionsAPI.as_view(), name='subscriptions-api'), - url(r'^api/(?P[12])/episodes/(?P[\w.+-]+)\.json', + path('api//episodes/.json', advanced.episodes), - url(r'^api/[12]/devices/(?P[\w.+-]+)/' - '(?P[\w.-]+)\.json', + path('api//devices//' + '.json', advanced.device), - url(r'^api/[12]/devices/(?P[\w.+-]+)\.json', + path('api//devices/.json', advanced.devices), - url(r'^api/2/auth/(?P[\w.+-]+)/login\.json', + path('api/2/auth//login.json', auth.login), - url(r'^api/2/auth/(?P[\w.+-]+)/logout\.json', + path('api/2/auth//logout.json', auth.logout), - url(r'^api/2/tags/(?P\d+)\.json', + path('api/2/tags/.json', advanced.directory.top_tags), - url(r'^api/2/tag/(?P[^/]+)/(?P\d+)\.json', + path('api/2/tag//.json', advanced.directory.tag_podcasts), - url(r'^api/2/data/podcast\.json', + path('api/2/data/podcast.json', advanced.directory.podcast_info), - url(r'^api/2/data/episode\.json', + path('api/2/data/episode.json', advanced.directory.episode_info, name='api-episode-info'), - url(r'^api/2/chapters/(?P[\w.+-]+)\.json', + path('api/2/chapters/.json', episode.ChaptersAPI.as_view()), - url(r'^api/2/updates/(?P[\w.+-]+)/(?P[\w.-]+)\.json', + path('api/2/updates//.json', updates.DeviceUpdates.as_view()), - url(r'^api/2/settings/(?P[\w.+-]+)/' - '(?Paccount|device|podcast|episode)\.json', + path('api/2/settings//.json', settings.SettingsAPI.as_view(), name='settings-api'), - url(r'^api/2/favorites/(?P[\w.+-]+).json', + path('api/2/favorites/.json', advanced.favorites), - url(r'^api/2/lists/(?P[\w.+-]+)/create\.(?P\w+)', + path('api/2/lists//create.', lists.create), - url(r'^api/2/lists/(?P[\w.+-]+)\.json', + path('api/2/lists/.json', lists.get_lists), - url(r'^api/2/lists/(?P[\w.+-]+)/list/' - '(?P[\w-]+)\.(?P\w+)', + path('api/2/lists//list/.', lists.podcast_list, name='api-get-list'), - url(r'^api/2/sync-devices/(?P[\w.+-]+)\.json', + path('api/2/sync-devices/.json', sync.main), ] diff --git a/mygpo/core/models.py b/mygpo/core/models.py index 2b8c358ba..8d0b730cd 100644 --- a/mygpo/core/models.py +++ b/mygpo/core/models.py @@ -14,7 +14,7 @@ class Meta: def get_id(self): """ String representation of the ID """ - return self.id.hex + return self.id class TwitterModel(models.Model): diff --git a/mygpo/core/slugs.py b/mygpo/core/slugs.py index 230fb15ab..eaaa6bd4b 100644 --- a/mygpo/core/slugs.py +++ b/mygpo/core/slugs.py @@ -7,10 +7,6 @@ class SlugGenerator(object): """ Generates a unique slug for an object """ def __init__(self, obj): - if obj.slug: - raise ValueError('%(obj)s already has slug %(slug)s' % - dict(obj=obj, slug=obj.slug)) - self.obj = obj self.base_slug = self._get_base_slug(obj) @@ -26,6 +22,10 @@ def __iter__(self): The consumer can can consume until it get's an unused one """ + if self.obj.slug: + # The object already has a slug + raise StopIteration + if not self.base_slug: raise StopIteration diff --git a/mygpo/data/admin.py b/mygpo/data/admin.py new file mode 100644 index 000000000..cc94ec441 --- /dev/null +++ b/mygpo/data/admin.py @@ -0,0 +1,18 @@ +from django.contrib import admin + +from . import models + + +@admin.register(models.PodcastUpdateResult) +class PodcastUpdateResultAdmin(admin.ModelAdmin): + model = models.PodcastUpdateResult + + list_display = ['title', 'start', 'duration', 'successful', + 'episodes_added'] + + readonly_fields = ['id', 'podcast_url', 'podcast', 'start', 'duration', + 'successful', 'error_message', 'podcast_created', + 'episodes_added'] + + def title(self, res): + return res.podcast or res.podcast_url diff --git a/mygpo/data/feeddownloader.py b/mygpo/data/feeddownloader.py index 9572eaa61..b7764a1f8 100755 --- a/mygpo/data/feeddownloader.py +++ b/mygpo/data/feeddownloader.py @@ -2,13 +2,12 @@ # -*- coding: utf-8 -*- import os.path -import urllib.request, urllib.error, urllib.parse +import urllib.request +import urllib.error from urllib.parse import urljoin -import http.client import hashlib from datetime import datetime, timedelta from itertools import chain, islice -import socket import requests from django.db import transaction @@ -18,7 +17,7 @@ from mygpo.core.slugs import PodcastSlugs, EpisodeSlugs from mygpo.podcasts.models import DEFAULT_UPDATE_INTERVAL, \ MIN_UPDATE_INTERVAL, MAX_UPDATE_INTERVAL -from mygpo.utils import file_hash, to_maxlength +from mygpo.utils import to_maxlength from mygpo.web.logo import CoverArt from mygpo.data.podcast import subscribe_at_hub from mygpo.data.tasks import update_related_podcasts @@ -26,6 +25,8 @@ from mygpo.directory.tags import update_category from mygpo.search import get_index_fields +from . import models + import logging logger = logging.getLogger(__name__) @@ -54,455 +55,495 @@ def update_podcasts(queue): continue try: - yield update_podcast(podcast_url) + updater = PodcastUpdater(podcast_url) + yield updater.update_podcast() except NoPodcastCreated as npc: logger.info('No podcast created: %s', npc) + except GeneratorExit: + pass + except: logger.exception('Error while updating podcast "%s"', podcast_url) raise -def update_podcast(podcast_url): - """ Update the podcast for the supplied URL """ +class PodcastUpdater(object): + """ Updates the podcast specified by the podcast_url """ - try: - parsed = _fetch_feed(podcast_url) - _validate_parsed(parsed) + def __init__(self, podcast_url): + self.podcast_url = podcast_url - except requests.exceptions.RequestException as re: - logging.exception('Error while fetching response from feedservice') + def update_podcast(self): + """ Update the podcast """ - # if we fail to parse the URL, we don't even create the - # podcast object - try: - p = Podcast.objects.get(urls__url=podcast_url) - # if it exists already, we mark it as outdated - _mark_outdated(p, 'error while fetching feed: %s' % str(re)) - p.last_update = datetime.utcnow() - p.save() - return p + with models.PodcastUpdateResult(podcast_url=self.podcast_url) as res: - except Podcast.DoesNotExist: - raise NoPodcastCreated(re) + parsed, podcast, created = self.parse_feed() - except NoEpisodesException as nee: - logging.warn('No episode found while parsing podcast') + if not podcast: + res.podcast_created = False + res.error_message = '"{}" could not be parsed'.format( + self.podcast_url) - # if we fail to parse the URL, we don't even create the - # podcast object - try: - p = Podcast.objects.get(urls__url=podcast_url) - # if it exists already, we mark it as outdated - _mark_outdated(p, 'error while fetching feed: %s' % str(nee)) - return p - - except Podcast.DoesNotExist: - raise NoPodcastCreated(nee) - - assert parsed, 'fetch_feed must return something' - p = Podcast.objects.get_or_create_for_url(podcast_url) - episodes = _update_episodes(p, parsed.get('episodes', [])) - p.refresh_from_db() - p.episode_count = Episode.objects.filter(podcast=p).count() - p.save() - max_episode_order = _order_episodes(p) - _update_podcast(p, parsed, episodes, max_episode_order) - return p - - -def verify_podcast_url(podcast_url): - parsed = _fetch_feed(podcast_url) - _validate_parsed(parsed) - return True + return + res.podcast = podcast + res.podcast_created = created -def _fetch_feed(podcast_url): - params = { - 'url': podcast_url, - 'process_text': 'markdown', - } - headers = { - 'Accept': 'application/json', - } - url = urljoin(settings.FEEDSERVICE_URL, 'parse') - r = requests.get(url, params=params, headers=headers, timeout=30) - - if r.status_code != 200: - logger.error('Feed-service status code for "%s" was %s', podcast_url, - r.status_code) - return None + res.episodes_added = 0 + episode_updater = MultiEpisodeUpdater(podcast, res) - try: - return r.json()[0] - except ValueError: - logger.exception( - 'Feed-service error while parsing response for url "%s": %s', - podcast_url, r.text, - ) - raise - - -def _validate_parsed(parsed): - """ validates the parsed results and raises an exception if invalid - - feedparser parses pretty much everything. We reject anything that - doesn't look like a feed""" - - if not parsed or not parsed.get('episodes', []): - raise NoEpisodesException('no episodes found') - - -def _update_podcast(podcast, parsed, episodes, max_episode_order): - """ updates a podcast according to new parser results """ - - # we need that later to decide if we can "bump" a category - prev_latest_episode_timestamp = podcast.latest_episode_timestamp - - # will later be used to see whether the index is outdated - old_index_fields = get_index_fields(podcast) - - podcast.title = parsed.get('title') or podcast.title - podcast.description = parsed.get('description') or podcast.description - podcast.subtitle = parsed.get('subtitle') or podcast.subtitle - podcast.link = parsed.get('link') or podcast.link - podcast.logo_url = parsed.get('logo') or podcast.logo_url - podcast.author = to_maxlength(Podcast, 'author', parsed.get('author') or - podcast.author) - podcast.language = to_maxlength(Podcast, 'language', - parsed.get('language') or podcast.language) - podcast.content_types = ','.join(parsed.get('content_types')) or \ - podcast.content_types - #podcast.tags['feed'] = parsed.tags or podcast.tags.get('feed', []) - podcast.common_episode_title = to_maxlength( - Podcast, - 'common_episode_title', - parsed.get('common_title') or podcast.common_episode_title) - podcast.new_location = parsed.get('new_location') or podcast.new_location - podcast.flattr_url = to_maxlength(Podcast, 'flattr_url', - parsed.get('flattr') or - podcast.flattr_url) - podcast.hub = parsed.get('hub') or podcast.hub - podcast.license = parsed.get('license') or podcast.license - podcast.max_episode_order = max_episode_order - - podcast.add_missing_urls(parsed.get('urls', [])) - - if podcast.new_location: - try: - new_podcast = Podcast.objects.get(urls__url=podcast.new_location) - if new_podcast != podcast: - _mark_outdated(podcast, 'redirected to different podcast') + if not parsed: + # if it exists already, we mark it as outdated + self._mark_outdated( + podcast, + 'error while fetching feed', + episode_updater) return - except Podcast.DoesNotExist: - podcast.set_url(podcast.new_location) - # latest episode timestamp - episodes = Episode.objects.filter(podcast=podcast, - released__isnull=False)\ - .order_by('released') + episode_updater.update_episodes(parsed.get('episodes', [])) - podcast.update_interval = get_update_interval(episodes) + podcast.refresh_from_db() + podcast.episode_count = episode_updater.count_episodes() + podcast.save() - latest_episode = episodes.last() - if latest_episode: - podcast.latest_episode_timestamp = latest_episode.released + episode_updater.order_episodes() - # podcast.episode_count is not update here on purpose. It is, instead, - # continuously updated when creating new episodes in - # EpisodeManager.get_or_create_for_url + self._update_podcast(podcast, parsed, episode_updater, res) - _update_categories(podcast, prev_latest_episode_timestamp) + return podcast - # try to download the logo and reset logo_url to None on http errors - found = _save_podcast_logo(podcast.logo_url) - if not found: - podcast.logo_url = None + def parse_feed(self): + try: + parsed = self._fetch_feed() + self._validate_parsed(parsed) + + except (requests.exceptions.RequestException, + NoEpisodesException) as ex: + logging.exception('Error while fetching/parsing feed') + # if we fail to parse the URL, we don't even create the + # podcast object + try: + p = Podcast.objects.get(urls__url=self.podcast_url) + return (None, p, False) + + except Podcast.DoesNotExist as pdne: + raise NoPodcastCreated(ex) from pdne + + # Parsing went well, get podcast + podcast, created = Podcast.objects.get_or_create_for_url( + self.podcast_url) + + return (parsed, podcast, created) + + def _fetch_feed(self): + params = { + 'url': self.podcast_url, + 'process_text': 'markdown', + } + headers = { + 'Accept': 'application/json', + } + url = urljoin(settings.FEEDSERVICE_URL, 'parse') + r = requests.get(url, params=params, headers=headers, timeout=30) + + if r.status_code != 200: + logger.error('Feed-service status code for "{}" was {}'.format( + url, r.status_code)) + return None - # check if search index should be considered out of date - new_index_fields = get_index_fields(podcast) - if list(old_index_fields.items()) != list(new_index_fields.items()): - podcast.search_index_uptodate = False + try: + return r.json()[0] + except ValueError: + logger.exception( + 'Feed-service error while parsing response for url "%s": %s', + podcast_url, r.text, + ) + raise - # The podcast is always saved (not just when there are changes) because - # we need to record the last update - logger.info('Saving podcast.') - podcast.last_update = datetime.utcnow() - podcast.save() + def _validate_parsed(self, parsed): + """ validates the parsed results and raises an exception if invalid - try: - subscribe_at_hub(podcast) - except SubscriptionError as se: - logger.warn('subscribing to hub failed: %s', str(se)) + feedparser parses pretty much everything. We reject anything that + doesn't look like a feed""" - assign_slug(podcast) - assign_missing_episode_slugs(podcast) - update_related_podcasts.delay(podcast.pk) + if not parsed or not parsed.get('episodes', []): + raise NoEpisodesException('no episodes found') + def _update_podcast(self, podcast, parsed, episode_updater, update_result): + """ updates a podcast according to new parser results """ -def assign_slug(podcast): - if podcast.slug: - return + # we need that later to decide if we can "bump" a category + prev_latest_episode_timestamp = podcast.latest_episode_timestamp - for slug in PodcastSlugs(podcast): - try: - with transaction.atomic(): - podcast.add_slug(slug) - break + # will later be used to see whether the index is outdated + old_index_fields = get_index_fields(podcast) - except: - continue + podcast.title = parsed.get('title') or podcast.title + podcast.description = parsed.get('description') or podcast.description + podcast.subtitle = parsed.get('subtitle') or podcast.subtitle + podcast.link = parsed.get('link') or podcast.link + podcast.logo_url = parsed.get('logo') or podcast.logo_url + podcast.author = to_maxlength( + Podcast, 'author', + parsed.get('author') or podcast.author) -def assign_missing_episode_slugs(podcast): - common_title = podcast.get_common_episode_title() + podcast.language = to_maxlength( + Podcast, 'language', + parsed.get('language') or podcast.language) - episodes = Episode.objects.filter(podcast=podcast, slugs__isnull=True) + podcast.content_types = (','.join(parsed.get('content_types')) or + podcast.content_types) - for episode in episodes: + # podcast.tags['feed'] = parsed.tags or podcast.tags.get('feed', []) - for slug in EpisodeSlugs(episode, common_title): + podcast.common_episode_title = to_maxlength( + Podcast, + 'common_episode_title', + parsed.get('common_title') or podcast.common_episode_title) + + podcast.new_location = (parsed.get('new_location') or + podcast.new_location) + podcast.flattr_url = to_maxlength(Podcast, 'flattr_url', + parsed.get('flattr') or + podcast.flattr_url) + podcast.hub = parsed.get('hub') or podcast.hub + podcast.license = parsed.get('license') or podcast.license + podcast.max_episode_order = episode_updater.max_episode_order + + podcast.add_missing_urls(parsed.get('urls', [])) + + if podcast.new_location: + try: + new_podcast = Podcast.objects.get( + urls__url=podcast.new_location + ) + + if new_podcast != podcast: + self._mark_outdated( + podcast, + 'redirected to different podcast', + episode_updater, + ) + return + except Podcast.DoesNotExist: + podcast.set_url(podcast.new_location) + + # latest episode timestamp + episodes = Episode.objects.filter(podcast=podcast, + released__isnull=False)\ + .order_by('released') + + # Determine update interval + + # Update interval is based on intervals between episodes + podcast.update_interval = episode_updater.get_update_interval(episodes) + + # factor is increased / decreased depending on whether the latest + # update has returned episodes + if update_result.episodes_added == 0: # no episodes, incr factor + newfactor = podcast.update_interval_factor * 1.2 + podcast.update_interval_factor = min(1000, newfactor) # never above 1000 + elif update_result.episodes_added > 1: # new episodes, decr factor + newfactor = podcast.update_interval_factor / 1.2 + podcast.update_interval_factor = max(1, newfactor) # never below 1 + + latest_episode = episodes.last() + if latest_episode: + podcast.latest_episode_timestamp = latest_episode.released + + # podcast.episode_count is not update here on purpose. It is, instead, + # continuously updated when creating new episodes in + # EpisodeManager.get_or_create_for_url + + self._update_categories(podcast, prev_latest_episode_timestamp) + + # try to download the logo and reset logo_url to None on http errors + found = CoverArt.save_podcast_logo(podcast.logo_url) + if not found: + podcast.logo_url = None + + # check if search index should be considered out of date + new_index_fields = get_index_fields(podcast) + if list(old_index_fields.items()) != list(new_index_fields.items()): + podcast.search_index_uptodate = False + + # The podcast is always saved (not just when there are changes) because + # we need to record the last update + logger.info('Saving podcast.') + podcast.last_update = datetime.utcnow() + podcast.save() + + try: + subscribe_at_hub(podcast) + except SubscriptionError as se: + logger.warn('subscribing to hub failed: %s', str(se)) + + self.assign_slug(podcast) + episode_updater.assign_missing_episode_slugs() + update_related_podcasts.delay(podcast.pk) + + def assign_slug(self, podcast): + if podcast.slug: + return + + for slug in PodcastSlugs(podcast): try: with transaction.atomic(): - episode.set_slug(slug) + podcast.add_slug(slug) break except: continue + def _update_categories(self, podcast, prev_timestamp): + """ checks some practical requirements and updates a category """ -def _update_categories(podcast, prev_timestamp): - """ checks some practical requirements and updates a category """ + max_timestamp = datetime.utcnow() + timedelta(days=1) - max_timestamp = datetime.utcnow() + timedelta(days=1) + # no episodes at all + if not podcast.latest_episode_timestamp: + return - # no episodes at all - if not podcast.latest_episode_timestamp: - return + # no new episode + if prev_timestamp and \ + (podcast.latest_episode_timestamp <= prev_timestamp): + return - # no new episode - if prev_timestamp and podcast.latest_episode_timestamp <= prev_timestamp: - return + # too far in the future + if podcast.latest_episode_timestamp > max_timestamp: + return - # too far in the future - if podcast.latest_episode_timestamp > max_timestamp: - return + # not enough subscribers + if podcast.subscriber_count() < settings.MIN_SUBSCRIBERS_CATEGORY: + return - # not enough subscribers - if podcast.subscriber_count() < settings.MIN_SUBSCRIBERS_CATEGORY: - return + update_category(podcast) - update_category(podcast) + def _mark_outdated(self, podcast, msg, episode_updater): + logger.info('marking podcast outdated: %s', msg) + podcast.outdated = True + podcast.last_update = datetime.utcnow() + podcast.save() + episode_updater.update_episodes([]) -def _update_episodes(podcast, parsed_episodes): +class MultiEpisodeUpdater(object): - pid = podcast.get_id() + def __init__(self, podcast, update_result): + self.podcast = podcast + self.update_result = update_result + self.updated_episodes = [] + self.max_episode_order = None - # list of (obj, fun) where fun is the function to update obj - updated_episodes = [] - episodes_to_update = list(islice(parsed_episodes, 0, MAX_EPISODES_UPDATE)) - logger.info('Parsed %d (%d) episodes', len(parsed_episodes), - len(episodes_to_update)) + def update_episodes(self, parsed_episodes): - logger.info('Updating %d episodes', len(episodes_to_update)) - for n, parsed in enumerate(episodes_to_update, 1): + pid = self.podcast.get_id() - url = get_episode_url(parsed) - if not url: - logger.info('Skipping episode %d for missing URL', n) - continue + episodes_to_update = list(islice(parsed_episodes, 0, + MAX_EPISODES_UPDATE)) + logger.info('Parsed %d (%d) episodes', len(parsed_episodes), + len(episodes_to_update)) - logger.info('Updating episode %d / %d', n, len(parsed_episodes)) + logger.info('Updating %d episodes', len(episodes_to_update)) + for n, parsed in enumerate(episodes_to_update, 1): - episode = Episode.objects.get_or_create_for_url(podcast, url) + url = self.get_episode_url(parsed) + if not url: + logger.info('Skipping episode %d for missing URL', n) + continue - update_episode(parsed, episode, podcast) - updated_episodes.append(episode) + logger.info('Updating episode %d / %d', n, len(parsed_episodes)) - # and mark the remaining ones outdated - current_episodes = Episode.objects.filter(podcast=podcast, - outdated=False)[:500] - outdated_episodes = set(current_episodes) - set(updated_episodes) + episode, created = Episode.objects.get_or_create_for_url( + self.podcast, url) - logger.info('Marking %d episodes as outdated', len(outdated_episodes)) - for episode in outdated_episodes: - mark_outdated(episode) + if created: + self.update_result.episodes_added += 1 + updater = EpisodeUpdater(episode, self.podcast) + updater.update_episode(parsed) -@transaction.atomic -def _order_episodes(podcast): - """ Reorder the podcast's episode according to release timestamp + self.updated_episodes.append(episode) - Returns the highest order value (corresponding to the most recent - episode) """ + # and mark the remaining ones outdated + current_episodes = Episode.objects.filter(podcast=self.podcast, + outdated=False)[:500] + outdated_episodes = set(current_episodes) - set(self.updated_episodes) - num_episodes = podcast.episode_count - if not num_episodes: - return 0 + logger.info('Marking %d episodes as outdated', len(outdated_episodes)) + for episode in outdated_episodes: + updater = EpisodeUpdater(episode, self.podcast) + updater.mark_outdated() - episodes = podcast.episode_set.all().extra(select={ - 'has_released': 'released IS NOT NULL', - })\ - .order_by('-has_released', '-released', 'pk')\ - .only('pk') + @transaction.atomic + def order_episodes(self): + """ Reorder the podcast's episode according to release timestamp - for n, episode in enumerate(episodes.iterator(), 1): - # assign ``order`` from higher (most recent) to 0 (oldest) - # None means "unknown" - new_order = num_episodes - n + Returns the highest order value (corresponding to the most recent + episode) """ - # optimize for new episodes that are newer than all existing - if episode.order == new_order: - continue + num_episodes = self.podcast.episode_count + if not num_episodes: + return 0 + + episodes = self.podcast.episode_set.all().extra(select={ + 'has_released': 'released IS NOT NULL', + })\ + .order_by('-has_released', '-released', 'pk')\ + .only('pk') + + for n, episode in enumerate(episodes.iterator(), 1): + # assign ``order`` from higher (most recent) to 0 (oldest) + # None means "unknown" + new_order = num_episodes - n + + # optimize for new episodes that are newer than all existing + if episode.order == new_order: + continue - logger.info('Updating order from {} to {}'.format(episode.order, - new_order)) - episode.order = new_order - episode.save() - - return num_episodes - 1 - - -def _save_podcast_logo(cover_art): - if not cover_art: - return - - try: - image_sha1 = hashlib.sha1(cover_art.encode('utf-8')).hexdigest() - prefix = CoverArt.get_prefix(image_sha1) - - filename = CoverArt.get_original(prefix, image_sha1) - dirname = CoverArt.get_dir(filename) - - # get hash of existing file - if os.path.exists(filename): - with open(filename, 'rb') as f: - old_hash = file_hash(f).digest() - else: - old_hash = '' - - logger.info('Logo %s', cover_art) - - # save new cover art - with open(filename, 'wb') as fp: - fp.write(urllib.request.urlopen(cover_art).read()) - - # get hash of new file - with open(filename, 'rb') as f: - new_hash = file_hash(f).digest() - - # remove thumbnails if cover changed - if old_hash != new_hash: - thumbnails = CoverArt.get_existing_thumbnails(prefix, filename) - logger.info('Removing %d thumbnails', len(thumbnails)) - for f in thumbnails: - os.unlink(f) - - return cover_art - - except (urllib.error.HTTPError, urllib.error.URLError, ValueError, - http.client.HTTPException, socket.error, IOError) as e: - logger.warn('Exception while updating podcast logo: %s', str(e)) - - -def _mark_outdated(podcast, msg=''): - logger.info('marking podcast outdated: %s', msg) - podcast.outdated = True - podcast.last_update = datetime.utcnow() - podcast.save() - _update_episodes(podcast, []) - - -def get_episode_url(parsed_episode): - """ returns the URL of a parsed episode """ - for f in parsed_episode.get('files', []): - if f.get('urls', []): - return f['urls'][0] - return None - - -def update_episode(parsed_episode, episode, podcast): - """ updates "episode" with the data from "parsed_episode" """ - - # TODO: check if there have been any changes, to avoid unnecessary updates - episode.guid = to_maxlength(Episode, 'guid', parsed_episode.get('guid') or - episode.guid) - episode.description = parsed_episode.get('description') or \ - episode.description - episode.subtitle = parsed_episode.get('subtitle') or episode.subtitle - episode.content = parsed_episode.get('content') or \ - parsed_episode.get('description') or episode.content - episode.link = to_maxlength(Episode, 'link', - parsed_episode.get('link') or episode.link) - episode.released = datetime.utcfromtimestamp( - parsed_episode.get('released')) if parsed_episode.get('released') \ - else episode.released - episode.author = to_maxlength(Episode, 'author', - parsed_episode.get('author') or - episode.author) - episode.duration = parsed_episode.get('duration') or episode.duration - episode.filesize = parsed_episode['files'][0]['filesize'] - episode.language = parsed_episode.get('language') or \ - episode.language or podcast.language - episode.mimetypes = ','.join(list(set( - filter(None, [f['mimetype'] for f in parsed_episode.get('files', [])]) - ))) - episode.flattr_url = to_maxlength(Episode, 'flattr_url', - parsed_episode.get('flattr') or - episode.flattr_url) - episode.license = parsed_episode.get('license') or episode.license - - episode.title = to_maxlength(Episode, 'title', - parsed_episode.get('title') or - episode.title or - file_basename_no_extension(episode.url)) - - episode.last_update = datetime.utcnow() - episode.save() - - parsed_urls = list(chain.from_iterable( - f.get('urls', []) for f in parsed_episode.get('files', []))) - episode.add_missing_urls(parsed_urls) - - -def mark_outdated(obj): - """ marks obj outdated if its not already """ - if obj.outdated: + logger.info('Updating order from {} to {}'.format(episode.order, + new_order)) + episode.order = new_order + episode.save() + + self.max_episode_order = num_episodes - 1 + + def get_episode_url(self, parsed_episode): + """ returns the URL of a parsed episode """ + for f in parsed_episode.get('files', []): + if f.get('urls', []): + return f['urls'][0] return None - obj.outdated = True - obj.last_update = datetime.utcnow() - obj.save() + def count_episodes(self): + return Episode.objects.filter(podcast=self.podcast).count() + + def get_update_interval(self, episodes): + """ calculates the avg interval between new episodes """ + + count = episodes.count() + if not count: + logger.info('no episodes, using default interval of %dh', + DEFAULT_UPDATE_INTERVAL) + return DEFAULT_UPDATE_INTERVAL + + earliest = episodes.first() + now = datetime.utcnow() + + timespan_s = (now - earliest.released).total_seconds() + timespan_h = timespan_s / 60 / 60 + + interval = int(timespan_h / count) + logger.info('%d episodes in %d days => %dh interval', count, + timespan_h / 24, interval) + + # place interval between {MIN,MAX}_UPDATE_INTERVAL + interval = max(interval, MIN_UPDATE_INTERVAL) + interval = min(interval, MAX_UPDATE_INTERVAL) + + return interval + + def assign_missing_episode_slugs(self): + common_title = self.podcast.get_common_episode_title() + + episodes = Episode.objects.filter(podcast=self.podcast, + slugs__isnull=True) + + for episode in episodes: + + for slug in EpisodeSlugs(episode, common_title): + try: + with transaction.atomic(): + episode.set_slug(slug) + break + + except: + continue + + +class EpisodeUpdater(object): + """ Updates an individual episode """ + def __init__(self, episode, podcast): + self.episode = episode + self.podcast = podcast -def get_update_interval(episodes): - """ calculates the avg interval between new episodes """ + def update_episode(self, parsed_episode): + """ updates "episode" with the data from "parsed_episode" """ - count = episodes.count() - if not count: - logger.info('no episodes, using default interval of %dh', - DEFAULT_UPDATE_INTERVAL) - return DEFAULT_UPDATE_INTERVAL + # TODO: check if there have been any changes, to + # avoid unnecessary updates + self.episode.guid = to_maxlength( + Episode, 'guid', + parsed_episode.get('guid') or self.episode.guid) - earliest = episodes.first() - now = datetime.utcnow() + self.episode.description = (parsed_episode.get('description') or + self.episode.description) - timespan_s = (now - earliest.released).total_seconds() - timespan_h = timespan_s / 60 / 60 + self.episode.subtitle = (parsed_episode.get('subtitle') or + self.episode.subtitle) - interval = int(timespan_h / count) - logger.info('%d episodes in %d days => %dh interval', count, - timespan_h / 24, interval) + self.episode.content = (parsed_episode.get('content') or + parsed_episode.get('description') or + self.episode.content) - # place interval between {MIN,MAX}_UPDATE_INTERVAL - interval = max(interval, MIN_UPDATE_INTERVAL) - interval = min(interval, MAX_UPDATE_INTERVAL) + self.episode.link = to_maxlength( + Episode, 'link', + parsed_episode.get('link') or self.episode.link) - return interval + self.episode.released = (datetime.utcfromtimestamp( + parsed_episode.get('released')) if parsed_episode.get('released') + else self.episode.released) + + self.episode.author = to_maxlength( + Episode, 'author', + parsed_episode.get('author') or self.episode.author) + + self.episode.duration = (parsed_episode.get('duration') or + self.episode.duration) + + self.episode.filesize = parsed_episode['files'][0]['filesize'] + + self.episode.language = (parsed_episode.get('language') or + self.episode.language or + self.podcast.language) + + mimetypes = [f['mimetype'] for f in parsed_episode.get('files', [])] + self.episode.mimetypes = ','.join(list(set(filter(None, mimetypes)))) + + self.episode.flattr_url = to_maxlength( + Episode, 'flattr_url', + parsed_episode.get('flattr') or self.episode.flattr_url) + + self.episode.license = (parsed_episode.get('license') or + self.episode.license) + + self.episode.title = to_maxlength( + Episode, 'title', + parsed_episode.get('title') or self.episode.title or + file_basename_no_extension(self.episode.url)) + + self.episode.last_update = datetime.utcnow() + self.episode.save() + + parsed_urls = list(chain.from_iterable( + f.get('urls', []) for f in parsed_episode.get('files', []))) + self.episode.add_missing_urls(parsed_urls) + + def mark_outdated(self): + """ marks the episode outdated if its not already """ + if self.episode.outdated: + return None + + self.episode.outdated = True + self.episode.last_update = datetime.utcnow() + self.episode.save() def file_basename_no_extension(filename): @@ -517,3 +558,10 @@ def file_basename_no_extension(filename): base = os.path.basename(filename) name, extension = os.path.splitext(base) return name + + +def verify_podcast_url(url): + updater = PodcastUpdater(url) + parsed = updater._fetch_feed() + updater._validate_parsed(parsed) + return True diff --git a/mygpo/data/flickr.py b/mygpo/data/flickr.py index 65209ddd2..918ae3cf7 100644 --- a/mygpo/data/flickr.py +++ b/mygpo/data/flickr.py @@ -1,6 +1,6 @@ import json import re -import urllib.request, urllib.parse, urllib.error +import requests from django.conf import settings @@ -8,23 +8,35 @@ logger = logging.getLogger(__name__) +GET_SIZES_TEMPLATE = 'https://api.flickr.com/services/rest/?method=flickr.photos.getSizes&api_key={api_key}&photo_id={photo_id}&format=json&nojsoncallback=1' + + def get_photo_sizes(photo_id): + """ Returns available sizes for the photo with the given ID + + Returns a list of dicts containing the following keys + * source + * url + * media + * height + * width + * label + """ + api_key = settings.FLICKR_API_KEY - request = 'https://api.flickr.com/services/rest/?method=flickr.photos.getSizes&api_key=%s&photo_id=%s&format=json' % (api_key, photo_id) + url = GET_SIZES_TEMPLATE.format(api_key=api_key, photo_id=photo_id) try: - resp = urllib.request.urlopen(request).read().decode('utf-8') - except urllib.error.HTTPError as e: + resp = requests.get(url) + except requests.exceptions.RequestException as e: logger.warn('Retrieving Flickr photo sizes failed: %s', str(e)) return [] - extract_re = '^jsonFlickrApi\((.*)\)$' - m = re.match(extract_re, resp) - if not m: + try: + resp_obj = resp.json() + except json.JSONDecodeError as jde: return [] - resp_obj = json.loads(m.group(1)) - try: return resp_obj['sizes']['size'] except KeyError: @@ -32,16 +44,44 @@ def get_photo_sizes(photo_id): def get_photo_id(url): - photo_id_re = 'http://.*flickr.com/[^/]+/([^_]+)_.*' - match = re.match(photo_id_re, url) - if match: - return match.group(1) + """ Returns the Photo ID for a Photo URL + + >>> get_photo_id('https://farm9.staticflickr.com/8747/12346789012_bf1e234567_b.jpg') + '12346789012' + + >>> get_photo_id('https://www.flickr.com/photos/someuser/12345678901/') + '12345678901' + + """ + + photo_id_re = [ + 'http://.*flickr.com/[^/]+/([^_]+)_.*', + 'https://.*staticflickr.com/[^/]+/([^_]+)_.*', + 'https?://.*flickr.com/photos/[^/]+/([^/]+).*', + ] + + for regex in photo_id_re: + match = re.match(regex, url) + if match: + return match.group(1) def is_flickr_image(url): + """ Returns True if the URL represents a Flickr images + + >>> is_flickr_image('https://farm9.staticflickr.com/8747/12346789012_bf1e234567_b.jpg') + True + + >>> is_flickr_image('http://www.example.com/podcast.mp3') + False + + >>> is_flickr_image(None) + False + """ + if url is None: return False - return re.search('flickr\.com.*\.(jpg|jpeg|png|gif)', url) + return bool(re.search('flickr\.com.*\.(jpg|jpeg|png|gif)', url)) def get_display_photo(url, label='Medium'): photo_id = get_photo_id(url) diff --git a/mygpo/data/management/commands/feed-downloader.py b/mygpo/data/management/commands/feed-downloader.py index a9a05cb75..3283c7251 100644 --- a/mygpo/data/management/commands/feed-downloader.py +++ b/mygpo/data/management/commands/feed-downloader.py @@ -16,8 +16,7 @@ class Command(PodcastCommand): def add_arguments(self, parser): - parser.add_argument('urls', nargs='+', type=str) - + super().add_arguments(parser) parser.add_argument('--list-only', action='store_true', dest='list', default=False, help="Don't update anything, just list podcasts "), diff --git a/mygpo/data/migrations/0001_initial.py b/mygpo/data/migrations/0001_initial.py new file mode 100644 index 000000000..4a4399168 --- /dev/null +++ b/mygpo/data/migrations/0001_initial.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.4 on 2017-08-06 14:46 +from __future__ import unicode_literals + +import datetime +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('podcasts', '0037_index_podcast_lastupdate'), + ] + + operations = [ + migrations.CreateModel( + name='PodcastUpdateResult', + fields=[ + ('id', models.UUIDField(primary_key=True, serialize=False)), + ('start', models.DateTimeField( + default=datetime.datetime.utcnow, + )), + ('duration', models.DurationField()), + ('successful', models.BooleanField()), + ('error_message', models.TextField()), + ('podcast_created', models.BooleanField()), + ('episodes_added', models.IntegerField()), + ('podcast', models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to='podcasts.Podcast', + )), + ], + options={ + 'get_latest_by': 'start', + 'ordering': ['-start'], + }, + ), + migrations.AddIndex( + model_name='podcastupdateresult', + index=models.Index( + fields=['podcast', 'start'], + name='data_podcas_podcast_cf4cc1_idx', + ), + ), + ] diff --git a/mygpo/data/migrations/0002_result_podcast_null.py b/mygpo/data/migrations/0002_result_podcast_null.py new file mode 100644 index 000000000..4149dfcf4 --- /dev/null +++ b/mygpo/data/migrations/0002_result_podcast_null.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2017-12-03 17:22 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('data', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='podcastupdateresult', + name='podcast', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='podcasts.Podcast'), + ), + ] diff --git a/mygpo/data/migrations/0003_podcastupdateresult_podcast_url.py b/mygpo/data/migrations/0003_podcastupdateresult_podcast_url.py new file mode 100644 index 000000000..89b0d97c5 --- /dev/null +++ b/mygpo/data/migrations/0003_podcastupdateresult_podcast_url.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2017-12-03 17:28 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('data', '0002_result_podcast_null'), + ] + + operations = [ + migrations.AddField( + model_name='podcastupdateresult', + name='podcast_url', + field=models.URLField(default='unknown', max_length=2048), + preserve_default=False, + ), + ] diff --git a/mygpo/data/migrations/__init__.py b/mygpo/data/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/mygpo/data/models.py b/mygpo/data/models.py new file mode 100644 index 000000000..02b0bc11e --- /dev/null +++ b/mygpo/data/models.py @@ -0,0 +1,76 @@ +import uuid + +from datetime import datetime + +from django.db import models + +from mygpo.core.models import UUIDModel +from mygpo.podcasts.models import Podcast + + +class PodcastUpdateResult(UUIDModel): + """ Results of a podcast update + + Once an instance is stored, the update is assumed to be finished. """ + + # URL of the podcast to be updated + podcast_url = models.URLField(max_length=2048) + + # The podcast that was updated + podcast = models.ForeignKey(Podcast, on_delete=models.CASCADE, null=True) + + # The timestamp at which the updated started to be executed + start = models.DateTimeField(default=datetime.utcnow) + + # The duration of the update + duration = models.DurationField() + + # A flad indicating whether the update was successful + successful = models.BooleanField() + + # An error message. Should be empty if the update was successful + error_message = models.TextField() + + # A flag indicating whether the update created the podcast + podcast_created = models.BooleanField() + + # The number of episodes that were created by the update + episodes_added = models.IntegerField() + + class Meta(object): + + get_latest_by = 'start' + + ordering = ['-start'] + + indexes = [ + models.Index(fields=['podcast', 'start']) + ] + + def __str__(self): + return 'Update Result for "{}" @ {:%Y-%m-%d %H:%M}'.format( + self.podcast, self.start) + + # Use as context manager + + def __enter__(self): + self.id = uuid.uuid4() + self.start = datetime.utcnow() + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.duration = datetime.utcnow() - self.start + + success = (exc_type, exc_value, traceback) == (None, None, None) + self.successful = success + + if not success: + self.error_message = str(exc_value) + + if self.podcast_created is None: + self.podcast_created = False + + if self.episodes_added is None: + self.episodes_added = 0 + + self.save() diff --git a/mygpo/data/tasks.py b/mygpo/data/tasks.py index 09d241c69..a4e577b12 100644 --- a/mygpo/data/tasks.py +++ b/mygpo/data/tasks.py @@ -18,6 +18,7 @@ def update_podcasts(podcast_urls): """ Task to update a podcast """ from mygpo.data.feeddownloader import update_podcasts as update podcasts = update(podcast_urls) + podcasts = filter(None, podcasts) return [podcast.pk for podcast in podcasts] @@ -25,7 +26,7 @@ def update_podcasts(podcast_urls): def update_related_podcasts(podcast_pk, max_related=20): get_podcast = itemgetter(0) - podcast = Podcast.objects.get(pk=pk) + podcast = Podcast.objects.get(pk=podcast_pk) related = calc_similar_podcasts(podcast)[:max_related] related = map(get_podcast, related) @@ -78,5 +79,6 @@ def _schedule_updates(podcasts): for podcast in podcasts: # update_podcasts.delay() seems to block other task execution, # therefore celery.send_task() is used instead + urls = [podcast.url] celery.send_task('mygpo.data.tasks.update_podcasts', - args=[podcast.url]) + args=[urls]) diff --git a/mygpo/data/tests.py b/mygpo/data/tests.py index 8b1378917..9c8f0380a 100644 --- a/mygpo/data/tests.py +++ b/mygpo/data/tests.py @@ -1 +1,64 @@ +import re +import json + +from django.test import TestCase + +from . import flickr + +import responses + + +MEDIUM_URL = 'https://farm6.staticflickr.com/5001/1246644888_36863b0856.jpg' + +API_RESPONSE = { + 'stat': 'ok', + 'sizes': { + 'canblog': 0, + 'size': [ + { + 'source': 'https://farm6.staticflickr.com/5001/1234533888_45673b0856_s.jpg', + 'url': 'https://www.flickr.com/photos/someuser/135643888/sizes/sq/', + 'media': 'photo', + 'height': 75, + 'width': 75, + 'label': 'Square' + }, + { + 'source': MEDIUM_URL, + 'url': 'https://www.flickr.com/photos/someuser/3465234888/sizes/m/', + 'media': 'photo', + 'height': '500', + 'width': '333', + 'label': 'Medium' + }, + ], + 'candownload': 1, + 'canprint': 0 + } +} + +FLICKR_URL = re.compile('https://api.flickr.com/services/rest/\?method=flickr.photos.getSizes&api_key=.*photo_id=.*&format=json&nojsoncallback=1') + + +class FlickrTests(TestCase): + + def test_get_sizes(self): + with responses.RequestsMock() as rsps: + rsps.add(responses.GET, FLICKR_URL, status=200, + body=json.dumps(API_RESPONSE)) + + sizes = flickr.get_photo_sizes('1235123123') + + self.assertEquals(sizes, API_RESPONSE['sizes']['size']) + + + def test_display_image(self): + with responses.RequestsMock() as rsps: + rsps.add(responses.GET, FLICKR_URL, status=200, + body=json.dumps(API_RESPONSE)) + + disp_photo = flickr.get_display_photo('https://farm9.staticflickr.com/8747/12346789012_bf1e234567_b.jpg') + + self.assertEquals(disp_photo, MEDIUM_URL) + diff --git a/mygpo/directory/search.py b/mygpo/directory/search.py index 483619a36..48d01c15c 100644 --- a/mygpo/directory/search.py +++ b/mygpo/directory/search.py @@ -1,6 +1,6 @@ from mygpo.podcasts.models import Podcast from mygpo.utils import is_url, normalize_feed_url -from mygpo.data.feeddownloader import update_podcast, NoPodcastCreated +from mygpo.data.feeddownloader import PodcastUpdater, NoPodcastCreated from mygpo.search.index import search_podcasts as search @@ -14,9 +14,11 @@ def search_podcasts(q): except Podcast.DoesNotExist: podcast = None + updater = PodcastUpdater(url) + if not podcast or not podcast.title: try: - update_podcast(url) + updater.update_podcast() except NoPodcastCreated as npc: return [] diff --git a/mygpo/directory/urls.py b/mygpo/directory/urls.py index ef2d15880..498f7f867 100644 --- a/mygpo/directory/urls.py +++ b/mygpo/directory/urls.py @@ -1,55 +1,55 @@ -from django.conf.urls import url +from django.urls import path from . import views urlpatterns = [ - url(r'^toplist/$', + path('toplist/', views.PodcastToplistView.as_view(), name='toplist'), - url(r'^toplist/episodes$', + path('toplist/episodes', views.EpisodeToplistView.as_view(), name='episode-toplist'), - url(r'^directory/$', + path('directory/', views.Directory.as_view(), name='directory-home'), - url(r'^carousel/$', + path('carousel/', views.Carousel.as_view(), name='carousel-demo'), - url(r'^missing/$', + path('missing/', views.MissingPodcast.as_view(), name='missing-podcast'), - url(r'^add-podcast/$', + path('add-podcast/', views.AddPodcast.as_view(), name='add-podcast'), - url(r'^add-podcast/(?P[^/]+)$', + path('add-podcast/', views.AddPodcastStatus.as_view(), name='add-podcast-status'), - url(r'^directory/\+license$', + path('directory/+license', views.LicenseList.as_view(), name='license-podcasts'), - url(r'^directory/\+license/\+url/(?P.+)$', + path('directory/+license/+url/', views.LicensePodcastList.as_view(), name='license-podcasts-url'), - url(r'^directory/(?P.+)$', + path('directory/', views.category, name='directory'), - url(r'^search/$', + path('search/', views.search, name='search'), - url(r'^lists/$', + path('lists/', views.podcast_lists, name='podcast-lists'), diff --git a/mygpo/web/templates/episode-history.html b/mygpo/history/templates/episode-history.html similarity index 93% rename from mygpo/web/templates/episode-history.html rename to mygpo/history/templates/episode-history.html index d7c4c0c13..f1b55ea5d 100644 --- a/mygpo/web/templates/episode-history.html +++ b/mygpo/history/templates/episode-history.html @@ -10,6 +10,7 @@ {% load flickr %} {% load charts %} {% load utils %} +{% load static %} {% load menu %} {% block mainmenu %}{{ "/podcast/"|main_menu }}{% endblock %} @@ -23,9 +24,9 @@ {% block head %} {% if episode.url|is_youtube_video %} - - - + + + {% endif %} {% endblock head %} diff --git a/mygpo/web/templates/podcast-history.html b/mygpo/history/templates/podcast-history.html similarity index 100% rename from mygpo/web/templates/podcast-history.html rename to mygpo/history/templates/podcast-history.html diff --git a/mygpo/history/urls.py b/mygpo/history/urls.py index a5ed7d674..f15d9f245 100644 --- a/mygpo/history/urls.py +++ b/mygpo/history/urls.py @@ -1,23 +1,27 @@ -from django.conf.urls import url +from django.urls import path, register_converter from . import views +from mygpo.users import converters + + +register_converter(converters.ClientUIDConverter, 'client-uid') urlpatterns = [ - url(r'^history/$', + path('history/', views.history, name='history'), - url(r'^podcast/(?P[0-9a-f]{32})/\+history', + path('podcast//+history', views.history_podcast_id, name='podcast-history-id'), - url(r'^podcast/(?P[\w-]+)/\+history', + path('podcast//+history', views.history_podcast_slug, name='podcast-history-slug'), - url(r'^device/(?P[\w.-]+)/history$', + path('device//history', views.history, name='device-history'), diff --git a/mygpo/locale/de/LC_MESSAGES/django.po b/mygpo/locale/de/LC_MESSAGES/django.po index 06d415a0e..cc73a135e 100644 --- a/mygpo/locale/de/LC_MESSAGES/django.po +++ b/mygpo/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: 1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-10-12 18:58+0000\n" +"POT-Creation-Date: 2018-06-29 20:11+0000\n" "PO-Revision-Date: 2010-07-16 08:51+0200\n" "Last-Translator: Stefan Koegl \n" "Language-Team: DE \n" @@ -105,47 +105,51 @@ msgstr "Typ" msgid "Episode URLs" msgstr "Episoden" -#: mygpo/administration/templates/admin/hostinfo.html:9 -#: mygpo/administration/templates/admin/hostinfo.html:12 +#: mygpo/administration/templates/admin/hostinfo.html:10 +#: mygpo/administration/templates/admin/hostinfo.html:13 #: mygpo/administration/templates/admin/overview.html:17 msgid "Host Information" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:20 +#: mygpo/administration/templates/admin/hostinfo.html:21 msgid "mygpo Version" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:31 +#: mygpo/administration/templates/admin/hostinfo.html:32 #, fuzzy msgid "Base Directory" msgstr "Verzeichnis" -#: mygpo/administration/templates/admin/hostinfo.html:36 +#: mygpo/administration/templates/admin/hostinfo.html:37 #, fuzzy msgid "Hostname" msgstr "Benutzername" -#: mygpo/administration/templates/admin/hostinfo.html:41 +#: mygpo/administration/templates/admin/hostinfo.html:42 msgid "Django Version" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:46 +#: mygpo/administration/templates/admin/hostinfo.html:47 msgid "Feed Update Queue" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:49 +#: mygpo/administration/templates/admin/hostinfo.html:50 msgid "min ahead" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:51 +#: mygpo/administration/templates/admin/hostinfo.html:52 msgid "min behind" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:59 +#: mygpo/administration/templates/admin/hostinfo.html:60 msgid "Number of podcasts with outdated search index" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:66 +#: mygpo/administration/templates/admin/hostinfo.html:69 +msgid "Average podcast update duration" +msgstr "" + +#: mygpo/administration/templates/admin/hostinfo.html:76 msgid "Scheduled Celery Tasks" msgstr "" @@ -153,7 +157,7 @@ msgstr "" #: mygpo/administration/templates/admin/make-publisher-input.html:12 #: mygpo/administration/templates/admin/make-publisher-result.html:9 #: mygpo/administration/templates/admin/make-publisher-result.html:12 -#: mygpo/administration/views.py:391 +#: mygpo/administration/views.py:404 #, fuzzy #| msgid "Publisher Pages" msgid "Publisher Permissions" @@ -276,21 +280,21 @@ msgstr "" msgid "User-Agent" msgstr "" -#: mygpo/administration/views.py:140 mygpo/administration/views.py:162 +#: mygpo/administration/views.py:153 mygpo/administration/views.py:175 #, python-brace-format msgid "No podcast with URL {url}" msgstr "" -#: mygpo/administration/views.py:316 +#: mygpo/administration/views.py:329 msgid "Provide either username or email address" msgstr "" -#: mygpo/administration/views.py:322 +#: mygpo/administration/views.py:335 #, fuzzy msgid "No user found" msgstr "Nicht gefunden" -#: mygpo/administration/views.py:327 +#: mygpo/administration/views.py:340 #, python-brace-format msgid "User {username} ({email}) activated" msgstr "" @@ -328,16 +332,16 @@ msgstr "abonnement gelöscht" msgid "%(username)s's Subscription List" msgstr "%(username)ss Podcast-Abos" -#: mygpo/api/simple.py:237 +#: mygpo/api/simple.py:240 #, fuzzy, python-format msgid "gpodder.net - Top %(count)d" msgstr "my.gpodder.org - Top %s" -#: mygpo/api/simple.py:272 +#: mygpo/api/simple.py:275 msgid "gpodder.net - Search" msgstr "gpodder.net - Suche" -#: mygpo/api/simple.py:289 +#: mygpo/api/simple.py:292 #, fuzzy, python-format msgid "gpodder.net - %(count)d Suggestions" msgstr "my.gpodder.org - %s Empfehlungen" @@ -360,7 +364,7 @@ msgid "Explore" msgstr "" #: mygpo/directory/templates/carousel.html:81 -#: mygpo/web/templates/episode.html:150 +#: mygpo/podcasts/templates/episode.html:150 #, fuzzy msgid "..." msgstr "mehr..." @@ -492,11 +496,11 @@ msgid "Listeners" msgstr "Hörer" #: mygpo/directory/templates/episode_toplist.html:36 +#: mygpo/history/templates/episode-history.html:51 +#: mygpo/podcasts/templates/episode.html:51 #: mygpo/publisher/templates/publisher/episode.html:33 #: mygpo/share/templates/userpage.html:118 #: mygpo/share/templates/userpage.html:138 -#: mygpo/web/templates/episode-history.html:50 -#: mygpo/web/templates/episode.html:51 msgid "from" msgstr "von" @@ -571,9 +575,9 @@ msgid "User" msgstr "Benutzername" #: mygpo/directory/templates/podcast_lists.html:25 +#: mygpo/history/templates/episode-history.html:53 +#: mygpo/podcasts/templates/episode.html:53 #: mygpo/publisher/templates/publisher/episode.html:35 -#: mygpo/web/templates/episode-history.html:52 -#: mygpo/web/templates/episode.html:53 msgid "Download" msgstr "herunterladen" @@ -632,10 +636,49 @@ msgstr "" msgid "%d podcasts added" msgstr "Podcasts" +#: mygpo/history/templates/episode-history.html:47 +#: mygpo/history/templates/episode-history.html:81 +#: mygpo/web/templates/device.html:73 mygpo/web/templatetags/menu.py:51 +msgid "History" +msgstr "Verlauf" + +#: mygpo/history/templates/episode-history.html:58 +#: mygpo/podcasts/templates/episode.html:58 +#: mygpo/podcasts/templates/podcast-base.html:54 +#: mygpo/publisher/templates/publisher/episode.html:40 +#: mygpo/publisher/templates/publisher/podcast.html:39 +#, fuzzy +msgid "Website" +msgstr "Website" + +#: mygpo/history/templates/episode-history.html:63 +#: mygpo/podcasts/templates/episode.html:63 +#: mygpo/publisher/templates/publisher/episode.html:45 +#, fuzzy +msgid "listeners" +msgstr "Hörer" + +#: mygpo/history/templates/episode-history.html:84 +msgid "Time" +msgstr "Zeitpunkt" + +#: mygpo/history/templates/episode-history.html:85 +msgid "Action" +msgstr "Aktivität" + +#: mygpo/history/templates/episode-history.html:86 +#: mygpo/web/templatetags/menu.py:50 +msgid "Device" +msgstr "Geräte" + +#: mygpo/history/templates/episode-history.html:127 +msgid "Add" +msgstr "Hinzufügen" + #: mygpo/history/templates/history.html:12 #: mygpo/history/templates/history.html:15 -#: mygpo/web/templates/podcast-history.html:32 -#: mygpo/web/templates/podcast.html:143 +#: mygpo/history/templates/podcast-history.html:32 +#: mygpo/podcasts/templates/podcast.html:143 #, fuzzy msgid "Subscription History" msgstr "Abo-Verlauf" @@ -670,6 +713,10 @@ msgstr "Nein" msgid "Earlier" msgstr "" +#: mygpo/history/templates/podcast-history.html:47 +msgid "no history yet" +msgstr "" + #: mygpo/podcastlists/templates/list.html:28 #, python-format msgid "\"%(list_title)s\" by %(ownername)s" @@ -765,17 +812,104 @@ msgstr "" msgid "Edit" msgstr "Bearbeiten" -#: mygpo/podcasts/models.py:688 +#: mygpo/podcasts/models.py:704 #, fuzzy #| msgid "Unnamed Podcast" msgid "Unknown Podcast" msgstr "Unbenannter Podcast" -#: mygpo/podcasts/models.py:690 +#: mygpo/podcasts/models.py:706 #, python-brace-format msgid "Unknown Podcast from {domain}" msgstr "" +#: mygpo/podcasts/templates/episode.html:85 +#: mygpo/podcasts/templates/episodes.html:27 +#: mygpo/podcasts/templates/podcast.html:39 +#: mygpo/publisher/templates/publisher/episode.html:31 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/info.html:9 +#: mygpo/publisher/templates/publisher/info.html:26 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Publisher Pages" +msgstr "Publisher-Seiten" + +#: mygpo/podcasts/templates/episode.html:91 +#, fuzzy +msgid "Remove Favorite" +msgstr "Favorit" + +#: mygpo/podcasts/templates/episode.html:95 +msgid "Favorite" +msgstr "Favorit" + +#: mygpo/podcasts/templates/episode.html:103 +#, fuzzy +#| msgid "Your Episode History" +msgid "Episode History" +msgstr "Dein Episoden Verlauf" + +#: mygpo/podcasts/templates/podcast-base.html:39 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Unnamed Podcast" +msgstr "Unbenannter Podcast" + +#: mygpo/podcasts/templates/podcast-base.html:44 +#: mygpo/publisher/templates/publisher/podcast.html:29 +msgid "by" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:50 +#: mygpo/publisher/templates/publisher/podcast.html:35 +#: mygpo/share/templates/share/favorites.html:29 +msgid "Feed" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:65 +#: mygpo/publisher/templates/publisher/podcast.html:44 +#, fuzzy +msgid "subscribers" +msgstr "Abonnenten" + +#: mygpo/podcasts/templates/podcast.html:48 +#, fuzzy +#| msgid "Subscribe" +msgid "Login to Subscribe" +msgstr "Abonnieren" + +#: mygpo/podcasts/templates/podcast.html:59 +#: mygpo/suggestions/templates/suggestions.html:31 +#: mygpo/web/templates/subscribe.html:11 mygpo/web/templates/subscribe.html:36 +msgid "Subscribe" +msgstr "Abonnieren" + +#: mygpo/podcasts/templates/podcast.html:67 mygpo/web/templates/device.html:31 +msgid "Unsubscribe" +msgstr "Entfernen" + +#: mygpo/podcasts/templates/podcast.html:78 +#, fuzzy +msgid "Subscribe on all devices" +msgstr "Abonniere neue Podcasts" + +#: mygpo/podcasts/templates/podcast.html:110 +#, fuzzy +msgid "Unsubscribe from all devices " +msgstr "Abonniere neue Podcasts" + +#: mygpo/podcasts/templates/podcast.html:149 +#: mygpo/podcasts/templates/podcast.html:164 +#, fuzzy +#| msgid "My Tags" +msgid "Tags" +msgstr "Meine Tags" + +#: mygpo/podcasts/templates/podcast.html:199 +#, fuzzy +msgid "Older Episodes" +msgstr "Episoden" + #: mygpo/publisher/forms.py:5 #: mygpo/publisher/templates/publisher/episode.html:60 msgid "URL" @@ -790,7 +924,7 @@ msgid "Link to" msgstr "Link zu" #: mygpo/publisher/templates/link.html:17 -#: mygpo/publisher/templates/publisher/podcast.html:171 +#: mygpo/publisher/templates/publisher/podcast.html:209 #, fuzzy, python-format msgid "" "You can paste this code on your website, so users of %(sitename)s can " @@ -898,31 +1032,6 @@ msgstr "" msgid "Unnamed Episode" msgstr "Namenlose Episode" -#: mygpo/publisher/templates/publisher/episode.html:31 -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/info.html:9 -#: mygpo/publisher/templates/publisher/info.html:26 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/episode.html:85 mygpo/web/templates/episodes.html:27 -#: mygpo/web/templates/podcast.html:39 -msgid "Publisher Pages" -msgstr "Publisher-Seiten" - -#: mygpo/publisher/templates/publisher/episode.html:40 -#: mygpo/publisher/templates/publisher/podcast.html:38 -#: mygpo/web/templates/episode-history.html:57 -#: mygpo/web/templates/episode.html:58 mygpo/web/templates/podcast-base.html:54 -#, fuzzy -msgid "Website" -msgstr "Website" - -#: mygpo/publisher/templates/publisher/episode.html:45 -#: mygpo/web/templates/episode-history.html:62 -#: mygpo/web/templates/episode.html:63 -#, fuzzy -msgid "listeners" -msgstr "Hörer" - #: mygpo/publisher/templates/publisher/episode.html:55 #, fuzzy msgid "Episode List" @@ -940,7 +1049,7 @@ msgid "" msgstr "" #: mygpo/publisher/templates/publisher/episode.html:72 -#: mygpo/publisher/templates/publisher/podcast.html:91 +#: mygpo/publisher/templates/publisher/podcast.html:129 #: mygpo/web/templates/account.html:65 mygpo/web/templates/account.html:142 #: mygpo/web/templates/device-edit.html:53 msgid "Save" @@ -955,12 +1064,6 @@ msgstr "Episoden-Status" msgid "Last update: " msgstr "" -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/podcast-base.html:39 -msgid "Unnamed Podcast" -msgstr "Unbenannter Podcast" - #: mygpo/publisher/templates/publisher/episodes.html:24 msgid "Return to Podcast Page" msgstr "Zurück zum Podcast" @@ -1124,77 +1227,59 @@ msgstr "" "Wenn Sie auf %(sitename)s verlinken machen " "Sie es Ihren Besuchern besonders einfach Ihren Podcasts zu abonnieren" -#: mygpo/publisher/templates/publisher/podcast.html:28 -#: mygpo/web/templates/podcast-base.html:44 -msgid "by" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:34 -#: mygpo/share/templates/share/favorites.html:29 -#: mygpo/web/templates/podcast-base.html:50 -msgid "Feed" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:43 -#: mygpo/web/templates/podcast-base.html:65 -#, fuzzy -msgid "subscribers" -msgstr "Abonnenten" - -#: mygpo/publisher/templates/publisher/podcast.html:48 +#: mygpo/publisher/templates/publisher/podcast.html:49 #, python-format msgid "" "This is the publisher page of %(ptitle)s. You can see some " "stats and provide additional data for the podcast page." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:49 +#: mygpo/publisher/templates/publisher/podcast.html:50 #, fuzzy msgid "Go to Podcast Page" msgstr "Zurück zum Podcast" -#: mygpo/publisher/templates/publisher/podcast.html:56 +#: mygpo/publisher/templates/publisher/podcast.html:57 msgid "The podcast information is regularly retrieved from the podcast feed" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:59 -msgid "Timing" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:61 +#: mygpo/publisher/templates/publisher/podcast.html:60 #, fuzzy -msgid "Last update:" -msgstr "Geräte-Liste" +msgid "Updates" +msgstr "Daten aus dem Feed holen" #: mygpo/publisher/templates/publisher/podcast.html:62 #, fuzzy msgid "Update interval:" msgstr "Daten aus dem Feed holen" -#: mygpo/publisher/templates/publisher/podcast.html:63 -#, fuzzy -msgid "Next update:" -msgstr "Geräte-Liste" +#: mygpo/publisher/templates/publisher/podcast.html:86 +msgid "Successful" +msgstr "" + +#: mygpo/publisher/templates/publisher/podcast.html:88 +msgid "Error" +msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:68 +#: mygpo/publisher/templates/publisher/podcast.html:106 #, fuzzy msgid "Update now" msgstr "Daten aus dem Feed holen" -#: mygpo/publisher/templates/publisher/podcast.html:79 mygpo/web/forms.py:70 +#: mygpo/publisher/templates/publisher/podcast.html:117 mygpo/web/forms.py:70 #: mygpo/web/templates/base.html:160 mygpo/web/templates/home.html:180 msgid "Twitter" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:97 +#: mygpo/publisher/templates/publisher/podcast.html:135 msgid "Feed Check" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:105 +#: mygpo/publisher/templates/publisher/podcast.html:143 msgid "PubSubHubbub" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:114 +#: mygpo/publisher/templates/publisher/podcast.html:152 #, python-format msgid "" "If you publish your podcast feed through a %(hub)s and should " "update immediatelly for each new episode." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:123 +#: mygpo/publisher/templates/publisher/podcast.html:161 #, python-format msgid "" "Your podcast is published through %(hub)s but our " "subscription has not yet been verified." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:128 +#: mygpo/publisher/templates/publisher/podcast.html:166 #, python-format msgid "" "We did not find a hub in your podcast feed. Your feed is updated regularly, " "but there might be some delay until a new episode shows up on %(sitename)s." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:140 +#: mygpo/publisher/templates/publisher/podcast.html:178 msgid "License Information" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:148 +#: mygpo/publisher/templates/publisher/podcast.html:186 #, python-format msgid "" "You should include license information in your feed so that users and " "%(sitename)s can know, under which conditions your content can be used." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:152 +#: mygpo/publisher/templates/publisher/podcast.html:190 #, python-format msgid "" "We found the following license in your podcast: " "%(license)s" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:156 +#: mygpo/publisher/templates/publisher/podcast.html:194 msgid "" "We did not find a license in your podcast feed. Refer to \n" "Language-Team: LANGUAGE \n" @@ -99,45 +99,49 @@ msgstr "" msgid "Episode URLs" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:9 -#: mygpo/administration/templates/admin/hostinfo.html:12 +#: mygpo/administration/templates/admin/hostinfo.html:10 +#: mygpo/administration/templates/admin/hostinfo.html:13 #: mygpo/administration/templates/admin/overview.html:17 msgid "Host Information" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:20 +#: mygpo/administration/templates/admin/hostinfo.html:21 msgid "mygpo Version" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:31 +#: mygpo/administration/templates/admin/hostinfo.html:32 msgid "Base Directory" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:36 +#: mygpo/administration/templates/admin/hostinfo.html:37 msgid "Hostname" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:41 +#: mygpo/administration/templates/admin/hostinfo.html:42 msgid "Django Version" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:46 +#: mygpo/administration/templates/admin/hostinfo.html:47 msgid "Feed Update Queue" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:49 +#: mygpo/administration/templates/admin/hostinfo.html:50 msgid "min ahead" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:51 +#: mygpo/administration/templates/admin/hostinfo.html:52 msgid "min behind" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:59 +#: mygpo/administration/templates/admin/hostinfo.html:60 msgid "Number of podcasts with outdated search index" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:66 +#: mygpo/administration/templates/admin/hostinfo.html:69 +msgid "Average podcast update duration" +msgstr "" + +#: mygpo/administration/templates/admin/hostinfo.html:76 msgid "Scheduled Celery Tasks" msgstr "" @@ -145,7 +149,7 @@ msgstr "" #: mygpo/administration/templates/admin/make-publisher-input.html:12 #: mygpo/administration/templates/admin/make-publisher-result.html:9 #: mygpo/administration/templates/admin/make-publisher-result.html:12 -#: mygpo/administration/views.py:391 +#: mygpo/administration/views.py:404 msgid "Publisher Permissions" msgstr "" @@ -256,20 +260,20 @@ msgstr "" msgid "User-Agent" msgstr "" -#: mygpo/administration/views.py:140 mygpo/administration/views.py:162 +#: mygpo/administration/views.py:153 mygpo/administration/views.py:175 #, python-brace-format msgid "No podcast with URL {url}" msgstr "" -#: mygpo/administration/views.py:316 +#: mygpo/administration/views.py:329 msgid "Provide either username or email address" msgstr "" -#: mygpo/administration/views.py:322 +#: mygpo/administration/views.py:335 msgid "No user found" msgstr "" -#: mygpo/administration/views.py:327 +#: mygpo/administration/views.py:340 #, python-brace-format msgid "User {username} ({email}) activated" msgstr "" @@ -307,16 +311,16 @@ msgstr "" msgid "%(username)s's Subscription List" msgstr "" -#: mygpo/api/simple.py:237 +#: mygpo/api/simple.py:240 #, python-format msgid "gpodder.net - Top %(count)d" msgstr "" -#: mygpo/api/simple.py:272 +#: mygpo/api/simple.py:275 msgid "gpodder.net - Search" msgstr "" -#: mygpo/api/simple.py:289 +#: mygpo/api/simple.py:292 #, python-format msgid "gpodder.net - %(count)d Suggestions" msgstr "" @@ -339,7 +343,7 @@ msgid "Explore" msgstr "" #: mygpo/directory/templates/carousel.html:81 -#: mygpo/web/templates/episode.html:150 +#: mygpo/podcasts/templates/episode.html:150 msgid "..." msgstr "" @@ -462,11 +466,11 @@ msgid "Listeners" msgstr "" #: mygpo/directory/templates/episode_toplist.html:36 +#: mygpo/history/templates/episode-history.html:51 +#: mygpo/podcasts/templates/episode.html:51 #: mygpo/publisher/templates/publisher/episode.html:33 #: mygpo/share/templates/userpage.html:118 #: mygpo/share/templates/userpage.html:138 -#: mygpo/web/templates/episode-history.html:50 -#: mygpo/web/templates/episode.html:51 msgid "from" msgstr "" @@ -534,9 +538,9 @@ msgid "User" msgstr "" #: mygpo/directory/templates/podcast_lists.html:25 +#: mygpo/history/templates/episode-history.html:53 +#: mygpo/podcasts/templates/episode.html:53 #: mygpo/publisher/templates/publisher/episode.html:35 -#: mygpo/web/templates/episode-history.html:52 -#: mygpo/web/templates/episode.html:53 msgid "Download" msgstr "" @@ -593,10 +597,47 @@ msgstr "" msgid "%d podcasts added" msgstr "" +#: mygpo/history/templates/episode-history.html:47 +#: mygpo/history/templates/episode-history.html:81 +#: mygpo/web/templates/device.html:73 mygpo/web/templatetags/menu.py:51 +msgid "History" +msgstr "" + +#: mygpo/history/templates/episode-history.html:58 +#: mygpo/podcasts/templates/episode.html:58 +#: mygpo/podcasts/templates/podcast-base.html:54 +#: mygpo/publisher/templates/publisher/episode.html:40 +#: mygpo/publisher/templates/publisher/podcast.html:39 +msgid "Website" +msgstr "" + +#: mygpo/history/templates/episode-history.html:63 +#: mygpo/podcasts/templates/episode.html:63 +#: mygpo/publisher/templates/publisher/episode.html:45 +msgid "listeners" +msgstr "" + +#: mygpo/history/templates/episode-history.html:84 +msgid "Time" +msgstr "" + +#: mygpo/history/templates/episode-history.html:85 +msgid "Action" +msgstr "" + +#: mygpo/history/templates/episode-history.html:86 +#: mygpo/web/templatetags/menu.py:50 +msgid "Device" +msgstr "" + +#: mygpo/history/templates/episode-history.html:127 +msgid "Add" +msgstr "" + #: mygpo/history/templates/history.html:12 #: mygpo/history/templates/history.html:15 -#: mygpo/web/templates/podcast-history.html:32 -#: mygpo/web/templates/podcast.html:143 +#: mygpo/history/templates/podcast-history.html:32 +#: mygpo/podcasts/templates/podcast.html:143 msgid "Subscription History" msgstr "" @@ -629,6 +670,10 @@ msgstr "" msgid "Earlier" msgstr "" +#: mygpo/history/templates/podcast-history.html:47 +msgid "no history yet" +msgstr "" + #: mygpo/podcastlists/templates/list.html:28 #, python-format msgid "\"%(list_title)s\" by %(ownername)s" @@ -720,15 +765,91 @@ msgstr "" msgid "Edit" msgstr "" -#: mygpo/podcasts/models.py:688 +#: mygpo/podcasts/models.py:704 msgid "Unknown Podcast" msgstr "" -#: mygpo/podcasts/models.py:690 +#: mygpo/podcasts/models.py:706 #, python-brace-format msgid "Unknown Podcast from {domain}" msgstr "" +#: mygpo/podcasts/templates/episode.html:85 +#: mygpo/podcasts/templates/episodes.html:27 +#: mygpo/podcasts/templates/podcast.html:39 +#: mygpo/publisher/templates/publisher/episode.html:31 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/info.html:9 +#: mygpo/publisher/templates/publisher/info.html:26 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Publisher Pages" +msgstr "" + +#: mygpo/podcasts/templates/episode.html:91 +msgid "Remove Favorite" +msgstr "" + +#: mygpo/podcasts/templates/episode.html:95 +msgid "Favorite" +msgstr "" + +#: mygpo/podcasts/templates/episode.html:103 +msgid "Episode History" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:39 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Unnamed Podcast" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:44 +#: mygpo/publisher/templates/publisher/podcast.html:29 +msgid "by" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:50 +#: mygpo/publisher/templates/publisher/podcast.html:35 +#: mygpo/share/templates/share/favorites.html:29 +msgid "Feed" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:65 +#: mygpo/publisher/templates/publisher/podcast.html:44 +msgid "subscribers" +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:48 +msgid "Login to Subscribe" +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:59 +#: mygpo/suggestions/templates/suggestions.html:31 +#: mygpo/web/templates/subscribe.html:11 mygpo/web/templates/subscribe.html:36 +msgid "Subscribe" +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:67 mygpo/web/templates/device.html:31 +msgid "Unsubscribe" +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:78 +msgid "Subscribe on all devices" +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:110 +msgid "Unsubscribe from all devices " +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:149 +#: mygpo/podcasts/templates/podcast.html:164 +msgid "Tags" +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:199 +msgid "Older Episodes" +msgstr "" + #: mygpo/publisher/forms.py:5 #: mygpo/publisher/templates/publisher/episode.html:60 msgid "URL" @@ -743,7 +864,7 @@ msgid "Link to" msgstr "" #: mygpo/publisher/templates/link.html:17 -#: mygpo/publisher/templates/publisher/podcast.html:171 +#: mygpo/publisher/templates/publisher/podcast.html:209 #, python-format msgid "" "You can paste this code on your website, so users of %(sitename)s can " @@ -831,29 +952,6 @@ msgstr "" msgid "Unnamed Episode" msgstr "" -#: mygpo/publisher/templates/publisher/episode.html:31 -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/info.html:9 -#: mygpo/publisher/templates/publisher/info.html:26 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/episode.html:85 mygpo/web/templates/episodes.html:27 -#: mygpo/web/templates/podcast.html:39 -msgid "Publisher Pages" -msgstr "" - -#: mygpo/publisher/templates/publisher/episode.html:40 -#: mygpo/publisher/templates/publisher/podcast.html:38 -#: mygpo/web/templates/episode-history.html:57 -#: mygpo/web/templates/episode.html:58 mygpo/web/templates/podcast-base.html:54 -msgid "Website" -msgstr "" - -#: mygpo/publisher/templates/publisher/episode.html:45 -#: mygpo/web/templates/episode-history.html:62 -#: mygpo/web/templates/episode.html:63 -msgid "listeners" -msgstr "" - #: mygpo/publisher/templates/publisher/episode.html:55 msgid "Episode List" msgstr "" @@ -869,7 +967,7 @@ msgid "" msgstr "" #: mygpo/publisher/templates/publisher/episode.html:72 -#: mygpo/publisher/templates/publisher/podcast.html:91 +#: mygpo/publisher/templates/publisher/podcast.html:129 #: mygpo/web/templates/account.html:65 mygpo/web/templates/account.html:142 #: mygpo/web/templates/device-edit.html:53 msgid "Save" @@ -883,12 +981,6 @@ msgstr "" msgid "Last update: " msgstr "" -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/podcast-base.html:39 -msgid "Unnamed Podcast" -msgstr "" - #: mygpo/publisher/templates/publisher/episodes.html:24 msgid "Return to Podcast Page" msgstr "" @@ -1023,71 +1115,55 @@ msgid "" "podcast - especially for users of mobile phones." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:28 -#: mygpo/web/templates/podcast-base.html:44 -msgid "by" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:34 -#: mygpo/share/templates/share/favorites.html:29 -#: mygpo/web/templates/podcast-base.html:50 -msgid "Feed" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:43 -#: mygpo/web/templates/podcast-base.html:65 -msgid "subscribers" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:48 +#: mygpo/publisher/templates/publisher/podcast.html:49 #, python-format msgid "" "This is the publisher page of %(ptitle)s. You can see some " "stats and provide additional data for the podcast page." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:49 +#: mygpo/publisher/templates/publisher/podcast.html:50 msgid "Go to Podcast Page" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:56 +#: mygpo/publisher/templates/publisher/podcast.html:57 msgid "The podcast information is regularly retrieved from the podcast feed" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:59 -msgid "Timing" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:61 -msgid "Last update:" +#: mygpo/publisher/templates/publisher/podcast.html:60 +msgid "Updates" msgstr "" #: mygpo/publisher/templates/publisher/podcast.html:62 msgid "Update interval:" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:63 -msgid "Next update:" +#: mygpo/publisher/templates/publisher/podcast.html:86 +msgid "Successful" +msgstr "" + +#: mygpo/publisher/templates/publisher/podcast.html:88 +msgid "Error" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:68 +#: mygpo/publisher/templates/publisher/podcast.html:106 msgid "Update now" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:79 mygpo/web/forms.py:70 +#: mygpo/publisher/templates/publisher/podcast.html:117 mygpo/web/forms.py:70 #: mygpo/web/templates/base.html:160 mygpo/web/templates/home.html:180 msgid "Twitter" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:97 +#: mygpo/publisher/templates/publisher/podcast.html:135 msgid "Feed Check" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:105 +#: mygpo/publisher/templates/publisher/podcast.html:143 msgid "PubSubHubbub" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:114 +#: mygpo/publisher/templates/publisher/podcast.html:152 #, python-format msgid "" "If you publish your podcast feed through a %(hub)s and should " "update immediatelly for each new episode." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:123 +#: mygpo/publisher/templates/publisher/podcast.html:161 #, python-format msgid "" "Your podcast is published through %(hub)s but our " "subscription has not yet been verified." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:128 +#: mygpo/publisher/templates/publisher/podcast.html:166 #, python-format msgid "" "We did not find a hub in your podcast feed. Your feed is updated regularly, " "but there might be some delay until a new episode shows up on %(sitename)s." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:140 +#: mygpo/publisher/templates/publisher/podcast.html:178 msgid "License Information" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:148 +#: mygpo/publisher/templates/publisher/podcast.html:186 #, python-format msgid "" "You should include license information in your feed so that users and " "%(sitename)s can know, under which conditions your content can be used." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:152 +#: mygpo/publisher/templates/publisher/podcast.html:190 #, python-format msgid "" "We found the following license in your podcast: " "%(license)s" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:156 +#: mygpo/publisher/templates/publisher/podcast.html:194 msgid "" "We did not find a license in your podcast feed. Refer to " msgstr "" -#: mygpo/web/templatetags/devices.py:31 +#: mygpo/web/templatetags/devices.py:32 msgid "Unknown" msgstr "" @@ -2514,11 +2520,16 @@ msgstr "" msgid "Link to gpodder.net" msgstr "" -#: mygpo/web/templatetags/time.py:38 +#: mygpo/web/templatetags/time.py:42 #, python-brace-format msgid "{h}h {m}m {s}s" msgstr "" +#: mygpo/web/templatetags/time.py:44 +#, python-brace-format +msgid "{m}m {s}s" +msgstr "" + #: mygpo/web/utils.py:281 #, python-format msgid "%(weeks)d week" diff --git a/mygpo/locale/es/LC_MESSAGES/django.po b/mygpo/locale/es/LC_MESSAGES/django.po index d004978b5..77803859f 100644 --- a/mygpo/locale/es/LC_MESSAGES/django.po +++ b/mygpo/locale/es/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: my.gpodder.org\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-10-12 18:58+0000\n" +"POT-Creation-Date: 2018-06-29 20:11+0000\n" "PO-Revision-Date: 2010-02-08 18:45-0300\n" "Last-Translator: Silvio Sisto \n" "Language-Team: gpodder team \n" @@ -99,46 +99,50 @@ msgstr "" msgid "Episode URLs" msgstr "Mejores podcasts" -#: mygpo/administration/templates/admin/hostinfo.html:9 -#: mygpo/administration/templates/admin/hostinfo.html:12 +#: mygpo/administration/templates/admin/hostinfo.html:10 +#: mygpo/administration/templates/admin/hostinfo.html:13 #: mygpo/administration/templates/admin/overview.html:17 msgid "Host Information" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:20 +#: mygpo/administration/templates/admin/hostinfo.html:21 msgid "mygpo Version" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:31 +#: mygpo/administration/templates/admin/hostinfo.html:32 #, fuzzy msgid "Base Directory" msgstr "Historial" -#: mygpo/administration/templates/admin/hostinfo.html:36 +#: mygpo/administration/templates/admin/hostinfo.html:37 msgid "Hostname" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:41 +#: mygpo/administration/templates/admin/hostinfo.html:42 msgid "Django Version" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:46 +#: mygpo/administration/templates/admin/hostinfo.html:47 msgid "Feed Update Queue" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:49 +#: mygpo/administration/templates/admin/hostinfo.html:50 msgid "min ahead" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:51 +#: mygpo/administration/templates/admin/hostinfo.html:52 msgid "min behind" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:59 +#: mygpo/administration/templates/admin/hostinfo.html:60 msgid "Number of podcasts with outdated search index" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:66 +#: mygpo/administration/templates/admin/hostinfo.html:69 +msgid "Average podcast update duration" +msgstr "" + +#: mygpo/administration/templates/admin/hostinfo.html:76 msgid "Scheduled Celery Tasks" msgstr "" @@ -146,7 +150,7 @@ msgstr "" #: mygpo/administration/templates/admin/make-publisher-input.html:12 #: mygpo/administration/templates/admin/make-publisher-result.html:9 #: mygpo/administration/templates/admin/make-publisher-result.html:12 -#: mygpo/administration/views.py:391 +#: mygpo/administration/views.py:404 msgid "Publisher Permissions" msgstr "" @@ -258,21 +262,21 @@ msgstr "" msgid "User-Agent" msgstr "" -#: mygpo/administration/views.py:140 mygpo/administration/views.py:162 +#: mygpo/administration/views.py:153 mygpo/administration/views.py:175 #, python-brace-format msgid "No podcast with URL {url}" msgstr "" -#: mygpo/administration/views.py:316 +#: mygpo/administration/views.py:329 msgid "Provide either username or email address" msgstr "" -#: mygpo/administration/views.py:322 +#: mygpo/administration/views.py:335 #, fuzzy msgid "No user found" msgstr "No se encuentra" -#: mygpo/administration/views.py:327 +#: mygpo/administration/views.py:340 #, python-brace-format msgid "User {username} ({email}) activated" msgstr "" @@ -310,16 +314,16 @@ msgstr "" msgid "%(username)s's Subscription List" msgstr "" -#: mygpo/api/simple.py:237 +#: mygpo/api/simple.py:240 #, python-format msgid "gpodder.net - Top %(count)d" msgstr "" -#: mygpo/api/simple.py:272 +#: mygpo/api/simple.py:275 msgid "gpodder.net - Search" msgstr "" -#: mygpo/api/simple.py:289 +#: mygpo/api/simple.py:292 #, python-format msgid "gpodder.net - %(count)d Suggestions" msgstr "" @@ -342,7 +346,7 @@ msgid "Explore" msgstr "" #: mygpo/directory/templates/carousel.html:81 -#: mygpo/web/templates/episode.html:150 +#: mygpo/podcasts/templates/episode.html:150 msgid "..." msgstr "" @@ -472,11 +476,11 @@ msgid "Listeners" msgstr "" #: mygpo/directory/templates/episode_toplist.html:36 +#: mygpo/history/templates/episode-history.html:51 +#: mygpo/podcasts/templates/episode.html:51 #: mygpo/publisher/templates/publisher/episode.html:33 #: mygpo/share/templates/userpage.html:118 #: mygpo/share/templates/userpage.html:138 -#: mygpo/web/templates/episode-history.html:50 -#: mygpo/web/templates/episode.html:51 msgid "from" msgstr "" @@ -548,9 +552,9 @@ msgid "User" msgstr "" #: mygpo/directory/templates/podcast_lists.html:25 +#: mygpo/history/templates/episode-history.html:53 +#: mygpo/podcasts/templates/episode.html:53 #: mygpo/publisher/templates/publisher/episode.html:35 -#: mygpo/web/templates/episode-history.html:52 -#: mygpo/web/templates/episode.html:53 msgid "Download" msgstr "" @@ -609,10 +613,48 @@ msgstr "" msgid "%d podcasts added" msgstr "" +#: mygpo/history/templates/episode-history.html:47 +#: mygpo/history/templates/episode-history.html:81 +#: mygpo/web/templates/device.html:73 mygpo/web/templatetags/menu.py:51 +msgid "History" +msgstr "Historial" + +#: mygpo/history/templates/episode-history.html:58 +#: mygpo/podcasts/templates/episode.html:58 +#: mygpo/podcasts/templates/podcast-base.html:54 +#: mygpo/publisher/templates/publisher/episode.html:40 +#: mygpo/publisher/templates/publisher/podcast.html:39 +#, fuzzy +msgid "Website" +msgstr "Dispositivos" + +#: mygpo/history/templates/episode-history.html:63 +#: mygpo/podcasts/templates/episode.html:63 +#: mygpo/publisher/templates/publisher/episode.html:45 +msgid "listeners" +msgstr "" + +#: mygpo/history/templates/episode-history.html:84 +msgid "Time" +msgstr "" + +#: mygpo/history/templates/episode-history.html:85 +msgid "Action" +msgstr "" + +#: mygpo/history/templates/episode-history.html:86 +#: mygpo/web/templatetags/menu.py:50 +msgid "Device" +msgstr "" + +#: mygpo/history/templates/episode-history.html:127 +msgid "Add" +msgstr "" + #: mygpo/history/templates/history.html:12 #: mygpo/history/templates/history.html:15 -#: mygpo/web/templates/podcast-history.html:32 -#: mygpo/web/templates/podcast.html:143 +#: mygpo/history/templates/podcast-history.html:32 +#: mygpo/podcasts/templates/podcast.html:143 msgid "Subscription History" msgstr "" @@ -645,6 +687,10 @@ msgstr "" msgid "Earlier" msgstr "" +#: mygpo/history/templates/podcast-history.html:47 +msgid "no history yet" +msgstr "" + #: mygpo/podcastlists/templates/list.html:28 #, python-format msgid "\"%(list_title)s\" by %(ownername)s" @@ -739,16 +785,94 @@ msgstr "" msgid "Edit" msgstr "" -#: mygpo/podcasts/models.py:688 +#: mygpo/podcasts/models.py:704 #, fuzzy msgid "Unknown Podcast" msgstr "Mejores podcasts" -#: mygpo/podcasts/models.py:690 +#: mygpo/podcasts/models.py:706 #, python-brace-format msgid "Unknown Podcast from {domain}" msgstr "" +#: mygpo/podcasts/templates/episode.html:85 +#: mygpo/podcasts/templates/episodes.html:27 +#: mygpo/podcasts/templates/podcast.html:39 +#: mygpo/publisher/templates/publisher/episode.html:31 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/info.html:9 +#: mygpo/publisher/templates/publisher/info.html:26 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Publisher Pages" +msgstr "" + +#: mygpo/podcasts/templates/episode.html:91 +msgid "Remove Favorite" +msgstr "" + +#: mygpo/podcasts/templates/episode.html:95 +msgid "Favorite" +msgstr "" + +#: mygpo/podcasts/templates/episode.html:103 +#, fuzzy +msgid "Episode History" +msgstr "Mejores podcasts" + +#: mygpo/podcasts/templates/podcast-base.html:39 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Unnamed Podcast" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:44 +#: mygpo/publisher/templates/publisher/podcast.html:29 +msgid "by" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:50 +#: mygpo/publisher/templates/publisher/podcast.html:35 +#: mygpo/share/templates/share/favorites.html:29 +msgid "Feed" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:65 +#: mygpo/publisher/templates/publisher/podcast.html:44 +msgid "subscribers" +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:48 +msgid "Login to Subscribe" +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:59 +#: mygpo/suggestions/templates/suggestions.html:31 +#: mygpo/web/templates/subscribe.html:11 mygpo/web/templates/subscribe.html:36 +msgid "Subscribe" +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:67 mygpo/web/templates/device.html:31 +msgid "Unsubscribe" +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:78 +msgid "Subscribe on all devices" +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:110 +msgid "Unsubscribe from all devices " +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:149 +#: mygpo/podcasts/templates/podcast.html:164 +msgid "Tags" +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:199 +#, fuzzy +msgid "Older Episodes" +msgstr "Mejores podcasts" + #: mygpo/publisher/forms.py:5 #: mygpo/publisher/templates/publisher/episode.html:60 msgid "URL" @@ -763,7 +887,7 @@ msgid "Link to" msgstr "" #: mygpo/publisher/templates/link.html:17 -#: mygpo/publisher/templates/publisher/podcast.html:171 +#: mygpo/publisher/templates/publisher/podcast.html:209 #, python-format msgid "" "You can paste this code on your website, so users of %(sitename)s can " @@ -853,30 +977,6 @@ msgstr "" msgid "Unnamed Episode" msgstr "" -#: mygpo/publisher/templates/publisher/episode.html:31 -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/info.html:9 -#: mygpo/publisher/templates/publisher/info.html:26 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/episode.html:85 mygpo/web/templates/episodes.html:27 -#: mygpo/web/templates/podcast.html:39 -msgid "Publisher Pages" -msgstr "" - -#: mygpo/publisher/templates/publisher/episode.html:40 -#: mygpo/publisher/templates/publisher/podcast.html:38 -#: mygpo/web/templates/episode-history.html:57 -#: mygpo/web/templates/episode.html:58 mygpo/web/templates/podcast-base.html:54 -#, fuzzy -msgid "Website" -msgstr "Dispositivos" - -#: mygpo/publisher/templates/publisher/episode.html:45 -#: mygpo/web/templates/episode-history.html:62 -#: mygpo/web/templates/episode.html:63 -msgid "listeners" -msgstr "" - #: mygpo/publisher/templates/publisher/episode.html:55 #, fuzzy msgid "Episode List" @@ -894,7 +994,7 @@ msgid "" msgstr "" #: mygpo/publisher/templates/publisher/episode.html:72 -#: mygpo/publisher/templates/publisher/podcast.html:91 +#: mygpo/publisher/templates/publisher/podcast.html:129 #: mygpo/web/templates/account.html:65 mygpo/web/templates/account.html:142 #: mygpo/web/templates/device-edit.html:53 msgid "Save" @@ -909,12 +1009,6 @@ msgstr "Mejores podcasts" msgid "Last update: " msgstr "" -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/podcast-base.html:39 -msgid "Unnamed Podcast" -msgstr "" - #: mygpo/publisher/templates/publisher/episodes.html:24 msgid "Return to Podcast Page" msgstr "" @@ -1051,74 +1145,57 @@ msgid "" "podcast - especially for users of mobile phones." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:28 -#: mygpo/web/templates/podcast-base.html:44 -msgid "by" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:34 -#: mygpo/share/templates/share/favorites.html:29 -#: mygpo/web/templates/podcast-base.html:50 -msgid "Feed" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:43 -#: mygpo/web/templates/podcast-base.html:65 -msgid "subscribers" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:48 +#: mygpo/publisher/templates/publisher/podcast.html:49 #, python-format msgid "" "This is the publisher page of %(ptitle)s. You can see some " "stats and provide additional data for the podcast page." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:49 +#: mygpo/publisher/templates/publisher/podcast.html:50 #, fuzzy msgid "Go to Podcast Page" msgstr "Mejores podcasts" -#: mygpo/publisher/templates/publisher/podcast.html:56 +#: mygpo/publisher/templates/publisher/podcast.html:57 msgid "The podcast information is regularly retrieved from the podcast feed" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:59 -msgid "Timing" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:61 +#: mygpo/publisher/templates/publisher/podcast.html:60 #, fuzzy -msgid "Last update:" -msgstr "Dispositivos" +msgid "Updates" +msgstr "Mejores podcasts" #: mygpo/publisher/templates/publisher/podcast.html:62 msgid "Update interval:" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:63 -#, fuzzy -msgid "Next update:" -msgstr "Dispositivos" +#: mygpo/publisher/templates/publisher/podcast.html:86 +msgid "Successful" +msgstr "" + +#: mygpo/publisher/templates/publisher/podcast.html:88 +msgid "Error" +msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:68 +#: mygpo/publisher/templates/publisher/podcast.html:106 msgid "Update now" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:79 mygpo/web/forms.py:70 +#: mygpo/publisher/templates/publisher/podcast.html:117 mygpo/web/forms.py:70 #: mygpo/web/templates/base.html:160 mygpo/web/templates/home.html:180 msgid "Twitter" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:97 +#: mygpo/publisher/templates/publisher/podcast.html:135 msgid "Feed Check" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:105 +#: mygpo/publisher/templates/publisher/podcast.html:143 msgid "PubSubHubbub" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:114 +#: mygpo/publisher/templates/publisher/podcast.html:152 #, python-format msgid "" "If you publish your podcast feed through a %(hub)s and should " "update immediatelly for each new episode." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:123 +#: mygpo/publisher/templates/publisher/podcast.html:161 #, python-format msgid "" "Your podcast is published through %(hub)s but our " "subscription has not yet been verified." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:128 +#: mygpo/publisher/templates/publisher/podcast.html:166 #, python-format msgid "" "We did not find a hub in your podcast feed. Your feed is updated regularly, " "but there might be some delay until a new episode shows up on %(sitename)s." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:140 +#: mygpo/publisher/templates/publisher/podcast.html:178 msgid "License Information" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:148 +#: mygpo/publisher/templates/publisher/podcast.html:186 #, python-format msgid "" "You should include license information in your feed so that users and " "%(sitename)s can know, under which conditions your content can be used." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:152 +#: mygpo/publisher/templates/publisher/podcast.html:190 #, python-format msgid "" "We found the following license in your podcast: " "%(license)s" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:156 +#: mygpo/publisher/templates/publisher/podcast.html:194 msgid "" "We did not find a license in your podcast feed. Refer to " msgstr "" -#: mygpo/web/templatetags/devices.py:31 +#: mygpo/web/templatetags/devices.py:32 msgid "Unknown" msgstr "" @@ -2581,11 +2586,16 @@ msgstr "" msgid "Link to gpodder.net" msgstr "" -#: mygpo/web/templatetags/time.py:38 +#: mygpo/web/templatetags/time.py:42 #, python-brace-format msgid "{h}h {m}m {s}s" msgstr "" +#: mygpo/web/templatetags/time.py:44 +#, python-brace-format +msgid "{m}m {s}s" +msgstr "" + #: mygpo/web/utils.py:281 #, python-format msgid "%(weeks)d week" @@ -2611,6 +2621,14 @@ msgstr[1] "" msgid "another site" msgstr "" +#, fuzzy +#~ msgid "Last update:" +#~ msgstr "Dispositivos" + +#, fuzzy +#~ msgid "Next update:" +#~ msgstr "Dispositivos" + #, fuzzy #~ msgid "No Payment URL available" #~ msgstr "No hay logotipo disponible" diff --git a/mygpo/locale/fr/LC_MESSAGES/django.po b/mygpo/locale/fr/LC_MESSAGES/django.po index c53a1b888..c8a55298b 100644 --- a/mygpo/locale/fr/LC_MESSAGES/django.po +++ b/mygpo/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-10-12 18:58+0000\n" +"POT-Creation-Date: 2018-06-29 20:11+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Louis Carlioz Luigi \n" "Language-Team: FR \n" @@ -105,47 +105,51 @@ msgstr "Genre" msgid "Episode URLs" msgstr "Épisodes" -#: mygpo/administration/templates/admin/hostinfo.html:9 -#: mygpo/administration/templates/admin/hostinfo.html:12 +#: mygpo/administration/templates/admin/hostinfo.html:10 +#: mygpo/administration/templates/admin/hostinfo.html:13 #: mygpo/administration/templates/admin/overview.html:17 msgid "Host Information" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:20 +#: mygpo/administration/templates/admin/hostinfo.html:21 msgid "mygpo Version" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:31 +#: mygpo/administration/templates/admin/hostinfo.html:32 #, fuzzy msgid "Base Directory" msgstr "Répertoire" -#: mygpo/administration/templates/admin/hostinfo.html:36 +#: mygpo/administration/templates/admin/hostinfo.html:37 #, fuzzy msgid "Hostname" msgstr "Nom d'utilisateur" -#: mygpo/administration/templates/admin/hostinfo.html:41 +#: mygpo/administration/templates/admin/hostinfo.html:42 msgid "Django Version" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:46 +#: mygpo/administration/templates/admin/hostinfo.html:47 msgid "Feed Update Queue" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:49 +#: mygpo/administration/templates/admin/hostinfo.html:50 msgid "min ahead" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:51 +#: mygpo/administration/templates/admin/hostinfo.html:52 msgid "min behind" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:59 +#: mygpo/administration/templates/admin/hostinfo.html:60 msgid "Number of podcasts with outdated search index" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:66 +#: mygpo/administration/templates/admin/hostinfo.html:69 +msgid "Average podcast update duration" +msgstr "" + +#: mygpo/administration/templates/admin/hostinfo.html:76 msgid "Scheduled Celery Tasks" msgstr "" @@ -153,7 +157,7 @@ msgstr "" #: mygpo/administration/templates/admin/make-publisher-input.html:12 #: mygpo/administration/templates/admin/make-publisher-result.html:9 #: mygpo/administration/templates/admin/make-publisher-result.html:12 -#: mygpo/administration/views.py:391 +#: mygpo/administration/views.py:404 #, fuzzy #| msgid "Publisher Pages" msgid "Publisher Permissions" @@ -274,21 +278,21 @@ msgstr "" msgid "User-Agent" msgstr "" -#: mygpo/administration/views.py:140 mygpo/administration/views.py:162 +#: mygpo/administration/views.py:153 mygpo/administration/views.py:175 #, python-brace-format msgid "No podcast with URL {url}" msgstr "" -#: mygpo/administration/views.py:316 +#: mygpo/administration/views.py:329 msgid "Provide either username or email address" msgstr "" -#: mygpo/administration/views.py:322 +#: mygpo/administration/views.py:335 #, fuzzy msgid "No user found" msgstr "Introuvable" -#: mygpo/administration/views.py:327 +#: mygpo/administration/views.py:340 #, python-brace-format msgid "User {username} ({email}) activated" msgstr "" @@ -326,16 +330,16 @@ msgstr "désabonné" msgid "%(username)s's Subscription List" msgstr "Historique des abonnements" -#: mygpo/api/simple.py:237 +#: mygpo/api/simple.py:240 #, python-format msgid "gpodder.net - Top %(count)d" msgstr "gpodder.net - Top %(count)d" -#: mygpo/api/simple.py:272 +#: mygpo/api/simple.py:275 msgid "gpodder.net - Search" msgstr "gpodder.net - Recherche" -#: mygpo/api/simple.py:289 +#: mygpo/api/simple.py:292 #, python-format msgid "gpodder.net - %(count)d Suggestions" msgstr "gpodder.net - %(count)d Suggestions" @@ -358,7 +362,7 @@ msgid "Explore" msgstr "" #: mygpo/directory/templates/carousel.html:81 -#: mygpo/web/templates/episode.html:150 +#: mygpo/podcasts/templates/episode.html:150 #, fuzzy msgid "..." msgstr "voir plus..." @@ -490,11 +494,11 @@ msgid "Listeners" msgstr "Auditeurs" #: mygpo/directory/templates/episode_toplist.html:36 +#: mygpo/history/templates/episode-history.html:51 +#: mygpo/podcasts/templates/episode.html:51 #: mygpo/publisher/templates/publisher/episode.html:33 #: mygpo/share/templates/userpage.html:118 #: mygpo/share/templates/userpage.html:138 -#: mygpo/web/templates/episode-history.html:50 -#: mygpo/web/templates/episode.html:51 msgid "from" msgstr "de" @@ -569,9 +573,9 @@ msgid "User" msgstr "Nom d'utilisateur" #: mygpo/directory/templates/podcast_lists.html:25 +#: mygpo/history/templates/episode-history.html:53 +#: mygpo/podcasts/templates/episode.html:53 #: mygpo/publisher/templates/publisher/episode.html:35 -#: mygpo/web/templates/episode-history.html:52 -#: mygpo/web/templates/episode.html:53 msgid "Download" msgstr "Télécharger" @@ -630,10 +634,49 @@ msgstr "" msgid "%d podcasts added" msgstr "podcasts" +#: mygpo/history/templates/episode-history.html:47 +#: mygpo/history/templates/episode-history.html:81 +#: mygpo/web/templates/device.html:73 mygpo/web/templatetags/menu.py:51 +msgid "History" +msgstr "Histoire" + +#: mygpo/history/templates/episode-history.html:58 +#: mygpo/podcasts/templates/episode.html:58 +#: mygpo/podcasts/templates/podcast-base.html:54 +#: mygpo/publisher/templates/publisher/episode.html:40 +#: mygpo/publisher/templates/publisher/podcast.html:39 +#, fuzzy +msgid "Website" +msgstr "site web" + +#: mygpo/history/templates/episode-history.html:63 +#: mygpo/podcasts/templates/episode.html:63 +#: mygpo/publisher/templates/publisher/episode.html:45 +#, fuzzy +msgid "listeners" +msgstr "Auditeurs" + +#: mygpo/history/templates/episode-history.html:84 +msgid "Time" +msgstr "Temps" + +#: mygpo/history/templates/episode-history.html:85 +msgid "Action" +msgstr "Action" + +#: mygpo/history/templates/episode-history.html:86 +#: mygpo/web/templatetags/menu.py:50 +msgid "Device" +msgstr "Périphérique" + +#: mygpo/history/templates/episode-history.html:127 +msgid "Add" +msgstr "Ajouter" + #: mygpo/history/templates/history.html:12 #: mygpo/history/templates/history.html:15 -#: mygpo/web/templates/podcast-history.html:32 -#: mygpo/web/templates/podcast.html:143 +#: mygpo/history/templates/podcast-history.html:32 +#: mygpo/podcasts/templates/podcast.html:143 #, fuzzy msgid "Subscription History" msgstr "Historique des abonnements" @@ -668,6 +711,10 @@ msgstr "Non" msgid "Earlier" msgstr "" +#: mygpo/history/templates/podcast-history.html:47 +msgid "no history yet" +msgstr "" + #: mygpo/podcastlists/templates/list.html:28 #, python-format msgid "\"%(list_title)s\" by %(ownername)s" @@ -763,17 +810,103 @@ msgstr "" msgid "Edit" msgstr "Éditer" -#: mygpo/podcasts/models.py:688 +#: mygpo/podcasts/models.py:704 #, fuzzy #| msgid "Unnamed Podcast" msgid "Unknown Podcast" msgstr "Podcast anonyme" -#: mygpo/podcasts/models.py:690 +#: mygpo/podcasts/models.py:706 #, python-brace-format msgid "Unknown Podcast from {domain}" msgstr "" +#: mygpo/podcasts/templates/episode.html:85 +#: mygpo/podcasts/templates/episodes.html:27 +#: mygpo/podcasts/templates/podcast.html:39 +#: mygpo/publisher/templates/publisher/episode.html:31 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/info.html:9 +#: mygpo/publisher/templates/publisher/info.html:26 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Publisher Pages" +msgstr "Pages pour l'éditeur" + +#: mygpo/podcasts/templates/episode.html:91 +#, fuzzy +msgid "Remove Favorite" +msgstr "Favoris" + +#: mygpo/podcasts/templates/episode.html:95 +msgid "Favorite" +msgstr "Favoris" + +#: mygpo/podcasts/templates/episode.html:103 +#, fuzzy +msgid "Episode History" +msgstr "Top des épisodes" + +#: mygpo/podcasts/templates/podcast-base.html:39 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Unnamed Podcast" +msgstr "Podcast anonyme" + +#: mygpo/podcasts/templates/podcast-base.html:44 +#: mygpo/publisher/templates/publisher/podcast.html:29 +msgid "by" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:50 +#: mygpo/publisher/templates/publisher/podcast.html:35 +#: mygpo/share/templates/share/favorites.html:29 +msgid "Feed" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:65 +#: mygpo/publisher/templates/publisher/podcast.html:44 +#, fuzzy +msgid "subscribers" +msgstr "Abonnés" + +#: mygpo/podcasts/templates/podcast.html:48 +#, fuzzy +#| msgid "Subscribe" +msgid "Login to Subscribe" +msgstr "S'abonner" + +#: mygpo/podcasts/templates/podcast.html:59 +#: mygpo/suggestions/templates/suggestions.html:31 +#: mygpo/web/templates/subscribe.html:11 mygpo/web/templates/subscribe.html:36 +msgid "Subscribe" +msgstr "S'abonner" + +#: mygpo/podcasts/templates/podcast.html:67 mygpo/web/templates/device.html:31 +msgid "Unsubscribe" +msgstr "Se désabonner" + +#: mygpo/podcasts/templates/podcast.html:78 +#, fuzzy +msgid "Subscribe on all devices" +msgstr "S'abonner à ce podcast" + +#: mygpo/podcasts/templates/podcast.html:110 +#, fuzzy +msgid "Unsubscribe from all devices " +msgstr "S'abonner à ce podcast" + +#: mygpo/podcasts/templates/podcast.html:149 +#: mygpo/podcasts/templates/podcast.html:164 +#, fuzzy +#| msgid "My Tags" +msgid "Tags" +msgstr "Mes Tags" + +#: mygpo/podcasts/templates/podcast.html:199 +#, fuzzy +msgid "Older Episodes" +msgstr "Épisodes" + #: mygpo/publisher/forms.py:5 #: mygpo/publisher/templates/publisher/episode.html:60 msgid "URL" @@ -788,7 +921,7 @@ msgid "Link to" msgstr "Lien vers" #: mygpo/publisher/templates/link.html:17 -#: mygpo/publisher/templates/publisher/podcast.html:171 +#: mygpo/publisher/templates/publisher/podcast.html:209 #, fuzzy, python-format msgid "" "You can paste this code on your website, so users of %(sitename)s can " @@ -900,31 +1033,6 @@ msgstr "" msgid "Unnamed Episode" msgstr "Épisode anonyme" -#: mygpo/publisher/templates/publisher/episode.html:31 -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/info.html:9 -#: mygpo/publisher/templates/publisher/info.html:26 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/episode.html:85 mygpo/web/templates/episodes.html:27 -#: mygpo/web/templates/podcast.html:39 -msgid "Publisher Pages" -msgstr "Pages pour l'éditeur" - -#: mygpo/publisher/templates/publisher/episode.html:40 -#: mygpo/publisher/templates/publisher/podcast.html:38 -#: mygpo/web/templates/episode-history.html:57 -#: mygpo/web/templates/episode.html:58 mygpo/web/templates/podcast-base.html:54 -#, fuzzy -msgid "Website" -msgstr "site web" - -#: mygpo/publisher/templates/publisher/episode.html:45 -#: mygpo/web/templates/episode-history.html:62 -#: mygpo/web/templates/episode.html:63 -#, fuzzy -msgid "listeners" -msgstr "Auditeurs" - #: mygpo/publisher/templates/publisher/episode.html:55 #, fuzzy msgid "Episode List" @@ -942,7 +1050,7 @@ msgid "" msgstr "" #: mygpo/publisher/templates/publisher/episode.html:72 -#: mygpo/publisher/templates/publisher/podcast.html:91 +#: mygpo/publisher/templates/publisher/podcast.html:129 #: mygpo/web/templates/account.html:65 mygpo/web/templates/account.html:142 #: mygpo/web/templates/device-edit.html:53 msgid "Save" @@ -957,12 +1065,6 @@ msgstr "Statuts de l'épisode" msgid "Last update: " msgstr "" -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/podcast-base.html:39 -msgid "Unnamed Podcast" -msgstr "Podcast anonyme" - #: mygpo/publisher/templates/publisher/episodes.html:24 msgid "Return to Podcast Page" msgstr "Retourner à la page des podcasts" @@ -1126,77 +1228,59 @@ msgstr "" "depuis votre site web, vos visiteurs pourront facilement s'abonner à votre " "podcast - spécialement les utilisateurs de téléphones portables." -#: mygpo/publisher/templates/publisher/podcast.html:28 -#: mygpo/web/templates/podcast-base.html:44 -msgid "by" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:34 -#: mygpo/share/templates/share/favorites.html:29 -#: mygpo/web/templates/podcast-base.html:50 -msgid "Feed" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:43 -#: mygpo/web/templates/podcast-base.html:65 -#, fuzzy -msgid "subscribers" -msgstr "Abonnés" - -#: mygpo/publisher/templates/publisher/podcast.html:48 +#: mygpo/publisher/templates/publisher/podcast.html:49 #, python-format msgid "" "This is the publisher page of %(ptitle)s. You can see some " "stats and provide additional data for the podcast page." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:49 +#: mygpo/publisher/templates/publisher/podcast.html:50 #, fuzzy msgid "Go to Podcast Page" msgstr "Retourner à la page des podcasts" -#: mygpo/publisher/templates/publisher/podcast.html:56 +#: mygpo/publisher/templates/publisher/podcast.html:57 msgid "The podcast information is regularly retrieved from the podcast feed" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:59 -msgid "Timing" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:61 +#: mygpo/publisher/templates/publisher/podcast.html:60 #, fuzzy -msgid "Last update:" -msgstr "ID du périphérique" +msgid "Updates" +msgstr "Mettre à jour le flux" #: mygpo/publisher/templates/publisher/podcast.html:62 #, fuzzy msgid "Update interval:" msgstr "Mettre à jour le flux" -#: mygpo/publisher/templates/publisher/podcast.html:63 -#, fuzzy -msgid "Next update:" -msgstr "ID du périphérique" +#: mygpo/publisher/templates/publisher/podcast.html:86 +msgid "Successful" +msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:68 +#: mygpo/publisher/templates/publisher/podcast.html:88 +msgid "Error" +msgstr "" + +#: mygpo/publisher/templates/publisher/podcast.html:106 #, fuzzy msgid "Update now" msgstr "Mettre à jour le flux" -#: mygpo/publisher/templates/publisher/podcast.html:79 mygpo/web/forms.py:70 +#: mygpo/publisher/templates/publisher/podcast.html:117 mygpo/web/forms.py:70 #: mygpo/web/templates/base.html:160 mygpo/web/templates/home.html:180 msgid "Twitter" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:97 +#: mygpo/publisher/templates/publisher/podcast.html:135 msgid "Feed Check" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:105 +#: mygpo/publisher/templates/publisher/podcast.html:143 msgid "PubSubHubbub" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:114 +#: mygpo/publisher/templates/publisher/podcast.html:152 #, python-format msgid "" "If you publish your podcast feed through a %(hub)s and should " "update immediatelly for each new episode." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:123 +#: mygpo/publisher/templates/publisher/podcast.html:161 #, python-format msgid "" "Your podcast is published through %(hub)s but our " "subscription has not yet been verified." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:128 +#: mygpo/publisher/templates/publisher/podcast.html:166 #, python-format msgid "" "We did not find a hub in your podcast feed. Your feed is updated regularly, " "but there might be some delay until a new episode shows up on %(sitename)s." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:140 +#: mygpo/publisher/templates/publisher/podcast.html:178 msgid "License Information" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:148 +#: mygpo/publisher/templates/publisher/podcast.html:186 #, python-format msgid "" "You should include license information in your feed so that users and " "%(sitename)s can know, under which conditions your content can be used." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:152 +#: mygpo/publisher/templates/publisher/podcast.html:190 #, python-format msgid "" "We found the following license in your podcast: " "%(license)s" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:156 +#: mygpo/publisher/templates/publisher/podcast.html:194 msgid "" "We did not find a license in your podcast feed. Refer to paramètres de votre compte " "pour obtenir le lien." -#: mygpo/web/templatetags/devices.py:31 +#: mygpo/web/templatetags/devices.py:32 msgid "Unknown" msgstr "Inconnu" @@ -2732,11 +2748,16 @@ msgstr "Vie privée" msgid "Link to gpodder.net" msgstr "Lien vers gpodder.net" -#: mygpo/web/templatetags/time.py:38 +#: mygpo/web/templatetags/time.py:42 #, python-brace-format msgid "{h}h {m}m {s}s" msgstr "" +#: mygpo/web/templatetags/time.py:44 +#, python-brace-format +msgid "{m}m {s}s" +msgstr "" + #: mygpo/web/utils.py:281 #, python-format msgid "%(weeks)d week" @@ -2762,6 +2783,14 @@ msgstr[1] "" msgid "another site" msgstr "un autre site" +#, fuzzy +#~ msgid "Last update:" +#~ msgstr "ID du périphérique" + +#, fuzzy +#~ msgid "Next update:" +#~ msgstr "ID du périphérique" + #, fuzzy #~ msgid "You have to login to Flattr to use the Flattr features." #~ msgstr "Vous devez vous identifier pour accéder à cette page" diff --git a/mygpo/locale/he/LC_MESSAGES/django.po b/mygpo/locale/he/LC_MESSAGES/django.po new file mode 100644 index 000000000..c53fffd23 --- /dev/null +++ b/mygpo/locale/he/LC_MESSAGES/django.po @@ -0,0 +1,2728 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-06-29 20:11+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Yaron Shahrabani , 2018\n" +"Language-Team: Hebrew (https://www.transifex.com/yaron/teams/87581/he/)\n" +"Language: he\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % " +"1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;\n" + +#: mygpo/administration/templates/admin/activate-user.html:9 +#: mygpo/administration/templates/admin/activate-user.html:12 +#: mygpo/administration/templates/admin/overview.html:22 +msgid "Activate User" +msgstr "הפעלת משתמש" + +#: mygpo/administration/templates/admin/activate-user.html:20 +#: mygpo/web/forms.py:179 mygpo/web/templates/account.html:112 +msgid "Username" +msgstr "שם משתמש" + +#: mygpo/administration/templates/admin/activate-user.html:27 +msgid "Email" +msgstr "כתובת" + +#: mygpo/administration/templates/admin/activate-user.html:35 +msgid "Activate" +msgstr "הפעלה" + +#: mygpo/administration/templates/admin/clients.html:10 +#: mygpo/administration/templates/admin/make-publisher-result.html:16 +#: mygpo/administration/templates/admin/merge-grouping.html:10 +#: mygpo/administration/templates/admin/merge-select.html:9 +#: mygpo/administration/templates/admin/overview.html:9 +#: mygpo/administration/templates/admin/overview.html:12 +#: mygpo/administration/templates/admin/stats.html:9 +#: mygpo/administration/templates/admin/useragents.html:9 +msgid "Admin Area" +msgstr "אזור ניהול" + +#: mygpo/administration/templates/admin/clients.html:13 +#: mygpo/administration/templates/admin/useragents.html:12 +msgid "User-Agent Statistics" +msgstr "סטטיסטיקת דפדפנים" + +#: mygpo/administration/templates/admin/clients.html:19 +#: mygpo/administration/templates/admin/filetypes.html:19 +#: mygpo/administration/templates/admin/useragents.html:18 +msgid "#" +msgstr "מס׳" + +#: mygpo/administration/templates/admin/clients.html:20 +msgid "Client" +msgstr "לקוח" + +#: mygpo/administration/templates/admin/clients.html:21 +#: mygpo/administration/templates/admin/clients.html:23 +#: mygpo/administration/templates/admin/clients.html:25 +msgid "Version" +msgstr "גרסה" + +#: mygpo/administration/templates/admin/clients.html:22 +msgid "Library" +msgstr "ספריה" + +#: mygpo/administration/templates/admin/clients.html:24 +msgid "OS" +msgstr "מערכת הפעלה" + +#: mygpo/administration/templates/admin/clients.html:26 +#: mygpo/administration/templates/admin/useragents.html:20 +msgid "Number of Clients" +msgstr "מספר לקוחות" + +#: mygpo/administration/templates/admin/clients.html:53 +#: mygpo/administration/templates/admin/useragents.html:33 +msgid "Total" +msgstr "סך הכול" + +#: mygpo/administration/templates/admin/filetypes.html:10 +#: mygpo/administration/templates/admin/filetypes.html:13 +msgid "File Type Stats" +msgstr "סטטיסטיקת סוגי קבצים" + +#: mygpo/administration/templates/admin/filetypes.html:20 +msgid "File Type" +msgstr "סוג קובץ" + +#: mygpo/administration/templates/admin/filetypes.html:21 +msgid "Episode URLs" +msgstr "כתובות פרקים" + +#: mygpo/administration/templates/admin/hostinfo.html:10 +#: mygpo/administration/templates/admin/hostinfo.html:13 +#: mygpo/administration/templates/admin/overview.html:17 +msgid "Host Information" +msgstr "פרטי מארח" + +#: mygpo/administration/templates/admin/hostinfo.html:21 +msgid "mygpo Version" +msgstr "גרסת mygpo" + +#: mygpo/administration/templates/admin/hostinfo.html:32 +msgid "Base Directory" +msgstr "תיקיית בסיס" + +#: mygpo/administration/templates/admin/hostinfo.html:37 +msgid "Hostname" +msgstr "שם מארח" + +#: mygpo/administration/templates/admin/hostinfo.html:42 +msgid "Django Version" +msgstr "גרסת Django" + +#: mygpo/administration/templates/admin/hostinfo.html:47 +msgid "Feed Update Queue" +msgstr "תור עדכון הזנות" + +#: mygpo/administration/templates/admin/hostinfo.html:50 +msgid "min ahead" +msgstr "דקות הקדמה" + +#: mygpo/administration/templates/admin/hostinfo.html:52 +msgid "min behind" +msgstr "דקות איחור" + +#: mygpo/administration/templates/admin/hostinfo.html:60 +msgid "Number of podcasts with outdated search index" +msgstr "מספר הפודקאסטים עם אינדקס חיפוש בלתי עדכני" + +#: mygpo/administration/templates/admin/hostinfo.html:69 +msgid "Average podcast update duration" +msgstr "" + +#: mygpo/administration/templates/admin/hostinfo.html:76 +msgid "Scheduled Celery Tasks" +msgstr "משימות מתוזמנות ב־Celery" + +#: mygpo/administration/templates/admin/make-publisher-input.html:9 +#: mygpo/administration/templates/admin/make-publisher-input.html:12 +#: mygpo/administration/templates/admin/make-publisher-result.html:9 +#: mygpo/administration/templates/admin/make-publisher-result.html:12 +#: mygpo/administration/views.py:404 +msgid "Publisher Permissions" +msgstr "הרשאות הפצה" + +#: mygpo/administration/templates/admin/make-publisher-mail.txt:2 +#, python-format +msgid "Hi %(username)s," +msgstr "שלום %(username)s," + +#: mygpo/administration/templates/admin/make-publisher-mail.txt:4 +msgid "You have been assigned as a publisher for the following podcasts:" +msgstr "הוקצית להפצה של הפודקאסטים הבאים:" + +#: mygpo/administration/templates/admin/make-publisher-mail.txt:11 +msgid "You can now access the publisher pages at" +msgstr "כעת באפשרותך לגשת לעמודי ההפצה תחת" + +#: mygpo/administration/templates/admin/make-publisher-mail.txt:15 +#, python-format +msgid "" +"There you can get statistics, insights and additional control over your " +"podcast at %(domain)s." +msgstr "" +"שם ניתן לקבל סטטיסטיקה, תובנות ושליטה נוספת על הפודקאסט שלך ב־%(domain)s." + +#: mygpo/administration/templates/admin/make-publisher-mail.txt:17 +#, python-format +msgid "If you have any questions, please visit %(url)s" +msgstr "אם יש לך שאלות, ניתן לבקר ב־%(url)s" + +#: mygpo/administration/templates/admin/merge-grouping.html:13 +#: mygpo/administration/templates/admin/merge-select.html:12 +#: mygpo/administration/templates/admin/overview.html:18 +msgid "Merge Podcasts and Episodes" +msgstr "מיזוג פודקאסטים ופרקים" + +#: mygpo/administration/templates/admin/merge-grouping.html:24 +msgid "" +"Episodes that have the same number will be merged. Please verify all your " +"changes by clicking on 'Renew Groups' before starting the Merge." +msgstr "" +"פרקים שיש להם את אותו המספר ימוזגו. נא לאמת את כל השינויים שלך על ידי לחיצה " +"על ‚חידוש קבוצות’ בטרם התחלת המיזוג." + +#: mygpo/administration/templates/admin/overview.html:19 +msgid "Client Stats" +msgstr "סטטיסטיקת לקוח" + +#: mygpo/administration/templates/admin/overview.html:19 +#: mygpo/administration/templates/admin/overview.html:21 +msgid "JSON" +msgstr "JSON" + +#: mygpo/administration/templates/admin/overview.html:20 +msgid "User-Agent Stats" +msgstr "סטטיסטיקת דפדפן" + +#: mygpo/administration/templates/admin/overview.html:21 +msgid "General Stats" +msgstr "סטטיסטיקה כללית" + +#: mygpo/administration/templates/admin/overview.html:23 +msgid "Assign Publisher Permissions" +msgstr "הקצאת הרשאות הפצה" + +#: mygpo/administration/templates/admin/stats.html:12 +msgid "Statistics" +msgstr "סטטיסטיקה" + +#: mygpo/administration/templates/admin/stats.html:18 +msgid "Property" +msgstr "מאפיין" + +#: mygpo/administration/templates/admin/stats.html:19 +msgid "Value" +msgstr "ערך" + +#: mygpo/administration/templates/admin/task-status.html:17 +#: mygpo/administration/templates/admin/task-status.html:26 +msgid "Operation Finished" +msgstr "הפעולה הסתיימה" + +#: mygpo/administration/templates/admin/task-status.html:19 +#: mygpo/administration/templates/admin/task-status.html:28 +msgid "Operation Ongoing" +msgstr "הפעולה מתבצעת" + +#: mygpo/administration/templates/admin/task-status.html:37 +msgid "The following actions were recorded:" +msgstr "הפעולות הבאות הוקלטו:" + +#: mygpo/administration/templates/admin/task-status.html:42 +#: mygpo/publisher/templates/publisher/episode.html:80 +msgid "none" +msgstr "אין" + +#: mygpo/administration/templates/admin/task-status.html:47 +msgid "Go to podcast" +msgstr "מעבר לפודקאסט" + +#: mygpo/administration/templates/admin/task-status.html:49 +#: mygpo/directory/templates/directory/add-podcast-status.html:50 +msgid "The operation is still ongoing..." +msgstr "הפעולה עדיין מתבצעת…" + +#: mygpo/administration/templates/admin/task-status.html:51 +#: mygpo/directory/templates/directory/add-podcast-status.html:52 +msgid "Refresh" +msgstr "רענון" + +#: mygpo/administration/templates/admin/useragents.html:19 +msgid "User-Agent" +msgstr "סוכן דפדפן" + +#: mygpo/administration/views.py:153 mygpo/administration/views.py:175 +#, python-brace-format +msgid "No podcast with URL {url}" +msgstr "אין פודקאסט בכתובת {url}" + +#: mygpo/administration/views.py:329 +msgid "Provide either username or email address" +msgstr "יש לספק שם משתמש או כתובת דוא״ל" + +#: mygpo/administration/views.py:335 +msgid "No user found" +msgstr "לא נמצאו משתמשים" + +#: mygpo/administration/views.py:340 +#, python-brace-format +msgid "User {username} ({email}) activated" +msgstr "המשתמש {username} ‎({email}) עבר עימות" + +#: mygpo/api/constants.py:8 +msgid "downloaded" +msgstr "התקבל" + +#: mygpo/api/constants.py:9 +msgid "played" +msgstr "התנגן" + +#: mygpo/api/constants.py:10 +msgid "deleted" +msgstr "נמחק" + +#: mygpo/api/constants.py:11 +msgid "marked as new" +msgstr "סומן כחדש" + +#: mygpo/api/constants.py:12 +msgid "flattr'd" +msgstr "קיבל flattr" + +#: mygpo/api/constants.py:20 +msgid "subscribed" +msgstr "נרשם" + +#: mygpo/api/constants.py:21 +msgid "unsubscribed" +msgstr "בוטל רישום" + +#: mygpo/api/simple.py:55 mygpo/api/simple.py:90 +#, python-format +msgid "%(username)s's Subscription List" +msgstr "רשימת המינויים של %(username)s" + +#: mygpo/api/simple.py:240 +#, python-format +msgid "gpodder.net - Top %(count)d" +msgstr "gpodder.net - %(count)d המובילים" + +#: mygpo/api/simple.py:275 +msgid "gpodder.net - Search" +msgstr "gpodder.net - חיפוש" + +#: mygpo/api/simple.py:292 +#, python-format +msgid "gpodder.net - %(count)d Suggestions" +msgstr "gpodder.net - %(count)d הצעות" + +#: mygpo/data/mimetype.py:13 +msgid "image" +msgstr "תמונה" + +#: mygpo/data/mimetype.py:13 +msgid "audio" +msgstr "קטע שמיעה" + +#: mygpo/data/mimetype.py:13 +msgid "video" +msgstr "סרטון" + +#: mygpo/directory/templates/carousel.html:43 +#: mygpo/directory/templates/carousel.html:46 +msgid "Explore" +msgstr "עיון" + +#: mygpo/directory/templates/carousel.html:81 +#: mygpo/podcasts/templates/episode.html:150 +msgid "..." +msgstr "…" + +#: mygpo/directory/templates/directory.html:13 +#: mygpo/directory/templates/directory.html:16 +msgid "Podcast Directory" +msgstr "תיקיית פודקאסטים" + +#: mygpo/directory/templates/directory.html:30 +#, python-format +msgid "" +"%(listtitle)s by " +"%(username)s" +msgstr "" +"%(listtitle)s מאת " +"%(username)s" + +#: mygpo/directory/templates/directory.html:46 +#: mygpo/directory/templates/directory.html:48 +#: mygpo/directory/templates/directory.html:133 +#: mygpo/directory/templates/podcast-ad-box.html:23 +#: mygpo/web/templates/dashboard.html:73 +msgid "more..." +msgstr "עוד…" + +#: mygpo/directory/templates/directory.html:85 +msgid "Podcast List by" +msgstr "רשימת פודקאסטים מאת" + +#: mygpo/directory/templates/directory.html:102 +#, python-format +msgid "and %(more)s more" +msgstr "ו־%(more)s נוספים" + +#: mygpo/directory/templates/directory.html:108 +msgid "Create a Podcast List" +msgstr "יצירת רשימת פודקאסטים" + +#: mygpo/directory/templates/directory.html:119 +#: mygpo/web/templates/dashboard.html:59 +msgid "Random" +msgstr "אקראי" + +#: mygpo/directory/templates/directory/add-podcast-status.html:16 +#: mygpo/directory/templates/directory/add-podcast-status.html:25 +msgid "Podcast Added" +msgstr "נוסף פודקאסט" + +#: mygpo/directory/templates/directory/add-podcast-status.html:18 +#: mygpo/directory/templates/directory/add-podcast-status.html:27 +msgid "Adding Podcast" +msgstr "פודקאסט מתווסף" + +#: mygpo/directory/templates/directory/add-podcast-status.html:44 +msgid "No podcasts could be added" +msgstr "לא ניתן להוסיף פודקאסטים" + +#: mygpo/directory/templates/directory/license-podcasts.html:13 +#: mygpo/directory/templates/directory/license-podcasts.html:16 +#, python-format +msgid "Podcasts with License %(licensename)s" +msgstr "פודקאסט עם הרישיון %(licensename)s" + +#: mygpo/directory/templates/directory/license-podcasts.html:24 +msgid "License overview" +msgstr "סקירת רישיון" + +#: mygpo/directory/templates/directory/license-podcasts.html:29 +msgid "View License" +msgstr "הצגת הרישיון" + +#: mygpo/directory/templates/directory/license-podcasts.html:36 +#: mygpo/directory/templates/search.html:35 +#: mygpo/directory/templates/toplist.html:26 +#: mygpo/podcastlists/templates/list.html:48 mygpo/web/templatetags/menu.py:33 +#: mygpo/web/templatetags/menu.py:68 +msgid "Podcast" +msgstr "פודקאסט" + +#: mygpo/directory/templates/directory/license-podcasts.html:37 +#: mygpo/directory/templates/search.html:36 +#: mygpo/directory/templates/toplist.html:27 +#: mygpo/podcastlists/templates/list.html:49 +msgid "Subscribers" +msgstr "מנויים" + +#: mygpo/directory/templates/directory/license-podcasts.html:50 +#: mygpo/directory/templates/episode_toplist.html:41 +#: mygpo/directory/templates/toplist.html:47 +msgid "Currently not available" +msgstr "לא זמין כרגע" + +#: mygpo/directory/templates/directory/licenses.html:13 +#: mygpo/directory/templates/directory/licenses.html:16 +msgid "Podcasts with License Information" +msgstr "פודקאסטים עם פרטי רישיון" + +#: mygpo/directory/templates/directory/licenses.html:23 +#: mygpo/web/templatetags/menu.py:40 +msgid "License" +msgstr "רישיון" + +#: mygpo/directory/templates/directory/licenses.html:24 +#: mygpo/directory/templates/podcast_lists.html:24 +#: mygpo/web/templates/base.html:135 mygpo/web/templates/home.html:155 +#: mygpo/web/templatetags/menu.py:42 +msgid "Podcasts" +msgstr "פודקאסטים" + +#: mygpo/directory/templates/episode_toplist.html:12 +#: mygpo/directory/templates/episode_toplist.html:15 +msgid "Episode-Toplist" +msgstr "רשימת הפרקים המובילים" + +#: mygpo/directory/templates/episode_toplist.html:24 +msgid "Episode" +msgstr "פרק" + +#: mygpo/directory/templates/episode_toplist.html:25 +#: mygpo/publisher/templates/publisher/episodes.html:36 +msgid "Listeners" +msgstr "מאזינים" + +#: mygpo/directory/templates/episode_toplist.html:36 +#: mygpo/history/templates/episode-history.html:51 +#: mygpo/podcasts/templates/episode.html:51 +#: mygpo/publisher/templates/publisher/episode.html:33 +#: mygpo/share/templates/userpage.html:118 +#: mygpo/share/templates/userpage.html:138 +msgid "from" +msgstr "מאת" + +#: mygpo/directory/templates/episode_toplist.html:53 +#: mygpo/directory/templates/episode_toplist.html:57 +#: mygpo/directory/templates/toplist.html:60 +#: mygpo/directory/templates/toplist.html:64 +msgid "Language" +msgstr "שפה" + +#: mygpo/directory/templates/episode_toplist.html:61 +#: mygpo/directory/templates/episode_toplist.html:63 +#: mygpo/directory/templates/toplist.html:68 +#: mygpo/directory/templates/toplist.html:70 +msgid "all" +msgstr "הכול" + +#: mygpo/directory/templates/episode_toplist.html:75 +#: mygpo/directory/templates/toplist.html:83 +msgid "OK" +msgstr "אישור" + +#: mygpo/directory/templates/missing.html:11 +#: mygpo/directory/templates/missing.html:14 mygpo/web/templatetags/menu.py:35 +msgid "Missing Podcast" +msgstr "פודקאסט חסר" + +#: mygpo/directory/templates/missing.html:20 +#, python-format +msgid "" +"If you're missing a podcast on %(sitename)s, please enter its feed URL here" +msgstr "אם חסר לך פודקאסט ב־%(sitename)s, נא להקליד את כתובת ההזנה שלו להלן" + +#: mygpo/directory/templates/missing.html:27 +msgid "Check" +msgstr "בדיקה" + +#: mygpo/directory/templates/missing.html:36 +msgid "The podcast already exists" +msgstr "הפודקאסט כבר קיים" + +#: mygpo/directory/templates/missing.html:49 +msgid "Add Podcast" +msgstr "הוספת פודקאסט" + +#: mygpo/directory/templates/podcast-ad-box.html:9 +msgid "Sponsoring Podcast" +msgstr "מימון פודקאסט" + +#: mygpo/directory/templates/podcast_lists.html:13 +#: mygpo/directory/templates/podcast_lists.html:16 +#: mygpo/podcastlists/templates/lists.html:13 +#: mygpo/podcastlists/templates/lists.html:16 +#: mygpo/podcastlists/templates/lists_user.html:9 +#: mygpo/web/templatetags/menu.py:36 mygpo/web/templatetags/menu.py:58 +msgid "Podcast Lists" +msgstr "רשימות פודקאסטים" + +#: mygpo/directory/templates/podcast_lists.html:22 +msgid "List Name" +msgstr "שם רשימה" + +#: mygpo/directory/templates/podcast_lists.html:23 +msgid "User" +msgstr "משתמש" + +#: mygpo/directory/templates/podcast_lists.html:25 +#: mygpo/history/templates/episode-history.html:53 +#: mygpo/podcasts/templates/episode.html:53 +#: mygpo/publisher/templates/publisher/episode.html:35 +msgid "Download" +msgstr "הורדה" + +#: mygpo/directory/templates/podcast_lists.html:76 +#: mygpo/podcastlists/templates/lists_user.html:37 +msgid "Know Some Great Podcasts?" +msgstr "ידוע לך על פודקאסטים מעולים?" + +#: mygpo/directory/templates/podcast_lists.html:77 +#: mygpo/podcastlists/templates/lists_user.html:38 +msgid "Create a list of podcasts about a topic you like" +msgstr "יצירת רשימת פודקאסטים על נושא מועדף עליך" + +#: mygpo/directory/templates/search.html:11 +#: mygpo/directory/templates/search.html:14 +#: mygpo/directory/templates/search.html:25 +#: mygpo/podcastlists/templates/list.html:104 +#: mygpo/subscriptions/templates/subscriptions.html:53 +#: mygpo/web/templates/base.html:136 mygpo/web/templates/home.html:156 +#: mygpo/web/templatetags/menu.py:34 +msgid "Search" +msgstr "חיפוש" + +#: mygpo/directory/templates/search.html:23 +#: mygpo/podcastlists/templates/list.html:102 +#: mygpo/subscriptions/templates/subscriptions.html:51 +msgid "Search term or feed URL" +msgstr "ביטוי לחיפוש או כתובת הזנה" + +#: mygpo/directory/templates/search.html:79 +#: mygpo/podcastlists/templates/list_search.html:65 +msgid "Nothing found" +msgstr "לא נמצא שום דבר" + +#: mygpo/directory/templates/search.html:91 +msgid "Search API" +msgstr "API לחיפוש" + +#: mygpo/directory/templates/search.html:92 +msgid "Access the following address to search for tech podcasts." +msgstr "ניתן לגשת לכתובת הבאה לחיפוש אחר פודקאסטים בנושא טכנולוגיה." + +#: mygpo/directory/templates/toplist.html:14 +#: mygpo/directory/templates/toplist.html:17 +msgid "Toplist" +msgstr "רשימות מובילות" + +#: mygpo/directory/templates/toplist.html:88 +msgid "Client Access" +msgstr "גישת לקוח" + +#: mygpo/directory/views.py:321 +#, python-format +msgid "%d podcasts added" +msgstr "נוספו %d פודקאסטים" + +#: mygpo/history/templates/episode-history.html:47 +#: mygpo/history/templates/episode-history.html:81 +#: mygpo/web/templates/device.html:73 mygpo/web/templatetags/menu.py:51 +msgid "History" +msgstr "היסטוריה" + +#: mygpo/history/templates/episode-history.html:58 +#: mygpo/podcasts/templates/episode.html:58 +#: mygpo/podcasts/templates/podcast-base.html:54 +#: mygpo/publisher/templates/publisher/episode.html:40 +#: mygpo/publisher/templates/publisher/podcast.html:39 +msgid "Website" +msgstr "אתר" + +#: mygpo/history/templates/episode-history.html:63 +#: mygpo/podcasts/templates/episode.html:63 +#: mygpo/publisher/templates/publisher/episode.html:45 +msgid "listeners" +msgstr "מאזינים" + +#: mygpo/history/templates/episode-history.html:84 +msgid "Time" +msgstr "זמן" + +#: mygpo/history/templates/episode-history.html:85 +msgid "Action" +msgstr "פעולה" + +#: mygpo/history/templates/episode-history.html:86 +#: mygpo/web/templatetags/menu.py:50 +msgid "Device" +msgstr "מכשיר" + +#: mygpo/history/templates/episode-history.html:127 +msgid "Add" +msgstr "הוספה" + +#: mygpo/history/templates/history.html:12 +#: mygpo/history/templates/history.html:15 +#: mygpo/history/templates/podcast-history.html:32 +#: mygpo/podcasts/templates/podcast.html:143 +msgid "Subscription History" +msgstr "היסטוריית מינויים" + +#: mygpo/history/templates/history.html:12 +#: mygpo/history/templates/history.html:15 +msgid "for" +msgstr "עבור" + +#: mygpo/history/templates/history.html:22 +#: mygpo/web/templates/device-edit.html:16 +msgid "Back to" +msgstr "חזרה אל" + +#: mygpo/history/templates/history.html:66 +msgid "Nothing happened yet." +msgstr "שום דבר לא קרה עדיין." + +#: mygpo/history/templates/history.html:73 +#: mygpo/history/templates/history.html:75 +msgid "Later" +msgstr "מאוחר יותר" + +#: mygpo/history/templates/history.html:80 +#: mygpo/history/templates/history.html:82 +msgid "Now" +msgstr "עכשיו" + +#: mygpo/history/templates/history.html:87 +#: mygpo/history/templates/history.html:89 +msgid "Earlier" +msgstr "מוקדם יותר" + +#: mygpo/history/templates/podcast-history.html:47 +msgid "no history yet" +msgstr "אין היסטוריה עדיין" + +#: mygpo/podcastlists/templates/list.html:28 +#, python-format +msgid "\"%(list_title)s\" by %(ownername)s" +msgstr "„%(list_title)s” מאת %(ownername)s" + +#: mygpo/podcastlists/templates/list.html:35 +#, python-format +msgid "a podcast list by %(ownername)s" +msgstr "רשימת פודקאסטים מאת %(ownername)s" + +#: mygpo/podcastlists/templates/list.html:74 +msgid "No Podcasts" +msgstr "אין פודקאסטים" + +#: mygpo/podcastlists/templates/list.html:84 +msgid "Vote" +msgstr "הצבעה" + +#: mygpo/podcastlists/templates/list.html:90 +msgid "Download as OPML" +msgstr "הורדה כ־OPML" + +#: mygpo/podcastlists/templates/list.html:121 +msgid "Your own Podcast Lists" +msgstr "רשימות הפודקאסטים האישיות שלך" + +#: mygpo/podcastlists/templates/list.html:122 +msgid "Create and share your own podcast lists " +msgstr "יצירה ושיתוף של רשימות פודקאסטים משלך" + +#: mygpo/podcastlists/templates/list.html:123 +#: mygpo/publisher/templates/publisher/home.html:31 +#: mygpo/web/templates/user_subscriptions.html:65 +msgid "Go" +msgstr "להמשיך" + +#: mygpo/podcastlists/templates/lists.html:29 +#: mygpo/podcastlists/templates/lists_user.html:22 +#: mygpo/share/templates/userpage.html:88 +#, python-format +msgid "%(num_podcasts)s Podcasts" +msgstr "%(num_podcasts)s פודקאסטים" + +#: mygpo/podcastlists/templates/lists.html:34 +#: mygpo/web/templates/device-edit.html:77 +msgid "Delete" +msgstr "מחיקה" + +#: mygpo/podcastlists/templates/lists.html:41 +msgid "You don't have any podcast lists yet." +msgstr "אין לך רשימות פודקאסטים עדיין." + +#: mygpo/podcastlists/templates/lists.html:52 +#: mygpo/share/templates/share/favorites.html:57 +#: mygpo/web/templates/devicelist.html:64 +msgid "Create" +msgstr "יצירה" + +#: mygpo/podcastlists/templates/lists.html:60 +msgid "" +"Podcast Lists are intended to share podcasts about certain topics and are " +"therefore always visible to everyone." +msgstr "" +"רשימות פודקאסטים מיועדות לשיתוף פודקאסטים בנושאים מסוימים ולכן הן תמיד " +"גלויות לכולם." + +#: mygpo/podcastlists/templates/lists_user.html:12 +#, python-format +msgid "Podcast Lists by %(username)s" +msgstr "רשימות פודקאסטים מאת %(username)s" + +#: mygpo/podcastlists/templates/lists_user.html:27 +#, python-format +msgid "%(username)s hasn't created any podcast lists yet." +msgstr "לא נוצרו עדיין רשימות פודקאסטים על ידי המשתמש %(username)s." + +#: mygpo/podcastlists/views.py:104 +msgid "You have to specify a title." +msgstr "עליך לציין כותרת." + +#: mygpo/podcastlists/views.py:110 +#, python-brace-format +msgid "\"{title}\" is not a valid title" +msgstr "„{title}” אינה כותרת תקנית" + +#: mygpo/podcastlists/views.py:169 +msgid "Thanks for rating!" +msgstr "תודה לך על הדירוג!" + +#: mygpo/podcasts/admin.py:22 mygpo/web/templates/device-edit.html:28 +msgid "Edit" +msgstr "עריכה" + +#: mygpo/podcasts/models.py:704 +msgid "Unknown Podcast" +msgstr "פודקאסט לא ידוע" + +#: mygpo/podcasts/models.py:706 +#, python-brace-format +msgid "Unknown Podcast from {domain}" +msgstr "פודקאסט לא ידוע מ־{domain}" + +#: mygpo/podcasts/templates/episode.html:85 +#: mygpo/podcasts/templates/episodes.html:27 +#: mygpo/podcasts/templates/podcast.html:39 +#: mygpo/publisher/templates/publisher/episode.html:31 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/info.html:9 +#: mygpo/publisher/templates/publisher/info.html:26 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Publisher Pages" +msgstr "עמודי הפצה" + +#: mygpo/podcasts/templates/episode.html:91 +msgid "Remove Favorite" +msgstr "הסרת מועדף" + +#: mygpo/podcasts/templates/episode.html:95 +msgid "Favorite" +msgstr "הוספה למועדפים" + +#: mygpo/podcasts/templates/episode.html:103 +msgid "Episode History" +msgstr "היסטוריית פרקים" + +#: mygpo/podcasts/templates/podcast-base.html:39 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Unnamed Podcast" +msgstr "פודקאסט ללא שם" + +#: mygpo/podcasts/templates/podcast-base.html:44 +#: mygpo/publisher/templates/publisher/podcast.html:29 +msgid "by" +msgstr "מאת" + +#: mygpo/podcasts/templates/podcast-base.html:50 +#: mygpo/publisher/templates/publisher/podcast.html:35 +#: mygpo/share/templates/share/favorites.html:29 +msgid "Feed" +msgstr "הזנה" + +#: mygpo/podcasts/templates/podcast-base.html:65 +#: mygpo/publisher/templates/publisher/podcast.html:44 +msgid "subscribers" +msgstr "מנויים" + +#: mygpo/podcasts/templates/podcast.html:48 +msgid "Login to Subscribe" +msgstr "יש להיכנס כדי להירשם" + +#: mygpo/podcasts/templates/podcast.html:59 +#: mygpo/suggestions/templates/suggestions.html:31 +#: mygpo/web/templates/subscribe.html:11 mygpo/web/templates/subscribe.html:36 +msgid "Subscribe" +msgstr "הרשמה" + +#: mygpo/podcasts/templates/podcast.html:67 mygpo/web/templates/device.html:31 +msgid "Unsubscribe" +msgstr "ביטול הרשמה" + +#: mygpo/podcasts/templates/podcast.html:78 +msgid "Subscribe on all devices" +msgstr "הרשמה בכל המכשירים" + +#: mygpo/podcasts/templates/podcast.html:110 +msgid "Unsubscribe from all devices " +msgstr "ביטול ההרשמה בכל המכשירים" + +#: mygpo/podcasts/templates/podcast.html:149 +#: mygpo/podcasts/templates/podcast.html:164 +msgid "Tags" +msgstr "תגיות" + +#: mygpo/podcasts/templates/podcast.html:199 +msgid "Older Episodes" +msgstr "פרקים ישנים יותר" + +#: mygpo/publisher/forms.py:5 +#: mygpo/publisher/templates/publisher/episode.html:60 +msgid "URL" +msgstr "כתובת" + +#: mygpo/publisher/templates/link.html:8 +msgid "For Podcast Authors" +msgstr "ליוצרי פודקאסטים" + +#: mygpo/publisher/templates/link.html:11 +msgid "Link to" +msgstr "קישור אל" + +#: mygpo/publisher/templates/link.html:17 +#: mygpo/publisher/templates/publisher/podcast.html:209 +#, python-format +msgid "" +"You can paste this code on your website, so users of %(sitename)s can " +"directly subscribe to your podcast." +msgstr "" +"ניתן להדביק את הקוד הזה באתר שלך, כדי שהמשתמשים של %(sitename)s יכולים " +"להירשם ישירות לפודקאסט שלך." + +#: mygpo/publisher/templates/link.html:18 +msgid "Change the field 'YOUR_ADRESS' with the adress of your podcast feed." +msgstr "יש להחליף את השדה ‚YOUR_ADRESS’ בכתובת הזנת הפודקאסט שלך." + +#: mygpo/publisher/templates/publisher/advertise.html:9 +#: mygpo/publisher/templates/publisher/info.html:20 +#: mygpo/web/templates/base.html:153 mygpo/web/templates/base.html:180 +#: mygpo/web/templates/home.html:173 mygpo/web/templates/home.html:200 +#: mygpo/web/templatetags/menu.py:66 +msgid "Advertise" +msgstr "פרסום" + +#: mygpo/publisher/templates/publisher/advertise.html:12 +#, python-format +msgid "Advertise on %(sitename)s" +msgstr "פרסום ב־%(sitename)s" + +#: mygpo/publisher/templates/publisher/advertise.html:18 +#, python-format +msgid "" +"For just € 20,-- your podcast will be shown on the front page of " +"%(sitename)s for a week. Additionally it will be the first podcast " +"in gPodder's list of example podcasts." +msgstr "" +"עבור 20 אירו בלבד הפודקאסט שלך יכול להופיע בעמוד הפתיחה של " +"%(sitename)s למשך שבוע. בנוסף הוא יהיה הפודקאסט הראשון " +"הרשימת הפודקאסטים לדוגמה של gPodder." + +#: mygpo/publisher/templates/publisher/advertise.html:20 +msgid "" +"You can chose when your advertisement will be active, and the text that will " +"be displayed for your podcast." +msgstr "ניתן לבחור אם הפרסום שלך יהיה פעיל, והטקסט שיוצג עבור הפודקאסט שלך." + +#: mygpo/publisher/templates/publisher/advertise.html:22 +msgid "Front Page" +msgstr "עמוד פתיחה" + +#: mygpo/publisher/templates/publisher/advertise.html:23 +#, python-format +msgid "" +"Your podcast will be displayed on the front page of %(sitename)s for both " +"logged-in and anonymous users." +msgstr "" +"הפודקאסט שלך יופיע בעמוד הפתיחה של %(sitename)s גם למשתמשים שנכנסו למערכת " +"וגם למשתמשים אלמוניים." + +#: mygpo/publisher/templates/publisher/advertise.html:25 +msgid "Example Podcasts" +msgstr "פודקאסטים לדוגמה" + +#: mygpo/publisher/templates/publisher/advertise.html:26 +msgid "" +"The gPodder client offers a list of example podcasts when started for the " +"first time. This list is loaded from the web on demand. For the time of your " +"advertisement, your podcast will be the first in the list." +msgstr "" +"הלקוח של gPodder מציע רשימת פודקאסטים לדוגמה כשהוא מופעל בפעם הראשונה. רשימה " +"זו נטענת מהאתר לפי דרישה. לזמן הפרסום שלך, הפודקאסט שלך יהיה הראשון ברשימה." + +#: mygpo/publisher/templates/publisher/advertise.html:28 +msgid "Visitors" +msgstr "מבקרים" + +#: mygpo/publisher/templates/publisher/advertise.html:29 +msgid "" +"Both the front page and the list of example podcasts are accessed by more " +"than 1000 people daily, who are actively looking for interesting podcasts. " +"We do, however, not guarantee a certain number of visitors during your " +"advertisement period." +msgstr "" +"גם עמוד הפתיחה וגם רשימת הדוגמאות מקבלים חשיפה של למעלה מ־1000 איש ביום, " +"שמחפשים באופן פעיל פודקאסטים מעניינים. עם זאת, איננו מתחייבים למספר מסוים של " +"מבקרים במהלך תקופת הפרסום שלך." + +#: mygpo/publisher/templates/publisher/advertise.html:31 +msgid "Start your Advertisement" +msgstr "התחלת הפרסום שלך" + +#: mygpo/publisher/templates/publisher/advertise.html:32 +#, python-format +msgid "" +"Contact stefan@gpodder.net if you " +"would like to advertise on %(sitename)s or if you have any questions. " +"Payments are done via PayPal to thp@perli.net." +msgstr "" +"עליך ליצור קשר עם stefan@gpodder.net אם ברצונך לפרסם באתר %(sitename)s או אם יש לך שאלות. התשלום הוא דרך " +"PayPal לטובת thp@perli.net." + +#: mygpo/publisher/templates/publisher/episode.html:31 +msgid "Unnamed Episode" +msgstr "פרק ללא שם" + +#: mygpo/publisher/templates/publisher/episode.html:55 +msgid "Episode List" +msgstr "רשימת פרקים" + +#: mygpo/publisher/templates/publisher/episode.html:56 +msgid "Podcast Page" +msgstr "עמוד פודקאסט" + +#: mygpo/publisher/templates/publisher/episode.html:61 +msgid "" +"You can configure the URL of this episode. Previous keys will automatically " +"be added to the alternative keys and always redirect to the current URL." +msgstr "" +"ניתן להגדיר את כתובת הפרק הזה. מפתחות קודמים יתווספו אוטומטית למפתחות " +"החלופיים ותמיד יפנו לכתובת הנוכחית." + +#: mygpo/publisher/templates/publisher/episode.html:72 +#: mygpo/publisher/templates/publisher/podcast.html:129 +#: mygpo/web/templates/account.html:65 mygpo/web/templates/account.html:142 +#: mygpo/web/templates/device-edit.html:53 +msgid "Save" +msgstr "שמירה" + +#: mygpo/publisher/templates/publisher/episode.html:86 +msgid "Episode data" +msgstr "נתוני פודקאסט" + +#: mygpo/publisher/templates/publisher/episode.html:87 +msgid "Last update: " +msgstr "עדכון אחרון:" + +#: mygpo/publisher/templates/publisher/episodes.html:24 +msgid "Return to Podcast Page" +msgstr "חזרה לעמוד הפודקאסט" + +#: mygpo/publisher/templates/publisher/episodes.html:31 +#: mygpo/share/templates/userpage.html:182 +#: mygpo/share/templates/userpage.html:188 mygpo/web/templatetags/menu.py:43 +msgid "Episodes" +msgstr "פרקים" + +#: mygpo/publisher/templates/publisher/episodes.html:34 +msgid "Title" +msgstr "כותרת" + +#: mygpo/publisher/templates/publisher/episodes.html:35 +msgid "Released" +msgstr "יצא לאור" + +#: mygpo/publisher/templates/publisher/group.html:21 +msgid "Publisher Pages:" +msgstr "עמודי הפצה:" + +#: mygpo/publisher/templates/publisher/group.html:26 +msgid "This is a group of podcasts containing" +msgstr "זו קבוצה של פודקאסטים שמכילה" + +#: mygpo/publisher/templates/publisher/home.html:9 +msgid "Podcasts Published by Me" +msgstr "פודקאסטים שאני פרסמתי" + +#: mygpo/publisher/templates/publisher/home.html:12 +msgid "Publisher Area" +msgstr "אזור הפצה" + +#: mygpo/publisher/templates/publisher/home.html:21 +msgid "Staff only" +msgstr "צוות בלבד" + +#: mygpo/publisher/templates/publisher/home.html:24 +msgid "Go to the publisher page of any podcast by entering its feed URL" +msgstr "ניתן לעבור לעמוד ההפצה של כל פודקאסט שהוא על ידי הקלדת כתובת ההזנה שלו" + +#: mygpo/publisher/templates/publisher/home.html:29 +msgid "http://" +msgstr "http://‎" + +#: mygpo/publisher/templates/publisher/home.html:43 +msgid "Published Podcasts" +msgstr "פודקאסטים שפורסמו" + +#: mygpo/publisher/templates/publisher/home.html:46 +msgid "You have publisher permissions for the following podcasts." +msgstr "יש לך הרשאות הפצה לפודקאסטים הבאים." + +#: mygpo/publisher/templates/publisher/home.html:62 +msgid "Update podcasts" +msgstr "עדכון פודקאסטים" + +#: mygpo/publisher/templates/publisher/home.html:65 +msgid "" +"To update all of your published podcasts automatically, you can request the " +"following URL" +msgstr "כדי לעדכן את כל הפודקאסטים שפרסמת אוטומטית, ניתן לבקש את הכתובת הבאה" + +#: mygpo/publisher/templates/publisher/home.html:69 +msgid "Create a new token" +msgstr "יצירת אסימון חדש" + +#: mygpo/publisher/templates/publisher/info.html:12 +msgid "Publisher Services" +msgstr "שירותי הפצה" + +#: mygpo/publisher/templates/publisher/info.html:18 +#, python-format +msgid "%(sitename)s offers several services for podcast publishers." +msgstr "%(sitename)s מציע מגוון שירותים למפיצי פודקאסטים." + +#: mygpo/publisher/templates/publisher/info.html:21 +#, python-format +msgid "" +"You can place your podcast on the frontpage of %(sitename)s to promote your " +"podcast. Users of the gPodder client will also find your podcast as the " +"first item in the list of example podcast." +msgstr "" +"יש לך אפשרות להציג את הפודקאסט שלך בעמוד הפתיחה של %(sitename)s כדי לקדם את " +"הפודקאסט שלך. המשתמשים בלקוח ה־gPodder גם כן יוכלו למצוא את הפודקאסט שלך " +"בתור הפריט הראשון ברשימת הפודקאסטים לדוגמה." + +#: mygpo/publisher/templates/publisher/info.html:24 +#, python-format +msgid "" +"You can find additional details on the advertisement page." +msgstr "" +"ניתן למצוא פרטים נוספים בעמוד הפרסומות." + +#: mygpo/publisher/templates/publisher/info.html:27 +#, python-format +msgid "" +"On the %(sitename)s Publisher Pages you can find valuable " +"information about the Podcasts published by you." +msgstr "" +"תחת עמודי ההפצה של %(sitename)s ניתן למצוא מידע חשוב בנוגע " +"לפודקאסטים שמופצים על ידיך." + +#: mygpo/publisher/templates/publisher/info.html:30 +#, python-format +msgid "" +"The Publisher Pages are currently in Beta, but if you are publishing a " +"podcast listed on %(sitename)s, feel free to apply by sending a mail with\n" +"
      \n" +"
    • your username on %(sitename)s (register " +"if you don't have one)
    • \n" +"
    • the name and URL of the podcast you are publishing
    • \n" +"
    • an email address that is visible somewhere on the podcast's website " +"(to legitimate you)\n" +"
    \n" +" to stefan@gpodder.net." +msgstr "" +"עמודי ההפצה נמצאים בשלבי ניסוי, אך אם יש לך פודקאסט בהפצתך שרשום תחת " +"%(sitename)s, אפשר להירשם לתכנית על ידי שליחת הודעה בדוא״ל עם\n" +"
      \n" +"
    • שם המשתמש שלך באתר %(sitename)s (עליך " +"להירשם אם עוד אין לך אחד כזה)
    • \n" +"
    • שם כתובת הפודקאסט שמופץ על ידיך
    • \n" +"
    • כתובת דוא״ל שגלויה במקום כלשהו באתר הפודקאסט (כדי לוודא שאכן יש לך " +"קשר ישיר אליו)\n" +"
    \n" +" אל stefan@gpodder.net." + +#: mygpo/publisher/templates/publisher/info.html:39 +#, python-format +msgid "" +"If you already are registered as a publisher, please login." +msgstr "" +"אם כבר נרשמת בתור מפיץ, נא להיכנס." + +#: mygpo/publisher/templates/publisher/info.html:41 +#, python-format +msgid "Link to %(sitename)s" +msgstr "קישור אל %(sitename)s" + +#: mygpo/publisher/templates/publisher/info.html:44 +#, python-format +msgid "" +"If you link to %(sitename)s from your " +"website, you can make it really easy for your visitors to subscribe to your " +"podcast - especially for users of mobile phones." +msgstr "" +"הוספת קישור אל %(sitename)s מהאתר שלך, תקל " +"על המבקרים באתר שלך להירשם לפודקאסט שלך - במיוחד עבור משתמשים בטלפונים " +"ניידים." + +#: mygpo/publisher/templates/publisher/podcast.html:49 +#, python-format +msgid "" +"This is the publisher page of %(ptitle)s. You can see some " +"stats and provide additional data for the podcast page." +msgstr "" +"זה הוא עמוד ההפצה של %(ptitle)s. ניתן לצפות בסטטיסטיקה " +"ולספק נתונים נוספים לעמוד הפודקאסט." + +#: mygpo/publisher/templates/publisher/podcast.html:50 +msgid "Go to Podcast Page" +msgstr "מעבר לעמוד הפודקאסט" + +#: mygpo/publisher/templates/publisher/podcast.html:57 +msgid "The podcast information is regularly retrieved from the podcast feed" +msgstr "פרטי הפודקאסט מתקבלים על פי רוב מהזנת הפודקאסט" + +#: mygpo/publisher/templates/publisher/podcast.html:60 +#, fuzzy +#| msgid "Update now" +msgid "Updates" +msgstr "עדכון כעת" + +#: mygpo/publisher/templates/publisher/podcast.html:62 +msgid "Update interval:" +msgstr "מרווח בין עדכונים:" + +#: mygpo/publisher/templates/publisher/podcast.html:86 +msgid "Successful" +msgstr "" + +#: mygpo/publisher/templates/publisher/podcast.html:88 +msgid "Error" +msgstr "" + +#: mygpo/publisher/templates/publisher/podcast.html:106 +msgid "Update now" +msgstr "עדכון כעת" + +#: mygpo/publisher/templates/publisher/podcast.html:117 mygpo/web/forms.py:70 +#: mygpo/web/templates/base.html:160 mygpo/web/templates/home.html:180 +msgid "Twitter" +msgstr "טוויטר" + +#: mygpo/publisher/templates/publisher/podcast.html:135 +msgid "Feed Check" +msgstr "בדיקת הזנה" + +#: mygpo/publisher/templates/publisher/podcast.html:143 +msgid "PubSubHubbub" +msgstr "PubSubHubbub" + +#: mygpo/publisher/templates/publisher/podcast.html:152 +#, python-format +msgid "" +"If you publish your podcast feed through a PubSubHubbu hub, %(sitename)s can immediatelly update " +"your podcast when a new episode is released." +msgstr "" +"על ידי פרסום הזנת הפודקאסטים שלך דרך נקודת הריכוז PubSubHubbu, ל־%(sitename)s יש אפשרות לעדכן " +"את הפודקאסט שלך ברגע שפרק חדש מופץ." + +#: mygpo/publisher/templates/publisher/podcast.html:157 +#, python-format +msgid "" +"Your podcast is published through %(hub)s and should " +"update immediatelly for each new episode." +msgstr "" +"הפודקאסט שלך מופץ דרך %(hub)s והוא אמור להתעדכן מיד " +"בכל פרק." + +#: mygpo/publisher/templates/publisher/podcast.html:161 +#, python-format +msgid "" +"Your podcast is published through %(hub)s but our " +"subscription has not yet been verified." +msgstr "" +"הפודקאסט שלך מופץ דרך %(hub)s אך המינוי שלך טרם אומת." + +#: mygpo/publisher/templates/publisher/podcast.html:166 +#, python-format +msgid "" +"We did not find a hub in your podcast feed. Your feed is updated regularly, " +"but there might be some delay until a new episode shows up on %(sitename)s." +msgstr "" +"לא מצאנו נקודת ריכוז להזנת הפודקאסט שלך. ההזנה שלך מתעדכנת באופן סדיר, אך " +"תיתכן השהיה קצרה עד שהפרקים יופיעו ב־%(sitename)s." + +#: mygpo/publisher/templates/publisher/podcast.html:178 +msgid "License Information" +msgstr "פרטי רישיון" + +#: mygpo/publisher/templates/publisher/podcast.html:186 +#, python-format +msgid "" +"You should include license information in your feed so that users and " +"%(sitename)s can know, under which conditions your content can be used." +msgstr "" +"מוטב לכלול פרטי רישיון כחלק מההזנה שלך כדי שהמשתמשים לצד %(sitename)s יוכלו " +"לדעת, תחת אילו תנאים ניתן להשתמש בתוכן שלך." + +#: mygpo/publisher/templates/publisher/podcast.html:190 +#, python-format +msgid "" +"We found the following license in your podcast: " +"%(license)s" +msgstr "" +"מצאנו את הרישיון הבא בפודקאסט שלך: %(license)s" + +#: mygpo/publisher/templates/publisher/podcast.html:194 +msgid "" +"We did not find a license in your podcast feed. Refer to gPodder Podcast Feed Best Practice on how to " +"include license information." +msgstr "" +"לא מצאנו רישיון בהזנת הפודקאסט שלך. יש לפנות אל ההמלצות להזנות פודקאסטים של gPodder על כיצד מוטב להוסיף " +"פרטי רישיון." + +#: mygpo/publisher/templates/publisher/podcast.html:208 +msgid "Link" +msgstr "קישור" + +#: mygpo/publisher/templates/publisher/podcast.html:216 +msgid "Show Group Stats" +msgstr "הצגת סטטיסטיקה קבוצתית" + +#: mygpo/publisher/views.py:148 +msgid "" +"The update has been scheduled. It might take some time until the results are " +"visible." +msgstr "תוזמן עדכון. יכול להיות שיחלוף קצת זמן עד שניתן יהיה לראות תוצאות." + +#: mygpo/publisher/views.py:161 mygpo/users/views/settings.py:98 +msgid "Data updated" +msgstr "נתונים עודכנו" + +#: mygpo/publisher/views.py:172 +msgid "Publisher token updated" +msgstr "אסימון מפרסם עודכן" + +#: mygpo/publisher/views.py:262 +#, python-brace-format +msgid "Removed slug {slug} from {episode}" +msgstr "הוסר השם המופשט {slug} מהפרק {episode}" + +#: mygpo/share/templates/share/components/private-toggle.html:11 +msgid "Public" +msgstr "ציבורי" + +#: mygpo/share/templates/share/components/private-toggle.html:16 +msgid "Private" +msgstr "פרטי" + +#: mygpo/share/templates/share/favorites.html:10 +#: mygpo/share/templates/share/favorites.html:14 +msgid "Share your favorite episodes" +msgstr "שיתוף הפרקים המועדפים עליך" + +#: mygpo/share/templates/share/favorites.html:46 +#: mygpo/share/templates/share/favorites.html:48 +msgid "Directory Entry" +msgstr "רשומה בספרייה" + +#: mygpo/share/templates/share/favorites.html:70 +#, python-format +msgid "" +"To keep your favorites private (from others and from the public pages of " +"%(site)s), you need to use HTTP-Authentication in your client. " +msgstr "" +"כדי לשמור על פרטיות המועדפים שלך (מפני אחרים ומפני העמודים הציבוריים של " +"%(site)s), עליך להשתמש ב־HTTP-Authentication בלקוח שלך." + +#: mygpo/share/templates/share/favorites.html:72 +msgid "Username:" +msgstr "שם משתמש:" + +#: mygpo/share/templates/share/favorites.html:73 +msgid "Password:" +msgstr "ססמה:" + +#: mygpo/share/templates/share/favorites.html:79 +msgid "Remove password" +msgstr "הסרת ססמה" + +#: mygpo/share/templates/share/favorites.html:82 +msgid "Generate new password" +msgstr "יצירת ססמה חדשה" + +#: mygpo/share/templates/share/favorites.html:98 +#, python-format +msgid "" +"You've marked your favorites-feed as public and others can\n" +" subscribe to it. It will also be indexed by %(sitename)s." +msgstr "" +"סימנת את רשימת המועדפים שלך כציבורית ואחרים יכולים\n" +" להירשם אליה. היא גם תופיע באינדקס של %(sitename)s." + +#: mygpo/share/templates/share/favorites.html:110 +msgid "Create password" +msgstr "יצירת ססמה" + +#: mygpo/share/templates/share/overview.html:9 +msgid "Sharing" +msgstr "מתבצע שיתוף" + +#: mygpo/share/templates/share/overview.html:12 +#: mygpo/subscriptions/templates/subscriptions.html:67 +#: mygpo/web/templates/favorites.html:39 +msgid "Share" +msgstr "שיתוף" + +#: mygpo/share/templates/share/overview.html:21 +#: mygpo/web/templates/device.html:19 mygpo/web/templates/privacy.html:25 +#: mygpo/web/templatetags/menu.py:45 mygpo/web/templatetags/menu.py:46 +#: mygpo/web/templatetags/menu.py:57 +msgid "Subscriptions" +msgstr "מינויים" + +#: mygpo/share/templates/share/overview.html:34 +#: mygpo/share/templates/share/overview.html:49 +#: mygpo/share/templates/share/overview.html:76 +msgid "New Token" +msgstr "אסימון חדש" + +#: mygpo/share/templates/share/overview.html:40 +msgid "User Page" +msgstr "דף משתמש" + +#: mygpo/share/templates/share/overview.html:51 +#: mygpo/web/templatetags/menu.py:60 +msgid "Settings" +msgstr "הגדרות" + +#: mygpo/share/templates/share/overview.html:57 +msgid "Favorites Feed" +msgstr "הזנת מועדפים" + +#: mygpo/share/templates/share/overview.html:84 +msgid "" +"Private items are protected by a special token. Only those who know the " +"exact link can access your content" +msgstr "" +"פריטים פרטיים מוגנים באסימון מיוחד. רק אלו שמכירים את הקישור המדויק יוכלו " +"לגשת לתוכן שלך" + +#: mygpo/share/templates/userpage-denied.html:16 +#, python-format +msgid "" +"%(username)s didn't share his user page with you. Maybe you should contact " +"him to get the correct link." +msgstr "" +"עמוד המשתמש של %(username)s לא משותף אתך. אולי עדיף ליצור קשר עם המשתמש כדי " +"לקבל את הקישור הנכון." + +#: mygpo/share/templates/userpage-denied.html:20 +msgid "Want to have your own user page?" +msgstr "מעניין אותך לקבל עמוד משתמש משלך?" + +#: mygpo/share/templates/userpage-denied.html:20 +#, python-format +msgid "" +" Go to your Share Page to get the link." +msgstr "" +" עליך לעבור לשיתוף עמוד כדי לקבל את הקישור." + +#: mygpo/share/templates/userpage.html:24 +#, python-format +msgid "a user on %(site)s" +msgstr "משתמש ב־%(site)s" + +#: mygpo/share/templates/userpage.html:35 +#, python-format +msgid "" +"%(username)s is sharing his favorite podcasts on %(site)s." +msgstr "" +"הפודקאסטים המועדפים של %(username)s משותפים ב־%(site)s." + +#: mygpo/share/templates/userpage.html:47 +#, python-format +msgid "%(subscription_count)s Subscriptions" +msgstr "%(subscription_count)s מינויים" + +#: mygpo/share/templates/userpage.html:52 +msgid "Episodes Listened" +msgstr "פרקים שנוגנו" + +#: mygpo/share/templates/userpage.html:68 +#: mygpo/share/templates/userpage.html:97 +#: mygpo/share/templates/userpage.html:148 +msgid "see all" +msgstr "להציג הכול" + +#: mygpo/share/templates/userpage.html:83 +#, python-format +msgid "%(list_count)s Podcast Lists" +msgstr "%(list_count)s רשימות פודקאסטים" + +#: mygpo/share/templates/userpage.html:110 +#, python-format +msgid "%(num_episodes)s Recently Played Episodes" +msgstr "%(num_episodes)s פרקים שנוגנו לאחרונה" + +#: mygpo/share/templates/userpage.html:131 +#, python-format +msgid "%(num_fav_episodes)s Favorite Episodes" +msgstr "%(num_fav_episodes)s פרקים מועדפים" + +#: mygpo/share/templates/userpage.html:166 +msgid "That's You!" +msgstr "מדובר בך!" + +#: mygpo/share/templates/userpage.html:168 +msgid "Edit your profile" +msgstr "עריכת הפרופיל שלך" + +#: mygpo/share/templates/userpage.html:176 +msgid "Stats" +msgstr "סטטיסטיקה" + +#: mygpo/share/templates/userpage.html:178 +msgid "Total Played:" +msgstr "סך הכול התנגנו:" + +#: mygpo/share/templates/userpage.html:184 +msgid "Played Last Month:" +msgstr "התנגנו בחודש שעבר:" + +#: mygpo/share/templates/userpage.html:190 +msgid "Member Since:" +msgstr "חברות מאז:" + +#: mygpo/share/templates/userpage.html:194 +msgid "the early days" +msgstr "ימי קדם" + +#: mygpo/subscriptions/templates/subscriptions.html:11 +#: mygpo/subscriptions/templates/subscriptions.html:15 +msgid "Podcast Subscriptions" +msgstr "מינויים לפודקאסטים" + +#: mygpo/subscriptions/templates/subscriptions.html:32 +#, python-format +msgid "%(episode_count)s Episodes" +msgstr "%(episode_count)s פרקים" + +#: mygpo/subscriptions/templates/subscriptions.html:42 +#, python-format +msgid "" +"You don't have any subscriptions yet. Set up your podcast clients and discover interesting " +"podcasts." +msgstr "" +"אין לך מינויים עדיין. עליך להגדיר את לקוח " +"הפודקאסטים שלך ואת איתור פודקאסטים מעניינים." + +#: mygpo/subscriptions/templates/subscriptions.html:63 +#: mygpo/suggestions/templates/suggestions.html:54 +#: mygpo/web/templates/device.html:53 +msgid "Download OPML" +msgstr "הורדת OPML" + +#: mygpo/subscriptions/views.py:108 +#, python-format +msgid "%(username)s's Podcast Subscriptions on %(site)s" +msgstr "מינויי הפודקאסטים של %(username)s ב־%(site)s" + +#: mygpo/subscriptions/views.py:112 +#, python-format +msgid "Recent changes to %(username)s's podcast subscriptions on %(site)s" +msgstr "השינויים האחרונים למינויי הפודקאסטים של %(username)s תחת %(site)s" + +#: mygpo/subscriptions/views.py:136 +#, python-format +msgid "%(username)s subscribed to %(podcast)s (%(site)s)" +msgstr "הרשמה אל %(podcast)s מצד %(username)s (%(site)s)" + +#: mygpo/subscriptions/views.py:138 +#, python-format +msgid "%(username)s unsubscribed from %(podcast)s (%(site)s)" +msgstr "ביטול הרשמה אל %(podcast)s מצד %(username)s (%(site)s)" + +#: mygpo/suggestions/templates/suggestions.html:9 +#: mygpo/suggestions/templates/suggestions.html:12 +msgid "Suggested Podcasts" +msgstr "פודקאסטים מומלצים" + +#: mygpo/suggestions/templates/suggestions.html:36 +msgid "No Interest" +msgstr "אין עניין" + +#: mygpo/suggestions/templates/suggestions.html:47 +msgid "No Suggestions Yet" +msgstr "אין הצעות עדיין" + +#: mygpo/users/models.py:226 +msgid "Desktop" +msgstr "מחשב שולחני" + +#: mygpo/users/models.py:227 +msgid "Laptop" +msgstr "מחשב נייד" + +#: mygpo/users/models.py:228 +msgid "Cell phone" +msgstr "טלפון סלולרי" + +#: mygpo/users/models.py:229 +msgid "Server" +msgstr "שרת" + +#: mygpo/users/models.py:230 +msgid "Tablet" +msgstr "מחשב לוח" + +#: mygpo/users/models.py:231 +msgid "Other" +msgstr "אחר" + +#: mygpo/users/templates/registration/activation_failed.html:8 +msgid "Activation failed" +msgstr "ההפעלה נכשלה" + +#: mygpo/users/templates/registration/activation_failed.html:11 +msgid "Account not activated" +msgstr "החשבון לא הופעל" + +#: mygpo/users/templates/registration/activation_failed.html:16 +msgid "Your account could not be activated." +msgstr "לא ניתן להפעיל את החשבון שלך." + +#: mygpo/users/templates/registration/registration_complete.html:9 +msgid "Registration complete" +msgstr "ההרשמה הושלמה" + +#: mygpo/users/templates/registration/registration_complete.html:13 +msgid "" +"Please confirm your account using the email you should just have received." +msgstr "נא לאמת את החשבון שלך בעזרת ההודעה שקיבלת בדוא״ל." + +#: mygpo/users/templates/registration/registration_form.html:8 +#: mygpo/users/templates/registration/registration_form.html:11 +msgid "Create Account" +msgstr "יצירת חשבון" + +#: mygpo/users/templates/registration/registration_form.html:18 +msgid "You are already registered." +msgstr "כבר נרשמת." + +#: mygpo/users/templates/registration/registration_form.html:72 +#: mygpo/web/templates/base.html:52 mygpo/web/templates/home.html:47 +#: mygpo/web/templates/home.html:103 mygpo/web/templates/login.html:48 +#: mygpo/web/templatetags/menu.py:24 +msgid "Register" +msgstr "הרשמה" + +#: mygpo/users/templates/registration/registration_form.html:87 +msgid "Activation Mail" +msgstr "הודעת הפעלה בדוא״ל" + +#: mygpo/users/templates/registration/registration_form.html:88 +msgid "" +"After registration, you'll receive an activation mail to confirm the " +"validity of your email address." +msgstr "" +"לאחר ההרשמה, תגיע אליך הודעת הפעלה בדוא״ל כדי לאמת את כתובת הדוא״ל שלך." + +#: mygpo/users/templates/registration/registration_form.html:90 +#, python-format +msgid "" +"If it doesn't arrive, you can resend it." +msgstr "" +"אם ההודעה לא מגיעה אליך, ניתן לשלוח " +"אותה מחדש." + +#: mygpo/users/templates/registration/resend_activation.html:4 +#: mygpo/users/templates/registration/resend_activation.html:8 +msgid "Resend activation e-mail" +msgstr "שליחת הודעת הפעלה בדוא״ל מחדש" + +#: mygpo/users/templates/registration/resend_activation.html:18 +#: mygpo/web/templates/restore_password.html:21 +msgid "" +"Please enter either your username or (if you don't know that either) the e-" +"mail address which you used to register your account. An e-mail will be sent " +"to you." +msgstr "" +"נא להקליד את שם משתמש או (או אם הפרט הזה חסר לך) את כתובת הדוא״ל בה השתמשת " +"לטובת רישום לחשבון. תישלח אליך הודעה בדוא״ל." + +#: mygpo/users/templates/registration/resend_activation.html:36 +msgid "Send activation e-mail" +msgstr "שליחת הודעת הפעלה בדוא״ל" + +#: mygpo/users/templates/registration/resent_activation.html:4 +#: mygpo/users/templates/registration/resent_activation.html:7 +msgid "Sent Activation Email" +msgstr "נשלחה הודעה הפעלה בדוא״ל" + +#: mygpo/users/templates/registration/resent_activation.html:11 +#, fuzzy +#| msgid "The activation email has been reset." +msgid "The activation email has been resent." +msgstr "הודעת האימות נשלחה בדוא״ל מחדש." + +#: mygpo/users/views/device.py:80 mygpo/users/views/device.py:170 +msgid "Synchronize with the following devices" +msgstr "סנכרון עם המכשירים הבאים" + +#: mygpo/users/views/device.py:98 +msgid "Please fill out all fields." +msgstr "נא למלא את כל השדות." + +#: mygpo/users/views/device.py:110 +msgid "Device saved" +msgstr "המכשיר נשמר" + +#: mygpo/users/views/device.py:117 mygpo/users/views/device.py:148 +msgid "You can't use the same Device ID for two devices." +msgstr " ניתן להשתמש באותו מזהה המכשיר על שני מכשירים." + +#: mygpo/users/views/device.py:141 +msgid "Device updated" +msgstr "מכשיר עודכן" + +#: mygpo/users/views/device.py:194 +#, python-brace-format +msgid "Could not upload subscriptions: {err}" +msgstr "לא ניתן להעלות מינויים: {err}" + +#: mygpo/users/views/device.py:292 +msgid "Your subscription will be updated in a moment." +msgstr "המינוי שלך יתעדכן בעוד רגע קט." + +#: mygpo/users/views/registration.py:87 +msgid "Username or email address already in use" +msgstr "שם המשתמש או כתובת הדוא״ל כבר נמצאים בשימוש" + +#: mygpo/users/views/registration.py:152 +msgid "The activation link is either not valid or has already expired." +msgstr "קישור ההפעלה שגוי או שתוקפו פג." + +#: mygpo/users/views/registration.py:157 +msgid "Your user has been activated. You can log in now." +msgstr "המשתמש שלך הופעל. כעת יתאפשר לך להיכנס." + +#: mygpo/users/views/registration.py:174 +msgid "Either username or email address are required." +msgstr "נדרשים שם משתמש או כתובת דוא״ל." + +#: mygpo/users/views/registration.py:194 mygpo/users/views/user.py:129 +msgid "User does not exist." +msgstr "משתמש לא קיים." + +#: mygpo/users/views/registration.py:198 +msgid "Your account already has been activated. Go ahead and log in." +msgstr "החשבון שלך כבר הופעל. אפשר לנסות להיכנס לחשבון." + +#: mygpo/users/views/settings.py:57 mygpo/users/views/settings.py:92 +msgid "Oops! Something went wrong. Please double-check the data you entered." +msgstr "אופס! משהו השתבש. נא לבדוק שנית את המידע שהקלדת." + +#: mygpo/users/views/settings.py:110 +msgid "Your account has been disconnected" +msgstr "חשבונך נותק" + +#: mygpo/users/views/user.py:71 +msgid "Username missing" +msgstr "חסר שם משתמש" + +#: mygpo/users/views/user.py:76 +msgid "Password missing" +msgstr "חסרה ססמה" + +#: mygpo/users/views/user.py:83 +msgid "Wrong username or password." +msgstr "שם משתמש או ססמה שגויים." + +#: mygpo/users/views/user.py:89 mygpo/users/views/user.py:134 +msgid "" +"Please activate your account first. We have just re-sent your activation " +"email" +msgstr "" +"נא להפעיל את החשבון שלך תחילה. הרגע שלחנו לך את הודעת ההפעלה בדוא״ל מחדש" + +#: mygpo/users/views/user.py:167 mygpo/users/views/user.py:172 +msgid "Login failed." +msgstr "הכניסה נכשלה." + +#: mygpo/users/views/user.py:180 +msgid "Login with Google is currently not possible." +msgstr "הכניסה עם Google אינה אפשרית כרגע." + +#: mygpo/users/views/user.py:190 +#, python-brace-format +msgid "" +"Your account has been connected with {google}. Open Settings to change this." +msgstr "החשבון שלך התחבר עם {google}. ניתן לפתוח את ההגדרות כדי לשנות את זה." + +#: mygpo/users/views/user.py:202 +#, python-format +msgid "" +"No account connected with your Google account %s. Please log in to connect." +msgstr "אף חשבון לא התחבר לחשבון שלך ב־Google %s. נא להיכנס כדי להתחבר." + +#: mygpo/web/forms.py:20 mygpo/web/forms.py:184 +msgid "E-Mail address" +msgstr "כתובת דוא״ל" + +#: mygpo/web/forms.py:27 +msgid "Current password" +msgstr "ססמה נוכחית" + +#: mygpo/web/forms.py:34 +msgid "New password" +msgstr "ססמה חדשה" + +#: mygpo/web/forms.py:41 +msgid "Confirm password" +msgstr "אימות ססמה" + +#: mygpo/web/forms.py:78 +msgid "A few words about you" +msgstr "כמה מילים עליך" + +#: mygpo/web/forms.py:91 mygpo/web/templates/devicelist.html:23 +msgid "Name" +msgstr "שם" + +#: mygpo/web/forms.py:96 mygpo/web/templates/devicelist.html:24 +msgid "Type" +msgstr "סוג" + +#: mygpo/web/forms.py:100 mygpo/web/templates/devicelist.html:25 +msgid "Device ID" +msgstr "מזהה מכשיר" + +#: mygpo/web/forms.py:103 +msgid "ID on device" +msgstr "מזהה על המכשיר" + +#: mygpo/web/forms.py:115 +msgid "Share this subscription with other users (public)" +msgstr "שיתוף המינוי הזה עם משתמשים אחרים (ציבורי)" + +#: mygpo/web/forms.py:158 +msgid "No device selected" +msgstr "לא נבחר מכשיר" + +#: mygpo/web/forms.py:167 +msgid "Please enter your username" +msgstr "נא להקליד את שם המשתמש שלך" + +#: mygpo/web/forms.py:172 +msgid "or the email address used while registering" +msgstr "או את כתובת הדוא״ל בה השתמשת בעת ההרשמה" + +#: mygpo/web/templates/400.html:4 mygpo/web/templates/400.html:7 +msgid "Bad request" +msgstr "בקשה שגויה" + +#: mygpo/web/templates/400.html:11 +msgid "There has been an error processing your request." +msgstr "אירעה שגיאה בעת עיבוד הבקשה שלך." + +#: mygpo/web/templates/401.html:4 mygpo/web/templates/401.html:7 +msgid "Unauthorized" +msgstr "אין הרשאה" + +#: mygpo/web/templates/401.html:12 +msgid "This page is only available to registered users." +msgstr "עמוד זה זמין ללקוחות רשומים בלבד." + +#: mygpo/web/templates/account.html:8 mygpo/web/templates/account.html:11 +msgid "Account Settings" +msgstr "הגדרות חשבון" + +#: mygpo/web/templates/account.html:73 mygpo/web/templates/account.html:76 +#: mygpo/web/templates/delete_account.html:9 +msgid "Delete Account" +msgstr "מחיקת חשבון" + +#: mygpo/web/templates/account.html:87 +msgid "Google" +msgstr "Google" + +#: mygpo/web/templates/account.html:90 +#, python-format +msgid "Connected with %(gmail)s" +msgstr "מחובר עם %(gmail)s" + +#: mygpo/web/templates/account.html:93 +msgid "Disconnect" +msgstr "ניתוק" + +#: mygpo/web/templates/account.html:96 +msgid "Connect" +msgstr "חיבור" + +#: mygpo/web/templates/base.html:40 mygpo/web/templatetags/menu.py:61 +msgid "Account" +msgstr "חשבון" + +#: mygpo/web/templates/base.html:46 mygpo/web/templates/base.html:50 +#: mygpo/web/templates/home.html:41 mygpo/web/templates/home.html:45 +#: mygpo/web/templates/home.html:102 mygpo/web/templates/login.html:8 +#: mygpo/web/templates/login.html:11 mygpo/web/templates/login.html:46 +#: mygpo/web/templates/restore_password.html:8 +#: mygpo/web/templatetags/menu.py:23 +msgid "Login" +msgstr "כניסה" + +#: mygpo/web/templates/base.html:51 mygpo/web/templates/home.html:46 +#: mygpo/web/templates/login.html:47 +msgid "Login with Google" +msgstr "כניסה עם Google" + +#: mygpo/web/templates/base.html:133 mygpo/web/templates/home.html:153 +#: mygpo/web/templatetags/menu.py:31 +msgid "Discover" +msgstr "עיון" + +#: mygpo/web/templates/base.html:134 mygpo/web/templates/home.html:154 +#: mygpo/web/templatetags/menu.py:32 +msgid "Directory" +msgstr "ספרייה" + +#: mygpo/web/templates/base.html:142 mygpo/web/templates/home.html:162 +msgid "Support" +msgstr "תמיכה" + +#: mygpo/web/templates/base.html:143 mygpo/web/templates/home.html:163 +#: mygpo/web/templatetags/menu.py:25 +msgid "Docs" +msgstr "מסמכים" + +#: mygpo/web/templates/base.html:144 mygpo/web/templates/home.html:164 +msgid "Mailing List" +msgstr "קבוצת דיונים" + +#: mygpo/web/templates/base.html:145 mygpo/web/templates/home.html:165 +msgid "Questions" +msgstr "שאלות" + +#: mygpo/web/templates/base.html:151 mygpo/web/templates/home.html:171 +msgid "Support Us" +msgstr "להביע תמיכה בנו" + +#: mygpo/web/templates/base.html:152 mygpo/web/templates/contribute.html:22 +#: mygpo/web/templates/home.html:172 +msgid "Donate" +msgstr "תרומה" + +#: mygpo/web/templates/base.html:159 mygpo/web/templates/home.html:179 +msgid "Follow" +msgstr "לעקוב" + +#: mygpo/web/templates/base.html:161 mygpo/web/templates/home.html:181 +msgid "Blog" +msgstr "בלוג" + +#: mygpo/web/templates/base.html:162 mygpo/web/templates/home.html:182 +msgid "Blog (RSS)" +msgstr "בלוג (RSS)" + +#: mygpo/web/templates/base.html:168 mygpo/web/templates/home.html:188 +msgid "Develop" +msgstr "פיתוח" + +#: mygpo/web/templates/base.html:169 mygpo/web/templates/home.html:189 +msgid "API" +msgstr "API" + +#: mygpo/web/templates/base.html:170 mygpo/web/templates/home.html:190 +msgid "Libraries" +msgstr "ספריות" + +#: mygpo/web/templates/base.html:171 mygpo/web/templates/home.html:191 +msgid "Clients" +msgstr "לקוחות" + +#: mygpo/web/templates/base.html:177 mygpo/web/templates/home.html:197 +#: mygpo/web/templatetags/menu.py:64 +msgid "Publish" +msgstr "פרסום" + +#: mygpo/web/templates/base.html:178 mygpo/web/templates/home.html:198 +msgid "Get Access" +msgstr "קבלת גישה" + +#: mygpo/web/templates/base.html:179 mygpo/web/templates/home.html:199 +msgid "Link To Us" +msgstr "קישור אלינו" + +#: mygpo/web/templates/base.html:188 mygpo/web/templates/home.html:207 +msgid "hosting provided by prgmr.com" +msgstr "האירוח סופק על ידי prgmr.com" + +#: mygpo/web/templates/contribute.html:8 mygpo/web/templatetags/menu.py:26 +msgid "Contribute" +msgstr "תרומה" + +#: mygpo/web/templates/contribute.html:11 +msgid "Contribute to gpodder.net" +msgstr "תרומה ל־gpodder.net" + +#: mygpo/web/templates/contribute.html:16 +msgid "" +"gpodder.net is run as a hobby project without continuous funding for " +"servers, storage and bandwidth. We rely on contributors and donations to " +"keep the site running and improve it constantly." +msgstr "" +"gpodder.net מופעל כמיזם תחביב ללא מקור מימון מתחדש לשרתים, לאחסון ולתעבורה. " +"אנו סומכים על מתנדבים ותרומות כדי להשאיר את האתר פעיל ולשפר אותו שלב אחר שלב." + +#: mygpo/web/templates/contribute.html:18 +msgid "Work" +msgstr "עבודה" + +#: mygpo/web/templates/contribute.html:19 +msgid "" +"If you want to contribute to gpodder.net by writing code, check out the Development page. We also appreciate contributions " +"of non-coding work, such as web and interface design, server operations, " +"testing, etc. If you are interested, join the mailing list or the IRC channel " +"#gpodder on chat.freenode.net." +msgstr "" +"אם מעניין אותך להתנדב לטובת gpodder.net עם כתיבת קוד, כדאי לבקר בעמוד פיתוח. אנו גם מעריכים צורות התנדבות שאינן בצורת " +"קוד, כגון עיצוב האתר, פעולות בשרת, בדיקות וכו׳. אם כל זה מעניין אותך, " +"באפשרותך להצטרף לרשימת הדיונים או לערוץ ה־IRC בשם ‎#gpodder תחת chat.freenode.net." + +#: mygpo/web/templates/contribute.html:23 +msgid "" +"If you want to support gpodder.net financially, you can donate via PayPal to " +"stefan@skoegl.net. You can also send a book via Amazon (DE, US or UK)." +msgstr "" +"אם ברצונך לתמוך ב־gpodder.net כלכלית, ניתן לתרום דרך PayPal לטובת " +"stefan@skoegl.net. ניתן גם לשלוח ספר דרך Amazon (DE, US או UK)." + +#: mygpo/web/templates/csrf.html:8 +msgid "Attention!" +msgstr "נא לשים לב!" + +#: mygpo/web/templates/csrf.html:11 +msgid "Attention, Attention!" +msgstr "לשים לב בבקשה!" + +#: mygpo/web/templates/csrf.html:17 +#, python-format +msgid "" +"It seems that %(referer)s has linked to %(site)s to get you to change some " +"data. This will possibly change some of your data / settings!. Do you really want to continue? If in doubt, chose No." +msgstr "" +"נראה כי %(referer)s קושר אל %(site)s כדי לנסות לשכנע אותך לשנות נתונים. " +"מצב כזה עשוי לגרום לך לשנות חלק מהנתונים / הגדרות שלך!. " +"להמשיך? במקרה של ספק עדיף לבחור בלא." + +#: mygpo/web/templates/csrf.html:24 +msgid "Yes" +msgstr "כן" + +#: mygpo/web/templates/csrf.html:25 +msgid "No" +msgstr "לא" + +#: mygpo/web/templates/dashboard.html:14 mygpo/web/templatetags/menu.py:54 +msgid "Overview" +msgstr "סקירה" + +#: mygpo/web/templates/dashboard.html:17 +msgid "Hi, " +msgstr "היי," + +#: mygpo/web/templates/dashboard.html:24 +msgid "Newest Episodes" +msgstr "הפרקים החדשים ביותר" + +#: mygpo/web/templates/dashboard.html:39 +#, python-format +msgid "" +"Welcome to %(site)s! If this is your first visit, you should set up your podcast client and try to check as many Explore boxes as you " +"can." +msgstr "" +"ברוך בואך אל %(site)s! אם זה הביקור הראשון שלך, כדאי לך להגדיר את לקוח " +"הפודקאסטים שלך ולנסות לחקור כמה שיותר תיבות סיור שאפשר." + +#: mygpo/web/templates/dashboard.html:44 +#, python-format +msgid "" +"If you have problems, have a look at the docs or " +"ask questions on the mailing list or forum." +msgstr "" +"במקרה של תקלות, מוטב לעיין במסמכים או לשאול שאלות " +"ברשימת הדיונים או בפורום." + +#: mygpo/web/templates/dashboard.html:81 +#, python-format +msgid "Explore %(site)s" +msgstr "סיור ב־%(site)s" + +#: mygpo/web/templates/dashboard.html:85 +#, python-format +msgid "Sign up to %(site)s" +msgstr "הרשמה ל־%(site)s" + +#: mygpo/web/templates/dashboard.html:90 +msgid "Connect your Podcast Clients" +msgstr "חיבור לקוחות הפודקאסטים שלך" + +#: mygpo/web/templates/dashboard.html:95 +msgid "Subscribe to Podcasts" +msgstr "רישום לפודקאסטים" + +#: mygpo/web/templates/dashboard.html:100 +msgid "Mark your Favorite Episodes" +msgstr "סימון הפרקים המועדפים עליך" + +#: mygpo/web/templates/dashboard.html:105 +msgid "Share your Subscriptions" +msgstr "שיתוף המינויים שלך" + +#: mygpo/web/templates/dashboard.html:110 +msgid "Share your Favorite Episodes" +msgstr "שיתוף הפרקים המועדפים עליך" + +#: mygpo/web/templates/dashboard.html:115 +msgid "Share your Userpage" +msgstr "שיתוף עמוד המשתמש שלך" + +#: mygpo/web/templates/dashboard.html:120 +msgid "Tag Podcasts" +msgstr "תיוג פודקאסטים" + +#: mygpo/web/templates/dashboard.html:125 +msgid "Create Podcast Lists" +msgstr "יצירת רשימות פודקאסטים" + +#: mygpo/web/templates/dashboard.html:130 +msgid "Publish your own Podcast" +msgstr "הפצת פודקאסט משלך" + +#: mygpo/web/templates/dashboard.html:140 +msgid "Subscribe in Your Browser" +msgstr "רישום בדפדפן שלך" + +#: mygpo/web/templates/dashboard.html:142 +#, python-format +msgid "" +"Register %(site.domain)s as a feed reader, and subscribe to podcasts " +"directly from your browser" +msgstr "" +"ניתן לרשום את %(site.domain)s כקורא הזנות ולהירשם לפודקאסטים ישירות מהדפדפן " +"שלך." + +#: mygpo/web/templates/dashboard.html:145 +msgid "Install" +msgstr "התקנה" + +#: mygpo/web/templates/delete_account.html:14 +msgid "Delete your Account?" +msgstr "למחוק את החשבון שלך?" + +#: mygpo/web/templates/delete_account.html:20 +msgid "Do you really want to delete your account?" +msgstr "האם באמת רצונך הוא למחוק את החשבון שלך?" + +#: mygpo/web/templates/delete_account.html:23 +msgid "Yes, please" +msgstr "כן, בבקשה" + +#: mygpo/web/templates/deleted_account.html:9 +msgid "Account Deleted" +msgstr "החשבון נמחק" + +#: mygpo/web/templates/deleted_account.html:13 +msgid "Your account has been deleted" +msgstr "חשבונך נמחק" + +#: mygpo/web/templates/deleted_account.html:18 +msgid "Thanks" +msgstr "תודות" + +#: mygpo/web/templates/developer.html:8 mygpo/web/templates/developer.html:11 +#: mygpo/web/templatetags/menu.py:27 +msgid "Development" +msgstr "פיתוח" + +#: mygpo/web/templates/developer.html:17 +msgid "Webservice" +msgstr "שירות מקוון" + +#: mygpo/web/templates/developer.html:18 +msgid "" +"The sourcecode of the webservice gpodder.net is released as open source " +"under the AGPLv3 and hosted at " +"GitHub. Bugs can be reported at the gPodder Bugtracker." +msgstr "" +"קוד המקור של השירות המקוון gpodder.net מופץ לקהל הרחב כקוד פתוח תחת הרישיון " +"AGPLv3 והוא מתארח ב־GitHub. " +"ניתן לדווח על תקלות בעוקב המשימות של gPodder." + +#: mygpo/web/templates/developer.html:21 +msgid "Integrating Clients" +msgstr "שילוב לקוחות" + +#: mygpo/web/templates/developer.html:22 +msgid "" +"If you want to integrate gpodder.net in some podcasting client, you might " +"want to use one of the existing client libraries." +msgstr "" +"אם ברצונך לשלב את gpodder.net בלקוח פודקאסטים כלשהו, יכול להיות שעדיף לך " +"להשתמש באחת מספריות הלקוח הקיימות." + +#: mygpo/web/templates/developer.html:23 +msgid "" +"There are already several clients for different platforms which can be used " +"as examples. See list of clients." +msgstr "" +"כבר ישנם מגוון לקוחות לפלטפורמות שונות בהם ניתן להשתמש כדוגמאות. הצגת רשימת " +"הלקוחות." + +#: mygpo/web/templates/device-edit.html:11 mygpo/web/templates/device.html:11 +#, python-format +msgid "Device %(devicename)s" +msgstr "מכשיר %(devicename)s" + +#: mygpo/web/templates/device-edit.html:22 mygpo/web/templates/device.html:18 +msgid "This device was deleted." +msgstr "מכשיר זה נמחק." + +#: mygpo/web/templates/device-edit.html:61 +msgid "Replace Subscriptions" +msgstr "החלפת מינויים" + +#: mygpo/web/templates/device-edit.html:65 +msgid "Replace your current subscriptions by uploading an OPML file." +msgstr "ניתן להחליף את המינויים הנוכחיים שלך על ידי העלאת קובץ OPML." + +#: mygpo/web/templates/device-edit.html:70 +msgid "Upload" +msgstr "העלאה" + +#: mygpo/web/templates/device-edit.html:83 +msgid "Delete Device" +msgstr "מחיקת מכשיר" + +#: mygpo/web/templates/device-edit.html:99 +#: mygpo/web/templates/device-edit.html:117 mygpo/web/templates/device.html:102 +#: mygpo/web/templates/device.html:120 +msgid "Synchronize" +msgstr "סנכרון" + +#: mygpo/web/templates/device-edit.html:100 mygpo/web/templates/device.html:103 +msgid "" +"If you synchronize devices, they will always have the same subscriptions. A " +"podcast that is subscribed on one device, will automatically be added to all " +"synchronized devices." +msgstr "" +"סמקרה של סנכרון מכשירים, תמיד יהיו להם את אותם המינויים. פודקאסט אליו נרשמת " +"במכשיר אחד יתווסף אוטומטית לכל המכשירים המסונכרנים." + +#: mygpo/web/templates/device-edit.html:104 mygpo/web/templates/device.html:107 +#, python-format +msgid "%(devicename)s is currently not synchronized with other devices." +msgstr "%(devicename)s לא מסונכרן כרגע עם מכשירים אחרים." + +#: mygpo/web/templates/device-edit.html:108 mygpo/web/templates/device.html:111 +#, python-format +msgid "%(devicename)s is currently synchronized with %(synclist)s." +msgstr "%(devicename)s מסונכרן כעת עם %(synclist)s." + +#: mygpo/web/templates/device-edit.html:109 mygpo/web/templates/device.html:112 +#, python-format +msgid "Stop synchronisation for %(devicename)s " +msgstr "עצירת סנכרון עבור %(devicename)s" + +#: mygpo/web/templates/device.html:40 +msgid "You don't have any podcasts subscribed on this device." +msgstr "לא נרשמת לאף פודקאסט במכשיר הזה." + +#: mygpo/web/templates/device.html:63 +msgid "OPML for Nokia Podcasting on Symbian " +msgstr "OPML לפודקאסטים של נוקיה על סימביאן" + +#: mygpo/web/templates/device.html:79 +msgid "Trigger Sync" +msgstr "הפעלת סנכרון" + +#: mygpo/web/templates/device.html:87 mygpo/web/templates/devicelist.html:26 +#: mygpo/web/templates/devicelist.html:45 +msgid "Configure" +msgstr "הגדרה" + +#: mygpo/web/templates/devicelist.html:10 +#: mygpo/web/templates/devicelist.html:13 +msgid "Managed Devices" +msgstr "מכשירים מנוהלים" + +#: mygpo/web/templates/devicelist.html:31 +msgid "Synchronized" +msgstr "מסונכרן" + +#: mygpo/web/templates/devicelist.html:33 +msgid "Not Synchronized" +msgstr "לא מסונכרן" + +#: mygpo/web/templates/devicelist.html:53 +msgid "" +"You don't have any devices yet. Go to the clients page to learn how " +"to set up your client applications." +msgstr "" +"לא מוגדרים עבורך מכשירים עדיין. נא לעבור לעמוד הלקוחות כדי ללמוד איך להגדיר " +"את יישומי הלקוח שלך." + +#: mygpo/web/templates/devicelist.html:74 +msgid "Deleted Devices" +msgstr "מכשירים שנמחקו" + +#: mygpo/web/templates/devicelist.html:85 +msgid "Reactivate" +msgstr "הפעלה מחדש" + +#: mygpo/web/templates/devicelist.html:93 +msgid "Delete Permanently" +msgstr "מחיקה לצמיתות" + +#: mygpo/web/templates/favorites.html:10 mygpo/web/templates/favorites.html:13 +#: mygpo/web/templatetags/menu.py:47 mygpo/web/templatetags/menu.py:55 +msgid "Favorite Episodes" +msgstr "פרקים מועדפים" + +#: mygpo/web/templates/login.html:15 +msgid "You need to login to access this page." +msgstr "עליך להיכנס כדי לגשת לעמוד הזה." + +#: mygpo/web/templates/login.html:40 +msgid "Forgot your password?" +msgstr "שכחת את הססמה שלך?" + +#: mygpo/web/templates/maintenance.html:8 +#: mygpo/web/templates/maintenance.html:11 +msgid "Maintenance" +msgstr "תחזוקה" + +#: mygpo/web/templates/maintenance.html:15 +msgid "" +"We are currently doing some work on gpodder.net but we should back online " +"within a few minutes. Just wait a bit and try again!" +msgstr "" +"אנו מבצעים עבודות על gpodder.net אך אנו צפויים לחזור בעוד מספר דקות. עליך " +"פשוט לחכות מעט ולנסות שוב!" + +#: mygpo/web/templates/mytags.html:11 mygpo/web/templates/mytags.html:14 +#: mygpo/web/templatetags/menu.py:48 +msgid "My Tags" +msgstr "התגיות שלי" + +#: mygpo/web/templates/mytags.html:41 +#, python-format +msgid "" +"You didn't tag any podcasts yet. Go to your subscriptions and tag some." +msgstr "" +"לא תייגת אף פודקאסט עדיין. יש לגשת אל המינויים שלך ולתייג כמה מהם." + +#: mygpo/web/templates/password_reset.html:4 +msgid "Password reset" +msgstr "איפוס ססמה" + +#: mygpo/web/templates/password_reset.html:7 +msgid "Your password has been reset" +msgstr "הססמה שלך עברה איפוס" + +#: mygpo/web/templates/password_reset.html:13 +#, python-format +msgid "" +"You should have received an mail with your new password. Please log in now." +msgstr "" +"אמורה הייתה להגיע אליך הודעה עם הססמה החדשה שלך. נא להיכנס כעת." + +#: mygpo/web/templates/password_reset_failed.html:4 +msgid "Password could not be reset" +msgstr "לא ניתן לאפס את הססמה" + +#: mygpo/web/templates/password_reset_failed.html:7 +msgid "Your password could not be reset" +msgstr "לא ניתן לאפס את הססמה שלך" + +#: mygpo/web/templates/password_reset_failed.html:12 +msgid "Unfortunately we were not able to reset your password." +msgstr "לרוע המזל, לא הצלחנו לאפס לך את הססמה." + +#: mygpo/web/templates/privacy.html:9 mygpo/web/templates/privacy.html:12 +msgid "Privacy Settings" +msgstr "הגדרות פרטיות" + +#: mygpo/web/templates/privacy.html:18 +#, python-format +msgid "%(domain)s distinguishes between public and private subscriptions." +msgstr "ב־%(domain)s קיימת הבדלה בין מינויים ציבוריים לפרטיים." + +#: mygpo/web/templates/privacy.html:20 +msgid "" +"Public subscriptions are included in your shared podcasts and our anonymized public statistics." +msgstr "" +"מינויים ציבוריים נכללים בפודקאסטים " +"המשותפים שלך ובסטטיסטיקה הציבורית האלמונית שלנו." + +#: mygpo/web/templates/privacy.html:21 +msgid "" +"Private subscriptions do neither show up in your list of " +"shared podcasts, nor are considered in our statistics." +msgstr "" +"מינויים פרטיים אינם מופיעים ברשימת הפודקאסטים המשותפים שלך, " +"או בסטטיסטיקות שלנו." + +#: mygpo/web/templates/privacy.html:29 +msgid "Default for new subscriptions" +msgstr "בררת מחדל למינויים חדשים" + +#: mygpo/web/templates/privacy.html:68 +msgid "Public Subscriptions" +msgstr "מינויים ציבוריים" + +#: mygpo/web/templates/privacy.html:79 +msgid "Make Private" +msgstr "הסתרה מהציבור" + +#: mygpo/web/templates/privacy.html:90 +msgid "Private Subscriptions" +msgstr "מינויים פרטיים" + +#: mygpo/web/templates/privacy.html:101 +msgid "Make Public" +msgstr "חשיפה לציבור" + +#: mygpo/web/templates/privacy_policy.html:8 mygpo/web/templatetags/menu.py:28 +msgid "Privacy Policy" +msgstr "מדיניות פרטיות" + +#: mygpo/web/templates/restore_password.html:29 +msgid "Reset Password" +msgstr "איפוס ססמה" + +#: mygpo/web/templates/subscribe.html:7 +#, python-format +msgid "Subscribe to %(podcasttitle)s" +msgstr "הרשמה אל %(podcasttitle)s" + +#: mygpo/web/templates/subscribe.html:41 +#, fuzzy +#| msgid "" +#| "You can't subscribe to this podcast, because you don't have any devices " +#| "(on which you don't have subscribed to the podcast already." +msgid "" +"You can't subscribe to this podcast, because you don't have any devices (on " +"which you don't have subscribed to the podcast already)." +msgstr "" +"אין לך אפשרות להירשום לפודקאסט הזה כיוון שאין לך מכשירים (שבהם לא נרשמת " +"עדיין לפודקאסט)." + +#: mygpo/web/templates/subscribe.html:43 +msgid "Create Device" +msgstr "יצירת התקן" + +#: mygpo/web/templates/subscribe.html:49 +msgid "Why Unnamed Podcast?" +msgstr "למה פודקאסט ללא שם?" + +#: mygpo/web/templates/subscribe.html:49 +#, fuzzy +#| msgid "" +#| "Because we display names after we have fetched the information form the " +#| "feed -- and this may take some time. Until this is completed, the podcast " +#| "will simply be called this way." +msgid "" +"Because we display names after we have fetched the information from the feed " +"-- and this may take some time. Until this is completed, the podcast will " +"simply be called this way." +msgstr "" +"כיוון שאנו מציגים שמות לאחר אחזור המידע מההזנה -- פעולה שעשויה לארוך זמן מה. " +"עד להשלמת פעולה זו, הפודקאסט פשוט ייקרא כך." + +#: mygpo/web/templates/user_subscriptions.html:14 +#: mygpo/web/templates/user_subscriptions.html:18 +#: mygpo/web/templates/user_subscriptions_denied.html:10 +#: mygpo/web/templates/user_subscriptions_denied.html:14 +#, python-format +msgid "%(username)s's Subscriptions" +msgstr "המינויים של %(username)s" + +#: mygpo/web/templates/user_subscriptions.html:21 +#, python-format +msgid "" +"%(username)s has %(num_subscriptions)s " +"subscriptions" +msgstr "" +"ל־%(username)s יש %(num_subscriptions)s " +"מינויים" + +#: mygpo/web/templates/user_subscriptions.html:42 +msgid "Download Subscriptions as OPML" +msgstr "הורדת המינויים כ־OPML" + +#: mygpo/web/templates/user_subscriptions.html:45 +msgid "Subscribe to Changes" +msgstr "רישום לשינויים" + +#: mygpo/web/templates/user_subscriptions.html:51 +#, python-format +msgid "%(username)s doesn't have any subscriptions yet." +msgstr "ל־%(username)s אין מינויים עדיין." + +#: mygpo/web/templates/user_subscriptions.html:61 +msgid "Share Your Subscriptions" +msgstr "שיתוף המינויים שלך" + +#: mygpo/web/templates/user_subscriptions.html:62 +msgid "Want to share your own subscriptions?" +msgstr "מעניין אותך לשתף את המינויים שלך?" + +#: mygpo/web/templates/user_subscriptions.html:71 +msgid "Nokia Podcasting" +msgstr "פודקאסטים של נוקיה" + +#: mygpo/web/templates/user_subscriptions.html:72 +msgid "" +"Add &symbian=true#.opml to the OPML URL to get an OPML file for " +"Nokia Podcasting on Symbian devices." +msgstr "" +"יש להוסיף &symbian=true#.opml לכתובת ה־OPML כדי לקבל קובץ OPML " +"עבור פודקאסטים של נוקיה על מכשירי סימביאן." + +#: mygpo/web/templates/user_subscriptions_denied.html:20 +#, python-format +msgid "" +"%(username)s didn't share his subscriptions with you. Maybe you should " +"contact him to get the correct link." +msgstr "" +"המינויים של %(username)s לא שותפו אתך. אולי עדיף ליצור קשר עם המשתמש כדי " +"לקבל את הקישור הנכון." + +#: mygpo/web/templates/user_subscriptions_denied.html:23 +msgid "Want to share your subscriptions?" +msgstr "מעניין אותך לשתף את המינויים שלך?" + +#: mygpo/web/templates/user_subscriptions_denied.html:23 +msgid "" +"Just go to your account settings to get the link." +msgstr "" +"עליך פשוט לגשת אל הגדרות החשבון כדי לקבל את הקישור." +"" + +#: mygpo/web/templatetags/devices.py:32 +msgid "Unknown" +msgstr "לא ידוע" + +#: mygpo/web/templatetags/episodes.py:20 +msgid "New episode" +msgstr "פרק חדש" + +#: mygpo/web/templatetags/episodes.py:23 +#, python-format +msgid "Downloaded to %s" +msgstr "התקבל אל %s" + +#: mygpo/web/templatetags/episodes.py:25 +msgid "Downloaded" +msgstr "התקבל" + +#: mygpo/web/templatetags/episodes.py:28 +#, python-format +msgid "Played on %s" +msgstr "התנגן ב־%s" + +#: mygpo/web/templatetags/episodes.py:30 +msgid "Played" +msgstr "התנגן" + +#: mygpo/web/templatetags/episodes.py:33 +#, python-format +msgid "Deleted on %s" +msgstr "נמחק ב־%s" + +#: mygpo/web/templatetags/episodes.py:35 +msgid "Deleted" +msgstr "נמחק" + +#: mygpo/web/templatetags/episodes.py:37 +msgid "Unknown status" +msgstr "מצב לא ידוע" + +#: mygpo/web/templatetags/episodes.py:43 +msgid "Unplayed episode" +msgstr "פרק שלא התנגן" + +#: mygpo/web/templatetags/episodes.py:46 mygpo/web/templatetags/episodes.py:47 +#, python-format +msgid " on %s" +msgstr "ב־%s" + +#: mygpo/web/templatetags/episodes.py:50 +msgid "The episode has been flattr'd" +msgstr "הפרק קיבל flattr" + +#: mygpo/web/templatetags/episodes.py:53 +msgid "This episode has been marked new" +msgstr "הפרק סומן כחדש" + +#: mygpo/web/templatetags/episodes.py:55 +msgid "This episode has been downloaded" +msgstr "הפרק התקבל" + +#: mygpo/web/templatetags/episodes.py:59 +#, python-format +msgid " from %(start)s to %(end)s" +msgstr " מ־%(start)s עד %(end)s" + +#: mygpo/web/templatetags/episodes.py:63 +#, python-format +msgid " to position %s" +msgstr "למיקום %s" + +#: mygpo/web/templatetags/episodes.py:67 +msgid "This episode has been played" +msgstr "הפרק התנגן" + +#: mygpo/web/templatetags/episodes.py:69 +msgid "This episode has been deleted" +msgstr "הפרק נמחק" + +#: mygpo/web/templatetags/episodes.py:126 +msgid "Unknown Episode" +msgstr "פרק לא ידוע" + +#: mygpo/web/templatetags/menu.py:22 mygpo/web/templatetags/menu.py:65 +msgid "Home" +msgstr "בית" + +#: mygpo/web/templatetags/menu.py:29 +msgid "Help" +msgstr "עזרה" + +#: mygpo/web/templatetags/menu.py:37 +msgid "User subscriptions" +msgstr "מינויי המשתמש" + +#: mygpo/web/templatetags/menu.py:38 +msgid "Suggestions" +msgstr "הצעות" + +#: mygpo/web/templatetags/menu.py:39 +msgid "Features" +msgstr "תכונות" + +#: mygpo/web/templatetags/menu.py:41 +msgid "Toplists" +msgstr "רשימות מובילות" + +#: mygpo/web/templatetags/menu.py:49 +msgid "Devices" +msgstr "התקנים" + +#: mygpo/web/templatetags/menu.py:53 +msgid "Community" +msgstr "קהילה" + +#: mygpo/web/templatetags/menu.py:56 +msgid "My Userpage" +msgstr "עמוד המשתמש שלי" + +#: mygpo/web/templatetags/menu.py:62 +msgid "Privacy" +msgstr "פרטיות" + +#: mygpo/web/templatetags/menu.py:67 +msgid "Link to gpodder.net" +msgstr "מעבר אל gpodder.net" + +#: mygpo/web/templatetags/time.py:42 +#, python-brace-format +msgid "{h}h {m}m {s}s" +msgstr "{h} שע׳ {m} דק׳ {s} שנ׳" + +#: mygpo/web/templatetags/time.py:44 +#, fuzzy, python-brace-format +#| msgid "{h}h {m}m {s}s" +msgid "{m}m {s}s" +msgstr "{h} שע׳ {m} דק׳ {s} שנ׳" + +#: mygpo/web/utils.py:281 +#, python-format +msgid "%(weeks)d week" +msgid_plural "%(weeks)d weeks" +msgstr[0] "שבוע אחד" +msgstr[1] "שבועיים" +msgstr[2] "%(weeks)d שבועות" +msgstr[3] "%(weeks)d שבועות" + +#: mygpo/web/utils.py:285 +#, python-format +msgid "%(days)d day" +msgid_plural "%(days)d days" +msgstr[0] "יום אחד" +msgstr[1] "יומיים" +msgstr[2] "%(days)d ימים" +msgstr[3] "%(days)d ימים" + +#: mygpo/web/utils.py:289 +#, python-format +msgid "%(hours)d hour" +msgid_plural "%(hours)d hours" +msgstr[0] "שעה אחת" +msgstr[1] "שעתיים" +msgstr[2] "%(hours)d שעות" +msgstr[3] "%(hours)d שעות" + +#: mygpo/web/views.py:145 +msgid "another site" +msgstr "אתר אחר" + +#~ msgid "Timing" +#~ msgstr "תזמון" + +#~ msgid "Last update:" +#~ msgstr "עדכון אחרון:" + +#~ msgid "Next update:" +#~ msgstr "העדכון הבא:" diff --git a/mygpo/locale/it/LC_MESSAGES/django.po b/mygpo/locale/it/LC_MESSAGES/django.po index 5d28862a6..5f04d645d 100644 --- a/mygpo/locale/it/LC_MESSAGES/django.po +++ b/mygpo/locale/it/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-10-12 18:58+0000\n" +"POT-Creation-Date: 2018-06-29 20:11+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -101,46 +101,50 @@ msgstr "Tipo" msgid "Episode URLs" msgstr "Podcasts" -#: mygpo/administration/templates/admin/hostinfo.html:9 -#: mygpo/administration/templates/admin/hostinfo.html:12 +#: mygpo/administration/templates/admin/hostinfo.html:10 +#: mygpo/administration/templates/admin/hostinfo.html:13 #: mygpo/administration/templates/admin/overview.html:17 msgid "Host Information" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:20 +#: mygpo/administration/templates/admin/hostinfo.html:21 msgid "mygpo Version" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:31 +#: mygpo/administration/templates/admin/hostinfo.html:32 #, fuzzy msgid "Base Directory" msgstr "Corso temporale" -#: mygpo/administration/templates/admin/hostinfo.html:36 +#: mygpo/administration/templates/admin/hostinfo.html:37 msgid "Hostname" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:41 +#: mygpo/administration/templates/admin/hostinfo.html:42 msgid "Django Version" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:46 +#: mygpo/administration/templates/admin/hostinfo.html:47 msgid "Feed Update Queue" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:49 +#: mygpo/administration/templates/admin/hostinfo.html:50 msgid "min ahead" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:51 +#: mygpo/administration/templates/admin/hostinfo.html:52 msgid "min behind" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:59 +#: mygpo/administration/templates/admin/hostinfo.html:60 msgid "Number of podcasts with outdated search index" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:66 +#: mygpo/administration/templates/admin/hostinfo.html:69 +msgid "Average podcast update duration" +msgstr "" + +#: mygpo/administration/templates/admin/hostinfo.html:76 msgid "Scheduled Celery Tasks" msgstr "" @@ -148,7 +152,7 @@ msgstr "" #: mygpo/administration/templates/admin/make-publisher-input.html:12 #: mygpo/administration/templates/admin/make-publisher-result.html:9 #: mygpo/administration/templates/admin/make-publisher-result.html:12 -#: mygpo/administration/views.py:391 +#: mygpo/administration/views.py:404 msgid "Publisher Permissions" msgstr "" @@ -263,21 +267,21 @@ msgstr "" msgid "User-Agent" msgstr "" -#: mygpo/administration/views.py:140 mygpo/administration/views.py:162 +#: mygpo/administration/views.py:153 mygpo/administration/views.py:175 #, python-brace-format msgid "No podcast with URL {url}" msgstr "" -#: mygpo/administration/views.py:316 +#: mygpo/administration/views.py:329 msgid "Provide either username or email address" msgstr "" -#: mygpo/administration/views.py:322 +#: mygpo/administration/views.py:335 #, fuzzy msgid "No user found" msgstr "Non trovato" -#: mygpo/administration/views.py:327 +#: mygpo/administration/views.py:340 #, python-brace-format msgid "User {username} ({email}) activated" msgstr "" @@ -317,16 +321,16 @@ msgstr "Disdire" msgid "%(username)s's Subscription List" msgstr "I tuoi abbonamenti" -#: mygpo/api/simple.py:237 +#: mygpo/api/simple.py:240 #, fuzzy, python-format msgid "gpodder.net - Top %(count)d" msgstr "my.gpodder.org - Top %s" -#: mygpo/api/simple.py:272 +#: mygpo/api/simple.py:275 msgid "gpodder.net - Search" msgstr "" -#: mygpo/api/simple.py:289 +#: mygpo/api/simple.py:292 #, fuzzy, python-format msgid "gpodder.net - %(count)d Suggestions" msgstr "my.gpodder.org - %s suggerimenti" @@ -349,7 +353,7 @@ msgid "Explore" msgstr "" #: mygpo/directory/templates/carousel.html:81 -#: mygpo/web/templates/episode.html:150 +#: mygpo/podcasts/templates/episode.html:150 msgid "..." msgstr "" @@ -480,11 +484,11 @@ msgid "Listeners" msgstr "" #: mygpo/directory/templates/episode_toplist.html:36 +#: mygpo/history/templates/episode-history.html:51 +#: mygpo/podcasts/templates/episode.html:51 #: mygpo/publisher/templates/publisher/episode.html:33 #: mygpo/share/templates/userpage.html:118 #: mygpo/share/templates/userpage.html:138 -#: mygpo/web/templates/episode-history.html:50 -#: mygpo/web/templates/episode.html:51 msgid "from" msgstr "" @@ -557,9 +561,9 @@ msgid "User" msgstr "" #: mygpo/directory/templates/podcast_lists.html:25 +#: mygpo/history/templates/episode-history.html:53 +#: mygpo/podcasts/templates/episode.html:53 #: mygpo/publisher/templates/publisher/episode.html:35 -#: mygpo/web/templates/episode-history.html:52 -#: mygpo/web/templates/episode.html:53 msgid "Download" msgstr "" @@ -618,10 +622,49 @@ msgstr "" msgid "%d podcasts added" msgstr "Podcasts" +#: mygpo/history/templates/episode-history.html:47 +#: mygpo/history/templates/episode-history.html:81 +#: mygpo/web/templates/device.html:73 mygpo/web/templatetags/menu.py:51 +msgid "History" +msgstr "Corso temporale" + +#: mygpo/history/templates/episode-history.html:58 +#: mygpo/podcasts/templates/episode.html:58 +#: mygpo/podcasts/templates/podcast-base.html:54 +#: mygpo/publisher/templates/publisher/episode.html:40 +#: mygpo/publisher/templates/publisher/podcast.html:39 +#, fuzzy +msgid "Website" +msgstr "Podcasts" + +#: mygpo/history/templates/episode-history.html:63 +#: mygpo/podcasts/templates/episode.html:63 +#: mygpo/publisher/templates/publisher/episode.html:45 +msgid "listeners" +msgstr "" + +#: mygpo/history/templates/episode-history.html:84 +#, fuzzy +msgid "Time" +msgstr "Titolo" + +#: mygpo/history/templates/episode-history.html:85 +msgid "Action" +msgstr "Azione" + +#: mygpo/history/templates/episode-history.html:86 +#: mygpo/web/templatetags/menu.py:50 +msgid "Device" +msgstr "Strumento tecnico" + +#: mygpo/history/templates/episode-history.html:127 +msgid "Add" +msgstr "" + #: mygpo/history/templates/history.html:12 #: mygpo/history/templates/history.html:15 -#: mygpo/web/templates/podcast-history.html:32 -#: mygpo/web/templates/podcast.html:143 +#: mygpo/history/templates/podcast-history.html:32 +#: mygpo/podcasts/templates/podcast.html:143 #, fuzzy msgid "Subscription History" msgstr "Corso degli abbonamenti" @@ -655,6 +698,10 @@ msgstr "" msgid "Earlier" msgstr "" +#: mygpo/history/templates/podcast-history.html:47 +msgid "no history yet" +msgstr "" + #: mygpo/podcastlists/templates/list.html:28 #, python-format msgid "\"%(list_title)s\" by %(ownername)s" @@ -750,16 +797,100 @@ msgstr "" msgid "Edit" msgstr "" -#: mygpo/podcasts/models.py:688 +#: mygpo/podcasts/models.py:704 #, fuzzy msgid "Unknown Podcast" msgstr "Podcasts" -#: mygpo/podcasts/models.py:690 +#: mygpo/podcasts/models.py:706 #, python-brace-format msgid "Unknown Podcast from {domain}" msgstr "" +#: mygpo/podcasts/templates/episode.html:85 +#: mygpo/podcasts/templates/episodes.html:27 +#: mygpo/podcasts/templates/podcast.html:39 +#: mygpo/publisher/templates/publisher/episode.html:31 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/info.html:9 +#: mygpo/publisher/templates/publisher/info.html:26 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Publisher Pages" +msgstr "" + +#: mygpo/podcasts/templates/episode.html:91 +msgid "Remove Favorite" +msgstr "" + +#: mygpo/podcasts/templates/episode.html:95 +msgid "Favorite" +msgstr "" + +#: mygpo/podcasts/templates/episode.html:103 +#, fuzzy +#| msgid "Your Episode History" +msgid "Episode History" +msgstr "Il tuo corso episodico." + +#: mygpo/podcasts/templates/podcast-base.html:39 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Unnamed Podcast" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:44 +#: mygpo/publisher/templates/publisher/podcast.html:29 +msgid "by" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:50 +#: mygpo/publisher/templates/publisher/podcast.html:35 +#: mygpo/share/templates/share/favorites.html:29 +msgid "Feed" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:65 +#: mygpo/publisher/templates/publisher/podcast.html:44 +#, fuzzy +msgid "subscribers" +msgstr "abboni" + +#: mygpo/podcasts/templates/podcast.html:48 +#, fuzzy +msgid "Login to Subscribe" +msgstr "abboni" + +#: mygpo/podcasts/templates/podcast.html:59 +#: mygpo/suggestions/templates/suggestions.html:31 +#: mygpo/web/templates/subscribe.html:11 mygpo/web/templates/subscribe.html:36 +#, fuzzy +msgid "Subscribe" +msgstr "abboni" + +#: mygpo/podcasts/templates/podcast.html:67 mygpo/web/templates/device.html:31 +msgid "Unsubscribe" +msgstr "Disdire" + +#: mygpo/podcasts/templates/podcast.html:78 +#, fuzzy +msgid "Subscribe on all devices" +msgstr "Abbonato su" + +#: mygpo/podcasts/templates/podcast.html:110 +#, fuzzy +msgid "Unsubscribe from all devices " +msgstr "Abbonato su" + +#: mygpo/podcasts/templates/podcast.html:149 +#: mygpo/podcasts/templates/podcast.html:164 +msgid "Tags" +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:199 +#, fuzzy +msgid "Older Episodes" +msgstr "Ultimo episodio" + #: mygpo/publisher/forms.py:5 #: mygpo/publisher/templates/publisher/episode.html:60 msgid "URL" @@ -775,7 +906,7 @@ msgid "Link to" msgstr "" #: mygpo/publisher/templates/link.html:17 -#: mygpo/publisher/templates/publisher/podcast.html:171 +#: mygpo/publisher/templates/publisher/podcast.html:209 #, fuzzy, python-format msgid "" "You can paste this code on your website, so users of %(sitename)s can " @@ -869,30 +1000,6 @@ msgstr "" msgid "Unnamed Episode" msgstr "Perchè episodi non nominati?" -#: mygpo/publisher/templates/publisher/episode.html:31 -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/info.html:9 -#: mygpo/publisher/templates/publisher/info.html:26 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/episode.html:85 mygpo/web/templates/episodes.html:27 -#: mygpo/web/templates/podcast.html:39 -msgid "Publisher Pages" -msgstr "" - -#: mygpo/publisher/templates/publisher/episode.html:40 -#: mygpo/publisher/templates/publisher/podcast.html:38 -#: mygpo/web/templates/episode-history.html:57 -#: mygpo/web/templates/episode.html:58 mygpo/web/templates/podcast-base.html:54 -#, fuzzy -msgid "Website" -msgstr "Podcasts" - -#: mygpo/publisher/templates/publisher/episode.html:45 -#: mygpo/web/templates/episode-history.html:62 -#: mygpo/web/templates/episode.html:63 -msgid "listeners" -msgstr "" - #: mygpo/publisher/templates/publisher/episode.html:55 #, fuzzy msgid "Episode List" @@ -910,7 +1017,7 @@ msgid "" msgstr "" #: mygpo/publisher/templates/publisher/episode.html:72 -#: mygpo/publisher/templates/publisher/podcast.html:91 +#: mygpo/publisher/templates/publisher/podcast.html:129 #: mygpo/web/templates/account.html:65 mygpo/web/templates/account.html:142 #: mygpo/web/templates/device-edit.html:53 msgid "Save" @@ -925,12 +1032,6 @@ msgstr "Ultimo episodio" msgid "Last update: " msgstr "" -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/podcast-base.html:39 -msgid "Unnamed Podcast" -msgstr "" - #: mygpo/publisher/templates/publisher/episodes.html:24 msgid "Return to Podcast Page" msgstr "" @@ -1068,75 +1169,57 @@ msgid "" "podcast - especially for users of mobile phones." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:28 -#: mygpo/web/templates/podcast-base.html:44 -msgid "by" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:34 -#: mygpo/share/templates/share/favorites.html:29 -#: mygpo/web/templates/podcast-base.html:50 -msgid "Feed" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:43 -#: mygpo/web/templates/podcast-base.html:65 -#, fuzzy -msgid "subscribers" -msgstr "abboni" - -#: mygpo/publisher/templates/publisher/podcast.html:48 +#: mygpo/publisher/templates/publisher/podcast.html:49 #, python-format msgid "" "This is the publisher page of %(ptitle)s. You can see some " "stats and provide additional data for the podcast page." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:49 +#: mygpo/publisher/templates/publisher/podcast.html:50 #, fuzzy msgid "Go to Podcast Page" msgstr "Podcast" -#: mygpo/publisher/templates/publisher/podcast.html:56 +#: mygpo/publisher/templates/publisher/podcast.html:57 msgid "The podcast information is regularly retrieved from the podcast feed" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:59 -msgid "Timing" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:61 +#: mygpo/publisher/templates/publisher/podcast.html:60 #, fuzzy -msgid "Last update:" -msgstr "Lista strumenti tecnici" +msgid "Updates" +msgstr "Podcast" #: mygpo/publisher/templates/publisher/podcast.html:62 msgid "Update interval:" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:63 -#, fuzzy -msgid "Next update:" -msgstr "Lista strumenti tecnici" +#: mygpo/publisher/templates/publisher/podcast.html:86 +msgid "Successful" +msgstr "" + +#: mygpo/publisher/templates/publisher/podcast.html:88 +msgid "Error" +msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:68 +#: mygpo/publisher/templates/publisher/podcast.html:106 msgid "Update now" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:79 mygpo/web/forms.py:70 +#: mygpo/publisher/templates/publisher/podcast.html:117 mygpo/web/forms.py:70 #: mygpo/web/templates/base.html:160 mygpo/web/templates/home.html:180 msgid "Twitter" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:97 +#: mygpo/publisher/templates/publisher/podcast.html:135 msgid "Feed Check" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:105 +#: mygpo/publisher/templates/publisher/podcast.html:143 msgid "PubSubHubbub" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:114 +#: mygpo/publisher/templates/publisher/podcast.html:152 #, python-format msgid "" "If you publish your podcast feed through a %(hub)s and should " "update immediatelly for each new episode." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:123 +#: mygpo/publisher/templates/publisher/podcast.html:161 #, python-format msgid "" "Your podcast is published through %(hub)s but our " "subscription has not yet been verified." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:128 +#: mygpo/publisher/templates/publisher/podcast.html:166 #, python-format msgid "" "We did not find a hub in your podcast feed. Your feed is updated regularly, " "but there might be some delay until a new episode shows up on %(sitename)s." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:140 +#: mygpo/publisher/templates/publisher/podcast.html:178 msgid "License Information" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:148 +#: mygpo/publisher/templates/publisher/podcast.html:186 #, python-format msgid "" "You should include license information in your feed so that users and " "%(sitename)s can know, under which conditions your content can be used." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:152 +#: mygpo/publisher/templates/publisher/podcast.html:190 #, python-format msgid "" "We found the following license in your podcast: " "%(license)s" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:156 +#: mygpo/publisher/templates/publisher/podcast.html:194 msgid "" "We did not find a license in your podcast feed. Refer to " msgstr "" -#: mygpo/web/templatetags/devices.py:31 +#: mygpo/web/templatetags/devices.py:32 msgid "Unknown" msgstr "" @@ -2644,11 +2654,16 @@ msgstr "" msgid "Link to gpodder.net" msgstr "" -#: mygpo/web/templatetags/time.py:38 +#: mygpo/web/templatetags/time.py:42 #, python-brace-format msgid "{h}h {m}m {s}s" msgstr "" +#: mygpo/web/templatetags/time.py:44 +#, python-brace-format +msgid "{m}m {s}s" +msgstr "" + #: mygpo/web/utils.py:281 #, python-format msgid "%(weeks)d week" @@ -2674,6 +2689,14 @@ msgstr[1] "" msgid "another site" msgstr "" +#, fuzzy +#~ msgid "Last update:" +#~ msgstr "Lista strumenti tecnici" + +#, fuzzy +#~ msgid "Next update:" +#~ msgstr "Lista strumenti tecnici" + #, fuzzy #~ msgid "You have to login to Flattr to use the Flattr features." #~ msgstr "Ti sei abbonato i podcats seguenti sul tuo strumento tecnico." diff --git a/mygpo/locale/ko/LC_MESSAGES/django.po b/mygpo/locale/ko/LC_MESSAGES/django.po index 94bda2421..dce4ac011 100644 --- a/mygpo/locale/ko/LC_MESSAGES/django.po +++ b/mygpo/locale/ko/LC_MESSAGES/django.po @@ -5,7 +5,7 @@ msgid "" msgstr "" "Project-Id-Version: mygpo\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-10-12 18:58+0000\n" +"POT-Creation-Date: 2018-06-29 20:11+0000\n" "PO-Revision-Date: 2017-07-06 17:10+0900\n" "Last-Translator: Changwoo Ryu \n" "Language-Team: Changwoo Ryu \n" @@ -100,47 +100,51 @@ msgstr "파일 종류" msgid "Episode URLs" msgstr "에피소드 URL" -#: mygpo/administration/templates/admin/hostinfo.html:9 -#: mygpo/administration/templates/admin/hostinfo.html:12 +#: mygpo/administration/templates/admin/hostinfo.html:10 +#: mygpo/administration/templates/admin/hostinfo.html:13 #: mygpo/administration/templates/admin/overview.html:17 msgid "Host Information" msgstr "호스트 정보" -#: mygpo/administration/templates/admin/hostinfo.html:20 +#: mygpo/administration/templates/admin/hostinfo.html:21 msgid "mygpo Version" msgstr "mygpo 버전" -#: mygpo/administration/templates/admin/hostinfo.html:31 +#: mygpo/administration/templates/admin/hostinfo.html:32 msgid "Base Directory" msgstr "베이스 디렉터리" -#: mygpo/administration/templates/admin/hostinfo.html:36 +#: mygpo/administration/templates/admin/hostinfo.html:37 msgid "Hostname" msgstr "호스트 이름" -#: mygpo/administration/templates/admin/hostinfo.html:41 +#: mygpo/administration/templates/admin/hostinfo.html:42 msgid "Django Version" msgstr "Django 버전" -#: mygpo/administration/templates/admin/hostinfo.html:46 +#: mygpo/administration/templates/admin/hostinfo.html:47 msgid "Feed Update Queue" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:49 +#: mygpo/administration/templates/admin/hostinfo.html:50 #, fuzzy #| msgid "Admin Area" msgid "min ahead" msgstr "관리 영역" -#: mygpo/administration/templates/admin/hostinfo.html:51 +#: mygpo/administration/templates/admin/hostinfo.html:52 msgid "min behind" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:59 +#: mygpo/administration/templates/admin/hostinfo.html:60 msgid "Number of podcasts with outdated search index" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:66 +#: mygpo/administration/templates/admin/hostinfo.html:69 +msgid "Average podcast update duration" +msgstr "" + +#: mygpo/administration/templates/admin/hostinfo.html:76 msgid "Scheduled Celery Tasks" msgstr "예정된 Celery 작업" @@ -148,7 +152,7 @@ msgstr "예정된 Celery 작업" #: mygpo/administration/templates/admin/make-publisher-input.html:12 #: mygpo/administration/templates/admin/make-publisher-result.html:9 #: mygpo/administration/templates/admin/make-publisher-result.html:12 -#: mygpo/administration/views.py:391 +#: mygpo/administration/views.py:404 #, fuzzy #| msgid "Publisher Pages" msgid "Publisher Permissions" @@ -272,22 +276,22 @@ msgstr "새로 고침" msgid "User-Agent" msgstr "User-Agent" -#: mygpo/administration/views.py:140 mygpo/administration/views.py:162 +#: mygpo/administration/views.py:153 mygpo/administration/views.py:175 #, python-brace-format msgid "No podcast with URL {url}" msgstr "{url} URL의 팟캐스트 없음" -#: mygpo/administration/views.py:316 +#: mygpo/administration/views.py:329 msgid "Provide either username or email address" msgstr "" -#: mygpo/administration/views.py:322 +#: mygpo/administration/views.py:335 #, fuzzy #| msgid "Nothing found" msgid "No user found" msgstr "검색 결과가 없습니다" -#: mygpo/administration/views.py:327 +#: mygpo/administration/views.py:340 #, python-brace-format msgid "User {username} ({email}) activated" msgstr "" @@ -325,16 +329,16 @@ msgstr "구독 해지함" msgid "%(username)s's Subscription List" msgstr "%(username)s의 구독 목록" -#: mygpo/api/simple.py:237 +#: mygpo/api/simple.py:240 #, python-format msgid "gpodder.net - Top %(count)d" msgstr "gpodder.net - 톱 %(count)d" -#: mygpo/api/simple.py:272 +#: mygpo/api/simple.py:275 msgid "gpodder.net - Search" msgstr "gpodder.net - 검색" -#: mygpo/api/simple.py:289 +#: mygpo/api/simple.py:292 #, python-format msgid "gpodder.net - %(count)d Suggestions" msgstr "gpodder.net - 제안 %(count)d개" @@ -357,7 +361,7 @@ msgid "Explore" msgstr "둘러보기" #: mygpo/directory/templates/carousel.html:81 -#: mygpo/web/templates/episode.html:150 +#: mygpo/podcasts/templates/episode.html:150 msgid "..." msgstr "..." @@ -485,11 +489,11 @@ msgid "Listeners" msgstr "청취자" #: mygpo/directory/templates/episode_toplist.html:36 +#: mygpo/history/templates/episode-history.html:51 +#: mygpo/podcasts/templates/episode.html:51 #: mygpo/publisher/templates/publisher/episode.html:33 #: mygpo/share/templates/userpage.html:118 #: mygpo/share/templates/userpage.html:138 -#: mygpo/web/templates/episode-history.html:50 -#: mygpo/web/templates/episode.html:51 msgid "from" msgstr "위치" @@ -559,9 +563,9 @@ msgid "User" msgstr "사용자" #: mygpo/directory/templates/podcast_lists.html:25 +#: mygpo/history/templates/episode-history.html:53 +#: mygpo/podcasts/templates/episode.html:53 #: mygpo/publisher/templates/publisher/episode.html:35 -#: mygpo/web/templates/episode-history.html:52 -#: mygpo/web/templates/episode.html:53 msgid "Download" msgstr "다운로드" @@ -618,10 +622,47 @@ msgstr "클라이언트 접근" msgid "%d podcasts added" msgstr "팟캐스트 %d개 추가함" +#: mygpo/history/templates/episode-history.html:47 +#: mygpo/history/templates/episode-history.html:81 +#: mygpo/web/templates/device.html:73 mygpo/web/templatetags/menu.py:51 +msgid "History" +msgstr "기록" + +#: mygpo/history/templates/episode-history.html:58 +#: mygpo/podcasts/templates/episode.html:58 +#: mygpo/podcasts/templates/podcast-base.html:54 +#: mygpo/publisher/templates/publisher/episode.html:40 +#: mygpo/publisher/templates/publisher/podcast.html:39 +msgid "Website" +msgstr "웹사이트" + +#: mygpo/history/templates/episode-history.html:63 +#: mygpo/podcasts/templates/episode.html:63 +#: mygpo/publisher/templates/publisher/episode.html:45 +msgid "listeners" +msgstr "청취자" + +#: mygpo/history/templates/episode-history.html:84 +msgid "Time" +msgstr "시간" + +#: mygpo/history/templates/episode-history.html:85 +msgid "Action" +msgstr "동작" + +#: mygpo/history/templates/episode-history.html:86 +#: mygpo/web/templatetags/menu.py:50 +msgid "Device" +msgstr "장치" + +#: mygpo/history/templates/episode-history.html:127 +msgid "Add" +msgstr "추가" + #: mygpo/history/templates/history.html:12 #: mygpo/history/templates/history.html:15 -#: mygpo/web/templates/podcast-history.html:32 -#: mygpo/web/templates/podcast.html:143 +#: mygpo/history/templates/podcast-history.html:32 +#: mygpo/podcasts/templates/podcast.html:143 msgid "Subscription History" msgstr "구독 기록" @@ -655,6 +696,10 @@ msgstr "지금" msgid "Earlier" msgstr "더 예전" +#: mygpo/history/templates/podcast-history.html:47 +msgid "no history yet" +msgstr "" + #: mygpo/podcastlists/templates/list.html:28 #, python-format msgid "\"%(list_title)s\" by %(ownername)s" @@ -748,17 +793,103 @@ msgstr "평가해 주셔서 감사합니다!" msgid "Edit" msgstr "편집" -#: mygpo/podcasts/models.py:688 +#: mygpo/podcasts/models.py:704 #, fuzzy #| msgid "Unnamed Podcast" msgid "Unknown Podcast" msgstr "이름 없는 팟캐스트" -#: mygpo/podcasts/models.py:690 +#: mygpo/podcasts/models.py:706 #, python-brace-format msgid "Unknown Podcast from {domain}" msgstr "" +#: mygpo/podcasts/templates/episode.html:85 +#: mygpo/podcasts/templates/episodes.html:27 +#: mygpo/podcasts/templates/podcast.html:39 +#: mygpo/publisher/templates/publisher/episode.html:31 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/info.html:9 +#: mygpo/publisher/templates/publisher/info.html:26 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Publisher Pages" +msgstr "제작자 페이지" + +#: mygpo/podcasts/templates/episode.html:91 +msgid "Remove Favorite" +msgstr "즐겨 듣기 삭제" + +#: mygpo/podcasts/templates/episode.html:95 +msgid "Favorite" +msgstr "즐겨 듣기" + +#: mygpo/podcasts/templates/episode.html:103 +#, fuzzy +#| msgid "Episodes" +msgid "Episode History" +msgstr "에피소드" + +#: mygpo/podcasts/templates/podcast-base.html:39 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Unnamed Podcast" +msgstr "이름 없는 팟캐스트" + +#: mygpo/podcasts/templates/podcast-base.html:44 +#: mygpo/publisher/templates/publisher/podcast.html:29 +msgid "by" +msgstr "by" + +#: mygpo/podcasts/templates/podcast-base.html:50 +#: mygpo/publisher/templates/publisher/podcast.html:35 +#: mygpo/share/templates/share/favorites.html:29 +msgid "Feed" +msgstr "피드" + +#: mygpo/podcasts/templates/podcast-base.html:65 +#: mygpo/publisher/templates/publisher/podcast.html:44 +msgid "subscribers" +msgstr "구독자" + +#: mygpo/podcasts/templates/podcast.html:48 +#, fuzzy +#| msgid "Subscribe" +msgid "Login to Subscribe" +msgstr "구독" + +#: mygpo/podcasts/templates/podcast.html:59 +#: mygpo/suggestions/templates/suggestions.html:31 +#: mygpo/web/templates/subscribe.html:11 mygpo/web/templates/subscribe.html:36 +msgid "Subscribe" +msgstr "구독" + +#: mygpo/podcasts/templates/podcast.html:67 mygpo/web/templates/device.html:31 +msgid "Unsubscribe" +msgstr "구독 해제" + +#: mygpo/podcasts/templates/podcast.html:78 +#, fuzzy +#| msgid "Subscribe on devices" +msgid "Subscribe on all devices" +msgstr "장치에서 구독" + +#: mygpo/podcasts/templates/podcast.html:110 +#, fuzzy +#| msgid "Subscribe on devices" +msgid "Unsubscribe from all devices " +msgstr "장치에서 구독" + +#: mygpo/podcasts/templates/podcast.html:149 +#: mygpo/podcasts/templates/podcast.html:164 +#, fuzzy +#| msgid "My Tags" +msgid "Tags" +msgstr "내 태그" + +#: mygpo/podcasts/templates/podcast.html:199 +msgid "Older Episodes" +msgstr "더 예전 에피소드" + #: mygpo/publisher/forms.py:5 #: mygpo/publisher/templates/publisher/episode.html:60 msgid "URL" @@ -773,7 +904,7 @@ msgid "Link to" msgstr "연결하기" #: mygpo/publisher/templates/link.html:17 -#: mygpo/publisher/templates/publisher/podcast.html:171 +#: mygpo/publisher/templates/publisher/podcast.html:209 #, python-format msgid "" "You can paste this code on your website, so users of %(sitename)s can " @@ -878,29 +1009,6 @@ msgstr "" msgid "Unnamed Episode" msgstr "이름 없는 에피소드" -#: mygpo/publisher/templates/publisher/episode.html:31 -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/info.html:9 -#: mygpo/publisher/templates/publisher/info.html:26 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/episode.html:85 mygpo/web/templates/episodes.html:27 -#: mygpo/web/templates/podcast.html:39 -msgid "Publisher Pages" -msgstr "제작자 페이지" - -#: mygpo/publisher/templates/publisher/episode.html:40 -#: mygpo/publisher/templates/publisher/podcast.html:38 -#: mygpo/web/templates/episode-history.html:57 -#: mygpo/web/templates/episode.html:58 mygpo/web/templates/podcast-base.html:54 -msgid "Website" -msgstr "웹사이트" - -#: mygpo/publisher/templates/publisher/episode.html:45 -#: mygpo/web/templates/episode-history.html:62 -#: mygpo/web/templates/episode.html:63 -msgid "listeners" -msgstr "청취자" - #: mygpo/publisher/templates/publisher/episode.html:55 #, fuzzy #| msgid "Episode URLs" @@ -922,7 +1030,7 @@ msgstr "" "고 현재 URL로 리다이렉트됩니다." #: mygpo/publisher/templates/publisher/episode.html:72 -#: mygpo/publisher/templates/publisher/podcast.html:91 +#: mygpo/publisher/templates/publisher/podcast.html:129 #: mygpo/web/templates/account.html:65 mygpo/web/templates/account.html:142 #: mygpo/web/templates/device-edit.html:53 msgid "Save" @@ -936,12 +1044,6 @@ msgstr "에피소드 데이터" msgid "Last update: " msgstr "최근 업데이트: " -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/podcast-base.html:39 -msgid "Unnamed Podcast" -msgstr "이름 없는 팟캐스트" - #: mygpo/publisher/templates/publisher/episodes.html:24 msgid "Return to Podcast Page" msgstr "팟캐스트 페이지로 돌아가기" @@ -1105,23 +1207,7 @@ msgstr "" "면, 웹 방문자가 팟캐스트에 아주 쉽게 구독할 수 있습니다. 특히 휴대폰 사용자" "의 경우 그렇습니다." -#: mygpo/publisher/templates/publisher/podcast.html:28 -#: mygpo/web/templates/podcast-base.html:44 -msgid "by" -msgstr "by" - -#: mygpo/publisher/templates/publisher/podcast.html:34 -#: mygpo/share/templates/share/favorites.html:29 -#: mygpo/web/templates/podcast-base.html:50 -msgid "Feed" -msgstr "피드" - -#: mygpo/publisher/templates/publisher/podcast.html:43 -#: mygpo/web/templates/podcast-base.html:65 -msgid "subscribers" -msgstr "구독자" - -#: mygpo/publisher/templates/publisher/podcast.html:48 +#: mygpo/publisher/templates/publisher/podcast.html:49 #, python-format msgid "" "This is the publisher page of %(ptitle)s. You can see some " @@ -1130,23 +1216,19 @@ msgstr "" "%(ptitle)s의 제작자 페이지입니다. 팟캐스트 페이지에 대한 통" "계와 추가 정보를 볼 수 있습니다." -#: mygpo/publisher/templates/publisher/podcast.html:49 +#: mygpo/publisher/templates/publisher/podcast.html:50 msgid "Go to Podcast Page" msgstr "팟캐스트 페이지로 이동" -#: mygpo/publisher/templates/publisher/podcast.html:56 +#: mygpo/publisher/templates/publisher/podcast.html:57 msgid "The podcast information is regularly retrieved from the podcast feed" msgstr "팟캐스트 정보는 주기적으로 팟캐스트 피드에서 가져옵니다" -#: mygpo/publisher/templates/publisher/podcast.html:59 -msgid "Timing" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:61 +#: mygpo/publisher/templates/publisher/podcast.html:60 #, fuzzy -#| msgid "Last update: " -msgid "Last update:" -msgstr "최근 업데이트: " +#| msgid "Update now" +msgid "Updates" +msgstr "지금 업데이트" #: mygpo/publisher/templates/publisher/podcast.html:62 #, fuzzy @@ -1154,32 +1236,34 @@ msgstr "최근 업데이트: " msgid "Update interval:" msgstr "지금 업데이트" -#: mygpo/publisher/templates/publisher/podcast.html:63 -#, fuzzy -#| msgid "Last update: " -msgid "Next update:" -msgstr "최근 업데이트: " +#: mygpo/publisher/templates/publisher/podcast.html:86 +msgid "Successful" +msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:68 +#: mygpo/publisher/templates/publisher/podcast.html:88 +msgid "Error" +msgstr "" + +#: mygpo/publisher/templates/publisher/podcast.html:106 msgid "Update now" msgstr "지금 업데이트" -#: mygpo/publisher/templates/publisher/podcast.html:79 mygpo/web/forms.py:70 +#: mygpo/publisher/templates/publisher/podcast.html:117 mygpo/web/forms.py:70 #: mygpo/web/templates/base.html:160 mygpo/web/templates/home.html:180 msgid "Twitter" msgstr "Twitter" -#: mygpo/publisher/templates/publisher/podcast.html:97 +#: mygpo/publisher/templates/publisher/podcast.html:135 #, fuzzy #| msgid "Check" msgid "Feed Check" msgstr "확인" -#: mygpo/publisher/templates/publisher/podcast.html:105 +#: mygpo/publisher/templates/publisher/podcast.html:143 msgid "PubSubHubbub" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:114 +#: mygpo/publisher/templates/publisher/podcast.html:152 #, python-format msgid "" "If you publish your podcast feed through a %(hub)s and should " "update immediatelly for each new episode." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:123 +#: mygpo/publisher/templates/publisher/podcast.html:161 #, python-format msgid "" "Your podcast is published through %(hub)s but our " "subscription has not yet been verified." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:128 +#: mygpo/publisher/templates/publisher/podcast.html:166 #, python-format msgid "" "We did not find a hub in your podcast feed. Your feed is updated regularly, " "but there might be some delay until a new episode shows up on %(sitename)s." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:140 +#: mygpo/publisher/templates/publisher/podcast.html:178 #, fuzzy #| msgid "Host Information" msgid "License Information" msgstr "호스트 정보" -#: mygpo/publisher/templates/publisher/podcast.html:148 +#: mygpo/publisher/templates/publisher/podcast.html:186 #, python-format msgid "" "You should include license information in your feed so that users and " "%(sitename)s can know, under which conditions your content can be used." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:152 +#: mygpo/publisher/templates/publisher/podcast.html:190 #, python-format msgid "" "We found the following license in your podcast: " "%(license)s" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:156 +#: mygpo/publisher/templates/publisher/podcast.html:194 msgid "" "We did not find a license in your podcast feed. Refer to " msgstr "계정 설정에서 링크를 알아내십시오." -#: mygpo/web/templatetags/devices.py:31 +#: mygpo/web/templatetags/devices.py:32 msgid "Unknown" msgstr "알 수 없음" @@ -2717,11 +2732,17 @@ msgstr "개인 정보" msgid "Link to gpodder.net" msgstr "gpodder.net에 링크" -#: mygpo/web/templatetags/time.py:38 +#: mygpo/web/templatetags/time.py:42 #, python-brace-format msgid "{h}h {m}m {s}s" msgstr "{h}시간 {m}분 {s}초" +#: mygpo/web/templatetags/time.py:44 +#, fuzzy, python-brace-format +#| msgid "{h}h {m}m {s}s" +msgid "{m}m {s}s" +msgstr "{h}시간 {m}분 {s}초" + #: mygpo/web/utils.py:281 #, python-format msgid "%(weeks)d week" @@ -2747,6 +2768,16 @@ msgstr[1] "" msgid "another site" msgstr "다른 사이트" +#, fuzzy +#~| msgid "Last update: " +#~ msgid "Last update:" +#~ msgstr "최근 업데이트: " + +#, fuzzy +#~| msgid "Last update: " +#~ msgid "Next update:" +#~ msgstr "최근 업데이트: " + #~ msgid "Flattr" #~ msgstr "Flattr" diff --git a/mygpo/locale/nb/LC_MESSAGES/django.po b/mygpo/locale/nb/LC_MESSAGES/django.po index 9eeb7a234..9fc06d07e 100644 --- a/mygpo/locale/nb/LC_MESSAGES/django.po +++ b/mygpo/locale/nb/LC_MESSAGES/django.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-10-12 18:58+0000\n" +"POT-Creation-Date: 2018-06-29 20:11+0000\n" "PO-Revision-Date: 2010-02-28 18:33+0100\n" "Last-Translator: Jim Nygård \n" "Language-Team: gpodder <>\n" @@ -103,47 +103,51 @@ msgstr "Type" msgid "Episode URLs" msgstr "Episoder" -#: mygpo/administration/templates/admin/hostinfo.html:9 -#: mygpo/administration/templates/admin/hostinfo.html:12 +#: mygpo/administration/templates/admin/hostinfo.html:10 +#: mygpo/administration/templates/admin/hostinfo.html:13 #: mygpo/administration/templates/admin/overview.html:17 msgid "Host Information" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:20 +#: mygpo/administration/templates/admin/hostinfo.html:21 msgid "mygpo Version" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:31 +#: mygpo/administration/templates/admin/hostinfo.html:32 #, fuzzy msgid "Base Directory" msgstr "Historikk" -#: mygpo/administration/templates/admin/hostinfo.html:36 +#: mygpo/administration/templates/admin/hostinfo.html:37 #, fuzzy msgid "Hostname" msgstr "Velg et brukernavn" -#: mygpo/administration/templates/admin/hostinfo.html:41 +#: mygpo/administration/templates/admin/hostinfo.html:42 msgid "Django Version" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:46 +#: mygpo/administration/templates/admin/hostinfo.html:47 msgid "Feed Update Queue" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:49 +#: mygpo/administration/templates/admin/hostinfo.html:50 msgid "min ahead" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:51 +#: mygpo/administration/templates/admin/hostinfo.html:52 msgid "min behind" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:59 +#: mygpo/administration/templates/admin/hostinfo.html:60 msgid "Number of podcasts with outdated search index" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:66 +#: mygpo/administration/templates/admin/hostinfo.html:69 +msgid "Average podcast update duration" +msgstr "" + +#: mygpo/administration/templates/admin/hostinfo.html:76 msgid "Scheduled Celery Tasks" msgstr "" @@ -151,7 +155,7 @@ msgstr "" #: mygpo/administration/templates/admin/make-publisher-input.html:12 #: mygpo/administration/templates/admin/make-publisher-result.html:9 #: mygpo/administration/templates/admin/make-publisher-result.html:12 -#: mygpo/administration/views.py:391 +#: mygpo/administration/views.py:404 msgid "Publisher Permissions" msgstr "" @@ -268,21 +272,21 @@ msgstr "" msgid "User-Agent" msgstr "" -#: mygpo/administration/views.py:140 mygpo/administration/views.py:162 +#: mygpo/administration/views.py:153 mygpo/administration/views.py:175 #, python-brace-format msgid "No podcast with URL {url}" msgstr "" -#: mygpo/administration/views.py:316 +#: mygpo/administration/views.py:329 msgid "Provide either username or email address" msgstr "" -#: mygpo/administration/views.py:322 +#: mygpo/administration/views.py:335 #, fuzzy msgid "No user found" msgstr "Ikke funnet" -#: mygpo/administration/views.py:327 +#: mygpo/administration/views.py:340 #, python-brace-format msgid "User {username} ({email}) activated" msgstr "" @@ -320,16 +324,16 @@ msgstr "avabonnement" msgid "%(username)s's Subscription List" msgstr "%(username)s sine Abonnement" -#: mygpo/api/simple.py:237 +#: mygpo/api/simple.py:240 #, fuzzy, python-format msgid "gpodder.net - Top %(count)d" msgstr "my.gpodder.org - Topp %s" -#: mygpo/api/simple.py:272 +#: mygpo/api/simple.py:275 msgid "gpodder.net - Search" msgstr "" -#: mygpo/api/simple.py:289 +#: mygpo/api/simple.py:292 #, fuzzy, python-format msgid "gpodder.net - %(count)d Suggestions" msgstr "my.gpodder.org - %s Forslag" @@ -352,7 +356,7 @@ msgid "Explore" msgstr "" #: mygpo/directory/templates/carousel.html:81 -#: mygpo/web/templates/episode.html:150 +#: mygpo/podcasts/templates/episode.html:150 msgid "..." msgstr "" @@ -482,11 +486,11 @@ msgid "Listeners" msgstr "Lyttere" #: mygpo/directory/templates/episode_toplist.html:36 +#: mygpo/history/templates/episode-history.html:51 +#: mygpo/podcasts/templates/episode.html:51 #: mygpo/publisher/templates/publisher/episode.html:33 #: mygpo/share/templates/userpage.html:118 #: mygpo/share/templates/userpage.html:138 -#: mygpo/web/templates/episode-history.html:50 -#: mygpo/web/templates/episode.html:51 msgid "from" msgstr "" @@ -560,9 +564,9 @@ msgid "User" msgstr "Velg et brukernavn" #: mygpo/directory/templates/podcast_lists.html:25 +#: mygpo/history/templates/episode-history.html:53 +#: mygpo/podcasts/templates/episode.html:53 #: mygpo/publisher/templates/publisher/episode.html:35 -#: mygpo/web/templates/episode-history.html:52 -#: mygpo/web/templates/episode.html:53 #, fuzzy msgid "Download" msgstr "lastet ned" @@ -622,10 +626,50 @@ msgstr "" msgid "%d podcasts added" msgstr "Podkaster" +#: mygpo/history/templates/episode-history.html:47 +#: mygpo/history/templates/episode-history.html:81 +#: mygpo/web/templates/device.html:73 mygpo/web/templatetags/menu.py:51 +msgid "History" +msgstr "Historikk" + +#: mygpo/history/templates/episode-history.html:58 +#: mygpo/podcasts/templates/episode.html:58 +#: mygpo/podcasts/templates/podcast-base.html:54 +#: mygpo/publisher/templates/publisher/episode.html:40 +#: mygpo/publisher/templates/publisher/podcast.html:39 +#, fuzzy +msgid "Website" +msgstr "Podkast nettjeneste" + +#: mygpo/history/templates/episode-history.html:63 +#: mygpo/podcasts/templates/episode.html:63 +#: mygpo/publisher/templates/publisher/episode.html:45 +#, fuzzy +msgid "listeners" +msgstr "Lyttere" + +#: mygpo/history/templates/episode-history.html:84 +#, fuzzy +msgid "Time" +msgstr "Tittel" + +#: mygpo/history/templates/episode-history.html:85 +msgid "Action" +msgstr "Handling" + +#: mygpo/history/templates/episode-history.html:86 +#: mygpo/web/templatetags/menu.py:50 +msgid "Device" +msgstr "Enhet" + +#: mygpo/history/templates/episode-history.html:127 +msgid "Add" +msgstr "" + #: mygpo/history/templates/history.html:12 #: mygpo/history/templates/history.html:15 -#: mygpo/web/templates/podcast-history.html:32 -#: mygpo/web/templates/podcast.html:143 +#: mygpo/history/templates/podcast-history.html:32 +#: mygpo/podcasts/templates/podcast.html:143 #, fuzzy msgid "Subscription History" msgstr "Abonnementshistorikk" @@ -659,6 +703,10 @@ msgstr "" msgid "Earlier" msgstr "" +#: mygpo/history/templates/podcast-history.html:47 +msgid "no history yet" +msgstr "" + #: mygpo/podcastlists/templates/list.html:28 #, python-format msgid "\"%(list_title)s\" by %(ownername)s" @@ -755,17 +803,101 @@ msgstr "" msgid "Edit" msgstr "Rediger" -#: mygpo/podcasts/models.py:688 +#: mygpo/podcasts/models.py:704 #, fuzzy #| msgid "Unnamed Podcast" msgid "Unknown Podcast" msgstr "Podkast uten navn" -#: mygpo/podcasts/models.py:690 +#: mygpo/podcasts/models.py:706 #, python-brace-format msgid "Unknown Podcast from {domain}" msgstr "" +#: mygpo/podcasts/templates/episode.html:85 +#: mygpo/podcasts/templates/episodes.html:27 +#: mygpo/podcasts/templates/podcast.html:39 +#: mygpo/publisher/templates/publisher/episode.html:31 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/info.html:9 +#: mygpo/publisher/templates/publisher/info.html:26 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Publisher Pages" +msgstr "" + +#: mygpo/podcasts/templates/episode.html:91 +msgid "Remove Favorite" +msgstr "" + +#: mygpo/podcasts/templates/episode.html:95 +msgid "Favorite" +msgstr "" + +#: mygpo/podcasts/templates/episode.html:103 +#, fuzzy +#| msgid "Your Episode History" +msgid "Episode History" +msgstr "Din episodehistorikk" + +#: mygpo/podcasts/templates/podcast-base.html:39 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Unnamed Podcast" +msgstr "Podkast uten navn" + +#: mygpo/podcasts/templates/podcast-base.html:44 +#: mygpo/publisher/templates/publisher/podcast.html:29 +msgid "by" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:50 +#: mygpo/publisher/templates/publisher/podcast.html:35 +#: mygpo/share/templates/share/favorites.html:29 +msgid "Feed" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:65 +#: mygpo/publisher/templates/publisher/podcast.html:44 +#, fuzzy +msgid "subscribers" +msgstr "Abonnenter" + +#: mygpo/podcasts/templates/podcast.html:48 +#, fuzzy +#| msgid "Subscribe" +msgid "Login to Subscribe" +msgstr "Abonnenter" + +#: mygpo/podcasts/templates/podcast.html:59 +#: mygpo/suggestions/templates/suggestions.html:31 +#: mygpo/web/templates/subscribe.html:11 mygpo/web/templates/subscribe.html:36 +msgid "Subscribe" +msgstr "Abonnenter" + +#: mygpo/podcasts/templates/podcast.html:67 mygpo/web/templates/device.html:31 +msgid "Unsubscribe" +msgstr "Avslutt abonnement" + +#: mygpo/podcasts/templates/podcast.html:78 +#, fuzzy +msgid "Subscribe on all devices" +msgstr "Abonner på nye podkaster" + +#: mygpo/podcasts/templates/podcast.html:110 +#, fuzzy +msgid "Unsubscribe from all devices " +msgstr "Abonner på nye podkaster" + +#: mygpo/podcasts/templates/podcast.html:149 +#: mygpo/podcasts/templates/podcast.html:164 +msgid "Tags" +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:199 +#, fuzzy +msgid "Older Episodes" +msgstr "Episoder" + #: mygpo/publisher/forms.py:5 #: mygpo/publisher/templates/publisher/episode.html:60 msgid "URL" @@ -780,7 +912,7 @@ msgid "Link to" msgstr "" #: mygpo/publisher/templates/link.html:17 -#: mygpo/publisher/templates/publisher/podcast.html:171 +#: mygpo/publisher/templates/publisher/podcast.html:209 #, fuzzy, python-format msgid "" "You can paste this code on your website, so users of %(sitename)s can " @@ -873,31 +1005,6 @@ msgstr "" msgid "Unnamed Episode" msgstr "Episoden uten navn" -#: mygpo/publisher/templates/publisher/episode.html:31 -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/info.html:9 -#: mygpo/publisher/templates/publisher/info.html:26 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/episode.html:85 mygpo/web/templates/episodes.html:27 -#: mygpo/web/templates/podcast.html:39 -msgid "Publisher Pages" -msgstr "" - -#: mygpo/publisher/templates/publisher/episode.html:40 -#: mygpo/publisher/templates/publisher/podcast.html:38 -#: mygpo/web/templates/episode-history.html:57 -#: mygpo/web/templates/episode.html:58 mygpo/web/templates/podcast-base.html:54 -#, fuzzy -msgid "Website" -msgstr "Podkast nettjeneste" - -#: mygpo/publisher/templates/publisher/episode.html:45 -#: mygpo/web/templates/episode-history.html:62 -#: mygpo/web/templates/episode.html:63 -#, fuzzy -msgid "listeners" -msgstr "Lyttere" - #: mygpo/publisher/templates/publisher/episode.html:55 #, fuzzy msgid "Episode List" @@ -915,7 +1022,7 @@ msgid "" msgstr "" #: mygpo/publisher/templates/publisher/episode.html:72 -#: mygpo/publisher/templates/publisher/podcast.html:91 +#: mygpo/publisher/templates/publisher/podcast.html:129 #: mygpo/web/templates/account.html:65 mygpo/web/templates/account.html:142 #: mygpo/web/templates/device-edit.html:53 msgid "Save" @@ -930,12 +1037,6 @@ msgstr "Episoder" msgid "Last update: " msgstr "" -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/podcast-base.html:39 -msgid "Unnamed Podcast" -msgstr "Podkast uten navn" - #: mygpo/publisher/templates/publisher/episodes.html:24 msgid "Return to Podcast Page" msgstr "" @@ -1077,75 +1178,57 @@ msgid "" "podcast - especially for users of mobile phones." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:28 -#: mygpo/web/templates/podcast-base.html:44 -msgid "by" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:34 -#: mygpo/share/templates/share/favorites.html:29 -#: mygpo/web/templates/podcast-base.html:50 -msgid "Feed" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:43 -#: mygpo/web/templates/podcast-base.html:65 -#, fuzzy -msgid "subscribers" -msgstr "Abonnenter" - -#: mygpo/publisher/templates/publisher/podcast.html:48 +#: mygpo/publisher/templates/publisher/podcast.html:49 #, python-format msgid "" "This is the publisher page of %(ptitle)s. You can see some " "stats and provide additional data for the podcast page." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:49 +#: mygpo/publisher/templates/publisher/podcast.html:50 #, fuzzy msgid "Go to Podcast Page" msgstr "Podkast" -#: mygpo/publisher/templates/publisher/podcast.html:56 +#: mygpo/publisher/templates/publisher/podcast.html:57 msgid "The podcast information is regularly retrieved from the podcast feed" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:59 -msgid "Timing" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:61 +#: mygpo/publisher/templates/publisher/podcast.html:60 #, fuzzy -msgid "Last update:" -msgstr "Enhetsliste" +msgid "Updates" +msgstr "Podkast" #: mygpo/publisher/templates/publisher/podcast.html:62 msgid "Update interval:" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:63 -#, fuzzy -msgid "Next update:" -msgstr "Enhetsliste" +#: mygpo/publisher/templates/publisher/podcast.html:86 +msgid "Successful" +msgstr "" + +#: mygpo/publisher/templates/publisher/podcast.html:88 +msgid "Error" +msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:68 +#: mygpo/publisher/templates/publisher/podcast.html:106 msgid "Update now" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:79 mygpo/web/forms.py:70 +#: mygpo/publisher/templates/publisher/podcast.html:117 mygpo/web/forms.py:70 #: mygpo/web/templates/base.html:160 mygpo/web/templates/home.html:180 msgid "Twitter" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:97 +#: mygpo/publisher/templates/publisher/podcast.html:135 msgid "Feed Check" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:105 +#: mygpo/publisher/templates/publisher/podcast.html:143 msgid "PubSubHubbub" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:114 +#: mygpo/publisher/templates/publisher/podcast.html:152 #, python-format msgid "" "If you publish your podcast feed through a %(hub)s and should " "update immediatelly for each new episode." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:123 +#: mygpo/publisher/templates/publisher/podcast.html:161 #, python-format msgid "" "Your podcast is published through %(hub)s but our " "subscription has not yet been verified." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:128 +#: mygpo/publisher/templates/publisher/podcast.html:166 #, python-format msgid "" "We did not find a hub in your podcast feed. Your feed is updated regularly, " "but there might be some delay until a new episode shows up on %(sitename)s." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:140 +#: mygpo/publisher/templates/publisher/podcast.html:178 msgid "License Information" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:148 +#: mygpo/publisher/templates/publisher/podcast.html:186 #, python-format msgid "" "You should include license information in your feed so that users and " "%(sitename)s can know, under which conditions your content can be used." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:152 +#: mygpo/publisher/templates/publisher/podcast.html:190 #, python-format msgid "" "We found the following license in your podcast: " "%(license)s" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:156 +#: mygpo/publisher/templates/publisher/podcast.html:194 msgid "" "We did not find a license in your podcast feed. Refer to konto innstillinger for å få " "lenken." -#: mygpo/web/templatetags/devices.py:31 +#: mygpo/web/templatetags/devices.py:32 msgid "Unknown" msgstr "Ukjent" @@ -2702,11 +2717,16 @@ msgstr "Personvern" msgid "Link to gpodder.net" msgstr "" -#: mygpo/web/templatetags/time.py:38 +#: mygpo/web/templatetags/time.py:42 #, python-brace-format msgid "{h}h {m}m {s}s" msgstr "" +#: mygpo/web/templatetags/time.py:44 +#, python-brace-format +msgid "{m}m {s}s" +msgstr "" + #: mygpo/web/utils.py:281 #, python-format msgid "%(weeks)d week" @@ -2732,6 +2752,14 @@ msgstr[1] "" msgid "another site" msgstr "" +#, fuzzy +#~ msgid "Last update:" +#~ msgstr "Enhetsliste" + +#, fuzzy +#~ msgid "Next update:" +#~ msgstr "Enhetsliste" + #, fuzzy #~ msgid "No Payment URL available" #~ msgstr "Ingen logo tilgjengelig" diff --git a/mygpo/locale/pl/LC_MESSAGES/django.po b/mygpo/locale/pl/LC_MESSAGES/django.po index c688d0c3b..9191a38d4 100644 --- a/mygpo/locale/pl/LC_MESSAGES/django.po +++ b/mygpo/locale/pl/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: mygpo\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-10-12 18:58+0000\n" +"POT-Creation-Date: 2018-06-29 20:11+0000\n" "PO-Revision-Date: 2010-01-27 17:38+0100\n" "Last-Translator: Tomasz Dominikowski \n" "Language-Team: Polish \n" @@ -106,49 +106,53 @@ msgstr "Typ" msgid "Episode URLs" msgstr "Odcinki" -#: mygpo/administration/templates/admin/hostinfo.html:9 -#: mygpo/administration/templates/admin/hostinfo.html:12 +#: mygpo/administration/templates/admin/hostinfo.html:10 +#: mygpo/administration/templates/admin/hostinfo.html:13 #: mygpo/administration/templates/admin/overview.html:17 msgid "Host Information" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:20 +#: mygpo/administration/templates/admin/hostinfo.html:21 msgid "mygpo Version" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:31 +#: mygpo/administration/templates/admin/hostinfo.html:32 #, fuzzy msgid "Base Directory" msgstr "Historia" -#: mygpo/administration/templates/admin/hostinfo.html:36 +#: mygpo/administration/templates/admin/hostinfo.html:37 #, fuzzy msgid "Hostname" msgstr "Nazwa użytkownika" -#: mygpo/administration/templates/admin/hostinfo.html:41 +#: mygpo/administration/templates/admin/hostinfo.html:42 msgid "Django Version" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:46 +#: mygpo/administration/templates/admin/hostinfo.html:47 msgid "Feed Update Queue" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:49 +#: mygpo/administration/templates/admin/hostinfo.html:50 #, fuzzy #| msgid "Admin Area" msgid "min ahead" msgstr "Strefa Admina" -#: mygpo/administration/templates/admin/hostinfo.html:51 +#: mygpo/administration/templates/admin/hostinfo.html:52 msgid "min behind" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:59 +#: mygpo/administration/templates/admin/hostinfo.html:60 msgid "Number of podcasts with outdated search index" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:66 +#: mygpo/administration/templates/admin/hostinfo.html:69 +msgid "Average podcast update duration" +msgstr "" + +#: mygpo/administration/templates/admin/hostinfo.html:76 msgid "Scheduled Celery Tasks" msgstr "" @@ -156,7 +160,7 @@ msgstr "" #: mygpo/administration/templates/admin/make-publisher-input.html:12 #: mygpo/administration/templates/admin/make-publisher-result.html:9 #: mygpo/administration/templates/admin/make-publisher-result.html:12 -#: mygpo/administration/views.py:391 +#: mygpo/administration/views.py:404 #, fuzzy #| msgid "Publisher Pages" msgid "Publisher Permissions" @@ -280,21 +284,21 @@ msgstr "" msgid "User-Agent" msgstr "User-Agent" -#: mygpo/administration/views.py:140 mygpo/administration/views.py:162 +#: mygpo/administration/views.py:153 mygpo/administration/views.py:175 #, python-brace-format msgid "No podcast with URL {url}" msgstr "Brak podcastu z URL {url}" -#: mygpo/administration/views.py:316 +#: mygpo/administration/views.py:329 msgid "Provide either username or email address" msgstr "" -#: mygpo/administration/views.py:322 +#: mygpo/administration/views.py:335 #, fuzzy msgid "No user found" msgstr "Nie odnaleziono" -#: mygpo/administration/views.py:327 +#: mygpo/administration/views.py:340 #, python-brace-format msgid "User {username} ({email}) activated" msgstr "" @@ -335,16 +339,16 @@ msgstr "Anuluj subskrypcję" msgid "%(username)s's Subscription List" msgstr "Twoje subskrypcje" -#: mygpo/api/simple.py:237 +#: mygpo/api/simple.py:240 #, fuzzy, python-format msgid "gpodder.net - Top %(count)d" msgstr "my.gpodder.org - najlepszych %s" -#: mygpo/api/simple.py:272 +#: mygpo/api/simple.py:275 msgid "gpodder.net - Search" msgstr "gpodder.net - Szukaj" -#: mygpo/api/simple.py:289 +#: mygpo/api/simple.py:292 #, fuzzy, python-format msgid "gpodder.net - %(count)d Suggestions" msgstr "my.gpodder.org - %s sugestii" @@ -367,7 +371,7 @@ msgid "Explore" msgstr "" #: mygpo/directory/templates/carousel.html:81 -#: mygpo/web/templates/episode.html:150 +#: mygpo/podcasts/templates/episode.html:150 #, fuzzy msgid "..." msgstr "więcej..." @@ -503,11 +507,11 @@ msgid "Listeners" msgstr "Słuchacze" #: mygpo/directory/templates/episode_toplist.html:36 +#: mygpo/history/templates/episode-history.html:51 +#: mygpo/podcasts/templates/episode.html:51 #: mygpo/publisher/templates/publisher/episode.html:33 #: mygpo/share/templates/userpage.html:118 #: mygpo/share/templates/userpage.html:138 -#: mygpo/web/templates/episode-history.html:50 -#: mygpo/web/templates/episode.html:51 msgid "from" msgstr "od" @@ -580,9 +584,9 @@ msgid "User" msgstr "Użytkownik" #: mygpo/directory/templates/podcast_lists.html:25 +#: mygpo/history/templates/episode-history.html:53 +#: mygpo/podcasts/templates/episode.html:53 #: mygpo/publisher/templates/publisher/episode.html:35 -#: mygpo/web/templates/episode-history.html:52 -#: mygpo/web/templates/episode.html:53 msgid "Download" msgstr "Pobierz" @@ -642,10 +646,50 @@ msgstr "Dostęp klienta" msgid "%d podcasts added" msgstr "Podcasty" +#: mygpo/history/templates/episode-history.html:47 +#: mygpo/history/templates/episode-history.html:81 +#: mygpo/web/templates/device.html:73 mygpo/web/templatetags/menu.py:51 +msgid "History" +msgstr "Historia" + +#: mygpo/history/templates/episode-history.html:58 +#: mygpo/podcasts/templates/episode.html:58 +#: mygpo/podcasts/templates/podcast-base.html:54 +#: mygpo/publisher/templates/publisher/episode.html:40 +#: mygpo/publisher/templates/publisher/podcast.html:39 +#, fuzzy +msgid "Website" +msgstr "strona" + +#: mygpo/history/templates/episode-history.html:63 +#: mygpo/podcasts/templates/episode.html:63 +#: mygpo/publisher/templates/publisher/episode.html:45 +#, fuzzy +msgid "listeners" +msgstr "Słuchacze" + +#: mygpo/history/templates/episode-history.html:84 +#, fuzzy +msgid "Time" +msgstr "Tytuł" + +#: mygpo/history/templates/episode-history.html:85 +msgid "Action" +msgstr "Działanie" + +#: mygpo/history/templates/episode-history.html:86 +#: mygpo/web/templatetags/menu.py:50 +msgid "Device" +msgstr "Urządzenie" + +#: mygpo/history/templates/episode-history.html:127 +msgid "Add" +msgstr "Dodaj" + #: mygpo/history/templates/history.html:12 #: mygpo/history/templates/history.html:15 -#: mygpo/web/templates/podcast-history.html:32 -#: mygpo/web/templates/podcast.html:143 +#: mygpo/history/templates/podcast-history.html:32 +#: mygpo/podcasts/templates/podcast.html:143 #, fuzzy msgid "Subscription History" msgstr "Historia subskrypcji" @@ -679,6 +723,10 @@ msgstr "Teraz" msgid "Earlier" msgstr "Wcześniej" +#: mygpo/history/templates/podcast-history.html:47 +msgid "no history yet" +msgstr "" + #: mygpo/podcastlists/templates/list.html:28 #, python-format msgid "\"%(list_title)s\" by %(ownername)s" @@ -776,17 +824,103 @@ msgstr "Dzięki za ocenę!" msgid "Edit" msgstr "Edytuj" -#: mygpo/podcasts/models.py:688 +#: mygpo/podcasts/models.py:704 #, fuzzy #| msgid "Unnamed Podcast" msgid "Unknown Podcast" msgstr "Podcast bez nazwy" -#: mygpo/podcasts/models.py:690 +#: mygpo/podcasts/models.py:706 #, python-brace-format msgid "Unknown Podcast from {domain}" msgstr "" +#: mygpo/podcasts/templates/episode.html:85 +#: mygpo/podcasts/templates/episodes.html:27 +#: mygpo/podcasts/templates/podcast.html:39 +#: mygpo/publisher/templates/publisher/episode.html:31 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/info.html:9 +#: mygpo/publisher/templates/publisher/info.html:26 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Publisher Pages" +msgstr "Strony Publikującego" + +#: mygpo/podcasts/templates/episode.html:91 +msgid "Remove Favorite" +msgstr "Usuń ulubione" + +#: mygpo/podcasts/templates/episode.html:95 +msgid "Favorite" +msgstr "Ulubione" + +#: mygpo/podcasts/templates/episode.html:103 +#, fuzzy +#| msgid "Your Episode History" +msgid "Episode History" +msgstr "Historia odcinków" + +#: mygpo/podcasts/templates/podcast-base.html:39 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Unnamed Podcast" +msgstr "Podcast bez nazwy" + +#: mygpo/podcasts/templates/podcast-base.html:44 +#: mygpo/publisher/templates/publisher/podcast.html:29 +msgid "by" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:50 +#: mygpo/publisher/templates/publisher/podcast.html:35 +#: mygpo/share/templates/share/favorites.html:29 +msgid "Feed" +msgstr "Kanał" + +#: mygpo/podcasts/templates/podcast-base.html:65 +#: mygpo/publisher/templates/publisher/podcast.html:44 +#, fuzzy +msgid "subscribers" +msgstr "Subkrybenci" + +#: mygpo/podcasts/templates/podcast.html:48 +#, fuzzy +msgid "Login to Subscribe" +msgstr "Subkrybenci" + +#: mygpo/podcasts/templates/podcast.html:59 +#: mygpo/suggestions/templates/suggestions.html:31 +#: mygpo/web/templates/subscribe.html:11 mygpo/web/templates/subscribe.html:36 +#, fuzzy +msgid "Subscribe" +msgstr "Subkrybenci" + +#: mygpo/podcasts/templates/podcast.html:67 mygpo/web/templates/device.html:31 +msgid "Unsubscribe" +msgstr "Anuluj subskrypcję" + +#: mygpo/podcasts/templates/podcast.html:78 +#, fuzzy +msgid "Subscribe on all devices" +msgstr "Subskrypcja tego podcastu" + +#: mygpo/podcasts/templates/podcast.html:110 +#, fuzzy +msgid "Unsubscribe from all devices " +msgstr "Subskrypcja tego podcastu" + +#: mygpo/podcasts/templates/podcast.html:149 +#: mygpo/podcasts/templates/podcast.html:164 +#, fuzzy +#| msgid "My Tags" +msgid "Tags" +msgstr "Moje Tagi" + +#: mygpo/podcasts/templates/podcast.html:199 +#, fuzzy +msgid "Older Episodes" +msgstr "Odcinki" + #: mygpo/publisher/forms.py:5 #: mygpo/publisher/templates/publisher/episode.html:60 msgid "URL" @@ -802,7 +936,7 @@ msgid "Link to" msgstr "Link do" #: mygpo/publisher/templates/link.html:17 -#: mygpo/publisher/templates/publisher/podcast.html:171 +#: mygpo/publisher/templates/publisher/podcast.html:209 #, fuzzy, python-format msgid "" "You can paste this code on your website, so users of %(sitename)s can " @@ -913,31 +1047,6 @@ msgstr "" msgid "Unnamed Episode" msgstr "Dlaczego odcinek bez nazwy?" -#: mygpo/publisher/templates/publisher/episode.html:31 -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/info.html:9 -#: mygpo/publisher/templates/publisher/info.html:26 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/episode.html:85 mygpo/web/templates/episodes.html:27 -#: mygpo/web/templates/podcast.html:39 -msgid "Publisher Pages" -msgstr "Strony Publikującego" - -#: mygpo/publisher/templates/publisher/episode.html:40 -#: mygpo/publisher/templates/publisher/podcast.html:38 -#: mygpo/web/templates/episode-history.html:57 -#: mygpo/web/templates/episode.html:58 mygpo/web/templates/podcast-base.html:54 -#, fuzzy -msgid "Website" -msgstr "strona" - -#: mygpo/publisher/templates/publisher/episode.html:45 -#: mygpo/web/templates/episode-history.html:62 -#: mygpo/web/templates/episode.html:63 -#, fuzzy -msgid "listeners" -msgstr "Słuchacze" - #: mygpo/publisher/templates/publisher/episode.html:55 #, fuzzy msgid "Episode List" @@ -955,7 +1064,7 @@ msgid "" msgstr "" #: mygpo/publisher/templates/publisher/episode.html:72 -#: mygpo/publisher/templates/publisher/podcast.html:91 +#: mygpo/publisher/templates/publisher/podcast.html:129 #: mygpo/web/templates/account.html:65 mygpo/web/templates/account.html:142 #: mygpo/web/templates/device-edit.html:53 msgid "Save" @@ -970,12 +1079,6 @@ msgstr "Odcinki" msgid "Last update: " msgstr "" -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/podcast-base.html:39 -msgid "Unnamed Podcast" -msgstr "Podcast bez nazwy" - #: mygpo/publisher/templates/publisher/episodes.html:24 msgid "Return to Podcast Page" msgstr "Powrót na Stronę Podcastów" @@ -1137,77 +1240,59 @@ msgstr "" "strony możesz sprawić by Twoi odwiedzający mogli łatwo subskrybować się do " "Twojego podcastu - szczególnie użytkownicy telefonów komórkowych." -#: mygpo/publisher/templates/publisher/podcast.html:28 -#: mygpo/web/templates/podcast-base.html:44 -msgid "by" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:34 -#: mygpo/share/templates/share/favorites.html:29 -#: mygpo/web/templates/podcast-base.html:50 -msgid "Feed" -msgstr "Kanał" - -#: mygpo/publisher/templates/publisher/podcast.html:43 -#: mygpo/web/templates/podcast-base.html:65 -#, fuzzy -msgid "subscribers" -msgstr "Subkrybenci" - -#: mygpo/publisher/templates/publisher/podcast.html:48 +#: mygpo/publisher/templates/publisher/podcast.html:49 #, python-format msgid "" "This is the publisher page of %(ptitle)s. You can see some " "stats and provide additional data for the podcast page." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:49 +#: mygpo/publisher/templates/publisher/podcast.html:50 #, fuzzy msgid "Go to Podcast Page" msgstr "Powrót na Stronę Podcastów" -#: mygpo/publisher/templates/publisher/podcast.html:56 +#: mygpo/publisher/templates/publisher/podcast.html:57 msgid "The podcast information is regularly retrieved from the podcast feed" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:59 -msgid "Timing" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:61 +#: mygpo/publisher/templates/publisher/podcast.html:60 #, fuzzy -msgid "Last update:" -msgstr "Lista urządzeń" +msgid "Updates" +msgstr "Zaktualizuj z kanału" #: mygpo/publisher/templates/publisher/podcast.html:62 #, fuzzy msgid "Update interval:" msgstr "Zaktualizuj z kanału" -#: mygpo/publisher/templates/publisher/podcast.html:63 -#, fuzzy -msgid "Next update:" -msgstr "Lista urządzeń" +#: mygpo/publisher/templates/publisher/podcast.html:86 +msgid "Successful" +msgstr "" + +#: mygpo/publisher/templates/publisher/podcast.html:88 +msgid "Error" +msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:68 +#: mygpo/publisher/templates/publisher/podcast.html:106 #, fuzzy msgid "Update now" msgstr "Zaktualizuj z kanału" -#: mygpo/publisher/templates/publisher/podcast.html:79 mygpo/web/forms.py:70 +#: mygpo/publisher/templates/publisher/podcast.html:117 mygpo/web/forms.py:70 #: mygpo/web/templates/base.html:160 mygpo/web/templates/home.html:180 msgid "Twitter" msgstr "Twitter" -#: mygpo/publisher/templates/publisher/podcast.html:97 +#: mygpo/publisher/templates/publisher/podcast.html:135 msgid "Feed Check" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:105 +#: mygpo/publisher/templates/publisher/podcast.html:143 msgid "PubSubHubbub" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:114 +#: mygpo/publisher/templates/publisher/podcast.html:152 #, python-format msgid "" "If you publish your podcast feed through a %(hub)s and should " "update immediatelly for each new episode." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:123 +#: mygpo/publisher/templates/publisher/podcast.html:161 #, python-format msgid "" "Your podcast is published through %(hub)s but our " "subscription has not yet been verified." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:128 +#: mygpo/publisher/templates/publisher/podcast.html:166 #, python-format msgid "" "We did not find a hub in your podcast feed. Your feed is updated regularly, " "but there might be some delay until a new episode shows up on %(sitename)s." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:140 +#: mygpo/publisher/templates/publisher/podcast.html:178 msgid "License Information" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:148 +#: mygpo/publisher/templates/publisher/podcast.html:186 #, python-format msgid "" "You should include license information in your feed so that users and " "%(sitename)s can know, under which conditions your content can be used." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:152 +#: mygpo/publisher/templates/publisher/podcast.html:190 #, python-format msgid "" "We found the following license in your podcast: " "%(license)s" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:156 +#: mygpo/publisher/templates/publisher/podcast.html:194 msgid "" "We did not find a license in your podcast feed. Refer to ustawień konta żeby otrzymać link." "" -#: mygpo/web/templatetags/devices.py:31 +#: mygpo/web/templatetags/devices.py:32 msgid "Unknown" msgstr "Nieznane" @@ -2830,11 +2846,17 @@ msgstr "Prywatność" msgid "Link to gpodder.net" msgstr "Link do gpodder.net" -#: mygpo/web/templatetags/time.py:38 +#: mygpo/web/templatetags/time.py:42 #, python-brace-format msgid "{h}h {m}m {s}s" msgstr "{h}h {m}m {s}s" +#: mygpo/web/templatetags/time.py:44 +#, fuzzy, python-brace-format +#| msgid "{h}h {m}m {s}s" +msgid "{m}m {s}s" +msgstr "{h}h {m}m {s}s" + #: mygpo/web/utils.py:281 #, python-format msgid "%(weeks)d week" @@ -2860,6 +2882,14 @@ msgstr[1] "" msgid "another site" msgstr "inna strona" +#, fuzzy +#~ msgid "Last update:" +#~ msgstr "Lista urządzeń" + +#, fuzzy +#~ msgid "Next update:" +#~ msgstr "Lista urządzeń" + #~ msgid "Flattr" #~ msgstr "Flattr" diff --git a/mygpo/locale/pt/LC_MESSAGES/django.po b/mygpo/locale/pt/LC_MESSAGES/django.po index bd28bd909..a8712bfea 100644 --- a/mygpo/locale/pt/LC_MESSAGES/django.po +++ b/mygpo/locale/pt/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: gpodder.net\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-10-12 18:58+0000\n" +"POT-Creation-Date: 2018-06-29 20:11+0000\n" "PO-Revision-Date: 2010-06-17 17:01-0000\n" "Last-Translator: Sérgio Marques \n" "Language-Team: PT\n" @@ -107,47 +107,51 @@ msgstr "Tipo" msgid "Episode URLs" msgstr "Episódios" -#: mygpo/administration/templates/admin/hostinfo.html:9 -#: mygpo/administration/templates/admin/hostinfo.html:12 +#: mygpo/administration/templates/admin/hostinfo.html:10 +#: mygpo/administration/templates/admin/hostinfo.html:13 #: mygpo/administration/templates/admin/overview.html:17 msgid "Host Information" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:20 +#: mygpo/administration/templates/admin/hostinfo.html:21 msgid "mygpo Version" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:31 +#: mygpo/administration/templates/admin/hostinfo.html:32 #, fuzzy msgid "Base Directory" msgstr "Directório" -#: mygpo/administration/templates/admin/hostinfo.html:36 +#: mygpo/administration/templates/admin/hostinfo.html:37 #, fuzzy msgid "Hostname" msgstr "Nome de utilizador" -#: mygpo/administration/templates/admin/hostinfo.html:41 +#: mygpo/administration/templates/admin/hostinfo.html:42 msgid "Django Version" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:46 +#: mygpo/administration/templates/admin/hostinfo.html:47 msgid "Feed Update Queue" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:49 +#: mygpo/administration/templates/admin/hostinfo.html:50 msgid "min ahead" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:51 +#: mygpo/administration/templates/admin/hostinfo.html:52 msgid "min behind" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:59 +#: mygpo/administration/templates/admin/hostinfo.html:60 msgid "Number of podcasts with outdated search index" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:66 +#: mygpo/administration/templates/admin/hostinfo.html:69 +msgid "Average podcast update duration" +msgstr "" + +#: mygpo/administration/templates/admin/hostinfo.html:76 msgid "Scheduled Celery Tasks" msgstr "" @@ -155,7 +159,7 @@ msgstr "" #: mygpo/administration/templates/admin/make-publisher-input.html:12 #: mygpo/administration/templates/admin/make-publisher-result.html:9 #: mygpo/administration/templates/admin/make-publisher-result.html:12 -#: mygpo/administration/views.py:391 +#: mygpo/administration/views.py:404 #, fuzzy #| msgid "Publisher Pages" msgid "Publisher Permissions" @@ -275,21 +279,21 @@ msgstr "" msgid "User-Agent" msgstr "" -#: mygpo/administration/views.py:140 mygpo/administration/views.py:162 +#: mygpo/administration/views.py:153 mygpo/administration/views.py:175 #, python-brace-format msgid "No podcast with URL {url}" msgstr "" -#: mygpo/administration/views.py:316 +#: mygpo/administration/views.py:329 msgid "Provide either username or email address" msgstr "" -#: mygpo/administration/views.py:322 +#: mygpo/administration/views.py:335 #, fuzzy msgid "No user found" msgstr "Não encontrado" -#: mygpo/administration/views.py:327 +#: mygpo/administration/views.py:340 #, python-brace-format msgid "User {username} ({email}) activated" msgstr "" @@ -327,16 +331,16 @@ msgstr "não subscrito" msgid "%(username)s's Subscription List" msgstr "Subscrições de %(username)s's" -#: mygpo/api/simple.py:237 +#: mygpo/api/simple.py:240 #, python-format msgid "gpodder.net - Top %(count)d" msgstr "gpodder.net - Top %(count)d" -#: mygpo/api/simple.py:272 +#: mygpo/api/simple.py:275 msgid "gpodder.net - Search" msgstr "gpodder.net - Procura" -#: mygpo/api/simple.py:289 +#: mygpo/api/simple.py:292 #, python-format msgid "gpodder.net - %(count)d Suggestions" msgstr "gpodder.net - %(count)d sugestões" @@ -359,7 +363,7 @@ msgid "Explore" msgstr "" #: mygpo/directory/templates/carousel.html:81 -#: mygpo/web/templates/episode.html:150 +#: mygpo/podcasts/templates/episode.html:150 #, fuzzy msgid "..." msgstr "mais..." @@ -490,11 +494,11 @@ msgid "Listeners" msgstr "Ouvintes" #: mygpo/directory/templates/episode_toplist.html:36 +#: mygpo/history/templates/episode-history.html:51 +#: mygpo/podcasts/templates/episode.html:51 #: mygpo/publisher/templates/publisher/episode.html:33 #: mygpo/share/templates/userpage.html:118 #: mygpo/share/templates/userpage.html:138 -#: mygpo/web/templates/episode-history.html:50 -#: mygpo/web/templates/episode.html:51 msgid "from" msgstr "de" @@ -569,9 +573,9 @@ msgid "User" msgstr "Nome de utilizador" #: mygpo/directory/templates/podcast_lists.html:25 +#: mygpo/history/templates/episode-history.html:53 +#: mygpo/podcasts/templates/episode.html:53 #: mygpo/publisher/templates/publisher/episode.html:35 -#: mygpo/web/templates/episode-history.html:52 -#: mygpo/web/templates/episode.html:53 #, fuzzy msgid "Download" msgstr "Transferido" @@ -631,10 +635,49 @@ msgstr "" msgid "%d podcasts added" msgstr "podcasts" +#: mygpo/history/templates/episode-history.html:47 +#: mygpo/history/templates/episode-history.html:81 +#: mygpo/web/templates/device.html:73 mygpo/web/templatetags/menu.py:51 +msgid "History" +msgstr "Histórico" + +#: mygpo/history/templates/episode-history.html:58 +#: mygpo/podcasts/templates/episode.html:58 +#: mygpo/podcasts/templates/podcast-base.html:54 +#: mygpo/publisher/templates/publisher/episode.html:40 +#: mygpo/publisher/templates/publisher/podcast.html:39 +#, fuzzy +msgid "Website" +msgstr "sítio web" + +#: mygpo/history/templates/episode-history.html:63 +#: mygpo/podcasts/templates/episode.html:63 +#: mygpo/publisher/templates/publisher/episode.html:45 +#, fuzzy +msgid "listeners" +msgstr "Ouvintes" + +#: mygpo/history/templates/episode-history.html:84 +msgid "Time" +msgstr "Hora" + +#: mygpo/history/templates/episode-history.html:85 +msgid "Action" +msgstr "Acção" + +#: mygpo/history/templates/episode-history.html:86 +#: mygpo/web/templatetags/menu.py:50 +msgid "Device" +msgstr "Dispositivo" + +#: mygpo/history/templates/episode-history.html:127 +msgid "Add" +msgstr "Adicionar" + #: mygpo/history/templates/history.html:12 #: mygpo/history/templates/history.html:15 -#: mygpo/web/templates/podcast-history.html:32 -#: mygpo/web/templates/podcast.html:143 +#: mygpo/history/templates/podcast-history.html:32 +#: mygpo/podcasts/templates/podcast.html:143 #, fuzzy msgid "Subscription History" msgstr "Histório de subscrição" @@ -669,6 +712,10 @@ msgstr "Não" msgid "Earlier" msgstr "" +#: mygpo/history/templates/podcast-history.html:47 +msgid "no history yet" +msgstr "" + #: mygpo/podcastlists/templates/list.html:28 #, python-format msgid "\"%(list_title)s\" by %(ownername)s" @@ -765,17 +812,103 @@ msgstr "" msgid "Edit" msgstr "Editar" -#: mygpo/podcasts/models.py:688 +#: mygpo/podcasts/models.py:704 #, fuzzy #| msgid "Unnamed Podcast" msgid "Unknown Podcast" msgstr "Podcasts Sem Nome" -#: mygpo/podcasts/models.py:690 +#: mygpo/podcasts/models.py:706 #, python-brace-format msgid "Unknown Podcast from {domain}" msgstr "" +#: mygpo/podcasts/templates/episode.html:85 +#: mygpo/podcasts/templates/episodes.html:27 +#: mygpo/podcasts/templates/podcast.html:39 +#: mygpo/publisher/templates/publisher/episode.html:31 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/info.html:9 +#: mygpo/publisher/templates/publisher/info.html:26 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Publisher Pages" +msgstr "Páginas dos Publicadores" + +#: mygpo/podcasts/templates/episode.html:91 +#, fuzzy +msgid "Remove Favorite" +msgstr "Favorito" + +#: mygpo/podcasts/templates/episode.html:95 +msgid "Favorite" +msgstr "Favorito" + +#: mygpo/podcasts/templates/episode.html:103 +#, fuzzy +msgid "Episode History" +msgstr "Os Melhores Episódios" + +#: mygpo/podcasts/templates/podcast-base.html:39 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Unnamed Podcast" +msgstr "Podcasts Sem Nome" + +#: mygpo/podcasts/templates/podcast-base.html:44 +#: mygpo/publisher/templates/publisher/podcast.html:29 +msgid "by" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:50 +#: mygpo/publisher/templates/publisher/podcast.html:35 +#: mygpo/share/templates/share/favorites.html:29 +msgid "Feed" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:65 +#: mygpo/publisher/templates/publisher/podcast.html:44 +#, fuzzy +msgid "subscribers" +msgstr "Subscritores" + +#: mygpo/podcasts/templates/podcast.html:48 +#, fuzzy +#| msgid "Subscribe" +msgid "Login to Subscribe" +msgstr "Subscrever" + +#: mygpo/podcasts/templates/podcast.html:59 +#: mygpo/suggestions/templates/suggestions.html:31 +#: mygpo/web/templates/subscribe.html:11 mygpo/web/templates/subscribe.html:36 +msgid "Subscribe" +msgstr "Subscrever" + +#: mygpo/podcasts/templates/podcast.html:67 mygpo/web/templates/device.html:31 +msgid "Unsubscribe" +msgstr "Não Subscrito" + +#: mygpo/podcasts/templates/podcast.html:78 +#, fuzzy +msgid "Subscribe on all devices" +msgstr "Subscrever novo(s) Podcast(s)" + +#: mygpo/podcasts/templates/podcast.html:110 +#, fuzzy +msgid "Unsubscribe from all devices " +msgstr "Subscrever novo(s) Podcast(s)" + +#: mygpo/podcasts/templates/podcast.html:149 +#: mygpo/podcasts/templates/podcast.html:164 +#, fuzzy +#| msgid "My Tags" +msgid "Tags" +msgstr "As minhas marcações" + +#: mygpo/podcasts/templates/podcast.html:199 +#, fuzzy +msgid "Older Episodes" +msgstr "Episódios" + #: mygpo/publisher/forms.py:5 #: mygpo/publisher/templates/publisher/episode.html:60 msgid "URL" @@ -790,7 +923,7 @@ msgid "Link to" msgstr "Ligação a" #: mygpo/publisher/templates/link.html:17 -#: mygpo/publisher/templates/publisher/podcast.html:171 +#: mygpo/publisher/templates/publisher/podcast.html:209 #, fuzzy, python-format msgid "" "You can paste this code on your website, so users of %(sitename)s can " @@ -895,31 +1028,6 @@ msgstr "" msgid "Unnamed Episode" msgstr "Episódio Sem Nome" -#: mygpo/publisher/templates/publisher/episode.html:31 -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/info.html:9 -#: mygpo/publisher/templates/publisher/info.html:26 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/episode.html:85 mygpo/web/templates/episodes.html:27 -#: mygpo/web/templates/podcast.html:39 -msgid "Publisher Pages" -msgstr "Páginas dos Publicadores" - -#: mygpo/publisher/templates/publisher/episode.html:40 -#: mygpo/publisher/templates/publisher/podcast.html:38 -#: mygpo/web/templates/episode-history.html:57 -#: mygpo/web/templates/episode.html:58 mygpo/web/templates/podcast-base.html:54 -#, fuzzy -msgid "Website" -msgstr "sítio web" - -#: mygpo/publisher/templates/publisher/episode.html:45 -#: mygpo/web/templates/episode-history.html:62 -#: mygpo/web/templates/episode.html:63 -#, fuzzy -msgid "listeners" -msgstr "Ouvintes" - #: mygpo/publisher/templates/publisher/episode.html:55 #, fuzzy msgid "Episode List" @@ -937,7 +1045,7 @@ msgid "" msgstr "" #: mygpo/publisher/templates/publisher/episode.html:72 -#: mygpo/publisher/templates/publisher/podcast.html:91 +#: mygpo/publisher/templates/publisher/podcast.html:129 #: mygpo/web/templates/account.html:65 mygpo/web/templates/account.html:142 #: mygpo/web/templates/device-edit.html:53 msgid "Save" @@ -952,12 +1060,6 @@ msgstr "Estado dos Episódios" msgid "Last update: " msgstr "" -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/podcast-base.html:39 -msgid "Unnamed Podcast" -msgstr "Podcasts Sem Nome" - #: mygpo/publisher/templates/publisher/episodes.html:24 msgid "Return to Podcast Page" msgstr "Voltar à Página de Podcasts" @@ -1109,77 +1211,59 @@ msgid "" "podcast - especially for users of mobile phones." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:28 -#: mygpo/web/templates/podcast-base.html:44 -msgid "by" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:34 -#: mygpo/share/templates/share/favorites.html:29 -#: mygpo/web/templates/podcast-base.html:50 -msgid "Feed" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:43 -#: mygpo/web/templates/podcast-base.html:65 -#, fuzzy -msgid "subscribers" -msgstr "Subscritores" - -#: mygpo/publisher/templates/publisher/podcast.html:48 +#: mygpo/publisher/templates/publisher/podcast.html:49 #, python-format msgid "" "This is the publisher page of %(ptitle)s. You can see some " "stats and provide additional data for the podcast page." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:49 +#: mygpo/publisher/templates/publisher/podcast.html:50 #, fuzzy msgid "Go to Podcast Page" msgstr "Voltar à Página de Podcasts" -#: mygpo/publisher/templates/publisher/podcast.html:56 +#: mygpo/publisher/templates/publisher/podcast.html:57 msgid "The podcast information is regularly retrieved from the podcast feed" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:59 -msgid "Timing" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:61 +#: mygpo/publisher/templates/publisher/podcast.html:60 #, fuzzy -msgid "Last update:" -msgstr "Lista de Dispositivos" +msgid "Updates" +msgstr "Actualizar da Fonte" #: mygpo/publisher/templates/publisher/podcast.html:62 #, fuzzy msgid "Update interval:" msgstr "Actualizar da Fonte" -#: mygpo/publisher/templates/publisher/podcast.html:63 -#, fuzzy -msgid "Next update:" -msgstr "Lista de Dispositivos" +#: mygpo/publisher/templates/publisher/podcast.html:86 +msgid "Successful" +msgstr "" + +#: mygpo/publisher/templates/publisher/podcast.html:88 +msgid "Error" +msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:68 +#: mygpo/publisher/templates/publisher/podcast.html:106 #, fuzzy msgid "Update now" msgstr "Actualizar da Fonte" -#: mygpo/publisher/templates/publisher/podcast.html:79 mygpo/web/forms.py:70 +#: mygpo/publisher/templates/publisher/podcast.html:117 mygpo/web/forms.py:70 #: mygpo/web/templates/base.html:160 mygpo/web/templates/home.html:180 msgid "Twitter" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:97 +#: mygpo/publisher/templates/publisher/podcast.html:135 msgid "Feed Check" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:105 +#: mygpo/publisher/templates/publisher/podcast.html:143 msgid "PubSubHubbub" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:114 +#: mygpo/publisher/templates/publisher/podcast.html:152 #, python-format msgid "" "If you publish your podcast feed through a %(hub)s and should " "update immediatelly for each new episode." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:123 +#: mygpo/publisher/templates/publisher/podcast.html:161 #, python-format msgid "" "Your podcast is published through %(hub)s but our " "subscription has not yet been verified." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:128 +#: mygpo/publisher/templates/publisher/podcast.html:166 #, python-format msgid "" "We did not find a hub in your podcast feed. Your feed is updated regularly, " "but there might be some delay until a new episode shows up on %(sitename)s." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:140 +#: mygpo/publisher/templates/publisher/podcast.html:178 msgid "License Information" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:148 +#: mygpo/publisher/templates/publisher/podcast.html:186 #, python-format msgid "" "You should include license information in your feed so that users and " "%(sitename)s can know, under which conditions your content can be used." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:152 +#: mygpo/publisher/templates/publisher/podcast.html:190 #, python-format msgid "" "We found the following license in your podcast: " "%(license)s" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:156 +#: mygpo/publisher/templates/publisher/podcast.html:194 msgid "" "We did not find a license in your podcast feed. Refer to definicões de conta para obter a " "ligação." -#: mygpo/web/templatetags/devices.py:31 +#: mygpo/web/templatetags/devices.py:32 msgid "Unknown" msgstr "Desconhecido" @@ -2726,11 +2742,16 @@ msgstr "Privacidade" msgid "Link to gpodder.net" msgstr "Ligação para gpodder.net" -#: mygpo/web/templatetags/time.py:38 +#: mygpo/web/templatetags/time.py:42 #, python-brace-format msgid "{h}h {m}m {s}s" msgstr "" +#: mygpo/web/templatetags/time.py:44 +#, python-brace-format +msgid "{m}m {s}s" +msgstr "" + #: mygpo/web/utils.py:281 #, python-format msgid "%(weeks)d week" @@ -2756,6 +2777,14 @@ msgstr[1] "" msgid "another site" msgstr "outo sítio" +#, fuzzy +#~ msgid "Last update:" +#~ msgstr "Lista de Dispositivos" + +#, fuzzy +#~ msgid "Next update:" +#~ msgstr "Lista de Dispositivos" + #, fuzzy #~ msgid "No Payment URL available" #~ msgstr "Nenhum Logo Disponível" diff --git a/mygpo/locale/tr_TR/LC_MESSAGES/django.po b/mygpo/locale/tr_TR/LC_MESSAGES/django.po index f2e880e8f..f8fe14e48 100644 --- a/mygpo/locale/tr_TR/LC_MESSAGES/django.po +++ b/mygpo/locale/tr_TR/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: gpodder.net\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-10-12 18:58+0000\n" +"POT-Creation-Date: 2018-06-29 20:11+0000\n" "PO-Revision-Date: 2012-10-13 12:53+0000\n" "Last-Translator: zeugma \n" "Language-Team: Turkish (Turkey) (http://www.transifex.com/projects/p/mygpo/" @@ -102,46 +102,50 @@ msgstr "Türü" msgid "Episode URLs" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:9 -#: mygpo/administration/templates/admin/hostinfo.html:12 +#: mygpo/administration/templates/admin/hostinfo.html:10 +#: mygpo/administration/templates/admin/hostinfo.html:13 #: mygpo/administration/templates/admin/overview.html:17 msgid "Host Information" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:20 +#: mygpo/administration/templates/admin/hostinfo.html:21 msgid "mygpo Version" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:31 +#: mygpo/administration/templates/admin/hostinfo.html:32 msgid "Base Directory" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:36 +#: mygpo/administration/templates/admin/hostinfo.html:37 #, fuzzy msgid "Hostname" msgstr "Kullanıcı adı" -#: mygpo/administration/templates/admin/hostinfo.html:41 +#: mygpo/administration/templates/admin/hostinfo.html:42 msgid "Django Version" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:46 +#: mygpo/administration/templates/admin/hostinfo.html:47 msgid "Feed Update Queue" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:49 +#: mygpo/administration/templates/admin/hostinfo.html:50 msgid "min ahead" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:51 +#: mygpo/administration/templates/admin/hostinfo.html:52 msgid "min behind" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:59 +#: mygpo/administration/templates/admin/hostinfo.html:60 msgid "Number of podcasts with outdated search index" msgstr "" -#: mygpo/administration/templates/admin/hostinfo.html:66 +#: mygpo/administration/templates/admin/hostinfo.html:69 +msgid "Average podcast update duration" +msgstr "" + +#: mygpo/administration/templates/admin/hostinfo.html:76 msgid "Scheduled Celery Tasks" msgstr "" @@ -149,7 +153,7 @@ msgstr "" #: mygpo/administration/templates/admin/make-publisher-input.html:12 #: mygpo/administration/templates/admin/make-publisher-result.html:9 #: mygpo/administration/templates/admin/make-publisher-result.html:12 -#: mygpo/administration/views.py:391 +#: mygpo/administration/views.py:404 msgid "Publisher Permissions" msgstr "" @@ -260,20 +264,20 @@ msgstr "" msgid "User-Agent" msgstr "" -#: mygpo/administration/views.py:140 mygpo/administration/views.py:162 +#: mygpo/administration/views.py:153 mygpo/administration/views.py:175 #, python-brace-format msgid "No podcast with URL {url}" msgstr "" -#: mygpo/administration/views.py:316 +#: mygpo/administration/views.py:329 msgid "Provide either username or email address" msgstr "" -#: mygpo/administration/views.py:322 +#: mygpo/administration/views.py:335 msgid "No user found" msgstr "" -#: mygpo/administration/views.py:327 +#: mygpo/administration/views.py:340 #, python-brace-format msgid "User {username} ({email}) activated" msgstr "" @@ -311,16 +315,16 @@ msgstr "" msgid "%(username)s's Subscription List" msgstr "" -#: mygpo/api/simple.py:237 +#: mygpo/api/simple.py:240 #, python-format msgid "gpodder.net - Top %(count)d" msgstr "" -#: mygpo/api/simple.py:272 +#: mygpo/api/simple.py:275 msgid "gpodder.net - Search" msgstr "gpodder.net - Arama" -#: mygpo/api/simple.py:289 +#: mygpo/api/simple.py:292 #, python-format msgid "gpodder.net - %(count)d Suggestions" msgstr "" @@ -343,7 +347,7 @@ msgid "Explore" msgstr "" #: mygpo/directory/templates/carousel.html:81 -#: mygpo/web/templates/episode.html:150 +#: mygpo/podcasts/templates/episode.html:150 msgid "..." msgstr "" @@ -467,11 +471,11 @@ msgid "Listeners" msgstr "" #: mygpo/directory/templates/episode_toplist.html:36 +#: mygpo/history/templates/episode-history.html:51 +#: mygpo/podcasts/templates/episode.html:51 #: mygpo/publisher/templates/publisher/episode.html:33 #: mygpo/share/templates/userpage.html:118 #: mygpo/share/templates/userpage.html:138 -#: mygpo/web/templates/episode-history.html:50 -#: mygpo/web/templates/episode.html:51 msgid "from" msgstr "" @@ -541,9 +545,9 @@ msgid "User" msgstr "" #: mygpo/directory/templates/podcast_lists.html:25 +#: mygpo/history/templates/episode-history.html:53 +#: mygpo/podcasts/templates/episode.html:53 #: mygpo/publisher/templates/publisher/episode.html:35 -#: mygpo/web/templates/episode-history.html:52 -#: mygpo/web/templates/episode.html:53 msgid "Download" msgstr "" @@ -600,10 +604,48 @@ msgstr "" msgid "%d podcasts added" msgstr "Bir podcast ekle" +#: mygpo/history/templates/episode-history.html:47 +#: mygpo/history/templates/episode-history.html:81 +#: mygpo/web/templates/device.html:73 mygpo/web/templatetags/menu.py:51 +msgid "History" +msgstr "Geçmiş" + +#: mygpo/history/templates/episode-history.html:58 +#: mygpo/podcasts/templates/episode.html:58 +#: mygpo/podcasts/templates/podcast-base.html:54 +#: mygpo/publisher/templates/publisher/episode.html:40 +#: mygpo/publisher/templates/publisher/podcast.html:39 +#, fuzzy +msgid "Website" +msgstr "web sitesi" + +#: mygpo/history/templates/episode-history.html:63 +#: mygpo/podcasts/templates/episode.html:63 +#: mygpo/publisher/templates/publisher/episode.html:45 +msgid "listeners" +msgstr "" + +#: mygpo/history/templates/episode-history.html:84 +msgid "Time" +msgstr "Zaman" + +#: mygpo/history/templates/episode-history.html:85 +msgid "Action" +msgstr "Eylem" + +#: mygpo/history/templates/episode-history.html:86 +#: mygpo/web/templatetags/menu.py:50 +msgid "Device" +msgstr "Cihaz" + +#: mygpo/history/templates/episode-history.html:127 +msgid "Add" +msgstr "Ekle" + #: mygpo/history/templates/history.html:12 #: mygpo/history/templates/history.html:15 -#: mygpo/web/templates/podcast-history.html:32 -#: mygpo/web/templates/podcast.html:143 +#: mygpo/history/templates/podcast-history.html:32 +#: mygpo/podcasts/templates/podcast.html:143 msgid "Subscription History" msgstr "" @@ -636,6 +678,10 @@ msgstr "" msgid "Earlier" msgstr "" +#: mygpo/history/templates/podcast-history.html:47 +msgid "no history yet" +msgstr "" + #: mygpo/podcastlists/templates/list.html:28 #, python-format msgid "\"%(list_title)s\" by %(ownername)s" @@ -727,17 +773,97 @@ msgstr "" msgid "Edit" msgstr "Düzenle" -#: mygpo/podcasts/models.py:688 +#: mygpo/podcasts/models.py:704 #, fuzzy #| msgid "Unknown" msgid "Unknown Podcast" msgstr "Bilinmeyen" -#: mygpo/podcasts/models.py:690 +#: mygpo/podcasts/models.py:706 #, python-brace-format msgid "Unknown Podcast from {domain}" msgstr "" +#: mygpo/podcasts/templates/episode.html:85 +#: mygpo/podcasts/templates/episodes.html:27 +#: mygpo/podcasts/templates/podcast.html:39 +#: mygpo/publisher/templates/publisher/episode.html:31 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/info.html:9 +#: mygpo/publisher/templates/publisher/info.html:26 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Publisher Pages" +msgstr "" + +#: mygpo/podcasts/templates/episode.html:91 +msgid "Remove Favorite" +msgstr "" + +#: mygpo/podcasts/templates/episode.html:95 +msgid "Favorite" +msgstr "Favori" + +#: mygpo/podcasts/templates/episode.html:103 +#, fuzzy +#| msgid "History" +msgid "Episode History" +msgstr "Geçmiş" + +#: mygpo/podcasts/templates/podcast-base.html:39 +#: mygpo/publisher/templates/publisher/episodes.html:23 +#: mygpo/publisher/templates/publisher/podcast.html:27 +msgid "Unnamed Podcast" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:44 +#: mygpo/publisher/templates/publisher/podcast.html:29 +msgid "by" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:50 +#: mygpo/publisher/templates/publisher/podcast.html:35 +#: mygpo/share/templates/share/favorites.html:29 +msgid "Feed" +msgstr "" + +#: mygpo/podcasts/templates/podcast-base.html:65 +#: mygpo/publisher/templates/publisher/podcast.html:44 +msgid "subscribers" +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:48 +msgid "Login to Subscribe" +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:59 +#: mygpo/suggestions/templates/suggestions.html:31 +#: mygpo/web/templates/subscribe.html:11 mygpo/web/templates/subscribe.html:36 +msgid "Subscribe" +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:67 mygpo/web/templates/device.html:31 +msgid "Unsubscribe" +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:78 +msgid "Subscribe on all devices" +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:110 +msgid "Unsubscribe from all devices " +msgstr "" + +#: mygpo/podcasts/templates/podcast.html:149 +#: mygpo/podcasts/templates/podcast.html:164 +#, fuzzy +#| msgid "My Tags" +msgid "Tags" +msgstr "Etiketlerim" + +#: mygpo/podcasts/templates/podcast.html:199 +msgid "Older Episodes" +msgstr "" + #: mygpo/publisher/forms.py:5 #: mygpo/publisher/templates/publisher/episode.html:60 msgid "URL" @@ -752,7 +878,7 @@ msgid "Link to" msgstr "" #: mygpo/publisher/templates/link.html:17 -#: mygpo/publisher/templates/publisher/podcast.html:171 +#: mygpo/publisher/templates/publisher/podcast.html:209 #, python-format msgid "" "You can paste this code on your website, so users of %(sitename)s can " @@ -840,30 +966,6 @@ msgstr "" msgid "Unnamed Episode" msgstr "" -#: mygpo/publisher/templates/publisher/episode.html:31 -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/info.html:9 -#: mygpo/publisher/templates/publisher/info.html:26 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/episode.html:85 mygpo/web/templates/episodes.html:27 -#: mygpo/web/templates/podcast.html:39 -msgid "Publisher Pages" -msgstr "" - -#: mygpo/publisher/templates/publisher/episode.html:40 -#: mygpo/publisher/templates/publisher/podcast.html:38 -#: mygpo/web/templates/episode-history.html:57 -#: mygpo/web/templates/episode.html:58 mygpo/web/templates/podcast-base.html:54 -#, fuzzy -msgid "Website" -msgstr "web sitesi" - -#: mygpo/publisher/templates/publisher/episode.html:45 -#: mygpo/web/templates/episode-history.html:62 -#: mygpo/web/templates/episode.html:63 -msgid "listeners" -msgstr "" - #: mygpo/publisher/templates/publisher/episode.html:55 msgid "Episode List" msgstr "" @@ -880,7 +982,7 @@ msgid "" msgstr "" #: mygpo/publisher/templates/publisher/episode.html:72 -#: mygpo/publisher/templates/publisher/podcast.html:91 +#: mygpo/publisher/templates/publisher/podcast.html:129 #: mygpo/web/templates/account.html:65 mygpo/web/templates/account.html:142 #: mygpo/web/templates/device-edit.html:53 msgid "Save" @@ -894,12 +996,6 @@ msgstr "" msgid "Last update: " msgstr "" -#: mygpo/publisher/templates/publisher/episodes.html:23 -#: mygpo/publisher/templates/publisher/podcast.html:26 -#: mygpo/web/templates/podcast-base.html:39 -msgid "Unnamed Podcast" -msgstr "" - #: mygpo/publisher/templates/publisher/episodes.html:24 msgid "Return to Podcast Page" msgstr "" @@ -1036,71 +1132,56 @@ msgid "" "podcast - especially for users of mobile phones." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:28 -#: mygpo/web/templates/podcast-base.html:44 -msgid "by" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:34 -#: mygpo/share/templates/share/favorites.html:29 -#: mygpo/web/templates/podcast-base.html:50 -msgid "Feed" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:43 -#: mygpo/web/templates/podcast-base.html:65 -msgid "subscribers" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:48 +#: mygpo/publisher/templates/publisher/podcast.html:49 #, python-format msgid "" "This is the publisher page of %(ptitle)s. You can see some " "stats and provide additional data for the podcast page." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:49 +#: mygpo/publisher/templates/publisher/podcast.html:50 msgid "Go to Podcast Page" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:56 +#: mygpo/publisher/templates/publisher/podcast.html:57 msgid "The podcast information is regularly retrieved from the podcast feed" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:59 -msgid "Timing" -msgstr "" - -#: mygpo/publisher/templates/publisher/podcast.html:61 -msgid "Last update:" -msgstr "" +#: mygpo/publisher/templates/publisher/podcast.html:60 +#, fuzzy +msgid "Updates" +msgstr "Bir podcast ekle" #: mygpo/publisher/templates/publisher/podcast.html:62 msgid "Update interval:" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:63 -msgid "Next update:" +#: mygpo/publisher/templates/publisher/podcast.html:86 +msgid "Successful" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:68 +#: mygpo/publisher/templates/publisher/podcast.html:88 +msgid "Error" +msgstr "" + +#: mygpo/publisher/templates/publisher/podcast.html:106 msgid "Update now" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:79 mygpo/web/forms.py:70 +#: mygpo/publisher/templates/publisher/podcast.html:117 mygpo/web/forms.py:70 #: mygpo/web/templates/base.html:160 mygpo/web/templates/home.html:180 msgid "Twitter" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:97 +#: mygpo/publisher/templates/publisher/podcast.html:135 msgid "Feed Check" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:105 +#: mygpo/publisher/templates/publisher/podcast.html:143 msgid "PubSubHubbub" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:114 +#: mygpo/publisher/templates/publisher/podcast.html:152 #, python-format msgid "" "If you publish your podcast feed through a %(hub)s and should " "update immediatelly for each new episode." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:123 +#: mygpo/publisher/templates/publisher/podcast.html:161 #, python-format msgid "" "Your podcast is published through %(hub)s but our " "subscription has not yet been verified." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:128 +#: mygpo/publisher/templates/publisher/podcast.html:166 #, python-format msgid "" "We did not find a hub in your podcast feed. Your feed is updated regularly, " "but there might be some delay until a new episode shows up on %(sitename)s." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:140 +#: mygpo/publisher/templates/publisher/podcast.html:178 msgid "License Information" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:148 +#: mygpo/publisher/templates/publisher/podcast.html:186 #, python-format msgid "" "You should include license information in your feed so that users and " "%(sitename)s can know, under which conditions your content can be used." msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:152 +#: mygpo/publisher/templates/publisher/podcast.html:190 #, python-format msgid "" "We found the following license in your podcast: " "%(license)s" msgstr "" -#: mygpo/publisher/templates/publisher/podcast.html:156 +#: mygpo/publisher/templates/publisher/podcast.html:194 msgid "" "We did not find a license in your podcast feed. Refer to " msgstr "" -#: mygpo/web/templatetags/devices.py:31 +#: mygpo/web/templatetags/devices.py:32 msgid "Unknown" msgstr "Bilinmeyen" @@ -2536,11 +2543,16 @@ msgstr "Gizlilik" msgid "Link to gpodder.net" msgstr "" -#: mygpo/web/templatetags/time.py:38 +#: mygpo/web/templatetags/time.py:42 #, python-brace-format msgid "{h}h {m}m {s}s" msgstr "" +#: mygpo/web/templatetags/time.py:44 +#, python-brace-format +msgid "{m}m {s}s" +msgstr "" + #: mygpo/web/utils.py:281 #, python-format msgid "%(weeks)d week" diff --git a/mygpo/maintenance/management/podcastcmd.py b/mygpo/maintenance/management/podcastcmd.py index f59113204..b71cb6c34 100644 --- a/mygpo/maintenance/management/podcastcmd.py +++ b/mygpo/maintenance/management/podcastcmd.py @@ -18,7 +18,7 @@ def add_arguments(self, parser): parser.add_argument('--update-new', action='store_true', dest='new', default=False, help="Update all podcasts with new Episodes"), - parser.add_argument('--max', action='store', dest='max', type='int', + parser.add_argument('--max', action='store', dest='max', type=int, default=0, help="Set how many feeds should be updated at maximum"), parser.add_argument('--random', action='store_true', dest='random', @@ -27,6 +27,7 @@ def add_arguments(self, parser): parser.add_argument('--next', action='store_true', dest='next', default=False, help="Podcasts that are due to be updated next"), + parser.add_argument('urls', nargs='+', type=str) def get_podcasts(self, *args, **options): return chain.from_iterable(self._get_podcasts(*args, **options)) @@ -54,15 +55,12 @@ def _get_podcasts(self, *args, **options): podcasts = Podcast.objects.all().order_by_next_update()[:max_podcasts] yield (p.url for p in podcasts) - - if args: - yield args - if options.get('urls'): yield options.get('urls') - if not args and not options.get('toplist') and not options.get('new') \ - and not options.get('random') and not options.get('next'): + if not options.get('urls') and not options.get('toplist') and \ + not options.get('new') and not options.get('random') and \ + not options.get('next'): query = Podcast.objects.order_by('last_update') podcasts = query.select_related('urls')[:max_podcasts] yield (p.url for p in podcasts) diff --git a/mygpo/maintenance/tests.py b/mygpo/maintenance/tests.py index defa14d45..544d7d351 100644 --- a/mygpo/maintenance/tests.py +++ b/mygpo/maintenance/tests.py @@ -22,22 +22,22 @@ def setUp(self): self.podcast1 = Podcast.objects.get_or_create_for_url( 'http://example.com/simple-merge-test-feed.rss', defaults={'title': 'Podcast 1'}, - ) + ).object self.podcast2 = Podcast.objects.get_or_create_for_url( 'http://simple-merge-test.org/podcast/', defaults={'title': 'Podcast 2'}, - ) + ).object self.episode1 = Episode.objects.get_or_create_for_url( self.podcast1, 'http://example.com/simple-merge-test-episode1.mp3', defaults={ 'title': 'Episode 1 A', - }) + }).object self.episode2 = Episode.objects.get_or_create_for_url( self.podcast2, 'http://example.com/simple-merge-test-episode1.mp3', defaults={ 'title': 'Episode 1 B', - }) + }).object def test_merge_podcasts(self): # decide which episodes to merge @@ -55,22 +55,22 @@ def setUp(self): self.podcast1 = Podcast.objects.get_or_create_for_url( 'http://example.com/merge-test-feed.rss', defaults={'title': 'Podcast 1'}, - ) + ).object self.podcast2 = Podcast.objects.get_or_create_for_url( 'http://merge-test.org/podcast/', defaults={'title': 'Podcast 2'}, - ) + ).object self.episode1 = Episode.objects.get_or_create_for_url( self.podcast1, 'http://example.com/merge-test-episode1.mp3', defaults={ 'title': 'Episode 1 A', - }) + }).object self.episode2 = Episode.objects.get_or_create_for_url( self.podcast2, 'http://example.com/merge-test-episode1.mp3', defaults={ 'title': 'Episode 1 B', - }) + }).object User = get_user_model() self.user = User(username='test-merge') @@ -123,38 +123,38 @@ def setUp(self): defaults={ 'title': 'Podcast 1', }, - ) + ).object self.podcast2 = Podcast.objects.get_or_create_for_url( 'http://test.org/group-merge-podcast/', defaults={ 'title': 'Podcast 2', }, - ) + ).object self.podcast3 = Podcast.objects.get_or_create_for_url( 'http://group-test.org/feed/', defaults={ 'title': 'Podcast 3', }, - ) + ).object self.episode1 = Episode.objects.get_or_create_for_url( self.podcast1, 'http://example.com/group-merge-episode1.mp3', defaults={ 'title': 'Episode 1 A', }, - ) + ).object self.episode2 = Episode.objects.get_or_create_for_url( self.podcast2, 'http://example.com/group-merge-episode1.mp3', defaults={ 'title': 'Episode 1 B', }, - ) + ).object self.episode3 = Episode.objects.get_or_create_for_url( self.podcast3, 'http://example.com/group-merge-media.mp3', defaults={ 'title': 'Episode 2', }, - ) + ).object self.podcast2.group_with(self.podcast3, 'My Group', 'Feed1', 'Feed2') diff --git a/mygpo/moauth/views.py b/mygpo/moauth/views.py index 843162f5d..bba8ef7c8 100644 --- a/mygpo/moauth/views.py +++ b/mygpo/moauth/views.py @@ -4,7 +4,7 @@ import urllib.parse from django.db import IntegrityError -from django.core.urlresolvers import reverse +from django.urls import reverse from django.shortcuts import render from django.views.generic.base import RedirectView from django.views.generic.base import View diff --git a/mygpo/podcastlists/urls.py b/mygpo/podcastlists/urls.py index 43002d89c..110e21add 100644 --- a/mygpo/podcastlists/urls.py +++ b/mygpo/podcastlists/urls.py @@ -1,50 +1,61 @@ -from django.conf.urls import url +from django.urls import path, register_converter, include from . import views +from mygpo.users import converters -urlpatterns = [ - url(r'^share/lists/$', - views.lists_own, - name='lists-overview'), +register_converter(converters.UsernameConverter, 'username') - url(r'^share/lists/create$', - views.create_list, - name='list-create'), - url(r'^user/(?P[\w.+-]+)/lists/$', +userpatterns = [ + + path('lists/', views.lists_user, name='lists-user'), - url(r'^user/(?P[\w.+-]+)/list/(?P[\w-]+)$', + path('list/', views.list_show, name='list-show'), - url(r'^user/(?P[\w.+-]+)/list/(?P[\w-]+)\.opml$', + path('list/.opml', views.list_opml, name='list-opml'), - url(r'^user/(?P[\w.+-]+)/list/(?P[\w-]+)/search$', + path('list//search', views.search, name='list-search'), - url(r'^user/(?P[\w.+-]+)/list/(?P[\w-]+)/add/' - '(?P\w+)$', + path('list//add/', views.add_podcast, name='list-add-podcast'), - url(r'^user/(?P[\w.+-]+)/list/(?P[\w-]+)/remove/' - '(?P\d+)$', + path('list//remove/', views.remove_podcast, name='list-remove-podcast'), - url(r'^user/(?P[\w.+-]+)/list/(?P[\w-]+)/delete$', + path('list//delete', views.delete_list, name='list-delete'), - url(r'^user/(?P[\w.+-]+)/list/(?P[\w-]+)/rate$', + path('list//rate', views.rate_list, name='list-rate'), ] + + +urlpatterns = [ + + path('share/lists/', + views.lists_own, + name='lists-overview'), + + path('share/lists/create', + views.create_list, + name='list-create'), + + path('user//', + include(userpatterns)), + +] diff --git a/mygpo/podcasts/migrations/0040_podcast_update_interval_factor.py b/mygpo/podcasts/migrations/0040_podcast_update_interval_factor.py new file mode 100644 index 000000000..57447b2dc --- /dev/null +++ b/mygpo/podcasts/migrations/0040_podcast_update_interval_factor.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.5 on 2017-12-03 19:53 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('podcasts', '0039_podcast_search_index_uptodate'), + ] + + operations = [ + migrations.AddField( + model_name='podcast', + name='update_interval_factor', + field=models.FloatField(default=1), + ), + ] diff --git a/mygpo/podcasts/models.py b/mygpo/podcasts/models.py index d5e911516..f4596181f 100644 --- a/mygpo/podcasts/models.py +++ b/mygpo/podcasts/models.py @@ -1,5 +1,5 @@ - +import collections import uuid import re from datetime import timedelta @@ -22,6 +22,9 @@ logger = logging.getLogger(__name__) +GetCreateResult = collections.namedtuple('GetCreateResult', 'object created') + + # default podcast update interval in hours DEFAULT_UPDATE_INTERVAL = 7 * 24 @@ -338,16 +341,20 @@ def license(self, license_url=None): def order_by_next_update(self): """ Sort podcasts by next scheduled update """ - NEXTUPDATE = "last_update + (update_interval || ' hours')::INTERVAL" - q = self.extra(select={'next_update': NEXTUPDATE}) - return q.order_by('next_update') + NEXTUPDATE = ("last_update + (update_interval * " + "update_interval_factor || ' hours')::INTERVAL") + q = self.extra(select={'_next_update': NEXTUPDATE}) + return q.order_by('_next_update') @property def next_update(self): - return self.last_update + timedelta(hours=self.update_interval) + interval = (timedelta(hours=self.update_interval) * + self.update_interval_factor) + return self.last_update + interval def next_update_between(self, start, end): - NEXTUPDATE_BETWEEN = ("(last_update + (update_interval || " + NEXTUPDATE_BETWEEN = ("(last_update + (update_interval * " + " update_interval_factor || " "' hours')::INTERVAL) BETWEEN %s AND %s") return self.extra( where=[NEXTUPDATE_BETWEEN], params=[start, end] @@ -395,9 +402,11 @@ def get_or_create_for_url(self, url, defaults={}): url = utils.to_maxlength(URL, 'url', url) try: # try to fetch the podcast - return Podcast.objects.get(urls__url=url, - urls__scope='', - ) + podcast = Podcast.objects.get(urls__url=url, + urls__scope='', + ) + return GetCreateResult(podcast, False) + except Podcast.DoesNotExist: # episode did not exist, try to create it try: @@ -408,13 +417,14 @@ def get_or_create_for_url(self, url, defaults={}): scope='', content_object=podcast, ) - return podcast + return GetCreateResult(podcast, True) # URL could not be created, so it was created since the first get except IntegrityError: - return Podcast.objects.get(urls__url=url, - urls__scope='', - ) + podcast = Podcast.objects.get(urls__url=url, + urls__scope='', + ) + return GetCreateResult(podcast, False) class URL(OrderedModel, ScopedModel): @@ -564,9 +574,15 @@ class Podcast(UUIDModel, TitleModel, DescriptionModel, LinkModel, latest_episode_timestamp = models.DateTimeField(null=True) episode_count = models.PositiveIntegerField(default=0) hub = models.URLField(null=True) + + # Interval between episodes, within a specified range update_interval = models.PositiveSmallIntegerField(null=False, default=DEFAULT_UPDATE_INTERVAL) + # factor to increase update_interval if an update does not find any + # new episodes + update_interval_factor = models.FloatField(default=1) + # "order" value of the most recent episode (will be the highest of all) max_episode_order = models.PositiveIntegerField(null=True, default=None) @@ -690,6 +706,12 @@ def display_title(self): return _('Unknown Podcast from {domain}'.format( domain=utils.get_domain(self.url))) + @property + def next_update(self): + interval = (timedelta(hours=self.update_interval) * + self.update_interval_factor) + return self.last_update + interval + class EpisodeQuerySet(MergedUUIDQuerySet): """ QuerySet for Episodes """ @@ -723,6 +745,7 @@ def get_or_create_for_url(self, podcast, url, defaults={}): try: url = URL.objects.get(url=url, scope=podcast.as_scope) + created = False episode = url.content_object if episode is None: @@ -734,8 +757,9 @@ def get_or_create_for_url(self, podcast, url, defaults={}): url.content_object = episode url.save() + created = True - return episode + return GetCreateResult(episode, created) except URL.DoesNotExist: @@ -758,13 +782,14 @@ def get_or_create_for_url(self, podcast, url, defaults={}): Podcast.objects.filter(pk=podcast.pk)\ .update(episode_count=F('episode_count')+1) - return episode + return GetCreateResult(episode, True) # URL could not be created, so it was created since the first get except IntegrityError: - return Episode.objects.get(urls__url=url, - urls__scope=podcast.as_scope, - ) + episode = Episode.objects.get(urls__url=url, + urls__scope=podcast.as_scope, + ) + return GetCreateResult(episode, False) class Episode(UUIDModel, TitleModel, DescriptionModel, LinkModel, diff --git a/mygpo/web/templates/components/episode-box.html b/mygpo/podcasts/templates/components/episode-box.html similarity index 100% rename from mygpo/web/templates/components/episode-box.html rename to mygpo/podcasts/templates/components/episode-box.html diff --git a/mygpo/web/templates/episode.html b/mygpo/podcasts/templates/episode.html similarity index 100% rename from mygpo/web/templates/episode.html rename to mygpo/podcasts/templates/episode.html diff --git a/mygpo/web/templates/episodes.html b/mygpo/podcasts/templates/episodes.html similarity index 100% rename from mygpo/web/templates/episodes.html rename to mygpo/podcasts/templates/episodes.html diff --git a/mygpo/web/templates/podcast-base.html b/mygpo/podcasts/templates/podcast-base.html similarity index 100% rename from mygpo/web/templates/podcast-base.html rename to mygpo/podcasts/templates/podcast-base.html diff --git a/mygpo/web/templates/podcast.html b/mygpo/podcasts/templates/podcast.html similarity index 100% rename from mygpo/web/templates/podcast.html rename to mygpo/podcasts/templates/podcast.html diff --git a/mygpo/podcasts/tests.py b/mygpo/podcasts/tests.py index f0b58307c..9dd3be800 100644 --- a/mygpo/podcasts/tests.py +++ b/mygpo/podcasts/tests.py @@ -33,8 +33,8 @@ def test_next_update(self): def test_get_or_create_for_url(self): """ Test that get_or_create_for_url returns existing Podcast """ URL = 'http://example.com/get_or_create.rss' - p1 = Podcast.objects.get_or_create_for_url(URL) - p2 = Podcast.objects.get_or_create_for_url(URL) + p1 = Podcast.objects.get_or_create_for_url(URL).object + p2 = Podcast.objects.get_or_create_for_url(URL).object self.assertEqual(p1.pk, p2.pk) def test_episode_count(self): @@ -43,7 +43,7 @@ def test_episode_count(self): EPISODE_URL = 'http://example.com/episode%d.mp3' NUM_EPISODES=3 - p = Podcast.objects.get_or_create_for_url(PODCAST_URL) + p = Podcast.objects.get_or_create_for_url(PODCAST_URL).object for n in range(NUM_EPISODES): Episode.objects.get_or_create_for_url(p, EPISODE_URL % (n, )) diff --git a/mygpo/podcasts/urls.py b/mygpo/podcasts/urls.py index d7f60a8ae..b6f155bf4 100644 --- a/mygpo/podcasts/urls.py +++ b/mygpo/podcasts/urls.py @@ -1,138 +1,171 @@ -from django.conf.urls import url +from django.urls import path, register_converter, include from .views import podcast, episode +from mygpo.users import converters -urlpatterns = [ +register_converter(converters.ClientUIDConverter, 'client-uid') - url(r'^subscribe', - podcast.subscribe_url, - name='subscribe-by-url'), - # Podcast Views with UUIDs - url(r'^podcast/(?P[0-9a-f]{32})/?$', - podcast.show_id, - name='podcast-id'), +podcast_uuid_patterns = [ - url(r'^podcast/(?P[0-9a-f]{32})/subscribe$', + path('subscribe', podcast.subscribe_id, name='subscribe-id'), - url(r'^podcast/(?P[0-9a-f]{32})/subscribe/\+all$', + path('subscribe/+all', podcast.subscribe_all_id, name='subscribe-all-id'), - url(r'^podcast/(?P[0-9a-f]{32})/unsubscribe/' - '(?P[\w.-]+)', + path('unsubscribe/', podcast.unsubscribe_id, name='unsubscribe-id'), - url(r'^podcast/(?P[0-9a-f]{32})/unsubscribe/\+all$', + path('unsubscribe/+all', podcast.unsubscribe_all_id, name='unsubscribe-all-id'), - url(r'^podcast/(?P[0-9a-f]{32})/add-tag', + path('add-tag', podcast.add_tag_id, name='add-tag-id'), - url(r'^podcast/(?P[0-9a-f]{32})/remove-tag', + path('remove-tag', podcast.remove_tag_id, name='remove-tag-id'), - url(r'^podcast/(?P[0-9a-f]{32})/set-public', + path('set-public', podcast.set_public_id, name='podcast-public-id', kwargs={'public': True}), - url(r'^podcast/(?P[0-9a-f]{32})/set-private', + path('set-private', podcast.set_public_id, name='podcast-private-id', kwargs={'public': False}), - url(r'^podcast/(?P[0-9a-f]{32})/-episodes', + path('-episodes', podcast.all_episodes_id, name='podcast-all-episodes-id'), - # Podcast Views with Slugs - url(r'^podcast/(?P[\w-]+)/?$', - podcast.show_slug, - name='podcast-slug'), +] + +podcast_slug_patterns = [ - url(r'^podcast/(?P[\w-]+)/subscribe$', + path('subscribe', podcast.subscribe_slug, name='subscribe-slug'), - url(r'^podcast/(?P[\w-]+)/subscribe/\+all$', + path('subscribe/+all', podcast.subscribe_all_slug, name='subscribe-all-slug'), - url(r'^podcast/(?P[\w-]+)/unsubscribe/(?P[\w.-]+)', + path('unsubscribe/', podcast.unsubscribe_slug, name='unsubscribe-slug'), - url(r'^podcast/(?P[\w-]+)/unsubscribe/\+all$', + path('unsubscribe/+all', podcast.unsubscribe_all_slug, name='unsubscribe-all-slug'), - url(r'^podcast/(?P[\w-]+)/add-tag', + path('add-tag', podcast.add_tag_slug, name='add-tag-slug'), - url(r'^podcast/(?P[\w-]+)/remove-tag', + path('remove-tag', podcast.remove_tag_slug, name='remove-tag-slug'), - url(r'^podcast/(?P[\w-]+)/set-public', + path('set-public', podcast.set_public_slug, name='podcast-public-slug', kwargs={'public': True}), - url(r'^podcast/(?P[\w-]+)/set-private', + path('set-private', podcast.set_public_slug, name='podcast-private-slug', kwargs={'public': False}), - url(r'^podcast/(?P[\w-]+)/-episodes', + path('-episodes', podcast.all_episodes_slug, name='podcast-all-episodes-slug'), - url(r'^favorites/$', - episode.list_favorites, - name='favorites'), +] - # Episodes for UUIDs - url(r'^podcast/(?P[0-9a-f]{32})/(?P[0-9a-f]{32})$', - episode.show_id, - name='episode-id'), - url(r'^podcast/(?P[0-9a-f]{32})/(?P[0-9a-f]{32})/' - 'toggle-favorite', +episode_uuid_patterns = [ + + path('toggle-favorite', episode.toggle_favorite_id, name='episode-fav-id'), - url(r'^podcast/(?P[0-9a-f]{32})/(?P[0-9a-f]{32})/add-action', + path('add-action', episode.add_action_id, name='add-episode-action-id'), - url(r'^podcast/(?P[0-9a-f]{32})/(?P[0-9a-f]{32})/\+history', + path('+history', episode.episode_history_id, name='episode-history-id'), - # Episodes for Slugs - url(r'^podcast/(?P[\w-]+)/(?P[\w-]+)$', - episode.show_slug, - name='episode-slug'), +] + - url(r'^podcast/(?P[\w-]+)/(?P[\w-]+)/toggle-favorite', +episode_slug_patterns = [ + + path('toggle-favorite', episode.toggle_favorite_slug, name='episode-fav-slug'), - url(r'^podcast/(?P[\w-]+)/(?P[\w-]+)/add-action', + path('add-action', episode.add_action_slug, name='add-episode-action-slug'), - url(r'^podcast/(?P[\w-]+)/(?P[\w-]+)/\+history', + path('+history', episode.episode_history_slug, name='episode-history-slug'), ] + +urlpatterns = [ + + path('subscribe', + podcast.subscribe_url, + name='subscribe-by-url'), + + # Podcast Views with UUIDs + path('podcast/', + podcast.show_id, + name='podcast-id'), + + path('podcast//', + include(podcast_uuid_patterns)), + + # Podcast Views with Slugs + path('podcast/', + podcast.show_slug, + name='podcast-slug'), + + path('podcast//', + include(podcast_slug_patterns)), + + path('favorites/', + episode.list_favorites, + name='favorites'), + + # Episodes for UUIDs + path('podcast//', + episode.show_id, + name='episode-id'), + + path('podcast///', + include(episode_uuid_patterns)), + + + # Episodes for Slugs + path('podcast//', + episode.show_slug, + name='episode-slug'), + + path('podcast///', + include(episode_slug_patterns)), + +] diff --git a/mygpo/podcasts/views/podcast.py b/mygpo/podcasts/views/podcast.py index ace98826e..c047e2130 100644 --- a/mygpo/podcasts/views/podcast.py +++ b/mygpo/podcasts/views/podcast.py @@ -24,7 +24,7 @@ get_subscribe_targets ) from mygpo.history.models import HistoryEntry -from mygpo.utils import normalize_feed_url +from mygpo.utils import normalize_feed_url, to_maxlength from mygpo.users.settings import PUBLIC_SUB_PODCAST from mygpo.publisher.utils import check_publisher_permission from mygpo.usersettings.models import UserSettings @@ -186,10 +186,16 @@ def add_tag(request, podcast): tags = tag_str.split(',') tags = map(str.strip, tags) + tags = map(str.lower, tags) + tags = list(filter(None, tags)) ContentType.objects.get_for_model(podcast) for tag in tags: + + # trim to maximum length + tag = to_maxlength(Tag, 'tag', tag) + Tag.objects.get_or_create( tag=tag, source=Tag.USER, @@ -208,8 +214,8 @@ def add_tag(request, podcast): @login_required def remove_tag(request, podcast): - tag_str = request.GET.get('tag', '') - if not tag_str: + tag_str = request.GET.get('tag', None) + if tag_str is None: return HttpResponseBadRequest() user = request.user @@ -219,13 +225,14 @@ def remove_tag(request, podcast): ContentType.objects.get_for_model(podcast) - Tag.objects.filter( - tag__in=tags, - source=Tag.USER, - user=user, - content_type=ContentType.objects.get_for_model(podcast), - object_id=podcast.id, - ).delete() + for tag in tags: + Tag.objects.filter( + tag__iexact=tag, + source=Tag.USER, + user=user, + content_type=ContentType.objects.get_for_model(podcast), + object_id=podcast.id, + ).delete() if request.GET.get('next', '') == 'mytags': return HttpResponseRedirect('/tags/') @@ -326,7 +333,7 @@ def subscribe_url(request): if not url: raise Http404('Please specify a valid url') - podcast = Podcast.objects.get_or_create_for_url(url) + podcast = Podcast.objects.get_or_create_for_url(url).object return HttpResponseRedirect(get_podcast_link_target(podcast, 'subscribe')) diff --git a/mygpo/publisher/templates/publisher/podcast.html b/mygpo/publisher/templates/publisher/podcast.html index 93c03a38a..314ab388e 100644 --- a/mygpo/publisher/templates/publisher/podcast.html +++ b/mygpo/publisher/templates/publisher/podcast.html @@ -4,6 +4,7 @@ {% load podcasts %} {% load charts %} {% load pcharts %} +{% load time %} {% load static %} {% load menu %} {% load utils %} @@ -56,12 +57,49 @@

    Podcast Data

    {% trans "The podcast information is regularly retrieved from the podcast feed" %}

    {{ podcast.url }}
    -

    {% trans "Timing" %}

    -
      -
    • {% trans "Last update:" %} {{ podcast.last_update|naturaltime }}
    • -
    • {% trans "Update interval:" %} {{ podcast.update_interval|hours_to_str }}
    • -
    • {% trans "Next update:" %} {{ podcast.next_update|naturaltime }}
    • -
    +

    {% trans "Updates" %}

    + + {% trans "Update interval:" %} {{ podcast.update_interval|hours_to_str }} + + + + + + + + + + + + + + + + + + {% for result in update_results %} + + + + + + + {% empty %} + + + + + + + {% endfor %} + +
    StartDurationStatusEpisodes Added
    {{ podcast.next_update|naturaltime }}Next
    {{ result.start|naturaltime }}{{ result.duration.total_seconds|format_duration }} + {% if result.successful %} + {% trans "Successful" %} + {% else %} + {% trans "Error" %} {{ result.error_message }} + {% endif %} + {{ result.episodes_added }}
    {{ podcast.last_update|naturaltime }}
    {% csrf_token %} diff --git a/mygpo/publisher/urls.py b/mygpo/publisher/urls.py index 7f6c6961d..149b4652d 100644 --- a/mygpo/publisher/urls.py +++ b/mygpo/publisher/urls.py @@ -1,88 +1,93 @@ -from django.conf.urls import url +from django.urls import path, register_converter from . import views +from mygpo.users import converters + + +register_converter(converters.UsernameConverter, 'username') + urlpatterns = [ - url(r'^$', + path('', views.home, name='publisher'), - url(r'^(?P[\w.+-]+)/update$', + path('/update', views.update_published_podcasts, name='publisher-update'), - url(r'^(?P[\w.+-]+)/update-token', + path('/update-token', views.new_update_token, name='publisher-new-update-token'), - url(r'^podcast/(?P[\w-]+)/$', + path('podcast//', views.podcast_slug, name='podcast-publisher-detail-slug'), - url(r'^podcast/(?P[\w-]+)/update$', + path('podcast//update', views.update_podcast_slug, name='podcast-publisher-update-slug'), - url(r'^podcast/(?P[\w-]+)/save$', + path('podcast//save', views.save_podcast_slug, name='podcast-publisher-save-slug'), - url(r'^podcast/(?P[\w-]+)/episodes$', + path('podcast//episodes', views.episodes_slug, name='podcast-publisher-episodes-slug'), - url(r'^podcast/(?P[\w-]+)/(?P[\w-]+)$', + path('podcast//', views.episode_slug, name='episode-publisher-detail-slug'), - url(r'^podcast/(?P[\w-]+)/(?P[\w-]+)/set-slug$', + path('podcast///set-slug', views.update_episode_slug_slug, name='publisher-set-episode-slug-slug'), - url(r'^podcast/(?P[0-9a-f]{32})/$', + path('podcast//', views.podcast_id, name='podcast-publisher-detail-id'), - url(r'^podcast/(?P[0-9a-f]{32})/update$', + path('podcast//update', views.update_podcast_id, name='podcast-publisher-update-id'), - url(r'^podcast/(?P[0-9a-f]{32})/save$', + path('podcast//save', views.save_podcast_id, name='podcast-publisher-save-id'), - url(r'^podcast/(?P[0-9a-f]{32})/episodes$', + path('podcast//episodes', views.episodes_id, name='podcast-publisher-episodes-id'), - url(r'^podcast/(?P[0-9a-f]{32})/(?P[0-9a-f]{32})$', + path('podcast//', views.episode_id, name='episode-publisher-detail-id'), - url(r'^podcast/(?P[0-9a-f]{32})/(?P[0-9a-f]{32})/' - 'set-slug$', + path('podcast///' + 'set-slug', views.update_episode_slug_id, name='publisher-set-episode-slug-id'), - url(r'^group/(?P[\w-]+)$', + path('group/', views.group_slug, name='group-publisher-slug'), - url(r'^group/(?P[\w-]+)$', + path('group/', views.group_id, name='group-publisher-id'), - url(r'^podcast/search$', + path('podcast/search', views.search_podcast, name='podcast-publisher-search'), - url(r'^link/$', + path('link/', views.link, name='link-here'), - url(r'^advertise$', + path('advertise', views.advertise, name='advertise'), diff --git a/mygpo/publisher/views.py b/mygpo/publisher/views.py index 9e62e98b7..5038cdf8f 100644 --- a/mygpo/publisher/views.py +++ b/mygpo/publisher/views.py @@ -31,6 +31,7 @@ get_episode_link_target from django.contrib.sites.requests import RequestSite from mygpo.data.tasks import update_podcasts +from mygpo.data.models import PodcastUpdateResult from mygpo.decorators import requires_token, allowed_methods from mygpo.pubsub.models import HubSubscription @@ -92,6 +93,11 @@ def podcast(request, podcast): except HubSubscription.DoesNotExist: pubsubscription = None + MAX_UPDATE_RESULTS=10 + + update_results = PodcastUpdateResult.objects.filter(podcast=podcast) + update_results = update_results[:MAX_UPDATE_RESULTS] + site = RequestSite(request) feedurl_quoted = urllib.parse.quote(podcast.url.encode('ascii')) @@ -105,6 +111,7 @@ def podcast(request, podcast): 'update_token': update_token, 'feedurl_quoted': feedurl_quoted, 'pubsubscription': pubsubscription, + 'update_results': update_results, }) diff --git a/mygpo/pubsub/urls.py b/mygpo/pubsub/urls.py index 942c938e3..40cafb262 100644 --- a/mygpo/pubsub/urls.py +++ b/mygpo/pubsub/urls.py @@ -1,11 +1,11 @@ -from django.conf.urls import url +from django.urls import path from . import views urlpatterns = [ - url(r'^subscribe$', + path('subscribe', views.SubscribeView.as_view(), name='pubsub-subscribe'), diff --git a/mygpo/settings.py b/mygpo/settings.py index bca3782ac..0b3bb03b2 100644 --- a/mygpo/settings.py +++ b/mygpo/settings.py @@ -67,14 +67,25 @@ def get_intOrNone(name, default): # to load the internationalization machinery. USE_I18N = True + +# Static Files + STATIC_ROOT = 'staticfiles' -STATIC_URL = '/media/' +STATIC_URL = '/static/' STATICFILES_DIRS = ( - os.path.abspath(os.path.join(BASE_DIR, '..', 'htdocs', 'media')), + os.path.abspath(os.path.join(BASE_DIR, '..', 'static')), ) +# Media Files + +MEDIA_ROOT = os.getenv('MEDIA_ROOT', + os.path.abspath(os.path.join(BASE_DIR, '..', 'media'))) + +MEDIA_URL = '/media/' + + TEMPLATES = [{ 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], @@ -159,7 +170,6 @@ def get_intOrNone(name, default): 'mygpo.pubsub', 'mygpo.podcastlists', 'mygpo.votes', - 'django_nose', ] try: @@ -173,10 +183,9 @@ def get_intOrNone(name, default): try: - import opbeat - - if not DEBUG: - INSTALLED_APPS += ['opbeat.contrib.django'] + if DEBUG: + import django_extensions + INSTALLED_APPS += ['django_extensions'] except ImportError: pass @@ -211,7 +220,7 @@ def get_intOrNone(name, default): SECRET_KEY = os.getenv('SECRET_KEY', '') -if 'test' in sys.argv: +if 'pytest' in sys.argv[0]: SECRET_KEY = 'test' GOOGLE_ANALYTICS_PROPERTY_ID = os.getenv('GOOGLE_ANALYTICS_PROPERTY_ID', '') @@ -366,14 +375,8 @@ def get_intOrNone(name, default): PODCAST_AD_ID = os.getenv('PODCAST_AD_ID') -TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' - -NOSE_ARGS = [ - '--with-doctest', - '--stop', - '--where=mygpo', -] +MAX_EPISODE_ACTIONS = int(os.getenv('MAX_EPISODE_ACTIONS', 1000)) SEARCH_CUTOFF = float(os.getenv('SEARCH_CUTOFF', 0.3)) diff --git a/mygpo/share/templatetags/gravatar.py b/mygpo/share/templatetags/gravatar.py index 43d9a875b..9e01e64b7 100644 --- a/mygpo/share/templatetags/gravatar.py +++ b/mygpo/share/templatetags/gravatar.py @@ -1,5 +1,6 @@ import hashlib +from django.utils.safestring import mark_safe from django import template from mygpo.constants import PODCAST_LOGO_BIG_SIZE @@ -7,10 +8,11 @@ register = template.Library() -GRAVATAR_IMG = 'https://secure.gravatar.com/avatar/{hash_str}?s={size}&d=mm' +GRAVATAR_IMG = 'https://secure.gravatar.com/avatar/{hash_str}?s={size}' @register.simple_tag +@mark_safe def gravatar_img(user): return '{username}'.format( url=gravatar_url(user), diff --git a/mygpo/share/urls.py b/mygpo/share/urls.py index 32b31bd58..c01d7f41e 100644 --- a/mygpo/share/urls.py +++ b/mygpo/share/urls.py @@ -1,69 +1,74 @@ -from django.conf.urls import url +from django.urls import path, register_converter from . import views, userpage +from mygpo.users import converters + + +register_converter(converters.UsernameConverter, 'username') + urlpatterns = [ - url(r'^share/$', + path('share/', views.overview, name='share'), - url(r'^share/subscriptions-public$', + path('share/subscriptions-public', views.set_token_public, kwargs={'public': True, 'token_name': 'subscriptions_token'}, name='subscriptions-public'), - url(r'^share/subscriptions-private$', + path('share/subscriptions-private', views.set_token_public, kwargs={'public': False, 'token_name': 'subscriptions_token'}, name='subscriptions-private'), - url(r'^share/favfeed-public$', + path('share/favfeed-public', views.set_token_public, kwargs={'public': True, 'token_name': 'favorite_feeds_token'}, name='favfeed-public'), - url(r'^share/favfeed-private$', + path('share/favfeed-private', views.set_token_public, kwargs={'public': False, 'token_name': 'favorite_feeds_token'}, name='favfeed-private'), - url(r'^share/userpage-public$', + path('share/userpage-public', views.set_token_public, kwargs={'public': True, 'token_name': 'userpage_token'}, name='userpage-public'), - url(r'^share/userpage-private$', + path('share/userpage-private', views.set_token_public, kwargs={'public': False, 'token_name': 'userpage_token'}, name='userpage-private'), - url(r'^share/favorites$', + path('share/favorites', views.ShareFavorites.as_view(), name='share-favorites'), - url(r'^favorites/private', + path('favorites/private', views.FavoritesPublic.as_view(public=False), name='favorites_private'), - url(r'^favorites/public', + path('favorites/public', views.FavoritesPublic.as_view(public=True), name='favorites_public'), - url(r'^share/subscriptions/private', + path('share/subscriptions/private', views.PublicSubscriptions.as_view(public=False), name='private_subscriptions'), - url(r'^share/subscriptions/public', + path('share/subscriptions/public', views.PublicSubscriptions.as_view(public=True), name='public_subscriptions'), - url(r'^share/favorites/create-directory-entry', + path('share/favorites/create-directory-entry', views.FavoritesFeedCreateEntry.as_view(), name='favorites-create-entry'), - url(r'^user/(?P[\w.+-]+)/?$', + path('user//', userpage.UserpageView.as_view(), name='user'), diff --git a/mygpo/share/views.py b/mygpo/share/views.py index 64b86cbd6..b2a4f8be9 100644 --- a/mygpo/share/views.py +++ b/mygpo/share/views.py @@ -11,7 +11,7 @@ from mygpo.podcasts.models import Podcast from mygpo.publisher.models import PublishedPodcast from mygpo.userfeeds.feeds import FavoriteFeed -from mygpo.data.feeddownloader import update_podcast +from mygpo.data.feeddownloader import PodcastUpdater import logging logger = logging.getLogger(__name__) @@ -93,14 +93,15 @@ def post(self, request): site = RequestSite(request) feed_url = feed.get_public_url(site.domain) - podcast = Podcast.objects.get_or_create_for_url(feed_url) + podcast = Podcast.objects.get_or_create_for_url(feed_url).object PublishedPodcast.objects.get_or_create( podcast=podcast, publisher=user, ) - update_podcast(feed_url) + updater = PodcastUpdater(feed_url) + updater.update_podcast() return HttpResponseRedirect(reverse('share-favorites')) diff --git a/mygpo/subscriptions/tests.py b/mygpo/subscriptions/tests.py index 528cf1761..72d37d548 100644 --- a/mygpo/subscriptions/tests.py +++ b/mygpo/subscriptions/tests.py @@ -25,7 +25,7 @@ def setUp(self): user=self.user, uid='dev1', id=uuid.uuid1()) self.url = 'http://www.example.com/pdocast.rss' - self.podcast = Podcast.objects.get_or_create_for_url(self.url) + self.podcast = Podcast.objects.get_or_create_for_url(self.url).object def test_duplicate_subscribe(self): """ Test that a duplicate subscription is skipped """ diff --git a/mygpo/subscriptions/urls.py b/mygpo/subscriptions/urls.py index 2cc291d79..fa09d50e3 100644 --- a/mygpo/subscriptions/urls.py +++ b/mygpo/subscriptions/urls.py @@ -1,27 +1,32 @@ -from django.conf.urls import url +from django.urls import path, register_converter from . import views +from mygpo.users import converters + + +register_converter(converters.UsernameConverter, 'username') + urlpatterns = [ - url(r'^subscriptions/$', + path('subscriptions/', views.show_list, name='subscriptions'), - url(r'^download/subscriptions\.opml$', + path('download/subscriptions.opml', views.download_all, name='subscriptions-opml'), - url(r'^user/(?P[\w.+-]+)/subscriptions/rss/$', + path('user//subscriptions/rss/', views.subscriptions_feed, name='shared-subscriptions-rss'), - url(r'^user/(?P[\w.+-]+)/subscriptions$', + path('user//subscriptions', views.for_user, name='shared-subscriptions'), - url(r'^user/(?P[\w.+-]+)/subscriptions\.opml$', + path('user//subscriptions.opml', views.for_user_opml, name='shared-subscriptions-opml'), diff --git a/mygpo/subscriptions/views.py b/mygpo/subscriptions/views.py index 2fe87c610..dc3939c03 100644 --- a/mygpo/subscriptions/views.py +++ b/mygpo/subscriptions/views.py @@ -85,7 +85,7 @@ def subscriptions_feed(request, username): f = SubscriptionsFeed(username) obj = f.get_object(request, username) feedgen = f.get_feed(obj, request) - response = HttpResponse(content_type=feedgen.mime_type) + response = HttpResponse(content_type=feedgen.content_type) feedgen.write(response, 'utf-8') return response diff --git a/mygpo/suggestions/urls.py b/mygpo/suggestions/urls.py index d17bdeb74..ff1fceeef 100644 --- a/mygpo/suggestions/urls.py +++ b/mygpo/suggestions/urls.py @@ -1,19 +1,19 @@ -from django.conf.urls import url +from django.urls import path from . import views urlpatterns = [ - url(r'^$', + path('', views.suggestions, name='suggestions'), - url(r'^blacklist/(?P[\w-]+)$', + path('blacklist/', views.blacklist_slug, name='suggestions-blacklist-slug'), - url(r'^blacklist/(?P[0-9a-f]{32})$', + path('blacklist/', views.blacklist_id, name='suggestions-blacklist-id'), diff --git a/mygpo/urls.py b/mygpo/urls.py index 54f446e22..087d5e1fc 100644 --- a/mygpo/urls.py +++ b/mygpo/urls.py @@ -1,16 +1,12 @@ import os.path -from django.conf.urls import include, url +from django.urls import include, path, register_converter, re_path from django.contrib import admin from django.conf import settings -from django.contrib.staticfiles.views import serve +from django.conf.urls.static import static -# strip the leading "/" -static_prefix = settings.STATIC_URL[1:] # This URLs should be always be served, even during maintenance mode -urlpatterns = [ - url(r'^%s(?P.*)$' % static_prefix, serve) -] +urlpatterns = static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) # Check for maintenace mode @@ -18,27 +14,26 @@ if settings.MAINTENANCE: from mygpo.web import utils urlpatterns += [ - url(r'', utils.maintenance), + re_path('', utils.maintenance) ] - # URLs are still registered during maintenace mode because we need to # build links from them (eg login-link). urlpatterns += [ - url(r'^', include('mygpo.web.urls')), - url(r'^', include('mygpo.podcasts.urls')), - url(r'^', include('mygpo.directory.urls')), - url(r'^', include('mygpo.api.urls')), - url(r'^', include('mygpo.userfeeds.urls')), - url(r'^', include('mygpo.share.urls')), - url(r'^', include('mygpo.history.urls')), - url(r'^', include('mygpo.subscriptions.urls')), - url(r'^', include('mygpo.users.urls')), - url(r'^', include('mygpo.podcastlists.urls')), - url(r'^', include('mygpo.moauth.urls')), - url(r'^suggestions/', include('mygpo.suggestions.urls')), - url(r'^publisher/', include('mygpo.publisher.urls')), - url(r'^administration/', include('mygpo.administration.urls')), - url(r'^pubsub/', include('mygpo.pubsub.urls')), - url(r'^admin/', include(admin.site.urls)), + path('', include('mygpo.web.urls')), + path('', include('mygpo.podcasts.urls')), + path('', include('mygpo.directory.urls')), + path('', include('mygpo.api.urls')), + path('', include('mygpo.userfeeds.urls')), + path('', include('mygpo.share.urls')), + path('', include('mygpo.history.urls')), + path('', include('mygpo.subscriptions.urls')), + path('', include('mygpo.users.urls')), + path('', include('mygpo.podcastlists.urls')), + path('', include('mygpo.moauth.urls')), + path('suggestions/', include('mygpo.suggestions.urls')), + path('publisher/', include('mygpo.publisher.urls')), + path('administration/', include('mygpo.administration.urls')), + path('pubsub/', include('mygpo.pubsub.urls')), + path('admin/', admin.site.urls), ] diff --git a/mygpo/userfeeds/urls.py b/mygpo/userfeeds/urls.py index 3d3dca4c3..bef0723e4 100644 --- a/mygpo/userfeeds/urls.py +++ b/mygpo/userfeeds/urls.py @@ -1,11 +1,16 @@ -from django.conf.urls import url +from django.urls import path, register_converter from . import views +from mygpo.users import converters + + +register_converter(converters.UsernameConverter, 'username') + urlpatterns = [ - url(r'^user/(?P[\w.+-]+)/favorites.xml$', + path('user//favorites.xml', views.favorite_feed, name='favorites-feed'), diff --git a/mygpo/users/backend.py b/mygpo/users/backend.py index 72158deeb..b1ed36806 100644 --- a/mygpo/users/backend.py +++ b/mygpo/users/backend.py @@ -8,7 +8,7 @@ class CaseInsensitiveModelBackend(ModelBackend): """ Authenticates with a case-insensitive username """ - def authenticate(self, username=None, password=None, **kwargs): + def authenticate(self, request, username=None, password=None, **kwargs): UserModel = get_user_model() users = UserModel.objects.filter(username__iexact=username)\ .order_by('-last_login') diff --git a/mygpo/users/checks.py b/mygpo/users/checks.py index db462f33a..643c0473f 100644 --- a/mygpo/users/checks.py +++ b/mygpo/users/checks.py @@ -1,6 +1,6 @@ from django.core.checks import register, Warning from django.db import connection -from django.db.utils import OperationalError +from django.db.utils import OperationalError, ProgrammingError from django.conf import settings @@ -38,6 +38,14 @@ def check_case_insensitive_users(app_configs=None, **kwargs): else: raise + except ProgrammingError as pe: + if 'relation "auth_user" does not exist' in str(pe): + # Ignore if the table does not yet exist, eg when initally + # running ``manage.py migrate`` + pass + else: + raise + return errors diff --git a/mygpo/users/converters.py b/mygpo/users/converters.py new file mode 100644 index 000000000..6f2e74820 --- /dev/null +++ b/mygpo/users/converters.py @@ -0,0 +1,18 @@ +class UsernameConverter: + regex = '[\w.+-]+' + + def to_python(self, value): + return value + + def to_url(self, value): + return value + + +class ClientUIDConverter: + regex = '[\w.-]+' + + def to_python(self, value): + return value + + def to_url(self, value): + return value diff --git a/mygpo/users/models.py b/mygpo/users/models.py index 464099bf2..033e57839 100644 --- a/mygpo/users/models.py +++ b/mygpo/users/models.py @@ -123,6 +123,7 @@ class UserProfile(TwitterModel): # the user to which this profile belongs user = models.OneToOneField(settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, related_name='profile') # if False, suggestions should be updated diff --git a/mygpo/users/templates/registration/resent_activation.html b/mygpo/users/templates/registration/resent_activation.html index 99d2d3ddf..0a4aa42ed 100644 --- a/mygpo/users/templates/registration/resent_activation.html +++ b/mygpo/users/templates/registration/resent_activation.html @@ -8,6 +8,6 @@

    {% trans "Sent Activation Email" %}

    {% endblock %} {% block content %} -

    {% trans "The activation email has been reset." %}

    +

    {% trans "The activation email has been resent." %}

    {% endblock %} diff --git a/mygpo/users/tests.py b/mygpo/users/tests.py index 5f65b7b49..6d5f137c4 100644 --- a/mygpo/users/tests.py +++ b/mygpo/users/tests.py @@ -3,7 +3,7 @@ from collections import Counter from django.urls import reverse -from django.test.client import Client as TestClient +from django.test.client import Client as TClient from django.test import TestCase from django.test.utils import override_settings from django.contrib.auth import get_user_model @@ -66,8 +66,11 @@ class UnsubscribeMergeTests(TestCase): P2_URL = 'http://test.org/podcast/' def setUp(self): - self.podcast1 = Podcast.objects.get_or_create_for_url('http://example.com/feed.rss') - self.podcast2 = Podcast.objects.get_or_create_for_url(self.P2_URL) + self.podcast1 = Podcast.objects.get_or_create_for_url( + 'http://example.com/feed.rss').object + + self.podcast2 = Podcast.objects.get_or_create_for_url( + self.P2_URL).object User = get_user_model() self.user = User(username='test-merge') @@ -100,7 +103,7 @@ class AuthTests(TestCase): def setUp(self): self.user, pwd = create_user() - self.client = TestClient() + self.client = TClient() wrong_pwd = pwd + '1234' self.extra = { 'HTTP_AUTHORIZATION': create_auth_string(self.user.username, diff --git a/mygpo/users/urls.py b/mygpo/users/urls.py index 2ca246320..071c05bee 100644 --- a/mygpo/users/urls.py +++ b/mygpo/users/urls.py @@ -1,125 +1,128 @@ -from django.conf.urls import url +from django.urls import path, register_converter from django.contrib.auth.views import LogoutView from django.views.generic.base import TemplateView from .views import registration, settings, device, user +from mygpo.users import converters + +register_converter(converters.ClientUIDConverter, 'client-uid') urlpatterns = [ - url(r'^register/$', + path('register/', registration.RegistrationView.as_view(), name='register'), - url(r'^account/$', + path('account/', settings.account, name='account'), - url(r'^account/privacy$', + path('account/privacy', settings.privacy, name='privacy'), - url(r'^account/profile$', + path('account/profile', settings.ProfileView.as_view(), name='profile'), - url(r'^account/google/remove$', + path('account/google/remove', settings.AccountRemoveGoogle.as_view(), name='account-google-remove'), - url(r'^account/privacy/default-public$', + path('account/privacy/default-public', settings.DefaultPrivacySettings.as_view(public=True), name='privacy_default_public'), - url(r'^account/privacy/default-private$', + path('account/privacy/default-private', settings.DefaultPrivacySettings.as_view(public=False), name='privacy_default_private'), - url(r'^account/privacy/(?P[\w]+)/public$', + path('account/privacy//public', settings.PodcastPrivacySettings.as_view(public=True), name='privacy_podcast_public'), - url(r'^account/privacy/(?P[\w]+)/private$', + path('account/privacy//private', settings.PodcastPrivacySettings.as_view(public=False), name='privacy_podcast_private'), - url(r'^account/delete$', + path('account/delete', settings.delete_account, name='delete-account'), - url(r'^devices/$', + path('devices/', device.overview, name='devices'), - url(r'^devices/create-device$', + path('devices/create-device', device.create, name='device-create'), - url(r'^device/(?P[\w.-]+)\.opml$', + path('device/.opml', device.opml, name='device-opml'), - url(r'^device/(?P[\w.-]+)$', + path('device/', device.show, name='device'), - url(r'^device/(?P[\w.-]+)/symbian.opml$', + path('device//symbian.opml', device.symbian_opml, name='device-symbian-opml'), - url(r'^device/(?P[\w.-]+)/sync$', + path('device//sync', device.sync, name='device-sync'), - url(r'^device/(?P[\w.-]+)/unsync$', + path('device//unsync', device.unsync, name='device-unsync'), - url(r'^device/(?P[\w.-]+)/resync$', + path('device//resync', device.resync, name='trigger-sync'), - url(r'^device/(?P[\w.-]+)/delete$', + path('device//delete', device.delete, name='device-delete'), - url(r'^device/(?P[\w.-]+)/remove$', + path('device//remove', device.delete_permanently, name='device-delete-permanently'), - url(r'^device/(?P[\w.-]+)/undelete$', + path('device//undelete', device.undelete, name='device-undelete'), - url(r'^device/(?P[\w.-]+)/edit$', + path('device//edit', device.edit, name='device-edit'), - url(r'^device/(?P[\w.-]+)/update$', + path('device//update', device.update, name='device-update'), - url(r'^device/(?P[\w.-]+)/upload-opml$', + path('device//upload-opml', device.upload_opml, name='device-upload-opml'), - url(r'^register/restore_password$', + path('register/restore_password', user.restore_password, name='restore-password'), - url(r'^login/$', + path('login/', user.LoginView.as_view(), name='login'), - url(r'^login/google$', + path('login/google', user.GoogleLogin.as_view(), name='login-google'), - url(r'^login/oauth2callback$', + path('login/oauth2callback', user.GoogleLoginCallback.as_view(), name='login-google-callback'), - url(r'^logout/$', + path('logout/', LogoutView.as_view(), kwargs={'next_page': '/'}, name='logout'), diff --git a/mygpo/usersettings/converters.py b/mygpo/usersettings/converters.py new file mode 100644 index 000000000..a979d5d28 --- /dev/null +++ b/mygpo/usersettings/converters.py @@ -0,0 +1,8 @@ +class ScopeConverter: + regex = 'account|device|podcast|episode' + + def to_python(self, value): + return value + + def to_url(self, value): + return value diff --git a/mygpo/usersettings/tests.py b/mygpo/usersettings/tests.py index dda1d4be6..9de6321a0 100644 --- a/mygpo/usersettings/tests.py +++ b/mygpo/usersettings/tests.py @@ -5,7 +5,7 @@ import json from django.urls import reverse -from django.test.client import Client as TestClient +from django.test.client import Client as TClient from django.test import TestCase from mygpo.test import create_auth_string, create_user @@ -22,17 +22,20 @@ def setUp(self): self.podcast_url = 'http://example.com/podcast.rss' self.episode_url = 'http://example.com/podcast/episode-1.mp3' self.uid = 'client-uid' - self.podcast = Podcast.objects.get_or_create_for_url(self.podcast_url) + self.podcast = Podcast.objects.get_or_create_for_url( + self.podcast_url).object + self.episode = Episode.objects.get_or_create_for_url( self.podcast, self.episode_url, - ) + ).object + self.user_client = Client.objects.create( id = uuid.uuid1(), user = self.user, uid = self.uid, ) - self.client = TestClient() + self.client = TClient() self.extra = { 'HTTP_AUTHORIZATION': create_auth_string(self.user.username, pwd) } diff --git a/mygpo/web/auth.py b/mygpo/web/auth.py index 44ff3ebce..d9c45efe6 100644 --- a/mygpo/web/auth.py +++ b/mygpo/web/auth.py @@ -10,7 +10,7 @@ class EmailAuthenticationBackend(ModelBackend): """ Auth backend to enable login with email address as username """ - def authenticate(self, username=None, password=None): + def authenticate(self, request, username=None, password=None): try: validate_email(username) diff --git a/mygpo/web/logo.py b/mygpo/web/logo.py index 97a0d7833..07cc8c3ad 100644 --- a/mygpo/web/logo.py +++ b/mygpo/web/logo.py @@ -1,81 +1,87 @@ import os.path import io -from datetime import datetime -from glob import glob -import errno +import requests import hashlib +import socket import struct from PIL import Image, ImageDraw from django.urls import reverse from django.conf import settings -from django.http import Http404, HttpResponse, HttpResponseNotFound +from django.http import Http404, HttpResponseRedirect from django.views import View from django.utils.decorators import method_decorator from django.views.decorators.http import last_modified +from django.contrib.staticfiles.storage import staticfiles_storage +from django.core.files.storage import FileSystemStorage + +from mygpo.utils import file_hash import logging logger = logging.getLogger(__name__) -LOGO_DIR = os.path.join(settings.BASE_DIR, '..', 'htdocs', 'media', 'logo') +# Use Django's File Storage API to access podcast logos. This could be swapped +# out for another storage implementation (eg for storing to Amazon S3) +# https://docs.djangoproject.com/en/1.11/ref/files/storage/ +LOGO_STORAGE = FileSystemStorage( + location=settings.MEDIA_ROOT, +) def _last_modified(request, size, prefix, filename): - target = os.path.join(LOGO_DIR, size, prefix, filename) + target = os.path.join('logo', str(size), prefix, filename) try: - return datetime.fromtimestamp(os.path.getmtime(target)) + return LOGO_STORAGE.get_modified_time(target) - except OSError: + except (FileNotFoundError, NotImplementedError): return None class CoverArt(View): + def __init__(self): + self.storage = LOGO_STORAGE + @method_decorator(last_modified(_last_modified)) def get(self, request, size, prefix, filename): size = int(size) - target = self.get_thumbnail(size, prefix, filename) - original = self.get_original(prefix, filename) + prefix = get_prefix(filename) + target = self.get_thumbnail_path(size, prefix, filename) + original = self.get_original_path(prefix, filename) - if os.path.exists(target): + if self.storage.exists(target): return self.send_file(target) - if not os.path.exists(original): + if not self.storage.exists(original): + logger.warning('Original cover {} not found'.format(original)) raise Http404('Cover Art not available' + original) - target_dir = self.get_dir(target) - + target_dir = self.get_dir(filename) try: - im = Image.open(original) + fp = self.storage.open(original, 'rb') + im = Image.open(fp) if im.mode not in ('RGB', 'RGBA'): - im = im.convert('RGB') - except IOError: - raise Http404('Cannot open cover file') + im = im.convert('RGBA') + except IOError as ioe: + logger.warning('Cover file {} cannot be opened: {}'.format( + original, ioe)) + raise Http404('Cannot open cover file') from ioe try: im.thumbnail((size, size), Image.ANTIALIAS) resized = im except (struct.error, IOError, IndexError) as ex: # raised when trying to read an interlaced PNG; - logger.warn('Could not create thumbnail: %s', str(ex)) + logger.warning('Could not create thumbnail: %s', str(ex)) # we use the original instead return self.send_file(original) - # If it's a RGBA image, composite it onto a white background for JPEG - if resized.mode == 'RGBA': - background = Image.new('RGB', resized.size) - draw = ImageDraw.Draw(background) - draw.rectangle((-1, -1, resized.size[0]+1, resized.size[1]+1), - fill=(255, 255, 255)) - del draw - resized = Image.composite(resized, background, resized) - sio = io.BytesIO() try: @@ -83,65 +89,96 @@ def get(self, request, size, prefix, filename): quality=80) except IOError as ex: return self.send_file(original) + finally: + fp.close() - s = sio.getvalue() - - fp = open(target, 'wb') - fp.write(s) - fp.close() + self.storage.save(target, sio) return self.send_file(target) - # the length of the prefix is defined here and in web/urls.py @staticmethod - def get_prefix(filename): - return filename[:3] + def get_thumbnail_path(size, prefix, filename): + return os.path.join('logo', str(size), prefix, filename) @staticmethod - def get_thumbnail(size, prefix, filename): - return os.path.join(LOGO_DIR, str(size), prefix, filename) + def get_dir(filename): + return os.path.dirname(filename) @staticmethod - def get_existing_thumbnails(prefix, filename): - files = glob(os.path.join(LOGO_DIR, '*', prefix, filename)) - return [f for f in files if 'original' not in f] + def remove_existing_thumbnails(prefix, filename): + dirs, _files = LOGO_STORAGE.listdir('logo') # TODO: cache list of sizes + for size in dirs: + if size == 'original': + continue - @staticmethod - def get_original(prefix, filename): - return os.path.join(LOGO_DIR, 'original', prefix, filename) + path = os.path.join('logo', size, prefix, filename) + logger.info('Removing {}'.format(path)) + LOGO_STORAGE.delete(path) @staticmethod - def get_dir(filename): - path = os.path.dirname(filename) - try: - os.makedirs(path) + def get_original_path(prefix, filename): + return os.path.join('logo', 'original', prefix, filename) - except OSError as ose: - if ose.errno != errno.EEXIST: - raise + def send_file(self, filename): + return HttpResponseRedirect(LOGO_STORAGE.url(filename)) - return path + @classmethod + def save_podcast_logo(cls, cover_art_url): + if not cover_art_url: + return - def send_file(self, filename): try: - f = open(filename, 'rb') - except IOError: - return HttpResponseNotFound() + image_sha1 = hashlib.sha1(cover_art_url.encode('utf-8')).hexdigest() + prefix = get_prefix(image_sha1) + + filename = cls.get_original_path(prefix, image_sha1) + dirname = cls.get_dir(filename) - resp = HttpResponse(content_type='image/jpeg') - resp.status_code = 200 - resp.write(f.read()) - return resp + # get hash of existing file + if LOGO_STORAGE.exists(filename): + with LOGO_STORAGE.open(filename, 'rb') as f: + old_hash = file_hash(f).digest() + else: + old_hash = '' + + logger.info('Logo {}, saving to {}'.format(cover_art_url, filename)) + + # save new cover art + LOGO_STORAGE.delete(filename) + source = io.BytesIO(requests.get(cover_art_url).content) + LOGO_STORAGE.save(filename, source) + + # get hash of new file + with LOGO_STORAGE.open(filename, 'rb') as f: + new_hash = file_hash(f).digest() + + # remove thumbnails if cover changed + if old_hash != new_hash: + logger.info('Removing thumbnails') + thumbnails = cls.remove_existing_thumbnails(prefix, filename) + + return cover_art_url + + except (ValueError, requests.exceptions.RequestException, + socket.error, IOError) as e: + logger.warning('Exception while updating podcast logo: %s', str(e)) + + +def get_prefix(filename): + return filename[:3] def get_logo_url(podcast, size): - """ Return the logo URL for the podcast """ + """ Return the logo URL for the podcast + + The logo either comes from the media storage (see CoverArt) or from the + default logos in the static storage. + """ if podcast.logo_url: filename = hashlib.sha1(podcast.logo_url.encode('utf-8')).hexdigest() + return reverse('logo', args=[size, get_prefix(filename), filename]) + else: filename = 'podcast-%d.png' % (hash(podcast.title) % 5, ) - - prefix = CoverArt.get_prefix(filename) - - return reverse('logo', args=[size, prefix, filename]) + return staticfiles_storage.url('logo/{0}'.format(filename)) diff --git a/mygpo/web/templates/404.html b/mygpo/web/templates/404.html index 0767053dd..348214800 100644 --- a/mygpo/web/templates/404.html +++ b/mygpo/web/templates/404.html @@ -2,7 +2,7 @@ 404 Not found (gpodder.net) - +
    diff --git a/mygpo/web/templates/500.html b/mygpo/web/templates/500.html index a7c646437..7d4e1063f 100644 --- a/mygpo/web/templates/500.html +++ b/mygpo/web/templates/500.html @@ -3,7 +3,7 @@ 500 Internal server error (gpodder.net) - +
    diff --git a/mygpo/web/templates/base.html b/mygpo/web/templates/base.html index 2fd8a994d..fd7a2137f 100644 --- a/mygpo/web/templates/base.html +++ b/mygpo/web/templates/base.html @@ -204,20 +204,4 @@

    Block "header" not defined for this template

    {% block javascript %}{% endblock javascript %} - - - - {% endblock %} diff --git a/mygpo/web/templates/home.html b/mygpo/web/templates/home.html index c9b891439..f735fc40f 100644 --- a/mygpo/web/templates/home.html +++ b/mygpo/web/templates/home.html @@ -224,20 +224,4 @@

    Features

    {% block javascript %}{% endblock javascript %} - - - - {% endblock %} diff --git a/mygpo/web/templates/skeleton.html b/mygpo/web/templates/skeleton.html index bebaa3698..d5f0e32c2 100644 --- a/mygpo/web/templates/skeleton.html +++ b/mygpo/web/templates/skeleton.html @@ -1,5 +1,6 @@ {% load i18n %} {% load menu %} +{% load static %} {% load utils %} @@ -16,7 +17,7 @@ - + - - - + + +