diff options
author | Wojtek Kosior <koszko@koszko.org> | 2021-07-31 19:36:19 +0200 |
---|---|---|
committer | Wojtek Kosior <koszko@koszko.org> | 2021-07-31 19:36:19 +0200 |
commit | 73e4d5a57a49066469a7444dde21b5be87f1bbbc (patch) | |
tree | 86fc699ab88933b347fde14d8838f1075e1f4b25 | |
parent | d87528f43161b184754a812cb8603b81bfec8867 (diff) | |
download | hachette_fixes_tmp-73e4d5a57a49066469a7444dde21b5be87f1bbbc.tar.gz hachette_fixes_tmp-73e4d5a57a49066469a7444dde21b5be87f1bbbc.zip |
add Odysee video search; Odysee mostly works now but unicode might cause problems
-rw-r--r-- | phttps___odysee.com.json | 2 |
1 files changed, 1 insertions, 1 deletions
diff --git a/phttps___odysee.com.json b/phttps___odysee.com.json index 0f4c1ed..bbf6384 100644 --- a/phttps___odysee.com.json +++ b/phttps___odysee.com.json @@ -1 +1 @@ -[{"sodysee":{"url":"","hash":"","text":"/**\n * Copyright 2021 Wojtek Kosior\n *\n * This program is free software; you can redistribute it\n * and/or modify it under the terms of either:\n * - the GNU General Public License as published by the Free\n * Software Foundation; either version 3 of the License, or (at\n * your option) any later version, or\n * - the \"A\" license: <https://koszko.org/alicense.txt>; explained\n * at: <https://koszko.org/en/articles/my-new-license.html>\n *\n * As additional permission under GNU GPL version 3 section 7, you\n * may distribute forms of that code without the copy of the GNU\n * GPL normally required by section 4, provided you include this\n * license notice and, in case of non-source distribution, a URL\n * through which recipients can access the Corresponding Source.\n * If you modify file(s) with this exception, you may extend this\n * exception to your version of the file(s), but you are not\n * obligated to do so. If you do not wish to do so, delete this\n * exception statement from your version. If you delete this\n * exception statement from all source files in the program, then\n * also delete it here.\n *\n * As a special exception to the GPL, any HTML file which merely\n * makes function calls to this code, and for that purpose\n * includes it by reference shall be deemed a separate work for\n * copyright law purposes. If you modify this code, you may extend\n * this exception to your version of the code, but you are not\n * obligated to do so. If you do not wish to do so, delete this\n * exception statement from your version.\n */\n\n/* use with https://odysee.com/*** */\n\n/* If we're on a video page, show the video. */\n\nlet data = null;\n\nfunction process_json_script(json_script)\n{\n try {\n\tdata = JSON.parse(json_script.textContent);\n } catch (e) {\n\tconsole.log(\"Error parsing content data\", e);\n }\n}\n\nfor (const json_script of document.querySelectorAll(\"head script\")) {\n if ([\"blocked-type\", \"type\"].map(a => json_script.getAttribute(a))\n\t.includes(\"application/ld+json\"))\n\tprocess_json_script(json_script);\n}\n\nconst body = document.createElement(\"body\");\n\nif (data) {\n if (data[\"@type\"] === \"VideoObject\" && data.contentUrl) {\n\tconst video = document.createElement(\"video\");\n\tconst source = document.createElement(\"source\");\n\n\tsource.src = data.contentUrl;\n\n\tvideo.setAttribute(\"width\", \"100%\");\n\tvideo.setAttribute(\"height\", \"auto\");\n\tvideo.setAttribute(\"controls\", \"\");\n\n\tvideo.appendChild(source);\n\n\tbody.appendChild(video);\n }\n\n if (data.name) {\n\tconst h1 = document.createElement(\"h1\");\n\n\th1.textContent = data.name;\n\th1.setAttribute(\"style\", \"color: #555;\");\n\n\tbody.appendChild(h1);\n }\n\n if (data.uploadDate) {\n\ttry {\n\t const date = new Date(data.uploadDate).toString();\n\t const date_div = document.createElement(\"div\");\n\n\t date_div.textContent = `Uploaded: ${date}`;\n\t date_div.setAttribute(\"style\", \"font-size: 8; font-weight: bold;\");\n\n\t body.appendChild(date_div);\n\t} catch(e) {\n\t console.log(\"Error parsing content upload date\", e);\n\t}\n }\n\n if (data.description) {\n\tconst description_div = document.createElement(\"div\");\n\n\tdescription_div.textContent = data.description;\n\n\tbody.appendChild(description_div);\n }\n}\n\n/* Show search. */\n\nconst search_input = document.createElement(\"input\");\nconst search_submit = document.createElement(\"button\");\nconst search_form = document.createElement(\"form\");\nconst error_div = document.createElement(\"div\");\n\nsearch_submit.textContent = \"Search Odysee\";\n\nsearch_form.setAttribute(\"style\", \"margin: 15px 0 0 0;\");\n\nsearch_form.appendChild(search_input);\nsearch_form.appendChild(search_submit);\n\nerror_div.textContent = \"Failed to perform search :c\";\nerror_div.setAttribute(\"style\", \"display: none;\");\n\nbody.appendChild(search_form);\nbody.appendChild(error_div);\n\n/* Replace the UI. */\n\ndocument.documentElement.replaceChild(body, document.body);\n\n/* Add the logic of performing search and showing results. */\n\nlet results_div = null;\n\nfunction ajax_callback(xhttp, cb)\n{\n if (xhttp.readyState === 4)\n\tcb(xhttp.response);\n}\n\nfunction show_search_entries(new_results_div, response)\n{\n try {\n\tvar response_data = JSON.parse(response);\n } catch (e) {\n\terror_div.setAttribute(\"style\", \"color: #b44;\");\n\tconsole.log(\"Failed to parse search response :c\", e);\n\treturn;\n }\n\n if (results_div)\n\tresults_div.remove();\n\n error_div.setAttribute(\"style\", \"display: none;\");\n\n results_div = new_results_div;\n new_results_div.textContent = response;\n\n body.appendChild(results_div);\n}\n\nfunction get_search_entries(new_results_div, query)\n{\n const callback = r => show_search_entries(new_results_div, r);\n const url = `https://lighthouse.odysee.com/search?s=${encodeURIComponent(query)}&size=20&from=0&claimType=file,channel&nsfw=false&free_only=true`;\n\n xhttp = new XMLHttpRequest();\n xhttp.onreadystatechange = () => ajax_callback(xhttp, callback);\n xhttp.open(\"GET\", url, true);\n try {\n\txhttp.send();\n } catch(e) {\n\tconsole.log(\"Failed to query search server :c\", e);\n\terror_div.setAttribute(\"style\", \"color: #b44;\");\n }\n}\n\nfunction search(event)\n{\n if (event)\n\tevent.preventDefault();\n console.log(\"search\", search_input.value);\n if (!/[^\\s]/.test(search_input.value))\n\treturn;\n\n const new_results_div = document.createElement(\"div\");\n\n get_search_entries(new_results_div, search_input.value);\n}\n\nsearch_form.addEventListener(\"submit\", search);\n\n\nconst match = /^[^?]*search\\?q=([^&]+)/.exec(document.URL)\nif (match) {\n search_input.value = decodeURIComponent(match[1]);\n search();\n}\n"}},{"phttps://odysee.com":{"components":["s","odysee"],"allow":false}},{"phttps://odysee.com/***":{"components":["s","odysee"],"allow":false}}] +[{"sodysee":{"url":"","hash":"","text":"/**\n * Copyright 2021 Wojtek Kosior\n *\n * This program is free software; you can redistribute it\n * and/or modify it under the terms of either:\n * - the GNU General Public License as published by the Free\n * Software Foundation; either version 3 of the License, or (at\n * your option) any later version, or\n * - the \"A\" license: <https://koszko.org/alicense.txt>; explained\n * at: <https://koszko.org/en/articles/my-new-license.html>\n *\n * As additional permission under GNU GPL version 3 section 7, you\n * may distribute forms of that code without the copy of the GNU\n * GPL normally required by section 4, provided you include this\n * license notice and, in case of non-source distribution, a URL\n * through which recipients can access the Corresponding Source.\n * If you modify file(s) with this exception, you may extend this\n * exception to your version of the file(s), but you are not\n * obligated to do so. If you do not wish to do so, delete this\n * exception statement from your version. If you delete this\n * exception statement from all source files in the program, then\n * also delete it here.\n *\n * As a special exception to the GPL, any HTML file which merely\n * makes function calls to this code, and for that purpose\n * includes it by reference shall be deemed a separate work for\n * copyright law purposes. If you modify this code, you may extend\n * this exception to your version of the code, but you are not\n * obligated to do so. If you do not wish to do so, delete this\n * exception statement from your version.\n */\n\n/* use with https://odysee.com/*** */\n\n/* If we're on a video page, show the video. */\n\nlet data = null;\n\nfunction process_json_script(json_script)\n{\n try {\n\tdata = JSON.parse(json_script.textContent);\n } catch (e) {\n\tconsole.log(\"Error parsing content data\", e);\n }\n}\n\nfor (const json_script of document.querySelectorAll(\"head script\")) {\n if ([\"blocked-type\", \"type\"].map(a => json_script.getAttribute(a))\n\t.includes(\"application/ld+json\"))\n\tprocess_json_script(json_script);\n}\n\nconst body = document.createElement(\"body\");\n\nif (data) {\n if (data[\"@type\"] === \"VideoObject\" && data.contentUrl) {\n\tconst video = document.createElement(\"video\");\n\tconst source = document.createElement(\"source\");\n\n\tsource.src = data.contentUrl;\n\n\tvideo.setAttribute(\"width\", \"100%\");\n\tvideo.setAttribute(\"height\", \"auto\");\n\tvideo.setAttribute(\"controls\", \"\");\n\n\tvideo.appendChild(source);\n\n\tbody.appendChild(video);\n }\n\n if (data.name) {\n\tconst h1 = document.createElement(\"h1\");\n\n\th1.textContent = data.name;\n\th1.setAttribute(\"style\", \"color: #555;\");\n\n\tbody.appendChild(h1);\n }\n\n if (data.uploadDate) {\n\ttry {\n\t const date = new Date(data.uploadDate).toString();\n\t const date_div = document.createElement(\"div\");\n\n\t date_div.textContent = `Uploaded: ${date}`;\n\t date_div.setAttribute(\"style\", \"font-size: 14px; font-weight: bold;\");\n\n\t body.appendChild(date_div);\n\t} catch(e) {\n\t console.log(\"Error parsing content upload date\", e);\n\t}\n }\n\n if (data.description) {\n\tconst description_div = document.createElement(\"div\");\n\n\tdescription_div.textContent = data.description;\n\n\tbody.appendChild(description_div);\n }\n}\n\n/* Show search. */\n\nconst search_input = document.createElement(\"input\");\nconst search_submit = document.createElement(\"button\");\nconst search_form = document.createElement(\"form\");\nconst error_div = document.createElement(\"div\");\n\nsearch_submit.textContent = \"Search Odysee\";\n\nsearch_form.setAttribute(\"style\", \"margin: 15px 0 0 0;\");\n\nsearch_form.appendChild(search_input);\nsearch_form.appendChild(search_submit);\n\nerror_div.textContent = \"Failed to perform search :c\";\nerror_div.setAttribute(\"style\", \"display: none;\");\n\nbody.appendChild(search_form);\nbody.appendChild(error_div);\n\n/* Replace the UI. */\n\ndocument.documentElement.replaceChild(body, document.body);\n\n/* Add the logic of performing search and showing results. */\n\nfunction show_error()\n{\n error_div.setAttribute(\"style\", \"color: #b44;\");\n}\n\nfunction clear_error()\n{\n error_div.setAttribute(\"style\", \"display: none;\");\n}\n\nlet results_div = null;\nconst load_more_but = document.createElement(\"button\");\n\nload_more_but.textContent = \"Load more\";\n\nfunction ajax_callback(xhttp, cb)\n{\n if (xhttp.readyState === 4)\n\tcb(xhttp.response);\n}\n\nfunction perform_ajax(method, url, callback, data)\n{\n const xhttp = new XMLHttpRequest();\n xhttp.onreadystatechange = () => ajax_callback(xhttp, callback);\n xhttp.open(method, url, true);\n try {\n\txhttp.send(data);\n } catch(e) {\n\tconsole.log(\"Failed to query search server :c\", e);\n\tshow_error();\n\tenable_search_form();\n }\n}\n\nfunction show_search_entries(new_results_div, response)\n{\n try {\n\tvar results = Object.values(JSON.parse(response).result);\n } catch (e) {\n\tconsole.log(\"Failed to parse search response :c\",\n\t\t \"Bad response format from api.na-backend.odysee.com.\");\n\terror_div.show_error();\n\treturn;\n }\n\n for (const result of results) {\n\ttry {\n\t if (result.value_type !== \"stream\")\n\t\tcontinue;\n\n\t const channel_name = encodeURIComponent(result.signing_channel.name);\n\t const channel_id = result.signing_channel.claim_id[0];\n\t const video_name = encodeURIComponent(result.name);\n\t const video_id = result.claim_id[0];\n\n\t const result_a = document.createElement(\"a\");\n\t const thumbnail = document.createElement(\"img\");\n\t const title_span = document.createElement(\"span\");\n\t const uploader_div = document.createElement(\"div\");\n\t const description_div = document.createElement(\"div\");\n\n\t thumbnail.setAttribute(\"style\", \"width: 100px; height: auto;\");\n\t thumbnail.setAttribute(\"alt\", result.value.thumbnail.url);\n\t thumbnail.src = result.value.thumbnail.url;\n\n\t title_span.setAttribute(\"style\", \"font-weight: bold;\");\n\t title_span.textContent = result.value.title;\n\n\t uploader_div.setAttribute(\"style\", \"margin-left: 5px; font-size: 21px; color: #555;\");\n\t uploader_div.textContent = result.signing_channel.name;\n\n\t description_div.textContent = result.value.description;\n\n\t result_a.setAttribute(\"style\", \"display: block; width: 100%; text-decoration: none; color: #333; margin: 8px; border-style: solid; border-width: 3px 0 0 0; border-color: #7aa;\");\n\t result_a.href = `https://odysee.com/${channel_name}:${channel_id}/${video_name}:${video_id}`;\n\n\t if (result.value.thumbnail.url)\n\t\tresult_a.appendChild(thumbnail);\n\t result_a.appendChild(title_span);\n\t result_a.appendChild(uploader_div);\n\t result_a.appendChild(description_div);\n\n\t new_results_div.appendChild(result_a);\n\t}\n\tcatch(e) {\n\t console.log(e);\n\t}\n }\n\n clear_error();\n\n if (results_div)\n\tresults_div.remove();\n\n results_div = new_results_div;\n\n body.appendChild(results_div);\n body.appendChild(load_more_but);\n\n enable_search_form();\n}\n\nfunction get_detailed_search_entries(new_results_div, response)\n{\n /* TODO: Simplify JSON handling using sanitize_JSON.js from Hachette. */\n try {\n\tvar response_data = JSON.parse(response);\n\tif (!Array.isArray(response_data))\n\t throw \"Bad response format from lighthouse.odysee.com.\";\n } catch (e) {\n\tshow_error();\n\tconsole.log(\"Failed to parse search response :c\", e);\n\tenable_search_form();\n\treturn;\n }\n\n const callback = r => show_search_entries(new_results_div, r);\n const url = \"https://api.na-backend.odysee.com/api/v1/proxy?m=resolve\";\n const lbry_urls = [];\n\n for (const search_result of response_data) {\n\tif (!search_result.claimId || !search_result.name)\n\t continue;\n\tlbry_urls.push(`lbry://${search_result.name}#${search_result.claimId}`);\n }\n\n const payload = {\n\tjsonrpc: \"2.0\",\n\tmethod: \"resolve\",\n\tparams: {\n\t urls: lbry_urls,\n\t include_purchase_receipt: true\n\t},\n\tid: Math.round(Math.random() * 10**14)\n };\n\n console.log(\"payload\", payload);\n\n perform_ajax(\"POST\", url, callback, JSON.stringify(payload));\n}\n\nfunction get_search_entries(new_results_div, query, from)\n{\n const callback = r => get_detailed_search_entries(new_results_div, r);\n const url = `https://lighthouse.odysee.com/search?s=${encodeURIComponent(query)}&size=20&from=${from}&claimType=file,channel&nsfw=false&free_only=true`;\n\n new_results_div.setAttribute(\"data-fetched\", parseInt(from) + 20);\n\n perform_ajax(\"GET\", url, callback);\n}\n\nfunction search(event)\n{\n if (event)\n\tevent.preventDefault();\n console.log(\"search\", search_input.value);\n if (!/[^\\s]/.test(search_input.value))\n\treturn;\n\n disable_search_form();\n\n const new_results_div = document.createElement(\"div\");\n\n new_results_div.setAttribute(\"data-query\", search_input.value);\n\n get_search_entries(new_results_div, search_input.value, 0);\n}\n\nfunction search_more()\n{\n get_search_entries(results_div, results_div.getAttribute(\"data-query\"),\n\t\t results_div.getAttribute(\"data-fetched\"));\n}\n\nload_more_but.addEventListener(\"click\", search_more);\n\nfunction enable_search_form()\n{\n search_form.addEventListener(\"submit\", search);\n search_submit.removeAttribute(\"disabled\");\n if (results_div)\n\tload_more_but.removeAttribute(\"disabled\");\n}\n\nfunction disable_search_form()\n{\n search_form.removeEventListener(\"submit\", search);\n search_submit.setAttribute(\"disabled\", \"\");\n load_more_but.setAttribute(\"disabled\", \"\");\n}\n\n\nenable_search_form();\n\n\nconst match = /^[^?]*search\\?q=([^&]+)/.exec(document.URL)\nif (match) {\n search_input.value = decodeURIComponent(match[1]);\n search();\n}\n"}},{"phttps://odysee.com":{"components":["s","odysee"],"allow":false}},{"phttps://odysee.com/***":{"components":["s","odysee"],"allow":false}}] |