Code Monkey home page Code Monkey logo

photo-stream's Introduction

Photo Stream Social Preview

Photo Stream

Photo Stream is a simpler home for your photos initially created by @maxvoltar and now maintained by @waschinski, @boerniee and friends. Easy to use, self hosted, no tracking, just photos.

Demo

There is a demo of this repository hosted on a Free Plan on Render.

Live Examples

Features

  • Lazy loading
  • Only load larger resolutions when needed (to save on bandwidth)
  • Supports PNG, JPG, JPEG, GIF and WebP files
  • Photo tints
  • Keyboard shortcuts
  • Unique URL's for photos
  • RSS feed (Which you can plug into IFTTT and set up auto-posting to most social networks, like @maxvoltar has done here. Make sure you select "Post a tweet with image" when setting it up to embed the photo.)
  • Drag, drop, commit workflow (learn more about how to add photos to your stream)
  • Optimized light and dark themes (auto-enabled depending on your OS preferences)
  • Optional: Links to your social networks

Why?

We like to take photos and share them. Problem is it's hard to really own your photos and how they're represented across social media these days, so we set out to make a place for them. You host it yourself, wherever you want (Netlify, Github Pages...), you're in control.

How to install

Previously the recommended way to install Photo Stream was to fork the repository. In my opinion this was not really optimal and being a fan of Docker I began working on optimizations to run Photo Stream in a container. That's why configuration has been moved from _config.yml to .env so when switching from the initial repo you will have to set up the .env file accordingly.

Using Docker

There is an image over at Docker Hub which you can pull using:

docker pull waschinski/photo-stream:latest

Alternatively download the docker-compose.yml file, change the configuration as needed and use the following command to get Photo Stream running:

docker-compose up -d

The photos folder can be mounted as a volume. Make sure to put your photos in a folder called original.

Using Docker on Raspberry Pi (linux/arm/v6 only):

Prerequisites : docker and docker-compose are installed on RPI

In docker-compose.yml comment image section, uncomment build section, and setup BASE_IMAGE arg to arm32v6/ruby:3.1.2-alpine3.16.

Then docker-compose build

Then docker-compose up -d

Manually

Grab the latest version from the release page and extract it.

Make sure you meet the following requirements in order to run Photo Stream:

Build tools

How to install these depends on your OS. Debian users will go with sudo apt-get install build-essential while on MacOS you should be fine with xcode-select --install.

Ruby (v3+ recommended)

Check to see if you already have Ruby installed (ruby -v). If you don't, you can follow the installation instructions provided here.

libvips

Instructions on how to install libvips can be found here.

Jekyll

Next you'll have to install Jekyll (a simple gem install bundler jekyll should suffice). Make sure you meet its requirements or install them as well before proceeding.

Once all these requirements are met you can finally install all the gems required by Photo Stream (you should be in the Photo Stream folder):

bundle install

How to deploy directly

on Render

Fork this repo and add your own photos to the photos/original folder. Log in to your Render account or create a new one. Create a new static site on the Render Dashboard. Connect your Github account and select your photo-stream repository. Select the correct branch and adjust the Build Command (bundle exec jekyll build) and Publish Directory (_site). Under Advanced you Add Secret File and create a .env file containing your adjusted environment variables. You might be able to change this file directly in your repo and skip the former step (adding a secret file) but I haven't tested this.

How to use

Put your photos (not resized) in the photos/original directory. Optionally you can give them a name, which will appear as the title of the photo page and in the RSS feed.

This command will serve the static page on your local machine. http://localhost:4000

bundle exec jekyll serve

You can also statically build your site to be uploaded to a regular webhost.

bundle exec jekyll build

Now upload the contents of the _site/ directory to your webserver.

Automating the build & upload with rsync or lftp

Just execute the script you need to run directly from the _scripts folder like that:

sh ./_script/build-n-lftp.sh

build.sh will build your site while rsync.sh and lftp.sh will sync it accordingly. build-n-rsync.sh and build-n-lftp.sh are simply doing both steps in one. Don't forget to add your sync configuration in the .env file.

Customize

Basics

