Skip to content

Commit

Permalink
Merge branch 'main' into issue-44
Browse files Browse the repository at this point in the history
  • Loading branch information
glenrobson committed Jan 9, 2025
2 parents edd7137 + 83bfc16 commit 0c3e85f
Show file tree
Hide file tree
Showing 258 changed files with 182 additions and 89,901 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ '3.9' ]
python-version: [ '3.12' ]
name: Python ${{ matrix.python-version }} sample
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Setup python
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
architecture: x64

- uses: actions/cache@v2
- uses: actions/cache@v4
with:
path: ${{ env.pythonLocation }}
key: ${{ env.pythonLocation }}-${{ hashFiles('setup.py') }}-${{ hashFiles('dev-requirements.txt') }}
Expand Down
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,8 @@ cache/
.idea/

# Old folder from v2?
media/
media/

# Python venv
.venv/
venv/
60 changes: 37 additions & 23 deletions iiify/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from .configs import options, cors, approot, cache_root, media_root, \
cache_expr, version, image_server, cache_timeouts
from urllib.parse import quote
import re


app = Flask(__name__)
Expand All @@ -23,31 +24,13 @@
cors = CORS(app) if cors else None
cache = Cache(app)

ARCHIVE = 'http://archive.org'
ARCHIVE = 'https://archive.org'

# cache.init_app(app)

def sprite_concat(imgs):
from PIL import Image
images = list(map(Image.open, imgs))
widths, heights = zip(*[i.size for i in images])

total_width = sum(widths)
max_height = max(heights)

new_im = Image.new('RGB', (total_width, max_height))

x_offset = 0
for im in images:
new_im.paste(im, (x_offset, 0))
x_offset += im.size[0]
return new_im


def cache_bust():
if request.args.get("recache", "") in ["True", "true", "1"]:
return True
return False
return request.args.get("recache", "") in ["True", "true", "1"]

@app.route('/')
def mainentry():
Expand Down Expand Up @@ -98,8 +81,14 @@ def documentation():

@app.route('/iiif/helper/<identifier>/')
def helper(identifier):
domain = purify_domain(request.args.get('domain', request.url_root))
validate_ia_identifier(identifier, page_suffix=False)

metadata = requests.get('%s/metadata/%s' % (ARCHIVE, identifier)).json()

# If the item doesn't exist, the endpoint 200s with an empty object
if not metadata:
abort(404, f"Identifier '{identifier}' not found")

mediatype = metadata['metadata']['mediatype']

if mediatype == "image":
Expand All @@ -124,6 +113,8 @@ def helper(identifier):

@app.route('/iiif/<identifier>')
def view(identifier):
validate_ia_identifier(identifier, page_suffix=True)

domain = purify_domain(request.args.get('domain', request.url_root))
uri = '%s%s' % (domain, identifier)
page = request.args.get('page', None)
Expand All @@ -142,6 +133,7 @@ def view(identifier):
@app.route('/iiif/3/<identifier>/collection.json')
@cache.cached(timeout=cache_timeouts["med"], forced_update=cache_bust)
def collection3JSON(identifier):
validate_ia_identifier(identifier, page_suffix=False)
domain = purify_domain(request.args.get('domain', request.url_root))

try:
Expand All @@ -159,6 +151,7 @@ def collection3JSON(identifier):
@app.route('/iiif/3/<identifier>/<page>/collection.json')
@cache.cached(timeout=cache_timeouts["med"], forced_update=cache_bust)
def collection3page(identifier, page):
validate_ia_identifier(identifier, page_suffix=False)
domain = purify_domain(request.args.get('domain', request.url_root))

try:
Expand All @@ -177,18 +170,22 @@ def collection3page(identifier, page):
@app.route('/iiif/<identifier>/collection.json')
@cache.cached(timeout=cache_timeouts["long"], forced_update=cache_bust)
def collectionJSON(identifier):
validate_ia_identifier(identifier, page_suffix=False)
return redirect(f'/iiif/3/{identifier}/collection.json', code=302)


@app.route('/iiif/<identifier>/<page>/collection.json')
@cache.cached(timeout=cache_timeouts["long"], forced_update=cache_bust)
def collectionPage(identifier, page):
validate_ia_identifier(identifier, page_suffix=False)
return redirect(f'/iiif/3/{identifier}/{page}/collection.json', code=302)


