Browse Source

Fix generic user page route, minor code cleanup

pull/14/head
Timothy Warren 6 months ago
parent
commit
11475187fc

+ 1
- 0
app/appConf/routes.php View File

@@ -181,6 +181,7 @@ $routes = [
181 181
 	'user_info' => [
182 182
 		'path' => '/user/{username}',
183 183
 		'controller' => 'user',
184
+		'action' => 'about',
184 185
 		'tokens' => [
185 186
 			'username' => '.*?'
186 187
 		]

+ 9
- 7
composer.json View File

@@ -37,28 +37,30 @@
37 37
   },
38 38
   "require-dev": {
39 39
 	"consolidation/robo": "~1.0",
40
+	"filp/whoops": "^2.1",
40 41
 	"henrikbjorn/lurker": "^1.1.0",
41 42
 	"pdepend/pdepend": "^2.2",
42 43
 	"phploc/phploc": "^4.0",
43 44
 	"phpmd/phpmd": "^2.4",
44
-	"phpstan/phpstan": "^0.9.1",
45
-	"phpunit/phpunit": "^6.0",
45
+	"phpstan/phpstan": "^0.10.5",
46
+	"phpunit/phpunit": "^7.4.3",
46 47
 	"roave/security-advisories": "dev-master",
47 48
 	"robmorgan/phinx": "^0.10.6",
48
-	"sebastian/phpcpd": "^3.0",
49
+	"sebastian/phpcpd": "^4.1.0",
49 50
 	"spatie/phpunit-snapshot-assertions": "^1.2.0",
50 51
 	"squizlabs/php_codesniffer": "^3.2.2",
51 52
 	"symfony/var-dumper": "^4.0.1",
52
-	"theseer/phpdox": "^0.11.0",
53
-    "filp/whoops": "^2.1"
53
+    "theseer/phpdox": "*"
54 54
   },
55 55
   "scripts": {
56 56
 	"build": "vendor/bin/robo build",
57
-	"build:css": "cd public && npm run build && cd ..",
57
+	"build:css": "cd public && npm run build:css && cd ..",
58
+	"build:js": "cd public && npm run build:js && cd ..",
58 59
 	"clean": "vendor/bin/robo clean",
59 60
 	"coverage": "phpdbg -qrr -- vendor/bin/phpunit -c build",
60 61
 	"phpstan": "phpstan analyse -l 4 -c phpstan.neon src tests ./console index.php",
61
-	"watch:css": "cd public && npm run watch",
62
+	"watch:css": "cd public && npm run watch:css",
63
+	"watch:js": "cd public && npm run watch:js",
62 64
 	"test": "vendor/bin/phpunit"
63 65
   },
64 66
   "scripts-descriptions": {

+ 14
- 13
public/js/scripts-authed.min.js View File

@@ -7,17 +7,18 @@ sel.addEventListener(event,listener,false)}function delegateEvent(sel,target,eve
7 7
 "GET")url+=url.match(/\?/)?ajaxSerialize(config.data):"?"+ajaxSerialize(config.data);request.open(method,url);request.onreadystatechange=function(){if(request.readyState===4){var responseText="";if(request.responseType==="json")responseText=JSON.parse(request.responseText);else responseText=request.responseText;if(request.status>299)config.error.call(null,request.status,responseText,request.response);else config.success.call(null,responseText,request.status)}};if(config.dataType==="json"){config.data=
8 8
 JSON.stringify(config.data);config.mimeType="application/json"}else config.data=ajaxSerialize(config.data);request.setRequestHeader("Content-Type",config.mimeType);switch(method){case "GET":request.send(null);break;default:request.send(config.data);break}};AnimeClient.get=function(url,data,callback){callback=callback===undefined?null:callback;if(callback===null){callback=data;data={}}return AnimeClient.ajax(url,{data:data,success:callback})};AnimeClient.on("header","click",".message",function(e){AnimeClient.hide(e.target)});
