Giter Site home page Giter Site logo

smilyorg / photofield Goto Github PK

View Code? Open in Web Editor NEW
389.0 9.0 7.0 13.08 MB

Experimental fast photo viewer.

License: MIT License

PowerShell 0.36% Go 62.03% HTML 0.09% Dockerfile 0.23% Vue 24.29% CSS 0.84% JavaScript 7.04% Jupyter Notebook 1.12% Just 0.57% TypeScript 2.54% Gherkin 0.88%
photos gallery photoviewer golang vue3 self-hosted

photofield's Introduction


Logo

Photofield

Experimental fast photo viewer.

Demo ยท Report Bug ยท Request Feature

Table of Contents
  1. About
  2. Getting Started
  3. Configuration
  4. Usage
  5. Maintenance
  6. Development Setup
  7. Contributing
  8. License
  9. Acknowledgements

About

Zoom to logo within a 43k images

Zoom to logo within a sample of 43k images from open-images-dataset, i7-5820K 6-Core CPU, NVMe SSD

Photofield is a photo viewer built to mainly push the limits of what is possible in terms of the number of photos visible at the same time and at the speed at which they are displayed. The goal is to be as fast or faster than Google Photos on commodity hardware while displaying more photos at the same time. It is non-invasive and can be used either completely standalone or complementing other photo gallery software.

Features

  • Seamless zoomable interface. Thanks to tiled image loading supported by OpenLayers and the API implementing tile rendering, you can switch between levels of detail seamlessly without loading a special detailed or fullscreen view.

    Seamless zoom to giraffe face

  • Progressive multi-resolution loading. Not only are thumbnails used to show a single photo quicker, the whole layout is progressively loaded, so even if you move through photos quickly or zoom around, you will almost always have some form of feedback to not lose track.

    Progressive load of a deer

  • Different layouts. Collections of photos can be displayed with different layouts. layout examples

    • Album: chronological photos grouped by event
    • Timeline: reverse-chronological timeline similar to Google Photos
    • Wall: a square collage of all the photos, because zooming is fun!
    • Map: all the photos on a map? Sure!
    • More future ideas?
  • Semantic search using photofield-ai (alpha). If you set up an AI server and configure it in the ai section of the configuration, you should be able to search for photo contents using words like "beach sunset", "a couple kissing", or "cat eyes". semantic search for "cat eyes"

  • Tagging (alpha). You can tag photos with arbitrary tags. Currently tags are only stored in the database and not in the photos themselves. You need to enable them in the tags section of the configuration and restart the server. This forms a foundation for many other features, see below (checked - implemented).

    • Persistent photo selection. You can Ctrl+Click or Ctrl+Drag to select photos. This creates a new randomly generated "selection" tag that is persistent and shareable. It also means you can select tens of thousands of photos without losing your progress. These tags are currently never cleaned up and you can't do anything with it yet, so it's not useful yet, but it's a start.
    • Custom tags. You canadd your own tags to photos, e.g. #family or #vacation. Batch tagging not supported yet, but should be relatively easy to add considering the selections (above) are already tags.
    • EXIF tags. Tags are automatically added from the EXIF data, e.g. exif:make:sony or exif:model:sm-g950f. You need to enable this in the exif section of the configuration. Only make and model are currently supported (hardcoded).
    • Filter by tags. You can filter by a tag by searching for tag:TAG. For example, you can search for tag:fav to only show favorited photos, or tag:hello tag:world to only show photos with both hello and world tags. This is an early version of filtering and should be more user-friendly in the future.
    • Location tags. Photos could be automatically tagged with the location, e.g. city:berlin or country:germany. See #59.
    • Face recognition. Photos could be automatically tagged with the person's name. This would be a great way to search for photos of a specific person.
  • Reverse geolocation. Local, embedded reverse geolocation via tinygpkg. Does not need any API calls, has negligible performance impact, and supports ~50 thousand places. Currently only supported for photos with GPS coordinates in the EXIF data and the Timeline view.

  • Flexible media/thumbnail system. Do you have hundreds of gigabytes of existing thumbnails from an existing system? Me too! Let's reuse those. Don't have any? No worries, they will be generated automatically to speed up display. Here are the currently supported thumbnail sources:

    • Bespoke SQLite thumbnail database - photofield.thumbs.db.
    • Synology Moments / Photo Station auto-generated thumbnails in @eaDir.
    • Embedded JPEG thumbnails - ThumbnailImage Exif tag.
    • Native Go image package.
    • FFmpeg on-the-fly conversion - thumbnails and full sized variants.
    • Configurable via the sources section of the Configuration.
    • Please open an issue for other systems, bonus points for an idea on how to integrate!
  • Single file binary. Thanks to Go and GoReleaser, all the dependencies are packed into a single binary file for most major OSes.

  • Read-only file system based collections. Photofield never changes your photos, thumbnails or directories. You are encouraged to even mount your photos as read-only to ensure this. The file system is the source of truth, everything else is just a more or less stale cache.

  • Fast indexing. Thanks to godirwalk, file indexing practically runs at the speed of the file system 1000-10000 files/sec on fast SSD and hot cache. EXIF metadata and prominent color are extracted as separate follow-up operations and run at up to ~200 files/sec and ~1000 files/sec on a fast system.

  • Basic video support. Videos are supported, however the user experience is not great yet as there are some usability quirks. Different resolutions are supported if they have been previously transcoded, but there is no on-the-fly transcoding supported right now.

Limitations

  • No photo details (yet). There is no way to show metadata of a photo in the UI at this point.
  • Not optimized for many clients. As a lot of the normally client-side state is kept on the server, you will likely run into CPU or Memory problems with more than a few simultaneous users.
  • No user accounts. Not the focus right now. You can define separate collections for separate users based on the directory structure, but there is no authentication or authorization support.
  • Initial load can be slow. All the photos need to be laid out when you first load a page in a specific window size and configuration, which can take some time with a slow CPU and cold HDD cache.
  • No permalinks. Deep linking to images works, but it's currently not stable over time as IDs can change.

Built With

Getting Started

Docker

