Compare commits

..

No commits in common. "V2_rust" and "master" have entirely different histories.

28 changed files with 1386 additions and 4373 deletions

118
.gitignore vendored
View File

@ -1,3 +1,99 @@
# ---> Python
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
#*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# dotenv
.env
# virtualenv
.venv
venv/
ENV/
# Spyder project settings
.spyderproject
# Rope project settings
.ropeproject
# Images
*.jpg
*.jpeg
@ -6,25 +102,3 @@
# Others
*.db
*.db:encryptable
# ---> Rust
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
# IDE
.idea
# Git
.git
# Dist
/dist

0
.gitmodules vendored
View File

4033
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +0,0 @@
[package]
name = "simple_panorama_viewer"
version = "2.0.0"
edition = "2021"
[dependencies]
anyhow = "1.0"
log = "0.4"
simplelog = "0.12"
wry = "0.34"
native-dialog = "0.7"
rust-embed = "8"
directories = "5"
clap = "4"
warp = "0.3"
tokio = { version = "1", features = ["full"] }
portpicker = "0.1"
rand = "0.8"

View File

@ -1,24 +0,0 @@
#!/usr/bin/env -S just --justfile
set windows-shell := ["powershell.exe", "-NoLogo", "-Command"]
alias b := build
build:
cargo build
dist_linux:
rm -Rf ./dist
mkdir dist
cargo build --release
smc bin_linux files
smc sources
dist_win:
coreutils rm -Rf ./dist
coreutils mkdir dist
cargo build --release
smc bin_win files
smc sources
pandoc -i LICENSE.md -s -o LICENSE.rtf
Start-Process -FilePath "C:\Program Files (x86)\Inno Setup 6\compil32.exe" -ArgumentList "/cc", "win-setup.iss" -Wait
Start-Process -WorkingDirectory dist coreutils -ArgumentList sha512sum, -b, "SimplePanoramaViewer-2.0.0-x86_64-pc-windows-msvc-setup.exe" -RedirectStandardOutput "dist/SimplePanoramaViewer-2.0.0-x86_64-pc-windows-msvc-setup.exe.sha512sum"

View File

@ -1,7 +1,7 @@
The MIT License (MIT)
=====================
Copyright © 2018-2022 Rémi BERTHO
Copyright © 2018 Rémi BERTHO
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation

View File

@ -1,3 +1,3 @@
# SimplePanoramaViewer
This is a simple panorama viewer based on [Photo Sphere Viewer](http://photo-sphere-viewer.js.org) which is based on [Three.js](https://threejs.org/).
This is a simple panorama viewer based on [Photo Sphere Viewer](http://photo-sphere-viewer.js.org) which is based on [Three.js](https://threejs.org/), [D.js](https://malko.github.io/D.js/), [uEvenet](https://github.com/mistic100/uEvent) and [doT.js](https://olado.github.io/doT/).

49
SimplePanoramaViewer Executable file
View File

@ -0,0 +1,49 @@
#!/usr/bin/env python3
import os
import sys
from os import path
from pathlib import Path
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtWebEngine import *
from PyQt5.QtWebEngineWidgets import *
app = QApplication(sys.argv)
app.setApplicationName("SimplePanoramaViewer")
if len(sys.argv) < 2:
img_dialog = QFileDialog.getOpenFileName(None, "Open image", str(Path.home()), "JPG Images (*.jpg *.JPG *.jpeg *.pjpeg *.pjpg *.PJPG)")
img = img_dialog[0]
else:
img = path.abspath(sys.argv[1])
if img == "":
exit(0)
img = img.replace("\\", "/")
img = img.replace("'", "\\'")
if getattr( sys, 'frozen', False ) :
exe_dir = path.realpath(sys._MEIPASS)
else :
exe_dir = path.dirname(path.realpath(__file__))
os.chdir(exe_dir)
try:
with open("html/index.html", "r", encoding="utf8") as file:
html = file.read()
except OSError as exception:
print("Error: HTML file not found: " + str(exception))
html = html.replace("__IMG_PATH__", img)
web = QWebEngineView()
url = QUrl.fromLocalFile(exe_dir + "/index.html")
web.setHtml(html,url)
web.setWindowTitle(img)
web.showMaximized()
web.show()
sys.exit(app.exec_())

View File

@ -0,0 +1,39 @@
# -*- mode: python -*-
block_cipher = None
added_files = [
( 'README.md', '.' ),
( 'LICENSE.md', '.' ),
( 'css', 'css' ),
( 'html', 'html' ),
( 'js', 'js' )
]
a = Analysis(['SimplePanoramaViewer'],
pathex=['./SimplePanoramaViewer'],
binaries=[],
datas=added_files,
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='SimplePanoramaViewer',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
runtime_tmpdir=None,
console=False )

View File

@ -0,0 +1,43 @@
# -*- mode: python -*-
block_cipher = None
added_files = [
( 'README.md', '.' ),
( 'LICENSE.md', '.' ),
( 'css', 'css' ),
( 'html', 'html' ),
( 'js', 'js' )
]
a = Analysis(['SimplePanoramaViewer'],
pathex=['./SimplePanoramaViewer'],
binaries=[],
datas=added_files,
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='SimplePanoramaViewer',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False )
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
name='SimplePanoramaViewer')

7
css/photo-sphere-viewer.min.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,43 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Panorama viewer</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/photo-sphere-viewer.min.css">
<style>
html, body {
width: 100%; height: 100%; overflow: hidden; margin: 0; padding:
0;
}
#photosphere {
width: 100%;
height: 100%;
}
</style>
</head>
<!-- the viewer container must have a defined size -->
<div id="photosphere" style="width: 100vw; height: 100vh;"></div>
<script src="js/three.min.js"></script>
<script src="js/photo-sphere-viewer-core.min.js"></script>
<script>
var PSV = new PhotoSphereViewer.Viewer({
container: 'photosphere',
panorama: 'img',
caption: 'Panorama displayed with Photo Sphere Viewer V5.5.0',
defaultZoomLvl: 40,
minFov: 5,
maxFov: 75,
navbar: [
'zoom',
'caption'
]
});
</script>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

