From 3895ead151c82ef8d96f4d024a9b7b6f982a49ea Mon Sep 17 00:00:00 2001 From: flashwave Date: Sun, 12 Jun 2016 20:52:15 +0200 Subject: [PATCH] entire application --- .gitattributes | 2 + .gitignore | 7 ++ LICENSE | 2 +- README.md | 5 +- composer.json | 5 + composer.lock | 70 +++++++++++++ config.example.ini | 2 + gulpfile.js | 53 ++++++++++ package.json | 10 ++ public/get.php | 31 ++++++ public/index.html | 45 ++++++++ public/resources/grid.png | Bin 0 -> 945 bytes public/resources/no-cover.png | Bin 0 -> 23537 bytes src/less/app.less | 174 +++++++++++++++++++++++++++++++ src/typescript/AJAX.ts | 137 ++++++++++++++++++++++++ src/typescript/Background.ts | 8 ++ src/typescript/DOM.ts | 134 ++++++++++++++++++++++++ src/typescript/HTTPMethod.ts | 11 ++ src/typescript/Initialisation.ts | 13 +++ src/typescript/Mode.ts | 8 ++ src/typescript/UI.ts | 114 ++++++++++++++++++++ src/typescript/Watcher.ts | 53 ++++++++++ tsconfig.json | 15 +++ 23 files changed, 897 insertions(+), 2 deletions(-) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 config.example.ini create mode 100644 gulpfile.js create mode 100644 package.json create mode 100644 public/get.php create mode 100644 public/index.html create mode 100644 public/resources/grid.png create mode 100644 public/resources/no-cover.png create mode 100644 src/less/app.less create mode 100644 src/typescript/AJAX.ts create mode 100644 src/typescript/Background.ts create mode 100644 src/typescript/DOM.ts create mode 100644 src/typescript/HTTPMethod.ts create mode 100644 src/typescript/Initialisation.ts create mode 100644 src/typescript/Mode.ts create mode 100644 src/typescript/UI.ts create mode 100644 src/typescript/Watcher.ts create mode 100644 tsconfig.json diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a835337 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +node_modules/* +vendor/* +public/app.* +config.ini +[Tt]humbs.db +desktop.ini +$RECYCLE.BIN/ diff --git a/LICENSE b/LICENSE index 4cb818e..7380327 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 Julian van de Groep +Copyright (c) 2016 Julian van de Groep Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 1931f65..81d41ff 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ -# now-listening +# now listening A replacement for the old /now page on lastfm profiles + +## Usage +https://now.flash.moe/#/{last.fm username} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..6b1f6e9 --- /dev/null +++ b/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "matto1990/lastfm-api": "^1.2" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..86372c4 --- /dev/null +++ b/composer.lock @@ -0,0 +1,70 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "0447259d6060720f3c189f0fdca5c123", + "content-hash": "94d9635fa82a3525da82ed39e8471a8b", + "packages": [ + { + "name": "matto1990/lastfm-api", + "version": "v1.2", + "source": { + "type": "git", + "url": "https://github.com/matto1990/PHP-Last.fm-API.git", + "reference": "1cf9a8947bf756beb876d5f8e2af398058805e08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matto1990/PHP-Last.fm-API/zipball/1cf9a8947bf756beb876d5f8e2af398058805e08", + "reference": "1cf9a8947bf756beb876d5f8e2af398058805e08", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "php": ">=5.3.3", + "phpunit/phpunit": "~4.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "LastFmApi\\": "src/lastfmapi/", + "Tests\\": "tests" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marcos", + "email": "devilcius@gmail.com" + }, + { + "name": "Matt", + "email": "matt@oakes.ws" + } + ], + "description": "Last.fm webservice client", + "homepage": "https://github.com/matto1990/PHP-Last.fm-API", + "keywords": [ + "api", + "last.fm", + "webservice client" + ], + "time": "2016-03-17 16:41:24" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/config.example.ini b/config.example.ini new file mode 100644 index 0000000..fca056c --- /dev/null +++ b/config.example.ini @@ -0,0 +1,2 @@ +api_key = your api key here +endpoint = https://ws.audioscrobbler.com/2.0/ diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..6bcde5d --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,53 @@ +// aliases +var gulp = require('gulp'), + less = require('gulp-less'), + ts = require('gulp-typescript'), + concat = require('gulp-concat'), + jsmin = require('gulp-minify'), + path = require('path'); + +// variables +var destination = './public', + less_sources = './src/less/**/*.less', + less_watch = less_sources, + ts_config = './tsconfig.json', + ts_sources = './src/typescript/**/*.ts', + ts_watch = ts_sources; + +// default task +gulp.task('default', ['less', 'typescript']); + +// watcher +gulp.task('watch', function () { + gulp.watch(less_watch, ['less']); + gulp.watch(ts_watch, ['typescript']); +}); + +// less +gulp.task('less', function () { + return gulp.src(less_sources) + .pipe(less({ + paths: [ + path.join(__dirname, 'less', 'includes') + ], + compress: true + })) + .pipe(concat('app.css')) + .pipe(gulp.dest(destination)); +}); + +// typescript +gulp.task('typescript', function () { + var tsProject = ts.createProject(ts_config); + + return gulp.src(ts_sources) + .pipe(ts(tsProject)) + .pipe(jsmin({ + ext: { + src: '-', + min: '.js' + }, + noSource: true + })) + .pipe(gulp.dest(destination)); +}); diff --git a/package.json b/package.json new file mode 100644 index 0000000..7faec99 --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "private": true, + "dependencies": { + "gulp": "^3.9.1", + "gulp-concat": "^2.6.0", + "gulp-less": "^3.0.5", + "gulp-minify": "0.0.11", + "gulp-typescript": "^2.13.0" + } +} diff --git a/public/get.php b/public/get.php new file mode 100644 index 0000000..f1af208 --- /dev/null +++ b/public/get.php @@ -0,0 +1,31 @@ + 'Please run "composer install" in the main directory first!'])); +} + +require_once '../vendor/autoload.php'; + +if (!file_exists('../config.ini')) { + die(view(['error' => 'Configuration missing! Make a copy of config.example.ini named config.ini and set your API key.'])); +} + +$config = parse_ini_file('../config.ini'); + +$auth = new AuthApi('setsession', ['apiKey' => $config['api_key']]); +$user = new UserApi($auth); +$now = $user->getRecentTracks(['user' => (isset($_GET['u']) ? $_GET['u'] : ''), 'limit' => '1']); + +if ($now === false) { + $now = ['error' => 'User not found.']; +} + +echo view($now); diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..f0d2d84 --- /dev/null +++ b/public/index.html @@ -0,0 +1,45 @@ + + + + + + Now Listening + + + + + + +
+
+ + +
+ + + diff --git a/public/resources/grid.png b/public/resources/grid.png new file mode 100644 index 0000000000000000000000000000000000000000..c5fd92bcc6dfec5363b3972952a3eb7503694911 GIT binary patch literal 945 zcmaJ=O^ee&7>>JDWMM(Ptso3p1O=O9+9qy7@f_pZ=Dz& zI+pV9<_-G)`0L$sIvfzQPr7(SQYS`?=HVd%R^W_L2RYvS={Hnom|fQI^-148Ra_i! zPLAQSAfjxBsUK&N<4zF)Lp1h7mHqkYGYfoAWzR%gup=E!{F6nDx{FrNT})lsV~-z$ zdZti;01*dd!FiY{naZwo6*|wic^0f7WU8{4N%ieE&~c1_$W_a(03nbh4%S3Tf)7DO zfE8Y#uT+LLMU<4vBd~s08ZGulO2=rfW6_n$P6&w4!X+spTbFR9wEMi5Khg@Wf!$ev zLQ-(OD=ys1&8-kbG_rwW{~7X{F%H10WX0c_i(qcmTXVgwxd_dzT%IPw=Z*cV(Zv>Z zPd?q)mM%8NMl_bfx}W#5OR!9ZXxO5pd-hl3Z!_1)Wpor}9|>Fw9{ YwOa#bnUzl8eE9)#hGn*lWn=L259#ppAm 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