Make sure you create an empty data directory in the working directory and that you put some photos in a photos directory.

docker run -p 8080:8080 -v "$PWD/data:/app/data" -v "$PWD/photos:/app/photos:ro" ghcr.io/smilyorg/photofield

The cache database will be persisted to the data dir and the app should be accessible at http://localhost:8080. It should show the photos collection by default. For further configuration, create a configuration.yaml in the data dir.

docker-compose.yaml example

This example binds the usual Synology Moments photo directories and assumes a certain path structure, modify to your needs graciously. It also assumes you have configured the /photo and /user directories as collections in the configuration.yaml.

version: '3.3'
services:

  photofield:
    image: ghcr.io/smilyorg/photofield:latest
    ports:
      - 8080:8080
    volumes:
      - /volume1/docker/photofield/data:/app/data
      - /volume1/photo/:/photo:ro
      - /volume1/homes/ExampleUser/Drive/Moments:/exampleuser:ro

Binaries

  1. Download and unpack a release.
  2. Run ./photofield or double-click on photofield.exe to start the server.
  3. Open http://localhost:8080, folders in the working directory will be displayed as collections. ๐ŸŽ‰
  • ๐Ÿ“ Create a configuration.yaml in the working dir to configure the app
  • ๐Ÿ•ต๏ธโ€โ™€๏ธ Install exiftool and add it to PATH for better metadata support (esp. for video)
  • โšช Set the PHOTOFIELD_DATA_DIR environment variable to change the path where the app looks for the configuration.yaml and cache database

Configuration

You can configure the app via configuration.yaml.

The location of the file depends on the installation method, see Getting Started.

The following is a minimal configuration.yaml example, see defaults.yaml for all options.

collections:
  # Normal Album-type collection
  - name: Vacation Photos
    dirs:
      - /photo/vacation-photos

  # Timeline collection (similar to Google Photos)
  - name: My Timeline
    layout: timeline
    dirs:
      - /photo/myphotos
      - /exampleuser

  # Create collections from sub-directories based on their name
  - expand_subdirs: true
    expand_sort: desc
    dirs:
      - /photo

Usage

This section will cover some obvious uses, but also some possibly unintuitive UI quirks that exist in the current version.

App Bar

App bar explanation

Photo Viewer

  • Click to zoom to a photo

    • Escape or pinch out to get back to the list of photos
  • Zoom in/out directly with Ctrl/Cmd+Wheel

  • Pinch-to-zoom on touch devices

  • Press/hold Arrow Left or Arrow Right to quickly switch between photos

  • Right-click or long-tap as usual to open a custom context menu allowing you to copy or download original photos or thumbnails.

    context menu

    You can open/copy/copy link the original or access any existing thumbnails that already exist for it with the bottom list of thumbnails by pixel width.

Maintenance

Over time the cache database can grow in size due to version upgrades and so on. To shrink the database to its minimum size, you can vacuum it. Multiple vacuums in a row have no effect as the vacuum itself rewrites the database from the ground up.

While the vacuum is in progress, it will take twice the database size and may take several minutes if you have lots of photos and a low-power system.

As an example it took around 5 minutes to vacuum a 260 MiB database containing around 500k photos on a DS418play. The size after vacuuming was 61 MiB as all the leftover data from database upgrades was cleaned up.

# CLI
./photofield -vacuum

# Docker
docker exec -it photofield ./photofield -vacuum

Development Setup

Prerequisites

  • Go - for the backend / API server
  • Node.js - for the frontend
  • just - to run common commands conveniently
  • watchexec - for auto-reloading the Go server
  • sh-like shell (e.g. sh, bash, busybox) - required by just
  • exiftool - for testing metadata extraction

Scoop (Windows): scoop install busybox just exiftool watchexec

Installation

  1. Clone the repo
    git clone https://github.com/smilyorg/photofield.git
  2. Install Go dependencies
    go get
  3. Install NPM packages
    cd ui
    npm install

Running

Run both the API server and the UI server in separate terminals. They are set up to work with each other by default with the API server running at port 8080 and the UI server on port 3000.

just is just as defined in the prerequisites.

API

  • just watch the source files and auto-reload the server using watchexec
  • or just run the server

UI

  • just ui to start a hot-reloading development server
  • or run from within the ui folder
    cd ui
    npm run dev

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

License

Distributed under the MIT License. See LICENSE for more information.

Acknowledgements

photofield's People

Contributors

luusl avatar smilyorg avatar zach-capalbo 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

photofield's Issues

Better layout selection

  • Common / useful layouts should be directly accessible via buttons in the app bar (e.g. map icon button for map view)
  • The default layout should not be shown as "default", but as the actual layout name.

Object/face/anything detection

Alright, so this will be a one-sided dump of a conversation and it might be a completely stupid idea, but it's so stupid it might work.

When it comes to searching for things/people/faces...

one fun thing that's probably not super useful, but seems easy with the AI embeddings, would be text/image arithmetic. For example, searching for lion -male +female would return images of lionesses. Or img:[photo of a bike]+person would return photos of people riding bikes. ๐Ÿคทโ€โ™‚๏ธ Seems fun ๐Ÿ˜

Face recognition/detection, but what if it was different? Related to #46

I agree that face recognition is hard and faulty problem. I've been thinking how to tackle it, so if you don't mind indulging me for a moment.

So what I've usually seen is that face detection is a different process from face recognition. That is, with detection you know you have a million faces, but you don't have any names and only a certain confidence on the unique people those faces are from. The recognition is differentiating these faces.

Usually then what many apps do is they show you all the presumably unique faces and allow you to name them. And then since recognition is not infallible, they also allow you to accept and reject individual instances of a face to better train the model on the person. Now this is pretty standard and there are solutions for it already, so it's a safe way to go.

However! Integrating all that sounds a bit boring and I'm here to have fun, so I've been thinking of something else, which is so crazy it might work, or be a complete waste of weeks of development... But hear me out.

What if you think of the naming of a face (ie creating a person) as creating an "auto" person tag. Say that you take a reference image of the face of the person and then compute the tag by using the "related images" functionality and tagging any images that pass a similarity threshold. Maybe that would be pretty good already as a first try, but since there is only one reference image, it would probably find all kinds of other unrelated stuff.