9 9
 AnimeClient.on("form.js-delete","submit",function(event){var proceed=confirm("Are you ABSOLUTELY SURE you want to delete this item?");if(proceed===false){event.preventDefault();event.stopPropagation()}});AnimeClient.on(".js-clear-cache","click",function(){AnimeClient.get("/cache_purge",function(){AnimeClient.showMessage("success","Successfully purged api cache")})});AnimeClient.on(".vertical-tabs input","change",function(event){var el=event.currentTarget.parentElement;var rect=el.getBoundingClientRect();
10
-var top=rect.top+window.pageYOffset;window.scrollTo({top:top,behavior:"smooth"})});AnimeClient.on("main","change",".big-check",function(e){var id=e.target.id;document.getElementById("mal_"+id).checked=true});function renderAnimeSearchResults(data){var results=[];data.forEach(function(x){var item=x.attributes;var titles=item.titles.reduce(function(prev,current){return prev+(current+"<br />")},[]);results.push('\n\t\t\t<article class="media search">\n\t\t\t\t<div class="name">\n\t\t\t\t\t<input type="radio" class="mal-check" id="mal_'+
11
-item.slug+'" name="mal_id" value="'+x.mal_id+'" />\n\t\t\t\t\t<input type="radio" class="big-check" id="'+item.slug+'" name="id" value="'+x.id+'" />\n\t\t\t\t\t<label for="'+item.slug+'">\n\t\t\t\t\t\t<picture width="220">\n\t\t\t\t\t\t\t<source srcset="/public/images/anime/'+x.id+'.webp" type="image/webp" />\n\t\t\t\t\t\t\t<source srcset="/public/images/anime/'+x.id+'.jpg" type="image/jpeg" />\n\t\t\t\t\t\t\t<img src="/public/images/anime/'+x.id+'.jpg" alt="" width="220" />\n\t\t\t\t\t\t</picture>\n\t\t\t\t\t\t\n\t\t\t\t\t\t<span class="name">\n\t\t\t\t\t\t\t'+
12
-item.canonicalTitle+"<br />\n\t\t\t\t\t\t\t<small>"+titles+'</small>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div class="table">\n\t\t\t\t\t<div class="row">\n\t\t\t\t\t\t<span class="edit">\n\t\t\t\t\t\t\t<a class="bracketed" href="/anime/details/'+item.slug+'">Info Page</a>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</article>\n\t\t')});return results.join("")}function renderMangaSearchResults(data){var results=[];data.forEach(function(x){var item=x.attributes;
13
-var titles=item.titles.reduce(function(prev,current){return prev+(current+"<br />")},[]);results.push('\n\t\t\t<article class="media search">\n\t\t\t\t<div class="name">\n\t\t\t\t\t<input type="radio" id="mal_'+item.slug+'" name="mal_id" value="'+x.mal_id+'" />\n\t\t\t\t\t<input type="radio" class="big-check" id="'+item.slug+'" name="id" value="'+x.id+'" />\n\t\t\t\t\t<label for="'+item.slug+'">\n\t\t\t\t\t\t<picture width="220">\n\t\t\t\t\t\t\t<source srcset="/public/images/manga/'+x.id+'.webp" type="image/webp" />\n\t\t\t\t\t\t\t<source srcset="/public/images/manga/'+
14
-x.id+'.jpg" type="image/jpeg" />\n\t\t\t\t\t\t\t<img src="/public/images/manga/'+x.id+'.jpg" alt="" width="220" />\n\t\t\t\t\t\t</picture>\n\t\t\t\t\t\t<span class="name">\n\t\t\t\t\t\t\t'+item.canonicalTitle+"<br />\n\t\t\t\t\t\t\t<small>"+titles+'</small>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div class="table">\n\t\t\t\t\t<div class="row">\n\t\t\t\t\t\t<span class="edit">\n\t\t\t\t\t\t\t<a class="bracketed" href="/manga/details/'+item.slug+'">Info Page</a>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</article>\n\t\t')});
15
-return results.join("")}var search=function(query){AnimeClient.$(".cssload-loader")[0].removeAttribute("hidden");AnimeClient.get(AnimeClient.url("/anime-collection/search"),{query:query},function(searchResults,status){searchResults=JSON.parse(searchResults);AnimeClient.$(".cssload-loader")[0].setAttribute("hidden","hidden");AnimeClient.$("#series-list")[0].innerHTML=renderAnimeSearchResults(searchResults.data)})};if(AnimeClient.hasElement(".anime #search"))AnimeClient.on("#search","keyup",AnimeClient.throttle(250,
16
-function(e){var query=encodeURIComponent(e.target.value);if(query==="")return;search(query)}));AnimeClient.on("body.anime.list","click",".plus-one",function(e){var parentSel=AnimeClient.closestParent(e.target,"article");var watchedCount=parseInt(AnimeClient.$(".completed_number",parentSel)[0].textContent,10)||0;var totalCount=parseInt(AnimeClient.$(".total_number",parentSel)[0].textContent,10);var title=AnimeClient.$(".name a",parentSel)[0].textContent;var data={id:parentSel.dataset.kitsuId,mal_id:parentSel.dataset.malId,
17
-data:{progress:watchedCount+1}};if(isNaN(watchedCount)||watchedCount===0)data.data.status="current";if(!isNaN(watchedCount)&&watchedCount+1===totalCount)data.data.status="completed";AnimeClient.show(AnimeClient.$("#loading-shadow")[0]);AnimeClient.ajax(AnimeClient.url("/anime/increment"),{data:data,dataType:"json",type:"POST",success:function(res){var resData=JSON.parse(res);if(resData.errors){AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.showMessage("error","Failed to update "+
18
-title+". ");AnimeClient.scrollToTop();return}if(resData.data.attributes.status==="completed")AnimeClient.hide(parentSel);AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.showMessage("success","Successfully updated "+title);AnimeClient.$(".completed_number",parentSel)[0].textContent=++watchedCount;AnimeClient.scrollToTop()},error:function(){AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.showMessage("error","Failed to update "+title+". ");AnimeClient.scrollToTop()}})});
19
-var search$1=function(query){AnimeClient.$(".cssload-loader")[0].removeAttribute("hidden");AnimeClient.get(AnimeClient.url("/manga/search"),{query:query},function(searchResults,status){searchResults=JSON.parse(searchResults);AnimeClient.$(".cssload-loader")[0].setAttribute("hidden","hidden");AnimeClient.$("#series-list")[0].innerHTML=renderMangaSearchResults(searchResults.data)})};if(AnimeClient.hasElement(".manga #search"))AnimeClient.on("#search","keyup",AnimeClient.throttle(250,function(e){var query=
20
-encodeURIComponent(e.target.value);if(query==="")return;search$1(query)}));AnimeClient.on(".manga.list","click",".edit-buttons button",function(e){var thisSel=e.target;var parentSel=AnimeClient.closestParent(e.target,"article");var type=thisSel.classList.contains("plus-one-chapter")?"chapter":"volume";var completed=parseInt(AnimeClient.$("."+type+"s_read",parentSel)[0].textContent,10)||0;var total=parseInt(AnimeClient.$("."+type+"_count",parentSel)[0].textContent,10);var mangaName=AnimeClient.$(".name",
21
-parentSel)[0].textContent;if(isNaN(completed))completed=0;var data={id:parentSel.dataset.kitsuId,mal_id:parentSel.dataset.malId,data:{progress:completed}};if(isNaN(completed)||completed===0)data.data.status="current";if(!isNaN(completed)&&completed+1===total)data.data.status="completed";data.data.progress=++completed;AnimeClient.show(AnimeClient.$("#loading-shadow")[0]);AnimeClient.ajax(AnimeClient.url("/manga/increment"),{data:data,dataType:"json",type:"POST",mimeType:"application/json",success:function(){if(data.data.status===
22
-"completed")AnimeClient.hide(parentSel);AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.$("."+type+"s_read",parentSel)[0].textContent=completed;AnimeClient.showMessage("success","Successfully updated "+mangaName);AnimeClient.scrollToTop()},error:function(){AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.showMessage("error","Failed to update "+mangaName);AnimeClient.scrollToTop()}})})})();
10
+var top=rect.top+window.pageYOffset;window.scrollTo({top:top,behavior:"smooth"})});if("serviceWorker"in navigator)navigator.serviceWorker.register("/sw.js").then(function(reg){console.log("Service worker registered",reg.scope)})["catch"](function(error){console.error("Failed to register service worker",error)});AnimeClient.on("main","change",".big-check",function(e){var id=e.target.id;document.getElementById("mal_"+id).checked=true});function renderAnimeSearchResults(data){var results=[];data.forEach(function(x){var item=
11
+x.attributes;var titles=item.titles.reduce(function(prev,current){return prev+(current+"<br />")},[]);results.push('\n\t\t\t<article class="media search">\n\t\t\t\t<div class="name">\n\t\t\t\t\t<input type="radio" class="mal-check" id="mal_'+item.slug+'" name="mal_id" value="'+x.mal_id+'" />\n\t\t\t\t\t<input type="radio" class="big-check" id="'+item.slug+'" name="id" value="'+x.id+'" />\n\t\t\t\t\t<label for="'+item.slug+'">\n\t\t\t\t\t\t<picture width="220">\n\t\t\t\t\t\t\t<source srcset="/public/images/anime/'+
12
+x.id+'.webp" type="image/webp" />\n\t\t\t\t\t\t\t<source srcset="/public/images/anime/'+x.id+'.jpg" type="image/jpeg" />\n\t\t\t\t\t\t\t<img src="/public/images/anime/'+x.id+'.jpg" alt="" width="220" />\n\t\t\t\t\t\t</picture>\n\t\t\t\t\t\t\n\t\t\t\t\t\t<span class="name">\n\t\t\t\t\t\t\t'+item.canonicalTitle+"<br />\n\t\t\t\t\t\t\t<small>"+titles+'</small>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div class="table">\n\t\t\t\t\t<div class="row">\n\t\t\t\t\t\t<span class="edit">\n\t\t\t\t\t\t\t<a class="bracketed" href="/anime/details/'+
13
+item.slug+'">Info Page</a>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</article>\n\t\t')});return results.join("")}function renderMangaSearchResults(data){var results=[];data.forEach(function(x){var item=x.attributes;var titles=item.titles.reduce(function(prev,current){return prev+(current+"<br />")},[]);results.push('\n\t\t\t<article class="media search">\n\t\t\t\t<div class="name">\n\t\t\t\t\t<input type="radio" id="mal_'+item.slug+'" name="mal_id" value="'+x.mal_id+'" />\n\t\t\t\t\t<input type="radio" class="big-check" id="'+
14
+item.slug+'" name="id" value="'+x.id+'" />\n\t\t\t\t\t<label for="'+item.slug+'">\n\t\t\t\t\t\t<picture width="220">\n\t\t\t\t\t\t\t<source srcset="/public/images/manga/'+x.id+'.webp" type="image/webp" />\n\t\t\t\t\t\t\t<source srcset="/public/images/manga/'+x.id+'.jpg" type="image/jpeg" />\n\t\t\t\t\t\t\t<img src="/public/images/manga/'+x.id+'.jpg" alt="" width="220" />\n\t\t\t\t\t\t</picture>\n\t\t\t\t\t\t<span class="name">\n\t\t\t\t\t\t\t'+item.canonicalTitle+"<br />\n\t\t\t\t\t\t\t<small>"+titles+
15
+'</small>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</label>\n\t\t\t\t</div>\n\t\t\t\t<div class="table">\n\t\t\t\t\t<div class="row">\n\t\t\t\t\t\t<span class="edit">\n\t\t\t\t\t\t\t<a class="bracketed" href="/manga/details/'+item.slug+'">Info Page</a>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</article>\n\t\t')});return results.join("")}var search=function(query){AnimeClient.$(".cssload-loader")[0].removeAttribute("hidden");AnimeClient.get(AnimeClient.url("/anime-collection/search"),{query:query},
16
+function(searchResults,status){searchResults=JSON.parse(searchResults);AnimeClient.$(".cssload-loader")[0].setAttribute("hidden","hidden");AnimeClient.$("#series-list")[0].innerHTML=renderAnimeSearchResults(searchResults.data)})};if(AnimeClient.hasElement(".anime #search"))AnimeClient.on("#search","keyup",AnimeClient.throttle(250,function(e){var query=encodeURIComponent(e.target.value);if(query==="")return;search(query)}));AnimeClient.on("body.anime.list","click",".plus-one",function(e){var parentSel=
17
+AnimeClient.closestParent(e.target,"article");var watchedCount=parseInt(AnimeClient.$(".completed_number",parentSel)[0].textContent,10)||0;var totalCount=parseInt(AnimeClient.$(".total_number",parentSel)[0].textContent,10);var title=AnimeClient.$(".name a",parentSel)[0].textContent;var data={id:parentSel.dataset.kitsuId,mal_id:parentSel.dataset.malId,data:{progress:watchedCount+1}};if(isNaN(watchedCount)||watchedCount===0)data.data.status="current";if(!isNaN(watchedCount)&&watchedCount+1===totalCount)data.data.status=
18
+"completed";AnimeClient.show(AnimeClient.$("#loading-shadow")[0]);AnimeClient.ajax(AnimeClient.url("/anime/increment"),{data:data,dataType:"json",type:"POST",success:function(res){var resData=JSON.parse(res);if(resData.errors){AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.showMessage("error","Failed to update "+title+". ");AnimeClient.scrollToTop();return}if(resData.data.attributes.status==="completed")AnimeClient.hide(parentSel);AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);
19
+AnimeClient.showMessage("success","Successfully updated "+title);AnimeClient.$(".completed_number",parentSel)[0].textContent=++watchedCount;AnimeClient.scrollToTop()},error:function(){AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.showMessage("error","Failed to update "+title+". ");AnimeClient.scrollToTop()}})});var search$1=function(query){AnimeClient.$(".cssload-loader")[0].removeAttribute("hidden");AnimeClient.get(AnimeClient.url("/manga/search"),{query:query},function(searchResults,
20
+status){searchResults=JSON.parse(searchResults);AnimeClient.$(".cssload-loader")[0].setAttribute("hidden","hidden");AnimeClient.$("#series-list")[0].innerHTML=renderMangaSearchResults(searchResults.data)})};if(AnimeClient.hasElement(".manga #search"))AnimeClient.on("#search","keyup",AnimeClient.throttle(250,function(e){var query=encodeURIComponent(e.target.value);if(query==="")return;search$1(query)}));AnimeClient.on(".manga.list","click",".edit-buttons button",function(e){var thisSel=e.target;var parentSel=
21
+AnimeClient.closestParent(e.target,"article");var type=thisSel.classList.contains("plus-one-chapter")?"chapter":"volume";var completed=parseInt(AnimeClient.$("."+type+"s_read",parentSel)[0].textContent,10)||0;var total=parseInt(AnimeClient.$("."+type+"_count",parentSel)[0].textContent,10);var mangaName=AnimeClient.$(".name",parentSel)[0].textContent;if(isNaN(completed))completed=0;var data={id:parentSel.dataset.kitsuId,mal_id:parentSel.dataset.malId,data:{progress:completed}};if(isNaN(completed)||
22
+completed===0)data.data.status="current";if(!isNaN(completed)&&completed+1===total)data.data.status="completed";data.data.progress=++completed;AnimeClient.show(AnimeClient.$("#loading-shadow")[0]);AnimeClient.ajax(AnimeClient.url("/manga/increment"),{data:data,dataType:"json",type:"POST",mimeType:"application/json",success:function(){if(data.data.status==="completed")AnimeClient.hide(parentSel);AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.$("."+type+"s_read",parentSel)[0].textContent=
23
+completed;AnimeClient.showMessage("success","Successfully updated "+mangaName);AnimeClient.scrollToTop()},error:function(){AnimeClient.hide(AnimeClient.$("#loading-shadow")[0]);AnimeClient.showMessage("error","Failed to update "+mangaName);AnimeClient.scrollToTop()}})})})();
23 24
 //# sourceMappingURL=scripts-authed.min.js.map

