Z tego artykułu dowiesz się jak w prosty sposób bez kodowania możesz dodać na swoją stronę w miarę przyzwoity album z zdjęciami. Efekt końcowy możesz zobaczyć tutaj. Na końcu artykułu znajdziesz linka do githuba z prostym plikiem html, który możesz wykorzystać na swojej stronie.

Jakiś czas temu pomyślałem, że fajnie byłoby mieć na stronie galerię w postaci albumów z zdjęciami w dobrej jakości, do których miałbym łatwy dostęp z dowolnego urządzenia w postaci prostego linka, który mógłbym wpisać w pasku przeglądarki lub podesłać go komuś innemu. Jednym zdaniem mówiąc wszystko w jednym miejscu.

Zacząłem zastanawiać się jakich technologii mógłbym użyć aby zrealizować to co przyszło mi do głowy. Postanowiłem, że rozwiązanie ma być relatywnie jak najprostsze bez kodowania po stronie serwera. Chciałem również uniknąć serwowania zdjęć z serwera, na którym działa moja strona aby odciążyć serwer i zaoszczędzić miejsce a tym samym uniknąć zbędnych kosztów. Poza tym nie od dziś wiemy, że statyczny kontent lepiej serwować z oddzielnej domeny (cookie-less-domain).

Postanowiłem, że skorzystam z gotowych rozwiązań w chmurze tj. Dropbox lub Google Photos. Natomiast pojawił się problem z tymi usługami gdyż nie udostępniają one możliwości wyeksportowania kodu i zamieszczenia treści na stronie w postaci fajnie wyglądającej galerii zdjęć, przynajmniej nie w takiej postaci w jakiej bym oczekiwał. Kolejna rzecz to API tych usług wymaga uwierzytelniania, mimo że chciałem pobierać informacje tylko o publicznie dostępnych albumach/folderach. Wiązałoby się to z zaimplementowaniem autoryzacji po stronie serwera czego chciałem uniknąć. Zależało mi na prostocie rozwiązania.

Ostatecznie padło na google photos gdyż znalazłem ciekawe narzędzie, które po podaniu linka do publicznego albumu z google photos "eksportuje" nam ten album i generuje gotowy kod HTML do umieszczenia na stronie. Przykładowy kod wygenerowany przez wspomniane narzędzie:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/embed-ui.min.js" async></script>
<div class="pa-gallery-player-widget" style="width:100%; height:480px; display:none;"
  data-link="https://photos.app.goo.gl/CSV7NDstShTUwUZq5"
  data-title="Mr. Monstro"
  data-description="4 new items · Album by Pavel M.">
  <object data="https://lh3.googleusercontent.com/XlH6wo2PzrAEqmplYrZwV0fI-2TafTT6BRwZhKDfZSHd_zT7HIdPyPWd3Xuqhn1QQADuTJ32QFmcgYiTOEU0sC4Bvf-VyTIiq-DxxEaxIeWDYyUK_VjaW8-zrMGBvekDZT77lpduYQ=w1920-h1080"></object>
  <object data="https://lh3.googleusercontent.com/VvK__Vx8kpPTP57WZPLblacZbTE0NqWeIGTyHSQ8Rq9pvOpWQG_CQE_tOc6jHPtj02XIBYa0Zo9fWbXXQyNYs9hDGGj34QibKFJky4W9nYBpSb57OwxiQoDyo25vzIXMTN2SNxuzqg=w1920-h1080"></object>
  <object data="https://lh3.googleusercontent.com/HISe-DV_b4gjLvSEGzrJlsqBU2rSE8uQpSqHHKTPihg_Ax9VtfCrOrvdXF01raBeBleAWQKI7Hfb4_w9vZeJKFymQfNTlubwXxTBTbqGTPwjg7S0CBtQsQJqsspvIhD9c-pniSZrEw=w1920-h1080"></object>
  <object data="https://lh3.googleusercontent.com/05lhR1IAQY_B9rdQ_GvHDNLe1lJsSPyyuDeIMkt--gDDAnO2_EATwif7-sfNd2K_48RvyqKmN-u2svKZ06yfh8bnrbQ5kBUrIHfZvWheTzDGhIeFd1roPor-F_BycJmVKbQO6a9EaA=w1920-h1080"></object>
</div>

Jednak to narzędzie nie do końca spełniło moje oczekiwania. Wygenerowany widget, który wyświetla zdjęcia nie do końca trafia w moje gusta. Kolejna rzecz to adresy do zdjęć są na sztywno zahardkodowane w kodzie. W przypadku aktualizacji albumu w google photos nowe zdjęcia nie będą widoczne na stronie. Musiałbym jeszcze raz wygenerować kod z wykorzystaniem tego narzędzia i zaktualizować stronę co byłoby uciążliwe. Chciałem osiągnąć efekt, w którym dodając zdjęcia do albumu na google photos automatycznie będą widoczne na stronie.

