---
id: generationfree-api
name: Generation-Free (API)
description: "Generation-Free (GF-Free) is a FRENCH Private Torrent Tracker for MOVIES / TV / GENERAL"
language: fr-FR
type: private
encoding: UTF-8
links:
  - https://generation-free.org/
legacylinks:
  - https://generation-free.biz/

caps:
  categorymappings:
    - {id: 1, cat: Movies, desc: "Film"}
    - {id: 2, cat: TV, desc: "Série"}
    - {id: 3, cat: Audio, desc: "Musique"}
    - {id: 4, cat: Console, desc: "Jeux"}
    - {id: 5, cat: PC, desc: "Logiciel"}
    - {id: 6, cat: Books, desc: "Ebook"}

  modes:
    search: [q]
    tv-search: [q, season, ep, imdbid, tvdbid, tmdbid]
    movie-search: [q, imdbid, tmdbid]
    music-search: [q]
    book-search: [q]

settings:
  - name: apikey
    type: text
    label: APIKey
  - name: info_key
    type: info
    label: About your API key
    default: "Find or Generate a new API Token by accessing your <a href=\"https://generation-free.org/\" target=\"_blank\">Generation-Free</a> account <i>My Security</i> page and clicking on the <b>API Token</b> tab."
  - name: freeleech
    type: checkbox
    label: Search freeleech only
    default: false
  - name: multilang
    type: checkbox
    label: Replace MULTi by another language in release name
    default: false
  - name: multilanguage
    type: select
    label: Replace MULTi by this language
    default: FRENCH
    options:
      FRENCH: FRENCH
      MULTi FRENCH: MULTi FRENCH
      ENGLISH: ENGLISH
      MULTi ENGLISH: MULTi ENGLISH
      VOSTFR: VOSTFR
      MULTi VOSTFR: MULTi VOSTFR
  - name: vostfr
    type: checkbox
    label: Replace VOSTFR and SUBFRENCH with ENGLISH
    default: false
  - name: vfq
    type: checkbox
    label: Replace VFQ with FRENCH
    default: false
  - name: sort
    type: select
    label: Sort requested from site
    default: created_at
    options:
      created_at: created
      seeders: seeders
      size: size
      name: title
  - name: type
    type: select
    label: Order requested from site
    default: desc
    options:
      desc: desc
      asc: asc
  - name: info_activity
    type: info
    label: Account Inactivity
    default: "Accounts will be automatically deleted after 45 days of inactivity, regardless of class. To maintain an active account, all you have to do is authenticate yourself regularly and participate: In the life of the site, in the forum, in the shoot, in the games of the forum and not only in the downloads. Log in regularly to the site the Rules may change, view your messages."

login:
  path: /api/torrents
  method: get
  inputs: {} # TODO: remove in v10
  error:
    - selector: a[href*="/login"]
      message:
        text: "The API key was not accepted by {{ .Config.sitelink }}."

search:
  paths:
    # https://github.com/HDInnovations/UNIT3D-Community-Edition/wiki/Torrent-API-(UNIT3D-v8.x.x)
    # https://github.com/HDInnovations/UNIT3D-Community-Edition/blob/master/app/Http/Controllers/API/TorrentController.php#L476
    - path: "/api/torrents/filter"
      response:
        type: json

  headers:
    Authorization: ["Bearer {{ .Config.apikey }}"]

  inputs:
  # if we have an id based search, add Season and Episode as query in name for UNIT3D < v6.  Else pass S/E Params for UNIT3D >= v6
    $raw: "{{ range .Categories }}&categories[]={{.}}{{end}}"
    name: "{{ .Keywords }}"
    seasonNumber: "{{ .Query.Season }}"
    episodeNumber: "{{ .Query.Ep }}"
    imdbId: "{{ .Query.IMDBIDShort }}"
    tmdbId: "{{ .Query.TMDBID }}"
    tvdbId: "{{ .Query.TVDBID }}"
    free[]: "{{ if .Config.freeleech }}100{{ else }}{{ end }}"
    sortField: "{{ .Config.sort }}"
    sortDirection: "{{ .Config.type }}"
    perPage: 100

  keywordsfilters:
    - name: re_replace
      args: ["\\.", " "]

  rows:
    selector: data
    attribute: attributes

  fields:
    category:
      selector: category_id
    title_phase1:
      selector: name
    title_vfq:
      text: "{{ .Result.title_phase1 }}"
      filters:
        - name: re_replace
          args: ["(?i)\\b(VFQ)\\b", "FRENCH"]
    title_phase2:
      text: "{{ if .Config.vfq }}{{ .Result.title_vfq }}{{ else }}{{ .Result.title_phase1 }}{{ end }}"
    title_vostfr:
      text: "{{ .Result.title_phase2 }}"
      filters:
        - name: re_replace
          args: ["(?i)\\b(vostfr|subfrench)\\b", "ENGLISH"]
    title_phase3:
      text: "{{ if .Config.vostfr }}{{ .Result.title_vostfr }}{{ else }}{{ .Result.title_phase2 }}{{ end }}"
    title_multilang:
      text: "{{ .Result.title_phase3 }}"
      filters:
        - name: re_replace
          args: ["(?i)\\b(MULTI(?!.*(?:FRENCH|ENGLISH|VOSTFR)))\\b", "{{ .Config.multilanguage }}"]
    title:
      text: "{{ if .Config.multilang }}{{ .Result.title_multilang }}{{ else }}{{ .Result.title_phase3 }}{{ end }}"
    details:
      selector: details_link
    download:
      selector: download_link
    infohash:
      selector: info_hash
    poster:
      selector: meta.poster
      filters:
        - name: replace
          args: ["https://via.placeholder.com/90x135", ""]
    imdbid:
      selector: imdb_id
    tmdbid:
      selector: tmdb_id
    tvdbid:
      selector: tvdb_id
    genre:
      selector: meta.genres
      filters:
        - name: re_replace
          args: ["(?i)^None$", ""]
        - name: replace
          args: [" & ", "_&_"]
    description:
      text: "{{ .Result.genre }}"
    files:
      selector: num_file
    seeders:
      selector: seeders
    leechers:
      selector: leechers
    grabs:
      selector: times_completed
    date:
      # "created_at": "2021-10-18T00:34:50.000000Z" is returned by Newtonsoft.Json.Linq as 18/10/2021 00:34:50
      selector: created_at
      filters:
        - name: append
          args: " +00:00" # GMT
        - name: dateparse
          args: "MM/dd/yyyy HH:mm:ss zzz"
    size:
      selector: size
    downloadvolumefactor:
      # api returns 0%, 25%, 50%, 75%, 100%
      selector: freeleech
      case:
        0%: 1 # not free
        25%: 0.75
        50%: 0.5
        75%: 0.25
        100%: 0 # freeleech
        "*": 0 # catch errors
    uploadvolumefactor:
      # api returns 0=false, 1=true
      selector: double_upload
      case:
        0: 1 # normal
        1: 2 # double
    minimumseedtime:
      # 2 days (as seconds = 2 x 24 x 60 x 60)
      text: 172800
# json UNIT3D 6.5.0