So what if we take it one step further. Let's still have the one output auto tag, but then also have two "input" tags, one for "accepted" images and one for "rejected" ones, same as the face recognition systems record accepted and rejected faces. Then you could pick a model (eg logistic regression) to "train" on these positive and negative examples and at the end apply it to all images to get a potentially more accurate output auto face tag. Now this is just reinventing face recognition badly probably, however...

None of what I said is even specific to faces. If the CLIP AI embeddings are "expressive enough", you could theoretically have trained auto tags for your partner, your dog, for a specific bridge you often take photos of, for a certain type of cloud, for food pics, as long as you provide enough examples. Presumably the model would pick up on many cues beyond the face, like clothes and so on, so perhaps it could even detect people with obscured faces. It'd be like training (or fine tuning) small dumb AI models, but more interactively, by the user directly, and without the overhead usually associated with it. Or like "few shot detection" in ML lingo.

But I'm not an AI scientist, so it could also be a complete trash fire that works like shit. ๐Ÿคทโ€โ™‚๏ธ Only one way to find out

Linear regression for tags or something ๐Ÿคท

With accept/reject I meant providing the ground truth, by tagging it with e.g. person:alice:accept (could also be "in" or "+") you would say that the photo definitely contains Alice in it. With alice:reject or alice:out or alice:- you would say that this photo definitely does NOT have Alice in it. These would be just normal manual tags otherwise.

Then you could have a training process that takes e.g. (alice:+, alice:-, threshold:0.3) as input parameters, removes the person:alice tag from all photos and adds it back based on the new result. So as you say you could tune the threshold and the ground truth examples in case there are too many armpits or siblings detected :)

I agree that the UX would need to be slick for this to be usable, nobody will do it if you have to manually add the tags yourself. But kind of an interactive auto refreshing results page that updates as you click to accept/reject candidates would be sweet. If you really wanted to gamify it, you could even do a Tinder-like swipe left/right to say if it's a picture of your dog or not lol.

To be validated.

Basic Image Manipulation in WebUI

Is your feature request related to a problem? Please describe.
Depending on image metadata, an image may or may not be oriented properly.

Describe the solution you'd like
Having some basic image manipulation in the web UI would be a nice. Having those edits saved or reflected in export would be a plus.

  • Rotating an image (90deg increments)
  • Flipping an image (along X, along Y)
  • Cropping an image
  • Adjusting image profile (brightness, contrast, saturation, ...)
  • Keyboard shortcuts

Additional context
Orientation/rotation is handling in the goexif

Map improvements

  • The extent of the map should be the extent of the data / photos, not the whole world
  • Photos should be grouped by place with the name written on top
  • Show arrows between time-adjacent, but geo-distanced photos

SQLlite error on first run, executable for arm64v8 / raspberrypi 4

Describe the bug
Getting an SQLlite syntax error on first run. Executing the precompiled binary for arm64v8.

panic: sqlite: prepare "\n\tSELECT COUNT(id)\n\tFROM infos\n\tWHERE path_prefix_id IN (\n\t\tSELECT id\n\t\tFROM prefix\n\t\tWHERE\n\t\n\t\t)\n\t": SQL logic error: near ")": syntax error

goroutine 1 [running]:
zombiezen.com/go/sqlite.(*Conn).Prep(0x4000218be0, {0x40002ba000, 0x63})
        /home/runner/go/pkg/mod/zombiezen.com/go/[email protected]/sqlite.go:431 +0x130
photofield/internal/image.(*Database).GetDirsCount(0x400025b580, {0x0, 0x0, 0x0?})
        /home/runner/work/photofield/photofield/internal/image/database.go:722 +0x140
photofield/internal/image.(*Source).GetDirsCount(0x40000b6010?, {0x0?, 0x22?, 0xfa2d4?})
        /home/runner/work/photofield/photofield/internal/image/source.go:450 +0x38
photofield/internal/collection.(*Collection).UpdateStatus(0x40002bfc80, 0x48?)
        /home/runner/work/photofield/photofield/internal/collection/collection.go:78 +0x88
main.main()
        /home/runner/work/photofield/photofield/main.go:1234 +0xe54

To Reproduce
Steps to reproduce the behavior:

  1. downloaded release https://github.com/SmilyOrg/photofield/releases/download/v0.10.0/photofield_0.10.0_Linux_arm64.tar.gz
  2. make working directoy ~/photofield/
  3. extract contents to directory. Contents are:
  • LICENSE
  • photofield
  • README.md
  1. Create file configuration.yaml. Contents are
collections:
  - name: myPhotos
    layout: timeline
    dirs:
        - /mnt/myPhotos

I should note that the dir where the images are it's a network mounted cifs folder, belonging to the same user executing the script with 755 perms
5. Execute ./photofield
6. Output is as stated above, even after executing ./photofield -vacuum and deleting all photofield.cache.* and photofield.thumbs.* files

Expected behavior
No error and server starts correctly

Desktop:

  • OS: Debian GNU/Linux 11 (bullseye) (Raspberry Pi OS 11)

Refreshing the view can be way more complicated than it should be

Describe the bug
There are various ways to refresh various "caches", e.g. rescanning, reindexing, refreshing the page, refreshing the layout, etc. It's been like this due to some implementation difficulties and for debugging, but it's just dumb.

To Reproduce
Various caches fail in various ways.

Expected behavior
Ideally the user should care exactly 0% about this, all of it is just a workaround for lacking implementation. However realistically, it's hard to get rid of all of the "refresh buttons" without a lot of effort, so here's a ranked list of which buttons should really not even exist and which ones are common enough to be acceptable for some more time.

  1. Refresh layout. Very dumb and should not exist. This should be handled by tracking all the dependencies and their staleness and automatically refreshing stale layouts.
    image

  2. Reindexing metadata/colors/etc. Somewhat dumb and usually not needed already, but could be improved. Mostly used when new features are added that aren't picked up automatically.
    image

  3. Rescanning files. Somewhat less egregious as the two examples above. Should be solved by #73, but implementation is tricky.
    image

  4. Reloading the page for updated. Updating generally doesn't even need a reload, but it's usually foiled by the three points above, so it's the least urgent to fix.

