diff --git a/.bzrignore b/.bzrignore index b87a85b5..fc966d88 100644 --- a/.bzrignore +++ b/.bzrignore @@ -7,3 +7,4 @@ bin static/json/pandora.json static/js/pandora.min.js static/png/*.png +config.jsonc diff --git a/README b/README index ce631a3e..0d6c37c4 100644 --- a/README +++ b/README @@ -38,7 +38,7 @@ Get code from bazzar create local_settings.py and create site.jsonc in /srv/pandora/pandora do noy copy settings.py but only add your changes to local_settings.py (check https://wiki.0x2620.org/wiki/pandora/configuration - and use settings.py / 0xdb.jsonc / padma.jsonc as example) + and use settings.py / config.jsonc / config.*.jsonc as example) create and configure database as described below after that diff --git a/pandora/app/config.py b/pandora/app/config.py index 206887a2..c8b7ca3c 100644 --- a/pandora/app/config.py +++ b/pandora/app/config.py @@ -90,7 +90,7 @@ def update_static(): with open(f) as j: data += j.read() + '\n' js += [ - 'png/icon64.png', + 'png/icon.png', ] print 'write', pandora_js with open(pandora_js, 'w') as f: @@ -109,24 +109,29 @@ def update_static(): if os.path.splitext(f)[-1] in ('.js', '.json'): f = os.path.join(root, f) os.system('gzip -9 -c "%s" > "%s.gz"' % (f, f)) - - for size in (16, 64, 256): - pandora = os.path.join(settings.STATIC_ROOT, 'png/pandora/icon%d.png'%size) - image = os.path.join(settings.STATIC_ROOT, 'png/icon%d.png'%size) + + for name in ('logo', 'icon'): + site = os.path.join(settings.STATIC_ROOT, 'png/%s.%s.png'%(name, settings.CONFIG['site']['id'])) + pandora = os.path.join(settings.STATIC_ROOT, 'png/%s.pandora.png'%name) + image = os.path.join(settings.STATIC_ROOT, 'png/%s.png'%name) if not os.path.exists(image): - shutil.copyfile(pandora, image) + if os.path.exists(site): + shutil.copyfile(site, image) + else: + shutil.copyfile(pandora, image) - for size in (256, ): - pandora = os.path.join(settings.STATIC_ROOT, 'png/pandora/logo%d.png'%size) - image = os.path.join(settings.STATIC_ROOT, 'png/logo%d.png'%size) - if not os.path.exists(image): - shutil.copyfile(pandora, image) #download geo data update_geoip() - #poster script - if not os.path.exists(settings.ITEM_POSTER): - os.symlink(settings.ITEM_POSTER.replace('poster', 'oxdb_poster'), - settings.ITEM_POSTER) + + #scripts + for script in (settings.ITEM_POSTER, settings.ITEM_ICON, settings.LIST_ICON): + if not os.path.exists(script): + site_script = script.replace('.py', '.%s.py' % settings.CONFIG['site']['id']) + default_script = script.replace('.py', '.pandora.py') + if os.path.exists(site_script): + os.symlink(site_script, script) + else: + os.symlink(default_script, script) def update_geoip(force=False): path = os.path.join(settings.GEOIP_PATH, 'GeoLiteCity.dat') @@ -138,6 +143,6 @@ def update_geoip(force=False): os.unlink(path) os.system('gunzip "%s.gz"' % path) -def init(): +def init(): load_config() thread.start_new_thread(reloader_thread, ()) diff --git a/pandora/0xdb.jsonc b/pandora/config.0xdb.jsonc similarity index 99% rename from pandora/0xdb.jsonc rename to pandora/config.0xdb.jsonc index 9f8c68e3..417464ec 100644 --- a/pandora/0xdb.jsonc +++ b/pandora/config.0xdb.jsonc @@ -645,7 +645,7 @@ // E-mail address uses by the system (from) "system": "0xDB@0xDB.org" }, - "id": "oxdb", + "id": "0xdb", "name": "0xDB", "url": "0xDB.org", "videoprefix": "" diff --git a/pandora/padma.jsonc b/pandora/config.padma.jsonc similarity index 100% rename from pandora/padma.jsonc rename to pandora/config.padma.jsonc diff --git a/pandora/config.pandora.jsonc b/pandora/config.pandora.jsonc new file mode 100644 index 00000000..03ef9128 --- /dev/null +++ b/pandora/config.pandora.jsonc @@ -0,0 +1,692 @@ +/* + Pan.do/ra Settings + + You can edit this file. +*/ +{ + "additionalSort": [ + {"key": "title", "operator": "+"} + ], + "annotations": { + "showUsers": true + }, + /* + Capabilities are per user level. + They can either be general: + {level: true} means a user of that level has the capability) + or related to items: + {level: x} means a user of that level has the capability + for items of a rights level up to and including x + */ + "capabilities": { + "canDeleteItems": {"admin": true}, + "canDownloadVideo": {"guest": 1, "member": 1, "staff": 4, "admin": 4}, + "canEditAnnotations": {"staff": true, "admin": true}, + "canEditEvents": {"staff": true, "admin": true}, + "canEditFeaturedLists": {"staff": true, "admin": true}, + "canEditMetadata": {"staff": true, "admin": true}, + "canEditPlaces": {"staff": true, "admin": true}, + "canEditSitePages": {"staff": true, "admin": true}, + "canEditUsers": {"admin": true}, + "canImportAnnotations": {"member": true, "staff": true, "admin": true}, + "canManageTitlesAndNames": {"member": true, "staff": true, "admin": true}, + "canManagePlacesAndEvents": {"member": true, "staff": true, "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}, + "canSeeAccessed": {"staff": true, "admin": true}, + "canSeeDebugMenu": {"staff": true, "admin": true}, + "canSeeExtraItemViews": {"staff": true, "admin": true}, + "canSeeFiles": {"staff": true, "admin": true}, + "canSeeItem": {"guest": 1, "member": 1, "staff": 4, "admin": 4}, + "canSeeSize": {"staff": true, "admin": true}, + "canSendMail": {"staff": true, "admin": true}, + "canUploadVideo": {"guest": false, "member": true, "staff": true, "admin": true} + }, + /* + clipKeys are the properties that clips can by sorted by. + If sortOperator is not specified, it will be + for strings and - 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. Excluding a layer from this list means it will not be + included in find annotations. // FIXME: the last bit is not implemented. + */ + "clipLayers": ["transcripts", "keywords", "places", "events", "descriptions"], + // fixme: either this, or filter: true in itemKeys, but not both + "filters": [ + {"id": "source", "title": "Sources", "type": "string"}, + {"id": "project", "title": "Projects", "type": "string"}, + {"id": "topic", "title": "Topics", "type": "string"}, + {"id": "name", "title": "People", "type": "string"}, + {"id": "language", "title": "Languages", "type": "string"}, + {"id": "license", "title": "License", "type": "string"}, + {"id": "places", "title": "Places", "type": "string"}, + {"id": "events", "title": "Events", "type": "string"}, + {"id": "keywords", "title": "Keywords", "type": "string"} + ], + /* + An itemKey must have the following properties: + id: The id of the key (as known by the server) + title: The title of the key (as displayed by the client) + type: text, string, float, integer, or array of any of these + and can have any of the following properties: + autocomplete: If true, find element will autocomplete + autocompleteSortKey: The key that suggestions will be sorted by + capability: A capability required to see this key + columnRequired: If true, the column can't be removed + columnWidth: Default column width in px + filter: if true, one can filter results by this key + find: If true, this key will appear as a find option + format: {type: "...", args: [...]}, for special formatting + (Ox.formatType(args) will be called) + sort: If true, one can sort results by this key + sortOperator: sort operator (+, -), in case it differs from the + default for the key's type (+ for strings, - for numbers) + sortType: special sort type (title, person) + value: {key: "...", type: "..."}, for keys that are derived + from other keys (like number of actors), or "capability" + */ + "itemKeys": [ + { + "id": "*", + "title": "All", + "type": "text", + "find": true + }, + { + "id": "title", + "title": "Title", + "type": "string", + "autocomplete": true, + "autocompleteSortKey": "timesaccessed", + "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": "name", + "title": "People", + "type": ["string"], + "autocomplete": true, + "find": true + }, + { + "id": "director", + "title": "Director", + "type": ["string"], + "autocomplete": true, + "columnRequired": 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, + "columnRequired": 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": "id", + "title": "ID", + "type": "string", + "columnWidth": 90, + "sort": true + }, + { + "id": "annotations", + "title": "Annotations", + "type": "string", + "find": true + }, + { + "id": "places", + "title": "Places", + "type": "layer", + "find": true + }, + { + "id": "events", + "title": "Events", + "type": "layer", + "find": true + }, + { + "id": "keywords", + "title": "Keywords", + "type": "layer", + "find": true + }, + { + "id": "descriptions", + "title": "Descriptions", + "type": "layer", + "find": true + }, + { + "id": "transcripts", + "title": "Transcripts", + "type": "layer", + "find": true + }, + { + "id": "numberofannotations", + "title": "Annotations", + "type": "integer", + "columnWidth": 60, + "sort": true + }, + { + "id": "duration", + "title": "Duration", + "type": "float", + "columnWidth": 90, + "format": {"type": "duration", "args": [0, "short"]}, + "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"]}, + "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, + "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": "subtitles", "type": "words"} + }, + { + "id": "wordsperminute", + "title": "Words per Minute", + "type": "float", + "columnWidth": 60, + "format": {"type": "number", "args": [3]}, + "sort": true, + "value": {"layer": "subtitles", "type": "wordsperminute"} + }, + { + "id": "size", + "title": "Size", + "type": "integer", + "capability": "canSeeFiles", + "columnWidth": 60, + "format": {"type": "value", "args": ["B"]}, + "sort": true + }, + { + "id": "bitrate", + "title": "Bitrate", + "type": "integer", + "columnWidth": 60, + "format": {"type": "unit", "args": ["kbps"]}, + "sort": true + }, + { + "id": "numberoffiles", + "title": "Number of Files", + "type": "integer", + "capability": "canSeeFiles", + "columnWidth": 60, + "sort": true, + "value": {"key": "files", "type": "length"} + }, + { + "id": "user", + "title": "User", + "type": "string", + "capability": "canSeeFiles", + "find": true + }, + { + "id": "filename", + "title": "Filename", + "type": ["string"], + "capability": "canSeeFiles", + "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": "date", "args": ["%Y-%m-%d %H:%M:%S"]}, + "sort": true + }, + { + "id": "license", + "title": "License", + "type": ["string"], + "columnWidth": 120, + "filter": true, + "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"] + }, + { + "id": "random", + "title": "Random", + "type": "integer", + "sort": true + } + ], + "itemName": { + "singular": "Video", + "plural": "Videos" + }, + "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": "data", "title": "Data"}, + {"id": "files", "title": "Files"} + ], + "layers": [ + { + "id": "places", + "title": "Places", + "canAddAnnotations": {"member": true, "staff": true, "admin": true}, + "item": "Place", + "overlap": true, + "type": "place" + }, + { + "id": "events", + "title": "Events", + "canAddAnnotations": {"member": true, "staff": true, "admin": true}, + "item": "Event", + "overlap": true, + "type": "event" + }, + { + "id": "keywords", + "title": "Keywords", + "canAddAnnotations": {"member": true, "staff": true, "admin": true}, + "item": "Keyword", + "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}, + "item": "Transcript", + "showInfo": true, + "type": "text" + } + ], + "listViews": [ + {"id": "list", "title": "as List"}, + {"id": "grid", "title": "as Grid"}, + //{"id": "info", "title": "with Info"}, + {"id": "timelines", "title": "with Timelines"}, + {"id": "clips", "title": "with Clips"}, + //{"id": "maps", "title": "with Maps"}, + //{"id": "calendars", "title": "with Calendars"}, + {"id": "clip", "title": "as Clips"}, + //{"id": "video", "title": "as Video"}, + {"id": "map", "title": "on Map"}, + {"id": "calendar", "title": "on Calendar"} + ], + "media": { + "importPosters": false, + "importFrames": false + }, + "personalLists": [ + {"title": "Favorites"} + ], + "rightsLevel": {"member": 2, "staff": 2, "admin": 2}, + "rightsLevels": [ + {"name": "Public", "color": [128, 255, 128]}, + {"name": "Restricted", "color": [255, 192, 128]}, + {"name": "Private", "color": [255, 128, 128]} + ], + "sendReferrer": true, + "site": { + "description": "pan.do/ra is a free, open source media archive. It allows you to manage large, decentralized collections of video, to collaboratively create metadata and time-based annotations, and to serve your archive as a cutting-edge web application.", + "email": { + // E-mail address in contact form (to) + "contact": "system@pandora.local", + "footer": "-- \npan.do/ra - http://pan.do/ra", + "prefix": "pan.do/ra News -", + // E-mail address uses by the system (from) + "system": "system@pandora.local" + }, + "id": "pandora", + "name": "pan.do/ra", + "url": "pandora.local", + "videoprefix": "" + }, + "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"} + ], + "timelines": [ + {"id": "antialias", "title": "Anti-Alias"}, + {"id": "slitscan", "title": "Slit-Scan"}, + {"id": "keyframes", "title": "Key Frames"}, + {"id": "audio", "title": "Audio"} + ], + "totals": [ + {"id": "items"}, + {"id": "files", "admin": true}, + {"id": "duration", "admin": true}, + {"id": "size", "admin": true}, + {"id": "pixels"} + ], + "tv": { + "showLogo": false + }, + "user": { + "level": "guest", + "ui": { + "annotationsCalendarSize": 128, + "annotationsFont": "small", + "annotationsMapSize": 128, + "annotationsRange": "position", + "annotationsSize": 256, + "annotationsSort": "position", + "clipsColumns": 2, + "columns": { + "Colors": { + "columns": ["title", "source", "project", "language", "hue", "saturation", "brightness"], + "columnWidth": {} + } + }, + "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": "&"}, + "followPlayer": true, + "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": {}, + "mapFind": "", + "mapSelection": "", + "page": "", + "section": "items", + "showAnnotations": true, + "showAnnotationsCalendar": true, + "showAnnotationsMap": true, + "showBrowser": true, + "showCalendarControls": true, // fixme: should be false + "showFilters": true, + "showFlags": false, + "showHome": true, + "showIconBrowser": false, + "showInfo": true, + "showLayers": { + "places": false, + "events": false, + "keywords": true, + "descriptions": true, + "transcripts": true + }, + "showMapControls": false, + "showMapLabels": false, + "showFolder": { + "items": { + "personal": true, + "favorite": true, + "featured": true, + "volumes": true + } + }, + "showSidebar": true, + "showSitePosters": false, + "showTimeline": true, + "sidebarSize": 256, + "theme": "classic", + "videoMuted": false, + "videoPoints": {}, + "videoResolution": 240, + "videoScale": "fit", + "videoSize": "large", + "videoTimeline": "antialias", + "videoView": "player", + "videoVolume": 1 + }, + "username": "", + "volumes": [] + }, + "userLevels": ["guest", "member", "staff", "admin"], + "video": { + "download": true, + "formats": ["webm"], + "previewRatio": 1.3333333333, + //supported resolutions are + //1080, 720, 480, 432, 360, 288, 240, 144, 96 + "resolutions": [480, 240, 96] + } +} diff --git a/pandora/manage.py b/pandora/manage.py index b77df77c..0d7eb6f7 100755 --- a/pandora/manage.py +++ b/pandora/manage.py @@ -18,4 +18,8 @@ except ImportError: sys.exit(1) if __name__ == "__main__": + if not os.path.exists(settings.SITE_CONFIG): + import sys + sys.stderr.write("Error: Can't find '%s'.\nBefore you run pan.do/ra you must create it\n" % settings.SITE_CONFIG) + sys.exit(1) execute_manager(settings) diff --git a/pandora/settings.py b/pandora/settings.py index 7b9f7a26..2834b8eb 100644 --- a/pandora/settings.py +++ b/pandora/settings.py @@ -162,9 +162,10 @@ XSENDFILE = False #with nginx X-Accel-Redirect set this to True XACCELREDIRECT = False -SITE_CONFIG = join(PROJECT_ROOT, 'pandora.jsonc') +SITE_CONFIG = join(PROJECT_ROOT, 'config.jsonc') + #used if CONFIG['video']['download'] is set -TRACKER_URL="http://url2torrent.net:6970/announce" +TRACKER_URL="udp://tracker.openbittorrent.com:80" DATA_SERVICE = '' POSTER_PRECEDENCE = () @@ -178,10 +179,10 @@ VIDEO_PREFIX='' SCRIPT_ROOT = normpath(join(PROJECT_ROOT, '..', 'scripts')) #change script to customize -ITEM_POSTER = join(SCRIPT_ROOT, 'poster') +ITEM_POSTER = join(SCRIPT_ROOT, 'poster.py') #ITEM_POSTER = join(SCRIPT_ROOT, 'padma_poster') -ITEM_ICON = join(SCRIPT_ROOT, 'item_icon') -LIST_ICON = join(SCRIPT_ROOT, 'list_icon') +ITEM_ICON = join(SCRIPT_ROOT, 'item_icon.py') +LIST_ICON = join(SCRIPT_ROOT, 'list_icon.py') DB_GIN_TRGM = False diff --git a/pandora/templates/api.html b/pandora/templates/api.html index f5d28553..b511859e 100644 --- a/pandora/templates/api.html +++ b/pandora/templates/api.html @@ -4,7 +4,7 @@ {{sitename}} API - + diff --git a/pandora/templates/index.html b/pandora/templates/index.html index c5572886..ab654c79 100644 --- a/pandora/templates/index.html +++ b/pandora/templates/index.html @@ -3,9 +3,9 @@ {{title}} - - - + + + @@ -21,7 +21,7 @@ - + diff --git a/pandora/templates/item.html b/pandora/templates/item.html index aaf318a9..de802cca 100644 --- a/pandora/templates/item.html +++ b/pandora/templates/item.html @@ -3,9 +3,9 @@ {{settings.SITENAME}} - {{title}} - - - + + + diff --git a/scripts/item_icon b/scripts/item_icon.pandora.py similarity index 100% rename from scripts/item_icon rename to scripts/item_icon.pandora.py diff --git a/scripts/list_icon b/scripts/list_icon.pandora.py similarity index 100% rename from scripts/list_icon rename to scripts/list_icon.pandora.py diff --git a/scripts/oxdb_poster b/scripts/poster.0xdb.py similarity index 98% rename from scripts/oxdb_poster rename to scripts/poster.0xdb.py index 1993c494..44751a9f 100755 --- a/scripts/oxdb_poster +++ b/scripts/poster.0xdb.py @@ -129,7 +129,7 @@ def render_poster(title, director, year, series, oxdb_id, imdb_id, frame, timeli def main(): parser = OptionParser() parser.add_option('-o', '--oxdbid', dest='oxdb_id', help='0xDB Id') - parser.add_option('-i', '--imdbid', dest='imdb_id', help='IMDb Id') + parser.add_option('-i', '--id', dest='imdb_id', help='Item Id') parser.add_option('-t', '--title', dest='title', help='Title') parser.add_option('-d', '--director', dest='director', help='Director(s)', default='') parser.add_option('-y', '--year', dest='year', help='Year') diff --git a/scripts/padma_poster b/scripts/poster.padma.py similarity index 98% rename from scripts/padma_poster rename to scripts/poster.padma.py index c8a9138d..2e2086d4 100755 --- a/scripts/padma_poster +++ b/scripts/poster.padma.py @@ -60,7 +60,7 @@ def render_poster(id, title, frame, timeline, poster): def main(): parser = OptionParser() parser.add_option('-o', '--oxdbid', dest='oxdb_id', help='0xDB Id') - parser.add_option('-i', '--id', dest='id', help='Pad.ma Id') + parser.add_option('-i', '--id', dest='id', help='Item Id') parser.add_option('-t', '--title', dest='title', help='Title', default='') parser.add_option('-d', '--director', dest='director', help='Director', default='') parser.add_option('-y', '--year', dest='year', help='Year', default='') diff --git a/scripts/poster.pandora.py b/scripts/poster.pandora.py new file mode 100755 index 00000000..ecc9bc85 --- /dev/null +++ b/scripts/poster.pandora.py @@ -0,0 +1,86 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# vi:si:et:sw=4:sts=4:ts=4 +from __future__ import division +import os + +root_dir = os.path.normpath(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) + +#using virtualenv's activate_this.py to reorder sys.path +activate_this = os.path.join(root_dir, 'bin', 'activate_this.py') +execfile(activate_this, dict(__file__=activate_this)) + +import Image +import ImageDraw +from optparse import OptionParser +from ox.image import drawText, wrapText +import sys + +static_root = os.path.join(os.path.dirname(__file__), 'data') + +def render_poster(id, title, frame, timeline, poster): + poster_width = 640 + poster_height = 1024 + poster_ratio = poster_width / poster_height + poster_color = (255, 255, 0) + poster_image = Image.new('RGB', (poster_width, poster_height)) + font_file = os.path.join(static_root, 'DejaVuSansCondensedBold.ttf') + font_size = 48 + + # timeline + timeline_height = 64 + timeline_lines = 16 + if timeline: + timeline_image = Image.open(timeline) + timeline_image = timeline_image.resize((10240, timeline_height), Image.ANTIALIAS) + for i in range(timeline_lines): + line_image = timeline_image.crop((i * poster_width, 0, (i + 1) * poster_width, 64)) + poster_image.paste(line_image, (0, i * timeline_height)) + + # id + text = '' + id + text_image = Image.new('RGB', (1, 1)) + text_size = drawText(text_image, (0, 0), text, font_file, font_size, poster_color) + text_width = poster_width + text_height = timeline_height + text_left = int((poster_width - text_width) / 2) + text_top = 14 * timeline_height + for y in range(text_top, text_top + text_height): + for x in range(text_left, text_left + text_width): + if y < text_top + 4 or y >= text_top + text_height - 4: + poster_image.putpixel((x, y), poster_color) + else: + pixel = list(poster_image.getpixel((x, y))) + for c in range(3): + pixel[c] = int((pixel[c] + poster_color[c]) / 4) + poster_image.putpixel((x, y), tuple(pixel)) + drawText(poster_image, ((poster_width - text_size[0]) / 2, text_top + (text_height - text_size[1]) / 2), text, font_file, font_size, poster_color) + poster_image.save(poster) + +def main(): + parser = OptionParser() + parser.add_option('-o', '--oxdbid', dest='oxdb_id', help='0xDB Id') + parser.add_option('-i', '--id', dest='id', help='Item Id') + parser.add_option('-t', '--title', dest='title', help='Title', default='') + parser.add_option('-d', '--director', dest='director', help='Director', default='') + parser.add_option('-y', '--year', dest='year', help='Year', default='') + parser.add_option('-f', '--frame', dest='frame', help='Poster frame (image file to be read)') + parser.add_option('-l', '--timeline', dest='timeline', help='Timeline (image file to be read)') + parser.add_option('-p', '--poster', dest='poster', help='Poster (image file to be written)') + (options, args) = parser.parse_args() + if options.oxdb_id and not options.id: + options.id = options.oxdb_id + if None in (options.id, options.poster): + parser.print_help() + sys.exit() + opt = {} + for key in ('id', 'title', 'frame', 'timeline', 'poster'): + opt[key] = getattr(options, key) + + opt['title'] = opt['title'].decode('utf-8') + + render_poster(**opt) + +if __name__ == "__main__": + main() + diff --git a/static/favicon.ico b/static/favicon.ico index 9fc257be..d430bcea 120000 --- a/static/favicon.ico +++ b/static/favicon.ico @@ -1 +1 @@ -png/icon16.png \ No newline at end of file +png/icon.png \ No newline at end of file diff --git a/static/html/50x.html b/static/html/50x.html index 76ea6c2a..5b1cad17 100644 --- a/static/html/50x.html +++ b/static/html/50x.html @@ -12,7 +12,7 @@ - +
We will be back in a moment.
diff --git a/static/js/pandora.js b/static/js/pandora.js index 00ba3f0c..0e1f5711 100644 --- a/static/js/pandora.js +++ b/static/js/pandora.js @@ -75,7 +75,7 @@ appPanel images.reflection.style.MozTransform = 'scaleY(-1)'; images.reflection.style.OTransform = 'scaleY(-1)'; images.reflection.style.WebkitTransform = 'scaleY(-1)'; - images.reflection.src = '/static/png/logo256.png'; + images.reflection.src = '/static/png/logo.png'; images.loadingIcon = document.createElement('img'); images.loadingIcon.setAttribute('id', 'loadingIcon'); images.loadingIcon.style.position = 'absolute'; @@ -90,7 +90,7 @@ appPanel + '/Ox.UI/themes/' + theme + '/svg/symbolLoadingAnimated.svg'; callback(images); }; - images.logo.src = '/static/png/logo256.png'; + images.logo.src = '/static/png/logo.png'; } function loadScreen(images) { diff --git a/static/js/pandora/account.js b/static/js/pandora/account.js index 62a0a9d9..892711d4 100644 --- a/static/js/pandora/account.js +++ b/static/js/pandora/account.js @@ -94,7 +94,7 @@ pandora.ui.accountDialogOptions = function(action, value) { content: Ox.Element() .append( $('') - .attr({src: '/static/png/icon64.png'}) + .attr({src: '/static/png/icon.png'}) .css({position: 'absolute', left: '16px', top: '16px', width: '64px', height: '64px'}) ) .append( @@ -335,7 +335,7 @@ pandora.ui.accountSignoutDialog = function() { content: Ox.Element() .append( $('') - .attr({src: '/static/png/icon64.png'}) + .attr({src: '/static/png/icon.png'}) .css({position: 'absolute', left: '16px', top: '16px', width: '64px', height: '64px'}) ) .append( @@ -374,7 +374,7 @@ pandora.ui.accountWelcomeDialog = function() { content: Ox.Element() .append( $('') - .attr({src: '/static/png/icon64.png'}) + .attr({src: '/static/png/icon.png'}) .css({position: 'absolute', left: '16px', top: '16px', width: '64px', height: '64px'}) ) .append( diff --git a/static/js/pandora/allItems.js b/static/js/pandora/allItems.js index 0a331c49..ae844d65 100644 --- a/static/js/pandora/allItems.js +++ b/static/js/pandora/allItems.js @@ -21,7 +21,7 @@ pandora.ui.allItems = function() { } }), $icon = $('') - .attr({src: '/static/png/icon16.png'}) + .attr({src: '/static/png/icon.png'}) .css({float: 'left', width: '14px', height: '14px', margin: '1px'}) .appendTo(that), $name = $('
') diff --git a/static/js/pandora/annotationDialog.js b/static/js/pandora/annotationDialog.js index 0d16b870..cf1efbf7 100644 --- a/static/js/pandora/annotationDialog.js +++ b/static/js/pandora/annotationDialog.js @@ -26,7 +26,7 @@ pandora.ui.annotationDialog = function(layer) { content: Ox.Element() .append( $('') - .attr({src: '/static/png/icon64.png'}) + .attr({src: '/static/png/icon.png'}) .css({position: 'absolute', left: '16px', top: '16px', width: '64px', height: '64px'}) ) .append( diff --git a/static/js/pandora/contactForm.js b/static/js/pandora/contactForm.js index f304f42d..595cdfde 100644 --- a/static/js/pandora/contactForm.js +++ b/static/js/pandora/contactForm.js @@ -120,7 +120,7 @@ pandora.ui.contactForm = function() { content: Ox.Element() .append( $('') - .attr({src: '/static/png/icon64.png'}) + .attr({src: '/static/png/icon.png'}) .css({position: 'absolute', left: '16px', top: '16px', width: '64px', height: '64px'}) ) .append( diff --git a/static/js/pandora/deleteItemDialog.js b/static/js/pandora/deleteItemDialog.js index e86a2ab0..8e29edcc 100644 --- a/static/js/pandora/deleteItemDialog.js +++ b/static/js/pandora/deleteItemDialog.js @@ -31,7 +31,7 @@ pandora.ui.deleteItemDialog = function(item) { content: Ox.Element() .append( $('') - .attr({src: '/static/png/icon64.png'}) + .attr({src: '/static/png/icon.png'}) .css({position: 'absolute', left: '16px', top: '16px', width: '64px', height: '64px'}) ) .append( diff --git a/static/js/pandora/deleteListDialog.js b/static/js/pandora/deleteListDialog.js index 28f9b815..12e37a72 100644 --- a/static/js/pandora/deleteListDialog.js +++ b/static/js/pandora/deleteListDialog.js @@ -45,7 +45,7 @@ pandora.ui.deleteListDialog = function(list) { content: Ox.Element() .append( $('') - .attr({src: '/static/png/icon64.png'}) + .attr({src: '/static/png/icon.png'}) .css({position: 'absolute', left: '16px', top: '16px', width: '64px', height: '64px'}) ) .append( diff --git a/static/js/pandora/error.js b/static/js/pandora/error.js index 93e6b1ed..88c97850 100644 --- a/static/js/pandora/error.js +++ b/static/js/pandora/error.js @@ -26,7 +26,7 @@ pandora.ui.errorDialog = function(data) { content: Ox.Element() .append( $('') - .attr({src: '/static/png/icon64.png'}) + .attr({src: '/static/png/icon.png'}) .css({position: 'absolute', left: '16px', top: '16px', width: '64px', height: '64px'}) ) .append( @@ -66,7 +66,7 @@ pandora.ui.errorDialog = function(data) { content: Ox.Element() .append( $('') - .attr({src: '/static/png/icon64.png'}) + .attr({src: '/static/png/icon.png'}) .css({position: 'absolute', left: '16px', top: '16px', width: '64px', height: '64px'}) ) .append( diff --git a/static/js/pandora/folderBrowserList.js b/static/js/pandora/folderBrowserList.js index 12e020ea..9e9c4af0 100644 --- a/static/js/pandora/folderBrowserList.js +++ b/static/js/pandora/folderBrowserList.js @@ -11,7 +11,7 @@ pandora.ui.folderBrowserList = function(id) { clickable: true, format: function(value) { return $('').attr({ - src: '/list/' + value + '/icon16.jpg' + src: '/list/' + value + '/icon.jpg' }).css({ width: '14px', height: '14px', diff --git a/static/js/pandora/folderList.js b/static/js/pandora/folderList.js index 4ae9deb5..cbc52707 100644 --- a/static/js/pandora/folderList.js +++ b/static/js/pandora/folderList.js @@ -14,7 +14,7 @@ pandora.ui.folderList = function(id) { }, format: function(value, data) { return $('').attr({ - src: '/list/' + data.id + '/icon16.jpg' + src: '/list/' + data.id + '/icon.jpg' }).css({ width: '14px', height: '14px', diff --git a/static/js/pandora/folders.js b/static/js/pandora/folders.js index 5a545736..33104d92 100644 --- a/static/js/pandora/folders.js +++ b/static/js/pandora/folders.js @@ -304,7 +304,7 @@ pandora.ui.folders = function() { content: Ox.Element() .append( $('') - .attr({src: '/static/png/icon64.png'}) + .attr({src: '/static/png/icon.png'}) .css({position: 'absolute', left: '16px', top: '16px', width: '64px', height: '64px'}) ) .append( diff --git a/static/js/pandora/home.js b/static/js/pandora/home.js index 6ecaf9f5..7f5506cb 100644 --- a/static/js/pandora/home.js +++ b/static/js/pandora/home.js @@ -14,7 +14,7 @@ pandora.ui.home = function() { zIndex: 1001 }), $reflectionImage = $('') - .attr({src: '/static/png/logo256.png'}) + .attr({src: '/static/png/logo.png'}) .css({ position: 'absolute', left: 0, @@ -45,7 +45,7 @@ pandora.ui.home = function() { $logo = $('') .attr({ id: 'logo', - src: '/static/png/logo256.png' + src: '/static/png/logo.png' }) .css({ position: 'absolute', diff --git a/static/js/pandora/home.padma.js b/static/js/pandora/home.padma.js index 19b02564..006113f0 100644 --- a/static/js/pandora/home.padma.js +++ b/static/js/pandora/home.padma.js @@ -26,7 +26,7 @@ pandora.ui.home = function() { }) .appendTo(that), $reflectionImage = $('') - .attr({src: '/static/png/logo256.png'}) + .attr({src: '/static/png/logo.png'}) .css({ position: 'absolute', left: 0, @@ -55,7 +55,7 @@ pandora.ui.home = function() { $logo = $('') .attr({ id: 'logo', - src: '/static/png/logo256.png' + src: '/static/png/logo.png' }) .css({ position: 'absolute', diff --git a/static/js/pandora/homePage.js b/static/js/pandora/homePage.js index 587fd234..b74146a7 100644 --- a/static/js/pandora/homePage.js +++ b/static/js/pandora/homePage.js @@ -32,7 +32,7 @@ pandora.ui.homePage = function() { .html('right') .appendTo(that.$element), $logo = $('') - .attr({src: '/static/png/logo256.png'}) + .attr({src: '/static/png/logo.png'}) .appendTo($center); // fixme: duplicated $select = Ox.Select({ diff --git a/static/js/pandora/listDialog.js b/static/js/pandora/listDialog.js index 30b6d082..46c4d50c 100644 --- a/static/js/pandora/listDialog.js +++ b/static/js/pandora/listDialog.js @@ -475,7 +475,7 @@ pandora.ui.listIconPanel = function(listData) { pandora.$ui.folderList[listData.folder].$element .find('img[src*="/' + listData.id + '/"]') .attr({ - src: '/list/' + listData.id + '/icon16.jpg?' + Ox.uid() + src: '/list/' + listData.id + '/icon.jpg?' + Ox.uid() }); pandora.$ui.info.updateListInfo(); }); diff --git a/static/js/pandora/makeListPrivateDialog.js b/static/js/pandora/makeListPrivateDialog.js index 73250190..d92bfba0 100644 --- a/static/js/pandora/makeListPrivateDialog.js +++ b/static/js/pandora/makeListPrivateDialog.js @@ -26,7 +26,7 @@ pandora.ui.makeListPrivateDialog = function(name, subscribers, callback) { content: Ox.Element() .append( $('') - .attr({src: '/static/png/icon64.png'}) + .attr({src: '/static/png/icon.png'}) .css({position: 'absolute', left: '16px', top: '16px', width: '64px', height: '64px'}) ) .append( diff --git a/static/js/pandora/preferencesDialog.js b/static/js/pandora/preferencesDialog.js index 4d542a59..4e434825 100644 --- a/static/js/pandora/preferencesDialog.js +++ b/static/js/pandora/preferencesDialog.js @@ -12,7 +12,7 @@ pandora.ui.preferencesDialog = function() { .css({overflowY: 'auto'}) .append( $('') - .attr({src: '/static/png/icon64.png'}) + .attr({src: '/static/png/icon.png'}) .css({position: 'absolute', left: '16px', top: '16px'}) ); if (id == 'account') { diff --git a/static/js/pandora/siteDialog.js b/static/js/pandora/siteDialog.js index caf9e1d2..cd196a89 100644 --- a/static/js/pandora/siteDialog.js +++ b/static/js/pandora/siteDialog.js @@ -97,8 +97,8 @@ pandora.ui.siteDialog = function(section) { .append( $('') .attr({src: '/static/png/' + ( - id == 'software' ? 'pandora/icon' : 'logo' - ) + '256.png'}) + id == 'software' ? 'software.png' : 'logo.png' + )}) .css({width: '256px'}) ), size: 272 diff --git a/static/js/pandora/tv.js b/static/js/pandora/tv.js index e2afc80b..6cf8a36d 100644 --- a/static/js/pandora/tv.js +++ b/static/js/pandora/tv.js @@ -59,7 +59,7 @@ pandora.ui.tv = function() { duration: result.data.duration, enableSubtitles: pandora.user.ui.videoSubtitles, fullscreen: true, - logo: pandora.site.tv.showLogo ? '/static/png/logo256.png' : '', + logo: pandora.site.tv.showLogo ? '/static/png/logo.png' : '', muted: muted || pandora.user.ui.videoMuted, position: result.data.position, resolution: pandora.user.ui.videoResolution, diff --git a/static/js/pandora/usersDialog.js b/static/js/pandora/usersDialog.js index 7b5d11b9..281d1524 100644 --- a/static/js/pandora/usersDialog.js +++ b/static/js/pandora/usersDialog.js @@ -888,7 +888,7 @@ pandora.ui.usersDialog = function() { content: Ox.Element() .append( $('') - .attr({src: '/static/png/icon64.png'}) + .attr({src: '/static/png/icon.png'}) .css({position: 'absolute', left: '16px', top: '16px', width: '64px', height: '64px'}) ) .append( diff --git a/static/png/icon.0xdb.org b/static/png/icon.0xdb.org new file mode 100644 index 00000000..cff5bd59 Binary files /dev/null and b/static/png/icon.0xdb.org differ diff --git a/static/png/icon.padma.png b/static/png/icon.padma.png new file mode 100644 index 00000000..e58bf75b Binary files /dev/null and b/static/png/icon.padma.png differ diff --git a/static/png/pandora/icon256.png b/static/png/icon.pandora.png similarity index 100% rename from static/png/pandora/icon256.png rename to static/png/icon.pandora.png diff --git a/static/png/logo.0xdb.org b/static/png/logo.0xdb.org new file mode 100644 index 00000000..ce3e7202 Binary files /dev/null and b/static/png/logo.0xdb.org differ diff --git a/static/png/logo.padma.png b/static/png/logo.padma.png new file mode 100644 index 00000000..95c4a61a Binary files /dev/null and b/static/png/logo.padma.png differ diff --git a/static/png/pandora/logo256.png b/static/png/logo.pandora.png similarity index 100% rename from static/png/pandora/logo256.png rename to static/png/logo.pandora.png diff --git a/static/png/pandora/icon16.png b/static/png/pandora/icon16.png deleted file mode 100644 index 23e2830a..00000000 Binary files a/static/png/pandora/icon16.png and /dev/null differ diff --git a/static/png/pandora/icon64.png b/static/png/pandora/icon64.png deleted file mode 100644 index acce9cbb..00000000 Binary files a/static/png/pandora/icon64.png and /dev/null differ diff --git a/static/png/rights.png b/static/png/rights.png new file mode 100644 index 00000000..4a7dfbdd Binary files /dev/null and b/static/png/rights.png differ diff --git a/static/png/software.png b/static/png/software.png new file mode 100644 index 00000000..11e82fc2 Binary files /dev/null and b/static/png/software.png differ diff --git a/vm/firstboot.sh b/vm/firstboot.sh index 9c4496b1..ecc82633 100755 --- a/vm/firstboot.sh +++ b/vm/firstboot.sh @@ -22,8 +22,11 @@ rabbitmqctl set_permissions -p /pandora pandora ".*" ".*" ".*" #pandora HOST=$(hostname -s) -SITE_CONFIG="/srv/pandora/pandora/$HOST.jsonc" -test -e $SITE_CONFIG || cp /srv/pandora/pandora/0xdb.jsonc $SITE_CONFIG +HOST_CONFIG="/srv/pandora/pandora/config.$HOST.jsonc" +SITE_CONFIG="/srv/pandora/pandora/config.jsonc" +test -e $HOST_CONFIG && cp $HOST_CONFIG $SITE_CONFIG +test -e $SITE_CONFIG || cp /srv/pandora/pandora/config.pandora.jsonc $SITE_CONFIG + cat > /srv/pandora/pandora/local_settings.py << EOF DATABASES = { 'default': { @@ -33,12 +36,13 @@ DATABASES = { 'PASSWORD': '', } } -DEBUG = False -DATA_SERVICE = "" -SITE_CONFIG = '$SITE_CONFIG' BROKER_PASSWORD = "$RABBITPWD" XACCELREDIRECT = True DB_GIN_TRGM = True + +DEBUG = False +TEMPLATE_DEBUG = DEBUG +JSON_DEBUG = False EOF cd /srv/pandora/pandora