
# Franchises

A franchise contains the films and series of one story, in story order,
with its own calendar. People write its files in a git repository. A
`Library` of kind `franchises` reads a checkout of that repository and
resolves each member against the other libraries in the namespace. This
guide puts a checkout on a claim and declares the `Library`.
[Franchise files](/docs/reference/franchises/) describes the file
format.

## 1. The checkout

The public repository at
[`tangled.org/guid.foo/fiction-franchises`](https://tangled.org/guid.foo/fiction-franchises)
holds the first files, one directory per franchise. How a checkout
reaches a claim is your choice. Any volume with one directory per
franchise works. The [git CSI driver](https://liken.sh/git/) is one
way, and it keeps the checkout current:

    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: franchises-repo
    spec:
      capacity: {storage: 1Gi}
      accessModes: [ReadOnlyMany]
      persistentVolumeReclaimPolicy: Retain
      storageClassName: ""
      csi:
        driver: git.liken.sh
        volumeHandle: franchises-repo
        readOnly: true
        volumeAttributes:
          url: https://tangled.org/guid.foo/fiction-franchises
          ref: main
          pull: 5m
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: franchises-repo
      namespace: media
    spec:
      accessModes: [ReadOnlyMany]
      storageClassName: ""
      volumeName: franchises-repo
      resources: {requests: {storage: 1Gi}}

Every `Job` of the library mounts the storage claim with `readOnly: true`
on the pod's volume, which is what the driver requires of a
`ReadOnlyMany` volume. A screen does not mount the storage claim. It reads
the art claim.
[Read-only volumes](https://liken.sh/git/docs/guides/read-only/) in
the driver's manual covers `offline: allowStale` and private
repositories.

## 2. The art claim and the Library

The checkout is read-only, so the art a scan downloads needs a claim
of its own. Every `Job` of the library writes it, and every screen
that shows the library mounts it read-only. So the claim has to
allow those mounts at once:

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: franchises-art
      namespace: media
    spec:
      accessModes: [ReadWriteMany]
      resources: {requests: {storage: 1Gi}}
    ---
    apiVersion: library.liken.sh/v1alpha1
    kind: Library
    metadata:
      name: franchises
      namespace: media
    spec:
      storage:
        claim: franchises-repo
      kind: franchises
      franchises:
        art:
          claim: franchises-art

## 3. What a scan does

A scan walks the checkout in name order. Each directory with a
`franchise.yaml` is a franchise, named by its directory, so a renamed
directory is a new row. A directory without one is skipped, which is
how the checkout's own `.git` is skipped. A file the schema refuses is
counted in `status.unidentified` and reported by name, and the files
beside it still write their rows.

Before it reads the rows, the scan downloads the art each file links,
into the art claim under Kodi's names. A link the last scan already
read is not read again, and a file the scan did not write is kept.

A member is a provider id: `tmdb:` for a film and `tvdb:` for a
series. The browser joins it against every library of the namespace at
read time. A member no library holds draws as a gap:
coming when its release date is ahead, missing otherwise. Add the
title to a movies or series library, and the gap fills on that
library's next scan.

## 4. Write a franchise

Put the schema line at the top of the file, so an editor validates it:

    # yaml-language-server: $schema=https://liken.sh/library/franchise.schema.json

    name: Example Saga

    sources:
      - https://example.com/example-saga-timeline

    calendar:
      unit: years
      zero: the Founding
      before: BF
      after: AF

    universe: Prime

    eras:
      - name: The First Age
        from: -10
        to: 0
      - name: The Second Age
        from: 0
        to: 12

    order:
      - movie: tmdb:900001
        title: "Example Saga: The Beginning"
        released: 2001-03-10
        time: { from: -2, to: -2 }
      - series: tvdb:900002
        title: "Example Saga: The Chronicles"
        released: 2003-09-01
        seasons:
          - season: 1
            time: { from: 0, to: 0 }
          - season: 2
            episodes: [S02E01, S02E03-S02E10]
            time: { from: 1, to: 1 }
      - movie: tmdb:900003
        title: "Example Saga: Convergence"
        released: 2010-07-04
        universes: [Prime, Offshoot]
        time: { from: 12, to: 12 }

`order` is the story, first to last. A series with seasons is one row
on the wall, and a season with an episode range is one run inside it.
An entry that names no universes is in the franchise's own. The
calendar is the story's clock, apart from `released`, the real date.

A fork is a second `Library` over another checkout. Two libraries that
both define one franchise are two rows on the screen.