panic: determinant of affine transformation matrix is zero

This is the error I found in logs of the docker container while trying to load a wall of ~95k photos.

Below is the whole stack trace:

photofield-photofield-1  |  panic: determinant of affine transformation matrix is zero
photofield-photofield-1  |  
photofield-photofield-1  |  -> runtime/debug.Stack
photofield-photofield-1  |  ->   /opt/hostedtoolcache/go/1.19.7/x64/src/runtime/debug/stack.go:24
photofield-photofield-1  | 
photofield-photofield-1  |     github.com/go-chi/chi/v5/middleware.PrintPrettyStack
photofield-photofield-1  |       /home/runner/go/pkg/mod/github.com/go-chi/chi/[email protected]/middleware/recoverer.go:44
photofield-photofield-1  |     github.com/go-chi/chi/v5/middleware.Recoverer.func1.1
photofield-photofield-1  |       /home/runner/go/pkg/mod/github.com/go-chi/chi/[email protected]/middleware/recoverer.go:30
photofield-photofield-1  |     panic
photofield-photofield-1  |       /opt/hostedtoolcache/go/1.19.7/x64/src/runtime/panic.go:884
photofield-photofield-1  |     github.com/tdewolff/canvas.Matrix.Inv
photofield-photofield-1  |       /home/runner/go/pkg/mod/github.com/tdewolff/[email protected]/util.go:404
photofield-photofield-1  |     photofield/internal/render.(*Scene).Draw
photofield-photofield-1  |       /home/runner/work/photofield/photofield/internal/render/scene.go:116
photofield-photofield-1  |     main.drawTile
photofield-photofield-1  |       /home/runner/work/photofield/photofield/main.go:216
photofield-photofield-1  |     main.GetScenesSceneIdTilesImpl
photofield-photofield-1  |       /home/runner/work/photofield/photofield/main.go:715
photofield-photofield-1  |     main.(*Api).GetScenesSceneIdTiles
photofield-photofield-1  |       /home/runner/work/photofield/photofield/main.go:627
photofield-photofield-1  |     photofield/internal/openapi.(*ServerInterfaceWrapper).GetScenesSceneIdTiles.func1
photofield-photofield-1  |       /home/runner/work/photofield/photofield/internal/openapi/api.gen.go:892
photofield-photofield-1  |     photofield/internal/openapi.(*ServerInterfaceWrapper).GetScenesSceneIdTiles
photofield-photofield-1  |       /home/runner/work/photofield/photofield/internal/openapi/api.gen.go:899
photofield-photofield-1  |     net/http.HandlerFunc.ServeHTTP
photofield-photofield-1  |       /opt/hostedtoolcache/go/1.19.7/x64/src/net/http/server.go:2109
photofield-photofield-1  |     github.com/go-chi/chi/v5.(*ChainHandler).ServeHTTP
photofield-photofield-1  |       /home/runner/go/pkg/mod/github.com/go-chi/chi/[email protected]/chain.go:31
photofield-photofield-1  |     github.com/go-chi/chi/v5.(*Mux).routeHTTP
photofield-photofield-1  |       /home/runner/go/pkg/mod/github.com/go-chi/chi/[email protected]/mux.go:442
photofield-photofield-1  |     net/http.HandlerFunc.ServeHTTP
photofield-photofield-1  |       /opt/hostedtoolcache/go/1.19.7/x64/src/net/http/server.go:2109
photofield-photofield-1  |     github.com/go-chi/chi/v5.(*Mux).ServeHTTP
photofield-photofield-1  |       /home/runner/go/pkg/mod/github.com/go-chi/chi/[email protected]/mux.go:71
photofield-photofield-1  |     github.com/go-chi/chi/v5.(*Mux).Mount.func1
photofield-photofield-1  |       /home/runner/go/pkg/mod/github.com/go-chi/chi/[email protected]/mux.go:314
photofield-photofield-1  |     net/http.HandlerFunc.ServeHTTP
photofield-photofield-1  |       /opt/hostedtoolcache/go/1.19.7/x64/src/net/http/server.go:2109
photofield-photofield-1  |     github.com/go-chi/chi/v5.(*Mux).routeHTTP
photofield-photofield-1  |       /home/runner/go/pkg/mod/github.com/go-chi/chi/[email protected]/mux.go:442
photofield-photofield-1  |     net/http.HandlerFunc.ServeHTTP
photofield-photofield-1  |       /opt/hostedtoolcache/go/1.19.7/x64/src/net/http/server.go:2109
photofield-photofield-1  |     github.com/go-chi/chi/v5.(*Mux).ServeHTTP
photofield-photofield-1  |       /home/runner/go/pkg/mod/github.com/go-chi/chi/[email protected]/mux.go:71
photofield-photofield-1  |     github.com/go-chi/chi/v5.(*Mux).Mount.func1
photofield-photofield-1  |       /home/runner/go/pkg/mod/github.com/go-chi/chi/[email protected]/mux.go:314
photofield-photofield-1  |     net/http.HandlerFunc.ServeHTTP
photofield-photofield-1  |       /opt/hostedtoolcache/go/1.19.7/x64/src/net/http/server.go:2109
photofield-photofield-1  |     github.com/go-chi/chi/v5.(*Mux).routeHTTP
photofield-photofield-1  |       /home/runner/go/pkg/mod/github.com/go-chi/chi/[email protected]/mux.go:442
photofield-photofield-1  |     net/http.HandlerFunc.ServeHTTP
photofield-photofield-1  |       /opt/hostedtoolcache/go/1.19.7/x64/src/net/http/server.go:2109
photofield-photofield-1  |     github.com/go-chi/chi/v5/middleware.Recoverer.func1
photofield-photofield-1  |       /home/runner/go/pkg/mod/github.com/go-chi/chi/[email protected]/middleware/recoverer.go:37
photofield-photofield-1  |     net/http.HandlerFunc.ServeHTTP
photofield-photofield-1  |       /opt/hostedtoolcache/go/1.19.7/x64/src/net/http/server.go:2109
photofield-photofield-1  |     main.instrumentationMiddleware.func1
photofield-photofield-1  |       /home/runner/work/photofield/photofield/main.go:114
photofield-photofield-1  |     net/http.HandlerFunc.ServeHTTP
photofield-photofield-1  |       /opt/hostedtoolcache/go/1.19.7/x64/src/net/http/server.go:2109
photofield-photofield-1  |     github.com/go-chi/chi/v5.(*Mux).ServeHTTP
photofield-photofield-1  |       /home/runner/go/pkg/mod/github.com/go-chi/chi/[email protected]/mux.go:88
photofield-photofield-1  |     net/http.serverHandler.ServeHTTP
photofield-photofield-1  |       /opt/hostedtoolcache/go/1.19.7/x64/src/net/http/server.go:2947
photofield-photofield-1  |     net/http.(*conn).serve
photofield-photofield-1  |       /opt/hostedtoolcache/go/1.19.7/x64/src/net/http/server.go:1991
photofield-photofield-1  |     created by net/http.(*Server).Serve
photofield-photofield-1  |       /opt/hostedtoolcache/go/1.19.7/x64/src/net/http/server.go:3102

