From f3b9c2c0f5700feb444e13b8afbc5e794bffdd26 Mon Sep 17 00:00:00 2001 From: j Date: Tue, 14 Nov 2023 14:09:43 +0100 Subject: [PATCH] add config, bootstrap from external source --- config.jsonc | 1412 ++++++++++++++++++++++++++++++++++++++++++++++++++ install.py | 4 +- utils.py | 70 +++ 3 files changed, 1484 insertions(+), 2 deletions(-) create mode 100644 config.jsonc create mode 100644 utils.py diff --git a/config.jsonc b/config.jsonc new file mode 100644 index 0000000..4348dd5 --- /dev/null +++ b/config.jsonc @@ -0,0 +1,1412 @@ +/* + Pan.do/ra Settings + + You can edit this file. +*/ +{ + /* + "annotations" configures the annotation panel. + "showUsers": If true, include list of users in menu, so that + annotations by specific users can be turned on and off + */ + "annotations": { + "separator": ";", + "showUsers": true + }, + /* + "cantPlay" specifies the UI for clips or videos that a user is not allowed + to play. + "icon": Symbol name (see https://oxjs.org/#examples/symbols/live) + "link": URL loaded on click (for example "/rights", if "rights" is + defined in "sitePages") + "text": Text shown on mouseover + */ + "cantPlay": { + "icon": "", + "link": "", + "text": "" + }, + /* + "capabilities" are permissions per user level. + Depending on the capability, this is either global: + {level: true} means a user of that level has the capability + or related to the rights levels of items or texts: + {level: x} means a user of that level has the capability for items or + texts of a rights level up to and including x + */ + "capabilities": { + "canAddItems": {"member": true, "staff": true, "admin": true}, + "canAddDocuments": {"member": true, "staff": true, "admin": true}, + "canDownloadVideo": {"guest": 0, "member": 0, "staff": 4, "admin": 4}, + "canDownloadSource": {"guest": -1, "member": -1, "staff": 4, "admin": 4}, + "canEditAnnotations": {"staff": true, "admin": true}, + "canEditEntities": {"staff": true, "admin": true}, + "canEditDocuments": {"staff": true, "admin": true}, + "canEditEvents": {"member": true, "staff": true, "admin": true}, + "canEditFeaturedCollections": {"staff": true, "admin": true}, + "canEditFeaturedEdits": {"staff": true, "admin": true}, + "canEditFeaturedLists": {"staff": true, "admin": true}, + "canEditFeaturedTexts": {"staff": true, "admin": true}, + "canEditMedia": {"staff": true, "admin": true}, + "canEditMetadata": {"staff": true, "admin": true}, + "canEditPlaces": {"member": true, "staff": true, "admin": true}, + "canEditRightsLevel": {"member": true, "staff": true, "admin": true}, + "canEditSitePages": {"staff": true, "admin": true}, + "canEditUsers": {"admin": true}, + "canExportAnnotations": {"member": true, "staff": true, "admin": true}, + "canImportAnnotations": {"member": true, "staff": true, "admin": true}, + "canImportItems": {"member": true, "staff": true, "admin": true}, + "canManageDocuments": {"member": true, "staff": true, "admin": true}, + "canManageEntities": {"member": true, "staff": true, "admin": true}, + "canManageHome": {"staff": true, "admin": true}, + "canManagePlacesAndEvents": {"member": true, "staff": true, "admin": true}, + "canManageTitlesAndNames": {"member": true, "staff": true, "admin": true}, + "canManageTranslations": {"admin": true}, + "canManageUsers": {"staff": true, "admin": true}, + "canPlayClips": {"guest": 1, "member": 1, "staff": 4, "admin": 4}, + "canPlayVideo": {"guest": 1, "member": 1, "staff": 4, "admin": 4}, + "canReadText": {"guest": 0, "member": 0, "staff": 1, "admin": 1}, + "canRemoveItems": {"admin": true}, + "canRemoveDocuments": {"staff": true, "admin": true}, + "canSeeAccessed": {"staff": true, "admin": true}, + "canSeeAllTasks": {"staff": true, "admin": true}, + "canSeeDebugMenu": {"staff": true, "admin": true}, + "canSeeDocument": {"guest": 1, "member": 1, "staff": 4, "admin": 4}, + "canSeeExtraItemViews": {"staff": true, "admin": true}, + "canSeeItem": {"guest": 1, "member": 1, "staff": 4, "admin": 4}, + "canSeeMedia": {"staff": true, "admin": true}, + "canSeeSize": {"staff": true, "admin": true}, + "canSeeSoftwareVersion": {"staff": true, "admin": true}, + "canSendMail": {"staff": true, "admin": true}, + "canShare": {"staff": true, "admin": true} + }, + /* + "clipKeys" are the properties that clips can be sorted by (the values are + populated automatically). If "sortOperator" is not specified, it will be + "+" (ascending) for strings and "-" (descending) for numbers. + */ + "clipKeys": [ + {"id": "text", "title": "Text", "type": "string"}, + {"id": "position", "title": "Position", "type": "float", "sortOperator": "+"}, + {"id": "duration", "title": "Duration", "type": "float"}, + {"id": "hue", "title": "Hue", "type": "float", "sortOperator": "+"}, + {"id": "saturation", "title": "Saturation", "type": "float"}, + {"id": "lightness", "title": "Lightness", "type": "float"}, + {"id": "volume", "title": "Volume", "type": "float"} + ], + /* + "clipLayers" is the ordered list of public layers that will appear as the + text of clips (in grid view, below the icon). Excluding a layer from this + list means it will not be included in find annotations. + */ + "clipLayers": ["transcripts", "keywords", "places", "events", "descriptions"], + /* + "documentKeys" defines the metadata associated with each document. Required keys + are "*", "id" and "title". + A documentKey must have the following properties: + "id": The unique id of the key (as used by the server) + "title": The title of the key (as displayed by the client) + "type": Can be "boolean", "date", "enum", "float", "hue", "integer", + "layer", "string", "text", "time" or ["..."] (list of values of + this type). If type is "layer", this is a reference to the + annotations layer with the same id. + and can have any of the following properties: + "additionalSort": Ordered list of {key, operator} objects, where key is + another itemKey and operator is "+" or "-". This can be used to + override user.ui.listSort when results are sorted by this key. + "autocomplete": If true, the find element will provide autocomplete + "autocompleteSort": Sort order of autocomplete suggestions + "capability": A capability required to see data for this key + "columnRequired": If true, the column can't be removed from list view + "columnWidth": Default column width in px. If absent, no column for + this key can be added in list view. + "filter": If true, one can filter results by this key + "find": If true, this key will appear as an option in the find element + "flag": Can be "country" or "language". If set (and filter is true), a + flag icon corresponding to the field's value will be displayed. + "format": {type: string, args: [value, value, ...]}, used for special + formatting. This will invoke Ox.formatType(args). For details, see + https://oxjs.org/#doc/Ox.formatArea etc. + "secondaryId": If true, loading the URL "/value" will redirect to the + corresponding item, in case there is an exact match for this key + "sort": If true, one can sort results by this key + "sortOperator": Sort order ("+" or "-"), in case it differs from the + default for the key's type ("+" for strings, "-" for numbers) + "sortType": Special sort type ("person" or "title") which can be + further configured in "Manage Names" or "Manage Titles" + "value": {key: string, type: string} or {layer: string, type: string}, + for keys whose value is derived from other keys or layers (like + "number of actors" or "words per minute"). Possible values for type + are "length", "lengthperminute", "words", and "wordsperminute". + Alternatively, "value" can be set to the string "capability", which + results in an itemKey whose boolean value indicates the presence or + absence of a userLevel-dependent capability. This can be used to + create queries and lists like "all items this user can play" etc. + "values": [value, value, ...] Ordered list of values, in case "type" is + "enum" + */ + "documentKeys": [ + { + "id": "*", + "title": "All", + "type": "text", + "find": true + }, + { + "id": "title", + "operator": "+", + "title": "Title", + "type": "string", + "find": true, + "sort": true, + "sortType": "title", + "autocomplete": true, + "columnWidth": 256 + }, + { + "id": "type", + "operator": "+", + "title": "Type", + "type": "string", + "filter": true, + "find": true, + "sort": true, + "autocomplete": true, + "columnWidth": 128 + }, + { + "id": "author", + "operator": "+", + "title": "Author", + "type": ["string"], + "filter": true, + "find": true, + "sort": true, + "sortType": "person", + "autocomplete": true, + "columnWidth": 256 + }, + { + "id": "publisher", + "operator": "+", + "title": "Publisher", + "type": "string", + "filter": true, + "find": true, + "sort": true, + "autocomplete": true, + "columnWidth": 256 + }, + { + "id": "place", + "title": "Place", + "type": ["string"], + "columnWidth": 128, + "filter": true, + "find": true, + "sort": true + }, + { + "id": "date", + "title": "Date", + "type": "string", + "columnWidth": 120, + //"format": {"type": "date", "args": ["%a, %b %e, %Y"]}, + "filter": true, + "sort": true + }, + { + "id": "series", + "title": "Series", + "type": "string", + "columnWidth": 128, + "find": true, + "sort": true + }, + { + "id": "edition", + "title": "Edition", + "type": "string", + "columnWidth": 128, + "find": true + }, + { + "id": "language", + "title": "Language", + "type": ["string"], + "columnWidth": 128, + "filter": true, + "find": true, + "sort": true + }, + { + "id": "keywords", + "title": "Keywords", + "type": ["string"], + "columnWidth": 128, + "filter": true, + "find": true + }, + { + "id": "id", + "operator": "+", + "title": "ID", + "type": "string", + "sort": true, + "columnWidth": 64 + }, + { + "id": "extension", + "operator": "+", + "title": "Extension", + "type": "string", + "filter": true, + "find": true, + "sort": true, + "autocomplete": true, + "columnWidth": 64 + }, + { + "id": "dimensions", + "operator": "-", + "title": "Dimensions", + "type": "integer", + "sort": true, + "columnWidth": 128 + }, + { + "id": "size", + "operator": "-", + "title": "Size", + "type": "integer", + "sort": true, + "format": {"type": "value", "args": ["B"]}, + "columnWidth": 64 + }, + { + "id": "description", + "operator": "+", + "title": "Description", + "type": "text", + "find": true, + "sort": true, + "columnWidth": 256 + }, + { + "id": "matches", + "operator": "-", + "title": "Matches", + "type": "integer", + "sort": true, + "columnWidth": 64 + }, + { + "id": "user", + "operator": "+", + "title": "User", + "type": "string", + "filter": true, + "find": true, + "sort": true, + "autocomplete": true, + "columnWidth": 128 + }, + { + "id": "fulltext", + "operator": "+", + "title": "Fulltext", + "type": "text", + "fulltext": true, + "find": true + }, + { + "id": "created", + "operator": "-", + "title": "Created", + "format": {"type": "date", "args": ["%Y-%m-%d %H:%M:%S"]}, + "type": "date", + "sort": true, + "columnWidth": 144 + }, + { + "id": "modified", + "operator": "-", + "title": "Modified", + "format": {"type": "date", "args": ["%Y-%m-%d %H:%M:%S"]}, + "type": "date", + "sort": true, + "columnWidth": 144 + }, + { + "id": "accessed", + "title": "Last Accessed", + "type": "date", + "capability": "canSeeAccessed", + "columnWidth": 150, + "format": {"type": "date", "args": ["%Y-%m-%d %H:%M:%S"]}, + "sort": true + }, + { + "id": "timesaccessed", + "title": "Times Accessed", + "type": "integer", + "capability": "canSeeAccessed", + "columnWidth": 60, + "format": {"type": "number", "args": []}, + "sort": true + }, + { + "id": "rightslevel", + "title": "Rights Level", + "type": "enum", + "columnWidth": 90, + "format": {"type": "ColorLevel", "args": [ + ["Public", "Restricted", "Private"] + ]}, + "sort": true, + "sortOperator": "+", + "values": ["Public", "Restricted", "Private", "Unknown"] + } + ], + /* + "documentRightsLevel" defines which initial rights level will be assigned to documents + created by users of these user levels. + */ + "documentRightsLevel": {"member": 0, "staff": 0, "admin": 0}, + /* + "documentRightsLevels" is an ordered list of rights levels, one of which will be + assigned to each document. + */ + "documentRightsLevels": [ + {"name": "Public", "color": [128, 255, 128]}, + {"name": "Restricted", "color": [255, 212, 128]}, + {"name": "Private", "color": [255, 128, 128]} + ], + /* + "entities" can be used to store arbitrary data. They can be referenced in + annotations, info view, or elsewhere. Each entry defines a specific class + of entity object, its properties and their types (for example an "actor" + with "name", "biography", "portrait" etc). The HTML representation for + entities can be customized by adding "/static/js/entity.SITENAME.js". + Each entity has the following properties: + "id": Unique internal ID (can be referenced in "layers") + "title": Human-readable title + "keys": List of properties + "sortType": Optional. If set to "person" or "title", then the sort + order can be configured in "Manage Names" or "Manage Titles" + Each key has the properties "id", "title" and "type". "type" can be + "boolean", "number", "string", "text", "document" (reference to the id of a + document added via "Manage Documents") or ["string"] (list of strings). + "id" and "name" keys are required. If "alternativeNames" is present, these + will act as synonyms when autocompleting values entered as annotations. + */ + "entities": [], + /* + "flags", if set to true, will cause flag icons to appear in filters. + */ + "flags": false, + /* + "help" specifies the sections of the help dialog. + */ + "help": [ + {"id": "help", "title": "Help"}, + {"id": "accounts", "title": "Accounts"}, + {"id": "navigation", "title": "Navigation"}, + {"id": "views", "title": "Views"}, + {"id": "timelines", "title": "Timelines"}, + {"id": "clips", "title": "Clips"}, + {"id": "maps", "title": "Maps"}, + {"id": "calendars", "title": "Calendars"}, + {"id": "find", "title": "Find"}, + {"id": "filters", "title": "Filters"}, + {"id": "lists", "title": "Lists"}, + {"id": "player", "title": "Player"}, + {"id": "editor", "title": "Editor"}, + {"id": "documents", "title": "Documents"}, + {"id": "entities", "title": "Entities"}, + {"id": "edits", "title": "Edits"}, + {"id": "texts", "title": "Texts"}, + {"id": "embeds", "title": "Embeds"} + ], + /* + "importMetadata" defines what fields (as defined in "itemKeys") will get + populated with imported metadata. There is metadata for "title", + "description", "uploader", "date", "tags", "id", "url", and the value must + be a format string (like "{title} ({id})"). The type of the itemKeys must + be "string", ["string"], "text", "date" or "year". The last two only work + with "{date}". + */ + "importMetadata": { + "date": "{date}", + "topic": "{tags}", + "summary": "{description}", + "title": "{title}" + }, + /* + "itemKeys" defines the metadata associated with each item. Required keys + are "*", "id" and "title". Annotation layers can be referenced too, which + makes them available in the find element. Adding a key with "annotations" + as id adds a "find in any annotation layer" option. Further, adding a key + with "random" as id can be used for a random sort order. + An itemKey must have the following properties: + "id": The unique id of the key (as used by the server) + "title": The title of the key (as displayed by the client) + "type": Can be "boolean", "date", "enum", "float", "hue", "integer", + "layer", "string", "text", "time" or ["..."] (list of values of + this type). If type is "layer", this is a reference to the + annotations layer with the same id. + and can have any of the following properties: + "additionalSort": Ordered list of {key, operator} objects, where key is + another itemKey and operator is "+" or "-". This can be used to + override user.ui.listSort when results are sorted by this key. + "autocomplete": If true, the find element will provide autocomplete + "autocompleteSort": Sort order of autocomplete suggestions + "capability": A capability required to see data for this key + "columnRequired": If true, the column can't be removed from list view + "columnWidth": Default column width in px. If absent, no column for + this key can be added in list view. + "filter": If true, one can filter results by this key + "find": If true, this key will appear as an option in the find element + "flag": Can be "country" or "language". If set (and filter is true), a + flag icon corresponding to the field's value will be displayed. + "format": {type: string, args: [value, value, ...]}, used for special + formatting. This will invoke Ox.formatType(args). For details, see + https://oxjs.org/#doc/Ox.formatArea etc. + "secondaryId": If true, loading the URL "/value" will redirect to the + corresponding item, in case there is an exact match for this key + "sort": If true, one can sort results by this key + "sortOperator": Sort order ("+" or "-"), in case it differs from the + default for the key's type ("+" for strings, "-" for numbers) + "sortType": Special sort type ("person" or "title") which can be + further configured in "Manage Names" or "Manage Titles" + "value": {key: string, type: string} or {layer: string, type: string}, + for keys whose value is derived from other keys or layers (like + "number of actors" or "words per minute"). Possible values for type + are "length", "lengthperminute", "words", and "wordsperminute". + Alternatively, "value" can be set to the string "capability", which + results in an itemKey whose boolean value indicates the presence or + absence of a userLevel-dependent capability. This can be used to + create queries and lists like "all items this user can play" etc. + "values": [value, value, ...] Ordered list of values, in case "type" is + "enum" + */ + "itemKeys": [ + { + "id": "*", + "title": "All", + "type": "text", + "find": true + }, + { + "id": "title", + "title": "Title", + "type": "string", + "autocomplete": true, + "autocompleteSort": [{"key": "timesaccessed", "operator": "-"}], + "columnRequired": true, + "columnWidth": 180, + "find": true, + "sort": true, + "sortType": "title" + }, + { + "id": "source", + "title": "Source", + "type": "string", + "autocomplete": true, + "description": true, + "columnWidth": 180, + "filter": true, + "find": true, + "sort": true + }, + { + "id": "project", + "title": "Project", + "type": "string", + "autocomplete": true, + "description": true, + "columnWidth": 120, + "filter": true, + "find": true, + "sort": true + }, + { + "id": "topic", + "title": "Topic", + "type": ["string"], + "autocomplete": true, + "columnWidth": 180, + "filter": true, + "find": true, + "sort": true + }, + { + "id": "methodologies", + "title": "Methodologies", + "type": ["string"], + "autocomplete": true, + "columnWidth": 180, + "filter": true, + "find": true, + "sort": true + }, + { + "id": "collaborators", + "title": "Collaborators", + "type": ["string"], + "autocomplete": true, + "columnWidth": 180, + "filter": true, + "find": true, + "sort": true + }, + { + "id": "exhibitions", + "title": "Exhibitions", + "type": ["string"], + "autocomplete": true, + "columnWidth": 180, + "filter": true, + "find": true, + "sort": true + }, + { + "id": "additionalfunding", + "title": "additional funding", + "type": ["string"], + "autocomplete": true, + "columnWidth": 180, + "filter": true, + "find": true, + "sort": true + }, + { + "id": "commissionedby", + "title": "commissioned by", + "type": ["string"], + "autocomplete": true, + "columnWidth": 180, + "filter": true, + "find": true, + "sort": true + }, + { + "id": "links", + "title": "Links", + "type": ["string"], + "columnWidth": 180, + "find": true + }, + { + "id": "name", + "title": "People", + "type": ["string"], + "autocomplete": true, + "filter": true, + "find": true + }, + { + "id": "director", + "title": "Director", + "type": ["string"], + "autocomplete": true, + "columnWidth": 180, + "sort": true, + "sortType": "person" + }, + { + "id": "cinematographer", + "title": "Cinematographer", + "type": ["string"], + "autocomplete": true, + "columnWidth": 180, + "sort": true, + "sortType": "person" + }, + { + "id": "featuring", + "title": "Featuring", + "type": ["string"], + "autocomplete": true, + "columnWidth": 180, + "filter": true, + "sort": true, + "sortType": "person" + }, + { + "id": "language", + "title": "Language", + "type": ["string"], + "autocomplete": true, + "columnWidth": 120, + "filter": true, + "find": true, + "sort": true + }, + { + "id": "location", + "title": "Location", + "type": "string", + "autocomplete": true, + "columnWidth": 180, + "filter": true, + "find": true, + "sort": true + }, + { + "id": "date", + "title": "Date", + "type": "string", + "columnWidth": 120, + //"format": {"type": "date", "args": ["%a, %b %e, %Y"]}, + "sort": true + }, + { + "id": "summary", + "title": "Summary", + "type": "text", + "find": true + }, + { + "id": "notes", + "title": "Notes", + "type": "text", + "capability": "canEditMetadata" + }, + { + "id": "id", + "title": "ID", + "type": "string", + "columnWidth": 90, + "sort": true + }, + { + "id": "annotations", + "title": "Annotations", + "type": "text", // fixme: not the best type for this magic key + "find": true + }, + { + "id": "places", + "title": "Places", + "type": "layer", + "filter": true, + "find": true + }, + { + "id": "events", + "title": "Events", + "type": "layer", + "filter": true, + "find": true + }, + { + "id": "keywords", + "title": "Keywords", + "type": "layer", + "filter": true, + "find": true + }, + { + "id": "descriptions", + "title": "Descriptions", + "type": "layer", + "find": true + }, + { + "id": "transcripts", + "title": "Transcripts", + "type": "layer", + "find": true + }, + { + "id": "numberofannotations", + "title": "Number of Annotations", + "type": "integer", + "columnWidth": 60, + "sort": true + }, + { + "id": "duration", + "title": "Duration", + "type": "time", + "columnWidth": 90, + "format": {"type": "duration", "args": [0]}, + "sort": true + }, + { + "id": "resolution", + "title": "Resolution", + "type": ["integer"], + "columnWidth": 90, + "format": {"type": "resolution", "args": ["px"]}, + "sort": true + }, + { + "id": "aspectratio", + "title": "Aspect Ratio", + "type": "float", + "columnWidth": 90, + "format": {"type": "unit", "args": [":1", 3]}, + "sort": true + }, + { + "id": "pixels", + "title": "Pixels", + "type": "integer", + "columnWidth": 90, + "format": {"type": "value", "args": ["px"]}, + "sort": true + }, + { + "id": "hue", + "title": "Hue", + "type": "hue", + "columnWidth": 90, + "format": {"type": "color", "args": ["hue"]}, + "sort": true, + "sortOperator": "+" + }, + { + "id": "saturation", + "title": "Saturation", + "type": "float", + "columnWidth": 90, + "format": {"type": "color", "args": ["saturation"]}, + "sort": true + }, + { + "id": "lightness", + "title": "Lightness", + "type": "float", + "columnWidth": 90, + "format": {"type": "color", "args": ["lightness"]}, + "sort": true + }, + { + "id": "volume", + "title": "Volume", + "type": "float", + "columnWidth": 60, + "format": {"type": "color", "args": ["lightness"]}, + "sort": true + }, + { + "id": "numberofcuts", + "title": "Number of Cuts", + "type": "integer", + "columnWidth": 60, + "format": {"type": "number", "args": []}, + "sort": true, + "value": {"key": "cuts", "type": "length"} + }, + { + "id": "cutsperminute", + "title": "Cuts per Minute", + "type": "float", + "columnWidth": 60, + "format": {"type": "number", "args": [3]}, + "sort": true, + "value": {"key": "cuts", "type": "lengthperminute"} + }, + { + "id": "words", + "title": "Number of Words", + "type": "integer", + "columnWidth": 60, + "format": {"type": "number", "args": []}, + "sort": true, + "value": {"layer": "transcripts", "type": "words"} + }, + { + "id": "wordsperminute", + "title": "Words per Minute", + "type": "float", + "columnWidth": 60, + "format": {"type": "number", "args": [3]}, + "sort": true, + "value": {"layer": "transcripts", "type": "wordsperminute"} + }, + { + "id": "size", + "title": "Size", + "type": "integer", + "capability": "canSeeMedia", + "columnWidth": 60, + "format": {"type": "value", "args": ["B"]}, + "sort": true + }, + { + "id": "bitrate", + "title": "Bitrate", + "type": "integer", + "columnWidth": 60, + "format": {"type": "value", "args": ["bps"]}, + "sort": true + }, + { + "id": "numberoffiles", + "title": "Number of Files", + "type": "integer", + "capability": "canSeeMedia", + "columnWidth": 60, + "sort": true, + "value": {"key": "files", "type": "length"} + }, + { + "id": "user", + "title": "User", + "type": "string", + "columnWidth": 90, + "capability": "canSeeMedia", + "sort": true, + "find": true + }, + { + "id": "groups", + "title": "Group", + "columnWidth": 90, + "sort": true, + "type": ["string"] + }, + { + "id": "filename", + "title": "Filename", + "type": ["string"], + "capability": "canSeeMedia", + "find": true + }, + { + "id": "created", + "title": "Date Created", + "type": "date", + "columnWidth": 120, + "format": {"type": "date", "args": ["%Y-%m-%d %H:%M:%S"]}, + "sort": true + }, + { + "id": "modified", + "title": "Last Modified", + "type": "date", + "columnWidth": 90, + "format": {"type": "date", "args": ["%Y-%m-%d %H:%M:%S"]}, + "sort": true + }, + { + "id": "accessed", + "title": "Last Accessed", + "type": "date", + "capability": "canSeeAccessed", + "columnWidth": 90, + "format": {"type": "date", "args": ["%Y-%m-%d %H:%M:%S"]}, + "sort": true + }, + { + "id": "timesaccessed", + "title": "Times Accessed", + "type": "integer", + "capability": "canSeeAccessed", + "columnWidth": 60, + "format": {"type": "number", "args": []}, + "sort": true + }, + { + "id": "license", + "title": "License", + "type": ["string"], + "columnWidth": 120, + "filter": true, + "sort": true, + "default": [ + "Creative Commons Attribution-ShareAlike 3.0 License", + "Pad.ma General Public License" + ] + }, + { + "id": "rightslevel", + "title": "Rights Level", + "type": "enum", + "columnWidth": 90, + "format": {"type": "ColorLevel", "args": [ + ["Public", "Restricted", "Private"] + ]}, + "sort": true, + "sortOperator": "+", + "values": ["Public", "Restricted", "Private", "Unknown"] + }, + { + "id": "random", + "title": "Random", + "type": "integer", + "sort": true + } + ], + /* + "itemName" specifies the name of items ("Movies", "Videos" or similar). + Note that anything excessively long may cause layout errors. + */ + "itemName": { + "singular": "Video", + "plural": "Videos" + }, + /* + "itemRequiresVideo" regulates if items without video can be created. If set + to true, the only way to add a new item is to upload a new video. + */ + "itemRequiresVideo": true, + /* + "itemTitleKeys" is a list of itemKeys required to compose the item title + displayed at the top of the screen. This title can be customized by adding + "/static/js/getItemTitle.SITENAME.js". + */ + "itemTitleKeys": ["title", "director", "date"], + /* + "itemViews" is an ordered list of available item views. Implemented views + are "info", "documents", "player", "editor", "timeline", "clips", "map", + "calendar", "data" and "media". + */ + "itemViews": [ + {"id": "info", "title": "Info"}, + {"id": "player", "title": "Player"}, + {"id": "editor", "title": "Editor"}, + {"id": "timeline", "title": "Timeline"}, + {"id": "clips", "title": "Clips"}, + {"id": "map", "title": "Map"}, + {"id": "calendar", "title": "Calendar"}, + {"id": "documents", "title": "Documents"}, + {"id": "data", "title": "Data"}, + {"id": "media", "title": "Media"} + ], + /* + "language" is the default language for annotations of type "text". Text in + other languages can be added via markup, for example: + VoilĂ ! + */ + "language": "en", + /* + "languages" is the list of languages that the interface can be switched to. + Currently, localization exists for "ar", "el", "en" and "hi". + */ + "languages": ["ar", "el", "en", "hi"], + /* + "layers" defines the types of time-based annotations that can be entered. + Required keys are: + "id": Internal ID + "item": Name of one such annotation (singular) + "title": Display title + "type": Can be "entity", "string" or "text" (strings cannot contain + line breaks, text can contain HTML markup). + Optional keys are: + "autocomplete": Available if the layer is used as a filter + "canAddAnnotations": Permissions per user level + "canPlayClips": If true, clips from this layer will play for users + with canPlayClips access + "entity": ID of the referenced entity (if type is "entity") + "hasEvents": If true, the calendar will be populated with matches from + this layer + "hasPlaces": If true, the map will be populated with matches from this + layer + "isSubtitles": If true, this layer will be displayed as subtitles + "overlap": If true, overlapping annotations are allowed. Note that + enforcement of "overlap": false is not implemented + "showInfo": If true, user and creation time will be displayed in the + tooltip that appears on mouseover. + */ + "layers": [ + { + "id": "places", + "title": "Places", + "canAddAnnotations": {"member": true, "staff": true, "admin": true}, + "item": "Place", + "overlap": true, + "hasPlaces": true, + "type": "text" + }, + { + "id": "events", + "title": "Events", + "canAddAnnotations": {"member": true, "staff": true, "admin": true}, + "item": "Event", + "overlap": true, + "hasEvents": true, + "type": "text" + }, + { + "id": "keywords", + "title": "Keywords", + "canAddAnnotations": {"member": true, "staff": true, "admin": true}, + "item": "Keyword", + "autocomplete": true, + "overlap": true, + "type": "string" + }, + { + "id": "descriptions", + "title": "Descriptions", + "canAddAnnotations": {"member": true, "staff": true, "admin": true}, + "item": "Description", + "showInfo": true, + "type": "text" + }, + { + "id": "transcripts", + "title": "Transcripts", + "canAddAnnotations": {"member": true, "staff": true, "admin": true}, + "canPlayClips": true, + "isSubtitles": true, + "item": "Transcript", + "showInfo": true, + "type": "text" + } + ], + /* + "listViews" is an ordered list of available list views. Implemented views + are "list", "grid", "timelines", "clips", "clip", "map" and "calendar". + */ + "listViews": [ + {"id": "list", "title": "as List"}, + {"id": "grid", "title": "as Grid"}, + {"id": "timelines", "title": "with Timelines"}, + {"id": "clips", "title": "with Clips"}, + {"id": "clip", "title": "as Clips"}, + //{"id": "video", "title": "as Video"}, + {"id": "map", "title": "on Map"}, + {"id": "calendar", "title": "on Calendar"} + ], + /* + "media" defines additional media that gets added when importing items. + "importPosters": If true, a poster file will be imported (if present) + "importFrames": If true, 3 full-resolution frames per file will be + imported. This is useful to render high-resolution icons and + posters for an instance that has only low-resolution video. + */ + "media": { + "importPosters": false, + "importFrames": false + }, + /* + "menuExtras" can be used to add extra functionality to the main menu bar. + The plug-in architecture is not yet finalized, documentation forthcoming. + */ + "menuExtras": [ + "upload", + "user", + //"locale", + "reload" + ], + /* + "personalLists" specifies which pre-defined lists a new member will have + after signup. "title" is required. If "query" is present, this defines a + smart list. "query" can be any pan.do/ra query object, see /api/find for + further documentation. If you need to reference the username (for example + in order to include a default "My Videos" list), you can use the syntax + {"user": "{username}"} + */ + "personalLists": [ + {"title": "Favorites"} + ], + /* + "posters" contains details about the poster icons. "ratio" is the default + ratio (used to display a placeholder while poster icons are loading). + */ + "posters": { + "ratio": 0.625 + }, + /* + "rightsLevel" defines which initial rights level will be assigned to items + and texts created by users of these user levels. + */ + "rightsLevel": {"member": 2, "staff": 2, "admin": 2}, + /* + "rightsLevels" is an ordered list of rights levels, one of which will be + assigned to each item. + */ + "rightsLevels": [ + {"name": "Public", "color": [128, 255, 128]}, + {"name": "Restricted", "color": [255, 192, 128]}, + {"name": "Private", "color": [255, 128, 128]} + ], + /* + "site" contains various settings for this instance. In "email", "contact" + if the address in the contact form (to), "system" is the address used by + the system (from). + */ + "site": { + "description": "", + "email": { + // E-mail address in contact form (to) + "contact": "fcra@with.camp", + "footer": "-- \nPad.ma - https://fcra.with.camp", + "prefix": "Pad.ma News -", + // E-mail address uses by the system (from) + "system": "system@fcra.with.camp" + }, + "https": true, + "id": "fcra", + "name": "FCRA with CAMP", + "public": true, + "sendReferrer": true, + "url": "pad.ma" + }, + /* + "sitePages" defines the sections of the main site dialog. If "news" is + present, this will add an interface to add news items. If "contact" is + present, this will add an interface to contact the site owners. + */ + "sitePages": [ + {"id": "about", "title": "About"}, + //{"id": "news", "title": "News"}, + //{"id": "tour", "title": "Take a Tour"}, + //{"id": "faq", "title": "Frequently Asked Questions"}, + //{"id": "terms", "title": "Terms of Service"}, + {"id": "license", "title": "License"}, + {"id": "contact", "title": "Contact"} + ], + /* + "sites" specifies which other pan.do/ra instances, if any, will appear in + the user interface for creating embeds. This allows for easier creation of + cross-instance references. + */ + "sites": [ + {"name": "Pad.ma", "url": "pad.ma", "https": true}, + {"name": "Indiancine.ma", "url": "indiancine.ma", "https": true}, + {"name": "OxDB", "url": "0xdb.org", "https": true} + ], + /* + "textRightsLevels" defines a list of rights levels for texts. + */ + "textRightsLevels": [ + {"name": "Public", "color": [128, 255, 128]}, + {"name": "Private", "color": [255, 128, 128]} + ], + /* + "themes" is a list of themes that the user interface can be switched to. + Currently available themes are "oxlight", "oxmedium" and "oxdark". The + default theme can be set in user.ui.theme. + */ + "themes": ["oxlight", "oxmedium", "oxdark"], + /* + "timelines" is a list of timeline types. Implemented types are "antialias", + "slitscan", "keyframes" and "audio". + */ + "timelines": [ + {"id": "antialias", "title": "Anti-Alias"}, + {"id": "slitscan", "title": "Slit-Scan"}, + {"id": "keyframes", "title": "Keyframes"}, + {"id": "audio", "title": "Waveform"} + ], + /* + "totals" specifies which totals are displayed in the status bar at the + bottom of list views. Possible ids are "duration", "files", "items", + "pixels" and "size". Adding a capability limits the display of a specific + total to users of the corresponding user levels. + */ + "totals": [ + {"id": "items"}, + {"id": "files", "capability": "canSeeMedia"}, + {"id": "duration", "capability": "canSeeMedia"}, + {"id": "size", "capability": "canSeeMedia"}, + {"id": "pixels"} + ], + /* + If "tv" is set to true, then in TV mode, the site logo will be displayed in + the corner of the screen. + */ + "tv": { + "showLogo": false + }, + /* + The "user" object contains the default user settings. "ui" is the default + interface state for new users, and after selecting "Reset UI Settings" in + Preferences -> Advanced. This is the place to configure various defaults, + like the site-wide language and theme, the default list and item views, the + default set of filters, etc. + Please make sure that in case you rename or remove entries that are + referenced in "ui", you update them here as well. + */ + "user": { + "level": "guest", + "ui": { + "annotationsCalendarSize": 128, + "annotationsHighlight": "none", + "annotationsHighlight": false, + "annotationsMapSize": 128, + "annotationsRange": "selection", + "annotationsSize": 256, + "annotationsSort": "position", + "calendarFind": "", + "calendarSelection": "", + "clipColumns": 2, + "clipSize": 416, + "collectionColumns": ["title", "id", "extension", "dimensions", "size", "description", "matches", "user", "created", "modified"], + "collectionColumnWidth": {}, + "collectionSelection": [], + "collectionSort": [ + {"key": "title", "operator": "+"}, + {"key": "extension", "operator": "+"} + ], + "collectionView": "grid", + "collections": {}, + "columns": { + "Colors": { + "columns": ["title", "director", "language", "hue", "saturation", "brightness"], + "columnWidth": {} + } + }, + "document": "", + "documents": {}, + "documentFiltersSize": 176, + "documentSize": 256, + "documentView": "view", + "documentsSelection": {}, + "documentsSort": [{"key": "title", "operator": "+"}], + "documentsView": "grid", + "documentFilters": [ + {"id": "author", "sort": [{"key": "items", "operator": "-"}]}, + {"id": "place", "sort": [{"key": "items", "operator": "-"}]}, + {"id": "date", "sort": [{"key": "name", "operator": "-"}]}, + {"id": "publisher", "sort": [{"key": "items", "operator": "-"}]}, + {"id": "language", "sort": [{"key": "items", "operator": "-"}]} + ], + "edit": "", + "edits": {}, + "editSelection": [], + "editSort": [ + {"key": "index", "operator": "+"}, + {"key": "director", "operator": "+"}, + {"key": "title", "operator": "+"}, + {"key": "position", "operator": "+"}, + {"key": "duration", "operator": "+"} + ], + "editView": "list", + "embedSize": 256, + "entitiesSelection": {}, + "entitiesType": "", + "filters": [ + {"id": "source", "sort": [{"key": "name", "operator": "+"}]}, + {"id": "project", "sort": [{"key": "name", "operator": "+"}]}, + {"id": "topic", "sort": [{"key": "items", "operator": "-"}]}, + {"id": "name", "sort": [{"key": "items", "operator": "-"}]}, + {"id": "keywords", "sort": [{"key": "items", "operator": "-"}]}, + {"id": "places", "sort": [{"key": "items", "operator": "-"}]} + ], + "filtersSize": 176, + "find": {"conditions": [], "operator": "&"}, + "findDocuments": {"conditions": [], "operator": "&"}, + "followPlayer": true, + "help": "", + "icons": "frames", + "infoIconSize": 256, + "item": "", + "itemFind": "", + "itemSort": [{"key": "position", "operator": "+"}], + "itemView": "info", + "listColumns": ["title", "source", "project", "topic", "language", "duration"], + "listColumnWidth": {}, + "listSelection": [], + "listSort": [{"key": "title", "operator": "+"}], + "listView": "grid", + "lists": {}, + "locale": "en", + "mapFind": "", + "mapSelection": "", + "page": "", + "part": { + "api": "", + "document": "", + "entities": "", + "faq": "", + "help": "", + "news": "", + "preferences": "", + "tv": "" + }, + "section": "items", + "sequenceMode": "shape", + "sequenceSort": [{"key": "director", "operator": "+"}], + "showAdvancedEmbedOptions": false, + "showAnnotations": true, + "showAnnotationsCalendar": true, + "showAnnotationsMap": true, + "showBrowser": true, + "showCalendarControls": true, // fixme: should be false + "showClips": true, + "showDocument": true, + "showDocumentFilters": false, + "showFilters": true, + "showIconBrowser": false, + "showInfo": true, + "showLayers": { + "places": false, + "events": false, + "keywords": true, + "descriptions": true, + "transcripts": true + }, + "showMapControls": false, + "showMapLabels": false, + "showFolder": { + "edits": { + "personal": true, + "favorite": true, + "featured": true, + "volumes": true + }, + "items": { + "personal": true, + "favorite": true, + "featured": true, + "volumes": true + }, + "documents": { + "personal": true, + "favorite": true, + "featured": true + } + }, + "showReflections": true, + "showSidebar": true, + "showSitePosters": false, + "showTimeline": true, + "sidebarSize": 256, + "text": "", + "texts": {}, + "theme": "oxlight", + "updateAdvancedFindResults": false, + "videoLoop": false, + "videoMuted": false, + "videoPoints": {}, + "videoResolution": 240, + "videoScale": "fit", + "videoSize": "large", + "videoSubtitles": false, + "videoSubtitlesOffset": 0, + "videoTimeline": "keyframes", + "videoView": "player", + "videoVolume": 1 + }, + "script": "", + "username": "", + "volumes": [] + }, + /* + "userLevels" is an ordered list of user classes. The first entry is for + unregistered visitors. + */ + "userLevels": ["guest", "member", "staff", "admin"], + /* + "video" contains the video settings. + "formats": Supported video formats. Should be ["webm", "mp4"], or + ["webm"] in case iDevices are not part of the audience + "previewRatio": Aspect ratio used in the info panel in the bottom left + corner of the screen + "resolutions": List of video resolutions. Supported values are 96, 144, + 240, 288, 360, 432, 480, 720 and 1080. + */ + "video": { + "formats": ["mp4"], + "previewRatio": 1.3333333333, + "resolutions": [360, 720] + } +} diff --git a/install.py b/install.py index 1ad6f4d..432d012 100755 --- a/install.py +++ b/install.py @@ -8,8 +8,8 @@ import ox overwrite = ( - #('home', 'indiancinema'), - #('infoView', 'indiancinema'), + ('home', 'padma'), + ('infoView', 'padma'), ) base = abspath(dirname(__file__)) diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..7efc56f --- /dev/null +++ b/utils.py @@ -0,0 +1,70 @@ +import subprocess +import ox + +from archive.models import File, Stream +from archive.external import get_info + +def load_vimeo(item): + urls = [url for url in item.data.get("links", []) if "vimeo.com" in url] + cdir = os.path.abspath(os.curdir) + orig = None + for url in urls: + for resolution in (720, 360): + tmp = tempfile.mkdtemp() + if isinstance(tmp, bytes): + tmp = tmp.decode('utf-8') + os.chdir(tmp) + cmd = ['yt-dlp', '-q', url] + cmd += ['-o', '%(title)80s.%(ext)s'] + cmd += [ + '-f', 'bestvideo[height<=%s][ext=mp4]+bestaudio[ext=m4a]/bestvideo+bestaudio' % resolution, + '--merge-output-format', 'mp4' + ] + p = subprocess.Popen(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, close_fds=True) + stdout, stderr = p.communicate() + parts = list(os.listdir(tmp)) + if parts: + part = 1 + for name in parts: + name = os.path.join(tmp, name) + oshash = ox.oshash(name) + if orig is None: + f, created = File.objects.get_or_create(oshash=oshash) + if created: + orig = f + f.item = item + f.info = ox.avinfo(f.data.path) + f.info['extension'] = media['extension'] + f.info['url'] = url + f.path = '%(title)s.%(extension)s' % media + f.parse_info() + f.selected = True + f.queued = True + if len(parts) > 1: + f.part = part + part += 1 + f.save() + f.item.save() + f.extract_stream() + status = True + else: + return 'file exists' + stream, created = Stream.objects.get_or_create(file=orig, resolution=resolution, format="mp4") + stream.media.name = stream.path(stream.name()) + ox.makedirs(os.path.dirname(stream.media.path)) + shutil.move(name, stream.media.path) + stream.available = True + if resolution == 720: + source = stream + else: + stream.source = source + stream.save() + stream.make_timeline() + if resolution == 720 and len(parts) == 1: + info = get_info(url, None) + media = info[0] + add_subtitles(item, media, tmp) + o.chdir(cdir) + shutil.rmtree(tmp)