W związku z tym postanowiłem zautomatyzować to narzędzie. Na początku odpaliłem Burpa aby sprawdzić jaki endpoint jest wykorzystywany pod spodem. Okazało się, że jest wywoływany następujący endpoint:

POST https://www.publicalbum.org/api/v2/webapp/embed-player/jsonrpc

W ciele żądania jest m.in. link do publicznego albumu, który podałem narzędziu. W odpowiedzi otrzymuję m.in. takie dane jak nazwa albumu jak również listę linków do zdjęć w tym albumie. Zakładam, że pod spodem narzędzie wykorzystuje metodę web scrappingu (jeżeli masz pomysł jak to może być zrealizowane daj znać w komentarzu).

Postanowiłem więc skorzystać z tego API i wywoływać je dla każdego publicznego albumu, który chcę wyświetlić na stronie za każdym razem gdy galeria zdjęć jest ładowana. Dzięki temu zdjęcia wyświetlane na stronie będą zawsze "zsynchronizowane" z tymi w chmurze. Poniżej kod, który napisałem aby pobrać dane i wyrenderować galerię:

function fetchAlbum(albumId) {
    const payload = {
        method: 'getGooglePhotosAlbum',
        params: {
            sharedLink: GOOGLE_PHOTOS_URL + '/' + albumId,
            imageWidth: IMAGE_WIDTH,
            imageHeight: IMAGE_HEIGHT,
            includeThumbnails: true,
            videoQuality: '1080p',
            attachMetadata: false
        },
        id: 1
    };
    return fetch('/google-photo-album', {
        method: 'POST',
        body: JSON.stringify(payload),
        headers: {
            'Content-Type': 'text/plain;charset=UTF-8',
            'Accept': 'application/json'
        }
    })
        .then(response => response.json())
        .then(response => response["result"]);
}

$(document).ready(function () {
    (async () => {
        const items = [];
        for (const albumId of GOOGLE_ALBUM_IDS) {
            const album = await fetchAlbum(albumId);
            const title = album.title;
            album.mediaItems.forEach((mediaItem, i) => {
                if (i === 0) {
                    const item = {
                        ID: albumId,
                        src: mediaItem.url,
                        srct: mediaItem.url.split('=')[0] + `=w${THUMBNAIL_L1_WIDTH}-h${THUMBNAIL_L1_HEIGHT}`,
                        kind: 'album',
                        title: title
                    };
                    items.push(item);
                }
                const item = {
                    ID: mediaItem.id,
                    albumID: albumId,
                    src: mediaItem.url,
                    srct: mediaItem.url.split('=')[0] + `=w${THUMBNAIL_WIDTH}-h${THUMBNAIL_HEIGHT}`
                };
                items.push(item);
            });
        }
        return items;
    })().then(items => renderGallery(items));

Stała GOOGLE_ALBUM_IDS jest tablicą, która przechowuje identyfikatory publicznych albumów z google photos. Jeżeli chcesz wyświetlić swoje albumy po prostu wrzuć do tablicy odpowiednie identyfikatory.

Endpoint /google-photo-album jest usługą wystawioną przeze mnie, która tylko przekierowuje do docelowego API przy okazji podmieniając nagłówek Referer. Poniżej konfiguracja nginx:

location /google-photo-album {
        proxy_set_header        Referer https://www.publicalbum.org/blog/embedding-google-photos-albums;
        proxy_pass              https://www.publicalbum.org/api/v2/webapp/embed-player/jsonrpc;
}

Za renderowanie galerii odpowiedzialna jest biblioteka nanoGallery2, która spełnia moje oczekiwania. Udostępnia ciekawy, nowoczesny i interaktywny styl i dobrze sprawdza się na urządzeniach mobilnych.

Zaletą rozwiązania, które opisałem powyżej jest prostota. Wystarczy jeden plik HTML aby wyświetlić galerię w postaci albumów "zsynchronizowanych" z google photos dzięki pobieraniu danych z API przy każdym ładowaniu.

Na koniec warto wspomnieć o tym, że rozwiązanie nie jest idealne. Pierwszą wadą rozwiązania jest to, że jest uzależnione od zewnętrznego API. Jeżeli API się zmieni lub usługa zostanie wyłączona galeria się nie wyświetli. Kolejna wada (moim zdaniem bardziej istotna) to czas ładowania galerii. Im więcej albumów chcemy wyświetlić tym dłużej będziemy czekać ponieważ każdy album to oddzielne żądanie HTTP. Jeżeli masz pomysł jak to zoptymalizować lub lepiej zintegrować/zrealizować daj znać w komentarzu 😉.

Jeżeli ten artykuł był dla Ciebie wartościowy podziel się nim z innymi. A tymczasem zostawiam Ciebie z przydatnymi linkami i utworem, którego teraz słucham 😎.

Linki

Galeria: https://lsdev.pl/gallery/
Github: https://github.com/lukascode/photo-gallery/