20
generate.ps1 Normal file
View File

@ -0,0 +1,20 @@
$version="1.1"
if (Test-Path dist\"SimplePanoramaViewer-$version-win64.exe")
{
Remove-Item dist\"SimplePanoramaViewer-$version-win64.exe" -Force -Recurse
}
if (Test-Path dist\SimplePanoramaViewer)
{
Remove-Item dist\SimplePanoramaViewer -Force -Recurse
}
pandoc -i LICENSE.md -s -o LICENSE.rtf
pyinstaller SimplePanoramaViewer_folder.spec
Start-Process -FilePath "C:\Program Files (x86)\Inno Setup 6\compil32.exe" -ArgumentList "/cc", "win-setup.iss" -Wait
Remove-Item dist\SimplePanoramaViewer -Force -Recurse
Get-FileHash "dist/SimplePanoramaViewer-$version-win64-setup.exe" -Algorithm SHA512 | Select-Object -ExpandProperty Hash | Out-File "dist/SimplePanoramaViewer-$version-win64-setup.exe.sha512sum"
pyinstaller SimplePanoramaViewer_file.spec
Rename-Item -Path "dist\SimplePanoramaViewer.exe" -NewName "SimplePanoramaViewer-$version-win64.exe"
Get-FileHash "dist/SimplePanoramaViewer-$version-win64.exe" -Algorithm SHA512 | Select-Object -ExpandProperty Hash | Out-File "dist/SimplePanoramaViewer-$version-win64.exe.sha512sum"

66
html/index.html Normal file
View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Panorama viewer</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/photo-sphere-viewer.min.css">
<style>
html, body {
width: 100%; height: 100%; overflow: hidden; margin: 0; padding:
0;
}
#photosphere {
width: 100%;
height: 100%;
}
.psv-button.custom-button {
font-size: 22px;
line-height: 20px;
}
.demo-label {
color: white;
font-size: 20px;
font-family: Helvetica, sans-serif;
text-align: center;
padding: 5px;
border: 1px solid white;
background: rgba(0,0,0,0.4);
}
</style>
</head>
<body>
<div id="photosphere"></div>
<script src="js/three.min.js"></script>
<script src="js/D.min.js"></script>
<script src="js/uevent.min.js"></script>
<script src="js/doT.min.js"></script>
<script src="js/photo-sphere-viewer.min.js"></script>
<h1>Panorama</h1>>
<script>
var PSV = new PhotoSphereViewer({
container: 'photosphere',
panorama: '__IMG_PATH__',
caption: 'Panorama displayed with Photo Sphere Viewer V3.5.1',
time_anim: false,
default_fov: 40,
min_fov: 5,
max_fov: 75,
navbar: [
'autorotate',
'zoom',
'caption'
]
});
</script>
</body>
</html>