First thing you want to do is edit a couple of things in /.env:

  • TITLE: The title of your photo stream.
  • EMAIL: Your email address (this line is optional, you can remove it).
  • AUTHOR_NAME: Your name.
  • AUTHOR_EMAIL: Your email address (optional).
  • AUTHOR_WEBSITE: Your website (could be the address of this photo stream).
  • DESCRIPTION: Description of your photo stream.
  • BASEURL: Should be left empty or removed ⚠️ Do not change unless you know what you're doing
  • URL: Where will this photo stream live (example: https://maxvoltar.photo), must NOT end with / or links will be messed up.
  • SHOW_RSS_FEED: Set to either 1 or 0 to enable or disable showing the RSS feed button.
  • SHOW_OFFICIAL_GITHUB: Set to either 1 or 0 to enable or disable showing the link to the official github repository.
  • DEFAULT_REVERSE_SORT: Set this to 1 to reverse the photo sort order and show oldest photos first. Defaults to 0.
  • ALLOW_ORDER_SORT_CHANGE: Set this to 1 to allow users to reverse the sort order of the photos.
  • ALLOW_ORIGINAL_DOWNLOAD: Set this to 1 to allow users to download the photos in their original size.
  • ALLOW_INDEXING: Set this to 0 to prevent crawlers from indexing your photo stream by adding meta tag robots. Defaults to 1.
  • ALLOW_IMAGE_SHARING: Set this to 1 to allow users to share images with friends. Defaults to 1.
  • TWITTER_USERNAME: Your Twitter username or remove/comment this line.
  • GITHUB_USERNAME: Your Github username or remove/comment this line.
  • INSTAGRAM_USERNAME: Your Instagram username or remove/comment this line.
  • SYNCUSER: Your username being used by lftp/rsync in the shell scripts to sync your site to your webserver.
  • SYNCPASS: Your password being used by lftp/rsync in the shell scripts to sync your site to your webserver.
  • SYNCSERVER: The URL of your webserver being used by lftp/rsync in the shell scripts where your site will be synced to.
  • SYNCFOLDER: The folder on your webserver being used by lftp/rsync in the shell scripts where your site will be synced to.

Don't include the @-part of your social handles. Links to your Github, Twitter and Instagram profiles are only shown when set.

Advanced

Before publishing your website, Jekyll will resize your photos into 3 different buckets:

  • /photos/large: These are only shown when a user navigates to a photo page. By default these are resized to a maximum of 2048 wide and 2048 tall. If you wish, you can change these by changing the values in /_config.yml (by default they look something like this: resize_to_limit: [2048, 2048]).
  • /photos/thumbnail: These are used in the grid. Photo Stream will load all thumbnails above the fold, then more as you scroll down; all to save bandwidth. Standard size for these is 640 by 640 (max), but you can also change this if needed.
  • /photos/tint: What you see while the page loads its first batch of thumbnails, also used as the background for photo pages. ⚠️ Do not make changes to the tint versions in your config file.

Credits

photo-stream's People

Contributors

alexkutsan avatar benubois avatar boerniee avatar brianmcarey avatar cloudz avatar dependabot[bot] avatar harsht avatar immaax avatar jadlimcaco avatar lukekarrys avatar mattsacks avatar maxvoltar avatar mikedholt avatar mschader avatar myhrmans avatar nhoizey avatar nicosomb avatar oliverhihn avatar richbarton avatar waschinski avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

photo-stream's Issues

[Need Help with Error] /usr/local/bundle/gems/google-protobuf-3.22.2-aarch64-linux/lib/google/protobuf.rb:49:in `require': Error loading shared library

Hello, I just tried installing PS and am getting this error in my log when I try to run the container.

Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.

bundler: failed to load command: jekyll (/usr/local/bundle/bin/jekyll)

/usr/local/bundle/gems/google-protobuf-3.22.2-aarch64-linux/lib/google/protobuf.rb:51:in `require': cannot load such file -- google/protobuf_c (LoadError)

/usr/local/bundle/gems/google-protobuf-3.22.2-aarch64-linux/lib/google/protobuf.rb:49:in `require': Error loading shared library ld-linux-aarch64.so.1: No such file or directory (needed by /usr/local/bundle/gems/google-protobuf-3.22.2-aarch64-linux/lib/google/3.1/protobuf_c.so) - /usr/local/bundle/gems/google-protobuf-3.22.2-aarch64-linux/lib/google/3.1/protobuf_c.so (LoadError)

I followed the instructions in Readme to install it. I tried going through all the issues posted and couldn't understand if any of those pertain to my problem. I also installed jekyll (sudo apt install jekyll - sorry, I am a novice, so please let me know if this wasn't the way)

My config details are as follows:
Installed on: RPI 4b arm64
Docker version 23.0.2, build 569dd73

Disable RSS Button

It would be nice if there was a way, like with all the other buttons, to disable the RSS Feed button. Something like SHOW_RSS_BUTTON=1.

Numeric-only image names break the arrow key JS.

I'm pretty sure this is probably just a situation where something should be explicitly made a string, but I hadn't found where that should go because it was just easier for me to rename my files.

No such file or directory @ rb_sysopen - photos/original/DSC03516.JPG in feed.xml

Trying to build using manual instructions. Running on Ruby 2.7.0p
Added photos/originals, added single photo to directory. Then:

/photo-stream git:(master) ✗ bundle exec jekyll build [21/08/23|14:22:01]
Configuration file: /home/jesse/docs/clients/photography/photo-stream/_config.yml
Source: /home/jesse/docs/clients/photography/photo-stream
Destination: /home/jesse/docs/clients/photography/photo-stream/_site
Incremental build: disabled. Enable with --incremental
Generating...
Liquid Exception: No such file or directory @ rb_sysopen - photos/original/DSC03516.JPG in feed.xml
------------------------------------------------
Jekyll 4.2.0 Please append --trace to the build command
for any additional information or backtrace.
------------------------------------------------
Traceback (most recent call last):
55: from /usr/local/bin/jekyll:23:in <main>' 54: from /usr/local/bin/jekyll:23:in load'
53: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/exe/jekyll:15:in <top (required)>' 52: from /var/lib/gems/2.7.0/gems/mercenary-0.4.0/lib/mercenary.rb:21:in program'
51: from /var/lib/gems/2.7.0/gems/mercenary-0.4.0/lib/mercenary/program.rb:44:in go' 50: from /var/lib/gems/2.7.0/gems/mercenary-0.4.0/lib/mercenary/command.rb:221:in execute'
49: from /var/lib/gems/2.7.0/gems/mercenary-0.4.0/lib/mercenary/command.rb:221:in each' 48: from /var/lib/gems/2.7.0/gems/mercenary-0.4.0/lib/mercenary/command.rb:221:in block in execute'
47: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/commands/build.rb:18:in block (2 levels) in init_with_program' 46: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/command.rb:91:in process_with_graceful_fail'
45: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/command.rb:91:in each' 44: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/command.rb:91:in block in process_with_graceful_fail'
43: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/commands/build.rb:36:in process' 42: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/commands/build.rb:65:in build'
41: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/command.rb:28:in process_site' 40: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/site.rb:80:in process'
39: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/site.rb:211:in render' 38: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/site.rb:538:in render_pages'
37: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/site.rb:538:in each' 36: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/site.rb:539:in block in render_pages'
35: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/site.rb:547:in render_regenerated' 34: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/renderer.rb:63:in run'
33: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/renderer.rb:80:in render_document' 32: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/renderer.rb:131:in render_liquid'
31: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/liquid_renderer/file.rb:36:in render!' 30: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/liquid_renderer/file.rb:70:in measure_time'
29: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/liquid_renderer/file.rb:37:in block in render!' 28: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/liquid_renderer/file.rb:63:in measure_bytes'
27: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/liquid_renderer/file.rb:38:in block (2 levels) in render!' 26: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/liquid_renderer/file.rb:59:in measure_counts'
25: from /var/lib/gems/2.7.0/gems/jekyll-4.2.0/lib/jekyll/liquid_renderer/file.rb:39:in block (3 levels) in render!' 24: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/template.rb:220:in render!'
23: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/template.rb:207:in render' 22: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/template.rb:242:in with_profiling'
21: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/template.rb:208:in block in render' 20: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/block_body.rb:82:in render'
19: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/block_body.rb:103:in render_node_to_output' 18: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/tags/for.rb:79:in render'
17: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/tags/for.rb:150:in render_segment' 16: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/context.rb:123:in stack'
15: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/tags/for.rb:158:in block in render_segment' 14: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/tags/for.rb:158:in each'
13: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/tags/for.rb:160:in block (2 levels) in render_segment' 12: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/block_body.rb:80:in render'
11: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/block_body.rb:103:in render_node_to_output' 10: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/variable.rb:82:in render'
9: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/variable.rb:82:in inject' 8: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/variable.rb:82:in each'
7: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/variable.rb:84:in block in render' 6: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/context.rb:86:in invoke'
5: from /var/lib/gems/2.7.0/gems/liquid-4.0.3/lib/liquid/strainer.rb:56:in invoke' 4: from /var/lib/gems/2.7.0/gems/jekyll-exif-data-0.0.3/lib/jekyll/exif-data.rb:9:in exif'
3: from /var/lib/gems/2.7.0/gems/jekyll-exif-data-0.0.3/lib/jekyll/exif-data.rb:9:in new' 2: from /var/lib/gems/2.7.0/gems/exifr-1.3.9/lib/exifr/jpeg.rb:34:in initialize'
1: from /var/lib/gems/2.7.0/gems/exifr-1.3.9/lib/exifr/jpeg.rb:34:in open' /var/lib/gems/2.7.0/gems/exifr-1.3.9/lib/exifr/jpeg.rb:34:in initialize': No such file or directory @ rb_sysopen - photos/original/DSC03516.JPG (Errno::ENOENT)

ruby -v [21/08/23|14:27:25]
ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux-gnu]

Password protection

Hi, thanks for your work!
Is it possible to protect the stream via password without just using basic auth?
Thanks!

BASEURL seems not having any effect

Trying to deploy to my website's subfolder (eg. www.myserver.com/mydir/) so I have set BASEURL in .env correspondingly.

However, it seems there is no effect of doing this, not in HTML Header or in the generated links, which still looks like eg.: href="/css/master.css" - using absolute path and referencing the webserver's root, not my subfulder (/mydir/ in the example mentioned before).

wrong order on render

I followed the readme and built a static site on render.
It all worked nice and dandy, however the resulting order of photos seems wrong.
In the default setting photo-stream shows the photos first that have been submitted to github first.
Switching the default order via DEFAULT_REVERSE_SORT is not quite an option for me as I want to both host a number of exisiting photos and then append new ones from day to day.
As it stands I can't get the photos to be shown in the right order: the oldest photo of the old series is always shown next to the latest photo commited.
I appreciate that github does not know the date the photo was created, but can photo-stream pick up the time the photo was committed?

Uppercase file extension ".JPG" does not process correctly

Hi, when I add a file such as "PA289387.JPG" to photos/original, the directory and index.html will be created for it, but the image file itself will not be processed. Here's the server log:

[2021-08-31 21:16:51] ERROR /photos/tint/PA289387-65ba1d.JPG' not found. [2021-08-31 21:16:51] ERROR /photos/thumbnail/PA289387-ccf4df.JPG' not found.
[2021-08-31 21:16:53] ERROR `/photos/large/PA289387-686656.JPG' not found.

and in photo-stream\_site\photos, the image does not show under any of the directories.

When I change the extension to lowercase "PA289387.jpg" the file is processed immediately and everything works as expected.

I realize I could pre-process all my file names to be lowercase, but it would be cool if photo-stream could accept all casing. Tried to figure out how to do this myself, but I don't understand enough about Jekyll plugins. Any help appreciated!

Sort photos by MetadataDate

Because I publish old photos on my website https://instantanes.loeuillet.org/, and because I want to display them at the first position of the list (last published, first in the list), I made a change in photo_filter.rb, to use MetadataDate (available in Exif values):

      sorted = photos.sort_by { |photo|
          Exiftool.new(photo.path)[:metadata_date]
      }

The RSS feed is also generated like that and that's fine for me.

I didn't have a look to make it cleaner (with a setting in .env) because I'm not a ruby expert.
I just opened this issue to give you the idea 🙂

Video support

Any plans to support video? I envision it to look the very same except maybe to add a "triangle icon" over thumbnails so that its clear its a video.

What is the photo directory?

I am using docker to install and mount my image folder e.g. "picture" to /photos/original but there is no photo showing up. What have I done wrong?
image

PageCrypt breaks lazyload

In order to control the access to my photo-stream I implemented PageCrypt as described here.

Now I have very long loading times: Once the page is decrypted my browser not only loads all /tint/ files (this happens also w/o PageCrypt) but after that also all /large/ image files.

Can anyone confirm this behaviour? Is there a way to correct it?

Issue with direct links behind reverse proxy

Hello!

I am currently running the docker image from docker hub in kubernetes, behind a reverse proxy, and have discovered an issue that I know how to work around, but do not know how to fix. If I copy a URL to a photo (https://photos.altheashaheen.com/r0000336) and open that link directly, it will not resolve properly to my hostname for the proxy. It will redirect to my internal hostname (which is not publically accessible), and fail. If I do a curl to get the contents of the URL, it is a redirect, without the domain:

tsrats@macbookpro ~ % curl https://photos.altheashaheen.com/r0000336
<HTML><A HREF="/r0000336/">/r0000336/</A>.</HTML>

However, if I add a / to the end of the URL (https://photos.altheashaheen.com/r0000336/), then it works a-ok. I believe this is probably something to do with how Jekyll builds the site, but I am not familar with Jekyll so I don't know what I can do to fix that.

Any help would be greatly appreciated!

Thanks!

Make photos shareable via Web Share API

One of the features I would like to add to photo-stream is to make photos shareable via the Web Share API. The title should be configurable via an environment variable and the default title could be something like this:
I found a cool photo over at %TITLE%! Check it out!
The share button should be styled the same as the already existing buttons.

Album feature

Hey, I love this project. It's cool and saved me tons of hours updating our wordpress site to host about 300 photos.

I have one question and i'm not quite sure if i overread the answer. Is there some sort of album feature? That only specific photos are show if for example a special path is called or in what way so ever. Then I would build a small html site that links to the specific paths with the photos.
We would love to host photos of specific events in one page alone (so that other photos are not shown).

Thanks in advance for your help ❤️

support PNG

Hi, what can I do to make the project support PNG, thanks!

Photos not sorting based on exif data

I was about to see if I should just fork the repo in case this was a conscious decision, but I found a commit that shows it was not. From what I can tell, it is possibly sorting based of file modified date rather than exif data?

example: https://danbush.photos/ , all the BLM photos are from 2020 (and I confirmed by looking at the exif data in an exif viewer), and that big leaf photo (https://danbush.photos/dscf9180) is from 2021.

any file with -original etc in it, breaks

Anytime a photo is in /photos/original

if it has a filename with original, eg

 BMAB1537-original.jpg

This happens:

W, [2022-09-09T02:32:34.155186 #1]  WARN -- : Badly formed IFD: Infinity
  Liquid Exception: No such file or directory @ rb_sysopen - photos/original/VMAA2435-original-686656.jpg in feed.xml

Renaming the file, and deleting it like this... works

/share/BIG/Pictures/photo-stream/original % convert BMAB1537-original.jpg BMABMAB1537.jpg
/share/BIG/Pictures/photo-stream/original % ls -al BMAB1537-original.jpg BMABMAB1537.jpg
-rw-rw-r-- 1 sean 332348 Sep  8 20:12 BMAB1537-original.jpg
-rw-rw-r-- 1 sean 332319 Sep  8 20:31 BMABMAB1537.jpg
 /share/BIG/Pictures/photo-stream/original % \rm BMAB1537-original.jpg
  Liquid Exception: No such file or directory @ rb_sysopen - /photo-stream/photos/original/VMAA2435-original.jpg in feed.xml
             Error: No such file or directory @ rb_sysopen - /photo-stream/photos/original/VMAA2435-original.jpg
             Error: Run jekyll build --trace for more information.

      Regenerating: 1 file(s) changed at 2022-09-09 02:32:36
                    photos/original/VMAA2435-original.jpg

Feature Request: Resort Order

Not finding a way to change the sort order from newest to oldest to oldest to newest.

Would be cool to see this either as an on the fly button in the webUI or something set in the config prior to thumbnail creation.

If I'm just missing something, my apologies.

Docker error and questions

Hi, I'm starting right now with docker so please forgive my ignorance. I have some questions:

  1. I trying to start this docker image so with the terminal I used this commands:
    Screenshot 2023-03-17 at 12 22 13 PMbut then if I open my browser at localhost:8080 it shows nothing. Where am I going wrong?

  2. To edit images files do I have to go to Docker Desktop -> Containers -> My Container -> Files and here create a folder named "original" ?

  3. Without docker if I fork the repo and clone it locally with vscode how can I run it in my localhost ?

Support for resizing full screen images on mobile devices

If clicking an image to view it in full screen, you can't "pinch" to enlarge the image on mobile devices. Trying to do so will either close the picture or jump back/forth to other pictures. Would be excellent if you can click an image in the grid, view it full screen and then use familiar gestures to enlarge the image.

Orientation based on exif info [enhancement]

In current version exif is ignored, and vertical photos shown rotated:

image

But exif info about present present in file:

image

Would be good to honor exif orientation information in stream

pages always come up, in dutch?

Just got photo-stream going .. and I've noticed even on other examples, Chrome is wanting to translate from Dutch to English. Any idea why? I searched all around I can't see why-

Additional Gems

Thank you for continuing this. I wanted to add this in case it saves someone else some time.
I'm testing with:
ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [x86_64-linux]
jekyll 4.2.0
To be able to use bundle exec jekyll serve I had to add gem "webrick", "~> 1.7" to the Gemfile and to get it to read the /.env file I had to add gem "jekyll-dotenv"

Then added
-jekyll-dotenv under the plugins section of the _config.yml

Going to see if there is a way to randomize the photo names. So far I've just been running a script to do it before copying them into the folder

Error when deploying with docker-compose

Hi there,

I have been trying to deploy photo-stream using the Docker image that you provide.

I cloned the repository, and left docker-compose.yml and .env unchanged (I also tried with changing the values to my needs but still same result) and get the following error message with which the container exits:

root@dockerhost:/docker/photo-stream# docker-compose up 
Creating network "photo-stream_default" with the default driver
Creating photo-stream ... done
Attaching to photo-stream
photo-stream    | Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
photo-stream    | `/root` is not writable.
photo-stream    | Bundler will use `/tmp/bundler20220827-1-td0no01' as your home directory temporarily.
photo-stream    | bundler: command not found: jekyll
photo-stream    | Install missing gem executables with `bundle install`
photo-stream exited with code 127

Any idea what is going wrong? Thank you for your help!

Difficult to zoom in and navigate on mobile devices

So I'm using photostream to host a little gallery of my astrophotography; its difficult to zoom into an image on mobile devices because any touch on the screen causes a transition to the previous photo, the next photo, or the gallery view. Is there a way to make the... button areas... be maybe 10% of the window size so that one can easily pan/zoom on mobile?

Page breaks if title contains single quotes

Hi there, big fan of the project!

Apparently when trying to deploy using a title with single quotes (e.g. My Family's Photo Stream), the page breaks on this particular JavaScript part:

const shareImage = (title, url) => {
if (navigator.canShare) {
const shareData = {
title: title,
text: 'I found a cool photo over at {{ site.env.TITLE }}! Check it out!',
url: url
}
navigator.share(shareData)
} else {
navigator.clipboard.writeText(`I found a cool photo over at {{ site.env.TITLE }}! Check it out!\n\n${window.location.origin}${url}`);
Toastify({
text: "Copied to clipboard",
duration: 3000,
style: {
background: "rgba(0, 0, 0, 0.7)"
}
}).showToast();
}
}

Note on line 54, title is directly used without escaping or sanitizing the quotes. I am not familiar with Jekyll or Ruby, but should there be a way to escape or sanitize env values before usage? Maybe using xml_escape1?

- text: 'I found a cool photo over at {{ site.env.TITLE }}! Check it out!',
+ text: 'I found a cool photo over at {{ site.env.TITLE | xml_escape }}! Check it out!',

Footnotes

  1. https://jekyllrb.com/docs/liquid/filters/

Error when deploying via Docker

After the change in the docker-compose.yml it now successfully build. Thank you again for the fast help, but now it still doesn't work on my Raspberry Pi. I tried it with the example pictures and also with a own picture in jpg format. Bouth give the same error. Am I missing something or doing something wrong? Also I would love to help solving that deprecated call warning but I cannot find the file where this is called.

Output of docker logs photo-stream

Calling `DidYouMean::SPELL_CHECKERS.merge!(error_name => spell_checker)' has been deprecated. Please call `DidYouMean.correct_error(error_name, spell_checker)' instead.
Configuration file: /photo-stream/_config.yml
            Source: /photo-stream
       Destination: /photo-stream/_site
 Incremental build: disabled. Enable with --incremental
      Generating...
  Liquid Exception: No such file or directory @ rb_sysopen - photos/original/carrie-cronan-XNJdMuqtk70-unsplash.jpg in feed.xml
                    ------------------------------------------------
      Jekyll 4.2.2   Please append `--trace` to the `serve` command
                     for any additional information or backtrace.
                    ------------------------------------------------
/usr/local/bundle/gems/exifr-1.3.9/lib/exifr/jpeg.rb:34:in `initialize': No such file or directory @ rb_sysopen - photos/original/carrie-cronan-XNJdMuqtk70-unsplash.jpg (Errno::ENOENT)
        from /usr/local/bundle/gems/exifr-1.3.9/lib/exifr/jpeg.rb:34:in `open'
        from /usr/local/bundle/gems/exifr-1.3.9/lib/exifr/jpeg.rb:34:in `initialize'
        from /usr/local/bundle/gems/jekyll-exif-data-0.0.3/lib/jekyll/exif-data.rb:9:in `new'
        from /usr/local/bundle/gems/jekyll-exif-data-0.0.3/lib/jekyll/exif-data.rb:9:in `exif'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/strainer.rb:56:in `invoke'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/context.rb:86:in `invoke'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/variable.rb:84:in `block in render'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/variable.rb:82:in `each'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/variable.rb:82:in `inject'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/variable.rb:82:in `render'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/block_body.rb:103:in `render_node_to_output'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/block_body.rb:80:in `render'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/tags/for.rb:160:in `block (2 levels) in render_segment'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/tags/for.rb:158:in `each'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/tags/for.rb:158:in `block in render_segment'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/context.rb:123:in `stack'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/tags/for.rb:150:in `render_segment'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/tags/for.rb:79:in `render'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/block_body.rb:103:in `render_node_to_output'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/block_body.rb:82:in `render'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/template.rb:208:in `block in render'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/template.rb:242:in `with_profiling'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/template.rb:207:in `render'
        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/template.rb:220:in `render!'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/liquid_renderer/file.rb:39:in `block (3 levels) in render!'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/liquid_renderer/file.rb:59:in `measure_counts'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/liquid_renderer/file.rb:38:in `block (2 levels) in render!'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/liquid_renderer/file.rb:63:in `measure_bytes'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/liquid_renderer/file.rb:37:in `block in render!'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/liquid_renderer/file.rb:70:in `measure_time'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/liquid_renderer/file.rb:36:in `render!'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/renderer.rb:131:in `render_liquid'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/renderer.rb:80:in `render_document'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/renderer.rb:63:in `run'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/site.rb:547:in `render_regenerated'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/site.rb:539:in `block in render_pages'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/site.rb:538:in `each'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/site.rb:538:in `render_pages'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/site.rb:211:in `render'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/site.rb:80:in `process'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/command.rb:28:in `process_site'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/commands/build.rb:65:in `build'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/commands/build.rb:36:in `process'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/command.rb:91:in `block in process_with_graceful_fail'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/command.rb:91:in `each'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/command.rb:91:in `process_with_graceful_fail'
        from /usr/local/bundle/gems/jekyll-4.2.2/lib/jekyll/commands/serve.rb:86:in `block (2 levels) in init_with_program'
        from /usr/local/bundle/gems/mercenary-0.4.0/lib/mercenary/command.rb:221:in `block in execute'
        from /usr/local/bundle/gems/mercenary-0.4.0/lib/mercenary/command.rb:221:in `each'
        from /usr/local/bundle/gems/mercenary-0.4.0/lib/mercenary/command.rb:221:in `execute'
        from /usr/local/bundle/gems/mercenary-0.4.0/lib/mercenary/program.rb:44:in `go'
        from /usr/local/bundle/gems/mercenary-0.4.0/lib/mercenary.rb:21:in `program'
        from /usr/local/bundle/gems/jekyll-4.2.2/exe/jekyll:15:in `<top (required)>'
        from /usr/local/bundle/bin/jekyll:25:in `load'
        from /usr/local/bundle/bin/jekyll:25:in `<top (required)>'
        from /usr/local/bundle/gems/bundler-2.2.15/lib/bundler/cli/exec.rb:63:in `load'
        from /usr/local/bundle/gems/bundler-2.2.15/lib/bundler/cli/exec.rb:63:in `kernel_load'
        from /usr/local/bundle/gems/bundler-2.2.15/lib/bundler/cli/exec.rb:28:in `run'
        from /usr/local/bundle/gems/bundler-2.2.15/lib/bundler/cli.rb:494:in `exec'
        from /usr/local/bundle/gems/bundler-2.2.15/lib/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
        from /usr/local/bundle/gems/bundler-2.2.15/lib/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command'
        from /usr/local/bundle/gems/bundler-2.2.15/lib/bundler/vendor/thor/lib/thor.rb:392:in `dispatch'
        from /usr/local/bundle/gems/bundler-2.2.15/lib/bundler/cli.rb:30:in `dispatch'
        from /usr/local/bundle/gems/bundler-2.2.15/lib/bundler/vendor/thor/lib/thor/base.rb:485:in `start'
        from /usr/local/bundle/gems/bundler-2.2.15/lib/bundler/cli.rb:24:in `start'
        from /usr/local/bundle/gems/bundler-2.2.15/exe/bundle:49:in `block in <top (required)>'
        from /usr/local/bundle/gems/bundler-2.2.15/lib/bundler/friendly_errors.rb:130:in `with_friendly_errors'
        from /usr/local/bundle/gems/bundler-2.2.15/exe/bundle:37:in `<top (required)>'
        from /usr/local/bundle/bin/bundle:25:in `load'
        from /usr/local/bundle/bin/bundle:25:in `<main>'

Feature Request: Any kind of text upon clicking an image

Any text upon clicking an image, even a tiny text box would suffice. This project is perfect for me as I am trying to replace Instagram, but I usually include some text underneath images talking about my workflow.

Not having the option to include text with images is a unfortunately deal breaker, I would love to see this project with them!

Docker is halted after boot, seemingly doing nothing.

I went with the 'throw 8 galleries to the wall and see what works' approach. photo-stream is one of them.

Problem:

 Configuration file: /photo-stream/_config.yml
             Source: /photo-stream
        Destination: /photo-stream/_site
  Incremental build: disabled. Enable with --incremental
       Generating...
 Configuration file: /photo-stream/_config.yml

The container seems halted, doing nothing. Very small CPU and memory usage, no IO usage.

compose:

# domain.tld is a redaction, not actual
  domain.tld: # https://github.com/waschinski/photo-stream/
    image: waschinski/photo-stream:latest
    environment:
      - TITLE=domain.tld
      - [email protected]
      - AUTHOR_NAME=noreply
      - [email protected]
      - AUTHOR_WEBSITE=domain.tld
      - DESCRIPTION=domain.tld
      - BASEURL=https://domain.tld/ # also tried with BASEURL=
      - URL=https://domain.tld/
      - SHOW_OFFICIAL_GITHUB=1
    volumes:
      - /path/to/photos:/photo-stream/photos/original:ro
    labels:
      - "traefik.frontend.auth.basic.users=foo:bar"
      - traefik.frontend.rule=Host:domain.tld
      - traefik.port=4000
    networks:
      - proxy

Add a "back to homepage" link

With the new header bar, a link to the homepage could be a nice improvement.

If you want, I can work on this easy issue next week.

Optional header bar

As being asked for in #58 it'd be nice to have a header bar similar to what jad.photos has been done manually.

It could show the already existing TITLE next to an icon (maybe from a pre-defined selection or just use an icon library like Fontawesome?). Background and font color set using hex color codes.

Questions

Hi again,

is it possible to define on how to sort the images? E.g. by recording date?
Furthermore, how can I change the favicon when using docker-compose image?

Thanks! :)

Wrong timezone in RSS feed

I want to define the correct timezone for my website. For example, I have this date in my RSS feed:

<published>2023-03-03T14:11:01+00:00</published>

But in my feed agregator, the item is displayed in the future, at 3:11PM (FYI, I'm in France and I don't have the same datetime configuration than you).

Error building on a Raspberry Pi using Docker

I try to build this project by following the README.
First I pulled the repo and changed the docker-compose.yml according to the instructions provided by the README.md.

After the build finished without error, I tried to deploy the container using docker-compose up -d

Error I get:

Step 6/10 : COPY ./ /photo-stream
 ---> 67abb44bda60
Step 7/10 : WORKDIR /photo-stream
 ---> [Warning] The requested image's platform (linux/arm) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
 ---> Running in 52c90a63e1ab
Removing intermediate container 52c90a63e1ab
 ---> 7f2f829b64f2
Step 8/10 : RUN ruby -v && gem install bundler jekyll &&    bundle config --local build.sassc --disable-march-tune-native &&    bundle install
 ---> [Warning] The requested image's platform (linux/arm) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
 ---> Running in ff1d41042927
ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [arm-linux-musleabihf]
Successfully installed bundler-2.3.17
Successfully installed unicode-display_width-1.8.0
Successfully installed terminal-table-2.0.0
Successfully installed safe_yaml-1.0.5
Successfully installed rouge-3.29.0
Successfully installed forwardable-extended-2.6.0
Successfully installed pathutil-0.16.2
Successfully installed mercenary-0.4.0
Successfully installed liquid-4.0.3
Successfully installed kramdown-2.4.0
Successfully installed kramdown-parser-gfm-1.1.0
Building native extensions. This could take a while...
Successfully installed ffi-1.15.5
Successfully installed rb-inotify-0.10.1
Successfully installed rb-fsevent-0.11.1
Successfully installed listen-3.7.1
Successfully installed jekyll-watch-2.2.1
Building native extensions. This could take a while...
Successfully installed sassc-2.4.0
Successfully installed jekyll-sass-converter-2.2.0
Successfully installed concurrent-ruby-1.1.10
Successfully installed i18n-1.10.0
Building native extensions. This could take a while...
Successfully installed http_parser.rb-0.8.0
Building native extensions. This could take a while...
Successfully installed eventmachine-1.2.7
Successfully installed em-websocket-0.5.3
Successfully installed colorator-1.1.0
Successfully installed public_suffix-4.0.7
Successfully installed addressable-2.8.0
Successfully installed jekyll-4.2.2
27 gems installed
Your Ruby version is 3.0.1, but your Gemfile specified ~> 3.1.2
ERROR: Service 'photo-stream' failed to build: The command '/bin/sh -c ruby -v && gem install bundler jekyll &&    bundle config --local build.sassc --disable-march-tune-native &&    bundle install' returned a non-zero code: 18

OS Info:

OS: Debian GNU/Linux 11 (bullseye) aarch64
Host: Raspberry Pi 4 Model B Rev 1.2
Kernel: 5.15.32-v8+

docker-compose version output:

docker-compose version 1.25.0, build unknown
docker-py version: 4.1.0
CPython version: 3.9.2
OpenSSL version: OpenSSL 1.1.1n  15 Mar 2022

docker version output:

Client: Docker Engine - Community
 Version:           20.10.17
 API version:       1.41
 Go version:        go1.17.11
 Git commit:        100c701
 Built:             Mon Jun  6 23:02:34 2022
 OS/Arch:           linux/arm64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.17
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.17.11
  Git commit:       a89b842
  Built:            Mon Jun  6 23:01:01 2022
  OS/Arch:          linux/arm64
  Experimental:     false
 containerd:
  Version:          1.6.6
  GitCommit:        10c12954828e7c7c9b6e0ea9b0c02b01407d3ae1
 runc:
  Version:          1.1.2
  GitCommit:        v1.1.2-0-ga916309
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

Have a map for all pictures

On my website, I added a link to OpenStreetMap for each picture (thanks to exif, we have longitude and latitude).
A new page with a map of the world, with each picture on it, could be a nice feature.

WebP support?

Hello,

Is that possible to add WebP support to your great tool?

Direct links to images does not work

Hi there.

Tried to launch this using docker today, as you can see here: https://photoclass.myhrmans.com

Images load fine but when you open an image and copy the url to go directly to the image the following error is spotted out in the console:

Uncaught TypeError: Cannot read properties of null (reading 'getAttribute')
    at openPhoto ((index):66:29)
    at photos.js:7:1
    at photos.js:9:3

Using docker-compose.

jekyll serve --host=0.0.0.0

FYI if you're running this on a remote server, the command given in the example will bind it to localhost and photo-stream won't be available.

bundle exec jekyll serve --host=0.0.0.0 is a safer command that ensures it is reachable whever.

Cheers and nice work!

Useless file when deploying

When I build the website, why is there a docker-compose.yml at the root of it?

I don't think it's useful for a static website.

github/instagram buttons

  • no option to disable instagram button
  • SHOW_OFFICIAL_GITHUB refers to waschinski github button only so no option to disable github button too

immagine

immagine

BTW I noticed that the original github/instagram buttons are used when declaring GITHUB_USERNAME/INSTAGRAM_USERNAME...I'm not a code-guy but I wonder if the aforementioned issues maybe depend on this...

immagine
immagine

Thanks!

Trailing slash breaks URL in RSS feed

Thank you for photo-stream!

In the .env file, I put https://mywebsite.com/ for the URL setting, but in the generated RSS feed, I have <media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://mywebsite.com//photos/large/IMG_2809-686656.jpeg" /> with 2 slashes.

It should be removed but I don't see how I can fix it quickly.

Whats the docker image tag for arm 64bit processor

Hi there,

I'm unable to run using image: waschinski/photo-stream:latest on my Raspberry-pi 4 64bit. Couldn't find apt tag on dockerhub (https://hub.docker.com/r/waschinski/photo-stream/tags).

photo_stream_container |       Generating...
photo_stream_container | Configuration file: /photo-stream/_config.yml
photo_stream_container |   Liquid Exception: no start of image marker found in feed.xml
photo_stream_container |                     ------------------------------------------------
photo_stream_container |       Jekyll 4.2.0   Please append `--trace` to the `serve` command
photo_stream_container |                      for any additional information or backtrace.
photo_stream_container |                     ------------------------------------------------
photo_stream_container | /usr/local/bundle/gems/exifr-1.3.9/lib/exifr/jpeg.rb:102:in `examine': no start of image marker found
(EXIFR::MalformedJPEG)
photo_stream_container |        from /usr/local/bundle/gems/exifr-1.3.9/lib/exifr/jpeg.rb:34:in `block in initialize'
photo_stream_container |        from /usr/local/bundle/gems/exifr-1.3.9/lib/exifr/jpeg.rb:34:in `open'
photo_stream_container |        from /usr/local/bundle/gems/exifr-1.3.9/lib/exifr/jpeg.rb:34:in `initialize'
photo_stream_container |        from /photo-stream/_plugins/photo_filter.rb:8:in `new'
photo_stream_container |        from /photo-stream/_plugins/photo_filter.rb:8:in `block in photo_filter'
photo_stream_container |        from /photo-stream/_plugins/photo_filter.rb:7:in `each'
photo_stream_container |        from /photo-stream/_plugins/photo_filter.rb:7:in `sort_by'
photo_stream_container |        from /photo-stream/_plugins/photo_filter.rb:7:in `photo_filter'
photo_stream_container |        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/strainer.rb:56:in `invoke'
photo_stream_container |        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/context.rb:86:in `invoke'
photo_stream_container |        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/variable.rb:84:in `block in render'
photo_stream_container |        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/variable.rb:82:in `each'
photo_stream_container |        from /usr/local/bundle/gems/liquid-4.0.3/lib/liquid/variable.rb:82:in `inject'

Default sorting

Hi! Just implemented this project on my self hosted website in an evening and I love it! There's one thing that I kind of miss: default sorting. Now the sorting is new->old (as far as I can see) but this does not work for the pictures of my wedding. I would like to show them old->new. Is this something that I could easily add?

Thanks!
Jesse

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.