diff options
| -rw-r--r-- | assets/js/player.js | 4 | ||||
| -rw-r--r-- | locales/el.json | 107 | ||||
| -rw-r--r-- | locales/fi.json | 141 | ||||
| -rw-r--r-- | src/invidious.cr | 17 | ||||
| -rw-r--r-- | src/invidious/channels/channels.cr | 24 | ||||
| -rw-r--r-- | src/invidious/database/base.cr | 64 | ||||
| -rw-r--r-- | src/invidious/database/channels.cr | 31 | ||||
| -rw-r--r-- | src/invidious/database/playlists.cr | 26 | ||||
| -rw-r--r-- | src/invidious/database/sessions.cr | 4 | ||||
| -rw-r--r-- | src/invidious/database/users.cr | 6 | ||||
| -rw-r--r-- | src/invidious/jobs/refresh_channels_job.cr | 2 | ||||
| -rw-r--r-- | src/invidious/routes/api/v1/authenticated.cr | 2 | ||||
| -rw-r--r-- | src/invidious/routes/feeds.cr | 2 | ||||
| -rw-r--r-- | src/invidious/routes/playlists.cr | 16 | ||||
| -rw-r--r-- | src/invidious/routes/preferences.cr | 10 | ||||
| -rw-r--r-- | src/invidious/routes/subscriptions.cr | 2 | ||||
| -rw-r--r-- | src/invidious/users.cr | 2 |
17 files changed, 324 insertions, 136 deletions
diff --git a/assets/js/player.js b/assets/js/player.js index 5ff55eb3..66d1682f 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -23,7 +23,7 @@ var options = { }, html5: { preloadTextTracks: false, - hls: { + vhs: { overrideNative: true } } @@ -50,7 +50,7 @@ var shareOptions = { embedCode: "<iframe id='ivplayer' width='640' height='360' src='" + embed_url + "' style='border:none;'></iframe>" } -videojs.Hls.xhr.beforeRequest = function(options) { +videojs.Vhs.xhr.beforeRequest = function(options) { if (options.uri.indexOf('videoplayback') === -1 && options.uri.indexOf('local=true') === -1) { options.uri = options.uri + '?local=true'; } diff --git a/locales/el.json b/locales/el.json index 8800941a..b33fc02f 100644 --- a/locales/el.json +++ b/locales/el.json @@ -60,7 +60,7 @@ "preferences_volume_label": "Ένταση αναπαραγωγής: ", "preferences_comments_label": "Προεπιλεγμένα σχόλια: ", "youtube": "YouTube", - "reddit": "reddit", + "reddit": "Reddit", "preferences_captions_label": "Προεπιλεγμένοι υπότιτλοι: ", "Fallback captions: ": "Εναλλακτικοί υπότιτλοι: ", "preferences_related_videos_label": "Προβολή σχετικών βίντεο; ", @@ -318,5 +318,108 @@ "Videos": "Βίντεο", "Playlists": "Λίστες Αναπαραγωγής", "Community": "Κοινότητα", - "Current version: ": "Τρέχουσα έκδοση: " + "Current version: ": "Τρέχουσα έκδοση: ", + "generic_playlists_count": "{{count}} λίστα αναπαραγωγής", + "generic_playlists_count_plural": "{{count}} λίστες αναπαραγωγής", + "preferences_quality_dash_option_worst": "Χειρότερη", + "preferences_quality_dash_option_2160p": "2160 p", + "Video unavailable": "Το βίντεο δεν είναι διαθέσιμο", + "preferences_quality_dash_option_auto": "Αυτόματη", + "preferences_quality_dash_option_1440p": "1440p", + "preferences_quality_dash_option_1080p": "1080p", + "comments_view_x_replies": "Προβολή {{count}} απάντησης", + "comments_view_x_replies_plural": "Προβολή {{count}} απαντήσεων", + "crash_page_report_issue": "Εάν κανένα από τα παραπάνω δεν βοήθησε, παρακαλούμε <a href=\"`x`\">ανοίξτε ένα νέο θέμα στο GitHub</a> (κατά προτίμηση στα αγγλικά) και συμπεριλάβετε το ακόλουθο κείμενο στο μήνυμά σας (ΜΗΝ μεταφράζετε αυτό το κείμενο):", + "generic_count_hours": "{{count}} ώρα", + "generic_count_hours_plural": "{{count}} ώρες", + "generic_count_minutes": "{{count}} λεπτό", + "generic_count_minutes_plural": "{{count}} λεπτά", + "generic_count_seconds": "{{count}} δευτερόλεπτο", + "generic_count_seconds_plural": "{{count}} δευτερόλεπτα", + "preferences_quality_dash_label": "Προτιμώμενη ποιότητα βίντεο DASH: ", + "preferences_quality_dash_option_best": "Καλύτερη", + "preferences_quality_dash_option_480p": "480p", + "preferences_quality_dash_option_360p": "360p", + "preferences_quality_dash_option_240p": "240p", + "preferences_quality_dash_option_144p": "144p", + "generic_subscribers_count": "{{count}} συνδρομητής", + "generic_subscribers_count_plural": "{{count}} συνδρομητές", + "generic_subscriptions_count": "{{count}} συνδρομή", + "generic_subscriptions_count_plural": "{{count}} συνδρομές", + "generic_count_years": "{{count}} έτος", + "generic_count_years_plural": "{{count}} έτη", + "generic_count_months": "{{count}} μήνας", + "generic_count_months_plural": "{{count}} μήνες", + "generic_count_weeks": "{{count}} εβδομάδα", + "generic_count_weeks_plural": "{{count}} εβδομάδες", + "generic_count_days": "{{count}} ημέρα", + "generic_count_days_plural": "{{count}} ημέρες", + "crash_page_you_found_a_bug": "Φαίνεται ότι βρήκατε ένα σφάλμα στο Invidious!", + "crash_page_before_reporting": "Πριν αναφέρετε ένα σφάλμα, βεβαιωθείτε ότι έχετε:", + "crash_page_refresh": "προσπαθήσει να <a href=\"`x`\">ανανεώσετε τη σελίδα</a>", + "crash_page_read_the_faq": "διαβάσει τις <a href=\"`x`\">Συχνές Ερωτήσεις (ΣΕ)</a>", + "crash_page_search_issue": "αναζητήσει για <a href=\"`x`\">υπάρχοντα θέματα στο Github</a>", + "generic_views_count": "{{count}} προβολή", + "generic_views_count_plural": "{{count}} προβολές", + "generic_videos_count": "{{count}} βίντεο", + "generic_videos_count_plural": "{{count}} βίντεο", + "preferences_quality_option_hd720": "HD720", + "preferences_quality_option_medium": "Μεσαία", + "preferences_quality_option_small": "Μικρό", + "preferences_quality_option_dash": "DASH (προσαρμοστική ποιότητα)", + "preferences_quality_dash_option_4320p": "4320p", + "preferences_quality_dash_option_720p": "720p", + "invidious": "Invidious", + "preferences_region_label": "Χώρα περιεχομένου: ", + "preferences_category_misc": "Διάφορες προτιμήσεις", + "Show more": "Εμφάνιση περισσότερων", + "today": "Σήμερα", + "360": "360°", + "videoinfo_started_streaming_x_ago": "Ξεκίνησε η ροή `x` πριν από", + "videoinfo_watch_on_youTube": "Παρακολουθήστε στο YouTube", + "download_subtitles": "Υπότιτλοι - `x` (.vtt)", + "user_created_playlists": "`x` δημιουργημένες λίστες αναπαραγωγής", + "user_saved_playlists": "`x` αποθηκευμένες λίστες αναπαραγωγής", + "rating": "Αξιολόγηση", + "relevance": "Συνάφεια", + "purchased": "Αγορασμένο", + "date": "Ημερομηνία μεταφόρτωσης", + "content_type": "Τύπος", + "duration": "Διάρκεια", + "week": "Αυτή την εβδομάδα", + "year": "Φέτος", + "channel": "Κανάλι", + "playlist": "Λίστα αναπαραγωγής", + "long": "Μεγάλο (> 20 λεπτά)", + "hd": "HD", + "location": "Τοποθεσία", + "3d": "3D", + "next_steps_error_message": "Μετά από αυτό θα πρέπει να προσπαθήσετε να: ", + "next_steps_error_message_go_to_youtube": "Μεταβείτε στο YouTube", + "footer_donate_page": "Δωρεά", + "footer_original_source_code": "Πρωτότυπος πηγαίος κώδικας", + "preferences_show_nick_label": "Εμφάνιση ψευδώνυμου στην κορυφή: ", + "hour": "Τελευταία ώρα", + "adminprefs_modified_source_code_url_label": "URL σε αποθετήριο τροποποιημένου πηγαίου κώδικα", + "subtitles": "Υπότιτλοι/CC", + "month": "Αυτόν τον μήνα", + "Released under the AGPLv3 on Github.": "Κυκλοφορεί υπό την AGPLv3 στο Github.", + "sort": "Ταξινόμηση κατά", + "filter": "Φίλτρο", + "movie": "Ταινία", + "footer_modfied_source_code": "Τροποποιημένος πηγαίος κώδικας", + "features": "Χαρακτηριστικά", + "4k": "4K", + "footer_documentation": "Τεκμηρίωση", + "short": "Σύντομο (< 4 λεπτά)", + "next_steps_error_message_refresh": "Ανανέωση", + "video": "Βίντεο", + "live": "Ζωντανά", + "creative_commons": "Creative Commons", + "Search": "Αναζήτηση", + "hdr": "HDR", + "preferences_extend_desc_label": "Αυτόματη επέκταση της περιγραφής του βίντεο: ", + "preferences_vr_mode_label": "Διαδραστικά βίντεο 360 μοιρών: ", + "Show less": "Εμφάνιση λιγότερων", + "footer_source_code": "Πηγαίος κώδικας" } diff --git a/locales/fi.json b/locales/fi.json index df7bb2be..7f97e869 100644 --- a/locales/fi.json +++ b/locales/fi.json @@ -21,47 +21,47 @@ "No": "Ei", "Import and Export Data": "Tuo ja vie tietoja", "Import": "Tuo", - "Import Invidious data": "Vie Invidious-tietoja", + "Import Invidious data": "Tuo Invidious-tietoja", "Import YouTube subscriptions": "Tuo YouTube-tilaukset", "Import FreeTube subscriptions (.db)": "Tuo FreeTube-tilaukset (.db)", "Import NewPipe subscriptions (.json)": "Tuo NewPipe-tilaukset (.json)", - "Import NewPipe data (.zip)": "Tuo NewPipe data (.zip)", + "Import NewPipe data (.zip)": "Tuo NewPipe-tietoja (.zip)", "Export": "Vie", - "Export subscriptions as OPML": "Vie tilaukset muodossa OPML", - "Export subscriptions as OPML (for NewPipe & FreeTube)": "Vie tilaukset muodossa OPML (NewPipe ja FreeTube)", - "Export data as JSON": "Vie data muodossa JSON", + "Export subscriptions as OPML": "Vie tilaukset OPML-muodossa", + "Export subscriptions as OPML (for NewPipe & FreeTube)": "Vie tilaukset OPML-muodossa (NewPipe & FreeTube)", + "Export data as JSON": "Vie data JSON-muodossa", "Delete account?": "Poista tili?", "History": "Historia", - "An alternative front-end to YouTube": "Vaihtoehtoinen käyttöliittymä YouTubelle", - "JavaScript license information": "JavaScript-käyttöoikeustiedot", + "An alternative front-end to YouTube": "Vaihtoehtoinen front-end YouTubelle", + "JavaScript license information": "JavaScript-lisenssitiedot", "source": "lähde", - "Log in": "Kirjaudu", - "Log in/register": "Kirjaudu sisään / Rekisteröidy", + "Log in": "Kirjaudu sisään", + "Log in/register": "Kirjaudu sisään/rekisteröidy", "Log in with Google": "Kirjaudu sisään Googlella", "User ID": "Käyttäjätunnus", "Password": "Salasana", "Time (h:mm:ss):": "Aika (h:mm:ss):", - "Text CAPTCHA": "Teksti CAPTCHA", - "Image CAPTCHA": "Kuva CAPTCHA", + "Text CAPTCHA": "Teksti-CAPTCHA", + "Image CAPTCHA": "Kuva-CAPTCHA", "Sign In": "Kirjaudu sisään", "Register": "Rekisteröidy", "E-mail": "Sähköposti", "Google verification code": "Google-vahvistuskoodi", "Preferences": "Asetukset", "preferences_category_player": "Soittimen asetukset", - "preferences_video_loop_label": "Aina silmukka: ", + "preferences_video_loop_label": "Toista jatkuvasti aina: ", "preferences_autoplay_label": "Automaattinen toisto: ", "preferences_continue_label": "Toista seuraava oletuksena: ", "preferences_continue_autoplay_label": "Toista seuraava video automaattisesti: ", "preferences_listen_label": "Kuuntele oletuksena: ", - "preferences_local_label": "Proxy videot: ", + "preferences_local_label": "Proxytä videot: ", "preferences_speed_label": "Oletusnopeus: ", "preferences_quality_label": "Ensisijainen videon laatu: ", "preferences_volume_label": "Soittimen äänenvoimakkuus: ", "preferences_comments_label": "Oletuskommentit: ", "youtube": "YouTube", "reddit": "Reddit", - "preferences_captions_label": "Tekstitykset: ", + "preferences_captions_label": "Oletustekstitykset: ", "Fallback captions: ": "Toissijaiset tekstitykset: ", "preferences_related_videos_label": "Näytä aiheeseen liittyviä videoita: ", "preferences_annotations_label": "Näytä huomautukset oletuksena: ", @@ -73,9 +73,9 @@ "preferences_dark_mode_label": "Teema: ", "dark": "tumma", "light": "vaalea", - "preferences_thin_mode_label": "Kapea tila ", + "preferences_thin_mode_label": "Kapea tila: ", "preferences_category_misc": "Sekalaiset asetukset", - "preferences_automatic_instance_redirect_label": "Automaattinen palveluntarjoajan uudelleenohjaus (perääntyminen sivulle redirect.invidious.io) ", + "preferences_automatic_instance_redirect_label": "Automaattinen instanssin uudelleenohjaus (perääntyminen sivulle redirect.invidious.io): ", "preferences_category_subscription": "Tilausten asetukset", "preferences_annotations_subscribed_label": "Näytä oletuksena tilattujen kanavien huomautukset: ", "Redirect homepage to feed: ": "Uudelleenohjaa kotisivu syötteeseen: ", @@ -122,7 +122,7 @@ "search": "haku", "Log out": "Kirjaudu ulos", "Source available here.": "Lähdekoodi on saatavilla täällä.", - "View JavaScript license information.": "JavaScript-koodin lisenssit.", + "View JavaScript license information.": "Katso JavaScript-koodin lisenssitiedot.", "View privacy policy.": "Katso tietosuojaseloste.", "Trending": "Nousussa", "Public": "Julkinen", @@ -130,7 +130,7 @@ "Private": "Yksityinen", "View all playlists": "Kaikki soittolistat", "Updated `x` ago": "Päivitetty `x` sitten", - "Delete playlist `x`?": "Poistetaanko soittolista `x`?", + "Delete playlist `x`?": "Poista soittolista `x`?", "Delete playlist": "Poista soittolista", "Create playlist": "Luo soittolista", "Title": "Nimi", @@ -139,8 +139,8 @@ "Show more": "Näytä enemmän", "Show less": "Näytä vähemmän", "Watch on YouTube": "Katso YouTubessa", - "Switch Invidious Instance": "Vaihda Invidious-palveluntarjoajaa", - "Broken? Try another Invidious Instance": "Rikki? Kokeile toista Invidious-palveluntarjoajaa", + "Switch Invidious Instance": "Vaihda Invidious-instanssia", + "Broken? Try another Invidious Instance": "Rikki? Kokeile toista Invidious-instanssia", "Hide annotations": "Piilota merkkaukset", "Show annotations": "Näytä merkkaukset", "Genre: ": "Genre: ", @@ -157,10 +157,10 @@ "View YouTube comments": "Näytä YouTube-kommentit", "View more comments on Reddit": "Katso lisää kommentteja Redditissä", "View `x` comments": { - "([^.,0-9]|^)1([^.,0-9]|$)": "Näytä `x` komenttia", - "": "Näytä `x` kommenttia" + "([^.,0-9]|^)1([^.,0-9]|$)": "Katso `x` kommentti", + "": "Katso `x` kommenttia" }, - "View Reddit comments": "Näytä Reddit-kommentit", + "View Reddit comments": "Katso Reddit-kommentit", "Hide replies": "Piilota vastaukset", "Show replies": "Näytä vastaukset", "Incorrect password": "Väärä salasana", @@ -178,7 +178,7 @@ "Password cannot be empty": "Salasana ei voi olla tyhjä", "Password cannot be longer than 55 characters": "Salasana ei voi olla yli 55 merkkiä pitkä", "Please log in": "Kirjaudu sisään, ole hyvä", - "Invidious Private Feed for `x`": "Invidousin yksityinen syöte `x`:lle", + "Invidious Private Feed for `x`": "Invidiousin yksityinen syöte `x`:lle", "channel:`x`": "kanava:`x`", "Deleted or invalid channel": "Poistettu tai virheellinen kanava", "This channel does not exist.": "Tätä kanavaa ei ole olemassa.", @@ -192,11 +192,11 @@ "Playlist does not exist.": "Soittolistaa ei ole olemassa.", "Could not pull trending pages.": "Nousussa olevien sivujen lataus epäonnistui.", "Hidden field \"challenge\" is a required field": "Piilotettu kenttä \"challenge\" vaaditaan", - "Hidden field \"token\" is a required field": "Piilotettu kenttä \"tunniste\" vaaditaan", + "Hidden field \"token\" is a required field": "Piilotettu kenttä \"tunnus\" vaaditaan", "Erroneous challenge": "Virheellinen haaste", - "Erroneous token": "Virheellinen tunniste", + "Erroneous token": "Virheellinen tunnus", "No such user": "Käyttäjää ei ole olemassa", - "Token is expired, please try again": "Tunniste on vanhentunut, yritä uudestaan", + "Token is expired, please try again": "Tunnus on vanhentunut, yritä uudestaan", "English": "englanti", "English (auto-generated)": "englanti (automaattisesti luotu)", "Afrikaans": "afrikaans", @@ -221,7 +221,7 @@ "Danish": "tanska", "Dutch": "hollanti", "Esperanto": "esperanto", - "Estonian": "eesti", + "Estonian": "viro", "Filipino": "filipino", "Finnish": "suomi", "French": "ranska", @@ -310,7 +310,7 @@ "About": "Tietoa", "Rating: ": "Arvosana: ", "preferences_locale_label": "Kieli: ", - "View as playlist": "Näytä soittolistana", + "View as playlist": "Katso soittolistana", "Default": "Oletus", "Music": "Musiikki", "Gaming": "Videopelit", @@ -322,7 +322,7 @@ "(edited)": "(muokattu)", "YouTube comment permalink": "Pysyvä linkki YouTube-kommenttiin", "permalink": "pysyvä linkki", - "`x` marked it with a ❤": "`x` merkattu ❤:llä", + "`x` marked it with a ❤": "`x` merkkasi ❤:llä", "Audio mode": "Äänitila", "Video mode": "Videotila", "Videos": "Videot", @@ -358,5 +358,84 @@ "Current version: ": "Tämänhetkinen versio: ", "next_steps_error_message": "Sinun tulisi kokeilla seuraavia: ", "next_steps_error_message_refresh": "Päivitä", - "next_steps_error_message_go_to_youtube": "Siirry YouTubeen" + "next_steps_error_message_go_to_youtube": "Siirry YouTubeen", + "generic_count_hours": "{{count}} tunti", + "generic_count_hours_plural": "{{count}} tuntia", + "download_subtitles": "Tekstitykset - `x` (.vtt)", + "user_created_playlists": "`x` luotua soittolistaa", + "Video unavailable": "Video ei ole saatavilla", + "videoinfo_youTube_embed_link": "Upota", + "tokens_count": "{{count}} tunnus", + "tokens_count_plural": "{{count}} tunnusta", + "generic_videos_count": "{{count}} video", + "generic_videos_count_plural": "{{count}} videota", + "generic_playlists_count": "{{count}} soittolista", + "generic_playlists_count_plural": "{{count}} soittolistaa", + "generic_subscriptions_count": "{{count}} tilaus", + "generic_subscriptions_count_plural": "{{count}} tilausta", + "comments_view_x_replies": "Katso {{count}} vastaus", + "comments_view_x_replies_plural": "Katso {{count}} vastausta", + "generic_count_months": "{{count}} kuukausi", + "generic_count_months_plural": "{{count}} kuukautta", + "generic_count_weeks": "{{count}} viikko", + "generic_count_weeks_plural": "{{count}} viikkoa", + "generic_count_days": "{{count}} päivä", + "generic_count_days_plural": "{{count}} päivää", + "generic_count_minutes": "{{count}} minuutti", + "generic_count_minutes_plural": "{{count}} minuuttia", + "comments_points_count": "{{count}} piste", + "comments_points_count_plural": "{{count}} pistettä", + "generic_count_seconds": "{{count}} sekunti", + "generic_count_seconds_plural": "{{count}} sekuntia", + "crash_page_before_reporting": "Varmista ennen bugin ilmoittamista, että sinä olet:", + "crash_page_refresh": "yrittänyt <a href=\"`x`\">päivittää sivun</a>", + "crash_page_read_the_faq": "lukenut <a href=\"`x`\">Usein kysytyt kysymykset (FAQ)</a>", + "crash_page_search_issue": "etsinyt <a href=\"`x`\">olemassa olevia issueita Githubissa</a>", + "generic_views_count": "{{count}} katselu", + "generic_views_count_plural": "{{count}} katselua", + "preferences_quality_dash_option_720p": "720p", + "generic_subscribers_count": "{{count}} tilaaja", + "generic_subscribers_count_plural": "{{count}} tilaajaa", + "preferences_quality_dash_option_1440p": "1440p", + "crash_page_you_found_a_bug": "Vaikuttaa siltä, että löysit bugin Invidiousista!", + "subscriptions_unseen_notifs_count": "{{count}} näkemätön ilmoitus", + "subscriptions_unseen_notifs_count_plural": "{{count}} näkemätöntä ilmoitusta", + "crash_page_switch_instance": "yrittänyt <a href=\"`x`\">käyttää toista instassia</a>", + "videoinfo_invidious_embed_link": "Upotuslinkki", + "user_saved_playlists": "`x` tallennetua soittolistaa", + "crash_page_report_issue": "Jos mikään näistä ei auttanut, <a href=\"`x`\">avaathan uuden issuen GitHubissa</a> (mieluiten englanniksi) ja sisällytät seuraavan tekstin viestissäsi (ÄLÄ käännä tätä tekstiä):", + "preferences_quality_option_hd720": "HD720", + "preferences_quality_dash_option_worst": "Huonoin", + "preferences_quality_dash_option_4320p": "4320p", + "preferences_quality_dash_option_2160p": "2160p", + "preferences_quality_dash_option_1080p": "1080p", + "preferences_quality_dash_option_480p": "480p", + "preferences_quality_dash_option_360p": "360p", + "preferences_quality_dash_option_240p": "240p", + "preferences_quality_dash_option_144p": "144p", + "invidious": "Invidious", + "preferences_region_label": "Sisältömaa: ", + "preferences_quality_option_medium": "Keskitaso", + "preferences_quality_option_small": "Pieni", + "preferences_quality_dash_option_auto": "Auto", + "preferences_quality_dash_option_best": "Paras", + "preferences_quality_option_dash": "DASH (mukautuva laatu)", + "preferences_quality_dash_label": "Haluttava DASH-videolaatu: ", + "generic_count_years": "{{count}} vuosi", + "generic_count_years_plural": "{{count}} vuotta", + "purchased": "Ostettu", + "360": "360°", + "videoinfo_watch_on_youTube": "Katso YouTubessa", + "none": "ei mikään", + "videoinfo_started_streaming_x_ago": "Striimaaminen aloitettu `x` sitten", + "preferences_save_player_pos_label": "Tallenna toistokohta: ", + "footer_donate_page": "Lahjoita", + "footer_source_code": "Lähdekoodi", + "adminprefs_modified_source_code_url_label": "URL muokattuun lähdekoodirepositoryyn", + "Released under the AGPLv3 on Github.": "Julkaistu AGPLv3-lisenssin alla GitHubissa.", + "short": "Lyhyt (< 4 minuuttia)", + "long": "Pitkä (> 20 minuuttia)", + "footer_documentation": "Dokumentaatio", + "footer_original_source_code": "Alkuperäinen lähdekoodi", + "footer_modfied_source_code": "Muokattu lähdekoodi" } diff --git a/src/invidious.cr b/src/invidious.cr index 8ba62503..b09f31c2 100644 --- a/src/invidious.cr +++ b/src/invidious.cr @@ -112,22 +112,7 @@ OUTPUT = CONFIG.output.upcase == "STDOUT" ? STDOUT : File.open(CONFIG.output, mo LOGGER = Invidious::LogHandler.new(OUTPUT, CONFIG.log_level) # Check table integrity -if CONFIG.check_tables - Invidious::Database.check_enum(PG_DB, "privacy", PlaylistPrivacy) - - Invidious::Database.check_table(PG_DB, "channels", InvidiousChannel) - Invidious::Database.check_table(PG_DB, "channel_videos", ChannelVideo) - Invidious::Database.check_table(PG_DB, "playlists", InvidiousPlaylist) - Invidious::Database.check_table(PG_DB, "playlist_videos", PlaylistVideo) - Invidious::Database.check_table(PG_DB, "nonces", Nonce) - Invidious::Database.check_table(PG_DB, "session_ids", SessionId) - Invidious::Database.check_table(PG_DB, "users", User) - Invidious::Database.check_table(PG_DB, "videos", Video) - - if CONFIG.cache_annotations - Invidious::Database.check_table(PG_DB, "annotations", Annotation) - end -end +Invidious::Database.check_integrity(CONFIG) # Resolve player dependencies. This is done at compile time. # diff --git a/src/invidious/channels/channels.cr b/src/invidious/channels/channels.cr index 155ec559..6905b6f8 100644 --- a/src/invidious/channels/channels.cr +++ b/src/invidious/channels/channels.cr @@ -114,8 +114,9 @@ class ChannelRedirect < Exception end end -def get_batch_channels(channels, refresh = false, pull_all_videos = true, max_threads = 10) +def get_batch_channels(channels) finished_channel = Channel(String | Nil).new + max_threads = 10 spawn do active_threads = 0 @@ -130,7 +131,7 @@ def get_batch_channels(channels, refresh = false, pull_all_videos = true, max_th active_threads += 1 spawn do begin - get_channel(ucid, refresh, pull_all_videos) + get_channel(ucid) finished_channel.send(ucid) rescue ex finished_channel.send(nil) @@ -151,23 +152,20 @@ def get_batch_channels(channels, refresh = false, pull_all_videos = true, max_th return final end -def get_channel(id, refresh = true, pull_all_videos = true) - if channel = Invidious::Database::Channels.select(id) - if refresh && Time.utc - channel.updated > 10.minutes - channel = fetch_channel(id, pull_all_videos: pull_all_videos) - Invidious::Database::Channels.insert(channel, update_on_conflict: true) - end - else - channel = fetch_channel(id, pull_all_videos: pull_all_videos) - Invidious::Database::Channels.insert(channel) +def get_channel(id) : InvidiousChannel + channel = Invidious::Database::Channels.select(id) + + if channel.nil? || (Time.utc - channel.updated) > 2.days + channel = fetch_channel(id, pull_all_videos: false) + Invidious::Database::Channels.insert(channel, update_on_conflict: true) end return channel end -def fetch_channel(ucid, pull_all_videos = true, locale = nil) +def fetch_channel(ucid, pull_all_videos : Bool) LOGGER.debug("fetch_channel: #{ucid}") - LOGGER.trace("fetch_channel: #{ucid} : pull_all_videos = #{pull_all_videos}, locale = #{locale}") + LOGGER.trace("fetch_channel: #{ucid} : pull_all_videos = #{pull_all_videos}") LOGGER.trace("fetch_channel: #{ucid} : Downloading RSS feed") rss = YT_POOL.client &.get("/feeds/videos.xml?channel_id=#{ucid}").body diff --git a/src/invidious/database/base.cr b/src/invidious/database/base.cr index 6e49ea1a..0fb1b6af 100644 --- a/src/invidious/database/base.cr +++ b/src/invidious/database/base.cr @@ -3,26 +3,52 @@ require "pg" module Invidious::Database extend self - def check_enum(db, enum_name, struct_type = nil) + # Checks table integrity + # + # Note: config is passed as a parameter to avoid complex + # dependencies between different parts of the software. + def check_integrity(cfg) + return if !cfg.check_tables + Invidious::Database.check_enum("privacy", PlaylistPrivacy) + + Invidious::Database.check_table("channels", InvidiousChannel) + Invidious::Database.check_table("channel_videos", ChannelVideo) + Invidious::Database.check_table("playlists", InvidiousPlaylist) + Invidious::Database.check_table("playlist_videos", PlaylistVideo) + Invidious::Database.check_table("nonces", Nonce) + Invidious::Database.check_table("session_ids", SessionId) + Invidious::Database.check_table("users", User) + Invidious::Database.check_table("videos", Video) + + if cfg.cache_annotations + Invidious::Database.check_table("annotations", Annotation) + end + end + + # + # Table/enum integrity checks + # + + def check_enum(enum_name, struct_type = nil) return # TODO - if !db.query_one?("SELECT true FROM pg_type WHERE typname = $1", enum_name, as: Bool) + if !PG_DB.query_one?("SELECT true FROM pg_type WHERE typname = $1", enum_name, as: Bool) LOGGER.info("check_enum: CREATE TYPE #{enum_name}") - db.using_connection do |conn| + PG_DB.using_connection do |conn| conn.as(PG::Connection).exec_all(File.read("config/sql/#{enum_name}.sql")) end end end - def check_table(db, table_name, struct_type = nil) + def check_table(table_name, struct_type = nil) # Create table if it doesn't exist begin - db.exec("SELECT * FROM #{table_name} LIMIT 0") + PG_DB.exec("SELECT * FROM #{table_name} LIMIT 0") rescue ex LOGGER.info("check_table: check_table: CREATE TABLE #{table_name}") - db.using_connection do |conn| + PG_DB.using_connection do |conn| conn.as(PG::Connection).exec_all(File.read("config/sql/#{table_name}.sql")) end end @@ -30,7 +56,7 @@ module Invidious::Database return if !struct_type struct_array = struct_type.type_array - column_array = get_column_array(db, table_name) + column_array = get_column_array(PG_DB, table_name) column_types = File.read("config/sql/#{table_name}.sql").match(/CREATE TABLE public\.#{table_name}\n\((?<types>[\d\D]*?)\);/) .try &.["types"].split(",").map(&.strip).reject &.starts_with?("CONSTRAINT") @@ -41,14 +67,14 @@ module Invidious::Database if !column_array[i]? new_column = column_types.select(&.starts_with?(name))[0] LOGGER.info("check_table: ALTER TABLE #{table_name} ADD COLUMN #{new_column}") - db.exec("ALTER TABLE #{table_name} ADD COLUMN #{new_column}") + PG_DB.exec("ALTER TABLE #{table_name} ADD COLUMN #{new_column}") next end # Column doesn't exist if !column_array.includes? name new_column = column_types.select(&.starts_with?(name))[0] - db.exec("ALTER TABLE #{table_name} ADD COLUMN #{new_column}") + PG_DB.exec("ALTER TABLE #{table_name} ADD COLUMN #{new_column}") end # Column exists but in the wrong position, rotate @@ -59,29 +85,29 @@ module Invidious::Database # There's a column we didn't expect if !new_column LOGGER.info("check_table: ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]}") - db.exec("ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]} CASCADE") + PG_DB.exec("ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]} CASCADE") - column_array = get_column_array(db, table_name) + column_array = get_column_array(PG_DB, table_name) next end LOGGER.info("check_table: ALTER TABLE #{table_name} ADD COLUMN #{new_column}") - db.exec("ALTER TABLE #{table_name} ADD COLUMN #{new_column}") + PG_DB.exec("ALTER TABLE #{table_name} ADD COLUMN #{new_column}") LOGGER.info("check_table: UPDATE #{table_name} SET #{column_array[i]}_new=#{column_array[i]}") - db.exec("UPDATE #{table_name} SET #{column_array[i]}_new=#{column_array[i]}") + PG_DB.exec("UPDATE #{table_name} SET #{column_array[i]}_new=#{column_array[i]}") LOGGER.info("check_table: ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]} CASCADE") - db.exec("ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]} CASCADE") + PG_DB.exec("ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]} CASCADE") LOGGER.info("check_table: ALTER TABLE #{table_name} RENAME COLUMN #{column_array[i]}_new TO #{column_array[i]}") - db.exec("ALTER TABLE #{table_name} RENAME COLUMN #{column_array[i]}_new TO #{column_array[i]}") + PG_DB.exec("ALTER TABLE #{table_name} RENAME COLUMN #{column_array[i]}_new TO #{column_array[i]}") - column_array = get_column_array(db, table_name) + column_array = get_column_array(PG_DB, table_name) end else LOGGER.info("check_table: ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]} CASCADE") - db.exec("ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]} CASCADE") + PG_DB.exec("ALTER TABLE #{table_name} DROP COLUMN #{column_array[i]} CASCADE") end end end @@ -91,14 +117,14 @@ module Invidious::Database column_array.each do |column| if !struct_array.includes? column LOGGER.info("check_table: ALTER TABLE #{table_name} DROP COLUMN #{column} CASCADE") - db.exec("ALTER TABLE #{table_name} DROP COLUMN #{column} CASCADE") + PG_DB.exec("ALTER TABLE #{table_name} DROP COLUMN #{column} CASCADE") end end end def get_column_array(db, table_name) column_array = [] of String - db.query("SELECT * FROM #{table_name} LIMIT 0") do |rs| + PG_DB.query("SELECT * FROM #{table_name} LIMIT 0") do |rs| rs.column_count.times do |i| column = rs.as(PG::ResultSet).field(i) column_array << column.name diff --git a/src/invidious/database/channels.cr b/src/invidious/database/channels.cr index 134cf59d..df44e485 100644 --- a/src/invidious/database/channels.cr +++ b/src/invidious/database/channels.cr @@ -35,21 +35,31 @@ module Invidious::Database::Channels def update_author(id : String, author : String) request = <<-SQL UPDATE channels - SET updated = $1, author = $2, deleted = false - WHERE id = $3 + SET updated = now(), author = $1, deleted = false + WHERE id = $2 + SQL + + PG_DB.exec(request, author, id) + end + + def update_subscription_time(id : String) + request = <<-SQL + UPDATE channels + SET subscribed = now() + WHERE id = $1 SQL - PG_DB.exec(request, Time.utc, author, id) + PG_DB.exec(request, id) end def update_mark_deleted(id : String) request = <<-SQL UPDATE channels - SET updated = $1, deleted = true - WHERE id = $2 + SET updated = now(), deleted = true + WHERE id = $1 SQL - PG_DB.exec(request, Time.utc, id) + PG_DB.exec(request, id) end # ------------------- @@ -67,14 +77,13 @@ module Invidious::Database::Channels def select(ids : Array(String)) : Array(InvidiousChannel)? return [] of InvidiousChannel if ids.empty? - values = ids.map { |id| %(('#{id}')) }.join(",") request = <<-SQL SELECT * FROM channels - WHERE id = ANY(VALUES #{values}) + WHERE id = ANY($1) SQL - return PG_DB.query_all(request, as: InvidiousChannel) + return PG_DB.query_all(request, ids, as: InvidiousChannel) end end @@ -117,11 +126,11 @@ module Invidious::Database::ChannelVideos request = <<-SQL SELECT * FROM channel_videos - WHERE id IN (#{arg_array(ids)}) + WHERE id = ANY($1) ORDER BY published DESC SQL - return PG_DB.query_all(request, args: ids, as: ChannelVideo) + return PG_DB.query_all(request, ids, as: ChannelVideo) end def select_notfications(ucid : String, since : Time) : Array(ChannelVideo) diff --git a/src/invidious/database/playlists.cr b/src/invidious/database/playlists.cr index 7a5f61dc..c6754a1e 100644 --- a/src/invidious/database/playlists.cr +++ b/src/invidious/database/playlists.cr @@ -59,11 +59,11 @@ module Invidious::Database::Playlists def update_subscription_time(id : String) request = <<-SQL UPDATE playlists - SET subscribed = $1 - WHERE id = $2 + SET subscribed = now() + WHERE id = $1 SQL - PG_DB.exec(request, Time.utc, id) + PG_DB.exec(request, id) end def update_video_added(id : String, index : String | Int64) @@ -71,11 +71,11 @@ module Invidious::Database::Playlists UPDATE playlists SET index = array_append(index, $1), video_count = cardinality(index) + 1, - updated = $2 - WHERE id = $3 + updated = now() + WHERE id = $2 SQL - PG_DB.exec(request, index, Time.utc, id) + PG_DB.exec(request, index, id) end def update_video_removed(id : String, index : String | Int64) @@ -83,28 +83,24 @@ module Invidious::Database::Playlists UPDATE playlists SET index = array_remove(index, $1), video_count = cardinality(index) - 1, - updated = $2 - WHERE id = $3 + updated = now() + WHERE id = $2 SQL - PG_DB.exec(request, index, Time.utc, id) + PG_DB.exec(request, index, id) end # ------------------- # Salect # ------------------- - def select(*, id : String, raise_on_fail : Bool = false) : InvidiousPlaylist? + def select(*, id : String) : InvidiousPlaylist? request = <<-SQL SELECT * FROM playlists WHERE id = $1 SQL - if raise_on_fail - return PG_DB.query_one(request, id, as: InvidiousPlaylist) - else - return PG_DB.query_one?(request, id, as: InvidiousPlaylist) - end + return PG_DB.query_one?(request, id, as: InvidiousPlaylist) end def select_all(*, author : String) : Array(InvidiousPlaylist) diff --git a/src/invidious/database/sessions.cr b/src/invidious/database/sessions.cr index d5f85dd6..96587082 100644 --- a/src/invidious/database/sessions.cr +++ b/src/invidious/database/sessions.cr @@ -10,12 +10,12 @@ module Invidious::Database::SessionIDs def insert(sid : String, email : String, handle_conflicts : Bool = false) request = <<-SQL INSERT INTO session_ids - VALUES ($1, $2, $3) + VALUES ($1, $2, now()) SQL request += " ON CONFLICT (id) DO NOTHING" if handle_conflicts - PG_DB.exec(request, sid, email, Time.utc) + PG_DB.exec(request, sid, email) end # ------------------- diff --git a/src/invidious/database/users.cr b/src/invidious/database/users.cr index 53724dbf..26be4270 100644 --- a/src/invidious/database/users.cr +++ b/src/invidious/database/users.cr @@ -143,11 +143,11 @@ module Invidious::Database::Users def clear_notifications(user : User) request = <<-SQL UPDATE users - SET notifications = '{}', updated = $1 - WHERE email = $2 + SET notifications = '{}', updated = now() + WHERE email = $1 SQL - PG_DB.exec(request, Time.utc, user.email) + PG_DB.exec(request, user.email) end # ------------------- diff --git a/src/invidious/jobs/refresh_channels_job.cr b/src/invidious/jobs/refresh_channels_job.cr index 941089c1..55fb8154 100644 --- a/src/invidious/jobs/refresh_channels_job.cr +++ b/src/invidious/jobs/refresh_channels_job.cr @@ -30,7 +30,7 @@ class Invidious::Jobs::RefreshChannelsJob < Invidious::Jobs::BaseJob spawn do begin LOGGER.trace("RefreshChannelsJob: #{id} fiber : Fetching channel") - channel = fetch_channel(id, CONFIG.full_refresh) + channel = fetch_channel(id, pull_all_videos: CONFIG.full_refresh) lim_fibers = max_fibers diff --git a/src/invidious/routes/api/v1/authenticated.cr b/src/invidious/routes/api/v1/authenticated.cr index fda655ef..4d0fe030 100644 --- a/src/invidious/routes/api/v1/authenticated.cr +++ b/src/invidious/routes/api/v1/authenticated.cr @@ -92,7 +92,7 @@ module Invidious::Routes::API::V1::Authenticated ucid = env.params.url["ucid"] if !user.subscriptions.includes? ucid - get_channel(ucid, false, false) + get_channel(ucid) Invidious::Database::Users.subscribe_channel(user, ucid) end diff --git a/src/invidious/routes/feeds.cr b/src/invidious/routes/feeds.cr index fd8c25ce..c323cdf7 100644 --- a/src/invidious/routes/feeds.cr +++ b/src/invidious/routes/feeds.cr @@ -362,7 +362,7 @@ module Invidious::Routes::Feeds end if ucid = HTTP::Params.parse(URI.parse(topic).query.not_nil!)["channel_id"]? - PG_DB.exec("UPDATE channels SET subscribed = $1 WHERE id = $2", Time.utc, ucid) + Invidious::Database::Channels.update_subscription_time(ucid) elsif plid = HTTP::Params.parse(URI.parse(topic).query.not_nil!)["playlist_id"]? Invidious::Database::Playlists.update_subscription_time(plid) else diff --git a/src/invidious/routes/playlists.cr b/src/invidious/routes/playlists.cr index d437b79c..1c4f1bef 100644 --- a/src/invidious/routes/playlists.cr +++ b/src/invidious/routes/playlists.cr @@ -151,12 +151,8 @@ module Invidious::Routes::Playlists page = env.params.query["page"]?.try &.to_i? page ||= 1 - begin - playlist = Invidious::Database::Playlists.select(id: plid, raise_on_fail: true) - if !playlist || playlist.author != user.email - return env.redirect referer - end - rescue ex + playlist = Invidious::Database::Playlists.select(id: plid) + if !playlist || playlist.author != user.email return env.redirect referer end @@ -235,12 +231,8 @@ module Invidious::Routes::Playlists page = env.params.query["page"]?.try &.to_i? page ||= 1 - begin - playlist = Invidious::Database::Playlists.select(id: plid, raise_on_fail: true) - if !playlist || playlist.author != user.email - return env.redirect referer - end - rescue ex + playlist = Invidious::Database::Playlists.select(id: plid) + if !playlist || playlist.author != user.email return env.redirect referer end diff --git a/src/invidious/routes/preferences.cr b/src/invidious/routes/preferences.cr index faae03bc..9c740cf2 100644 --- a/src/invidious/routes/preferences.cr +++ b/src/invidious/routes/preferences.cr @@ -327,7 +327,7 @@ module Invidious::Routes::PreferencesRoute user.subscriptions += body["subscriptions"].as_a.map(&.as_s) user.subscriptions.uniq! - user.subscriptions = get_batch_channels(user.subscriptions, false, false) + user.subscriptions = get_batch_channels(user.subscriptions) Invidious::Database::Users.update_subscriptions(user) end @@ -409,7 +409,7 @@ module Invidious::Routes::PreferencesRoute end user.subscriptions.uniq! - user.subscriptions = get_batch_channels(user.subscriptions, false, false) + user.subscriptions = get_batch_channels(user.subscriptions) Invidious::Database::Users.update_subscriptions(user) when "import_freetube" @@ -418,7 +418,7 @@ module Invidious::Routes::PreferencesRoute end user.subscriptions.uniq! - user.subscriptions = get_batch_channels(user.subscriptions, false, false) + user.subscriptions = get_batch_channels(user.subscriptions) Invidious::Database::Users.update_subscriptions(user) when "import_newpipe_subscriptions" @@ -437,7 +437,7 @@ module Invidious::Routes::PreferencesRoute end user.subscriptions.uniq! - user.subscriptions = get_batch_channels(user.subscriptions, false, false) + user.subscriptions = get_batch_channels(user.subscriptions) Invidious::Database::Users.update_subscriptions(user) when "import_newpipe" @@ -456,7 +456,7 @@ module Invidious::Routes::PreferencesRoute user.subscriptions += db.query_all("SELECT url FROM subscriptions", as: String).map(&.lchop("https://www.youtube.com/channel/")) user.subscriptions.uniq! - user.subscriptions = get_batch_channels(user.subscriptions, false, false) + user.subscriptions = get_batch_channels(user.subscriptions) Invidious::Database::Users.update_subscriptions(user) diff --git a/src/invidious/routes/subscriptions.cr b/src/invidious/routes/subscriptions.cr index 29152afb..ec8fe67b 100644 --- a/src/invidious/routes/subscriptions.cr +++ b/src/invidious/routes/subscriptions.cr @@ -51,7 +51,7 @@ module Invidious::Routes::Subscriptions case action when "action_create_subscription_to_channel" if !user.subscriptions.includes? channel_id - get_channel(channel_id, false, false) + get_channel(channel_id) Invidious::Database::Users.subscribe_channel(user, channel_id) end when "action_remove_subscriptions" diff --git a/src/invidious/users.cr b/src/invidious/users.cr index 49074994..a7ee72a9 100644 --- a/src/invidious/users.cr +++ b/src/invidious/users.cr @@ -74,7 +74,7 @@ def fetch_user(sid, headers) end end - channels = get_batch_channels(channels, false, false) + channels = get_batch_channels(channels) email = feed.xpath_node(%q(//a[@class="yt-masthead-picker-header yt-masthead-picker-active-account"])) if email |