+ 1
- 1
public/js/scripts-authed.min.js.map
File diff suppressed because it is too large
View File


+ 1
- 1
public/js/scripts.min.js View File

@@ -7,5 +7,5 @@ sel.addEventListener(event,listener,false)}function delegateEvent(sel,target,eve
7 7
 "GET")url+=url.match(/\?/)?ajaxSerialize(config.data):"?"+ajaxSerialize(config.data);request.open(method,url);request.onreadystatechange=function(){if(request.readyState===4){var responseText="";if(request.responseType==="json")responseText=JSON.parse(request.responseText);else responseText=request.responseText;if(request.status>299)config.error.call(null,request.status,responseText,request.response);else config.success.call(null,responseText,request.status)}};if(config.dataType==="json"){config.data=
8 8
 JSON.stringify(config.data);config.mimeType="application/json"}else config.data=ajaxSerialize(config.data);request.setRequestHeader("Content-Type",config.mimeType);switch(method){case "GET":request.send(null);break;default:request.send(config.data);break}};AnimeClient.get=function(url,data,callback){callback=callback===undefined?null:callback;if(callback===null){callback=data;data={}}return AnimeClient.ajax(url,{data:data,success:callback})};AnimeClient.on("header","click",".message",function(e){AnimeClient.hide(e.target)});
