Giter VIP home page Giter VIP logo

Comments (10)

alvistar avatar alvistar commented on August 18, 2024

Hi,
I tried to reproduce it without success.

Anyway, I noticed you used trailing backslashes -> /DATA/Gallery/Events/

Can you try to remove them?

I see Python is returning "" as basepath if you use trailing slash. -> "Album does not exist"

Eventually remove ".album" files in folders if they are created. Let me know if this works and I will set a note in readme to avoid trailing slash.

I can eventually try to workaround it, but I do suspect it will create issue with Windows and backslash.

Thanks,
a.

from immich-albums.

floco avatar floco commented on August 18, 2024

Hi,
Many thanks for taking a look at it. Note I run immich v1.88.1
Same results unfortunately without the trailing slash. I tried also when being place in the folder that is targeted (logs from this attempt below) but same results. Tried also with just one plain folder with photos but same as well.

(immich-albums-py3.11) root@casaos:/DATA/Gallery/Events# im --api-key v06xNULsbzUnhvTbwRftzuSxESZzLiLSSllehdtfhz --api-host photos.casa.something.com --dry-run --replace-path /DATA/Gallery/Events --original-path /DATA/Gallery/Events --recursive .
Processing folder: .

Album . does not exist
DRY RUN: Creating album .
DRY RUN: Assets ids: []
Processing folder: ./2007

Album 2007 does not exist
DRY RUN: Creating album 2007
DRY RUN: Assets ids: []
Processing folder: ./2007/2007-09-Disneyland

Album 2007-09-Disneyland does not exist
searching for: ./2007/2007-09-Disneyland/IMG_9517.jpg
Traceback (most recent call last):
  File "pydantic/main.py", line 522, in pydantic.main.BaseModel.parse_obj