View File

@ -4,9 +4,9 @@ Type=Application
Name=SimplePanoramaViewer
Comment=Simple web based panoram viewer
Icon=emblem-photos
Exec=simple_panorama_viewer
Exec=SimplePanoramaViewer
NoDisplay=false
Categories=Graphics;Viewer
StartupNotify=false
Terminal=false
MimeType=image/jpeg;image/pjpg;image/webp;image/avif
MimeType=image/jpeg;image/pjpg

10
install/pjpg.xml Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="image/pjpg">
<comment>Panoramic JPEG image</comment>
<comment xml:lang="fr">Image JPEG panoramique</comment>
<glob pattern="*.pjpeg"/>
<glob pattern="*.pjpg"/>
<glob pattern="*.pjpe"/>
</mime-type>
</mime-info>

2
js/D.min.js vendored Normal file
View File

@ -0,0 +1,2 @@
/* (c) Jonathan Gotti - licence: https://github.com/malko/d.js/LICENCE.txt @version 0.7.5*/
!function(a){"use strict";function b(a){l(function(){throw a})}function c(b){return this.then(b,a)}function d(b){return this.then(a,b)}function e(b,c){return this.then(function(a){return m(b)?b.apply(null,n(a)?a:[a]):v.onlyFuncs?a:b},c||a)}function f(a){function b(){a()}return this.then(b,b),this}function g(a){return this.then(function(b){return m(a)?a.apply(null,n(b)?b.splice(0,0,void 0)&&b:[void 0,b]):v.onlyFuncs?b:a},function(b){return a(b)})}function h(c){return this.then(a,c?function(a){throw c(a),a}:b)}function i(a,b){var c=q(a);if(1===c.length&&n(c[0])){if(!c[0].length)return v.fulfilled([]);c=c[0]}var d=[],e=v(),f=c.length;if(f)for(var g=function(a){c[a]=v.promisify(c[a]),c[a].then(function(g){d[a]=b?c[a]:g,--f||e.resolve(d)},function(g){b?(d[a]=c[a],--f||e.resolve(d)):e.reject(g)})},h=0,i=f;i>h;h++)g(h);else e.resolve(d);return e.promise}function j(a,b){return a.then(m(b)?b:function(){return b})}function k(a){var b=q(a);1===b.length&&n(b[0])&&(b=b[0]);for(var c=v(),d=0,e=b.length,f=v.resolved();e>d;d++)f=j(f,b[d]);return c.resolve(f),c.promise}var l,m=function(a){return"function"==typeof a},n=function(a){return Array.isArray?Array.isArray(a):a instanceof Array},o=function(a){return!(!a||!(typeof a).match(/function|object/))},p=function(b){return b===!1||b===a||null===b},q=function(a,b){return[].slice.call(a,b)},r="undefined",s=typeof TypeError===r?Error:TypeError;if(typeof process!==r&&process.nextTick)l=process.nextTick;else if(typeof MessageChannel!==r){var t=new MessageChannel,u=[];t.port1.onmessage=function(){u.length&&u.shift()()},l=function(a){u.push(a),t.port2.postMessage(0)}}else l=function(a){setTimeout(a,0)};var v=function(b){function i(){if(0!==r){var a,b=t,c=0,d=b.length,e=~r?0:1;for(t=[];d>c;c++)(a=b[c][e])&&a(n)}}function j(a){function b(a){return function(b){return c?void 0:(c=!0,a(b))}}var c=!1;if(r)return this;try{var d=o(a)&&a.then;if(m(d)){if(a===u)throw new s("Promise can't resolve itself");return d.call(a,b(j),b(k)),this}}catch(e){return b(k)(e),this}return q(function(){n=a,r=1,i()}),this}function k(a){return r||q(function(){try{throw a}catch(b){n=b}r=-1,i()}),this}var n,q=(a!==b?b:v.alwaysAsync)?l:function(a){a()},r=0,t=[],u={then:function(a,b){var c=v();return t.push([function(b){try{p(a)?c.resolve(b):c.resolve(m(a)?a(b):v.onlyFuncs?b:a)}catch(d){c.reject(d)}},function(a){if((p(b)||!m(b)&&v.onlyFuncs)&&c.reject(a),b)try{c.resolve(m(b)?b(a):b)}catch(d){c.reject(d)}}]),0!==r&&q(i),c.promise},success:c,error:d,otherwise:d,apply:e,spread:e,ensure:f,nodify:g,rethrow:h,isPending:function(){return 0===r},getStatus:function(){return r}};return u.toSource=u.toString=u.valueOf=function(){return n===a?this:n},{promise:u,resolve:j,fulfill:j,reject:k}};if(v.deferred=v.defer=v,v.nextTick=l,v.alwaysAsync=!0,v.onlyFuncs=!0,v.resolve=v.resolved=v.fulfilled=function(a){return v(!0).resolve(a).promise},v.reject=v.rejected=function(a){return v(!0).reject(a).promise},v.wait=function(a){var b=v();return setTimeout(b.resolve,a||0),b.promise},v.delay=function(a,b){var c=v();return setTimeout(function(){try{c.resolve(m(a)?a.apply(null):a)}catch(b){c.reject(b)}},b||0),c.promise},v.promisify=function(a){return a&&m(a.then)?a:v.resolved(a)},v.all=function(){return i(arguments,!1)},v.resolveAll=function(){return i(arguments,!0)},v.sequence=function(){return k(arguments)},v.nodeCapsule=function(a,b){return b||(b=a,a=void 0),function(){var c=v(),d=q(arguments);d.push(function(a,b){a?c.reject(a):c.resolve(arguments.length>2?q(arguments,1):b)});try{b.apply(a,d)}catch(e){c.reject(e)}return c.promise}},"function"==typeof define&&define.amd)define("D.js",[],function(){return v});else if(typeof module!==r&&module.exports)module.exports=v;else if(typeof window!==r){var w=window.D;v.noConflict=function(){return window.D=w,v},window.D=v}}();