9 9
 AnimeClient.on("form.js-delete","submit",function(event){var proceed=confirm("Are you ABSOLUTELY SURE you want to delete this item?");if(proceed===false){event.preventDefault();event.stopPropagation()}});AnimeClient.on(".js-clear-cache","click",function(){AnimeClient.get("/cache_purge",function(){AnimeClient.showMessage("success","Successfully purged api cache")})});AnimeClient.on(".vertical-tabs input","change",function(event){var el=event.currentTarget.parentElement;var rect=el.getBoundingClientRect();
10
-var top=rect.top+window.pageYOffset;window.scrollTo({top:top,behavior:"smooth"})})})();
10
+var top=rect.top+window.pageYOffset;window.scrollTo({top:top,behavior:"smooth"})});if("serviceWorker"in navigator)navigator.serviceWorker.register("/sw.js").then(function(reg){console.log("Service worker registered",reg.scope)})["catch"](function(error){console.error("Failed to register service worker",error)})})();
11 11
 //# sourceMappingURL=scripts.min.js.map

+ 1
- 1
public/js/scripts.min.js.map
File diff suppressed because it is too large
View File


+ 2
- 2
public/js/src/index.js View File

@@ -1,10 +1,10 @@
1 1
 import './base/events.js';
2 2
 
3
-/* if ('serviceWorker' in navigator) {
3
+if ('serviceWorker' in navigator) {
4 4
 	navigator.serviceWorker.register('/sw.js').then(reg => {
5 5
 		console.log('Service worker registered', reg.scope);
6 6
 	}).catch(error => {
7 7
 		console.error('Failed to register service worker', error);
8 8
 	});
9
-} */
9
+}
10 10
 

