jsgui3-server 0.0.121 → 0.0.122
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/settings.json +6 -0
- package/README.md +10 -10
- package/controls/Active_HTML_Document.js +116 -116
- package/controls/README.md +7 -7
- package/controls/page/admin.js +74 -74
- package/controls/panel/admin.js +11 -11
- package/examples/_css/basic.css +913 -913
- package/examples/_css/database-control.css +51 -51
- package/examples/_css/jsgui.css +66 -66
- package/examples/_css/multi-layout.css +23 -23
- package/examples/_css/style.css +1669 -1669
- package/examples/_css/top-and-bottom-bars.css +54 -54
- package/examples/box/1) square box/client.js +188 -188
- package/examples/box/1) square box/server.js +112 -112
- package/examples/box/2) twenty square boxes/css flex wrap/client.js +248 -248
- package/examples/box/2) twenty square boxes/css flex wrap/server.js +112 -112
- package/examples/boxes/square_boxes.js +45 -48
- package/examples/boxes/square_boxes_client.js +132 -136
- package/examples/client-side-popup-menu-button.html +93 -93
- package/examples/color_palette.js +51 -51
- package/examples/color_palette_client.js +95 -95
- package/examples/controls/1) window/client.js +186 -186
- package/examples/controls/1) window/server.js +117 -117
- package/examples/controls/10) window, mirrored text inputs/client.js +320 -320
- package/examples/controls/10) window, mirrored text inputs/server.js +117 -117
- package/examples/controls/11) window, mirrored text fields/client.js +493 -251
- package/examples/controls/11) window, mirrored text fields/server.js +117 -117
- package/examples/controls/11b) window, shared Data_Object model mirrored text fields/client.js +613 -331
- package/examples/controls/11b) window, shared Data_Object model mirrored text fields/server.js +117 -117
- package/examples/controls/11c) window, shared Data_Value model mirrored text fields/client.js +617 -617
- package/examples/controls/11c) window, shared Data_Value model mirrored text fields/server.js +117 -117
- package/examples/controls/11d) window, shared model mirrored integer text fields/client.js +611 -280
- package/examples/controls/11d) window, shared model mirrored integer text fields/server.js +134 -22
- package/examples/controls/12) window, Select_Options control/client.js +435 -452
- package/examples/controls/12) window, Select_Options control/server.js +117 -117
- package/examples/controls/2) two windows/client.js +193 -193
- package/examples/controls/2) two windows/server.js +113 -113
- package/examples/controls/3) five windows/client.js +217 -217
- package/examples/controls/3) five windows/server.js +116 -115
- package/examples/controls/4) window, tabbed panel/client.js +54 -225
- package/examples/controls/4) window, tabbed panel/server.js +17 -117
- package/examples/controls/5) window, grid/client.js +204 -484
- package/examples/controls/5) window, grid/server.js +117 -119
- package/examples/controls/6) window, color_palette/client.js +204 -204
- package/examples/controls/6) window, color_palette/server.js +117 -117
- package/examples/controls/7) window, month_view/client.js +40 -231
- package/examples/controls/7) window, month_view/server.js +117 -117
- package/examples/controls/8) window, checkbox/client.js +247 -209
- package/examples/controls/8) window, checkbox/server.js +117 -117
- package/examples/controls/9) window, date picker/client.js +303 -303
- package/examples/controls/9) window, date picker/server.js +117 -117
- package/examples/controls/9b) window, shared data.model mirrored date pickers/client.js +398 -398
- package/examples/controls/9b) window, shared data.model mirrored date pickers/server.js +117 -117
- package/examples/controls/__old/_html-server-color-palette.js +114 -114
- package/examples/controls/__old/html-server-combo-box.js +104 -104
- package/examples/controls/__old/html-server-list.js +98 -98
- package/examples/controls/__old/html-server-popup-menu-button.js +114 -114
- package/examples/controls/__old/html-server-start-stop-toggle-button.js +146 -146
- package/examples/controls/__old/scs-arrow-button.js +36 -36
- package/examples/controls/__old/scs-date-picker.js +157 -157
- package/examples/controls/__old/scs-file-browser.js +82 -82
- package/examples/controls/__old/scs-item.js +159 -159
- package/examples/controls/__old/scs-month-arrow-selector.js +126 -126
- package/examples/controls/__old/scs-month-view.js +94 -94
- package/examples/controls/__old/scs-start-stop-toggle-button.js +40 -40
- package/examples/controls/__old/scs-tree.js +49 -49
- package/examples/controls/__old/scs-year-arrow-selector.js +127 -127
- package/examples/demos/date-picker.js +119 -119
- package/examples/demos/explain-encapsulation.js +9 -9
- package/examples/demos/resizing.js +35 -35
- package/examples/demos/server_time.js +6 -6
- package/examples/grids/grid_1.js +45 -45
- package/examples/grids/grid_1_client.js +329 -329
- package/examples/html-rendering.js +20 -20
- package/examples/html-server.js +105 -105
- package/examples/introducing jsgui3/server.js +110 -110
- package/examples/mx_display/mx_display_1.js +45 -45
- package/examples/mx_display/mx_display_1_client.js +444 -444
- package/fs2.js +1836 -1836
- package/http/responders/HTTP_Responder.js +15 -15
- package/http/responders/static/Static_Route_HTTP_Responder.js +105 -105
- package/module.js +34 -34
- package/old/_single-control-server.js +418 -418
- package/old/single-control-server.js +368 -368
- package/old/single-page-app.js +131 -131
- package/package.json +42 -42
- package/page-context.js +92 -92
- package/publishers/helpers/assigners/Assigner.js +10 -10
- package/publishers/helpers/assigners/static-compressed-response-buffers/Single_Control_Webpage_Server_Static_Compressed_Response_Buffers_Assigner.js +150 -150
- package/publishers/helpers/assigners/static-headers/Single_Control_Webpage_Server_Static_Headers_Assigner.js +109 -109
- package/publishers/helpers/assigners/static-routes/Single_Control_Webpage_Server_Static_Routes_Assigner.js +91 -91
- package/publishers/helpers/assigners/static-uncompressed-response-buffers/Single_Control_Webpage_Server_Static_Uncompressed_Response_Buffers_Assigner.js +104 -104
- package/publishers/helpers/preparers/static/bundle/Ready_To_Serve_Preparer.js +18 -18
- package/publishers/helpers/preparers/static/bundle/Static_Routes_Responses_Webpage_Bundle_Preparer.js +44 -44
- package/publishers/http-function-publisher.js +212 -212
- package/publishers/http-html-page-publisher.js +5 -5
- package/publishers/http-html-publisher.js +24 -24
- package/publishers/http-js-publisher.js +135 -135
- package/publishers/http-observable-publisher.js +124 -124
- package/publishers/http-publisher.js +53 -53
- package/publishers/http-resource-publisher.js +325 -325
- package/publishers/http-webpage-publisher.js +659 -658
- package/publishers/http-webpageorsite-publisher.js +343 -343
- package/publishers/http-website-publisher.js +640 -640
- package/publishers/notes.md +9 -9
- package/resources/README.md +16 -16
- package/resources/_old_website-javascript-resource.js +994 -994
- package/resources/_old_website-resource.js +507 -507
- package/resources/compile/server-resource-compilation.js +43 -43
- package/resources/data-resource.js +118 -118
- package/resources/fs-resource.js +146 -146
- package/resources/jsbuilder/Abstract_Single_Declaration.js +105 -105
- package/resources/jsbuilder/Abstract_Single_Declaration_Sequence.js +42 -42
- package/resources/jsbuilder/JS_AST/JS_AST_Abstract_Node.js +61 -61
- package/resources/jsbuilder/JS_AST/JS_AST_Abstract_Node_Group.js +41 -41
- package/resources/jsbuilder/JS_AST/JS_AST_Group_Shared.js +61 -61
- package/resources/jsbuilder/JS_AST/JS_AST_Node.js +93 -93
- package/resources/jsbuilder/JS_AST/JS_AST_Node_0-Core.js +253 -253
- package/resources/jsbuilder/JS_AST/JS_AST_Node_1-Babel.js +337 -337
- package/resources/jsbuilder/JS_AST/JS_AST_Node_10-Changing.js +39 -39
- package/resources/jsbuilder/JS_AST/JS_AST_Node_2.1.1-Child.js +96 -96
- package/resources/jsbuilder/JS_AST/JS_AST_Node_2.1.2-Parent.js +37 -37
- package/resources/jsbuilder/JS_AST/JS_AST_Node_2.1.3-Ancestor.js +61 -61
- package/resources/jsbuilder/JS_AST/JS_AST_Node_2.2-Inner.js +43 -43
- package/resources/jsbuilder/JS_AST/JS_AST_Node_2.3-All.js +72 -72
- package/resources/jsbuilder/JS_AST/JS_AST_Node_2.4-Sibling.js +92 -92
- package/resources/jsbuilder/JS_AST/JS_AST_Node_2.5-Available_In_Scope.js +29 -29
- package/resources/jsbuilder/JS_AST/JS_AST_Node_2.9-Signature.js +116 -116
- package/resources/jsbuilder/JS_AST/JS_AST_Node_3-Basics.js +159 -159
- package/resources/jsbuilder/JS_AST/JS_AST_Node_3.0.0-Basics_First.js +178 -178
- package/resources/jsbuilder/JS_AST/JS_AST_Node_3.0.1-Basics_Second.js +87 -87
- package/resources/jsbuilder/JS_AST/JS_AST_Node_3.0.99-Basics_Last.js +91 -91
- package/resources/jsbuilder/JS_AST/JS_AST_Node_3.1-Basics_Each.js +136 -136
- package/resources/jsbuilder/JS_AST/JS_AST_Node_3.1.5-Basics_Count.js +73 -73
- package/resources/jsbuilder/JS_AST/JS_AST_Node_3.2-Basics_Filter.js +39 -39
- package/resources/jsbuilder/JS_AST/JS_AST_Node_3.3-Basics_Collect.js +85 -85
- package/resources/jsbuilder/JS_AST/JS_AST_Node_3.4-Basics_Select.js +42 -42
- package/resources/jsbuilder/JS_AST/JS_AST_Node_3.5-Basics_Find.js +40 -40
- package/resources/jsbuilder/JS_AST/JS_AST_Node_3.6-Basics_Callmap.js +54 -54
- package/resources/jsbuilder/JS_AST/JS_AST_Node_4.0-Index_Indexes.js +45 -45
- package/resources/jsbuilder/JS_AST/JS_AST_Node_4.1-Index.js +343 -343
- package/resources/jsbuilder/JS_AST/JS_AST_Node_5.0-Category.js +38 -38
- package/resources/jsbuilder/JS_AST/JS_AST_Node_5.1-Category_Identifier.js +30 -30
- package/resources/jsbuilder/JS_AST/JS_AST_Node_5.2-Category_Literal.js +28 -28
- package/resources/jsbuilder/JS_AST/JS_AST_Node_5.3-Category_Expression.js +26 -26
- package/resources/jsbuilder/JS_AST/JS_AST_Node_5.4-Category_Pattern.js +8 -8
- package/resources/jsbuilder/JS_AST/JS_AST_Node_5.5-Category_Declaration.js +43 -43
- package/resources/jsbuilder/JS_AST/JS_AST_Node_5.6-Category_Statement.js +21 -21
- package/resources/jsbuilder/JS_AST/JS_AST_Node_6.0-Type.js +89 -89
- package/resources/jsbuilder/JS_AST/JS_AST_Node_6.1-Type_Class_Declaration.js +8 -8
- package/resources/jsbuilder/JS_AST/JS_AST_Node_6.2-Type_Variable_Declaration.js +27 -27
- package/resources/jsbuilder/JS_AST/JS_AST_Node_6.3-Type_Variable_Declarator.js +28 -28
- package/resources/jsbuilder/JS_AST/JS_AST_Node_7-Query.js +736 -736
- package/resources/jsbuilder/JS_AST/JS_AST_Node_8-Features.js +64 -64
- package/resources/jsbuilder/JS_AST/JS_AST_Node_9-Planning.js +31 -31
- package/resources/jsbuilder/JS_AST/JS_AST_Node_Arrangement.js +15 -15
- package/resources/jsbuilder/JS_AST/JS_AST_Node_Feature/JS_AST_Node_Declared_Object.js +305 -305
- package/resources/jsbuilder/JS_AST/JS_AST_Node_Feature/JS_AST_Node_Feature.js +77 -77
- package/resources/jsbuilder/JS_AST/JS_AST_Node_Feature/JS_AST_Node_Feature_Declaration.js +248 -248
- package/resources/jsbuilder/JS_AST/JS_AST_Node_Feature/JS_AST_Node_Feature_Declarator.js +138 -138
- package/resources/jsbuilder/JS_AST/JS_AST_Node_Feature/JS_AST_Root_Node_Feature/JS_AST_Root_Node_Feature.js +10 -10
- package/resources/jsbuilder/JS_AST/JS_AST_Node_Feature/JS_AST_Root_Node_Feature/JS_AST_Root_Node_Feature_Exported.js +100 -100
- package/resources/jsbuilder/JS_AST/JS_AST_Node_Feature/JS_AST_Root_Node_Feature/JS_AST_Root_Node_Feature_Exports.js +60 -60
- package/resources/jsbuilder/JS_AST/JS_AST_Node_Feature/JS_AST_Root_Node_Feature/JS_AST_Root_Node_Interpreted.js +179 -179
- package/resources/jsbuilder/JS_AST/JS_AST_Node_Feature/JS_AST_Root_Node_Feature/_JSGUI_Root_Node_Interpreted.js +43 -43
- package/resources/jsbuilder/JS_AST/JS_AST_Node_Feature/JS_AST_Root_Node_Feature/special_case_objectassign_to_object.js +12 -12
- package/resources/jsbuilder/JS_AST/JS_AST_Node_Group.js +35 -35
- package/resources/jsbuilder/JS_AST/JS_AST_Operation.js +11 -11
- package/resources/jsbuilder/JS_AST/JS_AST_Operation_On_Relationship.js +31 -31
- package/resources/jsbuilder/JS_AST/JS_AST_Ordered_Relationship_Node_To_Group.js +37 -37
- package/resources/jsbuilder/JS_AST/JS_AST_Ordinal.js +39 -39
- package/resources/jsbuilder/JS_AST/JS_AST_Ordinal_Relationship.js +25 -25
- package/resources/jsbuilder/JS_AST/JS_AST_Relationship_Node_To_Group.js +200 -200
- package/resources/jsbuilder/JS_AST/JS_AST_Relationship_Node_Within_Group_To_Node.js +43 -43
- package/resources/jsbuilder/JS_AST/_JS_AST_Node_3.8-Query_Features.js +76 -76
- package/resources/jsbuilder/JS_AST/query/enable_array_as_queryable.js +227 -227
- package/resources/jsbuilder/JS_AST/query/find_object_keys.js +404 -404
- package/resources/jsbuilder/JS_AST/query/node_queries.js +8 -8
- package/resources/jsbuilder/JS_AST/query/root_query_identidy.js +11 -11
- package/resources/jsbuilder/JS_AST_Node_Extended/JSGUI_Singular_Declaration.js +85 -85
- package/resources/jsbuilder/JS_AST_Node_Extended/JS_AST_Node_Declaration.js +123 -123
- package/resources/jsbuilder/JS_AST_Node_Extended/JS_AST_Node_Extended.js +87 -87
- package/resources/jsbuilder/JS_AST_Node_Extended/JS_AST_Node_Extended_0-Core.js +10 -10
- package/resources/jsbuilder/JS_Builder.js +10 -10
- package/resources/jsbuilder/JS_File/Feature/JS_File_Declared_Object.js +31 -31
- package/resources/jsbuilder/JS_File/Feature/JS_File_Exported_Object_Info.js +25 -25
- package/resources/jsbuilder/JS_File/Feature/JS_File_Exports.js +78 -78
- package/resources/jsbuilder/JS_File/Feature/JS_File_Feature.js +17 -17
- package/resources/jsbuilder/JS_File/Feature/JS_File_Imported_Object_Info.js +25 -25
- package/resources/jsbuilder/JS_File/Feature/JS_File_Imports.js +8 -8
- package/resources/jsbuilder/JS_File/JS_File.js +12 -12
- package/resources/jsbuilder/JS_File/JS_File_0-Core.js +202 -202
- package/resources/jsbuilder/JS_File/JS_File_1-Early_Parse.js +175 -175
- package/resources/jsbuilder/JS_File/JS_File_2-Babel.js +81 -81
- package/resources/jsbuilder/JS_File/JS_File_3-JS_AST_Node.js +86 -86
- package/resources/jsbuilder/JS_File/JS_File_4-Query.js +413 -413
- package/resources/jsbuilder/JS_File/JS_File_4.1-Query_Features.js +414 -414
- package/resources/jsbuilder/JS_File/JS_File_5-Planning.js +59 -59
- package/resources/jsbuilder/JS_File/JS_File_6-Changing.js +24 -24
- package/resources/jsbuilder/JS_File/JS_File_Export_Reference.js +12 -12
- package/resources/jsbuilder/JS_File/JS_File_Import_Reference.js +23 -23
- package/resources/jsbuilder/JS_File/JS_File_Import_References.js +31 -31
- package/resources/jsbuilder/JS_File/JS_File_Processor.js +16 -16
- package/resources/jsbuilder/JS_File/JS_Files.js +15 -15
- package/resources/jsbuilder/Module.js +14 -14
- package/resources/jsbuilder/Platform.js +13 -13
- package/resources/jsbuilder/Platforms.js +69 -69
- package/resources/jsbuilder/Project.js +109 -109
- package/resources/jsbuilder/Reference.js +1 -1
- package/resources/jsbuilder/Reference_Sequence.js +16 -16
- package/resources/jsbuilder/Scope.js +29 -29
- package/resources/jsbuilder/Variable_Name_Provider.js +42 -42
- package/resources/jsbuilder/_JS_File.js +225 -225
- package/resources/jsbuilder/ast_query.js +20 -20
- package/resources/jsbuilder/babel/babel_consts.js +162 -162
- package/resources/jsbuilder/babel/babel_node_tools.js +541 -541
- package/resources/jsbuilder/babel/deep_iterate/deep_iterate_babel.js +923 -904
- package/resources/jsbuilder/build.js +16 -16
- package/resources/jsbuilder/platform_notes.md +66 -66
- package/resources/jsbuilder/test/test_ast_node.js +381 -381
- package/resources/jsbuilder/test/test_js_file.js +303 -303
- package/resources/jsbuilder/test/test_project.js +157 -157
- package/resources/local-server-info-resource.js +96 -96
- package/resources/notes.txt +10 -10
- package/resources/old/website-image-resource.js +1185 -1185
- package/resources/process-js.js +498 -498
- package/resources/processors/bundlers/bundle.js +29 -29
- package/resources/processors/bundlers/bundler.js +23 -23
- package/resources/processors/bundlers/css-bundler.js +234 -234
- package/resources/processors/bundlers/js/JS_Bundler.js +51 -51
- package/resources/processors/bundlers/js/esbuild/Advanced_JS_Bundler_Using_ESBuild.js +388 -391
- package/resources/processors/bundlers/js/esbuild/Bundler_Using_ESBuild.js +8 -8
- package/resources/processors/bundlers/js/esbuild/Core_JS_Non_Minifying_Bundler_Using_ESBuild.js +188 -188
- package/resources/processors/bundlers/js/esbuild/Core_JS_Single_File_Minifying_Bundler_Using_ESBuild.js +191 -192
- package/resources/processors/bundlers/js/esbuild/_Old_CSS_Extractor.js +239 -239
- package/resources/processors/bundlers/js-bundler.js +263 -263
- package/resources/processors/bundlers/test_ast.js +73 -73
- package/resources/processors/bundlers/webpage-bundler.js +404 -404
- package/resources/processors/bundlers/website-bundler.js +22 -22
- package/resources/processors/extractors/Extractor.js +9 -11
- package/resources/processors/extractors/js/css_and_js/AST_Node/CSS_And_JS_From_JS_String_Using_AST_Node_Extractor.js +239 -254
- package/resources/processors/extractors/js/css_and_js/CSS_And_JS_From_JS_String_Extractor.js +3 -3
- package/resources/processors/extractors/string/Pos_Span_String_Extractor.js +93 -93
- package/resources/server-installed-tools.js +28 -28
- package/resources/server-resource-pool.js +41 -41
- package/resources/website-audio-resource.js +735 -735
- package/resources/website-css-resource.js +411 -411
- package/resources/website-image-resource.js +412 -412
- package/resources/website-javascript-resource-processor.js +908 -908
- package/resources/website-javascript-resource.js +874 -874
- package/resources/website-resource-processor.js +10 -10
- package/resources/website-resource.js +164 -164
- package/resources/website-static-html-resource.js +199 -199
- package/resources/website-template-html-resource.js +231 -231
- package/roadmap.md +75 -75
- package/server.js +609 -573
- package/static-page-context.js +13 -13
- package/website/webpage.js +81 -81
- package/website/website-group.js +15 -15
- package/website/website.js +260 -260
- package/examples/controls/11d) window, shared model mirrored integer text fields/both.js +0 -17
- package/examples/controls/13) window, shared model mirrored lat_long/client.js +0 -933
- package/examples/controls/13) window, shared model mirrored lat_long/server.js +0 -50
- package/examples/controls/14) window, control compositional model/client.js +0 -328
- package/examples/controls/14) window, control compositional model/server.js +0 -118
- package/examples/controls/14a) window, control spec has compositional model/client.js +0 -440
- package/examples/controls/14a) window, control spec has compositional model/server.js +0 -118
- package/examples/controls/15) window, text field/client.js +0 -256
- package/examples/controls/15) window, text field/server.js +0 -39
- package/examples/controls/16) Window([Text_Input])/client.js +0 -266
- package/examples/controls/16) Window([Text_Input])/server.js +0 -109
- package/examples/controls/16a) Window([Text_Input]) Integer data.model.data_type/client.js +0 -494
- package/examples/controls/16a) Window([Text_Input]) Integer data.model.data_type/isomorphic.js +0 -24
- package/examples/controls/16a) Window([Text_Input]) Integer data.model.data_type/server.js +0 -73
- package/examples/controls/2b) two window, context menus/client.js +0 -193
- package/examples/controls/2b) two window, context menus/server.js +0 -114
- package/examples/controls/4a) window, tabbed panel with various controls inside/client.js +0 -233
- package/examples/controls/4a) window, tabbed panel with various controls inside/server.js +0 -118
|
@@ -1,735 +1,735 @@
|
|
|
1
|
-
|
|
2
|
-
// Need to maintain the various concepts applying here.
|
|
3
|
-
// Get them working or throwing errors / set to NYI if they are not so simple to fix / implement.
|
|
4
|
-
|
|
5
|
-
/*
|
|
6
|
-
if (typeof define !== 'function') {
|
|
7
|
-
var define = require('amdefine')(module);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// I would like my own (fast) way of reading MP3 metadata.
|
|
11
|
-
|
|
12
|
-
// Had a problem seeking later parts of the track in Chrome. It would request a few seconds too little audio from the server.
|
|
13
|
-
// Seems like Chrome estimated positions for VBR audio are out.
|
|
14
|
-
// This bay be improved by changing to a constant bitrate (though that is less efficient encoding, generally)
|
|
15
|
-
|
|
16
|
-
// Other parts of the system are likely to be more important.
|
|
17
|
-
// It may be useful to have an interface to administer the site's audio.
|
|
18
|
-
|
|
19
|
-
// Browse tracks, and encode them into different bit rates and formats.
|
|
20
|
-
// Maintain copies of the tracks in different bit rates and formats.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
define(['module', 'path', 'fs', 'url', '../../web/jsgui-html', 'os', 'http', 'url', './resource',
|
|
24
|
-
'../../web/jsgui-je-suis-xml', 'cookies', '../../fs/jsgui-node-fs2-core', '../../audio/jsgui-node-audio-metadata', 'crypto'],
|
|
25
|
-
|
|
26
|
-
function(module, path, fs, url, jsgui, os, http, libUrl,
|
|
27
|
-
Resource, JeSuisXML, Cookies, fs2, audio_metadata, crypto) {
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
const path = require('path'), fs = require('fs'), url = require('url'), jsgui = require('../../web/jsgui-html'), os = require('os'), http = require('http'), libUrl = require('url'),
|
|
31
|
-
Resource = require('./resource'), Cookies = require('./cookies'), fs2 = require('../../fs/jsgui-node-fs2-core'), audio_metadata = require('../../audio/jsgui-node-audio-metadata'), crypto = require('crypto');
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
var stringify = jsgui.stringify, each = jsgui.eac, arrayify = jsgui.arrayify, tof = jsgui.tof;
|
|
35
|
-
var filter_map_by_regex = jsgui.filter_map_by_regex;
|
|
36
|
-
var Class = jsgui.Class, Data_Object = jsgui.Data_Object, Enhanced_Data_Object = jsgui.Enhanced_Data_Object;
|
|
37
|
-
var fp = jsgui.fp, is_defined = jsgui.is_defined;
|
|
38
|
-
var Collection = jsgui.Collection;
|
|
39
|
-
|
|
40
|
-
// This resource may have quite a lot of functionality put in to deal with
|
|
41
|
-
// seriving images in an optimized way.
|
|
42
|
-
// Want this so it also serves the developer in that images can be put in place
|
|
43
|
-
// and modified easily in an unoptimized way. Then they get put in place in an
|
|
44
|
-
// optimized way on request or automatically. Want to incorporate sprite sheet generation
|
|
45
|
-
// here. This should handle requests for individual images, but it's also possible sprites
|
|
46
|
-
// could be requested. If that's the case, maybe it should pass it onto a sprite resource.
|
|
47
|
-
// It would be really good to have practically instant sprite generation, using C / C++
|
|
48
|
-
// and maybe OpenCL.
|
|
49
|
-
// It could be requests to a sprite resource that returns a predefined selection of
|
|
50
|
-
// assets.
|
|
51
|
-
// The image resource may also be called upon to return rescaled versions.
|
|
52
|
-
// That's something that OpenCL would also be very fast at.
|
|
53
|
-
// The image / spritesheet resources may be connected to other database resources that provide
|
|
54
|
-
// caching and indexing of images.
|
|
55
|
-
// These would most likely be abstractions over data resources.
|
|
56
|
-
|
|
57
|
-
// Want to make an image interface to a flexible db interface.
|
|
58
|
-
// Serving and caching optimized images would be cool.
|
|
59
|
-
|
|
60
|
-
// Rather than relying on storing image versions in a DB (which would be cool, esp for distributed)
|
|
61
|
-
// we can store image versions + spritesheets on the local disk.
|
|
62
|
-
// Cache them in RAM as well.
|
|
63
|
-
// Storing them in GPU RAM would be very cool, especially if from there we can get versions with
|
|
64
|
-
// different operations done, or the output of operations.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// May need to change around a fair few references to make it workable.
|
|
68
|
-
// May need some more complicated logic to change it to the path for service.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
// need to see what type of image it is.
|
|
72
|
-
var mime_types = {
|
|
73
|
-
'acc': 'audio/acc',
|
|
74
|
-
'mp4': 'audio/mp4',
|
|
75
|
-
'mp3': 'audio/mpeg3',
|
|
76
|
-
'mpeg': 'audio/mpeg',
|
|
77
|
-
'ogg': 'audio/ogg',
|
|
78
|
-
'wav': 'audio/wav',
|
|
79
|
-
'webm': 'audio/webm'
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
var serve_audio_file_from_disk = fp(function(a, sig) {
|
|
83
|
-
|
|
84
|
-
var filePath, start_pos = 0, end_pos, response;
|
|
85
|
-
var using_byte_range = false;
|
|
86
|
-
|
|
87
|
-
if (a.l == 2) {
|
|
88
|
-
filePath = a[0];
|
|
89
|
-
response = a[1];
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
if (a.l == 3) {
|
|
93
|
-
filePath = a[0];
|
|
94
|
-
start_pos = a[1];
|
|
95
|
-
response = a[2];
|
|
96
|
-
using_byte_range = true;
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
if (a.l == 4) {
|
|
100
|
-
filePath = a[0];
|
|
101
|
-
start_pos = a[1];
|
|
102
|
-
end_pos = a[2]
|
|
103
|
-
response = a[3];
|
|
104
|
-
using_byte_range = true;
|
|
105
|
-
}
|
|
106
|
-
console.log('serve_audio_file_from_disk filePath ', filePath);
|
|
107
|
-
|
|
108
|
-
var extname = path.extname(filePath);
|
|
109
|
-
console.log('extname ' + extname);
|
|
110
|
-
|
|
111
|
-
var extension = extname.substr(1);
|
|
112
|
-
console.log('extension ' + extension);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
// then return the right MIME type for that extension.
|
|
116
|
-
|
|
117
|
-
// No, don't load the image file as a string.
|
|
118
|
-
|
|
119
|
-
// fs loadfile
|
|
120
|
-
// async, then serve it with the correct mime type, write to the response buffer.
|
|
121
|
-
|
|
122
|
-
// can this be streamed to the response buffer?
|
|
123
|
-
|
|
124
|
-
//console.log('filePath', filePath);
|
|
125
|
-
|
|
126
|
-
// This was served from Express in another app:
|
|
127
|
-
/*
|
|
128
|
-
Accept-Ranges:bytes
|
|
129
|
-
Cache-Control:public, max-age=0
|
|
130
|
-
Connection:keep-alive
|
|
131
|
-
Content-Length:7831024
|
|
132
|
-
Content-Range:bytes 0-7831023/7831024
|
|
133
|
-
Content-Type:audio/mpeg
|
|
134
|
-
Date:Fri, 02 May 2014 15:41:56 GMT
|
|
135
|
-
ETag:"7831024-1357308361000"
|
|
136
|
-
Last-Modified:Fri, 04 Jan 2013 14:06:01 GMT
|
|
137
|
-
X-Powered-By:Express
|
|
138
|
-
|
|
139
|
-
Currently served:
|
|
140
|
-
|
|
141
|
-
Accept-Ranges:bytes
|
|
142
|
-
Connection:keep-alive
|
|
143
|
-
Content-Length:7831024
|
|
144
|
-
Content-Range:bytes 0-7831024/7831024
|
|
145
|
-
Content-Type:audio/mpeg3
|
|
146
|
-
Date:Fri, 02 May 2014 18:01:47 GMT
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
*/
|
|
150
|
-
|
|
151
|
-
// It seems like this needs to present an etag to the client in order to work successfully.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
// May be better to use fs.read
|
|
157
|
-
// Serve this as a stream
|
|
158
|
-
// The client may not request it so quickly.
|
|
159
|
-
// However, may not want lots of access to the disk, so storing it in RAM while being served may make sense.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
/*
|
|
167
|
-
|
|
168
|
-
fs.open('./data/index.html', 'r', function(err, fd) {
|
|
169
|
-
if(err) throw err;
|
|
170
|
-
var str = new Buffer(3);
|
|
171
|
-
fs.read(fd, buf, 0, buf.length, null, function(err, bytesRead, buffer) {
|
|
172
|
-
if(err) throw err;
|
|
173
|
-
console.log(err, bytesRead, buffer);
|
|
174
|
-
fs.close(fd, ChaipaKwa, are you there?
|
|
175
|
-
function() {
|
|
176
|
-
console.log('Done');
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
*/
|
|
182
|
-
|
|
183
|
-
// This current code is probably more memory efficient.
|
|
184
|
-
// A greater efficiency could be gained (possibly) by storing the whole MP3 in memory while it is being served,
|
|
185
|
-
// and using the same buffer to send to more than 1 client.
|
|
186
|
-
|
|
187
|
-
fs.stat(filePath, function (err, stats) {
|
|
188
|
-
if (err) {
|
|
189
|
-
|
|
190
|
-
// The file probably was not found.
|
|
191
|
-
// Return a 404.
|
|
192
|
-
|
|
193
|
-
//response.status(404).send('Not found');
|
|
194
|
-
|
|
195
|
-
response.writeHead(404, {
|
|
196
|
-
|
|
197
|
-
"Content-Type": "text/plain"
|
|
198
|
-
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
response.write("404 Not Found\n");
|
|
202
|
-
|
|
203
|
-
response.end();
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
//throw err;
|
|
207
|
-
} else {
|
|
208
|
-
console.log('stats.size', stats.size);
|
|
209
|
-
|
|
210
|
-
//var l = stats.size;
|
|
211
|
-
|
|
212
|
-
var l;
|
|
213
|
-
|
|
214
|
-
var response_code = 200;
|
|
215
|
-
//if (start_pos > 0) {
|
|
216
|
-
// response_code = 206;
|
|
217
|
-
//}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
if (using_byte_range) {
|
|
221
|
-
response_code = 206;
|
|
222
|
-
if (start_pos) {
|
|
223
|
-
l = stats.size - start_pos ;
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
var rs_opts = {
|
|
230
|
-
'start': start_pos
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
if (end_pos) {
|
|
234
|
-
rs_opts.end = end_pos + 1;
|
|
235
|
-
l = end_pos;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (start_pos && end_pos) {
|
|
239
|
-
l = (end_pos + 1) - start_pos;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
} else {
|
|
244
|
-
l = stats.size;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
console.log('rs_opts', rs_opts);
|
|
249
|
-
var rs = fs.createReadStream(filePath, rs_opts);
|
|
250
|
-
|
|
251
|
-
rs.pause();
|
|
252
|
-
|
|
253
|
-
var c = 0;
|
|
254
|
-
|
|
255
|
-
// Response code 206 for partial content.
|
|
256
|
-
// Need to only do this if the browser is asking for partial content though.
|
|
257
|
-
|
|
258
|
-
// It seems like partial requests rely on etags.
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
// Needs to have a new etag for each file.
|
|
268
|
-
// Can use a hash of the file path.
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
var key = 'ready salted';
|
|
272
|
-
var hash = crypto.createHmac('sha1', key).update(filePath).digest('hex');
|
|
273
|
-
|
|
274
|
-
// Get last modified time from file.
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
// Need to handle 0-n ranges
|
|
278
|
-
// iOS starts the audio download with a 0-1 range.
|
|
279
|
-
|
|
280
|
-
// Seems that with the range request we should return 1 more byte.
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
// Don't necessarily do content-range.
|
|
285
|
-
|
|
286
|
-
var o_head = {'Content-Type': mime_types[extension],
|
|
287
|
-
//'Accept-Ranges': 'bytes',
|
|
288
|
-
|
|
289
|
-
'ETag': hash,
|
|
290
|
-
//'Last-Modified': 'Fri, 04 Jan 2013 14:06:01 GMT',
|
|
291
|
-
'Last-Modified': stats.mtime,
|
|
292
|
-
|
|
293
|
-
'Content-Length': l//,
|
|
294
|
-
|
|
295
|
-
// Not serving the whole content (fill amount - 1) gets the 206 response to work.
|
|
296
|
-
//'Content-Range': 'bytes ' + start_pos + '-' + (stats.size - 1) + '/' + stats.size
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
if (using_byte_range) {
|
|
300
|
-
o_head['Accept-Ranges'] = 'bytes';
|
|
301
|
-
start_pos = start_pos || 0;
|
|
302
|
-
|
|
303
|
-
if (end_pos) {
|
|
304
|
-
o_head['Content-Range'] = 'bytes ' + start_pos + '-' + (end_pos) + '/' + stats.size
|
|
305
|
-
} else {
|
|
306
|
-
o_head['Content-Range'] = 'bytes ' + start_pos + '-' + (stats.size - 1) + '/' + stats.size
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
console.log('o_head', o_head);
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
response.writeHead(response_code, o_head);
|
|
316
|
-
|
|
317
|
-
//response.pipe()
|
|
318
|
-
|
|
319
|
-
rs.pipe(response);
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
//rs.on('data', function(chunk) {
|
|
324
|
-
//c = c + chunk.length;
|
|
325
|
-
//console.log('got %d bytes of data', chunk.length);
|
|
326
|
-
//console.log('bytes read so far', c);
|
|
327
|
-
//});
|
|
328
|
-
rs.resume();
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
/*
|
|
336
|
-
fs.open(filePath, 'r', function(err, fd) {
|
|
337
|
-
if (err) {
|
|
338
|
-
throw err;
|
|
339
|
-
} else {
|
|
340
|
-
// Do we know how long the file is?
|
|
341
|
-
// We need to get the metadata first?
|
|
342
|
-
|
|
343
|
-
// Want to stream the file, sequentially.
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
}
|
|
349
|
-
})
|
|
350
|
-
*/
|
|
351
|
-
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
/*
|
|
359
|
-
|
|
360
|
-
fs.readFile(filePath, function(err, data) {
|
|
361
|
-
if (err) {
|
|
362
|
-
throw err;
|
|
363
|
-
} else {
|
|
364
|
-
console.log('cb readfile');
|
|
365
|
-
response.writeHead(200, {'Content-Type': mime_types[extension],
|
|
366
|
-
'Accept-Ranges': 'bytes',
|
|
367
|
-
'Content-Length': data.length,
|
|
368
|
-
'Content-Range': 'bytes 0-' + data.length + '/' + data.length
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
// Accept-Ranges:bytes
|
|
372
|
-
response.end(data, 'binary');
|
|
373
|
-
|
|
374
|
-
}
|
|
375
|
-
});
|
|
376
|
-
*/
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
//fs2.load_file_as_string(filePath, function (err, data) {
|
|
380
|
-
// if (err) {
|
|
381
|
-
// throw err;
|
|
382
|
-
// } else {
|
|
383
|
-
// //var servableJs = updateReferencesForServing(data);
|
|
384
|
-
// response.writeHead(200, {'Content-Type': mime_types[extension]});
|
|
385
|
-
// response.end(data);
|
|
386
|
-
// }
|
|
387
|
-
//});
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
var Site_Audio = Resource.extend({
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
'init': function(spec) {
|
|
395
|
-
super(spec);
|
|
396
|
-
//this._super(spec);
|
|
397
|
-
|
|
398
|
-
this.meta.set('custom_paths', new Data_Object({}));
|
|
399
|
-
// Those are custom file paths.
|
|
400
|
-
|
|
401
|
-
// could have a collection of directories, indexed by name, that get served.
|
|
402
|
-
|
|
403
|
-
// Index the collection by string value?
|
|
404
|
-
this.meta.set('served_directories', new Collection({'index_by': 'name'}));
|
|
405
|
-
},
|
|
406
|
-
'start': function(callback) {
|
|
407
|
-
// Load the tracks and find out their lengths.
|
|
408
|
-
|
|
409
|
-
console.log('Site Audio Resource Start');
|
|
410
|
-
|
|
411
|
-
var meta = this.meta;
|
|
412
|
-
var albums = meta.get('albums');
|
|
413
|
-
|
|
414
|
-
if (albums) {
|
|
415
|
-
each(albums, function(album) {
|
|
416
|
-
// We load the track length.
|
|
417
|
-
|
|
418
|
-
var tracks = album.tracks;
|
|
419
|
-
|
|
420
|
-
var l = tracks.length;
|
|
421
|
-
|
|
422
|
-
each(tracks, function(track, i_track) {
|
|
423
|
-
var track_base_name = (i_track + 1).toString();
|
|
424
|
-
if (track_base_name.length == 1) {
|
|
425
|
-
track_base_name = '0' + track_base_name;
|
|
426
|
-
}
|
|
427
|
-
var track_mp3_path = album.path + '/mp3/' + track_base_name + '.mp3';
|
|
428
|
-
//console.log('track_mp3_path', track_mp3_path);
|
|
429
|
-
// perhaps using call_multi
|
|
430
|
-
// load this up, get the track length
|
|
431
|
-
audio_metadata.from_file(track_mp3_path, function(err, metadata) {
|
|
432
|
-
if (err) {
|
|
433
|
-
throw err;
|
|
434
|
-
} else {
|
|
435
|
-
//console.log('metadata', metadata);
|
|
436
|
-
|
|
437
|
-
var ms_duration = metadata.ms_duration;
|
|
438
|
-
track.ms_duration = ms_duration;
|
|
439
|
-
|
|
440
|
-
l--;
|
|
441
|
-
|
|
442
|
-
if (l == 0) {
|
|
443
|
-
//console.log('tracks', tracks);
|
|
444
|
-
callback(null, true);
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
}
|
|
448
|
-
})
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
} else {
|
|
457
|
-
callback(null, true);
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
//console.log('albums', albums);
|
|
461
|
-
//console.log('tof(albums)', tof(albums));
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
},
|
|
466
|
-
'serve_directory': function(path) {
|
|
467
|
-
// Serves that directory, as any files given in that directory can be served from /js
|
|
468
|
-
var served_directories = this.meta.get('served_directories');
|
|
469
|
-
//console.log('served_directories ' + stringify(served_directories));
|
|
470
|
-
//served_directories.push(path);
|
|
471
|
-
served_directories.push({
|
|
472
|
-
'name': path
|
|
473
|
-
});
|
|
474
|
-
//console.log('served_directories ' + stringify(served_directories));
|
|
475
|
-
//console.log('path ' + path);
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
//throw 'stop';
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
},
|
|
483
|
-
|
|
484
|
-
// basically get requests, but can handle more than just get.
|
|
485
|
-
'process': function(req, res) {
|
|
486
|
-
console.log('Site_JavaScript processing');
|
|
487
|
-
var remoteAddress = req.connection.remoteAddress;
|
|
488
|
-
|
|
489
|
-
var custom_paths = this.meta.get('custom_paths');
|
|
490
|
-
|
|
491
|
-
var rurl = req.url;
|
|
492
|
-
|
|
493
|
-
var pool = this.meta.get('pool');
|
|
494
|
-
// should have a bunch of resources from the pool.
|
|
495
|
-
|
|
496
|
-
//var pool_resources = pool.resources();
|
|
497
|
-
//console.log('pool_resources ' + stringify(pool_resources));
|
|
498
|
-
|
|
499
|
-
//console.log('req', req);
|
|
500
|
-
|
|
501
|
-
var headers = req.headers;
|
|
502
|
-
console.log('headers', headers);
|
|
503
|
-
|
|
504
|
-
// bytes=6723861-
|
|
505
|
-
|
|
506
|
-
var range = headers.range;
|
|
507
|
-
console.log('range', range);
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
var byte_start_pos;
|
|
512
|
-
var byte_end_pos;
|
|
513
|
-
|
|
514
|
-
if (range) {
|
|
515
|
-
var s_range = range.split('=');
|
|
516
|
-
|
|
517
|
-
console.log('s_range', s_range);
|
|
518
|
-
|
|
519
|
-
var s_range_2 = s_range[1].split('-');
|
|
520
|
-
|
|
521
|
-
console.log('s_range_2', s_range_2);
|
|
522
|
-
|
|
523
|
-
if (s_range_2[0]) {
|
|
524
|
-
byte_start_pos = parseInt(s_range_2[0], 10);
|
|
525
|
-
}
|
|
526
|
-
if (s_range_2[1]) {
|
|
527
|
-
console.log('s_range_2[1]', s_range_2[1]);
|
|
528
|
-
byte_end_pos = parseInt(s_range_2[1], 10);
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
console.log('byte_start_pos', byte_start_pos);
|
|
533
|
-
console.log('byte_end_pos', byte_end_pos);
|
|
534
|
-
|
|
535
|
-
//if (range.substr())
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
// It looks like this will need to handle byte ranges too.
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
var wildcard_value = req.params.wildcard_value;
|
|
547
|
-
//console.log('wildcard_value', wildcard_value);
|
|
548
|
-
|
|
549
|
-
if (wildcard_value) {
|
|
550
|
-
var s_path = wildcard_value.split('/');
|
|
551
|
-
//console.log('s_path', s_path);
|
|
552
|
-
|
|
553
|
-
if (s_path.length == 3) {
|
|
554
|
-
var album_url_name = s_path[1];
|
|
555
|
-
var track_url_name = s_path[2];
|
|
556
|
-
|
|
557
|
-
var s_track_url_name = track_url_name.split('.');
|
|
558
|
-
var track_base_name = s_track_url_name[0];
|
|
559
|
-
var track_extension = s_track_url_name[1];
|
|
560
|
-
|
|
561
|
-
//console.log('track_base_name', track_base_name);
|
|
562
|
-
console.log('track_extension', track_extension);
|
|
563
|
-
|
|
564
|
-
// Need to match it up with the resource's albums
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
// Then load up and serve the file.
|
|
569
|
-
|
|
570
|
-
//var media_file_path =
|
|
571
|
-
|
|
572
|
-
// Likely to have been given specialised path info, in the metadata.
|
|
573
|
-
// Could have been provided with an album's path.
|
|
574
|
-
|
|
575
|
-
var meta = this.meta;
|
|
576
|
-
//console.log('meta', meta);
|
|
577
|
-
|
|
578
|
-
var albums = meta.get('albums');
|
|
579
|
-
//console.log('albums', albums);
|
|
580
|
-
|
|
581
|
-
var parsed_album_url_name = parseInt(album_url_name, 10);
|
|
582
|
-
var i_album = parsed_album_url_name - 1;
|
|
583
|
-
|
|
584
|
-
var album = albums[i_album];
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
//var tracks = album.get('tracks');
|
|
588
|
-
var tracks = album.tracks;
|
|
589
|
-
|
|
590
|
-
var i_track = parseInt(track_base_name, 10);
|
|
591
|
-
|
|
592
|
-
var s_track = i_track.toString();
|
|
593
|
-
if (s_track.length == 1) {
|
|
594
|
-
s_track = '0' + s_track;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
//console.log('s_track', s_track);
|
|
598
|
-
|
|
599
|
-
// Anyway, choose the right file (from mp3 dir for the moment)
|
|
600
|
-
|
|
601
|
-
// Must also be able to serve ogg paths.
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
var media_file_path = album.path + '/' + track_extension + '/' + s_track + '.' + track_extension;
|
|
606
|
-
console.log('media_file_path', media_file_path);
|
|
607
|
-
//console.log('res (response)', res);
|
|
608
|
-
|
|
609
|
-
// Also, we may not want to serve the whole audio file, but to begin the stream.
|
|
610
|
-
// Maybe we could keep the whole audio file in memory as a buffer.
|
|
611
|
-
// Don't want lots of large temporary buffers.
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
// Also need to take into account the byte range.
|
|
617
|
-
// The serve audio function could be changed so that it can serve a byterange, eg x-
|
|
618
|
-
// from x to the end
|
|
619
|
-
|
|
620
|
-
if (byte_end_pos) {
|
|
621
|
-
serve_audio_file_from_disk(media_file_path, byte_start_pos, byte_end_pos, res);
|
|
622
|
-
} else {
|
|
623
|
-
|
|
624
|
-
if (typeof byte_start_pos != 'undefined') {
|
|
625
|
-
serve_audio_file_from_disk(media_file_path, byte_start_pos, res);
|
|
626
|
-
} else {
|
|
627
|
-
serve_audio_file_from_disk(media_file_path, res);
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
//console.log('tracks', tracks);
|
|
638
|
-
|
|
639
|
-
//
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
//throw 'stop';
|
|
644
|
-
|
|
645
|
-
/*
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
var url_parts = url.parse(req.url, true);
|
|
651
|
-
//console.log('url_parts ' + stringify(url_parts));
|
|
652
|
-
var splitPath = url_parts.path.substr(1).split('/');
|
|
653
|
-
|
|
654
|
-
// Want the path inside the resource as well.
|
|
655
|
-
|
|
656
|
-
//console.log('resource site css splitPath ' + stringify(splitPath));
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
if (rurl.substr(0, 1) == '/') rurl = rurl.substr(1);
|
|
660
|
-
rurl = rurl.replace(/\./g, '☺');
|
|
661
|
-
//console.log('rurl ' + rurl);
|
|
662
|
-
|
|
663
|
-
var custom_response_entry = custom_paths.get(rurl);
|
|
664
|
-
//console.log('custom_response_entry ' + stringify(custom_response_entry));
|
|
665
|
-
|
|
666
|
-
// Should probably already be given the audio path, as well as the path within the audio path.
|
|
667
|
-
|
|
668
|
-
// Custom audio resource publishers could seem like a decent business.
|
|
669
|
-
|
|
670
|
-
if (custom_response_entry) {
|
|
671
|
-
var tcr = tof(custom_response_entry);
|
|
672
|
-
//console.log('tcr ' + tcr);
|
|
673
|
-
|
|
674
|
-
if (tcr == 'data_value') {
|
|
675
|
-
val = custom_response_entry.value();
|
|
676
|
-
//console.log('val ' + val);
|
|
677
|
-
|
|
678
|
-
var tval = tof(val);
|
|
679
|
-
|
|
680
|
-
if (tval == 'string') {
|
|
681
|
-
// then it should be a local file path, serve it.
|
|
682
|
-
serve_image_file_from_disk(val, res);
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
} else {
|
|
686
|
-
//console.log('splitPath', splitPath);
|
|
687
|
-
if (splitPath.length > 0) {
|
|
688
|
-
|
|
689
|
-
if (splitPath[0] == 'audio') {
|
|
690
|
-
|
|
691
|
-
if (splitPath.length > 1) {
|
|
692
|
-
if (splitPath.length == 2) {
|
|
693
|
-
var fileName = splitPath[1];
|
|
694
|
-
//console.log('url_parts.path ' + url_parts.path);
|
|
695
|
-
var filePath = url_parts.path.substr(1);
|
|
696
|
-
//console.log('module.uri ' + module.uri);
|
|
697
|
-
var val2 = path.dirname(module.uri);
|
|
698
|
-
console.log('val2 ' + val2);
|
|
699
|
-
|
|
700
|
-
var diskPath = '../../ws/audio/' + fileName;
|
|
701
|
-
|
|
702
|
-
serve_image_file_from_disk(diskPath, res);
|
|
703
|
-
|
|
704
|
-
} else {
|
|
705
|
-
if (splitPath.length > 2) {
|
|
706
|
-
|
|
707
|
-
// need to put the rest of it together...
|
|
708
|
-
|
|
709
|
-
var fileName = splitPath.slice(1, splitPath.length).join('/');
|
|
710
|
-
console.log('fileName', fileName);
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
var filePath = url_parts.path.substr(1);
|
|
714
|
-
//console.log('module.uri ' + module.uri);
|
|
715
|
-
var val2 = path.dirname(module.uri);
|
|
716
|
-
console.log('val2 ' + val2);
|
|
717
|
-
var diskPath = '../../ws/audio/' + fileName;
|
|
718
|
-
|
|
719
|
-
serve_image_file_from_disk(diskPath, res);
|
|
720
|
-
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
*/
|
|
729
|
-
}
|
|
730
|
-
});
|
|
731
|
-
|
|
732
|
-
//return Site_Audio;
|
|
733
|
-
//});
|
|
734
|
-
|
|
735
|
-
module.exports = Site_Audio;
|
|
1
|
+
|
|
2
|
+
// Need to maintain the various concepts applying here.
|
|
3
|
+
// Get them working or throwing errors / set to NYI if they are not so simple to fix / implement.
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
if (typeof define !== 'function') {
|
|
7
|
+
var define = require('amdefine')(module);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// I would like my own (fast) way of reading MP3 metadata.
|
|
11
|
+
|
|
12
|
+
// Had a problem seeking later parts of the track in Chrome. It would request a few seconds too little audio from the server.
|
|
13
|
+
// Seems like Chrome estimated positions for VBR audio are out.
|
|
14
|
+
// This bay be improved by changing to a constant bitrate (though that is less efficient encoding, generally)
|
|
15
|
+
|
|
16
|
+
// Other parts of the system are likely to be more important.
|
|
17
|
+
// It may be useful to have an interface to administer the site's audio.
|
|
18
|
+
|
|
19
|
+
// Browse tracks, and encode them into different bit rates and formats.
|
|
20
|
+
// Maintain copies of the tracks in different bit rates and formats.
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
define(['module', 'path', 'fs', 'url', '../../web/jsgui-html', 'os', 'http', 'url', './resource',
|
|
24
|
+
'../../web/jsgui-je-suis-xml', 'cookies', '../../fs/jsgui-node-fs2-core', '../../audio/jsgui-node-audio-metadata', 'crypto'],
|
|
25
|
+
|
|
26
|
+
function(module, path, fs, url, jsgui, os, http, libUrl,
|
|
27
|
+
Resource, JeSuisXML, Cookies, fs2, audio_metadata, crypto) {
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const path = require('path'), fs = require('fs'), url = require('url'), jsgui = require('../../web/jsgui-html'), os = require('os'), http = require('http'), libUrl = require('url'),
|
|
31
|
+
Resource = require('./resource'), Cookies = require('./cookies'), fs2 = require('../../fs/jsgui-node-fs2-core'), audio_metadata = require('../../audio/jsgui-node-audio-metadata'), crypto = require('crypto');
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
var stringify = jsgui.stringify, each = jsgui.eac, arrayify = jsgui.arrayify, tof = jsgui.tof;
|
|
35
|
+
var filter_map_by_regex = jsgui.filter_map_by_regex;
|
|
36
|
+
var Class = jsgui.Class, Data_Object = jsgui.Data_Object, Enhanced_Data_Object = jsgui.Enhanced_Data_Object;
|
|
37
|
+
var fp = jsgui.fp, is_defined = jsgui.is_defined;
|
|
38
|
+
var Collection = jsgui.Collection;
|
|
39
|
+
|
|
40
|
+
// This resource may have quite a lot of functionality put in to deal with
|
|
41
|
+
// seriving images in an optimized way.
|
|
42
|
+
// Want this so it also serves the developer in that images can be put in place
|
|
43
|
+
// and modified easily in an unoptimized way. Then they get put in place in an
|
|
44
|
+
// optimized way on request or automatically. Want to incorporate sprite sheet generation
|
|
45
|
+
// here. This should handle requests for individual images, but it's also possible sprites
|
|
46
|
+
// could be requested. If that's the case, maybe it should pass it onto a sprite resource.
|
|
47
|
+
// It would be really good to have practically instant sprite generation, using C / C++
|
|
48
|
+
// and maybe OpenCL.
|
|
49
|
+
// It could be requests to a sprite resource that returns a predefined selection of
|
|
50
|
+
// assets.
|
|
51
|
+
// The image resource may also be called upon to return rescaled versions.
|
|
52
|
+
// That's something that OpenCL would also be very fast at.
|
|
53
|
+
// The image / spritesheet resources may be connected to other database resources that provide
|
|
54
|
+
// caching and indexing of images.
|
|
55
|
+
// These would most likely be abstractions over data resources.
|
|
56
|
+
|
|
57
|
+
// Want to make an image interface to a flexible db interface.
|
|
58
|
+
// Serving and caching optimized images would be cool.
|
|
59
|
+
|
|
60
|
+
// Rather than relying on storing image versions in a DB (which would be cool, esp for distributed)
|
|
61
|
+
// we can store image versions + spritesheets on the local disk.
|
|
62
|
+
// Cache them in RAM as well.
|
|
63
|
+
// Storing them in GPU RAM would be very cool, especially if from there we can get versions with
|
|
64
|
+
// different operations done, or the output of operations.
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
// May need to change around a fair few references to make it workable.
|
|
68
|
+
// May need some more complicated logic to change it to the path for service.
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
// need to see what type of image it is.
|
|
72
|
+
var mime_types = {
|
|
73
|
+
'acc': 'audio/acc',
|
|
74
|
+
'mp4': 'audio/mp4',
|
|
75
|
+
'mp3': 'audio/mpeg3',
|
|
76
|
+
'mpeg': 'audio/mpeg',
|
|
77
|
+
'ogg': 'audio/ogg',
|
|
78
|
+
'wav': 'audio/wav',
|
|
79
|
+
'webm': 'audio/webm'
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
var serve_audio_file_from_disk = fp(function(a, sig) {
|
|
83
|
+
|
|
84
|
+
var filePath, start_pos = 0, end_pos, response;
|
|
85
|
+
var using_byte_range = false;
|
|
86
|
+
|
|
87
|
+
if (a.l == 2) {
|
|
88
|
+
filePath = a[0];
|
|
89
|
+
response = a[1];
|
|
90
|
+
|
|
91
|
+
}
|
|
92
|
+
if (a.l == 3) {
|
|
93
|
+
filePath = a[0];
|
|
94
|
+
start_pos = a[1];
|
|
95
|
+
response = a[2];
|
|
96
|
+
using_byte_range = true;
|
|
97
|
+
|
|
98
|
+
}
|
|
99
|
+
if (a.l == 4) {
|
|
100
|
+
filePath = a[0];
|
|
101
|
+
start_pos = a[1];
|
|
102
|
+
end_pos = a[2]
|
|
103
|
+
response = a[3];
|
|
104
|
+
using_byte_range = true;
|
|
105
|
+
}
|
|
106
|
+
console.log('serve_audio_file_from_disk filePath ', filePath);
|
|
107
|
+
|
|
108
|
+
var extname = path.extname(filePath);
|
|
109
|
+
console.log('extname ' + extname);
|
|
110
|
+
|
|
111
|
+
var extension = extname.substr(1);
|
|
112
|
+
console.log('extension ' + extension);
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
// then return the right MIME type for that extension.
|
|
116
|
+
|
|
117
|
+
// No, don't load the image file as a string.
|
|
118
|
+
|
|
119
|
+
// fs loadfile
|
|
120
|
+
// async, then serve it with the correct mime type, write to the response buffer.
|
|
121
|
+
|
|
122
|
+
// can this be streamed to the response buffer?
|
|
123
|
+
|
|
124
|
+
//console.log('filePath', filePath);
|
|
125
|
+
|
|
126
|
+
// This was served from Express in another app:
|
|
127
|
+
/*
|
|
128
|
+
Accept-Ranges:bytes
|
|
129
|
+
Cache-Control:public, max-age=0
|
|
130
|
+
Connection:keep-alive
|
|
131
|
+
Content-Length:7831024
|
|
132
|
+
Content-Range:bytes 0-7831023/7831024
|
|
133
|
+
Content-Type:audio/mpeg
|
|
134
|
+
Date:Fri, 02 May 2014 15:41:56 GMT
|
|
135
|
+
ETag:"7831024-1357308361000"
|
|
136
|
+
Last-Modified:Fri, 04 Jan 2013 14:06:01 GMT
|
|
137
|
+
X-Powered-By:Express
|
|
138
|
+
|
|
139
|
+
Currently served:
|
|
140
|
+
|
|
141
|
+
Accept-Ranges:bytes
|
|
142
|
+
Connection:keep-alive
|
|
143
|
+
Content-Length:7831024
|
|
144
|
+
Content-Range:bytes 0-7831024/7831024
|
|
145
|
+
Content-Type:audio/mpeg3
|
|
146
|
+
Date:Fri, 02 May 2014 18:01:47 GMT
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
*/
|
|
150
|
+
|
|
151
|
+
// It seems like this needs to present an etag to the client in order to work successfully.
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
// May be better to use fs.read
|
|
157
|
+
// Serve this as a stream
|
|
158
|
+
// The client may not request it so quickly.
|
|
159
|
+
// However, may not want lots of access to the disk, so storing it in RAM while being served may make sense.
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
//
|
|
165
|
+
|
|
166
|
+
/*
|
|
167
|
+
|
|
168
|
+
fs.open('./data/index.html', 'r', function(err, fd) {
|
|
169
|
+
if(err) throw err;
|
|
170
|
+
var str = new Buffer(3);
|
|
171
|
+
fs.read(fd, buf, 0, buf.length, null, function(err, bytesRead, buffer) {
|
|
172
|
+
if(err) throw err;
|
|
173
|
+
console.log(err, bytesRead, buffer);
|
|
174
|
+
fs.close(fd, ChaipaKwa, are you there?
|
|
175
|
+
function() {
|
|
176
|
+
console.log('Done');
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
*/
|
|
182
|
+
|
|
183
|
+
// This current code is probably more memory efficient.
|
|
184
|
+
// A greater efficiency could be gained (possibly) by storing the whole MP3 in memory while it is being served,
|
|
185
|
+
// and using the same buffer to send to more than 1 client.
|
|
186
|
+
|
|
187
|
+
fs.stat(filePath, function (err, stats) {
|
|
188
|
+
if (err) {
|
|
189
|
+
|
|
190
|
+
// The file probably was not found.
|
|
191
|
+
// Return a 404.
|
|
192
|
+
|
|
193
|
+
//response.status(404).send('Not found');
|
|
194
|
+
|
|
195
|
+
response.writeHead(404, {
|
|
196
|
+
|
|
197
|
+
"Content-Type": "text/plain"
|
|
198
|
+
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
response.write("404 Not Found\n");
|
|
202
|
+
|
|
203
|
+
response.end();
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
//throw err;
|
|
207
|
+
} else {
|
|
208
|
+
console.log('stats.size', stats.size);
|
|
209
|
+
|
|
210
|
+
//var l = stats.size;
|
|
211
|
+
|
|
212
|
+
var l;
|
|
213
|
+
|
|
214
|
+
var response_code = 200;
|
|
215
|
+
//if (start_pos > 0) {
|
|
216
|
+
// response_code = 206;
|
|
217
|
+
//}
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
if (using_byte_range) {
|
|
221
|
+
response_code = 206;
|
|
222
|
+
if (start_pos) {
|
|
223
|
+
l = stats.size - start_pos ;
|
|
224
|
+
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
var rs_opts = {
|
|
230
|
+
'start': start_pos
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
if (end_pos) {
|
|
234
|
+
rs_opts.end = end_pos + 1;
|
|
235
|
+
l = end_pos;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (start_pos && end_pos) {
|
|
239
|
+
l = (end_pos + 1) - start_pos;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
} else {
|
|
244
|
+
l = stats.size;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
console.log('rs_opts', rs_opts);
|
|
249
|
+
var rs = fs.createReadStream(filePath, rs_opts);
|
|
250
|
+
|
|
251
|
+
rs.pause();
|
|
252
|
+
|
|
253
|
+
var c = 0;
|
|
254
|
+
|
|
255
|
+
// Response code 206 for partial content.
|
|
256
|
+
// Need to only do this if the browser is asking for partial content though.
|
|
257
|
+
|
|
258
|
+
// It seems like partial requests rely on etags.
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
// Needs to have a new etag for each file.
|
|
268
|
+
// Can use a hash of the file path.
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
var key = 'ready salted';
|
|
272
|
+
var hash = crypto.createHmac('sha1', key).update(filePath).digest('hex');
|
|
273
|
+
|
|
274
|
+
// Get last modified time from file.
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
// Need to handle 0-n ranges
|
|
278
|
+
// iOS starts the audio download with a 0-1 range.
|
|
279
|
+
|
|
280
|
+
// Seems that with the range request we should return 1 more byte.
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
// Don't necessarily do content-range.
|
|
285
|
+
|
|
286
|
+
var o_head = {'Content-Type': mime_types[extension],
|
|
287
|
+
//'Accept-Ranges': 'bytes',
|
|
288
|
+
|
|
289
|
+
'ETag': hash,
|
|
290
|
+
//'Last-Modified': 'Fri, 04 Jan 2013 14:06:01 GMT',
|
|
291
|
+
'Last-Modified': stats.mtime,
|
|
292
|
+
|
|
293
|
+
'Content-Length': l//,
|
|
294
|
+
|
|
295
|
+
// Not serving the whole content (fill amount - 1) gets the 206 response to work.
|
|
296
|
+
//'Content-Range': 'bytes ' + start_pos + '-' + (stats.size - 1) + '/' + stats.size
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
if (using_byte_range) {
|
|
300
|
+
o_head['Accept-Ranges'] = 'bytes';
|
|
301
|
+
start_pos = start_pos || 0;
|
|
302
|
+
|
|
303
|
+
if (end_pos) {
|
|
304
|
+
o_head['Content-Range'] = 'bytes ' + start_pos + '-' + (end_pos) + '/' + stats.size
|
|
305
|
+
} else {
|
|
306
|
+
o_head['Content-Range'] = 'bytes ' + start_pos + '-' + (stats.size - 1) + '/' + stats.size
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
console.log('o_head', o_head);
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
response.writeHead(response_code, o_head);
|
|
316
|
+
|
|
317
|
+
//response.pipe()
|
|
318
|
+
|
|
319
|
+
rs.pipe(response);
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
//rs.on('data', function(chunk) {
|
|
324
|
+
//c = c + chunk.length;
|
|
325
|
+
//console.log('got %d bytes of data', chunk.length);
|
|
326
|
+
//console.log('bytes read so far', c);
|
|
327
|
+
//});
|
|
328
|
+
rs.resume();
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
/*
|
|
336
|
+
fs.open(filePath, 'r', function(err, fd) {
|
|
337
|
+
if (err) {
|
|
338
|
+
throw err;
|
|
339
|
+
} else {
|
|
340
|
+
// Do we know how long the file is?
|
|
341
|
+
// We need to get the metadata first?
|
|
342
|
+
|
|
343
|
+
// Want to stream the file, sequentially.
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
}
|
|
349
|
+
})
|
|
350
|
+
*/
|
|
351
|
+
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
/*
|
|
359
|
+
|
|
360
|
+
fs.readFile(filePath, function(err, data) {
|
|
361
|
+
if (err) {
|
|
362
|
+
throw err;
|
|
363
|
+
} else {
|
|
364
|
+
console.log('cb readfile');
|
|
365
|
+
response.writeHead(200, {'Content-Type': mime_types[extension],
|
|
366
|
+
'Accept-Ranges': 'bytes',
|
|
367
|
+
'Content-Length': data.length,
|
|
368
|
+
'Content-Range': 'bytes 0-' + data.length + '/' + data.length
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// Accept-Ranges:bytes
|
|
372
|
+
response.end(data, 'binary');
|
|
373
|
+
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
*/
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
//fs2.load_file_as_string(filePath, function (err, data) {
|
|
380
|
+
// if (err) {
|
|
381
|
+
// throw err;
|
|
382
|
+
// } else {
|
|
383
|
+
// //var servableJs = updateReferencesForServing(data);
|
|
384
|
+
// response.writeHead(200, {'Content-Type': mime_types[extension]});
|
|
385
|
+
// response.end(data);
|
|
386
|
+
// }
|
|
387
|
+
//});
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
var Site_Audio = Resource.extend({
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
'init': function(spec) {
|
|
395
|
+
super(spec);
|
|
396
|
+
//this._super(spec);
|
|
397
|
+
|
|
398
|
+
this.meta.set('custom_paths', new Data_Object({}));
|
|
399
|
+
// Those are custom file paths.
|
|
400
|
+
|
|
401
|
+
// could have a collection of directories, indexed by name, that get served.
|
|
402
|
+
|
|
403
|
+
// Index the collection by string value?
|
|
404
|
+
this.meta.set('served_directories', new Collection({'index_by': 'name'}));
|
|
405
|
+
},
|
|
406
|
+
'start': function(callback) {
|
|
407
|
+
// Load the tracks and find out their lengths.
|
|
408
|
+
|
|
409
|
+
console.log('Site Audio Resource Start');
|
|
410
|
+
|
|
411
|
+
var meta = this.meta;
|
|
412
|
+
var albums = meta.get('albums');
|
|
413
|
+
|
|
414
|
+
if (albums) {
|
|
415
|
+
each(albums, function(album) {
|
|
416
|
+
// We load the track length.
|
|
417
|
+
|
|
418
|
+
var tracks = album.tracks;
|
|
419
|
+
|
|
420
|
+
var l = tracks.length;
|
|
421
|
+
|
|
422
|
+
each(tracks, function(track, i_track) {
|
|
423
|
+
var track_base_name = (i_track + 1).toString();
|
|
424
|
+
if (track_base_name.length == 1) {
|
|
425
|
+
track_base_name = '0' + track_base_name;
|
|
426
|
+
}
|
|
427
|
+
var track_mp3_path = album.path + '/mp3/' + track_base_name + '.mp3';
|
|
428
|
+
//console.log('track_mp3_path', track_mp3_path);
|
|
429
|
+
// perhaps using call_multi
|
|
430
|
+
// load this up, get the track length
|
|
431
|
+
audio_metadata.from_file(track_mp3_path, function(err, metadata) {
|
|
432
|
+
if (err) {
|
|
433
|
+
throw err;
|
|
434
|
+
} else {
|
|
435
|
+
//console.log('metadata', metadata);
|
|
436
|
+
|
|
437
|
+
var ms_duration = metadata.ms_duration;
|
|
438
|
+
track.ms_duration = ms_duration;
|
|
439
|
+
|
|
440
|
+
l--;
|
|
441
|
+
|
|
442
|
+
if (l == 0) {
|
|
443
|
+
//console.log('tracks', tracks);
|
|
444
|
+
callback(null, true);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
}
|
|
448
|
+
})
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
} else {
|
|
457
|
+
callback(null, true);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
//console.log('albums', albums);
|
|
461
|
+
//console.log('tof(albums)', tof(albums));
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
},
|
|
466
|
+
'serve_directory': function(path) {
|
|
467
|
+
// Serves that directory, as any files given in that directory can be served from /js
|
|
468
|
+
var served_directories = this.meta.get('served_directories');
|
|
469
|
+
//console.log('served_directories ' + stringify(served_directories));
|
|
470
|
+
//served_directories.push(path);
|
|
471
|
+
served_directories.push({
|
|
472
|
+
'name': path
|
|
473
|
+
});
|
|
474
|
+
//console.log('served_directories ' + stringify(served_directories));
|
|
475
|
+
//console.log('path ' + path);
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
//throw 'stop';
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
},
|
|
483
|
+
|
|
484
|
+
// basically get requests, but can handle more than just get.
|
|
485
|
+
'process': function(req, res) {
|
|
486
|
+
console.log('Site_JavaScript processing');
|
|
487
|
+
var remoteAddress = req.connection.remoteAddress;
|
|
488
|
+
|
|
489
|
+
var custom_paths = this.meta.get('custom_paths');
|
|
490
|
+
|
|
491
|
+
var rurl = req.url;
|
|
492
|
+
|
|
493
|
+
var pool = this.meta.get('pool');
|
|
494
|
+
// should have a bunch of resources from the pool.
|
|
495
|
+
|
|
496
|
+
//var pool_resources = pool.resources();
|
|
497
|
+
//console.log('pool_resources ' + stringify(pool_resources));
|
|
498
|
+
|
|
499
|
+
//console.log('req', req);
|
|
500
|
+
|
|
501
|
+
var headers = req.headers;
|
|
502
|
+
console.log('headers', headers);
|
|
503
|
+
|
|
504
|
+
// bytes=6723861-
|
|
505
|
+
|
|
506
|
+
var range = headers.range;
|
|
507
|
+
console.log('range', range);
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
var byte_start_pos;
|
|
512
|
+
var byte_end_pos;
|
|
513
|
+
|
|
514
|
+
if (range) {
|
|
515
|
+
var s_range = range.split('=');
|
|
516
|
+
|
|
517
|
+
console.log('s_range', s_range);
|
|
518
|
+
|
|
519
|
+
var s_range_2 = s_range[1].split('-');
|
|
520
|
+
|
|
521
|
+
console.log('s_range_2', s_range_2);
|
|
522
|
+
|
|
523
|
+
if (s_range_2[0]) {
|
|
524
|
+
byte_start_pos = parseInt(s_range_2[0], 10);
|
|
525
|
+
}
|
|
526
|
+
if (s_range_2[1]) {
|
|
527
|
+
console.log('s_range_2[1]', s_range_2[1]);
|
|
528
|
+
byte_end_pos = parseInt(s_range_2[1], 10);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
console.log('byte_start_pos', byte_start_pos);
|
|
533
|
+
console.log('byte_end_pos', byte_end_pos);
|
|
534
|
+
|
|
535
|
+
//if (range.substr())
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
// It looks like this will need to handle byte ranges too.
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
var wildcard_value = req.params.wildcard_value;
|
|
547
|
+
//console.log('wildcard_value', wildcard_value);
|
|
548
|
+
|
|
549
|
+
if (wildcard_value) {
|
|
550
|
+
var s_path = wildcard_value.split('/');
|
|
551
|
+
//console.log('s_path', s_path);
|
|
552
|
+
|
|
553
|
+
if (s_path.length == 3) {
|
|
554
|
+
var album_url_name = s_path[1];
|
|
555
|
+
var track_url_name = s_path[2];
|
|
556
|
+
|
|
557
|
+
var s_track_url_name = track_url_name.split('.');
|
|
558
|
+
var track_base_name = s_track_url_name[0];
|
|
559
|
+
var track_extension = s_track_url_name[1];
|
|
560
|
+
|
|
561
|
+
//console.log('track_base_name', track_base_name);
|
|
562
|
+
console.log('track_extension', track_extension);
|
|
563
|
+
|
|
564
|
+
// Need to match it up with the resource's albums
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
// Then load up and serve the file.
|
|
569
|
+
|
|
570
|
+
//var media_file_path =
|
|
571
|
+
|
|
572
|
+
// Likely to have been given specialised path info, in the metadata.
|
|
573
|
+
// Could have been provided with an album's path.
|
|
574
|
+
|
|
575
|
+
var meta = this.meta;
|
|
576
|
+
//console.log('meta', meta);
|
|
577
|
+
|
|
578
|
+
var albums = meta.get('albums');
|
|
579
|
+
//console.log('albums', albums);
|
|
580
|
+
|
|
581
|
+
var parsed_album_url_name = parseInt(album_url_name, 10);
|
|
582
|
+
var i_album = parsed_album_url_name - 1;
|
|
583
|
+
|
|
584
|
+
var album = albums[i_album];
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
//var tracks = album.get('tracks');
|
|
588
|
+
var tracks = album.tracks;
|
|
589
|
+
|
|
590
|
+
var i_track = parseInt(track_base_name, 10);
|
|
591
|
+
|
|
592
|
+
var s_track = i_track.toString();
|
|
593
|
+
if (s_track.length == 1) {
|
|
594
|
+
s_track = '0' + s_track;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
//console.log('s_track', s_track);
|
|
598
|
+
|
|
599
|
+
// Anyway, choose the right file (from mp3 dir for the moment)
|
|
600
|
+
|
|
601
|
+
// Must also be able to serve ogg paths.
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
var media_file_path = album.path + '/' + track_extension + '/' + s_track + '.' + track_extension;
|
|
606
|
+
console.log('media_file_path', media_file_path);
|
|
607
|
+
//console.log('res (response)', res);
|
|
608
|
+
|
|
609
|
+
// Also, we may not want to serve the whole audio file, but to begin the stream.
|
|
610
|
+
// Maybe we could keep the whole audio file in memory as a buffer.
|
|
611
|
+
// Don't want lots of large temporary buffers.
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
// Also need to take into account the byte range.
|
|
617
|
+
// The serve audio function could be changed so that it can serve a byterange, eg x-
|
|
618
|
+
// from x to the end
|
|
619
|
+
|
|
620
|
+
if (byte_end_pos) {
|
|
621
|
+
serve_audio_file_from_disk(media_file_path, byte_start_pos, byte_end_pos, res);
|
|
622
|
+
} else {
|
|
623
|
+
|
|
624
|
+
if (typeof byte_start_pos != 'undefined') {
|
|
625
|
+
serve_audio_file_from_disk(media_file_path, byte_start_pos, res);
|
|
626
|
+
} else {
|
|
627
|
+
serve_audio_file_from_disk(media_file_path, res);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
//console.log('tracks', tracks);
|
|
638
|
+
|
|
639
|
+
//
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
//throw 'stop';
|
|
644
|
+
|
|
645
|
+
/*
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
var url_parts = url.parse(req.url, true);
|
|
651
|
+
//console.log('url_parts ' + stringify(url_parts));
|
|
652
|
+
var splitPath = url_parts.path.substr(1).split('/');
|
|
653
|
+
|
|
654
|
+
// Want the path inside the resource as well.
|
|
655
|
+
|
|
656
|
+
//console.log('resource site css splitPath ' + stringify(splitPath));
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
if (rurl.substr(0, 1) == '/') rurl = rurl.substr(1);
|
|
660
|
+
rurl = rurl.replace(/\./g, '☺');
|
|
661
|
+
//console.log('rurl ' + rurl);
|
|
662
|
+
|
|
663
|
+
var custom_response_entry = custom_paths.get(rurl);
|
|
664
|
+
//console.log('custom_response_entry ' + stringify(custom_response_entry));
|
|
665
|
+
|
|
666
|
+
// Should probably already be given the audio path, as well as the path within the audio path.
|
|
667
|
+
|
|
668
|
+
// Custom audio resource publishers could seem like a decent business.
|
|
669
|
+
|
|
670
|
+
if (custom_response_entry) {
|
|
671
|
+
var tcr = tof(custom_response_entry);
|
|
672
|
+
//console.log('tcr ' + tcr);
|
|
673
|
+
|
|
674
|
+
if (tcr == 'data_value') {
|
|
675
|
+
val = custom_response_entry.value();
|
|
676
|
+
//console.log('val ' + val);
|
|
677
|
+
|
|
678
|
+
var tval = tof(val);
|
|
679
|
+
|
|
680
|
+
if (tval == 'string') {
|
|
681
|
+
// then it should be a local file path, serve it.
|
|
682
|
+
serve_image_file_from_disk(val, res);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
} else {
|
|
686
|
+
//console.log('splitPath', splitPath);
|
|
687
|
+
if (splitPath.length > 0) {
|
|
688
|
+
|
|
689
|
+
if (splitPath[0] == 'audio') {
|
|
690
|
+
|
|
691
|
+
if (splitPath.length > 1) {
|
|
692
|
+
if (splitPath.length == 2) {
|
|
693
|
+
var fileName = splitPath[1];
|
|
694
|
+
//console.log('url_parts.path ' + url_parts.path);
|
|
695
|
+
var filePath = url_parts.path.substr(1);
|
|
696
|
+
//console.log('module.uri ' + module.uri);
|
|
697
|
+
var val2 = path.dirname(module.uri);
|
|
698
|
+
console.log('val2 ' + val2);
|
|
699
|
+
|
|
700
|
+
var diskPath = '../../ws/audio/' + fileName;
|
|
701
|
+
|
|
702
|
+
serve_image_file_from_disk(diskPath, res);
|
|
703
|
+
|
|
704
|
+
} else {
|
|
705
|
+
if (splitPath.length > 2) {
|
|
706
|
+
|
|
707
|
+
// need to put the rest of it together...
|
|
708
|
+
|
|
709
|
+
var fileName = splitPath.slice(1, splitPath.length).join('/');
|
|
710
|
+
console.log('fileName', fileName);
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
var filePath = url_parts.path.substr(1);
|
|
714
|
+
//console.log('module.uri ' + module.uri);
|
|
715
|
+
var val2 = path.dirname(module.uri);
|
|
716
|
+
console.log('val2 ' + val2);
|
|
717
|
+
var diskPath = '../../ws/audio/' + fileName;
|
|
718
|
+
|
|
719
|
+
serve_image_file_from_disk(diskPath, res);
|
|
720
|
+
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
*/
|
|
729
|
+
}
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
//return Site_Audio;
|
|
733
|
+
//});
|
|
734
|
+
|
|
735
|
+
module.exports = Site_Audio;
|