Maybe it's just to much photos, but I wanted to experiment with it and see if it would be possible.

Also I must say, it's a great project, I hope to see it grow into something even more powerful!

Nextcloud integration

All of the long term Nextcloud users suffer with a terribly broken, poor performance photo gallery app. An easy integration Nextcloud App for Photofield would be a huge boon to the community. I'm not aware of any other project for photos that has done that yet. - adx442

Improved search

Is your feature request related to a problem? Please describe.
The current search implementation is not discoverable and has lots of limitations, see #65 (comment). It's not possible to combine search terms or have expressive search queries.

Describe the solution you'd like

Describe alternatives you've considered
Not doing it or making it simpler ๐Ÿ˜…

Additional context
Add any other context or screenshots about the feature request here.

Quick welcome tutorial

You can set up a quick features tutorial with 2-3 steps on screen. Lots of commercial web apps use this tactic to reduces the clutter but still inform of features. Just don't overdo it or people won't remember / will just skip it. - H_Q_

Pull Request "next" builds

Is your feature request related to a problem? Please describe.
It's annoying to not be able to easily publish a "next" release from a pull request for testing, so I usually just merge and ๐Ÿคทโ€โ™‚๏ธ

Describe the solution you'd like
Each PR is built and published as a "next" release via goreleaser. See goreleaser/goreleaser#1534

Describe alternatives you've considered
๐Ÿคทโ€โ™‚๏ธ

Additional context
Make sure it's not possible to create pwn requests

Better zoomed-in gallery functionality

Is your feature request related to a problem? Please describe.
It's not clear that it's even working as intended as the zoomed in view is very bare right now.

Describe the solution you'd like

A few things would make it better

  • Arrow/X icon top left to zoom back out by tapping instead of zooming, perhaps another tap of the photo can also zoom out
  • Visible/clickable left/right arrows for moving back and forth
  • Support for swiping on mobile
  • Action icon buttons on top right or somewhere for common functionality (e.g. download)
  • Single image view (not showing photos on the side)
  • Image details/metadata (as a sidebar?), maybe this big enough to be it's own issue

Document how the implementation works

Having an in-depth, but comprehensive (blog post?) about what went into making this fast would

  1. be good for my own reference and more importantly
  2. give other developers more of an idea of how it could be implemented or packaged up for their use (see comment from DerNeuere)

DNG files render with wrong colors/lighting

After loading my library, I noticed that theres a lot of dark images from a few days. It turns out I was on a trip and I was taking photos in RAW, so DNG files from my Pixel4a.

This is what the image looks like in the browser:
image

This is what it should look like:
image

Example image:
PXL_.tar.gz

I'm running it with docker-compose.

Unable to find configuration.yaml file in data directory

Describe the bug
When using docker-compose, the stack is unable to open the configuration.yaml file and, as a result, does not publish photos

To Reproduce
Steps to reproduce the behavior:

  1. I create a configuration.yaml file (following the defaults in the documentation) into the local directory bound to the /app/data directory in the stack
  2. I give 777access rights to the file (not secure, but for now I want to eliminate any access right issues)
  3. I docker-compose up or, in my case, use Portainer
  4. The stack is launched, the container is launched too but it does not publish anything on the chosen port.
  5. The log reports this error:
2021/11/07 12:40:51 unable to open configuration.yaml, using defaults (open configuration.yaml: no such file or directory)

I checked within the container for the configuration.yaml file: it is indeed there with the right content.

Expected behavior
The container should publish photos on the chosen port.

Environment
Debian 10

Product docs

Is your feature request related to a problem? Please describe.
README.md is getting out of hand. Would be nice to have actual proper "product" docs, e.g. mkdocs.

Describe the solution you'd like
https://github.com/SmilyOrg/photofield/tree/docs has a WIP of what it could look like.

Describe alternatives you've considered
Godoc, but it's very library-focused. It would be good to have docs on how to use it, explaining the different configuration options in more detail, how to install it in different ways, etc.

PhotoPrism integration

Is your feature request related to a problem? Please describe.

Greta job! Any way to re use the thumbnail generatesi by photoprism? They are saved in the filesys but the name and directory are not related to the original image name - Diccole

Describe the solution you'd like
Figure out the feasibility of yoinking thumbnails from PhotoPrism. It might be tricky if database access is required.

panic: Unable to compute cost, unsupported image format *image.Paletted

Describe the bug
Whenever I try to open an auto-generated collection (my Messenger photos and videos), the app crashes.

Screenshots

Attaching the logs from the crash