@app.route('/iiif/3/<identifier>/manifest.json')
@cache.cached(timeout=cache_timeouts["long"], forced_update=cache_bust)
def manifest3(identifier):
validate_ia_identifier(identifier, page_suffix=False)

domain = purify_domain(request.args.get('domain', request.url_root))
page = None

Expand All @@ -203,26 +200,30 @@ def manifest3(identifier):
raise excpt
# abort(404)

@app.route('/iiif/<version>/annotations/<identifier>/<fileName>/<canvas_no>.json')
@app.route('/iiif/<int:version>/annotations/<identifier>/<fileName>/<int:canvas_no>.json')
@cache.cached(timeout=cache_timeouts["long"], forced_update=cache_bust)
def annnotations(version, identifier, fileName, canvas_no):
def annnotations(version: str, identifier: str, fileName: str, canvas_no: int):
validate_ia_identifier(identifier, page_suffix=False)
domain = purify_domain(request.args.get('domain', request.url_root))
return ldjsonify(create_annotations(version, identifier, fileName, canvas_no, domain=domain))

@app.route('/iiif/vtt/streaming/<identifier>.vtt')
@cache.cached(timeout=cache_timeouts["long"], forced_update=cache_bust)
def vtt_stream(identifier):
validate_ia_identifier(identifier, page_suffix=False)
response = make_response(create_vtt_stream(identifier))
response.headers['Content-Type'] = 'text/vtt'
return response

@app.route('/iiif/<identifier>/manifest.json')
@cache.cached(timeout=cache_timeouts["long"], forced_update=cache_bust)
def manifest(identifier):
validate_ia_identifier(identifier, page_suffix=False)
return redirect(f'/iiif/3/{identifier}/manifest.json', code=302)

@app.route('/iiif/2/<identifier>/manifest.json')
def manifest2(identifier):
validate_ia_identifier(identifier, page_suffix=True)
domain = purify_domain(request.args.get('domain', request.url_root))
page = None
if '$' in identifier:
Expand All @@ -238,24 +239,28 @@ def manifest2(identifier):

@app.route('/iiif/<identifier>/info.json')
def info(identifier):
validate_ia_identifier(identifier, page_suffix=True)
cantaloupe_id = cantaloupe_resolver(identifier)
cantaloupe_url = f"{image_server}/2/{cantaloupe_id}/info.json"
return redirect(cantaloupe_url, code=302)

@app.route('/iiif/3/<identifier>/info.json')
def info3(identifier):
validate_ia_identifier(identifier, page_suffix=True)
cantaloupe_id = cantaloupe_resolver(identifier)
cantaloupe_url = f"{image_server}/3/{cantaloupe_id}/info.json"
return redirect(cantaloupe_url, code=302)

@app.route('/iiif/2/<identifier>/info.json')
def info2(identifier):
validate_ia_identifier(identifier, page_suffix=True)
cantaloupe_id = cantaloupe_resolver(identifier)
cantaloupe_url = f"{image_server}/2/{cantaloupe_id}/info.json"
return redirect(cantaloupe_url, code=302)

@app.route('/iiif/<identifier>/<region>/<size>/<rotation>/<quality>.<fmt>')
def image_processor(identifier, region, size, rotation, quality, fmt):
validate_ia_identifier(identifier, page_suffix=True)
cantaloupe_id = cantaloupe_resolver(identifier)
cantaloupe_url = f"{image_server}/2/{cantaloupe_id}/{region}/{size}/{rotation}/{quality}.{fmt}"
return redirect(cantaloupe_url, code=302)
Expand All @@ -272,6 +277,15 @@ def ldjsonify(data):
j.mimetype = "application/ld+json"
return j

def validate_ia_identifier(identifier: str, page_suffix: bool) -> None:
if page_suffix:
if not re.match(r'^[a-zA-Z0-9_.-]{1,100}(\$\d+)?$', identifier):
abort(400, "Invalid identifier")
return

if not re.match(r'^[a-zA-Z0-9_.-]{1,100}$', identifier):
abort(400, "Invalid identifier")
return

if __name__ == '__main__':
app.run(**options)
Loading

0 comments on commit 0c3e85f

Please sign in to comment.