diff options
21 files changed, 188 insertions, 53 deletions
diff --git a/animism-align/cli/app/server/viewer.py b/animism-align/cli/app/server/viewer.py new file mode 100644 index 0000000..3eecc4e --- /dev/null +++ b/animism-align/cli/app/server/viewer.py @@ -0,0 +1,69 @@ +import os +import logging +import logging.handlers + +logger = logging.getLogger("") +logger.setLevel(logging.DEBUG) +handler = logging.handlers.RotatingFileHandler("flask.log", + maxBytes=3000000, backupCount=2) +formatter = logging.Formatter( + '[%(asctime)s] {%(filename)s:%(lineno)d} %(levelname)s - %(message)s') +handler.setFormatter(formatter) +logger.addHandler(handler) +logging.getLogger().addHandler(logging.StreamHandler()) + +from flask import Flask, Blueprint, send_from_directory, request + +from app.settings import app_cfg + +def create_app(script_info=None): + """ + functional pattern for creating the flask app + """ + logging.debug("Starting site viewer...") + # print(app_cfg.SERVER_NAME) + app = Flask(__name__, static_folder=app_cfg.DIR_STATIC_SITE_VIEWER, static_url_path='/') + app.config['SERVER_NAME'] = app_cfg.SERVER_NAME + app.url_map.strict_slashes = False + + # index_html = 'site.html' + + # @app.route('/episode1', methods=['GET']) + # def serve_dir_directory_index(): + # return send_from_directory(app_cfg.DIR_STATIC_SITE_VIEWER, 'episode1/index.html') + + # @app.errorhandler(404) + # def page_not_found(e): + # filename = request.url.replace(app_cfg.SERVER_NAME, '') + # print(filename) + # # return app.send_static_file(index_html), 200 + + # @app.route('/<path:filename>', methods=['GET']) + # def serve_file_in_dir(filename): + # print(filename) + # if os.path.isfile(os.path.join(app_cfg.DIR_STATIC_SITE_VIEWER, filename)): + # return send_from_directory(app_cfg.DIR_STATIC_SITE_VIEWER, filename) + # if os.path.isfile(os.path.join(app_cfg.DIR_STATIC_SITE_VIEWER, filename, 'index.html')): + # filename = os.path.join(filename, 'index.html') + # return send_from_directory(app_cfg.DIR_STATIC_SITE_VIEWER, filename) + # return 404 + + # return app.send_static_file(index_html), 200 + # path = os.path.join(os.path.dirname(__file__), 'site.html') + # with open(path, "r") as f: + # return f.read(), 200 + + # @app.route('/', methods=['GET']) + # def index(): + # return app.send_static_file('site.html') + + @app.route('/favicon.ico') + def favicon(): + return send_from_directory(os.path.join(app.root_path, 'static/img/'), + 'favicon.ico',mimetype='image/vnd.microsoft.icon') + + @app.shell_context_processor + def shell_context(): + return { 'app': app, 'db': db } + + return app diff --git a/animism-align/cli/app/settings/app_cfg.py b/animism-align/cli/app/settings/app_cfg.py index 963e265..de5cb6e 100644 --- a/animism-align/cli/app/settings/app_cfg.py +++ b/animism-align/cli/app/settings/app_cfg.py @@ -35,6 +35,7 @@ CLICK_GROUPS = { 'site': 'commands/site', 'db': '', 'flask': '', + 'viewer': '', } @@ -63,6 +64,8 @@ if 'cli' in os.getcwd(): else: DIR_STATIC = os.path.abspath('static') +DIR_STATIC_SITE_VIEWER = join(DIR_DATA_STORE, 'exports/animism') + HASH_TREE_DEPTH = 3 # for sha256 subdirs HASH_BRANCH_SIZE = 3 # for sha256 subdirs diff --git a/animism-align/cli/cli.py b/animism-align/cli/cli.py index 2158398..0913cd1 100755 --- a/animism-align/cli/cli.py +++ b/animism-align/cli/cli.py @@ -15,7 +15,7 @@ if __name__ == '__main__': # argparse: intercept group argv_tmp = sys.argv sys.argv = sys.argv[:2] - ap = argparse.ArgumentParser('\033[1m\033[94mSwimmer\033[0m') + ap = argparse.ArgumentParser('\033[1m\033[94mAnimism\033[0m') ap.add_argument('group', choices=app_cfg.CLICK_GROUPS.keys()) args = ap.parse_args() sys.argv = argv_tmp @@ -29,6 +29,13 @@ if __name__ == '__main__': cli = FlaskGroup(create_app=create_app) + elif args.group == 'viewer': + + from flask.cli import FlaskGroup + from app.server.viewer import create_app + + cli = FlaskGroup(create_app=create_app) + elif args.group == 'db': import re diff --git a/animism-align/cli/commands/site/export.py b/animism-align/cli/commands/site/export.py index f4cc23e..f9560c6 100644 --- a/animism-align/cli/commands/site/export.py +++ b/animism-align/cli/commands/site/export.py @@ -10,7 +10,7 @@ import os @click.command('info') # @click.option('-g', '--graph', 'opt_graph_path', required=True, # help='Graph name') -@click.option('-o', '--output', 'opt_output_dir', required=False, +@click.option('-o', '--output', 'opt_output_dir', required=False, default="animism", help='Output directory') @click.pass_context def cli(ctx, opt_output_dir): @@ -20,6 +20,7 @@ def cli(ctx, opt_output_dir): # imports import datetime + import json from distutils.dir_util import copy_tree # ------------------------------------------------ @@ -45,7 +46,7 @@ def cli(ctx, opt_output_dir): prune_db(db) media_to_copy = rewrite_db_media(db, site_fp_media, media_url) - db['urls'] = { 'audio': 'media/animism_episode_01.mp3' } + db['urls'] = { 'audio': '/' + page_name + '/media/animism_episode_01.mp3' } media_to_copy['audio'] = { 'src': join(app_cfg.DIR_DATA_STORE, 'peaks/animism_episode_01_2810.mp3'), 'dst': join(site_fp_media, 'animism_episode_01.mp3'), @@ -56,6 +57,7 @@ def cli(ctx, opt_output_dir): index_html = load_text(join(app_cfg.DIR_STATIC, 'site.html'), split=False) index_html = index_html.replace('SITE_PATH', page_url) + index_html = index_html.replace('SITE_JSON', json.dumps(db, separators=(',', ':'))) index_html = index_html.replace('PAGE_TITLE', page_title) index_html = index_html.replace('PAGE_DESCRIPTION', page_desc) index_html = index_html.replace('PLAIN_CONTENT', plain_content(db, site_title)) diff --git a/animism-align/frontend/app/store.js b/animism-align/frontend/app/store.js index f7477df..d1843f7 100644 --- a/animism-align/frontend/app/store.js +++ b/animism-align/frontend/app/store.js @@ -5,31 +5,39 @@ import { createBrowserHistory } from 'history' import thunk from 'redux-thunk' // import { login } from 'app/utils' +// cms import uploadReducer from 'app/views/upload/upload.reducer' -import alignReducer from 'app/views/align/align.reducer' -import audioReducer from 'app/views/audio/audio.reducer' -import paragraphReducer from 'app/views/paragraph/paragraph.reducer' import annotationReducer from 'app/views/annotation/annotation.reducer' import siteReducer from 'app/views/site/site.reducer' import mediaReducer from 'app/views/media/media.reducer' -import viewerReducer from 'app/views/viewer/viewer.reducer' import episodeReducer from 'app/views/episode/episode.reducer' import venueReducer from 'app/views/venue/venue.reducer' +// editor +import alignReducer from 'app/views/align/align.reducer' +import paragraphReducer from 'app/views/paragraph/paragraph.reducer' + +// viewer +import audioReducer from 'app/views/audio/audio.reducer' +import viewerReducer from 'app/views/viewer/viewer.reducer' + const createRootReducer = history => ( combineReducers({ auth: (state = {}) => state, router: connectRouter(history), site: siteReducer, + upload: uploadReducer, - align: alignReducer, - audio: audioReducer, - paragraph: paragraphReducer, annotation: annotationReducer, media: mediaReducer, - viewer: viewerReducer, episode: episodeReducer, venue: venueReducer, + + align: alignReducer, + paragraph: paragraphReducer, + + audio: audioReducer, + viewer: viewerReducer, }) ) diff --git a/animism-align/frontend/app/views/align/align.reducer.js b/animism-align/frontend/app/views/align/align.reducer.js index dec27a4..8a660c0 100644 --- a/animism-align/frontend/app/views/align/align.reducer.js +++ b/animism-align/frontend/app/views/align/align.reducer.js @@ -1,5 +1,5 @@ import * as types from 'app/types' -import { session, getDefault, getDefaultInt } from 'app/session' +// import { session, getDefault, getDefaultInt } from 'app/session' const initialState = { timeline: { diff --git a/animism-align/frontend/app/views/audio/audio.actions.js b/animism-align/frontend/app/views/audio/audio.actions.js index b5d4e1b..ef5a4f6 100644 --- a/animism-align/frontend/app/views/audio/audio.actions.js +++ b/animism-align/frontend/app/views/audio/audio.actions.js @@ -34,12 +34,14 @@ export const pause = () => dispatch => { audioPlayer.pause() } export const seek = play_ts => dispatch => { + console.log('seek to', play_ts) audioPlayer.currentTime = play_ts - dispatch({ type: types.audio.seek, seek_ts: audioPlayer.currentTime }) + console.log(play_ts, audioPlayer.currentTime) + dispatch({ type: types.audio.seek, seek_ts: audioPlayer.currentTime || play_ts }) } export const jump = delta_ts => dispatch => { audioPlayer.currentTime += delta_ts - dispatch({ type: types.audio.seek, seek_ts: audioPlayer.currentTime }) + dispatch({ type: types.audio.seek, seek_ts: audioPlayer.currentTime || play_ts }) } export const toggle = () => dispatch => { if (store.getState().audio.playing) { diff --git a/animism-align/frontend/app/views/paragraph/components/paragraph.list.js b/animism-align/frontend/app/views/paragraph/components/paragraph.list.js index a8f32b3..1507b51 100644 --- a/animism-align/frontend/app/views/paragraph/components/paragraph.list.js +++ b/animism-align/frontend/app/views/paragraph/components/paragraph.list.js @@ -14,6 +14,7 @@ class ParagraphList extends Component { componentDidUpdate(prevProps) { if (this.props.audio.play_ts === prevProps.audio.play_ts) return + if (!this.props.paragraphs) return this.setCurrentParagraph() } @@ -64,6 +65,7 @@ class ParagraphList extends Component { onAnnotationClick, onParagraphDoubleClick, } = this.props const { currentParagraph, currentAnnotation } = this.state + if (!paragraphs) return null return paragraphs.map((paragraph, i) => { if (selectedParagraph && selectedParagraph.id === paragraph.id) { paragraph = selectedParagraph diff --git a/animism-align/frontend/app/views/paragraph/paragraph.reducer.js b/animism-align/frontend/app/views/paragraph/paragraph.reducer.js index 041014f..3b33128 100644 --- a/animism-align/frontend/app/views/paragraph/paragraph.reducer.js +++ b/animism-align/frontend/app/views/paragraph/paragraph.reducer.js @@ -1,5 +1,5 @@ import * as types from 'app/types' -import { session, getDefault, getDefaultInt } from 'app/session' +// import { session, getDefault, getDefaultInt } from 'app/session' import { crudState, crudReducer } from 'app/api/crud.reducer' diff --git a/animism-align/frontend/app/views/viewer/nav/nav.parent.js b/animism-align/frontend/app/views/viewer/nav/nav.parent.js index d18aac0..cb83f94 100644 --- a/animism-align/frontend/app/views/viewer/nav/nav.parent.js +++ b/animism-align/frontend/app/views/viewer/nav/nav.parent.js @@ -61,7 +61,7 @@ class NavParent extends Component { e && e.preventDefault() e && e.stopPropagation() const { viewer } = this.props - // console.log('>> SEEK') + console.log('>> SEEK', viewer.nextSection) if (viewer.nextSection) { actions.viewer.seekToSection(viewer.nextSection) } else { diff --git a/animism-align/frontend/app/views/viewer/player/player.container.js b/animism-align/frontend/app/views/viewer/player/player.container.js index 26697f9..0bf64d1 100644 --- a/animism-align/frontend/app/views/viewer/player/player.container.js +++ b/animism-align/frontend/app/views/viewer/player/player.container.js @@ -83,6 +83,7 @@ class PlayerContainer extends Component { // console.log('inCurrentSection?', inCurrentSection) // console.log('didSeek?', didSeek) // if the current TS isn't in the same section as the current one... + console.log(currentSection.start_ts, play_ts, currentSection.end_ts) if (inCurrentSection) { return } diff --git a/animism-align/frontend/app/views/viewer/player/player.transcript.js b/animism-align/frontend/app/views/viewer/player/player.transcript.js index 15d376a..9796008 100644 --- a/animism-align/frontend/app/views/viewer/player/player.transcript.js +++ b/animism-align/frontend/app/views/viewer/player/player.transcript.js @@ -60,6 +60,7 @@ class PlayerTranscript extends Component { const className = "player-transcript " + color + " " + ( inlineParagraphCount > 2 ? 'scrollable' : 'not-scrollable' ) + console.log(this.props.section) return ( <div className={className} diff --git a/animism-align/frontend/app/views/viewer/viewer.actions.js b/animism-align/frontend/app/views/viewer/viewer.actions.js index 82d3cf1..1372013 100644 --- a/animism-align/frontend/app/views/viewer/viewer.actions.js +++ b/animism-align/frontend/app/views/viewer/viewer.actions.js @@ -47,6 +47,8 @@ export const loadSections = () => dispatch => { const { order: annotationOrder, lookup: annotationLookup } = state.annotation.index const { lookup: mediaLookup } = state.media.index + console.log(state) + // loop over the annotations in time order. annotationOrder.forEach((annotation_id, i) => { // fetch the current annotation @@ -206,7 +208,6 @@ export const loadSections = () => dispatch => { }) // console.log(sections) // console.log(footnoteList) - // console.log(fullscreenTimeline) dispatch({ type: types.viewer.load_sections, sections, footnoteList }) } diff --git a/animism-align/frontend/app/views/viewer/viewer.container.js b/animism-align/frontend/app/views/viewer/viewer.container.js index 90787db..c81b79e 100644 --- a/animism-align/frontend/app/views/viewer/viewer.container.js +++ b/animism-align/frontend/app/views/viewer/viewer.container.js @@ -35,6 +35,7 @@ import VitrineModal from './modals/modals.vitrine' class ViewerContainer extends Component { render() { const { loaded, viewer, playing } = this.props + // console.log(loaded, playing, viewer) if (!loaded) { return <div className='viewer loading'><Loader /></div> } diff --git a/animism-align/frontend/site/actions.js b/animism-align/frontend/site/actions.js index f11ce13..2642469 100644 --- a/animism-align/frontend/site/actions.js +++ b/animism-align/frontend/site/actions.js @@ -1,8 +1,10 @@ import { bindActionCreators } from 'redux' // import { actions as crudActions } from './api' -import * as audioActions from 'app/views/audio.actions' -import * as viewerActions from 'app/views/viewer.actions' +import * as siteActions from 'site/site/site.actions' +import * as audioActions from 'app/views/audio/audio.actions' +import * as viewerActions from 'app/views/viewer/viewer.actions' +import * as transcriptActions from 'app/views/paragraph/transcript.actions' import { store } from './store' @@ -11,12 +13,13 @@ export default // .map(a => [a, crudActions[a]]) // .concat( [ - // ['site', siteActions], + ['site', siteActions], ['audio', audioActions], ['viewer', viewerActions], + ['transcript', transcriptActions] ] //) .map(p => [p[0], bindActionCreators(p[1], store.dispatch)]) .concat([ // ['socket', socketActions], ]) - .reduce((a,b) => (a[b[0]] = b[1])&&a,{})
\ No newline at end of file + .reduce((a,b) => (a[b[0]] = b[1])&&a,{}) diff --git a/animism-align/frontend/site/app.js b/animism-align/frontend/site/app.js index 4f8a6cf..8509954 100644 --- a/animism-align/frontend/site/app.js +++ b/animism-align/frontend/site/app.js @@ -3,31 +3,26 @@ import { ConnectedRouter } from 'connected-react-router' import { Route } from 'react-router' // import ViewerContainer from './viewer/viewer.container' +import Viewer from 'app/views/viewer/viewer.container' import actions from './actions' export default class App extends Component { componentDidMount() { - const path_partz = window.location.pathname.split('/') - const graph_name = path_partz[1] - let path_name = null - if (path_partz.length > 2) { - path_name = path_partz[2] - } - // console.log('loading', graph_name, path_name) - actions.site.loadSite(graph_name, path_name) + actions.site.loadProject() } render() { - return ( - <ConnectedRouter history={this.props.history}> - <div className='app'> - <Route exact key='root' path='/' render={() => { - // setTimeout(() => this.props.history.push('/'), 10) - return null - }} /> - </div> - </ConnectedRouter> - ) + return <Viewer /> + // return ( + // <ConnectedRouter history={this.props.history}> + // <div className='app'> + // <Route exact key='root' path='/' render={() => { + // // setTimeout(() => this.props.history.push('/'), 10) + // return null + // }} /> + // </div> + // </ConnectedRouter> + // ) } } /* diff --git a/animism-align/frontend/site/index.js b/animism-align/frontend/site/index.js index 448d38d..7d47bd0 100644 --- a/animism-align/frontend/site/index.js +++ b/animism-align/frontend/site/index.js @@ -7,7 +7,7 @@ import '../app/common/form.css' import { store } from './store' -import Viewer from 'app/views/viewer/viewer.container' +import App from './app' const container = document.createElement('div') container.classList.add('container') @@ -15,6 +15,6 @@ document.body.appendChild(container) ReactDOM.render( <Provider store={store}> - <Viewer /> + <App /> </Provider>, container ) diff --git a/animism-align/frontend/site/site/site.reducer.js b/animism-align/frontend/site/site/site.reducer.js new file mode 100644 index 0000000..ea10886 --- /dev/null +++ b/animism-align/frontend/site/site/site.reducer.js @@ -0,0 +1,12 @@ +// import * as types from 'app/types' + +const initialState = { +} + +export default function siteReducer(state = initialState, action) { + console.log(action.type, action) + switch (action.type) { + default: + return state + } +} diff --git a/animism-align/frontend/site/store.js b/animism-align/frontend/site/store.js index 0b4aff7..2d9ffbc 100644 --- a/animism-align/frontend/site/store.js +++ b/animism-align/frontend/site/store.js @@ -1,25 +1,46 @@ -import { crudReducer } from 'app/api/crud.reducer' +import { crudState, crudReducer } from 'app/api/crud.reducer' import { applyMiddleware, compose, combineReducers, createStore } from 'redux' import { connectRouter, routerMiddleware } from 'connected-react-router' import { createBrowserHistory } from 'history' import thunk from 'redux-thunk' -// import siteReducer from './site/site.reducer' +import audioReducer from 'app/views/audio/audio.reducer' +import alignReducer from 'app/views/align/align.reducer' +import viewerReducer from 'app/views/viewer/viewer.reducer' +import paragraphReducer from 'app/views/paragraph/paragraph.reducer' +import siteReducer from 'site/site/site.reducer' + +// document.querySelector("#plain_content").remove() +const siteData = JSON.parse(document.querySelector("#site_data").innerText) const createRootReducer = history => ( combineReducers([ "media", - "upload", "annotation", "paragraph", "episode", "venue", ].reduce((a, type) => { - a[type] = crudReducer(type) + a[type] = (() => { + const initialState = { + ...crudState(), + index: siteData[type], + } + if (type === 'paragraph') { + return (state=initialState, action) => paragraphReducer(state, action) + } else { + const typeReducer = crudReducer(type) + return (state=initialState, action) => typeReducer(state, action) + } + })() return a }, { - auth: (state = {}) => state, + // auth: (state = {}) => state, router: connectRouter(history), + site: siteReducer, + audio: audioReducer, + align: alignReducer, + viewer: viewerReducer, })) ) diff --git a/animism-align/static/site.html b/animism-align/static/site.html index fd7eb84..6cc5a02 100644 --- a/animism-align/static/site.html +++ b/animism-align/static/site.html @@ -5,16 +5,14 @@ <title>PAGE_TITLE</title> <meta name="viewport" content="width=device-width,initial-scale=1.0" /> <meta name="description" content="PAGE_DESCRIPTION" /> - <link rel="stylesheet" type="text/css" href="SITE_PATH/site.css" /> <style> -/* - html { background: #000; } - .plain_content { display: none; } -*/ + html { background: #fff; } + #plain_content { display: none; } </style> </head> <body> -<div class="plain_content">PLAIN_CONTENT</div> +<div id="plain_content">PLAIN_CONTENT</div> +<script type="text/json" id="site_data">SITE_JSON</script> <script> var s = document.createElement('script'); s.setAttribute('src', 'BUNDLE_PATH?' + (Date.now() / 3600)) diff --git a/animism-align/webpack.config.site.js b/animism-align/webpack.config.site.js index 7b76b93..bce064f 100644 --- a/animism-align/webpack.config.site.js +++ b/animism-align/webpack.config.site.js @@ -61,7 +61,16 @@ module.exports = { presets: ['@babel/preset-react'], plugins: [ "@babel/plugin-transform-runtime", - ], + [ "@babel/plugin-proposal-class-properties", { "loose": true } ], + "transform-async-to-generator", + ["module-resolver", { + "root": ["./frontend"], + "alias": { + "app/store": "./frontend/site/store", + "app/actions": "./frontend/site/actions", + } + }] + ] } } ] |