2022/09/10 17:38:53 load meta 25% completed, 499 loaded, 1444 pending, 4.50 / sec
2022/09/10 17:38:55 load meta 26% completed, 511 loaded, 1432 pending, 5.66 / sec
2022/09/10 17:38:56 load color 4% completed, 159 loaded, 3751 pending, 1.25 / sec
2022/09/10 17:38:57 load meta 27% completed, 527 loaded, 1416 pending, 7.89 / sec
panic: Unable to compute cost, unsupported image format *image.Paletted
goroutine 38 [running]:
photofield/internal/image.newImageCache.func1({0xcaa940, 0xc01209d770})
/home/runner/work/photofield/photofield/internal/image/cache.go:100 +0x285
github.com/dgraph-io/ristretto.(*Cache).processItems(0xc00023ac00)
/home/runner/go/pkg/mod/github.com/dgraph-io/[email protected]/cache.go:303 +0x109
created by github.com/dgraph-io/ristretto.NewCache
/home/runner/go/pkg/mod/github.com/dgraph-io/[email protected]/cache.go:162 +0x347

Additional context

This particular folder contains some corrupted jpeg files and I know for a fact that some apps have trouble crawling them. I believe Photofield can't handle them and crashes.

Also hey, H_Q_ here.

Better loading indicators

Is your feature request related to a problem? Please describe.
Many background loads are currently not indicated and errors are also not reported, making it confusing whether there's a problem, or it's just slow, or what.

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

iOS Live Photo Support?

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

Scene constantly loads while contents are being indexed

Describe the bug
While image contents are loaded the scene seems to never end loading

image

image

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

After scrolling for a while, photos no longer load

Describe the bug
After scrolling for a while in a large album, I reach pixellated thumbnails, then blurred thumbnails, then just a blur. The blur no longer responds to clicks or loads.

To Reproduce
Steps to reproduce the behavior:

  1. Go to 'https://demo.photofield.dev/'
  2. Enter a large album and scroll for a while
  3. Pictures will eventually stop loading

Expected behavior
Memory to be cleared of earlier thumbnails and the new thumbnails to be loaded while scrolling, rather than stopping at some point.

Screenshots
of course, once I try and get a screenshot, I couldn't replicate. Maybe click-zooming clears memory and gets things working again?

Desktop (please complete the following information):

  • OS: Windows 10 v1709
  • Browse: Ungoogled Chromium
  • Version: 90

Smartphone (please complete the following information):
N/A

RAW file support

Is your feature request related to a problem? Please describe.
RAW files are completely unsupported right now so it's not possible to see them at all.

Describe the solution you'd like
Transparent support for common formats so that they just look like any other photo. Depending on how long it takes to convert, this might need thumbnail generation (issue TBD). I believe often they are also paired with JPEGs, which would be nice to take into account.

Additional context
Can be a deal breaker for many if not supported, see H_Q_'s comment

Better video support

Is your feature request related to a problem? Please describe.
It's not possible to control the video right now.

Describe the solution you'd like
Make it possible to control the video, but also keeping the functionality of being able to freely move around the space. Perhaps only restrict movement for the control bar part of the video player?

Additional context
See reddit thread

Support ImageMagick as an image source

