From e12fbfa2aaabd3f8c90ff20ce5281c4708c2f571 Mon Sep 17 00:00:00 2001 From: Simon Hardt Date: Mon, 31 May 2021 23:19:02 +0200 Subject: [PATCH] add: Task Status --- Modules/Server/src/api.cpp | 70 ++- Modules/Server/src/api.hpp | 2 + Modules/Server/src/tables.hpp | 7 +- Modules/Test.http | 8 +- Modules/Website/elm.json | 3 +- Modules/Website/index.html | 874 ++++++++++++++++++++++++++++--- Modules/Website/src/Files.elm | 6 +- Modules/Website/src/Globals.elm | 21 + Modules/Website/src/Main.elm | 32 +- Modules/Website/src/MainPage.elm | 112 ++-- Modules/Website/src/Route.elm | 2 + Modules/Website/src/Status.elm | 232 ++++++++ 12 files changed, 1222 insertions(+), 147 deletions(-) create mode 100644 Modules/Website/src/Globals.elm create mode 100644 Modules/Website/src/Status.elm diff --git a/Modules/Server/src/api.cpp b/Modules/Server/src/api.cpp index 4eda569..283d9e4 100644 --- a/Modules/Server/src/api.cpp +++ b/Modules/Server/src/api.cpp @@ -8,6 +8,8 @@ #include #include +#include + #include #include #include @@ -30,7 +32,9 @@ void Api::RegisterServerHandles(httplib::Server& server) server.Get("/api/files", std::bind_front(&Api::files, this)); - server.Get("/api/file/([A-E0-9]+)", std::bind_front(&Api::file, this)); + server.Get("/api/file/([A-F0-9]+)", std::bind_front(&Api::file, this)); + + server.Get("/api/status/([A-F0-9]+)", std::bind_front(&Api::status, this)); server.Options("/(.*)", [this](httplib::Request const& rq, httplib::Response& rp) {}); @@ -205,6 +209,70 @@ void Api::file(const httplib::Request& rq, httplib::Response& rs) rs.set_content("File Not Found", "text/plain"); } } + +void Api::status(httplib::Request const& rq, httplib::Response& rs) +{ + if (!rq.matches[1].matched) + { + rs.status = 404; + rs.set_content("Parameter error", "text/plain"); + return; + } + + std::string hash = rq.matches[1]; + + boost::optional data; + + sql << "SELECT * FROM Tasks WHERE hash=:hash", soci::use(hash, "hash"), soci::into(data); + + /*soci::rowset data = (sql.prepare << "SELECT * " + "FROM Tasks " + "WHERE hash=:hash ", + soci::use(hash, "hash"));*/ + json response; + + auto& status = response["status"]; + + if (data) + { + auto const& el = data.value(); + + status["name"] = fmt::format("Task {}", el.hash); + status["source"] = el.url; + status["status"] = magic_enum::enum_name(el.status); + status["status_id"] = static_cast(el.status); + status["timestamp"] = el.timestamp; + status["task_file_type"] = + fmt::format("{}: {}", el.audio_only ? "Audio" : "Video", el.format.value_or("Auto")); + status["id"] = el.hash; + + if (el.status == FileStatus::COMPLETE) + { + boost::optional file_data; + sql << "SELECT * FROM Files WHERE hash=:hash", soci::use(hash, "hash"), + soci::into(file_data); + + if (file_data) + { + auto const& file = file_data.value(); + + auto& file_status = status["file"]; + + file_status["timestamp"] = file.timestamp; + file_status["name"] = file.filename; + file_status["format"] = + fmt::format("{}: {}", el.audio_only ? "Audio" : "Video", file.format); + } + } + + } else + { + rs.status = 404; + response["error"] = fmt::format("File '{}' not found!", hash); + } + rs.set_content(response.dump(), "application/json"); +} + void Api::set_cross_headers(httplib::Response& rs) { rs.set_header("Access-Control-Allow-Origin", "*"); diff --git a/Modules/Server/src/api.hpp b/Modules/Server/src/api.hpp index ab2e242..1663546 100644 --- a/Modules/Server/src/api.hpp +++ b/Modules/Server/src/api.hpp @@ -18,6 +18,8 @@ public: // Endpoints void files(httplib::Request const& rq, httplib::Response& rs); void file(httplib::Request const& rq, httplib::Response& rs); + void status(httplib::Request const& rq, httplib::Response& rs); + void set_cross_headers(httplib::Response& rs); }; \ No newline at end of file diff --git a/Modules/Server/src/tables.hpp b/Modules/Server/src/tables.hpp index bc682b2..598ca33 100644 --- a/Modules/Server/src/tables.hpp +++ b/Modules/Server/src/tables.hpp @@ -97,10 +97,11 @@ struct Task inline std::ostream& operator<<(std::ostream& os, Task const& file) { - return os << fmt::format("Task [{}][{}] url: {} ", - file.id, + return os << fmt::format("Task [{}] url: {} Audio: {} Format: {}", magic_enum::enum_name(file.status), - file.url); + file.url, + file.audio_only, + file.format.value_or("Auto")); } namespace soci diff --git a/Modules/Test.http b/Modules/Test.http index 0d56b2d..f7a33c8 100644 --- a/Modules/Test.http +++ b/Modules/Test.http @@ -2,9 +2,13 @@ POST 127.0.0.1:80/api/add Content-Type: text/json { -"url": "https://www.youtube.com/watch?v=ZWIwLMpgcbI" +"url": "https://www.youtube.com/watch?v=izc1XLbU5Vk" } ### -GET 127.0.0.1:80/api/queue \ No newline at end of file +GET 127.0.0.1:80/api/files + + +### +GET 127.0.0.1:80/api/status/C3F953D7 \ No newline at end of file diff --git a/Modules/Website/elm.json b/Modules/Website/elm.json index c6885f9..2f5cd11 100644 --- a/Modules/Website/elm.json +++ b/Modules/Website/elm.json @@ -7,8 +7,10 @@ "dependencies": { "direct": { "NoRedInk/elm-json-decode-pipeline": "1.0.0", + "andrewMacmurray/elm-delay": "4.0.0", "elm/browser": "1.0.2", "elm/core": "1.0.5", + "elm/file": "1.0.5", "elm/html": "1.0.0", "elm/http": "2.0.0", "elm/json": "1.1.3", @@ -17,7 +19,6 @@ }, "indirect": { "elm/bytes": "1.0.8", - "elm/file": "1.0.5", "elm/time": "1.0.0", "elm/virtual-dom": "1.0.2" } diff --git a/Modules/Website/index.html b/Modules/Website/index.html index e908401..5ec062a 100644 --- a/Modules/Website/index.html +++ b/Modules/Website/index.html @@ -4598,7 +4598,185 @@ function _Url_percentDecode(string) { return $elm$core$Maybe$Nothing; } -}var $author$project$Main$LinkClicked = function (a) { +} + + +// DECODER + +var _File_decoder = _Json_decodePrim(function(value) { + // NOTE: checks if `File` exists in case this is run on node + return (typeof File !== 'undefined' && value instanceof File) + ? $elm$core$Result$Ok(value) + : _Json_expecting('a FILE', value); +}); + + +// METADATA + +function _File_name(file) { return file.name; } +function _File_mime(file) { return file.type; } +function _File_size(file) { return file.size; } + +function _File_lastModified(file) +{ + return $elm$time$Time$millisToPosix(file.lastModified); +} + + +// DOWNLOAD + +var _File_downloadNode; + +function _File_getDownloadNode() +{ + return _File_downloadNode || (_File_downloadNode = document.createElement('a')); +} + +var _File_download = F3(function(name, mime, content) +{ + return _Scheduler_binding(function(callback) + { + var blob = new Blob([content], {type: mime}); + + // for IE10+ + if (navigator.msSaveOrOpenBlob) + { + navigator.msSaveOrOpenBlob(blob, name); + return; + } + + // for HTML5 + var node = _File_getDownloadNode(); + var objectUrl = URL.createObjectURL(blob); + node.href = objectUrl; + node.download = name; + _File_click(node); + URL.revokeObjectURL(objectUrl); + }); +}); + +function _File_downloadUrl(href) +{ + return _Scheduler_binding(function(callback) + { + var node = _File_getDownloadNode(); + node.href = href; + node.download = ''; + node.origin === location.origin || (node.target = '_blank'); + _File_click(node); + }); +} + + +// IE COMPATIBILITY + +function _File_makeBytesSafeForInternetExplorer(bytes) +{ + // only needed by IE10 and IE11 to fix https://github.com/elm/file/issues/10 + // all other browsers can just run `new Blob([bytes])` directly with no problem + // + return new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength); +} + +function _File_click(node) +{ + // only needed by IE10 and IE11 to fix https://github.com/elm/file/issues/11 + // all other browsers have MouseEvent and do not need this conditional stuff + // + if (typeof MouseEvent === 'function') + { + node.dispatchEvent(new MouseEvent('click')); + } + else + { + var event = document.createEvent('MouseEvents'); + event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); + document.body.appendChild(node); + node.dispatchEvent(event); + document.body.removeChild(node); + } +} + + +// UPLOAD + +var _File_node; + +function _File_uploadOne(mimes) +{ + return _Scheduler_binding(function(callback) + { + _File_node = document.createElement('input'); + _File_node.type = 'file'; + _File_node.accept = A2($elm$core$String$join, ',', mimes); + _File_node.addEventListener('change', function(event) + { + callback(_Scheduler_succeed(event.target.files[0])); + }); + _File_click(_File_node); + }); +} + +function _File_uploadOneOrMore(mimes) +{ + return _Scheduler_binding(function(callback) + { + _File_node = document.createElement('input'); + _File_node.type = 'file'; + _File_node.multiple = true; + _File_node.accept = A2($elm$core$String$join, ',', mimes); + _File_node.addEventListener('change', function(event) + { + var elmFiles = _List_fromArray(event.target.files); + callback(_Scheduler_succeed(_Utils_Tuple2(elmFiles.a, elmFiles.b))); + }); + _File_click(_File_node); + }); +} + + +// CONTENT + +function _File_toString(blob) +{ + return _Scheduler_binding(function(callback) + { + var reader = new FileReader(); + reader.addEventListener('loadend', function() { + callback(_Scheduler_succeed(reader.result)); + }); + reader.readAsText(blob); + return function() { reader.abort(); }; + }); +} + +function _File_toBytes(blob) +{ + return _Scheduler_binding(function(callback) + { + var reader = new FileReader(); + reader.addEventListener('loadend', function() { + callback(_Scheduler_succeed(new DataView(reader.result))); + }); + reader.readAsArrayBuffer(blob); + return function() { reader.abort(); }; + }); +} + +function _File_toUrl(blob) +{ + return _Scheduler_binding(function(callback) + { + var reader = new FileReader(); + reader.addEventListener('loadend', function() { + callback(_Scheduler_succeed(reader.result)); + }); + reader.readAsDataURL(blob); + return function() { reader.abort(); }; + }); +} + +var $author$project$Main$LinkClicked = function (a) { return {$: 'LinkClicked', a: a}; }; var $author$project$Main$UrlChanged = function (a) { @@ -5399,6 +5577,9 @@ var $author$project$Main$FilesPageMsg = function (a) { var $author$project$Main$HomePageMsg = function (a) { return {$: 'HomePageMsg', a: a}; }; +var $author$project$Main$StatusPageMsg = function (a) { + return {$: 'StatusPageMsg', a: a}; +}; var $mdgriffith$elm_ui$Internal$Model$Unkeyed = function (a) { return {$: 'Unkeyed', a: a}; }; @@ -11998,18 +12179,6 @@ var $author$project$MainPage$audioOnlyCheckbox = function (isChecked) { $mdgriffith$elm_ui$Element$text('Video')) ]))); }; -var $mdgriffith$elm_ui$Internal$Model$Below = {$: 'Below'}; -var $mdgriffith$elm_ui$Element$createNearby = F2( - function (loc, element) { - if (element.$ === 'Empty') { - return $mdgriffith$elm_ui$Internal$Model$NoAttribute; - } else { - return A2($mdgriffith$elm_ui$Internal$Model$Nearby, loc, element); - } - }); -var $mdgriffith$elm_ui$Element$below = function (element) { - return A2($mdgriffith$elm_ui$Element$createNearby, $mdgriffith$elm_ui$Internal$Model$Below, element); -}; var $mdgriffith$elm_ui$Internal$Model$Button = {$: 'Button'}; var $elm$json$Json$Encode$bool = _Json_wrap; var $elm$html$Html$Attributes$boolProperty = F2( @@ -12422,6 +12591,8 @@ var $author$project$MainPage$formatButton = F3( $mdgriffith$elm_ui$Element$Border$roundEach(corners), $mdgriffith$elm_ui$Element$Border$widthEach(borders), $mdgriffith$elm_ui$Element$Border$color($author$project$Color$color.blue), + $mdgriffith$elm_ui$Element$width( + $mdgriffith$elm_ui$Element$px(80)), $mdgriffith$elm_ui$Element$Background$color( _Utils_eq(state, $mdgriffith$elm_ui$Element$Input$Selected) ? $author$project$Color$color.lightBlue : $author$project$Color$color.white) ]), @@ -12452,7 +12623,7 @@ var $author$project$MainPage$videoFormatName = function (format) { case 'OGG': return 'ogg'; default: - return 'wav'; + return 'webm'; } }; var $author$project$MainPage$formatName = function (format) { @@ -12497,16 +12668,6 @@ var $mdgriffith$elm_ui$Element$Input$HiddenLabel = function (a) { var $mdgriffith$elm_ui$Element$Input$labelHidden = $mdgriffith$elm_ui$Element$Input$HiddenLabel; var $mdgriffith$elm_ui$Element$Input$OnLeft = {$: 'OnLeft'}; var $mdgriffith$elm_ui$Element$Input$labelLeft = $mdgriffith$elm_ui$Element$Input$Label($mdgriffith$elm_ui$Element$Input$OnLeft); -var $mdgriffith$elm_ui$Internal$Model$MoveY = function (a) { - return {$: 'MoveY', a: a}; -}; -var $mdgriffith$elm_ui$Internal$Flag$moveY = $mdgriffith$elm_ui$Internal$Flag$flag(26); -var $mdgriffith$elm_ui$Element$moveDown = function (y) { - return A2( - $mdgriffith$elm_ui$Internal$Model$TransformComponent, - $mdgriffith$elm_ui$Internal$Flag$moveY, - $mdgriffith$elm_ui$Internal$Model$MoveY(y)); -}; var $mdgriffith$elm_ui$Element$paddingXY = F2( function (x, y) { if (_Utils_eq(x, y)) { @@ -12856,9 +13017,21 @@ var $mdgriffith$elm_ui$Element$Input$autofill = A2( $mdgriffith$elm_ui$Internal$Model$Attr, $elm$html$Html$Attributes$attribute('autocomplete')); var $mdgriffith$elm_ui$Internal$Model$Behind = {$: 'Behind'}; +var $mdgriffith$elm_ui$Element$createNearby = F2( + function (loc, element) { + if (element.$ === 'Empty') { + return $mdgriffith$elm_ui$Internal$Model$NoAttribute; + } else { + return A2($mdgriffith$elm_ui$Internal$Model$Nearby, loc, element); + } + }); var $mdgriffith$elm_ui$Element$behindContent = function (element) { return A2($mdgriffith$elm_ui$Element$createNearby, $mdgriffith$elm_ui$Internal$Model$Behind, element); }; +var $mdgriffith$elm_ui$Internal$Model$MoveY = function (a) { + return {$: 'MoveY', a: a}; +}; +var $mdgriffith$elm_ui$Internal$Flag$moveY = $mdgriffith$elm_ui$Internal$Flag$flag(26); var $mdgriffith$elm_ui$Element$moveUp = function (y) { return A2( $mdgriffith$elm_ui$Internal$Model$TransformComponent, @@ -13609,18 +13782,7 @@ var $author$project$MainPage$view = function (model) { $mdgriffith$elm_ui$Element$Input$text, _List_fromArray( [ - $mdgriffith$elm_ui$Element$spacing(12), - $mdgriffith$elm_ui$Element$below( - A2( - $mdgriffith$elm_ui$Element$el, - _List_fromArray( - [ - $mdgriffith$elm_ui$Element$Font$color($author$project$Color$color.red), - $mdgriffith$elm_ui$Element$Font$size(14), - $mdgriffith$elm_ui$Element$alignRight, - $mdgriffith$elm_ui$Element$moveDown(6) - ]), - $mdgriffith$elm_ui$Element$text('This one is wrong'))) + $mdgriffith$elm_ui$Element$spacing(12) ]), { label: A2( @@ -13747,6 +13909,237 @@ var $author$project$MainPage$view = function (model) { }) ])); }; +var $author$project$Status$Download = function (a) { + return {$: 'Download', a: a}; +}; +var $mdgriffith$elm_ui$Internal$Flag$fontWeight = $mdgriffith$elm_ui$Internal$Flag$flag(13); +var $mdgriffith$elm_ui$Element$Font$bold = A2($mdgriffith$elm_ui$Internal$Model$Class, $mdgriffith$elm_ui$Internal$Flag$fontWeight, $mdgriffith$elm_ui$Internal$Style$classes.bold); +var $author$project$Status$cardShadow = $mdgriffith$elm_ui$Element$Border$shadow( + { + blur: 8, + color: A4($mdgriffith$elm_ui$Element$rgba, 0, 0, 0, 0.2), + offset: _Utils_Tuple2(0, 4), + size: 4 + }); +var $author$project$Status$cardShadowOver = $mdgriffith$elm_ui$Element$Border$shadow( + { + blur: 16, + color: A4($mdgriffith$elm_ui$Element$rgba, 0, 0, 0, 0.2), + offset: _Utils_Tuple2(0, 4), + size: 8 + }); +var $author$project$Status$card = F2( + function (attr, element) { + return A2( + $mdgriffith$elm_ui$Element$el, + $elm$core$List$concat( + _List_fromArray( + [ + attr, + _List_fromArray( + [ + $mdgriffith$elm_ui$Element$Border$rounded(15), + $author$project$Status$cardShadow, + $mdgriffith$elm_ui$Element$mouseOver( + _List_fromArray( + [$author$project$Status$cardShadowOver])), + $mdgriffith$elm_ui$Element$padding(20) + ]) + ])), + element); + }); +var $author$project$Status$innerCard = F2( + function (attr, element) { + return A2( + $mdgriffith$elm_ui$Element$el, + $elm$core$List$concat( + _List_fromArray( + [ + attr, + _List_fromArray( + [ + $mdgriffith$elm_ui$Element$Border$roundEach( + {bottomLeft: 15, bottomRight: 15, topLeft: 15, topRight: 15}), + $mdgriffith$elm_ui$Element$padding(5) + ]) + ])), + element); + }); +var $elm$html$Html$Attributes$rel = _VirtualDom_attribute('rel'); +var $mdgriffith$elm_ui$Element$link = F2( + function (attrs, _v0) { + var url = _v0.url; + var label = _v0.label; + return A4( + $mdgriffith$elm_ui$Internal$Model$element, + $mdgriffith$elm_ui$Internal$Model$asEl, + $mdgriffith$elm_ui$Internal$Model$NodeName('a'), + A2( + $elm$core$List$cons, + $mdgriffith$elm_ui$Internal$Model$Attr( + $elm$html$Html$Attributes$href(url)), + A2( + $elm$core$List$cons, + $mdgriffith$elm_ui$Internal$Model$Attr( + $elm$html$Html$Attributes$rel('noopener noreferrer')), + A2( + $elm$core$List$cons, + $mdgriffith$elm_ui$Element$width($mdgriffith$elm_ui$Element$shrink), + A2( + $elm$core$List$cons, + $mdgriffith$elm_ui$Element$height($mdgriffith$elm_ui$Element$shrink), + A2( + $elm$core$List$cons, + $mdgriffith$elm_ui$Internal$Model$htmlClass($mdgriffith$elm_ui$Internal$Style$classes.contentCenterX + (' ' + ($mdgriffith$elm_ui$Internal$Style$classes.contentCenterY + (' ' + $mdgriffith$elm_ui$Internal$Style$classes.link)))), + attrs))))), + $mdgriffith$elm_ui$Internal$Model$Unkeyed( + _List_fromArray( + [label]))); + }); +var $mdgriffith$elm_ui$Element$Font$underline = $mdgriffith$elm_ui$Internal$Model$htmlClass($mdgriffith$elm_ui$Internal$Style$classes.underline); +var $author$project$Status$statusView = function (status) { + return A2( + $author$project$Status$card, + _List_fromArray( + [ + $mdgriffith$elm_ui$Element$width($mdgriffith$elm_ui$Element$fill), + $mdgriffith$elm_ui$Element$height($mdgriffith$elm_ui$Element$shrink) + ]), + A2( + $mdgriffith$elm_ui$Element$column, + _List_fromArray( + [ + $mdgriffith$elm_ui$Element$width($mdgriffith$elm_ui$Element$fill) + ]), + _List_fromArray( + [ + A2( + $mdgriffith$elm_ui$Element$row, + _List_fromArray( + [ + $mdgriffith$elm_ui$Element$width($mdgriffith$elm_ui$Element$fill), + $mdgriffith$elm_ui$Element$height( + $mdgriffith$elm_ui$Element$px(40)) + ]), + _List_fromArray( + [ + A2( + $mdgriffith$elm_ui$Element$el, + _List_fromArray( + [ + $mdgriffith$elm_ui$Element$Font$size(28), + $mdgriffith$elm_ui$Element$alignLeft + ]), + $mdgriffith$elm_ui$Element$text(status.name)), + A2( + $mdgriffith$elm_ui$Element$el, + _List_fromArray( + [ + $mdgriffith$elm_ui$Element$Font$size(28), + $mdgriffith$elm_ui$Element$alignRight + ]), + $mdgriffith$elm_ui$Element$text( + $elm$core$String$concat( + _List_fromArray( + ['[', status.status, ']'])))) + ])), + A2( + $mdgriffith$elm_ui$Element$link, + _List_fromArray( + [ + $mdgriffith$elm_ui$Element$Font$bold, + $mdgriffith$elm_ui$Element$Font$underline, + $mdgriffith$elm_ui$Element$Font$color($author$project$Color$color.blue), + $mdgriffith$elm_ui$Element$Font$size(16), + $mdgriffith$elm_ui$Element$padding(2) + ]), + { + label: $mdgriffith$elm_ui$Element$text(status.source), + url: status.source + }), + A2( + $mdgriffith$elm_ui$Element$el, + _List_fromArray( + [ + $mdgriffith$elm_ui$Element$height( + $mdgriffith$elm_ui$Element$px(10)) + ]), + $mdgriffith$elm_ui$Element$none), + function () { + var _v0 = status.file; + if (_v0.$ === 'Just') { + var file = _v0.a; + return A2( + $author$project$Status$innerCard, + _List_fromArray( + [ + $mdgriffith$elm_ui$Element$width($mdgriffith$elm_ui$Element$fill), + $mdgriffith$elm_ui$Element$height($mdgriffith$elm_ui$Element$shrink), + $mdgriffith$elm_ui$Element$Background$color($author$project$Color$color.grey) + ]), + A2( + $mdgriffith$elm_ui$Element$row, + _List_fromArray( + [ + $mdgriffith$elm_ui$Element$width($mdgriffith$elm_ui$Element$fill), + $mdgriffith$elm_ui$Element$spacing(20) + ]), + _List_fromArray( + [ + A2( + $mdgriffith$elm_ui$Element$Input$button, + _List_fromArray( + [ + $mdgriffith$elm_ui$Element$Background$color($author$project$Color$color.blue), + $mdgriffith$elm_ui$Element$Font$color($author$project$Color$color.white), + $mdgriffith$elm_ui$Element$Border$color($author$project$Color$color.red), + A2($mdgriffith$elm_ui$Element$paddingXY, 32, 16), + $mdgriffith$elm_ui$Element$Border$rounded(15) + ]), + { + label: $mdgriffith$elm_ui$Element$text('Download'), + onPress: $elm$core$Maybe$Just( + $author$project$Status$Download(status.id)) + }), + A2( + $mdgriffith$elm_ui$Element$el, + _List_fromArray( + [ + $mdgriffith$elm_ui$Element$Font$size(16) + ]), + $mdgriffith$elm_ui$Element$text(file.filename)) + ]))); + } else { + return $mdgriffith$elm_ui$Element$none; + } + }() + ]))); +}; +var $author$project$Status$view = function (model) { + return A2( + $mdgriffith$elm_ui$Element$column, + _List_fromArray( + [ + $mdgriffith$elm_ui$Element$width( + $mdgriffith$elm_ui$Element$px(800)), + $mdgriffith$elm_ui$Element$height($mdgriffith$elm_ui$Element$shrink), + $mdgriffith$elm_ui$Element$centerX, + $mdgriffith$elm_ui$Element$centerY, + $mdgriffith$elm_ui$Element$spacing(36) + ]), + _List_fromArray( + [ + function () { + var _v0 = model.status; + if (_v0.$ === 'Just') { + var status = _v0.a; + return $author$project$Status$statusView(status); + } else { + return $mdgriffith$elm_ui$Element$text('Test'); + } + }() + ])); +}; var $author$project$Main$view = function (model) { return A2( $mdgriffith$elm_ui$Element$layout, @@ -13769,6 +14162,12 @@ var $author$project$Main$view = function (model) { $mdgriffith$elm_ui$Element$map, $author$project$Main$FilesPageMsg, $author$project$Files$view(pageModel)); + case 'StatusPage': + var pageModel = _v0.a; + return A2( + $mdgriffith$elm_ui$Element$map, + $author$project$Main$StatusPageMsg, + $author$project$Status$view(pageModel)); default: return A2( $mdgriffith$elm_ui$Element$el, @@ -13797,11 +14196,21 @@ var $author$project$Main$FilesPage = function (a) { var $author$project$Main$Home = function (a) { return {$: 'Home', a: a}; }; +var $author$project$Main$StatusPage = function (a) { + return {$: 'StatusPage', a: a}; +}; var $elm$core$Platform$Cmd$batch = _Platform_batch; var $author$project$Files$initModel = {error: $elm$core$Maybe$Nothing, files: _List_Nil, filter: ''}; var $author$project$Files$QueryRequestResult = function (a) { return {$: 'QueryRequestResult', a: a}; }; +var $author$project$Globals$api = 'http://127.0.0.1/api/'; +var $author$project$Globals$apiEndpoint = function (path) { + return $elm$core$String$concat( + _List_fromArray( + [$author$project$Globals$api, path])); +}; +var $author$project$Globals$apiFiles = $author$project$Globals$apiEndpoint('files'); var $elm$json$Json$Decode$decodeString = _Json_runOnString; var $elm$http$Http$BadStatus_ = F2( function (a, b) { @@ -14475,9 +14884,21 @@ var $author$project$Files$queueDecoder = A3( var $author$project$Files$queryFiles = $elm$http$Http$get( { expect: A2($elm$http$Http$expectJson, $author$project$Files$QueryRequestResult, $author$project$Files$queueDecoder), - url: 'http://127.0.0.1/api/files' + url: $author$project$Globals$apiFiles }); var $author$project$Files$init = _Utils_Tuple2($author$project$Files$initModel, $author$project$Files$queryFiles); +var $author$project$Status$Reload = {$: 'Reload'}; +var $author$project$Status$send = function (msg) { + return A2( + $elm$core$Task$perform, + $elm$core$Basics$identity, + $elm$core$Task$succeed(msg)); +}; +var $author$project$Status$init = function (file_id) { + return _Utils_Tuple2( + {file_id: file_id, status: $elm$core$Maybe$Nothing}, + $author$project$Status$send($author$project$Status$Reload)); +}; var $author$project$MainPage$initModel = { form: {audio_only: false, format: $author$project$MainPage$Auto, url: ''} }; @@ -14498,13 +14919,21 @@ var $author$project$Main$initCurrentPage = function (_v0) { return _Utils_Tuple2( $author$project$Main$Home(pageModel), A2($elm$core$Platform$Cmd$map, $author$project$Main$HomePageMsg, pageCmd)); - default: + case 'Files': var _v4 = $author$project$Files$init; var pageModel = _v4.a; var pageCmd = _v4.b; return _Utils_Tuple2( $author$project$Main$FilesPage(pageModel), A2($elm$core$Platform$Cmd$map, $author$project$Main$FilesPageMsg, pageCmd)); + default: + var file = _v2.a; + var _v5 = $author$project$Status$init(file); + var pageModel = _v5.a; + var pageCmd = _v5.b; + return _Utils_Tuple2( + $author$project$Main$StatusPage(pageModel), + A2($elm$core$Platform$Cmd$map, $author$project$Main$StatusPageMsg, pageCmd)); } }(); var currentPage = _v1.a; @@ -14520,6 +14949,9 @@ var $author$project$Main$initCurrentPage = function (_v0) { var $author$project$Route$NotFound = {$: 'NotFound'}; var $author$project$Route$Files = {$: 'Files'}; var $author$project$Route$Home = {$: 'Home'}; +var $author$project$Route$Status = function (a) { + return {$: 'Status', a: a}; +}; var $elm$url$Url$Parser$Parser = function (a) { return {$: 'Parser', a: a}; }; @@ -14597,6 +15029,52 @@ var $elm$url$Url$Parser$s = function (str) { } }); }; +var $elm$url$Url$Parser$slash = F2( + function (_v0, _v1) { + var parseBefore = _v0.a; + var parseAfter = _v1.a; + return $elm$url$Url$Parser$Parser( + function (state) { + return A2( + $elm$core$List$concatMap, + parseAfter, + parseBefore(state)); + }); + }); +var $elm$url$Url$Parser$custom = F2( + function (tipe, stringToSomething) { + return $elm$url$Url$Parser$Parser( + function (_v0) { + var visited = _v0.visited; + var unvisited = _v0.unvisited; + var params = _v0.params; + var frag = _v0.frag; + var value = _v0.value; + if (!unvisited.b) { + return _List_Nil; + } else { + var next = unvisited.a; + var rest = unvisited.b; + var _v2 = stringToSomething(next); + if (_v2.$ === 'Just') { + var nextValue = _v2.a; + return _List_fromArray( + [ + A5( + $elm$url$Url$Parser$State, + A2($elm$core$List$cons, next, visited), + rest, + params, + frag, + value(nextValue)) + ]); + } else { + return _List_Nil; + } + } + }); + }); +var $elm$url$Url$Parser$string = A2($elm$url$Url$Parser$custom, 'STRING', $elm$core$Maybe$Just); var $elm$url$Url$Parser$top = $elm$url$Url$Parser$Parser( function (state) { return _List_fromArray( @@ -14609,7 +15087,14 @@ var $author$project$Route$matchRoute = $elm$url$Url$Parser$oneOf( A2( $elm$url$Url$Parser$map, $author$project$Route$Files, - $elm$url$Url$Parser$s('files')) + $elm$url$Url$Parser$s('files')), + A2( + $elm$url$Url$Parser$map, + $author$project$Route$Status, + A2( + $elm$url$Url$Parser$slash, + $elm$url$Url$Parser$s('status'), + $elm$url$Url$Parser$string)) ])); var $elm$url$Url$Parser$getFirstMatch = function (states) { getFirstMatch: @@ -14838,8 +15323,8 @@ var $author$project$MainPage$formDecoder = A3( $elm$json$Json$Decode$string, A3( $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, - 'id', - $elm$json$Json$Decode$int, + 'file_id', + $elm$json$Json$Decode$string, $elm$json$Json$Decode$succeed($author$project$MainPage$PostFormResponse))); var $author$project$MainPage$formPostEncoder = function (form) { return $elm$json$Json$Encode$object( @@ -14877,63 +15362,267 @@ var $elm$http$Http$post = function (r) { return $elm$http$Http$request( {body: r.body, expect: r.expect, headers: _List_Nil, method: 'POST', timeout: $elm$core$Maybe$Nothing, tracker: $elm$core$Maybe$Nothing, url: r.url}); }; -var $author$project$MainPage$update = F2( +var $author$project$MainPage$update = F3( + function (navKey, msg, model) { + _v0$5: + while (true) { + switch (msg.$) { + case 'UpdateForm': + var new_form = msg.a; + return _Utils_Tuple2( + _Utils_update( + model, + {form: new_form}), + $elm$core$Platform$Cmd$none); + case 'UpdateFormAudioOnlyChanged': + var form = model.form; + var new_form = _Utils_update( + form, + {audio_only: !form.audio_only, format: $author$project$MainPage$Auto}); + return _Utils_Tuple2( + _Utils_update( + model, + {form: new_form}), + $elm$core$Platform$Cmd$none); + case 'UpdateFormFormatChnaged': + var format = msg.a; + var form = model.form; + var new_form = _Utils_update( + form, + {format: format}); + return _Utils_Tuple2( + _Utils_update( + model, + {form: new_form}), + $elm$core$Platform$Cmd$none); + case 'PostForm': + var form = msg.a; + return (!$elm$core$String$isEmpty(form.url)) ? _Utils_Tuple2( + model, + $elm$http$Http$post( + { + body: $elm$http$Http$jsonBody( + $author$project$MainPage$formPostEncoder(form)), + expect: A2($elm$http$Http$expectJson, $author$project$MainPage$PostResult, $author$project$MainPage$formDecoder), + url: 'http://127.0.0.1/api/add' + })) : _Utils_Tuple2(model, $elm$core$Platform$Cmd$none); + case 'PostResult': + if (msg.a.$ === 'Ok') { + var data = msg.a.a; + return _Utils_Tuple2( + model, + A2( + $elm$browser$Browser$Navigation$pushUrl, + navKey, + $elm$core$String$concat( + _List_fromArray( + ['/status/', data.id])))); + } else { + break _v0$5; + } + default: + break _v0$5; + } + } + return _Utils_Tuple2(model, $elm$core$Platform$Cmd$none); + }); +var $elm$core$Process$sleep = _Process_sleep; +var $andrewMacmurray$elm_delay$Delay$after = F2( + function (time, msg) { + return A2( + $elm$core$Task$perform, + $elm$core$Basics$always(msg), + $elm$core$Process$sleep(time)); + }); +var $author$project$Globals$apiDownloadFile = function (id) { + return $elm$core$String$concat( + _List_fromArray( + [ + $author$project$Globals$apiEndpoint('file/'), + id + ])); +}; +var $author$project$Status$StatusRequestResult = function (a) { + return {$: 'StatusRequestResult', a: a}; +}; +var $author$project$Globals$apiStatus = function (id) { + return $elm$core$String$concat( + _List_fromArray( + [ + $author$project$Globals$apiEndpoint('status/'), + id + ])); +}; +var $author$project$Status$StatusResponse = function (status) { + return {status: status}; +}; +var $author$project$Status$Status = F8( + function (file, name, source, status, status_id, format, timestamp, id) { + return {file: file, format: format, id: id, name: name, source: source, status: status, status_id: status_id, timestamp: timestamp}; + }); +var $author$project$Status$File = F3( + function (format, filename, timestamp) { + return {filename: filename, format: format, timestamp: timestamp}; + }); +var $author$project$Status$fileDecoder = A3( + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, + 'timestamp', + $elm$json$Json$Decode$int, + A3( + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, + 'name', + $elm$json$Json$Decode$string, + A3( + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, + 'format', + $elm$json$Json$Decode$string, + $elm$json$Json$Decode$succeed($author$project$Status$File)))); +var $elm$json$Json$Decode$decodeValue = _Json_run; +var $elm$json$Json$Decode$null = _Json_decodeNull; +var $elm$json$Json$Decode$oneOf = _Json_oneOf; +var $elm$json$Json$Decode$value = _Json_decodeValue; +var $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$optionalDecoder = F3( + function (pathDecoder, valDecoder, fallback) { + var nullOr = function (decoder) { + return $elm$json$Json$Decode$oneOf( + _List_fromArray( + [ + decoder, + $elm$json$Json$Decode$null(fallback) + ])); + }; + var handleResult = function (input) { + var _v0 = A2($elm$json$Json$Decode$decodeValue, pathDecoder, input); + if (_v0.$ === 'Ok') { + var rawValue = _v0.a; + var _v1 = A2( + $elm$json$Json$Decode$decodeValue, + nullOr(valDecoder), + rawValue); + if (_v1.$ === 'Ok') { + var finalResult = _v1.a; + return $elm$json$Json$Decode$succeed(finalResult); + } else { + var finalErr = _v1.a; + return $elm$json$Json$Decode$fail( + $elm$json$Json$Decode$errorToString(finalErr)); + } + } else { + return $elm$json$Json$Decode$succeed(fallback); + } + }; + return A2($elm$json$Json$Decode$andThen, handleResult, $elm$json$Json$Decode$value); + }); +var $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$optional = F4( + function (key, valDecoder, fallback, decoder) { + return A2( + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$custom, + A3( + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$optionalDecoder, + A2($elm$json$Json$Decode$field, key, $elm$json$Json$Decode$value), + valDecoder, + fallback), + decoder); + }); +var $author$project$Status$statusDecoder = A3( + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, + 'id', + $elm$json$Json$Decode$string, + A3( + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, + 'timestamp', + $elm$json$Json$Decode$int, + A3( + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, + 'task_file_type', + $elm$json$Json$Decode$string, + A3( + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, + 'status_id', + $elm$json$Json$Decode$int, + A3( + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, + 'status', + $elm$json$Json$Decode$string, + A3( + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, + 'source', + $elm$json$Json$Decode$string, + A3( + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, + 'name', + $elm$json$Json$Decode$string, + A4( + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$optional, + 'file', + A2($elm$json$Json$Decode$map, $elm$core$Maybe$Just, $author$project$Status$fileDecoder), + $elm$core$Maybe$Nothing, + $elm$json$Json$Decode$succeed($author$project$Status$Status))))))))); +var $author$project$Status$statusResponseDecoder = A3( + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, + 'status', + $author$project$Status$statusDecoder, + $elm$json$Json$Decode$succeed($author$project$Status$StatusResponse)); +var $author$project$Status$queryStatus = function (id) { + return $elm$http$Http$get( + { + expect: A2($elm$http$Http$expectJson, $author$project$Status$StatusRequestResult, $author$project$Status$statusResponseDecoder), + url: $author$project$Globals$apiStatus(id) + }); +}; +var $elm$time$Time$Posix = function (a) { + return {$: 'Posix', a: a}; +}; +var $elm$time$Time$millisToPosix = $elm$time$Time$Posix; +var $elm$file$File$Download$url = function (href) { + return A2( + $elm$core$Task$perform, + $elm$core$Basics$never, + _File_downloadUrl(href)); +}; +var $author$project$Status$update = F2( function (msg, model) { switch (msg.$) { - case 'UpdateForm': - var new_form = msg.a; - return _Utils_Tuple2( - _Utils_update( - model, - {form: new_form}), - $elm$core$Platform$Cmd$none); - case 'UpdateFormAudioOnlyChanged': - var form = model.form; - var new_form = _Utils_update( - form, - {audio_only: !form.audio_only, format: $author$project$MainPage$Auto}); - return _Utils_Tuple2( - _Utils_update( - model, - {form: new_form}), - $elm$core$Platform$Cmd$none); - case 'UpdateFormFormatChnaged': - var format = msg.a; - var form = model.form; - var new_form = _Utils_update( - form, - {format: format}); - return _Utils_Tuple2( - _Utils_update( - model, - {form: new_form}), - $elm$core$Platform$Cmd$none); - case 'PostForm': - var form = msg.a; + case 'Reload': return _Utils_Tuple2( model, - $elm$http$Http$post( - { - body: $elm$http$Http$jsonBody( - $author$project$MainPage$formPostEncoder(form)), - expect: A2($elm$http$Http$expectJson, $author$project$MainPage$PostResult, $author$project$MainPage$formDecoder), - url: 'http://127.0.0.1/api/add' - })); + $author$project$Status$queryStatus(model.file_id)); + case 'StatusRequestResult': + if (msg.a.$ === 'Ok') { + var res = msg.a.a; + return _Utils_Tuple2( + _Utils_update( + model, + { + status: $elm$core$Maybe$Just(res.status) + }), + A2($andrewMacmurray$elm_delay$Delay$after, 1000, $author$project$Status$Reload)); + } else { + return _Utils_Tuple2( + _Utils_update( + model, + {status: $elm$core$Maybe$Nothing}), + $elm$core$Platform$Cmd$none); + } default: - return _Utils_Tuple2(model, $elm$core$Platform$Cmd$none); + var file_id = msg.a; + return _Utils_Tuple2( + model, + $elm$file$File$Download$url( + $author$project$Globals$apiDownloadFile(file_id))); } }); var $author$project$Main$update = F2( function (msg, model) { var _v0 = _Utils_Tuple2(msg, model.page); - _v0$4: + _v0$5: while (true) { switch (_v0.a.$) { case 'HomePageMsg': if (_v0.b.$ === 'Home') { var subMsg = _v0.a.a; var pageModel = _v0.b.a; - var _v1 = A2($author$project$MainPage$update, subMsg, pageModel); + var _v1 = A3($author$project$MainPage$update, model.navKey, subMsg, pageModel); var updatePageModel = _v1.a; var updateCmd = _v1.b; return _Utils_Tuple2( @@ -14944,7 +15633,7 @@ var $author$project$Main$update = F2( }), A2($elm$core$Platform$Cmd$map, $author$project$Main$HomePageMsg, updateCmd)); } else { - break _v0$4; + break _v0$5; } case 'FilesPageMsg': if (_v0.b.$ === 'FilesPage') { @@ -14961,7 +15650,24 @@ var $author$project$Main$update = F2( }), A2($elm$core$Platform$Cmd$map, $author$project$Main$FilesPageMsg, updateCmd)); } else { - break _v0$4; + break _v0$5; + } + case 'StatusPageMsg': + if (_v0.b.$ === 'StatusPage') { + var subMsg = _v0.a.a; + var pageModel = _v0.b.a; + var _v3 = A2($author$project$Status$update, subMsg, pageModel); + var updatePageModel = _v3.a; + var updateCmd = _v3.b; + return _Utils_Tuple2( + _Utils_update( + model, + { + page: $author$project$Main$StatusPage(updatePageModel) + }), + A2($elm$core$Platform$Cmd$map, $author$project$Main$StatusPageMsg, updateCmd)); + } else { + break _v0$5; } case 'LinkClicked': var urlRequest = _v0.a.a; diff --git a/Modules/Website/src/Files.elm b/Modules/Website/src/Files.elm index dc34232..a6c98ff 100644 --- a/Modules/Website/src/Files.elm +++ b/Modules/Website/src/Files.elm @@ -2,15 +2,13 @@ module Files exposing (..) import Debug exposing (toString) import Element exposing (..) -import Element.Background as Background import Element.Border as Border import Element.Font as Font -import Element.Input as Input import Element.Region as Region +import Globals exposing (apiFiles) import Http import Json.Decode exposing (Decoder, int, list, string, succeed) import Json.Decode.Pipeline exposing (required) -import String exposing (left) type alias File = @@ -100,7 +98,7 @@ update msg model = queryFiles : Cmd Msg queryFiles = Http.get - { url = "http://127.0.0.1/api/files" + { url = apiFiles , expect = Http.expectJson QueryRequestResult queueDecoder } diff --git a/Modules/Website/src/Globals.elm b/Modules/Website/src/Globals.elm new file mode 100644 index 0000000..7aacdfe --- /dev/null +++ b/Modules/Website/src/Globals.elm @@ -0,0 +1,21 @@ +module Globals exposing (..) + + +api : String +api = + "http://127.0.0.1/api/" + + +apiEndpoint : String -> String +apiEndpoint path = + String.concat [ api, path ] + + +apiStatus : String -> String +apiStatus id = String.concat [apiEndpoint "status/", id] + +apiDownloadFile : String -> String +apiDownloadFile id = String.concat [apiEndpoint "file/", id] + +apiFiles : String +apiFiles = apiEndpoint "files" \ No newline at end of file diff --git a/Modules/Website/src/Main.elm b/Modules/Website/src/Main.elm index d4d92f8..2feabd5 100644 --- a/Modules/Website/src/Main.elm +++ b/Modules/Website/src/Main.elm @@ -8,7 +8,8 @@ import Element.Region as Region import Files import Html exposing (Html) import MainPage -import Route exposing (Route) +import Route exposing (Route(..)) +import Status import Url exposing (Url) @@ -23,12 +24,7 @@ type Page = NotFound | Home MainPage.Model | FilesPage Files.Model - - - ---initModel : Model ---initModel = --- PageMain MainPage.initModel + | StatusPage Status.Model init : () -> Url -> Nav.Key -> ( Model, Cmd Msg ) @@ -64,6 +60,13 @@ initCurrentPage ( model, existingCmds ) = Files.init in ( FilesPage pageModel, Cmd.map FilesPageMsg pageCmd ) + + Route.Status file -> + let + ( pageModel, pageCmd ) = + Status.init file + in + ( StatusPage pageModel, Cmd.map StatusPageMsg pageCmd ) in ( { model | page = currentPage } , Cmd.batch [ existingCmds, mappedPageCmds ] @@ -79,6 +82,7 @@ type Msg | UrlChanged Url | HomePageMsg MainPage.Msg | FilesPageMsg Files.Msg + | StatusPageMsg Status.Msg @@ -91,7 +95,7 @@ update msg model = ( HomePageMsg subMsg, Home pageModel ) -> let ( updatePageModel, updateCmd ) = - MainPage.update subMsg pageModel + MainPage.update model.navKey subMsg pageModel in ( { model | page = Home updatePageModel } , Cmd.map HomePageMsg updateCmd @@ -106,6 +110,15 @@ update msg model = , Cmd.map FilesPageMsg updateCmd ) + ( StatusPageMsg subMsg, StatusPage pageModel ) -> + let + ( updatePageModel, updateCmd ) = + Status.update subMsg pageModel + in + ( { model | page = StatusPage updatePageModel } + , Cmd.map StatusPageMsg updateCmd + ) + ( LinkClicked urlRequest, _ ) -> case urlRequest of Browser.Internal url -> @@ -141,6 +154,9 @@ view model = FilesPage pageModel -> Files.view pageModel |> Element.map FilesPageMsg + StatusPage pageModel -> + Status.view pageModel |> Element.map StatusPageMsg + _ -> el [ Region.heading 1 diff --git a/Modules/Website/src/MainPage.elm b/Modules/Website/src/MainPage.elm index a7fd97a..47f47cb 100644 --- a/Modules/Website/src/MainPage.elm +++ b/Modules/Website/src/MainPage.elm @@ -1,5 +1,6 @@ module MainPage exposing (Model, Msg, initModel, update, view) +import Browser.Navigation as Nav import Color exposing (..) import Element exposing (..) import Element.Background as Background @@ -7,13 +8,11 @@ import Element.Border as Border import Element.Font as Font import Element.Input as Input import Element.Region as Region +import Html exposing (form) import Http import Json.Decode exposing (Decoder, int, string, succeed) import Json.Decode.Pipeline exposing (required) import Json.Encode as Encode -import List exposing (map3) -import Tuple exposing (mapBoth) -import Html exposing (form) type alias Form = @@ -53,13 +52,21 @@ type AudioFormats | AAC | WAV + audioFormatName : AudioFormats -> String audioFormatName format = case format of - MP3 -> "mp3" - OPUS -> "opus" - AAC -> "aac" - WAV -> "wav" + MP3 -> + "mp3" + + OPUS -> + "opus" + + AAC -> + "aac" + + WAV -> + "wav" type VideoFormats @@ -68,38 +75,54 @@ type VideoFormats | OGG | WEBM + videoFormatName : VideoFormats -> String videoFormatName format = case format of - FLV -> "flv" - MP4 -> "mp4" - OGG -> "ogg" - WEBM -> "wav" + FLV -> + "flv" + + MP4 -> + "mp4" + + OGG -> + "ogg" + + WEBM -> + "webm" + type Format = Auto | AudioFormat AudioFormats | VideoFormat VideoFormats + formatName : Format -> String formatName format = case format of - Auto -> "Auto" - AudioFormat audio -> audioFormatName audio - VideoFormat video -> videoFormatName video + Auto -> + "Auto" + + AudioFormat audio -> + audioFormatName audio + + VideoFormat video -> + videoFormatName video + -- -- Request -- -- type alias PostFormResponse = - { id : Int, status : String } + { id : String, status : String } formDecoder : Decoder PostFormResponse formDecoder = succeed PostFormResponse - |> required "id" int + |> required "file_id" string |> required "status" string @@ -115,8 +138,9 @@ formPostEncoder form = [] , if form.format == Auto then [] - else - [( "format", Encode.string <| formatName form.format )] + + else + [ ( "format", Encode.string <| formatName form.format ) ] ] @@ -124,8 +148,8 @@ formPostEncoder form = -- -- Update -- -- -update : Msg -> Model -> ( Model, Cmd Msg ) -update msg model = +update : Nav.Key -> Msg -> Model -> ( Model, Cmd Msg ) +update navKey msg model = case msg of UpdateForm new_form -> ( { model | form = new_form }, Cmd.none ) @@ -151,13 +175,20 @@ update msg model = ( { model | form = new_form }, Cmd.none ) PostForm form -> - ( model - , Http.post - { url = "http://127.0.0.1/api/add" - , body = Http.jsonBody (formPostEncoder form) - , expect = Http.expectJson PostResult formDecoder - } - ) + if not (String.isEmpty form.url) then + ( model + , Http.post + { url = "http://127.0.0.1/api/add" + , body = Http.jsonBody (formPostEncoder form) + , expect = Http.expectJson PostResult formDecoder + } + ) + + else + ( model, Cmd.none ) + + PostResult (Ok data) -> + ( model, Nav.pushUrl navKey <| String.concat [ "/status/", data.id ] ) _ -> ( model, Cmd.none ) @@ -254,6 +285,7 @@ formatButton position label state = , Border.roundEach corners , Border.widthEach borders , Border.color color.blue + , width (px 80) , Background.color <| if state == Input.Selected then color.lightBlue @@ -269,6 +301,7 @@ formatButton position label state = formatOption format position = Input.optionWith format <| formatButton position <| formatName format + view : Model -> Element Msg view model = Element.column @@ -287,15 +320,6 @@ view model = (text "localTube") , Input.text [ spacing 12 - , below - (el - [ Font.color color.red - , Font.size 14 - , alignRight - , moveDown 6 - ] - (text "This one is wrong") - ) ] { text = model.form.url , placeholder = Just (Input.placeholder [] (text "http://youtube.com")) @@ -325,17 +349,17 @@ view model = List.concat [ [ Input.optionWith Auto <| formatButton First "Auto" ] , if model.form.audio_only then - [ formatOption (AudioFormat MP3) Mid - , formatOption (AudioFormat OPUS) Mid - , formatOption (AudioFormat AAC) Mid - , formatOption (AudioFormat WAV) Last + [ formatOption (AudioFormat MP3) Mid + , formatOption (AudioFormat OPUS) Mid + , formatOption (AudioFormat AAC) Mid + , formatOption (AudioFormat WAV) Last ] else - [ formatOption (VideoFormat FLV) Mid - , formatOption (VideoFormat MP4) Mid - , formatOption (VideoFormat OGG) Mid - , formatOption (VideoFormat WEBM)Last + [ formatOption (VideoFormat FLV) Mid + , formatOption (VideoFormat MP4) Mid + , formatOption (VideoFormat OGG) Mid + , formatOption (VideoFormat WEBM) Last ] ] } diff --git a/Modules/Website/src/Route.elm b/Modules/Website/src/Route.elm index d939cee..eff108f 100644 --- a/Modules/Website/src/Route.elm +++ b/Modules/Website/src/Route.elm @@ -8,6 +8,7 @@ type Route = NotFound | Home | Files + | Status String parseUrl : Url -> Route @@ -25,4 +26,5 @@ matchRoute = oneOf [ map Home top , map Files (s "files") + , map Status (s "status" string) ] diff --git a/Modules/Website/src/Status.elm b/Modules/Website/src/Status.elm new file mode 100644 index 0000000..80ad3e5 --- /dev/null +++ b/Modules/Website/src/Status.elm @@ -0,0 +1,232 @@ +module Status exposing (Model, Msg, init, queryStatus, update, view) + +import Color exposing (color) +import Debug exposing (toString) +import Delay +import Element exposing (..) +import Element.Background as Background +import Element.Border as Border +import Element.Font as Font +import Element.Input as Input +import File.Download exposing (url) +import Files exposing (Msg(..)) +import Globals exposing (apiDownloadFile, apiStatus) +import Http +import Json.Decode exposing (Decoder, int, list, string, succeed) +import Json.Decode.Pipeline exposing (optional, required) +import Route exposing (Route(..)) +import String exposing (right) +import Task + + + +-- Messages + + +type Msg + = Reload + | StatusRequestResult (Result Http.Error StatusResponse) + | Download String + + + +-- Model + + +type alias Model = + { file_id : String + , status : Maybe Status + } + + +init : String -> ( Model, Cmd Msg ) +init file_id = + ( { file_id = file_id, status = Nothing }, send Reload ) + + + +-- Request + + +type alias File = + { format : String + , filename : String + , timestamp : Int + } + + +type alias Status = + { file : Maybe File + , name : String + , source : String + , status : String + , status_id : Int + , format : String + , timestamp : Int + , id : String + } + + +type alias StatusResponse = + { status : Status } + + +fileDecoder : Decoder File +fileDecoder = + succeed File + |> required "format" string + |> required "name" string + |> required "timestamp" int + + +statusDecoder : Decoder Status +statusDecoder = + succeed Status + |> optional "file" (Json.Decode.map Just fileDecoder) Nothing + |> required "name" string + |> required "source" string + |> required "status" string + |> required "status_id" int + |> required "task_file_type" string + |> required "timestamp" int + |> required "id" string + + +statusResponseDecoder : Decoder StatusResponse +statusResponseDecoder = + succeed StatusResponse + |> required "status" statusDecoder + + +queryStatus : String -> Cmd Msg +queryStatus id = + Http.get + { url = apiStatus id + , expect = Http.expectJson StatusRequestResult statusResponseDecoder + } + + + +-- Update + + +cardShadow : Attr decorative msg +cardShadow = + Border.shadow + { offset = ( 0, 4 ) + , size = 4 + , blur = 8 + , color = rgba 0 0 0 0.2 + } + + +cardShadowOver : Attr decorative msg +cardShadowOver = + Border.shadow + { offset = ( 0, 4 ) + , size = 8 + , blur = 16 + , color = rgba 0 0 0 0.2 + } + + +card : List (Attribute msg) -> Element msg -> Element msg +card attr element = + el + (List.concat + [ attr + , [ Border.rounded 15, cardShadow, mouseOver [ cardShadowOver ], padding 20 ] + ] + ) + element + + +innerCard : List (Attribute msg) -> Element msg -> Element msg +innerCard attr element = + el + (List.concat + [ attr + , [ Border.roundEach { topLeft = 15, topRight = 15, bottomLeft = 15, bottomRight = 15 }, padding 5 ] + ] + ) + element + + +send : msg -> Cmd msg +send msg = + Task.succeed msg + |> Task.perform identity + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + Reload -> + ( model, queryStatus model.file_id ) + + StatusRequestResult (Ok res) -> + ( { model | status = Just res.status }, Delay.after 1000 Reload ) + + StatusRequestResult _ -> + ( { model | status = Nothing }, Cmd.none ) + + Download file_id -> + ( model, url <| apiDownloadFile file_id ) + + + +-- View +--Just (Download. + + +statusView : Status -> Element Msg +statusView status = + card [ width fill, height shrink ] + (Element.column + [ width fill ] + [ row [ width fill, height <| px 40 ] + [ el [ Font.size 28, alignLeft ] <| text status.name + , el [ Font.size 28, alignRight ] <| text (String.concat [ "[", status.status, "]" ]) + ] + , link [ Font.bold, Font.underline, Font.color color.blue, Font.size 16, padding 2 ] + { url = status.source, label = text status.source } + , el [ height <| px 10 ] none + , case status.file of + Just file -> + innerCard [ width fill, height shrink, Background.color color.grey ] <| + row [ width fill, spacing 20 ] + [ Input.button + [ Background.color color.blue + , Font.color color.white + , Border.color color.red + , paddingXY 32 16 + , Border.rounded 15 + ] + { onPress = Just <| Download status.id + , label = Element.text "Download" + } + , el [ Font.size 16 ] <| text file.filename + ] + + Nothing -> + Element.none + ] + ) + + +view : Model -> Element Msg +view model = + Element.column + [ width (px 800) + , height shrink + , centerX + , centerY + , spacing 36 + ] + [ case model.status of + Just status -> + statusView status + + Nothing -> + Element.text "Test" + ]