+ 3
- 3
src/AnimeClient.php View File

@@ -259,7 +259,7 @@ function createPlaceholderImage ($path, $width, $height, $text = 'Image Unavaila
259 259
 	// Background is the first color by default
260 260
 	$fillColor = imagecolorallocatealpha($img, 255, 255, 255, 127);
261 261
 	imagefill($img, 0, 0, $fillColor);
262
-	
262
+
263 263
 	$textColor = imagecolorallocate($img, 64, 64, 64);
264 264
 
265 265
 	imagealphablending($img, TRUE);
@@ -280,11 +280,11 @@ function createPlaceholderImage ($path, $width, $height, $text = 'Image Unavaila
280 280
 	imagesavealpha($img, TRUE);
281 281
 	imagepng($img, $path . '/placeholder.png', 9);
282 282
 	imagedestroy($img);
283
-	
283
+
284 284
 	$pngImage = imagecreatefrompng($path . '/placeholder.png');
285 285
 	imagealphablending($pngImage, TRUE);
286 286
 	imagesavealpha($pngImage, TRUE);
287
-	
287
+
288 288
 	imagewebp($pngImage, $path . '/placeholder.webp');
289 289
 
290 290
 	imagedestroy($pngImage);

+ 197
- 197
src/Controller/Images.php View File

@@ -1,198 +1,198 @@
1
-<?php declare(strict_types=1);
2
-/**
3
- * Hummingbird Anime List Client
4
- *
5
- * An API client for Kitsu to manage anime and manga watch lists
6
- *
7
- * PHP version 7.1
8
- *
9
- * @package     HummingbirdAnimeClient
10
- * @author      Timothy J. Warren <tim@timshomepage.net>
11
- * @copyright   2015 - 2018  Timothy J. Warren
12
- * @license     http://www.opensource.org/licenses/mit-license.html  MIT License
13
- * @version     4.1
14
- * @link        https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
15
- */
16
-
17
-namespace Aviat\AnimeClient\Controller;
18
-
19
-use function Aviat\AnimeClient\createPlaceholderImage;
20
-use function Amp\Promise\wait;
21
-
22
-use Aviat\AnimeClient\Controller as BaseController;
23
-use Aviat\AnimeClient\API\{HummingbirdClient, JsonAPI};
24
-use Aviat\Ion\Di\ContainerInterface;
25
-use Aviat\Ion\View\HtmlView;
26
-
27
-/**
28
- * Controller for handling routes that don't fit elsewhere
29
- */
30
-final class Images extends BaseController {
31
-	/**
32
-	 * Get image covers from kitsu
33
-	 *
34
-	 * @param string $type The category of image
35
-	 * @param string $file The filename to look for
36
-	 * @param bool $display Whether to output the image to the server
37
-	 * @throws \Aviat\Ion\Di\ContainerException
38
-	 * @throws \Aviat\Ion\Di\NotFoundException
39
-	 * @throws \InvalidArgumentException
40
-	 * @throws \TypeError
41
-	 * @throws \Error
42
-	 * @throws \Throwable
43
-	 * @return void
44
-	 */
45
-	public function cache(string $type, string $file, $display = TRUE): void
46
-	{
47
-		$currentUrl = $this->request->getUri()->__toString();
48
-		
49
-		$kitsuUrl = 'https://media.kitsu.io/';
50
-		$fileName = str_replace('-original', '', $file);
51
-		[$id, $ext] = explode('.', basename($fileName));
52
-
53
-		$baseSavePath = $this->config->get('img_cache_path');
54
-		
55
-		// Kitsu doesn't serve webp, but for most use cases,
56
-		// jpg is a safe assumption
57
-		$tryJpg = ['anime','characters','manga','people'];
58
-		if ($ext === 'webp' && in_array($type, $tryJpg, TRUE))
59
-		{
60
-			$ext = 'jpg';
61
-			$currentUrl = str_replace('webp', 'jpg', $currentUrl);
62
-		}
63
-
64
-		$typeMap = [
65
-			'anime' => [
66
-				'kitsuUrl' => "anime/poster_images/{$id}/medium.{$ext}",
67
-				'width' => 220,
68
-				'height' => 312,
69
-			],
70
-			'avatars' => [
71
-				'kitsuUrl' => "users/avatars/{$id}/original.{$ext}",
72
-				'width' => null,
73
-				'height' => null,
74
-			],
75
-			'characters' => [
76
-				'kitsuUrl' => "characters/images/{$id}/original.{$ext}",
77
-				'width' => 225,
78
-				'height' => 350,
79
-			],
80
-			'manga' => [
81
-				'kitsuUrl' => "manga/poster_images/{$id}/medium.{$ext}",
82
-				'width' => 220,
83
-				'height' => 312,
84
-			],
85
-			'people' => [
86
-				'kitsuUrl' => "people/images/{$id}/original.{$ext}",
87
-				'width' => null,
88
-				'height' => null,
89
-			],
90
-		];
91
-		
92
-		$imageType = $typeMap[$type] ?? NULL;
93
-
94
-		if (NULL === $imageType)
95
-		{
96
-			$this->getPlaceholder($baseSavePath, 200, 200);
97
-			return;
98
-		}
99
-
100
-		$kitsuUrl .= $imageType['kitsuUrl'];
101
-		$width = $imageType['width'];
102
-		$height = $imageType['height'];
103
-		$filePrefix = "{$baseSavePath}/{$type}/{$id}";
104
-
105
-		$promise = (new HummingbirdClient)->request($kitsuUrl);
106
-		$response = wait($promise);
107
-
108
-		if ($response->getStatus() !== 200)
109
-		{
110
-			// Try a few different file types before giving up
111
-			// webm => jpg => png => gif
112
-			$nextType = [
113
-				'jpg' => 'png',
114
-				'png' => 'gif',
115
-			];
116
-			
117
-			if (array_key_exists($ext, $nextType))
118
-			{
119
-				$newUrl = str_replace($ext, $nextType[$ext], $currentUrl);
120
-				$this->redirect($newUrl, 303);
121
-				return;
122
-			}
123
-			
124
-			if ($display)
125
-			{
126
-				$this->getPlaceholder("{$baseSavePath}/{$type}", $width, $height);
127
-			}
128
-			else
129
-			{
130
-				createPlaceholderImage("{$baseSavePath}/{$type}", $width, $height);
131
-			}
132
-			return;
133
-		}
134
-
135
-		$data = wait($response->getBody());
136
-
137
-		
138
-
139
-		[$origWidth] = getimagesizefromstring($data);
140
-		$gdImg = imagecreatefromstring($data);
141
-		$resizedImg = imagescale($gdImg, $width ?? $origWidth);
142
-
143
-		if ($ext === 'gif')
144
-		{
145
-			file_put_contents("{$filePrefix}.gif", $data);
146
-			imagepalletetotruecolor($gdImg);
147
-		}
148
-
149
-		// save the webp versions
150
-		imagewebp($gdImg, "{$filePrefix}-original.webp");
151
-		imagewebp($resizedImg, "{$filePrefix}.webp");
152
-
153
-		// save the scaled jpeg file
154
-		imagejpeg($resizedImg, "{$filePrefix}.jpg");
155
-
156
-		// And the original
157
-		file_put_contents("{$filePrefix}-original.jpg", $data);
158
-
159
-		imagedestroy($gdImg);
160
-		imagedestroy($resizedImg);
161
-
162
-		if ($display)
163
-		{
164
-			$contentType = ($ext === 'webp')
165
-				? "image/webp"
166
-				: $response->getHeader('content-type')[0];
167
-
168
-			$outputFile = (strpos($file, '-original') !== FALSE)
169
-				? "{$filePrefix}-original.{$ext}"
170
-				: "{$filePrefix}.{$ext}";
171
-
172
-			header("Content-Type: {$contentType}");
173
-			echo file_get_contents($outputFile);
174
-		}
175
-	}
176
-
177
-	/**
178
-	 * Get a placeholder for a missing image
179
-	 *
180
-	 * @param string $path
181
-	 * @param int|null $width
182
-	 * @param int|null $height
183
-	 */
184
-	private function getPlaceholder (string $path, ?int $width = 200, ?int $height = NULL): void
185
-	{
186
-		$height = $height ?? $width;
187
-
188
-		$filename = $path . '/placeholder.png';
189
-
190
-		if ( ! file_exists($path . '/placeholder.png'))
191
-		{
192
-			createPlaceholderImage($path, $width, $height);
193
-		}
194
-
195
-		header('Content-Type: image/png');
196
-		echo file_get_contents($filename);
197
-	}
1
+<?php declare(strict_types=1);
2
+/**
3
+ * Hummingbird Anime List Client
4
+ *
5
+ * An API client for Kitsu to manage anime and manga watch lists
6
+ *
7
+ * PHP version 7.1
8
+ *
9
+ * @package     HummingbirdAnimeClient
10
+ * @author      Timothy J. Warren <tim@timshomepage.net>
11
+ * @copyright   2015 - 2018  Timothy J. Warren
12
+ * @license     http://www.opensource.org/licenses/mit-license.html  MIT License
13
+ * @version     4.1
14
+ * @link        https://git.timshomepage.net/timw4mail/HummingBirdAnimeClient
15
+ */
16
+
17
+namespace Aviat\AnimeClient\Controller;
18
+
19
+use function Aviat\AnimeClient\createPlaceholderImage;
20
+use function Amp\Promise\wait;
21
+
22
+use Aviat\AnimeClient\Controller as BaseController;
23
+use Aviat\AnimeClient\API\{HummingbirdClient, JsonAPI};
24
+use Aviat\Ion\Di\ContainerInterface;
25
+use Aviat\Ion\View\HtmlView;
26
+
27
+/**
28
+ * Controller for handling routes that don't fit elsewhere
29
+ */
30
+final class Images extends BaseController {
31
+	/**
32
+	 * Get image covers from kitsu
33
+	 *
34
+	 * @param string $type The category of image
35
+	 * @param string $file The filename to look for
36
+	 * @param bool $display Whether to output the image to the server
37
+	 * @throws \Aviat\Ion\Di\ContainerException
38
+	 * @throws \Aviat\Ion\Di\NotFoundException
39
+	 * @throws \InvalidArgumentException
40
+	 * @throws \TypeError
41
+	 * @throws \Error
42
+	 * @throws \Throwable
43
+	 * @return void
44
+	 */
45
+	public function cache(string $type, string $file, $display = TRUE): void
46
+	{
47
+		$currentUrl = $this->request->getUri()->__toString();
48
+
49
+		$kitsuUrl = 'https://media.kitsu.io/';
50
+		$fileName = str_replace('-original', '', $file);
51
+		[$id, $ext] = explode('.', basename($fileName));
52
+
53
+		$baseSavePath = $this->config->get('img_cache_path');
54
+
55
+		// Kitsu doesn't serve webp, but for most use cases,
56
+		// jpg is a safe assumption
57
+		$tryJpg = ['anime','characters','manga','people'];
58
+		if ($ext === 'webp' && in_array($type, $tryJpg, TRUE))
59
+		{
60
+			$ext = 'jpg';
61
+			$currentUrl = str_replace('webp', 'jpg', $currentUrl);
62
+		}
63
+
64
+		$typeMap = [
65
+			'anime' => [
66
+				'kitsuUrl' => "anime/poster_images/{$id}/medium.{$ext}",
67
+				'width' => 220,
68
+				'height' => 312,
69
+			],
70
+			'avatars' => [
71
+				'kitsuUrl' => "users/avatars/{$id}/original.{$ext}",
72
+				'width' => null,
73
+				'height' => null,
74
+			],
75
+			'characters' => [
76
+				'kitsuUrl' => "characters/images/{$id}/original.{$ext}",
77
+				'width' => 225,
78
+				'height' => 350,
79
+			],
80
+			'manga' => [
81
+				'kitsuUrl' => "manga/poster_images/{$id}/medium.{$ext}",
82
+				'width' => 220,
83
+				'height' => 312,
84
+			],
85
+			'people' => [
86
+				'kitsuUrl' => "people/images/{$id}/original.{$ext}",
87
+				'width' => null,
88
+				'height' => null,
89
+			],
90
+		];
91
+
92
+		$imageType = $typeMap[$type] ?? NULL;
93
+
94
+		if (NULL === $imageType)
95
+		{
96
+			$this->getPlaceholder($baseSavePath, 200, 200);
97
+			return;
98
+		}
99
+
100
+		$kitsuUrl .= $imageType['kitsuUrl'];
101
+		$width = $imageType['width'];
102
+		$height = $imageType['height'];
103
+		$filePrefix = "{$baseSavePath}/{$type}/{$id}";
104
+
105
+		$promise = (new HummingbirdClient)->request($kitsuUrl);
106
+		$response = wait($promise);
107
+
108
+		if ($response->getStatus() !== 200)
109
+		{
110
+			// Try a few different file types before giving up
111
+			// webm => jpg => png => gif
112
+			$nextType = [
113
+				'jpg' => 'png',
114
+				'png' => 'gif',
115
+			];
116
+
117
+			if (array_key_exists($ext, $nextType))
118
+			{
119
+				$newUrl = str_replace($ext, $nextType[$ext], $currentUrl);
120
+				$this->redirect($newUrl, 303);
121
+				return;
122
+			}
123
+
124
+			if ($display)
125
+			{
126
+				$this->getPlaceholder("{$baseSavePath}/{$type}", $width, $height);
127
+			}
128
+			else
129
+			{
130
+				createPlaceholderImage("{$baseSavePath}/{$type}", $width, $height);
131
+			}
132
+			return;
133
+		}
134
+
135
+		$data = wait($response->getBody());
136
+
137
+
138
+
139
+		[$origWidth] = getimagesizefromstring($data);
140
+		$gdImg = imagecreatefromstring($data);
141
+		$resizedImg = imagescale($gdImg, $width ?? $origWidth);
142
+
143
+		if ($ext === 'gif')
144
+		{
145
+			file_put_contents("{$filePrefix}.gif", $data);
146
+			\imagepalletetotruecolor($gdImg);
147
+		}
148
+
149
+		// save the webp versions
150
+		imagewebp($gdImg, "{$filePrefix}-original.webp");
151
+		imagewebp($resizedImg, "{$filePrefix}.webp");
152
+
153
+		// save the scaled jpeg file
154
+		imagejpeg($resizedImg, "{$filePrefix}.jpg");
155
+
156
+		// And the original
157
+		file_put_contents("{$filePrefix}-original.jpg", $data);
158
+
159
+		imagedestroy($gdImg);
160
+		imagedestroy($resizedImg);
161
+
162
+		if ($display)
163
+		{
164
+			$contentType = ($ext === 'webp')
165
+				? "image/webp"
166
+				: $response->getHeader('content-type')[0];
167
+
168
+			$outputFile = (strpos($file, '-original') !== FALSE)
169
+				? "{$filePrefix}-original.{$ext}"
170
+				: "{$filePrefix}.{$ext}";
171
+
172
+			header("Content-Type: {$contentType}");
173
+			echo file_get_contents($outputFile);
174
+		}
175
+	}
176
+
177
+	/**
178
+	 * Get a placeholder for a missing image
179
+	 *
180
+	 * @param string $path
181
+	 * @param int|null $width
182
+	 * @param int|null $height
183
+	 */
184
+	private function getPlaceholder (string $path, ?int $width = 200, ?int $height = NULL): void
185
+	{
186
+		$height = $height ?? $width;
187
+
188
+		$filename = $path . '/placeholder.png';
189
+
190
+		if ( ! file_exists($path . '/placeholder.png'))
191
+		{
192
+			createPlaceholderImage($path, $width, $height);
193
+		}
194
+
195
+		header('Content-Type: image/png');
196
+		echo file_get_contents($filename);
197
+	}
198 198
 }

+ 0
- 8
src/Util.php View File

@@ -16,7 +16,6 @@
16 16
 
17 17
 namespace Aviat\AnimeClient;
18 18
 
19
-use Aviat\Ion\ConfigInterface;
20 19
 use Aviat\Ion\Di\{ContainerAware, ContainerInterface};
21 20
 
22 21
 /**
@@ -42,12 +41,6 @@ class Util {
42 41
 		'me'
43 42
 	];
44 43
 
45
-	/**
46
-	 * The config manager
47
-	 * @var ConfigInterface
48
-	 */
49
-	private $config;
50
-
51 44
 	/**
52 45
 	 * Set up the Util class
53 46
 	 *
@@ -58,7 +51,6 @@ class Util {
58 51
 	public function __construct(ContainerInterface $container)
59 52
 	{
60 53
 		$this->setContainer($container);
61
-		$this->config = $container->get('config');
62 54
 	}
63 55
 
64 56
 	/**

+ 4
- 27
sw.js View File

@@ -5,36 +5,17 @@ async function fromCache (request) {
5 5
 	return await cache.match(request);
6 6
 }
7 7
 
8
-async function fromNetwork (request) {
9
-	return await fetch(request);
10
-}
11
-
12
-async function update (request) {
8
+async function updateCache (request) {
13 9
 	const cache = await caches.open(CACHE_NAME);
14 10
 	const response = await fetch(request);
15 11
 
16 12
 	if (request.url.includes('/public/images/')) {
17
-		console.log('Saving to cache: ', request.url);
18 13
 		await cache.put(request, response.clone());
19 14
 	}
20 15
 
21 16
 	return response;
22 17
 }
23 18
 
24
-/* function refresh (response) {
25
-	return self.clients.matchAll().then(clients => {
26
-		clients.forEach(client => {
27
-			const message = {
28
-				type: 'refresh',
29
-				url: response.url,
30
-				eTag: response.headers.get('ETag')
31
-			};
32
-
33
-			client.postMessage(JSON.stringify(message));
34
-		})
35
-	});
36
-} */
37
-
38 19
 self.addEventListener('install', event => {
39 20
 	console.log('Public Folder Worker installed');
40 21
 
@@ -55,8 +36,8 @@ self.addEventListener('install', event => {
55 36
 	)
56 37
 });
57 38
 
58
-self.addEventListener('activate', event => {
59
-	console.log('Public Folder Worker activated');
39
+self.addEventListener('activate', () => {
40
+	console.info('Public Folder Worker activated');
60 41
 });
61 42
 
62 43
 // Pull css, images, and javascript from cache
@@ -71,11 +52,7 @@ self.addEventListener('fetch', event => {
71 52
 		if (cached !== undefined) {
72 53
 			event.respondWith(cached);
73 54
 		} else {
74
-			event.respondWith(fromNetwork(event.request));
55
+			event.respondWith(updateCache(event.request));
75 56
 		}
76 57
 	});
77
-
78
-	event.waitUntil(
79
-		update(event.request)
80
-	);
81 58
 });

Loading…
Cancel
Save