Is your feature request related to a problem? Please describe.
Current image sources (native Go, JPEG thumbnails, FFmpeg, ...) do not support all the various popular formats out there, the biggest one being HEIC, which is widely used for iPhone photos (see #52).

Describe the solution you'd like
Add optional support for decoding images with imagemagick. There is a mostly complete implementation already in main...imagemagick, but I wasn't super happy with usability yet, so it might need more work?

Describe alternatives you've considered
https://www.libvips.org/ might be another good one that supports many formats, including HEIC.

Additional context
N/A

add admin username/password

You seem to have mentioned in readme that you plan to support it later.

No user accounts. Not the focus right now. You can define separate collections for separate users based on the directory structure, but there is no authentication or authorization support.

Would it be possible to add basic support for admin only account. Probably in configuration.yml

password: "some_hashed_password"
collections:
   .....
   .....

More layout ideas

Map added in #77!

An idea would be to add a layout that is essentially a completely zoomable map with every photo drawn on it directly.

Mosaic

Pick a photo and have it be constructed from all your other photos. Have each pixel be its own photo? Or maybe even do it recursively forever? But there lies madness.

Watch folders for new/changed/removed photos

Is your feature request related to a problem? Please describe.
Actually using the app in day-to-day life is not a great experience. You have to rescan all the time, which can take forever. If you haven't rescanned in a while, the newly added photos can also take minutes to hours to finish indexing. Additionally, there are a bunch of caches that aren't invalidated when scanning, even if you refresh the page, which seems broken and is very annoying.

Describe the solution you'd like

  • Watch for files in realtime, using https://github.com/fsnotify/fsnotify
  • Handle adding new files
  • Handle removing files
  • Handle file renames
  • Handle folder renames
  • Never (?) delete metadata on files so that e.g. moves do not require reindexing. This is also important in case you add e.g. tags to files
  • Add marking scenes as "stale" if the dependencies (i.e. files) change and refresh them, at least upon page refresh

Describe alternatives you've considered
Improving the caching part without adding watch.

Additional context
Add any other context or screenshots about the feature request here.

Idea for Face Tagging

Is your feature request related to a problem? Please describe.
Sorting by Faces is an awesome (and complicated) feature, usually done via machine learning algorithms in PhotoView, Photoprism, etc. However, there is an easier way...

Describe the solution you'd like
Google Picasa uses machine learning to recognise faces in photos, but the actual face tagging is done via either EXIF metadata with the person's name (if I recall correctly), or via multiple .picasa.ini files that list hashes matching to position on the photo (?) and a name, for example, a folder full of pictures of my face:

[Contacts2]
caefa70bc4c5dad2=Darren;;
[Profile Picture 3.jpg]
faces=rect64(1ee22a22b94da514),caefa70bc4c5dad2
backuphash=16963
[Profile Picture 3 medium.jpg]
faces=rect64(1e830830b94dc1dc),caefa70bc4c5dad2
backuphash=16963
[Profile Picture 1.JPG]
faces=rect64(34000aabbb99e3bb),caefa70bc4c5dad2
backuphash=1120
[Profile Picture 2.JPG]
faces=rect64(6000000de66ffff),caefa70bc4c5dad2
backuphash=37025
[admin.jpg]
faces=rect64(1e1407aeb999c1eb),caefa70bc4c5dad2
backuphash=64588

A "quick" way to add face recognition to Photofield, without implementing actual recognition models and related complications, would be to make it check for .picasa.ini and name metadata already present and map a People view from those. I don't know how useful this would be in the long term, but it was a silly idea I had of outsourcing the facial recognition to already-existing tools and just using what they generate. e.g. I believe Digikam uses name EXIF metadata by default (what Picasa calls Tools >> Options >> Name tags >> Store name tags in photo)

Home page sometimes loads slowly

Describe the bug
The view with collections is loading slowly sometimes, not sure why. It might be doing a SELECT counting all the files for each collection, which is not something that's even shown, which would be bad...

To Reproduce
Usually shows up on a slower HDD NAS after some idle time.

Expected behavior
Home page loads instantly.

Multi-Arch Docker images

Is your feature request related to a problem? Please describe.
From some conversations, it seems like if you run the Docker image on something like a M1/M2/M3 Mac, it runs it some sort of an x86 emulation mode, making it super slow.

Describe the solution you'd like
If the Docker image contained an arm build, it would probably work better. But I'm not sure how to do that and I don't have a Mac to test. ๐Ÿคทโ€โ™€๏ธ

Panic: no migration

Describe the bug
When trying to start the application first time in windows 10 it ended up with panic: no migration. Do I need to configure something? After start I have a new 12k sized file called photofield.cache.db in the directory.

To Reproduce
Steps to reproduce the behavior:

  1. open photofield.exe

Expected behavior
Successful start.

Screenshots

PS C:\Users\Downloads\photofield_0.3.0_Windows_x86_64> .\photofield.exe
2022/06/14 17:19:22 photofield 0.3.0
2022/06/14 17:19:22 unable to open configuration.yaml, using defaults (open configuration.yaml: The system cannot find the file specified.)
2022/06/14 17:19:22 unable to use exiftool, defaulting to goexif - no video metadata support (Could not create StayOpen: Failed starting exiftool in stay_open mode: exec: "exiftool": executable file not found in %PATH%)
panic: no migration

goroutine 1 [running]:
photofield/internal/image.(*Database).migrate(0xc00015dd40, {0xc0000d77b8})
        /home/runner/work/photofield/photofield/internal/image/database.go:138 +0x295
photofield/internal/image.NewDatabase({0xc000132648, 0x13}, {0xd19e99})
        /home/runner/work/photofield/photofield/internal/image/database.go:90 +0x6b
photofield/internal/image.NewSource({{0xc000132648, 0x13}, 0x4, 0x0, 0x8, 0x4, {0xc00015d7c0, 0x4, 0x4}, {0xc0001473a0, ...}, ...}, ...)
        /home/runner/work/photofield/photofield/internal/image/source.go:85 +0xe5
main.main()
        /home/runner/work/photofield/photofield/main.go:937 +0x5df

Desktop (please complete the following information):

  • OS: Windows 10
  • Browser: none
  • Version: none

Smartphone (please complete the following information):

  • none

Additional context

Remove strip viewer, keep strip view

This is about the strip viewer added in #47. The general idea is good - common left-right swiping when zoomed in, shown on a black background. However, the implementation leaves a bit to be desired.

Problems

  • It has visual glitches as the two separate viewers (scroll view & strip view) need to be reconciled switching between them and this takes a lot of effort to solve
  • It's not efficient as the same photos are loaded in a separate view, especially right now as both views load at the same time and only one is shown
  • The strip viewer might be running into numerical issues as 300k photos are put next to each other... or at least it leads to very long virtual images. Not necessarily a problem, but another potential issue down the road

Solution

Potentially go back to the previous solution, where the existing views (scroll viewer, map viewer) are zoomed into directly, reusing them to switch between photos. Here are some problems from last time and how they can be addressed this time.

  • Nearby photos are visible and not hidden under a black background. This can be solved by hooking into OpenLayers rendering directly and masking out everything that is not the desired region. This is the approach tested in https://github.com/SmilyOrg/photofield/tree/remove-strip-viewer
  • Switching between photos is problematic as they are not arranged linearly on a horizontal strip. This can be resolved by virtualizing the movement with the viewer always showing the focused region, but the viewer itself being moved left and right or up/down.

Make the docker log more verbose

Currently, the log displayed by photofield in the console of the docker environment is fairly scant, almost useless when something does not work.
Add more information to the log, such as:

  • path to the configuration.yaml file if any
  • path to the indexed image folder(s)
  • number of photos indexed
  • type of photo extensions considered, etc.

Found some images that Photofield doesn't like

Describe the bug
The attached (ZIP) images display as big red squares in Photofield v0.7.0

To Reproduce
Steps to reproduce the behavior:
Load up photofield and get it to process these photos (JPG, PNG, wallpapers from Telegram, sourced from here)
Telegram wallpapers.zip. At least on Windows, running v0.7.0 plain (no exiftool or anything) they display as big red squares.

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.
brave_bHDPbCcung

Desktop (please complete the following information):

  • OS: Win10 v1709
  • Browser: Ungoogled Chromium, Brave
  • Version: v90, v1.45.116

Left/Right keys don't proceed past photos with different aspect ratios

Describe the bug
Testing on localhost, I found that I had some trouble cycling through photos with the arrow keys when the aspect ratio changed. Here's the photos I was testing with; I would start at the beginning, press Right Right Right [...] and then I couldn't cycle from the 2nd to last (black and white cartoon) on to the last photo (stars over beach). Cycling left, I had no problems, despite identical changes in aspect ratio.

Expected behavior
Should be able to cycle with arrow keys without it randomly stopping? Unknown cause, see above for the sample photos.

Desktop (please complete the following information):

  • OS: Win10 v1709
  • Browser: Ungoogled Chromium
  • Version: 90

Smartphone (please complete the following information):
N/A

Delete / Remove photos

Thanks for photofield - it looks really great.

Nevertheless, I got a feature request: I would like to select multiple photos and remove them from disk.

Cursor does not show up as a hand on hover

Describe the bug
If you hover over photos, the cursor remains an arrow, when it should be a hand instead, indicating that you can click on them to zoom in.

Additional context
This is because the photos don't actually exist as individual objects, making this a non-trivial functionality to add,

See also user reports #1, #2, #3

A few possible solutions:

  1. Each photo is created as an individual object (as a vector map?). This has performance implications when showing thousands or tens of thousands of photos.
  2. The server is queried for each mouse position to check if it's over a photo or not. This needs minimal additional implementation, but seems unpractical as it would spam the API too much and look laggy / delayed.
  3. The frontend is able to query the loaded images directly (or some mask equivalent loaded in the background) and can tell which pixel is a photo and which one isn't.

LibrePhotos integration

Very cool! Do you plan to write something about the implementation or release a package, that other software devs can import? Would love to have something similar in LibrePhotos. - DerNeuere

@derneuere hi! I opened an issue here so I don't forget it :)

Layout = wall not displaying

Describe the bug
Layout "wall" only produces white screen. Header with album name is still rendered.

To Reproduce
Steps to reproduce the behavior:

  1. Go to 'any album'
  2. Click on 'gear icon top right'
  3. Select layout 'wall'
  4. See error

Expected behavior
Images are layouted as wall of pictures.

Desktop (please complete the following information):

  • OS: Win 10 21H2
  • Browser: Chromium (115.0.5790.131), but also happens on Edge and FF

Additional context
Chromium console shows

index.fb736cc0.js:4 [Violation] Added non-passive event listener to a scroll-blocking 'wheel' event. Consider marking event handler as 'passive' to make the page more responsive. See https://www.ch40mestatus.qjz9zk/feature/5745543795965952
index.fb736cc0.js:47 [Violation] Added non-passive event listener to a scroll-blocking 'touchstart' event. Consider marking event handler as 'passive' to make the page more responsive. See https://www.ch40mestatus.qjz9zk/feature/5745543795965952
index.fb736cc0.js:29 Map not initialized yet, setting pending view {x: 0, y: 0, w: 1920, h: 0}
index.fb736cc0.js:29 Map not initialized yet, setting pending view {x: 0, y: 0, w: 1920, h: 1083}

Browser network analysis shows that lots of files like /api/scenes/8NDGBbzTGL/tiles?tile_size=512&zoom=9&background_color=%23000000&x=257&y=0 are loaded, all http 200, so OK.

PF shows (taken from syslog because running as a service)

Aug  5 18:43:23 server photofield[166612]: 2023/08/05 18:43:23 scene loading 2019
Aug  5 18:43:23 server photofield[166612]: 2023/08/05 18:43:23 list infos sqlite        0 ms
Aug  5 18:43:23 server photofield[166612]: 2023/08/05 18:43:23 list infos               0 ms
Aug  5 18:43:23 server photofield[166612]: 2023/08/05 18:43:23 layout wall width 1920 cols 12
Aug  5 18:43:23 server photofield[166612]: 2023/08/05 18:43:23 layout wall image 218.181818 174.545455
Aug  5 18:43:23 server photofield[166612]: 2023/08/05 18:43:23 layout                   0 ms
Aug  5 18:43:23 server photofield[166612]: 2023/08/05 18:43:23 scene load 2019          1 ms
Aug  5 18:43:23 server photofield[166612]: 2023/08/05 18:43:23 photos 146, scene 1920 x 198

No way to close the Hamburger Menu

Describe how to reproduce the bug
Click on the top-left Hamburger Menu on PC, and there is no way to close the menu. Page must be reloaded. (Similar with settings icon, but at least that can be closed by clicking on it again).

Expected behavior
Clicking off the Hamburger Menu should close it. Likewise, clicking off the Settings icon should (maybe?) close the settings menu. What about combining the settings menu and the hamburger menu?

Desktop (please complete the following information):

  • OS: Windows
  • Browser: Ungoogled Chromium 90
  • Version: Photofield 0.5.0

Smartphone (please complete the following information):
N/A

Nested folders are ignored; shouldn't they be "Albums"?

I also noticed it doesn't seem to pay attention to nested folders? Didn't notice that before... e.g. I have the above wallpapers organised as:
/Wallpapers/iPhone Wallpapers/Telegram wallpapers/wall.jpg (where / is Photofield's root).

I kinda expected, when (in Album view) I went into iPhone Wallpapers/ to see the photos in that folder, headed up by another Album called Telegram wallpapers. Instead I saw a flat display of every photo in every folder in iPhone Wallpapers/, mixed with photos that are just there in the folder (which I logically expected only when flattening things to view as a Timeline or Wall).

Expected display:

Album layout:

  • Grouping wallpapers by subfolder, i.e. "Album"

Timeline layout:

  • Grouping using month/day/time/year, more or less as current "Default"/"Album" layout
  • But with <h2> = Year/Month, <h3> = Day/Date, order invisibly by Time?

Wall layout:

  • exactly as-is, a flat display of all images, ordered (invisibly) I assume by date and subfolder?

Default layout:

  • no idea, deprecate as uninformative and replaced by other layouts? Photofield doesn't (yet) save different layouts per folder as far as I can tell, nor are there enough different layouts to warrant this (maybe if Geo and People tags are implemented, it would be useful)

How it's actually displayed

Album layout:

  • <h2> is Day, Month [numerical date], <h3> is Time
  • ... Doesn't seem to sort by any definition of "Album"...? Confuzzled.

Timeline layout:

  • Full date, time, year grouping... not very useful, more or less a zoomed-out duplicate version of current "Album"

Wall layout:

  • It's perfect!

Default:

  • ??? Seems to duplicate "Album", and doesn't mean "Default for this folder" (which isn't saved or currently worth saving)

EDIT: Moving this comment to new issue, as it's kinda separate

Originally posted by @Darthagnon in #44 (comment)

AVIF support

Is your feature request related to a problem? Please describe.
Currently AVIF images are not supported.

Describe the solution you'd like
Transparent support for AVIF.

Describe alternatives you've considered
Not supporting it? ๐Ÿ˜…

Additional context
Sample images were kindly provided by houseoflightshadow

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.