ValueError: dictionary update sequence element #0 has length 1; 2 is required

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/root/.cache/pypoetry/virtualenvs/immich-albums-Lu1yoump-py3.11/bin/im", line 6, in <module>
    sys.exit(cli())
             ^^^^^
  File "/root/.cache/pypoetry/virtualenvs/immich-albums-Lu1yoump-py3.11/lib/python3.11/site-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.cache/pypoetry/virtualenvs/immich-albums-Lu1yoump-py3.11/lib/python3.11/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "/root/.cache/pypoetry/virtualenvs/immich-albums-Lu1yoump-py3.11/lib/python3.11/site-packages/click/core.py", line 1434, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.cache/pypoetry/virtualenvs/immich-albums-Lu1yoump-py3.11/lib/python3.11/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/immich-albums/src/immich_albums/im.py", line 192, in cli
    immich_albums.create_albums_from_folder(
  File "/root/immich-albums/src/immich_albums/im.py", line 154, in create_albums_from_folder
    self.create_albums_from_folder(path, original_path, replace_path, True, dry_run,
  File "/root/immich-albums/src/immich_albums/im.py", line 154, in create_albums_from_folder
    self.create_albums_from_folder(path, original_path, replace_path, True, dry_run,
  File "/root/immich-albums/src/immich_albums/im.py", line 150, in create_albums_from_folder
    self.create_album_from_folder(path, original_path, replace_path, dry_run, skip_existing=skip_existing)
  File "/root/immich-albums/src/immich_albums/im.py", line 116, in create_album_from_folder
    assets_ids = self.get_assets_in_folder(path, original_path, replace_path)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/immich-albums/src/immich_albums/im.py", line 93, in get_assets_in_folder
    asset_id = self.get_asset_by_original_path(replaced_path)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/immich-albums/src/immich_albums/im.py", line 45, in get_asset_by_original_path
    assets = api_instance.search_assets(original_path=original_path)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "pydantic/decorator.py", line 40, in pydantic.decorator.validate_arguments.validate.wrapper_function
  File "pydantic/decorator.py", line 134, in pydantic.decorator.ValidatedFunction.call
  File "pydantic/decorator.py", line 206, in pydantic.decorator.ValidatedFunction.execute
  File "/root/.cache/pypoetry/virtualenvs/immich-albums-Lu1yoump-py3.11/lib/python3.11/site-packages/openapi_client/api/asset_api.py", line 3658, in search_assets
    return self.search_assets_with_http_info(id, library_id, type, order, device_asset_id, device_id, checksum, is_archived, is_encoded, is_external, is_favorite, is_motion, is_offline, is_read_only, is_visible, with_deleted, with_stacked, with_exif, with_people, created_before, created_after, updated_before, updated_after, trashed_before, trashed_after, taken_before, taken_after, original_file_name, original_path, resize_path, webp_path, encoded_video_path, city, state, country, make, model, lens_model, page, size, **kwargs)  # noqa: E501
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "pydantic/decorator.py", line 40, in pydantic.decorator.validate_arguments.validate.wrapper_function
  File "pydantic/decorator.py", line 134, in pydantic.decorator.ValidatedFunction.call
  File "pydantic/decorator.py", line 206, in pydantic.decorator.ValidatedFunction.execute
  File "/root/.cache/pypoetry/virtualenvs/immich-albums-Lu1yoump-py3.11/lib/python3.11/site-packages/openapi_client/api/asset_api.py", line 4010, in search_assets_with_http_info
    return self.api_client.call_api(
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.cache/pypoetry/virtualenvs/immich-albums-Lu1yoump-py3.11/lib/python3.11/site-packages/openapi_client/api_client.py", line 409, in call_api
    return self.__call_api(resource_path, method,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.cache/pypoetry/virtualenvs/immich-albums-Lu1yoump-py3.11/lib/python3.11/site-packages/openapi_client/api_client.py", line 247, in __call_api
    return_data = self.deserialize(response_data, response_type)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.cache/pypoetry/virtualenvs/immich-albums-Lu1yoump-py3.11/lib/python3.11/site-packages/openapi_client/api_client.py", line 319, in deserialize
    return self.__deserialize(data, response_type)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.cache/pypoetry/virtualenvs/immich-albums-Lu1yoump-py3.11/lib/python3.11/site-packages/openapi_client/api_client.py", line 335, in __deserialize
    return [self.__deserialize(sub_data, sub_kls)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.cache/pypoetry/virtualenvs/immich-albums-Lu1yoump-py3.11/lib/python3.11/site-packages/openapi_client/api_client.py", line 335, in <listcomp>
    return [self.__deserialize(sub_data, sub_kls)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.cache/pypoetry/virtualenvs/immich-albums-Lu1yoump-py3.11/lib/python3.11/site-packages/openapi_client/api_client.py", line 358, in __deserialize
    return self.__deserialize_model(data, klass)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/.cache/pypoetry/virtualenvs/immich-albums-Lu1yoump-py3.11/lib/python3.11/site-packages/openapi_client/api_client.py", line 758, in __deserialize_model
    return klass.from_dict(data)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/root/.cache/pypoetry/virtualenvs/immich-albums-Lu1yoump-py3.11/lib/python3.11/site-packages/openapi_client/models/asset_response_dto.py", line 151, in from_dict
    return AssetResponseDto.parse_obj(obj)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "pydantic/main.py", line 525, in pydantic.main.BaseModel.parse_obj
pydantic.error_wrappers.ValidationError: 1 validation error for AssetResponseDto
__root__
  AssetResponseDto expected dict not str (type=type_error)
(immich-albums-py3.11) root@casaos:/DATA/Gallery/Events# ls -al
total 116
drwxrwsr-x 29 julien devmon 4096 Jan 30  2023 .
drwxr-sr-x  5 julien devmon 4096 Nov 23 21:20 ..
drwxrwsr-x  5 julien devmon 4096 Jan 14  2023 0000
drwxrwsr-x 12 julien devmon 4096 Jan 30  2023 1975-97
drwxrwsr-x 11 julien devmon 4096 Jan 14  2023 1998-99
drwxrwsr-x  4 julien devmon 4096 Jan 14  2023 2000
drwxrwsr-x 16 julien devmon 4096 Jan 14  2023 2001
drwxrwsr-x 16 julien devmon 4096 Jan 14  2023 2002
drwxrwsr-x 26 julien devmon 4096 Jan 14  2023 2003
drwxrwsr-x 18 julien devmon 4096 Jan 14  2023 2004
drwxrwsr-x 25 julien devmon 4096 Jan 14  2023 2005
drwxrwsr-x 25 julien devmon 4096 Jan 14  2023 2006
drwxrwsr-x 18 julien devmon 4096 Jan 14  2023 2007
drwxrwsr-x 51 julien devmon 4096 Jan 14  2023 2008
drwxrwsr-x 28 julien devmon 4096 Jan 14  2023 2009
drwxrwsr-x 22 julien devmon 4096 Jan 14  2023 2010
drwxrwsr-x 30 julien devmon 4096 Jan 14  2023 2011
drwxrwsr-x 38 julien devmon 4096 Jan 14  2023 2012
drwxrwsr-x 33 julien devmon 4096 Jan 14  2023 2013
drwxrwsr-x 26 julien devmon 4096 Jan 14  2023 2014
drwxrwsr-x 23 julien devmon 4096 Jan 14  2023 2015
drwxrwsr-x 19 julien devmon 4096 Oct 16 16:22 2016
drwxrwsr-x 11 julien devmon 4096 Jan 14  2023 2017
drwxrwsr-x 16 julien devmon 4096 Jan 14  2023 2018
drwxrwsr-x 16 julien devmon 4096 Jan 14  2023 2019
drwxrwsr-x 13 julien devmon 4096 Jan 14  2023 2020
drwxrwsr-x 15 julien devmon 4096 Feb  1  2023 2021
drwxrwsr-x 13 julien devmon 4096 Oct 16 16:18 2022
drwxrwsr-x 20 julien devmon 4096 Oct 16 16:07 2023

from immich-albums.

floco avatar floco commented on August 18, 2024

Progressing with the analysis. I did a curl to test the api.
curl -L -X GET https://photos.casa.something.com/api/server-info -H 'Accept: application/json' -H 'x-api-key: v06xNULsbzUnhvTbwRftzuSxESZzLiLSSllehdtfhz'
{"diskAvailable":"455.0 GiB","diskSize":"915.8 GiB","diskUse":"414.2 GiB","diskAvailableRaw":488597385216,"diskSizeRaw":983349325824,"diskUseRaw":444725010432,"diskUsagePercentage":45.23}
That works.
Then I tried doing the same im command but with --api-host photos.casa.something.com/api
no more errors and it goes further.
Now it creates albums but empty ones.
Will look further a bit later.
Thanks again.

from immich-albums.

floco avatar floco commented on August 18, 2024

Here is the log, leading to empty albums. I probably do something wrong but can't yet figure out what.
Is the "searching" and then "not found" expected ?

(immich-albums-py3.11) root@casaos:/DATA/Gallery/Events# im --api-key v06xNULsbzUnhvTbwRftzuSxESZzLiLSSllehdtfhz --api-host https://photos.casa.something.com/api --replace-path /DATA/Gallery/Events --original-path /DATA/Gallery/Events --recursive .
Processing folder: .

Album . does not exist
Creating album .
Creating album $.

Album id 60741282-4351-4785-b875-7acf1d35d9f8
The response of create album:

album_name='.' album_thumbnail_asset_id=None asset_count=0 assets=[] created_at=datetime.datetime(2023, 11, 27, 8, 19, 0, 55000, tzinfo=datetime.timezone.utc) description='' end_date=None has_shared_link=False id='60741282-4351-4785-b875-7acf1d35d9f8' is_activity_enabled=True last_modified_asset_timestamp=None owner=UserResponseDto(avatar_color=<UserAvatarColor.PRIMARY: 'primary'>, created_at=datetime.datetime(2023, 11, 12, 16, 48, 26, 741000, tzinfo=datetime.timezone.utc), deleted_at=None, email='[email protected]', external_path='/DATA/Gallery/Events', id='dbac9fe1-5bfe-40ed-b7a7-575907bca959', is_admin=False, memories_enabled=True, name='All Photos', oauth_id='', profile_image_path='', should_change_password=False, storage_label=None, updated_at=datetime.datetime(2023, 11, 12, 16, 50, 26, 493000, tzinfo=datetime.timezone.utc)) owner_id='dbac9fe1-5bfe-40ed-b7a7-575907bca959' shared=False shared_users=[] start_date=None updated_at=datetime.datetime(2023, 11, 27, 8, 19, 0, 55000, tzinfo=datetime.timezone.utc)
Creating .album
Processing folder: ./2007

Album 2007 does not exist
Creating album 2007
Creating album $2007

Album id 8bb0e716-facd-4670-8996-f793e84f3d30
The response of create album:

album_name='2007' album_thumbnail_asset_id=None asset_count=0 assets=[] created_at=datetime.datetime(2023, 11, 27, 8, 19, 0, 130000, tzinfo=datetime.timezone.utc) description='' end_date=None has_shared_link=False id='8bb0e716-facd-4670-8996-f793e84f3d30' is_activity_enabled=True last_modified_asset_timestamp=None owner=UserResponseDto(avatar_color=<UserAvatarColor.PRIMARY: 'primary'>, created_at=datetime.datetime(2023, 11, 12, 16, 48, 26, 741000, tzinfo=datetime.timezone.utc), deleted_at=None, email='[email protected]', external_path='/DATA/Gallery/Events', id='dbac9fe1-5bfe-40ed-b7a7-575907bca959', is_admin=False, memories_enabled=True, name='All Photos', oauth_id='', profile_image_path='', should_change_password=False, storage_label=None, updated_at=datetime.datetime(2023, 11, 12, 16, 50, 26, 493000, tzinfo=datetime.timezone.utc)) owner_id='dbac9fe1-5bfe-40ed-b7a7-575907bca959' shared=False shared_users=[] start_date=None updated_at=datetime.datetime(2023, 11, 27, 8, 19, 0, 130000, tzinfo=datetime.timezone.utc)
Creating .album
Processing folder: ./2007/2007-09-Disneyland

Album 2007-09-Disneyland does not exist
searching for: ./2007/2007-09-Disneyland/IMG_9517.jpg
not found: ./2007/2007-09-Disneyland/IMG_9517.jpg
searching for: ./2007/2007-09-Disneyland/IMG_9443.jpg
not found: ./2007/2007-09-Disneyland/IMG_9443.jpg
[Redacted ...]
searching for: ./2007/2007-09-Disneyland/IMG_9470_edited.jpg
not found: ./2007/2007-09-Disneyland/IMG_9470_edited.jpg
Creating album 2007-09-Disneyland
Creating album $2007-09-Disneyland

Album id 920a7dbc-dd19-49c0-8cb0-e79393a388ae
The response of create album:

album_name='2007-09-Disneyland' album_thumbnail_asset_id=None asset_count=0 assets=[] created_at=datetime.datetime(2023, 11, 27, 8, 20, 10, 824000, tzinfo=datetime.timezone.utc) description='' end_date=None has_shared_link=False id='920a7dbc-dd19-49c0-8cb0-e79393a388ae' is_activity_enabled=True last_modified_asset_timestamp=None owner=UserResponseDto(avatar_color=<UserAvatarColor.PRIMARY: 'primary'>, created_at=datetime.datetime(2023, 11, 12, 16, 48, 26, 741000, tzinfo=datetime.timezone.utc), deleted_at=None, email='[email protected]', external_path='/DATA/Gallery/Events', id='dbac9fe1-5bfe-40ed-b7a7-575907bca959', is_admin=False, memories_enabled=True, name='All Photos', oauth_id='', profile_image_path='', should_change_password=False, storage_label=None, updated_at=datetime.datetime(2023, 11, 12, 16, 50, 26, 493000, tzinfo=datetime.timezone.utc)) owner_id='dbac9fe1-5bfe-40ed-b7a7-575907bca959' shared=False shared_users=[] start_date=None updated_at=datetime.datetime(2023, 11, 27, 8, 20, 10, 824000, tzinfo=datetime.timezone.utc)
Creating .album
Processing folder: ./2007/2007-08-Test_Pocitnice

Album 2007-08-Test_Pocitnice does not exist
searching for: ./2007/2007-08-Test_Pocitnice/IMG_8957.jpg
not found: ./2007/2007-08-Test_Pocitnice/IMG_8957.jpg
searching for: ./2007/2007-08-Test_Pocitnice/IMG_8731.jpg
[Redacted ...]
^C
Aborted!

from immich-albums.

alvistar avatar alvistar commented on August 18, 2024

Hi, I just pushed a minor commit to script.

Now I parse the relative path to full path ie . will become /Data/gallery ...

The main issue is that the path you specify on the script must match the one as mounted on docker host.
Pictures on immich will have an attribute which is original_path as set as seen in external library path in docker.
Example:
if you mounted /DATA/gallery under /mnt/data/gallery -> original path = /mnt/data/gallery/pic1.jpg

So you need original path /DATA/gallery and replace path /mnt/DATA/gallery. You will see then the script will search picture for /mnt/DATA/gallery.

Hope this is clear.

from immich-albums.

floco avatar floco commented on August 18, 2024

Thanks :-)
That's the thing though, the path on my host "/DATA/Gallery/Events" is mounted in the immich-server container as "/DATA/Gallery/Events" so I'm not sure why it can't find the pictures ... I will try with the new version and report back.

docker inspect immich-server
[Redacted ...]
        "Mounts": [
            {
                "Type": "bind",
                "Source": "/DATA/Gallery/Events",
                "Destination": "/DATA/Gallery/Events",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            },
           [Redacted ...]
        ],
[Redacted ...]

from immich-albums.

floco avatar floco commented on August 18, 2024

With the new version displaying the entire path, I see what may be happening ... perhaps due to symbolic link on my host ?

(immich-albums-py3.11) root@casaos:/DATA# ls -l
total 16
drwxrwsrwx 13 root root 4096 Nov 22 11:25 AppData
drwxrwsrwx  2 root root 4096 Dec 18  2022 Documents
drwxrwsrwx  2 root root 4096 Dec 18  2022 Downloads
lrwxrwxrwx  1 root root   27 Sep  9 12:35 Gallery -> /srv/sdb1/sharedrive/Photos
drwxr-sr-x  5 root root 4096 Dec 18  2022 Media

(immich-albums-py3.11) root@casaos:/DATA/Gallery/Events# im --api-key v06xNULsbzUnhvTbwRftzuSxESZzLiLSSllehdtfhz --api-host photos.casa.something.com/api --dry-run --replace-path /DATA/Gallery/Events --original-path /DATA/Gallery/Events --recursive .
Processing folder: /srv/sdb1/sharedrive/Photos/Events

Album Events exists with id 60741282-4351-4785-b875-7acf1d35d9f8
searching for: /srv/sdb1/sharedrive/Photos/Events/.album
not found: /srv/sdb1/sharedrive/Photos/Events/.album
DRY RUN: Creating album Events
DRY RUN: Assets ids: []
Processing folder: /srv/sdb1/sharedrive/Photos/Events/2007

Album 2007 exists with id 8bb0e716-facd-4670-8996-f793e84f3d30
searching for: /srv/sdb1/sharedrive/Photos/Events/2007/.album
not found: /srv/sdb1/sharedrive/Photos/Events/2007/.album
DRY RUN: Creating album 2007
DRY RUN: Assets ids: []
Processing folder: /srv/sdb1/sharedrive/Photos/Events/2007/2007-09-Disneyland

Album 2007-09-Disneyland exists with id 920a7dbc-dd19-49c0-8cb0-e79393a388ae
searching for: /srv/sdb1/sharedrive/Photos/Events/2007/2007-09-Disneyland/IMG_9517.jpg
not found: /srv/sdb1/sharedrive/Photos/Events/2007/2007-09-Disneyland/IMG_9517.jpg
searching for: /srv/sdb1/sharedrive/Photos/Events/2007/2007-09-Disneyland/IMG_9443.jpg
not found: /srv/sdb1/sharedrive/Photos/Events/2007/2007-09-Disneyland/IMG_9443.jpg
searching for: /srv/sdb1/sharedrive/Photos/Events/2007/2007-09-Disneyland/IMG_9408.jpg

from immich-albums.

alvistar avatar alvistar commented on August 18, 2024

Ok, so it would be easy to fix

use as originalpath /srv/sdb1/sharedrive/Photos/Events
replacepath /Data/Gallery/Events

Something like that. You will see it will start searching for the photos in /Data/Gallery/Events

from immich-albums.

floco avatar floco commented on August 18, 2024

That's it ! did exactly that just before seeing your post to see what it would do.
--original-path /srv/sdb1/sharedrive/Photos/Events
And that worked, thanks again for your help :-)

from immich-albums.

floco avatar floco commented on August 18, 2024

resolved

from immich-albums.

Related Issues (12)

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.