Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: Publish Python Package

on:
workflow_dispatch:
release:
types: [created]

Expand All @@ -24,5 +25,5 @@ jobs:
- name: Check distributions
run: twine check dist/*
- name: Publish
if: ${{ vars.PUBLISH_TO_PYPI == 'true' }}
if: ${{ github.event_name == 'release' && vars.PUBLISH_TO_PYPI == 'true' }}
uses: pypa/gh-action-pypi-publish@release/v1
12 changes: 12 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
Changelog
=========

v0.1.0 (17-06-2026)
*******************

- Added the ``filmaffinity`` command line interface.
- Added optional Pydantic models via ``as_model=True`` and
``python-filmaffinity[models]``.
- Added JSON, CSV and Markdown export helpers.
- Moved the default persistent cache to the user's cache directory.
- Moved parser tests to reusable HTML fixtures.
- Renamed the PyPI publishing workflow to ``publish-to-pypi.yml`` and added
manual build/check runs.

v0.0.21 (17-06-2026)
********************

Expand Down
21 changes: 15 additions & 6 deletions INSTALL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,32 @@ Pip
***
::

pip install python_filmaffinity
pip install python-filmaffinity

Optional Pydantic models:

::

pip install "python-filmaffinity[models]"


From Source
***********

::

git clone git@github.com:sergiormb/python_filmaffinity.git`
cd python_filmaffinity`
python setup.py install`
git clone git@github.com:sergiormb/python_filmaffinity.git
cd python_filmaffinity
python -m pip install -e ".[dev]"


Requirements
**********************

::

requests >= 2.0.1`
bs4 >= 0.0.1`
beautifulsoup4 >= 4.9.1
requests >= 2.24.0
requests-cache >= 1.0.0

Python 3.9 or newer is required.
3 changes: 2 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
include LICENSE.rst README.rst CHANGES.rst AUTHORS.rst EXAMPLES.rst
include LICENSE.rst README.rst CHANGES.rst AUTHORS.rst EXAMPLES.rst
recursive-include tests/fixtures *.html
77 changes: 72 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This is a simple python scraping for the FilmAffinity.
:target: https://github.com/sergiormb/python_filmaffinity/actions/workflows/python-test.yml?query=branch%3Amaster
.. image:: https://img.shields.io/github/license/mashape/apistatus.svg
:target: https://github.com/sergiormb/python_filmaffinity/blob/master/LICENSE.rst
.. image:: https://img.shields.io/pypi/pyversions/Django.svg
.. image:: https://img.shields.io/pypi/pyversions/python-filmaffinity.svg
:target: https://pypi.python.org/pypi/python_filmaffinity/
.. image:: https://readthedocs.org/projects/python-filmaffinity/badge/?version=latest
:target: http://python-filmaffinity.readthedocs.io/en/latest/?badge=latest
Expand All @@ -23,6 +23,12 @@ Pip

pip install python-filmaffinity

Optional Pydantic models:

::

pip install "python-filmaffinity[models]"


From Source
***********
Expand All @@ -31,17 +37,22 @@ From Source

git clone git@github.com:sergiormb/python_filmaffinity.git
cd python_filmaffinity
python setup.py install
python -m pip install -e ".[dev]"


Requirements
**********************

::

requests >= 2.0.1
requests-cache >= 0.4.13
bs4 >= 0.0.1
beautifulsoup4 >= 4.9.1
requests >= 2.24.0
requests-cache >= 1.0.0

Python 3.9 or newer is required.

FilmAffinity does not provide a public API for this package. The library uses
HTML scraping, so selectors can break when FilmAffinity changes its layout.


Examples
Expand All @@ -62,6 +73,53 @@ Examples
['Luis Tosar', 'Alberto Ammann', 'Antonio Resines', 'Carlos Bardem', 'Marta Etura', 'Vicente Romero', 'Manuel Morón', 'Manolo Solo', 'Fernando Soto', 'Luis Zahera', 'Patxi Bisquert', 'Félix Cubero', 'Josean Bengoetxea', 'Juan Carlos Mangas', 'Jesús Carroza']


Optional models and exporters
*****************************

The public API keeps returning dictionaries by default. Install the ``models``
extra and pass ``as_model=True`` to receive Pydantic models:

.. code-block:: python

service = python_filmaffinity.FilmAffinity()
movie = service.get_movie(id='495280', as_model=True)
movie.title

Export helpers accept dictionaries, lists and models:

.. code-block:: python

movies = service.search(title='Batman')
python_filmaffinity.to_json(movies, 'movies.json')
python_filmaffinity.to_csv(movies, 'movies.csv')
python_filmaffinity.to_markdown(movies, 'movies.md')


Command line
************

.. code-block:: bash

filmaffinity search "Batman" --top 5
filmaffinity movie 197671 --images --json
filmaffinity top --kind filmaffinity --top 10


Development
***********

.. code-block:: bash

python -m pip install -e ".[dev]"
python -m pytest
RUN_INTEGRATION=1 python -m pytest -m integration
python -m build
twine check dist/*

The persistent sqlite cache is stored in the user's cache directory by default.
Use ``cache_backend="memory"`` for runs that should not write to disk.


Usage
=====

Expand Down Expand Up @@ -213,6 +271,15 @@ recommend HBO, Netflix, Filmin, Movistar, Rakuten

Changelog
=========
v0.1.0 (17-06-2026)
*******************

- Added the ``filmaffinity`` command line interface.
- Added optional Pydantic models via ``as_model=True``.
- Added JSON, CSV and Markdown export helpers.
- Moved the default persistent cache to the user's cache directory.
- Renamed the PyPI publishing workflow to ``publish-to-pypi.yml``.

v0.0.21 (17-06-2026)
********************

Expand Down
29 changes: 28 additions & 1 deletion USAGE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ search
.. code-block:: python

movies = service.search(cast='Nicolas Cage')
movie_models = service.search(title='Batman', as_model=True)


get_movie
Expand All @@ -65,6 +66,32 @@ get_movie

movies = service.get_movie(title='Avatar')
movies = service.get_movie(id='495280')
movie_model = service.get_movie(id='495280', as_model=True)


exporters
*********

- Example

.. code-block:: python

movies = service.search(title='Batman')
python_filmaffinity.to_json(movies, 'movies.json')
python_filmaffinity.to_csv(movies, 'movies.csv')
python_filmaffinity.to_markdown(movies, 'movies.md')


command line
************

- Example

.. code-block:: bash

filmaffinity search "Batman" --top 5
filmaffinity movie 197671 --images --json
filmaffinity top --kind filmaffinity --top 10


top_filmaffinity
Expand Down Expand Up @@ -144,4 +171,4 @@ recommend HBO, Netflix, Filmin, Movistar, Rakuten
movies = service.recommend_hbo()
movies = service.recommend_filmin()
movies = service.recommend_movistar()
movies = service.recommend_rakuten()
movies = service.recommend_rakuten()
14 changes: 10 additions & 4 deletions python_filmaffinity/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
from .__meta__ import *
from .client import Client
from .config import FIELDS_TYPE
from .exporters import to_csv, to_json, to_markdown
from .models import _require_pydantic, movie_to_model, movies_to_models

class FilmAffinity(Client):

def get_movie(self, trailer=False, images=False, **kwargs):
def get_movie(self, trailer=False, images=False, as_model=False, **kwargs):
"""Return a dictionary with the data of the movie.

Args:
Expand All @@ -16,6 +18,8 @@ def get_movie(self, trailer=False, images=False, **kwargs):
Returns:
TYPE: Dictionary with movie data
"""
if as_model:
_require_pydantic()
movie = {}
if kwargs is not None:
for key, value in iter(kwargs.items()):
Expand All @@ -25,9 +29,9 @@ def get_movie(self, trailer=False, images=False, **kwargs):
else:
movie = self._get_movie_by_args(
key, value, trailer, images)
return movie
return movie_to_model(movie) if as_model else movie

def search(self, top=10, **kwargs):
def search(self, top=10, as_model=False, **kwargs):
"""Return a list with the data of the movies.

Args:
Expand Down Expand Up @@ -57,6 +61,8 @@ def search(self, top=10, **kwargs):
Returns:
TYPE: Lis with movies data
"""
if as_model:
_require_pydantic()
top = 20 if top > 20 else top
movies = []
if kwargs is not None:
Expand Down Expand Up @@ -84,7 +90,7 @@ def search(self, top=10, **kwargs):
url = self.url + 'advsearch.php?' + options
page = self._load_url(url)
movies = self._return_list_movies(page, 'search', top)
return movies
return movies_to_models(movies) if as_model else movies

def top_filmaffinity(self, top=10, **kwargs):
"""Return a list with the top filmaffinity movies.
Expand Down
2 changes: 1 addition & 1 deletion python_filmaffinity/__meta__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
__summary__ = 'Python wrapper for FilmAffinity'
__url__ = 'https://github.com/sergiormb/python_filmaffinity'

__version__ = '0.0.21'
__version__ = '0.1.0'

__install_requires__ = [
'beautifulsoup4>=4.9.1',
Expand Down
Loading
Loading