2
js/doT.min.js vendored Normal file
View File

@ -0,0 +1,2 @@
/* Laura Doktorova https://github.com/olado/doT */
!function(){"use strict";function e(n,t,r){return("string"==typeof t?t:t.toString()).replace(n.define||a,function(e,t,o,a){return 0===t.indexOf("def.")&&(t=t.substring(4)),t in r||(":"===o?(n.defineParams&&a.replace(n.defineParams,function(e,n,o){r[t]={arg:n,text:o}}),t in r||(r[t]=a)):new Function("def","def['"+t+"']="+a)(r)),""}).replace(n.use||a,function(t,o){n.useParams&&(o=o.replace(n.useParams,function(e,n,t,o){if(r[t]&&r[t].arg&&o){var a=(t+":"+o).replace(/'|\\/g,"_");return r.__exp=r.__exp||{},r.__exp[a]=r[t].text.replace(new RegExp("(^|[^\\w$])"+r[t].arg+"([^\\w$])","g"),"$1"+o+"$2"),n+"def.__exp['"+a+"']"}}));var a=new Function("def","return "+o)(r);return a?e(n,a,r):a})}function n(e){return e.replace(/\\('|\\)/g,"$1").replace(/[\r\t\n]/g," ")}var t,r={name:"doT",version:"1.1.1",templateSettings:{evaluate:/\{\{([\s\S]+?(\}?)+)\}\}/g,interpolate:/\{\{=([\s\S]+?)\}\}/g,encode:/\{\{!([\s\S]+?)\}\}/g,use:/\{\{#([\s\S]+?)\}\}/g,useParams:/(^|[^\w$])def(?:\.|\[[\'\"])([\w$\.]+)(?:[\'\"]\])?\s*\:\s*([\w$\.]+|\"[^\"]+\"|\'[^\']+\'|\{[^\}]+\})/g,define:/\{\{##\s*([\w\.$]+)\s*(\:|=)([\s\S]+?)#\}\}/g,defineParams:/^\s*([\w$]+):([\s\S]+)/,conditional:/\{\{\?(\?)?\s*([\s\S]*?)\s*\}\}/g,iterate:/\{\{~\s*(?:\}\}|([\s\S]+?)\s*\:\s*([\w$]+)\s*(?:\:\s*([\w$]+))?\s*\}\})/g,varname:"it",strip:!0,append:!0,selfcontained:!1,doNotSkipEncoded:!1},template:void 0,compile:void 0,log:!0};r.encodeHTMLSource=function(e){var n={"&":"&#38;","<":"&#60;",">":"&#62;",'"':"&#34;","'":"&#39;","/":"&#47;"},t=e?/[&<>"'\/]/g:/&(?!#?\w+;)|<|>|"|'|\//g;return function(e){return e?e.toString().replace(t,function(e){return n[e]||e}):""}},t=function(){return this||(0,eval)("this")}(),"undefined"!=typeof module&&module.exports?module.exports=r:"function"==typeof define&&define.amd?define(function(){return r}):t.doT=r;var o={append:{start:"'+(",end:")+'",startencode:"'+encodeHTML("},split:{start:"';out+=(",end:");out+='",startencode:"';out+=encodeHTML("}},a=/$^/;r.template=function(c,i,u){i=i||r.templateSettings;var d,s,p=i.append?o.append:o.split,l=0,f=i.use||i.define?e(i,c,u||{}):c;f=("var out='"+(i.strip?f.replace(/(^|\r|\n)\t* +| +\t*(\r|\n|$)/g," ").replace(/\r|\n|\t|\/\*[\s\S]*?\*\//g,""):f).replace(/'|\\/g,"\\$&").replace(i.interpolate||a,function(e,t){return p.start+n(t)+p.end}).replace(i.encode||a,function(e,t){return d=!0,p.startencode+n(t)+p.end}).replace(i.conditional||a,function(e,t,r){return t?r?"';}else if("+n(r)+"){out+='":"';}else{out+='":r?"';if("+n(r)+"){out+='":"';}out+='"}).replace(i.iterate||a,function(e,t,r,o){return t?(l+=1,s=o||"i"+l,t=n(t),"';var arr"+l+"="+t+";if(arr"+l+"){var "+r+","+s+"=-1,l"+l+"=arr"+l+".length-1;while("+s+"<l"+l+"){"+r+"=arr"+l+"["+s+"+=1];out+='"):"';} } out+='"}).replace(i.evaluate||a,function(e,t){return"';"+n(t)+"out+='"})+"';return out;").replace(/\n/g,"\\n").replace(/\t/g,"\\t").replace(/\r/g,"\\r").replace(/(\s|;|\}|^|\{)out\+='';/g,"$1").replace(/\+''/g,""),d&&(i.selfcontained||!t||t._encodeHTML||(t._encodeHTML=r.encodeHTMLSource(i.doNotSkipEncoded)),f="var encodeHTML = typeof _encodeHTML !== 'undefined' ? _encodeHTML : ("+r.encodeHTMLSource.toString()+"("+(i.doNotSkipEncoded||"")+"));"+f);try{return new Function(i.varname,f)}catch(e){throw"undefined"!=typeof console&&console.log("Could not create a template function: "+f),e}},r.compile=function(e,n){return r.template(e,null,n)}}();

7
js/photo-sphere-viewer.min.js vendored Normal file

File diff suppressed because one or more lines are too long

927
js/three.min.js vendored Normal file

File diff suppressed because one or more lines are too long

7
js/uevent.min.js vendored Normal file
View File

@ -0,0 +1,7 @@
/*!
* uEvent 1.0.0 - to make any js object an event emitter
* Copyright 2011 Jerome Etienne (http://jetienne.com)
* Copyright 2015-2016 Damien "Mistic" Sorel (http://www.strangeplanet.fr)
* Licensed under MIT (http://opensource.org/licenses/MIT)
*/
!function(a,b){"undefined"!=typeof module&&module.exports?module.exports=b():"function"==typeof define&&define.amd?define([],b):a.uEvent=b()}(this,function(){"use strict";var a=function(){return!0},b=function(){return!1},c=function(){};return c.Event=function(a,b){var c=a,d=b;Object.defineProperties(this,{type:{get:function(){return c},set:function(a){},enumerable:!0},args:{get:function(){return d},set:function(a){},enumerable:!0}})},c.Event.prototype={constructor:c.Event,isDefaultPrevented:b,isPropagationStopped:b,preventDefault:function(){this.isDefaultPrevented=a},stopPropagation:function(){this.isPropagationStopped=a}},c.prototype={constructor:c,on:function(a,b){if(this.__events=this.__events||{},"object"==typeof a)for(var c in a)a.hasOwnProperty(c)&&(this.__events[c]=this.__events[c]||[],this.__events[c].push(a[c]));else a.split(" ").forEach(function(a){this.__events[a]=this.__events[a]||[],this.__events[a].push(b)},this);return this},off:function(a,b){if(this.__events=this.__events||{},"object"==typeof a){for(var c in a)if(a.hasOwnProperty(c)&&c in this.__events){var d=this.__events[c].indexOf(a[c]);-1!==d&&this.__events[c].splice(d,1)}}else a?a.split(" ").forEach(function(a){if(a in this.__events)if(b){var c=this.__events[a].indexOf(b);-1!==c&&this.__events[a].splice(c,1)}else this.__events[a].length=0},this):this.__events={};return this},once:function(a,b){if(this.__once=this.__once||{},"object"==typeof a)for(var c in a)a.hasOwnProperty(c)&&(this.__once[c]=this.__once[c]||[],this.__once[c].push(a[c]));else a.split(" ").forEach(function(a){this.__once[a]=this.__once[a]||[],this.__once[a].push(b)},this);return this},trigger:function(a){var b,d,e,f=Array.prototype.slice.call(arguments,1),g=new c.Event(a,f);if(f.push(g),this.__events&&a in this.__events)for(b=0,d=this.__events[a].length;d>b;b++)if(e=this.__events[a][b],"object"==typeof e?e.handleEvent(g):e.apply(this,f),g.isPropagationStopped())return g;if(this.__once&&a in this.__once){for(b=0,d=this.__once[a].length;d>b;b++)if(e=this.__once[a][b],"object"==typeof e?e.handleEvent(g):e.apply(this,f),g.isPropagationStopped())return delete this.__once[a],g;delete this.__once[a]}return g},change:function(a,b){var d,e,f,g=Array.prototype.slice.call(arguments,1),h=new c.Event(a,g);if(g.push(h),this.__events&&a in this.__events)for(d=0,e=this.__events[a].length;e>d;d++)if(g[0]=b,f=this.__events[a][d],b="object"==typeof f?f.handleEvent(h):f.apply(this,g),h.isPropagationStopped())return b;return b}},c.mixin=function(a,b){b=b||{},a="function"==typeof a?a.prototype:a,["on","off","once","trigger","change"].forEach(function(d){var e=b[d]||d;a[e]=c.prototype[d]}),Object.defineProperties(a,{__events:{value:null,writable:!0},__once:{value:null,writable:!0}})},c});

View File

@ -1,30 +0,0 @@
[profiles]
[profiles.bin_linux]
output = "./dist/SimplePanoramaViewer-2.0.0-x86_64-unknown-linux-gnu$EXT"
directory = "target/release/"
files = ["simple_panorama_viewer"]
container = "Tar"
compression = "Zstd"
compression_level = 15
signatures = ["Sha512"]
[profiles.bin_win]
output = "./dist/SimplePanoramaViewer-2.0.0-x86_64-pc-windows-msvc$EXT"
directory = "target/release/"
files = ["simple_panorama_viewer.exe"]
container = "Zip"
compression = "Deflate"
compression_level = 9
signatures = ["Sha512"]
[profiles.files]
files = ["README.md", "LICENSE.md"]
[profiles.sources]
paths = ["./"]
output = "./dist/SimplePanoramaViewer-2.0.0-src$EXT"
container = "Tar"
compression = "Zstd"
compression_level = 15
hidden = true
signatures = ["Sha512"]

View File

@ -1,164 +0,0 @@
#![windows_subsystem = "windows"]
use anyhow::{anyhow, bail, Result};
use clap::{Arg, Command};
use log::info;
use native_dialog::{FileDialog, MessageDialog};
use rand::distributions::DistString;
use rust_embed::RustEmbed;
use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode};
use std::{borrow::Cow, path::PathBuf, thread::JoinHandle};
use warp::Filter;
use wry::{
application::{
event::{Event, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
},
webview::{WebContext, WebViewBuilder},
};
#[derive(RustEmbed)]
#[folder = "embed/"]
struct Embed;
fn main() -> Result<()> {
// Init log
TermLogger::init(
LevelFilter::Info,
ConfigBuilder::new()
.set_time_offset_to_local()
.unwrap()
.build(),
TerminalMode::Mixed,
ColorChoice::Auto,
)?;
let cmd = Command::new("Simple panorama viewer")
.version(env!("CARGO_PKG_VERSION"))
.author(env!("CARGO_PKG_AUTHORS"))
.about(env!("CARGO_PKG_DESCRIPTION"))
.arg(
Arg::new("filename")
.value_parser(clap::value_parser!(PathBuf))
.help("Image path"),
)
.get_matches();
let img_path = if let Some(img_path) = cmd.get_one::<PathBuf>("filename") {
Cow::Borrowed(img_path)
} else {
let user_dirs = directories::UserDirs::new().unwrap();
let dir = if let Some(img_dir) = user_dirs.picture_dir() {
img_dir
} else {
user_dirs.home_dir()
};
Cow::Owned(
FileDialog::new()
.add_filter(
"Images",
&[
"jpg", "JPG", "jpeg", "pjpeg", "pjpg", "PJPG", "webp", "avif",
],
)
.set_location(dir)
.show_open_single_file()?
.ok_or_else(|| anyhow!("No file"))?,
)
};
if !img_path.exists() {
MessageDialog::new()
.set_title("Image error")
.set_text(&format!("Image `{}` does not exist", img_path.display()))
.set_type(native_dialog::MessageType::Error)
.show_alert()?;
bail!("File `{}` does not exist !", img_path.display());
}
info!("Run server");
let port = portpicker::pick_unused_port().ok_or_else(|| anyhow!("Cannot find ununsed port"))?;
let user_agent = rand::distributions::Alphanumeric.sample_string(&mut rand::thread_rng(), 32);
run_server(img_path.to_path_buf(), port, user_agent.clone());
info!("Create webview");
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_title("Simple panorama viewer")
.with_maximized(true)
.build(&event_loop)?;
let data_dir = directories::ProjectDirs::from("fr", "dalan", "SimplePanoramaViwer").unwrap();
let _webview = WebViewBuilder::new(window)?
.with_url(&format!("http://127.0.0.1:{}/index.html", port))?
.with_user_agent(&user_agent)
.with_web_context(&mut WebContext::new(Some(
data_dir.cache_dir().to_path_buf(),
)))
.build()?;
info!("Event loop");
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::NewEvents(StartCause::Init) => info!("Panorama open"),
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => {
info!("Close");
*control_flow = ControlFlow::Exit
}
_ => (),
}
});
}
fn run_server(img_path: PathBuf, port: u16, user_agent: String) -> JoinHandle<()> {
std::thread::spawn(move || {
info!("Create runtime");
let async_runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
info!("Create response");
let index = warp::path("index.html").map(|| {
info!("Request `index.html`");
let datas = get_file_data("index.html");
warp::reply::html(datas)
});
let css = warp::path!("css" / String).map(move |val: String| {
info!("Request css `{}`", &val);
let datas = get_file_data(&format!("css/{}", val));
warp::http::Response::builder().body(datas)
});
let js = warp::path!("js" / String).map(move |val: String| {
info!("Request js `{}`", &val);
let datas = get_file_data(&format!("js/{}", val));
warp::http::Response::builder().body(datas)
});
let img = warp::path!("img")
.and(warp::header("user-agent"))
.map(move |agent: String| {
if agent == user_agent {
std::fs::read(&img_path).unwrap()
} else {
Vec::new()
}
});
info!("Launch webserver");
async_runtime
.block_on(warp::serve(img.or(index).or(css).or(js)).run(([127, 0, 0, 1], port)));
})
}
fn get_file_data(filename: &str) -> String {
if let Some(file) = Embed::get(filename) {
std::str::from_utf8(file.data.as_ref())
.unwrap_or("")
.to_string()
} else {
"".to_string()
}
}

101
utility.sh Executable file
View File

@ -0,0 +1,101 @@
#!/bin/bash
print_help()
{
echo "SimplePanoramaViewer utility script"
echo
echo "Use:"
echo " bash utility.sh OPTION [PATH_TO_INSTALL_DIR]"
echo
echo "Options:"
echo " -i, --install Install SimplePanoramaViewer in PATH_TO_INSTALL_DIR"
echo " -u, --uninstall Uninstall SimplePanoramaViewer from PATH_TO_INSTALL_DIR"
echo " -p, --package Generate a package for SimplePanoramaViewer"
}
check_dir()
{
if [ -z "$1" ]; then
echo "You need to pass a directory"
exit
fi
}
install()
{
DESTDIR=$1
check_dir "$DESTDIR"
if [ -z "$2" ]; then
PREFIX=$DESTDIR
else
PREFIX="$2"
DESTDIR=$DESTDIR$PREFIX
echo $DESTDIR
fi
mkdir -p "$DESTDIR/share/SimplePanoramaViewer/"
mkdir -p "$DESTDIR/bin/"
mkdir -p "$DESTDIR/share/applications"
mkdir -p "$DESTDIR/share/mime/image"
cp SimplePanoramaViewer "$DESTDIR/share/SimplePanoramaViewer"
cp -r css "$DESTDIR/share/SimplePanoramaViewer"
cp -r html "$DESTDIR/share/SimplePanoramaViewer"
cp -r js "$DESTDIR/share/SimplePanoramaViewer"
cp install/SimplePanoramaViewer.desktop "$DESTDIR/share/applications"
cp install/pjpg.xml "$DESTDIR/share/mime/image"
echo "#!/bin/bash
$PREFIX/share/SimplePanoramaViewer/SimplePanoramaViewer \"\$@\"" > "$DESTDIR/bin/SimplePanoramaViewer"
chmod a+x "$DESTDIR/bin/SimplePanoramaViewer"
chmod -R a+r "$DESTDIR/share/SimplePanoramaViewer"
}
uninstall()
{
DIR=$1
check_dir "$DIR"
rm "$DIR/bin/SimplePanoramaViewer"
rm -r "$DIR/share/SimplePanoramaViewer/"
rm "$DIR/share/applications/SimplePanoramaViewer.desktop"
rm "$DIR/share/mime/image/pjpg.xml"
}
package()
{
DIR="/tmp/SimplePanoramaViewer"
mkdir "$DIR"
cp SimplePanoramaViewer "$DIR"
cp -r css "$DIR"
cp -r html "$DIR"
cp -r js "$DIR"
chmod a+x "$DIR/SimplePanoramaViewer"
mkdir -p dist
cd /tmp || exit
tar Jcvf "SimplePanoramaViewer.tar.xz" "SimplePanoramaViewer"
cd - || exit
cp -f "$DIR.tar.xz" dist
rm -r "$DIR"
rm "$DIR.tar.xz"
}
case "$1" in
"-h" | "--help")
print_help
;;
"-i" | "--install")
install "$2" "$3"
;;
"-u" | "--uninstall")
uninstall "$2"
;;
"-p" | "--package")
package
;;
*)
print_help
;;
esac

View File

@ -2,10 +2,10 @@
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppName "SimplePanoramaViewer"
#define MyAppVersion "2.0.0"
#define MyAppVersion "1.1"
#define MyAppPublisher "Dalan"
#define MyAppURL "https://www.dalan.fr"
#define MyAppExeName "simple_panorama_viewer.exe"
#define MyAppExeName "SimplePanoramaViewer.exe"
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
@ -19,12 +19,12 @@ AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={commonpf}\{#MyAppName}
DefaultDirName={pf}\{#MyAppName}
DisableProgramGroupPage=yes
LicenseFile=LICENSE.rtf
OutputBaseFilename={#MyAppName}-{#MyAppVersion}-x86_64-pc-windows-msvc-setup
OutputBaseFilename={#MyAppName}-{#MyAppVersion}-win64-setup
OutputDir=dist
Compression=lzma2
Compression=lzma
SolidCompression=yes
ArchitecturesAllowed=x64
ArchitecturesInstallIn64BitMode=x64
@ -37,7 +37,7 @@ Name: "french"; MessagesFile: "compiler:Languages\French.isl"
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Files]
Source: "target\release\simple_panorama_viewer.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "dist\SimplePanoramaViewer\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
[Icons]
Name: "{commonprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"